From e658e9fe65306346e827676a121eca3534ad75ff Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 9 Nov 2010 09:12:42 -0800 Subject: driver core: the development tree has switched to git So change the MAINTAINERS file to show where the tree now is at. Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 0094224ca79b..283d5f54ddce 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2051,7 +2051,7 @@ F: Documentation/blockdev/drbd/ DRIVER CORE, KOBJECTS, DEBUGFS AND SYSFS M: Greg Kroah-Hartman -T: quilt kernel.org/pub/linux/kernel/people/gregkh/gregkh-2.6/ +T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core-2.6.git S: Supported F: Documentation/kobject.txt F: drivers/base/ -- cgit v1.2.3 From 4aafd3f71a257a3522932944b5204262dfdff147 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 19 Nov 2010 16:46:26 -0200 Subject: perf tools: Change my maintainer address Also remove old snail mail address from CREDITS, moved years ago. LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- CREDITS | 2 -- MAINTAINERS | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'MAINTAINERS') diff --git a/CREDITS b/CREDITS index 41d8e63d5165..494b6e4746d7 100644 --- a/CREDITS +++ b/CREDITS @@ -2365,8 +2365,6 @@ E: acme@redhat.com W: http://oops.ghostprotocols.net:81/blog/ P: 1024D/9224DF01 D5DF E3BB E3C8 BCBB F8AD 841A B6AB 4681 9224 DF01 D: IPX, LLC, DCCP, cyc2x, wl3501_cs, net/ hacks -S: R. Brasílio Itiberê, 4270/1010 - Água Verde -S: 80240-060 - Curitiba - Paraná S: Brazil N: Karsten Merker diff --git a/MAINTAINERS b/MAINTAINERS index 8e6548dbd5db..f94cd37d63da 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4602,7 +4602,7 @@ PERFORMANCE EVENTS SUBSYSTEM M: Peter Zijlstra M: Paul Mackerras M: Ingo Molnar -M: Arnaldo Carvalho de Melo +M: Arnaldo Carvalho de Melo S: Supported F: kernel/perf_event*.c F: include/linux/perf_event.h -- cgit v1.2.3 From cc840f77036fa0a141951967cd010014f088b5e0 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 23 Nov 2010 22:36:42 -0800 Subject: MAINTAINERS: INTEL DRM DRIVERS list (intel-gfx) is subscribers-only Mark it so. Signed-off-by: Joe Perches Signed-off-by: Chris Wilson --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index a92c994ba935..86f9bb60d084 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2080,7 +2080,7 @@ F: include/drm/ INTEL DRM DRIVERS (excluding Poulsbo, Moorestown and derivative chipsets) M: Chris Wilson -L: intel-gfx@lists.freedesktop.org +L: intel-gfx@lists.freedesktop.org (subscribers-only) L: dri-devel@lists.freedesktop.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/ickle/drm-intel.git S: Supported -- cgit v1.2.3 From e3d33cb13202cd51378c8d61e92a96183cc0e6fd Mon Sep 17 00:00:00 2001 From: Chien Tung Date: Tue, 2 Nov 2010 16:29:54 +0000 Subject: MAINTAINERS: Update NetEffect entry Correct web link as www.neteffect.com is no longer valid. Remove Chien Tung as maintainer. I am moving on to other responsibilities at Intel. Thanks for all the fish. Signed-off-by: Chien Tung Signed-off-by: Roland Dreier --- MAINTAINERS | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index debde0128cd0..e067aa959d47 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4043,9 +4043,8 @@ F: drivers/scsi/NCR_D700.* NETEFFECT IWARP RNIC DRIVER (IW_NES) M: Faisal Latif -M: Chien Tung L: linux-rdma@vger.kernel.org -W: http://www.neteffect.com +W: http://www.intel.com/Products/Server/Adapters/Server-Cluster/Server-Cluster-overview.htm S: Supported F: drivers/infiniband/hw/nes/ -- cgit v1.2.3 From 9a10a870e09f1fd50fd024d55232b4b72cf4e387 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 1 Dec 2010 09:37:55 -0800 Subject: MAINTAINERS: Add ATH GENERIC UTILITIES This file pattern is not currently shown as maintained by atheros. Perhaps it should be? Signed-off-by: Joe Perches Signed-off-by: John W. Linville --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 0094224ca79b..4d8bde32a26b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1080,6 +1080,12 @@ S: Supported F: Documentation/aoe/ F: drivers/block/aoe/ +ATHEROS ATH GENERIC UTILITIES +M: "Luis R. Rodriguez" +L: linux-wireless@vger.kernel.org +S: Supported +F: drivers/net/wireless/ath/* + ATHEROS ATH5K WIRELESS DRIVER M: Jiri Slaby M: Nick Kossifidis -- cgit v1.2.3 From fb890ae7d615f4b4f5689144b6832230ab2046aa Mon Sep 17 00:00:00 2001 From: Kyle McMartin Date: Thu, 9 Dec 2010 09:50:08 +0000 Subject: MAINTAINERS: remove me from tulip It was a nice idea, but -ENOTIME and -ENOHW. I never got around to doing a lot of the clean up that I intended to. Signed-off-by: Kyle McMartin Acked-by: Grant Grundler Signed-off-by: David S. Miller --- MAINTAINERS | 1 - 1 file changed, 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 9206cb462913..dec390dff141 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5926,7 +5926,6 @@ F: include/linux/tty.h TULIP NETWORK DRIVERS M: Grant Grundler -M: Kyle McMartin L: netdev@vger.kernel.org S: Maintained F: drivers/net/tulip/ -- cgit v1.2.3 From fa795e6b9c59a14c80a475428df7e4cb97396f47 Mon Sep 17 00:00:00 2001 From: Jeff Kirsher Date: Fri, 10 Dec 2010 00:09:08 -0800 Subject: MAINTAINERS: Update Intel Wired LAN info Update with Intel Wired Ethernet public git trees. Signed-off-by: Jeff Kirsher --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 9206cb462913..4a77d0d300a5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3115,6 +3115,8 @@ M: Alex Duyck M: John Ronciak L: e1000-devel@lists.sourceforge.net W: http://e1000.sourceforge.net/ +T: git git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/net-2.6.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/net-next-2.6.git S: Supported F: Documentation/networking/e100.txt F: Documentation/networking/e1000.txt -- cgit v1.2.3 From 3e6cd7a4b6a04cf354a18c9d2e7ecec8fa1772fb Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 10 Dec 2010 17:20:23 +0100 Subject: MAINTAINERS: Add workqueue entry Signed-off-by: Tejun Heo --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index b3be8b3d0437..462e39709c03 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6585,6 +6585,15 @@ F: include/linux/mfd/wm8400* F: include/sound/wm????.h F: sound/soc/codecs/wm* +WORKQUEUE +M: Tejun Heo +L: linux-kernel@vger.kernel.org +T: git git://git.kernel.org/pub/scm/linux/kernel/git/tj/wq.git +S: Maintained +F: include/linux/workqueue.h +F: kernel/workqueue.c +F: Documentation/workqueue.txt + X.25 NETWORK LAYER M: Andrew Hendry L: linux-x25@vger.kernel.org -- cgit v1.2.3 From 88606e80da0e8d862a42ee19e5bb60b01b940ea7 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 14 Dec 2010 21:37:13 +0100 Subject: MAINTAINERS: Update timer related entries Bring the existing file list up to date and add a new entry for timekeeping and ntp. Assign John Stultz to this new entry so he gets all the blame :) Signed-off-by: Thomas Gleixner Cc: John Stultz --- MAINTAINERS | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 087912aa09bd..2a1b25673d3e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2730,6 +2730,10 @@ M: Thomas Gleixner S: Maintained F: Documentation/timers/ F: kernel/hrtimer.c +F: kernel/time/clockevents.c +F: kernel/time/tick*.* +F: kernel/time/timer_*.c +F include/linux/clockevents.h F: include/linux/hrtimer.h HIGH-SPEED SCC DRIVER FOR AX.25 @@ -4986,6 +4990,18 @@ F: drivers/media/common/saa7146* F: drivers/media/video/*7146* F: include/media/*7146* +TIMEKEEPING, NTP +M: John Stultz +M: Thomas Gleixner +S: Supported +F: include/linux/clocksource.h +F: include/linux/time.h +F: include/linux/timex.h +F: include/linux/timekeeping.h +F: kernel/time/clocksource.c +F: kernel/time/time*.c +F: kernel/time/ntp.c + TLG2300 VIDEO4LINUX-2 DRIVER M: Huang Shijie M: Kang Yong -- cgit v1.2.3 From d6db46675dba21203378b6993068ec04bb53389c Mon Sep 17 00:00:00 2001 From: Jiandong Zheng Date: Tue, 14 Dec 2010 21:55:49 +0000 Subject: Change bcmring Maintainer list. I am Jiandong Zheng working on BCMRING in Broadcom Canada Ltd. I am replacing Leo Chen (leochen@broadcom.com) as "ARM/BCMRING ARM ARCHITECTURE" and "ARM/BCMRING MTD NAND DRIVER" maintainer from Broadcom as he is no longer the maintainer of these components. Signed-off-by: Jiandong Zheng Acked-by: Scott Branden Signed-off-by: Russell King --- MAINTAINERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 1a1c27b9c557..5202dc3ad5c4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -559,14 +559,14 @@ W: http://maxim.org.za/at91_26.html S: Maintained ARM/BCMRING ARM ARCHITECTURE -M: Leo Chen +M: Jiandong Zheng M: Scott Branden L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: arch/arm/mach-bcmring ARM/BCMRING MTD NAND DRIVER -M: Leo Chen +M: Jiandong Zheng M: Scott Branden L: linux-mtd@lists.infradead.org S: Maintained -- cgit v1.2.3 From 6c965ff5e7ca844494f1dcf0ec0440146db01294 Mon Sep 17 00:00:00 2001 From: Daniel Walker Date: Thu, 9 Dec 2010 15:45:27 -0800 Subject: MAINTAINERS: update MSM git tree The MSM main git tree has changed over to this new address. Signed-off-by: Daniel Walker Signed-off-by: Linus Torvalds --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 0b0d2ba69a8e..6a588873cf8d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -815,7 +815,7 @@ F: drivers/mmc/host/msm_sdcc.c F: drivers/mmc/host/msm_sdcc.h F: drivers/serial/msm_serial.h F: drivers/serial/msm_serial.c -T: git git://codeaurora.org/quic/kernel/dwalker/linux-msm.git +T: git git://codeaurora.org/quic/kernel/davidb/linux-msm.git S: Maintained ARM/TOSA MACHINE SUPPORT -- cgit v1.2.3 From 7f9c2454010159e871e9416dcf64b1e6bfce78be Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Tue, 7 Dec 2010 09:18:28 +0100 Subject: MAINTAINERS: Update input-mt entry The input multitouch core is now located in its own file, and maintained via a git tree. Update the maintainers entry accordingly. Signed-off-by: Henrik Rydberg --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 0094224ca79b..a9d3a1343698 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3020,8 +3020,10 @@ F: drivers/input/ INPUT MULTITOUCH (MT) PROTOCOL M: Henrik Rydberg L: linux-input@vger.kernel.org +T: git git://git.kernel.org/pub/scm/linux/kernel/git/rydberg/input-mt.git S: Maintained F: Documentation/input/multi-touch-protocol.txt +F: drivers/input/input-mt.c K: \b(ABS|SYN)_MT_ INTEL IDLE DRIVER -- cgit v1.2.3 From 3cf0c8ad94ba1ba241b970cd19c11a4f3cea93c2 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 16 Dec 2010 09:13:21 -0600 Subject: rtl8192ce: Update MAINTAINERS Signed-off-by: Larry Finger Signed-off-by: John W. Linville --- MAINTAINERS | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 4d8bde32a26b..43ab5fa42a73 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5044,6 +5044,16 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.g S: Maintained F: drivers/net/wireless/rtl818x/rtl8187* +RTL8192CE WIRELESS DRIVER +M: Larry Finger +M: Chaoming Li +L: linux-wireless@vger.kernel.org +W: http://linuxwireless.org/ +T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git +S: Maintained +F: drivers/net/wireless/rtlwifi/ +F: drivers/net/wireless/rtl8192ce/ + S3 SAVAGE FRAMEBUFFER DRIVER M: Antonino Daplas L: linux-fbdev@vger.kernel.org -- cgit v1.2.3 From c6c8fea29769d998d94fcec9b9f14d4b52b349d3 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Mon, 13 Dec 2010 11:19:28 +0000 Subject: net: Add batman-adv meshing protocol B.A.T.M.A.N. (better approach to mobile ad-hoc networking) is a routing protocol for multi-hop ad-hoc mesh networks. The networks may be wired or wireless. See http://www.open-mesh.org/ for more information and user space tools. Signed-off-by: Sven Eckelmann Signed-off-by: David S. Miller --- .../ABI/testing/sysfs-class-net-batman-adv | 14 + Documentation/ABI/testing/sysfs-class-net-mesh | 69 + Documentation/networking/batman-adv.txt | 240 ++++ MAINTAINERS | 9 + net/Kconfig | 1 + net/Makefile | 1 + net/batman-adv/Kconfig | 25 + net/batman-adv/Makefile | 39 + net/batman-adv/aggregation.c | 273 ++++ net/batman-adv/aggregation.h | 43 + net/batman-adv/bat_debugfs.c | 360 +++++ net/batman-adv/bat_debugfs.h | 33 + net/batman-adv/bat_sysfs.c | 593 +++++++++ net/batman-adv/bat_sysfs.h | 42 + net/batman-adv/bitarray.c | 201 +++ net/batman-adv/bitarray.h | 44 + net/batman-adv/gateway_client.c | 477 +++++++ net/batman-adv/gateway_client.h | 36 + net/batman-adv/gateway_common.c | 177 +++ net/batman-adv/gateway_common.h | 38 + net/batman-adv/hard-interface.c | 651 +++++++++ net/batman-adv/hard-interface.h | 53 + net/batman-adv/hash.c | 62 + net/batman-adv/hash.h | 176 +++ net/batman-adv/icmp_socket.c | 356 +++++ net/batman-adv/icmp_socket.h | 34 + net/batman-adv/main.c | 187 +++ net/batman-adv/main.h | 183 +++ net/batman-adv/originator.c | 564 ++++++++ net/batman-adv/originator.h | 64 + net/batman-adv/packet.h | 136 ++ net/batman-adv/ring_buffer.c | 52 + net/batman-adv/ring_buffer.h | 28 + net/batman-adv/routing.c | 1397 ++++++++++++++++++++ net/batman-adv/routing.h | 48 + net/batman-adv/send.c | 585 ++++++++ net/batman-adv/send.h | 41 + net/batman-adv/soft-interface.c | 697 ++++++++++ net/batman-adv/soft-interface.h | 35 + net/batman-adv/translation-table.c | 534 ++++++++ net/batman-adv/translation-table.h | 45 + net/batman-adv/types.h | 271 ++++ net/batman-adv/unicast.c | 343 +++++ net/batman-adv/unicast.h | 35 + net/batman-adv/vis.c | 949 +++++++++++++ net/batman-adv/vis.h | 37 + 46 files changed, 10278 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-class-net-batman-adv create mode 100644 Documentation/ABI/testing/sysfs-class-net-mesh create mode 100644 Documentation/networking/batman-adv.txt create mode 100644 net/batman-adv/Kconfig create mode 100644 net/batman-adv/Makefile create mode 100644 net/batman-adv/aggregation.c create mode 100644 net/batman-adv/aggregation.h create mode 100644 net/batman-adv/bat_debugfs.c create mode 100644 net/batman-adv/bat_debugfs.h create mode 100644 net/batman-adv/bat_sysfs.c create mode 100644 net/batman-adv/bat_sysfs.h create mode 100644 net/batman-adv/bitarray.c create mode 100644 net/batman-adv/bitarray.h create mode 100644 net/batman-adv/gateway_client.c create mode 100644 net/batman-adv/gateway_client.h create mode 100644 net/batman-adv/gateway_common.c create mode 100644 net/batman-adv/gateway_common.h create mode 100644 net/batman-adv/hard-interface.c create mode 100644 net/batman-adv/hard-interface.h create mode 100644 net/batman-adv/hash.c create mode 100644 net/batman-adv/hash.h create mode 100644 net/batman-adv/icmp_socket.c create mode 100644 net/batman-adv/icmp_socket.h create mode 100644 net/batman-adv/main.c create mode 100644 net/batman-adv/main.h create mode 100644 net/batman-adv/originator.c create mode 100644 net/batman-adv/originator.h create mode 100644 net/batman-adv/packet.h create mode 100644 net/batman-adv/ring_buffer.c create mode 100644 net/batman-adv/ring_buffer.h create mode 100644 net/batman-adv/routing.c create mode 100644 net/batman-adv/routing.h create mode 100644 net/batman-adv/send.c create mode 100644 net/batman-adv/send.h create mode 100644 net/batman-adv/soft-interface.c create mode 100644 net/batman-adv/soft-interface.h create mode 100644 net/batman-adv/translation-table.c create mode 100644 net/batman-adv/translation-table.h create mode 100644 net/batman-adv/types.h create mode 100644 net/batman-adv/unicast.c create mode 100644 net/batman-adv/unicast.h create mode 100644 net/batman-adv/vis.c create mode 100644 net/batman-adv/vis.h (limited to 'MAINTAINERS') diff --git a/Documentation/ABI/testing/sysfs-class-net-batman-adv b/Documentation/ABI/testing/sysfs-class-net-batman-adv new file mode 100644 index 000000000000..38dd762def4b --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-net-batman-adv @@ -0,0 +1,14 @@ + +What: /sys/class/net//batman-adv/mesh_iface +Date: May 2010 +Contact: Marek Lindner +Description: + The /sys/class/net//batman-adv/mesh_iface file + displays the batman mesh interface this + currently is associated with. + +What: /sys/class/net//batman-adv/iface_status +Date: May 2010 +Contact: Marek Lindner +Description: + Indicates the status of as it is seen by batman. diff --git a/Documentation/ABI/testing/sysfs-class-net-mesh b/Documentation/ABI/testing/sysfs-class-net-mesh new file mode 100644 index 000000000000..748fe1701d25 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-net-mesh @@ -0,0 +1,69 @@ + +What: /sys/class/net//mesh/aggregated_ogms +Date: May 2010 +Contact: Marek Lindner +Description: + Indicates whether the batman protocol messages of the + mesh shall be aggregated or not. + +What: /sys/class/net//mesh/bonding +Date: June 2010 +Contact: Simon Wunderlich +Description: + Indicates whether the data traffic going through the + mesh will be sent using multiple interfaces at the + same time (if available). + +What: /sys/class/net//mesh/fragmentation +Date: October 2010 +Contact: Andreas Langer +Description: + Indicates whether the data traffic going through the + mesh will be fragmented or silently discarded if the + packet size exceeds the outgoing interface MTU. + +What: /sys/class/net//mesh/gw_bandwidth +Date: October 2010 +Contact: Marek Lindner +Description: + Defines the bandwidth which is propagated by this + node if gw_mode was set to 'server'. + +What: /sys/class/net//mesh/gw_mode +Date: October 2010 +Contact: Marek Lindner +Description: + Defines the state of the gateway features. Can be + either 'off', 'client' or 'server'. + +What: /sys/class/net//mesh/gw_sel_class +Date: October 2010 +Contact: Marek Lindner +Description: + Defines the selection criteria this node will use + to choose a gateway if gw_mode was set to 'client'. + +What: /sys/class/net//mesh/orig_interval +Date: May 2010 +Contact: Marek Lindner +Description: + Defines the interval in milliseconds in which batman + sends its protocol messages. + +What: /sys/class/net//mesh/hop_penalty +Date: Oct 2010 +Contact: Linus Lüssing +Description: + Defines the penalty which will be applied to an + originator message's tq-field on every hop. + +What: /sys/class/net//mesh/vis_mode +Date: May 2010 +Contact: Marek Lindner +Description: + Each batman node only maintains information about its + own local neighborhood, therefore generating graphs + showing the topology of the entire mesh is not easily + feasible without having a central instance to collect + the local topologies from all nodes. This file allows + to activate the collecting (server) mode. diff --git a/Documentation/networking/batman-adv.txt b/Documentation/networking/batman-adv.txt new file mode 100644 index 000000000000..77f0cdd5b0dd --- /dev/null +++ b/Documentation/networking/batman-adv.txt @@ -0,0 +1,240 @@ +[state: 21-11-2010] + +BATMAN-ADV +---------- + +Batman advanced is a new approach to wireless networking which +does no longer operate on the IP basis. Unlike the batman daemon, +which exchanges information using UDP packets and sets routing +tables, batman-advanced operates on ISO/OSI Layer 2 only and uses +and routes (or better: bridges) Ethernet Frames. It emulates a +virtual network switch of all nodes participating. Therefore all +nodes appear to be link local, thus all higher operating proto- +cols won't be affected by any changes within the network. You can +run almost any protocol above batman advanced, prominent examples +are: IPv4, IPv6, DHCP, IPX. + +Batman advanced was implemented as a Linux kernel driver to re- +duce the overhead to a minimum. It does not depend on any (other) +network driver, and can be used on wifi as well as ethernet lan, +vpn, etc ... (anything with ethernet-style layer 2). + +CONFIGURATION +------------- + +Load the batman-adv module into your kernel: + +# insmod batman-adv.ko + +The module is now waiting for activation. You must add some in- +terfaces on which batman can operate. After loading the module +batman advanced will scan your systems interfaces to search for +compatible interfaces. Once found, it will create subfolders in +the /sys directories of each supported interface, e.g. + +# ls /sys/class/net/eth0/batman_adv/ +# iface_status mesh_iface + +If an interface does not have the "batman_adv" subfolder it prob- +ably is not supported. Not supported interfaces are: loopback, +non-ethernet and batman's own interfaces. + +Note: After the module was loaded it will continuously watch for +new interfaces to verify the compatibility. There is no need to +reload the module if you plug your USB wifi adapter into your ma- +chine after batman advanced was initially loaded. + +To activate a given interface simply write "bat0" into its +"mesh_iface" file inside the batman_adv subfolder: + +# echo bat0 > /sys/class/net/eth0/batman_adv/mesh_iface + +Repeat this step for all interfaces you wish to add. Now batman +starts using/broadcasting on this/these interface(s). + +By reading the "iface_status" file you can check its status: + +# cat /sys/class/net/eth0/batman_adv/iface_status +# active + +To deactivate an interface you have to write "none" into its +"mesh_iface" file: + +# echo none > /sys/class/net/eth0/batman_adv/mesh_iface + + +All mesh wide settings can be found in batman's own interface +folder: + +# ls /sys/class/net/bat0/mesh/ +# aggregated_ogms bonding fragmentation orig_interval +# vis_mode + + +There is a special folder for debugging informations: + +# ls /sys/kernel/debug/batman_adv/bat0/ +# originators socket transtable_global transtable_local +# vis_data + + +Some of the files contain all sort of status information regard- +ing the mesh network. For example, you can view the table of +originators (mesh participants) with: + +# cat /sys/kernel/debug/batman_adv/bat0/originators + +Other files allow to change batman's behaviour to better fit your +requirements. For instance, you can check the current originator +interval (value in milliseconds which determines how often batman +sends its broadcast packets): + +# cat /sys/class/net/bat0/mesh/orig_interval +# 1000 + +and also change its value: + +# echo 3000 > /sys/class/net/bat0/mesh/orig_interval + +In very mobile scenarios, you might want to adjust the originator +interval to a lower value. This will make the mesh more respon- +sive to topology changes, but will also increase the overhead. + + +USAGE +----- + +To make use of your newly created mesh, batman advanced provides +a new interface "bat0" which you should use from this point on. +All interfaces added to batman advanced are not relevant any +longer because batman handles them for you. Basically, one "hands +over" the data by using the batman interface and batman will make +sure it reaches its destination. + +The "bat0" interface can be used like any other regular inter- +face. It needs an IP address which can be either statically con- +figured or dynamically (by using DHCP or similar services): + +# NodeA: ifconfig bat0 192.168.0.1 +# NodeB: ifconfig bat0 192.168.0.2 +# NodeB: ping 192.168.0.1 + +Note: In order to avoid problems remove all IP addresses previ- +ously assigned to interfaces now used by batman advanced, e.g. + +# ifconfig eth0 0.0.0.0 + + +VISUALIZATION +------------- + +If you want topology visualization, at least one mesh node must +be configured as VIS-server: + +# echo "server" > /sys/class/net/bat0/mesh/vis_mode + +Each node is either configured as "server" or as "client" (de- +fault: "client"). Clients send their topology data to the server +next to them, and server synchronize with other servers. If there +is no server configured (default) within the mesh, no topology +information will be transmitted. With these "synchronizing +servers", there can be 1 or more vis servers sharing the same (or +at least very similar) data. + +When configured as server, you can get a topology snapshot of +your mesh: + +# cat /sys/kernel/debug/batman_adv/bat0/vis_data + +This raw output is intended to be easily parsable and convertable +with other tools. Have a look at the batctl README if you want a +vis output in dot or json format for instance and how those out- +puts could then be visualised in an image. + +The raw format consists of comma separated values per entry where +each entry is giving information about a certain source inter- +face. Each entry can/has to have the following values: +-> "mac" - mac address of an originator's source interface + (each line begins with it) +-> "TQ mac value" - src mac's link quality towards mac address + of a neighbor originator's interface which + is being used for routing +-> "HNA mac" - HNA announced by source mac +-> "PRIMARY" - this is a primary interface +-> "SEC mac" - secondary mac address of source + (requires preceding PRIMARY) + +The TQ value has a range from 4 to 255 with 255 being the best. +The HNA entries are showing which hosts are connected to the mesh +via bat0 or being bridged into the mesh network. The PRIMARY/SEC +values are only applied on primary interfaces + + +LOGGING/DEBUGGING +----------------- + +All error messages, warnings and information messages are sent to +the kernel log. Depending on your operating system distribution +this can be read in one of a number of ways. Try using the com- +mands: dmesg, logread, or looking in the files /var/log/kern.log +or /var/log/syslog. All batman-adv messages are prefixed with +"batman-adv:" So to see just these messages try + +# dmesg | grep batman-adv + +When investigating problems with your mesh network it is some- +times necessary to see more detail debug messages. This must be +enabled when compiling the batman-adv module. When building bat- +man-adv as part of kernel, use "make menuconfig" and enable the +option "B.A.T.M.A.N. debugging". + +Those additional debug messages can be accessed using a special +file in debugfs + +# cat /sys/kernel/debug/batman_adv/bat0/log + +The additional debug output is by default disabled. It can be en- +abled during run time. Following log_levels are defined: + +0 - All debug output disabled +1 - Enable messages related to routing / flooding / broadcasting +2 - Enable route or hna added / changed / deleted +3 - Enable all messages + +The debug output can be changed at runtime using the file +/sys/class/net/bat0/mesh/log_level. e.g. + +# echo 2 > /sys/class/net/bat0/mesh/log_level + +will enable debug messages for when routes or HNAs change. + + +BATCTL +------ + +As batman advanced operates on layer 2 all hosts participating in +the virtual switch are completely transparent for all protocols +above layer 2. Therefore the common diagnosis tools do not work +as expected. To overcome these problems batctl was created. At +the moment the batctl contains ping, traceroute, tcpdump and +interfaces to the kernel module settings. + +For more information, please see the manpage (man batctl). + +batctl is available on http://www.open-mesh.org/ + + +CONTACT +------- + +Please send us comments, experiences, questions, anything :) + +IRC: #batman on irc.freenode.org +Mailing-list: b.a.t.m.a.n@b.a.t.m.a.n@lists.open-mesh.org + (optional subscription at + https://lists.open-mesh.org/mm/listinfo/b.a.t.m.a.n) + +You can also contact the Authors: + +Marek Lindner +Simon Wunderlich diff --git a/MAINTAINERS b/MAINTAINERS index f16ce8f46934..2a5e60fb3b5d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1264,6 +1264,15 @@ S: Maintained F: drivers/video/backlight/ F: include/linux/backlight.h +BATMAN ADVANCED +M: Marek Lindner +M: Simon Wunderlich +M: Sven Eckelmann +L: b.a.t.m.a.n@lists.open-mesh.org +W: http://www.open-mesh.org/ +S: Maintained +F: net/batman-adv/ + BAYCOM/HDLCDRV DRIVERS FOR AX.25 M: Thomas Sailer L: linux-hams@vger.kernel.org diff --git a/net/Kconfig b/net/Kconfig index 126c2af0fc1f..ad0aafe903f8 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -214,6 +214,7 @@ source "net/ieee802154/Kconfig" source "net/sched/Kconfig" source "net/dcb/Kconfig" source "net/dns_resolver/Kconfig" +source "net/batman-adv/Kconfig" config RPS boolean diff --git a/net/Makefile b/net/Makefile index 6b7bfd7f1416..a3330ebe2c53 100644 --- a/net/Makefile +++ b/net/Makefile @@ -69,3 +69,4 @@ endif obj-$(CONFIG_WIMAX) += wimax/ obj-$(CONFIG_DNS_RESOLVER) += dns_resolver/ obj-$(CONFIG_CEPH_LIB) += ceph/ +obj-$(CONFIG_BATMAN_ADV) += batman-adv/ diff --git a/net/batman-adv/Kconfig b/net/batman-adv/Kconfig new file mode 100644 index 000000000000..6c051ad833eb --- /dev/null +++ b/net/batman-adv/Kconfig @@ -0,0 +1,25 @@ +# +# B.A.T.M.A.N meshing protocol +# + +config BATMAN_ADV + tristate "B.A.T.M.A.N. Advanced Meshing Protocol" + depends on NET + default n + ---help--- + + B.A.T.M.A.N. (better approach to mobile ad-hoc networking) is + a routing protocol for multi-hop ad-hoc mesh networks. The + networks may be wired or wireless. See + http://www.open-mesh.org/ for more information and user space + tools. + +config BATMAN_ADV_DEBUG + bool "B.A.T.M.A.N. debugging" + depends on BATMAN_ADV != n + ---help--- + + This is an option for use by developers; most people should + say N here. This enables compilation of support for + outputting debugging information to the kernel log. The + output is controlled via the module parameter debug. diff --git a/net/batman-adv/Makefile b/net/batman-adv/Makefile new file mode 100644 index 000000000000..d936aeccd194 --- /dev/null +++ b/net/batman-adv/Makefile @@ -0,0 +1,39 @@ +# +# Copyright (C) 2007-2010 B.A.T.M.A.N. contributors: +# +# Marek Lindner, Simon Wunderlich +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of version 2 of the GNU General Public +# License as published by the Free Software Foundation. +# +# 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., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA +# + +obj-$(CONFIG_BATMAN_ADV) += batman-adv.o +batman-adv-y += aggregation.o +batman-adv-y += bat_debugfs.o +batman-adv-y += bat_sysfs.o +batman-adv-y += bitarray.o +batman-adv-y += gateway_client.o +batman-adv-y += gateway_common.o +batman-adv-y += hard-interface.o +batman-adv-y += hash.o +batman-adv-y += icmp_socket.o +batman-adv-y += main.o +batman-adv-y += originator.o +batman-adv-y += ring_buffer.o +batman-adv-y += routing.o +batman-adv-y += send.o +batman-adv-y += soft-interface.o +batman-adv-y += translation-table.o +batman-adv-y += unicast.o +batman-adv-y += vis.o diff --git a/net/batman-adv/aggregation.c b/net/batman-adv/aggregation.c new file mode 100644 index 000000000000..3850a3ecf947 --- /dev/null +++ b/net/batman-adv/aggregation.c @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner, Simon Wunderlich + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include "main.h" +#include "aggregation.h" +#include "send.h" +#include "routing.h" + +/* calculate the size of the hna information for a given packet */ +static int hna_len(struct batman_packet *batman_packet) +{ + return batman_packet->num_hna * ETH_ALEN; +} + +/* return true if new_packet can be aggregated with forw_packet */ +static bool can_aggregate_with(struct batman_packet *new_batman_packet, + int packet_len, + unsigned long send_time, + bool directlink, + struct batman_if *if_incoming, + struct forw_packet *forw_packet) +{ + struct batman_packet *batman_packet = + (struct batman_packet *)forw_packet->skb->data; + int aggregated_bytes = forw_packet->packet_len + packet_len; + + /** + * we can aggregate the current packet to this aggregated packet + * if: + * + * - the send time is within our MAX_AGGREGATION_MS time + * - the resulting packet wont be bigger than + * MAX_AGGREGATION_BYTES + */ + + if (time_before(send_time, forw_packet->send_time) && + time_after_eq(send_time + msecs_to_jiffies(MAX_AGGREGATION_MS), + forw_packet->send_time) && + (aggregated_bytes <= MAX_AGGREGATION_BYTES)) { + + /** + * check aggregation compatibility + * -> direct link packets are broadcasted on + * their interface only + * -> aggregate packet if the current packet is + * a "global" packet as well as the base + * packet + */ + + /* packets without direct link flag and high TTL + * are flooded through the net */ + if ((!directlink) && + (!(batman_packet->flags & DIRECTLINK)) && + (batman_packet->ttl != 1) && + + /* own packets originating non-primary + * interfaces leave only that interface */ + ((!forw_packet->own) || + (forw_packet->if_incoming->if_num == 0))) + return true; + + /* if the incoming packet is sent via this one + * interface only - we still can aggregate */ + if ((directlink) && + (new_batman_packet->ttl == 1) && + (forw_packet->if_incoming == if_incoming) && + + /* packets from direct neighbors or + * own secondary interface packets + * (= secondary interface packets in general) */ + (batman_packet->flags & DIRECTLINK || + (forw_packet->own && + forw_packet->if_incoming->if_num != 0))) + return true; + } + + return false; +} + +#define atomic_dec_not_zero(v) atomic_add_unless((v), -1, 0) +/* create a new aggregated packet and add this packet to it */ +static void new_aggregated_packet(unsigned char *packet_buff, int packet_len, + unsigned long send_time, bool direct_link, + struct batman_if *if_incoming, + int own_packet) +{ + struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); + struct forw_packet *forw_packet_aggr; + unsigned char *skb_buff; + + /* own packet should always be scheduled */ + if (!own_packet) { + if (!atomic_dec_not_zero(&bat_priv->batman_queue_left)) { + bat_dbg(DBG_BATMAN, bat_priv, + "batman packet queue full\n"); + return; + } + } + + forw_packet_aggr = kmalloc(sizeof(struct forw_packet), GFP_ATOMIC); + if (!forw_packet_aggr) { + if (!own_packet) + atomic_inc(&bat_priv->batman_queue_left); + return; + } + + if ((atomic_read(&bat_priv->aggregated_ogms)) && + (packet_len < MAX_AGGREGATION_BYTES)) + forw_packet_aggr->skb = dev_alloc_skb(MAX_AGGREGATION_BYTES + + sizeof(struct ethhdr)); + else + forw_packet_aggr->skb = dev_alloc_skb(packet_len + + sizeof(struct ethhdr)); + + if (!forw_packet_aggr->skb) { + if (!own_packet) + atomic_inc(&bat_priv->batman_queue_left); + kfree(forw_packet_aggr); + return; + } + skb_reserve(forw_packet_aggr->skb, sizeof(struct ethhdr)); + + INIT_HLIST_NODE(&forw_packet_aggr->list); + + skb_buff = skb_put(forw_packet_aggr->skb, packet_len); + forw_packet_aggr->packet_len = packet_len; + memcpy(skb_buff, packet_buff, packet_len); + + forw_packet_aggr->own = own_packet; + forw_packet_aggr->if_incoming = if_incoming; + forw_packet_aggr->num_packets = 0; + forw_packet_aggr->direct_link_flags = 0; + forw_packet_aggr->send_time = send_time; + + /* save packet direct link flag status */ + if (direct_link) + forw_packet_aggr->direct_link_flags |= 1; + + /* add new packet to packet list */ + spin_lock_bh(&bat_priv->forw_bat_list_lock); + hlist_add_head(&forw_packet_aggr->list, &bat_priv->forw_bat_list); + spin_unlock_bh(&bat_priv->forw_bat_list_lock); + + /* start timer for this packet */ + INIT_DELAYED_WORK(&forw_packet_aggr->delayed_work, + send_outstanding_bat_packet); + queue_delayed_work(bat_event_workqueue, + &forw_packet_aggr->delayed_work, + send_time - jiffies); +} + +/* aggregate a new packet into the existing aggregation */ +static void aggregate(struct forw_packet *forw_packet_aggr, + unsigned char *packet_buff, + int packet_len, + bool direct_link) +{ + unsigned char *skb_buff; + + skb_buff = skb_put(forw_packet_aggr->skb, packet_len); + memcpy(skb_buff, packet_buff, packet_len); + forw_packet_aggr->packet_len += packet_len; + forw_packet_aggr->num_packets++; + + /* save packet direct link flag status */ + if (direct_link) + forw_packet_aggr->direct_link_flags |= + (1 << forw_packet_aggr->num_packets); +} + +void add_bat_packet_to_list(struct bat_priv *bat_priv, + unsigned char *packet_buff, int packet_len, + struct batman_if *if_incoming, char own_packet, + unsigned long send_time) +{ + /** + * _aggr -> pointer to the packet we want to aggregate with + * _pos -> pointer to the position in the queue + */ + struct forw_packet *forw_packet_aggr = NULL, *forw_packet_pos = NULL; + struct hlist_node *tmp_node; + struct batman_packet *batman_packet = + (struct batman_packet *)packet_buff; + bool direct_link = batman_packet->flags & DIRECTLINK ? 1 : 0; + + /* find position for the packet in the forward queue */ + spin_lock_bh(&bat_priv->forw_bat_list_lock); + /* own packets are not to be aggregated */ + if ((atomic_read(&bat_priv->aggregated_ogms)) && (!own_packet)) { + hlist_for_each_entry(forw_packet_pos, tmp_node, + &bat_priv->forw_bat_list, list) { + if (can_aggregate_with(batman_packet, + packet_len, + send_time, + direct_link, + if_incoming, + forw_packet_pos)) { + forw_packet_aggr = forw_packet_pos; + break; + } + } + } + + /* nothing to aggregate with - either aggregation disabled or no + * suitable aggregation packet found */ + if (!forw_packet_aggr) { + /* the following section can run without the lock */ + spin_unlock_bh(&bat_priv->forw_bat_list_lock); + + /** + * if we could not aggregate this packet with one of the others + * we hold it back for a while, so that it might be aggregated + * later on + */ + if ((!own_packet) && + (atomic_read(&bat_priv->aggregated_ogms))) + send_time += msecs_to_jiffies(MAX_AGGREGATION_MS); + + new_aggregated_packet(packet_buff, packet_len, + send_time, direct_link, + if_incoming, own_packet); + } else { + aggregate(forw_packet_aggr, + packet_buff, packet_len, + direct_link); + spin_unlock_bh(&bat_priv->forw_bat_list_lock); + } +} + +/* unpack the aggregated packets and process them one by one */ +void receive_aggr_bat_packet(struct ethhdr *ethhdr, unsigned char *packet_buff, + int packet_len, struct batman_if *if_incoming) +{ + struct batman_packet *batman_packet; + int buff_pos = 0; + unsigned char *hna_buff; + + batman_packet = (struct batman_packet *)packet_buff; + + do { + /* network to host order for our 32bit seqno, and the + orig_interval. */ + batman_packet->seqno = ntohl(batman_packet->seqno); + + hna_buff = packet_buff + buff_pos + BAT_PACKET_LEN; + receive_bat_packet(ethhdr, batman_packet, + hna_buff, hna_len(batman_packet), + if_incoming); + + buff_pos += BAT_PACKET_LEN + hna_len(batman_packet); + batman_packet = (struct batman_packet *) + (packet_buff + buff_pos); + } while (aggregated_packet(buff_pos, packet_len, + batman_packet->num_hna)); +} diff --git a/net/batman-adv/aggregation.h b/net/batman-adv/aggregation.h new file mode 100644 index 000000000000..71a91b3da913 --- /dev/null +++ b/net/batman-adv/aggregation.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner, Simon Wunderlich + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#ifndef _NET_BATMAN_ADV_AGGREGATION_H_ +#define _NET_BATMAN_ADV_AGGREGATION_H_ + +#include "main.h" + +/* is there another aggregated packet here? */ +static inline int aggregated_packet(int buff_pos, int packet_len, int num_hna) +{ + int next_buff_pos = buff_pos + BAT_PACKET_LEN + (num_hna * ETH_ALEN); + + return (next_buff_pos <= packet_len) && + (next_buff_pos <= MAX_AGGREGATION_BYTES); +} + +void add_bat_packet_to_list(struct bat_priv *bat_priv, + unsigned char *packet_buff, int packet_len, + struct batman_if *if_incoming, char own_packet, + unsigned long send_time); +void receive_aggr_bat_packet(struct ethhdr *ethhdr, unsigned char *packet_buff, + int packet_len, struct batman_if *if_incoming); + +#endif /* _NET_BATMAN_ADV_AGGREGATION_H_ */ diff --git a/net/batman-adv/bat_debugfs.c b/net/batman-adv/bat_debugfs.c new file mode 100644 index 000000000000..0ae81d07f102 --- /dev/null +++ b/net/batman-adv/bat_debugfs.c @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include "main.h" + +#include + +#include "bat_debugfs.h" +#include "translation-table.h" +#include "originator.h" +#include "hard-interface.h" +#include "gateway_common.h" +#include "gateway_client.h" +#include "soft-interface.h" +#include "vis.h" +#include "icmp_socket.h" + +static struct dentry *bat_debugfs; + +#ifdef CONFIG_BATMAN_ADV_DEBUG +#define LOG_BUFF_MASK (log_buff_len-1) +#define LOG_BUFF(idx) (debug_log->log_buff[(idx) & LOG_BUFF_MASK]) + +static int log_buff_len = LOG_BUF_LEN; + +static void emit_log_char(struct debug_log *debug_log, char c) +{ + LOG_BUFF(debug_log->log_end) = c; + debug_log->log_end++; + + if (debug_log->log_end - debug_log->log_start > log_buff_len) + debug_log->log_start = debug_log->log_end - log_buff_len; +} + +static int fdebug_log(struct debug_log *debug_log, char *fmt, ...) +{ + int printed_len; + va_list args; + static char debug_log_buf[256]; + char *p; + + if (!debug_log) + return 0; + + spin_lock_bh(&debug_log->lock); + va_start(args, fmt); + printed_len = vscnprintf(debug_log_buf, sizeof(debug_log_buf), + fmt, args); + va_end(args); + + for (p = debug_log_buf; *p != 0; p++) + emit_log_char(debug_log, *p); + + spin_unlock_bh(&debug_log->lock); + + wake_up(&debug_log->queue_wait); + + return 0; +} + +int debug_log(struct bat_priv *bat_priv, char *fmt, ...) +{ + va_list args; + char tmp_log_buf[256]; + + va_start(args, fmt); + vscnprintf(tmp_log_buf, sizeof(tmp_log_buf), fmt, args); + fdebug_log(bat_priv->debug_log, "[%10u] %s", + (jiffies / HZ), tmp_log_buf); + va_end(args); + + return 0; +} + +static int log_open(struct inode *inode, struct file *file) +{ + nonseekable_open(inode, file); + file->private_data = inode->i_private; + inc_module_count(); + return 0; +} + +static int log_release(struct inode *inode, struct file *file) +{ + dec_module_count(); + return 0; +} + +static ssize_t log_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct bat_priv *bat_priv = file->private_data; + struct debug_log *debug_log = bat_priv->debug_log; + int error, i = 0; + char c; + + if ((file->f_flags & O_NONBLOCK) && + !(debug_log->log_end - debug_log->log_start)) + return -EAGAIN; + + if ((!buf) || (count < 0)) + return -EINVAL; + + if (count == 0) + return 0; + + if (!access_ok(VERIFY_WRITE, buf, count)) + return -EFAULT; + + error = wait_event_interruptible(debug_log->queue_wait, + (debug_log->log_start - debug_log->log_end)); + + if (error) + return error; + + spin_lock_bh(&debug_log->lock); + + while ((!error) && (i < count) && + (debug_log->log_start != debug_log->log_end)) { + c = LOG_BUFF(debug_log->log_start); + + debug_log->log_start++; + + spin_unlock_bh(&debug_log->lock); + + error = __put_user(c, buf); + + spin_lock_bh(&debug_log->lock); + + buf++; + i++; + + } + + spin_unlock_bh(&debug_log->lock); + + if (!error) + return i; + + return error; +} + +static unsigned int log_poll(struct file *file, poll_table *wait) +{ + struct bat_priv *bat_priv = file->private_data; + struct debug_log *debug_log = bat_priv->debug_log; + + poll_wait(file, &debug_log->queue_wait, wait); + + if (debug_log->log_end - debug_log->log_start) + return POLLIN | POLLRDNORM; + + return 0; +} + +static const struct file_operations log_fops = { + .open = log_open, + .release = log_release, + .read = log_read, + .poll = log_poll, + .llseek = no_llseek, +}; + +static int debug_log_setup(struct bat_priv *bat_priv) +{ + struct dentry *d; + + if (!bat_priv->debug_dir) + goto err; + + bat_priv->debug_log = kzalloc(sizeof(struct debug_log), GFP_ATOMIC); + if (!bat_priv->debug_log) + goto err; + + spin_lock_init(&bat_priv->debug_log->lock); + init_waitqueue_head(&bat_priv->debug_log->queue_wait); + + d = debugfs_create_file("log", S_IFREG | S_IRUSR, + bat_priv->debug_dir, bat_priv, &log_fops); + if (d) + goto err; + + return 0; + +err: + return 1; +} + +static void debug_log_cleanup(struct bat_priv *bat_priv) +{ + kfree(bat_priv->debug_log); + bat_priv->debug_log = NULL; +} +#else /* CONFIG_BATMAN_ADV_DEBUG */ +static int debug_log_setup(struct bat_priv *bat_priv) +{ + bat_priv->debug_log = NULL; + return 0; +} + +static void debug_log_cleanup(struct bat_priv *bat_priv) +{ + return; +} +#endif + +static int originators_open(struct inode *inode, struct file *file) +{ + struct net_device *net_dev = (struct net_device *)inode->i_private; + return single_open(file, orig_seq_print_text, net_dev); +} + +static int gateways_open(struct inode *inode, struct file *file) +{ + struct net_device *net_dev = (struct net_device *)inode->i_private; + return single_open(file, gw_client_seq_print_text, net_dev); +} + +static int softif_neigh_open(struct inode *inode, struct file *file) +{ + struct net_device *net_dev = (struct net_device *)inode->i_private; + return single_open(file, softif_neigh_seq_print_text, net_dev); +} + +static int transtable_global_open(struct inode *inode, struct file *file) +{ + struct net_device *net_dev = (struct net_device *)inode->i_private; + return single_open(file, hna_global_seq_print_text, net_dev); +} + +static int transtable_local_open(struct inode *inode, struct file *file) +{ + struct net_device *net_dev = (struct net_device *)inode->i_private; + return single_open(file, hna_local_seq_print_text, net_dev); +} + +static int vis_data_open(struct inode *inode, struct file *file) +{ + struct net_device *net_dev = (struct net_device *)inode->i_private; + return single_open(file, vis_seq_print_text, net_dev); +} + +struct bat_debuginfo { + struct attribute attr; + const struct file_operations fops; +}; + +#define BAT_DEBUGINFO(_name, _mode, _open) \ +struct bat_debuginfo bat_debuginfo_##_name = { \ + .attr = { .name = __stringify(_name), \ + .mode = _mode, }, \ + .fops = { .owner = THIS_MODULE, \ + .open = _open, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = single_release, \ + } \ +}; + +static BAT_DEBUGINFO(originators, S_IRUGO, originators_open); +static BAT_DEBUGINFO(gateways, S_IRUGO, gateways_open); +static BAT_DEBUGINFO(softif_neigh, S_IRUGO, softif_neigh_open); +static BAT_DEBUGINFO(transtable_global, S_IRUGO, transtable_global_open); +static BAT_DEBUGINFO(transtable_local, S_IRUGO, transtable_local_open); +static BAT_DEBUGINFO(vis_data, S_IRUGO, vis_data_open); + +static struct bat_debuginfo *mesh_debuginfos[] = { + &bat_debuginfo_originators, + &bat_debuginfo_gateways, + &bat_debuginfo_softif_neigh, + &bat_debuginfo_transtable_global, + &bat_debuginfo_transtable_local, + &bat_debuginfo_vis_data, + NULL, +}; + +void debugfs_init(void) +{ + bat_debugfs = debugfs_create_dir(DEBUGFS_BAT_SUBDIR, NULL); + if (bat_debugfs == ERR_PTR(-ENODEV)) + bat_debugfs = NULL; +} + +void debugfs_destroy(void) +{ + if (bat_debugfs) { + debugfs_remove_recursive(bat_debugfs); + bat_debugfs = NULL; + } +} + +int debugfs_add_meshif(struct net_device *dev) +{ + struct bat_priv *bat_priv = netdev_priv(dev); + struct bat_debuginfo **bat_debug; + struct dentry *file; + + if (!bat_debugfs) + goto out; + + bat_priv->debug_dir = debugfs_create_dir(dev->name, bat_debugfs); + if (!bat_priv->debug_dir) + goto out; + + bat_socket_setup(bat_priv); + debug_log_setup(bat_priv); + + for (bat_debug = mesh_debuginfos; *bat_debug; ++bat_debug) { + file = debugfs_create_file(((*bat_debug)->attr).name, + S_IFREG | ((*bat_debug)->attr).mode, + bat_priv->debug_dir, + dev, &(*bat_debug)->fops); + if (!file) { + bat_err(dev, "Can't add debugfs file: %s/%s\n", + dev->name, ((*bat_debug)->attr).name); + goto rem_attr; + } + } + + return 0; +rem_attr: + debugfs_remove_recursive(bat_priv->debug_dir); + bat_priv->debug_dir = NULL; +out: +#ifdef CONFIG_DEBUG_FS + return -ENOMEM; +#else + return 0; +#endif /* CONFIG_DEBUG_FS */ +} + +void debugfs_del_meshif(struct net_device *dev) +{ + struct bat_priv *bat_priv = netdev_priv(dev); + + debug_log_cleanup(bat_priv); + + if (bat_debugfs) { + debugfs_remove_recursive(bat_priv->debug_dir); + bat_priv->debug_dir = NULL; + } +} diff --git a/net/batman-adv/bat_debugfs.h b/net/batman-adv/bat_debugfs.h new file mode 100644 index 000000000000..72df532b7d5f --- /dev/null +++ b/net/batman-adv/bat_debugfs.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + + +#ifndef _NET_BATMAN_ADV_DEBUGFS_H_ +#define _NET_BATMAN_ADV_DEBUGFS_H_ + +#define DEBUGFS_BAT_SUBDIR "batman_adv" + +void debugfs_init(void); +void debugfs_destroy(void); +int debugfs_add_meshif(struct net_device *dev); +void debugfs_del_meshif(struct net_device *dev); + +#endif /* _NET_BATMAN_ADV_DEBUGFS_H_ */ diff --git a/net/batman-adv/bat_sysfs.c b/net/batman-adv/bat_sysfs.c new file mode 100644 index 000000000000..cd7bb51825f1 --- /dev/null +++ b/net/batman-adv/bat_sysfs.c @@ -0,0 +1,593 @@ +/* + * Copyright (C) 2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include "main.h" +#include "bat_sysfs.h" +#include "translation-table.h" +#include "originator.h" +#include "hard-interface.h" +#include "gateway_common.h" +#include "gateway_client.h" +#include "vis.h" + +#define to_dev(obj) container_of(obj, struct device, kobj) +#define kobj_to_netdev(obj) to_net_dev(to_dev(obj->parent)) +#define kobj_to_batpriv(obj) netdev_priv(kobj_to_netdev(obj)) + +/* Use this, if you have customized show and store functions */ +#define BAT_ATTR(_name, _mode, _show, _store) \ +struct bat_attribute bat_attr_##_name = { \ + .attr = {.name = __stringify(_name), \ + .mode = _mode }, \ + .show = _show, \ + .store = _store, \ +}; + +#define BAT_ATTR_STORE_BOOL(_name, _post_func) \ +ssize_t store_##_name(struct kobject *kobj, struct attribute *attr, \ + char *buff, size_t count) \ +{ \ + struct net_device *net_dev = kobj_to_netdev(kobj); \ + struct bat_priv *bat_priv = netdev_priv(net_dev); \ + return __store_bool_attr(buff, count, _post_func, attr, \ + &bat_priv->_name, net_dev); \ +} + +#define BAT_ATTR_SHOW_BOOL(_name) \ +ssize_t show_##_name(struct kobject *kobj, struct attribute *attr, \ + char *buff) \ +{ \ + struct bat_priv *bat_priv = kobj_to_batpriv(kobj); \ + return sprintf(buff, "%s\n", \ + atomic_read(&bat_priv->_name) == 0 ? \ + "disabled" : "enabled"); \ +} \ + +/* Use this, if you are going to turn a [name] in bat_priv on or off */ +#define BAT_ATTR_BOOL(_name, _mode, _post_func) \ + static BAT_ATTR_STORE_BOOL(_name, _post_func) \ + static BAT_ATTR_SHOW_BOOL(_name) \ + static BAT_ATTR(_name, _mode, show_##_name, store_##_name) + + +#define BAT_ATTR_STORE_UINT(_name, _min, _max, _post_func) \ +ssize_t store_##_name(struct kobject *kobj, struct attribute *attr, \ + char *buff, size_t count) \ +{ \ + struct net_device *net_dev = kobj_to_netdev(kobj); \ + struct bat_priv *bat_priv = netdev_priv(net_dev); \ + return __store_uint_attr(buff, count, _min, _max, _post_func, \ + attr, &bat_priv->_name, net_dev); \ +} + +#define BAT_ATTR_SHOW_UINT(_name) \ +ssize_t show_##_name(struct kobject *kobj, struct attribute *attr, \ + char *buff) \ +{ \ + struct bat_priv *bat_priv = kobj_to_batpriv(kobj); \ + return sprintf(buff, "%i\n", atomic_read(&bat_priv->_name)); \ +} \ + +/* Use this, if you are going to set [name] in bat_priv to unsigned integer + * values only */ +#define BAT_ATTR_UINT(_name, _mode, _min, _max, _post_func) \ + static BAT_ATTR_STORE_UINT(_name, _min, _max, _post_func) \ + static BAT_ATTR_SHOW_UINT(_name) \ + static BAT_ATTR(_name, _mode, show_##_name, store_##_name) + + +static int store_bool_attr(char *buff, size_t count, + struct net_device *net_dev, + char *attr_name, atomic_t *attr) +{ + int enabled = -1; + + if (buff[count - 1] == '\n') + buff[count - 1] = '\0'; + + if ((strncmp(buff, "1", 2) == 0) || + (strncmp(buff, "enable", 7) == 0) || + (strncmp(buff, "enabled", 8) == 0)) + enabled = 1; + + if ((strncmp(buff, "0", 2) == 0) || + (strncmp(buff, "disable", 8) == 0) || + (strncmp(buff, "disabled", 9) == 0)) + enabled = 0; + + if (enabled < 0) { + bat_info(net_dev, + "%s: Invalid parameter received: %s\n", + attr_name, buff); + return -EINVAL; + } + + if (atomic_read(attr) == enabled) + return count; + + bat_info(net_dev, "%s: Changing from: %s to: %s\n", attr_name, + atomic_read(attr) == 1 ? "enabled" : "disabled", + enabled == 1 ? "enabled" : "disabled"); + + atomic_set(attr, (unsigned)enabled); + return count; +} + +static inline ssize_t __store_bool_attr(char *buff, size_t count, + void (*post_func)(struct net_device *), + struct attribute *attr, + atomic_t *attr_store, struct net_device *net_dev) +{ + int ret; + + ret = store_bool_attr(buff, count, net_dev, (char *)attr->name, + attr_store); + if (post_func && ret) + post_func(net_dev); + + return ret; +} + +static int store_uint_attr(char *buff, size_t count, + struct net_device *net_dev, char *attr_name, + unsigned int min, unsigned int max, atomic_t *attr) +{ + unsigned long uint_val; + int ret; + + ret = strict_strtoul(buff, 10, &uint_val); + if (ret) { + bat_info(net_dev, + "%s: Invalid parameter received: %s\n", + attr_name, buff); + return -EINVAL; + } + + if (uint_val < min) { + bat_info(net_dev, "%s: Value is too small: %lu min: %u\n", + attr_name, uint_val, min); + return -EINVAL; + } + + if (uint_val > max) { + bat_info(net_dev, "%s: Value is too big: %lu max: %u\n", + attr_name, uint_val, max); + return -EINVAL; + } + + if (atomic_read(attr) == uint_val) + return count; + + bat_info(net_dev, "%s: Changing from: %i to: %lu\n", + attr_name, atomic_read(attr), uint_val); + + atomic_set(attr, uint_val); + return count; +} + +static inline ssize_t __store_uint_attr(char *buff, size_t count, + int min, int max, + void (*post_func)(struct net_device *), + struct attribute *attr, + atomic_t *attr_store, struct net_device *net_dev) +{ + int ret; + + ret = store_uint_attr(buff, count, net_dev, (char *)attr->name, + min, max, attr_store); + if (post_func && ret) + post_func(net_dev); + + return ret; +} + +static ssize_t show_vis_mode(struct kobject *kobj, struct attribute *attr, + char *buff) +{ + struct bat_priv *bat_priv = kobj_to_batpriv(kobj); + int vis_mode = atomic_read(&bat_priv->vis_mode); + + return sprintf(buff, "%s\n", + vis_mode == VIS_TYPE_CLIENT_UPDATE ? + "client" : "server"); +} + +static ssize_t store_vis_mode(struct kobject *kobj, struct attribute *attr, + char *buff, size_t count) +{ + struct net_device *net_dev = kobj_to_netdev(kobj); + struct bat_priv *bat_priv = netdev_priv(net_dev); + unsigned long val; + int ret, vis_mode_tmp = -1; + + ret = strict_strtoul(buff, 10, &val); + + if (((count == 2) && (!ret) && (val == VIS_TYPE_CLIENT_UPDATE)) || + (strncmp(buff, "client", 6) == 0) || + (strncmp(buff, "off", 3) == 0)) + vis_mode_tmp = VIS_TYPE_CLIENT_UPDATE; + + if (((count == 2) && (!ret) && (val == VIS_TYPE_SERVER_SYNC)) || + (strncmp(buff, "server", 6) == 0)) + vis_mode_tmp = VIS_TYPE_SERVER_SYNC; + + if (vis_mode_tmp < 0) { + if (buff[count - 1] == '\n') + buff[count - 1] = '\0'; + + bat_info(net_dev, + "Invalid parameter for 'vis mode' setting received: " + "%s\n", buff); + return -EINVAL; + } + + if (atomic_read(&bat_priv->vis_mode) == vis_mode_tmp) + return count; + + bat_info(net_dev, "Changing vis mode from: %s to: %s\n", + atomic_read(&bat_priv->vis_mode) == VIS_TYPE_CLIENT_UPDATE ? + "client" : "server", vis_mode_tmp == VIS_TYPE_CLIENT_UPDATE ? + "client" : "server"); + + atomic_set(&bat_priv->vis_mode, (unsigned)vis_mode_tmp); + return count; +} + +static void post_gw_deselect(struct net_device *net_dev) +{ + struct bat_priv *bat_priv = netdev_priv(net_dev); + gw_deselect(bat_priv); +} + +static ssize_t show_gw_mode(struct kobject *kobj, struct attribute *attr, + char *buff) +{ + struct bat_priv *bat_priv = kobj_to_batpriv(kobj); + int bytes_written; + + switch (atomic_read(&bat_priv->gw_mode)) { + case GW_MODE_CLIENT: + bytes_written = sprintf(buff, "%s\n", GW_MODE_CLIENT_NAME); + break; + case GW_MODE_SERVER: + bytes_written = sprintf(buff, "%s\n", GW_MODE_SERVER_NAME); + break; + default: + bytes_written = sprintf(buff, "%s\n", GW_MODE_OFF_NAME); + break; + } + + return bytes_written; +} + +static ssize_t store_gw_mode(struct kobject *kobj, struct attribute *attr, + char *buff, size_t count) +{ + struct net_device *net_dev = kobj_to_netdev(kobj); + struct bat_priv *bat_priv = netdev_priv(net_dev); + char *curr_gw_mode_str; + int gw_mode_tmp = -1; + + if (buff[count - 1] == '\n') + buff[count - 1] = '\0'; + + if (strncmp(buff, GW_MODE_OFF_NAME, strlen(GW_MODE_OFF_NAME)) == 0) + gw_mode_tmp = GW_MODE_OFF; + + if (strncmp(buff, GW_MODE_CLIENT_NAME, + strlen(GW_MODE_CLIENT_NAME)) == 0) + gw_mode_tmp = GW_MODE_CLIENT; + + if (strncmp(buff, GW_MODE_SERVER_NAME, + strlen(GW_MODE_SERVER_NAME)) == 0) + gw_mode_tmp = GW_MODE_SERVER; + + if (gw_mode_tmp < 0) { + bat_info(net_dev, + "Invalid parameter for 'gw mode' setting received: " + "%s\n", buff); + return -EINVAL; + } + + if (atomic_read(&bat_priv->gw_mode) == gw_mode_tmp) + return count; + + switch (atomic_read(&bat_priv->gw_mode)) { + case GW_MODE_CLIENT: + curr_gw_mode_str = GW_MODE_CLIENT_NAME; + break; + case GW_MODE_SERVER: + curr_gw_mode_str = GW_MODE_SERVER_NAME; + break; + default: + curr_gw_mode_str = GW_MODE_OFF_NAME; + break; + } + + bat_info(net_dev, "Changing gw mode from: %s to: %s\n", + curr_gw_mode_str, buff); + + gw_deselect(bat_priv); + atomic_set(&bat_priv->gw_mode, (unsigned)gw_mode_tmp); + return count; +} + +static ssize_t show_gw_bwidth(struct kobject *kobj, struct attribute *attr, + char *buff) +{ + struct bat_priv *bat_priv = kobj_to_batpriv(kobj); + int down, up; + int gw_bandwidth = atomic_read(&bat_priv->gw_bandwidth); + + gw_bandwidth_to_kbit(gw_bandwidth, &down, &up); + return sprintf(buff, "%i%s/%i%s\n", + (down > 2048 ? down / 1024 : down), + (down > 2048 ? "MBit" : "KBit"), + (up > 2048 ? up / 1024 : up), + (up > 2048 ? "MBit" : "KBit")); +} + +static ssize_t store_gw_bwidth(struct kobject *kobj, struct attribute *attr, + char *buff, size_t count) +{ + struct net_device *net_dev = kobj_to_netdev(kobj); + + if (buff[count - 1] == '\n') + buff[count - 1] = '\0'; + + return gw_bandwidth_set(net_dev, buff, count); +} + +BAT_ATTR_BOOL(aggregated_ogms, S_IRUGO | S_IWUSR, NULL); +BAT_ATTR_BOOL(bonding, S_IRUGO | S_IWUSR, NULL); +BAT_ATTR_BOOL(fragmentation, S_IRUGO | S_IWUSR, update_min_mtu); +static BAT_ATTR(vis_mode, S_IRUGO | S_IWUSR, show_vis_mode, store_vis_mode); +static BAT_ATTR(gw_mode, S_IRUGO | S_IWUSR, show_gw_mode, store_gw_mode); +BAT_ATTR_UINT(orig_interval, S_IRUGO | S_IWUSR, 2 * JITTER, INT_MAX, NULL); +BAT_ATTR_UINT(hop_penalty, S_IRUGO | S_IWUSR, 0, TQ_MAX_VALUE, NULL); +BAT_ATTR_UINT(gw_sel_class, S_IRUGO | S_IWUSR, 1, TQ_MAX_VALUE, + post_gw_deselect); +static BAT_ATTR(gw_bandwidth, S_IRUGO | S_IWUSR, show_gw_bwidth, + store_gw_bwidth); +#ifdef CONFIG_BATMAN_ADV_DEBUG +BAT_ATTR_UINT(log_level, S_IRUGO | S_IWUSR, 0, 3, NULL); +#endif + +static struct bat_attribute *mesh_attrs[] = { + &bat_attr_aggregated_ogms, + &bat_attr_bonding, + &bat_attr_fragmentation, + &bat_attr_vis_mode, + &bat_attr_gw_mode, + &bat_attr_orig_interval, + &bat_attr_hop_penalty, + &bat_attr_gw_sel_class, + &bat_attr_gw_bandwidth, +#ifdef CONFIG_BATMAN_ADV_DEBUG + &bat_attr_log_level, +#endif + NULL, +}; + +int sysfs_add_meshif(struct net_device *dev) +{ + struct kobject *batif_kobject = &dev->dev.kobj; + struct bat_priv *bat_priv = netdev_priv(dev); + struct bat_attribute **bat_attr; + int err; + + bat_priv->mesh_obj = kobject_create_and_add(SYSFS_IF_MESH_SUBDIR, + batif_kobject); + if (!bat_priv->mesh_obj) { + bat_err(dev, "Can't add sysfs directory: %s/%s\n", dev->name, + SYSFS_IF_MESH_SUBDIR); + goto out; + } + + for (bat_attr = mesh_attrs; *bat_attr; ++bat_attr) { + err = sysfs_create_file(bat_priv->mesh_obj, + &((*bat_attr)->attr)); + if (err) { + bat_err(dev, "Can't add sysfs file: %s/%s/%s\n", + dev->name, SYSFS_IF_MESH_SUBDIR, + ((*bat_attr)->attr).name); + goto rem_attr; + } + } + + return 0; + +rem_attr: + for (bat_attr = mesh_attrs; *bat_attr; ++bat_attr) + sysfs_remove_file(bat_priv->mesh_obj, &((*bat_attr)->attr)); + + kobject_put(bat_priv->mesh_obj); + bat_priv->mesh_obj = NULL; +out: + return -ENOMEM; +} + +void sysfs_del_meshif(struct net_device *dev) +{ + struct bat_priv *bat_priv = netdev_priv(dev); + struct bat_attribute **bat_attr; + + for (bat_attr = mesh_attrs; *bat_attr; ++bat_attr) + sysfs_remove_file(bat_priv->mesh_obj, &((*bat_attr)->attr)); + + kobject_put(bat_priv->mesh_obj); + bat_priv->mesh_obj = NULL; +} + +static ssize_t show_mesh_iface(struct kobject *kobj, struct attribute *attr, + char *buff) +{ + struct net_device *net_dev = kobj_to_netdev(kobj); + struct batman_if *batman_if = get_batman_if_by_netdev(net_dev); + ssize_t length; + + if (!batman_if) + return 0; + + length = sprintf(buff, "%s\n", batman_if->if_status == IF_NOT_IN_USE ? + "none" : batman_if->soft_iface->name); + + kref_put(&batman_if->refcount, hardif_free_ref); + + return length; +} + +static ssize_t store_mesh_iface(struct kobject *kobj, struct attribute *attr, + char *buff, size_t count) +{ + struct net_device *net_dev = kobj_to_netdev(kobj); + struct batman_if *batman_if = get_batman_if_by_netdev(net_dev); + int status_tmp = -1; + int ret; + + if (!batman_if) + return count; + + if (buff[count - 1] == '\n') + buff[count - 1] = '\0'; + + if (strlen(buff) >= IFNAMSIZ) { + pr_err("Invalid parameter for 'mesh_iface' setting received: " + "interface name too long '%s'\n", buff); + kref_put(&batman_if->refcount, hardif_free_ref); + return -EINVAL; + } + + if (strncmp(buff, "none", 4) == 0) + status_tmp = IF_NOT_IN_USE; + else + status_tmp = IF_I_WANT_YOU; + + if ((batman_if->if_status == status_tmp) || ((batman_if->soft_iface) && + (strncmp(batman_if->soft_iface->name, buff, IFNAMSIZ) == 0))) { + kref_put(&batman_if->refcount, hardif_free_ref); + return count; + } + + if (status_tmp == IF_NOT_IN_USE) { + rtnl_lock(); + hardif_disable_interface(batman_if); + rtnl_unlock(); + kref_put(&batman_if->refcount, hardif_free_ref); + return count; + } + + /* if the interface already is in use */ + if (batman_if->if_status != IF_NOT_IN_USE) { + rtnl_lock(); + hardif_disable_interface(batman_if); + rtnl_unlock(); + } + + ret = hardif_enable_interface(batman_if, buff); + kref_put(&batman_if->refcount, hardif_free_ref); + + return ret; +} + +static ssize_t show_iface_status(struct kobject *kobj, struct attribute *attr, + char *buff) +{ + struct net_device *net_dev = kobj_to_netdev(kobj); + struct batman_if *batman_if = get_batman_if_by_netdev(net_dev); + ssize_t length; + + if (!batman_if) + return 0; + + switch (batman_if->if_status) { + case IF_TO_BE_REMOVED: + length = sprintf(buff, "disabling\n"); + break; + case IF_INACTIVE: + length = sprintf(buff, "inactive\n"); + break; + case IF_ACTIVE: + length = sprintf(buff, "active\n"); + break; + case IF_TO_BE_ACTIVATED: + length = sprintf(buff, "enabling\n"); + break; + case IF_NOT_IN_USE: + default: + length = sprintf(buff, "not in use\n"); + break; + } + + kref_put(&batman_if->refcount, hardif_free_ref); + + return length; +} + +static BAT_ATTR(mesh_iface, S_IRUGO | S_IWUSR, + show_mesh_iface, store_mesh_iface); +static BAT_ATTR(iface_status, S_IRUGO, show_iface_status, NULL); + +static struct bat_attribute *batman_attrs[] = { + &bat_attr_mesh_iface, + &bat_attr_iface_status, + NULL, +}; + +int sysfs_add_hardif(struct kobject **hardif_obj, struct net_device *dev) +{ + struct kobject *hardif_kobject = &dev->dev.kobj; + struct bat_attribute **bat_attr; + int err; + + *hardif_obj = kobject_create_and_add(SYSFS_IF_BAT_SUBDIR, + hardif_kobject); + + if (!*hardif_obj) { + bat_err(dev, "Can't add sysfs directory: %s/%s\n", dev->name, + SYSFS_IF_BAT_SUBDIR); + goto out; + } + + for (bat_attr = batman_attrs; *bat_attr; ++bat_attr) { + err = sysfs_create_file(*hardif_obj, &((*bat_attr)->attr)); + if (err) { + bat_err(dev, "Can't add sysfs file: %s/%s/%s\n", + dev->name, SYSFS_IF_BAT_SUBDIR, + ((*bat_attr)->attr).name); + goto rem_attr; + } + } + + return 0; + +rem_attr: + for (bat_attr = batman_attrs; *bat_attr; ++bat_attr) + sysfs_remove_file(*hardif_obj, &((*bat_attr)->attr)); +out: + return -ENOMEM; +} + +void sysfs_del_hardif(struct kobject **hardif_obj) +{ + kobject_put(*hardif_obj); + *hardif_obj = NULL; +} diff --git a/net/batman-adv/bat_sysfs.h b/net/batman-adv/bat_sysfs.h new file mode 100644 index 000000000000..7f186c007b4f --- /dev/null +++ b/net/batman-adv/bat_sysfs.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + + +#ifndef _NET_BATMAN_ADV_SYSFS_H_ +#define _NET_BATMAN_ADV_SYSFS_H_ + +#define SYSFS_IF_MESH_SUBDIR "mesh" +#define SYSFS_IF_BAT_SUBDIR "batman_adv" + +struct bat_attribute { + struct attribute attr; + ssize_t (*show)(struct kobject *kobj, struct attribute *attr, + char *buf); + ssize_t (*store)(struct kobject *kobj, struct attribute *attr, + char *buf, size_t count); +}; + +int sysfs_add_meshif(struct net_device *dev); +void sysfs_del_meshif(struct net_device *dev); +int sysfs_add_hardif(struct kobject **hardif_obj, struct net_device *dev); +void sysfs_del_hardif(struct kobject **hardif_obj); + +#endif /* _NET_BATMAN_ADV_SYSFS_H_ */ diff --git a/net/batman-adv/bitarray.c b/net/batman-adv/bitarray.c new file mode 100644 index 000000000000..bbcd8f744cdd --- /dev/null +++ b/net/batman-adv/bitarray.c @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2006-2010 B.A.T.M.A.N. contributors: + * + * Simon Wunderlich, Marek Lindner + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include "main.h" +#include "bitarray.h" + +#include + +/* returns true if the corresponding bit in the given seq_bits indicates true + * and curr_seqno is within range of last_seqno */ +uint8_t get_bit_status(unsigned long *seq_bits, uint32_t last_seqno, + uint32_t curr_seqno) +{ + int32_t diff, word_offset, word_num; + + diff = last_seqno - curr_seqno; + if (diff < 0 || diff >= TQ_LOCAL_WINDOW_SIZE) { + return 0; + } else { + /* which word */ + word_num = (last_seqno - curr_seqno) / WORD_BIT_SIZE; + /* which position in the selected word */ + word_offset = (last_seqno - curr_seqno) % WORD_BIT_SIZE; + + if (test_bit(word_offset, &seq_bits[word_num])) + return 1; + else + return 0; + } +} + +/* turn corresponding bit on, so we can remember that we got the packet */ +void bit_mark(unsigned long *seq_bits, int32_t n) +{ + int32_t word_offset, word_num; + + /* if too old, just drop it */ + if (n < 0 || n >= TQ_LOCAL_WINDOW_SIZE) + return; + + /* which word */ + word_num = n / WORD_BIT_SIZE; + /* which position in the selected word */ + word_offset = n % WORD_BIT_SIZE; + + set_bit(word_offset, &seq_bits[word_num]); /* turn the position on */ +} + +/* shift the packet array by n places. */ +static void bit_shift(unsigned long *seq_bits, int32_t n) +{ + int32_t word_offset, word_num; + int32_t i; + + if (n <= 0 || n >= TQ_LOCAL_WINDOW_SIZE) + return; + + word_offset = n % WORD_BIT_SIZE;/* shift how much inside each word */ + word_num = n / WORD_BIT_SIZE; /* shift over how much (full) words */ + + for (i = NUM_WORDS - 1; i > word_num; i--) { + /* going from old to new, so we don't overwrite the data we copy + * from. + * + * left is high, right is low: FEDC BA98 7654 3210 + * ^^ ^^ + * vvvv + * ^^^^ = from, vvvvv =to, we'd have word_num==1 and + * word_offset==WORD_BIT_SIZE/2 ????? in this example. + * (=24 bits) + * + * our desired output would be: 9876 5432 1000 0000 + * */ + + seq_bits[i] = + (seq_bits[i - word_num] << word_offset) + + /* take the lower port from the left half, shift it left + * to its final position */ + (seq_bits[i - word_num - 1] >> + (WORD_BIT_SIZE-word_offset)); + /* and the upper part of the right half and shift it left to + * it's position */ + /* for our example that would be: word[0] = 9800 + 0076 = + * 9876 */ + } + /* now for our last word, i==word_num, we only have the it's "left" + * half. that's the 1000 word in our example.*/ + + seq_bits[i] = (seq_bits[i - word_num] << word_offset); + + /* pad the rest with 0, if there is anything */ + i--; + + for (; i >= 0; i--) + seq_bits[i] = 0; +} + +static void bit_reset_window(unsigned long *seq_bits) +{ + int i; + for (i = 0; i < NUM_WORDS; i++) + seq_bits[i] = 0; +} + + +/* receive and process one packet within the sequence number window. + * + * returns: + * 1 if the window was moved (either new or very old) + * 0 if the window was not moved/shifted. + */ +char bit_get_packet(void *priv, unsigned long *seq_bits, + int32_t seq_num_diff, int8_t set_mark) +{ + struct bat_priv *bat_priv = (struct bat_priv *)priv; + + /* sequence number is slightly older. We already got a sequence number + * higher than this one, so we just mark it. */ + + if ((seq_num_diff <= 0) && (seq_num_diff > -TQ_LOCAL_WINDOW_SIZE)) { + if (set_mark) + bit_mark(seq_bits, -seq_num_diff); + return 0; + } + + /* sequence number is slightly newer, so we shift the window and + * set the mark if required */ + + if ((seq_num_diff > 0) && (seq_num_diff < TQ_LOCAL_WINDOW_SIZE)) { + bit_shift(seq_bits, seq_num_diff); + + if (set_mark) + bit_mark(seq_bits, 0); + return 1; + } + + /* sequence number is much newer, probably missed a lot of packets */ + + if ((seq_num_diff >= TQ_LOCAL_WINDOW_SIZE) + || (seq_num_diff < EXPECTED_SEQNO_RANGE)) { + bat_dbg(DBG_BATMAN, bat_priv, + "We missed a lot of packets (%i) !\n", + seq_num_diff - 1); + bit_reset_window(seq_bits); + if (set_mark) + bit_mark(seq_bits, 0); + return 1; + } + + /* received a much older packet. The other host either restarted + * or the old packet got delayed somewhere in the network. The + * packet should be dropped without calling this function if the + * seqno window is protected. */ + + if ((seq_num_diff <= -TQ_LOCAL_WINDOW_SIZE) + || (seq_num_diff >= EXPECTED_SEQNO_RANGE)) { + + bat_dbg(DBG_BATMAN, bat_priv, + "Other host probably restarted!\n"); + + bit_reset_window(seq_bits); + if (set_mark) + bit_mark(seq_bits, 0); + + return 1; + } + + /* never reached */ + return 0; +} + +/* count the hamming weight, how many good packets did we receive? just count + * the 1's. + */ +int bit_packet_count(unsigned long *seq_bits) +{ + int i, hamming = 0; + + for (i = 0; i < NUM_WORDS; i++) + hamming += hweight_long(seq_bits[i]); + + return hamming; +} diff --git a/net/batman-adv/bitarray.h b/net/batman-adv/bitarray.h new file mode 100644 index 000000000000..ac54017601b1 --- /dev/null +++ b/net/batman-adv/bitarray.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2006-2010 B.A.T.M.A.N. contributors: + * + * Simon Wunderlich, Marek Lindner + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#ifndef _NET_BATMAN_ADV_BITARRAY_H_ +#define _NET_BATMAN_ADV_BITARRAY_H_ + +#define WORD_BIT_SIZE (sizeof(unsigned long) * 8) + +/* returns true if the corresponding bit in the given seq_bits indicates true + * and curr_seqno is within range of last_seqno */ +uint8_t get_bit_status(unsigned long *seq_bits, uint32_t last_seqno, + uint32_t curr_seqno); + +/* turn corresponding bit on, so we can remember that we got the packet */ +void bit_mark(unsigned long *seq_bits, int32_t n); + + +/* receive and process one packet, returns 1 if received seq_num is considered + * new, 0 if old */ +char bit_get_packet(void *priv, unsigned long *seq_bits, + int32_t seq_num_diff, int8_t set_mark); + +/* count the hamming weight, how many good packets did we receive? */ +int bit_packet_count(unsigned long *seq_bits); + +#endif /* _NET_BATMAN_ADV_BITARRAY_H_ */ diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c new file mode 100644 index 000000000000..0065ffb8d96d --- /dev/null +++ b/net/batman-adv/gateway_client.c @@ -0,0 +1,477 @@ +/* + * Copyright (C) 2009-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include "main.h" +#include "gateway_client.h" +#include "gateway_common.h" +#include "hard-interface.h" +#include +#include +#include +#include + +static void gw_node_free_ref(struct kref *refcount) +{ + struct gw_node *gw_node; + + gw_node = container_of(refcount, struct gw_node, refcount); + kfree(gw_node); +} + +static void gw_node_free_rcu(struct rcu_head *rcu) +{ + struct gw_node *gw_node; + + gw_node = container_of(rcu, struct gw_node, rcu); + kref_put(&gw_node->refcount, gw_node_free_ref); +} + +void *gw_get_selected(struct bat_priv *bat_priv) +{ + struct gw_node *curr_gateway_tmp = bat_priv->curr_gw; + + if (!curr_gateway_tmp) + return NULL; + + return curr_gateway_tmp->orig_node; +} + +void gw_deselect(struct bat_priv *bat_priv) +{ + struct gw_node *gw_node = bat_priv->curr_gw; + + bat_priv->curr_gw = NULL; + + if (gw_node) + kref_put(&gw_node->refcount, gw_node_free_ref); +} + +static struct gw_node *gw_select(struct bat_priv *bat_priv, + struct gw_node *new_gw_node) +{ + struct gw_node *curr_gw_node = bat_priv->curr_gw; + + if (new_gw_node) + kref_get(&new_gw_node->refcount); + + bat_priv->curr_gw = new_gw_node; + return curr_gw_node; +} + +void gw_election(struct bat_priv *bat_priv) +{ + struct hlist_node *node; + struct gw_node *gw_node, *curr_gw_tmp = NULL, *old_gw_node = NULL; + uint8_t max_tq = 0; + uint32_t max_gw_factor = 0, tmp_gw_factor = 0; + int down, up; + + /** + * The batman daemon checks here if we already passed a full originator + * cycle in order to make sure we don't choose the first gateway we + * hear about. This check is based on the daemon's uptime which we + * don't have. + **/ + if (atomic_read(&bat_priv->gw_mode) != GW_MODE_CLIENT) + return; + + if (bat_priv->curr_gw) + return; + + rcu_read_lock(); + if (hlist_empty(&bat_priv->gw_list)) { + rcu_read_unlock(); + + if (bat_priv->curr_gw) { + bat_dbg(DBG_BATMAN, bat_priv, + "Removing selected gateway - " + "no gateway in range\n"); + gw_deselect(bat_priv); + } + + return; + } + + hlist_for_each_entry_rcu(gw_node, node, &bat_priv->gw_list, list) { + if (!gw_node->orig_node->router) + continue; + + if (gw_node->deleted) + continue; + + switch (atomic_read(&bat_priv->gw_sel_class)) { + case 1: /* fast connection */ + gw_bandwidth_to_kbit(gw_node->orig_node->gw_flags, + &down, &up); + + tmp_gw_factor = (gw_node->orig_node->router->tq_avg * + gw_node->orig_node->router->tq_avg * + down * 100 * 100) / + (TQ_LOCAL_WINDOW_SIZE * + TQ_LOCAL_WINDOW_SIZE * 64); + + if ((tmp_gw_factor > max_gw_factor) || + ((tmp_gw_factor == max_gw_factor) && + (gw_node->orig_node->router->tq_avg > max_tq))) + curr_gw_tmp = gw_node; + break; + + default: /** + * 2: stable connection (use best statistic) + * 3: fast-switch (use best statistic but change as + * soon as a better gateway appears) + * XX: late-switch (use best statistic but change as + * soon as a better gateway appears which has + * $routing_class more tq points) + **/ + if (gw_node->orig_node->router->tq_avg > max_tq) + curr_gw_tmp = gw_node; + break; + } + + if (gw_node->orig_node->router->tq_avg > max_tq) + max_tq = gw_node->orig_node->router->tq_avg; + + if (tmp_gw_factor > max_gw_factor) + max_gw_factor = tmp_gw_factor; + } + + if (bat_priv->curr_gw != curr_gw_tmp) { + if ((bat_priv->curr_gw) && (!curr_gw_tmp)) + bat_dbg(DBG_BATMAN, bat_priv, + "Removing selected gateway - " + "no gateway in range\n"); + else if ((!bat_priv->curr_gw) && (curr_gw_tmp)) + bat_dbg(DBG_BATMAN, bat_priv, + "Adding route to gateway %pM " + "(gw_flags: %i, tq: %i)\n", + curr_gw_tmp->orig_node->orig, + curr_gw_tmp->orig_node->gw_flags, + curr_gw_tmp->orig_node->router->tq_avg); + else + bat_dbg(DBG_BATMAN, bat_priv, + "Changing route to gateway %pM " + "(gw_flags: %i, tq: %i)\n", + curr_gw_tmp->orig_node->orig, + curr_gw_tmp->orig_node->gw_flags, + curr_gw_tmp->orig_node->router->tq_avg); + + old_gw_node = gw_select(bat_priv, curr_gw_tmp); + } + + rcu_read_unlock(); + + /* the kfree() has to be outside of the rcu lock */ + if (old_gw_node) + kref_put(&old_gw_node->refcount, gw_node_free_ref); +} + +void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node) +{ + struct gw_node *curr_gateway_tmp = bat_priv->curr_gw; + uint8_t gw_tq_avg, orig_tq_avg; + + if (!curr_gateway_tmp) + return; + + if (!curr_gateway_tmp->orig_node) + goto deselect; + + if (!curr_gateway_tmp->orig_node->router) + goto deselect; + + /* this node already is the gateway */ + if (curr_gateway_tmp->orig_node == orig_node) + return; + + if (!orig_node->router) + return; + + gw_tq_avg = curr_gateway_tmp->orig_node->router->tq_avg; + orig_tq_avg = orig_node->router->tq_avg; + + /* the TQ value has to be better */ + if (orig_tq_avg < gw_tq_avg) + return; + + /** + * if the routing class is greater than 3 the value tells us how much + * greater the TQ value of the new gateway must be + **/ + if ((atomic_read(&bat_priv->gw_sel_class) > 3) && + (orig_tq_avg - gw_tq_avg < atomic_read(&bat_priv->gw_sel_class))) + return; + + bat_dbg(DBG_BATMAN, bat_priv, + "Restarting gateway selection: better gateway found (tq curr: " + "%i, tq new: %i)\n", + gw_tq_avg, orig_tq_avg); + +deselect: + gw_deselect(bat_priv); +} + +static void gw_node_add(struct bat_priv *bat_priv, + struct orig_node *orig_node, uint8_t new_gwflags) +{ + struct gw_node *gw_node; + int down, up; + + gw_node = kmalloc(sizeof(struct gw_node), GFP_ATOMIC); + if (!gw_node) + return; + + memset(gw_node, 0, sizeof(struct gw_node)); + INIT_HLIST_NODE(&gw_node->list); + gw_node->orig_node = orig_node; + kref_init(&gw_node->refcount); + + spin_lock_bh(&bat_priv->gw_list_lock); + hlist_add_head_rcu(&gw_node->list, &bat_priv->gw_list); + spin_unlock_bh(&bat_priv->gw_list_lock); + + gw_bandwidth_to_kbit(new_gwflags, &down, &up); + bat_dbg(DBG_BATMAN, bat_priv, + "Found new gateway %pM -> gw_class: %i - %i%s/%i%s\n", + orig_node->orig, new_gwflags, + (down > 2048 ? down / 1024 : down), + (down > 2048 ? "MBit" : "KBit"), + (up > 2048 ? up / 1024 : up), + (up > 2048 ? "MBit" : "KBit")); +} + +void gw_node_update(struct bat_priv *bat_priv, + struct orig_node *orig_node, uint8_t new_gwflags) +{ + struct hlist_node *node; + struct gw_node *gw_node; + + rcu_read_lock(); + hlist_for_each_entry_rcu(gw_node, node, &bat_priv->gw_list, list) { + if (gw_node->orig_node != orig_node) + continue; + + bat_dbg(DBG_BATMAN, bat_priv, + "Gateway class of originator %pM changed from " + "%i to %i\n", + orig_node->orig, gw_node->orig_node->gw_flags, + new_gwflags); + + gw_node->deleted = 0; + + if (new_gwflags == 0) { + gw_node->deleted = jiffies; + bat_dbg(DBG_BATMAN, bat_priv, + "Gateway %pM removed from gateway list\n", + orig_node->orig); + + if (gw_node == bat_priv->curr_gw) { + rcu_read_unlock(); + gw_deselect(bat_priv); + return; + } + } + + rcu_read_unlock(); + return; + } + rcu_read_unlock(); + + if (new_gwflags == 0) + return; + + gw_node_add(bat_priv, orig_node, new_gwflags); +} + +void gw_node_delete(struct bat_priv *bat_priv, struct orig_node *orig_node) +{ + return gw_node_update(bat_priv, orig_node, 0); +} + +void gw_node_purge(struct bat_priv *bat_priv) +{ + struct gw_node *gw_node; + struct hlist_node *node, *node_tmp; + unsigned long timeout = 2 * PURGE_TIMEOUT * HZ; + + spin_lock_bh(&bat_priv->gw_list_lock); + + hlist_for_each_entry_safe(gw_node, node, node_tmp, + &bat_priv->gw_list, list) { + if (((!gw_node->deleted) || + (time_before(jiffies, gw_node->deleted + timeout))) && + atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE) + continue; + + if (bat_priv->curr_gw == gw_node) + gw_deselect(bat_priv); + + hlist_del_rcu(&gw_node->list); + call_rcu(&gw_node->rcu, gw_node_free_rcu); + } + + + spin_unlock_bh(&bat_priv->gw_list_lock); +} + +static int _write_buffer_text(struct bat_priv *bat_priv, + struct seq_file *seq, struct gw_node *gw_node) +{ + int down, up; + + gw_bandwidth_to_kbit(gw_node->orig_node->gw_flags, &down, &up); + + return seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %3i - %i%s/%i%s\n", + (bat_priv->curr_gw == gw_node ? "=>" : " "), + gw_node->orig_node->orig, + gw_node->orig_node->router->tq_avg, + gw_node->orig_node->router->addr, + gw_node->orig_node->router->if_incoming->net_dev->name, + gw_node->orig_node->gw_flags, + (down > 2048 ? down / 1024 : down), + (down > 2048 ? "MBit" : "KBit"), + (up > 2048 ? up / 1024 : up), + (up > 2048 ? "MBit" : "KBit")); +} + +int gw_client_seq_print_text(struct seq_file *seq, void *offset) +{ + struct net_device *net_dev = (struct net_device *)seq->private; + struct bat_priv *bat_priv = netdev_priv(net_dev); + struct gw_node *gw_node; + struct hlist_node *node; + int gw_count = 0; + + if (!bat_priv->primary_if) { + + return seq_printf(seq, "BATMAN mesh %s disabled - please " + "specify interfaces to enable it\n", + net_dev->name); + } + + if (bat_priv->primary_if->if_status != IF_ACTIVE) { + + return seq_printf(seq, "BATMAN mesh %s disabled - " + "primary interface not active\n", + net_dev->name); + } + + seq_printf(seq, " %-12s (%s/%i) %17s [%10s]: gw_class ... " + "[B.A.T.M.A.N. adv %s%s, MainIF/MAC: %s/%pM (%s)]\n", + "Gateway", "#", TQ_MAX_VALUE, "Nexthop", + "outgoingIF", SOURCE_VERSION, REVISION_VERSION_STR, + bat_priv->primary_if->net_dev->name, + bat_priv->primary_if->net_dev->dev_addr, net_dev->name); + + rcu_read_lock(); + hlist_for_each_entry_rcu(gw_node, node, &bat_priv->gw_list, list) { + if (gw_node->deleted) + continue; + + if (!gw_node->orig_node->router) + continue; + + _write_buffer_text(bat_priv, seq, gw_node); + gw_count++; + } + rcu_read_unlock(); + + if (gw_count == 0) + seq_printf(seq, "No gateways in range ...\n"); + + return 0; +} + +int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) +{ + struct ethhdr *ethhdr; + struct iphdr *iphdr; + struct ipv6hdr *ipv6hdr; + struct udphdr *udphdr; + unsigned int header_len = 0; + + if (atomic_read(&bat_priv->gw_mode) == GW_MODE_OFF) + return 0; + + /* check for ethernet header */ + if (!pskb_may_pull(skb, header_len + ETH_HLEN)) + return 0; + ethhdr = (struct ethhdr *)skb->data; + header_len += ETH_HLEN; + + /* check for initial vlan header */ + if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) { + if (!pskb_may_pull(skb, header_len + VLAN_HLEN)) + return 0; + ethhdr = (struct ethhdr *)(skb->data + VLAN_HLEN); + header_len += VLAN_HLEN; + } + + /* check for ip header */ + switch (ntohs(ethhdr->h_proto)) { + case ETH_P_IP: + if (!pskb_may_pull(skb, header_len + sizeof(struct iphdr))) + return 0; + iphdr = (struct iphdr *)(skb->data + header_len); + header_len += iphdr->ihl * 4; + + /* check for udp header */ + if (iphdr->protocol != IPPROTO_UDP) + return 0; + + break; + case ETH_P_IPV6: + if (!pskb_may_pull(skb, header_len + sizeof(struct ipv6hdr))) + return 0; + ipv6hdr = (struct ipv6hdr *)(skb->data + header_len); + header_len += sizeof(struct ipv6hdr); + + /* check for udp header */ + if (ipv6hdr->nexthdr != IPPROTO_UDP) + return 0; + + break; + default: + return 0; + } + + if (!pskb_may_pull(skb, header_len + sizeof(struct udphdr))) + return 0; + udphdr = (struct udphdr *)(skb->data + header_len); + header_len += sizeof(struct udphdr); + + /* check for bootp port */ + if ((ntohs(ethhdr->h_proto) == ETH_P_IP) && + (ntohs(udphdr->dest) != 67)) + return 0; + + if ((ntohs(ethhdr->h_proto) == ETH_P_IPV6) && + (ntohs(udphdr->dest) != 547)) + return 0; + + if (atomic_read(&bat_priv->gw_mode) == GW_MODE_SERVER) + return -1; + + if (!bat_priv->curr_gw) + return 0; + + return 1; +} diff --git a/net/batman-adv/gateway_client.h b/net/batman-adv/gateway_client.h new file mode 100644 index 000000000000..4585e6549844 --- /dev/null +++ b/net/batman-adv/gateway_client.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2009-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#ifndef _NET_BATMAN_ADV_GATEWAY_CLIENT_H_ +#define _NET_BATMAN_ADV_GATEWAY_CLIENT_H_ + +void gw_deselect(struct bat_priv *bat_priv); +void gw_election(struct bat_priv *bat_priv); +void *gw_get_selected(struct bat_priv *bat_priv); +void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node); +void gw_node_update(struct bat_priv *bat_priv, + struct orig_node *orig_node, uint8_t new_gwflags); +void gw_node_delete(struct bat_priv *bat_priv, struct orig_node *orig_node); +void gw_node_purge(struct bat_priv *bat_priv); +int gw_client_seq_print_text(struct seq_file *seq, void *offset); +int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb); + +#endif /* _NET_BATMAN_ADV_GATEWAY_CLIENT_H_ */ diff --git a/net/batman-adv/gateway_common.c b/net/batman-adv/gateway_common.c new file mode 100644 index 000000000000..b962982f017e --- /dev/null +++ b/net/batman-adv/gateway_common.c @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2009-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include "main.h" +#include "gateway_common.h" +#include "gateway_client.h" + +/* calculates the gateway class from kbit */ +static void kbit_to_gw_bandwidth(int down, int up, long *gw_srv_class) +{ + int mdown = 0, tdown, tup, difference; + uint8_t sbit, part; + + *gw_srv_class = 0; + difference = 0x0FFFFFFF; + + /* test all downspeeds */ + for (sbit = 0; sbit < 2; sbit++) { + for (part = 0; part < 16; part++) { + tdown = 32 * (sbit + 2) * (1 << part); + + if (abs(tdown - down) < difference) { + *gw_srv_class = (sbit << 7) + (part << 3); + difference = abs(tdown - down); + mdown = tdown; + } + } + } + + /* test all upspeeds */ + difference = 0x0FFFFFFF; + + for (part = 0; part < 8; part++) { + tup = ((part + 1) * (mdown)) / 8; + + if (abs(tup - up) < difference) { + *gw_srv_class = (*gw_srv_class & 0xF8) | part; + difference = abs(tup - up); + } + } +} + +/* returns the up and downspeeds in kbit, calculated from the class */ +void gw_bandwidth_to_kbit(uint8_t gw_srv_class, int *down, int *up) +{ + char sbit = (gw_srv_class & 0x80) >> 7; + char dpart = (gw_srv_class & 0x78) >> 3; + char upart = (gw_srv_class & 0x07); + + if (!gw_srv_class) { + *down = 0; + *up = 0; + return; + } + + *down = 32 * (sbit + 2) * (1 << dpart); + *up = ((upart + 1) * (*down)) / 8; +} + +static bool parse_gw_bandwidth(struct net_device *net_dev, char *buff, + long *up, long *down) +{ + int ret, multi = 1; + char *slash_ptr, *tmp_ptr; + + slash_ptr = strchr(buff, '/'); + if (slash_ptr) + *slash_ptr = 0; + + if (strlen(buff) > 4) { + tmp_ptr = buff + strlen(buff) - 4; + + if (strnicmp(tmp_ptr, "mbit", 4) == 0) + multi = 1024; + + if ((strnicmp(tmp_ptr, "kbit", 4) == 0) || + (multi > 1)) + *tmp_ptr = '\0'; + } + + ret = strict_strtoul(buff, 10, down); + if (ret) { + bat_err(net_dev, + "Download speed of gateway mode invalid: %s\n", + buff); + return false; + } + + *down *= multi; + + /* we also got some upload info */ + if (slash_ptr) { + multi = 1; + + if (strlen(slash_ptr + 1) > 4) { + tmp_ptr = slash_ptr + 1 - 4 + strlen(slash_ptr + 1); + + if (strnicmp(tmp_ptr, "mbit", 4) == 0) + multi = 1024; + + if ((strnicmp(tmp_ptr, "kbit", 4) == 0) || + (multi > 1)) + *tmp_ptr = '\0'; + } + + ret = strict_strtoul(slash_ptr + 1, 10, up); + if (ret) { + bat_err(net_dev, + "Upload speed of gateway mode invalid: " + "%s\n", slash_ptr + 1); + return false; + } + + *up *= multi; + } + + return true; +} + +ssize_t gw_bandwidth_set(struct net_device *net_dev, char *buff, size_t count) +{ + struct bat_priv *bat_priv = netdev_priv(net_dev); + long gw_bandwidth_tmp = 0, up = 0, down = 0; + bool ret; + + ret = parse_gw_bandwidth(net_dev, buff, &up, &down); + if (!ret) + goto end; + + if ((!down) || (down < 256)) + down = 2000; + + if (!up) + up = down / 5; + + kbit_to_gw_bandwidth(down, up, &gw_bandwidth_tmp); + + /** + * the gw bandwidth we guessed above might not match the given + * speeds, hence we need to calculate it back to show the number + * that is going to be propagated + **/ + gw_bandwidth_to_kbit((uint8_t)gw_bandwidth_tmp, + (int *)&down, (int *)&up); + + gw_deselect(bat_priv); + bat_info(net_dev, "Changing gateway bandwidth from: '%i' to: '%ld' " + "(propagating: %ld%s/%ld%s)\n", + atomic_read(&bat_priv->gw_bandwidth), gw_bandwidth_tmp, + (down > 2048 ? down / 1024 : down), + (down > 2048 ? "MBit" : "KBit"), + (up > 2048 ? up / 1024 : up), + (up > 2048 ? "MBit" : "KBit")); + + atomic_set(&bat_priv->gw_bandwidth, gw_bandwidth_tmp); + +end: + return count; +} diff --git a/net/batman-adv/gateway_common.h b/net/batman-adv/gateway_common.h new file mode 100644 index 000000000000..5e728d0b7959 --- /dev/null +++ b/net/batman-adv/gateway_common.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2009-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#ifndef _NET_BATMAN_ADV_GATEWAY_COMMON_H_ +#define _NET_BATMAN_ADV_GATEWAY_COMMON_H_ + +enum gw_modes { + GW_MODE_OFF, + GW_MODE_CLIENT, + GW_MODE_SERVER, +}; + +#define GW_MODE_OFF_NAME "off" +#define GW_MODE_CLIENT_NAME "client" +#define GW_MODE_SERVER_NAME "server" + +void gw_bandwidth_to_kbit(uint8_t gw_class, int *down, int *up); +ssize_t gw_bandwidth_set(struct net_device *net_dev, char *buff, size_t count); + +#endif /* _NET_BATMAN_ADV_GATEWAY_COMMON_H_ */ diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c new file mode 100644 index 000000000000..4f95777ce080 --- /dev/null +++ b/net/batman-adv/hard-interface.c @@ -0,0 +1,651 @@ +/* + * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner, Simon Wunderlich + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include "main.h" +#include "hard-interface.h" +#include "soft-interface.h" +#include "send.h" +#include "translation-table.h" +#include "routing.h" +#include "bat_sysfs.h" +#include "originator.h" +#include "hash.h" + +#include + +/* protect update critical side of if_list - but not the content */ +static DEFINE_SPINLOCK(if_list_lock); + +static void hardif_free_rcu(struct rcu_head *rcu) +{ + struct batman_if *batman_if; + + batman_if = container_of(rcu, struct batman_if, rcu); + dev_put(batman_if->net_dev); + kref_put(&batman_if->refcount, hardif_free_ref); +} + +struct batman_if *get_batman_if_by_netdev(struct net_device *net_dev) +{ + struct batman_if *batman_if; + + rcu_read_lock(); + list_for_each_entry_rcu(batman_if, &if_list, list) { + if (batman_if->net_dev == net_dev) + goto out; + } + + batman_if = NULL; + +out: + if (batman_if) + kref_get(&batman_if->refcount); + + rcu_read_unlock(); + return batman_if; +} + +static int is_valid_iface(struct net_device *net_dev) +{ + if (net_dev->flags & IFF_LOOPBACK) + return 0; + + if (net_dev->type != ARPHRD_ETHER) + return 0; + + if (net_dev->addr_len != ETH_ALEN) + return 0; + + /* no batman over batman */ +#ifdef HAVE_NET_DEVICE_OPS + if (net_dev->netdev_ops->ndo_start_xmit == interface_tx) + return 0; +#else + if (net_dev->hard_start_xmit == interface_tx) + return 0; +#endif + + /* Device is being bridged */ + /* if (net_dev->priv_flags & IFF_BRIDGE_PORT) + return 0; */ + + return 1; +} + +static struct batman_if *get_active_batman_if(struct net_device *soft_iface) +{ + struct batman_if *batman_if; + + rcu_read_lock(); + list_for_each_entry_rcu(batman_if, &if_list, list) { + if (batman_if->soft_iface != soft_iface) + continue; + + if (batman_if->if_status == IF_ACTIVE) + goto out; + } + + batman_if = NULL; + +out: + if (batman_if) + kref_get(&batman_if->refcount); + + rcu_read_unlock(); + return batman_if; +} + +static void update_primary_addr(struct bat_priv *bat_priv) +{ + struct vis_packet *vis_packet; + + vis_packet = (struct vis_packet *) + bat_priv->my_vis_info->skb_packet->data; + memcpy(vis_packet->vis_orig, + bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN); + memcpy(vis_packet->sender_orig, + bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN); +} + +static void set_primary_if(struct bat_priv *bat_priv, + struct batman_if *batman_if) +{ + struct batman_packet *batman_packet; + struct batman_if *old_if; + + if (batman_if) + kref_get(&batman_if->refcount); + + old_if = bat_priv->primary_if; + bat_priv->primary_if = batman_if; + + if (old_if) + kref_put(&old_if->refcount, hardif_free_ref); + + if (!bat_priv->primary_if) + return; + + batman_packet = (struct batman_packet *)(batman_if->packet_buff); + batman_packet->flags = PRIMARIES_FIRST_HOP; + batman_packet->ttl = TTL; + + update_primary_addr(bat_priv); + + /*** + * hacky trick to make sure that we send the HNA information via + * our new primary interface + */ + atomic_set(&bat_priv->hna_local_changed, 1); +} + +static bool hardif_is_iface_up(struct batman_if *batman_if) +{ + if (batman_if->net_dev->flags & IFF_UP) + return true; + + return false; +} + +static void update_mac_addresses(struct batman_if *batman_if) +{ + memcpy(((struct batman_packet *)(batman_if->packet_buff))->orig, + batman_if->net_dev->dev_addr, ETH_ALEN); + memcpy(((struct batman_packet *)(batman_if->packet_buff))->prev_sender, + batman_if->net_dev->dev_addr, ETH_ALEN); +} + +static void check_known_mac_addr(struct net_device *net_dev) +{ + struct batman_if *batman_if; + + rcu_read_lock(); + list_for_each_entry_rcu(batman_if, &if_list, list) { + if ((batman_if->if_status != IF_ACTIVE) && + (batman_if->if_status != IF_TO_BE_ACTIVATED)) + continue; + + if (batman_if->net_dev == net_dev) + continue; + + if (!compare_orig(batman_if->net_dev->dev_addr, + net_dev->dev_addr)) + continue; + + pr_warning("The newly added mac address (%pM) already exists " + "on: %s\n", net_dev->dev_addr, + batman_if->net_dev->name); + pr_warning("It is strongly recommended to keep mac addresses " + "unique to avoid problems!\n"); + } + rcu_read_unlock(); +} + +int hardif_min_mtu(struct net_device *soft_iface) +{ + struct bat_priv *bat_priv = netdev_priv(soft_iface); + struct batman_if *batman_if; + /* allow big frames if all devices are capable to do so + * (have MTU > 1500 + BAT_HEADER_LEN) */ + int min_mtu = ETH_DATA_LEN; + + if (atomic_read(&bat_priv->fragmentation)) + goto out; + + rcu_read_lock(); + list_for_each_entry_rcu(batman_if, &if_list, list) { + if ((batman_if->if_status != IF_ACTIVE) && + (batman_if->if_status != IF_TO_BE_ACTIVATED)) + continue; + + if (batman_if->soft_iface != soft_iface) + continue; + + min_mtu = min_t(int, batman_if->net_dev->mtu - BAT_HEADER_LEN, + min_mtu); + } + rcu_read_unlock(); +out: + return min_mtu; +} + +/* adjusts the MTU if a new interface with a smaller MTU appeared. */ +void update_min_mtu(struct net_device *soft_iface) +{ + int min_mtu; + + min_mtu = hardif_min_mtu(soft_iface); + if (soft_iface->mtu != min_mtu) + soft_iface->mtu = min_mtu; +} + +static void hardif_activate_interface(struct batman_if *batman_if) +{ + struct bat_priv *bat_priv; + + if (batman_if->if_status != IF_INACTIVE) + return; + + bat_priv = netdev_priv(batman_if->soft_iface); + + update_mac_addresses(batman_if); + batman_if->if_status = IF_TO_BE_ACTIVATED; + + /** + * the first active interface becomes our primary interface or + * the next active interface after the old primay interface was removed + */ + if (!bat_priv->primary_if) + set_primary_if(bat_priv, batman_if); + + bat_info(batman_if->soft_iface, "Interface activated: %s\n", + batman_if->net_dev->name); + + update_min_mtu(batman_if->soft_iface); + return; +} + +static void hardif_deactivate_interface(struct batman_if *batman_if) +{ + if ((batman_if->if_status != IF_ACTIVE) && + (batman_if->if_status != IF_TO_BE_ACTIVATED)) + return; + + batman_if->if_status = IF_INACTIVE; + + bat_info(batman_if->soft_iface, "Interface deactivated: %s\n", + batman_if->net_dev->name); + + update_min_mtu(batman_if->soft_iface); +} + +int hardif_enable_interface(struct batman_if *batman_if, char *iface_name) +{ + struct bat_priv *bat_priv; + struct batman_packet *batman_packet; + + if (batman_if->if_status != IF_NOT_IN_USE) + goto out; + + batman_if->soft_iface = dev_get_by_name(&init_net, iface_name); + + if (!batman_if->soft_iface) { + batman_if->soft_iface = softif_create(iface_name); + + if (!batman_if->soft_iface) + goto err; + + /* dev_get_by_name() increases the reference counter for us */ + dev_hold(batman_if->soft_iface); + } + + bat_priv = netdev_priv(batman_if->soft_iface); + batman_if->packet_len = BAT_PACKET_LEN; + batman_if->packet_buff = kmalloc(batman_if->packet_len, GFP_ATOMIC); + + if (!batman_if->packet_buff) { + bat_err(batman_if->soft_iface, "Can't add interface packet " + "(%s): out of memory\n", batman_if->net_dev->name); + goto err; + } + + batman_packet = (struct batman_packet *)(batman_if->packet_buff); + batman_packet->packet_type = BAT_PACKET; + batman_packet->version = COMPAT_VERSION; + batman_packet->flags = 0; + batman_packet->ttl = 2; + batman_packet->tq = TQ_MAX_VALUE; + batman_packet->num_hna = 0; + + batman_if->if_num = bat_priv->num_ifaces; + bat_priv->num_ifaces++; + batman_if->if_status = IF_INACTIVE; + orig_hash_add_if(batman_if, bat_priv->num_ifaces); + + batman_if->batman_adv_ptype.type = __constant_htons(ETH_P_BATMAN); + batman_if->batman_adv_ptype.func = batman_skb_recv; + batman_if->batman_adv_ptype.dev = batman_if->net_dev; + kref_get(&batman_if->refcount); + dev_add_pack(&batman_if->batman_adv_ptype); + + atomic_set(&batman_if->seqno, 1); + atomic_set(&batman_if->frag_seqno, 1); + bat_info(batman_if->soft_iface, "Adding interface: %s\n", + batman_if->net_dev->name); + + if (atomic_read(&bat_priv->fragmentation) && batman_if->net_dev->mtu < + ETH_DATA_LEN + BAT_HEADER_LEN) + bat_info(batman_if->soft_iface, + "The MTU of interface %s is too small (%i) to handle " + "the transport of batman-adv packets. Packets going " + "over this interface will be fragmented on layer2 " + "which could impact the performance. Setting the MTU " + "to %zi would solve the problem.\n", + batman_if->net_dev->name, batman_if->net_dev->mtu, + ETH_DATA_LEN + BAT_HEADER_LEN); + + if (!atomic_read(&bat_priv->fragmentation) && batman_if->net_dev->mtu < + ETH_DATA_LEN + BAT_HEADER_LEN) + bat_info(batman_if->soft_iface, + "The MTU of interface %s is too small (%i) to handle " + "the transport of batman-adv packets. If you experience" + " problems getting traffic through try increasing the " + "MTU to %zi.\n", + batman_if->net_dev->name, batman_if->net_dev->mtu, + ETH_DATA_LEN + BAT_HEADER_LEN); + + if (hardif_is_iface_up(batman_if)) + hardif_activate_interface(batman_if); + else + bat_err(batman_if->soft_iface, "Not using interface %s " + "(retrying later): interface not active\n", + batman_if->net_dev->name); + + /* begin scheduling originator messages on that interface */ + schedule_own_packet(batman_if); + +out: + return 0; + +err: + return -ENOMEM; +} + +void hardif_disable_interface(struct batman_if *batman_if) +{ + struct bat_priv *bat_priv = netdev_priv(batman_if->soft_iface); + + if (batman_if->if_status == IF_ACTIVE) + hardif_deactivate_interface(batman_if); + + if (batman_if->if_status != IF_INACTIVE) + return; + + bat_info(batman_if->soft_iface, "Removing interface: %s\n", + batman_if->net_dev->name); + dev_remove_pack(&batman_if->batman_adv_ptype); + kref_put(&batman_if->refcount, hardif_free_ref); + + bat_priv->num_ifaces--; + orig_hash_del_if(batman_if, bat_priv->num_ifaces); + + if (batman_if == bat_priv->primary_if) { + struct batman_if *new_if; + + new_if = get_active_batman_if(batman_if->soft_iface); + set_primary_if(bat_priv, new_if); + + if (new_if) + kref_put(&new_if->refcount, hardif_free_ref); + } + + kfree(batman_if->packet_buff); + batman_if->packet_buff = NULL; + batman_if->if_status = IF_NOT_IN_USE; + + /* delete all references to this batman_if */ + purge_orig_ref(bat_priv); + purge_outstanding_packets(bat_priv, batman_if); + dev_put(batman_if->soft_iface); + + /* nobody uses this interface anymore */ + if (!bat_priv->num_ifaces) + softif_destroy(batman_if->soft_iface); + + batman_if->soft_iface = NULL; +} + +static struct batman_if *hardif_add_interface(struct net_device *net_dev) +{ + struct batman_if *batman_if; + int ret; + + ret = is_valid_iface(net_dev); + if (ret != 1) + goto out; + + dev_hold(net_dev); + + batman_if = kmalloc(sizeof(struct batman_if), GFP_ATOMIC); + if (!batman_if) { + pr_err("Can't add interface (%s): out of memory\n", + net_dev->name); + goto release_dev; + } + + ret = sysfs_add_hardif(&batman_if->hardif_obj, net_dev); + if (ret) + goto free_if; + + batman_if->if_num = -1; + batman_if->net_dev = net_dev; + batman_if->soft_iface = NULL; + batman_if->if_status = IF_NOT_IN_USE; + INIT_LIST_HEAD(&batman_if->list); + kref_init(&batman_if->refcount); + + check_known_mac_addr(batman_if->net_dev); + + spin_lock(&if_list_lock); + list_add_tail_rcu(&batman_if->list, &if_list); + spin_unlock(&if_list_lock); + + /* extra reference for return */ + kref_get(&batman_if->refcount); + return batman_if; + +free_if: + kfree(batman_if); +release_dev: + dev_put(net_dev); +out: + return NULL; +} + +static void hardif_remove_interface(struct batman_if *batman_if) +{ + /* first deactivate interface */ + if (batman_if->if_status != IF_NOT_IN_USE) + hardif_disable_interface(batman_if); + + if (batman_if->if_status != IF_NOT_IN_USE) + return; + + batman_if->if_status = IF_TO_BE_REMOVED; + sysfs_del_hardif(&batman_if->hardif_obj); + call_rcu(&batman_if->rcu, hardif_free_rcu); +} + +void hardif_remove_interfaces(void) +{ + struct batman_if *batman_if, *batman_if_tmp; + struct list_head if_queue; + + INIT_LIST_HEAD(&if_queue); + + spin_lock(&if_list_lock); + list_for_each_entry_safe(batman_if, batman_if_tmp, &if_list, list) { + list_del_rcu(&batman_if->list); + list_add_tail(&batman_if->list, &if_queue); + } + spin_unlock(&if_list_lock); + + rtnl_lock(); + list_for_each_entry_safe(batman_if, batman_if_tmp, &if_queue, list) { + hardif_remove_interface(batman_if); + } + rtnl_unlock(); +} + +static int hard_if_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct net_device *net_dev = (struct net_device *)ptr; + struct batman_if *batman_if = get_batman_if_by_netdev(net_dev); + struct bat_priv *bat_priv; + + if (!batman_if && event == NETDEV_REGISTER) + batman_if = hardif_add_interface(net_dev); + + if (!batman_if) + goto out; + + switch (event) { + case NETDEV_UP: + hardif_activate_interface(batman_if); + break; + case NETDEV_GOING_DOWN: + case NETDEV_DOWN: + hardif_deactivate_interface(batman_if); + break; + case NETDEV_UNREGISTER: + spin_lock(&if_list_lock); + list_del_rcu(&batman_if->list); + spin_unlock(&if_list_lock); + + hardif_remove_interface(batman_if); + break; + case NETDEV_CHANGEMTU: + if (batman_if->soft_iface) + update_min_mtu(batman_if->soft_iface); + break; + case NETDEV_CHANGEADDR: + if (batman_if->if_status == IF_NOT_IN_USE) + goto hardif_put; + + check_known_mac_addr(batman_if->net_dev); + update_mac_addresses(batman_if); + + bat_priv = netdev_priv(batman_if->soft_iface); + if (batman_if == bat_priv->primary_if) + update_primary_addr(bat_priv); + break; + default: + break; + }; + +hardif_put: + kref_put(&batman_if->refcount, hardif_free_ref); +out: + return NOTIFY_DONE; +} + +/* receive a packet with the batman ethertype coming on a hard + * interface */ +int batman_skb_recv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *ptype, struct net_device *orig_dev) +{ + struct bat_priv *bat_priv; + struct batman_packet *batman_packet; + struct batman_if *batman_if; + int ret; + + batman_if = container_of(ptype, struct batman_if, batman_adv_ptype); + skb = skb_share_check(skb, GFP_ATOMIC); + + /* skb was released by skb_share_check() */ + if (!skb) + goto err_out; + + /* packet should hold at least type and version */ + if (unlikely(!pskb_may_pull(skb, 2))) + goto err_free; + + /* expect a valid ethernet header here. */ + if (unlikely(skb->mac_len != sizeof(struct ethhdr) + || !skb_mac_header(skb))) + goto err_free; + + if (!batman_if->soft_iface) + goto err_free; + + bat_priv = netdev_priv(batman_if->soft_iface); + + if (atomic_read(&bat_priv->mesh_state) != MESH_ACTIVE) + goto err_free; + + /* discard frames on not active interfaces */ + if (batman_if->if_status != IF_ACTIVE) + goto err_free; + + batman_packet = (struct batman_packet *)skb->data; + + if (batman_packet->version != COMPAT_VERSION) { + bat_dbg(DBG_BATMAN, bat_priv, + "Drop packet: incompatible batman version (%i)\n", + batman_packet->version); + goto err_free; + } + + /* all receive handlers return whether they received or reused + * the supplied skb. if not, we have to free the skb. */ + + switch (batman_packet->packet_type) { + /* batman originator packet */ + case BAT_PACKET: + ret = recv_bat_packet(skb, batman_if); + break; + + /* batman icmp packet */ + case BAT_ICMP: + ret = recv_icmp_packet(skb, batman_if); + break; + + /* unicast packet */ + case BAT_UNICAST: + ret = recv_unicast_packet(skb, batman_if); + break; + + /* fragmented unicast packet */ + case BAT_UNICAST_FRAG: + ret = recv_ucast_frag_packet(skb, batman_if); + break; + + /* broadcast packet */ + case BAT_BCAST: + ret = recv_bcast_packet(skb, batman_if); + break; + + /* vis packet */ + case BAT_VIS: + ret = recv_vis_packet(skb, batman_if); + break; + default: + ret = NET_RX_DROP; + } + + if (ret == NET_RX_DROP) + kfree_skb(skb); + + /* return NET_RX_SUCCESS in any case as we + * most probably dropped the packet for + * routing-logical reasons. */ + + return NET_RX_SUCCESS; + +err_free: + kfree_skb(skb); +err_out: + return NET_RX_DROP; +} + +struct notifier_block hard_if_notifier = { + .notifier_call = hard_if_event, +}; diff --git a/net/batman-adv/hard-interface.h b/net/batman-adv/hard-interface.h new file mode 100644 index 000000000000..30ec3b8db459 --- /dev/null +++ b/net/batman-adv/hard-interface.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner, Simon Wunderlich + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#ifndef _NET_BATMAN_ADV_HARD_INTERFACE_H_ +#define _NET_BATMAN_ADV_HARD_INTERFACE_H_ + +#define IF_NOT_IN_USE 0 +#define IF_TO_BE_REMOVED 1 +#define IF_INACTIVE 2 +#define IF_ACTIVE 3 +#define IF_TO_BE_ACTIVATED 4 +#define IF_I_WANT_YOU 5 + +extern struct notifier_block hard_if_notifier; + +struct batman_if *get_batman_if_by_netdev(struct net_device *net_dev); +int hardif_enable_interface(struct batman_if *batman_if, char *iface_name); +void hardif_disable_interface(struct batman_if *batman_if); +void hardif_remove_interfaces(void); +int batman_skb_recv(struct sk_buff *skb, + struct net_device *dev, + struct packet_type *ptype, + struct net_device *orig_dev); +int hardif_min_mtu(struct net_device *soft_iface); +void update_min_mtu(struct net_device *soft_iface); + +static inline void hardif_free_ref(struct kref *refcount) +{ + struct batman_if *batman_if; + + batman_if = container_of(refcount, struct batman_if, refcount); + kfree(batman_if); +} + +#endif /* _NET_BATMAN_ADV_HARD_INTERFACE_H_ */ diff --git a/net/batman-adv/hash.c b/net/batman-adv/hash.c new file mode 100644 index 000000000000..26e623eb9def --- /dev/null +++ b/net/batman-adv/hash.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2006-2010 B.A.T.M.A.N. contributors: + * + * Simon Wunderlich, Marek Lindner + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include "main.h" +#include "hash.h" + +/* clears the hash */ +static void hash_init(struct hashtable_t *hash) +{ + int i; + + for (i = 0 ; i < hash->size; i++) + INIT_HLIST_HEAD(&hash->table[i]); +} + +/* free only the hashtable and the hash itself. */ +void hash_destroy(struct hashtable_t *hash) +{ + kfree(hash->table); + kfree(hash); +} + +/* allocates and clears the hash */ +struct hashtable_t *hash_new(int size) +{ + struct hashtable_t *hash; + + hash = kmalloc(sizeof(struct hashtable_t) , GFP_ATOMIC); + + if (!hash) + return NULL; + + hash->size = size; + hash->table = kmalloc(sizeof(struct element_t *) * size, GFP_ATOMIC); + + if (!hash->table) { + kfree(hash); + return NULL; + } + + hash_init(hash); + + return hash; +} diff --git a/net/batman-adv/hash.h b/net/batman-adv/hash.h new file mode 100644 index 000000000000..09216ade16f1 --- /dev/null +++ b/net/batman-adv/hash.h @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2006-2010 B.A.T.M.A.N. contributors: + * + * Simon Wunderlich, Marek Lindner + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#ifndef _NET_BATMAN_ADV_HASH_H_ +#define _NET_BATMAN_ADV_HASH_H_ + +#include + +/* callback to a compare function. should + * compare 2 element datas for their keys, + * return 0 if same and not 0 if not + * same */ +typedef int (*hashdata_compare_cb)(void *, void *); + +/* the hashfunction, should return an index + * based on the key in the data of the first + * argument and the size the second */ +typedef int (*hashdata_choose_cb)(void *, int); +typedef void (*hashdata_free_cb)(void *, void *); + +struct element_t { + void *data; /* pointer to the data */ + struct hlist_node hlist; /* bucket list pointer */ +}; + +struct hashtable_t { + struct hlist_head *table; /* the hashtable itself, with the buckets */ + int size; /* size of hashtable */ +}; + +/* allocates and clears the hash */ +struct hashtable_t *hash_new(int size); + +/* remove element if you already found the element you want to delete and don't + * need the overhead to find it again with hash_remove(). But usually, you + * don't want to use this function, as it fiddles with hash-internals. */ +void *hash_remove_element(struct hashtable_t *hash, struct element_t *elem); + +/* free only the hashtable and the hash itself. */ +void hash_destroy(struct hashtable_t *hash); + +/* remove the hash structure. if hashdata_free_cb != NULL, this function will be + * called to remove the elements inside of the hash. if you don't remove the + * elements, memory might be leaked. */ +static inline void hash_delete(struct hashtable_t *hash, + hashdata_free_cb free_cb, void *arg) +{ + struct hlist_head *head; + struct hlist_node *walk, *safe; + struct element_t *bucket; + int i; + + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + hlist_for_each_safe(walk, safe, head) { + bucket = hlist_entry(walk, struct element_t, hlist); + if (free_cb) + free_cb(bucket->data, arg); + + hlist_del(walk); + kfree(bucket); + } + } + + hash_destroy(hash); +} + +/* adds data to the hashtable. returns 0 on success, -1 on error */ +static inline int hash_add(struct hashtable_t *hash, + hashdata_compare_cb compare, + hashdata_choose_cb choose, void *data) +{ + int index; + struct hlist_head *head; + struct hlist_node *walk, *safe; + struct element_t *bucket; + + if (!hash) + return -1; + + index = choose(data, hash->size); + head = &hash->table[index]; + + hlist_for_each_safe(walk, safe, head) { + bucket = hlist_entry(walk, struct element_t, hlist); + if (compare(bucket->data, data)) + return -1; + } + + /* no duplicate found in list, add new element */ + bucket = kmalloc(sizeof(struct element_t), GFP_ATOMIC); + + if (!bucket) + return -1; + + bucket->data = data; + hlist_add_head(&bucket->hlist, head); + + return 0; +} + +/* removes data from hash, if found. returns pointer do data on success, so you + * can remove the used structure yourself, or NULL on error . data could be the + * structure you use with just the key filled, we just need the key for + * comparing. */ +static inline void *hash_remove(struct hashtable_t *hash, + hashdata_compare_cb compare, + hashdata_choose_cb choose, void *data) +{ + size_t index; + struct hlist_node *walk; + struct element_t *bucket; + struct hlist_head *head; + void *data_save; + + index = choose(data, hash->size); + head = &hash->table[index]; + + hlist_for_each_entry(bucket, walk, head, hlist) { + if (compare(bucket->data, data)) { + data_save = bucket->data; + hlist_del(walk); + kfree(bucket); + return data_save; + } + } + + return NULL; +} + +/* finds data, based on the key in keydata. returns the found data on success, + * or NULL on error */ +static inline void *hash_find(struct hashtable_t *hash, + hashdata_compare_cb compare, + hashdata_choose_cb choose, void *keydata) +{ + int index; + struct hlist_head *head; + struct hlist_node *walk; + struct element_t *bucket; + + if (!hash) + return NULL; + + index = choose(keydata , hash->size); + head = &hash->table[index]; + + hlist_for_each(walk, head) { + bucket = hlist_entry(walk, struct element_t, hlist); + if (compare(bucket->data, keydata)) + return bucket->data; + } + + return NULL; +} + +#endif /* _NET_BATMAN_ADV_HASH_H_ */ diff --git a/net/batman-adv/icmp_socket.c b/net/batman-adv/icmp_socket.c new file mode 100644 index 000000000000..ecf6d7ffab2e --- /dev/null +++ b/net/batman-adv/icmp_socket.c @@ -0,0 +1,356 @@ +/* + * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include "main.h" +#include +#include +#include "icmp_socket.h" +#include "send.h" +#include "types.h" +#include "hash.h" +#include "originator.h" +#include "hard-interface.h" + +static struct socket_client *socket_client_hash[256]; + +static void bat_socket_add_packet(struct socket_client *socket_client, + struct icmp_packet_rr *icmp_packet, + size_t icmp_len); + +void bat_socket_init(void) +{ + memset(socket_client_hash, 0, sizeof(socket_client_hash)); +} + +static int bat_socket_open(struct inode *inode, struct file *file) +{ + unsigned int i; + struct socket_client *socket_client; + + nonseekable_open(inode, file); + + socket_client = kmalloc(sizeof(struct socket_client), GFP_KERNEL); + + if (!socket_client) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(socket_client_hash); i++) { + if (!socket_client_hash[i]) { + socket_client_hash[i] = socket_client; + break; + } + } + + if (i == ARRAY_SIZE(socket_client_hash)) { + pr_err("Error - can't add another packet client: " + "maximum number of clients reached\n"); + kfree(socket_client); + return -EXFULL; + } + + INIT_LIST_HEAD(&socket_client->queue_list); + socket_client->queue_len = 0; + socket_client->index = i; + socket_client->bat_priv = inode->i_private; + spin_lock_init(&socket_client->lock); + init_waitqueue_head(&socket_client->queue_wait); + + file->private_data = socket_client; + + inc_module_count(); + return 0; +} + +static int bat_socket_release(struct inode *inode, struct file *file) +{ + struct socket_client *socket_client = file->private_data; + struct socket_packet *socket_packet; + struct list_head *list_pos, *list_pos_tmp; + + spin_lock_bh(&socket_client->lock); + + /* for all packets in the queue ... */ + list_for_each_safe(list_pos, list_pos_tmp, &socket_client->queue_list) { + socket_packet = list_entry(list_pos, + struct socket_packet, list); + + list_del(list_pos); + kfree(socket_packet); + } + + socket_client_hash[socket_client->index] = NULL; + spin_unlock_bh(&socket_client->lock); + + kfree(socket_client); + dec_module_count(); + + return 0; +} + +static ssize_t bat_socket_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct socket_client *socket_client = file->private_data; + struct socket_packet *socket_packet; + size_t packet_len; + int error; + + if ((file->f_flags & O_NONBLOCK) && (socket_client->queue_len == 0)) + return -EAGAIN; + + if ((!buf) || (count < sizeof(struct icmp_packet))) + return -EINVAL; + + if (!access_ok(VERIFY_WRITE, buf, count)) + return -EFAULT; + + error = wait_event_interruptible(socket_client->queue_wait, + socket_client->queue_len); + + if (error) + return error; + + spin_lock_bh(&socket_client->lock); + + socket_packet = list_first_entry(&socket_client->queue_list, + struct socket_packet, list); + list_del(&socket_packet->list); + socket_client->queue_len--; + + spin_unlock_bh(&socket_client->lock); + + error = __copy_to_user(buf, &socket_packet->icmp_packet, + socket_packet->icmp_len); + + packet_len = socket_packet->icmp_len; + kfree(socket_packet); + + if (error) + return -EFAULT; + + return packet_len; +} + +static ssize_t bat_socket_write(struct file *file, const char __user *buff, + size_t len, loff_t *off) +{ + struct socket_client *socket_client = file->private_data; + struct bat_priv *bat_priv = socket_client->bat_priv; + struct sk_buff *skb; + struct icmp_packet_rr *icmp_packet; + + struct orig_node *orig_node; + struct batman_if *batman_if; + size_t packet_len = sizeof(struct icmp_packet); + uint8_t dstaddr[ETH_ALEN]; + + if (len < sizeof(struct icmp_packet)) { + bat_dbg(DBG_BATMAN, bat_priv, + "Error - can't send packet from char device: " + "invalid packet size\n"); + return -EINVAL; + } + + if (!bat_priv->primary_if) + return -EFAULT; + + if (len >= sizeof(struct icmp_packet_rr)) + packet_len = sizeof(struct icmp_packet_rr); + + skb = dev_alloc_skb(packet_len + sizeof(struct ethhdr)); + if (!skb) + return -ENOMEM; + + skb_reserve(skb, sizeof(struct ethhdr)); + icmp_packet = (struct icmp_packet_rr *)skb_put(skb, packet_len); + + if (!access_ok(VERIFY_READ, buff, packet_len)) { + len = -EFAULT; + goto free_skb; + } + + if (__copy_from_user(icmp_packet, buff, packet_len)) { + len = -EFAULT; + goto free_skb; + } + + if (icmp_packet->packet_type != BAT_ICMP) { + bat_dbg(DBG_BATMAN, bat_priv, + "Error - can't send packet from char device: " + "got bogus packet type (expected: BAT_ICMP)\n"); + len = -EINVAL; + goto free_skb; + } + + if (icmp_packet->msg_type != ECHO_REQUEST) { + bat_dbg(DBG_BATMAN, bat_priv, + "Error - can't send packet from char device: " + "got bogus message type (expected: ECHO_REQUEST)\n"); + len = -EINVAL; + goto free_skb; + } + + icmp_packet->uid = socket_client->index; + + if (icmp_packet->version != COMPAT_VERSION) { + icmp_packet->msg_type = PARAMETER_PROBLEM; + icmp_packet->ttl = COMPAT_VERSION; + bat_socket_add_packet(socket_client, icmp_packet, packet_len); + goto free_skb; + } + + if (atomic_read(&bat_priv->mesh_state) != MESH_ACTIVE) + goto dst_unreach; + + spin_lock_bh(&bat_priv->orig_hash_lock); + orig_node = ((struct orig_node *)hash_find(bat_priv->orig_hash, + compare_orig, choose_orig, + icmp_packet->dst)); + + if (!orig_node) + goto unlock; + + if (!orig_node->router) + goto unlock; + + batman_if = orig_node->router->if_incoming; + memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); + + spin_unlock_bh(&bat_priv->orig_hash_lock); + + if (!batman_if) + goto dst_unreach; + + if (batman_if->if_status != IF_ACTIVE) + goto dst_unreach; + + memcpy(icmp_packet->orig, + bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN); + + if (packet_len == sizeof(struct icmp_packet_rr)) + memcpy(icmp_packet->rr, batman_if->net_dev->dev_addr, ETH_ALEN); + + + send_skb_packet(skb, batman_if, dstaddr); + + goto out; + +unlock: + spin_unlock_bh(&bat_priv->orig_hash_lock); +dst_unreach: + icmp_packet->msg_type = DESTINATION_UNREACHABLE; + bat_socket_add_packet(socket_client, icmp_packet, packet_len); +free_skb: + kfree_skb(skb); +out: + return len; +} + +static unsigned int bat_socket_poll(struct file *file, poll_table *wait) +{ + struct socket_client *socket_client = file->private_data; + + poll_wait(file, &socket_client->queue_wait, wait); + + if (socket_client->queue_len > 0) + return POLLIN | POLLRDNORM; + + return 0; +} + +static const struct file_operations fops = { + .owner = THIS_MODULE, + .open = bat_socket_open, + .release = bat_socket_release, + .read = bat_socket_read, + .write = bat_socket_write, + .poll = bat_socket_poll, + .llseek = no_llseek, +}; + +int bat_socket_setup(struct bat_priv *bat_priv) +{ + struct dentry *d; + + if (!bat_priv->debug_dir) + goto err; + + d = debugfs_create_file(ICMP_SOCKET, S_IFREG | S_IWUSR | S_IRUSR, + bat_priv->debug_dir, bat_priv, &fops); + if (d) + goto err; + + return 0; + +err: + return 1; +} + +static void bat_socket_add_packet(struct socket_client *socket_client, + struct icmp_packet_rr *icmp_packet, + size_t icmp_len) +{ + struct socket_packet *socket_packet; + + socket_packet = kmalloc(sizeof(struct socket_packet), GFP_ATOMIC); + + if (!socket_packet) + return; + + INIT_LIST_HEAD(&socket_packet->list); + memcpy(&socket_packet->icmp_packet, icmp_packet, icmp_len); + socket_packet->icmp_len = icmp_len; + + spin_lock_bh(&socket_client->lock); + + /* while waiting for the lock the socket_client could have been + * deleted */ + if (!socket_client_hash[icmp_packet->uid]) { + spin_unlock_bh(&socket_client->lock); + kfree(socket_packet); + return; + } + + list_add_tail(&socket_packet->list, &socket_client->queue_list); + socket_client->queue_len++; + + if (socket_client->queue_len > 100) { + socket_packet = list_first_entry(&socket_client->queue_list, + struct socket_packet, list); + + list_del(&socket_packet->list); + kfree(socket_packet); + socket_client->queue_len--; + } + + spin_unlock_bh(&socket_client->lock); + + wake_up(&socket_client->queue_wait); +} + +void bat_socket_receive_packet(struct icmp_packet_rr *icmp_packet, + size_t icmp_len) +{ + struct socket_client *hash = socket_client_hash[icmp_packet->uid]; + + if (hash) + bat_socket_add_packet(hash, icmp_packet, icmp_len); +} diff --git a/net/batman-adv/icmp_socket.h b/net/batman-adv/icmp_socket.h new file mode 100644 index 000000000000..bf9b348cde27 --- /dev/null +++ b/net/batman-adv/icmp_socket.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#ifndef _NET_BATMAN_ADV_ICMP_SOCKET_H_ +#define _NET_BATMAN_ADV_ICMP_SOCKET_H_ + +#include "types.h" + +#define ICMP_SOCKET "socket" + +void bat_socket_init(void); +int bat_socket_setup(struct bat_priv *bat_priv); +void bat_socket_receive_packet(struct icmp_packet_rr *icmp_packet, + size_t icmp_len); + +#endif /* _NET_BATMAN_ADV_ICMP_SOCKET_H_ */ diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c new file mode 100644 index 000000000000..b827f6a158cb --- /dev/null +++ b/net/batman-adv/main.c @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner, Simon Wunderlich + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include "main.h" +#include "bat_sysfs.h" +#include "bat_debugfs.h" +#include "routing.h" +#include "send.h" +#include "originator.h" +#include "soft-interface.h" +#include "icmp_socket.h" +#include "translation-table.h" +#include "hard-interface.h" +#include "gateway_client.h" +#include "types.h" +#include "vis.h" +#include "hash.h" + +struct list_head if_list; + +unsigned char broadcast_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + +struct workqueue_struct *bat_event_workqueue; + +static int __init batman_init(void) +{ + INIT_LIST_HEAD(&if_list); + + /* the name should not be longer than 10 chars - see + * http://lwn.net/Articles/23634/ */ + bat_event_workqueue = create_singlethread_workqueue("bat_events"); + + if (!bat_event_workqueue) + return -ENOMEM; + + bat_socket_init(); + debugfs_init(); + + register_netdevice_notifier(&hard_if_notifier); + + pr_info("B.A.T.M.A.N. advanced %s%s (compatibility version %i) " + "loaded\n", SOURCE_VERSION, REVISION_VERSION_STR, + COMPAT_VERSION); + + return 0; +} + +static void __exit batman_exit(void) +{ + debugfs_destroy(); + unregister_netdevice_notifier(&hard_if_notifier); + hardif_remove_interfaces(); + + flush_workqueue(bat_event_workqueue); + destroy_workqueue(bat_event_workqueue); + bat_event_workqueue = NULL; + + rcu_barrier(); +} + +int mesh_init(struct net_device *soft_iface) +{ + struct bat_priv *bat_priv = netdev_priv(soft_iface); + + spin_lock_init(&bat_priv->orig_hash_lock); + spin_lock_init(&bat_priv->forw_bat_list_lock); + spin_lock_init(&bat_priv->forw_bcast_list_lock); + spin_lock_init(&bat_priv->hna_lhash_lock); + spin_lock_init(&bat_priv->hna_ghash_lock); + spin_lock_init(&bat_priv->gw_list_lock); + spin_lock_init(&bat_priv->vis_hash_lock); + spin_lock_init(&bat_priv->vis_list_lock); + spin_lock_init(&bat_priv->softif_neigh_lock); + + INIT_HLIST_HEAD(&bat_priv->forw_bat_list); + INIT_HLIST_HEAD(&bat_priv->forw_bcast_list); + INIT_HLIST_HEAD(&bat_priv->gw_list); + INIT_HLIST_HEAD(&bat_priv->softif_neigh_list); + + if (originator_init(bat_priv) < 1) + goto err; + + if (hna_local_init(bat_priv) < 1) + goto err; + + if (hna_global_init(bat_priv) < 1) + goto err; + + hna_local_add(soft_iface, soft_iface->dev_addr); + + if (vis_init(bat_priv) < 1) + goto err; + + atomic_set(&bat_priv->mesh_state, MESH_ACTIVE); + goto end; + +err: + pr_err("Unable to allocate memory for mesh information structures: " + "out of mem ?\n"); + mesh_free(soft_iface); + return -1; + +end: + return 0; +} + +void mesh_free(struct net_device *soft_iface) +{ + struct bat_priv *bat_priv = netdev_priv(soft_iface); + + atomic_set(&bat_priv->mesh_state, MESH_DEACTIVATING); + + purge_outstanding_packets(bat_priv, NULL); + + vis_quit(bat_priv); + + gw_node_purge(bat_priv); + originator_free(bat_priv); + + hna_local_free(bat_priv); + hna_global_free(bat_priv); + + softif_neigh_purge(bat_priv); + + atomic_set(&bat_priv->mesh_state, MESH_INACTIVE); +} + +void inc_module_count(void) +{ + try_module_get(THIS_MODULE); +} + +void dec_module_count(void) +{ + module_put(THIS_MODULE); +} + +int is_my_mac(uint8_t *addr) +{ + struct batman_if *batman_if; + + rcu_read_lock(); + list_for_each_entry_rcu(batman_if, &if_list, list) { + if (batman_if->if_status != IF_ACTIVE) + continue; + + if (compare_orig(batman_if->net_dev->dev_addr, addr)) { + rcu_read_unlock(); + return 1; + } + } + rcu_read_unlock(); + return 0; + +} + +module_init(batman_init); +module_exit(batman_exit); + +MODULE_LICENSE("GPL"); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_SUPPORTED_DEVICE(DRIVER_DEVICE); +#ifdef REVISION_VERSION +MODULE_VERSION(SOURCE_VERSION "-" REVISION_VERSION); +#else +MODULE_VERSION(SOURCE_VERSION); +#endif diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h new file mode 100644 index 000000000000..d4d9926c2201 --- /dev/null +++ b/net/batman-adv/main.h @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner, Simon Wunderlich + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#ifndef _NET_BATMAN_ADV_MAIN_H_ +#define _NET_BATMAN_ADV_MAIN_H_ + +/* Kernel Programming */ +#define LINUX + +#define DRIVER_AUTHOR "Marek Lindner , " \ + "Simon Wunderlich " +#define DRIVER_DESC "B.A.T.M.A.N. advanced" +#define DRIVER_DEVICE "batman-adv" + +#define SOURCE_VERSION "next" + + +/* B.A.T.M.A.N. parameters */ + +#define TQ_MAX_VALUE 255 +#define JITTER 20 +#define TTL 50 /* Time To Live of broadcast messages */ + +#define PURGE_TIMEOUT 200 /* purge originators after time in seconds if no + * valid packet comes in -> TODO: check + * influence on TQ_LOCAL_WINDOW_SIZE */ +#define LOCAL_HNA_TIMEOUT 3600 /* in seconds */ + +#define TQ_LOCAL_WINDOW_SIZE 64 /* sliding packet range of received originator + * messages in squence numbers (should be a + * multiple of our word size) */ +#define TQ_GLOBAL_WINDOW_SIZE 5 +#define TQ_LOCAL_BIDRECT_SEND_MINIMUM 1 +#define TQ_LOCAL_BIDRECT_RECV_MINIMUM 1 +#define TQ_TOTAL_BIDRECT_LIMIT 1 + +#define NUM_WORDS (TQ_LOCAL_WINDOW_SIZE / WORD_BIT_SIZE) + +#define PACKBUFF_SIZE 2000 +#define LOG_BUF_LEN 8192 /* has to be a power of 2 */ + +#define VIS_INTERVAL 5000 /* 5 seconds */ + +/* how much worse secondary interfaces may be to + * to be considered as bonding candidates */ + +#define BONDING_TQ_THRESHOLD 50 + +#define MAX_AGGREGATION_BYTES 512 /* should not be bigger than 512 bytes or + * change the size of + * forw_packet->direct_link_flags */ +#define MAX_AGGREGATION_MS 100 + +#define SOFTIF_NEIGH_TIMEOUT 180000 /* 3 minutes */ + +#define RESET_PROTECTION_MS 30000 +#define EXPECTED_SEQNO_RANGE 65536 +/* don't reset again within 30 seconds */ + +#define MESH_INACTIVE 0 +#define MESH_ACTIVE 1 +#define MESH_DEACTIVATING 2 + +#define BCAST_QUEUE_LEN 256 +#define BATMAN_QUEUE_LEN 256 + +/* + * Debug Messages + */ +#ifdef pr_fmt +#undef pr_fmt +#endif +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt /* Append 'batman-adv: ' before + * kernel messages */ + +#define DBG_BATMAN 1 /* all messages related to routing / flooding / + * broadcasting / etc */ +#define DBG_ROUTES 2 /* route or hna added / changed / deleted */ +#define DBG_ALL 3 + +#define LOG_BUF_LEN 8192 /* has to be a power of 2 */ + + +/* + * Vis + */ + +/* #define VIS_SUBCLUSTERS_DISABLED */ + +/* + * Kernel headers + */ + +#include /* mutex */ +#include /* needed by all modules */ +#include /* netdevice */ +#include /* ethernet address classifaction */ +#include /* ethernet header */ +#include /* poll_table */ +#include /* kernel threads */ +#include /* schedule types */ +#include /* workqueue */ +#include +#include /* struct sock */ +#include +#include +#include "types.h" + +#ifndef REVISION_VERSION +#define REVISION_VERSION_STR "" +#else +#define REVISION_VERSION_STR " "REVISION_VERSION +#endif + +extern struct list_head if_list; + +extern unsigned char broadcast_addr[]; +extern struct workqueue_struct *bat_event_workqueue; + +int mesh_init(struct net_device *soft_iface); +void mesh_free(struct net_device *soft_iface); +void inc_module_count(void); +void dec_module_count(void); +int is_my_mac(uint8_t *addr); + +#ifdef CONFIG_BATMAN_ADV_DEBUG +int debug_log(struct bat_priv *bat_priv, char *fmt, ...); + +#define bat_dbg(type, bat_priv, fmt, arg...) \ + do { \ + if (atomic_read(&bat_priv->log_level) & type) \ + debug_log(bat_priv, fmt, ## arg); \ + } \ + while (0) +#else /* !CONFIG_BATMAN_ADV_DEBUG */ +static inline void bat_dbg(char type __attribute__((unused)), + struct bat_priv *bat_priv __attribute__((unused)), + char *fmt __attribute__((unused)), ...) +{ +} +#endif + +#define bat_warning(net_dev, fmt, arg...) \ + do { \ + struct net_device *_netdev = (net_dev); \ + struct bat_priv *_batpriv = netdev_priv(_netdev); \ + bat_dbg(DBG_ALL, _batpriv, fmt, ## arg); \ + pr_warning("%s: " fmt, _netdev->name, ## arg); \ + } while (0) +#define bat_info(net_dev, fmt, arg...) \ + do { \ + struct net_device *_netdev = (net_dev); \ + struct bat_priv *_batpriv = netdev_priv(_netdev); \ + bat_dbg(DBG_ALL, _batpriv, fmt, ## arg); \ + pr_info("%s: " fmt, _netdev->name, ## arg); \ + } while (0) +#define bat_err(net_dev, fmt, arg...) \ + do { \ + struct net_device *_netdev = (net_dev); \ + struct bat_priv *_batpriv = netdev_priv(_netdev); \ + bat_dbg(DBG_ALL, _batpriv, fmt, ## arg); \ + pr_err("%s: " fmt, _netdev->name, ## arg); \ + } while (0) + +#endif /* _NET_BATMAN_ADV_MAIN_H_ */ diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c new file mode 100644 index 000000000000..6b7fb6b7e6f9 --- /dev/null +++ b/net/batman-adv/originator.c @@ -0,0 +1,564 @@ +/* + * Copyright (C) 2009-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner, Simon Wunderlich + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +/* increase the reference counter for this originator */ + +#include "main.h" +#include "originator.h" +#include "hash.h" +#include "translation-table.h" +#include "routing.h" +#include "gateway_client.h" +#include "hard-interface.h" +#include "unicast.h" +#include "soft-interface.h" + +static void purge_orig(struct work_struct *work); + +static void start_purge_timer(struct bat_priv *bat_priv) +{ + INIT_DELAYED_WORK(&bat_priv->orig_work, purge_orig); + queue_delayed_work(bat_event_workqueue, &bat_priv->orig_work, 1 * HZ); +} + +int originator_init(struct bat_priv *bat_priv) +{ + if (bat_priv->orig_hash) + return 1; + + spin_lock_bh(&bat_priv->orig_hash_lock); + bat_priv->orig_hash = hash_new(1024); + + if (!bat_priv->orig_hash) + goto err; + + spin_unlock_bh(&bat_priv->orig_hash_lock); + start_purge_timer(bat_priv); + return 1; + +err: + spin_unlock_bh(&bat_priv->orig_hash_lock); + return 0; +} + +struct neigh_node * +create_neighbor(struct orig_node *orig_node, struct orig_node *orig_neigh_node, + uint8_t *neigh, struct batman_if *if_incoming) +{ + struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); + struct neigh_node *neigh_node; + + bat_dbg(DBG_BATMAN, bat_priv, + "Creating new last-hop neighbor of originator\n"); + + neigh_node = kzalloc(sizeof(struct neigh_node), GFP_ATOMIC); + if (!neigh_node) + return NULL; + + INIT_LIST_HEAD(&neigh_node->list); + + memcpy(neigh_node->addr, neigh, ETH_ALEN); + neigh_node->orig_node = orig_neigh_node; + neigh_node->if_incoming = if_incoming; + + list_add_tail(&neigh_node->list, &orig_node->neigh_list); + return neigh_node; +} + +static void free_orig_node(void *data, void *arg) +{ + struct list_head *list_pos, *list_pos_tmp; + struct neigh_node *neigh_node; + struct orig_node *orig_node = (struct orig_node *)data; + struct bat_priv *bat_priv = (struct bat_priv *)arg; + + /* for all neighbors towards this originator ... */ + list_for_each_safe(list_pos, list_pos_tmp, &orig_node->neigh_list) { + neigh_node = list_entry(list_pos, struct neigh_node, list); + + list_del(list_pos); + kfree(neigh_node); + } + + frag_list_free(&orig_node->frag_list); + hna_global_del_orig(bat_priv, orig_node, "originator timed out"); + + kfree(orig_node->bcast_own); + kfree(orig_node->bcast_own_sum); + kfree(orig_node); +} + +void originator_free(struct bat_priv *bat_priv) +{ + if (!bat_priv->orig_hash) + return; + + cancel_delayed_work_sync(&bat_priv->orig_work); + + spin_lock_bh(&bat_priv->orig_hash_lock); + hash_delete(bat_priv->orig_hash, free_orig_node, bat_priv); + bat_priv->orig_hash = NULL; + spin_unlock_bh(&bat_priv->orig_hash_lock); +} + +/* this function finds or creates an originator entry for the given + * address if it does not exits */ +struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr) +{ + struct orig_node *orig_node; + int size; + int hash_added; + + orig_node = ((struct orig_node *)hash_find(bat_priv->orig_hash, + compare_orig, choose_orig, + addr)); + + if (orig_node) + return orig_node; + + bat_dbg(DBG_BATMAN, bat_priv, + "Creating new originator: %pM\n", addr); + + orig_node = kzalloc(sizeof(struct orig_node), GFP_ATOMIC); + if (!orig_node) + return NULL; + + INIT_LIST_HEAD(&orig_node->neigh_list); + + memcpy(orig_node->orig, addr, ETH_ALEN); + orig_node->router = NULL; + orig_node->hna_buff = NULL; + orig_node->bcast_seqno_reset = jiffies - 1 + - msecs_to_jiffies(RESET_PROTECTION_MS); + orig_node->batman_seqno_reset = jiffies - 1 + - msecs_to_jiffies(RESET_PROTECTION_MS); + + size = bat_priv->num_ifaces * sizeof(unsigned long) * NUM_WORDS; + + orig_node->bcast_own = kzalloc(size, GFP_ATOMIC); + if (!orig_node->bcast_own) + goto free_orig_node; + + size = bat_priv->num_ifaces * sizeof(uint8_t); + orig_node->bcast_own_sum = kzalloc(size, GFP_ATOMIC); + + INIT_LIST_HEAD(&orig_node->frag_list); + orig_node->last_frag_packet = 0; + + if (!orig_node->bcast_own_sum) + goto free_bcast_own; + + hash_added = hash_add(bat_priv->orig_hash, compare_orig, choose_orig, + orig_node); + if (hash_added < 0) + goto free_bcast_own_sum; + + return orig_node; +free_bcast_own_sum: + kfree(orig_node->bcast_own_sum); +free_bcast_own: + kfree(orig_node->bcast_own); +free_orig_node: + kfree(orig_node); + return NULL; +} + +static bool purge_orig_neighbors(struct bat_priv *bat_priv, + struct orig_node *orig_node, + struct neigh_node **best_neigh_node) +{ + struct list_head *list_pos, *list_pos_tmp; + struct neigh_node *neigh_node; + bool neigh_purged = false; + + *best_neigh_node = NULL; + + /* for all neighbors towards this originator ... */ + list_for_each_safe(list_pos, list_pos_tmp, &orig_node->neigh_list) { + neigh_node = list_entry(list_pos, struct neigh_node, list); + + if ((time_after(jiffies, + neigh_node->last_valid + PURGE_TIMEOUT * HZ)) || + (neigh_node->if_incoming->if_status == IF_INACTIVE) || + (neigh_node->if_incoming->if_status == IF_TO_BE_REMOVED)) { + + if (neigh_node->if_incoming->if_status == + IF_TO_BE_REMOVED) + bat_dbg(DBG_BATMAN, bat_priv, + "neighbor purge: originator %pM, " + "neighbor: %pM, iface: %s\n", + orig_node->orig, neigh_node->addr, + neigh_node->if_incoming->net_dev->name); + else + bat_dbg(DBG_BATMAN, bat_priv, + "neighbor timeout: originator %pM, " + "neighbor: %pM, last_valid: %lu\n", + orig_node->orig, neigh_node->addr, + (neigh_node->last_valid / HZ)); + + neigh_purged = true; + list_del(list_pos); + kfree(neigh_node); + } else { + if ((!*best_neigh_node) || + (neigh_node->tq_avg > (*best_neigh_node)->tq_avg)) + *best_neigh_node = neigh_node; + } + } + return neigh_purged; +} + +static bool purge_orig_node(struct bat_priv *bat_priv, + struct orig_node *orig_node) +{ + struct neigh_node *best_neigh_node; + + if (time_after(jiffies, + orig_node->last_valid + 2 * PURGE_TIMEOUT * HZ)) { + + bat_dbg(DBG_BATMAN, bat_priv, + "Originator timeout: originator %pM, last_valid %lu\n", + orig_node->orig, (orig_node->last_valid / HZ)); + return true; + } else { + if (purge_orig_neighbors(bat_priv, orig_node, + &best_neigh_node)) { + update_routes(bat_priv, orig_node, + best_neigh_node, + orig_node->hna_buff, + orig_node->hna_buff_len); + /* update bonding candidates, we could have lost + * some candidates. */ + update_bonding_candidates(bat_priv, orig_node); + } + } + + return false; +} + +static void _purge_orig(struct bat_priv *bat_priv) +{ + struct hashtable_t *hash = bat_priv->orig_hash; + struct hlist_node *walk, *safe; + struct hlist_head *head; + struct element_t *bucket; + struct orig_node *orig_node; + int i; + + if (!hash) + return; + + spin_lock_bh(&bat_priv->orig_hash_lock); + + /* for all origins... */ + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + hlist_for_each_entry_safe(bucket, walk, safe, head, hlist) { + orig_node = bucket->data; + + if (purge_orig_node(bat_priv, orig_node)) { + if (orig_node->gw_flags) + gw_node_delete(bat_priv, orig_node); + hlist_del(walk); + kfree(bucket); + free_orig_node(orig_node, bat_priv); + } + + if (time_after(jiffies, orig_node->last_frag_packet + + msecs_to_jiffies(FRAG_TIMEOUT))) + frag_list_free(&orig_node->frag_list); + } + } + + spin_unlock_bh(&bat_priv->orig_hash_lock); + + gw_node_purge(bat_priv); + gw_election(bat_priv); + + softif_neigh_purge(bat_priv); +} + +static void purge_orig(struct work_struct *work) +{ + struct delayed_work *delayed_work = + container_of(work, struct delayed_work, work); + struct bat_priv *bat_priv = + container_of(delayed_work, struct bat_priv, orig_work); + + _purge_orig(bat_priv); + start_purge_timer(bat_priv); +} + +void purge_orig_ref(struct bat_priv *bat_priv) +{ + _purge_orig(bat_priv); +} + +int orig_seq_print_text(struct seq_file *seq, void *offset) +{ + struct net_device *net_dev = (struct net_device *)seq->private; + struct bat_priv *bat_priv = netdev_priv(net_dev); + struct hashtable_t *hash = bat_priv->orig_hash; + struct hlist_node *walk; + struct hlist_head *head; + struct element_t *bucket; + struct orig_node *orig_node; + struct neigh_node *neigh_node; + int batman_count = 0; + int last_seen_secs; + int last_seen_msecs; + int i; + + if ((!bat_priv->primary_if) || + (bat_priv->primary_if->if_status != IF_ACTIVE)) { + if (!bat_priv->primary_if) + return seq_printf(seq, "BATMAN mesh %s disabled - " + "please specify interfaces to enable it\n", + net_dev->name); + + return seq_printf(seq, "BATMAN mesh %s " + "disabled - primary interface not active\n", + net_dev->name); + } + + seq_printf(seq, "[B.A.T.M.A.N. adv %s%s, MainIF/MAC: %s/%pM (%s)]\n", + SOURCE_VERSION, REVISION_VERSION_STR, + bat_priv->primary_if->net_dev->name, + bat_priv->primary_if->net_dev->dev_addr, net_dev->name); + seq_printf(seq, " %-15s %s (%s/%i) %17s [%10s]: %20s ...\n", + "Originator", "last-seen", "#", TQ_MAX_VALUE, "Nexthop", + "outgoingIF", "Potential nexthops"); + + spin_lock_bh(&bat_priv->orig_hash_lock); + + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + hlist_for_each_entry(bucket, walk, head, hlist) { + orig_node = bucket->data; + + if (!orig_node->router) + continue; + + if (orig_node->router->tq_avg == 0) + continue; + + last_seen_secs = jiffies_to_msecs(jiffies - + orig_node->last_valid) / 1000; + last_seen_msecs = jiffies_to_msecs(jiffies - + orig_node->last_valid) % 1000; + + neigh_node = orig_node->router; + seq_printf(seq, "%pM %4i.%03is (%3i) %pM [%10s]:", + orig_node->orig, last_seen_secs, + last_seen_msecs, neigh_node->tq_avg, + neigh_node->addr, + neigh_node->if_incoming->net_dev->name); + + list_for_each_entry(neigh_node, &orig_node->neigh_list, + list) { + seq_printf(seq, " %pM (%3i)", neigh_node->addr, + neigh_node->tq_avg); + } + + seq_printf(seq, "\n"); + batman_count++; + } + } + + spin_unlock_bh(&bat_priv->orig_hash_lock); + + if ((batman_count == 0)) + seq_printf(seq, "No batman nodes in range ...\n"); + + return 0; +} + +static int orig_node_add_if(struct orig_node *orig_node, int max_if_num) +{ + void *data_ptr; + + data_ptr = kmalloc(max_if_num * sizeof(unsigned long) * NUM_WORDS, + GFP_ATOMIC); + if (!data_ptr) { + pr_err("Can't resize orig: out of memory\n"); + return -1; + } + + memcpy(data_ptr, orig_node->bcast_own, + (max_if_num - 1) * sizeof(unsigned long) * NUM_WORDS); + kfree(orig_node->bcast_own); + orig_node->bcast_own = data_ptr; + + data_ptr = kmalloc(max_if_num * sizeof(uint8_t), GFP_ATOMIC); + if (!data_ptr) { + pr_err("Can't resize orig: out of memory\n"); + return -1; + } + + memcpy(data_ptr, orig_node->bcast_own_sum, + (max_if_num - 1) * sizeof(uint8_t)); + kfree(orig_node->bcast_own_sum); + orig_node->bcast_own_sum = data_ptr; + + return 0; +} + +int orig_hash_add_if(struct batman_if *batman_if, int max_if_num) +{ + struct bat_priv *bat_priv = netdev_priv(batman_if->soft_iface); + struct hashtable_t *hash = bat_priv->orig_hash; + struct hlist_node *walk; + struct hlist_head *head; + struct element_t *bucket; + struct orig_node *orig_node; + int i; + + /* resize all orig nodes because orig_node->bcast_own(_sum) depend on + * if_num */ + spin_lock_bh(&bat_priv->orig_hash_lock); + + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + hlist_for_each_entry(bucket, walk, head, hlist) { + orig_node = bucket->data; + + if (orig_node_add_if(orig_node, max_if_num) == -1) + goto err; + } + } + + spin_unlock_bh(&bat_priv->orig_hash_lock); + return 0; + +err: + spin_unlock_bh(&bat_priv->orig_hash_lock); + return -ENOMEM; +} + +static int orig_node_del_if(struct orig_node *orig_node, + int max_if_num, int del_if_num) +{ + void *data_ptr = NULL; + int chunk_size; + + /* last interface was removed */ + if (max_if_num == 0) + goto free_bcast_own; + + chunk_size = sizeof(unsigned long) * NUM_WORDS; + data_ptr = kmalloc(max_if_num * chunk_size, GFP_ATOMIC); + if (!data_ptr) { + pr_err("Can't resize orig: out of memory\n"); + return -1; + } + + /* copy first part */ + memcpy(data_ptr, orig_node->bcast_own, del_if_num * chunk_size); + + /* copy second part */ + memcpy(data_ptr + del_if_num * chunk_size, + orig_node->bcast_own + ((del_if_num + 1) * chunk_size), + (max_if_num - del_if_num) * chunk_size); + +free_bcast_own: + kfree(orig_node->bcast_own); + orig_node->bcast_own = data_ptr; + + if (max_if_num == 0) + goto free_own_sum; + + data_ptr = kmalloc(max_if_num * sizeof(uint8_t), GFP_ATOMIC); + if (!data_ptr) { + pr_err("Can't resize orig: out of memory\n"); + return -1; + } + + memcpy(data_ptr, orig_node->bcast_own_sum, + del_if_num * sizeof(uint8_t)); + + memcpy(data_ptr + del_if_num * sizeof(uint8_t), + orig_node->bcast_own_sum + ((del_if_num + 1) * sizeof(uint8_t)), + (max_if_num - del_if_num) * sizeof(uint8_t)); + +free_own_sum: + kfree(orig_node->bcast_own_sum); + orig_node->bcast_own_sum = data_ptr; + + return 0; +} + +int orig_hash_del_if(struct batman_if *batman_if, int max_if_num) +{ + struct bat_priv *bat_priv = netdev_priv(batman_if->soft_iface); + struct hashtable_t *hash = bat_priv->orig_hash; + struct hlist_node *walk; + struct hlist_head *head; + struct element_t *bucket; + struct batman_if *batman_if_tmp; + struct orig_node *orig_node; + int i, ret; + + /* resize all orig nodes because orig_node->bcast_own(_sum) depend on + * if_num */ + spin_lock_bh(&bat_priv->orig_hash_lock); + + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + hlist_for_each_entry(bucket, walk, head, hlist) { + orig_node = bucket->data; + + ret = orig_node_del_if(orig_node, max_if_num, + batman_if->if_num); + + if (ret == -1) + goto err; + } + } + + /* renumber remaining batman interfaces _inside_ of orig_hash_lock */ + rcu_read_lock(); + list_for_each_entry_rcu(batman_if_tmp, &if_list, list) { + if (batman_if_tmp->if_status == IF_NOT_IN_USE) + continue; + + if (batman_if == batman_if_tmp) + continue; + + if (batman_if->soft_iface != batman_if_tmp->soft_iface) + continue; + + if (batman_if_tmp->if_num > batman_if->if_num) + batman_if_tmp->if_num--; + } + rcu_read_unlock(); + + batman_if->if_num = -1; + spin_unlock_bh(&bat_priv->orig_hash_lock); + return 0; + +err: + spin_unlock_bh(&bat_priv->orig_hash_lock); + return -ENOMEM; +} diff --git a/net/batman-adv/originator.h b/net/batman-adv/originator.h new file mode 100644 index 000000000000..d474ceb2a4eb --- /dev/null +++ b/net/batman-adv/originator.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner, Simon Wunderlich + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#ifndef _NET_BATMAN_ADV_ORIGINATOR_H_ +#define _NET_BATMAN_ADV_ORIGINATOR_H_ + +int originator_init(struct bat_priv *bat_priv); +void originator_free(struct bat_priv *bat_priv); +void purge_orig_ref(struct bat_priv *bat_priv); +struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr); +struct neigh_node * +create_neighbor(struct orig_node *orig_node, struct orig_node *orig_neigh_node, + uint8_t *neigh, struct batman_if *if_incoming); +int orig_seq_print_text(struct seq_file *seq, void *offset); +int orig_hash_add_if(struct batman_if *batman_if, int max_if_num); +int orig_hash_del_if(struct batman_if *batman_if, int max_if_num); + + +/* returns 1 if they are the same originator */ +static inline int compare_orig(void *data1, void *data2) +{ + return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); +} + +/* hashfunction to choose an entry in a hash table of given size */ +/* hash algorithm from http://en.wikipedia.org/wiki/Hash_table */ +static inline int choose_orig(void *data, int32_t size) +{ + unsigned char *key = data; + uint32_t hash = 0; + size_t i; + + for (i = 0; i < 6; i++) { + hash += key[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + + return hash % size; +} + +#endif /* _NET_BATMAN_ADV_ORIGINATOR_H_ */ diff --git a/net/batman-adv/packet.h b/net/batman-adv/packet.h new file mode 100644 index 000000000000..b49fdf70a6d5 --- /dev/null +++ b/net/batman-adv/packet.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner, Simon Wunderlich + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#ifndef _NET_BATMAN_ADV_PACKET_H_ +#define _NET_BATMAN_ADV_PACKET_H_ + +#define ETH_P_BATMAN 0x4305 /* unofficial/not registered Ethertype */ + +#define BAT_PACKET 0x01 +#define BAT_ICMP 0x02 +#define BAT_UNICAST 0x03 +#define BAT_BCAST 0x04 +#define BAT_VIS 0x05 +#define BAT_UNICAST_FRAG 0x06 + +/* this file is included by batctl which needs these defines */ +#define COMPAT_VERSION 12 +#define DIRECTLINK 0x40 +#define VIS_SERVER 0x20 +#define PRIMARIES_FIRST_HOP 0x10 + +/* ICMP message types */ +#define ECHO_REPLY 0 +#define DESTINATION_UNREACHABLE 3 +#define ECHO_REQUEST 8 +#define TTL_EXCEEDED 11 +#define PARAMETER_PROBLEM 12 + +/* vis defines */ +#define VIS_TYPE_SERVER_SYNC 0 +#define VIS_TYPE_CLIENT_UPDATE 1 + +/* fragmentation defines */ +#define UNI_FRAG_HEAD 0x01 + +struct batman_packet { + uint8_t packet_type; + uint8_t version; /* batman version field */ + uint8_t flags; /* 0x40: DIRECTLINK flag, 0x20 VIS_SERVER flag... */ + uint8_t tq; + uint32_t seqno; + uint8_t orig[6]; + uint8_t prev_sender[6]; + uint8_t ttl; + uint8_t num_hna; + uint8_t gw_flags; /* flags related to gateway class */ + uint8_t align; +} __attribute__((packed)); + +#define BAT_PACKET_LEN sizeof(struct batman_packet) + +struct icmp_packet { + uint8_t packet_type; + uint8_t version; /* batman version field */ + uint8_t msg_type; /* see ICMP message types above */ + uint8_t ttl; + uint8_t dst[6]; + uint8_t orig[6]; + uint16_t seqno; + uint8_t uid; +} __attribute__((packed)); + +#define BAT_RR_LEN 16 + +/* icmp_packet_rr must start with all fields from imcp_packet + * as this is assumed by code that handles ICMP packets */ +struct icmp_packet_rr { + uint8_t packet_type; + uint8_t version; /* batman version field */ + uint8_t msg_type; /* see ICMP message types above */ + uint8_t ttl; + uint8_t dst[6]; + uint8_t orig[6]; + uint16_t seqno; + uint8_t uid; + uint8_t rr_cur; + uint8_t rr[BAT_RR_LEN][ETH_ALEN]; +} __attribute__((packed)); + +struct unicast_packet { + uint8_t packet_type; + uint8_t version; /* batman version field */ + uint8_t dest[6]; + uint8_t ttl; +} __attribute__((packed)); + +struct unicast_frag_packet { + uint8_t packet_type; + uint8_t version; /* batman version field */ + uint8_t dest[6]; + uint8_t ttl; + uint8_t flags; + uint8_t orig[6]; + uint16_t seqno; +} __attribute__((packed)); + +struct bcast_packet { + uint8_t packet_type; + uint8_t version; /* batman version field */ + uint8_t orig[6]; + uint8_t ttl; + uint32_t seqno; +} __attribute__((packed)); + +struct vis_packet { + uint8_t packet_type; + uint8_t version; /* batman version field */ + uint8_t vis_type; /* which type of vis-participant sent this? */ + uint8_t entries; /* number of entries behind this struct */ + uint32_t seqno; /* sequence number */ + uint8_t ttl; /* TTL */ + uint8_t vis_orig[6]; /* originator that informs about its + * neighbors */ + uint8_t target_orig[6]; /* who should receive this packet */ + uint8_t sender_orig[6]; /* who sent or rebroadcasted this packet */ +} __attribute__((packed)); + +#endif /* _NET_BATMAN_ADV_PACKET_H_ */ diff --git a/net/batman-adv/ring_buffer.c b/net/batman-adv/ring_buffer.c new file mode 100644 index 000000000000..defd37c9be1f --- /dev/null +++ b/net/batman-adv/ring_buffer.c @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include "main.h" +#include "ring_buffer.h" + +void ring_buffer_set(uint8_t lq_recv[], uint8_t *lq_index, uint8_t value) +{ + lq_recv[*lq_index] = value; + *lq_index = (*lq_index + 1) % TQ_GLOBAL_WINDOW_SIZE; +} + +uint8_t ring_buffer_avg(uint8_t lq_recv[]) +{ + uint8_t *ptr; + uint16_t count = 0, i = 0, sum = 0; + + ptr = lq_recv; + + while (i < TQ_GLOBAL_WINDOW_SIZE) { + if (*ptr != 0) { + count++; + sum += *ptr; + } + + i++; + ptr++; + } + + if (count == 0) + return 0; + + return (uint8_t)(sum / count); +} diff --git a/net/batman-adv/ring_buffer.h b/net/batman-adv/ring_buffer.h new file mode 100644 index 000000000000..6b0cb9aaeba5 --- /dev/null +++ b/net/batman-adv/ring_buffer.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#ifndef _NET_BATMAN_ADV_RING_BUFFER_H_ +#define _NET_BATMAN_ADV_RING_BUFFER_H_ + +void ring_buffer_set(uint8_t lq_recv[], uint8_t *lq_index, uint8_t value); +uint8_t ring_buffer_avg(uint8_t lq_recv[]); + +#endif /* _NET_BATMAN_ADV_RING_BUFFER_H_ */ diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c new file mode 100644 index 000000000000..8828eddd3f72 --- /dev/null +++ b/net/batman-adv/routing.c @@ -0,0 +1,1397 @@ +/* + * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner, Simon Wunderlich + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include "main.h" +#include "routing.h" +#include "send.h" +#include "hash.h" +#include "soft-interface.h" +#include "hard-interface.h" +#include "icmp_socket.h" +#include "translation-table.h" +#include "originator.h" +#include "types.h" +#include "ring_buffer.h" +#include "vis.h" +#include "aggregation.h" +#include "gateway_common.h" +#include "gateway_client.h" +#include "unicast.h" + +void slide_own_bcast_window(struct batman_if *batman_if) +{ + struct bat_priv *bat_priv = netdev_priv(batman_if->soft_iface); + struct hashtable_t *hash = bat_priv->orig_hash; + struct hlist_node *walk; + struct hlist_head *head; + struct element_t *bucket; + struct orig_node *orig_node; + unsigned long *word; + int i; + size_t word_index; + + spin_lock_bh(&bat_priv->orig_hash_lock); + + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + hlist_for_each_entry(bucket, walk, head, hlist) { + orig_node = bucket->data; + word_index = batman_if->if_num * NUM_WORDS; + word = &(orig_node->bcast_own[word_index]); + + bit_get_packet(bat_priv, word, 1, 0); + orig_node->bcast_own_sum[batman_if->if_num] = + bit_packet_count(word); + } + } + + spin_unlock_bh(&bat_priv->orig_hash_lock); +} + +static void update_HNA(struct bat_priv *bat_priv, struct orig_node *orig_node, + unsigned char *hna_buff, int hna_buff_len) +{ + if ((hna_buff_len != orig_node->hna_buff_len) || + ((hna_buff_len > 0) && + (orig_node->hna_buff_len > 0) && + (memcmp(orig_node->hna_buff, hna_buff, hna_buff_len) != 0))) { + + if (orig_node->hna_buff_len > 0) + hna_global_del_orig(bat_priv, orig_node, + "originator changed hna"); + + if ((hna_buff_len > 0) && (hna_buff)) + hna_global_add_orig(bat_priv, orig_node, + hna_buff, hna_buff_len); + } +} + +static void update_route(struct bat_priv *bat_priv, + struct orig_node *orig_node, + struct neigh_node *neigh_node, + unsigned char *hna_buff, int hna_buff_len) +{ + /* route deleted */ + if ((orig_node->router) && (!neigh_node)) { + + bat_dbg(DBG_ROUTES, bat_priv, "Deleting route towards: %pM\n", + orig_node->orig); + hna_global_del_orig(bat_priv, orig_node, + "originator timed out"); + + /* route added */ + } else if ((!orig_node->router) && (neigh_node)) { + + bat_dbg(DBG_ROUTES, bat_priv, + "Adding route towards: %pM (via %pM)\n", + orig_node->orig, neigh_node->addr); + hna_global_add_orig(bat_priv, orig_node, + hna_buff, hna_buff_len); + + /* route changed */ + } else { + bat_dbg(DBG_ROUTES, bat_priv, + "Changing route towards: %pM " + "(now via %pM - was via %pM)\n", + orig_node->orig, neigh_node->addr, + orig_node->router->addr); + } + + orig_node->router = neigh_node; +} + + +void update_routes(struct bat_priv *bat_priv, struct orig_node *orig_node, + struct neigh_node *neigh_node, unsigned char *hna_buff, + int hna_buff_len) +{ + + if (!orig_node) + return; + + if (orig_node->router != neigh_node) + update_route(bat_priv, orig_node, neigh_node, + hna_buff, hna_buff_len); + /* may be just HNA changed */ + else + update_HNA(bat_priv, orig_node, hna_buff, hna_buff_len); +} + +static int is_bidirectional_neigh(struct orig_node *orig_node, + struct orig_node *orig_neigh_node, + struct batman_packet *batman_packet, + struct batman_if *if_incoming) +{ + struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); + struct neigh_node *neigh_node = NULL, *tmp_neigh_node = NULL; + unsigned char total_count; + + if (orig_node == orig_neigh_node) { + list_for_each_entry(tmp_neigh_node, + &orig_node->neigh_list, + list) { + + if (compare_orig(tmp_neigh_node->addr, + orig_neigh_node->orig) && + (tmp_neigh_node->if_incoming == if_incoming)) + neigh_node = tmp_neigh_node; + } + + if (!neigh_node) + neigh_node = create_neighbor(orig_node, + orig_neigh_node, + orig_neigh_node->orig, + if_incoming); + /* create_neighbor failed, return 0 */ + if (!neigh_node) + return 0; + + neigh_node->last_valid = jiffies; + } else { + /* find packet count of corresponding one hop neighbor */ + list_for_each_entry(tmp_neigh_node, + &orig_neigh_node->neigh_list, list) { + + if (compare_orig(tmp_neigh_node->addr, + orig_neigh_node->orig) && + (tmp_neigh_node->if_incoming == if_incoming)) + neigh_node = tmp_neigh_node; + } + + if (!neigh_node) + neigh_node = create_neighbor(orig_neigh_node, + orig_neigh_node, + orig_neigh_node->orig, + if_incoming); + /* create_neighbor failed, return 0 */ + if (!neigh_node) + return 0; + } + + orig_node->last_valid = jiffies; + + /* pay attention to not get a value bigger than 100 % */ + total_count = (orig_neigh_node->bcast_own_sum[if_incoming->if_num] > + neigh_node->real_packet_count ? + neigh_node->real_packet_count : + orig_neigh_node->bcast_own_sum[if_incoming->if_num]); + + /* if we have too few packets (too less data) we set tq_own to zero */ + /* if we receive too few packets it is not considered bidirectional */ + if ((total_count < TQ_LOCAL_BIDRECT_SEND_MINIMUM) || + (neigh_node->real_packet_count < TQ_LOCAL_BIDRECT_RECV_MINIMUM)) + orig_neigh_node->tq_own = 0; + else + /* neigh_node->real_packet_count is never zero as we + * only purge old information when getting new + * information */ + orig_neigh_node->tq_own = (TQ_MAX_VALUE * total_count) / + neigh_node->real_packet_count; + + /* + * 1 - ((1-x) ** 3), normalized to TQ_MAX_VALUE this does + * affect the nearly-symmetric links only a little, but + * punishes asymmetric links more. This will give a value + * between 0 and TQ_MAX_VALUE + */ + orig_neigh_node->tq_asym_penalty = + TQ_MAX_VALUE - + (TQ_MAX_VALUE * + (TQ_LOCAL_WINDOW_SIZE - neigh_node->real_packet_count) * + (TQ_LOCAL_WINDOW_SIZE - neigh_node->real_packet_count) * + (TQ_LOCAL_WINDOW_SIZE - neigh_node->real_packet_count)) / + (TQ_LOCAL_WINDOW_SIZE * + TQ_LOCAL_WINDOW_SIZE * + TQ_LOCAL_WINDOW_SIZE); + + batman_packet->tq = ((batman_packet->tq * + orig_neigh_node->tq_own * + orig_neigh_node->tq_asym_penalty) / + (TQ_MAX_VALUE * TQ_MAX_VALUE)); + + bat_dbg(DBG_BATMAN, bat_priv, + "bidirectional: " + "orig = %-15pM neigh = %-15pM => own_bcast = %2i, " + "real recv = %2i, local tq: %3i, asym_penalty: %3i, " + "total tq: %3i\n", + orig_node->orig, orig_neigh_node->orig, total_count, + neigh_node->real_packet_count, orig_neigh_node->tq_own, + orig_neigh_node->tq_asym_penalty, batman_packet->tq); + + /* if link has the minimum required transmission quality + * consider it bidirectional */ + if (batman_packet->tq >= TQ_TOTAL_BIDRECT_LIMIT) + return 1; + + return 0; +} + +static void update_orig(struct bat_priv *bat_priv, + struct orig_node *orig_node, + struct ethhdr *ethhdr, + struct batman_packet *batman_packet, + struct batman_if *if_incoming, + unsigned char *hna_buff, int hna_buff_len, + char is_duplicate) +{ + struct neigh_node *neigh_node = NULL, *tmp_neigh_node = NULL; + int tmp_hna_buff_len; + + bat_dbg(DBG_BATMAN, bat_priv, "update_originator(): " + "Searching and updating originator entry of received packet\n"); + + list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) { + if (compare_orig(tmp_neigh_node->addr, ethhdr->h_source) && + (tmp_neigh_node->if_incoming == if_incoming)) { + neigh_node = tmp_neigh_node; + continue; + } + + if (is_duplicate) + continue; + + ring_buffer_set(tmp_neigh_node->tq_recv, + &tmp_neigh_node->tq_index, 0); + tmp_neigh_node->tq_avg = + ring_buffer_avg(tmp_neigh_node->tq_recv); + } + + if (!neigh_node) { + struct orig_node *orig_tmp; + + orig_tmp = get_orig_node(bat_priv, ethhdr->h_source); + if (!orig_tmp) + return; + + neigh_node = create_neighbor(orig_node, orig_tmp, + ethhdr->h_source, if_incoming); + if (!neigh_node) + return; + } else + bat_dbg(DBG_BATMAN, bat_priv, + "Updating existing last-hop neighbor of originator\n"); + + orig_node->flags = batman_packet->flags; + neigh_node->last_valid = jiffies; + + ring_buffer_set(neigh_node->tq_recv, + &neigh_node->tq_index, + batman_packet->tq); + neigh_node->tq_avg = ring_buffer_avg(neigh_node->tq_recv); + + if (!is_duplicate) { + orig_node->last_ttl = batman_packet->ttl; + neigh_node->last_ttl = batman_packet->ttl; + } + + tmp_hna_buff_len = (hna_buff_len > batman_packet->num_hna * ETH_ALEN ? + batman_packet->num_hna * ETH_ALEN : hna_buff_len); + + /* if this neighbor already is our next hop there is nothing + * to change */ + if (orig_node->router == neigh_node) + goto update_hna; + + /* if this neighbor does not offer a better TQ we won't consider it */ + if ((orig_node->router) && + (orig_node->router->tq_avg > neigh_node->tq_avg)) + goto update_hna; + + /* if the TQ is the same and the link not more symetric we + * won't consider it either */ + if ((orig_node->router) && + ((neigh_node->tq_avg == orig_node->router->tq_avg) && + (orig_node->router->orig_node->bcast_own_sum[if_incoming->if_num] + >= neigh_node->orig_node->bcast_own_sum[if_incoming->if_num]))) + goto update_hna; + + update_routes(bat_priv, orig_node, neigh_node, + hna_buff, tmp_hna_buff_len); + goto update_gw; + +update_hna: + update_routes(bat_priv, orig_node, orig_node->router, + hna_buff, tmp_hna_buff_len); + +update_gw: + if (orig_node->gw_flags != batman_packet->gw_flags) + gw_node_update(bat_priv, orig_node, batman_packet->gw_flags); + + orig_node->gw_flags = batman_packet->gw_flags; + + /* restart gateway selection if fast or late switching was enabled */ + if ((orig_node->gw_flags) && + (atomic_read(&bat_priv->gw_mode) == GW_MODE_CLIENT) && + (atomic_read(&bat_priv->gw_sel_class) > 2)) + gw_check_election(bat_priv, orig_node); +} + +/* checks whether the host restarted and is in the protection time. + * returns: + * 0 if the packet is to be accepted + * 1 if the packet is to be ignored. + */ +static int window_protected(struct bat_priv *bat_priv, + int32_t seq_num_diff, + unsigned long *last_reset) +{ + if ((seq_num_diff <= -TQ_LOCAL_WINDOW_SIZE) + || (seq_num_diff >= EXPECTED_SEQNO_RANGE)) { + if (time_after(jiffies, *last_reset + + msecs_to_jiffies(RESET_PROTECTION_MS))) { + + *last_reset = jiffies; + bat_dbg(DBG_BATMAN, bat_priv, + "old packet received, start protection\n"); + + return 0; + } else + return 1; + } + return 0; +} + +/* processes a batman packet for all interfaces, adjusts the sequence number and + * finds out whether it is a duplicate. + * returns: + * 1 the packet is a duplicate + * 0 the packet has not yet been received + * -1 the packet is old and has been received while the seqno window + * was protected. Caller should drop it. + */ +static char count_real_packets(struct ethhdr *ethhdr, + struct batman_packet *batman_packet, + struct batman_if *if_incoming) +{ + struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); + struct orig_node *orig_node; + struct neigh_node *tmp_neigh_node; + char is_duplicate = 0; + int32_t seq_diff; + int need_update = 0; + int set_mark; + + orig_node = get_orig_node(bat_priv, batman_packet->orig); + if (!orig_node) + return 0; + + seq_diff = batman_packet->seqno - orig_node->last_real_seqno; + + /* signalize caller that the packet is to be dropped. */ + if (window_protected(bat_priv, seq_diff, + &orig_node->batman_seqno_reset)) + return -1; + + list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) { + + is_duplicate |= get_bit_status(tmp_neigh_node->real_bits, + orig_node->last_real_seqno, + batman_packet->seqno); + + if (compare_orig(tmp_neigh_node->addr, ethhdr->h_source) && + (tmp_neigh_node->if_incoming == if_incoming)) + set_mark = 1; + else + set_mark = 0; + + /* if the window moved, set the update flag. */ + need_update |= bit_get_packet(bat_priv, + tmp_neigh_node->real_bits, + seq_diff, set_mark); + + tmp_neigh_node->real_packet_count = + bit_packet_count(tmp_neigh_node->real_bits); + } + + if (need_update) { + bat_dbg(DBG_BATMAN, bat_priv, + "updating last_seqno: old %d, new %d\n", + orig_node->last_real_seqno, batman_packet->seqno); + orig_node->last_real_seqno = batman_packet->seqno; + } + + return is_duplicate; +} + +/* copy primary address for bonding */ +static void mark_bonding_address(struct bat_priv *bat_priv, + struct orig_node *orig_node, + struct orig_node *orig_neigh_node, + struct batman_packet *batman_packet) + +{ + if (batman_packet->flags & PRIMARIES_FIRST_HOP) + memcpy(orig_neigh_node->primary_addr, + orig_node->orig, ETH_ALEN); + + return; +} + +/* mark possible bond.candidates in the neighbor list */ +void update_bonding_candidates(struct bat_priv *bat_priv, + struct orig_node *orig_node) +{ + int candidates; + int interference_candidate; + int best_tq; + struct neigh_node *tmp_neigh_node, *tmp_neigh_node2; + struct neigh_node *first_candidate, *last_candidate; + + /* update the candidates for this originator */ + if (!orig_node->router) { + orig_node->bond.candidates = 0; + return; + } + + best_tq = orig_node->router->tq_avg; + + /* update bond.candidates */ + + candidates = 0; + + /* mark other nodes which also received "PRIMARIES FIRST HOP" packets + * as "bonding partner" */ + + /* first, zero the list */ + list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) { + tmp_neigh_node->next_bond_candidate = NULL; + } + + first_candidate = NULL; + last_candidate = NULL; + list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) { + + /* only consider if it has the same primary address ... */ + if (memcmp(orig_node->orig, + tmp_neigh_node->orig_node->primary_addr, + ETH_ALEN) != 0) + continue; + + /* ... and is good enough to be considered */ + if (tmp_neigh_node->tq_avg < best_tq - BONDING_TQ_THRESHOLD) + continue; + + /* check if we have another candidate with the same + * mac address or interface. If we do, we won't + * select this candidate because of possible interference. */ + + interference_candidate = 0; + list_for_each_entry(tmp_neigh_node2, + &orig_node->neigh_list, list) { + + if (tmp_neigh_node2 == tmp_neigh_node) + continue; + + /* we only care if the other candidate is even + * considered as candidate. */ + if (!tmp_neigh_node2->next_bond_candidate) + continue; + + + if ((tmp_neigh_node->if_incoming == + tmp_neigh_node2->if_incoming) + || (memcmp(tmp_neigh_node->addr, + tmp_neigh_node2->addr, ETH_ALEN) == 0)) { + + interference_candidate = 1; + break; + } + } + /* don't care further if it is an interference candidate */ + if (interference_candidate) + continue; + + if (!first_candidate) { + first_candidate = tmp_neigh_node; + tmp_neigh_node->next_bond_candidate = first_candidate; + } else + tmp_neigh_node->next_bond_candidate = last_candidate; + + last_candidate = tmp_neigh_node; + + candidates++; + } + + if (candidates > 0) { + first_candidate->next_bond_candidate = last_candidate; + orig_node->bond.selected = first_candidate; + } + + orig_node->bond.candidates = candidates; +} + +void receive_bat_packet(struct ethhdr *ethhdr, + struct batman_packet *batman_packet, + unsigned char *hna_buff, int hna_buff_len, + struct batman_if *if_incoming) +{ + struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); + struct batman_if *batman_if; + struct orig_node *orig_neigh_node, *orig_node; + char has_directlink_flag; + char is_my_addr = 0, is_my_orig = 0, is_my_oldorig = 0; + char is_broadcast = 0, is_bidirectional, is_single_hop_neigh; + char is_duplicate; + uint32_t if_incoming_seqno; + + /* Silently drop when the batman packet is actually not a + * correct packet. + * + * This might happen if a packet is padded (e.g. Ethernet has a + * minimum frame length of 64 byte) and the aggregation interprets + * it as an additional length. + * + * TODO: A more sane solution would be to have a bit in the + * batman_packet to detect whether the packet is the last + * packet in an aggregation. Here we expect that the padding + * is always zero (or not 0x01) + */ + if (batman_packet->packet_type != BAT_PACKET) + return; + + /* could be changed by schedule_own_packet() */ + if_incoming_seqno = atomic_read(&if_incoming->seqno); + + has_directlink_flag = (batman_packet->flags & DIRECTLINK ? 1 : 0); + + is_single_hop_neigh = (compare_orig(ethhdr->h_source, + batman_packet->orig) ? 1 : 0); + + bat_dbg(DBG_BATMAN, bat_priv, + "Received BATMAN packet via NB: %pM, IF: %s [%pM] " + "(from OG: %pM, via prev OG: %pM, seqno %d, tq %d, " + "TTL %d, V %d, IDF %d)\n", + ethhdr->h_source, if_incoming->net_dev->name, + if_incoming->net_dev->dev_addr, batman_packet->orig, + batman_packet->prev_sender, batman_packet->seqno, + batman_packet->tq, batman_packet->ttl, batman_packet->version, + has_directlink_flag); + + rcu_read_lock(); + list_for_each_entry_rcu(batman_if, &if_list, list) { + if (batman_if->if_status != IF_ACTIVE) + continue; + + if (batman_if->soft_iface != if_incoming->soft_iface) + continue; + + if (compare_orig(ethhdr->h_source, + batman_if->net_dev->dev_addr)) + is_my_addr = 1; + + if (compare_orig(batman_packet->orig, + batman_if->net_dev->dev_addr)) + is_my_orig = 1; + + if (compare_orig(batman_packet->prev_sender, + batman_if->net_dev->dev_addr)) + is_my_oldorig = 1; + + if (compare_orig(ethhdr->h_source, broadcast_addr)) + is_broadcast = 1; + } + rcu_read_unlock(); + + if (batman_packet->version != COMPAT_VERSION) { + bat_dbg(DBG_BATMAN, bat_priv, + "Drop packet: incompatible batman version (%i)\n", + batman_packet->version); + return; + } + + if (is_my_addr) { + bat_dbg(DBG_BATMAN, bat_priv, + "Drop packet: received my own broadcast (sender: %pM" + ")\n", + ethhdr->h_source); + return; + } + + if (is_broadcast) { + bat_dbg(DBG_BATMAN, bat_priv, "Drop packet: " + "ignoring all packets with broadcast source addr (sender: %pM" + ")\n", ethhdr->h_source); + return; + } + + if (is_my_orig) { + unsigned long *word; + int offset; + + orig_neigh_node = get_orig_node(bat_priv, ethhdr->h_source); + + if (!orig_neigh_node) + return; + + /* neighbor has to indicate direct link and it has to + * come via the corresponding interface */ + /* if received seqno equals last send seqno save new + * seqno for bidirectional check */ + if (has_directlink_flag && + compare_orig(if_incoming->net_dev->dev_addr, + batman_packet->orig) && + (batman_packet->seqno - if_incoming_seqno + 2 == 0)) { + offset = if_incoming->if_num * NUM_WORDS; + word = &(orig_neigh_node->bcast_own[offset]); + bit_mark(word, 0); + orig_neigh_node->bcast_own_sum[if_incoming->if_num] = + bit_packet_count(word); + } + + bat_dbg(DBG_BATMAN, bat_priv, "Drop packet: " + "originator packet from myself (via neighbor)\n"); + return; + } + + if (is_my_oldorig) { + bat_dbg(DBG_BATMAN, bat_priv, + "Drop packet: ignoring all rebroadcast echos (sender: " + "%pM)\n", ethhdr->h_source); + return; + } + + orig_node = get_orig_node(bat_priv, batman_packet->orig); + if (!orig_node) + return; + + is_duplicate = count_real_packets(ethhdr, batman_packet, if_incoming); + + if (is_duplicate == -1) { + bat_dbg(DBG_BATMAN, bat_priv, + "Drop packet: packet within seqno protection time " + "(sender: %pM)\n", ethhdr->h_source); + return; + } + + if (batman_packet->tq == 0) { + bat_dbg(DBG_BATMAN, bat_priv, + "Drop packet: originator packet with tq equal 0\n"); + return; + } + + /* avoid temporary routing loops */ + if ((orig_node->router) && + (orig_node->router->orig_node->router) && + (compare_orig(orig_node->router->addr, + batman_packet->prev_sender)) && + !(compare_orig(batman_packet->orig, batman_packet->prev_sender)) && + (compare_orig(orig_node->router->addr, + orig_node->router->orig_node->router->addr))) { + bat_dbg(DBG_BATMAN, bat_priv, + "Drop packet: ignoring all rebroadcast packets that " + "may make me loop (sender: %pM)\n", ethhdr->h_source); + return; + } + + /* if sender is a direct neighbor the sender mac equals + * originator mac */ + orig_neigh_node = (is_single_hop_neigh ? + orig_node : + get_orig_node(bat_priv, ethhdr->h_source)); + if (!orig_neigh_node) + return; + + /* drop packet if sender is not a direct neighbor and if we + * don't route towards it */ + if (!is_single_hop_neigh && (!orig_neigh_node->router)) { + bat_dbg(DBG_BATMAN, bat_priv, + "Drop packet: OGM via unknown neighbor!\n"); + return; + } + + is_bidirectional = is_bidirectional_neigh(orig_node, orig_neigh_node, + batman_packet, if_incoming); + + /* update ranking if it is not a duplicate or has the same + * seqno and similar ttl as the non-duplicate */ + if (is_bidirectional && + (!is_duplicate || + ((orig_node->last_real_seqno == batman_packet->seqno) && + (orig_node->last_ttl - 3 <= batman_packet->ttl)))) + update_orig(bat_priv, orig_node, ethhdr, batman_packet, + if_incoming, hna_buff, hna_buff_len, is_duplicate); + + mark_bonding_address(bat_priv, orig_node, + orig_neigh_node, batman_packet); + update_bonding_candidates(bat_priv, orig_node); + + /* is single hop (direct) neighbor */ + if (is_single_hop_neigh) { + + /* mark direct link on incoming interface */ + schedule_forward_packet(orig_node, ethhdr, batman_packet, + 1, hna_buff_len, if_incoming); + + bat_dbg(DBG_BATMAN, bat_priv, "Forwarding packet: " + "rebroadcast neighbor packet with direct link flag\n"); + return; + } + + /* multihop originator */ + if (!is_bidirectional) { + bat_dbg(DBG_BATMAN, bat_priv, + "Drop packet: not received via bidirectional link\n"); + return; + } + + if (is_duplicate) { + bat_dbg(DBG_BATMAN, bat_priv, + "Drop packet: duplicate packet received\n"); + return; + } + + bat_dbg(DBG_BATMAN, bat_priv, + "Forwarding packet: rebroadcast originator packet\n"); + schedule_forward_packet(orig_node, ethhdr, batman_packet, + 0, hna_buff_len, if_incoming); +} + +int recv_bat_packet(struct sk_buff *skb, struct batman_if *batman_if) +{ + struct bat_priv *bat_priv = netdev_priv(batman_if->soft_iface); + struct ethhdr *ethhdr; + + /* drop packet if it has not necessary minimum size */ + if (unlikely(!pskb_may_pull(skb, sizeof(struct batman_packet)))) + return NET_RX_DROP; + + ethhdr = (struct ethhdr *)skb_mac_header(skb); + + /* packet with broadcast indication but unicast recipient */ + if (!is_broadcast_ether_addr(ethhdr->h_dest)) + return NET_RX_DROP; + + /* packet with broadcast sender address */ + if (is_broadcast_ether_addr(ethhdr->h_source)) + return NET_RX_DROP; + + /* create a copy of the skb, if needed, to modify it. */ + if (skb_cow(skb, 0) < 0) + return NET_RX_DROP; + + /* keep skb linear */ + if (skb_linearize(skb) < 0) + return NET_RX_DROP; + + ethhdr = (struct ethhdr *)skb_mac_header(skb); + + spin_lock_bh(&bat_priv->orig_hash_lock); + receive_aggr_bat_packet(ethhdr, + skb->data, + skb_headlen(skb), + batman_if); + spin_unlock_bh(&bat_priv->orig_hash_lock); + + kfree_skb(skb); + return NET_RX_SUCCESS; +} + +static int recv_my_icmp_packet(struct bat_priv *bat_priv, + struct sk_buff *skb, size_t icmp_len) +{ + struct orig_node *orig_node; + struct icmp_packet_rr *icmp_packet; + struct ethhdr *ethhdr; + struct batman_if *batman_if; + int ret; + uint8_t dstaddr[ETH_ALEN]; + + icmp_packet = (struct icmp_packet_rr *)skb->data; + ethhdr = (struct ethhdr *)skb_mac_header(skb); + + /* add data to device queue */ + if (icmp_packet->msg_type != ECHO_REQUEST) { + bat_socket_receive_packet(icmp_packet, icmp_len); + return NET_RX_DROP; + } + + if (!bat_priv->primary_if) + return NET_RX_DROP; + + /* answer echo request (ping) */ + /* get routing information */ + spin_lock_bh(&bat_priv->orig_hash_lock); + orig_node = ((struct orig_node *)hash_find(bat_priv->orig_hash, + compare_orig, choose_orig, + icmp_packet->orig)); + ret = NET_RX_DROP; + + if ((orig_node) && (orig_node->router)) { + + /* don't lock while sending the packets ... we therefore + * copy the required data before sending */ + batman_if = orig_node->router->if_incoming; + memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); + spin_unlock_bh(&bat_priv->orig_hash_lock); + + /* create a copy of the skb, if needed, to modify it. */ + if (skb_cow(skb, sizeof(struct ethhdr)) < 0) + return NET_RX_DROP; + + icmp_packet = (struct icmp_packet_rr *)skb->data; + ethhdr = (struct ethhdr *)skb_mac_header(skb); + + memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN); + memcpy(icmp_packet->orig, + bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN); + icmp_packet->msg_type = ECHO_REPLY; + icmp_packet->ttl = TTL; + + send_skb_packet(skb, batman_if, dstaddr); + ret = NET_RX_SUCCESS; + + } else + spin_unlock_bh(&bat_priv->orig_hash_lock); + + return ret; +} + +static int recv_icmp_ttl_exceeded(struct bat_priv *bat_priv, + struct sk_buff *skb, size_t icmp_len) +{ + struct orig_node *orig_node; + struct icmp_packet *icmp_packet; + struct ethhdr *ethhdr; + struct batman_if *batman_if; + int ret; + uint8_t dstaddr[ETH_ALEN]; + + icmp_packet = (struct icmp_packet *)skb->data; + ethhdr = (struct ethhdr *)skb_mac_header(skb); + + /* send TTL exceeded if packet is an echo request (traceroute) */ + if (icmp_packet->msg_type != ECHO_REQUEST) { + pr_debug("Warning - can't forward icmp packet from %pM to " + "%pM: ttl exceeded\n", icmp_packet->orig, + icmp_packet->dst); + return NET_RX_DROP; + } + + if (!bat_priv->primary_if) + return NET_RX_DROP; + + /* get routing information */ + spin_lock_bh(&bat_priv->orig_hash_lock); + orig_node = ((struct orig_node *) + hash_find(bat_priv->orig_hash, compare_orig, choose_orig, + icmp_packet->orig)); + ret = NET_RX_DROP; + + if ((orig_node) && (orig_node->router)) { + + /* don't lock while sending the packets ... we therefore + * copy the required data before sending */ + batman_if = orig_node->router->if_incoming; + memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); + spin_unlock_bh(&bat_priv->orig_hash_lock); + + /* create a copy of the skb, if needed, to modify it. */ + if (skb_cow(skb, sizeof(struct ethhdr)) < 0) + return NET_RX_DROP; + + icmp_packet = (struct icmp_packet *) skb->data; + ethhdr = (struct ethhdr *)skb_mac_header(skb); + + memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN); + memcpy(icmp_packet->orig, + bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN); + icmp_packet->msg_type = TTL_EXCEEDED; + icmp_packet->ttl = TTL; + + send_skb_packet(skb, batman_if, dstaddr); + ret = NET_RX_SUCCESS; + + } else + spin_unlock_bh(&bat_priv->orig_hash_lock); + + return ret; +} + + +int recv_icmp_packet(struct sk_buff *skb, struct batman_if *recv_if) +{ + struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface); + struct icmp_packet_rr *icmp_packet; + struct ethhdr *ethhdr; + struct orig_node *orig_node; + struct batman_if *batman_if; + int hdr_size = sizeof(struct icmp_packet); + int ret; + uint8_t dstaddr[ETH_ALEN]; + + /** + * we truncate all incoming icmp packets if they don't match our size + */ + if (skb->len >= sizeof(struct icmp_packet_rr)) + hdr_size = sizeof(struct icmp_packet_rr); + + /* drop packet if it has not necessary minimum size */ + if (unlikely(!pskb_may_pull(skb, hdr_size))) + return NET_RX_DROP; + + ethhdr = (struct ethhdr *)skb_mac_header(skb); + + /* packet with unicast indication but broadcast recipient */ + if (is_broadcast_ether_addr(ethhdr->h_dest)) + return NET_RX_DROP; + + /* packet with broadcast sender address */ + if (is_broadcast_ether_addr(ethhdr->h_source)) + return NET_RX_DROP; + + /* not for me */ + if (!is_my_mac(ethhdr->h_dest)) + return NET_RX_DROP; + + icmp_packet = (struct icmp_packet_rr *)skb->data; + + /* add record route information if not full */ + if ((hdr_size == sizeof(struct icmp_packet_rr)) && + (icmp_packet->rr_cur < BAT_RR_LEN)) { + memcpy(&(icmp_packet->rr[icmp_packet->rr_cur]), + ethhdr->h_dest, ETH_ALEN); + icmp_packet->rr_cur++; + } + + /* packet for me */ + if (is_my_mac(icmp_packet->dst)) + return recv_my_icmp_packet(bat_priv, skb, hdr_size); + + /* TTL exceeded */ + if (icmp_packet->ttl < 2) + return recv_icmp_ttl_exceeded(bat_priv, skb, hdr_size); + + ret = NET_RX_DROP; + + /* get routing information */ + spin_lock_bh(&bat_priv->orig_hash_lock); + orig_node = ((struct orig_node *) + hash_find(bat_priv->orig_hash, compare_orig, choose_orig, + icmp_packet->dst)); + + if ((orig_node) && (orig_node->router)) { + + /* don't lock while sending the packets ... we therefore + * copy the required data before sending */ + batman_if = orig_node->router->if_incoming; + memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); + spin_unlock_bh(&bat_priv->orig_hash_lock); + + /* create a copy of the skb, if needed, to modify it. */ + if (skb_cow(skb, sizeof(struct ethhdr)) < 0) + return NET_RX_DROP; + + icmp_packet = (struct icmp_packet_rr *)skb->data; + ethhdr = (struct ethhdr *)skb_mac_header(skb); + + /* decrement ttl */ + icmp_packet->ttl--; + + /* route it */ + send_skb_packet(skb, batman_if, dstaddr); + ret = NET_RX_SUCCESS; + + } else + spin_unlock_bh(&bat_priv->orig_hash_lock); + + return ret; +} + +/* find a suitable router for this originator, and use + * bonding if possible. */ +struct neigh_node *find_router(struct bat_priv *bat_priv, + struct orig_node *orig_node, + struct batman_if *recv_if) +{ + struct orig_node *primary_orig_node; + struct orig_node *router_orig; + struct neigh_node *router, *first_candidate, *best_router; + static uint8_t zero_mac[ETH_ALEN] = {0, 0, 0, 0, 0, 0}; + int bonding_enabled; + + if (!orig_node) + return NULL; + + if (!orig_node->router) + return NULL; + + /* without bonding, the first node should + * always choose the default router. */ + + bonding_enabled = atomic_read(&bat_priv->bonding); + + if ((!recv_if) && (!bonding_enabled)) + return orig_node->router; + + router_orig = orig_node->router->orig_node; + + /* if we have something in the primary_addr, we can search + * for a potential bonding candidate. */ + if (memcmp(router_orig->primary_addr, zero_mac, ETH_ALEN) == 0) + return orig_node->router; + + /* find the orig_node which has the primary interface. might + * even be the same as our router_orig in many cases */ + + if (memcmp(router_orig->primary_addr, + router_orig->orig, ETH_ALEN) == 0) { + primary_orig_node = router_orig; + } else { + primary_orig_node = hash_find(bat_priv->orig_hash, compare_orig, + choose_orig, + router_orig->primary_addr); + + if (!primary_orig_node) + return orig_node->router; + } + + /* with less than 2 candidates, we can't do any + * bonding and prefer the original router. */ + + if (primary_orig_node->bond.candidates < 2) + return orig_node->router; + + + /* all nodes between should choose a candidate which + * is is not on the interface where the packet came + * in. */ + first_candidate = primary_orig_node->bond.selected; + router = first_candidate; + + if (bonding_enabled) { + /* in the bonding case, send the packets in a round + * robin fashion over the remaining interfaces. */ + do { + /* recv_if == NULL on the first node. */ + if (router->if_incoming != recv_if) + break; + + router = router->next_bond_candidate; + } while (router != first_candidate); + + primary_orig_node->bond.selected = router->next_bond_candidate; + + } else { + /* if bonding is disabled, use the best of the + * remaining candidates which are not using + * this interface. */ + best_router = first_candidate; + + do { + /* recv_if == NULL on the first node. */ + if ((router->if_incoming != recv_if) && + (router->tq_avg > best_router->tq_avg)) + best_router = router; + + router = router->next_bond_candidate; + } while (router != first_candidate); + + router = best_router; + } + + return router; +} + +static int check_unicast_packet(struct sk_buff *skb, int hdr_size) +{ + struct ethhdr *ethhdr; + + /* drop packet if it has not necessary minimum size */ + if (unlikely(!pskb_may_pull(skb, hdr_size))) + return -1; + + ethhdr = (struct ethhdr *)skb_mac_header(skb); + + /* packet with unicast indication but broadcast recipient */ + if (is_broadcast_ether_addr(ethhdr->h_dest)) + return -1; + + /* packet with broadcast sender address */ + if (is_broadcast_ether_addr(ethhdr->h_source)) + return -1; + + /* not for me */ + if (!is_my_mac(ethhdr->h_dest)) + return -1; + + return 0; +} + +int route_unicast_packet(struct sk_buff *skb, struct batman_if *recv_if, + int hdr_size) +{ + struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface); + struct orig_node *orig_node; + struct neigh_node *router; + struct batman_if *batman_if; + uint8_t dstaddr[ETH_ALEN]; + struct unicast_packet *unicast_packet; + struct ethhdr *ethhdr = (struct ethhdr *)skb_mac_header(skb); + int ret; + struct sk_buff *new_skb; + + unicast_packet = (struct unicast_packet *)skb->data; + + /* TTL exceeded */ + if (unicast_packet->ttl < 2) { + pr_debug("Warning - can't forward unicast packet from %pM to " + "%pM: ttl exceeded\n", ethhdr->h_source, + unicast_packet->dest); + return NET_RX_DROP; + } + + /* get routing information */ + spin_lock_bh(&bat_priv->orig_hash_lock); + orig_node = ((struct orig_node *) + hash_find(bat_priv->orig_hash, compare_orig, choose_orig, + unicast_packet->dest)); + + router = find_router(bat_priv, orig_node, recv_if); + + if (!router) { + spin_unlock_bh(&bat_priv->orig_hash_lock); + return NET_RX_DROP; + } + + /* don't lock while sending the packets ... we therefore + * copy the required data before sending */ + + batman_if = router->if_incoming; + memcpy(dstaddr, router->addr, ETH_ALEN); + + spin_unlock_bh(&bat_priv->orig_hash_lock); + + /* create a copy of the skb, if needed, to modify it. */ + if (skb_cow(skb, sizeof(struct ethhdr)) < 0) + return NET_RX_DROP; + + unicast_packet = (struct unicast_packet *)skb->data; + + if (unicast_packet->packet_type == BAT_UNICAST && + atomic_read(&bat_priv->fragmentation) && + skb->len > batman_if->net_dev->mtu) + return frag_send_skb(skb, bat_priv, batman_if, + dstaddr); + + if (unicast_packet->packet_type == BAT_UNICAST_FRAG && + 2 * skb->len - hdr_size <= batman_if->net_dev->mtu) { + + ret = frag_reassemble_skb(skb, bat_priv, &new_skb); + + if (ret == NET_RX_DROP) + return NET_RX_DROP; + + /* packet was buffered for late merge */ + if (!new_skb) + return NET_RX_SUCCESS; + + skb = new_skb; + unicast_packet = (struct unicast_packet *)skb->data; + } + + /* decrement ttl */ + unicast_packet->ttl--; + + /* route it */ + send_skb_packet(skb, batman_if, dstaddr); + + return NET_RX_SUCCESS; +} + +int recv_unicast_packet(struct sk_buff *skb, struct batman_if *recv_if) +{ + struct unicast_packet *unicast_packet; + int hdr_size = sizeof(struct unicast_packet); + + if (check_unicast_packet(skb, hdr_size) < 0) + return NET_RX_DROP; + + unicast_packet = (struct unicast_packet *)skb->data; + + /* packet for me */ + if (is_my_mac(unicast_packet->dest)) { + interface_rx(recv_if->soft_iface, skb, recv_if, hdr_size); + return NET_RX_SUCCESS; + } + + return route_unicast_packet(skb, recv_if, hdr_size); +} + +int recv_ucast_frag_packet(struct sk_buff *skb, struct batman_if *recv_if) +{ + struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface); + struct unicast_frag_packet *unicast_packet; + int hdr_size = sizeof(struct unicast_frag_packet); + struct sk_buff *new_skb = NULL; + int ret; + + if (check_unicast_packet(skb, hdr_size) < 0) + return NET_RX_DROP; + + unicast_packet = (struct unicast_frag_packet *)skb->data; + + /* packet for me */ + if (is_my_mac(unicast_packet->dest)) { + + ret = frag_reassemble_skb(skb, bat_priv, &new_skb); + + if (ret == NET_RX_DROP) + return NET_RX_DROP; + + /* packet was buffered for late merge */ + if (!new_skb) + return NET_RX_SUCCESS; + + interface_rx(recv_if->soft_iface, new_skb, recv_if, + sizeof(struct unicast_packet)); + return NET_RX_SUCCESS; + } + + return route_unicast_packet(skb, recv_if, hdr_size); +} + + +int recv_bcast_packet(struct sk_buff *skb, struct batman_if *recv_if) +{ + struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface); + struct orig_node *orig_node; + struct bcast_packet *bcast_packet; + struct ethhdr *ethhdr; + int hdr_size = sizeof(struct bcast_packet); + int32_t seq_diff; + + /* drop packet if it has not necessary minimum size */ + if (unlikely(!pskb_may_pull(skb, hdr_size))) + return NET_RX_DROP; + + ethhdr = (struct ethhdr *)skb_mac_header(skb); + + /* packet with broadcast indication but unicast recipient */ + if (!is_broadcast_ether_addr(ethhdr->h_dest)) + return NET_RX_DROP; + + /* packet with broadcast sender address */ + if (is_broadcast_ether_addr(ethhdr->h_source)) + return NET_RX_DROP; + + /* ignore broadcasts sent by myself */ + if (is_my_mac(ethhdr->h_source)) + return NET_RX_DROP; + + bcast_packet = (struct bcast_packet *)skb->data; + + /* ignore broadcasts originated by myself */ + if (is_my_mac(bcast_packet->orig)) + return NET_RX_DROP; + + if (bcast_packet->ttl < 2) + return NET_RX_DROP; + + spin_lock_bh(&bat_priv->orig_hash_lock); + orig_node = ((struct orig_node *) + hash_find(bat_priv->orig_hash, compare_orig, choose_orig, + bcast_packet->orig)); + + if (!orig_node) { + spin_unlock_bh(&bat_priv->orig_hash_lock); + return NET_RX_DROP; + } + + /* check whether the packet is a duplicate */ + if (get_bit_status(orig_node->bcast_bits, + orig_node->last_bcast_seqno, + ntohl(bcast_packet->seqno))) { + spin_unlock_bh(&bat_priv->orig_hash_lock); + return NET_RX_DROP; + } + + seq_diff = ntohl(bcast_packet->seqno) - orig_node->last_bcast_seqno; + + /* check whether the packet is old and the host just restarted. */ + if (window_protected(bat_priv, seq_diff, + &orig_node->bcast_seqno_reset)) { + spin_unlock_bh(&bat_priv->orig_hash_lock); + return NET_RX_DROP; + } + + /* mark broadcast in flood history, update window position + * if required. */ + if (bit_get_packet(bat_priv, orig_node->bcast_bits, seq_diff, 1)) + orig_node->last_bcast_seqno = ntohl(bcast_packet->seqno); + + spin_unlock_bh(&bat_priv->orig_hash_lock); + /* rebroadcast packet */ + add_bcast_packet_to_list(bat_priv, skb); + + /* broadcast for me */ + interface_rx(recv_if->soft_iface, skb, recv_if, hdr_size); + + return NET_RX_SUCCESS; +} + +int recv_vis_packet(struct sk_buff *skb, struct batman_if *recv_if) +{ + struct vis_packet *vis_packet; + struct ethhdr *ethhdr; + struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface); + int hdr_size = sizeof(struct vis_packet); + + /* keep skb linear */ + if (skb_linearize(skb) < 0) + return NET_RX_DROP; + + if (unlikely(!pskb_may_pull(skb, hdr_size))) + return NET_RX_DROP; + + vis_packet = (struct vis_packet *)skb->data; + ethhdr = (struct ethhdr *)skb_mac_header(skb); + + /* not for me */ + if (!is_my_mac(ethhdr->h_dest)) + return NET_RX_DROP; + + /* ignore own packets */ + if (is_my_mac(vis_packet->vis_orig)) + return NET_RX_DROP; + + if (is_my_mac(vis_packet->sender_orig)) + return NET_RX_DROP; + + switch (vis_packet->vis_type) { + case VIS_TYPE_SERVER_SYNC: + receive_server_sync_packet(bat_priv, vis_packet, + skb_headlen(skb)); + break; + + case VIS_TYPE_CLIENT_UPDATE: + receive_client_update_packet(bat_priv, vis_packet, + skb_headlen(skb)); + break; + + default: /* ignore unknown packet */ + break; + } + + /* We take a copy of the data in the packet, so we should + always free the skbuf. */ + return NET_RX_DROP; +} diff --git a/net/batman-adv/routing.h b/net/batman-adv/routing.h new file mode 100644 index 000000000000..f108f230bfdb --- /dev/null +++ b/net/batman-adv/routing.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner, Simon Wunderlich + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#ifndef _NET_BATMAN_ADV_ROUTING_H_ +#define _NET_BATMAN_ADV_ROUTING_H_ + +#include "types.h" + +void slide_own_bcast_window(struct batman_if *batman_if); +void receive_bat_packet(struct ethhdr *ethhdr, + struct batman_packet *batman_packet, + unsigned char *hna_buff, int hna_buff_len, + struct batman_if *if_incoming); +void update_routes(struct bat_priv *bat_priv, struct orig_node *orig_node, + struct neigh_node *neigh_node, unsigned char *hna_buff, + int hna_buff_len); +int route_unicast_packet(struct sk_buff *skb, struct batman_if *recv_if, + int hdr_size); +int recv_icmp_packet(struct sk_buff *skb, struct batman_if *recv_if); +int recv_unicast_packet(struct sk_buff *skb, struct batman_if *recv_if); +int recv_ucast_frag_packet(struct sk_buff *skb, struct batman_if *recv_if); +int recv_bcast_packet(struct sk_buff *skb, struct batman_if *recv_if); +int recv_vis_packet(struct sk_buff *skb, struct batman_if *recv_if); +int recv_bat_packet(struct sk_buff *skb, struct batman_if *recv_if); +struct neigh_node *find_router(struct bat_priv *bat_priv, + struct orig_node *orig_node, struct batman_if *recv_if); +void update_bonding_candidates(struct bat_priv *bat_priv, + struct orig_node *orig_node); + +#endif /* _NET_BATMAN_ADV_ROUTING_H_ */ diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c new file mode 100644 index 000000000000..b89b9f7709ae --- /dev/null +++ b/net/batman-adv/send.c @@ -0,0 +1,585 @@ +/* + * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner, Simon Wunderlich + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include "main.h" +#include "send.h" +#include "routing.h" +#include "translation-table.h" +#include "soft-interface.h" +#include "hard-interface.h" +#include "types.h" +#include "vis.h" +#include "aggregation.h" +#include "gateway_common.h" +#include "originator.h" + +static void send_outstanding_bcast_packet(struct work_struct *work); + +/* apply hop penalty for a normal link */ +static uint8_t hop_penalty(const uint8_t tq, struct bat_priv *bat_priv) +{ + int hop_penalty = atomic_read(&bat_priv->hop_penalty); + return (tq * (TQ_MAX_VALUE - hop_penalty)) / (TQ_MAX_VALUE); +} + +/* when do we schedule our own packet to be sent */ +static unsigned long own_send_time(struct bat_priv *bat_priv) +{ + return jiffies + msecs_to_jiffies( + atomic_read(&bat_priv->orig_interval) - + JITTER + (random32() % 2*JITTER)); +} + +/* when do we schedule a forwarded packet to be sent */ +static unsigned long forward_send_time(struct bat_priv *bat_priv) +{ + return jiffies + msecs_to_jiffies(random32() % (JITTER/2)); +} + +/* send out an already prepared packet to the given address via the + * specified batman interface */ +int send_skb_packet(struct sk_buff *skb, + struct batman_if *batman_if, + uint8_t *dst_addr) +{ + struct ethhdr *ethhdr; + + if (batman_if->if_status != IF_ACTIVE) + goto send_skb_err; + + if (unlikely(!batman_if->net_dev)) + goto send_skb_err; + + if (!(batman_if->net_dev->flags & IFF_UP)) { + pr_warning("Interface %s is not up - can't send packet via " + "that interface!\n", batman_if->net_dev->name); + goto send_skb_err; + } + + /* push to the ethernet header. */ + if (my_skb_head_push(skb, sizeof(struct ethhdr)) < 0) + goto send_skb_err; + + skb_reset_mac_header(skb); + + ethhdr = (struct ethhdr *) skb_mac_header(skb); + memcpy(ethhdr->h_source, batman_if->net_dev->dev_addr, ETH_ALEN); + memcpy(ethhdr->h_dest, dst_addr, ETH_ALEN); + ethhdr->h_proto = __constant_htons(ETH_P_BATMAN); + + skb_set_network_header(skb, ETH_HLEN); + skb->priority = TC_PRIO_CONTROL; + skb->protocol = __constant_htons(ETH_P_BATMAN); + + skb->dev = batman_if->net_dev; + + /* dev_queue_xmit() returns a negative result on error. However on + * congestion and traffic shaping, it drops and returns NET_XMIT_DROP + * (which is > 0). This will not be treated as an error. */ + + return dev_queue_xmit(skb); +send_skb_err: + kfree_skb(skb); + return NET_XMIT_DROP; +} + +/* Send a packet to a given interface */ +static void send_packet_to_if(struct forw_packet *forw_packet, + struct batman_if *batman_if) +{ + struct bat_priv *bat_priv = netdev_priv(batman_if->soft_iface); + char *fwd_str; + uint8_t packet_num; + int16_t buff_pos; + struct batman_packet *batman_packet; + struct sk_buff *skb; + + if (batman_if->if_status != IF_ACTIVE) + return; + + packet_num = 0; + buff_pos = 0; + batman_packet = (struct batman_packet *)forw_packet->skb->data; + + /* adjust all flags and log packets */ + while (aggregated_packet(buff_pos, + forw_packet->packet_len, + batman_packet->num_hna)) { + + /* we might have aggregated direct link packets with an + * ordinary base packet */ + if ((forw_packet->direct_link_flags & (1 << packet_num)) && + (forw_packet->if_incoming == batman_if)) + batman_packet->flags |= DIRECTLINK; + else + batman_packet->flags &= ~DIRECTLINK; + + fwd_str = (packet_num > 0 ? "Forwarding" : (forw_packet->own ? + "Sending own" : + "Forwarding")); + bat_dbg(DBG_BATMAN, bat_priv, + "%s %spacket (originator %pM, seqno %d, TQ %d, TTL %d," + " IDF %s) on interface %s [%pM]\n", + fwd_str, (packet_num > 0 ? "aggregated " : ""), + batman_packet->orig, ntohl(batman_packet->seqno), + batman_packet->tq, batman_packet->ttl, + (batman_packet->flags & DIRECTLINK ? + "on" : "off"), + batman_if->net_dev->name, batman_if->net_dev->dev_addr); + + buff_pos += sizeof(struct batman_packet) + + (batman_packet->num_hna * ETH_ALEN); + packet_num++; + batman_packet = (struct batman_packet *) + (forw_packet->skb->data + buff_pos); + } + + /* create clone because function is called more than once */ + skb = skb_clone(forw_packet->skb, GFP_ATOMIC); + if (skb) + send_skb_packet(skb, batman_if, broadcast_addr); +} + +/* send a batman packet */ +static void send_packet(struct forw_packet *forw_packet) +{ + struct batman_if *batman_if; + struct net_device *soft_iface; + struct bat_priv *bat_priv; + struct batman_packet *batman_packet = + (struct batman_packet *)(forw_packet->skb->data); + unsigned char directlink = (batman_packet->flags & DIRECTLINK ? 1 : 0); + + if (!forw_packet->if_incoming) { + pr_err("Error - can't forward packet: incoming iface not " + "specified\n"); + return; + } + + soft_iface = forw_packet->if_incoming->soft_iface; + bat_priv = netdev_priv(soft_iface); + + if (forw_packet->if_incoming->if_status != IF_ACTIVE) + return; + + /* multihomed peer assumed */ + /* non-primary OGMs are only broadcasted on their interface */ + if ((directlink && (batman_packet->ttl == 1)) || + (forw_packet->own && (forw_packet->if_incoming->if_num > 0))) { + + /* FIXME: what about aggregated packets ? */ + bat_dbg(DBG_BATMAN, bat_priv, + "%s packet (originator %pM, seqno %d, TTL %d) " + "on interface %s [%pM]\n", + (forw_packet->own ? "Sending own" : "Forwarding"), + batman_packet->orig, ntohl(batman_packet->seqno), + batman_packet->ttl, + forw_packet->if_incoming->net_dev->name, + forw_packet->if_incoming->net_dev->dev_addr); + + /* skb is only used once and than forw_packet is free'd */ + send_skb_packet(forw_packet->skb, forw_packet->if_incoming, + broadcast_addr); + forw_packet->skb = NULL; + + return; + } + + /* broadcast on every interface */ + rcu_read_lock(); + list_for_each_entry_rcu(batman_if, &if_list, list) { + if (batman_if->soft_iface != soft_iface) + continue; + + send_packet_to_if(forw_packet, batman_if); + } + rcu_read_unlock(); +} + +static void rebuild_batman_packet(struct bat_priv *bat_priv, + struct batman_if *batman_if) +{ + int new_len; + unsigned char *new_buff; + struct batman_packet *batman_packet; + + new_len = sizeof(struct batman_packet) + + (bat_priv->num_local_hna * ETH_ALEN); + new_buff = kmalloc(new_len, GFP_ATOMIC); + + /* keep old buffer if kmalloc should fail */ + if (new_buff) { + memcpy(new_buff, batman_if->packet_buff, + sizeof(struct batman_packet)); + batman_packet = (struct batman_packet *)new_buff; + + batman_packet->num_hna = hna_local_fill_buffer(bat_priv, + new_buff + sizeof(struct batman_packet), + new_len - sizeof(struct batman_packet)); + + kfree(batman_if->packet_buff); + batman_if->packet_buff = new_buff; + batman_if->packet_len = new_len; + } +} + +void schedule_own_packet(struct batman_if *batman_if) +{ + struct bat_priv *bat_priv = netdev_priv(batman_if->soft_iface); + unsigned long send_time; + struct batman_packet *batman_packet; + int vis_server; + + if ((batman_if->if_status == IF_NOT_IN_USE) || + (batman_if->if_status == IF_TO_BE_REMOVED)) + return; + + vis_server = atomic_read(&bat_priv->vis_mode); + + /** + * the interface gets activated here to avoid race conditions between + * the moment of activating the interface in + * hardif_activate_interface() where the originator mac is set and + * outdated packets (especially uninitialized mac addresses) in the + * packet queue + */ + if (batman_if->if_status == IF_TO_BE_ACTIVATED) + batman_if->if_status = IF_ACTIVE; + + /* if local hna has changed and interface is a primary interface */ + if ((atomic_read(&bat_priv->hna_local_changed)) && + (batman_if == bat_priv->primary_if)) + rebuild_batman_packet(bat_priv, batman_if); + + /** + * NOTE: packet_buff might just have been re-allocated in + * rebuild_batman_packet() + */ + batman_packet = (struct batman_packet *)batman_if->packet_buff; + + /* change sequence number to network order */ + batman_packet->seqno = + htonl((uint32_t)atomic_read(&batman_if->seqno)); + + if (vis_server == VIS_TYPE_SERVER_SYNC) + batman_packet->flags |= VIS_SERVER; + else + batman_packet->flags &= ~VIS_SERVER; + + if ((batman_if == bat_priv->primary_if) && + (atomic_read(&bat_priv->gw_mode) == GW_MODE_SERVER)) + batman_packet->gw_flags = + (uint8_t)atomic_read(&bat_priv->gw_bandwidth); + else + batman_packet->gw_flags = 0; + + atomic_inc(&batman_if->seqno); + + slide_own_bcast_window(batman_if); + send_time = own_send_time(bat_priv); + add_bat_packet_to_list(bat_priv, + batman_if->packet_buff, + batman_if->packet_len, + batman_if, 1, send_time); +} + +void schedule_forward_packet(struct orig_node *orig_node, + struct ethhdr *ethhdr, + struct batman_packet *batman_packet, + uint8_t directlink, int hna_buff_len, + struct batman_if *if_incoming) +{ + struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); + unsigned char in_tq, in_ttl, tq_avg = 0; + unsigned long send_time; + + if (batman_packet->ttl <= 1) { + bat_dbg(DBG_BATMAN, bat_priv, "ttl exceeded\n"); + return; + } + + in_tq = batman_packet->tq; + in_ttl = batman_packet->ttl; + + batman_packet->ttl--; + memcpy(batman_packet->prev_sender, ethhdr->h_source, ETH_ALEN); + + /* rebroadcast tq of our best ranking neighbor to ensure the rebroadcast + * of our best tq value */ + if ((orig_node->router) && (orig_node->router->tq_avg != 0)) { + + /* rebroadcast ogm of best ranking neighbor as is */ + if (!compare_orig(orig_node->router->addr, ethhdr->h_source)) { + batman_packet->tq = orig_node->router->tq_avg; + + if (orig_node->router->last_ttl) + batman_packet->ttl = orig_node->router->last_ttl + - 1; + } + + tq_avg = orig_node->router->tq_avg; + } + + /* apply hop penalty */ + batman_packet->tq = hop_penalty(batman_packet->tq, bat_priv); + + bat_dbg(DBG_BATMAN, bat_priv, + "Forwarding packet: tq_orig: %i, tq_avg: %i, " + "tq_forw: %i, ttl_orig: %i, ttl_forw: %i\n", + in_tq, tq_avg, batman_packet->tq, in_ttl - 1, + batman_packet->ttl); + + batman_packet->seqno = htonl(batman_packet->seqno); + + /* switch of primaries first hop flag when forwarding */ + batman_packet->flags &= ~PRIMARIES_FIRST_HOP; + if (directlink) + batman_packet->flags |= DIRECTLINK; + else + batman_packet->flags &= ~DIRECTLINK; + + send_time = forward_send_time(bat_priv); + add_bat_packet_to_list(bat_priv, + (unsigned char *)batman_packet, + sizeof(struct batman_packet) + hna_buff_len, + if_incoming, 0, send_time); +} + +static void forw_packet_free(struct forw_packet *forw_packet) +{ + if (forw_packet->skb) + kfree_skb(forw_packet->skb); + kfree(forw_packet); +} + +static void _add_bcast_packet_to_list(struct bat_priv *bat_priv, + struct forw_packet *forw_packet, + unsigned long send_time) +{ + INIT_HLIST_NODE(&forw_packet->list); + + /* add new packet to packet list */ + spin_lock_bh(&bat_priv->forw_bcast_list_lock); + hlist_add_head(&forw_packet->list, &bat_priv->forw_bcast_list); + spin_unlock_bh(&bat_priv->forw_bcast_list_lock); + + /* start timer for this packet */ + INIT_DELAYED_WORK(&forw_packet->delayed_work, + send_outstanding_bcast_packet); + queue_delayed_work(bat_event_workqueue, &forw_packet->delayed_work, + send_time); +} + +#define atomic_dec_not_zero(v) atomic_add_unless((v), -1, 0) +/* add a broadcast packet to the queue and setup timers. broadcast packets + * are sent multiple times to increase probability for beeing received. + * + * This function returns NETDEV_TX_OK on success and NETDEV_TX_BUSY on + * errors. + * + * The skb is not consumed, so the caller should make sure that the + * skb is freed. */ +int add_bcast_packet_to_list(struct bat_priv *bat_priv, struct sk_buff *skb) +{ + struct forw_packet *forw_packet; + struct bcast_packet *bcast_packet; + + if (!atomic_dec_not_zero(&bat_priv->bcast_queue_left)) { + bat_dbg(DBG_BATMAN, bat_priv, "bcast packet queue full\n"); + goto out; + } + + if (!bat_priv->primary_if) + goto out; + + forw_packet = kmalloc(sizeof(struct forw_packet), GFP_ATOMIC); + + if (!forw_packet) + goto out_and_inc; + + skb = skb_copy(skb, GFP_ATOMIC); + if (!skb) + goto packet_free; + + /* as we have a copy now, it is safe to decrease the TTL */ + bcast_packet = (struct bcast_packet *)skb->data; + bcast_packet->ttl--; + + skb_reset_mac_header(skb); + + forw_packet->skb = skb; + forw_packet->if_incoming = bat_priv->primary_if; + + /* how often did we send the bcast packet ? */ + forw_packet->num_packets = 0; + + _add_bcast_packet_to_list(bat_priv, forw_packet, 1); + return NETDEV_TX_OK; + +packet_free: + kfree(forw_packet); +out_and_inc: + atomic_inc(&bat_priv->bcast_queue_left); +out: + return NETDEV_TX_BUSY; +} + +static void send_outstanding_bcast_packet(struct work_struct *work) +{ + struct batman_if *batman_if; + struct delayed_work *delayed_work = + container_of(work, struct delayed_work, work); + struct forw_packet *forw_packet = + container_of(delayed_work, struct forw_packet, delayed_work); + struct sk_buff *skb1; + struct net_device *soft_iface = forw_packet->if_incoming->soft_iface; + struct bat_priv *bat_priv = netdev_priv(soft_iface); + + spin_lock_bh(&bat_priv->forw_bcast_list_lock); + hlist_del(&forw_packet->list); + spin_unlock_bh(&bat_priv->forw_bcast_list_lock); + + if (atomic_read(&bat_priv->mesh_state) == MESH_DEACTIVATING) + goto out; + + /* rebroadcast packet */ + rcu_read_lock(); + list_for_each_entry_rcu(batman_if, &if_list, list) { + if (batman_if->soft_iface != soft_iface) + continue; + + /* send a copy of the saved skb */ + skb1 = skb_clone(forw_packet->skb, GFP_ATOMIC); + if (skb1) + send_skb_packet(skb1, batman_if, broadcast_addr); + } + rcu_read_unlock(); + + forw_packet->num_packets++; + + /* if we still have some more bcasts to send */ + if (forw_packet->num_packets < 3) { + _add_bcast_packet_to_list(bat_priv, forw_packet, + ((5 * HZ) / 1000)); + return; + } + +out: + forw_packet_free(forw_packet); + atomic_inc(&bat_priv->bcast_queue_left); +} + +void send_outstanding_bat_packet(struct work_struct *work) +{ + struct delayed_work *delayed_work = + container_of(work, struct delayed_work, work); + struct forw_packet *forw_packet = + container_of(delayed_work, struct forw_packet, delayed_work); + struct bat_priv *bat_priv; + + bat_priv = netdev_priv(forw_packet->if_incoming->soft_iface); + spin_lock_bh(&bat_priv->forw_bat_list_lock); + hlist_del(&forw_packet->list); + spin_unlock_bh(&bat_priv->forw_bat_list_lock); + + if (atomic_read(&bat_priv->mesh_state) == MESH_DEACTIVATING) + goto out; + + send_packet(forw_packet); + + /** + * we have to have at least one packet in the queue + * to determine the queues wake up time unless we are + * shutting down + */ + if (forw_packet->own) + schedule_own_packet(forw_packet->if_incoming); + +out: + /* don't count own packet */ + if (!forw_packet->own) + atomic_inc(&bat_priv->batman_queue_left); + + forw_packet_free(forw_packet); +} + +void purge_outstanding_packets(struct bat_priv *bat_priv, + struct batman_if *batman_if) +{ + struct forw_packet *forw_packet; + struct hlist_node *tmp_node, *safe_tmp_node; + + if (batman_if) + bat_dbg(DBG_BATMAN, bat_priv, + "purge_outstanding_packets(): %s\n", + batman_if->net_dev->name); + else + bat_dbg(DBG_BATMAN, bat_priv, + "purge_outstanding_packets()\n"); + + /* free bcast list */ + spin_lock_bh(&bat_priv->forw_bcast_list_lock); + hlist_for_each_entry_safe(forw_packet, tmp_node, safe_tmp_node, + &bat_priv->forw_bcast_list, list) { + + /** + * if purge_outstanding_packets() was called with an argmument + * we delete only packets belonging to the given interface + */ + if ((batman_if) && + (forw_packet->if_incoming != batman_if)) + continue; + + spin_unlock_bh(&bat_priv->forw_bcast_list_lock); + + /** + * send_outstanding_bcast_packet() will lock the list to + * delete the item from the list + */ + cancel_delayed_work_sync(&forw_packet->delayed_work); + spin_lock_bh(&bat_priv->forw_bcast_list_lock); + } + spin_unlock_bh(&bat_priv->forw_bcast_list_lock); + + /* free batman packet list */ + spin_lock_bh(&bat_priv->forw_bat_list_lock); + hlist_for_each_entry_safe(forw_packet, tmp_node, safe_tmp_node, + &bat_priv->forw_bat_list, list) { + + /** + * if purge_outstanding_packets() was called with an argmument + * we delete only packets belonging to the given interface + */ + if ((batman_if) && + (forw_packet->if_incoming != batman_if)) + continue; + + spin_unlock_bh(&bat_priv->forw_bat_list_lock); + + /** + * send_outstanding_bat_packet() will lock the list to + * delete the item from the list + */ + cancel_delayed_work_sync(&forw_packet->delayed_work); + spin_lock_bh(&bat_priv->forw_bat_list_lock); + } + spin_unlock_bh(&bat_priv->forw_bat_list_lock); +} diff --git a/net/batman-adv/send.h b/net/batman-adv/send.h new file mode 100644 index 000000000000..c4cefa8e4f85 --- /dev/null +++ b/net/batman-adv/send.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner, Simon Wunderlich + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#ifndef _NET_BATMAN_ADV_SEND_H_ +#define _NET_BATMAN_ADV_SEND_H_ + +#include "types.h" + +int send_skb_packet(struct sk_buff *skb, + struct batman_if *batman_if, + uint8_t *dst_addr); +void schedule_own_packet(struct batman_if *batman_if); +void schedule_forward_packet(struct orig_node *orig_node, + struct ethhdr *ethhdr, + struct batman_packet *batman_packet, + uint8_t directlink, int hna_buff_len, + struct batman_if *if_outgoing); +int add_bcast_packet_to_list(struct bat_priv *bat_priv, struct sk_buff *skb); +void send_outstanding_bat_packet(struct work_struct *work); +void purge_outstanding_packets(struct bat_priv *bat_priv, + struct batman_if *batman_if); + +#endif /* _NET_BATMAN_ADV_SEND_H_ */ diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c new file mode 100644 index 000000000000..e89ede192ed0 --- /dev/null +++ b/net/batman-adv/soft-interface.c @@ -0,0 +1,697 @@ +/* + * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner, Simon Wunderlich + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include "main.h" +#include "soft-interface.h" +#include "hard-interface.h" +#include "routing.h" +#include "send.h" +#include "bat_debugfs.h" +#include "translation-table.h" +#include "types.h" +#include "hash.h" +#include "gateway_common.h" +#include "gateway_client.h" +#include "send.h" +#include "bat_sysfs.h" +#include +#include +#include +#include +#include "unicast.h" +#include "routing.h" + + +static int bat_get_settings(struct net_device *dev, struct ethtool_cmd *cmd); +static void bat_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info); +static u32 bat_get_msglevel(struct net_device *dev); +static void bat_set_msglevel(struct net_device *dev, u32 value); +static u32 bat_get_link(struct net_device *dev); +static u32 bat_get_rx_csum(struct net_device *dev); +static int bat_set_rx_csum(struct net_device *dev, u32 data); + +static const struct ethtool_ops bat_ethtool_ops = { + .get_settings = bat_get_settings, + .get_drvinfo = bat_get_drvinfo, + .get_msglevel = bat_get_msglevel, + .set_msglevel = bat_set_msglevel, + .get_link = bat_get_link, + .get_rx_csum = bat_get_rx_csum, + .set_rx_csum = bat_set_rx_csum +}; + +int my_skb_head_push(struct sk_buff *skb, unsigned int len) +{ + int result; + + /** + * TODO: We must check if we can release all references to non-payload + * data using skb_header_release in our skbs to allow skb_cow_header to + * work optimally. This means that those skbs are not allowed to read + * or write any data which is before the current position of skb->data + * after that call and thus allow other skbs with the same data buffer + * to write freely in that area. + */ + result = skb_cow_head(skb, len); + if (result < 0) + return result; + + skb_push(skb, len); + return 0; +} + +static void softif_neigh_free_ref(struct kref *refcount) +{ + struct softif_neigh *softif_neigh; + + softif_neigh = container_of(refcount, struct softif_neigh, refcount); + kfree(softif_neigh); +} + +static void softif_neigh_free_rcu(struct rcu_head *rcu) +{ + struct softif_neigh *softif_neigh; + + softif_neigh = container_of(rcu, struct softif_neigh, rcu); + kref_put(&softif_neigh->refcount, softif_neigh_free_ref); +} + +void softif_neigh_purge(struct bat_priv *bat_priv) +{ + struct softif_neigh *softif_neigh, *softif_neigh_tmp; + struct hlist_node *node, *node_tmp; + + spin_lock_bh(&bat_priv->softif_neigh_lock); + + hlist_for_each_entry_safe(softif_neigh, node, node_tmp, + &bat_priv->softif_neigh_list, list) { + + if ((!time_after(jiffies, softif_neigh->last_seen + + msecs_to_jiffies(SOFTIF_NEIGH_TIMEOUT))) && + (atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE)) + continue; + + hlist_del_rcu(&softif_neigh->list); + + if (bat_priv->softif_neigh == softif_neigh) { + bat_dbg(DBG_ROUTES, bat_priv, + "Current mesh exit point '%pM' vanished " + "(vid: %d).\n", + softif_neigh->addr, softif_neigh->vid); + softif_neigh_tmp = bat_priv->softif_neigh; + bat_priv->softif_neigh = NULL; + kref_put(&softif_neigh_tmp->refcount, + softif_neigh_free_ref); + } + + call_rcu(&softif_neigh->rcu, softif_neigh_free_rcu); + } + + spin_unlock_bh(&bat_priv->softif_neigh_lock); +} + +static struct softif_neigh *softif_neigh_get(struct bat_priv *bat_priv, + uint8_t *addr, short vid) +{ + struct softif_neigh *softif_neigh; + struct hlist_node *node; + + rcu_read_lock(); + hlist_for_each_entry_rcu(softif_neigh, node, + &bat_priv->softif_neigh_list, list) { + if (memcmp(softif_neigh->addr, addr, ETH_ALEN) != 0) + continue; + + if (softif_neigh->vid != vid) + continue; + + softif_neigh->last_seen = jiffies; + goto found; + } + + softif_neigh = kzalloc(sizeof(struct softif_neigh), GFP_ATOMIC); + if (!softif_neigh) + goto out; + + memcpy(softif_neigh->addr, addr, ETH_ALEN); + softif_neigh->vid = vid; + softif_neigh->last_seen = jiffies; + kref_init(&softif_neigh->refcount); + + INIT_HLIST_NODE(&softif_neigh->list); + spin_lock_bh(&bat_priv->softif_neigh_lock); + hlist_add_head_rcu(&softif_neigh->list, &bat_priv->softif_neigh_list); + spin_unlock_bh(&bat_priv->softif_neigh_lock); + +found: + kref_get(&softif_neigh->refcount); +out: + rcu_read_unlock(); + return softif_neigh; +} + +int softif_neigh_seq_print_text(struct seq_file *seq, void *offset) +{ + struct net_device *net_dev = (struct net_device *)seq->private; + struct bat_priv *bat_priv = netdev_priv(net_dev); + struct softif_neigh *softif_neigh; + struct hlist_node *node; + size_t buf_size, pos; + char *buff; + + if (!bat_priv->primary_if) { + return seq_printf(seq, "BATMAN mesh %s disabled - " + "please specify interfaces to enable it\n", + net_dev->name); + } + + seq_printf(seq, "Softif neighbor list (%s)\n", net_dev->name); + + buf_size = 1; + /* Estimate length for: " xx:xx:xx:xx:xx:xx\n" */ + rcu_read_lock(); + hlist_for_each_entry_rcu(softif_neigh, node, + &bat_priv->softif_neigh_list, list) + buf_size += 30; + rcu_read_unlock(); + + buff = kmalloc(buf_size, GFP_ATOMIC); + if (!buff) + return -ENOMEM; + + buff[0] = '\0'; + pos = 0; + + rcu_read_lock(); + hlist_for_each_entry_rcu(softif_neigh, node, + &bat_priv->softif_neigh_list, list) { + pos += snprintf(buff + pos, 31, "%s %pM (vid: %d)\n", + bat_priv->softif_neigh == softif_neigh + ? "=>" : " ", softif_neigh->addr, + softif_neigh->vid); + } + rcu_read_unlock(); + + seq_printf(seq, "%s", buff); + kfree(buff); + return 0; +} + +static void softif_batman_recv(struct sk_buff *skb, struct net_device *dev, + short vid) +{ + struct bat_priv *bat_priv = netdev_priv(dev); + struct ethhdr *ethhdr = (struct ethhdr *)skb->data; + struct batman_packet *batman_packet; + struct softif_neigh *softif_neigh, *softif_neigh_tmp; + + if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) + batman_packet = (struct batman_packet *) + (skb->data + ETH_HLEN + VLAN_HLEN); + else + batman_packet = (struct batman_packet *)(skb->data + ETH_HLEN); + + if (batman_packet->version != COMPAT_VERSION) + goto err; + + if (batman_packet->packet_type != BAT_PACKET) + goto err; + + if (!(batman_packet->flags & PRIMARIES_FIRST_HOP)) + goto err; + + if (is_my_mac(batman_packet->orig)) + goto err; + + softif_neigh = softif_neigh_get(bat_priv, batman_packet->orig, vid); + + if (!softif_neigh) + goto err; + + if (bat_priv->softif_neigh == softif_neigh) + goto out; + + /* we got a neighbor but its mac is 'bigger' than ours */ + if (memcmp(bat_priv->primary_if->net_dev->dev_addr, + softif_neigh->addr, ETH_ALEN) < 0) + goto out; + + /* switch to new 'smallest neighbor' */ + if ((bat_priv->softif_neigh) && + (memcmp(softif_neigh->addr, bat_priv->softif_neigh->addr, + ETH_ALEN) < 0)) { + bat_dbg(DBG_ROUTES, bat_priv, + "Changing mesh exit point from %pM (vid: %d) " + "to %pM (vid: %d).\n", + bat_priv->softif_neigh->addr, + bat_priv->softif_neigh->vid, + softif_neigh->addr, softif_neigh->vid); + softif_neigh_tmp = bat_priv->softif_neigh; + bat_priv->softif_neigh = softif_neigh; + kref_put(&softif_neigh_tmp->refcount, softif_neigh_free_ref); + /* we need to hold the additional reference */ + goto err; + } + + /* close own batX device and use softif_neigh as exit node */ + if ((!bat_priv->softif_neigh) && + (memcmp(softif_neigh->addr, + bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN) < 0)) { + bat_dbg(DBG_ROUTES, bat_priv, + "Setting mesh exit point to %pM (vid: %d).\n", + softif_neigh->addr, softif_neigh->vid); + bat_priv->softif_neigh = softif_neigh; + /* we need to hold the additional reference */ + goto err; + } + +out: + kref_put(&softif_neigh->refcount, softif_neigh_free_ref); +err: + kfree_skb(skb); + return; +} + +static int interface_open(struct net_device *dev) +{ + netif_start_queue(dev); + return 0; +} + +static int interface_release(struct net_device *dev) +{ + netif_stop_queue(dev); + return 0; +} + +static struct net_device_stats *interface_stats(struct net_device *dev) +{ + struct bat_priv *bat_priv = netdev_priv(dev); + return &bat_priv->stats; +} + +static int interface_set_mac_addr(struct net_device *dev, void *p) +{ + struct bat_priv *bat_priv = netdev_priv(dev); + struct sockaddr *addr = p; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + /* only modify hna-table if it has been initialised before */ + if (atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE) { + hna_local_remove(bat_priv, dev->dev_addr, + "mac address changed"); + hna_local_add(dev, addr->sa_data); + } + + memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); + return 0; +} + +static int interface_change_mtu(struct net_device *dev, int new_mtu) +{ + /* check ranges */ + if ((new_mtu < 68) || (new_mtu > hardif_min_mtu(dev))) + return -EINVAL; + + dev->mtu = new_mtu; + + return 0; +} + +int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) +{ + struct ethhdr *ethhdr = (struct ethhdr *)skb->data; + struct bat_priv *bat_priv = netdev_priv(soft_iface); + struct bcast_packet *bcast_packet; + struct vlan_ethhdr *vhdr; + int data_len = skb->len, ret; + short vid = -1; + bool do_bcast = false; + + if (atomic_read(&bat_priv->mesh_state) != MESH_ACTIVE) + goto dropped; + + soft_iface->trans_start = jiffies; + + switch (ntohs(ethhdr->h_proto)) { + case ETH_P_8021Q: + vhdr = (struct vlan_ethhdr *)skb->data; + vid = ntohs(vhdr->h_vlan_TCI) & VLAN_VID_MASK; + + if (ntohs(vhdr->h_vlan_encapsulated_proto) != ETH_P_BATMAN) + break; + + /* fall through */ + case ETH_P_BATMAN: + softif_batman_recv(skb, soft_iface, vid); + goto end; + } + + /** + * if we have a another chosen mesh exit node in range + * it will transport the packets to the mesh + */ + if ((bat_priv->softif_neigh) && (bat_priv->softif_neigh->vid == vid)) + goto dropped; + + /* TODO: check this for locks */ + hna_local_add(soft_iface, ethhdr->h_source); + + if (is_multicast_ether_addr(ethhdr->h_dest)) { + ret = gw_is_target(bat_priv, skb); + + if (ret < 0) + goto dropped; + + if (ret == 0) + do_bcast = true; + } + + /* ethernet packet should be broadcasted */ + if (do_bcast) { + if (!bat_priv->primary_if) + goto dropped; + + if (my_skb_head_push(skb, sizeof(struct bcast_packet)) < 0) + goto dropped; + + bcast_packet = (struct bcast_packet *)skb->data; + bcast_packet->version = COMPAT_VERSION; + bcast_packet->ttl = TTL; + + /* batman packet type: broadcast */ + bcast_packet->packet_type = BAT_BCAST; + + /* hw address of first interface is the orig mac because only + * this mac is known throughout the mesh */ + memcpy(bcast_packet->orig, + bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN); + + /* set broadcast sequence number */ + bcast_packet->seqno = + htonl(atomic_inc_return(&bat_priv->bcast_seqno)); + + add_bcast_packet_to_list(bat_priv, skb); + + /* a copy is stored in the bcast list, therefore removing + * the original skb. */ + kfree_skb(skb); + + /* unicast packet */ + } else { + ret = unicast_send_skb(skb, bat_priv); + if (ret != 0) + goto dropped_freed; + } + + bat_priv->stats.tx_packets++; + bat_priv->stats.tx_bytes += data_len; + goto end; + +dropped: + kfree_skb(skb); +dropped_freed: + bat_priv->stats.tx_dropped++; +end: + return NETDEV_TX_OK; +} + +void interface_rx(struct net_device *soft_iface, + struct sk_buff *skb, struct batman_if *recv_if, + int hdr_size) +{ + struct bat_priv *bat_priv = netdev_priv(soft_iface); + struct unicast_packet *unicast_packet; + struct ethhdr *ethhdr; + struct vlan_ethhdr *vhdr; + short vid = -1; + int ret; + + /* check if enough space is available for pulling, and pull */ + if (!pskb_may_pull(skb, hdr_size)) + goto dropped; + + skb_pull_rcsum(skb, hdr_size); + skb_reset_mac_header(skb); + + ethhdr = (struct ethhdr *)skb_mac_header(skb); + + switch (ntohs(ethhdr->h_proto)) { + case ETH_P_8021Q: + vhdr = (struct vlan_ethhdr *)skb->data; + vid = ntohs(vhdr->h_vlan_TCI) & VLAN_VID_MASK; + + if (ntohs(vhdr->h_vlan_encapsulated_proto) != ETH_P_BATMAN) + break; + + /* fall through */ + case ETH_P_BATMAN: + goto dropped; + } + + /** + * if we have a another chosen mesh exit node in range + * it will transport the packets to the non-mesh network + */ + if ((bat_priv->softif_neigh) && (bat_priv->softif_neigh->vid == vid)) { + skb_push(skb, hdr_size); + unicast_packet = (struct unicast_packet *)skb->data; + + if ((unicast_packet->packet_type != BAT_UNICAST) && + (unicast_packet->packet_type != BAT_UNICAST_FRAG)) + goto dropped; + + skb_reset_mac_header(skb); + + memcpy(unicast_packet->dest, + bat_priv->softif_neigh->addr, ETH_ALEN); + ret = route_unicast_packet(skb, recv_if, hdr_size); + if (ret == NET_RX_DROP) + goto dropped; + + goto out; + } + + /* skb->dev & skb->pkt_type are set here */ + if (unlikely(!pskb_may_pull(skb, ETH_HLEN))) + goto dropped; + skb->protocol = eth_type_trans(skb, soft_iface); + + /* should not be neccesary anymore as we use skb_pull_rcsum() + * TODO: please verify this and remove this TODO + * -- Dec 21st 2009, Simon Wunderlich */ + +/* skb->ip_summed = CHECKSUM_UNNECESSARY;*/ + + bat_priv->stats.rx_packets++; + bat_priv->stats.rx_bytes += skb->len + sizeof(struct ethhdr); + + soft_iface->last_rx = jiffies; + + netif_rx(skb); + return; + +dropped: + kfree_skb(skb); +out: + return; +} + +#ifdef HAVE_NET_DEVICE_OPS +static const struct net_device_ops bat_netdev_ops = { + .ndo_open = interface_open, + .ndo_stop = interface_release, + .ndo_get_stats = interface_stats, + .ndo_set_mac_address = interface_set_mac_addr, + .ndo_change_mtu = interface_change_mtu, + .ndo_start_xmit = interface_tx, + .ndo_validate_addr = eth_validate_addr +}; +#endif + +static void interface_setup(struct net_device *dev) +{ + struct bat_priv *priv = netdev_priv(dev); + char dev_addr[ETH_ALEN]; + + ether_setup(dev); + +#ifdef HAVE_NET_DEVICE_OPS + dev->netdev_ops = &bat_netdev_ops; +#else + dev->open = interface_open; + dev->stop = interface_release; + dev->get_stats = interface_stats; + dev->set_mac_address = interface_set_mac_addr; + dev->change_mtu = interface_change_mtu; + dev->hard_start_xmit = interface_tx; +#endif + dev->destructor = free_netdev; + + /** + * can't call min_mtu, because the needed variables + * have not been initialized yet + */ + dev->mtu = ETH_DATA_LEN; + dev->hard_header_len = BAT_HEADER_LEN; /* reserve more space in the + * skbuff for our header */ + + /* generate random address */ + random_ether_addr(dev_addr); + memcpy(dev->dev_addr, dev_addr, ETH_ALEN); + + SET_ETHTOOL_OPS(dev, &bat_ethtool_ops); + + memset(priv, 0, sizeof(struct bat_priv)); +} + +struct net_device *softif_create(char *name) +{ + struct net_device *soft_iface; + struct bat_priv *bat_priv; + int ret; + + soft_iface = alloc_netdev(sizeof(struct bat_priv) , name, + interface_setup); + + if (!soft_iface) { + pr_err("Unable to allocate the batman interface: %s\n", name); + goto out; + } + + ret = register_netdev(soft_iface); + if (ret < 0) { + pr_err("Unable to register the batman interface '%s': %i\n", + name, ret); + goto free_soft_iface; + } + + bat_priv = netdev_priv(soft_iface); + + atomic_set(&bat_priv->aggregated_ogms, 1); + atomic_set(&bat_priv->bonding, 0); + atomic_set(&bat_priv->vis_mode, VIS_TYPE_CLIENT_UPDATE); + atomic_set(&bat_priv->gw_mode, GW_MODE_OFF); + atomic_set(&bat_priv->gw_sel_class, 20); + atomic_set(&bat_priv->gw_bandwidth, 41); + atomic_set(&bat_priv->orig_interval, 1000); + atomic_set(&bat_priv->hop_penalty, 10); + atomic_set(&bat_priv->log_level, 0); + atomic_set(&bat_priv->fragmentation, 1); + atomic_set(&bat_priv->bcast_queue_left, BCAST_QUEUE_LEN); + atomic_set(&bat_priv->batman_queue_left, BATMAN_QUEUE_LEN); + + atomic_set(&bat_priv->mesh_state, MESH_INACTIVE); + atomic_set(&bat_priv->bcast_seqno, 1); + atomic_set(&bat_priv->hna_local_changed, 0); + + bat_priv->primary_if = NULL; + bat_priv->num_ifaces = 0; + bat_priv->softif_neigh = NULL; + + ret = sysfs_add_meshif(soft_iface); + if (ret < 0) + goto unreg_soft_iface; + + ret = debugfs_add_meshif(soft_iface); + if (ret < 0) + goto unreg_sysfs; + + ret = mesh_init(soft_iface); + if (ret < 0) + goto unreg_debugfs; + + return soft_iface; + +unreg_debugfs: + debugfs_del_meshif(soft_iface); +unreg_sysfs: + sysfs_del_meshif(soft_iface); +unreg_soft_iface: + unregister_netdev(soft_iface); + return NULL; + +free_soft_iface: + free_netdev(soft_iface); +out: + return NULL; +} + +void softif_destroy(struct net_device *soft_iface) +{ + debugfs_del_meshif(soft_iface); + sysfs_del_meshif(soft_iface); + mesh_free(soft_iface); + unregister_netdevice(soft_iface); +} + +/* ethtool */ +static int bat_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + cmd->supported = 0; + cmd->advertising = 0; + cmd->speed = SPEED_10; + cmd->duplex = DUPLEX_FULL; + cmd->port = PORT_TP; + cmd->phy_address = 0; + cmd->transceiver = XCVR_INTERNAL; + cmd->autoneg = AUTONEG_DISABLE; + cmd->maxtxpkt = 0; + cmd->maxrxpkt = 0; + + return 0; +} + +static void bat_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strcpy(info->driver, "B.A.T.M.A.N. advanced"); + strcpy(info->version, SOURCE_VERSION); + strcpy(info->fw_version, "N/A"); + strcpy(info->bus_info, "batman"); +} + +static u32 bat_get_msglevel(struct net_device *dev) +{ + return -EOPNOTSUPP; +} + +static void bat_set_msglevel(struct net_device *dev, u32 value) +{ +} + +static u32 bat_get_link(struct net_device *dev) +{ + return 1; +} + +static u32 bat_get_rx_csum(struct net_device *dev) +{ + return 0; +} + +static int bat_set_rx_csum(struct net_device *dev, u32 data) +{ + return -EOPNOTSUPP; +} diff --git a/net/batman-adv/soft-interface.h b/net/batman-adv/soft-interface.h new file mode 100644 index 000000000000..02b77334d10d --- /dev/null +++ b/net/batman-adv/soft-interface.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#ifndef _NET_BATMAN_ADV_SOFT_INTERFACE_H_ +#define _NET_BATMAN_ADV_SOFT_INTERFACE_H_ + +int my_skb_head_push(struct sk_buff *skb, unsigned int len); +int softif_neigh_seq_print_text(struct seq_file *seq, void *offset); +void softif_neigh_purge(struct bat_priv *bat_priv); +int interface_tx(struct sk_buff *skb, struct net_device *soft_iface); +void interface_rx(struct net_device *soft_iface, + struct sk_buff *skb, struct batman_if *recv_if, + int hdr_size); +struct net_device *softif_create(char *name); +void softif_destroy(struct net_device *soft_iface); + +#endif /* _NET_BATMAN_ADV_SOFT_INTERFACE_H_ */ diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c new file mode 100644 index 000000000000..a19e16c94da5 --- /dev/null +++ b/net/batman-adv/translation-table.c @@ -0,0 +1,534 @@ +/* + * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner, Simon Wunderlich + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include "main.h" +#include "translation-table.h" +#include "soft-interface.h" +#include "types.h" +#include "hash.h" +#include "originator.h" + +static void hna_local_purge(struct work_struct *work); +static void _hna_global_del_orig(struct bat_priv *bat_priv, + struct hna_global_entry *hna_global_entry, + char *message); + +static void hna_local_start_timer(struct bat_priv *bat_priv) +{ + INIT_DELAYED_WORK(&bat_priv->hna_work, hna_local_purge); + queue_delayed_work(bat_event_workqueue, &bat_priv->hna_work, 10 * HZ); +} + +int hna_local_init(struct bat_priv *bat_priv) +{ + if (bat_priv->hna_local_hash) + return 1; + + bat_priv->hna_local_hash = hash_new(1024); + + if (!bat_priv->hna_local_hash) + return 0; + + atomic_set(&bat_priv->hna_local_changed, 0); + hna_local_start_timer(bat_priv); + + return 1; +} + +void hna_local_add(struct net_device *soft_iface, uint8_t *addr) +{ + struct bat_priv *bat_priv = netdev_priv(soft_iface); + struct hna_local_entry *hna_local_entry; + struct hna_global_entry *hna_global_entry; + int required_bytes; + + spin_lock_bh(&bat_priv->hna_lhash_lock); + hna_local_entry = + ((struct hna_local_entry *)hash_find(bat_priv->hna_local_hash, + compare_orig, choose_orig, + addr)); + spin_unlock_bh(&bat_priv->hna_lhash_lock); + + if (hna_local_entry) { + hna_local_entry->last_seen = jiffies; + return; + } + + /* only announce as many hosts as possible in the batman-packet and + space in batman_packet->num_hna That also should give a limit to + MAC-flooding. */ + required_bytes = (bat_priv->num_local_hna + 1) * ETH_ALEN; + required_bytes += BAT_PACKET_LEN; + + if ((required_bytes > ETH_DATA_LEN) || + (atomic_read(&bat_priv->aggregated_ogms) && + required_bytes > MAX_AGGREGATION_BYTES) || + (bat_priv->num_local_hna + 1 > 255)) { + bat_dbg(DBG_ROUTES, bat_priv, + "Can't add new local hna entry (%pM): " + "number of local hna entries exceeds packet size\n", + addr); + return; + } + + bat_dbg(DBG_ROUTES, bat_priv, + "Creating new local hna entry: %pM\n", addr); + + hna_local_entry = kmalloc(sizeof(struct hna_local_entry), GFP_ATOMIC); + if (!hna_local_entry) + return; + + memcpy(hna_local_entry->addr, addr, ETH_ALEN); + hna_local_entry->last_seen = jiffies; + + /* the batman interface mac address should never be purged */ + if (compare_orig(addr, soft_iface->dev_addr)) + hna_local_entry->never_purge = 1; + else + hna_local_entry->never_purge = 0; + + spin_lock_bh(&bat_priv->hna_lhash_lock); + + hash_add(bat_priv->hna_local_hash, compare_orig, choose_orig, + hna_local_entry); + bat_priv->num_local_hna++; + atomic_set(&bat_priv->hna_local_changed, 1); + + spin_unlock_bh(&bat_priv->hna_lhash_lock); + + /* remove address from global hash if present */ + spin_lock_bh(&bat_priv->hna_ghash_lock); + + hna_global_entry = ((struct hna_global_entry *) + hash_find(bat_priv->hna_global_hash, + compare_orig, choose_orig, addr)); + + if (hna_global_entry) + _hna_global_del_orig(bat_priv, hna_global_entry, + "local hna received"); + + spin_unlock_bh(&bat_priv->hna_ghash_lock); +} + +int hna_local_fill_buffer(struct bat_priv *bat_priv, + unsigned char *buff, int buff_len) +{ + struct hashtable_t *hash = bat_priv->hna_local_hash; + struct hna_local_entry *hna_local_entry; + struct element_t *bucket; + int i; + struct hlist_node *walk; + struct hlist_head *head; + int count = 0; + + spin_lock_bh(&bat_priv->hna_lhash_lock); + + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + hlist_for_each_entry(bucket, walk, head, hlist) { + + if (buff_len < (count + 1) * ETH_ALEN) + break; + + hna_local_entry = bucket->data; + memcpy(buff + (count * ETH_ALEN), hna_local_entry->addr, + ETH_ALEN); + + count++; + } + } + + /* if we did not get all new local hnas see you next time ;-) */ + if (count == bat_priv->num_local_hna) + atomic_set(&bat_priv->hna_local_changed, 0); + + spin_unlock_bh(&bat_priv->hna_lhash_lock); + return i; +} + +int hna_local_seq_print_text(struct seq_file *seq, void *offset) +{ + struct net_device *net_dev = (struct net_device *)seq->private; + struct bat_priv *bat_priv = netdev_priv(net_dev); + struct hashtable_t *hash = bat_priv->hna_local_hash; + struct hna_local_entry *hna_local_entry; + int i; + struct hlist_node *walk; + struct hlist_head *head; + struct element_t *bucket; + size_t buf_size, pos; + char *buff; + + if (!bat_priv->primary_if) { + return seq_printf(seq, "BATMAN mesh %s disabled - " + "please specify interfaces to enable it\n", + net_dev->name); + } + + seq_printf(seq, "Locally retrieved addresses (from %s) " + "announced via HNA:\n", + net_dev->name); + + spin_lock_bh(&bat_priv->hna_lhash_lock); + + buf_size = 1; + /* Estimate length for: " * xx:xx:xx:xx:xx:xx\n" */ + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + hlist_for_each(walk, head) + buf_size += 21; + } + + buff = kmalloc(buf_size, GFP_ATOMIC); + if (!buff) { + spin_unlock_bh(&bat_priv->hna_lhash_lock); + return -ENOMEM; + } + buff[0] = '\0'; + pos = 0; + + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + hlist_for_each_entry(bucket, walk, head, hlist) { + hna_local_entry = bucket->data; + + pos += snprintf(buff + pos, 22, " * %pM\n", + hna_local_entry->addr); + } + } + + spin_unlock_bh(&bat_priv->hna_lhash_lock); + + seq_printf(seq, "%s", buff); + kfree(buff); + return 0; +} + +static void _hna_local_del(void *data, void *arg) +{ + struct bat_priv *bat_priv = (struct bat_priv *)arg; + + kfree(data); + bat_priv->num_local_hna--; + atomic_set(&bat_priv->hna_local_changed, 1); +} + +static void hna_local_del(struct bat_priv *bat_priv, + struct hna_local_entry *hna_local_entry, + char *message) +{ + bat_dbg(DBG_ROUTES, bat_priv, "Deleting local hna entry (%pM): %s\n", + hna_local_entry->addr, message); + + hash_remove(bat_priv->hna_local_hash, compare_orig, choose_orig, + hna_local_entry->addr); + _hna_local_del(hna_local_entry, bat_priv); +} + +void hna_local_remove(struct bat_priv *bat_priv, + uint8_t *addr, char *message) +{ + struct hna_local_entry *hna_local_entry; + + spin_lock_bh(&bat_priv->hna_lhash_lock); + + hna_local_entry = (struct hna_local_entry *) + hash_find(bat_priv->hna_local_hash, compare_orig, choose_orig, + addr); + + if (hna_local_entry) + hna_local_del(bat_priv, hna_local_entry, message); + + spin_unlock_bh(&bat_priv->hna_lhash_lock); +} + +static void hna_local_purge(struct work_struct *work) +{ + struct delayed_work *delayed_work = + container_of(work, struct delayed_work, work); + struct bat_priv *bat_priv = + container_of(delayed_work, struct bat_priv, hna_work); + struct hashtable_t *hash = bat_priv->hna_local_hash; + struct hna_local_entry *hna_local_entry; + int i; + struct hlist_node *walk, *safe; + struct hlist_head *head; + struct element_t *bucket; + unsigned long timeout; + + spin_lock_bh(&bat_priv->hna_lhash_lock); + + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + hlist_for_each_entry_safe(bucket, walk, safe, head, hlist) { + hna_local_entry = bucket->data; + + timeout = hna_local_entry->last_seen; + timeout += LOCAL_HNA_TIMEOUT * HZ; + + if ((!hna_local_entry->never_purge) && + time_after(jiffies, timeout)) + hna_local_del(bat_priv, hna_local_entry, + "address timed out"); + } + } + + spin_unlock_bh(&bat_priv->hna_lhash_lock); + hna_local_start_timer(bat_priv); +} + +void hna_local_free(struct bat_priv *bat_priv) +{ + if (!bat_priv->hna_local_hash) + return; + + cancel_delayed_work_sync(&bat_priv->hna_work); + hash_delete(bat_priv->hna_local_hash, _hna_local_del, bat_priv); + bat_priv->hna_local_hash = NULL; +} + +int hna_global_init(struct bat_priv *bat_priv) +{ + if (bat_priv->hna_global_hash) + return 1; + + bat_priv->hna_global_hash = hash_new(1024); + + if (!bat_priv->hna_global_hash) + return 0; + + return 1; +} + +void hna_global_add_orig(struct bat_priv *bat_priv, + struct orig_node *orig_node, + unsigned char *hna_buff, int hna_buff_len) +{ + struct hna_global_entry *hna_global_entry; + struct hna_local_entry *hna_local_entry; + int hna_buff_count = 0; + unsigned char *hna_ptr; + + while ((hna_buff_count + 1) * ETH_ALEN <= hna_buff_len) { + spin_lock_bh(&bat_priv->hna_ghash_lock); + + hna_ptr = hna_buff + (hna_buff_count * ETH_ALEN); + hna_global_entry = (struct hna_global_entry *) + hash_find(bat_priv->hna_global_hash, compare_orig, + choose_orig, hna_ptr); + + if (!hna_global_entry) { + spin_unlock_bh(&bat_priv->hna_ghash_lock); + + hna_global_entry = + kmalloc(sizeof(struct hna_global_entry), + GFP_ATOMIC); + + if (!hna_global_entry) + break; + + memcpy(hna_global_entry->addr, hna_ptr, ETH_ALEN); + + bat_dbg(DBG_ROUTES, bat_priv, + "Creating new global hna entry: " + "%pM (via %pM)\n", + hna_global_entry->addr, orig_node->orig); + + spin_lock_bh(&bat_priv->hna_ghash_lock); + hash_add(bat_priv->hna_global_hash, compare_orig, + choose_orig, hna_global_entry); + + } + + hna_global_entry->orig_node = orig_node; + spin_unlock_bh(&bat_priv->hna_ghash_lock); + + /* remove address from local hash if present */ + spin_lock_bh(&bat_priv->hna_lhash_lock); + + hna_ptr = hna_buff + (hna_buff_count * ETH_ALEN); + hna_local_entry = (struct hna_local_entry *) + hash_find(bat_priv->hna_local_hash, compare_orig, + choose_orig, hna_ptr); + + if (hna_local_entry) + hna_local_del(bat_priv, hna_local_entry, + "global hna received"); + + spin_unlock_bh(&bat_priv->hna_lhash_lock); + + hna_buff_count++; + } + + /* initialize, and overwrite if malloc succeeds */ + orig_node->hna_buff = NULL; + orig_node->hna_buff_len = 0; + + if (hna_buff_len > 0) { + orig_node->hna_buff = kmalloc(hna_buff_len, GFP_ATOMIC); + if (orig_node->hna_buff) { + memcpy(orig_node->hna_buff, hna_buff, hna_buff_len); + orig_node->hna_buff_len = hna_buff_len; + } + } +} + +int hna_global_seq_print_text(struct seq_file *seq, void *offset) +{ + struct net_device *net_dev = (struct net_device *)seq->private; + struct bat_priv *bat_priv = netdev_priv(net_dev); + struct hashtable_t *hash = bat_priv->hna_global_hash; + struct hna_global_entry *hna_global_entry; + int i; + struct hlist_node *walk; + struct hlist_head *head; + struct element_t *bucket; + size_t buf_size, pos; + char *buff; + + if (!bat_priv->primary_if) { + return seq_printf(seq, "BATMAN mesh %s disabled - " + "please specify interfaces to enable it\n", + net_dev->name); + } + + seq_printf(seq, "Globally announced HNAs received via the mesh %s\n", + net_dev->name); + + spin_lock_bh(&bat_priv->hna_ghash_lock); + + buf_size = 1; + /* Estimate length for: " * xx:xx:xx:xx:xx:xx via xx:xx:xx:xx:xx:xx\n"*/ + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + hlist_for_each(walk, head) + buf_size += 43; + } + + buff = kmalloc(buf_size, GFP_ATOMIC); + if (!buff) { + spin_unlock_bh(&bat_priv->hna_ghash_lock); + return -ENOMEM; + } + buff[0] = '\0'; + pos = 0; + + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + hlist_for_each_entry(bucket, walk, head, hlist) { + hna_global_entry = bucket->data; + + pos += snprintf(buff + pos, 44, + " * %pM via %pM\n", + hna_global_entry->addr, + hna_global_entry->orig_node->orig); + } + } + + spin_unlock_bh(&bat_priv->hna_ghash_lock); + + seq_printf(seq, "%s", buff); + kfree(buff); + return 0; +} + +static void _hna_global_del_orig(struct bat_priv *bat_priv, + struct hna_global_entry *hna_global_entry, + char *message) +{ + bat_dbg(DBG_ROUTES, bat_priv, + "Deleting global hna entry %pM (via %pM): %s\n", + hna_global_entry->addr, hna_global_entry->orig_node->orig, + message); + + hash_remove(bat_priv->hna_global_hash, compare_orig, choose_orig, + hna_global_entry->addr); + kfree(hna_global_entry); +} + +void hna_global_del_orig(struct bat_priv *bat_priv, + struct orig_node *orig_node, char *message) +{ + struct hna_global_entry *hna_global_entry; + int hna_buff_count = 0; + unsigned char *hna_ptr; + + if (orig_node->hna_buff_len == 0) + return; + + spin_lock_bh(&bat_priv->hna_ghash_lock); + + while ((hna_buff_count + 1) * ETH_ALEN <= orig_node->hna_buff_len) { + hna_ptr = orig_node->hna_buff + (hna_buff_count * ETH_ALEN); + hna_global_entry = (struct hna_global_entry *) + hash_find(bat_priv->hna_global_hash, compare_orig, + choose_orig, hna_ptr); + + if ((hna_global_entry) && + (hna_global_entry->orig_node == orig_node)) + _hna_global_del_orig(bat_priv, hna_global_entry, + message); + + hna_buff_count++; + } + + spin_unlock_bh(&bat_priv->hna_ghash_lock); + + orig_node->hna_buff_len = 0; + kfree(orig_node->hna_buff); + orig_node->hna_buff = NULL; +} + +static void hna_global_del(void *data, void *arg) +{ + kfree(data); +} + +void hna_global_free(struct bat_priv *bat_priv) +{ + if (!bat_priv->hna_global_hash) + return; + + hash_delete(bat_priv->hna_global_hash, hna_global_del, NULL); + bat_priv->hna_global_hash = NULL; +} + +struct orig_node *transtable_search(struct bat_priv *bat_priv, uint8_t *addr) +{ + struct hna_global_entry *hna_global_entry; + + spin_lock_bh(&bat_priv->hna_ghash_lock); + hna_global_entry = (struct hna_global_entry *) + hash_find(bat_priv->hna_global_hash, + compare_orig, choose_orig, addr); + spin_unlock_bh(&bat_priv->hna_ghash_lock); + + if (!hna_global_entry) + return NULL; + + return hna_global_entry->orig_node; +} diff --git a/net/batman-adv/translation-table.h b/net/batman-adv/translation-table.h new file mode 100644 index 000000000000..10c4c5c319b6 --- /dev/null +++ b/net/batman-adv/translation-table.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner, Simon Wunderlich + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#ifndef _NET_BATMAN_ADV_TRANSLATION_TABLE_H_ +#define _NET_BATMAN_ADV_TRANSLATION_TABLE_H_ + +#include "types.h" + +int hna_local_init(struct bat_priv *bat_priv); +void hna_local_add(struct net_device *soft_iface, uint8_t *addr); +void hna_local_remove(struct bat_priv *bat_priv, + uint8_t *addr, char *message); +int hna_local_fill_buffer(struct bat_priv *bat_priv, + unsigned char *buff, int buff_len); +int hna_local_seq_print_text(struct seq_file *seq, void *offset); +void hna_local_free(struct bat_priv *bat_priv); +int hna_global_init(struct bat_priv *bat_priv); +void hna_global_add_orig(struct bat_priv *bat_priv, + struct orig_node *orig_node, + unsigned char *hna_buff, int hna_buff_len); +int hna_global_seq_print_text(struct seq_file *seq, void *offset); +void hna_global_del_orig(struct bat_priv *bat_priv, + struct orig_node *orig_node, char *message); +void hna_global_free(struct bat_priv *bat_priv); +struct orig_node *transtable_search(struct bat_priv *bat_priv, uint8_t *addr); + +#endif /* _NET_BATMAN_ADV_TRANSLATION_TABLE_H_ */ diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h new file mode 100644 index 000000000000..97cb23dd3e69 --- /dev/null +++ b/net/batman-adv/types.h @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2007-2010 B.A.T.M.A.N. contributors: + * + * Marek Lindner, Simon Wunderlich + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + + + +#ifndef _NET_BATMAN_ADV_TYPES_H_ +#define _NET_BATMAN_ADV_TYPES_H_ + +#include "packet.h" +#include "bitarray.h" + +#define BAT_HEADER_LEN (sizeof(struct ethhdr) + \ + ((sizeof(struct unicast_packet) > sizeof(struct bcast_packet) ? \ + sizeof(struct unicast_packet) : \ + sizeof(struct bcast_packet)))) + + +struct batman_if { + struct list_head list; + int16_t if_num; + char if_status; + struct net_device *net_dev; + atomic_t seqno; + atomic_t frag_seqno; + unsigned char *packet_buff; + int packet_len; + struct kobject *hardif_obj; + struct kref refcount; + struct packet_type batman_adv_ptype; + struct net_device *soft_iface; + struct rcu_head rcu; +}; + +/** + * orig_node - structure for orig_list maintaining nodes of mesh + * @primary_addr: hosts primary interface address + * @last_valid: when last packet from this node was received + * @bcast_seqno_reset: time when the broadcast seqno window was reset + * @batman_seqno_reset: time when the batman seqno window was reset + * @gw_flags: flags related to gateway class + * @flags: for now only VIS_SERVER flag + * @last_real_seqno: last and best known squence number + * @last_ttl: ttl of last received packet + * @last_bcast_seqno: last broadcast sequence number received by this host + * + * @candidates: how many candidates are available + * @selected: next bonding candidate + */ +struct orig_node { + uint8_t orig[ETH_ALEN]; + uint8_t primary_addr[ETH_ALEN]; + struct neigh_node *router; + unsigned long *bcast_own; + uint8_t *bcast_own_sum; + uint8_t tq_own; + int tq_asym_penalty; + unsigned long last_valid; + unsigned long bcast_seqno_reset; + unsigned long batman_seqno_reset; + uint8_t gw_flags; + uint8_t flags; + unsigned char *hna_buff; + int16_t hna_buff_len; + uint32_t last_real_seqno; + uint8_t last_ttl; + unsigned long bcast_bits[NUM_WORDS]; + uint32_t last_bcast_seqno; + struct list_head neigh_list; + struct list_head frag_list; + unsigned long last_frag_packet; + struct { + uint8_t candidates; + struct neigh_node *selected; + } bond; +}; + +struct gw_node { + struct hlist_node list; + struct orig_node *orig_node; + unsigned long deleted; + struct kref refcount; + struct rcu_head rcu; +}; + +/** + * neigh_node + * @last_valid: when last packet via this neighbor was received + */ +struct neigh_node { + struct list_head list; + uint8_t addr[ETH_ALEN]; + uint8_t real_packet_count; + uint8_t tq_recv[TQ_GLOBAL_WINDOW_SIZE]; + uint8_t tq_index; + uint8_t tq_avg; + uint8_t last_ttl; + struct neigh_node *next_bond_candidate; + unsigned long last_valid; + unsigned long real_bits[NUM_WORDS]; + struct orig_node *orig_node; + struct batman_if *if_incoming; +}; + + +struct bat_priv { + atomic_t mesh_state; + struct net_device_stats stats; + atomic_t aggregated_ogms; /* boolean */ + atomic_t bonding; /* boolean */ + atomic_t fragmentation; /* boolean */ + atomic_t vis_mode; /* VIS_TYPE_* */ + atomic_t gw_mode; /* GW_MODE_* */ + atomic_t gw_sel_class; /* uint */ + atomic_t gw_bandwidth; /* gw bandwidth */ + atomic_t orig_interval; /* uint */ + atomic_t hop_penalty; /* uint */ + atomic_t log_level; /* uint */ + atomic_t bcast_seqno; + atomic_t bcast_queue_left; + atomic_t batman_queue_left; + char num_ifaces; + struct hlist_head softif_neigh_list; + struct softif_neigh *softif_neigh; + struct debug_log *debug_log; + struct batman_if *primary_if; + struct kobject *mesh_obj; + struct dentry *debug_dir; + struct hlist_head forw_bat_list; + struct hlist_head forw_bcast_list; + struct hlist_head gw_list; + struct list_head vis_send_list; + struct hashtable_t *orig_hash; + struct hashtable_t *hna_local_hash; + struct hashtable_t *hna_global_hash; + struct hashtable_t *vis_hash; + spinlock_t orig_hash_lock; /* protects orig_hash */ + spinlock_t forw_bat_list_lock; /* protects forw_bat_list */ + spinlock_t forw_bcast_list_lock; /* protects */ + spinlock_t hna_lhash_lock; /* protects hna_local_hash */ + spinlock_t hna_ghash_lock; /* protects hna_global_hash */ + spinlock_t gw_list_lock; /* protects gw_list */ + spinlock_t vis_hash_lock; /* protects vis_hash */ + spinlock_t vis_list_lock; /* protects vis_info::recv_list */ + spinlock_t softif_neigh_lock; /* protects soft-interface neigh list */ + int16_t num_local_hna; + atomic_t hna_local_changed; + struct delayed_work hna_work; + struct delayed_work orig_work; + struct delayed_work vis_work; + struct gw_node *curr_gw; + struct vis_info *my_vis_info; +}; + +struct socket_client { + struct list_head queue_list; + unsigned int queue_len; + unsigned char index; + spinlock_t lock; /* protects queue_list, queue_len, index */ + wait_queue_head_t queue_wait; + struct bat_priv *bat_priv; +}; + +struct socket_packet { + struct list_head list; + size_t icmp_len; + struct icmp_packet_rr icmp_packet; +}; + +struct hna_local_entry { + uint8_t addr[ETH_ALEN]; + unsigned long last_seen; + char never_purge; +}; + +struct hna_global_entry { + uint8_t addr[ETH_ALEN]; + struct orig_node *orig_node; +}; + +/** + * forw_packet - structure for forw_list maintaining packets to be + * send/forwarded + */ +struct forw_packet { + struct hlist_node list; + unsigned long send_time; + uint8_t own; + struct sk_buff *skb; + uint16_t packet_len; + uint32_t direct_link_flags; + uint8_t num_packets; + struct delayed_work delayed_work; + struct batman_if *if_incoming; +}; + +/* While scanning for vis-entries of a particular vis-originator + * this list collects its interfaces to create a subgraph/cluster + * out of them later + */ +struct if_list_entry { + uint8_t addr[ETH_ALEN]; + bool primary; + struct hlist_node list; +}; + +struct debug_log { + char log_buff[LOG_BUF_LEN]; + unsigned long log_start; + unsigned long log_end; + spinlock_t lock; /* protects log_buff, log_start and log_end */ + wait_queue_head_t queue_wait; +}; + +struct frag_packet_list_entry { + struct list_head list; + uint16_t seqno; + struct sk_buff *skb; +}; + +struct vis_info { + unsigned long first_seen; + struct list_head recv_list; + /* list of server-neighbors we received a vis-packet + * from. we should not reply to them. */ + struct list_head send_list; + struct kref refcount; + struct bat_priv *bat_priv; + /* this packet might be part of the vis send queue. */ + struct sk_buff *skb_packet; + /* vis_info may follow here*/ +} __attribute__((packed)); + +struct vis_info_entry { + uint8_t src[ETH_ALEN]; + uint8_t dest[ETH_ALEN]; + uint8_t quality; /* quality = 0 means HNA */ +} __attribute__((packed)); + +struct recvlist_node { + struct list_head list; + uint8_t mac[ETH_ALEN]; +}; + +struct softif_neigh { + struct hlist_node list; + uint8_t addr[ETH_ALEN]; + unsigned long last_seen; + short vid; + struct kref refcount; + struct rcu_head rcu; +}; + +#endif /* _NET_BATMAN_ADV_TYPES_H_ */ diff --git a/net/batman-adv/unicast.c b/net/batman-adv/unicast.c new file mode 100644 index 000000000000..dc2e28bed844 --- /dev/null +++ b/net/batman-adv/unicast.c @@ -0,0 +1,343 @@ +/* + * Copyright (C) 2010 B.A.T.M.A.N. contributors: + * + * Andreas Langer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include "main.h" +#include "unicast.h" +#include "send.h" +#include "soft-interface.h" +#include "gateway_client.h" +#include "originator.h" +#include "hash.h" +#include "translation-table.h" +#include "routing.h" +#include "hard-interface.h" + + +static struct sk_buff *frag_merge_packet(struct list_head *head, + struct frag_packet_list_entry *tfp, + struct sk_buff *skb) +{ + struct unicast_frag_packet *up = + (struct unicast_frag_packet *)skb->data; + struct sk_buff *tmp_skb; + struct unicast_packet *unicast_packet; + int hdr_len = sizeof(struct unicast_packet), + uni_diff = sizeof(struct unicast_frag_packet) - hdr_len; + + /* set skb to the first part and tmp_skb to the second part */ + if (up->flags & UNI_FRAG_HEAD) { + tmp_skb = tfp->skb; + } else { + tmp_skb = skb; + skb = tfp->skb; + } + + skb_pull(tmp_skb, sizeof(struct unicast_frag_packet)); + if (pskb_expand_head(skb, 0, tmp_skb->len, GFP_ATOMIC) < 0) { + /* free buffered skb, skb will be freed later */ + kfree_skb(tfp->skb); + return NULL; + } + + /* move free entry to end */ + tfp->skb = NULL; + tfp->seqno = 0; + list_move_tail(&tfp->list, head); + + memcpy(skb_put(skb, tmp_skb->len), tmp_skb->data, tmp_skb->len); + kfree_skb(tmp_skb); + + memmove(skb->data + uni_diff, skb->data, hdr_len); + unicast_packet = (struct unicast_packet *) skb_pull(skb, uni_diff); + unicast_packet->packet_type = BAT_UNICAST; + + return skb; +} + +static void frag_create_entry(struct list_head *head, struct sk_buff *skb) +{ + struct frag_packet_list_entry *tfp; + struct unicast_frag_packet *up = + (struct unicast_frag_packet *)skb->data; + + /* free and oldest packets stand at the end */ + tfp = list_entry((head)->prev, typeof(*tfp), list); + kfree_skb(tfp->skb); + + tfp->seqno = ntohs(up->seqno); + tfp->skb = skb; + list_move(&tfp->list, head); + return; +} + +static int frag_create_buffer(struct list_head *head) +{ + int i; + struct frag_packet_list_entry *tfp; + + for (i = 0; i < FRAG_BUFFER_SIZE; i++) { + tfp = kmalloc(sizeof(struct frag_packet_list_entry), + GFP_ATOMIC); + if (!tfp) { + frag_list_free(head); + return -ENOMEM; + } + tfp->skb = NULL; + tfp->seqno = 0; + INIT_LIST_HEAD(&tfp->list); + list_add(&tfp->list, head); + } + + return 0; +} + +static struct frag_packet_list_entry *frag_search_packet(struct list_head *head, + struct unicast_frag_packet *up) +{ + struct frag_packet_list_entry *tfp; + struct unicast_frag_packet *tmp_up = NULL; + uint16_t search_seqno; + + if (up->flags & UNI_FRAG_HEAD) + search_seqno = ntohs(up->seqno)+1; + else + search_seqno = ntohs(up->seqno)-1; + + list_for_each_entry(tfp, head, list) { + + if (!tfp->skb) + continue; + + if (tfp->seqno == ntohs(up->seqno)) + goto mov_tail; + + tmp_up = (struct unicast_frag_packet *)tfp->skb->data; + + if (tfp->seqno == search_seqno) { + + if ((tmp_up->flags & UNI_FRAG_HEAD) != + (up->flags & UNI_FRAG_HEAD)) + return tfp; + else + goto mov_tail; + } + } + return NULL; + +mov_tail: + list_move_tail(&tfp->list, head); + return NULL; +} + +void frag_list_free(struct list_head *head) +{ + struct frag_packet_list_entry *pf, *tmp_pf; + + if (!list_empty(head)) { + + list_for_each_entry_safe(pf, tmp_pf, head, list) { + kfree_skb(pf->skb); + list_del(&pf->list); + kfree(pf); + } + } + return; +} + +/* frag_reassemble_skb(): + * returns NET_RX_DROP if the operation failed - skb is left intact + * returns NET_RX_SUCCESS if the fragment was buffered (skb_new will be NULL) + * or the skb could be reassembled (skb_new will point to the new packet and + * skb was freed) + */ +int frag_reassemble_skb(struct sk_buff *skb, struct bat_priv *bat_priv, + struct sk_buff **new_skb) +{ + struct orig_node *orig_node; + struct frag_packet_list_entry *tmp_frag_entry; + int ret = NET_RX_DROP; + struct unicast_frag_packet *unicast_packet = + (struct unicast_frag_packet *)skb->data; + + *new_skb = NULL; + spin_lock_bh(&bat_priv->orig_hash_lock); + orig_node = ((struct orig_node *) + hash_find(bat_priv->orig_hash, compare_orig, choose_orig, + unicast_packet->orig)); + + if (!orig_node) { + pr_debug("couldn't find originator in orig_hash\n"); + goto out; + } + + orig_node->last_frag_packet = jiffies; + + if (list_empty(&orig_node->frag_list) && + frag_create_buffer(&orig_node->frag_list)) { + pr_debug("couldn't create frag buffer\n"); + goto out; + } + + tmp_frag_entry = frag_search_packet(&orig_node->frag_list, + unicast_packet); + + if (!tmp_frag_entry) { + frag_create_entry(&orig_node->frag_list, skb); + ret = NET_RX_SUCCESS; + goto out; + } + + *new_skb = frag_merge_packet(&orig_node->frag_list, tmp_frag_entry, + skb); + /* if not, merge failed */ + if (*new_skb) + ret = NET_RX_SUCCESS; +out: + spin_unlock_bh(&bat_priv->orig_hash_lock); + + return ret; +} + +int frag_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv, + struct batman_if *batman_if, uint8_t dstaddr[]) +{ + struct unicast_packet tmp_uc, *unicast_packet; + struct sk_buff *frag_skb; + struct unicast_frag_packet *frag1, *frag2; + int uc_hdr_len = sizeof(struct unicast_packet); + int ucf_hdr_len = sizeof(struct unicast_frag_packet); + int data_len = skb->len; + + if (!bat_priv->primary_if) + goto dropped; + + unicast_packet = (struct unicast_packet *) skb->data; + + memcpy(&tmp_uc, unicast_packet, uc_hdr_len); + frag_skb = dev_alloc_skb(data_len - (data_len / 2) + ucf_hdr_len); + skb_split(skb, frag_skb, data_len / 2); + + if (my_skb_head_push(skb, ucf_hdr_len - uc_hdr_len) < 0 || + my_skb_head_push(frag_skb, ucf_hdr_len) < 0) + goto drop_frag; + + frag1 = (struct unicast_frag_packet *)skb->data; + frag2 = (struct unicast_frag_packet *)frag_skb->data; + + memcpy(frag1, &tmp_uc, sizeof(struct unicast_packet)); + + frag1->ttl--; + frag1->version = COMPAT_VERSION; + frag1->packet_type = BAT_UNICAST_FRAG; + + memcpy(frag1->orig, bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN); + memcpy(frag2, frag1, sizeof(struct unicast_frag_packet)); + + frag1->flags |= UNI_FRAG_HEAD; + frag2->flags &= ~UNI_FRAG_HEAD; + + frag1->seqno = htons((uint16_t)atomic_inc_return( + &batman_if->frag_seqno)); + frag2->seqno = htons((uint16_t)atomic_inc_return( + &batman_if->frag_seqno)); + + send_skb_packet(skb, batman_if, dstaddr); + send_skb_packet(frag_skb, batman_if, dstaddr); + return NET_RX_SUCCESS; + +drop_frag: + kfree_skb(frag_skb); +dropped: + kfree_skb(skb); + return NET_RX_DROP; +} + +int unicast_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv) +{ + struct ethhdr *ethhdr = (struct ethhdr *)skb->data; + struct unicast_packet *unicast_packet; + struct orig_node *orig_node; + struct batman_if *batman_if; + struct neigh_node *router; + int data_len = skb->len; + uint8_t dstaddr[6]; + + spin_lock_bh(&bat_priv->orig_hash_lock); + + /* get routing information */ + if (is_multicast_ether_addr(ethhdr->h_dest)) + orig_node = (struct orig_node *)gw_get_selected(bat_priv); + else + orig_node = ((struct orig_node *)hash_find(bat_priv->orig_hash, + compare_orig, + choose_orig, + ethhdr->h_dest)); + + /* check for hna host */ + if (!orig_node) + orig_node = transtable_search(bat_priv, ethhdr->h_dest); + + router = find_router(bat_priv, orig_node, NULL); + + if (!router) + goto unlock; + + /* don't lock while sending the packets ... we therefore + * copy the required data before sending */ + + batman_if = router->if_incoming; + memcpy(dstaddr, router->addr, ETH_ALEN); + + spin_unlock_bh(&bat_priv->orig_hash_lock); + + if (batman_if->if_status != IF_ACTIVE) + goto dropped; + + if (my_skb_head_push(skb, sizeof(struct unicast_packet)) < 0) + goto dropped; + + unicast_packet = (struct unicast_packet *)skb->data; + + unicast_packet->version = COMPAT_VERSION; + /* batman packet type: unicast */ + unicast_packet->packet_type = BAT_UNICAST; + /* set unicast ttl */ + unicast_packet->ttl = TTL; + /* copy the destination for faster routing */ + memcpy(unicast_packet->dest, orig_node->orig, ETH_ALEN); + + if (atomic_read(&bat_priv->fragmentation) && + data_len + sizeof(struct unicast_packet) > + batman_if->net_dev->mtu) { + /* send frag skb decreases ttl */ + unicast_packet->ttl++; + return frag_send_skb(skb, bat_priv, batman_if, + dstaddr); + } + send_skb_packet(skb, batman_if, dstaddr); + return 0; + +unlock: + spin_unlock_bh(&bat_priv->orig_hash_lock); +dropped: + kfree_skb(skb); + return 1; +} diff --git a/net/batman-adv/unicast.h b/net/batman-adv/unicast.h new file mode 100644 index 000000000000..e32b7867a9a4 --- /dev/null +++ b/net/batman-adv/unicast.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2010 B.A.T.M.A.N. contributors: + * + * Andreas Langer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#ifndef _NET_BATMAN_ADV_UNICAST_H_ +#define _NET_BATMAN_ADV_UNICAST_H_ + +#define FRAG_TIMEOUT 10000 /* purge frag list entrys after time in ms */ +#define FRAG_BUFFER_SIZE 6 /* number of list elements in buffer */ + +int frag_reassemble_skb(struct sk_buff *skb, struct bat_priv *bat_priv, + struct sk_buff **new_skb); +void frag_list_free(struct list_head *head); +int unicast_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv); +int frag_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv, + struct batman_if *batman_if, uint8_t dstaddr[]); + +#endif /* _NET_BATMAN_ADV_UNICAST_H_ */ diff --git a/net/batman-adv/vis.c b/net/batman-adv/vis.c new file mode 100644 index 000000000000..cd4c4231fa48 --- /dev/null +++ b/net/batman-adv/vis.c @@ -0,0 +1,949 @@ +/* + * Copyright (C) 2008-2010 B.A.T.M.A.N. contributors: + * + * Simon Wunderlich + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include "main.h" +#include "send.h" +#include "translation-table.h" +#include "vis.h" +#include "soft-interface.h" +#include "hard-interface.h" +#include "hash.h" +#include "originator.h" + +#define MAX_VIS_PACKET_SIZE 1000 + +/* Returns the smallest signed integer in two's complement with the sizeof x */ +#define smallest_signed_int(x) (1u << (7u + 8u * (sizeof(x) - 1u))) + +/* Checks if a sequence number x is a predecessor/successor of y. + * they handle overflows/underflows and can correctly check for a + * predecessor/successor unless the variable sequence number has grown by + * more then 2**(bitwidth(x)-1)-1. + * This means that for a uint8_t with the maximum value 255, it would think: + * - when adding nothing - it is neither a predecessor nor a successor + * - before adding more than 127 to the starting value - it is a predecessor, + * - when adding 128 - it is neither a predecessor nor a successor, + * - after adding more than 127 to the starting value - it is a successor */ +#define seq_before(x, y) ({typeof(x) _dummy = (x - y); \ + _dummy > smallest_signed_int(_dummy); }) +#define seq_after(x, y) seq_before(y, x) + +static void start_vis_timer(struct bat_priv *bat_priv); + +/* free the info */ +static void free_info(struct kref *ref) +{ + struct vis_info *info = container_of(ref, struct vis_info, refcount); + struct bat_priv *bat_priv = info->bat_priv; + struct recvlist_node *entry, *tmp; + + list_del_init(&info->send_list); + spin_lock_bh(&bat_priv->vis_list_lock); + list_for_each_entry_safe(entry, tmp, &info->recv_list, list) { + list_del(&entry->list); + kfree(entry); + } + + spin_unlock_bh(&bat_priv->vis_list_lock); + kfree_skb(info->skb_packet); +} + +/* Compare two vis packets, used by the hashing algorithm */ +static int vis_info_cmp(void *data1, void *data2) +{ + struct vis_info *d1, *d2; + struct vis_packet *p1, *p2; + d1 = data1; + d2 = data2; + p1 = (struct vis_packet *)d1->skb_packet->data; + p2 = (struct vis_packet *)d2->skb_packet->data; + return compare_orig(p1->vis_orig, p2->vis_orig); +} + +/* hash function to choose an entry in a hash table of given size */ +/* hash algorithm from http://en.wikipedia.org/wiki/Hash_table */ +static int vis_info_choose(void *data, int size) +{ + struct vis_info *vis_info = data; + struct vis_packet *packet; + unsigned char *key; + uint32_t hash = 0; + size_t i; + + packet = (struct vis_packet *)vis_info->skb_packet->data; + key = packet->vis_orig; + for (i = 0; i < ETH_ALEN; i++) { + hash += key[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + + return hash % size; +} + +/* insert interface to the list of interfaces of one originator, if it + * does not already exist in the list */ +static void vis_data_insert_interface(const uint8_t *interface, + struct hlist_head *if_list, + bool primary) +{ + struct if_list_entry *entry; + struct hlist_node *pos; + + hlist_for_each_entry(entry, pos, if_list, list) { + if (compare_orig(entry->addr, (void *)interface)) + return; + } + + /* its a new address, add it to the list */ + entry = kmalloc(sizeof(*entry), GFP_ATOMIC); + if (!entry) + return; + memcpy(entry->addr, interface, ETH_ALEN); + entry->primary = primary; + hlist_add_head(&entry->list, if_list); +} + +static ssize_t vis_data_read_prim_sec(char *buff, struct hlist_head *if_list) +{ + struct if_list_entry *entry; + struct hlist_node *pos; + size_t len = 0; + + hlist_for_each_entry(entry, pos, if_list, list) { + if (entry->primary) + len += sprintf(buff + len, "PRIMARY, "); + else + len += sprintf(buff + len, "SEC %pM, ", entry->addr); + } + + return len; +} + +static size_t vis_data_count_prim_sec(struct hlist_head *if_list) +{ + struct if_list_entry *entry; + struct hlist_node *pos; + size_t count = 0; + + hlist_for_each_entry(entry, pos, if_list, list) { + if (entry->primary) + count += 9; + else + count += 23; + } + + return count; +} + +/* read an entry */ +static ssize_t vis_data_read_entry(char *buff, struct vis_info_entry *entry, + uint8_t *src, bool primary) +{ + /* maximal length: max(4+17+2, 3+17+1+3+2) == 26 */ + if (primary && entry->quality == 0) + return sprintf(buff, "HNA %pM, ", entry->dest); + else if (compare_orig(entry->src, src)) + return sprintf(buff, "TQ %pM %d, ", entry->dest, + entry->quality); + + return 0; +} + +int vis_seq_print_text(struct seq_file *seq, void *offset) +{ + struct hlist_node *walk; + struct hlist_head *head; + struct element_t *bucket; + struct vis_info *info; + struct vis_packet *packet; + struct vis_info_entry *entries; + struct net_device *net_dev = (struct net_device *)seq->private; + struct bat_priv *bat_priv = netdev_priv(net_dev); + struct hashtable_t *hash = bat_priv->vis_hash; + HLIST_HEAD(vis_if_list); + struct if_list_entry *entry; + struct hlist_node *pos, *n; + int i, j; + int vis_server = atomic_read(&bat_priv->vis_mode); + size_t buff_pos, buf_size; + char *buff; + int compare; + + if ((!bat_priv->primary_if) || + (vis_server == VIS_TYPE_CLIENT_UPDATE)) + return 0; + + buf_size = 1; + /* Estimate length */ + spin_lock_bh(&bat_priv->vis_hash_lock); + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + hlist_for_each_entry(bucket, walk, head, hlist) { + info = bucket->data; + packet = (struct vis_packet *)info->skb_packet->data; + entries = (struct vis_info_entry *) + ((char *)packet + sizeof(struct vis_packet)); + + for (j = 0; j < packet->entries; j++) { + if (entries[j].quality == 0) + continue; + compare = + compare_orig(entries[j].src, packet->vis_orig); + vis_data_insert_interface(entries[j].src, + &vis_if_list, + compare); + } + + hlist_for_each_entry(entry, pos, &vis_if_list, list) { + buf_size += 18 + 26 * packet->entries; + + /* add primary/secondary records */ + if (compare_orig(entry->addr, packet->vis_orig)) + buf_size += + vis_data_count_prim_sec(&vis_if_list); + + buf_size += 1; + } + + hlist_for_each_entry_safe(entry, pos, n, &vis_if_list, + list) { + hlist_del(&entry->list); + kfree(entry); + } + } + } + + buff = kmalloc(buf_size, GFP_ATOMIC); + if (!buff) { + spin_unlock_bh(&bat_priv->vis_hash_lock); + return -ENOMEM; + } + buff[0] = '\0'; + buff_pos = 0; + + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + hlist_for_each_entry(bucket, walk, head, hlist) { + info = bucket->data; + packet = (struct vis_packet *)info->skb_packet->data; + entries = (struct vis_info_entry *) + ((char *)packet + sizeof(struct vis_packet)); + + for (j = 0; j < packet->entries; j++) { + if (entries[j].quality == 0) + continue; + compare = + compare_orig(entries[j].src, packet->vis_orig); + vis_data_insert_interface(entries[j].src, + &vis_if_list, + compare); + } + + hlist_for_each_entry(entry, pos, &vis_if_list, list) { + buff_pos += sprintf(buff + buff_pos, "%pM,", + entry->addr); + + for (i = 0; i < packet->entries; i++) + buff_pos += vis_data_read_entry( + buff + buff_pos, + &entries[i], + entry->addr, + entry->primary); + + /* add primary/secondary records */ + if (compare_orig(entry->addr, packet->vis_orig)) + buff_pos += + vis_data_read_prim_sec(buff + buff_pos, + &vis_if_list); + + buff_pos += sprintf(buff + buff_pos, "\n"); + } + + hlist_for_each_entry_safe(entry, pos, n, &vis_if_list, + list) { + hlist_del(&entry->list); + kfree(entry); + } + } + } + + spin_unlock_bh(&bat_priv->vis_hash_lock); + + seq_printf(seq, "%s", buff); + kfree(buff); + + return 0; +} + +/* add the info packet to the send list, if it was not + * already linked in. */ +static void send_list_add(struct bat_priv *bat_priv, struct vis_info *info) +{ + if (list_empty(&info->send_list)) { + kref_get(&info->refcount); + list_add_tail(&info->send_list, &bat_priv->vis_send_list); + } +} + +/* delete the info packet from the send list, if it was + * linked in. */ +static void send_list_del(struct vis_info *info) +{ + if (!list_empty(&info->send_list)) { + list_del_init(&info->send_list); + kref_put(&info->refcount, free_info); + } +} + +/* tries to add one entry to the receive list. */ +static void recv_list_add(struct bat_priv *bat_priv, + struct list_head *recv_list, char *mac) +{ + struct recvlist_node *entry; + + entry = kmalloc(sizeof(struct recvlist_node), GFP_ATOMIC); + if (!entry) + return; + + memcpy(entry->mac, mac, ETH_ALEN); + spin_lock_bh(&bat_priv->vis_list_lock); + list_add_tail(&entry->list, recv_list); + spin_unlock_bh(&bat_priv->vis_list_lock); +} + +/* returns 1 if this mac is in the recv_list */ +static int recv_list_is_in(struct bat_priv *bat_priv, + struct list_head *recv_list, char *mac) +{ + struct recvlist_node *entry; + + spin_lock_bh(&bat_priv->vis_list_lock); + list_for_each_entry(entry, recv_list, list) { + if (memcmp(entry->mac, mac, ETH_ALEN) == 0) { + spin_unlock_bh(&bat_priv->vis_list_lock); + return 1; + } + } + spin_unlock_bh(&bat_priv->vis_list_lock); + return 0; +} + +/* try to add the packet to the vis_hash. return NULL if invalid (e.g. too old, + * broken.. ). vis hash must be locked outside. is_new is set when the packet + * is newer than old entries in the hash. */ +static struct vis_info *add_packet(struct bat_priv *bat_priv, + struct vis_packet *vis_packet, + int vis_info_len, int *is_new, + int make_broadcast) +{ + struct vis_info *info, *old_info; + struct vis_packet *search_packet, *old_packet; + struct vis_info search_elem; + struct vis_packet *packet; + int hash_added; + + *is_new = 0; + /* sanity check */ + if (!bat_priv->vis_hash) + return NULL; + + /* see if the packet is already in vis_hash */ + search_elem.skb_packet = dev_alloc_skb(sizeof(struct vis_packet)); + if (!search_elem.skb_packet) + return NULL; + search_packet = (struct vis_packet *)skb_put(search_elem.skb_packet, + sizeof(struct vis_packet)); + + memcpy(search_packet->vis_orig, vis_packet->vis_orig, ETH_ALEN); + old_info = hash_find(bat_priv->vis_hash, vis_info_cmp, vis_info_choose, + &search_elem); + kfree_skb(search_elem.skb_packet); + + if (old_info) { + old_packet = (struct vis_packet *)old_info->skb_packet->data; + if (!seq_after(ntohl(vis_packet->seqno), + ntohl(old_packet->seqno))) { + if (old_packet->seqno == vis_packet->seqno) { + recv_list_add(bat_priv, &old_info->recv_list, + vis_packet->sender_orig); + return old_info; + } else { + /* newer packet is already in hash. */ + return NULL; + } + } + /* remove old entry */ + hash_remove(bat_priv->vis_hash, vis_info_cmp, vis_info_choose, + old_info); + send_list_del(old_info); + kref_put(&old_info->refcount, free_info); + } + + info = kmalloc(sizeof(struct vis_info), GFP_ATOMIC); + if (!info) + return NULL; + + info->skb_packet = dev_alloc_skb(sizeof(struct vis_packet) + + vis_info_len + sizeof(struct ethhdr)); + if (!info->skb_packet) { + kfree(info); + return NULL; + } + skb_reserve(info->skb_packet, sizeof(struct ethhdr)); + packet = (struct vis_packet *)skb_put(info->skb_packet, + sizeof(struct vis_packet) + + vis_info_len); + + kref_init(&info->refcount); + INIT_LIST_HEAD(&info->send_list); + INIT_LIST_HEAD(&info->recv_list); + info->first_seen = jiffies; + info->bat_priv = bat_priv; + memcpy(packet, vis_packet, sizeof(struct vis_packet) + vis_info_len); + + /* initialize and add new packet. */ + *is_new = 1; + + /* Make it a broadcast packet, if required */ + if (make_broadcast) + memcpy(packet->target_orig, broadcast_addr, ETH_ALEN); + + /* repair if entries is longer than packet. */ + if (packet->entries * sizeof(struct vis_info_entry) > vis_info_len) + packet->entries = vis_info_len / sizeof(struct vis_info_entry); + + recv_list_add(bat_priv, &info->recv_list, packet->sender_orig); + + /* try to add it */ + hash_added = hash_add(bat_priv->vis_hash, vis_info_cmp, vis_info_choose, + info); + if (hash_added < 0) { + /* did not work (for some reason) */ + kref_put(&old_info->refcount, free_info); + info = NULL; + } + + return info; +} + +/* handle the server sync packet, forward if needed. */ +void receive_server_sync_packet(struct bat_priv *bat_priv, + struct vis_packet *vis_packet, + int vis_info_len) +{ + struct vis_info *info; + int is_new, make_broadcast; + int vis_server = atomic_read(&bat_priv->vis_mode); + + make_broadcast = (vis_server == VIS_TYPE_SERVER_SYNC); + + spin_lock_bh(&bat_priv->vis_hash_lock); + info = add_packet(bat_priv, vis_packet, vis_info_len, + &is_new, make_broadcast); + if (!info) + goto end; + + /* only if we are server ourselves and packet is newer than the one in + * hash.*/ + if (vis_server == VIS_TYPE_SERVER_SYNC && is_new) + send_list_add(bat_priv, info); +end: + spin_unlock_bh(&bat_priv->vis_hash_lock); +} + +/* handle an incoming client update packet and schedule forward if needed. */ +void receive_client_update_packet(struct bat_priv *bat_priv, + struct vis_packet *vis_packet, + int vis_info_len) +{ + struct vis_info *info; + struct vis_packet *packet; + int is_new; + int vis_server = atomic_read(&bat_priv->vis_mode); + int are_target = 0; + + /* clients shall not broadcast. */ + if (is_broadcast_ether_addr(vis_packet->target_orig)) + return; + + /* Are we the target for this VIS packet? */ + if (vis_server == VIS_TYPE_SERVER_SYNC && + is_my_mac(vis_packet->target_orig)) + are_target = 1; + + spin_lock_bh(&bat_priv->vis_hash_lock); + info = add_packet(bat_priv, vis_packet, vis_info_len, + &is_new, are_target); + + if (!info) + goto end; + /* note that outdated packets will be dropped at this point. */ + + packet = (struct vis_packet *)info->skb_packet->data; + + /* send only if we're the target server or ... */ + if (are_target && is_new) { + packet->vis_type = VIS_TYPE_SERVER_SYNC; /* upgrade! */ + send_list_add(bat_priv, info); + + /* ... we're not the recipient (and thus need to forward). */ + } else if (!is_my_mac(packet->target_orig)) { + send_list_add(bat_priv, info); + } + +end: + spin_unlock_bh(&bat_priv->vis_hash_lock); +} + +/* Walk the originators and find the VIS server with the best tq. Set the packet + * address to its address and return the best_tq. + * + * Must be called with the originator hash locked */ +static int find_best_vis_server(struct bat_priv *bat_priv, + struct vis_info *info) +{ + struct hashtable_t *hash = bat_priv->orig_hash; + struct hlist_node *walk; + struct hlist_head *head; + struct element_t *bucket; + struct orig_node *orig_node; + struct vis_packet *packet; + int best_tq = -1, i; + + packet = (struct vis_packet *)info->skb_packet->data; + + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + hlist_for_each_entry(bucket, walk, head, hlist) { + orig_node = bucket->data; + if ((orig_node) && (orig_node->router) && + (orig_node->flags & VIS_SERVER) && + (orig_node->router->tq_avg > best_tq)) { + best_tq = orig_node->router->tq_avg; + memcpy(packet->target_orig, orig_node->orig, + ETH_ALEN); + } + } + } + + return best_tq; +} + +/* Return true if the vis packet is full. */ +static bool vis_packet_full(struct vis_info *info) +{ + struct vis_packet *packet; + packet = (struct vis_packet *)info->skb_packet->data; + + if (MAX_VIS_PACKET_SIZE / sizeof(struct vis_info_entry) + < packet->entries + 1) + return true; + return false; +} + +/* generates a packet of own vis data, + * returns 0 on success, -1 if no packet could be generated */ +static int generate_vis_packet(struct bat_priv *bat_priv) +{ + struct hashtable_t *hash = bat_priv->orig_hash; + struct hlist_node *walk; + struct hlist_head *head; + struct element_t *bucket; + struct orig_node *orig_node; + struct neigh_node *neigh_node; + struct vis_info *info = (struct vis_info *)bat_priv->my_vis_info; + struct vis_packet *packet = (struct vis_packet *)info->skb_packet->data; + struct vis_info_entry *entry; + struct hna_local_entry *hna_local_entry; + int best_tq = -1, i; + + info->first_seen = jiffies; + packet->vis_type = atomic_read(&bat_priv->vis_mode); + + spin_lock_bh(&bat_priv->orig_hash_lock); + memcpy(packet->target_orig, broadcast_addr, ETH_ALEN); + packet->ttl = TTL; + packet->seqno = htonl(ntohl(packet->seqno) + 1); + packet->entries = 0; + skb_trim(info->skb_packet, sizeof(struct vis_packet)); + + if (packet->vis_type == VIS_TYPE_CLIENT_UPDATE) { + best_tq = find_best_vis_server(bat_priv, info); + + if (best_tq < 0) { + spin_unlock_bh(&bat_priv->orig_hash_lock); + return -1; + } + } + + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + hlist_for_each_entry(bucket, walk, head, hlist) { + orig_node = bucket->data; + neigh_node = orig_node->router; + + if (!neigh_node) + continue; + + if (!compare_orig(neigh_node->addr, orig_node->orig)) + continue; + + if (neigh_node->if_incoming->if_status != IF_ACTIVE) + continue; + + if (neigh_node->tq_avg < 1) + continue; + + /* fill one entry into buffer. */ + entry = (struct vis_info_entry *) + skb_put(info->skb_packet, sizeof(*entry)); + memcpy(entry->src, + neigh_node->if_incoming->net_dev->dev_addr, + ETH_ALEN); + memcpy(entry->dest, orig_node->orig, ETH_ALEN); + entry->quality = neigh_node->tq_avg; + packet->entries++; + + if (vis_packet_full(info)) { + spin_unlock_bh(&bat_priv->orig_hash_lock); + return 0; + } + } + } + + spin_unlock_bh(&bat_priv->orig_hash_lock); + + hash = bat_priv->hna_local_hash; + + spin_lock_bh(&bat_priv->hna_lhash_lock); + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + hlist_for_each_entry(bucket, walk, head, hlist) { + hna_local_entry = bucket->data; + entry = (struct vis_info_entry *) + skb_put(info->skb_packet, + sizeof(*entry)); + memset(entry->src, 0, ETH_ALEN); + memcpy(entry->dest, hna_local_entry->addr, ETH_ALEN); + entry->quality = 0; /* 0 means HNA */ + packet->entries++; + + if (vis_packet_full(info)) { + spin_unlock_bh(&bat_priv->hna_lhash_lock); + return 0; + } + } + } + + spin_unlock_bh(&bat_priv->hna_lhash_lock); + return 0; +} + +/* free old vis packets. Must be called with this vis_hash_lock + * held */ +static void purge_vis_packets(struct bat_priv *bat_priv) +{ + int i; + struct hashtable_t *hash = bat_priv->vis_hash; + struct hlist_node *walk, *safe; + struct hlist_head *head; + struct element_t *bucket; + struct vis_info *info; + + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + hlist_for_each_entry_safe(bucket, walk, safe, head, hlist) { + info = bucket->data; + + /* never purge own data. */ + if (info == bat_priv->my_vis_info) + continue; + + if (time_after(jiffies, + info->first_seen + VIS_TIMEOUT * HZ)) { + hlist_del(walk); + kfree(bucket); + send_list_del(info); + kref_put(&info->refcount, free_info); + } + } + } +} + +static void broadcast_vis_packet(struct bat_priv *bat_priv, + struct vis_info *info) +{ + struct hashtable_t *hash = bat_priv->orig_hash; + struct hlist_node *walk; + struct hlist_head *head; + struct element_t *bucket; + struct orig_node *orig_node; + struct vis_packet *packet; + struct sk_buff *skb; + struct batman_if *batman_if; + uint8_t dstaddr[ETH_ALEN]; + int i; + + + spin_lock_bh(&bat_priv->orig_hash_lock); + packet = (struct vis_packet *)info->skb_packet->data; + + /* send to all routers in range. */ + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + hlist_for_each_entry(bucket, walk, head, hlist) { + orig_node = bucket->data; + + /* if it's a vis server and reachable, send it. */ + if ((!orig_node) || (!orig_node->router)) + continue; + if (!(orig_node->flags & VIS_SERVER)) + continue; + /* don't send it if we already received the packet from + * this node. */ + if (recv_list_is_in(bat_priv, &info->recv_list, + orig_node->orig)) + continue; + + memcpy(packet->target_orig, orig_node->orig, ETH_ALEN); + batman_if = orig_node->router->if_incoming; + memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); + spin_unlock_bh(&bat_priv->orig_hash_lock); + + skb = skb_clone(info->skb_packet, GFP_ATOMIC); + if (skb) + send_skb_packet(skb, batman_if, dstaddr); + + spin_lock_bh(&bat_priv->orig_hash_lock); + } + + } + + spin_unlock_bh(&bat_priv->orig_hash_lock); +} + +static void unicast_vis_packet(struct bat_priv *bat_priv, + struct vis_info *info) +{ + struct orig_node *orig_node; + struct sk_buff *skb; + struct vis_packet *packet; + struct batman_if *batman_if; + uint8_t dstaddr[ETH_ALEN]; + + spin_lock_bh(&bat_priv->orig_hash_lock); + packet = (struct vis_packet *)info->skb_packet->data; + orig_node = ((struct orig_node *)hash_find(bat_priv->orig_hash, + compare_orig, choose_orig, + packet->target_orig)); + + if ((!orig_node) || (!orig_node->router)) + goto out; + + /* don't lock while sending the packets ... we therefore + * copy the required data before sending */ + batman_if = orig_node->router->if_incoming; + memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); + spin_unlock_bh(&bat_priv->orig_hash_lock); + + skb = skb_clone(info->skb_packet, GFP_ATOMIC); + if (skb) + send_skb_packet(skb, batman_if, dstaddr); + + return; + +out: + spin_unlock_bh(&bat_priv->orig_hash_lock); +} + +/* only send one vis packet. called from send_vis_packets() */ +static void send_vis_packet(struct bat_priv *bat_priv, struct vis_info *info) +{ + struct vis_packet *packet; + + packet = (struct vis_packet *)info->skb_packet->data; + if (packet->ttl < 2) { + pr_debug("Error - can't send vis packet: ttl exceeded\n"); + return; + } + + memcpy(packet->sender_orig, bat_priv->primary_if->net_dev->dev_addr, + ETH_ALEN); + packet->ttl--; + + if (is_broadcast_ether_addr(packet->target_orig)) + broadcast_vis_packet(bat_priv, info); + else + unicast_vis_packet(bat_priv, info); + packet->ttl++; /* restore TTL */ +} + +/* called from timer; send (and maybe generate) vis packet. */ +static void send_vis_packets(struct work_struct *work) +{ + struct delayed_work *delayed_work = + container_of(work, struct delayed_work, work); + struct bat_priv *bat_priv = + container_of(delayed_work, struct bat_priv, vis_work); + struct vis_info *info, *temp; + + spin_lock_bh(&bat_priv->vis_hash_lock); + purge_vis_packets(bat_priv); + + if (generate_vis_packet(bat_priv) == 0) { + /* schedule if generation was successful */ + send_list_add(bat_priv, bat_priv->my_vis_info); + } + + list_for_each_entry_safe(info, temp, &bat_priv->vis_send_list, + send_list) { + + kref_get(&info->refcount); + spin_unlock_bh(&bat_priv->vis_hash_lock); + + if (bat_priv->primary_if) + send_vis_packet(bat_priv, info); + + spin_lock_bh(&bat_priv->vis_hash_lock); + send_list_del(info); + kref_put(&info->refcount, free_info); + } + spin_unlock_bh(&bat_priv->vis_hash_lock); + start_vis_timer(bat_priv); +} + +/* init the vis server. this may only be called when if_list is already + * initialized (e.g. bat0 is initialized, interfaces have been added) */ +int vis_init(struct bat_priv *bat_priv) +{ + struct vis_packet *packet; + int hash_added; + + if (bat_priv->vis_hash) + return 1; + + spin_lock_bh(&bat_priv->vis_hash_lock); + + bat_priv->vis_hash = hash_new(256); + if (!bat_priv->vis_hash) { + pr_err("Can't initialize vis_hash\n"); + goto err; + } + + bat_priv->my_vis_info = kmalloc(MAX_VIS_PACKET_SIZE, GFP_ATOMIC); + if (!bat_priv->my_vis_info) { + pr_err("Can't initialize vis packet\n"); + goto err; + } + + bat_priv->my_vis_info->skb_packet = dev_alloc_skb( + sizeof(struct vis_packet) + + MAX_VIS_PACKET_SIZE + + sizeof(struct ethhdr)); + if (!bat_priv->my_vis_info->skb_packet) + goto free_info; + + skb_reserve(bat_priv->my_vis_info->skb_packet, sizeof(struct ethhdr)); + packet = (struct vis_packet *)skb_put( + bat_priv->my_vis_info->skb_packet, + sizeof(struct vis_packet)); + + /* prefill the vis info */ + bat_priv->my_vis_info->first_seen = jiffies - + msecs_to_jiffies(VIS_INTERVAL); + INIT_LIST_HEAD(&bat_priv->my_vis_info->recv_list); + INIT_LIST_HEAD(&bat_priv->my_vis_info->send_list); + kref_init(&bat_priv->my_vis_info->refcount); + bat_priv->my_vis_info->bat_priv = bat_priv; + packet->version = COMPAT_VERSION; + packet->packet_type = BAT_VIS; + packet->ttl = TTL; + packet->seqno = 0; + packet->entries = 0; + + INIT_LIST_HEAD(&bat_priv->vis_send_list); + + hash_added = hash_add(bat_priv->vis_hash, vis_info_cmp, vis_info_choose, + bat_priv->my_vis_info); + if (hash_added < 0) { + pr_err("Can't add own vis packet into hash\n"); + /* not in hash, need to remove it manually. */ + kref_put(&bat_priv->my_vis_info->refcount, free_info); + goto err; + } + + spin_unlock_bh(&bat_priv->vis_hash_lock); + start_vis_timer(bat_priv); + return 1; + +free_info: + kfree(bat_priv->my_vis_info); + bat_priv->my_vis_info = NULL; +err: + spin_unlock_bh(&bat_priv->vis_hash_lock); + vis_quit(bat_priv); + return 0; +} + +/* Decrease the reference count on a hash item info */ +static void free_info_ref(void *data, void *arg) +{ + struct vis_info *info = data; + + send_list_del(info); + kref_put(&info->refcount, free_info); +} + +/* shutdown vis-server */ +void vis_quit(struct bat_priv *bat_priv) +{ + if (!bat_priv->vis_hash) + return; + + cancel_delayed_work_sync(&bat_priv->vis_work); + + spin_lock_bh(&bat_priv->vis_hash_lock); + /* properly remove, kill timers ... */ + hash_delete(bat_priv->vis_hash, free_info_ref, NULL); + bat_priv->vis_hash = NULL; + bat_priv->my_vis_info = NULL; + spin_unlock_bh(&bat_priv->vis_hash_lock); +} + +/* schedule packets for (re)transmission */ +static void start_vis_timer(struct bat_priv *bat_priv) +{ + INIT_DELAYED_WORK(&bat_priv->vis_work, send_vis_packets); + queue_delayed_work(bat_event_workqueue, &bat_priv->vis_work, + msecs_to_jiffies(VIS_INTERVAL)); +} diff --git a/net/batman-adv/vis.h b/net/batman-adv/vis.h new file mode 100644 index 000000000000..2c3b33089a9b --- /dev/null +++ b/net/batman-adv/vis.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2008-2010 B.A.T.M.A.N. contributors: + * + * Simon Wunderlich, Marek Lindner + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#ifndef _NET_BATMAN_ADV_VIS_H_ +#define _NET_BATMAN_ADV_VIS_H_ + +#define VIS_TIMEOUT 200 /* timeout of vis packets in seconds */ + +int vis_seq_print_text(struct seq_file *seq, void *offset); +void receive_server_sync_packet(struct bat_priv *bat_priv, + struct vis_packet *vis_packet, + int vis_info_len); +void receive_client_update_packet(struct bat_priv *bat_priv, + struct vis_packet *vis_packet, + int vis_info_len); +int vis_init(struct bat_priv *bat_priv); +void vis_quit(struct bat_priv *bat_priv); + +#endif /* _NET_BATMAN_ADV_VIS_H_ */ -- cgit v1.2.3 From e72df0b847adf064e64bcbd5141f0031524e723e Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 10 Dec 2010 17:02:49 +0100 Subject: MAINTAINERS: Add percpu allocator entry Add me and Christoph Lameter as maintainers for the percpu memory allocator. Signed-off-by: Tejun Heo Cc: Christoph Lameter Acked-by: Christoph Lameter --- MAINTAINERS | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 1a1c27b9c557..abcc9cae0100 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4602,6 +4602,16 @@ S: Maintained F: crypto/pcrypt.c F: include/crypto/pcrypt.h +PER-CPU MEMORY ALLOCATOR +M: Tejun Heo +M: Christoph Lameter +L: linux-kernel@vger.kernel.org +T: git git://git.kernel.org/pub/scm/linux/kernel/git/tj/percpu.git +S: Maintained +F: include/linux/percpu*.h +F: mm/percpu*.c +F: arch/*/include/asm/percpu.h + PER-TASK DELAY ACCOUNTING M: Balbir Singh S: Maintained -- cgit v1.2.3 From f0b3e4b7307f02a203029441b0473a84aebc6435 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Fri, 17 Dec 2010 16:04:11 -0600 Subject: MAINTAINERS: Fix typo in rtl8192ce entry Signed-off-by: Larry Finger Signed-off-by: John W. Linville --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 43ab5fa42a73..94c129f7240c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5052,7 +5052,7 @@ W: http://linuxwireless.org/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git S: Maintained F: drivers/net/wireless/rtlwifi/ -F: drivers/net/wireless/rtl8192ce/ +F: drivers/net/wireless/rtlwifi/rtl8192ce/ S3 SAVAGE FRAMEBUFFER DRIVER M: Antonino Daplas -- cgit v1.2.3 From 227fb925d3d3c5e565c1f1323a530564509195f2 Mon Sep 17 00:00:00 2001 From: Don Fry Date: Tue, 21 Dec 2010 19:58:15 -0800 Subject: MAINTAINERS: email address change My ISP has changed and therefore my email address. Signed-off-by: Don Fry Signed-off-by: David S. Miller --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index dec390dff141..b0b118a546ae 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4586,7 +4586,7 @@ F: drivers/pcmcia/ F: include/pcmcia/ PCNET32 NETWORK DRIVER -M: Don Fry +M: Don Fry L: netdev@vger.kernel.org S: Maintained F: drivers/net/pcnet32.c -- cgit v1.2.3 From 3cfeb0c33f5cbcc6dde371392877ef3101b8f805 Mon Sep 17 00:00:00 2001 From: "John W. Linville" Date: Mon, 20 Dec 2010 15:16:53 -0500 Subject: rtl818x: move rtl8180 and rtl8187 to separate subdirectories These drivers share one header file, but nothing else. Worse, both drivers use the rtl8225 part with different register settings. The results has been some ugly naming -- let's simplify that. Signed-off-by: John W. Linville Acked-by: Hin-Tak Leung Acked-by: Larry Finger Acked-by: Herton Ronaldo Krzesinski --- MAINTAINERS | 4 +- drivers/net/wireless/rtl818x/Makefile | 9 +- drivers/net/wireless/rtl818x/rtl8180.h | 130 -- drivers/net/wireless/rtl818x/rtl8180/Makefile | 5 + drivers/net/wireless/rtl818x/rtl8180/dev.c | 1188 ++++++++++++++++++ drivers/net/wireless/rtl818x/rtl8180/grf5101.c | 190 +++ drivers/net/wireless/rtl818x/rtl8180/grf5101.h | 28 + drivers/net/wireless/rtl818x/rtl8180/max2820.c | 169 +++ drivers/net/wireless/rtl818x/rtl8180/max2820.h | 28 + drivers/net/wireless/rtl818x/rtl8180/rtl8180.h | 130 ++ drivers/net/wireless/rtl818x/rtl8180/rtl8225.c | 791 ++++++++++++ drivers/net/wireless/rtl818x/rtl8180/rtl8225.h | 23 + drivers/net/wireless/rtl818x/rtl8180/sa2400.c | 228 ++++ drivers/net/wireless/rtl818x/rtl8180/sa2400.h | 36 + drivers/net/wireless/rtl818x/rtl8180_dev.c | 1188 ------------------ drivers/net/wireless/rtl818x/rtl8180_grf5101.c | 190 --- drivers/net/wireless/rtl818x/rtl8180_grf5101.h | 28 - drivers/net/wireless/rtl818x/rtl8180_max2820.c | 169 --- drivers/net/wireless/rtl818x/rtl8180_max2820.h | 28 - drivers/net/wireless/rtl818x/rtl8180_rtl8225.c | 791 ------------ drivers/net/wireless/rtl818x/rtl8180_rtl8225.h | 23 - drivers/net/wireless/rtl818x/rtl8180_sa2400.c | 228 ---- drivers/net/wireless/rtl818x/rtl8180_sa2400.h | 36 - drivers/net/wireless/rtl818x/rtl8187.h | 271 ---- drivers/net/wireless/rtl818x/rtl8187/Makefile | 5 + drivers/net/wireless/rtl818x/rtl8187/dev.c | 1591 ++++++++++++++++++++++++ drivers/net/wireless/rtl818x/rtl8187/leds.c | 245 ++++ drivers/net/wireless/rtl818x/rtl8187/leds.h | 59 + drivers/net/wireless/rtl818x/rtl8187/rfkill.c | 64 + drivers/net/wireless/rtl818x/rtl8187/rfkill.h | 8 + drivers/net/wireless/rtl818x/rtl8187/rtl8187.h | 271 ++++ drivers/net/wireless/rtl818x/rtl8187/rtl8225.c | 961 ++++++++++++++ drivers/net/wireless/rtl818x/rtl8187/rtl8225.h | 44 + drivers/net/wireless/rtl818x/rtl8187_dev.c | 1591 ------------------------ drivers/net/wireless/rtl818x/rtl8187_leds.c | 245 ---- drivers/net/wireless/rtl818x/rtl8187_leds.h | 59 - drivers/net/wireless/rtl818x/rtl8187_rfkill.c | 64 - drivers/net/wireless/rtl818x/rtl8187_rfkill.h | 8 - drivers/net/wireless/rtl818x/rtl8187_rtl8225.c | 961 -------------- drivers/net/wireless/rtl818x/rtl8187_rtl8225.h | 44 - 40 files changed, 6068 insertions(+), 6063 deletions(-) delete mode 100644 drivers/net/wireless/rtl818x/rtl8180.h create mode 100644 drivers/net/wireless/rtl818x/rtl8180/Makefile create mode 100644 drivers/net/wireless/rtl818x/rtl8180/dev.c create mode 100644 drivers/net/wireless/rtl818x/rtl8180/grf5101.c create mode 100644 drivers/net/wireless/rtl818x/rtl8180/grf5101.h create mode 100644 drivers/net/wireless/rtl818x/rtl8180/max2820.c create mode 100644 drivers/net/wireless/rtl818x/rtl8180/max2820.h create mode 100644 drivers/net/wireless/rtl818x/rtl8180/rtl8180.h create mode 100644 drivers/net/wireless/rtl818x/rtl8180/rtl8225.c create mode 100644 drivers/net/wireless/rtl818x/rtl8180/rtl8225.h create mode 100644 drivers/net/wireless/rtl818x/rtl8180/sa2400.c create mode 100644 drivers/net/wireless/rtl818x/rtl8180/sa2400.h delete mode 100644 drivers/net/wireless/rtl818x/rtl8180_dev.c delete mode 100644 drivers/net/wireless/rtl818x/rtl8180_grf5101.c delete mode 100644 drivers/net/wireless/rtl818x/rtl8180_grf5101.h delete mode 100644 drivers/net/wireless/rtl818x/rtl8180_max2820.c delete mode 100644 drivers/net/wireless/rtl818x/rtl8180_max2820.h delete mode 100644 drivers/net/wireless/rtl818x/rtl8180_rtl8225.c delete mode 100644 drivers/net/wireless/rtl818x/rtl8180_rtl8225.h delete mode 100644 drivers/net/wireless/rtl818x/rtl8180_sa2400.c delete mode 100644 drivers/net/wireless/rtl818x/rtl8180_sa2400.h delete mode 100644 drivers/net/wireless/rtl818x/rtl8187.h create mode 100644 drivers/net/wireless/rtl818x/rtl8187/Makefile create mode 100644 drivers/net/wireless/rtl818x/rtl8187/dev.c create mode 100644 drivers/net/wireless/rtl818x/rtl8187/leds.c create mode 100644 drivers/net/wireless/rtl818x/rtl8187/leds.h create mode 100644 drivers/net/wireless/rtl818x/rtl8187/rfkill.c create mode 100644 drivers/net/wireless/rtl818x/rtl8187/rfkill.h create mode 100644 drivers/net/wireless/rtl818x/rtl8187/rtl8187.h create mode 100644 drivers/net/wireless/rtl818x/rtl8187/rtl8225.c create mode 100644 drivers/net/wireless/rtl818x/rtl8187/rtl8225.h delete mode 100644 drivers/net/wireless/rtl818x/rtl8187_dev.c delete mode 100644 drivers/net/wireless/rtl818x/rtl8187_leds.c delete mode 100644 drivers/net/wireless/rtl818x/rtl8187_leds.h delete mode 100644 drivers/net/wireless/rtl818x/rtl8187_rfkill.c delete mode 100644 drivers/net/wireless/rtl818x/rtl8187_rfkill.h delete mode 100644 drivers/net/wireless/rtl818x/rtl8187_rtl8225.c delete mode 100644 drivers/net/wireless/rtl818x/rtl8187_rtl8225.h (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 94c129f7240c..ff19c305d1c5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5032,7 +5032,7 @@ L: linux-wireless@vger.kernel.org W: http://linuxwireless.org/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git S: Maintained -F: drivers/net/wireless/rtl818x/rtl8180* +F: drivers/net/wireless/rtl818x/rtl8180/ RTL8187 WIRELESS DRIVER M: Herton Ronaldo Krzesinski @@ -5042,7 +5042,7 @@ L: linux-wireless@vger.kernel.org W: http://linuxwireless.org/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git S: Maintained -F: drivers/net/wireless/rtl818x/rtl8187* +F: drivers/net/wireless/rtl818x/rtl8187/ RTL8192CE WIRELESS DRIVER M: Larry Finger diff --git a/drivers/net/wireless/rtl818x/Makefile b/drivers/net/wireless/rtl818x/Makefile index 93cbfbedb46d..997569076923 100644 --- a/drivers/net/wireless/rtl818x/Makefile +++ b/drivers/net/wireless/rtl818x/Makefile @@ -1,7 +1,2 @@ -rtl8180-objs := rtl8180_dev.o rtl8180_rtl8225.o rtl8180_sa2400.o rtl8180_max2820.o rtl8180_grf5101.o -rtl8187-objs := rtl8187_dev.o rtl8187_rtl8225.o rtl8187_leds.o rtl8187_rfkill.o - -obj-$(CONFIG_RTL8180) += rtl8180.o -obj-$(CONFIG_RTL8187) += rtl8187.o - - +obj-$(CONFIG_RTL8180) += rtl8180/ +obj-$(CONFIG_RTL8187) += rtl8187/ diff --git a/drivers/net/wireless/rtl818x/rtl8180.h b/drivers/net/wireless/rtl818x/rtl8180.h deleted file mode 100644 index 30523314da43..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8180.h +++ /dev/null @@ -1,130 +0,0 @@ -#ifndef RTL8180_H -#define RTL8180_H - -#include "rtl818x.h" - -#define MAX_RX_SIZE IEEE80211_MAX_RTS_THRESHOLD - -#define RF_PARAM_ANALOGPHY (1 << 0) -#define RF_PARAM_ANTBDEFAULT (1 << 1) -#define RF_PARAM_CARRIERSENSE1 (1 << 2) -#define RF_PARAM_CARRIERSENSE2 (1 << 3) - -#define BB_ANTATTEN_CHAN14 0x0C -#define BB_ANTENNA_B 0x40 - -#define BB_HOST_BANG (1 << 30) -#define BB_HOST_BANG_EN (1 << 2) -#define BB_HOST_BANG_CLK (1 << 1) -#define BB_HOST_BANG_DATA 1 - -#define ANAPARAM_TXDACOFF_SHIFT 27 -#define ANAPARAM_PWR0_SHIFT 28 -#define ANAPARAM_PWR0_MASK (0x07 << ANAPARAM_PWR0_SHIFT) -#define ANAPARAM_PWR1_SHIFT 20 -#define ANAPARAM_PWR1_MASK (0x7F << ANAPARAM_PWR1_SHIFT) - -struct rtl8180_tx_desc { - __le32 flags; - __le16 rts_duration; - __le16 plcp_len; - __le32 tx_buf; - __le32 frame_len; - __le32 next_tx_desc; - u8 cw; - u8 retry_limit; - u8 agc; - u8 flags2; - u32 reserved[2]; -} __packed; - -struct rtl8180_rx_desc { - __le32 flags; - __le32 flags2; - union { - __le32 rx_buf; - __le64 tsft; - }; -} __packed; - -struct rtl8180_tx_ring { - struct rtl8180_tx_desc *desc; - dma_addr_t dma; - unsigned int idx; - unsigned int entries; - struct sk_buff_head queue; -}; - -struct rtl8180_vif { - struct ieee80211_hw *dev; - - /* beaconing */ - struct delayed_work beacon_work; - bool enable_beacon; -}; - -struct rtl8180_priv { - /* common between rtl818x drivers */ - struct rtl818x_csr __iomem *map; - const struct rtl818x_rf_ops *rf; - struct ieee80211_vif *vif; - - /* rtl8180 driver specific */ - spinlock_t lock; - struct rtl8180_rx_desc *rx_ring; - dma_addr_t rx_ring_dma; - unsigned int rx_idx; - struct sk_buff *rx_buf[32]; - struct rtl8180_tx_ring tx_ring[4]; - struct ieee80211_channel channels[14]; - struct ieee80211_rate rates[12]; - struct ieee80211_supported_band band; - struct pci_dev *pdev; - u32 rx_conf; - - int r8185; - u32 anaparam; - u16 rfparam; - u8 csthreshold; - - /* sequence # */ - u16 seqno; -}; - -void rtl8180_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data); -void rtl8180_set_anaparam(struct rtl8180_priv *priv, u32 anaparam); - -static inline u8 rtl818x_ioread8(struct rtl8180_priv *priv, u8 __iomem *addr) -{ - return ioread8(addr); -} - -static inline u16 rtl818x_ioread16(struct rtl8180_priv *priv, __le16 __iomem *addr) -{ - return ioread16(addr); -} - -static inline u32 rtl818x_ioread32(struct rtl8180_priv *priv, __le32 __iomem *addr) -{ - return ioread32(addr); -} - -static inline void rtl818x_iowrite8(struct rtl8180_priv *priv, - u8 __iomem *addr, u8 val) -{ - iowrite8(val, addr); -} - -static inline void rtl818x_iowrite16(struct rtl8180_priv *priv, - __le16 __iomem *addr, u16 val) -{ - iowrite16(val, addr); -} - -static inline void rtl818x_iowrite32(struct rtl8180_priv *priv, - __le32 __iomem *addr, u32 val) -{ - iowrite32(val, addr); -} - -#endif /* RTL8180_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8180/Makefile b/drivers/net/wireless/rtl818x/rtl8180/Makefile new file mode 100644 index 000000000000..cb4fb8596f0b --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8180/Makefile @@ -0,0 +1,5 @@ +rtl8180-objs := dev.o rtl8225.o sa2400.o max2820.o grf5101.o + +obj-$(CONFIG_RTL8180) += rtl8180.o + +ccflags-y += -Idrivers/net/wireless/rtl818x diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c new file mode 100644 index 000000000000..5851cbc1e957 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c @@ -0,0 +1,1188 @@ + +/* + * Linux device driver for RTL8180 / RTL8185 + * + * Copyright 2007 Michael Wu + * Copyright 2007 Andrea Merello + * + * Based on the r8180 driver, which is: + * Copyright 2004-2005 Andrea Merello , et al. + * + * Thanks to Realtek for their support! + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "rtl8180.h" +#include "rtl8225.h" +#include "sa2400.h" +#include "max2820.h" +#include "grf5101.h" + +MODULE_AUTHOR("Michael Wu "); +MODULE_AUTHOR("Andrea Merello "); +MODULE_DESCRIPTION("RTL8180 / RTL8185 PCI wireless driver"); +MODULE_LICENSE("GPL"); + +static DEFINE_PCI_DEVICE_TABLE(rtl8180_table) = { + /* rtl8185 */ + { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8185) }, + { PCI_DEVICE(PCI_VENDOR_ID_BELKIN, 0x700f) }, + { PCI_DEVICE(PCI_VENDOR_ID_BELKIN, 0x701f) }, + + /* rtl8180 */ + { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8180) }, + { PCI_DEVICE(0x1799, 0x6001) }, + { PCI_DEVICE(0x1799, 0x6020) }, + { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x3300) }, + { } +}; + +MODULE_DEVICE_TABLE(pci, rtl8180_table); + +static const struct ieee80211_rate rtl818x_rates[] = { + { .bitrate = 10, .hw_value = 0, }, + { .bitrate = 20, .hw_value = 1, }, + { .bitrate = 55, .hw_value = 2, }, + { .bitrate = 110, .hw_value = 3, }, + { .bitrate = 60, .hw_value = 4, }, + { .bitrate = 90, .hw_value = 5, }, + { .bitrate = 120, .hw_value = 6, }, + { .bitrate = 180, .hw_value = 7, }, + { .bitrate = 240, .hw_value = 8, }, + { .bitrate = 360, .hw_value = 9, }, + { .bitrate = 480, .hw_value = 10, }, + { .bitrate = 540, .hw_value = 11, }, +}; + +static const struct ieee80211_channel rtl818x_channels[] = { + { .center_freq = 2412 }, + { .center_freq = 2417 }, + { .center_freq = 2422 }, + { .center_freq = 2427 }, + { .center_freq = 2432 }, + { .center_freq = 2437 }, + { .center_freq = 2442 }, + { .center_freq = 2447 }, + { .center_freq = 2452 }, + { .center_freq = 2457 }, + { .center_freq = 2462 }, + { .center_freq = 2467 }, + { .center_freq = 2472 }, + { .center_freq = 2484 }, +}; + + +void rtl8180_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data) +{ + struct rtl8180_priv *priv = dev->priv; + int i = 10; + u32 buf; + + buf = (data << 8) | addr; + + rtl818x_iowrite32(priv, (__le32 __iomem *)&priv->map->PHY[0], buf | 0x80); + while (i--) { + rtl818x_iowrite32(priv, (__le32 __iomem *)&priv->map->PHY[0], buf); + if (rtl818x_ioread8(priv, &priv->map->PHY[2]) == (data & 0xFF)) + return; + } +} + +static void rtl8180_handle_rx(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + unsigned int count = 32; + u8 signal, agc, sq; + + while (count--) { + struct rtl8180_rx_desc *entry = &priv->rx_ring[priv->rx_idx]; + struct sk_buff *skb = priv->rx_buf[priv->rx_idx]; + u32 flags = le32_to_cpu(entry->flags); + + if (flags & RTL818X_RX_DESC_FLAG_OWN) + return; + + if (unlikely(flags & (RTL818X_RX_DESC_FLAG_DMA_FAIL | + RTL818X_RX_DESC_FLAG_FOF | + RTL818X_RX_DESC_FLAG_RX_ERR))) + goto done; + else { + u32 flags2 = le32_to_cpu(entry->flags2); + struct ieee80211_rx_status rx_status = {0}; + struct sk_buff *new_skb = dev_alloc_skb(MAX_RX_SIZE); + + if (unlikely(!new_skb)) + goto done; + + pci_unmap_single(priv->pdev, + *((dma_addr_t *)skb->cb), + MAX_RX_SIZE, PCI_DMA_FROMDEVICE); + skb_put(skb, flags & 0xFFF); + + rx_status.antenna = (flags2 >> 15) & 1; + rx_status.rate_idx = (flags >> 20) & 0xF; + agc = (flags2 >> 17) & 0x7F; + if (priv->r8185) { + if (rx_status.rate_idx > 3) + signal = 90 - clamp_t(u8, agc, 25, 90); + else + signal = 95 - clamp_t(u8, agc, 30, 95); + } else { + sq = flags2 & 0xff; + signal = priv->rf->calc_rssi(agc, sq); + } + rx_status.signal = signal; + rx_status.freq = dev->conf.channel->center_freq; + rx_status.band = dev->conf.channel->band; + rx_status.mactime = le64_to_cpu(entry->tsft); + rx_status.flag |= RX_FLAG_TSFT; + if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR) + rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; + + memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); + ieee80211_rx_irqsafe(dev, skb); + + skb = new_skb; + priv->rx_buf[priv->rx_idx] = skb; + *((dma_addr_t *) skb->cb) = + pci_map_single(priv->pdev, skb_tail_pointer(skb), + MAX_RX_SIZE, PCI_DMA_FROMDEVICE); + } + + done: + entry->rx_buf = cpu_to_le32(*((dma_addr_t *)skb->cb)); + entry->flags = cpu_to_le32(RTL818X_RX_DESC_FLAG_OWN | + MAX_RX_SIZE); + if (priv->rx_idx == 31) + entry->flags |= cpu_to_le32(RTL818X_RX_DESC_FLAG_EOR); + priv->rx_idx = (priv->rx_idx + 1) % 32; + } +} + +static void rtl8180_handle_tx(struct ieee80211_hw *dev, unsigned int prio) +{ + struct rtl8180_priv *priv = dev->priv; + struct rtl8180_tx_ring *ring = &priv->tx_ring[prio]; + + while (skb_queue_len(&ring->queue)) { + struct rtl8180_tx_desc *entry = &ring->desc[ring->idx]; + struct sk_buff *skb; + struct ieee80211_tx_info *info; + u32 flags = le32_to_cpu(entry->flags); + + if (flags & RTL818X_TX_DESC_FLAG_OWN) + return; + + ring->idx = (ring->idx + 1) % ring->entries; + skb = __skb_dequeue(&ring->queue); + pci_unmap_single(priv->pdev, le32_to_cpu(entry->tx_buf), + skb->len, PCI_DMA_TODEVICE); + + info = IEEE80211_SKB_CB(skb); + ieee80211_tx_info_clear_status(info); + + if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && + (flags & RTL818X_TX_DESC_FLAG_TX_OK)) + info->flags |= IEEE80211_TX_STAT_ACK; + + info->status.rates[0].count = (flags & 0xFF) + 1; + info->status.rates[1].idx = -1; + + ieee80211_tx_status_irqsafe(dev, skb); + if (ring->entries - skb_queue_len(&ring->queue) == 2) + ieee80211_wake_queue(dev, prio); + } +} + +static irqreturn_t rtl8180_interrupt(int irq, void *dev_id) +{ + struct ieee80211_hw *dev = dev_id; + struct rtl8180_priv *priv = dev->priv; + u16 reg; + + spin_lock(&priv->lock); + reg = rtl818x_ioread16(priv, &priv->map->INT_STATUS); + if (unlikely(reg == 0xFFFF)) { + spin_unlock(&priv->lock); + return IRQ_HANDLED; + } + + rtl818x_iowrite16(priv, &priv->map->INT_STATUS, reg); + + if (reg & (RTL818X_INT_TXB_OK | RTL818X_INT_TXB_ERR)) + rtl8180_handle_tx(dev, 3); + + if (reg & (RTL818X_INT_TXH_OK | RTL818X_INT_TXH_ERR)) + rtl8180_handle_tx(dev, 2); + + if (reg & (RTL818X_INT_TXN_OK | RTL818X_INT_TXN_ERR)) + rtl8180_handle_tx(dev, 1); + + if (reg & (RTL818X_INT_TXL_OK | RTL818X_INT_TXL_ERR)) + rtl8180_handle_tx(dev, 0); + + if (reg & (RTL818X_INT_RX_OK | RTL818X_INT_RX_ERR)) + rtl8180_handle_rx(dev); + + spin_unlock(&priv->lock); + + return IRQ_HANDLED; +} + +static int rtl8180_tx(struct ieee80211_hw *dev, struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct rtl8180_priv *priv = dev->priv; + struct rtl8180_tx_ring *ring; + struct rtl8180_tx_desc *entry; + unsigned long flags; + unsigned int idx, prio; + dma_addr_t mapping; + u32 tx_flags; + u8 rc_flags; + u16 plcp_len = 0; + __le16 rts_duration = 0; + + prio = skb_get_queue_mapping(skb); + ring = &priv->tx_ring[prio]; + + mapping = pci_map_single(priv->pdev, skb->data, + skb->len, PCI_DMA_TODEVICE); + + tx_flags = RTL818X_TX_DESC_FLAG_OWN | RTL818X_TX_DESC_FLAG_FS | + RTL818X_TX_DESC_FLAG_LS | + (ieee80211_get_tx_rate(dev, info)->hw_value << 24) | + skb->len; + + if (priv->r8185) + tx_flags |= RTL818X_TX_DESC_FLAG_DMA | + RTL818X_TX_DESC_FLAG_NO_ENC; + + rc_flags = info->control.rates[0].flags; + if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) { + tx_flags |= RTL818X_TX_DESC_FLAG_RTS; + tx_flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; + } else if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { + tx_flags |= RTL818X_TX_DESC_FLAG_CTS; + tx_flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; + } + + if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) + rts_duration = ieee80211_rts_duration(dev, priv->vif, skb->len, + info); + + if (!priv->r8185) { + unsigned int remainder; + + plcp_len = DIV_ROUND_UP(16 * (skb->len + 4), + (ieee80211_get_tx_rate(dev, info)->bitrate * 2) / 10); + remainder = (16 * (skb->len + 4)) % + ((ieee80211_get_tx_rate(dev, info)->bitrate * 2) / 10); + if (remainder <= 6) + plcp_len |= 1 << 15; + } + + spin_lock_irqsave(&priv->lock, flags); + + if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { + if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) + priv->seqno += 0x10; + hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); + hdr->seq_ctrl |= cpu_to_le16(priv->seqno); + } + + idx = (ring->idx + skb_queue_len(&ring->queue)) % ring->entries; + entry = &ring->desc[idx]; + + entry->rts_duration = rts_duration; + entry->plcp_len = cpu_to_le16(plcp_len); + entry->tx_buf = cpu_to_le32(mapping); + entry->frame_len = cpu_to_le32(skb->len); + entry->flags2 = info->control.rates[1].idx >= 0 ? + ieee80211_get_alt_retry_rate(dev, info, 0)->bitrate << 4 : 0; + entry->retry_limit = info->control.rates[0].count; + entry->flags = cpu_to_le32(tx_flags); + __skb_queue_tail(&ring->queue, skb); + if (ring->entries - skb_queue_len(&ring->queue) < 2) + ieee80211_stop_queue(dev, prio); + + spin_unlock_irqrestore(&priv->lock, flags); + + rtl818x_iowrite8(priv, &priv->map->TX_DMA_POLLING, (1 << (prio + 4))); + + return 0; +} + +void rtl8180_set_anaparam(struct rtl8180_priv *priv, u32 anaparam) +{ + u8 reg; + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, + reg | RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite32(priv, &priv->map->ANAPARAM, anaparam); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, + reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); +} + +static int rtl8180_init_hw(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + u16 reg; + + rtl818x_iowrite8(priv, &priv->map->CMD, 0); + rtl818x_ioread8(priv, &priv->map->CMD); + msleep(10); + + /* reset */ + rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0); + rtl818x_ioread8(priv, &priv->map->CMD); + + reg = rtl818x_ioread8(priv, &priv->map->CMD); + reg &= (1 << 1); + reg |= RTL818X_CMD_RESET; + rtl818x_iowrite8(priv, &priv->map->CMD, RTL818X_CMD_RESET); + rtl818x_ioread8(priv, &priv->map->CMD); + msleep(200); + + /* check success of reset */ + if (rtl818x_ioread8(priv, &priv->map->CMD) & RTL818X_CMD_RESET) { + wiphy_err(dev->wiphy, "reset timeout!\n"); + return -ETIMEDOUT; + } + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_LOAD); + rtl818x_ioread8(priv, &priv->map->CMD); + msleep(200); + + if (rtl818x_ioread8(priv, &priv->map->CONFIG3) & (1 << 3)) { + /* For cardbus */ + reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); + reg |= 1 << 1; + rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg); + reg = rtl818x_ioread16(priv, &priv->map->FEMR); + reg |= (1 << 15) | (1 << 14) | (1 << 4); + rtl818x_iowrite16(priv, &priv->map->FEMR, reg); + } + + rtl818x_iowrite8(priv, &priv->map->MSR, 0); + + if (!priv->r8185) + rtl8180_set_anaparam(priv, priv->anaparam); + + rtl818x_iowrite32(priv, &priv->map->RDSAR, priv->rx_ring_dma); + rtl818x_iowrite32(priv, &priv->map->TBDA, priv->tx_ring[3].dma); + rtl818x_iowrite32(priv, &priv->map->THPDA, priv->tx_ring[2].dma); + rtl818x_iowrite32(priv, &priv->map->TNPDA, priv->tx_ring[1].dma); + rtl818x_iowrite32(priv, &priv->map->TLPDA, priv->tx_ring[0].dma); + + /* TODO: necessary? specs indicate not */ + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG2); + rtl818x_iowrite8(priv, &priv->map->CONFIG2, reg & ~(1 << 3)); + if (priv->r8185) { + reg = rtl818x_ioread8(priv, &priv->map->CONFIG2); + rtl818x_iowrite8(priv, &priv->map->CONFIG2, reg | (1 << 4)); + } + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + + /* TODO: set CONFIG5 for calibrating AGC on rtl8180 + philips radio? */ + + /* TODO: turn off hw wep on rtl8180 */ + + rtl818x_iowrite32(priv, &priv->map->INT_TIMEOUT, 0); + + if (priv->r8185) { + rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0); + rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, 0x81); + rtl818x_iowrite8(priv, &priv->map->RESP_RATE, (8 << 4) | 0); + + rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3); + + /* TODO: set ClkRun enable? necessary? */ + reg = rtl818x_ioread8(priv, &priv->map->GP_ENABLE); + rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, reg & ~(1 << 6)); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | (1 << 2)); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + } else { + rtl818x_iowrite16(priv, &priv->map->BRSR, 0x1); + rtl818x_iowrite8(priv, &priv->map->SECURITY, 0); + + rtl818x_iowrite8(priv, &priv->map->PHY_DELAY, 0x6); + rtl818x_iowrite8(priv, &priv->map->CARRIER_SENSE_COUNTER, 0x4C); + } + + priv->rf->init(dev); + if (priv->r8185) + rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3); + return 0; +} + +static int rtl8180_init_rx_ring(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + struct rtl8180_rx_desc *entry; + int i; + + priv->rx_ring = pci_alloc_consistent(priv->pdev, + sizeof(*priv->rx_ring) * 32, + &priv->rx_ring_dma); + + if (!priv->rx_ring || (unsigned long)priv->rx_ring & 0xFF) { + wiphy_err(dev->wiphy, "Cannot allocate RX ring\n"); + return -ENOMEM; + } + + memset(priv->rx_ring, 0, sizeof(*priv->rx_ring) * 32); + priv->rx_idx = 0; + + for (i = 0; i < 32; i++) { + struct sk_buff *skb = dev_alloc_skb(MAX_RX_SIZE); + dma_addr_t *mapping; + entry = &priv->rx_ring[i]; + if (!skb) + return 0; + + priv->rx_buf[i] = skb; + mapping = (dma_addr_t *)skb->cb; + *mapping = pci_map_single(priv->pdev, skb_tail_pointer(skb), + MAX_RX_SIZE, PCI_DMA_FROMDEVICE); + entry->rx_buf = cpu_to_le32(*mapping); + entry->flags = cpu_to_le32(RTL818X_RX_DESC_FLAG_OWN | + MAX_RX_SIZE); + } + entry->flags |= cpu_to_le32(RTL818X_RX_DESC_FLAG_EOR); + return 0; +} + +static void rtl8180_free_rx_ring(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + int i; + + for (i = 0; i < 32; i++) { + struct sk_buff *skb = priv->rx_buf[i]; + if (!skb) + continue; + + pci_unmap_single(priv->pdev, + *((dma_addr_t *)skb->cb), + MAX_RX_SIZE, PCI_DMA_FROMDEVICE); + kfree_skb(skb); + } + + pci_free_consistent(priv->pdev, sizeof(*priv->rx_ring) * 32, + priv->rx_ring, priv->rx_ring_dma); + priv->rx_ring = NULL; +} + +static int rtl8180_init_tx_ring(struct ieee80211_hw *dev, + unsigned int prio, unsigned int entries) +{ + struct rtl8180_priv *priv = dev->priv; + struct rtl8180_tx_desc *ring; + dma_addr_t dma; + int i; + + ring = pci_alloc_consistent(priv->pdev, sizeof(*ring) * entries, &dma); + if (!ring || (unsigned long)ring & 0xFF) { + wiphy_err(dev->wiphy, "Cannot allocate TX ring (prio = %d)\n", + prio); + return -ENOMEM; + } + + memset(ring, 0, sizeof(*ring)*entries); + priv->tx_ring[prio].desc = ring; + priv->tx_ring[prio].dma = dma; + priv->tx_ring[prio].idx = 0; + priv->tx_ring[prio].entries = entries; + skb_queue_head_init(&priv->tx_ring[prio].queue); + + for (i = 0; i < entries; i++) + ring[i].next_tx_desc = + cpu_to_le32((u32)dma + ((i + 1) % entries) * sizeof(*ring)); + + return 0; +} + +static void rtl8180_free_tx_ring(struct ieee80211_hw *dev, unsigned int prio) +{ + struct rtl8180_priv *priv = dev->priv; + struct rtl8180_tx_ring *ring = &priv->tx_ring[prio]; + + while (skb_queue_len(&ring->queue)) { + struct rtl8180_tx_desc *entry = &ring->desc[ring->idx]; + struct sk_buff *skb = __skb_dequeue(&ring->queue); + + pci_unmap_single(priv->pdev, le32_to_cpu(entry->tx_buf), + skb->len, PCI_DMA_TODEVICE); + kfree_skb(skb); + ring->idx = (ring->idx + 1) % ring->entries; + } + + pci_free_consistent(priv->pdev, sizeof(*ring->desc)*ring->entries, + ring->desc, ring->dma); + ring->desc = NULL; +} + +static int rtl8180_start(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + int ret, i; + u32 reg; + + ret = rtl8180_init_rx_ring(dev); + if (ret) + return ret; + + for (i = 0; i < 4; i++) + if ((ret = rtl8180_init_tx_ring(dev, i, 16))) + goto err_free_rings; + + ret = rtl8180_init_hw(dev); + if (ret) + goto err_free_rings; + + rtl818x_iowrite32(priv, &priv->map->RDSAR, priv->rx_ring_dma); + rtl818x_iowrite32(priv, &priv->map->TBDA, priv->tx_ring[3].dma); + rtl818x_iowrite32(priv, &priv->map->THPDA, priv->tx_ring[2].dma); + rtl818x_iowrite32(priv, &priv->map->TNPDA, priv->tx_ring[1].dma); + rtl818x_iowrite32(priv, &priv->map->TLPDA, priv->tx_ring[0].dma); + + ret = request_irq(priv->pdev->irq, rtl8180_interrupt, + IRQF_SHARED, KBUILD_MODNAME, dev); + if (ret) { + wiphy_err(dev->wiphy, "failed to register IRQ handler\n"); + goto err_free_rings; + } + + rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF); + + rtl818x_iowrite32(priv, &priv->map->MAR[0], ~0); + rtl818x_iowrite32(priv, &priv->map->MAR[1], ~0); + + reg = RTL818X_RX_CONF_ONLYERLPKT | + RTL818X_RX_CONF_RX_AUTORESETPHY | + RTL818X_RX_CONF_MGMT | + RTL818X_RX_CONF_DATA | + (7 << 8 /* MAX RX DMA */) | + RTL818X_RX_CONF_BROADCAST | + RTL818X_RX_CONF_NICMAC; + + if (priv->r8185) + reg |= RTL818X_RX_CONF_CSDM1 | RTL818X_RX_CONF_CSDM2; + else { + reg |= (priv->rfparam & RF_PARAM_CARRIERSENSE1) + ? RTL818X_RX_CONF_CSDM1 : 0; + reg |= (priv->rfparam & RF_PARAM_CARRIERSENSE2) + ? RTL818X_RX_CONF_CSDM2 : 0; + } + + priv->rx_conf = reg; + rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg); + + if (priv->r8185) { + reg = rtl818x_ioread8(priv, &priv->map->CW_CONF); + reg &= ~RTL818X_CW_CONF_PERPACKET_CW_SHIFT; + reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT; + rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg); + + reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL); + reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT; + reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT; + reg |= RTL818X_TX_AGC_CTL_FEEDBACK_ANT; + rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg); + + /* disable early TX */ + rtl818x_iowrite8(priv, (u8 __iomem *)priv->map + 0xec, 0x3f); + } + + reg = rtl818x_ioread32(priv, &priv->map->TX_CONF); + reg |= (6 << 21 /* MAX TX DMA */) | + RTL818X_TX_CONF_NO_ICV; + + if (priv->r8185) + reg &= ~RTL818X_TX_CONF_PROBE_DTS; + else + reg &= ~RTL818X_TX_CONF_HW_SEQNUM; + + /* different meaning, same value on both rtl8185 and rtl8180 */ + reg &= ~RTL818X_TX_CONF_SAT_HWPLCP; + + rtl818x_iowrite32(priv, &priv->map->TX_CONF, reg); + + reg = rtl818x_ioread8(priv, &priv->map->CMD); + reg |= RTL818X_CMD_RX_ENABLE; + reg |= RTL818X_CMD_TX_ENABLE; + rtl818x_iowrite8(priv, &priv->map->CMD, reg); + + return 0; + + err_free_rings: + rtl8180_free_rx_ring(dev); + for (i = 0; i < 4; i++) + if (priv->tx_ring[i].desc) + rtl8180_free_tx_ring(dev, i); + + return ret; +} + +static void rtl8180_stop(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + u8 reg; + int i; + + rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0); + + reg = rtl818x_ioread8(priv, &priv->map->CMD); + reg &= ~RTL818X_CMD_TX_ENABLE; + reg &= ~RTL818X_CMD_RX_ENABLE; + rtl818x_iowrite8(priv, &priv->map->CMD, reg); + + priv->rf->stop(dev); + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG4); + rtl818x_iowrite8(priv, &priv->map->CONFIG4, reg | RTL818X_CONFIG4_VCOOFF); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + + free_irq(priv->pdev->irq, dev); + + rtl8180_free_rx_ring(dev); + for (i = 0; i < 4; i++) + rtl8180_free_tx_ring(dev, i); +} + +static u64 rtl8180_get_tsf(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + + return rtl818x_ioread32(priv, &priv->map->TSFT[0]) | + (u64)(rtl818x_ioread32(priv, &priv->map->TSFT[1])) << 32; +} + +static void rtl8180_beacon_work(struct work_struct *work) +{ + struct rtl8180_vif *vif_priv = + container_of(work, struct rtl8180_vif, beacon_work.work); + struct ieee80211_vif *vif = + container_of((void *)vif_priv, struct ieee80211_vif, drv_priv); + struct ieee80211_hw *dev = vif_priv->dev; + struct ieee80211_mgmt *mgmt; + struct sk_buff *skb; + int err = 0; + + /* don't overflow the tx ring */ + if (ieee80211_queue_stopped(dev, 0)) + goto resched; + + /* grab a fresh beacon */ + skb = ieee80211_beacon_get(dev, vif); + if (!skb) + goto resched; + + /* + * update beacon timestamp w/ TSF value + * TODO: make hardware update beacon timestamp + */ + mgmt = (struct ieee80211_mgmt *)skb->data; + mgmt->u.beacon.timestamp = cpu_to_le64(rtl8180_get_tsf(dev)); + + /* TODO: use actual beacon queue */ + skb_set_queue_mapping(skb, 0); + + err = rtl8180_tx(dev, skb); + WARN_ON(err); + +resched: + /* + * schedule next beacon + * TODO: use hardware support for beacon timing + */ + schedule_delayed_work(&vif_priv->beacon_work, + usecs_to_jiffies(1024 * vif->bss_conf.beacon_int)); +} + +static int rtl8180_add_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif) +{ + struct rtl8180_priv *priv = dev->priv; + struct rtl8180_vif *vif_priv; + + /* + * We only support one active interface at a time. + */ + if (priv->vif) + return -EBUSY; + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + break; + default: + return -EOPNOTSUPP; + } + + priv->vif = vif; + + /* Initialize driver private area */ + vif_priv = (struct rtl8180_vif *)&vif->drv_priv; + vif_priv->dev = dev; + INIT_DELAYED_WORK(&vif_priv->beacon_work, rtl8180_beacon_work); + vif_priv->enable_beacon = false; + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + rtl818x_iowrite32(priv, (__le32 __iomem *)&priv->map->MAC[0], + le32_to_cpu(*(__le32 *)vif->addr)); + rtl818x_iowrite16(priv, (__le16 __iomem *)&priv->map->MAC[4], + le16_to_cpu(*(__le16 *)(vif->addr + 4))); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + + return 0; +} + +static void rtl8180_remove_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif) +{ + struct rtl8180_priv *priv = dev->priv; + priv->vif = NULL; +} + +static int rtl8180_config(struct ieee80211_hw *dev, u32 changed) +{ + struct rtl8180_priv *priv = dev->priv; + struct ieee80211_conf *conf = &dev->conf; + + priv->rf->set_chan(dev, conf); + + return 0; +} + +static void rtl8180_bss_info_changed(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u32 changed) +{ + struct rtl8180_priv *priv = dev->priv; + struct rtl8180_vif *vif_priv; + int i; + u8 reg; + + vif_priv = (struct rtl8180_vif *)&vif->drv_priv; + + if (changed & BSS_CHANGED_BSSID) { + for (i = 0; i < ETH_ALEN; i++) + rtl818x_iowrite8(priv, &priv->map->BSSID[i], + info->bssid[i]); + + if (is_valid_ether_addr(info->bssid)) { + if (vif->type == NL80211_IFTYPE_ADHOC) + reg = RTL818X_MSR_ADHOC; + else + reg = RTL818X_MSR_INFRA; + } else + reg = RTL818X_MSR_NO_LINK; + rtl818x_iowrite8(priv, &priv->map->MSR, reg); + } + + if (changed & BSS_CHANGED_ERP_SLOT && priv->rf->conf_erp) + priv->rf->conf_erp(dev, info); + + if (changed & BSS_CHANGED_BEACON_ENABLED) + vif_priv->enable_beacon = info->enable_beacon; + + if (changed & (BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_BEACON)) { + cancel_delayed_work_sync(&vif_priv->beacon_work); + if (vif_priv->enable_beacon) + schedule_work(&vif_priv->beacon_work.work); + } +} + +static u64 rtl8180_prepare_multicast(struct ieee80211_hw *dev, + struct netdev_hw_addr_list *mc_list) +{ + return netdev_hw_addr_list_count(mc_list); +} + +static void rtl8180_configure_filter(struct ieee80211_hw *dev, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct rtl8180_priv *priv = dev->priv; + + if (changed_flags & FIF_FCSFAIL) + priv->rx_conf ^= RTL818X_RX_CONF_FCS; + if (changed_flags & FIF_CONTROL) + priv->rx_conf ^= RTL818X_RX_CONF_CTRL; + if (changed_flags & FIF_OTHER_BSS) + priv->rx_conf ^= RTL818X_RX_CONF_MONITOR; + if (*total_flags & FIF_ALLMULTI || multicast > 0) + priv->rx_conf |= RTL818X_RX_CONF_MULTICAST; + else + priv->rx_conf &= ~RTL818X_RX_CONF_MULTICAST; + + *total_flags = 0; + + if (priv->rx_conf & RTL818X_RX_CONF_FCS) + *total_flags |= FIF_FCSFAIL; + if (priv->rx_conf & RTL818X_RX_CONF_CTRL) + *total_flags |= FIF_CONTROL; + if (priv->rx_conf & RTL818X_RX_CONF_MONITOR) + *total_flags |= FIF_OTHER_BSS; + if (priv->rx_conf & RTL818X_RX_CONF_MULTICAST) + *total_flags |= FIF_ALLMULTI; + + rtl818x_iowrite32(priv, &priv->map->RX_CONF, priv->rx_conf); +} + +static const struct ieee80211_ops rtl8180_ops = { + .tx = rtl8180_tx, + .start = rtl8180_start, + .stop = rtl8180_stop, + .add_interface = rtl8180_add_interface, + .remove_interface = rtl8180_remove_interface, + .config = rtl8180_config, + .bss_info_changed = rtl8180_bss_info_changed, + .prepare_multicast = rtl8180_prepare_multicast, + .configure_filter = rtl8180_configure_filter, + .get_tsf = rtl8180_get_tsf, +}; + +static void rtl8180_eeprom_register_read(struct eeprom_93cx6 *eeprom) +{ + struct ieee80211_hw *dev = eeprom->data; + struct rtl8180_priv *priv = dev->priv; + u8 reg = rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + + eeprom->reg_data_in = reg & RTL818X_EEPROM_CMD_WRITE; + eeprom->reg_data_out = reg & RTL818X_EEPROM_CMD_READ; + eeprom->reg_data_clock = reg & RTL818X_EEPROM_CMD_CK; + eeprom->reg_chip_select = reg & RTL818X_EEPROM_CMD_CS; +} + +static void rtl8180_eeprom_register_write(struct eeprom_93cx6 *eeprom) +{ + struct ieee80211_hw *dev = eeprom->data; + struct rtl8180_priv *priv = dev->priv; + u8 reg = 2 << 6; + + if (eeprom->reg_data_in) + reg |= RTL818X_EEPROM_CMD_WRITE; + if (eeprom->reg_data_out) + reg |= RTL818X_EEPROM_CMD_READ; + if (eeprom->reg_data_clock) + reg |= RTL818X_EEPROM_CMD_CK; + if (eeprom->reg_chip_select) + reg |= RTL818X_EEPROM_CMD_CS; + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, reg); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(10); +} + +static int __devinit rtl8180_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct ieee80211_hw *dev; + struct rtl8180_priv *priv; + unsigned long mem_addr, mem_len; + unsigned int io_addr, io_len; + int err, i; + struct eeprom_93cx6 eeprom; + const char *chip_name, *rf_name = NULL; + u32 reg; + u16 eeprom_val; + u8 mac_addr[ETH_ALEN]; + + err = pci_enable_device(pdev); + if (err) { + printk(KERN_ERR "%s (rtl8180): Cannot enable new PCI device\n", + pci_name(pdev)); + return err; + } + + err = pci_request_regions(pdev, KBUILD_MODNAME); + if (err) { + printk(KERN_ERR "%s (rtl8180): Cannot obtain PCI resources\n", + pci_name(pdev)); + return err; + } + + io_addr = pci_resource_start(pdev, 0); + io_len = pci_resource_len(pdev, 0); + mem_addr = pci_resource_start(pdev, 1); + mem_len = pci_resource_len(pdev, 1); + + if (mem_len < sizeof(struct rtl818x_csr) || + io_len < sizeof(struct rtl818x_csr)) { + printk(KERN_ERR "%s (rtl8180): Too short PCI resources\n", + pci_name(pdev)); + err = -ENOMEM; + goto err_free_reg; + } + + if ((err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) || + (err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)))) { + printk(KERN_ERR "%s (rtl8180): No suitable DMA available\n", + pci_name(pdev)); + goto err_free_reg; + } + + pci_set_master(pdev); + + dev = ieee80211_alloc_hw(sizeof(*priv), &rtl8180_ops); + if (!dev) { + printk(KERN_ERR "%s (rtl8180): ieee80211 alloc failed\n", + pci_name(pdev)); + err = -ENOMEM; + goto err_free_reg; + } + + priv = dev->priv; + priv->pdev = pdev; + + dev->max_rates = 2; + SET_IEEE80211_DEV(dev, &pdev->dev); + pci_set_drvdata(pdev, dev); + + priv->map = pci_iomap(pdev, 1, mem_len); + if (!priv->map) + priv->map = pci_iomap(pdev, 0, io_len); + + if (!priv->map) { + printk(KERN_ERR "%s (rtl8180): Cannot map device memory\n", + pci_name(pdev)); + goto err_free_dev; + } + + BUILD_BUG_ON(sizeof(priv->channels) != sizeof(rtl818x_channels)); + BUILD_BUG_ON(sizeof(priv->rates) != sizeof(rtl818x_rates)); + + memcpy(priv->channels, rtl818x_channels, sizeof(rtl818x_channels)); + memcpy(priv->rates, rtl818x_rates, sizeof(rtl818x_rates)); + + priv->band.band = IEEE80211_BAND_2GHZ; + priv->band.channels = priv->channels; + priv->band.n_channels = ARRAY_SIZE(rtl818x_channels); + priv->band.bitrates = priv->rates; + priv->band.n_bitrates = 4; + dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; + + dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | + IEEE80211_HW_RX_INCLUDES_FCS | + IEEE80211_HW_SIGNAL_UNSPEC; + dev->vif_data_size = sizeof(struct rtl8180_vif); + dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC); + dev->queues = 1; + dev->max_signal = 65; + + reg = rtl818x_ioread32(priv, &priv->map->TX_CONF); + reg &= RTL818X_TX_CONF_HWVER_MASK; + switch (reg) { + case RTL818X_TX_CONF_R8180_ABCD: + chip_name = "RTL8180"; + break; + case RTL818X_TX_CONF_R8180_F: + chip_name = "RTL8180vF"; + break; + case RTL818X_TX_CONF_R8185_ABC: + chip_name = "RTL8185"; + break; + case RTL818X_TX_CONF_R8185_D: + chip_name = "RTL8185vD"; + break; + default: + printk(KERN_ERR "%s (rtl8180): Unknown chip! (0x%x)\n", + pci_name(pdev), reg >> 25); + goto err_iounmap; + } + + priv->r8185 = reg & RTL818X_TX_CONF_R8185_ABC; + if (priv->r8185) { + priv->band.n_bitrates = ARRAY_SIZE(rtl818x_rates); + pci_try_set_mwi(pdev); + } + + eeprom.data = dev; + eeprom.register_read = rtl8180_eeprom_register_read; + eeprom.register_write = rtl8180_eeprom_register_write; + if (rtl818x_ioread32(priv, &priv->map->RX_CONF) & (1 << 6)) + eeprom.width = PCI_EEPROM_WIDTH_93C66; + else + eeprom.width = PCI_EEPROM_WIDTH_93C46; + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_PROGRAM); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(10); + + eeprom_93cx6_read(&eeprom, 0x06, &eeprom_val); + eeprom_val &= 0xFF; + switch (eeprom_val) { + case 1: rf_name = "Intersil"; + break; + case 2: rf_name = "RFMD"; + break; + case 3: priv->rf = &sa2400_rf_ops; + break; + case 4: priv->rf = &max2820_rf_ops; + break; + case 5: priv->rf = &grf5101_rf_ops; + break; + case 9: priv->rf = rtl8180_detect_rf(dev); + break; + case 10: + rf_name = "RTL8255"; + break; + default: + printk(KERN_ERR "%s (rtl8180): Unknown RF! (0x%x)\n", + pci_name(pdev), eeprom_val); + goto err_iounmap; + } + + if (!priv->rf) { + printk(KERN_ERR "%s (rtl8180): %s RF frontend not supported!\n", + pci_name(pdev), rf_name); + goto err_iounmap; + } + + eeprom_93cx6_read(&eeprom, 0x17, &eeprom_val); + priv->csthreshold = eeprom_val >> 8; + if (!priv->r8185) { + __le32 anaparam; + eeprom_93cx6_multiread(&eeprom, 0xD, (__le16 *)&anaparam, 2); + priv->anaparam = le32_to_cpu(anaparam); + eeprom_93cx6_read(&eeprom, 0x19, &priv->rfparam); + } + + eeprom_93cx6_multiread(&eeprom, 0x7, (__le16 *)mac_addr, 3); + if (!is_valid_ether_addr(mac_addr)) { + printk(KERN_WARNING "%s (rtl8180): Invalid hwaddr! Using" + " randomly generated MAC addr\n", pci_name(pdev)); + random_ether_addr(mac_addr); + } + SET_IEEE80211_PERM_ADDR(dev, mac_addr); + + /* CCK TX power */ + for (i = 0; i < 14; i += 2) { + u16 txpwr; + eeprom_93cx6_read(&eeprom, 0x10 + (i >> 1), &txpwr); + priv->channels[i].hw_value = txpwr & 0xFF; + priv->channels[i + 1].hw_value = txpwr >> 8; + } + + /* OFDM TX power */ + if (priv->r8185) { + for (i = 0; i < 14; i += 2) { + u16 txpwr; + eeprom_93cx6_read(&eeprom, 0x20 + (i >> 1), &txpwr); + priv->channels[i].hw_value |= (txpwr & 0xFF) << 8; + priv->channels[i + 1].hw_value |= txpwr & 0xFF00; + } + } + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + + spin_lock_init(&priv->lock); + + err = ieee80211_register_hw(dev); + if (err) { + printk(KERN_ERR "%s (rtl8180): Cannot register device\n", + pci_name(pdev)); + goto err_iounmap; + } + + wiphy_info(dev->wiphy, "hwaddr %pm, %s + %s\n", + mac_addr, chip_name, priv->rf->name); + + return 0; + + err_iounmap: + iounmap(priv->map); + + err_free_dev: + pci_set_drvdata(pdev, NULL); + ieee80211_free_hw(dev); + + err_free_reg: + pci_release_regions(pdev); + pci_disable_device(pdev); + return err; +} + +static void __devexit rtl8180_remove(struct pci_dev *pdev) +{ + struct ieee80211_hw *dev = pci_get_drvdata(pdev); + struct rtl8180_priv *priv; + + if (!dev) + return; + + ieee80211_unregister_hw(dev); + + priv = dev->priv; + + pci_iounmap(pdev, priv->map); + pci_release_regions(pdev); + pci_disable_device(pdev); + ieee80211_free_hw(dev); +} + +#ifdef CONFIG_PM +static int rtl8180_suspend(struct pci_dev *pdev, pm_message_t state) +{ + pci_save_state(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + return 0; +} + +static int rtl8180_resume(struct pci_dev *pdev) +{ + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + return 0; +} + +#endif /* CONFIG_PM */ + +static struct pci_driver rtl8180_driver = { + .name = KBUILD_MODNAME, + .id_table = rtl8180_table, + .probe = rtl8180_probe, + .remove = __devexit_p(rtl8180_remove), +#ifdef CONFIG_PM + .suspend = rtl8180_suspend, + .resume = rtl8180_resume, +#endif /* CONFIG_PM */ +}; + +static int __init rtl8180_init(void) +{ + return pci_register_driver(&rtl8180_driver); +} + +static void __exit rtl8180_exit(void) +{ + pci_unregister_driver(&rtl8180_driver); +} + +module_init(rtl8180_init); +module_exit(rtl8180_exit); diff --git a/drivers/net/wireless/rtl818x/rtl8180/grf5101.c b/drivers/net/wireless/rtl818x/rtl8180/grf5101.c new file mode 100644 index 000000000000..5ee7589dd546 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8180/grf5101.c @@ -0,0 +1,190 @@ + +/* + * Radio tuning for GCT GRF5101 on RTL8180 + * + * Copyright 2007 Andrea Merello + * + * Code from the BSD driver and the rtl8181 project have been + * very useful to understand certain things + * + * I want to thanks the Authors of such projects and the Ndiswrapper + * project Authors. + * + * A special Big Thanks also is for all people who donated me cards, + * making possible the creation of the original rtl8180 driver + * from which this code is derived! + * + * 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 +#include +#include +#include + +#include "rtl8180.h" +#include "grf5101.h" + +static const int grf5101_encode[] = { + 0x0, 0x8, 0x4, 0xC, + 0x2, 0xA, 0x6, 0xE, + 0x1, 0x9, 0x5, 0xD, + 0x3, 0xB, 0x7, 0xF +}; + +static void write_grf5101(struct ieee80211_hw *dev, u8 addr, u32 data) +{ + struct rtl8180_priv *priv = dev->priv; + u32 phy_config; + + phy_config = grf5101_encode[(data >> 8) & 0xF]; + phy_config |= grf5101_encode[(data >> 4) & 0xF] << 4; + phy_config |= grf5101_encode[data & 0xF] << 8; + phy_config |= grf5101_encode[(addr >> 1) & 0xF] << 12; + phy_config |= (addr & 1) << 16; + phy_config |= grf5101_encode[(data & 0xf000) >> 12] << 24; + + /* MAC will bang bits to the chip */ + phy_config |= 0x90000000; + + rtl818x_iowrite32(priv, + (__le32 __iomem *) &priv->map->RFPinsOutput, phy_config); + + msleep(3); +} + +static void grf5101_write_phy_antenna(struct ieee80211_hw *dev, short chan) +{ + struct rtl8180_priv *priv = dev->priv; + u8 ant = GRF5101_ANTENNA; + + if (priv->rfparam & RF_PARAM_ANTBDEFAULT) + ant |= BB_ANTENNA_B; + + if (chan == 14) + ant |= BB_ANTATTEN_CHAN14; + + rtl8180_write_phy(dev, 0x10, ant); +} + +static u8 grf5101_rf_calc_rssi(u8 agc, u8 sq) +{ + if (agc > 60) + return 65; + + /* TODO(?): just return agc (or agc + 5) to avoid mult / div */ + return 65 * agc / 60; +} + +static void grf5101_rf_set_channel(struct ieee80211_hw *dev, + struct ieee80211_conf *conf) +{ + struct rtl8180_priv *priv = dev->priv; + int channel = ieee80211_frequency_to_channel(conf->channel->center_freq); + u32 txpw = priv->channels[channel - 1].hw_value & 0xFF; + u32 chan = channel - 1; + + /* set TX power */ + write_grf5101(dev, 0x15, 0x0); + write_grf5101(dev, 0x06, txpw); + write_grf5101(dev, 0x15, 0x10); + write_grf5101(dev, 0x15, 0x0); + + /* set frequency */ + write_grf5101(dev, 0x07, 0x0); + write_grf5101(dev, 0x0B, chan); + write_grf5101(dev, 0x07, 0x1000); + + grf5101_write_phy_antenna(dev, channel); +} + +static void grf5101_rf_stop(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + u32 anaparam; + + anaparam = priv->anaparam; + anaparam &= 0x000fffff; + anaparam |= 0x3f900000; + rtl8180_set_anaparam(priv, anaparam); + + write_grf5101(dev, 0x07, 0x0); + write_grf5101(dev, 0x1f, 0x45); + write_grf5101(dev, 0x1f, 0x5); + write_grf5101(dev, 0x00, 0x8e4); +} + +static void grf5101_rf_init(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + + rtl8180_set_anaparam(priv, priv->anaparam); + + write_grf5101(dev, 0x1f, 0x0); + write_grf5101(dev, 0x1f, 0x0); + write_grf5101(dev, 0x1f, 0x40); + write_grf5101(dev, 0x1f, 0x60); + write_grf5101(dev, 0x1f, 0x61); + write_grf5101(dev, 0x1f, 0x61); + write_grf5101(dev, 0x00, 0xae4); + write_grf5101(dev, 0x1f, 0x1); + write_grf5101(dev, 0x1f, 0x41); + write_grf5101(dev, 0x1f, 0x61); + + write_grf5101(dev, 0x01, 0x1a23); + write_grf5101(dev, 0x02, 0x4971); + write_grf5101(dev, 0x03, 0x41de); + write_grf5101(dev, 0x04, 0x2d80); + write_grf5101(dev, 0x05, 0x68ff); /* 0x61ff original value */ + write_grf5101(dev, 0x06, 0x0); + write_grf5101(dev, 0x07, 0x0); + write_grf5101(dev, 0x08, 0x7533); + write_grf5101(dev, 0x09, 0xc401); + write_grf5101(dev, 0x0a, 0x0); + write_grf5101(dev, 0x0c, 0x1c7); + write_grf5101(dev, 0x0d, 0x29d3); + write_grf5101(dev, 0x0e, 0x2e8); + write_grf5101(dev, 0x10, 0x192); + write_grf5101(dev, 0x11, 0x248); + write_grf5101(dev, 0x12, 0x0); + write_grf5101(dev, 0x13, 0x20c4); + write_grf5101(dev, 0x14, 0xf4fc); + write_grf5101(dev, 0x15, 0x0); + write_grf5101(dev, 0x16, 0x1500); + + write_grf5101(dev, 0x07, 0x1000); + + /* baseband configuration */ + rtl8180_write_phy(dev, 0, 0xa8); + rtl8180_write_phy(dev, 3, 0x0); + rtl8180_write_phy(dev, 4, 0xc0); + rtl8180_write_phy(dev, 5, 0x90); + rtl8180_write_phy(dev, 6, 0x1e); + rtl8180_write_phy(dev, 7, 0x64); + + grf5101_write_phy_antenna(dev, 1); + + rtl8180_write_phy(dev, 0x11, 0x88); + + if (rtl818x_ioread8(priv, &priv->map->CONFIG2) & + RTL818X_CONFIG2_ANTENNA_DIV) + rtl8180_write_phy(dev, 0x12, 0xc0); /* enable ant diversity */ + else + rtl8180_write_phy(dev, 0x12, 0x40); /* disable ant diversity */ + + rtl8180_write_phy(dev, 0x13, 0x90 | priv->csthreshold); + + rtl8180_write_phy(dev, 0x19, 0x0); + rtl8180_write_phy(dev, 0x1a, 0xa0); + rtl8180_write_phy(dev, 0x1b, 0x44); +} + +const struct rtl818x_rf_ops grf5101_rf_ops = { + .name = "GCT", + .init = grf5101_rf_init, + .stop = grf5101_rf_stop, + .set_chan = grf5101_rf_set_channel, + .calc_rssi = grf5101_rf_calc_rssi, +}; diff --git a/drivers/net/wireless/rtl818x/rtl8180/grf5101.h b/drivers/net/wireless/rtl818x/rtl8180/grf5101.h new file mode 100644 index 000000000000..76647111bcff --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8180/grf5101.h @@ -0,0 +1,28 @@ +#ifndef RTL8180_GRF5101_H +#define RTL8180_GRF5101_H + +/* + * Radio tuning for GCT GRF5101 on RTL8180 + * + * Copyright 2007 Andrea Merello + * + * Code from the BSD driver and the rtl8181 project have been + * very useful to understand certain things + * + * I want to thanks the Authors of such projects and the Ndiswrapper + * project Authors. + * + * A special Big Thanks also is for all people who donated me cards, + * making possible the creation of the original rtl8180 driver + * from which this code is derived! + * + * 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. + */ + +#define GRF5101_ANTENNA 0xA3 + +extern const struct rtl818x_rf_ops grf5101_rf_ops; + +#endif /* RTL8180_GRF5101_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8180/max2820.c b/drivers/net/wireless/rtl818x/rtl8180/max2820.c new file mode 100644 index 000000000000..667b3363d437 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8180/max2820.c @@ -0,0 +1,169 @@ +/* + * Radio tuning for Maxim max2820 on RTL8180 + * + * Copyright 2007 Andrea Merello + * + * Code from the BSD driver and the rtl8181 project have been + * very useful to understand certain things + * + * I want to thanks the Authors of such projects and the Ndiswrapper + * project Authors. + * + * A special Big Thanks also is for all people who donated me cards, + * making possible the creation of the original rtl8180 driver + * from which this code is derived! + * + * 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 +#include +#include +#include + +#include "rtl8180.h" +#include "max2820.h" + +static const u32 max2820_chan[] = { + 12, /* CH 1 */ + 17, + 22, + 27, + 32, + 37, + 42, + 47, + 52, + 57, + 62, + 67, + 72, + 84, /* CH 14 */ +}; + +static void write_max2820(struct ieee80211_hw *dev, u8 addr, u32 data) +{ + struct rtl8180_priv *priv = dev->priv; + u32 phy_config; + + phy_config = 0x90 + (data & 0xf); + phy_config <<= 16; + phy_config += addr; + phy_config <<= 8; + phy_config += (data >> 4) & 0xff; + + rtl818x_iowrite32(priv, + (__le32 __iomem *) &priv->map->RFPinsOutput, phy_config); + + msleep(1); +} + +static void max2820_write_phy_antenna(struct ieee80211_hw *dev, short chan) +{ + struct rtl8180_priv *priv = dev->priv; + u8 ant; + + ant = MAXIM_ANTENNA; + if (priv->rfparam & RF_PARAM_ANTBDEFAULT) + ant |= BB_ANTENNA_B; + if (chan == 14) + ant |= BB_ANTATTEN_CHAN14; + + rtl8180_write_phy(dev, 0x10, ant); +} + +static u8 max2820_rf_calc_rssi(u8 agc, u8 sq) +{ + bool odd; + + odd = !!(agc & 1); + + agc >>= 1; + if (odd) + agc += 76; + else + agc += 66; + + /* TODO: change addends above to avoid mult / div below */ + return 65 * agc / 100; +} + +static void max2820_rf_set_channel(struct ieee80211_hw *dev, + struct ieee80211_conf *conf) +{ + struct rtl8180_priv *priv = dev->priv; + int channel = conf ? + ieee80211_frequency_to_channel(conf->channel->center_freq) : 1; + unsigned int chan_idx = channel - 1; + u32 txpw = priv->channels[chan_idx].hw_value & 0xFF; + u32 chan = max2820_chan[chan_idx]; + + /* While philips SA2400 drive the PA bias from + * sa2400, for MAXIM we do this directly from BB */ + rtl8180_write_phy(dev, 3, txpw); + + max2820_write_phy_antenna(dev, channel); + write_max2820(dev, 3, chan); +} + +static void max2820_rf_stop(struct ieee80211_hw *dev) +{ + rtl8180_write_phy(dev, 3, 0x8); + write_max2820(dev, 1, 0); +} + + +static void max2820_rf_init(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + + /* MAXIM from netbsd driver */ + write_max2820(dev, 0, 0x007); /* test mode as indicated in datasheet */ + write_max2820(dev, 1, 0x01e); /* enable register */ + write_max2820(dev, 2, 0x001); /* synt register */ + + max2820_rf_set_channel(dev, NULL); + + write_max2820(dev, 4, 0x313); /* rx register */ + + /* PA is driven directly by the BB, we keep the MAXIM bias + * at the highest value in case that setting it to lower + * values may introduce some further attenuation somewhere.. + */ + write_max2820(dev, 5, 0x00f); + + /* baseband configuration */ + rtl8180_write_phy(dev, 0, 0x88); /* sys1 */ + rtl8180_write_phy(dev, 3, 0x08); /* txagc */ + rtl8180_write_phy(dev, 4, 0xf8); /* lnadet */ + rtl8180_write_phy(dev, 5, 0x90); /* ifagcinit */ + rtl8180_write_phy(dev, 6, 0x1a); /* ifagclimit */ + rtl8180_write_phy(dev, 7, 0x64); /* ifagcdet */ + + max2820_write_phy_antenna(dev, 1); + + rtl8180_write_phy(dev, 0x11, 0x88); /* trl */ + + if (rtl818x_ioread8(priv, &priv->map->CONFIG2) & + RTL818X_CONFIG2_ANTENNA_DIV) + rtl8180_write_phy(dev, 0x12, 0xc7); + else + rtl8180_write_phy(dev, 0x12, 0x47); + + rtl8180_write_phy(dev, 0x13, 0x9b); + + rtl8180_write_phy(dev, 0x19, 0x0); /* CHESTLIM */ + rtl8180_write_phy(dev, 0x1a, 0x9f); /* CHSQLIM */ + + max2820_rf_set_channel(dev, NULL); +} + +const struct rtl818x_rf_ops max2820_rf_ops = { + .name = "Maxim", + .init = max2820_rf_init, + .stop = max2820_rf_stop, + .set_chan = max2820_rf_set_channel, + .calc_rssi = max2820_rf_calc_rssi, +}; diff --git a/drivers/net/wireless/rtl818x/rtl8180/max2820.h b/drivers/net/wireless/rtl818x/rtl8180/max2820.h new file mode 100644 index 000000000000..61cf6d1e7d57 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8180/max2820.h @@ -0,0 +1,28 @@ +#ifndef RTL8180_MAX2820_H +#define RTL8180_MAX2820_H + +/* + * Radio tuning for Maxim max2820 on RTL8180 + * + * Copyright 2007 Andrea Merello + * + * Code from the BSD driver and the rtl8181 project have been + * very useful to understand certain things + * + * I want to thanks the Authors of such projects and the Ndiswrapper + * project Authors. + * + * A special Big Thanks also is for all people who donated me cards, + * making possible the creation of the original rtl8180 driver + * from which this code is derived! + * + * 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. + */ + +#define MAXIM_ANTENNA 0xb3 + +extern const struct rtl818x_rf_ops max2820_rf_ops; + +#endif /* RTL8180_MAX2820_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8180/rtl8180.h b/drivers/net/wireless/rtl818x/rtl8180/rtl8180.h new file mode 100644 index 000000000000..30523314da43 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8180/rtl8180.h @@ -0,0 +1,130 @@ +#ifndef RTL8180_H +#define RTL8180_H + +#include "rtl818x.h" + +#define MAX_RX_SIZE IEEE80211_MAX_RTS_THRESHOLD + +#define RF_PARAM_ANALOGPHY (1 << 0) +#define RF_PARAM_ANTBDEFAULT (1 << 1) +#define RF_PARAM_CARRIERSENSE1 (1 << 2) +#define RF_PARAM_CARRIERSENSE2 (1 << 3) + +#define BB_ANTATTEN_CHAN14 0x0C +#define BB_ANTENNA_B 0x40 + +#define BB_HOST_BANG (1 << 30) +#define BB_HOST_BANG_EN (1 << 2) +#define BB_HOST_BANG_CLK (1 << 1) +#define BB_HOST_BANG_DATA 1 + +#define ANAPARAM_TXDACOFF_SHIFT 27 +#define ANAPARAM_PWR0_SHIFT 28 +#define ANAPARAM_PWR0_MASK (0x07 << ANAPARAM_PWR0_SHIFT) +#define ANAPARAM_PWR1_SHIFT 20 +#define ANAPARAM_PWR1_MASK (0x7F << ANAPARAM_PWR1_SHIFT) + +struct rtl8180_tx_desc { + __le32 flags; + __le16 rts_duration; + __le16 plcp_len; + __le32 tx_buf; + __le32 frame_len; + __le32 next_tx_desc; + u8 cw; + u8 retry_limit; + u8 agc; + u8 flags2; + u32 reserved[2]; +} __packed; + +struct rtl8180_rx_desc { + __le32 flags; + __le32 flags2; + union { + __le32 rx_buf; + __le64 tsft; + }; +} __packed; + +struct rtl8180_tx_ring { + struct rtl8180_tx_desc *desc; + dma_addr_t dma; + unsigned int idx; + unsigned int entries; + struct sk_buff_head queue; +}; + +struct rtl8180_vif { + struct ieee80211_hw *dev; + + /* beaconing */ + struct delayed_work beacon_work; + bool enable_beacon; +}; + +struct rtl8180_priv { + /* common between rtl818x drivers */ + struct rtl818x_csr __iomem *map; + const struct rtl818x_rf_ops *rf; + struct ieee80211_vif *vif; + + /* rtl8180 driver specific */ + spinlock_t lock; + struct rtl8180_rx_desc *rx_ring; + dma_addr_t rx_ring_dma; + unsigned int rx_idx; + struct sk_buff *rx_buf[32]; + struct rtl8180_tx_ring tx_ring[4]; + struct ieee80211_channel channels[14]; + struct ieee80211_rate rates[12]; + struct ieee80211_supported_band band; + struct pci_dev *pdev; + u32 rx_conf; + + int r8185; + u32 anaparam; + u16 rfparam; + u8 csthreshold; + + /* sequence # */ + u16 seqno; +}; + +void rtl8180_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data); +void rtl8180_set_anaparam(struct rtl8180_priv *priv, u32 anaparam); + +static inline u8 rtl818x_ioread8(struct rtl8180_priv *priv, u8 __iomem *addr) +{ + return ioread8(addr); +} + +static inline u16 rtl818x_ioread16(struct rtl8180_priv *priv, __le16 __iomem *addr) +{ + return ioread16(addr); +} + +static inline u32 rtl818x_ioread32(struct rtl8180_priv *priv, __le32 __iomem *addr) +{ + return ioread32(addr); +} + +static inline void rtl818x_iowrite8(struct rtl8180_priv *priv, + u8 __iomem *addr, u8 val) +{ + iowrite8(val, addr); +} + +static inline void rtl818x_iowrite16(struct rtl8180_priv *priv, + __le16 __iomem *addr, u16 val) +{ + iowrite16(val, addr); +} + +static inline void rtl818x_iowrite32(struct rtl8180_priv *priv, + __le32 __iomem *addr, u32 val) +{ + iowrite32(val, addr); +} + +#endif /* RTL8180_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8180/rtl8225.c b/drivers/net/wireless/rtl818x/rtl8180/rtl8225.c new file mode 100644 index 000000000000..7c4574ba9d75 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8180/rtl8225.c @@ -0,0 +1,791 @@ + +/* + * Radio tuning for RTL8225 on RTL8180 + * + * Copyright 2007 Michael Wu + * Copyright 2007 Andrea Merello + * + * Based on the r8180 driver, which is: + * Copyright 2005 Andrea Merello , et al. + * + * Thanks to Realtek for their support! + * + * 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 +#include +#include +#include + +#include "rtl8180.h" +#include "rtl8225.h" + +static void rtl8225_write(struct ieee80211_hw *dev, u8 addr, u16 data) +{ + struct rtl8180_priv *priv = dev->priv; + u16 reg80, reg84, reg82; + u32 bangdata; + int i; + + bangdata = (data << 4) | (addr & 0xf); + + reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput) & 0xfff3; + reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable); + + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x7); + + reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x7 | 0x400); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(10); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(2); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(10); + + for (i = 15; i >= 0; i--) { + u16 reg = reg80; + + if (bangdata & (1 << i)) + reg |= 1; + + if (i & 1) + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1)); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1)); + + if (!(i & 1)) + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); + } + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(10); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x400); + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); +} + +static u16 rtl8225_read(struct ieee80211_hw *dev, u8 addr) +{ + struct rtl8180_priv *priv = dev->priv; + u16 reg80, reg82, reg84, out; + int i; + + reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput); + reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable); + reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect) | 0x400; + + reg80 &= ~0xF; + + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x000F); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x000F); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(4); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(5); + + for (i = 4; i >= 0; i--) { + u16 reg = reg80 | ((addr >> i) & 1); + + if (!(i & 1)) { + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(1); + } + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg | (1 << 1)); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(2); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg | (1 << 1)); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(2); + + if (i & 1) { + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(1); + } + } + + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x000E); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x040E); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3) | (1 << 1)); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(2); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3)); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(2); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3)); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(2); + + out = 0; + for (i = 11; i >= 0; i--) { + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3)); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(1); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3) | (1 << 1)); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(2); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3) | (1 << 1)); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(2); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3) | (1 << 1)); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(2); + + if (rtl818x_ioread16(priv, &priv->map->RFPinsInput) & (1 << 1)) + out |= 1 << i; + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3)); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(2); + } + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3) | (1 << 2)); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + udelay(2); + + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x03A0); + + return out; +} + +static const u16 rtl8225bcd_rxgain[] = { + 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409, + 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541, + 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583, + 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644, + 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688, + 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745, + 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789, + 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793, + 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d, + 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9, + 0x07aa, 0x07ab, 0x07ac, 0x07ad, 0x07b0, 0x07b1, 0x07b2, 0x07b3, + 0x07b4, 0x07b5, 0x07b8, 0x07b9, 0x07ba, 0x07bb, 0x07bb +}; + +static const u8 rtl8225_agc[] = { + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, 0x97, 0x96, + 0x95, 0x94, 0x93, 0x92, 0x91, 0x90, 0x8f, 0x8e, + 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86, + 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, 0x3f, 0x3e, + 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, + 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2f, 0x2e, + 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, + 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e, + 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, + 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, + 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, + 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 +}; + +static const u8 rtl8225_gain[] = { + 0x23, 0x88, 0x7c, 0xa5, /* -82dbm */ + 0x23, 0x88, 0x7c, 0xb5, /* -82dbm */ + 0x23, 0x88, 0x7c, 0xc5, /* -82dbm */ + 0x33, 0x80, 0x79, 0xc5, /* -78dbm */ + 0x43, 0x78, 0x76, 0xc5, /* -74dbm */ + 0x53, 0x60, 0x73, 0xc5, /* -70dbm */ + 0x63, 0x58, 0x70, 0xc5, /* -66dbm */ +}; + +static const u8 rtl8225_threshold[] = { + 0x8d, 0x8d, 0x8d, 0x8d, 0x9d, 0xad, 0xbd +}; + +static const u8 rtl8225_tx_gain_cck_ofdm[] = { + 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e +}; + +static const u8 rtl8225_tx_power_cck[] = { + 0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02, + 0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02, + 0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02, + 0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02, + 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03, + 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03 +}; + +static const u8 rtl8225_tx_power_cck_ch14[] = { + 0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00, + 0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00 +}; + +static const u8 rtl8225_tx_power_ofdm[] = { + 0x80, 0x90, 0xa2, 0xb5, 0xcb, 0xe4 +}; + +static const u32 rtl8225_chan[] = { + 0x085c, 0x08dc, 0x095c, 0x09dc, 0x0a5c, 0x0adc, 0x0b5c, + 0x0bdc, 0x0c5c, 0x0cdc, 0x0d5c, 0x0ddc, 0x0e5c, 0x0f72 +}; + +static void rtl8225_rf_set_tx_power(struct ieee80211_hw *dev, int channel) +{ + struct rtl8180_priv *priv = dev->priv; + u8 cck_power, ofdm_power; + const u8 *tmp; + u32 reg; + int i; + + cck_power = priv->channels[channel - 1].hw_value & 0xFF; + ofdm_power = priv->channels[channel - 1].hw_value >> 8; + + cck_power = min(cck_power, (u8)35); + ofdm_power = min(ofdm_power, (u8)35); + + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, + rtl8225_tx_gain_cck_ofdm[cck_power / 6] >> 1); + + if (channel == 14) + tmp = &rtl8225_tx_power_cck_ch14[(cck_power % 6) * 8]; + else + tmp = &rtl8225_tx_power_cck[(cck_power % 6) * 8]; + + for (i = 0; i < 8; i++) + rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++); + + msleep(1); /* FIXME: optional? */ + + /* anaparam2 on */ + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, RTL8225_ANAPARAM2_ON); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, + rtl8225_tx_gain_cck_ofdm[ofdm_power/6] >> 1); + + tmp = &rtl8225_tx_power_ofdm[ofdm_power % 6]; + + rtl8225_write_phy_ofdm(dev, 5, *tmp); + rtl8225_write_phy_ofdm(dev, 7, *tmp); + + msleep(1); +} + +static void rtl8225_rf_init(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + int i; + + rtl8180_set_anaparam(priv, RTL8225_ANAPARAM_ON); + + /* host_pci_init */ + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480); + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x0488); + rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + msleep(200); /* FIXME: ehh?? */ + rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0xFF & ~(1 << 6)); + + rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x000a8008); + + /* TODO: check if we need really to change BRSR to do RF config */ + rtl818x_ioread16(priv, &priv->map->BRSR); + rtl818x_iowrite16(priv, &priv->map->BRSR, 0xFFFF); + rtl818x_iowrite32(priv, &priv->map->RF_PARA, 0x00100044); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, 0x44); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + + rtl8225_write(dev, 0x0, 0x067); + rtl8225_write(dev, 0x1, 0xFE0); + rtl8225_write(dev, 0x2, 0x44D); + rtl8225_write(dev, 0x3, 0x441); + rtl8225_write(dev, 0x4, 0x8BE); + rtl8225_write(dev, 0x5, 0xBF0); /* TODO: minipci */ + rtl8225_write(dev, 0x6, 0xAE6); + rtl8225_write(dev, 0x7, rtl8225_chan[0]); + rtl8225_write(dev, 0x8, 0x01F); + rtl8225_write(dev, 0x9, 0x334); + rtl8225_write(dev, 0xA, 0xFD4); + rtl8225_write(dev, 0xB, 0x391); + rtl8225_write(dev, 0xC, 0x050); + rtl8225_write(dev, 0xD, 0x6DB); + rtl8225_write(dev, 0xE, 0x029); + rtl8225_write(dev, 0xF, 0x914); msleep(1); + + rtl8225_write(dev, 0x2, 0xC4D); msleep(100); + + rtl8225_write(dev, 0x0, 0x127); + + for (i = 0; i < ARRAY_SIZE(rtl8225bcd_rxgain); i++) { + rtl8225_write(dev, 0x1, i + 1); + rtl8225_write(dev, 0x2, rtl8225bcd_rxgain[i]); + } + + rtl8225_write(dev, 0x0, 0x027); + rtl8225_write(dev, 0x0, 0x22F); + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); + + for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) { + rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]); + msleep(1); + rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i); + msleep(1); + } + + msleep(1); + + rtl8225_write_phy_ofdm(dev, 0x00, 0x01); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x01, 0x02); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x02, 0x62); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x03, 0x00); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x04, 0x00); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x05, 0x00); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x06, 0x00); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x07, 0x00); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x08, 0x00); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x0a, 0x09); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x10, 0x84); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x11, 0x03); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x12, 0x20); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x13, 0x20); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x14, 0x00); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x15, 0x40); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x16, 0x00); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x17, 0x40); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x18, 0xef); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x19, 0x19); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x1b, 0x76); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x1e, 0x95); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x21, 0x27); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x22, 0x16); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x24, 0x46); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x25, 0x20); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x27, 0x88); msleep(1); + + rtl8225_write_phy_cck(dev, 0x00, 0x98); msleep(1); + rtl8225_write_phy_cck(dev, 0x03, 0x20); msleep(1); + rtl8225_write_phy_cck(dev, 0x04, 0x7e); msleep(1); + rtl8225_write_phy_cck(dev, 0x05, 0x12); msleep(1); + rtl8225_write_phy_cck(dev, 0x06, 0xfc); msleep(1); + rtl8225_write_phy_cck(dev, 0x07, 0x78); msleep(1); + rtl8225_write_phy_cck(dev, 0x08, 0x2e); msleep(1); + rtl8225_write_phy_cck(dev, 0x10, 0x93); msleep(1); + rtl8225_write_phy_cck(dev, 0x11, 0x88); msleep(1); + rtl8225_write_phy_cck(dev, 0x12, 0x47); msleep(1); + rtl8225_write_phy_cck(dev, 0x13, 0xd0); + rtl8225_write_phy_cck(dev, 0x19, 0x00); + rtl8225_write_phy_cck(dev, 0x1a, 0xa0); + rtl8225_write_phy_cck(dev, 0x1b, 0x08); + rtl8225_write_phy_cck(dev, 0x40, 0x86); + rtl8225_write_phy_cck(dev, 0x41, 0x8d); msleep(1); + rtl8225_write_phy_cck(dev, 0x42, 0x15); msleep(1); + rtl8225_write_phy_cck(dev, 0x43, 0x18); msleep(1); + rtl8225_write_phy_cck(dev, 0x44, 0x1f); msleep(1); + rtl8225_write_phy_cck(dev, 0x45, 0x1e); msleep(1); + rtl8225_write_phy_cck(dev, 0x46, 0x1a); msleep(1); + rtl8225_write_phy_cck(dev, 0x47, 0x15); msleep(1); + rtl8225_write_phy_cck(dev, 0x48, 0x10); msleep(1); + rtl8225_write_phy_cck(dev, 0x49, 0x0a); msleep(1); + rtl8225_write_phy_cck(dev, 0x4a, 0x05); msleep(1); + rtl8225_write_phy_cck(dev, 0x4b, 0x02); msleep(1); + rtl8225_write_phy_cck(dev, 0x4c, 0x05); msleep(1); + + rtl818x_iowrite8(priv, &priv->map->TESTR, 0x0D); msleep(1); + + rtl8225_rf_set_tx_power(dev, 1); + + /* RX antenna default to A */ + rtl8225_write_phy_cck(dev, 0x10, 0x9b); msleep(1); /* B: 0xDB */ + rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1); /* B: 0x10 */ + + rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); /* B: 0x00 */ + msleep(1); + rtl818x_iowrite32(priv, (__le32 __iomem *)((void __iomem *)priv->map + 0x94), 0x15c00002); + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); + + rtl8225_write(dev, 0x0c, 0x50); + /* set OFDM initial gain */ + rtl8225_write_phy_ofdm(dev, 0x0d, rtl8225_gain[4 * 4]); + rtl8225_write_phy_ofdm(dev, 0x23, rtl8225_gain[4 * 4 + 1]); + rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225_gain[4 * 4 + 2]); + rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225_gain[4 * 4 + 3]); + /* set CCK threshold */ + rtl8225_write_phy_cck(dev, 0x41, rtl8225_threshold[0]); +} + +static const u8 rtl8225z2_tx_power_cck_ch14[] = { + 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00 +}; + +static const u8 rtl8225z2_tx_power_cck_B[] = { + 0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x04 +}; + +static const u8 rtl8225z2_tx_power_cck_A[] = { + 0x33, 0x32, 0x2b, 0x23, 0x1a, 0x11, 0x08, 0x04 +}; + +static const u8 rtl8225z2_tx_power_cck[] = { + 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04 +}; + +static void rtl8225z2_rf_set_tx_power(struct ieee80211_hw *dev, int channel) +{ + struct rtl8180_priv *priv = dev->priv; + u8 cck_power, ofdm_power; + const u8 *tmp; + int i; + + cck_power = priv->channels[channel - 1].hw_value & 0xFF; + ofdm_power = priv->channels[channel - 1].hw_value >> 8; + + if (channel == 14) + tmp = rtl8225z2_tx_power_cck_ch14; + else if (cck_power == 12) + tmp = rtl8225z2_tx_power_cck_B; + else if (cck_power == 13) + tmp = rtl8225z2_tx_power_cck_A; + else + tmp = rtl8225z2_tx_power_cck; + + for (i = 0; i < 8; i++) + rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++); + + cck_power = min(cck_power, (u8)35); + if (cck_power == 13 || cck_power == 14) + cck_power = 12; + if (cck_power >= 15) + cck_power -= 2; + + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, cck_power); + rtl818x_ioread8(priv, &priv->map->TX_GAIN_CCK); + msleep(1); + + ofdm_power = min(ofdm_power, (u8)35); + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, ofdm_power); + + rtl8225_write_phy_ofdm(dev, 2, 0x62); + rtl8225_write_phy_ofdm(dev, 5, 0x00); + rtl8225_write_phy_ofdm(dev, 6, 0x40); + rtl8225_write_phy_ofdm(dev, 7, 0x00); + rtl8225_write_phy_ofdm(dev, 8, 0x40); + + msleep(1); +} + +static const u16 rtl8225z2_rxgain[] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0008, 0x0009, + 0x000a, 0x000b, 0x0102, 0x0103, 0x0104, 0x0105, 0x0140, 0x0141, + 0x0142, 0x0143, 0x0144, 0x0145, 0x0180, 0x0181, 0x0182, 0x0183, + 0x0184, 0x0185, 0x0188, 0x0189, 0x018a, 0x018b, 0x0243, 0x0244, + 0x0245, 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0288, + 0x0289, 0x028a, 0x028b, 0x028c, 0x0342, 0x0343, 0x0344, 0x0345, + 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0388, 0x0389, + 0x038a, 0x038b, 0x038c, 0x038d, 0x0390, 0x0391, 0x0392, 0x0393, + 0x0394, 0x0395, 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, + 0x03a0, 0x03a1, 0x03a2, 0x03a3, 0x03a4, 0x03a5, 0x03a8, 0x03a9, + 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3, + 0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb +}; + +static void rtl8225z2_rf_init(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + int i; + + rtl8180_set_anaparam(priv, RTL8225_ANAPARAM_ON); + + /* host_pci_init */ + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480); + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x0488); + rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + msleep(200); /* FIXME: ehh?? */ + rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0xFF & ~(1 << 6)); + + rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x00088008); + + /* TODO: check if we need really to change BRSR to do RF config */ + rtl818x_ioread16(priv, &priv->map->BRSR); + rtl818x_iowrite16(priv, &priv->map->BRSR, 0xFFFF); + rtl818x_iowrite32(priv, &priv->map->RF_PARA, 0x00100044); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, 0x44); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); + + rtl8225_write(dev, 0x0, 0x0B7); msleep(1); + rtl8225_write(dev, 0x1, 0xEE0); msleep(1); + rtl8225_write(dev, 0x2, 0x44D); msleep(1); + rtl8225_write(dev, 0x3, 0x441); msleep(1); + rtl8225_write(dev, 0x4, 0x8C3); msleep(1); + rtl8225_write(dev, 0x5, 0xC72); msleep(1); + rtl8225_write(dev, 0x6, 0x0E6); msleep(1); + rtl8225_write(dev, 0x7, 0x82A); msleep(1); + rtl8225_write(dev, 0x8, 0x03F); msleep(1); + rtl8225_write(dev, 0x9, 0x335); msleep(1); + rtl8225_write(dev, 0xa, 0x9D4); msleep(1); + rtl8225_write(dev, 0xb, 0x7BB); msleep(1); + rtl8225_write(dev, 0xc, 0x850); msleep(1); + rtl8225_write(dev, 0xd, 0xCDF); msleep(1); + rtl8225_write(dev, 0xe, 0x02B); msleep(1); + rtl8225_write(dev, 0xf, 0x114); msleep(100); + + if (!(rtl8225_read(dev, 6) & (1 << 7))) { + rtl8225_write(dev, 0x02, 0x0C4D); + msleep(200); + rtl8225_write(dev, 0x02, 0x044D); + msleep(100); + /* TODO: readd calibration failure message when the calibration + check works */ + } + + rtl8225_write(dev, 0x0, 0x1B7); + rtl8225_write(dev, 0x3, 0x002); + rtl8225_write(dev, 0x5, 0x004); + + for (i = 0; i < ARRAY_SIZE(rtl8225z2_rxgain); i++) { + rtl8225_write(dev, 0x1, i + 1); + rtl8225_write(dev, 0x2, rtl8225z2_rxgain[i]); + } + + rtl8225_write(dev, 0x0, 0x0B7); msleep(100); + rtl8225_write(dev, 0x2, 0xC4D); + + msleep(200); + rtl8225_write(dev, 0x2, 0x44D); + msleep(100); + + rtl8225_write(dev, 0x00, 0x2BF); + rtl8225_write(dev, 0xFF, 0xFFFF); + + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); + + for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) { + rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]); + msleep(1); + rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i); + msleep(1); + } + + msleep(1); + + rtl8225_write_phy_ofdm(dev, 0x00, 0x01); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x01, 0x02); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x02, 0x62); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x03, 0x00); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x04, 0x00); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x05, 0x00); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x06, 0x40); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x07, 0x00); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x08, 0x40); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x0a, 0x09); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x18, 0xef); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x0d, 0x43); + rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x10, 0x84); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x11, 0x06); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x12, 0x20); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x13, 0x20); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x14, 0x00); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x15, 0x40); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x16, 0x00); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x17, 0x40); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x18, 0xef); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x19, 0x19); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x1b, 0x11); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x1d, 0xc5); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x1e, 0xb3); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x21, 0x27); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x22, 0x16); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x23, 0x80); msleep(1); /* FIXME: not needed? */ + rtl8225_write_phy_ofdm(dev, 0x24, 0x46); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x25, 0x20); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1); + rtl8225_write_phy_ofdm(dev, 0x27, 0x88); msleep(1); + + rtl8225_write_phy_cck(dev, 0x00, 0x98); msleep(1); + rtl8225_write_phy_cck(dev, 0x03, 0x20); msleep(1); + rtl8225_write_phy_cck(dev, 0x04, 0x7e); msleep(1); + rtl8225_write_phy_cck(dev, 0x05, 0x12); msleep(1); + rtl8225_write_phy_cck(dev, 0x06, 0xfc); msleep(1); + rtl8225_write_phy_cck(dev, 0x07, 0x78); msleep(1); + rtl8225_write_phy_cck(dev, 0x08, 0x2e); msleep(1); + rtl8225_write_phy_cck(dev, 0x10, 0x93); msleep(1); + rtl8225_write_phy_cck(dev, 0x11, 0x88); msleep(1); + rtl8225_write_phy_cck(dev, 0x12, 0x47); msleep(1); + rtl8225_write_phy_cck(dev, 0x13, 0xd0); + rtl8225_write_phy_cck(dev, 0x19, 0x00); + rtl8225_write_phy_cck(dev, 0x1a, 0xa0); + rtl8225_write_phy_cck(dev, 0x1b, 0x08); + rtl8225_write_phy_cck(dev, 0x40, 0x86); + rtl8225_write_phy_cck(dev, 0x41, 0x8a); msleep(1); + rtl8225_write_phy_cck(dev, 0x42, 0x15); msleep(1); + rtl8225_write_phy_cck(dev, 0x43, 0x18); msleep(1); + rtl8225_write_phy_cck(dev, 0x44, 0x36); msleep(1); + rtl8225_write_phy_cck(dev, 0x45, 0x35); msleep(1); + rtl8225_write_phy_cck(dev, 0x46, 0x2e); msleep(1); + rtl8225_write_phy_cck(dev, 0x47, 0x25); msleep(1); + rtl8225_write_phy_cck(dev, 0x48, 0x1c); msleep(1); + rtl8225_write_phy_cck(dev, 0x49, 0x12); msleep(1); + rtl8225_write_phy_cck(dev, 0x4a, 0x09); msleep(1); + rtl8225_write_phy_cck(dev, 0x4b, 0x04); msleep(1); + rtl8225_write_phy_cck(dev, 0x4c, 0x05); msleep(1); + + rtl818x_iowrite8(priv, (u8 __iomem *)((void __iomem *)priv->map + 0x5B), 0x0D); msleep(1); + + rtl8225z2_rf_set_tx_power(dev, 1); + + /* RX antenna default to A */ + rtl8225_write_phy_cck(dev, 0x10, 0x9b); msleep(1); /* B: 0xDB */ + rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1); /* B: 0x10 */ + + rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); /* B: 0x00 */ + msleep(1); + rtl818x_iowrite32(priv, (__le32 __iomem *)((void __iomem *)priv->map + 0x94), 0x15c00002); + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); +} + +static void rtl8225_rf_stop(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + u8 reg; + + rtl8225_write(dev, 0x4, 0x1f); msleep(1); + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, RTL8225_ANAPARAM2_OFF); + rtl818x_iowrite32(priv, &priv->map->ANAPARAM, RTL8225_ANAPARAM_OFF); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); +} + +static void rtl8225_rf_set_channel(struct ieee80211_hw *dev, + struct ieee80211_conf *conf) +{ + struct rtl8180_priv *priv = dev->priv; + int chan = ieee80211_frequency_to_channel(conf->channel->center_freq); + + if (priv->rf->init == rtl8225_rf_init) + rtl8225_rf_set_tx_power(dev, chan); + else + rtl8225z2_rf_set_tx_power(dev, chan); + + rtl8225_write(dev, 0x7, rtl8225_chan[chan - 1]); + msleep(10); +} + +static void rtl8225_rf_conf_erp(struct ieee80211_hw *dev, + struct ieee80211_bss_conf *info) +{ + struct rtl8180_priv *priv = dev->priv; + + if (info->use_short_slot) { + rtl818x_iowrite8(priv, &priv->map->SLOT, 0x9); + rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22); + rtl818x_iowrite8(priv, &priv->map->DIFS, 0x14); + rtl818x_iowrite8(priv, &priv->map->EIFS, 81); + rtl818x_iowrite8(priv, &priv->map->CW_VAL, 0x73); + } else { + rtl818x_iowrite8(priv, &priv->map->SLOT, 0x14); + rtl818x_iowrite8(priv, &priv->map->SIFS, 0x44); + rtl818x_iowrite8(priv, &priv->map->DIFS, 0x24); + rtl818x_iowrite8(priv, &priv->map->EIFS, 81); + rtl818x_iowrite8(priv, &priv->map->CW_VAL, 0xa5); + } +} + +static const struct rtl818x_rf_ops rtl8225_ops = { + .name = "rtl8225", + .init = rtl8225_rf_init, + .stop = rtl8225_rf_stop, + .set_chan = rtl8225_rf_set_channel, + .conf_erp = rtl8225_rf_conf_erp, +}; + +static const struct rtl818x_rf_ops rtl8225z2_ops = { + .name = "rtl8225z2", + .init = rtl8225z2_rf_init, + .stop = rtl8225_rf_stop, + .set_chan = rtl8225_rf_set_channel, + .conf_erp = rtl8225_rf_conf_erp, +}; + +const struct rtl818x_rf_ops * rtl8180_detect_rf(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + u16 reg8, reg9; + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x0488); + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); + rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + msleep(100); + + rtl8225_write(dev, 0, 0x1B7); + + reg8 = rtl8225_read(dev, 8); + reg9 = rtl8225_read(dev, 9); + + rtl8225_write(dev, 0, 0x0B7); + + if (reg8 != 0x588 || reg9 != 0x700) + return &rtl8225_ops; + + return &rtl8225z2_ops; +} diff --git a/drivers/net/wireless/rtl818x/rtl8180/rtl8225.h b/drivers/net/wireless/rtl818x/rtl8180/rtl8225.h new file mode 100644 index 000000000000..310013a2d726 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8180/rtl8225.h @@ -0,0 +1,23 @@ +#ifndef RTL8180_RTL8225_H +#define RTL8180_RTL8225_H + +#define RTL8225_ANAPARAM_ON 0xa0000b59 +#define RTL8225_ANAPARAM2_ON 0x860dec11 +#define RTL8225_ANAPARAM_OFF 0xa00beb59 +#define RTL8225_ANAPARAM2_OFF 0x840dec11 + +const struct rtl818x_rf_ops * rtl8180_detect_rf(struct ieee80211_hw *); + +static inline void rtl8225_write_phy_ofdm(struct ieee80211_hw *dev, + u8 addr, u8 data) +{ + rtl8180_write_phy(dev, addr, data); +} + +static inline void rtl8225_write_phy_cck(struct ieee80211_hw *dev, + u8 addr, u8 data) +{ + rtl8180_write_phy(dev, addr, data | 0x10000); +} + +#endif /* RTL8180_RTL8225_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8180/sa2400.c b/drivers/net/wireless/rtl818x/rtl8180/sa2400.c new file mode 100644 index 000000000000..44771a6286af --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8180/sa2400.c @@ -0,0 +1,228 @@ + +/* + * Radio tuning for Philips SA2400 on RTL8180 + * + * Copyright 2007 Andrea Merello + * + * Code from the BSD driver and the rtl8181 project have been + * very useful to understand certain things + * + * I want to thanks the Authors of such projects and the Ndiswrapper + * project Authors. + * + * A special Big Thanks also is for all people who donated me cards, + * making possible the creation of the original rtl8180 driver + * from which this code is derived! + * + * 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 +#include +#include +#include + +#include "rtl8180.h" +#include "sa2400.h" + +static const u32 sa2400_chan[] = { + 0x00096c, /* ch1 */ + 0x080970, + 0x100974, + 0x180978, + 0x000980, + 0x080984, + 0x100988, + 0x18098c, + 0x000994, + 0x080998, + 0x10099c, + 0x1809a0, + 0x0009a8, + 0x0009b4, /* ch 14 */ +}; + +static void write_sa2400(struct ieee80211_hw *dev, u8 addr, u32 data) +{ + struct rtl8180_priv *priv = dev->priv; + u32 phy_config; + + /* MAC will bang bits to the sa2400. sw 3-wire is NOT used */ + phy_config = 0xb0000000; + + phy_config |= ((u32)(addr & 0xf)) << 24; + phy_config |= data & 0xffffff; + + rtl818x_iowrite32(priv, + (__le32 __iomem *) &priv->map->RFPinsOutput, phy_config); + + msleep(3); +} + +static void sa2400_write_phy_antenna(struct ieee80211_hw *dev, short chan) +{ + struct rtl8180_priv *priv = dev->priv; + u8 ant = SA2400_ANTENNA; + + if (priv->rfparam & RF_PARAM_ANTBDEFAULT) + ant |= BB_ANTENNA_B; + + if (chan == 14) + ant |= BB_ANTATTEN_CHAN14; + + rtl8180_write_phy(dev, 0x10, ant); + +} + +static u8 sa2400_rf_rssi_map[] = { + 0x64, 0x64, 0x63, 0x62, 0x61, 0x60, 0x5f, 0x5e, + 0x5d, 0x5c, 0x5b, 0x5a, 0x57, 0x54, 0x52, 0x50, + 0x4e, 0x4c, 0x4a, 0x48, 0x46, 0x44, 0x41, 0x3f, + 0x3c, 0x3a, 0x37, 0x36, 0x36, 0x1c, 0x1c, 0x1b, + 0x1b, 0x1a, 0x1a, 0x19, 0x19, 0x18, 0x18, 0x17, + 0x17, 0x16, 0x16, 0x15, 0x15, 0x14, 0x14, 0x13, + 0x13, 0x12, 0x12, 0x11, 0x11, 0x10, 0x10, 0x0f, + 0x0f, 0x0e, 0x0e, 0x0d, 0x0d, 0x0c, 0x0c, 0x0b, + 0x0b, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x07, + 0x07, 0x06, 0x06, 0x05, 0x04, 0x03, 0x02, +}; + +static u8 sa2400_rf_calc_rssi(u8 agc, u8 sq) +{ + if (sq == 0x80) + return 1; + + if (sq > 78) + return 32; + + /* TODO: recalc sa2400_rf_rssi_map to avoid mult / div */ + return 65 * sa2400_rf_rssi_map[sq] / 100; +} + +static void sa2400_rf_set_channel(struct ieee80211_hw *dev, + struct ieee80211_conf *conf) +{ + struct rtl8180_priv *priv = dev->priv; + int channel = ieee80211_frequency_to_channel(conf->channel->center_freq); + u32 txpw = priv->channels[channel - 1].hw_value & 0xFF; + u32 chan = sa2400_chan[channel - 1]; + + write_sa2400(dev, 7, txpw); + + sa2400_write_phy_antenna(dev, channel); + + write_sa2400(dev, 0, chan); + write_sa2400(dev, 1, 0xbb50); + write_sa2400(dev, 2, 0x80); + write_sa2400(dev, 3, 0); +} + +static void sa2400_rf_stop(struct ieee80211_hw *dev) +{ + write_sa2400(dev, 4, 0); +} + +static void sa2400_rf_init(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + u32 anaparam, txconf; + u8 firdac; + int analogphy = priv->rfparam & RF_PARAM_ANALOGPHY; + + anaparam = priv->anaparam; + anaparam &= ~(1 << ANAPARAM_TXDACOFF_SHIFT); + anaparam &= ~ANAPARAM_PWR1_MASK; + anaparam &= ~ANAPARAM_PWR0_MASK; + + if (analogphy) { + anaparam |= SA2400_ANA_ANAPARAM_PWR1_ON << ANAPARAM_PWR1_SHIFT; + firdac = 0; + } else { + anaparam |= (SA2400_DIG_ANAPARAM_PWR1_ON << ANAPARAM_PWR1_SHIFT); + anaparam |= (SA2400_ANAPARAM_PWR0_ON << ANAPARAM_PWR0_SHIFT); + firdac = 1 << SA2400_REG4_FIRDAC_SHIFT; + } + + rtl8180_set_anaparam(priv, anaparam); + + write_sa2400(dev, 0, sa2400_chan[0]); + write_sa2400(dev, 1, 0xbb50); + write_sa2400(dev, 2, 0x80); + write_sa2400(dev, 3, 0); + write_sa2400(dev, 4, 0x19340 | firdac); + write_sa2400(dev, 5, 0x1dfb | (SA2400_MAX_SENS - 54) << 15); + write_sa2400(dev, 4, 0x19348 | firdac); /* calibrate VCO */ + + if (!analogphy) + write_sa2400(dev, 4, 0x1938c); /*???*/ + + write_sa2400(dev, 4, 0x19340 | firdac); + + write_sa2400(dev, 0, sa2400_chan[0]); + write_sa2400(dev, 1, 0xbb50); + write_sa2400(dev, 2, 0x80); + write_sa2400(dev, 3, 0); + write_sa2400(dev, 4, 0x19344 | firdac); /* calibrate filter */ + + /* new from rtl8180 embedded driver (rtl8181 project) */ + write_sa2400(dev, 6, 0x13ff | (1 << 23)); /* MANRX */ + write_sa2400(dev, 8, 0); /* VCO */ + + if (analogphy) { + rtl8180_set_anaparam(priv, anaparam | + (1 << ANAPARAM_TXDACOFF_SHIFT)); + + txconf = rtl818x_ioread32(priv, &priv->map->TX_CONF); + rtl818x_iowrite32(priv, &priv->map->TX_CONF, + txconf | RTL818X_TX_CONF_LOOPBACK_CONT); + + write_sa2400(dev, 4, 0x19341); /* calibrates DC */ + + /* a 5us sleep is required here, + * we rely on the 3ms delay introduced in write_sa2400 */ + write_sa2400(dev, 4, 0x19345); + + /* a 20us sleep is required here, + * we rely on the 3ms delay introduced in write_sa2400 */ + + rtl818x_iowrite32(priv, &priv->map->TX_CONF, txconf); + + rtl8180_set_anaparam(priv, anaparam); + } + /* end new code */ + + write_sa2400(dev, 4, 0x19341 | firdac); /* RTX MODE */ + + /* baseband configuration */ + rtl8180_write_phy(dev, 0, 0x98); + rtl8180_write_phy(dev, 3, 0x38); + rtl8180_write_phy(dev, 4, 0xe0); + rtl8180_write_phy(dev, 5, 0x90); + rtl8180_write_phy(dev, 6, 0x1a); + rtl8180_write_phy(dev, 7, 0x64); + + sa2400_write_phy_antenna(dev, 1); + + rtl8180_write_phy(dev, 0x11, 0x80); + + if (rtl818x_ioread8(priv, &priv->map->CONFIG2) & + RTL818X_CONFIG2_ANTENNA_DIV) + rtl8180_write_phy(dev, 0x12, 0xc7); /* enable ant diversity */ + else + rtl8180_write_phy(dev, 0x12, 0x47); /* disable ant diversity */ + + rtl8180_write_phy(dev, 0x13, 0x90 | priv->csthreshold); + + rtl8180_write_phy(dev, 0x19, 0x0); + rtl8180_write_phy(dev, 0x1a, 0xa0); +} + +const struct rtl818x_rf_ops sa2400_rf_ops = { + .name = "Philips", + .init = sa2400_rf_init, + .stop = sa2400_rf_stop, + .set_chan = sa2400_rf_set_channel, + .calc_rssi = sa2400_rf_calc_rssi, +}; diff --git a/drivers/net/wireless/rtl818x/rtl8180/sa2400.h b/drivers/net/wireless/rtl818x/rtl8180/sa2400.h new file mode 100644 index 000000000000..a4aaa0d413f1 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8180/sa2400.h @@ -0,0 +1,36 @@ +#ifndef RTL8180_SA2400_H +#define RTL8180_SA2400_H + +/* + * Radio tuning for Philips SA2400 on RTL8180 + * + * Copyright 2007 Andrea Merello + * + * Code from the BSD driver and the rtl8181 project have been + * very useful to understand certain things + * + * I want to thanks the Authors of such projects and the Ndiswrapper + * project Authors. + * + * A special Big Thanks also is for all people who donated me cards, + * making possible the creation of the original rtl8180 driver + * from which this code is derived! + * + * 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. + */ + +#define SA2400_ANTENNA 0x91 +#define SA2400_DIG_ANAPARAM_PWR1_ON 0x8 +#define SA2400_ANA_ANAPARAM_PWR1_ON 0x28 +#define SA2400_ANAPARAM_PWR0_ON 0x3 + +/* RX sensitivity in dbm */ +#define SA2400_MAX_SENS 85 + +#define SA2400_REG4_FIRDAC_SHIFT 7 + +extern const struct rtl818x_rf_ops sa2400_rf_ops; + +#endif /* RTL8180_SA2400_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8180_dev.c b/drivers/net/wireless/rtl818x/rtl8180_dev.c deleted file mode 100644 index 707c688da618..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8180_dev.c +++ /dev/null @@ -1,1188 +0,0 @@ - -/* - * Linux device driver for RTL8180 / RTL8185 - * - * Copyright 2007 Michael Wu - * Copyright 2007 Andrea Merello - * - * Based on the r8180 driver, which is: - * Copyright 2004-2005 Andrea Merello , et al. - * - * Thanks to Realtek for their support! - * - * 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 -#include -#include -#include -#include -#include -#include - -#include "rtl8180.h" -#include "rtl8180_rtl8225.h" -#include "rtl8180_sa2400.h" -#include "rtl8180_max2820.h" -#include "rtl8180_grf5101.h" - -MODULE_AUTHOR("Michael Wu "); -MODULE_AUTHOR("Andrea Merello "); -MODULE_DESCRIPTION("RTL8180 / RTL8185 PCI wireless driver"); -MODULE_LICENSE("GPL"); - -static DEFINE_PCI_DEVICE_TABLE(rtl8180_table) = { - /* rtl8185 */ - { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8185) }, - { PCI_DEVICE(PCI_VENDOR_ID_BELKIN, 0x700f) }, - { PCI_DEVICE(PCI_VENDOR_ID_BELKIN, 0x701f) }, - - /* rtl8180 */ - { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8180) }, - { PCI_DEVICE(0x1799, 0x6001) }, - { PCI_DEVICE(0x1799, 0x6020) }, - { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x3300) }, - { } -}; - -MODULE_DEVICE_TABLE(pci, rtl8180_table); - -static const struct ieee80211_rate rtl818x_rates[] = { - { .bitrate = 10, .hw_value = 0, }, - { .bitrate = 20, .hw_value = 1, }, - { .bitrate = 55, .hw_value = 2, }, - { .bitrate = 110, .hw_value = 3, }, - { .bitrate = 60, .hw_value = 4, }, - { .bitrate = 90, .hw_value = 5, }, - { .bitrate = 120, .hw_value = 6, }, - { .bitrate = 180, .hw_value = 7, }, - { .bitrate = 240, .hw_value = 8, }, - { .bitrate = 360, .hw_value = 9, }, - { .bitrate = 480, .hw_value = 10, }, - { .bitrate = 540, .hw_value = 11, }, -}; - -static const struct ieee80211_channel rtl818x_channels[] = { - { .center_freq = 2412 }, - { .center_freq = 2417 }, - { .center_freq = 2422 }, - { .center_freq = 2427 }, - { .center_freq = 2432 }, - { .center_freq = 2437 }, - { .center_freq = 2442 }, - { .center_freq = 2447 }, - { .center_freq = 2452 }, - { .center_freq = 2457 }, - { .center_freq = 2462 }, - { .center_freq = 2467 }, - { .center_freq = 2472 }, - { .center_freq = 2484 }, -}; - - -void rtl8180_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data) -{ - struct rtl8180_priv *priv = dev->priv; - int i = 10; - u32 buf; - - buf = (data << 8) | addr; - - rtl818x_iowrite32(priv, (__le32 __iomem *)&priv->map->PHY[0], buf | 0x80); - while (i--) { - rtl818x_iowrite32(priv, (__le32 __iomem *)&priv->map->PHY[0], buf); - if (rtl818x_ioread8(priv, &priv->map->PHY[2]) == (data & 0xFF)) - return; - } -} - -static void rtl8180_handle_rx(struct ieee80211_hw *dev) -{ - struct rtl8180_priv *priv = dev->priv; - unsigned int count = 32; - u8 signal, agc, sq; - - while (count--) { - struct rtl8180_rx_desc *entry = &priv->rx_ring[priv->rx_idx]; - struct sk_buff *skb = priv->rx_buf[priv->rx_idx]; - u32 flags = le32_to_cpu(entry->flags); - - if (flags & RTL818X_RX_DESC_FLAG_OWN) - return; - - if (unlikely(flags & (RTL818X_RX_DESC_FLAG_DMA_FAIL | - RTL818X_RX_DESC_FLAG_FOF | - RTL818X_RX_DESC_FLAG_RX_ERR))) - goto done; - else { - u32 flags2 = le32_to_cpu(entry->flags2); - struct ieee80211_rx_status rx_status = {0}; - struct sk_buff *new_skb = dev_alloc_skb(MAX_RX_SIZE); - - if (unlikely(!new_skb)) - goto done; - - pci_unmap_single(priv->pdev, - *((dma_addr_t *)skb->cb), - MAX_RX_SIZE, PCI_DMA_FROMDEVICE); - skb_put(skb, flags & 0xFFF); - - rx_status.antenna = (flags2 >> 15) & 1; - rx_status.rate_idx = (flags >> 20) & 0xF; - agc = (flags2 >> 17) & 0x7F; - if (priv->r8185) { - if (rx_status.rate_idx > 3) - signal = 90 - clamp_t(u8, agc, 25, 90); - else - signal = 95 - clamp_t(u8, agc, 30, 95); - } else { - sq = flags2 & 0xff; - signal = priv->rf->calc_rssi(agc, sq); - } - rx_status.signal = signal; - rx_status.freq = dev->conf.channel->center_freq; - rx_status.band = dev->conf.channel->band; - rx_status.mactime = le64_to_cpu(entry->tsft); - rx_status.flag |= RX_FLAG_TSFT; - if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR) - rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; - - memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); - ieee80211_rx_irqsafe(dev, skb); - - skb = new_skb; - priv->rx_buf[priv->rx_idx] = skb; - *((dma_addr_t *) skb->cb) = - pci_map_single(priv->pdev, skb_tail_pointer(skb), - MAX_RX_SIZE, PCI_DMA_FROMDEVICE); - } - - done: - entry->rx_buf = cpu_to_le32(*((dma_addr_t *)skb->cb)); - entry->flags = cpu_to_le32(RTL818X_RX_DESC_FLAG_OWN | - MAX_RX_SIZE); - if (priv->rx_idx == 31) - entry->flags |= cpu_to_le32(RTL818X_RX_DESC_FLAG_EOR); - priv->rx_idx = (priv->rx_idx + 1) % 32; - } -} - -static void rtl8180_handle_tx(struct ieee80211_hw *dev, unsigned int prio) -{ - struct rtl8180_priv *priv = dev->priv; - struct rtl8180_tx_ring *ring = &priv->tx_ring[prio]; - - while (skb_queue_len(&ring->queue)) { - struct rtl8180_tx_desc *entry = &ring->desc[ring->idx]; - struct sk_buff *skb; - struct ieee80211_tx_info *info; - u32 flags = le32_to_cpu(entry->flags); - - if (flags & RTL818X_TX_DESC_FLAG_OWN) - return; - - ring->idx = (ring->idx + 1) % ring->entries; - skb = __skb_dequeue(&ring->queue); - pci_unmap_single(priv->pdev, le32_to_cpu(entry->tx_buf), - skb->len, PCI_DMA_TODEVICE); - - info = IEEE80211_SKB_CB(skb); - ieee80211_tx_info_clear_status(info); - - if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && - (flags & RTL818X_TX_DESC_FLAG_TX_OK)) - info->flags |= IEEE80211_TX_STAT_ACK; - - info->status.rates[0].count = (flags & 0xFF) + 1; - info->status.rates[1].idx = -1; - - ieee80211_tx_status_irqsafe(dev, skb); - if (ring->entries - skb_queue_len(&ring->queue) == 2) - ieee80211_wake_queue(dev, prio); - } -} - -static irqreturn_t rtl8180_interrupt(int irq, void *dev_id) -{ - struct ieee80211_hw *dev = dev_id; - struct rtl8180_priv *priv = dev->priv; - u16 reg; - - spin_lock(&priv->lock); - reg = rtl818x_ioread16(priv, &priv->map->INT_STATUS); - if (unlikely(reg == 0xFFFF)) { - spin_unlock(&priv->lock); - return IRQ_HANDLED; - } - - rtl818x_iowrite16(priv, &priv->map->INT_STATUS, reg); - - if (reg & (RTL818X_INT_TXB_OK | RTL818X_INT_TXB_ERR)) - rtl8180_handle_tx(dev, 3); - - if (reg & (RTL818X_INT_TXH_OK | RTL818X_INT_TXH_ERR)) - rtl8180_handle_tx(dev, 2); - - if (reg & (RTL818X_INT_TXN_OK | RTL818X_INT_TXN_ERR)) - rtl8180_handle_tx(dev, 1); - - if (reg & (RTL818X_INT_TXL_OK | RTL818X_INT_TXL_ERR)) - rtl8180_handle_tx(dev, 0); - - if (reg & (RTL818X_INT_RX_OK | RTL818X_INT_RX_ERR)) - rtl8180_handle_rx(dev); - - spin_unlock(&priv->lock); - - return IRQ_HANDLED; -} - -static int rtl8180_tx(struct ieee80211_hw *dev, struct sk_buff *skb) -{ - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct rtl8180_priv *priv = dev->priv; - struct rtl8180_tx_ring *ring; - struct rtl8180_tx_desc *entry; - unsigned long flags; - unsigned int idx, prio; - dma_addr_t mapping; - u32 tx_flags; - u8 rc_flags; - u16 plcp_len = 0; - __le16 rts_duration = 0; - - prio = skb_get_queue_mapping(skb); - ring = &priv->tx_ring[prio]; - - mapping = pci_map_single(priv->pdev, skb->data, - skb->len, PCI_DMA_TODEVICE); - - tx_flags = RTL818X_TX_DESC_FLAG_OWN | RTL818X_TX_DESC_FLAG_FS | - RTL818X_TX_DESC_FLAG_LS | - (ieee80211_get_tx_rate(dev, info)->hw_value << 24) | - skb->len; - - if (priv->r8185) - tx_flags |= RTL818X_TX_DESC_FLAG_DMA | - RTL818X_TX_DESC_FLAG_NO_ENC; - - rc_flags = info->control.rates[0].flags; - if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) { - tx_flags |= RTL818X_TX_DESC_FLAG_RTS; - tx_flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; - } else if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { - tx_flags |= RTL818X_TX_DESC_FLAG_CTS; - tx_flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; - } - - if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) - rts_duration = ieee80211_rts_duration(dev, priv->vif, skb->len, - info); - - if (!priv->r8185) { - unsigned int remainder; - - plcp_len = DIV_ROUND_UP(16 * (skb->len + 4), - (ieee80211_get_tx_rate(dev, info)->bitrate * 2) / 10); - remainder = (16 * (skb->len + 4)) % - ((ieee80211_get_tx_rate(dev, info)->bitrate * 2) / 10); - if (remainder <= 6) - plcp_len |= 1 << 15; - } - - spin_lock_irqsave(&priv->lock, flags); - - if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { - if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) - priv->seqno += 0x10; - hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); - hdr->seq_ctrl |= cpu_to_le16(priv->seqno); - } - - idx = (ring->idx + skb_queue_len(&ring->queue)) % ring->entries; - entry = &ring->desc[idx]; - - entry->rts_duration = rts_duration; - entry->plcp_len = cpu_to_le16(plcp_len); - entry->tx_buf = cpu_to_le32(mapping); - entry->frame_len = cpu_to_le32(skb->len); - entry->flags2 = info->control.rates[1].idx >= 0 ? - ieee80211_get_alt_retry_rate(dev, info, 0)->bitrate << 4 : 0; - entry->retry_limit = info->control.rates[0].count; - entry->flags = cpu_to_le32(tx_flags); - __skb_queue_tail(&ring->queue, skb); - if (ring->entries - skb_queue_len(&ring->queue) < 2) - ieee80211_stop_queue(dev, prio); - - spin_unlock_irqrestore(&priv->lock, flags); - - rtl818x_iowrite8(priv, &priv->map->TX_DMA_POLLING, (1 << (prio + 4))); - - return 0; -} - -void rtl8180_set_anaparam(struct rtl8180_priv *priv, u32 anaparam) -{ - u8 reg; - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); - reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); - rtl818x_iowrite8(priv, &priv->map->CONFIG3, - reg | RTL818X_CONFIG3_ANAPARAM_WRITE); - rtl818x_iowrite32(priv, &priv->map->ANAPARAM, anaparam); - rtl818x_iowrite8(priv, &priv->map->CONFIG3, - reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); -} - -static int rtl8180_init_hw(struct ieee80211_hw *dev) -{ - struct rtl8180_priv *priv = dev->priv; - u16 reg; - - rtl818x_iowrite8(priv, &priv->map->CMD, 0); - rtl818x_ioread8(priv, &priv->map->CMD); - msleep(10); - - /* reset */ - rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0); - rtl818x_ioread8(priv, &priv->map->CMD); - - reg = rtl818x_ioread8(priv, &priv->map->CMD); - reg &= (1 << 1); - reg |= RTL818X_CMD_RESET; - rtl818x_iowrite8(priv, &priv->map->CMD, RTL818X_CMD_RESET); - rtl818x_ioread8(priv, &priv->map->CMD); - msleep(200); - - /* check success of reset */ - if (rtl818x_ioread8(priv, &priv->map->CMD) & RTL818X_CMD_RESET) { - wiphy_err(dev->wiphy, "reset timeout!\n"); - return -ETIMEDOUT; - } - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_LOAD); - rtl818x_ioread8(priv, &priv->map->CMD); - msleep(200); - - if (rtl818x_ioread8(priv, &priv->map->CONFIG3) & (1 << 3)) { - /* For cardbus */ - reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); - reg |= 1 << 1; - rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg); - reg = rtl818x_ioread16(priv, &priv->map->FEMR); - reg |= (1 << 15) | (1 << 14) | (1 << 4); - rtl818x_iowrite16(priv, &priv->map->FEMR, reg); - } - - rtl818x_iowrite8(priv, &priv->map->MSR, 0); - - if (!priv->r8185) - rtl8180_set_anaparam(priv, priv->anaparam); - - rtl818x_iowrite32(priv, &priv->map->RDSAR, priv->rx_ring_dma); - rtl818x_iowrite32(priv, &priv->map->TBDA, priv->tx_ring[3].dma); - rtl818x_iowrite32(priv, &priv->map->THPDA, priv->tx_ring[2].dma); - rtl818x_iowrite32(priv, &priv->map->TNPDA, priv->tx_ring[1].dma); - rtl818x_iowrite32(priv, &priv->map->TLPDA, priv->tx_ring[0].dma); - - /* TODO: necessary? specs indicate not */ - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); - reg = rtl818x_ioread8(priv, &priv->map->CONFIG2); - rtl818x_iowrite8(priv, &priv->map->CONFIG2, reg & ~(1 << 3)); - if (priv->r8185) { - reg = rtl818x_ioread8(priv, &priv->map->CONFIG2); - rtl818x_iowrite8(priv, &priv->map->CONFIG2, reg | (1 << 4)); - } - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); - - /* TODO: set CONFIG5 for calibrating AGC on rtl8180 + philips radio? */ - - /* TODO: turn off hw wep on rtl8180 */ - - rtl818x_iowrite32(priv, &priv->map->INT_TIMEOUT, 0); - - if (priv->r8185) { - rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0); - rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, 0x81); - rtl818x_iowrite8(priv, &priv->map->RESP_RATE, (8 << 4) | 0); - - rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3); - - /* TODO: set ClkRun enable? necessary? */ - reg = rtl818x_ioread8(priv, &priv->map->GP_ENABLE); - rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, reg & ~(1 << 6)); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); - reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); - rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | (1 << 2)); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); - } else { - rtl818x_iowrite16(priv, &priv->map->BRSR, 0x1); - rtl818x_iowrite8(priv, &priv->map->SECURITY, 0); - - rtl818x_iowrite8(priv, &priv->map->PHY_DELAY, 0x6); - rtl818x_iowrite8(priv, &priv->map->CARRIER_SENSE_COUNTER, 0x4C); - } - - priv->rf->init(dev); - if (priv->r8185) - rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3); - return 0; -} - -static int rtl8180_init_rx_ring(struct ieee80211_hw *dev) -{ - struct rtl8180_priv *priv = dev->priv; - struct rtl8180_rx_desc *entry; - int i; - - priv->rx_ring = pci_alloc_consistent(priv->pdev, - sizeof(*priv->rx_ring) * 32, - &priv->rx_ring_dma); - - if (!priv->rx_ring || (unsigned long)priv->rx_ring & 0xFF) { - wiphy_err(dev->wiphy, "Cannot allocate RX ring\n"); - return -ENOMEM; - } - - memset(priv->rx_ring, 0, sizeof(*priv->rx_ring) * 32); - priv->rx_idx = 0; - - for (i = 0; i < 32; i++) { - struct sk_buff *skb = dev_alloc_skb(MAX_RX_SIZE); - dma_addr_t *mapping; - entry = &priv->rx_ring[i]; - if (!skb) - return 0; - - priv->rx_buf[i] = skb; - mapping = (dma_addr_t *)skb->cb; - *mapping = pci_map_single(priv->pdev, skb_tail_pointer(skb), - MAX_RX_SIZE, PCI_DMA_FROMDEVICE); - entry->rx_buf = cpu_to_le32(*mapping); - entry->flags = cpu_to_le32(RTL818X_RX_DESC_FLAG_OWN | - MAX_RX_SIZE); - } - entry->flags |= cpu_to_le32(RTL818X_RX_DESC_FLAG_EOR); - return 0; -} - -static void rtl8180_free_rx_ring(struct ieee80211_hw *dev) -{ - struct rtl8180_priv *priv = dev->priv; - int i; - - for (i = 0; i < 32; i++) { - struct sk_buff *skb = priv->rx_buf[i]; - if (!skb) - continue; - - pci_unmap_single(priv->pdev, - *((dma_addr_t *)skb->cb), - MAX_RX_SIZE, PCI_DMA_FROMDEVICE); - kfree_skb(skb); - } - - pci_free_consistent(priv->pdev, sizeof(*priv->rx_ring) * 32, - priv->rx_ring, priv->rx_ring_dma); - priv->rx_ring = NULL; -} - -static int rtl8180_init_tx_ring(struct ieee80211_hw *dev, - unsigned int prio, unsigned int entries) -{ - struct rtl8180_priv *priv = dev->priv; - struct rtl8180_tx_desc *ring; - dma_addr_t dma; - int i; - - ring = pci_alloc_consistent(priv->pdev, sizeof(*ring) * entries, &dma); - if (!ring || (unsigned long)ring & 0xFF) { - wiphy_err(dev->wiphy, "Cannot allocate TX ring (prio = %d)\n", - prio); - return -ENOMEM; - } - - memset(ring, 0, sizeof(*ring)*entries); - priv->tx_ring[prio].desc = ring; - priv->tx_ring[prio].dma = dma; - priv->tx_ring[prio].idx = 0; - priv->tx_ring[prio].entries = entries; - skb_queue_head_init(&priv->tx_ring[prio].queue); - - for (i = 0; i < entries; i++) - ring[i].next_tx_desc = - cpu_to_le32((u32)dma + ((i + 1) % entries) * sizeof(*ring)); - - return 0; -} - -static void rtl8180_free_tx_ring(struct ieee80211_hw *dev, unsigned int prio) -{ - struct rtl8180_priv *priv = dev->priv; - struct rtl8180_tx_ring *ring = &priv->tx_ring[prio]; - - while (skb_queue_len(&ring->queue)) { - struct rtl8180_tx_desc *entry = &ring->desc[ring->idx]; - struct sk_buff *skb = __skb_dequeue(&ring->queue); - - pci_unmap_single(priv->pdev, le32_to_cpu(entry->tx_buf), - skb->len, PCI_DMA_TODEVICE); - kfree_skb(skb); - ring->idx = (ring->idx + 1) % ring->entries; - } - - pci_free_consistent(priv->pdev, sizeof(*ring->desc)*ring->entries, - ring->desc, ring->dma); - ring->desc = NULL; -} - -static int rtl8180_start(struct ieee80211_hw *dev) -{ - struct rtl8180_priv *priv = dev->priv; - int ret, i; - u32 reg; - - ret = rtl8180_init_rx_ring(dev); - if (ret) - return ret; - - for (i = 0; i < 4; i++) - if ((ret = rtl8180_init_tx_ring(dev, i, 16))) - goto err_free_rings; - - ret = rtl8180_init_hw(dev); - if (ret) - goto err_free_rings; - - rtl818x_iowrite32(priv, &priv->map->RDSAR, priv->rx_ring_dma); - rtl818x_iowrite32(priv, &priv->map->TBDA, priv->tx_ring[3].dma); - rtl818x_iowrite32(priv, &priv->map->THPDA, priv->tx_ring[2].dma); - rtl818x_iowrite32(priv, &priv->map->TNPDA, priv->tx_ring[1].dma); - rtl818x_iowrite32(priv, &priv->map->TLPDA, priv->tx_ring[0].dma); - - ret = request_irq(priv->pdev->irq, rtl8180_interrupt, - IRQF_SHARED, KBUILD_MODNAME, dev); - if (ret) { - wiphy_err(dev->wiphy, "failed to register IRQ handler\n"); - goto err_free_rings; - } - - rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF); - - rtl818x_iowrite32(priv, &priv->map->MAR[0], ~0); - rtl818x_iowrite32(priv, &priv->map->MAR[1], ~0); - - reg = RTL818X_RX_CONF_ONLYERLPKT | - RTL818X_RX_CONF_RX_AUTORESETPHY | - RTL818X_RX_CONF_MGMT | - RTL818X_RX_CONF_DATA | - (7 << 8 /* MAX RX DMA */) | - RTL818X_RX_CONF_BROADCAST | - RTL818X_RX_CONF_NICMAC; - - if (priv->r8185) - reg |= RTL818X_RX_CONF_CSDM1 | RTL818X_RX_CONF_CSDM2; - else { - reg |= (priv->rfparam & RF_PARAM_CARRIERSENSE1) - ? RTL818X_RX_CONF_CSDM1 : 0; - reg |= (priv->rfparam & RF_PARAM_CARRIERSENSE2) - ? RTL818X_RX_CONF_CSDM2 : 0; - } - - priv->rx_conf = reg; - rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg); - - if (priv->r8185) { - reg = rtl818x_ioread8(priv, &priv->map->CW_CONF); - reg &= ~RTL818X_CW_CONF_PERPACKET_CW_SHIFT; - reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT; - rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg); - - reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL); - reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT; - reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT; - reg |= RTL818X_TX_AGC_CTL_FEEDBACK_ANT; - rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg); - - /* disable early TX */ - rtl818x_iowrite8(priv, (u8 __iomem *)priv->map + 0xec, 0x3f); - } - - reg = rtl818x_ioread32(priv, &priv->map->TX_CONF); - reg |= (6 << 21 /* MAX TX DMA */) | - RTL818X_TX_CONF_NO_ICV; - - if (priv->r8185) - reg &= ~RTL818X_TX_CONF_PROBE_DTS; - else - reg &= ~RTL818X_TX_CONF_HW_SEQNUM; - - /* different meaning, same value on both rtl8185 and rtl8180 */ - reg &= ~RTL818X_TX_CONF_SAT_HWPLCP; - - rtl818x_iowrite32(priv, &priv->map->TX_CONF, reg); - - reg = rtl818x_ioread8(priv, &priv->map->CMD); - reg |= RTL818X_CMD_RX_ENABLE; - reg |= RTL818X_CMD_TX_ENABLE; - rtl818x_iowrite8(priv, &priv->map->CMD, reg); - - return 0; - - err_free_rings: - rtl8180_free_rx_ring(dev); - for (i = 0; i < 4; i++) - if (priv->tx_ring[i].desc) - rtl8180_free_tx_ring(dev, i); - - return ret; -} - -static void rtl8180_stop(struct ieee80211_hw *dev) -{ - struct rtl8180_priv *priv = dev->priv; - u8 reg; - int i; - - rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0); - - reg = rtl818x_ioread8(priv, &priv->map->CMD); - reg &= ~RTL818X_CMD_TX_ENABLE; - reg &= ~RTL818X_CMD_RX_ENABLE; - rtl818x_iowrite8(priv, &priv->map->CMD, reg); - - priv->rf->stop(dev); - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); - reg = rtl818x_ioread8(priv, &priv->map->CONFIG4); - rtl818x_iowrite8(priv, &priv->map->CONFIG4, reg | RTL818X_CONFIG4_VCOOFF); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); - - free_irq(priv->pdev->irq, dev); - - rtl8180_free_rx_ring(dev); - for (i = 0; i < 4; i++) - rtl8180_free_tx_ring(dev, i); -} - -static u64 rtl8180_get_tsf(struct ieee80211_hw *dev) -{ - struct rtl8180_priv *priv = dev->priv; - - return rtl818x_ioread32(priv, &priv->map->TSFT[0]) | - (u64)(rtl818x_ioread32(priv, &priv->map->TSFT[1])) << 32; -} - -static void rtl8180_beacon_work(struct work_struct *work) -{ - struct rtl8180_vif *vif_priv = - container_of(work, struct rtl8180_vif, beacon_work.work); - struct ieee80211_vif *vif = - container_of((void *)vif_priv, struct ieee80211_vif, drv_priv); - struct ieee80211_hw *dev = vif_priv->dev; - struct ieee80211_mgmt *mgmt; - struct sk_buff *skb; - int err = 0; - - /* don't overflow the tx ring */ - if (ieee80211_queue_stopped(dev, 0)) - goto resched; - - /* grab a fresh beacon */ - skb = ieee80211_beacon_get(dev, vif); - if (!skb) - goto resched; - - /* - * update beacon timestamp w/ TSF value - * TODO: make hardware update beacon timestamp - */ - mgmt = (struct ieee80211_mgmt *)skb->data; - mgmt->u.beacon.timestamp = cpu_to_le64(rtl8180_get_tsf(dev)); - - /* TODO: use actual beacon queue */ - skb_set_queue_mapping(skb, 0); - - err = rtl8180_tx(dev, skb); - WARN_ON(err); - -resched: - /* - * schedule next beacon - * TODO: use hardware support for beacon timing - */ - schedule_delayed_work(&vif_priv->beacon_work, - usecs_to_jiffies(1024 * vif->bss_conf.beacon_int)); -} - -static int rtl8180_add_interface(struct ieee80211_hw *dev, - struct ieee80211_vif *vif) -{ - struct rtl8180_priv *priv = dev->priv; - struct rtl8180_vif *vif_priv; - - /* - * We only support one active interface at a time. - */ - if (priv->vif) - return -EBUSY; - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_ADHOC: - break; - default: - return -EOPNOTSUPP; - } - - priv->vif = vif; - - /* Initialize driver private area */ - vif_priv = (struct rtl8180_vif *)&vif->drv_priv; - vif_priv->dev = dev; - INIT_DELAYED_WORK(&vif_priv->beacon_work, rtl8180_beacon_work); - vif_priv->enable_beacon = false; - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); - rtl818x_iowrite32(priv, (__le32 __iomem *)&priv->map->MAC[0], - le32_to_cpu(*(__le32 *)vif->addr)); - rtl818x_iowrite16(priv, (__le16 __iomem *)&priv->map->MAC[4], - le16_to_cpu(*(__le16 *)(vif->addr + 4))); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); - - return 0; -} - -static void rtl8180_remove_interface(struct ieee80211_hw *dev, - struct ieee80211_vif *vif) -{ - struct rtl8180_priv *priv = dev->priv; - priv->vif = NULL; -} - -static int rtl8180_config(struct ieee80211_hw *dev, u32 changed) -{ - struct rtl8180_priv *priv = dev->priv; - struct ieee80211_conf *conf = &dev->conf; - - priv->rf->set_chan(dev, conf); - - return 0; -} - -static void rtl8180_bss_info_changed(struct ieee80211_hw *dev, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *info, - u32 changed) -{ - struct rtl8180_priv *priv = dev->priv; - struct rtl8180_vif *vif_priv; - int i; - u8 reg; - - vif_priv = (struct rtl8180_vif *)&vif->drv_priv; - - if (changed & BSS_CHANGED_BSSID) { - for (i = 0; i < ETH_ALEN; i++) - rtl818x_iowrite8(priv, &priv->map->BSSID[i], - info->bssid[i]); - - if (is_valid_ether_addr(info->bssid)) { - if (vif->type == NL80211_IFTYPE_ADHOC) - reg = RTL818X_MSR_ADHOC; - else - reg = RTL818X_MSR_INFRA; - } else - reg = RTL818X_MSR_NO_LINK; - rtl818x_iowrite8(priv, &priv->map->MSR, reg); - } - - if (changed & BSS_CHANGED_ERP_SLOT && priv->rf->conf_erp) - priv->rf->conf_erp(dev, info); - - if (changed & BSS_CHANGED_BEACON_ENABLED) - vif_priv->enable_beacon = info->enable_beacon; - - if (changed & (BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_BEACON)) { - cancel_delayed_work_sync(&vif_priv->beacon_work); - if (vif_priv->enable_beacon) - schedule_work(&vif_priv->beacon_work.work); - } -} - -static u64 rtl8180_prepare_multicast(struct ieee80211_hw *dev, - struct netdev_hw_addr_list *mc_list) -{ - return netdev_hw_addr_list_count(mc_list); -} - -static void rtl8180_configure_filter(struct ieee80211_hw *dev, - unsigned int changed_flags, - unsigned int *total_flags, - u64 multicast) -{ - struct rtl8180_priv *priv = dev->priv; - - if (changed_flags & FIF_FCSFAIL) - priv->rx_conf ^= RTL818X_RX_CONF_FCS; - if (changed_flags & FIF_CONTROL) - priv->rx_conf ^= RTL818X_RX_CONF_CTRL; - if (changed_flags & FIF_OTHER_BSS) - priv->rx_conf ^= RTL818X_RX_CONF_MONITOR; - if (*total_flags & FIF_ALLMULTI || multicast > 0) - priv->rx_conf |= RTL818X_RX_CONF_MULTICAST; - else - priv->rx_conf &= ~RTL818X_RX_CONF_MULTICAST; - - *total_flags = 0; - - if (priv->rx_conf & RTL818X_RX_CONF_FCS) - *total_flags |= FIF_FCSFAIL; - if (priv->rx_conf & RTL818X_RX_CONF_CTRL) - *total_flags |= FIF_CONTROL; - if (priv->rx_conf & RTL818X_RX_CONF_MONITOR) - *total_flags |= FIF_OTHER_BSS; - if (priv->rx_conf & RTL818X_RX_CONF_MULTICAST) - *total_flags |= FIF_ALLMULTI; - - rtl818x_iowrite32(priv, &priv->map->RX_CONF, priv->rx_conf); -} - -static const struct ieee80211_ops rtl8180_ops = { - .tx = rtl8180_tx, - .start = rtl8180_start, - .stop = rtl8180_stop, - .add_interface = rtl8180_add_interface, - .remove_interface = rtl8180_remove_interface, - .config = rtl8180_config, - .bss_info_changed = rtl8180_bss_info_changed, - .prepare_multicast = rtl8180_prepare_multicast, - .configure_filter = rtl8180_configure_filter, - .get_tsf = rtl8180_get_tsf, -}; - -static void rtl8180_eeprom_register_read(struct eeprom_93cx6 *eeprom) -{ - struct ieee80211_hw *dev = eeprom->data; - struct rtl8180_priv *priv = dev->priv; - u8 reg = rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - - eeprom->reg_data_in = reg & RTL818X_EEPROM_CMD_WRITE; - eeprom->reg_data_out = reg & RTL818X_EEPROM_CMD_READ; - eeprom->reg_data_clock = reg & RTL818X_EEPROM_CMD_CK; - eeprom->reg_chip_select = reg & RTL818X_EEPROM_CMD_CS; -} - -static void rtl8180_eeprom_register_write(struct eeprom_93cx6 *eeprom) -{ - struct ieee80211_hw *dev = eeprom->data; - struct rtl8180_priv *priv = dev->priv; - u8 reg = 2 << 6; - - if (eeprom->reg_data_in) - reg |= RTL818X_EEPROM_CMD_WRITE; - if (eeprom->reg_data_out) - reg |= RTL818X_EEPROM_CMD_READ; - if (eeprom->reg_data_clock) - reg |= RTL818X_EEPROM_CMD_CK; - if (eeprom->reg_chip_select) - reg |= RTL818X_EEPROM_CMD_CS; - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, reg); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(10); -} - -static int __devinit rtl8180_probe(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - struct ieee80211_hw *dev; - struct rtl8180_priv *priv; - unsigned long mem_addr, mem_len; - unsigned int io_addr, io_len; - int err, i; - struct eeprom_93cx6 eeprom; - const char *chip_name, *rf_name = NULL; - u32 reg; - u16 eeprom_val; - u8 mac_addr[ETH_ALEN]; - - err = pci_enable_device(pdev); - if (err) { - printk(KERN_ERR "%s (rtl8180): Cannot enable new PCI device\n", - pci_name(pdev)); - return err; - } - - err = pci_request_regions(pdev, KBUILD_MODNAME); - if (err) { - printk(KERN_ERR "%s (rtl8180): Cannot obtain PCI resources\n", - pci_name(pdev)); - return err; - } - - io_addr = pci_resource_start(pdev, 0); - io_len = pci_resource_len(pdev, 0); - mem_addr = pci_resource_start(pdev, 1); - mem_len = pci_resource_len(pdev, 1); - - if (mem_len < sizeof(struct rtl818x_csr) || - io_len < sizeof(struct rtl818x_csr)) { - printk(KERN_ERR "%s (rtl8180): Too short PCI resources\n", - pci_name(pdev)); - err = -ENOMEM; - goto err_free_reg; - } - - if ((err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) || - (err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)))) { - printk(KERN_ERR "%s (rtl8180): No suitable DMA available\n", - pci_name(pdev)); - goto err_free_reg; - } - - pci_set_master(pdev); - - dev = ieee80211_alloc_hw(sizeof(*priv), &rtl8180_ops); - if (!dev) { - printk(KERN_ERR "%s (rtl8180): ieee80211 alloc failed\n", - pci_name(pdev)); - err = -ENOMEM; - goto err_free_reg; - } - - priv = dev->priv; - priv->pdev = pdev; - - dev->max_rates = 2; - SET_IEEE80211_DEV(dev, &pdev->dev); - pci_set_drvdata(pdev, dev); - - priv->map = pci_iomap(pdev, 1, mem_len); - if (!priv->map) - priv->map = pci_iomap(pdev, 0, io_len); - - if (!priv->map) { - printk(KERN_ERR "%s (rtl8180): Cannot map device memory\n", - pci_name(pdev)); - goto err_free_dev; - } - - BUILD_BUG_ON(sizeof(priv->channels) != sizeof(rtl818x_channels)); - BUILD_BUG_ON(sizeof(priv->rates) != sizeof(rtl818x_rates)); - - memcpy(priv->channels, rtl818x_channels, sizeof(rtl818x_channels)); - memcpy(priv->rates, rtl818x_rates, sizeof(rtl818x_rates)); - - priv->band.band = IEEE80211_BAND_2GHZ; - priv->band.channels = priv->channels; - priv->band.n_channels = ARRAY_SIZE(rtl818x_channels); - priv->band.bitrates = priv->rates; - priv->band.n_bitrates = 4; - dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; - - dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | - IEEE80211_HW_RX_INCLUDES_FCS | - IEEE80211_HW_SIGNAL_UNSPEC; - dev->vif_data_size = sizeof(struct rtl8180_vif); - dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC); - dev->queues = 1; - dev->max_signal = 65; - - reg = rtl818x_ioread32(priv, &priv->map->TX_CONF); - reg &= RTL818X_TX_CONF_HWVER_MASK; - switch (reg) { - case RTL818X_TX_CONF_R8180_ABCD: - chip_name = "RTL8180"; - break; - case RTL818X_TX_CONF_R8180_F: - chip_name = "RTL8180vF"; - break; - case RTL818X_TX_CONF_R8185_ABC: - chip_name = "RTL8185"; - break; - case RTL818X_TX_CONF_R8185_D: - chip_name = "RTL8185vD"; - break; - default: - printk(KERN_ERR "%s (rtl8180): Unknown chip! (0x%x)\n", - pci_name(pdev), reg >> 25); - goto err_iounmap; - } - - priv->r8185 = reg & RTL818X_TX_CONF_R8185_ABC; - if (priv->r8185) { - priv->band.n_bitrates = ARRAY_SIZE(rtl818x_rates); - pci_try_set_mwi(pdev); - } - - eeprom.data = dev; - eeprom.register_read = rtl8180_eeprom_register_read; - eeprom.register_write = rtl8180_eeprom_register_write; - if (rtl818x_ioread32(priv, &priv->map->RX_CONF) & (1 << 6)) - eeprom.width = PCI_EEPROM_WIDTH_93C66; - else - eeprom.width = PCI_EEPROM_WIDTH_93C46; - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_PROGRAM); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(10); - - eeprom_93cx6_read(&eeprom, 0x06, &eeprom_val); - eeprom_val &= 0xFF; - switch (eeprom_val) { - case 1: rf_name = "Intersil"; - break; - case 2: rf_name = "RFMD"; - break; - case 3: priv->rf = &sa2400_rf_ops; - break; - case 4: priv->rf = &max2820_rf_ops; - break; - case 5: priv->rf = &grf5101_rf_ops; - break; - case 9: priv->rf = rtl8180_detect_rf(dev); - break; - case 10: - rf_name = "RTL8255"; - break; - default: - printk(KERN_ERR "%s (rtl8180): Unknown RF! (0x%x)\n", - pci_name(pdev), eeprom_val); - goto err_iounmap; - } - - if (!priv->rf) { - printk(KERN_ERR "%s (rtl8180): %s RF frontend not supported!\n", - pci_name(pdev), rf_name); - goto err_iounmap; - } - - eeprom_93cx6_read(&eeprom, 0x17, &eeprom_val); - priv->csthreshold = eeprom_val >> 8; - if (!priv->r8185) { - __le32 anaparam; - eeprom_93cx6_multiread(&eeprom, 0xD, (__le16 *)&anaparam, 2); - priv->anaparam = le32_to_cpu(anaparam); - eeprom_93cx6_read(&eeprom, 0x19, &priv->rfparam); - } - - eeprom_93cx6_multiread(&eeprom, 0x7, (__le16 *)mac_addr, 3); - if (!is_valid_ether_addr(mac_addr)) { - printk(KERN_WARNING "%s (rtl8180): Invalid hwaddr! Using" - " randomly generated MAC addr\n", pci_name(pdev)); - random_ether_addr(mac_addr); - } - SET_IEEE80211_PERM_ADDR(dev, mac_addr); - - /* CCK TX power */ - for (i = 0; i < 14; i += 2) { - u16 txpwr; - eeprom_93cx6_read(&eeprom, 0x10 + (i >> 1), &txpwr); - priv->channels[i].hw_value = txpwr & 0xFF; - priv->channels[i + 1].hw_value = txpwr >> 8; - } - - /* OFDM TX power */ - if (priv->r8185) { - for (i = 0; i < 14; i += 2) { - u16 txpwr; - eeprom_93cx6_read(&eeprom, 0x20 + (i >> 1), &txpwr); - priv->channels[i].hw_value |= (txpwr & 0xFF) << 8; - priv->channels[i + 1].hw_value |= txpwr & 0xFF00; - } - } - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); - - spin_lock_init(&priv->lock); - - err = ieee80211_register_hw(dev); - if (err) { - printk(KERN_ERR "%s (rtl8180): Cannot register device\n", - pci_name(pdev)); - goto err_iounmap; - } - - wiphy_info(dev->wiphy, "hwaddr %pm, %s + %s\n", - mac_addr, chip_name, priv->rf->name); - - return 0; - - err_iounmap: - iounmap(priv->map); - - err_free_dev: - pci_set_drvdata(pdev, NULL); - ieee80211_free_hw(dev); - - err_free_reg: - pci_release_regions(pdev); - pci_disable_device(pdev); - return err; -} - -static void __devexit rtl8180_remove(struct pci_dev *pdev) -{ - struct ieee80211_hw *dev = pci_get_drvdata(pdev); - struct rtl8180_priv *priv; - - if (!dev) - return; - - ieee80211_unregister_hw(dev); - - priv = dev->priv; - - pci_iounmap(pdev, priv->map); - pci_release_regions(pdev); - pci_disable_device(pdev); - ieee80211_free_hw(dev); -} - -#ifdef CONFIG_PM -static int rtl8180_suspend(struct pci_dev *pdev, pm_message_t state) -{ - pci_save_state(pdev); - pci_set_power_state(pdev, pci_choose_state(pdev, state)); - return 0; -} - -static int rtl8180_resume(struct pci_dev *pdev) -{ - pci_set_power_state(pdev, PCI_D0); - pci_restore_state(pdev); - return 0; -} - -#endif /* CONFIG_PM */ - -static struct pci_driver rtl8180_driver = { - .name = KBUILD_MODNAME, - .id_table = rtl8180_table, - .probe = rtl8180_probe, - .remove = __devexit_p(rtl8180_remove), -#ifdef CONFIG_PM - .suspend = rtl8180_suspend, - .resume = rtl8180_resume, -#endif /* CONFIG_PM */ -}; - -static int __init rtl8180_init(void) -{ - return pci_register_driver(&rtl8180_driver); -} - -static void __exit rtl8180_exit(void) -{ - pci_unregister_driver(&rtl8180_driver); -} - -module_init(rtl8180_init); -module_exit(rtl8180_exit); diff --git a/drivers/net/wireless/rtl818x/rtl8180_grf5101.c b/drivers/net/wireless/rtl818x/rtl8180_grf5101.c deleted file mode 100644 index 5cab9dfa8c07..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8180_grf5101.c +++ /dev/null @@ -1,190 +0,0 @@ - -/* - * Radio tuning for GCT GRF5101 on RTL8180 - * - * Copyright 2007 Andrea Merello - * - * Code from the BSD driver and the rtl8181 project have been - * very useful to understand certain things - * - * I want to thanks the Authors of such projects and the Ndiswrapper - * project Authors. - * - * A special Big Thanks also is for all people who donated me cards, - * making possible the creation of the original rtl8180 driver - * from which this code is derived! - * - * 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 -#include -#include -#include - -#include "rtl8180.h" -#include "rtl8180_grf5101.h" - -static const int grf5101_encode[] = { - 0x0, 0x8, 0x4, 0xC, - 0x2, 0xA, 0x6, 0xE, - 0x1, 0x9, 0x5, 0xD, - 0x3, 0xB, 0x7, 0xF -}; - -static void write_grf5101(struct ieee80211_hw *dev, u8 addr, u32 data) -{ - struct rtl8180_priv *priv = dev->priv; - u32 phy_config; - - phy_config = grf5101_encode[(data >> 8) & 0xF]; - phy_config |= grf5101_encode[(data >> 4) & 0xF] << 4; - phy_config |= grf5101_encode[data & 0xF] << 8; - phy_config |= grf5101_encode[(addr >> 1) & 0xF] << 12; - phy_config |= (addr & 1) << 16; - phy_config |= grf5101_encode[(data & 0xf000) >> 12] << 24; - - /* MAC will bang bits to the chip */ - phy_config |= 0x90000000; - - rtl818x_iowrite32(priv, - (__le32 __iomem *) &priv->map->RFPinsOutput, phy_config); - - msleep(3); -} - -static void grf5101_write_phy_antenna(struct ieee80211_hw *dev, short chan) -{ - struct rtl8180_priv *priv = dev->priv; - u8 ant = GRF5101_ANTENNA; - - if (priv->rfparam & RF_PARAM_ANTBDEFAULT) - ant |= BB_ANTENNA_B; - - if (chan == 14) - ant |= BB_ANTATTEN_CHAN14; - - rtl8180_write_phy(dev, 0x10, ant); -} - -static u8 grf5101_rf_calc_rssi(u8 agc, u8 sq) -{ - if (agc > 60) - return 65; - - /* TODO(?): just return agc (or agc + 5) to avoid mult / div */ - return 65 * agc / 60; -} - -static void grf5101_rf_set_channel(struct ieee80211_hw *dev, - struct ieee80211_conf *conf) -{ - struct rtl8180_priv *priv = dev->priv; - int channel = ieee80211_frequency_to_channel(conf->channel->center_freq); - u32 txpw = priv->channels[channel - 1].hw_value & 0xFF; - u32 chan = channel - 1; - - /* set TX power */ - write_grf5101(dev, 0x15, 0x0); - write_grf5101(dev, 0x06, txpw); - write_grf5101(dev, 0x15, 0x10); - write_grf5101(dev, 0x15, 0x0); - - /* set frequency */ - write_grf5101(dev, 0x07, 0x0); - write_grf5101(dev, 0x0B, chan); - write_grf5101(dev, 0x07, 0x1000); - - grf5101_write_phy_antenna(dev, channel); -} - -static void grf5101_rf_stop(struct ieee80211_hw *dev) -{ - struct rtl8180_priv *priv = dev->priv; - u32 anaparam; - - anaparam = priv->anaparam; - anaparam &= 0x000fffff; - anaparam |= 0x3f900000; - rtl8180_set_anaparam(priv, anaparam); - - write_grf5101(dev, 0x07, 0x0); - write_grf5101(dev, 0x1f, 0x45); - write_grf5101(dev, 0x1f, 0x5); - write_grf5101(dev, 0x00, 0x8e4); -} - -static void grf5101_rf_init(struct ieee80211_hw *dev) -{ - struct rtl8180_priv *priv = dev->priv; - - rtl8180_set_anaparam(priv, priv->anaparam); - - write_grf5101(dev, 0x1f, 0x0); - write_grf5101(dev, 0x1f, 0x0); - write_grf5101(dev, 0x1f, 0x40); - write_grf5101(dev, 0x1f, 0x60); - write_grf5101(dev, 0x1f, 0x61); - write_grf5101(dev, 0x1f, 0x61); - write_grf5101(dev, 0x00, 0xae4); - write_grf5101(dev, 0x1f, 0x1); - write_grf5101(dev, 0x1f, 0x41); - write_grf5101(dev, 0x1f, 0x61); - - write_grf5101(dev, 0x01, 0x1a23); - write_grf5101(dev, 0x02, 0x4971); - write_grf5101(dev, 0x03, 0x41de); - write_grf5101(dev, 0x04, 0x2d80); - write_grf5101(dev, 0x05, 0x68ff); /* 0x61ff original value */ - write_grf5101(dev, 0x06, 0x0); - write_grf5101(dev, 0x07, 0x0); - write_grf5101(dev, 0x08, 0x7533); - write_grf5101(dev, 0x09, 0xc401); - write_grf5101(dev, 0x0a, 0x0); - write_grf5101(dev, 0x0c, 0x1c7); - write_grf5101(dev, 0x0d, 0x29d3); - write_grf5101(dev, 0x0e, 0x2e8); - write_grf5101(dev, 0x10, 0x192); - write_grf5101(dev, 0x11, 0x248); - write_grf5101(dev, 0x12, 0x0); - write_grf5101(dev, 0x13, 0x20c4); - write_grf5101(dev, 0x14, 0xf4fc); - write_grf5101(dev, 0x15, 0x0); - write_grf5101(dev, 0x16, 0x1500); - - write_grf5101(dev, 0x07, 0x1000); - - /* baseband configuration */ - rtl8180_write_phy(dev, 0, 0xa8); - rtl8180_write_phy(dev, 3, 0x0); - rtl8180_write_phy(dev, 4, 0xc0); - rtl8180_write_phy(dev, 5, 0x90); - rtl8180_write_phy(dev, 6, 0x1e); - rtl8180_write_phy(dev, 7, 0x64); - - grf5101_write_phy_antenna(dev, 1); - - rtl8180_write_phy(dev, 0x11, 0x88); - - if (rtl818x_ioread8(priv, &priv->map->CONFIG2) & - RTL818X_CONFIG2_ANTENNA_DIV) - rtl8180_write_phy(dev, 0x12, 0xc0); /* enable ant diversity */ - else - rtl8180_write_phy(dev, 0x12, 0x40); /* disable ant diversity */ - - rtl8180_write_phy(dev, 0x13, 0x90 | priv->csthreshold); - - rtl8180_write_phy(dev, 0x19, 0x0); - rtl8180_write_phy(dev, 0x1a, 0xa0); - rtl8180_write_phy(dev, 0x1b, 0x44); -} - -const struct rtl818x_rf_ops grf5101_rf_ops = { - .name = "GCT", - .init = grf5101_rf_init, - .stop = grf5101_rf_stop, - .set_chan = grf5101_rf_set_channel, - .calc_rssi = grf5101_rf_calc_rssi, -}; diff --git a/drivers/net/wireless/rtl818x/rtl8180_grf5101.h b/drivers/net/wireless/rtl818x/rtl8180_grf5101.h deleted file mode 100644 index 76647111bcff..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8180_grf5101.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef RTL8180_GRF5101_H -#define RTL8180_GRF5101_H - -/* - * Radio tuning for GCT GRF5101 on RTL8180 - * - * Copyright 2007 Andrea Merello - * - * Code from the BSD driver and the rtl8181 project have been - * very useful to understand certain things - * - * I want to thanks the Authors of such projects and the Ndiswrapper - * project Authors. - * - * A special Big Thanks also is for all people who donated me cards, - * making possible the creation of the original rtl8180 driver - * from which this code is derived! - * - * 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. - */ - -#define GRF5101_ANTENNA 0xA3 - -extern const struct rtl818x_rf_ops grf5101_rf_ops; - -#endif /* RTL8180_GRF5101_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8180_max2820.c b/drivers/net/wireless/rtl818x/rtl8180_max2820.c deleted file mode 100644 index 16c4655181c0..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8180_max2820.c +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Radio tuning for Maxim max2820 on RTL8180 - * - * Copyright 2007 Andrea Merello - * - * Code from the BSD driver and the rtl8181 project have been - * very useful to understand certain things - * - * I want to thanks the Authors of such projects and the Ndiswrapper - * project Authors. - * - * A special Big Thanks also is for all people who donated me cards, - * making possible the creation of the original rtl8180 driver - * from which this code is derived! - * - * 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 -#include -#include -#include - -#include "rtl8180.h" -#include "rtl8180_max2820.h" - -static const u32 max2820_chan[] = { - 12, /* CH 1 */ - 17, - 22, - 27, - 32, - 37, - 42, - 47, - 52, - 57, - 62, - 67, - 72, - 84, /* CH 14 */ -}; - -static void write_max2820(struct ieee80211_hw *dev, u8 addr, u32 data) -{ - struct rtl8180_priv *priv = dev->priv; - u32 phy_config; - - phy_config = 0x90 + (data & 0xf); - phy_config <<= 16; - phy_config += addr; - phy_config <<= 8; - phy_config += (data >> 4) & 0xff; - - rtl818x_iowrite32(priv, - (__le32 __iomem *) &priv->map->RFPinsOutput, phy_config); - - msleep(1); -} - -static void max2820_write_phy_antenna(struct ieee80211_hw *dev, short chan) -{ - struct rtl8180_priv *priv = dev->priv; - u8 ant; - - ant = MAXIM_ANTENNA; - if (priv->rfparam & RF_PARAM_ANTBDEFAULT) - ant |= BB_ANTENNA_B; - if (chan == 14) - ant |= BB_ANTATTEN_CHAN14; - - rtl8180_write_phy(dev, 0x10, ant); -} - -static u8 max2820_rf_calc_rssi(u8 agc, u8 sq) -{ - bool odd; - - odd = !!(agc & 1); - - agc >>= 1; - if (odd) - agc += 76; - else - agc += 66; - - /* TODO: change addends above to avoid mult / div below */ - return 65 * agc / 100; -} - -static void max2820_rf_set_channel(struct ieee80211_hw *dev, - struct ieee80211_conf *conf) -{ - struct rtl8180_priv *priv = dev->priv; - int channel = conf ? - ieee80211_frequency_to_channel(conf->channel->center_freq) : 1; - unsigned int chan_idx = channel - 1; - u32 txpw = priv->channels[chan_idx].hw_value & 0xFF; - u32 chan = max2820_chan[chan_idx]; - - /* While philips SA2400 drive the PA bias from - * sa2400, for MAXIM we do this directly from BB */ - rtl8180_write_phy(dev, 3, txpw); - - max2820_write_phy_antenna(dev, channel); - write_max2820(dev, 3, chan); -} - -static void max2820_rf_stop(struct ieee80211_hw *dev) -{ - rtl8180_write_phy(dev, 3, 0x8); - write_max2820(dev, 1, 0); -} - - -static void max2820_rf_init(struct ieee80211_hw *dev) -{ - struct rtl8180_priv *priv = dev->priv; - - /* MAXIM from netbsd driver */ - write_max2820(dev, 0, 0x007); /* test mode as indicated in datasheet */ - write_max2820(dev, 1, 0x01e); /* enable register */ - write_max2820(dev, 2, 0x001); /* synt register */ - - max2820_rf_set_channel(dev, NULL); - - write_max2820(dev, 4, 0x313); /* rx register */ - - /* PA is driven directly by the BB, we keep the MAXIM bias - * at the highest value in case that setting it to lower - * values may introduce some further attenuation somewhere.. - */ - write_max2820(dev, 5, 0x00f); - - /* baseband configuration */ - rtl8180_write_phy(dev, 0, 0x88); /* sys1 */ - rtl8180_write_phy(dev, 3, 0x08); /* txagc */ - rtl8180_write_phy(dev, 4, 0xf8); /* lnadet */ - rtl8180_write_phy(dev, 5, 0x90); /* ifagcinit */ - rtl8180_write_phy(dev, 6, 0x1a); /* ifagclimit */ - rtl8180_write_phy(dev, 7, 0x64); /* ifagcdet */ - - max2820_write_phy_antenna(dev, 1); - - rtl8180_write_phy(dev, 0x11, 0x88); /* trl */ - - if (rtl818x_ioread8(priv, &priv->map->CONFIG2) & - RTL818X_CONFIG2_ANTENNA_DIV) - rtl8180_write_phy(dev, 0x12, 0xc7); - else - rtl8180_write_phy(dev, 0x12, 0x47); - - rtl8180_write_phy(dev, 0x13, 0x9b); - - rtl8180_write_phy(dev, 0x19, 0x0); /* CHESTLIM */ - rtl8180_write_phy(dev, 0x1a, 0x9f); /* CHSQLIM */ - - max2820_rf_set_channel(dev, NULL); -} - -const struct rtl818x_rf_ops max2820_rf_ops = { - .name = "Maxim", - .init = max2820_rf_init, - .stop = max2820_rf_stop, - .set_chan = max2820_rf_set_channel, - .calc_rssi = max2820_rf_calc_rssi, -}; diff --git a/drivers/net/wireless/rtl818x/rtl8180_max2820.h b/drivers/net/wireless/rtl818x/rtl8180_max2820.h deleted file mode 100644 index 61cf6d1e7d57..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8180_max2820.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef RTL8180_MAX2820_H -#define RTL8180_MAX2820_H - -/* - * Radio tuning for Maxim max2820 on RTL8180 - * - * Copyright 2007 Andrea Merello - * - * Code from the BSD driver and the rtl8181 project have been - * very useful to understand certain things - * - * I want to thanks the Authors of such projects and the Ndiswrapper - * project Authors. - * - * A special Big Thanks also is for all people who donated me cards, - * making possible the creation of the original rtl8180 driver - * from which this code is derived! - * - * 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. - */ - -#define MAXIM_ANTENNA 0xb3 - -extern const struct rtl818x_rf_ops max2820_rf_ops; - -#endif /* RTL8180_MAX2820_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8180_rtl8225.c b/drivers/net/wireless/rtl818x/rtl8180_rtl8225.c deleted file mode 100644 index 69e4d4745dae..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8180_rtl8225.c +++ /dev/null @@ -1,791 +0,0 @@ - -/* - * Radio tuning for RTL8225 on RTL8180 - * - * Copyright 2007 Michael Wu - * Copyright 2007 Andrea Merello - * - * Based on the r8180 driver, which is: - * Copyright 2005 Andrea Merello , et al. - * - * Thanks to Realtek for their support! - * - * 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 -#include -#include -#include - -#include "rtl8180.h" -#include "rtl8180_rtl8225.h" - -static void rtl8225_write(struct ieee80211_hw *dev, u8 addr, u16 data) -{ - struct rtl8180_priv *priv = dev->priv; - u16 reg80, reg84, reg82; - u32 bangdata; - int i; - - bangdata = (data << 4) | (addr & 0xf); - - reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput) & 0xfff3; - reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable); - - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x7); - - reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x7 | 0x400); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(10); - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(2); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(10); - - for (i = 15; i >= 0; i--) { - u16 reg = reg80; - - if (bangdata & (1 << i)) - reg |= 1; - - if (i & 1) - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1)); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1)); - - if (!(i & 1)) - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); - } - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(10); - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x400); - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); -} - -static u16 rtl8225_read(struct ieee80211_hw *dev, u8 addr) -{ - struct rtl8180_priv *priv = dev->priv; - u16 reg80, reg82, reg84, out; - int i; - - reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput); - reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable); - reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect) | 0x400; - - reg80 &= ~0xF; - - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x000F); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x000F); - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(4); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(5); - - for (i = 4; i >= 0; i--) { - u16 reg = reg80 | ((addr >> i) & 1); - - if (!(i & 1)) { - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(1); - } - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg | (1 << 1)); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(2); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg | (1 << 1)); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(2); - - if (i & 1) { - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(1); - } - } - - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x000E); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x040E); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3) | (1 << 1)); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(2); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3)); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(2); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3)); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(2); - - out = 0; - for (i = 11; i >= 0; i--) { - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3)); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(1); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3) | (1 << 1)); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(2); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3) | (1 << 1)); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(2); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3) | (1 << 1)); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(2); - - if (rtl818x_ioread16(priv, &priv->map->RFPinsInput) & (1 << 1)) - out |= 1 << i; - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3)); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(2); - } - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3) | (1 << 2)); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - udelay(2); - - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x03A0); - - return out; -} - -static const u16 rtl8225bcd_rxgain[] = { - 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409, - 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541, - 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583, - 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644, - 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688, - 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745, - 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789, - 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793, - 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d, - 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9, - 0x07aa, 0x07ab, 0x07ac, 0x07ad, 0x07b0, 0x07b1, 0x07b2, 0x07b3, - 0x07b4, 0x07b5, 0x07b8, 0x07b9, 0x07ba, 0x07bb, 0x07bb -}; - -static const u8 rtl8225_agc[] = { - 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, - 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, 0x97, 0x96, - 0x95, 0x94, 0x93, 0x92, 0x91, 0x90, 0x8f, 0x8e, - 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86, - 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, 0x3f, 0x3e, - 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, - 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2f, 0x2e, - 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, - 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e, - 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, - 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, - 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, - 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 -}; - -static const u8 rtl8225_gain[] = { - 0x23, 0x88, 0x7c, 0xa5, /* -82dbm */ - 0x23, 0x88, 0x7c, 0xb5, /* -82dbm */ - 0x23, 0x88, 0x7c, 0xc5, /* -82dbm */ - 0x33, 0x80, 0x79, 0xc5, /* -78dbm */ - 0x43, 0x78, 0x76, 0xc5, /* -74dbm */ - 0x53, 0x60, 0x73, 0xc5, /* -70dbm */ - 0x63, 0x58, 0x70, 0xc5, /* -66dbm */ -}; - -static const u8 rtl8225_threshold[] = { - 0x8d, 0x8d, 0x8d, 0x8d, 0x9d, 0xad, 0xbd -}; - -static const u8 rtl8225_tx_gain_cck_ofdm[] = { - 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e -}; - -static const u8 rtl8225_tx_power_cck[] = { - 0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02, - 0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02, - 0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02, - 0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02, - 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03, - 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03 -}; - -static const u8 rtl8225_tx_power_cck_ch14[] = { - 0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00, - 0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00, - 0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00, - 0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00, - 0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00 -}; - -static const u8 rtl8225_tx_power_ofdm[] = { - 0x80, 0x90, 0xa2, 0xb5, 0xcb, 0xe4 -}; - -static const u32 rtl8225_chan[] = { - 0x085c, 0x08dc, 0x095c, 0x09dc, 0x0a5c, 0x0adc, 0x0b5c, - 0x0bdc, 0x0c5c, 0x0cdc, 0x0d5c, 0x0ddc, 0x0e5c, 0x0f72 -}; - -static void rtl8225_rf_set_tx_power(struct ieee80211_hw *dev, int channel) -{ - struct rtl8180_priv *priv = dev->priv; - u8 cck_power, ofdm_power; - const u8 *tmp; - u32 reg; - int i; - - cck_power = priv->channels[channel - 1].hw_value & 0xFF; - ofdm_power = priv->channels[channel - 1].hw_value >> 8; - - cck_power = min(cck_power, (u8)35); - ofdm_power = min(ofdm_power, (u8)35); - - rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, - rtl8225_tx_gain_cck_ofdm[cck_power / 6] >> 1); - - if (channel == 14) - tmp = &rtl8225_tx_power_cck_ch14[(cck_power % 6) * 8]; - else - tmp = &rtl8225_tx_power_cck[(cck_power % 6) * 8]; - - for (i = 0; i < 8; i++) - rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++); - - msleep(1); /* FIXME: optional? */ - - /* anaparam2 on */ - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); - reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); - rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE); - rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, RTL8225_ANAPARAM2_ON); - rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); - - rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, - rtl8225_tx_gain_cck_ofdm[ofdm_power/6] >> 1); - - tmp = &rtl8225_tx_power_ofdm[ofdm_power % 6]; - - rtl8225_write_phy_ofdm(dev, 5, *tmp); - rtl8225_write_phy_ofdm(dev, 7, *tmp); - - msleep(1); -} - -static void rtl8225_rf_init(struct ieee80211_hw *dev) -{ - struct rtl8180_priv *priv = dev->priv; - int i; - - rtl8180_set_anaparam(priv, RTL8225_ANAPARAM_ON); - - /* host_pci_init */ - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480); - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x0488); - rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - msleep(200); /* FIXME: ehh?? */ - rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0xFF & ~(1 << 6)); - - rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x000a8008); - - /* TODO: check if we need really to change BRSR to do RF config */ - rtl818x_ioread16(priv, &priv->map->BRSR); - rtl818x_iowrite16(priv, &priv->map->BRSR, 0xFFFF); - rtl818x_iowrite32(priv, &priv->map->RF_PARA, 0x00100044); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); - rtl818x_iowrite8(priv, &priv->map->CONFIG3, 0x44); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); - - rtl8225_write(dev, 0x0, 0x067); - rtl8225_write(dev, 0x1, 0xFE0); - rtl8225_write(dev, 0x2, 0x44D); - rtl8225_write(dev, 0x3, 0x441); - rtl8225_write(dev, 0x4, 0x8BE); - rtl8225_write(dev, 0x5, 0xBF0); /* TODO: minipci */ - rtl8225_write(dev, 0x6, 0xAE6); - rtl8225_write(dev, 0x7, rtl8225_chan[0]); - rtl8225_write(dev, 0x8, 0x01F); - rtl8225_write(dev, 0x9, 0x334); - rtl8225_write(dev, 0xA, 0xFD4); - rtl8225_write(dev, 0xB, 0x391); - rtl8225_write(dev, 0xC, 0x050); - rtl8225_write(dev, 0xD, 0x6DB); - rtl8225_write(dev, 0xE, 0x029); - rtl8225_write(dev, 0xF, 0x914); msleep(1); - - rtl8225_write(dev, 0x2, 0xC4D); msleep(100); - - rtl8225_write(dev, 0x0, 0x127); - - for (i = 0; i < ARRAY_SIZE(rtl8225bcd_rxgain); i++) { - rtl8225_write(dev, 0x1, i + 1); - rtl8225_write(dev, 0x2, rtl8225bcd_rxgain[i]); - } - - rtl8225_write(dev, 0x0, 0x027); - rtl8225_write(dev, 0x0, 0x22F); - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); - - for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) { - rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]); - msleep(1); - rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i); - msleep(1); - } - - msleep(1); - - rtl8225_write_phy_ofdm(dev, 0x00, 0x01); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x01, 0x02); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x02, 0x62); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x03, 0x00); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x04, 0x00); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x05, 0x00); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x06, 0x00); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x07, 0x00); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x08, 0x00); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x0a, 0x09); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x10, 0x84); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x11, 0x03); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x12, 0x20); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x13, 0x20); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x14, 0x00); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x15, 0x40); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x16, 0x00); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x17, 0x40); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x18, 0xef); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x19, 0x19); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x1b, 0x76); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x1e, 0x95); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x21, 0x27); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x22, 0x16); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x24, 0x46); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x25, 0x20); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x27, 0x88); msleep(1); - - rtl8225_write_phy_cck(dev, 0x00, 0x98); msleep(1); - rtl8225_write_phy_cck(dev, 0x03, 0x20); msleep(1); - rtl8225_write_phy_cck(dev, 0x04, 0x7e); msleep(1); - rtl8225_write_phy_cck(dev, 0x05, 0x12); msleep(1); - rtl8225_write_phy_cck(dev, 0x06, 0xfc); msleep(1); - rtl8225_write_phy_cck(dev, 0x07, 0x78); msleep(1); - rtl8225_write_phy_cck(dev, 0x08, 0x2e); msleep(1); - rtl8225_write_phy_cck(dev, 0x10, 0x93); msleep(1); - rtl8225_write_phy_cck(dev, 0x11, 0x88); msleep(1); - rtl8225_write_phy_cck(dev, 0x12, 0x47); msleep(1); - rtl8225_write_phy_cck(dev, 0x13, 0xd0); - rtl8225_write_phy_cck(dev, 0x19, 0x00); - rtl8225_write_phy_cck(dev, 0x1a, 0xa0); - rtl8225_write_phy_cck(dev, 0x1b, 0x08); - rtl8225_write_phy_cck(dev, 0x40, 0x86); - rtl8225_write_phy_cck(dev, 0x41, 0x8d); msleep(1); - rtl8225_write_phy_cck(dev, 0x42, 0x15); msleep(1); - rtl8225_write_phy_cck(dev, 0x43, 0x18); msleep(1); - rtl8225_write_phy_cck(dev, 0x44, 0x1f); msleep(1); - rtl8225_write_phy_cck(dev, 0x45, 0x1e); msleep(1); - rtl8225_write_phy_cck(dev, 0x46, 0x1a); msleep(1); - rtl8225_write_phy_cck(dev, 0x47, 0x15); msleep(1); - rtl8225_write_phy_cck(dev, 0x48, 0x10); msleep(1); - rtl8225_write_phy_cck(dev, 0x49, 0x0a); msleep(1); - rtl8225_write_phy_cck(dev, 0x4a, 0x05); msleep(1); - rtl8225_write_phy_cck(dev, 0x4b, 0x02); msleep(1); - rtl8225_write_phy_cck(dev, 0x4c, 0x05); msleep(1); - - rtl818x_iowrite8(priv, &priv->map->TESTR, 0x0D); msleep(1); - - rtl8225_rf_set_tx_power(dev, 1); - - /* RX antenna default to A */ - rtl8225_write_phy_cck(dev, 0x10, 0x9b); msleep(1); /* B: 0xDB */ - rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1); /* B: 0x10 */ - - rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); /* B: 0x00 */ - msleep(1); - rtl818x_iowrite32(priv, (__le32 __iomem *)((void __iomem *)priv->map + 0x94), 0x15c00002); - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); - - rtl8225_write(dev, 0x0c, 0x50); - /* set OFDM initial gain */ - rtl8225_write_phy_ofdm(dev, 0x0d, rtl8225_gain[4 * 4]); - rtl8225_write_phy_ofdm(dev, 0x23, rtl8225_gain[4 * 4 + 1]); - rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225_gain[4 * 4 + 2]); - rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225_gain[4 * 4 + 3]); - /* set CCK threshold */ - rtl8225_write_phy_cck(dev, 0x41, rtl8225_threshold[0]); -} - -static const u8 rtl8225z2_tx_power_cck_ch14[] = { - 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00 -}; - -static const u8 rtl8225z2_tx_power_cck_B[] = { - 0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x04 -}; - -static const u8 rtl8225z2_tx_power_cck_A[] = { - 0x33, 0x32, 0x2b, 0x23, 0x1a, 0x11, 0x08, 0x04 -}; - -static const u8 rtl8225z2_tx_power_cck[] = { - 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04 -}; - -static void rtl8225z2_rf_set_tx_power(struct ieee80211_hw *dev, int channel) -{ - struct rtl8180_priv *priv = dev->priv; - u8 cck_power, ofdm_power; - const u8 *tmp; - int i; - - cck_power = priv->channels[channel - 1].hw_value & 0xFF; - ofdm_power = priv->channels[channel - 1].hw_value >> 8; - - if (channel == 14) - tmp = rtl8225z2_tx_power_cck_ch14; - else if (cck_power == 12) - tmp = rtl8225z2_tx_power_cck_B; - else if (cck_power == 13) - tmp = rtl8225z2_tx_power_cck_A; - else - tmp = rtl8225z2_tx_power_cck; - - for (i = 0; i < 8; i++) - rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++); - - cck_power = min(cck_power, (u8)35); - if (cck_power == 13 || cck_power == 14) - cck_power = 12; - if (cck_power >= 15) - cck_power -= 2; - - rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, cck_power); - rtl818x_ioread8(priv, &priv->map->TX_GAIN_CCK); - msleep(1); - - ofdm_power = min(ofdm_power, (u8)35); - rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, ofdm_power); - - rtl8225_write_phy_ofdm(dev, 2, 0x62); - rtl8225_write_phy_ofdm(dev, 5, 0x00); - rtl8225_write_phy_ofdm(dev, 6, 0x40); - rtl8225_write_phy_ofdm(dev, 7, 0x00); - rtl8225_write_phy_ofdm(dev, 8, 0x40); - - msleep(1); -} - -static const u16 rtl8225z2_rxgain[] = { - 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0008, 0x0009, - 0x000a, 0x000b, 0x0102, 0x0103, 0x0104, 0x0105, 0x0140, 0x0141, - 0x0142, 0x0143, 0x0144, 0x0145, 0x0180, 0x0181, 0x0182, 0x0183, - 0x0184, 0x0185, 0x0188, 0x0189, 0x018a, 0x018b, 0x0243, 0x0244, - 0x0245, 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0288, - 0x0289, 0x028a, 0x028b, 0x028c, 0x0342, 0x0343, 0x0344, 0x0345, - 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0388, 0x0389, - 0x038a, 0x038b, 0x038c, 0x038d, 0x0390, 0x0391, 0x0392, 0x0393, - 0x0394, 0x0395, 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, - 0x03a0, 0x03a1, 0x03a2, 0x03a3, 0x03a4, 0x03a5, 0x03a8, 0x03a9, - 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3, - 0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb -}; - -static void rtl8225z2_rf_init(struct ieee80211_hw *dev) -{ - struct rtl8180_priv *priv = dev->priv; - int i; - - rtl8180_set_anaparam(priv, RTL8225_ANAPARAM_ON); - - /* host_pci_init */ - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480); - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x0488); - rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - msleep(200); /* FIXME: ehh?? */ - rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0xFF & ~(1 << 6)); - - rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x00088008); - - /* TODO: check if we need really to change BRSR to do RF config */ - rtl818x_ioread16(priv, &priv->map->BRSR); - rtl818x_iowrite16(priv, &priv->map->BRSR, 0xFFFF); - rtl818x_iowrite32(priv, &priv->map->RF_PARA, 0x00100044); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); - rtl818x_iowrite8(priv, &priv->map->CONFIG3, 0x44); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); - - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); - - rtl8225_write(dev, 0x0, 0x0B7); msleep(1); - rtl8225_write(dev, 0x1, 0xEE0); msleep(1); - rtl8225_write(dev, 0x2, 0x44D); msleep(1); - rtl8225_write(dev, 0x3, 0x441); msleep(1); - rtl8225_write(dev, 0x4, 0x8C3); msleep(1); - rtl8225_write(dev, 0x5, 0xC72); msleep(1); - rtl8225_write(dev, 0x6, 0x0E6); msleep(1); - rtl8225_write(dev, 0x7, 0x82A); msleep(1); - rtl8225_write(dev, 0x8, 0x03F); msleep(1); - rtl8225_write(dev, 0x9, 0x335); msleep(1); - rtl8225_write(dev, 0xa, 0x9D4); msleep(1); - rtl8225_write(dev, 0xb, 0x7BB); msleep(1); - rtl8225_write(dev, 0xc, 0x850); msleep(1); - rtl8225_write(dev, 0xd, 0xCDF); msleep(1); - rtl8225_write(dev, 0xe, 0x02B); msleep(1); - rtl8225_write(dev, 0xf, 0x114); msleep(100); - - if (!(rtl8225_read(dev, 6) & (1 << 7))) { - rtl8225_write(dev, 0x02, 0x0C4D); - msleep(200); - rtl8225_write(dev, 0x02, 0x044D); - msleep(100); - /* TODO: readd calibration failure message when the calibration - check works */ - } - - rtl8225_write(dev, 0x0, 0x1B7); - rtl8225_write(dev, 0x3, 0x002); - rtl8225_write(dev, 0x5, 0x004); - - for (i = 0; i < ARRAY_SIZE(rtl8225z2_rxgain); i++) { - rtl8225_write(dev, 0x1, i + 1); - rtl8225_write(dev, 0x2, rtl8225z2_rxgain[i]); - } - - rtl8225_write(dev, 0x0, 0x0B7); msleep(100); - rtl8225_write(dev, 0x2, 0xC4D); - - msleep(200); - rtl8225_write(dev, 0x2, 0x44D); - msleep(100); - - rtl8225_write(dev, 0x00, 0x2BF); - rtl8225_write(dev, 0xFF, 0xFFFF); - - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); - - for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) { - rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]); - msleep(1); - rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i); - msleep(1); - } - - msleep(1); - - rtl8225_write_phy_ofdm(dev, 0x00, 0x01); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x01, 0x02); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x02, 0x62); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x03, 0x00); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x04, 0x00); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x05, 0x00); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x06, 0x40); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x07, 0x00); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x08, 0x40); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x0a, 0x09); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x18, 0xef); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x0d, 0x43); - rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x10, 0x84); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x11, 0x06); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x12, 0x20); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x13, 0x20); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x14, 0x00); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x15, 0x40); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x16, 0x00); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x17, 0x40); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x18, 0xef); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x19, 0x19); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x1b, 0x11); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x1d, 0xc5); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x1e, 0xb3); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x21, 0x27); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x22, 0x16); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x23, 0x80); msleep(1); /* FIXME: not needed? */ - rtl8225_write_phy_ofdm(dev, 0x24, 0x46); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x25, 0x20); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1); - rtl8225_write_phy_ofdm(dev, 0x27, 0x88); msleep(1); - - rtl8225_write_phy_cck(dev, 0x00, 0x98); msleep(1); - rtl8225_write_phy_cck(dev, 0x03, 0x20); msleep(1); - rtl8225_write_phy_cck(dev, 0x04, 0x7e); msleep(1); - rtl8225_write_phy_cck(dev, 0x05, 0x12); msleep(1); - rtl8225_write_phy_cck(dev, 0x06, 0xfc); msleep(1); - rtl8225_write_phy_cck(dev, 0x07, 0x78); msleep(1); - rtl8225_write_phy_cck(dev, 0x08, 0x2e); msleep(1); - rtl8225_write_phy_cck(dev, 0x10, 0x93); msleep(1); - rtl8225_write_phy_cck(dev, 0x11, 0x88); msleep(1); - rtl8225_write_phy_cck(dev, 0x12, 0x47); msleep(1); - rtl8225_write_phy_cck(dev, 0x13, 0xd0); - rtl8225_write_phy_cck(dev, 0x19, 0x00); - rtl8225_write_phy_cck(dev, 0x1a, 0xa0); - rtl8225_write_phy_cck(dev, 0x1b, 0x08); - rtl8225_write_phy_cck(dev, 0x40, 0x86); - rtl8225_write_phy_cck(dev, 0x41, 0x8a); msleep(1); - rtl8225_write_phy_cck(dev, 0x42, 0x15); msleep(1); - rtl8225_write_phy_cck(dev, 0x43, 0x18); msleep(1); - rtl8225_write_phy_cck(dev, 0x44, 0x36); msleep(1); - rtl8225_write_phy_cck(dev, 0x45, 0x35); msleep(1); - rtl8225_write_phy_cck(dev, 0x46, 0x2e); msleep(1); - rtl8225_write_phy_cck(dev, 0x47, 0x25); msleep(1); - rtl8225_write_phy_cck(dev, 0x48, 0x1c); msleep(1); - rtl8225_write_phy_cck(dev, 0x49, 0x12); msleep(1); - rtl8225_write_phy_cck(dev, 0x4a, 0x09); msleep(1); - rtl8225_write_phy_cck(dev, 0x4b, 0x04); msleep(1); - rtl8225_write_phy_cck(dev, 0x4c, 0x05); msleep(1); - - rtl818x_iowrite8(priv, (u8 __iomem *)((void __iomem *)priv->map + 0x5B), 0x0D); msleep(1); - - rtl8225z2_rf_set_tx_power(dev, 1); - - /* RX antenna default to A */ - rtl8225_write_phy_cck(dev, 0x10, 0x9b); msleep(1); /* B: 0xDB */ - rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1); /* B: 0x10 */ - - rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); /* B: 0x00 */ - msleep(1); - rtl818x_iowrite32(priv, (__le32 __iomem *)((void __iomem *)priv->map + 0x94), 0x15c00002); - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); -} - -static void rtl8225_rf_stop(struct ieee80211_hw *dev) -{ - struct rtl8180_priv *priv = dev->priv; - u8 reg; - - rtl8225_write(dev, 0x4, 0x1f); msleep(1); - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); - reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); - rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE); - rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, RTL8225_ANAPARAM2_OFF); - rtl818x_iowrite32(priv, &priv->map->ANAPARAM, RTL8225_ANAPARAM_OFF); - rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); -} - -static void rtl8225_rf_set_channel(struct ieee80211_hw *dev, - struct ieee80211_conf *conf) -{ - struct rtl8180_priv *priv = dev->priv; - int chan = ieee80211_frequency_to_channel(conf->channel->center_freq); - - if (priv->rf->init == rtl8225_rf_init) - rtl8225_rf_set_tx_power(dev, chan); - else - rtl8225z2_rf_set_tx_power(dev, chan); - - rtl8225_write(dev, 0x7, rtl8225_chan[chan - 1]); - msleep(10); -} - -static void rtl8225_rf_conf_erp(struct ieee80211_hw *dev, - struct ieee80211_bss_conf *info) -{ - struct rtl8180_priv *priv = dev->priv; - - if (info->use_short_slot) { - rtl818x_iowrite8(priv, &priv->map->SLOT, 0x9); - rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22); - rtl818x_iowrite8(priv, &priv->map->DIFS, 0x14); - rtl818x_iowrite8(priv, &priv->map->EIFS, 81); - rtl818x_iowrite8(priv, &priv->map->CW_VAL, 0x73); - } else { - rtl818x_iowrite8(priv, &priv->map->SLOT, 0x14); - rtl818x_iowrite8(priv, &priv->map->SIFS, 0x44); - rtl818x_iowrite8(priv, &priv->map->DIFS, 0x24); - rtl818x_iowrite8(priv, &priv->map->EIFS, 81); - rtl818x_iowrite8(priv, &priv->map->CW_VAL, 0xa5); - } -} - -static const struct rtl818x_rf_ops rtl8225_ops = { - .name = "rtl8225", - .init = rtl8225_rf_init, - .stop = rtl8225_rf_stop, - .set_chan = rtl8225_rf_set_channel, - .conf_erp = rtl8225_rf_conf_erp, -}; - -static const struct rtl818x_rf_ops rtl8225z2_ops = { - .name = "rtl8225z2", - .init = rtl8225z2_rf_init, - .stop = rtl8225_rf_stop, - .set_chan = rtl8225_rf_set_channel, - .conf_erp = rtl8225_rf_conf_erp, -}; - -const struct rtl818x_rf_ops * rtl8180_detect_rf(struct ieee80211_hw *dev) -{ - struct rtl8180_priv *priv = dev->priv; - u16 reg8, reg9; - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x0488); - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); - rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - msleep(100); - - rtl8225_write(dev, 0, 0x1B7); - - reg8 = rtl8225_read(dev, 8); - reg9 = rtl8225_read(dev, 9); - - rtl8225_write(dev, 0, 0x0B7); - - if (reg8 != 0x588 || reg9 != 0x700) - return &rtl8225_ops; - - return &rtl8225z2_ops; -} diff --git a/drivers/net/wireless/rtl818x/rtl8180_rtl8225.h b/drivers/net/wireless/rtl818x/rtl8180_rtl8225.h deleted file mode 100644 index 310013a2d726..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8180_rtl8225.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef RTL8180_RTL8225_H -#define RTL8180_RTL8225_H - -#define RTL8225_ANAPARAM_ON 0xa0000b59 -#define RTL8225_ANAPARAM2_ON 0x860dec11 -#define RTL8225_ANAPARAM_OFF 0xa00beb59 -#define RTL8225_ANAPARAM2_OFF 0x840dec11 - -const struct rtl818x_rf_ops * rtl8180_detect_rf(struct ieee80211_hw *); - -static inline void rtl8225_write_phy_ofdm(struct ieee80211_hw *dev, - u8 addr, u8 data) -{ - rtl8180_write_phy(dev, addr, data); -} - -static inline void rtl8225_write_phy_cck(struct ieee80211_hw *dev, - u8 addr, u8 data) -{ - rtl8180_write_phy(dev, addr, data | 0x10000); -} - -#endif /* RTL8180_RTL8225_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8180_sa2400.c b/drivers/net/wireless/rtl818x/rtl8180_sa2400.c deleted file mode 100644 index d064fcc5ec08..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8180_sa2400.c +++ /dev/null @@ -1,228 +0,0 @@ - -/* - * Radio tuning for Philips SA2400 on RTL8180 - * - * Copyright 2007 Andrea Merello - * - * Code from the BSD driver and the rtl8181 project have been - * very useful to understand certain things - * - * I want to thanks the Authors of such projects and the Ndiswrapper - * project Authors. - * - * A special Big Thanks also is for all people who donated me cards, - * making possible the creation of the original rtl8180 driver - * from which this code is derived! - * - * 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 -#include -#include -#include - -#include "rtl8180.h" -#include "rtl8180_sa2400.h" - -static const u32 sa2400_chan[] = { - 0x00096c, /* ch1 */ - 0x080970, - 0x100974, - 0x180978, - 0x000980, - 0x080984, - 0x100988, - 0x18098c, - 0x000994, - 0x080998, - 0x10099c, - 0x1809a0, - 0x0009a8, - 0x0009b4, /* ch 14 */ -}; - -static void write_sa2400(struct ieee80211_hw *dev, u8 addr, u32 data) -{ - struct rtl8180_priv *priv = dev->priv; - u32 phy_config; - - /* MAC will bang bits to the sa2400. sw 3-wire is NOT used */ - phy_config = 0xb0000000; - - phy_config |= ((u32)(addr & 0xf)) << 24; - phy_config |= data & 0xffffff; - - rtl818x_iowrite32(priv, - (__le32 __iomem *) &priv->map->RFPinsOutput, phy_config); - - msleep(3); -} - -static void sa2400_write_phy_antenna(struct ieee80211_hw *dev, short chan) -{ - struct rtl8180_priv *priv = dev->priv; - u8 ant = SA2400_ANTENNA; - - if (priv->rfparam & RF_PARAM_ANTBDEFAULT) - ant |= BB_ANTENNA_B; - - if (chan == 14) - ant |= BB_ANTATTEN_CHAN14; - - rtl8180_write_phy(dev, 0x10, ant); - -} - -static u8 sa2400_rf_rssi_map[] = { - 0x64, 0x64, 0x63, 0x62, 0x61, 0x60, 0x5f, 0x5e, - 0x5d, 0x5c, 0x5b, 0x5a, 0x57, 0x54, 0x52, 0x50, - 0x4e, 0x4c, 0x4a, 0x48, 0x46, 0x44, 0x41, 0x3f, - 0x3c, 0x3a, 0x37, 0x36, 0x36, 0x1c, 0x1c, 0x1b, - 0x1b, 0x1a, 0x1a, 0x19, 0x19, 0x18, 0x18, 0x17, - 0x17, 0x16, 0x16, 0x15, 0x15, 0x14, 0x14, 0x13, - 0x13, 0x12, 0x12, 0x11, 0x11, 0x10, 0x10, 0x0f, - 0x0f, 0x0e, 0x0e, 0x0d, 0x0d, 0x0c, 0x0c, 0x0b, - 0x0b, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x07, - 0x07, 0x06, 0x06, 0x05, 0x04, 0x03, 0x02, -}; - -static u8 sa2400_rf_calc_rssi(u8 agc, u8 sq) -{ - if (sq == 0x80) - return 1; - - if (sq > 78) - return 32; - - /* TODO: recalc sa2400_rf_rssi_map to avoid mult / div */ - return 65 * sa2400_rf_rssi_map[sq] / 100; -} - -static void sa2400_rf_set_channel(struct ieee80211_hw *dev, - struct ieee80211_conf *conf) -{ - struct rtl8180_priv *priv = dev->priv; - int channel = ieee80211_frequency_to_channel(conf->channel->center_freq); - u32 txpw = priv->channels[channel - 1].hw_value & 0xFF; - u32 chan = sa2400_chan[channel - 1]; - - write_sa2400(dev, 7, txpw); - - sa2400_write_phy_antenna(dev, channel); - - write_sa2400(dev, 0, chan); - write_sa2400(dev, 1, 0xbb50); - write_sa2400(dev, 2, 0x80); - write_sa2400(dev, 3, 0); -} - -static void sa2400_rf_stop(struct ieee80211_hw *dev) -{ - write_sa2400(dev, 4, 0); -} - -static void sa2400_rf_init(struct ieee80211_hw *dev) -{ - struct rtl8180_priv *priv = dev->priv; - u32 anaparam, txconf; - u8 firdac; - int analogphy = priv->rfparam & RF_PARAM_ANALOGPHY; - - anaparam = priv->anaparam; - anaparam &= ~(1 << ANAPARAM_TXDACOFF_SHIFT); - anaparam &= ~ANAPARAM_PWR1_MASK; - anaparam &= ~ANAPARAM_PWR0_MASK; - - if (analogphy) { - anaparam |= SA2400_ANA_ANAPARAM_PWR1_ON << ANAPARAM_PWR1_SHIFT; - firdac = 0; - } else { - anaparam |= (SA2400_DIG_ANAPARAM_PWR1_ON << ANAPARAM_PWR1_SHIFT); - anaparam |= (SA2400_ANAPARAM_PWR0_ON << ANAPARAM_PWR0_SHIFT); - firdac = 1 << SA2400_REG4_FIRDAC_SHIFT; - } - - rtl8180_set_anaparam(priv, anaparam); - - write_sa2400(dev, 0, sa2400_chan[0]); - write_sa2400(dev, 1, 0xbb50); - write_sa2400(dev, 2, 0x80); - write_sa2400(dev, 3, 0); - write_sa2400(dev, 4, 0x19340 | firdac); - write_sa2400(dev, 5, 0x1dfb | (SA2400_MAX_SENS - 54) << 15); - write_sa2400(dev, 4, 0x19348 | firdac); /* calibrate VCO */ - - if (!analogphy) - write_sa2400(dev, 4, 0x1938c); /*???*/ - - write_sa2400(dev, 4, 0x19340 | firdac); - - write_sa2400(dev, 0, sa2400_chan[0]); - write_sa2400(dev, 1, 0xbb50); - write_sa2400(dev, 2, 0x80); - write_sa2400(dev, 3, 0); - write_sa2400(dev, 4, 0x19344 | firdac); /* calibrate filter */ - - /* new from rtl8180 embedded driver (rtl8181 project) */ - write_sa2400(dev, 6, 0x13ff | (1 << 23)); /* MANRX */ - write_sa2400(dev, 8, 0); /* VCO */ - - if (analogphy) { - rtl8180_set_anaparam(priv, anaparam | - (1 << ANAPARAM_TXDACOFF_SHIFT)); - - txconf = rtl818x_ioread32(priv, &priv->map->TX_CONF); - rtl818x_iowrite32(priv, &priv->map->TX_CONF, - txconf | RTL818X_TX_CONF_LOOPBACK_CONT); - - write_sa2400(dev, 4, 0x19341); /* calibrates DC */ - - /* a 5us sleep is required here, - * we rely on the 3ms delay introduced in write_sa2400 */ - write_sa2400(dev, 4, 0x19345); - - /* a 20us sleep is required here, - * we rely on the 3ms delay introduced in write_sa2400 */ - - rtl818x_iowrite32(priv, &priv->map->TX_CONF, txconf); - - rtl8180_set_anaparam(priv, anaparam); - } - /* end new code */ - - write_sa2400(dev, 4, 0x19341 | firdac); /* RTX MODE */ - - /* baseband configuration */ - rtl8180_write_phy(dev, 0, 0x98); - rtl8180_write_phy(dev, 3, 0x38); - rtl8180_write_phy(dev, 4, 0xe0); - rtl8180_write_phy(dev, 5, 0x90); - rtl8180_write_phy(dev, 6, 0x1a); - rtl8180_write_phy(dev, 7, 0x64); - - sa2400_write_phy_antenna(dev, 1); - - rtl8180_write_phy(dev, 0x11, 0x80); - - if (rtl818x_ioread8(priv, &priv->map->CONFIG2) & - RTL818X_CONFIG2_ANTENNA_DIV) - rtl8180_write_phy(dev, 0x12, 0xc7); /* enable ant diversity */ - else - rtl8180_write_phy(dev, 0x12, 0x47); /* disable ant diversity */ - - rtl8180_write_phy(dev, 0x13, 0x90 | priv->csthreshold); - - rtl8180_write_phy(dev, 0x19, 0x0); - rtl8180_write_phy(dev, 0x1a, 0xa0); -} - -const struct rtl818x_rf_ops sa2400_rf_ops = { - .name = "Philips", - .init = sa2400_rf_init, - .stop = sa2400_rf_stop, - .set_chan = sa2400_rf_set_channel, - .calc_rssi = sa2400_rf_calc_rssi, -}; diff --git a/drivers/net/wireless/rtl818x/rtl8180_sa2400.h b/drivers/net/wireless/rtl818x/rtl8180_sa2400.h deleted file mode 100644 index a4aaa0d413f1..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8180_sa2400.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef RTL8180_SA2400_H -#define RTL8180_SA2400_H - -/* - * Radio tuning for Philips SA2400 on RTL8180 - * - * Copyright 2007 Andrea Merello - * - * Code from the BSD driver and the rtl8181 project have been - * very useful to understand certain things - * - * I want to thanks the Authors of such projects and the Ndiswrapper - * project Authors. - * - * A special Big Thanks also is for all people who donated me cards, - * making possible the creation of the original rtl8180 driver - * from which this code is derived! - * - * 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. - */ - -#define SA2400_ANTENNA 0x91 -#define SA2400_DIG_ANAPARAM_PWR1_ON 0x8 -#define SA2400_ANA_ANAPARAM_PWR1_ON 0x28 -#define SA2400_ANAPARAM_PWR0_ON 0x3 - -/* RX sensitivity in dbm */ -#define SA2400_MAX_SENS 85 - -#define SA2400_REG4_FIRDAC_SHIFT 7 - -extern const struct rtl818x_rf_ops sa2400_rf_ops; - -#endif /* RTL8180_SA2400_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8187.h b/drivers/net/wireless/rtl818x/rtl8187.h deleted file mode 100644 index 98878160a65a..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8187.h +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Definitions for RTL8187 hardware - * - * Copyright 2007 Michael Wu - * Copyright 2007 Andrea Merello - * - * Based on the r8187 driver, which is: - * Copyright 2005 Andrea Merello , et al. - * - * 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. - */ - -#ifndef RTL8187_H -#define RTL8187_H - -#include "rtl818x.h" -#include "rtl8187_leds.h" - -#define RTL8187_EEPROM_TXPWR_BASE 0x05 -#define RTL8187_EEPROM_MAC_ADDR 0x07 -#define RTL8187_EEPROM_TXPWR_CHAN_1 0x16 /* 3 channels */ -#define RTL8187_EEPROM_TXPWR_CHAN_6 0x1B /* 2 channels */ -#define RTL8187_EEPROM_TXPWR_CHAN_4 0x3D /* 2 channels */ -#define RTL8187_EEPROM_SELECT_GPIO 0x3B - -#define RTL8187_REQT_READ 0xC0 -#define RTL8187_REQT_WRITE 0x40 -#define RTL8187_REQ_GET_REG 0x05 -#define RTL8187_REQ_SET_REG 0x05 - -#define RTL8187_MAX_RX 0x9C4 - -#define RFKILL_MASK_8187_89_97 0x2 -#define RFKILL_MASK_8198 0x4 - -struct rtl8187_rx_info { - struct urb *urb; - struct ieee80211_hw *dev; -}; - -struct rtl8187_rx_hdr { - __le32 flags; - u8 noise; - u8 signal; - u8 agc; - u8 reserved; - __le64 mac_time; -} __packed; - -struct rtl8187b_rx_hdr { - __le32 flags; - __le64 mac_time; - u8 sq; - u8 rssi; - u8 agc; - u8 flags2; - __le16 snr_long2end; - s8 pwdb_g12; - u8 fot; -} __packed; - -/* {rtl8187,rtl8187b}_tx_info is in skb */ - -struct rtl8187_tx_hdr { - __le32 flags; - __le16 rts_duration; - __le16 len; - __le32 retry; -} __packed; - -struct rtl8187b_tx_hdr { - __le32 flags; - __le16 rts_duration; - __le16 len; - __le32 unused_1; - __le16 unused_2; - __le16 tx_duration; - __le32 unused_3; - __le32 retry; - __le32 unused_4[2]; -} __packed; - -enum { - DEVICE_RTL8187, - DEVICE_RTL8187B -}; - -struct rtl8187_priv { - /* common between rtl818x drivers */ - struct rtl818x_csr *map; - const struct rtl818x_rf_ops *rf; - struct ieee80211_vif *vif; - - /* The mutex protects the TX loopback state. - * Any attempt to set channels concurrently locks the device. - */ - struct mutex conf_mutex; - - /* rtl8187 specific */ - struct ieee80211_channel channels[14]; - struct ieee80211_rate rates[12]; - struct ieee80211_supported_band band; - struct usb_device *udev; - u32 rx_conf; - struct usb_anchor anchored; - struct delayed_work work; - struct ieee80211_hw *dev; -#ifdef CONFIG_RTL8187_LEDS - struct rtl8187_led led_radio; - struct rtl8187_led led_tx; - struct rtl8187_led led_rx; - struct delayed_work led_on; - struct delayed_work led_off; -#endif - u16 txpwr_base; - u8 asic_rev; - u8 is_rtl8187b; - enum { - RTL8187BvB, - RTL8187BvD, - RTL8187BvE - } hw_rev; - struct sk_buff_head rx_queue; - u8 signal; - u8 noise; - u8 slot_time; - u8 aifsn[4]; - u8 rfkill_mask; - struct { - __le64 buf; - struct sk_buff_head queue; - } b_tx_status; /* This queue is used by both -b and non-b devices */ - struct mutex io_mutex; - union { - u8 bits8; - __le16 bits16; - __le32 bits32; - } *io_dmabuf; - bool rfkill_off; -}; - -void rtl8187_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data); - -static inline u8 rtl818x_ioread8_idx(struct rtl8187_priv *priv, - u8 *addr, u8 idx) -{ - u8 val; - - mutex_lock(&priv->io_mutex); - usb_control_msg(priv->udev, usb_rcvctrlpipe(priv->udev, 0), - RTL8187_REQ_GET_REG, RTL8187_REQT_READ, - (unsigned long)addr, idx & 0x03, - &priv->io_dmabuf->bits8, sizeof(val), HZ / 2); - - val = priv->io_dmabuf->bits8; - mutex_unlock(&priv->io_mutex); - - return val; -} - -static inline u8 rtl818x_ioread8(struct rtl8187_priv *priv, u8 *addr) -{ - return rtl818x_ioread8_idx(priv, addr, 0); -} - -static inline u16 rtl818x_ioread16_idx(struct rtl8187_priv *priv, - __le16 *addr, u8 idx) -{ - __le16 val; - - mutex_lock(&priv->io_mutex); - usb_control_msg(priv->udev, usb_rcvctrlpipe(priv->udev, 0), - RTL8187_REQ_GET_REG, RTL8187_REQT_READ, - (unsigned long)addr, idx & 0x03, - &priv->io_dmabuf->bits16, sizeof(val), HZ / 2); - - val = priv->io_dmabuf->bits16; - mutex_unlock(&priv->io_mutex); - - return le16_to_cpu(val); -} - -static inline u16 rtl818x_ioread16(struct rtl8187_priv *priv, __le16 *addr) -{ - return rtl818x_ioread16_idx(priv, addr, 0); -} - -static inline u32 rtl818x_ioread32_idx(struct rtl8187_priv *priv, - __le32 *addr, u8 idx) -{ - __le32 val; - - mutex_lock(&priv->io_mutex); - usb_control_msg(priv->udev, usb_rcvctrlpipe(priv->udev, 0), - RTL8187_REQ_GET_REG, RTL8187_REQT_READ, - (unsigned long)addr, idx & 0x03, - &priv->io_dmabuf->bits32, sizeof(val), HZ / 2); - - val = priv->io_dmabuf->bits32; - mutex_unlock(&priv->io_mutex); - - return le32_to_cpu(val); -} - -static inline u32 rtl818x_ioread32(struct rtl8187_priv *priv, __le32 *addr) -{ - return rtl818x_ioread32_idx(priv, addr, 0); -} - -static inline void rtl818x_iowrite8_idx(struct rtl8187_priv *priv, - u8 *addr, u8 val, u8 idx) -{ - mutex_lock(&priv->io_mutex); - - priv->io_dmabuf->bits8 = val; - usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0), - RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE, - (unsigned long)addr, idx & 0x03, - &priv->io_dmabuf->bits8, sizeof(val), HZ / 2); - - mutex_unlock(&priv->io_mutex); -} - -static inline void rtl818x_iowrite8(struct rtl8187_priv *priv, u8 *addr, u8 val) -{ - rtl818x_iowrite8_idx(priv, addr, val, 0); -} - -static inline void rtl818x_iowrite16_idx(struct rtl8187_priv *priv, - __le16 *addr, u16 val, u8 idx) -{ - mutex_lock(&priv->io_mutex); - - priv->io_dmabuf->bits16 = cpu_to_le16(val); - usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0), - RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE, - (unsigned long)addr, idx & 0x03, - &priv->io_dmabuf->bits16, sizeof(val), HZ / 2); - - mutex_unlock(&priv->io_mutex); -} - -static inline void rtl818x_iowrite16(struct rtl8187_priv *priv, __le16 *addr, - u16 val) -{ - rtl818x_iowrite16_idx(priv, addr, val, 0); -} - -static inline void rtl818x_iowrite32_idx(struct rtl8187_priv *priv, - __le32 *addr, u32 val, u8 idx) -{ - mutex_lock(&priv->io_mutex); - - priv->io_dmabuf->bits32 = cpu_to_le32(val); - usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0), - RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE, - (unsigned long)addr, idx & 0x03, - &priv->io_dmabuf->bits32, sizeof(val), HZ / 2); - - mutex_unlock(&priv->io_mutex); -} - -static inline void rtl818x_iowrite32(struct rtl8187_priv *priv, __le32 *addr, - u32 val) -{ - rtl818x_iowrite32_idx(priv, addr, val, 0); -} - -#endif /* RTL8187_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8187/Makefile b/drivers/net/wireless/rtl818x/rtl8187/Makefile new file mode 100644 index 000000000000..7b6299268ecf --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8187/Makefile @@ -0,0 +1,5 @@ +rtl8187-objs := dev.o rtl8225.o leds.o rfkill.o + +obj-$(CONFIG_RTL8187) += rtl8187.o + +ccflags-y += -Idrivers/net/wireless/rtl818x diff --git a/drivers/net/wireless/rtl818x/rtl8187/dev.c b/drivers/net/wireless/rtl818x/rtl8187/dev.c new file mode 100644 index 000000000000..6b82cac37ee3 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8187/dev.c @@ -0,0 +1,1591 @@ +/* + * Linux device driver for RTL8187 + * + * Copyright 2007 Michael Wu + * Copyright 2007 Andrea Merello + * + * Based on the r8187 driver, which is: + * Copyright 2005 Andrea Merello , et al. + * + * The driver was extended to the RTL8187B in 2008 by: + * Herton Ronaldo Krzesinski + * Hin-Tak Leung + * Larry Finger + * + * Magic delays and register offsets below are taken from the original + * r8187 driver sources. Thanks to Realtek for their support! + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "rtl8187.h" +#include "rtl8225.h" +#ifdef CONFIG_RTL8187_LEDS +#include "leds.h" +#endif +#include "rfkill.h" + +MODULE_AUTHOR("Michael Wu "); +MODULE_AUTHOR("Andrea Merello "); +MODULE_AUTHOR("Herton Ronaldo Krzesinski "); +MODULE_AUTHOR("Hin-Tak Leung "); +MODULE_AUTHOR("Larry Finger "); +MODULE_DESCRIPTION("RTL8187/RTL8187B USB wireless driver"); +MODULE_LICENSE("GPL"); + +static struct usb_device_id rtl8187_table[] __devinitdata = { + /* Asus */ + {USB_DEVICE(0x0b05, 0x171d), .driver_info = DEVICE_RTL8187}, + /* Belkin */ + {USB_DEVICE(0x050d, 0x705e), .driver_info = DEVICE_RTL8187B}, + /* Realtek */ + {USB_DEVICE(0x0bda, 0x8187), .driver_info = DEVICE_RTL8187}, + {USB_DEVICE(0x0bda, 0x8189), .driver_info = DEVICE_RTL8187B}, + {USB_DEVICE(0x0bda, 0x8197), .driver_info = DEVICE_RTL8187B}, + {USB_DEVICE(0x0bda, 0x8198), .driver_info = DEVICE_RTL8187B}, + /* Surecom */ + {USB_DEVICE(0x0769, 0x11F2), .driver_info = DEVICE_RTL8187}, + /* Logitech */ + {USB_DEVICE(0x0789, 0x010C), .driver_info = DEVICE_RTL8187}, + /* Netgear */ + {USB_DEVICE(0x0846, 0x6100), .driver_info = DEVICE_RTL8187}, + {USB_DEVICE(0x0846, 0x6a00), .driver_info = DEVICE_RTL8187}, + {USB_DEVICE(0x0846, 0x4260), .driver_info = DEVICE_RTL8187B}, + /* HP */ + {USB_DEVICE(0x03f0, 0xca02), .driver_info = DEVICE_RTL8187}, + /* Sitecom */ + {USB_DEVICE(0x0df6, 0x000d), .driver_info = DEVICE_RTL8187}, + {USB_DEVICE(0x0df6, 0x0028), .driver_info = DEVICE_RTL8187B}, + {USB_DEVICE(0x0df6, 0x0029), .driver_info = DEVICE_RTL8187B}, + /* Sphairon Access Systems GmbH */ + {USB_DEVICE(0x114B, 0x0150), .driver_info = DEVICE_RTL8187}, + /* Dick Smith Electronics */ + {USB_DEVICE(0x1371, 0x9401), .driver_info = DEVICE_RTL8187}, + /* Abocom */ + {USB_DEVICE(0x13d1, 0xabe6), .driver_info = DEVICE_RTL8187}, + /* Qcom */ + {USB_DEVICE(0x18E8, 0x6232), .driver_info = DEVICE_RTL8187}, + /* AirLive */ + {USB_DEVICE(0x1b75, 0x8187), .driver_info = DEVICE_RTL8187}, + /* Linksys */ + {USB_DEVICE(0x1737, 0x0073), .driver_info = DEVICE_RTL8187B}, + {} +}; + +MODULE_DEVICE_TABLE(usb, rtl8187_table); + +static const struct ieee80211_rate rtl818x_rates[] = { + { .bitrate = 10, .hw_value = 0, }, + { .bitrate = 20, .hw_value = 1, }, + { .bitrate = 55, .hw_value = 2, }, + { .bitrate = 110, .hw_value = 3, }, + { .bitrate = 60, .hw_value = 4, }, + { .bitrate = 90, .hw_value = 5, }, + { .bitrate = 120, .hw_value = 6, }, + { .bitrate = 180, .hw_value = 7, }, + { .bitrate = 240, .hw_value = 8, }, + { .bitrate = 360, .hw_value = 9, }, + { .bitrate = 480, .hw_value = 10, }, + { .bitrate = 540, .hw_value = 11, }, +}; + +static const struct ieee80211_channel rtl818x_channels[] = { + { .center_freq = 2412 }, + { .center_freq = 2417 }, + { .center_freq = 2422 }, + { .center_freq = 2427 }, + { .center_freq = 2432 }, + { .center_freq = 2437 }, + { .center_freq = 2442 }, + { .center_freq = 2447 }, + { .center_freq = 2452 }, + { .center_freq = 2457 }, + { .center_freq = 2462 }, + { .center_freq = 2467 }, + { .center_freq = 2472 }, + { .center_freq = 2484 }, +}; + +static void rtl8187_iowrite_async_cb(struct urb *urb) +{ + kfree(urb->context); +} + +static void rtl8187_iowrite_async(struct rtl8187_priv *priv, __le16 addr, + void *data, u16 len) +{ + struct usb_ctrlrequest *dr; + struct urb *urb; + struct rtl8187_async_write_data { + u8 data[4]; + struct usb_ctrlrequest dr; + } *buf; + int rc; + + buf = kmalloc(sizeof(*buf), GFP_ATOMIC); + if (!buf) + return; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + kfree(buf); + return; + } + + dr = &buf->dr; + + dr->bRequestType = RTL8187_REQT_WRITE; + dr->bRequest = RTL8187_REQ_SET_REG; + dr->wValue = addr; + dr->wIndex = 0; + dr->wLength = cpu_to_le16(len); + + memcpy(buf, data, len); + + usb_fill_control_urb(urb, priv->udev, usb_sndctrlpipe(priv->udev, 0), + (unsigned char *)dr, buf, len, + rtl8187_iowrite_async_cb, buf); + usb_anchor_urb(urb, &priv->anchored); + rc = usb_submit_urb(urb, GFP_ATOMIC); + if (rc < 0) { + kfree(buf); + usb_unanchor_urb(urb); + } + usb_free_urb(urb); +} + +static inline void rtl818x_iowrite32_async(struct rtl8187_priv *priv, + __le32 *addr, u32 val) +{ + __le32 buf = cpu_to_le32(val); + + rtl8187_iowrite_async(priv, cpu_to_le16((unsigned long)addr), + &buf, sizeof(buf)); +} + +void rtl8187_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data) +{ + struct rtl8187_priv *priv = dev->priv; + + data <<= 8; + data |= addr | 0x80; + + rtl818x_iowrite8(priv, &priv->map->PHY[3], (data >> 24) & 0xFF); + rtl818x_iowrite8(priv, &priv->map->PHY[2], (data >> 16) & 0xFF); + rtl818x_iowrite8(priv, &priv->map->PHY[1], (data >> 8) & 0xFF); + rtl818x_iowrite8(priv, &priv->map->PHY[0], data & 0xFF); +} + +static void rtl8187_tx_cb(struct urb *urb) +{ + struct sk_buff *skb = (struct sk_buff *)urb->context; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hw *hw = info->rate_driver_data[0]; + struct rtl8187_priv *priv = hw->priv; + + skb_pull(skb, priv->is_rtl8187b ? sizeof(struct rtl8187b_tx_hdr) : + sizeof(struct rtl8187_tx_hdr)); + ieee80211_tx_info_clear_status(info); + + if (!(urb->status) && !(info->flags & IEEE80211_TX_CTL_NO_ACK)) { + if (priv->is_rtl8187b) { + skb_queue_tail(&priv->b_tx_status.queue, skb); + + /* queue is "full", discard last items */ + while (skb_queue_len(&priv->b_tx_status.queue) > 5) { + struct sk_buff *old_skb; + + dev_dbg(&priv->udev->dev, + "transmit status queue full\n"); + + old_skb = skb_dequeue(&priv->b_tx_status.queue); + ieee80211_tx_status_irqsafe(hw, old_skb); + } + return; + } else { + info->flags |= IEEE80211_TX_STAT_ACK; + } + } + if (priv->is_rtl8187b) + ieee80211_tx_status_irqsafe(hw, skb); + else { + /* Retry information for the RTI8187 is only available by + * reading a register in the device. We are in interrupt mode + * here, thus queue the skb and finish on a work queue. */ + skb_queue_tail(&priv->b_tx_status.queue, skb); + ieee80211_queue_delayed_work(hw, &priv->work, 0); + } +} + +static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb) +{ + struct rtl8187_priv *priv = dev->priv; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + unsigned int ep; + void *buf; + struct urb *urb; + __le16 rts_dur = 0; + u32 flags; + int rc; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + kfree_skb(skb); + return NETDEV_TX_OK; + } + + flags = skb->len; + flags |= RTL818X_TX_DESC_FLAG_NO_ENC; + + flags |= ieee80211_get_tx_rate(dev, info)->hw_value << 24; + if (ieee80211_has_morefrags(((struct ieee80211_hdr *)skb->data)->frame_control)) + flags |= RTL818X_TX_DESC_FLAG_MOREFRAG; + if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) { + flags |= RTL818X_TX_DESC_FLAG_RTS; + flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; + rts_dur = ieee80211_rts_duration(dev, priv->vif, + skb->len, info); + } else if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { + flags |= RTL818X_TX_DESC_FLAG_CTS; + flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; + } + + if (!priv->is_rtl8187b) { + struct rtl8187_tx_hdr *hdr = + (struct rtl8187_tx_hdr *)skb_push(skb, sizeof(*hdr)); + hdr->flags = cpu_to_le32(flags); + hdr->len = 0; + hdr->rts_duration = rts_dur; + hdr->retry = cpu_to_le32((info->control.rates[0].count - 1) << 8); + buf = hdr; + + ep = 2; + } else { + /* fc needs to be calculated before skb_push() */ + unsigned int epmap[4] = { 6, 7, 5, 4 }; + struct ieee80211_hdr *tx_hdr = + (struct ieee80211_hdr *)(skb->data); + u16 fc = le16_to_cpu(tx_hdr->frame_control); + + struct rtl8187b_tx_hdr *hdr = + (struct rtl8187b_tx_hdr *)skb_push(skb, sizeof(*hdr)); + struct ieee80211_rate *txrate = + ieee80211_get_tx_rate(dev, info); + memset(hdr, 0, sizeof(*hdr)); + hdr->flags = cpu_to_le32(flags); + hdr->rts_duration = rts_dur; + hdr->retry = cpu_to_le32((info->control.rates[0].count - 1) << 8); + hdr->tx_duration = + ieee80211_generic_frame_duration(dev, priv->vif, + skb->len, txrate); + buf = hdr; + + if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) + ep = 12; + else + ep = epmap[skb_get_queue_mapping(skb)]; + } + + info->rate_driver_data[0] = dev; + info->rate_driver_data[1] = urb; + + usb_fill_bulk_urb(urb, priv->udev, usb_sndbulkpipe(priv->udev, ep), + buf, skb->len, rtl8187_tx_cb, skb); + urb->transfer_flags |= URB_ZERO_PACKET; + usb_anchor_urb(urb, &priv->anchored); + rc = usb_submit_urb(urb, GFP_ATOMIC); + if (rc < 0) { + usb_unanchor_urb(urb); + kfree_skb(skb); + } + usb_free_urb(urb); + + return NETDEV_TX_OK; +} + +static void rtl8187_rx_cb(struct urb *urb) +{ + struct sk_buff *skb = (struct sk_buff *)urb->context; + struct rtl8187_rx_info *info = (struct rtl8187_rx_info *)skb->cb; + struct ieee80211_hw *dev = info->dev; + struct rtl8187_priv *priv = dev->priv; + struct ieee80211_rx_status rx_status = { 0 }; + int rate, signal; + u32 flags; + unsigned long f; + + spin_lock_irqsave(&priv->rx_queue.lock, f); + __skb_unlink(skb, &priv->rx_queue); + spin_unlock_irqrestore(&priv->rx_queue.lock, f); + skb_put(skb, urb->actual_length); + + if (unlikely(urb->status)) { + dev_kfree_skb_irq(skb); + return; + } + + if (!priv->is_rtl8187b) { + struct rtl8187_rx_hdr *hdr = + (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr)); + flags = le32_to_cpu(hdr->flags); + /* As with the RTL8187B below, the AGC is used to calculate + * signal strength. In this case, the scaling + * constants are derived from the output of p54usb. + */ + signal = -4 - ((27 * hdr->agc) >> 6); + rx_status.antenna = (hdr->signal >> 7) & 1; + rx_status.mactime = le64_to_cpu(hdr->mac_time); + } else { + struct rtl8187b_rx_hdr *hdr = + (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr)); + /* The Realtek datasheet for the RTL8187B shows that the RX + * header contains the following quantities: signal quality, + * RSSI, AGC, the received power in dB, and the measured SNR. + * In testing, none of these quantities show qualitative + * agreement with AP signal strength, except for the AGC, + * which is inversely proportional to the strength of the + * signal. In the following, the signal strength + * is derived from the AGC. The arbitrary scaling constants + * are chosen to make the results close to the values obtained + * for a BCM4312 using b43 as the driver. The noise is ignored + * for now. + */ + flags = le32_to_cpu(hdr->flags); + signal = 14 - hdr->agc / 2; + rx_status.antenna = (hdr->rssi >> 7) & 1; + rx_status.mactime = le64_to_cpu(hdr->mac_time); + } + + rx_status.signal = signal; + priv->signal = signal; + rate = (flags >> 20) & 0xF; + skb_trim(skb, flags & 0x0FFF); + rx_status.rate_idx = rate; + rx_status.freq = dev->conf.channel->center_freq; + rx_status.band = dev->conf.channel->band; + rx_status.flag |= RX_FLAG_TSFT; + if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR) + rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; + memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); + ieee80211_rx_irqsafe(dev, skb); + + skb = dev_alloc_skb(RTL8187_MAX_RX); + if (unlikely(!skb)) { + /* TODO check rx queue length and refill *somewhere* */ + return; + } + + info = (struct rtl8187_rx_info *)skb->cb; + info->urb = urb; + info->dev = dev; + urb->transfer_buffer = skb_tail_pointer(skb); + urb->context = skb; + skb_queue_tail(&priv->rx_queue, skb); + + usb_anchor_urb(urb, &priv->anchored); + if (usb_submit_urb(urb, GFP_ATOMIC)) { + usb_unanchor_urb(urb); + skb_unlink(skb, &priv->rx_queue); + dev_kfree_skb_irq(skb); + } +} + +static int rtl8187_init_urbs(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + struct urb *entry = NULL; + struct sk_buff *skb; + struct rtl8187_rx_info *info; + int ret = 0; + + while (skb_queue_len(&priv->rx_queue) < 16) { + skb = __dev_alloc_skb(RTL8187_MAX_RX, GFP_KERNEL); + if (!skb) { + ret = -ENOMEM; + goto err; + } + entry = usb_alloc_urb(0, GFP_KERNEL); + if (!entry) { + ret = -ENOMEM; + goto err; + } + usb_fill_bulk_urb(entry, priv->udev, + usb_rcvbulkpipe(priv->udev, + priv->is_rtl8187b ? 3 : 1), + skb_tail_pointer(skb), + RTL8187_MAX_RX, rtl8187_rx_cb, skb); + info = (struct rtl8187_rx_info *)skb->cb; + info->urb = entry; + info->dev = dev; + skb_queue_tail(&priv->rx_queue, skb); + usb_anchor_urb(entry, &priv->anchored); + ret = usb_submit_urb(entry, GFP_KERNEL); + if (ret) { + skb_unlink(skb, &priv->rx_queue); + usb_unanchor_urb(entry); + goto err; + } + usb_free_urb(entry); + } + return ret; + +err: + usb_free_urb(entry); + kfree_skb(skb); + usb_kill_anchored_urbs(&priv->anchored); + return ret; +} + +static void rtl8187b_status_cb(struct urb *urb) +{ + struct ieee80211_hw *hw = (struct ieee80211_hw *)urb->context; + struct rtl8187_priv *priv = hw->priv; + u64 val; + unsigned int cmd_type; + + if (unlikely(urb->status)) + return; + + /* + * Read from status buffer: + * + * bits [30:31] = cmd type: + * - 0 indicates tx beacon interrupt + * - 1 indicates tx close descriptor + * + * In the case of tx beacon interrupt: + * [0:9] = Last Beacon CW + * [10:29] = reserved + * [30:31] = 00b + * [32:63] = Last Beacon TSF + * + * If it's tx close descriptor: + * [0:7] = Packet Retry Count + * [8:14] = RTS Retry Count + * [15] = TOK + * [16:27] = Sequence No + * [28] = LS + * [29] = FS + * [30:31] = 01b + * [32:47] = unused (reserved?) + * [48:63] = MAC Used Time + */ + val = le64_to_cpu(priv->b_tx_status.buf); + + cmd_type = (val >> 30) & 0x3; + if (cmd_type == 1) { + unsigned int pkt_rc, seq_no; + bool tok; + struct sk_buff *skb; + struct ieee80211_hdr *ieee80211hdr; + unsigned long flags; + + pkt_rc = val & 0xFF; + tok = val & (1 << 15); + seq_no = (val >> 16) & 0xFFF; + + spin_lock_irqsave(&priv->b_tx_status.queue.lock, flags); + skb_queue_reverse_walk(&priv->b_tx_status.queue, skb) { + ieee80211hdr = (struct ieee80211_hdr *)skb->data; + + /* + * While testing, it was discovered that the seq_no + * doesn't actually contains the sequence number. + * Instead of returning just the 12 bits of sequence + * number, hardware is returning entire sequence control + * (fragment number plus sequence number) in a 12 bit + * only field overflowing after some time. As a + * workaround, just consider the lower bits, and expect + * it's unlikely we wrongly ack some sent data + */ + if ((le16_to_cpu(ieee80211hdr->seq_ctrl) + & 0xFFF) == seq_no) + break; + } + if (skb != (struct sk_buff *) &priv->b_tx_status.queue) { + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + __skb_unlink(skb, &priv->b_tx_status.queue); + if (tok) + info->flags |= IEEE80211_TX_STAT_ACK; + info->status.rates[0].count = pkt_rc + 1; + + ieee80211_tx_status_irqsafe(hw, skb); + } + spin_unlock_irqrestore(&priv->b_tx_status.queue.lock, flags); + } + + usb_anchor_urb(urb, &priv->anchored); + if (usb_submit_urb(urb, GFP_ATOMIC)) + usb_unanchor_urb(urb); +} + +static int rtl8187b_init_status_urb(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + struct urb *entry; + int ret = 0; + + entry = usb_alloc_urb(0, GFP_KERNEL); + if (!entry) + return -ENOMEM; + + usb_fill_bulk_urb(entry, priv->udev, usb_rcvbulkpipe(priv->udev, 9), + &priv->b_tx_status.buf, sizeof(priv->b_tx_status.buf), + rtl8187b_status_cb, dev); + + usb_anchor_urb(entry, &priv->anchored); + ret = usb_submit_urb(entry, GFP_KERNEL); + if (ret) + usb_unanchor_urb(entry); + usb_free_urb(entry); + + return ret; +} + +static void rtl8187_set_anaparam(struct rtl8187_priv *priv, bool rfon) +{ + u32 anaparam, anaparam2; + u8 anaparam3, reg; + + if (!priv->is_rtl8187b) { + if (rfon) { + anaparam = RTL8187_RTL8225_ANAPARAM_ON; + anaparam2 = RTL8187_RTL8225_ANAPARAM2_ON; + } else { + anaparam = RTL8187_RTL8225_ANAPARAM_OFF; + anaparam2 = RTL8187_RTL8225_ANAPARAM2_OFF; + } + } else { + if (rfon) { + anaparam = RTL8187B_RTL8225_ANAPARAM_ON; + anaparam2 = RTL8187B_RTL8225_ANAPARAM2_ON; + anaparam3 = RTL8187B_RTL8225_ANAPARAM3_ON; + } else { + anaparam = RTL8187B_RTL8225_ANAPARAM_OFF; + anaparam2 = RTL8187B_RTL8225_ANAPARAM2_OFF; + anaparam3 = RTL8187B_RTL8225_ANAPARAM3_OFF; + } + } + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, + RTL818X_EEPROM_CMD_CONFIG); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); + reg |= RTL818X_CONFIG3_ANAPARAM_WRITE; + rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg); + rtl818x_iowrite32(priv, &priv->map->ANAPARAM, anaparam); + rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, anaparam2); + if (priv->is_rtl8187b) + rtl818x_iowrite8(priv, &priv->map->ANAPARAM3, anaparam3); + reg &= ~RTL818X_CONFIG3_ANAPARAM_WRITE; + rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, + RTL818X_EEPROM_CMD_NORMAL); +} + +static int rtl8187_cmd_reset(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + u8 reg; + int i; + + reg = rtl818x_ioread8(priv, &priv->map->CMD); + reg &= (1 << 1); + reg |= RTL818X_CMD_RESET; + rtl818x_iowrite8(priv, &priv->map->CMD, reg); + + i = 10; + do { + msleep(2); + if (!(rtl818x_ioread8(priv, &priv->map->CMD) & + RTL818X_CMD_RESET)) + break; + } while (--i); + + if (!i) { + wiphy_err(dev->wiphy, "Reset timeout!\n"); + return -ETIMEDOUT; + } + + /* reload registers from eeprom */ + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_LOAD); + + i = 10; + do { + msleep(4); + if (!(rtl818x_ioread8(priv, &priv->map->EEPROM_CMD) & + RTL818X_EEPROM_CMD_CONFIG)) + break; + } while (--i); + + if (!i) { + wiphy_err(dev->wiphy, "eeprom reset timeout!\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int rtl8187_init_hw(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + u8 reg; + int res; + + /* reset */ + rtl8187_set_anaparam(priv, true); + + rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0); + + msleep(200); + rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x10); + rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x11); + rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x00); + msleep(200); + + res = rtl8187_cmd_reset(dev); + if (res) + return res; + + rtl8187_set_anaparam(priv, true); + + /* setup card */ + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0); + rtl818x_iowrite8(priv, &priv->map->GPIO0, 0); + + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, (4 << 8)); + rtl818x_iowrite8(priv, &priv->map->GPIO0, 1); + rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0); + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + + rtl818x_iowrite16(priv, (__le16 *)0xFFF4, 0xFFFF); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG1); + reg &= 0x3F; + reg |= 0x80; + rtl818x_iowrite8(priv, &priv->map->CONFIG1, reg); + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + + rtl818x_iowrite32(priv, &priv->map->INT_TIMEOUT, 0); + rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0); + rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, 0); + + // TODO: set RESP_RATE and BRSR properly + rtl818x_iowrite8(priv, &priv->map->RESP_RATE, (8 << 4) | 0); + rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3); + + /* host_usb_init */ + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0); + rtl818x_iowrite8(priv, &priv->map->GPIO0, 0); + reg = rtl818x_ioread8(priv, (u8 *)0xFE53); + rtl818x_iowrite8(priv, (u8 *)0xFE53, reg | (1 << 7)); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, (4 << 8)); + rtl818x_iowrite8(priv, &priv->map->GPIO0, 0x20); + rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x80); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x80); + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x80); + msleep(100); + + rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x000a8008); + rtl818x_iowrite16(priv, &priv->map->BRSR, 0xFFFF); + rtl818x_iowrite32(priv, &priv->map->RF_PARA, 0x00100044); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, + RTL818X_EEPROM_CMD_CONFIG); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, 0x44); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, + RTL818X_EEPROM_CMD_NORMAL); + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FF7); + msleep(100); + + priv->rf->init(dev); + + rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3); + reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~1; + rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg | 1); + rtl818x_iowrite16(priv, (__le16 *)0xFFFE, 0x10); + rtl818x_iowrite8(priv, &priv->map->TALLY_SEL, 0x80); + rtl818x_iowrite8(priv, (u8 *)0xFFFF, 0x60); + rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); + + return 0; +} + +static const u8 rtl8187b_reg_table[][3] = { + {0xF0, 0x32, 0}, {0xF1, 0x32, 0}, {0xF2, 0x00, 0}, {0xF3, 0x00, 0}, + {0xF4, 0x32, 0}, {0xF5, 0x43, 0}, {0xF6, 0x00, 0}, {0xF7, 0x00, 0}, + {0xF8, 0x46, 0}, {0xF9, 0xA4, 0}, {0xFA, 0x00, 0}, {0xFB, 0x00, 0}, + {0xFC, 0x96, 0}, {0xFD, 0xA4, 0}, {0xFE, 0x00, 0}, {0xFF, 0x00, 0}, + + {0x58, 0x4B, 1}, {0x59, 0x00, 1}, {0x5A, 0x4B, 1}, {0x5B, 0x00, 1}, + {0x60, 0x4B, 1}, {0x61, 0x09, 1}, {0x62, 0x4B, 1}, {0x63, 0x09, 1}, + {0xCE, 0x0F, 1}, {0xCF, 0x00, 1}, {0xF0, 0x4E, 1}, {0xF1, 0x01, 1}, + {0xF2, 0x02, 1}, {0xF3, 0x03, 1}, {0xF4, 0x04, 1}, {0xF5, 0x05, 1}, + {0xF6, 0x06, 1}, {0xF7, 0x07, 1}, {0xF8, 0x08, 1}, + + {0x4E, 0x00, 2}, {0x0C, 0x04, 2}, {0x21, 0x61, 2}, {0x22, 0x68, 2}, + {0x23, 0x6F, 2}, {0x24, 0x76, 2}, {0x25, 0x7D, 2}, {0x26, 0x84, 2}, + {0x27, 0x8D, 2}, {0x4D, 0x08, 2}, {0x50, 0x05, 2}, {0x51, 0xF5, 2}, + {0x52, 0x04, 2}, {0x53, 0xA0, 2}, {0x54, 0x1F, 2}, {0x55, 0x23, 2}, + {0x56, 0x45, 2}, {0x57, 0x67, 2}, {0x58, 0x08, 2}, {0x59, 0x08, 2}, + {0x5A, 0x08, 2}, {0x5B, 0x08, 2}, {0x60, 0x08, 2}, {0x61, 0x08, 2}, + {0x62, 0x08, 2}, {0x63, 0x08, 2}, {0x64, 0xCF, 2}, + + {0x5B, 0x40, 0}, {0x84, 0x88, 0}, {0x85, 0x24, 0}, {0x88, 0x54, 0}, + {0x8B, 0xB8, 0}, {0x8C, 0x07, 0}, {0x8D, 0x00, 0}, {0x94, 0x1B, 0}, + {0x95, 0x12, 0}, {0x96, 0x00, 0}, {0x97, 0x06, 0}, {0x9D, 0x1A, 0}, + {0x9F, 0x10, 0}, {0xB4, 0x22, 0}, {0xBE, 0x80, 0}, {0xDB, 0x00, 0}, + {0xEE, 0x00, 0}, {0x4C, 0x00, 2}, + + {0x9F, 0x00, 3}, {0x8C, 0x01, 0}, {0x8D, 0x10, 0}, {0x8E, 0x08, 0}, + {0x8F, 0x00, 0} +}; + +static int rtl8187b_init_hw(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + int res, i; + u8 reg; + + rtl8187_set_anaparam(priv, true); + + /* Reset PLL sequence on 8187B. Realtek note: reduces power + * consumption about 30 mA */ + rtl818x_iowrite8(priv, (u8 *)0xFF61, 0x10); + reg = rtl818x_ioread8(priv, (u8 *)0xFF62); + rtl818x_iowrite8(priv, (u8 *)0xFF62, reg & ~(1 << 5)); + rtl818x_iowrite8(priv, (u8 *)0xFF62, reg | (1 << 5)); + + res = rtl8187_cmd_reset(dev); + if (res) + return res; + + rtl8187_set_anaparam(priv, true); + + /* BRSR (Basic Rate Set Register) on 8187B looks to be the same as + * RESP_RATE on 8187L in Realtek sources: each bit should be each + * one of the 12 rates, all are enabled */ + rtl818x_iowrite16(priv, (__le16 *)0xFF34, 0x0FFF); + + reg = rtl818x_ioread8(priv, &priv->map->CW_CONF); + reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT; + rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg); + + /* Auto Rate Fallback Register (ARFR): 1M-54M setting */ + rtl818x_iowrite16_idx(priv, (__le16 *)0xFFE0, 0x0FFF, 1); + rtl818x_iowrite8_idx(priv, (u8 *)0xFFE2, 0x00, 1); + + rtl818x_iowrite16_idx(priv, (__le16 *)0xFFD4, 0xFFFF, 1); + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, + RTL818X_EEPROM_CMD_CONFIG); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG1); + rtl818x_iowrite8(priv, &priv->map->CONFIG1, (reg & 0x3F) | 0x80); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, + RTL818X_EEPROM_CMD_NORMAL); + + rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0); + for (i = 0; i < ARRAY_SIZE(rtl8187b_reg_table); i++) { + rtl818x_iowrite8_idx(priv, + (u8 *)(uintptr_t) + (rtl8187b_reg_table[i][0] | 0xFF00), + rtl8187b_reg_table[i][1], + rtl8187b_reg_table[i][2]); + } + + rtl818x_iowrite16(priv, &priv->map->TID_AC_MAP, 0xFA50); + rtl818x_iowrite16(priv, &priv->map->INT_MIG, 0); + + rtl818x_iowrite32_idx(priv, (__le32 *)0xFFF0, 0, 1); + rtl818x_iowrite32_idx(priv, (__le32 *)0xFFF4, 0, 1); + rtl818x_iowrite8_idx(priv, (u8 *)0xFFF8, 0, 1); + + rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x00004001); + + /* RFSW_CTRL register */ + rtl818x_iowrite16_idx(priv, (__le16 *)0xFF72, 0x569A, 2); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x2488); + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); + msleep(100); + + priv->rf->init(dev); + + reg = RTL818X_CMD_TX_ENABLE | RTL818X_CMD_RX_ENABLE; + rtl818x_iowrite8(priv, &priv->map->CMD, reg); + rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF); + + rtl818x_iowrite8(priv, (u8 *)0xFE41, 0xF4); + rtl818x_iowrite8(priv, (u8 *)0xFE40, 0x00); + rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x00); + rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x01); + rtl818x_iowrite8(priv, (u8 *)0xFE40, 0x0F); + rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x00); + rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x01); + + reg = rtl818x_ioread8(priv, (u8 *)0xFFDB); + rtl818x_iowrite8(priv, (u8 *)0xFFDB, reg | (1 << 2)); + rtl818x_iowrite16_idx(priv, (__le16 *)0xFF72, 0x59FA, 3); + rtl818x_iowrite16_idx(priv, (__le16 *)0xFF74, 0x59D2, 3); + rtl818x_iowrite16_idx(priv, (__le16 *)0xFF76, 0x59D2, 3); + rtl818x_iowrite16_idx(priv, (__le16 *)0xFF78, 0x19FA, 3); + rtl818x_iowrite16_idx(priv, (__le16 *)0xFF7A, 0x19FA, 3); + rtl818x_iowrite16_idx(priv, (__le16 *)0xFF7C, 0x00D0, 3); + rtl818x_iowrite8(priv, (u8 *)0xFF61, 0); + rtl818x_iowrite8_idx(priv, (u8 *)0xFF80, 0x0F, 1); + rtl818x_iowrite8_idx(priv, (u8 *)0xFF83, 0x03, 1); + rtl818x_iowrite8(priv, (u8 *)0xFFDA, 0x10); + rtl818x_iowrite8_idx(priv, (u8 *)0xFF4D, 0x08, 2); + + rtl818x_iowrite32(priv, &priv->map->HSSI_PARA, 0x0600321B); + + rtl818x_iowrite16_idx(priv, (__le16 *)0xFFEC, 0x0800, 1); + + priv->slot_time = 0x9; + priv->aifsn[0] = 2; /* AIFSN[AC_VO] */ + priv->aifsn[1] = 2; /* AIFSN[AC_VI] */ + priv->aifsn[2] = 7; /* AIFSN[AC_BK] */ + priv->aifsn[3] = 3; /* AIFSN[AC_BE] */ + rtl818x_iowrite8(priv, &priv->map->ACM_CONTROL, 0); + + /* ENEDCA flag must always be set, transmit issues? */ + rtl818x_iowrite8(priv, &priv->map->MSR, RTL818X_MSR_ENEDCA); + + return 0; +} + +static void rtl8187_work(struct work_struct *work) +{ + /* The RTL8187 returns the retry count through register 0xFFFA. In + * addition, it appears to be a cumulative retry count, not the + * value for the current TX packet. When multiple TX entries are + * queued, the retry count will be valid for the last one in the queue. + * The "error" should not matter for purposes of rate setting. */ + struct rtl8187_priv *priv = container_of(work, struct rtl8187_priv, + work.work); + struct ieee80211_tx_info *info; + struct ieee80211_hw *dev = priv->dev; + static u16 retry; + u16 tmp; + + mutex_lock(&priv->conf_mutex); + tmp = rtl818x_ioread16(priv, (__le16 *)0xFFFA); + while (skb_queue_len(&priv->b_tx_status.queue) > 0) { + struct sk_buff *old_skb; + + old_skb = skb_dequeue(&priv->b_tx_status.queue); + info = IEEE80211_SKB_CB(old_skb); + info->status.rates[0].count = tmp - retry + 1; + ieee80211_tx_status_irqsafe(dev, old_skb); + } + retry = tmp; + mutex_unlock(&priv->conf_mutex); +} + +static int rtl8187_start(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + u32 reg; + int ret; + + mutex_lock(&priv->conf_mutex); + + ret = (!priv->is_rtl8187b) ? rtl8187_init_hw(dev) : + rtl8187b_init_hw(dev); + if (ret) + goto rtl8187_start_exit; + + init_usb_anchor(&priv->anchored); + priv->dev = dev; + + if (priv->is_rtl8187b) { + reg = RTL818X_RX_CONF_MGMT | + RTL818X_RX_CONF_DATA | + RTL818X_RX_CONF_BROADCAST | + RTL818X_RX_CONF_NICMAC | + RTL818X_RX_CONF_BSSID | + (7 << 13 /* RX FIFO threshold NONE */) | + (7 << 10 /* MAX RX DMA */) | + RTL818X_RX_CONF_RX_AUTORESETPHY | + RTL818X_RX_CONF_ONLYERLPKT | + RTL818X_RX_CONF_MULTICAST; + priv->rx_conf = reg; + rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg); + + reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL); + reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT; + reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT; + reg &= ~RTL818X_TX_AGC_CTL_FEEDBACK_ANT; + rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg); + + rtl818x_iowrite32(priv, &priv->map->TX_CONF, + RTL818X_TX_CONF_HW_SEQNUM | + RTL818X_TX_CONF_DISREQQSIZE | + (7 << 8 /* short retry limit */) | + (7 << 0 /* long retry limit */) | + (7 << 21 /* MAX TX DMA */)); + rtl8187_init_urbs(dev); + rtl8187b_init_status_urb(dev); + goto rtl8187_start_exit; + } + + rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF); + + rtl818x_iowrite32(priv, &priv->map->MAR[0], ~0); + rtl818x_iowrite32(priv, &priv->map->MAR[1], ~0); + + rtl8187_init_urbs(dev); + + reg = RTL818X_RX_CONF_ONLYERLPKT | + RTL818X_RX_CONF_RX_AUTORESETPHY | + RTL818X_RX_CONF_BSSID | + RTL818X_RX_CONF_MGMT | + RTL818X_RX_CONF_DATA | + (7 << 13 /* RX FIFO threshold NONE */) | + (7 << 10 /* MAX RX DMA */) | + RTL818X_RX_CONF_BROADCAST | + RTL818X_RX_CONF_NICMAC; + + priv->rx_conf = reg; + rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg); + + reg = rtl818x_ioread8(priv, &priv->map->CW_CONF); + reg &= ~RTL818X_CW_CONF_PERPACKET_CW_SHIFT; + reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT; + rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg); + + reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL); + reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT; + reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT; + reg &= ~RTL818X_TX_AGC_CTL_FEEDBACK_ANT; + rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg); + + reg = RTL818X_TX_CONF_CW_MIN | + (7 << 21 /* MAX TX DMA */) | + RTL818X_TX_CONF_NO_ICV; + rtl818x_iowrite32(priv, &priv->map->TX_CONF, reg); + + reg = rtl818x_ioread8(priv, &priv->map->CMD); + reg |= RTL818X_CMD_TX_ENABLE; + reg |= RTL818X_CMD_RX_ENABLE; + rtl818x_iowrite8(priv, &priv->map->CMD, reg); + INIT_DELAYED_WORK(&priv->work, rtl8187_work); + +rtl8187_start_exit: + mutex_unlock(&priv->conf_mutex); + return ret; +} + +static void rtl8187_stop(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + struct sk_buff *skb; + u32 reg; + + mutex_lock(&priv->conf_mutex); + rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0); + + reg = rtl818x_ioread8(priv, &priv->map->CMD); + reg &= ~RTL818X_CMD_TX_ENABLE; + reg &= ~RTL818X_CMD_RX_ENABLE; + rtl818x_iowrite8(priv, &priv->map->CMD, reg); + + priv->rf->stop(dev); + rtl8187_set_anaparam(priv, false); + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG4); + rtl818x_iowrite8(priv, &priv->map->CONFIG4, reg | RTL818X_CONFIG4_VCOOFF); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + + while ((skb = skb_dequeue(&priv->b_tx_status.queue))) + dev_kfree_skb_any(skb); + + usb_kill_anchored_urbs(&priv->anchored); + mutex_unlock(&priv->conf_mutex); + + if (!priv->is_rtl8187b) + cancel_delayed_work_sync(&priv->work); +} + +static int rtl8187_add_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif) +{ + struct rtl8187_priv *priv = dev->priv; + int i; + int ret = -EOPNOTSUPP; + + mutex_lock(&priv->conf_mutex); + if (priv->vif) + goto exit; + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + break; + default: + goto exit; + } + + ret = 0; + priv->vif = vif; + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + for (i = 0; i < ETH_ALEN; i++) + rtl818x_iowrite8(priv, &priv->map->MAC[i], + ((u8 *)vif->addr)[i]); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + +exit: + mutex_unlock(&priv->conf_mutex); + return ret; +} + +static void rtl8187_remove_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif) +{ + struct rtl8187_priv *priv = dev->priv; + mutex_lock(&priv->conf_mutex); + priv->vif = NULL; + mutex_unlock(&priv->conf_mutex); +} + +static int rtl8187_config(struct ieee80211_hw *dev, u32 changed) +{ + struct rtl8187_priv *priv = dev->priv; + struct ieee80211_conf *conf = &dev->conf; + u32 reg; + + mutex_lock(&priv->conf_mutex); + reg = rtl818x_ioread32(priv, &priv->map->TX_CONF); + /* Enable TX loopback on MAC level to avoid TX during channel + * changes, as this has be seen to causes problems and the + * card will stop work until next reset + */ + rtl818x_iowrite32(priv, &priv->map->TX_CONF, + reg | RTL818X_TX_CONF_LOOPBACK_MAC); + priv->rf->set_chan(dev, conf); + msleep(10); + rtl818x_iowrite32(priv, &priv->map->TX_CONF, reg); + + rtl818x_iowrite16(priv, &priv->map->ATIM_WND, 2); + rtl818x_iowrite16(priv, &priv->map->ATIMTR_INTERVAL, 100); + rtl818x_iowrite16(priv, &priv->map->BEACON_INTERVAL, 100); + rtl818x_iowrite16(priv, &priv->map->BEACON_INTERVAL_TIME, 100); + mutex_unlock(&priv->conf_mutex); + return 0; +} + +/* + * With 8187B, AC_*_PARAM clashes with FEMR definition in struct rtl818x_csr for + * example. Thus we have to use raw values for AC_*_PARAM register addresses. + */ +static __le32 *rtl8187b_ac_addr[4] = { + (__le32 *) 0xFFF0, /* AC_VO */ + (__le32 *) 0xFFF4, /* AC_VI */ + (__le32 *) 0xFFFC, /* AC_BK */ + (__le32 *) 0xFFF8, /* AC_BE */ +}; + +#define SIFS_TIME 0xa + +static void rtl8187_conf_erp(struct rtl8187_priv *priv, bool use_short_slot, + bool use_short_preamble) +{ + if (priv->is_rtl8187b) { + u8 difs, eifs; + u16 ack_timeout; + int queue; + + if (use_short_slot) { + priv->slot_time = 0x9; + difs = 0x1c; + eifs = 0x53; + } else { + priv->slot_time = 0x14; + difs = 0x32; + eifs = 0x5b; + } + rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22); + rtl818x_iowrite8(priv, &priv->map->SLOT, priv->slot_time); + rtl818x_iowrite8(priv, &priv->map->DIFS, difs); + + /* + * BRSR+1 on 8187B is in fact EIFS register + * Value in units of 4 us + */ + rtl818x_iowrite8(priv, (u8 *)&priv->map->BRSR + 1, eifs); + + /* + * For 8187B, CARRIER_SENSE_COUNTER is in fact ack timeout + * register. In units of 4 us like eifs register + * ack_timeout = ack duration + plcp + difs + preamble + */ + ack_timeout = 112 + 48 + difs; + if (use_short_preamble) + ack_timeout += 72; + else + ack_timeout += 144; + rtl818x_iowrite8(priv, &priv->map->CARRIER_SENSE_COUNTER, + DIV_ROUND_UP(ack_timeout, 4)); + + for (queue = 0; queue < 4; queue++) + rtl818x_iowrite8(priv, (u8 *) rtl8187b_ac_addr[queue], + priv->aifsn[queue] * priv->slot_time + + SIFS_TIME); + } else { + rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22); + if (use_short_slot) { + rtl818x_iowrite8(priv, &priv->map->SLOT, 0x9); + rtl818x_iowrite8(priv, &priv->map->DIFS, 0x14); + rtl818x_iowrite8(priv, &priv->map->EIFS, 91 - 0x14); + } else { + rtl818x_iowrite8(priv, &priv->map->SLOT, 0x14); + rtl818x_iowrite8(priv, &priv->map->DIFS, 0x24); + rtl818x_iowrite8(priv, &priv->map->EIFS, 91 - 0x24); + } + } +} + +static void rtl8187_bss_info_changed(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u32 changed) +{ + struct rtl8187_priv *priv = dev->priv; + int i; + u8 reg; + + if (changed & BSS_CHANGED_BSSID) { + mutex_lock(&priv->conf_mutex); + for (i = 0; i < ETH_ALEN; i++) + rtl818x_iowrite8(priv, &priv->map->BSSID[i], + info->bssid[i]); + + if (priv->is_rtl8187b) + reg = RTL818X_MSR_ENEDCA; + else + reg = 0; + + if (is_valid_ether_addr(info->bssid)) + reg |= RTL818X_MSR_INFRA; + else + reg |= RTL818X_MSR_NO_LINK; + + rtl818x_iowrite8(priv, &priv->map->MSR, reg); + + mutex_unlock(&priv->conf_mutex); + } + + if (changed & (BSS_CHANGED_ERP_SLOT | BSS_CHANGED_ERP_PREAMBLE)) + rtl8187_conf_erp(priv, info->use_short_slot, + info->use_short_preamble); +} + +static u64 rtl8187_prepare_multicast(struct ieee80211_hw *dev, + struct netdev_hw_addr_list *mc_list) +{ + return netdev_hw_addr_list_count(mc_list); +} + +static void rtl8187_configure_filter(struct ieee80211_hw *dev, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct rtl8187_priv *priv = dev->priv; + + if (changed_flags & FIF_FCSFAIL) + priv->rx_conf ^= RTL818X_RX_CONF_FCS; + if (changed_flags & FIF_CONTROL) + priv->rx_conf ^= RTL818X_RX_CONF_CTRL; + if (changed_flags & FIF_OTHER_BSS) + priv->rx_conf ^= RTL818X_RX_CONF_MONITOR; + if (*total_flags & FIF_ALLMULTI || multicast > 0) + priv->rx_conf |= RTL818X_RX_CONF_MULTICAST; + else + priv->rx_conf &= ~RTL818X_RX_CONF_MULTICAST; + + *total_flags = 0; + + if (priv->rx_conf & RTL818X_RX_CONF_FCS) + *total_flags |= FIF_FCSFAIL; + if (priv->rx_conf & RTL818X_RX_CONF_CTRL) + *total_flags |= FIF_CONTROL; + if (priv->rx_conf & RTL818X_RX_CONF_MONITOR) + *total_flags |= FIF_OTHER_BSS; + if (priv->rx_conf & RTL818X_RX_CONF_MULTICAST) + *total_flags |= FIF_ALLMULTI; + + rtl818x_iowrite32_async(priv, &priv->map->RX_CONF, priv->rx_conf); +} + +static int rtl8187_conf_tx(struct ieee80211_hw *dev, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct rtl8187_priv *priv = dev->priv; + u8 cw_min, cw_max; + + if (queue > 3) + return -EINVAL; + + cw_min = fls(params->cw_min); + cw_max = fls(params->cw_max); + + if (priv->is_rtl8187b) { + priv->aifsn[queue] = params->aifs; + + /* + * This is the structure of AC_*_PARAM registers in 8187B: + * - TXOP limit field, bit offset = 16 + * - ECWmax, bit offset = 12 + * - ECWmin, bit offset = 8 + * - AIFS, bit offset = 0 + */ + rtl818x_iowrite32(priv, rtl8187b_ac_addr[queue], + (params->txop << 16) | (cw_max << 12) | + (cw_min << 8) | (params->aifs * + priv->slot_time + SIFS_TIME)); + } else { + if (queue != 0) + return -EINVAL; + + rtl818x_iowrite8(priv, &priv->map->CW_VAL, + cw_min | (cw_max << 4)); + } + return 0; +} + +static u64 rtl8187_get_tsf(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + + return rtl818x_ioread32(priv, &priv->map->TSFT[0]) | + (u64)(rtl818x_ioread32(priv, &priv->map->TSFT[1])) << 32; +} + +static const struct ieee80211_ops rtl8187_ops = { + .tx = rtl8187_tx, + .start = rtl8187_start, + .stop = rtl8187_stop, + .add_interface = rtl8187_add_interface, + .remove_interface = rtl8187_remove_interface, + .config = rtl8187_config, + .bss_info_changed = rtl8187_bss_info_changed, + .prepare_multicast = rtl8187_prepare_multicast, + .configure_filter = rtl8187_configure_filter, + .conf_tx = rtl8187_conf_tx, + .rfkill_poll = rtl8187_rfkill_poll, + .get_tsf = rtl8187_get_tsf, +}; + +static void rtl8187_eeprom_register_read(struct eeprom_93cx6 *eeprom) +{ + struct ieee80211_hw *dev = eeprom->data; + struct rtl8187_priv *priv = dev->priv; + u8 reg = rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + + eeprom->reg_data_in = reg & RTL818X_EEPROM_CMD_WRITE; + eeprom->reg_data_out = reg & RTL818X_EEPROM_CMD_READ; + eeprom->reg_data_clock = reg & RTL818X_EEPROM_CMD_CK; + eeprom->reg_chip_select = reg & RTL818X_EEPROM_CMD_CS; +} + +static void rtl8187_eeprom_register_write(struct eeprom_93cx6 *eeprom) +{ + struct ieee80211_hw *dev = eeprom->data; + struct rtl8187_priv *priv = dev->priv; + u8 reg = RTL818X_EEPROM_CMD_PROGRAM; + + if (eeprom->reg_data_in) + reg |= RTL818X_EEPROM_CMD_WRITE; + if (eeprom->reg_data_out) + reg |= RTL818X_EEPROM_CMD_READ; + if (eeprom->reg_data_clock) + reg |= RTL818X_EEPROM_CMD_CK; + if (eeprom->reg_chip_select) + reg |= RTL818X_EEPROM_CMD_CS; + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, reg); + udelay(10); +} + +static int __devinit rtl8187_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct ieee80211_hw *dev; + struct rtl8187_priv *priv; + struct eeprom_93cx6 eeprom; + struct ieee80211_channel *channel; + const char *chip_name; + u16 txpwr, reg; + u16 product_id = le16_to_cpu(udev->descriptor.idProduct); + int err, i; + u8 mac_addr[ETH_ALEN]; + + dev = ieee80211_alloc_hw(sizeof(*priv), &rtl8187_ops); + if (!dev) { + printk(KERN_ERR "rtl8187: ieee80211 alloc failed\n"); + return -ENOMEM; + } + + priv = dev->priv; + priv->is_rtl8187b = (id->driver_info == DEVICE_RTL8187B); + + /* allocate "DMA aware" buffer for register accesses */ + priv->io_dmabuf = kmalloc(sizeof(*priv->io_dmabuf), GFP_KERNEL); + if (!priv->io_dmabuf) { + err = -ENOMEM; + goto err_free_dev; + } + mutex_init(&priv->io_mutex); + + SET_IEEE80211_DEV(dev, &intf->dev); + usb_set_intfdata(intf, dev); + priv->udev = udev; + + usb_get_dev(udev); + + skb_queue_head_init(&priv->rx_queue); + + BUILD_BUG_ON(sizeof(priv->channels) != sizeof(rtl818x_channels)); + BUILD_BUG_ON(sizeof(priv->rates) != sizeof(rtl818x_rates)); + + memcpy(priv->channels, rtl818x_channels, sizeof(rtl818x_channels)); + memcpy(priv->rates, rtl818x_rates, sizeof(rtl818x_rates)); + priv->map = (struct rtl818x_csr *)0xFF00; + + priv->band.band = IEEE80211_BAND_2GHZ; + priv->band.channels = priv->channels; + priv->band.n_channels = ARRAY_SIZE(rtl818x_channels); + priv->band.bitrates = priv->rates; + priv->band.n_bitrates = ARRAY_SIZE(rtl818x_rates); + dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; + + + dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_RX_INCLUDES_FCS; + + eeprom.data = dev; + eeprom.register_read = rtl8187_eeprom_register_read; + eeprom.register_write = rtl8187_eeprom_register_write; + if (rtl818x_ioread32(priv, &priv->map->RX_CONF) & (1 << 6)) + eeprom.width = PCI_EEPROM_WIDTH_93C66; + else + eeprom.width = PCI_EEPROM_WIDTH_93C46; + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + udelay(10); + + eeprom_93cx6_multiread(&eeprom, RTL8187_EEPROM_MAC_ADDR, + (__le16 __force *)mac_addr, 3); + if (!is_valid_ether_addr(mac_addr)) { + printk(KERN_WARNING "rtl8187: Invalid hwaddr! Using randomly " + "generated MAC address\n"); + random_ether_addr(mac_addr); + } + SET_IEEE80211_PERM_ADDR(dev, mac_addr); + + channel = priv->channels; + for (i = 0; i < 3; i++) { + eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_CHAN_1 + i, + &txpwr); + (*channel++).hw_value = txpwr & 0xFF; + (*channel++).hw_value = txpwr >> 8; + } + for (i = 0; i < 2; i++) { + eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_CHAN_4 + i, + &txpwr); + (*channel++).hw_value = txpwr & 0xFF; + (*channel++).hw_value = txpwr >> 8; + } + + eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_BASE, + &priv->txpwr_base); + + reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~1; + rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg | 1); + /* 0 means asic B-cut, we should use SW 3 wire + * bit-by-bit banging for radio. 1 means we can use + * USB specific request to write radio registers */ + priv->asic_rev = rtl818x_ioread8(priv, (u8 *)0xFFFE) & 0x3; + rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + + if (!priv->is_rtl8187b) { + u32 reg32; + reg32 = rtl818x_ioread32(priv, &priv->map->TX_CONF); + reg32 &= RTL818X_TX_CONF_HWVER_MASK; + switch (reg32) { + case RTL818X_TX_CONF_R8187vD_B: + /* Some RTL8187B devices have a USB ID of 0x8187 + * detect them here */ + chip_name = "RTL8187BvB(early)"; + priv->is_rtl8187b = 1; + priv->hw_rev = RTL8187BvB; + break; + case RTL818X_TX_CONF_R8187vD: + chip_name = "RTL8187vD"; + break; + default: + chip_name = "RTL8187vB (default)"; + } + } else { + /* + * Force USB request to write radio registers for 8187B, Realtek + * only uses it in their sources + */ + /*if (priv->asic_rev == 0) { + printk(KERN_WARNING "rtl8187: Forcing use of USB " + "requests to write to radio registers\n"); + priv->asic_rev = 1; + }*/ + switch (rtl818x_ioread8(priv, (u8 *)0xFFE1)) { + case RTL818X_R8187B_B: + chip_name = "RTL8187BvB"; + priv->hw_rev = RTL8187BvB; + break; + case RTL818X_R8187B_D: + chip_name = "RTL8187BvD"; + priv->hw_rev = RTL8187BvD; + break; + case RTL818X_R8187B_E: + chip_name = "RTL8187BvE"; + priv->hw_rev = RTL8187BvE; + break; + default: + chip_name = "RTL8187BvB (default)"; + priv->hw_rev = RTL8187BvB; + } + } + + if (!priv->is_rtl8187b) { + for (i = 0; i < 2; i++) { + eeprom_93cx6_read(&eeprom, + RTL8187_EEPROM_TXPWR_CHAN_6 + i, + &txpwr); + (*channel++).hw_value = txpwr & 0xFF; + (*channel++).hw_value = txpwr >> 8; + } + } else { + eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_CHAN_6, + &txpwr); + (*channel++).hw_value = txpwr & 0xFF; + + eeprom_93cx6_read(&eeprom, 0x0A, &txpwr); + (*channel++).hw_value = txpwr & 0xFF; + + eeprom_93cx6_read(&eeprom, 0x1C, &txpwr); + (*channel++).hw_value = txpwr & 0xFF; + (*channel++).hw_value = txpwr >> 8; + } + /* Handle the differing rfkill GPIO bit in different models */ + priv->rfkill_mask = RFKILL_MASK_8187_89_97; + if (product_id == 0x8197 || product_id == 0x8198) { + eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_SELECT_GPIO, ®); + if (reg & 0xFF00) + priv->rfkill_mask = RFKILL_MASK_8198; + } + + /* + * XXX: Once this driver supports anything that requires + * beacons it must implement IEEE80211_TX_CTL_ASSIGN_SEQ. + */ + dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); + + if ((id->driver_info == DEVICE_RTL8187) && priv->is_rtl8187b) + printk(KERN_INFO "rtl8187: inconsistency between id with OEM" + " info!\n"); + + priv->rf = rtl8187_detect_rf(dev); + dev->extra_tx_headroom = (!priv->is_rtl8187b) ? + sizeof(struct rtl8187_tx_hdr) : + sizeof(struct rtl8187b_tx_hdr); + if (!priv->is_rtl8187b) + dev->queues = 1; + else + dev->queues = 4; + + err = ieee80211_register_hw(dev); + if (err) { + printk(KERN_ERR "rtl8187: Cannot register device\n"); + goto err_free_dmabuf; + } + mutex_init(&priv->conf_mutex); + skb_queue_head_init(&priv->b_tx_status.queue); + + wiphy_info(dev->wiphy, "hwaddr %pM, %s V%d + %s, rfkill mask %d\n", + mac_addr, chip_name, priv->asic_rev, priv->rf->name, + priv->rfkill_mask); + +#ifdef CONFIG_RTL8187_LEDS + eeprom_93cx6_read(&eeprom, 0x3F, ®); + reg &= 0xFF; + rtl8187_leds_init(dev, reg); +#endif + rtl8187_rfkill_init(dev); + + return 0; + + err_free_dmabuf: + kfree(priv->io_dmabuf); + err_free_dev: + ieee80211_free_hw(dev); + usb_set_intfdata(intf, NULL); + usb_put_dev(udev); + return err; +} + +static void __devexit rtl8187_disconnect(struct usb_interface *intf) +{ + struct ieee80211_hw *dev = usb_get_intfdata(intf); + struct rtl8187_priv *priv; + + if (!dev) + return; + +#ifdef CONFIG_RTL8187_LEDS + rtl8187_leds_exit(dev); +#endif + rtl8187_rfkill_exit(dev); + ieee80211_unregister_hw(dev); + + priv = dev->priv; + usb_reset_device(priv->udev); + usb_put_dev(interface_to_usbdev(intf)); + kfree(priv->io_dmabuf); + ieee80211_free_hw(dev); +} + +static struct usb_driver rtl8187_driver = { + .name = KBUILD_MODNAME, + .id_table = rtl8187_table, + .probe = rtl8187_probe, + .disconnect = __devexit_p(rtl8187_disconnect), +}; + +static int __init rtl8187_init(void) +{ + return usb_register(&rtl8187_driver); +} + +static void __exit rtl8187_exit(void) +{ + usb_deregister(&rtl8187_driver); +} + +module_init(rtl8187_init); +module_exit(rtl8187_exit); diff --git a/drivers/net/wireless/rtl818x/rtl8187/leds.c b/drivers/net/wireless/rtl818x/rtl8187/leds.c new file mode 100644 index 000000000000..2e0de2f5f0f9 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8187/leds.c @@ -0,0 +1,245 @@ +/* + * Linux LED driver for RTL8187 + * + * Copyright 2009 Larry Finger + * + * Based on the LED handling in the r8187 driver, which is: + * Copyright (c) Realtek Semiconductor Corp. All rights reserved. + * + * Thanks to Realtek for their support! + * + * 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. + */ + +#ifdef CONFIG_RTL8187_LEDS + +#include +#include +#include + +#include "rtl8187.h" +#include "leds.h" + +static void led_turn_on(struct work_struct *work) +{ + /* As this routine does read/write operations on the hardware, it must + * be run from a work queue. + */ + u8 reg; + struct rtl8187_priv *priv = container_of(work, struct rtl8187_priv, + led_on.work); + struct rtl8187_led *led = &priv->led_tx; + + /* Don't change the LED, when the device is down. */ + if (!priv->vif || priv->vif->type == NL80211_IFTYPE_UNSPECIFIED) + return ; + + /* Skip if the LED is not registered. */ + if (!led->dev) + return; + mutex_lock(&priv->conf_mutex); + switch (led->ledpin) { + case LED_PIN_GPIO0: + rtl818x_iowrite8(priv, &priv->map->GPIO0, 0x01); + rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0x00); + break; + case LED_PIN_LED0: + reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~(1 << 4); + rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); + break; + case LED_PIN_LED1: + reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~(1 << 5); + rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); + break; + case LED_PIN_HW: + default: + break; + } + mutex_unlock(&priv->conf_mutex); +} + +static void led_turn_off(struct work_struct *work) +{ + /* As this routine does read/write operations on the hardware, it must + * be run from a work queue. + */ + u8 reg; + struct rtl8187_priv *priv = container_of(work, struct rtl8187_priv, + led_off.work); + struct rtl8187_led *led = &priv->led_tx; + + /* Don't change the LED, when the device is down. */ + if (!priv->vif || priv->vif->type == NL80211_IFTYPE_UNSPECIFIED) + return ; + + /* Skip if the LED is not registered. */ + if (!led->dev) + return; + mutex_lock(&priv->conf_mutex); + switch (led->ledpin) { + case LED_PIN_GPIO0: + rtl818x_iowrite8(priv, &priv->map->GPIO0, 0x01); + rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0x01); + break; + case LED_PIN_LED0: + reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) | (1 << 4); + rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); + break; + case LED_PIN_LED1: + reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) | (1 << 5); + rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); + break; + case LED_PIN_HW: + default: + break; + } + mutex_unlock(&priv->conf_mutex); +} + +/* Callback from the LED subsystem. */ +static void rtl8187_led_brightness_set(struct led_classdev *led_dev, + enum led_brightness brightness) +{ + struct rtl8187_led *led = container_of(led_dev, struct rtl8187_led, + led_dev); + struct ieee80211_hw *hw = led->dev; + struct rtl8187_priv *priv; + static bool radio_on; + + if (!hw) + return; + priv = hw->priv; + if (led->is_radio) { + if (brightness == LED_FULL) { + ieee80211_queue_delayed_work(hw, &priv->led_on, 0); + radio_on = true; + } else if (radio_on) { + radio_on = false; + cancel_delayed_work_sync(&priv->led_on); + ieee80211_queue_delayed_work(hw, &priv->led_off, 0); + } + } else if (radio_on) { + if (brightness == LED_OFF) { + ieee80211_queue_delayed_work(hw, &priv->led_off, 0); + /* The LED is off for 1/20 sec - it just blinks. */ + ieee80211_queue_delayed_work(hw, &priv->led_on, + HZ / 20); + } else + ieee80211_queue_delayed_work(hw, &priv->led_on, 0); + } +} + +static int rtl8187_register_led(struct ieee80211_hw *dev, + struct rtl8187_led *led, const char *name, + const char *default_trigger, u8 ledpin, + bool is_radio) +{ + int err; + struct rtl8187_priv *priv = dev->priv; + + if (led->dev) + return -EEXIST; + if (!default_trigger) + return -EINVAL; + led->dev = dev; + led->ledpin = ledpin; + led->is_radio = is_radio; + strncpy(led->name, name, sizeof(led->name)); + + led->led_dev.name = led->name; + led->led_dev.default_trigger = default_trigger; + led->led_dev.brightness_set = rtl8187_led_brightness_set; + + err = led_classdev_register(&priv->udev->dev, &led->led_dev); + if (err) { + printk(KERN_INFO "LEDs: Failed to register %s\n", name); + led->dev = NULL; + return err; + } + return 0; +} + +static void rtl8187_unregister_led(struct rtl8187_led *led) +{ + struct ieee80211_hw *hw = led->dev; + struct rtl8187_priv *priv = hw->priv; + + led_classdev_unregister(&led->led_dev); + flush_delayed_work(&priv->led_off); + led->dev = NULL; +} + +void rtl8187_leds_init(struct ieee80211_hw *dev, u16 custid) +{ + struct rtl8187_priv *priv = dev->priv; + char name[RTL8187_LED_MAX_NAME_LEN + 1]; + u8 ledpin; + int err; + + /* According to the vendor driver, the LED operation depends on the + * customer ID encoded in the EEPROM + */ + printk(KERN_INFO "rtl8187: Customer ID is 0x%02X\n", custid); + switch (custid) { + case EEPROM_CID_RSVD0: + case EEPROM_CID_RSVD1: + case EEPROM_CID_SERCOMM_PS: + case EEPROM_CID_QMI: + case EEPROM_CID_DELL: + case EEPROM_CID_TOSHIBA: + ledpin = LED_PIN_GPIO0; + break; + case EEPROM_CID_ALPHA0: + ledpin = LED_PIN_LED0; + break; + case EEPROM_CID_HW: + ledpin = LED_PIN_HW; + break; + default: + ledpin = LED_PIN_GPIO0; + } + + INIT_DELAYED_WORK(&priv->led_on, led_turn_on); + INIT_DELAYED_WORK(&priv->led_off, led_turn_off); + + snprintf(name, sizeof(name), + "rtl8187-%s::radio", wiphy_name(dev->wiphy)); + err = rtl8187_register_led(dev, &priv->led_radio, name, + ieee80211_get_radio_led_name(dev), ledpin, true); + if (err) + return; + + snprintf(name, sizeof(name), + "rtl8187-%s::tx", wiphy_name(dev->wiphy)); + err = rtl8187_register_led(dev, &priv->led_tx, name, + ieee80211_get_tx_led_name(dev), ledpin, false); + if (err) + goto err_tx; + + snprintf(name, sizeof(name), + "rtl8187-%s::rx", wiphy_name(dev->wiphy)); + err = rtl8187_register_led(dev, &priv->led_rx, name, + ieee80211_get_rx_led_name(dev), ledpin, false); + if (!err) + return; + + /* registration of RX LED failed - unregister */ + rtl8187_unregister_led(&priv->led_tx); +err_tx: + rtl8187_unregister_led(&priv->led_radio); +} + +void rtl8187_leds_exit(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + + rtl8187_unregister_led(&priv->led_radio); + rtl8187_unregister_led(&priv->led_rx); + rtl8187_unregister_led(&priv->led_tx); + cancel_delayed_work_sync(&priv->led_off); + cancel_delayed_work_sync(&priv->led_on); +} +#endif /* def CONFIG_RTL8187_LEDS */ + diff --git a/drivers/net/wireless/rtl818x/rtl8187/leds.h b/drivers/net/wireless/rtl818x/rtl8187/leds.h new file mode 100644 index 000000000000..d743c96d4a20 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8187/leds.h @@ -0,0 +1,59 @@ +/* + * Definitions for RTL8187 leds + * + * Copyright 2009 Larry Finger + * + * Based on the LED handling in the r8187 driver, which is: + * Copyright (c) Realtek Semiconductor Corp. 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 version 2 as + * published by the Free Software Foundation. + */ + +#ifndef RTL8187_LED_H +#define RTL8187_LED_H + +#ifdef CONFIG_RTL8187_LEDS + +#define RTL8187_LED_MAX_NAME_LEN 21 + +#include +#include + +enum { + LED_PIN_LED0, + LED_PIN_LED1, + LED_PIN_GPIO0, + LED_PIN_HW +}; + +enum { + EEPROM_CID_RSVD0 = 0x00, + EEPROM_CID_RSVD1 = 0xFF, + EEPROM_CID_ALPHA0 = 0x01, + EEPROM_CID_SERCOMM_PS = 0x02, + EEPROM_CID_HW = 0x03, + EEPROM_CID_TOSHIBA = 0x04, + EEPROM_CID_QMI = 0x07, + EEPROM_CID_DELL = 0x08 +}; + +struct rtl8187_led { + struct ieee80211_hw *dev; + /* The LED class device */ + struct led_classdev led_dev; + /* The pin/method used to control the led */ + u8 ledpin; + /* The unique name string for this LED device. */ + char name[RTL8187_LED_MAX_NAME_LEN + 1]; + /* If the LED is radio or tx/rx */ + bool is_radio; +}; + +void rtl8187_leds_init(struct ieee80211_hw *dev, u16 code); +void rtl8187_leds_exit(struct ieee80211_hw *dev); + +#endif /* def CONFIG_RTL8187_LEDS */ + +#endif /* RTL8187_LED_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8187/rfkill.c b/drivers/net/wireless/rtl818x/rtl8187/rfkill.c new file mode 100644 index 000000000000..34116719974a --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8187/rfkill.c @@ -0,0 +1,64 @@ +/* + * Linux RFKILL support for RTL8187 + * + * Copyright (c) 2009 Herton Ronaldo Krzesinski + * + * Based on the RFKILL handling in the r8187 driver, which is: + * Copyright (c) Realtek Semiconductor Corp. All rights reserved. + * + * Thanks to Realtek for their support! + * + * 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 +#include +#include + +#include "rtl8187.h" +#include "rfkill.h" + +static bool rtl8187_is_radio_enabled(struct rtl8187_priv *priv) +{ + u8 gpio; + + gpio = rtl818x_ioread8(priv, &priv->map->GPIO0); + rtl818x_iowrite8(priv, &priv->map->GPIO0, gpio & ~priv->rfkill_mask); + gpio = rtl818x_ioread8(priv, &priv->map->GPIO1); + + return gpio & priv->rfkill_mask; +} + +void rtl8187_rfkill_init(struct ieee80211_hw *hw) +{ + struct rtl8187_priv *priv = hw->priv; + + priv->rfkill_off = rtl8187_is_radio_enabled(priv); + printk(KERN_INFO "rtl8187: wireless switch is %s\n", + priv->rfkill_off ? "on" : "off"); + wiphy_rfkill_set_hw_state(hw->wiphy, !priv->rfkill_off); + wiphy_rfkill_start_polling(hw->wiphy); +} + +void rtl8187_rfkill_poll(struct ieee80211_hw *hw) +{ + bool enabled; + struct rtl8187_priv *priv = hw->priv; + + mutex_lock(&priv->conf_mutex); + enabled = rtl8187_is_radio_enabled(priv); + if (unlikely(enabled != priv->rfkill_off)) { + priv->rfkill_off = enabled; + printk(KERN_INFO "rtl8187: wireless radio switch turned %s\n", + enabled ? "on" : "off"); + wiphy_rfkill_set_hw_state(hw->wiphy, !enabled); + } + mutex_unlock(&priv->conf_mutex); +} + +void rtl8187_rfkill_exit(struct ieee80211_hw *hw) +{ + wiphy_rfkill_stop_polling(hw->wiphy); +} diff --git a/drivers/net/wireless/rtl818x/rtl8187/rfkill.h b/drivers/net/wireless/rtl818x/rtl8187/rfkill.h new file mode 100644 index 000000000000..e12575e96d11 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8187/rfkill.h @@ -0,0 +1,8 @@ +#ifndef RTL8187_RFKILL_H +#define RTL8187_RFKILL_H + +void rtl8187_rfkill_init(struct ieee80211_hw *hw); +void rtl8187_rfkill_poll(struct ieee80211_hw *hw); +void rtl8187_rfkill_exit(struct ieee80211_hw *hw); + +#endif /* RTL8187_RFKILL_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8187/rtl8187.h b/drivers/net/wireless/rtl818x/rtl8187/rtl8187.h new file mode 100644 index 000000000000..0d7b1423f77b --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8187/rtl8187.h @@ -0,0 +1,271 @@ +/* + * Definitions for RTL8187 hardware + * + * Copyright 2007 Michael Wu + * Copyright 2007 Andrea Merello + * + * Based on the r8187 driver, which is: + * Copyright 2005 Andrea Merello , et al. + * + * 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. + */ + +#ifndef RTL8187_H +#define RTL8187_H + +#include "rtl818x.h" +#include "leds.h" + +#define RTL8187_EEPROM_TXPWR_BASE 0x05 +#define RTL8187_EEPROM_MAC_ADDR 0x07 +#define RTL8187_EEPROM_TXPWR_CHAN_1 0x16 /* 3 channels */ +#define RTL8187_EEPROM_TXPWR_CHAN_6 0x1B /* 2 channels */ +#define RTL8187_EEPROM_TXPWR_CHAN_4 0x3D /* 2 channels */ +#define RTL8187_EEPROM_SELECT_GPIO 0x3B + +#define RTL8187_REQT_READ 0xC0 +#define RTL8187_REQT_WRITE 0x40 +#define RTL8187_REQ_GET_REG 0x05 +#define RTL8187_REQ_SET_REG 0x05 + +#define RTL8187_MAX_RX 0x9C4 + +#define RFKILL_MASK_8187_89_97 0x2 +#define RFKILL_MASK_8198 0x4 + +struct rtl8187_rx_info { + struct urb *urb; + struct ieee80211_hw *dev; +}; + +struct rtl8187_rx_hdr { + __le32 flags; + u8 noise; + u8 signal; + u8 agc; + u8 reserved; + __le64 mac_time; +} __packed; + +struct rtl8187b_rx_hdr { + __le32 flags; + __le64 mac_time; + u8 sq; + u8 rssi; + u8 agc; + u8 flags2; + __le16 snr_long2end; + s8 pwdb_g12; + u8 fot; +} __packed; + +/* {rtl8187,rtl8187b}_tx_info is in skb */ + +struct rtl8187_tx_hdr { + __le32 flags; + __le16 rts_duration; + __le16 len; + __le32 retry; +} __packed; + +struct rtl8187b_tx_hdr { + __le32 flags; + __le16 rts_duration; + __le16 len; + __le32 unused_1; + __le16 unused_2; + __le16 tx_duration; + __le32 unused_3; + __le32 retry; + __le32 unused_4[2]; +} __packed; + +enum { + DEVICE_RTL8187, + DEVICE_RTL8187B +}; + +struct rtl8187_priv { + /* common between rtl818x drivers */ + struct rtl818x_csr *map; + const struct rtl818x_rf_ops *rf; + struct ieee80211_vif *vif; + + /* The mutex protects the TX loopback state. + * Any attempt to set channels concurrently locks the device. + */ + struct mutex conf_mutex; + + /* rtl8187 specific */ + struct ieee80211_channel channels[14]; + struct ieee80211_rate rates[12]; + struct ieee80211_supported_band band; + struct usb_device *udev; + u32 rx_conf; + struct usb_anchor anchored; + struct delayed_work work; + struct ieee80211_hw *dev; +#ifdef CONFIG_RTL8187_LEDS + struct rtl8187_led led_radio; + struct rtl8187_led led_tx; + struct rtl8187_led led_rx; + struct delayed_work led_on; + struct delayed_work led_off; +#endif + u16 txpwr_base; + u8 asic_rev; + u8 is_rtl8187b; + enum { + RTL8187BvB, + RTL8187BvD, + RTL8187BvE + } hw_rev; + struct sk_buff_head rx_queue; + u8 signal; + u8 noise; + u8 slot_time; + u8 aifsn[4]; + u8 rfkill_mask; + struct { + __le64 buf; + struct sk_buff_head queue; + } b_tx_status; /* This queue is used by both -b and non-b devices */ + struct mutex io_mutex; + union { + u8 bits8; + __le16 bits16; + __le32 bits32; + } *io_dmabuf; + bool rfkill_off; +}; + +void rtl8187_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data); + +static inline u8 rtl818x_ioread8_idx(struct rtl8187_priv *priv, + u8 *addr, u8 idx) +{ + u8 val; + + mutex_lock(&priv->io_mutex); + usb_control_msg(priv->udev, usb_rcvctrlpipe(priv->udev, 0), + RTL8187_REQ_GET_REG, RTL8187_REQT_READ, + (unsigned long)addr, idx & 0x03, + &priv->io_dmabuf->bits8, sizeof(val), HZ / 2); + + val = priv->io_dmabuf->bits8; + mutex_unlock(&priv->io_mutex); + + return val; +} + +static inline u8 rtl818x_ioread8(struct rtl8187_priv *priv, u8 *addr) +{ + return rtl818x_ioread8_idx(priv, addr, 0); +} + +static inline u16 rtl818x_ioread16_idx(struct rtl8187_priv *priv, + __le16 *addr, u8 idx) +{ + __le16 val; + + mutex_lock(&priv->io_mutex); + usb_control_msg(priv->udev, usb_rcvctrlpipe(priv->udev, 0), + RTL8187_REQ_GET_REG, RTL8187_REQT_READ, + (unsigned long)addr, idx & 0x03, + &priv->io_dmabuf->bits16, sizeof(val), HZ / 2); + + val = priv->io_dmabuf->bits16; + mutex_unlock(&priv->io_mutex); + + return le16_to_cpu(val); +} + +static inline u16 rtl818x_ioread16(struct rtl8187_priv *priv, __le16 *addr) +{ + return rtl818x_ioread16_idx(priv, addr, 0); +} + +static inline u32 rtl818x_ioread32_idx(struct rtl8187_priv *priv, + __le32 *addr, u8 idx) +{ + __le32 val; + + mutex_lock(&priv->io_mutex); + usb_control_msg(priv->udev, usb_rcvctrlpipe(priv->udev, 0), + RTL8187_REQ_GET_REG, RTL8187_REQT_READ, + (unsigned long)addr, idx & 0x03, + &priv->io_dmabuf->bits32, sizeof(val), HZ / 2); + + val = priv->io_dmabuf->bits32; + mutex_unlock(&priv->io_mutex); + + return le32_to_cpu(val); +} + +static inline u32 rtl818x_ioread32(struct rtl8187_priv *priv, __le32 *addr) +{ + return rtl818x_ioread32_idx(priv, addr, 0); +} + +static inline void rtl818x_iowrite8_idx(struct rtl8187_priv *priv, + u8 *addr, u8 val, u8 idx) +{ + mutex_lock(&priv->io_mutex); + + priv->io_dmabuf->bits8 = val; + usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0), + RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE, + (unsigned long)addr, idx & 0x03, + &priv->io_dmabuf->bits8, sizeof(val), HZ / 2); + + mutex_unlock(&priv->io_mutex); +} + +static inline void rtl818x_iowrite8(struct rtl8187_priv *priv, u8 *addr, u8 val) +{ + rtl818x_iowrite8_idx(priv, addr, val, 0); +} + +static inline void rtl818x_iowrite16_idx(struct rtl8187_priv *priv, + __le16 *addr, u16 val, u8 idx) +{ + mutex_lock(&priv->io_mutex); + + priv->io_dmabuf->bits16 = cpu_to_le16(val); + usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0), + RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE, + (unsigned long)addr, idx & 0x03, + &priv->io_dmabuf->bits16, sizeof(val), HZ / 2); + + mutex_unlock(&priv->io_mutex); +} + +static inline void rtl818x_iowrite16(struct rtl8187_priv *priv, __le16 *addr, + u16 val) +{ + rtl818x_iowrite16_idx(priv, addr, val, 0); +} + +static inline void rtl818x_iowrite32_idx(struct rtl8187_priv *priv, + __le32 *addr, u32 val, u8 idx) +{ + mutex_lock(&priv->io_mutex); + + priv->io_dmabuf->bits32 = cpu_to_le32(val); + usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0), + RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE, + (unsigned long)addr, idx & 0x03, + &priv->io_dmabuf->bits32, sizeof(val), HZ / 2); + + mutex_unlock(&priv->io_mutex); +} + +static inline void rtl818x_iowrite32(struct rtl8187_priv *priv, __le32 *addr, + u32 val) +{ + rtl818x_iowrite32_idx(priv, addr, val, 0); +} + +#endif /* RTL8187_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8187/rtl8225.c b/drivers/net/wireless/rtl818x/rtl8187/rtl8225.c new file mode 100644 index 000000000000..908903f721f5 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8187/rtl8225.c @@ -0,0 +1,961 @@ +/* + * Radio tuning for RTL8225 on RTL8187 + * + * Copyright 2007 Michael Wu + * Copyright 2007 Andrea Merello + * + * Based on the r8187 driver, which is: + * Copyright 2005 Andrea Merello , et al. + * + * Magic delays, register offsets, and phy value tables below are + * taken from the original r8187 driver sources. Thanks to Realtek + * for their support! + * + * 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 +#include +#include + +#include "rtl8187.h" +#include "rtl8225.h" + +static void rtl8225_write_bitbang(struct ieee80211_hw *dev, u8 addr, u16 data) +{ + struct rtl8187_priv *priv = dev->priv; + u16 reg80, reg84, reg82; + u32 bangdata; + int i; + + bangdata = (data << 4) | (addr & 0xf); + + reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput) & 0xfff3; + reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable); + + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x7); + + reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x7); + udelay(10); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); + udelay(2); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80); + udelay(10); + + for (i = 15; i >= 0; i--) { + u16 reg = reg80 | (bangdata & (1 << i)) >> i; + + if (i & 1) + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1)); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1)); + + if (!(i & 1)) + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); + } + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); + udelay(10); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84); +} + +static void rtl8225_write_8051(struct ieee80211_hw *dev, u8 addr, __le16 data) +{ + struct rtl8187_priv *priv = dev->priv; + u16 reg80, reg82, reg84; + + reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput); + reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable); + reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect); + + reg80 &= ~(0x3 << 2); + reg84 &= ~0xF; + + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x0007); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x0007); + udelay(10); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); + udelay(2); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80); + udelay(10); + + mutex_lock(&priv->io_mutex); + + priv->io_dmabuf->bits16 = data; + usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0), + RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE, + addr, 0x8225, &priv->io_dmabuf->bits16, sizeof(data), + HZ / 2); + + mutex_unlock(&priv->io_mutex); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); + udelay(10); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84); +} + +static void rtl8225_write(struct ieee80211_hw *dev, u8 addr, u16 data) +{ + struct rtl8187_priv *priv = dev->priv; + + if (priv->asic_rev) + rtl8225_write_8051(dev, addr, cpu_to_le16(data)); + else + rtl8225_write_bitbang(dev, addr, data); +} + +static u16 rtl8225_read(struct ieee80211_hw *dev, u8 addr) +{ + struct rtl8187_priv *priv = dev->priv; + u16 reg80, reg82, reg84, out; + int i; + + reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput); + reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable); + reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect); + + reg80 &= ~0xF; + + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x000F); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x000F); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); + udelay(4); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80); + udelay(5); + + for (i = 4; i >= 0; i--) { + u16 reg = reg80 | ((addr >> i) & 1); + + if (!(i & 1)) { + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); + udelay(1); + } + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg | (1 << 1)); + udelay(2); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg | (1 << 1)); + udelay(2); + + if (i & 1) { + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); + udelay(1); + } + } + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3) | (1 << 1)); + udelay(2); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3)); + udelay(2); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3)); + udelay(2); + + out = 0; + for (i = 11; i >= 0; i--) { + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3)); + udelay(1); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3) | (1 << 1)); + udelay(2); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3) | (1 << 1)); + udelay(2); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3) | (1 << 1)); + udelay(2); + + if (rtl818x_ioread16(priv, &priv->map->RFPinsInput) & (1 << 1)) + out |= 1 << i; + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3)); + udelay(2); + } + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3) | (1 << 2)); + udelay(2); + + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x03A0); + + return out; +} + +static const u16 rtl8225bcd_rxgain[] = { + 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409, + 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541, + 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583, + 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644, + 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688, + 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745, + 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789, + 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793, + 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d, + 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9, + 0x07aa, 0x07ab, 0x07ac, 0x07ad, 0x07b0, 0x07b1, 0x07b2, 0x07b3, + 0x07b4, 0x07b5, 0x07b8, 0x07b9, 0x07ba, 0x07bb, 0x07bb +}; + +static const u8 rtl8225_agc[] = { + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, 0x97, 0x96, + 0x95, 0x94, 0x93, 0x92, 0x91, 0x90, 0x8f, 0x8e, + 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86, + 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, 0x3f, 0x3e, + 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, + 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2f, 0x2e, + 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, + 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e, + 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, + 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, + 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, + 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 +}; + +static const u8 rtl8225_gain[] = { + 0x23, 0x88, 0x7c, 0xa5, /* -82dBm */ + 0x23, 0x88, 0x7c, 0xb5, /* -82dBm */ + 0x23, 0x88, 0x7c, 0xc5, /* -82dBm */ + 0x33, 0x80, 0x79, 0xc5, /* -78dBm */ + 0x43, 0x78, 0x76, 0xc5, /* -74dBm */ + 0x53, 0x60, 0x73, 0xc5, /* -70dBm */ + 0x63, 0x58, 0x70, 0xc5, /* -66dBm */ +}; + +static const u8 rtl8225_threshold[] = { + 0x8d, 0x8d, 0x8d, 0x8d, 0x9d, 0xad, 0xbd +}; + +static const u8 rtl8225_tx_gain_cck_ofdm[] = { + 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e +}; + +static const u8 rtl8225_tx_power_cck[] = { + 0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02, + 0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02, + 0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02, + 0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02, + 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03, + 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03 +}; + +static const u8 rtl8225_tx_power_cck_ch14[] = { + 0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00, + 0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00 +}; + +static const u8 rtl8225_tx_power_ofdm[] = { + 0x80, 0x90, 0xa2, 0xb5, 0xcb, 0xe4 +}; + +static const u32 rtl8225_chan[] = { + 0x085c, 0x08dc, 0x095c, 0x09dc, 0x0a5c, 0x0adc, 0x0b5c, + 0x0bdc, 0x0c5c, 0x0cdc, 0x0d5c, 0x0ddc, 0x0e5c, 0x0f72 +}; + +static void rtl8225_rf_set_tx_power(struct ieee80211_hw *dev, int channel) +{ + struct rtl8187_priv *priv = dev->priv; + u8 cck_power, ofdm_power; + const u8 *tmp; + u32 reg; + int i; + + cck_power = priv->channels[channel - 1].hw_value & 0xF; + ofdm_power = priv->channels[channel - 1].hw_value >> 4; + + cck_power = min(cck_power, (u8)11); + if (ofdm_power > (u8)15) + ofdm_power = 25; + else + ofdm_power += 10; + + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, + rtl8225_tx_gain_cck_ofdm[cck_power / 6] >> 1); + + if (channel == 14) + tmp = &rtl8225_tx_power_cck_ch14[(cck_power % 6) * 8]; + else + tmp = &rtl8225_tx_power_cck[(cck_power % 6) * 8]; + + for (i = 0; i < 8; i++) + rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++); + + msleep(1); // FIXME: optional? + + /* anaparam2 on */ + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, + reg | RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, + RTL8187_RTL8225_ANAPARAM2_ON); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, + reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + + rtl8225_write_phy_ofdm(dev, 2, 0x42); + rtl8225_write_phy_ofdm(dev, 6, 0x00); + rtl8225_write_phy_ofdm(dev, 8, 0x00); + + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, + rtl8225_tx_gain_cck_ofdm[ofdm_power / 6] >> 1); + + tmp = &rtl8225_tx_power_ofdm[ofdm_power % 6]; + + rtl8225_write_phy_ofdm(dev, 5, *tmp); + rtl8225_write_phy_ofdm(dev, 7, *tmp); + + msleep(1); +} + +static void rtl8225_rf_init(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + int i; + + rtl8225_write(dev, 0x0, 0x067); + rtl8225_write(dev, 0x1, 0xFE0); + rtl8225_write(dev, 0x2, 0x44D); + rtl8225_write(dev, 0x3, 0x441); + rtl8225_write(dev, 0x4, 0x486); + rtl8225_write(dev, 0x5, 0xBC0); + rtl8225_write(dev, 0x6, 0xAE6); + rtl8225_write(dev, 0x7, 0x82A); + rtl8225_write(dev, 0x8, 0x01F); + rtl8225_write(dev, 0x9, 0x334); + rtl8225_write(dev, 0xA, 0xFD4); + rtl8225_write(dev, 0xB, 0x391); + rtl8225_write(dev, 0xC, 0x050); + rtl8225_write(dev, 0xD, 0x6DB); + rtl8225_write(dev, 0xE, 0x029); + rtl8225_write(dev, 0xF, 0x914); msleep(100); + + rtl8225_write(dev, 0x2, 0xC4D); msleep(200); + rtl8225_write(dev, 0x2, 0x44D); msleep(200); + + if (!(rtl8225_read(dev, 6) & (1 << 7))) { + rtl8225_write(dev, 0x02, 0x0c4d); + msleep(200); + rtl8225_write(dev, 0x02, 0x044d); + msleep(100); + if (!(rtl8225_read(dev, 6) & (1 << 7))) + wiphy_warn(dev->wiphy, "RF Calibration Failed! %x\n", + rtl8225_read(dev, 6)); + } + + rtl8225_write(dev, 0x0, 0x127); + + for (i = 0; i < ARRAY_SIZE(rtl8225bcd_rxgain); i++) { + rtl8225_write(dev, 0x1, i + 1); + rtl8225_write(dev, 0x2, rtl8225bcd_rxgain[i]); + } + + rtl8225_write(dev, 0x0, 0x027); + rtl8225_write(dev, 0x0, 0x22F); + + for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) { + rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]); + rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i); + } + + msleep(1); + + rtl8225_write_phy_ofdm(dev, 0x00, 0x01); + rtl8225_write_phy_ofdm(dev, 0x01, 0x02); + rtl8225_write_phy_ofdm(dev, 0x02, 0x42); + rtl8225_write_phy_ofdm(dev, 0x03, 0x00); + rtl8225_write_phy_ofdm(dev, 0x04, 0x00); + rtl8225_write_phy_ofdm(dev, 0x05, 0x00); + rtl8225_write_phy_ofdm(dev, 0x06, 0x40); + rtl8225_write_phy_ofdm(dev, 0x07, 0x00); + rtl8225_write_phy_ofdm(dev, 0x08, 0x40); + rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); + rtl8225_write_phy_ofdm(dev, 0x0a, 0x09); + rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); + rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); + rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); + rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); + rtl8225_write_phy_ofdm(dev, 0x10, 0x84); + rtl8225_write_phy_ofdm(dev, 0x11, 0x06); + rtl8225_write_phy_ofdm(dev, 0x12, 0x20); + rtl8225_write_phy_ofdm(dev, 0x13, 0x20); + rtl8225_write_phy_ofdm(dev, 0x14, 0x00); + rtl8225_write_phy_ofdm(dev, 0x15, 0x40); + rtl8225_write_phy_ofdm(dev, 0x16, 0x00); + rtl8225_write_phy_ofdm(dev, 0x17, 0x40); + rtl8225_write_phy_ofdm(dev, 0x18, 0xef); + rtl8225_write_phy_ofdm(dev, 0x19, 0x19); + rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); + rtl8225_write_phy_ofdm(dev, 0x1b, 0x76); + rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); + rtl8225_write_phy_ofdm(dev, 0x1e, 0x95); + rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); + rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); + rtl8225_write_phy_ofdm(dev, 0x21, 0x27); + rtl8225_write_phy_ofdm(dev, 0x22, 0x16); + rtl8225_write_phy_ofdm(dev, 0x24, 0x46); + rtl8225_write_phy_ofdm(dev, 0x25, 0x20); + rtl8225_write_phy_ofdm(dev, 0x26, 0x90); + rtl8225_write_phy_ofdm(dev, 0x27, 0x88); + + rtl8225_write_phy_ofdm(dev, 0x0d, rtl8225_gain[2 * 4]); + rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225_gain[2 * 4 + 2]); + rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225_gain[2 * 4 + 3]); + rtl8225_write_phy_ofdm(dev, 0x23, rtl8225_gain[2 * 4 + 1]); + + rtl8225_write_phy_cck(dev, 0x00, 0x98); + rtl8225_write_phy_cck(dev, 0x03, 0x20); + rtl8225_write_phy_cck(dev, 0x04, 0x7e); + rtl8225_write_phy_cck(dev, 0x05, 0x12); + rtl8225_write_phy_cck(dev, 0x06, 0xfc); + rtl8225_write_phy_cck(dev, 0x07, 0x78); + rtl8225_write_phy_cck(dev, 0x08, 0x2e); + rtl8225_write_phy_cck(dev, 0x10, 0x9b); + rtl8225_write_phy_cck(dev, 0x11, 0x88); + rtl8225_write_phy_cck(dev, 0x12, 0x47); + rtl8225_write_phy_cck(dev, 0x13, 0xd0); + rtl8225_write_phy_cck(dev, 0x19, 0x00); + rtl8225_write_phy_cck(dev, 0x1a, 0xa0); + rtl8225_write_phy_cck(dev, 0x1b, 0x08); + rtl8225_write_phy_cck(dev, 0x40, 0x86); + rtl8225_write_phy_cck(dev, 0x41, 0x8d); + rtl8225_write_phy_cck(dev, 0x42, 0x15); + rtl8225_write_phy_cck(dev, 0x43, 0x18); + rtl8225_write_phy_cck(dev, 0x44, 0x1f); + rtl8225_write_phy_cck(dev, 0x45, 0x1e); + rtl8225_write_phy_cck(dev, 0x46, 0x1a); + rtl8225_write_phy_cck(dev, 0x47, 0x15); + rtl8225_write_phy_cck(dev, 0x48, 0x10); + rtl8225_write_phy_cck(dev, 0x49, 0x0a); + rtl8225_write_phy_cck(dev, 0x4a, 0x05); + rtl8225_write_phy_cck(dev, 0x4b, 0x02); + rtl8225_write_phy_cck(dev, 0x4c, 0x05); + + rtl818x_iowrite8(priv, &priv->map->TESTR, 0x0D); + + rtl8225_rf_set_tx_power(dev, 1); + + /* RX antenna default to A */ + rtl8225_write_phy_cck(dev, 0x10, 0x9b); /* B: 0xDB */ + rtl8225_write_phy_ofdm(dev, 0x26, 0x90); /* B: 0x10 */ + + rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); /* B: 0x00 */ + msleep(1); + rtl818x_iowrite32(priv, (__le32 *)0xFF94, 0x3dc00002); + + /* set sensitivity */ + rtl8225_write(dev, 0x0c, 0x50); + rtl8225_write_phy_ofdm(dev, 0x0d, rtl8225_gain[2 * 4]); + rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225_gain[2 * 4 + 2]); + rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225_gain[2 * 4 + 3]); + rtl8225_write_phy_ofdm(dev, 0x23, rtl8225_gain[2 * 4 + 1]); + rtl8225_write_phy_cck(dev, 0x41, rtl8225_threshold[2]); +} + +static const u8 rtl8225z2_agc[] = { + 0x5e, 0x5e, 0x5e, 0x5e, 0x5d, 0x5b, 0x59, 0x57, 0x55, 0x53, 0x51, 0x4f, + 0x4d, 0x4b, 0x49, 0x47, 0x45, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x39, 0x37, + 0x35, 0x33, 0x31, 0x2f, 0x2d, 0x2b, 0x29, 0x27, 0x25, 0x23, 0x21, 0x1f, + 0x1d, 0x1b, 0x19, 0x17, 0x15, 0x13, 0x11, 0x0f, 0x0d, 0x0b, 0x09, 0x07, + 0x05, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, + 0x19, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, + 0x28, 0x29, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c, 0x2c, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x30, 0x30, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31 +}; +static const u8 rtl8225z2_ofdm[] = { + 0x10, 0x0d, 0x01, 0x00, 0x14, 0xfb, 0xfb, 0x60, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, + 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, 0xa8, 0x26, + 0x32, 0x33, 0x07, 0xa5, 0x6f, 0x55, 0xc8, 0xb3, + 0x0a, 0xe1, 0x2C, 0x8a, 0x86, 0x83, 0x34, 0x0f, + 0x4f, 0x24, 0x6f, 0xc2, 0x6b, 0x40, 0x80, 0x00, + 0xc0, 0xc1, 0x58, 0xf1, 0x00, 0xe4, 0x90, 0x3e, + 0x6d, 0x3c, 0xfb, 0x07 +}; + +static const u8 rtl8225z2_tx_power_cck_ch14[] = { + 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00 +}; + +static const u8 rtl8225z2_tx_power_cck[] = { + 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04, + 0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03, + 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03, + 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03 +}; + +static const u8 rtl8225z2_tx_power_ofdm[] = { + 0x42, 0x00, 0x40, 0x00, 0x40 +}; + +static const u8 rtl8225z2_tx_gain_cck_ofdm[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23 +}; + +static void rtl8225z2_rf_set_tx_power(struct ieee80211_hw *dev, int channel) +{ + struct rtl8187_priv *priv = dev->priv; + u8 cck_power, ofdm_power; + const u8 *tmp; + u32 reg; + int i; + + cck_power = priv->channels[channel - 1].hw_value & 0xF; + ofdm_power = priv->channels[channel - 1].hw_value >> 4; + + cck_power = min(cck_power, (u8)15); + cck_power += priv->txpwr_base & 0xF; + cck_power = min(cck_power, (u8)35); + + if (ofdm_power > (u8)15) + ofdm_power = 25; + else + ofdm_power += 10; + ofdm_power += priv->txpwr_base >> 4; + ofdm_power = min(ofdm_power, (u8)35); + + if (channel == 14) + tmp = rtl8225z2_tx_power_cck_ch14; + else + tmp = rtl8225z2_tx_power_cck; + + for (i = 0; i < 8; i++) + rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++); + + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, + rtl8225z2_tx_gain_cck_ofdm[cck_power]); + msleep(1); + + /* anaparam2 on */ + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, + reg | RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, + RTL8187_RTL8225_ANAPARAM2_ON); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, + reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + + rtl8225_write_phy_ofdm(dev, 2, 0x42); + rtl8225_write_phy_ofdm(dev, 5, 0x00); + rtl8225_write_phy_ofdm(dev, 6, 0x40); + rtl8225_write_phy_ofdm(dev, 7, 0x00); + rtl8225_write_phy_ofdm(dev, 8, 0x40); + + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, + rtl8225z2_tx_gain_cck_ofdm[ofdm_power]); + msleep(1); +} + +static void rtl8225z2_b_rf_set_tx_power(struct ieee80211_hw *dev, int channel) +{ + struct rtl8187_priv *priv = dev->priv; + u8 cck_power, ofdm_power; + const u8 *tmp; + int i; + + cck_power = priv->channels[channel - 1].hw_value & 0xF; + ofdm_power = priv->channels[channel - 1].hw_value >> 4; + + if (cck_power > 15) + cck_power = (priv->hw_rev == RTL8187BvB) ? 15 : 22; + else + cck_power += (priv->hw_rev == RTL8187BvB) ? 0 : 7; + cck_power += priv->txpwr_base & 0xF; + cck_power = min(cck_power, (u8)35); + + if (ofdm_power > 15) + ofdm_power = (priv->hw_rev == RTL8187BvB) ? 17 : 25; + else + ofdm_power += (priv->hw_rev == RTL8187BvB) ? 2 : 10; + ofdm_power += (priv->txpwr_base >> 4) & 0xF; + ofdm_power = min(ofdm_power, (u8)35); + + if (channel == 14) + tmp = rtl8225z2_tx_power_cck_ch14; + else + tmp = rtl8225z2_tx_power_cck; + + if (priv->hw_rev == RTL8187BvB) { + if (cck_power <= 6) + ; /* do nothing */ + else if (cck_power <= 11) + tmp += 8; + else + tmp += 16; + } else { + if (cck_power <= 5) + ; /* do nothing */ + else if (cck_power <= 11) + tmp += 8; + else if (cck_power <= 17) + tmp += 16; + else + tmp += 24; + } + + for (i = 0; i < 8; i++) + rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++); + + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, + rtl8225z2_tx_gain_cck_ofdm[cck_power] << 1); + msleep(1); + + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, + rtl8225z2_tx_gain_cck_ofdm[ofdm_power] << 1); + if (priv->hw_rev == RTL8187BvB) { + if (ofdm_power <= 11) { + rtl8225_write_phy_ofdm(dev, 0x87, 0x60); + rtl8225_write_phy_ofdm(dev, 0x89, 0x60); + } else { + rtl8225_write_phy_ofdm(dev, 0x87, 0x5c); + rtl8225_write_phy_ofdm(dev, 0x89, 0x5c); + } + } else { + if (ofdm_power <= 11) { + rtl8225_write_phy_ofdm(dev, 0x87, 0x5c); + rtl8225_write_phy_ofdm(dev, 0x89, 0x5c); + } else if (ofdm_power <= 17) { + rtl8225_write_phy_ofdm(dev, 0x87, 0x54); + rtl8225_write_phy_ofdm(dev, 0x89, 0x54); + } else { + rtl8225_write_phy_ofdm(dev, 0x87, 0x50); + rtl8225_write_phy_ofdm(dev, 0x89, 0x50); + } + } + msleep(1); +} + +static const u16 rtl8225z2_rxgain[] = { + 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409, + 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541, + 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583, + 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644, + 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688, + 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745, + 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789, + 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793, + 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d, + 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9, + 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3, + 0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb +}; + +static const u8 rtl8225z2_gain_bg[] = { + 0x23, 0x15, 0xa5, /* -82-1dBm */ + 0x23, 0x15, 0xb5, /* -82-2dBm */ + 0x23, 0x15, 0xc5, /* -82-3dBm */ + 0x33, 0x15, 0xc5, /* -78dBm */ + 0x43, 0x15, 0xc5, /* -74dBm */ + 0x53, 0x15, 0xc5, /* -70dBm */ + 0x63, 0x15, 0xc5 /* -66dBm */ +}; + +static void rtl8225z2_rf_init(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + int i; + + rtl8225_write(dev, 0x0, 0x2BF); + rtl8225_write(dev, 0x1, 0xEE0); + rtl8225_write(dev, 0x2, 0x44D); + rtl8225_write(dev, 0x3, 0x441); + rtl8225_write(dev, 0x4, 0x8C3); + rtl8225_write(dev, 0x5, 0xC72); + rtl8225_write(dev, 0x6, 0x0E6); + rtl8225_write(dev, 0x7, 0x82A); + rtl8225_write(dev, 0x8, 0x03F); + rtl8225_write(dev, 0x9, 0x335); + rtl8225_write(dev, 0xa, 0x9D4); + rtl8225_write(dev, 0xb, 0x7BB); + rtl8225_write(dev, 0xc, 0x850); + rtl8225_write(dev, 0xd, 0xCDF); + rtl8225_write(dev, 0xe, 0x02B); + rtl8225_write(dev, 0xf, 0x114); + msleep(100); + + rtl8225_write(dev, 0x0, 0x1B7); + + for (i = 0; i < ARRAY_SIZE(rtl8225z2_rxgain); i++) { + rtl8225_write(dev, 0x1, i + 1); + rtl8225_write(dev, 0x2, rtl8225z2_rxgain[i]); + } + + rtl8225_write(dev, 0x3, 0x080); + rtl8225_write(dev, 0x5, 0x004); + rtl8225_write(dev, 0x0, 0x0B7); + rtl8225_write(dev, 0x2, 0xc4D); + + msleep(200); + rtl8225_write(dev, 0x2, 0x44D); + msleep(100); + + if (!(rtl8225_read(dev, 6) & (1 << 7))) { + rtl8225_write(dev, 0x02, 0x0C4D); + msleep(200); + rtl8225_write(dev, 0x02, 0x044D); + msleep(100); + if (!(rtl8225_read(dev, 6) & (1 << 7))) + wiphy_warn(dev->wiphy, "RF Calibration Failed! %x\n", + rtl8225_read(dev, 6)); + } + + msleep(200); + + rtl8225_write(dev, 0x0, 0x2BF); + + for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) { + rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]); + rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i); + } + + msleep(1); + + rtl8225_write_phy_ofdm(dev, 0x00, 0x01); + rtl8225_write_phy_ofdm(dev, 0x01, 0x02); + rtl8225_write_phy_ofdm(dev, 0x02, 0x42); + rtl8225_write_phy_ofdm(dev, 0x03, 0x00); + rtl8225_write_phy_ofdm(dev, 0x04, 0x00); + rtl8225_write_phy_ofdm(dev, 0x05, 0x00); + rtl8225_write_phy_ofdm(dev, 0x06, 0x40); + rtl8225_write_phy_ofdm(dev, 0x07, 0x00); + rtl8225_write_phy_ofdm(dev, 0x08, 0x40); + rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); + rtl8225_write_phy_ofdm(dev, 0x0a, 0x08); + rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); + rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); + rtl8225_write_phy_ofdm(dev, 0x0d, 0x43); + rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); + rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); + rtl8225_write_phy_ofdm(dev, 0x10, 0x84); + rtl8225_write_phy_ofdm(dev, 0x11, 0x07); + rtl8225_write_phy_ofdm(dev, 0x12, 0x20); + rtl8225_write_phy_ofdm(dev, 0x13, 0x20); + rtl8225_write_phy_ofdm(dev, 0x14, 0x00); + rtl8225_write_phy_ofdm(dev, 0x15, 0x40); + rtl8225_write_phy_ofdm(dev, 0x16, 0x00); + rtl8225_write_phy_ofdm(dev, 0x17, 0x40); + rtl8225_write_phy_ofdm(dev, 0x18, 0xef); + rtl8225_write_phy_ofdm(dev, 0x19, 0x19); + rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); + rtl8225_write_phy_ofdm(dev, 0x1b, 0x15); + rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); + rtl8225_write_phy_ofdm(dev, 0x1d, 0xc5); + rtl8225_write_phy_ofdm(dev, 0x1e, 0x95); + rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); + rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); + rtl8225_write_phy_ofdm(dev, 0x21, 0x17); + rtl8225_write_phy_ofdm(dev, 0x22, 0x16); + rtl8225_write_phy_ofdm(dev, 0x23, 0x80); + rtl8225_write_phy_ofdm(dev, 0x24, 0x46); + rtl8225_write_phy_ofdm(dev, 0x25, 0x00); + rtl8225_write_phy_ofdm(dev, 0x26, 0x90); + rtl8225_write_phy_ofdm(dev, 0x27, 0x88); + + rtl8225_write_phy_ofdm(dev, 0x0b, rtl8225z2_gain_bg[4 * 3]); + rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225z2_gain_bg[4 * 3 + 1]); + rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225z2_gain_bg[4 * 3 + 2]); + rtl8225_write_phy_ofdm(dev, 0x21, 0x37); + + rtl8225_write_phy_cck(dev, 0x00, 0x98); + rtl8225_write_phy_cck(dev, 0x03, 0x20); + rtl8225_write_phy_cck(dev, 0x04, 0x7e); + rtl8225_write_phy_cck(dev, 0x05, 0x12); + rtl8225_write_phy_cck(dev, 0x06, 0xfc); + rtl8225_write_phy_cck(dev, 0x07, 0x78); + rtl8225_write_phy_cck(dev, 0x08, 0x2e); + rtl8225_write_phy_cck(dev, 0x10, 0x9b); + rtl8225_write_phy_cck(dev, 0x11, 0x88); + rtl8225_write_phy_cck(dev, 0x12, 0x47); + rtl8225_write_phy_cck(dev, 0x13, 0xd0); + rtl8225_write_phy_cck(dev, 0x19, 0x00); + rtl8225_write_phy_cck(dev, 0x1a, 0xa0); + rtl8225_write_phy_cck(dev, 0x1b, 0x08); + rtl8225_write_phy_cck(dev, 0x40, 0x86); + rtl8225_write_phy_cck(dev, 0x41, 0x8d); + rtl8225_write_phy_cck(dev, 0x42, 0x15); + rtl8225_write_phy_cck(dev, 0x43, 0x18); + rtl8225_write_phy_cck(dev, 0x44, 0x36); + rtl8225_write_phy_cck(dev, 0x45, 0x35); + rtl8225_write_phy_cck(dev, 0x46, 0x2e); + rtl8225_write_phy_cck(dev, 0x47, 0x25); + rtl8225_write_phy_cck(dev, 0x48, 0x1c); + rtl8225_write_phy_cck(dev, 0x49, 0x12); + rtl8225_write_phy_cck(dev, 0x4a, 0x09); + rtl8225_write_phy_cck(dev, 0x4b, 0x04); + rtl8225_write_phy_cck(dev, 0x4c, 0x05); + + rtl818x_iowrite8(priv, (u8 *)0xFF5B, 0x0D); msleep(1); + + rtl8225z2_rf_set_tx_power(dev, 1); + + /* RX antenna default to A */ + rtl8225_write_phy_cck(dev, 0x10, 0x9b); /* B: 0xDB */ + rtl8225_write_phy_ofdm(dev, 0x26, 0x90); /* B: 0x10 */ + + rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); /* B: 0x00 */ + msleep(1); + rtl818x_iowrite32(priv, (__le32 *)0xFF94, 0x3dc00002); +} + +static void rtl8225z2_b_rf_init(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + int i; + + rtl8225_write(dev, 0x0, 0x0B7); + rtl8225_write(dev, 0x1, 0xEE0); + rtl8225_write(dev, 0x2, 0x44D); + rtl8225_write(dev, 0x3, 0x441); + rtl8225_write(dev, 0x4, 0x8C3); + rtl8225_write(dev, 0x5, 0xC72); + rtl8225_write(dev, 0x6, 0x0E6); + rtl8225_write(dev, 0x7, 0x82A); + rtl8225_write(dev, 0x8, 0x03F); + rtl8225_write(dev, 0x9, 0x335); + rtl8225_write(dev, 0xa, 0x9D4); + rtl8225_write(dev, 0xb, 0x7BB); + rtl8225_write(dev, 0xc, 0x850); + rtl8225_write(dev, 0xd, 0xCDF); + rtl8225_write(dev, 0xe, 0x02B); + rtl8225_write(dev, 0xf, 0x114); + + rtl8225_write(dev, 0x0, 0x1B7); + + for (i = 0; i < ARRAY_SIZE(rtl8225z2_rxgain); i++) { + rtl8225_write(dev, 0x1, i + 1); + rtl8225_write(dev, 0x2, rtl8225z2_rxgain[i]); + } + + rtl8225_write(dev, 0x3, 0x080); + rtl8225_write(dev, 0x5, 0x004); + rtl8225_write(dev, 0x0, 0x0B7); + + rtl8225_write(dev, 0x2, 0xC4D); + + rtl8225_write(dev, 0x2, 0x44D); + rtl8225_write(dev, 0x0, 0x2BF); + + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, 0x03); + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, 0x07); + rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); + + rtl8225_write_phy_ofdm(dev, 0x80, 0x12); + for (i = 0; i < ARRAY_SIZE(rtl8225z2_agc); i++) { + rtl8225_write_phy_ofdm(dev, 0xF, rtl8225z2_agc[i]); + rtl8225_write_phy_ofdm(dev, 0xE, 0x80 + i); + rtl8225_write_phy_ofdm(dev, 0xE, 0); + } + rtl8225_write_phy_ofdm(dev, 0x80, 0x10); + + for (i = 0; i < ARRAY_SIZE(rtl8225z2_ofdm); i++) + rtl8225_write_phy_ofdm(dev, i, rtl8225z2_ofdm[i]); + + rtl8225_write_phy_ofdm(dev, 0x97, 0x46); + rtl8225_write_phy_ofdm(dev, 0xa4, 0xb6); + rtl8225_write_phy_ofdm(dev, 0x85, 0xfc); + rtl8225_write_phy_cck(dev, 0xc1, 0x88); +} + +static void rtl8225_rf_stop(struct ieee80211_hw *dev) +{ + rtl8225_write(dev, 0x4, 0x1f); +} + +static void rtl8225_rf_set_channel(struct ieee80211_hw *dev, + struct ieee80211_conf *conf) +{ + struct rtl8187_priv *priv = dev->priv; + int chan = ieee80211_frequency_to_channel(conf->channel->center_freq); + + if (priv->rf->init == rtl8225_rf_init) + rtl8225_rf_set_tx_power(dev, chan); + else if (priv->rf->init == rtl8225z2_rf_init) + rtl8225z2_rf_set_tx_power(dev, chan); + else + rtl8225z2_b_rf_set_tx_power(dev, chan); + + rtl8225_write(dev, 0x7, rtl8225_chan[chan - 1]); + msleep(10); +} + +static const struct rtl818x_rf_ops rtl8225_ops = { + .name = "rtl8225", + .init = rtl8225_rf_init, + .stop = rtl8225_rf_stop, + .set_chan = rtl8225_rf_set_channel +}; + +static const struct rtl818x_rf_ops rtl8225z2_ops = { + .name = "rtl8225z2", + .init = rtl8225z2_rf_init, + .stop = rtl8225_rf_stop, + .set_chan = rtl8225_rf_set_channel +}; + +static const struct rtl818x_rf_ops rtl8225z2_b_ops = { + .name = "rtl8225z2", + .init = rtl8225z2_b_rf_init, + .stop = rtl8225_rf_stop, + .set_chan = rtl8225_rf_set_channel +}; + +const struct rtl818x_rf_ops * rtl8187_detect_rf(struct ieee80211_hw *dev) +{ + u16 reg8, reg9; + struct rtl8187_priv *priv = dev->priv; + + if (!priv->is_rtl8187b) { + rtl8225_write(dev, 0, 0x1B7); + + reg8 = rtl8225_read(dev, 8); + reg9 = rtl8225_read(dev, 9); + + rtl8225_write(dev, 0, 0x0B7); + + if (reg8 != 0x588 || reg9 != 0x700) + return &rtl8225_ops; + + return &rtl8225z2_ops; + } else + return &rtl8225z2_b_ops; +} diff --git a/drivers/net/wireless/rtl818x/rtl8187/rtl8225.h b/drivers/net/wireless/rtl818x/rtl8187/rtl8225.h new file mode 100644 index 000000000000..20c5b6ead0f6 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8187/rtl8225.h @@ -0,0 +1,44 @@ +/* + * Radio tuning definitions for RTL8225 on RTL8187 + * + * Copyright 2007 Michael Wu + * Copyright 2007 Andrea Merello + * + * Based on the r8187 driver, which is: + * Copyright 2005 Andrea Merello , et al. + * + * 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. + */ + +#ifndef RTL8187_RTL8225_H +#define RTL8187_RTL8225_H + +#define RTL8187_RTL8225_ANAPARAM_ON 0xa0000a59 +#define RTL8187_RTL8225_ANAPARAM2_ON 0x860c7312 +#define RTL8187_RTL8225_ANAPARAM_OFF 0xa00beb59 +#define RTL8187_RTL8225_ANAPARAM2_OFF 0x840dec11 + +#define RTL8187B_RTL8225_ANAPARAM_ON 0x45090658 +#define RTL8187B_RTL8225_ANAPARAM2_ON 0x727f3f52 +#define RTL8187B_RTL8225_ANAPARAM3_ON 0x00 +#define RTL8187B_RTL8225_ANAPARAM_OFF 0x55480658 +#define RTL8187B_RTL8225_ANAPARAM2_OFF 0x72003f50 +#define RTL8187B_RTL8225_ANAPARAM3_OFF 0x00 + +const struct rtl818x_rf_ops * rtl8187_detect_rf(struct ieee80211_hw *); + +static inline void rtl8225_write_phy_ofdm(struct ieee80211_hw *dev, + u8 addr, u32 data) +{ + rtl8187_write_phy(dev, addr, data); +} + +static inline void rtl8225_write_phy_cck(struct ieee80211_hw *dev, + u8 addr, u32 data) +{ + rtl8187_write_phy(dev, addr, data | 0x10000); +} + +#endif /* RTL8187_RTL8225_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c deleted file mode 100644 index eeee244fcaab..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8187_dev.c +++ /dev/null @@ -1,1591 +0,0 @@ -/* - * Linux device driver for RTL8187 - * - * Copyright 2007 Michael Wu - * Copyright 2007 Andrea Merello - * - * Based on the r8187 driver, which is: - * Copyright 2005 Andrea Merello , et al. - * - * The driver was extended to the RTL8187B in 2008 by: - * Herton Ronaldo Krzesinski - * Hin-Tak Leung - * Larry Finger - * - * Magic delays and register offsets below are taken from the original - * r8187 driver sources. Thanks to Realtek for their support! - * - * 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 -#include -#include -#include -#include -#include -#include - -#include "rtl8187.h" -#include "rtl8187_rtl8225.h" -#ifdef CONFIG_RTL8187_LEDS -#include "rtl8187_leds.h" -#endif -#include "rtl8187_rfkill.h" - -MODULE_AUTHOR("Michael Wu "); -MODULE_AUTHOR("Andrea Merello "); -MODULE_AUTHOR("Herton Ronaldo Krzesinski "); -MODULE_AUTHOR("Hin-Tak Leung "); -MODULE_AUTHOR("Larry Finger "); -MODULE_DESCRIPTION("RTL8187/RTL8187B USB wireless driver"); -MODULE_LICENSE("GPL"); - -static struct usb_device_id rtl8187_table[] __devinitdata = { - /* Asus */ - {USB_DEVICE(0x0b05, 0x171d), .driver_info = DEVICE_RTL8187}, - /* Belkin */ - {USB_DEVICE(0x050d, 0x705e), .driver_info = DEVICE_RTL8187B}, - /* Realtek */ - {USB_DEVICE(0x0bda, 0x8187), .driver_info = DEVICE_RTL8187}, - {USB_DEVICE(0x0bda, 0x8189), .driver_info = DEVICE_RTL8187B}, - {USB_DEVICE(0x0bda, 0x8197), .driver_info = DEVICE_RTL8187B}, - {USB_DEVICE(0x0bda, 0x8198), .driver_info = DEVICE_RTL8187B}, - /* Surecom */ - {USB_DEVICE(0x0769, 0x11F2), .driver_info = DEVICE_RTL8187}, - /* Logitech */ - {USB_DEVICE(0x0789, 0x010C), .driver_info = DEVICE_RTL8187}, - /* Netgear */ - {USB_DEVICE(0x0846, 0x6100), .driver_info = DEVICE_RTL8187}, - {USB_DEVICE(0x0846, 0x6a00), .driver_info = DEVICE_RTL8187}, - {USB_DEVICE(0x0846, 0x4260), .driver_info = DEVICE_RTL8187B}, - /* HP */ - {USB_DEVICE(0x03f0, 0xca02), .driver_info = DEVICE_RTL8187}, - /* Sitecom */ - {USB_DEVICE(0x0df6, 0x000d), .driver_info = DEVICE_RTL8187}, - {USB_DEVICE(0x0df6, 0x0028), .driver_info = DEVICE_RTL8187B}, - {USB_DEVICE(0x0df6, 0x0029), .driver_info = DEVICE_RTL8187B}, - /* Sphairon Access Systems GmbH */ - {USB_DEVICE(0x114B, 0x0150), .driver_info = DEVICE_RTL8187}, - /* Dick Smith Electronics */ - {USB_DEVICE(0x1371, 0x9401), .driver_info = DEVICE_RTL8187}, - /* Abocom */ - {USB_DEVICE(0x13d1, 0xabe6), .driver_info = DEVICE_RTL8187}, - /* Qcom */ - {USB_DEVICE(0x18E8, 0x6232), .driver_info = DEVICE_RTL8187}, - /* AirLive */ - {USB_DEVICE(0x1b75, 0x8187), .driver_info = DEVICE_RTL8187}, - /* Linksys */ - {USB_DEVICE(0x1737, 0x0073), .driver_info = DEVICE_RTL8187B}, - {} -}; - -MODULE_DEVICE_TABLE(usb, rtl8187_table); - -static const struct ieee80211_rate rtl818x_rates[] = { - { .bitrate = 10, .hw_value = 0, }, - { .bitrate = 20, .hw_value = 1, }, - { .bitrate = 55, .hw_value = 2, }, - { .bitrate = 110, .hw_value = 3, }, - { .bitrate = 60, .hw_value = 4, }, - { .bitrate = 90, .hw_value = 5, }, - { .bitrate = 120, .hw_value = 6, }, - { .bitrate = 180, .hw_value = 7, }, - { .bitrate = 240, .hw_value = 8, }, - { .bitrate = 360, .hw_value = 9, }, - { .bitrate = 480, .hw_value = 10, }, - { .bitrate = 540, .hw_value = 11, }, -}; - -static const struct ieee80211_channel rtl818x_channels[] = { - { .center_freq = 2412 }, - { .center_freq = 2417 }, - { .center_freq = 2422 }, - { .center_freq = 2427 }, - { .center_freq = 2432 }, - { .center_freq = 2437 }, - { .center_freq = 2442 }, - { .center_freq = 2447 }, - { .center_freq = 2452 }, - { .center_freq = 2457 }, - { .center_freq = 2462 }, - { .center_freq = 2467 }, - { .center_freq = 2472 }, - { .center_freq = 2484 }, -}; - -static void rtl8187_iowrite_async_cb(struct urb *urb) -{ - kfree(urb->context); -} - -static void rtl8187_iowrite_async(struct rtl8187_priv *priv, __le16 addr, - void *data, u16 len) -{ - struct usb_ctrlrequest *dr; - struct urb *urb; - struct rtl8187_async_write_data { - u8 data[4]; - struct usb_ctrlrequest dr; - } *buf; - int rc; - - buf = kmalloc(sizeof(*buf), GFP_ATOMIC); - if (!buf) - return; - - urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!urb) { - kfree(buf); - return; - } - - dr = &buf->dr; - - dr->bRequestType = RTL8187_REQT_WRITE; - dr->bRequest = RTL8187_REQ_SET_REG; - dr->wValue = addr; - dr->wIndex = 0; - dr->wLength = cpu_to_le16(len); - - memcpy(buf, data, len); - - usb_fill_control_urb(urb, priv->udev, usb_sndctrlpipe(priv->udev, 0), - (unsigned char *)dr, buf, len, - rtl8187_iowrite_async_cb, buf); - usb_anchor_urb(urb, &priv->anchored); - rc = usb_submit_urb(urb, GFP_ATOMIC); - if (rc < 0) { - kfree(buf); - usb_unanchor_urb(urb); - } - usb_free_urb(urb); -} - -static inline void rtl818x_iowrite32_async(struct rtl8187_priv *priv, - __le32 *addr, u32 val) -{ - __le32 buf = cpu_to_le32(val); - - rtl8187_iowrite_async(priv, cpu_to_le16((unsigned long)addr), - &buf, sizeof(buf)); -} - -void rtl8187_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data) -{ - struct rtl8187_priv *priv = dev->priv; - - data <<= 8; - data |= addr | 0x80; - - rtl818x_iowrite8(priv, &priv->map->PHY[3], (data >> 24) & 0xFF); - rtl818x_iowrite8(priv, &priv->map->PHY[2], (data >> 16) & 0xFF); - rtl818x_iowrite8(priv, &priv->map->PHY[1], (data >> 8) & 0xFF); - rtl818x_iowrite8(priv, &priv->map->PHY[0], data & 0xFF); -} - -static void rtl8187_tx_cb(struct urb *urb) -{ - struct sk_buff *skb = (struct sk_buff *)urb->context; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_hw *hw = info->rate_driver_data[0]; - struct rtl8187_priv *priv = hw->priv; - - skb_pull(skb, priv->is_rtl8187b ? sizeof(struct rtl8187b_tx_hdr) : - sizeof(struct rtl8187_tx_hdr)); - ieee80211_tx_info_clear_status(info); - - if (!(urb->status) && !(info->flags & IEEE80211_TX_CTL_NO_ACK)) { - if (priv->is_rtl8187b) { - skb_queue_tail(&priv->b_tx_status.queue, skb); - - /* queue is "full", discard last items */ - while (skb_queue_len(&priv->b_tx_status.queue) > 5) { - struct sk_buff *old_skb; - - dev_dbg(&priv->udev->dev, - "transmit status queue full\n"); - - old_skb = skb_dequeue(&priv->b_tx_status.queue); - ieee80211_tx_status_irqsafe(hw, old_skb); - } - return; - } else { - info->flags |= IEEE80211_TX_STAT_ACK; - } - } - if (priv->is_rtl8187b) - ieee80211_tx_status_irqsafe(hw, skb); - else { - /* Retry information for the RTI8187 is only available by - * reading a register in the device. We are in interrupt mode - * here, thus queue the skb and finish on a work queue. */ - skb_queue_tail(&priv->b_tx_status.queue, skb); - ieee80211_queue_delayed_work(hw, &priv->work, 0); - } -} - -static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb) -{ - struct rtl8187_priv *priv = dev->priv; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - unsigned int ep; - void *buf; - struct urb *urb; - __le16 rts_dur = 0; - u32 flags; - int rc; - - urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!urb) { - kfree_skb(skb); - return NETDEV_TX_OK; - } - - flags = skb->len; - flags |= RTL818X_TX_DESC_FLAG_NO_ENC; - - flags |= ieee80211_get_tx_rate(dev, info)->hw_value << 24; - if (ieee80211_has_morefrags(((struct ieee80211_hdr *)skb->data)->frame_control)) - flags |= RTL818X_TX_DESC_FLAG_MOREFRAG; - if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) { - flags |= RTL818X_TX_DESC_FLAG_RTS; - flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; - rts_dur = ieee80211_rts_duration(dev, priv->vif, - skb->len, info); - } else if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { - flags |= RTL818X_TX_DESC_FLAG_CTS; - flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; - } - - if (!priv->is_rtl8187b) { - struct rtl8187_tx_hdr *hdr = - (struct rtl8187_tx_hdr *)skb_push(skb, sizeof(*hdr)); - hdr->flags = cpu_to_le32(flags); - hdr->len = 0; - hdr->rts_duration = rts_dur; - hdr->retry = cpu_to_le32((info->control.rates[0].count - 1) << 8); - buf = hdr; - - ep = 2; - } else { - /* fc needs to be calculated before skb_push() */ - unsigned int epmap[4] = { 6, 7, 5, 4 }; - struct ieee80211_hdr *tx_hdr = - (struct ieee80211_hdr *)(skb->data); - u16 fc = le16_to_cpu(tx_hdr->frame_control); - - struct rtl8187b_tx_hdr *hdr = - (struct rtl8187b_tx_hdr *)skb_push(skb, sizeof(*hdr)); - struct ieee80211_rate *txrate = - ieee80211_get_tx_rate(dev, info); - memset(hdr, 0, sizeof(*hdr)); - hdr->flags = cpu_to_le32(flags); - hdr->rts_duration = rts_dur; - hdr->retry = cpu_to_le32((info->control.rates[0].count - 1) << 8); - hdr->tx_duration = - ieee80211_generic_frame_duration(dev, priv->vif, - skb->len, txrate); - buf = hdr; - - if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) - ep = 12; - else - ep = epmap[skb_get_queue_mapping(skb)]; - } - - info->rate_driver_data[0] = dev; - info->rate_driver_data[1] = urb; - - usb_fill_bulk_urb(urb, priv->udev, usb_sndbulkpipe(priv->udev, ep), - buf, skb->len, rtl8187_tx_cb, skb); - urb->transfer_flags |= URB_ZERO_PACKET; - usb_anchor_urb(urb, &priv->anchored); - rc = usb_submit_urb(urb, GFP_ATOMIC); - if (rc < 0) { - usb_unanchor_urb(urb); - kfree_skb(skb); - } - usb_free_urb(urb); - - return NETDEV_TX_OK; -} - -static void rtl8187_rx_cb(struct urb *urb) -{ - struct sk_buff *skb = (struct sk_buff *)urb->context; - struct rtl8187_rx_info *info = (struct rtl8187_rx_info *)skb->cb; - struct ieee80211_hw *dev = info->dev; - struct rtl8187_priv *priv = dev->priv; - struct ieee80211_rx_status rx_status = { 0 }; - int rate, signal; - u32 flags; - unsigned long f; - - spin_lock_irqsave(&priv->rx_queue.lock, f); - __skb_unlink(skb, &priv->rx_queue); - spin_unlock_irqrestore(&priv->rx_queue.lock, f); - skb_put(skb, urb->actual_length); - - if (unlikely(urb->status)) { - dev_kfree_skb_irq(skb); - return; - } - - if (!priv->is_rtl8187b) { - struct rtl8187_rx_hdr *hdr = - (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr)); - flags = le32_to_cpu(hdr->flags); - /* As with the RTL8187B below, the AGC is used to calculate - * signal strength. In this case, the scaling - * constants are derived from the output of p54usb. - */ - signal = -4 - ((27 * hdr->agc) >> 6); - rx_status.antenna = (hdr->signal >> 7) & 1; - rx_status.mactime = le64_to_cpu(hdr->mac_time); - } else { - struct rtl8187b_rx_hdr *hdr = - (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr)); - /* The Realtek datasheet for the RTL8187B shows that the RX - * header contains the following quantities: signal quality, - * RSSI, AGC, the received power in dB, and the measured SNR. - * In testing, none of these quantities show qualitative - * agreement with AP signal strength, except for the AGC, - * which is inversely proportional to the strength of the - * signal. In the following, the signal strength - * is derived from the AGC. The arbitrary scaling constants - * are chosen to make the results close to the values obtained - * for a BCM4312 using b43 as the driver. The noise is ignored - * for now. - */ - flags = le32_to_cpu(hdr->flags); - signal = 14 - hdr->agc / 2; - rx_status.antenna = (hdr->rssi >> 7) & 1; - rx_status.mactime = le64_to_cpu(hdr->mac_time); - } - - rx_status.signal = signal; - priv->signal = signal; - rate = (flags >> 20) & 0xF; - skb_trim(skb, flags & 0x0FFF); - rx_status.rate_idx = rate; - rx_status.freq = dev->conf.channel->center_freq; - rx_status.band = dev->conf.channel->band; - rx_status.flag |= RX_FLAG_TSFT; - if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR) - rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; - memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); - ieee80211_rx_irqsafe(dev, skb); - - skb = dev_alloc_skb(RTL8187_MAX_RX); - if (unlikely(!skb)) { - /* TODO check rx queue length and refill *somewhere* */ - return; - } - - info = (struct rtl8187_rx_info *)skb->cb; - info->urb = urb; - info->dev = dev; - urb->transfer_buffer = skb_tail_pointer(skb); - urb->context = skb; - skb_queue_tail(&priv->rx_queue, skb); - - usb_anchor_urb(urb, &priv->anchored); - if (usb_submit_urb(urb, GFP_ATOMIC)) { - usb_unanchor_urb(urb); - skb_unlink(skb, &priv->rx_queue); - dev_kfree_skb_irq(skb); - } -} - -static int rtl8187_init_urbs(struct ieee80211_hw *dev) -{ - struct rtl8187_priv *priv = dev->priv; - struct urb *entry = NULL; - struct sk_buff *skb; - struct rtl8187_rx_info *info; - int ret = 0; - - while (skb_queue_len(&priv->rx_queue) < 16) { - skb = __dev_alloc_skb(RTL8187_MAX_RX, GFP_KERNEL); - if (!skb) { - ret = -ENOMEM; - goto err; - } - entry = usb_alloc_urb(0, GFP_KERNEL); - if (!entry) { - ret = -ENOMEM; - goto err; - } - usb_fill_bulk_urb(entry, priv->udev, - usb_rcvbulkpipe(priv->udev, - priv->is_rtl8187b ? 3 : 1), - skb_tail_pointer(skb), - RTL8187_MAX_RX, rtl8187_rx_cb, skb); - info = (struct rtl8187_rx_info *)skb->cb; - info->urb = entry; - info->dev = dev; - skb_queue_tail(&priv->rx_queue, skb); - usb_anchor_urb(entry, &priv->anchored); - ret = usb_submit_urb(entry, GFP_KERNEL); - if (ret) { - skb_unlink(skb, &priv->rx_queue); - usb_unanchor_urb(entry); - goto err; - } - usb_free_urb(entry); - } - return ret; - -err: - usb_free_urb(entry); - kfree_skb(skb); - usb_kill_anchored_urbs(&priv->anchored); - return ret; -} - -static void rtl8187b_status_cb(struct urb *urb) -{ - struct ieee80211_hw *hw = (struct ieee80211_hw *)urb->context; - struct rtl8187_priv *priv = hw->priv; - u64 val; - unsigned int cmd_type; - - if (unlikely(urb->status)) - return; - - /* - * Read from status buffer: - * - * bits [30:31] = cmd type: - * - 0 indicates tx beacon interrupt - * - 1 indicates tx close descriptor - * - * In the case of tx beacon interrupt: - * [0:9] = Last Beacon CW - * [10:29] = reserved - * [30:31] = 00b - * [32:63] = Last Beacon TSF - * - * If it's tx close descriptor: - * [0:7] = Packet Retry Count - * [8:14] = RTS Retry Count - * [15] = TOK - * [16:27] = Sequence No - * [28] = LS - * [29] = FS - * [30:31] = 01b - * [32:47] = unused (reserved?) - * [48:63] = MAC Used Time - */ - val = le64_to_cpu(priv->b_tx_status.buf); - - cmd_type = (val >> 30) & 0x3; - if (cmd_type == 1) { - unsigned int pkt_rc, seq_no; - bool tok; - struct sk_buff *skb; - struct ieee80211_hdr *ieee80211hdr; - unsigned long flags; - - pkt_rc = val & 0xFF; - tok = val & (1 << 15); - seq_no = (val >> 16) & 0xFFF; - - spin_lock_irqsave(&priv->b_tx_status.queue.lock, flags); - skb_queue_reverse_walk(&priv->b_tx_status.queue, skb) { - ieee80211hdr = (struct ieee80211_hdr *)skb->data; - - /* - * While testing, it was discovered that the seq_no - * doesn't actually contains the sequence number. - * Instead of returning just the 12 bits of sequence - * number, hardware is returning entire sequence control - * (fragment number plus sequence number) in a 12 bit - * only field overflowing after some time. As a - * workaround, just consider the lower bits, and expect - * it's unlikely we wrongly ack some sent data - */ - if ((le16_to_cpu(ieee80211hdr->seq_ctrl) - & 0xFFF) == seq_no) - break; - } - if (skb != (struct sk_buff *) &priv->b_tx_status.queue) { - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - - __skb_unlink(skb, &priv->b_tx_status.queue); - if (tok) - info->flags |= IEEE80211_TX_STAT_ACK; - info->status.rates[0].count = pkt_rc + 1; - - ieee80211_tx_status_irqsafe(hw, skb); - } - spin_unlock_irqrestore(&priv->b_tx_status.queue.lock, flags); - } - - usb_anchor_urb(urb, &priv->anchored); - if (usb_submit_urb(urb, GFP_ATOMIC)) - usb_unanchor_urb(urb); -} - -static int rtl8187b_init_status_urb(struct ieee80211_hw *dev) -{ - struct rtl8187_priv *priv = dev->priv; - struct urb *entry; - int ret = 0; - - entry = usb_alloc_urb(0, GFP_KERNEL); - if (!entry) - return -ENOMEM; - - usb_fill_bulk_urb(entry, priv->udev, usb_rcvbulkpipe(priv->udev, 9), - &priv->b_tx_status.buf, sizeof(priv->b_tx_status.buf), - rtl8187b_status_cb, dev); - - usb_anchor_urb(entry, &priv->anchored); - ret = usb_submit_urb(entry, GFP_KERNEL); - if (ret) - usb_unanchor_urb(entry); - usb_free_urb(entry); - - return ret; -} - -static void rtl8187_set_anaparam(struct rtl8187_priv *priv, bool rfon) -{ - u32 anaparam, anaparam2; - u8 anaparam3, reg; - - if (!priv->is_rtl8187b) { - if (rfon) { - anaparam = RTL8187_RTL8225_ANAPARAM_ON; - anaparam2 = RTL8187_RTL8225_ANAPARAM2_ON; - } else { - anaparam = RTL8187_RTL8225_ANAPARAM_OFF; - anaparam2 = RTL8187_RTL8225_ANAPARAM2_OFF; - } - } else { - if (rfon) { - anaparam = RTL8187B_RTL8225_ANAPARAM_ON; - anaparam2 = RTL8187B_RTL8225_ANAPARAM2_ON; - anaparam3 = RTL8187B_RTL8225_ANAPARAM3_ON; - } else { - anaparam = RTL8187B_RTL8225_ANAPARAM_OFF; - anaparam2 = RTL8187B_RTL8225_ANAPARAM2_OFF; - anaparam3 = RTL8187B_RTL8225_ANAPARAM3_OFF; - } - } - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, - RTL818X_EEPROM_CMD_CONFIG); - reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); - reg |= RTL818X_CONFIG3_ANAPARAM_WRITE; - rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg); - rtl818x_iowrite32(priv, &priv->map->ANAPARAM, anaparam); - rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, anaparam2); - if (priv->is_rtl8187b) - rtl818x_iowrite8(priv, &priv->map->ANAPARAM3, anaparam3); - reg &= ~RTL818X_CONFIG3_ANAPARAM_WRITE; - rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, - RTL818X_EEPROM_CMD_NORMAL); -} - -static int rtl8187_cmd_reset(struct ieee80211_hw *dev) -{ - struct rtl8187_priv *priv = dev->priv; - u8 reg; - int i; - - reg = rtl818x_ioread8(priv, &priv->map->CMD); - reg &= (1 << 1); - reg |= RTL818X_CMD_RESET; - rtl818x_iowrite8(priv, &priv->map->CMD, reg); - - i = 10; - do { - msleep(2); - if (!(rtl818x_ioread8(priv, &priv->map->CMD) & - RTL818X_CMD_RESET)) - break; - } while (--i); - - if (!i) { - wiphy_err(dev->wiphy, "Reset timeout!\n"); - return -ETIMEDOUT; - } - - /* reload registers from eeprom */ - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_LOAD); - - i = 10; - do { - msleep(4); - if (!(rtl818x_ioread8(priv, &priv->map->EEPROM_CMD) & - RTL818X_EEPROM_CMD_CONFIG)) - break; - } while (--i); - - if (!i) { - wiphy_err(dev->wiphy, "eeprom reset timeout!\n"); - return -ETIMEDOUT; - } - - return 0; -} - -static int rtl8187_init_hw(struct ieee80211_hw *dev) -{ - struct rtl8187_priv *priv = dev->priv; - u8 reg; - int res; - - /* reset */ - rtl8187_set_anaparam(priv, true); - - rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0); - - msleep(200); - rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x10); - rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x11); - rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x00); - msleep(200); - - res = rtl8187_cmd_reset(dev); - if (res) - return res; - - rtl8187_set_anaparam(priv, true); - - /* setup card */ - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0); - rtl818x_iowrite8(priv, &priv->map->GPIO0, 0); - - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, (4 << 8)); - rtl818x_iowrite8(priv, &priv->map->GPIO0, 1); - rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0); - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); - - rtl818x_iowrite16(priv, (__le16 *)0xFFF4, 0xFFFF); - reg = rtl818x_ioread8(priv, &priv->map->CONFIG1); - reg &= 0x3F; - reg |= 0x80; - rtl818x_iowrite8(priv, &priv->map->CONFIG1, reg); - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); - - rtl818x_iowrite32(priv, &priv->map->INT_TIMEOUT, 0); - rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0); - rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, 0); - - // TODO: set RESP_RATE and BRSR properly - rtl818x_iowrite8(priv, &priv->map->RESP_RATE, (8 << 4) | 0); - rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3); - - /* host_usb_init */ - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0); - rtl818x_iowrite8(priv, &priv->map->GPIO0, 0); - reg = rtl818x_ioread8(priv, (u8 *)0xFE53); - rtl818x_iowrite8(priv, (u8 *)0xFE53, reg | (1 << 7)); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, (4 << 8)); - rtl818x_iowrite8(priv, &priv->map->GPIO0, 0x20); - rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x80); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x80); - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x80); - msleep(100); - - rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x000a8008); - rtl818x_iowrite16(priv, &priv->map->BRSR, 0xFFFF); - rtl818x_iowrite32(priv, &priv->map->RF_PARA, 0x00100044); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, - RTL818X_EEPROM_CMD_CONFIG); - rtl818x_iowrite8(priv, &priv->map->CONFIG3, 0x44); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, - RTL818X_EEPROM_CMD_NORMAL); - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FF7); - msleep(100); - - priv->rf->init(dev); - - rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3); - reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~1; - rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg | 1); - rtl818x_iowrite16(priv, (__le16 *)0xFFFE, 0x10); - rtl818x_iowrite8(priv, &priv->map->TALLY_SEL, 0x80); - rtl818x_iowrite8(priv, (u8 *)0xFFFF, 0x60); - rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); - - return 0; -} - -static const u8 rtl8187b_reg_table[][3] = { - {0xF0, 0x32, 0}, {0xF1, 0x32, 0}, {0xF2, 0x00, 0}, {0xF3, 0x00, 0}, - {0xF4, 0x32, 0}, {0xF5, 0x43, 0}, {0xF6, 0x00, 0}, {0xF7, 0x00, 0}, - {0xF8, 0x46, 0}, {0xF9, 0xA4, 0}, {0xFA, 0x00, 0}, {0xFB, 0x00, 0}, - {0xFC, 0x96, 0}, {0xFD, 0xA4, 0}, {0xFE, 0x00, 0}, {0xFF, 0x00, 0}, - - {0x58, 0x4B, 1}, {0x59, 0x00, 1}, {0x5A, 0x4B, 1}, {0x5B, 0x00, 1}, - {0x60, 0x4B, 1}, {0x61, 0x09, 1}, {0x62, 0x4B, 1}, {0x63, 0x09, 1}, - {0xCE, 0x0F, 1}, {0xCF, 0x00, 1}, {0xF0, 0x4E, 1}, {0xF1, 0x01, 1}, - {0xF2, 0x02, 1}, {0xF3, 0x03, 1}, {0xF4, 0x04, 1}, {0xF5, 0x05, 1}, - {0xF6, 0x06, 1}, {0xF7, 0x07, 1}, {0xF8, 0x08, 1}, - - {0x4E, 0x00, 2}, {0x0C, 0x04, 2}, {0x21, 0x61, 2}, {0x22, 0x68, 2}, - {0x23, 0x6F, 2}, {0x24, 0x76, 2}, {0x25, 0x7D, 2}, {0x26, 0x84, 2}, - {0x27, 0x8D, 2}, {0x4D, 0x08, 2}, {0x50, 0x05, 2}, {0x51, 0xF5, 2}, - {0x52, 0x04, 2}, {0x53, 0xA0, 2}, {0x54, 0x1F, 2}, {0x55, 0x23, 2}, - {0x56, 0x45, 2}, {0x57, 0x67, 2}, {0x58, 0x08, 2}, {0x59, 0x08, 2}, - {0x5A, 0x08, 2}, {0x5B, 0x08, 2}, {0x60, 0x08, 2}, {0x61, 0x08, 2}, - {0x62, 0x08, 2}, {0x63, 0x08, 2}, {0x64, 0xCF, 2}, - - {0x5B, 0x40, 0}, {0x84, 0x88, 0}, {0x85, 0x24, 0}, {0x88, 0x54, 0}, - {0x8B, 0xB8, 0}, {0x8C, 0x07, 0}, {0x8D, 0x00, 0}, {0x94, 0x1B, 0}, - {0x95, 0x12, 0}, {0x96, 0x00, 0}, {0x97, 0x06, 0}, {0x9D, 0x1A, 0}, - {0x9F, 0x10, 0}, {0xB4, 0x22, 0}, {0xBE, 0x80, 0}, {0xDB, 0x00, 0}, - {0xEE, 0x00, 0}, {0x4C, 0x00, 2}, - - {0x9F, 0x00, 3}, {0x8C, 0x01, 0}, {0x8D, 0x10, 0}, {0x8E, 0x08, 0}, - {0x8F, 0x00, 0} -}; - -static int rtl8187b_init_hw(struct ieee80211_hw *dev) -{ - struct rtl8187_priv *priv = dev->priv; - int res, i; - u8 reg; - - rtl8187_set_anaparam(priv, true); - - /* Reset PLL sequence on 8187B. Realtek note: reduces power - * consumption about 30 mA */ - rtl818x_iowrite8(priv, (u8 *)0xFF61, 0x10); - reg = rtl818x_ioread8(priv, (u8 *)0xFF62); - rtl818x_iowrite8(priv, (u8 *)0xFF62, reg & ~(1 << 5)); - rtl818x_iowrite8(priv, (u8 *)0xFF62, reg | (1 << 5)); - - res = rtl8187_cmd_reset(dev); - if (res) - return res; - - rtl8187_set_anaparam(priv, true); - - /* BRSR (Basic Rate Set Register) on 8187B looks to be the same as - * RESP_RATE on 8187L in Realtek sources: each bit should be each - * one of the 12 rates, all are enabled */ - rtl818x_iowrite16(priv, (__le16 *)0xFF34, 0x0FFF); - - reg = rtl818x_ioread8(priv, &priv->map->CW_CONF); - reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT; - rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg); - - /* Auto Rate Fallback Register (ARFR): 1M-54M setting */ - rtl818x_iowrite16_idx(priv, (__le16 *)0xFFE0, 0x0FFF, 1); - rtl818x_iowrite8_idx(priv, (u8 *)0xFFE2, 0x00, 1); - - rtl818x_iowrite16_idx(priv, (__le16 *)0xFFD4, 0xFFFF, 1); - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, - RTL818X_EEPROM_CMD_CONFIG); - reg = rtl818x_ioread8(priv, &priv->map->CONFIG1); - rtl818x_iowrite8(priv, &priv->map->CONFIG1, (reg & 0x3F) | 0x80); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, - RTL818X_EEPROM_CMD_NORMAL); - - rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0); - for (i = 0; i < ARRAY_SIZE(rtl8187b_reg_table); i++) { - rtl818x_iowrite8_idx(priv, - (u8 *)(uintptr_t) - (rtl8187b_reg_table[i][0] | 0xFF00), - rtl8187b_reg_table[i][1], - rtl8187b_reg_table[i][2]); - } - - rtl818x_iowrite16(priv, &priv->map->TID_AC_MAP, 0xFA50); - rtl818x_iowrite16(priv, &priv->map->INT_MIG, 0); - - rtl818x_iowrite32_idx(priv, (__le32 *)0xFFF0, 0, 1); - rtl818x_iowrite32_idx(priv, (__le32 *)0xFFF4, 0, 1); - rtl818x_iowrite8_idx(priv, (u8 *)0xFFF8, 0, 1); - - rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x00004001); - - /* RFSW_CTRL register */ - rtl818x_iowrite16_idx(priv, (__le16 *)0xFF72, 0x569A, 2); - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x2488); - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF); - msleep(100); - - priv->rf->init(dev); - - reg = RTL818X_CMD_TX_ENABLE | RTL818X_CMD_RX_ENABLE; - rtl818x_iowrite8(priv, &priv->map->CMD, reg); - rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF); - - rtl818x_iowrite8(priv, (u8 *)0xFE41, 0xF4); - rtl818x_iowrite8(priv, (u8 *)0xFE40, 0x00); - rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x00); - rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x01); - rtl818x_iowrite8(priv, (u8 *)0xFE40, 0x0F); - rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x00); - rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x01); - - reg = rtl818x_ioread8(priv, (u8 *)0xFFDB); - rtl818x_iowrite8(priv, (u8 *)0xFFDB, reg | (1 << 2)); - rtl818x_iowrite16_idx(priv, (__le16 *)0xFF72, 0x59FA, 3); - rtl818x_iowrite16_idx(priv, (__le16 *)0xFF74, 0x59D2, 3); - rtl818x_iowrite16_idx(priv, (__le16 *)0xFF76, 0x59D2, 3); - rtl818x_iowrite16_idx(priv, (__le16 *)0xFF78, 0x19FA, 3); - rtl818x_iowrite16_idx(priv, (__le16 *)0xFF7A, 0x19FA, 3); - rtl818x_iowrite16_idx(priv, (__le16 *)0xFF7C, 0x00D0, 3); - rtl818x_iowrite8(priv, (u8 *)0xFF61, 0); - rtl818x_iowrite8_idx(priv, (u8 *)0xFF80, 0x0F, 1); - rtl818x_iowrite8_idx(priv, (u8 *)0xFF83, 0x03, 1); - rtl818x_iowrite8(priv, (u8 *)0xFFDA, 0x10); - rtl818x_iowrite8_idx(priv, (u8 *)0xFF4D, 0x08, 2); - - rtl818x_iowrite32(priv, &priv->map->HSSI_PARA, 0x0600321B); - - rtl818x_iowrite16_idx(priv, (__le16 *)0xFFEC, 0x0800, 1); - - priv->slot_time = 0x9; - priv->aifsn[0] = 2; /* AIFSN[AC_VO] */ - priv->aifsn[1] = 2; /* AIFSN[AC_VI] */ - priv->aifsn[2] = 7; /* AIFSN[AC_BK] */ - priv->aifsn[3] = 3; /* AIFSN[AC_BE] */ - rtl818x_iowrite8(priv, &priv->map->ACM_CONTROL, 0); - - /* ENEDCA flag must always be set, transmit issues? */ - rtl818x_iowrite8(priv, &priv->map->MSR, RTL818X_MSR_ENEDCA); - - return 0; -} - -static void rtl8187_work(struct work_struct *work) -{ - /* The RTL8187 returns the retry count through register 0xFFFA. In - * addition, it appears to be a cumulative retry count, not the - * value for the current TX packet. When multiple TX entries are - * queued, the retry count will be valid for the last one in the queue. - * The "error" should not matter for purposes of rate setting. */ - struct rtl8187_priv *priv = container_of(work, struct rtl8187_priv, - work.work); - struct ieee80211_tx_info *info; - struct ieee80211_hw *dev = priv->dev; - static u16 retry; - u16 tmp; - - mutex_lock(&priv->conf_mutex); - tmp = rtl818x_ioread16(priv, (__le16 *)0xFFFA); - while (skb_queue_len(&priv->b_tx_status.queue) > 0) { - struct sk_buff *old_skb; - - old_skb = skb_dequeue(&priv->b_tx_status.queue); - info = IEEE80211_SKB_CB(old_skb); - info->status.rates[0].count = tmp - retry + 1; - ieee80211_tx_status_irqsafe(dev, old_skb); - } - retry = tmp; - mutex_unlock(&priv->conf_mutex); -} - -static int rtl8187_start(struct ieee80211_hw *dev) -{ - struct rtl8187_priv *priv = dev->priv; - u32 reg; - int ret; - - mutex_lock(&priv->conf_mutex); - - ret = (!priv->is_rtl8187b) ? rtl8187_init_hw(dev) : - rtl8187b_init_hw(dev); - if (ret) - goto rtl8187_start_exit; - - init_usb_anchor(&priv->anchored); - priv->dev = dev; - - if (priv->is_rtl8187b) { - reg = RTL818X_RX_CONF_MGMT | - RTL818X_RX_CONF_DATA | - RTL818X_RX_CONF_BROADCAST | - RTL818X_RX_CONF_NICMAC | - RTL818X_RX_CONF_BSSID | - (7 << 13 /* RX FIFO threshold NONE */) | - (7 << 10 /* MAX RX DMA */) | - RTL818X_RX_CONF_RX_AUTORESETPHY | - RTL818X_RX_CONF_ONLYERLPKT | - RTL818X_RX_CONF_MULTICAST; - priv->rx_conf = reg; - rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg); - - reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL); - reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT; - reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT; - reg &= ~RTL818X_TX_AGC_CTL_FEEDBACK_ANT; - rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg); - - rtl818x_iowrite32(priv, &priv->map->TX_CONF, - RTL818X_TX_CONF_HW_SEQNUM | - RTL818X_TX_CONF_DISREQQSIZE | - (7 << 8 /* short retry limit */) | - (7 << 0 /* long retry limit */) | - (7 << 21 /* MAX TX DMA */)); - rtl8187_init_urbs(dev); - rtl8187b_init_status_urb(dev); - goto rtl8187_start_exit; - } - - rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF); - - rtl818x_iowrite32(priv, &priv->map->MAR[0], ~0); - rtl818x_iowrite32(priv, &priv->map->MAR[1], ~0); - - rtl8187_init_urbs(dev); - - reg = RTL818X_RX_CONF_ONLYERLPKT | - RTL818X_RX_CONF_RX_AUTORESETPHY | - RTL818X_RX_CONF_BSSID | - RTL818X_RX_CONF_MGMT | - RTL818X_RX_CONF_DATA | - (7 << 13 /* RX FIFO threshold NONE */) | - (7 << 10 /* MAX RX DMA */) | - RTL818X_RX_CONF_BROADCAST | - RTL818X_RX_CONF_NICMAC; - - priv->rx_conf = reg; - rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg); - - reg = rtl818x_ioread8(priv, &priv->map->CW_CONF); - reg &= ~RTL818X_CW_CONF_PERPACKET_CW_SHIFT; - reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT; - rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg); - - reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL); - reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT; - reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT; - reg &= ~RTL818X_TX_AGC_CTL_FEEDBACK_ANT; - rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg); - - reg = RTL818X_TX_CONF_CW_MIN | - (7 << 21 /* MAX TX DMA */) | - RTL818X_TX_CONF_NO_ICV; - rtl818x_iowrite32(priv, &priv->map->TX_CONF, reg); - - reg = rtl818x_ioread8(priv, &priv->map->CMD); - reg |= RTL818X_CMD_TX_ENABLE; - reg |= RTL818X_CMD_RX_ENABLE; - rtl818x_iowrite8(priv, &priv->map->CMD, reg); - INIT_DELAYED_WORK(&priv->work, rtl8187_work); - -rtl8187_start_exit: - mutex_unlock(&priv->conf_mutex); - return ret; -} - -static void rtl8187_stop(struct ieee80211_hw *dev) -{ - struct rtl8187_priv *priv = dev->priv; - struct sk_buff *skb; - u32 reg; - - mutex_lock(&priv->conf_mutex); - rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0); - - reg = rtl818x_ioread8(priv, &priv->map->CMD); - reg &= ~RTL818X_CMD_TX_ENABLE; - reg &= ~RTL818X_CMD_RX_ENABLE; - rtl818x_iowrite8(priv, &priv->map->CMD, reg); - - priv->rf->stop(dev); - rtl8187_set_anaparam(priv, false); - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); - reg = rtl818x_ioread8(priv, &priv->map->CONFIG4); - rtl818x_iowrite8(priv, &priv->map->CONFIG4, reg | RTL818X_CONFIG4_VCOOFF); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); - - while ((skb = skb_dequeue(&priv->b_tx_status.queue))) - dev_kfree_skb_any(skb); - - usb_kill_anchored_urbs(&priv->anchored); - mutex_unlock(&priv->conf_mutex); - - if (!priv->is_rtl8187b) - cancel_delayed_work_sync(&priv->work); -} - -static int rtl8187_add_interface(struct ieee80211_hw *dev, - struct ieee80211_vif *vif) -{ - struct rtl8187_priv *priv = dev->priv; - int i; - int ret = -EOPNOTSUPP; - - mutex_lock(&priv->conf_mutex); - if (priv->vif) - goto exit; - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - break; - default: - goto exit; - } - - ret = 0; - priv->vif = vif; - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); - for (i = 0; i < ETH_ALEN; i++) - rtl818x_iowrite8(priv, &priv->map->MAC[i], - ((u8 *)vif->addr)[i]); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); - -exit: - mutex_unlock(&priv->conf_mutex); - return ret; -} - -static void rtl8187_remove_interface(struct ieee80211_hw *dev, - struct ieee80211_vif *vif) -{ - struct rtl8187_priv *priv = dev->priv; - mutex_lock(&priv->conf_mutex); - priv->vif = NULL; - mutex_unlock(&priv->conf_mutex); -} - -static int rtl8187_config(struct ieee80211_hw *dev, u32 changed) -{ - struct rtl8187_priv *priv = dev->priv; - struct ieee80211_conf *conf = &dev->conf; - u32 reg; - - mutex_lock(&priv->conf_mutex); - reg = rtl818x_ioread32(priv, &priv->map->TX_CONF); - /* Enable TX loopback on MAC level to avoid TX during channel - * changes, as this has be seen to causes problems and the - * card will stop work until next reset - */ - rtl818x_iowrite32(priv, &priv->map->TX_CONF, - reg | RTL818X_TX_CONF_LOOPBACK_MAC); - priv->rf->set_chan(dev, conf); - msleep(10); - rtl818x_iowrite32(priv, &priv->map->TX_CONF, reg); - - rtl818x_iowrite16(priv, &priv->map->ATIM_WND, 2); - rtl818x_iowrite16(priv, &priv->map->ATIMTR_INTERVAL, 100); - rtl818x_iowrite16(priv, &priv->map->BEACON_INTERVAL, 100); - rtl818x_iowrite16(priv, &priv->map->BEACON_INTERVAL_TIME, 100); - mutex_unlock(&priv->conf_mutex); - return 0; -} - -/* - * With 8187B, AC_*_PARAM clashes with FEMR definition in struct rtl818x_csr for - * example. Thus we have to use raw values for AC_*_PARAM register addresses. - */ -static __le32 *rtl8187b_ac_addr[4] = { - (__le32 *) 0xFFF0, /* AC_VO */ - (__le32 *) 0xFFF4, /* AC_VI */ - (__le32 *) 0xFFFC, /* AC_BK */ - (__le32 *) 0xFFF8, /* AC_BE */ -}; - -#define SIFS_TIME 0xa - -static void rtl8187_conf_erp(struct rtl8187_priv *priv, bool use_short_slot, - bool use_short_preamble) -{ - if (priv->is_rtl8187b) { - u8 difs, eifs; - u16 ack_timeout; - int queue; - - if (use_short_slot) { - priv->slot_time = 0x9; - difs = 0x1c; - eifs = 0x53; - } else { - priv->slot_time = 0x14; - difs = 0x32; - eifs = 0x5b; - } - rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22); - rtl818x_iowrite8(priv, &priv->map->SLOT, priv->slot_time); - rtl818x_iowrite8(priv, &priv->map->DIFS, difs); - - /* - * BRSR+1 on 8187B is in fact EIFS register - * Value in units of 4 us - */ - rtl818x_iowrite8(priv, (u8 *)&priv->map->BRSR + 1, eifs); - - /* - * For 8187B, CARRIER_SENSE_COUNTER is in fact ack timeout - * register. In units of 4 us like eifs register - * ack_timeout = ack duration + plcp + difs + preamble - */ - ack_timeout = 112 + 48 + difs; - if (use_short_preamble) - ack_timeout += 72; - else - ack_timeout += 144; - rtl818x_iowrite8(priv, &priv->map->CARRIER_SENSE_COUNTER, - DIV_ROUND_UP(ack_timeout, 4)); - - for (queue = 0; queue < 4; queue++) - rtl818x_iowrite8(priv, (u8 *) rtl8187b_ac_addr[queue], - priv->aifsn[queue] * priv->slot_time + - SIFS_TIME); - } else { - rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22); - if (use_short_slot) { - rtl818x_iowrite8(priv, &priv->map->SLOT, 0x9); - rtl818x_iowrite8(priv, &priv->map->DIFS, 0x14); - rtl818x_iowrite8(priv, &priv->map->EIFS, 91 - 0x14); - } else { - rtl818x_iowrite8(priv, &priv->map->SLOT, 0x14); - rtl818x_iowrite8(priv, &priv->map->DIFS, 0x24); - rtl818x_iowrite8(priv, &priv->map->EIFS, 91 - 0x24); - } - } -} - -static void rtl8187_bss_info_changed(struct ieee80211_hw *dev, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *info, - u32 changed) -{ - struct rtl8187_priv *priv = dev->priv; - int i; - u8 reg; - - if (changed & BSS_CHANGED_BSSID) { - mutex_lock(&priv->conf_mutex); - for (i = 0; i < ETH_ALEN; i++) - rtl818x_iowrite8(priv, &priv->map->BSSID[i], - info->bssid[i]); - - if (priv->is_rtl8187b) - reg = RTL818X_MSR_ENEDCA; - else - reg = 0; - - if (is_valid_ether_addr(info->bssid)) - reg |= RTL818X_MSR_INFRA; - else - reg |= RTL818X_MSR_NO_LINK; - - rtl818x_iowrite8(priv, &priv->map->MSR, reg); - - mutex_unlock(&priv->conf_mutex); - } - - if (changed & (BSS_CHANGED_ERP_SLOT | BSS_CHANGED_ERP_PREAMBLE)) - rtl8187_conf_erp(priv, info->use_short_slot, - info->use_short_preamble); -} - -static u64 rtl8187_prepare_multicast(struct ieee80211_hw *dev, - struct netdev_hw_addr_list *mc_list) -{ - return netdev_hw_addr_list_count(mc_list); -} - -static void rtl8187_configure_filter(struct ieee80211_hw *dev, - unsigned int changed_flags, - unsigned int *total_flags, - u64 multicast) -{ - struct rtl8187_priv *priv = dev->priv; - - if (changed_flags & FIF_FCSFAIL) - priv->rx_conf ^= RTL818X_RX_CONF_FCS; - if (changed_flags & FIF_CONTROL) - priv->rx_conf ^= RTL818X_RX_CONF_CTRL; - if (changed_flags & FIF_OTHER_BSS) - priv->rx_conf ^= RTL818X_RX_CONF_MONITOR; - if (*total_flags & FIF_ALLMULTI || multicast > 0) - priv->rx_conf |= RTL818X_RX_CONF_MULTICAST; - else - priv->rx_conf &= ~RTL818X_RX_CONF_MULTICAST; - - *total_flags = 0; - - if (priv->rx_conf & RTL818X_RX_CONF_FCS) - *total_flags |= FIF_FCSFAIL; - if (priv->rx_conf & RTL818X_RX_CONF_CTRL) - *total_flags |= FIF_CONTROL; - if (priv->rx_conf & RTL818X_RX_CONF_MONITOR) - *total_flags |= FIF_OTHER_BSS; - if (priv->rx_conf & RTL818X_RX_CONF_MULTICAST) - *total_flags |= FIF_ALLMULTI; - - rtl818x_iowrite32_async(priv, &priv->map->RX_CONF, priv->rx_conf); -} - -static int rtl8187_conf_tx(struct ieee80211_hw *dev, u16 queue, - const struct ieee80211_tx_queue_params *params) -{ - struct rtl8187_priv *priv = dev->priv; - u8 cw_min, cw_max; - - if (queue > 3) - return -EINVAL; - - cw_min = fls(params->cw_min); - cw_max = fls(params->cw_max); - - if (priv->is_rtl8187b) { - priv->aifsn[queue] = params->aifs; - - /* - * This is the structure of AC_*_PARAM registers in 8187B: - * - TXOP limit field, bit offset = 16 - * - ECWmax, bit offset = 12 - * - ECWmin, bit offset = 8 - * - AIFS, bit offset = 0 - */ - rtl818x_iowrite32(priv, rtl8187b_ac_addr[queue], - (params->txop << 16) | (cw_max << 12) | - (cw_min << 8) | (params->aifs * - priv->slot_time + SIFS_TIME)); - } else { - if (queue != 0) - return -EINVAL; - - rtl818x_iowrite8(priv, &priv->map->CW_VAL, - cw_min | (cw_max << 4)); - } - return 0; -} - -static u64 rtl8187_get_tsf(struct ieee80211_hw *dev) -{ - struct rtl8187_priv *priv = dev->priv; - - return rtl818x_ioread32(priv, &priv->map->TSFT[0]) | - (u64)(rtl818x_ioread32(priv, &priv->map->TSFT[1])) << 32; -} - -static const struct ieee80211_ops rtl8187_ops = { - .tx = rtl8187_tx, - .start = rtl8187_start, - .stop = rtl8187_stop, - .add_interface = rtl8187_add_interface, - .remove_interface = rtl8187_remove_interface, - .config = rtl8187_config, - .bss_info_changed = rtl8187_bss_info_changed, - .prepare_multicast = rtl8187_prepare_multicast, - .configure_filter = rtl8187_configure_filter, - .conf_tx = rtl8187_conf_tx, - .rfkill_poll = rtl8187_rfkill_poll, - .get_tsf = rtl8187_get_tsf, -}; - -static void rtl8187_eeprom_register_read(struct eeprom_93cx6 *eeprom) -{ - struct ieee80211_hw *dev = eeprom->data; - struct rtl8187_priv *priv = dev->priv; - u8 reg = rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); - - eeprom->reg_data_in = reg & RTL818X_EEPROM_CMD_WRITE; - eeprom->reg_data_out = reg & RTL818X_EEPROM_CMD_READ; - eeprom->reg_data_clock = reg & RTL818X_EEPROM_CMD_CK; - eeprom->reg_chip_select = reg & RTL818X_EEPROM_CMD_CS; -} - -static void rtl8187_eeprom_register_write(struct eeprom_93cx6 *eeprom) -{ - struct ieee80211_hw *dev = eeprom->data; - struct rtl8187_priv *priv = dev->priv; - u8 reg = RTL818X_EEPROM_CMD_PROGRAM; - - if (eeprom->reg_data_in) - reg |= RTL818X_EEPROM_CMD_WRITE; - if (eeprom->reg_data_out) - reg |= RTL818X_EEPROM_CMD_READ; - if (eeprom->reg_data_clock) - reg |= RTL818X_EEPROM_CMD_CK; - if (eeprom->reg_chip_select) - reg |= RTL818X_EEPROM_CMD_CS; - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, reg); - udelay(10); -} - -static int __devinit rtl8187_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct ieee80211_hw *dev; - struct rtl8187_priv *priv; - struct eeprom_93cx6 eeprom; - struct ieee80211_channel *channel; - const char *chip_name; - u16 txpwr, reg; - u16 product_id = le16_to_cpu(udev->descriptor.idProduct); - int err, i; - u8 mac_addr[ETH_ALEN]; - - dev = ieee80211_alloc_hw(sizeof(*priv), &rtl8187_ops); - if (!dev) { - printk(KERN_ERR "rtl8187: ieee80211 alloc failed\n"); - return -ENOMEM; - } - - priv = dev->priv; - priv->is_rtl8187b = (id->driver_info == DEVICE_RTL8187B); - - /* allocate "DMA aware" buffer for register accesses */ - priv->io_dmabuf = kmalloc(sizeof(*priv->io_dmabuf), GFP_KERNEL); - if (!priv->io_dmabuf) { - err = -ENOMEM; - goto err_free_dev; - } - mutex_init(&priv->io_mutex); - - SET_IEEE80211_DEV(dev, &intf->dev); - usb_set_intfdata(intf, dev); - priv->udev = udev; - - usb_get_dev(udev); - - skb_queue_head_init(&priv->rx_queue); - - BUILD_BUG_ON(sizeof(priv->channels) != sizeof(rtl818x_channels)); - BUILD_BUG_ON(sizeof(priv->rates) != sizeof(rtl818x_rates)); - - memcpy(priv->channels, rtl818x_channels, sizeof(rtl818x_channels)); - memcpy(priv->rates, rtl818x_rates, sizeof(rtl818x_rates)); - priv->map = (struct rtl818x_csr *)0xFF00; - - priv->band.band = IEEE80211_BAND_2GHZ; - priv->band.channels = priv->channels; - priv->band.n_channels = ARRAY_SIZE(rtl818x_channels); - priv->band.bitrates = priv->rates; - priv->band.n_bitrates = ARRAY_SIZE(rtl818x_rates); - dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; - - - dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | - IEEE80211_HW_SIGNAL_DBM | - IEEE80211_HW_RX_INCLUDES_FCS; - - eeprom.data = dev; - eeprom.register_read = rtl8187_eeprom_register_read; - eeprom.register_write = rtl8187_eeprom_register_write; - if (rtl818x_ioread32(priv, &priv->map->RX_CONF) & (1 << 6)) - eeprom.width = PCI_EEPROM_WIDTH_93C66; - else - eeprom.width = PCI_EEPROM_WIDTH_93C46; - - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); - udelay(10); - - eeprom_93cx6_multiread(&eeprom, RTL8187_EEPROM_MAC_ADDR, - (__le16 __force *)mac_addr, 3); - if (!is_valid_ether_addr(mac_addr)) { - printk(KERN_WARNING "rtl8187: Invalid hwaddr! Using randomly " - "generated MAC address\n"); - random_ether_addr(mac_addr); - } - SET_IEEE80211_PERM_ADDR(dev, mac_addr); - - channel = priv->channels; - for (i = 0; i < 3; i++) { - eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_CHAN_1 + i, - &txpwr); - (*channel++).hw_value = txpwr & 0xFF; - (*channel++).hw_value = txpwr >> 8; - } - for (i = 0; i < 2; i++) { - eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_CHAN_4 + i, - &txpwr); - (*channel++).hw_value = txpwr & 0xFF; - (*channel++).hw_value = txpwr >> 8; - } - - eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_BASE, - &priv->txpwr_base); - - reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~1; - rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg | 1); - /* 0 means asic B-cut, we should use SW 3 wire - * bit-by-bit banging for radio. 1 means we can use - * USB specific request to write radio registers */ - priv->asic_rev = rtl818x_ioread8(priv, (u8 *)0xFFFE) & 0x3; - rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); - - if (!priv->is_rtl8187b) { - u32 reg32; - reg32 = rtl818x_ioread32(priv, &priv->map->TX_CONF); - reg32 &= RTL818X_TX_CONF_HWVER_MASK; - switch (reg32) { - case RTL818X_TX_CONF_R8187vD_B: - /* Some RTL8187B devices have a USB ID of 0x8187 - * detect them here */ - chip_name = "RTL8187BvB(early)"; - priv->is_rtl8187b = 1; - priv->hw_rev = RTL8187BvB; - break; - case RTL818X_TX_CONF_R8187vD: - chip_name = "RTL8187vD"; - break; - default: - chip_name = "RTL8187vB (default)"; - } - } else { - /* - * Force USB request to write radio registers for 8187B, Realtek - * only uses it in their sources - */ - /*if (priv->asic_rev == 0) { - printk(KERN_WARNING "rtl8187: Forcing use of USB " - "requests to write to radio registers\n"); - priv->asic_rev = 1; - }*/ - switch (rtl818x_ioread8(priv, (u8 *)0xFFE1)) { - case RTL818X_R8187B_B: - chip_name = "RTL8187BvB"; - priv->hw_rev = RTL8187BvB; - break; - case RTL818X_R8187B_D: - chip_name = "RTL8187BvD"; - priv->hw_rev = RTL8187BvD; - break; - case RTL818X_R8187B_E: - chip_name = "RTL8187BvE"; - priv->hw_rev = RTL8187BvE; - break; - default: - chip_name = "RTL8187BvB (default)"; - priv->hw_rev = RTL8187BvB; - } - } - - if (!priv->is_rtl8187b) { - for (i = 0; i < 2; i++) { - eeprom_93cx6_read(&eeprom, - RTL8187_EEPROM_TXPWR_CHAN_6 + i, - &txpwr); - (*channel++).hw_value = txpwr & 0xFF; - (*channel++).hw_value = txpwr >> 8; - } - } else { - eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_CHAN_6, - &txpwr); - (*channel++).hw_value = txpwr & 0xFF; - - eeprom_93cx6_read(&eeprom, 0x0A, &txpwr); - (*channel++).hw_value = txpwr & 0xFF; - - eeprom_93cx6_read(&eeprom, 0x1C, &txpwr); - (*channel++).hw_value = txpwr & 0xFF; - (*channel++).hw_value = txpwr >> 8; - } - /* Handle the differing rfkill GPIO bit in different models */ - priv->rfkill_mask = RFKILL_MASK_8187_89_97; - if (product_id == 0x8197 || product_id == 0x8198) { - eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_SELECT_GPIO, ®); - if (reg & 0xFF00) - priv->rfkill_mask = RFKILL_MASK_8198; - } - - /* - * XXX: Once this driver supports anything that requires - * beacons it must implement IEEE80211_TX_CTL_ASSIGN_SEQ. - */ - dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); - - if ((id->driver_info == DEVICE_RTL8187) && priv->is_rtl8187b) - printk(KERN_INFO "rtl8187: inconsistency between id with OEM" - " info!\n"); - - priv->rf = rtl8187_detect_rf(dev); - dev->extra_tx_headroom = (!priv->is_rtl8187b) ? - sizeof(struct rtl8187_tx_hdr) : - sizeof(struct rtl8187b_tx_hdr); - if (!priv->is_rtl8187b) - dev->queues = 1; - else - dev->queues = 4; - - err = ieee80211_register_hw(dev); - if (err) { - printk(KERN_ERR "rtl8187: Cannot register device\n"); - goto err_free_dmabuf; - } - mutex_init(&priv->conf_mutex); - skb_queue_head_init(&priv->b_tx_status.queue); - - wiphy_info(dev->wiphy, "hwaddr %pM, %s V%d + %s, rfkill mask %d\n", - mac_addr, chip_name, priv->asic_rev, priv->rf->name, - priv->rfkill_mask); - -#ifdef CONFIG_RTL8187_LEDS - eeprom_93cx6_read(&eeprom, 0x3F, ®); - reg &= 0xFF; - rtl8187_leds_init(dev, reg); -#endif - rtl8187_rfkill_init(dev); - - return 0; - - err_free_dmabuf: - kfree(priv->io_dmabuf); - err_free_dev: - ieee80211_free_hw(dev); - usb_set_intfdata(intf, NULL); - usb_put_dev(udev); - return err; -} - -static void __devexit rtl8187_disconnect(struct usb_interface *intf) -{ - struct ieee80211_hw *dev = usb_get_intfdata(intf); - struct rtl8187_priv *priv; - - if (!dev) - return; - -#ifdef CONFIG_RTL8187_LEDS - rtl8187_leds_exit(dev); -#endif - rtl8187_rfkill_exit(dev); - ieee80211_unregister_hw(dev); - - priv = dev->priv; - usb_reset_device(priv->udev); - usb_put_dev(interface_to_usbdev(intf)); - kfree(priv->io_dmabuf); - ieee80211_free_hw(dev); -} - -static struct usb_driver rtl8187_driver = { - .name = KBUILD_MODNAME, - .id_table = rtl8187_table, - .probe = rtl8187_probe, - .disconnect = __devexit_p(rtl8187_disconnect), -}; - -static int __init rtl8187_init(void) -{ - return usb_register(&rtl8187_driver); -} - -static void __exit rtl8187_exit(void) -{ - usb_deregister(&rtl8187_driver); -} - -module_init(rtl8187_init); -module_exit(rtl8187_exit); diff --git a/drivers/net/wireless/rtl818x/rtl8187_leds.c b/drivers/net/wireless/rtl818x/rtl8187_leds.c deleted file mode 100644 index 4637337d5ce6..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8187_leds.c +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Linux LED driver for RTL8187 - * - * Copyright 2009 Larry Finger - * - * Based on the LED handling in the r8187 driver, which is: - * Copyright (c) Realtek Semiconductor Corp. All rights reserved. - * - * Thanks to Realtek for their support! - * - * 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. - */ - -#ifdef CONFIG_RTL8187_LEDS - -#include -#include -#include - -#include "rtl8187.h" -#include "rtl8187_leds.h" - -static void led_turn_on(struct work_struct *work) -{ - /* As this routine does read/write operations on the hardware, it must - * be run from a work queue. - */ - u8 reg; - struct rtl8187_priv *priv = container_of(work, struct rtl8187_priv, - led_on.work); - struct rtl8187_led *led = &priv->led_tx; - - /* Don't change the LED, when the device is down. */ - if (!priv->vif || priv->vif->type == NL80211_IFTYPE_UNSPECIFIED) - return ; - - /* Skip if the LED is not registered. */ - if (!led->dev) - return; - mutex_lock(&priv->conf_mutex); - switch (led->ledpin) { - case LED_PIN_GPIO0: - rtl818x_iowrite8(priv, &priv->map->GPIO0, 0x01); - rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0x00); - break; - case LED_PIN_LED0: - reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~(1 << 4); - rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); - break; - case LED_PIN_LED1: - reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~(1 << 5); - rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); - break; - case LED_PIN_HW: - default: - break; - } - mutex_unlock(&priv->conf_mutex); -} - -static void led_turn_off(struct work_struct *work) -{ - /* As this routine does read/write operations on the hardware, it must - * be run from a work queue. - */ - u8 reg; - struct rtl8187_priv *priv = container_of(work, struct rtl8187_priv, - led_off.work); - struct rtl8187_led *led = &priv->led_tx; - - /* Don't change the LED, when the device is down. */ - if (!priv->vif || priv->vif->type == NL80211_IFTYPE_UNSPECIFIED) - return ; - - /* Skip if the LED is not registered. */ - if (!led->dev) - return; - mutex_lock(&priv->conf_mutex); - switch (led->ledpin) { - case LED_PIN_GPIO0: - rtl818x_iowrite8(priv, &priv->map->GPIO0, 0x01); - rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0x01); - break; - case LED_PIN_LED0: - reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) | (1 << 4); - rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); - break; - case LED_PIN_LED1: - reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) | (1 << 5); - rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg); - break; - case LED_PIN_HW: - default: - break; - } - mutex_unlock(&priv->conf_mutex); -} - -/* Callback from the LED subsystem. */ -static void rtl8187_led_brightness_set(struct led_classdev *led_dev, - enum led_brightness brightness) -{ - struct rtl8187_led *led = container_of(led_dev, struct rtl8187_led, - led_dev); - struct ieee80211_hw *hw = led->dev; - struct rtl8187_priv *priv; - static bool radio_on; - - if (!hw) - return; - priv = hw->priv; - if (led->is_radio) { - if (brightness == LED_FULL) { - ieee80211_queue_delayed_work(hw, &priv->led_on, 0); - radio_on = true; - } else if (radio_on) { - radio_on = false; - cancel_delayed_work_sync(&priv->led_on); - ieee80211_queue_delayed_work(hw, &priv->led_off, 0); - } - } else if (radio_on) { - if (brightness == LED_OFF) { - ieee80211_queue_delayed_work(hw, &priv->led_off, 0); - /* The LED is off for 1/20 sec - it just blinks. */ - ieee80211_queue_delayed_work(hw, &priv->led_on, - HZ / 20); - } else - ieee80211_queue_delayed_work(hw, &priv->led_on, 0); - } -} - -static int rtl8187_register_led(struct ieee80211_hw *dev, - struct rtl8187_led *led, const char *name, - const char *default_trigger, u8 ledpin, - bool is_radio) -{ - int err; - struct rtl8187_priv *priv = dev->priv; - - if (led->dev) - return -EEXIST; - if (!default_trigger) - return -EINVAL; - led->dev = dev; - led->ledpin = ledpin; - led->is_radio = is_radio; - strncpy(led->name, name, sizeof(led->name)); - - led->led_dev.name = led->name; - led->led_dev.default_trigger = default_trigger; - led->led_dev.brightness_set = rtl8187_led_brightness_set; - - err = led_classdev_register(&priv->udev->dev, &led->led_dev); - if (err) { - printk(KERN_INFO "LEDs: Failed to register %s\n", name); - led->dev = NULL; - return err; - } - return 0; -} - -static void rtl8187_unregister_led(struct rtl8187_led *led) -{ - struct ieee80211_hw *hw = led->dev; - struct rtl8187_priv *priv = hw->priv; - - led_classdev_unregister(&led->led_dev); - flush_delayed_work(&priv->led_off); - led->dev = NULL; -} - -void rtl8187_leds_init(struct ieee80211_hw *dev, u16 custid) -{ - struct rtl8187_priv *priv = dev->priv; - char name[RTL8187_LED_MAX_NAME_LEN + 1]; - u8 ledpin; - int err; - - /* According to the vendor driver, the LED operation depends on the - * customer ID encoded in the EEPROM - */ - printk(KERN_INFO "rtl8187: Customer ID is 0x%02X\n", custid); - switch (custid) { - case EEPROM_CID_RSVD0: - case EEPROM_CID_RSVD1: - case EEPROM_CID_SERCOMM_PS: - case EEPROM_CID_QMI: - case EEPROM_CID_DELL: - case EEPROM_CID_TOSHIBA: - ledpin = LED_PIN_GPIO0; - break; - case EEPROM_CID_ALPHA0: - ledpin = LED_PIN_LED0; - break; - case EEPROM_CID_HW: - ledpin = LED_PIN_HW; - break; - default: - ledpin = LED_PIN_GPIO0; - } - - INIT_DELAYED_WORK(&priv->led_on, led_turn_on); - INIT_DELAYED_WORK(&priv->led_off, led_turn_off); - - snprintf(name, sizeof(name), - "rtl8187-%s::radio", wiphy_name(dev->wiphy)); - err = rtl8187_register_led(dev, &priv->led_radio, name, - ieee80211_get_radio_led_name(dev), ledpin, true); - if (err) - return; - - snprintf(name, sizeof(name), - "rtl8187-%s::tx", wiphy_name(dev->wiphy)); - err = rtl8187_register_led(dev, &priv->led_tx, name, - ieee80211_get_tx_led_name(dev), ledpin, false); - if (err) - goto err_tx; - - snprintf(name, sizeof(name), - "rtl8187-%s::rx", wiphy_name(dev->wiphy)); - err = rtl8187_register_led(dev, &priv->led_rx, name, - ieee80211_get_rx_led_name(dev), ledpin, false); - if (!err) - return; - - /* registration of RX LED failed - unregister */ - rtl8187_unregister_led(&priv->led_tx); -err_tx: - rtl8187_unregister_led(&priv->led_radio); -} - -void rtl8187_leds_exit(struct ieee80211_hw *dev) -{ - struct rtl8187_priv *priv = dev->priv; - - rtl8187_unregister_led(&priv->led_radio); - rtl8187_unregister_led(&priv->led_rx); - rtl8187_unregister_led(&priv->led_tx); - cancel_delayed_work_sync(&priv->led_off); - cancel_delayed_work_sync(&priv->led_on); -} -#endif /* def CONFIG_RTL8187_LEDS */ - diff --git a/drivers/net/wireless/rtl818x/rtl8187_leds.h b/drivers/net/wireless/rtl818x/rtl8187_leds.h deleted file mode 100644 index d743c96d4a20..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8187_leds.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Definitions for RTL8187 leds - * - * Copyright 2009 Larry Finger - * - * Based on the LED handling in the r8187 driver, which is: - * Copyright (c) Realtek Semiconductor Corp. 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 version 2 as - * published by the Free Software Foundation. - */ - -#ifndef RTL8187_LED_H -#define RTL8187_LED_H - -#ifdef CONFIG_RTL8187_LEDS - -#define RTL8187_LED_MAX_NAME_LEN 21 - -#include -#include - -enum { - LED_PIN_LED0, - LED_PIN_LED1, - LED_PIN_GPIO0, - LED_PIN_HW -}; - -enum { - EEPROM_CID_RSVD0 = 0x00, - EEPROM_CID_RSVD1 = 0xFF, - EEPROM_CID_ALPHA0 = 0x01, - EEPROM_CID_SERCOMM_PS = 0x02, - EEPROM_CID_HW = 0x03, - EEPROM_CID_TOSHIBA = 0x04, - EEPROM_CID_QMI = 0x07, - EEPROM_CID_DELL = 0x08 -}; - -struct rtl8187_led { - struct ieee80211_hw *dev; - /* The LED class device */ - struct led_classdev led_dev; - /* The pin/method used to control the led */ - u8 ledpin; - /* The unique name string for this LED device. */ - char name[RTL8187_LED_MAX_NAME_LEN + 1]; - /* If the LED is radio or tx/rx */ - bool is_radio; -}; - -void rtl8187_leds_init(struct ieee80211_hw *dev, u16 code); -void rtl8187_leds_exit(struct ieee80211_hw *dev); - -#endif /* def CONFIG_RTL8187_LEDS */ - -#endif /* RTL8187_LED_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8187_rfkill.c b/drivers/net/wireless/rtl818x/rtl8187_rfkill.c deleted file mode 100644 index 03555e1e0cab..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8187_rfkill.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Linux RFKILL support for RTL8187 - * - * Copyright (c) 2009 Herton Ronaldo Krzesinski - * - * Based on the RFKILL handling in the r8187 driver, which is: - * Copyright (c) Realtek Semiconductor Corp. All rights reserved. - * - * Thanks to Realtek for their support! - * - * 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 -#include -#include - -#include "rtl8187.h" -#include "rtl8187_rfkill.h" - -static bool rtl8187_is_radio_enabled(struct rtl8187_priv *priv) -{ - u8 gpio; - - gpio = rtl818x_ioread8(priv, &priv->map->GPIO0); - rtl818x_iowrite8(priv, &priv->map->GPIO0, gpio & ~priv->rfkill_mask); - gpio = rtl818x_ioread8(priv, &priv->map->GPIO1); - - return gpio & priv->rfkill_mask; -} - -void rtl8187_rfkill_init(struct ieee80211_hw *hw) -{ - struct rtl8187_priv *priv = hw->priv; - - priv->rfkill_off = rtl8187_is_radio_enabled(priv); - printk(KERN_INFO "rtl8187: wireless switch is %s\n", - priv->rfkill_off ? "on" : "off"); - wiphy_rfkill_set_hw_state(hw->wiphy, !priv->rfkill_off); - wiphy_rfkill_start_polling(hw->wiphy); -} - -void rtl8187_rfkill_poll(struct ieee80211_hw *hw) -{ - bool enabled; - struct rtl8187_priv *priv = hw->priv; - - mutex_lock(&priv->conf_mutex); - enabled = rtl8187_is_radio_enabled(priv); - if (unlikely(enabled != priv->rfkill_off)) { - priv->rfkill_off = enabled; - printk(KERN_INFO "rtl8187: wireless radio switch turned %s\n", - enabled ? "on" : "off"); - wiphy_rfkill_set_hw_state(hw->wiphy, !enabled); - } - mutex_unlock(&priv->conf_mutex); -} - -void rtl8187_rfkill_exit(struct ieee80211_hw *hw) -{ - wiphy_rfkill_stop_polling(hw->wiphy); -} diff --git a/drivers/net/wireless/rtl818x/rtl8187_rfkill.h b/drivers/net/wireless/rtl818x/rtl8187_rfkill.h deleted file mode 100644 index e12575e96d11..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8187_rfkill.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef RTL8187_RFKILL_H -#define RTL8187_RFKILL_H - -void rtl8187_rfkill_init(struct ieee80211_hw *hw); -void rtl8187_rfkill_poll(struct ieee80211_hw *hw); -void rtl8187_rfkill_exit(struct ieee80211_hw *hw); - -#endif /* RTL8187_RFKILL_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8187_rtl8225.c b/drivers/net/wireless/rtl818x/rtl8187_rtl8225.c deleted file mode 100644 index 5c6666f09ac1..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8187_rtl8225.c +++ /dev/null @@ -1,961 +0,0 @@ -/* - * Radio tuning for RTL8225 on RTL8187 - * - * Copyright 2007 Michael Wu - * Copyright 2007 Andrea Merello - * - * Based on the r8187 driver, which is: - * Copyright 2005 Andrea Merello , et al. - * - * Magic delays, register offsets, and phy value tables below are - * taken from the original r8187 driver sources. Thanks to Realtek - * for their support! - * - * 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 -#include -#include - -#include "rtl8187.h" -#include "rtl8187_rtl8225.h" - -static void rtl8225_write_bitbang(struct ieee80211_hw *dev, u8 addr, u16 data) -{ - struct rtl8187_priv *priv = dev->priv; - u16 reg80, reg84, reg82; - u32 bangdata; - int i; - - bangdata = (data << 4) | (addr & 0xf); - - reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput) & 0xfff3; - reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable); - - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x7); - - reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x7); - udelay(10); - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); - udelay(2); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80); - udelay(10); - - for (i = 15; i >= 0; i--) { - u16 reg = reg80 | (bangdata & (1 << i)) >> i; - - if (i & 1) - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1)); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1)); - - if (!(i & 1)) - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); - } - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); - udelay(10); - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84); -} - -static void rtl8225_write_8051(struct ieee80211_hw *dev, u8 addr, __le16 data) -{ - struct rtl8187_priv *priv = dev->priv; - u16 reg80, reg82, reg84; - - reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput); - reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable); - reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect); - - reg80 &= ~(0x3 << 2); - reg84 &= ~0xF; - - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x0007); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x0007); - udelay(10); - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); - udelay(2); - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80); - udelay(10); - - mutex_lock(&priv->io_mutex); - - priv->io_dmabuf->bits16 = data; - usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0), - RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE, - addr, 0x8225, &priv->io_dmabuf->bits16, sizeof(data), - HZ / 2); - - mutex_unlock(&priv->io_mutex); - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); - udelay(10); - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84); -} - -static void rtl8225_write(struct ieee80211_hw *dev, u8 addr, u16 data) -{ - struct rtl8187_priv *priv = dev->priv; - - if (priv->asic_rev) - rtl8225_write_8051(dev, addr, cpu_to_le16(data)); - else - rtl8225_write_bitbang(dev, addr, data); -} - -static u16 rtl8225_read(struct ieee80211_hw *dev, u8 addr) -{ - struct rtl8187_priv *priv = dev->priv; - u16 reg80, reg82, reg84, out; - int i; - - reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput); - reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable); - reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect); - - reg80 &= ~0xF; - - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x000F); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x000F); - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); - udelay(4); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80); - udelay(5); - - for (i = 4; i >= 0; i--) { - u16 reg = reg80 | ((addr >> i) & 1); - - if (!(i & 1)) { - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); - udelay(1); - } - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg | (1 << 1)); - udelay(2); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg | (1 << 1)); - udelay(2); - - if (i & 1) { - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); - udelay(1); - } - } - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3) | (1 << 1)); - udelay(2); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3)); - udelay(2); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3)); - udelay(2); - - out = 0; - for (i = 11; i >= 0; i--) { - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3)); - udelay(1); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3) | (1 << 1)); - udelay(2); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3) | (1 << 1)); - udelay(2); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3) | (1 << 1)); - udelay(2); - - if (rtl818x_ioread16(priv, &priv->map->RFPinsInput) & (1 << 1)) - out |= 1 << i; - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3)); - udelay(2); - } - - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, - reg80 | (1 << 3) | (1 << 2)); - udelay(2); - - rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82); - rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84); - rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x03A0); - - return out; -} - -static const u16 rtl8225bcd_rxgain[] = { - 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409, - 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541, - 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583, - 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644, - 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688, - 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745, - 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789, - 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793, - 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d, - 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9, - 0x07aa, 0x07ab, 0x07ac, 0x07ad, 0x07b0, 0x07b1, 0x07b2, 0x07b3, - 0x07b4, 0x07b5, 0x07b8, 0x07b9, 0x07ba, 0x07bb, 0x07bb -}; - -static const u8 rtl8225_agc[] = { - 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, - 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, 0x97, 0x96, - 0x95, 0x94, 0x93, 0x92, 0x91, 0x90, 0x8f, 0x8e, - 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86, - 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, 0x3f, 0x3e, - 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, - 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2f, 0x2e, - 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, - 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e, - 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, - 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, - 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, - 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 -}; - -static const u8 rtl8225_gain[] = { - 0x23, 0x88, 0x7c, 0xa5, /* -82dBm */ - 0x23, 0x88, 0x7c, 0xb5, /* -82dBm */ - 0x23, 0x88, 0x7c, 0xc5, /* -82dBm */ - 0x33, 0x80, 0x79, 0xc5, /* -78dBm */ - 0x43, 0x78, 0x76, 0xc5, /* -74dBm */ - 0x53, 0x60, 0x73, 0xc5, /* -70dBm */ - 0x63, 0x58, 0x70, 0xc5, /* -66dBm */ -}; - -static const u8 rtl8225_threshold[] = { - 0x8d, 0x8d, 0x8d, 0x8d, 0x9d, 0xad, 0xbd -}; - -static const u8 rtl8225_tx_gain_cck_ofdm[] = { - 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e -}; - -static const u8 rtl8225_tx_power_cck[] = { - 0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02, - 0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02, - 0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02, - 0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02, - 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03, - 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03 -}; - -static const u8 rtl8225_tx_power_cck_ch14[] = { - 0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00, - 0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00, - 0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00, - 0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00, - 0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00 -}; - -static const u8 rtl8225_tx_power_ofdm[] = { - 0x80, 0x90, 0xa2, 0xb5, 0xcb, 0xe4 -}; - -static const u32 rtl8225_chan[] = { - 0x085c, 0x08dc, 0x095c, 0x09dc, 0x0a5c, 0x0adc, 0x0b5c, - 0x0bdc, 0x0c5c, 0x0cdc, 0x0d5c, 0x0ddc, 0x0e5c, 0x0f72 -}; - -static void rtl8225_rf_set_tx_power(struct ieee80211_hw *dev, int channel) -{ - struct rtl8187_priv *priv = dev->priv; - u8 cck_power, ofdm_power; - const u8 *tmp; - u32 reg; - int i; - - cck_power = priv->channels[channel - 1].hw_value & 0xF; - ofdm_power = priv->channels[channel - 1].hw_value >> 4; - - cck_power = min(cck_power, (u8)11); - if (ofdm_power > (u8)15) - ofdm_power = 25; - else - ofdm_power += 10; - - rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, - rtl8225_tx_gain_cck_ofdm[cck_power / 6] >> 1); - - if (channel == 14) - tmp = &rtl8225_tx_power_cck_ch14[(cck_power % 6) * 8]; - else - tmp = &rtl8225_tx_power_cck[(cck_power % 6) * 8]; - - for (i = 0; i < 8; i++) - rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++); - - msleep(1); // FIXME: optional? - - /* anaparam2 on */ - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); - reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); - rtl818x_iowrite8(priv, &priv->map->CONFIG3, - reg | RTL818X_CONFIG3_ANAPARAM_WRITE); - rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, - RTL8187_RTL8225_ANAPARAM2_ON); - rtl818x_iowrite8(priv, &priv->map->CONFIG3, - reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); - - rtl8225_write_phy_ofdm(dev, 2, 0x42); - rtl8225_write_phy_ofdm(dev, 6, 0x00); - rtl8225_write_phy_ofdm(dev, 8, 0x00); - - rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, - rtl8225_tx_gain_cck_ofdm[ofdm_power / 6] >> 1); - - tmp = &rtl8225_tx_power_ofdm[ofdm_power % 6]; - - rtl8225_write_phy_ofdm(dev, 5, *tmp); - rtl8225_write_phy_ofdm(dev, 7, *tmp); - - msleep(1); -} - -static void rtl8225_rf_init(struct ieee80211_hw *dev) -{ - struct rtl8187_priv *priv = dev->priv; - int i; - - rtl8225_write(dev, 0x0, 0x067); - rtl8225_write(dev, 0x1, 0xFE0); - rtl8225_write(dev, 0x2, 0x44D); - rtl8225_write(dev, 0x3, 0x441); - rtl8225_write(dev, 0x4, 0x486); - rtl8225_write(dev, 0x5, 0xBC0); - rtl8225_write(dev, 0x6, 0xAE6); - rtl8225_write(dev, 0x7, 0x82A); - rtl8225_write(dev, 0x8, 0x01F); - rtl8225_write(dev, 0x9, 0x334); - rtl8225_write(dev, 0xA, 0xFD4); - rtl8225_write(dev, 0xB, 0x391); - rtl8225_write(dev, 0xC, 0x050); - rtl8225_write(dev, 0xD, 0x6DB); - rtl8225_write(dev, 0xE, 0x029); - rtl8225_write(dev, 0xF, 0x914); msleep(100); - - rtl8225_write(dev, 0x2, 0xC4D); msleep(200); - rtl8225_write(dev, 0x2, 0x44D); msleep(200); - - if (!(rtl8225_read(dev, 6) & (1 << 7))) { - rtl8225_write(dev, 0x02, 0x0c4d); - msleep(200); - rtl8225_write(dev, 0x02, 0x044d); - msleep(100); - if (!(rtl8225_read(dev, 6) & (1 << 7))) - wiphy_warn(dev->wiphy, "RF Calibration Failed! %x\n", - rtl8225_read(dev, 6)); - } - - rtl8225_write(dev, 0x0, 0x127); - - for (i = 0; i < ARRAY_SIZE(rtl8225bcd_rxgain); i++) { - rtl8225_write(dev, 0x1, i + 1); - rtl8225_write(dev, 0x2, rtl8225bcd_rxgain[i]); - } - - rtl8225_write(dev, 0x0, 0x027); - rtl8225_write(dev, 0x0, 0x22F); - - for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) { - rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]); - rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i); - } - - msleep(1); - - rtl8225_write_phy_ofdm(dev, 0x00, 0x01); - rtl8225_write_phy_ofdm(dev, 0x01, 0x02); - rtl8225_write_phy_ofdm(dev, 0x02, 0x42); - rtl8225_write_phy_ofdm(dev, 0x03, 0x00); - rtl8225_write_phy_ofdm(dev, 0x04, 0x00); - rtl8225_write_phy_ofdm(dev, 0x05, 0x00); - rtl8225_write_phy_ofdm(dev, 0x06, 0x40); - rtl8225_write_phy_ofdm(dev, 0x07, 0x00); - rtl8225_write_phy_ofdm(dev, 0x08, 0x40); - rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); - rtl8225_write_phy_ofdm(dev, 0x0a, 0x09); - rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); - rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); - rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); - rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); - rtl8225_write_phy_ofdm(dev, 0x10, 0x84); - rtl8225_write_phy_ofdm(dev, 0x11, 0x06); - rtl8225_write_phy_ofdm(dev, 0x12, 0x20); - rtl8225_write_phy_ofdm(dev, 0x13, 0x20); - rtl8225_write_phy_ofdm(dev, 0x14, 0x00); - rtl8225_write_phy_ofdm(dev, 0x15, 0x40); - rtl8225_write_phy_ofdm(dev, 0x16, 0x00); - rtl8225_write_phy_ofdm(dev, 0x17, 0x40); - rtl8225_write_phy_ofdm(dev, 0x18, 0xef); - rtl8225_write_phy_ofdm(dev, 0x19, 0x19); - rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); - rtl8225_write_phy_ofdm(dev, 0x1b, 0x76); - rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); - rtl8225_write_phy_ofdm(dev, 0x1e, 0x95); - rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); - rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); - rtl8225_write_phy_ofdm(dev, 0x21, 0x27); - rtl8225_write_phy_ofdm(dev, 0x22, 0x16); - rtl8225_write_phy_ofdm(dev, 0x24, 0x46); - rtl8225_write_phy_ofdm(dev, 0x25, 0x20); - rtl8225_write_phy_ofdm(dev, 0x26, 0x90); - rtl8225_write_phy_ofdm(dev, 0x27, 0x88); - - rtl8225_write_phy_ofdm(dev, 0x0d, rtl8225_gain[2 * 4]); - rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225_gain[2 * 4 + 2]); - rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225_gain[2 * 4 + 3]); - rtl8225_write_phy_ofdm(dev, 0x23, rtl8225_gain[2 * 4 + 1]); - - rtl8225_write_phy_cck(dev, 0x00, 0x98); - rtl8225_write_phy_cck(dev, 0x03, 0x20); - rtl8225_write_phy_cck(dev, 0x04, 0x7e); - rtl8225_write_phy_cck(dev, 0x05, 0x12); - rtl8225_write_phy_cck(dev, 0x06, 0xfc); - rtl8225_write_phy_cck(dev, 0x07, 0x78); - rtl8225_write_phy_cck(dev, 0x08, 0x2e); - rtl8225_write_phy_cck(dev, 0x10, 0x9b); - rtl8225_write_phy_cck(dev, 0x11, 0x88); - rtl8225_write_phy_cck(dev, 0x12, 0x47); - rtl8225_write_phy_cck(dev, 0x13, 0xd0); - rtl8225_write_phy_cck(dev, 0x19, 0x00); - rtl8225_write_phy_cck(dev, 0x1a, 0xa0); - rtl8225_write_phy_cck(dev, 0x1b, 0x08); - rtl8225_write_phy_cck(dev, 0x40, 0x86); - rtl8225_write_phy_cck(dev, 0x41, 0x8d); - rtl8225_write_phy_cck(dev, 0x42, 0x15); - rtl8225_write_phy_cck(dev, 0x43, 0x18); - rtl8225_write_phy_cck(dev, 0x44, 0x1f); - rtl8225_write_phy_cck(dev, 0x45, 0x1e); - rtl8225_write_phy_cck(dev, 0x46, 0x1a); - rtl8225_write_phy_cck(dev, 0x47, 0x15); - rtl8225_write_phy_cck(dev, 0x48, 0x10); - rtl8225_write_phy_cck(dev, 0x49, 0x0a); - rtl8225_write_phy_cck(dev, 0x4a, 0x05); - rtl8225_write_phy_cck(dev, 0x4b, 0x02); - rtl8225_write_phy_cck(dev, 0x4c, 0x05); - - rtl818x_iowrite8(priv, &priv->map->TESTR, 0x0D); - - rtl8225_rf_set_tx_power(dev, 1); - - /* RX antenna default to A */ - rtl8225_write_phy_cck(dev, 0x10, 0x9b); /* B: 0xDB */ - rtl8225_write_phy_ofdm(dev, 0x26, 0x90); /* B: 0x10 */ - - rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); /* B: 0x00 */ - msleep(1); - rtl818x_iowrite32(priv, (__le32 *)0xFF94, 0x3dc00002); - - /* set sensitivity */ - rtl8225_write(dev, 0x0c, 0x50); - rtl8225_write_phy_ofdm(dev, 0x0d, rtl8225_gain[2 * 4]); - rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225_gain[2 * 4 + 2]); - rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225_gain[2 * 4 + 3]); - rtl8225_write_phy_ofdm(dev, 0x23, rtl8225_gain[2 * 4 + 1]); - rtl8225_write_phy_cck(dev, 0x41, rtl8225_threshold[2]); -} - -static const u8 rtl8225z2_agc[] = { - 0x5e, 0x5e, 0x5e, 0x5e, 0x5d, 0x5b, 0x59, 0x57, 0x55, 0x53, 0x51, 0x4f, - 0x4d, 0x4b, 0x49, 0x47, 0x45, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x39, 0x37, - 0x35, 0x33, 0x31, 0x2f, 0x2d, 0x2b, 0x29, 0x27, 0x25, 0x23, 0x21, 0x1f, - 0x1d, 0x1b, 0x19, 0x17, 0x15, 0x13, 0x11, 0x0f, 0x0d, 0x0b, 0x09, 0x07, - 0x05, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, - 0x19, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, - 0x28, 0x29, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c, 0x2c, 0x2d, - 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x30, 0x30, - 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, - 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31 -}; -static const u8 rtl8225z2_ofdm[] = { - 0x10, 0x0d, 0x01, 0x00, 0x14, 0xfb, 0xfb, 0x60, - 0x00, 0x60, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, - 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, 0xa8, 0x26, - 0x32, 0x33, 0x07, 0xa5, 0x6f, 0x55, 0xc8, 0xb3, - 0x0a, 0xe1, 0x2C, 0x8a, 0x86, 0x83, 0x34, 0x0f, - 0x4f, 0x24, 0x6f, 0xc2, 0x6b, 0x40, 0x80, 0x00, - 0xc0, 0xc1, 0x58, 0xf1, 0x00, 0xe4, 0x90, 0x3e, - 0x6d, 0x3c, 0xfb, 0x07 -}; - -static const u8 rtl8225z2_tx_power_cck_ch14[] = { - 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00, - 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00 -}; - -static const u8 rtl8225z2_tx_power_cck[] = { - 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04, - 0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03, - 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03, - 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03 -}; - -static const u8 rtl8225z2_tx_power_ofdm[] = { - 0x42, 0x00, 0x40, 0x00, 0x40 -}; - -static const u8 rtl8225z2_tx_gain_cck_ofdm[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, - 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, - 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, - 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, - 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23 -}; - -static void rtl8225z2_rf_set_tx_power(struct ieee80211_hw *dev, int channel) -{ - struct rtl8187_priv *priv = dev->priv; - u8 cck_power, ofdm_power; - const u8 *tmp; - u32 reg; - int i; - - cck_power = priv->channels[channel - 1].hw_value & 0xF; - ofdm_power = priv->channels[channel - 1].hw_value >> 4; - - cck_power = min(cck_power, (u8)15); - cck_power += priv->txpwr_base & 0xF; - cck_power = min(cck_power, (u8)35); - - if (ofdm_power > (u8)15) - ofdm_power = 25; - else - ofdm_power += 10; - ofdm_power += priv->txpwr_base >> 4; - ofdm_power = min(ofdm_power, (u8)35); - - if (channel == 14) - tmp = rtl8225z2_tx_power_cck_ch14; - else - tmp = rtl8225z2_tx_power_cck; - - for (i = 0; i < 8; i++) - rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++); - - rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, - rtl8225z2_tx_gain_cck_ofdm[cck_power]); - msleep(1); - - /* anaparam2 on */ - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); - reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); - rtl818x_iowrite8(priv, &priv->map->CONFIG3, - reg | RTL818X_CONFIG3_ANAPARAM_WRITE); - rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, - RTL8187_RTL8225_ANAPARAM2_ON); - rtl818x_iowrite8(priv, &priv->map->CONFIG3, - reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); - rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); - - rtl8225_write_phy_ofdm(dev, 2, 0x42); - rtl8225_write_phy_ofdm(dev, 5, 0x00); - rtl8225_write_phy_ofdm(dev, 6, 0x40); - rtl8225_write_phy_ofdm(dev, 7, 0x00); - rtl8225_write_phy_ofdm(dev, 8, 0x40); - - rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, - rtl8225z2_tx_gain_cck_ofdm[ofdm_power]); - msleep(1); -} - -static void rtl8225z2_b_rf_set_tx_power(struct ieee80211_hw *dev, int channel) -{ - struct rtl8187_priv *priv = dev->priv; - u8 cck_power, ofdm_power; - const u8 *tmp; - int i; - - cck_power = priv->channels[channel - 1].hw_value & 0xF; - ofdm_power = priv->channels[channel - 1].hw_value >> 4; - - if (cck_power > 15) - cck_power = (priv->hw_rev == RTL8187BvB) ? 15 : 22; - else - cck_power += (priv->hw_rev == RTL8187BvB) ? 0 : 7; - cck_power += priv->txpwr_base & 0xF; - cck_power = min(cck_power, (u8)35); - - if (ofdm_power > 15) - ofdm_power = (priv->hw_rev == RTL8187BvB) ? 17 : 25; - else - ofdm_power += (priv->hw_rev == RTL8187BvB) ? 2 : 10; - ofdm_power += (priv->txpwr_base >> 4) & 0xF; - ofdm_power = min(ofdm_power, (u8)35); - - if (channel == 14) - tmp = rtl8225z2_tx_power_cck_ch14; - else - tmp = rtl8225z2_tx_power_cck; - - if (priv->hw_rev == RTL8187BvB) { - if (cck_power <= 6) - ; /* do nothing */ - else if (cck_power <= 11) - tmp += 8; - else - tmp += 16; - } else { - if (cck_power <= 5) - ; /* do nothing */ - else if (cck_power <= 11) - tmp += 8; - else if (cck_power <= 17) - tmp += 16; - else - tmp += 24; - } - - for (i = 0; i < 8; i++) - rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++); - - rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, - rtl8225z2_tx_gain_cck_ofdm[cck_power] << 1); - msleep(1); - - rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, - rtl8225z2_tx_gain_cck_ofdm[ofdm_power] << 1); - if (priv->hw_rev == RTL8187BvB) { - if (ofdm_power <= 11) { - rtl8225_write_phy_ofdm(dev, 0x87, 0x60); - rtl8225_write_phy_ofdm(dev, 0x89, 0x60); - } else { - rtl8225_write_phy_ofdm(dev, 0x87, 0x5c); - rtl8225_write_phy_ofdm(dev, 0x89, 0x5c); - } - } else { - if (ofdm_power <= 11) { - rtl8225_write_phy_ofdm(dev, 0x87, 0x5c); - rtl8225_write_phy_ofdm(dev, 0x89, 0x5c); - } else if (ofdm_power <= 17) { - rtl8225_write_phy_ofdm(dev, 0x87, 0x54); - rtl8225_write_phy_ofdm(dev, 0x89, 0x54); - } else { - rtl8225_write_phy_ofdm(dev, 0x87, 0x50); - rtl8225_write_phy_ofdm(dev, 0x89, 0x50); - } - } - msleep(1); -} - -static const u16 rtl8225z2_rxgain[] = { - 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409, - 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541, - 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583, - 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644, - 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688, - 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745, - 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789, - 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793, - 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d, - 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9, - 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3, - 0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb -}; - -static const u8 rtl8225z2_gain_bg[] = { - 0x23, 0x15, 0xa5, /* -82-1dBm */ - 0x23, 0x15, 0xb5, /* -82-2dBm */ - 0x23, 0x15, 0xc5, /* -82-3dBm */ - 0x33, 0x15, 0xc5, /* -78dBm */ - 0x43, 0x15, 0xc5, /* -74dBm */ - 0x53, 0x15, 0xc5, /* -70dBm */ - 0x63, 0x15, 0xc5 /* -66dBm */ -}; - -static void rtl8225z2_rf_init(struct ieee80211_hw *dev) -{ - struct rtl8187_priv *priv = dev->priv; - int i; - - rtl8225_write(dev, 0x0, 0x2BF); - rtl8225_write(dev, 0x1, 0xEE0); - rtl8225_write(dev, 0x2, 0x44D); - rtl8225_write(dev, 0x3, 0x441); - rtl8225_write(dev, 0x4, 0x8C3); - rtl8225_write(dev, 0x5, 0xC72); - rtl8225_write(dev, 0x6, 0x0E6); - rtl8225_write(dev, 0x7, 0x82A); - rtl8225_write(dev, 0x8, 0x03F); - rtl8225_write(dev, 0x9, 0x335); - rtl8225_write(dev, 0xa, 0x9D4); - rtl8225_write(dev, 0xb, 0x7BB); - rtl8225_write(dev, 0xc, 0x850); - rtl8225_write(dev, 0xd, 0xCDF); - rtl8225_write(dev, 0xe, 0x02B); - rtl8225_write(dev, 0xf, 0x114); - msleep(100); - - rtl8225_write(dev, 0x0, 0x1B7); - - for (i = 0; i < ARRAY_SIZE(rtl8225z2_rxgain); i++) { - rtl8225_write(dev, 0x1, i + 1); - rtl8225_write(dev, 0x2, rtl8225z2_rxgain[i]); - } - - rtl8225_write(dev, 0x3, 0x080); - rtl8225_write(dev, 0x5, 0x004); - rtl8225_write(dev, 0x0, 0x0B7); - rtl8225_write(dev, 0x2, 0xc4D); - - msleep(200); - rtl8225_write(dev, 0x2, 0x44D); - msleep(100); - - if (!(rtl8225_read(dev, 6) & (1 << 7))) { - rtl8225_write(dev, 0x02, 0x0C4D); - msleep(200); - rtl8225_write(dev, 0x02, 0x044D); - msleep(100); - if (!(rtl8225_read(dev, 6) & (1 << 7))) - wiphy_warn(dev->wiphy, "RF Calibration Failed! %x\n", - rtl8225_read(dev, 6)); - } - - msleep(200); - - rtl8225_write(dev, 0x0, 0x2BF); - - for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) { - rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]); - rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i); - } - - msleep(1); - - rtl8225_write_phy_ofdm(dev, 0x00, 0x01); - rtl8225_write_phy_ofdm(dev, 0x01, 0x02); - rtl8225_write_phy_ofdm(dev, 0x02, 0x42); - rtl8225_write_phy_ofdm(dev, 0x03, 0x00); - rtl8225_write_phy_ofdm(dev, 0x04, 0x00); - rtl8225_write_phy_ofdm(dev, 0x05, 0x00); - rtl8225_write_phy_ofdm(dev, 0x06, 0x40); - rtl8225_write_phy_ofdm(dev, 0x07, 0x00); - rtl8225_write_phy_ofdm(dev, 0x08, 0x40); - rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); - rtl8225_write_phy_ofdm(dev, 0x0a, 0x08); - rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); - rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); - rtl8225_write_phy_ofdm(dev, 0x0d, 0x43); - rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); - rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); - rtl8225_write_phy_ofdm(dev, 0x10, 0x84); - rtl8225_write_phy_ofdm(dev, 0x11, 0x07); - rtl8225_write_phy_ofdm(dev, 0x12, 0x20); - rtl8225_write_phy_ofdm(dev, 0x13, 0x20); - rtl8225_write_phy_ofdm(dev, 0x14, 0x00); - rtl8225_write_phy_ofdm(dev, 0x15, 0x40); - rtl8225_write_phy_ofdm(dev, 0x16, 0x00); - rtl8225_write_phy_ofdm(dev, 0x17, 0x40); - rtl8225_write_phy_ofdm(dev, 0x18, 0xef); - rtl8225_write_phy_ofdm(dev, 0x19, 0x19); - rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); - rtl8225_write_phy_ofdm(dev, 0x1b, 0x15); - rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); - rtl8225_write_phy_ofdm(dev, 0x1d, 0xc5); - rtl8225_write_phy_ofdm(dev, 0x1e, 0x95); - rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); - rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); - rtl8225_write_phy_ofdm(dev, 0x21, 0x17); - rtl8225_write_phy_ofdm(dev, 0x22, 0x16); - rtl8225_write_phy_ofdm(dev, 0x23, 0x80); - rtl8225_write_phy_ofdm(dev, 0x24, 0x46); - rtl8225_write_phy_ofdm(dev, 0x25, 0x00); - rtl8225_write_phy_ofdm(dev, 0x26, 0x90); - rtl8225_write_phy_ofdm(dev, 0x27, 0x88); - - rtl8225_write_phy_ofdm(dev, 0x0b, rtl8225z2_gain_bg[4 * 3]); - rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225z2_gain_bg[4 * 3 + 1]); - rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225z2_gain_bg[4 * 3 + 2]); - rtl8225_write_phy_ofdm(dev, 0x21, 0x37); - - rtl8225_write_phy_cck(dev, 0x00, 0x98); - rtl8225_write_phy_cck(dev, 0x03, 0x20); - rtl8225_write_phy_cck(dev, 0x04, 0x7e); - rtl8225_write_phy_cck(dev, 0x05, 0x12); - rtl8225_write_phy_cck(dev, 0x06, 0xfc); - rtl8225_write_phy_cck(dev, 0x07, 0x78); - rtl8225_write_phy_cck(dev, 0x08, 0x2e); - rtl8225_write_phy_cck(dev, 0x10, 0x9b); - rtl8225_write_phy_cck(dev, 0x11, 0x88); - rtl8225_write_phy_cck(dev, 0x12, 0x47); - rtl8225_write_phy_cck(dev, 0x13, 0xd0); - rtl8225_write_phy_cck(dev, 0x19, 0x00); - rtl8225_write_phy_cck(dev, 0x1a, 0xa0); - rtl8225_write_phy_cck(dev, 0x1b, 0x08); - rtl8225_write_phy_cck(dev, 0x40, 0x86); - rtl8225_write_phy_cck(dev, 0x41, 0x8d); - rtl8225_write_phy_cck(dev, 0x42, 0x15); - rtl8225_write_phy_cck(dev, 0x43, 0x18); - rtl8225_write_phy_cck(dev, 0x44, 0x36); - rtl8225_write_phy_cck(dev, 0x45, 0x35); - rtl8225_write_phy_cck(dev, 0x46, 0x2e); - rtl8225_write_phy_cck(dev, 0x47, 0x25); - rtl8225_write_phy_cck(dev, 0x48, 0x1c); - rtl8225_write_phy_cck(dev, 0x49, 0x12); - rtl8225_write_phy_cck(dev, 0x4a, 0x09); - rtl8225_write_phy_cck(dev, 0x4b, 0x04); - rtl8225_write_phy_cck(dev, 0x4c, 0x05); - - rtl818x_iowrite8(priv, (u8 *)0xFF5B, 0x0D); msleep(1); - - rtl8225z2_rf_set_tx_power(dev, 1); - - /* RX antenna default to A */ - rtl8225_write_phy_cck(dev, 0x10, 0x9b); /* B: 0xDB */ - rtl8225_write_phy_ofdm(dev, 0x26, 0x90); /* B: 0x10 */ - - rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); /* B: 0x00 */ - msleep(1); - rtl818x_iowrite32(priv, (__le32 *)0xFF94, 0x3dc00002); -} - -static void rtl8225z2_b_rf_init(struct ieee80211_hw *dev) -{ - struct rtl8187_priv *priv = dev->priv; - int i; - - rtl8225_write(dev, 0x0, 0x0B7); - rtl8225_write(dev, 0x1, 0xEE0); - rtl8225_write(dev, 0x2, 0x44D); - rtl8225_write(dev, 0x3, 0x441); - rtl8225_write(dev, 0x4, 0x8C3); - rtl8225_write(dev, 0x5, 0xC72); - rtl8225_write(dev, 0x6, 0x0E6); - rtl8225_write(dev, 0x7, 0x82A); - rtl8225_write(dev, 0x8, 0x03F); - rtl8225_write(dev, 0x9, 0x335); - rtl8225_write(dev, 0xa, 0x9D4); - rtl8225_write(dev, 0xb, 0x7BB); - rtl8225_write(dev, 0xc, 0x850); - rtl8225_write(dev, 0xd, 0xCDF); - rtl8225_write(dev, 0xe, 0x02B); - rtl8225_write(dev, 0xf, 0x114); - - rtl8225_write(dev, 0x0, 0x1B7); - - for (i = 0; i < ARRAY_SIZE(rtl8225z2_rxgain); i++) { - rtl8225_write(dev, 0x1, i + 1); - rtl8225_write(dev, 0x2, rtl8225z2_rxgain[i]); - } - - rtl8225_write(dev, 0x3, 0x080); - rtl8225_write(dev, 0x5, 0x004); - rtl8225_write(dev, 0x0, 0x0B7); - - rtl8225_write(dev, 0x2, 0xC4D); - - rtl8225_write(dev, 0x2, 0x44D); - rtl8225_write(dev, 0x0, 0x2BF); - - rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, 0x03); - rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, 0x07); - rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); - - rtl8225_write_phy_ofdm(dev, 0x80, 0x12); - for (i = 0; i < ARRAY_SIZE(rtl8225z2_agc); i++) { - rtl8225_write_phy_ofdm(dev, 0xF, rtl8225z2_agc[i]); - rtl8225_write_phy_ofdm(dev, 0xE, 0x80 + i); - rtl8225_write_phy_ofdm(dev, 0xE, 0); - } - rtl8225_write_phy_ofdm(dev, 0x80, 0x10); - - for (i = 0; i < ARRAY_SIZE(rtl8225z2_ofdm); i++) - rtl8225_write_phy_ofdm(dev, i, rtl8225z2_ofdm[i]); - - rtl8225_write_phy_ofdm(dev, 0x97, 0x46); - rtl8225_write_phy_ofdm(dev, 0xa4, 0xb6); - rtl8225_write_phy_ofdm(dev, 0x85, 0xfc); - rtl8225_write_phy_cck(dev, 0xc1, 0x88); -} - -static void rtl8225_rf_stop(struct ieee80211_hw *dev) -{ - rtl8225_write(dev, 0x4, 0x1f); -} - -static void rtl8225_rf_set_channel(struct ieee80211_hw *dev, - struct ieee80211_conf *conf) -{ - struct rtl8187_priv *priv = dev->priv; - int chan = ieee80211_frequency_to_channel(conf->channel->center_freq); - - if (priv->rf->init == rtl8225_rf_init) - rtl8225_rf_set_tx_power(dev, chan); - else if (priv->rf->init == rtl8225z2_rf_init) - rtl8225z2_rf_set_tx_power(dev, chan); - else - rtl8225z2_b_rf_set_tx_power(dev, chan); - - rtl8225_write(dev, 0x7, rtl8225_chan[chan - 1]); - msleep(10); -} - -static const struct rtl818x_rf_ops rtl8225_ops = { - .name = "rtl8225", - .init = rtl8225_rf_init, - .stop = rtl8225_rf_stop, - .set_chan = rtl8225_rf_set_channel -}; - -static const struct rtl818x_rf_ops rtl8225z2_ops = { - .name = "rtl8225z2", - .init = rtl8225z2_rf_init, - .stop = rtl8225_rf_stop, - .set_chan = rtl8225_rf_set_channel -}; - -static const struct rtl818x_rf_ops rtl8225z2_b_ops = { - .name = "rtl8225z2", - .init = rtl8225z2_b_rf_init, - .stop = rtl8225_rf_stop, - .set_chan = rtl8225_rf_set_channel -}; - -const struct rtl818x_rf_ops * rtl8187_detect_rf(struct ieee80211_hw *dev) -{ - u16 reg8, reg9; - struct rtl8187_priv *priv = dev->priv; - - if (!priv->is_rtl8187b) { - rtl8225_write(dev, 0, 0x1B7); - - reg8 = rtl8225_read(dev, 8); - reg9 = rtl8225_read(dev, 9); - - rtl8225_write(dev, 0, 0x0B7); - - if (reg8 != 0x588 || reg9 != 0x700) - return &rtl8225_ops; - - return &rtl8225z2_ops; - } else - return &rtl8225z2_b_ops; -} diff --git a/drivers/net/wireless/rtl818x/rtl8187_rtl8225.h b/drivers/net/wireless/rtl818x/rtl8187_rtl8225.h deleted file mode 100644 index 20c5b6ead0f6..000000000000 --- a/drivers/net/wireless/rtl818x/rtl8187_rtl8225.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Radio tuning definitions for RTL8225 on RTL8187 - * - * Copyright 2007 Michael Wu - * Copyright 2007 Andrea Merello - * - * Based on the r8187 driver, which is: - * Copyright 2005 Andrea Merello , et al. - * - * 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. - */ - -#ifndef RTL8187_RTL8225_H -#define RTL8187_RTL8225_H - -#define RTL8187_RTL8225_ANAPARAM_ON 0xa0000a59 -#define RTL8187_RTL8225_ANAPARAM2_ON 0x860c7312 -#define RTL8187_RTL8225_ANAPARAM_OFF 0xa00beb59 -#define RTL8187_RTL8225_ANAPARAM2_OFF 0x840dec11 - -#define RTL8187B_RTL8225_ANAPARAM_ON 0x45090658 -#define RTL8187B_RTL8225_ANAPARAM2_ON 0x727f3f52 -#define RTL8187B_RTL8225_ANAPARAM3_ON 0x00 -#define RTL8187B_RTL8225_ANAPARAM_OFF 0x55480658 -#define RTL8187B_RTL8225_ANAPARAM2_OFF 0x72003f50 -#define RTL8187B_RTL8225_ANAPARAM3_OFF 0x00 - -const struct rtl818x_rf_ops * rtl8187_detect_rf(struct ieee80211_hw *); - -static inline void rtl8225_write_phy_ofdm(struct ieee80211_hw *dev, - u8 addr, u32 data) -{ - rtl8187_write_phy(dev, addr, data); -} - -static inline void rtl8225_write_phy_cck(struct ieee80211_hw *dev, - u8 addr, u32 data) -{ - rtl8187_write_phy(dev, addr, data | 0x10000); -} - -#endif /* RTL8187_RTL8225_H */ -- cgit v1.2.3 From f400c82efb474b2ccf01c796b60b36408f7845a3 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Mon, 6 Dec 2010 19:08:54 -0700 Subject: MAINTAINERS: OMAP: hwmod: update hwmod code, data maintainership MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add myself and Benoît as co-maintainers of the OMAP hwmod core code. (The OMAP hwmod code manages the integration of IP blocks on the OMAP SoC family.) Add Benoît as the maintainer of OMAP4-based SoC hwmod mode. Signed-off-by: Paul Walmsley Signed-off-by: Benoît Cousson Signed-off-by: Tony Lindgren --- MAINTAINERS | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 6a588873cf8d..462be605aa3c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4328,6 +4328,20 @@ M: Deepak Saxena S: Maintained F: drivers/char/hw_random/omap-rng.c +OMAP HWMOD SUPPORT +M: Benoît Cousson +M: Paul Walmsley +L: linux-omap@vger.kernel.org +S: Maintained +F: arch/arm/mach-omap2/omap_hwmod.c +F: arch/arm/plat-omap/include/plat/omap_hwmod.h + +OMAP HWMOD DATA FOR OMAP4-BASED DEVICES +M: Benoît Cousson +L: linux-omap@vger.kernel.org +S: Maintained +F: arch/arm/mach-omap2/omap_hwmod_44xx_data.c + OMAP USB SUPPORT M: Felipe Balbi M: David Brownell -- cgit v1.2.3 From 6900609612d26ca6ba11935613aa9db112613a48 Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Tue, 21 Dec 2010 17:24:23 -0800 Subject: MAINTAINERS: update geode entry Remove Jordan as the geode maintainer (he's not been interested in geode for some time), and add myself as the maintainer. Signed-off-by: Andres Salomon Cc: Daniel Drake Cc: Jordan Crouse Cc: Chris Ball Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 6a588873cf8d..239782e51f77 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -405,7 +405,7 @@ S: Supported F: drivers/usb/gadget/amd5536udc.* AMD GEODE PROCESSOR/CHIPSET SUPPORT -P: Jordan Crouse +P: Andres Salomon L: linux-geode@lists.infradead.org (moderated for non-subscribers) W: http://www.amd.com/us-en/ConnectivitySolutions/TechnicalResources/0,,50_2334_2452_11363,00.html S: Supported -- cgit v1.2.3 From 875728807ff001b67a1e0535de5ad2cd3c41d47c Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 22 Dec 2010 09:18:29 +0100 Subject: ARM: 6537/1: update Nomadik, U300 and Ux500 maintainers Adding in self as maintainer for Nomadik and Ux500, I'm running an active -next tree for that stuff now. Extend file matchers to cover a few more relevant drivers and add git references. Cc: Alessandro Rubini Acked-by: Srinidhi Kasagar Signed-off-by: Linus Walleij Signed-off-by: Russell King --- MAINTAINERS | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 6a588873cf8d..4323f8fe38a6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -792,11 +792,14 @@ S: Maintained ARM/NOMADIK ARCHITECTURE M: Alessandro Rubini +M: Linus Walleij M: STEricsson L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: arch/arm/mach-nomadik/ F: arch/arm/plat-nomadik/ +F: drivers/i2c/busses/i2c-nomadik.c +T: git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-stericsson.git ARM/OPENMOKO NEO FREERUNNER (GTA02) MACHINE SUPPORT M: Nelson Castillo @@ -998,12 +1001,24 @@ F: drivers/i2c/busses/i2c-stu300.c F: drivers/rtc/rtc-coh901331.c F: drivers/watchdog/coh901327_wdt.c F: drivers/dma/coh901318* +F: drivers/mfd/ab3100* +F: drivers/rtc/rtc-ab3100.c +F: drivers/rtc/rtc-coh901331.c +T: git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-stericsson.git -ARM/U8500 ARM ARCHITECTURE +ARM/Ux500 ARM ARCHITECTURE M: Srinidhi Kasagar +M: Linus Walleij L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: arch/arm/mach-ux500/ +F: drivers/dma/ste_dma40* +F: drivers/mfd/ab3550* +F: drivers/mfd/abx500* +F: drivers/mfd/ab8500* +F: drivers/mfd/stmpe* +F: drivers/rtc/rtc-ab8500.c +T: git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-stericsson.git ARM/VFP SUPPORT M: Russell King -- cgit v1.2.3 From 39c3d488452ae206cfc8afda0db041ee55d01c3c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 25 Dec 2010 07:44:33 -0300 Subject: [media] cpia, stradis: remove deprecated V4L1 drivers Nobody stepped in to convert these drivers to V4L2, so they are now removed from the kernel. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/README.cpia | 191 -- Documentation/video4linux/bttv/Cards | 4 - MAINTAINERS | 6 - drivers/staging/Kconfig | 4 - drivers/staging/Makefile | 2 - drivers/staging/cpia/Kconfig | 39 - drivers/staging/cpia/Makefile | 5 - drivers/staging/cpia/TODO | 8 - drivers/staging/cpia/cpia.c | 4028 --------------------------------- drivers/staging/cpia/cpia.h | 432 ---- drivers/staging/cpia/cpia_pp.c | 869 ------- drivers/staging/cpia/cpia_usb.c | 640 ------ drivers/staging/stradis/Kconfig | 7 - drivers/staging/stradis/Makefile | 3 - drivers/staging/stradis/TODO | 6 - drivers/staging/stradis/stradis.c | 2222 ------------------ 16 files changed, 8466 deletions(-) delete mode 100644 Documentation/video4linux/README.cpia delete mode 100644 drivers/staging/cpia/Kconfig delete mode 100644 drivers/staging/cpia/Makefile delete mode 100644 drivers/staging/cpia/TODO delete mode 100644 drivers/staging/cpia/cpia.c delete mode 100644 drivers/staging/cpia/cpia.h delete mode 100644 drivers/staging/cpia/cpia_pp.c delete mode 100644 drivers/staging/cpia/cpia_usb.c delete mode 100644 drivers/staging/stradis/Kconfig delete mode 100644 drivers/staging/stradis/Makefile delete mode 100644 drivers/staging/stradis/TODO delete mode 100644 drivers/staging/stradis/stradis.c (limited to 'MAINTAINERS') diff --git a/Documentation/video4linux/README.cpia b/Documentation/video4linux/README.cpia deleted file mode 100644 index 8a747fee661f..000000000000 --- a/Documentation/video4linux/README.cpia +++ /dev/null @@ -1,191 +0,0 @@ -This is a driver for the CPiA PPC2 driven parallel connected -Camera. For example the Creative WebcamII is CPiA driven. - - ) [1]Peter Pregler, Linz 2000, published under the [2]GNU GPL - ---------------------------------------------------------------------------- - -USAGE: - -General: -======== - -1) Make sure you have created the video devices (/dev/video*): - -- if you have a recent MAKEDEV do a 'cd /dev;./MAKEDEV video' -- otherwise do a: - -cd /dev -mknod video0 c 81 0 -ln -s video0 video - -2) Compile the kernel (see below for the list of options to use), - configure your parport and reboot. - -3) If all worked well you should get messages similar - to the following (your versions may be different) on the console: - -V4L-Driver for Vision CPiA based cameras v0.7.4 -parport0: read2 timeout. -parport0: Multimedia device, VLSI Vision Ltd PPC2 -Parallel port driver for Vision CPiA based camera - CPIA Version: 1.20 (2.0) - CPIA PnP-ID: 0553:0002:0100 - VP-Version: 1.0 0100 - 1 camera(s) found - - -As modules: -=========== - -Make sure you have selected the following kernel options (you can -select all stuff as modules): - -The cpia-stuff is in the section 'Character devices -> Video For Linux'. - -CONFIG_PARPORT=m -CONFIG_PARPORT_PC=m -CONFIG_PARPORT_PC_FIFO=y -CONFIG_PARPORT_1284=y -CONFIG_VIDEO_DEV=m -CONFIG_VIDEO_CPIA=m -CONFIG_VIDEO_CPIA_PP=m - -For autoloading of all those modules you need to tell module-init-tools -some stuff. Add the following line to your module-init-tools config-file -(e.g. /etc/modprobe.conf or wherever your distribution does store that -stuff): - -options parport_pc io=0x378 irq=7 dma=3 -alias char-major-81 cpia_pp - -The first line tells the dma/irq channels to use. Those _must_ match -the settings of your BIOS. Do NOT simply use the values above. See -Documentation/parport.txt for more information about this. The second -line associates the video-device file with the driver. Of cause you -can also load the modules once upon boot (usually done in /etc/modules). - -Linked into the kernel: -======================= - -Make sure you have selected the following kernel options. Note that -you cannot compile the parport-stuff as modules and the cpia-driver -statically (the other way round is okay though). - -The cpia-stuff is in the section 'Character devices -> Video For Linux'. - -CONFIG_PARPORT=y -CONFIG_PARPORT_PC=y -CONFIG_PARPORT_PC_FIFO=y -CONFIG_PARPORT_1284=y -CONFIG_VIDEO_DEV=y -CONFIG_VIDEO_CPIA=y -CONFIG_VIDEO_CPIA_PP=y - -To use DMA/irq you will need to tell the kernel upon boot time the -hardware configuration of the parport. You can give the boot-parameter -at the LILO-prompt or specify it in lilo.conf. I use the following -append-line in lilo.conf: - - append="parport=0x378,7,3" - -See Documentation/parport.txt for more information about the -configuration of the parport and the values given above. Do not simply -use the values given above. - ---------------------------------------------------------------------------- -FEATURES: - -- mmap/read v4l-interface (but no overlay) -- image formats: CIF/QCIF, SIF/QSIF, various others used by isabel; - note: all sizes except CIF/QCIF are implemented by clipping, i.e. - pixels are not uploaded from the camera -- palettes: VIDEO_PALETTE_GRAY, VIDEO_PALETTE_RGB565, VIDEO_PALETTE_RGB555, - VIDEO_PALETTE_RGB24, VIDEO_PALETTE_RGB32, VIDEO_PALETTE_YUYV, - VIDEO_PALETTE_UYVY, VIDEO_PALETTE_YUV422 -- state information (color balance, exposure, ...) is preserved between - device opens -- complete control over camera via proc-interface (_all_ camera settings are - supported), there is also a python-gtk application available for this [3] -- works under SMP (but the driver is completely serialized and synchronous) - so you get no benefit from SMP, but at least it does not crash your box -- might work for non-Intel architecture, let us know about this - ---------------------------------------------------------------------------- -TESTED APPLICATIONS: - -- a simple test application based on Xt is available at [3] -- another test-application based on gqcam-0.4 (uses GTK) -- gqcam-0.6 should work -- xawtv-3.x (also the webcam software) -- xawtv-2.46 -- w3cam (cgi-interface and vidcat, e.g. you may try out 'vidcat |xv - -maxpect -root -quit +noresetroot -rmode 5 -') -- vic, the MBONE video conferencing tool (version 2.8ucl4-1) -- isabel 3R4beta (barely working, but AFAICT all the problems are on - their side) -- camserv-0.40 - -See [3] for pointers to v4l-applications. - ---------------------------------------------------------------------------- -KNOWN PROBLEMS: - -- some applications do not handle the image format correctly, you will - see strange horizontal stripes instead of a nice picture -> make sure - your application does use a supported image size or queries the driver - for the actually used size (reason behind this: the camera cannot - provide any image format, so if size NxM is requested the driver will - use a format to the closest fitting N1xM1, the application should now - query for this granted size, most applications do not). -- all the todo ;) -- if there is not enough light and the picture is too dark try to - adjust the SetSensorFPS setting, automatic frame rate adjustment - has its price -- do not try out isabel 3R4beta (built 135), you will be disappointed - ---------------------------------------------------------------------------- -TODO: - -- multiple camera support (struct camera or something) - This should work, - but hasn't been tested yet. -- architecture independence? -- SMP-safe asynchronous mmap interface -- nibble mode for old parport interfaces -- streaming capture, this should give a performance gain - ---------------------------------------------------------------------------- -IMPLEMENTATION NOTES: - -The camera can act in two modes, streaming or grabbing. Right now a -polling grab-scheme is used. Maybe interrupt driven streaming will be -used for a asynchronous mmap interface in the next major release of the -driver. This might give a better frame rate. - ---------------------------------------------------------------------------- -THANKS (in no particular order): - -- Scott J. Bertin for cleanups, the proc-filesystem - and much more -- Henry Bruce for providing developers information about - the CPiA chip, I wish all companies would treat Linux as seriously -- Karoly Erdei and RISC-Linz for being - my boss ;) resp. my employer and for providing me the hardware and - allow me to devote some working time to this project -- Manuel J. Petit de Gabriel for providing help - with Isabel (http://isabel.dit.upm.es/) -- Bas Huisman for writing the initial parport code -- Jarl Totland for setting up the mailing list - and maintaining the web-server[3] -- Chris Whiteford for fixes related to the - 1.02 firmware -- special kudos to all the tester whose machines crashed and/or - will crash. :) - ---------------------------------------------------------------------------- -REFERENCES - - 1. http://www.risc.uni-linz.ac.at/ - mailto:Peter_Pregler@email.com - 2. see the file COPYING in the top directory of the kernel tree - 3. http://webcam.sourceforge.net/ diff --git a/Documentation/video4linux/bttv/Cards b/Documentation/video4linux/bttv/Cards index 12217fc49725..db833ced2cb8 100644 --- a/Documentation/video4linux/bttv/Cards +++ b/Documentation/video4linux/bttv/Cards @@ -464,10 +464,6 @@ Siemens ------- Multimedia eXtension Board (MXB) (SAA7146, SAA7111) -Stradis -------- - SDM275,SDM250,SDM026,SDM025 (SAA7146, IBMMPEG2): MPEG2 decoder only - Powercolor ---------- MTV878 diff --git a/MAINTAINERS b/MAINTAINERS index 71e40f9118df..92203a35d233 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5697,12 +5697,6 @@ M: Ion Badulescu S: Odd Fixes F: drivers/net/starfire* -STRADIS MPEG-2 DECODER DRIVER -M: Nathan Laredo -W: http://www.stradis.com/ -S: Maintained -F: drivers/media/video/stradis.c - SUN3/3X M: Sam Creasey W: http://sammy.net/sun3/ diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index d9e3b6ddf682..7e7b27b3ddb0 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -51,10 +51,6 @@ source "drivers/staging/cx25821/Kconfig" source "drivers/staging/tm6000/Kconfig" -source "drivers/staging/cpia/Kconfig" - -source "drivers/staging/stradis/Kconfig" - source "drivers/staging/se401/Kconfig" source "drivers/staging/usbvideo/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 8058a15b0cd7..72e0c839ea34 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -8,8 +8,6 @@ obj-$(CONFIG_SLICOSS) += slicoss/ obj-$(CONFIG_VIDEO_GO7007) += go7007/ obj-$(CONFIG_VIDEO_CX25821) += cx25821/ obj-$(CONFIG_VIDEO_TM6000) += tm6000/ -obj-$(CONFIG_VIDEO_CPIA) += cpia/ -obj-$(CONFIG_VIDEO_STRADIS) += stradis/ obj-$(CONFIG_USB_VICAM) += usbvideo/ obj-$(CONFIG_USB_SE401) += se401/ obj-$(CONFIG_LIRC_STAGING) += lirc/ diff --git a/drivers/staging/cpia/Kconfig b/drivers/staging/cpia/Kconfig deleted file mode 100644 index 205d247ad373..000000000000 --- a/drivers/staging/cpia/Kconfig +++ /dev/null @@ -1,39 +0,0 @@ -config VIDEO_CPIA - tristate "CPiA Video For Linux (DEPRECATED)" - depends on VIDEO_V4L1 - default n - ---help--- - This driver is DEPRECATED please use the gspca cpia1 module - instead. Note that you need atleast version 0.6.4 of libv4l for - the cpia1 gspca module. - - This is the video4linux driver for cameras based on Vision's CPiA - (Colour Processor Interface ASIC), such as the Creative Labs Video - Blaster Webcam II. If you have one of these cameras, say Y here - and select parallel port and/or USB lowlevel support below, - otherwise say N. This will not work with the Creative Webcam III. - - Please read for more - information. - - This driver is also available as a module (cpia). - -config VIDEO_CPIA_PP - tristate "CPiA Parallel Port Lowlevel Support" - depends on PARPORT_1284 && VIDEO_CPIA && PARPORT - help - This is the lowlevel parallel port support for cameras based on - Vision's CPiA (Colour Processor Interface ASIC), such as the - Creative Webcam II. If you have the parallel port version of one - of these cameras, say Y here, otherwise say N. It is also available - as a module (cpia_pp). - -config VIDEO_CPIA_USB - tristate "CPiA USB Lowlevel Support" - depends on VIDEO_CPIA && USB - help - This is the lowlevel USB support for cameras based on Vision's CPiA - (Colour Processor Interface ASIC), such as the Creative Webcam II. - If you have the USB version of one of these cameras, say Y here, - otherwise say N. This will not work with the Creative Webcam III. - It is also available as a module (cpia_usb). diff --git a/drivers/staging/cpia/Makefile b/drivers/staging/cpia/Makefile deleted file mode 100644 index 89e52f10d739..000000000000 --- a/drivers/staging/cpia/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -obj-$(CONFIG_VIDEO_CPIA) += cpia.o -obj-$(CONFIG_VIDEO_CPIA_PP) += cpia_pp.o -obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb.o - -EXTRA_CFLAGS += -Idrivers/media/video diff --git a/drivers/staging/cpia/TODO b/drivers/staging/cpia/TODO deleted file mode 100644 index ccb1c0775eec..000000000000 --- a/drivers/staging/cpia/TODO +++ /dev/null @@ -1,8 +0,0 @@ -This is an obsolete driver for some cpia-based webcams that use the parallel port. -We couldn't find anyone with this hardware in order to port it to use V4L2. - -Also, parallel-port webcams are obsolete nowadays. - -If nobody take care on it, the driver will be removed for 2.6.38. - -Please send patches to linux-media@vger.kernel.org diff --git a/drivers/staging/cpia/cpia.c b/drivers/staging/cpia/cpia.c deleted file mode 100644 index 0e740b8dafc3..000000000000 --- a/drivers/staging/cpia/cpia.c +++ /dev/null @@ -1,4028 +0,0 @@ -/* - * cpia CPiA driver - * - * Supports CPiA based Video Camera's. - * - * (C) Copyright 1999-2000 Peter Pregler - * (C) Copyright 1999-2000 Scott J. Bertin - * (C) Copyright 1999-2000 Johannes Erdfelt - * (C) Copyright 2000 STMicroelectronics - * - * 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. - */ - -/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */ -/* #define _CPIA_DEBUG_ 1 */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cpia.h" - -static int video_nr = -1; - -#ifdef MODULE -module_param(video_nr, int, 0); -MODULE_AUTHOR("Scott J. Bertin & Peter Pregler & Johannes Erdfelt "); -MODULE_DESCRIPTION("V4L-driver for Vision CPiA based cameras"); -MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("video"); -#endif - -static unsigned short colorspace_conv; -module_param(colorspace_conv, ushort, 0444); -MODULE_PARM_DESC(colorspace_conv, - " Colorspace conversion:" - "\n 0 = disable, 1 = enable" - "\n Default value is 0" - ); - -#define ABOUT "V4L-Driver for Vision CPiA based cameras" - -#define CPIA_MODULE_CPIA (0<<5) -#define CPIA_MODULE_SYSTEM (1<<5) -#define CPIA_MODULE_VP_CTRL (5<<5) -#define CPIA_MODULE_CAPTURE (6<<5) -#define CPIA_MODULE_DEBUG (7<<5) - -#define INPUT (DATA_IN << 8) -#define OUTPUT (DATA_OUT << 8) - -#define CPIA_COMMAND_GetCPIAVersion (INPUT | CPIA_MODULE_CPIA | 1) -#define CPIA_COMMAND_GetPnPID (INPUT | CPIA_MODULE_CPIA | 2) -#define CPIA_COMMAND_GetCameraStatus (INPUT | CPIA_MODULE_CPIA | 3) -#define CPIA_COMMAND_GotoHiPower (OUTPUT | CPIA_MODULE_CPIA | 4) -#define CPIA_COMMAND_GotoLoPower (OUTPUT | CPIA_MODULE_CPIA | 5) -#define CPIA_COMMAND_GotoSuspend (OUTPUT | CPIA_MODULE_CPIA | 7) -#define CPIA_COMMAND_GotoPassThrough (OUTPUT | CPIA_MODULE_CPIA | 8) -#define CPIA_COMMAND_ModifyCameraStatus (OUTPUT | CPIA_MODULE_CPIA | 10) - -#define CPIA_COMMAND_ReadVCRegs (INPUT | CPIA_MODULE_SYSTEM | 1) -#define CPIA_COMMAND_WriteVCReg (OUTPUT | CPIA_MODULE_SYSTEM | 2) -#define CPIA_COMMAND_ReadMCPorts (INPUT | CPIA_MODULE_SYSTEM | 3) -#define CPIA_COMMAND_WriteMCPort (OUTPUT | CPIA_MODULE_SYSTEM | 4) -#define CPIA_COMMAND_SetBaudRate (OUTPUT | CPIA_MODULE_SYSTEM | 5) -#define CPIA_COMMAND_SetECPTiming (OUTPUT | CPIA_MODULE_SYSTEM | 6) -#define CPIA_COMMAND_ReadIDATA (INPUT | CPIA_MODULE_SYSTEM | 7) -#define CPIA_COMMAND_WriteIDATA (OUTPUT | CPIA_MODULE_SYSTEM | 8) -#define CPIA_COMMAND_GenericCall (OUTPUT | CPIA_MODULE_SYSTEM | 9) -#define CPIA_COMMAND_I2CStart (OUTPUT | CPIA_MODULE_SYSTEM | 10) -#define CPIA_COMMAND_I2CStop (OUTPUT | CPIA_MODULE_SYSTEM | 11) -#define CPIA_COMMAND_I2CWrite (OUTPUT | CPIA_MODULE_SYSTEM | 12) -#define CPIA_COMMAND_I2CRead (INPUT | CPIA_MODULE_SYSTEM | 13) - -#define CPIA_COMMAND_GetVPVersion (INPUT | CPIA_MODULE_VP_CTRL | 1) -#define CPIA_COMMAND_ResetFrameCounter (INPUT | CPIA_MODULE_VP_CTRL | 2) -#define CPIA_COMMAND_SetColourParams (OUTPUT | CPIA_MODULE_VP_CTRL | 3) -#define CPIA_COMMAND_SetExposure (OUTPUT | CPIA_MODULE_VP_CTRL | 4) -#define CPIA_COMMAND_SetColourBalance (OUTPUT | CPIA_MODULE_VP_CTRL | 6) -#define CPIA_COMMAND_SetSensorFPS (OUTPUT | CPIA_MODULE_VP_CTRL | 7) -#define CPIA_COMMAND_SetVPDefaults (OUTPUT | CPIA_MODULE_VP_CTRL | 8) -#define CPIA_COMMAND_SetApcor (OUTPUT | CPIA_MODULE_VP_CTRL | 9) -#define CPIA_COMMAND_SetFlickerCtrl (OUTPUT | CPIA_MODULE_VP_CTRL | 10) -#define CPIA_COMMAND_SetVLOffset (OUTPUT | CPIA_MODULE_VP_CTRL | 11) -#define CPIA_COMMAND_GetColourParams (INPUT | CPIA_MODULE_VP_CTRL | 16) -#define CPIA_COMMAND_GetColourBalance (INPUT | CPIA_MODULE_VP_CTRL | 17) -#define CPIA_COMMAND_GetExposure (INPUT | CPIA_MODULE_VP_CTRL | 18) -#define CPIA_COMMAND_SetSensorMatrix (OUTPUT | CPIA_MODULE_VP_CTRL | 19) -#define CPIA_COMMAND_ColourBars (OUTPUT | CPIA_MODULE_VP_CTRL | 25) -#define CPIA_COMMAND_ReadVPRegs (INPUT | CPIA_MODULE_VP_CTRL | 30) -#define CPIA_COMMAND_WriteVPReg (OUTPUT | CPIA_MODULE_VP_CTRL | 31) - -#define CPIA_COMMAND_GrabFrame (OUTPUT | CPIA_MODULE_CAPTURE | 1) -#define CPIA_COMMAND_UploadFrame (OUTPUT | CPIA_MODULE_CAPTURE | 2) -#define CPIA_COMMAND_SetGrabMode (OUTPUT | CPIA_MODULE_CAPTURE | 3) -#define CPIA_COMMAND_InitStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 4) -#define CPIA_COMMAND_FiniStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 5) -#define CPIA_COMMAND_StartStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 6) -#define CPIA_COMMAND_EndStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 7) -#define CPIA_COMMAND_SetFormat (OUTPUT | CPIA_MODULE_CAPTURE | 8) -#define CPIA_COMMAND_SetROI (OUTPUT | CPIA_MODULE_CAPTURE | 9) -#define CPIA_COMMAND_SetCompression (OUTPUT | CPIA_MODULE_CAPTURE | 10) -#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11) -#define CPIA_COMMAND_SetYUVThresh (OUTPUT | CPIA_MODULE_CAPTURE | 12) -#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13) -#define CPIA_COMMAND_DiscardFrame (OUTPUT | CPIA_MODULE_CAPTURE | 14) -#define CPIA_COMMAND_GrabReset (OUTPUT | CPIA_MODULE_CAPTURE | 15) - -#define CPIA_COMMAND_OutputRS232 (OUTPUT | CPIA_MODULE_DEBUG | 1) -#define CPIA_COMMAND_AbortProcess (OUTPUT | CPIA_MODULE_DEBUG | 4) -#define CPIA_COMMAND_SetDramPage (OUTPUT | CPIA_MODULE_DEBUG | 5) -#define CPIA_COMMAND_StartDramUpload (OUTPUT | CPIA_MODULE_DEBUG | 6) -#define CPIA_COMMAND_StartDummyDtream (OUTPUT | CPIA_MODULE_DEBUG | 8) -#define CPIA_COMMAND_AbortStream (OUTPUT | CPIA_MODULE_DEBUG | 9) -#define CPIA_COMMAND_DownloadDRAM (OUTPUT | CPIA_MODULE_DEBUG | 10) -#define CPIA_COMMAND_Null (OUTPUT | CPIA_MODULE_DEBUG | 11) - -enum { - FRAME_READY, /* Ready to grab into */ - FRAME_GRABBING, /* In the process of being grabbed into */ - FRAME_DONE, /* Finished grabbing, but not been synced yet */ - FRAME_UNUSED, /* Unused (no MCAPTURE) */ -}; - -#define COMMAND_NONE 0x0000 -#define COMMAND_SETCOMPRESSION 0x0001 -#define COMMAND_SETCOMPRESSIONTARGET 0x0002 -#define COMMAND_SETCOLOURPARAMS 0x0004 -#define COMMAND_SETFORMAT 0x0008 -#define COMMAND_PAUSE 0x0010 -#define COMMAND_RESUME 0x0020 -#define COMMAND_SETYUVTHRESH 0x0040 -#define COMMAND_SETECPTIMING 0x0080 -#define COMMAND_SETCOMPRESSIONPARAMS 0x0100 -#define COMMAND_SETEXPOSURE 0x0200 -#define COMMAND_SETCOLOURBALANCE 0x0400 -#define COMMAND_SETSENSORFPS 0x0800 -#define COMMAND_SETAPCOR 0x1000 -#define COMMAND_SETFLICKERCTRL 0x2000 -#define COMMAND_SETVLOFFSET 0x4000 -#define COMMAND_SETLIGHTS 0x8000 - -#define ROUND_UP_EXP_FOR_FLICKER 15 - -/* Constants for automatic frame rate adjustment */ -#define MAX_EXP 302 -#define MAX_EXP_102 255 -#define LOW_EXP 140 -#define VERY_LOW_EXP 70 -#define TC 94 -#define EXP_ACC_DARK 50 -#define EXP_ACC_LIGHT 90 -#define HIGH_COMP_102 160 -#define MAX_COMP 239 -#define DARK_TIME 3 -#define LIGHT_TIME 3 - -/* Maximum number of 10ms loops to wait for the stream to become ready */ -#define READY_TIMEOUT 100 - -/* Developer's Guide Table 5 p 3-34 - * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/ -static u8 flicker_jumps[2][2][4] = -{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } }, - { { 64, 32, 16, 8 }, { 76, 38, 19, 9} } -}; - -/* forward declaration of local function */ -static void reset_camera_struct(struct cam_data *cam); -static int find_over_exposure(int brightness); -static void set_flicker(struct cam_params *params, volatile u32 *command_flags, - int on); - - -/********************************************************************** - * - * Memory management - * - **********************************************************************/ -static void *rvmalloc(unsigned long size) -{ - void *mem; - unsigned long adr; - - size = PAGE_ALIGN(size); - mem = vmalloc_32(size); - if (!mem) - return NULL; - - memset(mem, 0, size); /* Clear the ram out, no junk to the user */ - adr = (unsigned long) mem; - while (size > 0) { - SetPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - - return mem; -} - -static void rvfree(void *mem, unsigned long size) -{ - unsigned long adr; - - if (!mem) - return; - - adr = (unsigned long) mem; - while ((long) size > 0) { - ClearPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - vfree(mem); -} - -/********************************************************************** - * - * /proc interface - * - **********************************************************************/ -#ifdef CONFIG_PROC_FS -static struct proc_dir_entry *cpia_proc_root=NULL; - -static int cpia_proc_show(struct seq_file *m, void *v) -{ - struct cam_data *cam = m->private; - int tmp; - char tmpstr[29]; - - seq_printf(m, "read-only\n-----------------------\n"); - seq_printf(m, "V4L Driver version: %d.%d.%d\n", - CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER); - seq_printf(m, "CPIA Version: %d.%02d (%d.%d)\n", - cam->params.version.firmwareVersion, - cam->params.version.firmwareRevision, - cam->params.version.vcVersion, - cam->params.version.vcRevision); - seq_printf(m, "CPIA PnP-ID: %04x:%04x:%04x\n", - cam->params.pnpID.vendor, cam->params.pnpID.product, - cam->params.pnpID.deviceRevision); - seq_printf(m, "VP-Version: %d.%d %04x\n", - cam->params.vpVersion.vpVersion, - cam->params.vpVersion.vpRevision, - cam->params.vpVersion.cameraHeadID); - - seq_printf(m, "system_state: %#04x\n", - cam->params.status.systemState); - seq_printf(m, "grab_state: %#04x\n", - cam->params.status.grabState); - seq_printf(m, "stream_state: %#04x\n", - cam->params.status.streamState); - seq_printf(m, "fatal_error: %#04x\n", - cam->params.status.fatalError); - seq_printf(m, "cmd_error: %#04x\n", - cam->params.status.cmdError); - seq_printf(m, "debug_flags: %#04x\n", - cam->params.status.debugFlags); - seq_printf(m, "vp_status: %#04x\n", - cam->params.status.vpStatus); - seq_printf(m, "error_code: %#04x\n", - cam->params.status.errorCode); - /* QX3 specific entries */ - if (cam->params.qx3.qx3_detected) { - seq_printf(m, "button: %4d\n", - cam->params.qx3.button); - seq_printf(m, "cradled: %4d\n", - cam->params.qx3.cradled); - } - seq_printf(m, "video_size: %s\n", - cam->params.format.videoSize == VIDEOSIZE_CIF ? - "CIF " : "QCIF"); - seq_printf(m, "roi: (%3d, %3d) to (%3d, %3d)\n", - cam->params.roi.colStart*8, - cam->params.roi.rowStart*4, - cam->params.roi.colEnd*8, - cam->params.roi.rowEnd*4); - seq_printf(m, "actual_fps: %3d\n", cam->fps); - seq_printf(m, "transfer_rate: %4dkB/s\n", - cam->transfer_rate); - - seq_printf(m, "\nread-write\n"); - seq_printf(m, "----------------------- current min" - " max default comment\n"); - seq_printf(m, "brightness: %8d %8d %8d %8d\n", - cam->params.colourParams.brightness, 0, 100, 50); - if (cam->params.version.firmwareVersion == 1 && - cam->params.version.firmwareRevision == 2) - /* 1-02 firmware limits contrast to 80 */ - tmp = 80; - else - tmp = 96; - - seq_printf(m, "contrast: %8d %8d %8d %8d" - " steps of 8\n", - cam->params.colourParams.contrast, 0, tmp, 48); - seq_printf(m, "saturation: %8d %8d %8d %8d\n", - cam->params.colourParams.saturation, 0, 100, 50); - tmp = (25000+5000*cam->params.sensorFps.baserate)/ - (1<params.sensorFps.divisor); - seq_printf(m, "sensor_fps: %4d.%03d %8d %8d %8d\n", - tmp/1000, tmp%1000, 3, 30, 15); - seq_printf(m, "stream_start_line: %8d %8d %8d %8d\n", - 2*cam->params.streamStartLine, 0, - cam->params.format.videoSize == VIDEOSIZE_CIF ? 288:144, - cam->params.format.videoSize == VIDEOSIZE_CIF ? 240:120); - seq_printf(m, "sub_sample: %8s %8s %8s %8s\n", - cam->params.format.subSample == SUBSAMPLE_420 ? - "420" : "422", "420", "422", "422"); - seq_printf(m, "yuv_order: %8s %8s %8s %8s\n", - cam->params.format.yuvOrder == YUVORDER_YUYV ? - "YUYV" : "UYVY", "YUYV" , "UYVY", "YUYV"); - seq_printf(m, "ecp_timing: %8s %8s %8s %8s\n", - cam->params.ecpTiming ? "slow" : "normal", "slow", - "normal", "normal"); - - if (cam->params.colourBalance.balanceMode == 2) { - sprintf(tmpstr, "auto"); - } else { - sprintf(tmpstr, "manual"); - } - seq_printf(m, "color_balance_mode: %8s %8s %8s" - " %8s\n", tmpstr, "manual", "auto", "auto"); - seq_printf(m, "red_gain: %8d %8d %8d %8d\n", - cam->params.colourBalance.redGain, 0, 212, 32); - seq_printf(m, "green_gain: %8d %8d %8d %8d\n", - cam->params.colourBalance.greenGain, 0, 212, 6); - seq_printf(m, "blue_gain: %8d %8d %8d %8d\n", - cam->params.colourBalance.blueGain, 0, 212, 92); - - if (cam->params.version.firmwareVersion == 1 && - cam->params.version.firmwareRevision == 2) - /* 1-02 firmware limits gain to 2 */ - sprintf(tmpstr, "%8d %8d %8d", 1, 2, 2); - else - sprintf(tmpstr, "%8d %8d %8d", 1, 8, 2); - - if (cam->params.exposure.gainMode == 0) - seq_printf(m, "max_gain: unknown %28s" - " powers of 2\n", tmpstr); - else - seq_printf(m, "max_gain: %8d %28s" - " 1,2,4 or 8 \n", - 1<<(cam->params.exposure.gainMode-1), tmpstr); - - switch(cam->params.exposure.expMode) { - case 1: - case 3: - sprintf(tmpstr, "manual"); - break; - case 2: - sprintf(tmpstr, "auto"); - break; - default: - sprintf(tmpstr, "unknown"); - break; - } - seq_printf(m, "exposure_mode: %8s %8s %8s" - " %8s\n", tmpstr, "manual", "auto", "auto"); - seq_printf(m, "centre_weight: %8s %8s %8s %8s\n", - (2-cam->params.exposure.centreWeight) ? "on" : "off", - "off", "on", "on"); - seq_printf(m, "gain: %8d %8d max_gain %8d 1,2,4,8 possible\n", - 1<params.exposure.gain, 1, 1); - if (cam->params.version.firmwareVersion == 1 && - cam->params.version.firmwareRevision == 2) - /* 1-02 firmware limits fineExp/2 to 127 */ - tmp = 254; - else - tmp = 510; - - seq_printf(m, "fine_exp: %8d %8d %8d %8d\n", - cam->params.exposure.fineExp*2, 0, tmp, 0); - if (cam->params.version.firmwareVersion == 1 && - cam->params.version.firmwareRevision == 2) - /* 1-02 firmware limits coarseExpHi to 0 */ - tmp = MAX_EXP_102; - else - tmp = MAX_EXP; - - seq_printf(m, "coarse_exp: %8d %8d %8d" - " %8d\n", cam->params.exposure.coarseExpLo+ - 256*cam->params.exposure.coarseExpHi, 0, tmp, 185); - seq_printf(m, "red_comp: %8d %8d %8d %8d\n", - cam->params.exposure.redComp, COMP_RED, 255, COMP_RED); - seq_printf(m, "green1_comp: %8d %8d %8d %8d\n", - cam->params.exposure.green1Comp, COMP_GREEN1, 255, - COMP_GREEN1); - seq_printf(m, "green2_comp: %8d %8d %8d %8d\n", - cam->params.exposure.green2Comp, COMP_GREEN2, 255, - COMP_GREEN2); - seq_printf(m, "blue_comp: %8d %8d %8d %8d\n", - cam->params.exposure.blueComp, COMP_BLUE, 255, COMP_BLUE); - - seq_printf(m, "apcor_gain1: %#8x %#8x %#8x %#8x\n", - cam->params.apcor.gain1, 0, 0xff, 0x1c); - seq_printf(m, "apcor_gain2: %#8x %#8x %#8x %#8x\n", - cam->params.apcor.gain2, 0, 0xff, 0x1a); - seq_printf(m, "apcor_gain4: %#8x %#8x %#8x %#8x\n", - cam->params.apcor.gain4, 0, 0xff, 0x2d); - seq_printf(m, "apcor_gain8: %#8x %#8x %#8x %#8x\n", - cam->params.apcor.gain8, 0, 0xff, 0x2a); - seq_printf(m, "vl_offset_gain1: %8d %8d %8d %8d\n", - cam->params.vlOffset.gain1, 0, 255, 24); - seq_printf(m, "vl_offset_gain2: %8d %8d %8d %8d\n", - cam->params.vlOffset.gain2, 0, 255, 28); - seq_printf(m, "vl_offset_gain4: %8d %8d %8d %8d\n", - cam->params.vlOffset.gain4, 0, 255, 30); - seq_printf(m, "vl_offset_gain8: %8d %8d %8d %8d\n", - cam->params.vlOffset.gain8, 0, 255, 30); - seq_printf(m, "flicker_control: %8s %8s %8s %8s\n", - cam->params.flickerControl.flickerMode ? "on" : "off", - "off", "on", "off"); - seq_printf(m, "mains_frequency: %8d %8d %8d %8d" - " only 50/60\n", - cam->mainsFreq ? 60 : 50, 50, 60, 50); - if(cam->params.flickerControl.allowableOverExposure < 0) - seq_printf(m, "allowable_overexposure: %4dauto auto %8d auto\n", - -cam->params.flickerControl.allowableOverExposure, - 255); - else - seq_printf(m, "allowable_overexposure: %8d auto %8d auto\n", - cam->params.flickerControl.allowableOverExposure, - 255); - seq_printf(m, "compression_mode: "); - switch(cam->params.compression.mode) { - case CPIA_COMPRESSION_NONE: - seq_printf(m, "%8s", "none"); - break; - case CPIA_COMPRESSION_AUTO: - seq_printf(m, "%8s", "auto"); - break; - case CPIA_COMPRESSION_MANUAL: - seq_printf(m, "%8s", "manual"); - break; - default: - seq_printf(m, "%8s", "unknown"); - break; - } - seq_printf(m, " none,auto,manual auto\n"); - seq_printf(m, "decimation_enable: %8s %8s %8s %8s\n", - cam->params.compression.decimation == - DECIMATION_ENAB ? "on":"off", "off", "on", - "off"); - seq_printf(m, "compression_target: %9s %9s %9s %9s\n", - cam->params.compressionTarget.frTargeting == - CPIA_COMPRESSION_TARGET_FRAMERATE ? - "framerate":"quality", - "framerate", "quality", "quality"); - seq_printf(m, "target_framerate: %8d %8d %8d %8d\n", - cam->params.compressionTarget.targetFR, 1, 30, 15); - seq_printf(m, "target_quality: %8d %8d %8d %8d\n", - cam->params.compressionTarget.targetQ, 1, 64, 5); - seq_printf(m, "y_threshold: %8d %8d %8d %8d\n", - cam->params.yuvThreshold.yThreshold, 0, 31, 6); - seq_printf(m, "uv_threshold: %8d %8d %8d %8d\n", - cam->params.yuvThreshold.uvThreshold, 0, 31, 6); - seq_printf(m, "hysteresis: %8d %8d %8d %8d\n", - cam->params.compressionParams.hysteresis, 0, 255, 3); - seq_printf(m, "threshold_max: %8d %8d %8d %8d\n", - cam->params.compressionParams.threshMax, 0, 255, 11); - seq_printf(m, "small_step: %8d %8d %8d %8d\n", - cam->params.compressionParams.smallStep, 0, 255, 1); - seq_printf(m, "large_step: %8d %8d %8d %8d\n", - cam->params.compressionParams.largeStep, 0, 255, 3); - seq_printf(m, "decimation_hysteresis: %8d %8d %8d %8d\n", - cam->params.compressionParams.decimationHysteresis, - 0, 255, 2); - seq_printf(m, "fr_diff_step_thresh: %8d %8d %8d %8d\n", - cam->params.compressionParams.frDiffStepThresh, - 0, 255, 5); - seq_printf(m, "q_diff_step_thresh: %8d %8d %8d %8d\n", - cam->params.compressionParams.qDiffStepThresh, - 0, 255, 3); - seq_printf(m, "decimation_thresh_mod: %8d %8d %8d %8d\n", - cam->params.compressionParams.decimationThreshMod, - 0, 255, 2); - /* QX3 specific entries */ - if (cam->params.qx3.qx3_detected) { - seq_printf(m, "toplight: %8s %8s %8s %8s\n", - cam->params.qx3.toplight ? "on" : "off", - "off", "on", "off"); - seq_printf(m, "bottomlight: %8s %8s %8s %8s\n", - cam->params.qx3.bottomlight ? "on" : "off", - "off", "on", "off"); - } - - return 0; -} - -static int cpia_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, cpia_proc_show, PDE(inode)->data); -} - -static int match(char *checkstr, char **buffer, size_t *count, - int *find_colon, int *err) -{ - int ret, colon_found = 1; - int len = strlen(checkstr); - ret = (len <= *count && strncmp(*buffer, checkstr, len) == 0); - if (ret) { - *buffer += len; - *count -= len; - if (*find_colon) { - colon_found = 0; - while (*count && (**buffer == ' ' || **buffer == '\t' || - (!colon_found && **buffer == ':'))) { - if (**buffer == ':') - colon_found = 1; - --*count; - ++*buffer; - } - if (!*count || !colon_found) - *err = -EINVAL; - *find_colon = 0; - } - } - return ret; -} - -static unsigned long int value(char **buffer, size_t *count, int *err) -{ - char *p; - unsigned long int ret; - ret = simple_strtoul(*buffer, &p, 0); - if (p == *buffer) - *err = -EINVAL; - else { - *count -= p - *buffer; - *buffer = p; - } - return ret; -} - -static ssize_t cpia_proc_write(struct file *file, const char __user *buf, - size_t count, loff_t *pos) -{ - struct cam_data *cam = PDE(file->f_path.dentry->d_inode)->data; - struct cam_params new_params; - char *page, *buffer; - int retval, find_colon; - int size = count; - unsigned long val = 0; - u32 command_flags = 0; - u8 new_mains; - - /* - * This code to copy from buf to page is shamelessly copied - * from the comx driver - */ - if (count > PAGE_SIZE) { - printk(KERN_ERR "count is %zu > %d!!!\n", count, (int)PAGE_SIZE); - return -ENOSPC; - } - - if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM; - - if(copy_from_user(page, buf, count)) - { - retval = -EFAULT; - goto out; - } - - if (page[count-1] == '\n') - page[count-1] = '\0'; - else if (count < PAGE_SIZE) - page[count] = '\0'; - else if (page[count]) { - retval = -EINVAL; - goto out; - } - - buffer = page; - - if (mutex_lock_interruptible(&cam->param_lock)) - return -ERESTARTSYS; - - /* - * Skip over leading whitespace - */ - while (count && isspace(*buffer)) { - --count; - ++buffer; - } - - memcpy(&new_params, &cam->params, sizeof(struct cam_params)); - new_mains = cam->mainsFreq; - -#define MATCH(x) (match(x, &buffer, &count, &find_colon, &retval)) -#define VALUE (value(&buffer,&count, &retval)) -#define FIRMWARE_VERSION(x,y) (new_params.version.firmwareVersion == (x) && \ - new_params.version.firmwareRevision == (y)) - - retval = 0; - while (count && !retval) { - find_colon = 1; - if (MATCH("brightness")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 100) - new_params.colourParams.brightness = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOLOURPARAMS; - if(new_params.flickerControl.allowableOverExposure < 0) - new_params.flickerControl.allowableOverExposure = - -find_over_exposure(new_params.colourParams.brightness); - if(new_params.flickerControl.flickerMode != 0) - command_flags |= COMMAND_SETFLICKERCTRL; - - } else if (MATCH("contrast")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 100) { - /* contrast is in steps of 8, so round*/ - val = ((val + 3) / 8) * 8; - /* 1-02 firmware limits contrast to 80*/ - if (FIRMWARE_VERSION(1,2) && val > 80) - val = 80; - - new_params.colourParams.contrast = val; - } else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOLOURPARAMS; - } else if (MATCH("saturation")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 100) - new_params.colourParams.saturation = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOLOURPARAMS; - } else if (MATCH("sensor_fps")) { - if (!retval) - val = VALUE; - - if (!retval) { - /* find values so that sensorFPS is minimized, - * but >= val */ - if (val > 30) - retval = -EINVAL; - else if (val > 25) { - new_params.sensorFps.divisor = 0; - new_params.sensorFps.baserate = 1; - } else if (val > 15) { - new_params.sensorFps.divisor = 0; - new_params.sensorFps.baserate = 0; - } else if (val > 12) { - new_params.sensorFps.divisor = 1; - new_params.sensorFps.baserate = 1; - } else if (val > 7) { - new_params.sensorFps.divisor = 1; - new_params.sensorFps.baserate = 0; - } else if (val > 6) { - new_params.sensorFps.divisor = 2; - new_params.sensorFps.baserate = 1; - } else if (val > 3) { - new_params.sensorFps.divisor = 2; - new_params.sensorFps.baserate = 0; - } else { - new_params.sensorFps.divisor = 3; - /* Either base rate would work here */ - new_params.sensorFps.baserate = 1; - } - new_params.flickerControl.coarseJump = - flicker_jumps[new_mains] - [new_params.sensorFps.baserate] - [new_params.sensorFps.divisor]; - if (new_params.flickerControl.flickerMode) - command_flags |= COMMAND_SETFLICKERCTRL; - } - command_flags |= COMMAND_SETSENSORFPS; - cam->exposure_status = EXPOSURE_NORMAL; - } else if (MATCH("stream_start_line")) { - if (!retval) - val = VALUE; - - if (!retval) { - int max_line = 288; - - if (new_params.format.videoSize == VIDEOSIZE_QCIF) - max_line = 144; - if (val <= max_line) - new_params.streamStartLine = val/2; - else - retval = -EINVAL; - } - } else if (MATCH("sub_sample")) { - if (!retval && MATCH("420")) - new_params.format.subSample = SUBSAMPLE_420; - else if (!retval && MATCH("422")) - new_params.format.subSample = SUBSAMPLE_422; - else - retval = -EINVAL; - - command_flags |= COMMAND_SETFORMAT; - } else if (MATCH("yuv_order")) { - if (!retval && MATCH("YUYV")) - new_params.format.yuvOrder = YUVORDER_YUYV; - else if (!retval && MATCH("UYVY")) - new_params.format.yuvOrder = YUVORDER_UYVY; - else - retval = -EINVAL; - - command_flags |= COMMAND_SETFORMAT; - } else if (MATCH("ecp_timing")) { - if (!retval && MATCH("normal")) - new_params.ecpTiming = 0; - else if (!retval && MATCH("slow")) - new_params.ecpTiming = 1; - else - retval = -EINVAL; - - command_flags |= COMMAND_SETECPTIMING; - } else if (MATCH("color_balance_mode")) { - if (!retval && MATCH("manual")) - new_params.colourBalance.balanceMode = 3; - else if (!retval && MATCH("auto")) - new_params.colourBalance.balanceMode = 2; - else - retval = -EINVAL; - - command_flags |= COMMAND_SETCOLOURBALANCE; - } else if (MATCH("red_gain")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 212) { - new_params.colourBalance.redGain = val; - new_params.colourBalance.balanceMode = 1; - } else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOLOURBALANCE; - } else if (MATCH("green_gain")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 212) { - new_params.colourBalance.greenGain = val; - new_params.colourBalance.balanceMode = 1; - } else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOLOURBALANCE; - } else if (MATCH("blue_gain")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 212) { - new_params.colourBalance.blueGain = val; - new_params.colourBalance.balanceMode = 1; - } else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOLOURBALANCE; - } else if (MATCH("max_gain")) { - if (!retval) - val = VALUE; - - if (!retval) { - /* 1-02 firmware limits gain to 2 */ - if (FIRMWARE_VERSION(1,2) && val > 2) - val = 2; - switch(val) { - case 1: - new_params.exposure.gainMode = 1; - break; - case 2: - new_params.exposure.gainMode = 2; - break; - case 4: - new_params.exposure.gainMode = 3; - break; - case 8: - new_params.exposure.gainMode = 4; - break; - default: - retval = -EINVAL; - break; - } - } - command_flags |= COMMAND_SETEXPOSURE; - } else if (MATCH("exposure_mode")) { - if (!retval && MATCH("auto")) - new_params.exposure.expMode = 2; - else if (!retval && MATCH("manual")) { - if (new_params.exposure.expMode == 2) - new_params.exposure.expMode = 3; - if(new_params.flickerControl.flickerMode != 0) - command_flags |= COMMAND_SETFLICKERCTRL; - new_params.flickerControl.flickerMode = 0; - } else - retval = -EINVAL; - - command_flags |= COMMAND_SETEXPOSURE; - } else if (MATCH("centre_weight")) { - if (!retval && MATCH("on")) - new_params.exposure.centreWeight = 1; - else if (!retval && MATCH("off")) - new_params.exposure.centreWeight = 2; - else - retval = -EINVAL; - - command_flags |= COMMAND_SETEXPOSURE; - } else if (MATCH("gain")) { - if (!retval) - val = VALUE; - - if (!retval) { - switch(val) { - case 1: - new_params.exposure.gain = 0; - break; - case 2: - new_params.exposure.gain = 1; - break; - case 4: - new_params.exposure.gain = 2; - break; - case 8: - new_params.exposure.gain = 3; - break; - default: - retval = -EINVAL; - break; - } - new_params.exposure.expMode = 1; - if(new_params.flickerControl.flickerMode != 0) - command_flags |= COMMAND_SETFLICKERCTRL; - new_params.flickerControl.flickerMode = 0; - command_flags |= COMMAND_SETEXPOSURE; - if (new_params.exposure.gain > - new_params.exposure.gainMode-1) - retval = -EINVAL; - } - } else if (MATCH("fine_exp")) { - if (!retval) - val = VALUE/2; - - if (!retval) { - if (val < 256) { - /* 1-02 firmware limits fineExp/2 to 127*/ - if (FIRMWARE_VERSION(1,2) && val > 127) - val = 127; - new_params.exposure.fineExp = val; - new_params.exposure.expMode = 1; - command_flags |= COMMAND_SETEXPOSURE; - if(new_params.flickerControl.flickerMode != 0) - command_flags |= COMMAND_SETFLICKERCTRL; - new_params.flickerControl.flickerMode = 0; - command_flags |= COMMAND_SETFLICKERCTRL; - } else - retval = -EINVAL; - } - } else if (MATCH("coarse_exp")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= MAX_EXP) { - if (FIRMWARE_VERSION(1,2) && - val > MAX_EXP_102) - val = MAX_EXP_102; - new_params.exposure.coarseExpLo = - val & 0xff; - new_params.exposure.coarseExpHi = - val >> 8; - new_params.exposure.expMode = 1; - command_flags |= COMMAND_SETEXPOSURE; - if(new_params.flickerControl.flickerMode != 0) - command_flags |= COMMAND_SETFLICKERCTRL; - new_params.flickerControl.flickerMode = 0; - command_flags |= COMMAND_SETFLICKERCTRL; - } else - retval = -EINVAL; - } - } else if (MATCH("red_comp")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val >= COMP_RED && val <= 255) { - new_params.exposure.redComp = val; - new_params.exposure.compMode = 1; - command_flags |= COMMAND_SETEXPOSURE; - } else - retval = -EINVAL; - } - } else if (MATCH("green1_comp")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val >= COMP_GREEN1 && val <= 255) { - new_params.exposure.green1Comp = val; - new_params.exposure.compMode = 1; - command_flags |= COMMAND_SETEXPOSURE; - } else - retval = -EINVAL; - } - } else if (MATCH("green2_comp")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val >= COMP_GREEN2 && val <= 255) { - new_params.exposure.green2Comp = val; - new_params.exposure.compMode = 1; - command_flags |= COMMAND_SETEXPOSURE; - } else - retval = -EINVAL; - } - } else if (MATCH("blue_comp")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val >= COMP_BLUE && val <= 255) { - new_params.exposure.blueComp = val; - new_params.exposure.compMode = 1; - command_flags |= COMMAND_SETEXPOSURE; - } else - retval = -EINVAL; - } - } else if (MATCH("apcor_gain1")) { - if (!retval) - val = VALUE; - - if (!retval) { - command_flags |= COMMAND_SETAPCOR; - if (val <= 0xff) - new_params.apcor.gain1 = val; - else - retval = -EINVAL; - } - } else if (MATCH("apcor_gain2")) { - if (!retval) - val = VALUE; - - if (!retval) { - command_flags |= COMMAND_SETAPCOR; - if (val <= 0xff) - new_params.apcor.gain2 = val; - else - retval = -EINVAL; - } - } else if (MATCH("apcor_gain4")) { - if (!retval) - val = VALUE; - - if (!retval) { - command_flags |= COMMAND_SETAPCOR; - if (val <= 0xff) - new_params.apcor.gain4 = val; - else - retval = -EINVAL; - } - } else if (MATCH("apcor_gain8")) { - if (!retval) - val = VALUE; - - if (!retval) { - command_flags |= COMMAND_SETAPCOR; - if (val <= 0xff) - new_params.apcor.gain8 = val; - else - retval = -EINVAL; - } - } else if (MATCH("vl_offset_gain1")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.vlOffset.gain1 = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETVLOFFSET; - } else if (MATCH("vl_offset_gain2")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.vlOffset.gain2 = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETVLOFFSET; - } else if (MATCH("vl_offset_gain4")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.vlOffset.gain4 = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETVLOFFSET; - } else if (MATCH("vl_offset_gain8")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.vlOffset.gain8 = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETVLOFFSET; - } else if (MATCH("flicker_control")) { - if (!retval && MATCH("on")) { - set_flicker(&new_params, &command_flags, 1); - } else if (!retval && MATCH("off")) { - set_flicker(&new_params, &command_flags, 0); - } else - retval = -EINVAL; - - command_flags |= COMMAND_SETFLICKERCTRL; - } else if (MATCH("mains_frequency")) { - if (!retval && MATCH("50")) { - new_mains = 0; - new_params.flickerControl.coarseJump = - flicker_jumps[new_mains] - [new_params.sensorFps.baserate] - [new_params.sensorFps.divisor]; - if (new_params.flickerControl.flickerMode) - command_flags |= COMMAND_SETFLICKERCTRL; - } else if (!retval && MATCH("60")) { - new_mains = 1; - new_params.flickerControl.coarseJump = - flicker_jumps[new_mains] - [new_params.sensorFps.baserate] - [new_params.sensorFps.divisor]; - if (new_params.flickerControl.flickerMode) - command_flags |= COMMAND_SETFLICKERCTRL; - } else - retval = -EINVAL; - } else if (MATCH("allowable_overexposure")) { - if (!retval && MATCH("auto")) { - new_params.flickerControl.allowableOverExposure = - -find_over_exposure(new_params.colourParams.brightness); - if(new_params.flickerControl.flickerMode != 0) - command_flags |= COMMAND_SETFLICKERCTRL; - } else { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) { - new_params.flickerControl. - allowableOverExposure = val; - if(new_params.flickerControl.flickerMode != 0) - command_flags |= COMMAND_SETFLICKERCTRL; - } else - retval = -EINVAL; - } - } - } else if (MATCH("compression_mode")) { - if (!retval && MATCH("none")) - new_params.compression.mode = - CPIA_COMPRESSION_NONE; - else if (!retval && MATCH("auto")) - new_params.compression.mode = - CPIA_COMPRESSION_AUTO; - else if (!retval && MATCH("manual")) - new_params.compression.mode = - CPIA_COMPRESSION_MANUAL; - else - retval = -EINVAL; - - command_flags |= COMMAND_SETCOMPRESSION; - } else if (MATCH("decimation_enable")) { - if (!retval && MATCH("off")) - new_params.compression.decimation = 0; - else if (!retval && MATCH("on")) - new_params.compression.decimation = 1; - else - retval = -EINVAL; - - command_flags |= COMMAND_SETCOMPRESSION; - } else if (MATCH("compression_target")) { - if (!retval && MATCH("quality")) - new_params.compressionTarget.frTargeting = - CPIA_COMPRESSION_TARGET_QUALITY; - else if (!retval && MATCH("framerate")) - new_params.compressionTarget.frTargeting = - CPIA_COMPRESSION_TARGET_FRAMERATE; - else - retval = -EINVAL; - - command_flags |= COMMAND_SETCOMPRESSIONTARGET; - } else if (MATCH("target_framerate")) { - if (!retval) - val = VALUE; - - if (!retval) { - if(val > 0 && val <= 30) - new_params.compressionTarget.targetFR = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOMPRESSIONTARGET; - } else if (MATCH("target_quality")) { - if (!retval) - val = VALUE; - - if (!retval) { - if(val > 0 && val <= 64) - new_params.compressionTarget.targetQ = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOMPRESSIONTARGET; - } else if (MATCH("y_threshold")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val < 32) - new_params.yuvThreshold.yThreshold = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETYUVTHRESH; - } else if (MATCH("uv_threshold")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val < 32) - new_params.yuvThreshold.uvThreshold = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETYUVTHRESH; - } else if (MATCH("hysteresis")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.compressionParams.hysteresis = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - } else if (MATCH("threshold_max")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.compressionParams.threshMax = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - } else if (MATCH("small_step")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.compressionParams.smallStep = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - } else if (MATCH("large_step")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.compressionParams.largeStep = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - } else if (MATCH("decimation_hysteresis")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.compressionParams.decimationHysteresis = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - } else if (MATCH("fr_diff_step_thresh")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.compressionParams.frDiffStepThresh = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - } else if (MATCH("q_diff_step_thresh")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.compressionParams.qDiffStepThresh = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - } else if (MATCH("decimation_thresh_mod")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - new_params.compressionParams.decimationThreshMod = val; - else - retval = -EINVAL; - } - command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - } else if (MATCH("toplight")) { - if (!retval && MATCH("on")) - new_params.qx3.toplight = 1; - else if (!retval && MATCH("off")) - new_params.qx3.toplight = 0; - else - retval = -EINVAL; - command_flags |= COMMAND_SETLIGHTS; - } else if (MATCH("bottomlight")) { - if (!retval && MATCH("on")) - new_params.qx3.bottomlight = 1; - else if (!retval && MATCH("off")) - new_params.qx3.bottomlight = 0; - else - retval = -EINVAL; - command_flags |= COMMAND_SETLIGHTS; - } else { - DBG("No match found\n"); - retval = -EINVAL; - } - - if (!retval) { - while (count && isspace(*buffer) && *buffer != '\n') { - --count; - ++buffer; - } - if (count) { - if (*buffer == '\0' && count != 1) - retval = -EINVAL; - else if (*buffer != '\n' && *buffer != ';' && - *buffer != '\0') - retval = -EINVAL; - else { - --count; - ++buffer; - } - } - } - } -#undef MATCH -#undef VALUE -#undef FIRMWARE_VERSION - if (!retval) { - if (command_flags & COMMAND_SETCOLOURPARAMS) { - /* Adjust cam->vp to reflect these changes */ - cam->vp.brightness = - new_params.colourParams.brightness*65535/100; - cam->vp.contrast = - new_params.colourParams.contrast*65535/100; - cam->vp.colour = - new_params.colourParams.saturation*65535/100; - } - if((command_flags & COMMAND_SETEXPOSURE) && - new_params.exposure.expMode == 2) - cam->exposure_status = EXPOSURE_NORMAL; - - memcpy(&cam->params, &new_params, sizeof(struct cam_params)); - cam->mainsFreq = new_mains; - cam->cmd_queue |= command_flags; - retval = size; - } else - DBG("error: %d\n", retval); - - mutex_unlock(&cam->param_lock); - -out: - free_page((unsigned long)page); - return retval; -} - -static const struct file_operations cpia_proc_fops = { - .owner = THIS_MODULE, - .open = cpia_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .write = cpia_proc_write, -}; - -static void create_proc_cpia_cam(struct cam_data *cam) -{ - struct proc_dir_entry *ent; - - if (!cpia_proc_root || !cam) - return; - - ent = proc_create_data(video_device_node_name(&cam->vdev), - S_IRUGO|S_IWUSR, cpia_proc_root, - &cpia_proc_fops, cam); - if (!ent) - return; - - /* - size of the proc entry is 3736 bytes for the standard webcam; - the extra features of the QX3 microscope add 189 bytes. - (we have not yet probed the camera to see which type it is). - */ - ent->size = 3736 + 189; - cam->proc_entry = ent; -} - -static void destroy_proc_cpia_cam(struct cam_data *cam) -{ - if (!cam || !cam->proc_entry) - return; - - remove_proc_entry(video_device_node_name(&cam->vdev), cpia_proc_root); - cam->proc_entry = NULL; -} - -static void proc_cpia_create(void) -{ - cpia_proc_root = proc_mkdir("cpia", NULL); - - if (!cpia_proc_root) - LOG("Unable to initialise /proc/cpia\n"); -} - -static void __exit proc_cpia_destroy(void) -{ - remove_proc_entry("cpia", NULL); -} -#endif /* CONFIG_PROC_FS */ - -/* ----------------------- debug functions ---------------------- */ - -#define printstatus(cam) \ - DBG("%02x %02x %02x %02x %02x %02x %02x %02x\n",\ - cam->params.status.systemState, cam->params.status.grabState, \ - cam->params.status.streamState, cam->params.status.fatalError, \ - cam->params.status.cmdError, cam->params.status.debugFlags, \ - cam->params.status.vpStatus, cam->params.status.errorCode); - -/* ----------------------- v4l helpers -------------------------- */ - -/* supported frame palettes and depths */ -static inline int valid_mode(u16 palette, u16 depth) -{ - if ((palette == VIDEO_PALETTE_YUV422 && depth == 16) || - (palette == VIDEO_PALETTE_YUYV && depth == 16)) - return 1; - - if (colorspace_conv) - return (palette == VIDEO_PALETTE_GREY && depth == 8) || - (palette == VIDEO_PALETTE_RGB555 && depth == 16) || - (palette == VIDEO_PALETTE_RGB565 && depth == 16) || - (palette == VIDEO_PALETTE_RGB24 && depth == 24) || - (palette == VIDEO_PALETTE_RGB32 && depth == 32) || - (palette == VIDEO_PALETTE_UYVY && depth == 16); - - return 0; -} - -static int match_videosize( int width, int height ) -{ - /* return the best match, where 'best' is as always - * the largest that is not bigger than what is requested. */ - if (width>=352 && height>=288) - return VIDEOSIZE_352_288; /* CIF */ - - if (width>=320 && height>=240) - return VIDEOSIZE_320_240; /* SIF */ - - if (width>=288 && height>=216) - return VIDEOSIZE_288_216; - - if (width>=256 && height>=192) - return VIDEOSIZE_256_192; - - if (width>=224 && height>=168) - return VIDEOSIZE_224_168; - - if (width>=192 && height>=144) - return VIDEOSIZE_192_144; - - if (width>=176 && height>=144) - return VIDEOSIZE_176_144; /* QCIF */ - - if (width>=160 && height>=120) - return VIDEOSIZE_160_120; /* QSIF */ - - if (width>=128 && height>=96) - return VIDEOSIZE_128_96; - - if (width>=88 && height>=72) - return VIDEOSIZE_88_72; - - if (width>=64 && height>=48) - return VIDEOSIZE_64_48; - - if (width>=48 && height>=48) - return VIDEOSIZE_48_48; - - return -1; -} - -/* these are the capture sizes we support */ -static void set_vw_size(struct cam_data *cam) -{ - /* the col/row/start/end values are the result of simple math */ - /* study the SetROI-command in cpia developers guide p 2-22 */ - /* streamStartLine is set to the recommended value in the cpia */ - /* developers guide p 3-37 */ - switch(cam->video_size) { - case VIDEOSIZE_CIF: - cam->vw.width = 352; - cam->vw.height = 288; - cam->params.format.videoSize=VIDEOSIZE_CIF; - cam->params.roi.colStart=0; - cam->params.roi.rowStart=0; - cam->params.streamStartLine = 120; - break; - case VIDEOSIZE_SIF: - cam->vw.width = 320; - cam->vw.height = 240; - cam->params.format.videoSize=VIDEOSIZE_CIF; - cam->params.roi.colStart=2; - cam->params.roi.rowStart=6; - cam->params.streamStartLine = 120; - break; - case VIDEOSIZE_288_216: - cam->vw.width = 288; - cam->vw.height = 216; - cam->params.format.videoSize=VIDEOSIZE_CIF; - cam->params.roi.colStart=4; - cam->params.roi.rowStart=9; - cam->params.streamStartLine = 120; - break; - case VIDEOSIZE_256_192: - cam->vw.width = 256; - cam->vw.height = 192; - cam->params.format.videoSize=VIDEOSIZE_CIF; - cam->params.roi.colStart=6; - cam->params.roi.rowStart=12; - cam->params.streamStartLine = 120; - break; - case VIDEOSIZE_224_168: - cam->vw.width = 224; - cam->vw.height = 168; - cam->params.format.videoSize=VIDEOSIZE_CIF; - cam->params.roi.colStart=8; - cam->params.roi.rowStart=15; - cam->params.streamStartLine = 120; - break; - case VIDEOSIZE_192_144: - cam->vw.width = 192; - cam->vw.height = 144; - cam->params.format.videoSize=VIDEOSIZE_CIF; - cam->params.roi.colStart=10; - cam->params.roi.rowStart=18; - cam->params.streamStartLine = 120; - break; - case VIDEOSIZE_QCIF: - cam->vw.width = 176; - cam->vw.height = 144; - cam->params.format.videoSize=VIDEOSIZE_QCIF; - cam->params.roi.colStart=0; - cam->params.roi.rowStart=0; - cam->params.streamStartLine = 60; - break; - case VIDEOSIZE_QSIF: - cam->vw.width = 160; - cam->vw.height = 120; - cam->params.format.videoSize=VIDEOSIZE_QCIF; - cam->params.roi.colStart=1; - cam->params.roi.rowStart=3; - cam->params.streamStartLine = 60; - break; - case VIDEOSIZE_128_96: - cam->vw.width = 128; - cam->vw.height = 96; - cam->params.format.videoSize=VIDEOSIZE_QCIF; - cam->params.roi.colStart=3; - cam->params.roi.rowStart=6; - cam->params.streamStartLine = 60; - break; - case VIDEOSIZE_88_72: - cam->vw.width = 88; - cam->vw.height = 72; - cam->params.format.videoSize=VIDEOSIZE_QCIF; - cam->params.roi.colStart=5; - cam->params.roi.rowStart=9; - cam->params.streamStartLine = 60; - break; - case VIDEOSIZE_64_48: - cam->vw.width = 64; - cam->vw.height = 48; - cam->params.format.videoSize=VIDEOSIZE_QCIF; - cam->params.roi.colStart=7; - cam->params.roi.rowStart=12; - cam->params.streamStartLine = 60; - break; - case VIDEOSIZE_48_48: - cam->vw.width = 48; - cam->vw.height = 48; - cam->params.format.videoSize=VIDEOSIZE_QCIF; - cam->params.roi.colStart=8; - cam->params.roi.rowStart=6; - cam->params.streamStartLine = 60; - break; - default: - LOG("bad videosize value: %d\n", cam->video_size); - return; - } - - if(cam->vc.width == 0) - cam->vc.width = cam->vw.width; - if(cam->vc.height == 0) - cam->vc.height = cam->vw.height; - - cam->params.roi.colStart += cam->vc.x >> 3; - cam->params.roi.colEnd = cam->params.roi.colStart + - (cam->vc.width >> 3); - cam->params.roi.rowStart += cam->vc.y >> 2; - cam->params.roi.rowEnd = cam->params.roi.rowStart + - (cam->vc.height >> 2); - - return; -} - -static int allocate_frame_buf(struct cam_data *cam) -{ - int i; - - cam->frame_buf = rvmalloc(FRAME_NUM * CPIA_MAX_FRAME_SIZE); - if (!cam->frame_buf) - return -ENOBUFS; - - for (i = 0; i < FRAME_NUM; i++) - cam->frame[i].data = cam->frame_buf + i * CPIA_MAX_FRAME_SIZE; - - return 0; -} - -static int free_frame_buf(struct cam_data *cam) -{ - int i; - - rvfree(cam->frame_buf, FRAME_NUM*CPIA_MAX_FRAME_SIZE); - cam->frame_buf = NULL; - for (i=0; i < FRAME_NUM; i++) - cam->frame[i].data = NULL; - - return 0; -} - - -static inline void free_frames(struct cpia_frame frame[FRAME_NUM]) -{ - int i; - - for (i=0; i < FRAME_NUM; i++) - frame[i].state = FRAME_UNUSED; - return; -} - -/********************************************************************** - * - * General functions - * - **********************************************************************/ -/* send an arbitrary command to the camera */ -static int do_command(struct cam_data *cam, u16 command, u8 a, u8 b, u8 c, u8 d) -{ - int retval, datasize; - u8 cmd[8], data[8]; - - switch(command) { - case CPIA_COMMAND_GetCPIAVersion: - case CPIA_COMMAND_GetPnPID: - case CPIA_COMMAND_GetCameraStatus: - case CPIA_COMMAND_GetVPVersion: - datasize=8; - break; - case CPIA_COMMAND_GetColourParams: - case CPIA_COMMAND_GetColourBalance: - case CPIA_COMMAND_GetExposure: - mutex_lock(&cam->param_lock); - datasize=8; - break; - case CPIA_COMMAND_ReadMCPorts: - case CPIA_COMMAND_ReadVCRegs: - datasize = 4; - break; - default: - datasize=0; - break; - } - - cmd[0] = command>>8; - cmd[1] = command&0xff; - cmd[2] = a; - cmd[3] = b; - cmd[4] = c; - cmd[5] = d; - cmd[6] = datasize; - cmd[7] = 0; - - retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data); - if (retval) { - DBG("%x - failed, retval=%d\n", command, retval); - if (command == CPIA_COMMAND_GetColourParams || - command == CPIA_COMMAND_GetColourBalance || - command == CPIA_COMMAND_GetExposure) - mutex_unlock(&cam->param_lock); - } else { - switch(command) { - case CPIA_COMMAND_GetCPIAVersion: - cam->params.version.firmwareVersion = data[0]; - cam->params.version.firmwareRevision = data[1]; - cam->params.version.vcVersion = data[2]; - cam->params.version.vcRevision = data[3]; - break; - case CPIA_COMMAND_GetPnPID: - cam->params.pnpID.vendor = data[0]+(((u16)data[1])<<8); - cam->params.pnpID.product = data[2]+(((u16)data[3])<<8); - cam->params.pnpID.deviceRevision = - data[4]+(((u16)data[5])<<8); - break; - case CPIA_COMMAND_GetCameraStatus: - cam->params.status.systemState = data[0]; - cam->params.status.grabState = data[1]; - cam->params.status.streamState = data[2]; - cam->params.status.fatalError = data[3]; - cam->params.status.cmdError = data[4]; - cam->params.status.debugFlags = data[5]; - cam->params.status.vpStatus = data[6]; - cam->params.status.errorCode = data[7]; - break; - case CPIA_COMMAND_GetVPVersion: - cam->params.vpVersion.vpVersion = data[0]; - cam->params.vpVersion.vpRevision = data[1]; - cam->params.vpVersion.cameraHeadID = - data[2]+(((u16)data[3])<<8); - break; - case CPIA_COMMAND_GetColourParams: - cam->params.colourParams.brightness = data[0]; - cam->params.colourParams.contrast = data[1]; - cam->params.colourParams.saturation = data[2]; - mutex_unlock(&cam->param_lock); - break; - case CPIA_COMMAND_GetColourBalance: - cam->params.colourBalance.redGain = data[0]; - cam->params.colourBalance.greenGain = data[1]; - cam->params.colourBalance.blueGain = data[2]; - mutex_unlock(&cam->param_lock); - break; - case CPIA_COMMAND_GetExposure: - cam->params.exposure.gain = data[0]; - cam->params.exposure.fineExp = data[1]; - cam->params.exposure.coarseExpLo = data[2]; - cam->params.exposure.coarseExpHi = data[3]; - cam->params.exposure.redComp = data[4]; - cam->params.exposure.green1Comp = data[5]; - cam->params.exposure.green2Comp = data[6]; - cam->params.exposure.blueComp = data[7]; - mutex_unlock(&cam->param_lock); - break; - - case CPIA_COMMAND_ReadMCPorts: - if (!cam->params.qx3.qx3_detected) - break; - /* test button press */ - cam->params.qx3.button = ((data[1] & 0x02) == 0); - if (cam->params.qx3.button) { - /* button pressed - unlock the latch */ - do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xDF,0xDF,0); - do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xFF,0xFF,0); - } - - /* test whether microscope is cradled */ - cam->params.qx3.cradled = ((data[2] & 0x40) == 0); - break; - - default: - break; - } - } - return retval; -} - -/* send a command to the camera with an additional data transaction */ -static int do_command_extended(struct cam_data *cam, u16 command, - u8 a, u8 b, u8 c, u8 d, - u8 e, u8 f, u8 g, u8 h, - u8 i, u8 j, u8 k, u8 l) -{ - int retval; - u8 cmd[8], data[8]; - - cmd[0] = command>>8; - cmd[1] = command&0xff; - cmd[2] = a; - cmd[3] = b; - cmd[4] = c; - cmd[5] = d; - cmd[6] = 8; - cmd[7] = 0; - data[0] = e; - data[1] = f; - data[2] = g; - data[3] = h; - data[4] = i; - data[5] = j; - data[6] = k; - data[7] = l; - - retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data); - if (retval) - DBG("%x - failed\n", command); - - return retval; -} - -/********************************************************************** - * - * Colorspace conversion - * - **********************************************************************/ -#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16) - -static int convert420(unsigned char *yuv, unsigned char *rgb, int out_fmt, - int linesize, int mmap_kludge) -{ - int y, u, v, r, g, b, y1; - - /* Odd lines use the same u and v as the previous line. - * Because of compression, it is necessary to get this - * information from the decoded image. */ - switch(out_fmt) { - case VIDEO_PALETTE_RGB555: - y = (*yuv++ - 16) * 76310; - y1 = (*yuv - 16) * 76310; - r = ((*(rgb+1-linesize)) & 0x7c) << 1; - g = ((*(rgb-linesize)) & 0xe0) >> 4 | - ((*(rgb+1-linesize)) & 0x03) << 6; - b = ((*(rgb-linesize)) & 0x1f) << 3; - u = (-53294 * r - 104635 * g + 157929 * b) / 5756495; - v = (157968 * r - 132278 * g - 25690 * b) / 5366159; - r = 104635 * v; - g = -25690 * u - 53294 * v; - b = 132278 * u; - *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3); - *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6); - *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3); - *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6); - return 4; - case VIDEO_PALETTE_RGB565: - y = (*yuv++ - 16) * 76310; - y1 = (*yuv - 16) * 76310; - r = (*(rgb+1-linesize)) & 0xf8; - g = ((*(rgb-linesize)) & 0xe0) >> 3 | - ((*(rgb+1-linesize)) & 0x07) << 5; - b = ((*(rgb-linesize)) & 0x1f) << 3; - u = (-53294 * r - 104635 * g + 157929 * b) / 5756495; - v = (157968 * r - 132278 * g - 25690 * b) / 5366159; - r = 104635 * v; - g = -25690 * u - 53294 * v; - b = 132278 * u; - *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3); - *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5); - *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3); - *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5); - return 4; - break; - case VIDEO_PALETTE_RGB24: - case VIDEO_PALETTE_RGB32: - y = (*yuv++ - 16) * 76310; - y1 = (*yuv - 16) * 76310; - if (mmap_kludge) { - r = *(rgb+2-linesize); - g = *(rgb+1-linesize); - b = *(rgb-linesize); - } else { - r = *(rgb-linesize); - g = *(rgb+1-linesize); - b = *(rgb+2-linesize); - } - u = (-53294 * r - 104635 * g + 157929 * b) / 5756495; - v = (157968 * r - 132278 * g - 25690 * b) / 5366159; - r = 104635 * v; - g = -25690 * u + -53294 * v; - b = 132278 * u; - if (mmap_kludge) { - *rgb++ = LIMIT(b+y); - *rgb++ = LIMIT(g+y); - *rgb++ = LIMIT(r+y); - if(out_fmt == VIDEO_PALETTE_RGB32) - rgb++; - *rgb++ = LIMIT(b+y1); - *rgb++ = LIMIT(g+y1); - *rgb = LIMIT(r+y1); - } else { - *rgb++ = LIMIT(r+y); - *rgb++ = LIMIT(g+y); - *rgb++ = LIMIT(b+y); - if(out_fmt == VIDEO_PALETTE_RGB32) - rgb++; - *rgb++ = LIMIT(r+y1); - *rgb++ = LIMIT(g+y1); - *rgb = LIMIT(b+y1); - } - if(out_fmt == VIDEO_PALETTE_RGB32) - return 8; - return 6; - case VIDEO_PALETTE_YUV422: - case VIDEO_PALETTE_YUYV: - y = *yuv++; - u = *(rgb+1-linesize); - y1 = *yuv; - v = *(rgb+3-linesize); - *rgb++ = y; - *rgb++ = u; - *rgb++ = y1; - *rgb = v; - return 4; - case VIDEO_PALETTE_UYVY: - u = *(rgb-linesize); - y = *yuv++; - v = *(rgb+2-linesize); - y1 = *yuv; - *rgb++ = u; - *rgb++ = y; - *rgb++ = v; - *rgb = y1; - return 4; - case VIDEO_PALETTE_GREY: - *rgb++ = *yuv++; - *rgb = *yuv; - return 2; - default: - DBG("Empty: %d\n", out_fmt); - return 0; - } -} - - -static int yuvconvert(unsigned char *yuv, unsigned char *rgb, int out_fmt, - int in_uyvy, int mmap_kludge) -{ - int y, u, v, r, g, b, y1; - - switch(out_fmt) { - case VIDEO_PALETTE_RGB555: - case VIDEO_PALETTE_RGB565: - case VIDEO_PALETTE_RGB24: - case VIDEO_PALETTE_RGB32: - if (in_uyvy) { - u = *yuv++ - 128; - y = (*yuv++ - 16) * 76310; - v = *yuv++ - 128; - y1 = (*yuv - 16) * 76310; - } else { - y = (*yuv++ - 16) * 76310; - u = *yuv++ - 128; - y1 = (*yuv++ - 16) * 76310; - v = *yuv - 128; - } - r = 104635 * v; - g = -25690 * u + -53294 * v; - b = 132278 * u; - break; - default: - y = *yuv++; - u = *yuv++; - y1 = *yuv++; - v = *yuv; - /* Just to avoid compiler warnings */ - r = 0; - g = 0; - b = 0; - break; - } - switch(out_fmt) { - case VIDEO_PALETTE_RGB555: - *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3); - *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6); - *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3); - *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6); - return 4; - case VIDEO_PALETTE_RGB565: - *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3); - *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5); - *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3); - *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5); - return 4; - case VIDEO_PALETTE_RGB24: - if (mmap_kludge) { - *rgb++ = LIMIT(b+y); - *rgb++ = LIMIT(g+y); - *rgb++ = LIMIT(r+y); - *rgb++ = LIMIT(b+y1); - *rgb++ = LIMIT(g+y1); - *rgb = LIMIT(r+y1); - } else { - *rgb++ = LIMIT(r+y); - *rgb++ = LIMIT(g+y); - *rgb++ = LIMIT(b+y); - *rgb++ = LIMIT(r+y1); - *rgb++ = LIMIT(g+y1); - *rgb = LIMIT(b+y1); - } - return 6; - case VIDEO_PALETTE_RGB32: - if (mmap_kludge) { - *rgb++ = LIMIT(b+y); - *rgb++ = LIMIT(g+y); - *rgb++ = LIMIT(r+y); - rgb++; - *rgb++ = LIMIT(b+y1); - *rgb++ = LIMIT(g+y1); - *rgb = LIMIT(r+y1); - } else { - *rgb++ = LIMIT(r+y); - *rgb++ = LIMIT(g+y); - *rgb++ = LIMIT(b+y); - rgb++; - *rgb++ = LIMIT(r+y1); - *rgb++ = LIMIT(g+y1); - *rgb = LIMIT(b+y1); - } - return 8; - case VIDEO_PALETTE_GREY: - *rgb++ = y; - *rgb = y1; - return 2; - case VIDEO_PALETTE_YUV422: - case VIDEO_PALETTE_YUYV: - *rgb++ = y; - *rgb++ = u; - *rgb++ = y1; - *rgb = v; - return 4; - case VIDEO_PALETTE_UYVY: - *rgb++ = u; - *rgb++ = y; - *rgb++ = v; - *rgb = y1; - return 4; - default: - DBG("Empty: %d\n", out_fmt); - return 0; - } -} - -static int skipcount(int count, int fmt) -{ - switch(fmt) { - case VIDEO_PALETTE_GREY: - return count; - case VIDEO_PALETTE_RGB555: - case VIDEO_PALETTE_RGB565: - case VIDEO_PALETTE_YUV422: - case VIDEO_PALETTE_YUYV: - case VIDEO_PALETTE_UYVY: - return 2*count; - case VIDEO_PALETTE_RGB24: - return 3*count; - case VIDEO_PALETTE_RGB32: - return 4*count; - default: - return 0; - } -} - -static int parse_picture(struct cam_data *cam, int size) -{ - u8 *obuf, *ibuf, *end_obuf; - int ll, in_uyvy, compressed, decimation, even_line, origsize, out_fmt; - int rows, cols, linesize, subsample_422; - - /* make sure params don't change while we are decoding */ - mutex_lock(&cam->param_lock); - - obuf = cam->decompressed_frame.data; - end_obuf = obuf+CPIA_MAX_FRAME_SIZE; - ibuf = cam->raw_image; - origsize = size; - out_fmt = cam->vp.palette; - - if ((ibuf[0] != MAGIC_0) || (ibuf[1] != MAGIC_1)) { - LOG("header not found\n"); - mutex_unlock(&cam->param_lock); - return -1; - } - - if ((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) { - LOG("wrong video size\n"); - mutex_unlock(&cam->param_lock); - return -1; - } - - if (ibuf[17] != SUBSAMPLE_420 && ibuf[17] != SUBSAMPLE_422) { - LOG("illegal subtype %d\n",ibuf[17]); - mutex_unlock(&cam->param_lock); - return -1; - } - subsample_422 = ibuf[17] == SUBSAMPLE_422; - - if (ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) { - LOG("illegal yuvorder %d\n",ibuf[18]); - mutex_unlock(&cam->param_lock); - return -1; - } - in_uyvy = ibuf[18] == YUVORDER_UYVY; - - if ((ibuf[24] != cam->params.roi.colStart) || - (ibuf[25] != cam->params.roi.colEnd) || - (ibuf[26] != cam->params.roi.rowStart) || - (ibuf[27] != cam->params.roi.rowEnd)) { - LOG("ROI mismatch\n"); - mutex_unlock(&cam->param_lock); - return -1; - } - cols = 8*(ibuf[25] - ibuf[24]); - rows = 4*(ibuf[27] - ibuf[26]); - - - if ((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) { - LOG("illegal compression %d\n",ibuf[28]); - mutex_unlock(&cam->param_lock); - return -1; - } - compressed = (ibuf[28] == COMPRESSED); - - if (ibuf[29] != NO_DECIMATION && ibuf[29] != DECIMATION_ENAB) { - LOG("illegal decimation %d\n",ibuf[29]); - mutex_unlock(&cam->param_lock); - return -1; - } - decimation = (ibuf[29] == DECIMATION_ENAB); - - cam->params.yuvThreshold.yThreshold = ibuf[30]; - cam->params.yuvThreshold.uvThreshold = ibuf[31]; - cam->params.status.systemState = ibuf[32]; - cam->params.status.grabState = ibuf[33]; - cam->params.status.streamState = ibuf[34]; - cam->params.status.fatalError = ibuf[35]; - cam->params.status.cmdError = ibuf[36]; - cam->params.status.debugFlags = ibuf[37]; - cam->params.status.vpStatus = ibuf[38]; - cam->params.status.errorCode = ibuf[39]; - cam->fps = ibuf[41]; - mutex_unlock(&cam->param_lock); - - linesize = skipcount(cols, out_fmt); - ibuf += FRAME_HEADER_SIZE; - size -= FRAME_HEADER_SIZE; - ll = ibuf[0] | (ibuf[1] << 8); - ibuf += 2; - even_line = 1; - - while (size > 0) { - size -= (ll+2); - if (size < 0) { - LOG("Insufficient data in buffer\n"); - return -1; - } - - while (ll > 1) { - if (!compressed || (compressed && !(*ibuf & 1))) { - if(subsample_422 || even_line) { - obuf += yuvconvert(ibuf, obuf, out_fmt, - in_uyvy, cam->mmap_kludge); - ibuf += 4; - ll -= 4; - } else { - /* SUBSAMPLE_420 on an odd line */ - obuf += convert420(ibuf, obuf, - out_fmt, linesize, - cam->mmap_kludge); - ibuf += 2; - ll -= 2; - } - } else { - /*skip compressed interval from previous frame*/ - obuf += skipcount(*ibuf >> 1, out_fmt); - if (obuf > end_obuf) { - LOG("Insufficient buffer size\n"); - return -1; - } - ++ibuf; - ll--; - } - } - if (ll == 1) { - if (*ibuf != EOL) { - DBG("EOL not found giving up after %d/%d" - " bytes\n", origsize-size, origsize); - return -1; - } - - ++ibuf; /* skip over EOL */ - - if ((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) && - (ibuf[2] == EOI) && (ibuf[3] == EOI)) { - size -= 4; - break; - } - - if(decimation) { - /* skip the odd lines for now */ - obuf += linesize; - } - - if (size > 1) { - ll = ibuf[0] | (ibuf[1] << 8); - ibuf += 2; /* skip over line length */ - } - if(!decimation) - even_line = !even_line; - } else { - LOG("line length was not 1 but %d after %d/%d bytes\n", - ll, origsize-size, origsize); - return -1; - } - } - - if(decimation) { - /* interpolate odd rows */ - int i, j; - u8 *prev, *next; - prev = cam->decompressed_frame.data; - obuf = prev+linesize; - next = obuf+linesize; - for(i=1; idecompressed_frame.count = obuf-cam->decompressed_frame.data; - - return cam->decompressed_frame.count; -} - -/* InitStreamCap wrapper to select correct start line */ -static inline int init_stream_cap(struct cam_data *cam) -{ - return do_command(cam, CPIA_COMMAND_InitStreamCap, - 0, cam->params.streamStartLine, 0, 0); -} - - -/* find_over_exposure - * Finds a suitable value of OverExposure for use with SetFlickerCtrl - * Some calculation is required because this value changes with the brightness - * set with SetColourParameters - * - * Parameters: Brightness - last brightness value set with SetColourParameters - * - * Returns: OverExposure value to use with SetFlickerCtrl - */ -#define FLICKER_MAX_EXPOSURE 250 -#define FLICKER_ALLOWABLE_OVER_EXPOSURE 146 -#define FLICKER_BRIGHTNESS_CONSTANT 59 -static int find_over_exposure(int brightness) -{ - int MaxAllowableOverExposure, OverExposure; - - MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness - - FLICKER_BRIGHTNESS_CONSTANT; - - if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE) { - OverExposure = MaxAllowableOverExposure; - } else { - OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE; - } - - return OverExposure; -} -#undef FLICKER_MAX_EXPOSURE -#undef FLICKER_ALLOWABLE_OVER_EXPOSURE -#undef FLICKER_BRIGHTNESS_CONSTANT - -/* update various camera modes and settings */ -static void dispatch_commands(struct cam_data *cam) -{ - mutex_lock(&cam->param_lock); - if (cam->cmd_queue==COMMAND_NONE) { - mutex_unlock(&cam->param_lock); - return; - } - DEB_BYTE(cam->cmd_queue); - DEB_BYTE(cam->cmd_queue>>8); - if (cam->cmd_queue & COMMAND_SETFORMAT) { - do_command(cam, CPIA_COMMAND_SetFormat, - cam->params.format.videoSize, - cam->params.format.subSample, - cam->params.format.yuvOrder, 0); - do_command(cam, CPIA_COMMAND_SetROI, - cam->params.roi.colStart, cam->params.roi.colEnd, - cam->params.roi.rowStart, cam->params.roi.rowEnd); - cam->first_frame = 1; - } - - if (cam->cmd_queue & COMMAND_SETCOLOURPARAMS) - do_command(cam, CPIA_COMMAND_SetColourParams, - cam->params.colourParams.brightness, - cam->params.colourParams.contrast, - cam->params.colourParams.saturation, 0); - - if (cam->cmd_queue & COMMAND_SETAPCOR) - do_command(cam, CPIA_COMMAND_SetApcor, - cam->params.apcor.gain1, - cam->params.apcor.gain2, - cam->params.apcor.gain4, - cam->params.apcor.gain8); - - if (cam->cmd_queue & COMMAND_SETVLOFFSET) - do_command(cam, CPIA_COMMAND_SetVLOffset, - cam->params.vlOffset.gain1, - cam->params.vlOffset.gain2, - cam->params.vlOffset.gain4, - cam->params.vlOffset.gain8); - - if (cam->cmd_queue & COMMAND_SETEXPOSURE) { - do_command_extended(cam, CPIA_COMMAND_SetExposure, - cam->params.exposure.gainMode, - 1, - cam->params.exposure.compMode, - cam->params.exposure.centreWeight, - cam->params.exposure.gain, - cam->params.exposure.fineExp, - cam->params.exposure.coarseExpLo, - cam->params.exposure.coarseExpHi, - cam->params.exposure.redComp, - cam->params.exposure.green1Comp, - cam->params.exposure.green2Comp, - cam->params.exposure.blueComp); - if(cam->params.exposure.expMode != 1) { - do_command_extended(cam, CPIA_COMMAND_SetExposure, - 0, - cam->params.exposure.expMode, - 0, 0, - cam->params.exposure.gain, - cam->params.exposure.fineExp, - cam->params.exposure.coarseExpLo, - cam->params.exposure.coarseExpHi, - 0, 0, 0, 0); - } - } - - if (cam->cmd_queue & COMMAND_SETCOLOURBALANCE) { - if (cam->params.colourBalance.balanceMode == 1) { - do_command(cam, CPIA_COMMAND_SetColourBalance, - 1, - cam->params.colourBalance.redGain, - cam->params.colourBalance.greenGain, - cam->params.colourBalance.blueGain); - do_command(cam, CPIA_COMMAND_SetColourBalance, - 3, 0, 0, 0); - } - if (cam->params.colourBalance.balanceMode == 2) { - do_command(cam, CPIA_COMMAND_SetColourBalance, - 2, 0, 0, 0); - } - if (cam->params.colourBalance.balanceMode == 3) { - do_command(cam, CPIA_COMMAND_SetColourBalance, - 3, 0, 0, 0); - } - } - - if (cam->cmd_queue & COMMAND_SETCOMPRESSIONTARGET) - do_command(cam, CPIA_COMMAND_SetCompressionTarget, - cam->params.compressionTarget.frTargeting, - cam->params.compressionTarget.targetFR, - cam->params.compressionTarget.targetQ, 0); - - if (cam->cmd_queue & COMMAND_SETYUVTHRESH) - do_command(cam, CPIA_COMMAND_SetYUVThresh, - cam->params.yuvThreshold.yThreshold, - cam->params.yuvThreshold.uvThreshold, 0, 0); - - if (cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS) - do_command_extended(cam, CPIA_COMMAND_SetCompressionParams, - 0, 0, 0, 0, - cam->params.compressionParams.hysteresis, - cam->params.compressionParams.threshMax, - cam->params.compressionParams.smallStep, - cam->params.compressionParams.largeStep, - cam->params.compressionParams.decimationHysteresis, - cam->params.compressionParams.frDiffStepThresh, - cam->params.compressionParams.qDiffStepThresh, - cam->params.compressionParams.decimationThreshMod); - - if (cam->cmd_queue & COMMAND_SETCOMPRESSION) - do_command(cam, CPIA_COMMAND_SetCompression, - cam->params.compression.mode, - cam->params.compression.decimation, 0, 0); - - if (cam->cmd_queue & COMMAND_SETSENSORFPS) - do_command(cam, CPIA_COMMAND_SetSensorFPS, - cam->params.sensorFps.divisor, - cam->params.sensorFps.baserate, 0, 0); - - if (cam->cmd_queue & COMMAND_SETFLICKERCTRL) - do_command(cam, CPIA_COMMAND_SetFlickerCtrl, - cam->params.flickerControl.flickerMode, - cam->params.flickerControl.coarseJump, - abs(cam->params.flickerControl.allowableOverExposure), - 0); - - if (cam->cmd_queue & COMMAND_SETECPTIMING) - do_command(cam, CPIA_COMMAND_SetECPTiming, - cam->params.ecpTiming, 0, 0, 0); - - if (cam->cmd_queue & COMMAND_PAUSE) - do_command(cam, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0); - - if (cam->cmd_queue & COMMAND_RESUME) - init_stream_cap(cam); - - if (cam->cmd_queue & COMMAND_SETLIGHTS && cam->params.qx3.qx3_detected) - { - int p1 = (cam->params.qx3.bottomlight == 0) << 1; - int p2 = (cam->params.qx3.toplight == 0) << 3; - do_command(cam, CPIA_COMMAND_WriteVCReg, 0x90, 0x8F, 0x50, 0); - do_command(cam, CPIA_COMMAND_WriteMCPort, 2, 0, (p1|p2|0xE0), 0); - } - - cam->cmd_queue = COMMAND_NONE; - mutex_unlock(&cam->param_lock); - return; -} - - - -static void set_flicker(struct cam_params *params, volatile u32 *command_flags, - int on) -{ - /* Everything in here is from the Windows driver */ -#define FIRMWARE_VERSION(x,y) (params->version.firmwareVersion == (x) && \ - params->version.firmwareRevision == (y)) -/* define for compgain calculation */ -#if 0 -#define COMPGAIN(base, curexp, newexp) \ - (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5) -#define EXP_FROM_COMP(basecomp, curcomp, curexp) \ - (u16)((float)curexp * (float)(u8)(curcomp + 128) / (float)(u8)(basecomp - 128)) -#else - /* equivalent functions without floating point math */ -#define COMPGAIN(base, curexp, newexp) \ - (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2* newexp)) ) -#define EXP_FROM_COMP(basecomp, curcomp, curexp) \ - (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128))) -#endif - - - int currentexp = params->exposure.coarseExpLo + - params->exposure.coarseExpHi*256; - int startexp; - if (on) { - int cj = params->flickerControl.coarseJump; - params->flickerControl.flickerMode = 1; - params->flickerControl.disabled = 0; - if(params->exposure.expMode != 2) - *command_flags |= COMMAND_SETEXPOSURE; - params->exposure.expMode = 2; - currentexp = currentexp << params->exposure.gain; - params->exposure.gain = 0; - /* round down current exposure to nearest value */ - startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj; - if(startexp < 1) - startexp = 1; - startexp = (startexp * cj) - 1; - if(FIRMWARE_VERSION(1,2)) - while(startexp > MAX_EXP_102) - startexp -= cj; - else - while(startexp > MAX_EXP) - startexp -= cj; - params->exposure.coarseExpLo = startexp & 0xff; - params->exposure.coarseExpHi = startexp >> 8; - if (currentexp > startexp) { - if (currentexp > (2 * startexp)) - currentexp = 2 * startexp; - params->exposure.redComp = COMPGAIN (COMP_RED, currentexp, startexp); - params->exposure.green1Comp = COMPGAIN (COMP_GREEN1, currentexp, startexp); - params->exposure.green2Comp = COMPGAIN (COMP_GREEN2, currentexp, startexp); - params->exposure.blueComp = COMPGAIN (COMP_BLUE, currentexp, startexp); - } else { - params->exposure.redComp = COMP_RED; - params->exposure.green1Comp = COMP_GREEN1; - params->exposure.green2Comp = COMP_GREEN2; - params->exposure.blueComp = COMP_BLUE; - } - if(FIRMWARE_VERSION(1,2)) - params->exposure.compMode = 0; - else - params->exposure.compMode = 1; - - params->apcor.gain1 = 0x18; - params->apcor.gain2 = 0x18; - params->apcor.gain4 = 0x16; - params->apcor.gain8 = 0x14; - *command_flags |= COMMAND_SETAPCOR; - } else { - params->flickerControl.flickerMode = 0; - params->flickerControl.disabled = 1; - /* Coarse = average of equivalent coarse for each comp channel */ - startexp = EXP_FROM_COMP(COMP_RED, params->exposure.redComp, currentexp); - startexp += EXP_FROM_COMP(COMP_GREEN1, params->exposure.green1Comp, currentexp); - startexp += EXP_FROM_COMP(COMP_GREEN2, params->exposure.green2Comp, currentexp); - startexp += EXP_FROM_COMP(COMP_BLUE, params->exposure.blueComp, currentexp); - startexp = startexp >> 2; - while(startexp > MAX_EXP && - params->exposure.gain < params->exposure.gainMode-1) { - startexp = startexp >> 1; - ++params->exposure.gain; - } - if(FIRMWARE_VERSION(1,2) && startexp > MAX_EXP_102) - startexp = MAX_EXP_102; - if(startexp > MAX_EXP) - startexp = MAX_EXP; - params->exposure.coarseExpLo = startexp&0xff; - params->exposure.coarseExpHi = startexp >> 8; - params->exposure.redComp = COMP_RED; - params->exposure.green1Comp = COMP_GREEN1; - params->exposure.green2Comp = COMP_GREEN2; - params->exposure.blueComp = COMP_BLUE; - params->exposure.compMode = 1; - *command_flags |= COMMAND_SETEXPOSURE; - params->apcor.gain1 = 0x18; - params->apcor.gain2 = 0x16; - params->apcor.gain4 = 0x24; - params->apcor.gain8 = 0x34; - *command_flags |= COMMAND_SETAPCOR; - } - params->vlOffset.gain1 = 20; - params->vlOffset.gain2 = 24; - params->vlOffset.gain4 = 26; - params->vlOffset.gain8 = 26; - *command_flags |= COMMAND_SETVLOFFSET; -#undef FIRMWARE_VERSION -#undef EXP_FROM_COMP -#undef COMPGAIN -} - -#define FIRMWARE_VERSION(x,y) (cam->params.version.firmwareVersion == (x) && \ - cam->params.version.firmwareRevision == (y)) -/* monitor the exposure and adjust the sensor frame rate if needed */ -static void monitor_exposure(struct cam_data *cam) -{ - u8 exp_acc, bcomp, gain, coarseL, cmd[8], data[8]; - int retval, light_exp, dark_exp, very_dark_exp; - int old_exposure, new_exposure, framerate; - - /* get necessary stats and register settings from camera */ - /* do_command can't handle this, so do it ourselves */ - cmd[0] = CPIA_COMMAND_ReadVPRegs>>8; - cmd[1] = CPIA_COMMAND_ReadVPRegs&0xff; - cmd[2] = 30; - cmd[3] = 4; - cmd[4] = 9; - cmd[5] = 8; - cmd[6] = 8; - cmd[7] = 0; - retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data); - if (retval) { - LOG("ReadVPRegs(30,4,9,8) - failed, retval=%d\n", - retval); - return; - } - exp_acc = data[0]; - bcomp = data[1]; - gain = data[2]; - coarseL = data[3]; - - mutex_lock(&cam->param_lock); - light_exp = cam->params.colourParams.brightness + - TC - 50 + EXP_ACC_LIGHT; - if(light_exp > 255) - light_exp = 255; - dark_exp = cam->params.colourParams.brightness + - TC - 50 - EXP_ACC_DARK; - if(dark_exp < 0) - dark_exp = 0; - very_dark_exp = dark_exp/2; - - old_exposure = cam->params.exposure.coarseExpHi * 256 + - cam->params.exposure.coarseExpLo; - - if(!cam->params.flickerControl.disabled) { - /* Flicker control on */ - int max_comp = FIRMWARE_VERSION(1,2) ? MAX_COMP : HIGH_COMP_102; - bcomp += 128; /* decode */ - if(bcomp >= max_comp && exp_acc < dark_exp) { - /* dark */ - if(exp_acc < very_dark_exp) { - /* very dark */ - if(cam->exposure_status == EXPOSURE_VERY_DARK) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_VERY_DARK; - cam->exposure_count = 1; - } - } else { - /* just dark */ - if(cam->exposure_status == EXPOSURE_DARK) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_DARK; - cam->exposure_count = 1; - } - } - } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) { - /* light */ - if(old_exposure <= VERY_LOW_EXP) { - /* very light */ - if(cam->exposure_status == EXPOSURE_VERY_LIGHT) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_VERY_LIGHT; - cam->exposure_count = 1; - } - } else { - /* just light */ - if(cam->exposure_status == EXPOSURE_LIGHT) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_LIGHT; - cam->exposure_count = 1; - } - } - } else { - /* not dark or light */ - cam->exposure_status = EXPOSURE_NORMAL; - } - } else { - /* Flicker control off */ - if(old_exposure >= MAX_EXP && exp_acc < dark_exp) { - /* dark */ - if(exp_acc < very_dark_exp) { - /* very dark */ - if(cam->exposure_status == EXPOSURE_VERY_DARK) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_VERY_DARK; - cam->exposure_count = 1; - } - } else { - /* just dark */ - if(cam->exposure_status == EXPOSURE_DARK) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_DARK; - cam->exposure_count = 1; - } - } - } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) { - /* light */ - if(old_exposure <= VERY_LOW_EXP) { - /* very light */ - if(cam->exposure_status == EXPOSURE_VERY_LIGHT) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_VERY_LIGHT; - cam->exposure_count = 1; - } - } else { - /* just light */ - if(cam->exposure_status == EXPOSURE_LIGHT) - ++cam->exposure_count; - else { - cam->exposure_status = EXPOSURE_LIGHT; - cam->exposure_count = 1; - } - } - } else { - /* not dark or light */ - cam->exposure_status = EXPOSURE_NORMAL; - } - } - - framerate = cam->fps; - if(framerate > 30 || framerate < 1) - framerate = 1; - - if(!cam->params.flickerControl.disabled) { - /* Flicker control on */ - if((cam->exposure_status == EXPOSURE_VERY_DARK || - cam->exposure_status == EXPOSURE_DARK) && - cam->exposure_count >= DARK_TIME*framerate && - cam->params.sensorFps.divisor < 3) { - - /* dark for too long */ - ++cam->params.sensorFps.divisor; - cam->cmd_queue |= COMMAND_SETSENSORFPS; - - cam->params.flickerControl.coarseJump = - flicker_jumps[cam->mainsFreq] - [cam->params.sensorFps.baserate] - [cam->params.sensorFps.divisor]; - cam->cmd_queue |= COMMAND_SETFLICKERCTRL; - - new_exposure = cam->params.flickerControl.coarseJump-1; - while(new_exposure < old_exposure/2) - new_exposure += cam->params.flickerControl.coarseJump; - cam->params.exposure.coarseExpLo = new_exposure & 0xff; - cam->params.exposure.coarseExpHi = new_exposure >> 8; - cam->cmd_queue |= COMMAND_SETEXPOSURE; - cam->exposure_status = EXPOSURE_NORMAL; - LOG("Automatically decreasing sensor_fps\n"); - - } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT || - cam->exposure_status == EXPOSURE_LIGHT) && - cam->exposure_count >= LIGHT_TIME*framerate && - cam->params.sensorFps.divisor > 0) { - - /* light for too long */ - int max_exp = FIRMWARE_VERSION(1,2) ? MAX_EXP_102 : MAX_EXP ; - - --cam->params.sensorFps.divisor; - cam->cmd_queue |= COMMAND_SETSENSORFPS; - - cam->params.flickerControl.coarseJump = - flicker_jumps[cam->mainsFreq] - [cam->params.sensorFps.baserate] - [cam->params.sensorFps.divisor]; - cam->cmd_queue |= COMMAND_SETFLICKERCTRL; - - new_exposure = cam->params.flickerControl.coarseJump-1; - while(new_exposure < 2*old_exposure && - new_exposure+ - cam->params.flickerControl.coarseJump < max_exp) - new_exposure += cam->params.flickerControl.coarseJump; - cam->params.exposure.coarseExpLo = new_exposure & 0xff; - cam->params.exposure.coarseExpHi = new_exposure >> 8; - cam->cmd_queue |= COMMAND_SETEXPOSURE; - cam->exposure_status = EXPOSURE_NORMAL; - LOG("Automatically increasing sensor_fps\n"); - } - } else { - /* Flicker control off */ - if((cam->exposure_status == EXPOSURE_VERY_DARK || - cam->exposure_status == EXPOSURE_DARK) && - cam->exposure_count >= DARK_TIME*framerate && - cam->params.sensorFps.divisor < 3) { - - /* dark for too long */ - ++cam->params.sensorFps.divisor; - cam->cmd_queue |= COMMAND_SETSENSORFPS; - - if(cam->params.exposure.gain > 0) { - --cam->params.exposure.gain; - cam->cmd_queue |= COMMAND_SETEXPOSURE; - } - cam->exposure_status = EXPOSURE_NORMAL; - LOG("Automatically decreasing sensor_fps\n"); - - } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT || - cam->exposure_status == EXPOSURE_LIGHT) && - cam->exposure_count >= LIGHT_TIME*framerate && - cam->params.sensorFps.divisor > 0) { - - /* light for too long */ - --cam->params.sensorFps.divisor; - cam->cmd_queue |= COMMAND_SETSENSORFPS; - - if(cam->params.exposure.gain < - cam->params.exposure.gainMode-1) { - ++cam->params.exposure.gain; - cam->cmd_queue |= COMMAND_SETEXPOSURE; - } - cam->exposure_status = EXPOSURE_NORMAL; - LOG("Automatically increasing sensor_fps\n"); - } - } - mutex_unlock(&cam->param_lock); -} - -/*-----------------------------------------------------------------*/ -/* if flicker is switched off, this function switches it back on.It checks, - however, that conditions are suitable before restarting it. - This should only be called for firmware version 1.2. - - It also adjust the colour balance when an exposure step is detected - as - long as flicker is running -*/ -static void restart_flicker(struct cam_data *cam) -{ - int cam_exposure, old_exp; - if(!FIRMWARE_VERSION(1,2)) - return; - mutex_lock(&cam->param_lock); - if(cam->params.flickerControl.flickerMode == 0 || - cam->raw_image[39] == 0) { - mutex_unlock(&cam->param_lock); - return; - } - cam_exposure = cam->raw_image[39]*2; - old_exp = cam->params.exposure.coarseExpLo + - cam->params.exposure.coarseExpHi*256; - /* - see how far away camera exposure is from a valid - flicker exposure value - */ - cam_exposure %= cam->params.flickerControl.coarseJump; - if(!cam->params.flickerControl.disabled && - cam_exposure <= cam->params.flickerControl.coarseJump - 3) { - /* Flicker control auto-disabled */ - cam->params.flickerControl.disabled = 1; - } - - if(cam->params.flickerControl.disabled && - cam->params.flickerControl.flickerMode && - old_exp > cam->params.flickerControl.coarseJump + - ROUND_UP_EXP_FOR_FLICKER) { - /* exposure is now high enough to switch - flicker control back on */ - set_flicker(&cam->params, &cam->cmd_queue, 1); - if((cam->cmd_queue & COMMAND_SETEXPOSURE) && - cam->params.exposure.expMode == 2) - cam->exposure_status = EXPOSURE_NORMAL; - - } - mutex_unlock(&cam->param_lock); -} -#undef FIRMWARE_VERSION - -static int clear_stall(struct cam_data *cam) -{ - /* FIXME: Does this actually work? */ - LOG("Clearing stall\n"); - - cam->ops->streamRead(cam->lowlevel_data, cam->raw_image, 0); - do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0); - return cam->params.status.streamState != STREAM_PAUSED; -} - -/* kernel thread function to read image from camera */ -static int fetch_frame(void *data) -{ - int image_size, retry; - struct cam_data *cam = (struct cam_data *)data; - unsigned long oldjif, rate, diff; - - /* Allow up to two bad images in a row to be read and - * ignored before an error is reported */ - for (retry = 0; retry < 3; ++retry) { - if (retry) - DBG("retry=%d\n", retry); - - if (!cam->ops) - continue; - - /* load first frame always uncompressed */ - if (cam->first_frame && - cam->params.compression.mode != CPIA_COMPRESSION_NONE) { - do_command(cam, CPIA_COMMAND_SetCompression, - CPIA_COMPRESSION_NONE, - NO_DECIMATION, 0, 0); - /* Trial & error - Discarding a frame prevents the - first frame from having an error in the data. */ - do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0); - } - - /* init camera upload */ - if (do_command(cam, CPIA_COMMAND_GrabFrame, 0, - cam->params.streamStartLine, 0, 0)) - continue; - - if (cam->ops->wait_for_stream_ready) { - /* loop until image ready */ - int count = 0; - do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0); - while (cam->params.status.streamState != STREAM_READY) { - if(++count > READY_TIMEOUT) - break; - if(cam->params.status.streamState == - STREAM_PAUSED) { - /* Bad news */ - if(!clear_stall(cam)) - return -EIO; - } - - cond_resched(); - - /* sleep for 10 ms, hopefully ;) */ - msleep_interruptible(10); - if (signal_pending(current)) - return -EINTR; - - do_command(cam, CPIA_COMMAND_GetCameraStatus, - 0, 0, 0, 0); - } - if(cam->params.status.streamState != STREAM_READY) { - continue; - } - } - - cond_resched(); - - /* grab image from camera */ - oldjif = jiffies; - image_size = cam->ops->streamRead(cam->lowlevel_data, - cam->raw_image, 0); - if (image_size <= 0) { - DBG("streamRead failed: %d\n", image_size); - continue; - } - - rate = image_size * HZ / 1024; - diff = jiffies-oldjif; - cam->transfer_rate = diff==0 ? rate : rate/diff; - /* diff==0 ? unlikely but possible */ - - /* Switch flicker control back on if it got turned off */ - restart_flicker(cam); - - /* If AEC is enabled, monitor the exposure and - adjust the sensor frame rate if needed */ - if(cam->params.exposure.expMode == 2) - monitor_exposure(cam); - - /* camera idle now so dispatch queued commands */ - dispatch_commands(cam); - - /* Update our knowledge of the camera state */ - do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0); - do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0); - do_command(cam, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0); - - /* decompress and convert image to by copying it from - * raw_image to decompressed_frame - */ - - cond_resched(); - - cam->image_size = parse_picture(cam, image_size); - if (cam->image_size <= 0) { - DBG("parse_picture failed %d\n", cam->image_size); - if(cam->params.compression.mode != - CPIA_COMPRESSION_NONE) { - /* Compression may not work right if we - had a bad frame, get the next one - uncompressed. */ - cam->first_frame = 1; - do_command(cam, CPIA_COMMAND_SetGrabMode, - CPIA_GRAB_SINGLE, 0, 0, 0); - /* FIXME: Trial & error - need up to 70ms for - the grab mode change to complete ? */ - msleep_interruptible(70); - if (signal_pending(current)) - return -EINTR; - } - } else - break; - } - - if (retry < 3) { - /* FIXME: this only works for double buffering */ - if (cam->frame[cam->curframe].state == FRAME_READY) { - memcpy(cam->frame[cam->curframe].data, - cam->decompressed_frame.data, - cam->decompressed_frame.count); - cam->frame[cam->curframe].state = FRAME_DONE; - } else - cam->decompressed_frame.state = FRAME_DONE; - - if (cam->first_frame) { - cam->first_frame = 0; - do_command(cam, CPIA_COMMAND_SetCompression, - cam->params.compression.mode, - cam->params.compression.decimation, 0, 0); - - /* Switch from single-grab to continuous grab */ - do_command(cam, CPIA_COMMAND_SetGrabMode, - CPIA_GRAB_CONTINUOUS, 0, 0, 0); - } - return 0; - } - return -EIO; -} - -static int capture_frame(struct cam_data *cam, struct video_mmap *vm) -{ - if (!cam->frame_buf) { - /* we do lazy allocation */ - int err; - if ((err = allocate_frame_buf(cam))) - return err; - } - - cam->curframe = vm->frame; - cam->frame[cam->curframe].state = FRAME_READY; - return fetch_frame(cam); -} - -static int goto_high_power(struct cam_data *cam) -{ - if (do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0)) - return -EIO; - msleep_interruptible(40); /* windows driver does it too */ - if(signal_pending(current)) - return -EINTR; - if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) - return -EIO; - if (cam->params.status.systemState == HI_POWER_STATE) { - DBG("camera now in HIGH power state\n"); - return 0; - } - printstatus(cam); - return -EIO; -} - -static int goto_low_power(struct cam_data *cam) -{ - if (do_command(cam, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0)) - return -1; - if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) - return -1; - if (cam->params.status.systemState == LO_POWER_STATE) { - DBG("camera now in LOW power state\n"); - return 0; - } - printstatus(cam); - return -1; -} - -static void save_camera_state(struct cam_data *cam) -{ - if(!(cam->cmd_queue & COMMAND_SETCOLOURBALANCE)) - do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0); - if(!(cam->cmd_queue & COMMAND_SETEXPOSURE)) - do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0); - - DBG("%d/%d/%d/%d/%d/%d/%d/%d\n", - cam->params.exposure.gain, - cam->params.exposure.fineExp, - cam->params.exposure.coarseExpLo, - cam->params.exposure.coarseExpHi, - cam->params.exposure.redComp, - cam->params.exposure.green1Comp, - cam->params.exposure.green2Comp, - cam->params.exposure.blueComp); - DBG("%d/%d/%d\n", - cam->params.colourBalance.redGain, - cam->params.colourBalance.greenGain, - cam->params.colourBalance.blueGain); -} - -static int set_camera_state(struct cam_data *cam) -{ - cam->cmd_queue = COMMAND_SETCOMPRESSION | - COMMAND_SETCOMPRESSIONTARGET | - COMMAND_SETCOLOURPARAMS | - COMMAND_SETFORMAT | - COMMAND_SETYUVTHRESH | - COMMAND_SETECPTIMING | - COMMAND_SETCOMPRESSIONPARAMS | - COMMAND_SETEXPOSURE | - COMMAND_SETCOLOURBALANCE | - COMMAND_SETSENSORFPS | - COMMAND_SETAPCOR | - COMMAND_SETFLICKERCTRL | - COMMAND_SETVLOFFSET; - - do_command(cam, CPIA_COMMAND_SetGrabMode, CPIA_GRAB_SINGLE,0,0,0); - dispatch_commands(cam); - - /* Wait 6 frames for the sensor to get all settings and - AEC/ACB to settle */ - msleep_interruptible(6*(cam->params.sensorFps.baserate ? 33 : 40) * - (1 << cam->params.sensorFps.divisor) + 10); - - if(signal_pending(current)) - return -EINTR; - - save_camera_state(cam); - - return 0; -} - -static void get_version_information(struct cam_data *cam) -{ - /* GetCPIAVersion */ - do_command(cam, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0); - - /* GetPnPID */ - do_command(cam, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0); -} - -/* initialize camera */ -static int reset_camera(struct cam_data *cam) -{ - int err; - /* Start the camera in low power mode */ - if (goto_low_power(cam)) { - if (cam->params.status.systemState != WARM_BOOT_STATE) - return -ENODEV; - - /* FIXME: this is just dirty trial and error */ - err = goto_high_power(cam); - if(err) - return err; - do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0); - if (goto_low_power(cam)) - return -ENODEV; - } - - /* procedure described in developer's guide p3-28 */ - - /* Check the firmware version. */ - cam->params.version.firmwareVersion = 0; - get_version_information(cam); - if (cam->params.version.firmwareVersion != 1) - return -ENODEV; - - /* A bug in firmware 1-02 limits gainMode to 2 */ - if(cam->params.version.firmwareRevision <= 2 && - cam->params.exposure.gainMode > 2) { - cam->params.exposure.gainMode = 2; - } - - /* set QX3 detected flag */ - cam->params.qx3.qx3_detected = (cam->params.pnpID.vendor == 0x0813 && - cam->params.pnpID.product == 0x0001); - - /* The fatal error checking should be done after - * the camera powers up (developer's guide p 3-38) */ - - /* Set streamState before transition to high power to avoid bug - * in firmware 1-02 */ - do_command(cam, CPIA_COMMAND_ModifyCameraStatus, STREAMSTATE, 0, - STREAM_NOT_READY, 0); - - /* GotoHiPower */ - err = goto_high_power(cam); - if (err) - return err; - - /* Check the camera status */ - if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) - return -EIO; - - if (cam->params.status.fatalError) { - DBG("fatal_error: %#04x\n", - cam->params.status.fatalError); - DBG("vp_status: %#04x\n", - cam->params.status.vpStatus); - if (cam->params.status.fatalError & ~(COM_FLAG|CPIA_FLAG)) { - /* Fatal error in camera */ - return -EIO; - } else if (cam->params.status.fatalError & (COM_FLAG|CPIA_FLAG)) { - /* Firmware 1-02 may do this for parallel port cameras, - * just clear the flags (developer's guide p 3-38) */ - do_command(cam, CPIA_COMMAND_ModifyCameraStatus, - FATALERROR, ~(COM_FLAG|CPIA_FLAG), 0, 0); - } - } - - /* Check the camera status again */ - if (cam->params.status.fatalError) { - if (cam->params.status.fatalError) - return -EIO; - } - - /* VPVersion can't be retrieved before the camera is in HiPower, - * so get it here instead of in get_version_information. */ - do_command(cam, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0); - - /* set camera to a known state */ - return set_camera_state(cam); -} - -static void put_cam(struct cpia_camera_ops* ops) -{ - module_put(ops->owner); -} - -/* ------------------------- V4L interface --------------------- */ -static int cpia_open(struct file *file) -{ - struct video_device *dev = video_devdata(file); - struct cam_data *cam = video_get_drvdata(dev); - int err; - - if (!cam) { - DBG("Internal error, cam_data not found!\n"); - return -ENODEV; - } - - if (cam->open_count > 0) { - DBG("Camera already open\n"); - return -EBUSY; - } - - if (!try_module_get(cam->ops->owner)) - return -ENODEV; - - mutex_lock(&cam->busy_lock); - err = -ENOMEM; - if (!cam->raw_image) { - cam->raw_image = rvmalloc(CPIA_MAX_IMAGE_SIZE); - if (!cam->raw_image) - goto oops; - } - - if (!cam->decompressed_frame.data) { - cam->decompressed_frame.data = rvmalloc(CPIA_MAX_FRAME_SIZE); - if (!cam->decompressed_frame.data) - goto oops; - } - - /* open cpia */ - err = -ENODEV; - if (cam->ops->open(cam->lowlevel_data)) - goto oops; - - /* reset the camera */ - if ((err = reset_camera(cam)) != 0) { - cam->ops->close(cam->lowlevel_data); - goto oops; - } - - /* Set ownership of /proc/cpia/videoX to current user */ - if(cam->proc_entry) - cam->proc_entry->uid = current_euid(); - - /* set mark for loading first frame uncompressed */ - cam->first_frame = 1; - - /* init it to something */ - cam->mmap_kludge = 0; - - ++cam->open_count; - file->private_data = dev; - mutex_unlock(&cam->busy_lock); - return 0; - - oops: - if (cam->decompressed_frame.data) { - rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); - cam->decompressed_frame.data = NULL; - } - if (cam->raw_image) { - rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); - cam->raw_image = NULL; - } - mutex_unlock(&cam->busy_lock); - put_cam(cam->ops); - return err; -} - -static int cpia_close(struct file *file) -{ - struct video_device *dev = file->private_data; - struct cam_data *cam = video_get_drvdata(dev); - - if (cam->ops) { - /* Return ownership of /proc/cpia/videoX to root */ - if(cam->proc_entry) - cam->proc_entry->uid = 0; - - /* save camera state for later open (developers guide ch 3.5.3) */ - save_camera_state(cam); - - /* GotoLoPower */ - goto_low_power(cam); - - /* Update the camera status */ - do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); - - /* cleanup internal state stuff */ - free_frames(cam->frame); - - /* close cpia */ - cam->ops->close(cam->lowlevel_data); - - put_cam(cam->ops); - } - - if (--cam->open_count == 0) { - /* clean up capture-buffers */ - if (cam->raw_image) { - rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); - cam->raw_image = NULL; - } - - if (cam->decompressed_frame.data) { - rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); - cam->decompressed_frame.data = NULL; - } - - if (cam->frame_buf) - free_frame_buf(cam); - - if (!cam->ops) - kfree(cam); - } - file->private_data = NULL; - - return 0; -} - -static ssize_t cpia_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct video_device *dev = file->private_data; - struct cam_data *cam = video_get_drvdata(dev); - int err; - - /* make this _really_ smp and multithread-safe */ - if (mutex_lock_interruptible(&cam->busy_lock)) - return -EINTR; - - if (!buf) { - DBG("buf NULL\n"); - mutex_unlock(&cam->busy_lock); - return -EINVAL; - } - - if (!count) { - DBG("count 0\n"); - mutex_unlock(&cam->busy_lock); - return 0; - } - - if (!cam->ops) { - DBG("ops NULL\n"); - mutex_unlock(&cam->busy_lock); - return -ENODEV; - } - - /* upload frame */ - cam->decompressed_frame.state = FRAME_READY; - cam->mmap_kludge=0; - if((err = fetch_frame(cam)) != 0) { - DBG("ERROR from fetch_frame: %d\n", err); - mutex_unlock(&cam->busy_lock); - return err; - } - cam->decompressed_frame.state = FRAME_UNUSED; - - /* copy data to user space */ - if (cam->decompressed_frame.count > count) { - DBG("count wrong: %d, %lu\n", cam->decompressed_frame.count, - (unsigned long) count); - mutex_unlock(&cam->busy_lock); - return -EFAULT; - } - if (copy_to_user(buf, cam->decompressed_frame.data, - cam->decompressed_frame.count)) { - DBG("copy_to_user failed\n"); - mutex_unlock(&cam->busy_lock); - return -EFAULT; - } - - mutex_unlock(&cam->busy_lock); - return cam->decompressed_frame.count; -} - -static long cpia_do_ioctl(struct file *file, unsigned int cmd, void *arg) -{ - struct video_device *dev = file->private_data; - struct cam_data *cam = video_get_drvdata(dev); - int retval = 0; - - if (!cam || !cam->ops) - return -ENODEV; - - /* make this _really_ smp-safe */ - if (mutex_lock_interruptible(&cam->busy_lock)) - return -EINTR; - - /* DBG("cpia_ioctl: %u\n", cmd); */ - - switch (cmd) { - /* query capabilities */ - case VIDIOCGCAP: - { - struct video_capability *b = arg; - - DBG("VIDIOCGCAP\n"); - strcpy(b->name, "CPiA Camera"); - b->type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE; - b->channels = 1; - b->audios = 0; - b->maxwidth = 352; /* VIDEOSIZE_CIF */ - b->maxheight = 288; - b->minwidth = 48; /* VIDEOSIZE_48_48 */ - b->minheight = 48; - break; - } - - /* get/set video source - we are a camera and nothing else */ - case VIDIOCGCHAN: - { - struct video_channel *v = arg; - - DBG("VIDIOCGCHAN\n"); - if (v->channel != 0) { - retval = -EINVAL; - break; - } - - v->channel = 0; - strcpy(v->name, "Camera"); - v->tuners = 0; - v->flags = 0; - v->type = VIDEO_TYPE_CAMERA; - v->norm = 0; - break; - } - - case VIDIOCSCHAN: - { - struct video_channel *v = arg; - - DBG("VIDIOCSCHAN\n"); - if (v->channel != 0) - retval = -EINVAL; - break; - } - - /* image properties */ - case VIDIOCGPICT: - { - struct video_picture *pic = arg; - DBG("VIDIOCGPICT\n"); - *pic = cam->vp; - break; - } - - case VIDIOCSPICT: - { - struct video_picture *vp = arg; - - DBG("VIDIOCSPICT\n"); - - /* check validity */ - DBG("palette: %d\n", vp->palette); - DBG("depth: %d\n", vp->depth); - if (!valid_mode(vp->palette, vp->depth)) { - retval = -EINVAL; - break; - } - - mutex_lock(&cam->param_lock); - /* brightness, colour, contrast need no check 0-65535 */ - cam->vp = *vp; - /* update cam->params.colourParams */ - cam->params.colourParams.brightness = vp->brightness*100/65535; - cam->params.colourParams.contrast = vp->contrast*100/65535; - cam->params.colourParams.saturation = vp->colour*100/65535; - /* contrast is in steps of 8, so round */ - cam->params.colourParams.contrast = - ((cam->params.colourParams.contrast + 3) / 8) * 8; - if (cam->params.version.firmwareVersion == 1 && - cam->params.version.firmwareRevision == 2 && - cam->params.colourParams.contrast > 80) { - /* 1-02 firmware limits contrast to 80 */ - cam->params.colourParams.contrast = 80; - } - - /* Adjust flicker control if necessary */ - if(cam->params.flickerControl.allowableOverExposure < 0) - cam->params.flickerControl.allowableOverExposure = - -find_over_exposure(cam->params.colourParams.brightness); - if(cam->params.flickerControl.flickerMode != 0) - cam->cmd_queue |= COMMAND_SETFLICKERCTRL; - - - /* queue command to update camera */ - cam->cmd_queue |= COMMAND_SETCOLOURPARAMS; - mutex_unlock(&cam->param_lock); - DBG("VIDIOCSPICT: %d / %d // %d / %d / %d / %d\n", - vp->depth, vp->palette, vp->brightness, vp->hue, vp->colour, - vp->contrast); - break; - } - - /* get/set capture window */ - case VIDIOCGWIN: - { - struct video_window *vw = arg; - DBG("VIDIOCGWIN\n"); - - *vw = cam->vw; - break; - } - - case VIDIOCSWIN: - { - /* copy_from_user, check validity, copy to internal structure */ - struct video_window *vw = arg; - DBG("VIDIOCSWIN\n"); - - if (vw->clipcount != 0) { /* clipping not supported */ - retval = -EINVAL; - break; - } - if (vw->clips != NULL) { /* clipping not supported */ - retval = -EINVAL; - break; - } - - /* we set the video window to something smaller or equal to what - * is requested by the user??? - */ - mutex_lock(&cam->param_lock); - if (vw->width != cam->vw.width || vw->height != cam->vw.height) { - int video_size = match_videosize(vw->width, vw->height); - - if (video_size < 0) { - retval = -EINVAL; - mutex_unlock(&cam->param_lock); - break; - } - cam->video_size = video_size; - - /* video size is changing, reset the subcapture area */ - memset(&cam->vc, 0, sizeof(cam->vc)); - - set_vw_size(cam); - DBG("%d / %d\n", cam->vw.width, cam->vw.height); - cam->cmd_queue |= COMMAND_SETFORMAT; - } - - mutex_unlock(&cam->param_lock); - - /* setformat ignored by camera during streaming, - * so stop/dispatch/start */ - if (cam->cmd_queue & COMMAND_SETFORMAT) { - DBG("\n"); - dispatch_commands(cam); - } - DBG("%d/%d:%d\n", cam->video_size, - cam->vw.width, cam->vw.height); - break; - } - - /* mmap interface */ - case VIDIOCGMBUF: - { - struct video_mbuf *vm = arg; - int i; - - DBG("VIDIOCGMBUF\n"); - memset(vm, 0, sizeof(*vm)); - vm->size = CPIA_MAX_FRAME_SIZE*FRAME_NUM; - vm->frames = FRAME_NUM; - for (i = 0; i < FRAME_NUM; i++) - vm->offsets[i] = CPIA_MAX_FRAME_SIZE * i; - break; - } - - case VIDIOCMCAPTURE: - { - struct video_mmap *vm = arg; - int video_size; - - DBG("VIDIOCMCAPTURE: %d / %d / %dx%d\n", vm->format, vm->frame, - vm->width, vm->height); - if (vm->frame<0||vm->frame>=FRAME_NUM) { - retval = -EINVAL; - break; - } - - /* set video format */ - cam->vp.palette = vm->format; - switch(vm->format) { - case VIDEO_PALETTE_GREY: - cam->vp.depth=8; - break; - case VIDEO_PALETTE_RGB555: - case VIDEO_PALETTE_RGB565: - case VIDEO_PALETTE_YUV422: - case VIDEO_PALETTE_YUYV: - case VIDEO_PALETTE_UYVY: - cam->vp.depth = 16; - break; - case VIDEO_PALETTE_RGB24: - cam->vp.depth = 24; - break; - case VIDEO_PALETTE_RGB32: - cam->vp.depth = 32; - break; - default: - retval = -EINVAL; - break; - } - if (retval) - break; - - /* set video size */ - video_size = match_videosize(vm->width, vm->height); - if (video_size < 0) { - retval = -EINVAL; - break; - } - if (video_size != cam->video_size) { - cam->video_size = video_size; - - /* video size is changing, reset the subcapture area */ - memset(&cam->vc, 0, sizeof(cam->vc)); - - set_vw_size(cam); - cam->cmd_queue |= COMMAND_SETFORMAT; - dispatch_commands(cam); - } - /* according to v4l-spec we must start streaming here */ - cam->mmap_kludge = 1; - retval = capture_frame(cam, vm); - - break; - } - - case VIDIOCSYNC: - { - int *frame = arg; - - //DBG("VIDIOCSYNC: %d\n", *frame); - - if (*frame<0 || *frame >= FRAME_NUM) { - retval = -EINVAL; - break; - } - - switch (cam->frame[*frame].state) { - case FRAME_UNUSED: - case FRAME_READY: - case FRAME_GRABBING: - DBG("sync to unused frame %d\n", *frame); - retval = -EINVAL; - break; - - case FRAME_DONE: - cam->frame[*frame].state = FRAME_UNUSED; - //DBG("VIDIOCSYNC: %d synced\n", *frame); - break; - } - if (retval == -EINTR) { - /* FIXME - xawtv does not handle this nice */ - retval = 0; - } - break; - } - - case VIDIOCGCAPTURE: - { - struct video_capture *vc = arg; - - DBG("VIDIOCGCAPTURE\n"); - - *vc = cam->vc; - - break; - } - - case VIDIOCSCAPTURE: - { - struct video_capture *vc = arg; - - DBG("VIDIOCSCAPTURE\n"); - - if (vc->decimation != 0) { /* How should this be used? */ - retval = -EINVAL; - break; - } - if (vc->flags != 0) { /* Even/odd grab not supported */ - retval = -EINVAL; - break; - } - - /* Clip to the resolution we can set for the ROI - (every 8 columns and 4 rows) */ - vc->x = vc->x & ~(__u32)7; - vc->y = vc->y & ~(__u32)3; - vc->width = vc->width & ~(__u32)7; - vc->height = vc->height & ~(__u32)3; - - if(vc->width == 0 || vc->height == 0 || - vc->x + vc->width > cam->vw.width || - vc->y + vc->height > cam->vw.height) { - retval = -EINVAL; - break; - } - - DBG("%d,%d/%dx%d\n", vc->x,vc->y,vc->width, vc->height); - - mutex_lock(&cam->param_lock); - - cam->vc.x = vc->x; - cam->vc.y = vc->y; - cam->vc.width = vc->width; - cam->vc.height = vc->height; - - set_vw_size(cam); - cam->cmd_queue |= COMMAND_SETFORMAT; - - mutex_unlock(&cam->param_lock); - - /* setformat ignored by camera during streaming, - * so stop/dispatch/start */ - dispatch_commands(cam); - break; - } - - case VIDIOCGUNIT: - { - struct video_unit *vu = arg; - - DBG("VIDIOCGUNIT\n"); - - vu->video = cam->vdev.minor; - vu->vbi = VIDEO_NO_UNIT; - vu->radio = VIDEO_NO_UNIT; - vu->audio = VIDEO_NO_UNIT; - vu->teletext = VIDEO_NO_UNIT; - - break; - } - - - /* pointless to implement overlay with this camera */ - case VIDIOCCAPTURE: - case VIDIOCGFBUF: - case VIDIOCSFBUF: - case VIDIOCKEY: - /* tuner interface - we have none */ - case VIDIOCGTUNER: - case VIDIOCSTUNER: - case VIDIOCGFREQ: - case VIDIOCSFREQ: - /* audio interface - we have none */ - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: - retval = -EINVAL; - break; - default: - retval = -ENOIOCTLCMD; - break; - } - - mutex_unlock(&cam->busy_lock); - return retval; -} - -static long cpia_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - return video_usercopy(file, cmd, arg, cpia_do_ioctl); -} - - -/* FIXME */ -static int cpia_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct video_device *dev = file->private_data; - unsigned long start = vma->vm_start; - unsigned long size = vma->vm_end - vma->vm_start; - unsigned long page, pos; - struct cam_data *cam = video_get_drvdata(dev); - int retval; - - if (!cam || !cam->ops) - return -ENODEV; - - DBG("cpia_mmap: %ld\n", size); - - if (size > FRAME_NUM*CPIA_MAX_FRAME_SIZE) - return -EINVAL; - - /* make this _really_ smp-safe */ - if (mutex_lock_interruptible(&cam->busy_lock)) - return -EINTR; - - if (!cam->frame_buf) { /* we do lazy allocation */ - if ((retval = allocate_frame_buf(cam))) { - mutex_unlock(&cam->busy_lock); - return retval; - } - } - - pos = (unsigned long)(cam->frame_buf); - while (size > 0) { - page = vmalloc_to_pfn((void *)pos); - if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { - mutex_unlock(&cam->busy_lock); - return -EAGAIN; - } - start += PAGE_SIZE; - pos += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; - } - - DBG("cpia_mmap: %ld\n", size); - mutex_unlock(&cam->busy_lock); - - return 0; -} - -static const struct v4l2_file_operations cpia_fops = { - .owner = THIS_MODULE, - .open = cpia_open, - .release = cpia_close, - .read = cpia_read, - .mmap = cpia_mmap, - .ioctl = cpia_ioctl, -}; - -static struct video_device cpia_template = { - .name = "CPiA Camera", - .fops = &cpia_fops, - .release = video_device_release_empty, -}; - -/* initialise cam_data structure */ -static void reset_camera_struct(struct cam_data *cam) -{ - /* The following parameter values are the defaults from - * "Software Developer's Guide for CPiA Cameras". Any changes - * to the defaults are noted in comments. */ - cam->params.colourParams.brightness = 50; - cam->params.colourParams.contrast = 48; - cam->params.colourParams.saturation = 50; - cam->params.exposure.gainMode = 4; - cam->params.exposure.expMode = 2; /* AEC */ - cam->params.exposure.compMode = 1; - cam->params.exposure.centreWeight = 1; - cam->params.exposure.gain = 0; - cam->params.exposure.fineExp = 0; - cam->params.exposure.coarseExpLo = 185; - cam->params.exposure.coarseExpHi = 0; - cam->params.exposure.redComp = COMP_RED; - cam->params.exposure.green1Comp = COMP_GREEN1; - cam->params.exposure.green2Comp = COMP_GREEN2; - cam->params.exposure.blueComp = COMP_BLUE; - cam->params.colourBalance.balanceMode = 2; /* ACB */ - cam->params.colourBalance.redGain = 32; - cam->params.colourBalance.greenGain = 6; - cam->params.colourBalance.blueGain = 92; - cam->params.apcor.gain1 = 0x18; - cam->params.apcor.gain2 = 0x16; - cam->params.apcor.gain4 = 0x24; - cam->params.apcor.gain8 = 0x34; - cam->params.flickerControl.flickerMode = 0; - cam->params.flickerControl.disabled = 1; - - cam->params.flickerControl.coarseJump = - flicker_jumps[cam->mainsFreq] - [cam->params.sensorFps.baserate] - [cam->params.sensorFps.divisor]; - cam->params.flickerControl.allowableOverExposure = - -find_over_exposure(cam->params.colourParams.brightness); - cam->params.vlOffset.gain1 = 20; - cam->params.vlOffset.gain2 = 24; - cam->params.vlOffset.gain4 = 26; - cam->params.vlOffset.gain8 = 26; - cam->params.compressionParams.hysteresis = 3; - cam->params.compressionParams.threshMax = 11; - cam->params.compressionParams.smallStep = 1; - cam->params.compressionParams.largeStep = 3; - cam->params.compressionParams.decimationHysteresis = 2; - cam->params.compressionParams.frDiffStepThresh = 5; - cam->params.compressionParams.qDiffStepThresh = 3; - cam->params.compressionParams.decimationThreshMod = 2; - /* End of default values from Software Developer's Guide */ - - cam->transfer_rate = 0; - cam->exposure_status = EXPOSURE_NORMAL; - - /* Set Sensor FPS to 15fps. This seems better than 30fps - * for indoor lighting. */ - cam->params.sensorFps.divisor = 1; - cam->params.sensorFps.baserate = 1; - - cam->params.yuvThreshold.yThreshold = 6; /* From windows driver */ - cam->params.yuvThreshold.uvThreshold = 6; /* From windows driver */ - - cam->params.format.subSample = SUBSAMPLE_422; - cam->params.format.yuvOrder = YUVORDER_YUYV; - - cam->params.compression.mode = CPIA_COMPRESSION_AUTO; - cam->params.compressionTarget.frTargeting = - CPIA_COMPRESSION_TARGET_QUALITY; - cam->params.compressionTarget.targetFR = 15; /* From windows driver */ - cam->params.compressionTarget.targetQ = 5; /* From windows driver */ - - cam->params.qx3.qx3_detected = 0; - cam->params.qx3.toplight = 0; - cam->params.qx3.bottomlight = 0; - cam->params.qx3.button = 0; - cam->params.qx3.cradled = 0; - - cam->video_size = VIDEOSIZE_CIF; - - cam->vp.colour = 32768; /* 50% */ - cam->vp.hue = 32768; /* 50% */ - cam->vp.brightness = 32768; /* 50% */ - cam->vp.contrast = 32768; /* 50% */ - cam->vp.whiteness = 0; /* not used -> grayscale only */ - cam->vp.depth = 24; /* to be set by user */ - cam->vp.palette = VIDEO_PALETTE_RGB24; /* to be set by user */ - - cam->vc.x = 0; - cam->vc.y = 0; - cam->vc.width = 0; - cam->vc.height = 0; - - cam->vw.x = 0; - cam->vw.y = 0; - set_vw_size(cam); - cam->vw.chromakey = 0; - cam->vw.flags = 0; - cam->vw.clipcount = 0; - cam->vw.clips = NULL; - - cam->cmd_queue = COMMAND_NONE; - cam->first_frame = 1; - - return; -} - -/* initialize cam_data structure */ -static void init_camera_struct(struct cam_data *cam, - struct cpia_camera_ops *ops ) -{ - int i; - - /* Default everything to 0 */ - memset(cam, 0, sizeof(struct cam_data)); - - cam->ops = ops; - mutex_init(&cam->param_lock); - mutex_init(&cam->busy_lock); - - reset_camera_struct(cam); - - cam->proc_entry = NULL; - - memcpy(&cam->vdev, &cpia_template, sizeof(cpia_template)); - video_set_drvdata(&cam->vdev, cam); - - cam->curframe = 0; - for (i = 0; i < FRAME_NUM; i++) { - cam->frame[i].width = 0; - cam->frame[i].height = 0; - cam->frame[i].state = FRAME_UNUSED; - cam->frame[i].data = NULL; - } - cam->decompressed_frame.width = 0; - cam->decompressed_frame.height = 0; - cam->decompressed_frame.state = FRAME_UNUSED; - cam->decompressed_frame.data = NULL; -} - -struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel) -{ - struct cam_data *camera; - - if ((camera = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL) - return NULL; - - - init_camera_struct( camera, ops ); - camera->lowlevel_data = lowlevel; - - /* register v4l device */ - if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { - kfree(camera); - printk(KERN_DEBUG "video_register_device failed\n"); - return NULL; - } - - /* get version information from camera: open/reset/close */ - - /* open cpia */ - if (camera->ops->open(camera->lowlevel_data)) - return camera; - - /* reset the camera */ - if (reset_camera(camera) != 0) { - camera->ops->close(camera->lowlevel_data); - return camera; - } - - /* close cpia */ - camera->ops->close(camera->lowlevel_data); - -#ifdef CONFIG_PROC_FS - create_proc_cpia_cam(camera); -#endif - - printk(KERN_INFO " CPiA Version: %d.%02d (%d.%d)\n", - camera->params.version.firmwareVersion, - camera->params.version.firmwareRevision, - camera->params.version.vcVersion, - camera->params.version.vcRevision); - printk(KERN_INFO " CPiA PnP-ID: %04x:%04x:%04x\n", - camera->params.pnpID.vendor, - camera->params.pnpID.product, - camera->params.pnpID.deviceRevision); - printk(KERN_INFO " VP-Version: %d.%d %04x\n", - camera->params.vpVersion.vpVersion, - camera->params.vpVersion.vpRevision, - camera->params.vpVersion.cameraHeadID); - - return camera; -} - -void cpia_unregister_camera(struct cam_data *cam) -{ - DBG("unregistering video\n"); - video_unregister_device(&cam->vdev); - if (cam->open_count) { - put_cam(cam->ops); - DBG("camera open -- setting ops to NULL\n"); - cam->ops = NULL; - } - -#ifdef CONFIG_PROC_FS - DBG("destroying /proc/cpia/%s\n", video_device_node_name(&cam->vdev)); - destroy_proc_cpia_cam(cam); -#endif - if (!cam->open_count) { - DBG("freeing camera\n"); - kfree(cam); - } -} - -static int __init cpia_init(void) -{ - printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT, - CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER); - - printk(KERN_WARNING "Since in-kernel colorspace conversion is not " - "allowed, it is disabled by default now. Users should fix the " - "applications in case they don't work without conversion " - "reenabled by setting the 'colorspace_conv' module " - "parameter to 1\n"); - -#ifdef CONFIG_PROC_FS - proc_cpia_create(); -#endif - - return 0; -} - -static void __exit cpia_exit(void) -{ -#ifdef CONFIG_PROC_FS - proc_cpia_destroy(); -#endif -} - -module_init(cpia_init); -module_exit(cpia_exit); - -/* Exported symbols for modules. */ - -EXPORT_SYMBOL(cpia_register_camera); -EXPORT_SYMBOL(cpia_unregister_camera); diff --git a/drivers/staging/cpia/cpia.h b/drivers/staging/cpia/cpia.h deleted file mode 100644 index 8f0cfee4b8a1..000000000000 --- a/drivers/staging/cpia/cpia.h +++ /dev/null @@ -1,432 +0,0 @@ -#ifndef cpia_h -#define cpia_h - -/* - * CPiA Parallel Port Video4Linux driver - * - * Supports CPiA based parallel port Video Camera's. - * - * (C) Copyright 1999 Bas Huisman, - * Peter Pregler, - * Scott J. Bertin, - * VLSI Vision Ltd. - * - * 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. - */ - -#define CPIA_MAJ_VER 1 -#define CPIA_MIN_VER 2 -#define CPIA_PATCH_VER 3 - -#define CPIA_PP_MAJ_VER CPIA_MAJ_VER -#define CPIA_PP_MIN_VER CPIA_MIN_VER -#define CPIA_PP_PATCH_VER CPIA_PATCH_VER - -#define CPIA_USB_MAJ_VER CPIA_MAJ_VER -#define CPIA_USB_MIN_VER CPIA_MIN_VER -#define CPIA_USB_PATCH_VER CPIA_PATCH_VER - -#define CPIA_MAX_FRAME_SIZE_UNALIGNED (352 * 288 * 4) /* CIF at RGB32 */ -#define CPIA_MAX_FRAME_SIZE ((CPIA_MAX_FRAME_SIZE_UNALIGNED + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) /* align above to PAGE_SIZE */ - -#ifdef __KERNEL__ - -#include -#include -#include -#include -#include -#include - -struct cpia_camera_ops -{ - /* open sets privdata to point to structure for this camera. - * Returns negative value on error, otherwise 0. - */ - int (*open)(void *privdata); - - /* Registers callback function cb to be called with cbdata - * when an image is ready. If cb is NULL, only single image grabs - * should be used. cb should immediately call streamRead to read - * the data or data may be lost. Returns negative value on error, - * otherwise 0. - */ - int (*registerCallback)(void *privdata, void (*cb)(void *cbdata), - void *cbdata); - - /* transferCmd sends commands to the camera. command MUST point to - * an 8 byte buffer in kernel space. data can be NULL if no extra - * data is needed. The size of the data is given by the last 2 - * bytes of command. data must also point to memory in kernel space. - * Returns negative value on error, otherwise 0. - */ - int (*transferCmd)(void *privdata, u8 *command, u8 *data); - - /* streamStart initiates stream capture mode. - * Returns negative value on error, otherwise 0. - */ - int (*streamStart)(void *privdata); - - /* streamStop terminates stream capture mode. - * Returns negative value on error, otherwise 0. - */ - int (*streamStop)(void *privdata); - - /* streamRead reads a frame from the camera. buffer points to a - * buffer large enough to hold a complete frame in kernel space. - * noblock indicates if this should be a non blocking read. - * Returns the number of bytes read, or negative value on error. - */ - int (*streamRead)(void *privdata, u8 *buffer, int noblock); - - /* close disables the device until open() is called again. - * Returns negative value on error, otherwise 0. - */ - int (*close)(void *privdata); - - /* If wait_for_stream_ready is non-zero, wait until the streamState - * is STREAM_READY before calling streamRead. - */ - int wait_for_stream_ready; - - /* - * Used to maintain lowlevel module usage counts - */ - struct module *owner; -}; - -struct cpia_frame { - u8 *data; - int count; - int width; - int height; - volatile int state; -}; - -struct cam_params { - struct { - u8 firmwareVersion; - u8 firmwareRevision; - u8 vcVersion; - u8 vcRevision; - } version; - struct { - u16 vendor; - u16 product; - u16 deviceRevision; - } pnpID; - struct { - u8 vpVersion; - u8 vpRevision; - u16 cameraHeadID; - } vpVersion; - struct { - u8 systemState; - u8 grabState; - u8 streamState; - u8 fatalError; - u8 cmdError; - u8 debugFlags; - u8 vpStatus; - u8 errorCode; - } status; - struct { - u8 brightness; - u8 contrast; - u8 saturation; - } colourParams; - struct { - u8 gainMode; - u8 expMode; - u8 compMode; - u8 centreWeight; - u8 gain; - u8 fineExp; - u8 coarseExpLo; - u8 coarseExpHi; - u8 redComp; - u8 green1Comp; - u8 green2Comp; - u8 blueComp; - } exposure; - struct { - u8 balanceMode; - u8 redGain; - u8 greenGain; - u8 blueGain; - } colourBalance; - struct { - u8 divisor; - u8 baserate; - } sensorFps; - struct { - u8 gain1; - u8 gain2; - u8 gain4; - u8 gain8; - } apcor; - struct { - u8 disabled; - u8 flickerMode; - u8 coarseJump; - int allowableOverExposure; - } flickerControl; - struct { - u8 gain1; - u8 gain2; - u8 gain4; - u8 gain8; - } vlOffset; - struct { - u8 mode; - u8 decimation; - } compression; - struct { - u8 frTargeting; - u8 targetFR; - u8 targetQ; - } compressionTarget; - struct { - u8 yThreshold; - u8 uvThreshold; - } yuvThreshold; - struct { - u8 hysteresis; - u8 threshMax; - u8 smallStep; - u8 largeStep; - u8 decimationHysteresis; - u8 frDiffStepThresh; - u8 qDiffStepThresh; - u8 decimationThreshMod; - } compressionParams; - struct { - u8 videoSize; /* CIF/QCIF */ - u8 subSample; - u8 yuvOrder; - } format; - struct { /* Intel QX3 specific data */ - u8 qx3_detected; /* a QX3 is present */ - u8 toplight; /* top light lit , R/W */ - u8 bottomlight; /* bottom light lit, R/W */ - u8 button; /* snapshot button pressed (R/O) */ - u8 cradled; /* microscope is in cradle (R/O) */ - } qx3; - struct { - u8 colStart; /* skip first 8*colStart pixels */ - u8 colEnd; /* finish at 8*colEnd pixels */ - u8 rowStart; /* skip first 4*rowStart lines */ - u8 rowEnd; /* finish at 4*rowEnd lines */ - } roi; - u8 ecpTiming; - u8 streamStartLine; -}; - -enum v4l_camstates { - CPIA_V4L_IDLE = 0, - CPIA_V4L_ERROR, - CPIA_V4L_COMMAND, - CPIA_V4L_GRABBING, - CPIA_V4L_STREAMING, - CPIA_V4L_STREAMING_PAUSED, -}; - -#define FRAME_NUM 2 /* double buffering for now */ - -struct cam_data { - struct list_head cam_data_list; - - struct mutex busy_lock; /* guard against SMP multithreading */ - struct cpia_camera_ops *ops; /* lowlevel driver operations */ - void *lowlevel_data; /* private data for lowlevel driver */ - u8 *raw_image; /* buffer for raw image data */ - struct cpia_frame decompressed_frame; - /* buffer to hold decompressed frame */ - int image_size; /* sizeof last decompressed image */ - int open_count; /* # of process that have camera open */ - - /* camera status */ - int fps; /* actual fps reported by the camera */ - int transfer_rate; /* transfer rate from camera in kB/s */ - u8 mainsFreq; /* for flicker control */ - - /* proc interface */ - struct mutex param_lock; /* params lock for this camera */ - struct cam_params params; /* camera settings */ - struct proc_dir_entry *proc_entry; /* /proc/cpia/videoX */ - - /* v4l */ - int video_size; /* VIDEO_SIZE_ */ - volatile enum v4l_camstates camstate; /* v4l layer status */ - struct video_device vdev; /* v4l videodev */ - struct video_picture vp; /* v4l camera settings */ - struct video_window vw; /* v4l capture area */ - struct video_capture vc; /* v4l subcapture area */ - - /* mmap interface */ - int curframe; /* the current frame to grab into */ - u8 *frame_buf; /* frame buffer data */ - struct cpia_frame frame[FRAME_NUM]; - /* FRAME_NUM-buffering, so we need a array */ - - int first_frame; - int mmap_kludge; /* 'wrong' byte order for mmap */ - volatile u32 cmd_queue; /* queued commands */ - int exposure_status; /* EXPOSURE_* */ - int exposure_count; /* number of frames at this status */ -}; - -/* cpia_register_camera is called by low level driver for each camera. - * A unique camera number is returned, or a negative value on error */ -struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel); - -/* cpia_unregister_camera is called by low level driver when a camera - * is removed. This must not fail. */ -void cpia_unregister_camera(struct cam_data *cam); - -/* raw CIF + 64 byte header + (2 bytes line_length + EOL) per line + 4*EOI + - * one byte 16bit DMA alignment - */ -#define CPIA_MAX_IMAGE_SIZE ((352*288*2)+64+(288*3)+5) - -/* constant value's */ -#define MAGIC_0 0x19 -#define MAGIC_1 0x68 -#define DATA_IN 0xC0 -#define DATA_OUT 0x40 -#define VIDEOSIZE_QCIF 0 /* 176x144 */ -#define VIDEOSIZE_CIF 1 /* 352x288 */ -#define VIDEOSIZE_SIF 2 /* 320x240 */ -#define VIDEOSIZE_QSIF 3 /* 160x120 */ -#define VIDEOSIZE_48_48 4 /* where no one has gone before, iconsize! */ -#define VIDEOSIZE_64_48 5 -#define VIDEOSIZE_128_96 6 -#define VIDEOSIZE_160_120 VIDEOSIZE_QSIF -#define VIDEOSIZE_176_144 VIDEOSIZE_QCIF -#define VIDEOSIZE_192_144 7 -#define VIDEOSIZE_224_168 8 -#define VIDEOSIZE_256_192 9 -#define VIDEOSIZE_288_216 10 -#define VIDEOSIZE_320_240 VIDEOSIZE_SIF -#define VIDEOSIZE_352_288 VIDEOSIZE_CIF -#define VIDEOSIZE_88_72 11 /* quarter CIF */ -#define SUBSAMPLE_420 0 -#define SUBSAMPLE_422 1 -#define YUVORDER_YUYV 0 -#define YUVORDER_UYVY 1 -#define NOT_COMPRESSED 0 -#define COMPRESSED 1 -#define NO_DECIMATION 0 -#define DECIMATION_ENAB 1 -#define EOI 0xff /* End Of Image */ -#define EOL 0xfd /* End Of Line */ -#define FRAME_HEADER_SIZE 64 - -/* Image grab modes */ -#define CPIA_GRAB_SINGLE 0 -#define CPIA_GRAB_CONTINUOUS 1 - -/* Compression parameters */ -#define CPIA_COMPRESSION_NONE 0 -#define CPIA_COMPRESSION_AUTO 1 -#define CPIA_COMPRESSION_MANUAL 2 -#define CPIA_COMPRESSION_TARGET_QUALITY 0 -#define CPIA_COMPRESSION_TARGET_FRAMERATE 1 - -/* Return offsets for GetCameraState */ -#define SYSTEMSTATE 0 -#define GRABSTATE 1 -#define STREAMSTATE 2 -#define FATALERROR 3 -#define CMDERROR 4 -#define DEBUGFLAGS 5 -#define VPSTATUS 6 -#define ERRORCODE 7 - -/* SystemState */ -#define UNINITIALISED_STATE 0 -#define PASS_THROUGH_STATE 1 -#define LO_POWER_STATE 2 -#define HI_POWER_STATE 3 -#define WARM_BOOT_STATE 4 - -/* GrabState */ -#define GRAB_IDLE 0 -#define GRAB_ACTIVE 1 -#define GRAB_DONE 2 - -/* StreamState */ -#define STREAM_NOT_READY 0 -#define STREAM_READY 1 -#define STREAM_OPEN 2 -#define STREAM_PAUSED 3 -#define STREAM_FINISHED 4 - -/* Fatal Error, CmdError, and DebugFlags */ -#define CPIA_FLAG 1 -#define SYSTEM_FLAG 2 -#define INT_CTRL_FLAG 4 -#define PROCESS_FLAG 8 -#define COM_FLAG 16 -#define VP_CTRL_FLAG 32 -#define CAPTURE_FLAG 64 -#define DEBUG_FLAG 128 - -/* VPStatus */ -#define VP_STATE_OK 0x00 - -#define VP_STATE_FAILED_VIDEOINIT 0x01 -#define VP_STATE_FAILED_AECACBINIT 0x02 -#define VP_STATE_AEC_MAX 0x04 -#define VP_STATE_ACB_BMAX 0x08 - -#define VP_STATE_ACB_RMIN 0x10 -#define VP_STATE_ACB_GMIN 0x20 -#define VP_STATE_ACB_RMAX 0x40 -#define VP_STATE_ACB_GMAX 0x80 - -/* default (minimum) compensation values */ -#define COMP_RED 220 -#define COMP_GREEN1 214 -#define COMP_GREEN2 COMP_GREEN1 -#define COMP_BLUE 230 - -/* exposure status */ -#define EXPOSURE_VERY_LIGHT 0 -#define EXPOSURE_LIGHT 1 -#define EXPOSURE_NORMAL 2 -#define EXPOSURE_DARK 3 -#define EXPOSURE_VERY_DARK 4 - -/* ErrorCode */ -#define ERROR_FLICKER_BELOW_MIN_EXP 0x01 /*flicker exposure got below minimum exposure */ -#define ALOG(fmt,args...) printk(fmt, ##args) -#define LOG(fmt,args...) ALOG(KERN_INFO __FILE__ ":%s(%d):" fmt, __func__ , __LINE__ , ##args) - -#ifdef _CPIA_DEBUG_ -#define ADBG(fmt,args...) printk(fmt, jiffies, ##args) -#define DBG(fmt,args...) ADBG(KERN_DEBUG __FILE__" (%ld):%s(%d):" fmt, __func__, __LINE__ , ##args) -#else -#define DBG(fmn,args...) do {} while(0) -#endif - -#define DEB_BYTE(p)\ - DBG("%1d %1d %1d %1d %1d %1d %1d %1d \n",\ - (p)&0x80?1:0, (p)&0x40?1:0, (p)&0x20?1:0, (p)&0x10?1:0,\ - (p)&0x08?1:0, (p)&0x04?1:0, (p)&0x02?1:0, (p)&0x01?1:0); - -#endif /* __KERNEL__ */ - -#endif /* cpia_h */ diff --git a/drivers/staging/cpia/cpia_pp.c b/drivers/staging/cpia/cpia_pp.c deleted file mode 100644 index f5604c16a092..000000000000 --- a/drivers/staging/cpia/cpia_pp.c +++ /dev/null @@ -1,869 +0,0 @@ -/* - * cpia_pp CPiA Parallel Port driver - * - * Supports CPiA based parallel port Video Camera's. - * - * (C) Copyright 1999 Bas Huisman - * (C) Copyright 1999-2000 Scott J. Bertin , - * (C) Copyright 1999-2000 Peter Pregler - * - * 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. - */ - -/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */ -/* #define _CPIA_DEBUG_ 1 */ - - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -/* #define _CPIA_DEBUG_ define for verbose debug output */ -#include "cpia.h" - -static int cpia_pp_open(void *privdata); -static int cpia_pp_registerCallback(void *privdata, void (*cb) (void *cbdata), - void *cbdata); -static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data); -static int cpia_pp_streamStart(void *privdata); -static int cpia_pp_streamStop(void *privdata); -static int cpia_pp_streamRead(void *privdata, u8 *buffer, int noblock); -static int cpia_pp_close(void *privdata); - - -#define ABOUT "Parallel port driver for Vision CPiA based cameras" - -#define PACKET_LENGTH 8 - -/* Magic numbers for defining port-device mappings */ -#define PPCPIA_PARPORT_UNSPEC -4 -#define PPCPIA_PARPORT_AUTO -3 -#define PPCPIA_PARPORT_OFF -2 -#define PPCPIA_PARPORT_NONE -1 - -static int parport_nr[PARPORT_MAX] = {[0 ... PARPORT_MAX - 1] = PPCPIA_PARPORT_UNSPEC}; -static char *parport[PARPORT_MAX] = {NULL,}; - -MODULE_AUTHOR("B. Huisman & Peter Pregler "); -MODULE_DESCRIPTION("Parallel port driver for Vision CPiA based cameras"); -MODULE_LICENSE("GPL"); - -module_param_array(parport, charp, NULL, 0); -MODULE_PARM_DESC(parport, "'auto' or a list of parallel port numbers. Just like lp."); - -struct pp_cam_entry { - struct pardevice *pdev; - struct parport *port; - struct work_struct cb_task; - void (*cb_func)(void *cbdata); - void *cb_data; - int open_count; - wait_queue_head_t wq_stream; - /* image state flags */ - int image_ready; /* we got an interrupt */ - int image_complete; /* we have seen 4 EOI */ - - int streaming; /* we are in streaming mode */ - int stream_irq; -}; - -static struct cpia_camera_ops cpia_pp_ops = -{ - cpia_pp_open, - cpia_pp_registerCallback, - cpia_pp_transferCmd, - cpia_pp_streamStart, - cpia_pp_streamStop, - cpia_pp_streamRead, - cpia_pp_close, - 1, - THIS_MODULE -}; - -static LIST_HEAD(cam_list); -static spinlock_t cam_list_lock_pp; - -/* FIXME */ -static void cpia_parport_enable_irq( struct parport *port ) { - parport_enable_irq(port); - mdelay(10); - return; -} - -static void cpia_parport_disable_irq( struct parport *port ) { - parport_disable_irq(port); - mdelay(10); - return; -} - -/* Special CPiA PPC modes: These are invoked by using the 1284 Extensibility - * Link Flag during negotiation */ -#define UPLOAD_FLAG 0x08 -#define NIBBLE_TRANSFER 0x01 -#define ECP_TRANSFER 0x03 - -#define PARPORT_CHUNK_SIZE PAGE_SIZE - - -static void cpia_pp_run_callback(struct work_struct *work) -{ - void (*cb_func)(void *cbdata); - void *cb_data; - struct pp_cam_entry *cam; - - cam = container_of(work, struct pp_cam_entry, cb_task); - cb_func = cam->cb_func; - cb_data = cam->cb_data; - - cb_func(cb_data); -} - -/**************************************************************************** - * - * CPiA-specific low-level parport functions for nibble uploads - * - ***************************************************************************/ -/* CPiA nonstandard "Nibble" mode (no nDataAvail signal after each byte). */ -/* The standard kernel parport_ieee1284_read_nibble() fails with the CPiA... */ - -static size_t cpia_read_nibble (struct parport *port, - void *buffer, size_t len, - int flags) -{ - /* adapted verbatim, with one change, from - parport_ieee1284_read_nibble() in drivers/parport/ieee1284-ops.c */ - - unsigned char *buf = buffer; - int i; - unsigned char byte = 0; - - len *= 2; /* in nibbles */ - for (i=0; i < len; i++) { - unsigned char nibble; - - /* The CPiA firmware suppresses the use of nDataAvail (nFault LO) - * after every second nibble to signal that more - * data is available. (the total number of Bytes that - * should be sent is known; if too few are received, an error - * will be recorded after a timeout). - * This is incompatible with parport_ieee1284_read_nibble(), - * which expects to find nFault LO after every second nibble. - */ - - /* Solution: modify cpia_read_nibble to only check for - * nDataAvail before the first nibble is sent. - */ - - /* Does the error line indicate end of data? */ - if (((i /*& 1*/) == 0) && - (parport_read_status(port) & PARPORT_STATUS_ERROR)) { - DBG("%s: No more nibble data (%d bytes)\n", - port->name, i/2); - goto end_of_data; - } - - /* Event 7: Set nAutoFd low. */ - parport_frob_control (port, - PARPORT_CONTROL_AUTOFD, - PARPORT_CONTROL_AUTOFD); - - /* Event 9: nAck goes low. */ - port->ieee1284.phase = IEEE1284_PH_REV_DATA; - if (parport_wait_peripheral (port, - PARPORT_STATUS_ACK, 0)) { - /* Timeout -- no more data? */ - DBG("%s: Nibble timeout at event 9 (%d bytes)\n", - port->name, i/2); - parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); - break; - } - - - /* Read a nibble. */ - nibble = parport_read_status (port) >> 3; - nibble &= ~8; - if ((nibble & 0x10) == 0) - nibble |= 8; - nibble &= 0xf; - - /* Event 10: Set nAutoFd high. */ - parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); - - /* Event 11: nAck goes high. */ - if (parport_wait_peripheral (port, - PARPORT_STATUS_ACK, - PARPORT_STATUS_ACK)) { - /* Timeout -- no more data? */ - DBG("%s: Nibble timeout at event 11\n", - port->name); - break; - } - - if (i & 1) { - /* Second nibble */ - byte |= nibble << 4; - *buf++ = byte; - } else - byte = nibble; - } - - if (i == len) { - /* Read the last nibble without checking data avail. */ - if (parport_read_status (port) & PARPORT_STATUS_ERROR) { - end_of_data: - /* Go to reverse idle phase. */ - parport_frob_control (port, - PARPORT_CONTROL_AUTOFD, - PARPORT_CONTROL_AUTOFD); - port->physport->ieee1284.phase = IEEE1284_PH_REV_IDLE; - } - else - port->physport->ieee1284.phase = IEEE1284_PH_HBUSY_DAVAIL; - } - - return i/2; -} - -/* CPiA nonstandard "Nibble Stream" mode (2 nibbles per cycle, instead of 1) - * (See CPiA Data sheet p. 31) - * - * "Nibble Stream" mode used by CPiA for uploads to non-ECP ports is a - * nonstandard variant of nibble mode which allows the same (mediocre) - * data flow of 8 bits per cycle as software-enabled ECP by TRISTATE-capable - * parallel ports, but works also for non-TRISTATE-capable ports. - * (Standard nibble mode only send 4 bits per cycle) - * - */ - -static size_t cpia_read_nibble_stream(struct parport *port, - void *buffer, size_t len, - int flags) -{ - int i; - unsigned char *buf = buffer; - int endseen = 0; - - for (i=0; i < len; i++) { - unsigned char nibble[2], byte = 0; - int j; - - /* Image Data is complete when 4 consecutive EOI bytes (0xff) are seen */ - if (endseen > 3 ) - break; - - /* Event 7: Set nAutoFd low. */ - parport_frob_control (port, - PARPORT_CONTROL_AUTOFD, - PARPORT_CONTROL_AUTOFD); - - /* Event 9: nAck goes low. */ - port->ieee1284.phase = IEEE1284_PH_REV_DATA; - if (parport_wait_peripheral (port, - PARPORT_STATUS_ACK, 0)) { - /* Timeout -- no more data? */ - DBG("%s: Nibble timeout at event 9 (%d bytes)\n", - port->name, i/2); - parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); - break; - } - - /* Read lower nibble */ - nibble[0] = parport_read_status (port) >>3; - - /* Event 10: Set nAutoFd high. */ - parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0); - - /* Event 11: nAck goes high. */ - if (parport_wait_peripheral (port, - PARPORT_STATUS_ACK, - PARPORT_STATUS_ACK)) { - /* Timeout -- no more data? */ - DBG("%s: Nibble timeout at event 11\n", - port->name); - break; - } - - /* Read upper nibble */ - nibble[1] = parport_read_status (port) >>3; - - /* reassemble the byte */ - for (j = 0; j < 2 ; j++ ) { - nibble[j] &= ~8; - if ((nibble[j] & 0x10) == 0) - nibble[j] |= 8; - nibble[j] &= 0xf; - } - byte = (nibble[0] |(nibble[1] << 4)); - *buf++ = byte; - - if(byte == EOI) - endseen++; - else - endseen = 0; - } - return i; -} - -/**************************************************************************** - * - * EndTransferMode - * - ***************************************************************************/ -static void EndTransferMode(struct pp_cam_entry *cam) -{ - parport_negotiate(cam->port, IEEE1284_MODE_COMPAT); -} - -/**************************************************************************** - * - * ForwardSetup - * - ***************************************************************************/ -static int ForwardSetup(struct pp_cam_entry *cam) -{ - int retry; - - /* The CPiA uses ECP protocol for Downloads from the Host to the camera. - * This will be software-emulated if ECP hardware is not present - */ - - /* the usual camera maximum response time is 10ms, but after receiving - * some commands, it needs up to 40ms. (Data Sheet p. 32)*/ - - for(retry = 0; retry < 4; ++retry) { - if(!parport_negotiate(cam->port, IEEE1284_MODE_ECP)) { - break; - } - mdelay(10); - } - if(retry == 4) { - DBG("Unable to negotiate IEEE1284 ECP Download mode\n"); - return -1; - } - return 0; -} -/**************************************************************************** - * - * ReverseSetup - * - ***************************************************************************/ -static int ReverseSetup(struct pp_cam_entry *cam, int extensibility) -{ - int retry; - int upload_mode, mode = IEEE1284_MODE_ECP; - int transfer_mode = ECP_TRANSFER; - - if (!(cam->port->modes & PARPORT_MODE_ECP) && - !(cam->port->modes & PARPORT_MODE_TRISTATE)) { - mode = IEEE1284_MODE_NIBBLE; - transfer_mode = NIBBLE_TRANSFER; - } - - upload_mode = mode; - if(extensibility) mode = UPLOAD_FLAG|transfer_mode|IEEE1284_EXT_LINK; - - /* the usual camera maximum response time is 10ms, but after - * receiving some commands, it needs up to 40ms. */ - - for(retry = 0; retry < 4; ++retry) { - if(!parport_negotiate(cam->port, mode)) { - break; - } - mdelay(10); - } - if(retry == 4) { - if(extensibility) - DBG("Unable to negotiate upload extensibility mode\n"); - else - DBG("Unable to negotiate upload mode\n"); - return -1; - } - if(extensibility) cam->port->ieee1284.mode = upload_mode; - return 0; -} - -/**************************************************************************** - * - * WritePacket - * - ***************************************************************************/ -static int WritePacket(struct pp_cam_entry *cam, const u8 *packet, size_t size) -{ - int retval=0; - int size_written; - - if (packet == NULL) { - return -EINVAL; - } - if (ForwardSetup(cam)) { - DBG("Write failed in setup\n"); - return -EIO; - } - size_written = parport_write(cam->port, packet, size); - if(size_written != size) { - DBG("Write failed, wrote %d/%d\n", size_written, size); - retval = -EIO; - } - EndTransferMode(cam); - return retval; -} - -/**************************************************************************** - * - * ReadPacket - * - ***************************************************************************/ -static int ReadPacket(struct pp_cam_entry *cam, u8 *packet, size_t size) -{ - int retval=0; - - if (packet == NULL) { - return -EINVAL; - } - if (ReverseSetup(cam, 0)) { - return -EIO; - } - - /* support for CPiA variant nibble reads */ - if(cam->port->ieee1284.mode == IEEE1284_MODE_NIBBLE) { - if(cpia_read_nibble(cam->port, packet, size, 0) != size) - retval = -EIO; - } else { - if(parport_read(cam->port, packet, size) != size) - retval = -EIO; - } - EndTransferMode(cam); - return retval; -} - -/**************************************************************************** - * - * cpia_pp_streamStart - * - ***************************************************************************/ -static int cpia_pp_streamStart(void *privdata) -{ - struct pp_cam_entry *cam = privdata; - DBG("\n"); - cam->streaming=1; - cam->image_ready=0; - //if (ReverseSetup(cam,1)) return -EIO; - if(cam->stream_irq) cpia_parport_enable_irq(cam->port); - return 0; -} - -/**************************************************************************** - * - * cpia_pp_streamStop - * - ***************************************************************************/ -static int cpia_pp_streamStop(void *privdata) -{ - struct pp_cam_entry *cam = privdata; - - DBG("\n"); - cam->streaming=0; - cpia_parport_disable_irq(cam->port); - //EndTransferMode(cam); - - return 0; -} - -/**************************************************************************** - * - * cpia_pp_streamRead - * - ***************************************************************************/ -static int cpia_pp_read(struct parport *port, u8 *buffer, int len) -{ - int bytes_read; - - /* support for CPiA variant "nibble stream" reads */ - if(port->ieee1284.mode == IEEE1284_MODE_NIBBLE) - bytes_read = cpia_read_nibble_stream(port,buffer,len,0); - else { - int new_bytes; - for(bytes_read=0; bytes_readstreaming) DBG("%d / %d\n", cam->image_ready, noblock); - if( cam->stream_irq ) { - DBG("%d\n", cam->image_ready); - cam->image_ready--; - } - cam->image_complete=0; - if (0/*cam->streaming*/) { - if(!cam->image_ready) { - if(noblock) return -EWOULDBLOCK; - interruptible_sleep_on(&cam->wq_stream); - if( signal_pending(current) ) return -EINTR; - DBG("%d\n", cam->image_ready); - } - } else { - if (ReverseSetup(cam, 1)) { - DBG("unable to ReverseSetup\n"); - return -EIO; - } - } - endseen = 0; - block_size = PARPORT_CHUNK_SIZE; - while( !cam->image_complete ) { - cond_resched(); - - new_bytes = cpia_pp_read(cam->port, buffer, block_size ); - if( new_bytes <= 0 ) { - break; - } - i=-1; - while(++iimage_complete=1; - break; - } - if( CPIA_MAX_IMAGE_SIZE-read_bytes <= PARPORT_CHUNK_SIZE ) { - block_size=CPIA_MAX_IMAGE_SIZE-read_bytes; - } - } - EndTransferMode(cam); - return cam->image_complete ? read_bytes : -EIO; -} -/**************************************************************************** - * - * cpia_pp_transferCmd - * - ***************************************************************************/ -static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data) -{ - int err; - int retval=0; - int databytes; - struct pp_cam_entry *cam = privdata; - - if(cam == NULL) { - DBG("Internal driver error: cam is NULL\n"); - return -EINVAL; - } - if(command == NULL) { - DBG("Internal driver error: command is NULL\n"); - return -EINVAL; - } - databytes = (((int)command[7])<<8) | command[6]; - if ((err = WritePacket(cam, command, PACKET_LENGTH)) < 0) { - DBG("Error writing command\n"); - return err; - } - if(command[0] == DATA_IN) { - u8 buffer[8]; - if(data == NULL) { - DBG("Internal driver error: data is NULL\n"); - return -EINVAL; - } - if((err = ReadPacket(cam, buffer, 8)) < 0) { - DBG("Error reading command result\n"); - return err; - } - memcpy(data, buffer, databytes); - } else if(command[0] == DATA_OUT) { - if(databytes > 0) { - if(data == NULL) { - DBG("Internal driver error: data is NULL\n"); - retval = -EINVAL; - } else { - if((err=WritePacket(cam, data, databytes)) < 0){ - DBG("Error writing command data\n"); - return err; - } - } - } - } else { - DBG("Unexpected first byte of command: %x\n", command[0]); - retval = -EINVAL; - } - return retval; -} - -/**************************************************************************** - * - * cpia_pp_open - * - ***************************************************************************/ -static int cpia_pp_open(void *privdata) -{ - struct pp_cam_entry *cam = (struct pp_cam_entry *)privdata; - - if (cam == NULL) - return -EINVAL; - - if(cam->open_count == 0) { - if (parport_claim(cam->pdev)) { - DBG("failed to claim the port\n"); - return -EBUSY; - } - parport_negotiate(cam->port, IEEE1284_MODE_COMPAT); - parport_data_forward(cam->port); - parport_write_control(cam->port, PARPORT_CONTROL_SELECT); - udelay(50); - parport_write_control(cam->port, - PARPORT_CONTROL_SELECT - | PARPORT_CONTROL_INIT); - } - - ++cam->open_count; - - return 0; -} - -/**************************************************************************** - * - * cpia_pp_registerCallback - * - ***************************************************************************/ -static int cpia_pp_registerCallback(void *privdata, void (*cb)(void *cbdata), void *cbdata) -{ - struct pp_cam_entry *cam = privdata; - int retval = 0; - - if(cam->port->irq != PARPORT_IRQ_NONE) { - cam->cb_func = cb; - cam->cb_data = cbdata; - INIT_WORK(&cam->cb_task, cpia_pp_run_callback); - } else { - retval = -1; - } - return retval; -} - -/**************************************************************************** - * - * cpia_pp_close - * - ***************************************************************************/ -static int cpia_pp_close(void *privdata) -{ - struct pp_cam_entry *cam = privdata; - if (--cam->open_count == 0) { - parport_release(cam->pdev); - } - return 0; -} - -/**************************************************************************** - * - * cpia_pp_register - * - ***************************************************************************/ -static int cpia_pp_register(struct parport *port) -{ - struct pardevice *pdev = NULL; - struct pp_cam_entry *cam; - struct cam_data *cpia; - - if (!(port->modes & PARPORT_MODE_PCSPP)) { - LOG("port is not supported by CPiA driver\n"); - return -ENXIO; - } - - cam = kzalloc(sizeof(struct pp_cam_entry), GFP_KERNEL); - if (cam == NULL) { - LOG("failed to allocate camera structure\n"); - return -ENOMEM; - } - - pdev = parport_register_device(port, "cpia_pp", NULL, NULL, - NULL, 0, cam); - - if (!pdev) { - LOG("failed to parport_register_device\n"); - kfree(cam); - return -ENXIO; - } - - cam->pdev = pdev; - cam->port = port; - init_waitqueue_head(&cam->wq_stream); - - cam->streaming = 0; - cam->stream_irq = 0; - - if((cpia = cpia_register_camera(&cpia_pp_ops, cam)) == NULL) { - LOG("failed to cpia_register_camera\n"); - parport_unregister_device(pdev); - kfree(cam); - return -ENXIO; - } - spin_lock( &cam_list_lock_pp ); - list_add( &cpia->cam_data_list, &cam_list ); - spin_unlock( &cam_list_lock_pp ); - - return 0; -} - -static void cpia_pp_detach (struct parport *port) -{ - struct list_head *tmp; - struct cam_data *cpia = NULL; - struct pp_cam_entry *cam; - - spin_lock( &cam_list_lock_pp ); - list_for_each (tmp, &cam_list) { - cpia = list_entry(tmp, struct cam_data, cam_data_list); - cam = (struct pp_cam_entry *) cpia->lowlevel_data; - if (cam && cam->port->number == port->number) { - list_del(&cpia->cam_data_list); - break; - } - cpia = NULL; - } - spin_unlock( &cam_list_lock_pp ); - - if (!cpia) { - DBG("cpia_pp_detach failed to find cam_data in cam_list\n"); - return; - } - - cam = (struct pp_cam_entry *) cpia->lowlevel_data; - cpia_unregister_camera(cpia); - if(cam->open_count > 0) - cpia_pp_close(cam); - parport_unregister_device(cam->pdev); - cpia->lowlevel_data = NULL; - kfree(cam); -} - -static void cpia_pp_attach (struct parport *port) -{ - unsigned int i; - - switch (parport_nr[0]) - { - case PPCPIA_PARPORT_UNSPEC: - case PPCPIA_PARPORT_AUTO: - if (port->probe_info[0].class != PARPORT_CLASS_MEDIA || - port->probe_info[0].cmdset == NULL || - strncmp(port->probe_info[0].cmdset, "CPIA_1", 6) != 0) - return; - - cpia_pp_register(port); - - break; - - default: - for (i = 0; i < PARPORT_MAX; ++i) { - if (port->number == parport_nr[i]) { - cpia_pp_register(port); - break; - } - } - break; - } -} - -static struct parport_driver cpia_pp_driver = { - .name = "cpia_pp", - .attach = cpia_pp_attach, - .detach = cpia_pp_detach, -}; - -static int __init cpia_pp_init(void) -{ - printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT, - CPIA_PP_MAJ_VER,CPIA_PP_MIN_VER,CPIA_PP_PATCH_VER); - - if(parport_nr[0] == PPCPIA_PARPORT_OFF) { - printk(" disabled\n"); - return 0; - } - - spin_lock_init( &cam_list_lock_pp ); - - if (parport_register_driver (&cpia_pp_driver)) { - LOG ("unable to register with parport\n"); - return -EIO; - } - return 0; -} - -static int __init cpia_init(void) -{ - if (parport[0]) { - /* The user gave some parameters. Let's see what they were. */ - if (!strncmp(parport[0], "auto", 4)) { - parport_nr[0] = PPCPIA_PARPORT_AUTO; - } else { - int n; - for (n = 0; n < PARPORT_MAX && parport[n]; n++) { - if (!strncmp(parport[n], "none", 4)) { - parport_nr[n] = PPCPIA_PARPORT_NONE; - } else { - char *ep; - unsigned long r = simple_strtoul(parport[n], &ep, 0); - if (ep != parport[n]) { - parport_nr[n] = r; - } else { - LOG("bad port specifier `%s'\n", parport[n]); - return -ENODEV; - } - } - } - } - } - return cpia_pp_init(); -} - -static void __exit cpia_cleanup(void) -{ - parport_unregister_driver(&cpia_pp_driver); - return; -} - -module_init(cpia_init); -module_exit(cpia_cleanup); diff --git a/drivers/staging/cpia/cpia_usb.c b/drivers/staging/cpia/cpia_usb.c deleted file mode 100644 index 58d193ff591c..000000000000 --- a/drivers/staging/cpia/cpia_usb.c +++ /dev/null @@ -1,640 +0,0 @@ -/* - * cpia_usb CPiA USB driver - * - * Supports CPiA based parallel port Video Camera's. - * - * Copyright (C) 1999 Jochen Scharrlach - * Copyright (C) 1999, 2000 Johannes Erdfelt - * - * 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. - */ - -/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */ -/* #define _CPIA_DEBUG_ 1 */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cpia.h" - -#define USB_REQ_CPIA_GRAB_FRAME 0xC1 -#define USB_REQ_CPIA_UPLOAD_FRAME 0xC2 -#define WAIT_FOR_NEXT_FRAME 0 -#define FORCE_FRAME_UPLOAD 1 - -#define FRAMES_PER_DESC 10 -#define FRAME_SIZE_PER_DESC 960 /* Shouldn't be hardcoded */ -#define CPIA_NUMSBUF 2 -#define STREAM_BUF_SIZE (PAGE_SIZE * 4) -#define SCRATCH_BUF_SIZE (STREAM_BUF_SIZE * 2) - -struct cpia_sbuf { - char *data; - struct urb *urb; -}; - -#define FRAMEBUF_LEN (CPIA_MAX_FRAME_SIZE+100) -enum framebuf_status { - FRAME_EMPTY, - FRAME_READING, - FRAME_READY, - FRAME_ERROR, -}; - -struct framebuf { - int length; - enum framebuf_status status; - u8 data[FRAMEBUF_LEN]; - struct framebuf *next; -}; - -struct usb_cpia { - /* Device structure */ - struct usb_device *dev; - - unsigned char iface; - wait_queue_head_t wq_stream; - - int cursbuf; /* Current receiving sbuf */ - struct cpia_sbuf sbuf[CPIA_NUMSBUF]; /* Double buffering */ - - int streaming; - int open; - int present; - struct framebuf *buffers[3]; - struct framebuf *curbuff, *workbuff; -}; - -static int cpia_usb_open(void *privdata); -static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata), - void *cbdata); -static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data); -static int cpia_usb_streamStart(void *privdata); -static int cpia_usb_streamStop(void *privdata); -static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock); -static int cpia_usb_close(void *privdata); - -#define ABOUT "USB driver for Vision CPiA based cameras" - -static struct cpia_camera_ops cpia_usb_ops = { - cpia_usb_open, - cpia_usb_registerCallback, - cpia_usb_transferCmd, - cpia_usb_streamStart, - cpia_usb_streamStop, - cpia_usb_streamRead, - cpia_usb_close, - 0, - THIS_MODULE -}; - -static LIST_HEAD(cam_list); -static spinlock_t cam_list_lock_usb; - -static void cpia_usb_complete(struct urb *urb) -{ - int i; - char *cdata; - struct usb_cpia *ucpia; - - if (!urb || !urb->context) - return; - - ucpia = (struct usb_cpia *) urb->context; - - if (!ucpia->dev || !ucpia->streaming || !ucpia->present || !ucpia->open) - return; - - if (ucpia->workbuff->status == FRAME_EMPTY) { - ucpia->workbuff->status = FRAME_READING; - ucpia->workbuff->length = 0; - } - - for (i = 0; i < urb->number_of_packets; i++) { - int n = urb->iso_frame_desc[i].actual_length; - int st = urb->iso_frame_desc[i].status; - - cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - - if (st) - printk(KERN_DEBUG "cpia data error: [%d] len=%d, status=%X\n", i, n, st); - - if (FRAMEBUF_LEN < ucpia->workbuff->length + n) { - printk(KERN_DEBUG "cpia: scratch buf overflow!scr_len: %d, n: %d\n", ucpia->workbuff->length, n); - return; - } - - if (n) { - if ((ucpia->workbuff->length > 0) || - (0x19 == cdata[0] && 0x68 == cdata[1])) { - memcpy(ucpia->workbuff->data + ucpia->workbuff->length, cdata, n); - ucpia->workbuff->length += n; - } else - DBG("Ignoring packet!\n"); - } else { - if (ucpia->workbuff->length > 4 && - 0xff == ucpia->workbuff->data[ucpia->workbuff->length-1] && - 0xff == ucpia->workbuff->data[ucpia->workbuff->length-2] && - 0xff == ucpia->workbuff->data[ucpia->workbuff->length-3] && - 0xff == ucpia->workbuff->data[ucpia->workbuff->length-4]) { - ucpia->workbuff->status = FRAME_READY; - ucpia->curbuff = ucpia->workbuff; - ucpia->workbuff = ucpia->workbuff->next; - ucpia->workbuff->status = FRAME_EMPTY; - ucpia->workbuff->length = 0; - - if (waitqueue_active(&ucpia->wq_stream)) - wake_up_interruptible(&ucpia->wq_stream); - } - } - } - - /* resubmit */ - urb->dev = ucpia->dev; - if ((i = usb_submit_urb(urb, GFP_ATOMIC)) != 0) - printk(KERN_ERR "%s: usb_submit_urb ret %d\n", __func__, i); -} - -static int cpia_usb_open(void *privdata) -{ - struct usb_cpia *ucpia = (struct usb_cpia *) privdata; - struct urb *urb; - int ret, retval = 0, fx, err; - - if (!ucpia) - return -EINVAL; - - ucpia->sbuf[0].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL); - if (!ucpia->sbuf[0].data) - return -EINVAL; - - ucpia->sbuf[1].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL); - if (!ucpia->sbuf[1].data) { - retval = -EINVAL; - goto error_0; - } - - ret = usb_set_interface(ucpia->dev, ucpia->iface, 3); - if (ret < 0) { - printk(KERN_ERR "cpia_usb_open: usb_set_interface error (ret = %d)\n", ret); - retval = -EBUSY; - goto error_1; - } - - ucpia->buffers[0]->status = FRAME_EMPTY; - ucpia->buffers[0]->length = 0; - ucpia->buffers[1]->status = FRAME_EMPTY; - ucpia->buffers[1]->length = 0; - ucpia->buffers[2]->status = FRAME_EMPTY; - ucpia->buffers[2]->length = 0; - ucpia->curbuff = ucpia->buffers[0]; - ucpia->workbuff = ucpia->buffers[1]; - - /* We double buffer the Iso lists, and also know the polling - * interval is every frame (1 == (1 << (bInterval -1))). - */ - urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); - if (!urb) { - printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 0\n"); - retval = -ENOMEM; - goto error_1; - } - - ucpia->sbuf[0].urb = urb; - urb->dev = ucpia->dev; - urb->context = ucpia; - urb->pipe = usb_rcvisocpipe(ucpia->dev, 1); - urb->transfer_flags = URB_ISO_ASAP; - urb->transfer_buffer = ucpia->sbuf[0].data; - urb->complete = cpia_usb_complete; - urb->number_of_packets = FRAMES_PER_DESC; - urb->interval = 1; - urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; - for (fx = 0; fx < FRAMES_PER_DESC; fx++) { - urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx; - urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; - } - - urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); - if (!urb) { - printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 1\n"); - retval = -ENOMEM; - goto error_urb0; - } - - ucpia->sbuf[1].urb = urb; - urb->dev = ucpia->dev; - urb->context = ucpia; - urb->pipe = usb_rcvisocpipe(ucpia->dev, 1); - urb->transfer_flags = URB_ISO_ASAP; - urb->transfer_buffer = ucpia->sbuf[1].data; - urb->complete = cpia_usb_complete; - urb->number_of_packets = FRAMES_PER_DESC; - urb->interval = 1; - urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; - for (fx = 0; fx < FRAMES_PER_DESC; fx++) { - urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx; - urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; - } - - /* queue the ISO urbs, and resubmit in the completion handler */ - err = usb_submit_urb(ucpia->sbuf[0].urb, GFP_KERNEL); - if (err) { - printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 0 ret %d\n", - err); - goto error_urb1; - } - err = usb_submit_urb(ucpia->sbuf[1].urb, GFP_KERNEL); - if (err) { - printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 1 ret %d\n", - err); - goto error_urb1; - } - - ucpia->streaming = 1; - ucpia->open = 1; - - return 0; - -error_urb1: /* free urb 1 */ - usb_free_urb(ucpia->sbuf[1].urb); - ucpia->sbuf[1].urb = NULL; -error_urb0: /* free urb 0 */ - usb_free_urb(ucpia->sbuf[0].urb); - ucpia->sbuf[0].urb = NULL; -error_1: - kfree (ucpia->sbuf[1].data); - ucpia->sbuf[1].data = NULL; -error_0: - kfree (ucpia->sbuf[0].data); - ucpia->sbuf[0].data = NULL; - - return retval; -} - -// -// convenience functions -// - -/**************************************************************************** - * - * WritePacket - * - ***************************************************************************/ -static int WritePacket(struct usb_device *udev, const u8 *packet, u8 *buf, size_t size) -{ - if (!packet) - return -EINVAL; - - return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - packet[1] + (packet[0] << 8), - USB_TYPE_VENDOR | USB_RECIP_DEVICE, - packet[2] + (packet[3] << 8), - packet[4] + (packet[5] << 8), buf, size, 1000); -} - -/**************************************************************************** - * - * ReadPacket - * - ***************************************************************************/ -static int ReadPacket(struct usb_device *udev, u8 *packet, u8 *buf, size_t size) -{ - if (!packet || size <= 0) - return -EINVAL; - - return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), - packet[1] + (packet[0] << 8), - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - packet[2] + (packet[3] << 8), - packet[4] + (packet[5] << 8), buf, size, 1000); -} - -static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data) -{ - int err = 0; - int databytes; - struct usb_cpia *ucpia = (struct usb_cpia *)privdata; - struct usb_device *udev = ucpia->dev; - - if (!udev) { - DBG("Internal driver error: udev is NULL\n"); - return -EINVAL; - } - - if (!command) { - DBG("Internal driver error: command is NULL\n"); - return -EINVAL; - } - - databytes = (((int)command[7])<<8) | command[6]; - - if (command[0] == DATA_IN) { - u8 buffer[8]; - - if (!data) { - DBG("Internal driver error: data is NULL\n"); - return -EINVAL; - } - - err = ReadPacket(udev, command, buffer, 8); - if (err < 0) - return err; - - memcpy(data, buffer, databytes); - } else if(command[0] == DATA_OUT) - WritePacket(udev, command, data, databytes); - else { - DBG("Unexpected first byte of command: %x\n", command[0]); - err = -EINVAL; - } - - return 0; -} - -static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata), - void *cbdata) -{ - return -ENODEV; -} - -static int cpia_usb_streamStart(void *privdata) -{ - return -ENODEV; -} - -static int cpia_usb_streamStop(void *privdata) -{ - return -ENODEV; -} - -static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock) -{ - struct usb_cpia *ucpia = (struct usb_cpia *) privdata; - struct framebuf *mybuff; - - if (!ucpia || !ucpia->present) - return -1; - - if (ucpia->curbuff->status != FRAME_READY) - interruptible_sleep_on(&ucpia->wq_stream); - else - DBG("Frame already waiting!\n"); - - mybuff = ucpia->curbuff; - - if (!mybuff) - return -1; - - if (mybuff->status != FRAME_READY || mybuff->length < 4) { - DBG("Something went wrong!\n"); - return -1; - } - - memcpy(frame, mybuff->data, mybuff->length); - mybuff->status = FRAME_EMPTY; - -/* DBG("read done, %d bytes, Header: %x/%x, Footer: %x%x%x%x\n", */ -/* mybuff->length, frame[0], frame[1], */ -/* frame[mybuff->length-4], frame[mybuff->length-3], */ -/* frame[mybuff->length-2], frame[mybuff->length-1]); */ - - return mybuff->length; -} - -static void cpia_usb_free_resources(struct usb_cpia *ucpia, int try) -{ - if (!ucpia->streaming) - return; - - ucpia->streaming = 0; - - /* Set packet size to 0 */ - if (try) { - int ret; - - ret = usb_set_interface(ucpia->dev, ucpia->iface, 0); - if (ret < 0) { - printk(KERN_ERR "usb_set_interface error (ret = %d)\n", ret); - return; - } - } - - /* Unschedule all of the iso td's */ - if (ucpia->sbuf[1].urb) { - usb_kill_urb(ucpia->sbuf[1].urb); - usb_free_urb(ucpia->sbuf[1].urb); - ucpia->sbuf[1].urb = NULL; - } - - kfree(ucpia->sbuf[1].data); - ucpia->sbuf[1].data = NULL; - - if (ucpia->sbuf[0].urb) { - usb_kill_urb(ucpia->sbuf[0].urb); - usb_free_urb(ucpia->sbuf[0].urb); - ucpia->sbuf[0].urb = NULL; - } - - kfree(ucpia->sbuf[0].data); - ucpia->sbuf[0].data = NULL; -} - -static int cpia_usb_close(void *privdata) -{ - struct usb_cpia *ucpia = (struct usb_cpia *) privdata; - - if(!ucpia) - return -ENODEV; - - ucpia->open = 0; - - /* ucpia->present = 0 protects against trying to reset the - * alt setting if camera is physically disconnected while open */ - cpia_usb_free_resources(ucpia, ucpia->present); - - return 0; -} - -/* Probing and initializing */ - -static int cpia_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct usb_host_interface *interface; - struct usb_cpia *ucpia; - struct cam_data *cam; - int ret; - - /* A multi-config CPiA camera? */ - if (udev->descriptor.bNumConfigurations != 1) - return -ENODEV; - - interface = intf->cur_altsetting; - - printk(KERN_INFO "USB CPiA camera found\n"); - - ucpia = kzalloc(sizeof(*ucpia), GFP_KERNEL); - if (!ucpia) { - printk(KERN_ERR "couldn't kmalloc cpia struct\n"); - return -ENOMEM; - } - - ucpia->dev = udev; - ucpia->iface = interface->desc.bInterfaceNumber; - init_waitqueue_head(&ucpia->wq_stream); - - ucpia->buffers[0] = vmalloc(sizeof(*ucpia->buffers[0])); - if (!ucpia->buffers[0]) { - printk(KERN_ERR "couldn't vmalloc frame buffer 0\n"); - goto fail_alloc_0; - } - - ucpia->buffers[1] = vmalloc(sizeof(*ucpia->buffers[1])); - if (!ucpia->buffers[1]) { - printk(KERN_ERR "couldn't vmalloc frame buffer 1\n"); - goto fail_alloc_1; - } - - ucpia->buffers[2] = vmalloc(sizeof(*ucpia->buffers[2])); - if (!ucpia->buffers[2]) { - printk(KERN_ERR "couldn't vmalloc frame buffer 2\n"); - goto fail_alloc_2; - } - - ucpia->buffers[0]->next = ucpia->buffers[1]; - ucpia->buffers[1]->next = ucpia->buffers[2]; - ucpia->buffers[2]->next = ucpia->buffers[0]; - - ret = usb_set_interface(udev, ucpia->iface, 0); - if (ret < 0) { - printk(KERN_ERR "cpia_probe: usb_set_interface error (ret = %d)\n", ret); - /* goto fail_all; */ - } - - /* Before register_camera, important */ - ucpia->present = 1; - - cam = cpia_register_camera(&cpia_usb_ops, ucpia); - if (!cam) { - LOG("failed to cpia_register_camera\n"); - goto fail_all; - } - - spin_lock( &cam_list_lock_usb ); - list_add( &cam->cam_data_list, &cam_list ); - spin_unlock( &cam_list_lock_usb ); - - usb_set_intfdata(intf, cam); - return 0; - -fail_all: - vfree(ucpia->buffers[2]); - ucpia->buffers[2] = NULL; -fail_alloc_2: - vfree(ucpia->buffers[1]); - ucpia->buffers[1] = NULL; -fail_alloc_1: - vfree(ucpia->buffers[0]); - ucpia->buffers[0] = NULL; -fail_alloc_0: - kfree(ucpia); - return -EIO; -} - -static void cpia_disconnect(struct usb_interface *intf); - -static struct usb_device_id cpia_id_table [] = { - { USB_DEVICE(0x0553, 0x0002) }, - { USB_DEVICE(0x0813, 0x0001) }, - { } /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE (usb, cpia_id_table); -MODULE_LICENSE("GPL"); - - -static struct usb_driver cpia_driver = { - .name = "cpia", - .probe = cpia_probe, - .disconnect = cpia_disconnect, - .id_table = cpia_id_table, -}; - -static void cpia_disconnect(struct usb_interface *intf) -{ - struct cam_data *cam = usb_get_intfdata(intf); - struct usb_cpia *ucpia; - - usb_set_intfdata(intf, NULL); - if (!cam) - return; - - ucpia = (struct usb_cpia *) cam->lowlevel_data; - spin_lock( &cam_list_lock_usb ); - list_del(&cam->cam_data_list); - spin_unlock( &cam_list_lock_usb ); - - ucpia->present = 0; - - cpia_unregister_camera(cam); - if(ucpia->open) - cpia_usb_close(cam->lowlevel_data); - - ucpia->curbuff->status = FRAME_ERROR; - - if (waitqueue_active(&ucpia->wq_stream)) - wake_up_interruptible(&ucpia->wq_stream); - - ucpia->curbuff = ucpia->workbuff = NULL; - - vfree(ucpia->buffers[2]); - ucpia->buffers[2] = NULL; - - vfree(ucpia->buffers[1]); - ucpia->buffers[1] = NULL; - - vfree(ucpia->buffers[0]); - ucpia->buffers[0] = NULL; - - cam->lowlevel_data = NULL; - kfree(ucpia); -} - -static int __init usb_cpia_init(void) -{ - printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT, - CPIA_USB_MAJ_VER,CPIA_USB_MIN_VER,CPIA_USB_PATCH_VER); - - spin_lock_init(&cam_list_lock_usb); - return usb_register(&cpia_driver); -} - -static void __exit usb_cpia_cleanup(void) -{ - usb_deregister(&cpia_driver); -} - - -module_init (usb_cpia_init); -module_exit (usb_cpia_cleanup); - diff --git a/drivers/staging/stradis/Kconfig b/drivers/staging/stradis/Kconfig deleted file mode 100644 index 02f0fc504cf5..000000000000 --- a/drivers/staging/stradis/Kconfig +++ /dev/null @@ -1,7 +0,0 @@ -config VIDEO_STRADIS - tristate "Stradis 4:2:2 MPEG-2 video driver (DEPRECATED)" - depends on EXPERIMENTAL && PCI && VIDEO_V4L1 && VIRT_TO_BUS && BKL - help - Say Y here to enable support for the Stradis 4:2:2 MPEG-2 video - driver for PCI. There is a product page at - . diff --git a/drivers/staging/stradis/Makefile b/drivers/staging/stradis/Makefile deleted file mode 100644 index 0f1feab59e39..000000000000 --- a/drivers/staging/stradis/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -obj-$(CONFIG_VIDEO_STRADIS) += stradis.o - -EXTRA_CFLAGS += -Idrivers/media/video diff --git a/drivers/staging/stradis/TODO b/drivers/staging/stradis/TODO deleted file mode 100644 index f48150fe2fa9..000000000000 --- a/drivers/staging/stradis/TODO +++ /dev/null @@ -1,6 +0,0 @@ -This is an obsolete driver for ancient stradis hardware. -We couldn't find anyone with this hardware in order to port it to use V4L2. - -If nobody take care on it, the driver will be removed for 2.6.38. - -Please send patches to linux-media@vger.kernel.org diff --git a/drivers/staging/stradis/stradis.c b/drivers/staging/stradis/stradis.c deleted file mode 100644 index 807dd7eb748f..000000000000 --- a/drivers/staging/stradis/stradis.c +++ /dev/null @@ -1,2222 +0,0 @@ -/* - * stradis.c - stradis 4:2:2 mpeg decoder driver - * - * Stradis 4:2:2 MPEG-2 Decoder Driver - * Copyright (C) 1999 Nathan Laredo - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "saa7146.h" -#include "saa7146reg.h" -#include "ibmmpeg2.h" -#include "saa7121.h" -#include "cs8420.h" - -#define DEBUG(x) /* debug driver */ -#undef IDEBUG /* debug irq handler */ -#undef MDEBUG /* debug memory management */ - -#define SAA7146_MAX 6 - -static struct saa7146 saa7146s[SAA7146_MAX]; - -static int saa_num; /* number of SAA7146s in use */ - -static int video_nr = -1; -module_param(video_nr, int, 0); -MODULE_LICENSE("GPL"); - -#define nDebNormal 0x00480000 -#define nDebNoInc 0x00480000 -#define nDebVideo 0xd0480000 -#define nDebAudio 0xd0400000 -#define nDebDMA 0x02c80000 - -#define oDebNormal 0x13c80000 -#define oDebNoInc 0x13c80000 -#define oDebVideo 0xd1080000 -#define oDebAudio 0xd1080000 -#define oDebDMA 0x03080000 - -#define NewCard (saa->boardcfg[3]) -#define ChipControl (saa->boardcfg[1]) -#define NTSCFirstActive (saa->boardcfg[4]) -#define PALFirstActive (saa->boardcfg[5]) -#define NTSCLastActive (saa->boardcfg[54]) -#define PALLastActive (saa->boardcfg[55]) -#define Have2MB (saa->boardcfg[18] & 0x40) -#define HaveCS8420 (saa->boardcfg[18] & 0x04) -#define IBMMPEGCD20 (saa->boardcfg[18] & 0x20) -#define HaveCS3310 (saa->boardcfg[18] & 0x01) -#define CS3310MaxLvl ((saa->boardcfg[30] << 8) | saa->boardcfg[31]) -#define HaveCS4341 (saa->boardcfg[40] == 2) -#define SDIType (saa->boardcfg[27]) -#define CurrentMode (saa->boardcfg[2]) - -#define debNormal (NewCard ? nDebNormal : oDebNormal) -#define debNoInc (NewCard ? nDebNoInc : oDebNoInc) -#define debVideo (NewCard ? nDebVideo : oDebVideo) -#define debAudio (NewCard ? nDebAudio : oDebAudio) -#define debDMA (NewCard ? nDebDMA : oDebDMA) - -#ifdef USE_RESCUE_EEPROM_SDM275 -static unsigned char rescue_eeprom[64] = { - 0x00, 0x01, 0x04, 0x13, 0x26, 0x0f, 0x10, 0x00, 0x00, 0x00, 0x43, 0x63, - 0x22, 0x01, 0x29, 0x15, 0x73, 0x00, 0x1f, 'd', 'e', 'c', 'x', 'l', - 'd', 'v', 'a', 0x02, 0x00, 0x01, 0x00, 0xcc, 0xa4, 0x63, 0x09, 0xe2, - 0x10, 0x00, 0x0a, 0x00, 0x02, 0x02, 'd', 'e', 'c', 'x', 'l', 'a', - 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, -}; -#endif - -/* ----------------------------------------------------------------------- */ -/* Hardware I2C functions */ -static void I2CWipe(struct saa7146 *saa) -{ - int i; - /* set i2c to ~=100kHz, abort transfer, clear busy */ - saawrite(0x600 | SAA7146_I2C_ABORT, SAA7146_I2C_STATUS); - saawrite((SAA7146_MC2_UPLD_I2C << 16) | - SAA7146_MC2_UPLD_I2C, SAA7146_MC2); - /* wait for i2c registers to be programmed */ - for (i = 0; i < 1000 && - !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++) - schedule(); - saawrite(0x600, SAA7146_I2C_STATUS); - saawrite((SAA7146_MC2_UPLD_I2C << 16) | - SAA7146_MC2_UPLD_I2C, SAA7146_MC2); - /* wait for i2c registers to be programmed */ - for (i = 0; i < 1000 && - !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++) - schedule(); - saawrite(0x600, SAA7146_I2C_STATUS); - saawrite((SAA7146_MC2_UPLD_I2C << 16) | - SAA7146_MC2_UPLD_I2C, SAA7146_MC2); - /* wait for i2c registers to be programmed */ - for (i = 0; i < 1000 && - !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++) - schedule(); -} - -/* read I2C */ -static int I2CRead(struct saa7146 *saa, unsigned char addr, - unsigned char subaddr, int dosub) -{ - int i; - - if (saaread(SAA7146_I2C_STATUS) & 0x3c) - I2CWipe(saa); - for (i = 0; - i < 1000 && (saaread(SAA7146_I2C_STATUS) & SAA7146_I2C_BUSY); - i++) - schedule(); - if (i == 1000) - I2CWipe(saa); - if (dosub) - saawrite(((addr & 0xfe) << 24) | (((addr | 1) & 0xff) << 8) | - ((subaddr & 0xff) << 16) | 0xed, SAA7146_I2C_TRANSFER); - else - saawrite(((addr & 0xfe) << 24) | (((addr | 1) & 0xff) << 16) | - 0xf1, SAA7146_I2C_TRANSFER); - saawrite((SAA7146_MC2_UPLD_I2C << 16) | - SAA7146_MC2_UPLD_I2C, SAA7146_MC2); - /* wait for i2c registers to be programmed */ - for (i = 0; i < 1000 && - !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++) - schedule(); - /* wait for valid data */ - for (i = 0; i < 1000 && - (saaread(SAA7146_I2C_STATUS) & SAA7146_I2C_BUSY); i++) - schedule(); - if (saaread(SAA7146_I2C_STATUS) & SAA7146_I2C_ERR) - return -1; - if (i == 1000) - printk("i2c setup read timeout\n"); - saawrite(0x41, SAA7146_I2C_TRANSFER); - saawrite((SAA7146_MC2_UPLD_I2C << 16) | - SAA7146_MC2_UPLD_I2C, SAA7146_MC2); - /* wait for i2c registers to be programmed */ - for (i = 0; i < 1000 && - !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++) - schedule(); - /* wait for valid data */ - for (i = 0; i < 1000 && - (saaread(SAA7146_I2C_TRANSFER) & SAA7146_I2C_BUSY); i++) - schedule(); - if (saaread(SAA7146_I2C_TRANSFER) & SAA7146_I2C_ERR) - return -1; - if (i == 1000) - printk("i2c read timeout\n"); - return ((saaread(SAA7146_I2C_TRANSFER) >> 24) & 0xff); -} - -/* set both to write both bytes, reset it to write only b1 */ - -static int I2CWrite(struct saa7146 *saa, unsigned char addr, unsigned char b1, - unsigned char b2, int both) -{ - int i; - u32 data; - - if (saaread(SAA7146_I2C_STATUS) & 0x3c) - I2CWipe(saa); - for (i = 0; i < 1000 && - (saaread(SAA7146_I2C_STATUS) & SAA7146_I2C_BUSY); i++) - schedule(); - if (i == 1000) - I2CWipe(saa); - data = ((addr & 0xfe) << 24) | ((b1 & 0xff) << 16); - if (both) - data |= ((b2 & 0xff) << 8) | 0xe5; - else - data |= 0xd1; - saawrite(data, SAA7146_I2C_TRANSFER); - saawrite((SAA7146_MC2_UPLD_I2C << 16) | SAA7146_MC2_UPLD_I2C, - SAA7146_MC2); - return 0; -} - -static void attach_inform(struct saa7146 *saa, int id) -{ - int i; - - DEBUG(printk(KERN_DEBUG "stradis%d: i2c: device found=%02x\n", saa->nr, - id)); - if (id == 0xa0) { /* we have rev2 or later board, fill in info */ - for (i = 0; i < 64; i++) - saa->boardcfg[i] = I2CRead(saa, 0xa0, i, 1); -#ifdef USE_RESCUE_EEPROM_SDM275 - if (saa->boardcfg[0] != 0) { - printk("stradis%d: WARNING: EEPROM STORED VALUES HAVE " - "BEEN IGNORED\n", saa->nr); - for (i = 0; i < 64; i++) - saa->boardcfg[i] = rescue_eeprom[i]; - } -#endif - printk("stradis%d: config =", saa->nr); - for (i = 0; i < 51; i++) { - printk(" %02x", saa->boardcfg[i]); - } - printk("\n"); - } -} - -static void I2CBusScan(struct saa7146 *saa) -{ - int i; - for (i = 0; i < 0xff; i += 2) - if ((I2CRead(saa, i, 0, 0)) >= 0) - attach_inform(saa, i); -} - -static int debiwait_maxwait; - -static int wait_for_debi_done(struct saa7146 *saa) -{ - int i; - - /* wait for registers to be programmed */ - for (i = 0; i < 100000 && - !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_DEBI); i++) - saaread(SAA7146_MC2); - /* wait for transfer to complete */ - for (i = 0; i < 500000 && - (saaread(SAA7146_PSR) & SAA7146_PSR_DEBI_S); i++) - saaread(SAA7146_MC2); - - if (i > debiwait_maxwait) - printk("wait-for-debi-done maxwait: %d\n", - debiwait_maxwait = i); - - if (i == 500000) - return -1; - - return 0; -} - -static int debiwrite(struct saa7146 *saa, u32 config, int addr, - u32 val, int count) -{ - u32 cmd; - if (count <= 0 || count > 32764) - return -1; - if (wait_for_debi_done(saa) < 0) - return -1; - saawrite(config, SAA7146_DEBI_CONFIG); - if (count <= 4) /* immediate transfer */ - saawrite(val, SAA7146_DEBI_AD); - else /* block transfer */ - saawrite(virt_to_bus(saa->dmadebi), SAA7146_DEBI_AD); - saawrite((cmd = (count << 17) | (addr & 0xffff)), SAA7146_DEBI_COMMAND); - saawrite((SAA7146_MC2_UPLD_DEBI << 16) | SAA7146_MC2_UPLD_DEBI, - SAA7146_MC2); - return 0; -} - -static u32 debiread(struct saa7146 *saa, u32 config, int addr, int count) -{ - u32 result = 0; - - if (count > 32764 || count <= 0) - return 0; - if (wait_for_debi_done(saa) < 0) - return 0; - saawrite(virt_to_bus(saa->dmadebi), SAA7146_DEBI_AD); - saawrite((count << 17) | 0x10000 | (addr & 0xffff), - SAA7146_DEBI_COMMAND); - saawrite(config, SAA7146_DEBI_CONFIG); - saawrite((SAA7146_MC2_UPLD_DEBI << 16) | SAA7146_MC2_UPLD_DEBI, - SAA7146_MC2); - if (count > 4) /* not an immediate transfer */ - return count; - wait_for_debi_done(saa); - result = saaread(SAA7146_DEBI_AD); - if (count == 1) - result &= 0xff; - if (count == 2) - result &= 0xffff; - if (count == 3) - result &= 0xffffff; - return result; -} - -static void do_irq_send_data(struct saa7146 *saa) -{ - int split, audbytes, vidbytes; - - saawrite(SAA7146_PSR_PIN1, SAA7146_IER); - /* if special feature mode in effect, disable audio sending */ - if (saa->playmode != VID_PLAY_NORMAL) - saa->audtail = saa->audhead = 0; - if (saa->audhead <= saa->audtail) - audbytes = saa->audtail - saa->audhead; - else - audbytes = 65536 - (saa->audhead - saa->audtail); - if (saa->vidhead <= saa->vidtail) - vidbytes = saa->vidtail - saa->vidhead; - else - vidbytes = 524288 - (saa->vidhead - saa->vidtail); - if (audbytes == 0 && vidbytes == 0 && saa->osdtail == saa->osdhead) { - saawrite(0, SAA7146_IER); - return; - } - /* if at least 1 block audio waiting and audio fifo isn't full */ - if (audbytes >= 2048 && (debiread(saa, debNormal, IBM_MP2_AUD_FIFO, 2) - & 0xff) < 60) { - if (saa->audhead > saa->audtail) - split = 65536 - saa->audhead; - else - split = 0; - audbytes = 2048; - if (split > 0 && split < 2048) { - memcpy(saa->dmadebi, saa->audbuf + saa->audhead, split); - saa->audhead = 0; - audbytes -= split; - } else - split = 0; - memcpy(saa->dmadebi + split, saa->audbuf + saa->audhead, - audbytes); - saa->audhead += audbytes; - saa->audhead &= 0xffff; - debiwrite(saa, debAudio, (NewCard ? IBM_MP2_AUD_FIFO : - IBM_MP2_AUD_FIFOW), 0, 2048); - wake_up_interruptible(&saa->audq); - /* if at least 1 block video waiting and video fifo isn't full */ - } else if (vidbytes >= 30720 && (debiread(saa, debNormal, - IBM_MP2_FIFO, 2)) < 16384) { - if (saa->vidhead > saa->vidtail) - split = 524288 - saa->vidhead; - else - split = 0; - vidbytes = 30720; - if (split > 0 && split < 30720) { - memcpy(saa->dmadebi, saa->vidbuf + saa->vidhead, split); - saa->vidhead = 0; - vidbytes -= split; - } else - split = 0; - memcpy(saa->dmadebi + split, saa->vidbuf + saa->vidhead, - vidbytes); - saa->vidhead += vidbytes; - saa->vidhead &= 0x7ffff; - debiwrite(saa, debVideo, (NewCard ? IBM_MP2_FIFO : - IBM_MP2_FIFOW), 0, 30720); - wake_up_interruptible(&saa->vidq); - } - saawrite(SAA7146_PSR_DEBI_S | SAA7146_PSR_PIN1, SAA7146_IER); -} - -static void send_osd_data(struct saa7146 *saa) -{ - int size = saa->osdtail - saa->osdhead; - if (size > 30720) - size = 30720; - /* ensure some multiple of 8 bytes is transferred */ - size = 8 * ((size + 8) >> 3); - if (size) { - debiwrite(saa, debNormal, IBM_MP2_OSD_ADDR, - (saa->osdhead >> 3), 2); - memcpy(saa->dmadebi, &saa->osdbuf[saa->osdhead], size); - saa->osdhead += size; - /* block transfer of next 8 bytes to ~32k bytes */ - debiwrite(saa, debNormal, IBM_MP2_OSD_DATA, 0, size); - } - if (saa->osdhead >= saa->osdtail) { - saa->osdhead = saa->osdtail = 0; - debiwrite(saa, debNormal, IBM_MP2_MASK0, 0xc00c, 2); - } -} - -static irqreturn_t saa7146_irq(int irq, void *dev_id) -{ - struct saa7146 *saa = dev_id; - u32 stat, astat; - int count; - int handled = 0; - - count = 0; - while (1) { - /* get/clear interrupt status bits */ - stat = saaread(SAA7146_ISR); - astat = stat & saaread(SAA7146_IER); - if (!astat) - break; - handled = 1; - saawrite(astat, SAA7146_ISR); - if (astat & SAA7146_PSR_DEBI_S) { - do_irq_send_data(saa); - } - if (astat & SAA7146_PSR_PIN1) { - int istat; - /* the following read will trigger DEBI_S */ - istat = debiread(saa, debNormal, IBM_MP2_HOST_INT, 2); - if (istat & 1) { - saawrite(0, SAA7146_IER); - send_osd_data(saa); - saawrite(SAA7146_PSR_DEBI_S | - SAA7146_PSR_PIN1, SAA7146_IER); - } - if (istat & 0x20) { /* Video Start */ - saa->vidinfo.frame_count++; - } - if (istat & 0x400) { /* Picture Start */ - /* update temporal reference */ - } - if (istat & 0x200) { /* Picture Resolution Change */ - /* read new resolution */ - } - if (istat & 0x100) { /* New User Data found */ - /* read new user data */ - } - if (istat & 0x1000) { /* new GOP/SMPTE */ - /* read new SMPTE */ - } - if (istat & 0x8000) { /* Sequence Start Code */ - /* reset frame counter, load sizes */ - saa->vidinfo.frame_count = 0; - saa->vidinfo.h_size = 704; - saa->vidinfo.v_size = 480; -#if 0 - if (saa->endmarkhead != saa->endmarktail) { - saa->audhead = - saa->endmark[saa->endmarkhead]; - saa->endmarkhead++; - if (saa->endmarkhead >= MAX_MARKS) - saa->endmarkhead = 0; - } -#endif - } - if (istat & 0x4000) { /* Sequence Error Code */ - if (saa->endmarkhead != saa->endmarktail) { - saa->audhead = - saa->endmark[saa->endmarkhead]; - saa->endmarkhead++; - if (saa->endmarkhead >= MAX_MARKS) - saa->endmarkhead = 0; - } - } - } -#ifdef IDEBUG - if (astat & SAA7146_PSR_PPEF) { - IDEBUG(printk("stradis%d irq: PPEF\n", saa->nr)); - } - if (astat & SAA7146_PSR_PABO) { - IDEBUG(printk("stradis%d irq: PABO\n", saa->nr)); - } - if (astat & SAA7146_PSR_PPED) { - IDEBUG(printk("stradis%d irq: PPED\n", saa->nr)); - } - if (astat & SAA7146_PSR_RPS_I1) { - IDEBUG(printk("stradis%d irq: RPS_I1\n", saa->nr)); - } - if (astat & SAA7146_PSR_RPS_I0) { - IDEBUG(printk("stradis%d irq: RPS_I0\n", saa->nr)); - } - if (astat & SAA7146_PSR_RPS_LATE1) { - IDEBUG(printk("stradis%d irq: RPS_LATE1\n", saa->nr)); - } - if (astat & SAA7146_PSR_RPS_LATE0) { - IDEBUG(printk("stradis%d irq: RPS_LATE0\n", saa->nr)); - } - if (astat & SAA7146_PSR_RPS_E1) { - IDEBUG(printk("stradis%d irq: RPS_E1\n", saa->nr)); - } - if (astat & SAA7146_PSR_RPS_E0) { - IDEBUG(printk("stradis%d irq: RPS_E0\n", saa->nr)); - } - if (astat & SAA7146_PSR_RPS_TO1) { - IDEBUG(printk("stradis%d irq: RPS_TO1\n", saa->nr)); - } - if (astat & SAA7146_PSR_RPS_TO0) { - IDEBUG(printk("stradis%d irq: RPS_TO0\n", saa->nr)); - } - if (astat & SAA7146_PSR_UPLD) { - IDEBUG(printk("stradis%d irq: UPLD\n", saa->nr)); - } - if (astat & SAA7146_PSR_DEBI_E) { - IDEBUG(printk("stradis%d irq: DEBI_E\n", saa->nr)); - } - if (astat & SAA7146_PSR_I2C_S) { - IDEBUG(printk("stradis%d irq: I2C_S\n", saa->nr)); - } - if (astat & SAA7146_PSR_I2C_E) { - IDEBUG(printk("stradis%d irq: I2C_E\n", saa->nr)); - } - if (astat & SAA7146_PSR_A2_IN) { - IDEBUG(printk("stradis%d irq: A2_IN\n", saa->nr)); - } - if (astat & SAA7146_PSR_A2_OUT) { - IDEBUG(printk("stradis%d irq: A2_OUT\n", saa->nr)); - } - if (astat & SAA7146_PSR_A1_IN) { - IDEBUG(printk("stradis%d irq: A1_IN\n", saa->nr)); - } - if (astat & SAA7146_PSR_A1_OUT) { - IDEBUG(printk("stradis%d irq: A1_OUT\n", saa->nr)); - } - if (astat & SAA7146_PSR_AFOU) { - IDEBUG(printk("stradis%d irq: AFOU\n", saa->nr)); - } - if (astat & SAA7146_PSR_V_PE) { - IDEBUG(printk("stradis%d irq: V_PE\n", saa->nr)); - } - if (astat & SAA7146_PSR_VFOU) { - IDEBUG(printk("stradis%d irq: VFOU\n", saa->nr)); - } - if (astat & SAA7146_PSR_FIDA) { - IDEBUG(printk("stradis%d irq: FIDA\n", saa->nr)); - } - if (astat & SAA7146_PSR_FIDB) { - IDEBUG(printk("stradis%d irq: FIDB\n", saa->nr)); - } - if (astat & SAA7146_PSR_PIN3) { - IDEBUG(printk("stradis%d irq: PIN3\n", saa->nr)); - } - if (astat & SAA7146_PSR_PIN2) { - IDEBUG(printk("stradis%d irq: PIN2\n", saa->nr)); - } - if (astat & SAA7146_PSR_PIN0) { - IDEBUG(printk("stradis%d irq: PIN0\n", saa->nr)); - } - if (astat & SAA7146_PSR_ECS) { - IDEBUG(printk("stradis%d irq: ECS\n", saa->nr)); - } - if (astat & SAA7146_PSR_EC3S) { - IDEBUG(printk("stradis%d irq: EC3S\n", saa->nr)); - } - if (astat & SAA7146_PSR_EC0S) { - IDEBUG(printk("stradis%d irq: EC0S\n", saa->nr)); - } -#endif - count++; - if (count > 15) - printk(KERN_WARNING "stradis%d: irq loop %d\n", - saa->nr, count); - if (count > 20) { - saawrite(0, SAA7146_IER); - printk(KERN_ERR - "stradis%d: IRQ loop cleared\n", saa->nr); - } - } - return IRQ_RETVAL(handled); -} - -static int ibm_send_command(struct saa7146 *saa, - int command, int data, int chain) -{ - int i; - - if (chain) - debiwrite(saa, debNormal, IBM_MP2_COMMAND, (command << 1)| 1,2); - else - debiwrite(saa, debNormal, IBM_MP2_COMMAND, command << 1, 2); - debiwrite(saa, debNormal, IBM_MP2_CMD_DATA, data, 2); - debiwrite(saa, debNormal, IBM_MP2_CMD_STAT, 1, 2); - for (i = 0; i < 100 && - (debiread(saa, debNormal, IBM_MP2_CMD_STAT, 2) & 1); i++) - schedule(); - if (i == 100) - return -1; - return 0; -} - -static void cs4341_setlevel(struct saa7146 *saa, int left, int right) -{ - I2CWrite(saa, 0x22, 0x03, left > 94 ? 94 : left, 2); - I2CWrite(saa, 0x22, 0x04, right > 94 ? 94 : right, 2); -} - -static void initialize_cs4341(struct saa7146 *saa) -{ - int i; - for (i = 0; i < 200; i++) { - /* auto mute off, power on, no de-emphasis */ - /* I2S data up to 24-bit 64xFs internal SCLK */ - I2CWrite(saa, 0x22, 0x01, 0x11, 2); - /* ATAPI mixer settings */ - I2CWrite(saa, 0x22, 0x02, 0x49, 2); - /* attenuation left 3db */ - I2CWrite(saa, 0x22, 0x03, 0x00, 2); - /* attenuation right 3db */ - I2CWrite(saa, 0x22, 0x04, 0x00, 2); - I2CWrite(saa, 0x22, 0x01, 0x10, 2); - if (I2CRead(saa, 0x22, 0x02, 1) == 0x49) - break; - schedule(); - } - printk("stradis%d: CS4341 initialized (%d)\n", saa->nr, i); - return; -} - -static void initialize_cs8420(struct saa7146 *saa, int pro) -{ - int i; - u8 *sequence; - if (pro) - sequence = mode8420pro; - else - sequence = mode8420con; - for (i = 0; i < INIT8420LEN; i++) - I2CWrite(saa, 0x20, init8420[i * 2], init8420[i * 2 + 1], 2); - for (i = 0; i < MODE8420LEN; i++) - I2CWrite(saa, 0x20, sequence[i * 2], sequence[i * 2 + 1], 2); - printk("stradis%d: CS8420 initialized\n", saa->nr); -} - -static void initialize_saa7121(struct saa7146 *saa, int dopal) -{ - int i, mod; - u8 *sequence; - if (dopal) - sequence = init7121pal; - else - sequence = init7121ntsc; - mod = saaread(SAA7146_PSR) & 0x08; - /* initialize PAL/NTSC video encoder */ - for (i = 0; i < INIT7121LEN; i++) { - if (NewCard) { /* handle new card encoder differences */ - if (sequence[i * 2] == 0x3a) - I2CWrite(saa, 0x88, 0x3a, 0x13, 2); - else if (sequence[i * 2] == 0x6b) - I2CWrite(saa, 0x88, 0x6b, 0x20, 2); - else if (sequence[i * 2] == 0x6c) - I2CWrite(saa, 0x88, 0x6c, - dopal ? 0x09 : 0xf5, 2); - else if (sequence[i * 2] == 0x6d) - I2CWrite(saa, 0x88, 0x6d, - dopal ? 0x20 : 0x00, 2); - else if (sequence[i * 2] == 0x7a) - I2CWrite(saa, 0x88, 0x7a, - dopal ? (PALFirstActive - 1) : - (NTSCFirstActive - 4), 2); - else if (sequence[i * 2] == 0x7b) - I2CWrite(saa, 0x88, 0x7b, - dopal ? PALLastActive : - NTSCLastActive, 2); - else - I2CWrite(saa, 0x88, sequence[i * 2], - sequence[i * 2 + 1], 2); - } else { - if (sequence[i * 2] == 0x6b && mod) - I2CWrite(saa, 0x88, 0x6b, - (sequence[i * 2 + 1] ^ 0x09), 2); - else if (sequence[i * 2] == 0x7a) - I2CWrite(saa, 0x88, 0x7a, - dopal ? (PALFirstActive - 1) : - (NTSCFirstActive - 4), 2); - else if (sequence[i * 2] == 0x7b) - I2CWrite(saa, 0x88, 0x7b, - dopal ? PALLastActive : - NTSCLastActive, 2); - else - I2CWrite(saa, 0x88, sequence[i * 2], - sequence[i * 2 + 1], 2); - } - } -} - -static void set_genlock_offset(struct saa7146 *saa, int noffset) -{ - int nCode; - int PixelsPerLine = 858; - if (CurrentMode == VIDEO_MODE_PAL) - PixelsPerLine = 864; - if (noffset > 500) - noffset = 500; - else if (noffset < -500) - noffset = -500; - nCode = noffset + 0x100; - if (nCode == 1) - nCode = 0x401; - else if (nCode < 1) - nCode = 0x400 + PixelsPerLine + nCode; - debiwrite(saa, debNormal, XILINX_GLDELAY, nCode, 2); -} - -static void set_out_format(struct saa7146 *saa, int mode) -{ - initialize_saa7121(saa, (mode == VIDEO_MODE_NTSC ? 0 : 1)); - saa->boardcfg[2] = mode; - /* do not adjust analog video parameters here, use saa7121 init */ - /* you will affect the SDI output on the new card */ - if (mode == VIDEO_MODE_PAL) { /* PAL */ - debiwrite(saa, debNormal, XILINX_CTL0, 0x0808, 2); - mdelay(50); - saawrite(0x012002c0, SAA7146_NUM_LINE_BYTE1); - if (NewCard) { - debiwrite(saa, debNormal, IBM_MP2_DISP_MODE, 0xe100, 2); - mdelay(50); - } - debiwrite(saa, debNormal, IBM_MP2_DISP_MODE, - NewCard ? 0xe500 : 0x6500, 2); - debiwrite(saa, debNormal, IBM_MP2_DISP_DLY, - (1 << 8) | - (NewCard ? PALFirstActive : PALFirstActive - 6), 2); - } else { /* NTSC */ - debiwrite(saa, debNormal, XILINX_CTL0, 0x0800, 2); - mdelay(50); - saawrite(0x00f002c0, SAA7146_NUM_LINE_BYTE1); - debiwrite(saa, debNormal, IBM_MP2_DISP_MODE, - NewCard ? 0xe100 : 0x6100, 2); - debiwrite(saa, debNormal, IBM_MP2_DISP_DLY, - (1 << 8) | - (NewCard ? NTSCFirstActive : NTSCFirstActive - 6), 2); - } -} - -/* Intialize bitmangler to map from a byte value to the mangled word that - * must be output to program the Xilinx part through the DEBI port. - * Xilinx Data Bit->DEBI Bit: 0->15 1->7 2->6 3->12 4->11 5->2 6->1 7->0 - * transfer FPGA code, init IBM chip, transfer IBM microcode - * rev2 card mangles: 0->7 1->6 2->5 3->4 4->3 5->2 6->1 7->0 - */ -static u16 bitmangler[256]; - -static int initialize_fpga(struct video_code *bitdata) -{ - int i, num, startindex, failure = 0, loadtwo, loadfile = 0; - u16 *dmabuf; - u8 *newdma; - struct saa7146 *saa; - - /* verify fpga code */ - for (startindex = 0; startindex < bitdata->datasize; startindex++) - if (bitdata->data[startindex] == 255) - break; - if (startindex == bitdata->datasize) { - printk(KERN_INFO "stradis: bad fpga code\n"); - return -1; - } - /* initialize all detected cards */ - for (num = 0; num < saa_num; num++) { - saa = &saa7146s[num]; - if (saa->boardcfg[0] > 20) - continue; /* card was programmed */ - loadtwo = (saa->boardcfg[18] & 0x10); - if (!NewCard) /* we have an old board */ - for (i = 0; i < 256; i++) - bitmangler[i] = ((i & 0x01) << 15) | - ((i & 0x02) << 6) | ((i & 0x04) << 4) | - ((i & 0x08) << 9) | ((i & 0x10) << 7) | - ((i & 0x20) >> 3) | ((i & 0x40) >> 5) | - ((i & 0x80) >> 7); - else /* else we have a new board */ - for (i = 0; i < 256; i++) - bitmangler[i] = ((i & 0x01) << 7) | - ((i & 0x02) << 5) | ((i & 0x04) << 3) | - ((i & 0x08) << 1) | ((i & 0x10) >> 1) | - ((i & 0x20) >> 3) | ((i & 0x40) >> 5) | - ((i & 0x80) >> 7); - - dmabuf = (u16 *) saa->dmadebi; - newdma = (u8 *) saa->dmadebi; - if (NewCard) { /* SDM2xxx */ - if (!strncmp(bitdata->loadwhat, "decoder2", 8)) - continue; /* fpga not for this card */ - if (!strncmp(&saa->boardcfg[42], bitdata->loadwhat, 8)) - loadfile = 1; - else if (loadtwo && !strncmp(&saa->boardcfg[19], - bitdata->loadwhat, 8)) - loadfile = 2; - else if (!saa->boardcfg[42] && !strncmp("decxl", - bitdata->loadwhat, 8)) - loadfile = 1; /* special */ - else - continue; /* fpga not for this card */ - if (loadfile != 1 && loadfile != 2) - continue; /* skip to next card */ - if (saa->boardcfg[0] && loadfile == 1) - continue; /* skip to next card */ - if (saa->boardcfg[0] != 1 && loadfile == 2) - continue; /* skip to next card */ - saa->boardcfg[0]++; /* mark fpga handled */ - printk("stradis%d: loading %s\n", saa->nr, - bitdata->loadwhat); - if (loadtwo && loadfile == 2) - goto send_fpga_stuff; - /* turn on the Audio interface to set PROG low */ - saawrite(0x00400040, SAA7146_GPIO_CTRL); - saaread(SAA7146_PSR); /* ensure posted write */ - /* wait for everyone to reset */ - mdelay(10); - saawrite(0x00400000, SAA7146_GPIO_CTRL); - } else { /* original card */ - if (strncmp(bitdata->loadwhat, "decoder2", 8)) - continue; /* fpga not for this card */ - /* Pull the Xilinx PROG signal WS3 low */ - saawrite(0x02000200, SAA7146_MC1); - /* Turn on the Audio interface so can set PROG low */ - saawrite(0x000000c0, SAA7146_ACON1); - /* Pull the Xilinx INIT signal (GPIO2) low */ - saawrite(0x00400000, SAA7146_GPIO_CTRL); - /* Make sure everybody resets */ - saaread(SAA7146_PSR); /* ensure posted write */ - mdelay(10); - /* Release the Xilinx PROG signal */ - saawrite(0x00000000, SAA7146_ACON1); - /* Turn off the Audio interface */ - saawrite(0x02000000, SAA7146_MC1); - } - /* Release Xilinx INIT signal (WS2) */ - saawrite(0x00000000, SAA7146_GPIO_CTRL); - /* Wait for the INIT to go High */ - for (i = 0; - i < 10000 && !(saaread(SAA7146_PSR) & SAA7146_PSR_PIN2); - i++) - schedule(); - if (i == 1000) { - printk(KERN_INFO "stradis%d: no fpga INIT\n", saa->nr); - return -1; - } -send_fpga_stuff: - if (NewCard) { - for (i = startindex; i < bitdata->datasize; i++) - newdma[i - startindex] = - bitmangler[bitdata->data[i]]; - debiwrite(saa, 0x01420000, 0, 0, - ((bitdata->datasize - startindex) + 5)); - if (loadtwo && loadfile == 1) { - printk("stradis%d: awaiting 2nd FPGA bitfile\n", - saa->nr); - continue; /* skip to next card */ - } - } else { - for (i = startindex; i < bitdata->datasize; i++) - dmabuf[i - startindex] = - bitmangler[bitdata->data[i]]; - debiwrite(saa, 0x014a0000, 0, 0, - ((bitdata->datasize - startindex) + 5) * 2); - } - for (i = 0; - i < 1000 && !(saaread(SAA7146_PSR) & SAA7146_PSR_PIN2); - i++) - schedule(); - if (i == 1000) { - printk(KERN_INFO "stradis%d: FPGA load failed\n", - saa->nr); - failure++; - continue; - } - if (!NewCard) { - /* Pull the Xilinx INIT signal (GPIO2) low */ - saawrite(0x00400000, SAA7146_GPIO_CTRL); - saaread(SAA7146_PSR); /* ensure posted write */ - mdelay(2); - saawrite(0x00000000, SAA7146_GPIO_CTRL); - mdelay(2); - } - printk(KERN_INFO "stradis%d: FPGA Loaded\n", saa->nr); - saa->boardcfg[0] = 26; /* mark fpga programmed */ - /* set VXCO to its lowest frequency */ - debiwrite(saa, debNormal, XILINX_PWM, 0, 2); - if (NewCard) { - /* mute CS3310 */ - if (HaveCS3310) - debiwrite(saa, debNormal, XILINX_CS3310_CMPLT, - 0, 2); - /* set VXCO to PWM mode, release reset, blank on */ - debiwrite(saa, debNormal, XILINX_CTL0, 0xffc4, 2); - mdelay(10); - /* unmute CS3310 */ - if (HaveCS3310) - debiwrite(saa, debNormal, XILINX_CTL0, - 0x2020, 2); - } - /* set source Black */ - debiwrite(saa, debNormal, XILINX_CTL0, 0x1707, 2); - saa->boardcfg[4] = 22; /* set NTSC First Active Line */ - saa->boardcfg[5] = 23; /* set PAL First Active Line */ - saa->boardcfg[54] = 2; /* set NTSC Last Active Line - 256 */ - saa->boardcfg[55] = 54; /* set PAL Last Active Line - 256 */ - set_out_format(saa, VIDEO_MODE_NTSC); - mdelay(50); - /* begin IBM chip init */ - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 4, 2); - saaread(SAA7146_PSR); /* wait for reset */ - mdelay(5); - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 0, 2); - debiread(saa, debNormal, IBM_MP2_CHIP_CONTROL, 2); - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 0x10, 2); - debiwrite(saa, debNormal, IBM_MP2_CMD_ADDR, 0, 2); - debiwrite(saa, debNormal, IBM_MP2_CHIP_MODE, 0x2e, 2); - if (NewCard) { - mdelay(5); - /* set i2s rate converter to 48KHz */ - debiwrite(saa, debNormal, 0x80c0, 6, 2); - /* we must init CS8420 first since rev b pulls i2s */ - /* master clock low and CS4341 needs i2s master to */ - /* run the i2c port. */ - if (HaveCS8420) - /* 0=consumer, 1=pro */ - initialize_cs8420(saa, 0); - - mdelay(5); - if (HaveCS4341) - initialize_cs4341(saa); - } - debiwrite(saa, debNormal, IBM_MP2_INFC_CTL, 0x48, 2); - debiwrite(saa, debNormal, IBM_MP2_BEEP_CTL, 0xa000, 2); - debiwrite(saa, debNormal, IBM_MP2_DISP_LBOR, 0, 2); - debiwrite(saa, debNormal, IBM_MP2_DISP_TBOR, 0, 2); - if (NewCard) - set_genlock_offset(saa, 0); - debiwrite(saa, debNormal, IBM_MP2_FRNT_ATTEN, 0, 2); -#if 0 - /* enable genlock */ - debiwrite(saa, debNormal, XILINX_CTL0, 0x8000, 2); -#else - /* disable genlock */ - debiwrite(saa, debNormal, XILINX_CTL0, 0x8080, 2); -#endif - } - - return failure; -} - -static int do_ibm_reset(struct saa7146 *saa) -{ - /* failure if decoder not previously programmed */ - if (saa->boardcfg[0] < 37) - return -EIO; - /* mute CS3310 */ - if (HaveCS3310) - debiwrite(saa, debNormal, XILINX_CS3310_CMPLT, 0, 2); - /* disable interrupts */ - saawrite(0, SAA7146_IER); - saa->audhead = saa->audtail = 0; - saa->vidhead = saa->vidtail = 0; - /* tristate debi bus, disable debi transfers */ - saawrite(0x00880000, SAA7146_MC1); - /* ensure posted write */ - saaread(SAA7146_MC1); - mdelay(50); - /* re-enable debi transfers */ - saawrite(0x00880088, SAA7146_MC1); - /* set source Black */ - debiwrite(saa, debNormal, XILINX_CTL0, 0x1707, 2); - /* begin IBM chip init */ - set_out_format(saa, CurrentMode); - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 4, 2); - saaread(SAA7146_PSR); /* wait for reset */ - mdelay(5); - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 0, 2); - debiread(saa, debNormal, IBM_MP2_CHIP_CONTROL, 2); - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, ChipControl, 2); - debiwrite(saa, debNormal, IBM_MP2_CHIP_MODE, 0x2e, 2); - if (NewCard) { - mdelay(5); - /* set i2s rate converter to 48KHz */ - debiwrite(saa, debNormal, 0x80c0, 6, 2); - /* we must init CS8420 first since rev b pulls i2s */ - /* master clock low and CS4341 needs i2s master to */ - /* run the i2c port. */ - if (HaveCS8420) - /* 0=consumer, 1=pro */ - initialize_cs8420(saa, 1); - - mdelay(5); - if (HaveCS4341) - initialize_cs4341(saa); - } - debiwrite(saa, debNormal, IBM_MP2_INFC_CTL, 0x48, 2); - debiwrite(saa, debNormal, IBM_MP2_BEEP_CTL, 0xa000, 2); - debiwrite(saa, debNormal, IBM_MP2_DISP_LBOR, 0, 2); - debiwrite(saa, debNormal, IBM_MP2_DISP_TBOR, 0, 2); - if (NewCard) - set_genlock_offset(saa, 0); - debiwrite(saa, debNormal, IBM_MP2_FRNT_ATTEN, 0, 2); - debiwrite(saa, debNormal, IBM_MP2_OSD_SIZE, 0x2000, 2); - debiwrite(saa, debNormal, IBM_MP2_AUD_CTL, 0x4552, 2); - if (ibm_send_command(saa, IBM_MP2_CONFIG_DECODER, - (ChipControl == 0x43 ? 0xe800 : 0xe000), 1)) { - printk(KERN_ERR "stradis%d: IBM config failed\n", saa->nr); - } - if (HaveCS3310) { - int i = CS3310MaxLvl; - debiwrite(saa, debNormal, XILINX_CS3310_CMPLT, ((i << 8)| i),2); - } - /* start video decoder */ - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, ChipControl, 2); - /* 256k vid, 3520 bytes aud */ - debiwrite(saa, debNormal, IBM_MP2_RB_THRESHOLD, 0x4037, 2); - debiwrite(saa, debNormal, IBM_MP2_AUD_CTL, 0x4573, 2); - ibm_send_command(saa, IBM_MP2_PLAY, 0, 0); - /* enable buffer threshold irq */ - debiwrite(saa, debNormal, IBM_MP2_MASK0, 0xc00c, 2); - /* clear pending interrupts */ - debiread(saa, debNormal, IBM_MP2_HOST_INT, 2); - debiwrite(saa, debNormal, XILINX_CTL0, 0x1711, 2); - - return 0; -} - -/* load the decoder microcode */ -static int initialize_ibmmpeg2(struct video_code *microcode) -{ - int i, num; - struct saa7146 *saa; - - for (num = 0; num < saa_num; num++) { - saa = &saa7146s[num]; - /* check that FPGA is loaded */ - debiwrite(saa, debNormal, IBM_MP2_OSD_SIZE, 0xa55a, 2); - i = debiread(saa, debNormal, IBM_MP2_OSD_SIZE, 2); - if (i != 0xa55a) { - printk(KERN_INFO "stradis%d: %04x != 0xa55a\n", - saa->nr, i); -#if 0 - return -1; -#endif - } - if (!strncmp(microcode->loadwhat, "decoder.vid", 11)) { - if (saa->boardcfg[0] > 27) - continue; /* skip to next card */ - /* load video control store */ - saa->boardcfg[1] = 0x13; /* no-sync default */ - debiwrite(saa, debNormal, IBM_MP2_WR_PROT, 1, 2); - debiwrite(saa, debNormal, IBM_MP2_PROC_IADDR, 0, 2); - for (i = 0; i < microcode->datasize / 2; i++) - debiwrite(saa, debNormal, IBM_MP2_PROC_IDATA, - (microcode->data[i * 2] << 8) | - microcode->data[i * 2 + 1], 2); - debiwrite(saa, debNormal, IBM_MP2_PROC_IADDR, 0, 2); - debiwrite(saa, debNormal, IBM_MP2_WR_PROT, 0, 2); - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, - ChipControl, 2); - saa->boardcfg[0] = 28; - } - if (!strncmp(microcode->loadwhat, "decoder.aud", 11)) { - if (saa->boardcfg[0] > 35) - continue; /* skip to next card */ - /* load audio control store */ - debiwrite(saa, debNormal, IBM_MP2_WR_PROT, 1, 2); - debiwrite(saa, debNormal, IBM_MP2_AUD_IADDR, 0, 2); - for (i = 0; i < microcode->datasize; i++) - debiwrite(saa, debNormal, IBM_MP2_AUD_IDATA, - microcode->data[i], 1); - debiwrite(saa, debNormal, IBM_MP2_AUD_IADDR, 0, 2); - debiwrite(saa, debNormal, IBM_MP2_WR_PROT, 0, 2); - debiwrite(saa, debNormal, IBM_MP2_OSD_SIZE, 0x2000, 2); - debiwrite(saa, debNormal, IBM_MP2_AUD_CTL, 0x4552, 2); - if (ibm_send_command(saa, IBM_MP2_CONFIG_DECODER, - 0xe000, 1)) { - printk(KERN_ERR "stradis%d: IBM config " - "failed\n", saa->nr); - return -1; - } - /* set PWM to center value */ - if (NewCard) { - debiwrite(saa, debNormal, XILINX_PWM, - saa->boardcfg[14] + - (saa->boardcfg[13] << 8), 2); - } else - debiwrite(saa, debNormal, XILINX_PWM, 0x46, 2); - - if (HaveCS3310) { - i = CS3310MaxLvl; - debiwrite(saa, debNormal, XILINX_CS3310_CMPLT, - (i << 8) | i, 2); - } - printk(KERN_INFO "stradis%d: IBM MPEGCD%d Inited\n", - saa->nr, 18 + (debiread(saa, debNormal, - IBM_MP2_CHIP_CONTROL, 2) >> 12)); - /* start video decoder */ - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, - ChipControl, 2); - debiwrite(saa, debNormal, IBM_MP2_RB_THRESHOLD, 0x4037, - 2); /* 256k vid, 3520 bytes aud */ - debiwrite(saa, debNormal, IBM_MP2_AUD_CTL, 0x4573, 2); - ibm_send_command(saa, IBM_MP2_PLAY, 0, 0); - /* enable buffer threshold irq */ - debiwrite(saa, debNormal, IBM_MP2_MASK0, 0xc00c, 2); - debiread(saa, debNormal, IBM_MP2_HOST_INT, 2); - /* enable gpio irq */ - saawrite(0x00002000, SAA7146_GPIO_CTRL); - /* enable decoder output to HPS */ - debiwrite(saa, debNormal, XILINX_CTL0, 0x1711, 2); - saa->boardcfg[0] = 37; - } - } - - return 0; -} - -static u32 palette2fmt[] = { /* some of these YUV translations are wrong */ - 0xffffffff, 0x86000000, 0x87000000, 0x80000000, 0x8100000, 0x82000000, - 0x83000000, 0x00000000, 0x03000000, 0x03000000, 0x0a00000, 0x03000000, - 0x06000000, 0x00000000, 0x03000000, 0x0a000000, 0x0300000 -}; -static int bpp2fmt[4] = { - VIDEO_PALETTE_HI240, VIDEO_PALETTE_RGB565, VIDEO_PALETTE_RGB24, - VIDEO_PALETTE_RGB32 -}; - -/* I wish I could find a formula to calculate these... */ -static u32 h_prescale[64] = { - 0x10000000, 0x18040202, 0x18080000, 0x380c0606, 0x38100204, 0x38140808, - 0x38180000, 0x381c0000, 0x3820161c, 0x38242a3b, 0x38281230, 0x382c4460, - 0x38301040, 0x38340080, 0x38380000, 0x383c0000, 0x3840fefe, 0x3844ee9f, - 0x3848ee9f, 0x384cee9f, 0x3850ee9f, 0x38542a3b, 0x38581230, 0x385c0000, - 0x38600000, 0x38640000, 0x38680000, 0x386c0000, 0x38700000, 0x38740000, - 0x38780000, 0x387c0000, 0x30800000, 0x38840000, 0x38880000, 0x388c0000, - 0x38900000, 0x38940000, 0x38980000, 0x389c0000, 0x38a00000, 0x38a40000, - 0x38a80000, 0x38ac0000, 0x38b00000, 0x38b40000, 0x38b80000, 0x38bc0000, - 0x38c00000, 0x38c40000, 0x38c80000, 0x38cc0000, 0x38d00000, 0x38d40000, - 0x38d80000, 0x38dc0000, 0x38e00000, 0x38e40000, 0x38e80000, 0x38ec0000, - 0x38f00000, 0x38f40000, 0x38f80000, 0x38fc0000, -}; -static u32 v_gain[64] = { - 0x016000ff, 0x016100ff, 0x016100ff, 0x016200ff, 0x016200ff, 0x016200ff, - 0x016200ff, 0x016300ff, 0x016300ff, 0x016300ff, 0x016300ff, 0x016300ff, - 0x016300ff, 0x016300ff, 0x016300ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, -}; - -static void saa7146_set_winsize(struct saa7146 *saa) -{ - u32 format; - int offset, yacl, ysci; - saa->win.color_fmt = format = - (saa->win.depth == 15) ? palette2fmt[VIDEO_PALETTE_RGB555] : - palette2fmt[bpp2fmt[(saa->win.bpp - 1) & 3]]; - offset = saa->win.x * saa->win.bpp + saa->win.y * saa->win.bpl; - saawrite(saa->win.vidadr + offset, SAA7146_BASE_EVEN1); - saawrite(saa->win.vidadr + offset + saa->win.bpl, SAA7146_BASE_ODD1); - saawrite(saa->win.bpl * 2, SAA7146_PITCH1); - saawrite(saa->win.vidadr + saa->win.bpl * saa->win.sheight, - SAA7146_PROT_ADDR1); - saawrite(0, SAA7146_PAGE1); - saawrite(format | 0x60, SAA7146_CLIP_FORMAT_CTRL); - offset = (704 / (saa->win.width - 1)) & 0x3f; - saawrite(h_prescale[offset], SAA7146_HPS_H_PRESCALE); - offset = (720896 / saa->win.width) / (offset + 1); - saawrite((offset << 12) | 0x0c, SAA7146_HPS_H_SCALE); - if (CurrentMode == VIDEO_MODE_NTSC) { - yacl = /*(480 / saa->win.height - 1) & 0x3f */ 0; - ysci = 1024 - (saa->win.height * 1024 / 480); - } else { - yacl = /*(576 / saa->win.height - 1) & 0x3f */ 0; - ysci = 1024 - (saa->win.height * 1024 / 576); - } - saawrite((1 << 31) | (ysci << 21) | (yacl << 15), SAA7146_HPS_V_SCALE); - saawrite(v_gain[yacl], SAA7146_HPS_V_GAIN); - saawrite(((SAA7146_MC2_UPLD_DMA1 | SAA7146_MC2_UPLD_HPS_V | - SAA7146_MC2_UPLD_HPS_H) << 16) | (SAA7146_MC2_UPLD_DMA1 | - SAA7146_MC2_UPLD_HPS_V | SAA7146_MC2_UPLD_HPS_H), SAA7146_MC2); -} - -/* clip_draw_rectangle(cm,x,y,w,h) -- handle clipping an area - * bitmap is fixed width, 128 bytes (1024 pixels represented) - * arranged most-sigificant-bit-left in 32-bit words - * based on saa7146 clipping hardware, it swaps bytes if LE - * much of this makes up for egcs brain damage -- so if you - * are wondering "why did he do this?" it is because the C - * was adjusted to generate the optimal asm output without - * writing non-portable __asm__ directives. - */ - -static void clip_draw_rectangle(u32 *clipmap, int x, int y, int w, int h) -{ - register int startword, endword; - register u32 bitsleft, bitsright; - u32 *temp; - if (x < 0) { - w += x; - x = 0; - } - if (y < 0) { - h += y; - y = 0; - } - if (w <= 0 || h <= 0 || x > 1023 || y > 639) - return; /* throw away bad clips */ - if (x + w > 1024) - w = 1024 - x; - if (y + h > 640) - h = 640 - y; - startword = (x >> 5); - endword = ((x + w) >> 5); - bitsleft = (0xffffffff >> (x & 31)); - bitsright = (0xffffffff << (~((x + w) - (endword << 5)))); - temp = &clipmap[(y << 5) + startword]; - w = endword - startword; - if (!w) { - bitsleft |= bitsright; - for (y = 0; y < h; y++) { - *temp |= bitsleft; - temp += 32; - } - } else { - for (y = 0; y < h; y++) { - *temp++ |= bitsleft; - for (x = 1; x < w; x++) - *temp++ = 0xffffffff; - *temp |= bitsright; - temp += (32 - w); - } - } -} - -static void make_clip_tab(struct saa7146 *saa, struct video_clip *cr, int ncr) -{ - int i, width, height; - u32 *clipmap; - - clipmap = saa->dmavid2; - if ((width = saa->win.width) > 1023) - width = 1023; /* sanity check */ - if ((height = saa->win.height) > 640) - height = 639; /* sanity check */ - if (ncr > 0) { /* rectangles pased */ - /* convert rectangular clips to a bitmap */ - memset(clipmap, 0, VIDEO_CLIPMAP_SIZE); /* clear map */ - for (i = 0; i < ncr; i++) - clip_draw_rectangle(clipmap, cr[i].x, cr[i].y, - cr[i].width, cr[i].height); - } - /* clip against viewing window AND screen - so we do not have to rely on the user program - */ - clip_draw_rectangle(clipmap, (saa->win.x + width > saa->win.swidth) ? - (saa->win.swidth - saa->win.x) : width, 0, 1024, 768); - clip_draw_rectangle(clipmap, 0, - (saa->win.y + height > saa->win.sheight) ? - (saa->win.sheight - saa->win.y) : height, 1024, 768); - if (saa->win.x < 0) - clip_draw_rectangle(clipmap, 0, 0, -saa->win.x, 768); - if (saa->win.y < 0) - clip_draw_rectangle(clipmap, 0, 0, 1024, -saa->win.y); -} - -static long saa_ioctl(struct file *file, - unsigned int cmd, unsigned long argl) -{ - struct saa7146 *saa = file->private_data; - void __user *arg = (void __user *)argl; - - switch (cmd) { - case VIDIOCGCAP: - { - struct video_capability b; - memset(&b, 0, sizeof(b)); - strcpy(b.name, saa->video_dev.name); - b.type = VID_TYPE_CAPTURE | VID_TYPE_OVERLAY | - VID_TYPE_CLIPPING | VID_TYPE_FRAMERAM | - VID_TYPE_SCALES; - b.channels = 1; - b.audios = 1; - b.maxwidth = 768; - b.maxheight = 576; - b.minwidth = 32; - b.minheight = 32; - if (copy_to_user(arg, &b, sizeof(b))) - return -EFAULT; - return 0; - } - case VIDIOCGPICT: - { - struct video_picture p = saa->picture; - if (saa->win.depth == 8) - p.palette = VIDEO_PALETTE_HI240; - if (saa->win.depth == 15) - p.palette = VIDEO_PALETTE_RGB555; - if (saa->win.depth == 16) - p.palette = VIDEO_PALETTE_RGB565; - if (saa->win.depth == 24) - p.palette = VIDEO_PALETTE_RGB24; - if (saa->win.depth == 32) - p.palette = VIDEO_PALETTE_RGB32; - if (copy_to_user(arg, &p, sizeof(p))) - return -EFAULT; - return 0; - } - case VIDIOCSPICT: - { - struct video_picture p; - u32 format; - if (copy_from_user(&p, arg, sizeof(p))) - return -EFAULT; - if (p.palette < ARRAY_SIZE(palette2fmt)) { - format = palette2fmt[p.palette]; - saa->win.color_fmt = format; - saawrite(format | 0x60, - SAA7146_CLIP_FORMAT_CTRL); - } - saawrite(((p.brightness & 0xff00) << 16) | - ((p.contrast & 0xfe00) << 7) | - ((p.colour & 0xfe00) >> 9), SAA7146_BCS_CTRL); - saa->picture = p; - /* upload changed registers */ - saawrite(((SAA7146_MC2_UPLD_HPS_H | - SAA7146_MC2_UPLD_HPS_V) << 16) | - SAA7146_MC2_UPLD_HPS_H | - SAA7146_MC2_UPLD_HPS_V, SAA7146_MC2); - return 0; - } - case VIDIOCSWIN: - { - struct video_window vw; - struct video_clip *vcp = NULL; - - if (copy_from_user(&vw, arg, sizeof(vw))) - return -EFAULT; - - /* stop capture */ - if (vw.flags || vw.width < 16 || vw.height < 16) { - saawrite((SAA7146_MC1_TR_E_1 << 16), - SAA7146_MC1); - return -EINVAL; - } - /* 32-bit align start and adjust width */ - if (saa->win.bpp < 4) { - int i = vw.x; - vw.x = (vw.x + 3) & ~3; - i = vw.x - i; - vw.width -= i; - } - saa->win.x = vw.x; - saa->win.y = vw.y; - saa->win.width = vw.width; - if (saa->win.width > 768) - saa->win.width = 768; - saa->win.height = vw.height; - if (CurrentMode == VIDEO_MODE_NTSC) { - if (saa->win.height > 480) - saa->win.height = 480; - } else { - if (saa->win.height > 576) - saa->win.height = 576; - } - - /* stop capture */ - saawrite((SAA7146_MC1_TR_E_1 << 16), SAA7146_MC1); - saa7146_set_winsize(saa); - - /* - * Do any clips. - */ - if (vw.clipcount < 0) { - if (copy_from_user(saa->dmavid2, vw.clips, - VIDEO_CLIPMAP_SIZE)) - return -EFAULT; - } else if (vw.clipcount > 16384) { - return -EINVAL; - } else if (vw.clipcount > 0) { - vcp = vmalloc(sizeof(struct video_clip) * - vw.clipcount); - if (vcp == NULL) - return -ENOMEM; - if (copy_from_user(vcp, vw.clips, - sizeof(struct video_clip) * - vw.clipcount)) { - vfree(vcp); - return -EFAULT; - } - } else /* nothing clipped */ - memset(saa->dmavid2, 0, VIDEO_CLIPMAP_SIZE); - - make_clip_tab(saa, vcp, vw.clipcount); - if (vw.clipcount > 0) - vfree(vcp); - - /* start capture & clip dma if we have an address */ - if ((saa->cap & 3) && saa->win.vidadr != 0) - saawrite(((SAA7146_MC1_TR_E_1 | - SAA7146_MC1_TR_E_2) << 16) | 0xffff, - SAA7146_MC1); - return 0; - } - case VIDIOCGWIN: - { - struct video_window vw; - memset(&vw, 0, sizeof(vw)); - vw.x = saa->win.x; - vw.y = saa->win.y; - vw.width = saa->win.width; - vw.height = saa->win.height; - vw.chromakey = 0; - vw.flags = 0; - if (copy_to_user(arg, &vw, sizeof(vw))) - return -EFAULT; - return 0; - } - case VIDIOCCAPTURE: - { - int v; - if (copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - if (v == 0) { - saa->cap &= ~1; - saawrite((SAA7146_MC1_TR_E_1 << 16), - SAA7146_MC1); - } else { - if (saa->win.vidadr == 0 || saa->win.width == 0 - || saa->win.height == 0) - return -EINVAL; - saa->cap |= 1; - saawrite((SAA7146_MC1_TR_E_1 << 16) | 0xffff, - SAA7146_MC1); - } - return 0; - } - case VIDIOCGFBUF: - { - struct video_buffer v; - memset(&v, 0, sizeof(v)); - v.base = (void *)saa->win.vidadr; - v.height = saa->win.sheight; - v.width = saa->win.swidth; - v.depth = saa->win.depth; - v.bytesperline = saa->win.bpl; - if (copy_to_user(arg, &v, sizeof(v))) - return -EFAULT; - return 0; - - } - case VIDIOCSFBUF: - { - struct video_buffer v; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - if (v.depth != 8 && v.depth != 15 && v.depth != 16 && - v.depth != 24 && v.depth != 32 && v.width > 16 && - v.height > 16 && v.bytesperline > 16) - return -EINVAL; - if (v.base) - saa->win.vidadr = (unsigned long)v.base; - saa->win.sheight = v.height; - saa->win.swidth = v.width; - saa->win.bpp = ((v.depth + 7) & 0x38) / 8; - saa->win.depth = v.depth; - saa->win.bpl = v.bytesperline; - - DEBUG(printk("Display at %p is %d by %d, bytedepth %d, " - "bpl %d\n", v.base, v.width, v.height, - saa->win.bpp, saa->win.bpl)); - saa7146_set_winsize(saa); - return 0; - } - case VIDIOCKEY: - { - /* Will be handled higher up .. */ - return 0; - } - - case VIDIOCGAUDIO: - { - struct video_audio v; - memset(&v, 0, sizeof(v)); - v = saa->audio_dev; - v.flags &= ~(VIDEO_AUDIO_MUTE | VIDEO_AUDIO_MUTABLE); - v.flags |= VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME; - strcpy(v.name, "MPEG"); - v.mode = VIDEO_SOUND_STEREO; - if (copy_to_user(arg, &v, sizeof(v))) - return -EFAULT; - return 0; - } - case VIDIOCSAUDIO: - { - struct video_audio v; - int i; - if (copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - i = (~(v.volume >> 8)) & 0xff; - if (!HaveCS4341) { - if (v.flags & VIDEO_AUDIO_MUTE) - debiwrite(saa, debNormal, - IBM_MP2_FRNT_ATTEN, 0xffff, 2); - if (!(v.flags & VIDEO_AUDIO_MUTE)) - debiwrite(saa, debNormal, - IBM_MP2_FRNT_ATTEN, 0x0000, 2); - if (v.flags & VIDEO_AUDIO_VOLUME) - debiwrite(saa, debNormal, - IBM_MP2_FRNT_ATTEN, - (i << 8) | i, 2); - } else { - if (v.flags & VIDEO_AUDIO_MUTE) - cs4341_setlevel(saa, 0xff, 0xff); - if (!(v.flags & VIDEO_AUDIO_MUTE)) - cs4341_setlevel(saa, 0, 0); - if (v.flags & VIDEO_AUDIO_VOLUME) - cs4341_setlevel(saa, i, i); - } - saa->audio_dev = v; - return 0; - } - - case VIDIOCGUNIT: - { - struct video_unit vu; - memset(&vu, 0, sizeof(vu)); - vu.video = saa->video_dev.minor; - vu.vbi = VIDEO_NO_UNIT; - vu.radio = VIDEO_NO_UNIT; - vu.audio = VIDEO_NO_UNIT; - vu.teletext = VIDEO_NO_UNIT; - if (copy_to_user(arg, &vu, sizeof(vu))) - return -EFAULT; - return 0; - } - case VIDIOCSPLAYMODE: - { - struct video_play_mode pmode; - if (copy_from_user((void *)&pmode, arg, - sizeof(struct video_play_mode))) - return -EFAULT; - switch (pmode.mode) { - case VID_PLAY_VID_OUT_MODE: - if (pmode.p1 != VIDEO_MODE_NTSC && - pmode.p1 != VIDEO_MODE_PAL) - return -EINVAL; - set_out_format(saa, pmode.p1); - return 0; - case VID_PLAY_GENLOCK: - debiwrite(saa, debNormal, XILINX_CTL0, - pmode.p1 ? 0x8000 : 0x8080, 2); - if (NewCard) - set_genlock_offset(saa, pmode.p2); - return 0; - case VID_PLAY_NORMAL: - debiwrite(saa, debNormal, - IBM_MP2_CHIP_CONTROL, ChipControl, 2); - ibm_send_command(saa, IBM_MP2_PLAY, 0, 0); - saa->playmode = pmode.mode; - return 0; - case VID_PLAY_PAUSE: - /* IBM removed the PAUSE command */ - /* they say use SINGLE_FRAME now */ - case VID_PLAY_SINGLE_FRAME: - ibm_send_command(saa, IBM_MP2_SINGLE_FRAME,0,0); - if (saa->playmode == pmode.mode) { - debiwrite(saa, debNormal, - IBM_MP2_CHIP_CONTROL, - ChipControl, 2); - } - saa->playmode = pmode.mode; - return 0; - case VID_PLAY_FAST_FORWARD: - ibm_send_command(saa, IBM_MP2_FAST_FORWARD,0,0); - saa->playmode = pmode.mode; - return 0; - case VID_PLAY_SLOW_MOTION: - ibm_send_command(saa, IBM_MP2_SLOW_MOTION, - pmode.p1, 0); - saa->playmode = pmode.mode; - return 0; - case VID_PLAY_IMMEDIATE_NORMAL: - /* ensure transfers resume */ - debiwrite(saa, debNormal, - IBM_MP2_CHIP_CONTROL, ChipControl, 2); - ibm_send_command(saa, IBM_MP2_IMED_NORM_PLAY, - 0, 0); - saa->playmode = VID_PLAY_NORMAL; - return 0; - case VID_PLAY_SWITCH_CHANNELS: - saa->audhead = saa->audtail = 0; - saa->vidhead = saa->vidtail = 0; - ibm_send_command(saa, IBM_MP2_FREEZE_FRAME,0,1); - ibm_send_command(saa, IBM_MP2_RESET_AUD_RATE, - 0, 1); - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, - 0, 2); - ibm_send_command(saa, IBM_MP2_CHANNEL_SWITCH, - 0, 1); - debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, - ChipControl, 2); - ibm_send_command(saa, IBM_MP2_PLAY, 0, 0); - saa->playmode = VID_PLAY_NORMAL; - return 0; - case VID_PLAY_FREEZE_FRAME: - ibm_send_command(saa, IBM_MP2_FREEZE_FRAME,0,0); - saa->playmode = pmode.mode; - return 0; - case VID_PLAY_STILL_MODE: - ibm_send_command(saa, IBM_MP2_SET_STILL_MODE, - 0, 0); - saa->playmode = pmode.mode; - return 0; - case VID_PLAY_MASTER_MODE: - if (pmode.p1 == VID_PLAY_MASTER_NONE) - saa->boardcfg[1] = 0x13; - else if (pmode.p1 == VID_PLAY_MASTER_VIDEO) - saa->boardcfg[1] = 0x23; - else if (pmode.p1 == VID_PLAY_MASTER_AUDIO) - saa->boardcfg[1] = 0x43; - else - return -EINVAL; - debiwrite(saa, debNormal, - IBM_MP2_CHIP_CONTROL, ChipControl, 2); - return 0; - case VID_PLAY_ACTIVE_SCANLINES: - if (CurrentMode == VIDEO_MODE_PAL) { - if (pmode.p1 < 1 || pmode.p2 > 625) - return -EINVAL; - saa->boardcfg[5] = pmode.p1; - saa->boardcfg[55] = (pmode.p1 + - (pmode.p2 / 2) - 1) & 0xff; - } else { - if (pmode.p1 < 4 || pmode.p2 > 525) - return -EINVAL; - saa->boardcfg[4] = pmode.p1; - saa->boardcfg[54] = (pmode.p1 + - (pmode.p2 / 2) - 4) & 0xff; - } - set_out_format(saa, CurrentMode); - case VID_PLAY_RESET: - return do_ibm_reset(saa); - case VID_PLAY_END_MARK: - if (saa->endmarktail < saa->endmarkhead) { - if (saa->endmarkhead - - saa->endmarktail < 2) - return -ENOSPC; - } else if (saa->endmarkhead <=saa->endmarktail){ - if (saa->endmarktail - saa->endmarkhead - > (MAX_MARKS - 2)) - return -ENOSPC; - } else - return -ENOSPC; - saa->endmark[saa->endmarktail] = saa->audtail; - saa->endmarktail++; - if (saa->endmarktail >= MAX_MARKS) - saa->endmarktail = 0; - } - return -EINVAL; - } - case VIDIOCSWRITEMODE: - { - int mode; - if (copy_from_user((void *)&mode, arg, sizeof(int))) - return -EFAULT; - if (mode == VID_WRITE_MPEG_AUD || - mode == VID_WRITE_MPEG_VID || - mode == VID_WRITE_CC || - mode == VID_WRITE_TTX || - mode == VID_WRITE_OSD) { - saa->writemode = mode; - return 0; - } - return -EINVAL; - } - case VIDIOCSMICROCODE: - { - struct video_code ucode; - __u8 *udata; - int i; - if (copy_from_user(&ucode, arg, sizeof(ucode))) - return -EFAULT; - if (ucode.datasize > 65536 || ucode.datasize < 1024 || - strncmp(ucode.loadwhat, "dec", 3)) - return -EINVAL; - if ((udata = vmalloc(ucode.datasize)) == NULL) - return -ENOMEM; - if (copy_from_user(udata, ucode.data, ucode.datasize)) { - vfree(udata); - return -EFAULT; - } - ucode.data = udata; - if (!strncmp(ucode.loadwhat, "decoder.aud", 11) || - !strncmp(ucode.loadwhat, "decoder.vid", 11)) - i = initialize_ibmmpeg2(&ucode); - else - i = initialize_fpga(&ucode); - vfree(udata); - if (i) - return -EINVAL; - return 0; - - } - case VIDIOCGCHAN: /* this makes xawtv happy */ - { - struct video_channel v; - if (copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - v.flags = VIDEO_VC_AUDIO; - v.tuners = 0; - v.type = VID_TYPE_MPEG_DECODER; - v.norm = CurrentMode; - strcpy(v.name, "MPEG2"); - if (copy_to_user(arg, &v, sizeof(v))) - return -EFAULT; - return 0; - } - case VIDIOCSCHAN: /* this makes xawtv happy */ - { - struct video_channel v; - if (copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - /* do nothing */ - return 0; - } - default: - return -ENOIOCTLCMD; - } - return 0; -} - -static int saa_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct saa7146 *saa = file->private_data; - printk(KERN_DEBUG "stradis%d: saa_mmap called\n", saa->nr); - return -EINVAL; -} - -static ssize_t saa_read(struct file *file, char __user * buf, - size_t count, loff_t * ppos) -{ - return -EINVAL; -} - -static ssize_t saa_write(struct file *file, const char __user * buf, - size_t count, loff_t * ppos) -{ - struct saa7146 *saa = file->private_data; - unsigned long todo = count; - int blocksize, split; - unsigned long flags; - - while (todo > 0) { - if (saa->writemode == VID_WRITE_MPEG_AUD) { - spin_lock_irqsave(&saa->lock, flags); - if (saa->audhead <= saa->audtail) - blocksize = 65536 - - (saa->audtail - saa->audhead); - else - blocksize = saa->audhead - saa->audtail; - spin_unlock_irqrestore(&saa->lock, flags); - if (blocksize < 16384) { - saawrite(SAA7146_PSR_DEBI_S | - SAA7146_PSR_PIN1, SAA7146_IER); - saawrite(SAA7146_PSR_PIN1, SAA7146_PSR); - /* wait for buffer space to open */ - interruptible_sleep_on(&saa->audq); - } - spin_lock_irqsave(&saa->lock, flags); - if (saa->audhead <= saa->audtail) { - blocksize = 65536 - - (saa->audtail - saa->audhead); - split = 65536 - saa->audtail; - } else { - blocksize = saa->audhead - saa->audtail; - split = 65536; - } - spin_unlock_irqrestore(&saa->lock, flags); - blocksize--; - if (blocksize > todo) - blocksize = todo; - /* double check that we really have space */ - if (!blocksize) - return -ENOSPC; - if (split < blocksize) { - if (copy_from_user(saa->audbuf + - saa->audtail, buf, split)) - return -EFAULT; - buf += split; - todo -= split; - blocksize -= split; - saa->audtail = 0; - } - if (copy_from_user(saa->audbuf + saa->audtail, buf, - blocksize)) - return -EFAULT; - saa->audtail += blocksize; - todo -= blocksize; - buf += blocksize; - saa->audtail &= 0xffff; - } else if (saa->writemode == VID_WRITE_MPEG_VID) { - spin_lock_irqsave(&saa->lock, flags); - if (saa->vidhead <= saa->vidtail) - blocksize = 524288 - - (saa->vidtail - saa->vidhead); - else - blocksize = saa->vidhead - saa->vidtail; - spin_unlock_irqrestore(&saa->lock, flags); - if (blocksize < 65536) { - saawrite(SAA7146_PSR_DEBI_S | - SAA7146_PSR_PIN1, SAA7146_IER); - saawrite(SAA7146_PSR_PIN1, SAA7146_PSR); - /* wait for buffer space to open */ - interruptible_sleep_on(&saa->vidq); - } - spin_lock_irqsave(&saa->lock, flags); - if (saa->vidhead <= saa->vidtail) { - blocksize = 524288 - - (saa->vidtail - saa->vidhead); - split = 524288 - saa->vidtail; - } else { - blocksize = saa->vidhead - saa->vidtail; - split = 524288; - } - spin_unlock_irqrestore(&saa->lock, flags); - blocksize--; - if (blocksize > todo) - blocksize = todo; - /* double check that we really have space */ - if (!blocksize) - return -ENOSPC; - if (split < blocksize) { - if (copy_from_user(saa->vidbuf + - saa->vidtail, buf, split)) - return -EFAULT; - buf += split; - todo -= split; - blocksize -= split; - saa->vidtail = 0; - } - if (copy_from_user(saa->vidbuf + saa->vidtail, buf, - blocksize)) - return -EFAULT; - saa->vidtail += blocksize; - todo -= blocksize; - buf += blocksize; - saa->vidtail &= 0x7ffff; - } else if (saa->writemode == VID_WRITE_OSD) { - if (count > 131072) - return -ENOSPC; - if (copy_from_user(saa->osdbuf, buf, count)) - return -EFAULT; - buf += count; - saa->osdhead = 0; - saa->osdtail = count; - debiwrite(saa, debNormal, IBM_MP2_OSD_ADDR, 0, 2); - debiwrite(saa, debNormal, IBM_MP2_OSD_LINK_ADDR, 0, 2); - debiwrite(saa, debNormal, IBM_MP2_MASK0, 0xc00d, 2); - debiwrite(saa, debNormal, IBM_MP2_DISP_MODE, - debiread(saa, debNormal, - IBM_MP2_DISP_MODE, 2) | 1, 2); - /* trigger osd data transfer */ - saawrite(SAA7146_PSR_DEBI_S | - SAA7146_PSR_PIN1, SAA7146_IER); - saawrite(SAA7146_PSR_PIN1, SAA7146_PSR); - } - } - return count; -} - -static int saa_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct saa7146 *saa = container_of(vdev, struct saa7146, video_dev); - - lock_kernel(); - file->private_data = saa; - - saa->user++; - if (saa->user > 1) { - saa->user--; - unlock_kernel(); - return 0; /* device open already, don't reset */ - } - saa->writemode = VID_WRITE_MPEG_VID; /* default to video */ - unlock_kernel(); - return 0; -} - -static int saa_release(struct file *file) -{ - struct saa7146 *saa = file->private_data; - saa->user--; - - if (saa->user > 0) /* still someone using device */ - return 0; - saawrite(0x007f0000, SAA7146_MC1); /* stop all overlay dma */ - return 0; -} - -static const struct v4l2_file_operations saa_fops = { - .owner = THIS_MODULE, - .open = saa_open, - .release = saa_release, - .ioctl = saa_ioctl, - .read = saa_read, - .write = saa_write, - .mmap = saa_mmap, -}; - -/* template for video_device-structure */ -static struct video_device saa_template = { - .name = "SAA7146A", - .fops = &saa_fops, - .release = video_device_release_empty, -}; - -static int __devinit configure_saa7146(struct pci_dev *pdev, int num) -{ - int retval; - struct saa7146 *saa = pci_get_drvdata(pdev); - - saa->endmarkhead = saa->endmarktail = 0; - saa->win.x = saa->win.y = 0; - saa->win.width = saa->win.cropwidth = 720; - saa->win.height = saa->win.cropheight = 480; - saa->win.cropx = saa->win.cropy = 0; - saa->win.bpp = 2; - saa->win.depth = 16; - saa->win.color_fmt = palette2fmt[VIDEO_PALETTE_RGB565]; - saa->win.bpl = 1024 * saa->win.bpp; - saa->win.swidth = 1024; - saa->win.sheight = 768; - saa->picture.brightness = 32768; - saa->picture.contrast = 38768; - saa->picture.colour = 32768; - saa->cap = 0; - saa->nr = num; - saa->playmode = VID_PLAY_NORMAL; - memset(saa->boardcfg, 0, 64); /* clear board config area */ - saa->saa7146_mem = NULL; - saa->dmavid1 = saa->dmavid2 = saa->dmavid3 = saa->dmaa1in = - saa->dmaa1out = saa->dmaa2in = saa->dmaa2out = - saa->pagevid1 = saa->pagevid2 = saa->pagevid3 = saa->pagea1in = - saa->pagea1out = saa->pagea2in = saa->pagea2out = - saa->pagedebi = saa->dmaRPS1 = saa->dmaRPS2 = saa->pageRPS1 = - saa->pageRPS2 = NULL; - saa->audbuf = saa->vidbuf = saa->osdbuf = saa->dmadebi = NULL; - saa->audhead = saa->vidtail = 0; - - init_waitqueue_head(&saa->i2cq); - init_waitqueue_head(&saa->audq); - init_waitqueue_head(&saa->debiq); - init_waitqueue_head(&saa->vidq); - spin_lock_init(&saa->lock); - - retval = pci_enable_device(pdev); - if (retval) { - dev_err(&pdev->dev, "%d: pci_enable_device failed!\n", num); - goto err; - } - - saa->id = pdev->device; - saa->irq = pdev->irq; - saa->saa7146_adr = pci_resource_start(pdev, 0); - pci_read_config_byte(pdev, PCI_CLASS_REVISION, &saa->revision); - - saa->saa7146_mem = ioremap(saa->saa7146_adr, 0x200); - if (saa->saa7146_mem == NULL) { - dev_err(&pdev->dev, "%d: ioremap failed!\n", num); - retval = -EIO; - goto err; - } - - memcpy(&saa->video_dev, &saa_template, sizeof(saa_template)); - saawrite(0, SAA7146_IER); /* turn off all interrupts */ - - retval = request_irq(saa->irq, saa7146_irq, IRQF_SHARED | IRQF_DISABLED, - "stradis", saa); - if (retval == -EINVAL) - dev_err(&pdev->dev, "%d: Bad irq number or handler\n", num); - else if (retval == -EBUSY) - dev_err(&pdev->dev, "%d: IRQ %ld busy, change your PnP config " - "in BIOS\n", num, saa->irq); - if (retval < 0) - goto errio; - - pci_set_master(pdev); - retval = video_register_device(&saa->video_dev, VFL_TYPE_GRABBER, - video_nr); - if (retval < 0) { - dev_err(&pdev->dev, "%d: error in registering video device!\n", - num); - goto errirq; - } - - return 0; - -errirq: - free_irq(saa->irq, saa); -errio: - iounmap(saa->saa7146_mem); -err: - return retval; -} - -static int __devinit init_saa7146(struct pci_dev *pdev) -{ - struct saa7146 *saa = pci_get_drvdata(pdev); - - saa->user = 0; - /* reset the saa7146 */ - saawrite(0xffff0000, SAA7146_MC1); - mdelay(5); - /* enable debi and i2c transfers and pins */ - saawrite(((SAA7146_MC1_EDP | SAA7146_MC1_EI2C | - SAA7146_MC1_TR_E_DEBI) << 16) | 0xffff, SAA7146_MC1); - /* ensure proper state of chip */ - saawrite(0x00000000, SAA7146_PAGE1); - saawrite(0x00f302c0, SAA7146_NUM_LINE_BYTE1); - saawrite(0x00000000, SAA7146_PAGE2); - saawrite(0x01400080, SAA7146_NUM_LINE_BYTE2); - saawrite(0x00000000, SAA7146_DD1_INIT); - saawrite(0x00000000, SAA7146_DD1_STREAM_B); - saawrite(0x00000000, SAA7146_DD1_STREAM_A); - saawrite(0x00000000, SAA7146_BRS_CTRL); - saawrite(0x80400040, SAA7146_BCS_CTRL); - saawrite(0x0000e000 /*| (1<<29) */ , SAA7146_HPS_CTRL); - saawrite(0x00000060, SAA7146_CLIP_FORMAT_CTRL); - saawrite(0x00000000, SAA7146_ACON1); - saawrite(0x00000000, SAA7146_ACON2); - saawrite(0x00000600, SAA7146_I2C_STATUS); - saawrite(((SAA7146_MC2_UPLD_D1_B | SAA7146_MC2_UPLD_D1_A | - SAA7146_MC2_UPLD_BRS | SAA7146_MC2_UPLD_HPS_H | - SAA7146_MC2_UPLD_HPS_V | SAA7146_MC2_UPLD_DMA2 | - SAA7146_MC2_UPLD_DMA1 | SAA7146_MC2_UPLD_I2C) << 16) | 0xffff, - SAA7146_MC2); - /* setup arbitration control registers */ - saawrite(0x1412121a, SAA7146_PCI_BT_V1); - - /* allocate 32k dma buffer + 4k for page table */ - if ((saa->dmadebi = kmalloc(32768 + 4096, GFP_KERNEL)) == NULL) { - dev_err(&pdev->dev, "%d: debi kmalloc failed\n", saa->nr); - goto err; - } -#if 0 - saa->pagedebi = saa->dmadebi + 32768; /* top 4k is for mmu */ - saawrite(virt_to_bus(saa->pagedebi) /*|0x800 */ , SAA7146_DEBI_PAGE); - for (i = 0; i < 12; i++) /* setup mmu page table */ - saa->pagedebi[i] = virt_to_bus((saa->dmadebi + i * 4096)); -#endif - saa->audhead = saa->vidhead = saa->osdhead = 0; - saa->audtail = saa->vidtail = saa->osdtail = 0; - if (saa->vidbuf == NULL && (saa->vidbuf = vmalloc(524288)) == NULL) { - dev_err(&pdev->dev, "%d: malloc failed\n", saa->nr); - goto err; - } - if (saa->audbuf == NULL && (saa->audbuf = vmalloc(65536)) == NULL) { - dev_err(&pdev->dev, "%d: malloc failed\n", saa->nr); - goto errfree; - } - if (saa->osdbuf == NULL && (saa->osdbuf = vmalloc(131072)) == NULL) { - dev_err(&pdev->dev, "%d: malloc failed\n", saa->nr); - goto errfree; - } - /* allocate 81920 byte buffer for clipping */ - if ((saa->dmavid2 = kzalloc(VIDEO_CLIPMAP_SIZE, GFP_KERNEL)) == NULL) { - dev_err(&pdev->dev, "%d: clip kmalloc failed\n", saa->nr); - goto errfree; - } - /* setup clipping registers */ - saawrite(virt_to_bus(saa->dmavid2), SAA7146_BASE_EVEN2); - saawrite(virt_to_bus(saa->dmavid2) + 128, SAA7146_BASE_ODD2); - saawrite(virt_to_bus(saa->dmavid2) + VIDEO_CLIPMAP_SIZE, - SAA7146_PROT_ADDR2); - saawrite(256, SAA7146_PITCH2); - saawrite(4, SAA7146_PAGE2); /* dma direction: read, no byteswap */ - saawrite(((SAA7146_MC2_UPLD_DMA2) << 16) | SAA7146_MC2_UPLD_DMA2, - SAA7146_MC2); - I2CBusScan(saa); - - return 0; -errfree: - vfree(saa->osdbuf); - vfree(saa->audbuf); - vfree(saa->vidbuf); - saa->audbuf = saa->osdbuf = saa->vidbuf = NULL; -err: - return -ENOMEM; -} - -static void stradis_release_saa(struct pci_dev *pdev) -{ - u8 command; - struct saa7146 *saa = pci_get_drvdata(pdev); - - /* turn off all capturing, DMA and IRQs */ - saawrite(0xffff0000, SAA7146_MC1); /* reset chip */ - saawrite(0, SAA7146_MC2); - saawrite(0, SAA7146_IER); - saawrite(0xffffffffUL, SAA7146_ISR); - - /* disable PCI bus-mastering */ - pci_read_config_byte(pdev, PCI_COMMAND, &command); - command &= ~PCI_COMMAND_MASTER; - pci_write_config_byte(pdev, PCI_COMMAND, command); - - /* unmap and free memory */ - saa->audhead = saa->audtail = saa->osdhead = 0; - saa->vidhead = saa->vidtail = saa->osdtail = 0; - vfree(saa->vidbuf); - vfree(saa->audbuf); - vfree(saa->osdbuf); - kfree(saa->dmavid2); - saa->audbuf = saa->vidbuf = saa->osdbuf = NULL; - saa->dmavid2 = NULL; - kfree(saa->dmadebi); - kfree(saa->dmavid1); - kfree(saa->dmavid3); - kfree(saa->dmaa1in); - kfree(saa->dmaa1out); - kfree(saa->dmaa2in); - kfree(saa->dmaa2out); - kfree(saa->dmaRPS1); - kfree(saa->dmaRPS2); - free_irq(saa->irq, saa); - if (saa->saa7146_mem) - iounmap(saa->saa7146_mem); - if (video_is_registered(&saa->video_dev)) - video_unregister_device(&saa->video_dev); -} - -static int __devinit stradis_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - int retval = -EINVAL; - - if (saa_num >= SAA7146_MAX) - goto err; - - if (!pdev->subsystem_vendor) - dev_info(&pdev->dev, "%d: rev1 decoder\n", saa_num); - else - dev_info(&pdev->dev, "%d: SDM2xx found\n", saa_num); - - pci_set_drvdata(pdev, &saa7146s[saa_num]); - - retval = configure_saa7146(pdev, saa_num); - if (retval) { - dev_err(&pdev->dev, "%d: error in configuring\n", saa_num); - goto err; - } - - if (init_saa7146(pdev) < 0) { - dev_err(&pdev->dev, "%d: error in initialization\n", saa_num); - retval = -EIO; - goto errrel; - } - - saa_num++; - - return 0; -errrel: - stradis_release_saa(pdev); -err: - return retval; -} - -static void __devexit stradis_remove(struct pci_dev *pdev) -{ - stradis_release_saa(pdev); -} - -static struct pci_device_id stradis_pci_tbl[] = { - { PCI_DEVICE(PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146) }, - { 0 } -}; - - -static struct pci_driver stradis_driver = { - .name = "stradis", - .id_table = stradis_pci_tbl, - .probe = stradis_probe, - .remove = __devexit_p(stradis_remove) -}; - -static int __init stradis_init(void) -{ - int retval; - - saa_num = 0; - - retval = pci_register_driver(&stradis_driver); - if (retval) - printk(KERN_ERR "stradis: Unable to register pci driver.\n"); - - return retval; -} - -static void __exit stradis_exit(void) -{ - pci_unregister_driver(&stradis_driver); - printk(KERN_INFO "stradis: module cleanup complete\n"); -} - -module_init(stradis_init); -module_exit(stradis_exit); -- cgit v1.2.3 From 0cf445ceaf43be31c5fc70b0e2d5fdccb291c925 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sat, 1 Jan 2011 13:28:30 +0000 Subject: netdev: Update status of 8390 based drivers in MAINTAINERS With the original 8 bit ISA ne1000 card being over 20 years old, it only makes sense to consider ne.c and all the other toplevel 8390 based driver files as legacy for obsolete hardware. The most recent thing made in large quantities that was 8390 based were those crazy PCI ne2k clones - and even they are now 10+ years old. Also remove myself as maintainer, since the only changes to these drivers going forward will be the generic API type changes that touch all drivers. Signed-off-by: Paul Gortmaker Signed-off-by: David S. Miller --- MAINTAINERS | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'MAINTAINERS') diff --git a/MAINTAINERS b/MAINTAINERS index 4641289fc072..aa835f7c97d8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -166,9 +166,8 @@ F: drivers/serial/8250* F: include/linux/serial_8250.h 8390 NETWORK DRIVERS [WD80x3/SMC-ELITE, SMC-ULTRA, NE2000, 3C503, etc.] -M: Paul Gortmaker L: netdev@vger.kernel.org -S: Maintained +S: Orphan / Obsolete F: drivers/net/*8390* F: drivers/net/ax88796.c -- cgit v1.2.3