Merge backports 3.19-rc1.
Fix b/19040627 b/17790935 b/16350275
Change-Id: I7d1aa0cec743f88266e4f5a36f4839c3cc959e87
diff --git a/.local-symbols b/.local-symbols
index 82cfa7c..ab02e9a 100644
--- a/.local-symbols
+++ b/.local-symbols
@@ -1,24 +1,25 @@
+BACKPORT_DIR=
BACKPORTS_VERSION=
BACKPORTED_KERNEL_VERSION=
BACKPORTED_KERNEL_NAME=
WIRELESS=
NET_CORE=
EXPERT=
-BACKPORT_BUILD_CORDIC=
-BACKPORT_CORDIC=
-BACKPORT_BUILD_AVERAGE=
-BACKPORT_AVERAGE=
-BACKPORT_MII=
-BACKPORT_BUILD_DMA_SHARED_HELPERS=
-BACKPORT_BUILD_LEDS=
-BACKPORT_NEW_LEDS=
-BACKPORT_LEDS_CLASS=
-BACKPORT_LEDS_TRIGGERS=
-BACKPORT_USERSEL_BUILD_ALL=
-BACKPORT_CRYPTO_CCM=
-BACKPORT_BUILD_CRYPTO_CCM=
-BACKPORT_WANT_DEV_COREDUMP=
-BACKPORT_BUILD_WANT_DEV_COREDUMP=
+BPAUTO_BUILD_CORDIC=
+BPAUTO_CORDIC=
+BPAUTO_BUILD_AVERAGE=
+BPAUTO_AVERAGE=
+BPAUTO_MII=
+BPAUTO_BUILD_DMA_SHARED_HELPERS=
+BPAUTO_BUILD_LEDS=
+BPAUTO_NEW_LEDS=
+BPAUTO_LEDS_CLASS=
+BPAUTO_LEDS_TRIGGERS=
+BPAUTO_USERSEL_BUILD_ALL=
+BPAUTO_CRYPTO_CCM=
+BPAUTO_BUILD_CRYPTO_CCM=
+BPAUTO_WANT_DEV_COREDUMP=
+BPAUTO_BUILD_WANT_DEV_COREDUMP=
CFG80211=
NL80211_TESTMODE=
CFG80211_DEVELOPER_WARNINGS=
@@ -39,6 +40,7 @@
MAC80211_HAS_RC=
MAC80211_RC_MINSTREL=
MAC80211_RC_MINSTREL_HT=
+MAC80211_RC_MINSTREL_VHT=
MAC80211_RC_DEFAULT_MINSTREL=
MAC80211_RC_DEFAULT=
MAC80211_MESH=
@@ -51,6 +53,7 @@
MAC80211_MLME_DEBUG=
MAC80211_STA_DEBUG=
MAC80211_HT_DEBUG=
+MAC80211_OCB_DEBUG=
MAC80211_IBSS_DEBUG=
MAC80211_PS_DEBUG=
MAC80211_MPL_DEBUG=
@@ -62,6 +65,8 @@
MAC80211_TDLS_DEBUG=
MAC80211_DEBUG_COUNTERS=
BT=
+BT_BREDR=
+BT_LE=
BT_6LOWPAN=
BT_RFCOMM=
BT_RFCOMM_TTY=
@@ -120,6 +125,7 @@
ATH5K=
ATH5K_DEBUG=
ATH5K_TRACER=
+ATH5K_AHB=
ATH5K_PCI=
ATH5K_TEST_CHANNELS=
ATH9K_HW=
@@ -137,6 +143,7 @@
ATH9K_WOW=
ATH9K_RFKILL=
ATH9K_CHANNEL_CONTEXT=
+ATH9K_PCOEM=
ATH9K_HTC=
ATH9K_HTC_DEBUGFS=
CARL9170=
@@ -538,10 +545,12 @@
IR_MCEUSB=
IR_ITE_CIR=
IR_FINTEK=
+IR_MESON=
IR_NUVOTON=
IR_REDRAT3=
IR_STREAMZAP=
IR_WINBOND_CIR=
+IR_IGORPLUGUSB=
IR_IGUANA=
IR_TTUSBIR=
IR_RX51=
@@ -623,7 +632,6 @@
VIDEO_PVRUSB2_DVB=
VIDEO_PVRUSB2_DEBUGIFC=
VIDEO_HDPVR=
-VIDEO_TLG2300=
VIDEO_USBVISION=
VIDEO_STK1160_COMMON=
VIDEO_STK1160_AC97=
@@ -755,12 +763,12 @@
DVB_HOPPER=
DVB_NGENE=
DVB_DDBRIDGE=
+DVB_SMIPCIE=
V4L_PLATFORM_DRIVERS=
VIDEO_VIA_CAMERA=
VIDEO_SH_VOU=
VIDEO_VIU=
VIDEO_TIMBERDALE=
-VIDEO_VINO=
VIDEO_M32R_AR=
VIDEO_M32R_AR_M64278=
VIDEO_OMAP3=
@@ -779,7 +787,7 @@
VIDEO_TI_VPE=
VIDEO_TI_VPE_DEBUG=
V4L_TEST_DRIVERS=
-VIDEO_MEM2MEM_TESTDEV=
+VIDEO_VIM2M=
VIDEO_CAFE_CCIC=
VIDEO_MMP_CAMERA=
VIDEO_DAVINCI_VPIF_DISPLAY=
@@ -820,11 +828,6 @@
VIDEO_SAMSUNG_S5P_MIXER_DEBUG=
VIDEO_VIVID=
SMS_SDIO_DRV=
-MEDIA_PARPORT_SUPPORT=
-VIDEO_BWQCAM=
-VIDEO_CQCAM=
-VIDEO_PMS=
-VIDEO_W9966=
RADIO_ADAPTERS=
RADIO_TEA575X=
RADIO_SI470X=
@@ -914,7 +917,6 @@
VIDEO_ML86V7667=
VIDEO_SAA7110=
VIDEO_SAA711X=
-VIDEO_SAA7191=
VIDEO_TVP514X=
VIDEO_TVP5150=
VIDEO_TVP7002=
@@ -1007,6 +1009,7 @@
MEDIA_TUNER_E4000=
MEDIA_TUNER_FC2580=
MEDIA_TUNER_M88TS2022=
+MEDIA_TUNER_M88RS6000T=
MEDIA_TUNER_TUA9001=
MEDIA_TUNER_SI2157=
MEDIA_TUNER_IT913X=
@@ -1113,11 +1116,11 @@
DVB_AF9033=
DVB_DUMMY_FE=
DVB_DRX39XYJ=
+6LOWPAN=
IEEE802154=
IEEE802154_6LOWPAN=
MAC802154=
IEEE802154_DRIVERS=
-IEEE802154_FAKEHARD=
IEEE802154_FAKELB=
IEEE802154_AT86RF230=
IEEE802154_MRF24J40=
diff --git a/Kconfig b/Kconfig
index b14a268..9684574 100644
--- a/Kconfig
+++ b/Kconfig
@@ -1,5 +1,8 @@
-mainmenu "Linux Backports from $BACKPORTED_KERNEL_NAME $BACKPORTED_KERNEL_VERSION (with backports $BACKPORTS_VERSION)"
+mainmenu "Backports from $BACKPORTED_KERNEL_NAME $BACKPORTED_KERNEL_VERSION (backports $BACKPORTS_VERSION)"
+config BACKPORT_DIR
+ string
+ option env="BACKPORT_DIR"
config BACKPORTS_VERSION
string
option env="BACKPORTS_VERSION"
@@ -10,38 +13,12 @@
string
option env="BACKPORTED_KERNEL_NAME"
+# Packaging hacks
+source "$BACKPORT_DIR/Kconfig.package.hacks"
+
+# Code we backport
+source "$BACKPORT_DIR/Kconfig.sources"
+
# these will be generated
-source Kconfig.kernel
-source Kconfig.versions
-
-# some hacks ...
-config WIRELESS
- def_bool y
-config NET_CORE
- def_bool y
-config EXPERT
- def_bool y
-
-# this has the configuration for the backport code
-source compat/Kconfig
-
-# these are copied from the kernel
-source net/wireless/Kconfig
-source net/mac80211/Kconfig
-source net/bluetooth/Kconfig
-source drivers/net/wireless/Kconfig
-source drivers/net/ethernet/Kconfig
-source drivers/net/usb/Kconfig
-
-source drivers/ssb/Kconfig
-source drivers/bcma/Kconfig
-
-source net/nfc/Kconfig
-
-source drivers/media/Kconfig
-
-source net/ieee802154/Kconfig
-source net/mac802154/Kconfig
-source drivers/net/ieee802154/Kconfig
-
-source drivers/usb/class/Kconfig
+source "$BACKPORT_DIR/Kconfig.kernel"
+source "$BACKPORT_DIR/Kconfig.versions"
diff --git a/Kconfig.package.hacks b/Kconfig.package.hacks
new file mode 100644
index 0000000..6a429dd
--- /dev/null
+++ b/Kconfig.package.hacks
@@ -0,0 +1,8 @@
+# some hacks for when we use backports to generate a package
+# to build modules out of tree.
+config WIRELESS
+ def_bool y
+config NET_CORE
+ def_bool y
+config EXPERT
+ def_bool y
diff --git a/Kconfig.sources b/Kconfig.sources
new file mode 100644
index 0000000..5711433
--- /dev/null
+++ b/Kconfig.sources
@@ -0,0 +1,24 @@
+# this has the configuration for the backport code
+source "$BACKPORT_DIR/compat/Kconfig"
+
+# these are copied from the kernel
+source "$BACKPORT_DIR/net/wireless/Kconfig"
+source "$BACKPORT_DIR/net/mac80211/Kconfig"
+source "$BACKPORT_DIR/net/bluetooth/Kconfig"
+source "$BACKPORT_DIR/drivers/net/wireless/Kconfig"
+source "$BACKPORT_DIR/drivers/net/ethernet/Kconfig"
+source "$BACKPORT_DIR/drivers/net/usb/Kconfig"
+
+source "$BACKPORT_DIR/drivers/ssb/Kconfig"
+source "$BACKPORT_DIR/drivers/bcma/Kconfig"
+
+source "$BACKPORT_DIR/net/nfc/Kconfig"
+
+source "$BACKPORT_DIR/drivers/media/Kconfig"
+
+source "$BACKPORT_DIR/net/6lowpan/Kconfig"
+source "$BACKPORT_DIR/net/ieee802154/Kconfig"
+source "$BACKPORT_DIR/net/mac802154/Kconfig"
+source "$BACKPORT_DIR/drivers/net/ieee802154/Kconfig"
+
+source "$BACKPORT_DIR/drivers/usb/class/Kconfig"
diff --git a/MAINTAINERS b/MAINTAINERS
index a20df9b..ddb9ac8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -618,6 +618,16 @@
F: drivers/iommu/amd_iommu*.[ch]
F: include/linux/amd-iommu.h
+AMD KFD
+M: Oded Gabbay <oded.gabbay@amd.com>
+L: dri-devel@lists.freedesktop.org
+T: git git://people.freedesktop.org/~gabbayo/linux.git
+S: Supported
+F: drivers/gpu/drm/amd/amdkfd/
+F: drivers/gpu/drm/radeon/radeon_kfd.c
+F: drivers/gpu/drm/radeon/radeon_kfd.h
+F: include/uapi/linux/kfd_ioctl.h
+
AMD MICROCODE UPDATE SUPPORT
M: Andreas Herrmann <herrmann.der.user@googlemail.com>
L: amd64-microcode@amd64.org
@@ -850,6 +860,7 @@
M: Carlo Caione <carlo@caione.org>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
+F: drivers/media/rc/meson-ir.c
N: meson[x68]
ARM/ATMEL AT91RM9200 AND AT91SAM ARM ARCHITECTURES
@@ -861,6 +872,7 @@
W: http://www.linux4sam.org
S: Supported
F: arch/arm/mach-at91/
+F: include/soc/at91/
F: arch/arm/boot/dts/at91*.dts
F: arch/arm/boot/dts/at91*.dtsi
F: arch/arm/boot/dts/sama*.dts
@@ -918,6 +930,15 @@
S: Maintained
F: arch/arm/mach-ep93xx/micro9.c
+ARM/CORESIGHT FRAMEWORK AND DRIVERS
+M: Mathieu Poirier <mathieu.poirier@linaro.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: drivers/coresight/*
+F: Documentation/trace/coresight.txt
+F: Documentation/devicetree/bindings/arm/coresight.txt
+F: Documentation/ABI/testing/sysfs-bus-coresight-devices-*
+
ARM/CORGI MACHINE SUPPORT
M: Richard Purdie <rpurdie@rpsys.net>
S: Maintained
@@ -1308,30 +1329,22 @@
F: drivers/*/*/*rockchip*
F: sound/soc/rockchip/
-ARM/SAMSUNG ARM ARCHITECTURES
-M: Ben Dooks <ben-linux@fluff.org>
-M: Kukjin Kim <kgene.kim@samsung.com>
+ARM/SAMSUNG EXYNOS ARM ARCHITECTURES
+M: Kukjin Kim <kgene@kernel.org>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
L: linux-samsung-soc@vger.kernel.org (moderated for non-subscribers)
-W: http://www.fluff.org/ben/linux/
S: Maintained
F: arch/arm/boot/dts/s3c*
F: arch/arm/boot/dts/exynos*
F: arch/arm/plat-samsung/
F: arch/arm/mach-s3c24*/
F: arch/arm/mach-s3c64xx/
+F: arch/arm/mach-s5p*/
+F: arch/arm/mach-exynos*/
F: drivers/*/*s3c2410*
F: drivers/*/*/*s3c2410*
F: drivers/spi/spi-s3c*
F: sound/soc/samsung/*
-
-ARM/S5P EXYNOS ARM ARCHITECTURES
-M: Kukjin Kim <kgene.kim@samsung.com>
-L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
-L: linux-samsung-soc@vger.kernel.org (moderated for non-subscribers)
-S: Maintained
-F: arch/arm/mach-s5p*/
-F: arch/arm/mach-exynos*/
N: exynos
ARM/SAMSUNG MOBILE MACHINE SUPPORT
@@ -1381,12 +1394,12 @@
F: arch/arm/configs/ape6evm_defconfig
F: arch/arm/configs/armadillo800eva_defconfig
F: arch/arm/configs/bockw_defconfig
-F: arch/arm/configs/koelsch_defconfig
F: arch/arm/configs/kzm9g_defconfig
F: arch/arm/configs/lager_defconfig
F: arch/arm/configs/mackerel_defconfig
F: arch/arm/configs/marzen_defconfig
F: arch/arm/configs/shmobile_defconfig
+F: arch/arm/include/debug/renesas-scif.S
F: arch/arm/mach-shmobile/
F: drivers/sh/
@@ -1430,6 +1443,7 @@
F: drivers/usb/dwc3/dwc3-st.c
F: drivers/usb/host/ehci-st.c
F: drivers/usb/host/ohci-st.c
+F: drivers/ata/ahci_st.c
ARM/TECHNOLOGIC SYSTEMS TS7250 MACHINE SUPPORT
M: Lennert Buytenhek <kernel@wantstofly.org>
@@ -1503,6 +1517,19 @@
F: drivers/clk/ux500/
F: include/linux/platform_data/clk-ux500.h
+ARM/VERSATILE EXPRESS PLATFORM
+M: Liviu Dudau <liviu.dudau@arm.com>
+M: Sudeep Holla <sudeep.holla@arm.com>
+M: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm/boot/dts/vexpress*
+F: arch/arm/mach-vexpress/
+F: */*/vexpress*
+F: */*/*/vexpress*
+F: drivers/clk/versatile/clk-vexpress-osc.c
+F: drivers/clocksource/versatile.c
+
ARM/VFP SUPPORT
M: Russell King <linux@arm.linux.org.uk>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
@@ -1543,6 +1570,7 @@
ARM/ZYNQ ARCHITECTURE
M: Michal Simek <michal.simek@xilinx.com>
+R: Sören Brinkmann <soren.brinkmann@xilinx.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
W: http://wiki.xilinx.com
T: git git://git.xilinx.com/linux-xlnx.git
@@ -1713,6 +1741,13 @@
F: drivers/dma/at_hdmac_regs.h
F: include/linux/platform_data/dma-atmel.h
+ATMEL XDMA DRIVER
+M: Ludovic Desroches <ludovic.desroches@atmel.com>
+L: linux-arm-kernel@lists.infradead.org
+L: dmaengine@vger.kernel.org
+S: Supported
+F: drivers/dma/at_xdmac.c
+
ATMEL I2C DRIVER
M: Ludovic Desroches <ludovic.desroches@atmel.com>
L: linux-i2c@vger.kernel.org
@@ -1749,6 +1784,13 @@
S: Supported
F: drivers/spi/spi-atmel.*
+ATMEL SSC DRIVER
+M: Bo Shen <voice.shen@atmel.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Supported
+F: drivers/misc/atmel-ssc.c
+F: include/linux/atmel-ssc.h
+
ATMEL Timer Counter (TC) AND CLOCKSOURCE DRIVERS
M: Nicolas Ferre <nicolas.ferre@atmel.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
@@ -1778,10 +1820,11 @@
F: drivers/scsi/esas2r
AUDIT SUBSYSTEM
+M: Paul Moore <paul@paul-moore.com>
M: Eric Paris <eparis@redhat.com>
-L: linux-audit@redhat.com (subscribers-only)
+L: linux-audit@redhat.com (moderated for non-subscribers)
W: http://people.redhat.com/sgrubb/audit/
-T: git git://git.infradead.org/users/eparis/audit.git
+T: git git://git.infradead.org/users/pcmoore/audit
S: Maintained
F: include/linux/audit.h
F: include/uapi/linux/audit.h
@@ -1820,7 +1863,7 @@
F: net/ax25/
AZ6007 DVB DRIVER
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
@@ -1854,7 +1897,6 @@
BACKLIGHT CLASS/SUBSYSTEM
M: Jingoo Han <jg1.han@samsung.com>
-M: Bryan Wu <cooloney@gmail.com>
M: Lee Jones <lee.jones@linaro.org>
S: Maintained
F: drivers/video/backlight/
@@ -1883,13 +1925,6 @@
S: Maintained:
F: drivers/md/bcache/
-BECEEM BCS200/BCS220-3/BCSM250 WIMAX SUPPORT
-M: Kevin McKinney <klmckinney1@gmail.com>
-M: Matthias Beyer <mail@beyermatthias.de>
-L: devel@driverdev.osuosl.org
-S: Maintained
-F: drivers/staging/bcm*
-
BEFS FILE SYSTEM
S: Orphan
F: Documentation/filesystems/befs.txt
@@ -2064,11 +2099,20 @@
BROADCOM BCM2835 ARM ARCHITECTURE
M: Stephen Warren <swarren@wwwdotorg.org>
+M: Lee Jones <lee@kernel.org>
L: linux-rpi-kernel@lists.infradead.org (moderated for non-subscribers)
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/swarren/linux-rpi.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/rpi/linux-rpi.git
S: Maintained
N: bcm2835
+BROADCOM BCM33XX MIPS ARCHITECTURE
+M: Kevin Cernekee <cernekee@gmail.com>
+L: linux-mips@linux-mips.org
+S: Maintained
+F: arch/mips/bcm3384/*
+F: arch/mips/include/asm/mach-bcm3384/*
+F: arch/mips/kernel/*bmips*
+
BROADCOM BCM5301X ARM ARCHITECTURE
M: Hauke Mehrtens <hauke@hauke-m.de>
L: linux-arm-kernel@lists.infradead.org
@@ -2085,13 +2129,34 @@
F: arch/arm/mach-bcm/bcm63xx.c
F: arch/arm/include/debug/bcm63xx.S
+BROADCOM BCM63XX/BCM33XX UDC DRIVER
+M: Kevin Cernekee <cernekee@gmail.com>
+L: linux-usb@vger.kernel.org
+S: Maintained
+F: drivers/usb/gadget/udc/bcm63xx_udc.*
+
BROADCOM BCM7XXX ARM ARCHITECTURE
M: Marc Carino <marc.ceeeee@gmail.com>
M: Brian Norris <computersforpeace@gmail.com>
+M: Gregory Fong <gregory.0xf0@gmail.com>
+M: Florian Fainelli <f.fainelli@gmail.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
F: arch/arm/mach-bcm/*brcmstb*
F: arch/arm/boot/dts/bcm7*.dts*
+F: drivers/bus/brcmstb_gisb.c
+
+BROADCOM BMIPS MIPS ARCHITECTURE
+M: Kevin Cernekee <cernekee@gmail.com>
+M: Florian Fainelli <f.fainelli@gmail.com>
+L: linux-mips@linux-mips.org
+S: Maintained
+F: arch/mips/bmips/*
+F: arch/mips/include/asm/mach-bmips/*
+F: arch/mips/kernel/*bmips*
+F: arch/mips/boot/dts/bcm*.dts*
+F: drivers/irqchip/irq-bcm7*
+F: drivers/irqchip/irq-brcmstb*
BROADCOM TG3 GIGABIT ETHERNET DRIVER
M: Prashant Sreedharan <prashant@broadcom.com>
@@ -2122,6 +2187,20 @@
S: Supported
F: drivers/scsi/bnx2i/
+BROADCOM CYGNUS/IPROC ARM ARCHITECTURE
+M: Ray Jui <rjui@broadcom.com>
+M: Scott Branden <sbranden@broadcom.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+L: bcm-kernel-feedback-list@broadcom.com
+T: git git://git.github.com/brcm/linux.git
+S: Maintained
+N: iproc
+N: cygnus
+N: bcm9113*
+N: bcm9583*
+N: bcm583*
+N: bcm113*
+
BROADCOM KONA GPIO DRIVER
M: Ray Jui <rjui@broadcom.com>
L: bcm-kernel-feedback-list@broadcom.com
@@ -2189,7 +2268,7 @@
F: fs/btrfs/
BTTV VIDEO4LINUX DRIVER
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
@@ -2299,6 +2378,14 @@
F: security/commoncap.c
F: kernel/capability.c
+CC2520 IEEE-802.15.4 RADIO DRIVER
+M: Varka Bhadram <varkabhadram@gmail.com>
+L: linux-wpan@vger.kernel.org
+S: Maintained
+F: drivers/net/ieee802154/cc2520.c
+F: include/linux/spi/cc2520.h
+F: Documentation/devicetree/bindings/net/ieee802154/cc2520.txt
+
CELL BROADBAND ENGINE ARCHITECTURE
M: Arnd Bergmann <arnd@arndb.de>
L: linuxppc-dev@lists.ozlabs.org
@@ -2480,10 +2567,18 @@
F: include/linux/coda*.h
F: include/uapi/linux/coda*.h
+CODA V4L2 MEM2MEM DRIVER
+M: Philipp Zabel <p.zabel@pengutronix.de>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/media/coda.txt
+F: drivers/media/platform/coda/
+
COMMON CLK FRAMEWORK
M: Mike Turquette <mturquette@linaro.org>
+M: Stephen Boyd <sboyd@codeaurora.org>
L: linux-kernel@vger.kernel.org
-T: git git://git.linaro.org/people/mturquette/linux.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux.git
S: Maintained
F: drivers/clk/
X: drivers/clk/clkdev.c
@@ -2495,8 +2590,7 @@
L: linux-cifs@vger.kernel.org
L: samba-technical@lists.samba.org (moderated for non-subscribers)
W: http://linux-cifs.samba.org/
-Q: http://patchwork.ozlabs.org/project/linux-cifs-client/list/
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6.git
+T: git git://git.samba.org/sfrench/cifs-2.6.git
S: Supported
F: Documentation/filesystems/cifs/
F: fs/cifs/
@@ -2573,7 +2667,7 @@
L: linux-mm@kvack.org
S: Maintained
F: mm/memcontrol.c
-F: mm/page_cgroup.c
+F: mm/swap_cgroup.c
CORETEMP HARDWARE MONITORING DRIVER
M: Fenghua Yu <fenghua.yu@intel.com>
@@ -2623,6 +2717,16 @@
S: Maintained
F: drivers/cpuidle/cpuidle-big_little.c
+CPUIDLE DRIVER - ARM EXYNOS
+M: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
+M: Daniel Lezcano <daniel.lezcano@linaro.org>
+M: Kukjin Kim <kgene@kernel.org>
+L: linux-pm@vger.kernel.org
+L: linux-samsung-soc@vger.kernel.org
+S: Supported
+F: drivers/cpuidle/cpuidle-exynos.c
+F: arch/arm/mach-exynos/pm.c
+
CPUIDLE DRIVERS
M: Rafael J. Wysocki <rjw@rjwysocki.net>
M: Daniel Lezcano <daniel.lezcano@linaro.org>
@@ -2690,7 +2794,7 @@
CX18 VIDEO4LINUX DRIVER
M: Andy Walls <awalls@md.metrocast.net>
-L: ivtv-devel@ivtvdriver.org (moderated for non-subscribers)
+L: ivtv-devel@ivtvdriver.org (subscribers-only)
L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
W: http://linuxtv.org
@@ -2710,7 +2814,7 @@
F: include/media/cx2341x*
CX88 VIDEO4LINUX DRIVER
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
@@ -2735,6 +2839,13 @@
S: Supported
F: drivers/net/ethernet/chelsio/cxgb3/
+CXGB3 ISCSI DRIVER (CXGB3I)
+M: Karen Xie <kxie@chelsio.com>
+L: linux-scsi@vger.kernel.org
+W: http://www.chelsio.com
+S: Supported
+F: drivers/scsi/cxgbi/cxgb3i
+
CXGB3 IWARP RNIC DRIVER (IW_CXGB3)
M: Steve Wise <swise@chelsio.com>
L: linux-rdma@vger.kernel.org
@@ -2749,6 +2860,13 @@
S: Supported
F: drivers/net/ethernet/chelsio/cxgb4/
+CXGB4 ISCSI DRIVER (CXGB4I)
+M: Karen Xie <kxie@chelsio.com>
+L: linux-scsi@vger.kernel.org
+W: http://www.chelsio.com
+S: Supported
+F: drivers/scsi/cxgbi/cxgb4i
+
CXGB4 IWARP RNIC DRIVER (IW_CXGB4)
M: Steve Wise <swise@chelsio.com>
L: linux-rdma@vger.kernel.org
@@ -2839,11 +2957,10 @@
F: drivers/net/ethernet/dec/tulip/dmfe.c
DC390/AM53C974 SCSI driver
-M: Kurt Garloff <garloff@suse.de>
-W: http://www.garloff.de/kurt/linux/dc390/
-M: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+M: Hannes Reinecke <hare@suse.de>
+L: linux-scsi@vger.kernel.org
S: Maintained
-F: drivers/scsi/tmscsim.*
+F: drivers/scsi/am53c974.c
DC395x SCSI driver
M: Oliver Neukum <oliver@neukum.org>
@@ -3066,7 +3183,8 @@
S: Maintained
F: drivers/dma/
F: include/linux/dma*
-T: git git://git.infradead.org/users/vkoul/slave-dma.git (slave-dma)
+F: Documentation/dmaengine/
+T: git git://git.infradead.org/users/vkoul/slave-dma.git
DME1737 HARDWARE MONITOR DRIVER
M: Juerg Haefliger <juergh@gmail.com>
@@ -3183,6 +3301,13 @@
F: include/drm/exynos*
F: include/uapi/drm/exynos*
+DRM DRIVERS FOR FREESCALE IMX
+M: Philipp Zabel <p.zabel@pengutronix.de>
+L: dri-devel@lists.freedesktop.org
+S: Maintained
+F: drivers/gpu/drm/imx/
+F: Documentation/devicetree/bindings/drm/imx/
+
DRM DRIVERS FOR NVIDIA TEGRA
M: Thierry Reding <thierry.reding@gmail.com>
M: Terje Bergström <tbergstrom@nvidia.com>
@@ -3379,7 +3504,7 @@
EDAC-CORE
M: Doug Thompson <dougthompson@xmission.com>
M: Borislav Petkov <bp@alien8.de>
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Supported
@@ -3428,7 +3553,7 @@
F: drivers/edac/e7xxx_edac.c
EDAC-GHES
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained
@@ -3456,21 +3581,21 @@
F: drivers/edac/i5000_edac.c
EDAC-I5400
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained
F: drivers/edac/i5400_edac.c
EDAC-I7300
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained
F: drivers/edac/i7300_edac.c
EDAC-I7CORE
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained
@@ -3513,7 +3638,7 @@
F: drivers/edac/r82600_edac.c
EDAC-SBRIDGE
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained
@@ -3573,7 +3698,7 @@
F: drivers/net/ethernet/ibm/ehea/
EM28XX VIDEO4LINUX DRIVER
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
@@ -3941,7 +4066,7 @@
FREESCALE SOC SOUND DRIVERS
M: Timur Tabi <timur@tabi.org>
M: Nicolin Chen <nicoleotsuka@gmail.com>
-M: Xiubo Li <Li.Xiubo@freescale.com>
+M: Xiubo Li <Xiubo.Lee@gmail.com>
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
L: linuxppc-dev@lists.ozlabs.org
S: Maintained
@@ -4131,6 +4256,12 @@
S: Maintained
F: drivers/media/usb/go7007/
+GOODIX TOUCHSCREEN
+M: Bastien Nocera <hadess@hadess.net>
+L: linux-input@vger.kernel.org
+S: Maintained
+F: drivers/input/touchscreen/goodix.c
+
GPIO SUBSYSTEM
M: Linus Walleij <linus.walleij@linaro.org>
M: Alexandre Courbot <gnurou@gmail.com>
@@ -4305,8 +4436,10 @@
F: drivers/block/cpqarray.*
HEWLETT-PACKARD SMART ARRAY RAID DRIVER (hpsa)
-M: "Stephen M. Cameron" <scameron@beardog.cce.hp.com>
+M: Don Brace <don.brace@pmcs.com>
L: iss_storagedev@hp.com
+L: storagedev@pmcs.com
+L: linux-scsi@vger.kernel.org
S: Supported
F: Documentation/scsi/hpsa.txt
F: drivers/scsi/hpsa*.[ch]
@@ -4314,8 +4447,10 @@
F: include/uapi/linux/cciss*.h
HEWLETT-PACKARD SMART CISS RAID DRIVER (cciss)
-M: Mike Miller <mike.miller@hp.com>
+M: Don Brace <don.brace@pmcs.com>
L: iss_storagedev@hp.com
+L: storagedev@pmcs.com
+L: linux-scsi@vger.kernel.org
S: Supported
F: Documentation/blockdev/cciss.txt
F: drivers/block/cciss*
@@ -4548,6 +4683,7 @@
Q: https://patchwork.ozlabs.org/project/linux-i2c/list/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux.git
S: Maintained
+F: Documentation/devicetree/bindings/i2c/
F: Documentation/i2c/
F: drivers/i2c/
F: include/linux/i2c.h
@@ -4601,7 +4737,7 @@
F: drivers/crypto/nx/
IBM Power 842 compression accelerator
-M: Nathan Fontenot <nfont@linux.vnet.ibm.com>
+M: Dan Streetman <ddstreet@us.ibm.com>
S: Supported
F: drivers/crypto/nx/nx-842.c
F: include/linux/nx842.h
@@ -4686,8 +4822,21 @@
F: net/ieee802154/
F: net/mac802154/
F: drivers/net/ieee802154/
+F: include/linux/nl802154.h
+F: include/linux/ieee802154.h
+F: include/net/nl802154.h
+F: include/net/mac802154.h
+F: include/net/af_ieee802154.h
+F: include/net/cfg802154.h
+F: include/net/ieee802154_netdev.h
F: Documentation/networking/ieee802154.txt
+IGORPLUG-USB IR RECEIVER
+M: Sean Young <sean@mess.org>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: drivers/media/rc/igorplugusb.c
+
IGUANAWORKS USB IR TRANSCEIVER
M: Sean Young <sean@mess.org>
L: linux-media@vger.kernel.org
@@ -4703,6 +4852,7 @@
S: Maintained
F: drivers/iio/
F: drivers/staging/iio/
+F: include/linux/iio/
IKANOS/ADI EAGLE ADSL USB DRIVER
M: Matthieu Castet <castet.matthieu@free.fr>
@@ -4744,6 +4894,11 @@
S: Supported
F: security/integrity/ima/
+IMGTEC IR DECODER DRIVER
+M: James Hogan <james.hogan@imgtec.com>
+S: Maintained
+F: drivers/media/rc/img-ir/
+
IMS TWINTURBO FRAMEBUFFER DRIVER
L: linux-fbdev@vger.kernel.org
S: Orphan
@@ -4809,6 +4964,12 @@
S: Supported
F: drivers/idle/intel_idle.c
+INTEL PSTATE DRIVER
+M: Kristen Carlson Accardi <kristen@linux.intel.com>
+L: linux-pm@vger.kernel.org
+S: Supported
+F: drivers/cpufreq/intel_pstate.c
+
INTEL FRAMEBUFFER DRIVER (excluding 810 and 815)
M: Maik Broemme <mbroemme@plusserver.de>
L: linux-fbdev@vger.kernel.org
@@ -5158,7 +5319,7 @@
IVTV VIDEO4LINUX DRIVER
M: Andy Walls <awalls@md.metrocast.net>
-L: ivtv-devel@ivtvdriver.org (moderated for non-subscribers)
+L: ivtv-devel@ivtvdriver.org (subscribers-only)
L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
W: http://www.ivtvdriver.org
@@ -5341,15 +5502,6 @@
F: arch/powerpc/include/asm/kvm*
F: arch/powerpc/kvm/
-KERNEL VIRTUAL MACHINE For Itanium (KVM/IA64)
-M: Xiantao Zhang <xiantao.zhang@intel.com>
-L: kvm-ia64@vger.kernel.org
-W: http://kvm.qumranet.com
-S: Supported
-F: Documentation/ia64/kvm.txt
-F: arch/ia64/include/asm/kvm*
-F: arch/ia64/kvm/
-
KERNEL VIRTUAL MACHINE for s390 (KVM/s390)
M: Christian Borntraeger <borntraeger@de.ibm.com>
M: Cornelia Huck <cornelia.huck@de.ibm.com>
@@ -5834,6 +5986,14 @@
F: drivers/net/macvlan.c
F: include/linux/if_macvlan.h
+MAILBOX API
+M: Jassi Brar <jassisinghbrar@gmail.com>
+L: linux-kernel@vger.kernel.org
+S: Maintained
+F: drivers/mailbox/
+F: include/linux/mailbox_client.h
+F: include/linux/mailbox_controller.h
+
MAN-PAGES: MANUAL PAGES FOR LINUX -- Sections 2, 3, 4, 5, and 7
M: Michael Kerrisk <mtk.manpages@gmail.com>
W: http://www.kernel.org/doc/man-pages
@@ -5845,6 +6005,11 @@
S: Maintained
F: drivers/gpu/drm/armada/
+MARVELL 88E6352 DSA support
+M: Guenter Roeck <linux@roeck-us.net>
+S: Maintained
+F: drivers/net/dsa/mv88e6352.c
+
MARVELL GIGABIT ETHERNET DRIVERS (skge/sky2)
M: Mirko Lindner <mlindner@marvell.com>
M: Stephen Hemminger <stephen@networkplumber.org>
@@ -5926,7 +6091,7 @@
F: drivers/media/radio/radio-maxiradio*
MEDIA INPUT INFRASTRUCTURE (V4L/DVB)
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
P: LinuxTV.org Project
L: linux-media@vger.kernel.org
W: http://linuxtv.org
@@ -5955,10 +6120,13 @@
S: Odd Fixes
F: drivers/media/parport/pms*
-MEGARAID SCSI DRIVERS
-M: Neela Syam Kolli <megaraidlinux@lsi.com>
+MEGARAID SCSI/SAS DRIVERS
+M: Kashyap Desai <kashyap.desai@avagotech.com>
+M: Sumit Saxena <sumit.saxena@avagotech.com>
+M: Uday Lingala <uday.lingala@avagotech.com>
+L: megaraidlinux.pdl@avagotech.com
L: linux-scsi@vger.kernel.org
-W: http://megaraid.lsilogic.com
+W: http://www.lsi.com
S: Maintained
F: Documentation/scsi/megaraid.txt
F: drivers/scsi/megaraid.*
@@ -6081,6 +6249,28 @@
F: include/linux/mlx5/
F: drivers/infiniband/hw/mlx5/
+MN88472 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/staging/media/mn88472/
+F: drivers/media/dvb-frontends/mn88472.h
+
+MN88473 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/staging/media/mn88473/
+F: drivers/media/dvb-frontends/mn88473.h
+
MODULE SUPPORT
M: Rusty Russell <rusty@rustcorp.com.au>
S: Maintained
@@ -6269,7 +6459,6 @@
F: drivers/scsi/g_NCR5380_mmio.c
F: drivers/scsi/mac_scsi.*
F: drivers/scsi/pas16.*
-F: drivers/scsi/sun3_NCR5380.c
F: drivers/scsi/sun3_scsi.*
F: drivers/scsi/sun3_scsi_vme.c
F: drivers/scsi/t128.*
@@ -6428,19 +6617,8 @@
S: Maintained
NETWORKING [WIRELESS]
-M: "John W. Linville" <linville@tuxdriver.com>
L: linux-wireless@vger.kernel.org
Q: http://patchwork.kernel.org/project/linux-wireless/list/
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless.git
-S: Maintained
-F: net/mac80211/
-F: net/rfkill/
-F: net/wireless/
-F: include/net/ieee80211*
-F: include/linux/wireless.h
-F: include/uapi/linux/wireless.h
-F: include/net/iw_handler.h
-F: drivers/net/wireless/
NETWORKING DRIVERS
L: netdev@vger.kernel.org
@@ -6461,6 +6639,14 @@
F: include/uapi/linux/if_*
F: include/uapi/linux/netdevice.h
+NETWORKING DRIVERS (WIRELESS)
+M: Kalle Valo <kvalo@codeaurora.org>
+L: linux-wireless@vger.kernel.org
+Q: http://patchwork.kernel.org/project/linux-wireless/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/wireless-drivers.git/
+S: Maintained
+F: drivers/net/wireless/
+
NETXEN (1/10) GbE SUPPORT
M: Manish Chopra <manish.chopra@qlogic.com>
M: Sony Chacko <sony.chacko@qlogic.com>
@@ -6525,6 +6711,13 @@
F: Documentation/scsi/NinjaSCSI.txt
F: drivers/scsi/nsp32*
+NIOS2 ARCHITECTURE
+M: Ley Foon Tan <lftan@altera.com>
+L: nios2-dev@lists.rocketboards.org (moderated for non-subscribers)
+T: git git://git.rocketboards.org/linux-socfpga.git
+S: Maintained
+F: arch/nios2/
+
NTB DRIVER
M: Jon Mason <jdmason@kudzu.us>
M: Dave Jiang <dave.jiang@intel.com>
@@ -6565,6 +6758,12 @@
F: drivers/gpu/drm/i2c/tda998x_drv.c
F: include/drm/i2c/tda998x.h
+NXP TFA9879 DRIVER
+M: Peter Rosin <peda@axentia.se>
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+S: Maintained
+F: sound/soc/codecs/tfa9879*
+
OMAP SUPPORT
M: Tony Lindgren <tony@atomide.com>
L: linux-omap@vger.kernel.org
@@ -6575,6 +6774,23 @@
S: Maintained
F: arch/arm/*omap*/
F: drivers/i2c/busses/i2c-omap.c
+F: drivers/irqchip/irq-omap-intc.c
+F: drivers/mfd/*omap*.c
+F: drivers/mfd/menelaus.c
+F: drivers/mfd/palmas.c
+F: drivers/mfd/tps65217.c
+F: drivers/mfd/tps65218.c
+F: drivers/mfd/tps65910.c
+F: drivers/mfd/twl-core.[ch]
+F: drivers/mfd/twl4030*.c
+F: drivers/mfd/twl6030*.c
+F: drivers/mfd/twl6040*.c
+F: drivers/regulator/palmas-regulator*.c
+F: drivers/regulator/pbias-regulator.c
+F: drivers/regulator/tps65217-regulator.c
+F: drivers/regulator/tps65218-regulator.c
+F: drivers/regulator/tps65910-regulator.c
+F: drivers/regulator/twl-regulator.c
F: include/linux/i2c-omap.h
OMAP DEVICE TREE SUPPORT
@@ -6585,6 +6801,9 @@
S: Maintained
F: arch/arm/boot/dts/*omap*
F: arch/arm/boot/dts/*am3*
+F: arch/arm/boot/dts/*am4*
+F: arch/arm/boot/dts/*am5*
+F: arch/arm/boot/dts/*dra7*
OMAP CLOCK FRAMEWORK SUPPORT
M: Paul Walmsley <paul@pwsan.com>
@@ -6614,6 +6833,14 @@
S: Maintained
F: sound/soc/omap/
+OMAP GENERAL PURPOSE MEMORY CONTROLLER SUPPORT
+M: Roger Quadros <rogerq@ti.com>
+M: Tony Lindgren <tony@atomide.com>
+L: linux-omap@vger.kernel.org
+S: Maintained
+F: drivers/memory/omap-gpmc.c
+F: arch/arm/mach-omap2/*gpmc*
+
OMAP FRAMEBUFFER SUPPORT
M: Tomi Valkeinen <tomi.valkeinen@ti.com>
L: linux-fbdev@vger.kernel.org
@@ -6822,7 +7049,7 @@
F: drivers/net/wireless/orinoco/
OSD LIBRARY and FILESYSTEM
-M: Boaz Harrosh <bharrosh@panasas.com>
+M: Boaz Harrosh <ooo@electrozaur.com>
M: Benny Halevy <bhalevy@primarydata.com>
L: osd-dev@open-osd.org
W: http://open-osd.org
@@ -6832,6 +7059,14 @@
F: include/scsi/osd_*
F: fs/exofs/
+OVERLAY FILESYSTEM
+M: Miklos Szeredi <miklos@szeredi.hu>
+L: linux-unionfs@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs.git
+S: Supported
+F: fs/overlayfs/
+F: Documentation/filesystems/overlayfs.txt
+
P54 WIRELESS DRIVER
M: Christian Lamparter <chunkeey@googlemail.com>
L: linux-wireless@vger.kernel.org
@@ -6983,6 +7218,16 @@
F: Documentation/devicetree/bindings/pci/xgene-pci.txt
F: drivers/pci/host/pci-xgene.c
+PCI DRIVER FOR FREESCALE LAYERSCAPE
+M: Minghuan Lian <minghuan.Lian@freescale.com>
+M: Mingkai Hu <mingkai.hu@freescale.com>
+M: Roy Zang <tie-fei.zang@freescale.com>
+L: linuxppc-dev@lists.ozlabs.org
+L: linux-pci@vger.kernel.org
+L: linux-arm-kernel@lists.infradead.org
+S: Maintained
+F: drivers/pci/host/*layerscape*
+
PCI DRIVER FOR IMX6
M: Richard Zhu <r65037@freescale.com>
M: Lucas Stach <l.stach@pengutronix.de>
@@ -7153,6 +7398,7 @@
PIN CONTROL SUBSYSTEM
M: Linus Walleij <linus.walleij@linaro.org>
+L: linux-gpio@vger.kernel.org
S: Maintained
F: drivers/pinctrl/
F: include/linux/pinctrl/
@@ -7161,7 +7407,13 @@
M: Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
-F: drivers/pinctrl/pinctrl-at91.c
+F: drivers/pinctrl/pinctrl-at91.*
+
+PIN CONTROLLER - INTEL
+M: Mika Westerberg <mika.westerberg@linux.intel.com>
+M: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+S: Maintained
+F: drivers/pinctrl/intel/
PIN CONTROLLER - RENESAS
M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
@@ -7780,6 +8032,13 @@
F: include/linux/hid-roccat*
F: Documentation/ABI/*/sysfs-driver-hid-roccat*
+ROCKER DRIVER
+M: Jiri Pirko <jiri@resnulli.us>
+M: Scott Feldman <sfeldma@gmail.com>
+L: netdev@vger.kernel.org
+S: Supported
+F: drivers/net/ethernet/rocker/
+
ROCKETPORT DRIVER
P: Comtrol Corp.
W: http://www.comtrol.com
@@ -7787,6 +8046,12 @@
F: Documentation/serial/rocket.txt
F: drivers/tty/rocket*
+ROCKETPORT EXPRESS/INFINITY DRIVER
+M: Kevin Cernekee <cernekee@gmail.com>
+L: linux-serial@vger.kernel.org
+S: Odd Fixes
+F: drivers/tty/serial/rp2.*
+
ROSE NETWORK LAYER
M: Ralf Baechle <ralf@linux-mips.org>
L: linux-hams@vger.kernel.org
@@ -7827,11 +8092,10 @@
F: drivers/media/dvb-frontends/rtl2832_sdr*
RTL8180 WIRELESS DRIVER
-M: "John W. Linville" <linville@tuxdriver.com>
L: linux-wireless@vger.kernel.org
W: http://wireless.kernel.org/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git
-S: Maintained
+S: Orphan
F: drivers/net/wireless/rtl818x/rtl8180/
RTL8187 WIRELESS DRIVER
@@ -7948,7 +8212,7 @@
F: drivers/media/i2c/saa6588*
SAA7134 VIDEO4LINUX DRIVER
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
@@ -8406,7 +8670,7 @@
F: drivers/media/radio/si4713/radio-usb-si4713.c
SIANO DVB DRIVER
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
@@ -8416,6 +8680,14 @@
F: drivers/media/usb/siano/
F: drivers/media/mmc/siano/
+SIMPLEFB FB DRIVER
+M: Hans de Goede <hdegoede@redhat.com>
+L: linux-fbdev@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/video/simple-framebuffer.txt
+F: drivers/video/fbdev/simplefb.c
+F: include/linux/platform_data/simplefb.h
+
SH_VEU V4L2 MEM2MEM DRIVER
L: linux-media@vger.kernel.org
S: Orphan
@@ -8457,7 +8729,6 @@
TI DAVINCI MACHINE SUPPORT
M: Sekhar Nori <nsekhar@ti.com>
M: Kevin Hilman <khilman@deeprootsystems.com>
-L: davinci-linux-open-source@linux.davincidsp.com (moderated for non-subscribers)
T: git git://gitorious.org/linux-davinci/linux-davinci.git
Q: http://patchwork.kernel.org/project/linux-davinci/list/
S: Supported
@@ -8467,7 +8738,6 @@
TI DAVINCI SERIES MEDIA DRIVER
M: Lad, Prabhakar <prabhakar.csengg@gmail.com>
L: linux-media@vger.kernel.org
-L: davinci-linux-open-source@linux.davincidsp.com (moderated for non-subscribers)
W: http://linuxtv.org/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/mhadli/v4l-dvb-davinci_devices.git
@@ -8619,7 +8889,9 @@
F: drivers/leds/leds-net48xx.c
SOFTLOGIC 6x10 MPEG CODEC
-M: Ismael Luceno <ismael.luceno@corp.bluecherry.net>
+M: Bluecherry Maintainers <maintainers@bluecherrydvr.com>
+M: Andrey Utkin <andrey.utkin@corp.bluecherry.net>
+M: Andrey Utkin <andrey.krieger.utkin@gmail.com>
L: linux-media@vger.kernel.org
S: Supported
F: drivers/media/pci/solo6x10/
@@ -8976,6 +9248,13 @@
F: arch/*/kernel/pci-swiotlb.c
F: include/linux/swiotlb.h
+SWITCHDEV
+M: Jiri Pirko <jiri@resnulli.us>
+L: netdev@vger.kernel.org
+S: Supported
+F: net/switchdev/
+F: include/net/switchdev.h
+
SYNOPSYS ARC ARCHITECTURE
M: Vineet Gupta <vgupta@synopsys.com>
S: Supported
@@ -9093,7 +9372,7 @@
F: drivers/media/i2c/tda9840*
TEA5761 TUNER DRIVER
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
@@ -9101,7 +9380,7 @@
F: drivers/media/tuners/tea5761.*
TEA5767 TUNER DRIVER
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
@@ -9232,6 +9511,7 @@
S: Supported
F: drivers/thermal/
F: include/linux/thermal.h
+F: include/uapi/linux/thermal.h
F: include/linux/cpu_cooling.h
F: Documentation/devicetree/bindings/thermal/
@@ -9336,7 +9616,7 @@
F: net/tipc/
TILE ARCHITECTURE
-M: Chris Metcalf <cmetcalf@tilera.com>
+M: Chris Metcalf <cmetcalf@ezchip.com>
W: http://www.tilera.com/scm/
S: Supported
F: arch/tile/
@@ -9413,7 +9693,7 @@
F: mm/shmem.c
TM6000 VIDEO4LINUX DRIVER
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
@@ -9584,7 +9864,6 @@
UNIVERSAL FLASH STORAGE HOST CONTROLLER DRIVER
M: Vinayak Holikatti <vinholikatti@gmail.com>
-M: Santosh Y <santoshsy@gmail.com>
L: linux-scsi@vger.kernel.org
S: Supported
F: Documentation/scsi/ufs.txt
@@ -9678,11 +9957,6 @@
F: Documentation/hid/hiddev.txt
F: drivers/hid/usbhid/
-USB/IP DRIVERS
-L: linux-usb@vger.kernel.org
-S: Orphan
-F: drivers/staging/usbip/
-
USB ISP116X DRIVER
M: Olav Kongas <ok@artecdesign.ee>
L: linux-usb@vger.kernel.org
@@ -9970,13 +10244,13 @@
S: Maintained
F: drivers/net/ethernet/via/via-velocity.*
-VIVI VIRTUAL VIDEO DRIVER
+VIVID VIRTUAL VIDEO DRIVER
M: Hans Verkuil <hverkuil@xs4all.nl>
L: linux-media@vger.kernel.org
T: git git://linuxtv.org/media_tree.git
W: http://linuxtv.org
S: Maintained
-F: drivers/media/platform/vivi*
+F: drivers/media/platform/vivid/*
VLAN (802.1Q)
M: Patrick McHardy <kaber@trash.net>
@@ -10239,8 +10513,15 @@
S: Maintained
F: arch/x86/kernel/cpu/mcheck/*
+X86 VDSO
+M: Andy Lutomirski <luto@amacapital.net>
+L: linux-kernel@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git x86/vdso
+S: Maintained
+F: arch/x86/vdso/
+
XC2028/3028 TUNER DRIVER
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
diff --git a/Makefile b/Makefile
index ea3982b..c21b813 100644
--- a/Makefile
+++ b/Makefile
@@ -6,7 +6,7 @@
MAKEFLAGS += --no-print-directory
SHELL := /bin/bash
-BACKPORT_PWD := $(shell pwd)
+BACKPORT_DIR := $(shell pwd)
KMODDIR ?= updates
ifneq ($(origin KLIB), undefined)
@@ -20,7 +20,7 @@
KERNEL_MAKEFILE := $(KLIB_BUILD)/Makefile
CONFIG_MD5 := $(shell md5sum $(KERNEL_CONFIG) 2>/dev/null | sed 's/\s.*//')
-export KLIB KLIB_BUILD BACKPORT_PWD KMODDIR KMODPATH_ARG
+export KLIB KLIB_BUILD BACKPORT_DIR KMODDIR KMODPATH_ARG
# disable built-in rules for this file
.SUFFIXES:
@@ -93,7 +93,7 @@
print=0 ;\
for v in $$kvers ; do \
if [ "$$print" = "1" ] ; then \
- echo config BACKPORT_KERNEL_$$(echo $$v | tr . _) ;\
+ echo config KERNEL_$$(echo $$v | tr . _) ;\
echo " def_bool y" ;\
fi ;\
if [ "$$v" = "$$kver" ] ; then print=1 ; fi ;\
@@ -159,5 +159,5 @@
@echo ""
@echo "Execute "make" or "make all" to build all targets marked with [*]"
else
-include $(BACKPORT_PWD)/Makefile.kernel
+include $(BACKPORT_DIR)/Makefile.kernel
endif
diff --git a/Makefile.build b/Makefile.build
index d209041..a848b37 100644
--- a/Makefile.build
+++ b/Makefile.build
@@ -3,8 +3,8 @@
.PHONY: modules
modules:
- @$(MAKE) -C $(KLIB_BUILD) M=$(BACKPORT_PWD) modules
+ @$(MAKE) -C $(KLIB_BUILD) M=$(BACKPORT_DIR) modules
.PHONY: clean
clean:
- @$(MAKE) -C $(KLIB_BUILD) M=$(BACKPORT_PWD) clean
+ @$(MAKE) -C $(KLIB_BUILD) M=$(BACKPORT_DIR) clean
diff --git a/Makefile.kernel b/Makefile.kernel
index dcb2ba7..42333ad 100644
--- a/Makefile.kernel
+++ b/Makefile.kernel
@@ -1,3 +1,4 @@
+ifeq ($(CONFIG_BACKPORT_INTEGRATE),)
# Since 2.6.21, try-run is available, but cc-disable-warning
# was only added later, so we add it here ourselves:
backport-cc-disable-warning = $(call try-run,\
@@ -10,13 +11,25 @@
-I$(M)/include/uapi \
-include $(M)/backport-include/backport/backport.h \
$(call backport-cc-disable-warning, unused-but-set-variable) \
- -DBACKPORTS_VERSION=\"$(BACKPORTS_VERSION)\" \
- -DBACKPORTED_KERNEL_VERSION=\"$(BACKPORTED_KERNEL_VERSION)\" \
- -DBACKPORTED_KERNEL_NAME=\"$(BACKPORTED_KERNEL_NAME)\" \
+ -DCPTCFG_VERSION=\"$(BACKPORTS_VERSION)\" \
+ -DCPTCFG_KERNEL_VERSION=\"$(BACKPORTED_KERNEL_VERSION)\" \
+ -DCPTCFG_KERNEL_NAME=\"$(BACKPORTED_KERNEL_NAME)\" \
$(BACKPORTS_GIT_TRACKER_DEF) \
$(CFLAGS)
export backport_srctree = $(M)
+else
+export BACKPORT_DIR = backports/
+export backport_srctree = $(BACKPORT_DIR)
+NOSTDINC_FLAGS := \
+ -I$(BACKPORT_DIR)/backport-include/ \
+ -I$(BACKPORT_DIR)/backport-include/uapi \
+ -I$(BACKPORT_DIR)/include/ \
+ -I$(BACKPORT_DIR)/include/uapi \
+ -include $(BACKPORT_DIR)/backport-include/backport/backport.h \
+ $(CFLAGS)
+endif
+
obj-y += compat/
@@ -33,6 +46,7 @@
obj-$(CPTCFG_NFC) += drivers/nfc/
obj-$(CPTCFG_MEDIA_SUPPORT) += drivers/media/
+obj-$(CPTCFG_6LOWPAN) += net/6lowpan/
obj-$(CPTCFG_IEEE802154) += net/ieee802154/
obj-$(CPTCFG_BT) += net/ieee802154/
obj-$(CPTCFG_MAC802154) += net/mac802154/
diff --git a/Makefile.real b/Makefile.real
index f60d5ca..a0f4916 100644
--- a/Makefile.real
+++ b/Makefile.real
@@ -89,7 +89,7 @@
.PHONY: install
install: modules
- @$(MAKE) -C $(KLIB_BUILD) M=$(BACKPORT_PWD) \
+ @$(MAKE) -C $(KLIB_BUILD) M=$(BACKPORT_DIR) \
INSTALL_MOD_DIR=$(KMODDIR) $(KMODPATH_ARG) \
modules_install
@./scripts/blacklist.sh $(KLIB)/ $(KLIB)/$(KMODDIR)
diff --git a/backport-include/asm-generic/barrier.h b/backport-include/asm-generic/barrier.h
new file mode 100644
index 0000000..9169449
--- /dev/null
+++ b/backport-include/asm-generic/barrier.h
@@ -0,0 +1,9 @@
+#ifndef __BACKPORT_ASM_GENERIC_BARRIER_H
+#define __BACKPORT_ASM_GENERIC_BARRIER_H
+#include_next <asm-generic/barrier.h>
+
+#ifndef dma_rmb
+#define dma_rmb() rmb()
+#endif
+
+#endif /* __BACKPORT_ASM_GENERIC_BARRIER_H */
diff --git a/backport-include/asm/barrier.h b/backport-include/asm/barrier.h
new file mode 100644
index 0000000..b04bd33
--- /dev/null
+++ b/backport-include/asm/barrier.h
@@ -0,0 +1,9 @@
+#ifndef __BACKPORT_ASM_GENERIC_BARRIER_H
+#define __BACKPORT_ASM_GENERIC_BARRIER_H
+#include_next <asm/barrier.h>
+
+#ifndef dma_rmb
+#define dma_rmb() rmb()
+#endif
+
+#endif /* __BACKPORT_ASM_GENERIC_BARRIER_H */
diff --git a/backport-include/asm/dma-mapping.h b/backport-include/asm/dma-mapping.h
index 844fe3b..b73b7da 100644
--- a/backport-include/asm/dma-mapping.h
+++ b/backport-include/asm/dma-mapping.h
@@ -3,12 +3,12 @@
#include_next <asm/dma-mapping.h>
#include <linux/version.h>
-#if defined(CPTCFG_BACKPORT_BUILD_DMA_SHARED_HELPERS)
+#if defined(CPTCFG_BPAUTO_BUILD_DMA_SHARED_HELPERS)
#define dma_common_get_sgtable LINUX_BACKPORT(dma_common_get_sgtable)
int
dma_common_get_sgtable(struct device *dev, struct sg_table *sgt,
void *cpu_addr, dma_addr_t dma_addr, size_t size);
-#endif /* defined(CPTCFG_BACKPORT_BUILD_DMA_SHARED_HELPERS) */
+#endif /* defined(CPTCFG_BPAUTO_BUILD_DMA_SHARED_HELPERS) */
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0)
diff --git a/backport-include/backport/backport.h b/backport-include/backport/backport.h
index 7cf21aa..d1d3b10 100644
--- a/backport-include/backport/backport.h
+++ b/backport-include/backport/backport.h
@@ -1,11 +1,16 @@
#ifndef __BACKPORT_H
#define __BACKPORT_H
+#include <generated/autoconf.h>
+#ifndef CONFIG_BACKPORT_INTEGRATE
#include <backport/autoconf.h>
+#endif
#include <linux/kconfig.h>
#ifndef __ASSEMBLY__
#define LINUX_BACKPORT(__sym) backport_ ##__sym
+#ifndef CONFIG_BACKPORT_INTEGRATE
#include <backport/checks.h>
#endif
+#endif
#endif /* __BACKPORT_H */
diff --git a/backport-include/backport/leds-disabled.h b/backport-include/backport/leds-disabled.h
index 501f2a0..156d7fa 100644
--- a/backport-include/backport/leds-disabled.h
+++ b/backport-include/backport/leds-disabled.h
@@ -8,7 +8,7 @@
* allows compilation.
*/
-#ifdef CPTCFG_BACKPORT_BUILD_LEDS
+#ifdef CPTCFG_BPAUTO_BUILD_LEDS
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/rwsem.h>
diff --git a/backport-include/linux/bitops.h b/backport-include/linux/bitops.h
new file mode 100644
index 0000000..b0e9283
--- /dev/null
+++ b/backport-include/linux/bitops.h
@@ -0,0 +1,19 @@
+#ifndef __BACKPORT_BITOPS_H
+#define __BACKPORT_BITOPS_H
+#include_next <linux/bitops.h>
+#include <linux/version.h>
+#include <generated/utsrelease.h>
+
+#ifndef GENMASK
+
+/*
+ * Create a contiguous bitmask starting at bit position @l and ending at
+ * position @h. For example
+ * GENMASK_ULL(39, 21) gives us the 64bit vector 0x000000ffffe00000.
+ */
+#define GENMASK(h, l) (((U32_C(1) << ((h) - (l) + 1)) - 1) << (l))
+#define GENMASK_ULL(h, l) (((U64_C(1) << ((h) - (l) + 1)) - 1) << (l))
+
+#endif
+
+#endif /* __BACKPORT_BITOPS_H */
diff --git a/backport-include/linux/debugfs.h b/backport-include/linux/debugfs.h
new file mode 100644
index 0000000..77b2e64
--- /dev/null
+++ b/backport-include/linux/debugfs.h
@@ -0,0 +1,23 @@
+#ifndef __BACKPORT_DEBUGFS_H_
+#define __BACKPORT_DEBUGFS_H_
+#include_next <linux/debugfs.h>
+#include <linux/version.h>
+#include <generated/utsrelease.h>
+
+#if defined(CONFIG_DEBUG_FS)
+struct dentry *debugfs_create_devm_seqfile(struct device *dev, const char *name,
+ struct dentry *parent,
+ int (*read_fn)(struct seq_file *s,
+ void *data));
+#else
+static inline struct dentry *debugfs_create_devm_seqfile(struct device *dev,
+ const char *name,
+ struct dentry *parent,
+ int (*read_fn)(struct seq_file *s,
+ void *data))
+{
+ return ERR_PTR(-ENODEV);
+}
+#endif /* CONFIG_DEBUG_FS */
+
+#endif /* __BACKPORT_DEBUGFS_H_ */
diff --git a/backport-include/linux/device.h b/backport-include/linux/device.h
index 41f06c3..29dc077 100644
--- a/backport-include/linux/device.h
+++ b/backport-include/linux/device.h
@@ -177,4 +177,20 @@
}
#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0)
+#define devm_kmemdup LINUX_BACKPORT(devm_kmemdup)
+static inline void *devm_kmemdup(struct device *dev, const void *src,
+ size_t len, gfp_t gfp)
+{
+ void *p;
+
+ p = devm_kmalloc(dev, len, gfp);
+ if (p)
+ memcpy(p, src, len);
+
+ return p;
+}
+#endif
+
+
#endif /* __BACKPORT_DEVICE_H */
diff --git a/backport-include/linux/dma-buf.h b/backport-include/linux/dma-buf.h
index 13a225e..48c2ebf 100644
--- a/backport-include/linux/dma-buf.h
+++ b/backport-include/linux/dma-buf.h
@@ -4,6 +4,9 @@
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0)
#include_next <linux/dma-buf.h>
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0) */
+#include <linux/dma-direction.h>
+#include <linux/dma-attrs.h>
+#include <linux/dma-mapping.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
#define dma_buf_export(priv, ops, size, flags, resv) \
diff --git a/backport-include/linux/etherdevice.h b/backport-include/linux/etherdevice.h
index 70decd2..34dbf65 100644
--- a/backport-include/linux/etherdevice.h
+++ b/backport-include/linux/etherdevice.h
@@ -178,4 +178,19 @@
int eth_get_headlen(unsigned char *data, unsigned int max_len);
#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0) */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
+#define eth_skb_pad LINUX_BACKPORT(eth_skb_pad)
+/**
+ * eth_skb_pad - Pad buffer to mininum number of octets for Ethernet frame
+ * @skb: Buffer to pad
+ *
+ * An Ethernet frame should have a minimum size of 60 bytes. This function
+ * takes short frames and pads them with zeros up to the 60 byte limit.
+ */
+static inline int eth_skb_pad(struct sk_buff *skb)
+{
+ return skb_put_padto(skb, ETH_ZLEN);
+}
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0) */
+
#endif /* _BACKPORT_LINUX_ETHERDEVICE_H */
diff --git a/backport-include/linux/list.h b/backport-include/linux/list.h
index 9042830..3fbc10b 100644
--- a/backport-include/linux/list.h
+++ b/backport-include/linux/list.h
@@ -17,7 +17,9 @@
#undef hlist_entry_safe
#define hlist_entry_safe(ptr, type, member) \
- (ptr) ? hlist_entry(ptr, type, member) : NULL
+ ({ typeof(ptr) ____ptr = (ptr); \
+ ____ptr ? hlist_entry(____ptr, type, member) : NULL; \
+ })
#define hlist_for_each_entry4(tpos, pos, head, member) \
for (pos = (head)->first; \
@@ -73,4 +75,17 @@
list_entry((pos)->member.next, typeof(*(pos)), member)
#endif /* list_next_entry */
+#ifndef list_last_entry
+/**
+ * list_last_entry - get the last element from a list
+ * @ptr: the list head to take the element from.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define list_last_entry(ptr, type, member) \
+ list_entry((ptr)->prev, type, member)
+#endif
+
#endif /* __BACKPORT_LIST_H */
diff --git a/backport-include/linux/module.h b/backport-include/linux/module.h
index 82c96bd..8870abb 100644
--- a/backport-include/linux/module.h
+++ b/backport-include/linux/module.h
@@ -20,11 +20,12 @@
#define BACKPORT_MOD_VERSIONS MODULE_VERSION(BACKPORTS_GIT_TRACKED);
#else
#define BACKPORT_MOD_VERSIONS \
- MODULE_VERSION("backported from " BACKPORTED_KERNEL_NAME \
- " (" BACKPORTED_KERNEL_VERSION ")" \
- " using backports " BACKPORTS_VERSION);
+ MODULE_VERSION("backported from " CPTCFG_KERNEL_NAME \
+ " (" CPTCFG_KERNEL_VERSION ")" \
+ " using backports " CPTCFG_VERSION);
#endif
+#ifdef MODULE
#undef module_init
#define module_init(initfn) \
static int __init __init_backport(void) \
@@ -58,6 +59,7 @@
rcu_barrier(); \
} \
void cleanup_module(void) __attribute__((alias("__exit_compat")));
+#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0)
#undef param_check_bool
diff --git a/backport-include/linux/netdevice.h b/backport-include/linux/netdevice.h
index 8826771..961450a 100644
--- a/backport-include/linux/netdevice.h
+++ b/backport-include/linux/netdevice.h
@@ -176,4 +176,62 @@
count)
#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,17,0) */
+/*
+ * This backports this commit from upstream:
+ * commit 87757a917b0b3c0787e0563c679762152be81312
+ * net: force a list_del() in unregister_netdevice_many()
+ */
+#if (!(LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,45) && \
+ LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0)) && \
+ !(LINUX_VERSION_CODE >= KERNEL_VERSION(3,12,23) && \
+ LINUX_VERSION_CODE < KERNEL_VERSION(3,13,0)) && \
+ !(LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,9) && \
+ LINUX_VERSION_CODE < KERNEL_VERSION(3,15,0)) && \
+ !(LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,2) && \
+ LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0)) && \
+ (LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0)))
+static inline void backport_unregister_netdevice_many(struct list_head *head)
+{
+ unregister_netdevice_many(head);
+
+ if (!(head->next == LIST_POISON1 && head->prev == LIST_POISON2))
+ list_del(head);
+}
+#define unregister_netdevice_many LINUX_BACKPORT(unregister_netdevice_many)
+#endif
+
+/*
+ * Complicated way of saying: We only backport netdev_rss_key stuff on kernels
+ * that either already have net_get_random_once() (>= 3.13) or where we've been
+ * brave enough to backport it due to static keys, refer to backports commit
+ * 8cb8816d for details on difficulty to backport that further down.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,13,0)
+#define __BACKPORT_NETDEV_RSS_KEY_FILL 1
+#else
+#ifdef __BACKPORT_NET_GET_RANDOM_ONCE
+#define __BACKPORT_NETDEV_RSS_KEY_FILL 1
+#endif
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,13,0) */
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0) */
+
+#ifdef __BACKPORT_NETDEV_RSS_KEY_FILL
+/* RSS keys are 40 or 52 bytes long */
+#define NETDEV_RSS_KEY_LEN 52
+#define netdev_rss_key LINUX_BACKPORT(netdev_rss_key)
+extern u8 netdev_rss_key[NETDEV_RSS_KEY_LEN];
+#define netdev_rss_key_fill LINUX_BACKPORT(netdev_rss_key_fill)
+void netdev_rss_key_fill(void *buffer, size_t len);
+#endif /* __BACKPORT_NETDEV_RSS_KEY_FILL */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
+#define napi_alloc_skb LINUX_BACKPORT(napi_alloc_skb)
+static inline struct sk_buff *napi_alloc_skb(struct napi_struct *napi,
+ unsigned int length)
+{
+ return netdev_alloc_skb_ip_align(napi->dev, length);
+}
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0) */
+
#endif /* __BACKPORT_NETDEVICE_H */
diff --git a/backport-include/linux/of.h b/backport-include/linux/of.h
index f2a8ab4..b289bcb 100644
--- a/backport-include/linux/of.h
+++ b/backport-include/linux/of.h
@@ -29,6 +29,7 @@
#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) */
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)
+#define of_property_read_u8_array LINUX_BACKPORT(of_property_read_u8_array)
#ifdef CONFIG_OF
extern int of_property_read_u8_array(const struct device_node *np,
const char *propname, u8 *out_values, size_t sz);
@@ -39,7 +40,31 @@
return -ENOSYS;
}
#endif /* CONFIG_OF */
-#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) */
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0) */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,1,0)
+#define of_property_read_u32_array LINUX_BACKPORT(of_property_read_u32_array)
+#ifdef CONFIG_OF
+extern int of_property_read_u32_array(const struct device_node *np,
+ const char *propname,
+ u32 *out_values,
+ size_t sz);
+#else
+static inline int of_property_read_u32_array(const struct device_node *np,
+ const char *propname,
+ u32 *out_values, size_t sz)
+{
+ return -ENOSYS;
+}
+#endif /* CONFIG_OF */
+#define of_property_read_u32 LINUX_BACKPORT(of_property_read_u32)
+static inline int of_property_read_u32(const struct device_node *np,
+ const char *propname,
+ u32 *out_value)
+{
+ return of_property_read_u32_array(np, propname, out_value, 1);
+}
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,1,0) */
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
#define of_property_read_u32_index LINUX_BACKPORT(of_property_read_u32_index)
@@ -114,4 +139,22 @@
#endif /* CONFIG_OF */
#endif /* of_match_ptr */
+#ifndef for_each_compatible_node
+#define for_each_compatible_node(dn, type, compatible) \
+ for (dn = of_find_compatible_node(NULL, type, compatible); dn; \
+ dn = of_find_compatible_node(dn, type, compatible))
+#endif /* for_each_compatible_node */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0)
+#ifndef CONFIG_OF
+static inline struct device_node *of_find_compatible_node(
+ struct device_node *from,
+ const char *type,
+ const char *compat)
+{
+ return NULL;
+}
+#endif
+#endif
+
#endif /* _COMPAT_LINUX_OF_H */
diff --git a/backport-include/linux/pci.h b/backport-include/linux/pci.h
index 3dd2c7e..f168bde 100644
--- a/backport-include/linux/pci.h
+++ b/backport-include/linux/pci.h
@@ -159,4 +159,9 @@
#endif
#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,13,0)
+#define pci_device_is_present LINUX_BACKPORT(pci_device_is_present)
+bool pci_device_is_present(struct pci_dev *pdev);
+#endif
+
#endif /* _BACKPORT_LINUX_PCI_H */
diff --git a/backport-include/linux/pci_regs.h b/backport-include/linux/pci_regs.h
deleted file mode 100644
index b52c4a5..0000000
--- a/backport-include/linux/pci_regs.h
+++ /dev/null
@@ -1,136 +0,0 @@
-#ifndef __BACKPORT_UAPI_PCI_REGS_H
-#define __BACKPORT_UAPI_PCI_REGS_H
-#include_next <linux/pci_regs.h>
-
-#ifndef PCI_EXP_LNKCTL_ASPM_L0S
-#define PCI_EXP_LNKCTL_ASPM_L0S 0x01 /* L0s Enable */
-#endif
-
-#ifndef PCI_EXP_LNKCTL_ASPM_L1
-#define PCI_EXP_LNKCTL_ASPM_L1 0x02 /* L1 Enable */
-#endif
-
-/* This backports:
- *
- * commit 130f1b8f35f14d27c43da755f3c9226318c17f57
- * Author: Bjorn Helgaas <bhelgaas@google.com>
- * Date: Wed Dec 26 10:39:23 2012 -0700
- *
- * PCI: Add PCIe Link Capability link speed and width names
- */
-#ifndef PCI_EXP_LNKCAP_SLS_2_5GB
-#define PCI_EXP_LNKCAP_SLS_2_5GB 0x1 /* LNKCAP2 SLS Vector bit 0 (2.5GT/s) */
-#endif
-
-#ifndef PCI_EXP_LNKCAP_SLS_5_0GB
-#define PCI_EXP_LNKCAP_SLS_5_0GB 0x2 /* LNKCAP2 SLS Vector bit 1 (5.0GT/s) */
-#endif
-
-#ifndef PCI_EXP_LNKSTA2
-#define PCI_EXP_LNKSTA2 50 /* Link Status 2 */
-#endif
-
-/**
- * Backports
- *
- * commit cdcac9cd7741af2c2b9255cbf060f772596907bb
- * Author: Dave Airlie <airlied@redhat.com>
- * Date: Wed Jun 27 08:35:52 2012 +0100
- *
- * pci_regs: define LNKSTA2 pcie cap + bits.
- *
- * We need these for detecting the max link speed for drm drivers.
- *
- * Acked-by: Bjorn Helgaas <bhelgass@google.com>
- * Signed-off-by: Dave Airlie <airlied@redhat.com>
- */
-#ifndef PCI_EXP_LNKCAP2
-#define PCI_EXP_LNKCAP2 44 /* Link Capability 2 */
-#endif
-
-#ifndef PCI_EXP_LNKCAP2_SLS_2_5GB
-#define PCI_EXP_LNKCAP2_SLS_2_5GB 0x01 /* Current Link Speed 2.5GT/s */
-#endif
-
-#ifndef PCI_EXP_LNKCAP2_SLS_5_0GB
-#define PCI_EXP_LNKCAP2_SLS_5_0GB 0x02 /* Current Link Speed 5.0GT/s */
-#endif
-
-#ifndef PCI_EXP_LNKCAP2_SLS_8_0GB
-#define PCI_EXP_LNKCAP2_SLS_8_0GB 0x04 /* Current Link Speed 8.0GT/s */
-#endif
-
-#ifndef PCI_EXP_LNKCAP2_CROSSLINK
-#define PCI_EXP_LNKCAP2_CROSSLINK 0x100 /* Crosslink supported */
-#endif
-
-/*
- * PCI_EXP_TYPE_RC_EC was added via 1b6b8ce2 on v2.6.30-rc4~20 :
- *
- * mcgrof@frijol ~/linux-next (git::master)$ git describe --contains 1b6b8ce2
- * v2.6.30-rc4~20^2
- *
- * but the fix for its definition was merged on v3.3-rc1~101^2~67
- *
- * mcgrof@frijol ~/linux-next (git::master)$ git describe --contains 1830ea91
- * v3.3-rc1~101^2~67
- *
- * while we can assume it got merged and backported on v3.2.28 (which it did
- * see c1c3cd9) we cannot assume every kernel has it fixed so lets just undef
- * it here and redefine it.
- */
-#undef PCI_EXP_TYPE_RC_EC
-#define PCI_EXP_TYPE_RC_EC 0xa /* Root Complex Event Collector */
-
-#ifndef PCI_MSIX_ENTRY_CTRL_MASKBIT
-#define PCI_MSIX_ENTRY_CTRL_MASKBIT 1
-#endif
-
-/* MSI-X entry's format */
-#ifndef PCI_MSIX_ENTRY_SIZE
-#define PCI_MSIX_ENTRY_SIZE 16
-#define PCI_MSIX_ENTRY_LOWER_ADDR 0
-#define PCI_MSIX_ENTRY_UPPER_ADDR 4
-#define PCI_MSIX_ENTRY_DATA 8
-#define PCI_MSIX_ENTRY_VECTOR_CTRL 12
-#endif
-
-#ifndef PCI_EXP_LNKCTL2
-#define PCI_EXP_LNKCTL2 48 /* Link Control 2 */
-#endif
-
-#ifndef PCI_EXP_SLTCTL2
-#define PCI_EXP_SLTCTL2 56 /* Slot Control 2 */
-#endif
-
-#ifndef PCI_EXP_LNKCTL_ES
-#define PCI_EXP_LNKCTL_ES 0x0080 /* Extended Synch */
-#endif
-
-#ifndef PCI_EXP_SLTSTA_PDS
-#define PCI_EXP_SLTSTA_PDS 0x0040 /* Presence Detect State */
-#endif
-
-#ifndef PCI_EXP_DEVCAP2
-#define PCI_EXP_DEVCAP2 36 /* Device Capabilities 2 */
-#define PCI_EXP_DEVCAP2_ARI 0x20 /* Alternative Routing-ID */
-#endif
-
-#ifndef PCI_EXP_DEVCTL2
-#define PCI_EXP_DEVCTL2 40 /* Device Control 2 */
-#define PCI_EXP_DEVCTL2_ARI 0x20 /* Alternative Routing-ID */
-#endif
-
-#ifndef PCI_PM_CAP_PME_SHIFT
-#define PCI_PM_CAP_PME_SHIFT 11
-#endif
-
-#ifndef PCI_SRIOV_VF_DID
-#define PCI_SRIOV_VF_DID 0x1a /* VF Device ID */
-#endif
-
-#ifndef PCI_SRIOV_CTRL_VFE
-#define PCI_SRIOV_CTRL_VFE 0x01 /* VF Enable */
-#endif
-
-#endif /* __BACKPORT_UAPI_PCI_REGS_H */
diff --git a/backport-include/linux/rculist.h b/backport-include/linux/rculist.h
index eb3bcfe..74009ab 100644
--- a/backport-include/linux/rculist.h
+++ b/backport-include/linux/rculist.h
@@ -34,4 +34,24 @@
container_of(rcu_dereference(ptr), type, member)
#endif
+#ifndef list_first_or_null_rcu
+/**
+ * list_first_or_null_rcu - get the first element from a list
+ * @ptr: the list head to take the element from.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ *
+ * Note that if the list is empty, it returns NULL.
+ *
+ * This primitive may safely run concurrently with the _rcu list-mutation
+ * primitives such as list_add_rcu() as long as it's guarded by rcu_read_lock().
+ */
+#define list_first_or_null_rcu(ptr, type, member) \
+({ \
+ struct list_head *__ptr = (ptr); \
+ struct list_head *__next = ACCESS_ONCE(__ptr->next); \
+ likely(__ptr != __next) ? list_entry_rcu(__next, type, member) : NULL; \
+})
+#endif /* list_first_or_null_rcu */
+
#endif /* __BACKPORT_RCULIST_H */
diff --git a/backport-include/linux/skbuff.h b/backport-include/linux/skbuff.h
index 5a1725d..687297c 100644
--- a/backport-include/linux/skbuff.h
+++ b/backport-include/linux/skbuff.h
@@ -96,52 +96,6 @@
}
#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0) */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0)
-/**
- * __skb_alloc_pages - allocate pages for ps-rx on a skb and preserve pfmemalloc data
- * @gfp_mask: alloc_pages_node mask. Set __GFP_NOMEMALLOC if not for network packet RX
- * @skb: skb to set pfmemalloc on if __GFP_MEMALLOC is used
- * @order: size of the allocation
- *
- * Allocate a new page.
- *
- * %NULL is returned if there is no free memory.
-*/
-static inline struct page *__skb_alloc_pages(gfp_t gfp_mask,
- struct sk_buff *skb,
- unsigned int order)
-{
- struct page *page;
-
- gfp_mask |= __GFP_COLD;
-#if 0
- if (!(gfp_mask & __GFP_NOMEMALLOC))
- gfp_mask |= __GFP_MEMALLOC;
-#endif
- page = alloc_pages_node(NUMA_NO_NODE, gfp_mask, order);
-#if 0
- if (skb && page && page->pfmemalloc)
- skb->pfmemalloc = true;
-#endif
- return page;
-}
-
-/**
- * __skb_alloc_page - allocate a page for ps-rx for a given skb and preserve pfmemalloc data
- * @gfp_mask: alloc_pages_node mask. Set __GFP_NOMEMALLOC if not for network packet RX
- * @skb: skb to set pfmemalloc on if __GFP_MEMALLOC is used
- *
- * Allocate a new page.
- *
- * %NULL is returned if there is no free memory.
- */
-static inline struct page *__skb_alloc_page(gfp_t gfp_mask,
- struct sk_buff *skb)
-{
- return __skb_alloc_pages(gfp_mask, skb, 0);
-}
-#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0) */
-
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0)
#ifndef NETDEV_FRAG_PAGE_MAX_ORDER
#define NETDEV_FRAG_PAGE_MAX_ORDER get_order(32768)
@@ -164,129 +118,6 @@
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0)
-#define skb_frag_size_set LINUX_BACKPORT(skb_frag_size_set)
-static inline void skb_frag_size_set(skb_frag_t *frag, unsigned int size)
-{
- frag->size = size;
-}
-
-#define skb_frag_size_add LINUX_BACKPORT(skb_frag_size_add)
-static inline void skb_frag_size_add(skb_frag_t *frag, int delta)
-{
- frag->size += delta;
-}
-
-#define __skb_fill_page_desc LINUX_BACKPORT(__skb_fill_page_desc)
-/**
- * __skb_fill_page_desc - initialise a paged fragment in an skb
- * @skb: buffer containing fragment to be initialised
- * @i: paged fragment index to initialise
- * @page: the page to use for this fragment
- * @off: the offset to the data with @page
- * @size: the length of the data
- *
- * Initialises the @i'th fragment of @skb to point to &size bytes at
- * offset @off within @page.
- *
- * Does not take any additional reference on the fragment.
- */
-static inline void __skb_fill_page_desc(struct sk_buff *skb, int i,
- struct page *page, int off, int size)
-{
- skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
-
- /*
- * Propagate page->pfmemalloc to the skb if we can. The problem is
- * that not all callers have unique ownership of the page. If
- * pfmemalloc is set, we check the mapping as a mapping implies
- * page->index is set (index and pfmemalloc share space).
- * If it's a valid mapping, we cannot use page->pfmemalloc but we
- * do not lose pfmemalloc information as the pages would not be
- * allocated using __GFP_MEMALLOC.
- */
- frag->page = page;
- frag->page_offset = off;
- skb_frag_size_set(frag, size);
-
-#if 0 /* we can't backport this for older kernels */
- page = compound_head(page);
- if (page->pfmemalloc && !page->mapping)
- skb->pfmemalloc = true;
-#endif
-}
-
-#define skb_fill_page_desc LINUX_BACKPORT(skb_fill_page_desc)
-/**
- * skb_fill_page_desc - initialise a paged fragment in an skb
- * @skb: buffer containing fragment to be initialised
- * @i: paged fragment index to initialise
- * @page: the page to use for this fragment
- * @off: the offset to the data with @page
- * @size: the length of the data
- *
- * As per __skb_fill_page_desc() -- initialises the @i'th fragment of
- * @skb to point to @size bytes at offset @off within @page. In
- * addition updates @skb such that @i is the last fragment.
- *
- * Does not take any additional reference on the fragment.
- */
-static inline void skb_fill_page_desc(struct sk_buff *skb, int i,
- struct page *page, int off, int size)
-{
- __skb_fill_page_desc(skb, i, page, off, size);
- skb_shinfo(skb)->nr_frags = i + 1;
-}
-
-#define __skb_frag_ref LINUX_BACKPORT(__skb_frag_ref)
-/**
- * __skb_frag_ref - take an addition reference on kb_frag_page paged fragment.
- * @frag: the paged fragment
- *
- * Takes an additional reference on the paged fragment @frag.
- */
-static inline void __skb_frag_ref(skb_frag_t *frag)
-{
- get_page(skb_frag_page(frag));
-}
-
-#define skb_frag_ref LINUX_BACKPORT(skb_frag_ref)
-/**
- * skb_frag_ref - take an addition reference on a paged fragment of an skb.
- * @skb: the buffer
- * @f: the fragment offset.
- *
- * Takes an additional reference on the @f'th paged fragment of @skb.
- */
-static inline void skb_frag_ref(struct sk_buff *skb, int f)
-{
- __skb_frag_ref(&skb_shinfo(skb)->frags[f]);
-}
-
-#define __skb_frag_unref LINUX_BACKPORT(__skb_frag_unref)
-/**
- * __skb_frag_unref - release a reference on a paged fragment.
- * @frag: the paged fragment
- *
- * Releases a reference on the paged fragment @frag.
- */
-static inline void __skb_frag_unref(skb_frag_t *frag)
-{
- put_page(skb_frag_page(frag));
-}
-
-#define skb_frag_unref LINUX_BACKPORT(skb_frag_unref)
-/**
- * skb_frag_unref - release a reference on a paged fragment of an skb.
- * @skb: the buffer
- * @f: the fragment offset
- *
- * Releases a reference on the @f'th paged fragment of @skb.
- */
-static inline void skb_frag_unref(struct sk_buff *skb, int f)
-{
- __skb_frag_unref(&skb_shinfo(skb)->frags[f]);
-}
-
#define skb_frag_address_safe LINUX_BACKPORT(skb_frag_address_safe)
/**
* skb_frag_address_safe - gets the address of the data contained in a paged fragment
@@ -303,34 +134,6 @@
return ptr + frag->page_offset;
}
-
-#define __skb_frag_set_page LINUX_BACKPORT(__skb_frag_set_page)
-/**
- * __skb_frag_set_page - sets the page contained in a paged fragment
- * @frag: the paged fragment
- * @page: the page to set
- *
- * Sets the fragment @frag to contain @page.
- */
-static inline void __skb_frag_set_page(skb_frag_t *frag, struct page *page)
-{
- frag->page = page;
-}
-
-#define skb_frag_set_page LINUX_BACKPORT(skb_frag_set_page)
-/**
- * skb_frag_set_page - sets the page contained in a paged fragment of an skb
- * @skb: the buffer
- * @f: the fragment offset
- * @page: the page to set
- *
- * Sets the @f'th fragment of @skb to contain @page.
- */
-static inline void skb_frag_set_page(struct sk_buff *skb, int f,
- struct page *page)
-{
- __skb_frag_set_page(&skb_shinfo(skb)->frags[f], page);
-}
#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0) */
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0) && \
@@ -396,4 +199,100 @@
struct sk_buff *skb_clone_sk(struct sk_buff *skb);
#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
+/**
+ * __dev_alloc_pages - allocate page for network Rx
+ * @gfp_mask: allocation priority. Set __GFP_NOMEMALLOC if not for network Rx
+ * @order: size of the allocation
+ *
+ * Allocate a new page.
+ *
+ * %NULL is returned if there is no free memory.
+*/
+#define __dev_alloc_pages LINUX_BACKPORT(__dev_alloc_pages)
+static inline struct page *__dev_alloc_pages(gfp_t gfp_mask,
+ unsigned int order)
+{
+ /* This piece of code contains several assumptions.
+ * 1. This is for device Rx, therefor a cold page is preferred.
+ * 2. The expectation is the user wants a compound page.
+ * 3. If requesting a order 0 page it will not be compound
+ * due to the check to see if order has a value in prep_new_page
+ * 4. __GFP_MEMALLOC is ignored if __GFP_NOMEMALLOC is set due to
+ * code in gfp_to_alloc_flags that should be enforcing this.
+ */
+ gfp_mask |= __GFP_COLD | __GFP_COMP;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)
+ gfp_mask |= __GFP_MEMALLOC;
+#endif
+
+ return alloc_pages_node(NUMA_NO_NODE, gfp_mask, order);
+}
+
+#define dev_alloc_pages LINUX_BACKPORT(dev_alloc_pages)
+static inline struct page *dev_alloc_pages(unsigned int order)
+{
+ return __dev_alloc_pages(GFP_ATOMIC, order);
+}
+
+/**
+ * __dev_alloc_page - allocate a page for network Rx
+ * @gfp_mask: allocation priority. Set __GFP_NOMEMALLOC if not for network Rx
+ *
+ * Allocate a new page.
+ *
+ * %NULL is returned if there is no free memory.
+ */
+#define __dev_alloc_page LINUX_BACKPORT(__dev_alloc_page)
+static inline struct page *__dev_alloc_page(gfp_t gfp_mask)
+{
+ return __dev_alloc_pages(gfp_mask, 0);
+}
+
+#define dev_alloc_page LINUX_BACKPORT(dev_alloc_page)
+static inline struct page *dev_alloc_page(void)
+{
+ return __dev_alloc_page(GFP_ATOMIC);
+}
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0) */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
+#define skb_copy_datagram_msg LINUX_BACKPORT(skb_copy_datagram_msg)
+static inline int skb_copy_datagram_msg(const struct sk_buff *from, int offset,
+ struct msghdr *msg, int size)
+{
+ return skb_copy_datagram_iovec(from, offset, msg->msg_iov, size);
+}
+
+#define memcpy_from_msg LINUX_BACKPORT(memcpy_from_msg)
+static inline int memcpy_from_msg(void *data, struct msghdr *msg, int len)
+{
+ return memcpy_fromiovec(data, msg->msg_iov, len);
+}
+
+/**
+ * skb_put_padto - increase size and pad an skbuff up to a minimal size
+ * @skb: buffer to pad
+ * @len: minimal length
+ *
+ * Pads up a buffer to ensure the trailing bytes exist and are
+ * blanked. If the buffer already contains sufficient data it
+ * is untouched. Otherwise it is extended. Returns zero on
+ * success. The skb is freed on error.
+ */
+#define skb_put_padto LINUX_BACKPORT(skb_put_padto)
+static inline int skb_put_padto(struct sk_buff *skb, unsigned int len)
+{
+ unsigned int size = skb->len;
+
+ if (unlikely(size < len)) {
+ len -= size;
+ if (skb_pad(skb, len))
+ return -ENOMEM;
+ __skb_put(skb, len);
+ }
+ return 0;
+}
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0) */
+
#endif /* __BACKPORT_SKBUFF_H */
diff --git a/backport-include/linux/wait.h b/backport-include/linux/wait.h
index dfb111d..4057ff1 100644
--- a/backport-include/linux/wait.h
+++ b/backport-include/linux/wait.h
@@ -23,4 +23,13 @@
#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
+#define WQ_FLAG_WOKEN 0x02
+
+#define wait_woken LINUX_BACKPORT(wait_woken)
+long wait_woken(wait_queue_t *wait, unsigned mode, long timeout);
+#define wait_woken LINUX_BACKPORT(wait_woken)
+int woken_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key);
+#endif
+
#endif /* __BACKPORT_LINUX_WAIT_H */
diff --git a/backport-include/linux/workqueue.h b/backport-include/linux/workqueue.h
index ec5c230..078ed59 100644
--- a/backport-include/linux/workqueue.h
+++ b/backport-include/linux/workqueue.h
@@ -60,4 +60,8 @@
#define system_freezable_power_efficient_wq system_freezable_wq
#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,1,0)
+#define drain_workqueue(wq) flush_workqueue(wq)
+#endif
+
#endif /* __BACKPORT_LINUX_WORKQUEUE_H */
diff --git a/backport-include/net/genetlink.h b/backport-include/net/genetlink.h
index fc7eff4..0535b0f 100644
--- a/backport-include/net/genetlink.h
+++ b/backport-include/net/genetlink.h
@@ -1,6 +1,7 @@
#ifndef __BACKPORT_NET_GENETLINK_H
#define __BACKPORT_NET_GENETLINK_H
#include_next <net/genetlink.h>
+#include <linux/version.h>
/* this is for patches we apply */
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)
@@ -85,6 +86,10 @@
#define genl_unregister_family backport_genl_unregister_family
int genl_unregister_family(struct genl_family *family);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0)
+extern void genl_notify(struct sk_buff *skb, struct net *net, u32 pid,
+ u32 group, struct nlmsghdr *nlh, gfp_t flags);
+#endif
#define genl_notify(_fam, _skb, _net, _portid, _group, _nlh, _flags) \
genl_notify(_skb, _net, _portid, (_fam)->mcgrps[_group].id, \
_nlh, _flags)
diff --git a/backport-include/net/ip6_fib.h b/backport-include/net/ip6_fib.h
new file mode 100644
index 0000000..8c5d8ff
--- /dev/null
+++ b/backport-include/net/ip6_fib.h
@@ -0,0 +1,15 @@
+#ifndef __BACKPORT_NET_IP6_ROUTE_H
+#define __BACKPORT_NET_IP6_ROUTE_H
+#include_next <net/ip6_fib.h>
+#include <net/ip6_route.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
+#define rt6_nexthop LINUX_BACKPORT(rt6_nexthop)
+static inline struct in6_addr *rt6_nexthop(struct rt6_info *rt)
+{
+ return &rt->rt6i_gateway;
+}
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) */
+
+#endif /* __BACKPORT_NET_IP6_ROUTE_H */
diff --git a/backport-include/sound/pcm.h b/backport-include/sound/pcm.h
new file mode 100644
index 0000000..469e871
--- /dev/null
+++ b/backport-include/sound/pcm.h
@@ -0,0 +1,29 @@
+#ifndef __BACKPORT_SOUND_PCM_H
+#define __BACKPORT_SOUND_PCM_H
+#include_next <sound/pcm.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
+/**
+ * snd_pcm_stop_xrun - stop the running streams as XRUN
+ * @substream: the PCM substream instance
+ *
+ * This stops the given running substream (and all linked substreams) as XRUN.
+ * Unlike snd_pcm_stop(), this function takes the substream lock by itself.
+ *
+ * Return: Zero if successful, or a negative error code.
+ */
+static inline int snd_pcm_stop_xrun(struct snd_pcm_substream *substream)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ snd_pcm_stream_lock_irqsave(substream, flags);
+ if (snd_pcm_running(substream))
+ ret = snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+ snd_pcm_stream_unlock_irqrestore(substream, flags);
+ return ret;
+}
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0) */
+
+#endif /* __BACKPORT_SOUND_PCM_H */
diff --git a/compat/Kconfig b/compat/Kconfig
index d501092..a5ae300 100644
--- a/compat/Kconfig
+++ b/compat/Kconfig
@@ -1,37 +1,37 @@
#
# backport Kconfig
#
-# Some options are user-selectable ("BACKPORT_USERSEL_*")
+# Some options are user-selectable ("BPAUTO_USERSEL_*")
#
# Most options, however, follow a few different schemes:
#
# A) An option that is selected by drivers ("select FOO") will be
-# changed to "select BACKPORT_FOO" (if the option BACKPORT_FOO
-# exists). The option BACKPORT_FOO then controls setting of the
-# BACKPORT_BUILD_FOO option, which is a module, like this:
+# changed to "select BPAUTO_FOO" (if the option BPAUTO_FOO
+# exists). The option BPAUTO_FOO then controls setting of the
+# BPAUTO_BUILD_FOO option, which is a module, like this:
#
-# config BACKPORT_BUILD_FOO
+# config BPAUTO_BUILD_FOO
# tristate
# # or bool
#
# # not possible on kernel < X.Y, build will fail if any
# # drivers are allowed to build on kernels < X.Y
-# depends on BACKPORT_KERNEL_X_Y
+# depends on KERNEL_X_Y
#
# # don't build the backport code if FOO is in the kernel
# # already, but only if the kernel version is also >= X.Z;
# # this is an example of backporting where the version of
# # the FOO subsystem that we need is only available from
# # kernel version X.Z
-# depends on !FOO || BACKPORT_KERNEL_X_Z
+# depends on !FOO || KERNEL_X_Z
#
-# # build if driver needs it (it selects BACKPORT_FOO)
-# default m if BACKPORT_FOO
+# # build if driver needs it (it selects BPAUTO_FOO)
+# default m if BPAUTO_FOO
#
-# # or for build-testing (BACKPORT_USERSEL_BUILD_ALL is enabled)
-# default m if BACKPORT_USERSEL_BUILD_ALL
+# # or for build-testing (BPAUTO_USERSEL_BUILD_ALL is enabled)
+# default m if BPAUTO_USERSEL_BUILD_ALL
#
-# config BACKPORT_FOO
+# config BPAUTO_FOO
# bool
#
# This only works as-is if the kernel code is usable on any version,
@@ -41,72 +41,72 @@
# B) An option for code always present on some kernels (e.g. KFIFO).
# This simply depends on/sets the default based on the version:
#
-# config BACKPORT_BUILD_KFIFO
+# config BPAUTO_BUILD_KFIFO
# def_bool y
-# depends on BACKPORT_KERNEL_2_6_36
+# depends on KERNEL_2_6_36
#
#
# C) similarly, a kconfig symbol for an option, e.g.
-# BACKPORT_OPTION_SOME_FIX (no examples provided) check git log
+# BPAUTO_OPTION_SOME_FIX (no examples provided) check git log
#
#
# Variations are obviously possible.
#
-config BACKPORT_BUILD_CORDIC
+config BPAUTO_BUILD_CORDIC
tristate
depends on m
depends on !CORDIC
- default m if BACKPORT_CORDIC
- default m if BACKPORT_USERSEL_BUILD_ALL
+ default m if BPAUTO_CORDIC
+ default m if BPAUTO_USERSEL_BUILD_ALL
#module-name cordic
#c-file lib/cordic.c
-config BACKPORT_CORDIC
+config BPAUTO_CORDIC
bool
-config BACKPORT_BUILD_AVERAGE
+config BPAUTO_BUILD_AVERAGE
bool
depends on !AVERAGE
- default y if BACKPORT_USERSEL_BUILD_ALL
- default y if BACKPORT_AVERAGE
+ default y if BPAUTO_USERSEL_BUILD_ALL
+ default y if BPAUTO_AVERAGE
#h-file linux/average.h
#c-file lib/average.c
-config BACKPORT_AVERAGE
+config BPAUTO_AVERAGE
bool
-config BACKPORT_MII
+config BPAUTO_MII
bool
-config BACKPORT_BUILD_DMA_SHARED_HELPERS
+config BPAUTO_BUILD_DMA_SHARED_HELPERS
bool
depends on HAS_DMA
# Build on other kernels kernels < 3.9 if HAVE_GENERIC_DMA_COHERENT is
# not set. Kernels >= 3.8 have this if HAS_DMA is set.
- depends on (!HAVE_GENERIC_DMA_COHERENT || BACKPORT_KERNEL_3_9)
+ depends on (!HAVE_GENERIC_DMA_COHERENT || KERNEL_3_9)
# Always build if on 3.3 - 3.5
- default y if (BACKPORT_KERNEL_3_4 || BACKPORT_KERNEL_3_5 || BACKPORT_KERNEL_3_6)
+ default y if (KERNEL_3_4 || KERNEL_3_5 || KERNEL_3_6)
# build for testing
- default y if BACKPORT_USERSEL_BUILD_ALL
+ default y if BPAUTO_USERSEL_BUILD_ALL
-config BACKPORT_BUILD_LEDS
+config BPAUTO_BUILD_LEDS
bool
depends on !NEW_LEDS || LEDS_CLASS=n || !LEDS_TRIGGERS
- default y if BACKPORT_NEW_LEDS
- default y if BACKPORT_LEDS_CLASS
- default y if BACKPORT_LEDS_TRIGGERS
+ default y if BPAUTO_NEW_LEDS
+ default y if BPAUTO_LEDS_CLASS
+ default y if BPAUTO_LEDS_TRIGGERS
-config BACKPORT_NEW_LEDS
+config BPAUTO_NEW_LEDS
bool
-config BACKPORT_LEDS_CLASS
+config BPAUTO_LEDS_CLASS
bool
-config BACKPORT_LEDS_TRIGGERS
+config BPAUTO_LEDS_TRIGGERS
bool
-config BACKPORT_USERSEL_BUILD_ALL
+config BPAUTO_USERSEL_BUILD_ALL
bool "Build all compat code"
help
This option selects all the compat code options
@@ -115,25 +115,24 @@
It's only really useful for compat testing, so
you probably shouldn't enable it.
-config BACKPORT_CRYPTO_CCM
+config BPAUTO_CRYPTO_CCM
depends on CRYPTO_AEAD
depends on CRYPTO_CTR
bool
-config BACKPORT_BUILD_CRYPTO_CCM
+config BPAUTO_BUILD_CRYPTO_CCM
bool
default n if CRYPTO_CCM
- default y if BACKPORT_CRYPTO_CCM
+ default y if BPAUTO_CRYPTO_CCM
#c-file crypto/ccm.c
-config BACKPORT_WANT_DEV_COREDUMP
+config BPAUTO_WANT_DEV_COREDUMP
bool
-config BACKPORT_BUILD_WANT_DEV_COREDUMP
- depends on n
+config BPAUTO_BUILD_WANT_DEV_COREDUMP
bool
default n if DEV_COREDUMP
default n if DISABLE_DEV_COREDUMP
- default y if BACKPORT_WANT_DEV_COREDUMP
+ default y if BPAUTO_WANT_DEV_COREDUMP
#h-file linux/devcoredump.h
#c-file drivers/base/devcoredump.c
diff --git a/compat/Makefile b/compat/Makefile
index 1df6225..36c4377 100644
--- a/compat/Makefile
+++ b/compat/Makefile
@@ -1,29 +1,35 @@
ccflags-y += -I$(src)
+ifeq ($(CONFIG_BACKPORT_INTEGRATE),)
obj-m += compat.o
+else
+obj-y += compat.o
+endif
compat-y += main.o
# Kernel backport compatibility code
-compat-$(CPTCFG_BACKPORT_KERNEL_3_0) += compat-3.0.o
-compat-$(CPTCFG_BACKPORT_KERNEL_3_1) += compat-3.1.o
-compat-$(CPTCFG_BACKPORT_KERNEL_3_2) += backport-3.2.o
-compat-$(CPTCFG_BACKPORT_KERNEL_3_3) += compat-3.3.o
-compat-$(CPTCFG_BACKPORT_KERNEL_3_4) += compat-3.4.o
-compat-$(CPTCFG_BACKPORT_KERNEL_3_5) += compat-3.5.o user_namespace.o
-compat-$(CPTCFG_BACKPORT_KERNEL_3_6) += compat-3.6.o
-compat-$(CPTCFG_BACKPORT_KERNEL_3_7) += compat-3.7.o
-compat-$(CPTCFG_BACKPORT_KERNEL_3_8) += compat-3.8.o
-compat-$(CPTCFG_BACKPORT_KERNEL_3_9) += compat-3.9.o
-compat-$(CPTCFG_BACKPORT_KERNEL_3_10) += backport-3.10.o
-compat-$(CPTCFG_BACKPORT_KERNEL_3_12) += backport-3.12.o
-compat-$(CPTCFG_BACKPORT_KERNEL_3_13) += backport-3.13.o
-compat-$(CPTCFG_BACKPORT_KERNEL_3_14) += backport-3.14.o
-compat-$(CPTCFG_BACKPORT_KERNEL_3_15) += backport-3.15.o
-compat-$(CPTCFG_BACKPORT_KERNEL_3_17) += backport-3.17.o
-compat-$(CPTCFG_BACKPORT_KERNEL_3_18) += backport-3.18.o
+compat-$(CPTCFG_KERNEL_3_0) += compat-3.0.o
+compat-$(CPTCFG_KERNEL_3_1) += compat-3.1.o
+compat-$(CPTCFG_KERNEL_3_2) += backport-3.2.o
+compat-$(CPTCFG_KERNEL_3_3) += compat-3.3.o
+compat-$(CPTCFG_KERNEL_3_4) += compat-3.4.o
+compat-$(CPTCFG_KERNEL_3_5) += compat-3.5.o user_namespace.o
+compat-$(CPTCFG_KERNEL_3_6) += compat-3.6.o
+compat-$(CPTCFG_KERNEL_3_7) += compat-3.7.o
+compat-$(CPTCFG_KERNEL_3_8) += compat-3.8.o
+compat-$(CPTCFG_KERNEL_3_9) += compat-3.9.o
+compat-$(CPTCFG_KERNEL_3_10) += backport-3.10.o
+compat-$(CPTCFG_KERNEL_3_12) += backport-3.12.o
+compat-$(CPTCFG_KERNEL_3_13) += backport-3.13.o
+compat-$(CPTCFG_KERNEL_3_14) += backport-3.14.o
+compat-$(CPTCFG_KERNEL_3_15) += backport-3.15.o
+compat-$(CPTCFG_KERNEL_3_17) += backport-3.17.o
+compat-$(CPTCFG_KERNEL_3_18) += backport-3.18.o
+compat-$(CPTCFG_KERNEL_3_19) += backport-3.19.o
-compat-$(CPTCFG_BACKPORT_BUILD_CRYPTO_CCM) += crypto-ccm.o
-compat-$(CPTCFG_BACKPORT_BUILD_DMA_SHARED_HELPERS) += dma-shared-helpers.o
+compat-$(CPTCFG_BPAUTO_BUILD_CRYPTO_CCM) += crypto-ccm.o
+compat-$(CPTCFG_BPAUTO_BUILD_DMA_SHARED_HELPERS) += dma-shared-helpers.o
+compat-$(CPTCFG_BPAUTO_BUILD_WANT_DEV_COREDUMP) += drivers-base-devcoredump.o
+compat-$(CPTCFG_BPAUTO_BUILD_AVERAGE) += lib-average.o
cordic-objs += lib-cordic.o
-obj-$(CPTCFG_BACKPORT_BUILD_CORDIC) += cordic.o
-compat-$(CPTCFG_BACKPORT_BUILD_AVERAGE) += lib-average.o
-compat-$(CPTCFG_BACKPORT_BUILD_CRYPTO_CCM) += crypto-ccm.o
+obj-$(CPTCFG_BPAUTO_BUILD_CORDIC) += cordic.o
+compat-$(CPTCFG_BPAUTO_BUILD_CRYPTO_CCM) += crypto-ccm.o
diff --git a/compat/backport-3.13.c b/compat/backport-3.13.c
index fd562bb..553ed8f 100644
--- a/compat/backport-3.13.c
+++ b/compat/backport-3.13.c
@@ -12,6 +12,8 @@
#include <linux/version.h>
#include <linux/kernel.h>
#include <net/genetlink.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0))
#ifdef CONFIG_REGULATOR
@@ -200,3 +202,53 @@
}
EXPORT_SYMBOL_GPL(__net_get_random_once);
#endif /* __BACKPORT_NET_GET_RANDOM_ONCE */
+
+#ifdef CONFIG_PCI
+#define pci_bus_read_dev_vendor_id LINUX_BACKPORT(pci_bus_read_dev_vendor_id)
+static bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
+ int crs_timeout)
+{
+ int delay = 1;
+
+ if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, l))
+ return false;
+
+ /* some broken boards return 0 or ~0 if a slot is empty: */
+ if (*l == 0xffffffff || *l == 0x00000000 ||
+ *l == 0x0000ffff || *l == 0xffff0000)
+ return false;
+
+ /*
+ * Configuration Request Retry Status. Some root ports return the
+ * actual device ID instead of the synthetic ID (0xFFFF) required
+ * by the PCIe spec. Ignore the device ID and only check for
+ * (vendor id == 1).
+ */
+ while ((*l & 0xffff) == 0x0001) {
+ if (!crs_timeout)
+ return false;
+
+ msleep(delay);
+ delay *= 2;
+ if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, l))
+ return false;
+ /* Card hasn't responded in 60 seconds? Must be stuck. */
+ if (delay > crs_timeout) {
+ printk(KERN_WARNING "pci %04x:%02x:%02x.%d: not responding\n",
+ pci_domain_nr(bus), bus->number, PCI_SLOT(devfn),
+ PCI_FUNC(devfn));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool pci_device_is_present(struct pci_dev *pdev)
+{
+ u32 v;
+
+ return pci_bus_read_dev_vendor_id(pdev->bus, pdev->devfn, &v, 0);
+}
+EXPORT_SYMBOL_GPL(pci_device_is_present);
+#endif /* CONFIG_PCI */
diff --git a/compat/backport-3.19.c b/compat/backport-3.19.c
new file mode 100644
index 0000000..c633f38
--- /dev/null
+++ b/compat/backport-3.19.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2014 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Backport functionality introduced in Linux 3.18.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/export.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/debugfs.h>
+
+static inline bool is_kthread_should_stop(void)
+{
+ return (current->flags & PF_KTHREAD) && kthread_should_stop();
+}
+
+/*
+ * DEFINE_WAIT_FUNC(wait, woken_wake_func);
+ *
+ * add_wait_queue(&wq, &wait);
+ * for (;;) {
+ * if (condition)
+ * break;
+ *
+ * p->state = mode; condition = true;
+ * smp_mb(); // A smp_wmb(); // C
+ * if (!wait->flags & WQ_FLAG_WOKEN) wait->flags |= WQ_FLAG_WOKEN;
+ * schedule() try_to_wake_up();
+ * p->state = TASK_RUNNING; ~~~~~~~~~~~~~~~~~~
+ * wait->flags &= ~WQ_FLAG_WOKEN; condition = true;
+ * smp_mb() // B smp_wmb(); // C
+ * wait->flags |= WQ_FLAG_WOKEN;
+ * }
+ * remove_wait_queue(&wq, &wait);
+ *
+ */
+long wait_woken(wait_queue_t *wait, unsigned mode, long timeout)
+{
+ set_current_state(mode); /* A */
+ /*
+ * The above implies an smp_mb(), which matches with the smp_wmb() from
+ * woken_wake_function() such that if we observe WQ_FLAG_WOKEN we must
+ * also observe all state before the wakeup.
+ */
+ if (!(wait->flags & WQ_FLAG_WOKEN) && !is_kthread_should_stop())
+ timeout = schedule_timeout(timeout);
+ __set_current_state(TASK_RUNNING);
+
+ /*
+ * The below implies an smp_mb(), it too pairs with the smp_wmb() from
+ * woken_wake_function() such that we must either observe the wait
+ * condition being true _OR_ WQ_FLAG_WOKEN such that we will not miss
+ * an event.
+ */
+ set_mb(wait->flags, wait->flags & ~WQ_FLAG_WOKEN); /* B */
+
+ return timeout;
+}
+EXPORT_SYMBOL(wait_woken);
+
+int woken_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key)
+{
+ /*
+ * Although this function is called under waitqueue lock, LOCK
+ * doesn't imply write barrier and the users expects write
+ * barrier semantics on wakeup functions. The following
+ * smp_wmb() is equivalent to smp_wmb() in try_to_wake_up()
+ * and is paired with set_mb() in wait_woken().
+ */
+ smp_wmb(); /* C */
+ wait->flags |= WQ_FLAG_WOKEN;
+
+ return default_wake_function(wait, mode, sync, key);
+}
+EXPORT_SYMBOL(woken_wake_function);
+
+#ifdef __BACKPORT_NETDEV_RSS_KEY_FILL
+u8 netdev_rss_key[NETDEV_RSS_KEY_LEN];
+
+void netdev_rss_key_fill(void *buffer, size_t len)
+{
+ BUG_ON(len > sizeof(netdev_rss_key));
+ net_get_random_once(netdev_rss_key, sizeof(netdev_rss_key));
+ memcpy(buffer, netdev_rss_key, len);
+}
+EXPORT_SYMBOL_GPL(netdev_rss_key_fill);
+#endif /* __BACKPORT_NETDEV_RSS_KEY_FILL */
+
+#if defined(CONFIG_DEBUG_FS)
+struct debugfs_devm_entry {
+ int (*read)(struct seq_file *seq, void *data);
+ struct device *dev;
+};
+
+static int debugfs_devm_entry_open(struct inode *inode, struct file *f)
+{
+ struct debugfs_devm_entry *entry = inode->i_private;
+
+ return single_open(f, entry->read, entry->dev);
+}
+
+static const struct file_operations debugfs_devm_entry_ops = {
+ .owner = THIS_MODULE,
+ .open = debugfs_devm_entry_open,
+ .release = single_release,
+ .read = seq_read,
+ .llseek = seq_lseek
+};
+
+/**
+ * debugfs_create_devm_seqfile - create a debugfs file that is bound to device.
+ *
+ * @dev: device related to this debugfs file.
+ * @name: name of the debugfs file.
+ * @parent: a pointer to the parent dentry for this file. This should be a
+ * directory dentry if set. If this parameter is %NULL, then the
+ * file will be created in the root of the debugfs filesystem.
+ * @read_fn: function pointer called to print the seq_file content.
+ */
+struct dentry *debugfs_create_devm_seqfile(struct device *dev, const char *name,
+ struct dentry *parent,
+ int (*read_fn)(struct seq_file *s,
+ void *data))
+{
+ struct debugfs_devm_entry *entry;
+
+ if (IS_ERR(parent))
+ return ERR_PTR(-ENOENT);
+
+ entry = devm_kzalloc(dev, sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return ERR_PTR(-ENOMEM);
+
+ entry->read = read_fn;
+ entry->dev = dev;
+
+ return debugfs_create_file(name, S_IRUGO, parent, entry,
+ &debugfs_devm_entry_ops);
+}
+EXPORT_SYMBOL_GPL(debugfs_create_devm_seqfile);
+
+#endif /* CONFIG_DEBUG_FS */
diff --git a/compat/backports.h b/compat/backports.h
index c9094ac..ccc8972 100644
--- a/compat/backports.h
+++ b/compat/backports.h
@@ -3,7 +3,7 @@
#include <linux/version.h>
-#ifdef CPTCFG_BACKPORT_BUILD_CRYPTO_CCM
+#ifdef CPTCFG_BPAUTO_BUILD_CRYPTO_CCM
int crypto_ccm_module_init(void);
void crypto_ccm_module_exit(void);
#else
@@ -13,7 +13,7 @@
{}
#endif
-#ifdef CPTCFG_BACKPORT_BUILD_WANT_DEV_COREDUMP
+#ifdef CPTCFG_BPAUTO_BUILD_WANT_DEV_COREDUMP
int devcoredump_init(void);
void devcoredump_exit(void);
#else
diff --git a/compat/compat-3.1.c b/compat/compat-3.1.c
index 0d9c3a2..2618780 100644
--- a/compat/compat-3.1.c
+++ b/compat/compat-3.1.c
@@ -10,6 +10,7 @@
#include <linux/idr.h>
#include <linux/cpufreq.h>
+#include <linux/of.h>
static DEFINE_SPINLOCK(compat_simple_ida_lock);
@@ -82,3 +83,36 @@
EXPORT_SYMBOL_GPL(ida_simple_remove);
/* source lib/idr.c */
+#ifdef CONFIG_OF
+/**
+ * of_property_read_u32_array - Find and read an array of 32 bit integers
+ * from a property.
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @out_values: pointer to return value, modified only if return value is 0.
+ * @sz: number of array elements to read
+ *
+ * Search for a property in a device node and read 32-bit value(s) from
+ * it. Returns 0 on success, -EINVAL if the property does not exist,
+ * -ENODATA if property does not have a value, and -EOVERFLOW if the
+ * property data isn't large enough.
+ *
+ * The out_values is modified only if a valid u32 value can be decoded.
+ */
+int of_property_read_u32_array(const struct device_node *np,
+ const char *propname, u32 *out_values,
+ size_t sz)
+{
+ const __be32 *val = of_find_property_value_of_size(np, propname,
+ (sz * sizeof(*out_values)));
+
+ if (IS_ERR(val))
+ return PTR_ERR(val);
+
+ while (sz--)
+ *out_values++ = be32_to_cpup(val++);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_property_read_u32_array);
+#endif
diff --git a/compat/compat-3.3.c b/compat/compat-3.3.c
index 7ddc2bd..490c29b 100644
--- a/compat/compat-3.3.c
+++ b/compat/compat-3.3.c
@@ -224,3 +224,16 @@
spin_unlock(&wq_name_lock);
}
EXPORT_SYMBOL_GPL(backport_destroy_workqueue);
+
+void genl_notify(struct sk_buff *skb, struct net *net, u32 pid, u32 group,
+ struct nlmsghdr *nlh, gfp_t flags)
+{
+ struct sock *sk = net->genl_sock;
+ int report = 0;
+
+ if (nlh)
+ report = nlmsg_report(nlh);
+
+ nlmsg_notify(sk, skb, pid, group, report, flags);
+}
+EXPORT_SYMBOL_GPL(genl_notify);
diff --git a/compat/drivers-base-devcoredump.c b/compat/drivers-base-devcoredump.c
new file mode 100644
index 0000000..f15c830
--- /dev/null
+++ b/compat/drivers-base-devcoredump.c
@@ -0,0 +1,319 @@
+/*
+ * This file is provided under the GPLv2 license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2014 Intel Mobile Communications GmbH
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * Author: Johannes Berg <johannes@sipsolutions.net>
+ */
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/devcoredump.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/workqueue.h>
+#include "backports.h"
+
+static struct class devcd_class;
+
+/* global disable flag, for security purposes */
+static bool devcd_disabled;
+
+/* if data isn't read by userspace after 5 minutes then delete it */
+#define DEVCD_TIMEOUT (HZ * 60 * 5)
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0)
+static struct bin_attribute devcd_attr_data;
+#endif
+
+struct devcd_entry {
+ struct device devcd_dev;
+ const void *data;
+ size_t datalen;
+ struct module *owner;
+ ssize_t (*read)(char *buffer, loff_t offset, size_t count,
+ const void *data, size_t datalen);
+ void (*free)(const void *data);
+ struct delayed_work del_wk;
+ struct device *failing_dev;
+};
+
+static struct devcd_entry *dev_to_devcd(struct device *dev)
+{
+ return container_of(dev, struct devcd_entry, devcd_dev);
+}
+
+static void devcd_dev_release(struct device *dev)
+{
+ struct devcd_entry *devcd = dev_to_devcd(dev);
+
+ devcd->free(devcd->data);
+ module_put(devcd->owner);
+
+ /*
+ * this seems racy, but I don't see a notifier or such on
+ * a struct device to know when it goes away?
+ */
+ if (devcd->failing_dev->kobj.sd)
+ sysfs_remove_link(&devcd->failing_dev->kobj, "devcoredump");
+
+ put_device(devcd->failing_dev);
+ kfree(devcd);
+}
+
+static void devcd_del(struct work_struct *wk)
+{
+ struct devcd_entry *devcd;
+
+ devcd = container_of(wk, struct devcd_entry, del_wk.work);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0)
+ device_remove_bin_file(&devcd->devcd_dev, &devcd_attr_data);
+#endif
+ device_del(&devcd->devcd_dev);
+ put_device(&devcd->devcd_dev);
+}
+
+static ssize_t devcd_data_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buffer, loff_t offset, size_t count)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct devcd_entry *devcd = dev_to_devcd(dev);
+
+ return devcd->read(buffer, offset, count, devcd->data, devcd->datalen);
+}
+
+static ssize_t devcd_data_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buffer, loff_t offset, size_t count)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct devcd_entry *devcd = dev_to_devcd(dev);
+
+ mod_delayed_work(system_wq, &devcd->del_wk, 0);
+
+ return count;
+}
+
+static struct bin_attribute devcd_attr_data = {
+ .attr = { .name = "data", .mode = S_IRUSR | S_IWUSR, },
+ .size = 0,
+ .read = devcd_data_read,
+ .write = devcd_data_write,
+};
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
+static struct bin_attribute *devcd_dev_bin_attrs[] = {
+ &devcd_attr_data, NULL,
+};
+
+static const struct attribute_group devcd_dev_group = {
+ .bin_attrs = devcd_dev_bin_attrs,
+};
+
+static const struct attribute_group *devcd_dev_groups[] = {
+ &devcd_dev_group, NULL,
+};
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0) */
+
+static int devcd_free(struct device *dev, void *data)
+{
+ struct devcd_entry *devcd = dev_to_devcd(dev);
+
+ flush_delayed_work(&devcd->del_wk);
+ return 0;
+}
+
+static ssize_t disabled_show(struct class *class, struct class_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%d\n", devcd_disabled);
+}
+
+static ssize_t disabled_store(struct class *class, struct class_attribute *attr,
+ const char *buf, size_t count)
+{
+ long tmp = simple_strtol(buf, NULL, 10);
+
+ /*
+ * This essentially makes the attribute write-once, since you can't
+ * go back to not having it disabled. This is intentional, it serves
+ * as a system lockdown feature.
+ */
+ if (tmp != 1)
+ return -EINVAL;
+
+ devcd_disabled = true;
+
+ class_for_each_device(&devcd_class, NULL, NULL, devcd_free);
+
+ return count;
+}
+
+static struct class_attribute devcd_class_attrs[] = {
+ __ATTR_RW(disabled),
+ __ATTR_NULL
+};
+
+static struct class devcd_class = {
+ .name = "devcoredump",
+ .owner = THIS_MODULE,
+ .dev_release = devcd_dev_release,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
+ .dev_groups = devcd_dev_groups,
+#endif
+ .class_attrs = devcd_class_attrs,
+};
+
+static ssize_t devcd_readv(char *buffer, loff_t offset, size_t count,
+ const void *data, size_t datalen)
+{
+ if (offset > datalen)
+ return -EINVAL;
+
+ if (offset + count > datalen)
+ count = datalen - offset;
+
+ if (count)
+ memcpy(buffer, ((u8 *)data) + offset, count);
+
+ return count;
+}
+
+/**
+ * dev_coredumpv - create device coredump with vmalloc data
+ * @dev: the struct device for the crashed device
+ * @data: vmalloc data containing the device coredump
+ * @datalen: length of the data
+ * @gfp: allocation flags
+ *
+ * This function takes ownership of the vmalloc'ed data and will free
+ * it when it is no longer used. See dev_coredumpm() for more information.
+ */
+void dev_coredumpv(struct device *dev, const void *data, size_t datalen,
+ gfp_t gfp)
+{
+ dev_coredumpm(dev, NULL, data, datalen, gfp, devcd_readv, vfree);
+}
+EXPORT_SYMBOL_GPL(dev_coredumpv);
+
+static int devcd_match_failing(struct device *dev, const void *failing)
+{
+ struct devcd_entry *devcd = dev_to_devcd(dev);
+
+ return devcd->failing_dev == failing;
+}
+
+/**
+ * dev_coredumpm - create device coredump with read/free methods
+ * @dev: the struct device for the crashed device
+ * @owner: the module that contains the read/free functions, use %THIS_MODULE
+ * @data: data cookie for the @read/@free functions
+ * @datalen: length of the data
+ * @gfp: allocation flags
+ * @read: function to read from the given buffer
+ * @free: function to free the given buffer
+ *
+ * Creates a new device coredump for the given device. If a previous one hasn't
+ * been read yet, the new coredump is discarded. The data lifetime is determined
+ * by the device coredump framework and when it is no longer needed the @free
+ * function will be called to free the data.
+ */
+void dev_coredumpm(struct device *dev, struct module *owner,
+ const void *data, size_t datalen, gfp_t gfp,
+ ssize_t (*read)(char *buffer, loff_t offset, size_t count,
+ const void *data, size_t datalen),
+ void (*free)(const void *data))
+{
+ static atomic_t devcd_count = ATOMIC_INIT(0);
+ struct devcd_entry *devcd;
+ struct device *existing;
+
+ if (devcd_disabled)
+ goto free;
+
+ existing = class_find_device(&devcd_class, NULL, dev,
+ devcd_match_failing);
+ if (existing) {
+ put_device(existing);
+ goto free;
+ }
+
+ if (!try_module_get(owner))
+ goto free;
+
+ devcd = kzalloc(sizeof(*devcd), gfp);
+ if (!devcd)
+ goto put_module;
+
+ devcd->owner = owner;
+ devcd->data = data;
+ devcd->datalen = datalen;
+ devcd->read = read;
+ devcd->free = free;
+ devcd->failing_dev = get_device(dev);
+
+ device_initialize(&devcd->devcd_dev);
+
+ dev_set_name(&devcd->devcd_dev, "devcd%d",
+ atomic_inc_return(&devcd_count));
+ devcd->devcd_dev.class = &devcd_class;
+
+ if (device_add(&devcd->devcd_dev))
+ goto put_device;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0)
+ if (device_create_bin_file(&devcd->devcd_dev, &devcd_attr_data))
+ goto put_device;
+#endif
+
+ if (sysfs_create_link(&devcd->devcd_dev.kobj, &dev->kobj,
+ "failing_device"))
+ /* nothing - symlink will be missing */;
+
+ if (sysfs_create_link(&dev->kobj, &devcd->devcd_dev.kobj,
+ "devcoredump"))
+ /* nothing - symlink will be missing */;
+
+ INIT_DELAYED_WORK(&devcd->del_wk, devcd_del);
+ schedule_delayed_work(&devcd->del_wk, DEVCD_TIMEOUT);
+
+ return;
+ put_device:
+ put_device(&devcd->devcd_dev);
+ put_module:
+ module_put(owner);
+ free:
+ free(data);
+}
+EXPORT_SYMBOL_GPL(dev_coredumpm);
+
+int __init devcoredump_init(void)
+{
+ return class_register(&devcd_class);
+}
+
+void __exit devcoredump_exit(void)
+{
+ class_for_each_device(&devcd_class, NULL, NULL, devcd_free);
+ class_unregister(&devcd_class);
+}
diff --git a/compat/main.c b/compat/main.c
index d3f8944..5d45e3d 100644
--- a/compat/main.c
+++ b/compat/main.c
@@ -8,40 +8,40 @@
MODULE_DESCRIPTION("Kernel backport module");
MODULE_LICENSE("GPL");
-#ifndef BACKPORTED_KERNEL_NAME
-#error "You need a BACKPORTED_KERNEL_NAME"
+#ifndef CPTCFG_KERNEL_NAME
+#error "You need a CPTCFG_KERNEL_NAME"
#endif
-#ifndef BACKPORTED_KERNEL_VERSION
-#error "You need a BACKPORTED_KERNEL_VERSION"
+#ifndef CPTCFG_KERNEL_VERSION
+#error "You need a CPTCFG_KERNEL_VERSION"
#endif
-#ifndef BACKPORTS_VERSION
-#error "You need a BACKPORTS_VERSION"
+#ifndef CPTCFG_VERSION
+#error "You need a CPTCFG_VERSION"
#endif
-static char *backported_kernel_name = BACKPORTED_KERNEL_NAME;
+static char *backported_kernel_name = CPTCFG_KERNEL_NAME;
module_param(backported_kernel_name, charp, 0400);
MODULE_PARM_DESC(backported_kernel_name,
- "The kernel tree name that was used for this backport (" BACKPORTED_KERNEL_NAME ")");
+ "The kernel tree name that was used for this backport (" CPTCFG_KERNEL_NAME ")");
-#ifdef BACKPORTS_GIT_TRACKED
+#ifdef BACKPORTS_GIT_TRACKED
static char *backports_tracker_id = BACKPORTS_GIT_TRACKED;
module_param(backports_tracker_id, charp, 0400);
MODULE_PARM_DESC(backports_tracker_id,
"The version of the tree containing this backport (" BACKPORTS_GIT_TRACKED ")");
#else
-static char *backported_kernel_version = BACKPORTED_KERNEL_VERSION;
-static char *backports_version = BACKPORTS_VERSION;
+static char *backported_kernel_version = CPTCFG_KERNEL_VERSION;
+static char *backports_version = CPTCFG_VERSION;
module_param(backported_kernel_version, charp, 0400);
MODULE_PARM_DESC(backported_kernel_version,
- "The kernel version that was used for this backport (" BACKPORTED_KERNEL_VERSION ")");
+ "The kernel version that was used for this backport (" CPTCFG_KERNEL_VERSION ")");
module_param(backports_version, charp, 0400);
MODULE_PARM_DESC(backports_version,
- "The git version of the backports tree used to generate this backport (" BACKPORTS_VERSION ")");
+ "The git version of the backports tree used to generate this backport (" CPTCFG_VERSION ")");
#endif
@@ -63,16 +63,22 @@
return ret;
}
- printk(KERN_INFO "Loading modules backported from " BACKPORTED_KERNEL_NAME
+ printk(KERN_INFO "Loading modules backported from " CPTCFG_KERNEL_NAME
#ifndef BACKPORTS_GIT_TRACKED
- " version " BACKPORTED_KERNEL_VERSION
+ " version " CPTCFG_KERNEL_VERSION
#endif
"\n");
#ifdef BACKPORTS_GIT_TRACKED
printk(KERN_INFO BACKPORTS_GIT_TRACKED "\n");
#else
- printk(KERN_INFO "Backport generated by backports.git " BACKPORTS_VERSION "\n");
-#endif
+
+#ifdef CONFIG_BACKPORT_INTEGRATE
+ printk(KERN_INFO "Backport integrated by backports.git " CPTCFG_VERSION "\n");
+#else
+ printk(KERN_INFO "Backport generated by backports.git " CPTCFG_VERSION "\n");
+#endif /* CONFIG_BACKPORT_INTEGRATE */
+
+#endif /* BACKPORTS_GIT_TRACKED */
return 0;
}
diff --git a/defconfigs/ieee802154 b/defconfigs/ieee802154
deleted file mode 100644
index 2c6730a..0000000
--- a/defconfigs/ieee802154
+++ /dev/null
@@ -1,8 +0,0 @@
-CPTCFG_IEEE802154_DRIVERS=m
-CPTCFG_IEEE802154_FAKEHARD=m
-CPTCFG_IEEE802154_FAKELB=m
-CPTCFG_IEEE802154_AT86RF230=m
-CPTCFG_IEEE802154_MRF24J40=m
-CPTCFG_IEEE802154=m
-CPTCFG_IEEE802154_6LOWPAN=m
-CPTCFG_MAC802154=m
diff --git a/defconfigs/media b/defconfigs/media
deleted file mode 100644
index 7b1bfa9..0000000
--- a/defconfigs/media
+++ /dev/null
@@ -1,513 +0,0 @@
-CPTCFG_DVB_A8293=m
-CPTCFG_DVB_AF9013=m
-CPTCFG_DVB_AF9033=m
-CPTCFG_DVB_AS102=m
-CPTCFG_DVB_AS102_FE=m
-CPTCFG_DVB_ATBM8830=m
-CPTCFG_DVB_AU8522=m
-CPTCFG_DVB_AU8522_DTV=m
-CPTCFG_DVB_AU8522_V4L=m
-CPTCFG_DVB_AV7110=m
-CPTCFG_DVB_B2C2_FLEXCOP=m
-CPTCFG_DVB_B2C2_FLEXCOP_PCI=m
-CPTCFG_DVB_B2C2_FLEXCOP_USB=m
-CPTCFG_DVB_BCM3510=m
-CPTCFG_DVB_BT8XX=m
-CPTCFG_DVB_BUDGET=m
-CPTCFG_DVB_BUDGET_AV=m
-CPTCFG_DVB_BUDGET_CI=m
-CPTCFG_DVB_BUDGET_CORE=m
-CPTCFG_DVB_BUDGET_PATCH=m
-CPTCFG_DVB_CORE=m
-CPTCFG_DVB_CX22700=m
-CPTCFG_DVB_CX22702=m
-CPTCFG_DVB_CX24110=m
-CPTCFG_DVB_CX24116=m
-CPTCFG_DVB_CX24123=m
-CPTCFG_DVB_CXD2820R=m
-CPTCFG_DVB_DDBRIDGE=m
-CPTCFG_DVB_DIB3000MB=m
-CPTCFG_DVB_DIB3000MC=m
-CPTCFG_DVB_DIB7000M=m
-CPTCFG_DVB_DIB7000P=m
-CPTCFG_DVB_DIB8000=m
-CPTCFG_DVB_DIB9000=m
-CPTCFG_DVB_DM1105=m
-CPTCFG_DVB_DRXD=m
-CPTCFG_DVB_DRXK=m
-CPTCFG_DVB_DS3000=m
-CPTCFG_DVB_DUMMY_FE=m
-CPTCFG_DVB_EC100=m
-CPTCFG_DVB_FIREDTV=m
-CPTCFG_DVB_FIREDTV_INPUT=y
-CPTCFG_DVB_HD29L2=m
-CPTCFG_DVB_HOPPER=m
-CPTCFG_DVB_ISL6405=m
-CPTCFG_DVB_ISL6421=m
-CPTCFG_DVB_ISL6423=m
-CPTCFG_DVB_IX2505V=m
-CPTCFG_DVB_L64781=m
-CPTCFG_DVB_LG2160=m
-CPTCFG_DVB_LGDT3305=m
-CPTCFG_DVB_LGDT330X=m
-CPTCFG_DVB_LGS8GL5=m
-CPTCFG_DVB_LGS8GXX=m
-CPTCFG_DVB_LNBP21=m
-CPTCFG_DVB_LNBP22=m
-CPTCFG_DVB_M88RS2000=m
-CPTCFG_DVB_MANTIS=m
-CPTCFG_DVB_MB86A16=m
-CPTCFG_DVB_MB86A20S=m
-CPTCFG_DVB_MT312=m
-CPTCFG_DVB_MT352=m
-CPTCFG_DVB_NGENE=m
-CPTCFG_DVB_NXT200X=m
-CPTCFG_DVB_NXT6000=m
-CPTCFG_DVB_OR51132=m
-CPTCFG_DVB_OR51211=m
-CPTCFG_DVB_PLL=m
-CPTCFG_DVB_PLUTO2=m
-CPTCFG_DVB_PT1=m
-CPTCFG_DVB_PT3=m
-CPTCFG_DVB_RTL2830=m
-CPTCFG_DVB_RTL2832=m
-CPTCFG_DVB_S5H1409=m
-CPTCFG_DVB_S5H1411=m
-CPTCFG_DVB_S5H1420=m
-CPTCFG_DVB_S5H1432=m
-CPTCFG_DVB_S921=m
-CPTCFG_DVB_SI21XX=m
-CPTCFG_DVB_SP8870=m
-CPTCFG_DVB_SP887X=m
-CPTCFG_DVB_STB0899=m
-CPTCFG_DVB_STB6000=m
-CPTCFG_DVB_STB6100=m
-CPTCFG_DVB_STV0288=m
-CPTCFG_DVB_STV0297=m
-CPTCFG_DVB_STV0299=m
-CPTCFG_DVB_STV0367=m
-CPTCFG_DVB_STV0900=m
-CPTCFG_DVB_STV090x=m
-CPTCFG_DVB_STV6110=m
-CPTCFG_DVB_STV6110x=m
-CPTCFG_DVB_TDA10021=m
-CPTCFG_DVB_TDA10023=m
-CPTCFG_DVB_TDA10048=m
-CPTCFG_DVB_TDA1004X=m
-CPTCFG_DVB_TDA10071=m
-CPTCFG_DVB_TDA10086=m
-CPTCFG_DVB_TDA18271C2DD=m
-CPTCFG_DVB_TDA665x=m
-CPTCFG_DVB_TDA8083=m
-CPTCFG_DVB_TDA8261=m
-CPTCFG_DVB_TDA826X=m
-CPTCFG_DVB_TS2020=m
-CPTCFG_DVB_TTUSB_BUDGET=m
-CPTCFG_DVB_TTUSB_DEC=m
-CPTCFG_DVB_TUA6100=m
-CPTCFG_DVB_TUNER_CX24113=m
-CPTCFG_DVB_TUNER_DIB0070=m
-CPTCFG_DVB_TUNER_DIB0090=m
-CPTCFG_DVB_TUNER_ITD1000=m
-CPTCFG_DVB_USB=m
-CPTCFG_DVB_USB_A800=m
-CPTCFG_DVB_USB_AF9005=m
-CPTCFG_DVB_USB_AF9005_REMOTE=m
-CPTCFG_DVB_USB_AF9015=m
-CPTCFG_DVB_USB_AF9035=m
-CPTCFG_DVB_USB_ANYSEE=m
-CPTCFG_DVB_USB_AU6610=m
-CPTCFG_DVB_USB_AZ6007=m
-CPTCFG_DVB_USB_AZ6027=m
-CPTCFG_DVB_USB_CE6230=m
-CPTCFG_DVB_USB_CINERGY_T2=m
-CPTCFG_DVB_USB_CXUSB=m
-CPTCFG_DVB_USB_CYPRESS_FIRMWARE=m
-CPTCFG_DVB_USB_DIB0700=m
-CPTCFG_DVB_USB_DIBUSB_MB=m
-CPTCFG_DVB_USB_DIBUSB_MC=m
-CPTCFG_DVB_USB_DIGITV=m
-CPTCFG_DVB_USB_DTT200U=m
-CPTCFG_DVB_USB_DTV5100=m
-CPTCFG_DVB_USB_DVBSKY=m
-CPTCFG_DVB_USB_DW2102=m
-CPTCFG_DVB_USB_EC168=m
-CPTCFG_DVB_USB_FRIIO=m
-CPTCFG_DVB_USB_GL861=m
-CPTCFG_DVB_USB_GP8PSK=m
-CPTCFG_DVB_USB_LME2510=m
-CPTCFG_DVB_USB_M920X=m
-CPTCFG_DVB_USB_MXL111SF=m
-CPTCFG_DVB_USB_NOVA_T_USB2=m
-CPTCFG_DVB_USB_OPERA1=m
-CPTCFG_DVB_USB_PCTV452E=m
-CPTCFG_DVB_USB_RTL28XXU=m
-CPTCFG_DVB_USB_TECHNISAT_USB2=m
-CPTCFG_DVB_USB_TTUSB2=m
-CPTCFG_DVB_USB_UMT_010=m
-CPTCFG_DVB_USB_V2=m
-CPTCFG_DVB_USB_VP702X=m
-CPTCFG_DVB_USB_VP7045=m
-CPTCFG_DVB_VES1820=m
-CPTCFG_DVB_VES1X93=m
-CPTCFG_DVB_ZL10036=m
-CPTCFG_DVB_ZL10039=m
-CPTCFG_DVB_ZL10353=m
-CPTCFG_I2C_SI470X=m
-CPTCFG_I2C_SI4713=m
-CPTCFG_IR_ENE=m
-CPTCFG_IR_FINTEK=m
-CPTCFG_IR_GPIO_CIR=m
-CPTCFG_IR_IGUANA=m
-CPTCFG_IR_IMG=m
-CPTCFG_IR_IMG_HW=y
-CPTCFG_IR_IMG_JVC=y
-CPTCFG_IR_IMG_NEC=y
-CPTCFG_IR_IMG_RAW=y
-CPTCFG_IR_IMG_SANYO=y
-CPTCFG_IR_IMG_SHARP=y
-CPTCFG_IR_IMG_SONY=y
-CPTCFG_IR_IMON=m
-CPTCFG_IR_ITE_CIR=m
-CPTCFG_IR_JVC_DECODER=m
-CPTCFG_IR_LIRC_CODEC=m
-CPTCFG_IR_MCEUSB=m
-CPTCFG_IR_MCE_KBD_DECODER=m
-CPTCFG_IR_NEC_DECODER=m
-CPTCFG_IR_NUVOTON=m
-CPTCFG_IR_RC5_DECODER=m
-CPTCFG_IR_RC6_DECODER=m
-CPTCFG_IR_REDRAT3=m
-CPTCFG_IR_RX51=m
-CPTCFG_IR_SANYO_DECODER=m
-CPTCFG_IR_SONY_DECODER=m
-CPTCFG_IR_STREAMZAP=m
-CPTCFG_IR_TTUSBIR=m
-CPTCFG_IR_WINBOND_CIR=m
-CPTCFG_LIRC=m
-CPTCFG_MANTIS_CORE=m
-CPTCFG_MEDIA_ALTERA_CI=m
-CPTCFG_MEDIA_ANALOG_TV_SUPPORT=y
-CPTCFG_MEDIA_CAMERA_SUPPORT=y
-CPTCFG_MEDIA_CONTROLLER=y
-CPTCFG_MEDIA_DIGITAL_TV_SUPPORT=y
-CPTCFG_MEDIA_PARPORT_SUPPORT=y
-CPTCFG_MEDIA_PCI_SUPPORT=y
-CPTCFG_MEDIA_RADIO_SUPPORT=y
-CPTCFG_MEDIA_RC_SUPPORT=y
-CPTCFG_MEDIA_SDR_SUPPORT=y
-CPTCFG_MEDIA_SUPPORT=m
-CPTCFG_MEDIA_USB_SUPPORT=y
-CPTCFG_PLATFORM_SI4713=m
-CPTCFG_RADIO_AZTECH=m
-CPTCFG_RADIO_CADET=m
-CPTCFG_RADIO_GEMTEK=m
-CPTCFG_RADIO_ISA=m
-CPTCFG_RADIO_MAXIRADIO=m
-CPTCFG_RADIO_MIROPCM20=m
-CPTCFG_RADIO_RTRACK2=m
-CPTCFG_RADIO_RTRACK=m
-CPTCFG_RADIO_SAA7706H=m
-CPTCFG_RADIO_SF16FMI=m
-CPTCFG_RADIO_SF16FMR2=m
-CPTCFG_RADIO_SHARK2=m
-CPTCFG_RADIO_SHARK=m
-CPTCFG_RADIO_SI470X=y
-CPTCFG_RADIO_SI4713=m
-CPTCFG_RADIO_SI476X=m
-CPTCFG_RADIO_TEA5764=m
-CPTCFG_RADIO_TEF6862=m
-CPTCFG_RADIO_TERRATEC=m
-CPTCFG_RADIO_TIMBERDALE=m
-CPTCFG_RADIO_TRUST=m
-CPTCFG_RADIO_TYPHOON=m
-CPTCFG_RADIO_WL1273=m
-CPTCFG_RADIO_WL128X=m
-CPTCFG_RADIO_ZOLTRIX=m
-CPTCFG_RC_ATI_REMOTE=m
-CPTCFG_RC_CORE=m
-CPTCFG_RC_DEVICES=y
-CPTCFG_RC_LOOPBACK=m
-CPTCFG_RC_MAP=m
-CPTCFG_SMS_SDIO_DRV=m
-CPTCFG_SMS_SIANO_MDTV=m
-CPTCFG_SMS_USB_DRV=m
-CPTCFG_SOC_CAMERA=m
-CPTCFG_SOC_CAMERA_IMX074=m
-CPTCFG_SOC_CAMERA_MT9M001=m
-CPTCFG_SOC_CAMERA_MT9M111=m
-CPTCFG_SOC_CAMERA_MT9T031=m
-CPTCFG_SOC_CAMERA_MT9T112=m
-CPTCFG_SOC_CAMERA_MT9V022=m
-CPTCFG_SOC_CAMERA_OV2640=m
-CPTCFG_SOC_CAMERA_OV5642=m
-CPTCFG_SOC_CAMERA_OV6650=m
-CPTCFG_SOC_CAMERA_OV772X=m
-CPTCFG_SOC_CAMERA_OV9640=m
-CPTCFG_SOC_CAMERA_OV9740=m
-CPTCFG_SOC_CAMERA_PLATFORM=m
-CPTCFG_SOC_CAMERA_RJ54N1=m
-CPTCFG_SOC_CAMERA_TW9910=m
-CPTCFG_STA2X11_VIP=m
-CPTCFG_TTPCI_EEPROM=m
-CPTCFG_USB_AIRSPY=m
-CPTCFG_USB_DSBR=m
-CPTCFG_USB_GL860=m
-CPTCFG_USB_GSPCA=m
-CPTCFG_USB_GSPCA_BENQ=m
-CPTCFG_USB_GSPCA_CONEX=m
-CPTCFG_USB_GSPCA_CPIA1=m
-CPTCFG_USB_GSPCA_DTCS033=m
-CPTCFG_USB_GSPCA_ETOMS=m
-CPTCFG_USB_GSPCA_FINEPIX=m
-CPTCFG_USB_GSPCA_JEILINJ=m
-CPTCFG_USB_GSPCA_JL2005BCD=m
-CPTCFG_USB_GSPCA_KINECT=m
-CPTCFG_USB_GSPCA_KONICA=m
-CPTCFG_USB_GSPCA_MARS=m
-CPTCFG_USB_GSPCA_MR97310A=m
-CPTCFG_USB_GSPCA_NW80X=m
-CPTCFG_USB_GSPCA_OV519=m
-CPTCFG_USB_GSPCA_OV534=m
-CPTCFG_USB_GSPCA_OV534_9=m
-CPTCFG_USB_GSPCA_PAC207=m
-CPTCFG_USB_GSPCA_PAC7302=m
-CPTCFG_USB_GSPCA_PAC7311=m
-CPTCFG_USB_GSPCA_SE401=m
-CPTCFG_USB_GSPCA_SN9C2028=m
-CPTCFG_USB_GSPCA_SN9C20X=m
-CPTCFG_USB_GSPCA_SONIXB=m
-CPTCFG_USB_GSPCA_SONIXJ=m
-CPTCFG_USB_GSPCA_SPCA1528=m
-CPTCFG_USB_GSPCA_SPCA500=m
-CPTCFG_USB_GSPCA_SPCA501=m
-CPTCFG_USB_GSPCA_SPCA505=m
-CPTCFG_USB_GSPCA_SPCA506=m
-CPTCFG_USB_GSPCA_SPCA508=m
-CPTCFG_USB_GSPCA_SPCA561=m
-CPTCFG_USB_GSPCA_SQ905=m
-CPTCFG_USB_GSPCA_SQ905C=m
-CPTCFG_USB_GSPCA_SQ930X=m
-CPTCFG_USB_GSPCA_STK014=m
-CPTCFG_USB_GSPCA_STK1135=m
-CPTCFG_USB_GSPCA_STV0680=m
-CPTCFG_USB_GSPCA_SUNPLUS=m
-CPTCFG_USB_GSPCA_T613=m
-CPTCFG_USB_GSPCA_TOPRO=m
-CPTCFG_USB_GSPCA_TV8532=m
-CPTCFG_USB_GSPCA_VC032X=m
-CPTCFG_USB_GSPCA_VICAM=m
-CPTCFG_USB_GSPCA_XIRLINK_CIT=m
-CPTCFG_USB_GSPCA_ZC3XX=m
-CPTCFG_USB_HACKRF=m
-CPTCFG_USB_KEENE=m
-CPTCFG_USB_M5602=m
-CPTCFG_USB_MA901=m
-CPTCFG_USB_MR800=m
-CPTCFG_USB_MSI2500=m
-CPTCFG_USB_PWC=m
-CPTCFG_USB_RAREMONO=m
-CPTCFG_USB_S2255=m
-CPTCFG_USB_SI470X=m
-CPTCFG_USB_SI4713=m
-CPTCFG_USB_STKWEBCAM=m
-CPTCFG_USB_STV06XX=m
-CPTCFG_USB_VIDEO_CLASS=m
-CPTCFG_USB_ZR364XX=m
-CPTCFG_V4L2_MEM2MEM_DEV=m
-CPTCFG_V4L_MEM2MEM_DRIVERS=y
-CPTCFG_V4L_PLATFORM_DRIVERS=y
-CPTCFG_VIDEOBUF2_CORE=m
-CPTCFG_VIDEOBUF2_DMA_CONTIG=m
-CPTCFG_VIDEOBUF2_DMA_SG=m
-CPTCFG_VIDEOBUF2_MEMOPS=m
-CPTCFG_VIDEOBUF2_VMALLOC=m
-CPTCFG_VIDEOBUF_DMA_CONTIG=m
-CPTCFG_VIDEOBUF_DMA_SG=m
-CPTCFG_VIDEOBUF_DVB=m
-CPTCFG_VIDEOBUF_GEN=m
-CPTCFG_VIDEOBUF_VMALLOC=m
-CPTCFG_VIDEO_AD9389B=m
-CPTCFG_VIDEO_ADP1653=m
-CPTCFG_VIDEO_ADV7170=m
-CPTCFG_VIDEO_ADV7175=m
-CPTCFG_VIDEO_ADV7180=m
-CPTCFG_VIDEO_ADV7183=m
-CPTCFG_VIDEO_ADV7343=m
-CPTCFG_VIDEO_ADV7393=m
-CPTCFG_VIDEO_ADV7604=m
-CPTCFG_VIDEO_AK881X=m
-CPTCFG_VIDEO_APTINA_PLL=m
-CPTCFG_VIDEO_AS3645A=m
-CPTCFG_VIDEO_ATMEL_ISI=m
-CPTCFG_VIDEO_AU0828=m
-CPTCFG_VIDEO_AU0828_RC=y
-CPTCFG_VIDEO_BLACKFIN_CAPTURE=m
-CPTCFG_VIDEO_BLACKFIN_PPI=m
-CPTCFG_VIDEO_BT819=m
-CPTCFG_VIDEO_BT848=m
-CPTCFG_VIDEO_BT856=m
-CPTCFG_VIDEO_BT866=m
-CPTCFG_VIDEO_BTCX=m
-CPTCFG_VIDEO_BWQCAM=m
-CPTCFG_VIDEO_CAFE_CCIC=m
-CPTCFG_VIDEO_CODA=m
-CPTCFG_VIDEO_CPIA2=m
-CPTCFG_VIDEO_CQCAM=m
-CPTCFG_VIDEO_CS5345=m
-CPTCFG_VIDEO_CS53L32A=m
-CPTCFG_VIDEO_CX18=m
-CPTCFG_VIDEO_CX18_ALSA=m
-CPTCFG_VIDEO_CX231XX=m
-CPTCFG_VIDEO_CX231XX_ALSA=m
-CPTCFG_VIDEO_CX231XX_DVB=m
-CPTCFG_VIDEO_CX231XX_RC=y
-CPTCFG_VIDEO_CX2341X=m
-CPTCFG_VIDEO_CX23885=m
-CPTCFG_VIDEO_CX25821=m
-CPTCFG_VIDEO_CX25821_ALSA=m
-CPTCFG_VIDEO_CX25840=m
-CPTCFG_VIDEO_CX88=m
-CPTCFG_VIDEO_CX88_ALSA=m
-CPTCFG_VIDEO_CX88_BLACKBIRD=m
-CPTCFG_VIDEO_CX88_DVB=m
-CPTCFG_VIDEO_CX88_MPEG=m
-CPTCFG_VIDEO_CX88_VP3054=m
-CPTCFG_VIDEO_DAVINCI_VPBE_DISPLAY=m
-CPTCFG_VIDEO_DAVINCI_VPIF_CAPTURE=m
-CPTCFG_VIDEO_DAVINCI_VPIF_DISPLAY=m
-CPTCFG_VIDEO_DEV=m
-CPTCFG_VIDEO_DM355_CCDC=m
-CPTCFG_VIDEO_DM6446_CCDC=m
-CPTCFG_VIDEO_EM28XX=m
-CPTCFG_VIDEO_EM28XX_ALSA=m
-CPTCFG_VIDEO_EM28XX_DVB=m
-CPTCFG_VIDEO_EM28XX_RC=m
-CPTCFG_VIDEO_EM28XX_V4L2=m
-CPTCFG_VIDEO_EXYNOS_FIMC_LITE=m
-CPTCFG_VIDEO_FB_IVTV=m
-CPTCFG_VIDEO_GO7007=m
-CPTCFG_VIDEO_GO7007_USB=m
-CPTCFG_VIDEO_GO7007_USB_S2250_BOARD=m
-CPTCFG_VIDEO_HDPVR=m
-CPTCFG_VIDEO_HEXIUM_GEMINI=m
-CPTCFG_VIDEO_HEXIUM_ORION=m
-CPTCFG_VIDEO_IR_I2C=m
-CPTCFG_VIDEO_IVTV=m
-CPTCFG_VIDEO_IVTV_ALSA=m
-CPTCFG_VIDEO_KS0127=m
-CPTCFG_VIDEO_M32R_AR_M64278=m
-CPTCFG_VIDEO_M52790=m
-CPTCFG_VIDEO_M5MOLS=m
-CPTCFG_VIDEO_MEM2MEM_DEINTERLACE=m
-CPTCFG_VIDEO_MEM2MEM_TESTDEV=m
-CPTCFG_VIDEO_MEYE=m
-CPTCFG_VIDEO_MMP_CAMERA=m
-CPTCFG_VIDEO_MSP3400=m
-CPTCFG_VIDEO_MT9M032=m
-CPTCFG_VIDEO_MT9P031=m
-CPTCFG_VIDEO_MT9T001=m
-CPTCFG_VIDEO_MT9V011=m
-CPTCFG_VIDEO_MT9V032=m
-CPTCFG_VIDEO_MX2=m
-CPTCFG_VIDEO_MX2_EMMAPRP=m
-CPTCFG_VIDEO_MX3=m
-CPTCFG_VIDEO_MXB=m
-CPTCFG_VIDEO_NOON010PC30=m
-CPTCFG_VIDEO_OMAP1=m
-CPTCFG_VIDEO_OMAP2_VOUT=m
-CPTCFG_VIDEO_OMAP3=m
-CPTCFG_VIDEO_OV7640=m
-CPTCFG_VIDEO_OV7670=m
-CPTCFG_VIDEO_OV9650=m
-CPTCFG_VIDEO_PMS=m
-CPTCFG_VIDEO_PVRUSB2=m
-CPTCFG_VIDEO_PXA27x=m
-CPTCFG_VIDEO_S3C_CAMIF=m
-CPTCFG_VIDEO_S5C73M3=m
-CPTCFG_VIDEO_S5K4ECGX=m
-CPTCFG_VIDEO_S5K6AA=m
-CPTCFG_VIDEO_S5P_FIMC=m
-CPTCFG_VIDEO_S5P_MIPI_CSIS=m
-CPTCFG_VIDEO_SAA6588=m
-CPTCFG_VIDEO_SAA7110=m
-CPTCFG_VIDEO_SAA711X=m
-CPTCFG_VIDEO_SAA7127=m
-CPTCFG_VIDEO_SAA7134=m
-CPTCFG_VIDEO_SAA7134_ALSA=m
-CPTCFG_VIDEO_SAA7134_DVB=m
-CPTCFG_VIDEO_SAA7134_GO7007=m
-CPTCFG_VIDEO_SAA7134_RC=y
-CPTCFG_VIDEO_SAA7146=m
-CPTCFG_VIDEO_SAA7146_VV=m
-CPTCFG_VIDEO_SAA7164=m
-CPTCFG_VIDEO_SAA717X=m
-CPTCFG_VIDEO_SAA7185=m
-CPTCFG_VIDEO_SAA7191=m
-CPTCFG_VIDEO_SAMSUNG_EXYNOS_GSC=m
-CPTCFG_VIDEO_SAMSUNG_S5P_G2D=m
-CPTCFG_VIDEO_SAMSUNG_S5P_HDMI=m
-CPTCFG_VIDEO_SAMSUNG_S5P_HDMIPHY=m
-CPTCFG_VIDEO_SAMSUNG_S5P_JPEG=m
-CPTCFG_VIDEO_SAMSUNG_S5P_MFC=m
-CPTCFG_VIDEO_SAMSUNG_S5P_MIXER=m
-CPTCFG_VIDEO_SAMSUNG_S5P_SDO=m
-CPTCFG_VIDEO_SAMSUNG_S5P_SII9234=m
-CPTCFG_VIDEO_SAMSUNG_S5P_TV=y
-CPTCFG_VIDEO_SH_MOBILE_CEU=m
-CPTCFG_VIDEO_SH_MOBILE_CSI2=m
-CPTCFG_VIDEO_SH_VEU=m
-CPTCFG_VIDEO_SH_VOU=m
-CPTCFG_VIDEO_SMIAPP=m
-CPTCFG_VIDEO_SMIAPP_PLL=m
-CPTCFG_VIDEO_SOLO6X10=m
-CPTCFG_VIDEO_SONY_BTF_MPX=m
-CPTCFG_VIDEO_SR030PC30=m
-CPTCFG_VIDEO_STK1160=m
-CPTCFG_VIDEO_STK1160_AC97=y
-CPTCFG_VIDEO_STK1160_COMMON=m
-CPTCFG_VIDEO_TDA7432=m
-CPTCFG_VIDEO_TDA9840=m
-CPTCFG_VIDEO_TEA6415C=m
-CPTCFG_VIDEO_TEA6420=m
-CPTCFG_VIDEO_THS7303=m
-CPTCFG_VIDEO_TIMBERDALE=m
-CPTCFG_VIDEO_TLG2300=m
-CPTCFG_VIDEO_TLV320AIC23B=m
-CPTCFG_VIDEO_TM6000=m
-CPTCFG_VIDEO_TM6000_ALSA=m
-CPTCFG_VIDEO_TM6000_DVB=m
-CPTCFG_VIDEO_TUNER=m
-CPTCFG_VIDEO_TVAUDIO=m
-CPTCFG_VIDEO_TVEEPROM=m
-CPTCFG_VIDEO_TVP514X=m
-CPTCFG_VIDEO_TVP5150=m
-CPTCFG_VIDEO_TVP7002=m
-CPTCFG_VIDEO_TW2804=m
-CPTCFG_VIDEO_TW68=m
-CPTCFG_VIDEO_TW9903=m
-CPTCFG_VIDEO_TW9906=m
-CPTCFG_VIDEO_UDA1342=m
-CPTCFG_VIDEO_UPD64031A=m
-CPTCFG_VIDEO_UPD64083=m
-CPTCFG_VIDEO_USBTV=m
-CPTCFG_VIDEO_USBVISION=m
-CPTCFG_VIDEO_V4L2=m
-CPTCFG_VIDEO_V4L2_SUBDEV_API=y
-CPTCFG_VIDEO_VIA_CAMERA=m
-CPTCFG_VIDEO_VINO=m
-CPTCFG_VIDEO_VIU=m
-CPTCFG_VIDEO_VP27SMPX=m
-CPTCFG_VIDEO_VPX3220=m
-CPTCFG_VIDEO_VS6624=m
-CPTCFG_VIDEO_W9966=m
-CPTCFG_VIDEO_WM8739=m
-CPTCFG_VIDEO_WM8775=m
-CPTCFG_VIDEO_ZORAN=m
-CPTCFG_VIDEO_ZORAN_AVS6EYES=m
-CPTCFG_VIDEO_ZORAN_BUZ=m
-CPTCFG_VIDEO_ZORAN_DC10=m
-CPTCFG_VIDEO_ZORAN_DC30=m
-CPTCFG_VIDEO_ZORAN_LML33=m
-CPTCFG_VIDEO_ZORAN_LML33R10=m
-CPTCFG_VIDEO_ZORAN_ZR36060=m
diff --git a/drivers/bcma/Kconfig b/drivers/bcma/Kconfig
index c5180b8..b9686bb 100644
--- a/drivers/bcma/Kconfig
+++ b/drivers/bcma/Kconfig
@@ -36,7 +36,7 @@
PCI core hostmode operation (external PCI bus).
config BCMA_HOST_SOC
- depends on !BACKPORT_KERNEL_3_7
+ depends on !KERNEL_3_7
bool "Support for BCMA in a SoC"
depends on BCMA
help
@@ -47,7 +47,7 @@
If unsure, say N
config BCMA_DRIVER_MIPS
- depends on !BACKPORT_KERNEL_3_3
+ depends on !KERNEL_3_3
bool "BCMA Broadcom MIPS core driver"
depends on BCMA && MIPS
help
diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h
index 16fc849..cb91beb 100644
--- a/drivers/bcma/bcma_private.h
+++ b/drivers/bcma/bcma_private.h
@@ -24,6 +24,7 @@
/* main.c */
bool bcma_wait_value(struct bcma_device *core, u16 reg, u32 mask, u32 value,
int timeout);
+void bcma_prepare_core(struct bcma_bus *bus, struct bcma_device *core);
int bcma_bus_register(struct bcma_bus *bus);
void bcma_bus_unregister(struct bcma_bus *bus);
int __init bcma_bus_early_register(struct bcma_bus *bus,
diff --git a/drivers/bcma/driver_chipcommon.c b/drivers/bcma/driver_chipcommon.c
index 9ff6cdf..368c24c 100644
--- a/drivers/bcma/driver_chipcommon.c
+++ b/drivers/bcma/driver_chipcommon.c
@@ -339,7 +339,7 @@
return;
}
- irq = bcma_core_irq(cc->core);
+ irq = bcma_core_irq(cc->core, 0);
/* Determine the registers of the UARTs */
cc->nr_serial_ports = (cc->capabilities & BCMA_CC_CAP_NRUART);
diff --git a/drivers/bcma/driver_gpio.c b/drivers/bcma/driver_gpio.c
index 706b9ae..598a6cd 100644
--- a/drivers/bcma/driver_gpio.c
+++ b/drivers/bcma/driver_gpio.c
@@ -152,7 +152,7 @@
handle_simple_irq);
}
- hwirq = bcma_core_irq(cc->core);
+ hwirq = bcma_core_irq(cc->core, 0);
err = request_irq(hwirq, bcma_gpio_irq_handler, IRQF_SHARED, "gpio",
cc);
if (err)
@@ -183,7 +183,7 @@
return;
bcma_cc_mask32(cc, BCMA_CC_IRQMASK, ~BCMA_CC_IRQ_GPIO);
- free_irq(bcma_core_irq(cc->core), cc);
+ free_irq(bcma_core_irq(cc->core, 0), cc);
for (gpio = 0; gpio < chip->ngpio; gpio++) {
int irq = irq_find_mapping(cc->irq_domain, gpio);
diff --git a/drivers/bcma/driver_mips.c b/drivers/bcma/driver_mips.c
index 004d6aa..04faf6d 100644
--- a/drivers/bcma/driver_mips.c
+++ b/drivers/bcma/driver_mips.c
@@ -20,6 +20,9 @@
#include <linux/serial_core.h>
#include <linux/serial_reg.h>
#include <linux/time.h>
+#ifdef CONFIG_BCM47XX
+#include <bcm47xx_nvram.h>
+#endif
enum bcma_boot_dev {
BCMA_BOOT_DEV_UNK = 0,
@@ -115,7 +118,7 @@
* If disabled, 5 is returned.
* If not supported, 6 is returned.
*/
-static unsigned int bcma_core_mips_irq(struct bcma_device *dev)
+unsigned int bcma_core_mips_irq(struct bcma_device *dev)
{
struct bcma_device *mdev = dev->bus->drv_mips.core;
u32 irqflag;
@@ -133,13 +136,6 @@
return 5;
}
-unsigned int bcma_core_irq(struct bcma_device *dev)
-{
- unsigned int mips_irq = bcma_core_mips_irq(dev);
- return mips_irq <= 4 ? mips_irq + 2 : 0;
-}
-EXPORT_SYMBOL(bcma_core_irq);
-
static void bcma_core_mips_set_irq(struct bcma_device *dev, unsigned int irq)
{
unsigned int oldirq = bcma_core_mips_irq(dev);
@@ -323,10 +319,16 @@
switch (boot_dev) {
case BCMA_BOOT_DEV_PARALLEL:
case BCMA_BOOT_DEV_SERIAL:
- /* TODO: Init NVRAM using BCMA_SOC_FLASH2 window */
+#ifdef CONFIG_BCM47XX
+ bcm47xx_nvram_init_from_mem(BCMA_SOC_FLASH2,
+ BCMA_SOC_FLASH2_SZ);
+#endif
break;
case BCMA_BOOT_DEV_NAND:
- /* TODO: Init NVRAM using BCMA_SOC_FLASH1 window */
+#ifdef CONFIG_BCM47XX
+ bcm47xx_nvram_init_from_mem(BCMA_SOC_FLASH1,
+ BCMA_SOC_FLASH1_SZ);
+#endif
break;
default:
break;
@@ -423,7 +425,7 @@
break;
default:
list_for_each_entry(core, &bus->cores, list) {
- core->irq = bcma_core_irq(core);
+ core->irq = bcma_core_irq(core, 0);
}
bcma_err(bus,
"Unknown device (0x%x) found, can not configure IRQs\n",
diff --git a/drivers/bcma/driver_pci_host.c b/drivers/bcma/driver_pci_host.c
index c3d7b03..c8a6b74 100644
--- a/drivers/bcma/driver_pci_host.c
+++ b/drivers/bcma/driver_pci_host.c
@@ -593,7 +593,7 @@
pr_info("PCI: Fixing up device %s\n", pci_name(dev));
/* Fix up interrupt lines */
- dev->irq = bcma_core_irq(pc_host->pdev->core);
+ dev->irq = bcma_core_irq(pc_host->pdev->core, 0);
pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
readrq = pcie_get_readrq(dev);
@@ -617,6 +617,6 @@
pc_host = container_of(dev->bus->ops, struct bcma_drv_pci_host,
pci_ops);
- return bcma_core_irq(pc_host->pdev->core);
+ return bcma_core_irq(pc_host->pdev->core, 0);
}
EXPORT_SYMBOL(bcma_core_pci_pcibios_map_irq);
diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c
index 6a76e68..34b40bb 100644
--- a/drivers/bcma/host_pci.c
+++ b/drivers/bcma/host_pci.c
@@ -275,7 +275,7 @@
static const struct pci_device_id bcma_pci_bridge_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x0576) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4313) },
- { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43224) },
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43224) }, /* 0xa8d8 */
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4331) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4353) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4357) },
@@ -285,7 +285,8 @@
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43a9) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43aa) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4727) },
- { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43227) }, /* 0xA8DB */
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43227) }, /* 0xa8db, BCM43217 (sic!) */
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43228) }, /* 0xa8dc */
{ 0, },
};
MODULE_DEVICE_TABLE(pci, bcma_pci_bridge_tbl);
diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c
index 5379919..17fcf56 100644
--- a/drivers/bcma/main.c
+++ b/drivers/bcma/main.c
@@ -11,6 +11,7 @@
#include <linux/bcma/bcma.h>
#include <linux/slab.h>
#include <linux/of_address.h>
+#include <linux/of_irq.h>
MODULE_DESCRIPTION("Broadcom's specific AMBA driver");
MODULE_LICENSE("GPL");
@@ -141,7 +142,7 @@
return false;
}
-#ifdef CONFIG_OF
+#if defined(CONFIG_OF) && defined(CONFIG_OF_ADDRESS)
static struct device_node *bcma_of_find_child_device(struct platform_device *parent,
struct bcma_device *core)
{
@@ -162,6 +163,46 @@
return NULL;
}
+static int bcma_of_irq_parse(struct platform_device *parent,
+ struct bcma_device *core,
+ struct of_phandle_args *out_irq, int num)
+{
+ __be32 laddr[1];
+ int rc;
+
+ if (core->dev.of_node) {
+ rc = of_irq_parse_one(core->dev.of_node, num, out_irq);
+ if (!rc)
+ return rc;
+ }
+
+ out_irq->np = parent->dev.of_node;
+ out_irq->args_count = 1;
+ out_irq->args[0] = num;
+
+ laddr[0] = cpu_to_be32(core->addr);
+ return of_irq_parse_raw(laddr, out_irq);
+}
+
+static unsigned int bcma_of_get_irq(struct platform_device *parent,
+ struct bcma_device *core, int num)
+{
+ struct of_phandle_args out_irq;
+ int ret;
+
+ if (!parent || !parent->dev.of_node)
+ return 0;
+
+ ret = bcma_of_irq_parse(parent, core, &out_irq, num);
+ if (ret) {
+ bcma_debug(core->bus, "bcma_of_get_irq() failed with rc=%d\n",
+ ret);
+ return 0;
+ }
+
+ return irq_create_of_mapping(&out_irq);
+}
+
static void bcma_of_fill_device(struct platform_device *parent,
struct bcma_device *core)
{
@@ -170,18 +211,47 @@
node = bcma_of_find_child_device(parent, core);
if (node)
core->dev.of_node = node;
+
+ core->irq = bcma_of_get_irq(parent, core, 0);
}
#else
static void bcma_of_fill_device(struct platform_device *parent,
struct bcma_device *core)
{
}
+static inline unsigned int bcma_of_get_irq(struct platform_device *parent,
+ struct bcma_device *core, int num)
+{
+ return 0;
+}
#endif /* CONFIG_OF */
-static void bcma_register_core(struct bcma_bus *bus, struct bcma_device *core)
+unsigned int bcma_core_irq(struct bcma_device *core, int num)
{
- int err;
+ struct bcma_bus *bus = core->bus;
+ unsigned int mips_irq;
+ switch (bus->hosttype) {
+ case BCMA_HOSTTYPE_PCI:
+ return bus->host_pci->irq;
+ case BCMA_HOSTTYPE_SOC:
+ if (bus->drv_mips.core && num == 0) {
+ mips_irq = bcma_core_mips_irq(core);
+ return mips_irq <= 4 ? mips_irq + 2 : 0;
+ }
+ if (bus->host_pdev)
+ return bcma_of_get_irq(bus->host_pdev, core, num);
+ return 0;
+ case BCMA_HOSTTYPE_SDIO:
+ return 0;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(bcma_core_irq);
+
+void bcma_prepare_core(struct bcma_bus *bus, struct bcma_device *core)
+{
core->dev.release = bcma_release_core_dev;
core->dev.bus = &bcma_bus_type;
dev_set_name(&core->dev, "bcma%d:%d", bus->num, core->core_index);
@@ -205,6 +275,11 @@
case BCMA_HOSTTYPE_SDIO:
break;
}
+}
+
+static void bcma_register_core(struct bcma_bus *bus, struct bcma_device *core)
+{
+ int err;
err = device_register(&core->dev);
if (err) {
diff --git a/drivers/bcma/scan.c b/drivers/bcma/scan.c
index 14b5656..9175207 100644
--- a/drivers/bcma/scan.c
+++ b/drivers/bcma/scan.c
@@ -505,6 +505,7 @@
bus->nr_cores++;
other_core = bcma_find_core_reverse(bus, core->id.id);
core->core_unit = (other_core == NULL) ? 0 : other_core->core_unit + 1;
+ bcma_prepare_core(bus, core);
bcma_info(bus, "Core %d found: %s (manuf 0x%03X, id 0x%03X, rev 0x%02X, class 0x%X)\n",
core->core_index, bcma_device_name(&core->id),
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index dcc4d71..c30744e 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -223,6 +223,7 @@
depends on m
depends on BT_MRVL && MMC
depends on FW_LOADER
+ select BPAUTO_WANT_DEV_COREDUMP
help
The driver for Marvell Bluetooth chipsets with SDIO interface.
diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c
index 1723b6e..544feb3 100644
--- a/drivers/bluetooth/ath3k.c
+++ b/drivers/bluetooth/ath3k.c
@@ -79,6 +79,7 @@
{ USB_DEVICE(0x0489, 0xe057) },
{ USB_DEVICE(0x0489, 0xe056) },
{ USB_DEVICE(0x0489, 0xe05f) },
+ { USB_DEVICE(0x0489, 0xe078) },
{ USB_DEVICE(0x04c5, 0x1330) },
{ USB_DEVICE(0x04CA, 0x3004) },
{ USB_DEVICE(0x04CA, 0x3005) },
@@ -86,6 +87,7 @@
{ USB_DEVICE(0x04CA, 0x3007) },
{ USB_DEVICE(0x04CA, 0x3008) },
{ USB_DEVICE(0x04CA, 0x300b) },
+ { USB_DEVICE(0x04CA, 0x3010) },
{ USB_DEVICE(0x0930, 0x0219) },
{ USB_DEVICE(0x0930, 0x0220) },
{ USB_DEVICE(0x0930, 0x0227) },
@@ -105,6 +107,7 @@
{ USB_DEVICE(0x13d3, 0x3375) },
{ USB_DEVICE(0x13d3, 0x3393) },
{ USB_DEVICE(0x13d3, 0x3402) },
+ { USB_DEVICE(0x13d3, 0x3408) },
{ USB_DEVICE(0x13d3, 0x3432) },
/* Atheros AR5BBU12 with sflash firmware */
@@ -130,6 +133,7 @@
{ USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe05f), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0489, 0xe078), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 },
@@ -137,6 +141,7 @@
{ USB_DEVICE(0x04ca, 0x3007), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x300b), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0227), .driver_info = BTUSB_ATH3012 },
@@ -156,6 +161,7 @@
{ USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 },
/* Atheros AR5BBU22 with sflash firmware */
diff --git a/drivers/bluetooth/btmrvl_debugfs.c b/drivers/bluetooth/btmrvl_debugfs.c
index 023d35e..1828ed8 100644
--- a/drivers/bluetooth/btmrvl_debugfs.c
+++ b/drivers/bluetooth/btmrvl_debugfs.c
@@ -167,6 +167,35 @@
.llseek = default_llseek,
};
+static ssize_t btmrvl_fwdump_write(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct btmrvl_private *priv = file->private_data;
+ char buf[16];
+ bool result;
+
+ memset(buf, 0, sizeof(buf));
+
+ if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+ return -EFAULT;
+
+ if (strtobool(buf, &result))
+ return -EINVAL;
+
+ if (!result)
+ return -EINVAL;
+
+ btmrvl_firmware_dump(priv);
+
+ return count;
+}
+
+static const struct file_operations btmrvl_fwdump_fops = {
+ .write = btmrvl_fwdump_write,
+ .open = simple_open,
+ .llseek = default_llseek,
+};
+
void btmrvl_debugfs_init(struct hci_dev *hdev)
{
struct btmrvl_private *priv = hci_get_drvdata(hdev);
@@ -197,6 +226,8 @@
priv, &btmrvl_hscmd_fops);
debugfs_create_file("hscfgcmd", 0644, dbg->config_dir,
priv, &btmrvl_hscfgcmd_fops);
+ debugfs_create_file("fw_dump", 0200, dbg->config_dir,
+ priv, &btmrvl_fwdump_fops);
dbg->status_dir = debugfs_create_dir("status", hdev->debugfs);
debugfs_create_u8("curpsmode", 0444, dbg->status_dir,
diff --git a/drivers/bluetooth/btmrvl_drv.h b/drivers/bluetooth/btmrvl_drv.h
index 38ad662..330f8f8 100644
--- a/drivers/bluetooth/btmrvl_drv.h
+++ b/drivers/bluetooth/btmrvl_drv.h
@@ -32,6 +32,24 @@
/* Time to wait for command response in millisecond */
#define WAIT_UNTIL_CMD_RESP 5000
+enum rdwr_status {
+ RDWR_STATUS_SUCCESS = 0,
+ RDWR_STATUS_FAILURE = 1,
+ RDWR_STATUS_DONE = 2
+};
+
+#define FW_DUMP_MAX_NAME_LEN 8
+#define FW_DUMP_HOST_READY 0xEE
+#define FW_DUMP_DONE 0xFF
+#define FW_DUMP_READ_DONE 0xFE
+
+struct memory_type_mapping {
+ u8 mem_name[FW_DUMP_MAX_NAME_LEN];
+ u8 *mem_ptr;
+ u32 mem_size;
+ u8 done_flag;
+};
+
struct btmrvl_thread {
struct task_struct *task;
wait_queue_head_t wait_q;
@@ -81,6 +99,7 @@
u8 *payload, u16 nb);
int (*hw_wakeup_firmware) (struct btmrvl_private *priv);
int (*hw_process_int_status) (struct btmrvl_private *priv);
+ void (*firmware_dump)(struct btmrvl_private *priv);
spinlock_t driver_lock; /* spinlock used by driver */
#ifdef CONFIG_DEBUG_FS
void *debugfs_data;
@@ -151,6 +170,7 @@
int btmrvl_enable_ps(struct btmrvl_private *priv);
int btmrvl_prepare_command(struct btmrvl_private *priv);
int btmrvl_enable_hs(struct btmrvl_private *priv);
+void btmrvl_firmware_dump(struct btmrvl_private *priv);
#ifdef CONFIG_DEBUG_FS
void btmrvl_debugfs_init(struct hci_dev *hdev);
diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c
index 1d7db20..30939c9 100644
--- a/drivers/bluetooth/btmrvl_main.c
+++ b/drivers/bluetooth/btmrvl_main.c
@@ -22,6 +22,7 @@
#include <linux/of.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
+#include <linux/mmc/sdio_func.h>
#include "btmrvl_drv.h"
#include "btmrvl_sdio.h"
@@ -41,6 +42,11 @@
priv->adapter->int_count++;
+ if (priv->adapter->hs_state == HS_ACTIVATED) {
+ BT_DBG("BT: HS DEACTIVATED in ISR!");
+ priv->adapter->hs_state = HS_DEACTIVATED;
+ }
+
wake_up_interruptible(&priv->main_thread.wait_q);
}
EXPORT_SYMBOL_GPL(btmrvl_interrupt);
@@ -209,7 +215,7 @@
ret = btmrvl_send_sync_cmd(priv, BT_CMD_MODULE_CFG_REQ, &subcmd, 1);
if (ret)
- BT_ERR("module_cfg_cmd(%x) failed\n", subcmd);
+ BT_ERR("module_cfg_cmd(%x) failed", subcmd);
return ret;
}
@@ -245,7 +251,7 @@
ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_CONFIG, param, 2);
if (ret)
- BT_ERR("HSCFG command failed\n");
+ BT_ERR("HSCFG command failed");
return ret;
}
@@ -263,7 +269,7 @@
ret = btmrvl_send_sync_cmd(priv, BT_CMD_AUTO_SLEEP_MODE, ¶m, 1);
if (ret)
- BT_ERR("PSMODE command failed\n");
+ BT_ERR("PSMODE command failed");
return 0;
}
@@ -276,7 +282,7 @@
ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_ENABLE, NULL, 0);
if (ret) {
- BT_ERR("Host sleep enable command failed\n");
+ BT_ERR("Host sleep enable command failed");
return ret;
}
@@ -323,12 +329,19 @@
} else {
ret = priv->hw_wakeup_firmware(priv);
priv->adapter->hs_state = HS_DEACTIVATED;
+ BT_DBG("BT: HS DEACTIVATED due to host activity!");
}
}
return ret;
}
+void btmrvl_firmware_dump(struct btmrvl_private *priv)
+{
+ if (priv->firmware_dump)
+ priv->firmware_dump(priv);
+}
+
static int btmrvl_tx_pkt(struct btmrvl_private *priv, struct sk_buff *skb)
{
int ret = 0;
@@ -487,34 +500,36 @@
ret = btmrvl_send_sync_cmd(priv, BT_CMD_LOAD_CONFIG_DATA, data,
BT_CAL_HDR_LEN + len);
if (ret)
- BT_ERR("Failed to download caibration data\n");
+ BT_ERR("Failed to download caibration data");
return 0;
}
-static int btmrvl_cal_data_dt(struct btmrvl_private *priv)
+static int btmrvl_check_device_tree(struct btmrvl_private *priv)
{
struct device_node *dt_node;
u8 cal_data[BT_CAL_HDR_LEN + BT_CAL_DATA_SIZE];
- const char name[] = "btmrvl_caldata";
- const char property[] = "btmrvl,caldata";
int ret;
+ u32 val;
- dt_node = of_find_node_by_name(NULL, name);
- if (!dt_node)
- return -ENODEV;
+ for_each_compatible_node(dt_node, NULL, "btmrvl,cfgdata") {
+ ret = of_property_read_u32(dt_node, "btmrvl,gpio-gap", &val);
+ if (!ret)
+ priv->btmrvl_dev.gpio_gap = val;
- ret = of_property_read_u8_array(dt_node, property,
- cal_data + BT_CAL_HDR_LEN,
- BT_CAL_DATA_SIZE);
- if (ret)
- return ret;
+ ret = of_property_read_u8_array(dt_node, "btmrvl,cal-data",
+ cal_data + BT_CAL_HDR_LEN,
+ BT_CAL_DATA_SIZE);
+ if (ret)
+ return ret;
- BT_DBG("Use cal data from device tree");
- ret = btmrvl_download_cal_data(priv, cal_data, BT_CAL_DATA_SIZE);
- if (ret) {
- BT_ERR("Fail to download calibrate data");
- return ret;
+ BT_DBG("Use cal data from device tree");
+ ret = btmrvl_download_cal_data(priv, cal_data,
+ BT_CAL_DATA_SIZE);
+ if (ret) {
+ BT_ERR("Fail to download calibrate data");
+ return ret;
+ }
}
return 0;
@@ -526,14 +541,15 @@
btmrvl_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ);
- btmrvl_cal_data_dt(priv);
+ priv->btmrvl_dev.gpio_gap = 0xffff;
+
+ btmrvl_check_device_tree(priv);
btmrvl_pscan_window_reporting(priv, 0x01);
priv->btmrvl_dev.psmode = 1;
btmrvl_enable_ps(priv);
- priv->btmrvl_dev.gpio_gap = 0xffff;
btmrvl_send_hscfg_cmd(priv);
return 0;
diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c
index 550bce0..0057c0b 100644
--- a/drivers/bluetooth/btmrvl_sdio.c
+++ b/drivers/bluetooth/btmrvl_sdio.c
@@ -24,6 +24,7 @@
#include <linux/mmc/sdio_ids.h>
#include <linux/mmc/sdio_func.h>
#include <linux/module.h>
+#include <linux/devcoredump.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -33,6 +34,24 @@
#define VERSION "1.0"
+static struct memory_type_mapping mem_type_mapping_tbl[] = {
+ {"ITCM", NULL, 0, 0xF0},
+ {"DTCM", NULL, 0, 0xF1},
+ {"SQRAM", NULL, 0, 0xF2},
+ {"APU", NULL, 0, 0xF3},
+ {"CIU", NULL, 0, 0xF4},
+ {"ICU", NULL, 0, 0xF5},
+ {"MAC", NULL, 0, 0xF6},
+ {"EXT7", NULL, 0, 0xF7},
+ {"EXT8", NULL, 0, 0xF8},
+ {"EXT9", NULL, 0, 0xF9},
+ {"EXT10", NULL, 0, 0xFA},
+ {"EXT11", NULL, 0, 0xFB},
+ {"EXT12", NULL, 0, 0xFC},
+ {"EXT13", NULL, 0, 0xFD},
+ {"EXTLAST", NULL, 0, 0xFE},
+};
+
/* The btmrvl_sdio_remove() callback function is called
* when user removes this module from kernel space or ejects
* the card from the slot. The driver handles these 2 cases
@@ -122,6 +141,9 @@
.int_read_to_clear = true,
.host_int_rsr = 0x01,
.card_misc_cfg = 0xcc,
+ .fw_dump_ctrl = 0xe2,
+ .fw_dump_start = 0xe3,
+ .fw_dump_end = 0xea,
};
static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
@@ -130,6 +152,7 @@
.reg = &btmrvl_reg_8688,
.support_pscan_win_report = false,
.sd_blksz_fw_dl = 64,
+ .supports_fw_dump = false,
};
static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = {
@@ -138,6 +161,7 @@
.reg = &btmrvl_reg_87xx,
.support_pscan_win_report = false,
.sd_blksz_fw_dl = 256,
+ .supports_fw_dump = false,
};
static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = {
@@ -146,6 +170,7 @@
.reg = &btmrvl_reg_87xx,
.support_pscan_win_report = false,
.sd_blksz_fw_dl = 256,
+ .supports_fw_dump = false,
};
static const struct btmrvl_sdio_device btmrvl_sdio_sd8887 = {
@@ -154,6 +179,7 @@
.reg = &btmrvl_reg_8887,
.support_pscan_win_report = true,
.sd_blksz_fw_dl = 256,
+ .supports_fw_dump = false,
};
static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = {
@@ -162,6 +188,7 @@
.reg = &btmrvl_reg_8897,
.support_pscan_win_report = true,
.sd_blksz_fw_dl = 256,
+ .supports_fw_dump = true,
};
static const struct sdio_device_id btmrvl_sdio_ids[] = {
@@ -764,8 +791,8 @@
card = sdio_get_drvdata(func);
if (!card || !card->priv) {
- BT_ERR("sbi_interrupt(%p) card or priv is "
- "NULL, card=%p\n", func, card);
+ BT_ERR("sbi_interrupt(%p) card or priv is NULL, card=%p",
+ func, card);
return;
}
@@ -1080,6 +1107,277 @@
return ret;
}
+static void btmrvl_sdio_dump_regs(struct btmrvl_private *priv)
+{
+ struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
+ int ret = 0;
+ unsigned int reg, reg_start, reg_end;
+ char buf[256], *ptr;
+ u8 loop, func, data;
+ int MAX_LOOP = 2;
+
+ btmrvl_sdio_wakeup_fw(priv);
+ sdio_claim_host(card->func);
+
+ for (loop = 0; loop < MAX_LOOP; loop++) {
+ memset(buf, 0, sizeof(buf));
+ ptr = buf;
+
+ if (loop == 0) {
+ /* Read the registers of SDIO function0 */
+ func = loop;
+ reg_start = 0;
+ reg_end = 9;
+ } else {
+ func = 2;
+ reg_start = 0;
+ reg_end = 0x09;
+ }
+
+ ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ",
+ func, reg_start, reg_end);
+ for (reg = reg_start; reg <= reg_end; reg++) {
+ if (func == 0)
+ data = sdio_f0_readb(card->func, reg, &ret);
+ else
+ data = sdio_readb(card->func, reg, &ret);
+
+ if (!ret) {
+ ptr += sprintf(ptr, "%02x ", data);
+ } else {
+ ptr += sprintf(ptr, "ERR");
+ break;
+ }
+ }
+
+ BT_INFO("%s", buf);
+ }
+
+ sdio_release_host(card->func);
+}
+
+/* This function read/write firmware */
+static enum
+rdwr_status btmrvl_sdio_rdwr_firmware(struct btmrvl_private *priv,
+ u8 doneflag)
+{
+ struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
+ int ret, tries;
+ u8 ctrl_data = 0;
+
+ sdio_writeb(card->func, FW_DUMP_HOST_READY, card->reg->fw_dump_ctrl,
+ &ret);
+
+ if (ret) {
+ BT_ERR("SDIO write err");
+ return RDWR_STATUS_FAILURE;
+ }
+
+ for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
+ ctrl_data = sdio_readb(card->func, card->reg->fw_dump_ctrl,
+ &ret);
+
+ if (ret) {
+ BT_ERR("SDIO read err");
+ return RDWR_STATUS_FAILURE;
+ }
+
+ if (ctrl_data == FW_DUMP_DONE)
+ break;
+ if (doneflag && ctrl_data == doneflag)
+ return RDWR_STATUS_DONE;
+ if (ctrl_data != FW_DUMP_HOST_READY) {
+ BT_INFO("The ctrl reg was changed, re-try again!");
+ sdio_writeb(card->func, FW_DUMP_HOST_READY,
+ card->reg->fw_dump_ctrl, &ret);
+ if (ret) {
+ BT_ERR("SDIO write err");
+ return RDWR_STATUS_FAILURE;
+ }
+ }
+ usleep_range(100, 200);
+ }
+
+ if (ctrl_data == FW_DUMP_HOST_READY) {
+ BT_ERR("Fail to pull ctrl_data");
+ return RDWR_STATUS_FAILURE;
+ }
+
+ return RDWR_STATUS_SUCCESS;
+}
+
+/* This function dump sdio register and memory data */
+static void btmrvl_sdio_dump_firmware(struct btmrvl_private *priv)
+{
+ struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
+ int ret = 0;
+ unsigned int reg, reg_start, reg_end;
+ enum rdwr_status stat;
+ u8 *dbg_ptr, *end_ptr, *fw_dump_data, *fw_dump_ptr;
+ u8 dump_num, idx, i, read_reg, doneflag = 0;
+ u32 memory_size, fw_dump_len = 0;
+
+ /* dump sdio register first */
+ btmrvl_sdio_dump_regs(priv);
+
+ if (!card->supports_fw_dump) {
+ BT_ERR("Firmware dump not supported for this card!");
+ return;
+ }
+
+ for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) {
+ struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];
+
+ if (entry->mem_ptr) {
+ vfree(entry->mem_ptr);
+ entry->mem_ptr = NULL;
+ }
+ entry->mem_size = 0;
+ }
+
+ btmrvl_sdio_wakeup_fw(priv);
+ sdio_claim_host(card->func);
+
+ BT_INFO("== btmrvl firmware dump start ==");
+
+ stat = btmrvl_sdio_rdwr_firmware(priv, doneflag);
+ if (stat == RDWR_STATUS_FAILURE)
+ goto done;
+
+ reg = card->reg->fw_dump_start;
+ /* Read the number of the memories which will dump */
+ dump_num = sdio_readb(card->func, reg, &ret);
+
+ if (ret) {
+ BT_ERR("SDIO read memory length err");
+ goto done;
+ }
+
+ /* Read the length of every memory which will dump */
+ for (idx = 0; idx < dump_num; idx++) {
+ struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];
+
+ stat = btmrvl_sdio_rdwr_firmware(priv, doneflag);
+ if (stat == RDWR_STATUS_FAILURE)
+ goto done;
+
+ memory_size = 0;
+ reg = card->reg->fw_dump_start;
+ for (i = 0; i < 4; i++) {
+ read_reg = sdio_readb(card->func, reg, &ret);
+ if (ret) {
+ BT_ERR("SDIO read err");
+ goto done;
+ }
+ memory_size |= (read_reg << i*8);
+ reg++;
+ }
+
+ if (memory_size == 0) {
+ BT_INFO("Firmware dump finished!");
+ break;
+ }
+
+ BT_INFO("%s_SIZE=0x%x", entry->mem_name, memory_size);
+ entry->mem_ptr = vzalloc(memory_size + 1);
+ entry->mem_size = memory_size;
+ if (!entry->mem_ptr) {
+ BT_ERR("Vzalloc %s failed", entry->mem_name);
+ goto done;
+ }
+
+ fw_dump_len += (strlen("========Start dump ") +
+ strlen(entry->mem_name) +
+ strlen("========\n") +
+ (memory_size + 1) +
+ strlen("\n========End dump========\n"));
+
+ dbg_ptr = entry->mem_ptr;
+ end_ptr = dbg_ptr + memory_size;
+
+ doneflag = entry->done_flag;
+ BT_INFO("Start %s output, please wait...",
+ entry->mem_name);
+
+ do {
+ stat = btmrvl_sdio_rdwr_firmware(priv, doneflag);
+ if (stat == RDWR_STATUS_FAILURE)
+ goto done;
+
+ reg_start = card->reg->fw_dump_start;
+ reg_end = card->reg->fw_dump_end;
+ for (reg = reg_start; reg <= reg_end; reg++) {
+ *dbg_ptr = sdio_readb(card->func, reg, &ret);
+ if (ret) {
+ BT_ERR("SDIO read err");
+ goto done;
+ }
+ if (dbg_ptr < end_ptr)
+ dbg_ptr++;
+ else
+ BT_ERR("Allocated buffer not enough");
+ }
+
+ if (stat != RDWR_STATUS_DONE) {
+ continue;
+ } else {
+ BT_INFO("%s done: size=0x%tx",
+ entry->mem_name,
+ dbg_ptr - entry->mem_ptr);
+ break;
+ }
+ } while (1);
+ }
+
+ BT_INFO("== btmrvl firmware dump end ==");
+
+done:
+ sdio_release_host(card->func);
+
+ if (fw_dump_len == 0)
+ return;
+
+ fw_dump_data = vzalloc(fw_dump_len+1);
+ if (!fw_dump_data) {
+ BT_ERR("Vzalloc fw_dump_data fail!");
+ return;
+ }
+ fw_dump_ptr = fw_dump_data;
+
+ /* Dump all the memory data into single file, a userspace script will
+ be used to split all the memory data to multiple files*/
+ BT_INFO("== btmrvl firmware dump to /sys/class/devcoredump start");
+ for (idx = 0; idx < dump_num; idx++) {
+ struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];
+
+ if (entry->mem_ptr) {
+ strcpy(fw_dump_ptr, "========Start dump ");
+ fw_dump_ptr += strlen("========Start dump ");
+
+ strcpy(fw_dump_ptr, entry->mem_name);
+ fw_dump_ptr += strlen(entry->mem_name);
+
+ strcpy(fw_dump_ptr, "========\n");
+ fw_dump_ptr += strlen("========\n");
+
+ memcpy(fw_dump_ptr, entry->mem_ptr, entry->mem_size);
+ fw_dump_ptr += entry->mem_size;
+
+ strcpy(fw_dump_ptr, "\n========End dump========\n");
+ fw_dump_ptr += strlen("\n========End dump========\n");
+
+ vfree(mem_type_mapping_tbl[idx].mem_ptr);
+ mem_type_mapping_tbl[idx].mem_ptr = NULL;
+ }
+ }
+
+ /* fw_dump_data will be free in device coredump release function
+ after 5 min*/
+ dev_coredumpv(&priv->btmrvl_dev.hcidev->dev, fw_dump_data,
+ fw_dump_len, GFP_KERNEL);
+ BT_INFO("== btmrvl firmware dump to /sys/class/devcoredump end");
+}
+
static int btmrvl_sdio_probe(struct sdio_func *func,
const struct sdio_device_id *id)
{
@@ -1103,6 +1401,7 @@
card->reg = data->reg;
card->sd_blksz_fw_dl = data->sd_blksz_fw_dl;
card->support_pscan_win_report = data->support_pscan_win_report;
+ card->supports_fw_dump = data->supports_fw_dump;
}
if (btmrvl_sdio_register_dev(card) < 0) {
@@ -1134,6 +1433,7 @@
priv->hw_host_to_card = btmrvl_sdio_host_to_card;
priv->hw_wakeup_firmware = btmrvl_sdio_wakeup_fw;
priv->hw_process_int_status = btmrvl_sdio_process_int_status;
+ priv->firmware_dump = btmrvl_sdio_dump_firmware;
if (btmrvl_register_hdev(priv)) {
BT_ERR("Register hdev failed!");
diff --git a/drivers/bluetooth/btmrvl_sdio.h b/drivers/bluetooth/btmrvl_sdio.h
index 453559f..1a3bd06 100644
--- a/drivers/bluetooth/btmrvl_sdio.h
+++ b/drivers/bluetooth/btmrvl_sdio.h
@@ -81,6 +81,9 @@
bool int_read_to_clear;
u8 host_int_rsr;
u8 card_misc_cfg;
+ u8 fw_dump_ctrl;
+ u8 fw_dump_start;
+ u8 fw_dump_end;
};
struct btmrvl_sdio_card {
@@ -90,6 +93,7 @@
const char *firmware;
const struct btmrvl_sdio_card_reg *reg;
bool support_pscan_win_report;
+ bool supports_fw_dump;
u16 sd_blksz_fw_dl;
u8 rx_unit;
struct btmrvl_private *priv;
@@ -101,6 +105,7 @@
const struct btmrvl_sdio_card_reg *reg;
const bool support_pscan_win_report;
u16 sd_blksz_fw_dl;
+ bool supports_fw_dump;
};
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index f9db709..61d9c77 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -106,9 +106,12 @@
{ USB_DEVICE(0x0b05, 0x17b5) },
{ USB_DEVICE(0x0b05, 0x17cb) },
{ USB_DEVICE(0x413c, 0x8197) },
+ { USB_DEVICE(0x13d3, 0x3404),
+ .driver_info = BTUSB_BCM_PATCHRAM },
/* Foxconn - Hon Hai */
- { USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01) },
+ { USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01),
+ .driver_info = BTUSB_BCM_PATCHRAM },
/* Broadcom devices with vendor specific id */
{ USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01),
@@ -156,6 +159,7 @@
{ USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe05f), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0489, 0xe078), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 },
@@ -163,6 +167,7 @@
{ USB_DEVICE(0x04ca, 0x3007), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x300b), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0227), .driver_info = BTUSB_ATH3012 },
@@ -182,6 +187,7 @@
{ USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 },
/* Atheros AR5BBU12 with sflash firmware */
@@ -298,6 +304,8 @@
unsigned int sco_num;
int isoc_altsetting;
int suspend_count;
+
+ int (*recv_bulk)(struct btusb_data *data, void *buffer, int count);
};
static inline void btusb_free_frags(struct btusb_data *data)
@@ -589,7 +597,7 @@
if (urb->status == 0) {
hdev->stat.byte_rx += urb->actual_length;
- if (btusb_recv_bulk(data, urb->transfer_buffer,
+ if (data->recv_bulk(data, urb->transfer_buffer,
urb->actual_length) < 0) {
BT_ERR("%s corrupted ACL packet", hdev->name);
hdev->stat.err_rx++;
@@ -2011,6 +2019,8 @@
init_usb_anchor(&data->isoc_anchor);
spin_lock_init(&data->rxlock);
+ data->recv_bulk = btusb_recv_bulk;
+
hdev = hci_alloc_dev();
if (!hdev)
return -ENOMEM;
@@ -2034,6 +2044,7 @@
if (id->driver_info & BTUSB_BCM_PATCHRAM) {
hdev->setup = btusb_setup_bcm_patchram;
hdev->set_bdaddr = btusb_set_bdaddr_bcm;
+ set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
}
if (id->driver_info & BTUSB_INTEL) {
diff --git a/drivers/bluetooth/btwilink.c b/drivers/bluetooth/btwilink.c
index f038dba..55c135b 100644
--- a/drivers/bluetooth/btwilink.c
+++ b/drivers/bluetooth/btwilink.c
@@ -349,7 +349,6 @@
.remove = bt_ti_remove,
.driver = {
.name = "btwilink",
- .owner = THIS_MODULE,
},
};
diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c
index b6d639e..326eb27 100644
--- a/drivers/bluetooth/hci_ath.c
+++ b/drivers/bluetooth/hci_ath.c
@@ -78,7 +78,7 @@
status = tty->driver->ops->tiocmget(tty);
- /* Disable Automatic RTSCTS */
+ /* Enable Automatic RTSCTS */
ktermios.c_cflag |= CRTSCTS;
status = tty_set_termios(tty, &ktermios);
diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c
index a228386..ec0fa77 100644
--- a/drivers/bluetooth/hci_h5.c
+++ b/drivers/bluetooth/hci_h5.c
@@ -168,6 +168,27 @@
hci_uart_tx_wakeup(hu);
}
+static void h5_peer_reset(struct hci_uart *hu)
+{
+ struct h5 *h5 = hu->priv;
+
+ BT_ERR("Peer device has reset");
+
+ h5->state = H5_UNINITIALIZED;
+
+ del_timer(&h5->timer);
+
+ skb_queue_purge(&h5->rel);
+ skb_queue_purge(&h5->unrel);
+ skb_queue_purge(&h5->unack);
+
+ h5->tx_seq = 0;
+ h5->tx_ack = 0;
+
+ /* Send reset request to upper stack */
+ hci_reset_dev(hu->hdev);
+}
+
static int h5_open(struct hci_uart *hu)
{
struct h5 *h5;
@@ -283,8 +304,12 @@
conf_req[2] = h5_cfg_field(h5);
if (memcmp(data, sync_req, 2) == 0) {
+ if (h5->state == H5_ACTIVE)
+ h5_peer_reset(hu);
h5_link_control(hu, sync_rsp, 2);
} else if (memcmp(data, sync_rsp, 2) == 0) {
+ if (h5->state == H5_ACTIVE)
+ h5_peer_reset(hu);
h5->state = H5_INITIALIZED;
h5_link_control(hu, conf_req, 3);
} else if (memcmp(data, conf_req, 2) == 0) {
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
index fda0983..ac7a704 100644
--- a/drivers/media/Kconfig
+++ b/drivers/media/Kconfig
@@ -3,7 +3,7 @@
#
menuconfig MEDIA_SUPPORT
- depends on !BACKPORT_KERNEL_3_2
+ depends on !KERNEL_3_2
tristate "Multimedia support"
depends on m
depends on HAS_IOMEM
@@ -165,7 +165,6 @@
source "drivers/media/pci/Kconfig"
source "drivers/media/platform/Kconfig"
source "drivers/media/mmc/Kconfig"
-source "drivers/media/parport/Kconfig"
source "drivers/media/radio/Kconfig"
comment "Supported FireWire (IEEE 1394) Adapters"
diff --git a/drivers/media/Makefile b/drivers/media/Makefile
index 6219093..9f5d0bc 100644
--- a/drivers/media/Makefile
+++ b/drivers/media/Makefile
@@ -28,6 +28,6 @@
# Finally, merge the drivers that require the core
#
-obj-y += common/ platform/ pci/ usb/ mmc/ firewire/ parport/
+obj-y += common/ platform/ pci/ usb/ mmc/ firewire/
obj-$(CPTCFG_VIDEO_DEV) += radio/
diff --git a/drivers/media/common/cx2341x.c b/drivers/media/common/cx2341x.c
index be76315..c07b9db 100644
--- a/drivers/media/common/cx2341x.c
+++ b/drivers/media/common/cx2341x.c
@@ -931,6 +931,35 @@
}
}
+/* Check for correctness of the ctrl's value based on the data from
+ struct v4l2_queryctrl and the available menu items. Note that
+ menu_items may be NULL, in that case it is ignored. */
+static int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl,
+ const char * const *menu_items)
+{
+ if (qctrl->flags & V4L2_CTRL_FLAG_DISABLED)
+ return -EINVAL;
+ if (qctrl->flags & V4L2_CTRL_FLAG_GRABBED)
+ return -EBUSY;
+ if (qctrl->type == V4L2_CTRL_TYPE_STRING)
+ return 0;
+ if (qctrl->type == V4L2_CTRL_TYPE_BUTTON ||
+ qctrl->type == V4L2_CTRL_TYPE_INTEGER64 ||
+ qctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS)
+ return 0;
+ if (ctrl->value < qctrl->minimum || ctrl->value > qctrl->maximum)
+ return -ERANGE;
+ if (qctrl->type == V4L2_CTRL_TYPE_MENU && menu_items != NULL) {
+ if (menu_items[ctrl->value] == NULL ||
+ menu_items[ctrl->value][0] == '\0')
+ return -EINVAL;
+ }
+ if (qctrl->type == V4L2_CTRL_TYPE_BITMASK &&
+ (ctrl->value & ~qctrl->maximum))
+ return -ERANGE;
+ return 0;
+}
+
int cx2341x_ext_ctrls(struct cx2341x_mpeg_params *params, int busy,
struct v4l2_ext_controls *ctrls, unsigned int cmd)
{
diff --git a/drivers/media/common/saa7146/saa7146_core.c b/drivers/media/common/saa7146/saa7146_core.c
index 97afee6..1ff9f53 100644
--- a/drivers/media/common/saa7146/saa7146_core.c
+++ b/drivers/media/common/saa7146/saa7146_core.c
@@ -71,7 +71,7 @@
if (saa7146_read(dev, MC2) & 2)
break;
if (err) {
- pr_err("%s: %s timed out while waiting for registers getting programmed\n",
+ pr_debug("%s: %s timed out while waiting for registers getting programmed\n",
dev->name, __func__);
return -ETIMEDOUT;
}
@@ -364,6 +364,9 @@
goto out;
}
+ /* create a nice device name */
+ sprintf(dev->name, "saa7146 (%d)", saa7146_num);
+
DEB_EE("pci:%p\n", pci);
err = pci_enable_device(pci);
@@ -438,9 +441,6 @@
/* the rest + print status message */
- /* create a nice device name */
- sprintf(dev->name, "saa7146 (%d)", saa7146_num);
-
pr_info("found saa7146 @ mem %p (revision %d, irq %d) (0x%04x,0x%04x)\n",
dev->mem, dev->revision, pci->irq,
pci->subsystem_vendor, pci->subsystem_device);
diff --git a/drivers/media/common/siano/smsir.c b/drivers/media/common/siano/smsir.c
index 273043e..35d0e88 100644
--- a/drivers/media/common/siano/smsir.c
+++ b/drivers/media/common/siano/smsir.c
@@ -107,8 +107,7 @@
void sms_ir_exit(struct smscore_device_t *coredev)
{
- if (coredev->ir.dev)
- rc_unregister_device(coredev->ir.dev);
+ rc_unregister_device(coredev->ir.dev);
sms_log("");
}
diff --git a/drivers/media/common/tveeprom.c b/drivers/media/common/tveeprom.c
index c7dace6..47da037 100644
--- a/drivers/media/common/tveeprom.c
+++ b/drivers/media/common/tveeprom.c
@@ -286,9 +286,17 @@
{ TUNER_ABSENT, "Xceive XC5200C"},
{ TUNER_ABSENT, "NXP 18273"},
{ TUNER_ABSENT, "Montage M88TS2022"},
- /* 180-189 */
+ /* 180-188 */
{ TUNER_ABSENT, "NXP 18272M"},
{ TUNER_ABSENT, "NXP 18272S"},
+
+ { TUNER_ABSENT, "Mirics MSi003"},
+ { TUNER_ABSENT, "MaxLinear MxL256"},
+ { TUNER_ABSENT, "SiLabs Si2158"},
+ { TUNER_ABSENT, "SiLabs Si2178"},
+ { TUNER_ABSENT, "SiLabs Si2157"},
+ { TUNER_ABSENT, "SiLabs Si2177"},
+ { TUNER_ABSENT, "ITE IT9137FN"},
};
/* Use TVEEPROM_AUDPROC_INTERNAL for those audio 'chips' that are
@@ -351,6 +359,16 @@
{ TVEEPROM_AUDPROC_INTERNAL, "CX23887" },
{ TVEEPROM_AUDPROC_INTERNAL, "SAA7164" },
{ TVEEPROM_AUDPROC_INTERNAL, "AU8522" },
+ /* 45-49 */
+ { TVEEPROM_AUDPROC_INTERNAL, "AVF4910B" },
+ { TVEEPROM_AUDPROC_INTERNAL, "SAA7231" },
+ { TVEEPROM_AUDPROC_INTERNAL, "CX23102" },
+ { TVEEPROM_AUDPROC_INTERNAL, "SAA7163" },
+ { TVEEPROM_AUDPROC_OTHER, "AK4113" },
+ /* 50-52 */
+ { TVEEPROM_AUDPROC_OTHER, "CS5340" },
+ { TVEEPROM_AUDPROC_OTHER, "CS8416" },
+ { TVEEPROM_AUDPROC_OTHER, "CX20810" },
};
/* This list is supplied by Hauppauge. Thanks! */
@@ -371,8 +389,12 @@
"CX25843", "CX23418", "NEC61153", "CX23885", "CX23888",
/* 35-39 */
"SAA7131", "CX25837", "CX23887", "CX23885A", "CX23887A",
- /* 40-42 */
- "SAA7164", "CX23885B", "AU8522"
+ /* 40-44 */
+ "SAA7164", "CX23885B", "AU8522", "ADV7401", "AVF4910B",
+ /* 45-49 */
+ "SAA7231", "CX23102", "SAA7163", "ADV7441A", "ADV7181C",
+ /* 50-53 */
+ "CX25836", "TDA9955", "TDA19977", "ADV7842"
};
static int hasRadioTuner(int tunerType)
@@ -548,10 +570,10 @@
tvee->serial_number =
eeprom_data[i+5] +
(eeprom_data[i+6] << 8) +
- (eeprom_data[i+7] << 16);
+ (eeprom_data[i+7] << 16)+
+ (eeprom_data[i+8] << 24);
- if ((eeprom_data[i + 8] & 0xf0) &&
- (tvee->serial_number < 0xffffff)) {
+ if (eeprom_data[i + 8] == 0xf0) {
tvee->MAC_address[0] = 0x00;
tvee->MAC_address[1] = 0x0D;
tvee->MAC_address[2] = 0xFE;
@@ -696,7 +718,7 @@
}
}
- tveeprom_info("Hauppauge model %d, rev %s, serial# %d\n",
+ tveeprom_info("Hauppauge model %d, rev %s, serial# %u\n",
tvee->model, tvee->rev_str, tvee->serial_number);
if (tvee->has_MAC_address == 1)
tveeprom_info("MAC address is %pM\n", tvee->MAC_address);
diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h
index e07a84e..80ab8d0 100644
--- a/drivers/media/dvb-core/dvb-usb-ids.h
+++ b/drivers/media/dvb-core/dvb-usb-ids.h
@@ -356,6 +356,7 @@
#define USB_PID_MSI_DIGI_VOX_MINI_III 0x8807
#define USB_PID_SONY_PLAYTV 0x0003
#define USB_PID_MYGICA_D689 0xd811
+#define USB_PID_MYGICA_T230 0xc688
#define USB_PID_ELGATO_EYETV_DIVERSITY 0x0011
#define USB_PID_ELGATO_EYETV_DTT 0x0021
#define USB_PID_ELGATO_EYETV_DTT_2 0x003f
diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c
index 7e9b079..c29531e 100644
--- a/drivers/media/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb-core/dvb_frontend.c
@@ -962,6 +962,11 @@
case SYS_ATSC:
c->modulation = VSB_8;
break;
+ case SYS_ISDBS:
+ c->symbol_rate = 28860000;
+ c->rolloff = ROLLOFF_35;
+ c->bandwidth_hz = c->symbol_rate / 100 * 135;
+ break;
default:
c->modulation = QAM_AUTO;
break;
@@ -2072,6 +2077,7 @@
break;
case SYS_DVBS:
case SYS_TURBO:
+ case SYS_ISDBS:
rolloff = 135;
break;
case SYS_DVBS2:
diff --git a/drivers/media/dvb-core/dvb_net.c b/drivers/media/dvb-core/dvb_net.c
index 059e611..e4041f0 100644
--- a/drivers/media/dvb-core/dvb_net.c
+++ b/drivers/media/dvb-core/dvb_net.c
@@ -379,7 +379,9 @@
/* Check TS error conditions: sync_byte, transport_error_indicator, scrambling_control . */
if ((ts[0] != TS_SYNC) || (ts[1] & TS_TEI) || ((ts[3] & TS_SC) != 0)) {
printk(KERN_WARNING "%lu: Invalid TS cell: SYNC %#x, TEI %u, SC %#x.\n",
- priv->ts_count, ts[0], ts[1] & TS_TEI >> 7, ts[3] & 0xC0 >> 6);
+ priv->ts_count, ts[0],
+ (ts[1] & TS_TEI) >> 7,
+ (ts[3] & TS_SC) >> 6);
/* Drop partly decoded SNDU, reset state, resync on PUSI. */
if (priv->ule_skb) {
diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
index 8971120..bafccf7 100644
--- a/drivers/media/dvb-frontends/Kconfig
+++ b/drivers/media/dvb-frontends/Kconfig
@@ -728,13 +728,16 @@
A driver for Fujitsu mb86a20s ISDB-T/ISDB-Tsb demodulator.
Say Y when you want to support this frontend.
+comment "ISDB-S (satellite) & ISDB-T (terrestrial) frontends"
+ depends on DVB_CORE
+
config DVB_TC90522
tristate "Toshiba TC90522"
depends on m
depends on DVB_CORE && I2C
default m if !MEDIA_SUBDRV_AUTOSELECT
help
- A Toshiba TC90522 2xISDB-T + 2xISDB-S demodulator.
+ Toshiba TC90522 2xISDB-S 8PSK + 2xISDB-T OFDM demodulator.
Say Y when you want to support this frontend.
comment "Digital terrestrial only tuners/PLL"
diff --git a/drivers/media/dvb-frontends/af9033.c b/drivers/media/dvb-frontends/af9033.c
index 63a89c1..82ce47b 100644
--- a/drivers/media/dvb-frontends/af9033.c
+++ b/drivers/media/dvb-frontends/af9033.c
@@ -291,6 +291,12 @@
if (clock_adc_lut[i].clock == dev->cfg.clock)
break;
}
+ if (i == ARRAY_SIZE(clock_adc_lut)) {
+ dev_err(&dev->client->dev,
+ "Couldn't find ADC config for clock=%d\n",
+ dev->cfg.clock);
+ goto err;
+ }
adc_cw = af9033_div(dev, clock_adc_lut[i].adc, 1000000ul, 19ul);
buf[0] = (adc_cw >> 0) & 0xff;
@@ -580,7 +586,15 @@
break;
}
}
- ret = af9033_wr_regs(dev, 0x800001,
+ if (i == ARRAY_SIZE(coeff_lut)) {
+ dev_err(&dev->client->dev,
+ "Couldn't find LUT config for clock=%d\n",
+ dev->cfg.clock);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = af9033_wr_regs(dev, 0x800001,
coeff_lut[i].val, sizeof(coeff_lut[i].val));
}
@@ -592,6 +606,13 @@
if (clock_adc_lut[i].clock == dev->cfg.clock)
break;
}
+ if (i == ARRAY_SIZE(clock_adc_lut)) {
+ dev_err(&dev->client->dev,
+ "Couldn't find ADC clock for clock=%d\n",
+ dev->cfg.clock);
+ ret = -EINVAL;
+ goto err;
+ }
adc_freq = clock_adc_lut[i].adc;
/* get used IF frequency */
@@ -849,29 +870,97 @@
{
struct af9033_dev *dev = fe->demodulator_priv;
struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache;
+ int ret;
+ u8 u8tmp;
/* use DVBv5 CNR */
- if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL)
- *snr = div_s64(c->cnr.stat[0].svalue, 100); /* 1000x => 10x */
- else
+ if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL) {
+ /* Return 0.1 dB for AF9030 and 0-0xffff for IT9130. */
+ if (dev->is_af9035) {
+ /* 1000x => 10x (0.1 dB) */
+ *snr = div_s64(c->cnr.stat[0].svalue, 100);
+ } else {
+ /* 1000x => 1x (1 dB) */
+ *snr = div_s64(c->cnr.stat[0].svalue, 1000);
+
+ /* read current modulation */
+ ret = af9033_rd_reg(dev, 0x80f903, &u8tmp);
+ if (ret)
+ goto err;
+
+ /* scale value to 0x0000-0xffff */
+ switch ((u8tmp >> 0) & 3) {
+ case 0:
+ *snr = *snr * 0xffff / 23;
+ break;
+ case 1:
+ *snr = *snr * 0xffff / 26;
+ break;
+ case 2:
+ *snr = *snr * 0xffff / 32;
+ break;
+ default:
+ goto err;
+ }
+ }
+ } else {
*snr = 0;
+ }
return 0;
+
+err:
+ dev_dbg(&dev->client->dev, "failed=%d\n", ret);
+
+ return ret;
}
static int af9033_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
{
struct af9033_dev *dev = fe->demodulator_priv;
- int ret;
- u8 strength2;
+ struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache;
+ int ret, tmp, power_real;
+ u8 u8tmp, gain_offset, buf[7];
- /* read signal strength of 0-100 scale */
- ret = af9033_rd_reg(dev, 0x800048, &strength2);
- if (ret < 0)
- goto err;
+ if (dev->is_af9035) {
+ /* read signal strength of 0-100 scale */
+ ret = af9033_rd_reg(dev, 0x800048, &u8tmp);
+ if (ret < 0)
+ goto err;
- /* scale value to 0x0000-0xffff */
- *strength = strength2 * 0xffff / 100;
+ /* scale value to 0x0000-0xffff */
+ *strength = u8tmp * 0xffff / 100;
+ } else {
+ ret = af9033_rd_reg(dev, 0x8000f7, &u8tmp);
+ if (ret < 0)
+ goto err;
+
+ ret = af9033_rd_regs(dev, 0x80f900, buf, 7);
+ if (ret < 0)
+ goto err;
+
+ if (c->frequency <= 300000000)
+ gain_offset = 7; /* VHF */
+ else
+ gain_offset = 4; /* UHF */
+
+ power_real = (u8tmp - 100 - gain_offset) -
+ power_reference[((buf[3] >> 0) & 3)][((buf[6] >> 0) & 7)];
+
+ if (power_real < -15)
+ tmp = 0;
+ else if ((power_real >= -15) && (power_real < 0))
+ tmp = (2 * (power_real + 15)) / 3;
+ else if ((power_real >= 0) && (power_real < 20))
+ tmp = 4 * power_real + 10;
+ else if ((power_real >= 20) && (power_real < 35))
+ tmp = (2 * (power_real - 20)) / 3 + 90;
+ else
+ tmp = 100;
+
+ /* scale value to 0x0000-0xffff */
+ *strength = tmp * 0xffff / 100;
+ }
return 0;
@@ -1011,6 +1100,33 @@
snr_val = (buf[2] << 16) | (buf[1] << 8) | (buf[0] << 0);
+ /* read superframe number */
+ ret = af9033_rd_reg(dev, 0x80f78b, &u8tmp);
+ if (ret)
+ goto err;
+
+ if (u8tmp)
+ snr_val /= u8tmp;
+
+ /* read current transmission mode */
+ ret = af9033_rd_reg(dev, 0x80f900, &u8tmp);
+ if (ret)
+ goto err;
+
+ switch ((u8tmp >> 0) & 3) {
+ case 0:
+ snr_val *= 4;
+ break;
+ case 1:
+ snr_val *= 1;
+ break;
+ case 2:
+ snr_val *= 2;
+ break;
+ default:
+ goto err_schedule_delayed_work;
+ }
+
/* read current modulation */
ret = af9033_rd_reg(dev, 0x80f903, &u8tmp);
if (ret)
diff --git a/drivers/media/dvb-frontends/af9033_priv.h b/drivers/media/dvb-frontends/af9033_priv.h
index c12c92c..8e23275 100644
--- a/drivers/media/dvb-frontends/af9033_priv.h
+++ b/drivers/media/dvb-frontends/af9033_priv.h
@@ -181,7 +181,10 @@
{ 0x05570d, 26 },
{ 0x059feb, 27 },
{ 0x05bf38, 28 },
- { 0xffffff, 29 },
+ { 0x05f78f, 29 },
+ { 0x0612c3, 30 },
+ { 0x0626be, 31 },
+ { 0xffffff, 32 },
};
static const struct reg_val ofsm_init[] = {
@@ -2051,4 +2054,10 @@
{ 0x80fd8b, 0x00 },
};
+/* NorDig power reference table */
+static const int power_reference[][5] = {
+ {-93, -91, -90, -89, -88}, /* QPSK 1/2 ~ 7/8 */
+ {-87, -85, -84, -83, -82}, /* 16QAM 1/2 ~ 7/8 */
+ {-82, -80, -78, -77, -76}, /* 64QAM 1/2 ~ 7/8 */
+};
#endif /* AF9033_PRIV_H */
diff --git a/drivers/media/dvb-frontends/au8522_dig.c b/drivers/media/dvb-frontends/au8522_dig.c
index a68974f..5d06c99 100644
--- a/drivers/media/dvb-frontends/au8522_dig.c
+++ b/drivers/media/dvb-frontends/au8522_dig.c
@@ -29,6 +29,7 @@
#include "au8522_priv.h"
static int debug;
+static int zv_mode = 1; /* default to on */
#define dprintk(arg...)\
do { if (debug)\
@@ -469,6 +470,87 @@
{ 0x8526, 0x01 },
};
+static struct {
+ u16 reg;
+ u16 data;
+} QAM256_mod_tab_zv_mode[] = {
+ { 0x80a3, 0x09 },
+ { 0x80a4, 0x00 },
+ { 0x8081, 0xc4 },
+ { 0x80a5, 0x40 },
+ { 0x80b5, 0xfb },
+ { 0x80b6, 0x8e },
+ { 0x80b7, 0x39 },
+ { 0x80aa, 0x77 },
+ { 0x80ad, 0x77 },
+ { 0x80a6, 0x67 },
+ { 0x8262, 0x20 },
+ { 0x821c, 0x30 },
+ { 0x80b8, 0x3e },
+ { 0x80b9, 0xf0 },
+ { 0x80ba, 0x01 },
+ { 0x80bb, 0x18 },
+ { 0x80bc, 0x50 },
+ { 0x80bd, 0x00 },
+ { 0x80be, 0xea },
+ { 0x80bf, 0xef },
+ { 0x80c0, 0xfc },
+ { 0x80c1, 0xbd },
+ { 0x80c2, 0x1f },
+ { 0x80c3, 0xfc },
+ { 0x80c4, 0xdd },
+ { 0x80c5, 0xaf },
+ { 0x80c6, 0x00 },
+ { 0x80c7, 0x38 },
+ { 0x80c8, 0x30 },
+ { 0x80c9, 0x05 },
+ { 0x80ca, 0x4a },
+ { 0x80cb, 0xd0 },
+ { 0x80cc, 0x01 },
+ { 0x80cd, 0xd9 },
+ { 0x80ce, 0x6f },
+ { 0x80cf, 0xf9 },
+ { 0x80d0, 0x70 },
+ { 0x80d1, 0xdf },
+ { 0x80d2, 0xf7 },
+ { 0x80d3, 0xc2 },
+ { 0x80d4, 0xdf },
+ { 0x80d5, 0x02 },
+ { 0x80d6, 0x9a },
+ { 0x80d7, 0xd0 },
+ { 0x8250, 0x0d },
+ { 0x8251, 0xcd },
+ { 0x8252, 0xe0 },
+ { 0x8253, 0x05 },
+ { 0x8254, 0xa7 },
+ { 0x8255, 0xff },
+ { 0x8256, 0xed },
+ { 0x8257, 0x5b },
+ { 0x8258, 0xae },
+ { 0x8259, 0xe6 },
+ { 0x825a, 0x3d },
+ { 0x825b, 0x0f },
+ { 0x825c, 0x0d },
+ { 0x825d, 0xea },
+ { 0x825e, 0xf2 },
+ { 0x825f, 0x51 },
+ { 0x8260, 0xf5 },
+ { 0x8261, 0x06 },
+ { 0x821a, 0x01 },
+ { 0x8546, 0x40 },
+ { 0x8210, 0x26 },
+ { 0x8211, 0xf6 },
+ { 0x8212, 0x84 },
+ { 0x8213, 0x02 },
+ { 0x8502, 0x01 },
+ { 0x8121, 0x04 },
+ { 0x8122, 0x04 },
+ { 0x852e, 0x10 },
+ { 0x80a4, 0xca },
+ { 0x80a7, 0x40 },
+ { 0x8526, 0x01 },
+};
+
static int au8522_enable_modulation(struct dvb_frontend *fe,
fe_modulation_t m)
{
@@ -495,12 +577,23 @@
au8522_set_if(fe, state->config->qam_if);
break;
case QAM_256:
- dprintk("%s() QAM 256\n", __func__);
- for (i = 0; i < ARRAY_SIZE(QAM256_mod_tab); i++)
- au8522_writereg(state,
- QAM256_mod_tab[i].reg,
- QAM256_mod_tab[i].data);
- au8522_set_if(fe, state->config->qam_if);
+ if (zv_mode) {
+ dprintk("%s() QAM 256 (zv_mode)\n", __func__);
+ for (i = 0; i < ARRAY_SIZE(QAM256_mod_tab_zv_mode); i++)
+ au8522_writereg(state,
+ QAM256_mod_tab_zv_mode[i].reg,
+ QAM256_mod_tab_zv_mode[i].data);
+ au8522_set_if(fe, state->config->qam_if);
+ msleep(100);
+ au8522_writereg(state, 0x821a, 0x00);
+ } else {
+ dprintk("%s() QAM 256\n", __func__);
+ for (i = 0; i < ARRAY_SIZE(QAM256_mod_tab); i++)
+ au8522_writereg(state,
+ QAM256_mod_tab[i].reg,
+ QAM256_mod_tab[i].data);
+ au8522_set_if(fe, state->config->qam_if);
+ }
break;
default:
dprintk("%s() Invalid modulation\n", __func__);
@@ -537,7 +630,12 @@
return ret;
/* Allow the tuner to settle */
- msleep(100);
+ if (zv_mode) {
+ dprintk("%s() increase tuner settling time for zv_mode\n",
+ __func__);
+ msleep(250);
+ } else
+ msleep(100);
au8522_enable_modulation(fe, c->modulation);
@@ -823,6 +921,11 @@
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Enable verbose debug messages");
+module_param(zv_mode, int, 0644);
+MODULE_PARM_DESC(zv_mode, "Turn on/off ZeeVee modulator compatability mode (default:on).\n"
+ "\t\ton - modified AU8522 QAM256 initialization.\n"
+ "\t\tProvides faster lock when using ZeeVee modulator based sources");
+
MODULE_DESCRIPTION("Auvitek AU8522 QAM-B/ATSC Demodulator driver");
MODULE_AUTHOR("Steven Toth");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/cx22700.c b/drivers/media/dvb-frontends/cx22700.c
index 3d399d9..8656326 100644
--- a/drivers/media/dvb-frontends/cx22700.c
+++ b/drivers/media/dvb-frontends/cx22700.c
@@ -169,6 +169,9 @@
cx22700_writereg (state, 0x04, val);
+ if (p->code_rate_HP - FEC_1_2 >= sizeof(fec_tab) ||
+ p->code_rate_LP - FEC_1_2 >= sizeof(fec_tab))
+ return -EINVAL;
val = fec_tab[p->code_rate_HP - FEC_1_2] << 3;
val |= fec_tab[p->code_rate_LP - FEC_1_2];
diff --git a/drivers/media/dvb-frontends/cx24110.c b/drivers/media/dvb-frontends/cx24110.c
index 95b981c..7b510f2 100644
--- a/drivers/media/dvb-frontends/cx24110.c
+++ b/drivers/media/dvb-frontends/cx24110.c
@@ -177,47 +177,45 @@
return 0;
}
-static int cx24110_set_fec (struct cx24110_state* state, fe_code_rate_t fec)
+static int cx24110_set_fec(struct cx24110_state* state, fe_code_rate_t fec)
{
-/* fixme (low): error handling */
-
- static const int rate[]={-1,1,2,3,5,7,-1};
- static const int g1[]={-1,0x01,0x02,0x05,0x15,0x45,-1};
- static const int g2[]={-1,0x01,0x03,0x06,0x1a,0x7a,-1};
+ static const int rate[FEC_AUTO] = {-1, 1, 2, 3, 5, 7, -1};
+ static const int g1[FEC_AUTO] = {-1, 0x01, 0x02, 0x05, 0x15, 0x45, -1};
+ static const int g2[FEC_AUTO] = {-1, 0x01, 0x03, 0x06, 0x1a, 0x7a, -1};
/* Well, the AutoAcq engine of the cx24106 and 24110 automatically
searches all enabled viterbi rates, and can handle non-standard
rates as well. */
- if (fec>FEC_AUTO)
- fec=FEC_AUTO;
+ if (fec > FEC_AUTO)
+ fec = FEC_AUTO;
- if (fec==FEC_AUTO) { /* (re-)establish AutoAcq behaviour */
- cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)&0xdf);
+ if (fec == FEC_AUTO) { /* (re-)establish AutoAcq behaviour */
+ cx24110_writereg(state, 0x37, cx24110_readreg(state, 0x37) & 0xdf);
/* clear AcqVitDis bit */
- cx24110_writereg(state,0x18,0xae);
+ cx24110_writereg(state, 0x18, 0xae);
/* allow all DVB standard code rates */
- cx24110_writereg(state,0x05,(cx24110_readreg(state,0x05)&0xf0)|0x3);
+ cx24110_writereg(state, 0x05, (cx24110_readreg(state, 0x05) & 0xf0) | 0x3);
/* set nominal Viterbi rate 3/4 */
- cx24110_writereg(state,0x22,(cx24110_readreg(state,0x22)&0xf0)|0x3);
+ cx24110_writereg(state, 0x22, (cx24110_readreg(state, 0x22) & 0xf0) | 0x3);
/* set current Viterbi rate 3/4 */
- cx24110_writereg(state,0x1a,0x05); cx24110_writereg(state,0x1b,0x06);
+ cx24110_writereg(state, 0x1a, 0x05);
+ cx24110_writereg(state, 0x1b, 0x06);
/* set the puncture registers for code rate 3/4 */
return 0;
} else {
- cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)|0x20);
+ cx24110_writereg(state, 0x37, cx24110_readreg(state, 0x37) | 0x20);
/* set AcqVitDis bit */
- if(rate[fec]>0) {
- cx24110_writereg(state,0x05,(cx24110_readreg(state,0x05)&0xf0)|rate[fec]);
- /* set nominal Viterbi rate */
- cx24110_writereg(state,0x22,(cx24110_readreg(state,0x22)&0xf0)|rate[fec]);
- /* set current Viterbi rate */
- cx24110_writereg(state,0x1a,g1[fec]);
- cx24110_writereg(state,0x1b,g2[fec]);
- /* not sure if this is the right way: I always used AutoAcq mode */
- } else
- return -EOPNOTSUPP;
-/* fixme (low): which is the correct return code? */
+ if (rate[fec] < 0)
+ return -EINVAL;
+
+ cx24110_writereg(state, 0x05, (cx24110_readreg(state, 0x05) & 0xf0) | rate[fec]);
+ /* set nominal Viterbi rate */
+ cx24110_writereg(state, 0x22, (cx24110_readreg(state, 0x22) & 0xf0) | rate[fec]);
+ /* set current Viterbi rate */
+ cx24110_writereg(state, 0x1a, g1[fec]);
+ cx24110_writereg(state, 0x1b, g2[fec]);
+ /* not sure if this is the right way: I always used AutoAcq mode */
}
return 0;
}
diff --git a/drivers/media/dvb-frontends/cx24117.c b/drivers/media/dvb-frontends/cx24117.c
index a6c3c9e..acb965c 100644
--- a/drivers/media/dvb-frontends/cx24117.c
+++ b/drivers/media/dvb-frontends/cx24117.c
@@ -459,7 +459,7 @@
if (state->priv->skip_fw_load)
return 0;
- /* check if firmware if already running */
+ /* check if firmware is already running */
if (cx24117_readreg(state, 0xeb) != 0xa) {
/* Load firmware */
/* request the firmware, this will block until loaded */
diff --git a/drivers/media/dvb-frontends/dib7000p.c b/drivers/media/dvb-frontends/dib7000p.c
index 589134e..c505d69 100644
--- a/drivers/media/dvb-frontends/dib7000p.c
+++ b/drivers/media/dvb-frontends/dib7000p.c
@@ -1780,7 +1780,7 @@
}
/* FIXME: may require changes - this one was borrowed from dib8000 */
-static u32 dib7000p_get_time_us(struct dvb_frontend *demod, int layer)
+static u32 dib7000p_get_time_us(struct dvb_frontend *demod)
{
struct dtv_frontend_properties *c = &demod->dtv_property_cache;
u64 time_us, tmp64;
@@ -1881,7 +1881,6 @@
{
struct dib7000p_state *state = demod->demodulator_priv;
struct dtv_frontend_properties *c = &demod->dtv_property_cache;
- int i;
int show_per_stats = 0;
u32 time_us = 0, val, snr;
u64 blocks, ucb;
@@ -1935,7 +1934,7 @@
/* Estimate the number of packets based on bitrate */
if (!time_us)
- time_us = dib7000p_get_time_us(demod, -1);
+ time_us = dib7000p_get_time_us(demod);
if (time_us) {
blocks = 1250000ULL * 1000000ULL;
@@ -1949,7 +1948,7 @@
/* Get post-BER measures */
if (time_after(jiffies, state->ber_jiffies_stats)) {
- time_us = dib7000p_get_time_us(demod, -1);
+ time_us = dib7000p_get_time_us(demod);
state->ber_jiffies_stats = jiffies + msecs_to_jiffies((time_us + 500) / 1000);
dprintk("Next all layers stats available in %u us.", time_us);
@@ -1969,7 +1968,7 @@
c->block_error.stat[0].scale = FE_SCALE_COUNTER;
c->block_error.stat[0].uvalue += val;
- time_us = dib7000p_get_time_us(demod, i);
+ time_us = dib7000p_get_time_us(demod);
if (time_us) {
blocks = 1250000ULL * 1000000ULL;
do_div(blocks, time_us * 8 * 204);
diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c
index 5ec221f..2bfa7a4 100644
--- a/drivers/media/dvb-frontends/drx39xyj/drxj.c
+++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c
@@ -12255,8 +12255,7 @@
kfree(demod->my_ext_attr);
kfree(demod->my_common_attr);
kfree(demod->my_i2c_dev_addr);
- if (demod->firmware)
- release_firmware(demod->firmware);
+ release_firmware(demod->firmware);
kfree(demod);
kfree(state);
}
diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c
index 6721951..d46cf5f 100644
--- a/drivers/media/dvb-frontends/drxk_hard.c
+++ b/drivers/media/dvb-frontends/drxk_hard.c
@@ -166,9 +166,9 @@
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "enable debug messages");
-#define dprintk(level, fmt, arg...) do { \
-if (debug >= level) \
- pr_debug(fmt, ##arg); \
+#define dprintk(level, fmt, arg...) do { \
+if (debug >= level) \
+ printk(KERN_DEBUG KBUILD_MODNAME ": %s " fmt, __func__, ##arg); \
} while (0)
@@ -6310,8 +6310,7 @@
struct drxk_state *state = fe->demodulator_priv;
dprintk(1, "\n");
- if (state->fw)
- release_firmware(state->fw);
+ release_firmware(state->fw);
kfree(state);
}
diff --git a/drivers/media/dvb-frontends/ds3000.c b/drivers/media/dvb-frontends/ds3000.c
index 335daef..9d0d034 100644
--- a/drivers/media/dvb-frontends/ds3000.c
+++ b/drivers/media/dvb-frontends/ds3000.c
@@ -864,6 +864,13 @@
memcpy(&state->frontend.ops, &ds3000_ops,
sizeof(struct dvb_frontend_ops));
state->frontend.demodulator_priv = state;
+
+ /*
+ * Some devices like T480 starts with voltage on. Be sure
+ * to turn voltage off during init, as this can otherwise
+ * interfere with Unicable SCR systems.
+ */
+ ds3000_set_voltage(&state->frontend, SEC_VOLTAGE_OFF);
return &state->frontend;
error3:
diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c
index 81657e9..ba4ee0b 100644
--- a/drivers/media/dvb-frontends/m88ds3103.c
+++ b/drivers/media/dvb-frontends/m88ds3103.c
@@ -1,5 +1,5 @@
/*
- * Montage M88DS3103 demodulator driver
+ * Montage M88DS3103/M88RS6000 demodulator driver
*
* Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
*
@@ -162,7 +162,7 @@
dev_dbg(&priv->i2c->dev, "%s: tab_len=%d\n", __func__, tab_len);
- if (tab_len > 83) {
+ if (tab_len > 86) {
ret = -EINVAL;
goto err;
}
@@ -245,9 +245,9 @@
struct dtv_frontend_properties *c = &fe->dtv_property_cache;
int ret, len;
const struct m88ds3103_reg_val *init;
- u8 u8tmp, u8tmp1, u8tmp2;
- u8 buf[2];
- u16 u16tmp, divide_ratio;
+ u8 u8tmp, u8tmp1 = 0, u8tmp2 = 0; /* silence compiler warning */
+ u8 buf[3];
+ u16 u16tmp, divide_ratio = 0;
u32 tuner_frequency, target_mclk;
s32 s32tmp;
@@ -262,6 +262,22 @@
goto err;
}
+ /* reset */
+ ret = m88ds3103_wr_reg(priv, 0x07, 0x80);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_wr_reg(priv, 0x07, 0x00);
+ if (ret)
+ goto err;
+
+ /* Disable demod clock path */
+ if (priv->chip_id == M88RS6000_CHIP_ID) {
+ ret = m88ds3103_wr_reg(priv, 0x06, 0xe0);
+ if (ret)
+ goto err;
+ }
+
/* program tuner */
if (fe->ops.tuner_ops.set_params) {
ret = fe->ops.tuner_ops.set_params(fe);
@@ -282,14 +298,73 @@
tuner_frequency = c->frequency;
}
- /* reset */
- ret = m88ds3103_wr_reg(priv, 0x07, 0x80);
- if (ret)
- goto err;
+ /* select M88RS6000 demod main mclk and ts mclk from tuner die. */
+ if (priv->chip_id == M88RS6000_CHIP_ID) {
+ if (c->symbol_rate > 45010000)
+ priv->mclk_khz = 110250;
+ else
+ priv->mclk_khz = 96000;
- ret = m88ds3103_wr_reg(priv, 0x07, 0x00);
- if (ret)
- goto err;
+ if (c->delivery_system == SYS_DVBS)
+ target_mclk = 96000;
+ else
+ target_mclk = 144000;
+
+ /* Enable demod clock path */
+ ret = m88ds3103_wr_reg(priv, 0x06, 0x00);
+ if (ret)
+ goto err;
+ usleep_range(10000, 20000);
+ } else {
+ /* set M88DS3103 mclk and ts mclk. */
+ priv->mclk_khz = 96000;
+
+ switch (priv->cfg->ts_mode) {
+ case M88DS3103_TS_SERIAL:
+ case M88DS3103_TS_SERIAL_D7:
+ target_mclk = priv->cfg->ts_clk;
+ break;
+ case M88DS3103_TS_PARALLEL:
+ case M88DS3103_TS_CI:
+ if (c->delivery_system == SYS_DVBS)
+ target_mclk = 96000;
+ else {
+ if (c->symbol_rate < 18000000)
+ target_mclk = 96000;
+ else if (c->symbol_rate < 28000000)
+ target_mclk = 144000;
+ else
+ target_mclk = 192000;
+ }
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s: invalid ts_mode\n",
+ __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ switch (target_mclk) {
+ case 96000:
+ u8tmp1 = 0x02; /* 0b10 */
+ u8tmp2 = 0x01; /* 0b01 */
+ break;
+ case 144000:
+ u8tmp1 = 0x00; /* 0b00 */
+ u8tmp2 = 0x01; /* 0b01 */
+ break;
+ case 192000:
+ u8tmp1 = 0x03; /* 0b11 */
+ u8tmp2 = 0x00; /* 0b00 */
+ break;
+ }
+ ret = m88ds3103_wr_reg_mask(priv, 0x22, u8tmp1 << 6, 0xc0);
+ if (ret)
+ goto err;
+ ret = m88ds3103_wr_reg_mask(priv, 0x24, u8tmp2 << 6, 0xc0);
+ if (ret)
+ goto err;
+ }
ret = m88ds3103_wr_reg(priv, 0xb2, 0x01);
if (ret)
@@ -301,36 +376,21 @@
switch (c->delivery_system) {
case SYS_DVBS:
- len = ARRAY_SIZE(m88ds3103_dvbs_init_reg_vals);
- init = m88ds3103_dvbs_init_reg_vals;
- target_mclk = 96000;
+ if (priv->chip_id == M88RS6000_CHIP_ID) {
+ len = ARRAY_SIZE(m88rs6000_dvbs_init_reg_vals);
+ init = m88rs6000_dvbs_init_reg_vals;
+ } else {
+ len = ARRAY_SIZE(m88ds3103_dvbs_init_reg_vals);
+ init = m88ds3103_dvbs_init_reg_vals;
+ }
break;
case SYS_DVBS2:
- len = ARRAY_SIZE(m88ds3103_dvbs2_init_reg_vals);
- init = m88ds3103_dvbs2_init_reg_vals;
-
- switch (priv->cfg->ts_mode) {
- case M88DS3103_TS_SERIAL:
- case M88DS3103_TS_SERIAL_D7:
- if (c->symbol_rate < 18000000)
- target_mclk = 96000;
- else
- target_mclk = 144000;
- break;
- case M88DS3103_TS_PARALLEL:
- case M88DS3103_TS_CI:
- if (c->symbol_rate < 18000000)
- target_mclk = 96000;
- else if (c->symbol_rate < 28000000)
- target_mclk = 144000;
- else
- target_mclk = 192000;
- break;
- default:
- dev_dbg(&priv->i2c->dev, "%s: invalid ts_mode\n",
- __func__);
- ret = -EINVAL;
- goto err;
+ if (priv->chip_id == M88RS6000_CHIP_ID) {
+ len = ARRAY_SIZE(m88rs6000_dvbs2_init_reg_vals);
+ init = m88rs6000_dvbs2_init_reg_vals;
+ } else {
+ len = ARRAY_SIZE(m88ds3103_dvbs2_init_reg_vals);
+ init = m88ds3103_dvbs2_init_reg_vals;
}
break;
default:
@@ -347,7 +407,30 @@
goto err;
}
- u8tmp1 = 0; /* silence compiler warning */
+ if (priv->chip_id == M88RS6000_CHIP_ID) {
+ if ((c->delivery_system == SYS_DVBS2)
+ && ((c->symbol_rate / 1000) <= 5000)) {
+ ret = m88ds3103_wr_reg(priv, 0xc0, 0x04);
+ if (ret)
+ goto err;
+ buf[0] = 0x09;
+ buf[1] = 0x22;
+ buf[2] = 0x88;
+ ret = m88ds3103_wr_regs(priv, 0x8a, buf, 3);
+ if (ret)
+ goto err;
+ }
+ ret = m88ds3103_wr_reg_mask(priv, 0x9d, 0x08, 0x08);
+ if (ret)
+ goto err;
+ ret = m88ds3103_wr_reg(priv, 0xf1, 0x01);
+ if (ret)
+ goto err;
+ ret = m88ds3103_wr_reg_mask(priv, 0x30, 0x80, 0x80);
+ if (ret)
+ goto err;
+ }
+
switch (priv->cfg->ts_mode) {
case M88DS3103_TS_SERIAL:
u8tmp1 = 0x00;
@@ -383,16 +466,15 @@
ret = m88ds3103_wr_reg_mask(priv, 0x29, u8tmp1, 0x20);
if (ret)
goto err;
- }
-
- if (priv->cfg->ts_clk) {
- divide_ratio = DIV_ROUND_UP(target_mclk, priv->cfg->ts_clk);
- u8tmp1 = divide_ratio / 2;
- u8tmp2 = DIV_ROUND_UP(divide_ratio, 2);
- } else {
- divide_ratio = 0;
u8tmp1 = 0;
u8tmp2 = 0;
+ break;
+ default:
+ if (priv->cfg->ts_clk) {
+ divide_ratio = DIV_ROUND_UP(target_mclk, priv->cfg->ts_clk);
+ u8tmp1 = divide_ratio / 2;
+ u8tmp2 = DIV_ROUND_UP(divide_ratio, 2);
+ }
}
dev_dbg(&priv->i2c->dev,
@@ -420,29 +502,6 @@
if (ret)
goto err;
- switch (target_mclk) {
- case 96000:
- u8tmp1 = 0x02; /* 0b10 */
- u8tmp2 = 0x01; /* 0b01 */
- break;
- case 144000:
- u8tmp1 = 0x00; /* 0b00 */
- u8tmp2 = 0x01; /* 0b01 */
- break;
- case 192000:
- u8tmp1 = 0x03; /* 0b11 */
- u8tmp2 = 0x00; /* 0b00 */
- break;
- }
-
- ret = m88ds3103_wr_reg_mask(priv, 0x22, u8tmp1 << 6, 0xc0);
- if (ret)
- goto err;
-
- ret = m88ds3103_wr_reg_mask(priv, 0x24, u8tmp2 << 6, 0xc0);
- if (ret)
- goto err;
-
if (c->symbol_rate <= 3000000)
u8tmp = 0x20;
else if (c->symbol_rate <= 10000000)
@@ -466,7 +525,7 @@
if (ret)
goto err;
- u16tmp = DIV_ROUND_CLOSEST((c->symbol_rate / 1000) << 15, M88DS3103_MCLK_KHZ / 2);
+ u16tmp = DIV_ROUND_CLOSEST((c->symbol_rate / 1000) << 15, priv->mclk_khz / 2);
buf[0] = (u16tmp >> 0) & 0xff;
buf[1] = (u16tmp >> 8) & 0xff;
ret = m88ds3103_wr_regs(priv, 0x61, buf, 2);
@@ -489,7 +548,7 @@
(tuner_frequency - c->frequency));
s32tmp = 0x10000 * (tuner_frequency - c->frequency);
- s32tmp = DIV_ROUND_CLOSEST(s32tmp, M88DS3103_MCLK_KHZ);
+ s32tmp = DIV_ROUND_CLOSEST(s32tmp, priv->mclk_khz);
if (s32tmp < 0)
s32tmp += 0x10000;
@@ -520,7 +579,7 @@
struct m88ds3103_priv *priv = fe->demodulator_priv;
int ret, len, remaining;
const struct firmware *fw = NULL;
- u8 *fw_file = M88DS3103_FIRMWARE;
+ u8 *fw_file;
u8 u8tmp;
dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
@@ -541,15 +600,6 @@
if (ret)
goto err;
- /* reset */
- ret = m88ds3103_wr_reg(priv, 0x07, 0x60);
- if (ret)
- goto err;
-
- ret = m88ds3103_wr_reg(priv, 0x07, 0x00);
- if (ret)
- goto err;
-
/* firmware status */
ret = m88ds3103_rd_reg(priv, 0xb9, &u8tmp);
if (ret)
@@ -560,10 +610,23 @@
if (u8tmp)
goto skip_fw_download;
+ /* global reset, global diseqc reset, golbal fec reset */
+ ret = m88ds3103_wr_reg(priv, 0x07, 0xe0);
+ if (ret)
+ goto err;
+
+ ret = m88ds3103_wr_reg(priv, 0x07, 0x00);
+ if (ret)
+ goto err;
+
/* cold state - try to download firmware */
dev_info(&priv->i2c->dev, "%s: found a '%s' in cold state\n",
KBUILD_MODNAME, m88ds3103_ops.info.name);
+ if (priv->chip_id == M88RS6000_CHIP_ID)
+ fw_file = M88RS6000_FIRMWARE;
+ else
+ fw_file = M88DS3103_FIRMWARE;
/* request the firmware, this will block and timeout */
ret = request_firmware(&fw, fw_file, priv->i2c->dev.parent);
if (ret) {
@@ -577,7 +640,7 @@
ret = m88ds3103_wr_reg(priv, 0xb2, 0x01);
if (ret)
- goto err;
+ goto error_fw_release;
for (remaining = fw->size; remaining > 0;
remaining -= (priv->cfg->i2c_wr_max - 1)) {
@@ -591,13 +654,13 @@
dev_err(&priv->i2c->dev,
"%s: firmware download failed=%d\n",
KBUILD_MODNAME, ret);
- goto err;
+ goto error_fw_release;
}
}
ret = m88ds3103_wr_reg(priv, 0xb2, 0x00);
if (ret)
- goto err;
+ goto error_fw_release;
release_firmware(fw);
fw = NULL;
@@ -623,10 +686,10 @@
priv->warm = true;
return 0;
-err:
- if (fw)
- release_firmware(fw);
+error_fw_release:
+ release_firmware(fw);
+err:
dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
return ret;
}
@@ -635,13 +698,18 @@
{
struct m88ds3103_priv *priv = fe->demodulator_priv;
int ret;
+ u8 u8tmp;
dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
priv->delivery_system = SYS_UNDEFINED;
/* TS Hi-Z */
- ret = m88ds3103_wr_reg_mask(priv, 0x27, 0x00, 0x01);
+ if (priv->chip_id == M88RS6000_CHIP_ID)
+ u8tmp = 0x29;
+ else
+ u8tmp = 0x27;
+ ret = m88ds3103_wr_reg_mask(priv, u8tmp, 0x00, 0x01);
if (ret)
goto err;
@@ -830,7 +898,7 @@
goto err;
c->symbol_rate = 1ull * ((buf[1] << 8) | (buf[0] << 0)) *
- M88DS3103_MCLK_KHZ * 1000 / 0x10000;
+ priv->mclk_khz * 1000 / 0x10000;
return 0;
err:
@@ -1310,18 +1378,22 @@
priv->i2c = i2c;
mutex_init(&priv->i2c_mutex);
- ret = m88ds3103_rd_reg(priv, 0x01, &chip_id);
+ /* 0x00: chip id[6:0], 0x01: chip ver[7:0], 0x02: chip ver[15:8] */
+ ret = m88ds3103_rd_reg(priv, 0x00, &chip_id);
if (ret)
goto err;
- dev_dbg(&priv->i2c->dev, "%s: chip_id=%02x\n", __func__, chip_id);
+ chip_id >>= 1;
+ dev_info(&priv->i2c->dev, "%s: chip_id=%02x\n", __func__, chip_id);
switch (chip_id) {
- case 0xd0:
+ case M88RS6000_CHIP_ID:
+ case M88DS3103_CHIP_ID:
break;
default:
goto err;
}
+ priv->chip_id = chip_id;
switch (priv->cfg->clock_out) {
case M88DS3103_CLOCK_OUT_DISABLED:
@@ -1337,6 +1409,11 @@
goto err;
}
+ /* 0x29 register is defined differently for m88rs6000. */
+ /* set internal tuner address to 0x21 */
+ if (chip_id == M88RS6000_CHIP_ID)
+ u8tmp = 0x00;
+
ret = m88ds3103_wr_reg(priv, 0x29, u8tmp);
if (ret)
goto err;
@@ -1364,6 +1441,9 @@
/* create dvb_frontend */
memcpy(&priv->fe.ops, &m88ds3103_ops, sizeof(struct dvb_frontend_ops));
+ if (priv->chip_id == M88RS6000_CHIP_ID)
+ strncpy(priv->fe.ops.info.name,
+ "Montage M88RS6000", sizeof(priv->fe.ops.info.name));
priv->fe.demodulator_priv = priv;
return &priv->fe;
@@ -1423,3 +1503,4 @@
MODULE_DESCRIPTION("Montage M88DS3103 DVB-S/S2 demodulator driver");
MODULE_LICENSE("GPL");
MODULE_FIRMWARE(M88DS3103_FIRMWARE);
+MODULE_FIRMWARE(M88RS6000_FIRMWARE);
diff --git a/drivers/media/dvb-frontends/m88ds3103_priv.h b/drivers/media/dvb-frontends/m88ds3103_priv.h
index 9169fdd..a2c0958 100644
--- a/drivers/media/dvb-frontends/m88ds3103_priv.h
+++ b/drivers/media/dvb-frontends/m88ds3103_priv.h
@@ -25,7 +25,10 @@
#include <linux/math64.h>
#define M88DS3103_FIRMWARE "dvb-demod-m88ds3103.fw"
+#define M88RS6000_FIRMWARE "dvb-demod-m88rs6000.fw"
#define M88DS3103_MCLK_KHZ 96000
+#define M88RS6000_CHIP_ID 0x74
+#define M88DS3103_CHIP_ID 0x70
struct m88ds3103_priv {
struct i2c_adapter *i2c;
@@ -38,6 +41,10 @@
u32 ber;
bool warm; /* FW running */
struct i2c_adapter *i2c_adapter;
+ /* auto detect chip id to do different config */
+ u8 chip_id;
+ /* main mclk is calculated for M88RS6000 dynamically */
+ u32 mclk_khz;
};
struct m88ds3103_reg_val {
@@ -214,4 +221,178 @@
{0xb8, 0x00},
};
+static const struct m88ds3103_reg_val m88rs6000_dvbs_init_reg_vals[] = {
+ {0x23, 0x07},
+ {0x08, 0x03},
+ {0x0c, 0x02},
+ {0x20, 0x00},
+ {0x21, 0x54},
+ {0x25, 0x82},
+ {0x27, 0x31},
+ {0x30, 0x08},
+ {0x31, 0x40},
+ {0x32, 0x32},
+ {0x33, 0x35},
+ {0x35, 0xff},
+ {0x3a, 0x00},
+ {0x37, 0x10},
+ {0x38, 0x10},
+ {0x39, 0x02},
+ {0x42, 0x60},
+ {0x4a, 0x80},
+ {0x4b, 0x04},
+ {0x4d, 0x91},
+ {0x5d, 0xc8},
+ {0x50, 0x36},
+ {0x51, 0x36},
+ {0x52, 0x36},
+ {0x53, 0x36},
+ {0x63, 0x0f},
+ {0x64, 0x30},
+ {0x65, 0x40},
+ {0x68, 0x26},
+ {0x69, 0x4c},
+ {0x70, 0x20},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x40},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x60},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x80},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0xa0},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x1f},
+ {0x76, 0x38},
+ {0x77, 0xa6},
+ {0x78, 0x0c},
+ {0x79, 0x80},
+ {0x7f, 0x14},
+ {0x7c, 0x00},
+ {0xae, 0x82},
+ {0x80, 0x64},
+ {0x81, 0x66},
+ {0x82, 0x44},
+ {0x85, 0x04},
+ {0xcd, 0xf4},
+ {0x90, 0x33},
+ {0xa0, 0x44},
+ {0xbe, 0x00},
+ {0xc0, 0x08},
+ {0xc3, 0x10},
+ {0xc4, 0x08},
+ {0xc5, 0xf0},
+ {0xc6, 0xff},
+ {0xc7, 0x00},
+ {0xc8, 0x1a},
+ {0xc9, 0x80},
+ {0xe0, 0xf8},
+ {0xe6, 0x8b},
+ {0xd0, 0x40},
+ {0xf8, 0x20},
+ {0xfa, 0x0f},
+ {0x00, 0x00},
+ {0xbd, 0x01},
+ {0xb8, 0x00},
+ {0x29, 0x11},
+};
+
+static const struct m88ds3103_reg_val m88rs6000_dvbs2_init_reg_vals[] = {
+ {0x23, 0x07},
+ {0x08, 0x07},
+ {0x0c, 0x02},
+ {0x20, 0x00},
+ {0x21, 0x54},
+ {0x25, 0x82},
+ {0x27, 0x31},
+ {0x30, 0x08},
+ {0x32, 0x32},
+ {0x33, 0x35},
+ {0x35, 0xff},
+ {0x3a, 0x00},
+ {0x37, 0x10},
+ {0x38, 0x10},
+ {0x39, 0x02},
+ {0x42, 0x60},
+ {0x4a, 0x80},
+ {0x4b, 0x04},
+ {0x4d, 0x91},
+ {0x5d, 0xc8},
+ {0x50, 0x36},
+ {0x51, 0x36},
+ {0x52, 0x36},
+ {0x53, 0x36},
+ {0x63, 0x0f},
+ {0x64, 0x10},
+ {0x65, 0x20},
+ {0x68, 0x46},
+ {0x69, 0xcd},
+ {0x70, 0x20},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x40},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x60},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x80},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0xa0},
+ {0x71, 0x70},
+ {0x72, 0x04},
+ {0x73, 0x00},
+ {0x70, 0x1f},
+ {0x76, 0x38},
+ {0x77, 0xa6},
+ {0x78, 0x0c},
+ {0x79, 0x80},
+ {0x7f, 0x14},
+ {0x85, 0x08},
+ {0xcd, 0xf4},
+ {0x90, 0x33},
+ {0x86, 0x00},
+ {0x87, 0x0f},
+ {0x89, 0x00},
+ {0x8b, 0x44},
+ {0x8c, 0x66},
+ {0x9d, 0xc1},
+ {0x8a, 0x10},
+ {0xad, 0x40},
+ {0xa0, 0x44},
+ {0xbe, 0x00},
+ {0xc0, 0x08},
+ {0xc1, 0x10},
+ {0xc2, 0x08},
+ {0xc3, 0x10},
+ {0xc4, 0x08},
+ {0xc5, 0xf0},
+ {0xc6, 0xff},
+ {0xc7, 0x00},
+ {0xc8, 0x1a},
+ {0xc9, 0x80},
+ {0xca, 0x23},
+ {0xcb, 0x24},
+ {0xcc, 0xf4},
+ {0xce, 0x74},
+ {0x00, 0x00},
+ {0xbd, 0x01},
+ {0xb8, 0x00},
+ {0x29, 0x01},
+};
#endif
diff --git a/drivers/media/dvb-frontends/mn88472.h b/drivers/media/dvb-frontends/mn88472.h
new file mode 100644
index 0000000..da4558b
--- /dev/null
+++ b/drivers/media/dvb-frontends/mn88472.h
@@ -0,0 +1,38 @@
+/*
+ * Panasonic MN88472 DVB-T/T2/C demodulator driver
+ *
+ * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
+ *
+ * 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.
+ */
+
+#ifndef MN88472_H
+#define MN88472_H
+
+#include <linux/dvb/frontend.h>
+
+struct mn88472_config {
+ /*
+ * Max num of bytes given I2C adapter could write at once.
+ * Default: none
+ */
+ u16 i2c_wr_max;
+
+
+ /* Everything after that is returned by the driver. */
+
+ /*
+ * DVB frontend.
+ */
+ struct dvb_frontend **fe;
+};
+
+#endif
diff --git a/drivers/media/dvb-frontends/mn88473.h b/drivers/media/dvb-frontends/mn88473.h
new file mode 100644
index 0000000..a373ec9
--- /dev/null
+++ b/drivers/media/dvb-frontends/mn88473.h
@@ -0,0 +1,38 @@
+/*
+ * Panasonic MN88473 DVB-T/T2/C demodulator driver
+ *
+ * Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
+ *
+ * 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.
+ */
+
+#ifndef MN88473_H
+#define MN88473_H
+
+#include <linux/dvb/frontend.h>
+
+struct mn88473_config {
+ /*
+ * Max num of bytes given I2C adapter could write at once.
+ * Default: none
+ */
+ u16 i2c_wr_max;
+
+
+ /* Everything after that is returned by the driver. */
+
+ /*
+ * DVB frontend.
+ */
+ struct dvb_frontend **fe;
+};
+
+#endif
diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c
index eb737cf..9026e1a 100644
--- a/drivers/media/dvb-frontends/rtl2832.c
+++ b/drivers/media/dvb-frontends/rtl2832.c
@@ -258,13 +258,11 @@
return rtl2832_rd(priv, reg, val, len);
}
-#if 0 /* currently not used */
/* write single register */
static int rtl2832_wr_reg(struct rtl2832_priv *priv, u8 reg, u8 page, u8 val)
{
return rtl2832_wr_regs(priv, reg, page, &val, 1);
}
-#endif
/* read single register */
static int rtl2832_rd_reg(struct rtl2832_priv *priv, u8 reg, u8 page, u8 *val)
@@ -599,6 +597,11 @@
if (fe->ops.tuner_ops.set_params)
fe->ops.tuner_ops.set_params(fe);
+ /* PIP mode related */
+ ret = rtl2832_wr_regs(priv, 0x92, 1, "\x00\x0f\xff", 3);
+ if (ret)
+ goto err;
+
/* If the frontend has get_if_frequency(), use it */
if (fe->ops.tuner_ops.get_if_frequency) {
u32 if_freq;
@@ -661,7 +664,6 @@
if (ret)
goto err;
-
/* soft reset */
ret = rtl2832_wr_demod_reg(priv, DVBT_SOFT_RST, 0x1);
if (ret)
@@ -1020,6 +1022,58 @@
return 0;
}
+int rtl2832_enable_external_ts_if(struct dvb_frontend *fe)
+{
+ struct rtl2832_priv *priv = fe->demodulator_priv;
+ int ret;
+
+ dev_dbg(&priv->i2c->dev, "%s: setting PIP mode\n", __func__);
+
+ ret = rtl2832_wr_regs(priv, 0x0c, 1, "\x5f\xff", 2);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_wr_demod_reg(priv, DVBT_PIP_ON, 0x1);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_wr_reg(priv, 0xbc, 0, 0x18);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_wr_reg(priv, 0x22, 0, 0x01);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_wr_reg(priv, 0x26, 0, 0x1f);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_wr_reg(priv, 0x27, 0, 0xff);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_wr_regs(priv, 0x92, 1, "\x7f\xf7\xff", 3);
+ if (ret)
+ goto err;
+
+ /* soft reset */
+ ret = rtl2832_wr_demod_reg(priv, DVBT_SOFT_RST, 0x1);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_wr_demod_reg(priv, DVBT_SOFT_RST, 0x0);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+
+}
+EXPORT_SYMBOL(rtl2832_enable_external_ts_if);
+
struct i2c_adapter *rtl2832_get_i2c_adapter(struct dvb_frontend *fe)
{
struct rtl2832_priv *priv = fe->demodulator_priv;
diff --git a/drivers/media/dvb-frontends/rtl2832.h b/drivers/media/dvb-frontends/rtl2832.h
index f32b09c..5e72630 100644
--- a/drivers/media/dvb-frontends/rtl2832.h
+++ b/drivers/media/dvb-frontends/rtl2832.h
@@ -64,6 +64,10 @@
struct dvb_frontend *fe
);
+extern int rtl2832_enable_external_ts_if(
+ struct dvb_frontend *fe
+);
+
#else
static inline struct dvb_frontend *rtl2832_attach(
@@ -89,6 +93,13 @@
return NULL;
}
+static inline int rtl2832_enable_external_ts_if(
+ struct dvb_frontend *fe
+)
+{
+ return -ENODEV;
+}
+
#endif
diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c
index 7bf98cf..2896b47 100644
--- a/drivers/media/dvb-frontends/rtl2832_sdr.c
+++ b/drivers/media/dvb-frontends/rtl2832_sdr.c
@@ -1013,6 +1013,10 @@
if (s->d->props->power_ctrl)
s->d->props->power_ctrl(s->d, 1);
+ /* enable ADC */
+ if (s->d->props->frontend_ctrl)
+ s->d->props->frontend_ctrl(s->fe, 1);
+
set_bit(POWER_ON, &s->flags);
ret = rtl2832_sdr_set_tuner(s);
@@ -1064,6 +1068,10 @@
clear_bit(POWER_ON, &s->flags);
+ /* disable ADC */
+ if (s->d->props->frontend_ctrl)
+ s->d->props->frontend_ctrl(s->fe, 0);
+
if (s->d->props->power_ctrl)
s->d->props->power_ctrl(s->d, 0);
diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c
index 1cd93be..ce9ab44 100644
--- a/drivers/media/dvb-frontends/si2168.c
+++ b/drivers/media/dvb-frontends/si2168.c
@@ -308,14 +308,16 @@
if (ret)
goto err;
- memcpy(cmd.args, "\x14\x00\x09\x10\xe3\x18", 6);
+ memcpy(cmd.args, "\x14\x00\x09\x10\xe3\x08", 6);
+ cmd.args[5] |= s->ts_clock_inv ? 0x00 : 0x10;
cmd.wlen = 6;
cmd.rlen = 4;
ret = si2168_cmd_execute(s, &cmd);
if (ret)
goto err;
- memcpy(cmd.args, "\x14\x00\x08\x10\xd7\x15", 6);
+ memcpy(cmd.args, "\x14\x00\x08\x10\xd7\x05", 6);
+ cmd.args[5] |= s->ts_clock_inv ? 0x00 : 0x10;
cmd.wlen = 6;
cmd.rlen = 4;
ret = si2168_cmd_execute(s, &cmd);
@@ -453,27 +455,45 @@
dev_err(&s->client->dev,
"firmware file '%s' not found\n",
fw_file);
- goto err;
+ goto error_fw_release;
}
}
dev_info(&s->client->dev, "downloading firmware from file '%s'\n",
fw_file);
- for (remaining = fw->size; remaining > 0; remaining -= i2c_wr_max) {
- len = remaining;
- if (len > i2c_wr_max)
- len = i2c_wr_max;
+ if ((fw->size % 17 == 0) && (fw->data[0] > 5)) {
+ /* firmware is in the new format */
+ for (remaining = fw->size; remaining > 0; remaining -= 17) {
+ len = fw->data[fw->size - remaining];
+ memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len);
+ cmd.wlen = len;
+ cmd.rlen = 1;
+ ret = si2168_cmd_execute(s, &cmd);
+ if (ret) {
+ dev_err(&s->client->dev,
+ "firmware download failed=%d\n",
+ ret);
+ goto error_fw_release;
+ }
+ }
+ } else {
+ /* firmware is in the old format */
+ for (remaining = fw->size; remaining > 0; remaining -= i2c_wr_max) {
+ len = remaining;
+ if (len > i2c_wr_max)
+ len = i2c_wr_max;
- memcpy(cmd.args, &fw->data[fw->size - remaining], len);
- cmd.wlen = len;
- cmd.rlen = 1;
- ret = si2168_cmd_execute(s, &cmd);
- if (ret) {
- dev_err(&s->client->dev,
- "firmware download failed=%d\n",
- ret);
- goto err;
+ memcpy(cmd.args, &fw->data[fw->size - remaining], len);
+ cmd.wlen = len;
+ cmd.rlen = 1;
+ ret = si2168_cmd_execute(s, &cmd);
+ if (ret) {
+ dev_err(&s->client->dev,
+ "firmware download failed=%d\n",
+ ret);
+ goto error_fw_release;
+ }
}
}
@@ -487,6 +507,17 @@
if (ret)
goto err;
+ /* query firmware version */
+ memcpy(cmd.args, "\x11", 1);
+ cmd.wlen = 1;
+ cmd.rlen = 10;
+ ret = si2168_cmd_execute(s, &cmd);
+ if (ret)
+ goto err;
+
+ dev_dbg(&s->client->dev, "firmware version: %c.%c.%d\n",
+ cmd.args[6], cmd.args[7], cmd.args[8]);
+
/* set ts mode */
memcpy(cmd.args, "\x14\x00\x01\x10\x10\x00", 6);
cmd.args[4] |= s->ts_mode;
@@ -498,17 +529,16 @@
s->fw_loaded = true;
-warm:
dev_info(&s->client->dev, "found a '%s' in warm state\n",
si2168_ops.info.name);
-
+warm:
s->active = true;
return 0;
-err:
- if (fw)
- release_firmware(fw);
+error_fw_release:
+ release_firmware(fw);
+err:
dev_dbg(&s->client->dev, "failed=%d\n", ret);
return ret;
}
@@ -670,6 +700,7 @@
*config->i2c_adapter = s->adapter;
*config->fe = &s->fe;
s->ts_mode = config->ts_mode;
+ s->ts_clock_inv = config->ts_clock_inv;
s->fw_loaded = false;
i2c_set_clientdata(client, s);
diff --git a/drivers/media/dvb-frontends/si2168.h b/drivers/media/dvb-frontends/si2168.h
index e086d67..87bc121 100644
--- a/drivers/media/dvb-frontends/si2168.h
+++ b/drivers/media/dvb-frontends/si2168.h
@@ -37,6 +37,10 @@
/* TS mode */
u8 ts_mode;
+
+ /* TS clock inverted */
+ bool ts_clock_inv;
+
};
#define SI2168_TS_PARALLEL 0x06
diff --git a/drivers/media/dvb-frontends/si2168_priv.h b/drivers/media/dvb-frontends/si2168_priv.h
index e13983e..60bc334 100644
--- a/drivers/media/dvb-frontends/si2168_priv.h
+++ b/drivers/media/dvb-frontends/si2168_priv.h
@@ -38,6 +38,7 @@
bool active;
bool fw_loaded;
u8 ts_mode;
+ bool ts_clock_inv;
};
/* firmare command struct */
diff --git a/drivers/media/dvb-frontends/sp2.c b/drivers/media/dvb-frontends/sp2.c
index 9b684d5..cc1ef96 100644
--- a/drivers/media/dvb-frontends/sp2.c
+++ b/drivers/media/dvb-frontends/sp2.c
@@ -92,6 +92,9 @@
return -EIO;
}
+ dev_dbg(&s->client->dev, "addr=0x%04x, reg = 0x%02x, data = %*ph\n",
+ client->addr, reg, len, buf);
+
return 0;
}
@@ -103,9 +106,6 @@
int mem, ret;
int (*ci_op_cam)(void*, u8, int, u8, int*) = s->ci_control;
- dev_dbg(&s->client->dev, "slot=%d, acs=0x%02x, addr=0x%04x, data = 0x%02x",
- slot, acs, addr, data);
-
if (slot != 0)
return -EINVAL;
@@ -140,13 +140,16 @@
if (ret)
return ret;
- if (read) {
- dev_dbg(&s->client->dev, "cam read, addr=0x%04x, data = 0x%04x",
- addr, mem);
+ dev_dbg(&s->client->dev, "%s: slot=%d, addr=0x%04x, %s, data=%x",
+ (read) ? "read" : "write", slot, addr,
+ (acs == SP2_CI_ATTR_ACS) ? "attr" : "io",
+ (read) ? mem : data);
+
+ if (read)
return mem;
- } else {
+ else
return 0;
- }
+
}
int sp2_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221,
@@ -266,7 +269,7 @@
return s->status;
}
-int sp2_init(struct sp2 *s)
+static int sp2_init(struct sp2 *s)
{
int ret = 0;
u8 buf;
@@ -348,7 +351,7 @@
return ret;
}
-int sp2_exit(struct i2c_client *client)
+static int sp2_exit(struct i2c_client *client)
{
struct sp2 *s;
@@ -407,7 +410,7 @@
static int sp2_remove(struct i2c_client *client)
{
- struct si2157 *s = i2c_get_clientdata(client);
+ struct sp2 *s = i2c_get_clientdata(client);
dev_dbg(&client->dev, "\n");
diff --git a/drivers/media/dvb-frontends/stb0899_drv.c b/drivers/media/dvb-frontends/stb0899_drv.c
index 07cd5ea..19646fb 100644
--- a/drivers/media/dvb-frontends/stb0899_drv.c
+++ b/drivers/media/dvb-frontends/stb0899_drv.c
@@ -705,7 +705,7 @@
struct stb0899_state *state = fe->demodulator_priv;
u8 reg, i;
- if (cmd->msg_len > 8)
+ if (cmd->msg_len > sizeof(cmd->msg))
return -EINVAL;
/* enable FIFO precharge */
diff --git a/drivers/media/dvb-frontends/stv090x.c b/drivers/media/dvb-frontends/stv090x.c
index 23e872f..0b2a934 100644
--- a/drivers/media/dvb-frontends/stv090x.c
+++ b/drivers/media/dvb-frontends/stv090x.c
@@ -2146,7 +2146,7 @@
u32 reg;
s32 car_step, steps, cur_step, dir, freq, timeout_lock;
- int lock = 0;
+ int lock;
if (state->srate >= 10000000)
timeout_lock = timeout_dmd / 3;
@@ -2154,98 +2154,96 @@
timeout_lock = timeout_dmd / 2;
lock = stv090x_get_dmdlock(state, timeout_lock); /* cold start wait */
- if (!lock) {
- if (state->srate >= 10000000) {
- if (stv090x_chk_tmg(state)) {
- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x15) < 0)
- goto err;
- lock = stv090x_get_dmdlock(state, timeout_dmd);
- } else {
- lock = 0;
- }
- } else {
- if (state->srate <= 4000000)
- car_step = 1000;
- else if (state->srate <= 7000000)
- car_step = 2000;
- else if (state->srate <= 10000000)
- car_step = 3000;
- else
- car_step = 5000;
+ if (lock)
+ return lock;
- steps = (state->search_range / 1000) / car_step;
- steps /= 2;
- steps = 2 * (steps + 1);
- if (steps < 0)
- steps = 2;
- else if (steps > 12)
- steps = 12;
-
- cur_step = 1;
- dir = 1;
-
- if (!lock) {
- freq = state->frequency;
- state->tuner_bw = stv090x_car_width(state->srate, state->rolloff) + state->srate;
- while ((cur_step <= steps) && (!lock)) {
- if (dir > 0)
- freq += cur_step * car_step;
- else
- freq -= cur_step * car_step;
-
- /* Setup tuner */
- if (stv090x_i2c_gate_ctrl(state, 1) < 0)
- goto err;
-
- if (state->config->tuner_set_frequency) {
- if (state->config->tuner_set_frequency(fe, freq) < 0)
- goto err_gateoff;
- }
-
- if (state->config->tuner_set_bandwidth) {
- if (state->config->tuner_set_bandwidth(fe, state->tuner_bw) < 0)
- goto err_gateoff;
- }
-
- if (stv090x_i2c_gate_ctrl(state, 0) < 0)
- goto err;
-
- msleep(50);
-
- if (stv090x_i2c_gate_ctrl(state, 1) < 0)
- goto err;
-
- if (state->config->tuner_get_status) {
- if (state->config->tuner_get_status(fe, ®) < 0)
- goto err_gateoff;
- }
-
- if (reg)
- dprintk(FE_DEBUG, 1, "Tuner phase locked");
- else
- dprintk(FE_DEBUG, 1, "Tuner unlocked");
-
- if (stv090x_i2c_gate_ctrl(state, 0) < 0)
- goto err;
-
- STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1c);
- if (STV090x_WRITE_DEMOD(state, CFRINIT1, 0x00) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0x00) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x15) < 0)
- goto err;
- lock = stv090x_get_dmdlock(state, (timeout_dmd / 3));
-
- dir *= -1;
- cur_step++;
- }
- }
+ if (state->srate >= 10000000) {
+ if (stv090x_chk_tmg(state)) {
+ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x15) < 0)
+ goto err;
+ return stv090x_get_dmdlock(state, timeout_dmd);
}
+ return 0;
+ }
+
+ if (state->srate <= 4000000)
+ car_step = 1000;
+ else if (state->srate <= 7000000)
+ car_step = 2000;
+ else if (state->srate <= 10000000)
+ car_step = 3000;
+ else
+ car_step = 5000;
+
+ steps = (state->search_range / 1000) / car_step;
+ steps /= 2;
+ steps = 2 * (steps + 1);
+ if (steps < 0)
+ steps = 2;
+ else if (steps > 12)
+ steps = 12;
+
+ cur_step = 1;
+ dir = 1;
+
+ freq = state->frequency;
+ state->tuner_bw = stv090x_car_width(state->srate, state->rolloff) + state->srate;
+ while ((cur_step <= steps) && (!lock)) {
+ if (dir > 0)
+ freq += cur_step * car_step;
+ else
+ freq -= cur_step * car_step;
+
+ /* Setup tuner */
+ if (stv090x_i2c_gate_ctrl(state, 1) < 0)
+ goto err;
+
+ if (state->config->tuner_set_frequency) {
+ if (state->config->tuner_set_frequency(fe, freq) < 0)
+ goto err_gateoff;
+ }
+
+ if (state->config->tuner_set_bandwidth) {
+ if (state->config->tuner_set_bandwidth(fe, state->tuner_bw) < 0)
+ goto err_gateoff;
+ }
+
+ if (stv090x_i2c_gate_ctrl(state, 0) < 0)
+ goto err;
+
+ msleep(50);
+
+ if (stv090x_i2c_gate_ctrl(state, 1) < 0)
+ goto err;
+
+ if (state->config->tuner_get_status) {
+ if (state->config->tuner_get_status(fe, ®) < 0)
+ goto err_gateoff;
+ }
+
+ if (reg)
+ dprintk(FE_DEBUG, 1, "Tuner phase locked");
+ else
+ dprintk(FE_DEBUG, 1, "Tuner unlocked");
+
+ if (stv090x_i2c_gate_ctrl(state, 0) < 0)
+ goto err;
+
+ STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1c);
+ if (STV090x_WRITE_DEMOD(state, CFRINIT1, 0x00) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0x00) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x15) < 0)
+ goto err;
+ lock = stv090x_get_dmdlock(state, (timeout_dmd / 3));
+
+ dir *= -1;
+ cur_step++;
}
return lock;
@@ -2663,13 +2661,9 @@
return STV090x_RANGEOK;
else if (abs(offst_freq) <= (stv090x_car_width(state->srate, state->rolloff) / 2000))
return STV090x_RANGEOK;
- else
- return STV090x_OUTOFRANGE; /* Out of Range */
} else {
if (abs(offst_freq) <= ((state->search_range / 2000) + 500))
return STV090x_RANGEOK;
- else
- return STV090x_OUTOFRANGE;
}
return STV090x_OUTOFRANGE;
@@ -2789,6 +2783,12 @@
aclc = car_loop[i].crl_pilots_off_30;
}
} else { /* 16APSK and 32APSK */
+ /*
+ * This should never happen in practice, except if
+ * something is really wrong at the car_loop table.
+ */
+ if (i >= 11)
+ i = 10;
if (state->srate <= 3000000)
aclc = car_loop_apsk_low[i].crl_pilots_on_2;
else if (state->srate <= 7000000)
@@ -3470,7 +3470,20 @@
if (props->frequency == 0)
return DVBFE_ALGO_SEARCH_INVALID;
- state->delsys = props->delivery_system;
+ switch (props->delivery_system) {
+ case SYS_DSS:
+ state->delsys = STV090x_DSS;
+ break;
+ case SYS_DVBS:
+ state->delsys = STV090x_DVBS1;
+ break;
+ case SYS_DVBS2:
+ state->delsys = STV090x_DVBS2;
+ break;
+ default:
+ return DVBFE_ALGO_SEARCH_INVALID;
+ }
+
state->frequency = props->frequency;
state->srate = props->symbol_rate;
state->search_mode = STV090x_SEARCH_AUTO;
@@ -4859,8 +4872,8 @@
return -1;
}
-int stv090x_set_gpio(struct dvb_frontend *fe, u8 gpio, u8 dir, u8 value,
- u8 xor_value)
+static int stv090x_set_gpio(struct dvb_frontend *fe, u8 gpio, u8 dir,
+ u8 value, u8 xor_value)
{
struct stv090x_state *state = fe->demodulator_priv;
u8 reg = 0;
@@ -4871,7 +4884,6 @@
return stv090x_write_reg(state, STV090x_GPIOxCFG(gpio), reg);
}
-EXPORT_SYMBOL(stv090x_set_gpio);
static struct dvb_frontend_ops stv090x_ops = {
.delsys = { SYS_DVBS, SYS_DVBS2, SYS_DSS },
@@ -4908,7 +4920,7 @@
};
-struct dvb_frontend *stv090x_attach(const struct stv090x_config *config,
+struct dvb_frontend *stv090x_attach(struct stv090x_config *config,
struct i2c_adapter *i2c,
enum stv090x_demodulator demod)
{
@@ -4969,6 +4981,8 @@
if (config->diseqc_envelope_mode)
stv090x_send_diseqc_burst(&state->frontend, SEC_MINI_A);
+ config->set_gpio = stv090x_set_gpio;
+
dprintk(FE_ERROR, 1, "Attaching %s demodulator(%d) Cut=0x%02x",
state->device == STV0900 ? "STV0900" : "STV0903",
demod,
diff --git a/drivers/media/dvb-frontends/stv090x.h b/drivers/media/dvb-frontends/stv090x.h
index 86ef794..9a3ca6f 100644
--- a/drivers/media/dvb-frontends/stv090x.h
+++ b/drivers/media/dvb-frontends/stv090x.h
@@ -89,29 +89,29 @@
bool diseqc_envelope_mode;
- int (*tuner_init) (struct dvb_frontend *fe);
- int (*tuner_sleep) (struct dvb_frontend *fe);
- int (*tuner_set_mode) (struct dvb_frontend *fe, enum tuner_mode mode);
- int (*tuner_set_frequency) (struct dvb_frontend *fe, u32 frequency);
- int (*tuner_get_frequency) (struct dvb_frontend *fe, u32 *frequency);
- int (*tuner_set_bandwidth) (struct dvb_frontend *fe, u32 bandwidth);
- int (*tuner_get_bandwidth) (struct dvb_frontend *fe, u32 *bandwidth);
- int (*tuner_set_bbgain) (struct dvb_frontend *fe, u32 gain);
- int (*tuner_get_bbgain) (struct dvb_frontend *fe, u32 *gain);
- int (*tuner_set_refclk) (struct dvb_frontend *fe, u32 refclk);
- int (*tuner_get_status) (struct dvb_frontend *fe, u32 *status);
- void (*tuner_i2c_lock) (struct dvb_frontend *fe, int lock);
+ int (*tuner_init)(struct dvb_frontend *fe);
+ int (*tuner_sleep)(struct dvb_frontend *fe);
+ int (*tuner_set_mode)(struct dvb_frontend *fe, enum tuner_mode mode);
+ int (*tuner_set_frequency)(struct dvb_frontend *fe, u32 frequency);
+ int (*tuner_get_frequency)(struct dvb_frontend *fe, u32 *frequency);
+ int (*tuner_set_bandwidth)(struct dvb_frontend *fe, u32 bandwidth);
+ int (*tuner_get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth);
+ int (*tuner_set_bbgain)(struct dvb_frontend *fe, u32 gain);
+ int (*tuner_get_bbgain)(struct dvb_frontend *fe, u32 *gain);
+ int (*tuner_set_refclk)(struct dvb_frontend *fe, u32 refclk);
+ int (*tuner_get_status)(struct dvb_frontend *fe, u32 *status);
+ void (*tuner_i2c_lock)(struct dvb_frontend *fe, int lock);
+
+ /* dir = 0 -> output, dir = 1 -> input/open-drain */
+ int (*set_gpio)(struct dvb_frontend *fe, u8 gpio, u8 dir, u8 value,
+ u8 xor_value);
};
#if IS_ENABLED(CPTCFG_DVB_STV090x)
-extern struct dvb_frontend *stv090x_attach(const struct stv090x_config *config,
- struct i2c_adapter *i2c,
- enum stv090x_demodulator demod);
-
-/* dir = 0 -> output, dir = 1 -> input/open-drain */
-extern int stv090x_set_gpio(struct dvb_frontend *fe, u8 gpio,
- u8 dir, u8 value, u8 xor_value);
+struct dvb_frontend *stv090x_attach(struct stv090x_config *config,
+ struct i2c_adapter *i2c,
+ enum stv090x_demodulator demod);
#else
@@ -123,12 +123,6 @@
return NULL;
}
-static inline int stv090x_set_gpio(struct dvb_frontend *fe, u8 gpio,
- u8 opd, u8 value, u8 xor_value)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return -ENODEV;
-}
#endif /* CPTCFG_DVB_STV090x */
#endif /* __STV090x_H */
diff --git a/drivers/media/dvb-frontends/tc90522.c b/drivers/media/dvb-frontends/tc90522.c
index d9905fb..b35d65c 100644
--- a/drivers/media/dvb-frontends/tc90522.c
+++ b/drivers/media/dvb-frontends/tc90522.c
@@ -216,32 +216,30 @@
c->delivery_system = SYS_ISDBS;
layers = 0;
- ret = reg_read(state, 0xe8, val, 3);
+ ret = reg_read(state, 0xe6, val, 5);
if (ret == 0) {
- int slots;
u8 v;
+ c->stream_id = val[0] << 8 | val[1];
+
/* high/single layer */
- v = (val[0] & 0x70) >> 4;
+ v = (val[2] & 0x70) >> 4;
c->modulation = (v == 7) ? PSK_8 : QPSK;
c->fec_inner = fec_conv_sat[v];
c->layer[0].fec = c->fec_inner;
c->layer[0].modulation = c->modulation;
- c->layer[0].segment_count = val[1] & 0x3f; /* slots */
+ c->layer[0].segment_count = val[3] & 0x3f; /* slots */
/* low layer */
- v = (val[0] & 0x07);
+ v = (val[2] & 0x07);
c->layer[1].fec = fec_conv_sat[v];
if (v == 0) /* no low layer */
c->layer[1].segment_count = 0;
else
- c->layer[1].segment_count = val[2] & 0x3f; /* slots */
+ c->layer[1].segment_count = val[4] & 0x3f; /* slots */
/* actually, BPSK if v==1, but not defined in fe_modulation_t */
c->layer[1].modulation = QPSK;
layers = (v > 0) ? 2 : 1;
-
- slots = c->layer[0].segment_count + c->layer[1].segment_count;
- c->symbol_rate = 28860000 * slots / 48;
}
/* statistics */
@@ -363,7 +361,7 @@
u8 v;
c->isdbt_partial_reception = val[0] & 0x01;
- c->isdbt_sb_mode = (val[0] & 0xc0) == 0x01;
+ c->isdbt_sb_mode = (val[0] & 0xc0) == 0x40;
/* layer A */
v = (val[2] & 0x78) >> 3;
diff --git a/drivers/media/firewire/Kconfig b/drivers/media/firewire/Kconfig
index eea1e1d..0a173ac 100644
--- a/drivers/media/firewire/Kconfig
+++ b/drivers/media/firewire/Kconfig
@@ -1,5 +1,5 @@
config DVB_FIREDTV
- depends on !BACKPORT_KERNEL_3_11
+ depends on !KERNEL_3_11
tristate "FireDTV and FloppyDTV"
depends on m
depends on DVB_CORE && FIREWIRE
diff --git a/drivers/media/firewire/firedtv-ci.c b/drivers/media/firewire/firedtv-ci.c
index e5ebdbf..e63f582 100644
--- a/drivers/media/firewire/firedtv-ci.c
+++ b/drivers/media/firewire/firedtv-ci.c
@@ -253,6 +253,5 @@
void fdtv_ca_release(struct firedtv *fdtv)
{
- if (fdtv->cadev)
- dvb_unregister_device(fdtv->cadev);
+ dvb_unregister_device(fdtv->cadev);
}
diff --git a/drivers/media/firewire/firedtv.h b/drivers/media/firewire/firedtv.h
index 0d79513..23972d2 100644
--- a/drivers/media/firewire/firedtv.h
+++ b/drivers/media/firewire/firedtv.h
@@ -96,7 +96,7 @@
enum model_type type;
char subunit;
- char isochannel;
+ s8 isochannel;
struct fdtv_ir_context *ir_context;
fe_sec_voltage_t voltage;
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 1482285..bd78e2f 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -213,7 +213,7 @@
module will be called adv7183.
config VIDEO_ADV7604
- depends on !BACKPORT_KERNEL_3_13
+ depends on !KERNEL_3_13
tristate "Analog Devices ADV7604 decoder"
depends on m
depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER
@@ -312,16 +312,6 @@
To compile this driver as a module, choose M here: the
module will be called saa7115.
-config VIDEO_SAA7191
- tristate "Philips SAA7191 video decoder"
- depends on m
- depends on VIDEO_V4L2 && I2C
- ---help---
- Support for the Philips SAA7191 video decoder.
-
- To compile this driver as a module, choose M here: the
- module will be called saa7191.
-
config VIDEO_TVP514X
tristate "Texas Instruments TVP514x video decoder"
depends on m
@@ -453,6 +443,7 @@
module will be called adv7175.
config VIDEO_ADV7343
+ depends on !KERNEL_3_4
tristate "ADV7343 video encoder"
depends on m
depends on I2C
@@ -576,7 +567,7 @@
models only.
config VIDEO_MT9P031
- depends on !BACKPORT_KERNEL_3_3
+ depends on !KERNEL_3_5
tristate "Aptina MT9P031 support"
depends on m
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
@@ -587,7 +578,7 @@
(Micron) mt9p031 5 Mpixel camera.
config VIDEO_MT9T001
- depends on !BACKPORT_KERNEL_3_3
+ depends on !KERNEL_3_5
tristate "Aptina MT9T001 support"
depends on m
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
@@ -607,7 +598,7 @@
em28xx driver.
config VIDEO_MT9V032
- depends on !BACKPORT_KERNEL_3_3
+ depends on !KERNEL_3_3
tristate "Micron MT9V032 sensor support"
depends on m
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
@@ -626,7 +617,7 @@
This driver supports SR030PC30 VGA camera from Siliconfile
config VIDEO_NOON010PC30
- depends on !BACKPORT_KERNEL_3_3
+ depends on !KERNEL_3_5
tristate "Siliconfile NOON010PC30 sensor support"
depends on m
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
@@ -637,7 +628,8 @@
source "drivers/media/i2c/m5mols/Kconfig"
config VIDEO_S5K6AA
- depends on !BACKPORT_KERNEL_3_3
+ depends on !KERNEL_3_3
+ depends on !KERNEL_3_13
tristate "Samsung S5K6AAFX sensor support"
depends on m
depends on MEDIA_CAMERA_SUPPORT
@@ -647,7 +639,7 @@
camera sensor with an embedded SoC image signal processor.
config VIDEO_S5K6A3
- depends on !BACKPORT_KERNEL_3_3
+ depends on !KERNEL_3_13
tristate "Samsung S5K6A3 sensor support"
depends on m
depends on MEDIA_CAMERA_SUPPORT
@@ -657,7 +649,7 @@
camera sensor.
config VIDEO_S5K4ECGX
- depends on !BACKPORT_KERNEL_3_3
+ depends on !KERNEL_3_4
tristate "Samsung S5K4ECGX sensor support"
depends on m
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
@@ -666,7 +658,7 @@
camera sensor with an embedded SoC image signal processor.
config VIDEO_S5K5BAF
- depends on !BACKPORT_KERNEL_3_3
+ depends on !KERNEL_3_4
tristate "Samsung S5K5BAF sensor support"
depends on m
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
@@ -677,7 +669,7 @@
source "drivers/media/i2c/smiapp/Kconfig"
config VIDEO_S5C73M3
- depends on !BACKPORT_KERNEL_3_3
+ depends on !KERNEL_3_6
tristate "Samsung S5C73M3 sensor support"
depends on m
depends on I2C && SPI && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
@@ -706,7 +698,7 @@
build in control for flash, torch and indicator LEDs.
config VIDEO_LM3560
- depends on !BACKPORT_KERNEL_3_3
+ depends on !KERNEL_3_3
tristate "LM3560 dual flash driver support"
depends on m
depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER
@@ -717,7 +709,7 @@
flash, torch LEDs.
config VIDEO_LM3646
- depends on !BACKPORT_KERNEL_3_3
+ depends on !KERNEL_3_3
tristate "LM3646 dual flash driver support"
depends on m
depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 6345c00..61f289c 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -18,7 +18,6 @@
obj-$(CPTCFG_VIDEO_SAA717X) += saa717x.o
obj-$(CPTCFG_VIDEO_SAA7127) += saa7127.o
obj-$(CPTCFG_VIDEO_SAA7185) += saa7185.o
-obj-$(CPTCFG_VIDEO_SAA7191) += saa7191.o
obj-$(CPTCFG_VIDEO_SAA6752HS) += saa6752hs.o
obj-$(CPTCFG_VIDEO_ADV7170) += adv7170.o
obj-$(CPTCFG_VIDEO_ADV7175) += adv7175.o
diff --git a/drivers/media/i2c/adv7170.c b/drivers/media/i2c/adv7170.c
index 04bb297..40a1a95 100644
--- a/drivers/media/i2c/adv7170.c
+++ b/drivers/media/i2c/adv7170.c
@@ -63,9 +63,9 @@
static char *inputs[] = { "pass_through", "play_back" };
-static enum v4l2_mbus_pixelcode adv7170_codes[] = {
- V4L2_MBUS_FMT_UYVY8_2X8,
- V4L2_MBUS_FMT_UYVY8_1X16,
+static u32 adv7170_codes[] = {
+ MEDIA_BUS_FMT_UYVY8_2X8,
+ MEDIA_BUS_FMT_UYVY8_1X16,
};
/* ----------------------------------------------------------------------- */
@@ -263,7 +263,7 @@
}
static int adv7170_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
+ u32 *code)
{
if (index >= ARRAY_SIZE(adv7170_codes))
return -EINVAL;
@@ -278,9 +278,9 @@
u8 val = adv7170_read(sd, 0x7);
if ((val & 0x40) == (1 << 6))
- mf->code = V4L2_MBUS_FMT_UYVY8_1X16;
+ mf->code = MEDIA_BUS_FMT_UYVY8_1X16;
else
- mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
+ mf->code = MEDIA_BUS_FMT_UYVY8_2X8;
mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
mf->width = 0;
@@ -297,11 +297,11 @@
int ret;
switch (mf->code) {
- case V4L2_MBUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_UYVY8_2X8:
val &= ~0x40;
break;
- case V4L2_MBUS_FMT_UYVY8_1X16:
+ case MEDIA_BUS_FMT_UYVY8_1X16:
val |= 0x40;
break;
diff --git a/drivers/media/i2c/adv7175.c b/drivers/media/i2c/adv7175.c
index b88f3b3..d220af5 100644
--- a/drivers/media/i2c/adv7175.c
+++ b/drivers/media/i2c/adv7175.c
@@ -60,9 +60,9 @@
static char *inputs[] = { "pass_through", "play_back", "color_bar" };
-static enum v4l2_mbus_pixelcode adv7175_codes[] = {
- V4L2_MBUS_FMT_UYVY8_2X8,
- V4L2_MBUS_FMT_UYVY8_1X16,
+static u32 adv7175_codes[] = {
+ MEDIA_BUS_FMT_UYVY8_2X8,
+ MEDIA_BUS_FMT_UYVY8_1X16,
};
/* ----------------------------------------------------------------------- */
@@ -301,7 +301,7 @@
}
static int adv7175_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
+ u32 *code)
{
if (index >= ARRAY_SIZE(adv7175_codes))
return -EINVAL;
@@ -316,9 +316,9 @@
u8 val = adv7175_read(sd, 0x7);
if ((val & 0x40) == (1 << 6))
- mf->code = V4L2_MBUS_FMT_UYVY8_1X16;
+ mf->code = MEDIA_BUS_FMT_UYVY8_1X16;
else
- mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
+ mf->code = MEDIA_BUS_FMT_UYVY8_2X8;
mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
mf->width = 0;
@@ -335,11 +335,11 @@
int ret;
switch (mf->code) {
- case V4L2_MBUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_UYVY8_2X8:
val &= ~0x40;
break;
- case V4L2_MBUS_FMT_UYVY8_1X16:
+ case MEDIA_BUS_FMT_UYVY8_1X16:
val |= 0x40;
break;
diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c
index 821178d..bffe6eb 100644
--- a/drivers/media/i2c/adv7180.c
+++ b/drivers/media/i2c/adv7180.c
@@ -422,12 +422,12 @@
}
static int adv7180_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
+ u32 *code)
{
if (index > 0)
return -EINVAL;
- *code = V4L2_MBUS_FMT_YUYV8_2X8;
+ *code = MEDIA_BUS_FMT_YUYV8_2X8;
return 0;
}
@@ -437,7 +437,7 @@
{
struct adv7180_state *state = to_state(sd);
- fmt->code = V4L2_MBUS_FMT_YUYV8_2X8;
+ fmt->code = MEDIA_BUS_FMT_YUYV8_2X8;
fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
fmt->field = V4L2_FIELD_INTERLACED;
fmt->width = 720;
diff --git a/drivers/media/i2c/adv7183.c b/drivers/media/i2c/adv7183.c
index 3222c79..905890a 100644
--- a/drivers/media/i2c/adv7183.c
+++ b/drivers/media/i2c/adv7183.c
@@ -421,12 +421,12 @@
}
static int adv7183_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
- enum v4l2_mbus_pixelcode *code)
+ u32 *code)
{
if (index > 0)
return -EINVAL;
- *code = V4L2_MBUS_FMT_UYVY8_2X8;
+ *code = MEDIA_BUS_FMT_UYVY8_2X8;
return 0;
}
@@ -435,7 +435,7 @@
{
struct adv7183 *decoder = to_adv7183(sd);
- fmt->code = V4L2_MBUS_FMT_UYVY8_2X8;
+ fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
if (decoder->std & V4L2_STD_525_60) {
fmt->field = V4L2_FIELD_SEQ_TB;
diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c
index 7b2d7ce..3a943c6 100644
--- a/drivers/media/i2c/adv7511.c
+++ b/drivers/media/i2c/adv7511.c
@@ -26,6 +26,7 @@
#include <linux/videodev2.h>
#include <linux/gpio.h>
#include <linux/workqueue.h>
+#include <linux/hdmi.h>
#include <linux/v4l2-dv-timings.h>
#include <media/v4l2-device.h>
#include <media/v4l2-common.h>
@@ -96,6 +97,10 @@
bool have_monitor;
/* timings from s_dv_timings */
struct v4l2_dv_timings dv_timings;
+ u32 fmt_code;
+ u32 colorspace;
+ u32 ycbcr_enc;
+ u32 quantization;
/* controls */
struct v4l2_ctrl *hdmi_mode_ctrl;
struct v4l2_ctrl *hotplug_ctrl;
@@ -779,26 +784,234 @@
{
struct adv7511_state *state = get_adv7511_state(sd);
+ memset(edid->reserved, 0, sizeof(edid->reserved));
+
if (edid->pad != 0)
return -EINVAL;
- if ((edid->blocks == 0) || (edid->blocks > 256))
- return -EINVAL;
- if (!state->edid.segments) {
- v4l2_dbg(1, debug, sd, "EDID segment 0 not found\n");
- return -ENODATA;
+
+ if (edid->start_block == 0 && edid->blocks == 0) {
+ edid->blocks = state->edid.segments * 2;
+ return 0;
}
+
+ if (state->edid.segments == 0)
+ return -ENODATA;
+
if (edid->start_block >= state->edid.segments * 2)
- return -E2BIG;
- if ((edid->blocks + edid->start_block) >= state->edid.segments * 2)
+ return -EINVAL;
+
+ if (edid->start_block + edid->blocks > state->edid.segments * 2)
edid->blocks = state->edid.segments * 2 - edid->start_block;
memcpy(edid->edid, &state->edid.data[edid->start_block * 128],
128 * edid->blocks);
+
+ return 0;
+}
+
+static int adv7511_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->pad != 0)
+ return -EINVAL;
+
+ switch (code->index) {
+ case 0:
+ code->code = MEDIA_BUS_FMT_RGB888_1X24;
+ break;
+ case 1:
+ code->code = MEDIA_BUS_FMT_YUYV8_1X16;
+ break;
+ case 2:
+ code->code = MEDIA_BUS_FMT_UYVY8_1X16;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void adv7511_fill_format(struct adv7511_state *state,
+ struct v4l2_mbus_framefmt *format)
+{
+ memset(format, 0, sizeof(*format));
+
+ format->width = state->dv_timings.bt.width;
+ format->height = state->dv_timings.bt.height;
+ format->field = V4L2_FIELD_NONE;
+}
+
+static int adv7511_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *format)
+{
+ struct adv7511_state *state = get_adv7511_state(sd);
+
+ if (format->pad != 0)
+ return -EINVAL;
+
+ adv7511_fill_format(state, &format->format);
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+ struct v4l2_mbus_framefmt *fmt;
+
+ fmt = v4l2_subdev_get_try_format(fh, format->pad);
+ format->format.code = fmt->code;
+ format->format.colorspace = fmt->colorspace;
+ format->format.ycbcr_enc = fmt->ycbcr_enc;
+ format->format.quantization = fmt->quantization;
+ } else {
+ format->format.code = state->fmt_code;
+ format->format.colorspace = state->colorspace;
+ format->format.ycbcr_enc = state->ycbcr_enc;
+ format->format.quantization = state->quantization;
+ }
+
+ return 0;
+}
+
+static int adv7511_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *format)
+{
+ struct adv7511_state *state = get_adv7511_state(sd);
+ /*
+ * Bitfield namings come the CEA-861-F standard, table 8 "Auxiliary
+ * Video Information (AVI) InfoFrame Format"
+ *
+ * c = Colorimetry
+ * ec = Extended Colorimetry
+ * y = RGB or YCbCr
+ * q = RGB Quantization Range
+ * yq = YCC Quantization Range
+ */
+ u8 c = HDMI_COLORIMETRY_NONE;
+ u8 ec = HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
+ u8 y = HDMI_COLORSPACE_RGB;
+ u8 q = HDMI_QUANTIZATION_RANGE_DEFAULT;
+ u8 yq = HDMI_YCC_QUANTIZATION_RANGE_LIMITED;
+
+ if (format->pad != 0)
+ return -EINVAL;
+ switch (format->format.code) {
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ case MEDIA_BUS_FMT_YUYV8_1X16:
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ adv7511_fill_format(state, &format->format);
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+ struct v4l2_mbus_framefmt *fmt;
+
+ fmt = v4l2_subdev_get_try_format(fh, format->pad);
+ fmt->code = format->format.code;
+ fmt->colorspace = format->format.colorspace;
+ fmt->ycbcr_enc = format->format.ycbcr_enc;
+ fmt->quantization = format->format.quantization;
+ return 0;
+ }
+
+ switch (format->format.code) {
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ adv7511_wr_and_or(sd, 0x15, 0xf0, 0x01);
+ adv7511_wr_and_or(sd, 0x16, 0x03, 0xb8);
+ y = HDMI_COLORSPACE_YUV422;
+ break;
+ case MEDIA_BUS_FMT_YUYV8_1X16:
+ adv7511_wr_and_or(sd, 0x15, 0xf0, 0x01);
+ adv7511_wr_and_or(sd, 0x16, 0x03, 0xbc);
+ y = HDMI_COLORSPACE_YUV422;
+ break;
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ default:
+ adv7511_wr_and_or(sd, 0x15, 0xf0, 0x00);
+ adv7511_wr_and_or(sd, 0x16, 0x03, 0x00);
+ break;
+ }
+ state->fmt_code = format->format.code;
+ state->colorspace = format->format.colorspace;
+ state->ycbcr_enc = format->format.ycbcr_enc;
+ state->quantization = format->format.quantization;
+
+ switch (format->format.colorspace) {
+ case V4L2_COLORSPACE_ADOBERGB:
+ c = HDMI_COLORIMETRY_EXTENDED;
+ ec = y ? HDMI_EXTENDED_COLORIMETRY_ADOBE_YCC_601 :
+ HDMI_EXTENDED_COLORIMETRY_ADOBE_RGB;
+ break;
+ case V4L2_COLORSPACE_SMPTE170M:
+ c = y ? HDMI_COLORIMETRY_ITU_601 : HDMI_COLORIMETRY_NONE;
+ if (y && format->format.ycbcr_enc == V4L2_YCBCR_ENC_XV601) {
+ c = HDMI_COLORIMETRY_EXTENDED;
+ ec = HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
+ }
+ break;
+ case V4L2_COLORSPACE_REC709:
+ c = y ? HDMI_COLORIMETRY_ITU_709 : HDMI_COLORIMETRY_NONE;
+ if (y && format->format.ycbcr_enc == V4L2_YCBCR_ENC_XV709) {
+ c = HDMI_COLORIMETRY_EXTENDED;
+ ec = HDMI_EXTENDED_COLORIMETRY_XV_YCC_709;
+ }
+ break;
+ case V4L2_COLORSPACE_SRGB:
+ c = y ? HDMI_COLORIMETRY_EXTENDED : HDMI_COLORIMETRY_NONE;
+ ec = y ? HDMI_EXTENDED_COLORIMETRY_S_YCC_601 :
+ HDMI_EXTENDED_COLORIMETRY_XV_YCC_601;
+ break;
+ case V4L2_COLORSPACE_BT2020:
+ c = HDMI_COLORIMETRY_EXTENDED;
+ if (y && format->format.ycbcr_enc == V4L2_YCBCR_ENC_BT2020_CONST_LUM)
+ ec = 5; /* Not yet available in hdmi.h */
+ else
+ ec = 6; /* Not yet available in hdmi.h */
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * CEA-861-F says that for RGB formats the YCC range must match the
+ * RGB range, although sources should ignore the YCC range.
+ *
+ * The RGB quantization range shouldn't be non-zero if the EDID doesn't
+ * have the Q bit set in the Video Capabilities Data Block, however this
+ * isn't checked at the moment. The assumption is that the application
+ * knows the EDID and can detect this.
+ *
+ * The same is true for the YCC quantization range: non-standard YCC
+ * quantization ranges should only be sent if the EDID has the YQ bit
+ * set in the Video Capabilities Data Block.
+ */
+ switch (format->format.quantization) {
+ case V4L2_QUANTIZATION_FULL_RANGE:
+ q = y ? HDMI_QUANTIZATION_RANGE_DEFAULT :
+ HDMI_QUANTIZATION_RANGE_FULL;
+ yq = q ? q - 1 : HDMI_YCC_QUANTIZATION_RANGE_FULL;
+ break;
+ case V4L2_QUANTIZATION_LIM_RANGE:
+ q = y ? HDMI_QUANTIZATION_RANGE_DEFAULT :
+ HDMI_QUANTIZATION_RANGE_LIMITED;
+ yq = q ? q - 1 : HDMI_YCC_QUANTIZATION_RANGE_LIMITED;
+ break;
+ }
+
+ adv7511_wr_and_or(sd, 0x4a, 0xbf, 0);
+ adv7511_wr_and_or(sd, 0x55, 0x9f, y << 5);
+ adv7511_wr_and_or(sd, 0x56, 0x3f, c << 6);
+ adv7511_wr_and_or(sd, 0x57, 0x83, (ec << 4) | (q << 2));
+ adv7511_wr_and_or(sd, 0x59, 0x0f, yq << 4);
+ adv7511_wr_and_or(sd, 0x4a, 0xff, 1);
+
return 0;
}
static const struct v4l2_subdev_pad_ops adv7511_pad_ops = {
.get_edid = adv7511_get_edid,
+ .enum_mbus_code = adv7511_enum_mbus_code,
+ .get_fmt = adv7511_get_fmt,
+ .set_fmt = adv7511_set_fmt,
.enum_dv_timings = adv7511_enum_dv_timings,
.dv_timings_cap = adv7511_dv_timings_cap,
};
@@ -1116,6 +1329,8 @@
return -ENODEV;
}
memcpy(&state->pdata, pdata, sizeof(state->pdata));
+ state->fmt_code = MEDIA_BUS_FMT_RGB888_1X24;
+ state->colorspace = V4L2_COLORSPACE_SRGB;
sd = &state->sd;
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index bb6c3a4..764b2b7 100644
--- a/drivers/media/i2c/adv7604.c
+++ b/drivers/media/i2c/adv7604.c
@@ -88,7 +88,7 @@
};
struct adv7604_format_info {
- enum v4l2_mbus_pixelcode code;
+ u32 code;
u8 op_ch_sel;
bool rgb_out;
bool swap_cb_cr;
@@ -749,77 +749,77 @@
*/
static const struct adv7604_format_info adv7604_formats[] = {
- { V4L2_MBUS_FMT_RGB888_1X24, ADV7604_OP_CH_SEL_RGB, true, false,
+ { MEDIA_BUS_FMT_RGB888_1X24, ADV7604_OP_CH_SEL_RGB, true, false,
ADV7604_OP_MODE_SEL_SDR_444 | ADV7604_OP_FORMAT_SEL_8BIT },
- { V4L2_MBUS_FMT_YUYV8_2X8, ADV7604_OP_CH_SEL_RGB, false, false,
+ { MEDIA_BUS_FMT_YUYV8_2X8, ADV7604_OP_CH_SEL_RGB, false, false,
ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_8BIT },
- { V4L2_MBUS_FMT_YVYU8_2X8, ADV7604_OP_CH_SEL_RGB, false, true,
+ { MEDIA_BUS_FMT_YVYU8_2X8, ADV7604_OP_CH_SEL_RGB, false, true,
ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_8BIT },
- { V4L2_MBUS_FMT_YUYV10_2X10, ADV7604_OP_CH_SEL_RGB, false, false,
+ { MEDIA_BUS_FMT_YUYV10_2X10, ADV7604_OP_CH_SEL_RGB, false, false,
ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_10BIT },
- { V4L2_MBUS_FMT_YVYU10_2X10, ADV7604_OP_CH_SEL_RGB, false, true,
+ { MEDIA_BUS_FMT_YVYU10_2X10, ADV7604_OP_CH_SEL_RGB, false, true,
ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_10BIT },
- { V4L2_MBUS_FMT_YUYV12_2X12, ADV7604_OP_CH_SEL_RGB, false, false,
+ { MEDIA_BUS_FMT_YUYV12_2X12, ADV7604_OP_CH_SEL_RGB, false, false,
ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_12BIT },
- { V4L2_MBUS_FMT_YVYU12_2X12, ADV7604_OP_CH_SEL_RGB, false, true,
+ { MEDIA_BUS_FMT_YVYU12_2X12, ADV7604_OP_CH_SEL_RGB, false, true,
ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_12BIT },
- { V4L2_MBUS_FMT_UYVY8_1X16, ADV7604_OP_CH_SEL_RBG, false, false,
+ { MEDIA_BUS_FMT_UYVY8_1X16, ADV7604_OP_CH_SEL_RBG, false, false,
ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT },
- { V4L2_MBUS_FMT_VYUY8_1X16, ADV7604_OP_CH_SEL_RBG, false, true,
+ { MEDIA_BUS_FMT_VYUY8_1X16, ADV7604_OP_CH_SEL_RBG, false, true,
ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT },
- { V4L2_MBUS_FMT_YUYV8_1X16, ADV7604_OP_CH_SEL_RGB, false, false,
+ { MEDIA_BUS_FMT_YUYV8_1X16, ADV7604_OP_CH_SEL_RGB, false, false,
ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT },
- { V4L2_MBUS_FMT_YVYU8_1X16, ADV7604_OP_CH_SEL_RGB, false, true,
+ { MEDIA_BUS_FMT_YVYU8_1X16, ADV7604_OP_CH_SEL_RGB, false, true,
ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT },
- { V4L2_MBUS_FMT_UYVY10_1X20, ADV7604_OP_CH_SEL_RBG, false, false,
+ { MEDIA_BUS_FMT_UYVY10_1X20, ADV7604_OP_CH_SEL_RBG, false, false,
ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT },
- { V4L2_MBUS_FMT_VYUY10_1X20, ADV7604_OP_CH_SEL_RBG, false, true,
+ { MEDIA_BUS_FMT_VYUY10_1X20, ADV7604_OP_CH_SEL_RBG, false, true,
ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT },
- { V4L2_MBUS_FMT_YUYV10_1X20, ADV7604_OP_CH_SEL_RGB, false, false,
+ { MEDIA_BUS_FMT_YUYV10_1X20, ADV7604_OP_CH_SEL_RGB, false, false,
ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT },
- { V4L2_MBUS_FMT_YVYU10_1X20, ADV7604_OP_CH_SEL_RGB, false, true,
+ { MEDIA_BUS_FMT_YVYU10_1X20, ADV7604_OP_CH_SEL_RGB, false, true,
ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_10BIT },
- { V4L2_MBUS_FMT_UYVY12_1X24, ADV7604_OP_CH_SEL_RBG, false, false,
+ { MEDIA_BUS_FMT_UYVY12_1X24, ADV7604_OP_CH_SEL_RBG, false, false,
ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT },
- { V4L2_MBUS_FMT_VYUY12_1X24, ADV7604_OP_CH_SEL_RBG, false, true,
+ { MEDIA_BUS_FMT_VYUY12_1X24, ADV7604_OP_CH_SEL_RBG, false, true,
ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT },
- { V4L2_MBUS_FMT_YUYV12_1X24, ADV7604_OP_CH_SEL_RGB, false, false,
+ { MEDIA_BUS_FMT_YUYV12_1X24, ADV7604_OP_CH_SEL_RGB, false, false,
ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT },
- { V4L2_MBUS_FMT_YVYU12_1X24, ADV7604_OP_CH_SEL_RGB, false, true,
+ { MEDIA_BUS_FMT_YVYU12_1X24, ADV7604_OP_CH_SEL_RGB, false, true,
ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT },
};
static const struct adv7604_format_info adv7611_formats[] = {
- { V4L2_MBUS_FMT_RGB888_1X24, ADV7604_OP_CH_SEL_RGB, true, false,
+ { MEDIA_BUS_FMT_RGB888_1X24, ADV7604_OP_CH_SEL_RGB, true, false,
ADV7604_OP_MODE_SEL_SDR_444 | ADV7604_OP_FORMAT_SEL_8BIT },
- { V4L2_MBUS_FMT_YUYV8_2X8, ADV7604_OP_CH_SEL_RGB, false, false,
+ { MEDIA_BUS_FMT_YUYV8_2X8, ADV7604_OP_CH_SEL_RGB, false, false,
ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_8BIT },
- { V4L2_MBUS_FMT_YVYU8_2X8, ADV7604_OP_CH_SEL_RGB, false, true,
+ { MEDIA_BUS_FMT_YVYU8_2X8, ADV7604_OP_CH_SEL_RGB, false, true,
ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_8BIT },
- { V4L2_MBUS_FMT_YUYV12_2X12, ADV7604_OP_CH_SEL_RGB, false, false,
+ { MEDIA_BUS_FMT_YUYV12_2X12, ADV7604_OP_CH_SEL_RGB, false, false,
ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_12BIT },
- { V4L2_MBUS_FMT_YVYU12_2X12, ADV7604_OP_CH_SEL_RGB, false, true,
+ { MEDIA_BUS_FMT_YVYU12_2X12, ADV7604_OP_CH_SEL_RGB, false, true,
ADV7604_OP_MODE_SEL_SDR_422 | ADV7604_OP_FORMAT_SEL_12BIT },
- { V4L2_MBUS_FMT_UYVY8_1X16, ADV7604_OP_CH_SEL_RBG, false, false,
+ { MEDIA_BUS_FMT_UYVY8_1X16, ADV7604_OP_CH_SEL_RBG, false, false,
ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT },
- { V4L2_MBUS_FMT_VYUY8_1X16, ADV7604_OP_CH_SEL_RBG, false, true,
+ { MEDIA_BUS_FMT_VYUY8_1X16, ADV7604_OP_CH_SEL_RBG, false, true,
ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT },
- { V4L2_MBUS_FMT_YUYV8_1X16, ADV7604_OP_CH_SEL_RGB, false, false,
+ { MEDIA_BUS_FMT_YUYV8_1X16, ADV7604_OP_CH_SEL_RGB, false, false,
ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT },
- { V4L2_MBUS_FMT_YVYU8_1X16, ADV7604_OP_CH_SEL_RGB, false, true,
+ { MEDIA_BUS_FMT_YVYU8_1X16, ADV7604_OP_CH_SEL_RGB, false, true,
ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_8BIT },
- { V4L2_MBUS_FMT_UYVY12_1X24, ADV7604_OP_CH_SEL_RBG, false, false,
+ { MEDIA_BUS_FMT_UYVY12_1X24, ADV7604_OP_CH_SEL_RBG, false, false,
ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT },
- { V4L2_MBUS_FMT_VYUY12_1X24, ADV7604_OP_CH_SEL_RBG, false, true,
+ { MEDIA_BUS_FMT_VYUY12_1X24, ADV7604_OP_CH_SEL_RBG, false, true,
ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT },
- { V4L2_MBUS_FMT_YUYV12_1X24, ADV7604_OP_CH_SEL_RGB, false, false,
+ { MEDIA_BUS_FMT_YUYV12_1X24, ADV7604_OP_CH_SEL_RGB, false, false,
ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT },
- { V4L2_MBUS_FMT_YVYU12_1X24, ADV7604_OP_CH_SEL_RGB, false, true,
+ { MEDIA_BUS_FMT_YVYU12_1X24, ADV7604_OP_CH_SEL_RGB, false, true,
ADV7604_OP_MODE_SEL_SDR_422_2X | ADV7604_OP_FORMAT_SEL_12BIT },
};
static const struct adv7604_format_info *
-adv7604_format_info(struct adv7604_state *state, enum v4l2_mbus_pixelcode code)
+adv7604_format_info(struct adv7604_state *state, u32 code)
{
unsigned int i;
@@ -1917,7 +1917,7 @@
info = adv7604_format_info(state, format->format.code);
if (info == NULL)
- info = adv7604_format_info(state, V4L2_MBUS_FMT_YUYV8_2X8);
+ info = adv7604_format_info(state, MEDIA_BUS_FMT_YUYV8_2X8);
adv7604_fill_format(state, &format->format);
format->format.code = info->code;
@@ -1997,19 +1997,7 @@
struct adv7604_state *state = to_state(sd);
u8 *data = NULL;
- if (edid->pad > ADV7604_PAD_HDMI_PORT_D)
- return -EINVAL;
- if (edid->blocks == 0)
- return -EINVAL;
- if (edid->blocks > 2)
- return -EINVAL;
- if (edid->start_block > 1)
- return -EINVAL;
- if (edid->start_block == 1)
- edid->blocks = 1;
-
- if (edid->blocks > state->edid.blocks)
- edid->blocks = state->edid.blocks;
+ memset(edid->reserved, 0, sizeof(edid->reserved));
switch (edid->pad) {
case ADV7604_PAD_HDMI_PORT_A:
@@ -2021,14 +2009,24 @@
break;
default:
return -EINVAL;
- break;
}
- if (!data)
+
+ if (edid->start_block == 0 && edid->blocks == 0) {
+ edid->blocks = data ? state->edid.blocks : 0;
+ return 0;
+ }
+
+ if (data == NULL)
return -ENODATA;
- memcpy(edid->edid,
- data + edid->start_block * 128,
- edid->blocks * 128);
+ if (edid->start_block >= state->edid.blocks)
+ return -EINVAL;
+
+ if (edid->start_block + edid->blocks > state->edid.blocks)
+ edid->blocks = state->edid.blocks - edid->start_block;
+
+ memcpy(edid->edid, data + edid->start_block * 128, edid->blocks * 128);
+
return 0;
}
@@ -2068,6 +2066,8 @@
int err;
int i;
+ memset(edid->reserved, 0, sizeof(edid->reserved));
+
if (edid->pad > ADV7604_PAD_HDMI_PORT_D)
return -EINVAL;
if (edid->start_block != 0)
@@ -2164,7 +2164,6 @@
return -EIO;
}
-
/* enable hotplug after 100 ms */
queue_delayed_work(state->work_queues,
&state->delayed_work_enable_hotplug, HZ / 10);
@@ -2807,7 +2806,7 @@
}
state->timings = cea640x480;
- state->format = adv7604_format_info(state, V4L2_MBUS_FMT_YUYV8_2X8);
+ state->format = adv7604_format_info(state, MEDIA_BUS_FMT_YUYV8_2X8);
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &adv7604_ops);
diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c
index ba8e833..9af02d3 100644
--- a/drivers/media/i2c/adv7842.c
+++ b/drivers/media/i2c/adv7842.c
@@ -1877,12 +1877,12 @@
}
static int adv7842_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
+ u32 *code)
{
if (index)
return -EINVAL;
/* Good enough for now */
- *code = V4L2_MBUS_FMT_FIXED;
+ *code = MEDIA_BUS_FMT_FIXED;
return 0;
}
@@ -1893,7 +1893,7 @@
fmt->width = state->timings.bt.width;
fmt->height = state->timings.bt.height;
- fmt->code = V4L2_MBUS_FMT_FIXED;
+ fmt->code = MEDIA_BUS_FMT_FIXED;
fmt->field = V4L2_FIELD_NONE;
if (state->mode == ADV7842_MODE_SDP) {
@@ -2028,16 +2028,7 @@
struct adv7842_state *state = to_state(sd);
u8 *data = NULL;
- if (edid->pad > ADV7842_EDID_PORT_VGA)
- return -EINVAL;
- if (edid->blocks == 0)
- return -EINVAL;
- if (edid->blocks > 2)
- return -EINVAL;
- if (edid->start_block > 1)
- return -EINVAL;
- if (edid->start_block == 1)
- edid->blocks = 1;
+ memset(edid->reserved, 0, sizeof(edid->reserved));
switch (edid->pad) {
case ADV7842_EDID_PORT_A:
@@ -2052,12 +2043,23 @@
default:
return -EINVAL;
}
+
+ if (edid->start_block == 0 && edid->blocks == 0) {
+ edid->blocks = data ? 2 : 0;
+ return 0;
+ }
+
if (!data)
return -ENODATA;
- memcpy(edid->edid,
- data + edid->start_block * 128,
- edid->blocks * 128);
+ if (edid->start_block >= 2)
+ return -EINVAL;
+
+ if (edid->start_block + edid->blocks > 2)
+ edid->blocks = 2 - edid->start_block;
+
+ memcpy(edid->edid, data + edid->start_block * 128, edid->blocks * 128);
+
return 0;
}
@@ -2066,12 +2068,16 @@
struct adv7842_state *state = to_state(sd);
int err = 0;
+ memset(e->reserved, 0, sizeof(e->reserved));
+
if (e->pad > ADV7842_EDID_PORT_VGA)
return -EINVAL;
if (e->start_block != 0)
return -EINVAL;
- if (e->blocks > 2)
+ if (e->blocks > 2) {
+ e->blocks = 2;
return -E2BIG;
+ }
/* todo, per edid */
state->aspect_ratio = v4l2_calc_aspect_ratio(e->edid[0x15],
diff --git a/drivers/media/i2c/ak881x.c b/drivers/media/i2c/ak881x.c
index b480759..c8076f2 100644
--- a/drivers/media/i2c/ak881x.c
+++ b/drivers/media/i2c/ak881x.c
@@ -102,7 +102,7 @@
v4l_bound_align_image(&mf->width, 0, 720, 2,
&mf->height, 0, ak881x->lines, 1, 0);
mf->field = V4L2_FIELD_INTERLACED;
- mf->code = V4L2_MBUS_FMT_YUYV8_2X8;
+ mf->code = MEDIA_BUS_FMT_YUYV8_2X8;
mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
return 0;
@@ -112,19 +112,19 @@
struct v4l2_mbus_framefmt *mf)
{
if (mf->field != V4L2_FIELD_INTERLACED ||
- mf->code != V4L2_MBUS_FMT_YUYV8_2X8)
+ mf->code != MEDIA_BUS_FMT_YUYV8_2X8)
return -EINVAL;
return ak881x_try_g_mbus_fmt(sd, mf);
}
static int ak881x_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
+ u32 *code)
{
if (index)
return -EINVAL;
- *code = V4L2_MBUS_FMT_YUYV8_2X8;
+ *code = MEDIA_BUS_FMT_YUYV8_2X8;
return 0;
}
diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c
index f460ce0..05b7cd6 100644
--- a/drivers/media/i2c/cx25840/cx25840-core.c
+++ b/drivers/media/i2c/cx25840/cx25840-core.c
@@ -879,7 +879,7 @@
/* Sets horizontal blanking delay and active lines */
cx25840_write(client, 0x470, hblank);
cx25840_write(client, 0x471,
- 0xff & (((hblank >> 8) & 0x3) | (hactive << 4)));
+ (((hblank >> 8) & 0x3) | (hactive << 4)) & 0xff);
cx25840_write(client, 0x472, hactive >> 4);
/* Sets burst gate delay */
@@ -888,13 +888,13 @@
/* Sets vertical blanking delay and active duration */
cx25840_write(client, 0x474, vblank);
cx25840_write(client, 0x475,
- 0xff & (((vblank >> 8) & 0x3) | (vactive << 4)));
+ (((vblank >> 8) & 0x3) | (vactive << 4)) & 0xff);
cx25840_write(client, 0x476, vactive >> 4);
cx25840_write(client, 0x477, vblank656);
/* Sets src decimation rate */
- cx25840_write(client, 0x478, 0xff & src_decimation);
- cx25840_write(client, 0x479, 0xff & (src_decimation >> 8));
+ cx25840_write(client, 0x478, src_decimation & 0xff);
+ cx25840_write(client, 0x479, (src_decimation >> 8) & 0xff);
/* Sets Luma and UV Low pass filters */
cx25840_write(client, 0x47a, luma_lpf << 6 | ((uv_lpf << 4) & 0x30));
@@ -904,8 +904,8 @@
/* Sets SC Step*/
cx25840_write(client, 0x47c, sc);
- cx25840_write(client, 0x47d, 0xff & sc >> 8);
- cx25840_write(client, 0x47e, 0xff & sc >> 16);
+ cx25840_write(client, 0x47d, (sc >> 8) & 0xff);
+ cx25840_write(client, 0x47e, (sc >> 16) & 0xff);
/* Sets VBI parameters */
if (std & V4L2_STD_625_50) {
@@ -1373,7 +1373,7 @@
int HSC, VSC, Vsrc, Hsrc, filter, Vlines;
int is_50Hz = !(state->std & V4L2_STD_525_60);
- if (fmt->code != V4L2_MBUS_FMT_FIXED)
+ if (fmt->code != MEDIA_BUS_FMT_FIXED)
return -EINVAL;
fmt->field = V4L2_FIELD_INTERLACED;
diff --git a/drivers/media/i2c/cx25840/cx25840-firmware.c b/drivers/media/i2c/cx25840/cx25840-firmware.c
index b3169f9..9bbb31a 100644
--- a/drivers/media/i2c/cx25840/cx25840-firmware.c
+++ b/drivers/media/i2c/cx25840/cx25840-firmware.c
@@ -113,7 +113,7 @@
const u8 *ptr;
const char *fwname = get_fw_name(client);
int size, retval;
- int MAX_BUF_SIZE = FWSEND;
+ int max_buf_size = FWSEND;
u32 gpio_oe = 0, gpio_da = 0;
if (is_cx2388x(state)) {
@@ -122,10 +122,9 @@
gpio_da = cx25840_read(client, 0x164);
}
- if (is_cx231xx(state) && MAX_BUF_SIZE > 16) {
- v4l_err(client, " Firmware download size changed to 16 bytes max length\n");
- MAX_BUF_SIZE = 16; /* cx231xx cannot accept more than 16 bytes at a time */
- }
+ /* cx231xx cannot accept more than 16 bytes at a time */
+ if (is_cx231xx(state) && max_buf_size > 16)
+ max_buf_size = 16;
if (request_firmware(&fw, fwname, FWDEV(client)) != 0) {
v4l_err(client, "unable to open firmware %s\n", fwname);
@@ -140,7 +139,7 @@
size = fw->size;
ptr = fw->data;
while (size > 0) {
- int len = min(MAX_BUF_SIZE - 2, size);
+ int len = min(max_buf_size - 2, size);
memcpy(buffer + 2, ptr, len);
diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c
index 8311f1a..175a761 100644
--- a/drivers/media/i2c/ir-kbd-i2c.c
+++ b/drivers/media/i2c/ir-kbd-i2c.c
@@ -464,8 +464,7 @@
cancel_delayed_work_sync(&ir->work);
/* unregister device */
- if (ir->rc)
- rc_unregister_device(ir->rc);
+ rc_unregister_device(ir->rc);
/* free memory */
return 0;
diff --git a/drivers/media/i2c/m5mols/Kconfig b/drivers/media/i2c/m5mols/Kconfig
index c858c2f..4ed0509 100644
--- a/drivers/media/i2c/m5mols/Kconfig
+++ b/drivers/media/i2c/m5mols/Kconfig
@@ -1,5 +1,5 @@
config VIDEO_M5MOLS
- depends on !BACKPORT_KERNEL_3_3
+ depends on !KERNEL_3_6
tristate "Fujitsu M-5MOLS 8MP sensor support"
depends on m
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
diff --git a/drivers/media/i2c/m5mols/m5mols_core.c b/drivers/media/i2c/m5mols/m5mols_core.c
index 8d870b7..2820f7c 100644
--- a/drivers/media/i2c/m5mols/m5mols_core.c
+++ b/drivers/media/i2c/m5mols/m5mols_core.c
@@ -57,14 +57,14 @@
[M5MOLS_RESTYPE_MONITOR] = {
.width = 1920,
.height = 1080,
- .code = V4L2_MBUS_FMT_VYUY8_2X8,
+ .code = MEDIA_BUS_FMT_VYUY8_2X8,
.field = V4L2_FIELD_NONE,
.colorspace = V4L2_COLORSPACE_JPEG,
},
[M5MOLS_RESTYPE_CAPTURE] = {
.width = 1920,
.height = 1080,
- .code = V4L2_MBUS_FMT_JPEG_1X8,
+ .code = MEDIA_BUS_FMT_JPEG_1X8,
.field = V4L2_FIELD_NONE,
.colorspace = V4L2_COLORSPACE_JPEG,
},
@@ -479,7 +479,7 @@
* __find_restype - Lookup M-5MOLS resolution type according to pixel code
* @code: pixel code
*/
-static enum m5mols_restype __find_restype(enum v4l2_mbus_pixelcode code)
+static enum m5mols_restype __find_restype(u32 code)
{
enum m5mols_restype type = M5MOLS_RESTYPE_MONITOR;
diff --git a/drivers/media/i2c/ml86v7667.c b/drivers/media/i2c/ml86v7667.c
index c629b33..f6ed2a4 100644
--- a/drivers/media/i2c/ml86v7667.c
+++ b/drivers/media/i2c/ml86v7667.c
@@ -192,12 +192,12 @@
}
static int ml86v7667_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
+ u32 *code)
{
if (index > 0)
return -EINVAL;
- *code = V4L2_MBUS_FMT_YUYV8_2X8;
+ *code = MEDIA_BUS_FMT_YUYV8_2X8;
return 0;
}
@@ -207,7 +207,7 @@
{
struct ml86v7667_priv *priv = to_ml86v7667(sd);
- fmt->code = V4L2_MBUS_FMT_YUYV8_2X8;
+ fmt->code = MEDIA_BUS_FMT_YUYV8_2X8;
fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
/* The top field is always transferred first by the chip */
fmt->field = V4L2_FIELD_INTERLACED_TB;
diff --git a/drivers/media/i2c/mt9m032.c b/drivers/media/i2c/mt9m032.c
index fe9d820..7a2b628 100644
--- a/drivers/media/i2c/mt9m032.c
+++ b/drivers/media/i2c/mt9m032.c
@@ -323,7 +323,7 @@
if (code->index != 0)
return -EINVAL;
- code->code = V4L2_MBUS_FMT_Y8_1X8;
+ code->code = MEDIA_BUS_FMT_Y8_1X8;
return 0;
}
@@ -331,7 +331,7 @@
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_frame_size_enum *fse)
{
- if (fse->index != 0 || fse->code != V4L2_MBUS_FMT_Y8_1X8)
+ if (fse->index != 0 || fse->code != MEDIA_BUS_FMT_Y8_1X8)
return -EINVAL;
fse->min_width = MT9M032_COLUMN_SIZE_DEF;
@@ -759,7 +759,7 @@
sensor->format.width = sensor->crop.width;
sensor->format.height = sensor->crop.height;
- sensor->format.code = V4L2_MBUS_FMT_Y8_1X8;
+ sensor->format.code = MEDIA_BUS_FMT_Y8_1X8;
sensor->format.field = V4L2_FIELD_NONE;
sensor->format.colorspace = V4L2_COLORSPACE_SRGB;
diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c
index e18797f..edb76bd 100644
--- a/drivers/media/i2c/mt9p031.c
+++ b/drivers/media/i2c/mt9p031.c
@@ -950,9 +950,9 @@
format = v4l2_subdev_get_try_format(fh, 0);
if (mt9p031->model == MT9P031_MODEL_MONOCHROME)
- format->code = V4L2_MBUS_FMT_Y12_1X12;
+ format->code = MEDIA_BUS_FMT_Y12_1X12;
else
- format->code = V4L2_MBUS_FMT_SGRBG12_1X12;
+ format->code = MEDIA_BUS_FMT_SGRBG12_1X12;
format->width = MT9P031_WINDOW_WIDTH_DEF;
format->height = MT9P031_WINDOW_HEIGHT_DEF;
@@ -1120,9 +1120,9 @@
mt9p031->crop.top = MT9P031_ROW_START_DEF;
if (mt9p031->model == MT9P031_MODEL_MONOCHROME)
- mt9p031->format.code = V4L2_MBUS_FMT_Y12_1X12;
+ mt9p031->format.code = MEDIA_BUS_FMT_Y12_1X12;
else
- mt9p031->format.code = V4L2_MBUS_FMT_SGRBG12_1X12;
+ mt9p031->format.code = MEDIA_BUS_FMT_SGRBG12_1X12;
mt9p031->format.width = MT9P031_WINDOW_WIDTH_DEF;
mt9p031->format.height = MT9P031_WINDOW_HEIGHT_DEF;
diff --git a/drivers/media/i2c/mt9t001.c b/drivers/media/i2c/mt9t001.c
index 422e068..d9e9889 100644
--- a/drivers/media/i2c/mt9t001.c
+++ b/drivers/media/i2c/mt9t001.c
@@ -333,7 +333,7 @@
if (code->index > 0)
return -EINVAL;
- code->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ code->code = MEDIA_BUS_FMT_SGRBG10_1X10;
return 0;
}
@@ -341,7 +341,7 @@
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_frame_size_enum *fse)
{
- if (fse->index >= 8 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10)
+ if (fse->index >= 8 || fse->code != MEDIA_BUS_FMT_SGRBG10_1X10)
return -EINVAL;
fse->min_width = (MT9T001_WINDOW_WIDTH_DEF + 1) / fse->index;
@@ -792,7 +792,7 @@
crop->height = MT9T001_WINDOW_HEIGHT_DEF + 1;
format = v4l2_subdev_get_try_format(fh, 0);
- format->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ format->code = MEDIA_BUS_FMT_SGRBG10_1X10;
format->width = MT9T001_WINDOW_WIDTH_DEF + 1;
format->height = MT9T001_WINDOW_HEIGHT_DEF + 1;
format->field = V4L2_FIELD_NONE;
@@ -917,7 +917,7 @@
mt9t001->crop.width = MT9T001_WINDOW_WIDTH_DEF + 1;
mt9t001->crop.height = MT9T001_WINDOW_HEIGHT_DEF + 1;
- mt9t001->format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ mt9t001->format.code = MEDIA_BUS_FMT_SGRBG10_1X10;
mt9t001->format.width = MT9T001_WINDOW_WIDTH_DEF + 1;
mt9t001->format.height = MT9T001_WINDOW_HEIGHT_DEF + 1;
mt9t001->format.field = V4L2_FIELD_NONE;
diff --git a/drivers/media/i2c/mt9v011.c b/drivers/media/i2c/mt9v011.c
index 355646c..d56bd47 100644
--- a/drivers/media/i2c/mt9v011.c
+++ b/drivers/media/i2c/mt9v011.c
@@ -325,18 +325,18 @@
}
static int mt9v011_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
- enum v4l2_mbus_pixelcode *code)
+ u32 *code)
{
if (index > 0)
return -EINVAL;
- *code = V4L2_MBUS_FMT_SGRBG8_1X8;
+ *code = MEDIA_BUS_FMT_SGRBG8_1X8;
return 0;
}
static int mt9v011_try_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt)
{
- if (fmt->code != V4L2_MBUS_FMT_SGRBG8_1X8)
+ if (fmt->code != MEDIA_BUS_FMT_SGRBG8_1X8)
return -EINVAL;
v4l_bound_align_image(&fmt->width, 48, 639, 1,
diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c
index d044bce..93687c1 100644
--- a/drivers/media/i2c/mt9v032.c
+++ b/drivers/media/i2c/mt9v032.c
@@ -454,7 +454,7 @@
if (code->index > 0)
return -EINVAL;
- code->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ code->code = MEDIA_BUS_FMT_SGRBG10_1X10;
return 0;
}
@@ -462,7 +462,7 @@
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_frame_size_enum *fse)
{
- if (fse->index >= 3 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10)
+ if (fse->index >= 3 || fse->code != MEDIA_BUS_FMT_SGRBG10_1X10)
return -EINVAL;
fse->min_width = MT9V032_WINDOW_WIDTH_DEF / (1 << fse->index);
@@ -814,9 +814,9 @@
format = v4l2_subdev_get_try_format(fh, 0);
if (mt9v032->model->color)
- format->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ format->code = MEDIA_BUS_FMT_SGRBG10_1X10;
else
- format->code = V4L2_MBUS_FMT_Y10_1X10;
+ format->code = MEDIA_BUS_FMT_Y10_1X10;
format->width = MT9V032_WINDOW_WIDTH_DEF;
format->height = MT9V032_WINDOW_HEIGHT_DEF;
@@ -966,9 +966,9 @@
mt9v032->crop.height = MT9V032_WINDOW_HEIGHT_DEF;
if (mt9v032->model->color)
- mt9v032->format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ mt9v032->format.code = MEDIA_BUS_FMT_SGRBG10_1X10;
else
- mt9v032->format.code = V4L2_MBUS_FMT_Y10_1X10;
+ mt9v032->format.code = MEDIA_BUS_FMT_Y10_1X10;
mt9v032->format.width = MT9V032_WINDOW_WIDTH_DEF;
mt9v032->format.height = MT9V032_WINDOW_HEIGHT_DEF;
diff --git a/drivers/media/i2c/noon010pc30.c b/drivers/media/i2c/noon010pc30.c
index 7eae487..00c7b26 100644
--- a/drivers/media/i2c/noon010pc30.c
+++ b/drivers/media/i2c/noon010pc30.c
@@ -112,7 +112,7 @@
#define REG_TERM 0xFFFF
struct noon010_format {
- enum v4l2_mbus_pixelcode code;
+ u32 code;
enum v4l2_colorspace colorspace;
u16 ispctl1_reg;
};
@@ -175,23 +175,23 @@
/* Supported pixel formats. */
static const struct noon010_format noon010_formats[] = {
{
- .code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .code = MEDIA_BUS_FMT_YUYV8_2X8,
.colorspace = V4L2_COLORSPACE_JPEG,
.ispctl1_reg = 0x03,
}, {
- .code = V4L2_MBUS_FMT_YVYU8_2X8,
+ .code = MEDIA_BUS_FMT_YVYU8_2X8,
.colorspace = V4L2_COLORSPACE_JPEG,
.ispctl1_reg = 0x02,
}, {
- .code = V4L2_MBUS_FMT_VYUY8_2X8,
+ .code = MEDIA_BUS_FMT_VYUY8_2X8,
.colorspace = V4L2_COLORSPACE_JPEG,
.ispctl1_reg = 0,
}, {
- .code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
.colorspace = V4L2_COLORSPACE_JPEG,
.ispctl1_reg = 0x01,
}, {
- .code = V4L2_MBUS_FMT_RGB565_2X8_BE,
+ .code = MEDIA_BUS_FMT_RGB565_2X8_BE,
.colorspace = V4L2_COLORSPACE_JPEG,
.ispctl1_reg = 0x40,
},
diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c
index ad8795f..48eeb6a 100644
--- a/drivers/media/i2c/ov7670.c
+++ b/drivers/media/i2c/ov7670.c
@@ -632,31 +632,31 @@
* The magic matrix numbers come from OmniVision.
*/
static struct ov7670_format_struct {
- enum v4l2_mbus_pixelcode mbus_code;
+ u32 mbus_code;
enum v4l2_colorspace colorspace;
struct regval_list *regs;
int cmatrix[CMATRIX_LEN];
} ov7670_formats[] = {
{
- .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
.colorspace = V4L2_COLORSPACE_JPEG,
.regs = ov7670_fmt_yuv422,
.cmatrix = { 128, -128, 0, -34, -94, 128 },
},
{
- .mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE,
+ .mbus_code = MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE,
.colorspace = V4L2_COLORSPACE_SRGB,
.regs = ov7670_fmt_rgb444,
.cmatrix = { 179, -179, 0, -61, -176, 228 },
},
{
- .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE,
+ .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE,
.colorspace = V4L2_COLORSPACE_SRGB,
.regs = ov7670_fmt_rgb565,
.cmatrix = { 179, -179, 0, -61, -176, 228 },
},
{
- .mbus_code = V4L2_MBUS_FMT_SBGGR8_1X8,
+ .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
.colorspace = V4L2_COLORSPACE_SRGB,
.regs = ov7670_fmt_raw,
.cmatrix = { 0, 0, 0, 0, 0, 0 },
@@ -772,7 +772,7 @@
pll_factor = PLL_FACTOR;
clkrc++;
- if (info->fmt->mbus_code == V4L2_MBUS_FMT_SBGGR8_1X8)
+ if (info->fmt->mbus_code == MEDIA_BUS_FMT_SBGGR8_1X8)
clkrc = (clkrc >> 1);
tpf->numerator = 1;
@@ -810,7 +810,7 @@
} else {
clkrc = (5 * pll_factor * info->clock_speed * tpf->numerator) /
(4 * tpf->denominator);
- if (info->fmt->mbus_code == V4L2_MBUS_FMT_SBGGR8_1X8)
+ if (info->fmt->mbus_code == MEDIA_BUS_FMT_SBGGR8_1X8)
clkrc = (clkrc << 1);
clkrc--;
}
@@ -900,7 +900,7 @@
static int ov7670_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
- enum v4l2_mbus_pixelcode *code)
+ u32 *code)
{
if (index >= N_OV7670_FMTS)
return -EINVAL;
diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c
index 4da90c6..2246bd5 100644
--- a/drivers/media/i2c/ov9650.c
+++ b/drivers/media/i2c/ov9650.c
@@ -384,17 +384,17 @@
};
struct ov965x_pixfmt {
- enum v4l2_mbus_pixelcode code;
+ u32 code;
u32 colorspace;
/* REG_TSLB value, only bits [3:2] may be set. */
u8 tslb_reg;
};
static const struct ov965x_pixfmt ov965x_formats[] = {
- { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG, 0x00},
- { V4L2_MBUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_JPEG, 0x04},
- { V4L2_MBUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG, 0x0c},
- { V4L2_MBUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_JPEG, 0x08},
+ { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG, 0x00},
+ { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_JPEG, 0x04},
+ { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG, 0x0c},
+ { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_JPEG, 0x08},
};
/*
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3.h b/drivers/media/i2c/s5c73m3/s5c73m3.h
index 9656b67..13aed59 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3.h
+++ b/drivers/media/i2c/s5c73m3/s5c73m3.h
@@ -27,8 +27,8 @@
#define DRIVER_NAME "S5C73M3"
-#define S5C73M3_ISP_FMT V4L2_MBUS_FMT_VYUY8_2X8
-#define S5C73M3_JPEG_FMT V4L2_MBUS_FMT_S5C_UYVY_JPEG_1X8
+#define S5C73M3_ISP_FMT MEDIA_BUS_FMT_VYUY8_2X8
+#define S5C73M3_JPEG_FMT MEDIA_BUS_FMT_S5C_UYVY_JPEG_1X8
/* Subdevs pad index definitions */
enum s5c73m3_pads {
@@ -402,7 +402,7 @@
const struct s5c73m3_frame_size *sensor_pix_size[2];
const struct s5c73m3_frame_size *oif_pix_size[2];
- enum v4l2_mbus_pixelcode mbus_code;
+ u32 mbus_code;
const struct s5c73m3_interval *fiv;
diff --git a/drivers/media/i2c/s5k4ecgx.c b/drivers/media/i2c/s5k4ecgx.c
index 1fcc76f..d1c50c9 100644
--- a/drivers/media/i2c/s5k4ecgx.c
+++ b/drivers/media/i2c/s5k4ecgx.c
@@ -151,7 +151,7 @@
#define S5K4ECGX_NUM_PREV ARRAY_SIZE(s5k4ecgx_prev_sizes)
struct s5k4ecgx_pixfmt {
- enum v4l2_mbus_pixelcode code;
+ u32 code;
u32 colorspace;
/* REG_TC_PCFG_Format register value */
u16 reg_p_format;
@@ -159,7 +159,7 @@
/* By default value, output from sensor will be YUV422 0-255 */
static const struct s5k4ecgx_pixfmt s5k4ecgx_formats[] = {
- { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG, 5 },
+ { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG, 5 },
};
static const char * const s5k4ecgx_supply_names[] = {
diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c
index 0e461a6..60a74d8 100644
--- a/drivers/media/i2c/s5k5baf.c
+++ b/drivers/media/i2c/s5k5baf.c
@@ -248,7 +248,7 @@
#define NUM_ISP_PADS 2
struct s5k5baf_pixfmt {
- enum v4l2_mbus_pixelcode code;
+ u32 code;
u32 colorspace;
/* REG_P_FMT(x) register value */
u16 reg_p_fmt;
@@ -331,10 +331,10 @@
};
static const struct s5k5baf_pixfmt s5k5baf_formats[] = {
- { V4L2_MBUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_JPEG, 5 },
+ { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_JPEG, 5 },
/* range 16-240 */
- { V4L2_MBUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_REC709, 6 },
- { V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_JPEG, 0 },
+ { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_REC709, 6 },
+ { MEDIA_BUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_JPEG, 0 },
};
static struct v4l2_rect s5k5baf_cis_rect = {
@@ -1206,7 +1206,7 @@
if (code->pad == PAD_CIS) {
if (code->index > 0)
return -EINVAL;
- code->code = V4L2_MBUS_FMT_FIXED;
+ code->code = MEDIA_BUS_FMT_FIXED;
return 0;
}
@@ -1227,7 +1227,7 @@
return -EINVAL;
if (fse->pad == PAD_CIS) {
- fse->code = V4L2_MBUS_FMT_FIXED;
+ fse->code = MEDIA_BUS_FMT_FIXED;
fse->min_width = S5K5BAF_CIS_WIDTH;
fse->max_width = S5K5BAF_CIS_WIDTH;
fse->min_height = S5K5BAF_CIS_HEIGHT;
@@ -1252,7 +1252,7 @@
{
mf->width = S5K5BAF_CIS_WIDTH;
mf->height = S5K5BAF_CIS_HEIGHT;
- mf->code = V4L2_MBUS_FMT_FIXED;
+ mf->code = MEDIA_BUS_FMT_FIXED;
mf->colorspace = V4L2_COLORSPACE_JPEG;
mf->field = V4L2_FIELD_NONE;
}
diff --git a/drivers/media/i2c/s5k6a3.c b/drivers/media/i2c/s5k6a3.c
index c11a408..91b841a 100644
--- a/drivers/media/i2c/s5k6a3.c
+++ b/drivers/media/i2c/s5k6a3.c
@@ -80,7 +80,7 @@
static const struct v4l2_mbus_framefmt s5k6a3_formats[] = {
{
- .code = V4L2_MBUS_FMT_SGRBG10_1X10,
+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
.colorspace = V4L2_COLORSPACE_SRGB,
.field = V4L2_FIELD_NONE,
}
diff --git a/drivers/media/i2c/s5k6aa.c b/drivers/media/i2c/s5k6aa.c
index 629a5cd..2851581 100644
--- a/drivers/media/i2c/s5k6aa.c
+++ b/drivers/media/i2c/s5k6aa.c
@@ -191,7 +191,7 @@
};
struct s5k6aa_pixfmt {
- enum v4l2_mbus_pixelcode code;
+ u32 code;
u32 colorspace;
/* REG_P_FMT(x) register value */
u16 reg_p_fmt;
@@ -285,10 +285,10 @@
/* TODO: Add RGB888 and Bayer format */
static const struct s5k6aa_pixfmt s5k6aa_formats[] = {
- { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG, 5 },
+ { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG, 5 },
/* range 16-240 */
- { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_REC709, 6 },
- { V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_JPEG, 0 },
+ { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_REC709, 6 },
+ { MEDIA_BUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_JPEG, 0 },
};
static const struct s5k6aa_interval s5k6aa_intervals[] = {
diff --git a/drivers/media/i2c/saa6752hs.c b/drivers/media/i2c/saa6752hs.c
index 4024ea6..f14c0e6 100644
--- a/drivers/media/i2c/saa6752hs.c
+++ b/drivers/media/i2c/saa6752hs.c
@@ -562,7 +562,7 @@
h->video_format = SAA6752HS_VF_D1;
f->width = v4l2_format_table[h->video_format].fmt.pix.width;
f->height = v4l2_format_table[h->video_format].fmt.pix.height;
- f->code = V4L2_MBUS_FMT_FIXED;
+ f->code = MEDIA_BUS_FMT_FIXED;
f->field = V4L2_FIELD_INTERLACED;
f->colorspace = V4L2_COLORSPACE_SMPTE170M;
return 0;
@@ -572,7 +572,7 @@
{
int dist_352, dist_480, dist_720;
- f->code = V4L2_MBUS_FMT_FIXED;
+ f->code = MEDIA_BUS_FMT_FIXED;
dist_352 = abs(f->width - 352);
dist_480 = abs(f->width - 480);
@@ -599,7 +599,7 @@
{
struct saa6752hs_state *h = to_state(sd);
- if (f->code != V4L2_MBUS_FMT_FIXED)
+ if (f->code != MEDIA_BUS_FMT_FIXED)
return -EINVAL;
/*
diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c
index bdbac44..80a106b 100644
--- a/drivers/media/i2c/saa7115.c
+++ b/drivers/media/i2c/saa7115.c
@@ -1172,7 +1172,7 @@
static int saa711x_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt)
{
- if (fmt->code != V4L2_MBUS_FMT_FIXED)
+ if (fmt->code != MEDIA_BUS_FMT_FIXED)
return -EINVAL;
fmt->field = V4L2_FIELD_INTERLACED;
fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
diff --git a/drivers/media/i2c/saa717x.c b/drivers/media/i2c/saa717x.c
index 4aef84a..5ef14b2 100644
--- a/drivers/media/i2c/saa717x.c
+++ b/drivers/media/i2c/saa717x.c
@@ -998,7 +998,7 @@
v4l2_dbg(1, debug, sd, "decoder set size\n");
- if (fmt->code != V4L2_MBUS_FMT_FIXED)
+ if (fmt->code != MEDIA_BUS_FMT_FIXED)
return -EINVAL;
/* FIXME need better bounds checking here */
diff --git a/drivers/media/i2c/saa7191.c b/drivers/media/i2c/saa7191.c
deleted file mode 100644
index 8e96992..0000000
--- a/drivers/media/i2c/saa7191.c
+++ /dev/null
@@ -1,649 +0,0 @@
-/*
- * saa7191.c - Philips SAA7191 video decoder driver
- *
- * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
- * Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/major.h>
-#include <linux/module.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-
-#include <linux/videodev2.h>
-#include <linux/i2c.h>
-#include <media/v4l2-device.h>
-
-#include "saa7191.h"
-
-#define SAA7191_MODULE_VERSION "0.0.5"
-
-MODULE_DESCRIPTION("Philips SAA7191 video decoder driver");
-MODULE_VERSION(SAA7191_MODULE_VERSION);
-MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>");
-MODULE_LICENSE("GPL");
-
-
-// #define SAA7191_DEBUG
-
-#ifdef SAA7191_DEBUG
-#define dprintk(x...) printk("SAA7191: " x);
-#else
-#define dprintk(x...)
-#endif
-
-#define SAA7191_SYNC_COUNT 30
-#define SAA7191_SYNC_DELAY 100 /* milliseconds */
-
-struct saa7191 {
- struct v4l2_subdev sd;
-
- /* the register values are stored here as the actual
- * I2C-registers are write-only */
- u8 reg[25];
-
- int input;
- v4l2_std_id norm;
-};
-
-static inline struct saa7191 *to_saa7191(struct v4l2_subdev *sd)
-{
- return container_of(sd, struct saa7191, sd);
-}
-
-static const u8 initseq[] = {
- 0, /* Subaddress */
-
- 0x50, /* (0x50) SAA7191_REG_IDEL */
-
- /* 50 Hz signal timing */
- 0x30, /* (0x30) SAA7191_REG_HSYB */
- 0x00, /* (0x00) SAA7191_REG_HSYS */
- 0xe8, /* (0xe8) SAA7191_REG_HCLB */
- 0xb6, /* (0xb6) SAA7191_REG_HCLS */
- 0xf4, /* (0xf4) SAA7191_REG_HPHI */
-
- /* control */
- SAA7191_LUMA_APER_1, /* (0x01) SAA7191_REG_LUMA - CVBS mode */
- 0x00, /* (0x00) SAA7191_REG_HUEC */
- 0xf8, /* (0xf8) SAA7191_REG_CKTQ */
- 0xf8, /* (0xf8) SAA7191_REG_CKTS */
- 0x90, /* (0x90) SAA7191_REG_PLSE */
- 0x90, /* (0x90) SAA7191_REG_SESE */
- 0x00, /* (0x00) SAA7191_REG_GAIN */
- SAA7191_STDC_NFEN | SAA7191_STDC_HRMV, /* (0x0c) SAA7191_REG_STDC
- * - not SECAM,
- * slow time constant */
- SAA7191_IOCK_OEDC | SAA7191_IOCK_OEHS | SAA7191_IOCK_OEVS
- | SAA7191_IOCK_OEDY, /* (0x78) SAA7191_REG_IOCK
- * - chroma from CVBS, GPSW1 & 2 off */
- SAA7191_CTL3_AUFD | SAA7191_CTL3_SCEN | SAA7191_CTL3_OFTS
- | SAA7191_CTL3_YDEL0, /* (0x99) SAA7191_REG_CTL3
- * - automatic field detection */
- 0x00, /* (0x00) SAA7191_REG_CTL4 */
- 0x2c, /* (0x2c) SAA7191_REG_CHCV - PAL nominal value */
- 0x00, /* unused */
- 0x00, /* unused */
-
- /* 60 Hz signal timing */
- 0x34, /* (0x34) SAA7191_REG_HS6B */
- 0x0a, /* (0x0a) SAA7191_REG_HS6S */
- 0xf4, /* (0xf4) SAA7191_REG_HC6B */
- 0xce, /* (0xce) SAA7191_REG_HC6S */
- 0xf4, /* (0xf4) SAA7191_REG_HP6I */
-};
-
-/* SAA7191 register handling */
-
-static u8 saa7191_read_reg(struct v4l2_subdev *sd, u8 reg)
-{
- return to_saa7191(sd)->reg[reg];
-}
-
-static int saa7191_read_status(struct v4l2_subdev *sd, u8 *value)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret;
-
- ret = i2c_master_recv(client, value, 1);
- if (ret < 0) {
- printk(KERN_ERR "SAA7191: saa7191_read_status(): read failed\n");
- return ret;
- }
-
- return 0;
-}
-
-
-static int saa7191_write_reg(struct v4l2_subdev *sd, u8 reg, u8 value)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- to_saa7191(sd)->reg[reg] = value;
- return i2c_smbus_write_byte_data(client, reg, value);
-}
-
-/* the first byte of data must be the first subaddress number (register) */
-static int saa7191_write_block(struct v4l2_subdev *sd,
- u8 length, const u8 *data)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct saa7191 *decoder = to_saa7191(sd);
- int i;
- int ret;
-
- for (i = 0; i < (length - 1); i++) {
- decoder->reg[data[0] + i] = data[i + 1];
- }
-
- ret = i2c_master_send(client, data, length);
- if (ret < 0) {
- printk(KERN_ERR "SAA7191: saa7191_write_block(): "
- "write failed\n");
- return ret;
- }
-
- return 0;
-}
-
-/* Helper functions */
-
-static int saa7191_s_routing(struct v4l2_subdev *sd,
- u32 input, u32 output, u32 config)
-{
- struct saa7191 *decoder = to_saa7191(sd);
- u8 luma = saa7191_read_reg(sd, SAA7191_REG_LUMA);
- u8 iock = saa7191_read_reg(sd, SAA7191_REG_IOCK);
- int err;
-
- switch (input) {
- case SAA7191_INPUT_COMPOSITE: /* Set Composite input */
- iock &= ~(SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW1
- | SAA7191_IOCK_GPSW2);
- /* Chrominance trap active */
- luma &= ~SAA7191_LUMA_BYPS;
- break;
- case SAA7191_INPUT_SVIDEO: /* Set S-Video input */
- iock |= SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW2;
- /* Chrominance trap bypassed */
- luma |= SAA7191_LUMA_BYPS;
- break;
- default:
- return -EINVAL;
- }
-
- err = saa7191_write_reg(sd, SAA7191_REG_LUMA, luma);
- if (err)
- return -EIO;
- err = saa7191_write_reg(sd, SAA7191_REG_IOCK, iock);
- if (err)
- return -EIO;
-
- decoder->input = input;
-
- return 0;
-}
-
-static int saa7191_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
-{
- struct saa7191 *decoder = to_saa7191(sd);
- u8 stdc = saa7191_read_reg(sd, SAA7191_REG_STDC);
- u8 ctl3 = saa7191_read_reg(sd, SAA7191_REG_CTL3);
- u8 chcv = saa7191_read_reg(sd, SAA7191_REG_CHCV);
- int err;
-
- if (norm & V4L2_STD_PAL) {
- stdc &= ~SAA7191_STDC_SECS;
- ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL);
- chcv = SAA7191_CHCV_PAL;
- } else if (norm & V4L2_STD_NTSC) {
- stdc &= ~SAA7191_STDC_SECS;
- ctl3 &= ~SAA7191_CTL3_AUFD;
- ctl3 |= SAA7191_CTL3_FSEL;
- chcv = SAA7191_CHCV_NTSC;
- } else if (norm & V4L2_STD_SECAM) {
- stdc |= SAA7191_STDC_SECS;
- ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL);
- chcv = SAA7191_CHCV_PAL;
- } else {
- return -EINVAL;
- }
-
- err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3);
- if (err)
- return -EIO;
- err = saa7191_write_reg(sd, SAA7191_REG_STDC, stdc);
- if (err)
- return -EIO;
- err = saa7191_write_reg(sd, SAA7191_REG_CHCV, chcv);
- if (err)
- return -EIO;
-
- decoder->norm = norm;
-
- dprintk("ctl3: %02x stdc: %02x chcv: %02x\n", ctl3,
- stdc, chcv);
- dprintk("norm: %llx\n", norm);
-
- return 0;
-}
-
-static int saa7191_wait_for_signal(struct v4l2_subdev *sd, u8 *status)
-{
- int i = 0;
-
- dprintk("Checking for signal...\n");
-
- for (i = 0; i < SAA7191_SYNC_COUNT; i++) {
- if (saa7191_read_status(sd, status))
- return -EIO;
-
- if (((*status) & SAA7191_STATUS_HLCK) == 0) {
- dprintk("Signal found\n");
- return 0;
- }
-
- msleep(SAA7191_SYNC_DELAY);
- }
-
- dprintk("No signal\n");
-
- return -EBUSY;
-}
-
-static int saa7191_querystd(struct v4l2_subdev *sd, v4l2_std_id *norm)
-{
- struct saa7191 *decoder = to_saa7191(sd);
- u8 stdc = saa7191_read_reg(sd, SAA7191_REG_STDC);
- u8 ctl3 = saa7191_read_reg(sd, SAA7191_REG_CTL3);
- u8 status;
- v4l2_std_id old_norm = decoder->norm;
- int err = 0;
-
- dprintk("SAA7191 extended signal auto-detection...\n");
-
- *norm &= V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM;
- stdc &= ~SAA7191_STDC_SECS;
- ctl3 &= ~(SAA7191_CTL3_FSEL);
-
- err = saa7191_write_reg(sd, SAA7191_REG_STDC, stdc);
- if (err) {
- err = -EIO;
- goto out;
- }
- err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3);
- if (err) {
- err = -EIO;
- goto out;
- }
-
- ctl3 |= SAA7191_CTL3_AUFD;
- err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3);
- if (err) {
- err = -EIO;
- goto out;
- }
-
- msleep(SAA7191_SYNC_DELAY);
-
- err = saa7191_wait_for_signal(sd, &status);
- if (err)
- goto out;
-
- if (status & SAA7191_STATUS_FIDT) {
- /* 60Hz signal -> NTSC */
- dprintk("60Hz signal: NTSC\n");
- *norm &= V4L2_STD_NTSC;
- return 0;
- }
-
- /* 50Hz signal */
- dprintk("50Hz signal: Trying PAL...\n");
-
- /* try PAL first */
- err = saa7191_s_std(sd, V4L2_STD_PAL);
- if (err)
- goto out;
-
- msleep(SAA7191_SYNC_DELAY);
-
- err = saa7191_wait_for_signal(sd, &status);
- if (err)
- goto out;
-
- /* not 50Hz ? */
- if (status & SAA7191_STATUS_FIDT) {
- dprintk("No 50Hz signal\n");
- saa7191_s_std(sd, old_norm);
- *norm = V4L2_STD_UNKNOWN;
- return 0;
- }
-
- if (status & SAA7191_STATUS_CODE) {
- dprintk("PAL\n");
- *norm &= V4L2_STD_PAL;
- return saa7191_s_std(sd, old_norm);
- }
-
- dprintk("No color detected with PAL - Trying SECAM...\n");
-
- /* no color detected ? -> try SECAM */
- err = saa7191_s_std(sd, V4L2_STD_SECAM);
- if (err)
- goto out;
-
- msleep(SAA7191_SYNC_DELAY);
-
- err = saa7191_wait_for_signal(sd, &status);
- if (err)
- goto out;
-
- /* not 50Hz ? */
- if (status & SAA7191_STATUS_FIDT) {
- dprintk("No 50Hz signal\n");
- *norm = V4L2_STD_UNKNOWN;
- goto out;
- }
-
- if (status & SAA7191_STATUS_CODE) {
- /* Color detected -> SECAM */
- dprintk("SECAM\n");
- *norm &= V4L2_STD_SECAM;
- return saa7191_s_std(sd, old_norm);
- }
-
- dprintk("No color detected with SECAM - Going back to PAL.\n");
- *norm = V4L2_STD_UNKNOWN;
-
-out:
- return saa7191_s_std(sd, old_norm);
-}
-
-static int saa7191_autodetect_norm(struct v4l2_subdev *sd)
-{
- u8 status;
-
- dprintk("SAA7191 signal auto-detection...\n");
-
- dprintk("Reading status...\n");
-
- if (saa7191_read_status(sd, &status))
- return -EIO;
-
- dprintk("Checking for signal...\n");
-
- /* no signal ? */
- if (status & SAA7191_STATUS_HLCK) {
- dprintk("No signal\n");
- return -EBUSY;
- }
-
- dprintk("Signal found\n");
-
- if (status & SAA7191_STATUS_FIDT) {
- /* 60hz signal -> NTSC */
- dprintk("NTSC\n");
- return saa7191_s_std(sd, V4L2_STD_NTSC);
- } else {
- /* 50hz signal -> PAL */
- dprintk("PAL\n");
- return saa7191_s_std(sd, V4L2_STD_PAL);
- }
-}
-
-static int saa7191_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- u8 reg;
- int ret = 0;
-
- switch (ctrl->id) {
- case SAA7191_CONTROL_BANDPASS:
- case SAA7191_CONTROL_BANDPASS_WEIGHT:
- case SAA7191_CONTROL_CORING:
- reg = saa7191_read_reg(sd, SAA7191_REG_LUMA);
- switch (ctrl->id) {
- case SAA7191_CONTROL_BANDPASS:
- ctrl->value = ((s32)reg & SAA7191_LUMA_BPSS_MASK)
- >> SAA7191_LUMA_BPSS_SHIFT;
- break;
- case SAA7191_CONTROL_BANDPASS_WEIGHT:
- ctrl->value = ((s32)reg & SAA7191_LUMA_APER_MASK)
- >> SAA7191_LUMA_APER_SHIFT;
- break;
- case SAA7191_CONTROL_CORING:
- ctrl->value = ((s32)reg & SAA7191_LUMA_CORI_MASK)
- >> SAA7191_LUMA_CORI_SHIFT;
- break;
- }
- break;
- case SAA7191_CONTROL_FORCE_COLOUR:
- case SAA7191_CONTROL_CHROMA_GAIN:
- reg = saa7191_read_reg(sd, SAA7191_REG_GAIN);
- if (ctrl->id == SAA7191_CONTROL_FORCE_COLOUR)
- ctrl->value = ((s32)reg & SAA7191_GAIN_COLO) ? 1 : 0;
- else
- ctrl->value = ((s32)reg & SAA7191_GAIN_LFIS_MASK)
- >> SAA7191_GAIN_LFIS_SHIFT;
- break;
- case V4L2_CID_HUE:
- reg = saa7191_read_reg(sd, SAA7191_REG_HUEC);
- if (reg < 0x80)
- reg += 0x80;
- else
- reg -= 0x80;
- ctrl->value = (s32)reg;
- break;
- case SAA7191_CONTROL_VTRC:
- reg = saa7191_read_reg(sd, SAA7191_REG_STDC);
- ctrl->value = ((s32)reg & SAA7191_STDC_VTRC) ? 1 : 0;
- break;
- case SAA7191_CONTROL_LUMA_DELAY:
- reg = saa7191_read_reg(sd, SAA7191_REG_CTL3);
- ctrl->value = ((s32)reg & SAA7191_CTL3_YDEL_MASK)
- >> SAA7191_CTL3_YDEL_SHIFT;
- if (ctrl->value >= 4)
- ctrl->value -= 8;
- break;
- case SAA7191_CONTROL_VNR:
- reg = saa7191_read_reg(sd, SAA7191_REG_CTL4);
- ctrl->value = ((s32)reg & SAA7191_CTL4_VNOI_MASK)
- >> SAA7191_CTL4_VNOI_SHIFT;
- break;
- default:
- ret = -EINVAL;
- }
-
- return ret;
-}
-
-static int saa7191_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- u8 reg;
- int ret = 0;
-
- switch (ctrl->id) {
- case SAA7191_CONTROL_BANDPASS:
- case SAA7191_CONTROL_BANDPASS_WEIGHT:
- case SAA7191_CONTROL_CORING:
- reg = saa7191_read_reg(sd, SAA7191_REG_LUMA);
- switch (ctrl->id) {
- case SAA7191_CONTROL_BANDPASS:
- reg &= ~SAA7191_LUMA_BPSS_MASK;
- reg |= (ctrl->value << SAA7191_LUMA_BPSS_SHIFT)
- & SAA7191_LUMA_BPSS_MASK;
- break;
- case SAA7191_CONTROL_BANDPASS_WEIGHT:
- reg &= ~SAA7191_LUMA_APER_MASK;
- reg |= (ctrl->value << SAA7191_LUMA_APER_SHIFT)
- & SAA7191_LUMA_APER_MASK;
- break;
- case SAA7191_CONTROL_CORING:
- reg &= ~SAA7191_LUMA_CORI_MASK;
- reg |= (ctrl->value << SAA7191_LUMA_CORI_SHIFT)
- & SAA7191_LUMA_CORI_MASK;
- break;
- }
- ret = saa7191_write_reg(sd, SAA7191_REG_LUMA, reg);
- break;
- case SAA7191_CONTROL_FORCE_COLOUR:
- case SAA7191_CONTROL_CHROMA_GAIN:
- reg = saa7191_read_reg(sd, SAA7191_REG_GAIN);
- if (ctrl->id == SAA7191_CONTROL_FORCE_COLOUR) {
- if (ctrl->value)
- reg |= SAA7191_GAIN_COLO;
- else
- reg &= ~SAA7191_GAIN_COLO;
- } else {
- reg &= ~SAA7191_GAIN_LFIS_MASK;
- reg |= (ctrl->value << SAA7191_GAIN_LFIS_SHIFT)
- & SAA7191_GAIN_LFIS_MASK;
- }
- ret = saa7191_write_reg(sd, SAA7191_REG_GAIN, reg);
- break;
- case V4L2_CID_HUE:
- reg = ctrl->value & 0xff;
- if (reg < 0x80)
- reg += 0x80;
- else
- reg -= 0x80;
- ret = saa7191_write_reg(sd, SAA7191_REG_HUEC, reg);
- break;
- case SAA7191_CONTROL_VTRC:
- reg = saa7191_read_reg(sd, SAA7191_REG_STDC);
- if (ctrl->value)
- reg |= SAA7191_STDC_VTRC;
- else
- reg &= ~SAA7191_STDC_VTRC;
- ret = saa7191_write_reg(sd, SAA7191_REG_STDC, reg);
- break;
- case SAA7191_CONTROL_LUMA_DELAY: {
- s32 value = ctrl->value;
- if (value < 0)
- value += 8;
- reg = saa7191_read_reg(sd, SAA7191_REG_CTL3);
- reg &= ~SAA7191_CTL3_YDEL_MASK;
- reg |= (value << SAA7191_CTL3_YDEL_SHIFT)
- & SAA7191_CTL3_YDEL_MASK;
- ret = saa7191_write_reg(sd, SAA7191_REG_CTL3, reg);
- break;
- }
- case SAA7191_CONTROL_VNR:
- reg = saa7191_read_reg(sd, SAA7191_REG_CTL4);
- reg &= ~SAA7191_CTL4_VNOI_MASK;
- reg |= (ctrl->value << SAA7191_CTL4_VNOI_SHIFT)
- & SAA7191_CTL4_VNOI_MASK;
- ret = saa7191_write_reg(sd, SAA7191_REG_CTL4, reg);
- break;
- default:
- ret = -EINVAL;
- }
-
- return ret;
-}
-
-/* I2C-interface */
-
-static int saa7191_g_input_status(struct v4l2_subdev *sd, u32 *status)
-{
- u8 status_reg;
- int res = V4L2_IN_ST_NO_SIGNAL;
-
- if (saa7191_read_status(sd, &status_reg))
- return -EIO;
- if ((status_reg & SAA7191_STATUS_HLCK) == 0)
- res = 0;
- if (!(status_reg & SAA7191_STATUS_CODE))
- res |= V4L2_IN_ST_NO_COLOR;
- *status = res;
- return 0;
-}
-
-
-/* ----------------------------------------------------------------------- */
-
-static const struct v4l2_subdev_core_ops saa7191_core_ops = {
- .g_ctrl = saa7191_g_ctrl,
- .s_ctrl = saa7191_s_ctrl,
-};
-
-static const struct v4l2_subdev_video_ops saa7191_video_ops = {
- .s_std = saa7191_s_std,
- .s_routing = saa7191_s_routing,
- .querystd = saa7191_querystd,
- .g_input_status = saa7191_g_input_status,
-};
-
-static const struct v4l2_subdev_ops saa7191_ops = {
- .core = &saa7191_core_ops,
- .video = &saa7191_video_ops,
-};
-
-static int saa7191_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- int err = 0;
- struct saa7191 *decoder;
- struct v4l2_subdev *sd;
-
- v4l_info(client, "chip found @ 0x%x (%s)\n",
- client->addr << 1, client->adapter->name);
-
- decoder = devm_kzalloc(&client->dev, sizeof(*decoder), GFP_KERNEL);
- if (!decoder)
- return -ENOMEM;
-
- sd = &decoder->sd;
- v4l2_i2c_subdev_init(sd, client, &saa7191_ops);
-
- err = saa7191_write_block(sd, sizeof(initseq), initseq);
- if (err) {
- printk(KERN_ERR "SAA7191 initialization failed\n");
- return err;
- }
-
- printk(KERN_INFO "SAA7191 initialized\n");
-
- decoder->input = SAA7191_INPUT_COMPOSITE;
- decoder->norm = V4L2_STD_PAL;
-
- err = saa7191_autodetect_norm(sd);
- if (err && (err != -EBUSY))
- printk(KERN_ERR "SAA7191: Signal auto-detection failed\n");
-
- return 0;
-}
-
-static int saa7191_remove(struct i2c_client *client)
-{
- struct v4l2_subdev *sd = i2c_get_clientdata(client);
-
- v4l2_device_unregister_subdev(sd);
- return 0;
-}
-
-static const struct i2c_device_id saa7191_id[] = {
- { "saa7191", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, saa7191_id);
-
-static struct i2c_driver saa7191_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "saa7191",
- },
- .probe = saa7191_probe,
- .remove = saa7191_remove,
- .id_table = saa7191_id,
-};
-
-module_i2c_driver(saa7191_driver);
diff --git a/drivers/media/i2c/saa7191.h b/drivers/media/i2c/saa7191.h
deleted file mode 100644
index 803c74d..0000000
--- a/drivers/media/i2c/saa7191.h
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * saa7191.h - Philips SAA7191 video decoder driver
- *
- * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
- * Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi>
- *
- * 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 _SAA7191_H_
-#define _SAA7191_H_
-
-/* Philips SAA7191 DMSD I2C bus address */
-#define SAA7191_ADDR 0x8a
-
-/* Register subaddresses. */
-#define SAA7191_REG_IDEL 0x00
-#define SAA7191_REG_HSYB 0x01
-#define SAA7191_REG_HSYS 0x02
-#define SAA7191_REG_HCLB 0x03
-#define SAA7191_REG_HCLS 0x04
-#define SAA7191_REG_HPHI 0x05
-#define SAA7191_REG_LUMA 0x06
-#define SAA7191_REG_HUEC 0x07
-#define SAA7191_REG_CKTQ 0x08 /* bits 3-7 */
-#define SAA7191_REG_CKTS 0x09 /* bits 3-7 */
-#define SAA7191_REG_PLSE 0x0a
-#define SAA7191_REG_SESE 0x0b
-#define SAA7191_REG_GAIN 0x0c
-#define SAA7191_REG_STDC 0x0d
-#define SAA7191_REG_IOCK 0x0e
-#define SAA7191_REG_CTL3 0x0f
-#define SAA7191_REG_CTL4 0x10
-#define SAA7191_REG_CHCV 0x11
-#define SAA7191_REG_HS6B 0x14
-#define SAA7191_REG_HS6S 0x15
-#define SAA7191_REG_HC6B 0x16
-#define SAA7191_REG_HC6S 0x17
-#define SAA7191_REG_HP6I 0x18
-#define SAA7191_REG_STATUS 0xff /* not really a subaddress */
-
-/* Status Register definitions */
-#define SAA7191_STATUS_CODE 0x01 /* color detected flag */
-#define SAA7191_STATUS_FIDT 0x20 /* signal type 50/60 Hz */
-#define SAA7191_STATUS_HLCK 0x40 /* PLL unlocked(1)/locked(0) */
-#define SAA7191_STATUS_STTC 0x80 /* tv/vtr time constant */
-
-/* Luminance Control Register definitions */
-/* input mode select bit:
- * 0=CVBS (chrominance trap active), 1=S-Video (trap bypassed) */
-#define SAA7191_LUMA_BYPS 0x80
-/* pre-filter (only when chrominance trap is active) */
-#define SAA7191_LUMA_PREF 0x40
-/* aperture bandpass to select different characteristics with maximums
- * (bits 4-5) */
-#define SAA7191_LUMA_BPSS_MASK 0x30
-#define SAA7191_LUMA_BPSS_SHIFT 4
-#define SAA7191_LUMA_BPSS_3 0x30
-#define SAA7191_LUMA_BPSS_2 0x20
-#define SAA7191_LUMA_BPSS_1 0x10
-#define SAA7191_LUMA_BPSS_0 0x00
-/* coring range for high frequency components according to 8-bit luminance
- * (bits 2-3)
- * 0=coring off, n= (+-)n LSB */
-#define SAA7191_LUMA_CORI_MASK 0x0c
-#define SAA7191_LUMA_CORI_SHIFT 2
-#define SAA7191_LUMA_CORI_3 0x0c
-#define SAA7191_LUMA_CORI_2 0x08
-#define SAA7191_LUMA_CORI_1 0x04
-#define SAA7191_LUMA_CORI_0 0x00
-/* aperture bandpass filter weights high frequency components of luminance
- * signal (bits 0-1)
- * 0=factor 0, 1=0.25, 2=0.5, 3=1 */
-#define SAA7191_LUMA_APER_MASK 0x03
-#define SAA7191_LUMA_APER_SHIFT 0
-#define SAA7191_LUMA_APER_3 0x03
-#define SAA7191_LUMA_APER_2 0x02
-#define SAA7191_LUMA_APER_1 0x01
-#define SAA7191_LUMA_APER_0 0x00
-
-/* Chrominance Gain Control Settings Register definitions */
-/* colour on: 0=automatic colour-killer enabled, 1=forced colour on */
-#define SAA7191_GAIN_COLO 0x80
-/* chrominance gain control (AGC filter)
- * 0=loop filter time constant slow, 1=medium, 2=fast, 3=actual gain */
-#define SAA7191_GAIN_LFIS_MASK 0x60
-#define SAA7191_GAIN_LFIS_SHIFT 5
-#define SAA7191_GAIN_LFIS_3 0x60
-#define SAA7191_GAIN_LFIS_2 0x40
-#define SAA7191_GAIN_LFIS_1 0x20
-#define SAA7191_GAIN_LFIS_0 0x00
-
-/* Standard/Mode Control Register definitions */
-/* tv/vtr mode bit: 0=TV mode (slow time constant),
- * 1=VTR mode (fast time constant) */
-#define SAA7191_STDC_VTRC 0x80
-/* SAA7191B-specific functions enable (RTCO, ODD and GPSW0 outputs)
- * 0=outputs set to high-impedance (circuit equals SAA7191), 1=enabled */
-#define SAA7191_STDC_NFEN 0x08
-/* HREF generation: 0=like SAA7191, 1=HREF is 8xLLC2 clocks earlier */
-#define SAA7191_STDC_HRMV 0x04
-/* general purpose switch 0
- * (not used with VINO afaik) */
-#define SAA7191_STDC_GPSW0 0x02
-/* SECAM mode bit: 0=other standards, 1=SECAM */
-#define SAA7191_STDC_SECS 0x01
-
-/* I/O and Clock Control Register definitions */
-/* horizontal clock PLL: 0=PLL closed,
- * 1=PLL circuit open and horizontal freq fixed */
-#define SAA7191_IOCK_HPLL 0x80
-/* colour-difference output enable (outputs UV0-UV7) */
-#define SAA7191_IOCK_OEDC 0x40
-/* H-sync output enable */
-#define SAA7191_IOCK_OEHS 0x20
-/* V-sync output enable */
-#define SAA7191_IOCK_OEVS 0x10
-/* luminance output enable (outputs Y0-Y7) */
-#define SAA7191_IOCK_OEDY 0x08
-/* S-VHS bit (chrominance from CVBS or from chrominance input):
- * 0=controlled by BYPS-bit, 1=from chrominance input */
-#define SAA7191_IOCK_CHRS 0x04
-/* general purpose switch 2
- * VINO-specific: 0=used with CVBS, 1=used with S-Video */
-#define SAA7191_IOCK_GPSW2 0x02
-/* general purpose switch 1 */
-/* VINO-specific: 0=always, 1=not used!*/
-#define SAA7191_IOCK_GPSW1 0x01
-
-/* Miscellaneous Control #1 Register definitions */
-/* automatic field detection (50/60Hz standard) */
-#define SAA7191_CTL3_AUFD 0x80
-/* field select: (if AUFD=0)
- * 0=50Hz (625 lines), 1=60Hz (525 lines) */
-#define SAA7191_CTL3_FSEL 0x40
-/* SECAM cross-colour reduction enable */
-#define SAA7191_CTL3_SXCR 0x20
-/* sync and clamping pulse enable (HCL and HSY outputs) */
-#define SAA7191_CTL3_SCEN 0x10
-/* output format: 0=4:1:1, 1=4:2:2 (4:2:2 for VINO) */
-#define SAA7191_CTL3_OFTS 0x08
-/* luminance delay compensation
- * 0=0*2/LLC, 1=+1*2/LLC, 2=+2*2/LLC, 3=+3*2/LLC,
- * 4=-4*2/LLC, 5=-3*2/LLC, 6=-2*2/LLC, 7=-1*2/LLC
- * step size = 2/LLC = 67.8ns for 50Hz, 81.5ns for 60Hz */
-#define SAA7191_CTL3_YDEL_MASK 0x07
-#define SAA7191_CTL3_YDEL_SHIFT 0
-#define SAA7191_CTL3_YDEL2 0x04
-#define SAA7191_CTL3_YDEL1 0x02
-#define SAA7191_CTL3_YDEL0 0x01
-
-/* Miscellaneous Control #2 Register definitions */
-/* select HREF position
- * 0=normal, HREF is matched to YUV output port,
- * 1=HREF is matched to CVBS input port */
-#define SAA7191_CTL4_HRFS 0x04
-/* vertical noise reduction
- * 0=normal, 1=searching window, 2=auto-deflection, 3=reduction bypassed */
-#define SAA7191_CTL4_VNOI_MASK 0x03
-#define SAA7191_CTL4_VNOI_SHIFT 0
-#define SAA7191_CTL4_VNOI_3 0x03
-#define SAA7191_CTL4_VNOI_2 0x02
-#define SAA7191_CTL4_VNOI_1 0x01
-#define SAA7191_CTL4_VNOI_0 0x00
-
-/* Chrominance Gain Control Register definitions
- * - for QAM-modulated input signals, effects output amplitude
- * (SECAM gain fixed)
- * (nominal values for UV CCIR level) */
-#define SAA7191_CHCV_NTSC 0x2c
-#define SAA7191_CHCV_PAL 0x59
-
-/* Driver interface definitions */
-#define SAA7191_INPUT_COMPOSITE 0
-#define SAA7191_INPUT_SVIDEO 1
-
-#define SAA7191_NORM_PAL 1
-#define SAA7191_NORM_NTSC 2
-#define SAA7191_NORM_SECAM 3
-
-struct saa7191_status {
- /* 0=no signal, 1=signal detected */
- int signal;
- /* 0=50hz (pal) signal, 1=60hz (ntsc) signal */
- int signal_60hz;
- /* 0=no color detected, 1=color detected */
- int color;
-
- /* current SAA7191_INPUT_ */
- int input;
- /* current SAA7191_NORM_ */
- int norm;
-};
-
-#define SAA7191_BANDPASS_MIN 0x00
-#define SAA7191_BANDPASS_MAX 0x03
-#define SAA7191_BANDPASS_DEFAULT 0x00
-
-#define SAA7191_BANDPASS_WEIGHT_MIN 0x00
-#define SAA7191_BANDPASS_WEIGHT_MAX 0x03
-#define SAA7191_BANDPASS_WEIGHT_DEFAULT 0x01
-
-#define SAA7191_CORING_MIN 0x00
-#define SAA7191_CORING_MAX 0x03
-#define SAA7191_CORING_DEFAULT 0x00
-
-#define SAA7191_HUE_MIN 0x00
-#define SAA7191_HUE_MAX 0xff
-#define SAA7191_HUE_DEFAULT 0x80
-
-#define SAA7191_VTRC_MIN 0x00
-#define SAA7191_VTRC_MAX 0x01
-#define SAA7191_VTRC_DEFAULT 0x00
-
-#define SAA7191_FORCE_COLOUR_MIN 0x00
-#define SAA7191_FORCE_COLOUR_MAX 0x01
-#define SAA7191_FORCE_COLOUR_DEFAULT 0x00
-
-#define SAA7191_CHROMA_GAIN_MIN 0x00
-#define SAA7191_CHROMA_GAIN_MAX 0x03
-#define SAA7191_CHROMA_GAIN_DEFAULT 0x00
-
-#define SAA7191_LUMA_DELAY_MIN -0x04
-#define SAA7191_LUMA_DELAY_MAX 0x03
-#define SAA7191_LUMA_DELAY_DEFAULT 0x01
-
-#define SAA7191_VNR_MIN 0x00
-#define SAA7191_VNR_MAX 0x03
-#define SAA7191_VNR_DEFAULT 0x00
-
-#define SAA7191_CONTROL_BANDPASS (V4L2_CID_PRIVATE_BASE + 0)
-#define SAA7191_CONTROL_BANDPASS_WEIGHT (V4L2_CID_PRIVATE_BASE + 1)
-#define SAA7191_CONTROL_CORING (V4L2_CID_PRIVATE_BASE + 2)
-#define SAA7191_CONTROL_FORCE_COLOUR (V4L2_CID_PRIVATE_BASE + 3)
-#define SAA7191_CONTROL_CHROMA_GAIN (V4L2_CID_PRIVATE_BASE + 4)
-#define SAA7191_CONTROL_VTRC (V4L2_CID_PRIVATE_BASE + 5)
-#define SAA7191_CONTROL_LUMA_DELAY (V4L2_CID_PRIVATE_BASE + 6)
-#define SAA7191_CONTROL_VNR (V4L2_CID_PRIVATE_BASE + 7)
-
-#define DECODER_SAA7191_GET_STATUS _IOR('d', 195, struct saa7191_status)
-#define DECODER_SAA7191_SET_NORM _IOW('d', 196, int)
-
-#endif
diff --git a/drivers/media/i2c/smiapp-pll.c b/drivers/media/i2c/smiapp-pll.c
index 2335529..e40d902 100644
--- a/drivers/media/i2c/smiapp-pll.c
+++ b/drivers/media/i2c/smiapp-pll.c
@@ -65,264 +65,36 @@
static void print_pll(struct device *dev, struct smiapp_pll *pll)
{
- dev_dbg(dev, "pre_pll_clk_div\t%d\n", pll->pre_pll_clk_div);
- dev_dbg(dev, "pll_multiplier \t%d\n", pll->pll_multiplier);
- if (pll->flags != SMIAPP_PLL_FLAG_NO_OP_CLOCKS) {
- dev_dbg(dev, "op_sys_clk_div \t%d\n", pll->op_sys_clk_div);
- dev_dbg(dev, "op_pix_clk_div \t%d\n", pll->op_pix_clk_div);
+ dev_dbg(dev, "pre_pll_clk_div\t%u\n", pll->pre_pll_clk_div);
+ dev_dbg(dev, "pll_multiplier \t%u\n", pll->pll_multiplier);
+ if (!(pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS)) {
+ dev_dbg(dev, "op_sys_clk_div \t%u\n", pll->op.sys_clk_div);
+ dev_dbg(dev, "op_pix_clk_div \t%u\n", pll->op.pix_clk_div);
}
- dev_dbg(dev, "vt_sys_clk_div \t%d\n", pll->vt_sys_clk_div);
- dev_dbg(dev, "vt_pix_clk_div \t%d\n", pll->vt_pix_clk_div);
+ dev_dbg(dev, "vt_sys_clk_div \t%u\n", pll->vt.sys_clk_div);
+ dev_dbg(dev, "vt_pix_clk_div \t%u\n", pll->vt.pix_clk_div);
- dev_dbg(dev, "ext_clk_freq_hz \t%d\n", pll->ext_clk_freq_hz);
- dev_dbg(dev, "pll_ip_clk_freq_hz \t%d\n", pll->pll_ip_clk_freq_hz);
- dev_dbg(dev, "pll_op_clk_freq_hz \t%d\n", pll->pll_op_clk_freq_hz);
- if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) {
- dev_dbg(dev, "op_sys_clk_freq_hz \t%d\n",
- pll->op_sys_clk_freq_hz);
- dev_dbg(dev, "op_pix_clk_freq_hz \t%d\n",
- pll->op_pix_clk_freq_hz);
+ dev_dbg(dev, "ext_clk_freq_hz \t%u\n", pll->ext_clk_freq_hz);
+ dev_dbg(dev, "pll_ip_clk_freq_hz \t%u\n", pll->pll_ip_clk_freq_hz);
+ dev_dbg(dev, "pll_op_clk_freq_hz \t%u\n", pll->pll_op_clk_freq_hz);
+ if (!(pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS)) {
+ dev_dbg(dev, "op_sys_clk_freq_hz \t%u\n",
+ pll->op.sys_clk_freq_hz);
+ dev_dbg(dev, "op_pix_clk_freq_hz \t%u\n",
+ pll->op.pix_clk_freq_hz);
}
- dev_dbg(dev, "vt_sys_clk_freq_hz \t%d\n", pll->vt_sys_clk_freq_hz);
- dev_dbg(dev, "vt_pix_clk_freq_hz \t%d\n", pll->vt_pix_clk_freq_hz);
+ dev_dbg(dev, "vt_sys_clk_freq_hz \t%u\n", pll->vt.sys_clk_freq_hz);
+ dev_dbg(dev, "vt_pix_clk_freq_hz \t%u\n", pll->vt.pix_clk_freq_hz);
}
-/*
- * Heuristically guess the PLL tree for a given common multiplier and
- * divisor. Begin with the operational timing and continue to video
- * timing once operational timing has been verified.
- *
- * @mul is the PLL multiplier and @div is the common divisor
- * (pre_pll_clk_div and op_sys_clk_div combined). The final PLL
- * multiplier will be a multiple of @mul.
- *
- * @return Zero on success, error code on error.
- */
-static int __smiapp_pll_calculate(struct device *dev,
- const struct smiapp_pll_limits *limits,
- struct smiapp_pll *pll, uint32_t mul,
- uint32_t div, uint32_t lane_op_clock_ratio)
+static int check_all_bounds(struct device *dev,
+ const struct smiapp_pll_limits *limits,
+ const struct smiapp_pll_branch_limits *op_limits,
+ struct smiapp_pll *pll,
+ struct smiapp_pll_branch *op_pll)
{
- uint32_t sys_div;
- uint32_t best_pix_div = INT_MAX >> 1;
- uint32_t vt_op_binning_div;
- /*
- * Higher multipliers (and divisors) are often required than
- * necessitated by the external clock and the output clocks.
- * There are limits for all values in the clock tree. These
- * are the minimum and maximum multiplier for mul.
- */
- uint32_t more_mul_min, more_mul_max;
- uint32_t more_mul_factor;
- uint32_t min_vt_div, max_vt_div, vt_div;
- uint32_t min_sys_div, max_sys_div;
- unsigned int i;
int rval;
- /*
- * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be
- * too high.
- */
- dev_dbg(dev, "pre_pll_clk_div %d\n", pll->pre_pll_clk_div);
-
- /* Don't go above max pll multiplier. */
- more_mul_max = limits->max_pll_multiplier / mul;
- dev_dbg(dev, "more_mul_max: max_pll_multiplier check: %d\n",
- more_mul_max);
- /* Don't go above max pll op frequency. */
- more_mul_max =
- min_t(uint32_t,
- more_mul_max,
- limits->max_pll_op_freq_hz
- / (pll->ext_clk_freq_hz / pll->pre_pll_clk_div * mul));
- dev_dbg(dev, "more_mul_max: max_pll_op_freq_hz check: %d\n",
- more_mul_max);
- /* Don't go above the division capability of op sys clock divider. */
- more_mul_max = min(more_mul_max,
- limits->op.max_sys_clk_div * pll->pre_pll_clk_div
- / div);
- dev_dbg(dev, "more_mul_max: max_op_sys_clk_div check: %d\n",
- more_mul_max);
- /* Ensure we won't go above min_pll_multiplier. */
- more_mul_max = min(more_mul_max,
- DIV_ROUND_UP(limits->max_pll_multiplier, mul));
- dev_dbg(dev, "more_mul_max: min_pll_multiplier check: %d\n",
- more_mul_max);
-
- /* Ensure we won't go below min_pll_op_freq_hz. */
- more_mul_min = DIV_ROUND_UP(limits->min_pll_op_freq_hz,
- pll->ext_clk_freq_hz / pll->pre_pll_clk_div
- * mul);
- dev_dbg(dev, "more_mul_min: min_pll_op_freq_hz check: %d\n",
- more_mul_min);
- /* Ensure we won't go below min_pll_multiplier. */
- more_mul_min = max(more_mul_min,
- DIV_ROUND_UP(limits->min_pll_multiplier, mul));
- dev_dbg(dev, "more_mul_min: min_pll_multiplier check: %d\n",
- more_mul_min);
-
- if (more_mul_min > more_mul_max) {
- dev_dbg(dev,
- "unable to compute more_mul_min and more_mul_max\n");
- return -EINVAL;
- }
-
- more_mul_factor = lcm(div, pll->pre_pll_clk_div) / div;
- dev_dbg(dev, "more_mul_factor: %d\n", more_mul_factor);
- more_mul_factor = lcm(more_mul_factor, limits->op.min_sys_clk_div);
- dev_dbg(dev, "more_mul_factor: min_op_sys_clk_div: %d\n",
- more_mul_factor);
- i = roundup(more_mul_min, more_mul_factor);
- if (!is_one_or_even(i))
- i <<= 1;
-
- dev_dbg(dev, "final more_mul: %d\n", i);
- if (i > more_mul_max) {
- dev_dbg(dev, "final more_mul is bad, max %d\n", more_mul_max);
- return -EINVAL;
- }
-
- pll->pll_multiplier = mul * i;
- pll->op_sys_clk_div = div * i / pll->pre_pll_clk_div;
- dev_dbg(dev, "op_sys_clk_div: %d\n", pll->op_sys_clk_div);
-
- pll->pll_ip_clk_freq_hz = pll->ext_clk_freq_hz
- / pll->pre_pll_clk_div;
-
- pll->pll_op_clk_freq_hz = pll->pll_ip_clk_freq_hz
- * pll->pll_multiplier;
-
- /* Derive pll_op_clk_freq_hz. */
- pll->op_sys_clk_freq_hz =
- pll->pll_op_clk_freq_hz / pll->op_sys_clk_div;
-
- pll->op_pix_clk_div = pll->bits_per_pixel;
- dev_dbg(dev, "op_pix_clk_div: %d\n", pll->op_pix_clk_div);
-
- pll->op_pix_clk_freq_hz =
- pll->op_sys_clk_freq_hz / pll->op_pix_clk_div;
-
- /*
- * Some sensors perform analogue binning and some do this
- * digitally. The ones doing this digitally can be roughly be
- * found out using this formula. The ones doing this digitally
- * should run at higher clock rate, so smaller divisor is used
- * on video timing side.
- */
- if (limits->min_line_length_pck_bin > limits->min_line_length_pck
- / pll->binning_horizontal)
- vt_op_binning_div = pll->binning_horizontal;
- else
- vt_op_binning_div = 1;
- dev_dbg(dev, "vt_op_binning_div: %d\n", vt_op_binning_div);
-
- /*
- * Profile 2 supports vt_pix_clk_div E [4, 10]
- *
- * Horizontal binning can be used as a base for difference in
- * divisors. One must make sure that horizontal blanking is
- * enough to accommodate the CSI-2 sync codes.
- *
- * Take scaling factor into account as well.
- *
- * Find absolute limits for the factor of vt divider.
- */
- dev_dbg(dev, "scale_m: %d\n", pll->scale_m);
- min_vt_div = DIV_ROUND_UP(pll->op_pix_clk_div * pll->op_sys_clk_div
- * pll->scale_n,
- lane_op_clock_ratio * vt_op_binning_div
- * pll->scale_m);
-
- /* Find smallest and biggest allowed vt divisor. */
- dev_dbg(dev, "min_vt_div: %d\n", min_vt_div);
- min_vt_div = max(min_vt_div,
- DIV_ROUND_UP(pll->pll_op_clk_freq_hz,
- limits->vt.max_pix_clk_freq_hz));
- dev_dbg(dev, "min_vt_div: max_vt_pix_clk_freq_hz: %d\n",
- min_vt_div);
- min_vt_div = max_t(uint32_t, min_vt_div,
- limits->vt.min_pix_clk_div
- * limits->vt.min_sys_clk_div);
- dev_dbg(dev, "min_vt_div: min_vt_clk_div: %d\n", min_vt_div);
-
- max_vt_div = limits->vt.max_sys_clk_div * limits->vt.max_pix_clk_div;
- dev_dbg(dev, "max_vt_div: %d\n", max_vt_div);
- max_vt_div = min(max_vt_div,
- DIV_ROUND_UP(pll->pll_op_clk_freq_hz,
- limits->vt.min_pix_clk_freq_hz));
- dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %d\n",
- max_vt_div);
-
- /*
- * Find limitsits for sys_clk_div. Not all values are possible
- * with all values of pix_clk_div.
- */
- min_sys_div = limits->vt.min_sys_clk_div;
- dev_dbg(dev, "min_sys_div: %d\n", min_sys_div);
- min_sys_div = max(min_sys_div,
- DIV_ROUND_UP(min_vt_div,
- limits->vt.max_pix_clk_div));
- dev_dbg(dev, "min_sys_div: max_vt_pix_clk_div: %d\n", min_sys_div);
- min_sys_div = max(min_sys_div,
- pll->pll_op_clk_freq_hz
- / limits->vt.max_sys_clk_freq_hz);
- dev_dbg(dev, "min_sys_div: max_pll_op_clk_freq_hz: %d\n", min_sys_div);
- min_sys_div = clk_div_even_up(min_sys_div);
- dev_dbg(dev, "min_sys_div: one or even: %d\n", min_sys_div);
-
- max_sys_div = limits->vt.max_sys_clk_div;
- dev_dbg(dev, "max_sys_div: %d\n", max_sys_div);
- max_sys_div = min(max_sys_div,
- DIV_ROUND_UP(max_vt_div,
- limits->vt.min_pix_clk_div));
- dev_dbg(dev, "max_sys_div: min_vt_pix_clk_div: %d\n", max_sys_div);
- max_sys_div = min(max_sys_div,
- DIV_ROUND_UP(pll->pll_op_clk_freq_hz,
- limits->vt.min_pix_clk_freq_hz));
- dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %d\n", max_sys_div);
-
- /*
- * Find pix_div such that a legal pix_div * sys_div results
- * into a value which is not smaller than div, the desired
- * divisor.
- */
- for (vt_div = min_vt_div; vt_div <= max_vt_div;
- vt_div += 2 - (vt_div & 1)) {
- for (sys_div = min_sys_div;
- sys_div <= max_sys_div;
- sys_div += 2 - (sys_div & 1)) {
- uint16_t pix_div = DIV_ROUND_UP(vt_div, sys_div);
-
- if (pix_div < limits->vt.min_pix_clk_div
- || pix_div > limits->vt.max_pix_clk_div) {
- dev_dbg(dev,
- "pix_div %d too small or too big (%d--%d)\n",
- pix_div,
- limits->vt.min_pix_clk_div,
- limits->vt.max_pix_clk_div);
- continue;
- }
-
- /* Check if this one is better. */
- if (pix_div * sys_div
- <= roundup(min_vt_div, best_pix_div))
- best_pix_div = pix_div;
- }
- if (best_pix_div < INT_MAX >> 1)
- break;
- }
-
- pll->vt_sys_clk_div = DIV_ROUND_UP(min_vt_div, best_pix_div);
- pll->vt_pix_clk_div = best_pix_div;
-
- pll->vt_sys_clk_freq_hz =
- pll->pll_op_clk_freq_hz / pll->vt_sys_clk_div;
- pll->vt_pix_clk_freq_hz =
- pll->vt_sys_clk_freq_hz / pll->vt_pix_clk_div;
-
- pll->pixel_rate_csi =
- pll->op_pix_clk_freq_hz * lane_op_clock_ratio;
-
rval = bounds_check(dev, pll->pll_ip_clk_freq_hz,
limits->min_pll_ip_freq_hz,
limits->max_pll_ip_freq_hz,
@@ -339,35 +111,38 @@
"pll_op_clk_freq_hz");
if (!rval)
rval = bounds_check(
- dev, pll->op_sys_clk_div,
- limits->op.min_sys_clk_div, limits->op.max_sys_clk_div,
+ dev, op_pll->sys_clk_div,
+ op_limits->min_sys_clk_div, op_limits->max_sys_clk_div,
"op_sys_clk_div");
if (!rval)
rval = bounds_check(
- dev, pll->op_pix_clk_div,
- limits->op.min_pix_clk_div, limits->op.max_pix_clk_div,
- "op_pix_clk_div");
- if (!rval)
- rval = bounds_check(
- dev, pll->op_sys_clk_freq_hz,
- limits->op.min_sys_clk_freq_hz,
- limits->op.max_sys_clk_freq_hz,
+ dev, op_pll->sys_clk_freq_hz,
+ op_limits->min_sys_clk_freq_hz,
+ op_limits->max_sys_clk_freq_hz,
"op_sys_clk_freq_hz");
if (!rval)
rval = bounds_check(
- dev, pll->op_pix_clk_freq_hz,
- limits->op.min_pix_clk_freq_hz,
- limits->op.max_pix_clk_freq_hz,
+ dev, op_pll->pix_clk_freq_hz,
+ op_limits->min_pix_clk_freq_hz,
+ op_limits->max_pix_clk_freq_hz,
"op_pix_clk_freq_hz");
+
+ /*
+ * If there are no OP clocks, the VT clocks are contained in
+ * the OP clock struct.
+ */
+ if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS)
+ return rval;
+
if (!rval)
rval = bounds_check(
- dev, pll->vt_sys_clk_freq_hz,
+ dev, pll->vt.sys_clk_freq_hz,
limits->vt.min_sys_clk_freq_hz,
limits->vt.max_sys_clk_freq_hz,
"vt_sys_clk_freq_hz");
if (!rval)
rval = bounds_check(
- dev, pll->vt_pix_clk_freq_hz,
+ dev, pll->vt.pix_clk_freq_hz,
limits->vt.min_pix_clk_freq_hz,
limits->vt.max_pix_clk_freq_hz,
"vt_pix_clk_freq_hz");
@@ -375,10 +150,258 @@
return rval;
}
+/*
+ * Heuristically guess the PLL tree for a given common multiplier and
+ * divisor. Begin with the operational timing and continue to video
+ * timing once operational timing has been verified.
+ *
+ * @mul is the PLL multiplier and @div is the common divisor
+ * (pre_pll_clk_div and op_sys_clk_div combined). The final PLL
+ * multiplier will be a multiple of @mul.
+ *
+ * @return Zero on success, error code on error.
+ */
+static int __smiapp_pll_calculate(
+ struct device *dev, const struct smiapp_pll_limits *limits,
+ const struct smiapp_pll_branch_limits *op_limits,
+ struct smiapp_pll *pll, struct smiapp_pll_branch *op_pll, uint32_t mul,
+ uint32_t div, uint32_t lane_op_clock_ratio)
+{
+ uint32_t sys_div;
+ uint32_t best_pix_div = INT_MAX >> 1;
+ uint32_t vt_op_binning_div;
+ /*
+ * Higher multipliers (and divisors) are often required than
+ * necessitated by the external clock and the output clocks.
+ * There are limits for all values in the clock tree. These
+ * are the minimum and maximum multiplier for mul.
+ */
+ uint32_t more_mul_min, more_mul_max;
+ uint32_t more_mul_factor;
+ uint32_t min_vt_div, max_vt_div, vt_div;
+ uint32_t min_sys_div, max_sys_div;
+ unsigned int i;
+
+ /*
+ * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be
+ * too high.
+ */
+ dev_dbg(dev, "pre_pll_clk_div %u\n", pll->pre_pll_clk_div);
+
+ /* Don't go above max pll multiplier. */
+ more_mul_max = limits->max_pll_multiplier / mul;
+ dev_dbg(dev, "more_mul_max: max_pll_multiplier check: %u\n",
+ more_mul_max);
+ /* Don't go above max pll op frequency. */
+ more_mul_max =
+ min_t(uint32_t,
+ more_mul_max,
+ limits->max_pll_op_freq_hz
+ / (pll->ext_clk_freq_hz / pll->pre_pll_clk_div * mul));
+ dev_dbg(dev, "more_mul_max: max_pll_op_freq_hz check: %u\n",
+ more_mul_max);
+ /* Don't go above the division capability of op sys clock divider. */
+ more_mul_max = min(more_mul_max,
+ op_limits->max_sys_clk_div * pll->pre_pll_clk_div
+ / div);
+ dev_dbg(dev, "more_mul_max: max_op_sys_clk_div check: %u\n",
+ more_mul_max);
+ /* Ensure we won't go above min_pll_multiplier. */
+ more_mul_max = min(more_mul_max,
+ DIV_ROUND_UP(limits->max_pll_multiplier, mul));
+ dev_dbg(dev, "more_mul_max: min_pll_multiplier check: %u\n",
+ more_mul_max);
+
+ /* Ensure we won't go below min_pll_op_freq_hz. */
+ more_mul_min = DIV_ROUND_UP(limits->min_pll_op_freq_hz,
+ pll->ext_clk_freq_hz / pll->pre_pll_clk_div
+ * mul);
+ dev_dbg(dev, "more_mul_min: min_pll_op_freq_hz check: %u\n",
+ more_mul_min);
+ /* Ensure we won't go below min_pll_multiplier. */
+ more_mul_min = max(more_mul_min,
+ DIV_ROUND_UP(limits->min_pll_multiplier, mul));
+ dev_dbg(dev, "more_mul_min: min_pll_multiplier check: %u\n",
+ more_mul_min);
+
+ if (more_mul_min > more_mul_max) {
+ dev_dbg(dev,
+ "unable to compute more_mul_min and more_mul_max\n");
+ return -EINVAL;
+ }
+
+ more_mul_factor = lcm(div, pll->pre_pll_clk_div) / div;
+ dev_dbg(dev, "more_mul_factor: %u\n", more_mul_factor);
+ more_mul_factor = lcm(more_mul_factor, op_limits->min_sys_clk_div);
+ dev_dbg(dev, "more_mul_factor: min_op_sys_clk_div: %d\n",
+ more_mul_factor);
+ i = roundup(more_mul_min, more_mul_factor);
+ if (!is_one_or_even(i))
+ i <<= 1;
+
+ dev_dbg(dev, "final more_mul: %u\n", i);
+ if (i > more_mul_max) {
+ dev_dbg(dev, "final more_mul is bad, max %u\n", more_mul_max);
+ return -EINVAL;
+ }
+
+ pll->pll_multiplier = mul * i;
+ op_pll->sys_clk_div = div * i / pll->pre_pll_clk_div;
+ dev_dbg(dev, "op_sys_clk_div: %u\n", op_pll->sys_clk_div);
+
+ pll->pll_ip_clk_freq_hz = pll->ext_clk_freq_hz
+ / pll->pre_pll_clk_div;
+
+ pll->pll_op_clk_freq_hz = pll->pll_ip_clk_freq_hz
+ * pll->pll_multiplier;
+
+ /* Derive pll_op_clk_freq_hz. */
+ op_pll->sys_clk_freq_hz =
+ pll->pll_op_clk_freq_hz / op_pll->sys_clk_div;
+
+ op_pll->pix_clk_div = pll->bits_per_pixel;
+ dev_dbg(dev, "op_pix_clk_div: %u\n", op_pll->pix_clk_div);
+
+ op_pll->pix_clk_freq_hz =
+ op_pll->sys_clk_freq_hz / op_pll->pix_clk_div;
+
+ if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) {
+ /* No OP clocks --- VT clocks are used instead. */
+ goto out_skip_vt_calc;
+ }
+
+ /*
+ * Some sensors perform analogue binning and some do this
+ * digitally. The ones doing this digitally can be roughly be
+ * found out using this formula. The ones doing this digitally
+ * should run at higher clock rate, so smaller divisor is used
+ * on video timing side.
+ */
+ if (limits->min_line_length_pck_bin > limits->min_line_length_pck
+ / pll->binning_horizontal)
+ vt_op_binning_div = pll->binning_horizontal;
+ else
+ vt_op_binning_div = 1;
+ dev_dbg(dev, "vt_op_binning_div: %u\n", vt_op_binning_div);
+
+ /*
+ * Profile 2 supports vt_pix_clk_div E [4, 10]
+ *
+ * Horizontal binning can be used as a base for difference in
+ * divisors. One must make sure that horizontal blanking is
+ * enough to accommodate the CSI-2 sync codes.
+ *
+ * Take scaling factor into account as well.
+ *
+ * Find absolute limits for the factor of vt divider.
+ */
+ dev_dbg(dev, "scale_m: %u\n", pll->scale_m);
+ min_vt_div = DIV_ROUND_UP(op_pll->pix_clk_div * op_pll->sys_clk_div
+ * pll->scale_n,
+ lane_op_clock_ratio * vt_op_binning_div
+ * pll->scale_m);
+
+ /* Find smallest and biggest allowed vt divisor. */
+ dev_dbg(dev, "min_vt_div: %u\n", min_vt_div);
+ min_vt_div = max(min_vt_div,
+ DIV_ROUND_UP(pll->pll_op_clk_freq_hz,
+ limits->vt.max_pix_clk_freq_hz));
+ dev_dbg(dev, "min_vt_div: max_vt_pix_clk_freq_hz: %u\n",
+ min_vt_div);
+ min_vt_div = max_t(uint32_t, min_vt_div,
+ limits->vt.min_pix_clk_div
+ * limits->vt.min_sys_clk_div);
+ dev_dbg(dev, "min_vt_div: min_vt_clk_div: %u\n", min_vt_div);
+
+ max_vt_div = limits->vt.max_sys_clk_div * limits->vt.max_pix_clk_div;
+ dev_dbg(dev, "max_vt_div: %u\n", max_vt_div);
+ max_vt_div = min(max_vt_div,
+ DIV_ROUND_UP(pll->pll_op_clk_freq_hz,
+ limits->vt.min_pix_clk_freq_hz));
+ dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %u\n",
+ max_vt_div);
+
+ /*
+ * Find limitsits for sys_clk_div. Not all values are possible
+ * with all values of pix_clk_div.
+ */
+ min_sys_div = limits->vt.min_sys_clk_div;
+ dev_dbg(dev, "min_sys_div: %u\n", min_sys_div);
+ min_sys_div = max(min_sys_div,
+ DIV_ROUND_UP(min_vt_div,
+ limits->vt.max_pix_clk_div));
+ dev_dbg(dev, "min_sys_div: max_vt_pix_clk_div: %u\n", min_sys_div);
+ min_sys_div = max(min_sys_div,
+ pll->pll_op_clk_freq_hz
+ / limits->vt.max_sys_clk_freq_hz);
+ dev_dbg(dev, "min_sys_div: max_pll_op_clk_freq_hz: %u\n", min_sys_div);
+ min_sys_div = clk_div_even_up(min_sys_div);
+ dev_dbg(dev, "min_sys_div: one or even: %u\n", min_sys_div);
+
+ max_sys_div = limits->vt.max_sys_clk_div;
+ dev_dbg(dev, "max_sys_div: %u\n", max_sys_div);
+ max_sys_div = min(max_sys_div,
+ DIV_ROUND_UP(max_vt_div,
+ limits->vt.min_pix_clk_div));
+ dev_dbg(dev, "max_sys_div: min_vt_pix_clk_div: %u\n", max_sys_div);
+ max_sys_div = min(max_sys_div,
+ DIV_ROUND_UP(pll->pll_op_clk_freq_hz,
+ limits->vt.min_pix_clk_freq_hz));
+ dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %u\n", max_sys_div);
+
+ /*
+ * Find pix_div such that a legal pix_div * sys_div results
+ * into a value which is not smaller than div, the desired
+ * divisor.
+ */
+ for (vt_div = min_vt_div; vt_div <= max_vt_div;
+ vt_div += 2 - (vt_div & 1)) {
+ for (sys_div = min_sys_div;
+ sys_div <= max_sys_div;
+ sys_div += 2 - (sys_div & 1)) {
+ uint16_t pix_div = DIV_ROUND_UP(vt_div, sys_div);
+
+ if (pix_div < limits->vt.min_pix_clk_div
+ || pix_div > limits->vt.max_pix_clk_div) {
+ dev_dbg(dev,
+ "pix_div %u too small or too big (%u--%u)\n",
+ pix_div,
+ limits->vt.min_pix_clk_div,
+ limits->vt.max_pix_clk_div);
+ continue;
+ }
+
+ /* Check if this one is better. */
+ if (pix_div * sys_div
+ <= roundup(min_vt_div, best_pix_div))
+ best_pix_div = pix_div;
+ }
+ if (best_pix_div < INT_MAX >> 1)
+ break;
+ }
+
+ pll->vt.sys_clk_div = DIV_ROUND_UP(min_vt_div, best_pix_div);
+ pll->vt.pix_clk_div = best_pix_div;
+
+ pll->vt.sys_clk_freq_hz =
+ pll->pll_op_clk_freq_hz / pll->vt.sys_clk_div;
+ pll->vt.pix_clk_freq_hz =
+ pll->vt.sys_clk_freq_hz / pll->vt.pix_clk_div;
+
+out_skip_vt_calc:
+ pll->pixel_rate_csi =
+ op_pll->pix_clk_freq_hz * lane_op_clock_ratio;
+ pll->pixel_rate_pixel_array = pll->vt.pix_clk_freq_hz;
+
+ return check_all_bounds(dev, limits, op_limits, pll, op_pll);
+}
+
int smiapp_pll_calculate(struct device *dev,
const struct smiapp_pll_limits *limits,
struct smiapp_pll *pll)
{
+ const struct smiapp_pll_branch_limits *op_limits = &limits->op;
+ struct smiapp_pll_branch *op_pll = &pll->op;
uint16_t min_pre_pll_clk_div;
uint16_t max_pre_pll_clk_div;
uint32_t lane_op_clock_ratio;
@@ -386,13 +409,23 @@
unsigned int i;
int rval = -EINVAL;
+ if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) {
+ /*
+ * If there's no OP PLL at all, use the VT values
+ * instead. The OP values are ignored for the rest of
+ * the PLL calculation.
+ */
+ op_limits = &limits->vt;
+ op_pll = &pll->vt;
+ }
+
if (pll->flags & SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE)
lane_op_clock_ratio = pll->csi2.lanes;
else
lane_op_clock_ratio = 1;
- dev_dbg(dev, "lane_op_clock_ratio: %d\n", lane_op_clock_ratio);
+ dev_dbg(dev, "lane_op_clock_ratio: %u\n", lane_op_clock_ratio);
- dev_dbg(dev, "binning: %dx%d\n", pll->binning_horizontal,
+ dev_dbg(dev, "binning: %ux%u\n", pll->binning_horizontal,
pll->binning_vertical);
switch (pll->bus_type) {
@@ -411,7 +444,7 @@
}
/* Figure out limits for pre-pll divider based on extclk */
- dev_dbg(dev, "min / max pre_pll_clk_div: %d / %d\n",
+ dev_dbg(dev, "min / max pre_pll_clk_div: %u / %u\n",
limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div);
max_pre_pll_clk_div =
min_t(uint16_t, limits->max_pre_pll_clk_div,
@@ -422,26 +455,27 @@
clk_div_even_up(
DIV_ROUND_UP(pll->ext_clk_freq_hz,
limits->max_pll_ip_freq_hz)));
- dev_dbg(dev, "pre-pll check: min / max pre_pll_clk_div: %d / %d\n",
+ dev_dbg(dev, "pre-pll check: min / max pre_pll_clk_div: %u / %u\n",
min_pre_pll_clk_div, max_pre_pll_clk_div);
i = gcd(pll->pll_op_clk_freq_hz, pll->ext_clk_freq_hz);
mul = div_u64(pll->pll_op_clk_freq_hz, i);
div = pll->ext_clk_freq_hz / i;
- dev_dbg(dev, "mul %d / div %d\n", mul, div);
+ dev_dbg(dev, "mul %u / div %u\n", mul, div);
min_pre_pll_clk_div =
max_t(uint16_t, min_pre_pll_clk_div,
clk_div_even_up(
DIV_ROUND_UP(mul * pll->ext_clk_freq_hz,
limits->max_pll_op_freq_hz)));
- dev_dbg(dev, "pll_op check: min / max pre_pll_clk_div: %d / %d\n",
+ dev_dbg(dev, "pll_op check: min / max pre_pll_clk_div: %u / %u\n",
min_pre_pll_clk_div, max_pre_pll_clk_div);
for (pll->pre_pll_clk_div = min_pre_pll_clk_div;
pll->pre_pll_clk_div <= max_pre_pll_clk_div;
pll->pre_pll_clk_div += 2 - (pll->pre_pll_clk_div & 1)) {
- rval = __smiapp_pll_calculate(dev, limits, pll, mul, div,
+ rval = __smiapp_pll_calculate(dev, limits, op_limits, pll,
+ op_pll, mul, div,
lane_op_clock_ratio);
if (rval)
continue;
diff --git a/drivers/media/i2c/smiapp-pll.h b/drivers/media/i2c/smiapp-pll.h
index 5ce2b61..e8f035a 100644
--- a/drivers/media/i2c/smiapp-pll.h
+++ b/drivers/media/i2c/smiapp-pll.h
@@ -35,6 +35,13 @@
#define SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0)
#define SMIAPP_PLL_FLAG_NO_OP_CLOCKS (1 << 1)
+struct smiapp_pll_branch {
+ uint16_t sys_clk_div;
+ uint16_t pix_clk_div;
+ uint32_t sys_clk_freq_hz;
+ uint32_t pix_clk_freq_hz;
+};
+
struct smiapp_pll {
/* input values */
uint8_t bus_type;
@@ -53,24 +60,18 @@
uint8_t scale_n;
uint8_t bits_per_pixel;
uint32_t link_freq;
+ uint32_t ext_clk_freq_hz;
/* output values */
uint16_t pre_pll_clk_div;
uint16_t pll_multiplier;
- uint16_t op_sys_clk_div;
- uint16_t op_pix_clk_div;
- uint16_t vt_sys_clk_div;
- uint16_t vt_pix_clk_div;
-
- uint32_t ext_clk_freq_hz;
uint32_t pll_ip_clk_freq_hz;
uint32_t pll_op_clk_freq_hz;
- uint32_t op_sys_clk_freq_hz;
- uint32_t op_pix_clk_freq_hz;
- uint32_t vt_sys_clk_freq_hz;
- uint32_t vt_pix_clk_freq_hz;
+ struct smiapp_pll_branch vt;
+ struct smiapp_pll_branch op;
uint32_t pixel_rate_csi;
+ uint32_t pixel_rate_pixel_array;
};
struct smiapp_pll_branch_limits {
diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c
index 932ed9b..0df5070 100644
--- a/drivers/media/i2c/smiapp/smiapp-core.c
+++ b/drivers/media/i2c/smiapp/smiapp-core.c
@@ -205,12 +205,12 @@
int rval;
rval = smiapp_write(
- sensor, SMIAPP_REG_U16_VT_PIX_CLK_DIV, pll->vt_pix_clk_div);
+ sensor, SMIAPP_REG_U16_VT_PIX_CLK_DIV, pll->vt.pix_clk_div);
if (rval < 0)
return rval;
rval = smiapp_write(
- sensor, SMIAPP_REG_U16_VT_SYS_CLK_DIV, pll->vt_sys_clk_div);
+ sensor, SMIAPP_REG_U16_VT_SYS_CLK_DIV, pll->vt.sys_clk_div);
if (rval < 0)
return rval;
@@ -227,20 +227,21 @@
/* Lane op clock ratio does not apply here. */
rval = smiapp_write(
sensor, SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS,
- DIV_ROUND_UP(pll->op_sys_clk_freq_hz, 1000000 / 256 / 256));
+ DIV_ROUND_UP(pll->op.sys_clk_freq_hz, 1000000 / 256 / 256));
if (rval < 0 || sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0)
return rval;
rval = smiapp_write(
- sensor, SMIAPP_REG_U16_OP_PIX_CLK_DIV, pll->op_pix_clk_div);
+ sensor, SMIAPP_REG_U16_OP_PIX_CLK_DIV, pll->op.pix_clk_div);
if (rval < 0)
return rval;
return smiapp_write(
- sensor, SMIAPP_REG_U16_OP_SYS_CLK_DIV, pll->op_sys_clk_div);
+ sensor, SMIAPP_REG_U16_OP_SYS_CLK_DIV, pll->op.sys_clk_div);
}
-static int smiapp_pll_update(struct smiapp_sensor *sensor)
+static int smiapp_pll_try(struct smiapp_sensor *sensor,
+ struct smiapp_pll *pll)
{
struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
struct smiapp_pll_limits lim = {
@@ -274,19 +275,15 @@
.min_line_length_pck_bin = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN],
.min_line_length_pck = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK],
};
+
+ return smiapp_pll_calculate(&client->dev, &lim, pll);
+}
+
+static int smiapp_pll_update(struct smiapp_sensor *sensor)
+{
struct smiapp_pll *pll = &sensor->pll;
int rval;
- if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) {
- /*
- * Fill in operational clock divisors limits from the
- * video timing ones. On profile 0 sensors the
- * requirements regarding them are essentially the
- * same as on VT ones.
- */
- lim.op = lim.vt;
- }
-
pll->binning_horizontal = sensor->binning_horizontal;
pll->binning_vertical = sensor->binning_vertical;
pll->link_freq =
@@ -294,12 +291,12 @@
pll->scale_m = sensor->scale_m;
pll->bits_per_pixel = sensor->csi_format->compressed;
- rval = smiapp_pll_calculate(&client->dev, &lim, pll);
+ rval = smiapp_pll_try(sensor, pll);
if (rval < 0)
return rval;
__v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate_parray,
- pll->vt_pix_clk_freq_hz);
+ pll->pixel_rate_pixel_array);
__v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate_csi, pll->pixel_rate_csi);
return 0;
@@ -333,22 +330,22 @@
* orders must be defined.
*/
static const struct smiapp_csi_data_format smiapp_csi_data_formats[] = {
- { V4L2_MBUS_FMT_SGRBG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GRBG, },
- { V4L2_MBUS_FMT_SRGGB12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_RGGB, },
- { V4L2_MBUS_FMT_SBGGR12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_BGGR, },
- { V4L2_MBUS_FMT_SGBRG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GBRG, },
- { V4L2_MBUS_FMT_SGRBG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GRBG, },
- { V4L2_MBUS_FMT_SRGGB10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_RGGB, },
- { V4L2_MBUS_FMT_SBGGR10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_BGGR, },
- { V4L2_MBUS_FMT_SGBRG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GBRG, },
- { V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GRBG, },
- { V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_RGGB, },
- { V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_BGGR, },
- { V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GBRG, },
- { V4L2_MBUS_FMT_SGRBG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GRBG, },
- { V4L2_MBUS_FMT_SRGGB8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_RGGB, },
- { V4L2_MBUS_FMT_SBGGR8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_BGGR, },
- { V4L2_MBUS_FMT_SGBRG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GBRG, },
+ { MEDIA_BUS_FMT_SGRBG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GRBG, },
+ { MEDIA_BUS_FMT_SRGGB12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_RGGB, },
+ { MEDIA_BUS_FMT_SBGGR12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_BGGR, },
+ { MEDIA_BUS_FMT_SGBRG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GBRG, },
+ { MEDIA_BUS_FMT_SGRBG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GRBG, },
+ { MEDIA_BUS_FMT_SRGGB10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_RGGB, },
+ { MEDIA_BUS_FMT_SBGGR10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_BGGR, },
+ { MEDIA_BUS_FMT_SGBRG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GBRG, },
+ { MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GRBG, },
+ { MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_RGGB, },
+ { MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_BGGR, },
+ { MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GBRG, },
+ { MEDIA_BUS_FMT_SGRBG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GRBG, },
+ { MEDIA_BUS_FMT_SRGGB8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_RGGB, },
+ { MEDIA_BUS_FMT_SBGGR8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_BGGR, },
+ { MEDIA_BUS_FMT_SGBRG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GBRG, },
};
const char *pixel_order_str[] = { "GRBG", "RGGB", "BGGR", "GBRG" };
@@ -526,6 +523,8 @@
static int smiapp_init_controls(struct smiapp_sensor *sensor)
{
struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ unsigned long *valid_link_freqs = &sensor->valid_link_freqs[
+ sensor->csi_format->compressed - SMIAPP_COMPRESSED_BASE];
unsigned int max, i;
int rval;
@@ -608,8 +607,8 @@
sensor->link_freq = v4l2_ctrl_new_int_menu(
&sensor->src->ctrl_handler, &smiapp_ctrl_ops,
- V4L2_CID_LINK_FREQ, max, 0,
- sensor->platform_data->op_sys_clock);
+ V4L2_CID_LINK_FREQ, __fls(*valid_link_freqs),
+ __ffs(*valid_link_freqs), sensor->platform_data->op_sys_clock);
sensor->pixel_rate_csi = v4l2_ctrl_new_std(
&sensor->src->ctrl_handler, &smiapp_ctrl_ops,
@@ -745,6 +744,7 @@
static int smiapp_get_mbus_formats(struct smiapp_sensor *sensor)
{
struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ struct smiapp_pll *pll = &sensor->pll;
unsigned int type, n;
unsigned int i, pixel_order;
int rval;
@@ -808,14 +808,57 @@
dev_dbg(&client->dev, "jolly good! %d\n", j);
sensor->default_mbus_frame_fmts |= 1 << j;
- if (!sensor->csi_format
- || f->width > sensor->csi_format->width
- || (f->width == sensor->csi_format->width
- && f->compressed
- > sensor->csi_format->compressed)) {
- sensor->csi_format = f;
- sensor->internal_csi_format = f;
- }
+ }
+ }
+
+ /* Figure out which BPP values can be used with which formats. */
+ pll->binning_horizontal = 1;
+ pll->binning_vertical = 1;
+ pll->scale_m = sensor->scale_m;
+
+ for (i = 0; i < ARRAY_SIZE(smiapp_csi_data_formats); i++) {
+ const struct smiapp_csi_data_format *f =
+ &smiapp_csi_data_formats[i];
+ unsigned long *valid_link_freqs =
+ &sensor->valid_link_freqs[
+ f->compressed - SMIAPP_COMPRESSED_BASE];
+ unsigned int j;
+
+ BUG_ON(f->compressed < SMIAPP_COMPRESSED_BASE);
+ BUG_ON(f->compressed > SMIAPP_COMPRESSED_MAX);
+
+ if (!(sensor->default_mbus_frame_fmts & 1 << i))
+ continue;
+
+ pll->bits_per_pixel = f->compressed;
+
+ for (j = 0; sensor->platform_data->op_sys_clock[j]; j++) {
+ pll->link_freq = sensor->platform_data->op_sys_clock[j];
+
+ rval = smiapp_pll_try(sensor, pll);
+ dev_dbg(&client->dev, "link freq %u Hz, bpp %u %s\n",
+ pll->link_freq, pll->bits_per_pixel,
+ rval ? "not ok" : "ok");
+ if (rval)
+ continue;
+
+ set_bit(j, valid_link_freqs);
+ }
+
+ if (!*valid_link_freqs) {
+ dev_info(&client->dev,
+ "no valid link frequencies for %u bpp\n",
+ f->compressed);
+ sensor->default_mbus_frame_fmts &= ~BIT(i);
+ continue;
+ }
+
+ if (!sensor->csi_format
+ || f->width > sensor->csi_format->width
+ || (f->width == sensor->csi_format->width
+ && f->compressed > sensor->csi_format->compressed)) {
+ sensor->csi_format = f;
+ sensor->internal_csi_format = f;
}
}
@@ -904,7 +947,7 @@
dev_dbg(&client->dev, "hblank\t\t%d\n", sensor->hblank->val);
dev_dbg(&client->dev, "real timeperframe\t100/%d\n",
- sensor->pll.vt_pix_clk_freq_hz /
+ sensor->pll.pixel_rate_pixel_array /
((sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width
+ sensor->hblank->val) *
(sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height
@@ -1687,6 +1730,59 @@
return csi_format;
}
+static int smiapp_set_format_source(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ const struct smiapp_csi_data_format *csi_format,
+ *old_csi_format = sensor->csi_format;
+ unsigned long *valid_link_freqs;
+ u32 code = fmt->format.code;
+ unsigned int i;
+ int rval;
+
+ rval = __smiapp_get_format(subdev, fh, fmt);
+ if (rval)
+ return rval;
+
+ /*
+ * Media bus code is changeable on src subdev's source pad. On
+ * other source pads we just get format here.
+ */
+ if (subdev != &sensor->src->sd)
+ return 0;
+
+ csi_format = smiapp_validate_csi_data_format(sensor, code);
+
+ fmt->format.code = csi_format->code;
+
+ if (fmt->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return 0;
+
+ sensor->csi_format = csi_format;
+
+ if (csi_format->width != old_csi_format->width)
+ for (i = 0; i < ARRAY_SIZE(sensor->test_data); i++)
+ __v4l2_ctrl_modify_range(
+ sensor->test_data[i], 0,
+ (1 << csi_format->width) - 1, 1, 0);
+
+ if (csi_format->compressed == old_csi_format->compressed)
+ return 0;
+
+ valid_link_freqs =
+ &sensor->valid_link_freqs[sensor->csi_format->compressed
+ - SMIAPP_COMPRESSED_BASE];
+
+ __v4l2_ctrl_modify_range(
+ sensor->link_freq, 0,
+ __fls(*valid_link_freqs), ~*valid_link_freqs,
+ __ffs(*valid_link_freqs));
+
+ return smiapp_pll_update(sensor);
+}
+
static int smiapp_set_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
@@ -1697,41 +1793,14 @@
mutex_lock(&sensor->mutex);
- /*
- * Media bus code is changeable on src subdev's source pad. On
- * other source pads we just get format here.
- */
if (fmt->pad == ssd->source_pad) {
- u32 code = fmt->format.code;
- int rval = __smiapp_get_format(subdev, fh, fmt);
- bool range_changed = false;
- unsigned int i;
+ int rval;
- if (!rval && subdev == &sensor->src->sd) {
- const struct smiapp_csi_data_format *csi_format =
- smiapp_validate_csi_data_format(sensor, code);
-
- if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
- if (csi_format->width !=
- sensor->csi_format->width)
- range_changed = true;
-
- sensor->csi_format = csi_format;
- }
-
- fmt->format.code = csi_format->code;
- }
+ rval = smiapp_set_format_source(subdev, fh, fmt);
mutex_unlock(&sensor->mutex);
- if (rval || !range_changed)
- return rval;
- for (i = 0; i < ARRAY_SIZE(sensor->test_data); i++)
- v4l2_ctrl_modify_range(
- sensor->test_data[i],
- 0, (1 << sensor->csi_format->width) - 1, 1, 0);
-
- return 0;
+ return rval;
}
/* Sink pad. Width and height are changeable here. */
@@ -2023,6 +2092,11 @@
== SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP)
return 0;
return -EINVAL;
+ case V4L2_SEL_TGT_NATIVE_SIZE:
+ if (ssd == sensor->pixel_array
+ && sel->pad == SMIAPP_PA_PAD_SRC)
+ return 0;
+ return -EINVAL;
case V4L2_SEL_TGT_COMPOSE:
case V4L2_SEL_TGT_COMPOSE_BOUNDS:
if (sel->pad == ssd->source_pad)
@@ -2121,7 +2195,9 @@
switch (sel->target) {
case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_NATIVE_SIZE:
if (ssd == sensor->pixel_array) {
+ sel->r.left = sel->r.top = 0;
sel->r.width =
sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1;
sel->r.height =
@@ -2190,7 +2266,7 @@
ret = smiapp_set_compose(subdev, fh, sel);
break;
default:
- BUG();
+ ret = -EINVAL;
}
mutex_unlock(&sensor->mutex);
@@ -2482,12 +2558,6 @@
goto out_power_off;
}
- rval = smiapp_get_mbus_formats(sensor);
- if (rval) {
- rval = -ENODEV;
- goto out_power_off;
- }
-
if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY]) {
u32 val;
@@ -2569,6 +2639,22 @@
sensor->scale_m = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN];
+ /* prepare PLL configuration input values */
+ pll->bus_type = SMIAPP_PLL_BUS_TYPE_CSI2;
+ pll->csi2.lanes = sensor->platform_data->lanes;
+ pll->ext_clk_freq_hz = sensor->platform_data->ext_clk;
+ pll->flags = smiapp_call_quirk(sensor, pll_flags);
+ pll->scale_n = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN];
+ /* Profile 0 sensors have no separate OP clock branch. */
+ if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0)
+ pll->flags |= SMIAPP_PLL_FLAG_NO_OP_CLOCKS;
+
+ rval = smiapp_get_mbus_formats(sensor);
+ if (rval) {
+ rval = -ENODEV;
+ goto out_nvm_release;
+ }
+
for (i = 0; i < SMIAPP_SUBDEVS; i++) {
struct {
struct smiapp_subdev *ssd;
@@ -2666,18 +2752,9 @@
if (rval < 0)
goto out_nvm_release;
- /* prepare PLL configuration input values */
- pll->bus_type = SMIAPP_PLL_BUS_TYPE_CSI2;
- pll->csi2.lanes = sensor->platform_data->lanes;
- pll->ext_clk_freq_hz = sensor->platform_data->ext_clk;
- pll->flags = smiapp_call_quirk(sensor, pll_flags);
-
- /* Profile 0 sensors have no separate OP clock branch. */
- if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0)
- pll->flags |= SMIAPP_PLL_FLAG_NO_OP_CLOCKS;
- pll->scale_n = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN];
-
+ mutex_lock(&sensor->mutex);
rval = smiapp_update_mode(sensor);
+ mutex_unlock(&sensor->mutex);
if (rval) {
dev_err(&client->dev, "update mode failed\n");
goto out_nvm_release;
diff --git a/drivers/media/i2c/smiapp/smiapp.h b/drivers/media/i2c/smiapp/smiapp.h
index 874b49f..f88f8ec 100644
--- a/drivers/media/i2c/smiapp/smiapp.h
+++ b/drivers/media/i2c/smiapp/smiapp.h
@@ -156,6 +156,11 @@
#define SMIAPP_PAD_SRC 1
#define SMIAPP_PADS 2
+#define SMIAPP_COMPRESSED_BASE 8
+#define SMIAPP_COMPRESSED_MAX 12
+#define SMIAPP_NR_OF_COMPRESSED (SMIAPP_COMPRESSED_MAX - \
+ SMIAPP_COMPRESSED_BASE + 1)
+
struct smiapp_binning_subtype {
u8 horizontal:4;
u8 vertical:4;
@@ -232,6 +237,9 @@
struct smiapp_pll pll;
+ /* Is a default format supported for a given BPP? */
+ unsigned long valid_link_freqs[SMIAPP_NR_OF_COMPRESSED];
+
/* Pixel array controls */
struct v4l2_ctrl *analog_gain;
struct v4l2_ctrl *exposure;
diff --git a/drivers/media/i2c/soc_camera/imx074.c b/drivers/media/i2c/soc_camera/imx074.c
index 5b91593..ec89cfa 100644
--- a/drivers/media/i2c/soc_camera/imx074.c
+++ b/drivers/media/i2c/soc_camera/imx074.c
@@ -71,7 +71,7 @@
/* IMX074 has only one fixed colorspace per pixelcode */
struct imx074_datafmt {
- enum v4l2_mbus_pixelcode code;
+ u32 code;
enum v4l2_colorspace colorspace;
};
@@ -82,7 +82,7 @@
};
static const struct imx074_datafmt imx074_colour_fmts[] = {
- {V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
+ {MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
};
static struct imx074 *to_imx074(const struct i2c_client *client)
@@ -91,7 +91,7 @@
}
/* Find a data format by a pixel code in an array */
-static const struct imx074_datafmt *imx074_find_datafmt(enum v4l2_mbus_pixelcode code)
+static const struct imx074_datafmt *imx074_find_datafmt(u32 code)
{
int i;
@@ -236,7 +236,7 @@
}
static int imx074_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
+ u32 *code)
{
if ((unsigned int)index >= ARRAY_SIZE(imx074_colour_fmts))
return -EINVAL;
diff --git a/drivers/media/i2c/soc_camera/mt9m001.c b/drivers/media/i2c/soc_camera/mt9m001.c
index 8150d0d..0cc1901 100644
--- a/drivers/media/i2c/soc_camera/mt9m001.c
+++ b/drivers/media/i2c/soc_camera/mt9m001.c
@@ -53,13 +53,13 @@
/* MT9M001 has only one fixed colorspace per pixelcode */
struct mt9m001_datafmt {
- enum v4l2_mbus_pixelcode code;
+ u32 code;
enum v4l2_colorspace colorspace;
};
/* Find a data format by a pixel code in an array */
static const struct mt9m001_datafmt *mt9m001_find_datafmt(
- enum v4l2_mbus_pixelcode code, const struct mt9m001_datafmt *fmt,
+ u32 code, const struct mt9m001_datafmt *fmt,
int n)
{
int i;
@@ -75,14 +75,14 @@
* Order important: first natively supported,
* second supported with a GPIO extender
*/
- {V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB},
- {V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
+ {MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB},
+ {MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
};
static const struct mt9m001_datafmt mt9m001_monochrome_fmts[] = {
/* Order important - see above */
- {V4L2_MBUS_FMT_Y10_1X10, V4L2_COLORSPACE_JPEG},
- {V4L2_MBUS_FMT_Y8_1X8, V4L2_COLORSPACE_JPEG},
+ {MEDIA_BUS_FMT_Y10_1X10, V4L2_COLORSPACE_JPEG},
+ {MEDIA_BUS_FMT_Y8_1X8, V4L2_COLORSPACE_JPEG},
};
struct mt9m001 {
@@ -563,7 +563,7 @@
};
static int mt9m001_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
+ u32 *code)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9m001 *mt9m001 = to_mt9m001(client);
diff --git a/drivers/media/i2c/soc_camera/mt9m111.c b/drivers/media/i2c/soc_camera/mt9m111.c
index e5c5d9a..a35a2f9 100644
--- a/drivers/media/i2c/soc_camera/mt9m111.c
+++ b/drivers/media/i2c/soc_camera/mt9m111.c
@@ -182,23 +182,23 @@
/* MT9M111 has only one fixed colorspace per pixelcode */
struct mt9m111_datafmt {
- enum v4l2_mbus_pixelcode code;
+ u32 code;
enum v4l2_colorspace colorspace;
};
static const struct mt9m111_datafmt mt9m111_colour_fmts[] = {
- {V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG},
- {V4L2_MBUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_JPEG},
- {V4L2_MBUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG},
- {V4L2_MBUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_JPEG},
- {V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB},
- {V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE, V4L2_COLORSPACE_SRGB},
- {V4L2_MBUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB},
- {V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB},
- {V4L2_MBUS_FMT_BGR565_2X8_LE, V4L2_COLORSPACE_SRGB},
- {V4L2_MBUS_FMT_BGR565_2X8_BE, V4L2_COLORSPACE_SRGB},
- {V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
- {V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB},
+ {MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG},
+ {MEDIA_BUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_JPEG},
+ {MEDIA_BUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG},
+ {MEDIA_BUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_JPEG},
+ {MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB},
+ {MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, V4L2_COLORSPACE_SRGB},
+ {MEDIA_BUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB},
+ {MEDIA_BUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB},
+ {MEDIA_BUS_FMT_BGR565_2X8_LE, V4L2_COLORSPACE_SRGB},
+ {MEDIA_BUS_FMT_BGR565_2X8_BE, V4L2_COLORSPACE_SRGB},
+ {MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
+ {MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB},
};
struct mt9m111 {
@@ -218,7 +218,7 @@
/* Find a data format by a pixel code */
static const struct mt9m111_datafmt *mt9m111_find_datafmt(struct mt9m111 *mt9m111,
- enum v4l2_mbus_pixelcode code)
+ u32 code)
{
int i;
for (i = 0; i < ARRAY_SIZE(mt9m111_colour_fmts); i++)
@@ -331,7 +331,7 @@
}
static int mt9m111_setup_geometry(struct mt9m111 *mt9m111, struct v4l2_rect *rect,
- int width, int height, enum v4l2_mbus_pixelcode code)
+ int width, int height, u32 code)
{
struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
int ret;
@@ -345,7 +345,7 @@
if (!ret)
ret = reg_write(WINDOW_HEIGHT, rect->height);
- if (code != V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE) {
+ if (code != MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE) {
/* IFP in use, down-scaling possible */
if (!ret)
ret = mt9m111_setup_rect_ctx(mt9m111, &context_b,
@@ -393,8 +393,8 @@
if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- if (mt9m111->fmt->code == V4L2_MBUS_FMT_SBGGR8_1X8 ||
- mt9m111->fmt->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE) {
+ if (mt9m111->fmt->code == MEDIA_BUS_FMT_SBGGR8_1X8 ||
+ mt9m111->fmt->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE) {
/* Bayer format - even size lengths */
rect.width = ALIGN(rect.width, 2);
rect.height = ALIGN(rect.height, 2);
@@ -462,7 +462,7 @@
}
static int mt9m111_set_pixfmt(struct mt9m111 *mt9m111,
- enum v4l2_mbus_pixelcode code)
+ u32 code)
{
struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
u16 data_outfmt2, mask_outfmt2 = MT9M111_OUTFMT_PROCESSED_BAYER |
@@ -474,46 +474,46 @@
int ret;
switch (code) {
- case V4L2_MBUS_FMT_SBGGR8_1X8:
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
data_outfmt2 = MT9M111_OUTFMT_PROCESSED_BAYER |
MT9M111_OUTFMT_RGB;
break;
- case V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE:
+ case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE:
data_outfmt2 = MT9M111_OUTFMT_BYPASS_IFP | MT9M111_OUTFMT_RGB;
break;
- case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
+ case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE:
data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB555 |
MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN;
break;
- case V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE:
+ case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE:
data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB555;
break;
- case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ case MEDIA_BUS_FMT_RGB565_2X8_LE:
data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565 |
MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN;
break;
- case V4L2_MBUS_FMT_RGB565_2X8_BE:
+ case MEDIA_BUS_FMT_RGB565_2X8_BE:
data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565;
break;
- case V4L2_MBUS_FMT_BGR565_2X8_BE:
+ case MEDIA_BUS_FMT_BGR565_2X8_BE:
data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565 |
MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
break;
- case V4L2_MBUS_FMT_BGR565_2X8_LE:
+ case MEDIA_BUS_FMT_BGR565_2X8_LE:
data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565 |
MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN |
MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
break;
- case V4L2_MBUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_UYVY8_2X8:
data_outfmt2 = 0;
break;
- case V4L2_MBUS_FMT_VYUY8_2X8:
+ case MEDIA_BUS_FMT_VYUY8_2X8:
data_outfmt2 = MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
break;
- case V4L2_MBUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_YUYV8_2X8:
data_outfmt2 = MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN;
break;
- case V4L2_MBUS_FMT_YVYU8_2X8:
+ case MEDIA_BUS_FMT_YVYU8_2X8:
data_outfmt2 = MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN |
MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
break;
@@ -542,8 +542,8 @@
fmt = mt9m111_find_datafmt(mt9m111, mf->code);
- bayer = fmt->code == V4L2_MBUS_FMT_SBGGR8_1X8 ||
- fmt->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE;
+ bayer = fmt->code == MEDIA_BUS_FMT_SBGGR8_1X8 ||
+ fmt->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE;
/*
* With Bayer format enforce even side lengths, but let the user play
@@ -554,7 +554,7 @@
rect->height = ALIGN(rect->height, 2);
}
- if (fmt->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE) {
+ if (fmt->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE) {
/* IFP bypass mode, no scaling */
mf->width = rect->width;
mf->height = rect->height;
@@ -840,7 +840,7 @@
};
static int mt9m111_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
+ u32 *code)
{
if (index >= ARRAY_SIZE(mt9m111_colour_fmts))
return -EINVAL;
diff --git a/drivers/media/i2c/soc_camera/mt9t031.c b/drivers/media/i2c/soc_camera/mt9t031.c
index b8d9f2c..e803676 100644
--- a/drivers/media/i2c/soc_camera/mt9t031.c
+++ b/drivers/media/i2c/soc_camera/mt9t031.c
@@ -345,7 +345,7 @@
mf->width = mt9t031->rect.width / mt9t031->xskip;
mf->height = mt9t031->rect.height / mt9t031->yskip;
- mf->code = V4L2_MBUS_FMT_SBGGR10_1X10;
+ mf->code = MEDIA_BUS_FMT_SBGGR10_1X10;
mf->colorspace = V4L2_COLORSPACE_SRGB;
mf->field = V4L2_FIELD_NONE;
@@ -367,7 +367,7 @@
xskip = mt9t031_skip(&rect.width, mf->width, MT9T031_MAX_WIDTH);
yskip = mt9t031_skip(&rect.height, mf->height, MT9T031_MAX_HEIGHT);
- mf->code = V4L2_MBUS_FMT_SBGGR10_1X10;
+ mf->code = MEDIA_BUS_FMT_SBGGR10_1X10;
mf->colorspace = V4L2_COLORSPACE_SRGB;
/* mt9t031_set_params() doesn't change width and height */
@@ -385,7 +385,7 @@
&mf->width, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH, 1,
&mf->height, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT, 1, 0);
- mf->code = V4L2_MBUS_FMT_SBGGR10_1X10;
+ mf->code = MEDIA_BUS_FMT_SBGGR10_1X10;
mf->colorspace = V4L2_COLORSPACE_SRGB;
return 0;
@@ -673,12 +673,12 @@
};
static int mt9t031_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
+ u32 *code)
{
if (index)
return -EINVAL;
- *code = V4L2_MBUS_FMT_SBGGR10_1X10;
+ *code = MEDIA_BUS_FMT_SBGGR10_1X10;
return 0;
}
diff --git a/drivers/media/i2c/soc_camera/mt9t112.c b/drivers/media/i2c/soc_camera/mt9t112.c
index 7520e11..113f0eb 100644
--- a/drivers/media/i2c/soc_camera/mt9t112.c
+++ b/drivers/media/i2c/soc_camera/mt9t112.c
@@ -77,7 +77,7 @@
struct
************************************************************************/
struct mt9t112_format {
- enum v4l2_mbus_pixelcode code;
+ u32 code;
enum v4l2_colorspace colorspace;
u16 fmt;
u16 order;
@@ -103,32 +103,32 @@
static const struct mt9t112_format mt9t112_cfmts[] = {
{
- .code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
.colorspace = V4L2_COLORSPACE_JPEG,
.fmt = 1,
.order = 0,
}, {
- .code = V4L2_MBUS_FMT_VYUY8_2X8,
+ .code = MEDIA_BUS_FMT_VYUY8_2X8,
.colorspace = V4L2_COLORSPACE_JPEG,
.fmt = 1,
.order = 1,
}, {
- .code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .code = MEDIA_BUS_FMT_YUYV8_2X8,
.colorspace = V4L2_COLORSPACE_JPEG,
.fmt = 1,
.order = 2,
}, {
- .code = V4L2_MBUS_FMT_YVYU8_2X8,
+ .code = MEDIA_BUS_FMT_YVYU8_2X8,
.colorspace = V4L2_COLORSPACE_JPEG,
.fmt = 1,
.order = 3,
}, {
- .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE,
+ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
.colorspace = V4L2_COLORSPACE_SRGB,
.fmt = 8,
.order = 2,
}, {
- .code = V4L2_MBUS_FMT_RGB565_2X8_LE,
+ .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
.colorspace = V4L2_COLORSPACE_SRGB,
.fmt = 4,
.order = 2,
@@ -840,7 +840,7 @@
static int mt9t112_set_params(struct mt9t112_priv *priv,
const struct v4l2_rect *rect,
- enum v4l2_mbus_pixelcode code)
+ u32 code)
{
int i;
@@ -953,7 +953,7 @@
break;
if (i == priv->num_formats) {
- mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
+ mf->code = MEDIA_BUS_FMT_UYVY8_2X8;
mf->colorspace = V4L2_COLORSPACE_JPEG;
} else {
mf->colorspace = mt9t112_cfmts[i].colorspace;
@@ -967,7 +967,7 @@
}
static int mt9t112_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
+ u32 *code)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9t112_priv *priv = to_mt9t112(client);
@@ -1101,7 +1101,7 @@
/* Cannot fail: using the default supported pixel code */
if (!ret)
- mt9t112_set_params(priv, &rect, V4L2_MBUS_FMT_UYVY8_2X8);
+ mt9t112_set_params(priv, &rect, MEDIA_BUS_FMT_UYVY8_2X8);
else
v4l2_clk_put(priv->clk);
diff --git a/drivers/media/i2c/soc_camera/mt9v022.c b/drivers/media/i2c/soc_camera/mt9v022.c
index 8250357..a82cf3d 100644
--- a/drivers/media/i2c/soc_camera/mt9v022.c
+++ b/drivers/media/i2c/soc_camera/mt9v022.c
@@ -85,13 +85,13 @@
/* MT9V022 has only one fixed colorspace per pixelcode */
struct mt9v022_datafmt {
- enum v4l2_mbus_pixelcode code;
+ u32 code;
enum v4l2_colorspace colorspace;
};
/* Find a data format by a pixel code in an array */
static const struct mt9v022_datafmt *mt9v022_find_datafmt(
- enum v4l2_mbus_pixelcode code, const struct mt9v022_datafmt *fmt,
+ u32 code, const struct mt9v022_datafmt *fmt,
int n)
{
int i;
@@ -107,14 +107,14 @@
* Order important: first natively supported,
* second supported with a GPIO extender
*/
- {V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB},
- {V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
+ {MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB},
+ {MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
};
static const struct mt9v022_datafmt mt9v022_monochrome_fmts[] = {
/* Order important - see above */
- {V4L2_MBUS_FMT_Y10_1X10, V4L2_COLORSPACE_JPEG},
- {V4L2_MBUS_FMT_Y8_1X8, V4L2_COLORSPACE_JPEG},
+ {MEDIA_BUS_FMT_Y10_1X10, V4L2_COLORSPACE_JPEG},
+ {MEDIA_BUS_FMT_Y8_1X8, V4L2_COLORSPACE_JPEG},
};
/* only registers with different addresses on different mt9v02x sensors */
@@ -410,13 +410,13 @@
* .try_mbus_fmt(), datawidth is from our supported format list
*/
switch (mf->code) {
- case V4L2_MBUS_FMT_Y8_1X8:
- case V4L2_MBUS_FMT_Y10_1X10:
+ case MEDIA_BUS_FMT_Y8_1X8:
+ case MEDIA_BUS_FMT_Y10_1X10:
if (mt9v022->model != MT9V022IX7ATM)
return -EINVAL;
break;
- case V4L2_MBUS_FMT_SBGGR8_1X8:
- case V4L2_MBUS_FMT_SBGGR10_1X10:
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
if (mt9v022->model != MT9V022IX7ATC)
return -EINVAL;
break;
@@ -443,8 +443,8 @@
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9v022 *mt9v022 = to_mt9v022(client);
const struct mt9v022_datafmt *fmt;
- int align = mf->code == V4L2_MBUS_FMT_SBGGR8_1X8 ||
- mf->code == V4L2_MBUS_FMT_SBGGR10_1X10;
+ int align = mf->code == MEDIA_BUS_FMT_SBGGR8_1X8 ||
+ mf->code == MEDIA_BUS_FMT_SBGGR10_1X10;
v4l_bound_align_image(&mf->width, MT9V022_MIN_WIDTH,
MT9V022_MAX_WIDTH, align,
@@ -759,7 +759,7 @@
};
static int mt9v022_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
+ u32 *code)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct mt9v022 *mt9v022 = to_mt9v022(client);
diff --git a/drivers/media/i2c/soc_camera/ov2640.c b/drivers/media/i2c/soc_camera/ov2640.c
index ce0517d..0c05283 100644
--- a/drivers/media/i2c/soc_camera/ov2640.c
+++ b/drivers/media/i2c/soc_camera/ov2640.c
@@ -302,7 +302,7 @@
struct ov2640_priv {
struct v4l2_subdev subdev;
struct v4l2_ctrl_handler hdl;
- enum v4l2_mbus_pixelcode cfmt_code;
+ u32 cfmt_code;
struct v4l2_clk *clk;
const struct ov2640_win_size *win;
};
@@ -623,11 +623,11 @@
ENDMARKER,
};
-static enum v4l2_mbus_pixelcode ov2640_codes[] = {
- V4L2_MBUS_FMT_YUYV8_2X8,
- V4L2_MBUS_FMT_UYVY8_2X8,
- V4L2_MBUS_FMT_RGB565_2X8_BE,
- V4L2_MBUS_FMT_RGB565_2X8_LE,
+static u32 ov2640_codes[] = {
+ MEDIA_BUS_FMT_YUYV8_2X8,
+ MEDIA_BUS_FMT_UYVY8_2X8,
+ MEDIA_BUS_FMT_RGB565_2X8_BE,
+ MEDIA_BUS_FMT_RGB565_2X8_LE,
};
/*
@@ -785,7 +785,7 @@
}
static int ov2640_set_params(struct i2c_client *client, u32 *width, u32 *height,
- enum v4l2_mbus_pixelcode code)
+ u32 code)
{
struct ov2640_priv *priv = to_ov2640(client);
const struct regval_list *selected_cfmt_regs;
@@ -797,20 +797,20 @@
/* select format */
priv->cfmt_code = 0;
switch (code) {
- case V4L2_MBUS_FMT_RGB565_2X8_BE:
+ case MEDIA_BUS_FMT_RGB565_2X8_BE:
dev_dbg(&client->dev, "%s: Selected cfmt RGB565 BE", __func__);
selected_cfmt_regs = ov2640_rgb565_be_regs;
break;
- case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ case MEDIA_BUS_FMT_RGB565_2X8_LE:
dev_dbg(&client->dev, "%s: Selected cfmt RGB565 LE", __func__);
selected_cfmt_regs = ov2640_rgb565_le_regs;
break;
- case V4L2_MBUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_YUYV8_2X8:
dev_dbg(&client->dev, "%s: Selected cfmt YUYV (YUV422)", __func__);
selected_cfmt_regs = ov2640_yuyv_regs;
break;
default:
- case V4L2_MBUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_UYVY8_2X8:
dev_dbg(&client->dev, "%s: Selected cfmt UYVY", __func__);
selected_cfmt_regs = ov2640_uyvy_regs;
}
@@ -869,7 +869,7 @@
if (!priv->win) {
u32 width = W_SVGA, height = H_SVGA;
priv->win = ov2640_select_win(&width, &height);
- priv->cfmt_code = V4L2_MBUS_FMT_UYVY8_2X8;
+ priv->cfmt_code = MEDIA_BUS_FMT_UYVY8_2X8;
}
mf->width = priv->win->width;
@@ -877,13 +877,13 @@
mf->code = priv->cfmt_code;
switch (mf->code) {
- case V4L2_MBUS_FMT_RGB565_2X8_BE:
- case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ case MEDIA_BUS_FMT_RGB565_2X8_BE:
+ case MEDIA_BUS_FMT_RGB565_2X8_LE:
mf->colorspace = V4L2_COLORSPACE_SRGB;
break;
default:
- case V4L2_MBUS_FMT_YUYV8_2X8:
- case V4L2_MBUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_UYVY8_2X8:
mf->colorspace = V4L2_COLORSPACE_JPEG;
}
mf->field = V4L2_FIELD_NONE;
@@ -899,14 +899,14 @@
switch (mf->code) {
- case V4L2_MBUS_FMT_RGB565_2X8_BE:
- case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ case MEDIA_BUS_FMT_RGB565_2X8_BE:
+ case MEDIA_BUS_FMT_RGB565_2X8_LE:
mf->colorspace = V4L2_COLORSPACE_SRGB;
break;
default:
- mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
- case V4L2_MBUS_FMT_YUYV8_2X8:
- case V4L2_MBUS_FMT_UYVY8_2X8:
+ mf->code = MEDIA_BUS_FMT_UYVY8_2X8;
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_UYVY8_2X8:
mf->colorspace = V4L2_COLORSPACE_JPEG;
}
@@ -926,14 +926,14 @@
mf->field = V4L2_FIELD_NONE;
switch (mf->code) {
- case V4L2_MBUS_FMT_RGB565_2X8_BE:
- case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ case MEDIA_BUS_FMT_RGB565_2X8_BE:
+ case MEDIA_BUS_FMT_RGB565_2X8_LE:
mf->colorspace = V4L2_COLORSPACE_SRGB;
break;
default:
- mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
- case V4L2_MBUS_FMT_YUYV8_2X8:
- case V4L2_MBUS_FMT_UYVY8_2X8:
+ mf->code = MEDIA_BUS_FMT_UYVY8_2X8;
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_UYVY8_2X8:
mf->colorspace = V4L2_COLORSPACE_JPEG;
}
@@ -941,7 +941,7 @@
}
static int ov2640_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
+ u32 *code)
{
if (index >= ARRAY_SIZE(ov2640_codes))
return -EINVAL;
diff --git a/drivers/media/i2c/soc_camera/ov5642.c b/drivers/media/i2c/soc_camera/ov5642.c
index c8a3921..e9a2931 100644
--- a/drivers/media/i2c/soc_camera/ov5642.c
+++ b/drivers/media/i2c/soc_camera/ov5642.c
@@ -602,7 +602,7 @@
};
struct ov5642_datafmt {
- enum v4l2_mbus_pixelcode code;
+ u32 code;
enum v4l2_colorspace colorspace;
};
@@ -618,7 +618,7 @@
};
static const struct ov5642_datafmt ov5642_colour_fmts[] = {
- {V4L2_MBUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG},
+ {MEDIA_BUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG},
};
static struct ov5642 *to_ov5642(const struct i2c_client *client)
@@ -628,7 +628,7 @@
/* Find a data format by a pixel code in an array */
static const struct ov5642_datafmt
- *ov5642_find_datafmt(enum v4l2_mbus_pixelcode code)
+ *ov5642_find_datafmt(u32 code)
{
int i;
@@ -840,7 +840,7 @@
}
static int ov5642_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
+ u32 *code)
{
if (index >= ARRAY_SIZE(ov5642_colour_fmts))
return -EINVAL;
diff --git a/drivers/media/i2c/soc_camera/ov6650.c b/drivers/media/i2c/soc_camera/ov6650.c
index adbc185..3d3dfeb 100644
--- a/drivers/media/i2c/soc_camera/ov6650.c
+++ b/drivers/media/i2c/soc_camera/ov6650.c
@@ -202,18 +202,18 @@
unsigned long pclk_limit; /* from host */
unsigned long pclk_max; /* from resolution and format */
struct v4l2_fract tpf; /* as requested with s_parm */
- enum v4l2_mbus_pixelcode code;
+ u32 code;
enum v4l2_colorspace colorspace;
};
-static enum v4l2_mbus_pixelcode ov6650_codes[] = {
- V4L2_MBUS_FMT_YUYV8_2X8,
- V4L2_MBUS_FMT_UYVY8_2X8,
- V4L2_MBUS_FMT_YVYU8_2X8,
- V4L2_MBUS_FMT_VYUY8_2X8,
- V4L2_MBUS_FMT_SBGGR8_1X8,
- V4L2_MBUS_FMT_Y8_1X8,
+static u32 ov6650_codes[] = {
+ MEDIA_BUS_FMT_YUYV8_2X8,
+ MEDIA_BUS_FMT_UYVY8_2X8,
+ MEDIA_BUS_FMT_YVYU8_2X8,
+ MEDIA_BUS_FMT_VYUY8_2X8,
+ MEDIA_BUS_FMT_SBGGR8_1X8,
+ MEDIA_BUS_FMT_Y8_1X8,
};
/* read a register */
@@ -555,29 +555,29 @@
.height = mf->height << half_scale,
},
};
- enum v4l2_mbus_pixelcode code = mf->code;
+ u32 code = mf->code;
unsigned long mclk, pclk;
u8 coma_set = 0, coma_mask = 0, coml_set, coml_mask, clkrc;
int ret;
/* select color matrix configuration for given color encoding */
switch (code) {
- case V4L2_MBUS_FMT_Y8_1X8:
+ case MEDIA_BUS_FMT_Y8_1X8:
dev_dbg(&client->dev, "pixel format GREY8_1X8\n");
coma_mask |= COMA_RGB | COMA_WORD_SWAP | COMA_BYTE_SWAP;
coma_set |= COMA_BW;
break;
- case V4L2_MBUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_YUYV8_2X8:
dev_dbg(&client->dev, "pixel format YUYV8_2X8_LE\n");
coma_mask |= COMA_RGB | COMA_BW | COMA_BYTE_SWAP;
coma_set |= COMA_WORD_SWAP;
break;
- case V4L2_MBUS_FMT_YVYU8_2X8:
+ case MEDIA_BUS_FMT_YVYU8_2X8:
dev_dbg(&client->dev, "pixel format YVYU8_2X8_LE (untested)\n");
coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP |
COMA_BYTE_SWAP;
break;
- case V4L2_MBUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_UYVY8_2X8:
dev_dbg(&client->dev, "pixel format YUYV8_2X8_BE\n");
if (half_scale) {
coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP;
@@ -587,7 +587,7 @@
coma_set |= COMA_BYTE_SWAP | COMA_WORD_SWAP;
}
break;
- case V4L2_MBUS_FMT_VYUY8_2X8:
+ case MEDIA_BUS_FMT_VYUY8_2X8:
dev_dbg(&client->dev, "pixel format YVYU8_2X8_BE (untested)\n");
if (half_scale) {
coma_mask |= COMA_RGB | COMA_BW;
@@ -597,7 +597,7 @@
coma_set |= COMA_BYTE_SWAP;
}
break;
- case V4L2_MBUS_FMT_SBGGR8_1X8:
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
dev_dbg(&client->dev, "pixel format SBGGR8_1X8 (untested)\n");
coma_mask |= COMA_BW | COMA_BYTE_SWAP | COMA_WORD_SWAP;
coma_set |= COMA_RAW_RGB | COMA_RGB;
@@ -608,8 +608,8 @@
}
priv->code = code;
- if (code == V4L2_MBUS_FMT_Y8_1X8 ||
- code == V4L2_MBUS_FMT_SBGGR8_1X8) {
+ if (code == MEDIA_BUS_FMT_Y8_1X8 ||
+ code == MEDIA_BUS_FMT_SBGGR8_1X8) {
coml_mask = COML_ONE_CHANNEL;
coml_set = 0;
priv->pclk_max = 4000000;
@@ -619,7 +619,7 @@
priv->pclk_max = 8000000;
}
- if (code == V4L2_MBUS_FMT_SBGGR8_1X8)
+ if (code == MEDIA_BUS_FMT_SBGGR8_1X8)
priv->colorspace = V4L2_COLORSPACE_SRGB;
else if (code != 0)
priv->colorspace = V4L2_COLORSPACE_JPEG;
@@ -697,18 +697,18 @@
mf->field = V4L2_FIELD_NONE;
switch (mf->code) {
- case V4L2_MBUS_FMT_Y10_1X10:
- mf->code = V4L2_MBUS_FMT_Y8_1X8;
- case V4L2_MBUS_FMT_Y8_1X8:
- case V4L2_MBUS_FMT_YVYU8_2X8:
- case V4L2_MBUS_FMT_YUYV8_2X8:
- case V4L2_MBUS_FMT_VYUY8_2X8:
- case V4L2_MBUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_Y10_1X10:
+ mf->code = MEDIA_BUS_FMT_Y8_1X8;
+ case MEDIA_BUS_FMT_Y8_1X8:
+ case MEDIA_BUS_FMT_YVYU8_2X8:
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_VYUY8_2X8:
+ case MEDIA_BUS_FMT_UYVY8_2X8:
mf->colorspace = V4L2_COLORSPACE_JPEG;
break;
default:
- mf->code = V4L2_MBUS_FMT_SBGGR8_1X8;
- case V4L2_MBUS_FMT_SBGGR8_1X8:
+ mf->code = MEDIA_BUS_FMT_SBGGR8_1X8;
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
mf->colorspace = V4L2_COLORSPACE_SRGB;
break;
}
@@ -717,7 +717,7 @@
}
static int ov6650_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
+ u32 *code)
{
if (index >= ARRAY_SIZE(ov6650_codes))
return -EINVAL;
@@ -1013,7 +1013,7 @@
priv->rect.width = W_CIF;
priv->rect.height = H_CIF;
priv->half_scale = false;
- priv->code = V4L2_MBUS_FMT_YUYV8_2X8;
+ priv->code = MEDIA_BUS_FMT_YUYV8_2X8;
priv->colorspace = V4L2_COLORSPACE_JPEG;
priv->clk = v4l2_clk_get(&client->dev, "mclk");
diff --git a/drivers/media/i2c/soc_camera/ov772x.c b/drivers/media/i2c/soc_camera/ov772x.c
index 8650c78..4eef535 100644
--- a/drivers/media/i2c/soc_camera/ov772x.c
+++ b/drivers/media/i2c/soc_camera/ov772x.c
@@ -376,7 +376,7 @@
*/
struct ov772x_color_format {
- enum v4l2_mbus_pixelcode code;
+ u32 code;
enum v4l2_colorspace colorspace;
u8 dsp3;
u8 dsp4;
@@ -408,7 +408,7 @@
*/
static const struct ov772x_color_format ov772x_cfmts[] = {
{
- .code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .code = MEDIA_BUS_FMT_YUYV8_2X8,
.colorspace = V4L2_COLORSPACE_JPEG,
.dsp3 = 0x0,
.dsp4 = DSP_OFMT_YUV,
@@ -416,7 +416,7 @@
.com7 = OFMT_YUV,
},
{
- .code = V4L2_MBUS_FMT_YVYU8_2X8,
+ .code = MEDIA_BUS_FMT_YVYU8_2X8,
.colorspace = V4L2_COLORSPACE_JPEG,
.dsp3 = UV_ON,
.dsp4 = DSP_OFMT_YUV,
@@ -424,7 +424,7 @@
.com7 = OFMT_YUV,
},
{
- .code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
.colorspace = V4L2_COLORSPACE_JPEG,
.dsp3 = 0x0,
.dsp4 = DSP_OFMT_YUV,
@@ -432,7 +432,7 @@
.com7 = OFMT_YUV,
},
{
- .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE,
+ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
.colorspace = V4L2_COLORSPACE_SRGB,
.dsp3 = 0x0,
.dsp4 = DSP_OFMT_YUV,
@@ -440,7 +440,7 @@
.com7 = FMT_RGB555 | OFMT_RGB,
},
{
- .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE,
+ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE,
.colorspace = V4L2_COLORSPACE_SRGB,
.dsp3 = 0x0,
.dsp4 = DSP_OFMT_YUV,
@@ -448,7 +448,7 @@
.com7 = FMT_RGB555 | OFMT_RGB,
},
{
- .code = V4L2_MBUS_FMT_RGB565_2X8_LE,
+ .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
.colorspace = V4L2_COLORSPACE_SRGB,
.dsp3 = 0x0,
.dsp4 = DSP_OFMT_YUV,
@@ -456,7 +456,7 @@
.com7 = FMT_RGB565 | OFMT_RGB,
},
{
- .code = V4L2_MBUS_FMT_RGB565_2X8_BE,
+ .code = MEDIA_BUS_FMT_RGB565_2X8_BE,
.colorspace = V4L2_COLORSPACE_SRGB,
.dsp3 = 0x0,
.dsp4 = DSP_OFMT_YUV,
@@ -468,7 +468,7 @@
* regardless of the COM7 value. We can thus only support 10-bit
* Bayer until someone figures it out.
*/
- .code = V4L2_MBUS_FMT_SBGGR10_1X10,
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
.colorspace = V4L2_COLORSPACE_SRGB,
.dsp3 = 0x0,
.dsp4 = DSP_OFMT_RAW10,
@@ -990,7 +990,7 @@
};
static int ov772x_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
+ u32 *code)
{
if (index >= ARRAY_SIZE(ov772x_cfmts))
return -EINVAL;
diff --git a/drivers/media/i2c/soc_camera/ov9640.c b/drivers/media/i2c/soc_camera/ov9640.c
index 1a3a4a3..299308d 100644
--- a/drivers/media/i2c/soc_camera/ov9640.c
+++ b/drivers/media/i2c/soc_camera/ov9640.c
@@ -159,10 +159,10 @@
{ OV9640_MTXS, 0x65 },
};
-static enum v4l2_mbus_pixelcode ov9640_codes[] = {
- V4L2_MBUS_FMT_UYVY8_2X8,
- V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE,
- V4L2_MBUS_FMT_RGB565_2X8_LE,
+static u32 ov9640_codes[] = {
+ MEDIA_BUS_FMT_UYVY8_2X8,
+ MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
+ MEDIA_BUS_FMT_RGB565_2X8_LE,
};
/* read a register */
@@ -351,22 +351,22 @@
}
/* Prepare necessary register changes depending on color encoding */
-static void ov9640_alter_regs(enum v4l2_mbus_pixelcode code,
+static void ov9640_alter_regs(u32 code,
struct ov9640_reg_alt *alt)
{
switch (code) {
default:
- case V4L2_MBUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_UYVY8_2X8:
alt->com12 = OV9640_COM12_YUV_AVG;
alt->com13 = OV9640_COM13_Y_DELAY_EN |
OV9640_COM13_YUV_DLY(0x01);
break;
- case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
+ case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE:
alt->com7 = OV9640_COM7_RGB;
alt->com13 = OV9640_COM13_RGB_AVG;
alt->com15 = OV9640_COM15_RGB_555;
break;
- case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ case MEDIA_BUS_FMT_RGB565_2X8_LE:
alt->com7 = OV9640_COM7_RGB;
alt->com13 = OV9640_COM13_RGB_AVG;
alt->com15 = OV9640_COM15_RGB_565;
@@ -376,7 +376,7 @@
/* Setup registers according to resolution and color encoding */
static int ov9640_write_regs(struct i2c_client *client, u32 width,
- enum v4l2_mbus_pixelcode code, struct ov9640_reg_alt *alts)
+ u32 code, struct ov9640_reg_alt *alts)
{
const struct ov9640_reg *ov9640_regs, *matrix_regs;
int ov9640_regs_len, matrix_regs_len;
@@ -419,7 +419,7 @@
}
/* select color matrix configuration for given color encoding */
- if (code == V4L2_MBUS_FMT_UYVY8_2X8) {
+ if (code == MEDIA_BUS_FMT_UYVY8_2X8) {
matrix_regs = ov9640_regs_yuv;
matrix_regs_len = ARRAY_SIZE(ov9640_regs_yuv);
} else {
@@ -487,7 +487,7 @@
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov9640_reg_alt alts = {0};
enum v4l2_colorspace cspace;
- enum v4l2_mbus_pixelcode code = mf->code;
+ u32 code = mf->code;
int ret;
ov9640_res_roundup(&mf->width, &mf->height);
@@ -500,13 +500,13 @@
return ret;
switch (code) {
- case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
- case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE:
+ case MEDIA_BUS_FMT_RGB565_2X8_LE:
cspace = V4L2_COLORSPACE_SRGB;
break;
default:
- code = V4L2_MBUS_FMT_UYVY8_2X8;
- case V4L2_MBUS_FMT_UYVY8_2X8:
+ code = MEDIA_BUS_FMT_UYVY8_2X8;
+ case MEDIA_BUS_FMT_UYVY8_2X8:
cspace = V4L2_COLORSPACE_JPEG;
}
@@ -527,13 +527,13 @@
mf->field = V4L2_FIELD_NONE;
switch (mf->code) {
- case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
- case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE:
+ case MEDIA_BUS_FMT_RGB565_2X8_LE:
mf->colorspace = V4L2_COLORSPACE_SRGB;
break;
default:
- mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
- case V4L2_MBUS_FMT_UYVY8_2X8:
+ mf->code = MEDIA_BUS_FMT_UYVY8_2X8;
+ case MEDIA_BUS_FMT_UYVY8_2X8:
mf->colorspace = V4L2_COLORSPACE_JPEG;
}
@@ -541,7 +541,7 @@
}
static int ov9640_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
+ u32 *code)
{
if (index >= ARRAY_SIZE(ov9640_codes))
return -EINVAL;
diff --git a/drivers/media/i2c/soc_camera/ov9740.c b/drivers/media/i2c/soc_camera/ov9740.c
index 7e2656a..8cf6b21 100644
--- a/drivers/media/i2c/soc_camera/ov9740.c
+++ b/drivers/media/i2c/soc_camera/ov9740.c
@@ -392,8 +392,8 @@
{ OV9740_ISP_CTRL19, 0x02 },
};
-static enum v4l2_mbus_pixelcode ov9740_codes[] = {
- V4L2_MBUS_FMT_YUYV8_2X8,
+static u32 ov9740_codes[] = {
+ MEDIA_BUS_FMT_YUYV8_2X8,
};
/* read a register */
@@ -674,13 +674,13 @@
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov9740_priv *priv = to_ov9740(sd);
enum v4l2_colorspace cspace;
- enum v4l2_mbus_pixelcode code = mf->code;
+ u32 code = mf->code;
int ret;
ov9740_res_roundup(&mf->width, &mf->height);
switch (code) {
- case V4L2_MBUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_YUYV8_2X8:
cspace = V4L2_COLORSPACE_SRGB;
break;
default:
@@ -710,14 +710,14 @@
ov9740_res_roundup(&mf->width, &mf->height);
mf->field = V4L2_FIELD_NONE;
- mf->code = V4L2_MBUS_FMT_YUYV8_2X8;
+ mf->code = MEDIA_BUS_FMT_YUYV8_2X8;
mf->colorspace = V4L2_COLORSPACE_SRGB;
return 0;
}
static int ov9740_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
+ u32 *code)
{
if (index >= ARRAY_SIZE(ov9740_codes))
return -EINVAL;
diff --git a/drivers/media/i2c/soc_camera/rj54n1cb0c.c b/drivers/media/i2c/soc_camera/rj54n1cb0c.c
index d523249..2b26200 100644
--- a/drivers/media/i2c/soc_camera/rj54n1cb0c.c
+++ b/drivers/media/i2c/soc_camera/rj54n1cb0c.c
@@ -111,13 +111,13 @@
/* RJ54N1CB0C has only one fixed colorspace per pixelcode */
struct rj54n1_datafmt {
- enum v4l2_mbus_pixelcode code;
+ u32 code;
enum v4l2_colorspace colorspace;
};
/* Find a data format by a pixel code in an array */
static const struct rj54n1_datafmt *rj54n1_find_datafmt(
- enum v4l2_mbus_pixelcode code, const struct rj54n1_datafmt *fmt,
+ u32 code, const struct rj54n1_datafmt *fmt,
int n)
{
int i;
@@ -129,15 +129,15 @@
}
static const struct rj54n1_datafmt rj54n1_colour_fmts[] = {
- {V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG},
- {V4L2_MBUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_JPEG},
- {V4L2_MBUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB},
- {V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB},
- {V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB},
- {V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE, V4L2_COLORSPACE_SRGB},
- {V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE, V4L2_COLORSPACE_SRGB},
- {V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE, V4L2_COLORSPACE_SRGB},
- {V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB},
+ {MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG},
+ {MEDIA_BUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_JPEG},
+ {MEDIA_BUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB},
+ {MEDIA_BUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB},
+ {MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB},
+ {MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE, V4L2_COLORSPACE_SRGB},
+ {MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE, V4L2_COLORSPACE_SRGB},
+ {MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE, V4L2_COLORSPACE_SRGB},
+ {MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB},
};
struct rj54n1_clock_div {
@@ -486,7 +486,7 @@
}
static int rj54n1_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
+ u32 *code)
{
if (index >= ARRAY_SIZE(rj54n1_colour_fmts))
return -EINVAL;
@@ -965,11 +965,11 @@
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct rj54n1 *rj54n1 = to_rj54n1(client);
const struct rj54n1_datafmt *fmt;
- int align = mf->code == V4L2_MBUS_FMT_SBGGR10_1X10 ||
- mf->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE ||
- mf->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE ||
- mf->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE ||
- mf->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE;
+ int align = mf->code == MEDIA_BUS_FMT_SBGGR10_1X10 ||
+ mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE ||
+ mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE ||
+ mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE ||
+ mf->code == MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE;
dev_dbg(&client->dev, "%s: code = %d, width = %u, height = %u\n",
__func__, mf->code, mf->width, mf->height);
@@ -1025,55 +1025,55 @@
/* RA_SEL_UL is only relevant for raw modes, ignored otherwise. */
switch (mf->code) {
- case V4L2_MBUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_YUYV8_2X8:
ret = reg_write(client, RJ54N1_OUT_SEL, 0);
if (!ret)
ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8);
break;
- case V4L2_MBUS_FMT_YVYU8_2X8:
+ case MEDIA_BUS_FMT_YVYU8_2X8:
ret = reg_write(client, RJ54N1_OUT_SEL, 0);
if (!ret)
ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8);
break;
- case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ case MEDIA_BUS_FMT_RGB565_2X8_LE:
ret = reg_write(client, RJ54N1_OUT_SEL, 0x11);
if (!ret)
ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8);
break;
- case V4L2_MBUS_FMT_RGB565_2X8_BE:
+ case MEDIA_BUS_FMT_RGB565_2X8_BE:
ret = reg_write(client, RJ54N1_OUT_SEL, 0x11);
if (!ret)
ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8);
break;
- case V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE:
+ case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE:
ret = reg_write(client, RJ54N1_OUT_SEL, 4);
if (!ret)
ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8);
if (!ret)
ret = reg_write(client, RJ54N1_RA_SEL_UL, 0);
break;
- case V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE:
+ case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE:
ret = reg_write(client, RJ54N1_OUT_SEL, 4);
if (!ret)
ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8);
if (!ret)
ret = reg_write(client, RJ54N1_RA_SEL_UL, 8);
break;
- case V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE:
+ case MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE:
ret = reg_write(client, RJ54N1_OUT_SEL, 4);
if (!ret)
ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8);
if (!ret)
ret = reg_write(client, RJ54N1_RA_SEL_UL, 0);
break;
- case V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE:
+ case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE:
ret = reg_write(client, RJ54N1_OUT_SEL, 4);
if (!ret)
ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8);
if (!ret)
ret = reg_write(client, RJ54N1_RA_SEL_UL, 8);
break;
- case V4L2_MBUS_FMT_SBGGR10_1X10:
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
ret = reg_write(client, RJ54N1_OUT_SEL, 5);
break;
default:
@@ -1083,7 +1083,7 @@
/* Special case: a raw mode with 10 bits of data per clock tick */
if (!ret)
ret = reg_set(client, RJ54N1_OCLK_SEL_EN,
- (mf->code == V4L2_MBUS_FMT_SBGGR10_1X10) << 1, 2);
+ (mf->code == MEDIA_BUS_FMT_SBGGR10_1X10) << 1, 2);
if (ret < 0)
return ret;
diff --git a/drivers/media/i2c/soc_camera/tw9910.c b/drivers/media/i2c/soc_camera/tw9910.c
index 3a84291..b5237c6 100644
--- a/drivers/media/i2c/soc_camera/tw9910.c
+++ b/drivers/media/i2c/soc_camera/tw9910.c
@@ -705,7 +705,7 @@
mf->width = priv->scale->width;
mf->height = priv->scale->height;
- mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
+ mf->code = MEDIA_BUS_FMT_UYVY8_2X8;
mf->colorspace = V4L2_COLORSPACE_JPEG;
mf->field = V4L2_FIELD_INTERLACED_BT;
@@ -724,7 +724,7 @@
/*
* check color format
*/
- if (mf->code != V4L2_MBUS_FMT_UYVY8_2X8)
+ if (mf->code != MEDIA_BUS_FMT_UYVY8_2X8)
return -EINVAL;
mf->colorspace = V4L2_COLORSPACE_JPEG;
@@ -751,7 +751,7 @@
return -EINVAL;
}
- mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
+ mf->code = MEDIA_BUS_FMT_UYVY8_2X8;
mf->colorspace = V4L2_COLORSPACE_JPEG;
/*
@@ -822,12 +822,12 @@
};
static int tw9910_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
+ u32 *code)
{
if (index)
return -EINVAL;
- *code = V4L2_MBUS_FMT_UYVY8_2X8;
+ *code = MEDIA_BUS_FMT_UYVY8_2X8;
return 0;
}
diff --git a/drivers/media/i2c/sr030pc30.c b/drivers/media/i2c/sr030pc30.c
index 118f8ee..10c735c 100644
--- a/drivers/media/i2c/sr030pc30.c
+++ b/drivers/media/i2c/sr030pc30.c
@@ -165,7 +165,7 @@
};
struct sr030pc30_format {
- enum v4l2_mbus_pixelcode code;
+ u32 code;
enum v4l2_colorspace colorspace;
u16 ispctl1_reg;
};
@@ -201,23 +201,23 @@
/* supported pixel formats */
static const struct sr030pc30_format sr030pc30_formats[] = {
{
- .code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .code = MEDIA_BUS_FMT_YUYV8_2X8,
.colorspace = V4L2_COLORSPACE_JPEG,
.ispctl1_reg = 0x03,
}, {
- .code = V4L2_MBUS_FMT_YVYU8_2X8,
+ .code = MEDIA_BUS_FMT_YVYU8_2X8,
.colorspace = V4L2_COLORSPACE_JPEG,
.ispctl1_reg = 0x02,
}, {
- .code = V4L2_MBUS_FMT_VYUY8_2X8,
+ .code = MEDIA_BUS_FMT_VYUY8_2X8,
.colorspace = V4L2_COLORSPACE_JPEG,
.ispctl1_reg = 0,
}, {
- .code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
.colorspace = V4L2_COLORSPACE_JPEG,
.ispctl1_reg = 0x01,
}, {
- .code = V4L2_MBUS_FMT_RGB565_2X8_BE,
+ .code = MEDIA_BUS_FMT_RGB565_2X8_BE,
.colorspace = V4L2_COLORSPACE_JPEG,
.ispctl1_reg = 0x40,
},
@@ -472,7 +472,7 @@
}
static int sr030pc30_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
+ u32 *code)
{
if (!code || index >= ARRAY_SIZE(sr030pc30_formats))
return -EINVAL;
diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c
index 5a28d99..c692fdf 100644
--- a/drivers/media/i2c/tvp514x.c
+++ b/drivers/media/i2c/tvp514x.c
@@ -756,12 +756,12 @@
*/
static int
tvp514x_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
- enum v4l2_mbus_pixelcode *code)
+ u32 *code)
{
if (index)
return -EINVAL;
- *code = V4L2_MBUS_FMT_YUYV10_2X10;
+ *code = MEDIA_BUS_FMT_YUYV10_2X10;
return 0;
}
@@ -784,7 +784,7 @@
/* Calculate height and width based on current standard */
current_std = decoder->current_std;
- f->code = V4L2_MBUS_FMT_YUYV8_2X8;
+ f->code = MEDIA_BUS_FMT_YUYV8_2X8;
f->width = decoder->std_list[current_std].width;
f->height = decoder->std_list[current_std].height;
f->field = V4L2_FIELD_INTERLACED;
@@ -942,7 +942,7 @@
if (index != 0)
return -EINVAL;
- code->code = V4L2_MBUS_FMT_YUYV8_2X8;
+ code->code = MEDIA_BUS_FMT_YUYV8_2X8;
return 0;
}
@@ -967,7 +967,7 @@
return 0;
}
- format->format.code = V4L2_MBUS_FMT_YUYV8_2X8;
+ format->format.code = MEDIA_BUS_FMT_YUYV8_2X8;
format->format.width = tvp514x_std_list[decoder->current_std].width;
format->format.height = tvp514x_std_list[decoder->current_std].height;
format->format.colorspace = V4L2_COLORSPACE_SMPTE170M;
@@ -991,7 +991,7 @@
struct tvp514x_decoder *decoder = to_decoder(sd);
if (fmt->format.field != V4L2_FIELD_INTERLACED ||
- fmt->format.code != V4L2_MBUS_FMT_YUYV8_2X8 ||
+ fmt->format.code != MEDIA_BUS_FMT_YUYV8_2X8 ||
fmt->format.colorspace != V4L2_COLORSPACE_SMPTE170M ||
fmt->format.width != tvp514x_std_list[decoder->current_std].width ||
fmt->format.height != tvp514x_std_list[decoder->current_std].height)
diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
index f32495f..f23110c 100644
--- a/drivers/media/i2c/tvp5150.c
+++ b/drivers/media/i2c/tvp5150.c
@@ -818,12 +818,12 @@
}
static int tvp5150_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
- enum v4l2_mbus_pixelcode *code)
+ u32 *code)
{
if (index)
return -EINVAL;
- *code = V4L2_MBUS_FMT_UYVY8_2X8;
+ *code = MEDIA_BUS_FMT_UYVY8_2X8;
return 0;
}
@@ -840,7 +840,7 @@
f->width = decoder->rect.width;
f->height = decoder->rect.height;
- f->code = V4L2_MBUS_FMT_UYVY8_2X8;
+ f->code = MEDIA_BUS_FMT_UYVY8_2X8;
f->field = V4L2_FIELD_SEQ_TB;
f->colorspace = V4L2_COLORSPACE_SMPTE170M;
diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c
index 28f978e..8d8e714 100644
--- a/drivers/media/i2c/tvp7002.c
+++ b/drivers/media/i2c/tvp7002.c
@@ -626,7 +626,7 @@
f->width = bt->width;
f->height = bt->height;
- f->code = V4L2_MBUS_FMT_YUYV10_1X20;
+ f->code = MEDIA_BUS_FMT_YUYV10_1X20;
f->field = device->current_timings->scanmode;
f->colorspace = device->current_timings->color_space;
@@ -756,12 +756,12 @@
*/
static int tvp7002_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
- enum v4l2_mbus_pixelcode *code)
+ u32 *code)
{
/* Check requested format index is within range */
if (index)
return -EINVAL;
- *code = V4L2_MBUS_FMT_YUYV10_1X20;
+ *code = MEDIA_BUS_FMT_YUYV10_1X20;
return 0;
}
@@ -859,7 +859,7 @@
if (code->index != 0)
return -EINVAL;
- code->code = V4L2_MBUS_FMT_YUYV10_1X20;
+ code->code = MEDIA_BUS_FMT_YUYV10_1X20;
return 0;
}
@@ -878,7 +878,7 @@
{
struct tvp7002 *tvp7002 = to_tvp7002(sd);
- fmt->format.code = V4L2_MBUS_FMT_YUYV10_1X20;
+ fmt->format.code = MEDIA_BUS_FMT_YUYV10_1X20;
fmt->format.width = tvp7002->current_timings->timings.bt.width;
fmt->format.height = tvp7002->current_timings->timings.bt.height;
fmt->format.field = tvp7002->current_timings->scanmode;
diff --git a/drivers/media/i2c/vs6624.c b/drivers/media/i2c/vs6624.c
index e315d8b..0127932 100644
--- a/drivers/media/i2c/vs6624.c
+++ b/drivers/media/i2c/vs6624.c
@@ -45,19 +45,19 @@
};
static const struct vs6624_format {
- enum v4l2_mbus_pixelcode mbus_code;
+ u32 mbus_code;
enum v4l2_colorspace colorspace;
} vs6624_formats[] = {
{
- .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
.colorspace = V4L2_COLORSPACE_JPEG,
},
{
- .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
.colorspace = V4L2_COLORSPACE_JPEG,
},
{
- .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE,
+ .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE,
.colorspace = V4L2_COLORSPACE_SRGB,
},
};
@@ -65,7 +65,7 @@
static struct v4l2_mbus_framefmt vs6624_default_fmt = {
.width = VGA_WIDTH,
.height = VGA_HEIGHT,
- .code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
.field = V4L2_FIELD_NONE,
.colorspace = V4L2_COLORSPACE_JPEG,
};
@@ -558,7 +558,7 @@
}
static int vs6624_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
- enum v4l2_mbus_pixelcode *code)
+ u32 *code)
{
if (index >= ARRAY_SIZE(vs6624_formats))
return -EINVAL;
@@ -605,15 +605,15 @@
/* set image format */
switch (fmt->code) {
- case V4L2_MBUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_UYVY8_2X8:
vs6624_write(sd, VS6624_IMG_FMT0, 0x0);
vs6624_write(sd, VS6624_YUV_SETUP, 0x1);
break;
- case V4L2_MBUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_YUYV8_2X8:
vs6624_write(sd, VS6624_IMG_FMT0, 0x0);
vs6624_write(sd, VS6624_YUV_SETUP, 0x3);
break;
- case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ case MEDIA_BUS_FMT_RGB565_2X8_LE:
vs6624_write(sd, VS6624_IMG_FMT0, 0x4);
vs6624_write(sd, VS6624_RGB_SETUP, 0x0);
break;
diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c
index 37c334e..4d8e01c 100644
--- a/drivers/media/media-entity.c
+++ b/drivers/media/media-entity.c
@@ -279,8 +279,14 @@
continue;
ret = entity->ops->link_validate(link);
- if (ret < 0 && ret != -ENOIOCTLCMD)
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_dbg(entity->parent->dev,
+ "link validation failed for \"%s\":%u -> \"%s\":%u, error %d\n",
+ entity->name, link->source->index,
+ link->sink->entity->name,
+ link->sink->index, ret);
goto error;
+ }
}
/* Either no links or validated links are fine. */
@@ -288,6 +294,11 @@
if (!bitmap_full(active, entity->num_pads)) {
ret = -EPIPE;
+ dev_dbg(entity->parent->dev,
+ "\"%s\":%u must be connected by an enabled link\n",
+ entity->name,
+ (unsigned)find_first_zero_bit(
+ active, entity->num_pads));
goto error;
}
}
diff --git a/drivers/media/parport/Kconfig b/drivers/media/parport/Kconfig
deleted file mode 100644
index ef209f5..0000000
--- a/drivers/media/parport/Kconfig
+++ /dev/null
@@ -1,57 +0,0 @@
-menuconfig MEDIA_PARPORT_SUPPORT
- bool "ISA and parallel port devices"
- depends on (ISA || PARPORT) && MEDIA_CAMERA_SUPPORT
- help
- Enables drivers for ISA and parallel port bus. If you
- need media drivers using those legacy buses, say Y.
-
-if MEDIA_PARPORT_SUPPORT
-config VIDEO_BWQCAM
- tristate "Quickcam BW Video For Linux"
- depends on m
- depends on PARPORT && VIDEO_V4L2
- select VIDEOBUF2_VMALLOC
- help
- Say Y have if you the black and white version of the QuickCam
- camera. See the next option for the color version.
-
- To compile this driver as a module, choose M here: the
- module will be called bw-qcam.
-
-config VIDEO_CQCAM
- tristate "QuickCam Colour Video For Linux"
- depends on m
- depends on PARPORT && VIDEO_V4L2
- help
- This is the video4linux driver for the colour version of the
- Connectix QuickCam. If you have one of these cameras, say Y here,
- otherwise say N. This driver does not work with the original
- monochrome QuickCam, QuickCam VC or QuickClip. It is also available
- as a module (c-qcam).
- Read <file:Documentation/video4linux/CQcam.txt> for more information.
-
-config VIDEO_PMS
- tristate "Mediavision Pro Movie Studio Video For Linux"
- depends on m
- depends on ISA && VIDEO_V4L2
- help
- Say Y if you have the ISA Mediavision Pro Movie Studio
- capture card.
-
- To compile this driver as a module, choose M here: the
- module will be called pms.
-
-config VIDEO_W9966
- tristate "W9966CF Webcam (FlyCam Supra and others) Video For Linux"
- depends on m
- depends on PARPORT_1284 && PARPORT && VIDEO_V4L2
- help
- Video4linux driver for Winbond's w9966 based Webcams.
- Currently tested with the LifeView FlyCam Supra.
- If you have one of these cameras, say Y here
- otherwise say N.
- This driver is also available as a module (w9966).
-
- Check out <file:Documentation/video4linux/w9966.txt> for more
- information.
-endif
diff --git a/drivers/media/parport/Makefile b/drivers/media/parport/Makefile
deleted file mode 100644
index f0b2dbe..0000000
--- a/drivers/media/parport/Makefile
+++ /dev/null
@@ -1,4 +0,0 @@
-obj-$(CPTCFG_VIDEO_CQCAM) += c-qcam.o
-obj-$(CPTCFG_VIDEO_BWQCAM) += bw-qcam.o
-obj-$(CPTCFG_VIDEO_W9966) += w9966.o
-obj-$(CPTCFG_VIDEO_PMS) += pms.o
diff --git a/drivers/media/parport/bw-qcam.c b/drivers/media/parport/bw-qcam.c
deleted file mode 100644
index 67b9da1..0000000
--- a/drivers/media/parport/bw-qcam.c
+++ /dev/null
@@ -1,1177 +0,0 @@
-/*
- * QuickCam Driver For Video4Linux.
- *
- * Video4Linux conversion work by Alan Cox.
- * Parport compatibility by Phil Blundell.
- * Busy loop avoidance by Mark Cooke.
- *
- * Module parameters:
- *
- * maxpoll=<1 - 5000>
- *
- * When polling the QuickCam for a response, busy-wait for a
- * maximum of this many loops. The default of 250 gives little
- * impact on interactive response.
- *
- * NOTE: If this parameter is set too high, the processor
- * will busy wait until this loop times out, and then
- * slowly poll for a further 5 seconds before failing
- * the transaction. You have been warned.
- *
- * yieldlines=<1 - 250>
- *
- * When acquiring a frame from the camera, the data gathering
- * loop will yield back to the scheduler after completing
- * this many lines. The default of 4 provides a trade-off
- * between increased frame acquisition time and impact on
- * interactive response.
- */
-
-/* qcam-lib.c -- Library for programming with the Connectix QuickCam.
- * See the included documentation for usage instructions and details
- * of the protocol involved. */
-
-
-/* Version 0.5, August 4, 1996 */
-/* Version 0.7, August 27, 1996 */
-/* Version 0.9, November 17, 1996 */
-
-
-/******************************************************************
-
-Copyright (C) 1996 by Scott Laird
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL SCOTT LAIRD BE LIABLE FOR ANY CLAIM, DAMAGES OR
-OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
-
-******************************************************************/
-
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/parport.h>
-#include <linux/sched.h>
-#include <linux/videodev2.h>
-#include <linux/mutex.h>
-#include <asm/uaccess.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-fh.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-event.h>
-#include <media/videobuf2-vmalloc.h>
-
-/* One from column A... */
-#define QC_NOTSET 0
-#define QC_UNIDIR 1
-#define QC_BIDIR 2
-#define QC_SERIAL 3
-
-/* ... and one from column B */
-#define QC_ANY 0x00
-#define QC_FORCE_UNIDIR 0x10
-#define QC_FORCE_BIDIR 0x20
-#define QC_FORCE_SERIAL 0x30
-/* in the port_mode member */
-
-#define QC_MODE_MASK 0x07
-#define QC_FORCE_MASK 0x70
-
-#define MAX_HEIGHT 243
-#define MAX_WIDTH 336
-
-/* Bit fields for status flags */
-#define QC_PARAM_CHANGE 0x01 /* Camera status change has occurred */
-
-struct qcam {
- struct v4l2_device v4l2_dev;
- struct video_device vdev;
- struct v4l2_ctrl_handler hdl;
- struct vb2_queue vb_vidq;
- struct pardevice *pdev;
- struct parport *pport;
- struct mutex lock;
- struct mutex queue_lock;
- int width, height;
- int bpp;
- int mode;
- int contrast, brightness, whitebal;
- int port_mode;
- int transfer_scale;
- int top, left;
- int status;
- unsigned int saved_bits;
- unsigned long in_use;
-};
-
-static unsigned int maxpoll = 250; /* Maximum busy-loop count for qcam I/O */
-static unsigned int yieldlines = 4; /* Yield after this many during capture */
-static int video_nr = -1;
-static unsigned int force_init; /* Whether to probe aggressively */
-
-module_param(maxpoll, int, 0);
-module_param(yieldlines, int, 0);
-module_param(video_nr, int, 0);
-
-/* Set force_init=1 to avoid detection by polling status register and
- * immediately attempt to initialize qcam */
-module_param(force_init, int, 0);
-
-#define MAX_CAMS 4
-static struct qcam *qcams[MAX_CAMS];
-static unsigned int num_cams;
-
-static inline int read_lpstatus(struct qcam *q)
-{
- return parport_read_status(q->pport);
-}
-
-static inline int read_lpdata(struct qcam *q)
-{
- return parport_read_data(q->pport);
-}
-
-static inline void write_lpdata(struct qcam *q, int d)
-{
- parport_write_data(q->pport, d);
-}
-
-static void write_lpcontrol(struct qcam *q, int d)
-{
- if (d & 0x20) {
- /* Set bidirectional mode to reverse (data in) */
- parport_data_reverse(q->pport);
- } else {
- /* Set bidirectional mode to forward (data out) */
- parport_data_forward(q->pport);
- }
-
- /* Now issue the regular port command, but strip out the
- * direction flag */
- d &= ~0x20;
- parport_write_control(q->pport, d);
-}
-
-
-/* qc_waithand busy-waits for a handshake signal from the QuickCam.
- * Almost all communication with the camera requires handshaking. */
-
-static int qc_waithand(struct qcam *q, int val)
-{
- int status;
- int runs = 0;
-
- if (val) {
- while (!((status = read_lpstatus(q)) & 8)) {
- /* 1000 is enough spins on the I/O for all normal
- cases, at that point we start to poll slowly
- until the camera wakes up. However, we are
- busy blocked until the camera responds, so
- setting it lower is much better for interactive
- response. */
-
- if (runs++ > maxpoll)
- msleep_interruptible(5);
- if (runs > (maxpoll + 1000)) /* 5 seconds */
- return -1;
- }
- } else {
- while (((status = read_lpstatus(q)) & 8)) {
- /* 1000 is enough spins on the I/O for all normal
- cases, at that point we start to poll slowly
- until the camera wakes up. However, we are
- busy blocked until the camera responds, so
- setting it lower is much better for interactive
- response. */
-
- if (runs++ > maxpoll)
- msleep_interruptible(5);
- if (runs++ > (maxpoll + 1000)) /* 5 seconds */
- return -1;
- }
- }
-
- return status;
-}
-
-/* Waithand2 is used when the qcam is in bidirectional mode, and the
- * handshaking signal is CamRdy2 (bit 0 of data reg) instead of CamRdy1
- * (bit 3 of status register). It also returns the last value read,
- * since this data is useful. */
-
-static unsigned int qc_waithand2(struct qcam *q, int val)
-{
- unsigned int status;
- int runs = 0;
-
- do {
- status = read_lpdata(q);
- /* 1000 is enough spins on the I/O for all normal
- cases, at that point we start to poll slowly
- until the camera wakes up. However, we are
- busy blocked until the camera responds, so
- setting it lower is much better for interactive
- response. */
-
- if (runs++ > maxpoll)
- msleep_interruptible(5);
- if (runs++ > (maxpoll + 1000)) /* 5 seconds */
- return 0;
- } while ((status & 1) != val);
-
- return status;
-}
-
-/* qc_command is probably a bit of a misnomer -- it's used to send
- * bytes *to* the camera. Generally, these bytes are either commands
- * or arguments to commands, so the name fits, but it still bugs me a
- * bit. See the documentation for a list of commands. */
-
-static int qc_command(struct qcam *q, int command)
-{
- int n1, n2;
- int cmd;
-
- write_lpdata(q, command);
- write_lpcontrol(q, 6);
-
- n1 = qc_waithand(q, 1);
-
- write_lpcontrol(q, 0xe);
- n2 = qc_waithand(q, 0);
-
- cmd = (n1 & 0xf0) | ((n2 & 0xf0) >> 4);
- return cmd;
-}
-
-static int qc_readparam(struct qcam *q)
-{
- int n1, n2;
- int cmd;
-
- write_lpcontrol(q, 6);
- n1 = qc_waithand(q, 1);
-
- write_lpcontrol(q, 0xe);
- n2 = qc_waithand(q, 0);
-
- cmd = (n1 & 0xf0) | ((n2 & 0xf0) >> 4);
- return cmd;
-}
-
-
-/* Try to detect a QuickCam. It appears to flash the upper 4 bits of
- the status register at 5-10 Hz. This is only used in the autoprobe
- code. Be aware that this isn't the way Connectix detects the
- camera (they send a reset and try to handshake), but this should be
- almost completely safe, while their method screws up my printer if
- I plug it in before the camera. */
-
-static int qc_detect(struct qcam *q)
-{
- int reg, lastreg;
- int count = 0;
- int i;
-
- if (force_init)
- return 1;
-
- lastreg = reg = read_lpstatus(q) & 0xf0;
-
- for (i = 0; i < 500; i++) {
- reg = read_lpstatus(q) & 0xf0;
- if (reg != lastreg)
- count++;
- lastreg = reg;
- mdelay(2);
- }
-
-
-#if 0
- /* Force camera detection during testing. Sometimes the camera
- won't be flashing these bits. Possibly unloading the module
- in the middle of a grab? Or some timeout condition?
- I've seen this parameter as low as 19 on my 450Mhz box - mpc */
- printk(KERN_DEBUG "Debugging: QCam detection counter <30-200 counts as detected>: %d\n", count);
- return 1;
-#endif
-
- /* Be (even more) liberal in what you accept... */
-
- if (count > 20 && count < 400) {
- return 1; /* found */
- } else {
- printk(KERN_ERR "No Quickcam found on port %s\n",
- q->pport->name);
- printk(KERN_DEBUG "Quickcam detection counter: %u\n", count);
- return 0; /* not found */
- }
-}
-
-/* Decide which scan mode to use. There's no real requirement that
- * the scanmode match the resolution in q->height and q-> width -- the
- * camera takes the picture at the resolution specified in the
- * "scanmode" and then returns the image at the resolution specified
- * with the resolution commands. If the scan is bigger than the
- * requested resolution, the upper-left hand corner of the scan is
- * returned. If the scan is smaller, then the rest of the image
- * returned contains garbage. */
-
-static int qc_setscanmode(struct qcam *q)
-{
- int old_mode = q->mode;
-
- switch (q->transfer_scale) {
- case 1:
- q->mode = 0;
- break;
- case 2:
- q->mode = 4;
- break;
- case 4:
- q->mode = 8;
- break;
- }
-
- switch (q->bpp) {
- case 4:
- break;
- case 6:
- q->mode += 2;
- break;
- }
-
- switch (q->port_mode & QC_MODE_MASK) {
- case QC_BIDIR:
- q->mode += 1;
- break;
- case QC_NOTSET:
- case QC_UNIDIR:
- break;
- }
-
- if (q->mode != old_mode)
- q->status |= QC_PARAM_CHANGE;
-
- return 0;
-}
-
-
-/* Reset the QuickCam. This uses the same sequence the Windows
- * QuickPic program uses. Someone with a bi-directional port should
- * check that bi-directional mode is detected right, and then
- * implement bi-directional mode in qc_readbyte(). */
-
-static void qc_reset(struct qcam *q)
-{
- switch (q->port_mode & QC_FORCE_MASK) {
- case QC_FORCE_UNIDIR:
- q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_UNIDIR;
- break;
-
- case QC_FORCE_BIDIR:
- q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_BIDIR;
- break;
-
- case QC_ANY:
- write_lpcontrol(q, 0x20);
- write_lpdata(q, 0x75);
-
- if (read_lpdata(q) != 0x75)
- q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_BIDIR;
- else
- q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_UNIDIR;
- break;
- }
-
- write_lpcontrol(q, 0xb);
- udelay(250);
- write_lpcontrol(q, 0xe);
- qc_setscanmode(q); /* in case port_mode changed */
-}
-
-
-
-/* Reset the QuickCam and program for brightness, contrast,
- * white-balance, and resolution. */
-
-static void qc_set(struct qcam *q)
-{
- int val;
- int val2;
-
- /* Set the brightness. Yes, this is repetitive, but it works.
- * Shorter versions seem to fail subtly. Feel free to try :-). */
- /* I think the problem was in qc_command, not here -- bls */
-
- qc_command(q, 0xb);
- qc_command(q, q->brightness);
-
- val = q->height / q->transfer_scale;
- qc_command(q, 0x11);
- qc_command(q, val);
- if ((q->port_mode & QC_MODE_MASK) == QC_UNIDIR && q->bpp == 6) {
- /* The normal "transfers per line" calculation doesn't seem to work
- as expected here (and yet it works fine in qc_scan). No idea
- why this case is the odd man out. Fortunately, Laird's original
- working version gives me a good way to guess at working values.
- -- bls */
- val = q->width;
- val2 = q->transfer_scale * 4;
- } else {
- val = q->width * q->bpp;
- val2 = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR) ? 24 : 8) *
- q->transfer_scale;
- }
- val = DIV_ROUND_UP(val, val2);
- qc_command(q, 0x13);
- qc_command(q, val);
-
- /* Setting top and left -- bls */
- qc_command(q, 0xd);
- qc_command(q, q->top);
- qc_command(q, 0xf);
- qc_command(q, q->left / 2);
-
- qc_command(q, 0x19);
- qc_command(q, q->contrast);
- qc_command(q, 0x1f);
- qc_command(q, q->whitebal);
-
- /* Clear flag that we must update the grabbing parameters on the camera
- before we grab the next frame */
- q->status &= (~QC_PARAM_CHANGE);
-}
-
-/* Qc_readbytes reads some bytes from the QC and puts them in
- the supplied buffer. It returns the number of bytes read,
- or -1 on error. */
-
-static inline int qc_readbytes(struct qcam *q, char buffer[])
-{
- int ret = 1;
- unsigned int hi, lo;
- unsigned int hi2, lo2;
- static int state;
-
- if (buffer == NULL) {
- state = 0;
- return 0;
- }
-
- switch (q->port_mode & QC_MODE_MASK) {
- case QC_BIDIR: /* Bi-directional Port */
- write_lpcontrol(q, 0x26);
- lo = (qc_waithand2(q, 1) >> 1);
- hi = (read_lpstatus(q) >> 3) & 0x1f;
- write_lpcontrol(q, 0x2e);
- lo2 = (qc_waithand2(q, 0) >> 1);
- hi2 = (read_lpstatus(q) >> 3) & 0x1f;
- switch (q->bpp) {
- case 4:
- buffer[0] = lo & 0xf;
- buffer[1] = ((lo & 0x70) >> 4) | ((hi & 1) << 3);
- buffer[2] = (hi & 0x1e) >> 1;
- buffer[3] = lo2 & 0xf;
- buffer[4] = ((lo2 & 0x70) >> 4) | ((hi2 & 1) << 3);
- buffer[5] = (hi2 & 0x1e) >> 1;
- ret = 6;
- break;
- case 6:
- buffer[0] = lo & 0x3f;
- buffer[1] = ((lo & 0x40) >> 6) | (hi << 1);
- buffer[2] = lo2 & 0x3f;
- buffer[3] = ((lo2 & 0x40) >> 6) | (hi2 << 1);
- ret = 4;
- break;
- }
- break;
-
- case QC_UNIDIR: /* Unidirectional Port */
- write_lpcontrol(q, 6);
- lo = (qc_waithand(q, 1) & 0xf0) >> 4;
- write_lpcontrol(q, 0xe);
- hi = (qc_waithand(q, 0) & 0xf0) >> 4;
-
- switch (q->bpp) {
- case 4:
- buffer[0] = lo;
- buffer[1] = hi;
- ret = 2;
- break;
- case 6:
- switch (state) {
- case 0:
- buffer[0] = (lo << 2) | ((hi & 0xc) >> 2);
- q->saved_bits = (hi & 3) << 4;
- state = 1;
- ret = 1;
- break;
- case 1:
- buffer[0] = lo | q->saved_bits;
- q->saved_bits = hi << 2;
- state = 2;
- ret = 1;
- break;
- case 2:
- buffer[0] = ((lo & 0xc) >> 2) | q->saved_bits;
- buffer[1] = ((lo & 3) << 4) | hi;
- state = 0;
- ret = 2;
- break;
- }
- break;
- }
- break;
- }
- return ret;
-}
-
-/* requests a scan from the camera. It sends the correct instructions
- * to the camera and then reads back the correct number of bytes. In
- * previous versions of this routine the return structure contained
- * the raw output from the camera, and there was a 'qc_convertscan'
- * function that converted that to a useful format. In version 0.3 I
- * rolled qc_convertscan into qc_scan and now I only return the
- * converted scan. The format is just an one-dimensional array of
- * characters, one for each pixel, with 0=black up to n=white, where
- * n=2^(bit depth)-1. Ask me for more details if you don't understand
- * this. */
-
-static long qc_capture(struct qcam *q, u8 *buf, unsigned long len)
-{
- int i, j, k, yield;
- int bytes;
- int linestotrans, transperline;
- int divisor;
- int pixels_per_line;
- int pixels_read = 0;
- int got = 0;
- char buffer[6];
- int shift = 8 - q->bpp;
- char invert;
-
- if (q->mode == -1)
- return -ENXIO;
-
- qc_command(q, 0x7);
- qc_command(q, q->mode);
-
- if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR) {
- write_lpcontrol(q, 0x2e); /* turn port around */
- write_lpcontrol(q, 0x26);
- qc_waithand(q, 1);
- write_lpcontrol(q, 0x2e);
- qc_waithand(q, 0);
- }
-
- /* strange -- should be 15:63 below, but 4bpp is odd */
- invert = (q->bpp == 4) ? 16 : 63;
-
- linestotrans = q->height / q->transfer_scale;
- pixels_per_line = q->width / q->transfer_scale;
- transperline = q->width * q->bpp;
- divisor = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR) ? 24 : 8) *
- q->transfer_scale;
- transperline = DIV_ROUND_UP(transperline, divisor);
-
- for (i = 0, yield = yieldlines; i < linestotrans; i++) {
- for (pixels_read = j = 0; j < transperline; j++) {
- bytes = qc_readbytes(q, buffer);
- for (k = 0; k < bytes && (pixels_read + k) < pixels_per_line; k++) {
- int o;
- if (buffer[k] == 0 && invert == 16) {
- /* 4bpp is odd (again) -- inverter is 16, not 15, but output
- must be 0-15 -- bls */
- buffer[k] = 16;
- }
- o = i * pixels_per_line + pixels_read + k;
- if (o < len) {
- u8 ch = invert - buffer[k];
- got++;
- buf[o] = ch << shift;
- }
- }
- pixels_read += bytes;
- }
- qc_readbytes(q, NULL); /* reset state machine */
-
- /* Grabbing an entire frame from the quickcam is a lengthy
- process. We don't (usually) want to busy-block the
- processor for the entire frame. yieldlines is a module
- parameter. If we yield every line, the minimum frame
- time will be 240 / 200 = 1.2 seconds. The compile-time
- default is to yield every 4 lines. */
- if (i >= yield) {
- msleep_interruptible(5);
- yield = i + yieldlines;
- }
- }
-
- if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR) {
- write_lpcontrol(q, 2);
- write_lpcontrol(q, 6);
- udelay(3);
- write_lpcontrol(q, 0xe);
- }
- if (got < len)
- return got;
- return len;
-}
-
-/* ------------------------------------------------------------------
- Videobuf operations
- ------------------------------------------------------------------*/
-static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
- unsigned int *nbuffers, unsigned int *nplanes,
- unsigned int sizes[], void *alloc_ctxs[])
-{
- struct qcam *dev = vb2_get_drv_priv(vq);
-
- if (0 == *nbuffers)
- *nbuffers = 3;
- *nplanes = 1;
- mutex_lock(&dev->lock);
- if (fmt)
- sizes[0] = fmt->fmt.pix.width * fmt->fmt.pix.height;
- else
- sizes[0] = (dev->width / dev->transfer_scale) *
- (dev->height / dev->transfer_scale);
- mutex_unlock(&dev->lock);
- return 0;
-}
-
-static void buffer_queue(struct vb2_buffer *vb)
-{
- vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
-}
-
-static void buffer_finish(struct vb2_buffer *vb)
-{
- struct qcam *qcam = vb2_get_drv_priv(vb->vb2_queue);
- void *vbuf = vb2_plane_vaddr(vb, 0);
- int size = vb->vb2_queue->plane_sizes[0];
- int len;
-
- if (!vb2_is_streaming(vb->vb2_queue))
- return;
-
- mutex_lock(&qcam->lock);
- parport_claim_or_block(qcam->pdev);
-
- qc_reset(qcam);
-
- /* Update the camera parameters if we need to */
- if (qcam->status & QC_PARAM_CHANGE)
- qc_set(qcam);
-
- len = qc_capture(qcam, vbuf, size);
-
- parport_release(qcam->pdev);
- mutex_unlock(&qcam->lock);
- v4l2_get_timestamp(&vb->v4l2_buf.timestamp);
- if (len != size)
- vb->state = VB2_BUF_STATE_ERROR;
- vb2_set_plane_payload(vb, 0, len);
-}
-
-static struct vb2_ops qcam_video_qops = {
- .queue_setup = queue_setup,
- .buf_queue = buffer_queue,
- .buf_finish = buffer_finish,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
-};
-
-/*
- * Video4linux interfacing
- */
-
-static int qcam_querycap(struct file *file, void *priv,
- struct v4l2_capability *vcap)
-{
- struct qcam *qcam = video_drvdata(file);
-
- strlcpy(vcap->driver, qcam->v4l2_dev.name, sizeof(vcap->driver));
- strlcpy(vcap->card, "Connectix B&W Quickcam", sizeof(vcap->card));
- strlcpy(vcap->bus_info, qcam->pport->name, sizeof(vcap->bus_info));
- vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
- V4L2_CAP_STREAMING;
- vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS;
- return 0;
-}
-
-static int qcam_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
-{
- if (vin->index > 0)
- return -EINVAL;
- strlcpy(vin->name, "Camera", sizeof(vin->name));
- vin->type = V4L2_INPUT_TYPE_CAMERA;
- vin->audioset = 0;
- vin->tuner = 0;
- vin->std = 0;
- vin->status = 0;
- return 0;
-}
-
-static int qcam_g_input(struct file *file, void *fh, unsigned int *inp)
-{
- *inp = 0;
- return 0;
-}
-
-static int qcam_s_input(struct file *file, void *fh, unsigned int inp)
-{
- return (inp > 0) ? -EINVAL : 0;
-}
-
-static int qcam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct qcam *qcam = video_drvdata(file);
- struct v4l2_pix_format *pix = &fmt->fmt.pix;
-
- pix->width = qcam->width / qcam->transfer_scale;
- pix->height = qcam->height / qcam->transfer_scale;
- pix->pixelformat = (qcam->bpp == 4) ? V4L2_PIX_FMT_Y4 : V4L2_PIX_FMT_Y6;
- pix->field = V4L2_FIELD_NONE;
- pix->bytesperline = pix->width;
- pix->sizeimage = pix->width * pix->height;
- /* Just a guess */
- pix->colorspace = V4L2_COLORSPACE_SRGB;
- return 0;
-}
-
-static int qcam_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct v4l2_pix_format *pix = &fmt->fmt.pix;
-
- if (pix->height <= 60 || pix->width <= 80) {
- pix->height = 60;
- pix->width = 80;
- } else if (pix->height <= 120 || pix->width <= 160) {
- pix->height = 120;
- pix->width = 160;
- } else {
- pix->height = 240;
- pix->width = 320;
- }
- if (pix->pixelformat != V4L2_PIX_FMT_Y4 &&
- pix->pixelformat != V4L2_PIX_FMT_Y6)
- pix->pixelformat = V4L2_PIX_FMT_Y4;
- pix->field = V4L2_FIELD_NONE;
- pix->bytesperline = pix->width;
- pix->sizeimage = pix->width * pix->height;
- /* Just a guess */
- pix->colorspace = V4L2_COLORSPACE_SRGB;
- return 0;
-}
-
-static int qcam_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct qcam *qcam = video_drvdata(file);
- struct v4l2_pix_format *pix = &fmt->fmt.pix;
- int ret = qcam_try_fmt_vid_cap(file, fh, fmt);
-
- if (ret)
- return ret;
- if (vb2_is_busy(&qcam->vb_vidq))
- return -EBUSY;
- qcam->width = 320;
- qcam->height = 240;
- if (pix->height == 60)
- qcam->transfer_scale = 4;
- else if (pix->height == 120)
- qcam->transfer_scale = 2;
- else
- qcam->transfer_scale = 1;
- if (pix->pixelformat == V4L2_PIX_FMT_Y6)
- qcam->bpp = 6;
- else
- qcam->bpp = 4;
-
- qc_setscanmode(qcam);
- /* We must update the camera before we grab. We could
- just have changed the grab size */
- qcam->status |= QC_PARAM_CHANGE;
- return 0;
-}
-
-static int qcam_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
-{
- static struct v4l2_fmtdesc formats[] = {
- { 0, 0, 0,
- "4-Bit Monochrome", V4L2_PIX_FMT_Y4,
- { 0, 0, 0, 0 }
- },
- { 1, 0, 0,
- "6-Bit Monochrome", V4L2_PIX_FMT_Y6,
- { 0, 0, 0, 0 }
- },
- };
- enum v4l2_buf_type type = fmt->type;
-
- if (fmt->index > 1)
- return -EINVAL;
-
- *fmt = formats[fmt->index];
- fmt->type = type;
- return 0;
-}
-
-static int qcam_enum_framesizes(struct file *file, void *fh,
- struct v4l2_frmsizeenum *fsize)
-{
- static const struct v4l2_frmsize_discrete sizes[] = {
- { 80, 60 },
- { 160, 120 },
- { 320, 240 },
- };
-
- if (fsize->index > 2)
- return -EINVAL;
- if (fsize->pixel_format != V4L2_PIX_FMT_Y4 &&
- fsize->pixel_format != V4L2_PIX_FMT_Y6)
- return -EINVAL;
- fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
- fsize->discrete = sizes[fsize->index];
- return 0;
-}
-
-static int qcam_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct qcam *qcam =
- container_of(ctrl->handler, struct qcam, hdl);
- int ret = 0;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- qcam->brightness = ctrl->val;
- break;
- case V4L2_CID_CONTRAST:
- qcam->contrast = ctrl->val;
- break;
- case V4L2_CID_GAMMA:
- qcam->whitebal = ctrl->val;
- break;
- default:
- ret = -EINVAL;
- break;
- }
- if (ret == 0)
- qcam->status |= QC_PARAM_CHANGE;
- return ret;
-}
-
-static const struct v4l2_file_operations qcam_fops = {
- .owner = THIS_MODULE,
- .open = v4l2_fh_open,
- .release = vb2_fop_release,
- .poll = vb2_fop_poll,
- .unlocked_ioctl = video_ioctl2,
- .read = vb2_fop_read,
- .mmap = vb2_fop_mmap,
-};
-
-static const struct v4l2_ioctl_ops qcam_ioctl_ops = {
- .vidioc_querycap = qcam_querycap,
- .vidioc_g_input = qcam_g_input,
- .vidioc_s_input = qcam_s_input,
- .vidioc_enum_input = qcam_enum_input,
- .vidioc_enum_fmt_vid_cap = qcam_enum_fmt_vid_cap,
- .vidioc_enum_framesizes = qcam_enum_framesizes,
- .vidioc_g_fmt_vid_cap = qcam_g_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = qcam_s_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = qcam_try_fmt_vid_cap,
- .vidioc_reqbufs = vb2_ioctl_reqbufs,
- .vidioc_create_bufs = vb2_ioctl_create_bufs,
- .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
- .vidioc_querybuf = vb2_ioctl_querybuf,
- .vidioc_qbuf = vb2_ioctl_qbuf,
- .vidioc_dqbuf = vb2_ioctl_dqbuf,
- .vidioc_streamon = vb2_ioctl_streamon,
- .vidioc_streamoff = vb2_ioctl_streamoff,
- .vidioc_log_status = v4l2_ctrl_log_status,
- .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
- .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
-};
-
-static const struct v4l2_ctrl_ops qcam_ctrl_ops = {
- .s_ctrl = qcam_s_ctrl,
-};
-
-/* Initialize the QuickCam driver control structure. This is where
- * defaults are set for people who don't have a config file.*/
-
-static struct qcam *qcam_init(struct parport *port)
-{
- struct qcam *qcam;
- struct v4l2_device *v4l2_dev;
- struct vb2_queue *q;
- int err;
-
- qcam = kzalloc(sizeof(struct qcam), GFP_KERNEL);
- if (qcam == NULL)
- return NULL;
-
- v4l2_dev = &qcam->v4l2_dev;
- snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "bw-qcam%u", num_cams);
-
- if (v4l2_device_register(port->dev, v4l2_dev) < 0) {
- v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
- kfree(qcam);
- return NULL;
- }
-
- v4l2_ctrl_handler_init(&qcam->hdl, 3);
- v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops,
- V4L2_CID_BRIGHTNESS, 0, 255, 1, 180);
- v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops,
- V4L2_CID_CONTRAST, 0, 255, 1, 192);
- v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops,
- V4L2_CID_GAMMA, 0, 255, 1, 105);
- if (qcam->hdl.error) {
- v4l2_err(v4l2_dev, "couldn't register controls\n");
- goto exit;
- }
-
- mutex_init(&qcam->lock);
- mutex_init(&qcam->queue_lock);
-
- /* initialize queue */
- q = &qcam->vb_vidq;
- q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
- q->drv_priv = qcam;
- q->ops = &qcam_video_qops;
- q->mem_ops = &vb2_vmalloc_memops;
- q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- err = vb2_queue_init(q);
- if (err < 0) {
- v4l2_err(v4l2_dev, "couldn't init vb2_queue for %s.\n", port->name);
- goto exit;
- }
- qcam->vdev.queue = q;
- qcam->vdev.queue->lock = &qcam->queue_lock;
-
- qcam->pport = port;
- qcam->pdev = parport_register_device(port, v4l2_dev->name, NULL, NULL,
- NULL, 0, NULL);
- if (qcam->pdev == NULL) {
- v4l2_err(v4l2_dev, "couldn't register for %s.\n", port->name);
- goto exit;
- }
-
- strlcpy(qcam->vdev.name, "Connectix QuickCam", sizeof(qcam->vdev.name));
- qcam->vdev.v4l2_dev = v4l2_dev;
- qcam->vdev.ctrl_handler = &qcam->hdl;
- qcam->vdev.fops = &qcam_fops;
- qcam->vdev.lock = &qcam->lock;
- qcam->vdev.ioctl_ops = &qcam_ioctl_ops;
- qcam->vdev.release = video_device_release_empty;
- video_set_drvdata(&qcam->vdev, qcam);
-
- qcam->port_mode = (QC_ANY | QC_NOTSET);
- qcam->width = 320;
- qcam->height = 240;
- qcam->bpp = 4;
- qcam->transfer_scale = 2;
- qcam->contrast = 192;
- qcam->brightness = 180;
- qcam->whitebal = 105;
- qcam->top = 1;
- qcam->left = 14;
- qcam->mode = -1;
- qcam->status = QC_PARAM_CHANGE;
- return qcam;
-
-exit:
- v4l2_ctrl_handler_free(&qcam->hdl);
- kfree(qcam);
- return NULL;
-}
-
-static int qc_calibrate(struct qcam *q)
-{
- /*
- * Bugfix by Hanno Mueller hmueller@kabel.de, Mai 21 96
- * The white balance is an individual value for each
- * quickcam.
- */
-
- int value;
- int count = 0;
-
- qc_command(q, 27); /* AutoAdjustOffset */
- qc_command(q, 0); /* Dummy Parameter, ignored by the camera */
-
- /* GetOffset (33) will read 255 until autocalibration */
- /* is finished. After that, a value of 1-254 will be */
- /* returned. */
-
- do {
- qc_command(q, 33);
- value = qc_readparam(q);
- mdelay(1);
- schedule();
- count++;
- } while (value == 0xff && count < 2048);
-
- q->whitebal = value;
- return value;
-}
-
-static int init_bwqcam(struct parport *port)
-{
- struct qcam *qcam;
-
- if (num_cams == MAX_CAMS) {
- printk(KERN_ERR "Too many Quickcams (max %d)\n", MAX_CAMS);
- return -ENOSPC;
- }
-
- qcam = qcam_init(port);
- if (qcam == NULL)
- return -ENODEV;
-
- parport_claim_or_block(qcam->pdev);
-
- qc_reset(qcam);
-
- if (qc_detect(qcam) == 0) {
- parport_release(qcam->pdev);
- parport_unregister_device(qcam->pdev);
- kfree(qcam);
- return -ENODEV;
- }
- qc_calibrate(qcam);
- v4l2_ctrl_handler_setup(&qcam->hdl);
-
- parport_release(qcam->pdev);
-
- v4l2_info(&qcam->v4l2_dev, "Connectix Quickcam on %s\n", qcam->pport->name);
-
- if (video_register_device(&qcam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
- parport_unregister_device(qcam->pdev);
- kfree(qcam);
- return -ENODEV;
- }
-
- qcams[num_cams++] = qcam;
-
- return 0;
-}
-
-static void close_bwqcam(struct qcam *qcam)
-{
- video_unregister_device(&qcam->vdev);
- v4l2_ctrl_handler_free(&qcam->hdl);
- parport_unregister_device(qcam->pdev);
- kfree(qcam);
-}
-
-/* The parport parameter controls which parports will be scanned.
- * Scanning all parports causes some printers to print a garbage page.
- * -- March 14, 1999 Billy Donahue <billy@escape.com> */
-#ifdef MODULE
-static char *parport[MAX_CAMS] = { NULL, };
-module_param_array(parport, charp, NULL, 0);
-#endif
-
-static int accept_bwqcam(struct parport *port)
-{
-#ifdef MODULE
- int n;
-
- if (parport[0] && strncmp(parport[0], "auto", 4) != 0) {
- /* user gave parport parameters */
- for (n = 0; n < MAX_CAMS && parport[n]; n++) {
- char *ep;
- unsigned long r;
- r = simple_strtoul(parport[n], &ep, 0);
- if (ep == parport[n]) {
- printk(KERN_ERR
- "bw-qcam: bad port specifier \"%s\"\n",
- parport[n]);
- continue;
- }
- if (r == port->number)
- return 1;
- }
- return 0;
- }
-#endif
- return 1;
-}
-
-static void bwqcam_attach(struct parport *port)
-{
- if (accept_bwqcam(port))
- init_bwqcam(port);
-}
-
-static void bwqcam_detach(struct parport *port)
-{
- int i;
- for (i = 0; i < num_cams; i++) {
- struct qcam *qcam = qcams[i];
- if (qcam && qcam->pdev->port == port) {
- qcams[i] = NULL;
- close_bwqcam(qcam);
- }
- }
-}
-
-static struct parport_driver bwqcam_driver = {
- .name = "bw-qcam",
- .attach = bwqcam_attach,
- .detach = bwqcam_detach,
-};
-
-static void __exit exit_bw_qcams(void)
-{
- parport_unregister_driver(&bwqcam_driver);
-}
-
-static int __init init_bw_qcams(void)
-{
-#ifdef MODULE
- /* Do some sanity checks on the module parameters. */
- if (maxpoll > 5000) {
- printk(KERN_INFO "Connectix Quickcam max-poll was above 5000. Using 5000.\n");
- maxpoll = 5000;
- }
-
- if (yieldlines < 1) {
- printk(KERN_INFO "Connectix Quickcam yieldlines was less than 1. Using 1.\n");
- yieldlines = 1;
- }
-#endif
- return parport_register_driver(&bwqcam_driver);
-}
-
-module_init(init_bw_qcams);
-module_exit(exit_bw_qcams);
-
-MODULE_LICENSE("GPL");
-MODULE_VERSION("0.0.3");
diff --git a/drivers/media/parport/c-qcam.c b/drivers/media/parport/c-qcam.c
deleted file mode 100644
index b9010bd..0000000
--- a/drivers/media/parport/c-qcam.c
+++ /dev/null
@@ -1,882 +0,0 @@
-/*
- * Video4Linux Colour QuickCam driver
- * Copyright 1997-2000 Philip Blundell <philb@gnu.org>
- *
- * Module parameters:
- *
- * parport=auto -- probe all parports (default)
- * parport=0 -- parport0 becomes qcam1
- * parport=2,0,1 -- parports 2,0,1 are tried in that order
- *
- * probe=0 -- do no probing, assume camera is present
- * probe=1 -- use IEEE-1284 autoprobe data only (default)
- * probe=2 -- probe aggressively for cameras
- *
- * force_rgb=1 -- force data format to RGB (default is BGR)
- *
- * The parport parameter controls which parports will be scanned.
- * Scanning all parports causes some printers to print a garbage page.
- * -- March 14, 1999 Billy Donahue <billy@escape.com>
- *
- * Fixed data format to BGR, added force_rgb parameter. Added missing
- * parport_unregister_driver() on module removal.
- * -- May 28, 2000 Claudio Matsuoka <claudio@conectiva.com>
- */
-
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/parport.h>
-#include <linux/sched.h>
-#include <linux/mutex.h>
-#include <linux/jiffies.h>
-#include <linux/videodev2.h>
-#include <asm/uaccess.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-fh.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-event.h>
-
-struct qcam {
- struct v4l2_device v4l2_dev;
- struct video_device vdev;
- struct v4l2_ctrl_handler hdl;
- struct pardevice *pdev;
- struct parport *pport;
- int width, height;
- int ccd_width, ccd_height;
- int mode;
- int contrast, brightness, whitebal;
- int top, left;
- unsigned int bidirectional;
- struct mutex lock;
-};
-
-/* cameras maximum */
-#define MAX_CAMS 4
-
-/* The three possible QuickCam modes */
-#define QC_MILLIONS 0x18
-#define QC_BILLIONS 0x10
-#define QC_THOUSANDS 0x08 /* with VIDEC compression (not supported) */
-
-/* The three possible decimations */
-#define QC_DECIMATION_1 0
-#define QC_DECIMATION_2 2
-#define QC_DECIMATION_4 4
-
-#define BANNER "Colour QuickCam for Video4Linux v0.06"
-
-static int parport[MAX_CAMS] = { [1 ... MAX_CAMS-1] = -1 };
-static int probe = 2;
-static bool force_rgb;
-static int video_nr = -1;
-
-/* FIXME: parport=auto would never have worked, surely? --RR */
-MODULE_PARM_DESC(parport, "parport=<auto|n[,n]...> for port detection method\n"
- "probe=<0|1|2> for camera detection method\n"
- "force_rgb=<0|1> for RGB data format (default BGR)");
-module_param_array(parport, int, NULL, 0);
-module_param(probe, int, 0);
-module_param(force_rgb, bool, 0);
-module_param(video_nr, int, 0);
-
-static struct qcam *qcams[MAX_CAMS];
-static unsigned int num_cams;
-
-static inline void qcam_set_ack(struct qcam *qcam, unsigned int i)
-{
- /* note: the QC specs refer to the PCAck pin by voltage, not
- software level. PC ports have builtin inverters. */
- parport_frob_control(qcam->pport, 8, i ? 8 : 0);
-}
-
-static inline unsigned int qcam_ready1(struct qcam *qcam)
-{
- return (parport_read_status(qcam->pport) & 0x8) ? 1 : 0;
-}
-
-static inline unsigned int qcam_ready2(struct qcam *qcam)
-{
- return (parport_read_data(qcam->pport) & 0x1) ? 1 : 0;
-}
-
-static unsigned int qcam_await_ready1(struct qcam *qcam, int value)
-{
- struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
- unsigned long oldjiffies = jiffies;
- unsigned int i;
-
- for (oldjiffies = jiffies;
- time_before(jiffies, oldjiffies + msecs_to_jiffies(40));)
- if (qcam_ready1(qcam) == value)
- return 0;
-
- /* If the camera didn't respond within 1/25 second, poll slowly
- for a while. */
- for (i = 0; i < 50; i++) {
- if (qcam_ready1(qcam) == value)
- return 0;
- msleep_interruptible(100);
- }
-
- /* Probably somebody pulled the plug out. Not much we can do. */
- v4l2_err(v4l2_dev, "ready1 timeout (%d) %x %x\n", value,
- parport_read_status(qcam->pport),
- parport_read_control(qcam->pport));
- return 1;
-}
-
-static unsigned int qcam_await_ready2(struct qcam *qcam, int value)
-{
- struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
- unsigned long oldjiffies = jiffies;
- unsigned int i;
-
- for (oldjiffies = jiffies;
- time_before(jiffies, oldjiffies + msecs_to_jiffies(40));)
- if (qcam_ready2(qcam) == value)
- return 0;
-
- /* If the camera didn't respond within 1/25 second, poll slowly
- for a while. */
- for (i = 0; i < 50; i++) {
- if (qcam_ready2(qcam) == value)
- return 0;
- msleep_interruptible(100);
- }
-
- /* Probably somebody pulled the plug out. Not much we can do. */
- v4l2_err(v4l2_dev, "ready2 timeout (%d) %x %x %x\n", value,
- parport_read_status(qcam->pport),
- parport_read_control(qcam->pport),
- parport_read_data(qcam->pport));
- return 1;
-}
-
-static int qcam_read_data(struct qcam *qcam)
-{
- unsigned int idata;
-
- qcam_set_ack(qcam, 0);
- if (qcam_await_ready1(qcam, 1))
- return -1;
- idata = parport_read_status(qcam->pport) & 0xf0;
- qcam_set_ack(qcam, 1);
- if (qcam_await_ready1(qcam, 0))
- return -1;
- idata |= parport_read_status(qcam->pport) >> 4;
- return idata;
-}
-
-static int qcam_write_data(struct qcam *qcam, unsigned int data)
-{
- struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
- unsigned int idata;
-
- parport_write_data(qcam->pport, data);
- idata = qcam_read_data(qcam);
- if (data != idata) {
- v4l2_warn(v4l2_dev, "sent %x but received %x\n", data,
- idata);
- return 1;
- }
- return 0;
-}
-
-static inline int qcam_set(struct qcam *qcam, unsigned int cmd, unsigned int data)
-{
- if (qcam_write_data(qcam, cmd))
- return -1;
- if (qcam_write_data(qcam, data))
- return -1;
- return 0;
-}
-
-static inline int qcam_get(struct qcam *qcam, unsigned int cmd)
-{
- if (qcam_write_data(qcam, cmd))
- return -1;
- return qcam_read_data(qcam);
-}
-
-static int qc_detect(struct qcam *qcam)
-{
- unsigned int stat, ostat, i, count = 0;
-
- /* The probe routine below is not very reliable. The IEEE-1284
- probe takes precedence. */
- /* XXX Currently parport provides no way to distinguish between
- "the IEEE probe was not done" and "the probe was done, but
- no device was found". Fix this one day. */
- if (qcam->pport->probe_info[0].class == PARPORT_CLASS_MEDIA
- && qcam->pport->probe_info[0].model
- && !strcmp(qcam->pdev->port->probe_info[0].model,
- "Color QuickCam 2.0")) {
- printk(KERN_DEBUG "QuickCam: Found by IEEE1284 probe.\n");
- return 1;
- }
-
- if (probe < 2)
- return 0;
-
- parport_write_control(qcam->pport, 0xc);
-
- /* look for a heartbeat */
- ostat = stat = parport_read_status(qcam->pport);
- for (i = 0; i < 250; i++) {
- mdelay(1);
- stat = parport_read_status(qcam->pport);
- if (ostat != stat) {
- if (++count >= 3)
- return 1;
- ostat = stat;
- }
- }
-
- /* Reset the camera and try again */
- parport_write_control(qcam->pport, 0xc);
- parport_write_control(qcam->pport, 0x8);
- mdelay(1);
- parport_write_control(qcam->pport, 0xc);
- mdelay(1);
- count = 0;
-
- ostat = stat = parport_read_status(qcam->pport);
- for (i = 0; i < 250; i++) {
- mdelay(1);
- stat = parport_read_status(qcam->pport);
- if (ostat != stat) {
- if (++count >= 3)
- return 1;
- ostat = stat;
- }
- }
-
- /* no (or flatline) camera, give up */
- return 0;
-}
-
-static void qc_reset(struct qcam *qcam)
-{
- parport_write_control(qcam->pport, 0xc);
- parport_write_control(qcam->pport, 0x8);
- mdelay(1);
- parport_write_control(qcam->pport, 0xc);
- mdelay(1);
-}
-
-/* Reset the QuickCam and program for brightness, contrast,
- * white-balance, and resolution. */
-
-static void qc_setup(struct qcam *qcam)
-{
- qc_reset(qcam);
-
- /* Set the brightness. */
- qcam_set(qcam, 11, qcam->brightness);
-
- /* Set the height and width. These refer to the actual
- CCD area *before* applying the selected decimation. */
- qcam_set(qcam, 17, qcam->ccd_height);
- qcam_set(qcam, 19, qcam->ccd_width / 2);
-
- /* Set top and left. */
- qcam_set(qcam, 0xd, qcam->top);
- qcam_set(qcam, 0xf, qcam->left);
-
- /* Set contrast and white balance. */
- qcam_set(qcam, 0x19, qcam->contrast);
- qcam_set(qcam, 0x1f, qcam->whitebal);
-
- /* Set the speed. */
- qcam_set(qcam, 45, 2);
-}
-
-/* Read some bytes from the camera and put them in the buffer.
- nbytes should be a multiple of 3, because bidirectional mode gives
- us three bytes at a time. */
-
-static unsigned int qcam_read_bytes(struct qcam *qcam, unsigned char *buf, unsigned int nbytes)
-{
- unsigned int bytes = 0;
-
- qcam_set_ack(qcam, 0);
- if (qcam->bidirectional) {
- /* It's a bidirectional port */
- while (bytes < nbytes) {
- unsigned int lo1, hi1, lo2, hi2;
- unsigned char r, g, b;
-
- if (qcam_await_ready2(qcam, 1))
- return bytes;
- lo1 = parport_read_data(qcam->pport) >> 1;
- hi1 = ((parport_read_status(qcam->pport) >> 3) & 0x1f) ^ 0x10;
- qcam_set_ack(qcam, 1);
- if (qcam_await_ready2(qcam, 0))
- return bytes;
- lo2 = parport_read_data(qcam->pport) >> 1;
- hi2 = ((parport_read_status(qcam->pport) >> 3) & 0x1f) ^ 0x10;
- qcam_set_ack(qcam, 0);
- r = lo1 | ((hi1 & 1) << 7);
- g = ((hi1 & 0x1e) << 3) | ((hi2 & 0x1e) >> 1);
- b = lo2 | ((hi2 & 1) << 7);
- if (force_rgb) {
- buf[bytes++] = r;
- buf[bytes++] = g;
- buf[bytes++] = b;
- } else {
- buf[bytes++] = b;
- buf[bytes++] = g;
- buf[bytes++] = r;
- }
- }
- } else {
- /* It's a unidirectional port */
- int i = 0, n = bytes;
- unsigned char rgb[3];
-
- while (bytes < nbytes) {
- unsigned int hi, lo;
-
- if (qcam_await_ready1(qcam, 1))
- return bytes;
- hi = (parport_read_status(qcam->pport) & 0xf0);
- qcam_set_ack(qcam, 1);
- if (qcam_await_ready1(qcam, 0))
- return bytes;
- lo = (parport_read_status(qcam->pport) & 0xf0);
- qcam_set_ack(qcam, 0);
- /* flip some bits */
- rgb[(i = bytes++ % 3)] = (hi | (lo >> 4)) ^ 0x88;
- if (i >= 2) {
-get_fragment:
- if (force_rgb) {
- buf[n++] = rgb[0];
- buf[n++] = rgb[1];
- buf[n++] = rgb[2];
- } else {
- buf[n++] = rgb[2];
- buf[n++] = rgb[1];
- buf[n++] = rgb[0];
- }
- }
- }
- if (i) {
- i = 0;
- goto get_fragment;
- }
- }
- return bytes;
-}
-
-#define BUFSZ 150
-
-static long qc_capture(struct qcam *qcam, char __user *buf, unsigned long len)
-{
- struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
- unsigned lines, pixelsperline;
- unsigned int is_bi_dir = qcam->bidirectional;
- size_t wantlen, outptr = 0;
- char tmpbuf[BUFSZ];
-
- if (!access_ok(VERIFY_WRITE, buf, len))
- return -EFAULT;
-
- /* Wait for camera to become ready */
- for (;;) {
- int i = qcam_get(qcam, 41);
-
- if (i == -1) {
- qc_setup(qcam);
- return -EIO;
- }
- if ((i & 0x80) == 0)
- break;
- schedule();
- }
-
- if (qcam_set(qcam, 7, (qcam->mode | (is_bi_dir ? 1 : 0)) + 1))
- return -EIO;
-
- lines = qcam->height;
- pixelsperline = qcam->width;
-
- if (is_bi_dir) {
- /* Turn the port around */
- parport_data_reverse(qcam->pport);
- mdelay(3);
- qcam_set_ack(qcam, 0);
- if (qcam_await_ready1(qcam, 1)) {
- qc_setup(qcam);
- return -EIO;
- }
- qcam_set_ack(qcam, 1);
- if (qcam_await_ready1(qcam, 0)) {
- qc_setup(qcam);
- return -EIO;
- }
- }
-
- wantlen = lines * pixelsperline * 24 / 8;
-
- while (wantlen) {
- size_t t, s;
-
- s = (wantlen > BUFSZ) ? BUFSZ : wantlen;
- t = qcam_read_bytes(qcam, tmpbuf, s);
- if (outptr < len) {
- size_t sz = len - outptr;
-
- if (sz > t)
- sz = t;
- if (__copy_to_user(buf + outptr, tmpbuf, sz))
- break;
- outptr += sz;
- }
- wantlen -= t;
- if (t < s)
- break;
- cond_resched();
- }
-
- len = outptr;
-
- if (wantlen) {
- v4l2_err(v4l2_dev, "short read.\n");
- if (is_bi_dir)
- parport_data_forward(qcam->pport);
- qc_setup(qcam);
- return len;
- }
-
- if (is_bi_dir) {
- int l;
-
- do {
- l = qcam_read_bytes(qcam, tmpbuf, 3);
- cond_resched();
- } while (l && (tmpbuf[0] == 0x7e || tmpbuf[1] == 0x7e || tmpbuf[2] == 0x7e));
- if (force_rgb) {
- if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf)
- v4l2_err(v4l2_dev, "bad EOF\n");
- } else {
- if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe)
- v4l2_err(v4l2_dev, "bad EOF\n");
- }
- qcam_set_ack(qcam, 0);
- if (qcam_await_ready1(qcam, 1)) {
- v4l2_err(v4l2_dev, "no ack after EOF\n");
- parport_data_forward(qcam->pport);
- qc_setup(qcam);
- return len;
- }
- parport_data_forward(qcam->pport);
- mdelay(3);
- qcam_set_ack(qcam, 1);
- if (qcam_await_ready1(qcam, 0)) {
- v4l2_err(v4l2_dev, "no ack to port turnaround\n");
- qc_setup(qcam);
- return len;
- }
- } else {
- int l;
-
- do {
- l = qcam_read_bytes(qcam, tmpbuf, 1);
- cond_resched();
- } while (l && tmpbuf[0] == 0x7e);
- l = qcam_read_bytes(qcam, tmpbuf + 1, 2);
- if (force_rgb) {
- if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf)
- v4l2_err(v4l2_dev, "bad EOF\n");
- } else {
- if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe)
- v4l2_err(v4l2_dev, "bad EOF\n");
- }
- }
-
- qcam_write_data(qcam, 0);
- return len;
-}
-
-/*
- * Video4linux interfacing
- */
-
-static int qcam_querycap(struct file *file, void *priv,
- struct v4l2_capability *vcap)
-{
- struct qcam *qcam = video_drvdata(file);
-
- strlcpy(vcap->driver, qcam->v4l2_dev.name, sizeof(vcap->driver));
- strlcpy(vcap->card, "Color Quickcam", sizeof(vcap->card));
- strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info));
- vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
- vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS;
- return 0;
-}
-
-static int qcam_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
-{
- if (vin->index > 0)
- return -EINVAL;
- strlcpy(vin->name, "Camera", sizeof(vin->name));
- vin->type = V4L2_INPUT_TYPE_CAMERA;
- vin->audioset = 0;
- vin->tuner = 0;
- vin->std = 0;
- vin->status = 0;
- return 0;
-}
-
-static int qcam_g_input(struct file *file, void *fh, unsigned int *inp)
-{
- *inp = 0;
- return 0;
-}
-
-static int qcam_s_input(struct file *file, void *fh, unsigned int inp)
-{
- return (inp > 0) ? -EINVAL : 0;
-}
-
-static int qcam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct qcam *qcam = video_drvdata(file);
- struct v4l2_pix_format *pix = &fmt->fmt.pix;
-
- pix->width = qcam->width;
- pix->height = qcam->height;
- pix->pixelformat = V4L2_PIX_FMT_RGB24;
- pix->field = V4L2_FIELD_NONE;
- pix->bytesperline = 3 * qcam->width;
- pix->sizeimage = 3 * qcam->width * qcam->height;
- /* Just a guess */
- pix->colorspace = V4L2_COLORSPACE_SRGB;
- return 0;
-}
-
-static int qcam_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct v4l2_pix_format *pix = &fmt->fmt.pix;
-
- if (pix->height < 60 || pix->width < 80) {
- pix->height = 60;
- pix->width = 80;
- } else if (pix->height < 120 || pix->width < 160) {
- pix->height = 120;
- pix->width = 160;
- } else {
- pix->height = 240;
- pix->width = 320;
- }
- pix->pixelformat = V4L2_PIX_FMT_RGB24;
- pix->field = V4L2_FIELD_NONE;
- pix->bytesperline = 3 * pix->width;
- pix->sizeimage = 3 * pix->width * pix->height;
- /* Just a guess */
- pix->colorspace = V4L2_COLORSPACE_SRGB;
- return 0;
-}
-
-static int qcam_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct qcam *qcam = video_drvdata(file);
- struct v4l2_pix_format *pix = &fmt->fmt.pix;
- int ret = qcam_try_fmt_vid_cap(file, fh, fmt);
-
- if (ret)
- return ret;
- switch (pix->height) {
- case 60:
- qcam->mode = QC_DECIMATION_4;
- break;
- case 120:
- qcam->mode = QC_DECIMATION_2;
- break;
- default:
- qcam->mode = QC_DECIMATION_1;
- break;
- }
-
- mutex_lock(&qcam->lock);
- qcam->mode |= QC_MILLIONS;
- qcam->height = pix->height;
- qcam->width = pix->width;
- parport_claim_or_block(qcam->pdev);
- qc_setup(qcam);
- parport_release(qcam->pdev);
- mutex_unlock(&qcam->lock);
- return 0;
-}
-
-static int qcam_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
-{
- static struct v4l2_fmtdesc formats[] = {
- { 0, 0, 0,
- "RGB 8:8:8", V4L2_PIX_FMT_RGB24,
- { 0, 0, 0, 0 }
- },
- };
- enum v4l2_buf_type type = fmt->type;
-
- if (fmt->index > 0)
- return -EINVAL;
-
- *fmt = formats[fmt->index];
- fmt->type = type;
- return 0;
-}
-
-static ssize_t qcam_read(struct file *file, char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct qcam *qcam = video_drvdata(file);
- int len;
-
- mutex_lock(&qcam->lock);
- parport_claim_or_block(qcam->pdev);
- /* Probably should have a semaphore against multiple users */
- len = qc_capture(qcam, buf, count);
- parport_release(qcam->pdev);
- mutex_unlock(&qcam->lock);
- return len;
-}
-
-static int qcam_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct qcam *qcam =
- container_of(ctrl->handler, struct qcam, hdl);
- int ret = 0;
-
- mutex_lock(&qcam->lock);
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- qcam->brightness = ctrl->val;
- break;
- case V4L2_CID_CONTRAST:
- qcam->contrast = ctrl->val;
- break;
- case V4L2_CID_GAMMA:
- qcam->whitebal = ctrl->val;
- break;
- default:
- ret = -EINVAL;
- break;
- }
- if (ret == 0) {
- parport_claim_or_block(qcam->pdev);
- qc_setup(qcam);
- parport_release(qcam->pdev);
- }
- mutex_unlock(&qcam->lock);
- return ret;
-}
-
-static const struct v4l2_file_operations qcam_fops = {
- .owner = THIS_MODULE,
- .open = v4l2_fh_open,
- .release = v4l2_fh_release,
- .poll = v4l2_ctrl_poll,
- .unlocked_ioctl = video_ioctl2,
- .read = qcam_read,
-};
-
-static const struct v4l2_ioctl_ops qcam_ioctl_ops = {
- .vidioc_querycap = qcam_querycap,
- .vidioc_g_input = qcam_g_input,
- .vidioc_s_input = qcam_s_input,
- .vidioc_enum_input = qcam_enum_input,
- .vidioc_enum_fmt_vid_cap = qcam_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = qcam_g_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = qcam_s_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = qcam_try_fmt_vid_cap,
- .vidioc_log_status = v4l2_ctrl_log_status,
- .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
- .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
-};
-
-static const struct v4l2_ctrl_ops qcam_ctrl_ops = {
- .s_ctrl = qcam_s_ctrl,
-};
-
-/* Initialize the QuickCam driver control structure. */
-
-static struct qcam *qcam_init(struct parport *port)
-{
- struct qcam *qcam;
- struct v4l2_device *v4l2_dev;
-
- qcam = kzalloc(sizeof(*qcam), GFP_KERNEL);
- if (qcam == NULL)
- return NULL;
-
- v4l2_dev = &qcam->v4l2_dev;
- strlcpy(v4l2_dev->name, "c-qcam", sizeof(v4l2_dev->name));
-
- if (v4l2_device_register(NULL, v4l2_dev) < 0) {
- v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
- kfree(qcam);
- return NULL;
- }
-
- v4l2_ctrl_handler_init(&qcam->hdl, 3);
- v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops,
- V4L2_CID_BRIGHTNESS, 0, 255, 1, 240);
- v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops,
- V4L2_CID_CONTRAST, 0, 255, 1, 192);
- v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops,
- V4L2_CID_GAMMA, 0, 255, 1, 128);
- if (qcam->hdl.error) {
- v4l2_err(v4l2_dev, "couldn't register controls\n");
- v4l2_ctrl_handler_free(&qcam->hdl);
- kfree(qcam);
- return NULL;
- }
-
- qcam->pport = port;
- qcam->pdev = parport_register_device(port, "c-qcam", NULL, NULL,
- NULL, 0, NULL);
-
- qcam->bidirectional = (qcam->pport->modes & PARPORT_MODE_TRISTATE) ? 1 : 0;
-
- if (qcam->pdev == NULL) {
- v4l2_err(v4l2_dev, "couldn't register for %s.\n", port->name);
- v4l2_ctrl_handler_free(&qcam->hdl);
- kfree(qcam);
- return NULL;
- }
-
- strlcpy(qcam->vdev.name, "Colour QuickCam", sizeof(qcam->vdev.name));
- qcam->vdev.v4l2_dev = v4l2_dev;
- qcam->vdev.fops = &qcam_fops;
- qcam->vdev.ioctl_ops = &qcam_ioctl_ops;
- qcam->vdev.release = video_device_release_empty;
- qcam->vdev.ctrl_handler = &qcam->hdl;
- video_set_drvdata(&qcam->vdev, qcam);
-
- mutex_init(&qcam->lock);
- qcam->width = qcam->ccd_width = 320;
- qcam->height = qcam->ccd_height = 240;
- qcam->mode = QC_MILLIONS | QC_DECIMATION_1;
- qcam->contrast = 192;
- qcam->brightness = 240;
- qcam->whitebal = 128;
- qcam->top = 1;
- qcam->left = 14;
- return qcam;
-}
-
-static int init_cqcam(struct parport *port)
-{
- struct qcam *qcam;
- struct v4l2_device *v4l2_dev;
-
- if (parport[0] != -1) {
- /* The user gave specific instructions */
- int i, found = 0;
-
- for (i = 0; i < MAX_CAMS && parport[i] != -1; i++) {
- if (parport[0] == port->number)
- found = 1;
- }
- if (!found)
- return -ENODEV;
- }
-
- if (num_cams == MAX_CAMS)
- return -ENOSPC;
-
- qcam = qcam_init(port);
- if (qcam == NULL)
- return -ENODEV;
-
- v4l2_dev = &qcam->v4l2_dev;
-
- parport_claim_or_block(qcam->pdev);
-
- qc_reset(qcam);
-
- if (probe && qc_detect(qcam) == 0) {
- parport_release(qcam->pdev);
- parport_unregister_device(qcam->pdev);
- kfree(qcam);
- return -ENODEV;
- }
-
- qc_setup(qcam);
-
- parport_release(qcam->pdev);
-
- if (video_register_device(&qcam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
- v4l2_err(v4l2_dev, "Unable to register Colour QuickCam on %s\n",
- qcam->pport->name);
- parport_unregister_device(qcam->pdev);
- kfree(qcam);
- return -ENODEV;
- }
-
- v4l2_info(v4l2_dev, "%s: Colour QuickCam found on %s\n",
- video_device_node_name(&qcam->vdev), qcam->pport->name);
-
- qcams[num_cams++] = qcam;
-
- return 0;
-}
-
-static void close_cqcam(struct qcam *qcam)
-{
- video_unregister_device(&qcam->vdev);
- v4l2_ctrl_handler_free(&qcam->hdl);
- parport_unregister_device(qcam->pdev);
- kfree(qcam);
-}
-
-static void cq_attach(struct parport *port)
-{
- init_cqcam(port);
-}
-
-static void cq_detach(struct parport *port)
-{
- /* Write this some day. */
-}
-
-static struct parport_driver cqcam_driver = {
- .name = "cqcam",
- .attach = cq_attach,
- .detach = cq_detach,
-};
-
-static int __init cqcam_init(void)
-{
- printk(KERN_INFO BANNER "\n");
-
- return parport_register_driver(&cqcam_driver);
-}
-
-static void __exit cqcam_cleanup(void)
-{
- unsigned int i;
-
- for (i = 0; i < num_cams; i++)
- close_cqcam(qcams[i]);
-
- parport_unregister_driver(&cqcam_driver);
-}
-
-MODULE_AUTHOR("Philip Blundell <philb@gnu.org>");
-MODULE_DESCRIPTION(BANNER);
-MODULE_LICENSE("GPL");
-MODULE_VERSION("0.0.4");
-
-module_init(cqcam_init);
-module_exit(cqcam_cleanup);
diff --git a/drivers/media/parport/pms.c b/drivers/media/parport/pms.c
deleted file mode 100644
index e6b4975..0000000
--- a/drivers/media/parport/pms.c
+++ /dev/null
@@ -1,1156 +0,0 @@
-/*
- * Media Vision Pro Movie Studio
- * or
- * "all you need is an I2C bus some RAM and a prayer"
- *
- * This draws heavily on code
- *
- * (c) Wolfgang Koehler, wolf@first.gmd.de, Dec. 1994
- * Kiefernring 15
- * 14478 Potsdam, Germany
- *
- * Most of this code is directly derived from his userspace driver.
- * His driver works so send any reports to alan@lxorguk.ukuu.org.uk
- * unless the userspace driver also doesn't work for you...
- *
- * Changes:
- * 25-11-2009 Hans Verkuil <hverkuil@xs4all.nl>
- * - converted to version 2 of the V4L API.
- * 08/07/2003 Daniele Bellucci <bellucda@tiscali.it>
- * - pms_capture: report back -EFAULT
- */
-
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
-#include <linux/mutex.h>
-#include <linux/uaccess.h>
-#include <linux/isa.h>
-#include <asm/io.h>
-
-#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-fh.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-device.h>
-
-MODULE_LICENSE("GPL");
-MODULE_VERSION("0.0.5");
-
-#define MOTOROLA 1
-#define PHILIPS2 2 /* SAA7191 */
-#define PHILIPS1 3
-#define MVVMEMORYWIDTH 0x40 /* 512 bytes */
-
-struct i2c_info {
- u8 slave;
- u8 sub;
- u8 data;
- u8 hits;
-};
-
-struct pms {
- struct v4l2_device v4l2_dev;
- struct video_device vdev;
- struct v4l2_ctrl_handler hdl;
- int height;
- int width;
- int depth;
- int input;
- struct mutex lock;
- int i2c_count;
- struct i2c_info i2cinfo[64];
-
- int decoder;
- int standard; /* 0 - auto 1 - ntsc 2 - pal 3 - secam */
- v4l2_std_id std;
- int io;
- int data;
- void __iomem *mem;
-};
-
-/*
- * I/O ports and Shared Memory
- */
-
-static int io_port = 0x250;
-module_param(io_port, int, 0);
-
-static int mem_base = 0xc8000;
-module_param(mem_base, int, 0);
-
-static int video_nr = -1;
-module_param(video_nr, int, 0);
-
-
-static inline void mvv_write(struct pms *dev, u8 index, u8 value)
-{
- outw(index | (value << 8), dev->io);
-}
-
-static inline u8 mvv_read(struct pms *dev, u8 index)
-{
- outb(index, dev->io);
- return inb(dev->data);
-}
-
-static int pms_i2c_stat(struct pms *dev, u8 slave)
-{
- int counter = 0;
- int i;
-
- outb(0x28, dev->io);
-
- while ((inb(dev->data) & 0x01) == 0)
- if (counter++ == 256)
- break;
-
- while ((inb(dev->data) & 0x01) != 0)
- if (counter++ == 256)
- break;
-
- outb(slave, dev->io);
-
- counter = 0;
- while ((inb(dev->data) & 0x01) == 0)
- if (counter++ == 256)
- break;
-
- while ((inb(dev->data) & 0x01) != 0)
- if (counter++ == 256)
- break;
-
- for (i = 0; i < 12; i++) {
- char st = inb(dev->data);
-
- if ((st & 2) != 0)
- return -1;
- if ((st & 1) == 0)
- break;
- }
- outb(0x29, dev->io);
- return inb(dev->data);
-}
-
-static int pms_i2c_write(struct pms *dev, u16 slave, u16 sub, u16 data)
-{
- int skip = 0;
- int count;
- int i;
-
- for (i = 0; i < dev->i2c_count; i++) {
- if ((dev->i2cinfo[i].slave == slave) &&
- (dev->i2cinfo[i].sub == sub)) {
- if (dev->i2cinfo[i].data == data)
- skip = 1;
- dev->i2cinfo[i].data = data;
- i = dev->i2c_count + 1;
- }
- }
-
- if (i == dev->i2c_count && dev->i2c_count < 64) {
- dev->i2cinfo[dev->i2c_count].slave = slave;
- dev->i2cinfo[dev->i2c_count].sub = sub;
- dev->i2cinfo[dev->i2c_count].data = data;
- dev->i2c_count++;
- }
-
- if (skip)
- return 0;
-
- mvv_write(dev, 0x29, sub);
- mvv_write(dev, 0x2A, data);
- mvv_write(dev, 0x28, slave);
-
- outb(0x28, dev->io);
-
- count = 0;
- while ((inb(dev->data) & 1) == 0)
- if (count > 255)
- break;
- while ((inb(dev->data) & 1) != 0)
- if (count > 255)
- break;
-
- count = inb(dev->data);
-
- if (count & 2)
- return -1;
- return count;
-}
-
-static int pms_i2c_read(struct pms *dev, int slave, int sub)
-{
- int i;
-
- for (i = 0; i < dev->i2c_count; i++) {
- if (dev->i2cinfo[i].slave == slave && dev->i2cinfo[i].sub == sub)
- return dev->i2cinfo[i].data;
- }
- return 0;
-}
-
-
-static void pms_i2c_andor(struct pms *dev, int slave, int sub, int and, int or)
-{
- u8 tmp;
-
- tmp = pms_i2c_read(dev, slave, sub);
- tmp = (tmp & and) | or;
- pms_i2c_write(dev, slave, sub, tmp);
-}
-
-/*
- * Control functions
- */
-
-
-static void pms_videosource(struct pms *dev, short source)
-{
- switch (dev->decoder) {
- case MOTOROLA:
- break;
- case PHILIPS2:
- pms_i2c_andor(dev, 0x8a, 0x06, 0x7f, source ? 0x80 : 0);
- break;
- case PHILIPS1:
- break;
- }
- mvv_write(dev, 0x2E, 0x31);
- /* Was: mvv_write(dev, 0x2E, source ? 0x31 : 0x30);
- But could not make this work correctly. Only Composite input
- worked for me. */
-}
-
-static void pms_hue(struct pms *dev, short hue)
-{
- switch (dev->decoder) {
- case MOTOROLA:
- pms_i2c_write(dev, 0x8a, 0x00, hue);
- break;
- case PHILIPS2:
- pms_i2c_write(dev, 0x8a, 0x07, hue);
- break;
- case PHILIPS1:
- pms_i2c_write(dev, 0x42, 0x07, hue);
- break;
- }
-}
-
-static void pms_saturation(struct pms *dev, short sat)
-{
- switch (dev->decoder) {
- case MOTOROLA:
- pms_i2c_write(dev, 0x8a, 0x00, sat);
- break;
- case PHILIPS1:
- pms_i2c_write(dev, 0x42, 0x12, sat);
- break;
- }
-}
-
-
-static void pms_contrast(struct pms *dev, short contrast)
-{
- switch (dev->decoder) {
- case MOTOROLA:
- pms_i2c_write(dev, 0x8a, 0x00, contrast);
- break;
- case PHILIPS1:
- pms_i2c_write(dev, 0x42, 0x13, contrast);
- break;
- }
-}
-
-static void pms_brightness(struct pms *dev, short brightness)
-{
- switch (dev->decoder) {
- case MOTOROLA:
- pms_i2c_write(dev, 0x8a, 0x00, brightness);
- pms_i2c_write(dev, 0x8a, 0x00, brightness);
- pms_i2c_write(dev, 0x8a, 0x00, brightness);
- break;
- case PHILIPS1:
- pms_i2c_write(dev, 0x42, 0x19, brightness);
- break;
- }
-}
-
-
-static void pms_format(struct pms *dev, short format)
-{
- int target;
-
- dev->standard = format;
-
- if (dev->decoder == PHILIPS1)
- target = 0x42;
- else if (dev->decoder == PHILIPS2)
- target = 0x8a;
- else
- return;
-
- switch (format) {
- case 0: /* Auto */
- pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
- pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x80);
- break;
- case 1: /* NTSC */
- pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
- pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x40);
- break;
- case 2: /* PAL */
- pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
- pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00);
- break;
- case 3: /* SECAM */
- pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x01);
- pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00);
- break;
- }
-}
-
-#ifdef FOR_FUTURE_EXPANSION
-
-/*
- * These features of the PMS card are not currently exposes. They
- * could become a private v4l ioctl for PMSCONFIG or somesuch if
- * people need it. We also don't yet use the PMS interrupt.
- */
-
-static void pms_hstart(struct pms *dev, short start)
-{
- switch (dev->decoder) {
- case PHILIPS1:
- pms_i2c_write(dev, 0x8a, 0x05, start);
- pms_i2c_write(dev, 0x8a, 0x18, start);
- break;
- case PHILIPS2:
- pms_i2c_write(dev, 0x42, 0x05, start);
- pms_i2c_write(dev, 0x42, 0x18, start);
- break;
- }
-}
-
-/*
- * Bandpass filters
- */
-
-static void pms_bandpass(struct pms *dev, short pass)
-{
- if (dev->decoder == PHILIPS2)
- pms_i2c_andor(dev, 0x8a, 0x06, 0xcf, (pass & 0x03) << 4);
- else if (dev->decoder == PHILIPS1)
- pms_i2c_andor(dev, 0x42, 0x06, 0xcf, (pass & 0x03) << 4);
-}
-
-static void pms_antisnow(struct pms *dev, short snow)
-{
- if (dev->decoder == PHILIPS2)
- pms_i2c_andor(dev, 0x8a, 0x06, 0xf3, (snow & 0x03) << 2);
- else if (dev->decoder == PHILIPS1)
- pms_i2c_andor(dev, 0x42, 0x06, 0xf3, (snow & 0x03) << 2);
-}
-
-static void pms_sharpness(struct pms *dev, short sharp)
-{
- if (dev->decoder == PHILIPS2)
- pms_i2c_andor(dev, 0x8a, 0x06, 0xfc, sharp & 0x03);
- else if (dev->decoder == PHILIPS1)
- pms_i2c_andor(dev, 0x42, 0x06, 0xfc, sharp & 0x03);
-}
-
-static void pms_chromaagc(struct pms *dev, short agc)
-{
- if (dev->decoder == PHILIPS2)
- pms_i2c_andor(dev, 0x8a, 0x0c, 0x9f, (agc & 0x03) << 5);
- else if (dev->decoder == PHILIPS1)
- pms_i2c_andor(dev, 0x42, 0x0c, 0x9f, (agc & 0x03) << 5);
-}
-
-static void pms_vertnoise(struct pms *dev, short noise)
-{
- if (dev->decoder == PHILIPS2)
- pms_i2c_andor(dev, 0x8a, 0x10, 0xfc, noise & 3);
- else if (dev->decoder == PHILIPS1)
- pms_i2c_andor(dev, 0x42, 0x10, 0xfc, noise & 3);
-}
-
-static void pms_forcecolour(struct pms *dev, short colour)
-{
- if (dev->decoder == PHILIPS2)
- pms_i2c_andor(dev, 0x8a, 0x0c, 0x7f, (colour & 1) << 7);
- else if (dev->decoder == PHILIPS1)
- pms_i2c_andor(dev, 0x42, 0x0c, 0x7, (colour & 1) << 7);
-}
-
-static void pms_antigamma(struct pms *dev, short gamma)
-{
- if (dev->decoder == PHILIPS2)
- pms_i2c_andor(dev, 0xb8, 0x00, 0x7f, (gamma & 1) << 7);
- else if (dev->decoder == PHILIPS1)
- pms_i2c_andor(dev, 0x42, 0x20, 0x7, (gamma & 1) << 7);
-}
-
-static void pms_prefilter(struct pms *dev, short filter)
-{
- if (dev->decoder == PHILIPS2)
- pms_i2c_andor(dev, 0x8a, 0x06, 0xbf, (filter & 1) << 6);
- else if (dev->decoder == PHILIPS1)
- pms_i2c_andor(dev, 0x42, 0x06, 0xbf, (filter & 1) << 6);
-}
-
-static void pms_hfilter(struct pms *dev, short filter)
-{
- if (dev->decoder == PHILIPS2)
- pms_i2c_andor(dev, 0xb8, 0x04, 0x1f, (filter & 7) << 5);
- else if (dev->decoder == PHILIPS1)
- pms_i2c_andor(dev, 0x42, 0x24, 0x1f, (filter & 7) << 5);
-}
-
-static void pms_vfilter(struct pms *dev, short filter)
-{
- if (dev->decoder == PHILIPS2)
- pms_i2c_andor(dev, 0xb8, 0x08, 0x9f, (filter & 3) << 5);
- else if (dev->decoder == PHILIPS1)
- pms_i2c_andor(dev, 0x42, 0x28, 0x9f, (filter & 3) << 5);
-}
-
-static void pms_killcolour(struct pms *dev, short colour)
-{
- if (dev->decoder == PHILIPS2) {
- pms_i2c_andor(dev, 0x8a, 0x08, 0x07, (colour & 0x1f) << 3);
- pms_i2c_andor(dev, 0x8a, 0x09, 0x07, (colour & 0x1f) << 3);
- } else if (dev->decoder == PHILIPS1) {
- pms_i2c_andor(dev, 0x42, 0x08, 0x07, (colour & 0x1f) << 3);
- pms_i2c_andor(dev, 0x42, 0x09, 0x07, (colour & 0x1f) << 3);
- }
-}
-
-static void pms_chromagain(struct pms *dev, short chroma)
-{
- if (dev->decoder == PHILIPS2)
- pms_i2c_write(dev, 0x8a, 0x11, chroma);
- else if (dev->decoder == PHILIPS1)
- pms_i2c_write(dev, 0x42, 0x11, chroma);
-}
-
-
-static void pms_spacialcompl(struct pms *dev, short data)
-{
- mvv_write(dev, 0x3b, data);
-}
-
-static void pms_spacialcomph(struct pms *dev, short data)
-{
- mvv_write(dev, 0x3a, data);
-}
-
-static void pms_vstart(struct pms *dev, short start)
-{
- mvv_write(dev, 0x16, start);
- mvv_write(dev, 0x17, (start >> 8) & 0x01);
-}
-
-#endif
-
-static void pms_secamcross(struct pms *dev, short cross)
-{
- if (dev->decoder == PHILIPS2)
- pms_i2c_andor(dev, 0x8a, 0x0f, 0xdf, (cross & 1) << 5);
- else if (dev->decoder == PHILIPS1)
- pms_i2c_andor(dev, 0x42, 0x0f, 0xdf, (cross & 1) << 5);
-}
-
-
-static void pms_swsense(struct pms *dev, short sense)
-{
- if (dev->decoder == PHILIPS2) {
- pms_i2c_write(dev, 0x8a, 0x0a, sense);
- pms_i2c_write(dev, 0x8a, 0x0b, sense);
- } else if (dev->decoder == PHILIPS1) {
- pms_i2c_write(dev, 0x42, 0x0a, sense);
- pms_i2c_write(dev, 0x42, 0x0b, sense);
- }
-}
-
-
-static void pms_framerate(struct pms *dev, short frr)
-{
- int fps = (dev->std & V4L2_STD_525_60) ? 30 : 25;
-
- if (frr == 0)
- return;
- fps = fps/frr;
- mvv_write(dev, 0x14, 0x80 | fps);
- mvv_write(dev, 0x15, 1);
-}
-
-static void pms_vert(struct pms *dev, u8 deciden, u8 decinum)
-{
- mvv_write(dev, 0x1c, deciden); /* Denominator */
- mvv_write(dev, 0x1d, decinum); /* Numerator */
-}
-
-/*
- * Turn 16bit ratios into best small ratio the chipset can grok
- */
-
-static void pms_vertdeci(struct pms *dev, unsigned short decinum, unsigned short deciden)
-{
- /* Knock it down by / 5 once */
- if (decinum % 5 == 0) {
- deciden /= 5;
- decinum /= 5;
- }
- /*
- * 3's
- */
- while (decinum % 3 == 0 && deciden % 3 == 0) {
- deciden /= 3;
- decinum /= 3;
- }
- /*
- * 2's
- */
- while (decinum % 2 == 0 && deciden % 2 == 0) {
- decinum /= 2;
- deciden /= 2;
- }
- /*
- * Fudgyify
- */
- while (deciden > 32) {
- deciden /= 2;
- decinum = (decinum + 1) / 2;
- }
- if (deciden == 32)
- deciden--;
- pms_vert(dev, deciden, decinum);
-}
-
-static void pms_horzdeci(struct pms *dev, short decinum, short deciden)
-{
- if (decinum <= 512) {
- if (decinum % 5 == 0) {
- decinum /= 5;
- deciden /= 5;
- }
- } else {
- decinum = 512;
- deciden = 640; /* 768 would be ideal */
- }
-
- while (((decinum | deciden) & 1) == 0) {
- decinum >>= 1;
- deciden >>= 1;
- }
- while (deciden > 32) {
- deciden >>= 1;
- decinum = (decinum + 1) >> 1;
- }
- if (deciden == 32)
- deciden--;
-
- mvv_write(dev, 0x24, 0x80 | deciden);
- mvv_write(dev, 0x25, decinum);
-}
-
-static void pms_resolution(struct pms *dev, short width, short height)
-{
- int fg_height;
-
- fg_height = height;
- if (fg_height > 280)
- fg_height = 280;
-
- mvv_write(dev, 0x18, fg_height);
- mvv_write(dev, 0x19, fg_height >> 8);
-
- if (dev->std & V4L2_STD_525_60) {
- mvv_write(dev, 0x1a, 0xfc);
- mvv_write(dev, 0x1b, 0x00);
- if (height > fg_height)
- pms_vertdeci(dev, 240, 240);
- else
- pms_vertdeci(dev, fg_height, 240);
- } else {
- mvv_write(dev, 0x1a, 0x1a);
- mvv_write(dev, 0x1b, 0x01);
- if (fg_height > 256)
- pms_vertdeci(dev, 270, 270);
- else
- pms_vertdeci(dev, fg_height, 270);
- }
- mvv_write(dev, 0x12, 0);
- mvv_write(dev, 0x13, MVVMEMORYWIDTH);
- mvv_write(dev, 0x42, 0x00);
- mvv_write(dev, 0x43, 0x00);
- mvv_write(dev, 0x44, MVVMEMORYWIDTH);
-
- mvv_write(dev, 0x22, width + 8);
- mvv_write(dev, 0x23, (width + 8) >> 8);
-
- if (dev->std & V4L2_STD_525_60)
- pms_horzdeci(dev, width, 640);
- else
- pms_horzdeci(dev, width + 8, 768);
-
- mvv_write(dev, 0x30, mvv_read(dev, 0x30) & 0xfe);
- mvv_write(dev, 0x08, mvv_read(dev, 0x08) | 0x01);
- mvv_write(dev, 0x01, mvv_read(dev, 0x01) & 0xfd);
- mvv_write(dev, 0x32, 0x00);
- mvv_write(dev, 0x33, MVVMEMORYWIDTH);
-}
-
-
-/*
- * Set Input
- */
-
-static void pms_vcrinput(struct pms *dev, short input)
-{
- if (dev->decoder == PHILIPS2)
- pms_i2c_andor(dev, 0x8a, 0x0d, 0x7f, (input & 1) << 7);
- else if (dev->decoder == PHILIPS1)
- pms_i2c_andor(dev, 0x42, 0x0d, 0x7f, (input & 1) << 7);
-}
-
-
-static int pms_capture(struct pms *dev, char __user *buf, int rgb555, int count)
-{
- int y;
- int dw = 2 * dev->width;
- char *tmp; /* using a temp buffer is faster than direct */
- int cnt = 0;
- int len = 0;
- unsigned char r8 = 0x5; /* value for reg8 */
-
- tmp = kmalloc(dw + 32, GFP_KERNEL);
- if (!tmp)
- return 0;
-
- if (rgb555)
- r8 |= 0x20; /* else use untranslated rgb = 565 */
- mvv_write(dev, 0x08, r8); /* capture rgb555/565, init DRAM, PC enable */
-
-/* printf("%d %d %d %d %d %x %x\n",width,height,voff,nom,den,mvv_buf); */
-
- for (y = 0; y < dev->height; y++) {
- writeb(0, dev->mem); /* synchronisiert neue Zeile */
-
- /*
- * This is in truth a fifo, be very careful as if you
- * forgot this odd things will occur 8)
- */
-
- memcpy_fromio(tmp, dev->mem, dw + 32); /* discard 16 word */
- cnt -= dev->height;
- while (cnt <= 0) {
- /*
- * Don't copy too far
- */
- int dt = dw;
- if (dt + len > count)
- dt = count - len;
- cnt += dev->height;
- if (copy_to_user(buf, tmp + 32, dt))
- return len ? len : -EFAULT;
- buf += dt;
- len += dt;
- }
- }
- kfree(tmp);
- return len;
-}
-
-
-/*
- * Video4linux interfacing
- */
-
-static int pms_querycap(struct file *file, void *priv,
- struct v4l2_capability *vcap)
-{
- struct pms *dev = video_drvdata(file);
-
- strlcpy(vcap->driver, dev->v4l2_dev.name, sizeof(vcap->driver));
- strlcpy(vcap->card, "Mediavision PMS", sizeof(vcap->card));
- snprintf(vcap->bus_info, sizeof(vcap->bus_info),
- "ISA:%s", dev->v4l2_dev.name);
- vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
- vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS;
- return 0;
-}
-
-static int pms_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
-{
- static const char *inputs[4] = {
- "Composite",
- "S-Video",
- "Composite (VCR)",
- "S-Video (VCR)"
- };
-
- if (vin->index > 3)
- return -EINVAL;
- strlcpy(vin->name, inputs[vin->index], sizeof(vin->name));
- vin->type = V4L2_INPUT_TYPE_CAMERA;
- vin->audioset = 0;
- vin->tuner = 0;
- vin->std = V4L2_STD_ALL;
- vin->status = 0;
- return 0;
-}
-
-static int pms_g_input(struct file *file, void *fh, unsigned int *inp)
-{
- struct pms *dev = video_drvdata(file);
-
- *inp = dev->input;
- return 0;
-}
-
-static int pms_s_input(struct file *file, void *fh, unsigned int inp)
-{
- struct pms *dev = video_drvdata(file);
-
- if (inp > 3)
- return -EINVAL;
-
- dev->input = inp;
- pms_videosource(dev, inp & 1);
- pms_vcrinput(dev, inp >> 1);
- return 0;
-}
-
-static int pms_g_std(struct file *file, void *fh, v4l2_std_id *std)
-{
- struct pms *dev = video_drvdata(file);
-
- *std = dev->std;
- return 0;
-}
-
-static int pms_s_std(struct file *file, void *fh, v4l2_std_id std)
-{
- struct pms *dev = video_drvdata(file);
- int ret = 0;
-
- dev->std = std;
- if (dev->std & V4L2_STD_NTSC) {
- pms_framerate(dev, 30);
- pms_secamcross(dev, 0);
- pms_format(dev, 1);
- } else if (dev->std & V4L2_STD_PAL) {
- pms_framerate(dev, 25);
- pms_secamcross(dev, 0);
- pms_format(dev, 2);
- } else if (dev->std & V4L2_STD_SECAM) {
- pms_framerate(dev, 25);
- pms_secamcross(dev, 1);
- pms_format(dev, 2);
- } else {
- ret = -EINVAL;
- }
- /*
- switch (v->mode) {
- case VIDEO_MODE_AUTO:
- pms_framerate(dev, 25);
- pms_secamcross(dev, 0);
- pms_format(dev, 0);
- break;
- }*/
- return ret;
-}
-
-static int pms_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct pms *dev = container_of(ctrl->handler, struct pms, hdl);
- int ret = 0;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- pms_brightness(dev, ctrl->val);
- break;
- case V4L2_CID_CONTRAST:
- pms_contrast(dev, ctrl->val);
- break;
- case V4L2_CID_SATURATION:
- pms_saturation(dev, ctrl->val);
- break;
- case V4L2_CID_HUE:
- pms_hue(dev, ctrl->val);
- break;
- default:
- ret = -EINVAL;
- break;
- }
- return ret;
-}
-
-static int pms_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct pms *dev = video_drvdata(file);
- struct v4l2_pix_format *pix = &fmt->fmt.pix;
-
- pix->width = dev->width;
- pix->height = dev->height;
- pix->pixelformat = dev->width == 15 ?
- V4L2_PIX_FMT_RGB555 : V4L2_PIX_FMT_RGB565;
- pix->field = V4L2_FIELD_NONE;
- pix->bytesperline = 2 * dev->width;
- pix->sizeimage = 2 * dev->width * dev->height;
- /* Just a guess */
- pix->colorspace = V4L2_COLORSPACE_SRGB;
- return 0;
-}
-
-static int pms_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct v4l2_pix_format *pix = &fmt->fmt.pix;
-
- if (pix->height < 16 || pix->height > 480)
- return -EINVAL;
- if (pix->width < 16 || pix->width > 640)
- return -EINVAL;
- if (pix->pixelformat != V4L2_PIX_FMT_RGB555 &&
- pix->pixelformat != V4L2_PIX_FMT_RGB565)
- return -EINVAL;
- pix->field = V4L2_FIELD_NONE;
- pix->bytesperline = 2 * pix->width;
- pix->sizeimage = 2 * pix->width * pix->height;
- /* Just a guess */
- pix->colorspace = V4L2_COLORSPACE_SRGB;
- return 0;
-}
-
-static int pms_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct pms *dev = video_drvdata(file);
- struct v4l2_pix_format *pix = &fmt->fmt.pix;
- int ret = pms_try_fmt_vid_cap(file, fh, fmt);
-
- if (ret)
- return ret;
- dev->width = pix->width;
- dev->height = pix->height;
- dev->depth = (pix->pixelformat == V4L2_PIX_FMT_RGB555) ? 15 : 16;
- pms_resolution(dev, dev->width, dev->height);
- /* Ok we figured out what to use from our wide choice */
- return 0;
-}
-
-static int pms_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
-{
- static struct v4l2_fmtdesc formats[] = {
- { 0, 0, 0,
- "RGB 5:5:5", V4L2_PIX_FMT_RGB555,
- { 0, 0, 0, 0 }
- },
- { 1, 0, 0,
- "RGB 5:6:5", V4L2_PIX_FMT_RGB565,
- { 0, 0, 0, 0 }
- },
- };
- enum v4l2_buf_type type = fmt->type;
-
- if (fmt->index > 1)
- return -EINVAL;
-
- *fmt = formats[fmt->index];
- fmt->type = type;
- return 0;
-}
-
-static ssize_t pms_read(struct file *file, char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct pms *dev = video_drvdata(file);
- int len;
-
- len = pms_capture(dev, buf, (dev->depth == 15), count);
- return len;
-}
-
-static unsigned int pms_poll(struct file *file, struct poll_table_struct *wait)
-{
- struct v4l2_fh *fh = file->private_data;
- unsigned int res = POLLIN | POLLRDNORM;
-
- if (v4l2_event_pending(fh))
- res |= POLLPRI;
- poll_wait(file, &fh->wait, wait);
- return res;
-}
-
-static const struct v4l2_file_operations pms_fops = {
- .owner = THIS_MODULE,
- .open = v4l2_fh_open,
- .release = v4l2_fh_release,
- .poll = pms_poll,
- .unlocked_ioctl = video_ioctl2,
- .read = pms_read,
-};
-
-static const struct v4l2_ioctl_ops pms_ioctl_ops = {
- .vidioc_querycap = pms_querycap,
- .vidioc_g_input = pms_g_input,
- .vidioc_s_input = pms_s_input,
- .vidioc_enum_input = pms_enum_input,
- .vidioc_g_std = pms_g_std,
- .vidioc_s_std = pms_s_std,
- .vidioc_enum_fmt_vid_cap = pms_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = pms_g_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = pms_s_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = pms_try_fmt_vid_cap,
- .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
- .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
-};
-
-/*
- * Probe for and initialise the Mediavision PMS
- */
-
-static int init_mediavision(struct pms *dev)
-{
- int idec, decst;
- int i;
- static const unsigned char i2c_defs[] = {
- 0x4c, 0x30, 0x00, 0xe8,
- 0xb6, 0xe2, 0x00, 0x00,
- 0xff, 0xff, 0x00, 0x00,
- 0x00, 0x00, 0x78, 0x98,
- 0x00, 0x00, 0x00, 0x00,
- 0x34, 0x0a, 0xf4, 0xce,
- 0xe4
- };
-
- dev->mem = ioremap(mem_base, 0x800);
- if (!dev->mem)
- return -ENOMEM;
-
- if (!request_region(0x9a01, 1, "Mediavision PMS config")) {
- printk(KERN_WARNING "mediavision: unable to detect: 0x9a01 in use.\n");
- iounmap(dev->mem);
- return -EBUSY;
- }
- if (!request_region(dev->io, 3, "Mediavision PMS")) {
- printk(KERN_WARNING "mediavision: I/O port %d in use.\n", dev->io);
- release_region(0x9a01, 1);
- iounmap(dev->mem);
- return -EBUSY;
- }
- outb(0xb8, 0x9a01); /* Unlock */
- outb(dev->io >> 4, 0x9a01); /* Set IO port */
-
-
- decst = pms_i2c_stat(dev, 0x43);
-
- if (decst != -1)
- idec = 2;
- else if (pms_i2c_stat(dev, 0xb9) != -1)
- idec = 3;
- else if (pms_i2c_stat(dev, 0x8b) != -1)
- idec = 1;
- else
- idec = 0;
-
- printk(KERN_INFO "PMS type is %d\n", idec);
- if (idec == 0) {
- release_region(dev->io, 3);
- release_region(0x9a01, 1);
- iounmap(dev->mem);
- return -ENODEV;
- }
-
- /*
- * Ok we have a PMS of some sort
- */
-
- mvv_write(dev, 0x04, mem_base >> 12); /* Set the memory area */
-
- /* Ok now load the defaults */
-
- for (i = 0; i < 0x19; i++) {
- if (i2c_defs[i] == 0xff)
- pms_i2c_andor(dev, 0x8a, i, 0x07, 0x00);
- else
- pms_i2c_write(dev, 0x8a, i, i2c_defs[i]);
- }
-
- pms_i2c_write(dev, 0xb8, 0x00, 0x12);
- pms_i2c_write(dev, 0xb8, 0x04, 0x00);
- pms_i2c_write(dev, 0xb8, 0x07, 0x00);
- pms_i2c_write(dev, 0xb8, 0x08, 0x00);
- pms_i2c_write(dev, 0xb8, 0x09, 0xff);
- pms_i2c_write(dev, 0xb8, 0x0a, 0x00);
- pms_i2c_write(dev, 0xb8, 0x0b, 0x10);
- pms_i2c_write(dev, 0xb8, 0x10, 0x03);
-
- mvv_write(dev, 0x01, 0x00);
- mvv_write(dev, 0x05, 0xa0);
- mvv_write(dev, 0x08, 0x25);
- mvv_write(dev, 0x09, 0x00);
- mvv_write(dev, 0x0a, 0x20 | MVVMEMORYWIDTH);
-
- mvv_write(dev, 0x10, 0x02);
- mvv_write(dev, 0x1e, 0x0c);
- mvv_write(dev, 0x1f, 0x03);
- mvv_write(dev, 0x26, 0x06);
-
- mvv_write(dev, 0x2b, 0x00);
- mvv_write(dev, 0x2c, 0x20);
- mvv_write(dev, 0x2d, 0x00);
- mvv_write(dev, 0x2f, 0x70);
- mvv_write(dev, 0x32, 0x00);
- mvv_write(dev, 0x33, MVVMEMORYWIDTH);
- mvv_write(dev, 0x34, 0x00);
- mvv_write(dev, 0x35, 0x00);
- mvv_write(dev, 0x3a, 0x80);
- mvv_write(dev, 0x3b, 0x10);
- mvv_write(dev, 0x20, 0x00);
- mvv_write(dev, 0x21, 0x00);
- mvv_write(dev, 0x30, 0x22);
- return 0;
-}
-
-/*
- * Initialization and module stuff
- */
-
-#ifndef MODULE
-static int enable;
-module_param(enable, int, 0);
-#endif
-
-static const struct v4l2_ctrl_ops pms_ctrl_ops = {
- .s_ctrl = pms_s_ctrl,
-};
-
-static int pms_probe(struct device *pdev, unsigned int card)
-{
- struct pms *dev;
- struct v4l2_device *v4l2_dev;
- struct v4l2_ctrl_handler *hdl;
- int res;
-
-#ifndef MODULE
- if (!enable) {
- pr_err("PMS: not enabled, use pms.enable=1 to probe\n");
- return -ENODEV;
- }
-#endif
-
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (dev == NULL)
- return -ENOMEM;
-
- dev->decoder = PHILIPS2;
- dev->io = io_port;
- dev->data = io_port + 1;
- v4l2_dev = &dev->v4l2_dev;
- hdl = &dev->hdl;
-
- res = v4l2_device_register(pdev, v4l2_dev);
- if (res < 0) {
- v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
- goto free_dev;
- }
- v4l2_info(v4l2_dev, "Mediavision Pro Movie Studio driver 0.05\n");
-
- res = init_mediavision(dev);
- if (res) {
- v4l2_err(v4l2_dev, "Board not found.\n");
- goto free_io;
- }
-
- v4l2_ctrl_handler_init(hdl, 4);
- v4l2_ctrl_new_std(hdl, &pms_ctrl_ops,
- V4L2_CID_BRIGHTNESS, 0, 255, 1, 139);
- v4l2_ctrl_new_std(hdl, &pms_ctrl_ops,
- V4L2_CID_CONTRAST, 0, 255, 1, 70);
- v4l2_ctrl_new_std(hdl, &pms_ctrl_ops,
- V4L2_CID_SATURATION, 0, 255, 1, 64);
- v4l2_ctrl_new_std(hdl, &pms_ctrl_ops,
- V4L2_CID_HUE, 0, 255, 1, 0);
- if (hdl->error) {
- res = hdl->error;
- goto free_hdl;
- }
-
- mutex_init(&dev->lock);
- strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
- dev->vdev.v4l2_dev = v4l2_dev;
- dev->vdev.ctrl_handler = hdl;
- dev->vdev.fops = &pms_fops;
- dev->vdev.ioctl_ops = &pms_ioctl_ops;
- dev->vdev.release = video_device_release_empty;
- dev->vdev.lock = &dev->lock;
- dev->vdev.tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM;
- video_set_drvdata(&dev->vdev, dev);
- dev->std = V4L2_STD_NTSC_M;
- dev->height = 240;
- dev->width = 320;
- dev->depth = 16;
- pms_swsense(dev, 75);
- pms_resolution(dev, 320, 240);
- pms_videosource(dev, 0);
- pms_vcrinput(dev, 0);
- v4l2_ctrl_handler_setup(hdl);
- res = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, video_nr);
- if (res >= 0)
- return 0;
-
-free_hdl:
- v4l2_ctrl_handler_free(hdl);
- v4l2_device_unregister(&dev->v4l2_dev);
-free_io:
- release_region(dev->io, 3);
- release_region(0x9a01, 1);
- iounmap(dev->mem);
-free_dev:
- kfree(dev);
- return res;
-}
-
-static int pms_remove(struct device *pdev, unsigned int card)
-{
- struct pms *dev = dev_get_drvdata(pdev);
-
- video_unregister_device(&dev->vdev);
- v4l2_ctrl_handler_free(&dev->hdl);
- release_region(dev->io, 3);
- release_region(0x9a01, 1);
- iounmap(dev->mem);
- return 0;
-}
-
-static struct isa_driver pms_driver = {
- .probe = pms_probe,
- .remove = pms_remove,
- .driver = {
- .name = "pms",
- },
-};
-
-static int __init pms_init(void)
-{
- return isa_register_driver(&pms_driver, 1);
-}
-
-static void __exit pms_exit(void)
-{
- isa_unregister_driver(&pms_driver);
-}
-
-module_init(pms_init);
-module_exit(pms_exit);
diff --git a/drivers/media/parport/w9966.c b/drivers/media/parport/w9966.c
deleted file mode 100644
index f7502f3..0000000
--- a/drivers/media/parport/w9966.c
+++ /dev/null
@@ -1,980 +0,0 @@
-/*
- Winbond w9966cf Webcam parport driver.
-
- Version 0.33
-
- Copyright (C) 2001 Jakob Kemi <jakob.kemi@post.utfors.se>
-
- 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.
-*/
-/*
- Supported devices:
- *Lifeview FlyCam Supra (using the Philips saa7111a chip)
-
- Does any other model using the w9966 interface chip exist ?
-
- Todo:
-
- *Add a working EPP mode, since DMA ECP read isn't implemented
- in the parport drivers. (That's why it's so sloow)
-
- *Add support for other ccd-control chips than the saa7111
- please send me feedback on what kind of chips you have.
-
- *Add proper probing. I don't know what's wrong with the IEEE1284
- parport drivers but (IEEE1284_MODE_NIBBLE|IEEE1284_DEVICE_ID)
- and nibble read seems to be broken for some peripherals.
-
- *Add probing for onboard SRAM, port directions etc. (if possible)
-
- *Add support for the hardware compressed modes (maybe using v4l2)
-
- *Fix better support for the capture window (no skewed images, v4l
- interface to capt. window)
-
- *Probably some bugs that I don't know of
-
- Please support me by sending feedback!
-
- Changes:
-
- Alan Cox: Removed RGB mode for kernel merge, added THIS_MODULE
- and owner support for newer module locks
-*/
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/videodev2.h>
-#include <linux/slab.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-fh.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-event.h>
-#include <linux/parport.h>
-
-/*#define DEBUG*/ /* Undef me for production */
-
-#ifdef DEBUG
-#define DPRINTF(x, a...) printk(KERN_DEBUG "W9966: %s(): "x, __func__ , ##a)
-#else
-#define DPRINTF(x...)
-#endif
-
-/*
- * Defines, simple typedefs etc.
- */
-
-#define W9966_DRIVERNAME "W9966CF Webcam"
-#define W9966_MAXCAMS 4 /* Maximum number of cameras */
-#define W9966_RBUFFER 2048 /* Read buffer (must be an even number) */
-#define W9966_SRAMSIZE 131072 /* 128kb */
-#define W9966_SRAMID 0x02 /* check w9966cf.pdf */
-
-/* Empirically determined window limits */
-#define W9966_WND_MIN_X 16
-#define W9966_WND_MIN_Y 14
-#define W9966_WND_MAX_X 705
-#define W9966_WND_MAX_Y 253
-#define W9966_WND_MAX_W (W9966_WND_MAX_X - W9966_WND_MIN_X)
-#define W9966_WND_MAX_H (W9966_WND_MAX_Y - W9966_WND_MIN_Y)
-
-/* Keep track of our current state */
-#define W9966_STATE_PDEV 0x01
-#define W9966_STATE_CLAIMED 0x02
-#define W9966_STATE_VDEV 0x04
-
-#define W9966_I2C_W_ID 0x48
-#define W9966_I2C_R_ID 0x49
-#define W9966_I2C_R_DATA 0x08
-#define W9966_I2C_R_CLOCK 0x04
-#define W9966_I2C_W_DATA 0x02
-#define W9966_I2C_W_CLOCK 0x01
-
-struct w9966 {
- struct v4l2_device v4l2_dev;
- struct v4l2_ctrl_handler hdl;
- unsigned char dev_state;
- unsigned char i2c_state;
- unsigned short ppmode;
- struct parport *pport;
- struct pardevice *pdev;
- struct video_device vdev;
- unsigned short width;
- unsigned short height;
- unsigned char brightness;
- signed char contrast;
- signed char color;
- signed char hue;
- struct mutex lock;
-};
-
-/*
- * Module specific properties
- */
-
-MODULE_AUTHOR("Jakob Kemi <jakob.kemi@post.utfors.se>");
-MODULE_DESCRIPTION("Winbond w9966cf WebCam driver (0.32)");
-MODULE_LICENSE("GPL");
-MODULE_VERSION("0.33.1");
-
-#ifdef MODULE
-static char *pardev[] = {[0 ... W9966_MAXCAMS] = ""};
-#else
-static char *pardev[] = {[0 ... W9966_MAXCAMS] = "aggressive"};
-#endif
-module_param_array(pardev, charp, NULL, 0);
-MODULE_PARM_DESC(pardev, "pardev: where to search for\n"
- "\teach camera. 'aggressive' means brute-force search.\n"
- "\tEg: >pardev=parport3,aggressive,parport2,parport1< would assign\n"
- "\tcam 1 to parport3 and search every parport for cam 2 etc...");
-
-static int parmode;
-module_param(parmode, int, 0);
-MODULE_PARM_DESC(parmode, "parmode: transfer mode (0=auto, 1=ecp, 2=epp");
-
-static int video_nr = -1;
-module_param(video_nr, int, 0);
-
-static struct w9966 w9966_cams[W9966_MAXCAMS];
-
-/*
- * Private function defines
- */
-
-
-/* Set camera phase flags, so we know what to uninit when terminating */
-static inline void w9966_set_state(struct w9966 *cam, int mask, int val)
-{
- cam->dev_state = (cam->dev_state & ~mask) ^ val;
-}
-
-/* Get camera phase flags */
-static inline int w9966_get_state(struct w9966 *cam, int mask, int val)
-{
- return ((cam->dev_state & mask) == val);
-}
-
-/* Claim parport for ourself */
-static void w9966_pdev_claim(struct w9966 *cam)
-{
- if (w9966_get_state(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED))
- return;
- parport_claim_or_block(cam->pdev);
- w9966_set_state(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED);
-}
-
-/* Release parport for others to use */
-static void w9966_pdev_release(struct w9966 *cam)
-{
- if (w9966_get_state(cam, W9966_STATE_CLAIMED, 0))
- return;
- parport_release(cam->pdev);
- w9966_set_state(cam, W9966_STATE_CLAIMED, 0);
-}
-
-/* Read register from W9966 interface-chip
- Expects a claimed pdev
- -1 on error, else register data (byte) */
-static int w9966_read_reg(struct w9966 *cam, int reg)
-{
- /* ECP, read, regtransfer, REG, REG, REG, REG, REG */
- const unsigned char addr = 0x80 | (reg & 0x1f);
- unsigned char val;
-
- if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_ADDR) != 0)
- return -1;
- if (parport_write(cam->pport, &addr, 1) != 1)
- return -1;
- if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_DATA) != 0)
- return -1;
- if (parport_read(cam->pport, &val, 1) != 1)
- return -1;
-
- return val;
-}
-
-/* Write register to W9966 interface-chip
- Expects a claimed pdev
- -1 on error */
-static int w9966_write_reg(struct w9966 *cam, int reg, int data)
-{
- /* ECP, write, regtransfer, REG, REG, REG, REG, REG */
- const unsigned char addr = 0xc0 | (reg & 0x1f);
- const unsigned char val = data;
-
- if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_ADDR) != 0)
- return -1;
- if (parport_write(cam->pport, &addr, 1) != 1)
- return -1;
- if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_DATA) != 0)
- return -1;
- if (parport_write(cam->pport, &val, 1) != 1)
- return -1;
-
- return 0;
-}
-
-/*
- * Ugly and primitive i2c protocol functions
- */
-
-/* Sets the data line on the i2c bus.
- Expects a claimed pdev. */
-static void w9966_i2c_setsda(struct w9966 *cam, int state)
-{
- if (state)
- cam->i2c_state |= W9966_I2C_W_DATA;
- else
- cam->i2c_state &= ~W9966_I2C_W_DATA;
-
- w9966_write_reg(cam, 0x18, cam->i2c_state);
- udelay(5);
-}
-
-/* Get peripheral clock line
- Expects a claimed pdev. */
-static int w9966_i2c_getscl(struct w9966 *cam)
-{
- const unsigned char state = w9966_read_reg(cam, 0x18);
- return ((state & W9966_I2C_R_CLOCK) > 0);
-}
-
-/* Sets the clock line on the i2c bus.
- Expects a claimed pdev. -1 on error */
-static int w9966_i2c_setscl(struct w9966 *cam, int state)
-{
- unsigned long timeout;
-
- if (state)
- cam->i2c_state |= W9966_I2C_W_CLOCK;
- else
- cam->i2c_state &= ~W9966_I2C_W_CLOCK;
-
- w9966_write_reg(cam, 0x18, cam->i2c_state);
- udelay(5);
-
- /* we go to high, we also expect the peripheral to ack. */
- if (state) {
- timeout = jiffies + 100;
- while (!w9966_i2c_getscl(cam)) {
- if (time_after(jiffies, timeout))
- return -1;
- }
- }
- return 0;
-}
-
-#if 0
-/* Get peripheral data line
- Expects a claimed pdev. */
-static int w9966_i2c_getsda(struct w9966 *cam)
-{
- const unsigned char state = w9966_read_reg(cam, 0x18);
- return ((state & W9966_I2C_R_DATA) > 0);
-}
-#endif
-
-/* Write a byte with ack to the i2c bus.
- Expects a claimed pdev. -1 on error */
-static int w9966_i2c_wbyte(struct w9966 *cam, int data)
-{
- int i;
-
- for (i = 7; i >= 0; i--) {
- w9966_i2c_setsda(cam, (data >> i) & 0x01);
-
- if (w9966_i2c_setscl(cam, 1) == -1)
- return -1;
- w9966_i2c_setscl(cam, 0);
- }
-
- w9966_i2c_setsda(cam, 1);
-
- if (w9966_i2c_setscl(cam, 1) == -1)
- return -1;
- w9966_i2c_setscl(cam, 0);
-
- return 0;
-}
-
-/* Read a data byte with ack from the i2c-bus
- Expects a claimed pdev. -1 on error */
-#if 0
-static int w9966_i2c_rbyte(struct w9966 *cam)
-{
- unsigned char data = 0x00;
- int i;
-
- w9966_i2c_setsda(cam, 1);
-
- for (i = 0; i < 8; i++) {
- if (w9966_i2c_setscl(cam, 1) == -1)
- return -1;
- data = data << 1;
- if (w9966_i2c_getsda(cam))
- data |= 0x01;
-
- w9966_i2c_setscl(cam, 0);
- }
- return data;
-}
-#endif
-
-/* Read a register from the i2c device.
- Expects claimed pdev. -1 on error */
-#if 0
-static int w9966_read_reg_i2c(struct w9966 *cam, int reg)
-{
- int data;
-
- w9966_i2c_setsda(cam, 0);
- w9966_i2c_setscl(cam, 0);
-
- if (w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 ||
- w9966_i2c_wbyte(cam, reg) == -1)
- return -1;
-
- w9966_i2c_setsda(cam, 1);
- if (w9966_i2c_setscl(cam, 1) == -1)
- return -1;
- w9966_i2c_setsda(cam, 0);
- w9966_i2c_setscl(cam, 0);
-
- if (w9966_i2c_wbyte(cam, W9966_I2C_R_ID) == -1)
- return -1;
- data = w9966_i2c_rbyte(cam);
- if (data == -1)
- return -1;
-
- w9966_i2c_setsda(cam, 0);
-
- if (w9966_i2c_setscl(cam, 1) == -1)
- return -1;
- w9966_i2c_setsda(cam, 1);
-
- return data;
-}
-#endif
-
-/* Write a register to the i2c device.
- Expects claimed pdev. -1 on error */
-static int w9966_write_reg_i2c(struct w9966 *cam, int reg, int data)
-{
- w9966_i2c_setsda(cam, 0);
- w9966_i2c_setscl(cam, 0);
-
- if (w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 ||
- w9966_i2c_wbyte(cam, reg) == -1 ||
- w9966_i2c_wbyte(cam, data) == -1)
- return -1;
-
- w9966_i2c_setsda(cam, 0);
- if (w9966_i2c_setscl(cam, 1) == -1)
- return -1;
-
- w9966_i2c_setsda(cam, 1);
-
- return 0;
-}
-
-/* Find a good length for capture window (used both for W and H)
- A bit ugly but pretty functional. The capture length
- have to match the downscale */
-static int w9966_findlen(int near, int size, int maxlen)
-{
- int bestlen = size;
- int besterr = abs(near - bestlen);
- int len;
-
- for (len = size + 1; len < maxlen; len++) {
- int err;
- if (((64 * size) % len) != 0)
- continue;
-
- err = abs(near - len);
-
- /* Only continue as long as we keep getting better values */
- if (err > besterr)
- break;
-
- besterr = err;
- bestlen = len;
- }
-
- return bestlen;
-}
-
-/* Modify capture window (if necessary)
- and calculate downscaling
- Return -1 on error */
-static int w9966_calcscale(int size, int min, int max, int *beg, int *end, unsigned char *factor)
-{
- int maxlen = max - min;
- int len = *end - *beg + 1;
- int newlen = w9966_findlen(len, size, maxlen);
- int err = newlen - len;
-
- /* Check for bad format */
- if (newlen > maxlen || newlen < size)
- return -1;
-
- /* Set factor (6 bit fixed) */
- *factor = (64 * size) / newlen;
- if (*factor == 64)
- *factor = 0x00; /* downscale is disabled */
- else
- *factor |= 0x80; /* set downscale-enable bit */
-
- /* Modify old beginning and end */
- *beg -= err / 2;
- *end += err - (err / 2);
-
- /* Move window if outside borders */
- if (*beg < min) {
- *end += min - *beg;
- *beg += min - *beg;
- }
- if (*end > max) {
- *beg -= *end - max;
- *end -= *end - max;
- }
-
- return 0;
-}
-
-/* Setup the cameras capture window etc.
- Expects a claimed pdev
- return -1 on error */
-static int w9966_setup(struct w9966 *cam, int x1, int y1, int x2, int y2, int w, int h)
-{
- unsigned int i;
- unsigned int enh_s, enh_e;
- unsigned char scale_x, scale_y;
- unsigned char regs[0x1c];
- unsigned char saa7111_regs[] = {
- 0x21, 0x00, 0xd8, 0x23, 0x00, 0x80, 0x80, 0x00,
- 0x88, 0x10, 0x80, 0x40, 0x40, 0x00, 0x01, 0x00,
- 0x48, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x71, 0xe7, 0x00, 0x00, 0xc0
- };
-
-
- if (w * h * 2 > W9966_SRAMSIZE) {
- DPRINTF("capture window exceeds SRAM size!.\n");
- w = 200; h = 160; /* Pick default values */
- }
-
- w &= ~0x1;
- if (w < 2)
- w = 2;
- if (h < 1)
- h = 1;
- if (w > W9966_WND_MAX_W)
- w = W9966_WND_MAX_W;
- if (h > W9966_WND_MAX_H)
- h = W9966_WND_MAX_H;
-
- cam->width = w;
- cam->height = h;
-
- enh_s = 0;
- enh_e = w * h * 2;
-
- /* Modify capture window if necessary and calculate downscaling */
- if (w9966_calcscale(w, W9966_WND_MIN_X, W9966_WND_MAX_X, &x1, &x2, &scale_x) != 0 ||
- w9966_calcscale(h, W9966_WND_MIN_Y, W9966_WND_MAX_Y, &y1, &y2, &scale_y) != 0)
- return -1;
-
- DPRINTF("%dx%d, x: %d<->%d, y: %d<->%d, sx: %d/64, sy: %d/64.\n",
- w, h, x1, x2, y1, y2, scale_x & ~0x80, scale_y & ~0x80);
-
- /* Setup registers */
- regs[0x00] = 0x00; /* Set normal operation */
- regs[0x01] = 0x18; /* Capture mode */
- regs[0x02] = scale_y; /* V-scaling */
- regs[0x03] = scale_x; /* H-scaling */
-
- /* Capture window */
- regs[0x04] = (x1 & 0x0ff); /* X-start (8 low bits) */
- regs[0x05] = (x1 & 0x300)>>8; /* X-start (2 high bits) */
- regs[0x06] = (y1 & 0x0ff); /* Y-start (8 low bits) */
- regs[0x07] = (y1 & 0x300)>>8; /* Y-start (2 high bits) */
- regs[0x08] = (x2 & 0x0ff); /* X-end (8 low bits) */
- regs[0x09] = (x2 & 0x300)>>8; /* X-end (2 high bits) */
- regs[0x0a] = (y2 & 0x0ff); /* Y-end (8 low bits) */
-
- regs[0x0c] = W9966_SRAMID; /* SRAM-banks (1x 128kb) */
-
- /* Enhancement layer */
- regs[0x0d] = (enh_s & 0x000ff); /* Enh. start (0-7) */
- regs[0x0e] = (enh_s & 0x0ff00) >> 8; /* Enh. start (8-15) */
- regs[0x0f] = (enh_s & 0x70000) >> 16; /* Enh. start (16-17/18??) */
- regs[0x10] = (enh_e & 0x000ff); /* Enh. end (0-7) */
- regs[0x11] = (enh_e & 0x0ff00) >> 8; /* Enh. end (8-15) */
- regs[0x12] = (enh_e & 0x70000) >> 16; /* Enh. end (16-17/18??) */
-
- /* Misc */
- regs[0x13] = 0x40; /* VEE control (raw 4:2:2) */
- regs[0x17] = 0x00; /* ??? */
- regs[0x18] = cam->i2c_state = 0x00; /* Serial bus */
- regs[0x19] = 0xff; /* I/O port direction control */
- regs[0x1a] = 0xff; /* I/O port data register */
- regs[0x1b] = 0x10; /* ??? */
-
- /* SAA7111 chip settings */
- saa7111_regs[0x0a] = cam->brightness;
- saa7111_regs[0x0b] = cam->contrast;
- saa7111_regs[0x0c] = cam->color;
- saa7111_regs[0x0d] = cam->hue;
-
- /* Reset (ECP-fifo & serial-bus) */
- if (w9966_write_reg(cam, 0x00, 0x03) == -1)
- return -1;
-
- /* Write regs to w9966cf chip */
- for (i = 0; i < 0x1c; i++)
- if (w9966_write_reg(cam, i, regs[i]) == -1)
- return -1;
-
- /* Write regs to saa7111 chip */
- for (i = 0; i < 0x20; i++)
- if (w9966_write_reg_i2c(cam, i, saa7111_regs[i]) == -1)
- return -1;
-
- return 0;
-}
-
-/*
- * Video4linux interfacing
- */
-
-static int cam_querycap(struct file *file, void *priv,
- struct v4l2_capability *vcap)
-{
- struct w9966 *cam = video_drvdata(file);
-
- strlcpy(vcap->driver, cam->v4l2_dev.name, sizeof(vcap->driver));
- strlcpy(vcap->card, W9966_DRIVERNAME, sizeof(vcap->card));
- strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info));
- vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
- vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS;
- return 0;
-}
-
-static int cam_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
-{
- if (vin->index > 0)
- return -EINVAL;
- strlcpy(vin->name, "Camera", sizeof(vin->name));
- vin->type = V4L2_INPUT_TYPE_CAMERA;
- vin->audioset = 0;
- vin->tuner = 0;
- vin->std = 0;
- vin->status = 0;
- return 0;
-}
-
-static int cam_g_input(struct file *file, void *fh, unsigned int *inp)
-{
- *inp = 0;
- return 0;
-}
-
-static int cam_s_input(struct file *file, void *fh, unsigned int inp)
-{
- return (inp > 0) ? -EINVAL : 0;
-}
-
-static int cam_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct w9966 *cam =
- container_of(ctrl->handler, struct w9966, hdl);
- int ret = 0;
-
- mutex_lock(&cam->lock);
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- cam->brightness = ctrl->val;
- break;
- case V4L2_CID_CONTRAST:
- cam->contrast = ctrl->val;
- break;
- case V4L2_CID_SATURATION:
- cam->color = ctrl->val;
- break;
- case V4L2_CID_HUE:
- cam->hue = ctrl->val;
- break;
- default:
- ret = -EINVAL;
- break;
- }
-
- if (ret == 0) {
- w9966_pdev_claim(cam);
-
- if (w9966_write_reg_i2c(cam, 0x0a, cam->brightness) == -1 ||
- w9966_write_reg_i2c(cam, 0x0b, cam->contrast) == -1 ||
- w9966_write_reg_i2c(cam, 0x0c, cam->color) == -1 ||
- w9966_write_reg_i2c(cam, 0x0d, cam->hue) == -1) {
- ret = -EIO;
- }
-
- w9966_pdev_release(cam);
- }
- mutex_unlock(&cam->lock);
- return ret;
-}
-
-static int cam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct w9966 *cam = video_drvdata(file);
- struct v4l2_pix_format *pix = &fmt->fmt.pix;
-
- pix->width = cam->width;
- pix->height = cam->height;
- pix->pixelformat = V4L2_PIX_FMT_YUYV;
- pix->field = V4L2_FIELD_NONE;
- pix->bytesperline = 2 * cam->width;
- pix->sizeimage = 2 * cam->width * cam->height;
- /* Just a guess */
- pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
- return 0;
-}
-
-static int cam_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct v4l2_pix_format *pix = &fmt->fmt.pix;
-
- if (pix->width < 2)
- pix->width = 2;
- if (pix->height < 1)
- pix->height = 1;
- if (pix->width > W9966_WND_MAX_W)
- pix->width = W9966_WND_MAX_W;
- if (pix->height > W9966_WND_MAX_H)
- pix->height = W9966_WND_MAX_H;
- pix->pixelformat = V4L2_PIX_FMT_YUYV;
- pix->field = V4L2_FIELD_NONE;
- pix->bytesperline = 2 * pix->width;
- pix->sizeimage = 2 * pix->width * pix->height;
- /* Just a guess */
- pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
- return 0;
-}
-
-static int cam_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct w9966 *cam = video_drvdata(file);
- struct v4l2_pix_format *pix = &fmt->fmt.pix;
- int ret = cam_try_fmt_vid_cap(file, fh, fmt);
-
- if (ret)
- return ret;
-
- mutex_lock(&cam->lock);
- /* Update camera regs */
- w9966_pdev_claim(cam);
- ret = w9966_setup(cam, 0, 0, 1023, 1023, pix->width, pix->height);
- w9966_pdev_release(cam);
- mutex_unlock(&cam->lock);
- return ret;
-}
-
-static int cam_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
-{
- static struct v4l2_fmtdesc formats[] = {
- { 0, 0, 0,
- "YUV 4:2:2", V4L2_PIX_FMT_YUYV,
- { 0, 0, 0, 0 }
- },
- };
- enum v4l2_buf_type type = fmt->type;
-
- if (fmt->index > 0)
- return -EINVAL;
-
- *fmt = formats[fmt->index];
- fmt->type = type;
- return 0;
-}
-
-/* Capture data */
-static ssize_t w9966_v4l_read(struct file *file, char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct w9966 *cam = video_drvdata(file);
- unsigned char addr = 0xa0; /* ECP, read, CCD-transfer, 00000 */
- unsigned char __user *dest = (unsigned char __user *)buf;
- unsigned long dleft = count;
- unsigned char *tbuf;
-
- /* Why would anyone want more than this?? */
- if (count > cam->width * cam->height * 2)
- return -EINVAL;
-
- mutex_lock(&cam->lock);
- w9966_pdev_claim(cam);
- w9966_write_reg(cam, 0x00, 0x02); /* Reset ECP-FIFO buffer */
- w9966_write_reg(cam, 0x00, 0x00); /* Return to normal operation */
- w9966_write_reg(cam, 0x01, 0x98); /* Enable capture */
-
- /* write special capture-addr and negotiate into data transfer */
- if ((parport_negotiate(cam->pport, cam->ppmode|IEEE1284_ADDR) != 0) ||
- (parport_write(cam->pport, &addr, 1) != 1) ||
- (parport_negotiate(cam->pport, cam->ppmode|IEEE1284_DATA) != 0)) {
- w9966_pdev_release(cam);
- mutex_unlock(&cam->lock);
- return -EFAULT;
- }
-
- tbuf = kmalloc(W9966_RBUFFER, GFP_KERNEL);
- if (tbuf == NULL) {
- count = -ENOMEM;
- goto out;
- }
-
- while (dleft > 0) {
- unsigned long tsize = (dleft > W9966_RBUFFER) ? W9966_RBUFFER : dleft;
-
- if (parport_read(cam->pport, tbuf, tsize) < tsize) {
- count = -EFAULT;
- goto out;
- }
- if (copy_to_user(dest, tbuf, tsize) != 0) {
- count = -EFAULT;
- goto out;
- }
- dest += tsize;
- dleft -= tsize;
- }
-
- w9966_write_reg(cam, 0x01, 0x18); /* Disable capture */
-
-out:
- kfree(tbuf);
- w9966_pdev_release(cam);
- mutex_unlock(&cam->lock);
-
- return count;
-}
-
-static const struct v4l2_file_operations w9966_fops = {
- .owner = THIS_MODULE,
- .open = v4l2_fh_open,
- .release = v4l2_fh_release,
- .poll = v4l2_ctrl_poll,
- .unlocked_ioctl = video_ioctl2,
- .read = w9966_v4l_read,
-};
-
-static const struct v4l2_ioctl_ops w9966_ioctl_ops = {
- .vidioc_querycap = cam_querycap,
- .vidioc_g_input = cam_g_input,
- .vidioc_s_input = cam_s_input,
- .vidioc_enum_input = cam_enum_input,
- .vidioc_enum_fmt_vid_cap = cam_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = cam_g_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = cam_s_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = cam_try_fmt_vid_cap,
- .vidioc_log_status = v4l2_ctrl_log_status,
- .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
- .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
-};
-
-static const struct v4l2_ctrl_ops cam_ctrl_ops = {
- .s_ctrl = cam_s_ctrl,
-};
-
-
-/* Initialize camera device. Setup all internal flags, set a
- default video mode, setup ccd-chip, register v4l device etc..
- Also used for 'probing' of hardware.
- -1 on error */
-static int w9966_init(struct w9966 *cam, struct parport *port)
-{
- struct v4l2_device *v4l2_dev = &cam->v4l2_dev;
-
- if (cam->dev_state != 0)
- return -1;
-
- strlcpy(v4l2_dev->name, "w9966", sizeof(v4l2_dev->name));
-
- if (v4l2_device_register(NULL, v4l2_dev) < 0) {
- v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
- return -1;
- }
-
- v4l2_ctrl_handler_init(&cam->hdl, 4);
- v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops,
- V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
- v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops,
- V4L2_CID_CONTRAST, -64, 64, 1, 64);
- v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops,
- V4L2_CID_SATURATION, -64, 64, 1, 64);
- v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops,
- V4L2_CID_HUE, -128, 127, 1, 0);
- if (cam->hdl.error) {
- v4l2_err(v4l2_dev, "couldn't register controls\n");
- return -1;
- }
- cam->pport = port;
- cam->brightness = 128;
- cam->contrast = 64;
- cam->color = 64;
- cam->hue = 0;
-
- /* Select requested transfer mode */
- switch (parmode) {
- default: /* Auto-detect (priority: hw-ecp, hw-epp, sw-ecp) */
- case 0:
- if (port->modes & PARPORT_MODE_ECP)
- cam->ppmode = IEEE1284_MODE_ECP;
- else if (port->modes & PARPORT_MODE_EPP)
- cam->ppmode = IEEE1284_MODE_EPP;
- else
- cam->ppmode = IEEE1284_MODE_ECP;
- break;
- case 1: /* hw- or sw-ecp */
- cam->ppmode = IEEE1284_MODE_ECP;
- break;
- case 2: /* hw- or sw-epp */
- cam->ppmode = IEEE1284_MODE_EPP;
- break;
- }
-
- /* Tell the parport driver that we exists */
- cam->pdev = parport_register_device(port, "w9966", NULL, NULL, NULL, 0, NULL);
- if (cam->pdev == NULL) {
- DPRINTF("parport_register_device() failed\n");
- return -1;
- }
- w9966_set_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV);
-
- w9966_pdev_claim(cam);
-
- /* Setup a default capture mode */
- if (w9966_setup(cam, 0, 0, 1023, 1023, 200, 160) != 0) {
- DPRINTF("w9966_setup() failed.\n");
- return -1;
- }
-
- w9966_pdev_release(cam);
-
- /* Fill in the video_device struct and register us to v4l */
- strlcpy(cam->vdev.name, W9966_DRIVERNAME, sizeof(cam->vdev.name));
- cam->vdev.v4l2_dev = v4l2_dev;
- cam->vdev.fops = &w9966_fops;
- cam->vdev.ioctl_ops = &w9966_ioctl_ops;
- cam->vdev.release = video_device_release_empty;
- cam->vdev.ctrl_handler = &cam->hdl;
- video_set_drvdata(&cam->vdev, cam);
-
- mutex_init(&cam->lock);
-
- if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0)
- return -1;
-
- w9966_set_state(cam, W9966_STATE_VDEV, W9966_STATE_VDEV);
-
- /* All ok */
- v4l2_info(v4l2_dev, "Found and initialized a webcam on %s.\n",
- cam->pport->name);
- return 0;
-}
-
-
-/* Terminate everything gracefully */
-static void w9966_term(struct w9966 *cam)
-{
- /* Unregister from v4l */
- if (w9966_get_state(cam, W9966_STATE_VDEV, W9966_STATE_VDEV)) {
- video_unregister_device(&cam->vdev);
- w9966_set_state(cam, W9966_STATE_VDEV, 0);
- }
-
- v4l2_ctrl_handler_free(&cam->hdl);
-
- /* Terminate from IEEE1284 mode and release pdev block */
- if (w9966_get_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) {
- w9966_pdev_claim(cam);
- parport_negotiate(cam->pport, IEEE1284_MODE_COMPAT);
- w9966_pdev_release(cam);
- }
-
- /* Unregister from parport */
- if (w9966_get_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) {
- parport_unregister_device(cam->pdev);
- w9966_set_state(cam, W9966_STATE_PDEV, 0);
- }
- memset(cam, 0, sizeof(*cam));
-}
-
-
-/* Called once for every parport on init */
-static void w9966_attach(struct parport *port)
-{
- int i;
-
- for (i = 0; i < W9966_MAXCAMS; i++) {
- if (w9966_cams[i].dev_state != 0) /* Cam is already assigned */
- continue;
- if (strcmp(pardev[i], "aggressive") == 0 || strcmp(pardev[i], port->name) == 0) {
- if (w9966_init(&w9966_cams[i], port) != 0)
- w9966_term(&w9966_cams[i]);
- break; /* return */
- }
- }
-}
-
-/* Called once for every parport on termination */
-static void w9966_detach(struct parport *port)
-{
- int i;
-
- for (i = 0; i < W9966_MAXCAMS; i++)
- if (w9966_cams[i].dev_state != 0 && w9966_cams[i].pport == port)
- w9966_term(&w9966_cams[i]);
-}
-
-
-static struct parport_driver w9966_ppd = {
- .name = W9966_DRIVERNAME,
- .attach = w9966_attach,
- .detach = w9966_detach,
-};
-
-/* Module entry point */
-static int __init w9966_mod_init(void)
-{
- int i;
-
- for (i = 0; i < W9966_MAXCAMS; i++)
- w9966_cams[i].dev_state = 0;
-
- return parport_register_driver(&w9966_ppd);
-}
-
-/* Module cleanup */
-static void __exit w9966_mod_term(void)
-{
- parport_unregister_driver(&w9966_ppd);
-}
-
-module_init(w9966_mod_init);
-module_exit(w9966_mod_term);
diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig
index f8cec8e..218144a 100644
--- a/drivers/media/pci/Kconfig
+++ b/drivers/media/pci/Kconfig
@@ -46,6 +46,7 @@
source "drivers/media/pci/mantis/Kconfig"
source "drivers/media/pci/ngene/Kconfig"
source "drivers/media/pci/ddbridge/Kconfig"
+source "drivers/media/pci/smipcie/Kconfig"
endif
endif #MEDIA_PCI_SUPPORT
diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile
index abdb56a..7f15888 100644
--- a/drivers/media/pci/Makefile
+++ b/drivers/media/pci/Makefile
@@ -11,7 +11,8 @@
mantis/ \
ngene/ \
ddbridge/ \
- saa7146/
+ saa7146/ \
+ smipcie/
obj-$(CPTCFG_VIDEO_IVTV) += ivtv/
obj-$(CPTCFG_VIDEO_ZORAN) += zoran/
diff --git a/drivers/media/pci/bt8xx/bttv-cards.c b/drivers/media/pci/bt8xx/bttv-cards.c
index d8ec583..4105560 100644
--- a/drivers/media/pci/bt8xx/bttv-cards.c
+++ b/drivers/media/pci/bt8xx/bttv-cards.c
@@ -3870,10 +3870,10 @@
} else {
unsigned short type;
- for (i = 4*16; i < 8*16; i += 16) {
- u16 checksum = ip_compute_csum(ee + i, 16);
+ for (i = 4 * 16; i < 8 * 16; i += 16) {
+ u16 checksum = (__force u16)ip_compute_csum(ee + i, 16);
- if ((checksum&0xff) + (checksum>>8) == 0xff)
+ if ((checksum & 0xff) + (checksum >> 8) == 0xff)
break;
}
if (i >= 8*16)
diff --git a/drivers/media/pci/bt8xx/bttv-risc.c b/drivers/media/pci/bt8xx/bttv-risc.c
index 82cc47d..4d3f05a 100644
--- a/drivers/media/pci/bt8xx/bttv-risc.c
+++ b/drivers/media/pci/bt8xx/bttv-risc.c
@@ -84,7 +84,7 @@
continue;
while (offset && offset >= sg_dma_len(sg)) {
offset -= sg_dma_len(sg);
- sg++;
+ sg = sg_next(sg);
}
if (bpl <= sg_dma_len(sg)-offset) {
/* fits into current chunk */
@@ -100,13 +100,13 @@
*(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
todo -= (sg_dma_len(sg)-offset);
offset = 0;
- sg++;
+ sg = sg_next(sg);
while (todo > sg_dma_len(sg)) {
*(rp++)=cpu_to_le32(BT848_RISC_WRITE|
sg_dma_len(sg));
*(rp++)=cpu_to_le32(sg_dma_address(sg));
todo -= sg_dma_len(sg);
- sg++;
+ sg = sg_next(sg);
}
*(rp++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_EOL|
todo);
@@ -187,15 +187,15 @@
/* go to next sg entry if needed */
while (yoffset && yoffset >= sg_dma_len(ysg)) {
yoffset -= sg_dma_len(ysg);
- ysg++;
+ ysg = sg_next(ysg);
}
while (uoffset && uoffset >= sg_dma_len(usg)) {
uoffset -= sg_dma_len(usg);
- usg++;
+ usg = sg_next(usg);
}
while (voffset && voffset >= sg_dma_len(vsg)) {
voffset -= sg_dma_len(vsg);
- vsg++;
+ vsg = sg_next(vsg);
}
/* calculate max number of bytes we can write */
diff --git a/drivers/media/pci/cx18/cx18-av-core.c b/drivers/media/pci/cx18/cx18-av-core.c
index d2402aa..7f2cec2 100644
--- a/drivers/media/pci/cx18/cx18-av-core.c
+++ b/drivers/media/pci/cx18/cx18-av-core.c
@@ -490,8 +490,8 @@
/* Sets horizontal blanking delay and active lines */
cx18_av_write(cx, 0x470, hblank);
- cx18_av_write(cx, 0x471, 0xff & (((hblank >> 8) & 0x3) |
- (hactive << 4)));
+ cx18_av_write(cx, 0x471,
+ (((hblank >> 8) & 0x3) | (hactive << 4)) & 0xff);
cx18_av_write(cx, 0x472, hactive >> 4);
/* Sets burst gate delay */
@@ -499,14 +499,14 @@
/* Sets vertical blanking delay and active duration */
cx18_av_write(cx, 0x474, vblank);
- cx18_av_write(cx, 0x475, 0xff & (((vblank >> 8) & 0x3) |
- (vactive << 4)));
+ cx18_av_write(cx, 0x475,
+ (((vblank >> 8) & 0x3) | (vactive << 4)) & 0xff);
cx18_av_write(cx, 0x476, vactive >> 4);
cx18_av_write(cx, 0x477, vblank656);
/* Sets src decimation rate */
- cx18_av_write(cx, 0x478, 0xff & src_decimation);
- cx18_av_write(cx, 0x479, 0xff & (src_decimation >> 8));
+ cx18_av_write(cx, 0x478, src_decimation & 0xff);
+ cx18_av_write(cx, 0x479, (src_decimation >> 8) & 0xff);
/* Sets Luma and UV Low pass filters */
cx18_av_write(cx, 0x47a, luma_lpf << 6 | ((uv_lpf << 4) & 0x30));
@@ -516,8 +516,8 @@
/* Sets SC Step*/
cx18_av_write(cx, 0x47c, sc);
- cx18_av_write(cx, 0x47d, 0xff & sc >> 8);
- cx18_av_write(cx, 0x47e, 0xff & sc >> 16);
+ cx18_av_write(cx, 0x47d, (sc >> 8) & 0xff);
+ cx18_av_write(cx, 0x47e, (sc >> 16) & 0xff);
if (std & V4L2_STD_625_50) {
state->slicer_line_delay = 1;
@@ -952,7 +952,7 @@
int HSC, VSC, Vsrc, Hsrc, filter, Vlines;
int is_50Hz = !(state->std & V4L2_STD_525_60);
- if (fmt->code != V4L2_MBUS_FMT_FIXED)
+ if (fmt->code != MEDIA_BUS_FMT_FIXED)
return -EINVAL;
fmt->field = V4L2_FIELD_INTERLACED;
diff --git a/drivers/media/pci/cx18/cx18-cards.h b/drivers/media/pci/cx18/cx18-cards.h
index add7391..f6b921f 100644
--- a/drivers/media/pci/cx18/cx18-cards.h
+++ b/drivers/media/pci/cx18/cx18-cards.h
@@ -57,7 +57,8 @@
/* V4L2 capability aliases */
#define CX18_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \
V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | \
- V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE)
+ V4L2_CAP_STREAMING | V4L2_CAP_VBI_CAPTURE | \
+ V4L2_CAP_SLICED_VBI_CAPTURE)
struct cx18_card_video_input {
u8 video_type; /* video input type */
diff --git a/drivers/media/pci/cx18/cx18-controls.c b/drivers/media/pci/cx18/cx18-controls.c
index 282a3d2..4aeb7c6 100644
--- a/drivers/media/pci/cx18/cx18-controls.c
+++ b/drivers/media/pci/cx18/cx18-controls.c
@@ -98,7 +98,7 @@
/* fix videodecoder resolution */
fmt.width = cxhdl->width / (is_mpeg1 ? 2 : 1);
fmt.height = cxhdl->height;
- fmt.code = V4L2_MBUS_FMT_FIXED;
+ fmt.code = MEDIA_BUS_FMT_FIXED;
v4l2_subdev_call(cx->sd_av, video, s_mbus_fmt, &fmt);
return 0;
}
diff --git a/drivers/media/pci/cx18/cx18-driver.h b/drivers/media/pci/cx18/cx18-driver.h
index 57f4688..207d6e8 100644
--- a/drivers/media/pci/cx18/cx18-driver.h
+++ b/drivers/media/pci/cx18/cx18-driver.h
@@ -290,7 +290,7 @@
* list_entry_is_past_end - check if a previous loop cursor is off list end
* @pos: the type * previously used as a loop cursor.
* @head: the head for your list.
- * @member: the name of the list_struct within the struct.
+ * @member: the name of the list_head within the struct.
*
* Check if the entry's list_head is the head of the list, thus it's not a
* real entry but was the loop cursor that walked past the end
@@ -379,6 +379,7 @@
const char *name; /* name of the stream */
int type; /* stream type */
u32 handle; /* task handle */
+ u32 v4l2_dev_caps; /* device capabilities */
unsigned int mdl_base_idx;
u32 id;
diff --git a/drivers/media/pci/cx18/cx18-ioctl.c b/drivers/media/pci/cx18/cx18-ioctl.c
index 0d6616b..27fdad8 100644
--- a/drivers/media/pci/cx18/cx18-ioctl.c
+++ b/drivers/media/pci/cx18/cx18-ioctl.c
@@ -294,7 +294,7 @@
mbus_fmt.width = cx->cxhdl.width = w;
mbus_fmt.height = cx->cxhdl.height = h;
- mbus_fmt.code = V4L2_MBUS_FMT_FIXED;
+ mbus_fmt.code = MEDIA_BUS_FMT_FIXED;
v4l2_subdev_call(cx->sd_av, video, s_mbus_fmt, &mbus_fmt);
return cx18_g_fmt_vid_cap(file, fh, fmt);
}
@@ -393,15 +393,16 @@
struct v4l2_capability *vcap)
{
struct cx18_open_id *id = fh2id(fh);
+ struct cx18_stream *s = video_drvdata(file);
struct cx18 *cx = id->cx;
strlcpy(vcap->driver, CX18_DRIVER_NAME, sizeof(vcap->driver));
strlcpy(vcap->card, cx->card_name, sizeof(vcap->card));
snprintf(vcap->bus_info, sizeof(vcap->bus_info),
"PCI:%s", pci_name(cx->pci_dev));
- vcap->capabilities = cx->v4l2_cap; /* capabilities */
- if (id->type == CX18_ENC_STREAM_TYPE_YUV)
- vcap->capabilities |= V4L2_CAP_STREAMING;
+ vcap->capabilities = cx->v4l2_cap; /* capabilities */
+ vcap->device_caps = s->v4l2_dev_caps; /* device capabilities */
+ vcap->capabilities |= V4L2_CAP_DEVICE_CAPS;
return 0;
}
diff --git a/drivers/media/pci/cx18/cx18-streams.c b/drivers/media/pci/cx18/cx18-streams.c
index f3541b5..369445f 100644
--- a/drivers/media/pci/cx18/cx18-streams.c
+++ b/drivers/media/pci/cx18/cx18-streams.c
@@ -58,11 +58,14 @@
int vfl_type;
int num_offset;
int dma;
+ u32 caps;
} cx18_stream_info[] = {
{ /* CX18_ENC_STREAM_TYPE_MPG */
"encoder MPEG",
VFL_TYPE_GRABBER, 0,
PCI_DMA_FROMDEVICE,
+ V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
+ V4L2_CAP_AUDIO | V4L2_CAP_TUNER
},
{ /* CX18_ENC_STREAM_TYPE_TS */
"TS",
@@ -73,11 +76,15 @@
"encoder YUV",
VFL_TYPE_GRABBER, CX18_V4L2_ENC_YUV_OFFSET,
PCI_DMA_FROMDEVICE,
+ V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING | V4L2_CAP_AUDIO | V4L2_CAP_TUNER
},
{ /* CX18_ENC_STREAM_TYPE_VBI */
"encoder VBI",
VFL_TYPE_VBI, 0,
PCI_DMA_FROMDEVICE,
+ V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE |
+ V4L2_CAP_READWRITE | V4L2_CAP_TUNER
},
{ /* CX18_ENC_STREAM_TYPE_PCM */
"encoder PCM audio",
@@ -93,6 +100,7 @@
"encoder radio",
VFL_TYPE_RADIO, 0,
PCI_DMA_NONE,
+ V4L2_CAP_RADIO | V4L2_CAP_TUNER
},
};
@@ -260,6 +268,7 @@
s->handle = CX18_INVALID_TASK_HANDLE;
s->dma = cx18_stream_info[type].dma;
+ s->v4l2_dev_caps = cx18_stream_info[type].caps;
s->buffers = cx->stream_buffers[type];
s->buf_size = cx->stream_buf_size[type];
INIT_LIST_HEAD(&s->buf_pool);
diff --git a/drivers/media/pci/cx23885/Kconfig b/drivers/media/pci/cx23885/Kconfig
index 911c260..fcbcf43 100644
--- a/drivers/media/pci/cx23885/Kconfig
+++ b/drivers/media/pci/cx23885/Kconfig
@@ -1,5 +1,5 @@
config VIDEO_CX23885
- depends on !BACKPORT_KERNEL_3_4
+ depends on !KERNEL_3_4
tristate "Conexant cx23885 (2388x successor) support"
depends on m
depends on DVB_CORE && VIDEO_DEV && PCI && I2C && INPUT && SND
diff --git a/drivers/media/pci/cx23885/cx23885-417.c b/drivers/media/pci/cx23885/cx23885-417.c
index 4a4f949..5a0f130 100644
--- a/drivers/media/pci/cx23885/cx23885-417.c
+++ b/drivers/media/pci/cx23885/cx23885-417.c
@@ -1148,6 +1148,7 @@
dev->ts1.ts_packet_count = mpeglines;
*num_planes = 1;
sizes[0] = mpeglinesize * mpeglines;
+ alloc_ctxs[0] = dev->alloc_ctx;
*num_buffers = mpegbufs;
return 0;
}
@@ -1166,11 +1167,8 @@
struct cx23885_dev *dev = vb->vb2_queue->drv_priv;
struct cx23885_buffer *buf = container_of(vb,
struct cx23885_buffer, vb);
- struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
cx23885_free_buffer(dev, buf);
-
- dma_unmap_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE);
}
static void buffer_queue(struct vb2_buffer *vb)
diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c
index 88c257d..db99ca2 100644
--- a/drivers/media/pci/cx23885/cx23885-cards.c
+++ b/drivers/media/pci/cx23885/cx23885-cards.c
@@ -680,6 +680,32 @@
.portb = CX23885_MPEG_DVB,
.portc = CX23885_MPEG_DVB,
},
+ [CX23885_BOARD_DVBSKY_T980C] = {
+ .name = "DVBSky T980C",
+ .portb = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_DVBSKY_S950C] = {
+ .name = "DVBSky S950C",
+ .portb = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_TT_CT2_4500_CI] = {
+ .name = "Technotrend TT-budget CT2-4500 CI",
+ .portb = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_DVBSKY_S950] = {
+ .name = "DVBSky S950",
+ .portb = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_DVBSKY_S952] = {
+ .name = "DVBSky S952",
+ .portb = CX23885_MPEG_DVB,
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_DVBSKY_T982] = {
+ .name = "DVBSky T982",
+ .portb = CX23885_MPEG_DVB,
+ .portc = CX23885_MPEG_DVB,
+ },
};
const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
@@ -939,6 +965,30 @@
.subvendor = 0x4254,
.subdevice = 0x9580,
.card = CX23885_BOARD_DVBSKY_T9580,
+ }, {
+ .subvendor = 0x4254,
+ .subdevice = 0x980c,
+ .card = CX23885_BOARD_DVBSKY_T980C,
+ }, {
+ .subvendor = 0x4254,
+ .subdevice = 0x950c,
+ .card = CX23885_BOARD_DVBSKY_S950C,
+ }, {
+ .subvendor = 0x13c2,
+ .subdevice = 0x3013,
+ .card = CX23885_BOARD_TT_CT2_4500_CI,
+ }, {
+ .subvendor = 0x4254,
+ .subdevice = 0x0950,
+ .card = CX23885_BOARD_DVBSKY_S950,
+ }, {
+ .subvendor = 0x4254,
+ .subdevice = 0x0952,
+ .card = CX23885_BOARD_DVBSKY_S952,
+ }, {
+ .subvendor = 0x4254,
+ .subdevice = 0x0982,
+ .card = CX23885_BOARD_DVBSKY_T982,
},
};
const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids);
@@ -1534,6 +1584,8 @@
mdelay(60);
break;
case CX23885_BOARD_DVBSKY_T9580:
+ case CX23885_BOARD_DVBSKY_S952:
+ case CX23885_BOARD_DVBSKY_T982:
/* enable GPIO3-18 pins */
cx_write(MC417_CTL, 0x00000037);
cx23885_gpio_enable(dev, GPIO_2 | GPIO_11, 1);
@@ -1541,6 +1593,45 @@
mdelay(100);
cx23885_gpio_set(dev, GPIO_2 | GPIO_11);
break;
+ case CX23885_BOARD_DVBSKY_T980C:
+ case CX23885_BOARD_DVBSKY_S950C:
+ case CX23885_BOARD_TT_CT2_4500_CI:
+ /*
+ * GPIO-0 INTA from CiMax, input
+ * GPIO-1 reset CiMax, output, high active
+ * GPIO-2 reset demod, output, low active
+ * GPIO-3 to GPIO-10 data/addr for CAM
+ * GPIO-11 ~CS0 to CiMax1
+ * GPIO-12 ~CS1 to CiMax2
+ * GPIO-13 ADL0 load LSB addr
+ * GPIO-14 ADL1 load MSB addr
+ * GPIO-15 ~RDY from CiMax
+ * GPIO-17 ~RD to CiMax
+ * GPIO-18 ~WR to CiMax
+ */
+
+ cx_set(GP0_IO, 0x00060002); /* GPIO 1/2 as output */
+ cx_clear(GP0_IO, 0x00010004); /* GPIO 0 as input */
+ mdelay(100); /* reset delay */
+ cx_set(GP0_IO, 0x00060004); /* GPIO as out, reset high */
+ cx_clear(GP0_IO, 0x00010002);
+ cx_write(MC417_CTL, 0x00000037); /* enable GPIO3-18 pins */
+
+ /* GPIO-15 IN as ~ACK, rest as OUT */
+ cx_write(MC417_OEN, 0x00001000);
+
+ /* ~RD, ~WR high; ADL0, ADL1 low; ~CS0, ~CS1 high */
+ cx_write(MC417_RWD, 0x0000c300);
+
+ /* enable irq */
+ cx_write(GPIO_ISM, 0x00000000); /* INTERRUPTS active low */
+ break;
+ case CX23885_BOARD_DVBSKY_S950:
+ cx23885_gpio_enable(dev, GPIO_2, 1);
+ cx23885_gpio_clear(dev, GPIO_2);
+ msleep(100);
+ cx23885_gpio_set(dev, GPIO_2);
+ break;
}
}
@@ -1621,6 +1712,13 @@
case CX23885_BOARD_MYGICA_X8507:
case CX23885_BOARD_TBS_6980:
case CX23885_BOARD_TBS_6981:
+ case CX23885_BOARD_DVBSKY_T9580:
+ case CX23885_BOARD_DVBSKY_T980C:
+ case CX23885_BOARD_DVBSKY_S950C:
+ case CX23885_BOARD_TT_CT2_4500_CI:
+ case CX23885_BOARD_DVBSKY_S950:
+ case CX23885_BOARD_DVBSKY_S952:
+ case CX23885_BOARD_DVBSKY_T982:
if (!enable_885_ir)
break;
dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE);
@@ -1667,6 +1765,13 @@
case CX23885_BOARD_MYGICA_X8507:
case CX23885_BOARD_TBS_6980:
case CX23885_BOARD_TBS_6981:
+ case CX23885_BOARD_DVBSKY_T9580:
+ case CX23885_BOARD_DVBSKY_T980C:
+ case CX23885_BOARD_DVBSKY_S950C:
+ case CX23885_BOARD_TT_CT2_4500_CI:
+ case CX23885_BOARD_DVBSKY_S950:
+ case CX23885_BOARD_DVBSKY_S952:
+ case CX23885_BOARD_DVBSKY_T982:
cx23885_irq_remove(dev, PCI_MSK_AV_CORE);
/* sd_ir is a duplicate pointer to the AV Core, just clear it */
dev->sd_ir = NULL;
@@ -1714,6 +1819,13 @@
case CX23885_BOARD_MYGICA_X8507:
case CX23885_BOARD_TBS_6980:
case CX23885_BOARD_TBS_6981:
+ case CX23885_BOARD_DVBSKY_T9580:
+ case CX23885_BOARD_DVBSKY_T980C:
+ case CX23885_BOARD_DVBSKY_S950C:
+ case CX23885_BOARD_TT_CT2_4500_CI:
+ case CX23885_BOARD_DVBSKY_S950:
+ case CX23885_BOARD_DVBSKY_S952:
+ case CX23885_BOARD_DVBSKY_T982:
if (dev->sd_ir)
cx23885_irq_add_enable(dev, PCI_MSK_AV_CORE);
break;
@@ -1817,6 +1929,10 @@
case CX23885_BOARD_TEVII_S471:
case CX23885_BOARD_DVBWORLD_2005:
case CX23885_BOARD_PROF_8000:
+ case CX23885_BOARD_DVBSKY_T980C:
+ case CX23885_BOARD_DVBSKY_S950C:
+ case CX23885_BOARD_TT_CT2_4500_CI:
+ case CX23885_BOARD_DVBSKY_S950:
ts1->gen_ctrl_val = 0x5; /* Parallel */
ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
@@ -1865,6 +1981,7 @@
ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
break;
case CX23885_BOARD_DVBSKY_T9580:
+ case CX23885_BOARD_DVBSKY_T982:
ts1->gen_ctrl_val = 0x5; /* Parallel */
ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
@@ -1872,6 +1989,14 @@
ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
break;
+ case CX23885_BOARD_DVBSKY_S952:
+ ts1->gen_ctrl_val = 0x5; /* Parallel */
+ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+ ts2->gen_ctrl_val = 0xe; /* Serial bus */
+ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+ break;
case CX23885_BOARD_HAUPPAUGE_HVR1250:
case CX23885_BOARD_HAUPPAUGE_HVR1500:
case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
@@ -1935,6 +2060,12 @@
case CX23885_BOARD_TBS_6980:
case CX23885_BOARD_TBS_6981:
case CX23885_BOARD_DVBSKY_T9580:
+ case CX23885_BOARD_DVBSKY_T980C:
+ case CX23885_BOARD_DVBSKY_S950C:
+ case CX23885_BOARD_TT_CT2_4500_CI:
+ case CX23885_BOARD_DVBSKY_S950:
+ case CX23885_BOARD_DVBSKY_S952:
+ case CX23885_BOARD_DVBSKY_T982:
dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
&dev->i2c_bus[2].i2c_adap,
"cx25840", 0x88 >> 1, NULL);
diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c
index 331edda..1d9d0f8 100644
--- a/drivers/media/pci/cx23885/cx23885-core.c
+++ b/drivers/media/pci/cx23885/cx23885-core.c
@@ -1078,7 +1078,7 @@
for (line = 0; line < lines; line++) {
while (offset && offset >= sg_dma_len(sg)) {
offset -= sg_dma_len(sg);
- sg++;
+ sg = sg_next(sg);
}
if (lpi && line > 0 && !(line % lpi))
@@ -1101,14 +1101,14 @@
*(rp++) = cpu_to_le32(0); /* bits 63-32 */
todo -= (sg_dma_len(sg)-offset);
offset = 0;
- sg++;
+ sg = sg_next(sg);
while (todo > sg_dma_len(sg)) {
*(rp++) = cpu_to_le32(RISC_WRITE|
sg_dma_len(sg));
*(rp++) = cpu_to_le32(sg_dma_address(sg));
*(rp++) = cpu_to_le32(0); /* bits 63-32 */
todo -= sg_dma_len(sg);
- sg++;
+ sg = sg_next(sg);
}
*(rp++) = cpu_to_le32(RISC_WRITE|RISC_EOL|todo);
*(rp++) = cpu_to_le32(sg_dma_address(sg));
@@ -1453,17 +1453,12 @@
struct cx23885_dev *dev = port->dev;
int size = port->ts_packet_size * port->ts_packet_count;
struct sg_table *sgt = vb2_dma_sg_plane_desc(&buf->vb, 0);
- int rc;
dprintk(1, "%s: %p\n", __func__, buf);
if (vb2_plane_size(&buf->vb, 0) < size)
return -EINVAL;
vb2_set_plane_payload(&buf->vb, 0, size);
- rc = dma_map_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE);
- if (!rc)
- return -EIO;
-
cx23885_risc_databuffer(dev->pci, &buf->risc,
sgt->sgl,
port->ts_packet_size, port->ts_packet_count, 0);
@@ -1997,9 +1992,14 @@
if (!pci_dma_supported(pci_dev, 0xffffffff)) {
printk("%s/0: Oops: no 32bit PCI DMA ???\n", dev->name);
err = -EIO;
- goto fail_irq;
+ goto fail_context;
}
+ dev->alloc_ctx = vb2_dma_sg_init_ctx(&pci_dev->dev);
+ if (IS_ERR(dev->alloc_ctx)) {
+ err = PTR_ERR(dev->alloc_ctx);
+ goto fail_context;
+ }
err = request_irq(pci_dev->irq, cx23885_irq,
IRQF_SHARED, dev->name, dev);
if (err < 0) {
@@ -2028,6 +2028,8 @@
return 0;
fail_irq:
+ vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
+fail_context:
cx23885_dev_unregister(dev);
fail_ctrl:
v4l2_ctrl_handler_free(hdl);
@@ -2053,6 +2055,7 @@
free_irq(pci_dev->irq, dev);
cx23885_dev_unregister(dev);
+ vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
v4l2_ctrl_handler_free(&dev->ctrl_handler);
v4l2_device_unregister(v4l2_dev);
kfree(dev);
diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c
index 13734b8..c47d182 100644
--- a/drivers/media/pci/cx23885/cx23885-dvb.c
+++ b/drivers/media/pci/cx23885/cx23885-dvb.c
@@ -71,6 +71,7 @@
#include "si2165.h"
#include "si2168.h"
#include "si2157.h"
+#include "sp2.h"
#include "m88ds3103.h"
#include "m88ts2022.h"
@@ -101,6 +102,7 @@
port->ts_packet_count = 32;
*num_planes = 1;
sizes[0] = port->ts_packet_size * port->ts_packet_count;
+ alloc_ctxs[0] = port->dev->alloc_ctx;
*num_buffers = 32;
return 0;
}
@@ -121,11 +123,8 @@
struct cx23885_dev *dev = port->dev;
struct cx23885_buffer *buf = container_of(vb,
struct cx23885_buffer, vb);
- struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
cx23885_free_buffer(dev, buf);
-
- dma_unmap_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE);
}
static void buffer_queue(struct vb2_buffer *vb)
@@ -616,6 +615,103 @@
return 0;
}
+static int dvbsky_s952_portc_set_voltage(struct dvb_frontend *fe,
+ fe_sec_voltage_t voltage)
+{
+ struct cx23885_tsport *port = fe->dvb->priv;
+ struct cx23885_dev *dev = port->dev;
+
+ cx23885_gpio_enable(dev, GPIO_12 | GPIO_13, 1);
+
+ switch (voltage) {
+ case SEC_VOLTAGE_13:
+ cx23885_gpio_set(dev, GPIO_13);
+ cx23885_gpio_clear(dev, GPIO_12);
+ break;
+ case SEC_VOLTAGE_18:
+ cx23885_gpio_set(dev, GPIO_13);
+ cx23885_gpio_set(dev, GPIO_12);
+ break;
+ case SEC_VOLTAGE_OFF:
+ cx23885_gpio_clear(dev, GPIO_13);
+ cx23885_gpio_clear(dev, GPIO_12);
+ break;
+ }
+ /* call the frontend set_voltage function */
+ return port->fe_set_voltage(fe, voltage);
+}
+
+static int cx23885_sp2_ci_ctrl(void *priv, u8 read, int addr,
+ u8 data, int *mem)
+{
+ /* MC417 */
+ #define SP2_DATA 0x000000ff
+ #define SP2_WR 0x00008000
+ #define SP2_RD 0x00004000
+ #define SP2_ACK 0x00001000
+ #define SP2_ADHI 0x00000800
+ #define SP2_ADLO 0x00000400
+ #define SP2_CS1 0x00000200
+ #define SP2_CS0 0x00000100
+ #define SP2_EN_ALL 0x00001000
+ #define SP2_CTRL_OFF (SP2_CS1 | SP2_CS0 | SP2_WR | SP2_RD)
+
+ struct cx23885_tsport *port = priv;
+ struct cx23885_dev *dev = port->dev;
+ int ret;
+ int tmp = 0;
+ unsigned long timeout;
+
+ mutex_lock(&dev->gpio_lock);
+
+ /* write addr */
+ cx_write(MC417_OEN, SP2_EN_ALL);
+ cx_write(MC417_RWD, SP2_CTRL_OFF |
+ SP2_ADLO | (0xff & addr));
+ cx_clear(MC417_RWD, SP2_ADLO);
+ cx_write(MC417_RWD, SP2_CTRL_OFF |
+ SP2_ADHI | (0xff & (addr >> 8)));
+ cx_clear(MC417_RWD, SP2_ADHI);
+
+ if (read)
+ /* data in */
+ cx_write(MC417_OEN, SP2_EN_ALL | SP2_DATA);
+ else
+ /* data out */
+ cx_write(MC417_RWD, SP2_CTRL_OFF | data);
+
+ /* chip select 0 */
+ cx_clear(MC417_RWD, SP2_CS0);
+
+ /* read/write */
+ cx_clear(MC417_RWD, (read) ? SP2_RD : SP2_WR);
+
+ /* wait for a maximum of 1 msec */
+ timeout = jiffies + msecs_to_jiffies(1);
+ while (!time_after(jiffies, timeout)) {
+ tmp = cx_read(MC417_RWD);
+ if ((tmp & SP2_ACK) == 0)
+ break;
+ usleep_range(50, 100);
+ }
+
+ cx_set(MC417_RWD, SP2_CTRL_OFF);
+ *mem = tmp & 0xff;
+
+ mutex_unlock(&dev->gpio_lock);
+
+ if (!read) {
+ if (*mem < 0) {
+ ret = -EREMOTEIO;
+ goto err;
+ }
+ }
+
+ return 0;
+err:
+ return ret;
+}
+
static int cx23885_dvb_set_frontend(struct dvb_frontend *fe)
{
struct dtv_frontend_properties *p = &fe->dtv_property_cache;
@@ -793,6 +889,32 @@
.agc = 0x99,
};
+static const struct m88ds3103_config dvbsky_s950c_m88ds3103_config = {
+ .i2c_addr = 0x68,
+ .clock = 27000000,
+ .i2c_wr_max = 33,
+ .clock_out = 0,
+ .ts_mode = M88DS3103_TS_CI,
+ .ts_clk = 10000,
+ .ts_clk_pol = 1,
+ .lnb_en_pol = 1,
+ .lnb_hv_pol = 0,
+ .agc = 0x99,
+};
+
+static const struct m88ds3103_config dvbsky_s952_portc_m88ds3103_config = {
+ .i2c_addr = 0x68,
+ .clock = 27000000,
+ .i2c_wr_max = 33,
+ .clock_out = 0,
+ .ts_mode = M88DS3103_TS_SERIAL,
+ .ts_clk = 96000,
+ .ts_clk_pol = 0,
+ .lnb_en_pol = 1,
+ .lnb_hv_pol = 0,
+ .agc = 0x99,
+};
+
static int netup_altera_fpga_rw(void *device, int flag, int data, int read)
{
struct cx23885_dev *dev = (struct cx23885_dev *)device;
@@ -944,11 +1066,13 @@
struct vb2_dvb_frontend *fe0, *fe1 = NULL;
struct si2168_config si2168_config;
struct si2157_config si2157_config;
+ struct sp2_config sp2_config;
struct m88ts2022_config m88ts2022_config;
struct i2c_board_info info;
struct i2c_adapter *adapter;
- struct i2c_client *client_demod;
- struct i2c_client *client_tuner;
+ struct i2c_client *client_demod = NULL, *client_tuner = NULL, *client_ci = NULL;
+ const struct m88ds3103_config *p_m88ds3103_config = NULL;
+ int (*p_set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage) = NULL;
int mfe_shared = 0; /* bus not shared by default */
int ret;
@@ -973,11 +1097,11 @@
fe0->dvb.frontend = dvb_attach(s5h1409_attach,
&hauppauge_generic_config,
&i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- dvb_attach(mt2131_attach, fe0->dvb.frontend,
- &i2c_bus->i2c_adap,
- &hauppauge_generic_tunerconfig, 0);
- }
+ if (fe0->dvb.frontend == NULL)
+ break;
+ dvb_attach(mt2131_attach, fe0->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &hauppauge_generic_tunerconfig, 0);
break;
case CX23885_BOARD_HAUPPAUGE_HVR1270:
case CX23885_BOARD_HAUPPAUGE_HVR1275:
@@ -985,11 +1109,11 @@
fe0->dvb.frontend = dvb_attach(lgdt3305_attach,
&hauppauge_lgdt3305_config,
&i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- dvb_attach(tda18271_attach, fe0->dvb.frontend,
- 0x60, &dev->i2c_bus[1].i2c_adap,
- &hauppauge_hvr127x_config);
- }
+ if (fe0->dvb.frontend == NULL)
+ break;
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_bus[1].i2c_adap,
+ &hauppauge_hvr127x_config);
if (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1275)
cx23885_set_frontend_hook(port, fe0->dvb.frontend);
break;
@@ -999,11 +1123,12 @@
fe0->dvb.frontend = dvb_attach(s5h1411_attach,
&hcw_s5h1411_config,
&i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- dvb_attach(tda18271_attach, fe0->dvb.frontend,
- 0x60, &dev->i2c_bus[1].i2c_adap,
- &hauppauge_tda18271_config);
- }
+ if (fe0->dvb.frontend == NULL)
+ break;
+
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_bus[1].i2c_adap,
+ &hauppauge_tda18271_config);
tda18271_attach(&dev->ts1.analog_fe,
0x60, &dev->i2c_bus[1].i2c_adap,
@@ -1018,14 +1143,15 @@
dvb_attach(s5h1409_attach,
&hauppauge_ezqam_config,
&i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- dvb_attach(tda829x_attach, fe0->dvb.frontend,
- &dev->i2c_bus[1].i2c_adap, 0x42,
- &tda829x_no_probe);
- dvb_attach(tda18271_attach, fe0->dvb.frontend,
- 0x60, &dev->i2c_bus[1].i2c_adap,
- &hauppauge_tda18271_config);
- }
+ if (fe0->dvb.frontend == NULL)
+ break;
+
+ dvb_attach(tda829x_attach, fe0->dvb.frontend,
+ &dev->i2c_bus[1].i2c_adap, 0x42,
+ &tda829x_no_probe);
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_bus[1].i2c_adap,
+ &hauppauge_tda18271_config);
break;
case 0:
default:
@@ -1033,11 +1159,11 @@
dvb_attach(s5h1409_attach,
&hauppauge_generic_config,
&i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL)
- dvb_attach(mt2131_attach, fe0->dvb.frontend,
- &i2c_bus->i2c_adap,
- &hauppauge_generic_tunerconfig, 0);
- break;
+ if (fe0->dvb.frontend == NULL)
+ break;
+ dvb_attach(mt2131_attach, fe0->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &hauppauge_generic_tunerconfig, 0);
}
break;
case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
@@ -1045,32 +1171,33 @@
fe0->dvb.frontend = dvb_attach(s5h1409_attach,
&hauppauge_hvr1800lp_config,
&i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- dvb_attach(mt2131_attach, fe0->dvb.frontend,
- &i2c_bus->i2c_adap,
- &hauppauge_generic_tunerconfig, 0);
- }
+ if (fe0->dvb.frontend == NULL)
+ break;
+ dvb_attach(mt2131_attach, fe0->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &hauppauge_generic_tunerconfig, 0);
break;
case CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP:
i2c_bus = &dev->i2c_bus[0];
fe0->dvb.frontend = dvb_attach(lgdt330x_attach,
&fusionhdtv_5_express,
&i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
- &i2c_bus->i2c_adap, 0x61,
- TUNER_LG_TDVS_H06XF);
- }
+ if (fe0->dvb.frontend == NULL)
+ break;
+ dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &i2c_bus->i2c_adap, 0x61,
+ TUNER_LG_TDVS_H06XF);
break;
case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
i2c_bus = &dev->i2c_bus[1];
fe0->dvb.frontend = dvb_attach(s5h1409_attach,
&hauppauge_hvr1500q_config,
&dev->i2c_bus[0].i2c_adap);
- if (fe0->dvb.frontend != NULL)
- dvb_attach(xc5000_attach, fe0->dvb.frontend,
- &i2c_bus->i2c_adap,
- &hauppauge_hvr1500q_tunerconfig);
+ if (fe0->dvb.frontend == NULL)
+ break;
+ dvb_attach(xc5000_attach, fe0->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &hauppauge_hvr1500q_tunerconfig);
break;
case CX23885_BOARD_HAUPPAUGE_HVR1500:
i2c_bus = &dev->i2c_bus[1];
@@ -1101,14 +1228,14 @@
fe0->dvb.frontend = dvb_attach(tda10048_attach,
&hauppauge_hvr1200_config,
&i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- dvb_attach(tda829x_attach, fe0->dvb.frontend,
- &dev->i2c_bus[1].i2c_adap, 0x42,
- &tda829x_no_probe);
- dvb_attach(tda18271_attach, fe0->dvb.frontend,
- 0x60, &dev->i2c_bus[1].i2c_adap,
- &hauppauge_hvr1200_tuner_config);
- }
+ if (fe0->dvb.frontend == NULL)
+ break;
+ dvb_attach(tda829x_attach, fe0->dvb.frontend,
+ &dev->i2c_bus[1].i2c_adap, 0x42,
+ &tda829x_no_probe);
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_bus[1].i2c_adap,
+ &hauppauge_hvr1200_tuner_config);
break;
case CX23885_BOARD_HAUPPAUGE_HVR1210:
i2c_bus = &dev->i2c_bus[0];
@@ -1367,12 +1494,10 @@
fe0->dvb.frontend = dvb_attach(lgs8gxx_attach,
&mygica_x8506_lgs8gl5_config,
&i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- dvb_attach(xc5000_attach,
- fe0->dvb.frontend,
- &i2c_bus2->i2c_adap,
- &mygica_x8506_xc5000_config);
- }
+ if (fe0->dvb.frontend == NULL)
+ break;
+ dvb_attach(xc5000_attach, fe0->dvb.frontend,
+ &i2c_bus2->i2c_adap, &mygica_x8506_xc5000_config);
cx23885_set_frontend_hook(port, fe0->dvb.frontend);
break;
case CX23885_BOARD_MYGICA_X8507:
@@ -1381,12 +1506,12 @@
fe0->dvb.frontend = dvb_attach(mb86a20s_attach,
&mygica_x8507_mb86a20s_config,
&i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- dvb_attach(xc5000_attach,
- fe0->dvb.frontend,
- &i2c_bus2->i2c_adap,
- &mygica_x8507_xc5000_config);
- }
+ if (fe0->dvb.frontend == NULL)
+ break;
+
+ dvb_attach(xc5000_attach, fe0->dvb.frontend,
+ &i2c_bus2->i2c_adap,
+ &mygica_x8507_xc5000_config);
cx23885_set_frontend_hook(port, fe0->dvb.frontend);
break;
case CX23885_BOARD_MAGICPRO_PROHDTVE2:
@@ -1395,12 +1520,11 @@
fe0->dvb.frontend = dvb_attach(lgs8gxx_attach,
&magicpro_prohdtve2_lgs8g75_config,
&i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- dvb_attach(xc5000_attach,
- fe0->dvb.frontend,
- &i2c_bus2->i2c_adap,
- &magicpro_prohdtve2_xc5000_config);
- }
+ if (fe0->dvb.frontend == NULL)
+ break;
+ dvb_attach(xc5000_attach, fe0->dvb.frontend,
+ &i2c_bus2->i2c_adap,
+ &magicpro_prohdtve2_xc5000_config);
cx23885_set_frontend_hook(port, fe0->dvb.frontend);
break;
case CX23885_BOARD_HAUPPAUGE_HVR1850:
@@ -1408,10 +1532,11 @@
fe0->dvb.frontend = dvb_attach(s5h1411_attach,
&hcw_s5h1411_config,
&i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL)
- dvb_attach(tda18271_attach, fe0->dvb.frontend,
- 0x60, &dev->i2c_bus[0].i2c_adap,
- &hauppauge_tda18271_config);
+ if (fe0->dvb.frontend == NULL)
+ break;
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_bus[0].i2c_adap,
+ &hauppauge_tda18271_config);
tda18271_attach(&dev->ts1.analog_fe,
0x60, &dev->i2c_bus[1].i2c_adap,
@@ -1423,10 +1548,11 @@
fe0->dvb.frontend = dvb_attach(s5h1411_attach,
&hcw_s5h1411_config,
&i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL)
- dvb_attach(tda18271_attach, fe0->dvb.frontend,
- 0x60, &dev->i2c_bus[0].i2c_adap,
- &hauppauge_tda18271_config);
+ if (fe0->dvb.frontend == NULL)
+ break;
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_bus[0].i2c_adap,
+ &hauppauge_tda18271_config);
break;
case CX23885_BOARD_MYGICA_X8558PRO:
switch (port->nr) {
@@ -1436,12 +1562,11 @@
fe0->dvb.frontend = dvb_attach(atbm8830_attach,
&mygica_x8558pro_atbm8830_cfg1,
&i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- dvb_attach(max2165_attach,
- fe0->dvb.frontend,
- &i2c_bus->i2c_adap,
- &mygic_x8558pro_max2165_cfg1);
- }
+ if (fe0->dvb.frontend == NULL)
+ break;
+ dvb_attach(max2165_attach, fe0->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &mygic_x8558pro_max2165_cfg1);
break;
/* port C */
case 2:
@@ -1449,13 +1574,11 @@
fe0->dvb.frontend = dvb_attach(atbm8830_attach,
&mygica_x8558pro_atbm8830_cfg2,
&i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- dvb_attach(max2165_attach,
- fe0->dvb.frontend,
- &i2c_bus->i2c_adap,
- &mygic_x8558pro_max2165_cfg2);
- }
- break;
+ if (fe0->dvb.frontend == NULL)
+ break;
+ dvb_attach(max2165_attach, fe0->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &mygic_x8558pro_max2165_cfg2);
}
break;
case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
@@ -1467,15 +1590,15 @@
fe0->dvb.frontend = dvb_attach(stv0367ter_attach,
&netup_stv0367_config[port->nr - 1],
&i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- if (NULL == dvb_attach(xc5000_attach,
- fe0->dvb.frontend,
+ if (fe0->dvb.frontend == NULL)
+ break;
+ if (NULL == dvb_attach(xc5000_attach, fe0->dvb.frontend,
&i2c_bus->i2c_adap,
&netup_xc5000_config[port->nr - 1]))
- goto frontend_detach;
- /* load xc5000 firmware */
- fe0->dvb.frontend->ops.tuner_ops.init(fe0->dvb.frontend);
- }
+ goto frontend_detach;
+ /* load xc5000 firmware */
+ fe0->dvb.frontend->ops.tuner_ops.init(fe0->dvb.frontend);
+
/* MFE frontend 2 */
fe1 = vb2_dvb_get_frontend(&port->frontends, 2);
if (fe1 == NULL)
@@ -1484,14 +1607,15 @@
fe1->dvb.frontend = dvb_attach(stv0367cab_attach,
&netup_stv0367_config[port->nr - 1],
&i2c_bus->i2c_adap);
- if (fe1->dvb.frontend != NULL) {
- fe1->dvb.frontend->id = 1;
- if (NULL == dvb_attach(xc5000_attach,
- fe1->dvb.frontend,
- &i2c_bus->i2c_adap,
- &netup_xc5000_config[port->nr - 1]))
- goto frontend_detach;
- }
+ if (fe1->dvb.frontend == NULL)
+ break;
+
+ fe1->dvb.frontend->id = 1;
+ if (NULL == dvb_attach(xc5000_attach,
+ fe1->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &netup_xc5000_config[port->nr - 1]))
+ goto frontend_detach;
break;
case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
i2c_bus = &dev->i2c_bus[0];
@@ -1503,26 +1627,26 @@
fe0->dvb.frontend = dvb_attach(drxk_attach,
&terratec_drxk_config[0],
&i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- if (!dvb_attach(mt2063_attach,
- fe0->dvb.frontend,
- &terratec_mt2063_config[0],
- &i2c_bus2->i2c_adap))
- goto frontend_detach;
- }
+ if (fe0->dvb.frontend == NULL)
+ break;
+ if (!dvb_attach(mt2063_attach,
+ fe0->dvb.frontend,
+ &terratec_mt2063_config[0],
+ &i2c_bus2->i2c_adap))
+ goto frontend_detach;
break;
/* port c */
case 2:
fe0->dvb.frontend = dvb_attach(drxk_attach,
&terratec_drxk_config[1],
&i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- if (!dvb_attach(mt2063_attach,
- fe0->dvb.frontend,
- &terratec_mt2063_config[1],
- &i2c_bus2->i2c_adap))
- goto frontend_detach;
- }
+ if (fe0->dvb.frontend == NULL)
+ break;
+ if (!dvb_attach(mt2063_attach,
+ fe0->dvb.frontend,
+ &terratec_mt2063_config[1],
+ &i2c_bus2->i2c_adap))
+ goto frontend_detach;
break;
}
break;
@@ -1532,10 +1656,10 @@
fe0->dvb.frontend = dvb_attach(ds3000_attach,
&tevii_ds3000_config,
&i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- dvb_attach(ts2020_attach, fe0->dvb.frontend,
- &tevii_ts2020_config, &i2c_bus->i2c_adap);
- }
+ if (fe0->dvb.frontend == NULL)
+ break;
+ dvb_attach(ts2020_attach, fe0->dvb.frontend,
+ &tevii_ts2020_config, &i2c_bus->i2c_adap);
break;
case CX23885_BOARD_PROF_8000:
i2c_bus = &dev->i2c_bus[0];
@@ -1544,15 +1668,15 @@
&prof_8000_stv090x_config,
&i2c_bus->i2c_adap,
STV090x_DEMODULATOR_0);
- if (fe0->dvb.frontend != NULL) {
- if (!dvb_attach(stb6100_attach,
- fe0->dvb.frontend,
- &prof_8000_stb6100_config,
- &i2c_bus->i2c_adap))
- goto frontend_detach;
+ if (fe0->dvb.frontend == NULL)
+ break;
+ if (!dvb_attach(stb6100_attach,
+ fe0->dvb.frontend,
+ &prof_8000_stb6100_config,
+ &i2c_bus->i2c_adap))
+ goto frontend_detach;
- fe0->dvb.frontend->ops.set_voltage = p8000_set_voltage;
- }
+ fe0->dvb.frontend->ops.set_voltage = p8000_set_voltage;
break;
case CX23885_BOARD_HAUPPAUGE_HVR4400:
i2c_bus = &dev->i2c_bus[0];
@@ -1563,30 +1687,31 @@
fe0->dvb.frontend = dvb_attach(tda10071_attach,
&hauppauge_tda10071_config,
&i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- if (!dvb_attach(a8293_attach, fe0->dvb.frontend,
- &i2c_bus->i2c_adap,
- &hauppauge_a8293_config))
- goto frontend_detach;
- }
+ if (fe0->dvb.frontend == NULL)
+ break;
+ if (!dvb_attach(a8293_attach, fe0->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &hauppauge_a8293_config))
+ goto frontend_detach;
break;
/* port c */
case 2:
fe0->dvb.frontend = dvb_attach(si2165_attach,
&hauppauge_hvr4400_si2165_config,
&i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL;
- if (!dvb_attach(tda18271_attach,
- fe0->dvb.frontend,
- 0x60, &i2c_bus2->i2c_adap,
- &hauppauge_hvr4400_tuner_config))
- goto frontend_detach;
- }
+ if (fe0->dvb.frontend == NULL)
+ break;
+ fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL;
+ if (!dvb_attach(tda18271_attach,
+ fe0->dvb.frontend,
+ 0x60, &i2c_bus2->i2c_adap,
+ &hauppauge_hvr4400_tuner_config))
+ goto frontend_detach;
break;
}
break;
case CX23885_BOARD_DVBSKY_T9580:
+ case CX23885_BOARD_DVBSKY_S950:
i2c_bus = &dev->i2c_bus[0];
i2c_bus2 = &dev->i2c_bus[1];
switch (port->nr) {
@@ -1600,6 +1725,7 @@
break;
/* attach tuner */
+ memset(&m88ts2022_config, 0, sizeof(m88ts2022_config));
m88ts2022_config.fe = fe0->dvb.frontend;
m88ts2022_config.clock = 27000000;
memset(&info, 0, sizeof(struct i2c_board_info));
@@ -1635,6 +1761,7 @@
/* port c - terrestrial/cable */
case 2:
/* attach frontend */
+ memset(&si2168_config, 0, sizeof(si2168_config));
si2168_config.i2c_adapter = &adapter;
si2168_config.fe = &fe0->dvb.frontend;
si2168_config.ts_mode = SI2168_TS_SERIAL;
@@ -1654,6 +1781,7 @@
port->i2c_client_demod = client_demod;
/* attach tuner */
+ memset(&si2157_config, 0, sizeof(si2157_config));
si2157_config.fe = fe0->dvb.frontend;
memset(&info, 0, sizeof(struct i2c_board_info));
strlcpy(info.type, "si2157", I2C_NAME_SIZE);
@@ -1677,6 +1805,201 @@
break;
}
break;
+ case CX23885_BOARD_DVBSKY_T980C:
+ case CX23885_BOARD_TT_CT2_4500_CI:
+ i2c_bus = &dev->i2c_bus[1];
+ i2c_bus2 = &dev->i2c_bus[0];
+
+ /* attach frontend */
+ memset(&si2168_config, 0, sizeof(si2168_config));
+ si2168_config.i2c_adapter = &adapter;
+ si2168_config.fe = &fe0->dvb.frontend;
+ si2168_config.ts_mode = SI2168_TS_PARALLEL;
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "si2168", I2C_NAME_SIZE);
+ info.addr = 0x64;
+ info.platform_data = &si2168_config;
+ request_module(info.type);
+ client_demod = i2c_new_device(&i2c_bus->i2c_adap, &info);
+ if (client_demod == NULL ||
+ client_demod->dev.driver == NULL)
+ goto frontend_detach;
+ if (!try_module_get(client_demod->dev.driver->owner)) {
+ i2c_unregister_device(client_demod);
+ goto frontend_detach;
+ }
+ port->i2c_client_demod = client_demod;
+
+ /* attach tuner */
+ memset(&si2157_config, 0, sizeof(si2157_config));
+ si2157_config.fe = fe0->dvb.frontend;
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "si2157", I2C_NAME_SIZE);
+ info.addr = 0x60;
+ info.platform_data = &si2157_config;
+ request_module(info.type);
+ client_tuner = i2c_new_device(adapter, &info);
+ if (client_tuner == NULL ||
+ client_tuner->dev.driver == NULL) {
+ module_put(client_demod->dev.driver->owner);
+ i2c_unregister_device(client_demod);
+ goto frontend_detach;
+ }
+ if (!try_module_get(client_tuner->dev.driver->owner)) {
+ i2c_unregister_device(client_tuner);
+ module_put(client_demod->dev.driver->owner);
+ i2c_unregister_device(client_demod);
+ goto frontend_detach;
+ }
+ port->i2c_client_tuner = client_tuner;
+ break;
+ case CX23885_BOARD_DVBSKY_S950C:
+ i2c_bus = &dev->i2c_bus[1];
+ i2c_bus2 = &dev->i2c_bus[0];
+
+ /* attach frontend */
+ fe0->dvb.frontend = dvb_attach(m88ds3103_attach,
+ &dvbsky_s950c_m88ds3103_config,
+ &i2c_bus->i2c_adap, &adapter);
+ if (fe0->dvb.frontend == NULL)
+ break;
+
+ /* attach tuner */
+ memset(&m88ts2022_config, 0, sizeof(m88ts2022_config));
+ m88ts2022_config.fe = fe0->dvb.frontend;
+ m88ts2022_config.clock = 27000000;
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "m88ts2022", I2C_NAME_SIZE);
+ info.addr = 0x60;
+ info.platform_data = &m88ts2022_config;
+ request_module(info.type);
+ client_tuner = i2c_new_device(adapter, &info);
+ if (client_tuner == NULL ||
+ client_tuner->dev.driver == NULL)
+ goto frontend_detach;
+ if (!try_module_get(client_tuner->dev.driver->owner)) {
+ i2c_unregister_device(client_tuner);
+ goto frontend_detach;
+ }
+
+ /* delegate signal strength measurement to tuner */
+ fe0->dvb.frontend->ops.read_signal_strength =
+ fe0->dvb.frontend->ops.tuner_ops.get_rf_strength;
+
+ port->i2c_client_tuner = client_tuner;
+ break;
+ case CX23885_BOARD_DVBSKY_S952:
+ switch (port->nr) {
+ /* port b */
+ case 1:
+ i2c_bus = &dev->i2c_bus[1];
+ p_m88ds3103_config = &dvbsky_t9580_m88ds3103_config;
+ p_set_voltage = dvbsky_t9580_set_voltage;
+ break;
+ /* port c */
+ case 2:
+ i2c_bus = &dev->i2c_bus[0];
+ p_m88ds3103_config = &dvbsky_s952_portc_m88ds3103_config;
+ p_set_voltage = dvbsky_s952_portc_set_voltage;
+ break;
+ }
+
+ /* attach frontend */
+ fe0->dvb.frontend = dvb_attach(m88ds3103_attach,
+ p_m88ds3103_config,
+ &i2c_bus->i2c_adap, &adapter);
+ if (fe0->dvb.frontend == NULL)
+ break;
+
+ /* attach tuner */
+ memset(&m88ts2022_config, 0, sizeof(m88ts2022_config));
+ m88ts2022_config.fe = fe0->dvb.frontend;
+ m88ts2022_config.clock = 27000000;
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "m88ts2022", I2C_NAME_SIZE);
+ info.addr = 0x60;
+ info.platform_data = &m88ts2022_config;
+ request_module(info.type);
+ client_tuner = i2c_new_device(adapter, &info);
+ if (client_tuner == NULL ||
+ client_tuner->dev.driver == NULL)
+ goto frontend_detach;
+ if (!try_module_get(client_tuner->dev.driver->owner)) {
+ i2c_unregister_device(client_tuner);
+ goto frontend_detach;
+ }
+
+ /* delegate signal strength measurement to tuner */
+ fe0->dvb.frontend->ops.read_signal_strength =
+ fe0->dvb.frontend->ops.tuner_ops.get_rf_strength;
+
+ /*
+ * for setting the voltage we need to set GPIOs on
+ * the card.
+ */
+ port->fe_set_voltage =
+ fe0->dvb.frontend->ops.set_voltage;
+ fe0->dvb.frontend->ops.set_voltage = p_set_voltage;
+
+ port->i2c_client_tuner = client_tuner;
+ break;
+ case CX23885_BOARD_DVBSKY_T982:
+ memset(&si2168_config, 0, sizeof(si2168_config));
+ switch (port->nr) {
+ /* port b */
+ case 1:
+ i2c_bus = &dev->i2c_bus[1];
+ si2168_config.ts_mode = SI2168_TS_PARALLEL;
+ break;
+ /* port c */
+ case 2:
+ i2c_bus = &dev->i2c_bus[0];
+ si2168_config.ts_mode = SI2168_TS_SERIAL;
+ break;
+ }
+
+ /* attach frontend */
+ si2168_config.i2c_adapter = &adapter;
+ si2168_config.fe = &fe0->dvb.frontend;
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "si2168", I2C_NAME_SIZE);
+ info.addr = 0x64;
+ info.platform_data = &si2168_config;
+ request_module(info.type);
+ client_demod = i2c_new_device(&i2c_bus->i2c_adap, &info);
+ if (client_demod == NULL ||
+ client_demod->dev.driver == NULL)
+ goto frontend_detach;
+ if (!try_module_get(client_demod->dev.driver->owner)) {
+ i2c_unregister_device(client_demod);
+ goto frontend_detach;
+ }
+ port->i2c_client_demod = client_demod;
+
+ /* attach tuner */
+ memset(&si2157_config, 0, sizeof(si2157_config));
+ si2157_config.fe = fe0->dvb.frontend;
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "si2157", I2C_NAME_SIZE);
+ info.addr = 0x60;
+ info.platform_data = &si2157_config;
+ request_module(info.type);
+ client_tuner = i2c_new_device(adapter, &info);
+ if (client_tuner == NULL ||
+ client_tuner->dev.driver == NULL) {
+ module_put(client_demod->dev.driver->owner);
+ i2c_unregister_device(client_demod);
+ goto frontend_detach;
+ }
+ if (!try_module_get(client_tuner->dev.driver->owner)) {
+ i2c_unregister_device(client_tuner);
+ module_put(client_demod->dev.driver->owner);
+ i2c_unregister_device(client_demod);
+ port->i2c_client_demod = NULL;
+ goto frontend_detach;
+ }
+ port->i2c_client_tuner = client_tuner;
+ break;
default:
printk(KERN_INFO "%s: The frontend of your DVB/ATSC card "
" isn't supported yet\n",
@@ -1751,7 +2074,10 @@
memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xa0, 6);
break;
}
- case CX23885_BOARD_DVBSKY_T9580: {
+ case CX23885_BOARD_DVBSKY_T9580:
+ case CX23885_BOARD_DVBSKY_S950:
+ case CX23885_BOARD_DVBSKY_S952:
+ case CX23885_BOARD_DVBSKY_T982: {
u8 eeprom[256]; /* 24C02 i2c eeprom */
if (port->nr > 2)
@@ -1761,12 +2087,67 @@
dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1;
tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom,
sizeof(eeprom));
- printk(KERN_INFO "DVBSky T9580 port %d MAC address: %pM\n",
- port->nr, eeprom + 0xc0 + (port->nr-1) * 8);
+ printk(KERN_INFO "%s port %d MAC address: %pM\n",
+ cx23885_boards[dev->board].name, port->nr,
+ eeprom + 0xc0 + (port->nr-1) * 8);
memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xc0 +
(port->nr-1) * 8, 6);
break;
}
+ case CX23885_BOARD_DVBSKY_S950C:
+ case CX23885_BOARD_DVBSKY_T980C:
+ case CX23885_BOARD_TT_CT2_4500_CI: {
+ u8 eeprom[256]; /* 24C02 i2c eeprom */
+
+ /* attach CI */
+ memset(&sp2_config, 0, sizeof(sp2_config));
+ sp2_config.dvb_adap = &port->frontends.adapter;
+ sp2_config.priv = port;
+ sp2_config.ci_control = cx23885_sp2_ci_ctrl;
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "sp2", I2C_NAME_SIZE);
+ info.addr = 0x40;
+ info.platform_data = &sp2_config;
+ request_module(info.type);
+ client_ci = i2c_new_device(&i2c_bus2->i2c_adap, &info);
+ if (client_ci == NULL ||
+ client_ci->dev.driver == NULL) {
+ if (client_tuner) {
+ module_put(client_tuner->dev.driver->owner);
+ i2c_unregister_device(client_tuner);
+ }
+ if (client_demod) {
+ module_put(client_demod->dev.driver->owner);
+ i2c_unregister_device(client_demod);
+ }
+ goto frontend_detach;
+ }
+ if (!try_module_get(client_ci->dev.driver->owner)) {
+ i2c_unregister_device(client_ci);
+ if (client_tuner) {
+ module_put(client_tuner->dev.driver->owner);
+ i2c_unregister_device(client_tuner);
+ }
+ if (client_demod) {
+ module_put(client_demod->dev.driver->owner);
+ i2c_unregister_device(client_demod);
+ }
+ goto frontend_detach;
+ }
+ port->i2c_client_ci = client_ci;
+
+ if (port->nr != 1)
+ break;
+
+ /* Read entire EEPROM */
+ dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1;
+ tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom,
+ sizeof(eeprom));
+ printk(KERN_INFO "%s MAC address: %pM\n",
+ cx23885_boards[dev->board].name, eeprom + 0xc0);
+ memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xc0, 6);
+ break;
+ }
}
return ret;
@@ -1807,7 +2188,7 @@
fe0 = vb2_dvb_get_frontend(&port->frontends, i);
if (!fe0)
- err = -EINVAL;
+ return -EINVAL;
dprintk(1, "%s\n", __func__);
dprintk(1, " ->probed by Card=%d Name=%s, PCI %02x:%02x\n",
@@ -1850,6 +2231,13 @@
struct vb2_dvb_frontend *fe0;
struct i2c_client *client;
+ /* remove I2C client for CI */
+ client = port->i2c_client_ci;
+ if (client) {
+ module_put(client->dev.driver->owner);
+ i2c_unregister_device(client);
+ }
+
/* remove I2C client for tuner */
client = port->i2c_client_tuner;
if (client) {
diff --git a/drivers/media/pci/cx23885/cx23885-input.c b/drivers/media/pci/cx23885/cx23885-input.c
index 9d37fe6..088799c 100644
--- a/drivers/media/pci/cx23885/cx23885-input.c
+++ b/drivers/media/pci/cx23885/cx23885-input.c
@@ -87,6 +87,13 @@
case CX23885_BOARD_MYGICA_X8507:
case CX23885_BOARD_TBS_6980:
case CX23885_BOARD_TBS_6981:
+ case CX23885_BOARD_DVBSKY_T9580:
+ case CX23885_BOARD_DVBSKY_T980C:
+ case CX23885_BOARD_DVBSKY_S950C:
+ case CX23885_BOARD_TT_CT2_4500_CI:
+ case CX23885_BOARD_DVBSKY_S950:
+ case CX23885_BOARD_DVBSKY_S952:
+ case CX23885_BOARD_DVBSKY_T982:
/*
* The only boards we handle right now. However other boards
* using the CX2388x integrated IR controller should be similar
@@ -139,6 +146,13 @@
case CX23885_BOARD_HAUPPAUGE_HVR1290:
case CX23885_BOARD_HAUPPAUGE_HVR1250:
case CX23885_BOARD_MYGICA_X8507:
+ case CX23885_BOARD_DVBSKY_T9580:
+ case CX23885_BOARD_DVBSKY_T980C:
+ case CX23885_BOARD_DVBSKY_S950C:
+ case CX23885_BOARD_TT_CT2_4500_CI:
+ case CX23885_BOARD_DVBSKY_S950:
+ case CX23885_BOARD_DVBSKY_S952:
+ case CX23885_BOARD_DVBSKY_T982:
/*
* The IR controller on this board only returns pulse widths.
* Any other mode setting will fail to set up the device.
@@ -305,6 +319,23 @@
/* A guess at the remote */
rc_map = RC_MAP_TBS_NEC;
break;
+ case CX23885_BOARD_DVBSKY_T9580:
+ case CX23885_BOARD_DVBSKY_T980C:
+ case CX23885_BOARD_DVBSKY_S950C:
+ case CX23885_BOARD_DVBSKY_S950:
+ case CX23885_BOARD_DVBSKY_S952:
+ case CX23885_BOARD_DVBSKY_T982:
+ /* Integrated CX23885 IR controller */
+ driver_type = RC_DRIVER_IR_RAW;
+ allowed_protos = RC_BIT_ALL;
+ rc_map = RC_MAP_DVBSKY;
+ break;
+ case CX23885_BOARD_TT_CT2_4500_CI:
+ /* Integrated CX23885 IR controller */
+ driver_type = RC_DRIVER_IR_RAW;
+ allowed_protos = RC_BIT_ALL;
+ rc_map = RC_MAP_TT_1500;
+ break;
default:
return -ENODEV;
}
diff --git a/drivers/media/pci/cx23885/cx23885-vbi.c b/drivers/media/pci/cx23885/cx23885-vbi.c
index a7c6ef8..d362d38 100644
--- a/drivers/media/pci/cx23885/cx23885-vbi.c
+++ b/drivers/media/pci/cx23885/cx23885-vbi.c
@@ -132,6 +132,7 @@
lines = VBI_NTSC_LINE_COUNT;
*num_planes = 1;
sizes[0] = lines * VBI_LINE_LENGTH * 2;
+ alloc_ctxs[0] = dev->alloc_ctx;
return 0;
}
@@ -142,7 +143,6 @@
struct cx23885_buffer, vb);
struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
unsigned lines = VBI_PAL_LINE_COUNT;
- int ret;
if (dev->tvnorm & V4L2_STD_525_60)
lines = VBI_NTSC_LINE_COUNT;
@@ -151,10 +151,6 @@
return -EINVAL;
vb2_set_plane_payload(vb, 0, lines * VBI_LINE_LENGTH * 2);
- ret = dma_map_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE);
- if (!ret)
- return -EIO;
-
cx23885_risc_vbibuffer(dev->pci, &buf->risc,
sgt->sgl,
0, VBI_LINE_LENGTH * lines,
@@ -165,14 +161,10 @@
static void buffer_finish(struct vb2_buffer *vb)
{
- struct cx23885_dev *dev = vb->vb2_queue->drv_priv;
struct cx23885_buffer *buf = container_of(vb,
struct cx23885_buffer, vb);
- struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
cx23885_free_buffer(vb->vb2_queue->drv_priv, buf);
-
- dma_unmap_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE);
}
/*
diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c
index f10469a..2cd103d 100644
--- a/drivers/media/pci/cx23885/cx23885-video.c
+++ b/drivers/media/pci/cx23885/cx23885-video.c
@@ -323,6 +323,7 @@
*num_planes = 1;
sizes[0] = (dev->fmt->depth * dev->width * dev->height) >> 3;
+ alloc_ctxs[0] = dev->alloc_ctx;
return 0;
}
@@ -334,7 +335,6 @@
u32 line0_offset, line1_offset;
struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
int field_tff;
- int ret;
buf->bpl = (dev->width * dev->fmt->depth) >> 3;
@@ -342,10 +342,6 @@
return -EINVAL;
vb2_set_plane_payload(vb, 0, dev->height * buf->bpl);
- ret = dma_map_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE);
- if (!ret)
- return -EIO;
-
switch (dev->field) {
case V4L2_FIELD_TOP:
cx23885_risc_buffer(dev->pci, &buf->risc,
@@ -413,14 +409,10 @@
static void buffer_finish(struct vb2_buffer *vb)
{
- struct cx23885_dev *dev = vb->vb2_queue->drv_priv;
struct cx23885_buffer *buf = container_of(vb,
struct cx23885_buffer, vb);
- struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
cx23885_free_buffer(vb->vb2_queue->drv_priv, buf);
-
- dma_unmap_sg(&dev->pci->dev, sgt->sgl, sgt->nents, DMA_FROM_DEVICE);
}
/*
@@ -608,7 +600,7 @@
dev->field = f->fmt.pix.field;
dprintk(2, "%s() width=%d height=%d field=%d\n", __func__,
dev->width, dev->height, dev->field);
- v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED);
+ v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, MEDIA_BUS_FMT_FIXED);
call_all(dev, video, s_mbus_fmt, &mbus_fmt);
v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt);
/* s_mbus_fmt overwrites f->fmt.pix.field, restore it */
diff --git a/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h
index 6c35e61..f55cd12 100644
--- a/drivers/media/pci/cx23885/cx23885.h
+++ b/drivers/media/pci/cx23885/cx23885.h
@@ -93,6 +93,12 @@
#define CX23885_BOARD_HAUPPAUGE_IMPACTVCBE 43
#define CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP2 44
#define CX23885_BOARD_DVBSKY_T9580 45
+#define CX23885_BOARD_DVBSKY_T980C 46
+#define CX23885_BOARD_DVBSKY_S950C 47
+#define CX23885_BOARD_TT_CT2_4500_CI 48
+#define CX23885_BOARD_DVBSKY_S950 49
+#define CX23885_BOARD_DVBSKY_S952 50
+#define CX23885_BOARD_DVBSKY_T982 51
#define GPIO_0 0x00000001
#define GPIO_1 0x00000002
@@ -296,6 +302,7 @@
struct i2c_client *i2c_client_demod;
struct i2c_client *i2c_client_tuner;
+ struct i2c_client *i2c_client_ci;
int (*set_frontend)(struct dvb_frontend *fe);
int (*fe_set_voltage)(struct dvb_frontend *fe,
@@ -418,6 +425,7 @@
struct vb2_queue vb2_vidq;
struct cx23885_dmaqueue vbiq;
struct vb2_queue vb2_vbiq;
+ void *alloc_ctx;
spinlock_t slock;
diff --git a/drivers/media/pci/cx25821/cx25821-core.c b/drivers/media/pci/cx25821/cx25821-core.c
index e81173c..389fffd 100644
--- a/drivers/media/pci/cx25821/cx25821-core.c
+++ b/drivers/media/pci/cx25821/cx25821-core.c
@@ -996,7 +996,7 @@
for (line = 0; line < lines; line++) {
while (offset && offset >= sg_dma_len(sg)) {
offset -= sg_dma_len(sg);
- sg++;
+ sg = sg_next(sg);
}
if (bpl <= sg_dma_len(sg) - offset) {
/* fits into current chunk */
@@ -1014,14 +1014,14 @@
*(rp++) = cpu_to_le32(0); /* bits 63-32 */
todo -= (sg_dma_len(sg) - offset);
offset = 0;
- sg++;
+ sg = sg_next(sg);
while (todo > sg_dma_len(sg)) {
*(rp++) = cpu_to_le32(RISC_WRITE |
sg_dma_len(sg));
*(rp++) = cpu_to_le32(sg_dma_address(sg));
*(rp++) = cpu_to_le32(0); /* bits 63-32 */
todo -= sg_dma_len(sg);
- sg++;
+ sg = sg_next(sg);
}
*(rp++) = cpu_to_le32(RISC_WRITE | RISC_EOL | todo);
*(rp++) = cpu_to_le32(sg_dma_address(sg));
@@ -1101,7 +1101,7 @@
for (line = 0; line < lines; line++) {
while (offset && offset >= sg_dma_len(sg)) {
offset -= sg_dma_len(sg);
- sg++;
+ sg = sg_next(sg);
}
if (lpi && line > 0 && !(line % lpi))
@@ -1125,14 +1125,14 @@
*(rp++) = cpu_to_le32(0); /* bits 63-32 */
todo -= (sg_dma_len(sg) - offset);
offset = 0;
- sg++;
+ sg = sg_next(sg);
while (todo > sg_dma_len(sg)) {
*(rp++) = cpu_to_le32(RISC_WRITE |
sg_dma_len(sg));
*(rp++) = cpu_to_le32(sg_dma_address(sg));
*(rp++) = cpu_to_le32(0); /* bits 63-32 */
todo -= sg_dma_len(sg);
- sg++;
+ sg = sg_next(sg);
}
*(rp++) = cpu_to_le32(RISC_WRITE | RISC_EOL | todo);
*(rp++) = cpu_to_le32(sg_dma_address(sg));
diff --git a/drivers/media/pci/cx88/Kconfig b/drivers/media/pci/cx88/Kconfig
index 3d30f90..4a7d489 100644
--- a/drivers/media/pci/cx88/Kconfig
+++ b/drivers/media/pci/cx88/Kconfig
@@ -3,8 +3,7 @@
depends on m
depends on VIDEO_DEV && PCI && I2C && RC_CORE
depends on I2C_ALGOBIT
- select VIDEO_BTCX
- select VIDEOBUF_DMA_SG
+ select VIDEOBUF2_DMA_SG
select VIDEO_TUNER
select VIDEO_TVEEPROM
select VIDEO_WM8775 if MEDIA_SUBDRV_AUTOSELECT
@@ -49,7 +48,7 @@
tristate "DVB/ATSC Support for cx2388x based TV cards"
depends on m
depends on VIDEO_CX88 && DVB_CORE
- select VIDEOBUF_DVB
+ select VIDEOBUF2_DVB
select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT
select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT
diff --git a/drivers/media/pci/cx88/Makefile b/drivers/media/pci/cx88/Makefile
index a1e2796..97b6f50 100644
--- a/drivers/media/pci/cx88/Makefile
+++ b/drivers/media/pci/cx88/Makefile
@@ -11,7 +11,6 @@
obj-$(CPTCFG_VIDEO_CX88_VP3054) += cx88-vp3054-i2c.o
ccflags-y += -I$(backport_srctree)/drivers/media/i2c
-ccflags-y += -I$(backport_srctree)/drivers/media/common
ccflags-y += -I$(backport_srctree)/drivers/media/tuners
ccflags-y += -I$(backport_srctree)/drivers/media/dvb-core
ccflags-y += -I$(backport_srctree)/drivers/media/dvb-frontends
diff --git a/drivers/media/pci/cx88/cx88-alsa.c b/drivers/media/pci/cx88/cx88-alsa.c
index a72579a..7f8dc60 100644
--- a/drivers/media/pci/cx88/cx88-alsa.c
+++ b/drivers/media/pci/cx88/cx88-alsa.c
@@ -61,8 +61,11 @@
struct cx88_audio_buffer {
unsigned int bpl;
- struct btcx_riscmem risc;
- struct videobuf_dmabuf dma;
+ struct cx88_riscmem risc;
+ void *vaddr;
+ struct scatterlist *sglist;
+ int sglen;
+ int nr_pages;
};
struct cx88_audio_dev {
@@ -84,8 +87,6 @@
unsigned int period_size;
unsigned int num_periods;
- struct videobuf_dmabuf *dma_risc;
-
struct cx88_audio_buffer *buf;
struct snd_pcm_substream *substream;
@@ -290,19 +291,97 @@
return IRQ_RETVAL(handled);
}
+static int cx88_alsa_dma_init(struct cx88_audio_dev *chip, int nr_pages)
+{
+ struct cx88_audio_buffer *buf = chip->buf;
+ struct page *pg;
+ int i;
+
+ buf->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT);
+ if (NULL == buf->vaddr) {
+ dprintk(1, "vmalloc_32(%d pages) failed\n", nr_pages);
+ return -ENOMEM;
+ }
+
+ dprintk(1, "vmalloc is at addr 0x%08lx, size=%d\n",
+ (unsigned long)buf->vaddr,
+ nr_pages << PAGE_SHIFT);
+
+ memset(buf->vaddr, 0, nr_pages << PAGE_SHIFT);
+ buf->nr_pages = nr_pages;
+
+ buf->sglist = vzalloc(buf->nr_pages * sizeof(*buf->sglist));
+ if (NULL == buf->sglist)
+ goto vzalloc_err;
+
+ sg_init_table(buf->sglist, buf->nr_pages);
+ for (i = 0; i < buf->nr_pages; i++) {
+ pg = vmalloc_to_page(buf->vaddr + i * PAGE_SIZE);
+ if (NULL == pg)
+ goto vmalloc_to_page_err;
+ sg_set_page(&buf->sglist[i], pg, PAGE_SIZE, 0);
+ }
+ return 0;
+
+vmalloc_to_page_err:
+ vfree(buf->sglist);
+ buf->sglist = NULL;
+vzalloc_err:
+ vfree(buf->vaddr);
+ buf->vaddr = NULL;
+ return -ENOMEM;
+}
+
+static int cx88_alsa_dma_map(struct cx88_audio_dev *dev)
+{
+ struct cx88_audio_buffer *buf = dev->buf;
+
+ buf->sglen = dma_map_sg(&dev->pci->dev, buf->sglist,
+ buf->nr_pages, PCI_DMA_FROMDEVICE);
+
+ if (0 == buf->sglen) {
+ pr_warn("%s: cx88_alsa_map_sg failed\n", __func__);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static int cx88_alsa_dma_unmap(struct cx88_audio_dev *dev)
+{
+ struct cx88_audio_buffer *buf = dev->buf;
+
+ if (!buf->sglen)
+ return 0;
+
+ dma_unmap_sg(&dev->pci->dev, buf->sglist, buf->sglen, PCI_DMA_FROMDEVICE);
+ buf->sglen = 0;
+ return 0;
+}
+
+static int cx88_alsa_dma_free(struct cx88_audio_buffer *buf)
+{
+ vfree(buf->sglist);
+ buf->sglist = NULL;
+ vfree(buf->vaddr);
+ buf->vaddr = NULL;
+ return 0;
+}
+
static int dsp_buffer_free(snd_cx88_card_t *chip)
{
+ struct cx88_riscmem *risc = &chip->buf->risc;
+
BUG_ON(!chip->dma_size);
dprintk(2,"Freeing buffer\n");
- videobuf_dma_unmap(&chip->pci->dev, chip->dma_risc);
- videobuf_dma_free(chip->dma_risc);
- btcx_riscmem_free(chip->pci,&chip->buf->risc);
+ cx88_alsa_dma_unmap(chip);
+ cx88_alsa_dma_free(chip->buf);
+ if (risc->cpu)
+ pci_free_consistent(chip->pci, risc->size, risc->cpu, risc->dma);
kfree(chip->buf);
- chip->dma_risc = NULL;
- chip->dma_size = 0;
+ chip->buf = NULL;
return 0;
}
@@ -387,7 +466,6 @@
struct snd_pcm_hw_params * hw_params)
{
snd_cx88_card_t *chip = snd_pcm_substream_chip(substream);
- struct videobuf_dmabuf *dma;
struct cx88_audio_buffer *buf;
int ret;
@@ -408,20 +486,19 @@
if (NULL == buf)
return -ENOMEM;
+ chip->buf = buf;
buf->bpl = chip->period_size;
- dma = &buf->dma;
- videobuf_dma_init(dma);
- ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE,
+ ret = cx88_alsa_dma_init(chip,
(PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT));
if (ret < 0)
goto error;
- ret = videobuf_dma_map(&chip->pci->dev, dma);
+ ret = cx88_alsa_dma_map(chip);
if (ret < 0)
goto error;
- ret = cx88_risc_databuffer(chip->pci, &buf->risc, dma->sglist,
+ ret = cx88_risc_databuffer(chip->pci, &buf->risc, buf->sglist,
chip->period_size, chip->num_periods, 1);
if (ret < 0)
goto error;
@@ -430,10 +507,7 @@
buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP|RISC_IRQ1|RISC_CNT_INC);
buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
- chip->buf = buf;
- chip->dma_risc = dma;
-
- substream->runtime->dma_area = chip->dma_risc->vaddr;
+ substream->runtime->dma_area = chip->buf->vaddr;
substream->runtime->dma_bytes = chip->dma_size;
substream->runtime->dma_addr = 0;
return 0;
diff --git a/drivers/media/pci/cx88/cx88-blackbird.c b/drivers/media/pci/cx88/cx88-blackbird.c
index 150bb76..d3c79d9 100644
--- a/drivers/media/pci/cx88/cx88-blackbird.c
+++ b/drivers/media/pci/cx88/cx88-blackbird.c
@@ -45,10 +45,6 @@
MODULE_LICENSE("GPL");
MODULE_VERSION(CX88_VERSION);
-static unsigned int mpegbufs = 32;
-module_param(mpegbufs,int,0644);
-MODULE_PARM_DESC(mpegbufs,"number of mpeg buffers, range 2-32");
-
static unsigned int debug;
module_param(debug,int,0644);
MODULE_PARM_DESC(debug,"enable debug messages [blackbird]");
@@ -326,13 +322,13 @@
memory_read(dev->core, dev->mailbox - 4, &value);
if (value != 0x12345678) {
dprintk(0, "Firmware and/or mailbox pointer not initialized or corrupted\n");
- return -1;
+ return -EIO;
}
memory_read(dev->core, dev->mailbox, &flag);
if (flag) {
dprintk(0, "ERROR: Mailbox appears to be in use (%x)\n", flag);
- return -1;
+ return -EIO;
}
flag |= 1; /* tell 'em we're working on it */
@@ -352,14 +348,14 @@
memory_write(dev->core, dev->mailbox, flag);
/* wait for firmware to handle the API command */
- timeout = jiffies + msecs_to_jiffies(10);
+ timeout = jiffies + msecs_to_jiffies(1000);
for (;;) {
memory_read(dev->core, dev->mailbox, &flag);
if (0 != (flag & 4))
break;
if (time_after(jiffies,timeout)) {
- dprintk(0, "ERROR: API Mailbox timeout\n");
- return -1;
+ dprintk(0, "ERROR: API Mailbox timeout %x\n", command);
+ return -EIO;
}
udelay(10);
}
@@ -420,7 +416,7 @@
}
}
dprintk(0, "Mailbox signature values not found!\n");
- return -1;
+ return -EIO;
}
static int blackbird_load_firmware(struct cx8802_dev *dev)
@@ -432,7 +428,7 @@
int i, retval = 0;
u32 value = 0;
u32 checksum = 0;
- u32 *dataptr;
+ __le32 *dataptr;
retval = register_write(dev->core, IVTV_REG_VPU, 0xFFFFFFED);
retval |= register_write(dev->core, IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST);
@@ -449,29 +445,28 @@
if (retval != 0) {
- dprintk(0, "ERROR: Hotplug firmware request failed (%s).\n",
+ pr_err("Hotplug firmware request failed (%s).\n",
CX2341X_FIRM_ENC_FILENAME);
- dprintk(0, "Please fix your hotplug setup, the board will "
- "not work without firmware loaded!\n");
- return -1;
+ pr_err("Please fix your hotplug setup, the board will not work without firmware loaded!\n");
+ return -EIO;
}
if (firmware->size != BLACKBIRD_FIRM_IMAGE_SIZE) {
- dprintk(0, "ERROR: Firmware size mismatch (have %zd, expected %d)\n",
+ pr_err("Firmware size mismatch (have %zd, expected %d)\n",
firmware->size, BLACKBIRD_FIRM_IMAGE_SIZE);
release_firmware(firmware);
- return -1;
+ return -EINVAL;
}
if (0 != memcmp(firmware->data, magic, 8)) {
- dprintk(0, "ERROR: Firmware magic mismatch, wrong file?\n");
+ pr_err("Firmware magic mismatch, wrong file?\n");
release_firmware(firmware);
- return -1;
+ return -EINVAL;
}
/* transfer to the chip */
dprintk(1,"Loading firmware ...\n");
- dataptr = (u32*)firmware->data;
+ dataptr = (__le32 *)firmware->data;
for (i = 0; i < (firmware->size >> 2); i++) {
value = le32_to_cpu(*dataptr);
checksum += ~value;
@@ -484,12 +479,11 @@
memory_read(dev->core, i, &value);
checksum -= ~value;
}
- if (checksum) {
- dprintk(0, "ERROR: Firmware load failed (checksum mismatch).\n");
- release_firmware(firmware);
- return -1;
- }
release_firmware(firmware);
+ if (checksum) {
+ pr_err("Firmware load might have failed (checksum mismatch).\n");
+ return -EIO;
+ }
dprintk(0, "Firmware upload successful.\n");
retval |= register_write(dev->core, IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST);
@@ -521,12 +515,14 @@
static void blackbird_codec_settings(struct cx8802_dev *dev)
{
+ struct cx88_core *core = dev->core;
+
/* assign frame size */
blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0,
- dev->height, dev->width);
+ core->height, core->width);
- dev->cxhdl.width = dev->width;
- dev->cxhdl.height = dev->height;
+ dev->cxhdl.width = core->width;
+ dev->cxhdl.height = core->height;
cx2341x_handler_set_50hz(&dev->cxhdl, dev->core->tvnorm & V4L2_STD_625_50);
cx2341x_handler_setup(&dev->cxhdl);
}
@@ -540,9 +536,6 @@
dprintk(1,"Initialize codec\n");
retval = blackbird_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */
if (retval < 0) {
-
- dev->mpeg_active = 0;
-
/* ping was not successful, reset and upload firmware */
cx_write(MO_SRST_IO, 0); /* SYS_RSTO=0 */
cx_write(MO_SRST_IO, 1); /* SYS_RSTO=1 */
@@ -589,9 +582,8 @@
return 0;
}
-static int blackbird_start_codec(struct file *file, void *priv)
+static int blackbird_start_codec(struct cx8802_dev *dev)
{
- struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev;
struct cx88_core *core = dev->core;
/* start capturing to the host interface */
u32 reg;
@@ -627,7 +619,6 @@
BLACKBIRD_RAW_BITS_NONE
);
- dev->mpeg_active = 1;
return 0;
}
@@ -641,51 +632,137 @@
cx2341x_handler_set_busy(&dev->cxhdl, 0);
- dev->mpeg_active = 0;
return 0;
}
/* ------------------------------------------------------------------ */
-static int bb_buf_setup(struct videobuf_queue *q,
- unsigned int *count, unsigned int *size)
+static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], void *alloc_ctxs[])
{
- struct cx8802_fh *fh = q->priv_data;
+ struct cx8802_dev *dev = q->drv_priv;
- fh->dev->ts_packet_size = 188 * 4; /* was: 512 */
- fh->dev->ts_packet_count = mpegbufs; /* was: 100 */
-
- *size = fh->dev->ts_packet_size * fh->dev->ts_packet_count;
- *count = fh->dev->ts_packet_count;
+ *num_planes = 1;
+ dev->ts_packet_size = 188 * 4;
+ dev->ts_packet_count = 32;
+ sizes[0] = dev->ts_packet_size * dev->ts_packet_count;
+ alloc_ctxs[0] = dev->alloc_ctx;
return 0;
}
-static int
-bb_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
- enum v4l2_field field)
+static int buffer_prepare(struct vb2_buffer *vb)
{
- struct cx8802_fh *fh = q->priv_data;
- return cx8802_buf_prepare(q, fh->dev, (struct cx88_buffer*)vb, field);
+ struct cx8802_dev *dev = vb->vb2_queue->drv_priv;
+ struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb);
+
+ return cx8802_buf_prepare(vb->vb2_queue, dev, buf);
}
-static void
-bb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+static void buffer_finish(struct vb2_buffer *vb)
{
- struct cx8802_fh *fh = q->priv_data;
- cx8802_buf_queue(fh->dev, (struct cx88_buffer*)vb);
+ struct cx8802_dev *dev = vb->vb2_queue->drv_priv;
+ struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb);
+ struct cx88_riscmem *risc = &buf->risc;
+
+ if (risc->cpu)
+ pci_free_consistent(dev->pci, risc->size, risc->cpu, risc->dma);
+ memset(risc, 0, sizeof(*risc));
}
-static void
-bb_buf_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+static void buffer_queue(struct vb2_buffer *vb)
{
- cx88_free_buffer(q, (struct cx88_buffer*)vb);
+ struct cx8802_dev *dev = vb->vb2_queue->drv_priv;
+ struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb);
+
+ cx8802_buf_queue(dev, buf);
}
-static struct videobuf_queue_ops blackbird_qops = {
- .buf_setup = bb_buf_setup,
- .buf_prepare = bb_buf_prepare,
- .buf_queue = bb_buf_queue,
- .buf_release = bb_buf_release,
+static int start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct cx8802_dev *dev = q->drv_priv;
+ struct cx88_dmaqueue *dmaq = &dev->mpegq;
+ struct cx8802_driver *drv;
+ struct cx88_buffer *buf;
+ unsigned long flags;
+ int err;
+
+ /* Make sure we can acquire the hardware */
+ drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD);
+ if (!drv) {
+ dprintk(1, "%s: blackbird driver is not loaded\n", __func__);
+ err = -ENODEV;
+ goto fail;
+ }
+
+ err = drv->request_acquire(drv);
+ if (err != 0) {
+ dprintk(1, "%s: Unable to acquire hardware, %d\n", __func__, err);
+ goto fail;
+ }
+
+ if (blackbird_initialize_codec(dev) < 0) {
+ drv->request_release(drv);
+ err = -EINVAL;
+ goto fail;
+ }
+
+ err = blackbird_start_codec(dev);
+ if (err == 0) {
+ buf = list_entry(dmaq->active.next, struct cx88_buffer, list);
+ cx8802_start_dma(dev, dmaq, buf);
+ return 0;
+ }
+
+fail:
+ spin_lock_irqsave(&dev->slock, flags);
+ while (!list_empty(&dmaq->active)) {
+ struct cx88_buffer *buf = list_entry(dmaq->active.next,
+ struct cx88_buffer, list);
+
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED);
+ }
+ spin_unlock_irqrestore(&dev->slock, flags);
+ return err;
+}
+
+static void stop_streaming(struct vb2_queue *q)
+{
+ struct cx8802_dev *dev = q->drv_priv;
+ struct cx88_dmaqueue *dmaq = &dev->mpegq;
+ struct cx8802_driver *drv = NULL;
+ unsigned long flags;
+
+ cx8802_cancel_buffers(dev);
+ blackbird_stop_codec(dev);
+
+ /* Make sure we release the hardware */
+ drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD);
+ WARN_ON(!drv);
+ if (drv)
+ drv->request_release(drv);
+
+ spin_lock_irqsave(&dev->slock, flags);
+ while (!list_empty(&dmaq->active)) {
+ struct cx88_buffer *buf = list_entry(dmaq->active.next,
+ struct cx88_buffer, list);
+
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+static struct vb2_ops blackbird_qops = {
+ .queue_setup = queue_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_finish = buffer_finish,
+ .buf_queue = buffer_queue,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .start_streaming = start_streaming,
+ .stop_streaming = stop_streaming,
};
/* ------------------------------------------------------------------ */
@@ -693,8 +770,8 @@
static int vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
- struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev;
- struct cx88_core *core = dev->core;
+ struct cx8802_dev *dev = video_drvdata(file);
+ struct cx88_core *core = dev->core;
strcpy(cap->driver, "cx88_blackbird");
sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
@@ -714,131 +791,111 @@
return 0;
}
-static int vidioc_g_fmt_vid_cap (struct file *file, void *priv,
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct cx8802_fh *fh = priv;
- struct cx8802_dev *dev = fh->dev;
+ struct cx8802_dev *dev = video_drvdata(file);
+ struct cx88_core *core = dev->core;
f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
f->fmt.pix.bytesperline = 0;
- f->fmt.pix.sizeimage = 188 * 4 * mpegbufs; /* 188 * 4 * 1024; */
+ f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count;
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
- f->fmt.pix.width = dev->width;
- f->fmt.pix.height = dev->height;
- f->fmt.pix.field = fh->mpegq.field;
- dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d, f: %d\n",
- dev->width, dev->height, fh->mpegq.field );
+ f->fmt.pix.width = core->width;
+ f->fmt.pix.height = core->height;
+ f->fmt.pix.field = core->field;
return 0;
}
-static int vidioc_try_fmt_vid_cap (struct file *file, void *priv,
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct cx8802_fh *fh = priv;
- struct cx8802_dev *dev = fh->dev;
+ struct cx8802_dev *dev = video_drvdata(file);
+ struct cx88_core *core = dev->core;
+ unsigned maxw, maxh;
+ enum v4l2_field field;
f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
f->fmt.pix.bytesperline = 0;
- f->fmt.pix.sizeimage = 188 * 4 * mpegbufs; /* 188 * 4 * 1024; */
+ f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count;
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
- dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n",
- dev->width, dev->height, fh->mpegq.field );
+
+ maxw = norm_maxw(core->tvnorm);
+ maxh = norm_maxh(core->tvnorm);
+
+ field = f->fmt.pix.field;
+
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ case V4L2_FIELD_INTERLACED:
+ case V4L2_FIELD_SEQ_BT:
+ case V4L2_FIELD_SEQ_TB:
+ break;
+ default:
+ field = (f->fmt.pix.height > maxh / 2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_BOTTOM;
+ break;
+ }
+ if (V4L2_FIELD_HAS_T_OR_B(field))
+ maxh /= 2;
+
+ v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2,
+ &f->fmt.pix.height, 32, maxh, 0, 0);
+ f->fmt.pix.field = field;
return 0;
}
-static int vidioc_s_fmt_vid_cap (struct file *file, void *priv,
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct cx8802_fh *fh = priv;
- struct cx8802_dev *dev = fh->dev;
+ struct cx8802_dev *dev = video_drvdata(file);
struct cx88_core *core = dev->core;
- f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
- f->fmt.pix.bytesperline = 0;
- f->fmt.pix.sizeimage = 188 * 4 * mpegbufs; /* 188 * 4 * 1024; */
- f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
- dev->width = f->fmt.pix.width;
- dev->height = f->fmt.pix.height;
- fh->mpegq.field = f->fmt.pix.field;
+ if (vb2_is_busy(&dev->vb2_mpegq))
+ return -EBUSY;
+ if (core->v4ldev && (vb2_is_busy(&core->v4ldev->vb2_vidq) ||
+ vb2_is_busy(&core->v4ldev->vb2_vbiq)))
+ return -EBUSY;
+ vidioc_try_fmt_vid_cap(file, priv, f);
+ core->width = f->fmt.pix.width;
+ core->height = f->fmt.pix.height;
+ core->field = f->fmt.pix.field;
cx88_set_scale(core, f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field);
blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0,
f->fmt.pix.height, f->fmt.pix.width);
- dprintk(1, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n",
- f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field );
return 0;
}
-static int vidioc_reqbufs (struct file *file, void *priv, struct v4l2_requestbuffers *p)
-{
- struct cx8802_fh *fh = priv;
- return (videobuf_reqbufs(&fh->mpegq, p));
-}
-
-static int vidioc_querybuf (struct file *file, void *priv, struct v4l2_buffer *p)
-{
- struct cx8802_fh *fh = priv;
- return (videobuf_querybuf(&fh->mpegq, p));
-}
-
-static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *p)
-{
- struct cx8802_fh *fh = priv;
- return (videobuf_qbuf(&fh->mpegq, p));
-}
-
-static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *p)
-{
- struct cx8802_fh *fh = priv;
- return (videobuf_dqbuf(&fh->mpegq, p,
- file->f_flags & O_NONBLOCK));
-}
-
-static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- struct cx8802_fh *fh = priv;
- struct cx8802_dev *dev = fh->dev;
-
- if (!dev->mpeg_active)
- blackbird_start_codec(file, fh);
- return videobuf_streamon(&fh->mpegq);
-}
-
-static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- struct cx8802_fh *fh = priv;
- struct cx8802_dev *dev = fh->dev;
-
- if (dev->mpeg_active)
- blackbird_stop_codec(dev);
- return videobuf_streamoff(&fh->mpegq);
-}
-
static int vidioc_s_frequency (struct file *file, void *priv,
const struct v4l2_frequency *f)
{
- struct cx8802_fh *fh = priv;
- struct cx8802_dev *dev = fh->dev;
- struct cx88_core *core = dev->core;
+ struct cx8802_dev *dev = video_drvdata(file);
+ struct cx88_core *core = dev->core;
+ bool streaming;
if (unlikely(UNSET == core->board.tuner_type))
return -EINVAL;
if (unlikely(f->tuner != 0))
return -EINVAL;
- if (dev->mpeg_active)
+ streaming = vb2_start_streaming_called(&dev->vb2_mpegq);
+ if (streaming)
blackbird_stop_codec(dev);
cx88_set_freq (core,f);
blackbird_initialize_codec(dev);
- cx88_set_scale(dev->core, dev->width, dev->height,
- fh->mpegq.field);
+ cx88_set_scale(core, core->width, core->height,
+ core->field);
+ if (streaming)
+ blackbird_start_codec(dev);
return 0;
}
static int vidioc_log_status (struct file *file, void *priv)
{
- struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev;
- struct cx88_core *core = dev->core;
+ struct cx8802_dev *dev = video_drvdata(file);
+ struct cx88_core *core = dev->core;
char name[32 + 2];
snprintf(name, sizeof(name), "%s/2", core->name);
@@ -850,15 +907,16 @@
static int vidioc_enum_input (struct file *file, void *priv,
struct v4l2_input *i)
{
- struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
+ struct cx8802_dev *dev = video_drvdata(file);
+ struct cx88_core *core = dev->core;
return cx88_enum_input (core,i);
}
static int vidioc_g_frequency (struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct cx8802_fh *fh = priv;
- struct cx88_core *core = fh->dev->core;
+ struct cx8802_dev *dev = video_drvdata(file);
+ struct cx88_core *core = dev->core;
if (unlikely(UNSET == core->board.tuner_type))
return -EINVAL;
@@ -873,7 +931,8 @@
static int vidioc_g_input (struct file *file, void *priv, unsigned int *i)
{
- struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
+ struct cx8802_dev *dev = video_drvdata(file);
+ struct cx88_core *core = dev->core;
*i = core->input;
return 0;
@@ -881,24 +940,24 @@
static int vidioc_s_input (struct file *file, void *priv, unsigned int i)
{
- struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
+ struct cx8802_dev *dev = video_drvdata(file);
+ struct cx88_core *core = dev->core;
if (i >= 4)
return -EINVAL;
if (0 == INPUT(i).type)
return -EINVAL;
- mutex_lock(&core->lock);
cx88_newstation(core);
cx88_video_mux(core,i);
- mutex_unlock(&core->lock);
return 0;
}
static int vidioc_g_tuner (struct file *file, void *priv,
struct v4l2_tuner *t)
{
- struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
+ struct cx8802_dev *dev = video_drvdata(file);
+ struct cx88_core *core = dev->core;
u32 reg;
if (unlikely(UNSET == core->board.tuner_type))
@@ -920,7 +979,8 @@
static int vidioc_s_tuner (struct file *file, void *priv,
const struct v4l2_tuner *t)
{
- struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
+ struct cx8802_dev *dev = video_drvdata(file);
+ struct cx88_core *core = dev->core;
if (UNSET == core->board.tuner_type)
return -EINVAL;
@@ -933,7 +993,8 @@
static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorm)
{
- struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
+ struct cx8802_dev *dev = video_drvdata(file);
+ struct cx88_core *core = dev->core;
*tvnorm = core->tvnorm;
return 0;
@@ -941,155 +1002,20 @@
static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id)
{
- struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
-
- mutex_lock(&core->lock);
- cx88_set_tvnorm(core, id);
- mutex_unlock(&core->lock);
- return 0;
-}
-
-/* FIXME: cx88_ioctl_hook not implemented */
-
-static int mpeg_open(struct file *file)
-{
- struct video_device *vdev = video_devdata(file);
struct cx8802_dev *dev = video_drvdata(file);
- struct cx8802_fh *fh;
- struct cx8802_driver *drv = NULL;
- int err;
+ struct cx88_core *core = dev->core;
- dprintk( 1, "%s\n", __func__);
-
- mutex_lock(&dev->core->lock);
-
- /* Make sure we can acquire the hardware */
- drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD);
- if (!drv) {
- dprintk(1, "%s: blackbird driver is not loaded\n", __func__);
- mutex_unlock(&dev->core->lock);
- return -ENODEV;
- }
-
- err = drv->request_acquire(drv);
- if (err != 0) {
- dprintk(1,"%s: Unable to acquire hardware, %d\n", __func__, err);
- mutex_unlock(&dev->core->lock);
- return err;
- }
-
- if (!dev->core->mpeg_users && blackbird_initialize_codec(dev) < 0) {
- drv->request_release(drv);
- mutex_unlock(&dev->core->lock);
- return -EINVAL;
- }
- dprintk(1, "open dev=%s\n", video_device_node_name(vdev));
-
- /* allocate + initialize per filehandle data */
- fh = kzalloc(sizeof(*fh),GFP_KERNEL);
- if (NULL == fh) {
- drv->request_release(drv);
- mutex_unlock(&dev->core->lock);
- return -ENOMEM;
- }
- v4l2_fh_init(&fh->fh, vdev);
- file->private_data = fh;
- fh->dev = dev;
-
- videobuf_queue_sg_init(&fh->mpegq, &blackbird_qops,
- &dev->pci->dev, &dev->slock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_INTERLACED,
- sizeof(struct cx88_buffer),
- fh, NULL);
-
- /* FIXME: locking against other video device */
- cx88_set_scale(dev->core, dev->width, dev->height,
- fh->mpegq.field);
-
- dev->core->mpeg_users++;
- mutex_unlock(&dev->core->lock);
- v4l2_fh_add(&fh->fh);
- return 0;
-}
-
-static int mpeg_release(struct file *file)
-{
- struct cx8802_fh *fh = file->private_data;
- struct cx8802_dev *dev = fh->dev;
- struct cx8802_driver *drv = NULL;
-
- mutex_lock(&dev->core->lock);
-
- if (dev->mpeg_active && dev->core->mpeg_users == 1)
- blackbird_stop_codec(dev);
-
- cx8802_cancel_buffers(fh->dev);
- /* stop mpeg capture */
- videobuf_stop(&fh->mpegq);
-
- videobuf_mmap_free(&fh->mpegq);
-
- v4l2_fh_del(&fh->fh);
- v4l2_fh_exit(&fh->fh);
- file->private_data = NULL;
- kfree(fh);
-
- /* Make sure we release the hardware */
- drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD);
- WARN_ON(!drv);
- if (drv)
- drv->request_release(drv);
-
- dev->core->mpeg_users--;
-
- mutex_unlock(&dev->core->lock);
-
- return 0;
-}
-
-static ssize_t
-mpeg_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
-{
- struct cx8802_fh *fh = file->private_data;
- struct cx8802_dev *dev = fh->dev;
-
- if (!dev->mpeg_active)
- blackbird_start_codec(file, fh);
-
- return videobuf_read_stream(&fh->mpegq, data, count, ppos, 0,
- file->f_flags & O_NONBLOCK);
-}
-
-static unsigned int
-mpeg_poll(struct file *file, struct poll_table_struct *wait)
-{
- unsigned long req_events = poll_requested_events(wait);
- struct cx8802_fh *fh = file->private_data;
- struct cx8802_dev *dev = fh->dev;
-
- if (!dev->mpeg_active && (req_events & (POLLIN | POLLRDNORM)))
- blackbird_start_codec(file, fh);
-
- return v4l2_ctrl_poll(file, wait) | videobuf_poll_stream(file, &fh->mpegq, wait);
-}
-
-static int
-mpeg_mmap(struct file *file, struct vm_area_struct * vma)
-{
- struct cx8802_fh *fh = file->private_data;
-
- return videobuf_mmap_mapper(&fh->mpegq, vma);
+ return cx88_set_tvnorm(core, id);
}
static const struct v4l2_file_operations mpeg_fops =
{
.owner = THIS_MODULE,
- .open = mpeg_open,
- .release = mpeg_release,
- .read = mpeg_read,
- .poll = mpeg_poll,
- .mmap = mpeg_mmap,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .read = vb2_fop_read,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
.unlocked_ioctl = video_ioctl2,
};
@@ -1099,12 +1025,12 @@
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
.vidioc_s_frequency = vidioc_s_frequency,
.vidioc_log_status = vidioc_log_status,
.vidioc_enum_input = vidioc_enum_input,
@@ -1189,11 +1115,12 @@
{
int err;
- dev->mpeg_dev = cx88_vdev_init(dev->core,dev->pci,
- &cx8802_mpeg_template,"mpeg");
+ dev->mpeg_dev = cx88_vdev_init(dev->core, dev->pci,
+ &cx8802_mpeg_template, "mpeg");
dev->mpeg_dev->ctrl_handler = &dev->cxhdl.hdl;
video_set_drvdata(dev->mpeg_dev, dev);
- err = video_register_device(dev->mpeg_dev,VFL_TYPE_GRABBER, -1);
+ dev->mpeg_dev->queue = &dev->vb2_mpegq;
+ err = video_register_device(dev->mpeg_dev, VFL_TYPE_GRABBER, -1);
if (err < 0) {
printk(KERN_INFO "%s/2: can't register mpeg device\n",
dev->core->name);
@@ -1210,6 +1137,7 @@
{
struct cx88_core *core = drv->core;
struct cx8802_dev *dev = core->dvbdev;
+ struct vb2_queue *q;
int err;
dprintk( 1, "%s\n", __func__);
@@ -1223,15 +1151,9 @@
if (!(core->board.mpeg & CX88_MPEG_BLACKBIRD))
goto fail_core;
- dev->width = 720;
- if (core->tvnorm & V4L2_STD_525_60) {
- dev->height = 480;
- } else {
- dev->height = 576;
- }
dev->cxhdl.port = CX2341X_PORT_STREAMING;
- dev->cxhdl.width = dev->width;
- dev->cxhdl.height = dev->height;
+ dev->cxhdl.width = core->width;
+ dev->cxhdl.height = core->height;
dev->cxhdl.func = blackbird_mbox_func;
dev->cxhdl.priv = dev;
err = cx2341x_handler_init(&dev->cxhdl, 36);
@@ -1250,13 +1172,30 @@
// init_controls(core);
cx88_set_tvnorm(core,core->tvnorm);
cx88_video_mux(core,0);
- cx2341x_handler_set_50hz(&dev->cxhdl, dev->height == 576);
+ cx2341x_handler_set_50hz(&dev->cxhdl, core->height == 576);
cx2341x_handler_setup(&dev->cxhdl);
+
+ q = &dev->vb2_mpegq;
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
+ q->gfp_flags = GFP_DMA32;
+ q->min_buffers_needed = 2;
+ q->drv_priv = dev;
+ q->buf_struct_size = sizeof(struct cx88_buffer);
+ q->ops = &blackbird_qops;
+ q->mem_ops = &vb2_dma_sg_memops;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &core->lock;
+
+ err = vb2_queue_init(q);
+ if (err < 0)
+ goto fail_core;
+
blackbird_register_video(dev);
return 0;
- fail_core:
+fail_core:
return err;
}
diff --git a/drivers/media/pci/cx88/cx88-cards.c b/drivers/media/pci/cx88/cx88-cards.c
index 851754b..8f2556e 100644
--- a/drivers/media/pci/cx88/cx88-cards.c
+++ b/drivers/media/pci/cx88/cx88-cards.c
@@ -347,7 +347,7 @@
},
[CX88_BOARD_IODATA_GVVCP3PCI] = {
.name = "IODATA GV-VCP3/PCI",
- .tuner_type = TUNER_ABSENT,
+ .tuner_type = UNSET,
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
@@ -436,7 +436,7 @@
},
[CX88_BOARD_KWORLD_DVB_T] = {
.name = "KWorld/VStream XPert DVB-T",
- .tuner_type = TUNER_ABSENT,
+ .tuner_type = UNSET,
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
@@ -455,7 +455,7 @@
},
[CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1] = {
.name = "DViCO FusionHDTV DVB-T1",
- .tuner_type = TUNER_ABSENT, /* No analog tuner */
+ .tuner_type = UNSET, /* No analog tuner */
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
@@ -542,7 +542,7 @@
},
[CX88_BOARD_HAUPPAUGE_DVB_T1] = {
.name = "Hauppauge Nova-T DVB-T",
- .tuner_type = TUNER_ABSENT,
+ .tuner_type = UNSET,
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
@@ -554,7 +554,7 @@
},
[CX88_BOARD_CONEXANT_DVB_T1] = {
.name = "Conexant DVB-T reference design",
- .tuner_type = TUNER_ABSENT,
+ .tuner_type = UNSET,
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
@@ -579,7 +579,7 @@
},
[CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS] = {
.name = "DViCO FusionHDTV DVB-T Plus",
- .tuner_type = TUNER_ABSENT, /* No analog tuner */
+ .tuner_type = UNSET, /* No analog tuner */
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
@@ -596,7 +596,7 @@
},
[CX88_BOARD_DNTV_LIVE_DVB_T] = {
.name = "digitalnow DNTV Live! DVB-T",
- .tuner_type = TUNER_ABSENT,
+ .tuner_type = UNSET,
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
@@ -787,7 +787,7 @@
},
[CX88_BOARD_ADSTECH_DVB_T_PCI] = {
.name = "ADS Tech Instant TV DVB-T PCI",
- .tuner_type = TUNER_ABSENT,
+ .tuner_type = UNSET,
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
@@ -806,7 +806,7 @@
},
[CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1] = {
.name = "TerraTec Cinergy 1400 DVB-T",
- .tuner_type = TUNER_ABSENT,
+ .tuner_type = UNSET,
.input = { {
.type = CX88_VMUX_DVB,
.vmux = 0,
@@ -924,7 +924,7 @@
},
[CX88_BOARD_WINFAST_DTV1000] = {
.name = "WinFast DTV1000-T",
- .tuner_type = TUNER_ABSENT,
+ .tuner_type = UNSET,
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
@@ -972,7 +972,7 @@
},
[CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1] = {
.name = "Hauppauge Nova-S-Plus DVB-S",
- .tuner_type = TUNER_ABSENT,
+ .tuner_type = UNSET,
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
@@ -998,7 +998,7 @@
},
[CX88_BOARD_HAUPPAUGE_NOVASE2_S1] = {
.name = "Hauppauge Nova-SE2 DVB-S",
- .tuner_type = TUNER_ABSENT,
+ .tuner_type = UNSET,
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
@@ -1010,7 +1010,7 @@
},
[CX88_BOARD_KWORLD_DVBS_100] = {
.name = "KWorld DVB-S 100",
- .tuner_type = TUNER_ABSENT,
+ .tuner_type = UNSET,
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
@@ -1102,7 +1102,7 @@
/* DTT 7579 Conexant CX22702-19 Conexant CX2388x */
/* Manenti Marco <marco_manenti@colman.it> */
.name = "KWorld/VStream XPert DVB-T with cx22702",
- .tuner_type = TUNER_ABSENT,
+ .tuner_type = UNSET,
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
@@ -1121,7 +1121,7 @@
},
[CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL] = {
.name = "DViCO FusionHDTV DVB-T Dual Digital",
- .tuner_type = TUNER_ABSENT, /* No analog tuner */
+ .tuner_type = UNSET, /* No analog tuner */
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
@@ -1356,7 +1356,7 @@
},
[CX88_BOARD_GENIATECH_DVBS] = {
.name = "Geniatech DVB-S",
- .tuner_type = TUNER_ABSENT,
+ .tuner_type = UNSET,
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
@@ -1494,7 +1494,7 @@
},
[CX88_BOARD_SAMSUNG_SMT_7020] = {
.name = "Samsung SMT 7020 DVB-S",
- .tuner_type = TUNER_ABSENT,
+ .tuner_type = UNSET,
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
@@ -1506,7 +1506,7 @@
},
[CX88_BOARD_ADSTECH_PTV_390] = {
.name = "ADS Tech Instant Video PCI",
- .tuner_type = TUNER_ABSENT,
+ .tuner_type = UNSET,
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
@@ -1553,7 +1553,7 @@
[CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO] = {
.name = "DViCO FusionHDTV 5 PCI nano",
/* xc3008 tuner, digital only for now */
- .tuner_type = TUNER_ABSENT,
+ .tuner_type = UNSET,
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
@@ -2069,7 +2069,7 @@
},
[CX88_BOARD_TBS_8920] = {
.name = "TBS 8920 DVB-S/S2",
- .tuner_type = TUNER_ABSENT,
+ .tuner_type = UNSET,
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
@@ -2304,7 +2304,7 @@
},
[CX88_BOARD_TWINHAN_VP1027_DVBS] = {
.name = "Twinhan VP-1027 DVB-S",
- .tuner_type = TUNER_ABSENT,
+ .tuner_type = UNSET,
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
@@ -2921,33 +2921,33 @@
int fm;
const char *name;
} gdi_tuner[] = {
- [ 0x01 ] = { .id = TUNER_ABSENT,
+ [ 0x01 ] = { .id = UNSET,
.name = "NTSC_M" },
- [ 0x02 ] = { .id = TUNER_ABSENT,
+ [ 0x02 ] = { .id = UNSET,
.name = "PAL_B" },
- [ 0x03 ] = { .id = TUNER_ABSENT,
+ [ 0x03 ] = { .id = UNSET,
.name = "PAL_I" },
- [ 0x04 ] = { .id = TUNER_ABSENT,
+ [ 0x04 ] = { .id = UNSET,
.name = "PAL_D" },
- [ 0x05 ] = { .id = TUNER_ABSENT,
+ [ 0x05 ] = { .id = UNSET,
.name = "SECAM" },
- [ 0x10 ] = { .id = TUNER_ABSENT,
+ [ 0x10 ] = { .id = UNSET,
.fm = 1,
.name = "TEMIC_4049" },
[ 0x11 ] = { .id = TUNER_TEMIC_4136FY5,
.name = "TEMIC_4136" },
- [ 0x12 ] = { .id = TUNER_ABSENT,
+ [ 0x12 ] = { .id = UNSET,
.name = "TEMIC_4146" },
[ 0x20 ] = { .id = TUNER_PHILIPS_FQ1216ME,
.fm = 1,
.name = "PHILIPS_FQ1216_MK3" },
- [ 0x21 ] = { .id = TUNER_ABSENT, .fm = 1,
+ [ 0x21 ] = { .id = UNSET, .fm = 1,
.name = "PHILIPS_FQ1236_MK3" },
- [ 0x22 ] = { .id = TUNER_ABSENT,
+ [ 0x22 ] = { .id = UNSET,
.name = "PHILIPS_FI1236_MK3" },
- [ 0x23 ] = { .id = TUNER_ABSENT,
+ [ 0x23 ] = { .id = UNSET,
.name = "PHILIPS_FI1216_MK3" },
};
@@ -3564,7 +3564,7 @@
mode_mask &= ~T_RADIO;
}
- if (core->board.tuner_type != TUNER_ABSENT) {
+ if (core->board.tuner_type != UNSET) {
tun_setup.mode_mask = mode_mask;
tun_setup.type = core->board.tuner_type;
tun_setup.addr = core->board.tuner_addr;
@@ -3691,6 +3691,11 @@
core->nr = nr;
sprintf(core->name, "cx88[%d]", core->nr);
+ core->tvnorm = V4L2_STD_NTSC_M;
+ core->width = 320;
+ core->height = 240;
+ core->field = V4L2_FIELD_INTERLACED;
+
strcpy(core->v4l2_dev.name, core->name);
if (v4l2_device_register(NULL, &core->v4l2_dev)) {
kfree(core);
@@ -3772,7 +3777,7 @@
cx88_i2c_init(core, pci);
/* load tuner module, if needed */
- if (TUNER_ABSENT != core->board.tuner_type) {
+ if (UNSET != core->board.tuner_type) {
/* Ignore 0x6b and 0x6f on cx88 boards.
* FusionHDTV5 RT Gold has an ir receiver at 0x6b
* and an RTC at 0x6f which can get corrupted if probed. */
diff --git a/drivers/media/pci/cx88/cx88-core.c b/drivers/media/pci/cx88/cx88-core.c
index 7163023..dee177e 100644
--- a/drivers/media/pci/cx88/cx88-core.c
+++ b/drivers/media/pci/cx88/cx88-core.c
@@ -76,11 +76,16 @@
static __le32* cx88_risc_field(__le32 *rp, struct scatterlist *sglist,
unsigned int offset, u32 sync_line,
unsigned int bpl, unsigned int padding,
- unsigned int lines, unsigned int lpi)
+ unsigned int lines, unsigned int lpi, bool jump)
{
struct scatterlist *sg;
unsigned int line,todo,sol;
+ if (jump) {
+ (*rp++) = cpu_to_le32(RISC_JUMP);
+ (*rp++) = 0;
+ }
+
/* sync instruction */
if (sync_line != NO_SYNC_LINE)
*(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
@@ -90,7 +95,7 @@
for (line = 0; line < lines; line++) {
while (offset && offset >= sg_dma_len(sg)) {
offset -= sg_dma_len(sg);
- sg++;
+ sg = sg_next(sg);
}
if (lpi && line>0 && !(line % lpi))
sol = RISC_SOL | RISC_IRQ1 | RISC_CNT_INC;
@@ -109,13 +114,13 @@
*(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
todo -= (sg_dma_len(sg)-offset);
offset = 0;
- sg++;
+ sg = sg_next(sg);
while (todo > sg_dma_len(sg)) {
*(rp++)=cpu_to_le32(RISC_WRITE|
sg_dma_len(sg));
*(rp++)=cpu_to_le32(sg_dma_address(sg));
todo -= sg_dma_len(sg);
- sg++;
+ sg = sg_next(sg);
}
*(rp++)=cpu_to_le32(RISC_WRITE|RISC_EOL|todo);
*(rp++)=cpu_to_le32(sg_dma_address(sg));
@@ -127,14 +132,13 @@
return rp;
}
-int cx88_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+int cx88_risc_buffer(struct pci_dev *pci, struct cx88_riscmem *risc,
struct scatterlist *sglist,
unsigned int top_offset, unsigned int bottom_offset,
unsigned int bpl, unsigned int padding, unsigned int lines)
{
u32 instructions,fields;
__le32 *rp;
- int rc;
fields = 0;
if (UNSET != top_offset)
@@ -147,18 +151,21 @@
can cause next bpl to start close to a page border. First DMA
region may be smaller than PAGE_SIZE */
instructions = fields * (1 + ((bpl + padding) * lines) / PAGE_SIZE + lines);
- instructions += 2;
- if ((rc = btcx_riscmem_alloc(pci,risc,instructions*8)) < 0)
- return rc;
+ instructions += 4;
+ risc->size = instructions * 8;
+ risc->dma = 0;
+ risc->cpu = pci_zalloc_consistent(pci, risc->size, &risc->dma);
+ if (NULL == risc->cpu)
+ return -ENOMEM;
/* write risc instructions */
rp = risc->cpu;
if (UNSET != top_offset)
rp = cx88_risc_field(rp, sglist, top_offset, 0,
- bpl, padding, lines, 0);
+ bpl, padding, lines, 0, true);
if (UNSET != bottom_offset)
rp = cx88_risc_field(rp, sglist, bottom_offset, 0x200,
- bpl, padding, lines, 0);
+ bpl, padding, lines, 0, top_offset == UNSET);
/* save pointer to jmp instruction address */
risc->jmp = rp;
@@ -166,26 +173,28 @@
return 0;
}
-int cx88_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+int cx88_risc_databuffer(struct pci_dev *pci, struct cx88_riscmem *risc,
struct scatterlist *sglist, unsigned int bpl,
unsigned int lines, unsigned int lpi)
{
u32 instructions;
__le32 *rp;
- int rc;
/* estimate risc mem: worst case is one write per page border +
one write per scan line + syncs + jump (all 2 dwords). Here
there is no padding and no sync. First DMA region may be smaller
than PAGE_SIZE */
instructions = 1 + (bpl * lines) / PAGE_SIZE + lines;
- instructions += 1;
- if ((rc = btcx_riscmem_alloc(pci,risc,instructions*8)) < 0)
- return rc;
+ instructions += 3;
+ risc->size = instructions * 8;
+ risc->dma = 0;
+ risc->cpu = pci_zalloc_consistent(pci, risc->size, &risc->dma);
+ if (NULL == risc->cpu)
+ return -ENOMEM;
/* write risc instructions */
rp = risc->cpu;
- rp = cx88_risc_field(rp, sglist, 0, NO_SYNC_LINE, bpl, 0, lines, lpi);
+ rp = cx88_risc_field(rp, sglist, 0, NO_SYNC_LINE, bpl, 0, lines, lpi, !lpi);
/* save pointer to jmp instruction address */
risc->jmp = rp;
@@ -193,39 +202,6 @@
return 0;
}
-int cx88_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
- u32 reg, u32 mask, u32 value)
-{
- __le32 *rp;
- int rc;
-
- if ((rc = btcx_riscmem_alloc(pci, risc, 4*16)) < 0)
- return rc;
-
- /* write risc instructions */
- rp = risc->cpu;
- *(rp++) = cpu_to_le32(RISC_WRITECR | RISC_IRQ2 | RISC_IMM);
- *(rp++) = cpu_to_le32(reg);
- *(rp++) = cpu_to_le32(value);
- *(rp++) = cpu_to_le32(mask);
- *(rp++) = cpu_to_le32(RISC_JUMP);
- *(rp++) = cpu_to_le32(risc->dma);
- return 0;
-}
-
-void
-cx88_free_buffer(struct videobuf_queue *q, struct cx88_buffer *buf)
-{
- struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
-
- BUG_ON(in_interrupt());
- videobuf_waiton(q, &buf->vb, 0, 0);
- videobuf_dma_unmap(q->dev, dma);
- videobuf_dma_free(dma);
- btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc);
- buf->vb.state = VIDEOBUF_NEEDS_INIT;
-}
-
/* ------------------------------------------------------------------ */
/* our SRAM memory layout */
@@ -539,33 +515,12 @@
struct cx88_dmaqueue *q, u32 count)
{
struct cx88_buffer *buf;
- int bc;
- for (bc = 0;; bc++) {
- if (list_empty(&q->active))
- break;
- buf = list_entry(q->active.next,
- struct cx88_buffer, vb.queue);
- /* count comes from the hw and is is 16bit wide --
- * this trick handles wrap-arounds correctly for
- * up to 32767 buffers in flight... */
- if ((s16) (count - buf->count) < 0)
- break;
- v4l2_get_timestamp(&buf->vb.ts);
- dprintk(2,"[%p/%d] wakeup reg=%d buf=%d\n",buf,buf->vb.i,
- count, buf->count);
- buf->vb.state = VIDEOBUF_DONE;
- list_del(&buf->vb.queue);
- wake_up(&buf->vb.done);
- }
- if (list_empty(&q->active)) {
- del_timer(&q->timeout);
- } else {
- mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
- }
- if (bc != 1)
- dprintk(2, "%s: %d buffers handled (should be 1)\n",
- __func__, bc);
+ buf = list_entry(q->active.next,
+ struct cx88_buffer, list);
+ v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
}
void cx88_shutdown(struct cx88_core *core)
@@ -909,6 +864,13 @@
u32 bdelay,agcdelay,htotal;
u32 cxiformat, cxoformat;
+ if (norm == core->tvnorm)
+ return 0;
+ if (core->v4ldev && (vb2_is_busy(&core->v4ldev->vb2_vidq) ||
+ vb2_is_busy(&core->v4ldev->vb2_vbiq)))
+ return -EBUSY;
+ if (core->dvbdev && vb2_is_busy(&core->dvbdev->vb2_mpegq))
+ return -EBUSY;
core->tvnorm = norm;
fsc8 = norm_fsc8(norm);
adc_clock = xtal;
@@ -1043,6 +1005,7 @@
vfd->v4l2_dev = &core->v4l2_dev;
vfd->dev_parent = &pci->dev;
vfd->release = video_device_release;
+ vfd->lock = &core->lock;
snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)",
core->name, type, core->board.name);
return vfd;
@@ -1114,8 +1077,6 @@
EXPORT_SYMBOL(cx88_risc_buffer);
EXPORT_SYMBOL(cx88_risc_databuffer);
-EXPORT_SYMBOL(cx88_risc_stopper);
-EXPORT_SYMBOL(cx88_free_buffer);
EXPORT_SYMBOL(cx88_sram_channels);
EXPORT_SYMBOL(cx88_sram_channel_setup);
diff --git a/drivers/media/pci/cx88/cx88-dvb.c b/drivers/media/pci/cx88/cx88-dvb.c
index 4f3c001..8b19efa 100644
--- a/drivers/media/pci/cx88/cx88-dvb.c
+++ b/drivers/media/pci/cx88/cx88-dvb.c
@@ -82,43 +82,87 @@
/* ------------------------------------------------------------------ */
-static int dvb_buf_setup(struct videobuf_queue *q,
- unsigned int *count, unsigned int *size)
+static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], void *alloc_ctxs[])
{
- struct cx8802_dev *dev = q->priv_data;
+ struct cx8802_dev *dev = q->drv_priv;
+ *num_planes = 1;
dev->ts_packet_size = 188 * 4;
dev->ts_packet_count = dvb_buf_tscnt;
-
- *size = dev->ts_packet_size * dev->ts_packet_count;
- *count = dvb_buf_tscnt;
+ sizes[0] = dev->ts_packet_size * dev->ts_packet_count;
+ alloc_ctxs[0] = dev->alloc_ctx;
+ *num_buffers = dvb_buf_tscnt;
return 0;
}
-static int dvb_buf_prepare(struct videobuf_queue *q,
- struct videobuf_buffer *vb, enum v4l2_field field)
+static int buffer_prepare(struct vb2_buffer *vb)
{
- struct cx8802_dev *dev = q->priv_data;
- return cx8802_buf_prepare(q, dev, (struct cx88_buffer*)vb,field);
+ struct cx8802_dev *dev = vb->vb2_queue->drv_priv;
+ struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb);
+
+ return cx8802_buf_prepare(vb->vb2_queue, dev, buf);
}
-static void dvb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+static void buffer_finish(struct vb2_buffer *vb)
{
- struct cx8802_dev *dev = q->priv_data;
- cx8802_buf_queue(dev, (struct cx88_buffer*)vb);
+ struct cx8802_dev *dev = vb->vb2_queue->drv_priv;
+ struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb);
+ struct cx88_riscmem *risc = &buf->risc;
+
+ if (risc->cpu)
+ pci_free_consistent(dev->pci, risc->size, risc->cpu, risc->dma);
+ memset(risc, 0, sizeof(*risc));
}
-static void dvb_buf_release(struct videobuf_queue *q,
- struct videobuf_buffer *vb)
+static void buffer_queue(struct vb2_buffer *vb)
{
- cx88_free_buffer(q, (struct cx88_buffer*)vb);
+ struct cx8802_dev *dev = vb->vb2_queue->drv_priv;
+ struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb);
+
+ cx8802_buf_queue(dev, buf);
}
-static const struct videobuf_queue_ops dvb_qops = {
- .buf_setup = dvb_buf_setup,
- .buf_prepare = dvb_buf_prepare,
- .buf_queue = dvb_buf_queue,
- .buf_release = dvb_buf_release,
+static int start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct cx8802_dev *dev = q->drv_priv;
+ struct cx88_dmaqueue *dmaq = &dev->mpegq;
+ struct cx88_buffer *buf;
+
+ buf = list_entry(dmaq->active.next, struct cx88_buffer, list);
+ cx8802_start_dma(dev, dmaq, buf);
+ return 0;
+}
+
+static void stop_streaming(struct vb2_queue *q)
+{
+ struct cx8802_dev *dev = q->drv_priv;
+ struct cx88_dmaqueue *dmaq = &dev->mpegq;
+ unsigned long flags;
+
+ cx8802_cancel_buffers(dev);
+
+ spin_lock_irqsave(&dev->slock, flags);
+ while (!list_empty(&dmaq->active)) {
+ struct cx88_buffer *buf = list_entry(dmaq->active.next,
+ struct cx88_buffer, list);
+
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+static struct vb2_ops dvb_qops = {
+ .queue_setup = queue_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_finish = buffer_finish,
+ .buf_queue = buffer_queue,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .start_streaming = start_streaming,
+ .stop_streaming = stop_streaming,
};
/* ------------------------------------------------------------------ */
@@ -130,7 +174,7 @@
int ret = 0;
int fe_id;
- fe_id = videobuf_dvb_find_frontend(&dev->frontends, fe);
+ fe_id = vb2_dvb_find_frontend(&dev->frontends, fe);
if (!fe_id) {
printk(KERN_ERR "%s() No frontend found\n", __func__);
return -EINVAL;
@@ -154,8 +198,8 @@
static void cx88_dvb_gate_ctrl(struct cx88_core *core, int open)
{
- struct videobuf_dvb_frontends *f;
- struct videobuf_dvb_frontend *fe;
+ struct vb2_dvb_frontends *f;
+ struct vb2_dvb_frontend *fe;
if (!core->dvbdev)
return;
@@ -166,9 +210,9 @@
return;
if (f->gate <= 1) /* undefined or fe0 */
- fe = videobuf_dvb_get_frontend(f, 1);
+ fe = vb2_dvb_get_frontend(f, 1);
else
- fe = videobuf_dvb_get_frontend(f, f->gate);
+ fe = vb2_dvb_get_frontend(f, f->gate);
if (fe && fe->dvb.frontend && fe->dvb.frontend->ops.i2c_gate_ctrl)
fe->dvb.frontend->ops.i2c_gate_ctrl(fe->dvb.frontend, open);
@@ -565,7 +609,7 @@
static int attach_xc3028(u8 addr, struct cx8802_dev *dev)
{
struct dvb_frontend *fe;
- struct videobuf_dvb_frontend *fe0 = NULL;
+ struct vb2_dvb_frontend *fe0 = NULL;
struct xc2028_ctrl ctl;
struct xc2028_config cfg = {
.i2c_adap = &dev->core->i2c_adap,
@@ -574,7 +618,7 @@
};
/* Get the first frontend */
- fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
+ fe0 = vb2_dvb_get_frontend(&dev->frontends, 1);
if (!fe0)
return -EINVAL;
@@ -611,10 +655,10 @@
static int attach_xc4000(struct cx8802_dev *dev, struct xc4000_config *cfg)
{
struct dvb_frontend *fe;
- struct videobuf_dvb_frontend *fe0 = NULL;
+ struct vb2_dvb_frontend *fe0 = NULL;
/* Get the first frontend */
- fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
+ fe0 = vb2_dvb_get_frontend(&dev->frontends, 1);
if (!fe0)
return -EINVAL;
@@ -745,7 +789,7 @@
static int cx8802_alloc_frontends(struct cx8802_dev *dev)
{
struct cx88_core *core = dev->core;
- struct videobuf_dvb_frontend *fe = NULL;
+ struct vb2_dvb_frontend *fe = NULL;
int i;
mutex_init(&dev->frontends.lock);
@@ -757,10 +801,10 @@
printk(KERN_INFO "%s() allocating %d frontend(s)\n", __func__,
core->board.num_frontends);
for (i = 1; i <= core->board.num_frontends; i++) {
- fe = videobuf_dvb_alloc_frontend(&dev->frontends, i);
+ fe = vb2_dvb_alloc_frontend(&dev->frontends, i);
if (!fe) {
printk(KERN_ERR "%s() failed to alloc\n", __func__);
- videobuf_dvb_dealloc_frontends(&dev->frontends);
+ vb2_dvb_dealloc_frontends(&dev->frontends);
return -ENOMEM;
}
}
@@ -958,7 +1002,7 @@
static int dvb_register(struct cx8802_dev *dev)
{
struct cx88_core *core = dev->core;
- struct videobuf_dvb_frontend *fe0, *fe1 = NULL;
+ struct vb2_dvb_frontend *fe0, *fe1 = NULL;
int mfe_shared = 0; /* bus not shared by default */
int res = -EINVAL;
@@ -968,7 +1012,7 @@
}
/* Get the first frontend */
- fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
+ fe0 = vb2_dvb_get_frontend(&dev->frontends, 1);
if (!fe0)
goto frontend_detach;
@@ -1046,7 +1090,7 @@
goto frontend_detach;
}
/* MFE frontend 2 */
- fe1 = videobuf_dvb_get_frontend(&dev->frontends, 2);
+ fe1 = vb2_dvb_get_frontend(&dev->frontends, 2);
if (!fe1)
goto frontend_detach;
/* DVB-T init */
@@ -1415,7 +1459,7 @@
goto frontend_detach;
}
/* MFE frontend 2 */
- fe1 = videobuf_dvb_get_frontend(&dev->frontends, 2);
+ fe1 = vb2_dvb_get_frontend(&dev->frontends, 2);
if (!fe1)
goto frontend_detach;
/* DVB-T Init */
@@ -1594,7 +1638,7 @@
call_all(core, core, s_power, 0);
/* register everything */
- res = videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev,
+ res = vb2_dvb_register_bus(&dev->frontends, THIS_MODULE, dev,
&dev->pci->dev, adapter_nr, mfe_shared);
if (res)
goto frontend_detach;
@@ -1602,7 +1646,7 @@
frontend_detach:
core->gate_ctrl = NULL;
- videobuf_dvb_dealloc_frontends(&dev->frontends);
+ vb2_dvb_dealloc_frontends(&dev->frontends);
return res;
}
@@ -1697,7 +1741,7 @@
struct cx88_core *core = drv->core;
struct cx8802_dev *dev = drv->core->dvbdev;
int err;
- struct videobuf_dvb_frontend *fe;
+ struct vb2_dvb_frontend *fe;
int i;
dprintk( 1, "%s\n", __func__);
@@ -1726,19 +1770,31 @@
err = -ENODEV;
for (i = 1; i <= core->board.num_frontends; i++) {
- fe = videobuf_dvb_get_frontend(&core->dvbdev->frontends, i);
+ struct vb2_queue *q;
+
+ fe = vb2_dvb_get_frontend(&core->dvbdev->frontends, i);
if (fe == NULL) {
printk(KERN_ERR "%s() failed to get frontend(%d)\n",
__func__, i);
goto fail_probe;
}
- videobuf_queue_sg_init(&fe->dvb.dvbq, &dvb_qops,
- &dev->pci->dev, &dev->slock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_TOP,
- sizeof(struct cx88_buffer),
- dev, NULL);
- /* init struct videobuf_dvb */
+ q = &fe->dvb.dvbq;
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
+ q->gfp_flags = GFP_DMA32;
+ q->min_buffers_needed = 2;
+ q->drv_priv = dev;
+ q->buf_struct_size = sizeof(struct cx88_buffer);
+ q->ops = &dvb_qops;
+ q->mem_ops = &vb2_dma_sg_memops;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &core->lock;
+
+ err = vb2_queue_init(q);
+ if (err < 0)
+ goto fail_probe;
+
+ /* init struct vb2_dvb */
fe->dvb.name = dev->core->name;
}
@@ -1749,7 +1805,7 @@
core->name, err);
return err;
fail_probe:
- videobuf_dvb_dealloc_frontends(&core->dvbdev->frontends);
+ vb2_dvb_dealloc_frontends(&core->dvbdev->frontends);
fail_core:
return err;
}
@@ -1761,7 +1817,7 @@
dprintk( 1, "%s\n", __func__);
- videobuf_dvb_unregister_bus(&dev->frontends);
+ vb2_dvb_unregister_bus(&dev->frontends);
vp3054_i2c_remove(dev);
diff --git a/drivers/media/pci/cx88/cx88-mpeg.c b/drivers/media/pci/cx88/cx88-mpeg.c
index 74b7b86..1c1f69e 100644
--- a/drivers/media/pci/cx88/cx88-mpeg.c
+++ b/drivers/media/pci/cx88/cx88-mpeg.c
@@ -86,21 +86,21 @@
static DEFINE_MUTEX(cx8802_mutex);
/* ------------------------------------------------------------------ */
-static int cx8802_start_dma(struct cx8802_dev *dev,
+int cx8802_start_dma(struct cx8802_dev *dev,
struct cx88_dmaqueue *q,
struct cx88_buffer *buf)
{
struct cx88_core *core = dev->core;
dprintk(1, "cx8802_start_dma w: %d, h: %d, f: %d\n",
- buf->vb.width, buf->vb.height, buf->vb.field);
+ core->width, core->height, core->field);
/* setup fifo + format */
cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28],
dev->ts_packet_size, buf->risc.dma);
/* write TS length to chip */
- cx_write(MO_TS_LNGTH, buf->vb.width);
+ cx_write(MO_TS_LNGTH, dev->ts_packet_size);
/* FIXME: this needs a review.
* also: move to cx88-blackbird + cx88-dvb source files? */
@@ -210,83 +210,40 @@
dprintk( 1, "cx8802_restart_queue\n" );
if (list_empty(&q->active))
- {
- struct cx88_buffer *prev;
- prev = NULL;
-
- dprintk(1, "cx8802_restart_queue: queue is empty\n" );
-
- for (;;) {
- if (list_empty(&q->queued))
- return 0;
- buf = list_entry(q->queued.next, struct cx88_buffer, vb.queue);
- if (NULL == prev) {
- list_move_tail(&buf->vb.queue, &q->active);
- cx8802_start_dma(dev, q, buf);
- buf->vb.state = VIDEOBUF_ACTIVE;
- buf->count = q->count++;
- mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
- dprintk(1,"[%p/%d] restart_queue - first active\n",
- buf,buf->vb.i);
-
- } else if (prev->vb.width == buf->vb.width &&
- prev->vb.height == buf->vb.height &&
- prev->fmt == buf->fmt) {
- list_move_tail(&buf->vb.queue, &q->active);
- buf->vb.state = VIDEOBUF_ACTIVE;
- buf->count = q->count++;
- prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
- dprintk(1,"[%p/%d] restart_queue - move to active\n",
- buf,buf->vb.i);
- } else {
- return 0;
- }
- prev = buf;
- }
return 0;
- }
- buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
+ buf = list_entry(q->active.next, struct cx88_buffer, list);
dprintk(2,"restart_queue [%p/%d]: restart dma\n",
- buf, buf->vb.i);
+ buf, buf->vb.v4l2_buf.index);
cx8802_start_dma(dev, q, buf);
- list_for_each_entry(buf, &q->active, vb.queue)
+ list_for_each_entry(buf, &q->active, list)
buf->count = q->count++;
- mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
return 0;
}
/* ------------------------------------------------------------------ */
-int cx8802_buf_prepare(struct videobuf_queue *q, struct cx8802_dev *dev,
- struct cx88_buffer *buf, enum v4l2_field field)
+int cx8802_buf_prepare(struct vb2_queue *q, struct cx8802_dev *dev,
+ struct cx88_buffer *buf)
{
int size = dev->ts_packet_size * dev->ts_packet_count;
- struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+ struct sg_table *sgt = vb2_dma_sg_plane_desc(&buf->vb, 0);
+ struct cx88_riscmem *risc = &buf->risc;
int rc;
- dprintk(1, "%s: %p\n", __func__, buf);
- if (0 != buf->vb.baddr && buf->vb.bsize < size)
+ if (vb2_plane_size(&buf->vb, 0) < size)
return -EINVAL;
+ vb2_set_plane_payload(&buf->vb, 0, size);
- if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
- buf->vb.width = dev->ts_packet_size;
- buf->vb.height = dev->ts_packet_count;
- buf->vb.size = size;
- buf->vb.field = field /*V4L2_FIELD_TOP*/;
-
- if (0 != (rc = videobuf_iolock(q,&buf->vb,NULL)))
- goto fail;
- cx88_risc_databuffer(dev->pci, &buf->risc,
- dma->sglist,
- buf->vb.width, buf->vb.height, 0);
+ rc = cx88_risc_databuffer(dev->pci, risc, sgt->sgl,
+ dev->ts_packet_size, dev->ts_packet_count, 0);
+ if (rc) {
+ if (risc->cpu)
+ pci_free_consistent(dev->pci, risc->size, risc->cpu, risc->dma);
+ memset(risc, 0, sizeof(*risc));
+ return rc;
}
- buf->vb.state = VIDEOBUF_PREPARED;
return 0;
-
- fail:
- cx88_free_buffer(q,buf);
- return rc;
}
void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf)
@@ -295,35 +252,33 @@
struct cx88_dmaqueue *cx88q = &dev->mpegq;
dprintk( 1, "cx8802_buf_queue\n" );
- /* add jump to stopper */
- buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
- buf->risc.jmp[1] = cpu_to_le32(cx88q->stopper.dma);
+ /* add jump to start */
+ buf->risc.cpu[1] = cpu_to_le32(buf->risc.dma + 8);
+ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_CNT_INC);
+ buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma + 8);
if (list_empty(&cx88q->active)) {
dprintk( 1, "queue is empty - first active\n" );
- list_add_tail(&buf->vb.queue,&cx88q->active);
- cx8802_start_dma(dev, cx88q, buf);
- buf->vb.state = VIDEOBUF_ACTIVE;
+ list_add_tail(&buf->list, &cx88q->active);
buf->count = cx88q->count++;
- mod_timer(&cx88q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(1,"[%p/%d] %s - first active\n",
- buf, buf->vb.i, __func__);
+ buf, buf->vb.v4l2_buf.index, __func__);
} else {
+ buf->risc.cpu[0] |= cpu_to_le32(RISC_IRQ1);
dprintk( 1, "queue is not empty - append to active\n" );
- prev = list_entry(cx88q->active.prev, struct cx88_buffer, vb.queue);
- list_add_tail(&buf->vb.queue,&cx88q->active);
- buf->vb.state = VIDEOBUF_ACTIVE;
+ prev = list_entry(cx88q->active.prev, struct cx88_buffer, list);
+ list_add_tail(&buf->list, &cx88q->active);
buf->count = cx88q->count++;
prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
dprintk( 1, "[%p/%d] %s - append to active\n",
- buf, buf->vb.i, __func__);
+ buf, buf->vb.v4l2_buf.index, __func__);
}
}
/* ----------------------------------------------------------- */
-static void do_cancel_buffers(struct cx8802_dev *dev, const char *reason, int restart)
+static void do_cancel_buffers(struct cx8802_dev *dev)
{
struct cx88_dmaqueue *q = &dev->mpegq;
struct cx88_buffer *buf;
@@ -331,41 +286,18 @@
spin_lock_irqsave(&dev->slock,flags);
while (!list_empty(&q->active)) {
- buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
- list_del(&buf->vb.queue);
- buf->vb.state = VIDEOBUF_ERROR;
- wake_up(&buf->vb.done);
- dprintk(1,"[%p/%d] %s - dma=0x%08lx\n",
- buf, buf->vb.i, reason, (unsigned long)buf->risc.dma);
- }
- if (restart)
- {
- dprintk(1, "restarting queue\n" );
- cx8802_restart_queue(dev,q);
+ buf = list_entry(q->active.next, struct cx88_buffer, list);
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
}
spin_unlock_irqrestore(&dev->slock,flags);
}
void cx8802_cancel_buffers(struct cx8802_dev *dev)
{
- struct cx88_dmaqueue *q = &dev->mpegq;
-
dprintk( 1, "cx8802_cancel_buffers" );
- del_timer_sync(&q->timeout);
cx8802_stop_dma(dev);
- do_cancel_buffers(dev,"cancel",0);
-}
-
-static void cx8802_timeout(unsigned long data)
-{
- struct cx8802_dev *dev = (struct cx8802_dev*)data;
-
- dprintk(1, "%s\n",__func__);
-
- if (debug)
- cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH28]);
- cx8802_stop_dma(dev);
- do_cancel_buffers(dev,"timeout",1);
+ do_cancel_buffers(dev);
}
static const char * cx88_mpeg_irqs[32] = {
@@ -411,19 +343,11 @@
spin_unlock(&dev->slock);
}
- /* risc2 y */
- if (status & 0x10) {
- spin_lock(&dev->slock);
- cx8802_restart_queue(dev,&dev->mpegq);
- spin_unlock(&dev->slock);
- }
-
/* other general errors */
if (status & 0x1f0100) {
dprintk( 0, "general errors: 0x%08x\n", status & 0x1f0100 );
spin_lock(&dev->slock);
cx8802_stop_dma(dev);
- cx8802_restart_queue(dev,&dev->mpegq);
spin_unlock(&dev->slock);
}
}
@@ -490,12 +414,6 @@
/* init dma queue */
INIT_LIST_HEAD(&dev->mpegq.active);
- INIT_LIST_HEAD(&dev->mpegq.queued);
- dev->mpegq.timeout.function = cx8802_timeout;
- dev->mpegq.timeout.data = (unsigned long)dev;
- init_timer(&dev->mpegq.timeout);
- cx88_risc_stopper(dev->pci,&dev->mpegq.stopper,
- MO_TS_DMACNTRL,0x11,0x00);
/* get irq */
err = request_irq(dev->pci->irq, cx8802_irq,
@@ -520,9 +438,6 @@
/* unregister stuff */
free_irq(dev->pci->irq, dev);
-
- /* free memory */
- btcx_riscmem_free(dev->pci,&dev->mpegq.stopper);
}
/* ----------------------------------------------------------- */
@@ -539,7 +454,6 @@
dprintk( 2, "suspend\n" );
printk("%s: suspend mpeg\n", core->name);
cx8802_stop_dma(dev);
- del_timer(&dev->mpegq.timeout);
}
spin_unlock_irqrestore(&dev->slock, flags);
@@ -815,6 +729,11 @@
if (NULL == dev)
goto fail_core;
dev->pci = pci_dev;
+ dev->alloc_ctx = vb2_dma_sg_init_ctx(&pci_dev->dev);
+ if (IS_ERR(dev->alloc_ctx)) {
+ err = PTR_ERR(dev->alloc_ctx);
+ goto fail_core;
+ }
dev->core = core;
/* Maintain a reference so cx88-video can query the 8802 device. */
@@ -834,6 +753,7 @@
return 0;
fail_free:
+ vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
kfree(dev);
fail_core:
core->dvbdev = NULL;
@@ -880,6 +800,7 @@
/* common */
cx8802_fini_common(dev);
cx88_core_put(dev->core,dev->pci);
+ vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
kfree(dev);
}
@@ -907,6 +828,7 @@
EXPORT_SYMBOL(cx8802_buf_prepare);
EXPORT_SYMBOL(cx8802_buf_queue);
EXPORT_SYMBOL(cx8802_cancel_buffers);
+EXPORT_SYMBOL(cx8802_start_dma);
EXPORT_SYMBOL(cx8802_register_driver);
EXPORT_SYMBOL(cx8802_unregister_driver);
diff --git a/drivers/media/pci/cx88/cx88-vbi.c b/drivers/media/pci/cx88/cx88-vbi.c
index f8f8389..32eb7fd 100644
--- a/drivers/media/pci/cx88/cx88-vbi.c
+++ b/drivers/media/pci/cx88/cx88-vbi.c
@@ -6,10 +6,6 @@
#include "cx88.h"
-static unsigned int vbibufs = 4;
-module_param(vbibufs,int,0644);
-MODULE_PARM_DESC(vbibufs,"number of vbi buffers, range 2-32");
-
static unsigned int vbi_debug;
module_param(vbi_debug,int,0644);
MODULE_PARM_DESC(vbi_debug,"enable debug messages [vbi]");
@@ -22,26 +18,27 @@
int cx8800_vbi_fmt (struct file *file, void *priv,
struct v4l2_format *f)
{
- struct cx8800_fh *fh = priv;
- struct cx8800_dev *dev = fh->dev;
+ struct cx8800_dev *dev = video_drvdata(file);
f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
f->fmt.vbi.offset = 244;
- f->fmt.vbi.count[0] = VBI_LINE_COUNT;
- f->fmt.vbi.count[1] = VBI_LINE_COUNT;
if (dev->core->tvnorm & V4L2_STD_525_60) {
/* ntsc */
f->fmt.vbi.sampling_rate = 28636363;
f->fmt.vbi.start[0] = 10;
f->fmt.vbi.start[1] = 273;
+ f->fmt.vbi.count[0] = VBI_LINE_NTSC_COUNT;
+ f->fmt.vbi.count[1] = VBI_LINE_NTSC_COUNT;
} else if (dev->core->tvnorm & V4L2_STD_625_50) {
/* pal */
f->fmt.vbi.sampling_rate = 35468950;
- f->fmt.vbi.start[0] = 7 -1;
- f->fmt.vbi.start[1] = 319 -1;
+ f->fmt.vbi.start[0] = V4L2_VBI_ITU_625_F1_START + 5;
+ f->fmt.vbi.start[1] = V4L2_VBI_ITU_625_F2_START + 5;
+ f->fmt.vbi.count[0] = VBI_LINE_PAL_COUNT;
+ f->fmt.vbi.count[1] = VBI_LINE_PAL_COUNT;
}
return 0;
}
@@ -54,7 +51,7 @@
/* setup fifo + format */
cx88_sram_channel_setup(dev->core, &cx88_sram_channels[SRAM_CH24],
- buf->vb.width, buf->risc.dma);
+ VBI_LINE_LENGTH, buf->risc.dma);
cx_write(MO_VBOS_CONTROL, ( (1 << 18) | // comb filter delay fixup
(1 << 15) | // enable vbi capture
@@ -78,7 +75,7 @@
return 0;
}
-int cx8800_stop_vbi_dma(struct cx8800_dev *dev)
+void cx8800_stop_vbi_dma(struct cx8800_dev *dev)
{
struct cx88_core *core = dev->core;
@@ -91,7 +88,6 @@
/* disable irqs */
cx_clear(MO_PCI_INTMSK, PCI_INT_VIDINT);
cx_clear(MO_VID_INTMSK, 0x0f0088);
- return 0;
}
int cx8800_restart_vbi_queue(struct cx8800_dev *dev,
@@ -102,144 +98,137 @@
if (list_empty(&q->active))
return 0;
- buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
+ buf = list_entry(q->active.next, struct cx88_buffer, list);
dprintk(2,"restart_queue [%p/%d]: restart dma\n",
- buf, buf->vb.i);
+ buf, buf->vb.v4l2_buf.index);
cx8800_start_vbi_dma(dev, q, buf);
- list_for_each_entry(buf, &q->active, vb.queue)
+ list_for_each_entry(buf, &q->active, list)
buf->count = q->count++;
- mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
return 0;
}
-void cx8800_vbi_timeout(unsigned long data)
-{
- struct cx8800_dev *dev = (struct cx8800_dev*)data;
- struct cx88_core *core = dev->core;
- struct cx88_dmaqueue *q = &dev->vbiq;
- struct cx88_buffer *buf;
- unsigned long flags;
-
- cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH24]);
-
- cx_clear(MO_VID_DMACNTRL, 0x88);
- cx_clear(VID_CAPTURE_CONTROL, 0x18);
-
- spin_lock_irqsave(&dev->slock,flags);
- while (!list_empty(&q->active)) {
- buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
- list_del(&buf->vb.queue);
- buf->vb.state = VIDEOBUF_ERROR;
- wake_up(&buf->vb.done);
- printk("%s/0: [%p/%d] timeout - dma=0x%08lx\n", dev->core->name,
- buf, buf->vb.i, (unsigned long)buf->risc.dma);
- }
- cx8800_restart_vbi_queue(dev,q);
- spin_unlock_irqrestore(&dev->slock,flags);
-}
-
/* ------------------------------------------------------------------ */
-static int
-vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], void *alloc_ctxs[])
{
- *size = VBI_LINE_COUNT * VBI_LINE_LENGTH * 2;
- if (0 == *count)
- *count = vbibufs;
- if (*count < 2)
- *count = 2;
- if (*count > 32)
- *count = 32;
+ struct cx8800_dev *dev = q->drv_priv;
+
+ *num_planes = 1;
+ if (dev->core->tvnorm & V4L2_STD_525_60)
+ sizes[0] = VBI_LINE_NTSC_COUNT * VBI_LINE_LENGTH * 2;
+ else
+ sizes[0] = VBI_LINE_PAL_COUNT * VBI_LINE_LENGTH * 2;
+ alloc_ctxs[0] = dev->alloc_ctx;
return 0;
}
-static int
-vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
- enum v4l2_field field)
+
+static int buffer_prepare(struct vb2_buffer *vb)
{
- struct cx8800_fh *fh = q->priv_data;
- struct cx8800_dev *dev = fh->dev;
- struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb);
+ struct cx8800_dev *dev = vb->vb2_queue->drv_priv;
+ struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb);
+ struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
+ unsigned int lines;
unsigned int size;
- int rc;
- size = VBI_LINE_COUNT * VBI_LINE_LENGTH * 2;
- if (0 != buf->vb.baddr && buf->vb.bsize < size)
+ if (dev->core->tvnorm & V4L2_STD_525_60)
+ lines = VBI_LINE_NTSC_COUNT;
+ else
+ lines = VBI_LINE_PAL_COUNT;
+ size = lines * VBI_LINE_LENGTH * 2;
+ if (vb2_plane_size(vb, 0) < size)
return -EINVAL;
+ vb2_set_plane_payload(vb, 0, size);
- if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
- struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
- buf->vb.width = VBI_LINE_LENGTH;
- buf->vb.height = VBI_LINE_COUNT;
- buf->vb.size = size;
- buf->vb.field = V4L2_FIELD_SEQ_TB;
-
- if (0 != (rc = videobuf_iolock(q,&buf->vb,NULL)))
- goto fail;
- cx88_risc_buffer(dev->pci, &buf->risc,
- dma->sglist,
- 0, buf->vb.width * buf->vb.height,
- buf->vb.width, 0,
- buf->vb.height);
- }
- buf->vb.state = VIDEOBUF_PREPARED;
+ cx88_risc_buffer(dev->pci, &buf->risc, sgt->sgl,
+ 0, VBI_LINE_LENGTH * lines,
+ VBI_LINE_LENGTH, 0,
+ lines);
return 0;
-
- fail:
- cx88_free_buffer(q,buf);
- return rc;
}
-static void
-vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+static void buffer_finish(struct vb2_buffer *vb)
{
- struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb);
+ struct cx8800_dev *dev = vb->vb2_queue->drv_priv;
+ struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb);
+ struct cx88_riscmem *risc = &buf->risc;
+
+ if (risc->cpu)
+ pci_free_consistent(dev->pci, risc->size, risc->cpu, risc->dma);
+ memset(risc, 0, sizeof(*risc));
+}
+
+static void buffer_queue(struct vb2_buffer *vb)
+{
+ struct cx8800_dev *dev = vb->vb2_queue->drv_priv;
+ struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb);
struct cx88_buffer *prev;
- struct cx8800_fh *fh = vq->priv_data;
- struct cx8800_dev *dev = fh->dev;
struct cx88_dmaqueue *q = &dev->vbiq;
- /* add jump to stopper */
- buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
- buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma);
+ /* add jump to start */
+ buf->risc.cpu[1] = cpu_to_le32(buf->risc.dma + 8);
+ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_CNT_INC);
+ buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma + 8);
if (list_empty(&q->active)) {
- list_add_tail(&buf->vb.queue,&q->active);
+ list_add_tail(&buf->list, &q->active);
cx8800_start_vbi_dma(dev, q, buf);
- buf->vb.state = VIDEOBUF_ACTIVE;
buf->count = q->count++;
- mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(2,"[%p/%d] vbi_queue - first active\n",
- buf, buf->vb.i);
+ buf, buf->vb.v4l2_buf.index);
} else {
- prev = list_entry(q->active.prev, struct cx88_buffer, vb.queue);
- list_add_tail(&buf->vb.queue,&q->active);
- buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->risc.cpu[0] |= cpu_to_le32(RISC_IRQ1);
+ prev = list_entry(q->active.prev, struct cx88_buffer, list);
+ list_add_tail(&buf->list, &q->active);
buf->count = q->count++;
prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
dprintk(2,"[%p/%d] buffer_queue - append to active\n",
- buf, buf->vb.i);
+ buf, buf->vb.v4l2_buf.index);
}
}
-static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+static int start_streaming(struct vb2_queue *q, unsigned int count)
{
- struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb);
+ struct cx8800_dev *dev = q->drv_priv;
+ struct cx88_dmaqueue *dmaq = &dev->vbiq;
+ struct cx88_buffer *buf = list_entry(dmaq->active.next,
+ struct cx88_buffer, list);
- cx88_free_buffer(q,buf);
+ cx8800_start_vbi_dma(dev, dmaq, buf);
+ return 0;
}
-const struct videobuf_queue_ops cx8800_vbi_qops = {
- .buf_setup = vbi_setup,
- .buf_prepare = vbi_prepare,
- .buf_queue = vbi_queue,
- .buf_release = vbi_release,
-};
+static void stop_streaming(struct vb2_queue *q)
+{
+ struct cx8800_dev *dev = q->drv_priv;
+ struct cx88_core *core = dev->core;
+ struct cx88_dmaqueue *dmaq = &dev->vbiq;
+ unsigned long flags;
-/* ------------------------------------------------------------------ */
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
+ cx_clear(MO_VID_DMACNTRL, 0x11);
+ cx_clear(VID_CAPTURE_CONTROL, 0x06);
+ cx8800_stop_vbi_dma(dev);
+ spin_lock_irqsave(&dev->slock, flags);
+ while (!list_empty(&dmaq->active)) {
+ struct cx88_buffer *buf = list_entry(dmaq->active.next,
+ struct cx88_buffer, list);
+
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+const struct vb2_ops cx8800_vbi_qops = {
+ .queue_setup = queue_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_finish = buffer_finish,
+ .buf_queue = buffer_queue,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .start_streaming = start_streaming,
+ .stop_streaming = stop_streaming,
+};
diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c
index e46b732..05d5844 100644
--- a/drivers/media/pci/cx88/cx88-video.c
+++ b/drivers/media/pci/cx88/cx88-video.c
@@ -70,10 +70,6 @@
module_param(irq_debug,int,0644);
MODULE_PARM_DESC(irq_debug,"enable debug messages [IRQ handler]");
-static unsigned int vid_limit = 16;
-module_param(vid_limit,int,0644);
-MODULE_PARM_DESC(vid_limit,"capture memory limit in megabytes");
-
#define dprintk(level,fmt, arg...) if (video_debug >= level) \
printk(KERN_DEBUG "%s/0: " fmt, core->name , ## arg)
@@ -297,56 +293,6 @@
CX8800_AUD_CTLS = ARRAY_SIZE(cx8800_aud_ctls),
};
-/* ------------------------------------------------------------------- */
-/* resource management */
-
-static int res_get(struct cx8800_dev *dev, struct cx8800_fh *fh, unsigned int bit)
-{
- struct cx88_core *core = dev->core;
- if (fh->resources & bit)
- /* have it already allocated */
- return 1;
-
- /* is it free? */
- mutex_lock(&core->lock);
- if (dev->resources & bit) {
- /* no, someone else uses it */
- mutex_unlock(&core->lock);
- return 0;
- }
- /* it's free, grab it */
- fh->resources |= bit;
- dev->resources |= bit;
- dprintk(1,"res: get %d\n",bit);
- mutex_unlock(&core->lock);
- return 1;
-}
-
-static
-int res_check(struct cx8800_fh *fh, unsigned int bit)
-{
- return (fh->resources & bit);
-}
-
-static
-int res_locked(struct cx8800_dev *dev, unsigned int bit)
-{
- return (dev->resources & bit);
-}
-
-static
-void res_free(struct cx8800_dev *dev, struct cx8800_fh *fh, unsigned int bits)
-{
- struct cx88_core *core = dev->core;
- BUG_ON((fh->resources & bits) != bits);
-
- mutex_lock(&core->lock);
- fh->resources &= ~bits;
- dev->resources &= ~bits;
- dprintk(1,"res: put %d\n",bits);
- mutex_unlock(&core->lock);
-}
-
/* ------------------------------------------------------------------ */
int cx88_video_mux(struct cx88_core *core, unsigned int input)
@@ -419,8 +365,8 @@
/* setup fifo + format */
cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH21],
buf->bpl, buf->risc.dma);
- cx88_set_scale(core, buf->vb.width, buf->vb.height, buf->vb.field);
- cx_write(MO_COLOR_CTRL, buf->fmt->cxformat | ColorFormatGamma);
+ cx88_set_scale(core, core->width, core->height, core->field);
+ cx_write(MO_COLOR_CTRL, dev->fmt->cxformat | ColorFormatGamma);
/* reset counter */
cx_write(MO_VIDY_GPCNTRL,GP_COUNT_CONTROL_RESET);
@@ -470,433 +416,203 @@
struct cx88_dmaqueue *q)
{
struct cx88_core *core = dev->core;
- struct cx88_buffer *buf, *prev;
+ struct cx88_buffer *buf;
if (!list_empty(&q->active)) {
- buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
+ buf = list_entry(q->active.next, struct cx88_buffer, list);
dprintk(2,"restart_queue [%p/%d]: restart dma\n",
- buf, buf->vb.i);
+ buf, buf->vb.v4l2_buf.index);
start_video_dma(dev, q, buf);
- list_for_each_entry(buf, &q->active, vb.queue)
+ list_for_each_entry(buf, &q->active, list)
buf->count = q->count++;
- mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
- return 0;
}
-
- prev = NULL;
- for (;;) {
- if (list_empty(&q->queued))
- return 0;
- buf = list_entry(q->queued.next, struct cx88_buffer, vb.queue);
- if (NULL == prev) {
- list_move_tail(&buf->vb.queue, &q->active);
- start_video_dma(dev, q, buf);
- buf->vb.state = VIDEOBUF_ACTIVE;
- buf->count = q->count++;
- mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
- dprintk(2,"[%p/%d] restart_queue - first active\n",
- buf,buf->vb.i);
-
- } else if (prev->vb.width == buf->vb.width &&
- prev->vb.height == buf->vb.height &&
- prev->fmt == buf->fmt) {
- list_move_tail(&buf->vb.queue, &q->active);
- buf->vb.state = VIDEOBUF_ACTIVE;
- buf->count = q->count++;
- prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
- dprintk(2,"[%p/%d] restart_queue - move to active\n",
- buf,buf->vb.i);
- } else {
- return 0;
- }
- prev = buf;
- }
+ return 0;
}
/* ------------------------------------------------------------------ */
-static int
-buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], void *alloc_ctxs[])
{
- struct cx8800_fh *fh = q->priv_data;
- struct cx8800_dev *dev = fh->dev;
+ struct cx8800_dev *dev = q->drv_priv;
+ struct cx88_core *core = dev->core;
- *size = dev->fmt->depth * dev->width * dev->height >> 3;
- if (0 == *count)
- *count = 32;
- if (*size * *count > vid_limit * 1024 * 1024)
- *count = (vid_limit * 1024 * 1024) / *size;
+ *num_planes = 1;
+ sizes[0] = (dev->fmt->depth * core->width * core->height) >> 3;
+ alloc_ctxs[0] = dev->alloc_ctx;
return 0;
}
-static int
-buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
- enum v4l2_field field)
+static int buffer_prepare(struct vb2_buffer *vb)
{
- struct cx8800_fh *fh = q->priv_data;
- struct cx8800_dev *dev = fh->dev;
+ struct cx8800_dev *dev = vb->vb2_queue->drv_priv;
struct cx88_core *core = dev->core;
- struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb);
- struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
- int rc, init_buffer = 0;
+ struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb);
+ struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
- BUG_ON(NULL == dev->fmt);
- if (dev->width < 48 || dev->width > norm_maxw(core->tvnorm) ||
- dev->height < 32 || dev->height > norm_maxh(core->tvnorm))
+ buf->bpl = core->width * dev->fmt->depth >> 3;
+
+ if (vb2_plane_size(vb, 0) < core->height * buf->bpl)
return -EINVAL;
- buf->vb.size = (dev->width * dev->height * dev->fmt->depth) >> 3;
- if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
- return -EINVAL;
+ vb2_set_plane_payload(vb, 0, core->height * buf->bpl);
- if (buf->fmt != dev->fmt ||
- buf->vb.width != dev->width ||
- buf->vb.height != dev->height ||
- buf->vb.field != field) {
- buf->fmt = dev->fmt;
- buf->vb.width = dev->width;
- buf->vb.height = dev->height;
- buf->vb.field = field;
- init_buffer = 1;
- }
-
- if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
- init_buffer = 1;
- if (0 != (rc = videobuf_iolock(q,&buf->vb,NULL)))
- goto fail;
- }
-
- if (init_buffer) {
- buf->bpl = buf->vb.width * buf->fmt->depth >> 3;
- switch (buf->vb.field) {
- case V4L2_FIELD_TOP:
- cx88_risc_buffer(dev->pci, &buf->risc,
- dma->sglist, 0, UNSET,
- buf->bpl, 0, buf->vb.height);
- break;
- case V4L2_FIELD_BOTTOM:
- cx88_risc_buffer(dev->pci, &buf->risc,
- dma->sglist, UNSET, 0,
- buf->bpl, 0, buf->vb.height);
- break;
- case V4L2_FIELD_INTERLACED:
- cx88_risc_buffer(dev->pci, &buf->risc,
- dma->sglist, 0, buf->bpl,
- buf->bpl, buf->bpl,
- buf->vb.height >> 1);
- break;
- case V4L2_FIELD_SEQ_TB:
- cx88_risc_buffer(dev->pci, &buf->risc,
- dma->sglist,
- 0, buf->bpl * (buf->vb.height >> 1),
- buf->bpl, 0,
- buf->vb.height >> 1);
- break;
- case V4L2_FIELD_SEQ_BT:
- cx88_risc_buffer(dev->pci, &buf->risc,
- dma->sglist,
- buf->bpl * (buf->vb.height >> 1), 0,
- buf->bpl, 0,
- buf->vb.height >> 1);
- break;
- default:
- BUG();
- }
+ switch (core->field) {
+ case V4L2_FIELD_TOP:
+ cx88_risc_buffer(dev->pci, &buf->risc,
+ sgt->sgl, 0, UNSET,
+ buf->bpl, 0, core->height);
+ break;
+ case V4L2_FIELD_BOTTOM:
+ cx88_risc_buffer(dev->pci, &buf->risc,
+ sgt->sgl, UNSET, 0,
+ buf->bpl, 0, core->height);
+ break;
+ case V4L2_FIELD_SEQ_TB:
+ cx88_risc_buffer(dev->pci, &buf->risc,
+ sgt->sgl,
+ 0, buf->bpl * (core->height >> 1),
+ buf->bpl, 0,
+ core->height >> 1);
+ break;
+ case V4L2_FIELD_SEQ_BT:
+ cx88_risc_buffer(dev->pci, &buf->risc,
+ sgt->sgl,
+ buf->bpl * (core->height >> 1), 0,
+ buf->bpl, 0,
+ core->height >> 1);
+ break;
+ case V4L2_FIELD_INTERLACED:
+ default:
+ cx88_risc_buffer(dev->pci, &buf->risc,
+ sgt->sgl, 0, buf->bpl,
+ buf->bpl, buf->bpl,
+ core->height >> 1);
+ break;
}
dprintk(2,"[%p/%d] buffer_prepare - %dx%d %dbpp \"%s\" - dma=0x%08lx\n",
- buf, buf->vb.i,
- dev->width, dev->height, dev->fmt->depth, dev->fmt->name,
+ buf, buf->vb.v4l2_buf.index,
+ core->width, core->height, dev->fmt->depth, dev->fmt->name,
(unsigned long)buf->risc.dma);
-
- buf->vb.state = VIDEOBUF_PREPARED;
return 0;
-
- fail:
- cx88_free_buffer(q,buf);
- return rc;
}
-static void
-buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+static void buffer_finish(struct vb2_buffer *vb)
{
- struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb);
+ struct cx8800_dev *dev = vb->vb2_queue->drv_priv;
+ struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb);
+ struct cx88_riscmem *risc = &buf->risc;
+
+ if (risc->cpu)
+ pci_free_consistent(dev->pci, risc->size, risc->cpu, risc->dma);
+ memset(risc, 0, sizeof(*risc));
+}
+
+static void buffer_queue(struct vb2_buffer *vb)
+{
+ struct cx8800_dev *dev = vb->vb2_queue->drv_priv;
+ struct cx88_buffer *buf = container_of(vb, struct cx88_buffer, vb);
struct cx88_buffer *prev;
- struct cx8800_fh *fh = vq->priv_data;
- struct cx8800_dev *dev = fh->dev;
struct cx88_core *core = dev->core;
struct cx88_dmaqueue *q = &dev->vidq;
- /* add jump to stopper */
- buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
- buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma);
+ /* add jump to start */
+ buf->risc.cpu[1] = cpu_to_le32(buf->risc.dma + 8);
+ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_CNT_INC);
+ buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma + 8);
- if (!list_empty(&q->queued)) {
- list_add_tail(&buf->vb.queue,&q->queued);
- buf->vb.state = VIDEOBUF_QUEUED;
- dprintk(2,"[%p/%d] buffer_queue - append to queued\n",
- buf, buf->vb.i);
-
- } else if (list_empty(&q->active)) {
- list_add_tail(&buf->vb.queue,&q->active);
- start_video_dma(dev, q, buf);
- buf->vb.state = VIDEOBUF_ACTIVE;
+ if (list_empty(&q->active)) {
+ list_add_tail(&buf->list, &q->active);
buf->count = q->count++;
- mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(2,"[%p/%d] buffer_queue - first active\n",
- buf, buf->vb.i);
+ buf, buf->vb.v4l2_buf.index);
} else {
- prev = list_entry(q->active.prev, struct cx88_buffer, vb.queue);
- if (prev->vb.width == buf->vb.width &&
- prev->vb.height == buf->vb.height &&
- prev->fmt == buf->fmt) {
- list_add_tail(&buf->vb.queue,&q->active);
- buf->vb.state = VIDEOBUF_ACTIVE;
- buf->count = q->count++;
- prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
- dprintk(2,"[%p/%d] buffer_queue - append to active\n",
- buf, buf->vb.i);
-
- } else {
- list_add_tail(&buf->vb.queue,&q->queued);
- buf->vb.state = VIDEOBUF_QUEUED;
- dprintk(2,"[%p/%d] buffer_queue - first queued\n",
- buf, buf->vb.i);
- }
+ buf->risc.cpu[0] |= cpu_to_le32(RISC_IRQ1);
+ prev = list_entry(q->active.prev, struct cx88_buffer, list);
+ list_add_tail(&buf->list, &q->active);
+ buf->count = q->count++;
+ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+ dprintk(2, "[%p/%d] buffer_queue - append to active\n",
+ buf, buf->vb.v4l2_buf.index);
}
}
-static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+static int start_streaming(struct vb2_queue *q, unsigned int count)
{
- struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb);
+ struct cx8800_dev *dev = q->drv_priv;
+ struct cx88_dmaqueue *dmaq = &dev->vidq;
+ struct cx88_buffer *buf = list_entry(dmaq->active.next,
+ struct cx88_buffer, list);
- cx88_free_buffer(q,buf);
+ start_video_dma(dev, dmaq, buf);
+ return 0;
}
-static const struct videobuf_queue_ops cx8800_video_qops = {
- .buf_setup = buffer_setup,
+static void stop_streaming(struct vb2_queue *q)
+{
+ struct cx8800_dev *dev = q->drv_priv;
+ struct cx88_core *core = dev->core;
+ struct cx88_dmaqueue *dmaq = &dev->vidq;
+ unsigned long flags;
+
+ cx_clear(MO_VID_DMACNTRL, 0x11);
+ cx_clear(VID_CAPTURE_CONTROL, 0x06);
+ spin_lock_irqsave(&dev->slock, flags);
+ while (!list_empty(&dmaq->active)) {
+ struct cx88_buffer *buf = list_entry(dmaq->active.next,
+ struct cx88_buffer, list);
+
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+static struct vb2_ops cx8800_video_qops = {
+ .queue_setup = queue_setup,
.buf_prepare = buffer_prepare,
+ .buf_finish = buffer_finish,
.buf_queue = buffer_queue,
- .buf_release = buffer_release,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .start_streaming = start_streaming,
+ .stop_streaming = stop_streaming,
};
/* ------------------------------------------------------------------ */
-
-/* ------------------------------------------------------------------ */
-
-static struct videobuf_queue *get_queue(struct file *file)
+static int radio_open(struct file *file)
{
- struct video_device *vdev = video_devdata(file);
- struct cx8800_fh *fh = file->private_data;
-
- switch (vdev->vfl_type) {
- case VFL_TYPE_GRABBER:
- return &fh->vidq;
- case VFL_TYPE_VBI:
- return &fh->vbiq;
- default:
- BUG();
- }
-}
-
-static int get_resource(struct file *file)
-{
- struct video_device *vdev = video_devdata(file);
-
- switch (vdev->vfl_type) {
- case VFL_TYPE_GRABBER:
- return RESOURCE_VIDEO;
- case VFL_TYPE_VBI:
- return RESOURCE_VBI;
- default:
- BUG();
- }
-}
-
-static int video_open(struct file *file)
-{
- struct video_device *vdev = video_devdata(file);
struct cx8800_dev *dev = video_drvdata(file);
struct cx88_core *core = dev->core;
- struct cx8800_fh *fh;
- enum v4l2_buf_type type = 0;
- int radio = 0;
+ int ret = v4l2_fh_open(file);
- switch (vdev->vfl_type) {
- case VFL_TYPE_GRABBER:
- type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- break;
- case VFL_TYPE_VBI:
- type = V4L2_BUF_TYPE_VBI_CAPTURE;
- break;
- case VFL_TYPE_RADIO:
- radio = 1;
- break;
- }
+ if (ret)
+ return ret;
- dprintk(1, "open dev=%s radio=%d type=%s\n",
- video_device_node_name(vdev), radio, v4l2_type_names[type]);
-
- /* allocate + initialize per filehandle data */
- fh = kzalloc(sizeof(*fh),GFP_KERNEL);
- if (unlikely(!fh))
- return -ENOMEM;
-
- v4l2_fh_init(&fh->fh, vdev);
- file->private_data = fh;
- fh->dev = dev;
-
- mutex_lock(&core->lock);
-
- videobuf_queue_sg_init(&fh->vidq, &cx8800_video_qops,
- &dev->pci->dev, &dev->slock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_INTERLACED,
- sizeof(struct cx88_buffer),
- fh, NULL);
- videobuf_queue_sg_init(&fh->vbiq, &cx8800_vbi_qops,
- &dev->pci->dev, &dev->slock,
- V4L2_BUF_TYPE_VBI_CAPTURE,
- V4L2_FIELD_SEQ_TB,
- sizeof(struct cx88_buffer),
- fh, NULL);
-
- if (vdev->vfl_type == VFL_TYPE_RADIO) {
- dprintk(1,"video_open: setting radio device\n");
- cx_write(MO_GP3_IO, core->board.radio.gpio3);
- cx_write(MO_GP0_IO, core->board.radio.gpio0);
- cx_write(MO_GP1_IO, core->board.radio.gpio1);
- cx_write(MO_GP2_IO, core->board.radio.gpio2);
- if (core->board.radio.audioroute) {
- if (core->sd_wm8775) {
- call_all(core, audio, s_routing,
+ cx_write(MO_GP3_IO, core->board.radio.gpio3);
+ cx_write(MO_GP0_IO, core->board.radio.gpio0);
+ cx_write(MO_GP1_IO, core->board.radio.gpio1);
+ cx_write(MO_GP2_IO, core->board.radio.gpio2);
+ if (core->board.radio.audioroute) {
+ if (core->sd_wm8775) {
+ call_all(core, audio, s_routing,
core->board.radio.audioroute, 0, 0);
- }
- /* "I2S ADC mode" */
- core->tvaudio = WW_I2SADC;
- cx88_set_tvaudio(core);
- } else {
- /* FM Mode */
- core->tvaudio = WW_FM;
- cx88_set_tvaudio(core);
- cx88_set_stereo(core,V4L2_TUNER_MODE_STEREO,1);
}
- call_all(core, tuner, s_radio);
- }
-
- core->users++;
- mutex_unlock(&core->lock);
- v4l2_fh_add(&fh->fh);
-
- return 0;
-}
-
-static ssize_t
-video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
-{
- struct video_device *vdev = video_devdata(file);
- struct cx8800_fh *fh = file->private_data;
-
- switch (vdev->vfl_type) {
- case VFL_TYPE_GRABBER:
- if (res_locked(fh->dev,RESOURCE_VIDEO))
- return -EBUSY;
- return videobuf_read_one(&fh->vidq, data, count, ppos,
- file->f_flags & O_NONBLOCK);
- case VFL_TYPE_VBI:
- if (!res_get(fh->dev,fh,RESOURCE_VBI))
- return -EBUSY;
- return videobuf_read_stream(&fh->vbiq, data, count, ppos, 1,
- file->f_flags & O_NONBLOCK);
- default:
- BUG();
- }
-}
-
-static unsigned int
-video_poll(struct file *file, struct poll_table_struct *wait)
-{
- struct video_device *vdev = video_devdata(file);
- struct cx8800_fh *fh = file->private_data;
- struct cx88_buffer *buf;
- unsigned int rc = v4l2_ctrl_poll(file, wait);
-
- if (vdev->vfl_type == VFL_TYPE_VBI) {
- if (!res_get(fh->dev,fh,RESOURCE_VBI))
- return rc | POLLERR;
- return rc | videobuf_poll_stream(file, &fh->vbiq, wait);
- }
- mutex_lock(&fh->vidq.vb_lock);
- if (res_check(fh,RESOURCE_VIDEO)) {
- /* streaming capture */
- if (list_empty(&fh->vidq.stream))
- goto done;
- buf = list_entry(fh->vidq.stream.next,struct cx88_buffer,vb.stream);
+ /* "I2S ADC mode" */
+ core->tvaudio = WW_I2SADC;
+ cx88_set_tvaudio(core);
} else {
- /* read() capture */
- buf = (struct cx88_buffer*)fh->vidq.read_buf;
- if (NULL == buf)
- goto done;
+ /* FM Mode */
+ core->tvaudio = WW_FM;
+ cx88_set_tvaudio(core);
+ cx88_set_stereo(core, V4L2_TUNER_MODE_STEREO, 1);
}
- poll_wait(file, &buf->vb.done, wait);
- if (buf->vb.state == VIDEOBUF_DONE ||
- buf->vb.state == VIDEOBUF_ERROR)
- rc |= POLLIN|POLLRDNORM;
-done:
- mutex_unlock(&fh->vidq.vb_lock);
- return rc;
-}
-
-static int video_release(struct file *file)
-{
- struct cx8800_fh *fh = file->private_data;
- struct cx8800_dev *dev = fh->dev;
-
- /* turn off overlay */
- if (res_check(fh, RESOURCE_OVERLAY)) {
- /* FIXME */
- res_free(dev,fh,RESOURCE_OVERLAY);
- }
-
- /* stop video capture */
- if (res_check(fh, RESOURCE_VIDEO)) {
- videobuf_queue_cancel(&fh->vidq);
- res_free(dev,fh,RESOURCE_VIDEO);
- }
- if (fh->vidq.read_buf) {
- buffer_release(&fh->vidq,fh->vidq.read_buf);
- kfree(fh->vidq.read_buf);
- }
-
- /* stop vbi capture */
- if (res_check(fh, RESOURCE_VBI)) {
- videobuf_stop(&fh->vbiq);
- res_free(dev,fh,RESOURCE_VBI);
- }
-
- videobuf_mmap_free(&fh->vidq);
- videobuf_mmap_free(&fh->vbiq);
-
- mutex_lock(&dev->core->lock);
- v4l2_fh_del(&fh->fh);
- v4l2_fh_exit(&fh->fh);
- file->private_data = NULL;
- kfree(fh);
-
- dev->core->users--;
- if (!dev->core->users)
- call_all(dev->core, core, s_power, 0);
- mutex_unlock(&dev->core->lock);
-
+ call_all(core, tuner, s_radio);
return 0;
}
-static int
-video_mmap(struct file *file, struct vm_area_struct * vma)
-{
- return videobuf_mmap_mapper(get_queue(file), vma);
-}
-
/* ------------------------------------------------------------------ */
/* VIDEO CTRL IOCTLS */
@@ -999,12 +715,12 @@
static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct cx8800_fh *fh = priv;
- struct cx8800_dev *dev = fh->dev;
+ struct cx8800_dev *dev = video_drvdata(file);
+ struct cx88_core *core = dev->core;
- f->fmt.pix.width = dev->width;
- f->fmt.pix.height = dev->height;
- f->fmt.pix.field = fh->vidq.field;
+ f->fmt.pix.width = core->width;
+ f->fmt.pix.height = core->height;
+ f->fmt.pix.field = core->field;
f->fmt.pix.pixelformat = dev->fmt->fourcc;
f->fmt.pix.bytesperline =
(f->fmt.pix.width * dev->fmt->depth) >> 3;
@@ -1017,7 +733,8 @@
static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+ struct cx8800_dev *dev = video_drvdata(file);
+ struct cx88_core *core = dev->core;
const struct cx8800_fmt *fmt;
enum v4l2_field field;
unsigned int maxw, maxh;
@@ -1026,30 +743,30 @@
if (NULL == fmt)
return -EINVAL;
- field = f->fmt.pix.field;
- maxw = norm_maxw(core->tvnorm);
- maxh = norm_maxh(core->tvnorm);
+ maxw = norm_maxw(core->tvnorm);
+ maxh = norm_maxh(core->tvnorm);
- if (V4L2_FIELD_ANY == field) {
- field = (f->fmt.pix.height > maxh/2)
- ? V4L2_FIELD_INTERLACED
- : V4L2_FIELD_BOTTOM;
- }
+ field = f->fmt.pix.field;
switch (field) {
case V4L2_FIELD_TOP:
case V4L2_FIELD_BOTTOM:
- maxh = maxh / 2;
- break;
case V4L2_FIELD_INTERLACED:
+ case V4L2_FIELD_SEQ_BT:
+ case V4L2_FIELD_SEQ_TB:
break;
default:
- return -EINVAL;
+ field = (f->fmt.pix.height > maxh / 2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_BOTTOM;
+ break;
}
+ if (V4L2_FIELD_HAS_T_OR_B(field))
+ maxh /= 2;
- f->fmt.pix.field = field;
v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2,
&f->fmt.pix.height, 32, maxh, 0, 0);
+ f->fmt.pix.field = field;
f->fmt.pix.bytesperline =
(f->fmt.pix.width * fmt->depth) >> 3;
f->fmt.pix.sizeimage =
@@ -1061,16 +778,20 @@
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct cx8800_fh *fh = priv;
- struct cx8800_dev *dev = fh->dev;
+ struct cx8800_dev *dev = video_drvdata(file);
+ struct cx88_core *core = dev->core;
int err = vidioc_try_fmt_vid_cap (file,priv,f);
if (0 != err)
return err;
- dev->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
- dev->width = f->fmt.pix.width;
- dev->height = f->fmt.pix.height;
- fh->vidq.field = f->fmt.pix.field;
+ if (vb2_is_busy(&dev->vb2_vidq) || vb2_is_busy(&dev->vb2_vbiq))
+ return -EBUSY;
+ if (core->dvbdev && vb2_is_busy(&core->dvbdev->vb2_mpegq))
+ return -EBUSY;
+ dev->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ core->width = f->fmt.pix.width;
+ core->height = f->fmt.pix.height;
+ core->field = f->fmt.pix.field;
return 0;
}
@@ -1104,8 +825,8 @@
static int vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
- struct cx8800_dev *dev = ((struct cx8800_fh *)priv)->dev;
- struct cx88_core *core = dev->core;
+ struct cx8800_dev *dev = video_drvdata(file);
+ struct cx88_core *core = dev->core;
strcpy(cap->driver, "cx8800");
sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
@@ -1125,64 +846,10 @@
return 0;
}
-static int vidioc_reqbufs (struct file *file, void *priv, struct v4l2_requestbuffers *p)
-{
- return videobuf_reqbufs(get_queue(file), p);
-}
-
-static int vidioc_querybuf (struct file *file, void *priv, struct v4l2_buffer *p)
-{
- return videobuf_querybuf(get_queue(file), p);
-}
-
-static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *p)
-{
- return videobuf_qbuf(get_queue(file), p);
-}
-
-static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *p)
-{
- return videobuf_dqbuf(get_queue(file), p,
- file->f_flags & O_NONBLOCK);
-}
-
-static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- struct video_device *vdev = video_devdata(file);
- struct cx8800_fh *fh = priv;
- struct cx8800_dev *dev = fh->dev;
-
- if ((vdev->vfl_type == VFL_TYPE_GRABBER && i != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
- (vdev->vfl_type == VFL_TYPE_VBI && i != V4L2_BUF_TYPE_VBI_CAPTURE))
- return -EINVAL;
-
- if (unlikely(!res_get(dev, fh, get_resource(file))))
- return -EBUSY;
- return videobuf_streamon(get_queue(file));
-}
-
-static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- struct video_device *vdev = video_devdata(file);
- struct cx8800_fh *fh = priv;
- struct cx8800_dev *dev = fh->dev;
- int err, res;
-
- if ((vdev->vfl_type == VFL_TYPE_GRABBER && i != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
- (vdev->vfl_type == VFL_TYPE_VBI && i != V4L2_BUF_TYPE_VBI_CAPTURE))
- return -EINVAL;
-
- res = get_resource(file);
- err = videobuf_streamoff(get_queue(file));
- if (err < 0)
- return err;
- res_free(dev,fh,res);
- return 0;
-}
-
static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorm)
{
- struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+ struct cx8800_dev *dev = video_drvdata(file);
+ struct cx88_core *core = dev->core;
*tvnorm = core->tvnorm;
return 0;
@@ -1190,13 +857,10 @@
static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id tvnorms)
{
- struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+ struct cx8800_dev *dev = video_drvdata(file);
+ struct cx88_core *core = dev->core;
- mutex_lock(&core->lock);
- cx88_set_tvnorm(core, tvnorms);
- mutex_unlock(&core->lock);
-
- return 0;
+ return cx88_set_tvnorm(core, tvnorms);
}
/* only one input in this sample driver */
@@ -1233,13 +897,15 @@
static int vidioc_enum_input (struct file *file, void *priv,
struct v4l2_input *i)
{
- struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+ struct cx8800_dev *dev = video_drvdata(file);
+ struct cx88_core *core = dev->core;
return cx88_enum_input (core,i);
}
static int vidioc_g_input (struct file *file, void *priv, unsigned int *i)
{
- struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+ struct cx8800_dev *dev = video_drvdata(file);
+ struct cx88_core *core = dev->core;
*i = core->input;
return 0;
@@ -1247,24 +913,24 @@
static int vidioc_s_input (struct file *file, void *priv, unsigned int i)
{
- struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+ struct cx8800_dev *dev = video_drvdata(file);
+ struct cx88_core *core = dev->core;
if (i >= 4)
return -EINVAL;
if (0 == INPUT(i).type)
return -EINVAL;
- mutex_lock(&core->lock);
cx88_newstation(core);
cx88_video_mux(core,i);
- mutex_unlock(&core->lock);
return 0;
}
static int vidioc_g_tuner (struct file *file, void *priv,
struct v4l2_tuner *t)
{
- struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+ struct cx8800_dev *dev = video_drvdata(file);
+ struct cx88_core *core = dev->core;
u32 reg;
if (unlikely(UNSET == core->board.tuner_type))
@@ -1286,7 +952,8 @@
static int vidioc_s_tuner (struct file *file, void *priv,
const struct v4l2_tuner *t)
{
- struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+ struct cx8800_dev *dev = video_drvdata(file);
+ struct cx88_core *core = dev->core;
if (UNSET == core->board.tuner_type)
return -EINVAL;
@@ -1300,8 +967,8 @@
static int vidioc_g_frequency (struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct cx8800_fh *fh = priv;
- struct cx88_core *core = fh->dev->core;
+ struct cx8800_dev *dev = video_drvdata(file);
+ struct cx88_core *core = dev->core;
if (unlikely(UNSET == core->board.tuner_type))
return -EINVAL;
@@ -1325,7 +992,6 @@
if (unlikely(f->tuner != 0))
return -EINVAL;
- mutex_lock(&core->lock);
cx88_newstation(core);
call_all(core, tuner, s_frequency, f);
call_all(core, tuner, g_frequency, &new_freq);
@@ -1335,8 +1001,6 @@
msleep (10);
cx88_set_tvaudio(core);
- mutex_unlock(&core->lock);
-
return 0;
}
EXPORT_SYMBOL(cx88_set_freq);
@@ -1344,8 +1008,8 @@
static int vidioc_s_frequency (struct file *file, void *priv,
const struct v4l2_frequency *f)
{
- struct cx8800_fh *fh = priv;
- struct cx88_core *core = fh->dev->core;
+ struct cx8800_dev *dev = video_drvdata(file);
+ struct cx88_core *core = dev->core;
return cx88_set_freq(core, f);
}
@@ -1354,7 +1018,8 @@
static int vidioc_g_register (struct file *file, void *fh,
struct v4l2_dbg_register *reg)
{
- struct cx88_core *core = ((struct cx8800_fh*)fh)->dev->core;
+ struct cx8800_dev *dev = video_drvdata(file);
+ struct cx88_core *core = dev->core;
/* cx2388x has a 24-bit register space */
reg->val = cx_read(reg->reg & 0xfffffc);
@@ -1365,7 +1030,8 @@
static int vidioc_s_register (struct file *file, void *fh,
const struct v4l2_dbg_register *reg)
{
- struct cx88_core *core = ((struct cx8800_fh*)fh)->dev->core;
+ struct cx8800_dev *dev = video_drvdata(file);
+ struct cx88_core *core = dev->core;
cx_write(reg->reg & 0xfffffc, reg->val);
return 0;
@@ -1379,7 +1045,8 @@
static int radio_g_tuner (struct file *file, void *priv,
struct v4l2_tuner *t)
{
- struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+ struct cx8800_dev *dev = video_drvdata(file);
+ struct cx88_core *core = dev->core;
if (unlikely(t->index > 0))
return -EINVAL;
@@ -1393,7 +1060,8 @@
static int radio_s_tuner (struct file *file, void *priv,
const struct v4l2_tuner *t)
{
- struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+ struct cx8800_dev *dev = video_drvdata(file);
+ struct cx88_core *core = dev->core;
if (0 != t->index)
return -EINVAL;
@@ -1404,32 +1072,6 @@
/* ----------------------------------------------------------- */
-static void cx8800_vid_timeout(unsigned long data)
-{
- struct cx8800_dev *dev = (struct cx8800_dev*)data;
- struct cx88_core *core = dev->core;
- struct cx88_dmaqueue *q = &dev->vidq;
- struct cx88_buffer *buf;
- unsigned long flags;
-
- cx88_sram_channel_dump(core, &cx88_sram_channels[SRAM_CH21]);
-
- cx_clear(MO_VID_DMACNTRL, 0x11);
- cx_clear(VID_CAPTURE_CONTROL, 0x06);
-
- spin_lock_irqsave(&dev->slock,flags);
- while (!list_empty(&q->active)) {
- buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
- list_del(&buf->vb.queue);
- buf->vb.state = VIDEOBUF_ERROR;
- wake_up(&buf->vb.done);
- printk("%s/0: [%p/%d] timeout - dma=0x%08lx\n", core->name,
- buf, buf->vb.i, (unsigned long)buf->risc.dma);
- }
- restart_video_queue(dev,q);
- spin_unlock_irqrestore(&dev->slock,flags);
-}
-
static const char *cx88_vid_irqs[32] = {
"y_risci1", "u_risci1", "v_risci1", "vbi_risc1",
"y_risci2", "u_risci2", "v_risci2", "vbi_risc2",
@@ -1476,22 +1118,6 @@
cx88_wakeup(core, &dev->vbiq, count);
spin_unlock(&dev->slock);
}
-
- /* risc2 y */
- if (status & 0x10) {
- dprintk(2,"stopper video\n");
- spin_lock(&dev->slock);
- restart_video_queue(dev,&dev->vidq);
- spin_unlock(&dev->slock);
- }
-
- /* risc2 vbi */
- if (status & 0x80) {
- dprintk(2,"stopper vbi\n");
- spin_lock(&dev->slock);
- cx8800_restart_vbi_queue(dev,&dev->vbiq);
- spin_unlock(&dev->slock);
- }
}
static irqreturn_t cx8800_irq(int irq, void *dev_id)
@@ -1530,11 +1156,11 @@
static const struct v4l2_file_operations video_fops =
{
.owner = THIS_MODULE,
- .open = video_open,
- .release = video_release,
- .read = video_read,
- .poll = video_poll,
- .mmap = video_mmap,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .read = vb2_fop_read,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
.unlocked_ioctl = video_ioctl2,
};
@@ -1544,17 +1170,17 @@
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
.vidioc_g_std = vidioc_g_std,
.vidioc_s_std = vidioc_s_std,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
.vidioc_g_tuner = vidioc_g_tuner,
.vidioc_s_tuner = vidioc_s_tuner,
.vidioc_g_frequency = vidioc_g_frequency,
@@ -1579,17 +1205,17 @@
.vidioc_g_fmt_vbi_cap = cx8800_vbi_fmt,
.vidioc_try_fmt_vbi_cap = cx8800_vbi_fmt,
.vidioc_s_fmt_vbi_cap = cx8800_vbi_fmt,
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
.vidioc_g_std = vidioc_g_std,
.vidioc_s_std = vidioc_s_std,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
.vidioc_g_tuner = vidioc_g_tuner,
.vidioc_s_tuner = vidioc_s_tuner,
.vidioc_g_frequency = vidioc_g_frequency,
@@ -1610,9 +1236,9 @@
static const struct v4l2_file_operations radio_fops =
{
.owner = THIS_MODULE,
- .open = video_open,
+ .open = radio_open,
.poll = v4l2_ctrl_poll,
- .release = video_release,
+ .release = v4l2_fh_release,
.unlocked_ioctl = video_ioctl2,
};
@@ -1676,6 +1302,7 @@
{
struct cx8800_dev *dev;
struct cx88_core *core;
+ struct vb2_queue *q;
int err;
int i;
@@ -1710,28 +1337,21 @@
err = -EIO;
goto fail_core;
}
+ dev->alloc_ctx = vb2_dma_sg_init_ctx(&pci_dev->dev);
+ if (IS_ERR(dev->alloc_ctx)) {
+ err = PTR_ERR(dev->alloc_ctx);
+ goto fail_core;
+ }
+
/* initialize driver struct */
spin_lock_init(&dev->slock);
- core->tvnorm = V4L2_STD_NTSC_M;
/* init video dma queues */
INIT_LIST_HEAD(&dev->vidq.active);
- INIT_LIST_HEAD(&dev->vidq.queued);
- dev->vidq.timeout.function = cx8800_vid_timeout;
- dev->vidq.timeout.data = (unsigned long)dev;
- init_timer(&dev->vidq.timeout);
- cx88_risc_stopper(dev->pci,&dev->vidq.stopper,
- MO_VID_DMACNTRL,0x11,0x00);
/* init vbi dma queues */
INIT_LIST_HEAD(&dev->vbiq.active);
- INIT_LIST_HEAD(&dev->vbiq.queued);
- dev->vbiq.timeout.function = cx8800_vbi_timeout;
- dev->vbiq.timeout.data = (unsigned long)dev;
- init_timer(&dev->vbiq.timeout);
- cx88_risc_stopper(dev->pci,&dev->vbiq.stopper,
- MO_VID_DMACNTRL,0x88,0x00);
/* get irq */
err = request_irq(pci_dev->irq, cx8800_irq,
@@ -1820,9 +1440,10 @@
/* Sets device info at pci_dev */
pci_set_drvdata(pci_dev, dev);
- dev->width = 320;
- dev->height = 240;
- dev->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
+ dev->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
+
+ /* Maintain a reference so cx88-blackbird can query the 8800 device. */
+ core->v4ldev = dev;
/* initial device configuration */
mutex_lock(&core->lock);
@@ -1831,11 +1452,44 @@
v4l2_ctrl_handler_setup(&core->audio_hdl);
cx88_video_mux(core, 0);
+ q = &dev->vb2_vidq;
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
+ q->gfp_flags = GFP_DMA32;
+ q->min_buffers_needed = 2;
+ q->drv_priv = dev;
+ q->buf_struct_size = sizeof(struct cx88_buffer);
+ q->ops = &cx8800_video_qops;
+ q->mem_ops = &vb2_dma_sg_memops;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &core->lock;
+
+ err = vb2_queue_init(q);
+ if (err < 0)
+ goto fail_unreg;
+
+ q = &dev->vb2_vbiq;
+ q->type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
+ q->gfp_flags = GFP_DMA32;
+ q->min_buffers_needed = 2;
+ q->drv_priv = dev;
+ q->buf_struct_size = sizeof(struct cx88_buffer);
+ q->ops = &cx8800_vbi_qops;
+ q->mem_ops = &vb2_dma_sg_memops;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &core->lock;
+
+ err = vb2_queue_init(q);
+ if (err < 0)
+ goto fail_unreg;
+
/* register v4l devices */
dev->video_dev = cx88_vdev_init(core,dev->pci,
&cx8800_video_template,"video");
video_set_drvdata(dev->video_dev, dev);
dev->video_dev->ctrl_handler = &core->video_hdl;
+ dev->video_dev->queue = &dev->vb2_vidq;
err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER,
video_nr[core->nr]);
if (err < 0) {
@@ -1848,6 +1502,7 @@
dev->vbi_dev = cx88_vdev_init(core,dev->pci,&cx8800_vbi_template,"vbi");
video_set_drvdata(dev->vbi_dev, dev);
+ dev->vbi_dev->queue = &dev->vb2_vbiq;
err = video_register_device(dev->vbi_dev,VFL_TYPE_VBI,
vbi_nr[core->nr]);
if (err < 0) {
@@ -1875,7 +1530,7 @@
}
/* start tvaudio thread */
- if (core->board.tuner_type != TUNER_ABSENT) {
+ if (core->board.tuner_type != UNSET) {
core->kthread = kthread_run(cx88_audio_thread, core, "cx88 tvaudio");
if (IS_ERR(core->kthread)) {
err = PTR_ERR(core->kthread);
@@ -1892,6 +1547,8 @@
free_irq(pci_dev->irq, dev);
mutex_unlock(&core->lock);
fail_core:
+ vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
+ core->v4ldev = NULL;
cx88_core_put(core,dev->pci);
fail_free:
kfree(dev);
@@ -1913,16 +1570,18 @@
cx88_ir_stop(core);
cx88_shutdown(core); /* FIXME */
- pci_disable_device(pci_dev);
/* unregister stuff */
free_irq(pci_dev->irq, dev);
cx8800_unregister_video(dev);
+ pci_disable_device(pci_dev);
+
+ core->v4ldev = NULL;
/* free memory */
- btcx_riscmem_free(dev->pci,&dev->vidq.stopper);
cx88_core_put(core,dev->pci);
+ vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
kfree(dev);
}
@@ -1938,12 +1597,10 @@
if (!list_empty(&dev->vidq.active)) {
printk("%s/0: suspend video\n", core->name);
stop_video_dma(dev);
- del_timer(&dev->vidq.timeout);
}
if (!list_empty(&dev->vbiq.active)) {
printk("%s/0: suspend vbi\n", core->name);
cx8800_stop_vbi_dma(dev);
- del_timer(&dev->vbiq.timeout);
}
spin_unlock_irqrestore(&dev->slock, flags);
diff --git a/drivers/media/pci/cx88/cx88.h b/drivers/media/pci/cx88/cx88.h
index d744348..f3eccd7 100644
--- a/drivers/media/pci/cx88/cx88.h
+++ b/drivers/media/pci/cx88/cx88.h
@@ -29,19 +29,18 @@
#include <media/v4l2-fh.h>
#include <media/tuner.h>
#include <media/tveeprom.h>
-#include <media/videobuf-dma-sg.h>
+#include <media/videobuf2-dma-sg.h>
#include <media/cx2341x.h>
-#include <media/videobuf-dvb.h>
+#include <media/videobuf2-dvb.h>
#include <media/ir-kbd-i2c.h>
#include <media/wm8775.h>
-#include "btcx-risc.h"
#include "cx88-reg.h"
#include "tuner-xc2028.h"
#include <linux/mutex.h>
-#define CX88_VERSION "0.0.9"
+#define CX88_VERSION "1.0.0"
#define UNSET (-1U)
@@ -62,7 +61,8 @@
#define FORMAT_FLAGS_PACKED 0x01
#define FORMAT_FLAGS_PLANAR 0x02
-#define VBI_LINE_COUNT 17
+#define VBI_LINE_PAL_COUNT 18
+#define VBI_LINE_NTSC_COUNT 12
#define VBI_LINE_LENGTH 2048
#define AUD_RDS_LINES 4
@@ -95,13 +95,13 @@
static inline unsigned int norm_maxw(v4l2_std_id norm)
{
- return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 720 : 768;
+ return 720;
}
static inline unsigned int norm_maxh(v4l2_std_id norm)
{
- return (norm & V4L2_STD_625_50) ? 576 : 480;
+ return (norm & V4L2_STD_525_60) ? 480 : 576;
}
/* ----------------------------------------------------------- */
@@ -311,26 +311,33 @@
#define BUFFER_TIMEOUT msecs_to_jiffies(2000)
+struct cx88_riscmem {
+ unsigned int size;
+ __le32 *cpu;
+ __le32 *jmp;
+ dma_addr_t dma;
+};
+
/* buffer for one video frame */
struct cx88_buffer {
/* common v4l buffer stuff -- must be first */
- struct videobuf_buffer vb;
+ struct vb2_buffer vb;
+ struct list_head list;
/* cx88 specific */
unsigned int bpl;
- struct btcx_riscmem risc;
- const struct cx8800_fmt *fmt;
+ struct cx88_riscmem risc;
u32 count;
};
struct cx88_dmaqueue {
struct list_head active;
- struct list_head queued;
- struct timer_list timeout;
- struct btcx_riscmem stopper;
u32 count;
};
+struct cx8800_dev;
+struct cx8802_dev;
+
struct cx88_core {
struct list_head devlist;
atomic_t refcount;
@@ -376,6 +383,8 @@
/* state info */
struct task_struct *kthread;
v4l2_std_id tvnorm;
+ unsigned width, height;
+ unsigned field;
enum cx88_tvaudio tvaudio;
u32 audiomode_manual;
u32 audiomode_current;
@@ -395,11 +404,14 @@
struct mutex lock;
/* various v4l controls */
u32 freq;
- int users;
- int mpeg_users;
- /* cx88-video needs to access cx8802 for hybrid tuner pll access. */
+ /*
+ * cx88-video needs to access cx8802 for hybrid tuner pll access and
+ * for vb2_is_busy() checks.
+ */
struct cx8802_dev *dvbdev;
+ /* cx88-blackbird needs to access cx8800 for vb2_is_busy() checks */
+ struct cx8800_dev *v4ldev;
enum cx88_board_type active_type_id;
int active_ref;
int active_fe_id;
@@ -453,24 +465,9 @@
val; \
})
-struct cx8800_dev;
-struct cx8802_dev;
-
/* ----------------------------------------------------------- */
/* function 0: video stuff */
-struct cx8800_fh {
- struct v4l2_fh fh;
- struct cx8800_dev *dev;
- unsigned int resources;
-
- /* video capture */
- struct videobuf_queue vidq;
-
- /* vbi capture */
- struct videobuf_queue vbiq;
-};
-
struct cx8800_suspend_state {
int disabled;
};
@@ -488,13 +485,15 @@
/* pci i/o */
struct pci_dev *pci;
unsigned char pci_rev,pci_lat;
+ void *alloc_ctx;
const struct cx8800_fmt *fmt;
- unsigned int width, height;
/* capture queues */
struct cx88_dmaqueue vidq;
+ struct vb2_queue vb2_vidq;
struct cx88_dmaqueue vbiq;
+ struct vb2_queue vb2_vbiq;
/* various v4l controls */
@@ -510,12 +509,6 @@
/* ----------------------------------------------------------- */
/* function 2: mpeg stuff */
-struct cx8802_fh {
- struct v4l2_fh fh;
- struct cx8802_dev *dev;
- struct videobuf_queue mpegq;
-};
-
struct cx8802_suspend_state {
int disabled;
};
@@ -556,9 +549,11 @@
/* pci i/o */
struct pci_dev *pci;
unsigned char pci_rev,pci_lat;
+ void *alloc_ctx;
/* dma queues */
struct cx88_dmaqueue mpegq;
+ struct vb2_queue vb2_mpegq;
u32 ts_packet_size;
u32 ts_packet_count;
@@ -570,9 +565,6 @@
#if IS_ENABLED(CPTCFG_VIDEO_CX88_BLACKBIRD)
struct video_device *mpeg_dev;
u32 mailbox;
- int width;
- int height;
- unsigned char mpeg_active; /* nonzero if mpeg encoder is active */
/* mpeg params */
struct cx2341x_handler cxhdl;
@@ -580,7 +572,7 @@
#if IS_ENABLED(CPTCFG_VIDEO_CX88_DVB)
/* for dvb only */
- struct videobuf_dvb_frontends frontends;
+ struct vb2_dvb_frontends frontends;
#endif
#if IS_ENABLED(CPTCFG_VIDEO_CX88_VP3054)
@@ -634,22 +626,17 @@
extern int cx88_reset(struct cx88_core *core);
extern int
-cx88_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+cx88_risc_buffer(struct pci_dev *pci, struct cx88_riscmem *risc,
struct scatterlist *sglist,
unsigned int top_offset, unsigned int bottom_offset,
unsigned int bpl, unsigned int padding, unsigned int lines);
extern int
-cx88_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+cx88_risc_databuffer(struct pci_dev *pci, struct cx88_riscmem *risc,
struct scatterlist *sglist, unsigned int bpl,
unsigned int lines, unsigned int lpi);
-extern int
-cx88_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
- u32 reg, u32 mask, u32 value);
-extern void
-cx88_free_buffer(struct videobuf_queue *q, struct cx88_buffer *buf);
extern void cx88_risc_disasm(struct cx88_core *core,
- struct btcx_riscmem *risc);
+ struct cx88_riscmem *risc);
extern int cx88_sram_channel_setup(struct cx88_core *core,
const struct sram_channel *ch,
unsigned int bpl, u32 risc);
@@ -664,7 +651,7 @@
struct pci_dev *pci,
const struct video_device *template_,
const char *type);
-extern struct cx88_core* cx88_core_get(struct pci_dev *pci);
+extern struct cx88_core *cx88_core_get(struct pci_dev *pci);
extern void cx88_core_put(struct cx88_core *core,
struct pci_dev *pci);
@@ -684,12 +671,10 @@
struct cx88_dmaqueue *q,
struct cx88_buffer *buf);
*/
-int cx8800_stop_vbi_dma(struct cx8800_dev *dev);
-int cx8800_restart_vbi_queue(struct cx8800_dev *dev,
- struct cx88_dmaqueue *q);
-void cx8800_vbi_timeout(unsigned long data);
+void cx8800_stop_vbi_dma(struct cx8800_dev *dev);
+int cx8800_restart_vbi_queue(struct cx8800_dev *dev, struct cx88_dmaqueue *q);
-extern const struct videobuf_queue_ops cx8800_vbi_qops;
+extern const struct vb2_ops cx8800_vbi_qops;
/* ----------------------------------------------------------- */
/* cx88-i2c.c */
@@ -739,14 +724,17 @@
/* ----------------------------------------------------------- */
/* cx88-mpeg.c */
-int cx8802_buf_prepare(struct videobuf_queue *q,struct cx8802_dev *dev,
- struct cx88_buffer *buf, enum v4l2_field field);
+int cx8802_buf_prepare(struct vb2_queue *q, struct cx8802_dev *dev,
+ struct cx88_buffer *buf);
void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf);
void cx8802_cancel_buffers(struct cx8802_dev *dev);
+int cx8802_start_dma(struct cx8802_dev *dev,
+ struct cx88_dmaqueue *q,
+ struct cx88_buffer *buf);
/* ----------------------------------------------------------- */
/* cx88-video.c*/
-int cx88_enum_input (struct cx88_core *core,struct v4l2_input *i);
+int cx88_enum_input(struct cx88_core *core, struct v4l2_input *i);
int cx88_set_freq(struct cx88_core *core, const struct v4l2_frequency *f);
int cx88_video_mux(struct cx88_core *core, unsigned int input);
void cx88_querycap(struct file *file, struct cx88_core *core,
diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c
index ad8dfdd..9c00ff1 100644
--- a/drivers/media/pci/ddbridge/ddbridge-core.c
+++ b/drivers/media/pci/ddbridge/ddbridge-core.c
@@ -1118,8 +1118,7 @@
dvb_input_detach(port->input[1]);
break;
case DDB_PORT_CI:
- if (port->output->dev)
- dvb_unregister_device(port->output->dev);
+ dvb_unregister_device(port->output->dev);
if (port->en) {
ddb_input_stop(port->input[0]);
ddb_output_stop(port->output);
diff --git a/drivers/media/pci/ivtv/ivtv-controls.c b/drivers/media/pci/ivtv/ivtv-controls.c
index 2b0ab26..ccf548c 100644
--- a/drivers/media/pci/ivtv/ivtv-controls.c
+++ b/drivers/media/pci/ivtv/ivtv-controls.c
@@ -69,7 +69,7 @@
/* fix videodecoder resolution */
fmt.width = cxhdl->width / (is_mpeg1 ? 2 : 1);
fmt.height = cxhdl->height;
- fmt.code = V4L2_MBUS_FMT_FIXED;
+ fmt.code = MEDIA_BUS_FMT_FIXED;
v4l2_subdev_call(itv->sd_video, video, s_mbus_fmt, &fmt);
return 0;
}
diff --git a/drivers/media/pci/ivtv/ivtv-ioctl.c b/drivers/media/pci/ivtv/ivtv-ioctl.c
index b9744b5..9c5f710 100644
--- a/drivers/media/pci/ivtv/ivtv-ioctl.c
+++ b/drivers/media/pci/ivtv/ivtv-ioctl.c
@@ -595,7 +595,7 @@
fmt->fmt.pix.width /= 2;
mbus_fmt.width = fmt->fmt.pix.width;
mbus_fmt.height = h;
- mbus_fmt.code = V4L2_MBUS_FMT_FIXED;
+ mbus_fmt.code = MEDIA_BUS_FMT_FIXED;
v4l2_subdev_call(itv->sd_video, video, s_mbus_fmt, &mbus_fmt);
return ivtv_g_fmt_vid_cap(file, fh, fmt);
}
diff --git a/drivers/media/pci/ivtv/ivtv-udma.c b/drivers/media/pci/ivtv/ivtv-udma.c
index 7338cb2..bee2329 100644
--- a/drivers/media/pci/ivtv/ivtv-udma.c
+++ b/drivers/media/pci/ivtv/ivtv-udma.c
@@ -76,7 +76,7 @@
int i;
struct scatterlist *sg;
- for (i = 0, sg = dma->SGlist; i < dma->SG_length; i++, sg++) {
+ for (i = 0, sg = dma->SGlist; i < dma->SG_length; i++, sg = sg_next(sg)) {
dma->SGarray[i].size = cpu_to_le32(sg_dma_len(sg));
dma->SGarray[i].src = cpu_to_le32(sg_dma_address(sg));
dma->SGarray[i].dst = cpu_to_le32(buffer_offset);
diff --git a/drivers/media/pci/meye/meye.c b/drivers/media/pci/meye/meye.c
index aeae547..9d9f90c 100644
--- a/drivers/media/pci/meye/meye.c
+++ b/drivers/media/pci/meye/meye.c
@@ -1031,9 +1031,6 @@
strcpy(cap->card, "meye");
sprintf(cap->bus_info, "PCI:%s", pci_name(meye.mchip_dev));
- cap->version = (MEYE_DRIVER_MAJORVERSION << 8) +
- MEYE_DRIVER_MINORVERSION;
-
cap->device_caps = V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_STREAMING;
cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
diff --git a/drivers/media/pci/pt1/pt1.c b/drivers/media/pci/pt1/pt1.c
index db887b0..acc35b4 100644
--- a/drivers/media/pci/pt1/pt1.c
+++ b/drivers/media/pci/pt1/pt1.c
@@ -109,9 +109,6 @@
int sleep;
};
-#define pt1_printk(level, pt1, format, arg...) \
- dev_printk(level, &(pt1)->pdev->dev, format, ##arg)
-
static void pt1_write_reg(struct pt1 *pt1, int reg, u32 data)
{
writel(data, pt1->regs + reg * 4);
@@ -154,7 +151,7 @@
return 0;
pt1_write_reg(pt1, 0, 0x00000008);
}
- pt1_printk(KERN_ERR, pt1, "could not sync\n");
+ dev_err(&pt1->pdev->dev, "could not sync\n");
return -EIO;
}
@@ -179,7 +176,7 @@
return 0;
schedule_timeout_uninterruptible((HZ + 999) / 1000);
}
- pt1_printk(KERN_ERR, pt1, "could not unlock\n");
+ dev_err(&pt1->pdev->dev, "could not unlock\n");
return -EIO;
}
@@ -193,7 +190,7 @@
return 0;
schedule_timeout_uninterruptible((HZ + 999) / 1000);
}
- pt1_printk(KERN_ERR, pt1, "could not reset PCI\n");
+ dev_err(&pt1->pdev->dev, "could not reset PCI\n");
return -EIO;
}
@@ -207,7 +204,7 @@
return 0;
schedule_timeout_uninterruptible((HZ + 999) / 1000);
}
- pt1_printk(KERN_ERR, pt1, "could not reset RAM\n");
+ dev_err(&pt1->pdev->dev, "could not reset RAM\n");
return -EIO;
}
@@ -224,7 +221,7 @@
}
schedule_timeout_uninterruptible((HZ + 999) / 1000);
}
- pt1_printk(KERN_ERR, pt1, "could not enable RAM\n");
+ dev_err(&pt1->pdev->dev, "could not enable RAM\n");
return -EIO;
}
diff --git a/drivers/media/pci/pt3/Kconfig b/drivers/media/pci/pt3/Kconfig
index f62f306..6a1671a 100644
--- a/drivers/media/pci/pt3/Kconfig
+++ b/drivers/media/pci/pt3/Kconfig
@@ -1,5 +1,5 @@
config DVB_PT3
- depends on !BACKPORT_KERNEL_3_3
+ depends on !KERNEL_3_3
tristate "Earthsoft PT3 cards"
depends on m
depends on DVB_CORE && PCI && I2C
diff --git a/drivers/media/pci/pt3/pt3.c b/drivers/media/pci/pt3/pt3.c
index 1fdeac1..7a37e8f 100644
--- a/drivers/media/pci/pt3/pt3.c
+++ b/drivers/media/pci/pt3/pt3.c
@@ -255,7 +255,7 @@
pt3_i2c_reset(pt3);
ret = pt3_init_all_demods(pt3);
if (ret < 0) {
- dev_warn(&pt3->pdev->dev, "Failed to init demod chips.");
+ dev_warn(&pt3->pdev->dev, "Failed to init demod chips\n");
return ret;
}
@@ -271,7 +271,7 @@
init0_ter, ARRAY_SIZE(init0_ter));
if (ret < 0) {
dev_warn(&pt3->pdev->dev,
- "demod[%d] faild in init sequence0.", i);
+ "demod[%d] failed in init sequence0\n", i);
return ret;
}
ret = fe->ops.init(fe);
@@ -282,7 +282,7 @@
usleep_range(2000, 4000);
ret = pt3_set_tuner_power(pt3, true, false);
if (ret < 0) {
- dev_warn(&pt3->pdev->dev, "Failed to control tuner module.");
+ dev_warn(&pt3->pdev->dev, "Failed to control tuner module\n");
return ret;
}
@@ -297,7 +297,7 @@
cfg_ter, ARRAY_SIZE(cfg_ter));
if (ret < 0) {
dev_warn(&pt3->pdev->dev,
- "demod[%d] faild in init sequence1.", i);
+ "demod[%d] failed in init sequence1\n", i);
return ret;
}
}
@@ -311,19 +311,19 @@
ret = fe->ops.tuner_ops.init(fe);
if (ret < 0) {
dev_warn(&pt3->pdev->dev,
- "Failed to init SAT-tuner[%d].", i);
+ "Failed to init SAT-tuner[%d]\n", i);
return ret;
}
}
ret = pt3_init_all_mxl301rf(pt3);
if (ret < 0) {
- dev_warn(&pt3->pdev->dev, "Failed to init TERR-tuners.");
+ dev_warn(&pt3->pdev->dev, "Failed to init TERR-tuners\n");
return ret;
}
ret = pt3_set_tuner_power(pt3, true, true);
if (ret < 0) {
- dev_warn(&pt3->pdev->dev, "Failed to control tuner module.");
+ dev_warn(&pt3->pdev->dev, "Failed to control tuner module\n");
return ret;
}
@@ -344,7 +344,7 @@
}
if (ret < 0) {
dev_warn(&pt3->pdev->dev,
- "Failed in initial tuning of tuner[%d].", i);
+ "Failed in initial tuning of tuner[%d]\n", i);
return ret;
}
}
@@ -366,7 +366,7 @@
fe->ops.set_lna = &pt3_set_lna;
}
if (i < PT3_NUM_FE) {
- dev_warn(&pt3->pdev->dev, "FE[%d] failed to standby.", i);
+ dev_warn(&pt3->pdev->dev, "FE[%d] failed to standby\n", i);
return ret;
}
return 0;
@@ -453,8 +453,8 @@
pt3_init_dmabuf(adap);
adap->num_discard = PT3_INITIAL_BUF_DROPS;
- dev_dbg(adap->dvb_adap.device,
- "PT3: [%s] started.\n", adap->thread->comm);
+ dev_dbg(adap->dvb_adap.device, "PT3: [%s] started\n",
+ adap->thread->comm);
set_freezable();
while (!kthread_freezable_should_stop(&was_frozen)) {
if (was_frozen)
@@ -468,8 +468,8 @@
PT3_FETCH_DELAY_DELTA * NSEC_PER_MSEC,
HRTIMER_MODE_REL);
}
- dev_dbg(adap->dvb_adap.device,
- "PT3: [%s] exited.\n", adap->thread->comm);
+ dev_dbg(adap->dvb_adap.device, "PT3: [%s] exited\n",
+ adap->thread->comm);
adap->thread = NULL;
return 0;
}
@@ -485,8 +485,8 @@
int ret = PTR_ERR(thread);
dev_warn(adap->dvb_adap.device,
- "PT3 (adap:%d, dmx:%d): failed to start kthread.\n",
- adap->dvb_adap.num, adap->dmxdev.dvbdev->id);
+ "PT3 (adap:%d, dmx:%d): failed to start kthread\n",
+ adap->dvb_adap.num, adap->dmxdev.dvbdev->id);
return ret;
}
adap->thread = thread;
@@ -501,8 +501,8 @@
ret = pt3_stop_dma(adap);
if (ret)
dev_warn(adap->dvb_adap.device,
- "PT3: failed to stop streaming of adap:%d/FE:%d\n",
- adap->dvb_adap.num, adap->fe->id);
+ "PT3: failed to stop streaming of adap:%d/FE:%d\n",
+ adap->dvb_adap.num, adap->fe->id);
/* kill the fetching thread */
ret = kthread_stop(adap->thread);
@@ -522,8 +522,8 @@
return 0;
if (adap->num_feeds != 1) {
dev_warn(adap->dvb_adap.device,
- "%s: unmatched start/stop_feed in adap:%i/dmx:%i.\n",
- __func__, adap->dvb_adap.num, adap->dmxdev.dvbdev->id);
+ "%s: unmatched start/stop_feed in adap:%i/dmx:%i\n",
+ __func__, adap->dvb_adap.num, adap->dmxdev.dvbdev->id);
adap->num_feeds = 1;
}
@@ -553,10 +553,9 @@
struct dvb_adapter *da;
adap = kzalloc(sizeof(*adap), GFP_KERNEL);
- if (!adap) {
- dev_err(&pt3->pdev->dev, "failed to alloc mem for adapter.\n");
+ if (!adap)
return -ENOMEM;
- }
+
pt3->adaps[index] = adap;
adap->adap_idx = index;
@@ -565,7 +564,7 @@
THIS_MODULE, &pt3->pdev->dev, adapter_nr);
if (ret < 0) {
dev_err(&pt3->pdev->dev,
- "failed to register adapter dev.\n");
+ "failed to register adapter dev\n");
goto err_mem;
}
da = &adap->dvb_adap;
@@ -581,7 +580,7 @@
adap->demux.stop_feed = pt3_stop_feed;
ret = dvb_dmx_init(&adap->demux);
if (ret < 0) {
- dev_err(&pt3->pdev->dev, "failed to init dmx dev.\n");
+ dev_err(&pt3->pdev->dev, "failed to init dmx dev\n");
goto err_adap;
}
@@ -589,13 +588,13 @@
adap->dmxdev.demux = &adap->demux.dmx;
ret = dvb_dmxdev_init(&adap->dmxdev, da);
if (ret < 0) {
- dev_err(&pt3->pdev->dev, "failed to init dmxdev.\n");
+ dev_err(&pt3->pdev->dev, "failed to init dmxdev\n");
goto err_demux;
}
ret = pt3_alloc_dmabuf(adap);
if (ret) {
- dev_err(&pt3->pdev->dev, "failed to alloc DMA buffers.\n");
+ dev_err(&pt3->pdev->dev, "failed to alloc DMA buffers\n");
goto err_dmabuf;
}
@@ -695,7 +694,7 @@
dvb_frontend_resume(adap->fe);
ret = pt3_alloc_dmabuf(adap);
if (ret) {
- dev_err(&pt3->pdev->dev, "failed to alloc DMA bufs.\n");
+ dev_err(&pt3->pdev->dev, "failed to alloc DMA bufs\n");
continue;
}
if (adap->num_feeds > 0)
@@ -753,15 +752,14 @@
if (ret == 0)
dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
else {
- dev_err(&pdev->dev, "Failed to set DMA mask.\n");
+ dev_err(&pdev->dev, "Failed to set DMA mask\n");
goto err_release_regions;
}
- dev_info(&pdev->dev, "Use 32bit DMA.\n");
+ dev_info(&pdev->dev, "Use 32bit DMA\n");
}
pt3 = kzalloc(sizeof(*pt3), GFP_KERNEL);
if (!pt3) {
- dev_err(&pdev->dev, "Failed to alloc mem for this dev.\n");
ret = -ENOMEM;
goto err_release_regions;
}
@@ -771,15 +769,15 @@
pt3->regs[0] = pci_ioremap_bar(pdev, 0);
pt3->regs[1] = pci_ioremap_bar(pdev, 2);
if (pt3->regs[0] == NULL || pt3->regs[1] == NULL) {
- dev_err(&pdev->dev, "Failed to ioremap.\n");
+ dev_err(&pdev->dev, "Failed to ioremap\n");
ret = -ENOMEM;
goto err_kfree;
}
ver = ioread32(pt3->regs[0] + REG_VERSION);
if ((ver >> 16) != 0x0301) {
- dev_warn(&pdev->dev, "PT%d, I/F-ver.:%d not supported",
- ver >> 24, (ver & 0x00ff0000) >> 16);
+ dev_warn(&pdev->dev, "PT%d, I/F-ver.:%d not supported\n",
+ ver >> 24, (ver & 0x00ff0000) >> 16);
ret = -ENODEV;
goto err_iounmap;
}
@@ -788,7 +786,6 @@
pt3->i2c_buf = kmalloc(sizeof(*pt3->i2c_buf), GFP_KERNEL);
if (pt3->i2c_buf == NULL) {
- dev_err(&pdev->dev, "Failed to alloc mem for i2c.\n");
ret = -ENOMEM;
goto err_iounmap;
}
@@ -801,7 +798,7 @@
i2c_set_adapdata(i2c, pt3);
ret = i2c_add_adapter(i2c);
if (ret < 0) {
- dev_err(&pdev->dev, "Failed to add i2c adapter.\n");
+ dev_err(&pdev->dev, "Failed to add i2c adapter\n");
goto err_i2cbuf;
}
@@ -815,20 +812,20 @@
break;
}
if (i < PT3_NUM_FE) {
- dev_err(&pdev->dev, "Failed to create FE%d.\n", i);
+ dev_err(&pdev->dev, "Failed to create FE%d\n", i);
goto err_cleanup_adapters;
}
ret = pt3_fe_init(pt3);
if (ret < 0) {
- dev_err(&pdev->dev, "Failed to init frontends.\n");
+ dev_err(&pdev->dev, "Failed to init frontends\n");
i = PT3_NUM_FE - 1;
goto err_cleanup_adapters;
}
dev_info(&pdev->dev,
- "successfully init'ed PT%d (fw:0x%02x, I/F:0x%02x).\n",
- ver >> 24, (ver >> 8) & 0xff, (ver >> 16) & 0xff);
+ "successfully init'ed PT%d (fw:0x%02x, I/F:0x%02x)\n",
+ ver >> 24, (ver >> 8) & 0xff, (ver >> 16) & 0xff);
return 0;
err_cleanup_adapters:
diff --git a/drivers/media/pci/saa7134/saa7134-alsa.c b/drivers/media/pci/saa7134/saa7134-alsa.c
index 4056989..ac3cd74 100644
--- a/drivers/media/pci/saa7134/saa7134-alsa.c
+++ b/drivers/media/pci/saa7134/saa7134-alsa.c
@@ -173,9 +173,7 @@
dprintk("irq: overrun [full=%d/%d] - Blocks in %d\n",dev->dmasound.read_count,
dev->dmasound.bufsize, dev->dmasound.blocks);
spin_unlock(&dev->slock);
- snd_pcm_stream_lock(dev->dmasound.substream);
- snd_pcm_stop(dev->dmasound.substream,SNDRV_PCM_STATE_XRUN);
- snd_pcm_stream_unlock(dev->dmasound.substream);
+ snd_pcm_stop_xrun(dev->dmasound.substream);
return;
}
diff --git a/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c
index 236ed72..a349e96 100644
--- a/drivers/media/pci/saa7134/saa7134-core.c
+++ b/drivers/media/pci/saa7134/saa7134-core.c
@@ -1001,13 +1001,18 @@
saa7134_board_init1(dev);
saa7134_hwinit1(dev);
+ dev->alloc_ctx = vb2_dma_sg_init_ctx(&pci_dev->dev);
+ if (IS_ERR(dev->alloc_ctx)) {
+ err = PTR_ERR(dev->alloc_ctx);
+ goto fail3;
+ }
/* get irq */
err = request_irq(pci_dev->irq, saa7134_irq,
IRQF_SHARED, dev->name, dev);
if (err < 0) {
printk(KERN_ERR "%s: can't get IRQ %d\n",
dev->name,pci_dev->irq);
- goto fail3;
+ goto fail4;
}
/* wait a bit, register i2c bus */
@@ -1065,7 +1070,7 @@
if (err < 0) {
printk(KERN_INFO "%s: can't register video device\n",
dev->name);
- goto fail4;
+ goto fail5;
}
printk(KERN_INFO "%s: registered device %s [v4l2]\n",
dev->name, video_device_node_name(dev->video_dev));
@@ -1078,7 +1083,7 @@
err = video_register_device(dev->vbi_dev,VFL_TYPE_VBI,
vbi_nr[dev->nr]);
if (err < 0)
- goto fail4;
+ goto fail5;
printk(KERN_INFO "%s: registered device %s\n",
dev->name, video_device_node_name(dev->vbi_dev));
@@ -1089,7 +1094,7 @@
err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO,
radio_nr[dev->nr]);
if (err < 0)
- goto fail4;
+ goto fail5;
printk(KERN_INFO "%s: registered device %s\n",
dev->name, video_device_node_name(dev->radio_dev));
}
@@ -1103,10 +1108,12 @@
request_submodules(dev);
return 0;
- fail4:
+ fail5:
saa7134_unregister_video(dev);
saa7134_i2c_unregister(dev);
free_irq(pci_dev->irq, dev);
+ fail4:
+ vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
fail3:
saa7134_hwfini(dev);
iounmap(dev->lmmio);
@@ -1173,6 +1180,7 @@
/* release resources */
free_irq(pci_dev->irq, dev);
+ vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
iounmap(dev->lmmio);
release_mem_region(pci_resource_start(pci_dev,0),
pci_resource_len(pci_dev,0));
diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c
index e4ea85f..594dc3a 100644
--- a/drivers/media/pci/saa7134/saa7134-empress.c
+++ b/drivers/media/pci/saa7134/saa7134-empress.c
@@ -96,7 +96,6 @@
.queue_setup = saa7134_ts_queue_setup,
.buf_init = saa7134_ts_buffer_init,
.buf_prepare = saa7134_ts_buffer_prepare,
- .buf_finish = saa7134_ts_buffer_finish,
.buf_queue = saa7134_vb2_buffer_queue,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
@@ -140,7 +139,7 @@
struct saa7134_dev *dev = video_drvdata(file);
struct v4l2_mbus_framefmt mbus_fmt;
- v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED);
+ v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, MEDIA_BUS_FMT_FIXED);
saa_call_all(dev, video, s_mbus_fmt, &mbus_fmt);
v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt);
@@ -157,7 +156,7 @@
struct saa7134_dev *dev = video_drvdata(file);
struct v4l2_mbus_framefmt mbus_fmt;
- v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED);
+ v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, MEDIA_BUS_FMT_FIXED);
saa_call_all(dev, video, try_mbus_fmt, &mbus_fmt);
v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt);
diff --git a/drivers/media/pci/saa7134/saa7134-ts.c b/drivers/media/pci/saa7134/saa7134-ts.c
index bd25323..2709b83 100644
--- a/drivers/media/pci/saa7134/saa7134-ts.c
+++ b/drivers/media/pci/saa7134/saa7134-ts.c
@@ -94,7 +94,6 @@
struct saa7134_buf *buf = container_of(vb2, struct saa7134_buf, vb2);
struct sg_table *dma = vb2_dma_sg_plane_desc(vb2, 0);
unsigned int lines, llength, size;
- int ret;
dprintk("buffer_prepare [%p]\n", buf);
@@ -108,25 +107,11 @@
vb2_set_plane_payload(vb2, 0, size);
vb2->v4l2_buf.field = dev->field;
- ret = dma_map_sg(&dev->pci->dev, dma->sgl, dma->nents, DMA_FROM_DEVICE);
- if (!ret)
- return -EIO;
return saa7134_pgtable_build(dev->pci, &dmaq->pt, dma->sgl, dma->nents,
saa7134_buffer_startpage(buf));
}
EXPORT_SYMBOL_GPL(saa7134_ts_buffer_prepare);
-void saa7134_ts_buffer_finish(struct vb2_buffer *vb2)
-{
- struct saa7134_dmaqueue *dmaq = vb2->vb2_queue->drv_priv;
- struct saa7134_dev *dev = dmaq->dev;
- struct saa7134_buf *buf = container_of(vb2, struct saa7134_buf, vb2);
- struct sg_table *dma = vb2_dma_sg_plane_desc(&buf->vb2, 0);
-
- dma_unmap_sg(&dev->pci->dev, dma->sgl, dma->nents, DMA_FROM_DEVICE);
-}
-EXPORT_SYMBOL_GPL(saa7134_ts_buffer_finish);
-
int saa7134_ts_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
@@ -142,6 +127,7 @@
*nbuffers = 3;
*nplanes = 1;
sizes[0] = size;
+ alloc_ctxs[0] = dev->alloc_ctx;
return 0;
}
EXPORT_SYMBOL_GPL(saa7134_ts_queue_setup);
@@ -187,7 +173,6 @@
.queue_setup = saa7134_ts_queue_setup,
.buf_init = saa7134_ts_buffer_init,
.buf_prepare = saa7134_ts_buffer_prepare,
- .buf_finish = saa7134_ts_buffer_finish,
.buf_queue = saa7134_vb2_buffer_queue,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
diff --git a/drivers/media/pci/saa7134/saa7134-vbi.c b/drivers/media/pci/saa7134/saa7134-vbi.c
index 4f0b101..5306e54 100644
--- a/drivers/media/pci/saa7134/saa7134-vbi.c
+++ b/drivers/media/pci/saa7134/saa7134-vbi.c
@@ -120,7 +120,6 @@
struct saa7134_buf *buf = container_of(vb2, struct saa7134_buf, vb2);
struct sg_table *dma = vb2_dma_sg_plane_desc(&buf->vb2, 0);
unsigned int size;
- int ret;
if (dma->sgl->offset) {
pr_err("The buffer is not page-aligned\n");
@@ -132,9 +131,6 @@
vb2_set_plane_payload(vb2, 0, size);
- ret = dma_map_sg(&dev->pci->dev, dma->sgl, dma->nents, DMA_FROM_DEVICE);
- if (!ret)
- return -EIO;
return saa7134_pgtable_build(dev->pci, &dmaq->pt, dma->sgl, dma->nents,
saa7134_buffer_startpage(buf));
}
@@ -156,6 +152,7 @@
*nbuffers = saa7134_buffer_count(size, *nbuffers);
*nplanes = 1;
sizes[0] = size;
+ alloc_ctxs[0] = dev->alloc_ctx;
return 0;
}
@@ -169,21 +166,10 @@
return 0;
}
-static void buffer_finish(struct vb2_buffer *vb2)
-{
- struct saa7134_dmaqueue *dmaq = vb2->vb2_queue->drv_priv;
- struct saa7134_dev *dev = dmaq->dev;
- struct saa7134_buf *buf = container_of(vb2, struct saa7134_buf, vb2);
- struct sg_table *dma = vb2_dma_sg_plane_desc(&buf->vb2, 0);
-
- dma_unmap_sg(&dev->pci->dev, dma->sgl, dma->nents, DMA_FROM_DEVICE);
-}
-
struct vb2_ops saa7134_vbi_qops = {
.queue_setup = queue_setup,
.buf_init = buffer_init,
.buf_prepare = buffer_prepare,
- .buf_finish = buffer_finish,
.buf_queue = saa7134_vb2_buffer_queue,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c
index b0f279c..87ca35a 100644
--- a/drivers/media/pci/saa7134/saa7134-video.c
+++ b/drivers/media/pci/saa7134/saa7134-video.c
@@ -883,7 +883,6 @@
struct saa7134_buf *buf = container_of(vb2, struct saa7134_buf, vb2);
struct sg_table *dma = vb2_dma_sg_plane_desc(&buf->vb2, 0);
unsigned int size;
- int ret;
if (dma->sgl->offset) {
pr_err("The buffer is not page-aligned\n");
@@ -896,23 +895,10 @@
vb2_set_plane_payload(vb2, 0, size);
vb2->v4l2_buf.field = dev->field;
- ret = dma_map_sg(&dev->pci->dev, dma->sgl, dma->nents, DMA_FROM_DEVICE);
- if (!ret)
- return -EIO;
return saa7134_pgtable_build(dev->pci, &dmaq->pt, dma->sgl, dma->nents,
saa7134_buffer_startpage(buf));
}
-static void buffer_finish(struct vb2_buffer *vb2)
-{
- struct saa7134_dmaqueue *dmaq = vb2->vb2_queue->drv_priv;
- struct saa7134_dev *dev = dmaq->dev;
- struct saa7134_buf *buf = container_of(vb2, struct saa7134_buf, vb2);
- struct sg_table *dma = vb2_dma_sg_plane_desc(&buf->vb2, 0);
-
- dma_unmap_sg(&dev->pci->dev, dma->sgl, dma->nents, DMA_FROM_DEVICE);
-}
-
static int queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
@@ -932,6 +918,7 @@
*nbuffers = saa7134_buffer_count(size, *nbuffers);
*nplanes = 1;
sizes[0] = size;
+ alloc_ctxs[0] = dev->alloc_ctx;
return 0;
}
@@ -1004,7 +991,6 @@
.queue_setup = queue_setup,
.buf_init = buffer_init,
.buf_prepare = buffer_prepare,
- .buf_finish = buffer_finish,
.buf_queue = saa7134_vb2_buffer_queue,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h
index 5e9cc30..d244ab1 100644
--- a/drivers/media/pci/saa7134/saa7134.h
+++ b/drivers/media/pci/saa7134/saa7134.h
@@ -588,6 +588,7 @@
/* video+ts+vbi capture */
+ void *alloc_ctx;
struct saa7134_dmaqueue video_q;
struct vb2_queue video_vbq;
struct saa7134_dmaqueue vbi_q;
@@ -814,7 +815,6 @@
int saa7134_ts_buffer_init(struct vb2_buffer *vb2);
int saa7134_ts_buffer_prepare(struct vb2_buffer *vb2);
-void saa7134_ts_buffer_finish(struct vb2_buffer *vb2);
int saa7134_ts_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[]);
diff --git a/drivers/media/pci/saa7164/saa7164-buffer.c b/drivers/media/pci/saa7164/saa7164-buffer.c
index 66696fa..9bd1f73 100644
--- a/drivers/media/pci/saa7164/saa7164-buffer.c
+++ b/drivers/media/pci/saa7164/saa7164-buffer.c
@@ -130,9 +130,9 @@
goto fail2;
/* init the buffers to a known pattern, easier during debugging */
- memset_io(buf->cpu, 0xff, buf->pci_size);
+ memset(buf->cpu, 0xff, buf->pci_size);
buf->crc = crc32(0, buf->cpu, buf->actual_size);
- memset_io(buf->pt_cpu, 0xff, buf->pt_size);
+ memset(buf->pt_cpu, 0xff, buf->pt_size);
dprintk(DBGLVL_BUF, "%s() allocated buffer @ 0x%p (%d pageptrs)\n",
__func__, buf, params->numpagetables);
diff --git a/drivers/media/pci/saa7164/saa7164-bus.c b/drivers/media/pci/saa7164/saa7164-bus.c
index 5f6f309..6c73f5b 100644
--- a/drivers/media/pci/saa7164/saa7164-bus.c
+++ b/drivers/media/pci/saa7164/saa7164-bus.c
@@ -33,12 +33,12 @@
b->Type = TYPE_BUS_PCIe;
b->m_wMaxReqSize = SAA_DEVICE_MAXREQUESTSIZE;
- b->m_pdwSetRing = (u8 *)(dev->bmmio +
+ b->m_pdwSetRing = (u8 __iomem *)(dev->bmmio +
((u32)dev->busdesc.CommandRing));
b->m_dwSizeSetRing = SAA_DEVICE_BUFFERBLOCKSIZE;
- b->m_pdwGetRing = (u8 *)(dev->bmmio +
+ b->m_pdwGetRing = (u8 __iomem *)(dev->bmmio +
((u32)dev->busdesc.ResponseRing));
b->m_dwSizeGetRing = SAA_DEVICE_BUFFERBLOCKSIZE;
@@ -138,6 +138,7 @@
u32 bytes_to_write, free_write_space, timeout, curr_srp, curr_swp;
u32 new_swp, space_rem;
int ret = SAA_ERR_BAD_PARAMETER;
+ u16 size;
if (!msg) {
printk(KERN_ERR "%s() !msg\n", __func__);
@@ -148,10 +149,6 @@
saa7164_bus_verify(dev);
- msg->size = cpu_to_le16(msg->size);
- msg->command = cpu_to_le32(msg->command);
- msg->controlselector = cpu_to_le16(msg->controlselector);
-
if (msg->size > dev->bus.m_wMaxReqSize) {
printk(KERN_ERR "%s() Exceeded dev->bus.m_wMaxReqSize\n",
__func__);
@@ -169,8 +166,8 @@
bytes_to_write = sizeof(*msg) + msg->size;
free_write_space = 0;
timeout = SAA_BUS_TIMEOUT;
- curr_srp = le32_to_cpu(saa7164_readl(bus->m_dwSetReadPos));
- curr_swp = le32_to_cpu(saa7164_readl(bus->m_dwSetWritePos));
+ curr_srp = saa7164_readl(bus->m_dwSetReadPos);
+ curr_swp = saa7164_readl(bus->m_dwSetWritePos);
/* Deal with ring wrapping issues */
if (curr_srp > curr_swp)
@@ -203,7 +200,7 @@
mdelay(1);
/* Check the space usage again */
- curr_srp = le32_to_cpu(saa7164_readl(bus->m_dwSetReadPos));
+ curr_srp = saa7164_readl(bus->m_dwSetReadPos);
/* Deal with ring wrapping issues */
if (curr_srp > curr_swp)
@@ -223,6 +220,16 @@
dprintk(DBGLVL_BUS, "%s() bus->m_dwSizeSetRing = %x\n", __func__,
bus->m_dwSizeSetRing);
+ /*
+ * Make a copy of msg->size before it is converted to le16 since it is
+ * used in the code below.
+ */
+ size = msg->size;
+ /* Convert to le16/le32 */
+ msg->size = (__force u16)cpu_to_le16(msg->size);
+ msg->command = (__force u32)cpu_to_le32(msg->command);
+ msg->controlselector = (__force u16)cpu_to_le16(msg->controlselector);
+
/* Mental Note: line 462 tmmhComResBusPCIe.cpp */
/* Check if we're going to wrap again */
@@ -243,28 +250,28 @@
dprintk(DBGLVL_BUS, "%s() tr4\n", __func__);
/* Split the msg into pieces as the ring wraps */
- memcpy(bus->m_pdwSetRing + curr_swp, msg, space_rem);
- memcpy(bus->m_pdwSetRing, (u8 *)msg + space_rem,
+ memcpy_toio(bus->m_pdwSetRing + curr_swp, msg, space_rem);
+ memcpy_toio(bus->m_pdwSetRing, (u8 *)msg + space_rem,
sizeof(*msg) - space_rem);
- memcpy(bus->m_pdwSetRing + sizeof(*msg) - space_rem,
- buf, msg->size);
+ memcpy_toio(bus->m_pdwSetRing + sizeof(*msg) - space_rem,
+ buf, size);
} else if (space_rem == sizeof(*msg)) {
dprintk(DBGLVL_BUS, "%s() tr5\n", __func__);
/* Additional data at the beginning of the ring */
- memcpy(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg));
- memcpy(bus->m_pdwSetRing, buf, msg->size);
+ memcpy_toio(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg));
+ memcpy_toio(bus->m_pdwSetRing, buf, size);
} else {
/* Additional data wraps around the ring */
- memcpy(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg));
- if (msg->size > 0) {
- memcpy(bus->m_pdwSetRing + curr_swp +
+ memcpy_toio(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg));
+ if (size > 0) {
+ memcpy_toio(bus->m_pdwSetRing + curr_swp +
sizeof(*msg), buf, space_rem -
sizeof(*msg));
- memcpy(bus->m_pdwSetRing, (u8 *)buf +
+ memcpy_toio(bus->m_pdwSetRing, (u8 *)buf +
space_rem - sizeof(*msg),
bytes_to_write - space_rem);
}
@@ -276,15 +283,20 @@
dprintk(DBGLVL_BUS, "%s() tr6\n", __func__);
/* The ring buffer doesn't wrap, two simple copies */
- memcpy(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg));
- memcpy(bus->m_pdwSetRing + curr_swp + sizeof(*msg), buf,
- msg->size);
+ memcpy_toio(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg));
+ memcpy_toio(bus->m_pdwSetRing + curr_swp + sizeof(*msg), buf,
+ size);
}
dprintk(DBGLVL_BUS, "%s() new_swp = %x\n", __func__, new_swp);
/* Update the bus write position */
- saa7164_writel(bus->m_dwSetWritePos, cpu_to_le32(new_swp));
+ saa7164_writel(bus->m_dwSetWritePos, new_swp);
+
+ /* Convert back to cpu after writing the msg to the ringbuffer. */
+ msg->size = le16_to_cpu((__force __le16)msg->size);
+ msg->command = le32_to_cpu((__force __le32)msg->command);
+ msg->controlselector = le16_to_cpu((__force __le16)msg->controlselector);
ret = SAA_OK;
out:
@@ -336,8 +348,8 @@
/* Peek the bus to see if a msg exists, if it's not what we're expecting
* then return cleanly else read the message from the bus.
*/
- curr_gwp = le32_to_cpu(saa7164_readl(bus->m_dwGetWritePos));
- curr_grp = le32_to_cpu(saa7164_readl(bus->m_dwGetReadPos));
+ curr_gwp = saa7164_readl(bus->m_dwGetWritePos);
+ curr_grp = saa7164_readl(bus->m_dwGetReadPos);
if (curr_gwp == curr_grp) {
ret = SAA_ERR_EMPTY;
@@ -369,14 +381,18 @@
new_grp -= bus->m_dwSizeGetRing;
space_rem = bus->m_dwSizeGetRing - curr_grp;
- memcpy(&msg_tmp, bus->m_pdwGetRing + curr_grp, space_rem);
- memcpy((u8 *)&msg_tmp + space_rem, bus->m_pdwGetRing,
+ memcpy_fromio(&msg_tmp, bus->m_pdwGetRing + curr_grp, space_rem);
+ memcpy_fromio((u8 *)&msg_tmp + space_rem, bus->m_pdwGetRing,
bytes_to_read - space_rem);
} else {
/* No wrapping */
- memcpy(&msg_tmp, bus->m_pdwGetRing + curr_grp, bytes_to_read);
+ memcpy_fromio(&msg_tmp, bus->m_pdwGetRing + curr_grp, bytes_to_read);
}
+ /* Convert from little endian to CPU */
+ msg_tmp.size = le16_to_cpu((__force __le16)msg_tmp.size);
+ msg_tmp.command = le32_to_cpu((__force __le32)msg_tmp.command);
+ msg_tmp.controlselector = le16_to_cpu((__force __le16)msg_tmp.controlselector);
/* No need to update the read positions, because this was a peek */
/* If the caller specifically want to peek, return */
@@ -427,24 +443,24 @@
if (space_rem < sizeof(*msg)) {
/* msg wraps around the ring */
- memcpy(msg, bus->m_pdwGetRing + curr_grp, space_rem);
- memcpy((u8 *)msg + space_rem, bus->m_pdwGetRing,
+ memcpy_fromio(msg, bus->m_pdwGetRing + curr_grp, space_rem);
+ memcpy_fromio((u8 *)msg + space_rem, bus->m_pdwGetRing,
sizeof(*msg) - space_rem);
if (buf)
- memcpy(buf, bus->m_pdwGetRing + sizeof(*msg) -
+ memcpy_fromio(buf, bus->m_pdwGetRing + sizeof(*msg) -
space_rem, buf_size);
} else if (space_rem == sizeof(*msg)) {
- memcpy(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg));
+ memcpy_fromio(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg));
if (buf)
- memcpy(buf, bus->m_pdwGetRing, buf_size);
+ memcpy_fromio(buf, bus->m_pdwGetRing, buf_size);
} else {
/* Additional data wraps around the ring */
- memcpy(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg));
+ memcpy_fromio(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg));
if (buf) {
- memcpy(buf, bus->m_pdwGetRing + curr_grp +
+ memcpy_fromio(buf, bus->m_pdwGetRing + curr_grp +
sizeof(*msg), space_rem - sizeof(*msg));
- memcpy(buf + space_rem - sizeof(*msg),
+ memcpy_fromio(buf + space_rem - sizeof(*msg),
bus->m_pdwGetRing, bytes_to_read -
space_rem);
}
@@ -453,19 +469,20 @@
} else {
/* No wrapping */
- memcpy(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg));
+ memcpy_fromio(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg));
if (buf)
- memcpy(buf, bus->m_pdwGetRing + curr_grp + sizeof(*msg),
+ memcpy_fromio(buf, bus->m_pdwGetRing + curr_grp + sizeof(*msg),
buf_size);
}
+ /* Convert from little endian to CPU */
+ msg->size = le16_to_cpu((__force __le16)msg->size);
+ msg->command = le32_to_cpu((__force __le32)msg->command);
+ msg->controlselector = le16_to_cpu((__force __le16)msg->controlselector);
/* Update the read positions, adjusting the ring */
- saa7164_writel(bus->m_dwGetReadPos, cpu_to_le32(new_grp));
+ saa7164_writel(bus->m_dwGetReadPos, new_grp);
peekout:
- msg->size = le16_to_cpu(msg->size);
- msg->command = le32_to_cpu(msg->command);
- msg->controlselector = le16_to_cpu(msg->controlselector);
ret = SAA_OK;
out:
mutex_unlock(&bus->lock);
diff --git a/drivers/media/pci/saa7164/saa7164-core.c b/drivers/media/pci/saa7164/saa7164-core.c
index cc1be8a..4b0bec3 100644
--- a/drivers/media/pci/saa7164/saa7164-core.c
+++ b/drivers/media/pci/saa7164/saa7164-core.c
@@ -119,7 +119,7 @@
u32 i;
u8 cc, a;
u16 pid;
- u8 __iomem *bufcpu = (u8 *)buf->cpu;
+ u8 *bufcpu = (u8 *)buf->cpu;
port->sync_errors = 0;
port->v_cc_errors = 0;
@@ -260,7 +260,7 @@
struct saa7164_user_buffer *ubuf = NULL;
struct list_head *c, *n;
int i = 0;
- u8 __iomem *p;
+ u8 *p;
mutex_lock(&port->dmaqueue_lock);
list_for_each_safe(c, n, &port->dmaqueue.list) {
@@ -318,8 +318,7 @@
if (buf->actual_size <= ubuf->actual_size) {
- memcpy_fromio(ubuf->data, buf->cpu,
- ubuf->actual_size);
+ memcpy(ubuf->data, buf->cpu, ubuf->actual_size);
if (crc_checking) {
/* Throw a new checksum on the read buffer */
@@ -346,7 +345,7 @@
* with known bad data. We check for this data at a later point
* in time. */
saa7164_buffer_zero_offsets(port, bufnr);
- memset_io(buf->cpu, 0xff, buf->pci_size);
+ memset(buf->cpu, 0xff, buf->pci_size);
if (crc_checking) {
/* Throw yet aanother new checksum on the dma buffer */
buf->crc = crc32(0, buf->cpu, buf->actual_size);
@@ -1096,7 +1095,7 @@
if (c == 0)
seq_printf(m, " %04x:", i);
- seq_printf(m, " %02x", *(b->m_pdwSetRing + i));
+ seq_printf(m, " %02x", readb(b->m_pdwSetRing + i));
if (++c == 16) {
seq_printf(m, "\n");
@@ -1111,7 +1110,7 @@
if (c == 0)
seq_printf(m, " %04x:", i);
- seq_printf(m, " %02x", *(b->m_pdwGetRing + i));
+ seq_printf(m, " %02x", readb(b->m_pdwGetRing + i));
if (++c == 16) {
seq_printf(m, "\n");
diff --git a/drivers/media/pci/saa7164/saa7164-fw.c b/drivers/media/pci/saa7164/saa7164-fw.c
index 8676320..add06ab 100644
--- a/drivers/media/pci/saa7164/saa7164-fw.c
+++ b/drivers/media/pci/saa7164/saa7164-fw.c
@@ -72,7 +72,7 @@
/* TODO: move dlflags into dev-> and change to write/readl/b */
/* TODO: Excessive levels of debug */
static int saa7164_downloadimage(struct saa7164_dev *dev, u8 *src, u32 srcsize,
- u32 dlflags, u8 *dst, u32 dstsize)
+ u32 dlflags, u8 __iomem *dst, u32 dstsize)
{
u32 reg, timeout, offset;
u8 *srcbuf = NULL;
@@ -136,7 +136,7 @@
srcsize -= dstsize, offset += dstsize) {
dprintk(DBGLVL_FW, "%s() memcpy %d\n", __func__, dstsize);
- memcpy(dst, srcbuf + offset, dstsize);
+ memcpy_toio(dst, srcbuf + offset, dstsize);
/* Flag the data as ready */
saa7164_writel(drflag, 1);
@@ -154,7 +154,7 @@
dprintk(DBGLVL_FW, "%s() memcpy(l) %d\n", __func__, dstsize);
/* Write last block to the device */
- memcpy(dst, srcbuf+offset, srcsize);
+ memcpy_toio(dst, srcbuf+offset, srcsize);
/* Flag the data as ready */
saa7164_writel(drflag, 1);
diff --git a/drivers/media/pci/saa7164/saa7164-types.h b/drivers/media/pci/saa7164/saa7164-types.h
index 1d2140a..f48ba97 100644
--- a/drivers/media/pci/saa7164/saa7164-types.h
+++ b/drivers/media/pci/saa7164/saa7164-types.h
@@ -78,9 +78,9 @@
struct tmComResBusInfo {
enum tmBusType Type;
u16 m_wMaxReqSize;
- u8 *m_pdwSetRing;
+ u8 __iomem *m_pdwSetRing;
u32 m_dwSizeSetRing;
- u8 *m_pdwGetRing;
+ u8 __iomem *m_pdwGetRing;
u32 m_dwSizeGetRing;
u32 m_dwSetWritePos;
u32 m_dwSetReadPos;
diff --git a/drivers/media/pci/saa7164/saa7164.h b/drivers/media/pci/saa7164/saa7164.h
index 8b29e89..cd1a07c 100644
--- a/drivers/media/pci/saa7164/saa7164.h
+++ b/drivers/media/pci/saa7164/saa7164.h
@@ -313,13 +313,13 @@
/* A block of page align PCI memory */
u32 pci_size; /* PCI allocation size in bytes */
- u64 __iomem *cpu; /* Virtual address */
+ u64 *cpu; /* Virtual address */
dma_addr_t dma; /* Physical address */
u32 crc; /* Checksum for the entire buffer data */
/* A page table that splits the block into a number of entries */
u32 pt_size; /* PCI allocation size in bytes */
- u64 __iomem *pt_cpu; /* Virtual address */
+ u64 *pt_cpu; /* Virtual address */
dma_addr_t pt_dma; /* Physical address */
/* Encoder fops */
diff --git a/drivers/media/pci/smipcie/Kconfig b/drivers/media/pci/smipcie/Kconfig
new file mode 100644
index 0000000..45d6a31
--- /dev/null
+++ b/drivers/media/pci/smipcie/Kconfig
@@ -0,0 +1,19 @@
+config DVB_SMIPCIE
+ depends on !KERNEL_3_4
+ tristate "SMI PCIe DVBSky cards"
+ depends on m
+ depends on DVB_CORE && PCI && I2C
+ depends on I2C_ALGOBIT
+ select DVB_M88DS3103 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_SI2168 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_M88TS2022 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_M88RS6000T if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Support for cards with SMI PCIe bridge:
+ - DVBSky S950 V3
+ - DVBSky S952 V3
+ - DVBSky T9580 V3
+
+ Say Y or M if you own such a device and want to use it.
+ If unsure say N.
diff --git a/drivers/media/pci/smipcie/Makefile b/drivers/media/pci/smipcie/Makefile
new file mode 100644
index 0000000..ac5f289
--- /dev/null
+++ b/drivers/media/pci/smipcie/Makefile
@@ -0,0 +1,6 @@
+obj-$(CPTCFG_DVB_SMIPCIE) += smipcie.o
+
+ccflags-y += -I$(backport_srctree)/drivers/media/tuners
+ccflags-y += -I$(backport_srctree)/drivers/media/dvb-core
+ccflags-y += -I$(backport_srctree)/drivers/media/dvb-frontends
+
diff --git a/drivers/media/pci/smipcie/smipcie.c b/drivers/media/pci/smipcie/smipcie.c
new file mode 100644
index 0000000..f773350
--- /dev/null
+++ b/drivers/media/pci/smipcie/smipcie.c
@@ -0,0 +1,1099 @@
+/*
+ * SMI PCIe driver for DVBSky cards.
+ *
+ * Copyright (C) 2014 Max nibble <nibble.max@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "smipcie.h"
+#include "m88ds3103.h"
+#include "m88ts2022.h"
+#include "m88rs6000t.h"
+#include "si2168.h"
+#include "si2157.h"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static int smi_hw_init(struct smi_dev *dev)
+{
+ u32 port_mux, port_ctrl, int_stat;
+
+ /* set port mux.*/
+ port_mux = smi_read(MUX_MODE_CTRL);
+ port_mux &= ~(rbPaMSMask);
+ port_mux |= rbPaMSDtvNoGpio;
+ port_mux &= ~(rbPbMSMask);
+ port_mux |= rbPbMSDtvNoGpio;
+ port_mux &= ~(0x0f0000);
+ port_mux |= 0x50000;
+ smi_write(MUX_MODE_CTRL, port_mux);
+
+ /* set DTV register.*/
+ /* Port A */
+ port_ctrl = smi_read(VIDEO_CTRL_STATUS_A);
+ port_ctrl &= ~0x01;
+ smi_write(VIDEO_CTRL_STATUS_A, port_ctrl);
+ port_ctrl = smi_read(MPEG2_CTRL_A);
+ port_ctrl &= ~0x40;
+ port_ctrl |= 0x80;
+ smi_write(MPEG2_CTRL_A, port_ctrl);
+ /* Port B */
+ port_ctrl = smi_read(VIDEO_CTRL_STATUS_B);
+ port_ctrl &= ~0x01;
+ smi_write(VIDEO_CTRL_STATUS_B, port_ctrl);
+ port_ctrl = smi_read(MPEG2_CTRL_B);
+ port_ctrl &= ~0x40;
+ port_ctrl |= 0x80;
+ smi_write(MPEG2_CTRL_B, port_ctrl);
+
+ /* disable and clear interrupt.*/
+ smi_write(MSI_INT_ENA_CLR, ALL_INT);
+ int_stat = smi_read(MSI_INT_STATUS);
+ smi_write(MSI_INT_STATUS_CLR, int_stat);
+
+ /* reset demod.*/
+ smi_clear(PERIPHERAL_CTRL, 0x0303);
+ msleep(50);
+ smi_set(PERIPHERAL_CTRL, 0x0101);
+ return 0;
+}
+
+/* i2c bit bus.*/
+static void smi_i2c_cfg(struct smi_dev *dev, u32 sw_ctl)
+{
+ u32 dwCtrl;
+
+ dwCtrl = smi_read(sw_ctl);
+ dwCtrl &= ~0x18; /* disable output.*/
+ dwCtrl |= 0x21; /* reset and software mode.*/
+ dwCtrl &= ~0xff00;
+ dwCtrl |= 0x6400;
+ smi_write(sw_ctl, dwCtrl);
+ msleep(20);
+ dwCtrl = smi_read(sw_ctl);
+ dwCtrl &= ~0x20;
+ smi_write(sw_ctl, dwCtrl);
+}
+
+static void smi_i2c_setsda(struct smi_dev *dev, int state, u32 sw_ctl)
+{
+ if (state) {
+ /* set as input.*/
+ smi_clear(sw_ctl, SW_I2C_MSK_DAT_EN);
+ } else {
+ smi_clear(sw_ctl, SW_I2C_MSK_DAT_OUT);
+ /* set as output.*/
+ smi_set(sw_ctl, SW_I2C_MSK_DAT_EN);
+ }
+}
+
+static void smi_i2c_setscl(void *data, int state, u32 sw_ctl)
+{
+ struct smi_dev *dev = data;
+
+ if (state) {
+ /* set as input.*/
+ smi_clear(sw_ctl, SW_I2C_MSK_CLK_EN);
+ } else {
+ smi_clear(sw_ctl, SW_I2C_MSK_CLK_OUT);
+ /* set as output.*/
+ smi_set(sw_ctl, SW_I2C_MSK_CLK_EN);
+ }
+}
+
+static int smi_i2c_getsda(void *data, u32 sw_ctl)
+{
+ struct smi_dev *dev = data;
+ /* set as input.*/
+ smi_clear(sw_ctl, SW_I2C_MSK_DAT_EN);
+ udelay(1);
+ return (smi_read(sw_ctl) & SW_I2C_MSK_DAT_IN) ? 1 : 0;
+}
+
+static int smi_i2c_getscl(void *data, u32 sw_ctl)
+{
+ struct smi_dev *dev = data;
+ /* set as input.*/
+ smi_clear(sw_ctl, SW_I2C_MSK_CLK_EN);
+ udelay(1);
+ return (smi_read(sw_ctl) & SW_I2C_MSK_CLK_IN) ? 1 : 0;
+}
+/* i2c 0.*/
+static void smi_i2c0_setsda(void *data, int state)
+{
+ struct smi_dev *dev = data;
+
+ smi_i2c_setsda(dev, state, I2C_A_SW_CTL);
+}
+
+static void smi_i2c0_setscl(void *data, int state)
+{
+ struct smi_dev *dev = data;
+
+ smi_i2c_setscl(dev, state, I2C_A_SW_CTL);
+}
+
+static int smi_i2c0_getsda(void *data)
+{
+ struct smi_dev *dev = data;
+
+ return smi_i2c_getsda(dev, I2C_A_SW_CTL);
+}
+
+static int smi_i2c0_getscl(void *data)
+{
+ struct smi_dev *dev = data;
+
+ return smi_i2c_getscl(dev, I2C_A_SW_CTL);
+}
+/* i2c 1.*/
+static void smi_i2c1_setsda(void *data, int state)
+{
+ struct smi_dev *dev = data;
+
+ smi_i2c_setsda(dev, state, I2C_B_SW_CTL);
+}
+
+static void smi_i2c1_setscl(void *data, int state)
+{
+ struct smi_dev *dev = data;
+
+ smi_i2c_setscl(dev, state, I2C_B_SW_CTL);
+}
+
+static int smi_i2c1_getsda(void *data)
+{
+ struct smi_dev *dev = data;
+
+ return smi_i2c_getsda(dev, I2C_B_SW_CTL);
+}
+
+static int smi_i2c1_getscl(void *data)
+{
+ struct smi_dev *dev = data;
+
+ return smi_i2c_getscl(dev, I2C_B_SW_CTL);
+}
+
+static int smi_i2c_init(struct smi_dev *dev)
+{
+ int ret;
+
+ /* i2c bus 0 */
+ smi_i2c_cfg(dev, I2C_A_SW_CTL);
+ i2c_set_adapdata(&dev->i2c_bus[0], dev);
+ strcpy(dev->i2c_bus[0].name, "SMI-I2C0");
+ dev->i2c_bus[0].owner = THIS_MODULE;
+ dev->i2c_bus[0].dev.parent = &dev->pci_dev->dev;
+ dev->i2c_bus[0].algo_data = &dev->i2c_bit[0];
+ dev->i2c_bit[0].data = dev;
+ dev->i2c_bit[0].setsda = smi_i2c0_setsda;
+ dev->i2c_bit[0].setscl = smi_i2c0_setscl;
+ dev->i2c_bit[0].getsda = smi_i2c0_getsda;
+ dev->i2c_bit[0].getscl = smi_i2c0_getscl;
+ dev->i2c_bit[0].udelay = 12;
+ dev->i2c_bit[0].timeout = 10;
+ /* Raise SCL and SDA */
+ smi_i2c0_setsda(dev, 1);
+ smi_i2c0_setscl(dev, 1);
+
+ ret = i2c_bit_add_bus(&dev->i2c_bus[0]);
+ if (ret < 0)
+ return ret;
+
+ /* i2c bus 1 */
+ smi_i2c_cfg(dev, I2C_B_SW_CTL);
+ i2c_set_adapdata(&dev->i2c_bus[1], dev);
+ strcpy(dev->i2c_bus[1].name, "SMI-I2C1");
+ dev->i2c_bus[1].owner = THIS_MODULE;
+ dev->i2c_bus[1].dev.parent = &dev->pci_dev->dev;
+ dev->i2c_bus[1].algo_data = &dev->i2c_bit[1];
+ dev->i2c_bit[1].data = dev;
+ dev->i2c_bit[1].setsda = smi_i2c1_setsda;
+ dev->i2c_bit[1].setscl = smi_i2c1_setscl;
+ dev->i2c_bit[1].getsda = smi_i2c1_getsda;
+ dev->i2c_bit[1].getscl = smi_i2c1_getscl;
+ dev->i2c_bit[1].udelay = 12;
+ dev->i2c_bit[1].timeout = 10;
+ /* Raise SCL and SDA */
+ smi_i2c1_setsda(dev, 1);
+ smi_i2c1_setscl(dev, 1);
+
+ ret = i2c_bit_add_bus(&dev->i2c_bus[1]);
+ if (ret < 0)
+ i2c_del_adapter(&dev->i2c_bus[0]);
+
+ return ret;
+}
+
+static void smi_i2c_exit(struct smi_dev *dev)
+{
+ i2c_del_adapter(&dev->i2c_bus[0]);
+ i2c_del_adapter(&dev->i2c_bus[1]);
+}
+
+static int smi_read_eeprom(struct i2c_adapter *i2c, u16 reg, u8 *data, u16 size)
+{
+ int ret;
+ u8 b0[2] = { (reg >> 8) & 0xff, reg & 0xff };
+
+ struct i2c_msg msg[] = {
+ { .addr = 0x50, .flags = 0,
+ .buf = b0, .len = 2 },
+ { .addr = 0x50, .flags = I2C_M_RD,
+ .buf = data, .len = size }
+ };
+
+ ret = i2c_transfer(i2c, msg, 2);
+
+ if (ret != 2) {
+ dev_err(&i2c->dev, "%s: reg=0x%x (error=%d)\n",
+ __func__, reg, ret);
+ return ret;
+ }
+ return ret;
+}
+
+/* ts port interrupt operations */
+static void smi_port_disableInterrupt(struct smi_port *port)
+{
+ struct smi_dev *dev = port->dev;
+
+ smi_write(MSI_INT_ENA_CLR,
+ (port->_dmaInterruptCH0 | port->_dmaInterruptCH1));
+}
+
+static void smi_port_enableInterrupt(struct smi_port *port)
+{
+ struct smi_dev *dev = port->dev;
+
+ smi_write(MSI_INT_ENA_SET,
+ (port->_dmaInterruptCH0 | port->_dmaInterruptCH1));
+}
+
+static void smi_port_clearInterrupt(struct smi_port *port)
+{
+ struct smi_dev *dev = port->dev;
+
+ smi_write(MSI_INT_STATUS_CLR,
+ (port->_dmaInterruptCH0 | port->_dmaInterruptCH1));
+}
+
+/* tasklet handler: DMA data to dmx.*/
+static void smi_dma_xfer(unsigned long data)
+{
+ struct smi_port *port = (struct smi_port *) data;
+ struct smi_dev *dev = port->dev;
+ u32 intr_status, finishedData, dmaManagement;
+ u8 dmaChan0State, dmaChan1State;
+
+ intr_status = port->_int_status;
+ dmaManagement = smi_read(port->DMA_MANAGEMENT);
+ dmaChan0State = (u8)((dmaManagement & 0x00000030) >> 4);
+ dmaChan1State = (u8)((dmaManagement & 0x00300000) >> 20);
+
+ /* CH-0 DMA interrupt.*/
+ if ((intr_status & port->_dmaInterruptCH0) && (dmaChan0State == 0x01)) {
+ dev_dbg(&dev->pci_dev->dev,
+ "Port[%d]-DMA CH0 engine complete successful !\n",
+ port->idx);
+ finishedData = smi_read(port->DMA_CHAN0_TRANS_STATE);
+ finishedData &= 0x003FFFFF;
+ /* value of DMA_PORT0_CHAN0_TRANS_STATE register [21:0]
+ * indicate dma total transfer length and
+ * zero of [21:0] indicate dma total transfer length
+ * equal to 0x400000 (4MB)*/
+ if (finishedData == 0)
+ finishedData = 0x00400000;
+ if (finishedData != SMI_TS_DMA_BUF_SIZE) {
+ dev_dbg(&dev->pci_dev->dev,
+ "DMA CH0 engine complete length mismatched, finish data=%d !\n",
+ finishedData);
+ }
+ dvb_dmx_swfilter_packets(&port->demux,
+ port->cpu_addr[0], (finishedData / 188));
+ /*dvb_dmx_swfilter(&port->demux,
+ port->cpu_addr[0], finishedData);*/
+ }
+ /* CH-1 DMA interrupt.*/
+ if ((intr_status & port->_dmaInterruptCH1) && (dmaChan1State == 0x01)) {
+ dev_dbg(&dev->pci_dev->dev,
+ "Port[%d]-DMA CH1 engine complete successful !\n",
+ port->idx);
+ finishedData = smi_read(port->DMA_CHAN1_TRANS_STATE);
+ finishedData &= 0x003FFFFF;
+ /* value of DMA_PORT0_CHAN0_TRANS_STATE register [21:0]
+ * indicate dma total transfer length and
+ * zero of [21:0] indicate dma total transfer length
+ * equal to 0x400000 (4MB)*/
+ if (finishedData == 0)
+ finishedData = 0x00400000;
+ if (finishedData != SMI_TS_DMA_BUF_SIZE) {
+ dev_dbg(&dev->pci_dev->dev,
+ "DMA CH1 engine complete length mismatched, finish data=%d !\n",
+ finishedData);
+ }
+ dvb_dmx_swfilter_packets(&port->demux,
+ port->cpu_addr[1], (finishedData / 188));
+ /*dvb_dmx_swfilter(&port->demux,
+ port->cpu_addr[1], finishedData);*/
+ }
+ /* restart DMA.*/
+ if (intr_status & port->_dmaInterruptCH0)
+ dmaManagement |= 0x00000002;
+ if (intr_status & port->_dmaInterruptCH1)
+ dmaManagement |= 0x00020000;
+ smi_write(port->DMA_MANAGEMENT, dmaManagement);
+ /* Re-enable interrupts */
+ smi_port_enableInterrupt(port);
+}
+
+static void smi_port_dma_free(struct smi_port *port)
+{
+ if (port->cpu_addr[0]) {
+ pci_free_consistent(port->dev->pci_dev, SMI_TS_DMA_BUF_SIZE,
+ port->cpu_addr[0], port->dma_addr[0]);
+ port->cpu_addr[0] = NULL;
+ }
+ if (port->cpu_addr[1]) {
+ pci_free_consistent(port->dev->pci_dev, SMI_TS_DMA_BUF_SIZE,
+ port->cpu_addr[1], port->dma_addr[1]);
+ port->cpu_addr[1] = NULL;
+ }
+}
+
+static int smi_port_init(struct smi_port *port, int dmaChanUsed)
+{
+ dev_dbg(&port->dev->pci_dev->dev,
+ "%s, port %d, dmaused %d\n", __func__, port->idx, dmaChanUsed);
+ port->enable = 0;
+ if (port->idx == 0) {
+ /* Port A */
+ port->_dmaInterruptCH0 = dmaChanUsed & 0x01;
+ port->_dmaInterruptCH1 = dmaChanUsed & 0x02;
+
+ port->DMA_CHAN0_ADDR_LOW = DMA_PORTA_CHAN0_ADDR_LOW;
+ port->DMA_CHAN0_ADDR_HI = DMA_PORTA_CHAN0_ADDR_HI;
+ port->DMA_CHAN0_TRANS_STATE = DMA_PORTA_CHAN0_TRANS_STATE;
+ port->DMA_CHAN0_CONTROL = DMA_PORTA_CHAN0_CONTROL;
+ port->DMA_CHAN1_ADDR_LOW = DMA_PORTA_CHAN1_ADDR_LOW;
+ port->DMA_CHAN1_ADDR_HI = DMA_PORTA_CHAN1_ADDR_HI;
+ port->DMA_CHAN1_TRANS_STATE = DMA_PORTA_CHAN1_TRANS_STATE;
+ port->DMA_CHAN1_CONTROL = DMA_PORTA_CHAN1_CONTROL;
+ port->DMA_MANAGEMENT = DMA_PORTA_MANAGEMENT;
+ } else {
+ /* Port B */
+ port->_dmaInterruptCH0 = (dmaChanUsed << 2) & 0x04;
+ port->_dmaInterruptCH1 = (dmaChanUsed << 2) & 0x08;
+
+ port->DMA_CHAN0_ADDR_LOW = DMA_PORTB_CHAN0_ADDR_LOW;
+ port->DMA_CHAN0_ADDR_HI = DMA_PORTB_CHAN0_ADDR_HI;
+ port->DMA_CHAN0_TRANS_STATE = DMA_PORTB_CHAN0_TRANS_STATE;
+ port->DMA_CHAN0_CONTROL = DMA_PORTB_CHAN0_CONTROL;
+ port->DMA_CHAN1_ADDR_LOW = DMA_PORTB_CHAN1_ADDR_LOW;
+ port->DMA_CHAN1_ADDR_HI = DMA_PORTB_CHAN1_ADDR_HI;
+ port->DMA_CHAN1_TRANS_STATE = DMA_PORTB_CHAN1_TRANS_STATE;
+ port->DMA_CHAN1_CONTROL = DMA_PORTB_CHAN1_CONTROL;
+ port->DMA_MANAGEMENT = DMA_PORTB_MANAGEMENT;
+ }
+
+ if (port->_dmaInterruptCH0) {
+ port->cpu_addr[0] = pci_alloc_consistent(port->dev->pci_dev,
+ SMI_TS_DMA_BUF_SIZE,
+ &port->dma_addr[0]);
+ if (!port->cpu_addr[0]) {
+ dev_err(&port->dev->pci_dev->dev,
+ "Port[%d] DMA CH0 memory allocation failed!\n",
+ port->idx);
+ goto err;
+ }
+ }
+
+ if (port->_dmaInterruptCH1) {
+ port->cpu_addr[1] = pci_alloc_consistent(port->dev->pci_dev,
+ SMI_TS_DMA_BUF_SIZE,
+ &port->dma_addr[1]);
+ if (!port->cpu_addr[1]) {
+ dev_err(&port->dev->pci_dev->dev,
+ "Port[%d] DMA CH1 memory allocation failed!\n",
+ port->idx);
+ goto err;
+ }
+ }
+
+ smi_port_disableInterrupt(port);
+ tasklet_init(&port->tasklet, smi_dma_xfer, (unsigned long)port);
+ tasklet_disable(&port->tasklet);
+ port->enable = 1;
+ return 0;
+err:
+ smi_port_dma_free(port);
+ return -ENOMEM;
+}
+
+static void smi_port_exit(struct smi_port *port)
+{
+ smi_port_disableInterrupt(port);
+ tasklet_kill(&port->tasklet);
+ smi_port_dma_free(port);
+ port->enable = 0;
+}
+
+static void smi_port_irq(struct smi_port *port, u32 int_status)
+{
+ u32 port_req_irq = port->_dmaInterruptCH0 | port->_dmaInterruptCH1;
+
+ if (int_status & port_req_irq) {
+ smi_port_disableInterrupt(port);
+ port->_int_status = int_status;
+ smi_port_clearInterrupt(port);
+ tasklet_schedule(&port->tasklet);
+ }
+}
+
+static irqreturn_t smi_irq_handler(int irq, void *dev_id)
+{
+ struct smi_dev *dev = dev_id;
+ struct smi_port *port0 = &dev->ts_port[0];
+ struct smi_port *port1 = &dev->ts_port[1];
+
+ u32 intr_status = smi_read(MSI_INT_STATUS);
+
+ /* ts0 interrupt.*/
+ if (dev->info->ts_0)
+ smi_port_irq(port0, intr_status);
+
+ /* ts1 interrupt.*/
+ if (dev->info->ts_1)
+ smi_port_irq(port1, intr_status);
+
+ return IRQ_HANDLED;
+}
+
+static struct i2c_client *smi_add_i2c_client(struct i2c_adapter *adapter,
+ struct i2c_board_info *info)
+{
+ struct i2c_client *client;
+
+ request_module(info->type);
+ client = i2c_new_device(adapter, info);
+ if (client == NULL || client->dev.driver == NULL)
+ goto err_add_i2c_client;
+
+ if (!try_module_get(client->dev.driver->owner)) {
+ i2c_unregister_device(client);
+ goto err_add_i2c_client;
+ }
+ return client;
+
+err_add_i2c_client:
+ client = NULL;
+ return client;
+}
+
+static void smi_del_i2c_client(struct i2c_client *client)
+{
+ module_put(client->dev.driver->owner);
+ i2c_unregister_device(client);
+}
+
+static const struct m88ds3103_config smi_dvbsky_m88ds3103_cfg = {
+ .i2c_addr = 0x68,
+ .clock = 27000000,
+ .i2c_wr_max = 33,
+ .clock_out = 0,
+ .ts_mode = M88DS3103_TS_PARALLEL,
+ .ts_clk = 16000,
+ .ts_clk_pol = 1,
+ .agc = 0x99,
+ .lnb_hv_pol = 0,
+ .lnb_en_pol = 1,
+};
+
+static int smi_dvbsky_m88ds3103_fe_attach(struct smi_port *port)
+{
+ int ret = 0;
+ struct smi_dev *dev = port->dev;
+ struct i2c_adapter *i2c;
+ /* tuner I2C module */
+ struct i2c_adapter *tuner_i2c_adapter;
+ struct i2c_client *tuner_client;
+ struct i2c_board_info tuner_info;
+ struct m88ts2022_config m88ts2022_config = {
+ .clock = 27000000,
+ };
+ memset(&tuner_info, 0, sizeof(struct i2c_board_info));
+ i2c = (port->idx == 0) ? &dev->i2c_bus[0] : &dev->i2c_bus[1];
+
+ /* attach demod */
+ port->fe = dvb_attach(m88ds3103_attach,
+ &smi_dvbsky_m88ds3103_cfg, i2c, &tuner_i2c_adapter);
+ if (!port->fe) {
+ ret = -ENODEV;
+ return ret;
+ }
+ /* attach tuner */
+ m88ts2022_config.fe = port->fe;
+ strlcpy(tuner_info.type, "m88ts2022", I2C_NAME_SIZE);
+ tuner_info.addr = 0x60;
+ tuner_info.platform_data = &m88ts2022_config;
+ tuner_client = smi_add_i2c_client(tuner_i2c_adapter, &tuner_info);
+ if (!tuner_client) {
+ ret = -ENODEV;
+ goto err_tuner_i2c_device;
+ }
+
+ /* delegate signal strength measurement to tuner */
+ port->fe->ops.read_signal_strength =
+ port->fe->ops.tuner_ops.get_rf_strength;
+
+ port->i2c_client_tuner = tuner_client;
+ return ret;
+
+err_tuner_i2c_device:
+ dvb_frontend_detach(port->fe);
+ return ret;
+}
+
+static const struct m88ds3103_config smi_dvbsky_m88rs6000_cfg = {
+ .i2c_addr = 0x69,
+ .clock = 27000000,
+ .i2c_wr_max = 33,
+ .ts_mode = M88DS3103_TS_PARALLEL,
+ .ts_clk = 16000,
+ .ts_clk_pol = 1,
+ .agc = 0x99,
+ .lnb_hv_pol = 0,
+ .lnb_en_pol = 1,
+};
+
+static int smi_dvbsky_m88rs6000_fe_attach(struct smi_port *port)
+{
+ int ret = 0;
+ struct smi_dev *dev = port->dev;
+ struct i2c_adapter *i2c;
+ /* tuner I2C module */
+ struct i2c_adapter *tuner_i2c_adapter;
+ struct i2c_client *tuner_client;
+ struct i2c_board_info tuner_info;
+ struct m88rs6000t_config m88rs6000t_config;
+
+ memset(&tuner_info, 0, sizeof(struct i2c_board_info));
+ i2c = (port->idx == 0) ? &dev->i2c_bus[0] : &dev->i2c_bus[1];
+
+ /* attach demod */
+ port->fe = dvb_attach(m88ds3103_attach,
+ &smi_dvbsky_m88rs6000_cfg, i2c, &tuner_i2c_adapter);
+ if (!port->fe) {
+ ret = -ENODEV;
+ return ret;
+ }
+ /* attach tuner */
+ m88rs6000t_config.fe = port->fe;
+ strlcpy(tuner_info.type, "m88rs6000t", I2C_NAME_SIZE);
+ tuner_info.addr = 0x21;
+ tuner_info.platform_data = &m88rs6000t_config;
+ tuner_client = smi_add_i2c_client(tuner_i2c_adapter, &tuner_info);
+ if (!tuner_client) {
+ ret = -ENODEV;
+ goto err_tuner_i2c_device;
+ }
+
+ /* delegate signal strength measurement to tuner */
+ port->fe->ops.read_signal_strength =
+ port->fe->ops.tuner_ops.get_rf_strength;
+
+ port->i2c_client_tuner = tuner_client;
+ return ret;
+
+err_tuner_i2c_device:
+ dvb_frontend_detach(port->fe);
+ return ret;
+}
+
+static int smi_dvbsky_sit2_fe_attach(struct smi_port *port)
+{
+ int ret = 0;
+ struct smi_dev *dev = port->dev;
+ struct i2c_adapter *i2c;
+ struct i2c_adapter *tuner_i2c_adapter;
+ struct i2c_client *client_tuner, *client_demod;
+ struct i2c_board_info client_info;
+ struct si2168_config si2168_config;
+ struct si2157_config si2157_config;
+
+ /* select i2c bus */
+ i2c = (port->idx == 0) ? &dev->i2c_bus[0] : &dev->i2c_bus[1];
+
+ /* attach demod */
+ memset(&si2168_config, 0, sizeof(si2168_config));
+ si2168_config.i2c_adapter = &tuner_i2c_adapter;
+ si2168_config.fe = &port->fe;
+ si2168_config.ts_mode = SI2168_TS_PARALLEL;
+
+ memset(&client_info, 0, sizeof(struct i2c_board_info));
+ strlcpy(client_info.type, "si2168", I2C_NAME_SIZE);
+ client_info.addr = 0x64;
+ client_info.platform_data = &si2168_config;
+
+ client_demod = smi_add_i2c_client(i2c, &client_info);
+ if (!client_demod) {
+ ret = -ENODEV;
+ return ret;
+ }
+ port->i2c_client_demod = client_demod;
+
+ /* attach tuner */
+ memset(&si2157_config, 0, sizeof(si2157_config));
+ si2157_config.fe = port->fe;
+
+ memset(&client_info, 0, sizeof(struct i2c_board_info));
+ strlcpy(client_info.type, "si2157", I2C_NAME_SIZE);
+ client_info.addr = 0x60;
+ client_info.platform_data = &si2157_config;
+
+ client_tuner = smi_add_i2c_client(tuner_i2c_adapter, &client_info);
+ if (!client_tuner) {
+ smi_del_i2c_client(port->i2c_client_demod);
+ port->i2c_client_demod = NULL;
+ ret = -ENODEV;
+ return ret;
+ }
+ port->i2c_client_tuner = client_tuner;
+ return ret;
+}
+
+static int smi_fe_init(struct smi_port *port)
+{
+ int ret = 0;
+ struct smi_dev *dev = port->dev;
+ struct dvb_adapter *adap = &port->dvb_adapter;
+ u8 mac_ee[16];
+
+ dev_dbg(&port->dev->pci_dev->dev,
+ "%s: port %d, fe_type = %d\n",
+ __func__, port->idx, port->fe_type);
+ switch (port->fe_type) {
+ case DVBSKY_FE_M88DS3103:
+ ret = smi_dvbsky_m88ds3103_fe_attach(port);
+ break;
+ case DVBSKY_FE_M88RS6000:
+ ret = smi_dvbsky_m88rs6000_fe_attach(port);
+ break;
+ case DVBSKY_FE_SIT2:
+ ret = smi_dvbsky_sit2_fe_attach(port);
+ break;
+ }
+ if (ret < 0)
+ return ret;
+
+ /* register dvb frontend */
+ ret = dvb_register_frontend(adap, port->fe);
+ if (ret < 0) {
+ if (port->i2c_client_tuner)
+ smi_del_i2c_client(port->i2c_client_tuner);
+ if (port->i2c_client_demod)
+ smi_del_i2c_client(port->i2c_client_demod);
+ dvb_frontend_detach(port->fe);
+ return ret;
+ }
+ /* init MAC.*/
+ ret = smi_read_eeprom(&dev->i2c_bus[0], 0xc0, mac_ee, 16);
+ dev_info(&port->dev->pci_dev->dev,
+ "DVBSky SMI PCIe MAC= %pM\n", mac_ee + (port->idx)*8);
+ memcpy(adap->proposed_mac, mac_ee + (port->idx)*8, 6);
+ return ret;
+}
+
+static void smi_fe_exit(struct smi_port *port)
+{
+ dvb_unregister_frontend(port->fe);
+ /* remove I2C demod and tuner */
+ if (port->i2c_client_tuner)
+ smi_del_i2c_client(port->i2c_client_tuner);
+ if (port->i2c_client_demod)
+ smi_del_i2c_client(port->i2c_client_demod);
+ dvb_frontend_detach(port->fe);
+}
+
+static int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id,
+ int (*start_feed)(struct dvb_demux_feed *),
+ int (*stop_feed)(struct dvb_demux_feed *),
+ void *priv)
+{
+ dvbdemux->priv = priv;
+
+ dvbdemux->filternum = 256;
+ dvbdemux->feednum = 256;
+ dvbdemux->start_feed = start_feed;
+ dvbdemux->stop_feed = stop_feed;
+ dvbdemux->write_to_decoder = NULL;
+ dvbdemux->dmx.capabilities = (DMX_TS_FILTERING |
+ DMX_SECTION_FILTERING |
+ DMX_MEMORY_BASED_FILTERING);
+ return dvb_dmx_init(dvbdemux);
+}
+
+static int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev,
+ struct dvb_demux *dvbdemux,
+ struct dmx_frontend *hw_frontend,
+ struct dmx_frontend *mem_frontend,
+ struct dvb_adapter *dvb_adapter)
+{
+ int ret;
+
+ dmxdev->filternum = 256;
+ dmxdev->demux = &dvbdemux->dmx;
+ dmxdev->capabilities = 0;
+ ret = dvb_dmxdev_init(dmxdev, dvb_adapter);
+ if (ret < 0)
+ return ret;
+
+ hw_frontend->source = DMX_FRONTEND_0;
+ dvbdemux->dmx.add_frontend(&dvbdemux->dmx, hw_frontend);
+ mem_frontend->source = DMX_MEMORY_FE;
+ dvbdemux->dmx.add_frontend(&dvbdemux->dmx, mem_frontend);
+ return dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, hw_frontend);
+}
+
+static u32 smi_config_DMA(struct smi_port *port)
+{
+ struct smi_dev *dev = port->dev;
+ u32 totalLength = 0, dmaMemPtrLow, dmaMemPtrHi, dmaCtlReg;
+ u8 chanLatencyTimer = 0, dmaChanEnable = 1, dmaTransStart = 1;
+ u32 dmaManagement = 0, tlpTransUnit = DMA_TRANS_UNIT_188;
+ u8 tlpTc = 0, tlpTd = 1, tlpEp = 0, tlpAttr = 0;
+ u64 mem;
+
+ dmaManagement = smi_read(port->DMA_MANAGEMENT);
+ /* Setup Channel-0 */
+ if (port->_dmaInterruptCH0) {
+ totalLength = SMI_TS_DMA_BUF_SIZE;
+ mem = port->dma_addr[0];
+ dmaMemPtrLow = mem & 0xffffffff;
+ dmaMemPtrHi = mem >> 32;
+ dmaCtlReg = (totalLength) | (tlpTransUnit << 22) | (tlpTc << 25)
+ | (tlpTd << 28) | (tlpEp << 29) | (tlpAttr << 30);
+ dmaManagement |= dmaChanEnable | (dmaTransStart << 1)
+ | (chanLatencyTimer << 8);
+ /* write DMA register, start DMA engine */
+ smi_write(port->DMA_CHAN0_ADDR_LOW, dmaMemPtrLow);
+ smi_write(port->DMA_CHAN0_ADDR_HI, dmaMemPtrHi);
+ smi_write(port->DMA_CHAN0_CONTROL, dmaCtlReg);
+ }
+ /* Setup Channel-1 */
+ if (port->_dmaInterruptCH1) {
+ totalLength = SMI_TS_DMA_BUF_SIZE;
+ mem = port->dma_addr[1];
+ dmaMemPtrLow = mem & 0xffffffff;
+ dmaMemPtrHi = mem >> 32;
+ dmaCtlReg = (totalLength) | (tlpTransUnit << 22) | (tlpTc << 25)
+ | (tlpTd << 28) | (tlpEp << 29) | (tlpAttr << 30);
+ dmaManagement |= (dmaChanEnable << 16) | (dmaTransStart << 17)
+ | (chanLatencyTimer << 24);
+ /* write DMA register, start DMA engine */
+ smi_write(port->DMA_CHAN1_ADDR_LOW, dmaMemPtrLow);
+ smi_write(port->DMA_CHAN1_ADDR_HI, dmaMemPtrHi);
+ smi_write(port->DMA_CHAN1_CONTROL, dmaCtlReg);
+ }
+ return dmaManagement;
+}
+
+static int smi_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+ struct smi_port *port = dvbdmx->priv;
+ struct smi_dev *dev = port->dev;
+ u32 dmaManagement;
+
+ if (port->users++ == 0) {
+ dmaManagement = smi_config_DMA(port);
+ smi_port_clearInterrupt(port);
+ smi_port_enableInterrupt(port);
+ smi_write(port->DMA_MANAGEMENT, dmaManagement);
+ tasklet_enable(&port->tasklet);
+ }
+ return port->users;
+}
+
+static int smi_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+ struct smi_port *port = dvbdmx->priv;
+ struct smi_dev *dev = port->dev;
+
+ if (--port->users)
+ return port->users;
+
+ tasklet_disable(&port->tasklet);
+ smi_port_disableInterrupt(port);
+ smi_clear(port->DMA_MANAGEMENT, 0x30003);
+ return 0;
+}
+
+static int smi_dvb_init(struct smi_port *port)
+{
+ int ret;
+ struct dvb_adapter *adap = &port->dvb_adapter;
+ struct dvb_demux *dvbdemux = &port->demux;
+
+ dev_dbg(&port->dev->pci_dev->dev,
+ "%s, port %d\n", __func__, port->idx);
+
+ ret = dvb_register_adapter(adap, "SMI_DVB", THIS_MODULE,
+ &port->dev->pci_dev->dev,
+ adapter_nr);
+ if (ret < 0) {
+ dev_err(&port->dev->pci_dev->dev, "Fail to register DVB adapter.\n");
+ return ret;
+ }
+ ret = my_dvb_dmx_ts_card_init(dvbdemux, "SW demux",
+ smi_start_feed,
+ smi_stop_feed, port);
+ if (ret < 0)
+ goto err_del_dvb_register_adapter;
+
+ ret = my_dvb_dmxdev_ts_card_init(&port->dmxdev, &port->demux,
+ &port->hw_frontend,
+ &port->mem_frontend, adap);
+ if (ret < 0)
+ goto err_del_dvb_dmx;
+
+ ret = dvb_net_init(adap, &port->dvbnet, port->dmxdev.demux);
+ if (ret < 0)
+ goto err_del_dvb_dmxdev;
+ return 0;
+err_del_dvb_dmxdev:
+ dvbdemux->dmx.close(&dvbdemux->dmx);
+ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &port->hw_frontend);
+ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &port->mem_frontend);
+ dvb_dmxdev_release(&port->dmxdev);
+err_del_dvb_dmx:
+ dvb_dmx_release(&port->demux);
+err_del_dvb_register_adapter:
+ dvb_unregister_adapter(&port->dvb_adapter);
+ return ret;
+}
+
+static void smi_dvb_exit(struct smi_port *port)
+{
+ struct dvb_demux *dvbdemux = &port->demux;
+
+ dvb_net_release(&port->dvbnet);
+
+ dvbdemux->dmx.close(&dvbdemux->dmx);
+ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &port->hw_frontend);
+ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &port->mem_frontend);
+ dvb_dmxdev_release(&port->dmxdev);
+ dvb_dmx_release(&port->demux);
+
+ dvb_unregister_adapter(&port->dvb_adapter);
+}
+
+static int smi_port_attach(struct smi_dev *dev,
+ struct smi_port *port, int index)
+{
+ int ret, dmachs;
+
+ port->dev = dev;
+ port->idx = index;
+ port->fe_type = (index == 0) ? dev->info->fe_0 : dev->info->fe_1;
+ dmachs = (index == 0) ? dev->info->ts_0 : dev->info->ts_1;
+ /* port init.*/
+ ret = smi_port_init(port, dmachs);
+ if (ret < 0)
+ return ret;
+ /* dvb init.*/
+ ret = smi_dvb_init(port);
+ if (ret < 0)
+ goto err_del_port_init;
+ /* fe init.*/
+ ret = smi_fe_init(port);
+ if (ret < 0)
+ goto err_del_dvb_init;
+ return 0;
+err_del_dvb_init:
+ smi_dvb_exit(port);
+err_del_port_init:
+ smi_port_exit(port);
+ return ret;
+}
+
+static void smi_port_detach(struct smi_port *port)
+{
+ smi_fe_exit(port);
+ smi_dvb_exit(port);
+ smi_port_exit(port);
+}
+
+static int smi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct smi_dev *dev;
+ int ret = -ENOMEM;
+
+ if (pci_enable_device(pdev) < 0)
+ return -ENODEV;
+
+ dev = kzalloc(sizeof(struct smi_dev), GFP_KERNEL);
+ if (!dev) {
+ ret = -ENOMEM;
+ goto err_pci_disable_device;
+ }
+
+ dev->pci_dev = pdev;
+ pci_set_drvdata(pdev, dev);
+ dev->info = (struct smi_cfg_info *) id->driver_data;
+ dev_info(&dev->pci_dev->dev,
+ "card detected: %s\n", dev->info->name);
+
+ dev->nr = dev->info->type;
+ dev->lmmio = ioremap(pci_resource_start(dev->pci_dev, 0),
+ pci_resource_len(dev->pci_dev, 0));
+ if (!dev->lmmio) {
+ ret = -ENOMEM;
+ goto err_kfree;
+ }
+
+ /* should we set to 32bit DMA? */
+ ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (ret < 0)
+ goto err_pci_iounmap;
+
+ pci_set_master(pdev);
+
+ ret = smi_hw_init(dev);
+ if (ret < 0)
+ goto err_pci_iounmap;
+
+ ret = smi_i2c_init(dev);
+ if (ret < 0)
+ goto err_pci_iounmap;
+
+ if (dev->info->ts_0) {
+ ret = smi_port_attach(dev, &dev->ts_port[0], 0);
+ if (ret < 0)
+ goto err_del_i2c_adaptor;
+ }
+
+ if (dev->info->ts_1) {
+ ret = smi_port_attach(dev, &dev->ts_port[1], 1);
+ if (ret < 0)
+ goto err_del_port0_attach;
+ }
+
+#ifdef CONFIG_PCI_MSI /* to do msi interrupt.???*/
+ if (pci_msi_enabled())
+ ret = pci_enable_msi(dev->pci_dev);
+ if (ret)
+ dev_info(&dev->pci_dev->dev, "MSI not available.\n");
+#endif
+
+ ret = request_irq(dev->pci_dev->irq, smi_irq_handler,
+ IRQF_SHARED, "SMI_PCIE", dev);
+ if (ret < 0)
+ goto err_del_port1_attach;
+
+ return 0;
+
+err_del_port1_attach:
+ if (dev->info->ts_1)
+ smi_port_detach(&dev->ts_port[1]);
+err_del_port0_attach:
+ if (dev->info->ts_0)
+ smi_port_detach(&dev->ts_port[0]);
+err_del_i2c_adaptor:
+ smi_i2c_exit(dev);
+err_pci_iounmap:
+ iounmap(dev->lmmio);
+err_kfree:
+ pci_set_drvdata(pdev, NULL);
+ kfree(dev);
+err_pci_disable_device:
+ pci_disable_device(pdev);
+ return ret;
+}
+
+static void smi_remove(struct pci_dev *pdev)
+{
+ struct smi_dev *dev = pci_get_drvdata(pdev);
+
+ smi_write(MSI_INT_ENA_CLR, ALL_INT);
+ free_irq(dev->pci_dev->irq, dev);
+#ifdef CONFIG_PCI_MSI
+ pci_disable_msi(dev->pci_dev);
+#endif
+ if (dev->info->ts_1)
+ smi_port_detach(&dev->ts_port[1]);
+ if (dev->info->ts_0)
+ smi_port_detach(&dev->ts_port[0]);
+
+ smi_i2c_exit(dev);
+ iounmap(dev->lmmio);
+ pci_set_drvdata(pdev, NULL);
+ pci_disable_device(pdev);
+ kfree(dev);
+}
+
+/* DVBSky cards */
+static struct smi_cfg_info dvbsky_s950_cfg = {
+ .type = SMI_DVBSKY_S950,
+ .name = "DVBSky S950 V3",
+ .ts_0 = SMI_TS_NULL,
+ .ts_1 = SMI_TS_DMA_BOTH,
+ .fe_0 = DVBSKY_FE_NULL,
+ .fe_1 = DVBSKY_FE_M88DS3103,
+};
+
+static struct smi_cfg_info dvbsky_s952_cfg = {
+ .type = SMI_DVBSKY_S952,
+ .name = "DVBSky S952 V3",
+ .ts_0 = SMI_TS_DMA_BOTH,
+ .ts_1 = SMI_TS_DMA_BOTH,
+ .fe_0 = DVBSKY_FE_M88RS6000,
+ .fe_1 = DVBSKY_FE_M88RS6000,
+};
+
+static struct smi_cfg_info dvbsky_t9580_cfg = {
+ .type = SMI_DVBSKY_T9580,
+ .name = "DVBSky T9580 V3",
+ .ts_0 = SMI_TS_DMA_BOTH,
+ .ts_1 = SMI_TS_DMA_BOTH,
+ .fe_0 = DVBSKY_FE_SIT2,
+ .fe_1 = DVBSKY_FE_M88DS3103,
+};
+
+/* PCI IDs */
+#define SMI_ID(_subvend, _subdev, _driverdata) { \
+ .vendor = SMI_VID, .device = SMI_PID, \
+ .subvendor = _subvend, .subdevice = _subdev, \
+ .driver_data = (unsigned long)&_driverdata }
+
+static const struct pci_device_id smi_id_table[] = {
+ SMI_ID(0x4254, 0x0550, dvbsky_s950_cfg),
+ SMI_ID(0x4254, 0x0552, dvbsky_s952_cfg),
+ SMI_ID(0x4254, 0x5580, dvbsky_t9580_cfg),
+ {0}
+};
+MODULE_DEVICE_TABLE(pci, smi_id_table);
+
+static struct pci_driver smipcie_driver = {
+ .name = "SMI PCIe driver",
+ .id_table = smi_id_table,
+ .probe = smi_probe,
+ .remove = smi_remove,
+};
+
+module_pci_driver(smipcie_driver);
+
+MODULE_AUTHOR("Max nibble <nibble.max@gmail.com>");
+MODULE_DESCRIPTION("SMI PCIe driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/smipcie/smipcie.h b/drivers/media/pci/smipcie/smipcie.h
new file mode 100644
index 0000000..10cdf20
--- /dev/null
+++ b/drivers/media/pci/smipcie/smipcie.h
@@ -0,0 +1,299 @@
+/*
+ * SMI PCIe driver for DVBSky cards.
+ *
+ * Copyright (C) 2014 Max nibble <nibble.max@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SMI_PCIE_H_
+#define _SMI_PCIE_H_
+
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <media/rc-core.h>
+
+#include "demux.h"
+#include "dmxdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+#include "dvbdev.h"
+
+/* -------- Register Base -------- */
+#define MSI_CONTROL_REG_BASE 0x0800
+#define SYSTEM_CONTROL_REG_BASE 0x0880
+#define PCIE_EP_DEBUG_REG_BASE 0x08C0
+#define IR_CONTROL_REG_BASE 0x0900
+#define I2C_A_CONTROL_REG_BASE 0x0940
+#define I2C_B_CONTROL_REG_BASE 0x0980
+#define ATV_PORTA_CONTROL_REG_BASE 0x09C0
+#define DTV_PORTA_CONTROL_REG_BASE 0x0A00
+#define AES_PORTA_CONTROL_REG_BASE 0x0A80
+#define DMA_PORTA_CONTROL_REG_BASE 0x0AC0
+#define ATV_PORTB_CONTROL_REG_BASE 0x0B00
+#define DTV_PORTB_CONTROL_REG_BASE 0x0B40
+#define AES_PORTB_CONTROL_REG_BASE 0x0BC0
+#define DMA_PORTB_CONTROL_REG_BASE 0x0C00
+#define UART_A_REGISTER_BASE 0x0C40
+#define UART_B_REGISTER_BASE 0x0C80
+#define GPS_CONTROL_REG_BASE 0x0CC0
+#define DMA_PORTC_CONTROL_REG_BASE 0x0D00
+#define DMA_PORTD_CONTROL_REG_BASE 0x0D00
+#define AES_RANDOM_DATA_BASE 0x0D80
+#define AES_KEY_IN_BASE 0x0D90
+#define RANDOM_DATA_LIB_BASE 0x0E00
+#define IR_DATA_BUFFER_BASE 0x0F00
+#define PORTA_TS_BUFFER_BASE 0x1000
+#define PORTA_I2S_BUFFER_BASE 0x1400
+#define PORTB_TS_BUFFER_BASE 0x1800
+#define PORTB_I2S_BUFFER_BASE 0x1C00
+
+/* -------- MSI control and state register -------- */
+#define MSI_DELAY_TIMER (MSI_CONTROL_REG_BASE + 0x00)
+#define MSI_INT_STATUS (MSI_CONTROL_REG_BASE + 0x08)
+#define MSI_INT_STATUS_CLR (MSI_CONTROL_REG_BASE + 0x0C)
+#define MSI_INT_STATUS_SET (MSI_CONTROL_REG_BASE + 0x10)
+#define MSI_INT_ENA (MSI_CONTROL_REG_BASE + 0x14)
+#define MSI_INT_ENA_CLR (MSI_CONTROL_REG_BASE + 0x18)
+#define MSI_INT_ENA_SET (MSI_CONTROL_REG_BASE + 0x1C)
+#define MSI_SOFT_RESET (MSI_CONTROL_REG_BASE + 0x20)
+#define MSI_CFG_SRC0 (MSI_CONTROL_REG_BASE + 0x24)
+
+/* -------- Hybird Controller System Control register -------- */
+#define MUX_MODE_CTRL (SYSTEM_CONTROL_REG_BASE + 0x00)
+ #define rbPaMSMask 0x07
+ #define rbPaMSDtvNoGpio 0x00 /*[2:0], DTV Simple mode */
+ #define rbPaMSDtv4bitGpio 0x01 /*[2:0], DTV TS2 Serial mode)*/
+ #define rbPaMSDtv7bitGpio 0x02 /*[2:0], DTV TS0 Serial mode*/
+ #define rbPaMS8bitGpio 0x03 /*[2:0], GPIO mode selected;(8bit GPIO)*/
+ #define rbPaMSAtv 0x04 /*[2:0], 3'b1xx: ATV mode select*/
+ #define rbPbMSMask 0x38
+ #define rbPbMSDtvNoGpio 0x00 /*[5:3], DTV Simple mode */
+ #define rbPbMSDtv4bitGpio 0x08 /*[5:3], DTV TS2 Serial mode*/
+ #define rbPbMSDtv7bitGpio 0x10 /*[5:3], DTV TS0 Serial mode*/
+ #define rbPbMS8bitGpio 0x18 /*[5:3], GPIO mode selected;(8bit GPIO)*/
+ #define rbPbMSAtv 0x20 /*[5:3], 3'b1xx: ATV mode select*/
+ #define rbPaAESEN 0x40 /*[6], port A AES enable bit*/
+ #define rbPbAESEN 0x80 /*[7], port B AES enable bit*/
+
+#define INTERNAL_RST (SYSTEM_CONTROL_REG_BASE + 0x04)
+#define PERIPHERAL_CTRL (SYSTEM_CONTROL_REG_BASE + 0x08)
+#define GPIO_0to7_CTRL (SYSTEM_CONTROL_REG_BASE + 0x0C)
+#define GPIO_8to15_CTRL (SYSTEM_CONTROL_REG_BASE + 0x10)
+#define GPIO_16to24_CTRL (SYSTEM_CONTROL_REG_BASE + 0x14)
+#define GPIO_INT_SRC_CFG (SYSTEM_CONTROL_REG_BASE + 0x18)
+#define SYS_BUF_STATUS (SYSTEM_CONTROL_REG_BASE + 0x1C)
+#define PCIE_IP_REG_ACS (SYSTEM_CONTROL_REG_BASE + 0x20)
+#define PCIE_IP_REG_ACS_ADDR (SYSTEM_CONTROL_REG_BASE + 0x24)
+#define PCIE_IP_REG_ACS_DATA (SYSTEM_CONTROL_REG_BASE + 0x28)
+
+/* -------- IR Control register -------- */
+#define IR_Init_Reg (IR_CONTROL_REG_BASE + 0x00)
+#define IR_Idle_Cnt_Low (IR_CONTROL_REG_BASE + 0x04)
+#define IR_Idle_Cnt_High (IR_CONTROL_REG_BASE + 0x05)
+#define IR_Unit_Cnt_Low (IR_CONTROL_REG_BASE + 0x06)
+#define IR_Unit_Cnt_High (IR_CONTROL_REG_BASE + 0x07)
+#define IR_Data_Cnt (IR_CONTROL_REG_BASE + 0x08)
+#define rbIRen 0x80
+#define rbIRhighidle 0x10
+#define rbIRlowidle 0x00
+#define rbIRVld 0x04
+
+/* -------- I2C A control and state register -------- */
+#define I2C_A_CTL_STATUS (I2C_A_CONTROL_REG_BASE + 0x00)
+#define I2C_A_ADDR (I2C_A_CONTROL_REG_BASE + 0x04)
+#define I2C_A_SW_CTL (I2C_A_CONTROL_REG_BASE + 0x08)
+#define I2C_A_TIME_OUT_CNT (I2C_A_CONTROL_REG_BASE + 0x0C)
+#define I2C_A_FIFO_STATUS (I2C_A_CONTROL_REG_BASE + 0x10)
+#define I2C_A_FS_EN (I2C_A_CONTROL_REG_BASE + 0x14)
+#define I2C_A_FIFO_DATA (I2C_A_CONTROL_REG_BASE + 0x20)
+
+/* -------- I2C B control and state register -------- */
+#define I2C_B_CTL_STATUS (I2C_B_CONTROL_REG_BASE + 0x00)
+#define I2C_B_ADDR (I2C_B_CONTROL_REG_BASE + 0x04)
+#define I2C_B_SW_CTL (I2C_B_CONTROL_REG_BASE + 0x08)
+#define I2C_B_TIME_OUT_CNT (I2C_B_CONTROL_REG_BASE + 0x0C)
+#define I2C_B_FIFO_STATUS (I2C_B_CONTROL_REG_BASE + 0x10)
+#define I2C_B_FS_EN (I2C_B_CONTROL_REG_BASE + 0x14)
+#define I2C_B_FIFO_DATA (I2C_B_CONTROL_REG_BASE + 0x20)
+
+#define VIDEO_CTRL_STATUS_A (ATV_PORTA_CONTROL_REG_BASE + 0x04)
+
+/* -------- Digital TV control register, Port A -------- */
+#define MPEG2_CTRL_A (DTV_PORTA_CONTROL_REG_BASE + 0x00)
+#define SERIAL_IN_ADDR_A (DTV_PORTA_CONTROL_REG_BASE + 0x4C)
+#define VLD_CNT_ADDR_A (DTV_PORTA_CONTROL_REG_BASE + 0x60)
+#define ERR_CNT_ADDR_A (DTV_PORTA_CONTROL_REG_BASE + 0x64)
+#define BRD_CNT_ADDR_A (DTV_PORTA_CONTROL_REG_BASE + 0x68)
+
+/* -------- DMA Control Register, Port A -------- */
+#define DMA_PORTA_CHAN0_ADDR_LOW (DMA_PORTA_CONTROL_REG_BASE + 0x00)
+#define DMA_PORTA_CHAN0_ADDR_HI (DMA_PORTA_CONTROL_REG_BASE + 0x04)
+#define DMA_PORTA_CHAN0_TRANS_STATE (DMA_PORTA_CONTROL_REG_BASE + 0x08)
+#define DMA_PORTA_CHAN0_CONTROL (DMA_PORTA_CONTROL_REG_BASE + 0x0C)
+#define DMA_PORTA_CHAN1_ADDR_LOW (DMA_PORTA_CONTROL_REG_BASE + 0x10)
+#define DMA_PORTA_CHAN1_ADDR_HI (DMA_PORTA_CONTROL_REG_BASE + 0x14)
+#define DMA_PORTA_CHAN1_TRANS_STATE (DMA_PORTA_CONTROL_REG_BASE + 0x18)
+#define DMA_PORTA_CHAN1_CONTROL (DMA_PORTA_CONTROL_REG_BASE + 0x1C)
+#define DMA_PORTA_MANAGEMENT (DMA_PORTA_CONTROL_REG_BASE + 0x20)
+#define VIDEO_CTRL_STATUS_B (ATV_PORTB_CONTROL_REG_BASE + 0x04)
+
+/* -------- Digital TV control register, Port B -------- */
+#define MPEG2_CTRL_B (DTV_PORTB_CONTROL_REG_BASE + 0x00)
+#define SERIAL_IN_ADDR_B (DTV_PORTB_CONTROL_REG_BASE + 0x4C)
+#define VLD_CNT_ADDR_B (DTV_PORTB_CONTROL_REG_BASE + 0x60)
+#define ERR_CNT_ADDR_B (DTV_PORTB_CONTROL_REG_BASE + 0x64)
+#define BRD_CNT_ADDR_B (DTV_PORTB_CONTROL_REG_BASE + 0x68)
+
+/* -------- AES control register, Port B -------- */
+#define AES_CTRL_B (AES_PORTB_CONTROL_REG_BASE + 0x00)
+#define AES_KEY_BASE_B (AES_PORTB_CONTROL_REG_BASE + 0x04)
+
+/* -------- DMA Control Register, Port B -------- */
+#define DMA_PORTB_CHAN0_ADDR_LOW (DMA_PORTB_CONTROL_REG_BASE + 0x00)
+#define DMA_PORTB_CHAN0_ADDR_HI (DMA_PORTB_CONTROL_REG_BASE + 0x04)
+#define DMA_PORTB_CHAN0_TRANS_STATE (DMA_PORTB_CONTROL_REG_BASE + 0x08)
+#define DMA_PORTB_CHAN0_CONTROL (DMA_PORTB_CONTROL_REG_BASE + 0x0C)
+#define DMA_PORTB_CHAN1_ADDR_LOW (DMA_PORTB_CONTROL_REG_BASE + 0x10)
+#define DMA_PORTB_CHAN1_ADDR_HI (DMA_PORTB_CONTROL_REG_BASE + 0x14)
+#define DMA_PORTB_CHAN1_TRANS_STATE (DMA_PORTB_CONTROL_REG_BASE + 0x18)
+#define DMA_PORTB_CHAN1_CONTROL (DMA_PORTB_CONTROL_REG_BASE + 0x1C)
+#define DMA_PORTB_MANAGEMENT (DMA_PORTB_CONTROL_REG_BASE + 0x20)
+
+#define DMA_TRANS_UNIT_188 (0x00000007)
+
+/* -------- Macro define of 24 interrupt resource --------*/
+#define DMA_A_CHAN0_DONE_INT (0x00000001)
+#define DMA_A_CHAN1_DONE_INT (0x00000002)
+#define DMA_B_CHAN0_DONE_INT (0x00000004)
+#define DMA_B_CHAN1_DONE_INT (0x00000008)
+#define DMA_C_CHAN0_DONE_INT (0x00000010)
+#define DMA_C_CHAN1_DONE_INT (0x00000020)
+#define DMA_D_CHAN0_DONE_INT (0x00000040)
+#define DMA_D_CHAN1_DONE_INT (0x00000080)
+#define DATA_BUF_OVERFLOW_INT (0x00000100)
+#define UART_0_X_INT (0x00000200)
+#define UART_1_X_INT (0x00000400)
+#define IR_X_INT (0x00000800)
+#define GPIO_0_INT (0x00001000)
+#define GPIO_1_INT (0x00002000)
+#define GPIO_2_INT (0x00004000)
+#define GPIO_3_INT (0x00008000)
+#define ALL_INT (0x0000FFFF)
+
+/* software I2C bit mask */
+#define SW_I2C_MSK_MODE 0x01
+#define SW_I2C_MSK_CLK_OUT 0x02
+#define SW_I2C_MSK_DAT_OUT 0x04
+#define SW_I2C_MSK_CLK_EN 0x08
+#define SW_I2C_MSK_DAT_EN 0x10
+#define SW_I2C_MSK_DAT_IN 0x40
+#define SW_I2C_MSK_CLK_IN 0x80
+
+#define SMI_VID 0x1ADE
+#define SMI_PID 0x3038
+#define SMI_TS_DMA_BUF_SIZE (1024 * 188)
+
+struct smi_cfg_info {
+#define SMI_DVBSKY_S952 0
+#define SMI_DVBSKY_S950 1
+#define SMI_DVBSKY_T9580 2
+#define SMI_DVBSKY_T982 3
+ int type;
+ char *name;
+#define SMI_TS_NULL 0
+#define SMI_TS_DMA_SINGLE 1
+#define SMI_TS_DMA_BOTH 3
+/* SMI_TS_NULL: not use;
+ * SMI_TS_DMA_SINGLE: use DMA 0 only;
+ * SMI_TS_DMA_BOTH:use DMA 0 and 1.*/
+ int ts_0;
+ int ts_1;
+#define DVBSKY_FE_NULL 0
+#define DVBSKY_FE_M88RS6000 1
+#define DVBSKY_FE_M88DS3103 2
+#define DVBSKY_FE_SIT2 3
+ int fe_0;
+ int fe_1;
+};
+
+struct smi_port {
+ struct smi_dev *dev;
+ int idx;
+ int enable;
+ int fe_type;
+ /* regs */
+ u32 DMA_CHAN0_ADDR_LOW;
+ u32 DMA_CHAN0_ADDR_HI;
+ u32 DMA_CHAN0_TRANS_STATE;
+ u32 DMA_CHAN0_CONTROL;
+ u32 DMA_CHAN1_ADDR_LOW;
+ u32 DMA_CHAN1_ADDR_HI;
+ u32 DMA_CHAN1_TRANS_STATE;
+ u32 DMA_CHAN1_CONTROL;
+ u32 DMA_MANAGEMENT;
+ /* dma */
+ dma_addr_t dma_addr[2];
+ u8 *cpu_addr[2];
+ u32 _dmaInterruptCH0;
+ u32 _dmaInterruptCH1;
+ u32 _int_status;
+ struct tasklet_struct tasklet;
+ /* dvb */
+ struct dmx_frontend hw_frontend;
+ struct dmx_frontend mem_frontend;
+ struct dmxdev dmxdev;
+ struct dvb_adapter dvb_adapter;
+ struct dvb_demux demux;
+ struct dvb_net dvbnet;
+ int users;
+ struct dvb_frontend *fe;
+ /* frontend i2c module */
+ struct i2c_client *i2c_client_demod;
+ struct i2c_client *i2c_client_tuner;
+};
+
+struct smi_dev {
+ int nr;
+ struct smi_cfg_info *info;
+
+ /* pcie */
+ struct pci_dev *pci_dev;
+ u32 __iomem *lmmio;
+
+ /* ts port */
+ struct smi_port ts_port[2];
+
+ /* i2c */
+ struct i2c_adapter i2c_bus[2];
+ struct i2c_algo_bit_data i2c_bit[2];
+};
+
+#define smi_read(reg) readl(dev->lmmio + ((reg)>>2))
+#define smi_write(reg, value) writel((value), dev->lmmio + ((reg)>>2))
+
+#define smi_andor(reg, mask, value) \
+ writel((readl(dev->lmmio+((reg)>>2)) & ~(mask)) |\
+ ((value) & (mask)), dev->lmmio+((reg)>>2))
+
+#define smi_set(reg, bit) smi_andor((reg), (bit), (bit))
+#define smi_clear(reg, bit) smi_andor((reg), (bit), 0)
+
+#endif /* #ifndef _SMI_PCIE_H_ */
diff --git a/drivers/media/pci/solo6x10/solo6x10-core.c b/drivers/media/pci/solo6x10/solo6x10-core.c
index 172583d..8cbe6b4 100644
--- a/drivers/media/pci/solo6x10/solo6x10-core.c
+++ b/drivers/media/pci/solo6x10/solo6x10-core.c
@@ -105,11 +105,8 @@
if (!status)
return IRQ_NONE;
- if (status & ~solo_dev->irq_mask) {
- solo_reg_write(solo_dev, SOLO_IRQ_STAT,
- status & ~solo_dev->irq_mask);
- status &= solo_dev->irq_mask;
- }
+ /* Acknowledge all interrupts immediately */
+ solo_reg_write(solo_dev, SOLO_IRQ_STAT, status);
if (status & SOLO_IRQ_PCI_ERR)
solo_p2m_error_isr(solo_dev);
@@ -132,9 +129,6 @@
if (status & SOLO_IRQ_G723)
solo_g723_isr(solo_dev);
- /* Clear all interrupts handled */
- solo_reg_write(solo_dev, SOLO_IRQ_STAT, status);
-
return IRQ_HANDLED;
}
diff --git a/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c
index 28023f9..6e933d3 100644
--- a/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c
+++ b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c
@@ -463,7 +463,6 @@
struct solo_dev *solo_dev = solo_enc->solo_dev;
struct sg_table *vbuf = vb2_dma_sg_plane_desc(vb, 0);
int frame_size;
- int ret;
vb->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
@@ -473,22 +472,10 @@
frame_size = ALIGN(vop_jpeg_size(vh) + solo_enc->jpeg_len, DMA_ALIGN);
vb2_set_plane_payload(vb, 0, vop_jpeg_size(vh) + solo_enc->jpeg_len);
- /* may discard all previous data in vbuf->sgl */
- if (!dma_map_sg(&solo_dev->pdev->dev, vbuf->sgl, vbuf->nents,
- DMA_FROM_DEVICE))
- return -ENOMEM;
- ret = solo_send_desc(solo_enc, solo_enc->jpeg_len, vbuf,
+ return solo_send_desc(solo_enc, solo_enc->jpeg_len, vbuf,
vop_jpeg_offset(vh) - SOLO_JPEG_EXT_ADDR(solo_dev),
frame_size, SOLO_JPEG_EXT_ADDR(solo_dev),
SOLO_JPEG_EXT_SIZE(solo_dev));
- dma_unmap_sg(&solo_dev->pdev->dev, vbuf->sgl, vbuf->nents,
- DMA_FROM_DEVICE);
-
- /* add the header only after dma_unmap_sg() */
- sg_copy_from_buffer(vbuf->sgl, vbuf->nents,
- solo_enc->jpeg_header, solo_enc->jpeg_len);
-
- return ret;
}
static int solo_fill_mpeg(struct solo_enc_dev *solo_enc,
@@ -498,7 +485,6 @@
struct sg_table *vbuf = vb2_dma_sg_plane_desc(vb, 0);
int frame_off, frame_size;
int skip = 0;
- int ret;
if (vb2_plane_size(vb, 0) < vop_mpeg_size(vh))
return -EIO;
@@ -521,21 +507,9 @@
sizeof(*vh)) % SOLO_MP4E_EXT_SIZE(solo_dev);
frame_size = ALIGN(vop_mpeg_size(vh) + skip, DMA_ALIGN);
- /* may discard all previous data in vbuf->sgl */
- if (!dma_map_sg(&solo_dev->pdev->dev, vbuf->sgl, vbuf->nents,
- DMA_FROM_DEVICE))
- return -ENOMEM;
- ret = solo_send_desc(solo_enc, skip, vbuf, frame_off, frame_size,
+ return solo_send_desc(solo_enc, skip, vbuf, frame_off, frame_size,
SOLO_MP4E_EXT_ADDR(solo_dev),
SOLO_MP4E_EXT_SIZE(solo_dev));
- dma_unmap_sg(&solo_dev->pdev->dev, vbuf->sgl, vbuf->nents,
- DMA_FROM_DEVICE);
-
- /* add the header only after dma_unmap_sg() */
- if (!vop_type(vh))
- sg_copy_from_buffer(vbuf->sgl, vbuf->nents,
- solo_enc->vop, solo_enc->vop_len);
- return ret;
}
static int solo_enc_fillbuf(struct solo_enc_dev *solo_enc,
@@ -703,9 +677,7 @@
if (timeout == -ERESTARTSYS || kthread_should_stop())
break;
- solo_irq_off(solo_dev, SOLO_IRQ_ENCODER);
solo_handle_ring(solo_dev);
- solo_irq_on(solo_dev, SOLO_IRQ_ENCODER);
try_to_freeze();
}
@@ -720,7 +692,10 @@
unsigned int *num_planes, unsigned int sizes[],
void *alloc_ctxs[])
{
+ struct solo_enc_dev *solo_enc = vb2_get_drv_priv(q);
+
sizes[0] = FRAME_BUF_SIZE;
+ alloc_ctxs[0] = solo_enc->alloc_ctx;
*num_planes = 1;
if (*num_buffers < MIN_VID_BUFFERS)
@@ -770,26 +745,51 @@
static int solo_enc_start_streaming(struct vb2_queue *q, unsigned int count)
{
struct solo_enc_dev *solo_enc = vb2_get_drv_priv(q);
- int ret;
- ret = solo_enc_on(solo_enc);
- if (ret)
- return ret;
- return solo_ring_start(solo_enc->solo_dev);
+ return solo_enc_on(solo_enc);
}
static void solo_enc_stop_streaming(struct vb2_queue *q)
{
struct solo_enc_dev *solo_enc = vb2_get_drv_priv(q);
+ unsigned long flags;
+ spin_lock_irqsave(&solo_enc->av_lock, flags);
solo_enc_off(solo_enc);
- INIT_LIST_HEAD(&solo_enc->vidq_active);
- solo_ring_stop(solo_enc->solo_dev);
+ while (!list_empty(&solo_enc->vidq_active)) {
+ struct solo_vb2_buf *buf = list_entry(
+ solo_enc->vidq_active.next,
+ struct solo_vb2_buf, list);
+
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+ spin_unlock_irqrestore(&solo_enc->av_lock, flags);
+}
+
+static void solo_enc_buf_finish(struct vb2_buffer *vb)
+{
+ struct solo_enc_dev *solo_enc = vb2_get_drv_priv(vb->vb2_queue);
+ struct sg_table *vbuf = vb2_dma_sg_plane_desc(vb, 0);
+
+ switch (solo_enc->fmt) {
+ case V4L2_PIX_FMT_MPEG4:
+ case V4L2_PIX_FMT_H264:
+ if (vb->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME)
+ sg_copy_from_buffer(vbuf->sgl, vbuf->nents,
+ solo_enc->vop, solo_enc->vop_len);
+ break;
+ default: /* V4L2_PIX_FMT_MJPEG */
+ sg_copy_from_buffer(vbuf->sgl, vbuf->nents,
+ solo_enc->jpeg_header, solo_enc->jpeg_len);
+ break;
+ }
}
static struct vb2_ops solo_enc_video_qops = {
.queue_setup = solo_enc_queue_setup,
.buf_queue = solo_enc_buf_queue,
+ .buf_finish = solo_enc_buf_finish,
.start_streaming = solo_enc_start_streaming,
.stop_streaming = solo_enc_stop_streaming,
.wait_prepare = vb2_ops_wait_prepare,
@@ -1263,6 +1263,11 @@
return ERR_PTR(-ENOMEM);
hdl = &solo_enc->hdl;
+ solo_enc->alloc_ctx = vb2_dma_sg_init_ctx(&solo_dev->pdev->dev);
+ if (IS_ERR(solo_enc->alloc_ctx)) {
+ ret = PTR_ERR(solo_enc->alloc_ctx);
+ goto hdl_free;
+ }
v4l2_ctrl_handler_init(hdl, 10);
v4l2_ctrl_new_std(hdl, &solo_ctrl_ops,
V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
@@ -1366,6 +1371,7 @@
solo_enc->desc_items, solo_enc->desc_dma);
hdl_free:
v4l2_ctrl_handler_free(hdl);
+ vb2_dma_sg_cleanup_ctx(solo_enc->alloc_ctx);
kfree(solo_enc);
return ERR_PTR(ret);
}
@@ -1375,8 +1381,12 @@
if (solo_enc == NULL)
return;
+ pci_free_consistent(solo_enc->solo_dev->pdev,
+ sizeof(struct solo_p2m_desc) * solo_enc->desc_nelts,
+ solo_enc->desc_items, solo_enc->desc_dma);
video_unregister_device(solo_enc->vfd);
v4l2_ctrl_handler_free(&solo_enc->hdl);
+ vb2_dma_sg_cleanup_ctx(solo_enc->alloc_ctx);
kfree(solo_enc);
}
@@ -1419,13 +1429,15 @@
solo_dev->v4l2_enc[0]->vfd->num,
solo_dev->v4l2_enc[solo_dev->nr_chans - 1]->vfd->num);
- return 0;
+ return solo_ring_start(solo_dev);
}
void solo_enc_v4l2_exit(struct solo_dev *solo_dev)
{
int i;
+ solo_ring_stop(solo_dev);
+
for (i = 0; i < solo_dev->nr_chans; i++)
solo_enc_free(solo_dev->v4l2_enc[i]);
diff --git a/drivers/media/pci/solo6x10/solo6x10.h b/drivers/media/pci/solo6x10/solo6x10.h
index 72017b7..bd8edfa 100644
--- a/drivers/media/pci/solo6x10/solo6x10.h
+++ b/drivers/media/pci/solo6x10/solo6x10.h
@@ -180,6 +180,7 @@
u32 sequence;
struct vb2_queue vidq;
struct list_head vidq_active;
+ void *alloc_ctx;
int desc_count;
int desc_nelts;
struct solo_p2m_desc *desc_items;
diff --git a/drivers/media/pci/ttpci/budget-patch.c b/drivers/media/pci/ttpci/budget-patch.c
index 2cb35c2..a4d8867 100644
--- a/drivers/media/pci/ttpci/budget-patch.c
+++ b/drivers/media/pci/ttpci/budget-patch.c
@@ -490,7 +490,7 @@
if(detected == 0)
printk("budget-patch not detected or saa7146 in non-default state.\n"
- "try enabling ressetting of 7146 with MASK_31 in MC1 register\n");
+ "try enabling resetting of 7146 with MASK_31 in MC1 register\n");
else
printk("BUDGET-PATCH DETECTED.\n");
diff --git a/drivers/media/pci/tw68/Kconfig b/drivers/media/pci/tw68/Kconfig
index 61b3e24..45b845e 100644
--- a/drivers/media/pci/tw68/Kconfig
+++ b/drivers/media/pci/tw68/Kconfig
@@ -2,7 +2,6 @@
tristate "Techwell tw68x Video For Linux"
depends on m
depends on VIDEO_DEV && PCI && VIDEO_V4L2
- depends on I2C_ALGOBIT
select VIDEOBUF2_DMA_SG
---help---
Support for Techwell tw68xx based frame grabber boards.
diff --git a/drivers/media/pci/tw68/tw68-core.c b/drivers/media/pci/tw68/tw68-core.c
index a6fb48c..c135165 100644
--- a/drivers/media/pci/tw68/tw68-core.c
+++ b/drivers/media/pci/tw68/tw68-core.c
@@ -304,13 +304,19 @@
/* Then do any initialisation wanted before interrupts are on */
tw68_hw_init1(dev);
+ dev->alloc_ctx = vb2_dma_sg_init_ctx(&pci_dev->dev);
+ if (IS_ERR(dev->alloc_ctx)) {
+ err = PTR_ERR(dev->alloc_ctx);
+ goto fail3;
+ }
+
/* get irq */
err = devm_request_irq(&pci_dev->dev, pci_dev->irq, tw68_irq,
- IRQF_SHARED | IRQF_DISABLED, dev->name, dev);
+ IRQF_SHARED, dev->name, dev);
if (err < 0) {
pr_err("%s: can't get IRQ %d\n",
dev->name, pci_dev->irq);
- goto fail3;
+ goto fail4;
}
/*
@@ -324,7 +330,7 @@
if (err < 0) {
pr_err("%s: can't register video device\n",
dev->name);
- goto fail4;
+ goto fail5;
}
tw_setl(TW68_INTMASK, dev->pci_irqmask);
@@ -333,8 +339,10 @@
return 0;
-fail4:
+fail5:
video_unregister_device(&dev->vdev);
+fail4:
+ vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
fail3:
iounmap(dev->lmmio);
fail2:
@@ -358,6 +366,7 @@
/* unregister */
video_unregister_device(&dev->vdev);
v4l2_ctrl_handler_free(&dev->hdl);
+ vb2_dma_sg_cleanup_ctx(dev->alloc_ctx);
/* release resources */
iounmap(dev->lmmio);
diff --git a/drivers/media/pci/tw68/tw68-video.c b/drivers/media/pci/tw68/tw68-video.c
index 25f0cc8..abe6314 100644
--- a/drivers/media/pci/tw68/tw68-video.c
+++ b/drivers/media/pci/tw68/tw68-video.c
@@ -384,6 +384,7 @@
unsigned tot_bufs = q->num_buffers + *num_buffers;
sizes[0] = (dev->fmt->depth * dev->width * dev->height) >> 3;
+ alloc_ctxs[0] = dev->alloc_ctx;
/*
* We allow create_bufs, but only if the sizeimage is the same as the
* current sizeimage. The tw68_buffer_count calculation becomes quite
@@ -461,17 +462,12 @@
struct tw68_buf *buf = container_of(vb, struct tw68_buf, vb);
struct sg_table *dma = vb2_dma_sg_plane_desc(vb, 0);
unsigned size, bpl;
- int rc;
size = (dev->width * dev->height * dev->fmt->depth) >> 3;
if (vb2_plane_size(vb, 0) < size)
return -EINVAL;
vb2_set_plane_payload(vb, 0, size);
- rc = dma_map_sg(&dev->pci->dev, dma->sgl, dma->nents, DMA_FROM_DEVICE);
- if (!rc)
- return -EIO;
-
bpl = (dev->width * dev->fmt->depth) >> 3;
switch (dev->field) {
case V4L2_FIELD_TOP:
@@ -505,11 +501,8 @@
{
struct vb2_queue *vq = vb->vb2_queue;
struct tw68_dev *dev = vb2_get_drv_priv(vq);
- struct sg_table *dma = vb2_dma_sg_plane_desc(vb, 0);
struct tw68_buf *buf = container_of(vb, struct tw68_buf, vb);
- dma_unmap_sg(&dev->pci->dev, dma->sgl, dma->nents, DMA_FROM_DEVICE);
-
pci_free_consistent(dev->pci, buf->size, buf->cpu, buf->dma);
}
diff --git a/drivers/media/pci/tw68/tw68.h b/drivers/media/pci/tw68/tw68.h
index 2c8abe2..7a7501b 100644
--- a/drivers/media/pci/tw68/tw68.h
+++ b/drivers/media/pci/tw68/tw68.h
@@ -181,6 +181,7 @@
unsigned field;
struct vb2_queue vidq;
struct list_head active;
+ void *alloc_ctx;
/* various v4l controls */
const struct tw68_tvnorm *tvnorm; /* video */
diff --git a/drivers/media/pci/zoran/zoran_driver.c b/drivers/media/pci/zoran/zoran_driver.c
index 099d5fb..2b25d31 100644
--- a/drivers/media/pci/zoran/zoran_driver.c
+++ b/drivers/media/pci/zoran/zoran_driver.c
@@ -1528,8 +1528,9 @@
strncpy(cap->driver, "zoran", sizeof(cap->driver)-1);
snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s",
pci_name(zr->pci_dev));
- cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OVERLAY;
+ cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OVERLAY;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
diff --git a/drivers/media/pci/zoran/zoran_procfs.c b/drivers/media/pci/zoran/zoran_procfs.c
index f7ceee0..4376527 100644
--- a/drivers/media/pci/zoran/zoran_procfs.c
+++ b/drivers/media/pci/zoran/zoran_procfs.c
@@ -157,8 +157,8 @@
return -EFAULT;
}
string[count] = 0;
- dprintk(4, KERN_INFO "%s: write_proc: name=%s count=%zu zr=%p\n",
- ZR_DEVNAME(zr), file->f_path.dentry->d_name.name, count, zr);
+ dprintk(4, KERN_INFO "%s: write_proc: name=%pD count=%zu zr=%p\n",
+ ZR_DEVNAME(zr), file, count, zr);
ldelim = " \t\n";
tdelim = "=";
line = strpbrk(sp, ldelim);
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 56ec66e..4a5f12a 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -58,7 +58,7 @@
In doubt, say N.
config VIDEO_TIMBERDALE
- depends on !BACKPORT_KERNEL_3_4
+ depends on !KERNEL_3_4
tristate "Support for timberdale Video In/LogiWIN"
depends on m
depends on VIDEO_V4L2 && I2C && DMADEVICES
@@ -70,15 +70,6 @@
---help---
Add support for the Video In peripherial of the timberdale FPGA.
-config VIDEO_VINO
- tristate "SGI Vino Video For Linux"
- depends on m
- depends on I2C && SGI_IP22 && VIDEO_V4L2
- select VIDEO_SAA7191 if MEDIA_SUBDRV_AUTOSELECT
- help
- Say Y here to build in support for the Vino video input system found
- on SGI Indy machines.
-
config VIDEO_M32R_AR
tristate "AR devices"
depends on m
@@ -122,7 +113,7 @@
tristate "Samsung S3C24XX/S3C64XX SoC Camera Interface driver"
depends on m
depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
- depends on PM_RUNTIME
+ depends on PM
depends on ARCH_S3C64XX || PLAT_S3C24XX || COMPILE_TEST
depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
@@ -166,7 +157,7 @@
H.264, MPEG-4, and other video formats.
config VIDEO_MEM2MEM_DEINTERLACE
- depends on !BACKPORT_KERNEL_3_3
+ depends on !KERNEL_3_3
tristate "Deinterlace support"
depends on m
depends on VIDEO_DEV && VIDEO_V4L2 && DMA_ENGINE
@@ -180,7 +171,7 @@
tristate "Samsung S5P and EXYNOS4 G2D 2d graphics accelerator driver"
depends on m
depends on VIDEO_DEV && VIDEO_V4L2
- depends on PLAT_S5P || ARCH_EXYNOS || COMPILE_TEST
+ depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
@@ -193,7 +184,7 @@
tristate "Samsung S5P/Exynos3250/Exynos4 JPEG codec driver"
depends on m
depends on VIDEO_DEV && VIDEO_V4L2
- depends on PLAT_S5P || ARCH_EXYNOS || COMPILE_TEST
+ depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
@@ -205,7 +196,7 @@
tristate "Samsung S5P MFC Video Codec"
depends on m
depends on VIDEO_DEV && VIDEO_V4L2
- depends on PLAT_S5P || ARCH_EXYNOS || COMPILE_TEST
+ depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
default n
@@ -248,7 +239,7 @@
SH-Mobile SoCs.
config VIDEO_RENESAS_VSP1
- depends on !BACKPORT_KERNEL_3_4
+ depends on !KERNEL_3_4
tristate "Renesas VSP1 Video Processing Engine"
depends on m
depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAS_DMA
@@ -289,8 +280,8 @@
source "drivers/media/platform/vivid/Kconfig"
-config VIDEO_MEM2MEM_TESTDEV
- tristate "Virtual test device for mem2mem framework"
+config VIDEO_VIM2M
+ tristate "Virtual Memory-to-Memory Driver"
depends on m
depends on VIDEO_DEV && VIDEO_V4L2
select VIDEOBUF2_VMALLOC
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 140e3e5..6449a06 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -2,9 +2,6 @@
# Makefile for the video capture/playback device drivers.
#
-obj-$(CPTCFG_VIDEO_VINO) += indycam.o
-obj-$(CPTCFG_VIDEO_VINO) += vino.o
-
obj-$(CPTCFG_VIDEO_TIMBERDALE) += timblogiw.o
obj-$(CPTCFG_VIDEO_M32R_AR_M64278) += arv.o
@@ -17,7 +14,7 @@
obj-$(CPTCFG_VIDEO_VIU) += fsl-viu.o
obj-$(CPTCFG_VIDEO_VIVID) += vivid/
-obj-$(CPTCFG_VIDEO_MEM2MEM_TESTDEV) += mem2mem_testdev.o
+obj-$(CPTCFG_VIDEO_VIM2M) += vim2m.o
obj-$(CPTCFG_VIDEO_TI_VPE) += ti-vpe/
diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c
index 9b5daa6..8f66986 100644
--- a/drivers/media/platform/blackfin/bfin_capture.c
+++ b/drivers/media/platform/blackfin/bfin_capture.c
@@ -49,7 +49,7 @@
struct bcap_format {
char *desc;
u32 pixelformat;
- enum v4l2_mbus_pixelcode mbus_code;
+ u32 mbus_code;
int bpp; /* bits per pixel */
int dlen; /* data length for ppi in bits */
};
@@ -116,35 +116,35 @@
{
.desc = "YCbCr 4:2:2 Interleaved UYVY",
.pixelformat = V4L2_PIX_FMT_UYVY,
- .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
.bpp = 16,
.dlen = 8,
},
{
.desc = "YCbCr 4:2:2 Interleaved YUYV",
.pixelformat = V4L2_PIX_FMT_YUYV,
- .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
.bpp = 16,
.dlen = 8,
},
{
.desc = "YCbCr 4:2:2 Interleaved UYVY",
.pixelformat = V4L2_PIX_FMT_UYVY,
- .mbus_code = V4L2_MBUS_FMT_UYVY8_1X16,
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16,
.bpp = 16,
.dlen = 16,
},
{
.desc = "RGB 565",
.pixelformat = V4L2_PIX_FMT_RGB565,
- .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE,
+ .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE,
.bpp = 16,
.dlen = 8,
},
{
.desc = "RGB 444",
.pixelformat = V4L2_PIX_FMT_RGB444,
- .mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE,
+ .mbus_code = MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE,
.bpp = 16,
.dlen = 8,
},
@@ -161,7 +161,7 @@
static int bcap_init_sensor_formats(struct bcap_device *bcap_dev)
{
- enum v4l2_mbus_pixelcode code;
+ u32 code;
struct bcap_format *sf;
unsigned int num_formats = 0;
int i, j;
@@ -349,18 +349,6 @@
spin_unlock_irqrestore(&bcap_dev->lock, flags);
}
-static void bcap_lock(struct vb2_queue *vq)
-{
- struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
- mutex_lock(&bcap_dev->mutex);
-}
-
-static void bcap_unlock(struct vb2_queue *vq)
-{
- struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
- mutex_unlock(&bcap_dev->mutex);
-}
-
static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
@@ -457,8 +445,8 @@
.buf_prepare = bcap_buffer_prepare,
.buf_cleanup = bcap_buffer_cleanup,
.buf_queue = bcap_buffer_queue,
- .wait_prepare = bcap_unlock,
- .wait_finish = bcap_lock,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
.start_streaming = bcap_start_streaming,
.stop_streaming = bcap_stop_streaming,
};
@@ -841,7 +829,8 @@
{
struct bcap_device *bcap_dev = video_drvdata(file);
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver));
strlcpy(cap->bus_info, "Blackfin Platform", sizeof(cap->bus_info));
strlcpy(cap->card, bcap_dev->cfg->card_name, sizeof(cap->card));
@@ -995,6 +984,7 @@
q->ops = &bcap_video_qops;
q->mem_ops = &vb2_dma_contig_memops;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &bcap_dev->mutex;
ret = vb2_queue_init(q);
if (ret)
@@ -1130,7 +1120,6 @@
static struct platform_driver bcap_driver = {
.driver = {
.name = CAPTURE_DRV_NAME,
- .owner = THIS_MODULE,
},
.probe = bcap_probe,
.remove = bcap_remove,
diff --git a/drivers/media/platform/coda/Makefile b/drivers/media/platform/coda/Makefile
index fb7529f..eee8998 100644
--- a/drivers/media/platform/coda/Makefile
+++ b/drivers/media/platform/coda/Makefile
@@ -1,3 +1,3 @@
-coda-objs := coda-common.o coda-bit.o coda-h264.o
+coda-objs := coda-common.o coda-bit.o coda-h264.o coda-jpeg.o
obj-$(CPTCFG_VIDEO_CODA) += coda.o
diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c
index 9b8ea8b..b4029ae 100644
--- a/drivers/media/platform/coda/coda-bit.c
+++ b/drivers/media/platform/coda/coda-bit.c
@@ -217,11 +217,34 @@
void coda_fill_bitstream(struct coda_ctx *ctx)
{
struct vb2_buffer *src_buf;
- struct coda_timestamp *ts;
+ struct coda_buffer_meta *meta;
+ u32 start;
while (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) > 0) {
+ /*
+ * Only queue a single JPEG into the bitstream buffer, except
+ * to increase payload over 512 bytes or if in hold state.
+ */
+ if (ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG &&
+ (coda_get_bitstream_payload(ctx) >= 512) && !ctx->hold)
+ break;
+
src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+ /* Drop frames that do not start/end with a SOI/EOI markers */
+ if (ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG &&
+ !coda_jpeg_check_buffer(ctx, src_buf)) {
+ v4l2_err(&ctx->dev->v4l2_dev,
+ "dropping invalid JPEG frame\n");
+ src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
+ continue;
+ }
+
+ /* Buffer start position */
+ start = ctx->bitstream_fifo.kfifo.in &
+ ctx->bitstream_fifo.kfifo.mask;
+
if (coda_bitstream_try_queue(ctx, src_buf)) {
/*
* Source buffer is queued in the bitstream ringbuffer;
@@ -229,12 +252,16 @@
*/
src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
- ts = kmalloc(sizeof(*ts), GFP_KERNEL);
- if (ts) {
- ts->sequence = src_buf->v4l2_buf.sequence;
- ts->timecode = src_buf->v4l2_buf.timecode;
- ts->timestamp = src_buf->v4l2_buf.timestamp;
- list_add_tail(&ts->list, &ctx->timestamp_list);
+ meta = kmalloc(sizeof(*meta), GFP_KERNEL);
+ if (meta) {
+ meta->sequence = src_buf->v4l2_buf.sequence;
+ meta->timecode = src_buf->v4l2_buf.timecode;
+ meta->timestamp = src_buf->v4l2_buf.timestamp;
+ meta->start = start;
+ meta->end = ctx->bitstream_fifo.kfifo.in &
+ ctx->bitstream_fifo.kfifo.mask;
+ list_add_tail(&meta->list,
+ &ctx->buffer_meta_list);
}
v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
@@ -691,6 +718,7 @@
struct vb2_buffer *buf;
int gamma, ret, value;
u32 dst_fourcc;
+ u32 stride;
q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
@@ -710,6 +738,14 @@
return -EFAULT;
}
+ if (dst_fourcc == V4L2_PIX_FMT_JPEG) {
+ if (!ctx->params.jpeg_qmat_tab[0])
+ ctx->params.jpeg_qmat_tab[0] = kmalloc(64, GFP_KERNEL);
+ if (!ctx->params.jpeg_qmat_tab[1])
+ ctx->params.jpeg_qmat_tab[1] = kmalloc(64, GFP_KERNEL);
+ coda_set_jpeg_compression_quality(ctx, ctx->params.jpeg_quality);
+ }
+
mutex_lock(&dev->coda_mutex);
coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR);
@@ -729,10 +765,10 @@
break;
}
- value = coda_read(dev, CODA_REG_BIT_FRAME_MEM_CTRL);
- value &= ~(1 << 2 | 0x7 << 9);
- ctx->frame_mem_ctrl = value;
- coda_write(dev, value, CODA_REG_BIT_FRAME_MEM_CTRL);
+ ctx->frame_mem_ctrl &= ~CODA_FRAME_CHROMA_INTERLEAVE;
+ if (q_data_src->fourcc == V4L2_PIX_FMT_NV12)
+ ctx->frame_mem_ctrl |= CODA_FRAME_CHROMA_INTERLEAVE;
+ coda_write(dev, ctx->frame_mem_ctrl, CODA_REG_BIT_FRAME_MEM_CTRL);
if (dev->devtype->product == CODA_DX6) {
/* Configure the coda */
@@ -741,6 +777,7 @@
}
/* Could set rotation here if needed */
+ value = 0;
switch (dev->devtype->product) {
case CODA_DX6:
value = (q_data_src->width & CODADX6_PICWIDTH_MASK)
@@ -764,6 +801,8 @@
<< CODA_PICHEIGHT_OFFSET;
}
coda_write(dev, value, CODA_CMD_ENC_SEQ_SRC_SIZE);
+ if (dst_fourcc == V4L2_PIX_FMT_JPEG)
+ ctx->params.framerate = 0;
coda_write(dev, ctx->params.framerate,
CODA_CMD_ENC_SEQ_SRC_F_RATE);
@@ -797,6 +836,16 @@
}
coda_write(dev, value, CODA_CMD_ENC_SEQ_264_PARA);
break;
+ case V4L2_PIX_FMT_JPEG:
+ coda_write(dev, 0, CODA_CMD_ENC_SEQ_JPG_PARA);
+ coda_write(dev, ctx->params.jpeg_restart_interval,
+ CODA_CMD_ENC_SEQ_JPG_RST_INTERVAL);
+ coda_write(dev, 0, CODA_CMD_ENC_SEQ_JPG_THUMB_EN);
+ coda_write(dev, 0, CODA_CMD_ENC_SEQ_JPG_THUMB_SIZE);
+ coda_write(dev, 0, CODA_CMD_ENC_SEQ_JPG_THUMB_OFFSET);
+
+ coda_jpeg_write_tables(ctx);
+ break;
default:
v4l2_err(v4l2_dev,
"dst format (0x%08x) invalid.\n", dst_fourcc);
@@ -804,28 +853,36 @@
goto out;
}
- switch (ctx->params.slice_mode) {
- case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE:
- value = 0;
- break;
- case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB:
- value = (ctx->params.slice_max_mb & CODA_SLICING_SIZE_MASK)
- << CODA_SLICING_SIZE_OFFSET;
- value |= (1 & CODA_SLICING_UNIT_MASK)
- << CODA_SLICING_UNIT_OFFSET;
- value |= 1 & CODA_SLICING_MODE_MASK;
- break;
- case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES:
- value = (ctx->params.slice_max_bits & CODA_SLICING_SIZE_MASK)
- << CODA_SLICING_SIZE_OFFSET;
- value |= (0 & CODA_SLICING_UNIT_MASK)
- << CODA_SLICING_UNIT_OFFSET;
- value |= 1 & CODA_SLICING_MODE_MASK;
- break;
+ /*
+ * slice mode and GOP size registers are used for thumb size/offset
+ * in JPEG mode
+ */
+ if (dst_fourcc != V4L2_PIX_FMT_JPEG) {
+ switch (ctx->params.slice_mode) {
+ case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE:
+ value = 0;
+ break;
+ case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB:
+ value = (ctx->params.slice_max_mb &
+ CODA_SLICING_SIZE_MASK)
+ << CODA_SLICING_SIZE_OFFSET;
+ value |= (1 & CODA_SLICING_UNIT_MASK)
+ << CODA_SLICING_UNIT_OFFSET;
+ value |= 1 & CODA_SLICING_MODE_MASK;
+ break;
+ case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES:
+ value = (ctx->params.slice_max_bits &
+ CODA_SLICING_SIZE_MASK)
+ << CODA_SLICING_SIZE_OFFSET;
+ value |= (0 & CODA_SLICING_UNIT_MASK)
+ << CODA_SLICING_UNIT_OFFSET;
+ value |= 1 & CODA_SLICING_MODE_MASK;
+ break;
+ }
+ coda_write(dev, value, CODA_CMD_ENC_SEQ_SLICE_MODE);
+ value = ctx->params.gop_size & CODA_GOP_SIZE_MASK;
+ coda_write(dev, value, CODA_CMD_ENC_SEQ_GOP_SIZE);
}
- coda_write(dev, value, CODA_CMD_ENC_SEQ_SLICE_MODE);
- value = ctx->params.gop_size & CODA_GOP_SIZE_MASK;
- coda_write(dev, value, CODA_CMD_ENC_SEQ_GOP_SIZE);
if (ctx->params.bitrate) {
/* Rate control enabled */
@@ -916,19 +973,24 @@
goto out;
}
- if (dev->devtype->product == CODA_960)
- ctx->num_internal_frames = 4;
- else
- ctx->num_internal_frames = 2;
- ret = coda_alloc_framebuffers(ctx, q_data_src, dst_fourcc);
- if (ret < 0) {
- v4l2_err(v4l2_dev, "failed to allocate framebuffers\n");
- goto out;
+ if (dst_fourcc != V4L2_PIX_FMT_JPEG) {
+ if (dev->devtype->product == CODA_960)
+ ctx->num_internal_frames = 4;
+ else
+ ctx->num_internal_frames = 2;
+ ret = coda_alloc_framebuffers(ctx, q_data_src, dst_fourcc);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "failed to allocate framebuffers\n");
+ goto out;
+ }
+ stride = q_data_src->bytesperline;
+ } else {
+ ctx->num_internal_frames = 0;
+ stride = 0;
}
-
coda_write(dev, ctx->num_internal_frames, CODA_CMD_SET_FRAME_BUF_NUM);
- coda_write(dev, q_data_src->bytesperline,
- CODA_CMD_SET_FRAME_BUF_STRIDE);
+ coda_write(dev, stride, CODA_CMD_SET_FRAME_BUF_STRIDE);
+
if (dev->devtype->product == CODA_7541) {
coda_write(dev, q_data_src->bytesperline,
CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE);
@@ -1036,9 +1098,10 @@
struct coda_dev *dev = ctx->dev;
int force_ipicture;
int quant_param = 0;
- u32 picture_y, picture_cb, picture_cr;
u32 pic_stream_buffer_addr, pic_stream_buffer_size;
+ u32 rot_mode = 0;
u32 dst_fourcc;
+ u32 reg;
src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
@@ -1076,7 +1139,7 @@
ctx->vpu_header_size[0] +
ctx->vpu_header_size[1] +
ctx->vpu_header_size[2];
- pic_stream_buffer_size = CODA_MAX_FRAME_SIZE -
+ pic_stream_buffer_size = q_data_dst->sizeimage -
ctx->vpu_header_size[0] -
ctx->vpu_header_size[1] -
ctx->vpu_header_size[2];
@@ -1090,7 +1153,7 @@
} else {
pic_stream_buffer_addr =
vb2_dma_contig_plane_dma_addr(dst_buf, 0);
- pic_stream_buffer_size = CODA_MAX_FRAME_SIZE;
+ pic_stream_buffer_size = q_data_dst->sizeimage;
}
if (src_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) {
@@ -1102,6 +1165,9 @@
case V4L2_PIX_FMT_MPEG4:
quant_param = ctx->params.mpeg4_intra_qp;
break;
+ case V4L2_PIX_FMT_JPEG:
+ quant_param = 30;
+ break;
default:
v4l2_warn(&ctx->dev->v4l2_dev,
"cannot set intra qp, fmt not supported\n");
@@ -1124,42 +1190,22 @@
}
/* submit */
- coda_write(dev, CODA_ROT_MIR_ENABLE | ctx->params.rot_mode,
- CODA_CMD_ENC_PIC_ROT_MODE);
+ if (ctx->params.rot_mode)
+ rot_mode = CODA_ROT_MIR_ENABLE | ctx->params.rot_mode;
+ coda_write(dev, rot_mode, CODA_CMD_ENC_PIC_ROT_MODE);
coda_write(dev, quant_param, CODA_CMD_ENC_PIC_QS);
-
- picture_y = vb2_dma_contig_plane_dma_addr(src_buf, 0);
- switch (q_data_src->fourcc) {
- case V4L2_PIX_FMT_YVU420:
- /* Switch Cb and Cr for YVU420 format */
- picture_cr = picture_y + q_data_src->bytesperline *
- q_data_src->height;
- picture_cb = picture_cr + q_data_src->bytesperline / 2 *
- q_data_src->height / 2;
- break;
- case V4L2_PIX_FMT_YUV420:
- default:
- picture_cb = picture_y + q_data_src->bytesperline *
- q_data_src->height;
- picture_cr = picture_cb + q_data_src->bytesperline / 2 *
- q_data_src->height / 2;
- break;
- }
-
if (dev->devtype->product == CODA_960) {
coda_write(dev, 4/*FIXME: 0*/, CODA9_CMD_ENC_PIC_SRC_INDEX);
coda_write(dev, q_data_src->width, CODA9_CMD_ENC_PIC_SRC_STRIDE);
coda_write(dev, 0, CODA9_CMD_ENC_PIC_SUB_FRAME_SYNC);
- coda_write(dev, picture_y, CODA9_CMD_ENC_PIC_SRC_ADDR_Y);
- coda_write(dev, picture_cb, CODA9_CMD_ENC_PIC_SRC_ADDR_CB);
- coda_write(dev, picture_cr, CODA9_CMD_ENC_PIC_SRC_ADDR_CR);
+ reg = CODA9_CMD_ENC_PIC_SRC_ADDR_Y;
} else {
- coda_write(dev, picture_y, CODA_CMD_ENC_PIC_SRC_ADDR_Y);
- coda_write(dev, picture_cb, CODA_CMD_ENC_PIC_SRC_ADDR_CB);
- coda_write(dev, picture_cr, CODA_CMD_ENC_PIC_SRC_ADDR_CR);
+ reg = CODA_CMD_ENC_PIC_SRC_ADDR_Y;
}
+ coda_write_base(ctx, q_data_src, src_buf, reg);
+
coda_write(dev, force_ipicture << 1 & 0x2,
CODA_CMD_ENC_PIC_OPTION);
@@ -1293,7 +1339,7 @@
u32 bitstream_buf, bitstream_size;
struct coda_dev *dev = ctx->dev;
int width, height;
- u32 src_fourcc;
+ u32 src_fourcc, dst_fourcc;
u32 val;
int ret;
@@ -1303,6 +1349,7 @@
bitstream_buf = ctx->bitstream.paddr;
bitstream_size = ctx->bitstream.size;
src_fourcc = q_data_src->fourcc;
+ dst_fourcc = q_data_dst->fourcc;
/* Allocate per-instance buffers */
ret = coda_alloc_context_buffers(ctx, q_data_src);
@@ -1314,6 +1361,11 @@
/* Update coda bitstream read and write pointers from kfifo */
coda_kfifo_sync_to_device_full(ctx);
+ ctx->frame_mem_ctrl &= ~CODA_FRAME_CHROMA_INTERLEAVE;
+ if (dst_fourcc == V4L2_PIX_FMT_NV12)
+ ctx->frame_mem_ctrl |= CODA_FRAME_CHROMA_INTERLEAVE;
+ coda_write(dev, ctx->frame_mem_ctrl, CODA_REG_BIT_FRAME_MEM_CTRL);
+
ctx->display_idx = -1;
ctx->frm_dis_flg = 0;
coda_write(dev, 0, CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx));
@@ -1327,6 +1379,8 @@
if ((dev->devtype->product == CODA_7541) ||
(dev->devtype->product == CODA_960))
val |= CODA_REORDER_ENABLE;
+ if (ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG)
+ val |= CODA_NO_INT_ENABLE;
coda_write(dev, val, CODA_CMD_DEC_SEQ_OPTION);
ctx->params.codec_mode = ctx->codec->mode;
@@ -1442,13 +1496,23 @@
}
if (dev->devtype->product == CODA_960) {
- coda_write(dev, -1, CODA9_CMD_SET_FRAME_DELAY);
+ int cbb_size, crb_size;
+ coda_write(dev, -1, CODA9_CMD_SET_FRAME_DELAY);
+ /* Luma 2x0 page, 2x6 cache, chroma 2x0 page, 2x4 cache size */
coda_write(dev, 0x20262024, CODA9_CMD_SET_FRAME_CACHE_SIZE);
+
+ if (dst_fourcc == V4L2_PIX_FMT_NV12) {
+ cbb_size = 0;
+ crb_size = 16;
+ } else {
+ cbb_size = 8;
+ crb_size = 8;
+ }
coda_write(dev, 2 << CODA9_CACHE_PAGEMERGE_OFFSET |
32 << CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET |
- 8 << CODA9_CACHE_CB_BUFFER_SIZE_OFFSET |
- 8 << CODA9_CACHE_CR_BUFFER_SIZE_OFFSET,
+ cbb_size << CODA9_CACHE_CB_BUFFER_SIZE_OFFSET |
+ crb_size << CODA9_CACHE_CR_BUFFER_SIZE_OFFSET,
CODA9_CMD_SET_FRAME_CACHE_CONFIG);
}
@@ -1501,20 +1565,11 @@
struct vb2_buffer *dst_buf;
struct coda_dev *dev = ctx->dev;
struct coda_q_data *q_data_dst;
- u32 stridey, height;
- u32 picture_y, picture_cb, picture_cr;
+ u32 reg_addr, reg_stride;
dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
- if (ctx->params.rot_mode & CODA_ROT_90) {
- stridey = q_data_dst->height;
- height = q_data_dst->width;
- } else {
- stridey = q_data_dst->width;
- height = q_data_dst->height;
- }
-
/* Try to copy source buffer contents into the bitstream ringbuffer */
mutex_lock(&ctx->bitstream_mutex);
coda_fill_bitstream(ctx);
@@ -1545,17 +1600,6 @@
if (dev->devtype->product == CODA_960)
coda_set_gdi_regs(ctx);
- /* Set rotator output */
- picture_y = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
- if (q_data_dst->fourcc == V4L2_PIX_FMT_YVU420) {
- /* Switch Cr and Cb for YVU420 format */
- picture_cr = picture_y + stridey * height;
- picture_cb = picture_cr + stridey / 2 * height / 2;
- } else {
- picture_cb = picture_y + stridey * height;
- picture_cr = picture_cb + stridey / 2 * height / 2;
- }
-
if (dev->devtype->product == CODA_960) {
/*
* The CODA960 seems to have an internal list of buffers with
@@ -1565,16 +1609,16 @@
*/
coda_write(dev, CODA_MAX_FRAMEBUFFERS + dst_buf->v4l2_buf.index,
CODA9_CMD_DEC_PIC_ROT_INDEX);
- coda_write(dev, picture_y, CODA9_CMD_DEC_PIC_ROT_ADDR_Y);
- coda_write(dev, picture_cb, CODA9_CMD_DEC_PIC_ROT_ADDR_CB);
- coda_write(dev, picture_cr, CODA9_CMD_DEC_PIC_ROT_ADDR_CR);
- coda_write(dev, stridey, CODA9_CMD_DEC_PIC_ROT_STRIDE);
+
+ reg_addr = CODA9_CMD_DEC_PIC_ROT_ADDR_Y;
+ reg_stride = CODA9_CMD_DEC_PIC_ROT_STRIDE;
} else {
- coda_write(dev, picture_y, CODA_CMD_DEC_PIC_ROT_ADDR_Y);
- coda_write(dev, picture_cb, CODA_CMD_DEC_PIC_ROT_ADDR_CB);
- coda_write(dev, picture_cr, CODA_CMD_DEC_PIC_ROT_ADDR_CR);
- coda_write(dev, stridey, CODA_CMD_DEC_PIC_ROT_STRIDE);
+ reg_addr = CODA_CMD_DEC_PIC_ROT_ADDR_Y;
+ reg_stride = CODA_CMD_DEC_PIC_ROT_STRIDE;
}
+ coda_write_base(ctx, q_data_dst, dst_buf, reg_addr);
+ coda_write(dev, q_data_dst->bytesperline, reg_stride);
+
coda_write(dev, CODA_ROT_MIR_ENABLE | ctx->params.rot_mode,
CODA_CMD_DEC_PIC_ROT_MODE);
@@ -1599,6 +1643,26 @@
coda_write(dev, ctx->iram_info.axi_sram_use,
CODA7_REG_BIT_AXI_SRAM_USE);
+ if (ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG) {
+ struct coda_buffer_meta *meta;
+
+ /* If this is the last buffer in the bitstream, add padding */
+ meta = list_first_entry(&ctx->buffer_meta_list,
+ struct coda_buffer_meta, list);
+ if (meta->end == (ctx->bitstream_fifo.kfifo.in &
+ ctx->bitstream_fifo.kfifo.mask)) {
+ static unsigned char buf[512];
+ unsigned int pad;
+
+ /* Pad to multiple of 256 and then add 256 more */
+ pad = ((0 - meta->end) & 0xff) + 256;
+
+ memset(buf, 0xff, sizeof(buf));
+
+ kfifo_in(&ctx->bitstream_fifo, buf, pad);
+ }
+ }
+
coda_kfifo_sync_to_device_full(ctx);
coda_command_async(ctx, CODA_COMMAND_PIC_RUN);
@@ -1612,7 +1676,8 @@
struct coda_q_data *q_data_src;
struct coda_q_data *q_data_dst;
struct vb2_buffer *dst_buf;
- struct coda_timestamp *ts;
+ struct coda_buffer_meta *meta;
+ unsigned long payload;
int width, height;
int decoded_idx;
int display_idx;
@@ -1739,23 +1804,23 @@
val = coda_read(dev, CODA_RET_DEC_PIC_FRAME_NUM) - 1;
val -= ctx->sequence_offset;
mutex_lock(&ctx->bitstream_mutex);
- if (!list_empty(&ctx->timestamp_list)) {
- ts = list_first_entry(&ctx->timestamp_list,
- struct coda_timestamp, list);
- list_del(&ts->list);
- if (val != (ts->sequence & 0xffff)) {
+ if (!list_empty(&ctx->buffer_meta_list)) {
+ meta = list_first_entry(&ctx->buffer_meta_list,
+ struct coda_buffer_meta, list);
+ list_del(&meta->list);
+ if (val != (meta->sequence & 0xffff)) {
v4l2_err(&dev->v4l2_dev,
"sequence number mismatch (%d(%d) != %d)\n",
val, ctx->sequence_offset,
- ts->sequence);
+ meta->sequence);
}
- ctx->frame_timestamps[decoded_idx] = *ts;
- kfree(ts);
+ ctx->frame_metas[decoded_idx] = *meta;
+ kfree(meta);
} else {
v4l2_err(&dev->v4l2_dev, "empty timestamp list!\n");
- memset(&ctx->frame_timestamps[decoded_idx], 0,
- sizeof(struct coda_timestamp));
- ctx->frame_timestamps[decoded_idx].sequence = val;
+ memset(&ctx->frame_metas[decoded_idx], 0,
+ sizeof(struct coda_buffer_meta));
+ ctx->frame_metas[decoded_idx].sequence = val;
}
mutex_unlock(&ctx->bitstream_mutex);
@@ -1794,11 +1859,22 @@
V4L2_BUF_FLAG_PFRAME |
V4L2_BUF_FLAG_BFRAME);
dst_buf->v4l2_buf.flags |= ctx->frame_types[ctx->display_idx];
- ts = &ctx->frame_timestamps[ctx->display_idx];
- dst_buf->v4l2_buf.timecode = ts->timecode;
- dst_buf->v4l2_buf.timestamp = ts->timestamp;
+ meta = &ctx->frame_metas[ctx->display_idx];
+ dst_buf->v4l2_buf.timecode = meta->timecode;
+ dst_buf->v4l2_buf.timestamp = meta->timestamp;
- vb2_set_plane_payload(dst_buf, 0, width * height * 3 / 2);
+ switch (q_data_dst->fourcc) {
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ case V4L2_PIX_FMT_NV12:
+ default:
+ payload = width * height * 3 / 2;
+ break;
+ case V4L2_PIX_FMT_YUV422P:
+ payload = width * height * 2;
+ break;
+ }
+ vb2_set_plane_payload(dst_buf, 0, payload);
v4l2_m2m_buf_done(dst_buf, ctx->frame_errors[display_idx] ?
VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c
index ced4760..0813f46 100644
--- a/drivers/media/platform/coda/coda-common.c
+++ b/drivers/media/platform/coda/coda-common.c
@@ -43,6 +43,7 @@
#define CODA_NAME "coda"
#define CODADX6_MAX_INSTANCES 4
+#define CODA_MAX_FORMATS 4
#define CODA_PARA_BUF_SIZE (10 * 1024)
#define CODA_ISRAM_SIZE (2048 * 2)
@@ -82,6 +83,34 @@
return data;
}
+void coda_write_base(struct coda_ctx *ctx, struct coda_q_data *q_data,
+ struct vb2_buffer *buf, unsigned int reg_y)
+{
+ u32 base_y = vb2_dma_contig_plane_dma_addr(buf, 0);
+ u32 base_cb, base_cr;
+
+ switch (q_data->fourcc) {
+ case V4L2_PIX_FMT_YVU420:
+ /* Switch Cb and Cr for YVU420 format */
+ base_cr = base_y + q_data->bytesperline * q_data->height;
+ base_cb = base_cr + q_data->bytesperline * q_data->height / 4;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_NV12:
+ default:
+ base_cb = base_y + q_data->bytesperline * q_data->height;
+ base_cr = base_cb + q_data->bytesperline * q_data->height / 4;
+ break;
+ case V4L2_PIX_FMT_YUV422P:
+ base_cb = base_y + q_data->bytesperline * q_data->height;
+ base_cr = base_cb + q_data->bytesperline * q_data->height / 2;
+ }
+
+ coda_write(ctx->dev, base_y, reg_y);
+ coda_write(ctx->dev, base_cb, reg_y + 4);
+ coda_write(ctx->dev, base_cr, reg_y + 8);
+}
+
/*
* Array of all formats supported by any version of Coda:
*/
@@ -95,6 +124,14 @@
.fourcc = V4L2_PIX_FMT_YVU420,
},
{
+ .name = "YUV 4:2:0 Partial interleaved Y/CbCr",
+ .fourcc = V4L2_PIX_FMT_NV12,
+ },
+ {
+ .name = "YUV 4:2:2 Planar, YCbCr",
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ },
+ {
.name = "H264 Encoded Stream",
.fourcc = V4L2_PIX_FMT_H264,
},
@@ -102,6 +139,10 @@
.name = "MPEG4 Encoded Stream",
.fourcc = V4L2_PIX_FMT_MPEG4,
},
+ {
+ .name = "JPEG Encoded Images",
+ .fourcc = V4L2_PIX_FMT_JPEG,
+ },
};
#define CODA_CODEC(mode, src_fourcc, dst_fourcc, max_w, max_h) \
@@ -122,8 +163,10 @@
static const struct coda_codec coda7_codecs[] = {
CODA_CODEC(CODA7_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 1280, 720),
CODA_CODEC(CODA7_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 1280, 720),
+ CODA_CODEC(CODA7_MODE_ENCODE_MJPG, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_JPEG, 8192, 8192),
CODA_CODEC(CODA7_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1088),
CODA_CODEC(CODA7_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1920, 1088),
+ CODA_CODEC(CODA7_MODE_DECODE_MJPG, V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_YUV420, 8192, 8192),
};
static const struct coda_codec coda9_codecs[] = {
@@ -133,17 +176,115 @@
CODA_CODEC(CODA9_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1920, 1088),
};
+struct coda_video_device {
+ const char *name;
+ enum coda_inst_type type;
+ const struct coda_context_ops *ops;
+ u32 src_formats[CODA_MAX_FORMATS];
+ u32 dst_formats[CODA_MAX_FORMATS];
+};
+
+static const struct coda_video_device coda_bit_encoder = {
+ .name = "coda-encoder",
+ .type = CODA_INST_ENCODER,
+ .ops = &coda_bit_encode_ops,
+ .src_formats = {
+ V4L2_PIX_FMT_YUV420,
+ V4L2_PIX_FMT_YVU420,
+ V4L2_PIX_FMT_NV12,
+ },
+ .dst_formats = {
+ V4L2_PIX_FMT_H264,
+ V4L2_PIX_FMT_MPEG4,
+ },
+};
+
+static const struct coda_video_device coda_bit_jpeg_encoder = {
+ .name = "coda-jpeg-encoder",
+ .type = CODA_INST_ENCODER,
+ .ops = &coda_bit_encode_ops,
+ .src_formats = {
+ V4L2_PIX_FMT_YUV420,
+ V4L2_PIX_FMT_YVU420,
+ V4L2_PIX_FMT_NV12,
+ V4L2_PIX_FMT_YUV422P,
+ },
+ .dst_formats = {
+ V4L2_PIX_FMT_JPEG,
+ },
+};
+
+static const struct coda_video_device coda_bit_decoder = {
+ .name = "coda-decoder",
+ .type = CODA_INST_DECODER,
+ .ops = &coda_bit_decode_ops,
+ .src_formats = {
+ V4L2_PIX_FMT_H264,
+ V4L2_PIX_FMT_MPEG4,
+ },
+ .dst_formats = {
+ V4L2_PIX_FMT_YUV420,
+ V4L2_PIX_FMT_YVU420,
+ V4L2_PIX_FMT_NV12,
+ },
+};
+
+static const struct coda_video_device coda_bit_jpeg_decoder = {
+ .name = "coda-jpeg-decoder",
+ .type = CODA_INST_DECODER,
+ .ops = &coda_bit_decode_ops,
+ .src_formats = {
+ V4L2_PIX_FMT_JPEG,
+ },
+ .dst_formats = {
+ V4L2_PIX_FMT_YUV420,
+ V4L2_PIX_FMT_YVU420,
+ V4L2_PIX_FMT_NV12,
+ V4L2_PIX_FMT_YUV422P,
+ },
+};
+
+static const struct coda_video_device *codadx6_video_devices[] = {
+ &coda_bit_encoder,
+};
+
+static const struct coda_video_device *coda7_video_devices[] = {
+ &coda_bit_jpeg_encoder,
+ &coda_bit_jpeg_decoder,
+ &coda_bit_encoder,
+ &coda_bit_decoder,
+};
+
+static const struct coda_video_device *coda9_video_devices[] = {
+ &coda_bit_encoder,
+ &coda_bit_decoder,
+};
+
static bool coda_format_is_yuv(u32 fourcc)
{
switch (fourcc) {
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YVU420:
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_YUV422P:
return true;
default:
return false;
}
}
+static const char *coda_format_name(u32 fourcc)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(coda_formats); i++) {
+ if (coda_formats[i].fourcc == fourcc)
+ return coda_formats[i].name;
+ }
+
+ return NULL;
+}
+
/*
* Normalize all supported YUV 4:2:0 formats to the value used in the codec
* tables.
@@ -202,6 +343,17 @@
*max_h = h;
}
+const struct coda_video_device *to_coda_video_device(struct video_device *vdev)
+{
+ struct coda_dev *dev = video_get_drvdata(vdev);
+ unsigned int i = vdev - dev->vfd;
+
+ if (i >= dev->devtype->num_vdevs)
+ return NULL;
+
+ return dev->devtype->vdevs[i];
+}
+
const char *coda_product_name(int product)
{
static char buf[9];
@@ -240,58 +392,28 @@
static int coda_enum_fmt(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
- struct coda_ctx *ctx = fh_to_ctx(priv);
- const struct coda_codec *codecs = ctx->dev->devtype->codecs;
- const struct coda_fmt *formats = coda_formats;
- const struct coda_fmt *fmt;
- int num_codecs = ctx->dev->devtype->num_codecs;
- int num_formats = ARRAY_SIZE(coda_formats);
- int i, k, num = 0;
- bool yuv;
+ struct video_device *vdev = video_devdata(file);
+ const struct coda_video_device *cvd = to_coda_video_device(vdev);
+ const u32 *formats;
+ const char *name;
- if (ctx->inst_type == CODA_INST_ENCODER)
- yuv = (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ formats = cvd->src_formats;
+ else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ formats = cvd->dst_formats;
else
- yuv = (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ return -EINVAL;
- for (i = 0; i < num_formats; i++) {
- /* Skip either raw or compressed formats */
- if (yuv != coda_format_is_yuv(formats[i].fourcc))
- continue;
- /* All uncompressed formats are always supported */
- if (yuv) {
- if (num == f->index)
- break;
- ++num;
- continue;
- }
- /* Compressed formats may be supported, check the codec list */
- for (k = 0; k < num_codecs; k++) {
- if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
- formats[i].fourcc == codecs[k].dst_fourcc)
- break;
- if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
- formats[i].fourcc == codecs[k].src_fourcc)
- break;
- }
- if (k < num_codecs) {
- if (num == f->index)
- break;
- ++num;
- }
- }
+ if (f->index >= CODA_MAX_FORMATS || formats[f->index] == 0)
+ return -EINVAL;
- if (i < num_formats) {
- fmt = &formats[i];
- strlcpy(f->description, fmt->name, sizeof(f->description));
- f->pixelformat = fmt->fourcc;
- if (!yuv)
- f->flags |= V4L2_FMT_FLAG_COMPRESSED;
- return 0;
- }
+ name = coda_format_name(formats[f->index]);
+ strlcpy(f->description, name, sizeof(f->description));
+ f->pixelformat = formats[f->index];
+ if (!coda_format_is_yuv(formats[f->index]))
+ f->flags |= V4L2_FMT_FLAG_COMPRESSED;
- /* Format not found */
- return -EINVAL;
+ return 0;
}
static int coda_g_fmt(struct file *file, void *priv,
@@ -311,7 +433,37 @@
f->fmt.pix.bytesperline = q_data->bytesperline;
f->fmt.pix.sizeimage = q_data->sizeimage;
- f->fmt.pix.colorspace = ctx->colorspace;
+ if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_JPEG)
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG;
+ else
+ f->fmt.pix.colorspace = ctx->colorspace;
+
+ return 0;
+}
+
+static int coda_try_pixelformat(struct coda_ctx *ctx, struct v4l2_format *f)
+{
+ struct coda_q_data *q_data;
+ const u32 *formats;
+ int i;
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ formats = ctx->cvd->src_formats;
+ else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ formats = ctx->cvd->dst_formats;
+ else
+ return -EINVAL;
+
+ for (i = 0; i < CODA_MAX_FORMATS; i++) {
+ if (formats[i] == f->fmt.pix.pixelformat) {
+ f->fmt.pix.pixelformat = formats[i];
+ return 0;
+ }
+ }
+
+ /* Fall back to currently set pixelformat */
+ q_data = get_q_data(ctx, f->type);
+ f->fmt.pix.pixelformat = q_data->fourcc;
return 0;
}
@@ -320,7 +472,6 @@
struct v4l2_format *f)
{
struct coda_dev *dev = ctx->dev;
- struct coda_q_data *q_data;
unsigned int max_w, max_h;
enum v4l2_field field;
@@ -342,30 +493,35 @@
switch (f->fmt.pix.pixelformat) {
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YVU420:
- case V4L2_PIX_FMT_H264:
- case V4L2_PIX_FMT_MPEG4:
- case V4L2_PIX_FMT_JPEG:
- break;
- default:
- q_data = get_q_data(ctx, f->type);
- if (!q_data)
- return -EINVAL;
- f->fmt.pix.pixelformat = q_data->fourcc;
- }
-
- switch (f->fmt.pix.pixelformat) {
- case V4L2_PIX_FMT_YUV420:
- case V4L2_PIX_FMT_YVU420:
- /* Frame stride must be multiple of 8, but 16 for h.264 */
+ case V4L2_PIX_FMT_NV12:
+ /*
+ * Frame stride must be at least multiple of 8,
+ * but multiple of 16 for h.264 or JPEG 4:2:x
+ */
f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16);
f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *
f->fmt.pix.height * 3 / 2;
break;
+ case V4L2_PIX_FMT_YUV422P:
+ f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 16);
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *
+ f->fmt.pix.height * 2;
+ break;
+ case V4L2_PIX_FMT_JPEG:
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG;
+ /* fallthrough */
case V4L2_PIX_FMT_H264:
case V4L2_PIX_FMT_MPEG4:
- case V4L2_PIX_FMT_JPEG:
f->fmt.pix.bytesperline = 0;
- f->fmt.pix.sizeimage = CODA_MAX_FRAME_SIZE;
+ /*
+ * This is a rough estimate for sensible compressed buffer
+ * sizes (between 1 and 16 bits per pixel). This could be
+ * improved by better format specific worst case estimates.
+ */
+ f->fmt.pix.sizeimage = round_up(clamp(f->fmt.pix.sizeimage,
+ f->fmt.pix.width * f->fmt.pix.height / 8,
+ f->fmt.pix.width * f->fmt.pix.height * 2),
+ PAGE_SIZE);
break;
default:
BUG();
@@ -378,34 +534,35 @@
struct v4l2_format *f)
{
struct coda_ctx *ctx = fh_to_ctx(priv);
- const struct coda_codec *codec = NULL;
+ const struct coda_q_data *q_data_src;
+ const struct coda_codec *codec;
struct vb2_queue *src_vq;
int ret;
+ ret = coda_try_pixelformat(ctx, f);
+ if (ret < 0)
+ return ret;
+
+ q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+
/*
- * If the source format is already fixed, try to find a codec that
- * converts to the given destination format
+ * If the source format is already fixed, only allow the same output
+ * resolution
*/
src_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
if (vb2_is_streaming(src_vq)) {
- struct coda_q_data *q_data_src;
-
- q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
- codec = coda_find_codec(ctx->dev, q_data_src->fourcc,
- f->fmt.pix.pixelformat);
- if (!codec)
- return -EINVAL;
-
f->fmt.pix.width = q_data_src->width;
f->fmt.pix.height = q_data_src->height;
- } else {
- /* Otherwise determine codec by encoded format, if possible */
- codec = coda_find_codec(ctx->dev, V4L2_PIX_FMT_YUV420,
- f->fmt.pix.pixelformat);
}
f->fmt.pix.colorspace = ctx->colorspace;
+ q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ codec = coda_find_codec(ctx->dev, q_data_src->fourcc,
+ f->fmt.pix.pixelformat);
+ if (!codec)
+ return -EINVAL;
+
ret = coda_try_fmt(ctx, codec, f);
if (ret < 0)
return ret;
@@ -426,21 +583,24 @@
struct v4l2_format *f)
{
struct coda_ctx *ctx = fh_to_ctx(priv);
- const struct coda_codec *codec = NULL;
+ struct coda_dev *dev = ctx->dev;
+ const struct coda_q_data *q_data_dst;
+ const struct coda_codec *codec;
+ int ret;
- /* Determine codec by encoded format, returns NULL if raw or invalid */
- if (ctx->inst_type == CODA_INST_DECODER) {
- codec = coda_find_codec(ctx->dev, f->fmt.pix.pixelformat,
- V4L2_PIX_FMT_YUV420);
- if (!codec)
- codec = coda_find_codec(ctx->dev, V4L2_PIX_FMT_H264,
- V4L2_PIX_FMT_YUV420);
- if (!codec)
- return -EINVAL;
+ ret = coda_try_pixelformat(ctx, f);
+ if (ret < 0)
+ return ret;
+
+ if (!f->fmt.pix.colorspace) {
+ if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_JPEG)
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG;
+ else
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709;
}
- if (!f->fmt.pix.colorspace)
- f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709;
+ q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ codec = coda_find_codec(dev, f->fmt.pix.pixelformat, q_data_dst->fourcc);
return coda_try_fmt(ctx, codec, f);
}
@@ -671,7 +831,9 @@
.vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
.vidioc_qbuf = coda_qbuf,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
.vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
+#endif
.vidioc_dqbuf = coda_dqbuf,
.vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
@@ -781,6 +943,7 @@
if (ctx->hold ||
((ctx->inst_type == CODA_INST_DECODER) &&
+ !v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) &&
(coda_get_bitstream_payload(ctx) < 512) &&
!(ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG))) {
v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
@@ -862,25 +1025,17 @@
static void set_default_params(struct coda_ctx *ctx)
{
- u32 src_fourcc, dst_fourcc;
- int max_w;
- int max_h;
+ unsigned int max_w, max_h, size;
- if (ctx->inst_type == CODA_INST_ENCODER) {
- src_fourcc = V4L2_PIX_FMT_YUV420;
- dst_fourcc = V4L2_PIX_FMT_H264;
- } else {
- src_fourcc = V4L2_PIX_FMT_H264;
- dst_fourcc = V4L2_PIX_FMT_YUV420;
- }
- ctx->codec = coda_find_codec(ctx->dev, src_fourcc, dst_fourcc);
- max_w = ctx->codec->max_w;
- max_h = ctx->codec->max_h;
+ ctx->codec = coda_find_codec(ctx->dev, ctx->cvd->src_formats[0],
+ ctx->cvd->dst_formats[0]);
+ max_w = min(ctx->codec->max_w, 1920U);
+ max_h = min(ctx->codec->max_h, 1088U);
+ size = max_w * max_h * 3 / 2;
ctx->params.codec_mode = ctx->codec->mode;
ctx->colorspace = V4L2_COLORSPACE_REC709;
ctx->params.framerate = 30;
- ctx->aborting = 0;
/* Default formats for output and input queues */
ctx->q_data[V4L2_M2M_SRC].fourcc = ctx->codec->src_fourcc;
@@ -891,14 +1046,14 @@
ctx->q_data[V4L2_M2M_DST].height = max_h;
if (ctx->codec->src_fourcc == V4L2_PIX_FMT_YUV420) {
ctx->q_data[V4L2_M2M_SRC].bytesperline = max_w;
- ctx->q_data[V4L2_M2M_SRC].sizeimage = (max_w * max_h * 3) / 2;
+ ctx->q_data[V4L2_M2M_SRC].sizeimage = size;
ctx->q_data[V4L2_M2M_DST].bytesperline = 0;
- ctx->q_data[V4L2_M2M_DST].sizeimage = CODA_MAX_FRAME_SIZE;
+ ctx->q_data[V4L2_M2M_DST].sizeimage = round_up(size, PAGE_SIZE);
} else {
ctx->q_data[V4L2_M2M_SRC].bytesperline = 0;
- ctx->q_data[V4L2_M2M_SRC].sizeimage = CODA_MAX_FRAME_SIZE;
+ ctx->q_data[V4L2_M2M_SRC].sizeimage = round_up(size, PAGE_SIZE);
ctx->q_data[V4L2_M2M_DST].bytesperline = max_w;
- ctx->q_data[V4L2_M2M_DST].sizeimage = (max_w * max_h * 3) / 2;
+ ctx->q_data[V4L2_M2M_DST].sizeimage = size;
}
ctx->q_data[V4L2_M2M_SRC].rect.width = max_w;
ctx->q_data[V4L2_M2M_SRC].rect.height = max_h;
@@ -964,7 +1119,7 @@
* In the decoder case, immediately try to copy the buffer into the
* bitstream ringbuffer and mark it as ready to be dequeued.
*/
- if (q_data->fourcc == V4L2_PIX_FMT_H264 &&
+ if (ctx->inst_type == CODA_INST_DECODER &&
vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
/*
* For backwards compatibility, queuing an empty buffer marks
@@ -1027,12 +1182,13 @@
struct v4l2_device *v4l2_dev = &ctx->dev->v4l2_dev;
struct coda_q_data *q_data_src, *q_data_dst;
struct vb2_buffer *buf;
- u32 dst_fourcc;
int ret = 0;
q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
- if (q_data_src->fourcc == V4L2_PIX_FMT_H264) {
+ if (q_data_src->fourcc == V4L2_PIX_FMT_H264 ||
+ (q_data_src->fourcc == V4L2_PIX_FMT_JPEG &&
+ ctx->dev->devtype->product == CODA_7541)) {
/* copy the buffers that where queued before streamon */
mutex_lock(&ctx->bitstream_mutex);
coda_fill_bitstream(ctx);
@@ -1063,13 +1219,12 @@
if (!(ctx->streamon_out & ctx->streamon_cap))
return 0;
- /* Allow decoder device_run with no new buffers queued */
+ /* Allow BIT decoder device_run with no new buffers queued */
if (ctx->inst_type == CODA_INST_DECODER)
v4l2_m2m_set_src_buffered(ctx->fh.m2m_ctx, true);
ctx->gopcounter = ctx->params.gop_size - 1;
q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
- dst_fourcc = q_data_dst->fourcc;
ctx->codec = coda_find_codec(ctx->dev, q_data_src->fourcc,
q_data_dst->fourcc);
@@ -1079,6 +1234,10 @@
goto err;
}
+ if (q_data_dst->fourcc == V4L2_PIX_FMT_JPEG)
+ ctx->params.gop_size = 1;
+ ctx->gopcounter = ctx->params.gop_size - 1;
+
ret = ctx->ops->start_streaming(ctx);
if (ctx->inst_type == CODA_INST_DECODER) {
if (ret == -EAGAIN)
@@ -1093,10 +1252,10 @@
err:
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx)))
- v4l2_m2m_buf_done(buf, VB2_BUF_STATE_DEQUEUED);
+ v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
} else {
while ((buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx)))
- v4l2_m2m_buf_done(buf, VB2_BUF_STATE_DEQUEUED);
+ v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED);
}
return ret;
}
@@ -1131,19 +1290,20 @@
}
if (!ctx->streamon_out && !ctx->streamon_cap) {
- struct coda_timestamp *ts;
+ struct coda_buffer_meta *meta;
mutex_lock(&ctx->bitstream_mutex);
- while (!list_empty(&ctx->timestamp_list)) {
- ts = list_first_entry(&ctx->timestamp_list,
- struct coda_timestamp, list);
- list_del(&ts->list);
- kfree(ts);
+ while (!list_empty(&ctx->buffer_meta_list)) {
+ meta = list_first_entry(&ctx->buffer_meta_list,
+ struct coda_buffer_meta, list);
+ list_del(&meta->list);
+ kfree(meta);
}
mutex_unlock(&ctx->bitstream_mutex);
kfifo_init(&ctx->bitstream_fifo,
ctx->bitstream.vaddr, ctx->bitstream.size);
ctx->runcounter = 0;
+ ctx->aborting = 0;
}
}
@@ -1226,6 +1386,12 @@
case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB:
ctx->params.intra_refresh = ctrl->val;
break;
+ case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+ coda_set_jpeg_compression_quality(ctx, ctrl->val);
+ break;
+ case V4L2_CID_JPEG_RESTART_INTERVAL:
+ ctx->params.jpeg_restart_interval = ctrl->val;
+ break;
default:
v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
"Invalid control, id=%d, val=%d\n",
@@ -1240,14 +1406,8 @@
.s_ctrl = coda_s_ctrl,
};
-static int coda_ctrls_setup(struct coda_ctx *ctx)
+static void coda_encode_ctrls(struct coda_ctx *ctx)
{
- v4l2_ctrl_handler_init(&ctx->ctrls, 9);
-
- v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
V4L2_CID_MPEG_VIDEO_BITRATE, 0, 32767000, 1, 0);
v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
@@ -1291,6 +1451,30 @@
v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB, 0,
1920 * 1088 / 256, 1, 0);
+}
+
+static void coda_jpeg_encode_ctrls(struct coda_ctx *ctx)
+{
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_JPEG_COMPRESSION_QUALITY, 5, 100, 1, 50);
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_JPEG_RESTART_INTERVAL, 0, 100, 1, 0);
+}
+
+static int coda_ctrls_setup(struct coda_ctx *ctx)
+{
+ v4l2_ctrl_handler_init(&ctx->ctrls, 2);
+
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ if (ctx->inst_type == CODA_INST_ENCODER) {
+ if (ctx->cvd->dst_formats[0] == V4L2_PIX_FMT_JPEG)
+ coda_jpeg_encode_ctrls(ctx);
+ else
+ coda_encode_ctrls(ctx);
+ }
if (ctx->ctrls.error) {
v4l2_err(&ctx->dev->v4l2_dev,
@@ -1364,10 +1548,14 @@
return idx;
}
-static int coda_open(struct file *file, enum coda_inst_type inst_type,
- const struct coda_context_ops *ctx_ops)
+/*
+ * File operations
+ */
+
+static int coda_open(struct file *file)
{
- struct coda_dev *dev = video_drvdata(file);
+ struct video_device *vdev = video_devdata(file);
+ struct coda_dev *dev = video_get_drvdata(vdev);
struct coda_ctx *ctx = NULL;
char *name;
int ret;
@@ -1388,8 +1576,9 @@
ctx->debugfs_entry = debugfs_create_dir(name, dev->debugfs_root);
kfree(name);
- ctx->inst_type = inst_type;
- ctx->ops = ctx_ops;
+ ctx->cvd = to_coda_video_device(vdev);
+ ctx->inst_type = ctx->cvd->type;
+ ctx->ops = ctx->cvd->ops;
init_completion(&ctx->completion);
INIT_WORK(&ctx->pic_run_work, coda_pic_run_work);
INIT_WORK(&ctx->seq_end_work, ctx->ops->seq_end_work);
@@ -1399,8 +1588,10 @@
ctx->dev = dev;
ctx->idx = idx;
switch (dev->devtype->product) {
- case CODA_7541:
case CODA_960:
+ ctx->frame_mem_ctrl = 1 << 12;
+ /* fallthrough */
+ case CODA_7541:
ctx->reg_idx = 0;
break;
default:
@@ -1441,16 +1632,17 @@
ctx->fh.ctrl_handler = &ctx->ctrls;
- ret = coda_alloc_context_buf(ctx, &ctx->parabuf, CODA_PARA_BUF_SIZE,
- "parabuf");
+ ret = coda_alloc_context_buf(ctx, &ctx->parabuf,
+ CODA_PARA_BUF_SIZE, "parabuf");
if (ret < 0) {
v4l2_err(&dev->v4l2_dev, "failed to allocate parabuf");
goto err_dma_alloc;
}
ctx->bitstream.size = CODA_MAX_FRAME_SIZE;
- ctx->bitstream.vaddr = dma_alloc_writecombine(&dev->plat_dev->dev,
- ctx->bitstream.size, &ctx->bitstream.paddr, GFP_KERNEL);
+ ctx->bitstream.vaddr = dma_alloc_writecombine(
+ &dev->plat_dev->dev, ctx->bitstream.size,
+ &ctx->bitstream.paddr, GFP_KERNEL);
if (!ctx->bitstream.vaddr) {
v4l2_err(&dev->v4l2_dev,
"failed to allocate bitstream ringbuffer");
@@ -1461,7 +1653,7 @@
ctx->bitstream.vaddr, ctx->bitstream.size);
mutex_init(&ctx->bitstream_mutex);
mutex_init(&ctx->buffer_mutex);
- INIT_LIST_HEAD(&ctx->timestamp_list);
+ INIT_LIST_HEAD(&ctx->buffer_meta_list);
coda_lock(ctx);
list_add(&ctx->list, &dev->instances);
@@ -1495,16 +1687,6 @@
return ret;
}
-static int coda_encoder_open(struct file *file)
-{
- return coda_open(file, CODA_INST_ENCODER, &coda_bit_encode_ops);
-}
-
-static int coda_decoder_open(struct file *file)
-{
- return coda_open(file, CODA_INST_DECODER, &coda_bit_decode_ops);
-}
-
static int coda_release(struct file *file)
{
struct coda_dev *dev = video_drvdata(file);
@@ -1515,6 +1697,9 @@
debugfs_remove_recursive(ctx->debugfs_entry);
+ if (ctx->inst_type == CODA_INST_DECODER)
+ coda_bit_stream_end_flag(ctx);
+
/* If this instance is running, call .job_abort and wait for it to end */
v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
@@ -1528,8 +1713,10 @@
list_del(&ctx->list);
coda_unlock(ctx);
- dma_free_writecombine(&dev->plat_dev->dev, ctx->bitstream.size,
- ctx->bitstream.vaddr, ctx->bitstream.paddr);
+ if (ctx->bitstream.vaddr) {
+ dma_free_writecombine(&dev->plat_dev->dev, ctx->bitstream.size,
+ ctx->bitstream.vaddr, ctx->bitstream.paddr);
+ }
if (ctx->dev->devtype->product == CODA_DX6)
coda_free_aux_buf(dev, &ctx->workbuf);
@@ -1548,18 +1735,9 @@
return 0;
}
-static const struct v4l2_file_operations coda_encoder_fops = {
+static const struct v4l2_file_operations coda_fops = {
.owner = THIS_MODULE,
- .open = coda_encoder_open,
- .release = coda_release,
- .poll = v4l2_m2m_fop_poll,
- .unlocked_ioctl = video_ioctl2,
- .mmap = v4l2_m2m_fop_mmap,
-};
-
-static const struct v4l2_file_operations coda_decoder_fops = {
- .owner = THIS_MODULE,
- .open = coda_decoder_open,
+ .open = coda_open,
.release = coda_release,
.poll = v4l2_m2m_fop_poll,
.unlocked_ioctl = video_ioctl2,
@@ -1664,8 +1842,16 @@
return ret;
}
-static int coda_register_device(struct coda_dev *dev, struct video_device *vfd)
+static int coda_register_device(struct coda_dev *dev, int i)
{
+ struct video_device *vfd = &dev->vfd[i];
+
+ if (i > ARRAY_SIZE(dev->vfd))
+ return -EINVAL;
+
+ snprintf(vfd->name, sizeof(vfd->name), dev->devtype->vdevs[i]->name);
+ vfd->fops = &coda_fops;
+ vfd->ioctl_ops = &coda_ioctl_ops;
vfd->release = video_device_release_empty,
vfd->lock = &dev->dev_mutex;
vfd->v4l2_dev = &dev->v4l2_dev;
@@ -1684,7 +1870,7 @@
{
struct coda_dev *dev = context;
struct platform_device *pdev = dev->plat_dev;
- int ret;
+ int i, ret;
if (!fw) {
v4l2_err(&dev->v4l2_dev, "firmware request failed\n");
@@ -1725,33 +1911,25 @@
goto rel_ctx;
}
- dev->vfd[0].fops = &coda_encoder_fops,
- dev->vfd[0].ioctl_ops = &coda_ioctl_ops;
- snprintf(dev->vfd[0].name, sizeof(dev->vfd[0].name), "coda-encoder");
- ret = coda_register_device(dev, &dev->vfd[0]);
- if (ret) {
- v4l2_err(&dev->v4l2_dev,
- "Failed to register encoder video device\n");
- goto rel_m2m;
- }
-
- dev->vfd[1].fops = &coda_decoder_fops,
- dev->vfd[1].ioctl_ops = &coda_ioctl_ops;
- snprintf(dev->vfd[1].name, sizeof(dev->vfd[1].name), "coda-decoder");
- ret = coda_register_device(dev, &dev->vfd[1]);
- if (ret) {
- v4l2_err(&dev->v4l2_dev,
- "Failed to register decoder video device\n");
- goto rel_m2m;
+ for (i = 0; i < dev->devtype->num_vdevs; i++) {
+ ret = coda_register_device(dev, i);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev,
+ "Failed to register %s video device: %d\n",
+ dev->devtype->vdevs[i]->name, ret);
+ goto rel_vfd;
+ }
}
v4l2_info(&dev->v4l2_dev, "codec registered as /dev/video[%d-%d]\n",
- dev->vfd[0].num, dev->vfd[1].num);
+ dev->vfd[0].num, dev->vfd[i - 1].num);
pm_runtime_put_sync(&pdev->dev);
return;
-rel_m2m:
+rel_vfd:
+ while (--i >= 0)
+ video_unregister_device(&dev->vfd[i]);
v4l2_m2m_release(dev->m2m_dev);
rel_ctx:
vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
@@ -1783,6 +1961,8 @@
.product = CODA_DX6,
.codecs = codadx6_codecs,
.num_codecs = ARRAY_SIZE(codadx6_codecs),
+ .vdevs = codadx6_video_devices,
+ .num_vdevs = ARRAY_SIZE(codadx6_video_devices),
.workbuf_size = 288 * 1024 + FMO_SLICE_SAVE_BUF_SIZE * 8 * 1024,
.iram_size = 0xb000,
},
@@ -1791,6 +1971,8 @@
.product = CODA_7541,
.codecs = coda7_codecs,
.num_codecs = ARRAY_SIZE(coda7_codecs),
+ .vdevs = coda7_video_devices,
+ .num_vdevs = ARRAY_SIZE(coda7_video_devices),
.workbuf_size = 128 * 1024,
.tempbuf_size = 304 * 1024,
.iram_size = 0x14000,
@@ -1800,6 +1982,8 @@
.product = CODA_960,
.codecs = coda9_codecs,
.num_codecs = ARRAY_SIZE(coda9_codecs),
+ .vdevs = coda9_video_devices,
+ .num_vdevs = ARRAY_SIZE(coda9_video_devices),
.workbuf_size = 80 * 1024,
.tempbuf_size = 204 * 1024,
.iram_size = 0x21000,
@@ -1809,6 +1993,8 @@
.product = CODA_960,
.codecs = coda9_codecs,
.num_codecs = ARRAY_SIZE(coda9_codecs),
+ .vdevs = coda9_video_devices,
+ .num_vdevs = ARRAY_SIZE(coda9_video_devices),
.workbuf_size = 80 * 1024,
.tempbuf_size = 204 * 1024,
.iram_size = 0x20000,
@@ -1846,10 +2032,18 @@
int ret, irq;
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
- if (!dev) {
- dev_err(&pdev->dev, "Not enough memory for %s\n",
- CODA_NAME);
+ if (!dev)
return -ENOMEM;
+
+ pdev_id = of_id ? of_id->data : platform_get_device_id(pdev);
+
+ if (of_id) {
+ dev->devtype = of_id->data;
+ } else if (pdev_id) {
+ dev->devtype = &coda_devdata[pdev_id->driver_data];
+ } else {
+ ret = -EINVAL;
+ goto err_v4l2_register;
}
spin_lock_init(&dev->irqlock);
@@ -1919,17 +2113,6 @@
mutex_init(&dev->dev_mutex);
mutex_init(&dev->coda_mutex);
- pdev_id = of_id ? of_id->data : platform_get_device_id(pdev);
-
- if (of_id) {
- dev->devtype = of_id->data;
- } else if (pdev_id) {
- dev->devtype = &coda_devdata[pdev_id->driver_data];
- } else {
- v4l2_device_unregister(&dev->v4l2_dev);
- return -EINVAL;
- }
-
dev->debugfs_root = debugfs_create_dir("coda", NULL);
if (!dev->debugfs_root)
dev_warn(&pdev->dev, "failed to create debugfs root\n");
@@ -1941,8 +2124,7 @@
dev->debugfs_root);
if (ret < 0) {
dev_err(&pdev->dev, "failed to allocate work buffer\n");
- v4l2_device_unregister(&dev->v4l2_dev);
- return ret;
+ goto err_v4l2_register;
}
}
@@ -1952,8 +2134,7 @@
dev->debugfs_root);
if (ret < 0) {
dev_err(&pdev->dev, "failed to allocate temp buffer\n");
- v4l2_device_unregister(&dev->v4l2_dev);
- return ret;
+ goto err_v4l2_register;
}
}
@@ -1973,14 +2154,15 @@
dev->workqueue = alloc_workqueue("coda", WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
if (!dev->workqueue) {
dev_err(&pdev->dev, "unable to alloc workqueue\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err_v4l2_register;
}
platform_set_drvdata(pdev, dev);
/*
* Start activated so we can directly call coda_hw_init in
- * coda_fw_callback regardless of whether CONFIG_PM_RUNTIME is
+ * coda_fw_callback regardless of whether CONFIG_PM is
* enabled or whether the device is associated with a PM domain.
*/
pm_runtime_get_noresume(&pdev->dev);
@@ -1988,14 +2170,21 @@
pm_runtime_enable(&pdev->dev);
return coda_firmware_request(dev);
+
+err_v4l2_register:
+ v4l2_device_unregister(&dev->v4l2_dev);
+ return ret;
}
static int coda_remove(struct platform_device *pdev)
{
struct coda_dev *dev = platform_get_drvdata(pdev);
+ int i;
- video_unregister_device(&dev->vfd[0]);
- video_unregister_device(&dev->vfd[1]);
+ for (i = 0; i < ARRAY_SIZE(dev->vfd); i++) {
+ if (video_get_drvdata(&dev->vfd[i]))
+ video_unregister_device(&dev->vfd[i]);
+ }
if (dev->m2m_dev)
v4l2_m2m_release(dev->m2m_dev);
pm_runtime_disable(&pdev->dev);
@@ -2013,7 +2202,7 @@
return 0;
}
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
static int coda_runtime_resume(struct device *dev)
{
struct coda_dev *cdev = dev_get_drvdata(dev);
@@ -2038,7 +2227,6 @@
.remove = coda_remove,
.driver = {
.name = CODA_NAME,
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(coda_dt_ids),
.pm = &coda_pm_ops,
},
diff --git a/drivers/media/platform/coda/coda-jpeg.c b/drivers/media/platform/coda/coda-jpeg.c
new file mode 100644
index 0000000..8fa3e35
--- /dev/null
+++ b/drivers/media/platform/coda/coda-jpeg.c
@@ -0,0 +1,238 @@
+/*
+ * Coda multi-standard codec IP - JPEG support functions
+ *
+ * Copyright (C) 2014 Philipp Zabel, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/swab.h>
+
+#include "coda.h"
+
+#define SOI_MARKER 0xffd8
+#define EOI_MARKER 0xffd9
+
+/*
+ * Typical Huffman tables for 8-bit precision luminance and
+ * chrominance from JPEG ITU-T.81 (ISO/IEC 10918-1) Annex K.3
+ */
+
+static const unsigned char luma_dc_bits[16] = {
+ 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const unsigned char luma_dc_value[12] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b,
+};
+
+static const unsigned char chroma_dc_bits[16] = {
+ 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const unsigned char chroma_dc_value[12] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b,
+};
+
+static const unsigned char luma_ac_bits[16] = {
+ 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03,
+ 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d,
+};
+
+static const unsigned char luma_ac_value[162 + 2] = {
+ 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
+ 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
+ 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
+ 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
+ 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
+ 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
+ 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
+ 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
+ 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+ 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
+ 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
+ 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+ 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
+ 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
+ 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
+ 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
+ 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
+ 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
+ 0xf9, 0xfa, /* padded to 32-bit */
+};
+
+static const unsigned char chroma_ac_bits[16] = {
+ 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04,
+ 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77,
+};
+
+static const unsigned char chroma_ac_value[162 + 2] = {
+ 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
+ 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
+ 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
+ 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
+ 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
+ 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
+ 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
+ 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
+ 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+ 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+ 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+ 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
+ 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
+ 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
+ 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
+ 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
+ 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
+ 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
+ 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
+ 0xf9, 0xfa, /* padded to 32-bit */
+};
+
+/*
+ * Quantization tables for luminance and chrominance components in
+ * zig-zag scan order from the Freescale i.MX VPU libaries
+ */
+
+static unsigned char luma_q[64] = {
+ 0x06, 0x04, 0x04, 0x04, 0x05, 0x04, 0x06, 0x05,
+ 0x05, 0x06, 0x09, 0x06, 0x05, 0x06, 0x09, 0x0b,
+ 0x08, 0x06, 0x06, 0x08, 0x0b, 0x0c, 0x0a, 0x0a,
+ 0x0b, 0x0a, 0x0a, 0x0c, 0x10, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x10, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+};
+
+static unsigned char chroma_q[64] = {
+ 0x07, 0x07, 0x07, 0x0d, 0x0c, 0x0d, 0x18, 0x10,
+ 0x10, 0x18, 0x14, 0x0e, 0x0e, 0x0e, 0x14, 0x14,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x14, 0x11, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x11, 0x11, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x11, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+};
+
+struct coda_memcpy_desc {
+ int offset;
+ const void *src;
+ size_t len;
+};
+
+static void coda_memcpy_parabuf(void *parabuf,
+ const struct coda_memcpy_desc *desc)
+{
+ u32 *dst = parabuf + desc->offset;
+ const u32 *src = desc->src;
+ int len = desc->len / 4;
+ int i;
+
+ for (i = 0; i < len; i += 2) {
+ dst[i + 1] = swab32(src[i]);
+ dst[i] = swab32(src[i + 1]);
+ }
+}
+
+int coda_jpeg_write_tables(struct coda_ctx *ctx)
+{
+ int i;
+ static const struct coda_memcpy_desc huff[8] = {
+ { 0, luma_dc_bits, sizeof(luma_dc_bits) },
+ { 16, luma_dc_value, sizeof(luma_dc_value) },
+ { 32, luma_ac_bits, sizeof(luma_ac_bits) },
+ { 48, luma_ac_value, sizeof(luma_ac_value) },
+ { 216, chroma_dc_bits, sizeof(chroma_dc_bits) },
+ { 232, chroma_dc_value, sizeof(chroma_dc_value) },
+ { 248, chroma_ac_bits, sizeof(chroma_ac_bits) },
+ { 264, chroma_ac_value, sizeof(chroma_ac_value) },
+ };
+ struct coda_memcpy_desc qmat[3] = {
+ { 512, ctx->params.jpeg_qmat_tab[0], 64 },
+ { 576, ctx->params.jpeg_qmat_tab[1], 64 },
+ { 640, ctx->params.jpeg_qmat_tab[1], 64 },
+ };
+
+ /* Write huffman tables to parameter memory */
+ for (i = 0; i < ARRAY_SIZE(huff); i++)
+ coda_memcpy_parabuf(ctx->parabuf.vaddr, huff + i);
+
+ /* Write Q-matrix to parameter memory */
+ for (i = 0; i < ARRAY_SIZE(qmat); i++)
+ coda_memcpy_parabuf(ctx->parabuf.vaddr, qmat + i);
+
+ return 0;
+}
+
+bool coda_jpeg_check_buffer(struct coda_ctx *ctx, struct vb2_buffer *vb)
+{
+ void *vaddr = vb2_plane_vaddr(vb, 0);
+ u16 soi = be16_to_cpup((__be16 *)vaddr);
+ u16 eoi = be16_to_cpup((__be16 *)(vaddr +
+ vb2_get_plane_payload(vb, 0) - 2));
+
+ return soi == SOI_MARKER && eoi == EOI_MARKER;
+}
+
+/*
+ * Scale quantization table using nonlinear scaling factor
+ * u8 qtab[64], scale [50,190]
+ */
+static void coda_scale_quant_table(u8 *q_tab, int scale)
+{
+ unsigned int temp;
+ int i;
+
+ for (i = 0; i < 64; i++) {
+ temp = DIV_ROUND_CLOSEST((unsigned int)q_tab[i] * scale, 100);
+ if (temp <= 0)
+ temp = 1;
+ if (temp > 255)
+ temp = 255;
+ q_tab[i] = (unsigned char)temp;
+ }
+}
+
+void coda_set_jpeg_compression_quality(struct coda_ctx *ctx, int quality)
+{
+ unsigned int scale;
+
+ ctx->params.jpeg_quality = quality;
+
+ /* Clip quality setting to [5,100] interval */
+ if (quality > 100)
+ quality = 100;
+ if (quality < 5)
+ quality = 5;
+
+ /*
+ * Non-linear scaling factor:
+ * [5,50] -> [1000..100], [51,100] -> [98..0]
+ */
+ if (quality < 50)
+ scale = 5000 / quality;
+ else
+ scale = 200 - 2 * quality;
+
+ if (ctx->params.jpeg_qmat_tab[0]) {
+ memcpy(ctx->params.jpeg_qmat_tab[0], luma_q, 64);
+ coda_scale_quant_table(ctx->params.jpeg_qmat_tab[0], scale);
+ }
+ if (ctx->params.jpeg_qmat_tab[1]) {
+ memcpy(ctx->params.jpeg_qmat_tab[1], chroma_q, 64);
+ coda_scale_quant_table(ctx->params.jpeg_qmat_tab[1], scale);
+ }
+}
diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h
index bbc18c0..5dd47e5 100644
--- a/drivers/media/platform/coda/coda.h
+++ b/drivers/media/platform/coda/coda.h
@@ -45,11 +45,15 @@
CODA_960 = 0xf020,
};
+struct coda_video_device;
+
struct coda_devtype {
char *firmware;
enum coda_product product;
const struct coda_codec *codecs;
unsigned int num_codecs;
+ const struct coda_video_device **vdevs;
+ unsigned int num_vdevs;
size_t workbuf_size;
size_t tempbuf_size;
size_t iram_size;
@@ -65,7 +69,7 @@
struct coda_dev {
struct v4l2_device v4l2_dev;
- struct video_device vfd[2];
+ struct video_device vfd[5];
struct platform_device *plat_dev;
const struct coda_devtype *devtype;
@@ -114,6 +118,9 @@
u8 mpeg4_inter_qp;
u8 gop_size;
int intra_refresh;
+ u8 jpeg_quality;
+ u8 jpeg_restart_interval;
+ u8 *jpeg_qmat_tab[3];
int codec_mode;
int codec_mode_aux;
enum v4l2_mpeg_video_multi_slice_mode slice_mode;
@@ -123,11 +130,13 @@
u32 slice_max_mb;
};
-struct coda_timestamp {
+struct coda_buffer_meta {
struct list_head list;
u32 sequence;
struct v4l2_timecode timecode;
struct timeval timestamp;
+ u32 start;
+ u32 end;
};
/* Per-queue, driver-specific private data */
@@ -183,6 +192,7 @@
struct work_struct pic_run_work;
struct work_struct seq_end_work;
struct completion completion;
+ const struct coda_video_device *cvd;
const struct coda_context_ops *ops;
int aborting;
int initialized;
@@ -212,9 +222,9 @@
struct coda_aux_buf slicebuf;
struct coda_aux_buf internal_frames[CODA_MAX_FRAMEBUFFERS];
u32 frame_types[CODA_MAX_FRAMEBUFFERS];
- struct coda_timestamp frame_timestamps[CODA_MAX_FRAMEBUFFERS];
+ struct coda_buffer_meta frame_metas[CODA_MAX_FRAMEBUFFERS];
u32 frame_errors[CODA_MAX_FRAMEBUFFERS];
- struct list_head timestamp_list;
+ struct list_head buffer_meta_list;
struct coda_aux_buf workbuf;
int num_internal_frames;
int idx;
@@ -232,6 +242,8 @@
void coda_write(struct coda_dev *dev, u32 data, u32 reg);
unsigned int coda_read(struct coda_dev *dev, u32 reg);
+void coda_write_base(struct coda_ctx *ctx, struct coda_q_data *q_data,
+ struct vb2_buffer *buf, unsigned int reg_y);
int coda_alloc_aux_buf(struct coda_dev *dev, struct coda_aux_buf *buf,
size_t size, const char *name, struct dentry *parent);
@@ -281,6 +293,10 @@
int coda_h264_padding(int size, char *p);
+bool coda_jpeg_check_buffer(struct coda_ctx *ctx, struct vb2_buffer *vb);
+int coda_jpeg_write_tables(struct coda_ctx *ctx);
+void coda_set_jpeg_compression_quality(struct coda_ctx *ctx, int quality);
+
extern const struct coda_context_ops coda_bit_encode_ops;
extern const struct coda_context_ops coda_bit_decode_ops;
diff --git a/drivers/media/platform/coda/coda_regs.h b/drivers/media/platform/coda/coda_regs.h
index c791275..8e015b8 100644
--- a/drivers/media/platform/coda/coda_regs.h
+++ b/drivers/media/platform/coda/coda_regs.h
@@ -147,6 +147,7 @@
#define CODA_CMD_DEC_SEQ_BB_START 0x180
#define CODA_CMD_DEC_SEQ_BB_SIZE 0x184
#define CODA_CMD_DEC_SEQ_OPTION 0x188
+#define CODA_NO_INT_ENABLE (1 << 10)
#define CODA_REORDER_ENABLE (1 << 1)
#define CODADX6_QP_REPORT (1 << 0)
#define CODA7_MP4_DEBLK_ENABLE (1 << 0)
@@ -332,6 +333,12 @@
#define CODA9_CMD_ENC_SEQ_ME_OPTION 0x1d8
#define CODA_RET_ENC_SEQ_SUCCESS 0x1c0
+#define CODA_CMD_ENC_SEQ_JPG_PARA 0x198
+#define CODA_CMD_ENC_SEQ_JPG_RST_INTERVAL 0x19C
+#define CODA_CMD_ENC_SEQ_JPG_THUMB_EN 0x1a0
+#define CODA_CMD_ENC_SEQ_JPG_THUMB_SIZE 0x1a4
+#define CODA_CMD_ENC_SEQ_JPG_THUMB_OFFSET 0x1a8
+
/* Encoder Picture Run */
#define CODA9_CMD_ENC_PIC_SRC_INDEX 0x180
#define CODA9_CMD_ENC_PIC_SRC_STRIDE 0x184
diff --git a/drivers/media/platform/davinci/dm355_ccdc.c b/drivers/media/platform/davinci/dm355_ccdc.c
index 3f44deb..c90b9a4 100644
--- a/drivers/media/platform/davinci/dm355_ccdc.c
+++ b/drivers/media/platform/davinci/dm355_ccdc.c
@@ -1030,7 +1030,6 @@
static struct platform_driver dm355_ccdc_driver = {
.driver = {
.name = "dm355_ccdc",
- .owner = THIS_MODULE,
},
.remove = dm355_ccdc_remove,
.probe = dm355_ccdc_probe,
diff --git a/drivers/media/platform/davinci/dm644x_ccdc.c b/drivers/media/platform/davinci/dm644x_ccdc.c
index 62a0ebb..ffbefdf 100644
--- a/drivers/media/platform/davinci/dm644x_ccdc.c
+++ b/drivers/media/platform/davinci/dm644x_ccdc.c
@@ -1029,7 +1029,6 @@
static struct platform_driver dm644x_ccdc_driver = {
.driver = {
.name = "dm644x_ccdc",
- .owner = THIS_MODULE,
.pm = &dm644x_ccdc_pm_ops,
},
.remove = dm644x_ccdc_remove,
diff --git a/drivers/media/platform/davinci/isif.c b/drivers/media/platform/davinci/isif.c
index 3332cca..99faea2 100644
--- a/drivers/media/platform/davinci/isif.c
+++ b/drivers/media/platform/davinci/isif.c
@@ -1134,7 +1134,6 @@
static struct platform_driver isif_driver = {
.driver = {
.name = "isif",
- .owner = THIS_MODULE,
},
.remove = isif_remove,
.probe = isif_probe,
diff --git a/drivers/media/platform/davinci/vpbe.c b/drivers/media/platform/davinci/vpbe.c
index 33b9660..9a6c2cc 100644
--- a/drivers/media/platform/davinci/vpbe.c
+++ b/drivers/media/platform/davinci/vpbe.c
@@ -227,7 +227,7 @@
vpbe_current_encoder_info(vpbe_dev);
struct vpbe_config *cfg = vpbe_dev->cfg;
struct venc_platform_data *venc_device = vpbe_dev->venc_device;
- enum v4l2_mbus_pixelcode if_params;
+ u32 if_params;
int enc_out_index;
int sd_index;
int ret = 0;
@@ -341,7 +341,7 @@
if (!(cfg->outputs[out_index].output.capabilities &
V4L2_OUT_CAP_DV_TIMINGS))
- return -EINVAL;
+ return -ENODATA;
for (i = 0; i < output->num_modes; i++) {
if (output->modes[i].timings_type == VPBE_ENC_DV_TIMINGS &&
@@ -384,6 +384,13 @@
static int vpbe_g_dv_timings(struct vpbe_device *vpbe_dev,
struct v4l2_dv_timings *dv_timings)
{
+ struct vpbe_config *cfg = vpbe_dev->cfg;
+ int out_index = vpbe_dev->current_out_index;
+
+ if (!(cfg->outputs[out_index].output.capabilities &
+ V4L2_OUT_CAP_DV_TIMINGS))
+ return -ENODATA;
+
if (vpbe_dev->current_timings.timings_type &
VPBE_ENC_DV_TIMINGS) {
*dv_timings = vpbe_dev->current_timings.dv_timings;
@@ -409,7 +416,7 @@
int i;
if (!(output->output.capabilities & V4L2_OUT_CAP_DV_TIMINGS))
- return -EINVAL;
+ return -ENODATA;
for (i = 0; i < output->num_modes; i++) {
if (output->modes[i].timings_type == VPBE_ENC_DV_TIMINGS) {
@@ -440,7 +447,7 @@
if (!(cfg->outputs[out_index].output.capabilities &
V4L2_OUT_CAP_STD))
- return -EINVAL;
+ return -ENODATA;
ret = vpbe_get_std_info(vpbe_dev, std_id);
if (ret)
@@ -473,6 +480,11 @@
static int vpbe_g_std(struct vpbe_device *vpbe_dev, v4l2_std_id *std_id)
{
struct vpbe_enc_mode_info *cur_timings = &vpbe_dev->current_timings;
+ struct vpbe_config *cfg = vpbe_dev->cfg;
+ int out_index = vpbe_dev->current_out_index;
+
+ if (!(cfg->outputs[out_index].output.capabilities & V4L2_OUT_CAP_STD))
+ return -ENODATA;
if (cur_timings->timings_type & VPBE_ENC_STD) {
*std_id = cur_timings->std_id;
@@ -613,6 +625,7 @@
}
if (clk_prepare_enable(vpbe_dev->dac_clk)) {
ret = -ENODEV;
+ clk_put(vpbe_dev->dac_clk);
goto fail_mutex_unlock;
}
}
@@ -863,7 +876,6 @@
static struct platform_driver vpbe_driver = {
.driver = {
.name = "vpbe_controller",
- .owner = THIS_MODULE,
},
.probe = vpbe_probe,
.remove = vpbe_remove,
diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c
index 73496d9..c4ab46f 100644
--- a/drivers/media/platform/davinci/vpbe_display.c
+++ b/drivers/media/platform/davinci/vpbe_display.c
@@ -152,8 +152,8 @@
for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
layer = disp_dev->dev[i];
- /* If streaming is started in this layer */
- if (!layer->started)
+
+ if (!vb2_start_streaming_called(&layer->buffer_queue))
continue;
if (layer->layer_first_int) {
@@ -207,31 +207,23 @@
*/
static int vpbe_buffer_prepare(struct vb2_buffer *vb)
{
- struct vpbe_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
struct vb2_queue *q = vb->vb2_queue;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ struct vpbe_layer *layer = vb2_get_drv_priv(q);
+ struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
unsigned long addr;
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
"vpbe_buffer_prepare\n");
- if (vb->state != VB2_BUF_STATE_ACTIVE &&
- vb->state != VB2_BUF_STATE_PREPARED) {
- vb2_set_plane_payload(vb, 0, layer->pix_fmt.sizeimage);
- if (vb2_plane_vaddr(vb, 0) &&
- vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0))
- return -EINVAL;
+ vb2_set_plane_payload(vb, 0, layer->pix_fmt.sizeimage);
+ if (vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0))
+ return -EINVAL;
- addr = vb2_dma_contig_plane_dma_addr(vb, 0);
- if (q->streaming) {
- if (!IS_ALIGNED(addr, 8)) {
- v4l2_err(&vpbe_dev->v4l2_dev,
- "buffer_prepare:offset is \
- not aligned to 32 bytes\n");
- return -EINVAL;
- }
- }
+ addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+ if (!IS_ALIGNED(addr, 8)) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "buffer_prepare:offset is not aligned to 32 bytes\n");
+ return -EINVAL;
}
return 0;
}
@@ -247,18 +239,20 @@
{
/* Get the file handle object and layer object */
- struct vpbe_fh *fh = vb2_get_drv_priv(vq);
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ struct vpbe_layer *layer = vb2_get_drv_priv(vq);
+ struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_buffer_setup\n");
+ if (fmt && fmt->fmt.pix.sizeimage < layer->pix_fmt.sizeimage)
+ return -EINVAL;
+
/* Store number of buffers allocated in numbuffer member */
- if (*nbuffers < VPBE_DEFAULT_NUM_BUFS)
- *nbuffers = layer->numbuffers = VPBE_DEFAULT_NUM_BUFS;
+ if (vq->num_buffers + *nbuffers < VPBE_DEFAULT_NUM_BUFS)
+ *nbuffers = VPBE_DEFAULT_NUM_BUFS - vq->num_buffers;
*nplanes = 1;
- sizes[0] = layer->pix_fmt.sizeimage;
+ sizes[0] = fmt ? fmt->fmt.pix.sizeimage : layer->pix_fmt.sizeimage;
alloc_ctxs[0] = layer->alloc_ctx;
return 0;
@@ -271,12 +265,11 @@
static void vpbe_buffer_queue(struct vb2_buffer *vb)
{
/* Get the file handle object and layer object */
- struct vpbe_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
struct vpbe_disp_buffer *buf = container_of(vb,
struct vpbe_disp_buffer, vb);
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_display *disp = fh->disp_dev;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ struct vpbe_layer *layer = vb2_get_drv_priv(vb->vb2_queue);
+ struct vpbe_display *disp = layer->disp_dev;
+ struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
unsigned long flags;
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
@@ -288,61 +281,14 @@
spin_unlock_irqrestore(&disp->dma_queue_lock, flags);
}
-/*
- * vpbe_buf_cleanup()
- * This function is called from the vb2 layer to free memory allocated to
- * the buffers
- */
-static void vpbe_buf_cleanup(struct vb2_buffer *vb)
-{
- /* Get the file handle object and layer object */
- struct vpbe_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
- struct vpbe_disp_buffer *buf = container_of(vb,
- struct vpbe_disp_buffer, vb);
- unsigned long flags;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "vpbe_buf_cleanup\n");
-
- spin_lock_irqsave(&layer->irqlock, flags);
- if (vb->state == VB2_BUF_STATE_ACTIVE)
- list_del_init(&buf->list);
- spin_unlock_irqrestore(&layer->irqlock, flags);
-}
-
-static void vpbe_wait_prepare(struct vb2_queue *vq)
-{
- struct vpbe_fh *fh = vb2_get_drv_priv(vq);
- struct vpbe_layer *layer = fh->layer;
-
- mutex_unlock(&layer->opslock);
-}
-
-static void vpbe_wait_finish(struct vb2_queue *vq)
-{
- struct vpbe_fh *fh = vb2_get_drv_priv(vq);
- struct vpbe_layer *layer = fh->layer;
-
- mutex_lock(&layer->opslock);
-}
-
-static int vpbe_buffer_init(struct vb2_buffer *vb)
-{
- struct vpbe_disp_buffer *buf = container_of(vb,
- struct vpbe_disp_buffer, vb);
-
- INIT_LIST_HEAD(&buf->list);
- return 0;
-}
-
static int vpbe_start_streaming(struct vb2_queue *vq, unsigned int count)
{
- struct vpbe_fh *fh = vb2_get_drv_priv(vq);
- struct vpbe_layer *layer = fh->layer;
+ struct vpbe_layer *layer = vb2_get_drv_priv(vq);
+ struct osd_state *osd_device = layer->disp_dev->osd_device;
int ret;
+ osd_device->ops.disable_layer(osd_device, layer->layer_info.id);
+
/* Get the next frame from the buffer queue */
layer->next_frm = layer->cur_frm = list_entry(layer->dma_queue.next,
struct vpbe_disp_buffer, list);
@@ -354,7 +300,7 @@
layer->field_id = 0;
/* Set parameters in OSD and VENC */
- ret = vpbe_set_osd_display_params(fh->disp_dev, layer);
+ ret = vpbe_set_osd_display_params(layer->disp_dev, layer);
if (ret < 0) {
struct vpbe_disp_buffer *buf, *tmp;
@@ -371,7 +317,6 @@
* if request format is yuv420 semiplanar, need to
* enable both video windows
*/
- layer->started = 1;
layer->layer_first_int = 1;
return ret;
@@ -379,14 +324,16 @@
static void vpbe_stop_streaming(struct vb2_queue *vq)
{
- struct vpbe_fh *fh = vb2_get_drv_priv(vq);
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_display *disp = fh->disp_dev;
+ struct vpbe_layer *layer = vb2_get_drv_priv(vq);
+ struct osd_state *osd_device = layer->disp_dev->osd_device;
+ struct vpbe_display *disp = layer->disp_dev;
unsigned long flags;
if (!vb2_is_streaming(vq))
return;
+ osd_device->ops.disable_layer(osd_device, layer->layer_info.id);
+
/* release all active buffers */
spin_lock_irqsave(&disp->dma_queue_lock, flags);
if (layer->cur_frm == layer->next_frm) {
@@ -411,13 +358,11 @@
static struct vb2_ops video_qops = {
.queue_setup = vpbe_buffer_queue_setup,
- .wait_prepare = vpbe_wait_prepare,
- .wait_finish = vpbe_wait_finish,
- .buf_init = vpbe_buffer_init,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
.buf_prepare = vpbe_buffer_prepare,
.start_streaming = vpbe_start_streaming,
.stop_streaming = vpbe_stop_streaming,
- .buf_cleanup = vpbe_buf_cleanup,
.buf_queue = vpbe_buffer_queue,
};
@@ -691,10 +636,9 @@
static int vpbe_display_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ struct vpbe_layer *layer = video_drvdata(file);
+ struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
- cap->version = VPBE_DISPLAY_VERSION_CODE;
cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
snprintf(cap->driver, sizeof(cap->driver), "%s",
@@ -709,9 +653,8 @@
static int vpbe_display_s_crop(struct file *file, void *priv,
const struct v4l2_crop *crop)
{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_display *disp_dev = fh->disp_dev;
+ struct vpbe_layer *layer = video_drvdata(file);
+ struct vpbe_display *disp_dev = layer->disp_dev;
struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
struct osd_layer_config *cfg = &layer->layer_info.config;
struct osd_state *osd_device = disp_dev->osd_device;
@@ -778,11 +721,10 @@
static int vpbe_display_g_crop(struct file *file, void *priv,
struct v4l2_crop *crop)
{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_layer *layer = fh->layer;
+ struct vpbe_layer *layer = video_drvdata(file);
struct osd_layer_config *cfg = &layer->layer_info.config;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
- struct osd_state *osd_device = fh->disp_dev->osd_device;
+ struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
+ struct osd_state *osd_device = layer->disp_dev->osd_device;
struct v4l2_rect *rect = &crop->c;
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
@@ -806,8 +748,8 @@
static int vpbe_display_cropcap(struct file *file, void *priv,
struct v4l2_cropcap *cropcap)
{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ struct vpbe_layer *layer = video_drvdata(file);
+ struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_CROPCAP ioctl\n");
@@ -824,9 +766,8 @@
static int vpbe_display_g_fmt(struct file *file, void *priv,
struct v4l2_format *fmt)
{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ struct vpbe_layer *layer = video_drvdata(file);
+ struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
"VIDIOC_G_FMT, layer id = %d\n",
@@ -846,9 +787,8 @@
static int vpbe_display_enum_fmt(struct file *file, void *priv,
struct v4l2_fmtdesc *fmt)
{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ struct vpbe_layer *layer = video_drvdata(file);
+ struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
unsigned int index = 0;
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
@@ -878,9 +818,8 @@
static int vpbe_display_s_fmt(struct file *file, void *priv,
struct v4l2_format *fmt)
{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_display *disp_dev = fh->disp_dev;
+ struct vpbe_layer *layer = video_drvdata(file);
+ struct vpbe_display *disp_dev = layer->disp_dev;
struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
struct osd_layer_config *cfg = &layer->layer_info.config;
struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
@@ -891,11 +830,9 @@
"VIDIOC_S_FMT, layer id = %d\n",
layer->device_id);
- /* If streaming is started, return error */
- if (layer->started) {
- v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n");
+ if (vb2_is_busy(&layer->buffer_queue))
return -EBUSY;
- }
+
if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) {
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "invalid type\n");
return -EINVAL;
@@ -967,9 +904,9 @@
static int vpbe_display_try_fmt(struct file *file, void *priv,
struct v4l2_format *fmt)
{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_display *disp_dev = fh->disp_dev;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ struct vpbe_layer *layer = video_drvdata(file);
+ struct vpbe_display *disp_dev = layer->disp_dev;
+ struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_TRY_FMT\n");
@@ -993,18 +930,15 @@
static int vpbe_display_s_std(struct file *file, void *priv,
v4l2_std_id std_id)
{
- struct vpbe_fh *fh = priv;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ struct vpbe_layer *layer = video_drvdata(file);
+ struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
int ret;
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_STD\n");
- /* If streaming is started, return error */
- if (layer->started) {
- v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n");
+ if (vb2_is_busy(&layer->buffer_queue))
return -EBUSY;
- }
+
if (NULL != vpbe_dev->ops.s_std) {
ret = vpbe_dev->ops.s_std(vpbe_dev, std_id);
if (ret) {
@@ -1028,8 +962,8 @@
static int vpbe_display_g_std(struct file *file, void *priv,
v4l2_std_id *std_id)
{
- struct vpbe_fh *fh = priv;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ struct vpbe_layer *layer = video_drvdata(file);
+ struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_STD\n");
@@ -1051,8 +985,8 @@
static int vpbe_display_enum_output(struct file *file, void *priv,
struct v4l2_output *output)
{
- struct vpbe_fh *fh = priv;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ struct vpbe_layer *layer = video_drvdata(file);
+ struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
int ret;
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_OUTPUT\n");
@@ -1079,17 +1013,15 @@
static int vpbe_display_s_output(struct file *file, void *priv,
unsigned int i)
{
- struct vpbe_fh *fh = priv;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ struct vpbe_layer *layer = video_drvdata(file);
+ struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
int ret;
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_OUTPUT\n");
- /* If streaming is started, return error */
- if (layer->started) {
- v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n");
+
+ if (vb2_is_busy(&layer->buffer_queue))
return -EBUSY;
- }
+
if (NULL == vpbe_dev->ops.set_output)
return -EINVAL;
@@ -1110,8 +1042,8 @@
static int vpbe_display_g_output(struct file *file, void *priv,
unsigned int *i)
{
- struct vpbe_fh *fh = priv;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ struct vpbe_layer *layer = video_drvdata(file);
+ struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_OUTPUT\n");
/* Get the standard from the current encoder */
@@ -1130,8 +1062,8 @@
vpbe_display_enum_dv_timings(struct file *file, void *priv,
struct v4l2_enum_dv_timings *timings)
{
- struct vpbe_fh *fh = priv;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ struct vpbe_layer *layer = video_drvdata(file);
+ struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
int ret;
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_DV_TIMINGS\n");
@@ -1160,19 +1092,14 @@
vpbe_display_s_dv_timings(struct file *file, void *priv,
struct v4l2_dv_timings *timings)
{
- struct vpbe_fh *fh = priv;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ struct vpbe_layer *layer = video_drvdata(file);
+ struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
int ret;
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_DV_TIMINGS\n");
-
- /* If streaming is started, return error */
- if (layer->started) {
- v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n");
+ if (vb2_is_busy(&layer->buffer_queue))
return -EBUSY;
- }
/* Set the given standard in the encoder */
if (!vpbe_dev->ops.s_dv_timings)
@@ -1198,8 +1125,8 @@
vpbe_display_g_dv_timings(struct file *file, void *priv,
struct v4l2_dv_timings *dv_timings)
{
- struct vpbe_fh *fh = priv;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ struct vpbe_layer *layer = video_drvdata(file);
+ struct vpbe_device *vpbe_dev = layer->disp_dev->vpbe_dev;
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_DV_TIMINGS\n");
@@ -1215,259 +1142,6 @@
return 0;
}
-static int vpbe_display_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type buf_type)
-{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
- struct osd_state *osd_device = fh->disp_dev->osd_device;
- int ret;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "VIDIOC_STREAMOFF,layer id = %d\n",
- layer->device_id);
-
- if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf_type) {
- v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
- return -EINVAL;
- }
-
- /* If io is allowed for this file handle, return error */
- if (!fh->io_allowed) {
- v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n");
- return -EACCES;
- }
-
- /* If streaming is not started, return error */
- if (!layer->started) {
- v4l2_err(&vpbe_dev->v4l2_dev, "streaming not started in layer"
- " id = %d\n", layer->device_id);
- return -EINVAL;
- }
-
- osd_device->ops.disable_layer(osd_device,
- layer->layer_info.id);
- layer->started = 0;
- ret = vb2_streamoff(&layer->buffer_queue, buf_type);
-
- return ret;
-}
-
-static int vpbe_display_streamon(struct file *file, void *priv,
- enum v4l2_buf_type buf_type)
-{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_display *disp_dev = fh->disp_dev;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
- struct osd_state *osd_device = disp_dev->osd_device;
- int ret;
-
- osd_device->ops.disable_layer(osd_device,
- layer->layer_info.id);
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_STREAMON, layerid=%d\n",
- layer->device_id);
-
- if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf_type) {
- v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
- return -EINVAL;
- }
-
- /* If file handle is not allowed IO, return error */
- if (!fh->io_allowed) {
- v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n");
- return -EACCES;
- }
- /* If Streaming is already started, return error */
- if (layer->started) {
- v4l2_err(&vpbe_dev->v4l2_dev, "layer is already streaming\n");
- return -EBUSY;
- }
-
- /*
- * Call vb2_streamon to start streaming
- * in videobuf
- */
- ret = vb2_streamon(&layer->buffer_queue, buf_type);
- if (ret) {
- v4l2_err(&vpbe_dev->v4l2_dev,
- "error in vb2_streamon\n");
- return ret;
- }
- return ret;
-}
-
-static int vpbe_display_dqbuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
- int ret;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "VIDIOC_DQBUF, layer id = %d\n",
- layer->device_id);
-
- if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) {
- v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
- return -EINVAL;
- }
- /* If this file handle is not allowed to do IO, return error */
- if (!fh->io_allowed) {
- v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n");
- return -EACCES;
- }
- if (file->f_flags & O_NONBLOCK)
- /* Call videobuf_dqbuf for non blocking mode */
- ret = vb2_dqbuf(&layer->buffer_queue, buf, 1);
- else
- /* Call videobuf_dqbuf for blocking mode */
- ret = vb2_dqbuf(&layer->buffer_queue, buf, 0);
-
- return ret;
-}
-
-static int vpbe_display_qbuf(struct file *file, void *priv,
- struct v4l2_buffer *p)
-{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "VIDIOC_QBUF, layer id = %d\n",
- layer->device_id);
-
- if (V4L2_BUF_TYPE_VIDEO_OUTPUT != p->type) {
- v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
- return -EINVAL;
- }
-
- /* If this file handle is not allowed to do IO, return error */
- if (!fh->io_allowed) {
- v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n");
- return -EACCES;
- }
-
- return vb2_qbuf(&layer->buffer_queue, p);
-}
-
-static int vpbe_display_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "VIDIOC_QUERYBUF, layer id = %d\n",
- layer->device_id);
-
- if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) {
- v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
- return -EINVAL;
- }
- /* Call vb2_querybuf to get information */
- return vb2_querybuf(&layer->buffer_queue, buf);
-}
-
-static int vpbe_display_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *req_buf)
-{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
- struct vb2_queue *q;
- int ret;
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_reqbufs\n");
-
- if (V4L2_BUF_TYPE_VIDEO_OUTPUT != req_buf->type) {
- v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
- return -EINVAL;
- }
-
- /* If io users of the layer is not zero, return error */
- if (0 != layer->io_usrs) {
- v4l2_err(&vpbe_dev->v4l2_dev, "not IO user\n");
- return -EBUSY;
- }
- /* Initialize videobuf queue as per the buffer type */
- layer->alloc_ctx = vb2_dma_contig_init_ctx(vpbe_dev->pdev);
- if (IS_ERR(layer->alloc_ctx)) {
- v4l2_err(&vpbe_dev->v4l2_dev, "Failed to get the context\n");
- return PTR_ERR(layer->alloc_ctx);
- }
- q = &layer->buffer_queue;
- memset(q, 0, sizeof(*q));
- q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- q->io_modes = VB2_MMAP | VB2_USERPTR;
- q->drv_priv = fh;
- q->ops = &video_qops;
- q->mem_ops = &vb2_dma_contig_memops;
- q->buf_struct_size = sizeof(struct vpbe_disp_buffer);
- q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- q->min_buffers_needed = 1;
-
- ret = vb2_queue_init(q);
- if (ret) {
- v4l2_err(&vpbe_dev->v4l2_dev, "vb2_queue_init() failed\n");
- vb2_dma_contig_cleanup_ctx(layer->alloc_ctx);
- return ret;
- }
- /* Set io allowed member of file handle to TRUE */
- fh->io_allowed = 1;
- /* Increment io usrs member of layer object to 1 */
- layer->io_usrs = 1;
- /* Store type of memory requested in layer object */
- layer->memory = req_buf->memory;
- /* Initialize buffer queue */
- INIT_LIST_HEAD(&layer->dma_queue);
- /* Allocate buffers */
- return vb2_reqbufs(q, req_buf);
-}
-
-/*
- * vpbe_display_mmap()
- * It is used to map kernel space buffers into user spaces
- */
-static int vpbe_display_mmap(struct file *filep, struct vm_area_struct *vma)
-{
- /* Get the layer object and file handle object */
- struct vpbe_fh *fh = filep->private_data;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
- int ret;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_mmap\n");
-
- if (mutex_lock_interruptible(&layer->opslock))
- return -ERESTARTSYS;
- ret = vb2_mmap(&layer->buffer_queue, vma);
- mutex_unlock(&layer->opslock);
- return ret;
-}
-
-/* vpbe_display_poll(): It is used for select/poll system call
- */
-static unsigned int vpbe_display_poll(struct file *filep, poll_table *wait)
-{
- struct vpbe_fh *fh = filep->private_data;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
- unsigned int err = 0;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_poll\n");
- if (layer->started) {
- mutex_lock(&layer->opslock);
- err = vb2_poll(&layer->buffer_queue, filep, wait);
- mutex_unlock(&layer->opslock);
- }
- return err;
-}
-
/*
* vpbe_display_open()
* It creates object of file handle structure and stores it in private_data
@@ -1475,30 +1149,22 @@
*/
static int vpbe_display_open(struct file *file)
{
- struct vpbe_fh *fh = NULL;
struct vpbe_layer *layer = video_drvdata(file);
- struct video_device *vdev = video_devdata(file);
struct vpbe_display *disp_dev = layer->disp_dev;
struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
struct osd_state *osd_device = disp_dev->osd_device;
int err;
- /* Allocate memory for the file handle object */
- fh = kmalloc(sizeof(struct vpbe_fh), GFP_KERNEL);
- if (fh == NULL) {
- v4l2_err(&vpbe_dev->v4l2_dev,
- "unable to allocate memory for file handle object\n");
- return -ENOMEM;
+ /* creating context for file descriptor */
+ err = v4l2_fh_open(file);
+ if (err) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "v4l2_fh_open failed\n");
+ return err;
}
- v4l2_fh_init(&fh->fh, vdev);
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "vpbe display open plane = %d\n",
- layer->device_id);
- /* store pointer to fh in private_data member of filep */
- file->private_data = fh;
- fh->layer = layer;
- fh->disp_dev = disp_dev;
+ /* leaving if layer is already initialized */
+ if (!v4l2_fh_is_singular_file(file))
+ return err;
if (!layer->usrs) {
if (mutex_lock_interruptible(&layer->opslock))
@@ -1511,15 +1177,12 @@
/* Couldn't get layer */
v4l2_err(&vpbe_dev->v4l2_dev,
"Display Manager failed to allocate layer\n");
- kfree(fh);
+ v4l2_fh_release(file);
return -EINVAL;
}
}
/* Increment layer usrs counter */
layer->usrs++;
- /* Set io_allowed member to false */
- fh->io_allowed = 0;
- v4l2_fh_add(&fh->fh);
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
"vpbe display device opened successfully\n");
return 0;
@@ -1532,30 +1195,18 @@
*/
static int vpbe_display_release(struct file *file)
{
- /* Get the layer object and file handle object */
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_layer *layer = fh->layer;
+ struct vpbe_layer *layer = video_drvdata(file);
struct osd_layer_config *cfg = &layer->layer_info.config;
- struct vpbe_display *disp_dev = fh->disp_dev;
+ struct vpbe_display *disp_dev = layer->disp_dev;
struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
struct osd_state *osd_device = disp_dev->osd_device;
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_release\n");
mutex_lock(&layer->opslock);
- /* if this instance is doing IO */
- if (fh->io_allowed) {
- /* Reset io_usrs member of layer object */
- layer->io_usrs = 0;
- osd_device->ops.disable_layer(osd_device,
- layer->layer_info.id);
- layer->started = 0;
- /* Free buffers allocated */
- vb2_queue_release(&layer->buffer_queue);
- vb2_dma_contig_cleanup_ctx(&layer->buffer_queue);
- }
-
+ osd_device->ops.disable_layer(osd_device,
+ layer->layer_info.id);
/* Decrement layer usrs counter */
layer->usrs--;
/* If this file handle has initialize encoder device, reset it */
@@ -1575,14 +1226,9 @@
layer->layer_info.id);
}
- v4l2_fh_del(&fh->fh);
- v4l2_fh_exit(&fh->fh);
- file->private_data = NULL;
+ _vb2_fop_release(file, NULL);
mutex_unlock(&layer->opslock);
- /* Free memory allocated to file handle object */
- kfree(fh);
-
disp_dev->cbcr_ofst = 0;
return 0;
@@ -1595,20 +1241,27 @@
.vidioc_enum_fmt_vid_out = vpbe_display_enum_fmt,
.vidioc_s_fmt_vid_out = vpbe_display_s_fmt,
.vidioc_try_fmt_vid_out = vpbe_display_try_fmt,
- .vidioc_reqbufs = vpbe_display_reqbufs,
- .vidioc_querybuf = vpbe_display_querybuf,
- .vidioc_qbuf = vpbe_display_qbuf,
- .vidioc_dqbuf = vpbe_display_dqbuf,
- .vidioc_streamon = vpbe_display_streamon,
- .vidioc_streamoff = vpbe_display_streamoff,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+
.vidioc_cropcap = vpbe_display_cropcap,
.vidioc_g_crop = vpbe_display_g_crop,
.vidioc_s_crop = vpbe_display_s_crop,
+
.vidioc_s_std = vpbe_display_s_std,
.vidioc_g_std = vpbe_display_g_std,
+
.vidioc_enum_output = vpbe_display_enum_output,
.vidioc_s_output = vpbe_display_s_output,
.vidioc_g_output = vpbe_display_g_output,
+
.vidioc_s_dv_timings = vpbe_display_s_dv_timings,
.vidioc_g_dv_timings = vpbe_display_g_dv_timings,
.vidioc_enum_dv_timings = vpbe_display_enum_dv_timings,
@@ -1619,8 +1272,8 @@
.open = vpbe_display_open,
.release = vpbe_display_release,
.unlocked_ioctl = video_ioctl2,
- .mmap = vpbe_display_mmap,
- .poll = vpbe_display_poll
+ .mmap = vb2_fop_mmap,
+ .poll = vb2_fop_poll,
};
static int vpbe_device_get(struct device *dev, void *data)
@@ -1700,6 +1353,7 @@
(int)vpbe_display_layer,
(int)&vpbe_display_layer->video_dev);
+ vpbe_display_layer->video_dev.queue = &vpbe_display_layer->buffer_queue;
err = video_register_device(&vpbe_display_layer->video_dev,
VFL_TYPE_GRABBER,
-1);
@@ -1724,9 +1378,10 @@
*/
static int vpbe_display_probe(struct platform_device *pdev)
{
- struct vpbe_layer *vpbe_display_layer;
struct vpbe_display *disp_dev;
+ struct v4l2_device *v4l2_dev;
struct resource *res = NULL;
+ struct vb2_queue *q;
int k;
int i;
int err;
@@ -1748,13 +1403,14 @@
vpbe_device_get);
if (err < 0)
return err;
+
+ v4l2_dev = &disp_dev->vpbe_dev->v4l2_dev;
/* Initialize the vpbe display controller */
if (NULL != disp_dev->vpbe_dev->ops.initialize) {
err = disp_dev->vpbe_dev->ops.initialize(&pdev->dev,
disp_dev->vpbe_dev);
if (err) {
- v4l2_err(&disp_dev->vpbe_dev->v4l2_dev,
- "Error initing vpbe\n");
+ v4l2_err(v4l2_dev, "Error initing vpbe\n");
err = -ENOMEM;
goto probe_out;
}
@@ -1769,8 +1425,7 @@
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
- v4l2_err(&disp_dev->vpbe_dev->v4l2_dev,
- "Unable to get VENC interrupt resource\n");
+ v4l2_err(v4l2_dev, "Unable to get VENC interrupt resource\n");
err = -ENODEV;
goto probe_out;
}
@@ -1779,30 +1434,57 @@
err = devm_request_irq(&pdev->dev, irq, venc_isr, 0,
VPBE_DISPLAY_DRIVER, disp_dev);
if (err) {
- v4l2_err(&disp_dev->vpbe_dev->v4l2_dev,
- "Unable to request interrupt\n");
+ v4l2_err(v4l2_dev, "VPBE IRQ request failed\n");
goto probe_out;
}
for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
+ /* initialize vb2 queue */
+ q = &disp_dev->dev[i]->buffer_queue;
+ memset(q, 0, sizeof(*q));
+ q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+ q->drv_priv = disp_dev->dev[i];
+ q->ops = &video_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct vpbe_disp_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->min_buffers_needed = 1;
+ q->lock = &disp_dev->dev[i]->opslock;
+ err = vb2_queue_init(q);
+ if (err) {
+ v4l2_err(v4l2_dev, "vb2_queue_init() failed\n");
+ goto probe_out;
+ }
+
+ disp_dev->dev[i]->alloc_ctx =
+ vb2_dma_contig_init_ctx(disp_dev->vpbe_dev->pdev);
+ if (IS_ERR(disp_dev->dev[i]->alloc_ctx)) {
+ v4l2_err(v4l2_dev, "Failed to get the context\n");
+ err = PTR_ERR(disp_dev->dev[i]->alloc_ctx);
+ goto probe_out;
+ }
+
+ INIT_LIST_HEAD(&disp_dev->dev[i]->dma_queue);
+
if (register_device(disp_dev->dev[i], disp_dev, pdev)) {
err = -ENODEV;
goto probe_out;
}
}
- printk(KERN_DEBUG "Successfully completed the probing of vpbe v4l2 device\n");
+ v4l2_dbg(1, debug, v4l2_dev,
+ "Successfully completed the probing of vpbe v4l2 device\n");
+
return 0;
probe_out:
for (k = 0; k < VPBE_DISPLAY_MAX_DEVICES; k++) {
- /* Get the pointer to the layer object */
- vpbe_display_layer = disp_dev->dev[k];
/* Unregister video device */
- if (vpbe_display_layer) {
- video_unregister_device(
- &vpbe_display_layer->video_dev);
- kfree(disp_dev->dev[k]);
+ if (disp_dev->dev[k] != NULL) {
+ vb2_dma_contig_cleanup_ctx(disp_dev->dev[k]->alloc_ctx);
+ video_unregister_device(&disp_dev->dev[k]->video_dev);
+ kfree(disp_dev->dev[k]);
}
}
return err;
@@ -1828,6 +1510,7 @@
for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
/* Get the pointer to the layer object */
vpbe_display_layer = disp_dev->dev[i];
+ vb2_dma_contig_cleanup_ctx(vpbe_display_layer->alloc_ctx);
/* Unregister video device */
video_unregister_device(&vpbe_display_layer->video_dev);
@@ -1843,7 +1526,6 @@
static struct platform_driver vpbe_display_driver = {
.driver = {
.name = VPBE_DISPLAY_DRIVER,
- .owner = THIS_MODULE,
.bus = &platform_bus_type,
},
.probe = vpbe_display_probe,
diff --git a/drivers/media/platform/davinci/vpbe_osd.c b/drivers/media/platform/davinci/vpbe_osd.c
index d053c26..7d96a4b 100644
--- a/drivers/media/platform/davinci/vpbe_osd.c
+++ b/drivers/media/platform/davinci/vpbe_osd.c
@@ -1585,7 +1585,6 @@
.remove = osd_remove,
.driver = {
.name = MODULE_NAME,
- .owner = THIS_MODULE,
},
.id_table = vpbe_osd_devtype
};
diff --git a/drivers/media/platform/davinci/vpbe_venc.c b/drivers/media/platform/davinci/vpbe_venc.c
index 14a023a..36ed146 100644
--- a/drivers/media/platform/davinci/vpbe_venc.c
+++ b/drivers/media/platform/davinci/vpbe_venc.c
@@ -687,7 +687,6 @@
.remove = venc_remove,
.driver = {
.name = MODULE_NAME,
- .owner = THIS_MODULE,
},
.id_table = vpbe_venc_devtype
};
diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c
index de55f47..b41bf7e 100644
--- a/drivers/media/platform/davinci/vpfe_capture.c
+++ b/drivers/media/platform/davinci/vpfe_capture.c
@@ -414,13 +414,13 @@
/* assume V4L2_PIX_FMT_UYVY as default */
pix->pixelformat = V4L2_PIX_FMT_UYVY;
v4l2_fill_mbus_format(&mbus_fmt, pix,
- V4L2_MBUS_FMT_YUYV10_2X10);
+ MEDIA_BUS_FMT_YUYV10_2X10);
} else {
pix->field = V4L2_FIELD_NONE;
/* assume V4L2_PIX_FMT_SBGGR8 */
pix->pixelformat = V4L2_PIX_FMT_SBGGR8;
v4l2_fill_mbus_format(&mbus_fmt, pix,
- V4L2_MBUS_FMT_SBGGR8_1X8);
+ MEDIA_BUS_FMT_SBGGR8_1X8);
}
/* if sub device supports g_mbus_fmt, override the defaults */
@@ -930,8 +930,8 @@
v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querycap\n");
- cap->version = VPFE_CAPTURE_VERSION_CODE;
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver));
strlcpy(cap->bus_info, "VPFE", sizeof(cap->bus_info));
strlcpy(cap->card, vpfe_dev->cfg->card_name, sizeof(cap->card));
@@ -2031,7 +2031,6 @@
static struct platform_driver vpfe_driver = {
.driver = {
.name = CAPTURE_DRV_NAME,
- .owner = THIS_MODULE,
.pm = &vpfe_dev_pm_ops,
},
.probe = vpfe_probe,
diff --git a/drivers/media/platform/davinci/vpif.c b/drivers/media/platform/davinci/vpif.c
index 3dad5bd..0380cf2 100644
--- a/drivers/media/platform/davinci/vpif.c
+++ b/drivers/media/platform/davinci/vpif.c
@@ -467,7 +467,6 @@
static struct platform_driver vpif_driver = {
.driver = {
.name = "vpif",
- .owner = THIS_MODULE,
.pm = vpif_pm_ops,
},
.remove = vpif_remove,
diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c
index 3ccb26f..fa0a515 100644
--- a/drivers/media/platform/davinci/vpif_capture.c
+++ b/drivers/media/platform/davinci/vpif_capture.c
@@ -311,6 +311,8 @@
.start_streaming = vpif_start_streaming,
.stop_streaming = vpif_stop_streaming,
.buf_queue = vpif_buffer_queue,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
};
/**
@@ -1656,7 +1658,6 @@
static __refdata struct platform_driver vpif_driver = {
.driver = {
.name = VPIF_DRIVER_NAME,
- .owner = THIS_MODULE,
.pm = &vpif_pm_ops,
},
.probe = vpif_probe,
diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c
index 8d6ced5..839c24d 100644
--- a/drivers/media/platform/davinci/vpif_display.c
+++ b/drivers/media/platform/davinci/vpif_display.c
@@ -1459,7 +1459,6 @@
static __refdata struct platform_driver vpif_driver = {
.driver = {
.name = VPIF_DRIVER_NAME,
- .owner = THIS_MODULE,
.pm = &vpif_pm_ops,
},
.probe = vpif_probe,
diff --git a/drivers/media/platform/davinci/vpss.c b/drivers/media/platform/davinci/vpss.c
index 31120b4..fce86f1 100644
--- a/drivers/media/platform/davinci/vpss.c
+++ b/drivers/media/platform/davinci/vpss.c
@@ -504,7 +504,6 @@
static struct platform_driver vpss_driver = {
.driver = {
.name = "vpss",
- .owner = THIS_MODULE,
.pm = &vpss_pm_ops,
},
.remove = vpss_remove,
diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c
index b4c9f1d..fd2891c 100644
--- a/drivers/media/platform/exynos-gsc/gsc-core.c
+++ b/drivers/media/platform/exynos-gsc/gsc-core.c
@@ -54,7 +54,7 @@
.corder = GSC_CBCR,
.num_planes = 1,
.num_comp = 1,
- .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
}, {
.name = "YUV 4:2:2 packed, CbYCrY",
.pixelformat = V4L2_PIX_FMT_UYVY,
@@ -64,7 +64,7 @@
.corder = GSC_CBCR,
.num_planes = 1,
.num_comp = 1,
- .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
}, {
.name = "YUV 4:2:2 packed, CrYCbY",
.pixelformat = V4L2_PIX_FMT_VYUY,
@@ -74,7 +74,7 @@
.corder = GSC_CRCB,
.num_planes = 1,
.num_comp = 1,
- .mbus_code = V4L2_MBUS_FMT_VYUY8_2X8,
+ .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8,
}, {
.name = "YUV 4:2:2 packed, YCrYCb",
.pixelformat = V4L2_PIX_FMT_YVYU,
@@ -84,7 +84,7 @@
.corder = GSC_CRCB,
.num_planes = 1,
.num_comp = 1,
- .mbus_code = V4L2_MBUS_FMT_YVYU8_2X8,
+ .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8,
}, {
.name = "YUV 4:4:4 planar, YCbYCr",
.pixelformat = V4L2_PIX_FMT_YUV32,
@@ -319,21 +319,22 @@
return 0;
}
-static u32 get_plane_info(struct gsc_frame *frm, u32 addr, u32 *index)
+static int get_plane_info(struct gsc_frame *frm, u32 addr, u32 *index, u32 *ret_addr)
{
if (frm->addr.y == addr) {
*index = 0;
- return frm->addr.y;
+ *ret_addr = frm->addr.y;
} else if (frm->addr.cb == addr) {
*index = 1;
- return frm->addr.cb;
+ *ret_addr = frm->addr.cb;
} else if (frm->addr.cr == addr) {
*index = 2;
- return frm->addr.cr;
+ *ret_addr = frm->addr.cr;
} else {
pr_err("Plane address is wrong");
return -EINVAL;
}
+ return 0;
}
void gsc_set_prefbuf(struct gsc_dev *gsc, struct gsc_frame *frm)
@@ -352,9 +353,11 @@
u32 t_min, t_max;
t_min = min3(frm->addr.y, frm->addr.cb, frm->addr.cr);
- low_addr = get_plane_info(frm, t_min, &low_plane);
+ if (get_plane_info(frm, t_min, &low_plane, &low_addr))
+ return;
t_max = max3(frm->addr.y, frm->addr.cb, frm->addr.cr);
- high_addr = get_plane_info(frm, t_max, &high_plane);
+ if (get_plane_info(frm, t_max, &high_plane, &high_addr))
+ return;
mid_plane = 3 - (low_plane + high_plane);
if (mid_plane == 0)
@@ -1253,7 +1256,6 @@
.id_table = gsc_driver_ids,
.driver = {
.name = GSC_MODULE_NAME,
- .owner = THIS_MODULE,
.pm = &gsc_pm_ops,
.of_match_table = exynos_gsc_match,
}
diff --git a/drivers/media/platform/exynos-gsc/gsc-core.h b/drivers/media/platform/exynos-gsc/gsc-core.h
index ef0a656..0abdb17 100644
--- a/drivers/media/platform/exynos-gsc/gsc-core.h
+++ b/drivers/media/platform/exynos-gsc/gsc-core.h
@@ -117,7 +117,7 @@
* @flags: flags indicating which operation mode format applies to
*/
struct gsc_fmt {
- enum v4l2_mbus_pixelcode mbus_code;
+ u32 mbus_code;
char *name;
u32 pixelformat;
u32 color;
diff --git a/drivers/media/platform/exynos4-is/Kconfig b/drivers/media/platform/exynos4-is/Kconfig
index 8b0a506..9986ed7 100644
--- a/drivers/media/platform/exynos4-is/Kconfig
+++ b/drivers/media/platform/exynos4-is/Kconfig
@@ -2,7 +2,7 @@
config VIDEO_SAMSUNG_EXYNOS4_IS
bool "Samsung S5P/EXYNOS4 SoC series Camera Subsystem driver"
depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
- depends on (PLAT_S5P || ARCH_EXYNOS || COMPILE_TEST)
+ depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
depends on OF && COMMON_CLK
help
Say Y here to enable camera host interface devices for
diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c
index 3d2babd..8a2fd8c 100644
--- a/drivers/media/platform/exynos4-is/fimc-capture.c
+++ b/drivers/media/platform/exynos4-is/fimc-capture.c
@@ -749,7 +749,7 @@
return -EINVAL;
strncpy(f->description, fmt->name, sizeof(f->description) - 1);
f->pixelformat = fmt->fourcc;
- if (fmt->fourcc == V4L2_MBUS_FMT_JPEG_1X8)
+ if (fmt->fourcc == MEDIA_BUS_FMT_JPEG_1X8)
f->flags |= V4L2_FMT_FLAG_COMPRESSED;
return 0;
}
diff --git a/drivers/media/platform/exynos4-is/fimc-core.c b/drivers/media/platform/exynos4-is/fimc-core.c
index b70fd99..1101c41 100644
--- a/drivers/media/platform/exynos4-is/fimc-core.c
+++ b/drivers/media/platform/exynos4-is/fimc-core.c
@@ -81,7 +81,7 @@
.flags = FMT_FLAGS_M2M_OUT | FMT_HAS_ALPHA,
}, {
.name = "YUV 4:4:4",
- .mbus_code = V4L2_MBUS_FMT_YUV10_1X30,
+ .mbus_code = MEDIA_BUS_FMT_YUV10_1X30,
.flags = FMT_FLAGS_WRITEBACK,
}, {
.name = "YUV 4:2:2 packed, YCbYCr",
@@ -90,7 +90,7 @@
.color = FIMC_FMT_YCBYCR422,
.memplanes = 1,
.colplanes = 1,
- .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
.flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM,
}, {
.name = "YUV 4:2:2 packed, CbYCrY",
@@ -99,7 +99,7 @@
.color = FIMC_FMT_CBYCRY422,
.memplanes = 1,
.colplanes = 1,
- .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
.flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM,
}, {
.name = "YUV 4:2:2 packed, CrYCbY",
@@ -108,7 +108,7 @@
.color = FIMC_FMT_CRYCBY422,
.memplanes = 1,
.colplanes = 1,
- .mbus_code = V4L2_MBUS_FMT_VYUY8_2X8,
+ .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8,
.flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM,
}, {
.name = "YUV 4:2:2 packed, YCrYCb",
@@ -117,7 +117,7 @@
.color = FIMC_FMT_YCRYCB422,
.memplanes = 1,
.colplanes = 1,
- .mbus_code = V4L2_MBUS_FMT_YVYU8_2X8,
+ .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8,
.flags = FMT_FLAGS_M2M | FMT_FLAGS_CAM,
}, {
.name = "YUV 4:2:2 planar, Y/Cb/Cr",
@@ -190,7 +190,7 @@
.depth = { 8 },
.memplanes = 1,
.colplanes = 1,
- .mbus_code = V4L2_MBUS_FMT_JPEG_1X8,
+ .mbus_code = MEDIA_BUS_FMT_JPEG_1X8,
.flags = FMT_FLAGS_CAM | FMT_FLAGS_COMPRESSED,
}, {
.name = "S5C73MX interleaved UYVY/JPEG",
@@ -200,7 +200,7 @@
.memplanes = 2,
.colplanes = 1,
.mdataplanes = 0x2, /* plane 1 holds frame meta data */
- .mbus_code = V4L2_MBUS_FMT_S5C_UYVY_JPEG_1X8,
+ .mbus_code = MEDIA_BUS_FMT_S5C_UYVY_JPEG_1X8,
.flags = FMT_FLAGS_CAM | FMT_FLAGS_COMPRESSED,
},
};
@@ -832,6 +832,7 @@
return -ENXIO;
}
+#ifdef CONFIG_PM
static int fimc_m2m_suspend(struct fimc_dev *fimc)
{
unsigned long flags;
@@ -870,6 +871,7 @@
return 0;
}
+#endif /* CONFIG_PM */
static const struct of_device_id fimc_of_match[];
@@ -1037,7 +1039,7 @@
return ret;
}
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
static int fimc_runtime_resume(struct device *dev)
{
struct fimc_dev *fimc = dev_get_drvdata(dev);
@@ -1292,7 +1294,6 @@
.driver = {
.of_match_table = fimc_of_match,
.name = FIMC_DRIVER_NAME,
- .owner = THIS_MODULE,
.pm = &fimc_pm_ops,
}
};
diff --git a/drivers/media/platform/exynos4-is/fimc-core.h b/drivers/media/platform/exynos4-is/fimc-core.h
index 6c75c6c..7328f08 100644
--- a/drivers/media/platform/exynos4-is/fimc-core.h
+++ b/drivers/media/platform/exynos4-is/fimc-core.h
@@ -579,8 +579,8 @@
static inline bool fimc_user_defined_mbus_fmt(u32 code)
{
- return (code == V4L2_MBUS_FMT_JPEG_1X8 ||
- code == V4L2_MBUS_FMT_S5C_UYVY_JPEG_1X8);
+ return (code == MEDIA_BUS_FMT_JPEG_1X8 ||
+ code == MEDIA_BUS_FMT_S5C_UYVY_JPEG_1X8);
}
/* Return the alpha component bit mask */
diff --git a/drivers/media/platform/exynos4-is/fimc-is-i2c.c b/drivers/media/platform/exynos4-is/fimc-is-i2c.c
index 371cad4..7521aa5 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-i2c.c
+++ b/drivers/media/platform/exynos4-is/fimc-is-i2c.c
@@ -81,7 +81,7 @@
return 0;
}
-#if defined(CONFIG_PM_RUNTIME) || defined(CONFIG_PM_SLEEP)
+#ifdef CONFIG_PM
static int fimc_is_i2c_runtime_suspend(struct device *dev)
{
struct fimc_is_i2c *isp_i2c = dev_get_drvdata(dev);
@@ -133,7 +133,6 @@
.driver = {
.of_match_table = fimc_is_i2c_of_match,
.name = "fimc-isp-i2c",
- .owner = THIS_MODULE,
.pm = &fimc_is_i2c_pm_ops,
}
};
diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c
index 22162b2..49658ca 100644
--- a/drivers/media/platform/exynos4-is/fimc-is.c
+++ b/drivers/media/platform/exynos4-is/fimc-is.c
@@ -428,8 +428,7 @@
* needed around for copying to the IS working memory every
* time before the Cortex-A5 is restarted.
*/
- if (is->fw.f_w)
- release_firmware(is->fw.f_w);
+ release_firmware(is->fw.f_w);
is->fw.f_w = fw;
done:
mutex_unlock(&is->lock);
@@ -814,9 +813,9 @@
return -ENOMEM;
is->irq = irq_of_parse_and_map(dev->of_node, 0);
- if (is->irq < 0) {
+ if (!is->irq) {
dev_err(dev, "no irq found\n");
- return is->irq;
+ return -EINVAL;
}
ret = fimc_is_get_clocks(is);
@@ -937,8 +936,7 @@
vb2_dma_contig_cleanup_ctx(is->alloc_ctx);
fimc_is_put_clocks(is);
fimc_is_debugfs_remove(is);
- if (is->fw.f_w)
- release_firmware(is->fw.f_w);
+ release_firmware(is->fw.f_w);
fimc_is_free_cpu_memory(is);
return 0;
@@ -962,7 +960,6 @@
.driver = {
.of_match_table = fimc_is_of_match,
.name = FIMC_IS_DRV_NAME,
- .owner = THIS_MODULE,
.pm = &fimc_is_pm_ops,
}
};
diff --git a/drivers/media/platform/exynos4-is/fimc-isp.c b/drivers/media/platform/exynos4-is/fimc-isp.c
index be62d6b..60c7449 100644
--- a/drivers/media/platform/exynos4-is/fimc-isp.c
+++ b/drivers/media/platform/exynos4-is/fimc-isp.c
@@ -41,21 +41,21 @@
.depth = { 8 },
.color = FIMC_FMT_RAW8,
.memplanes = 1,
- .mbus_code = V4L2_MBUS_FMT_SGRBG8_1X8,
+ .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
}, {
.name = "RAW10 (GRBG)",
.fourcc = V4L2_PIX_FMT_SGRBG10,
.depth = { 10 },
.color = FIMC_FMT_RAW10,
.memplanes = 1,
- .mbus_code = V4L2_MBUS_FMT_SGRBG10_1X10,
+ .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
}, {
.name = "RAW12 (GRBG)",
.fourcc = V4L2_PIX_FMT_SGRBG12,
.depth = { 12 },
.color = FIMC_FMT_RAW12,
.memplanes = 1,
- .mbus_code = V4L2_MBUS_FMT_SGRBG12_1X12,
+ .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
},
};
@@ -149,7 +149,7 @@
if (fmt->pad == FIMC_ISP_SD_PAD_SRC_FIFO) {
mf->colorspace = V4L2_COLORSPACE_JPEG;
- mf->code = V4L2_MBUS_FMT_YUV10_1X30;
+ mf->code = MEDIA_BUS_FMT_YUV10_1X30;
}
}
@@ -175,7 +175,7 @@
FIMC_ISP_SINK_WIDTH_MAX, 0,
&mf->height, FIMC_ISP_SINK_HEIGHT_MIN,
FIMC_ISP_SINK_HEIGHT_MAX, 0, 0);
- mf->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ mf->code = MEDIA_BUS_FMT_SGRBG10_1X10;
} else {
if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
format = v4l2_subdev_get_try_format(fh,
@@ -188,7 +188,7 @@
mf->height = format->height - FIMC_ISP_CAC_MARGIN_HEIGHT;
if (fmt->pad == FIMC_ISP_SD_PAD_SRC_FIFO) {
- mf->code = V4L2_MBUS_FMT_YUV10_1X30;
+ mf->code = MEDIA_BUS_FMT_YUV10_1X30;
mf->colorspace = V4L2_COLORSPACE_JPEG;
} else {
mf->code = format->code;
@@ -680,11 +680,11 @@
FIMC_ISP_CAC_MARGIN_WIDTH;
isp->sink_fmt.height = DEFAULT_PREVIEW_STILL_HEIGHT +
FIMC_ISP_CAC_MARGIN_HEIGHT;
- isp->sink_fmt.code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ isp->sink_fmt.code = MEDIA_BUS_FMT_SGRBG10_1X10;
isp->src_fmt.width = DEFAULT_PREVIEW_STILL_WIDTH;
isp->src_fmt.height = DEFAULT_PREVIEW_STILL_HEIGHT;
- isp->src_fmt.code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ isp->src_fmt.code = MEDIA_BUS_FMT_SGRBG10_1X10;
__is_set_frame_size(is, &isp->src_fmt);
}
diff --git a/drivers/media/platform/exynos4-is/fimc-lite-reg.c b/drivers/media/platform/exynos4-is/fimc-lite-reg.c
index bc3ec7d..0477716 100644
--- a/drivers/media/platform/exynos4-is/fimc-lite-reg.c
+++ b/drivers/media/platform/exynos4-is/fimc-lite-reg.c
@@ -112,24 +112,24 @@
}
static const u32 src_pixfmt_map[8][3] = {
- { V4L2_MBUS_FMT_YUYV8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_YCBYCR,
+ { MEDIA_BUS_FMT_YUYV8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_YCBYCR,
FLITE_REG_CIGCTRL_YUV422_1P },
- { V4L2_MBUS_FMT_YVYU8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_YCRYCB,
+ { MEDIA_BUS_FMT_YVYU8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_YCRYCB,
FLITE_REG_CIGCTRL_YUV422_1P },
- { V4L2_MBUS_FMT_UYVY8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_CBYCRY,
+ { MEDIA_BUS_FMT_UYVY8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_CBYCRY,
FLITE_REG_CIGCTRL_YUV422_1P },
- { V4L2_MBUS_FMT_VYUY8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_CRYCBY,
+ { MEDIA_BUS_FMT_VYUY8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_CRYCBY,
FLITE_REG_CIGCTRL_YUV422_1P },
- { V4L2_MBUS_FMT_SGRBG8_1X8, 0, FLITE_REG_CIGCTRL_RAW8 },
- { V4L2_MBUS_FMT_SGRBG10_1X10, 0, FLITE_REG_CIGCTRL_RAW10 },
- { V4L2_MBUS_FMT_SGRBG12_1X12, 0, FLITE_REG_CIGCTRL_RAW12 },
- { V4L2_MBUS_FMT_JPEG_1X8, 0, FLITE_REG_CIGCTRL_USER(1) },
+ { MEDIA_BUS_FMT_SGRBG8_1X8, 0, FLITE_REG_CIGCTRL_RAW8 },
+ { MEDIA_BUS_FMT_SGRBG10_1X10, 0, FLITE_REG_CIGCTRL_RAW10 },
+ { MEDIA_BUS_FMT_SGRBG12_1X12, 0, FLITE_REG_CIGCTRL_RAW12 },
+ { MEDIA_BUS_FMT_JPEG_1X8, 0, FLITE_REG_CIGCTRL_USER(1) },
};
/* Set camera input pixel format and resolution */
void flite_hw_set_source_format(struct fimc_lite *dev, struct flite_frame *f)
{
- enum v4l2_mbus_pixelcode pixelcode = f->fmt->mbus_code;
+ u32 pixelcode = f->fmt->mbus_code;
int i = ARRAY_SIZE(src_pixfmt_map);
u32 cfg;
@@ -232,10 +232,10 @@
static void flite_hw_set_out_order(struct fimc_lite *dev, struct flite_frame *f)
{
static const u32 pixcode[4][2] = {
- { V4L2_MBUS_FMT_YUYV8_2X8, FLITE_REG_CIODMAFMT_YCBYCR },
- { V4L2_MBUS_FMT_YVYU8_2X8, FLITE_REG_CIODMAFMT_YCRYCB },
- { V4L2_MBUS_FMT_UYVY8_2X8, FLITE_REG_CIODMAFMT_CBYCRY },
- { V4L2_MBUS_FMT_VYUY8_2X8, FLITE_REG_CIODMAFMT_CRYCBY },
+ { MEDIA_BUS_FMT_YUYV8_2X8, FLITE_REG_CIODMAFMT_YCBYCR },
+ { MEDIA_BUS_FMT_YVYU8_2X8, FLITE_REG_CIODMAFMT_YCRYCB },
+ { MEDIA_BUS_FMT_UYVY8_2X8, FLITE_REG_CIODMAFMT_CBYCRY },
+ { MEDIA_BUS_FMT_VYUY8_2X8, FLITE_REG_CIODMAFMT_CRYCBY },
};
u32 cfg = readl(dev->regs + FLITE_REG_CIODMAFMT);
int i = ARRAY_SIZE(pixcode);
diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c
index a97d235..2510f18 100644
--- a/drivers/media/platform/exynos4-is/fimc-lite.c
+++ b/drivers/media/platform/exynos4-is/fimc-lite.c
@@ -48,7 +48,7 @@
.depth = { 16 },
.color = FIMC_FMT_YCBYCR422,
.memplanes = 1,
- .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
.flags = FMT_FLAGS_YUV,
}, {
.name = "YUV 4:2:2 packed, CbYCrY",
@@ -57,7 +57,7 @@
.depth = { 16 },
.color = FIMC_FMT_CBYCRY422,
.memplanes = 1,
- .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
.flags = FMT_FLAGS_YUV,
}, {
.name = "YUV 4:2:2 packed, CrYCbY",
@@ -66,7 +66,7 @@
.depth = { 16 },
.color = FIMC_FMT_CRYCBY422,
.memplanes = 1,
- .mbus_code = V4L2_MBUS_FMT_VYUY8_2X8,
+ .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8,
.flags = FMT_FLAGS_YUV,
}, {
.name = "YUV 4:2:2 packed, YCrYCb",
@@ -75,7 +75,7 @@
.depth = { 16 },
.color = FIMC_FMT_YCRYCB422,
.memplanes = 1,
- .mbus_code = V4L2_MBUS_FMT_YVYU8_2X8,
+ .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8,
.flags = FMT_FLAGS_YUV,
}, {
.name = "RAW8 (GRBG)",
@@ -84,7 +84,7 @@
.depth = { 8 },
.color = FIMC_FMT_RAW8,
.memplanes = 1,
- .mbus_code = V4L2_MBUS_FMT_SGRBG8_1X8,
+ .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
.flags = FMT_FLAGS_RAW_BAYER,
}, {
.name = "RAW10 (GRBG)",
@@ -93,7 +93,7 @@
.depth = { 16 },
.color = FIMC_FMT_RAW10,
.memplanes = 1,
- .mbus_code = V4L2_MBUS_FMT_SGRBG10_1X10,
+ .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
.flags = FMT_FLAGS_RAW_BAYER,
}, {
.name = "RAW12 (GRBG)",
@@ -102,7 +102,7 @@
.depth = { 16 },
.color = FIMC_FMT_RAW12,
.memplanes = 1,
- .mbus_code = V4L2_MBUS_FMT_SGRBG12_1X12,
+ .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
.flags = FMT_FLAGS_RAW_BAYER,
},
};
@@ -1588,7 +1588,7 @@
return ret;
}
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
static int fimc_lite_runtime_resume(struct device *dev)
{
struct fimc_lite *fimc = dev_get_drvdata(dev);
@@ -1720,7 +1720,6 @@
.driver = {
.of_match_table = flite_of_match,
.name = FIMC_LITE_DRV_NAME,
- .owner = THIS_MODULE,
.pm = &fimc_lite_pm_ops,
}
};
diff --git a/drivers/media/platform/exynos4-is/fimc-m2m.c b/drivers/media/platform/exynos4-is/fimc-m2m.c
index 0ad1b6f..f3ed10b 100644
--- a/drivers/media/platform/exynos4-is/fimc-m2m.c
+++ b/drivers/media/platform/exynos4-is/fimc-m2m.c
@@ -538,7 +538,9 @@
.vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
.vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
.vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
.vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
+#endif
.vidioc_streamon = v4l2_m2m_ioctl_streamon,
.vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
.vidioc_g_crop = fimc_m2m_g_crop,
diff --git a/drivers/media/platform/exynos4-is/fimc-reg.c b/drivers/media/platform/exynos4-is/fimc-reg.c
index 2d77fd8..df0cbcb 100644
--- a/drivers/media/platform/exynos4-is/fimc-reg.c
+++ b/drivers/media/platform/exynos4-is/fimc-reg.c
@@ -592,10 +592,10 @@
};
static const struct mbus_pixfmt_desc pix_desc[] = {
- { V4L2_MBUS_FMT_YUYV8_2X8, FIMC_REG_CISRCFMT_ORDER422_YCBYCR, 8 },
- { V4L2_MBUS_FMT_YVYU8_2X8, FIMC_REG_CISRCFMT_ORDER422_YCRYCB, 8 },
- { V4L2_MBUS_FMT_VYUY8_2X8, FIMC_REG_CISRCFMT_ORDER422_CRYCBY, 8 },
- { V4L2_MBUS_FMT_UYVY8_2X8, FIMC_REG_CISRCFMT_ORDER422_CBYCRY, 8 },
+ { MEDIA_BUS_FMT_YUYV8_2X8, FIMC_REG_CISRCFMT_ORDER422_YCBYCR, 8 },
+ { MEDIA_BUS_FMT_YVYU8_2X8, FIMC_REG_CISRCFMT_ORDER422_YCRYCB, 8 },
+ { MEDIA_BUS_FMT_VYUY8_2X8, FIMC_REG_CISRCFMT_ORDER422_CRYCBY, 8 },
+ { MEDIA_BUS_FMT_UYVY8_2X8, FIMC_REG_CISRCFMT_ORDER422_CBYCRY, 8 },
};
int fimc_hw_set_camera_source(struct fimc_dev *fimc,
@@ -689,11 +689,11 @@
/* TODO: add remaining supported formats. */
switch (vid_cap->ci_fmt.code) {
- case V4L2_MBUS_FMT_VYUY8_2X8:
+ case MEDIA_BUS_FMT_VYUY8_2X8:
tmp = FIMC_REG_CSIIMGFMT_YCBCR422_8BIT;
break;
- case V4L2_MBUS_FMT_JPEG_1X8:
- case V4L2_MBUS_FMT_S5C_UYVY_JPEG_1X8:
+ case MEDIA_BUS_FMT_JPEG_1X8:
+ case MEDIA_BUS_FMT_S5C_UYVY_JPEG_1X8:
tmp = FIMC_REG_CSIIMGFMT_USER(1);
cfg |= FIMC_REG_CIGCTRL_CAM_JPEG;
break;
diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c
index 54c49d5..f315ef9 100644
--- a/drivers/media/platform/exynos4-is/media-dev.c
+++ b/drivers/media/platform/exynos4-is/media-dev.c
@@ -1469,7 +1469,6 @@
.driver = {
.of_match_table = of_match_ptr(fimc_md_of_match),
.name = "s5p-fimc-md",
- .owner = THIS_MODULE,
}
};
diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c
index db6fd14..2504aa8 100644
--- a/drivers/media/platform/exynos4-is/mipi-csis.c
+++ b/drivers/media/platform/exynos4-is/mipi-csis.c
@@ -238,34 +238,34 @@
*/
struct csis_pix_format {
unsigned int pix_width_alignment;
- enum v4l2_mbus_pixelcode code;
+ u32 code;
u32 fmt_reg;
u8 data_alignment;
};
static const struct csis_pix_format s5pcsis_formats[] = {
{
- .code = V4L2_MBUS_FMT_VYUY8_2X8,
+ .code = MEDIA_BUS_FMT_VYUY8_2X8,
.fmt_reg = S5PCSIS_CFG_FMT_YCBCR422_8BIT,
.data_alignment = 32,
}, {
- .code = V4L2_MBUS_FMT_JPEG_1X8,
+ .code = MEDIA_BUS_FMT_JPEG_1X8,
.fmt_reg = S5PCSIS_CFG_FMT_USER(1),
.data_alignment = 32,
}, {
- .code = V4L2_MBUS_FMT_S5C_UYVY_JPEG_1X8,
+ .code = MEDIA_BUS_FMT_S5C_UYVY_JPEG_1X8,
.fmt_reg = S5PCSIS_CFG_FMT_USER(1),
.data_alignment = 32,
}, {
- .code = V4L2_MBUS_FMT_SGRBG8_1X8,
+ .code = MEDIA_BUS_FMT_SGRBG8_1X8,
.fmt_reg = S5PCSIS_CFG_FMT_RAW8,
.data_alignment = 24,
}, {
- .code = V4L2_MBUS_FMT_SGRBG10_1X10,
+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
.fmt_reg = S5PCSIS_CFG_FMT_RAW10,
.data_alignment = 24,
}, {
- .code = V4L2_MBUS_FMT_SGRBG12_1X12,
+ .code = MEDIA_BUS_FMT_SGRBG12_1X12,
.fmt_reg = S5PCSIS_CFG_FMT_RAW12,
.data_alignment = 24,
}
@@ -978,7 +978,7 @@
}
#endif
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
static int s5pcsis_runtime_suspend(struct device *dev)
{
return s5pcsis_pm_suspend(dev, true);
@@ -1041,7 +1041,6 @@
.driver = {
.of_match_table = s5pcsis_of_match,
.name = CSIS_DRIVER_NAME,
- .owner = THIS_MODULE,
.pm = &s5pcsis_pm_ops,
},
};
diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c
index d5dc198..bbf4281 100644
--- a/drivers/media/platform/fsl-viu.c
+++ b/drivers/media/platform/fsl-viu.c
@@ -604,10 +604,11 @@
{
strcpy(cap->driver, "viu");
strcpy(cap->card, "viu");
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_STREAMING |
V4L2_CAP_VIDEO_OVERLAY |
V4L2_CAP_READWRITE;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -1680,7 +1681,6 @@
#endif
.driver = {
.name = DRV_NAME,
- .owner = THIS_MODULE,
.of_match_table = mpc512x_viu_of_match,
},
};
diff --git a/drivers/media/platform/indycam.c b/drivers/media/platform/indycam.c
deleted file mode 100644
index f1d192b..0000000
--- a/drivers/media/platform/indycam.c
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
- * indycam.c - Silicon Graphics IndyCam digital camera driver
- *
- * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
- * Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/major.h>
-#include <linux/module.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-
-/* IndyCam decodes stream of photons into digital image representation ;-) */
-#include <linux/videodev2.h>
-#include <linux/i2c.h>
-#include <media/v4l2-device.h>
-
-#include "indycam.h"
-
-#define INDYCAM_MODULE_VERSION "0.0.5"
-
-MODULE_DESCRIPTION("SGI IndyCam driver");
-MODULE_VERSION(INDYCAM_MODULE_VERSION);
-MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>");
-MODULE_LICENSE("GPL");
-
-
-// #define INDYCAM_DEBUG
-
-#ifdef INDYCAM_DEBUG
-#define dprintk(x...) printk("IndyCam: " x);
-#define indycam_regdump(client) indycam_regdump_debug(client)
-#else
-#define dprintk(x...)
-#define indycam_regdump(client)
-#endif
-
-struct indycam {
- struct v4l2_subdev sd;
- u8 version;
-};
-
-static inline struct indycam *to_indycam(struct v4l2_subdev *sd)
-{
- return container_of(sd, struct indycam, sd);
-}
-
-static const u8 initseq[] = {
- INDYCAM_CONTROL_AGCENA, /* INDYCAM_CONTROL */
- INDYCAM_SHUTTER_60, /* INDYCAM_SHUTTER */
- INDYCAM_GAIN_DEFAULT, /* INDYCAM_GAIN */
- 0x00, /* INDYCAM_BRIGHTNESS (read-only) */
- INDYCAM_RED_BALANCE_DEFAULT, /* INDYCAM_RED_BALANCE */
- INDYCAM_BLUE_BALANCE_DEFAULT, /* INDYCAM_BLUE_BALANCE */
- INDYCAM_RED_SATURATION_DEFAULT, /* INDYCAM_RED_SATURATION */
- INDYCAM_BLUE_SATURATION_DEFAULT,/* INDYCAM_BLUE_SATURATION */
-};
-
-/* IndyCam register handling */
-
-static int indycam_read_reg(struct v4l2_subdev *sd, u8 reg, u8 *value)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret;
-
- if (reg == INDYCAM_REG_RESET) {
- dprintk("indycam_read_reg(): "
- "skipping write-only register %d\n", reg);
- *value = 0;
- return 0;
- }
-
- ret = i2c_smbus_read_byte_data(client, reg);
-
- if (ret < 0) {
- printk(KERN_ERR "IndyCam: indycam_read_reg(): read failed, "
- "register = 0x%02x\n", reg);
- return ret;
- }
-
- *value = (u8)ret;
-
- return 0;
-}
-
-static int indycam_write_reg(struct v4l2_subdev *sd, u8 reg, u8 value)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int err;
-
- if (reg == INDYCAM_REG_BRIGHTNESS || reg == INDYCAM_REG_VERSION) {
- dprintk("indycam_write_reg(): "
- "skipping read-only register %d\n", reg);
- return 0;
- }
-
- dprintk("Writing Reg %d = 0x%02x\n", reg, value);
- err = i2c_smbus_write_byte_data(client, reg, value);
-
- if (err) {
- printk(KERN_ERR "IndyCam: indycam_write_reg(): write failed, "
- "register = 0x%02x, value = 0x%02x\n", reg, value);
- }
- return err;
-}
-
-static int indycam_write_block(struct v4l2_subdev *sd, u8 reg,
- u8 length, u8 *data)
-{
- int i, err;
-
- for (i = 0; i < length; i++) {
- err = indycam_write_reg(sd, reg + i, data[i]);
- if (err)
- return err;
- }
-
- return 0;
-}
-
-/* Helper functions */
-
-#ifdef INDYCAM_DEBUG
-static void indycam_regdump_debug(struct v4l2_subdev *sd)
-{
- int i;
- u8 val;
-
- for (i = 0; i < 9; i++) {
- indycam_read_reg(sd, i, &val);
- dprintk("Reg %d = 0x%02x\n", i, val);
- }
-}
-#endif
-
-static int indycam_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct indycam *camera = to_indycam(sd);
- u8 reg;
- int ret = 0;
-
- switch (ctrl->id) {
- case V4L2_CID_AUTOGAIN:
- case V4L2_CID_AUTO_WHITE_BALANCE:
- ret = indycam_read_reg(sd, INDYCAM_REG_CONTROL, ®);
- if (ret)
- return -EIO;
- if (ctrl->id == V4L2_CID_AUTOGAIN)
- ctrl->value = (reg & INDYCAM_CONTROL_AGCENA)
- ? 1 : 0;
- else
- ctrl->value = (reg & INDYCAM_CONTROL_AWBCTL)
- ? 1 : 0;
- break;
- case V4L2_CID_EXPOSURE:
- ret = indycam_read_reg(sd, INDYCAM_REG_SHUTTER, ®);
- if (ret)
- return -EIO;
- ctrl->value = ((s32)reg == 0x00) ? 0xff : ((s32)reg - 1);
- break;
- case V4L2_CID_GAIN:
- ret = indycam_read_reg(sd, INDYCAM_REG_GAIN, ®);
- if (ret)
- return -EIO;
- ctrl->value = (s32)reg;
- break;
- case V4L2_CID_RED_BALANCE:
- ret = indycam_read_reg(sd, INDYCAM_REG_RED_BALANCE, ®);
- if (ret)
- return -EIO;
- ctrl->value = (s32)reg;
- break;
- case V4L2_CID_BLUE_BALANCE:
- ret = indycam_read_reg(sd, INDYCAM_REG_BLUE_BALANCE, ®);
- if (ret)
- return -EIO;
- ctrl->value = (s32)reg;
- break;
- case INDYCAM_CONTROL_RED_SATURATION:
- ret = indycam_read_reg(sd,
- INDYCAM_REG_RED_SATURATION, ®);
- if (ret)
- return -EIO;
- ctrl->value = (s32)reg;
- break;
- case INDYCAM_CONTROL_BLUE_SATURATION:
- ret = indycam_read_reg(sd,
- INDYCAM_REG_BLUE_SATURATION, ®);
- if (ret)
- return -EIO;
- ctrl->value = (s32)reg;
- break;
- case V4L2_CID_GAMMA:
- if (camera->version == CAMERA_VERSION_MOOSE) {
- ret = indycam_read_reg(sd,
- INDYCAM_REG_GAMMA, ®);
- if (ret)
- return -EIO;
- ctrl->value = (s32)reg;
- } else {
- ctrl->value = INDYCAM_GAMMA_DEFAULT;
- }
- break;
- default:
- ret = -EINVAL;
- }
-
- return ret;
-}
-
-static int indycam_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct indycam *camera = to_indycam(sd);
- u8 reg;
- int ret = 0;
-
- switch (ctrl->id) {
- case V4L2_CID_AUTOGAIN:
- case V4L2_CID_AUTO_WHITE_BALANCE:
- ret = indycam_read_reg(sd, INDYCAM_REG_CONTROL, ®);
- if (ret)
- break;
-
- if (ctrl->id == V4L2_CID_AUTOGAIN) {
- if (ctrl->value)
- reg |= INDYCAM_CONTROL_AGCENA;
- else
- reg &= ~INDYCAM_CONTROL_AGCENA;
- } else {
- if (ctrl->value)
- reg |= INDYCAM_CONTROL_AWBCTL;
- else
- reg &= ~INDYCAM_CONTROL_AWBCTL;
- }
-
- ret = indycam_write_reg(sd, INDYCAM_REG_CONTROL, reg);
- break;
- case V4L2_CID_EXPOSURE:
- reg = (ctrl->value == 0xff) ? 0x00 : (ctrl->value + 1);
- ret = indycam_write_reg(sd, INDYCAM_REG_SHUTTER, reg);
- break;
- case V4L2_CID_GAIN:
- ret = indycam_write_reg(sd, INDYCAM_REG_GAIN, ctrl->value);
- break;
- case V4L2_CID_RED_BALANCE:
- ret = indycam_write_reg(sd, INDYCAM_REG_RED_BALANCE,
- ctrl->value);
- break;
- case V4L2_CID_BLUE_BALANCE:
- ret = indycam_write_reg(sd, INDYCAM_REG_BLUE_BALANCE,
- ctrl->value);
- break;
- case INDYCAM_CONTROL_RED_SATURATION:
- ret = indycam_write_reg(sd, INDYCAM_REG_RED_SATURATION,
- ctrl->value);
- break;
- case INDYCAM_CONTROL_BLUE_SATURATION:
- ret = indycam_write_reg(sd, INDYCAM_REG_BLUE_SATURATION,
- ctrl->value);
- break;
- case V4L2_CID_GAMMA:
- if (camera->version == CAMERA_VERSION_MOOSE) {
- ret = indycam_write_reg(sd, INDYCAM_REG_GAMMA,
- ctrl->value);
- }
- break;
- default:
- ret = -EINVAL;
- }
-
- return ret;
-}
-
-/* I2C-interface */
-
-/* ----------------------------------------------------------------------- */
-
-static const struct v4l2_subdev_core_ops indycam_core_ops = {
- .g_ctrl = indycam_g_ctrl,
- .s_ctrl = indycam_s_ctrl,
-};
-
-static const struct v4l2_subdev_ops indycam_ops = {
- .core = &indycam_core_ops,
-};
-
-static int indycam_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- int err = 0;
- struct indycam *camera;
- struct v4l2_subdev *sd;
-
- v4l_info(client, "chip found @ 0x%x (%s)\n",
- client->addr << 1, client->adapter->name);
-
- camera = kzalloc(sizeof(struct indycam), GFP_KERNEL);
- if (!camera)
- return -ENOMEM;
-
- sd = &camera->sd;
- v4l2_i2c_subdev_init(sd, client, &indycam_ops);
-
- camera->version = i2c_smbus_read_byte_data(client,
- INDYCAM_REG_VERSION);
- if (camera->version != CAMERA_VERSION_INDY &&
- camera->version != CAMERA_VERSION_MOOSE) {
- kfree(camera);
- return -ENODEV;
- }
-
- printk(KERN_INFO "IndyCam v%d.%d detected\n",
- INDYCAM_VERSION_MAJOR(camera->version),
- INDYCAM_VERSION_MINOR(camera->version));
-
- indycam_regdump(sd);
-
- // initialize
- err = indycam_write_block(sd, 0, sizeof(initseq), (u8 *)&initseq);
- if (err) {
- printk(KERN_ERR "IndyCam initialization failed\n");
- kfree(camera);
- return -EIO;
- }
-
- indycam_regdump(sd);
-
- // white balance
- err = indycam_write_reg(sd, INDYCAM_REG_CONTROL,
- INDYCAM_CONTROL_AGCENA | INDYCAM_CONTROL_AWBCTL);
- if (err) {
- printk(KERN_ERR "IndyCam: White balancing camera failed\n");
- kfree(camera);
- return -EIO;
- }
-
- indycam_regdump(sd);
-
- printk(KERN_INFO "IndyCam initialized\n");
-
- return 0;
-}
-
-static int indycam_remove(struct i2c_client *client)
-{
- struct v4l2_subdev *sd = i2c_get_clientdata(client);
-
- v4l2_device_unregister_subdev(sd);
- kfree(to_indycam(sd));
- return 0;
-}
-
-static const struct i2c_device_id indycam_id[] = {
- { "indycam", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, indycam_id);
-
-static struct i2c_driver indycam_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "indycam",
- },
- .probe = indycam_probe,
- .remove = indycam_remove,
- .id_table = indycam_id,
-};
-
-module_i2c_driver(indycam_driver);
diff --git a/drivers/media/platform/indycam.h b/drivers/media/platform/indycam.h
deleted file mode 100644
index 881f21c..0000000
--- a/drivers/media/platform/indycam.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * indycam.h - Silicon Graphics IndyCam digital camera driver
- *
- * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
- * Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi>
- *
- * 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 _INDYCAM_H_
-#define _INDYCAM_H_
-
-/* I2C address for the Guinness Camera */
-#define INDYCAM_ADDR 0x56
-
-/* Camera version */
-#define CAMERA_VERSION_INDY 0x10 /* v1.0 */
-#define CAMERA_VERSION_MOOSE 0x12 /* v1.2 */
-#define INDYCAM_VERSION_MAJOR(x) (((x) & 0xf0) >> 4)
-#define INDYCAM_VERSION_MINOR(x) ((x) & 0x0f)
-
-/* Register bus addresses */
-#define INDYCAM_REG_CONTROL 0x00
-#define INDYCAM_REG_SHUTTER 0x01
-#define INDYCAM_REG_GAIN 0x02
-#define INDYCAM_REG_BRIGHTNESS 0x03 /* read-only */
-#define INDYCAM_REG_RED_BALANCE 0x04
-#define INDYCAM_REG_BLUE_BALANCE 0x05
-#define INDYCAM_REG_RED_SATURATION 0x06
-#define INDYCAM_REG_BLUE_SATURATION 0x07
-#define INDYCAM_REG_GAMMA 0x08
-#define INDYCAM_REG_VERSION 0x0e /* read-only */
-#define INDYCAM_REG_RESET 0x0f /* write-only */
-
-#define INDYCAM_REG_LED 0x46
-#define INDYCAM_REG_ORIENTATION 0x47
-#define INDYCAM_REG_BUTTON 0x48
-
-/* Field definitions of registers */
-#define INDYCAM_CONTROL_AGCENA (1<<0) /* automatic gain control */
-#define INDYCAM_CONTROL_AWBCTL (1<<1) /* automatic white balance */
- /* 2-3 are reserved */
-#define INDYCAM_CONTROL_EVNFLD (1<<4) /* read-only */
-
-#define INDYCAM_SHUTTER_10000 0x02 /* 1/10000 second */
-#define INDYCAM_SHUTTER_4000 0x04 /* 1/4000 second */
-#define INDYCAM_SHUTTER_2000 0x08 /* 1/2000 second */
-#define INDYCAM_SHUTTER_1000 0x10 /* 1/1000 second */
-#define INDYCAM_SHUTTER_500 0x20 /* 1/500 second */
-#define INDYCAM_SHUTTER_250 0x3f /* 1/250 second */
-#define INDYCAM_SHUTTER_125 0x7e /* 1/125 second */
-#define INDYCAM_SHUTTER_100 0x9e /* 1/100 second */
-#define INDYCAM_SHUTTER_60 0x00 /* 1/60 second */
-
-#define INDYCAM_LED_ACTIVE 0x10
-#define INDYCAM_LED_INACTIVE 0x30
-#define INDYCAM_ORIENTATION_BOTTOM_TO_TOP 0x40
-#define INDYCAM_BUTTON_RELEASED 0x10
-
-/* Values for controls */
-#define INDYCAM_SHUTTER_MIN 0x00
-#define INDYCAM_SHUTTER_MAX 0xff
-#define INDYCAM_GAIN_MIN 0x00
-#define INDYCAM_GAIN_MAX 0xff
-#define INDYCAM_RED_BALANCE_MIN 0x00
-#define INDYCAM_RED_BALANCE_MAX 0xff
-#define INDYCAM_BLUE_BALANCE_MIN 0x00
-#define INDYCAM_BLUE_BALANCE_MAX 0xff
-#define INDYCAM_RED_SATURATION_MIN 0x00
-#define INDYCAM_RED_SATURATION_MAX 0xff
-#define INDYCAM_BLUE_SATURATION_MIN 0x00
-#define INDYCAM_BLUE_SATURATION_MAX 0xff
-#define INDYCAM_GAMMA_MIN 0x00
-#define INDYCAM_GAMMA_MAX 0xff
-
-#define INDYCAM_AGC_DEFAULT 1
-#define INDYCAM_AWB_DEFAULT 0
-#define INDYCAM_SHUTTER_DEFAULT 0xff
-#define INDYCAM_GAIN_DEFAULT 0x80
-#define INDYCAM_RED_BALANCE_DEFAULT 0x18
-#define INDYCAM_BLUE_BALANCE_DEFAULT 0xa4
-#define INDYCAM_RED_SATURATION_DEFAULT 0x80
-#define INDYCAM_BLUE_SATURATION_DEFAULT 0xc0
-#define INDYCAM_GAMMA_DEFAULT 0x80
-
-/* Driver interface definitions */
-
-#define INDYCAM_CONTROL_RED_SATURATION (V4L2_CID_PRIVATE_BASE + 0)
-#define INDYCAM_CONTROL_BLUE_SATURATION (V4L2_CID_PRIVATE_BASE + 1)
-
-#endif
diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c
index d36c507..b70c1ae 100644
--- a/drivers/media/platform/m2m-deinterlace.c
+++ b/drivers/media/platform/m2m-deinterlace.c
@@ -1101,7 +1101,6 @@
.remove = deinterlace_remove,
.driver = {
.name = MEM2MEM_NAME,
- .owner = THIS_MODULE,
},
};
module_platform_driver(deinterlace_pdrv);
diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c
index 6895e12..137ffa2 100644
--- a/drivers/media/platform/marvell-ccic/mcam-core.c
+++ b/drivers/media/platform/marvell-ccic/mcam-core.c
@@ -106,61 +106,61 @@
__u32 pixelformat;
int bpp; /* Bytes per pixel */
bool planar;
- enum v4l2_mbus_pixelcode mbus_code;
+ u32 mbus_code;
} mcam_formats[] = {
{
.desc = "YUYV 4:2:2",
.pixelformat = V4L2_PIX_FMT_YUYV,
- .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
.bpp = 2,
.planar = false,
},
{
.desc = "UYVY 4:2:2",
.pixelformat = V4L2_PIX_FMT_UYVY,
- .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
.bpp = 2,
.planar = false,
},
{
.desc = "YUV 4:2:2 PLANAR",
.pixelformat = V4L2_PIX_FMT_YUV422P,
- .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
.bpp = 2,
.planar = true,
},
{
.desc = "YUV 4:2:0 PLANAR",
.pixelformat = V4L2_PIX_FMT_YUV420,
- .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
.bpp = 2,
.planar = true,
},
{
.desc = "YVU 4:2:0 PLANAR",
.pixelformat = V4L2_PIX_FMT_YVU420,
- .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
.bpp = 2,
.planar = true,
},
{
.desc = "RGB 444",
.pixelformat = V4L2_PIX_FMT_RGB444,
- .mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE,
+ .mbus_code = MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE,
.bpp = 2,
.planar = false,
},
{
.desc = "RGB 565",
.pixelformat = V4L2_PIX_FMT_RGB565,
- .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE,
+ .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE,
.bpp = 2,
.planar = false,
},
{
.desc = "Raw RGB Bayer",
.pixelformat = V4L2_PIX_FMT_SBGGR8,
- .mbus_code = V4L2_MBUS_FMT_SBGGR8_1X8,
+ .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
.bpp = 1,
.planar = false,
},
@@ -190,8 +190,7 @@
.sizeimage = VGA_WIDTH*VGA_HEIGHT*2,
};
-static const enum v4l2_mbus_pixelcode mcam_def_mbus_code =
- V4L2_MBUS_FMT_YUYV8_2X8;
+static const u32 mcam_def_mbus_code = MEDIA_BUS_FMT_YUYV8_2X8;
/*
@@ -1080,6 +1079,8 @@
*nbufs = minbufs;
if (cam->buffer_mode == B_DMA_contig)
alloc_ctxs[0] = cam->vb_alloc_ctx;
+ else if (cam->buffer_mode == B_DMA_sg)
+ alloc_ctxs[0] = cam->vb_alloc_ctx_sg;
return 0;
}
@@ -1101,26 +1102,6 @@
mcam_read_setup(cam);
}
-
-/*
- * vb2 uses these to release the mutex when waiting in dqbuf. I'm
- * not actually sure we need to do this (I'm not sure that vb2_dqbuf() needs
- * to be called with the mutex held), but better safe than sorry.
- */
-static void mcam_vb_wait_prepare(struct vb2_queue *vq)
-{
- struct mcam_camera *cam = vb2_get_drv_priv(vq);
-
- mutex_unlock(&cam->s_mutex);
-}
-
-static void mcam_vb_wait_finish(struct vb2_queue *vq)
-{
- struct mcam_camera *cam = vb2_get_drv_priv(vq);
-
- mutex_lock(&cam->s_mutex);
-}
-
/*
* These need to be called with the mutex held from vb2
*/
@@ -1190,8 +1171,8 @@
.buf_queue = mcam_vb_buf_queue,
.start_streaming = mcam_vb_start_streaming,
.stop_streaming = mcam_vb_stop_streaming,
- .wait_prepare = mcam_vb_wait_prepare,
- .wait_finish = mcam_vb_wait_finish,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
};
@@ -1219,17 +1200,12 @@
static int mcam_vb_sg_buf_prepare(struct vb2_buffer *vb)
{
struct mcam_vb_buffer *mvb = vb_to_mvb(vb);
- struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue);
struct sg_table *sg_table = vb2_dma_sg_plane_desc(vb, 0);
struct mcam_dma_desc *desc = mvb->dma_desc;
struct scatterlist *sg;
int i;
- mvb->dma_desc_nent = dma_map_sg(cam->dev, sg_table->sgl,
- sg_table->nents, DMA_FROM_DEVICE);
- if (mvb->dma_desc_nent <= 0)
- return -EIO; /* Not sure what's right here */
- for_each_sg(sg_table->sgl, sg, mvb->dma_desc_nent, i) {
+ for_each_sg(sg_table->sgl, sg, sg_table->nents, i) {
desc->dma_addr = sg_dma_address(sg);
desc->segment_len = sg_dma_len(sg);
desc++;
@@ -1237,16 +1213,6 @@
return 0;
}
-static void mcam_vb_sg_buf_finish(struct vb2_buffer *vb)
-{
- struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue);
- struct sg_table *sg_table = vb2_dma_sg_plane_desc(vb, 0);
-
- if (sg_table)
- dma_unmap_sg(cam->dev, sg_table->sgl,
- sg_table->nents, DMA_FROM_DEVICE);
-}
-
static void mcam_vb_sg_buf_cleanup(struct vb2_buffer *vb)
{
struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue);
@@ -1263,12 +1229,11 @@
.buf_init = mcam_vb_sg_buf_init,
.buf_prepare = mcam_vb_sg_buf_prepare,
.buf_queue = mcam_vb_buf_queue,
- .buf_finish = mcam_vb_sg_buf_finish,
.buf_cleanup = mcam_vb_sg_buf_cleanup,
.start_streaming = mcam_vb_start_streaming,
.stop_streaming = mcam_vb_stop_streaming,
- .wait_prepare = mcam_vb_wait_prepare,
- .wait_finish = mcam_vb_wait_finish,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
};
#endif /* MCAM_MODE_DMA_SG */
@@ -1280,6 +1245,7 @@
memset(vq, 0, sizeof(*vq));
vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
vq->drv_priv = cam;
+ vq->lock = &cam->s_mutex;
INIT_LIST_HEAD(&cam->buffers);
switch (cam->buffer_mode) {
case B_DMA_contig:
@@ -1287,10 +1253,12 @@
vq->ops = &mcam_vb2_ops;
vq->mem_ops = &vb2_dma_contig_memops;
vq->buf_struct_size = sizeof(struct mcam_vb_buffer);
- cam->vb_alloc_ctx = vb2_dma_contig_init_ctx(cam->dev);
vq->io_modes = VB2_MMAP | VB2_USERPTR;
cam->dma_setup = mcam_ctlr_dma_contig;
cam->frame_complete = mcam_dma_contig_done;
+ cam->vb_alloc_ctx = vb2_dma_contig_init_ctx(cam->dev);
+ if (IS_ERR(cam->vb_alloc_ctx))
+ return PTR_ERR(cam->vb_alloc_ctx);
#endif
break;
case B_DMA_sg:
@@ -1301,6 +1269,9 @@
vq->io_modes = VB2_MMAP | VB2_USERPTR;
cam->dma_setup = mcam_ctlr_dma_sg;
cam->frame_complete = mcam_dma_sg_done;
+ cam->vb_alloc_ctx_sg = vb2_dma_sg_init_ctx(cam->dev);
+ if (IS_ERR(cam->vb_alloc_ctx_sg))
+ return PTR_ERR(cam->vb_alloc_ctx_sg);
#endif
break;
case B_vmalloc:
@@ -1326,6 +1297,10 @@
if (cam->buffer_mode == B_DMA_contig)
vb2_dma_contig_cleanup_ctx(cam->vb_alloc_ctx);
#endif
+#ifdef MCAM_MODE_DMA_SG
+ if (cam->buffer_mode == B_DMA_sg)
+ vb2_dma_sg_cleanup_ctx(cam->vb_alloc_ctx_sg);
+#endif
}
@@ -1414,9 +1389,9 @@
{
strcpy(cap->driver, "marvell_ccic");
strcpy(cap->card, "marvell_ccic");
- cap->version = 1;
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h
index 4dcdb69..6ccfbb6 100644
--- a/drivers/media/platform/marvell-ccic/mcam-core.h
+++ b/drivers/media/platform/marvell-ccic/mcam-core.h
@@ -176,6 +176,7 @@
/* DMA buffers - DMA modes */
struct mcam_vb_buffer *vb_bufs[MAX_DMA_BUFS];
struct vb2_alloc_ctx *vb_alloc_ctx;
+ struct vb2_alloc_ctx *vb_alloc_ctx_sg;
/* Mode-specific ops, set at open time */
void (*dma_setup)(struct mcam_camera *cam);
@@ -183,7 +184,7 @@
/* Current operating parameters */
struct v4l2_pix_format pix_format;
- enum v4l2_mbus_pixelcode mbus_code;
+ u32 mbus_code;
/* Locks */
struct mutex s_mutex; /* Access to this structure */
diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c
index 054507f..0ed9b3a 100644
--- a/drivers/media/platform/marvell-ccic/mmp-driver.c
+++ b/drivers/media/platform/marvell-ccic/mmp-driver.c
@@ -512,7 +512,6 @@
#endif
.driver = {
.name = "mmp-camera",
- .owner = THIS_MODULE
}
};
diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c
index 4971ff2..87314b7 100644
--- a/drivers/media/platform/mx2_emmaprp.c
+++ b/drivers/media/platform/mx2_emmaprp.c
@@ -402,13 +402,8 @@
{
strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1);
strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1);
- /*
- * This is only a mem-to-mem video device. The capture and output
- * device capability flags are left only for backward compatibility
- * and are scheduled for removal.
- */
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT |
- V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
+ cap->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -1010,7 +1005,6 @@
.remove = emmaprp_remove,
.driver = {
.name = MEM2MEM_NAME,
- .owner = THIS_MODULE,
},
};
module_platform_driver(emmaprp_pdrv);
diff --git a/drivers/media/platform/omap/Kconfig b/drivers/media/platform/omap/Kconfig
index f0bc674..97c28fd 100644
--- a/drivers/media/platform/omap/Kconfig
+++ b/drivers/media/platform/omap/Kconfig
@@ -4,7 +4,8 @@
config VIDEO_OMAP2_VOUT
tristate "OMAP2/OMAP3 V4L2-Display driver"
depends on m
- depends on ARCH_OMAP2 || ARCH_OMAP3 || (COMPILE_TEST && HAS_MMU)
+ depends on MMU
+ depends on ARCH_OMAP2 || ARCH_OMAP3
select VIDEOBUF_GEN
select VIDEOBUF_DMA_CONTIG
select OMAP2_DSS if HAS_IOMEM && ARCH_OMAP2PLUS
diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c
index 64ab6fb..ba2d8f9 100644
--- a/drivers/media/platform/omap/omap_vout.c
+++ b/drivers/media/platform/omap/omap_vout.c
@@ -198,7 +198,7 @@
* omap_vout_uservirt_to_phys: This inline function is used to convert user
* space virtual address to physical address.
*/
-static u32 omap_vout_uservirt_to_phys(u32 virtp)
+static unsigned long omap_vout_uservirt_to_phys(unsigned long virtp)
{
unsigned long physp = 0;
struct vm_area_struct *vma;
@@ -418,10 +418,10 @@
}
v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev,
- "%s enable=%d addr=%x width=%d\n height=%d color_mode=%d\n"
+ "%s enable=%d addr=%pad width=%d\n height=%d color_mode=%d\n"
"rotation=%d mirror=%d posx=%d posy=%d out_width = %d \n"
"out_height=%d rotation_type=%d screen_width=%d\n",
- __func__, ovl->is_enabled(ovl), info.paddr, info.width, info.height,
+ __func__, ovl->is_enabled(ovl), &info.paddr, info.width, info.height,
info.color_mode, info.rotation, info.mirror, info.pos_x,
info.pos_y, info.out_width, info.out_height, info.rotation_type,
info.screen_width);
@@ -794,7 +794,7 @@
vout->queued_buf_addr[vb->i] = (u8 *)
omap_vout_uservirt_to_phys(vb->baddr);
} else {
- u32 addr, dma_addr;
+ unsigned long addr, dma_addr;
unsigned long size;
addr = (unsigned long) vout->buf_virt_addr[vb->i];
@@ -1054,8 +1054,9 @@
strlcpy(cap->driver, VOUT_NAME, sizeof(cap->driver));
strlcpy(cap->card, vout->vfd->name, sizeof(cap->card));
cap->bus_info[0] = '\0';
- cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT |
+ cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT |
V4L2_CAP_VIDEO_OUTPUT_OVERLAY;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c
index 72265e5..51c2129 100644
--- a/drivers/media/platform/omap3isp/isp.c
+++ b/drivers/media/platform/omap3isp/isp.c
@@ -2380,7 +2380,6 @@
.remove = isp_remove,
.id_table = omap3isp_id_table,
.driver = {
- .owner = THIS_MODULE,
.name = "omap3isp",
.pm = &omap3isp_pm_ops,
},
diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/omap3isp/ispccdc.c
index 81a9dc0..587489a 100644
--- a/drivers/media/platform/omap3isp/ispccdc.c
+++ b/drivers/media/platform/omap3isp/ispccdc.c
@@ -36,23 +36,23 @@
unsigned int pad, enum v4l2_subdev_format_whence which);
static const unsigned int ccdc_fmts[] = {
- V4L2_MBUS_FMT_Y8_1X8,
- V4L2_MBUS_FMT_Y10_1X10,
- V4L2_MBUS_FMT_Y12_1X12,
- V4L2_MBUS_FMT_SGRBG8_1X8,
- V4L2_MBUS_FMT_SRGGB8_1X8,
- V4L2_MBUS_FMT_SBGGR8_1X8,
- V4L2_MBUS_FMT_SGBRG8_1X8,
- V4L2_MBUS_FMT_SGRBG10_1X10,
- V4L2_MBUS_FMT_SRGGB10_1X10,
- V4L2_MBUS_FMT_SBGGR10_1X10,
- V4L2_MBUS_FMT_SGBRG10_1X10,
- V4L2_MBUS_FMT_SGRBG12_1X12,
- V4L2_MBUS_FMT_SRGGB12_1X12,
- V4L2_MBUS_FMT_SBGGR12_1X12,
- V4L2_MBUS_FMT_SGBRG12_1X12,
- V4L2_MBUS_FMT_YUYV8_2X8,
- V4L2_MBUS_FMT_UYVY8_2X8,
+ MEDIA_BUS_FMT_Y8_1X8,
+ MEDIA_BUS_FMT_Y10_1X10,
+ MEDIA_BUS_FMT_Y12_1X12,
+ MEDIA_BUS_FMT_SGRBG8_1X8,
+ MEDIA_BUS_FMT_SRGGB8_1X8,
+ MEDIA_BUS_FMT_SBGGR8_1X8,
+ MEDIA_BUS_FMT_SGBRG8_1X8,
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ MEDIA_BUS_FMT_SRGGB10_1X10,
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+ MEDIA_BUS_FMT_SGRBG12_1X12,
+ MEDIA_BUS_FMT_SRGGB12_1X12,
+ MEDIA_BUS_FMT_SBGGR12_1X12,
+ MEDIA_BUS_FMT_SGBRG12_1X12,
+ MEDIA_BUS_FMT_YUYV8_2X8,
+ MEDIA_BUS_FMT_UYVY8_2X8,
};
/*
@@ -266,10 +266,10 @@
__ccdc_get_format(ccdc, NULL, CCDC_PAD_SINK,
V4L2_SUBDEV_FORMAT_ACTIVE);
- if ((format->code != V4L2_MBUS_FMT_SGRBG10_1X10) &&
- (format->code != V4L2_MBUS_FMT_SRGGB10_1X10) &&
- (format->code != V4L2_MBUS_FMT_SBGGR10_1X10) &&
- (format->code != V4L2_MBUS_FMT_SGBRG10_1X10))
+ if ((format->code != MEDIA_BUS_FMT_SGRBG10_1X10) &&
+ (format->code != MEDIA_BUS_FMT_SRGGB10_1X10) &&
+ (format->code != MEDIA_BUS_FMT_SBGGR10_1X10) &&
+ (format->code != MEDIA_BUS_FMT_SGBRG10_1X10))
return -EINVAL;
if (enable)
@@ -971,8 +971,8 @@
format = &ccdc->formats[CCDC_PAD_SINK];
- if (format->code == V4L2_MBUS_FMT_YUYV8_2X8 ||
- format->code == V4L2_MBUS_FMT_UYVY8_2X8) {
+ if (format->code == MEDIA_BUS_FMT_YUYV8_2X8 ||
+ format->code == MEDIA_BUS_FMT_UYVY8_2X8) {
/* According to the OMAP3 TRM the input mode only affects SYNC
* mode, enabling BT.656 mode should take precedence. However,
* in practice setting the input mode to YCbCr data on 8 bits
@@ -1020,7 +1020,7 @@
/* The CCDC_CFG.Y8POS bit is used in YCbCr8 input mode only. The
* hardware seems to ignore it in all other input modes.
*/
- if (format->code == V4L2_MBUS_FMT_UYVY8_2X8)
+ if (format->code == MEDIA_BUS_FMT_UYVY8_2X8)
isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG,
ISPCCDC_CFG_Y8POS);
else
@@ -1168,9 +1168,9 @@
if (ccdc->bt656)
bridge = ISPCTRL_PAR_BRIDGE_DISABLE;
- else if (fmt_info->code == V4L2_MBUS_FMT_YUYV8_2X8)
+ else if (fmt_info->code == MEDIA_BUS_FMT_YUYV8_2X8)
bridge = ISPCTRL_PAR_BRIDGE_LENDIAN;
- else if (fmt_info->code == V4L2_MBUS_FMT_UYVY8_2X8)
+ else if (fmt_info->code == MEDIA_BUS_FMT_UYVY8_2X8)
bridge = ISPCTRL_PAR_BRIDGE_BENDIAN;
else
bridge = ISPCTRL_PAR_BRIDGE_DISABLE;
@@ -1199,16 +1199,16 @@
/* Mosaic filter */
switch (format->code) {
- case V4L2_MBUS_FMT_SRGGB10_1X10:
- case V4L2_MBUS_FMT_SRGGB12_1X12:
+ case MEDIA_BUS_FMT_SRGGB10_1X10:
+ case MEDIA_BUS_FMT_SRGGB12_1X12:
ccdc_pattern = ccdc_srggb_pattern;
break;
- case V4L2_MBUS_FMT_SBGGR10_1X10:
- case V4L2_MBUS_FMT_SBGGR12_1X12:
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ case MEDIA_BUS_FMT_SBGGR12_1X12:
ccdc_pattern = ccdc_sbggr_pattern;
break;
- case V4L2_MBUS_FMT_SGBRG10_1X10:
- case V4L2_MBUS_FMT_SGBRG12_1X12:
+ case MEDIA_BUS_FMT_SGBRG10_1X10:
+ case MEDIA_BUS_FMT_SGBRG12_1X12:
ccdc_pattern = ccdc_sgbrg_pattern;
break;
default:
@@ -1267,7 +1267,7 @@
/* The CCDC outputs data in UYVY order by default. Swap bytes to get
* YUYV.
*/
- if (format->code == V4L2_MBUS_FMT_YUYV8_1X16)
+ if (format->code == MEDIA_BUS_FMT_YUYV8_1X16)
isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG,
ISPCCDC_CFG_BSWD);
else
@@ -1967,7 +1967,7 @@
enum v4l2_subdev_format_whence which)
{
const struct isp_format_info *info;
- enum v4l2_mbus_pixelcode pixelcode;
+ u32 pixelcode;
unsigned int width = fmt->width;
unsigned int height = fmt->height;
struct v4l2_rect *crop;
@@ -1983,7 +1983,7 @@
/* If not found, use SGRBG10 as default */
if (i >= ARRAY_SIZE(ccdc_fmts))
- fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
/* Clamp the input size. */
fmt->width = clamp_t(u32, width, 32, 4096);
@@ -2007,19 +2007,19 @@
* configured to pack bytes in BT.656, hiding the inaccuracy.
* In all cases bytes can be swapped.
*/
- if (fmt->code == V4L2_MBUS_FMT_YUYV8_2X8 ||
- fmt->code == V4L2_MBUS_FMT_UYVY8_2X8) {
+ if (fmt->code == MEDIA_BUS_FMT_YUYV8_2X8 ||
+ fmt->code == MEDIA_BUS_FMT_UYVY8_2X8) {
/* Use the user requested format if YUV. */
- if (pixelcode == V4L2_MBUS_FMT_YUYV8_2X8 ||
- pixelcode == V4L2_MBUS_FMT_UYVY8_2X8 ||
- pixelcode == V4L2_MBUS_FMT_YUYV8_1X16 ||
- pixelcode == V4L2_MBUS_FMT_UYVY8_1X16)
+ if (pixelcode == MEDIA_BUS_FMT_YUYV8_2X8 ||
+ pixelcode == MEDIA_BUS_FMT_UYVY8_2X8 ||
+ pixelcode == MEDIA_BUS_FMT_YUYV8_1X16 ||
+ pixelcode == MEDIA_BUS_FMT_UYVY8_1X16)
fmt->code = pixelcode;
- if (fmt->code == V4L2_MBUS_FMT_YUYV8_2X8)
- fmt->code = V4L2_MBUS_FMT_YUYV8_1X16;
- else if (fmt->code == V4L2_MBUS_FMT_UYVY8_2X8)
- fmt->code = V4L2_MBUS_FMT_UYVY8_1X16;
+ if (fmt->code == MEDIA_BUS_FMT_YUYV8_2X8)
+ fmt->code = MEDIA_BUS_FMT_YUYV8_1X16;
+ else if (fmt->code == MEDIA_BUS_FMT_UYVY8_2X8)
+ fmt->code = MEDIA_BUS_FMT_UYVY8_1X16;
}
/* Hardcode the output size to the crop rectangle size. */
@@ -2047,8 +2047,8 @@
fmt->code = info->truncated;
/* YUV formats are not supported by the video port. */
- if (fmt->code == V4L2_MBUS_FMT_YUYV8_2X8 ||
- fmt->code == V4L2_MBUS_FMT_UYVY8_2X8)
+ if (fmt->code == MEDIA_BUS_FMT_YUYV8_2X8 ||
+ fmt->code == MEDIA_BUS_FMT_UYVY8_2X8)
fmt->code = 0;
/* The number of lines that can be clocked out from the video
@@ -2083,7 +2083,7 @@
* to keep the Bayer pattern.
*/
info = omap3isp_video_format_info(sink->code);
- if (info->flavor != V4L2_MBUS_FMT_Y8_1X8) {
+ if (info->flavor != MEDIA_BUS_FMT_Y8_1X8) {
crop->left &= ~1;
crop->top &= ~1;
}
@@ -2103,7 +2103,7 @@
sink->height - crop->top);
/* Odd width/height values don't make sense for Bayer formats. */
- if (info->flavor != V4L2_MBUS_FMT_Y8_1X8) {
+ if (info->flavor != MEDIA_BUS_FMT_Y8_1X8) {
crop->width &= ~1;
crop->height &= ~1;
}
@@ -2135,13 +2135,13 @@
format = __ccdc_get_format(ccdc, fh, code->pad,
V4L2_SUBDEV_FORMAT_TRY);
- if (format->code == V4L2_MBUS_FMT_YUYV8_2X8 ||
- format->code == V4L2_MBUS_FMT_UYVY8_2X8) {
+ if (format->code == MEDIA_BUS_FMT_YUYV8_2X8 ||
+ format->code == MEDIA_BUS_FMT_UYVY8_2X8) {
/* In YUV mode the CCDC can swap bytes. */
if (code->index == 0)
- code->code = V4L2_MBUS_FMT_YUYV8_1X16;
+ code->code = MEDIA_BUS_FMT_YUYV8_1X16;
else if (code->index == 1)
- code->code = V4L2_MBUS_FMT_UYVY8_1X16;
+ code->code = MEDIA_BUS_FMT_UYVY8_1X16;
else
return -EINVAL;
} else {
@@ -2383,9 +2383,7 @@
* return true if the combination is possible
* return false otherwise
*/
-static bool ccdc_is_shiftable(enum v4l2_mbus_pixelcode in,
- enum v4l2_mbus_pixelcode out,
- unsigned int additional_shift)
+static bool ccdc_is_shiftable(u32 in, u32 out, unsigned int additional_shift)
{
const struct isp_format_info *in_info, *out_info;
@@ -2452,7 +2450,7 @@
memset(&format, 0, sizeof(format));
format.pad = CCDC_PAD_SINK;
format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
- format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10;
format.format.width = 4096;
format.format.height = 4096;
ccdc_set_format(sd, fh, &format);
diff --git a/drivers/media/platform/omap3isp/ispccp2.c b/drivers/media/platform/omap3isp/ispccp2.c
index 9cb49b3..f4aedb3 100644
--- a/drivers/media/platform/omap3isp/ispccp2.c
+++ b/drivers/media/platform/omap3isp/ispccp2.c
@@ -289,10 +289,10 @@
u32 val, format;
switch (config->format) {
- case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8:
+ case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8:
format = ISPCCP2_LCx_CTRL_FORMAT_RAW8_DPCM10_VP;
break;
- case V4L2_MBUS_FMT_SGRBG10_1X10:
+ case MEDIA_BUS_FMT_SGRBG10_1X10:
default:
format = ISPCCP2_LCx_CTRL_FORMAT_RAW10_VP; /* RAW10+VP */
break;
@@ -438,7 +438,7 @@
u32 val, hwords;
if (sink_pixcode != source_pixcode &&
- sink_pixcode == V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8)
+ sink_pixcode == MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8)
dpcm_decompress = 1;
ccp2_pwr_cfg(ccp2);
@@ -604,8 +604,8 @@
*/
static const unsigned int ccp2_fmts[] = {
- V4L2_MBUS_FMT_SGRBG10_1X10,
- V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8,
};
/*
@@ -643,8 +643,8 @@
switch (pad) {
case CCP2_PAD_SINK:
- if (fmt->code != V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8)
- fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ if (fmt->code != MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8)
+ fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
if (ccp2->input == CCP2_INPUT_SENSOR) {
fmt->width = clamp_t(u32, fmt->width,
@@ -671,7 +671,7 @@
*/
format = __ccp2_get_format(ccp2, fh, CCP2_PAD_SINK, which);
memcpy(fmt, format, sizeof(*fmt));
- fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
break;
}
@@ -808,7 +808,7 @@
memset(&format, 0, sizeof(format));
format.pad = CCP2_PAD_SINK;
format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
- format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10;
format.format.width = 4096;
format.format.height = 4096;
ccp2_set_format(sd, fh, &format);
diff --git a/drivers/media/platform/omap3isp/ispcsi2.c b/drivers/media/platform/omap3isp/ispcsi2.c
index 6530b25..09c686d 100644
--- a/drivers/media/platform/omap3isp/ispcsi2.c
+++ b/drivers/media/platform/omap3isp/ispcsi2.c
@@ -78,15 +78,15 @@
}
static const unsigned int csi2_input_fmts[] = {
- V4L2_MBUS_FMT_SGRBG10_1X10,
- V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
- V4L2_MBUS_FMT_SRGGB10_1X10,
- V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8,
- V4L2_MBUS_FMT_SBGGR10_1X10,
- V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8,
- V4L2_MBUS_FMT_SGBRG10_1X10,
- V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8,
- V4L2_MBUS_FMT_YUYV8_2X8,
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8,
+ MEDIA_BUS_FMT_SRGGB10_1X10,
+ MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8,
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8,
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+ MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8,
+ MEDIA_BUS_FMT_YUYV8_2X8,
};
/* To set the format on the CSI2 requires a mapping function that takes
@@ -171,19 +171,19 @@
int fmtidx, destidx, is_3630;
switch (fmt->code) {
- case V4L2_MBUS_FMT_SGRBG10_1X10:
- case V4L2_MBUS_FMT_SRGGB10_1X10:
- case V4L2_MBUS_FMT_SBGGR10_1X10:
- case V4L2_MBUS_FMT_SGBRG10_1X10:
+ case MEDIA_BUS_FMT_SGRBG10_1X10:
+ case MEDIA_BUS_FMT_SRGGB10_1X10:
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ case MEDIA_BUS_FMT_SGBRG10_1X10:
fmtidx = 0;
break;
- case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8:
- case V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8:
- case V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8:
- case V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8:
+ case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8:
+ case MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8:
+ case MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8:
+ case MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8:
fmtidx = 1;
break;
- case V4L2_MBUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_YUYV8_2X8:
fmtidx = 2;
break;
default:
@@ -843,7 +843,7 @@
unsigned int pad, struct v4l2_mbus_framefmt *fmt,
enum v4l2_subdev_format_whence which)
{
- enum v4l2_mbus_pixelcode pixelcode;
+ u32 pixelcode;
struct v4l2_mbus_framefmt *format;
const struct isp_format_info *info;
unsigned int i;
@@ -858,7 +858,7 @@
/* If not found, use SGRBG10 as default */
if (i >= ARRAY_SIZE(csi2_input_fmts))
- fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
fmt->width = clamp_t(u32, fmt->width, 1, 8191);
fmt->height = clamp_t(u32, fmt->height, 1, 8191);
@@ -1029,7 +1029,7 @@
memset(&format, 0, sizeof(format));
format.pad = CSI2_PAD_SINK;
format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
- format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10;
format.format.width = 4096;
format.format.height = 4096;
csi2_set_format(sd, fh, &format);
diff --git a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/omap3isp/isppreview.c
index 605f57e..dd9eed4 100644
--- a/drivers/media/platform/omap3isp/isppreview.c
+++ b/drivers/media/platform/omap3isp/isppreview.c
@@ -964,18 +964,16 @@
* @prev: pointer to previewer private structure
* @pixelcode: pixel code
*/
-static void
-preview_config_ycpos(struct isp_prev_device *prev,
- enum v4l2_mbus_pixelcode pixelcode)
+static void preview_config_ycpos(struct isp_prev_device *prev, u32 pixelcode)
{
struct isp_device *isp = to_isp_device(prev);
enum preview_ycpos_mode mode;
switch (pixelcode) {
- case V4L2_MBUS_FMT_YUYV8_1X16:
+ case MEDIA_BUS_FMT_YUYV8_1X16:
mode = YCPOS_CrYCbY;
break;
- case V4L2_MBUS_FMT_UYVY8_1X16:
+ case MEDIA_BUS_FMT_UYVY8_1X16:
mode = YCPOS_YCrYCb;
break;
default:
@@ -1028,16 +1026,16 @@
ISPPRV_PCR_WIDTH);
switch (info->flavor) {
- case V4L2_MBUS_FMT_SGRBG8_1X8:
+ case MEDIA_BUS_FMT_SGRBG8_1X8:
prev->params.cfa_order = 0;
break;
- case V4L2_MBUS_FMT_SRGGB8_1X8:
+ case MEDIA_BUS_FMT_SRGGB8_1X8:
prev->params.cfa_order = 1;
break;
- case V4L2_MBUS_FMT_SBGGR8_1X8:
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
prev->params.cfa_order = 2;
break;
- case V4L2_MBUS_FMT_SGBRG8_1X8:
+ case MEDIA_BUS_FMT_SGBRG8_1X8:
prev->params.cfa_order = 3;
break;
default:
@@ -1078,8 +1076,8 @@
unsigned int elv = prev->crop.top + prev->crop.height - 1;
u32 features;
- if (format->code != V4L2_MBUS_FMT_Y8_1X8 &&
- format->code != V4L2_MBUS_FMT_Y10_1X10) {
+ if (format->code != MEDIA_BUS_FMT_Y8_1X8 &&
+ format->code != MEDIA_BUS_FMT_Y10_1X10) {
sph -= 2;
eph += 2;
slv -= 2;
@@ -1709,21 +1707,21 @@
/* previewer format descriptions */
static const unsigned int preview_input_fmts[] = {
- V4L2_MBUS_FMT_Y8_1X8,
- V4L2_MBUS_FMT_SGRBG8_1X8,
- V4L2_MBUS_FMT_SRGGB8_1X8,
- V4L2_MBUS_FMT_SBGGR8_1X8,
- V4L2_MBUS_FMT_SGBRG8_1X8,
- V4L2_MBUS_FMT_Y10_1X10,
- V4L2_MBUS_FMT_SGRBG10_1X10,
- V4L2_MBUS_FMT_SRGGB10_1X10,
- V4L2_MBUS_FMT_SBGGR10_1X10,
- V4L2_MBUS_FMT_SGBRG10_1X10,
+ MEDIA_BUS_FMT_Y8_1X8,
+ MEDIA_BUS_FMT_SGRBG8_1X8,
+ MEDIA_BUS_FMT_SRGGB8_1X8,
+ MEDIA_BUS_FMT_SBGGR8_1X8,
+ MEDIA_BUS_FMT_SGBRG8_1X8,
+ MEDIA_BUS_FMT_Y10_1X10,
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ MEDIA_BUS_FMT_SRGGB10_1X10,
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SGBRG10_1X10,
};
static const unsigned int preview_output_fmts[] = {
- V4L2_MBUS_FMT_UYVY8_1X16,
- V4L2_MBUS_FMT_YUYV8_1X16,
+ MEDIA_BUS_FMT_UYVY8_1X16,
+ MEDIA_BUS_FMT_YUYV8_1X16,
};
/*
@@ -1742,7 +1740,7 @@
struct v4l2_mbus_framefmt *fmt,
enum v4l2_subdev_format_whence which)
{
- enum v4l2_mbus_pixelcode pixelcode;
+ u32 pixelcode;
struct v4l2_rect *crop;
unsigned int i;
@@ -1774,7 +1772,7 @@
/* If not found, use SGRBG10 as default */
if (i >= ARRAY_SIZE(preview_input_fmts))
- fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
break;
case PREV_PAD_SOURCE:
@@ -1782,13 +1780,13 @@
*fmt = *__preview_get_format(prev, fh, PREV_PAD_SINK, which);
switch (pixelcode) {
- case V4L2_MBUS_FMT_YUYV8_1X16:
- case V4L2_MBUS_FMT_UYVY8_1X16:
+ case MEDIA_BUS_FMT_YUYV8_1X16:
+ case MEDIA_BUS_FMT_UYVY8_1X16:
fmt->code = pixelcode;
break;
default:
- fmt->code = V4L2_MBUS_FMT_YUYV8_1X16;
+ fmt->code = MEDIA_BUS_FMT_YUYV8_1X16;
break;
}
@@ -1843,8 +1841,8 @@
* and no columns in other modes. Increase the margins based on the sink
* format.
*/
- if (sink->code != V4L2_MBUS_FMT_Y8_1X8 &&
- sink->code != V4L2_MBUS_FMT_Y10_1X10) {
+ if (sink->code != MEDIA_BUS_FMT_Y8_1X8 &&
+ sink->code != MEDIA_BUS_FMT_Y10_1X10) {
left += 2;
right -= 2;
top += 2;
@@ -2092,7 +2090,7 @@
memset(&format, 0, sizeof(format));
format.pad = PREV_PAD_SINK;
format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
- format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10;
format.format.width = 4096;
format.format.height = 4096;
preview_set_format(sd, fh, &format);
diff --git a/drivers/media/platform/omap3isp/ispresizer.c b/drivers/media/platform/omap3isp/ispresizer.c
index 05d1ace..2b9bc48 100644
--- a/drivers/media/platform/omap3isp/ispresizer.c
+++ b/drivers/media/platform/omap3isp/ispresizer.c
@@ -198,17 +198,16 @@
* @res: Device context.
* @pixelcode: pixel code.
*/
-static void resizer_set_ycpos(struct isp_res_device *res,
- enum v4l2_mbus_pixelcode pixelcode)
+static void resizer_set_ycpos(struct isp_res_device *res, u32 pixelcode)
{
struct isp_device *isp = to_isp_device(res);
switch (pixelcode) {
- case V4L2_MBUS_FMT_YUYV8_1X16:
+ case MEDIA_BUS_FMT_YUYV8_1X16:
isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
ISPRSZ_CNT_YCPOS);
break;
- case V4L2_MBUS_FMT_UYVY8_1X16:
+ case MEDIA_BUS_FMT_UYVY8_1X16:
isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
ISPRSZ_CNT_YCPOS);
break;
@@ -1348,8 +1347,8 @@
/* resizer pixel formats */
static const unsigned int resizer_formats[] = {
- V4L2_MBUS_FMT_UYVY8_1X16,
- V4L2_MBUS_FMT_YUYV8_1X16,
+ MEDIA_BUS_FMT_UYVY8_1X16,
+ MEDIA_BUS_FMT_YUYV8_1X16,
};
static unsigned int resizer_max_in_width(struct isp_res_device *res)
@@ -1385,9 +1384,9 @@
switch (pad) {
case RESZ_PAD_SINK:
- if (fmt->code != V4L2_MBUS_FMT_YUYV8_1X16 &&
- fmt->code != V4L2_MBUS_FMT_UYVY8_1X16)
- fmt->code = V4L2_MBUS_FMT_YUYV8_1X16;
+ if (fmt->code != MEDIA_BUS_FMT_YUYV8_1X16 &&
+ fmt->code != MEDIA_BUS_FMT_UYVY8_1X16)
+ fmt->code = MEDIA_BUS_FMT_YUYV8_1X16;
fmt->width = clamp_t(u32, fmt->width, MIN_IN_WIDTH,
resizer_max_in_width(res));
@@ -1571,7 +1570,7 @@
memset(&format, 0, sizeof(format));
format.pad = RESZ_PAD_SINK;
format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
- format.format.code = V4L2_MBUS_FMT_YUYV8_1X16;
+ format.format.code = MEDIA_BUS_FMT_YUYV8_1X16;
format.format.width = 4096;
format.format.height = 4096;
resizer_set_format(sd, fh, &format);
diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c
index bc38c88..b463fe1 100644
--- a/drivers/media/platform/omap3isp/ispvideo.c
+++ b/drivers/media/platform/omap3isp/ispvideo.c
@@ -39,74 +39,74 @@
* corresponding in-memory formats to the table below!!!
*/
static struct isp_format_info formats[] = {
- { V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8,
- V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8,
+ { MEDIA_BUS_FMT_Y8_1X8, MEDIA_BUS_FMT_Y8_1X8,
+ MEDIA_BUS_FMT_Y8_1X8, MEDIA_BUS_FMT_Y8_1X8,
V4L2_PIX_FMT_GREY, 8, 1, },
- { V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y10_1X10,
- V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y8_1X8,
+ { MEDIA_BUS_FMT_Y10_1X10, MEDIA_BUS_FMT_Y10_1X10,
+ MEDIA_BUS_FMT_Y10_1X10, MEDIA_BUS_FMT_Y8_1X8,
V4L2_PIX_FMT_Y10, 10, 2, },
- { V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y10_1X10,
- V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y8_1X8,
+ { MEDIA_BUS_FMT_Y12_1X12, MEDIA_BUS_FMT_Y10_1X10,
+ MEDIA_BUS_FMT_Y12_1X12, MEDIA_BUS_FMT_Y8_1X8,
V4L2_PIX_FMT_Y12, 12, 2, },
- { V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8,
- V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8,
+ { MEDIA_BUS_FMT_SBGGR8_1X8, MEDIA_BUS_FMT_SBGGR8_1X8,
+ MEDIA_BUS_FMT_SBGGR8_1X8, MEDIA_BUS_FMT_SBGGR8_1X8,
V4L2_PIX_FMT_SBGGR8, 8, 1, },
- { V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8,
- V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8,
+ { MEDIA_BUS_FMT_SGBRG8_1X8, MEDIA_BUS_FMT_SGBRG8_1X8,
+ MEDIA_BUS_FMT_SGBRG8_1X8, MEDIA_BUS_FMT_SGBRG8_1X8,
V4L2_PIX_FMT_SGBRG8, 8, 1, },
- { V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8,
- V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8,
+ { MEDIA_BUS_FMT_SGRBG8_1X8, MEDIA_BUS_FMT_SGRBG8_1X8,
+ MEDIA_BUS_FMT_SGRBG8_1X8, MEDIA_BUS_FMT_SGRBG8_1X8,
V4L2_PIX_FMT_SGRBG8, 8, 1, },
- { V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8,
- V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8,
+ { MEDIA_BUS_FMT_SRGGB8_1X8, MEDIA_BUS_FMT_SRGGB8_1X8,
+ MEDIA_BUS_FMT_SRGGB8_1X8, MEDIA_BUS_FMT_SRGGB8_1X8,
V4L2_PIX_FMT_SRGGB8, 8, 1, },
- { V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8,
- V4L2_MBUS_FMT_SBGGR10_1X10, 0,
+ { MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8, MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8,
+ MEDIA_BUS_FMT_SBGGR10_1X10, 0,
V4L2_PIX_FMT_SBGGR10DPCM8, 8, 1, },
- { V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8,
- V4L2_MBUS_FMT_SGBRG10_1X10, 0,
+ { MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8, MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8,
+ MEDIA_BUS_FMT_SGBRG10_1X10, 0,
V4L2_PIX_FMT_SGBRG10DPCM8, 8, 1, },
- { V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
- V4L2_MBUS_FMT_SGRBG10_1X10, 0,
+ { MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8,
+ MEDIA_BUS_FMT_SGRBG10_1X10, 0,
V4L2_PIX_FMT_SGRBG10DPCM8, 8, 1, },
- { V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8,
- V4L2_MBUS_FMT_SRGGB10_1X10, 0,
+ { MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8, MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8,
+ MEDIA_BUS_FMT_SRGGB10_1X10, 0,
V4L2_PIX_FMT_SRGGB10DPCM8, 8, 1, },
- { V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR10_1X10,
- V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR8_1X8,
+ { MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SBGGR8_1X8,
V4L2_PIX_FMT_SBGGR10, 10, 2, },
- { V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG10_1X10,
- V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG8_1X8,
+ { MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10,
+ MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SGBRG8_1X8,
V4L2_PIX_FMT_SGBRG10, 10, 2, },
- { V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG10_1X10,
- V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG8_1X8,
+ { MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SGRBG10_1X10,
+ MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SGRBG8_1X8,
V4L2_PIX_FMT_SGRBG10, 10, 2, },
- { V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB10_1X10,
- V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB8_1X8,
+ { MEDIA_BUS_FMT_SRGGB10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10,
+ MEDIA_BUS_FMT_SRGGB10_1X10, MEDIA_BUS_FMT_SRGGB8_1X8,
V4L2_PIX_FMT_SRGGB10, 10, 2, },
- { V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR10_1X10,
- V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR8_1X8,
+ { MEDIA_BUS_FMT_SBGGR12_1X12, MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SBGGR12_1X12, MEDIA_BUS_FMT_SBGGR8_1X8,
V4L2_PIX_FMT_SBGGR12, 12, 2, },
- { V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG10_1X10,
- V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG8_1X8,
+ { MEDIA_BUS_FMT_SGBRG12_1X12, MEDIA_BUS_FMT_SGBRG10_1X10,
+ MEDIA_BUS_FMT_SGBRG12_1X12, MEDIA_BUS_FMT_SGBRG8_1X8,
V4L2_PIX_FMT_SGBRG12, 12, 2, },
- { V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG10_1X10,
- V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG8_1X8,
+ { MEDIA_BUS_FMT_SGRBG12_1X12, MEDIA_BUS_FMT_SGRBG10_1X10,
+ MEDIA_BUS_FMT_SGRBG12_1X12, MEDIA_BUS_FMT_SGRBG8_1X8,
V4L2_PIX_FMT_SGRBG12, 12, 2, },
- { V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB10_1X10,
- V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB8_1X8,
+ { MEDIA_BUS_FMT_SRGGB12_1X12, MEDIA_BUS_FMT_SRGGB10_1X10,
+ MEDIA_BUS_FMT_SRGGB12_1X12, MEDIA_BUS_FMT_SRGGB8_1X8,
V4L2_PIX_FMT_SRGGB12, 12, 2, },
- { V4L2_MBUS_FMT_UYVY8_1X16, V4L2_MBUS_FMT_UYVY8_1X16,
- V4L2_MBUS_FMT_UYVY8_1X16, 0,
+ { MEDIA_BUS_FMT_UYVY8_1X16, MEDIA_BUS_FMT_UYVY8_1X16,
+ MEDIA_BUS_FMT_UYVY8_1X16, 0,
V4L2_PIX_FMT_UYVY, 16, 2, },
- { V4L2_MBUS_FMT_YUYV8_1X16, V4L2_MBUS_FMT_YUYV8_1X16,
- V4L2_MBUS_FMT_YUYV8_1X16, 0,
+ { MEDIA_BUS_FMT_YUYV8_1X16, MEDIA_BUS_FMT_YUYV8_1X16,
+ MEDIA_BUS_FMT_YUYV8_1X16, 0,
V4L2_PIX_FMT_YUYV, 16, 2, },
- { V4L2_MBUS_FMT_UYVY8_2X8, V4L2_MBUS_FMT_UYVY8_2X8,
- V4L2_MBUS_FMT_UYVY8_2X8, 0,
+ { MEDIA_BUS_FMT_UYVY8_2X8, MEDIA_BUS_FMT_UYVY8_2X8,
+ MEDIA_BUS_FMT_UYVY8_2X8, 0,
V4L2_PIX_FMT_UYVY, 8, 2, },
- { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_MBUS_FMT_YUYV8_2X8,
- V4L2_MBUS_FMT_YUYV8_2X8, 0,
+ { MEDIA_BUS_FMT_YUYV8_2X8, MEDIA_BUS_FMT_YUYV8_2X8,
+ MEDIA_BUS_FMT_YUYV8_2X8, 0,
V4L2_PIX_FMT_YUYV, 8, 2, },
/* Empty entry to catch the unsupported pixel code (0) used by the CCDC
* module and avoid NULL pointer dereferences.
@@ -114,8 +114,7 @@
{ 0, }
};
-const struct isp_format_info *
-omap3isp_video_format_info(enum v4l2_mbus_pixelcode code)
+const struct isp_format_info *omap3isp_video_format_info(u32 code)
{
unsigned int i;
diff --git a/drivers/media/platform/omap3isp/ispvideo.h b/drivers/media/platform/omap3isp/ispvideo.h
index 0b7efed..4071dd7 100644
--- a/drivers/media/platform/omap3isp/ispvideo.h
+++ b/drivers/media/platform/omap3isp/ispvideo.h
@@ -44,10 +44,10 @@
* @bpp: Bytes per pixel (when stored in memory)
*/
struct isp_format_info {
- enum v4l2_mbus_pixelcode code;
- enum v4l2_mbus_pixelcode truncated;
- enum v4l2_mbus_pixelcode uncompressed;
- enum v4l2_mbus_pixelcode flavor;
+ u32 code;
+ u32 truncated;
+ u32 uncompressed;
+ u32 flavor;
u32 pixelformat;
unsigned int width;
unsigned int bpp;
@@ -206,6 +206,6 @@
struct media_pad *omap3isp_video_remote_pad(struct isp_video *video);
const struct isp_format_info *
-omap3isp_video_format_info(enum v4l2_mbus_pixelcode code);
+omap3isp_video_format_info(u32 code);
#endif /* OMAP3_ISP_VIDEO_H */
diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c
index 4f81b4c..aa40c82 100644
--- a/drivers/media/platform/s3c-camif/camif-capture.c
+++ b/drivers/media/platform/s3c-camif/camif-capture.c
@@ -1218,11 +1218,11 @@
}
/* Media bus pixel formats supported at the camif input */
-static const enum v4l2_mbus_pixelcode camif_mbus_formats[] = {
- V4L2_MBUS_FMT_YUYV8_2X8,
- V4L2_MBUS_FMT_YVYU8_2X8,
- V4L2_MBUS_FMT_UYVY8_2X8,
- V4L2_MBUS_FMT_VYUY8_2X8,
+static const u32 camif_mbus_formats[] = {
+ MEDIA_BUS_FMT_YUYV8_2X8,
+ MEDIA_BUS_FMT_YVYU8_2X8,
+ MEDIA_BUS_FMT_UYVY8_2X8,
+ MEDIA_BUS_FMT_VYUY8_2X8,
};
/*
diff --git a/drivers/media/platform/s3c-camif/camif-core.c b/drivers/media/platform/s3c-camif/camif-core.c
index b385747..2d5bd3a 100644
--- a/drivers/media/platform/s3c-camif/camif-core.c
+++ b/drivers/media/platform/s3c-camif/camif-core.c
@@ -256,8 +256,7 @@
v4l2_device_unregister_subdev(sd);
camif->sensor.sd = NULL;
i2c_unregister_device(client);
- if (adapter)
- i2c_put_adapter(adapter);
+ i2c_put_adapter(adapter);
}
static int camif_create_media_links(struct camif_dev *camif)
@@ -652,7 +651,6 @@
.id_table = s3c_camif_driver_ids,
.driver = {
.name = S3C_CAMIF_DRIVER_NAME,
- .owner = THIS_MODULE,
.pm = &s3c_camif_pm_ops,
}
};
diff --git a/drivers/media/platform/s3c-camif/camif-regs.c b/drivers/media/platform/s3c-camif/camif-regs.c
index 6e0c998..812fb3a 100644
--- a/drivers/media/platform/s3c-camif/camif-regs.c
+++ b/drivers/media/platform/s3c-camif/camif-regs.c
@@ -96,10 +96,10 @@
}
static const u32 src_pixfmt_map[8][2] = {
- { V4L2_MBUS_FMT_YUYV8_2X8, CISRCFMT_ORDER422_YCBYCR },
- { V4L2_MBUS_FMT_YVYU8_2X8, CISRCFMT_ORDER422_YCRYCB },
- { V4L2_MBUS_FMT_UYVY8_2X8, CISRCFMT_ORDER422_CBYCRY },
- { V4L2_MBUS_FMT_VYUY8_2X8, CISRCFMT_ORDER422_CRYCBY },
+ { MEDIA_BUS_FMT_YUYV8_2X8, CISRCFMT_ORDER422_YCBYCR },
+ { MEDIA_BUS_FMT_YVYU8_2X8, CISRCFMT_ORDER422_YCRYCB },
+ { MEDIA_BUS_FMT_UYVY8_2X8, CISRCFMT_ORDER422_CBYCRY },
+ { MEDIA_BUS_FMT_VYUY8_2X8, CISRCFMT_ORDER422_CRYCBY },
};
/* Set camera input pixel format and resolution */
diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c
index d79e214..47ba8fb 100644
--- a/drivers/media/platform/s5p-g2d/g2d.c
+++ b/drivers/media/platform/s5p-g2d/g2d.c
@@ -297,14 +297,8 @@
strncpy(cap->driver, G2D_NAME, sizeof(cap->driver) - 1);
strncpy(cap->card, G2D_NAME, sizeof(cap->card) - 1);
cap->bus_info[0] = 0;
- cap->version = KERNEL_VERSION(1, 0, 0);
- /*
- * This is only a mem-to-mem video device. The capture and output
- * device capability flags are left only for backward compatibility
- * and are scheduled for removal.
- */
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT |
- V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
+ cap->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -812,7 +806,6 @@
.id_table = g2d_driver_ids,
.driver = {
.name = G2D_NAME,
- .owner = THIS_MODULE,
.of_match_table = exynos_g2d_match,
},
};
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c
index e525a7c..12f7452 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-core.c
+++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c
@@ -893,7 +893,7 @@
unsigned long buffer, unsigned long size,
struct s5p_jpeg_ctx *ctx)
{
- int c, components, notfound;
+ int c, components = 0, notfound;
unsigned int height, width, word, subsampling = 0;
long length;
struct s5p_jpeg_buffer jpeg_buffer;
@@ -1001,13 +1001,8 @@
sizeof(cap->card));
}
cap->bus_info[0] = 0;
- /*
- * This is only a mem-to-mem video device. The capture and output
- * device capability flags are left only for backward compatibility
- * and are scheduled for removal.
- */
- cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M |
- V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT;
+ cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -2632,6 +2627,7 @@
return 0;
}
+#ifdef CONFIG_PM
static int s5p_jpeg_runtime_suspend(struct device *dev)
{
struct s5p_jpeg *jpeg = dev_get_drvdata(dev);
@@ -2681,7 +2677,9 @@
return 0;
}
+#endif /* CONFIG_PM */
+#ifdef CONFIG_PM_SLEEP
static int s5p_jpeg_suspend(struct device *dev)
{
if (pm_runtime_suspended(dev))
@@ -2697,6 +2695,7 @@
return s5p_jpeg_runtime_resume(dev);
}
+#endif
static const struct dev_pm_ops s5p_jpeg_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(s5p_jpeg_suspend, s5p_jpeg_resume)
@@ -2764,7 +2763,6 @@
.remove = s5p_jpeg_remove,
.driver = {
.of_match_table = of_match_ptr(samsung_jpeg_match),
- .owner = THIS_MODULE,
.name = S5P_JPEG_M2M_NAME,
.pm = &s5p_jpeg_pm_ops,
},
diff --git a/drivers/media/platform/s5p-mfc/regs-mfc-v6.h b/drivers/media/platform/s5p-mfc/regs-mfc-v6.h
index 51cb2dd..83e01f3 100644
--- a/drivers/media/platform/s5p-mfc/regs-mfc-v6.h
+++ b/drivers/media/platform/s5p-mfc/regs-mfc-v6.h
@@ -71,6 +71,7 @@
#define S5P_FIMV_R2H_CMD_ENC_BUFFER_FUL_RET_V6 16
#define S5P_FIMV_R2H_CMD_ERR_RET_V6 32
+#define S5P_FIMV_MFC_BUS_RESET_CTRL 0x7110
#define S5P_FIMV_FW_VERSION_V6 0xf000
#define S5P_FIMV_INSTANCE_ID_V6 0xf008
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c
index 165bc86..fbfdf03 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c
@@ -159,6 +159,10 @@
}
clear_bit(0, &dev->hw_lock);
spin_unlock_irqrestore(&dev->irqlock, flags);
+
+ /* De-init MFC */
+ s5p_mfc_deinit_hw(dev);
+
/* Double check if there is at least one instance running.
* If no instance is in memory than no firmware should be present */
if (dev->num_inst > 0) {
@@ -220,11 +224,14 @@
size_t dec_y_addr;
unsigned int frame_type;
- dec_y_addr = s5p_mfc_hw_call(dev->mfc_ops, get_dec_y_adr, dev);
+ /* Make sure we actually have a new frame before continuing. */
frame_type = s5p_mfc_hw_call(dev->mfc_ops, get_dec_frame_type, dev);
+ if (frame_type == S5P_FIMV_DECODE_FRAME_SKIPPED)
+ return;
+ dec_y_addr = s5p_mfc_hw_call(dev->mfc_ops, get_dec_y_adr, dev);
/* Copy timestamp / timecode from decoded src to dst and set
- appropriate flags */
+ appropriate flags. */
src_buf = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
list_for_each_entry(dst_buf, &ctx->dst_queue, list) {
if (vb2_dma_contig_plane_dma_addr(dst_buf->b, 0) == dec_y_addr) {
@@ -250,6 +257,11 @@
dst_buf->b->v4l2_buf.flags |=
V4L2_BUF_FLAG_BFRAME;
break;
+ default:
+ /* Don't know how to handle
+ S5P_FIMV_DECODE_FRAME_OTHER_FRAME. */
+ mfc_debug(2, "Unexpected frame type: %d\n",
+ frame_type);
}
break;
}
@@ -334,8 +346,7 @@
ctx->state = MFCINST_RES_CHANGE_INIT;
s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
wake_up_ctx(ctx, reason, err);
- if (test_and_clear_bit(0, &dev->hw_lock) == 0)
- BUG();
+ WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
s5p_mfc_clock_off();
s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
return;
@@ -407,8 +418,7 @@
clear_work_bit(ctx);
s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
wake_up_ctx(ctx, reason, err);
- if (test_and_clear_bit(0, &dev->hw_lock) == 0)
- BUG();
+ WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
s5p_mfc_clock_off();
/* if suspending, wake up device and do not try_run again*/
if (test_bit(0, &dev->enter_suspend))
@@ -455,8 +465,7 @@
break;
}
}
- if (test_and_clear_bit(0, &dev->hw_lock) == 0)
- BUG();
+ WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
s5p_mfc_clock_off();
wake_up_dev(dev, reason, err);
@@ -510,8 +519,7 @@
}
s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
clear_work_bit(ctx);
- if (test_and_clear_bit(0, &dev->hw_lock) == 0)
- BUG();
+ WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
s5p_mfc_clock_off();
s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
wake_up_ctx(ctx, reason, err);
@@ -549,16 +557,14 @@
} else {
ctx->dpb_flush_flag = 0;
}
- if (test_and_clear_bit(0, &dev->hw_lock) == 0)
- BUG();
+ WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
s5p_mfc_clock_off();
wake_up(&ctx->queue);
s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
} else {
- if (test_and_clear_bit(0, &dev->hw_lock) == 0)
- BUG();
+ WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
s5p_mfc_clock_off();
@@ -635,8 +641,7 @@
mfc_err("post_frame_start() failed\n");
s5p_mfc_hw_call_void(dev->mfc_ops, clear_int_flags, dev);
wake_up_ctx(ctx, reason, err);
- if (test_and_clear_bit(0, &dev->hw_lock) == 0)
- BUG();
+ WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
s5p_mfc_clock_off();
s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
} else {
@@ -815,7 +820,7 @@
ret = -ENOENT;
goto err_queue_init;
}
- q->mem_ops = (struct vb2_mem_ops *)&vb2_dma_contig_memops;
+ q->mem_ops = &vb2_dma_contig_memops;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
ret = vb2_queue_init(q);
if (ret) {
@@ -837,7 +842,7 @@
ret = -ENOENT;
goto err_queue_init;
}
- q->mem_ops = (struct vb2_mem_ops *)&vb2_dma_contig_memops;
+ q->mem_ops = &vb2_dma_contig_memops;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
ret = vb2_queue_init(q);
if (ret) {
@@ -1284,11 +1289,17 @@
m_dev->int_cond, msecs_to_jiffies(MFC_INT_TIMEOUT));
if (ret == 0) {
mfc_err("Waiting for hardware to finish timed out\n");
+ clear_bit(0, &m_dev->enter_suspend);
return -EIO;
}
}
- return s5p_mfc_sleep(m_dev);
+ ret = s5p_mfc_sleep(m_dev);
+ if (ret) {
+ clear_bit(0, &m_dev->enter_suspend);
+ clear_bit(0, &m_dev->hw_lock);
+ }
+ return ret;
}
static int s5p_mfc_resume(struct device *dev)
@@ -1302,7 +1313,7 @@
}
#endif
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
static int s5p_mfc_runtime_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
@@ -1505,7 +1516,6 @@
.id_table = mfc_driver_ids,
.driver = {
.name = S5P_MFC_NAME,
- .owner = THIS_MODULE,
.pm = &s5p_mfc_pm_ops,
.of_match_table = exynos_mfc_match,
},
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
index 3e41ca1..15f7663 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
@@ -237,8 +237,6 @@
/**
* struct s5p_mfc_priv_buf - represents internal used buffer
- * @alloc: allocation-specific context for each buffer
- * (videobuf2 allocator)
* @ofs: offset of each buffer, will be used for MFC
* @virt: kernel virtual address, only valid when the
* buffer accessed by driver
@@ -246,7 +244,6 @@
* @size: size of the buffer
*/
struct s5p_mfc_priv_buf {
- void *alloc;
unsigned long ofs;
void *virt;
dma_addr_t dma;
@@ -340,6 +337,7 @@
struct s5p_mfc_hw_cmds *mfc_cmds;
const struct s5p_mfc_regs *mfc_regs;
enum s5p_mfc_fw_ver fw_ver;
+ bool risc_on; /* indicates if RISC is on or off */
};
/**
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c
index 0c885a8..40d8a03 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c
@@ -129,6 +129,25 @@
return 0;
}
+static int s5p_mfc_bus_reset(struct s5p_mfc_dev *dev)
+{
+ unsigned int status;
+ unsigned long timeout;
+
+ /* Reset */
+ mfc_write(dev, 0x1, S5P_FIMV_MFC_BUS_RESET_CTRL);
+ timeout = jiffies + msecs_to_jiffies(MFC_BW_TIMEOUT);
+ /* Check bus status */
+ do {
+ if (time_after(jiffies, timeout)) {
+ mfc_err("Timeout while resetting MFC.\n");
+ return -EIO;
+ }
+ status = mfc_read(dev, S5P_FIMV_MFC_BUS_RESET_CTRL);
+ } while ((status & 0x2) == 0);
+ return 0;
+}
+
/* Reset the device */
int s5p_mfc_reset(struct s5p_mfc_dev *dev)
{
@@ -139,12 +158,6 @@
mfc_debug_enter();
if (IS_MFCV6_PLUS(dev)) {
- /* Reset IP */
- /* except RISC, reset */
- mfc_write(dev, 0xFEE, S5P_FIMV_MFC_RESET_V6);
- /* reset release */
- mfc_write(dev, 0x0, S5P_FIMV_MFC_RESET_V6);
-
/* Zero Initialization of MFC registers */
mfc_write(dev, 0, S5P_FIMV_RISC2HOST_CMD_V6);
mfc_write(dev, 0, S5P_FIMV_HOST2RISC_CMD_V6);
@@ -153,8 +166,17 @@
for (i = 0; i < S5P_FIMV_REG_CLEAR_COUNT_V6; i++)
mfc_write(dev, 0, S5P_FIMV_REG_CLEAR_BEGIN_V6 + (i*4));
- /* Reset */
- mfc_write(dev, 0, S5P_FIMV_RISC_ON_V6);
+ /* check bus reset control before reset */
+ if (dev->risc_on)
+ if (s5p_mfc_bus_reset(dev))
+ return -EIO;
+ /* Reset
+ * set RISC_ON to 0 during power_on & wake_up.
+ * V6 needs RISC_ON set to 0 during reset also.
+ */
+ if ((!dev->risc_on) || (!IS_MFCV7_PLUS(dev)))
+ mfc_write(dev, 0, S5P_FIMV_RISC_ON_V6);
+
mfc_write(dev, 0x1FFF, S5P_FIMV_MFC_RESET_V6);
mfc_write(dev, 0, S5P_FIMV_MFC_RESET_V6);
} else {
@@ -226,6 +248,7 @@
/* 0. MFC reset */
mfc_debug(2, "MFC reset..\n");
s5p_mfc_clock_on();
+ dev->risc_on = 0;
ret = s5p_mfc_reset(dev);
if (ret) {
mfc_err("Failed to reset MFC - timeout\n");
@@ -238,8 +261,10 @@
s5p_mfc_clear_cmds(dev);
/* 3. Release reset signal to the RISC */
s5p_mfc_clean_dev_int_flags(dev);
- if (IS_MFCV6_PLUS(dev))
+ if (IS_MFCV6_PLUS(dev)) {
+ dev->risc_on = 1;
mfc_write(dev, 0x1, S5P_FIMV_RISC_ON_V6);
+ }
else
mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET);
mfc_debug(2, "Will now wait for completion of firmware transfer\n");
@@ -328,6 +353,58 @@
return ret;
}
+static int s5p_mfc_v8_wait_wakeup(struct s5p_mfc_dev *dev)
+{
+ int ret;
+
+ /* Release reset signal to the RISC */
+ dev->risc_on = 1;
+ mfc_write(dev, 0x1, S5P_FIMV_RISC_ON_V6);
+
+ if (s5p_mfc_wait_for_done_dev(dev, S5P_MFC_R2H_CMD_FW_STATUS_RET)) {
+ mfc_err("Failed to reset MFCV8\n");
+ return -EIO;
+ }
+ mfc_debug(2, "Write command to wakeup MFCV8\n");
+ ret = s5p_mfc_hw_call(dev->mfc_cmds, wakeup_cmd, dev);
+ if (ret) {
+ mfc_err("Failed to send command to MFCV8 - timeout\n");
+ return ret;
+ }
+
+ if (s5p_mfc_wait_for_done_dev(dev, S5P_MFC_R2H_CMD_WAKEUP_RET)) {
+ mfc_err("Failed to wakeup MFC\n");
+ return -EIO;
+ }
+ return ret;
+}
+
+static int s5p_mfc_wait_wakeup(struct s5p_mfc_dev *dev)
+{
+ int ret;
+
+ /* Send MFC wakeup command */
+ ret = s5p_mfc_hw_call(dev->mfc_cmds, wakeup_cmd, dev);
+ if (ret) {
+ mfc_err("Failed to send command to MFC - timeout\n");
+ return ret;
+ }
+
+ /* Release reset signal to the RISC */
+ if (IS_MFCV6_PLUS(dev)) {
+ dev->risc_on = 1;
+ mfc_write(dev, 0x1, S5P_FIMV_RISC_ON_V6);
+ } else {
+ mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET);
+ }
+
+ if (s5p_mfc_wait_for_done_dev(dev, S5P_MFC_R2H_CMD_WAKEUP_RET)) {
+ mfc_err("Failed to wakeup MFC\n");
+ return -EIO;
+ }
+ return ret;
+}
+
int s5p_mfc_wakeup(struct s5p_mfc_dev *dev)
{
int ret;
@@ -336,9 +413,11 @@
/* 0. MFC reset */
mfc_debug(2, "MFC reset..\n");
s5p_mfc_clock_on();
+ dev->risc_on = 0;
ret = s5p_mfc_reset(dev);
if (ret) {
mfc_err("Failed to reset MFC - timeout\n");
+ s5p_mfc_clock_off();
return ret;
}
mfc_debug(2, "Done MFC reset..\n");
@@ -347,23 +426,16 @@
/* 2. Initialize registers of channel I/F */
s5p_mfc_clear_cmds(dev);
s5p_mfc_clean_dev_int_flags(dev);
- /* 3. Initialize firmware */
- ret = s5p_mfc_hw_call(dev->mfc_cmds, wakeup_cmd, dev);
- if (ret) {
- mfc_err("Failed to send command to MFC - timeout\n");
- return ret;
- }
- /* 4. Release reset signal to the RISC */
- if (IS_MFCV6_PLUS(dev))
- mfc_write(dev, 0x1, S5P_FIMV_RISC_ON_V6);
+ /* 3. Send MFC wakeup command and wait for completion*/
+ if (IS_MFCV8(dev))
+ ret = s5p_mfc_v8_wait_wakeup(dev);
else
- mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET);
- mfc_debug(2, "Ok, now will write a command to wakeup the system\n");
- if (s5p_mfc_wait_for_done_dev(dev, S5P_MFC_R2H_CMD_WAKEUP_RET)) {
- mfc_err("Failed to load firmware\n");
- return -EIO;
- }
+ ret = s5p_mfc_wait_wakeup(dev);
+
s5p_mfc_clock_off();
+ if (ret)
+ return ret;
+
dev->int_cond = 0;
if (dev->int_err != 0 || dev->int_type !=
S5P_MFC_R2H_CMD_WAKEUP_RET) {
@@ -396,7 +468,6 @@
}
set_work_bit_irqsave(ctx);
- s5p_mfc_clean_ctx_int_flags(ctx);
s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
if (s5p_mfc_wait_for_done_ctx(ctx,
S5P_MFC_R2H_CMD_OPEN_INSTANCE_RET, 0)) {
@@ -422,7 +493,6 @@
{
ctx->state = MFCINST_RETURN_INST;
set_work_bit_irqsave(ctx);
- s5p_mfc_clean_ctx_int_flags(ctx);
s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
/* Wait until instance is returned or timeout occurred */
if (s5p_mfc_wait_for_done_ctx(ctx,
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
index a98fe02..c6c3452 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
@@ -269,15 +269,13 @@
strncpy(cap->driver, dev->plat_dev->name, sizeof(cap->driver) - 1);
strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1);
cap->bus_info[0] = 0;
- cap->version = KERNEL_VERSION(1, 0, 0);
/*
* This is only a mem-to-mem video device. The capture and output
* device capability flags are left only for backward compatibility
* and are scheduled for removal.
*/
- cap->capabilities = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING |
- V4L2_CAP_VIDEO_CAPTURE_MPLANE |
- V4L2_CAP_VIDEO_OUTPUT_MPLANE;
+ cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -334,7 +332,6 @@
MFCINST_RES_CHANGE_END)) {
/* If the MFC is parsing the header,
* so wait until it is finished */
- s5p_mfc_clean_ctx_int_flags(ctx);
s5p_mfc_wait_for_done_ctx(ctx, S5P_MFC_R2H_CMD_SEQ_DONE_RET,
0);
}
@@ -740,12 +737,12 @@
ctx->state < MFCINST_ABORT) {
ctrl->val = ctx->pb_count;
break;
- } else if (ctx->state != MFCINST_INIT) {
+ } else if (ctx->state != MFCINST_INIT &&
+ ctx->state != MFCINST_RES_CHANGE_END) {
v4l2_err(&dev->v4l2_dev, "Decoding not initialised\n");
return -EINVAL;
}
/* Should wait for the header to be parsed */
- s5p_mfc_clean_ctx_int_flags(ctx);
s5p_mfc_wait_for_done_ctx(ctx,
S5P_MFC_R2H_CMD_SEQ_DONE_RET, 0);
if (ctx->state >= MFCINST_HEAD_PARSED &&
@@ -1057,7 +1054,6 @@
if (IS_MFCV6_PLUS(dev) && (ctx->state == MFCINST_RUNNING)) {
ctx->state = MFCINST_FLUSH;
set_work_bit_irqsave(ctx);
- s5p_mfc_clean_ctx_int_flags(ctx);
s5p_mfc_hw_call_void(dev->mfc_ops, try_run, dev);
if (s5p_mfc_wait_for_done_ctx(ctx,
S5P_MFC_R2H_CMD_DPB_FLUSH_RET, 0))
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
index a904a1c..bd64f1d 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
@@ -32,7 +32,7 @@
#include "s5p_mfc_intr.h"
#include "s5p_mfc_opr.h"
-#define DEF_SRC_FMT_ENC V4L2_PIX_FMT_NV12MT
+#define DEF_SRC_FMT_ENC V4L2_PIX_FMT_NV12M
#define DEF_DST_FMT_ENC V4L2_PIX_FMT_H264
static struct s5p_mfc_fmt formats[] = {
@@ -67,8 +67,7 @@
.codec_mode = S5P_MFC_CODEC_NONE,
.type = MFC_FMT_RAW,
.num_planes = 2,
- .versions = MFC_V5_BIT | MFC_V6_BIT | MFC_V7_BIT |
- MFC_V8_BIT,
+ .versions = MFC_V6_BIT | MFC_V7_BIT | MFC_V8_BIT,
},
{
.name = "H264 Encoded Stream",
@@ -690,6 +689,16 @@
.step = 1,
.default_value = 0,
},
+ {
+ .id = V4L2_CID_MIN_BUFFERS_FOR_OUTPUT,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Minimum number of output bufs",
+ .minimum = 1,
+ .maximum = 32,
+ .step = 1,
+ .default_value = 1,
+ .is_volatile = 1,
+ },
};
#define NUM_CTRLS ARRAY_SIZE(controls)
@@ -938,15 +947,13 @@
strncpy(cap->driver, dev->plat_dev->name, sizeof(cap->driver) - 1);
strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1);
cap->bus_info[0] = 0;
- cap->version = KERNEL_VERSION(1, 0, 0);
/*
* This is only a mem-to-mem video device. The capture and output
* device capability flags are left only for backward compatibility
* and are scheduled for removal.
*/
- cap->capabilities = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING |
- V4L2_CAP_VIDEO_CAPTURE_MPLANE |
- V4L2_CAP_VIDEO_OUTPUT_MPLANE;
+ cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -1137,6 +1144,11 @@
(reqbufs->memory != V4L2_MEMORY_USERPTR))
return -EINVAL;
if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ if (reqbufs->count == 0) {
+ ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+ ctx->capture_state = QUEUE_FREE;
+ return ret;
+ }
if (ctx->capture_state != QUEUE_FREE) {
mfc_err("invalid capture state: %d\n",
ctx->capture_state);
@@ -1158,6 +1170,14 @@
return -ENOMEM;
}
} else if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ if (reqbufs->count == 0) {
+ mfc_debug(2, "Freeing buffers\n");
+ ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
+ s5p_mfc_hw_call_void(dev->mfc_ops, release_codec_buffers,
+ ctx);
+ ctx->output_state = QUEUE_FREE;
+ return ret;
+ }
if (ctx->output_state != QUEUE_FREE) {
mfc_err("invalid output state: %d\n",
ctx->output_state);
@@ -1624,8 +1644,39 @@
return ret;
}
+static int s5p_mfc_enc_g_v_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct s5p_mfc_ctx *ctx = ctrl_to_ctx(ctrl);
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT:
+ if (ctx->state >= MFCINST_HEAD_PARSED &&
+ ctx->state < MFCINST_ABORT) {
+ ctrl->val = ctx->pb_count;
+ break;
+ } else if (ctx->state != MFCINST_INIT) {
+ v4l2_err(&dev->v4l2_dev, "Encoding not initialised\n");
+ return -EINVAL;
+ }
+ /* Should wait for the header to be produced */
+ s5p_mfc_wait_for_done_ctx(ctx,
+ S5P_MFC_R2H_CMD_SEQ_DONE_RET, 0);
+ if (ctx->state >= MFCINST_HEAD_PARSED &&
+ ctx->state < MFCINST_ABORT) {
+ ctrl->val = ctx->pb_count;
+ } else {
+ v4l2_err(&dev->v4l2_dev, "Encoding not initialised\n");
+ return -EINVAL;
+ }
+ break;
+ }
+ return 0;
+}
+
static const struct v4l2_ctrl_ops s5p_mfc_enc_ctrl_ops = {
.s_ctrl = s5p_mfc_enc_s_ctrl,
+ .g_volatile_ctrl = s5p_mfc_enc_g_v_ctrl,
};
static int vidioc_s_parm(struct file *file, void *priv,
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c
index 7cf0796..0c4fcf2 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c
@@ -1178,7 +1178,6 @@
s5p_mfc_set_dec_stream_buffer_v5(ctx, 0, 0, 0);
dev->curr_ctx = ctx->num;
- s5p_mfc_clean_ctx_int_flags(ctx);
s5p_mfc_decode_one_frame_v5(ctx, MFC_DEC_RES_CHANGE);
}
@@ -1192,7 +1191,6 @@
last_frame = MFC_DEC_LAST_FRAME;
s5p_mfc_set_dec_stream_buffer_v5(ctx, 0, 0, 0);
dev->curr_ctx = ctx->num;
- s5p_mfc_clean_ctx_int_flags(ctx);
s5p_mfc_decode_one_frame_v5(ctx, last_frame);
return 0;
}
@@ -1212,7 +1210,6 @@
ctx->consumed_stream, temp_vb->b->v4l2_planes[0].bytesused);
spin_unlock_irqrestore(&dev->irqlock, flags);
dev->curr_ctx = ctx->num;
- s5p_mfc_clean_ctx_int_flags(ctx);
if (temp_vb->b->v4l2_planes[0].bytesused == 0) {
last_frame = MFC_DEC_LAST_FRAME;
mfc_debug(2, "Setting ctx->state to FINISHING\n");
@@ -1273,7 +1270,6 @@
s5p_mfc_set_enc_stream_buffer_v5(ctx, dst_addr, dst_size);
spin_unlock_irqrestore(&dev->irqlock, flags);
dev->curr_ctx = ctx->num;
- s5p_mfc_clean_ctx_int_flags(ctx);
mfc_debug(2, "encoding buffer with index=%d state=%d\n",
src_mb ? src_mb->b->v4l2_buf.index : -1, ctx->state);
s5p_mfc_encode_one_frame_v5(ctx);
@@ -1297,7 +1293,6 @@
0, temp_vb->b->v4l2_planes[0].bytesused);
spin_unlock_irqrestore(&dev->irqlock, flags);
dev->curr_ctx = ctx->num;
- s5p_mfc_clean_ctx_int_flags(ctx);
s5p_mfc_init_decode_v5(ctx);
}
@@ -1317,7 +1312,6 @@
s5p_mfc_set_enc_stream_buffer_v5(ctx, dst_addr, dst_size);
spin_unlock_irqrestore(&dev->irqlock, flags);
dev->curr_ctx = ctx->num;
- s5p_mfc_clean_ctx_int_flags(ctx);
s5p_mfc_init_encode_v5(ctx);
}
@@ -1352,7 +1346,6 @@
0, temp_vb->b->v4l2_planes[0].bytesused);
spin_unlock_irqrestore(&dev->irqlock, flags);
dev->curr_ctx = ctx->num;
- s5p_mfc_clean_ctx_int_flags(ctx);
ret = s5p_mfc_set_dec_frame_buffer_v5(ctx);
if (ret) {
mfc_err("Failed to alloc frame mem\n");
@@ -1396,6 +1389,8 @@
* Now obtaining frames from MFC buffer
*/
s5p_mfc_clock_on();
+ s5p_mfc_clean_ctx_int_flags(ctx);
+
if (ctx->type == MFCINST_DECODER) {
s5p_mfc_set_dec_desc_buffer(ctx);
switch (ctx->state) {
@@ -1406,12 +1401,10 @@
ret = s5p_mfc_run_dec_frame(ctx, MFC_DEC_FRAME);
break;
case MFCINST_INIT:
- s5p_mfc_clean_ctx_int_flags(ctx);
ret = s5p_mfc_hw_call(dev->mfc_cmds, open_inst_cmd,
ctx);
break;
case MFCINST_RETURN_INST:
- s5p_mfc_clean_ctx_int_flags(ctx);
ret = s5p_mfc_hw_call(dev->mfc_cmds, close_inst_cmd,
ctx);
break;
@@ -1444,12 +1437,10 @@
ret = s5p_mfc_run_enc_frame(ctx);
break;
case MFCINST_INIT:
- s5p_mfc_clean_ctx_int_flags(ctx);
ret = s5p_mfc_hw_call(dev->mfc_cmds, open_inst_cmd,
ctx);
break;
case MFCINST_RETURN_INST:
- s5p_mfc_clean_ctx_int_flags(ctx);
ret = s5p_mfc_hw_call(dev->mfc_cmds, close_inst_cmd,
ctx);
break;
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
index 8798b14..9aea179 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
@@ -1394,7 +1394,6 @@
if (flush) {
dev->curr_ctx = ctx->num;
- s5p_mfc_clean_ctx_int_flags(ctx);
writel(ctx->inst_no, mfc_regs->instance_id);
s5p_mfc_hw_call_void(dev->mfc_cmds, cmd_host2risc, dev,
S5P_FIMV_H2R_CMD_FLUSH_V6, NULL);
@@ -1532,27 +1531,10 @@
static inline void s5p_mfc_run_dec_last_frames(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
- struct s5p_mfc_buf *temp_vb;
- unsigned long flags;
- spin_lock_irqsave(&dev->irqlock, flags);
-
- /* Frames are being decoded */
- if (list_empty(&ctx->src_queue)) {
- mfc_debug(2, "No src buffers.\n");
- spin_unlock_irqrestore(&dev->irqlock, flags);
- return;
- }
- /* Get the next source buffer */
- temp_vb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
- temp_vb->flags |= MFC_BUF_FLAG_USED;
- s5p_mfc_set_dec_stream_buffer_v6(ctx,
- vb2_dma_contig_plane_dma_addr(temp_vb->b, 0), 0, 0);
- spin_unlock_irqrestore(&dev->irqlock, flags);
-
+ s5p_mfc_set_dec_stream_buffer_v6(ctx, 0, 0, 0);
dev->curr_ctx = ctx->num;
- s5p_mfc_clean_ctx_int_flags(ctx);
- s5p_mfc_decode_one_frame_v6(ctx, 1);
+ s5p_mfc_decode_one_frame_v6(ctx, MFC_DEC_LAST_FRAME);
}
static inline int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx)
@@ -1588,7 +1570,6 @@
spin_unlock_irqrestore(&dev->irqlock, flags);
dev->curr_ctx = ctx->num;
- s5p_mfc_clean_ctx_int_flags(ctx);
if (temp_vb->b->v4l2_planes[0].bytesused == 0) {
last_frame = 1;
mfc_debug(2, "Setting ctx->state to FINISHING\n");
@@ -1645,7 +1626,6 @@
spin_unlock_irqrestore(&dev->irqlock, flags);
dev->curr_ctx = ctx->num;
- s5p_mfc_clean_ctx_int_flags(ctx);
s5p_mfc_encode_one_frame_v6(ctx);
return 0;
@@ -1667,7 +1647,6 @@
temp_vb->b->v4l2_planes[0].bytesused);
spin_unlock_irqrestore(&dev->irqlock, flags);
dev->curr_ctx = ctx->num;
- s5p_mfc_clean_ctx_int_flags(ctx);
s5p_mfc_init_decode_v6(ctx);
}
@@ -1687,7 +1666,6 @@
s5p_mfc_set_enc_stream_buffer_v6(ctx, dst_addr, dst_size);
spin_unlock_irqrestore(&dev->irqlock, flags);
dev->curr_ctx = ctx->num;
- s5p_mfc_clean_ctx_int_flags(ctx);
s5p_mfc_init_encode_v6(ctx);
}
@@ -1707,7 +1685,6 @@
}
dev->curr_ctx = ctx->num;
- s5p_mfc_clean_ctx_int_flags(ctx);
ret = s5p_mfc_set_dec_frame_buffer_v6(ctx);
if (ret) {
mfc_err("Failed to alloc frame mem.\n");
@@ -1722,7 +1699,6 @@
int ret;
dev->curr_ctx = ctx->num;
- s5p_mfc_clean_ctx_int_flags(ctx);
ret = s5p_mfc_set_enc_ref_buffer_v6(ctx);
if (ret) {
mfc_err("Failed to alloc frame mem.\n");
@@ -1771,6 +1747,8 @@
* Now obtaining frames from MFC buffer */
s5p_mfc_clock_on();
+ s5p_mfc_clean_ctx_int_flags(ctx);
+
if (ctx->type == MFCINST_DECODER) {
switch (ctx->state) {
case MFCINST_FINISHING:
@@ -1780,12 +1758,10 @@
ret = s5p_mfc_run_dec_frame(ctx);
break;
case MFCINST_INIT:
- s5p_mfc_clean_ctx_int_flags(ctx);
ret = s5p_mfc_hw_call(dev->mfc_cmds, open_inst_cmd,
ctx);
break;
case MFCINST_RETURN_INST:
- s5p_mfc_clean_ctx_int_flags(ctx);
ret = s5p_mfc_hw_call(dev->mfc_cmds, close_inst_cmd,
ctx);
break;
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c
index 826c489..5f97a33 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c
@@ -13,9 +13,7 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/platform_device.h>
-#ifdef CONFIG_PM_RUNTIME
#include <linux/pm_runtime.h>
-#endif
#include "s5p_mfc_common.h"
#include "s5p_mfc_debug.h"
#include "s5p_mfc_pm.h"
@@ -67,7 +65,7 @@
}
atomic_set(&pm->power, 0);
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
pm->device = &dev->plat_dev->dev;
pm_runtime_enable(pm->device);
#endif
@@ -93,7 +91,7 @@
}
clk_unprepare(pm->clock_gate);
clk_put(pm->clock_gate);
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
pm_runtime_disable(pm->device);
#endif
}
@@ -120,7 +118,7 @@
int s5p_mfc_power_on(void)
{
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
return pm_runtime_get_sync(pm->device);
#else
atomic_set(&pm->power, 1);
@@ -130,7 +128,7 @@
int s5p_mfc_power_off(void)
{
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
return pm_runtime_put_sync(pm->device);
#else
atomic_set(&pm->power, 0);
diff --git a/drivers/media/platform/s5p-tv/Kconfig b/drivers/media/platform/s5p-tv/Kconfig
index c22aac6..8f78739 100644
--- a/drivers/media/platform/s5p-tv/Kconfig
+++ b/drivers/media/platform/s5p-tv/Kconfig
@@ -8,8 +8,8 @@
config VIDEO_SAMSUNG_S5P_TV
bool "Samsung TV driver for S5P platform"
- depends on PM_RUNTIME
- depends on PLAT_S5P || ARCH_EXYNOS || COMPILE_TEST
+ depends on PM
+ depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
default n
---help---
Say Y here to enable selecting the TV output devices for
diff --git a/drivers/media/platform/s5p-tv/hdmi_drv.c b/drivers/media/platform/s5p-tv/hdmi_drv.c
index 1434ba4..c78b0cf 100644
--- a/drivers/media/platform/s5p-tv/hdmi_drv.c
+++ b/drivers/media/platform/s5p-tv/hdmi_drv.c
@@ -660,7 +660,7 @@
memset(fmt, 0, sizeof(*fmt));
fmt->width = t->hact.end - t->hact.beg;
fmt->height = t->vact[0].end - t->vact[0].beg;
- fmt->code = V4L2_MBUS_FMT_FIXED; /* means RGB888 */
+ fmt->code = MEDIA_BUS_FMT_FIXED; /* means RGB888 */
fmt->colorspace = V4L2_COLORSPACE_SRGB;
if (t->interlaced) {
fmt->field = V4L2_FIELD_INTERLACED;
@@ -1046,7 +1046,6 @@
.id_table = hdmi_driver_types,
.driver = {
.name = "s5p-hdmi",
- .owner = THIS_MODULE,
.pm = &hdmi_pm_ops,
}
};
diff --git a/drivers/media/platform/s5p-tv/mixer_drv.c b/drivers/media/platform/s5p-tv/mixer_drv.c
index bc08b5f..2a9501d 100644
--- a/drivers/media/platform/s5p-tv/mixer_drv.c
+++ b/drivers/media/platform/s5p-tv/mixer_drv.c
@@ -487,7 +487,6 @@
.remove = mxr_remove,
.driver = {
.name = MXR_DRIVER_NAME,
- .owner = THIS_MODULE,
.pm = &mxr_pm_ops,
}
};
diff --git a/drivers/media/platform/s5p-tv/sdo_drv.c b/drivers/media/platform/s5p-tv/sdo_drv.c
index 72cf892..3621af9 100644
--- a/drivers/media/platform/s5p-tv/sdo_drv.c
+++ b/drivers/media/platform/s5p-tv/sdo_drv.c
@@ -170,7 +170,7 @@
/* all modes are 720 pixels wide */
fmt->width = 720;
fmt->height = sdev->fmt->height;
- fmt->code = V4L2_MBUS_FMT_FIXED;
+ fmt->code = MEDIA_BUS_FMT_FIXED;
fmt->field = V4L2_FIELD_INTERLACED;
fmt->colorspace = V4L2_COLORSPACE_JPEG;
return 0;
@@ -482,7 +482,6 @@
.remove = sdo_remove,
.driver = {
.name = "s5p-sdo",
- .owner = THIS_MODULE,
.pm = &sdo_pm_ops,
}
};
diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c
index be3b3bc..aaa1f6f 100644
--- a/drivers/media/platform/sh_veu.c
+++ b/drivers/media/platform/sh_veu.c
@@ -1235,7 +1235,6 @@
.remove = sh_veu_remove,
.driver = {
.name = "sh_veu",
- .owner = THIS_MODULE,
},
};
diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c
index e5f1d4c..261f119 100644
--- a/drivers/media/platform/sh_vou.c
+++ b/drivers/media/platform/sh_vou.c
@@ -396,7 +396,8 @@
dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
strlcpy(cap->card, "SuperH VOU", sizeof(cap->card));
- cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+ cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -680,7 +681,7 @@
struct sh_vou_geometry geo;
struct v4l2_mbus_framefmt mbfmt = {
/* Revisit: is this the correct code? */
- .code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .code = MEDIA_BUS_FMT_YUYV8_2X8,
.field = V4L2_FIELD_INTERLACED,
.colorspace = V4L2_COLORSPACE_SMPTE170M,
};
@@ -733,7 +734,7 @@
/* Sanity checks */
if ((unsigned)mbfmt.width > VOU_MAX_IMAGE_WIDTH ||
(unsigned)mbfmt.height > img_height_max ||
- mbfmt.code != V4L2_MBUS_FMT_YUYV8_2X8)
+ mbfmt.code != MEDIA_BUS_FMT_YUYV8_2X8)
return -EIO;
if (mbfmt.width != geo.output.width ||
@@ -943,7 +944,7 @@
struct sh_vou_geometry geo;
struct v4l2_mbus_framefmt mbfmt = {
/* Revisit: is this the correct code? */
- .code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .code = MEDIA_BUS_FMT_YUYV8_2X8,
.field = V4L2_FIELD_INTERLACED,
.colorspace = V4L2_COLORSPACE_SMPTE170M,
};
@@ -994,7 +995,7 @@
/* Sanity checks */
if ((unsigned)mbfmt.width > VOU_MAX_IMAGE_WIDTH ||
(unsigned)mbfmt.height > img_height_max ||
- mbfmt.code != V4L2_MBUS_FMT_YUYV8_2X8)
+ mbfmt.code != MEDIA_BUS_FMT_YUYV8_2X8)
return -EIO;
geo.output.width = mbfmt.width;
@@ -1449,7 +1450,6 @@
.remove = sh_vou_remove,
.driver = {
.name = "sh-vou",
- .owner = THIS_MODULE,
},
};
diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig
index aebae21..fa5bceb 100644
--- a/drivers/media/platform/soc_camera/Kconfig
+++ b/drivers/media/platform/soc_camera/Kconfig
@@ -1,6 +1,6 @@
config SOC_CAMERA
- depends on !BACKPORT_KERNEL_3_4
- depends on !BACKPORT_KERNEL_3_5
+ depends on !KERNEL_3_4
+ depends on !KERNEL_3_5
tristate "SoC camera support"
depends on m
depends on VIDEO_V4L2 && HAS_DMA && I2C
diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c
index c5291b0..8efe403 100644
--- a/drivers/media/platform/soc_camera/atmel-isi.c
+++ b/drivers/media/platform/soc_camera/atmel-isi.c
@@ -105,25 +105,25 @@
}
static int configure_geometry(struct atmel_isi *isi, u32 width,
- u32 height, enum v4l2_mbus_pixelcode code)
+ u32 height, u32 code)
{
u32 cfg2, cr;
switch (code) {
/* YUV, including grey */
- case V4L2_MBUS_FMT_Y8_1X8:
+ case MEDIA_BUS_FMT_Y8_1X8:
cr = ISI_CFG2_GRAYSCALE;
break;
- case V4L2_MBUS_FMT_VYUY8_2X8:
+ case MEDIA_BUS_FMT_VYUY8_2X8:
cr = ISI_CFG2_YCC_SWAP_MODE_3;
break;
- case V4L2_MBUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_UYVY8_2X8:
cr = ISI_CFG2_YCC_SWAP_MODE_2;
break;
- case V4L2_MBUS_FMT_YVYU8_2X8:
+ case MEDIA_BUS_FMT_YVYU8_2X8:
cr = ISI_CFG2_YCC_SWAP_MODE_1;
break;
- case V4L2_MBUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_YUYV8_2X8:
cr = ISI_CFG2_YCC_SWAP_DEFAULT;
break;
/* RGB, TODO */
@@ -645,7 +645,7 @@
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
int formats = 0, ret;
/* sensor format */
- enum v4l2_mbus_pixelcode code;
+ u32 code;
/* soc camera host format */
const struct soc_mbus_pixelfmt *fmt;
@@ -670,10 +670,10 @@
}
switch (code) {
- case V4L2_MBUS_FMT_UYVY8_2X8:
- case V4L2_MBUS_FMT_VYUY8_2X8:
- case V4L2_MBUS_FMT_YUYV8_2X8:
- case V4L2_MBUS_FMT_YVYU8_2X8:
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_VYUY8_2X8:
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_YVYU8_2X8:
formats++;
if (xlate) {
xlate->host_fmt = &isi_camera_formats[0];
@@ -1068,7 +1068,6 @@
.remove = atmel_isi_remove,
.driver = {
.name = "atmel_isi",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(atmel_isi_of_match),
},
};
diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c
index 2347612..ce72bd2 100644
--- a/drivers/media/platform/soc_camera/mx2_camera.c
+++ b/drivers/media/platform/soc_camera/mx2_camera.c
@@ -211,7 +211,7 @@
/* prp configuration for a client-host fmt pair */
struct mx2_fmt_cfg {
- enum v4l2_mbus_pixelcode in_fmt;
+ u32 in_fmt;
u32 out_fmt;
struct mx2_prp_cfg cfg;
};
@@ -309,7 +309,7 @@
}
},
{
- .in_fmt = V4L2_MBUS_FMT_UYVY8_2X8,
+ .in_fmt = MEDIA_BUS_FMT_UYVY8_2X8,
.out_fmt = V4L2_PIX_FMT_YUYV,
.cfg = {
.channel = 1,
@@ -323,7 +323,7 @@
}
},
{
- .in_fmt = V4L2_MBUS_FMT_YUYV8_2X8,
+ .in_fmt = MEDIA_BUS_FMT_YUYV8_2X8,
.out_fmt = V4L2_PIX_FMT_YUYV,
.cfg = {
.channel = 1,
@@ -337,7 +337,7 @@
}
},
{
- .in_fmt = V4L2_MBUS_FMT_YUYV8_2X8,
+ .in_fmt = MEDIA_BUS_FMT_YUYV8_2X8,
.out_fmt = V4L2_PIX_FMT_YUV420,
.cfg = {
.channel = 2,
@@ -351,7 +351,7 @@
}
},
{
- .in_fmt = V4L2_MBUS_FMT_UYVY8_2X8,
+ .in_fmt = MEDIA_BUS_FMT_UYVY8_2X8,
.out_fmt = V4L2_PIX_FMT_YUV420,
.cfg = {
.channel = 2,
@@ -366,9 +366,7 @@
},
};
-static struct mx2_fmt_cfg *mx27_emma_prp_get_format(
- enum v4l2_mbus_pixelcode in_fmt,
- u32 out_fmt)
+static struct mx2_fmt_cfg *mx27_emma_prp_get_format(u32 in_fmt, u32 out_fmt)
{
int i;
@@ -945,7 +943,7 @@
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
const struct soc_mbus_pixelfmt *fmt;
struct device *dev = icd->parent;
- enum v4l2_mbus_pixelcode code;
+ u32 code;
int ret, formats = 0;
ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
@@ -959,8 +957,8 @@
return 0;
}
- if (code == V4L2_MBUS_FMT_YUYV8_2X8 ||
- code == V4L2_MBUS_FMT_UYVY8_2X8) {
+ if (code == MEDIA_BUS_FMT_YUYV8_2X8 ||
+ code == MEDIA_BUS_FMT_UYVY8_2X8) {
formats++;
if (xlate) {
/*
@@ -968,7 +966,7 @@
* soc_mediabus.c
*/
xlate->host_fmt =
- soc_mbus_get_fmtdesc(V4L2_MBUS_FMT_YUYV8_1_5X8);
+ soc_mbus_get_fmtdesc(MEDIA_BUS_FMT_YUYV8_1_5X8);
xlate->code = code;
dev_dbg(dev, "Providing host format %s for sensor code %d\n",
xlate->host_fmt->name, code);
@@ -976,11 +974,11 @@
}
}
- if (code == V4L2_MBUS_FMT_UYVY8_2X8) {
+ if (code == MEDIA_BUS_FMT_UYVY8_2X8) {
formats++;
if (xlate) {
xlate->host_fmt =
- soc_mbus_get_fmtdesc(V4L2_MBUS_FMT_YUYV8_2X8);
+ soc_mbus_get_fmtdesc(MEDIA_BUS_FMT_YUYV8_2X8);
xlate->code = code;
dev_dbg(dev, "Providing host format %s for sensor code %d\n",
xlate->host_fmt->name, code);
diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c
index 7696a87..a60c3bb 100644
--- a/drivers/media/platform/soc_camera/mx3_camera.c
+++ b/drivers/media/platform/soc_camera/mx3_camera.c
@@ -656,7 +656,7 @@
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
struct device *dev = icd->parent;
int formats = 0, ret;
- enum v4l2_mbus_pixelcode code;
+ u32 code;
const struct soc_mbus_pixelfmt *fmt;
ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
@@ -677,7 +677,7 @@
return 0;
switch (code) {
- case V4L2_MBUS_FMT_SBGGR10_1X10:
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
formats++;
if (xlate) {
xlate->host_fmt = &mx3_camera_formats[0];
@@ -687,7 +687,7 @@
mx3_camera_formats[0].name, code);
}
break;
- case V4L2_MBUS_FMT_Y10_1X10:
+ case MEDIA_BUS_FMT_Y10_1X10:
formats++;
if (xlate) {
xlate->host_fmt = &mx3_camera_formats[1];
@@ -1253,7 +1253,6 @@
static struct platform_driver mx3_camera_driver = {
.driver = {
.name = MX3_CAM_DRV_NAME,
- .owner = THIS_MODULE,
},
.probe = mx3_camera_probe,
.remove = mx3_camera_remove,
diff --git a/drivers/media/platform/soc_camera/omap1_camera.c b/drivers/media/platform/soc_camera/omap1_camera.c
index 74ce8b6..e6b9328 100644
--- a/drivers/media/platform/soc_camera/omap1_camera.c
+++ b/drivers/media/platform/soc_camera/omap1_camera.c
@@ -140,7 +140,7 @@
/* buffer for one video frame */
struct omap1_cam_buf {
struct videobuf_buffer vb;
- enum v4l2_mbus_pixelcode code;
+ u32 code;
int inwork;
struct scatterlist *sgbuf;
int sgcount;
@@ -980,7 +980,7 @@
/* Duplicate standard formats based on host capability of byte swapping */
static const struct soc_mbus_lookup omap1_cam_formats[] = {
{
- .code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
.fmt = {
.fourcc = V4L2_PIX_FMT_YUYV,
.name = "YUYV",
@@ -990,7 +990,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_VYUY8_2X8,
+ .code = MEDIA_BUS_FMT_VYUY8_2X8,
.fmt = {
.fourcc = V4L2_PIX_FMT_YVYU,
.name = "YVYU",
@@ -1000,7 +1000,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .code = MEDIA_BUS_FMT_YUYV8_2X8,
.fmt = {
.fourcc = V4L2_PIX_FMT_UYVY,
.name = "UYVY",
@@ -1010,7 +1010,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_YVYU8_2X8,
+ .code = MEDIA_BUS_FMT_YVYU8_2X8,
.fmt = {
.fourcc = V4L2_PIX_FMT_VYUY,
.name = "VYUY",
@@ -1020,7 +1020,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE,
+ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE,
.fmt = {
.fourcc = V4L2_PIX_FMT_RGB555,
.name = "RGB555",
@@ -1030,7 +1030,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE,
+ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
.fmt = {
.fourcc = V4L2_PIX_FMT_RGB555X,
.name = "RGB555X",
@@ -1040,7 +1040,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_RGB565_2X8_BE,
+ .code = MEDIA_BUS_FMT_RGB565_2X8_BE,
.fmt = {
.fourcc = V4L2_PIX_FMT_RGB565,
.name = "RGB565",
@@ -1050,7 +1050,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_RGB565_2X8_LE,
+ .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
.fmt = {
.fourcc = V4L2_PIX_FMT_RGB565X,
.name = "RGB565X",
@@ -1068,7 +1068,7 @@
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
struct device *dev = icd->parent;
int formats = 0, ret;
- enum v4l2_mbus_pixelcode code;
+ u32 code;
const struct soc_mbus_pixelfmt *fmt;
ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
@@ -1088,14 +1088,14 @@
return 0;
switch (code) {
- case V4L2_MBUS_FMT_YUYV8_2X8:
- case V4L2_MBUS_FMT_YVYU8_2X8:
- case V4L2_MBUS_FMT_UYVY8_2X8:
- case V4L2_MBUS_FMT_VYUY8_2X8:
- case V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE:
- case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
- case V4L2_MBUS_FMT_RGB565_2X8_BE:
- case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_YVYU8_2X8:
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_VYUY8_2X8:
+ case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE:
+ case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE:
+ case MEDIA_BUS_FMT_RGB565_2X8_BE:
+ case MEDIA_BUS_FMT_RGB565_2X8_LE:
formats++;
if (xlate) {
xlate->host_fmt = soc_mbus_find_fmtdesc(code,
diff --git a/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/soc_camera/pxa_camera.c
index 66178fc..951226a 100644
--- a/drivers/media/platform/soc_camera/pxa_camera.c
+++ b/drivers/media/platform/soc_camera/pxa_camera.c
@@ -187,7 +187,7 @@
struct pxa_buffer {
/* common v4l buffer stuff -- must be first */
struct videobuf_buffer vb;
- enum v4l2_mbus_pixelcode code;
+ u32 code;
/* our descriptor lists for Y, U and V channels */
struct pxa_cam_dma dmas[3];
int inwork;
@@ -1253,7 +1253,7 @@
struct device *dev = icd->parent;
int formats = 0, ret;
struct pxa_cam *cam;
- enum v4l2_mbus_pixelcode code;
+ u32 code;
const struct soc_mbus_pixelfmt *fmt;
ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
@@ -1283,7 +1283,7 @@
}
switch (code) {
- case V4L2_MBUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_UYVY8_2X8:
formats++;
if (xlate) {
xlate->host_fmt = &pxa_camera_formats[0];
@@ -1292,11 +1292,11 @@
dev_dbg(dev, "Providing format %s using code %d\n",
pxa_camera_formats[0].name, code);
}
- case V4L2_MBUS_FMT_VYUY8_2X8:
- case V4L2_MBUS_FMT_YUYV8_2X8:
- case V4L2_MBUS_FMT_YVYU8_2X8:
- case V4L2_MBUS_FMT_RGB565_2X8_LE:
- case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
+ case MEDIA_BUS_FMT_VYUY8_2X8:
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_YVYU8_2X8:
+ case MEDIA_BUS_FMT_RGB565_2X8_LE:
+ case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE:
if (xlate)
dev_dbg(dev, "Providing format %s packed\n",
fmt->name);
diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c
index 20defcb..0c1f556 100644
--- a/drivers/media/platform/soc_camera/rcar_vin.c
+++ b/drivers/media/platform/soc_camera/rcar_vin.c
@@ -64,6 +64,30 @@
#define VNDMR_REG 0x58 /* Video n Data Mode Register */
#define VNDMR2_REG 0x5C /* Video n Data Mode Register 2 */
#define VNUVAOF_REG 0x60 /* Video n UV Address Offset Register */
+#define VNC1A_REG 0x80 /* Video n Coefficient Set C1A Register */
+#define VNC1B_REG 0x84 /* Video n Coefficient Set C1B Register */
+#define VNC1C_REG 0x88 /* Video n Coefficient Set C1C Register */
+#define VNC2A_REG 0x90 /* Video n Coefficient Set C2A Register */
+#define VNC2B_REG 0x94 /* Video n Coefficient Set C2B Register */
+#define VNC2C_REG 0x98 /* Video n Coefficient Set C2C Register */
+#define VNC3A_REG 0xA0 /* Video n Coefficient Set C3A Register */
+#define VNC3B_REG 0xA4 /* Video n Coefficient Set C3B Register */
+#define VNC3C_REG 0xA8 /* Video n Coefficient Set C3C Register */
+#define VNC4A_REG 0xB0 /* Video n Coefficient Set C4A Register */
+#define VNC4B_REG 0xB4 /* Video n Coefficient Set C4B Register */
+#define VNC4C_REG 0xB8 /* Video n Coefficient Set C4C Register */
+#define VNC5A_REG 0xC0 /* Video n Coefficient Set C5A Register */
+#define VNC5B_REG 0xC4 /* Video n Coefficient Set C5B Register */
+#define VNC5C_REG 0xC8 /* Video n Coefficient Set C5C Register */
+#define VNC6A_REG 0xD0 /* Video n Coefficient Set C6A Register */
+#define VNC6B_REG 0xD4 /* Video n Coefficient Set C6B Register */
+#define VNC6C_REG 0xD8 /* Video n Coefficient Set C6C Register */
+#define VNC7A_REG 0xE0 /* Video n Coefficient Set C7A Register */
+#define VNC7B_REG 0xE4 /* Video n Coefficient Set C7B Register */
+#define VNC7C_REG 0xE8 /* Video n Coefficient Set C7C Register */
+#define VNC8A_REG 0xF0 /* Video n Coefficient Set C8A Register */
+#define VNC8B_REG 0xF4 /* Video n Coefficient Set C8B Register */
+#define VNC8C_REG 0xF8 /* Video n Coefficient Set C8C Register */
/* Register bit fields for R-Car VIN */
/* Video n Main Control Register bits */
@@ -106,6 +130,7 @@
#define VNDMR2_VPS (1 << 30)
#define VNDMR2_HPS (1 << 29)
#define VNDMR2_FTEV (1 << 17)
+#define VNDMR2_VLV(n) ((n & 0xf) << 12)
#define VIN_MAX_WIDTH 2048
#define VIN_MAX_HEIGHT 2048
@@ -117,6 +142,324 @@
RCAR_E1,
};
+struct vin_coeff {
+ unsigned short xs_value;
+ u32 coeff_set[24];
+};
+
+static const struct vin_coeff vin_coeff_set[] = {
+ { 0x0000, {
+ 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000 },
+ },
+ { 0x1000, {
+ 0x000fa400, 0x000fa400, 0x09625902,
+ 0x000003f8, 0x00000403, 0x3de0d9f0,
+ 0x001fffed, 0x00000804, 0x3cc1f9c3,
+ 0x001003de, 0x00000c01, 0x3cb34d7f,
+ 0x002003d2, 0x00000c00, 0x3d24a92d,
+ 0x00200bca, 0x00000bff, 0x3df600d2,
+ 0x002013cc, 0x000007ff, 0x3ed70c7e,
+ 0x00100fde, 0x00000000, 0x3f87c036 },
+ },
+ { 0x1200, {
+ 0x002ffff1, 0x002ffff1, 0x02a0a9c8,
+ 0x002003e7, 0x001ffffa, 0x000185bc,
+ 0x002007dc, 0x000003ff, 0x3e52859c,
+ 0x00200bd4, 0x00000002, 0x3d53996b,
+ 0x00100fd0, 0x00000403, 0x3d04ad2d,
+ 0x00000bd5, 0x00000403, 0x3d35ace7,
+ 0x3ff003e4, 0x00000801, 0x3dc674a1,
+ 0x3fffe800, 0x00000800, 0x3e76f461 },
+ },
+ { 0x1400, {
+ 0x00100be3, 0x00100be3, 0x04d1359a,
+ 0x00000fdb, 0x002003ed, 0x0211fd93,
+ 0x00000fd6, 0x002003f4, 0x0002d97b,
+ 0x000007d6, 0x002ffffb, 0x3e93b956,
+ 0x3ff003da, 0x001003ff, 0x3db49926,
+ 0x3fffefe9, 0x00100001, 0x3d655cee,
+ 0x3fffd400, 0x00000003, 0x3d65f4b6,
+ 0x000fb421, 0x00000402, 0x3dc6547e },
+ },
+ { 0x1600, {
+ 0x00000bdd, 0x00000bdd, 0x06519578,
+ 0x3ff007da, 0x00000be3, 0x03c24973,
+ 0x3ff003d9, 0x00000be9, 0x01b30d5f,
+ 0x3ffff7df, 0x001003f1, 0x0003c542,
+ 0x000fdfec, 0x001003f7, 0x3ec4711d,
+ 0x000fc400, 0x002ffffd, 0x3df504f1,
+ 0x001fa81a, 0x002ffc00, 0x3d957cc2,
+ 0x002f8c3c, 0x00100000, 0x3db5c891 },
+ },
+ { 0x1800, {
+ 0x3ff003dc, 0x3ff003dc, 0x0791e558,
+ 0x000ff7dd, 0x3ff007de, 0x05328554,
+ 0x000fe7e3, 0x3ff00be2, 0x03232546,
+ 0x000fd7ee, 0x000007e9, 0x0143bd30,
+ 0x001fb800, 0x000007ee, 0x00044511,
+ 0x002fa015, 0x000007f4, 0x3ef4bcee,
+ 0x002f8832, 0x001003f9, 0x3e4514c7,
+ 0x001f7853, 0x001003fd, 0x3de54c9f },
+ },
+ { 0x1a00, {
+ 0x000fefe0, 0x000fefe0, 0x08721d3c,
+ 0x001fdbe7, 0x000ffbde, 0x0652a139,
+ 0x001fcbf0, 0x000003df, 0x0463292e,
+ 0x002fb3ff, 0x3ff007e3, 0x0293a91d,
+ 0x002f9c12, 0x3ff00be7, 0x01241905,
+ 0x001f8c29, 0x000007ed, 0x3fe470eb,
+ 0x000f7c46, 0x000007f2, 0x3f04b8ca,
+ 0x3fef7865, 0x000007f6, 0x3e74e4a8 },
+ },
+ { 0x1c00, {
+ 0x001fd3e9, 0x001fd3e9, 0x08f23d26,
+ 0x002fbff3, 0x001fe3e4, 0x0712ad23,
+ 0x002fa800, 0x000ff3e0, 0x05631d1b,
+ 0x001f9810, 0x000ffbe1, 0x03b3890d,
+ 0x000f8c23, 0x000003e3, 0x0233e8fa,
+ 0x3fef843b, 0x000003e7, 0x00f430e4,
+ 0x3fbf8456, 0x3ff00bea, 0x00046cc8,
+ 0x3f8f8c72, 0x3ff00bef, 0x3f3490ac },
+ },
+ { 0x1e00, {
+ 0x001fbbf4, 0x001fbbf4, 0x09425112,
+ 0x001fa800, 0x002fc7ed, 0x0792b110,
+ 0x000f980e, 0x001fdbe6, 0x0613110a,
+ 0x3fff8c20, 0x001fe7e3, 0x04a368fd,
+ 0x3fcf8c33, 0x000ff7e2, 0x0343b8ed,
+ 0x3f9f8c4a, 0x000fffe3, 0x0203f8da,
+ 0x3f5f9c61, 0x000003e6, 0x00e428c5,
+ 0x3f1fb07b, 0x000003eb, 0x3fe440af },
+ },
+ { 0x2000, {
+ 0x000fa400, 0x000fa400, 0x09625902,
+ 0x3fff980c, 0x001fb7f5, 0x0812b0ff,
+ 0x3fdf901c, 0x001fc7ed, 0x06b2fcfa,
+ 0x3faf902d, 0x001fd3e8, 0x055348f1,
+ 0x3f7f983f, 0x001fe3e5, 0x04038ce3,
+ 0x3f3fa454, 0x001fefe3, 0x02e3c8d1,
+ 0x3f0fb86a, 0x001ff7e4, 0x01c3e8c0,
+ 0x3ecfd880, 0x000fffe6, 0x00c404ac },
+ },
+ { 0x2200, {
+ 0x3fdf9c0b, 0x3fdf9c0b, 0x09725cf4,
+ 0x3fbf9818, 0x3fffa400, 0x0842a8f1,
+ 0x3f8f9827, 0x000fb3f7, 0x0702f0ec,
+ 0x3f5fa037, 0x000fc3ef, 0x05d330e4,
+ 0x3f2fac49, 0x001fcfea, 0x04a364d9,
+ 0x3effc05c, 0x001fdbe7, 0x038394ca,
+ 0x3ecfdc6f, 0x001fe7e6, 0x0273b0bb,
+ 0x3ea00083, 0x001fefe6, 0x0183c0a9 },
+ },
+ { 0x2400, {
+ 0x3f9fa014, 0x3f9fa014, 0x098260e6,
+ 0x3f7f9c23, 0x3fcf9c0a, 0x08629ce5,
+ 0x3f4fa431, 0x3fefa400, 0x0742d8e1,
+ 0x3f1fb440, 0x3fffb3f8, 0x062310d9,
+ 0x3eefc850, 0x000fbbf2, 0x050340d0,
+ 0x3ecfe062, 0x000fcbec, 0x041364c2,
+ 0x3ea00073, 0x001fd3ea, 0x03037cb5,
+ 0x3e902086, 0x001fdfe8, 0x022388a5 },
+ },
+ { 0x2600, {
+ 0x3f5fa81e, 0x3f5fa81e, 0x096258da,
+ 0x3f3fac2b, 0x3f8fa412, 0x088290d8,
+ 0x3f0fbc38, 0x3fafa408, 0x0772c8d5,
+ 0x3eefcc47, 0x3fcfa800, 0x0672f4ce,
+ 0x3ecfe456, 0x3fefaffa, 0x05531cc6,
+ 0x3eb00066, 0x3fffbbf3, 0x047334bb,
+ 0x3ea01c77, 0x000fc7ee, 0x039348ae,
+ 0x3ea04486, 0x000fd3eb, 0x02b350a1 },
+ },
+ { 0x2800, {
+ 0x3f2fb426, 0x3f2fb426, 0x094250ce,
+ 0x3f0fc032, 0x3f4fac1b, 0x086284cd,
+ 0x3eefd040, 0x3f7fa811, 0x0782acc9,
+ 0x3ecfe84c, 0x3f9fa807, 0x06a2d8c4,
+ 0x3eb0005b, 0x3fbfac00, 0x05b2f4bc,
+ 0x3eb0186a, 0x3fdfb3fa, 0x04c308b4,
+ 0x3eb04077, 0x3fefbbf4, 0x03f31ca8,
+ 0x3ec06884, 0x000fbff2, 0x03031c9e },
+ },
+ { 0x2a00, {
+ 0x3f0fc42d, 0x3f0fc42d, 0x090240c4,
+ 0x3eefd439, 0x3f2fb822, 0x08526cc2,
+ 0x3edfe845, 0x3f4fb018, 0x078294bf,
+ 0x3ec00051, 0x3f6fac0f, 0x06b2b4bb,
+ 0x3ec0185f, 0x3f8fac07, 0x05e2ccb4,
+ 0x3ec0386b, 0x3fafac00, 0x0502e8ac,
+ 0x3ed05c77, 0x3fcfb3fb, 0x0432f0a3,
+ 0x3ef08482, 0x3fdfbbf6, 0x0372f898 },
+ },
+ { 0x2c00, {
+ 0x3eefdc31, 0x3eefdc31, 0x08e238b8,
+ 0x3edfec3d, 0x3f0fc828, 0x082258b9,
+ 0x3ed00049, 0x3f1fc01e, 0x077278b6,
+ 0x3ed01455, 0x3f3fb815, 0x06c294b2,
+ 0x3ed03460, 0x3f5fb40d, 0x0602acac,
+ 0x3ef0506c, 0x3f7fb006, 0x0542c0a4,
+ 0x3f107476, 0x3f9fb400, 0x0472c89d,
+ 0x3f309c80, 0x3fbfb7fc, 0x03b2cc94 },
+ },
+ { 0x2e00, {
+ 0x3eefec37, 0x3eefec37, 0x088220b0,
+ 0x3ee00041, 0x3effdc2d, 0x07f244ae,
+ 0x3ee0144c, 0x3f0fd023, 0x07625cad,
+ 0x3ef02c57, 0x3f1fc81a, 0x06c274a9,
+ 0x3f004861, 0x3f3fbc13, 0x060288a6,
+ 0x3f20686b, 0x3f5fb80c, 0x05529c9e,
+ 0x3f408c74, 0x3f6fb805, 0x04b2ac96,
+ 0x3f80ac7e, 0x3f8fb800, 0x0402ac8e },
+ },
+ { 0x3000, {
+ 0x3ef0003a, 0x3ef0003a, 0x084210a6,
+ 0x3ef01045, 0x3effec32, 0x07b228a7,
+ 0x3f00284e, 0x3f0fdc29, 0x073244a4,
+ 0x3f104058, 0x3f0fd420, 0x06a258a2,
+ 0x3f305c62, 0x3f2fc818, 0x0612689d,
+ 0x3f508069, 0x3f3fc011, 0x05728496,
+ 0x3f80a072, 0x3f4fc00a, 0x04d28c90,
+ 0x3fc0c07b, 0x3f6fbc04, 0x04429088 },
+ },
+ { 0x3200, {
+ 0x3f00103e, 0x3f00103e, 0x07f1fc9e,
+ 0x3f102447, 0x3f000035, 0x0782149d,
+ 0x3f203c4f, 0x3f0ff02c, 0x07122c9c,
+ 0x3f405458, 0x3f0fe424, 0x06924099,
+ 0x3f607061, 0x3f1fd41d, 0x06024c97,
+ 0x3f909068, 0x3f2fcc16, 0x05726490,
+ 0x3fc0b070, 0x3f3fc80f, 0x04f26c8a,
+ 0x0000d077, 0x3f4fc409, 0x04627484 },
+ },
+ { 0x3400, {
+ 0x3f202040, 0x3f202040, 0x07a1e898,
+ 0x3f303449, 0x3f100c38, 0x0741fc98,
+ 0x3f504c50, 0x3f10002f, 0x06e21495,
+ 0x3f706459, 0x3f1ff028, 0x06722492,
+ 0x3fa08060, 0x3f1fe421, 0x05f2348f,
+ 0x3fd09c67, 0x3f1fdc19, 0x05824c89,
+ 0x0000bc6e, 0x3f2fd014, 0x04f25086,
+ 0x0040dc74, 0x3f3fcc0d, 0x04825c7f },
+ },
+ { 0x3600, {
+ 0x3f403042, 0x3f403042, 0x0761d890,
+ 0x3f504848, 0x3f301c3b, 0x0701f090,
+ 0x3f805c50, 0x3f200c33, 0x06a2008f,
+ 0x3fa07458, 0x3f10002b, 0x06520c8d,
+ 0x3fd0905e, 0x3f1ff424, 0x05e22089,
+ 0x0000ac65, 0x3f1fe81d, 0x05823483,
+ 0x0030cc6a, 0x3f2fdc18, 0x04f23c81,
+ 0x0080e871, 0x3f2fd412, 0x0482407c },
+ },
+ { 0x3800, {
+ 0x3f604043, 0x3f604043, 0x0721c88a,
+ 0x3f80544a, 0x3f502c3c, 0x06d1d88a,
+ 0x3fb06851, 0x3f301c35, 0x0681e889,
+ 0x3fd08456, 0x3f30082f, 0x0611fc88,
+ 0x00009c5d, 0x3f200027, 0x05d20884,
+ 0x0030b863, 0x3f2ff421, 0x05621880,
+ 0x0070d468, 0x3f2fe81b, 0x0502247c,
+ 0x00c0ec6f, 0x3f2fe015, 0x04a22877 },
+ },
+ { 0x3a00, {
+ 0x3f904c44, 0x3f904c44, 0x06e1b884,
+ 0x3fb0604a, 0x3f70383e, 0x0691c885,
+ 0x3fe07451, 0x3f502c36, 0x0661d483,
+ 0x00009055, 0x3f401831, 0x0601ec81,
+ 0x0030a85b, 0x3f300c2a, 0x05b1f480,
+ 0x0070c061, 0x3f300024, 0x0562047a,
+ 0x00b0d867, 0x3f3ff41e, 0x05020c77,
+ 0x00f0f46b, 0x3f2fec19, 0x04a21474 },
+ },
+ { 0x3c00, {
+ 0x3fb05c43, 0x3fb05c43, 0x06c1b07e,
+ 0x3fe06c4b, 0x3f902c3f, 0x0681c081,
+ 0x0000844f, 0x3f703838, 0x0631cc7d,
+ 0x00309855, 0x3f602433, 0x05d1d47e,
+ 0x0060b459, 0x3f50142e, 0x0581e47b,
+ 0x00a0c85f, 0x3f400828, 0x0531f078,
+ 0x00e0e064, 0x3f300021, 0x0501fc73,
+ 0x00b0fc6a, 0x3f3ff41d, 0x04a20873 },
+ },
+ { 0x3e00, {
+ 0x3fe06444, 0x3fe06444, 0x0681a07a,
+ 0x00007849, 0x3fc0503f, 0x0641b07a,
+ 0x0020904d, 0x3fa0403a, 0x05f1c07a,
+ 0x0060a453, 0x3f803034, 0x05c1c878,
+ 0x0090b858, 0x3f70202f, 0x0571d477,
+ 0x00d0d05d, 0x3f501829, 0x0531e073,
+ 0x0110e462, 0x3f500825, 0x04e1e471,
+ 0x01510065, 0x3f40001f, 0x04a1f06d },
+ },
+ { 0x4000, {
+ 0x00007044, 0x00007044, 0x06519476,
+ 0x00208448, 0x3fe05c3f, 0x0621a476,
+ 0x0050984d, 0x3fc04c3a, 0x05e1b075,
+ 0x0080ac52, 0x3fa03c35, 0x05a1b875,
+ 0x00c0c056, 0x3f803030, 0x0561c473,
+ 0x0100d45b, 0x3f70202b, 0x0521d46f,
+ 0x0140e860, 0x3f601427, 0x04d1d46e,
+ 0x01810064, 0x3f500822, 0x0491dc6b },
+ },
+ { 0x5000, {
+ 0x0110a442, 0x0110a442, 0x0551545e,
+ 0x0140b045, 0x00e0983f, 0x0531585f,
+ 0x0160c047, 0x00c08c3c, 0x0511645e,
+ 0x0190cc4a, 0x00908039, 0x04f1685f,
+ 0x01c0dc4c, 0x00707436, 0x04d1705e,
+ 0x0200e850, 0x00506833, 0x04b1785b,
+ 0x0230f453, 0x00305c30, 0x0491805a,
+ 0x02710056, 0x0010542d, 0x04718059 },
+ },
+ { 0x6000, {
+ 0x01c0bc40, 0x01c0bc40, 0x04c13052,
+ 0x01e0c841, 0x01a0b43d, 0x04c13851,
+ 0x0210cc44, 0x0180a83c, 0x04a13453,
+ 0x0230d845, 0x0160a03a, 0x04913c52,
+ 0x0260e047, 0x01409838, 0x04714052,
+ 0x0280ec49, 0x01208c37, 0x04514c50,
+ 0x02b0f44b, 0x01008435, 0x04414c50,
+ 0x02d1004c, 0x00e07c33, 0x0431544f },
+ },
+ { 0x7000, {
+ 0x0230c83e, 0x0230c83e, 0x04711c4c,
+ 0x0250d03f, 0x0210c43c, 0x0471204b,
+ 0x0270d840, 0x0200b83c, 0x0451244b,
+ 0x0290dc42, 0x01e0b43a, 0x0441244c,
+ 0x02b0e443, 0x01c0b038, 0x0441284b,
+ 0x02d0ec44, 0x01b0a438, 0x0421304a,
+ 0x02f0f445, 0x0190a036, 0x04213449,
+ 0x0310f847, 0x01709c34, 0x04213848 },
+ },
+ { 0x8000, {
+ 0x0280d03d, 0x0280d03d, 0x04310c48,
+ 0x02a0d43e, 0x0270c83c, 0x04311047,
+ 0x02b0dc3e, 0x0250c83a, 0x04311447,
+ 0x02d0e040, 0x0240c03a, 0x04211446,
+ 0x02e0e840, 0x0220bc39, 0x04111847,
+ 0x0300e842, 0x0210b438, 0x04012445,
+ 0x0310f043, 0x0200b037, 0x04012045,
+ 0x0330f444, 0x01e0ac36, 0x03f12445 },
+ },
+ { 0xefff, {
+ 0x0340dc3a, 0x0340dc3a, 0x03b0ec40,
+ 0x0340e03a, 0x0330e039, 0x03c0f03e,
+ 0x0350e03b, 0x0330dc39, 0x03c0ec3e,
+ 0x0350e43a, 0x0320dc38, 0x03c0f43e,
+ 0x0360e43b, 0x0320d839, 0x03b0f03e,
+ 0x0360e83b, 0x0310d838, 0x03c0fc3b,
+ 0x0370e83b, 0x0310d439, 0x03a0f83d,
+ 0x0370e83c, 0x0300d438, 0x03b0fc3c },
+ }
+};
+
enum rcar_vin_state {
STOPPED = 0,
RUNNING,
@@ -161,6 +504,9 @@
/* Client output, as seen by the VIN */
unsigned int width;
unsigned int height;
+ /* User window from S_FMT */
+ unsigned int out_width;
+ unsigned int out_height;
/*
* User window from S_CROP / G_CROP, produced by client cropping and
* scaling, VIN scaling and VIN cropping, mapped back onto the client
@@ -272,16 +618,16 @@
/* input interface */
switch (icd->current_fmt->code) {
- case V4L2_MBUS_FMT_YUYV8_1X16:
+ case MEDIA_BUS_FMT_YUYV8_1X16:
/* BT.601/BT.1358 16bit YCbCr422 */
vnmc |= VNMC_INF_YUV16;
break;
- case V4L2_MBUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_YUYV8_2X8:
/* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */
vnmc |= priv->pdata_flags & RCAR_VIN_BT656 ?
VNMC_INF_YUV8_BT656 : VNMC_INF_YUV8_BT601;
break;
- case V4L2_MBUS_FMT_YUYV10_2X10:
+ case MEDIA_BUS_FMT_YUYV10_2X10:
/* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */
vnmc |= priv->pdata_flags & RCAR_VIN_BT656 ?
VNMC_INF_YUV10_BT656 : VNMC_INF_YUV10_BT601;
@@ -332,7 +678,7 @@
vnmc |= VNMC_BPS;
/* progressive or interlaced mode */
- interrupts = progressive ? VNIE_FIE | VNIE_EFE : VNIE_EFE;
+ interrupts = progressive ? VNIE_FIE : VNIE_EFE;
/* ack interrupts */
iowrite32(interrupts, priv->base + VNINTS_REG);
@@ -667,6 +1013,60 @@
/* VIN does not have "mclk" */
}
+static void set_coeff(struct rcar_vin_priv *priv, unsigned short xs)
+{
+ int i;
+ const struct vin_coeff *p_prev_set = NULL;
+ const struct vin_coeff *p_set = NULL;
+
+ /* Look for suitable coefficient values */
+ for (i = 0; i < ARRAY_SIZE(vin_coeff_set); i++) {
+ p_prev_set = p_set;
+ p_set = &vin_coeff_set[i];
+
+ if (xs < p_set->xs_value)
+ break;
+ }
+
+ /* Use previous value if its XS value is closer */
+ if (p_prev_set && p_set &&
+ xs - p_prev_set->xs_value < p_set->xs_value - xs)
+ p_set = p_prev_set;
+
+ /* Set coefficient registers */
+ iowrite32(p_set->coeff_set[0], priv->base + VNC1A_REG);
+ iowrite32(p_set->coeff_set[1], priv->base + VNC1B_REG);
+ iowrite32(p_set->coeff_set[2], priv->base + VNC1C_REG);
+
+ iowrite32(p_set->coeff_set[3], priv->base + VNC2A_REG);
+ iowrite32(p_set->coeff_set[4], priv->base + VNC2B_REG);
+ iowrite32(p_set->coeff_set[5], priv->base + VNC2C_REG);
+
+ iowrite32(p_set->coeff_set[6], priv->base + VNC3A_REG);
+ iowrite32(p_set->coeff_set[7], priv->base + VNC3B_REG);
+ iowrite32(p_set->coeff_set[8], priv->base + VNC3C_REG);
+
+ iowrite32(p_set->coeff_set[9], priv->base + VNC4A_REG);
+ iowrite32(p_set->coeff_set[10], priv->base + VNC4B_REG);
+ iowrite32(p_set->coeff_set[11], priv->base + VNC4C_REG);
+
+ iowrite32(p_set->coeff_set[12], priv->base + VNC5A_REG);
+ iowrite32(p_set->coeff_set[13], priv->base + VNC5B_REG);
+ iowrite32(p_set->coeff_set[14], priv->base + VNC5C_REG);
+
+ iowrite32(p_set->coeff_set[15], priv->base + VNC6A_REG);
+ iowrite32(p_set->coeff_set[16], priv->base + VNC6B_REG);
+ iowrite32(p_set->coeff_set[17], priv->base + VNC6C_REG);
+
+ iowrite32(p_set->coeff_set[18], priv->base + VNC7A_REG);
+ iowrite32(p_set->coeff_set[19], priv->base + VNC7B_REG);
+ iowrite32(p_set->coeff_set[20], priv->base + VNC7C_REG);
+
+ iowrite32(p_set->coeff_set[21], priv->base + VNC8A_REG);
+ iowrite32(p_set->coeff_set[22], priv->base + VNC8B_REG);
+ iowrite32(p_set->coeff_set[23], priv->base + VNC8C_REG);
+}
+
/* rect is guaranteed to not exceed the scaled camera rectangle */
static int rcar_vin_set_rect(struct soc_camera_device *icd)
{
@@ -676,6 +1076,7 @@
unsigned int left_offset, top_offset;
unsigned char dsize = 0;
struct v4l2_rect *cam_subrect = &cam->subrect;
+ u32 value;
dev_dbg(icd->parent, "Crop %ux%u@%u:%u\n",
icd->user_width, icd->user_height, cam->vin_left, cam->vin_top);
@@ -695,40 +1096,64 @@
/* Set Start/End Pixel/Line Pre-Clip */
iowrite32(left_offset << dsize, priv->base + VNSPPRC_REG);
- iowrite32((left_offset + cam->width - 1) << dsize,
+ iowrite32((left_offset + cam_subrect->width - 1) << dsize,
priv->base + VNEPPRC_REG);
switch (priv->field) {
case V4L2_FIELD_INTERLACED:
case V4L2_FIELD_INTERLACED_TB:
case V4L2_FIELD_INTERLACED_BT:
iowrite32(top_offset / 2, priv->base + VNSLPRC_REG);
- iowrite32((top_offset + cam->height) / 2 - 1,
+ iowrite32((top_offset + cam_subrect->height) / 2 - 1,
priv->base + VNELPRC_REG);
break;
default:
iowrite32(top_offset, priv->base + VNSLPRC_REG);
- iowrite32(top_offset + cam->height - 1,
+ iowrite32(top_offset + cam_subrect->height - 1,
priv->base + VNELPRC_REG);
break;
}
+ /* Set scaling coefficient */
+ value = 0;
+ if (cam_subrect->height != cam->out_height)
+ value = (4096 * cam_subrect->height) / cam->out_height;
+ dev_dbg(icd->parent, "YS Value: %x\n", value);
+ iowrite32(value, priv->base + VNYS_REG);
+
+ value = 0;
+ if (cam_subrect->width != cam->out_width)
+ value = (4096 * cam_subrect->width) / cam->out_width;
+
+ /* Horizontal upscaling is up to double size */
+ if (0 < value && value < 2048)
+ value = 2048;
+
+ dev_dbg(icd->parent, "XS Value: %x\n", value);
+ iowrite32(value, priv->base + VNXS_REG);
+
+ /* Horizontal upscaling is carried out by scaling down from double size */
+ if (value < 4096)
+ value *= 2;
+
+ set_coeff(priv, value);
+
/* Set Start/End Pixel/Line Post-Clip */
iowrite32(0, priv->base + VNSPPOC_REG);
iowrite32(0, priv->base + VNSLPOC_REG);
- iowrite32((cam_subrect->width - 1) << dsize, priv->base + VNEPPOC_REG);
+ iowrite32((cam->out_width - 1) << dsize, priv->base + VNEPPOC_REG);
switch (priv->field) {
case V4L2_FIELD_INTERLACED:
case V4L2_FIELD_INTERLACED_TB:
case V4L2_FIELD_INTERLACED_BT:
- iowrite32(cam_subrect->height / 2 - 1,
+ iowrite32(cam->out_height / 2 - 1,
priv->base + VNELPOC_REG);
break;
default:
- iowrite32(cam_subrect->height - 1, priv->base + VNELPOC_REG);
+ iowrite32(cam->out_height - 1, priv->base + VNELPOC_REG);
break;
}
- iowrite32(ALIGN(cam->width, 0x10), priv->base + VNIS_REG);
+ iowrite32(ALIGN(cam->out_width, 0x10), priv->base + VNIS_REG);
return 0;
}
@@ -819,7 +1244,7 @@
if (ret < 0 && ret != -ENOIOCTLCMD)
return ret;
- val = priv->field == V4L2_FIELD_NONE ? VNDMR2_FTEV : 0;
+ val = VNDMR2_FTEV | VNDMR2_VLV(1);
if (!(common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW))
val |= VNDMR2_VPS;
if (!(common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW))
@@ -880,6 +1305,14 @@
.layout = SOC_MBUS_LAYOUT_PLANAR_Y_C,
},
{
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .name = "YUYV",
+ .bits_per_sample = 16,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+ {
.fourcc = V4L2_PIX_FMT_UYVY,
.name = "UYVY",
.bits_per_sample = 16,
@@ -921,7 +1354,7 @@
int ret, k, n;
int formats = 0;
struct rcar_vin_cam *cam;
- enum v4l2_mbus_pixelcode code;
+ u32 code;
const struct soc_mbus_pixelfmt *fmt;
ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
@@ -999,6 +1432,8 @@
cam->subrect = rect;
cam->width = mf.width;
cam->height = mf.height;
+ cam->out_width = mf.width;
+ cam->out_height = mf.height;
icd->host_priv = cam;
} else {
@@ -1010,9 +1445,9 @@
cam->extra_fmt = NULL;
switch (code) {
- case V4L2_MBUS_FMT_YUYV8_1X16:
- case V4L2_MBUS_FMT_YUYV8_2X8:
- case V4L2_MBUS_FMT_YUYV10_2X10:
+ case MEDIA_BUS_FMT_YUYV8_1X16:
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_YUYV10_2X10:
if (cam->extra_fmt)
break;
@@ -1259,6 +1694,9 @@
dev_dbg(dev, "W: %u : %u, H: %u : %u\n",
vin_sub_width, pix->width, vin_sub_height, pix->height);
+ cam->out_width = pix->width;
+ cam->out_height = pix->height;
+
icd->current_fmt = xlate;
priv->field = field;
@@ -1310,8 +1748,12 @@
if (ret < 0)
return ret;
- pix->width = mf.width;
- pix->height = mf.height;
+ /* Adjust only if VIN cannot scale */
+ if (pix->width > mf.width * 2)
+ pix->width = mf.width * 2;
+ if (pix->height > mf.height * 3)
+ pix->height = mf.height * 3;
+
pix->field = mf.field;
pix->colorspace = mf.colorspace;
@@ -1395,6 +1837,8 @@
#ifdef CONFIG_OF
static struct of_device_id rcar_vin_of_table[] = {
+ { .compatible = "renesas,vin-r8a7794", .data = (void *)RCAR_GEN2 },
+ { .compatible = "renesas,vin-r8a7793", .data = (void *)RCAR_GEN2 },
{ .compatible = "renesas,vin-r8a7791", .data = (void *)RCAR_GEN2 },
{ .compatible = "renesas,vin-r8a7790", .data = (void *)RCAR_GEN2 },
{ .compatible = "renesas,vin-r8a7779", .data = (void *)RCAR_H1 },
@@ -1543,7 +1987,6 @@
.remove = rcar_vin_remove,
.driver = {
.name = DRV_NAME,
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(rcar_vin_of_table),
},
.id_table = rcar_vin_id_table,
diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
index 20ad4a5..8b27b3e 100644
--- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
+++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
@@ -149,7 +149,7 @@
/* Camera cropping rectangle */
struct v4l2_rect rect;
const struct soc_mbus_pixelfmt *extra_fmt;
- enum v4l2_mbus_pixelcode code;
+ u32 code;
};
static struct sh_mobile_ceu_buffer *to_ceu_vb(struct vb2_buffer *vb)
@@ -861,16 +861,16 @@
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
switch (cam->code) {
- case V4L2_MBUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_UYVY8_2X8:
value = 0x00000000; /* Cb0, Y0, Cr0, Y1 */
break;
- case V4L2_MBUS_FMT_VYUY8_2X8:
+ case MEDIA_BUS_FMT_VYUY8_2X8:
value = 0x00000100; /* Cr0, Y0, Cb0, Y1 */
break;
- case V4L2_MBUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_YUYV8_2X8:
value = 0x00000200; /* Y0, Cb0, Y1, Cr0 */
break;
- case V4L2_MBUS_FMT_YVYU8_2X8:
+ case MEDIA_BUS_FMT_YVYU8_2X8:
value = 0x00000300; /* Y0, Cr0, Y1, Cb0 */
break;
default:
@@ -1048,7 +1048,7 @@
int ret, k, n;
int formats = 0;
struct sh_mobile_ceu_cam *cam;
- enum v4l2_mbus_pixelcode code;
+ u32 code;
const struct soc_mbus_pixelfmt *fmt;
ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
@@ -1141,10 +1141,10 @@
cam->extra_fmt = NULL;
switch (code) {
- case V4L2_MBUS_FMT_UYVY8_2X8:
- case V4L2_MBUS_FMT_VYUY8_2X8:
- case V4L2_MBUS_FMT_YUYV8_2X8:
- case V4L2_MBUS_FMT_YVYU8_2X8:
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_VYUY8_2X8:
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_YVYU8_2X8:
if (cam->extra_fmt)
break;
@@ -2009,7 +2009,6 @@
static struct platform_driver sh_mobile_ceu_driver = {
.driver = {
.name = "sh_mobile_ceu",
- .owner = THIS_MODULE,
.pm = &sh_mobile_ceu_dev_pm_ops,
.of_match_table = sh_mobile_ceu_of_match,
},
diff --git a/drivers/media/platform/soc_camera/sh_mobile_csi2.c b/drivers/media/platform/soc_camera/sh_mobile_csi2.c
index 05dd21a..c4e7aa0 100644
--- a/drivers/media/platform/soc_camera/sh_mobile_csi2.c
+++ b/drivers/media/platform/soc_camera/sh_mobile_csi2.c
@@ -59,28 +59,28 @@
switch (pdata->type) {
case SH_CSI2C:
switch (mf->code) {
- case V4L2_MBUS_FMT_UYVY8_2X8: /* YUV422 */
- case V4L2_MBUS_FMT_YUYV8_1_5X8: /* YUV420 */
- case V4L2_MBUS_FMT_Y8_1X8: /* RAW8 */
- case V4L2_MBUS_FMT_SBGGR8_1X8:
- case V4L2_MBUS_FMT_SGRBG8_1X8:
+ case MEDIA_BUS_FMT_UYVY8_2X8: /* YUV422 */
+ case MEDIA_BUS_FMT_YUYV8_1_5X8: /* YUV420 */
+ case MEDIA_BUS_FMT_Y8_1X8: /* RAW8 */
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ case MEDIA_BUS_FMT_SGRBG8_1X8:
break;
default:
/* All MIPI CSI-2 devices must support one of primary formats */
- mf->code = V4L2_MBUS_FMT_YUYV8_2X8;
+ mf->code = MEDIA_BUS_FMT_YUYV8_2X8;
}
break;
case SH_CSI2I:
switch (mf->code) {
- case V4L2_MBUS_FMT_Y8_1X8: /* RAW8 */
- case V4L2_MBUS_FMT_SBGGR8_1X8:
- case V4L2_MBUS_FMT_SGRBG8_1X8:
- case V4L2_MBUS_FMT_SBGGR10_1X10: /* RAW10 */
- case V4L2_MBUS_FMT_SBGGR12_1X12: /* RAW12 */
+ case MEDIA_BUS_FMT_Y8_1X8: /* RAW8 */
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ case MEDIA_BUS_FMT_SGRBG8_1X8:
+ case MEDIA_BUS_FMT_SBGGR10_1X10: /* RAW10 */
+ case MEDIA_BUS_FMT_SBGGR12_1X12: /* RAW12 */
break;
default:
/* All MIPI CSI-2 devices must support one of primary formats */
- mf->code = V4L2_MBUS_FMT_SBGGR8_1X8;
+ mf->code = MEDIA_BUS_FMT_SBGGR8_1X8;
}
break;
}
@@ -104,21 +104,21 @@
return -EINVAL;
switch (mf->code) {
- case V4L2_MBUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_UYVY8_2X8:
tmp |= 0x1e; /* YUV422 8 bit */
break;
- case V4L2_MBUS_FMT_YUYV8_1_5X8:
+ case MEDIA_BUS_FMT_YUYV8_1_5X8:
tmp |= 0x18; /* YUV420 8 bit */
break;
- case V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE:
+ case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE:
tmp |= 0x21; /* RGB555 */
break;
- case V4L2_MBUS_FMT_RGB565_2X8_BE:
+ case MEDIA_BUS_FMT_RGB565_2X8_BE:
tmp |= 0x22; /* RGB565 */
break;
- case V4L2_MBUS_FMT_Y8_1X8:
- case V4L2_MBUS_FMT_SBGGR8_1X8:
- case V4L2_MBUS_FMT_SGRBG8_1X8:
+ case MEDIA_BUS_FMT_Y8_1X8:
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ case MEDIA_BUS_FMT_SGRBG8_1X8:
tmp |= 0x2a; /* RAW8 */
break;
default:
@@ -391,7 +391,6 @@
.probe = sh_csi2_probe,
.driver = {
.name = "sh-mobile-csi2",
- .owner = THIS_MODULE,
},
};
diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c
index 8e61b97..b3db51c 100644
--- a/drivers/media/platform/soc_camera/soc_camera.c
+++ b/drivers/media/platform/soc_camera/soc_camera.c
@@ -460,7 +460,7 @@
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
unsigned int i, fmts = 0, raw_fmts = 0;
int ret;
- enum v4l2_mbus_pixelcode code;
+ u32 code;
while (!v4l2_subdev_call(sd, video, enum_mbus_fmt, raw_fmts, &code))
raw_fmts++;
@@ -2236,7 +2236,6 @@
.remove = soc_camera_pdrv_remove,
.driver = {
.name = "soc-camera-pdrv",
- .owner = THIS_MODULE,
},
};
diff --git a/drivers/media/platform/soc_camera/soc_camera_platform.c b/drivers/media/platform/soc_camera/soc_camera_platform.c
index ceaddfb..f535910 100644
--- a/drivers/media/platform/soc_camera/soc_camera_platform.c
+++ b/drivers/media/platform/soc_camera/soc_camera_platform.c
@@ -62,7 +62,7 @@
};
static int soc_camera_platform_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
+ u32 *code)
{
struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
@@ -180,7 +180,6 @@
static struct platform_driver soc_camera_platform_driver = {
.driver = {
.name = "soc_camera_platform",
- .owner = THIS_MODULE,
},
.probe = soc_camera_platform_probe,
.remove = soc_camera_platform_remove,
diff --git a/drivers/media/platform/soc_camera/soc_mediabus.c b/drivers/media/platform/soc_camera/soc_mediabus.c
index dc02dec..1dbcd42 100644
--- a/drivers/media/platform/soc_camera/soc_mediabus.c
+++ b/drivers/media/platform/soc_camera/soc_mediabus.c
@@ -17,7 +17,7 @@
static const struct soc_mbus_lookup mbus_fmt[] = {
{
- .code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .code = MEDIA_BUS_FMT_YUYV8_2X8,
.fmt = {
.fourcc = V4L2_PIX_FMT_YUYV,
.name = "YUYV",
@@ -27,7 +27,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_YVYU8_2X8,
+ .code = MEDIA_BUS_FMT_YVYU8_2X8,
.fmt = {
.fourcc = V4L2_PIX_FMT_YVYU,
.name = "YVYU",
@@ -37,7 +37,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
.fmt = {
.fourcc = V4L2_PIX_FMT_UYVY,
.name = "UYVY",
@@ -47,7 +47,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_VYUY8_2X8,
+ .code = MEDIA_BUS_FMT_VYUY8_2X8,
.fmt = {
.fourcc = V4L2_PIX_FMT_VYUY,
.name = "VYUY",
@@ -57,7 +57,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE,
+ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
.fmt = {
.fourcc = V4L2_PIX_FMT_RGB555,
.name = "RGB555",
@@ -67,7 +67,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE,
+ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE,
.fmt = {
.fourcc = V4L2_PIX_FMT_RGB555X,
.name = "RGB555X",
@@ -77,7 +77,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_RGB565_2X8_LE,
+ .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
.fmt = {
.fourcc = V4L2_PIX_FMT_RGB565,
.name = "RGB565",
@@ -87,7 +87,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_RGB565_2X8_BE,
+ .code = MEDIA_BUS_FMT_RGB565_2X8_BE,
.fmt = {
.fourcc = V4L2_PIX_FMT_RGB565X,
.name = "RGB565X",
@@ -97,7 +97,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_RGB666_1X18,
+ .code = MEDIA_BUS_FMT_RGB666_1X18,
.fmt = {
.fourcc = V4L2_PIX_FMT_RGB32,
.name = "RGB666/32bpp",
@@ -106,7 +106,7 @@
.order = SOC_MBUS_ORDER_LE,
},
}, {
- .code = V4L2_MBUS_FMT_RGB888_1X24,
+ .code = MEDIA_BUS_FMT_RGB888_1X24,
.fmt = {
.fourcc = V4L2_PIX_FMT_RGB32,
.name = "RGB888/32bpp",
@@ -115,7 +115,7 @@
.order = SOC_MBUS_ORDER_LE,
},
}, {
- .code = V4L2_MBUS_FMT_RGB888_2X12_BE,
+ .code = MEDIA_BUS_FMT_RGB888_2X12_BE,
.fmt = {
.fourcc = V4L2_PIX_FMT_RGB32,
.name = "RGB888/32bpp",
@@ -124,7 +124,7 @@
.order = SOC_MBUS_ORDER_BE,
},
}, {
- .code = V4L2_MBUS_FMT_RGB888_2X12_LE,
+ .code = MEDIA_BUS_FMT_RGB888_2X12_LE,
.fmt = {
.fourcc = V4L2_PIX_FMT_RGB32,
.name = "RGB888/32bpp",
@@ -133,7 +133,7 @@
.order = SOC_MBUS_ORDER_LE,
},
}, {
- .code = V4L2_MBUS_FMT_SBGGR8_1X8,
+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
.fmt = {
.fourcc = V4L2_PIX_FMT_SBGGR8,
.name = "Bayer 8 BGGR",
@@ -143,7 +143,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_SBGGR10_1X10,
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
.fmt = {
.fourcc = V4L2_PIX_FMT_SBGGR10,
.name = "Bayer 10 BGGR",
@@ -153,7 +153,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_Y8_1X8,
+ .code = MEDIA_BUS_FMT_Y8_1X8,
.fmt = {
.fourcc = V4L2_PIX_FMT_GREY,
.name = "Grey",
@@ -163,7 +163,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_Y10_1X10,
+ .code = MEDIA_BUS_FMT_Y10_1X10,
.fmt = {
.fourcc = V4L2_PIX_FMT_Y10,
.name = "Grey 10bit",
@@ -173,7 +173,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE,
+ .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE,
.fmt = {
.fourcc = V4L2_PIX_FMT_SBGGR10,
.name = "Bayer 10 BGGR",
@@ -183,7 +183,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE,
+ .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE,
.fmt = {
.fourcc = V4L2_PIX_FMT_SBGGR10,
.name = "Bayer 10 BGGR",
@@ -193,7 +193,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE,
+ .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE,
.fmt = {
.fourcc = V4L2_PIX_FMT_SBGGR10,
.name = "Bayer 10 BGGR",
@@ -203,7 +203,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE,
+ .code = MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE,
.fmt = {
.fourcc = V4L2_PIX_FMT_SBGGR10,
.name = "Bayer 10 BGGR",
@@ -213,7 +213,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_JPEG_1X8,
+ .code = MEDIA_BUS_FMT_JPEG_1X8,
.fmt = {
.fourcc = V4L2_PIX_FMT_JPEG,
.name = "JPEG",
@@ -223,7 +223,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_BE,
+ .code = MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE,
.fmt = {
.fourcc = V4L2_PIX_FMT_RGB444,
.name = "RGB444",
@@ -233,7 +233,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_YUYV8_1_5X8,
+ .code = MEDIA_BUS_FMT_YUYV8_1_5X8,
.fmt = {
.fourcc = V4L2_PIX_FMT_YUV420,
.name = "YUYV 4:2:0",
@@ -243,7 +243,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_YVYU8_1_5X8,
+ .code = MEDIA_BUS_FMT_YVYU8_1_5X8,
.fmt = {
.fourcc = V4L2_PIX_FMT_YVU420,
.name = "YVYU 4:2:0",
@@ -253,7 +253,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_UYVY8_1X16,
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
.fmt = {
.fourcc = V4L2_PIX_FMT_UYVY,
.name = "UYVY 16bit",
@@ -263,7 +263,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_VYUY8_1X16,
+ .code = MEDIA_BUS_FMT_VYUY8_1X16,
.fmt = {
.fourcc = V4L2_PIX_FMT_VYUY,
.name = "VYUY 16bit",
@@ -273,7 +273,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_YUYV8_1X16,
+ .code = MEDIA_BUS_FMT_YUYV8_1X16,
.fmt = {
.fourcc = V4L2_PIX_FMT_YUYV,
.name = "YUYV 16bit",
@@ -283,7 +283,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_YVYU8_1X16,
+ .code = MEDIA_BUS_FMT_YVYU8_1X16,
.fmt = {
.fourcc = V4L2_PIX_FMT_YVYU,
.name = "YVYU 16bit",
@@ -293,7 +293,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_SGRBG8_1X8,
+ .code = MEDIA_BUS_FMT_SGRBG8_1X8,
.fmt = {
.fourcc = V4L2_PIX_FMT_SGRBG8,
.name = "Bayer 8 GRBG",
@@ -303,7 +303,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
+ .code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8,
.fmt = {
.fourcc = V4L2_PIX_FMT_SGRBG10DPCM8,
.name = "Bayer 10 BGGR DPCM 8",
@@ -313,7 +313,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_SGBRG10_1X10,
+ .code = MEDIA_BUS_FMT_SGBRG10_1X10,
.fmt = {
.fourcc = V4L2_PIX_FMT_SGBRG10,
.name = "Bayer 10 GBRG",
@@ -323,7 +323,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_SGRBG10_1X10,
+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
.fmt = {
.fourcc = V4L2_PIX_FMT_SGRBG10,
.name = "Bayer 10 GRBG",
@@ -333,7 +333,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_SRGGB10_1X10,
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
.fmt = {
.fourcc = V4L2_PIX_FMT_SRGGB10,
.name = "Bayer 10 RGGB",
@@ -343,7 +343,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_SBGGR12_1X12,
+ .code = MEDIA_BUS_FMT_SBGGR12_1X12,
.fmt = {
.fourcc = V4L2_PIX_FMT_SBGGR12,
.name = "Bayer 12 BGGR",
@@ -353,7 +353,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_SGBRG12_1X12,
+ .code = MEDIA_BUS_FMT_SGBRG12_1X12,
.fmt = {
.fourcc = V4L2_PIX_FMT_SGBRG12,
.name = "Bayer 12 GBRG",
@@ -363,7 +363,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_SGRBG12_1X12,
+ .code = MEDIA_BUS_FMT_SGRBG12_1X12,
.fmt = {
.fourcc = V4L2_PIX_FMT_SGRBG12,
.name = "Bayer 12 GRBG",
@@ -373,7 +373,7 @@
.layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
- .code = V4L2_MBUS_FMT_SRGGB12_1X12,
+ .code = MEDIA_BUS_FMT_SRGGB12_1X12,
.fmt = {
.fourcc = V4L2_PIX_FMT_SRGGB12,
.name = "Bayer 12 RGGB",
@@ -458,7 +458,7 @@
EXPORT_SYMBOL(soc_mbus_image_size);
const struct soc_mbus_pixelfmt *soc_mbus_find_fmtdesc(
- enum v4l2_mbus_pixelcode code,
+ u32 code,
const struct soc_mbus_lookup *lookup,
int n)
{
@@ -473,7 +473,7 @@
EXPORT_SYMBOL(soc_mbus_find_fmtdesc);
const struct soc_mbus_pixelfmt *soc_mbus_get_fmtdesc(
- enum v4l2_mbus_pixelcode code)
+ u32 code)
{
return soc_mbus_find_fmtdesc(code, mbus_fmt, ARRAY_SIZE(mbus_fmt));
}
diff --git a/drivers/media/platform/ti-vpe/csc.c b/drivers/media/platform/ti-vpe/csc.c
index 940df40..bec6749 100644
--- a/drivers/media/platform/ti-vpe/csc.c
+++ b/drivers/media/platform/ti-vpe/csc.c
@@ -93,12 +93,8 @@
{
struct device *dev = &csc->pdev->dev;
- u32 read_reg(struct csc_data *csc, int offset)
- {
- return ioread32(csc->base + offset);
- }
-
-#define DUMPREG(r) dev_dbg(dev, "%-35s %08x\n", #r, read_reg(csc, CSC_##r))
+#define DUMPREG(r) dev_dbg(dev, "%-35s %08x\n", #r, \
+ ioread32(csc->base + CSC_##r))
DUMPREG(CSC00);
DUMPREG(CSC01);
@@ -189,7 +185,7 @@
csc->base = devm_ioremap_resource(&pdev->dev, csc->res);
if (IS_ERR(csc->base)) {
dev_err(&pdev->dev, "failed to ioremap\n");
- return csc->base;
+ return ERR_CAST(csc->base);
}
return csc;
diff --git a/drivers/media/platform/ti-vpe/sc.c b/drivers/media/platform/ti-vpe/sc.c
index 6314171..f82d1c7 100644
--- a/drivers/media/platform/ti-vpe/sc.c
+++ b/drivers/media/platform/ti-vpe/sc.c
@@ -24,12 +24,8 @@
{
struct device *dev = &sc->pdev->dev;
- u32 read_reg(struct sc_data *sc, int offset)
- {
- return ioread32(sc->base + offset);
- }
-
-#define DUMPREG(r) dev_dbg(dev, "%-35s %08x\n", #r, read_reg(sc, CFG_##r))
+#define DUMPREG(r) dev_dbg(dev, "%-35s %08x\n", #r, \
+ ioread32(sc->base + CFG_##r))
DUMPREG(SC0);
DUMPREG(SC1);
@@ -304,7 +300,7 @@
sc->base = devm_ioremap_resource(&pdev->dev, sc->res);
if (IS_ERR(sc->base)) {
dev_err(&pdev->dev, "failed to ioremap\n");
- return sc->base;
+ return ERR_CAST(sc->base);
}
return sc;
diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c
index 9a081c2..d628d1a 100644
--- a/drivers/media/platform/ti-vpe/vpe.c
+++ b/drivers/media/platform/ti-vpe/vpe.c
@@ -2376,7 +2376,6 @@
.remove = vpe_remove,
.driver = {
.name = VPE_MODULE_NAME,
- .owner = THIS_MODULE,
.of_match_table = vpe_of_match,
},
};
diff --git a/drivers/media/platform/timblogiw.c b/drivers/media/platform/timblogiw.c
index 3cb2f35..5820e45 100644
--- a/drivers/media/platform/timblogiw.c
+++ b/drivers/media/platform/timblogiw.c
@@ -857,7 +857,6 @@
static struct platform_driver timblogiw_platform_driver = {
.driver = {
.name = DRIVER_NAME,
- .owner = THIS_MODULE,
},
.probe = timblogiw_probe,
.remove = timblogiw_remove,
diff --git a/drivers/media/platform/via-camera.c b/drivers/media/platform/via-camera.c
index ae6870c..86989d8 100644
--- a/drivers/media/platform/via-camera.c
+++ b/drivers/media/platform/via-camera.c
@@ -101,7 +101,7 @@
*/
struct v4l2_pix_format sensor_format;
struct v4l2_pix_format user_format;
- enum v4l2_mbus_pixelcode mbus_code;
+ u32 mbus_code;
};
/*
@@ -143,12 +143,12 @@
__u8 *desc;
__u32 pixelformat;
int bpp; /* Bytes per pixel */
- enum v4l2_mbus_pixelcode mbus_code;
+ u32 mbus_code;
} via_formats[] = {
{
.desc = "YUYV 4:2:2",
.pixelformat = V4L2_PIX_FMT_YUYV,
- .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
.bpp = 2,
},
/* RGB444 and Bayer should be doable, but have never been
@@ -849,7 +849,7 @@
.sizeimage = VGA_WIDTH * VGA_HEIGHT * 2,
};
-static const enum v4l2_mbus_pixelcode via_def_mbus_code = V4L2_MBUS_FMT_YUYV8_2X8;
+static const u32 via_def_mbus_code = MEDIA_BUS_FMT_YUYV8_2X8;
static int viacam_enum_fmt_vid_cap(struct file *filp, void *priv,
struct v4l2_fmtdesc *fmt)
@@ -985,9 +985,9 @@
{
strcpy(cap->driver, "via-camera");
strcpy(cap->card, "via-camera");
- cap->version = 1;
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
diff --git a/drivers/media/platform/mem2mem_testdev.c b/drivers/media/platform/vim2m.c
similarity index 81%
rename from drivers/media/platform/mem2mem_testdev.c
rename to drivers/media/platform/vim2m.c
index c1b03cf..a3b6383 100644
--- a/drivers/media/platform/mem2mem_testdev.c
+++ b/drivers/media/platform/vim2m.c
@@ -31,12 +31,11 @@
#include <media/v4l2-event.h>
#include <media/videobuf2-vmalloc.h>
-#define MEM2MEM_TEST_MODULE_NAME "mem2mem-testdev"
-
MODULE_DESCRIPTION("Virtual device for mem2mem framework testing");
MODULE_AUTHOR("Pawel Osciak, <pawel@osciak.com>");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.1.1");
+MODULE_ALIAS("mem2mem_testdev");
static unsigned debug;
module_param(debug, uint, 0644);
@@ -52,7 +51,7 @@
#define MEM2MEM_CAPTURE (1 << 0)
#define MEM2MEM_OUTPUT (1 << 1)
-#define MEM2MEM_NAME "m2m-testdev"
+#define MEM2MEM_NAME "vim2m"
/* Per queue */
#define MEM2MEM_DEF_NUM_BUFS VIDEO_MAX_FRAME
@@ -72,15 +71,15 @@
v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg)
-static void m2mtest_dev_release(struct device *dev)
+static void vim2m_dev_release(struct device *dev)
{}
-static struct platform_device m2mtest_pdev = {
+static struct platform_device vim2m_pdev = {
.name = MEM2MEM_NAME,
- .dev.release = m2mtest_dev_release,
+ .dev.release = vim2m_dev_release,
};
-struct m2mtest_fmt {
+struct vim2m_fmt {
char *name;
u32 fourcc;
int depth;
@@ -88,7 +87,7 @@
u32 types;
};
-static struct m2mtest_fmt formats[] = {
+static struct vim2m_fmt formats[] = {
{
.name = "RGB565 (BE)",
.fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
@@ -108,12 +107,12 @@
#define NUM_FORMATS ARRAY_SIZE(formats)
/* Per-queue, driver-specific private data */
-struct m2mtest_q_data {
+struct vim2m_q_data {
unsigned int width;
unsigned int height;
unsigned int sizeimage;
unsigned int sequence;
- struct m2mtest_fmt *fmt;
+ struct vim2m_fmt *fmt;
};
enum {
@@ -124,9 +123,9 @@
#define V4L2_CID_TRANS_TIME_MSEC (V4L2_CID_USER_BASE + 0x1000)
#define V4L2_CID_TRANS_NUM_BUFS (V4L2_CID_USER_BASE + 0x1001)
-static struct m2mtest_fmt *find_format(struct v4l2_format *f)
+static struct vim2m_fmt *find_format(struct v4l2_format *f)
{
- struct m2mtest_fmt *fmt;
+ struct vim2m_fmt *fmt;
unsigned int k;
for (k = 0; k < NUM_FORMATS; k++) {
@@ -141,7 +140,7 @@
return &formats[k];
}
-struct m2mtest_dev {
+struct vim2m_dev {
struct v4l2_device v4l2_dev;
struct video_device *vfd;
@@ -154,9 +153,9 @@
struct v4l2_m2m_dev *m2m_dev;
};
-struct m2mtest_ctx {
+struct vim2m_ctx {
struct v4l2_fh fh;
- struct m2mtest_dev *dev;
+ struct vim2m_dev *dev;
struct v4l2_ctrl_handler hdl;
@@ -177,15 +176,15 @@
enum v4l2_colorspace colorspace;
/* Source and destination queue data */
- struct m2mtest_q_data q_data[2];
+ struct vim2m_q_data q_data[2];
};
-static inline struct m2mtest_ctx *file2ctx(struct file *file)
+static inline struct vim2m_ctx *file2ctx(struct file *file)
{
- return container_of(file->private_data, struct m2mtest_ctx, fh);
+ return container_of(file->private_data, struct vim2m_ctx, fh);
}
-static struct m2mtest_q_data *get_q_data(struct m2mtest_ctx *ctx,
+static struct vim2m_q_data *get_q_data(struct vim2m_ctx *ctx,
enum v4l2_buf_type type)
{
switch (type) {
@@ -200,12 +199,12 @@
}
-static int device_process(struct m2mtest_ctx *ctx,
+static int device_process(struct vim2m_ctx *ctx,
struct vb2_buffer *in_vb,
struct vb2_buffer *out_vb)
{
- struct m2mtest_dev *dev = ctx->dev;
- struct m2mtest_q_data *q_data;
+ struct vim2m_dev *dev = ctx->dev;
+ struct vim2m_q_data *q_data;
u8 *p_in, *p_out;
int x, y, t, w;
int tile_w, bytes_left;
@@ -334,7 +333,7 @@
return 0;
}
-static void schedule_irq(struct m2mtest_dev *dev, int msec_timeout)
+static void schedule_irq(struct vim2m_dev *dev, int msec_timeout)
{
dprintk(dev, "Scheduling a simulated irq\n");
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(msec_timeout));
@@ -349,7 +348,7 @@
*/
static int job_ready(void *priv)
{
- struct m2mtest_ctx *ctx = priv;
+ struct vim2m_ctx *ctx = priv;
if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) < ctx->translen
|| v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) < ctx->translen) {
@@ -362,7 +361,7 @@
static void job_abort(void *priv)
{
- struct m2mtest_ctx *ctx = priv;
+ struct vim2m_ctx *ctx = priv;
/* Will cancel the transaction in the next interrupt handler */
ctx->aborting = 1;
@@ -376,8 +375,8 @@
*/
static void device_run(void *priv)
{
- struct m2mtest_ctx *ctx = priv;
- struct m2mtest_dev *dev = ctx->dev;
+ struct vim2m_ctx *ctx = priv;
+ struct vim2m_dev *dev = ctx->dev;
struct vb2_buffer *src_buf, *dst_buf;
src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
@@ -391,12 +390,12 @@
static void device_isr(unsigned long priv)
{
- struct m2mtest_dev *m2mtest_dev = (struct m2mtest_dev *)priv;
- struct m2mtest_ctx *curr_ctx;
+ struct vim2m_dev *vim2m_dev = (struct vim2m_dev *)priv;
+ struct vim2m_ctx *curr_ctx;
struct vb2_buffer *src_vb, *dst_vb;
unsigned long flags;
- curr_ctx = v4l2_m2m_get_curr_priv(m2mtest_dev->m2m_dev);
+ curr_ctx = v4l2_m2m_get_curr_priv(vim2m_dev->m2m_dev);
if (NULL == curr_ctx) {
pr_err("Instance released before the end of transaction\n");
@@ -408,16 +407,16 @@
curr_ctx->num_processed++;
- spin_lock_irqsave(&m2mtest_dev->irqlock, flags);
+ spin_lock_irqsave(&vim2m_dev->irqlock, flags);
v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE);
v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE);
- spin_unlock_irqrestore(&m2mtest_dev->irqlock, flags);
+ spin_unlock_irqrestore(&vim2m_dev->irqlock, flags);
if (curr_ctx->num_processed == curr_ctx->translen
|| curr_ctx->aborting) {
dprintk(curr_ctx->dev, "Finishing transaction\n");
curr_ctx->num_processed = 0;
- v4l2_m2m_job_finish(m2mtest_dev->m2m_dev, curr_ctx->fh.m2m_ctx);
+ v4l2_m2m_job_finish(vim2m_dev->m2m_dev, curr_ctx->fh.m2m_ctx);
} else {
device_run(curr_ctx);
}
@@ -441,7 +440,7 @@
static int enum_fmt(struct v4l2_fmtdesc *f, u32 type)
{
int i, num;
- struct m2mtest_fmt *fmt;
+ struct vim2m_fmt *fmt;
num = 0;
@@ -480,10 +479,10 @@
return enum_fmt(f, MEM2MEM_OUTPUT);
}
-static int vidioc_g_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f)
+static int vidioc_g_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f)
{
struct vb2_queue *vq;
- struct m2mtest_q_data *q_data;
+ struct vim2m_q_data *q_data;
vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
if (!vq)
@@ -514,7 +513,7 @@
return vidioc_g_fmt(file2ctx(file), f);
}
-static int vidioc_try_fmt(struct v4l2_format *f, struct m2mtest_fmt *fmt)
+static int vidioc_try_fmt(struct v4l2_format *f, struct vim2m_fmt *fmt)
{
/* V4L2 specification suggests the driver corrects the format struct
* if any of the dimensions is unsupported */
@@ -539,8 +538,8 @@
static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct m2mtest_fmt *fmt;
- struct m2mtest_ctx *ctx = file2ctx(file);
+ struct vim2m_fmt *fmt;
+ struct vim2m_ctx *ctx = file2ctx(file);
fmt = find_format(f);
if (!fmt) {
@@ -561,8 +560,8 @@
static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct m2mtest_fmt *fmt;
- struct m2mtest_ctx *ctx = file2ctx(file);
+ struct vim2m_fmt *fmt;
+ struct vim2m_ctx *ctx = file2ctx(file);
fmt = find_format(f);
if (!fmt) {
@@ -581,9 +580,9 @@
return vidioc_try_fmt(f, fmt);
}
-static int vidioc_s_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f)
+static int vidioc_s_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f)
{
- struct m2mtest_q_data *q_data;
+ struct vim2m_q_data *q_data;
struct vb2_queue *vq;
vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
@@ -627,7 +626,7 @@
static int vidioc_s_fmt_vid_out(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct m2mtest_ctx *ctx = file2ctx(file);
+ struct vim2m_ctx *ctx = file2ctx(file);
int ret;
ret = vidioc_try_fmt_vid_out(file, priv, f);
@@ -640,10 +639,10 @@
return ret;
}
-static int m2mtest_s_ctrl(struct v4l2_ctrl *ctrl)
+static int vim2m_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct m2mtest_ctx *ctx =
- container_of(ctrl->handler, struct m2mtest_ctx, hdl);
+ struct vim2m_ctx *ctx =
+ container_of(ctrl->handler, struct vim2m_ctx, hdl);
switch (ctrl->id) {
case V4L2_CID_HFLIP:
@@ -676,12 +675,12 @@
return 0;
}
-static const struct v4l2_ctrl_ops m2mtest_ctrl_ops = {
- .s_ctrl = m2mtest_s_ctrl,
+static const struct v4l2_ctrl_ops vim2m_ctrl_ops = {
+ .s_ctrl = vim2m_s_ctrl,
};
-static const struct v4l2_ioctl_ops m2mtest_ioctl_ops = {
+static const struct v4l2_ioctl_ops vim2m_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
@@ -698,6 +697,9 @@
.vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
.vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
.vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
+ .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
+#endif
.vidioc_streamon = v4l2_m2m_ioctl_streamon,
.vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
@@ -711,13 +713,13 @@
* Queue operations
*/
-static int m2mtest_queue_setup(struct vb2_queue *vq,
+static int vim2m_queue_setup(struct vb2_queue *vq,
const struct v4l2_format *fmt,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
- struct m2mtest_ctx *ctx = vb2_get_drv_priv(vq);
- struct m2mtest_q_data *q_data;
+ struct vim2m_ctx *ctx = vb2_get_drv_priv(vq);
+ struct vim2m_q_data *q_data;
unsigned int size, count = *nbuffers;
q_data = get_q_data(ctx, vq->type);
@@ -741,10 +743,10 @@
return 0;
}
-static int m2mtest_buf_prepare(struct vb2_buffer *vb)
+static int vim2m_buf_prepare(struct vb2_buffer *vb)
{
- struct m2mtest_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
- struct m2mtest_q_data *q_data;
+ struct vim2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct vim2m_q_data *q_data;
dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type);
@@ -770,25 +772,25 @@
return 0;
}
-static void m2mtest_buf_queue(struct vb2_buffer *vb)
+static void vim2m_buf_queue(struct vb2_buffer *vb)
{
- struct m2mtest_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct vim2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb);
}
-static int m2mtest_start_streaming(struct vb2_queue *q, unsigned count)
+static int vim2m_start_streaming(struct vb2_queue *q, unsigned count)
{
- struct m2mtest_ctx *ctx = vb2_get_drv_priv(q);
- struct m2mtest_q_data *q_data = get_q_data(ctx, q->type);
+ struct vim2m_ctx *ctx = vb2_get_drv_priv(q);
+ struct vim2m_q_data *q_data = get_q_data(ctx, q->type);
q_data->sequence = 0;
return 0;
}
-static void m2mtest_stop_streaming(struct vb2_queue *q)
+static void vim2m_stop_streaming(struct vb2_queue *q)
{
- struct m2mtest_ctx *ctx = vb2_get_drv_priv(q);
+ struct vim2m_ctx *ctx = vb2_get_drv_priv(q);
struct vb2_buffer *vb;
unsigned long flags;
@@ -805,26 +807,26 @@
}
}
-static struct vb2_ops m2mtest_qops = {
- .queue_setup = m2mtest_queue_setup,
- .buf_prepare = m2mtest_buf_prepare,
- .buf_queue = m2mtest_buf_queue,
- .start_streaming = m2mtest_start_streaming,
- .stop_streaming = m2mtest_stop_streaming,
+static struct vb2_ops vim2m_qops = {
+ .queue_setup = vim2m_queue_setup,
+ .buf_prepare = vim2m_buf_prepare,
+ .buf_queue = vim2m_buf_queue,
+ .start_streaming = vim2m_start_streaming,
+ .stop_streaming = vim2m_stop_streaming,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
};
static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
{
- struct m2mtest_ctx *ctx = priv;
+ struct vim2m_ctx *ctx = priv;
int ret;
src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
src_vq->drv_priv = ctx;
src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
- src_vq->ops = &m2mtest_qops;
+ src_vq->ops = &vim2m_qops;
src_vq->mem_ops = &vb2_vmalloc_memops;
src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
src_vq->lock = &ctx->dev->dev_mutex;
@@ -837,7 +839,7 @@
dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
dst_vq->drv_priv = ctx;
dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
- dst_vq->ops = &m2mtest_qops;
+ dst_vq->ops = &vim2m_qops;
dst_vq->mem_ops = &vb2_vmalloc_memops;
dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
dst_vq->lock = &ctx->dev->dev_mutex;
@@ -845,8 +847,8 @@
return vb2_queue_init(dst_vq);
}
-static const struct v4l2_ctrl_config m2mtest_ctrl_trans_time_msec = {
- .ops = &m2mtest_ctrl_ops,
+static const struct v4l2_ctrl_config vim2m_ctrl_trans_time_msec = {
+ .ops = &vim2m_ctrl_ops,
.id = V4L2_CID_TRANS_TIME_MSEC,
.name = "Transaction Time (msec)",
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -856,8 +858,8 @@
.step = 1,
};
-static const struct v4l2_ctrl_config m2mtest_ctrl_trans_num_bufs = {
- .ops = &m2mtest_ctrl_ops,
+static const struct v4l2_ctrl_config vim2m_ctrl_trans_num_bufs = {
+ .ops = &vim2m_ctrl_ops,
.id = V4L2_CID_TRANS_NUM_BUFS,
.name = "Buffers Per Transaction",
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -870,10 +872,10 @@
/*
* File operations
*/
-static int m2mtest_open(struct file *file)
+static int vim2m_open(struct file *file)
{
- struct m2mtest_dev *dev = video_drvdata(file);
- struct m2mtest_ctx *ctx = NULL;
+ struct vim2m_dev *dev = video_drvdata(file);
+ struct vim2m_ctx *ctx = NULL;
struct v4l2_ctrl_handler *hdl;
int rc = 0;
@@ -890,10 +892,10 @@
ctx->dev = dev;
hdl = &ctx->hdl;
v4l2_ctrl_handler_init(hdl, 4);
- v4l2_ctrl_new_std(hdl, &m2mtest_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(hdl, &m2mtest_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_custom(hdl, &m2mtest_ctrl_trans_time_msec, NULL);
- v4l2_ctrl_new_custom(hdl, &m2mtest_ctrl_trans_num_bufs, NULL);
+ v4l2_ctrl_new_std(hdl, &vim2m_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(hdl, &vim2m_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_custom(hdl, &vim2m_ctrl_trans_time_msec, NULL);
+ v4l2_ctrl_new_custom(hdl, &vim2m_ctrl_trans_num_bufs, NULL);
if (hdl->error) {
rc = hdl->error;
v4l2_ctrl_handler_free(hdl);
@@ -933,10 +935,10 @@
return rc;
}
-static int m2mtest_release(struct file *file)
+static int vim2m_release(struct file *file)
{
- struct m2mtest_dev *dev = video_drvdata(file);
- struct m2mtest_ctx *ctx = file2ctx(file);
+ struct vim2m_dev *dev = video_drvdata(file);
+ struct vim2m_ctx *ctx = file2ctx(file);
dprintk(dev, "Releasing instance %p\n", ctx);
@@ -953,20 +955,20 @@
return 0;
}
-static const struct v4l2_file_operations m2mtest_fops = {
+static const struct v4l2_file_operations vim2m_fops = {
.owner = THIS_MODULE,
- .open = m2mtest_open,
- .release = m2mtest_release,
+ .open = vim2m_open,
+ .release = vim2m_release,
.poll = v4l2_m2m_fop_poll,
.unlocked_ioctl = video_ioctl2,
.mmap = v4l2_m2m_fop_mmap,
};
-static struct video_device m2mtest_videodev = {
+static struct video_device vim2m_videodev = {
.name = MEM2MEM_NAME,
.vfl_dir = VFL_DIR_M2M,
- .fops = &m2mtest_fops,
- .ioctl_ops = &m2mtest_ioctl_ops,
+ .fops = &vim2m_fops,
+ .ioctl_ops = &vim2m_ioctl_ops,
.minor = -1,
.release = video_device_release,
};
@@ -977,9 +979,9 @@
.job_abort = job_abort,
};
-static int m2mtest_probe(struct platform_device *pdev)
+static int vim2m_probe(struct platform_device *pdev)
{
- struct m2mtest_dev *dev;
+ struct vim2m_dev *dev;
struct video_device *vfd;
int ret;
@@ -1003,7 +1005,7 @@
goto unreg_dev;
}
- *vfd = m2mtest_videodev;
+ *vfd = vim2m_videodev;
vfd->lock = &dev->dev_mutex;
vfd->v4l2_dev = &dev->v4l2_dev;
@@ -1014,7 +1016,7 @@
}
video_set_drvdata(vfd, dev);
- snprintf(vfd->name, sizeof(vfd->name), "%s", m2mtest_videodev.name);
+ snprintf(vfd->name, sizeof(vfd->name), "%s", vim2m_videodev.name);
dev->vfd = vfd;
v4l2_info(&dev->v4l2_dev,
"Device registered as /dev/video%d\n", vfd->num);
@@ -1042,11 +1044,11 @@
return ret;
}
-static int m2mtest_remove(struct platform_device *pdev)
+static int vim2m_remove(struct platform_device *pdev)
{
- struct m2mtest_dev *dev = platform_get_drvdata(pdev);
+ struct vim2m_dev *dev = platform_get_drvdata(pdev);
- v4l2_info(&dev->v4l2_dev, "Removing " MEM2MEM_TEST_MODULE_NAME);
+ v4l2_info(&dev->v4l2_dev, "Removing " MEM2MEM_NAME);
v4l2_m2m_release(dev->m2m_dev);
del_timer_sync(&dev->timer);
video_unregister_device(dev->vfd);
@@ -1055,35 +1057,34 @@
return 0;
}
-static struct platform_driver m2mtest_pdrv = {
- .probe = m2mtest_probe,
- .remove = m2mtest_remove,
+static struct platform_driver vim2m_pdrv = {
+ .probe = vim2m_probe,
+ .remove = vim2m_remove,
.driver = {
.name = MEM2MEM_NAME,
- .owner = THIS_MODULE,
},
};
-static void __exit m2mtest_exit(void)
+static void __exit vim2m_exit(void)
{
- platform_driver_unregister(&m2mtest_pdrv);
- platform_device_unregister(&m2mtest_pdev);
+ platform_driver_unregister(&vim2m_pdrv);
+ platform_device_unregister(&vim2m_pdev);
}
-static int __init m2mtest_init(void)
+static int __init vim2m_init(void)
{
int ret;
- ret = platform_device_register(&m2mtest_pdev);
+ ret = platform_device_register(&vim2m_pdev);
if (ret)
return ret;
- ret = platform_driver_register(&m2mtest_pdrv);
+ ret = platform_driver_register(&vim2m_pdrv);
if (ret)
- platform_device_unregister(&m2mtest_pdev);
+ platform_device_unregister(&vim2m_pdev);
return 0;
}
-module_init(m2mtest_init);
-module_exit(m2mtest_exit);
+module_init(vim2m_init);
+module_exit(vim2m_exit);
diff --git a/drivers/media/platform/vino.c b/drivers/media/platform/vino.c
deleted file mode 100644
index 91d44ea..0000000
--- a/drivers/media/platform/vino.c
+++ /dev/null
@@ -1,4347 +0,0 @@
-/*
- * Driver for the VINO (Video In No Out) system found in SGI Indys.
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi>
- *
- * Based on the previous version of the driver for 2.4 kernels by:
- * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
- *
- * v4l2_device/v4l2_subdev conversion by:
- * Copyright (C) 2009 Hans Verkuil <hverkuil@xs4all.nl>
- *
- * Note: this conversion is untested! Please contact the linux-media
- * mailinglist if you can test this, together with the test results.
- */
-
-/*
- * TODO:
- * - remove "mark pages reserved-hacks" from memory allocation code
- * and implement fault()
- * - check decimation, calculating and reporting image size when
- * using decimation
- * - implement read(), user mode buffers and overlay (?)
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/dma-mapping.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/time.h>
-#include <linux/kmod.h>
-
-#include <linux/i2c.h>
-
-#include <linux/videodev2.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <linux/mutex.h>
-
-#include <asm/paccess.h>
-#include <asm/io.h>
-#include <asm/sgi/ip22.h>
-#include <asm/sgi/mc.h>
-
-#include "vino.h"
-#include "saa7191.h"
-#include "indycam.h"
-
-/* Uncomment the following line to get lots and lots of (mostly useless)
- * debug info.
- * Note that the debug output also slows down the driver significantly */
-// #define VINO_DEBUG
-// #define VINO_DEBUG_INT
-
-#define VINO_MODULE_VERSION "0.0.7"
-
-MODULE_DESCRIPTION("SGI VINO Video4Linux2 driver");
-MODULE_VERSION(VINO_MODULE_VERSION);
-MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>");
-MODULE_LICENSE("GPL");
-
-#ifdef VINO_DEBUG
-#define dprintk(x...) printk("VINO: " x);
-#else
-#define dprintk(x...)
-#endif
-
-#define VINO_NO_CHANNEL 0
-#define VINO_CHANNEL_A 1
-#define VINO_CHANNEL_B 2
-
-#define VINO_PAL_WIDTH 768
-#define VINO_PAL_HEIGHT 576
-#define VINO_NTSC_WIDTH 640
-#define VINO_NTSC_HEIGHT 480
-
-#define VINO_MIN_WIDTH 32
-#define VINO_MIN_HEIGHT 32
-
-#define VINO_CLIPPING_START_ODD_D1 1
-#define VINO_CLIPPING_START_ODD_PAL 15
-#define VINO_CLIPPING_START_ODD_NTSC 12
-
-#define VINO_CLIPPING_START_EVEN_D1 2
-#define VINO_CLIPPING_START_EVEN_PAL 15
-#define VINO_CLIPPING_START_EVEN_NTSC 12
-
-#define VINO_INPUT_CHANNEL_COUNT 3
-
-/* the number is the index for vino_inputs */
-#define VINO_INPUT_NONE -1
-#define VINO_INPUT_COMPOSITE 0
-#define VINO_INPUT_SVIDEO 1
-#define VINO_INPUT_D1 2
-
-#define VINO_PAGE_RATIO (PAGE_SIZE / VINO_PAGE_SIZE)
-
-#define VINO_FIFO_THRESHOLD_DEFAULT 16
-
-#define VINO_FRAMEBUFFER_SIZE ((VINO_PAL_WIDTH \
- * VINO_PAL_HEIGHT * 4 \
- + 3 * PAGE_SIZE) & ~(PAGE_SIZE - 1))
-
-#define VINO_FRAMEBUFFER_COUNT_MAX 8
-
-#define VINO_FRAMEBUFFER_UNUSED 0
-#define VINO_FRAMEBUFFER_IN_USE 1
-#define VINO_FRAMEBUFFER_READY 2
-
-#define VINO_QUEUE_ERROR -1
-#define VINO_QUEUE_MAGIC 0x20050125
-
-#define VINO_MEMORY_NONE 0
-#define VINO_MEMORY_MMAP 1
-#define VINO_MEMORY_USERPTR 2
-
-#define VINO_DUMMY_DESC_COUNT 4
-#define VINO_DESC_FETCH_DELAY 5 /* microseconds */
-
-#define VINO_MAX_FRAME_SKIP_COUNT 128
-
-/* the number is the index for vino_data_formats */
-#define VINO_DATA_FMT_NONE -1
-#define VINO_DATA_FMT_GREY 0
-#define VINO_DATA_FMT_RGB332 1
-#define VINO_DATA_FMT_RGB32 2
-#define VINO_DATA_FMT_YUV 3
-
-#define VINO_DATA_FMT_COUNT 4
-
-/* the number is the index for vino_data_norms */
-#define VINO_DATA_NORM_NONE -1
-#define VINO_DATA_NORM_NTSC 0
-#define VINO_DATA_NORM_PAL 1
-#define VINO_DATA_NORM_SECAM 2
-#define VINO_DATA_NORM_D1 3
-
-#define VINO_DATA_NORM_COUNT 4
-
-/* I2C controller flags */
-#define SGI_I2C_FORCE_IDLE (0 << 0)
-#define SGI_I2C_NOT_IDLE (1 << 0)
-#define SGI_I2C_WRITE (0 << 1)
-#define SGI_I2C_READ (1 << 1)
-#define SGI_I2C_RELEASE_BUS (0 << 2)
-#define SGI_I2C_HOLD_BUS (1 << 2)
-#define SGI_I2C_XFER_DONE (0 << 4)
-#define SGI_I2C_XFER_BUSY (1 << 4)
-#define SGI_I2C_ACK (0 << 5)
-#define SGI_I2C_NACK (1 << 5)
-#define SGI_I2C_BUS_OK (0 << 7)
-#define SGI_I2C_BUS_ERR (1 << 7)
-
-/* Internal data structure definitions */
-
-struct vino_input {
- char *name;
- v4l2_std_id std;
-};
-
-struct vino_clipping {
- unsigned int left, right, top, bottom;
-};
-
-struct vino_data_format {
- /* the description */
- char *description;
- /* bytes per pixel */
- unsigned int bpp;
- /* V4L2 fourcc code */
- __u32 pixelformat;
- /* V4L2 colorspace (duh!) */
- enum v4l2_colorspace colorspace;
-};
-
-struct vino_data_norm {
- char *description;
- unsigned int width, height;
- struct vino_clipping odd;
- struct vino_clipping even;
-
- v4l2_std_id std;
- unsigned int fps_min, fps_max;
- __u32 framelines;
-};
-
-struct vino_descriptor_table {
- /* the number of PAGE_SIZE sized pages in the buffer */
- unsigned int page_count;
- /* virtual (kmalloc'd) pointers to the actual data
- * (in PAGE_SIZE chunks, used with mmap streaming) */
- unsigned long *virtual;
-
- /* cpu address for the VINO descriptor table
- * (contains DMA addresses, VINO_PAGE_SIZE chunks) */
- unsigned long *dma_cpu;
- /* dma address for the VINO descriptor table
- * (contains DMA addresses, VINO_PAGE_SIZE chunks) */
- dma_addr_t dma;
-};
-
-struct vino_framebuffer {
- /* identifier nubmer */
- unsigned int id;
- /* the length of the whole buffer */
- unsigned int size;
- /* the length of actual data in buffer */
- unsigned int data_size;
- /* the data format */
- unsigned int data_format;
- /* the state of buffer data */
- unsigned int state;
- /* is the buffer mapped in user space? */
- unsigned int map_count;
- /* memory offset for mmap() */
- unsigned int offset;
- /* frame counter */
- unsigned int frame_counter;
- /* timestamp (written when image capture finishes) */
- struct timeval timestamp;
-
- struct vino_descriptor_table desc_table;
-
- spinlock_t state_lock;
-};
-
-struct vino_framebuffer_fifo {
- unsigned int length;
-
- unsigned int used;
- unsigned int head;
- unsigned int tail;
-
- unsigned int data[VINO_FRAMEBUFFER_COUNT_MAX];
-};
-
-struct vino_framebuffer_queue {
- unsigned int magic;
-
- /* VINO_MEMORY_NONE, VINO_MEMORY_MMAP or VINO_MEMORY_USERPTR */
- unsigned int type;
- unsigned int length;
-
- /* data field of in and out contain index numbers for buffer */
- struct vino_framebuffer_fifo in;
- struct vino_framebuffer_fifo out;
-
- struct vino_framebuffer *buffer[VINO_FRAMEBUFFER_COUNT_MAX];
-
- spinlock_t queue_lock;
- struct mutex queue_mutex;
- wait_queue_head_t frame_wait_queue;
-};
-
-struct vino_interrupt_data {
- struct timeval timestamp;
- unsigned int frame_counter;
- unsigned int skip_count;
- unsigned int skip;
-};
-
-struct vino_channel_settings {
- unsigned int channel;
-
- int input;
- unsigned int data_format;
- unsigned int data_norm;
- struct vino_clipping clipping;
- unsigned int decimation;
- unsigned int line_size;
- unsigned int alpha;
- unsigned int fps;
- unsigned int framert_reg;
-
- unsigned int fifo_threshold;
-
- struct vino_framebuffer_queue fb_queue;
-
- /* number of the current field */
- unsigned int field;
-
- /* read in progress */
- int reading;
- /* streaming is active */
- int streaming;
- /* the driver is currently processing the queue */
- int capturing;
-
- struct mutex mutex;
- spinlock_t capture_lock;
-
- unsigned int users;
-
- struct vino_interrupt_data int_data;
-
- /* V4L support */
- struct video_device *vdev;
-};
-
-struct vino_settings {
- struct v4l2_device v4l2_dev;
- struct vino_channel_settings a;
- struct vino_channel_settings b;
-
- /* the channel which owns this client:
- * VINO_NO_CHANNEL, VINO_CHANNEL_A or VINO_CHANNEL_B */
- unsigned int decoder_owner;
- struct v4l2_subdev *decoder;
- unsigned int camera_owner;
- struct v4l2_subdev *camera;
-
- /* a lock for vino register access */
- spinlock_t vino_lock;
- /* a lock for channel input changes */
- spinlock_t input_lock;
-
- unsigned long dummy_page;
- struct vino_descriptor_table dummy_desc_table;
-};
-
-/* Module parameters */
-
-/*
- * Using vino_pixel_conversion the ABGR32-format pixels supplied
- * by the VINO chip can be converted to more common formats
- * like RGBA32 (or probably RGB24 in the future). This way we
- * can give out data that can be specified correctly with
- * the V4L2-definitions.
- *
- * The pixel format is specified as RGBA32 when no conversion
- * is used.
- *
- * Note that this only affects the 32-bit bit depth.
- *
- * Use non-zero value to enable conversion.
- */
-static int vino_pixel_conversion;
-
-module_param_named(pixelconv, vino_pixel_conversion, int, 0);
-
-MODULE_PARM_DESC(pixelconv,
- "enable pixel conversion (non-zero value enables)");
-
-/* Internal data structures */
-
-static struct sgi_vino *vino;
-
-static struct vino_settings *vino_drvdata;
-
-#define camera_call(o, f, args...) \
- v4l2_subdev_call(vino_drvdata->camera, o, f, ##args)
-#define decoder_call(o, f, args...) \
- v4l2_subdev_call(vino_drvdata->decoder, o, f, ##args)
-
-static const char *vino_driver_name = "vino";
-static const char *vino_driver_description = "SGI VINO";
-static const char *vino_bus_name = "GIO64 bus";
-static const char *vino_vdev_name_a = "SGI VINO Channel A";
-static const char *vino_vdev_name_b = "SGI VINO Channel B";
-
-static void vino_capture_tasklet(unsigned long channel);
-
-DECLARE_TASKLET(vino_tasklet_a, vino_capture_tasklet, VINO_CHANNEL_A);
-DECLARE_TASKLET(vino_tasklet_b, vino_capture_tasklet, VINO_CHANNEL_B);
-
-static const struct vino_input vino_inputs[] = {
- {
- .name = "Composite",
- .std = V4L2_STD_NTSC | V4L2_STD_PAL
- | V4L2_STD_SECAM,
- }, {
- .name = "S-Video",
- .std = V4L2_STD_NTSC | V4L2_STD_PAL
- | V4L2_STD_SECAM,
- }, {
- .name = "D1/IndyCam",
- .std = V4L2_STD_NTSC,
- }
-};
-
-static const struct vino_data_format vino_data_formats[] = {
- {
- .description = "8-bit greyscale",
- .bpp = 1,
- .pixelformat = V4L2_PIX_FMT_GREY,
- .colorspace = V4L2_COLORSPACE_SMPTE170M,
- }, {
- .description = "8-bit dithered RGB 3-3-2",
- .bpp = 1,
- .pixelformat = V4L2_PIX_FMT_RGB332,
- .colorspace = V4L2_COLORSPACE_SRGB,
- }, {
- .description = "32-bit RGB",
- .bpp = 4,
- .pixelformat = V4L2_PIX_FMT_RGB32,
- .colorspace = V4L2_COLORSPACE_SRGB,
- }, {
- .description = "YUV 4:2:2",
- .bpp = 2,
- .pixelformat = V4L2_PIX_FMT_YUYV, // XXX: swapped?
- .colorspace = V4L2_COLORSPACE_SMPTE170M,
- }
-};
-
-static const struct vino_data_norm vino_data_norms[] = {
- {
- .description = "NTSC",
- .std = V4L2_STD_NTSC,
- .fps_min = 6,
- .fps_max = 30,
- .framelines = 525,
- .width = VINO_NTSC_WIDTH,
- .height = VINO_NTSC_HEIGHT,
- .odd = {
- .top = VINO_CLIPPING_START_ODD_NTSC,
- .left = 0,
- .bottom = VINO_CLIPPING_START_ODD_NTSC
- + VINO_NTSC_HEIGHT / 2 - 1,
- .right = VINO_NTSC_WIDTH,
- },
- .even = {
- .top = VINO_CLIPPING_START_EVEN_NTSC,
- .left = 0,
- .bottom = VINO_CLIPPING_START_EVEN_NTSC
- + VINO_NTSC_HEIGHT / 2 - 1,
- .right = VINO_NTSC_WIDTH,
- },
- }, {
- .description = "PAL",
- .std = V4L2_STD_PAL,
- .fps_min = 5,
- .fps_max = 25,
- .framelines = 625,
- .width = VINO_PAL_WIDTH,
- .height = VINO_PAL_HEIGHT,
- .odd = {
- .top = VINO_CLIPPING_START_ODD_PAL,
- .left = 0,
- .bottom = VINO_CLIPPING_START_ODD_PAL
- + VINO_PAL_HEIGHT / 2 - 1,
- .right = VINO_PAL_WIDTH,
- },
- .even = {
- .top = VINO_CLIPPING_START_EVEN_PAL,
- .left = 0,
- .bottom = VINO_CLIPPING_START_EVEN_PAL
- + VINO_PAL_HEIGHT / 2 - 1,
- .right = VINO_PAL_WIDTH,
- },
- }, {
- .description = "SECAM",
- .std = V4L2_STD_SECAM,
- .fps_min = 5,
- .fps_max = 25,
- .framelines = 625,
- .width = VINO_PAL_WIDTH,
- .height = VINO_PAL_HEIGHT,
- .odd = {
- .top = VINO_CLIPPING_START_ODD_PAL,
- .left = 0,
- .bottom = VINO_CLIPPING_START_ODD_PAL
- + VINO_PAL_HEIGHT / 2 - 1,
- .right = VINO_PAL_WIDTH,
- },
- .even = {
- .top = VINO_CLIPPING_START_EVEN_PAL,
- .left = 0,
- .bottom = VINO_CLIPPING_START_EVEN_PAL
- + VINO_PAL_HEIGHT / 2 - 1,
- .right = VINO_PAL_WIDTH,
- },
- }, {
- .description = "NTSC/D1",
- .std = V4L2_STD_NTSC,
- .fps_min = 6,
- .fps_max = 30,
- .framelines = 525,
- .width = VINO_NTSC_WIDTH,
- .height = VINO_NTSC_HEIGHT,
- .odd = {
- .top = VINO_CLIPPING_START_ODD_D1,
- .left = 0,
- .bottom = VINO_CLIPPING_START_ODD_D1
- + VINO_NTSC_HEIGHT / 2 - 1,
- .right = VINO_NTSC_WIDTH,
- },
- .even = {
- .top = VINO_CLIPPING_START_EVEN_D1,
- .left = 0,
- .bottom = VINO_CLIPPING_START_EVEN_D1
- + VINO_NTSC_HEIGHT / 2 - 1,
- .right = VINO_NTSC_WIDTH,
- },
- }
-};
-
-#define VINO_INDYCAM_V4L2_CONTROL_COUNT 9
-
-struct v4l2_queryctrl vino_indycam_v4l2_controls[] = {
- {
- .id = V4L2_CID_AUTOGAIN,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Automatic Gain Control",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = INDYCAM_AGC_DEFAULT,
- }, {
- .id = V4L2_CID_AUTO_WHITE_BALANCE,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Automatic White Balance",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = INDYCAM_AWB_DEFAULT,
- }, {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = INDYCAM_GAIN_MIN,
- .maximum = INDYCAM_GAIN_MAX,
- .step = 1,
- .default_value = INDYCAM_GAIN_DEFAULT,
- }, {
- .id = INDYCAM_CONTROL_RED_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Red Saturation",
- .minimum = INDYCAM_RED_SATURATION_MIN,
- .maximum = INDYCAM_RED_SATURATION_MAX,
- .step = 1,
- .default_value = INDYCAM_RED_SATURATION_DEFAULT,
- }, {
- .id = INDYCAM_CONTROL_BLUE_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Blue Saturation",
- .minimum = INDYCAM_BLUE_SATURATION_MIN,
- .maximum = INDYCAM_BLUE_SATURATION_MAX,
- .step = 1,
- .default_value = INDYCAM_BLUE_SATURATION_DEFAULT,
- }, {
- .id = V4L2_CID_RED_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Red Balance",
- .minimum = INDYCAM_RED_BALANCE_MIN,
- .maximum = INDYCAM_RED_BALANCE_MAX,
- .step = 1,
- .default_value = INDYCAM_RED_BALANCE_DEFAULT,
- }, {
- .id = V4L2_CID_BLUE_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Blue Balance",
- .minimum = INDYCAM_BLUE_BALANCE_MIN,
- .maximum = INDYCAM_BLUE_BALANCE_MAX,
- .step = 1,
- .default_value = INDYCAM_BLUE_BALANCE_DEFAULT,
- }, {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Shutter Control",
- .minimum = INDYCAM_SHUTTER_MIN,
- .maximum = INDYCAM_SHUTTER_MAX,
- .step = 1,
- .default_value = INDYCAM_SHUTTER_DEFAULT,
- }, {
- .id = V4L2_CID_GAMMA,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gamma",
- .minimum = INDYCAM_GAMMA_MIN,
- .maximum = INDYCAM_GAMMA_MAX,
- .step = 1,
- .default_value = INDYCAM_GAMMA_DEFAULT,
- }
-};
-
-#define VINO_SAA7191_V4L2_CONTROL_COUNT 9
-
-struct v4l2_queryctrl vino_saa7191_v4l2_controls[] = {
- {
- .id = V4L2_CID_HUE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Hue",
- .minimum = SAA7191_HUE_MIN,
- .maximum = SAA7191_HUE_MAX,
- .step = 1,
- .default_value = SAA7191_HUE_DEFAULT,
- }, {
- .id = SAA7191_CONTROL_BANDPASS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Luminance Bandpass",
- .minimum = SAA7191_BANDPASS_MIN,
- .maximum = SAA7191_BANDPASS_MAX,
- .step = 1,
- .default_value = SAA7191_BANDPASS_DEFAULT,
- }, {
- .id = SAA7191_CONTROL_BANDPASS_WEIGHT,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Luminance Bandpass Weight",
- .minimum = SAA7191_BANDPASS_WEIGHT_MIN,
- .maximum = SAA7191_BANDPASS_WEIGHT_MAX,
- .step = 1,
- .default_value = SAA7191_BANDPASS_WEIGHT_DEFAULT,
- }, {
- .id = SAA7191_CONTROL_CORING,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "HF Luminance Coring",
- .minimum = SAA7191_CORING_MIN,
- .maximum = SAA7191_CORING_MAX,
- .step = 1,
- .default_value = SAA7191_CORING_DEFAULT,
- }, {
- .id = SAA7191_CONTROL_FORCE_COLOUR,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Force Colour",
- .minimum = SAA7191_FORCE_COLOUR_MIN,
- .maximum = SAA7191_FORCE_COLOUR_MAX,
- .step = 1,
- .default_value = SAA7191_FORCE_COLOUR_DEFAULT,
- }, {
- .id = SAA7191_CONTROL_CHROMA_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Chrominance Gain Control",
- .minimum = SAA7191_CHROMA_GAIN_MIN,
- .maximum = SAA7191_CHROMA_GAIN_MAX,
- .step = 1,
- .default_value = SAA7191_CHROMA_GAIN_DEFAULT,
- }, {
- .id = SAA7191_CONTROL_VTRC,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "VTR Time Constant",
- .minimum = SAA7191_VTRC_MIN,
- .maximum = SAA7191_VTRC_MAX,
- .step = 1,
- .default_value = SAA7191_VTRC_DEFAULT,
- }, {
- .id = SAA7191_CONTROL_LUMA_DELAY,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Luminance Delay Compensation",
- .minimum = SAA7191_LUMA_DELAY_MIN,
- .maximum = SAA7191_LUMA_DELAY_MAX,
- .step = 1,
- .default_value = SAA7191_LUMA_DELAY_DEFAULT,
- }, {
- .id = SAA7191_CONTROL_VNR,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Vertical Noise Reduction",
- .minimum = SAA7191_VNR_MIN,
- .maximum = SAA7191_VNR_MAX,
- .step = 1,
- .default_value = SAA7191_VNR_DEFAULT,
- }
-};
-
-/* VINO framebuffer/DMA descriptor management */
-
-static void vino_free_buffer_with_count(struct vino_framebuffer *fb,
- unsigned int count)
-{
- unsigned int i;
-
- dprintk("vino_free_buffer_with_count(): count = %d\n", count);
-
- for (i = 0; i < count; i++) {
- ClearPageReserved(virt_to_page((void *)fb->desc_table.virtual[i]));
- dma_unmap_single(NULL,
- fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i],
- PAGE_SIZE, DMA_FROM_DEVICE);
- free_page(fb->desc_table.virtual[i]);
- }
-
- dma_free_coherent(NULL,
- VINO_PAGE_RATIO * (fb->desc_table.page_count + 4) *
- sizeof(dma_addr_t), (void *)fb->desc_table.dma_cpu,
- fb->desc_table.dma);
- kfree(fb->desc_table.virtual);
-
- memset(fb, 0, sizeof(struct vino_framebuffer));
-}
-
-static void vino_free_buffer(struct vino_framebuffer *fb)
-{
- vino_free_buffer_with_count(fb, fb->desc_table.page_count);
-}
-
-static int vino_allocate_buffer(struct vino_framebuffer *fb,
- unsigned int size)
-{
- unsigned int count, i, j;
- int ret = 0;
-
- dprintk("vino_allocate_buffer():\n");
-
- if (size < 1)
- return -EINVAL;
-
- memset(fb, 0, sizeof(struct vino_framebuffer));
-
- count = ((size / PAGE_SIZE) + 4) & ~3;
-
- dprintk("vino_allocate_buffer(): size = %d, count = %d\n",
- size, count);
-
- /* allocate memory for table with virtual (page) addresses */
- fb->desc_table.virtual =
- kmalloc(count * sizeof(unsigned long), GFP_KERNEL);
- if (!fb->desc_table.virtual)
- return -ENOMEM;
-
- /* allocate memory for table with dma addresses
- * (has space for four extra descriptors) */
- fb->desc_table.dma_cpu =
- dma_alloc_coherent(NULL, VINO_PAGE_RATIO * (count + 4) *
- sizeof(dma_addr_t), &fb->desc_table.dma,
- GFP_KERNEL | GFP_DMA);
- if (!fb->desc_table.dma_cpu) {
- ret = -ENOMEM;
- goto out_free_virtual;
- }
-
- /* allocate pages for the buffer and acquire the according
- * dma addresses */
- for (i = 0; i < count; i++) {
- dma_addr_t dma_data_addr;
-
- fb->desc_table.virtual[i] =
- get_zeroed_page(GFP_KERNEL | GFP_DMA);
- if (!fb->desc_table.virtual[i]) {
- ret = -ENOBUFS;
- break;
- }
-
- dma_data_addr =
- dma_map_single(NULL,
- (void *)fb->desc_table.virtual[i],
- PAGE_SIZE, DMA_FROM_DEVICE);
-
- for (j = 0; j < VINO_PAGE_RATIO; j++) {
- fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i + j] =
- dma_data_addr + VINO_PAGE_SIZE * j;
- }
-
- SetPageReserved(virt_to_page((void *)fb->desc_table.virtual[i]));
- }
-
- /* page_count needs to be set anyway, because the descriptor table has
- * been allocated according to this number */
- fb->desc_table.page_count = count;
-
- if (ret) {
- /* the descriptor with index i doesn't contain
- * a valid address yet */
- vino_free_buffer_with_count(fb, i);
- return ret;
- }
-
- //fb->size = size;
- fb->size = count * PAGE_SIZE;
- fb->data_format = VINO_DATA_FMT_NONE;
-
- /* set the dma stop-bit for the last (count+1)th descriptor */
- fb->desc_table.dma_cpu[VINO_PAGE_RATIO * count] = VINO_DESC_STOP;
- return 0;
-
- out_free_virtual:
- kfree(fb->desc_table.virtual);
- return ret;
-}
-
-#if 0
-/* user buffers not fully implemented yet */
-static int vino_prepare_user_buffer(struct vino_framebuffer *fb,
- void *user,
- unsigned int size)
-{
- unsigned int count, i, j;
- int ret = 0;
-
- dprintk("vino_prepare_user_buffer():\n");
-
- if (size < 1)
- return -EINVAL;
-
- memset(fb, 0, sizeof(struct vino_framebuffer));
-
- count = ((size / PAGE_SIZE)) & ~3;
-
- dprintk("vino_prepare_user_buffer(): size = %d, count = %d\n",
- size, count);
-
- /* allocate memory for table with virtual (page) addresses */
- fb->desc_table.virtual = (unsigned long *)
- kmalloc(count * sizeof(unsigned long), GFP_KERNEL);
- if (!fb->desc_table.virtual)
- return -ENOMEM;
-
- /* allocate memory for table with dma addresses
- * (has space for four extra descriptors) */
- fb->desc_table.dma_cpu =
- dma_alloc_coherent(NULL, VINO_PAGE_RATIO * (count + 4) *
- sizeof(dma_addr_t), &fb->desc_table.dma,
- GFP_KERNEL | GFP_DMA);
- if (!fb->desc_table.dma_cpu) {
- ret = -ENOMEM;
- goto out_free_virtual;
- }
-
- /* allocate pages for the buffer and acquire the according
- * dma addresses */
- for (i = 0; i < count; i++) {
- dma_addr_t dma_data_addr;
-
- fb->desc_table.virtual[i] =
- get_zeroed_page(GFP_KERNEL | GFP_DMA);
- if (!fb->desc_table.virtual[i]) {
- ret = -ENOBUFS;
- break;
- }
-
- dma_data_addr =
- dma_map_single(NULL,
- (void *)fb->desc_table.virtual[i],
- PAGE_SIZE, DMA_FROM_DEVICE);
-
- for (j = 0; j < VINO_PAGE_RATIO; j++) {
- fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i + j] =
- dma_data_addr + VINO_PAGE_SIZE * j;
- }
-
- SetPageReserved(virt_to_page((void *)fb->desc_table.virtual[i]));
- }
-
- /* page_count needs to be set anyway, because the descriptor table has
- * been allocated according to this number */
- fb->desc_table.page_count = count;
-
- if (ret) {
- /* the descriptor with index i doesn't contain
- * a valid address yet */
- vino_free_buffer_with_count(fb, i);
- return ret;
- }
-
- //fb->size = size;
- fb->size = count * PAGE_SIZE;
-
- /* set the dma stop-bit for the last (count+1)th descriptor */
- fb->desc_table.dma_cpu[VINO_PAGE_RATIO * count] = VINO_DESC_STOP;
- return 0;
-
- out_free_virtual:
- kfree(fb->desc_table.virtual);
- return ret;
-}
-#endif
-
-static void vino_sync_buffer(struct vino_framebuffer *fb)
-{
- int i;
-
- dprintk("vino_sync_buffer():\n");
-
- for (i = 0; i < fb->desc_table.page_count; i++)
- dma_sync_single_for_cpu(NULL,
- fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i],
- PAGE_SIZE, DMA_FROM_DEVICE);
-}
-
-/* Framebuffer fifo functions (need to be locked externally) */
-
-static inline void vino_fifo_init(struct vino_framebuffer_fifo *f,
- unsigned int length)
-{
- f->length = 0;
- f->used = 0;
- f->head = 0;
- f->tail = 0;
-
- if (length > VINO_FRAMEBUFFER_COUNT_MAX)
- length = VINO_FRAMEBUFFER_COUNT_MAX;
-
- f->length = length;
-}
-
-/* returns true/false */
-static inline int vino_fifo_has_id(struct vino_framebuffer_fifo *f,
- unsigned int id)
-{
- unsigned int i;
-
- for (i = f->head; i == (f->tail - 1); i = (i + 1) % f->length) {
- if (f->data[i] == id)
- return 1;
- }
-
- return 0;
-}
-
-#if 0
-/* returns true/false */
-static inline int vino_fifo_full(struct vino_framebuffer_fifo *f)
-{
- return (f->used == f->length);
-}
-#endif
-
-static inline unsigned int vino_fifo_get_used(struct vino_framebuffer_fifo *f)
-{
- return f->used;
-}
-
-static int vino_fifo_enqueue(struct vino_framebuffer_fifo *f, unsigned int id)
-{
- if (id >= f->length) {
- return VINO_QUEUE_ERROR;
- }
-
- if (vino_fifo_has_id(f, id)) {
- return VINO_QUEUE_ERROR;
- }
-
- if (f->used < f->length) {
- f->data[f->tail] = id;
- f->tail = (f->tail + 1) % f->length;
- f->used++;
- } else {
- return VINO_QUEUE_ERROR;
- }
-
- return 0;
-}
-
-static int vino_fifo_peek(struct vino_framebuffer_fifo *f, unsigned int *id)
-{
- if (f->used > 0) {
- *id = f->data[f->head];
- } else {
- return VINO_QUEUE_ERROR;
- }
-
- return 0;
-}
-
-static int vino_fifo_dequeue(struct vino_framebuffer_fifo *f, unsigned int *id)
-{
- if (f->used > 0) {
- *id = f->data[f->head];
- f->head = (f->head + 1) % f->length;
- f->used--;
- } else {
- return VINO_QUEUE_ERROR;
- }
-
- return 0;
-}
-
-/* Framebuffer queue functions */
-
-/* execute with queue_lock locked */
-static void vino_queue_free_with_count(struct vino_framebuffer_queue *q,
- unsigned int length)
-{
- unsigned int i;
-
- q->length = 0;
- memset(&q->in, 0, sizeof(struct vino_framebuffer_fifo));
- memset(&q->out, 0, sizeof(struct vino_framebuffer_fifo));
- for (i = 0; i < length; i++) {
- dprintk("vino_queue_free_with_count(): freeing buffer %d\n",
- i);
- vino_free_buffer(q->buffer[i]);
- kfree(q->buffer[i]);
- }
-
- q->type = VINO_MEMORY_NONE;
- q->magic = 0;
-}
-
-static void vino_queue_free(struct vino_framebuffer_queue *q)
-{
- dprintk("vino_queue_free():\n");
-
- if (q->magic != VINO_QUEUE_MAGIC)
- return;
- if (q->type != VINO_MEMORY_MMAP)
- return;
-
- mutex_lock(&q->queue_mutex);
-
- vino_queue_free_with_count(q, q->length);
-
- mutex_unlock(&q->queue_mutex);
-}
-
-static int vino_queue_init(struct vino_framebuffer_queue *q,
- unsigned int *length)
-{
- unsigned int i;
- int ret = 0;
-
- dprintk("vino_queue_init(): length = %d\n", *length);
-
- if (q->magic == VINO_QUEUE_MAGIC) {
- dprintk("vino_queue_init(): queue already initialized!\n");
- return -EINVAL;
- }
-
- if (q->type != VINO_MEMORY_NONE) {
- dprintk("vino_queue_init(): queue already initialized!\n");
- return -EINVAL;
- }
-
- if (*length < 1)
- return -EINVAL;
-
- mutex_lock(&q->queue_mutex);
-
- if (*length > VINO_FRAMEBUFFER_COUNT_MAX)
- *length = VINO_FRAMEBUFFER_COUNT_MAX;
-
- q->length = 0;
-
- for (i = 0; i < *length; i++) {
- dprintk("vino_queue_init(): allocating buffer %d\n", i);
- q->buffer[i] = kmalloc(sizeof(struct vino_framebuffer),
- GFP_KERNEL);
- if (!q->buffer[i]) {
- dprintk("vino_queue_init(): kmalloc() failed\n");
- ret = -ENOMEM;
- break;
- }
-
- ret = vino_allocate_buffer(q->buffer[i],
- VINO_FRAMEBUFFER_SIZE);
- if (ret) {
- kfree(q->buffer[i]);
- dprintk("vino_queue_init(): "
- "vino_allocate_buffer() failed\n");
- break;
- }
-
- q->buffer[i]->id = i;
- if (i > 0) {
- q->buffer[i]->offset = q->buffer[i - 1]->offset +
- q->buffer[i - 1]->size;
- } else {
- q->buffer[i]->offset = 0;
- }
-
- spin_lock_init(&q->buffer[i]->state_lock);
-
- dprintk("vino_queue_init(): buffer = %d, offset = %d, "
- "size = %d\n", i, q->buffer[i]->offset,
- q->buffer[i]->size);
- }
-
- if (ret) {
- vino_queue_free_with_count(q, i);
- *length = 0;
- } else {
- q->length = *length;
- vino_fifo_init(&q->in, q->length);
- vino_fifo_init(&q->out, q->length);
- q->type = VINO_MEMORY_MMAP;
- q->magic = VINO_QUEUE_MAGIC;
- }
-
- mutex_unlock(&q->queue_mutex);
-
- return ret;
-}
-
-static struct vino_framebuffer *vino_queue_add(struct
- vino_framebuffer_queue *q,
- unsigned int id)
-{
- struct vino_framebuffer *ret = NULL;
- unsigned int total;
- unsigned long flags;
-
- dprintk("vino_queue_add(): id = %d\n", id);
-
- if (q->magic != VINO_QUEUE_MAGIC) {
- return ret;
- }
-
- spin_lock_irqsave(&q->queue_lock, flags);
-
- if (q->length == 0)
- goto out;
-
- if (id >= q->length)
- goto out;
-
- /* not needed?: if (vino_fifo_full(&q->out)) {
- goto out;
- }*/
- /* check that outgoing queue isn't already full
- * (or that it won't become full) */
- total = vino_fifo_get_used(&q->in) +
- vino_fifo_get_used(&q->out);
- if (total >= q->length)
- goto out;
-
- if (vino_fifo_enqueue(&q->in, id))
- goto out;
-
- ret = q->buffer[id];
-
-out:
- spin_unlock_irqrestore(&q->queue_lock, flags);
-
- return ret;
-}
-
-static struct vino_framebuffer *vino_queue_transfer(struct
- vino_framebuffer_queue *q)
-{
- struct vino_framebuffer *ret = NULL;
- struct vino_framebuffer *fb;
- int id;
- unsigned long flags;
-
- dprintk("vino_queue_transfer():\n");
-
- if (q->magic != VINO_QUEUE_MAGIC) {
- return ret;
- }
-
- spin_lock_irqsave(&q->queue_lock, flags);
-
- if (q->length == 0)
- goto out;
-
- // now this actually removes an entry from the incoming queue
- if (vino_fifo_dequeue(&q->in, &id)) {
- goto out;
- }
-
- dprintk("vino_queue_transfer(): id = %d\n", id);
- fb = q->buffer[id];
-
- // we have already checked that the outgoing queue is not full, but...
- if (vino_fifo_enqueue(&q->out, id)) {
- printk(KERN_ERR "vino_queue_transfer(): "
- "outgoing queue is full, this shouldn't happen!\n");
- goto out;
- }
-
- ret = fb;
-out:
- spin_unlock_irqrestore(&q->queue_lock, flags);
-
- return ret;
-}
-
-/* returns true/false */
-static int vino_queue_incoming_contains(struct vino_framebuffer_queue *q,
- unsigned int id)
-{
- int ret = 0;
- unsigned long flags;
-
- if (q->magic != VINO_QUEUE_MAGIC) {
- return ret;
- }
-
- spin_lock_irqsave(&q->queue_lock, flags);
-
- if (q->length == 0)
- goto out;
-
- ret = vino_fifo_has_id(&q->in, id);
-
-out:
- spin_unlock_irqrestore(&q->queue_lock, flags);
-
- return ret;
-}
-
-/* returns true/false */
-static int vino_queue_outgoing_contains(struct vino_framebuffer_queue *q,
- unsigned int id)
-{
- int ret = 0;
- unsigned long flags;
-
- if (q->magic != VINO_QUEUE_MAGIC) {
- return ret;
- }
-
- spin_lock_irqsave(&q->queue_lock, flags);
-
- if (q->length == 0)
- goto out;
-
- ret = vino_fifo_has_id(&q->out, id);
-
-out:
- spin_unlock_irqrestore(&q->queue_lock, flags);
-
- return ret;
-}
-
-static int vino_queue_get_incoming(struct vino_framebuffer_queue *q,
- unsigned int *used)
-{
- int ret = 0;
- unsigned long flags;
-
- if (q->magic != VINO_QUEUE_MAGIC) {
- return VINO_QUEUE_ERROR;
- }
-
- spin_lock_irqsave(&q->queue_lock, flags);
-
- if (q->length == 0) {
- ret = VINO_QUEUE_ERROR;
- goto out;
- }
-
- *used = vino_fifo_get_used(&q->in);
-
-out:
- spin_unlock_irqrestore(&q->queue_lock, flags);
-
- return ret;
-}
-
-static int vino_queue_get_outgoing(struct vino_framebuffer_queue *q,
- unsigned int *used)
-{
- int ret = 0;
- unsigned long flags;
-
- if (q->magic != VINO_QUEUE_MAGIC) {
- return VINO_QUEUE_ERROR;
- }
-
- spin_lock_irqsave(&q->queue_lock, flags);
-
- if (q->length == 0) {
- ret = VINO_QUEUE_ERROR;
- goto out;
- }
-
- *used = vino_fifo_get_used(&q->out);
-
-out:
- spin_unlock_irqrestore(&q->queue_lock, flags);
-
- return ret;
-}
-
-#if 0
-static int vino_queue_get_total(struct vino_framebuffer_queue *q,
- unsigned int *total)
-{
- int ret = 0;
- unsigned long flags;
-
- if (q->magic != VINO_QUEUE_MAGIC) {
- return VINO_QUEUE_ERROR;
- }
-
- spin_lock_irqsave(&q->queue_lock, flags);
-
- if (q->length == 0) {
- ret = VINO_QUEUE_ERROR;
- goto out;
- }
-
- *total = vino_fifo_get_used(&q->in) +
- vino_fifo_get_used(&q->out);
-
-out:
- spin_unlock_irqrestore(&q->queue_lock, flags);
-
- return ret;
-}
-#endif
-
-static struct vino_framebuffer *vino_queue_peek(struct
- vino_framebuffer_queue *q,
- unsigned int *id)
-{
- struct vino_framebuffer *ret = NULL;
- unsigned long flags;
-
- if (q->magic != VINO_QUEUE_MAGIC) {
- return ret;
- }
-
- spin_lock_irqsave(&q->queue_lock, flags);
-
- if (q->length == 0)
- goto out;
-
- if (vino_fifo_peek(&q->in, id)) {
- goto out;
- }
-
- ret = q->buffer[*id];
-out:
- spin_unlock_irqrestore(&q->queue_lock, flags);
-
- return ret;
-}
-
-static struct vino_framebuffer *vino_queue_remove(struct
- vino_framebuffer_queue *q,
- unsigned int *id)
-{
- struct vino_framebuffer *ret = NULL;
- unsigned long flags;
- dprintk("vino_queue_remove():\n");
-
- if (q->magic != VINO_QUEUE_MAGIC) {
- return ret;
- }
-
- spin_lock_irqsave(&q->queue_lock, flags);
-
- if (q->length == 0)
- goto out;
-
- if (vino_fifo_dequeue(&q->out, id)) {
- goto out;
- }
-
- dprintk("vino_queue_remove(): id = %d\n", *id);
- ret = q->buffer[*id];
-out:
- spin_unlock_irqrestore(&q->queue_lock, flags);
-
- return ret;
-}
-
-static struct
-vino_framebuffer *vino_queue_get_buffer(struct vino_framebuffer_queue *q,
- unsigned int id)
-{
- struct vino_framebuffer *ret = NULL;
- unsigned long flags;
-
- if (q->magic != VINO_QUEUE_MAGIC) {
- return ret;
- }
-
- spin_lock_irqsave(&q->queue_lock, flags);
-
- if (q->length == 0)
- goto out;
-
- if (id >= q->length)
- goto out;
-
- ret = q->buffer[id];
- out:
- spin_unlock_irqrestore(&q->queue_lock, flags);
-
- return ret;
-}
-
-static unsigned int vino_queue_get_length(struct vino_framebuffer_queue *q)
-{
- unsigned int length = 0;
- unsigned long flags;
-
- if (q->magic != VINO_QUEUE_MAGIC) {
- return length;
- }
-
- spin_lock_irqsave(&q->queue_lock, flags);
- length = q->length;
- spin_unlock_irqrestore(&q->queue_lock, flags);
-
- return length;
-}
-
-static int vino_queue_has_mapped_buffers(struct vino_framebuffer_queue *q)
-{
- unsigned int i;
- int ret = 0;
- unsigned long flags;
-
- if (q->magic != VINO_QUEUE_MAGIC) {
- return ret;
- }
-
- spin_lock_irqsave(&q->queue_lock, flags);
- for (i = 0; i < q->length; i++) {
- if (q->buffer[i]->map_count > 0) {
- ret = 1;
- break;
- }
- }
- spin_unlock_irqrestore(&q->queue_lock, flags);
-
- return ret;
-}
-
-/* VINO functions */
-
-/* execute with input_lock locked */
-static void vino_update_line_size(struct vino_channel_settings *vcs)
-{
- unsigned int w = vcs->clipping.right - vcs->clipping.left;
- unsigned int d = vcs->decimation;
- unsigned int bpp = vino_data_formats[vcs->data_format].bpp;
- unsigned int lsize;
-
- dprintk("update_line_size(): before: w = %d, d = %d, "
- "line_size = %d\n", w, d, vcs->line_size);
-
- /* line size must be multiple of 8 bytes */
- lsize = (bpp * (w / d)) & ~7;
- w = (lsize / bpp) * d;
-
- vcs->clipping.right = vcs->clipping.left + w;
- vcs->line_size = lsize;
-
- dprintk("update_line_size(): after: w = %d, d = %d, "
- "line_size = %d\n", w, d, vcs->line_size);
-}
-
-/* execute with input_lock locked */
-static void vino_set_clipping(struct vino_channel_settings *vcs,
- unsigned int x, unsigned int y,
- unsigned int w, unsigned int h)
-{
- unsigned int maxwidth, maxheight;
- unsigned int d;
-
- maxwidth = vino_data_norms[vcs->data_norm].width;
- maxheight = vino_data_norms[vcs->data_norm].height;
- d = vcs->decimation;
-
- y &= ~1; /* odd/even fields */
-
- if (x > maxwidth) {
- x = 0;
- }
- if (y > maxheight) {
- y = 0;
- }
-
- if (((w / d) < VINO_MIN_WIDTH)
- || ((h / d) < VINO_MIN_HEIGHT)) {
- w = VINO_MIN_WIDTH * d;
- h = VINO_MIN_HEIGHT * d;
- }
-
- if ((x + w) > maxwidth) {
- w = maxwidth - x;
- if ((w / d) < VINO_MIN_WIDTH)
- x = maxwidth - VINO_MIN_WIDTH * d;
- }
- if ((y + h) > maxheight) {
- h = maxheight - y;
- if ((h / d) < VINO_MIN_HEIGHT)
- y = maxheight - VINO_MIN_HEIGHT * d;
- }
-
- vcs->clipping.left = x;
- vcs->clipping.top = y;
- vcs->clipping.right = x + w;
- vcs->clipping.bottom = y + h;
-
- vino_update_line_size(vcs);
-
- dprintk("clipping %d, %d, %d, %d / %d - %d\n",
- vcs->clipping.left, vcs->clipping.top, vcs->clipping.right,
- vcs->clipping.bottom, vcs->decimation, vcs->line_size);
-}
-
-/* execute with input_lock locked */
-static inline void vino_set_default_clipping(struct vino_channel_settings *vcs)
-{
- vino_set_clipping(vcs, 0, 0, vino_data_norms[vcs->data_norm].width,
- vino_data_norms[vcs->data_norm].height);
-}
-
-/* execute with input_lock locked */
-static void vino_set_scaling(struct vino_channel_settings *vcs,
- unsigned int w, unsigned int h)
-{
- unsigned int x, y, curw, curh, d;
-
- x = vcs->clipping.left;
- y = vcs->clipping.top;
- curw = vcs->clipping.right - vcs->clipping.left;
- curh = vcs->clipping.bottom - vcs->clipping.top;
-
- d = max(curw / w, curh / h);
-
- dprintk("scaling w: %d, h: %d, curw: %d, curh: %d, d: %d\n",
- w, h, curw, curh, d);
-
- if (d < 1) {
- d = 1;
- } else if (d > 8) {
- d = 8;
- }
-
- vcs->decimation = d;
- vino_set_clipping(vcs, x, y, w * d, h * d);
-
- dprintk("scaling %d, %d, %d, %d / %d - %d\n", vcs->clipping.left,
- vcs->clipping.top, vcs->clipping.right, vcs->clipping.bottom,
- vcs->decimation, vcs->line_size);
-}
-
-/* execute with input_lock locked */
-static inline void vino_set_default_scaling(struct vino_channel_settings *vcs)
-{
- vino_set_scaling(vcs, vcs->clipping.right - vcs->clipping.left,
- vcs->clipping.bottom - vcs->clipping.top);
-}
-
-/* execute with input_lock locked */
-static void vino_set_framerate(struct vino_channel_settings *vcs,
- unsigned int fps)
-{
- unsigned int mask;
-
- switch (vcs->data_norm) {
- case VINO_DATA_NORM_NTSC:
- case VINO_DATA_NORM_D1:
- fps = (unsigned int)(fps / 6) * 6; // FIXME: round!
-
- if (fps < vino_data_norms[vcs->data_norm].fps_min)
- fps = vino_data_norms[vcs->data_norm].fps_min;
- if (fps > vino_data_norms[vcs->data_norm].fps_max)
- fps = vino_data_norms[vcs->data_norm].fps_max;
-
- switch (fps) {
- case 6:
- mask = 0x003;
- break;
- case 12:
- mask = 0x0c3;
- break;
- case 18:
- mask = 0x333;
- break;
- case 24:
- mask = 0x3ff;
- break;
- case 30:
- mask = 0xfff;
- break;
- default:
- mask = VINO_FRAMERT_FULL;
- }
- vcs->framert_reg = VINO_FRAMERT_RT(mask);
- break;
- case VINO_DATA_NORM_PAL:
- case VINO_DATA_NORM_SECAM:
- fps = (unsigned int)(fps / 5) * 5; // FIXME: round!
-
- if (fps < vino_data_norms[vcs->data_norm].fps_min)
- fps = vino_data_norms[vcs->data_norm].fps_min;
- if (fps > vino_data_norms[vcs->data_norm].fps_max)
- fps = vino_data_norms[vcs->data_norm].fps_max;
-
- switch (fps) {
- case 5:
- mask = 0x003;
- break;
- case 10:
- mask = 0x0c3;
- break;
- case 15:
- mask = 0x333;
- break;
- case 20:
- mask = 0x0ff;
- break;
- case 25:
- mask = 0x3ff;
- break;
- default:
- mask = VINO_FRAMERT_FULL;
- }
- vcs->framert_reg = VINO_FRAMERT_RT(mask) | VINO_FRAMERT_PAL;
- break;
- }
-
- vcs->fps = fps;
-}
-
-/* execute with input_lock locked */
-static inline void vino_set_default_framerate(struct
- vino_channel_settings *vcs)
-{
- vino_set_framerate(vcs, vino_data_norms[vcs->data_norm].fps_max);
-}
-
-/* VINO I2C bus functions */
-
-struct i2c_algo_sgi_data {
- void *data; /* private data for lowlevel routines */
- unsigned (*getctrl)(void *data);
- void (*setctrl)(void *data, unsigned val);
- unsigned (*rdata)(void *data);
- void (*wdata)(void *data, unsigned val);
-
- int xfer_timeout;
- int ack_timeout;
-};
-
-static int wait_xfer_done(struct i2c_algo_sgi_data *adap)
-{
- int i;
-
- for (i = 0; i < adap->xfer_timeout; i++) {
- if ((adap->getctrl(adap->data) & SGI_I2C_XFER_BUSY) == 0)
- return 0;
- udelay(1);
- }
-
- return -ETIMEDOUT;
-}
-
-static int wait_ack(struct i2c_algo_sgi_data *adap)
-{
- int i;
-
- if (wait_xfer_done(adap))
- return -ETIMEDOUT;
- for (i = 0; i < adap->ack_timeout; i++) {
- if ((adap->getctrl(adap->data) & SGI_I2C_NACK) == 0)
- return 0;
- udelay(1);
- }
-
- return -ETIMEDOUT;
-}
-
-static int force_idle(struct i2c_algo_sgi_data *adap)
-{
- int i;
-
- adap->setctrl(adap->data, SGI_I2C_FORCE_IDLE);
- for (i = 0; i < adap->xfer_timeout; i++) {
- if ((adap->getctrl(adap->data) & SGI_I2C_NOT_IDLE) == 0)
- goto out;
- udelay(1);
- }
- return -ETIMEDOUT;
-out:
- if (adap->getctrl(adap->data) & SGI_I2C_BUS_ERR)
- return -EIO;
- return 0;
-}
-
-static int do_address(struct i2c_algo_sgi_data *adap, unsigned int addr,
- int rd)
-{
- if (rd)
- adap->setctrl(adap->data, SGI_I2C_NOT_IDLE);
- /* Check if bus is idle, eventually force it to do so */
- if (adap->getctrl(adap->data) & SGI_I2C_NOT_IDLE)
- if (force_idle(adap))
- return -EIO;
- /* Write out the i2c chip address and specify operation */
- adap->setctrl(adap->data,
- SGI_I2C_HOLD_BUS | SGI_I2C_WRITE | SGI_I2C_NOT_IDLE);
- if (rd)
- addr |= 1;
- adap->wdata(adap->data, addr);
- if (wait_ack(adap))
- return -EIO;
- return 0;
-}
-
-static int i2c_read(struct i2c_algo_sgi_data *adap, unsigned char *buf,
- unsigned int len)
-{
- int i;
-
- adap->setctrl(adap->data,
- SGI_I2C_HOLD_BUS | SGI_I2C_READ | SGI_I2C_NOT_IDLE);
- for (i = 0; i < len; i++) {
- if (wait_xfer_done(adap))
- return -EIO;
- buf[i] = adap->rdata(adap->data);
- }
- adap->setctrl(adap->data, SGI_I2C_RELEASE_BUS | SGI_I2C_FORCE_IDLE);
-
- return 0;
-
-}
-
-static int i2c_write(struct i2c_algo_sgi_data *adap, unsigned char *buf,
- unsigned int len)
-{
- int i;
-
- /* We are already in write state */
- for (i = 0; i < len; i++) {
- adap->wdata(adap->data, buf[i]);
- if (wait_ack(adap))
- return -EIO;
- }
- return 0;
-}
-
-static int sgi_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
- int num)
-{
- struct i2c_algo_sgi_data *adap = i2c_adap->algo_data;
- struct i2c_msg *p;
- int i, err = 0;
-
- for (i = 0; !err && i < num; i++) {
- p = &msgs[i];
- err = do_address(adap, p->addr, p->flags & I2C_M_RD);
- if (err || !p->len)
- continue;
- if (p->flags & I2C_M_RD)
- err = i2c_read(adap, p->buf, p->len);
- else
- err = i2c_write(adap, p->buf, p->len);
- }
-
- return (err < 0) ? err : i;
-}
-
-static u32 sgi_func(struct i2c_adapter *adap)
-{
- return I2C_FUNC_SMBUS_EMUL;
-}
-
-static const struct i2c_algorithm sgi_algo = {
- .master_xfer = sgi_xfer,
- .functionality = sgi_func,
-};
-
-static unsigned i2c_vino_getctrl(void *data)
-{
- return vino->i2c_control;
-}
-
-static void i2c_vino_setctrl(void *data, unsigned val)
-{
- vino->i2c_control = val;
-}
-
-static unsigned i2c_vino_rdata(void *data)
-{
- return vino->i2c_data;
-}
-
-static void i2c_vino_wdata(void *data, unsigned val)
-{
- vino->i2c_data = val;
-}
-
-static struct i2c_algo_sgi_data i2c_sgi_vino_data = {
- .getctrl = &i2c_vino_getctrl,
- .setctrl = &i2c_vino_setctrl,
- .rdata = &i2c_vino_rdata,
- .wdata = &i2c_vino_wdata,
- .xfer_timeout = 200,
- .ack_timeout = 1000,
-};
-
-static struct i2c_adapter vino_i2c_adapter = {
- .name = "VINO I2C bus",
- .algo = &sgi_algo,
- .algo_data = &i2c_sgi_vino_data,
- .owner = THIS_MODULE,
-};
-
-/*
- * Prepare VINO for DMA transfer...
- * (execute only with vino_lock and input_lock locked)
- */
-static int vino_dma_setup(struct vino_channel_settings *vcs,
- struct vino_framebuffer *fb)
-{
- u32 ctrl, intr;
- struct sgi_vino_channel *ch;
- const struct vino_data_norm *norm;
-
- dprintk("vino_dma_setup():\n");
-
- vcs->field = 0;
- fb->frame_counter = 0;
-
- ch = (vcs->channel == VINO_CHANNEL_A) ? &vino->a : &vino->b;
- norm = &vino_data_norms[vcs->data_norm];
-
- ch->page_index = 0;
- ch->line_count = 0;
-
- /* VINO line size register is set 8 bytes less than actual */
- ch->line_size = vcs->line_size - 8;
-
- /* let VINO know where to transfer data */
- ch->start_desc_tbl = fb->desc_table.dma;
- ch->next_4_desc = fb->desc_table.dma;
-
- /* give vino time to fetch the first four descriptors, 5 usec
- * should be more than enough time */
- udelay(VINO_DESC_FETCH_DELAY);
-
- dprintk("vino_dma_setup(): start desc = %08x, next 4 desc = %08x\n",
- ch->start_desc_tbl, ch->next_4_desc);
-
- /* set the alpha register */
- ch->alpha = vcs->alpha;
-
- /* set clipping registers */
- ch->clip_start = VINO_CLIP_ODD(norm->odd.top + vcs->clipping.top / 2) |
- VINO_CLIP_EVEN(norm->even.top +
- vcs->clipping.top / 2) |
- VINO_CLIP_X(vcs->clipping.left);
- ch->clip_end = VINO_CLIP_ODD(norm->odd.top +
- vcs->clipping.bottom / 2 - 1) |
- VINO_CLIP_EVEN(norm->even.top +
- vcs->clipping.bottom / 2 - 1) |
- VINO_CLIP_X(vcs->clipping.right);
-
- /* set the size of actual content in the buffer (DECIMATION !) */
- fb->data_size = ((vcs->clipping.right - vcs->clipping.left) /
- vcs->decimation) *
- ((vcs->clipping.bottom - vcs->clipping.top) /
- vcs->decimation) *
- vino_data_formats[vcs->data_format].bpp;
-
- ch->frame_rate = vcs->framert_reg;
-
- ctrl = vino->control;
- intr = vino->intr_status;
-
- if (vcs->channel == VINO_CHANNEL_A) {
- /* All interrupt conditions for this channel was cleared
- * so clear the interrupt status register and enable
- * interrupts */
- intr &= ~VINO_INTSTAT_A;
- ctrl |= VINO_CTRL_A_INT;
-
- /* enable synchronization */
- ctrl |= VINO_CTRL_A_SYNC_ENBL;
-
- /* enable frame assembly */
- ctrl |= VINO_CTRL_A_INTERLEAVE_ENBL;
-
- /* set decimation used */
- if (vcs->decimation < 2)
- ctrl &= ~VINO_CTRL_A_DEC_ENBL;
- else {
- ctrl |= VINO_CTRL_A_DEC_ENBL;
- ctrl &= ~VINO_CTRL_A_DEC_SCALE_MASK;
- ctrl |= (vcs->decimation - 1) <<
- VINO_CTRL_A_DEC_SCALE_SHIFT;
- }
-
- /* select input interface */
- if (vcs->input == VINO_INPUT_D1)
- ctrl |= VINO_CTRL_A_SELECT;
- else
- ctrl &= ~VINO_CTRL_A_SELECT;
-
- /* palette */
- ctrl &= ~(VINO_CTRL_A_LUMA_ONLY | VINO_CTRL_A_RGB |
- VINO_CTRL_A_DITHER);
- } else {
- intr &= ~VINO_INTSTAT_B;
- ctrl |= VINO_CTRL_B_INT;
-
- ctrl |= VINO_CTRL_B_SYNC_ENBL;
- ctrl |= VINO_CTRL_B_INTERLEAVE_ENBL;
-
- if (vcs->decimation < 2)
- ctrl &= ~VINO_CTRL_B_DEC_ENBL;
- else {
- ctrl |= VINO_CTRL_B_DEC_ENBL;
- ctrl &= ~VINO_CTRL_B_DEC_SCALE_MASK;
- ctrl |= (vcs->decimation - 1) <<
- VINO_CTRL_B_DEC_SCALE_SHIFT;
-
- }
- if (vcs->input == VINO_INPUT_D1)
- ctrl |= VINO_CTRL_B_SELECT;
- else
- ctrl &= ~VINO_CTRL_B_SELECT;
-
- ctrl &= ~(VINO_CTRL_B_LUMA_ONLY | VINO_CTRL_B_RGB |
- VINO_CTRL_B_DITHER);
- }
-
- /* set palette */
- fb->data_format = vcs->data_format;
-
- switch (vcs->data_format) {
- case VINO_DATA_FMT_GREY:
- ctrl |= (vcs->channel == VINO_CHANNEL_A) ?
- VINO_CTRL_A_LUMA_ONLY : VINO_CTRL_B_LUMA_ONLY;
- break;
- case VINO_DATA_FMT_RGB32:
- ctrl |= (vcs->channel == VINO_CHANNEL_A) ?
- VINO_CTRL_A_RGB : VINO_CTRL_B_RGB;
- break;
- case VINO_DATA_FMT_YUV:
- /* nothing needs to be done */
- break;
- case VINO_DATA_FMT_RGB332:
- ctrl |= (vcs->channel == VINO_CHANNEL_A) ?
- VINO_CTRL_A_RGB | VINO_CTRL_A_DITHER :
- VINO_CTRL_B_RGB | VINO_CTRL_B_DITHER;
- break;
- }
-
- vino->intr_status = intr;
- vino->control = ctrl;
-
- return 0;
-}
-
-/* (execute only with vino_lock locked) */
-static inline void vino_dma_start(struct vino_channel_settings *vcs)
-{
- u32 ctrl = vino->control;
-
- dprintk("vino_dma_start():\n");
- ctrl |= (vcs->channel == VINO_CHANNEL_A) ?
- VINO_CTRL_A_DMA_ENBL : VINO_CTRL_B_DMA_ENBL;
- vino->control = ctrl;
-}
-
-/* (execute only with vino_lock locked) */
-static inline void vino_dma_stop(struct vino_channel_settings *vcs)
-{
- u32 ctrl = vino->control;
-
- ctrl &= (vcs->channel == VINO_CHANNEL_A) ?
- ~VINO_CTRL_A_DMA_ENBL : ~VINO_CTRL_B_DMA_ENBL;
- ctrl &= (vcs->channel == VINO_CHANNEL_A) ?
- ~VINO_CTRL_A_INT : ~VINO_CTRL_B_INT;
- vino->control = ctrl;
- dprintk("vino_dma_stop():\n");
-}
-
-/*
- * Load dummy page to descriptor registers. This prevents generating of
- * spurious interrupts. (execute only with vino_lock locked)
- */
-static void vino_clear_interrupt(struct vino_channel_settings *vcs)
-{
- struct sgi_vino_channel *ch;
-
- ch = (vcs->channel == VINO_CHANNEL_A) ? &vino->a : &vino->b;
-
- ch->page_index = 0;
- ch->line_count = 0;
-
- ch->start_desc_tbl = vino_drvdata->dummy_desc_table.dma;
- ch->next_4_desc = vino_drvdata->dummy_desc_table.dma;
-
- udelay(VINO_DESC_FETCH_DELAY);
- dprintk("channel %c clear interrupt condition\n",
- (vcs->channel == VINO_CHANNEL_A) ? 'A':'B');
-}
-
-static int vino_capture(struct vino_channel_settings *vcs,
- struct vino_framebuffer *fb)
-{
- int err = 0;
- unsigned long flags, flags2;
-
- spin_lock_irqsave(&fb->state_lock, flags);
-
- if (fb->state == VINO_FRAMEBUFFER_IN_USE)
- err = -EBUSY;
- fb->state = VINO_FRAMEBUFFER_IN_USE;
-
- spin_unlock_irqrestore(&fb->state_lock, flags);
-
- if (err)
- return err;
-
- spin_lock_irqsave(&vino_drvdata->vino_lock, flags);
- spin_lock_irqsave(&vino_drvdata->input_lock, flags2);
-
- vino_dma_setup(vcs, fb);
- vino_dma_start(vcs);
-
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags2);
- spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags);
-
- return err;
-}
-
-static
-struct vino_framebuffer *vino_capture_enqueue(struct
- vino_channel_settings *vcs,
- unsigned int index)
-{
- struct vino_framebuffer *fb;
- unsigned long flags;
-
- dprintk("vino_capture_enqueue():\n");
-
- spin_lock_irqsave(&vcs->capture_lock, flags);
-
- fb = vino_queue_add(&vcs->fb_queue, index);
- if (fb == NULL) {
- dprintk("vino_capture_enqueue(): vino_queue_add() failed, "
- "queue full?\n");
- goto out;
- }
-out:
- spin_unlock_irqrestore(&vcs->capture_lock, flags);
-
- return fb;
-}
-
-static int vino_capture_next(struct vino_channel_settings *vcs, int start)
-{
- struct vino_framebuffer *fb;
- unsigned int incoming, id;
- int err = 0;
- unsigned long flags;
-
- dprintk("vino_capture_next():\n");
-
- spin_lock_irqsave(&vcs->capture_lock, flags);
-
- if (start) {
- /* start capture only if capture isn't in progress already */
- if (vcs->capturing) {
- spin_unlock_irqrestore(&vcs->capture_lock, flags);
- return 0;
- }
-
- } else {
- /* capture next frame:
- * stop capture if capturing is not set */
- if (!vcs->capturing) {
- spin_unlock_irqrestore(&vcs->capture_lock, flags);
- return 0;
- }
- }
-
- err = vino_queue_get_incoming(&vcs->fb_queue, &incoming);
- if (err) {
- dprintk("vino_capture_next(): vino_queue_get_incoming() "
- "failed\n");
- err = -EINVAL;
- goto out;
- }
- if (incoming == 0) {
- dprintk("vino_capture_next(): no buffers available\n");
- goto out;
- }
-
- fb = vino_queue_peek(&vcs->fb_queue, &id);
- if (fb == NULL) {
- dprintk("vino_capture_next(): vino_queue_peek() failed\n");
- err = -EINVAL;
- goto out;
- }
-
- if (start) {
- vcs->capturing = 1;
- }
-
- spin_unlock_irqrestore(&vcs->capture_lock, flags);
-
- err = vino_capture(vcs, fb);
-
- return err;
-
-out:
- vcs->capturing = 0;
- spin_unlock_irqrestore(&vcs->capture_lock, flags);
-
- return err;
-}
-
-static inline int vino_is_capturing(struct vino_channel_settings *vcs)
-{
- int ret;
- unsigned long flags;
-
- spin_lock_irqsave(&vcs->capture_lock, flags);
-
- ret = vcs->capturing;
-
- spin_unlock_irqrestore(&vcs->capture_lock, flags);
-
- return ret;
-}
-
-/* waits until a frame is captured */
-static int vino_wait_for_frame(struct vino_channel_settings *vcs)
-{
- wait_queue_t wait;
- int err = 0;
-
- dprintk("vino_wait_for_frame():\n");
-
- init_waitqueue_entry(&wait, current);
- /* add ourselves into wait queue */
- add_wait_queue(&vcs->fb_queue.frame_wait_queue, &wait);
-
- /* to ensure that schedule_timeout will return immediately
- * if VINO interrupt was triggered meanwhile */
- schedule_timeout_interruptible(msecs_to_jiffies(100));
-
- if (signal_pending(current))
- err = -EINTR;
-
- remove_wait_queue(&vcs->fb_queue.frame_wait_queue, &wait);
-
- dprintk("vino_wait_for_frame(): waiting for frame %s\n",
- err ? "failed" : "ok");
-
- return err;
-}
-
-/* the function assumes that PAGE_SIZE % 4 == 0 */
-static void vino_convert_to_rgba(struct vino_framebuffer *fb) {
- unsigned char *pageptr;
- unsigned int page, i;
- unsigned char a;
-
- for (page = 0; page < fb->desc_table.page_count; page++) {
- pageptr = (unsigned char *)fb->desc_table.virtual[page];
-
- for (i = 0; i < PAGE_SIZE; i += 4) {
- a = pageptr[0];
- pageptr[0] = pageptr[3];
- pageptr[1] = pageptr[2];
- pageptr[2] = pageptr[1];
- pageptr[3] = a;
- pageptr += 4;
- }
- }
-}
-
-/* checks if the buffer is in correct state and syncs data */
-static int vino_check_buffer(struct vino_channel_settings *vcs,
- struct vino_framebuffer *fb)
-{
- int err = 0;
- unsigned long flags;
-
- dprintk("vino_check_buffer():\n");
-
- spin_lock_irqsave(&fb->state_lock, flags);
- switch (fb->state) {
- case VINO_FRAMEBUFFER_IN_USE:
- err = -EIO;
- break;
- case VINO_FRAMEBUFFER_READY:
- vino_sync_buffer(fb);
- fb->state = VINO_FRAMEBUFFER_UNUSED;
- break;
- default:
- err = -EINVAL;
- }
- spin_unlock_irqrestore(&fb->state_lock, flags);
-
- if (!err) {
- if (vino_pixel_conversion
- && (fb->data_format == VINO_DATA_FMT_RGB32)) {
- vino_convert_to_rgba(fb);
- }
- } else if (err && (err != -EINVAL)) {
- dprintk("vino_check_buffer(): buffer not ready\n");
-
- spin_lock_irqsave(&vino_drvdata->vino_lock, flags);
- vino_dma_stop(vcs);
- vino_clear_interrupt(vcs);
- spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags);
- }
-
- return err;
-}
-
-/* forcefully terminates capture */
-static void vino_capture_stop(struct vino_channel_settings *vcs)
-{
- unsigned int incoming = 0, outgoing = 0, id;
- unsigned long flags, flags2;
-
- dprintk("vino_capture_stop():\n");
-
- spin_lock_irqsave(&vcs->capture_lock, flags);
-
- /* unset capturing to stop queue processing */
- vcs->capturing = 0;
-
- spin_lock_irqsave(&vino_drvdata->vino_lock, flags2);
-
- vino_dma_stop(vcs);
- vino_clear_interrupt(vcs);
-
- spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags2);
-
- /* remove all items from the queue */
- if (vino_queue_get_incoming(&vcs->fb_queue, &incoming)) {
- dprintk("vino_capture_stop(): "
- "vino_queue_get_incoming() failed\n");
- goto out;
- }
- while (incoming > 0) {
- vino_queue_transfer(&vcs->fb_queue);
-
- if (vino_queue_get_incoming(&vcs->fb_queue, &incoming)) {
- dprintk("vino_capture_stop(): "
- "vino_queue_get_incoming() failed\n");
- goto out;
- }
- }
-
- if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) {
- dprintk("vino_capture_stop(): "
- "vino_queue_get_outgoing() failed\n");
- goto out;
- }
- while (outgoing > 0) {
- vino_queue_remove(&vcs->fb_queue, &id);
-
- if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) {
- dprintk("vino_capture_stop(): "
- "vino_queue_get_outgoing() failed\n");
- goto out;
- }
- }
-
-out:
- spin_unlock_irqrestore(&vcs->capture_lock, flags);
-}
-
-#if 0
-static int vino_capture_failed(struct vino_channel_settings *vcs)
-{
- struct vino_framebuffer *fb;
- unsigned long flags;
- unsigned int i;
- int ret;
-
- dprintk("vino_capture_failed():\n");
-
- spin_lock_irqsave(&vino_drvdata->vino_lock, flags);
-
- vino_dma_stop(vcs);
- vino_clear_interrupt(vcs);
-
- spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags);
-
- ret = vino_queue_get_incoming(&vcs->fb_queue, &i);
- if (ret == VINO_QUEUE_ERROR) {
- dprintk("vino_queue_get_incoming() failed\n");
- return -EINVAL;
- }
- if (i == 0) {
- /* no buffers to process */
- return 0;
- }
-
- fb = vino_queue_peek(&vcs->fb_queue, &i);
- if (fb == NULL) {
- dprintk("vino_queue_peek() failed\n");
- return -EINVAL;
- }
-
- spin_lock_irqsave(&fb->state_lock, flags);
- if (fb->state == VINO_FRAMEBUFFER_IN_USE) {
- fb->state = VINO_FRAMEBUFFER_UNUSED;
- vino_queue_transfer(&vcs->fb_queue);
- vino_queue_remove(&vcs->fb_queue, &i);
- /* we should actually discard the newest frame,
- * but who cares ... */
- }
- spin_unlock_irqrestore(&fb->state_lock, flags);
-
- return 0;
-}
-#endif
-
-static void vino_skip_frame(struct vino_channel_settings *vcs)
-{
- struct vino_framebuffer *fb;
- unsigned long flags;
- unsigned int id;
-
- spin_lock_irqsave(&vcs->capture_lock, flags);
- fb = vino_queue_peek(&vcs->fb_queue, &id);
- if (!fb) {
- spin_unlock_irqrestore(&vcs->capture_lock, flags);
- dprintk("vino_skip_frame(): vino_queue_peek() failed!\n");
- return;
- }
- spin_unlock_irqrestore(&vcs->capture_lock, flags);
-
- spin_lock_irqsave(&fb->state_lock, flags);
- fb->state = VINO_FRAMEBUFFER_UNUSED;
- spin_unlock_irqrestore(&fb->state_lock, flags);
-
- vino_capture_next(vcs, 0);
-}
-
-static void vino_frame_done(struct vino_channel_settings *vcs)
-{
- struct vino_framebuffer *fb;
- unsigned long flags;
-
- spin_lock_irqsave(&vcs->capture_lock, flags);
- fb = vino_queue_transfer(&vcs->fb_queue);
- if (!fb) {
- spin_unlock_irqrestore(&vcs->capture_lock, flags);
- dprintk("vino_frame_done(): vino_queue_transfer() failed!\n");
- return;
- }
- spin_unlock_irqrestore(&vcs->capture_lock, flags);
-
- fb->frame_counter = vcs->int_data.frame_counter;
- memcpy(&fb->timestamp, &vcs->int_data.timestamp,
- sizeof(struct timeval));
-
- spin_lock_irqsave(&fb->state_lock, flags);
- if (fb->state == VINO_FRAMEBUFFER_IN_USE)
- fb->state = VINO_FRAMEBUFFER_READY;
- spin_unlock_irqrestore(&fb->state_lock, flags);
-
- wake_up(&vcs->fb_queue.frame_wait_queue);
-
- vino_capture_next(vcs, 0);
-}
-
-static void vino_capture_tasklet(unsigned long channel) {
- struct vino_channel_settings *vcs;
-
- vcs = (channel == VINO_CHANNEL_A)
- ? &vino_drvdata->a : &vino_drvdata->b;
-
- if (vcs->int_data.skip)
- vcs->int_data.skip_count++;
-
- if (vcs->int_data.skip && (vcs->int_data.skip_count
- <= VINO_MAX_FRAME_SKIP_COUNT)) {
- vino_skip_frame(vcs);
- } else {
- vcs->int_data.skip_count = 0;
- vino_frame_done(vcs);
- }
-}
-
-static irqreturn_t vino_interrupt(int irq, void *dev_id)
-{
- u32 ctrl, intr;
- unsigned int fc_a, fc_b;
- int handled_a = 0, skip_a = 0, done_a = 0;
- int handled_b = 0, skip_b = 0, done_b = 0;
-
-#ifdef VINO_DEBUG_INT
- int loop = 0;
- unsigned int line_count = vino->a.line_count,
- page_index = vino->a.page_index,
- field_counter = vino->a.field_counter,
- start_desc_tbl = vino->a.start_desc_tbl,
- next_4_desc = vino->a.next_4_desc;
- unsigned int line_count_2,
- page_index_2,
- field_counter_2,
- start_desc_tbl_2,
- next_4_desc_2;
-#endif
-
- spin_lock(&vino_drvdata->vino_lock);
-
- while ((intr = vino->intr_status)) {
- fc_a = vino->a.field_counter >> 1;
- fc_b = vino->b.field_counter >> 1;
-
- /* handle error-interrupts in some special way ?
- * --> skips frames */
- if (intr & VINO_INTSTAT_A) {
- if (intr & VINO_INTSTAT_A_EOF) {
- vino_drvdata->a.field++;
- if (vino_drvdata->a.field > 1) {
- vino_dma_stop(&vino_drvdata->a);
- vino_clear_interrupt(&vino_drvdata->a);
- vino_drvdata->a.field = 0;
- done_a = 1;
- } else {
- if (vino->a.page_index
- != vino_drvdata->a.line_size) {
- vino->a.line_count = 0;
- vino->a.page_index =
- vino_drvdata->
- a.line_size;
- vino->a.next_4_desc =
- vino->a.start_desc_tbl;
- }
- }
- dprintk("channel A end-of-field "
- "interrupt: %04x\n", intr);
- } else {
- vino_dma_stop(&vino_drvdata->a);
- vino_clear_interrupt(&vino_drvdata->a);
- vino_drvdata->a.field = 0;
- skip_a = 1;
- dprintk("channel A error interrupt: %04x\n",
- intr);
- }
-
-#ifdef VINO_DEBUG_INT
- line_count_2 = vino->a.line_count;
- page_index_2 = vino->a.page_index;
- field_counter_2 = vino->a.field_counter;
- start_desc_tbl_2 = vino->a.start_desc_tbl;
- next_4_desc_2 = vino->a.next_4_desc;
-
- printk("intr = %04x, loop = %d, field = %d\n",
- intr, loop, vino_drvdata->a.field);
- printk("1- line count = %04d, page index = %04d, "
- "start = %08x, next = %08x\n"
- " fieldc = %d, framec = %d\n",
- line_count, page_index, start_desc_tbl,
- next_4_desc, field_counter, fc_a);
- printk("12-line count = %04d, page index = %04d, "
- " start = %08x, next = %08x\n",
- line_count_2, page_index_2, start_desc_tbl_2,
- next_4_desc_2);
-
- if (done_a)
- printk("\n");
-#endif
- }
-
- if (intr & VINO_INTSTAT_B) {
- if (intr & VINO_INTSTAT_B_EOF) {
- vino_drvdata->b.field++;
- if (vino_drvdata->b.field > 1) {
- vino_dma_stop(&vino_drvdata->b);
- vino_clear_interrupt(&vino_drvdata->b);
- vino_drvdata->b.field = 0;
- done_b = 1;
- }
- dprintk("channel B end-of-field "
- "interrupt: %04x\n", intr);
- } else {
- vino_dma_stop(&vino_drvdata->b);
- vino_clear_interrupt(&vino_drvdata->b);
- vino_drvdata->b.field = 0;
- skip_b = 1;
- dprintk("channel B error interrupt: %04x\n",
- intr);
- }
- }
-
- /* Always remember to clear interrupt status.
- * Disable VINO interrupts while we do this. */
- ctrl = vino->control;
- vino->control = ctrl & ~(VINO_CTRL_A_INT | VINO_CTRL_B_INT);
- vino->intr_status = ~intr;
- vino->control = ctrl;
-
- spin_unlock(&vino_drvdata->vino_lock);
-
- if ((!handled_a) && (done_a || skip_a)) {
- if (!skip_a) {
- v4l2_get_timestamp(
- &vino_drvdata->a.int_data.timestamp);
- vino_drvdata->a.int_data.frame_counter = fc_a;
- }
- vino_drvdata->a.int_data.skip = skip_a;
-
- dprintk("channel A %s, interrupt: %d\n",
- skip_a ? "skipping frame" : "frame done",
- intr);
- tasklet_hi_schedule(&vino_tasklet_a);
- handled_a = 1;
- }
-
- if ((!handled_b) && (done_b || skip_b)) {
- if (!skip_b) {
- v4l2_get_timestamp(
- &vino_drvdata->b.int_data.timestamp);
- vino_drvdata->b.int_data.frame_counter = fc_b;
- }
- vino_drvdata->b.int_data.skip = skip_b;
-
- dprintk("channel B %s, interrupt: %d\n",
- skip_b ? "skipping frame" : "frame done",
- intr);
- tasklet_hi_schedule(&vino_tasklet_b);
- handled_b = 1;
- }
-
-#ifdef VINO_DEBUG_INT
- loop++;
-#endif
- spin_lock(&vino_drvdata->vino_lock);
- }
-
- spin_unlock(&vino_drvdata->vino_lock);
-
- return IRQ_HANDLED;
-}
-
-/* VINO video input management */
-
-static int vino_get_saa7191_input(int input)
-{
- switch (input) {
- case VINO_INPUT_COMPOSITE:
- return SAA7191_INPUT_COMPOSITE;
- case VINO_INPUT_SVIDEO:
- return SAA7191_INPUT_SVIDEO;
- default:
- printk(KERN_ERR "VINO: vino_get_saa7191_input(): "
- "invalid input!\n");
- return -1;
- }
-}
-
-/* execute with input_lock locked */
-static int vino_is_input_owner(struct vino_channel_settings *vcs)
-{
- switch(vcs->input) {
- case VINO_INPUT_COMPOSITE:
- case VINO_INPUT_SVIDEO:
- return vino_drvdata->decoder_owner == vcs->channel;
- case VINO_INPUT_D1:
- return vino_drvdata->camera_owner == vcs->channel;
- default:
- return 0;
- }
-}
-
-static int vino_acquire_input(struct vino_channel_settings *vcs)
-{
- unsigned long flags;
- int ret = 0;
-
- dprintk("vino_acquire_input():\n");
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- /* First try D1 and then SAA7191 */
- if (vino_drvdata->camera
- && (vino_drvdata->camera_owner == VINO_NO_CHANNEL)) {
- vino_drvdata->camera_owner = vcs->channel;
- vcs->input = VINO_INPUT_D1;
- vcs->data_norm = VINO_DATA_NORM_D1;
- } else if (vino_drvdata->decoder
- && (vino_drvdata->decoder_owner == VINO_NO_CHANNEL)) {
- int input;
- int data_norm = 0;
- v4l2_std_id norm;
-
- input = VINO_INPUT_COMPOSITE;
-
- ret = decoder_call(video, s_routing,
- vino_get_saa7191_input(input), 0, 0);
- if (ret) {
- ret = -EINVAL;
- goto out;
- }
-
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- /* Don't hold spinlocks while auto-detecting norm
- * as it may take a while... */
-
- ret = decoder_call(video, querystd, &norm);
- if (!ret) {
- for (data_norm = 0; data_norm < 3; data_norm++) {
- if (vino_data_norms[data_norm].std & norm)
- break;
- }
- if (data_norm == 3)
- data_norm = VINO_DATA_NORM_PAL;
- ret = decoder_call(video, s_std, norm);
- }
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- if (ret) {
- ret = -EINVAL;
- goto out;
- }
-
- vino_drvdata->decoder_owner = vcs->channel;
-
- vcs->input = input;
- vcs->data_norm = data_norm;
- } else {
- vcs->input = (vcs->channel == VINO_CHANNEL_A) ?
- vino_drvdata->b.input : vino_drvdata->a.input;
- vcs->data_norm = (vcs->channel == VINO_CHANNEL_A) ?
- vino_drvdata->b.data_norm : vino_drvdata->a.data_norm;
- }
-
- if (vcs->input == VINO_INPUT_NONE) {
- ret = -ENODEV;
- goto out;
- }
-
- vino_set_default_clipping(vcs);
- vino_set_default_scaling(vcs);
- vino_set_default_framerate(vcs);
-
- dprintk("vino_acquire_input(): %s\n", vino_inputs[vcs->input].name);
-
-out:
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- return ret;
-}
-
-static int vino_set_input(struct vino_channel_settings *vcs, int input)
-{
- struct vino_channel_settings *vcs2 = (vcs->channel == VINO_CHANNEL_A) ?
- &vino_drvdata->b : &vino_drvdata->a;
- unsigned long flags;
- int ret = 0;
-
- dprintk("vino_set_input():\n");
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- if (vcs->input == input)
- goto out;
-
- switch (input) {
- case VINO_INPUT_COMPOSITE:
- case VINO_INPUT_SVIDEO:
- if (!vino_drvdata->decoder) {
- ret = -EINVAL;
- goto out;
- }
-
- if (vino_drvdata->decoder_owner == VINO_NO_CHANNEL) {
- vino_drvdata->decoder_owner = vcs->channel;
- }
-
- if (vino_drvdata->decoder_owner == vcs->channel) {
- int data_norm = 0;
- v4l2_std_id norm;
-
- ret = decoder_call(video, s_routing,
- vino_get_saa7191_input(input), 0, 0);
- if (ret) {
- vino_drvdata->decoder_owner = VINO_NO_CHANNEL;
- ret = -EINVAL;
- goto out;
- }
-
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- /* Don't hold spinlocks while auto-detecting norm
- * as it may take a while... */
-
- ret = decoder_call(video, querystd, &norm);
- if (!ret) {
- for (data_norm = 0; data_norm < 3; data_norm++) {
- if (vino_data_norms[data_norm].std & norm)
- break;
- }
- if (data_norm == 3)
- data_norm = VINO_DATA_NORM_PAL;
- ret = decoder_call(video, s_std, norm);
- }
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- if (ret) {
- vino_drvdata->decoder_owner = VINO_NO_CHANNEL;
- ret = -EINVAL;
- goto out;
- }
-
- vcs->input = input;
- vcs->data_norm = data_norm;
- } else {
- if (input != vcs2->input) {
- ret = -EBUSY;
- goto out;
- }
-
- vcs->input = input;
- vcs->data_norm = vcs2->data_norm;
- }
-
- if (vino_drvdata->camera_owner == vcs->channel) {
- /* Transfer the ownership or release the input */
- if (vcs2->input == VINO_INPUT_D1) {
- vino_drvdata->camera_owner = vcs2->channel;
- } else {
- vino_drvdata->camera_owner = VINO_NO_CHANNEL;
- }
- }
- break;
- case VINO_INPUT_D1:
- if (!vino_drvdata->camera) {
- ret = -EINVAL;
- goto out;
- }
-
- if (vino_drvdata->camera_owner == VINO_NO_CHANNEL)
- vino_drvdata->camera_owner = vcs->channel;
-
- if (vino_drvdata->decoder_owner == vcs->channel) {
- /* Transfer the ownership or release the input */
- if ((vcs2->input == VINO_INPUT_COMPOSITE) ||
- (vcs2->input == VINO_INPUT_SVIDEO)) {
- vino_drvdata->decoder_owner = vcs2->channel;
- } else {
- vino_drvdata->decoder_owner = VINO_NO_CHANNEL;
- }
- }
-
- vcs->input = input;
- vcs->data_norm = VINO_DATA_NORM_D1;
- break;
- default:
- ret = -EINVAL;
- goto out;
- }
-
- vino_set_default_clipping(vcs);
- vino_set_default_scaling(vcs);
- vino_set_default_framerate(vcs);
-
- dprintk("vino_set_input(): %s\n", vino_inputs[vcs->input].name);
-
-out:
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- return ret;
-}
-
-static void vino_release_input(struct vino_channel_settings *vcs)
-{
- struct vino_channel_settings *vcs2 = (vcs->channel == VINO_CHANNEL_A) ?
- &vino_drvdata->b : &vino_drvdata->a;
- unsigned long flags;
-
- dprintk("vino_release_input():\n");
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- /* Release ownership of the channel
- * and if the other channel takes input from
- * the same source, transfer the ownership */
- if (vino_drvdata->camera_owner == vcs->channel) {
- if (vcs2->input == VINO_INPUT_D1) {
- vino_drvdata->camera_owner = vcs2->channel;
- } else {
- vino_drvdata->camera_owner = VINO_NO_CHANNEL;
- }
- } else if (vino_drvdata->decoder_owner == vcs->channel) {
- if ((vcs2->input == VINO_INPUT_COMPOSITE) ||
- (vcs2->input == VINO_INPUT_SVIDEO)) {
- vino_drvdata->decoder_owner = vcs2->channel;
- } else {
- vino_drvdata->decoder_owner = VINO_NO_CHANNEL;
- }
- }
- vcs->input = VINO_INPUT_NONE;
-
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-}
-
-/* execute with input_lock locked */
-static int vino_set_data_norm(struct vino_channel_settings *vcs,
- unsigned int data_norm,
- unsigned long *flags)
-{
- int err = 0;
-
- if (data_norm == vcs->data_norm)
- return 0;
-
- switch (vcs->input) {
- case VINO_INPUT_D1:
- /* only one "norm" supported */
- if (data_norm != VINO_DATA_NORM_D1)
- return -EINVAL;
- break;
- case VINO_INPUT_COMPOSITE:
- case VINO_INPUT_SVIDEO: {
- v4l2_std_id norm;
-
- if ((data_norm != VINO_DATA_NORM_PAL)
- && (data_norm != VINO_DATA_NORM_NTSC)
- && (data_norm != VINO_DATA_NORM_SECAM))
- return -EINVAL;
-
- spin_unlock_irqrestore(&vino_drvdata->input_lock, *flags);
-
- /* Don't hold spinlocks while setting norm
- * as it may take a while... */
-
- norm = vino_data_norms[data_norm].std;
- err = decoder_call(video, s_std, norm);
-
- spin_lock_irqsave(&vino_drvdata->input_lock, *flags);
-
- if (err)
- goto out;
-
- vcs->data_norm = data_norm;
-
- vino_set_default_clipping(vcs);
- vino_set_default_scaling(vcs);
- vino_set_default_framerate(vcs);
- break;
- }
- default:
- return -EINVAL;
- }
-
-out:
- return err;
-}
-
-/* V4L2 helper functions */
-
-static int vino_find_data_format(__u32 pixelformat)
-{
- int i;
-
- for (i = 0; i < VINO_DATA_FMT_COUNT; i++) {
- if (vino_data_formats[i].pixelformat == pixelformat)
- return i;
- }
-
- return VINO_DATA_FMT_NONE;
-}
-
-static int vino_int_enum_input(struct vino_channel_settings *vcs, __u32 index)
-{
- int input = VINO_INPUT_NONE;
- unsigned long flags;
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
- if (vino_drvdata->decoder && vino_drvdata->camera) {
- switch (index) {
- case 0:
- input = VINO_INPUT_COMPOSITE;
- break;
- case 1:
- input = VINO_INPUT_SVIDEO;
- break;
- case 2:
- input = VINO_INPUT_D1;
- break;
- }
- } else if (vino_drvdata->decoder) {
- switch (index) {
- case 0:
- input = VINO_INPUT_COMPOSITE;
- break;
- case 1:
- input = VINO_INPUT_SVIDEO;
- break;
- }
- } else if (vino_drvdata->camera) {
- switch (index) {
- case 0:
- input = VINO_INPUT_D1;
- break;
- }
- }
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- return input;
-}
-
-/* execute with input_lock locked */
-static __u32 vino_find_input_index(struct vino_channel_settings *vcs)
-{
- __u32 index = 0;
- // FIXME: detect when no inputs available
-
- if (vino_drvdata->decoder && vino_drvdata->camera) {
- switch (vcs->input) {
- case VINO_INPUT_COMPOSITE:
- index = 0;
- break;
- case VINO_INPUT_SVIDEO:
- index = 1;
- break;
- case VINO_INPUT_D1:
- index = 2;
- break;
- }
- } else if (vino_drvdata->decoder) {
- switch (vcs->input) {
- case VINO_INPUT_COMPOSITE:
- index = 0;
- break;
- case VINO_INPUT_SVIDEO:
- index = 1;
- break;
- }
- } else if (vino_drvdata->camera) {
- switch (vcs->input) {
- case VINO_INPUT_D1:
- index = 0;
- break;
- }
- }
-
- return index;
-}
-
-/* V4L2 ioctls */
-
-static int vino_querycap(struct file *file, void *__fh,
- struct v4l2_capability *cap)
-{
- memset(cap, 0, sizeof(struct v4l2_capability));
-
- strcpy(cap->driver, vino_driver_name);
- strcpy(cap->card, vino_driver_description);
- strcpy(cap->bus_info, vino_bus_name);
- cap->capabilities =
- V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_STREAMING;
- // V4L2_CAP_OVERLAY, V4L2_CAP_READWRITE
- return 0;
-}
-
-static int vino_enum_input(struct file *file, void *__fh,
- struct v4l2_input *i)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- __u32 index = i->index;
- int input;
- dprintk("requested index = %d\n", index);
-
- input = vino_int_enum_input(vcs, index);
- if (input == VINO_INPUT_NONE)
- return -EINVAL;
-
- i->type = V4L2_INPUT_TYPE_CAMERA;
- i->std = vino_inputs[input].std;
- strcpy(i->name, vino_inputs[input].name);
-
- if (input == VINO_INPUT_COMPOSITE || input == VINO_INPUT_SVIDEO)
- decoder_call(video, g_input_status, &i->status);
- return 0;
-}
-
-static int vino_g_input(struct file *file, void *__fh,
- unsigned int *i)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- __u32 index;
- int input;
- unsigned long flags;
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
- input = vcs->input;
- index = vino_find_input_index(vcs);
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- dprintk("input = %d\n", input);
-
- if (input == VINO_INPUT_NONE) {
- return -EINVAL;
- }
-
- *i = index;
-
- return 0;
-}
-
-static int vino_s_input(struct file *file, void *__fh,
- unsigned int i)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- int input;
- dprintk("requested input = %d\n", i);
-
- input = vino_int_enum_input(vcs, i);
- if (input == VINO_INPUT_NONE)
- return -EINVAL;
-
- return vino_set_input(vcs, input);
-}
-
-static int vino_querystd(struct file *file, void *__fh,
- v4l2_std_id *std)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- unsigned long flags;
- int err = 0;
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- switch (vcs->input) {
- case VINO_INPUT_D1:
- *std = vino_inputs[vcs->input].std;
- break;
- case VINO_INPUT_COMPOSITE:
- case VINO_INPUT_SVIDEO: {
- decoder_call(video, querystd, std);
- break;
- }
- default:
- err = -EINVAL;
- }
-
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- return err;
-}
-
-static int vino_g_std(struct file *file, void *__fh,
- v4l2_std_id *std)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- unsigned long flags;
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- *std = vino_data_norms[vcs->data_norm].std;
- dprintk("current standard = %d\n", vcs->data_norm);
-
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- return 0;
-}
-
-static int vino_s_std(struct file *file, void *__fh,
- v4l2_std_id std)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- unsigned long flags;
- int ret = 0;
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- if (!vino_is_input_owner(vcs)) {
- ret = -EBUSY;
- goto out;
- }
-
- /* check if the standard is valid for the current input */
- if (std & vino_inputs[vcs->input].std) {
- dprintk("standard accepted\n");
-
- /* change the video norm for SAA7191
- * and accept NTSC for D1 (do nothing) */
-
- if (vcs->input == VINO_INPUT_D1)
- goto out;
-
- if (std & V4L2_STD_PAL) {
- ret = vino_set_data_norm(vcs, VINO_DATA_NORM_PAL,
- &flags);
- } else if (std & V4L2_STD_NTSC) {
- ret = vino_set_data_norm(vcs, VINO_DATA_NORM_NTSC,
- &flags);
- } else if (std & V4L2_STD_SECAM) {
- ret = vino_set_data_norm(vcs, VINO_DATA_NORM_SECAM,
- &flags);
- } else {
- ret = -EINVAL;
- }
-
- if (ret) {
- ret = -EINVAL;
- }
- } else {
- ret = -EINVAL;
- }
-
-out:
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- return ret;
-}
-
-static int vino_enum_fmt_vid_cap(struct file *file, void *__fh,
- struct v4l2_fmtdesc *fd)
-{
- dprintk("format index = %d\n", fd->index);
-
- if (fd->index >= VINO_DATA_FMT_COUNT)
- return -EINVAL;
- dprintk("format name = %s\n", vino_data_formats[fd->index].description);
-
- fd->pixelformat = vino_data_formats[fd->index].pixelformat;
- strcpy(fd->description, vino_data_formats[fd->index].description);
- return 0;
-}
-
-static int vino_try_fmt_vid_cap(struct file *file, void *__fh,
- struct v4l2_format *f)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- struct vino_channel_settings tempvcs;
- unsigned long flags;
- struct v4l2_pix_format *pf = &f->fmt.pix;
-
- dprintk("requested: w = %d, h = %d\n",
- pf->width, pf->height);
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
- memcpy(&tempvcs, vcs, sizeof(struct vino_channel_settings));
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- tempvcs.data_format = vino_find_data_format(pf->pixelformat);
- if (tempvcs.data_format == VINO_DATA_FMT_NONE) {
- tempvcs.data_format = VINO_DATA_FMT_GREY;
- pf->pixelformat =
- vino_data_formats[tempvcs.data_format].
- pixelformat;
- }
-
- /* data format must be set before clipping/scaling */
- vino_set_scaling(&tempvcs, pf->width, pf->height);
-
- dprintk("data format = %s\n",
- vino_data_formats[tempvcs.data_format].description);
-
- pf->width = (tempvcs.clipping.right - tempvcs.clipping.left) /
- tempvcs.decimation;
- pf->height = (tempvcs.clipping.bottom - tempvcs.clipping.top) /
- tempvcs.decimation;
-
- pf->field = V4L2_FIELD_INTERLACED;
- pf->bytesperline = tempvcs.line_size;
- pf->sizeimage = tempvcs.line_size *
- (tempvcs.clipping.bottom - tempvcs.clipping.top) /
- tempvcs.decimation;
- pf->colorspace =
- vino_data_formats[tempvcs.data_format].colorspace;
-
- return 0;
-}
-
-static int vino_g_fmt_vid_cap(struct file *file, void *__fh,
- struct v4l2_format *f)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- unsigned long flags;
- struct v4l2_pix_format *pf = &f->fmt.pix;
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- pf->width = (vcs->clipping.right - vcs->clipping.left) /
- vcs->decimation;
- pf->height = (vcs->clipping.bottom - vcs->clipping.top) /
- vcs->decimation;
- pf->pixelformat =
- vino_data_formats[vcs->data_format].pixelformat;
-
- pf->field = V4L2_FIELD_INTERLACED;
- pf->bytesperline = vcs->line_size;
- pf->sizeimage = vcs->line_size *
- (vcs->clipping.bottom - vcs->clipping.top) /
- vcs->decimation;
- pf->colorspace =
- vino_data_formats[vcs->data_format].colorspace;
-
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
- return 0;
-}
-
-static int vino_s_fmt_vid_cap(struct file *file, void *__fh,
- struct v4l2_format *f)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- int data_format;
- unsigned long flags;
- struct v4l2_pix_format *pf = &f->fmt.pix;
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- data_format = vino_find_data_format(pf->pixelformat);
-
- if (data_format == VINO_DATA_FMT_NONE) {
- vcs->data_format = VINO_DATA_FMT_GREY;
- pf->pixelformat =
- vino_data_formats[vcs->data_format].
- pixelformat;
- } else {
- vcs->data_format = data_format;
- }
-
- /* data format must be set before clipping/scaling */
- vino_set_scaling(vcs, pf->width, pf->height);
-
- dprintk("data format = %s\n",
- vino_data_formats[vcs->data_format].description);
-
- pf->width = vcs->clipping.right - vcs->clipping.left;
- pf->height = vcs->clipping.bottom - vcs->clipping.top;
-
- pf->field = V4L2_FIELD_INTERLACED;
- pf->bytesperline = vcs->line_size;
- pf->sizeimage = vcs->line_size *
- (vcs->clipping.bottom - vcs->clipping.top) /
- vcs->decimation;
- pf->colorspace =
- vino_data_formats[vcs->data_format].colorspace;
-
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
- return 0;
-}
-
-static int vino_cropcap(struct file *file, void *__fh,
- struct v4l2_cropcap *ccap)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- const struct vino_data_norm *norm;
- unsigned long flags;
-
- switch (ccap->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- norm = &vino_data_norms[vcs->data_norm];
-
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- ccap->bounds.left = 0;
- ccap->bounds.top = 0;
- ccap->bounds.width = norm->width;
- ccap->bounds.height = norm->height;
- memcpy(&ccap->defrect, &ccap->bounds,
- sizeof(struct v4l2_rect));
-
- ccap->pixelaspect.numerator = 1;
- ccap->pixelaspect.denominator = 1;
- break;
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int vino_g_crop(struct file *file, void *__fh,
- struct v4l2_crop *c)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- unsigned long flags;
-
- switch (c->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- c->c.left = vcs->clipping.left;
- c->c.top = vcs->clipping.top;
- c->c.width = vcs->clipping.right - vcs->clipping.left;
- c->c.height = vcs->clipping.bottom - vcs->clipping.top;
-
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
- break;
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int vino_s_crop(struct file *file, void *__fh,
- const struct v4l2_crop *c)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- unsigned long flags;
-
- switch (c->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- vino_set_clipping(vcs, c->c.left, c->c.top,
- c->c.width, c->c.height);
-
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
- break;
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int vino_g_parm(struct file *file, void *__fh,
- struct v4l2_streamparm *sp)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- unsigned long flags;
- struct v4l2_captureparm *cp = &sp->parm.capture;
-
- cp->capability = V4L2_CAP_TIMEPERFRAME;
- cp->timeperframe.numerator = 1;
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- cp->timeperframe.denominator = vcs->fps;
-
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- /* TODO: cp->readbuffers = xxx; */
-
- return 0;
-}
-
-static int vino_s_parm(struct file *file, void *__fh,
- struct v4l2_streamparm *sp)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- unsigned long flags;
- struct v4l2_captureparm *cp = &sp->parm.capture;
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- if ((cp->timeperframe.numerator == 0) ||
- (cp->timeperframe.denominator == 0)) {
- /* reset framerate */
- vino_set_default_framerate(vcs);
- } else {
- vino_set_framerate(vcs, cp->timeperframe.denominator /
- cp->timeperframe.numerator);
- }
-
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- return 0;
-}
-
-static int vino_reqbufs(struct file *file, void *__fh,
- struct v4l2_requestbuffers *rb)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
-
- if (vcs->reading)
- return -EBUSY;
-
- /* TODO: check queue type */
- if (rb->memory != V4L2_MEMORY_MMAP) {
- dprintk("type not mmap\n");
- return -EINVAL;
- }
-
- dprintk("count = %d\n", rb->count);
- if (rb->count > 0) {
- if (vino_is_capturing(vcs)) {
- dprintk("busy, capturing\n");
- return -EBUSY;
- }
-
- if (vino_queue_has_mapped_buffers(&vcs->fb_queue)) {
- dprintk("busy, buffers still mapped\n");
- return -EBUSY;
- } else {
- vcs->streaming = 0;
- vino_queue_free(&vcs->fb_queue);
- vino_queue_init(&vcs->fb_queue, &rb->count);
- }
- } else {
- vcs->streaming = 0;
- vino_capture_stop(vcs);
- vino_queue_free(&vcs->fb_queue);
- }
-
- return 0;
-}
-
-static void vino_v4l2_get_buffer_status(struct vino_channel_settings *vcs,
- struct vino_framebuffer *fb,
- struct v4l2_buffer *b)
-{
- if (vino_queue_outgoing_contains(&vcs->fb_queue,
- fb->id)) {
- b->flags &= ~V4L2_BUF_FLAG_QUEUED;
- b->flags |= V4L2_BUF_FLAG_DONE;
- } else if (vino_queue_incoming_contains(&vcs->fb_queue,
- fb->id)) {
- b->flags &= ~V4L2_BUF_FLAG_DONE;
- b->flags |= V4L2_BUF_FLAG_QUEUED;
- } else {
- b->flags &= ~(V4L2_BUF_FLAG_DONE |
- V4L2_BUF_FLAG_QUEUED);
- }
-
- b->flags &= ~(V4L2_BUF_FLAG_TIMECODE);
-
- if (fb->map_count > 0)
- b->flags |= V4L2_BUF_FLAG_MAPPED;
-
- b->flags &= ~V4L2_BUF_FLAG_TIMESTAMP_MASK;
- b->flags |= V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
-
- b->index = fb->id;
- b->memory = (vcs->fb_queue.type == VINO_MEMORY_MMAP) ?
- V4L2_MEMORY_MMAP : V4L2_MEMORY_USERPTR;
- b->m.offset = fb->offset;
- b->bytesused = fb->data_size;
- b->length = fb->size;
- b->field = V4L2_FIELD_INTERLACED;
- b->sequence = fb->frame_counter;
- memcpy(&b->timestamp, &fb->timestamp,
- sizeof(struct timeval));
- // b->input ?
-
- dprintk("buffer %d: length = %d, bytesused = %d, offset = %d\n",
- fb->id, fb->size, fb->data_size, fb->offset);
-}
-
-static int vino_querybuf(struct file *file, void *__fh,
- struct v4l2_buffer *b)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- struct vino_framebuffer *fb;
-
- if (vcs->reading)
- return -EBUSY;
-
- /* TODO: check queue type */
- if (b->index >= vino_queue_get_length(&vcs->fb_queue)) {
- dprintk("invalid index = %d\n",
- b->index);
- return -EINVAL;
- }
-
- fb = vino_queue_get_buffer(&vcs->fb_queue,
- b->index);
- if (fb == NULL) {
- dprintk("vino_queue_get_buffer() failed");
- return -EINVAL;
- }
-
- vino_v4l2_get_buffer_status(vcs, fb, b);
-
- return 0;
-}
-
-static int vino_qbuf(struct file *file, void *__fh,
- struct v4l2_buffer *b)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- struct vino_framebuffer *fb;
- int ret;
-
- if (vcs->reading)
- return -EBUSY;
-
- /* TODO: check queue type */
- if (b->memory != V4L2_MEMORY_MMAP) {
- dprintk("type not mmap\n");
- return -EINVAL;
- }
-
- fb = vino_capture_enqueue(vcs, b->index);
- if (fb == NULL)
- return -EINVAL;
-
- vino_v4l2_get_buffer_status(vcs, fb, b);
-
- if (vcs->streaming) {
- ret = vino_capture_next(vcs, 1);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static int vino_dqbuf(struct file *file, void *__fh,
- struct v4l2_buffer *b)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- unsigned int nonblocking = file->f_flags & O_NONBLOCK;
- struct vino_framebuffer *fb;
- unsigned int incoming, outgoing;
- int err;
-
- if (vcs->reading)
- return -EBUSY;
-
- /* TODO: check queue type */
-
- err = vino_queue_get_incoming(&vcs->fb_queue, &incoming);
- if (err) {
- dprintk("vino_queue_get_incoming() failed\n");
- return -EINVAL;
- }
- err = vino_queue_get_outgoing(&vcs->fb_queue, &outgoing);
- if (err) {
- dprintk("vino_queue_get_outgoing() failed\n");
- return -EINVAL;
- }
-
- dprintk("incoming = %d, outgoing = %d\n", incoming, outgoing);
-
- if (outgoing == 0) {
- if (incoming == 0) {
- dprintk("no incoming or outgoing buffers\n");
- return -EINVAL;
- }
- if (nonblocking) {
- dprintk("non-blocking I/O was selected and "
- "there are no buffers to dequeue\n");
- return -EAGAIN;
- }
-
- err = vino_wait_for_frame(vcs);
- if (err) {
- err = vino_wait_for_frame(vcs);
- if (err) {
- /* interrupted or no frames captured because of
- * frame skipping */
- /* vino_capture_failed(vcs); */
- return -EIO;
- }
- }
- }
-
- fb = vino_queue_remove(&vcs->fb_queue, &b->index);
- if (fb == NULL) {
- dprintk("vino_queue_remove() failed\n");
- return -EINVAL;
- }
-
- err = vino_check_buffer(vcs, fb);
-
- vino_v4l2_get_buffer_status(vcs, fb, b);
-
- if (err)
- return -EIO;
-
- return 0;
-}
-
-static int vino_streamon(struct file *file, void *__fh,
- enum v4l2_buf_type i)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- unsigned int incoming;
- int ret;
- if (vcs->reading)
- return -EBUSY;
-
- if (vcs->streaming)
- return 0;
-
- // TODO: check queue type
-
- if (vino_queue_get_length(&vcs->fb_queue) < 1) {
- dprintk("no buffers allocated\n");
- return -EINVAL;
- }
-
- ret = vino_queue_get_incoming(&vcs->fb_queue, &incoming);
- if (ret) {
- dprintk("vino_queue_get_incoming() failed\n");
- return -EINVAL;
- }
-
- vcs->streaming = 1;
-
- if (incoming > 0) {
- ret = vino_capture_next(vcs, 1);
- if (ret) {
- vcs->streaming = 0;
-
- dprintk("couldn't start capture\n");
- return -EINVAL;
- }
- }
-
- return 0;
-}
-
-static int vino_streamoff(struct file *file, void *__fh,
- enum v4l2_buf_type i)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- if (vcs->reading)
- return -EBUSY;
-
- if (!vcs->streaming)
- return 0;
-
- vcs->streaming = 0;
- vino_capture_stop(vcs);
-
- return 0;
-}
-
-static int vino_queryctrl(struct file *file, void *__fh,
- struct v4l2_queryctrl *queryctrl)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- unsigned long flags;
- int i;
- int err = 0;
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- switch (vcs->input) {
- case VINO_INPUT_D1:
- for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) {
- if (vino_indycam_v4l2_controls[i].id ==
- queryctrl->id) {
- memcpy(queryctrl,
- &vino_indycam_v4l2_controls[i],
- sizeof(struct v4l2_queryctrl));
- queryctrl->reserved[0] = 0;
- goto found;
- }
- }
-
- err = -EINVAL;
- break;
- case VINO_INPUT_COMPOSITE:
- case VINO_INPUT_SVIDEO:
- for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) {
- if (vino_saa7191_v4l2_controls[i].id ==
- queryctrl->id) {
- memcpy(queryctrl,
- &vino_saa7191_v4l2_controls[i],
- sizeof(struct v4l2_queryctrl));
- queryctrl->reserved[0] = 0;
- goto found;
- }
- }
-
- err = -EINVAL;
- break;
- default:
- err = -EINVAL;
- }
-
- found:
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- return err;
-}
-
-static int vino_g_ctrl(struct file *file, void *__fh,
- struct v4l2_control *control)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- unsigned long flags;
- int i;
- int err = 0;
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- switch (vcs->input) {
- case VINO_INPUT_D1: {
- err = -EINVAL;
- for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) {
- if (vino_indycam_v4l2_controls[i].id == control->id) {
- err = 0;
- break;
- }
- }
-
- if (err)
- goto out;
-
- err = camera_call(core, g_ctrl, control);
- if (err)
- err = -EINVAL;
- break;
- }
- case VINO_INPUT_COMPOSITE:
- case VINO_INPUT_SVIDEO: {
- err = -EINVAL;
- for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) {
- if (vino_saa7191_v4l2_controls[i].id == control->id) {
- err = 0;
- break;
- }
- }
-
- if (err)
- goto out;
-
- err = decoder_call(core, g_ctrl, control);
- if (err)
- err = -EINVAL;
- break;
- }
- default:
- err = -EINVAL;
- }
-
-out:
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- return err;
-}
-
-static int vino_s_ctrl(struct file *file, void *__fh,
- struct v4l2_control *control)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- unsigned long flags;
- int i;
- int err = 0;
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- if (!vino_is_input_owner(vcs)) {
- err = -EBUSY;
- goto out;
- }
-
- switch (vcs->input) {
- case VINO_INPUT_D1: {
- err = -EINVAL;
- for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) {
- if (vino_indycam_v4l2_controls[i].id == control->id) {
- err = 0;
- break;
- }
- }
- if (err)
- goto out;
- if (control->value < vino_indycam_v4l2_controls[i].minimum ||
- control->value > vino_indycam_v4l2_controls[i].maximum) {
- err = -ERANGE;
- goto out;
- }
- err = camera_call(core, s_ctrl, control);
- if (err)
- err = -EINVAL;
- break;
- }
- case VINO_INPUT_COMPOSITE:
- case VINO_INPUT_SVIDEO: {
- err = -EINVAL;
- for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) {
- if (vino_saa7191_v4l2_controls[i].id == control->id) {
- err = 0;
- break;
- }
- }
- if (err)
- goto out;
- if (control->value < vino_saa7191_v4l2_controls[i].minimum ||
- control->value > vino_saa7191_v4l2_controls[i].maximum) {
- err = -ERANGE;
- goto out;
- }
-
- err = decoder_call(core, s_ctrl, control);
- if (err)
- err = -EINVAL;
- break;
- }
- default:
- err = -EINVAL;
- }
-
-out:
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- return err;
-}
-
-/* File operations */
-
-static int vino_open(struct file *file)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- int ret = 0;
- dprintk("open(): channel = %c\n",
- (vcs->channel == VINO_CHANNEL_A) ? 'A' : 'B');
-
- mutex_lock(&vcs->mutex);
-
- if (vcs->users) {
- dprintk("open(): driver busy\n");
- ret = -EBUSY;
- goto out;
- }
-
- ret = vino_acquire_input(vcs);
- if (ret) {
- dprintk("open(): vino_acquire_input() failed\n");
- goto out;
- }
-
- vcs->users++;
-
- out:
- mutex_unlock(&vcs->mutex);
-
- dprintk("open(): %s!\n", ret ? "failed" : "complete");
-
- return ret;
-}
-
-static int vino_close(struct file *file)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- dprintk("close():\n");
-
- mutex_lock(&vcs->mutex);
-
- vcs->users--;
-
- if (!vcs->users) {
- vino_release_input(vcs);
-
- /* stop DMA and free buffers */
- vino_capture_stop(vcs);
- vino_queue_free(&vcs->fb_queue);
- }
-
- mutex_unlock(&vcs->mutex);
-
- return 0;
-}
-
-static void vino_vm_open(struct vm_area_struct *vma)
-{
- struct vino_framebuffer *fb = vma->vm_private_data;
-
- fb->map_count++;
- dprintk("vino_vm_open(): count = %d\n", fb->map_count);
-}
-
-static void vino_vm_close(struct vm_area_struct *vma)
-{
- struct vino_framebuffer *fb = vma->vm_private_data;
-
- fb->map_count--;
- dprintk("vino_vm_close(): count = %d\n", fb->map_count);
-}
-
-static const struct vm_operations_struct vino_vm_ops = {
- .open = vino_vm_open,
- .close = vino_vm_close,
-};
-
-static int vino_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
-
- unsigned long start = vma->vm_start;
- unsigned long size = vma->vm_end - vma->vm_start;
- unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
-
- struct vino_framebuffer *fb = NULL;
- unsigned int i, length;
- int ret = 0;
-
- dprintk("mmap():\n");
-
- // TODO: reject mmap if already mapped
-
- if (mutex_lock_interruptible(&vcs->mutex))
- return -EINTR;
-
- if (vcs->reading) {
- ret = -EBUSY;
- goto out;
- }
-
- // TODO: check queue type
-
- if (!(vma->vm_flags & VM_WRITE)) {
- dprintk("mmap(): app bug: PROT_WRITE please\n");
- ret = -EINVAL;
- goto out;
- }
- if (!(vma->vm_flags & VM_SHARED)) {
- dprintk("mmap(): app bug: MAP_SHARED please\n");
- ret = -EINVAL;
- goto out;
- }
-
- /* find the correct buffer using offset */
- length = vino_queue_get_length(&vcs->fb_queue);
- if (length == 0) {
- dprintk("mmap(): queue not initialized\n");
- ret = -EINVAL;
- goto out;
- }
-
- for (i = 0; i < length; i++) {
- fb = vino_queue_get_buffer(&vcs->fb_queue, i);
- if (fb == NULL) {
- dprintk("mmap(): vino_queue_get_buffer() failed\n");
- ret = -EINVAL;
- goto out;
- }
-
- if (fb->offset == offset)
- goto found;
- }
-
- dprintk("mmap(): invalid offset = %lu\n", offset);
- ret = -EINVAL;
- goto out;
-
-found:
- dprintk("mmap(): buffer = %d\n", i);
-
- if (size > (fb->desc_table.page_count * PAGE_SIZE)) {
- dprintk("mmap(): failed: size = %lu > %lu\n",
- size, fb->desc_table.page_count * PAGE_SIZE);
- ret = -EINVAL;
- goto out;
- }
-
- for (i = 0; i < fb->desc_table.page_count; i++) {
- unsigned long pfn =
- virt_to_phys((void *)fb->desc_table.virtual[i]) >>
- PAGE_SHIFT;
-
- if (size < PAGE_SIZE)
- break;
-
- // protection was: PAGE_READONLY
- if (remap_pfn_range(vma, start, pfn, PAGE_SIZE,
- vma->vm_page_prot)) {
- dprintk("mmap(): remap_pfn_range() failed\n");
- ret = -EAGAIN;
- goto out;
- }
-
- start += PAGE_SIZE;
- size -= PAGE_SIZE;
- }
-
- fb->map_count = 1;
-
- vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
- vma->vm_flags &= ~VM_IO;
- vma->vm_private_data = fb;
- vma->vm_file = file;
- vma->vm_ops = &vino_vm_ops;
-
-out:
- mutex_unlock(&vcs->mutex);
-
- return ret;
-}
-
-static unsigned int vino_poll(struct file *file, poll_table *pt)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- unsigned int outgoing;
- unsigned int ret = 0;
-
- // lock mutex (?)
- // TODO: this has to be corrected for different read modes
-
- dprintk("poll():\n");
-
- if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) {
- dprintk("poll(): vino_queue_get_outgoing() failed\n");
- ret = POLLERR;
- goto error;
- }
- if (outgoing > 0)
- goto over;
-
- poll_wait(file, &vcs->fb_queue.frame_wait_queue, pt);
-
- if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) {
- dprintk("poll(): vino_queue_get_outgoing() failed\n");
- ret = POLLERR;
- goto error;
- }
-
-over:
- dprintk("poll(): data %savailable\n",
- (outgoing > 0) ? "" : "not ");
-
- if (outgoing > 0)
- ret = POLLIN | POLLRDNORM;
-
-error:
- return ret;
-}
-
-static long vino_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- long ret;
-
- if (mutex_lock_interruptible(&vcs->mutex))
- return -EINTR;
-
- ret = video_ioctl2(file, cmd, arg);
-
- mutex_unlock(&vcs->mutex);
-
- return ret;
-}
-
-/* Initialization and cleanup */
-
-/* __initdata */
-static int vino_init_stage;
-
-const struct v4l2_ioctl_ops vino_ioctl_ops = {
- .vidioc_enum_fmt_vid_cap = vino_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = vino_g_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vino_s_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = vino_try_fmt_vid_cap,
- .vidioc_querycap = vino_querycap,
- .vidioc_enum_input = vino_enum_input,
- .vidioc_g_input = vino_g_input,
- .vidioc_s_input = vino_s_input,
- .vidioc_g_std = vino_g_std,
- .vidioc_s_std = vino_s_std,
- .vidioc_querystd = vino_querystd,
- .vidioc_cropcap = vino_cropcap,
- .vidioc_s_crop = vino_s_crop,
- .vidioc_g_crop = vino_g_crop,
- .vidioc_s_parm = vino_s_parm,
- .vidioc_g_parm = vino_g_parm,
- .vidioc_reqbufs = vino_reqbufs,
- .vidioc_querybuf = vino_querybuf,
- .vidioc_qbuf = vino_qbuf,
- .vidioc_dqbuf = vino_dqbuf,
- .vidioc_streamon = vino_streamon,
- .vidioc_streamoff = vino_streamoff,
- .vidioc_queryctrl = vino_queryctrl,
- .vidioc_g_ctrl = vino_g_ctrl,
- .vidioc_s_ctrl = vino_s_ctrl,
-};
-
-static const struct v4l2_file_operations vino_fops = {
- .owner = THIS_MODULE,
- .open = vino_open,
- .release = vino_close,
- .unlocked_ioctl = vino_ioctl,
- .mmap = vino_mmap,
- .poll = vino_poll,
-};
-
-static struct video_device vdev_template = {
- .name = "NOT SET",
- .fops = &vino_fops,
- .ioctl_ops = &vino_ioctl_ops,
- .tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM,
-};
-
-static void vino_module_cleanup(int stage)
-{
- switch(stage) {
- case 11:
- video_unregister_device(vino_drvdata->b.vdev);
- vino_drvdata->b.vdev = NULL;
- case 10:
- video_unregister_device(vino_drvdata->a.vdev);
- vino_drvdata->a.vdev = NULL;
- case 9:
- i2c_del_adapter(&vino_i2c_adapter);
- case 8:
- free_irq(SGI_VINO_IRQ, NULL);
- case 7:
- if (vino_drvdata->b.vdev) {
- video_device_release(vino_drvdata->b.vdev);
- vino_drvdata->b.vdev = NULL;
- }
- case 6:
- if (vino_drvdata->a.vdev) {
- video_device_release(vino_drvdata->a.vdev);
- vino_drvdata->a.vdev = NULL;
- }
- case 5:
- /* all entries in dma_cpu dummy table have the same address */
- dma_unmap_single(NULL,
- vino_drvdata->dummy_desc_table.dma_cpu[0],
- PAGE_SIZE, DMA_FROM_DEVICE);
- dma_free_coherent(NULL, VINO_DUMMY_DESC_COUNT
- * sizeof(dma_addr_t),
- (void *)vino_drvdata->
- dummy_desc_table.dma_cpu,
- vino_drvdata->dummy_desc_table.dma);
- case 4:
- free_page(vino_drvdata->dummy_page);
- case 3:
- v4l2_device_unregister(&vino_drvdata->v4l2_dev);
- case 2:
- kfree(vino_drvdata);
- case 1:
- iounmap(vino);
- case 0:
- break;
- default:
- dprintk("vino_module_cleanup(): invalid cleanup stage = %d\n",
- stage);
- }
-}
-
-static int vino_probe(void)
-{
- unsigned long rev_id;
-
- if (ip22_is_fullhouse()) {
- printk(KERN_ERR "VINO doesn't exist in IP22 Fullhouse\n");
- return -ENODEV;
- }
-
- if (!(sgimc->systemid & SGIMC_SYSID_EPRESENT)) {
- printk(KERN_ERR "VINO is not found (EISA BUS not present)\n");
- return -ENODEV;
- }
-
- vino = (struct sgi_vino *)ioremap(VINO_BASE, sizeof(struct sgi_vino));
- if (!vino) {
- printk(KERN_ERR "VINO: ioremap() failed\n");
- return -EIO;
- }
- vino_init_stage++;
-
- if (get_dbe(rev_id, &(vino->rev_id))) {
- printk(KERN_ERR "Failed to read VINO revision register\n");
- vino_module_cleanup(vino_init_stage);
- return -ENODEV;
- }
-
- if (VINO_ID_VALUE(rev_id) != VINO_CHIP_ID) {
- printk(KERN_ERR "Unknown VINO chip ID (Rev/ID: 0x%02lx)\n",
- rev_id);
- vino_module_cleanup(vino_init_stage);
- return -ENODEV;
- }
-
- printk(KERN_INFO "VINO revision %ld found\n", VINO_REV_NUM(rev_id));
-
- return 0;
-}
-
-static int vino_init(void)
-{
- dma_addr_t dma_dummy_address;
- int err;
- int i;
-
- vino_drvdata = kzalloc(sizeof(struct vino_settings), GFP_KERNEL);
- if (!vino_drvdata) {
- vino_module_cleanup(vino_init_stage);
- return -ENOMEM;
- }
- vino_init_stage++;
- strlcpy(vino_drvdata->v4l2_dev.name, "vino",
- sizeof(vino_drvdata->v4l2_dev.name));
- err = v4l2_device_register(NULL, &vino_drvdata->v4l2_dev);
- if (err)
- return err;
- vino_init_stage++;
-
- /* create a dummy dma descriptor */
- vino_drvdata->dummy_page = get_zeroed_page(GFP_KERNEL | GFP_DMA);
- if (!vino_drvdata->dummy_page) {
- vino_module_cleanup(vino_init_stage);
- return -ENOMEM;
- }
- vino_init_stage++;
-
- // TODO: use page_count in dummy_desc_table
-
- vino_drvdata->dummy_desc_table.dma_cpu =
- dma_alloc_coherent(NULL,
- VINO_DUMMY_DESC_COUNT * sizeof(dma_addr_t),
- &vino_drvdata->dummy_desc_table.dma,
- GFP_KERNEL | GFP_DMA);
- if (!vino_drvdata->dummy_desc_table.dma_cpu) {
- vino_module_cleanup(vino_init_stage);
- return -ENOMEM;
- }
- vino_init_stage++;
-
- dma_dummy_address = dma_map_single(NULL,
- (void *)vino_drvdata->dummy_page,
- PAGE_SIZE, DMA_FROM_DEVICE);
- for (i = 0; i < VINO_DUMMY_DESC_COUNT; i++) {
- vino_drvdata->dummy_desc_table.dma_cpu[i] = dma_dummy_address;
- }
-
- /* initialize VINO */
-
- vino->control = 0;
- vino->a.next_4_desc = vino_drvdata->dummy_desc_table.dma;
- vino->b.next_4_desc = vino_drvdata->dummy_desc_table.dma;
- udelay(VINO_DESC_FETCH_DELAY);
-
- vino->intr_status = 0;
-
- vino->a.fifo_thres = VINO_FIFO_THRESHOLD_DEFAULT;
- vino->b.fifo_thres = VINO_FIFO_THRESHOLD_DEFAULT;
-
- return 0;
-}
-
-static int vino_init_channel_settings(struct vino_channel_settings *vcs,
- unsigned int channel, const char *name)
-{
- vcs->channel = channel;
- vcs->input = VINO_INPUT_NONE;
- vcs->alpha = 0;
- vcs->users = 0;
- vcs->data_format = VINO_DATA_FMT_GREY;
- vcs->data_norm = VINO_DATA_NORM_NTSC;
- vcs->decimation = 1;
- vino_set_default_clipping(vcs);
- vino_set_default_framerate(vcs);
-
- vcs->capturing = 0;
-
- mutex_init(&vcs->mutex);
- spin_lock_init(&vcs->capture_lock);
-
- mutex_init(&vcs->fb_queue.queue_mutex);
- spin_lock_init(&vcs->fb_queue.queue_lock);
- init_waitqueue_head(&vcs->fb_queue.frame_wait_queue);
-
- vcs->vdev = video_device_alloc();
- if (!vcs->vdev) {
- vino_module_cleanup(vino_init_stage);
- return -ENOMEM;
- }
- vino_init_stage++;
-
- memcpy(vcs->vdev, &vdev_template,
- sizeof(struct video_device));
- strcpy(vcs->vdev->name, name);
- vcs->vdev->release = video_device_release;
- vcs->vdev->v4l2_dev = &vino_drvdata->v4l2_dev;
-
- video_set_drvdata(vcs->vdev, vcs);
-
- return 0;
-}
-
-static int __init vino_module_init(void)
-{
- int ret;
-
- printk(KERN_INFO "SGI VINO driver version %s\n",
- VINO_MODULE_VERSION);
-
- ret = vino_probe();
- if (ret)
- return ret;
-
- ret = vino_init();
- if (ret)
- return ret;
-
- /* initialize data structures */
-
- spin_lock_init(&vino_drvdata->vino_lock);
- spin_lock_init(&vino_drvdata->input_lock);
-
- ret = vino_init_channel_settings(&vino_drvdata->a, VINO_CHANNEL_A,
- vino_vdev_name_a);
- if (ret)
- return ret;
-
- ret = vino_init_channel_settings(&vino_drvdata->b, VINO_CHANNEL_B,
- vino_vdev_name_b);
- if (ret)
- return ret;
-
- /* initialize hardware and register V4L devices */
-
- ret = request_irq(SGI_VINO_IRQ, vino_interrupt, 0,
- vino_driver_description, NULL);
- if (ret) {
- printk(KERN_ERR "VINO: requesting IRQ %02d failed\n",
- SGI_VINO_IRQ);
- vino_module_cleanup(vino_init_stage);
- return -EAGAIN;
- }
- vino_init_stage++;
-
- ret = i2c_add_adapter(&vino_i2c_adapter);
- if (ret) {
- printk(KERN_ERR "VINO I2C bus registration failed\n");
- vino_module_cleanup(vino_init_stage);
- return ret;
- }
- i2c_set_adapdata(&vino_i2c_adapter, &vino_drvdata->v4l2_dev);
- vino_init_stage++;
-
- ret = video_register_device(vino_drvdata->a.vdev,
- VFL_TYPE_GRABBER, -1);
- if (ret < 0) {
- printk(KERN_ERR "VINO channel A Video4Linux-device "
- "registration failed\n");
- vino_module_cleanup(vino_init_stage);
- return -EINVAL;
- }
- vino_init_stage++;
-
- ret = video_register_device(vino_drvdata->b.vdev,
- VFL_TYPE_GRABBER, -1);
- if (ret < 0) {
- printk(KERN_ERR "VINO channel B Video4Linux-device "
- "registration failed\n");
- vino_module_cleanup(vino_init_stage);
- return -EINVAL;
- }
- vino_init_stage++;
-
- vino_drvdata->decoder =
- v4l2_i2c_new_subdev(&vino_drvdata->v4l2_dev, &vino_i2c_adapter,
- "saa7191", 0, I2C_ADDRS(0x45));
- vino_drvdata->camera =
- v4l2_i2c_new_subdev(&vino_drvdata->v4l2_dev, &vino_i2c_adapter,
- "indycam", 0, I2C_ADDRS(0x2b));
-
- dprintk("init complete!\n");
-
- return 0;
-}
-
-static void __exit vino_module_exit(void)
-{
- dprintk("exiting, stage = %d ...\n", vino_init_stage);
- vino_module_cleanup(vino_init_stage);
- dprintk("cleanup complete, exit!\n");
-}
-
-module_init(vino_module_init);
-module_exit(vino_module_exit);
diff --git a/drivers/media/platform/vino.h b/drivers/media/platform/vino.h
deleted file mode 100644
index de2d615..0000000
--- a/drivers/media/platform/vino.h
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Driver for the VINO (Video In No Out) system found in SGI Indys.
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * Copyright (C) 1999 Ulf Karlsson <ulfc@bun.falkenberg.se>
- * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
- */
-
-#ifndef _VINO_H_
-#define _VINO_H_
-
-#define VINO_BASE 0x00080000 /* Vino is in the EISA address space,
- * but it is not an EISA bus card */
-#define VINO_PAGE_SIZE 4096
-
-struct sgi_vino_channel {
- u32 _pad_alpha;
- volatile u32 alpha;
-
-#define VINO_CLIP_X(x) ((x) & 0x3ff) /* bits 0:9 */
-#define VINO_CLIP_ODD(x) (((x) & 0x1ff) << 10) /* bits 10:18 */
-#define VINO_CLIP_EVEN(x) (((x) & 0x1ff) << 19) /* bits 19:27 */
- u32 _pad_clip_start;
- volatile u32 clip_start;
- u32 _pad_clip_end;
- volatile u32 clip_end;
-
-#define VINO_FRAMERT_FULL 0xfff
-#define VINO_FRAMERT_PAL (1<<0) /* 0=NTSC 1=PAL */
-#define VINO_FRAMERT_RT(x) (((x) & 0xfff) << 1) /* bits 1:12 */
- u32 _pad_frame_rate;
- volatile u32 frame_rate;
-
- u32 _pad_field_counter;
- volatile u32 field_counter;
- u32 _pad_line_size;
- volatile u32 line_size;
- u32 _pad_line_count;
- volatile u32 line_count;
- u32 _pad_page_index;
- volatile u32 page_index;
- u32 _pad_next_4_desc;
- volatile u32 next_4_desc;
- u32 _pad_start_desc_tbl;
- volatile u32 start_desc_tbl;
-
-#define VINO_DESC_JUMP (1<<30)
-#define VINO_DESC_STOP (1<<31)
-#define VINO_DESC_VALID (1<<32)
- u32 _pad_desc_0;
- volatile u32 desc_0;
- u32 _pad_desc_1;
- volatile u32 desc_1;
- u32 _pad_desc_2;
- volatile u32 desc_2;
- u32 _pad_Bdesc_3;
- volatile u32 desc_3;
-
- u32 _pad_fifo_thres;
- volatile u32 fifo_thres;
- u32 _pad_fifo_read;
- volatile u32 fifo_read;
- u32 _pad_fifo_write;
- volatile u32 fifo_write;
-};
-
-struct sgi_vino {
-#define VINO_CHIP_ID 0xb
-#define VINO_REV_NUM(x) ((x) & 0x0f)
-#define VINO_ID_VALUE(x) (((x) & 0xf0) >> 4)
- u32 _pad_rev_id;
- volatile u32 rev_id;
-
-#define VINO_CTRL_LITTLE_ENDIAN (1<<0)
-#define VINO_CTRL_A_EOF_INT (1<<1) /* Field transferred int */
-#define VINO_CTRL_A_FIFO_INT (1<<2) /* FIFO overflow int */
-#define VINO_CTRL_A_EOD_INT (1<<3) /* End of desc table int */
-#define VINO_CTRL_A_INT (VINO_CTRL_A_EOF_INT | \
- VINO_CTRL_A_FIFO_INT | \
- VINO_CTRL_A_EOD_INT)
-#define VINO_CTRL_B_EOF_INT (1<<4) /* Field transferred int */
-#define VINO_CTRL_B_FIFO_INT (1<<5) /* FIFO overflow int */
-#define VINO_CTRL_B_EOD_INT (1<<6) /* End of desc table int */
-#define VINO_CTRL_B_INT (VINO_CTRL_B_EOF_INT | \
- VINO_CTRL_B_FIFO_INT | \
- VINO_CTRL_B_EOD_INT)
-#define VINO_CTRL_A_DMA_ENBL (1<<7)
-#define VINO_CTRL_A_INTERLEAVE_ENBL (1<<8)
-#define VINO_CTRL_A_SYNC_ENBL (1<<9)
-#define VINO_CTRL_A_SELECT (1<<10) /* 1=D1 0=Philips */
-#define VINO_CTRL_A_RGB (1<<11) /* 1=RGB 0=YUV */
-#define VINO_CTRL_A_LUMA_ONLY (1<<12)
-#define VINO_CTRL_A_DEC_ENBL (1<<13) /* Decimation */
-#define VINO_CTRL_A_DEC_SCALE_MASK 0x1c000 /* bits 14:17 */
-#define VINO_CTRL_A_DEC_SCALE_SHIFT (14)
-#define VINO_CTRL_A_DEC_HOR_ONLY (1<<17) /* Horizontal only */
-#define VINO_CTRL_A_DITHER (1<<18) /* 24 -> 8 bit dither */
-#define VINO_CTRL_B_DMA_ENBL (1<<19)
-#define VINO_CTRL_B_INTERLEAVE_ENBL (1<<20)
-#define VINO_CTRL_B_SYNC_ENBL (1<<21)
-#define VINO_CTRL_B_SELECT (1<<22) /* 1=D1 0=Philips */
-#define VINO_CTRL_B_RGB (1<<23) /* 1=RGB 0=YUV */
-#define VINO_CTRL_B_LUMA_ONLY (1<<24)
-#define VINO_CTRL_B_DEC_ENBL (1<<25) /* Decimation */
-#define VINO_CTRL_B_DEC_SCALE_MASK 0x1c000000 /* bits 26:28 */
-#define VINO_CTRL_B_DEC_SCALE_SHIFT (26)
-#define VINO_CTRL_B_DEC_HOR_ONLY (1<<29) /* Decimation horizontal only */
-#define VINO_CTRL_B_DITHER (1<<30) /* ChanB 24 -> 8 bit dither */
- u32 _pad_control;
- volatile u32 control;
-
-#define VINO_INTSTAT_A_EOF (1<<0) /* Field transferred int */
-#define VINO_INTSTAT_A_FIFO (1<<1) /* FIFO overflow int */
-#define VINO_INTSTAT_A_EOD (1<<2) /* End of desc table int */
-#define VINO_INTSTAT_A (VINO_INTSTAT_A_EOF | \
- VINO_INTSTAT_A_FIFO | \
- VINO_INTSTAT_A_EOD)
-#define VINO_INTSTAT_B_EOF (1<<3) /* Field transferred int */
-#define VINO_INTSTAT_B_FIFO (1<<4) /* FIFO overflow int */
-#define VINO_INTSTAT_B_EOD (1<<5) /* End of desc table int */
-#define VINO_INTSTAT_B (VINO_INTSTAT_B_EOF | \
- VINO_INTSTAT_B_FIFO | \
- VINO_INTSTAT_B_EOD)
- u32 _pad_intr_status;
- volatile u32 intr_status;
-
- u32 _pad_i2c_control;
- volatile u32 i2c_control;
- u32 _pad_i2c_data;
- volatile u32 i2c_data;
-
- struct sgi_vino_channel a;
- struct sgi_vino_channel b;
-};
-
-#endif
diff --git a/drivers/media/platform/vivid/Kconfig b/drivers/media/platform/vivid/Kconfig
index 0d2d7c1..c03e09c 100644
--- a/drivers/media/platform/vivid/Kconfig
+++ b/drivers/media/platform/vivid/Kconfig
@@ -1,9 +1,12 @@
config VIDEO_VIVID
tristate "Virtual Video Test Driver"
depends on m
- depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64
+ depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64 && FB
depends on FONT_SUPPORT
depends on FONT_8x16
+ depends on FB_CFB_FILLRECT
+ depends on FB_CFB_COPYAREA
+ depends on FB_CFB_IMAGEBLIT
select VIDEOBUF2_VMALLOC
default n
---help---
diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c
index 2c61a62..a7e033a 100644
--- a/drivers/media/platform/vivid/vivid-core.c
+++ b/drivers/media/platform/vivid/vivid-core.c
@@ -100,11 +100,9 @@
"\t\t bit 0=crop, 1=compose, 2=scale,\n"
"\t\t -1=user-controlled (default)");
-static unsigned multiplanar[VIVID_MAX_DEVS];
+static unsigned multiplanar[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 1 };
module_param_array(multiplanar, uint, NULL, 0444);
-MODULE_PARM_DESC(multiplanar, " 0 (default) is alternating single and multiplanar devices,\n"
- "\t\t 1 is single planar devices,\n"
- "\t\t 2 is multiplanar devices");
+MODULE_PARM_DESC(multiplanar, " 1 (default) creates a single planar device, 2 creates a multiplanar device.");
/* Default: video + vbi-cap (raw and sliced) + radio rx + radio tx + sdr + vbi-out + vid-out */
static unsigned node_types[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 0x1d3d };
@@ -197,20 +195,6 @@
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd7
};
-void vivid_lock(struct vb2_queue *vq)
-{
- struct vivid_dev *dev = vb2_get_drv_priv(vq);
-
- mutex_lock(&dev->mutex);
-}
-
-void vivid_unlock(struct vb2_queue *vq)
-{
- struct vivid_dev *dev = vb2_get_drv_priv(vq);
-
- mutex_unlock(&dev->mutex);
-}
-
static int vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
@@ -588,7 +572,7 @@
.vidioc_querybuf = vb2_ioctl_querybuf,
.vidioc_qbuf = vb2_ioctl_qbuf,
.vidioc_dqbuf = vb2_ioctl_dqbuf,
-/* Not yet .vidioc_expbuf = vb2_ioctl_expbuf,*/
+ .vidioc_expbuf = vb2_ioctl_expbuf,
.vidioc_streamon = vb2_ioctl_streamon,
.vidioc_streamoff = vb2_ioctl_streamoff,
@@ -669,10 +653,7 @@
/* start detecting feature set */
/* do we use single- or multi-planar? */
- if (multiplanar[inst] == 0)
- dev->multiplanar = inst & 1;
- else
- dev->multiplanar = multiplanar[inst] > 1;
+ dev->multiplanar = multiplanar[inst] > 1;
v4l2_info(&dev->v4l2_dev, "using %splanar format API\n",
dev->multiplanar ? "multi" : "single ");
@@ -1023,6 +1004,7 @@
q->mem_ops = &vb2_vmalloc_memops;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
q->min_buffers_needed = 2;
+ q->lock = &dev->mutex;
ret = vb2_queue_init(q);
if (ret)
@@ -1041,6 +1023,7 @@
q->mem_ops = &vb2_vmalloc_memops;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
q->min_buffers_needed = 2;
+ q->lock = &dev->mutex;
ret = vb2_queue_init(q);
if (ret)
@@ -1059,6 +1042,7 @@
q->mem_ops = &vb2_vmalloc_memops;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
q->min_buffers_needed = 2;
+ q->lock = &dev->mutex;
ret = vb2_queue_init(q);
if (ret)
@@ -1077,6 +1061,7 @@
q->mem_ops = &vb2_vmalloc_memops;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
q->min_buffers_needed = 2;
+ q->lock = &dev->mutex;
ret = vb2_queue_init(q);
if (ret)
@@ -1094,6 +1079,7 @@
q->mem_ops = &vb2_vmalloc_memops;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
q->min_buffers_needed = 8;
+ q->lock = &dev->mutex;
ret = vb2_queue_init(q);
if (ret)
diff --git a/drivers/media/platform/vivid/vivid-core.h b/drivers/media/platform/vivid/vivid-core.h
index 811c286..4b497df 100644
--- a/drivers/media/platform/vivid/vivid-core.h
+++ b/drivers/media/platform/vivid/vivid-core.h
@@ -116,6 +116,17 @@
CUSTOM_DV_TIMINGS,
};
+enum vivid_colorspace {
+ VIVID_CS_170M,
+ VIVID_CS_709,
+ VIVID_CS_SRGB,
+ VIVID_CS_ADOBERGB,
+ VIVID_CS_2020,
+ VIVID_CS_240M,
+ VIVID_CS_SYS_M,
+ VIVID_CS_SYS_BG,
+};
+
#define VIVID_INVALID_SIGNAL(mode) \
((mode) == NO_SIGNAL || (mode) == NO_LOCK || (mode) == OUT_OF_RANGE)
@@ -318,6 +329,8 @@
v4l2_std_id std_out;
struct v4l2_dv_timings dv_timings_out;
u32 colorspace_out;
+ u32 ycbcr_enc_out;
+ u32 quantization_out;
u32 service_set_out;
u32 bytesperline_out[2];
unsigned tv_field_out;
@@ -514,7 +527,4 @@
return dev->output_type[dev->output] == HDMI;
}
-void vivid_lock(struct vb2_queue *vq);
-void vivid_unlock(struct vb2_queue *vq);
-
#endif
diff --git a/drivers/media/platform/vivid/vivid-ctrls.c b/drivers/media/platform/vivid/vivid-ctrls.c
index d5cbf00..857e786 100644
--- a/drivers/media/platform/vivid/vivid-ctrls.c
+++ b/drivers/media/platform/vivid/vivid-ctrls.c
@@ -40,6 +40,9 @@
#define VIVID_CID_STRING (VIVID_CID_CUSTOM_BASE + 5)
#define VIVID_CID_BITMASK (VIVID_CID_CUSTOM_BASE + 6)
#define VIVID_CID_INTMENU (VIVID_CID_CUSTOM_BASE + 7)
+#define VIVID_CID_U32_ARRAY (VIVID_CID_CUSTOM_BASE + 8)
+#define VIVID_CID_U16_MATRIX (VIVID_CID_CUSTOM_BASE + 9)
+#define VIVID_CID_U8_4D_ARRAY (VIVID_CID_CUSTOM_BASE + 10)
#define VIVID_CID_VIVID_BASE (0x00f00000 | 0xf000)
#define VIVID_CID_VIVID_CLASS (0x00f00000 | 1)
@@ -59,19 +62,21 @@
#define VIVID_CID_DV_TIMINGS_ASPECT_RATIO (VIVID_CID_VIVID_BASE + 23)
#define VIVID_CID_TSTAMP_SRC (VIVID_CID_VIVID_BASE + 24)
#define VIVID_CID_COLORSPACE (VIVID_CID_VIVID_BASE + 25)
-#define VIVID_CID_LIMITED_RGB_RANGE (VIVID_CID_VIVID_BASE + 26)
-#define VIVID_CID_ALPHA_MODE (VIVID_CID_VIVID_BASE + 27)
-#define VIVID_CID_HAS_CROP_CAP (VIVID_CID_VIVID_BASE + 28)
-#define VIVID_CID_HAS_COMPOSE_CAP (VIVID_CID_VIVID_BASE + 29)
-#define VIVID_CID_HAS_SCALER_CAP (VIVID_CID_VIVID_BASE + 30)
-#define VIVID_CID_HAS_CROP_OUT (VIVID_CID_VIVID_BASE + 31)
-#define VIVID_CID_HAS_COMPOSE_OUT (VIVID_CID_VIVID_BASE + 32)
-#define VIVID_CID_HAS_SCALER_OUT (VIVID_CID_VIVID_BASE + 33)
-#define VIVID_CID_LOOP_VIDEO (VIVID_CID_VIVID_BASE + 34)
-#define VIVID_CID_SEQ_WRAP (VIVID_CID_VIVID_BASE + 35)
-#define VIVID_CID_TIME_WRAP (VIVID_CID_VIVID_BASE + 36)
-#define VIVID_CID_MAX_EDID_BLOCKS (VIVID_CID_VIVID_BASE + 37)
-#define VIVID_CID_PERCENTAGE_FILL (VIVID_CID_VIVID_BASE + 38)
+#define VIVID_CID_YCBCR_ENC (VIVID_CID_VIVID_BASE + 26)
+#define VIVID_CID_QUANTIZATION (VIVID_CID_VIVID_BASE + 27)
+#define VIVID_CID_LIMITED_RGB_RANGE (VIVID_CID_VIVID_BASE + 28)
+#define VIVID_CID_ALPHA_MODE (VIVID_CID_VIVID_BASE + 29)
+#define VIVID_CID_HAS_CROP_CAP (VIVID_CID_VIVID_BASE + 30)
+#define VIVID_CID_HAS_COMPOSE_CAP (VIVID_CID_VIVID_BASE + 31)
+#define VIVID_CID_HAS_SCALER_CAP (VIVID_CID_VIVID_BASE + 32)
+#define VIVID_CID_HAS_CROP_OUT (VIVID_CID_VIVID_BASE + 33)
+#define VIVID_CID_HAS_COMPOSE_OUT (VIVID_CID_VIVID_BASE + 34)
+#define VIVID_CID_HAS_SCALER_OUT (VIVID_CID_VIVID_BASE + 35)
+#define VIVID_CID_LOOP_VIDEO (VIVID_CID_VIVID_BASE + 36)
+#define VIVID_CID_SEQ_WRAP (VIVID_CID_VIVID_BASE + 37)
+#define VIVID_CID_TIME_WRAP (VIVID_CID_VIVID_BASE + 38)
+#define VIVID_CID_MAX_EDID_BLOCKS (VIVID_CID_VIVID_BASE + 39)
+#define VIVID_CID_PERCENTAGE_FILL (VIVID_CID_VIVID_BASE + 40)
#define VIVID_CID_STD_SIGNAL_MODE (VIVID_CID_VIVID_BASE + 60)
#define VIVID_CID_STANDARD (VIVID_CID_VIVID_BASE + 61)
@@ -163,6 +168,42 @@
.step = 1,
};
+static const struct v4l2_ctrl_config vivid_ctrl_u32_array = {
+ .ops = &vivid_user_gen_ctrl_ops,
+ .id = VIVID_CID_U32_ARRAY,
+ .name = "U32 1 Element Array",
+ .type = V4L2_CTRL_TYPE_U32,
+ .def = 0x18,
+ .min = 0x10,
+ .max = 0x20000,
+ .step = 1,
+ .dims = { 1 },
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_u16_matrix = {
+ .ops = &vivid_user_gen_ctrl_ops,
+ .id = VIVID_CID_U16_MATRIX,
+ .name = "U16 8x16 Matrix",
+ .type = V4L2_CTRL_TYPE_U16,
+ .def = 0x18,
+ .min = 0x10,
+ .max = 0x2000,
+ .step = 1,
+ .dims = { 8, 16 },
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_u8_4d_array = {
+ .ops = &vivid_user_gen_ctrl_ops,
+ .id = VIVID_CID_U8_4D_ARRAY,
+ .name = "U8 2x3x4x5 Array",
+ .type = V4L2_CTRL_TYPE_U8,
+ .def = 0x18,
+ .min = 0x10,
+ .max = 0x20,
+ .step = 1,
+ .dims = { 2, 3, 4, 5 },
+};
+
static const char * const vivid_ctrl_menu_strings[] = {
"Menu Item 0 (Skipped)",
"Menu Item 1",
@@ -294,6 +335,16 @@
static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl)
{
+ static const u32 colorspaces[] = {
+ V4L2_COLORSPACE_SMPTE170M,
+ V4L2_COLORSPACE_REC709,
+ V4L2_COLORSPACE_SRGB,
+ V4L2_COLORSPACE_ADOBERGB,
+ V4L2_COLORSPACE_BT2020,
+ V4L2_COLORSPACE_SMPTE240M,
+ V4L2_COLORSPACE_470_SYSTEM_M,
+ V4L2_COLORSPACE_470_SYSTEM_BG,
+ };
struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, ctrl_hdl_vid_cap);
unsigned i;
@@ -303,7 +354,21 @@
tpg_s_pattern(&dev->tpg, ctrl->val);
break;
case VIVID_CID_COLORSPACE:
- tpg_s_colorspace(&dev->tpg, ctrl->val);
+ tpg_s_colorspace(&dev->tpg, colorspaces[ctrl->val]);
+ vivid_send_source_change(dev, TV);
+ vivid_send_source_change(dev, SVID);
+ vivid_send_source_change(dev, HDMI);
+ vivid_send_source_change(dev, WEBCAM);
+ break;
+ case VIVID_CID_YCBCR_ENC:
+ tpg_s_ycbcr_enc(&dev->tpg, ctrl->val);
+ vivid_send_source_change(dev, TV);
+ vivid_send_source_change(dev, SVID);
+ vivid_send_source_change(dev, HDMI);
+ vivid_send_source_change(dev, WEBCAM);
+ break;
+ case VIVID_CID_QUANTIZATION:
+ tpg_s_quantization(&dev->tpg, ctrl->val);
vivid_send_source_change(dev, TV);
vivid_send_source_change(dev, SVID);
vivid_send_source_change(dev, HDMI);
@@ -623,15 +688,14 @@
};
static const char * const vivid_ctrl_colorspace_strings[] = {
- "",
"SMPTE 170M",
- "SMPTE 240M",
"REC 709",
- "", /* Skip Bt878 entry */
+ "sRGB",
+ "AdobeRGB",
+ "BT.2020",
+ "SMPTE 240M",
"470 System M",
"470 System BG",
- "", /* Skip JPEG entry */
- "sRGB",
NULL,
};
@@ -640,13 +704,49 @@
.id = VIVID_CID_COLORSPACE,
.name = "Colorspace",
.type = V4L2_CTRL_TYPE_MENU,
- .min = 1,
- .max = 8,
- .menu_skip_mask = (1 << 4) | (1 << 7),
- .def = 8,
+ .max = 7,
+ .def = 2,
.qmenu = vivid_ctrl_colorspace_strings,
};
+static const char * const vivid_ctrl_ycbcr_enc_strings[] = {
+ "Default",
+ "ITU-R 601",
+ "Rec. 709",
+ "xvYCC 601",
+ "xvYCC 709",
+ "sYCC",
+ "BT.2020 Non-Constant Luminance",
+ "BT.2020 Constant Luminance",
+ "SMPTE 240M",
+ NULL,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_ycbcr_enc = {
+ .ops = &vivid_vid_cap_ctrl_ops,
+ .id = VIVID_CID_YCBCR_ENC,
+ .name = "Y'CbCr Encoding",
+ .type = V4L2_CTRL_TYPE_MENU,
+ .max = 8,
+ .qmenu = vivid_ctrl_ycbcr_enc_strings,
+};
+
+static const char * const vivid_ctrl_quantization_strings[] = {
+ "Default",
+ "Full Range",
+ "Limited Range",
+ NULL,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_quantization = {
+ .ops = &vivid_vid_cap_ctrl_ops,
+ .id = VIVID_CID_QUANTIZATION,
+ .name = "Quantization",
+ .type = V4L2_CTRL_TYPE_MENU,
+ .max = 2,
+ .qmenu = vivid_ctrl_quantization_strings,
+};
+
static const struct v4l2_ctrl_config vivid_ctrl_alpha_mode = {
.ops = &vivid_vid_cap_ctrl_ops,
.id = VIVID_CID_ALPHA_MODE,
@@ -723,8 +823,12 @@
dev->colorspace_out = V4L2_COLORSPACE_SMPTE170M;
else
dev->colorspace_out = V4L2_COLORSPACE_REC709;
+ dev->quantization_out = V4L2_QUANTIZATION_DEFAULT;
} else {
dev->colorspace_out = V4L2_COLORSPACE_SRGB;
+ dev->quantization_out = dev->dvi_d_out ?
+ V4L2_QUANTIZATION_LIM_RANGE :
+ V4L2_QUANTIZATION_DEFAULT;
}
if (dev->loop_video)
vivid_send_source_change(dev, HDMI);
@@ -792,15 +896,15 @@
dev->start_streaming_error = true;
break;
case VIVID_CID_QUEUE_ERROR:
- if (dev->vb_vid_cap_q.start_streaming_called)
+ if (vb2_start_streaming_called(&dev->vb_vid_cap_q))
vb2_queue_error(&dev->vb_vid_cap_q);
- if (dev->vb_vbi_cap_q.start_streaming_called)
+ if (vb2_start_streaming_called(&dev->vb_vbi_cap_q))
vb2_queue_error(&dev->vb_vbi_cap_q);
- if (dev->vb_vid_out_q.start_streaming_called)
+ if (vb2_start_streaming_called(&dev->vb_vid_out_q))
vb2_queue_error(&dev->vb_vid_out_q);
- if (dev->vb_vbi_out_q.start_streaming_called)
+ if (vb2_start_streaming_called(&dev->vb_vbi_out_q))
vb2_queue_error(&dev->vb_vbi_out_q);
- if (dev->vb_sdr_cap_q.start_streaming_called)
+ if (vb2_start_streaming_called(&dev->vb_sdr_cap_q))
vb2_queue_error(&dev->vb_sdr_cap_q);
break;
case VIVID_CID_SEQ_WRAP:
@@ -1222,6 +1326,9 @@
dev->string = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_string, NULL);
dev->bitmask = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_bitmask, NULL);
dev->int_menu = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_int_menu, NULL);
+ v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_u32_array, NULL);
+ v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_u16_matrix, NULL);
+ v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_u8_4d_array, NULL);
if (dev->has_vid_cap) {
/* Image Processing Controls */
@@ -1258,6 +1365,8 @@
v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_tstamp_src, NULL);
dev->colorspace = v4l2_ctrl_new_custom(hdl_vid_cap,
&vivid_ctrl_colorspace, NULL);
+ v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_ycbcr_enc, NULL);
+ v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_quantization, NULL);
v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_alpha_mode, NULL);
}
diff --git a/drivers/media/platform/vivid/vivid-sdr-cap.c b/drivers/media/platform/vivid/vivid-sdr-cap.c
index 8c5d661..4af55f1 100644
--- a/drivers/media/platform/vivid/vivid-sdr-cap.c
+++ b/drivers/media/platform/vivid/vivid-sdr-cap.c
@@ -297,8 +297,8 @@
.buf_queue = sdr_cap_buf_queue,
.start_streaming = sdr_cap_start_streaming,
.stop_streaming = sdr_cap_stop_streaming,
- .wait_prepare = vivid_unlock,
- .wait_finish = vivid_lock,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
};
int vivid_sdr_enum_freq_bands(struct file *file, void *fh, struct v4l2_frequency_band *band)
diff --git a/drivers/media/platform/vivid/vivid-tpg-colors.c b/drivers/media/platform/vivid/vivid-tpg-colors.c
index 2adddc0..424aa7a 100644
--- a/drivers/media/platform/vivid/vivid-tpg-colors.c
+++ b/drivers/media/platform/vivid/vivid-tpg-colors.c
@@ -12,7 +12,7 @@
* This source also contains the code used to generate the tpg_csc_colors
* table. Run the following command to compile it:
*
- * gcc vivid-colors.c -DCOMPILE_APP -o gen-colors -lm
+ * gcc vivid-tpg-colors.c -DCOMPILE_APP -o gen-colors -lm
*
* and run the utility.
*
@@ -78,22 +78,542 @@
#ifndef COMPILE_APP
/* Generated table */
-const struct color16 tpg_csc_colors[V4L2_COLORSPACE_SRGB + 1][TPG_COLOR_CSC_BLACK + 1] = {
- [V4L2_COLORSPACE_SMPTE170M][0] = { 2953, 2939, 2939 },
- [V4L2_COLORSPACE_SMPTE170M][1] = { 2954, 2963, 585 },
- [V4L2_COLORSPACE_SMPTE170M][2] = { 84, 2967, 2937 },
- [V4L2_COLORSPACE_SMPTE170M][3] = { 93, 2990, 575 },
- [V4L2_COLORSPACE_SMPTE170M][4] = { 3030, 259, 2933 },
- [V4L2_COLORSPACE_SMPTE170M][5] = { 3031, 406, 557 },
- [V4L2_COLORSPACE_SMPTE170M][6] = { 544, 428, 2931 },
- [V4L2_COLORSPACE_SMPTE170M][7] = { 551, 547, 547 },
+const unsigned short tpg_rec709_to_linear[255 * 16 + 1] = {
+ 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3,
+ 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7,
+ 7, 7, 8, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10,
+ 11, 11, 11, 11, 12, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14,
+ 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18,
+ 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 20, 21, 21, 21,
+ 21, 22, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 24, 25,
+ 25, 25, 25, 26, 26, 26, 26, 26, 27, 27, 27, 27, 28, 28, 28, 28,
+ 28, 29, 29, 29, 29, 30, 30, 30, 30, 30, 31, 31, 31, 31, 32, 32,
+ 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 34, 35, 35, 35, 35,
+ 36, 36, 36, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 38, 39, 39,
+ 39, 39, 40, 40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42, 42, 42,
+ 43, 43, 43, 43, 44, 44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46,
+ 46, 46, 47, 47, 47, 47, 48, 48, 48, 48, 48, 49, 49, 49, 49, 50,
+ 50, 50, 50, 50, 51, 51, 51, 51, 52, 52, 52, 52, 52, 53, 53, 53,
+ 53, 54, 54, 54, 54, 54, 55, 55, 55, 55, 56, 56, 56, 56, 56, 57,
+ 57, 57, 57, 58, 58, 58, 58, 58, 59, 59, 59, 59, 60, 60, 60, 60,
+ 60, 61, 61, 61, 61, 62, 62, 62, 62, 62, 63, 63, 63, 63, 64, 64,
+ 64, 64, 64, 65, 65, 65, 65, 66, 66, 66, 66, 66, 67, 67, 67, 67,
+ 68, 68, 68, 68, 68, 69, 69, 69, 69, 70, 70, 70, 70, 70, 71, 71,
+ 71, 71, 72, 72, 72, 72, 72, 73, 73, 73, 73, 73, 74, 74, 74, 74,
+ 74, 75, 75, 75, 75, 76, 76, 76, 76, 76, 77, 77, 77, 77, 78, 78,
+ 78, 78, 79, 79, 79, 79, 79, 80, 80, 80, 80, 81, 81, 81, 81, 82,
+ 82, 82, 82, 82, 83, 83, 83, 83, 84, 84, 84, 84, 85, 85, 85, 85,
+ 86, 86, 86, 86, 87, 87, 87, 87, 88, 88, 88, 88, 89, 89, 89, 89,
+ 90, 90, 90, 90, 91, 91, 91, 91, 92, 92, 92, 92, 93, 93, 93, 93,
+ 94, 94, 94, 94, 95, 95, 95, 95, 96, 96, 96, 96, 97, 97, 97, 97,
+ 98, 98, 98, 98, 99, 99, 99, 99, 100, 100, 100, 101, 101, 101, 101, 102,
+ 102, 102, 102, 103, 103, 103, 103, 104, 104, 104, 105, 105, 105, 105, 106, 106,
+ 106, 106, 107, 107, 107, 107, 108, 108, 108, 109, 109, 109, 109, 110, 110, 110,
+ 111, 111, 111, 111, 112, 112, 112, 112, 113, 113, 113, 114, 114, 114, 114, 115,
+ 115, 115, 116, 116, 116, 116, 117, 117, 117, 118, 118, 118, 118, 119, 119, 119,
+ 120, 120, 120, 120, 121, 121, 121, 122, 122, 122, 123, 123, 123, 123, 124, 124,
+ 124, 125, 125, 125, 125, 126, 126, 126, 127, 127, 127, 128, 128, 128, 128, 129,
+ 129, 129, 130, 130, 130, 131, 131, 131, 132, 132, 132, 132, 133, 133, 133, 134,
+ 134, 134, 135, 135, 135, 136, 136, 136, 136, 137, 137, 137, 138, 138, 138, 139,
+ 139, 139, 140, 140, 140, 141, 141, 141, 142, 142, 142, 142, 143, 143, 143, 144,
+ 144, 144, 145, 145, 145, 146, 146, 146, 147, 147, 147, 148, 148, 148, 149, 149,
+ 149, 150, 150, 150, 151, 151, 151, 152, 152, 152, 153, 153, 153, 154, 154, 154,
+ 155, 155, 155, 156, 156, 156, 157, 157, 157, 158, 158, 158, 159, 159, 159, 160,
+ 160, 160, 161, 161, 161, 162, 162, 162, 163, 163, 163, 164, 164, 164, 165, 165,
+ 165, 166, 166, 167, 167, 167, 168, 168, 168, 169, 169, 169, 170, 170, 170, 171,
+ 171, 171, 172, 172, 172, 173, 173, 174, 174, 174, 175, 175, 175, 176, 176, 176,
+ 177, 177, 177, 178, 178, 179, 179, 179, 180, 180, 180, 181, 181, 181, 182, 182,
+ 183, 183, 183, 184, 184, 184, 185, 185, 186, 186, 186, 187, 187, 187, 188, 188,
+ 188, 189, 189, 190, 190, 190, 191, 191, 191, 192, 192, 193, 193, 193, 194, 194,
+ 194, 195, 195, 196, 196, 196, 197, 197, 198, 198, 198, 199, 199, 199, 200, 200,
+ 201, 201, 201, 202, 202, 203, 203, 203, 204, 204, 204, 205, 205, 206, 206, 206,
+ 207, 207, 208, 208, 208, 209, 209, 210, 210, 210, 211, 211, 212, 212, 212, 213,
+ 213, 214, 214, 214, 215, 215, 216, 216, 216, 217, 217, 218, 218, 218, 219, 219,
+ 220, 220, 220, 221, 221, 222, 222, 222, 223, 223, 224, 224, 224, 225, 225, 226,
+ 226, 227, 227, 227, 228, 228, 229, 229, 229, 230, 230, 231, 231, 232, 232, 232,
+ 233, 233, 234, 234, 234, 235, 235, 236, 236, 237, 237, 237, 238, 238, 239, 239,
+ 240, 240, 240, 241, 241, 242, 242, 243, 243, 243, 244, 244, 245, 245, 246, 246,
+ 246, 247, 247, 248, 248, 249, 249, 249, 250, 250, 251, 251, 252, 252, 252, 253,
+ 253, 254, 254, 255, 255, 256, 256, 256, 257, 257, 258, 258, 259, 259, 260, 260,
+ 260, 261, 261, 262, 262, 263, 263, 264, 264, 264, 265, 265, 266, 266, 267, 267,
+ 268, 268, 269, 269, 269, 270, 270, 271, 271, 272, 272, 273, 273, 274, 274, 274,
+ 275, 275, 276, 276, 277, 277, 278, 278, 279, 279, 279, 280, 280, 281, 281, 282,
+ 282, 283, 283, 284, 284, 285, 285, 286, 286, 286, 287, 287, 288, 288, 289, 289,
+ 290, 290, 291, 291, 292, 292, 293, 293, 294, 294, 295, 295, 295, 296, 296, 297,
+ 297, 298, 298, 299, 299, 300, 300, 301, 301, 302, 302, 303, 303, 304, 304, 305,
+ 305, 306, 306, 307, 307, 308, 308, 309, 309, 309, 310, 310, 311, 311, 312, 312,
+ 313, 313, 314, 314, 315, 315, 316, 316, 317, 317, 318, 318, 319, 319, 320, 320,
+ 321, 321, 322, 322, 323, 323, 324, 324, 325, 325, 326, 326, 327, 327, 328, 328,
+ 329, 329, 330, 330, 331, 331, 332, 332, 333, 333, 334, 335, 335, 336, 336, 337,
+ 337, 338, 338, 339, 339, 340, 340, 341, 341, 342, 342, 343, 343, 344, 344, 345,
+ 345, 346, 346, 347, 347, 348, 348, 349, 349, 350, 351, 351, 352, 352, 353, 353,
+ 354, 354, 355, 355, 356, 356, 357, 357, 358, 358, 359, 360, 360, 361, 361, 362,
+ 362, 363, 363, 364, 364, 365, 365, 366, 366, 367, 368, 368, 369, 369, 370, 370,
+ 371, 371, 372, 372, 373, 373, 374, 375, 375, 376, 376, 377, 377, 378, 378, 379,
+ 379, 380, 381, 381, 382, 382, 383, 383, 384, 384, 385, 386, 386, 387, 387, 388,
+ 388, 389, 389, 390, 391, 391, 392, 392, 393, 393, 394, 394, 395, 396, 396, 397,
+ 397, 398, 398, 399, 399, 400, 401, 401, 402, 402, 403, 403, 404, 405, 405, 406,
+ 406, 407, 407, 408, 409, 409, 410, 410, 411, 411, 412, 413, 413, 414, 414, 415,
+ 415, 416, 417, 417, 418, 418, 419, 419, 420, 421, 421, 422, 422, 423, 424, 424,
+ 425, 425, 426, 426, 427, 428, 428, 429, 429, 430, 431, 431, 432, 432, 433, 433,
+ 434, 435, 435, 436, 436, 437, 438, 438, 439, 439, 440, 441, 441, 442, 442, 443,
+ 444, 444, 445, 445, 446, 447, 447, 448, 448, 449, 450, 450, 451, 451, 452, 453,
+ 453, 454, 454, 455, 456, 456, 457, 457, 458, 459, 459, 460, 460, 461, 462, 462,
+ 463, 463, 464, 465, 465, 466, 467, 467, 468, 468, 469, 470, 470, 471, 471, 472,
+ 473, 473, 474, 475, 475, 476, 476, 477, 478, 478, 479, 480, 480, 481, 481, 482,
+ 483, 483, 484, 485, 485, 486, 486, 487, 488, 488, 489, 490, 490, 491, 491, 492,
+ 493, 493, 494, 495, 495, 496, 497, 497, 498, 498, 499, 500, 500, 501, 502, 502,
+ 503, 504, 504, 505, 505, 506, 507, 507, 508, 509, 509, 510, 511, 511, 512, 513,
+ 513, 514, 514, 515, 516, 516, 517, 518, 518, 519, 520, 520, 521, 522, 522, 523,
+ 524, 524, 525, 526, 526, 527, 528, 528, 529, 529, 530, 531, 531, 532, 533, 533,
+ 534, 535, 535, 536, 537, 537, 538, 539, 539, 540, 541, 541, 542, 543, 543, 544,
+ 545, 545, 546, 547, 547, 548, 549, 549, 550, 551, 551, 552, 553, 553, 554, 555,
+ 555, 556, 557, 557, 558, 559, 560, 560, 561, 562, 562, 563, 564, 564, 565, 566,
+ 566, 567, 568, 568, 569, 570, 570, 571, 572, 572, 573, 574, 575, 575, 576, 577,
+ 577, 578, 579, 579, 580, 581, 581, 582, 583, 584, 584, 585, 586, 586, 587, 588,
+ 588, 589, 590, 590, 591, 592, 593, 593, 594, 595, 595, 596, 597, 598, 598, 599,
+ 600, 600, 601, 602, 602, 603, 604, 605, 605, 606, 607, 607, 608, 609, 610, 610,
+ 611, 612, 612, 613, 614, 615, 615, 616, 617, 617, 618, 619, 620, 620, 621, 622,
+ 622, 623, 624, 625, 625, 626, 627, 627, 628, 629, 630, 630, 631, 632, 632, 633,
+ 634, 635, 635, 636, 637, 638, 638, 639, 640, 640, 641, 642, 643, 643, 644, 645,
+ 646, 646, 647, 648, 649, 649, 650, 651, 652, 652, 653, 654, 654, 655, 656, 657,
+ 657, 658, 659, 660, 660, 661, 662, 663, 663, 664, 665, 666, 666, 667, 668, 669,
+ 669, 670, 671, 672, 672, 673, 674, 675, 675, 676, 677, 678, 678, 679, 680, 681,
+ 681, 682, 683, 684, 684, 685, 686, 687, 687, 688, 689, 690, 690, 691, 692, 693,
+ 694, 694, 695, 696, 697, 697, 698, 699, 700, 700, 701, 702, 703, 703, 704, 705,
+ 706, 707, 707, 708, 709, 710, 710, 711, 712, 713, 714, 714, 715, 716, 717, 717,
+ 718, 719, 720, 720, 721, 722, 723, 724, 724, 725, 726, 727, 728, 728, 729, 730,
+ 731, 731, 732, 733, 734, 735, 735, 736, 737, 738, 739, 739, 740, 741, 742, 742,
+ 743, 744, 745, 746, 746, 747, 748, 749, 750, 750, 751, 752, 753, 754, 754, 755,
+ 756, 757, 758, 758, 759, 760, 761, 762, 762, 763, 764, 765, 766, 766, 767, 768,
+ 769, 770, 771, 771, 772, 773, 774, 775, 775, 776, 777, 778, 779, 779, 780, 781,
+ 782, 783, 783, 784, 785, 786, 787, 788, 788, 789, 790, 791, 792, 793, 793, 794,
+ 795, 796, 797, 797, 798, 799, 800, 801, 802, 802, 803, 804, 805, 806, 807, 807,
+ 808, 809, 810, 811, 812, 812, 813, 814, 815, 816, 817, 817, 818, 819, 820, 821,
+ 822, 822, 823, 824, 825, 826, 827, 827, 828, 829, 830, 831, 832, 832, 833, 834,
+ 835, 836, 837, 838, 838, 839, 840, 841, 842, 843, 843, 844, 845, 846, 847, 848,
+ 849, 849, 850, 851, 852, 853, 854, 855, 855, 856, 857, 858, 859, 860, 861, 861,
+ 862, 863, 864, 865, 866, 867, 867, 868, 869, 870, 871, 872, 873, 873, 874, 875,
+ 876, 877, 878, 879, 880, 880, 881, 882, 883, 884, 885, 886, 887, 887, 888, 889,
+ 890, 891, 892, 893, 894, 894, 895, 896, 897, 898, 899, 900, 901, 901, 902, 903,
+ 904, 905, 906, 907, 908, 909, 909, 910, 911, 912, 913, 914, 915, 916, 916, 917,
+ 918, 919, 920, 921, 922, 923, 924, 925, 925, 926, 927, 928, 929, 930, 931, 932,
+ 933, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 942, 943, 944, 945, 946,
+ 947, 948, 949, 950, 951, 952, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961,
+ 962, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 973, 974, 975,
+ 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 985, 986, 987, 988, 989, 990,
+ 991, 992, 993, 994, 995, 996, 997, 998, 998, 999, 1000, 1001, 1002, 1003, 1004, 1005,
+ 1006, 1007, 1008, 1009, 1010, 1011, 1012, 1013, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020,
+ 1021, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1030, 1031, 1032, 1033, 1034, 1035,
+ 1036, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1050,
+ 1051, 1052, 1053, 1054, 1055, 1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066,
+ 1067, 1068, 1069, 1070, 1071, 1072, 1073, 1074, 1075, 1076, 1077, 1078, 1078, 1079, 1080, 1081,
+ 1082, 1083, 1084, 1085, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095, 1096, 1097,
+ 1098, 1099, 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, 1109, 1110, 1111, 1112, 1113,
+ 1114, 1115, 1116, 1117, 1118, 1119, 1120, 1121, 1122, 1123, 1124, 1125, 1126, 1127, 1128, 1129,
+ 1130, 1131, 1132, 1133, 1134, 1135, 1136, 1137, 1138, 1139, 1140, 1141, 1142, 1143, 1144, 1145,
+ 1146, 1147, 1148, 1149, 1150, 1151, 1152, 1153, 1154, 1155, 1156, 1157, 1158, 1159, 1160, 1161,
+ 1162, 1163, 1164, 1165, 1166, 1167, 1168, 1169, 1170, 1171, 1172, 1173, 1174, 1175, 1176, 1177,
+ 1178, 1179, 1180, 1181, 1182, 1183, 1184, 1185, 1186, 1187, 1188, 1189, 1190, 1191, 1193, 1194,
+ 1195, 1196, 1197, 1198, 1199, 1200, 1201, 1202, 1203, 1204, 1205, 1206, 1207, 1208, 1209, 1210,
+ 1211, 1212, 1213, 1214, 1215, 1216, 1217, 1218, 1219, 1220, 1221, 1223, 1224, 1225, 1226, 1227,
+ 1228, 1229, 1230, 1231, 1232, 1233, 1234, 1235, 1236, 1237, 1238, 1239, 1240, 1241, 1242, 1243,
+ 1245, 1246, 1247, 1248, 1249, 1250, 1251, 1252, 1253, 1254, 1255, 1256, 1257, 1258, 1259, 1260,
+ 1261, 1262, 1264, 1265, 1266, 1267, 1268, 1269, 1270, 1271, 1272, 1273, 1274, 1275, 1276, 1277,
+ 1278, 1280, 1281, 1282, 1283, 1284, 1285, 1286, 1287, 1288, 1289, 1290, 1291, 1292, 1293, 1295,
+ 1296, 1297, 1298, 1299, 1300, 1301, 1302, 1303, 1304, 1305, 1306, 1307, 1309, 1310, 1311, 1312,
+ 1313, 1314, 1315, 1316, 1317, 1318, 1319, 1320, 1322, 1323, 1324, 1325, 1326, 1327, 1328, 1329,
+ 1330, 1331, 1332, 1334, 1335, 1336, 1337, 1338, 1339, 1340, 1341, 1342, 1343, 1345, 1346, 1347,
+ 1348, 1349, 1350, 1351, 1352, 1353, 1354, 1356, 1357, 1358, 1359, 1360, 1361, 1362, 1363, 1364,
+ 1365, 1367, 1368, 1369, 1370, 1371, 1372, 1373, 1374, 1375, 1377, 1378, 1379, 1380, 1381, 1382,
+ 1383, 1384, 1385, 1387, 1388, 1389, 1390, 1391, 1392, 1393, 1394, 1396, 1397, 1398, 1399, 1400,
+ 1401, 1402, 1403, 1405, 1406, 1407, 1408, 1409, 1410, 1411, 1412, 1414, 1415, 1416, 1417, 1418,
+ 1419, 1420, 1421, 1423, 1424, 1425, 1426, 1427, 1428, 1429, 1431, 1432, 1433, 1434, 1435, 1436,
+ 1437, 1439, 1440, 1441, 1442, 1443, 1444, 1445, 1446, 1448, 1449, 1450, 1451, 1452, 1453, 1455,
+ 1456, 1457, 1458, 1459, 1460, 1461, 1463, 1464, 1465, 1466, 1467, 1468, 1469, 1471, 1472, 1473,
+ 1474, 1475, 1476, 1478, 1479, 1480, 1481, 1482, 1483, 1484, 1486, 1487, 1488, 1489, 1490, 1491,
+ 1493, 1494, 1495, 1496, 1497, 1498, 1500, 1501, 1502, 1503, 1504, 1505, 1507, 1508, 1509, 1510,
+ 1511, 1512, 1514, 1515, 1516, 1517, 1518, 1519, 1521, 1522, 1523, 1524, 1525, 1527, 1528, 1529,
+ 1530, 1531, 1532, 1534, 1535, 1536, 1537, 1538, 1540, 1541, 1542, 1543, 1544, 1545, 1547, 1548,
+ 1549, 1550, 1551, 1553, 1554, 1555, 1556, 1557, 1559, 1560, 1561, 1562, 1563, 1564, 1566, 1567,
+ 1568, 1569, 1570, 1572, 1573, 1574, 1575, 1576, 1578, 1579, 1580, 1581, 1582, 1584, 1585, 1586,
+ 1587, 1588, 1590, 1591, 1592, 1593, 1594, 1596, 1597, 1598, 1599, 1601, 1602, 1603, 1604, 1605,
+ 1607, 1608, 1609, 1610, 1611, 1613, 1614, 1615, 1616, 1617, 1619, 1620, 1621, 1622, 1624, 1625,
+ 1626, 1627, 1628, 1630, 1631, 1632, 1633, 1635, 1636, 1637, 1638, 1639, 1641, 1642, 1643, 1644,
+ 1646, 1647, 1648, 1649, 1650, 1652, 1653, 1654, 1655, 1657, 1658, 1659, 1660, 1662, 1663, 1664,
+ 1665, 1667, 1668, 1669, 1670, 1671, 1673, 1674, 1675, 1676, 1678, 1679, 1680, 1681, 1683, 1684,
+ 1685, 1686, 1688, 1689, 1690, 1691, 1693, 1694, 1695, 1696, 1698, 1699, 1700, 1701, 1703, 1704,
+ 1705, 1706, 1708, 1709, 1710, 1711, 1713, 1714, 1715, 1716, 1718, 1719, 1720, 1721, 1723, 1724,
+ 1725, 1726, 1728, 1729, 1730, 1731, 1733, 1734, 1735, 1737, 1738, 1739, 1740, 1742, 1743, 1744,
+ 1745, 1747, 1748, 1749, 1750, 1752, 1753, 1754, 1756, 1757, 1758, 1759, 1761, 1762, 1763, 1764,
+ 1766, 1767, 1768, 1770, 1771, 1772, 1773, 1775, 1776, 1777, 1778, 1780, 1781, 1782, 1784, 1785,
+ 1786, 1787, 1789, 1790, 1791, 1793, 1794, 1795, 1796, 1798, 1799, 1800, 1802, 1803, 1804, 1806,
+ 1807, 1808, 1809, 1811, 1812, 1813, 1815, 1816, 1817, 1818, 1820, 1821, 1822, 1824, 1825, 1826,
+ 1828, 1829, 1830, 1831, 1833, 1834, 1835, 1837, 1838, 1839, 1841, 1842, 1843, 1844, 1846, 1847,
+ 1848, 1850, 1851, 1852, 1854, 1855, 1856, 1858, 1859, 1860, 1862, 1863, 1864, 1865, 1867, 1868,
+ 1869, 1871, 1872, 1873, 1875, 1876, 1877, 1879, 1880, 1881, 1883, 1884, 1885, 1887, 1888, 1889,
+ 1891, 1892, 1893, 1894, 1896, 1897, 1898, 1900, 1901, 1902, 1904, 1905, 1906, 1908, 1909, 1910,
+ 1912, 1913, 1914, 1916, 1917, 1918, 1920, 1921, 1922, 1924, 1925, 1926, 1928, 1929, 1930, 1932,
+ 1933, 1935, 1936, 1937, 1939, 1940, 1941, 1943, 1944, 1945, 1947, 1948, 1949, 1951, 1952, 1953,
+ 1955, 1956, 1957, 1959, 1960, 1961, 1963, 1964, 1965, 1967, 1968, 1970, 1971, 1972, 1974, 1975,
+ 1976, 1978, 1979, 1980, 1982, 1983, 1984, 1986, 1987, 1989, 1990, 1991, 1993, 1994, 1995, 1997,
+ 1998, 1999, 2001, 2002, 2004, 2005, 2006, 2008, 2009, 2010, 2012, 2013, 2015, 2016, 2017, 2019,
+ 2020, 2021, 2023, 2024, 2026, 2027, 2028, 2030, 2031, 2032, 2034, 2035, 2037, 2038, 2039, 2041,
+ 2042, 2043, 2045, 2046, 2048, 2049, 2050, 2052, 2053, 2055, 2056, 2057, 2059, 2060, 2061, 2063,
+ 2064, 2066, 2067, 2068, 2070, 2071, 2073, 2074, 2075, 2077, 2078, 2080, 2081, 2082, 2084, 2085,
+ 2087, 2088, 2089, 2091, 2092, 2094, 2095, 2096, 2098, 2099, 2101, 2102, 2103, 2105, 2106, 2108,
+ 2109, 2110, 2112, 2113, 2115, 2116, 2117, 2119, 2120, 2122, 2123, 2124, 2126, 2127, 2129, 2130,
+ 2132, 2133, 2134, 2136, 2137, 2139, 2140, 2141, 2143, 2144, 2146, 2147, 2149, 2150, 2151, 2153,
+ 2154, 2156, 2157, 2159, 2160, 2161, 2163, 2164, 2166, 2167, 2169, 2170, 2171, 2173, 2174, 2176,
+ 2177, 2179, 2180, 2181, 2183, 2184, 2186, 2187, 2189, 2190, 2191, 2193, 2194, 2196, 2197, 2199,
+ 2200, 2202, 2203, 2204, 2206, 2207, 2209, 2210, 2212, 2213, 2214, 2216, 2217, 2219, 2220, 2222,
+ 2223, 2225, 2226, 2228, 2229, 2230, 2232, 2233, 2235, 2236, 2238, 2239, 2241, 2242, 2243, 2245,
+ 2246, 2248, 2249, 2251, 2252, 2254, 2255, 2257, 2258, 2260, 2261, 2262, 2264, 2265, 2267, 2268,
+ 2270, 2271, 2273, 2274, 2276, 2277, 2279, 2280, 2282, 2283, 2284, 2286, 2287, 2289, 2290, 2292,
+ 2293, 2295, 2296, 2298, 2299, 2301, 2302, 2304, 2305, 2307, 2308, 2310, 2311, 2312, 2314, 2315,
+ 2317, 2318, 2320, 2321, 2323, 2324, 2326, 2327, 2329, 2330, 2332, 2333, 2335, 2336, 2338, 2339,
+ 2341, 2342, 2344, 2345, 2347, 2348, 2350, 2351, 2353, 2354, 2356, 2357, 2359, 2360, 2362, 2363,
+ 2365, 2366, 2368, 2369, 2371, 2372, 2374, 2375, 2377, 2378, 2380, 2381, 2383, 2384, 2386, 2387,
+ 2389, 2390, 2392, 2393, 2395, 2396, 2398, 2399, 2401, 2402, 2404, 2405, 2407, 2408, 2410, 2411,
+ 2413, 2414, 2416, 2417, 2419, 2420, 2422, 2423, 2425, 2426, 2428, 2429, 2431, 2433, 2434, 2436,
+ 2437, 2439, 2440, 2442, 2443, 2445, 2446, 2448, 2449, 2451, 2452, 2454, 2455, 2457, 2458, 2460,
+ 2462, 2463, 2465, 2466, 2468, 2469, 2471, 2472, 2474, 2475, 2477, 2478, 2480, 2481, 2483, 2485,
+ 2486, 2488, 2489, 2491, 2492, 2494, 2495, 2497, 2498, 2500, 2502, 2503, 2505, 2506, 2508, 2509,
+ 2511, 2512, 2514, 2515, 2517, 2519, 2520, 2522, 2523, 2525, 2526, 2528, 2529, 2531, 2533, 2534,
+ 2536, 2537, 2539, 2540, 2542, 2543, 2545, 2547, 2548, 2550, 2551, 2553, 2554, 2556, 2557, 2559,
+ 2561, 2562, 2564, 2565, 2567, 2568, 2570, 2572, 2573, 2575, 2576, 2578, 2579, 2581, 2583, 2584,
+ 2586, 2587, 2589, 2590, 2592, 2594, 2595, 2597, 2598, 2600, 2601, 2603, 2605, 2606, 2608, 2609,
+ 2611, 2613, 2614, 2616, 2617, 2619, 2620, 2622, 2624, 2625, 2627, 2628, 2630, 2632, 2633, 2635,
+ 2636, 2638, 2640, 2641, 2643, 2644, 2646, 2647, 2649, 2651, 2652, 2654, 2655, 2657, 2659, 2660,
+ 2662, 2663, 2665, 2667, 2668, 2670, 2671, 2673, 2675, 2676, 2678, 2679, 2681, 2683, 2684, 2686,
+ 2687, 2689, 2691, 2692, 2694, 2696, 2697, 2699, 2700, 2702, 2704, 2705, 2707, 2708, 2710, 2712,
+ 2713, 2715, 2716, 2718, 2720, 2721, 2723, 2725, 2726, 2728, 2729, 2731, 2733, 2734, 2736, 2738,
+ 2739, 2741, 2742, 2744, 2746, 2747, 2749, 2751, 2752, 2754, 2755, 2757, 2759, 2760, 2762, 2764,
+ 2765, 2767, 2769, 2770, 2772, 2773, 2775, 2777, 2778, 2780, 2782, 2783, 2785, 2787, 2788, 2790,
+ 2791, 2793, 2795, 2796, 2798, 2800, 2801, 2803, 2805, 2806, 2808, 2810, 2811, 2813, 2814, 2816,
+ 2818, 2819, 2821, 2823, 2824, 2826, 2828, 2829, 2831, 2833, 2834, 2836, 2838, 2839, 2841, 2843,
+ 2844, 2846, 2848, 2849, 2851, 2853, 2854, 2856, 2857, 2859, 2861, 2862, 2864, 2866, 2867, 2869,
+ 2871, 2872, 2874, 2876, 2877, 2879, 2881, 2882, 2884, 2886, 2888, 2889, 2891, 2893, 2894, 2896,
+ 2898, 2899, 2901, 2903, 2904, 2906, 2908, 2909, 2911, 2913, 2914, 2916, 2918, 2919, 2921, 2923,
+ 2924, 2926, 2928, 2929, 2931, 2933, 2935, 2936, 2938, 2940, 2941, 2943, 2945, 2946, 2948, 2950,
+ 2951, 2953, 2955, 2956, 2958, 2960, 2962, 2963, 2965, 2967, 2968, 2970, 2972, 2973, 2975, 2977,
+ 2979, 2980, 2982, 2984, 2985, 2987, 2989, 2990, 2992, 2994, 2996, 2997, 2999, 3001, 3002, 3004,
+ 3006, 3008, 3009, 3011, 3013, 3014, 3016, 3018, 3020, 3021, 3023, 3025, 3026, 3028, 3030, 3032,
+ 3033, 3035, 3037, 3038, 3040, 3042, 3044, 3045, 3047, 3049, 3050, 3052, 3054, 3056, 3057, 3059,
+ 3061, 3063, 3064, 3066, 3068, 3069, 3071, 3073, 3075, 3076, 3078, 3080, 3082, 3083, 3085, 3087,
+ 3089, 3090, 3092, 3094, 3095, 3097, 3099, 3101, 3102, 3104, 3106, 3108, 3109, 3111, 3113, 3115,
+ 3116, 3118, 3120, 3122, 3123, 3125, 3127, 3129, 3130, 3132, 3134, 3136, 3137, 3139, 3141, 3143,
+ 3144, 3146, 3148, 3150, 3151, 3153, 3155, 3157, 3158, 3160, 3162, 3164, 3165, 3167, 3169, 3171,
+ 3172, 3174, 3176, 3178, 3179, 3181, 3183, 3185, 3187, 3188, 3190, 3192, 3194, 3195, 3197, 3199,
+ 3201, 3202, 3204, 3206, 3208, 3209, 3211, 3213, 3215, 3217, 3218, 3220, 3222, 3224, 3225, 3227,
+ 3229, 3231, 3233, 3234, 3236, 3238, 3240, 3241, 3243, 3245, 3247, 3249, 3250, 3252, 3254, 3256,
+ 3258, 3259, 3261, 3263, 3265, 3266, 3268, 3270, 3272, 3274, 3275, 3277, 3279, 3281, 3283, 3284,
+ 3286, 3288, 3290, 3292, 3293, 3295, 3297, 3299, 3301, 3302, 3304, 3306, 3308, 3310, 3311, 3313,
+ 3315, 3317, 3319, 3320, 3322, 3324, 3326, 3328, 3329, 3331, 3333, 3335, 3337, 3338, 3340, 3342,
+ 3344, 3346, 3348, 3349, 3351, 3353, 3355, 3357, 3358, 3360, 3362, 3364, 3366, 3368, 3369, 3371,
+ 3373, 3375, 3377, 3378, 3380, 3382, 3384, 3386, 3388, 3389, 3391, 3393, 3395, 3397, 3399, 3400,
+ 3402, 3404, 3406, 3408, 3410, 3411, 3413, 3415, 3417, 3419, 3421, 3422, 3424, 3426, 3428, 3430,
+ 3432, 3433, 3435, 3437, 3439, 3441, 3443, 3444, 3446, 3448, 3450, 3452, 3454, 3455, 3457, 3459,
+ 3461, 3463, 3465, 3467, 3468, 3470, 3472, 3474, 3476, 3478, 3480, 3481, 3483, 3485, 3487, 3489,
+ 3491, 3492, 3494, 3496, 3498, 3500, 3502, 3504, 3506, 3507, 3509, 3511, 3513, 3515, 3517, 3519,
+ 3520, 3522, 3524, 3526, 3528, 3530, 3532, 3533, 3535, 3537, 3539, 3541, 3543, 3545, 3547, 3548,
+ 3550, 3552, 3554, 3556, 3558, 3560, 3562, 3563, 3565, 3567, 3569, 3571, 3573, 3575, 3577, 3578,
+ 3580, 3582, 3584, 3586, 3588, 3590, 3592, 3594, 3595, 3597, 3599, 3601, 3603, 3605, 3607, 3609,
+ 3611, 3612, 3614, 3616, 3618, 3620, 3622, 3624, 3626, 3628, 3629, 3631, 3633, 3635, 3637, 3639,
+ 3641, 3643, 3645, 3647, 3648, 3650, 3652, 3654, 3656, 3658, 3660, 3662, 3664, 3666, 3667, 3669,
+ 3671, 3673, 3675, 3677, 3679, 3681, 3683, 3685, 3687, 3688, 3690, 3692, 3694, 3696, 3698, 3700,
+ 3702, 3704, 3706, 3708, 3710, 3711, 3713, 3715, 3717, 3719, 3721, 3723, 3725, 3727, 3729, 3731,
+ 3733, 3735, 3736, 3738, 3740, 3742, 3744, 3746, 3748, 3750, 3752, 3754, 3756, 3758, 3760, 3762,
+ 3764, 3765, 3767, 3769, 3771, 3773, 3775, 3777, 3779, 3781, 3783, 3785, 3787, 3789, 3791, 3793,
+ 3795, 3796, 3798, 3800, 3802, 3804, 3806, 3808, 3810, 3812, 3814, 3816, 3818, 3820, 3822, 3824,
+ 3826, 3828, 3830, 3832, 3833, 3835, 3837, 3839, 3841, 3843, 3845, 3847, 3849, 3851, 3853, 3855,
+ 3857, 3859, 3861, 3863, 3865, 3867, 3869, 3871, 3873, 3875, 3877, 3879, 3881, 3883, 3884, 3886,
+ 3888, 3890, 3892, 3894, 3896, 3898, 3900, 3902, 3904, 3906, 3908, 3910, 3912, 3914, 3916, 3918,
+ 3920, 3922, 3924, 3926, 3928, 3930, 3932, 3934, 3936, 3938, 3940, 3942, 3944, 3946, 3948, 3950,
+ 3952, 3954, 3956, 3958, 3960, 3962, 3964, 3966, 3968, 3970, 3972, 3974, 3976, 3978, 3980, 3982,
+ 3984, 3986, 3988, 3990, 3992, 3994, 3996, 3998, 4000, 4002, 4004, 4006, 4008, 4010, 4012, 4014,
+ 4016, 4018, 4020, 4022, 4024, 4026, 4028, 4030, 4032, 4034, 4036, 4038, 4040, 4042, 4044, 4046,
+ 4048, 4050, 4052, 4054, 4056, 4058, 4060, 4062, 4064, 4066, 4068, 4070, 4072, 4074, 4076, 4078,
+ 4080,
+};
+
+/* Generated table */
+const unsigned short tpg_linear_to_rec709[255 * 16 + 1] = {
+ 0, 5, 9, 14, 18, 22, 27, 32, 36, 41, 45, 50, 54, 59, 63, 68,
+ 72, 77, 81, 86, 90, 95, 99, 104, 108, 113, 117, 122, 126, 131, 135, 139,
+ 144, 149, 153, 158, 162, 167, 171, 176, 180, 185, 189, 194, 198, 203, 207, 212,
+ 216, 221, 225, 230, 234, 239, 243, 248, 252, 257, 261, 266, 270, 275, 279, 284,
+ 288, 293, 297, 302, 306, 311, 315, 320, 324, 328, 334, 338, 343, 347, 352, 356,
+ 360, 365, 369, 373, 377, 381, 386, 390, 394, 398, 402, 406, 410, 414, 418, 422,
+ 426, 430, 433, 437, 441, 445, 449, 452, 456, 460, 464, 467, 471, 475, 478, 482,
+ 485, 489, 492, 496, 499, 503, 506, 510, 513, 517, 520, 524, 527, 530, 534, 537,
+ 540, 544, 547, 550, 554, 557, 560, 563, 566, 570, 573, 576, 579, 582, 586, 589,
+ 592, 595, 598, 601, 604, 607, 610, 613, 616, 619, 622, 625, 628, 631, 634, 637,
+ 640, 643, 646, 649, 652, 655, 658, 660, 663, 666, 669, 672, 675, 677, 680, 683,
+ 686, 689, 691, 694, 697, 700, 702, 705, 708, 711, 713, 716, 719, 721, 724, 727,
+ 729, 732, 735, 737, 740, 743, 745, 748, 750, 753, 756, 758, 761, 763, 766, 768,
+ 771, 773, 776, 779, 781, 784, 786, 789, 791, 794, 796, 799, 801, 803, 806, 808,
+ 811, 813, 816, 818, 821, 823, 825, 828, 830, 833, 835, 837, 840, 842, 844, 847,
+ 849, 851, 854, 856, 858, 861, 863, 865, 868, 870, 872, 875, 877, 879, 881, 884,
+ 886, 888, 891, 893, 895, 897, 900, 902, 904, 906, 908, 911, 913, 915, 917, 919,
+ 922, 924, 926, 928, 930, 933, 935, 937, 939, 941, 943, 946, 948, 950, 952, 954,
+ 956, 958, 960, 963, 965, 967, 969, 971, 973, 975, 977, 979, 981, 984, 986, 988,
+ 990, 992, 994, 996, 998, 1000, 1002, 1004, 1006, 1008, 1010, 1012, 1014, 1016, 1018, 1020,
+ 1022, 1024, 1026, 1028, 1030, 1032, 1034, 1036, 1038, 1040, 1042, 1044, 1046, 1048, 1050, 1052,
+ 1054, 1056, 1058, 1060, 1062, 1064, 1066, 1068, 1069, 1071, 1073, 1075, 1077, 1079, 1081, 1083,
+ 1085, 1087, 1089, 1090, 1092, 1094, 1096, 1098, 1100, 1102, 1104, 1106, 1107, 1109, 1111, 1113,
+ 1115, 1117, 1119, 1120, 1122, 1124, 1126, 1128, 1130, 1131, 1133, 1135, 1137, 1139, 1141, 1142,
+ 1144, 1146, 1148, 1150, 1151, 1153, 1155, 1157, 1159, 1160, 1162, 1164, 1166, 1168, 1169, 1171,
+ 1173, 1175, 1176, 1178, 1180, 1182, 1184, 1185, 1187, 1189, 1191, 1192, 1194, 1196, 1198, 1199,
+ 1201, 1203, 1204, 1206, 1208, 1210, 1211, 1213, 1215, 1217, 1218, 1220, 1222, 1223, 1225, 1227,
+ 1228, 1230, 1232, 1234, 1235, 1237, 1239, 1240, 1242, 1244, 1245, 1247, 1249, 1250, 1252, 1254,
+ 1255, 1257, 1259, 1260, 1262, 1264, 1265, 1267, 1269, 1270, 1272, 1274, 1275, 1277, 1279, 1280,
+ 1282, 1283, 1285, 1287, 1288, 1290, 1292, 1293, 1295, 1296, 1298, 1300, 1301, 1303, 1305, 1306,
+ 1308, 1309, 1311, 1313, 1314, 1316, 1317, 1319, 1321, 1322, 1324, 1325, 1327, 1328, 1330, 1332,
+ 1333, 1335, 1336, 1338, 1339, 1341, 1343, 1344, 1346, 1347, 1349, 1350, 1352, 1354, 1355, 1357,
+ 1358, 1360, 1361, 1363, 1364, 1366, 1367, 1369, 1371, 1372, 1374, 1375, 1377, 1378, 1380, 1381,
+ 1383, 1384, 1386, 1387, 1389, 1390, 1392, 1393, 1395, 1396, 1398, 1399, 1401, 1402, 1404, 1405,
+ 1407, 1408, 1410, 1411, 1413, 1414, 1416, 1417, 1419, 1420, 1422, 1423, 1425, 1426, 1428, 1429,
+ 1431, 1432, 1434, 1435, 1437, 1438, 1440, 1441, 1442, 1444, 1445, 1447, 1448, 1450, 1451, 1453,
+ 1454, 1456, 1457, 1458, 1460, 1461, 1463, 1464, 1466, 1467, 1469, 1470, 1471, 1473, 1474, 1476,
+ 1477, 1479, 1480, 1481, 1483, 1484, 1486, 1487, 1489, 1490, 1491, 1493, 1494, 1496, 1497, 1498,
+ 1500, 1501, 1503, 1504, 1505, 1507, 1508, 1510, 1511, 1512, 1514, 1515, 1517, 1518, 1519, 1521,
+ 1522, 1524, 1525, 1526, 1528, 1529, 1531, 1532, 1533, 1535, 1536, 1537, 1539, 1540, 1542, 1543,
+ 1544, 1546, 1547, 1548, 1550, 1551, 1553, 1554, 1555, 1557, 1558, 1559, 1561, 1562, 1563, 1565,
+ 1566, 1567, 1569, 1570, 1571, 1573, 1574, 1576, 1577, 1578, 1580, 1581, 1582, 1584, 1585, 1586,
+ 1588, 1589, 1590, 1592, 1593, 1594, 1596, 1597, 1598, 1600, 1601, 1602, 1603, 1605, 1606, 1607,
+ 1609, 1610, 1611, 1613, 1614, 1615, 1617, 1618, 1619, 1621, 1622, 1623, 1624, 1626, 1627, 1628,
+ 1630, 1631, 1632, 1634, 1635, 1636, 1637, 1639, 1640, 1641, 1643, 1644, 1645, 1647, 1648, 1649,
+ 1650, 1652, 1653, 1654, 1655, 1657, 1658, 1659, 1661, 1662, 1663, 1664, 1666, 1667, 1668, 1670,
+ 1671, 1672, 1673, 1675, 1676, 1677, 1678, 1680, 1681, 1682, 1683, 1685, 1686, 1687, 1688, 1690,
+ 1691, 1692, 1693, 1695, 1696, 1697, 1698, 1700, 1701, 1702, 1703, 1705, 1706, 1707, 1708, 1710,
+ 1711, 1712, 1713, 1715, 1716, 1717, 1718, 1720, 1721, 1722, 1723, 1724, 1726, 1727, 1728, 1729,
+ 1731, 1732, 1733, 1734, 1736, 1737, 1738, 1739, 1740, 1742, 1743, 1744, 1745, 1746, 1748, 1749,
+ 1750, 1751, 1753, 1754, 1755, 1756, 1757, 1759, 1760, 1761, 1762, 1763, 1765, 1766, 1767, 1768,
+ 1769, 1771, 1772, 1773, 1774, 1775, 1777, 1778, 1779, 1780, 1781, 1783, 1784, 1785, 1786, 1787,
+ 1788, 1790, 1791, 1792, 1793, 1794, 1796, 1797, 1798, 1799, 1800, 1801, 1803, 1804, 1805, 1806,
+ 1807, 1809, 1810, 1811, 1812, 1813, 1814, 1816, 1817, 1818, 1819, 1820, 1821, 1823, 1824, 1825,
+ 1826, 1827, 1828, 1829, 1831, 1832, 1833, 1834, 1835, 1836, 1838, 1839, 1840, 1841, 1842, 1843,
+ 1845, 1846, 1847, 1848, 1849, 1850, 1851, 1853, 1854, 1855, 1856, 1857, 1858, 1859, 1861, 1862,
+ 1863, 1864, 1865, 1866, 1867, 1868, 1870, 1871, 1872, 1873, 1874, 1875, 1876, 1878, 1879, 1880,
+ 1881, 1882, 1883, 1884, 1885, 1887, 1888, 1889, 1890, 1891, 1892, 1893, 1894, 1896, 1897, 1898,
+ 1899, 1900, 1901, 1902, 1903, 1904, 1906, 1907, 1908, 1909, 1910, 1911, 1912, 1913, 1914, 1916,
+ 1917, 1918, 1919, 1920, 1921, 1922, 1923, 1924, 1925, 1927, 1928, 1929, 1930, 1931, 1932, 1933,
+ 1934, 1935, 1936, 1938, 1939, 1940, 1941, 1942, 1943, 1944, 1945, 1946, 1947, 1948, 1950, 1951,
+ 1952, 1953, 1954, 1955, 1956, 1957, 1958, 1959, 1960, 1961, 1963, 1964, 1965, 1966, 1967, 1968,
+ 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1977, 1978, 1979, 1980, 1981, 1982, 1983, 1984, 1985,
+ 1986, 1987, 1988, 1989, 1990, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+ 2003, 2004, 2005, 2006, 2007, 2008, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
+ 2020, 2021, 2022, 2023, 2024, 2025, 2026, 2027, 2028, 2029, 2031, 2032, 2033, 2034, 2035, 2036,
+ 2037, 2038, 2039, 2040, 2041, 2042, 2043, 2044, 2045, 2046, 2047, 2048, 2049, 2050, 2051, 2052,
+ 2053, 2054, 2055, 2056, 2057, 2058, 2060, 2061, 2062, 2063, 2064, 2065, 2066, 2067, 2068, 2069,
+ 2070, 2071, 2072, 2073, 2074, 2075, 2076, 2077, 2078, 2079, 2080, 2081, 2082, 2083, 2084, 2085,
+ 2086, 2087, 2088, 2089, 2090, 2091, 2092, 2093, 2094, 2095, 2096, 2097, 2098, 2099, 2100, 2101,
+ 2102, 2103, 2104, 2105, 2106, 2107, 2108, 2109, 2110, 2111, 2112, 2113, 2114, 2115, 2116, 2117,
+ 2118, 2119, 2120, 2121, 2122, 2123, 2124, 2125, 2126, 2127, 2128, 2129, 2130, 2131, 2132, 2133,
+ 2134, 2135, 2136, 2137, 2138, 2139, 2140, 2141, 2142, 2143, 2144, 2145, 2146, 2147, 2148, 2149,
+ 2150, 2151, 2152, 2153, 2154, 2155, 2156, 2157, 2158, 2159, 2160, 2161, 2162, 2163, 2164, 2165,
+ 2166, 2167, 2168, 2169, 2170, 2171, 2172, 2173, 2173, 2174, 2175, 2176, 2177, 2178, 2179, 2180,
+ 2181, 2182, 2183, 2184, 2185, 2186, 2187, 2188, 2189, 2190, 2191, 2192, 2193, 2194, 2195, 2196,
+ 2197, 2198, 2199, 2200, 2201, 2202, 2202, 2203, 2204, 2205, 2206, 2207, 2208, 2209, 2210, 2211,
+ 2212, 2213, 2214, 2215, 2216, 2217, 2218, 2219, 2220, 2221, 2222, 2223, 2224, 2224, 2225, 2226,
+ 2227, 2228, 2229, 2230, 2231, 2232, 2233, 2234, 2235, 2236, 2237, 2238, 2239, 2240, 2241, 2241,
+ 2242, 2243, 2244, 2245, 2246, 2247, 2248, 2249, 2250, 2251, 2252, 2253, 2254, 2255, 2256, 2257,
+ 2257, 2258, 2259, 2260, 2261, 2262, 2263, 2264, 2265, 2266, 2267, 2268, 2269, 2270, 2271, 2271,
+ 2272, 2273, 2274, 2275, 2276, 2277, 2278, 2279, 2280, 2281, 2282, 2283, 2283, 2284, 2285, 2286,
+ 2287, 2288, 2289, 2290, 2291, 2292, 2293, 2294, 2295, 2295, 2296, 2297, 2298, 2299, 2300, 2301,
+ 2302, 2303, 2304, 2305, 2306, 2306, 2307, 2308, 2309, 2310, 2311, 2312, 2313, 2314, 2315, 2316,
+ 2317, 2317, 2318, 2319, 2320, 2321, 2322, 2323, 2324, 2325, 2326, 2327, 2327, 2328, 2329, 2330,
+ 2331, 2332, 2333, 2334, 2335, 2336, 2336, 2337, 2338, 2339, 2340, 2341, 2342, 2343, 2344, 2345,
+ 2345, 2346, 2347, 2348, 2349, 2350, 2351, 2352, 2353, 2354, 2354, 2355, 2356, 2357, 2358, 2359,
+ 2360, 2361, 2362, 2363, 2363, 2364, 2365, 2366, 2367, 2368, 2369, 2370, 2371, 2371, 2372, 2373,
+ 2374, 2375, 2376, 2377, 2378, 2379, 2379, 2380, 2381, 2382, 2383, 2384, 2385, 2386, 2386, 2387,
+ 2388, 2389, 2390, 2391, 2392, 2393, 2394, 2394, 2395, 2396, 2397, 2398, 2399, 2400, 2401, 2401,
+ 2402, 2403, 2404, 2405, 2406, 2407, 2408, 2408, 2409, 2410, 2411, 2412, 2413, 2414, 2415, 2415,
+ 2416, 2417, 2418, 2419, 2420, 2421, 2422, 2422, 2423, 2424, 2425, 2426, 2427, 2428, 2428, 2429,
+ 2430, 2431, 2432, 2433, 2434, 2435, 2435, 2436, 2437, 2438, 2439, 2440, 2441, 2441, 2442, 2443,
+ 2444, 2445, 2446, 2447, 2447, 2448, 2449, 2450, 2451, 2452, 2453, 2453, 2454, 2455, 2456, 2457,
+ 2458, 2459, 2459, 2460, 2461, 2462, 2463, 2464, 2465, 2465, 2466, 2467, 2468, 2469, 2470, 2471,
+ 2471, 2472, 2473, 2474, 2475, 2476, 2477, 2477, 2478, 2479, 2480, 2481, 2482, 2482, 2483, 2484,
+ 2485, 2486, 2487, 2488, 2488, 2489, 2490, 2491, 2492, 2493, 2493, 2494, 2495, 2496, 2497, 2498,
+ 2499, 2499, 2500, 2501, 2502, 2503, 2504, 2504, 2505, 2506, 2507, 2508, 2509, 2509, 2510, 2511,
+ 2512, 2513, 2514, 2514, 2515, 2516, 2517, 2518, 2519, 2519, 2520, 2521, 2522, 2523, 2524, 2524,
+ 2525, 2526, 2527, 2528, 2529, 2529, 2530, 2531, 2532, 2533, 2534, 2534, 2535, 2536, 2537, 2538,
+ 2539, 2539, 2540, 2541, 2542, 2543, 2544, 2544, 2545, 2546, 2547, 2548, 2548, 2549, 2550, 2551,
+ 2552, 2553, 2553, 2554, 2555, 2556, 2557, 2558, 2558, 2559, 2560, 2561, 2562, 2562, 2563, 2564,
+ 2565, 2566, 2567, 2567, 2568, 2569, 2570, 2571, 2571, 2572, 2573, 2574, 2575, 2576, 2576, 2577,
+ 2578, 2579, 2580, 2580, 2581, 2582, 2583, 2584, 2584, 2585, 2586, 2587, 2588, 2589, 2589, 2590,
+ 2591, 2592, 2593, 2593, 2594, 2595, 2596, 2597, 2597, 2598, 2599, 2600, 2601, 2601, 2602, 2603,
+ 2604, 2605, 2605, 2606, 2607, 2608, 2609, 2610, 2610, 2611, 2612, 2613, 2614, 2614, 2615, 2616,
+ 2617, 2618, 2618, 2619, 2620, 2621, 2622, 2622, 2623, 2624, 2625, 2626, 2626, 2627, 2628, 2629,
+ 2630, 2630, 2631, 2632, 2633, 2634, 2634, 2635, 2636, 2637, 2637, 2638, 2639, 2640, 2641, 2641,
+ 2642, 2643, 2644, 2645, 2645, 2646, 2647, 2648, 2649, 2649, 2650, 2651, 2652, 2653, 2653, 2654,
+ 2655, 2656, 2656, 2657, 2658, 2659, 2660, 2660, 2661, 2662, 2663, 2664, 2664, 2665, 2666, 2667,
+ 2668, 2668, 2669, 2670, 2671, 2671, 2672, 2673, 2674, 2675, 2675, 2676, 2677, 2678, 2678, 2679,
+ 2680, 2681, 2682, 2682, 2683, 2684, 2685, 2686, 2686, 2687, 2688, 2689, 2689, 2690, 2691, 2692,
+ 2693, 2693, 2694, 2695, 2696, 2696, 2697, 2698, 2699, 2700, 2700, 2701, 2702, 2703, 2703, 2704,
+ 2705, 2706, 2706, 2707, 2708, 2709, 2710, 2710, 2711, 2712, 2713, 2713, 2714, 2715, 2716, 2717,
+ 2717, 2718, 2719, 2720, 2720, 2721, 2722, 2723, 2723, 2724, 2725, 2726, 2727, 2727, 2728, 2729,
+ 2730, 2730, 2731, 2732, 2733, 2733, 2734, 2735, 2736, 2736, 2737, 2738, 2739, 2740, 2740, 2741,
+ 2742, 2743, 2743, 2744, 2745, 2746, 2746, 2747, 2748, 2749, 2749, 2750, 2751, 2752, 2752, 2753,
+ 2754, 2755, 2755, 2756, 2757, 2758, 2759, 2759, 2760, 2761, 2762, 2762, 2763, 2764, 2765, 2765,
+ 2766, 2767, 2768, 2768, 2769, 2770, 2771, 2771, 2772, 2773, 2774, 2774, 2775, 2776, 2777, 2777,
+ 2778, 2779, 2780, 2780, 2781, 2782, 2783, 2783, 2784, 2785, 2786, 2786, 2787, 2788, 2789, 2789,
+ 2790, 2791, 2792, 2792, 2793, 2794, 2795, 2795, 2796, 2797, 2798, 2798, 2799, 2800, 2801, 2801,
+ 2802, 2803, 2804, 2804, 2805, 2806, 2807, 2807, 2808, 2809, 2810, 2810, 2811, 2812, 2813, 2813,
+ 2814, 2815, 2815, 2816, 2817, 2818, 2818, 2819, 2820, 2821, 2821, 2822, 2823, 2824, 2824, 2825,
+ 2826, 2827, 2827, 2828, 2829, 2830, 2830, 2831, 2832, 2832, 2833, 2834, 2835, 2835, 2836, 2837,
+ 2838, 2838, 2839, 2840, 2841, 2841, 2842, 2843, 2844, 2844, 2845, 2846, 2846, 2847, 2848, 2849,
+ 2849, 2850, 2851, 2852, 2852, 2853, 2854, 2855, 2855, 2856, 2857, 2857, 2858, 2859, 2860, 2860,
+ 2861, 2862, 2863, 2863, 2864, 2865, 2865, 2866, 2867, 2868, 2868, 2869, 2870, 2871, 2871, 2872,
+ 2873, 2873, 2874, 2875, 2876, 2876, 2877, 2878, 2879, 2879, 2880, 2881, 2881, 2882, 2883, 2884,
+ 2884, 2885, 2886, 2886, 2887, 2888, 2889, 2889, 2890, 2891, 2892, 2892, 2893, 2894, 2894, 2895,
+ 2896, 2897, 2897, 2898, 2899, 2899, 2900, 2901, 2902, 2902, 2903, 2904, 2904, 2905, 2906, 2907,
+ 2907, 2908, 2909, 2909, 2910, 2911, 2912, 2912, 2913, 2914, 2914, 2915, 2916, 2917, 2917, 2918,
+ 2919, 2919, 2920, 2921, 2922, 2922, 2923, 2924, 2924, 2925, 2926, 2927, 2927, 2928, 2929, 2929,
+ 2930, 2931, 2932, 2932, 2933, 2934, 2934, 2935, 2936, 2937, 2937, 2938, 2939, 2939, 2940, 2941,
+ 2941, 2942, 2943, 2944, 2944, 2945, 2946, 2946, 2947, 2948, 2949, 2949, 2950, 2951, 2951, 2952,
+ 2953, 2953, 2954, 2955, 2956, 2956, 2957, 2958, 2958, 2959, 2960, 2961, 2961, 2962, 2963, 2963,
+ 2964, 2965, 2965, 2966, 2967, 2968, 2968, 2969, 2970, 2970, 2971, 2972, 2972, 2973, 2974, 2975,
+ 2975, 2976, 2977, 2977, 2978, 2979, 2979, 2980, 2981, 2982, 2982, 2983, 2984, 2984, 2985, 2986,
+ 2986, 2987, 2988, 2988, 2989, 2990, 2991, 2991, 2992, 2993, 2993, 2994, 2995, 2995, 2996, 2997,
+ 2998, 2998, 2999, 3000, 3000, 3001, 3002, 3002, 3003, 3004, 3004, 3005, 3006, 3006, 3007, 3008,
+ 3009, 3009, 3010, 3011, 3011, 3012, 3013, 3013, 3014, 3015, 3015, 3016, 3017, 3018, 3018, 3019,
+ 3020, 3020, 3021, 3022, 3022, 3023, 3024, 3024, 3025, 3026, 3026, 3027, 3028, 3029, 3029, 3030,
+ 3031, 3031, 3032, 3033, 3033, 3034, 3035, 3035, 3036, 3037, 3037, 3038, 3039, 3039, 3040, 3041,
+ 3042, 3042, 3043, 3044, 3044, 3045, 3046, 3046, 3047, 3048, 3048, 3049, 3050, 3050, 3051, 3052,
+ 3052, 3053, 3054, 3054, 3055, 3056, 3056, 3057, 3058, 3059, 3059, 3060, 3061, 3061, 3062, 3063,
+ 3063, 3064, 3065, 3065, 3066, 3067, 3067, 3068, 3069, 3069, 3070, 3071, 3071, 3072, 3073, 3073,
+ 3074, 3075, 3075, 3076, 3077, 3077, 3078, 3079, 3079, 3080, 3081, 3081, 3082, 3083, 3084, 3084,
+ 3085, 3086, 3086, 3087, 3088, 3088, 3089, 3090, 3090, 3091, 3092, 3092, 3093, 3094, 3094, 3095,
+ 3096, 3096, 3097, 3098, 3098, 3099, 3100, 3100, 3101, 3102, 3102, 3103, 3104, 3104, 3105, 3106,
+ 3106, 3107, 3108, 3108, 3109, 3110, 3110, 3111, 3112, 3112, 3113, 3114, 3114, 3115, 3116, 3116,
+ 3117, 3118, 3118, 3119, 3120, 3120, 3121, 3122, 3122, 3123, 3124, 3124, 3125, 3126, 3126, 3127,
+ 3128, 3128, 3129, 3130, 3130, 3131, 3132, 3132, 3133, 3134, 3134, 3135, 3135, 3136, 3137, 3137,
+ 3138, 3139, 3139, 3140, 3141, 3141, 3142, 3143, 3143, 3144, 3145, 3145, 3146, 3147, 3147, 3148,
+ 3149, 3149, 3150, 3151, 3151, 3152, 3153, 3153, 3154, 3155, 3155, 3156, 3157, 3157, 3158, 3159,
+ 3159, 3160, 3160, 3161, 3162, 3162, 3163, 3164, 3164, 3165, 3166, 3166, 3167, 3168, 3168, 3169,
+ 3170, 3170, 3171, 3172, 3172, 3173, 3174, 3174, 3175, 3175, 3176, 3177, 3177, 3178, 3179, 3179,
+ 3180, 3181, 3181, 3182, 3183, 3183, 3184, 3185, 3185, 3186, 3187, 3187, 3188, 3188, 3189, 3190,
+ 3190, 3191, 3192, 3192, 3193, 3194, 3194, 3195, 3196, 3196, 3197, 3198, 3198, 3199, 3199, 3200,
+ 3201, 3201, 3202, 3203, 3203, 3204, 3205, 3205, 3206, 3207, 3207, 3208, 3209, 3209, 3210, 3210,
+ 3211, 3212, 3212, 3213, 3214, 3214, 3215, 3216, 3216, 3217, 3218, 3218, 3219, 3219, 3220, 3221,
+ 3221, 3222, 3223, 3223, 3224, 3225, 3225, 3226, 3227, 3227, 3228, 3228, 3229, 3230, 3230, 3231,
+ 3232, 3232, 3233, 3234, 3234, 3235, 3235, 3236, 3237, 3237, 3238, 3239, 3239, 3240, 3241, 3241,
+ 3242, 3242, 3243, 3244, 3244, 3245, 3246, 3246, 3247, 3248, 3248, 3249, 3249, 3250, 3251, 3251,
+ 3252, 3253, 3253, 3254, 3255, 3255, 3256, 3256, 3257, 3258, 3258, 3259, 3260, 3260, 3261, 3262,
+ 3262, 3263, 3263, 3264, 3265, 3265, 3266, 3267, 3267, 3268, 3268, 3269, 3270, 3270, 3271, 3272,
+ 3272, 3273, 3274, 3274, 3275, 3275, 3276, 3277, 3277, 3278, 3279, 3279, 3280, 3280, 3281, 3282,
+ 3282, 3283, 3284, 3284, 3285, 3285, 3286, 3287, 3287, 3288, 3289, 3289, 3290, 3290, 3291, 3292,
+ 3292, 3293, 3294, 3294, 3295, 3295, 3296, 3297, 3297, 3298, 3299, 3299, 3300, 3300, 3301, 3302,
+ 3302, 3303, 3304, 3304, 3305, 3305, 3306, 3307, 3307, 3308, 3309, 3309, 3310, 3310, 3311, 3312,
+ 3312, 3313, 3314, 3314, 3315, 3315, 3316, 3317, 3317, 3318, 3319, 3319, 3320, 3320, 3321, 3322,
+ 3322, 3323, 3323, 3324, 3325, 3325, 3326, 3327, 3327, 3328, 3328, 3329, 3330, 3330, 3331, 3332,
+ 3332, 3333, 3333, 3334, 3335, 3335, 3336, 3336, 3337, 3338, 3338, 3339, 3340, 3340, 3341, 3341,
+ 3342, 3343, 3343, 3344, 3345, 3345, 3346, 3346, 3347, 3348, 3348, 3349, 3349, 3350, 3351, 3351,
+ 3352, 3352, 3353, 3354, 3354, 3355, 3356, 3356, 3357, 3357, 3358, 3359, 3359, 3360, 3360, 3361,
+ 3362, 3362, 3363, 3364, 3364, 3365, 3365, 3366, 3367, 3367, 3368, 3368, 3369, 3370, 3370, 3371,
+ 3371, 3372, 3373, 3373, 3374, 3375, 3375, 3376, 3376, 3377, 3378, 3378, 3379, 3379, 3380, 3381,
+ 3381, 3382, 3382, 3383, 3384, 3384, 3385, 3385, 3386, 3387, 3387, 3388, 3389, 3389, 3390, 3390,
+ 3391, 3392, 3392, 3393, 3393, 3394, 3395, 3395, 3396, 3396, 3397, 3398, 3398, 3399, 3399, 3400,
+ 3401, 3401, 3402, 3402, 3403, 3404, 3404, 3405, 3405, 3406, 3407, 3407, 3408, 3408, 3409, 3410,
+ 3410, 3411, 3411, 3412, 3413, 3413, 3414, 3414, 3415, 3416, 3416, 3417, 3418, 3418, 3419, 3419,
+ 3420, 3421, 3421, 3422, 3422, 3423, 3424, 3424, 3425, 3425, 3426, 3427, 3427, 3428, 3428, 3429,
+ 3430, 3430, 3431, 3431, 3432, 3433, 3433, 3434, 3434, 3435, 3435, 3436, 3437, 3437, 3438, 3438,
+ 3439, 3440, 3440, 3441, 3441, 3442, 3443, 3443, 3444, 3444, 3445, 3446, 3446, 3447, 3447, 3448,
+ 3449, 3449, 3450, 3450, 3451, 3452, 3452, 3453, 3453, 3454, 3455, 3455, 3456, 3456, 3457, 3458,
+ 3458, 3459, 3459, 3460, 3461, 3461, 3462, 3462, 3463, 3463, 3464, 3465, 3465, 3466, 3466, 3467,
+ 3468, 3468, 3469, 3469, 3470, 3471, 3471, 3472, 3472, 3473, 3474, 3474, 3475, 3475, 3476, 3476,
+ 3477, 3478, 3478, 3479, 3479, 3480, 3481, 3481, 3482, 3482, 3483, 3484, 3484, 3485, 3485, 3486,
+ 3486, 3487, 3488, 3488, 3489, 3489, 3490, 3491, 3491, 3492, 3492, 3493, 3494, 3494, 3495, 3495,
+ 3496, 3496, 3497, 3498, 3498, 3499, 3499, 3500, 3501, 3501, 3502, 3502, 3503, 3504, 3504, 3505,
+ 3505, 3506, 3506, 3507, 3508, 3508, 3509, 3509, 3510, 3511, 3511, 3512, 3512, 3513, 3513, 3514,
+ 3515, 3515, 3516, 3516, 3517, 3518, 3518, 3519, 3519, 3520, 3520, 3521, 3522, 3522, 3523, 3523,
+ 3524, 3525, 3525, 3526, 3526, 3527, 3527, 3528, 3529, 3529, 3530, 3530, 3531, 3531, 3532, 3533,
+ 3533, 3534, 3534, 3535, 3536, 3536, 3537, 3537, 3538, 3538, 3539, 3540, 3540, 3541, 3541, 3542,
+ 3542, 3543, 3544, 3544, 3545, 3545, 3546, 3547, 3547, 3548, 3548, 3549, 3549, 3550, 3551, 3551,
+ 3552, 3552, 3553, 3553, 3554, 3555, 3555, 3556, 3556, 3557, 3557, 3558, 3559, 3559, 3560, 3560,
+ 3561, 3561, 3562, 3563, 3563, 3564, 3564, 3565, 3566, 3566, 3567, 3567, 3568, 3568, 3569, 3570,
+ 3570, 3571, 3571, 3572, 3572, 3573, 3574, 3574, 3575, 3575, 3576, 3576, 3577, 3578, 3578, 3579,
+ 3579, 3580, 3580, 3581, 3582, 3582, 3583, 3583, 3584, 3584, 3585, 3586, 3586, 3587, 3587, 3588,
+ 3588, 3589, 3590, 3590, 3591, 3591, 3592, 3592, 3593, 3594, 3594, 3595, 3595, 3596, 3596, 3597,
+ 3597, 3598, 3599, 3599, 3600, 3600, 3601, 3601, 3602, 3603, 3603, 3604, 3604, 3605, 3605, 3606,
+ 3607, 3607, 3608, 3608, 3609, 3609, 3610, 3611, 3611, 3612, 3612, 3613, 3613, 3614, 3615, 3615,
+ 3616, 3616, 3617, 3617, 3618, 3618, 3619, 3620, 3620, 3621, 3621, 3622, 3622, 3623, 3624, 3624,
+ 3625, 3625, 3626, 3626, 3627, 3627, 3628, 3629, 3629, 3630, 3630, 3631, 3631, 3632, 3633, 3633,
+ 3634, 3634, 3635, 3635, 3636, 3636, 3637, 3638, 3638, 3639, 3639, 3640, 3640, 3641, 3642, 3642,
+ 3643, 3643, 3644, 3644, 3645, 3645, 3646, 3647, 3647, 3648, 3648, 3649, 3649, 3650, 3650, 3651,
+ 3652, 3652, 3653, 3653, 3654, 3654, 3655, 3656, 3656, 3657, 3657, 3658, 3658, 3659, 3659, 3660,
+ 3661, 3661, 3662, 3662, 3663, 3663, 3664, 3664, 3665, 3666, 3666, 3667, 3667, 3668, 3668, 3669,
+ 3669, 3670, 3671, 3671, 3672, 3672, 3673, 3673, 3674, 3674, 3675, 3676, 3676, 3677, 3677, 3678,
+ 3678, 3679, 3679, 3680, 3681, 3681, 3682, 3682, 3683, 3683, 3684, 3684, 3685, 3686, 3686, 3687,
+ 3687, 3688, 3688, 3689, 3689, 3690, 3691, 3691, 3692, 3692, 3693, 3693, 3694, 3694, 3695, 3695,
+ 3696, 3697, 3697, 3698, 3698, 3699, 3699, 3700, 3700, 3701, 3702, 3702, 3703, 3703, 3704, 3704,
+ 3705, 3705, 3706, 3707, 3707, 3708, 3708, 3709, 3709, 3710, 3710, 3711, 3711, 3712, 3713, 3713,
+ 3714, 3714, 3715, 3715, 3716, 3716, 3717, 3717, 3718, 3719, 3719, 3720, 3720, 3721, 3721, 3722,
+ 3722, 3723, 3724, 3724, 3725, 3725, 3726, 3726, 3727, 3727, 3728, 3728, 3729, 3730, 3730, 3731,
+ 3731, 3732, 3732, 3733, 3733, 3734, 3734, 3735, 3736, 3736, 3737, 3737, 3738, 3738, 3739, 3739,
+ 3740, 3740, 3741, 3742, 3742, 3743, 3743, 3744, 3744, 3745, 3745, 3746, 3746, 3747, 3748, 3748,
+ 3749, 3749, 3750, 3750, 3751, 3751, 3752, 3752, 3753, 3753, 3754, 3755, 3755, 3756, 3756, 3757,
+ 3757, 3758, 3758, 3759, 3759, 3760, 3761, 3761, 3762, 3762, 3763, 3763, 3764, 3764, 3765, 3765,
+ 3766, 3766, 3767, 3768, 3768, 3769, 3769, 3770, 3770, 3771, 3771, 3772, 3772, 3773, 3773, 3774,
+ 3775, 3775, 3776, 3776, 3777, 3777, 3778, 3778, 3779, 3779, 3780, 3781, 3781, 3782, 3782, 3783,
+ 3783, 3784, 3784, 3785, 3785, 3786, 3786, 3787, 3787, 3788, 3789, 3789, 3790, 3790, 3791, 3791,
+ 3792, 3792, 3793, 3793, 3794, 3794, 3795, 3796, 3796, 3797, 3797, 3798, 3798, 3799, 3799, 3800,
+ 3800, 3801, 3801, 3802, 3802, 3803, 3804, 3804, 3805, 3805, 3806, 3806, 3807, 3807, 3808, 3808,
+ 3809, 3809, 3810, 3811, 3811, 3812, 3812, 3813, 3813, 3814, 3814, 3815, 3815, 3816, 3816, 3817,
+ 3817, 3818, 3819, 3819, 3820, 3820, 3821, 3821, 3822, 3822, 3823, 3823, 3824, 3824, 3825, 3825,
+ 3826, 3826, 3827, 3828, 3828, 3829, 3829, 3830, 3830, 3831, 3831, 3832, 3832, 3833, 3833, 3834,
+ 3834, 3835, 3835, 3836, 3837, 3837, 3838, 3838, 3839, 3839, 3840, 3840, 3841, 3841, 3842, 3842,
+ 3843, 3843, 3844, 3844, 3845, 3846, 3846, 3847, 3847, 3848, 3848, 3849, 3849, 3850, 3850, 3851,
+ 3851, 3852, 3852, 3853, 3853, 3854, 3855, 3855, 3856, 3856, 3857, 3857, 3858, 3858, 3859, 3859,
+ 3860, 3860, 3861, 3861, 3862, 3862, 3863, 3863, 3864, 3864, 3865, 3866, 3866, 3867, 3867, 3868,
+ 3868, 3869, 3869, 3870, 3870, 3871, 3871, 3872, 3872, 3873, 3873, 3874, 3874, 3875, 3876, 3876,
+ 3877, 3877, 3878, 3878, 3879, 3879, 3880, 3880, 3881, 3881, 3882, 3882, 3883, 3883, 3884, 3884,
+ 3885, 3885, 3886, 3886, 3887, 3888, 3888, 3889, 3889, 3890, 3890, 3891, 3891, 3892, 3892, 3893,
+ 3893, 3894, 3894, 3895, 3895, 3896, 3896, 3897, 3897, 3898, 3898, 3899, 3900, 3900, 3901, 3901,
+ 3902, 3902, 3903, 3903, 3904, 3904, 3905, 3905, 3906, 3906, 3907, 3907, 3908, 3908, 3909, 3909,
+ 3910, 3910, 3911, 3911, 3912, 3912, 3913, 3914, 3914, 3915, 3915, 3916, 3916, 3917, 3917, 3918,
+ 3918, 3919, 3919, 3920, 3920, 3921, 3921, 3922, 3922, 3923, 3923, 3924, 3924, 3925, 3925, 3926,
+ 3926, 3927, 3927, 3928, 3929, 3929, 3930, 3930, 3931, 3931, 3932, 3932, 3933, 3933, 3934, 3934,
+ 3935, 3935, 3936, 3936, 3937, 3937, 3938, 3938, 3939, 3939, 3940, 3940, 3941, 3941, 3942, 3942,
+ 3943, 3943, 3944, 3944, 3945, 3945, 3946, 3947, 3947, 3948, 3948, 3949, 3949, 3950, 3950, 3951,
+ 3951, 3952, 3952, 3953, 3953, 3954, 3954, 3955, 3955, 3956, 3956, 3957, 3957, 3958, 3958, 3959,
+ 3959, 3960, 3960, 3961, 3961, 3962, 3962, 3963, 3963, 3964, 3964, 3965, 3965, 3966, 3966, 3967,
+ 3967, 3968, 3969, 3969, 3970, 3970, 3971, 3971, 3972, 3972, 3973, 3973, 3974, 3974, 3975, 3975,
+ 3976, 3976, 3977, 3977, 3978, 3978, 3979, 3979, 3980, 3980, 3981, 3981, 3982, 3982, 3983, 3983,
+ 3984, 3984, 3985, 3985, 3986, 3986, 3987, 3987, 3988, 3988, 3989, 3989, 3990, 3990, 3991, 3991,
+ 3992, 3992, 3993, 3993, 3994, 3994, 3995, 3995, 3996, 3996, 3997, 3997, 3998, 3998, 3999, 3999,
+ 4000, 4001, 4001, 4002, 4002, 4003, 4003, 4004, 4004, 4005, 4005, 4006, 4006, 4007, 4007, 4008,
+ 4008, 4009, 4009, 4010, 4010, 4011, 4011, 4012, 4012, 4013, 4013, 4014, 4014, 4015, 4015, 4016,
+ 4016, 4017, 4017, 4018, 4018, 4019, 4019, 4020, 4020, 4021, 4021, 4022, 4022, 4023, 4023, 4024,
+ 4024, 4025, 4025, 4026, 4026, 4027, 4027, 4028, 4028, 4029, 4029, 4030, 4030, 4031, 4031, 4032,
+ 4032, 4033, 4033, 4034, 4034, 4035, 4035, 4036, 4036, 4037, 4037, 4038, 4038, 4039, 4039, 4040,
+ 4040, 4041, 4041, 4042, 4042, 4043, 4043, 4044, 4044, 4045, 4045, 4046, 4046, 4047, 4047, 4048,
+ 4048, 4049, 4049, 4050, 4050, 4051, 4051, 4052, 4052, 4053, 4053, 4054, 4054, 4055, 4055, 4056,
+ 4056, 4057, 4057, 4058, 4058, 4059, 4059, 4060, 4060, 4061, 4061, 4062, 4062, 4063, 4063, 4064,
+ 4064, 4065, 4065, 4066, 4066, 4067, 4067, 4068, 4068, 4069, 4069, 4070, 4070, 4071, 4071, 4072,
+ 4072, 4073, 4073, 4074, 4074, 4075, 4075, 4076, 4076, 4077, 4077, 4078, 4078, 4079, 4079, 4080,
+ 4080,
+};
+
+/* Generated table */
+const struct color16 tpg_csc_colors[V4L2_COLORSPACE_BT2020 + 1][TPG_COLOR_CSC_BLACK + 1] = {
+ [V4L2_COLORSPACE_SMPTE170M][0] = { 2939, 2939, 2939 },
+ [V4L2_COLORSPACE_SMPTE170M][1] = { 2953, 2963, 586 },
+ [V4L2_COLORSPACE_SMPTE170M][2] = { 0, 2967, 2937 },
+ [V4L2_COLORSPACE_SMPTE170M][3] = { 88, 2990, 575 },
+ [V4L2_COLORSPACE_SMPTE170M][4] = { 3016, 259, 2933 },
+ [V4L2_COLORSPACE_SMPTE170M][5] = { 3030, 405, 558 },
+ [V4L2_COLORSPACE_SMPTE170M][6] = { 478, 428, 2931 },
+ [V4L2_COLORSPACE_SMPTE170M][7] = { 547, 547, 547 },
[V4L2_COLORSPACE_SMPTE240M][0] = { 2926, 2926, 2926 },
- [V4L2_COLORSPACE_SMPTE240M][1] = { 2926, 2926, 857 },
- [V4L2_COLORSPACE_SMPTE240M][2] = { 1594, 2901, 2901 },
- [V4L2_COLORSPACE_SMPTE240M][3] = { 1594, 2901, 774 },
- [V4L2_COLORSPACE_SMPTE240M][4] = { 2484, 618, 2858 },
- [V4L2_COLORSPACE_SMPTE240M][5] = { 2484, 618, 617 },
- [V4L2_COLORSPACE_SMPTE240M][6] = { 507, 507, 2832 },
+ [V4L2_COLORSPACE_SMPTE240M][1] = { 2941, 2950, 546 },
+ [V4L2_COLORSPACE_SMPTE240M][2] = { 0, 2954, 2924 },
+ [V4L2_COLORSPACE_SMPTE240M][3] = { 78, 2978, 536 },
+ [V4L2_COLORSPACE_SMPTE240M][4] = { 3004, 230, 2920 },
+ [V4L2_COLORSPACE_SMPTE240M][5] = { 3018, 363, 518 },
+ [V4L2_COLORSPACE_SMPTE240M][6] = { 437, 387, 2918 },
[V4L2_COLORSPACE_SMPTE240M][7] = { 507, 507, 507 },
[V4L2_COLORSPACE_REC709][0] = { 2939, 2939, 2939 },
[V4L2_COLORSPACE_REC709][1] = { 2939, 2939, 547 },
@@ -103,21 +623,21 @@
[V4L2_COLORSPACE_REC709][5] = { 2939, 547, 547 },
[V4L2_COLORSPACE_REC709][6] = { 547, 547, 2939 },
[V4L2_COLORSPACE_REC709][7] = { 547, 547, 547 },
- [V4L2_COLORSPACE_470_SYSTEM_M][0] = { 2894, 2988, 2808 },
- [V4L2_COLORSPACE_470_SYSTEM_M][1] = { 2847, 3070, 843 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][0] = { 2892, 2988, 2807 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][1] = { 2846, 3070, 843 },
[V4L2_COLORSPACE_470_SYSTEM_M][2] = { 1656, 2962, 2783 },
[V4L2_COLORSPACE_470_SYSTEM_M][3] = { 1572, 3045, 763 },
- [V4L2_COLORSPACE_470_SYSTEM_M][4] = { 2477, 229, 2743 },
- [V4L2_COLORSPACE_470_SYSTEM_M][5] = { 2422, 672, 614 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][4] = { 2476, 229, 2742 },
+ [V4L2_COLORSPACE_470_SYSTEM_M][5] = { 2420, 672, 614 },
[V4L2_COLORSPACE_470_SYSTEM_M][6] = { 725, 63, 2718 },
[V4L2_COLORSPACE_470_SYSTEM_M][7] = { 534, 561, 509 },
[V4L2_COLORSPACE_470_SYSTEM_BG][0] = { 2939, 2939, 2939 },
- [V4L2_COLORSPACE_470_SYSTEM_BG][1] = { 2939, 2939, 621 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][1] = { 2939, 2939, 464 },
[V4L2_COLORSPACE_470_SYSTEM_BG][2] = { 786, 2939, 2939 },
- [V4L2_COLORSPACE_470_SYSTEM_BG][3] = { 786, 2939, 621 },
- [V4L2_COLORSPACE_470_SYSTEM_BG][4] = { 2879, 547, 2923 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][3] = { 786, 2939, 464 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][4] = { 2879, 547, 2956 },
[V4L2_COLORSPACE_470_SYSTEM_BG][5] = { 2879, 547, 547 },
- [V4L2_COLORSPACE_470_SYSTEM_BG][6] = { 547, 547, 2923 },
+ [V4L2_COLORSPACE_470_SYSTEM_BG][6] = { 547, 547, 2956 },
[V4L2_COLORSPACE_470_SYSTEM_BG][7] = { 547, 547, 547 },
[V4L2_COLORSPACE_SRGB][0] = { 3056, 3056, 3056 },
[V4L2_COLORSPACE_SRGB][1] = { 3056, 3056, 800 },
@@ -127,6 +647,22 @@
[V4L2_COLORSPACE_SRGB][5] = { 3056, 800, 800 },
[V4L2_COLORSPACE_SRGB][6] = { 800, 800, 3056 },
[V4L2_COLORSPACE_SRGB][7] = { 800, 800, 800 },
+ [V4L2_COLORSPACE_ADOBERGB][0] = { 3033, 3033, 3033 },
+ [V4L2_COLORSPACE_ADOBERGB][1] = { 3033, 3033, 1063 },
+ [V4L2_COLORSPACE_ADOBERGB][2] = { 1828, 3033, 3033 },
+ [V4L2_COLORSPACE_ADOBERGB][3] = { 1828, 3033, 1063 },
+ [V4L2_COLORSPACE_ADOBERGB][4] = { 2633, 851, 2979 },
+ [V4L2_COLORSPACE_ADOBERGB][5] = { 2633, 851, 851 },
+ [V4L2_COLORSPACE_ADOBERGB][6] = { 851, 851, 2979 },
+ [V4L2_COLORSPACE_ADOBERGB][7] = { 851, 851, 851 },
+ [V4L2_COLORSPACE_BT2020][0] = { 2939, 2939, 2939 },
+ [V4L2_COLORSPACE_BT2020][1] = { 2877, 2923, 1058 },
+ [V4L2_COLORSPACE_BT2020][2] = { 1837, 2840, 2916 },
+ [V4L2_COLORSPACE_BT2020][3] = { 1734, 2823, 993 },
+ [V4L2_COLORSPACE_BT2020][4] = { 2427, 961, 2812 },
+ [V4L2_COLORSPACE_BT2020][5] = { 2351, 912, 648 },
+ [V4L2_COLORSPACE_BT2020][6] = { 792, 618, 2788 },
+ [V4L2_COLORSPACE_BT2020][7] = { 547, 547, 547 },
};
#else
@@ -138,29 +674,40 @@
#include <stdlib.h>
static const double rec709_to_ntsc1953[3][3] = {
- { 0.6698, 0.2678, 0.0323 },
- { 0.0185, 1.0742, -0.0603 },
- { 0.0162, 0.0432, 0.8551 }
+ { 0.6689794, 0.2678309, 0.0323187 },
+ { 0.0184901, 1.0742442, -0.0602820 },
+ { 0.0162259, 0.0431716, 0.8549253 }
};
static const double rec709_to_ebu[3][3] = {
- { 0.9578, 0.0422, 0 },
- { 0 , 1 , 0 },
- { 0 , 0.0118, 0.9882 }
+ { 0.9578221, 0.0421779, -0.0000000 },
+ { -0.0000000, 1.0000000, 0.0000000 },
+ { -0.0000000, -0.0119367, 1.0119367 }
};
static const double rec709_to_170m[3][3] = {
- { 1.0654, -0.0554, -0.0010 },
- { -0.0196, 1.0364, -0.0167 },
- { 0.0016, 0.0044, 0.9940 }
+ { 1.0653640, -0.0553900, -0.0099740 },
+ { -0.0196361, 1.0363630, -0.0167269 },
+ { 0.0016327, 0.0044133, 0.9939540 },
};
static const double rec709_to_240m[3][3] = {
- { 0.7151, 0.2849, 0 },
- { 0.0179, 0.9821, 0 },
- { 0.0177, 0.0472, 0.9350 }
+ { 1.0653640, -0.0553900, -0.0099740 },
+ { -0.0196361, 1.0363630, -0.0167269 },
+ { 0.0016327, 0.0044133, 0.9939540 },
};
+static const double rec709_to_adobergb[3][3] = {
+ { 0.7151627, 0.2848373, -0.0000000 },
+ { 0.0000000, 1.0000000, 0.0000000 },
+ { -0.0000000, 0.0411705, 0.9588295 },
+};
+
+static const double rec709_to_bt2020[3][3] = {
+ { 0.6274524, 0.3292485, 0.0432991 },
+ { 0.0691092, 0.9195311, 0.0113597 },
+ { 0.0163976, 0.0880301, 0.8955723 },
+};
static void mult_matrix(double *r, double *g, double *b, const double m[3][3])
{
@@ -176,7 +723,18 @@
static double transfer_srgb_to_rgb(double v)
{
- return (v <= 0.03928) ? v / 12.92 : pow((v + 0.055) / 1.055, 2.4);
+ if (v < -0.04045)
+ return pow((-v + 0.055) / 1.055, 2.4);
+ return (v <= 0.04045) ? v / 12.92 : pow((v + 0.055) / 1.055, 2.4);
+}
+
+static double transfer_rgb_to_srgb(double v)
+{
+ if (v <= -0.0031308)
+ return -1.055 * pow(-v, 1.0 / 2.4) + 0.055;
+ if (v <= 0.0031308)
+ return v * 12.92;
+ return 1.055 * pow(v, 1.0 / 2.4) - 0.055;
}
static double transfer_rgb_to_smpte240m(double v)
@@ -186,9 +744,21 @@
static double transfer_rgb_to_rec709(double v)
{
+ if (v <= -0.018)
+ return -1.099 * pow(-v, 0.45) + 0.099;
return (v < 0.018) ? v * 4.5 : 1.099 * pow(v, 0.45) - 0.099;
}
+static double transfer_rec709_to_rgb(double v)
+{
+ return (v < 0.081) ? v / 4.5 : pow((v + 0.099) / 1.099, 1.0 / 0.45);
+}
+
+static double transfer_rgb_to_adobergb(double v)
+{
+ return pow(v, 1.0 / 2.19921875);
+}
+
static double transfer_srgb_to_rec709(double v)
{
return transfer_rgb_to_rec709(transfer_srgb_to_rgb(v));
@@ -196,6 +766,8 @@
static void csc(enum v4l2_colorspace colorspace, double *r, double *g, double *b)
{
+ int clamp = 1;
+
/* Convert the primaries of Rec. 709 Linear RGB */
switch (colorspace) {
case V4L2_COLORSPACE_SMPTE240M:
@@ -222,15 +794,29 @@
*b = transfer_srgb_to_rgb(*b);
mult_matrix(r, g, b, rec709_to_ntsc1953);
break;
+ case V4L2_COLORSPACE_ADOBERGB:
+ *r = transfer_srgb_to_rgb(*r);
+ *g = transfer_srgb_to_rgb(*g);
+ *b = transfer_srgb_to_rgb(*b);
+ mult_matrix(r, g, b, rec709_to_adobergb);
+ break;
+ case V4L2_COLORSPACE_BT2020:
+ *r = transfer_srgb_to_rgb(*r);
+ *g = transfer_srgb_to_rgb(*g);
+ *b = transfer_srgb_to_rgb(*b);
+ mult_matrix(r, g, b, rec709_to_bt2020);
+ break;
case V4L2_COLORSPACE_SRGB:
case V4L2_COLORSPACE_REC709:
default:
break;
}
- *r = ((*r) < 0) ? 0 : (((*r) > 1) ? 1 : (*r));
- *g = ((*g) < 0) ? 0 : (((*g) > 1) ? 1 : (*g));
- *b = ((*b) < 0) ? 0 : (((*b) > 1) ? 1 : (*b));
+ if (clamp) {
+ *r = ((*r) < 0) ? 0 : (((*r) > 1) ? 1 : (*r));
+ *g = ((*g) < 0) ? 0 : (((*g) > 1) ? 1 : (*g));
+ *b = ((*b) < 0) ? 0 : (((*b) > 1) ? 1 : (*b));
+ }
/* Encode to gamma corrected colorspace */
switch (colorspace) {
@@ -242,12 +828,18 @@
case V4L2_COLORSPACE_SMPTE170M:
case V4L2_COLORSPACE_470_SYSTEM_M:
case V4L2_COLORSPACE_470_SYSTEM_BG:
+ case V4L2_COLORSPACE_BT2020:
*r = transfer_rgb_to_rec709(*r);
*g = transfer_rgb_to_rec709(*g);
*b = transfer_rgb_to_rec709(*b);
break;
case V4L2_COLORSPACE_SRGB:
break;
+ case V4L2_COLORSPACE_ADOBERGB:
+ *r = transfer_rgb_to_adobergb(*r);
+ *g = transfer_rgb_to_adobergb(*g);
+ *b = transfer_rgb_to_adobergb(*b);
+ break;
case V4L2_COLORSPACE_REC709:
default:
*r = transfer_srgb_to_rec709(*r);
@@ -269,6 +861,8 @@
V4L2_COLORSPACE_470_SYSTEM_BG,
0,
V4L2_COLORSPACE_SRGB,
+ V4L2_COLORSPACE_ADOBERGB,
+ V4L2_COLORSPACE_BT2020,
};
static const char * const colorspace_names[] = {
"",
@@ -280,13 +874,39 @@
"V4L2_COLORSPACE_470_SYSTEM_BG",
"",
"V4L2_COLORSPACE_SRGB",
+ "V4L2_COLORSPACE_ADOBERGB",
+ "V4L2_COLORSPACE_BT2020",
};
int i;
int c;
printf("/* Generated table */\n");
- printf("const struct color16 tpg_csc_colors[V4L2_COLORSPACE_SRGB + 1][TPG_COLOR_CSC_BLACK + 1] = {\n");
- for (c = 0; c <= V4L2_COLORSPACE_SRGB; c++) {
+ printf("const unsigned short tpg_rec709_to_linear[255 * 16 + 1] = {");
+ for (i = 0; i <= 255 * 16; i++) {
+ if (i % 16 == 0)
+ printf("\n\t");
+ printf("%4d,%s",
+ (int)(0.5 + 16.0 * 255.0 *
+ transfer_rec709_to_rgb(i / (16.0 * 255.0))),
+ i % 16 == 15 || i == 255 * 16 ? "" : " ");
+ }
+ printf("\n};\n\n");
+
+ printf("/* Generated table */\n");
+ printf("const unsigned short tpg_linear_to_rec709[255 * 16 + 1] = {");
+ for (i = 0; i <= 255 * 16; i++) {
+ if (i % 16 == 0)
+ printf("\n\t");
+ printf("%4d,%s",
+ (int)(0.5 + 16.0 * 255.0 *
+ transfer_rgb_to_rec709(i / (16.0 * 255.0))),
+ i % 16 == 15 || i == 255 * 16 ? "" : " ");
+ }
+ printf("\n};\n\n");
+
+ printf("/* Generated table */\n");
+ printf("const struct color16 tpg_csc_colors[V4L2_COLORSPACE_BT2020 + 1][TPG_COLOR_CSC_BLACK + 1] = {\n");
+ for (c = 0; c <= V4L2_COLORSPACE_BT2020; c++) {
for (i = 0; i <= TPG_COLOR_CSC_BLACK; i++) {
double r, g, b;
diff --git a/drivers/media/platform/vivid/vivid-tpg-colors.h b/drivers/media/platform/vivid/vivid-tpg-colors.h
index a2678fb..2c33335 100644
--- a/drivers/media/platform/vivid/vivid-tpg-colors.h
+++ b/drivers/media/platform/vivid/vivid-tpg-colors.h
@@ -59,6 +59,8 @@
};
extern const struct color tpg_colors[TPG_COLOR_MAX];
-extern const struct color16 tpg_csc_colors[V4L2_COLORSPACE_SRGB + 1][TPG_COLOR_CSC_BLACK + 1];
+extern const unsigned short tpg_rec709_to_linear[255 * 16 + 1];
+extern const unsigned short tpg_linear_to_rec709[255 * 16 + 1];
+extern const struct color16 tpg_csc_colors[V4L2_COLORSPACE_BT2020 + 1][TPG_COLOR_CSC_BLACK + 1];
#endif
diff --git a/drivers/media/platform/vivid/vivid-tpg.c b/drivers/media/platform/vivid/vivid-tpg.c
index 0c6fa53..fc9c653 100644
--- a/drivers/media/platform/vivid/vivid-tpg.c
+++ b/drivers/media/platform/vivid/vivid-tpg.c
@@ -136,7 +136,7 @@
tpg->black_line[plane] = vzalloc(max_w * pixelsz);
if (!tpg->black_line[plane])
return -ENOMEM;
- tpg->random_line[plane] = vzalloc(max_w * pixelsz);
+ tpg->random_line[plane] = vzalloc(max_w * 2 * pixelsz);
if (!tpg->random_line[plane])
return -ENOMEM;
}
@@ -296,127 +296,193 @@
}
}
-static u16 color_to_y(struct tpg_data *tpg, int r, int g, int b)
+static inline int rec709_to_linear(int v)
{
- switch (tpg->colorspace) {
- case V4L2_COLORSPACE_SMPTE170M:
- case V4L2_COLORSPACE_470_SYSTEM_M:
- case V4L2_COLORSPACE_470_SYSTEM_BG:
- return ((16829 * r + 33039 * g + 6416 * b + 16 * 32768) >> 16) + (16 << 4);
- case V4L2_COLORSPACE_SMPTE240M:
- return ((11932 * r + 39455 * g + 4897 * b + 16 * 32768) >> 16) + (16 << 4);
- case V4L2_COLORSPACE_REC709:
- case V4L2_COLORSPACE_SRGB:
+ v = clamp(v, 0, 0xff0);
+ return tpg_rec709_to_linear[v];
+}
+
+static inline int linear_to_rec709(int v)
+{
+ v = clamp(v, 0, 0xff0);
+ return tpg_linear_to_rec709[v];
+}
+
+static void rgb2ycbcr(const int m[3][3], int r, int g, int b,
+ int y_offset, int *y, int *cb, int *cr)
+{
+ *y = ((m[0][0] * r + m[0][1] * g + m[0][2] * b) >> 16) + (y_offset << 4);
+ *cb = ((m[1][0] * r + m[1][1] * g + m[1][2] * b) >> 16) + (128 << 4);
+ *cr = ((m[2][0] * r + m[2][1] * g + m[2][2] * b) >> 16) + (128 << 4);
+}
+
+static void color_to_ycbcr(struct tpg_data *tpg, int r, int g, int b,
+ int *y, int *cb, int *cr)
+{
+#define COEFF(v, r) ((int)(0.5 + (v) * (r) * 256.0))
+
+ static const int bt601[3][3] = {
+ { COEFF(0.299, 219), COEFF(0.587, 219), COEFF(0.114, 219) },
+ { COEFF(-0.169, 224), COEFF(-0.331, 224), COEFF(0.5, 224) },
+ { COEFF(0.5, 224), COEFF(-0.419, 224), COEFF(-0.081, 224) },
+ };
+ static const int bt601_full[3][3] = {
+ { COEFF(0.299, 255), COEFF(0.587, 255), COEFF(0.114, 255) },
+ { COEFF(-0.169, 255), COEFF(-0.331, 255), COEFF(0.5, 255) },
+ { COEFF(0.5, 255), COEFF(-0.419, 255), COEFF(-0.081, 255) },
+ };
+ static const int rec709[3][3] = {
+ { COEFF(0.2126, 219), COEFF(0.7152, 219), COEFF(0.0722, 219) },
+ { COEFF(-0.1146, 224), COEFF(-0.3854, 224), COEFF(0.5, 224) },
+ { COEFF(0.5, 224), COEFF(-0.4542, 224), COEFF(-0.0458, 224) },
+ };
+ static const int rec709_full[3][3] = {
+ { COEFF(0.2126, 255), COEFF(0.7152, 255), COEFF(0.0722, 255) },
+ { COEFF(-0.1146, 255), COEFF(-0.3854, 255), COEFF(0.5, 255) },
+ { COEFF(0.5, 255), COEFF(-0.4542, 255), COEFF(-0.0458, 255) },
+ };
+ static const int smpte240m[3][3] = {
+ { COEFF(0.212, 219), COEFF(0.701, 219), COEFF(0.087, 219) },
+ { COEFF(-0.116, 224), COEFF(-0.384, 224), COEFF(0.5, 224) },
+ { COEFF(0.5, 224), COEFF(-0.445, 224), COEFF(-0.055, 224) },
+ };
+ static const int bt2020[3][3] = {
+ { COEFF(0.2726, 219), COEFF(0.6780, 219), COEFF(0.0593, 219) },
+ { COEFF(-0.1396, 224), COEFF(-0.3604, 224), COEFF(0.5, 224) },
+ { COEFF(0.5, 224), COEFF(-0.4629, 224), COEFF(-0.0405, 224) },
+ };
+ bool full = tpg->real_quantization == V4L2_QUANTIZATION_FULL_RANGE;
+ int lin_y, yc;
+
+ switch (tpg->real_ycbcr_enc) {
+ case V4L2_YCBCR_ENC_601:
+ case V4L2_YCBCR_ENC_XV601:
+ case V4L2_YCBCR_ENC_SYCC:
+ rgb2ycbcr(full ? bt601_full : bt601, r, g, b, 16, y, cb, cr);
+ break;
+ case V4L2_YCBCR_ENC_BT2020:
+ rgb2ycbcr(bt2020, r, g, b, 16, y, cb, cr);
+ break;
+ case V4L2_YCBCR_ENC_BT2020_CONST_LUM:
+ lin_y = (COEFF(0.2627, 255) * rec709_to_linear(r) +
+ COEFF(0.6780, 255) * rec709_to_linear(g) +
+ COEFF(0.0593, 255) * rec709_to_linear(b)) >> 16;
+ yc = linear_to_rec709(lin_y);
+ *y = (yc * 219) / 255 + (16 << 4);
+ if (b <= yc)
+ *cb = (((b - yc) * COEFF(1.0 / 1.9404, 224)) >> 16) + (128 << 4);
+ else
+ *cb = (((b - yc) * COEFF(1.0 / 1.5816, 224)) >> 16) + (128 << 4);
+ if (r <= yc)
+ *cr = (((r - yc) * COEFF(1.0 / 1.7184, 224)) >> 16) + (128 << 4);
+ else
+ *cr = (((r - yc) * COEFF(1.0 / 0.9936, 224)) >> 16) + (128 << 4);
+ break;
+ case V4L2_YCBCR_ENC_SMPTE240M:
+ rgb2ycbcr(smpte240m, r, g, b, 16, y, cb, cr);
+ break;
+ case V4L2_YCBCR_ENC_709:
+ case V4L2_YCBCR_ENC_XV709:
default:
- return ((11966 * r + 40254 * g + 4064 * b + 16 * 32768) >> 16) + (16 << 4);
+ rgb2ycbcr(full ? rec709_full : rec709, r, g, b, 0, y, cb, cr);
+ break;
}
}
-static u16 color_to_cb(struct tpg_data *tpg, int r, int g, int b)
+static void ycbcr2rgb(const int m[3][3], int y, int cb, int cr,
+ int y_offset, int *r, int *g, int *b)
{
- switch (tpg->colorspace) {
- case V4L2_COLORSPACE_SMPTE170M:
- case V4L2_COLORSPACE_470_SYSTEM_M:
- case V4L2_COLORSPACE_470_SYSTEM_BG:
- return ((-9714 * r - 19070 * g + 28784 * b + 16 * 32768) >> 16) + (128 << 4);
- case V4L2_COLORSPACE_SMPTE240M:
- return ((-6684 * r - 22100 * g + 28784 * b + 16 * 32768) >> 16) + (128 << 4);
- case V4L2_COLORSPACE_REC709:
- case V4L2_COLORSPACE_SRGB:
- default:
- return ((-6596 * r - 22189 * g + 28784 * b + 16 * 32768) >> 16) + (128 << 4);
- }
-}
-
-static u16 color_to_cr(struct tpg_data *tpg, int r, int g, int b)
-{
- switch (tpg->colorspace) {
- case V4L2_COLORSPACE_SMPTE170M:
- case V4L2_COLORSPACE_470_SYSTEM_M:
- case V4L2_COLORSPACE_470_SYSTEM_BG:
- return ((28784 * r - 24103 * g - 4681 * b + 16 * 32768) >> 16) + (128 << 4);
- case V4L2_COLORSPACE_SMPTE240M:
- return ((28784 * r - 25606 * g - 3178 * b + 16 * 32768) >> 16) + (128 << 4);
- case V4L2_COLORSPACE_REC709:
- case V4L2_COLORSPACE_SRGB:
- default:
- return ((28784 * r - 26145 * g - 2639 * b + 16 * 32768) >> 16) + (128 << 4);
- }
-}
-
-static u16 ycbcr_to_r(struct tpg_data *tpg, int y, int cb, int cr)
-{
- int r;
-
- y -= 16 << 4;
+ y -= y_offset << 4;
cb -= 128 << 4;
cr -= 128 << 4;
- switch (tpg->colorspace) {
- case V4L2_COLORSPACE_SMPTE170M:
- case V4L2_COLORSPACE_470_SYSTEM_M:
- case V4L2_COLORSPACE_470_SYSTEM_BG:
- r = 4769 * y + 6537 * cr;
- break;
- case V4L2_COLORSPACE_SMPTE240M:
- r = 4769 * y + 7376 * cr;
- break;
- case V4L2_COLORSPACE_REC709:
- case V4L2_COLORSPACE_SRGB:
- default:
- r = 4769 * y + 7343 * cr;
- break;
- }
- return clamp(r >> 12, 0, 0xff0);
+ *r = m[0][0] * y + m[0][1] * cb + m[0][2] * cr;
+ *g = m[1][0] * y + m[1][1] * cb + m[1][2] * cr;
+ *b = m[2][0] * y + m[2][1] * cb + m[2][2] * cr;
+ *r = clamp(*r >> 12, 0, 0xff0);
+ *g = clamp(*g >> 12, 0, 0xff0);
+ *b = clamp(*b >> 12, 0, 0xff0);
}
-static u16 ycbcr_to_g(struct tpg_data *tpg, int y, int cb, int cr)
+static void ycbcr_to_color(struct tpg_data *tpg, int y, int cb, int cr,
+ int *r, int *g, int *b)
{
- int g;
+#undef COEFF
+#define COEFF(v, r) ((int)(0.5 + (v) * ((255.0 * 255.0 * 16.0) / (r))))
+ static const int bt601[3][3] = {
+ { COEFF(1, 219), COEFF(0, 224), COEFF(1.4020, 224) },
+ { COEFF(1, 219), COEFF(-0.3441, 224), COEFF(-0.7141, 224) },
+ { COEFF(1, 219), COEFF(1.7720, 224), COEFF(0, 224) },
+ };
+ static const int bt601_full[3][3] = {
+ { COEFF(1, 255), COEFF(0, 255), COEFF(1.4020, 255) },
+ { COEFF(1, 255), COEFF(-0.3441, 255), COEFF(-0.7141, 255) },
+ { COEFF(1, 255), COEFF(1.7720, 255), COEFF(0, 255) },
+ };
+ static const int rec709[3][3] = {
+ { COEFF(1, 219), COEFF(0, 224), COEFF(1.5748, 224) },
+ { COEFF(1, 219), COEFF(-0.1873, 224), COEFF(-0.4681, 224) },
+ { COEFF(1, 219), COEFF(1.8556, 224), COEFF(0, 224) },
+ };
+ static const int rec709_full[3][3] = {
+ { COEFF(1, 255), COEFF(0, 255), COEFF(1.5748, 255) },
+ { COEFF(1, 255), COEFF(-0.1873, 255), COEFF(-0.4681, 255) },
+ { COEFF(1, 255), COEFF(1.8556, 255), COEFF(0, 255) },
+ };
+ static const int smpte240m[3][3] = {
+ { COEFF(1, 219), COEFF(0, 224), COEFF(1.5756, 224) },
+ { COEFF(1, 219), COEFF(-0.2253, 224), COEFF(-0.4767, 224) },
+ { COEFF(1, 219), COEFF(1.8270, 224), COEFF(0, 224) },
+ };
+ static const int bt2020[3][3] = {
+ { COEFF(1, 219), COEFF(0, 224), COEFF(1.4746, 224) },
+ { COEFF(1, 219), COEFF(-0.1646, 224), COEFF(-0.5714, 224) },
+ { COEFF(1, 219), COEFF(1.8814, 224), COEFF(0, 224) },
+ };
+ bool full = tpg->real_quantization == V4L2_QUANTIZATION_FULL_RANGE;
+ int lin_r, lin_g, lin_b, lin_y;
- y -= 16 << 4;
- cb -= 128 << 4;
- cr -= 128 << 4;
- switch (tpg->colorspace) {
- case V4L2_COLORSPACE_SMPTE170M:
- case V4L2_COLORSPACE_470_SYSTEM_M:
- case V4L2_COLORSPACE_470_SYSTEM_BG:
- g = 4769 * y - 1605 * cb - 3330 * cr;
+ switch (tpg->real_ycbcr_enc) {
+ case V4L2_YCBCR_ENC_601:
+ case V4L2_YCBCR_ENC_XV601:
+ case V4L2_YCBCR_ENC_SYCC:
+ ycbcr2rgb(full ? bt601_full : bt601, y, cb, cr, 16, r, g, b);
break;
- case V4L2_COLORSPACE_SMPTE240M:
- g = 4769 * y - 1055 * cb - 2341 * cr;
+ case V4L2_YCBCR_ENC_BT2020:
+ ycbcr2rgb(bt2020, y, cb, cr, 16, r, g, b);
break;
- case V4L2_COLORSPACE_REC709:
- case V4L2_COLORSPACE_SRGB:
+ case V4L2_YCBCR_ENC_BT2020_CONST_LUM:
+ y -= 16 << 4;
+ cb -= 128 << 4;
+ cr -= 128 << 4;
+
+ if (cb <= 0)
+ *b = COEFF(1.0, 219) * y + COEFF(1.9404, 224) * cb;
+ else
+ *b = COEFF(1.0, 219) * y + COEFF(1.5816, 224) * cb;
+ *b = *b >> 12;
+ if (cr <= 0)
+ *r = COEFF(1.0, 219) * y + COEFF(1.7184, 224) * cr;
+ else
+ *r = COEFF(1.0, 219) * y + COEFF(0.9936, 224) * cr;
+ *r = *r >> 12;
+ lin_r = rec709_to_linear(*r);
+ lin_b = rec709_to_linear(*b);
+ lin_y = rec709_to_linear((y * 255) / 219);
+
+ lin_g = COEFF(1.0 / 0.6780, 255) * lin_y -
+ COEFF(0.2627 / 0.6780, 255) * lin_r -
+ COEFF(0.0593 / 0.6780, 255) * lin_b;
+ *g = linear_to_rec709(lin_g >> 12);
+ break;
+ case V4L2_YCBCR_ENC_SMPTE240M:
+ ycbcr2rgb(smpte240m, y, cb, cr, 16, r, g, b);
+ break;
+ case V4L2_YCBCR_ENC_709:
+ case V4L2_YCBCR_ENC_XV709:
default:
- g = 4769 * y - 873 * cb - 2183 * cr;
+ ycbcr2rgb(full ? rec709_full : rec709, y, cb, cr, 16, r, g, b);
break;
}
- return clamp(g >> 12, 0, 0xff0);
-}
-
-static u16 ycbcr_to_b(struct tpg_data *tpg, int y, int cb, int cr)
-{
- int b;
-
- y -= 16 << 4;
- cb -= 128 << 4;
- cr -= 128 << 4;
- switch (tpg->colorspace) {
- case V4L2_COLORSPACE_SMPTE170M:
- case V4L2_COLORSPACE_470_SYSTEM_M:
- case V4L2_COLORSPACE_470_SYSTEM_BG:
- b = 4769 * y + 7343 * cb;
- break;
- case V4L2_COLORSPACE_SMPTE240M:
- b = 4769 * y + 8552 * cb;
- break;
- case V4L2_COLORSPACE_REC709:
- case V4L2_COLORSPACE_SRGB:
- default:
- b = 4769 * y + 8652 * cb;
- break;
- }
- return clamp(b >> 12, 0, 0xff0);
}
/* precalculate color bar values to speed up rendering */
@@ -456,18 +522,17 @@
g <<= 4;
b <<= 4;
}
- if (tpg->qual == TPG_QUAL_GRAY)
- r = g = b = color_to_y(tpg, r, g, b);
+ if (tpg->qual == TPG_QUAL_GRAY) {
+ /* Rec. 709 Luma function */
+ /* (0.2126, 0.7152, 0.0722) * (255 * 256) */
+ r = g = b = ((13879 * r + 46688 * g + 4713 * b) >> 16) + (16 << 4);
+ }
/*
* The assumption is that the RGB output is always full range,
* so only if the rgb_range overrides the 'real' rgb range do
* we need to convert the RGB values.
*
- * Currently there is no way of signalling to userspace if you
- * are actually giving it limited range RGB (or full range
- * YUV for that matter).
- *
* Remember that r, g and b are still in the 0 - 0xff0 range.
*/
if (tpg->real_rgb_range == V4L2_DV_RGB_RANGE_LIMITED &&
@@ -497,12 +562,12 @@
if (tpg->brightness != 128 || tpg->contrast != 128 ||
tpg->saturation != 128 || tpg->hue) {
/* Implement these operations */
+ int y, cb, cr;
+ int tmp_cb, tmp_cr;
/* First convert to YCbCr */
- int y = color_to_y(tpg, r, g, b); /* Luma */
- int cb = color_to_cb(tpg, r, g, b); /* Cb */
- int cr = color_to_cr(tpg, r, g, b); /* Cr */
- int tmp_cb, tmp_cr;
+
+ color_to_ycbcr(tpg, r, g, b, &y, &cb, &cr);
y = (16 << 4) + ((y - (16 << 4)) * tpg->contrast) / 128;
y += (tpg->brightness << 4) - (128 << 4);
@@ -520,21 +585,29 @@
tpg->colors[k][2] = clamp(cr >> 4, 1, 254);
return;
}
- r = ycbcr_to_r(tpg, y, cb, cr);
- g = ycbcr_to_g(tpg, y, cb, cr);
- b = ycbcr_to_b(tpg, y, cb, cr);
+ ycbcr_to_color(tpg, y, cb, cr, &r, &g, &b);
}
if (tpg->is_yuv) {
/* Convert to YCbCr */
- u16 y = color_to_y(tpg, r, g, b); /* Luma */
- u16 cb = color_to_cb(tpg, r, g, b); /* Cb */
- u16 cr = color_to_cr(tpg, r, g, b); /* Cr */
+ int y, cb, cr;
+ color_to_ycbcr(tpg, r, g, b, &y, &cb, &cr);
+
+ if (tpg->real_quantization == V4L2_QUANTIZATION_LIM_RANGE) {
+ y = clamp(y, 16 << 4, 235 << 4);
+ cb = clamp(cb, 16 << 4, 240 << 4);
+ cr = clamp(cr, 16 << 4, 240 << 4);
+ }
tpg->colors[k][0] = clamp(y >> 4, 1, 254);
tpg->colors[k][1] = clamp(cb >> 4, 1, 254);
tpg->colors[k][2] = clamp(cr >> 4, 1, 254);
} else {
+ if (tpg->real_quantization == V4L2_QUANTIZATION_LIM_RANGE) {
+ r = (r * 219) / 255 + (16 << 4);
+ g = (g * 219) / 255 + (16 << 4);
+ b = (b * 219) / 255 + (16 << 4);
+ }
switch (tpg->fourcc) {
case V4L2_PIX_FMT_RGB565:
case V4L2_PIX_FMT_RGB565X:
@@ -1152,6 +1225,46 @@
if (tpg->recalc_colors) {
tpg->recalc_colors = false;
tpg->recalc_lines = true;
+ tpg->real_ycbcr_enc = tpg->ycbcr_enc;
+ tpg->real_quantization = tpg->quantization;
+ if (tpg->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) {
+ switch (tpg->colorspace) {
+ case V4L2_COLORSPACE_REC709:
+ tpg->real_ycbcr_enc = V4L2_YCBCR_ENC_709;
+ break;
+ case V4L2_COLORSPACE_SRGB:
+ tpg->real_ycbcr_enc = V4L2_YCBCR_ENC_SYCC;
+ break;
+ case V4L2_COLORSPACE_BT2020:
+ tpg->real_ycbcr_enc = V4L2_YCBCR_ENC_BT2020;
+ break;
+ case V4L2_COLORSPACE_SMPTE240M:
+ tpg->real_ycbcr_enc = V4L2_YCBCR_ENC_SMPTE240M;
+ break;
+ case V4L2_COLORSPACE_SMPTE170M:
+ case V4L2_COLORSPACE_470_SYSTEM_M:
+ case V4L2_COLORSPACE_470_SYSTEM_BG:
+ case V4L2_COLORSPACE_ADOBERGB:
+ default:
+ tpg->real_ycbcr_enc = V4L2_YCBCR_ENC_601;
+ break;
+ }
+ }
+ if (tpg->quantization == V4L2_QUANTIZATION_DEFAULT) {
+ tpg->real_quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ if (tpg->is_yuv) {
+ switch (tpg->real_ycbcr_enc) {
+ case V4L2_YCBCR_ENC_SYCC:
+ case V4L2_YCBCR_ENC_XV601:
+ case V4L2_YCBCR_ENC_XV709:
+ break;
+ default:
+ tpg->real_quantization =
+ V4L2_QUANTIZATION_LIM_RANGE;
+ break;
+ }
+ }
+ }
tpg_precalculate_colors(tpg);
}
if (tpg->recalc_square_border) {
diff --git a/drivers/media/platform/vivid/vivid-tpg.h b/drivers/media/platform/vivid/vivid-tpg.h
index 8ef3e52..9dc463a 100644
--- a/drivers/media/platform/vivid/vivid-tpg.h
+++ b/drivers/media/platform/vivid/vivid-tpg.h
@@ -119,6 +119,18 @@
u32 fourcc;
bool is_yuv;
u32 colorspace;
+ u32 ycbcr_enc;
+ /*
+ * Stores the actual Y'CbCr encoding, i.e. will never be
+ * V4L2_YCBCR_ENC_DEFAULT.
+ */
+ u32 real_ycbcr_enc;
+ u32 quantization;
+ /*
+ * Stores the actual quantization, i.e. will never be
+ * V4L2_QUANTIZATION_DEFAULT.
+ */
+ u32 real_quantization;
enum tpg_video_aspect vid_aspect;
enum tpg_pixel_aspect pix_aspect;
unsigned rgb_range;
@@ -286,6 +298,32 @@
return tpg->colorspace;
}
+static inline void tpg_s_ycbcr_enc(struct tpg_data *tpg, u32 ycbcr_enc)
+{
+ if (tpg->ycbcr_enc == ycbcr_enc)
+ return;
+ tpg->ycbcr_enc = ycbcr_enc;
+ tpg->recalc_colors = true;
+}
+
+static inline u32 tpg_g_ycbcr_enc(const struct tpg_data *tpg)
+{
+ return tpg->ycbcr_enc;
+}
+
+static inline void tpg_s_quantization(struct tpg_data *tpg, u32 quantization)
+{
+ if (tpg->quantization == quantization)
+ return;
+ tpg->quantization = quantization;
+ tpg->recalc_colors = true;
+}
+
+static inline u32 tpg_g_quantization(const struct tpg_data *tpg)
+{
+ return tpg->quantization;
+}
+
static inline unsigned tpg_g_planes(const struct tpg_data *tpg)
{
return tpg->planes;
diff --git a/drivers/media/platform/vivid/vivid-vbi-cap.c b/drivers/media/platform/vivid/vivid-vbi-cap.c
index 2166d0b..ef81b01 100644
--- a/drivers/media/platform/vivid/vivid-vbi-cap.c
+++ b/drivers/media/platform/vivid/vivid-vbi-cap.c
@@ -236,8 +236,8 @@
.buf_queue = vbi_cap_buf_queue,
.start_streaming = vbi_cap_start_streaming,
.stop_streaming = vbi_cap_stop_streaming,
- .wait_prepare = vivid_unlock,
- .wait_finish = vivid_lock,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
};
int vidioc_g_fmt_vbi_cap(struct file *file, void *priv,
diff --git a/drivers/media/platform/vivid/vivid-vbi-out.c b/drivers/media/platform/vivid/vivid-vbi-out.c
index 9d00a07..4e4c70e 100644
--- a/drivers/media/platform/vivid/vivid-vbi-out.c
+++ b/drivers/media/platform/vivid/vivid-vbi-out.c
@@ -131,8 +131,8 @@
.buf_queue = vbi_out_buf_queue,
.start_streaming = vbi_out_start_streaming,
.stop_streaming = vbi_out_stop_streaming,
- .wait_prepare = vivid_unlock,
- .wait_finish = vivid_lock,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
};
int vidioc_g_fmt_vbi_out(struct file *file, void *priv,
diff --git a/drivers/media/platform/vivid/vivid-vid-cap.c b/drivers/media/platform/vivid/vivid-vid-cap.c
index 331c544..867a29a 100644
--- a/drivers/media/platform/vivid/vivid-vid-cap.c
+++ b/drivers/media/platform/vivid/vivid-vid-cap.c
@@ -288,8 +288,8 @@
.buf_queue = vid_cap_buf_queue,
.start_streaming = vid_cap_start_streaming,
.stop_streaming = vid_cap_stop_streaming,
- .wait_prepare = vivid_unlock,
- .wait_finish = vivid_lock,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
};
/*
@@ -443,12 +443,12 @@
break;
if (bt->standards & V4L2_DV_BT_STD_CEA861) {
if (bt->width == 720 && bt->height <= 576)
- v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_SMPTE170M);
+ v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_170M);
else
- v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_REC709);
+ v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_709);
v4l2_ctrl_s_ctrl(dev->real_rgb_range_cap, 1);
} else {
- v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_SRGB);
+ v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_SRGB);
v4l2_ctrl_s_ctrl(dev->real_rgb_range_cap, 0);
}
tpg_s_rgb_range(&dev->tpg, v4l2_ctrl_g_ctrl(dev->rgb_range_cap));
@@ -498,6 +498,20 @@
return dev->colorspace_out;
}
+static unsigned vivid_ycbcr_enc_cap(struct vivid_dev *dev)
+{
+ if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev))
+ return tpg_g_ycbcr_enc(&dev->tpg);
+ return dev->ycbcr_enc_out;
+}
+
+static unsigned vivid_quantization_cap(struct vivid_dev *dev)
+{
+ if (!dev->loop_video || vivid_is_webcam(dev) || vivid_is_tv_cap(dev))
+ return tpg_g_quantization(&dev->tpg);
+ return dev->quantization_out;
+}
+
int vivid_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
@@ -510,6 +524,8 @@
mp->field = dev->field_cap;
mp->pixelformat = dev->fmt_cap->fourcc;
mp->colorspace = vivid_colorspace_cap(dev);
+ mp->ycbcr_enc = vivid_ycbcr_enc_cap(dev);
+ mp->quantization = vivid_quantization_cap(dev);
mp->num_planes = dev->fmt_cap->planes;
for (p = 0; p < mp->num_planes; p++) {
mp->plane_fmt[p].bytesperline = tpg_g_bytesperline(&dev->tpg, p);
@@ -595,6 +611,8 @@
memset(pfmt[p].reserved, 0, sizeof(pfmt[p].reserved));
}
mp->colorspace = vivid_colorspace_cap(dev);
+ mp->ycbcr_enc = vivid_ycbcr_enc_cap(dev);
+ mp->quantization = vivid_quantization_cap(dev);
memset(mp->reserved, 0, sizeof(mp->reserved));
return 0;
}
@@ -1307,20 +1325,20 @@
if (dev->colorspace) {
switch (dev->input_type[i]) {
case WEBCAM:
- v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_SRGB);
+ v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_SRGB);
break;
case TV:
case SVID:
- v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_SMPTE170M);
+ v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_170M);
break;
case HDMI:
if (bt->standards & V4L2_DV_BT_STD_CEA861) {
if (dev->src_rect.width == 720 && dev->src_rect.height <= 576)
- v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_SMPTE170M);
+ v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_170M);
else
- v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_REC709);
+ v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_709);
} else {
- v4l2_ctrl_s_ctrl(dev->colorspace, V4L2_COLORSPACE_SRGB);
+ v4l2_ctrl_s_ctrl(dev->colorspace, VIVID_CS_SRGB);
}
break;
}
diff --git a/drivers/media/platform/vivid/vivid-vid-common.c b/drivers/media/platform/vivid/vivid-vid-common.c
index 16cd6d2..6bef1e6 100644
--- a/drivers/media/platform/vivid/vivid-vid-common.c
+++ b/drivers/media/platform/vivid/vivid-vid-common.c
@@ -259,6 +259,8 @@
mp->pixelformat = pix->pixelformat;
mp->field = pix->field;
mp->colorspace = pix->colorspace;
+ mp->ycbcr_enc = pix->ycbcr_enc;
+ mp->quantization = pix->quantization;
mp->num_planes = 1;
mp->flags = pix->flags;
ppix->sizeimage = pix->sizeimage;
@@ -285,6 +287,8 @@
pix->pixelformat = mp->pixelformat;
pix->field = mp->field;
pix->colorspace = mp->colorspace;
+ pix->ycbcr_enc = mp->ycbcr_enc;
+ pix->quantization = mp->quantization;
pix->sizeimage = ppix->sizeimage;
pix->bytesperline = ppix->bytesperline;
pix->flags = mp->flags;
diff --git a/drivers/media/platform/vivid/vivid-vid-out.c b/drivers/media/platform/vivid/vivid-vid-out.c
index 69c2dbd..39ff79f 100644
--- a/drivers/media/platform/vivid/vivid-vid-out.c
+++ b/drivers/media/platform/vivid/vivid-vid-out.c
@@ -209,8 +209,8 @@
.buf_queue = vid_out_buf_queue,
.start_streaming = vid_out_start_streaming,
.stop_streaming = vid_out_stop_streaming,
- .wait_prepare = vivid_unlock,
- .wait_finish = vivid_lock,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
};
/*
@@ -259,6 +259,8 @@
}
break;
}
+ dev->ycbcr_enc_out = V4L2_YCBCR_ENC_DEFAULT;
+ dev->quantization_out = V4L2_QUANTIZATION_DEFAULT;
dev->compose_out = dev->sink_rect;
dev->compose_bounds_out = dev->sink_rect;
dev->crop_out = dev->compose_out;
@@ -318,6 +320,8 @@
mp->field = dev->field_out;
mp->pixelformat = dev->fmt_out->fourcc;
mp->colorspace = dev->colorspace_out;
+ mp->ycbcr_enc = dev->ycbcr_enc_out;
+ mp->quantization = dev->quantization_out;
mp->num_planes = dev->fmt_out->planes;
for (p = 0; p < mp->num_planes; p++) {
mp->plane_fmt[p].bytesperline = dev->bytesperline_out[p];
@@ -394,16 +398,23 @@
pfmt[p].sizeimage = pfmt[p].bytesperline * mp->height;
memset(pfmt[p].reserved, 0, sizeof(pfmt[p].reserved));
}
- if (vivid_is_svid_out(dev))
+ mp->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ mp->quantization = V4L2_QUANTIZATION_DEFAULT;
+ if (vivid_is_svid_out(dev)) {
mp->colorspace = V4L2_COLORSPACE_SMPTE170M;
- else if (dev->dvi_d_out || !(bt->standards & V4L2_DV_BT_STD_CEA861))
+ } else if (dev->dvi_d_out || !(bt->standards & V4L2_DV_BT_STD_CEA861)) {
mp->colorspace = V4L2_COLORSPACE_SRGB;
- else if (bt->width == 720 && bt->height <= 576)
+ if (dev->dvi_d_out)
+ mp->quantization = V4L2_QUANTIZATION_LIM_RANGE;
+ } else if (bt->width == 720 && bt->height <= 576) {
mp->colorspace = V4L2_COLORSPACE_SMPTE170M;
- else if (mp->colorspace != V4L2_COLORSPACE_SMPTE170M &&
- mp->colorspace != V4L2_COLORSPACE_REC709 &&
- mp->colorspace != V4L2_COLORSPACE_SRGB)
+ } else if (mp->colorspace != V4L2_COLORSPACE_SMPTE170M &&
+ mp->colorspace != V4L2_COLORSPACE_REC709 &&
+ mp->colorspace != V4L2_COLORSPACE_ADOBERGB &&
+ mp->colorspace != V4L2_COLORSPACE_BT2020 &&
+ mp->colorspace != V4L2_COLORSPACE_SRGB) {
mp->colorspace = V4L2_COLORSPACE_REC709;
+ }
memset(mp->reserved, 0, sizeof(mp->reserved));
return 0;
}
@@ -522,6 +533,8 @@
set_colorspace:
dev->colorspace_out = mp->colorspace;
+ dev->ycbcr_enc_out = mp->ycbcr_enc;
+ dev->quantization_out = mp->quantization;
if (dev->loop_video) {
vivid_send_source_change(dev, SVID);
vivid_send_source_change(dev, HDMI);
@@ -612,7 +625,7 @@
sel->r = dev->fmt_out_rect;
break;
case V4L2_SEL_TGT_CROP_BOUNDS:
- if (!dev->has_compose_out)
+ if (!dev->has_crop_out)
return -EINVAL;
sel->r = vivid_max_rect;
break;
diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c
index a0c1984..b21f381 100644
--- a/drivers/media/platform/vsp1/vsp1_bru.c
+++ b/drivers/media/platform/vsp1/vsp1_bru.c
@@ -187,8 +187,8 @@
struct v4l2_subdev_mbus_code_enum *code)
{
static const unsigned int codes[] = {
- V4L2_MBUS_FMT_ARGB8888_1X32,
- V4L2_MBUS_FMT_AYUV8_1X32,
+ MEDIA_BUS_FMT_ARGB8888_1X32,
+ MEDIA_BUS_FMT_AYUV8_1X32,
};
struct v4l2_mbus_framefmt *format;
@@ -215,8 +215,8 @@
if (fse->index)
return -EINVAL;
- if (fse->code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
- fse->code != V4L2_MBUS_FMT_AYUV8_1X32)
+ if (fse->code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
+ fse->code != MEDIA_BUS_FMT_AYUV8_1X32)
return -EINVAL;
fse->min_width = BRU_MIN_SIZE;
@@ -261,9 +261,9 @@
switch (pad) {
case BRU_PAD_SINK(0):
/* Default to YUV if the requested format is not supported. */
- if (fmt->code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
- fmt->code != V4L2_MBUS_FMT_AYUV8_1X32)
- fmt->code = V4L2_MBUS_FMT_AYUV8_1X32;
+ if (fmt->code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
+ fmt->code != MEDIA_BUS_FMT_AYUV8_1X32)
+ fmt->code = MEDIA_BUS_FMT_AYUV8_1X32;
break;
default:
diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c
index 3e6601b..5eb16e8 100644
--- a/drivers/media/platform/vsp1/vsp1_drv.c
+++ b/drivers/media/platform/vsp1/vsp1_drv.c
@@ -562,7 +562,6 @@
.probe = vsp1_probe,
.remove = vsp1_remove,
.driver = {
- .owner = THIS_MODULE,
.name = "vsp1",
.pm = &vsp1_pm_ops,
.of_match_table = vsp1_of_match,
diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c
index db2950a..80bedc5 100644
--- a/drivers/media/platform/vsp1/vsp1_hsit.c
+++ b/drivers/media/platform/vsp1/vsp1_hsit.c
@@ -70,9 +70,9 @@
if ((code->pad == HSIT_PAD_SINK && !hsit->inverse) |
(code->pad == HSIT_PAD_SOURCE && hsit->inverse))
- code->code = V4L2_MBUS_FMT_ARGB8888_1X32;
+ code->code = MEDIA_BUS_FMT_ARGB8888_1X32;
else
- code->code = V4L2_MBUS_FMT_AHSV8888_1X32;
+ code->code = MEDIA_BUS_FMT_AHSV8888_1X32;
return 0;
}
@@ -136,8 +136,8 @@
return 0;
}
- format->code = hsit->inverse ? V4L2_MBUS_FMT_AHSV8888_1X32
- : V4L2_MBUS_FMT_ARGB8888_1X32;
+ format->code = hsit->inverse ? MEDIA_BUS_FMT_AHSV8888_1X32
+ : MEDIA_BUS_FMT_ARGB8888_1X32;
format->width = clamp_t(unsigned int, fmt->format.width,
HSIT_MIN_SIZE, HSIT_MAX_SIZE);
format->height = clamp_t(unsigned int, fmt->format.height,
@@ -151,8 +151,8 @@
format = vsp1_entity_get_pad_format(&hsit->entity, fh, HSIT_PAD_SOURCE,
fmt->which);
*format = fmt->format;
- format->code = hsit->inverse ? V4L2_MBUS_FMT_ARGB8888_1X32
- : V4L2_MBUS_FMT_AHSV8888_1X32;
+ format->code = hsit->inverse ? MEDIA_BUS_FMT_ARGB8888_1X32
+ : MEDIA_BUS_FMT_AHSV8888_1X32;
return 0;
}
diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c
index d4fb23e..17a6ca7 100644
--- a/drivers/media/platform/vsp1/vsp1_lif.c
+++ b/drivers/media/platform/vsp1/vsp1_lif.c
@@ -78,8 +78,8 @@
struct v4l2_subdev_mbus_code_enum *code)
{
static const unsigned int codes[] = {
- V4L2_MBUS_FMT_ARGB8888_1X32,
- V4L2_MBUS_FMT_AYUV8_1X32,
+ MEDIA_BUS_FMT_ARGB8888_1X32,
+ MEDIA_BUS_FMT_AYUV8_1X32,
};
if (code->pad == LIF_PAD_SINK) {
@@ -147,9 +147,9 @@
struct v4l2_mbus_framefmt *format;
/* Default to YUV if the requested format is not supported. */
- if (fmt->format.code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
- fmt->format.code != V4L2_MBUS_FMT_AYUV8_1X32)
- fmt->format.code = V4L2_MBUS_FMT_AYUV8_1X32;
+ if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
+ fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32)
+ fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32;
format = vsp1_entity_get_pad_format(&lif->entity, fh, fmt->pad,
fmt->which);
diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c
index fea36eb..6f185c3 100644
--- a/drivers/media/platform/vsp1/vsp1_lut.c
+++ b/drivers/media/platform/vsp1/vsp1_lut.c
@@ -86,9 +86,9 @@
struct v4l2_subdev_mbus_code_enum *code)
{
static const unsigned int codes[] = {
- V4L2_MBUS_FMT_ARGB8888_1X32,
- V4L2_MBUS_FMT_AHSV8888_1X32,
- V4L2_MBUS_FMT_AYUV8_1X32,
+ MEDIA_BUS_FMT_ARGB8888_1X32,
+ MEDIA_BUS_FMT_AHSV8888_1X32,
+ MEDIA_BUS_FMT_AYUV8_1X32,
};
struct v4l2_mbus_framefmt *format;
@@ -158,10 +158,10 @@
struct v4l2_mbus_framefmt *format;
/* Default to YUV if the requested format is not supported. */
- if (fmt->format.code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
- fmt->format.code != V4L2_MBUS_FMT_AHSV8888_1X32 &&
- fmt->format.code != V4L2_MBUS_FMT_AYUV8_1X32)
- fmt->format.code = V4L2_MBUS_FMT_AYUV8_1X32;
+ if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
+ fmt->format.code != MEDIA_BUS_FMT_AHSV8888_1X32 &&
+ fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32)
+ fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32;
format = vsp1_entity_get_pad_format(&lut->entity, fh, fmt->pad,
fmt->which);
diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c
index ec3dab6..1f1ba26 100644
--- a/drivers/media/platform/vsp1/vsp1_rwpf.c
+++ b/drivers/media/platform/vsp1/vsp1_rwpf.c
@@ -29,8 +29,8 @@
struct v4l2_subdev_mbus_code_enum *code)
{
static const unsigned int codes[] = {
- V4L2_MBUS_FMT_ARGB8888_1X32,
- V4L2_MBUS_FMT_AYUV8_1X32,
+ MEDIA_BUS_FMT_ARGB8888_1X32,
+ MEDIA_BUS_FMT_AYUV8_1X32,
};
if (code->index >= ARRAY_SIZE(codes))
@@ -103,9 +103,9 @@
struct v4l2_rect *crop;
/* Default to YUV if the requested format is not supported. */
- if (fmt->format.code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
- fmt->format.code != V4L2_MBUS_FMT_AYUV8_1X32)
- fmt->format.code = V4L2_MBUS_FMT_AYUV8_1X32;
+ if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
+ fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32)
+ fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32;
format = vsp1_entity_get_pad_format(&rwpf->entity, fh, fmt->pad,
fmt->which);
diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c
index b7d3c8b..1129494 100644
--- a/drivers/media/platform/vsp1/vsp1_sru.c
+++ b/drivers/media/platform/vsp1/vsp1_sru.c
@@ -139,7 +139,7 @@
input = &sru->entity.formats[SRU_PAD_SINK];
output = &sru->entity.formats[SRU_PAD_SOURCE];
- if (input->code == V4L2_MBUS_FMT_ARGB8888_1X32)
+ if (input->code == MEDIA_BUS_FMT_ARGB8888_1X32)
ctrl0 = VI6_SRU_CTRL0_PARAM2 | VI6_SRU_CTRL0_PARAM3
| VI6_SRU_CTRL0_PARAM4;
else
@@ -170,8 +170,8 @@
struct v4l2_subdev_mbus_code_enum *code)
{
static const unsigned int codes[] = {
- V4L2_MBUS_FMT_ARGB8888_1X32,
- V4L2_MBUS_FMT_AYUV8_1X32,
+ MEDIA_BUS_FMT_ARGB8888_1X32,
+ MEDIA_BUS_FMT_AYUV8_1X32,
};
struct v4l2_mbus_framefmt *format;
@@ -248,9 +248,9 @@
switch (pad) {
case SRU_PAD_SINK:
/* Default to YUV if the requested format is not supported. */
- if (fmt->code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
- fmt->code != V4L2_MBUS_FMT_AYUV8_1X32)
- fmt->code = V4L2_MBUS_FMT_AYUV8_1X32;
+ if (fmt->code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
+ fmt->code != MEDIA_BUS_FMT_AYUV8_1X32)
+ fmt->code = MEDIA_BUS_FMT_AYUV8_1X32;
fmt->width = clamp(fmt->width, SRU_MIN_SIZE, SRU_MAX_SIZE);
fmt->height = clamp(fmt->height, SRU_MIN_SIZE, SRU_MAX_SIZE);
diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c
index de92ef4..a4afec1 100644
--- a/drivers/media/platform/vsp1/vsp1_uds.c
+++ b/drivers/media/platform/vsp1/vsp1_uds.c
@@ -173,8 +173,8 @@
struct v4l2_subdev_mbus_code_enum *code)
{
static const unsigned int codes[] = {
- V4L2_MBUS_FMT_ARGB8888_1X32,
- V4L2_MBUS_FMT_AYUV8_1X32,
+ MEDIA_BUS_FMT_ARGB8888_1X32,
+ MEDIA_BUS_FMT_AYUV8_1X32,
};
if (code->pad == UDS_PAD_SINK) {
@@ -246,9 +246,9 @@
switch (pad) {
case UDS_PAD_SINK:
/* Default to YUV if the requested format is not supported. */
- if (fmt->code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
- fmt->code != V4L2_MBUS_FMT_AYUV8_1X32)
- fmt->code = V4L2_MBUS_FMT_AYUV8_1X32;
+ if (fmt->code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
+ fmt->code != MEDIA_BUS_FMT_AYUV8_1X32)
+ fmt->code = MEDIA_BUS_FMT_AYUV8_1X32;
fmt->width = clamp(fmt->width, UDS_MIN_SIZE, UDS_MAX_SIZE);
fmt->height = clamp(fmt->height, UDS_MIN_SIZE, UDS_MAX_SIZE);
diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c
index 915a20e..d91f19a 100644
--- a/drivers/media/platform/vsp1/vsp1_video.c
+++ b/drivers/media/platform/vsp1/vsp1_video.c
@@ -48,85 +48,85 @@
*/
static const struct vsp1_format_info vsp1_video_formats[] = {
- { V4L2_PIX_FMT_RGB332, V4L2_MBUS_FMT_ARGB8888_1X32,
+ { V4L2_PIX_FMT_RGB332, MEDIA_BUS_FMT_ARGB8888_1X32,
VI6_FMT_RGB_332, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
1, { 8, 0, 0 }, false, false, 1, 1, false },
- { V4L2_PIX_FMT_ARGB444, V4L2_MBUS_FMT_ARGB8888_1X32,
+ { V4L2_PIX_FMT_ARGB444, MEDIA_BUS_FMT_ARGB8888_1X32,
VI6_FMT_ARGB_4444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS,
1, { 16, 0, 0 }, false, false, 1, 1, true },
- { V4L2_PIX_FMT_XRGB444, V4L2_MBUS_FMT_ARGB8888_1X32,
+ { V4L2_PIX_FMT_XRGB444, MEDIA_BUS_FMT_ARGB8888_1X32,
VI6_FMT_XRGB_4444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS,
1, { 16, 0, 0 }, false, false, 1, 1, true },
- { V4L2_PIX_FMT_ARGB555, V4L2_MBUS_FMT_ARGB8888_1X32,
+ { V4L2_PIX_FMT_ARGB555, MEDIA_BUS_FMT_ARGB8888_1X32,
VI6_FMT_ARGB_1555, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS,
1, { 16, 0, 0 }, false, false, 1, 1, true },
- { V4L2_PIX_FMT_XRGB555, V4L2_MBUS_FMT_ARGB8888_1X32,
+ { V4L2_PIX_FMT_XRGB555, MEDIA_BUS_FMT_ARGB8888_1X32,
VI6_FMT_XRGB_1555, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS,
1, { 16, 0, 0 }, false, false, 1, 1, false },
- { V4L2_PIX_FMT_RGB565, V4L2_MBUS_FMT_ARGB8888_1X32,
+ { V4L2_PIX_FMT_RGB565, MEDIA_BUS_FMT_ARGB8888_1X32,
VI6_FMT_RGB_565, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS,
1, { 16, 0, 0 }, false, false, 1, 1, false },
- { V4L2_PIX_FMT_BGR24, V4L2_MBUS_FMT_ARGB8888_1X32,
+ { V4L2_PIX_FMT_BGR24, MEDIA_BUS_FMT_ARGB8888_1X32,
VI6_FMT_BGR_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
1, { 24, 0, 0 }, false, false, 1, 1, false },
- { V4L2_PIX_FMT_RGB24, V4L2_MBUS_FMT_ARGB8888_1X32,
+ { V4L2_PIX_FMT_RGB24, MEDIA_BUS_FMT_ARGB8888_1X32,
VI6_FMT_RGB_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
1, { 24, 0, 0 }, false, false, 1, 1, false },
- { V4L2_PIX_FMT_ABGR32, V4L2_MBUS_FMT_ARGB8888_1X32,
+ { V4L2_PIX_FMT_ABGR32, MEDIA_BUS_FMT_ARGB8888_1X32,
VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS,
1, { 32, 0, 0 }, false, false, 1, 1, true },
- { V4L2_PIX_FMT_XBGR32, V4L2_MBUS_FMT_ARGB8888_1X32,
+ { V4L2_PIX_FMT_XBGR32, MEDIA_BUS_FMT_ARGB8888_1X32,
VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS,
1, { 32, 0, 0 }, false, false, 1, 1, false },
- { V4L2_PIX_FMT_ARGB32, V4L2_MBUS_FMT_ARGB8888_1X32,
+ { V4L2_PIX_FMT_ARGB32, MEDIA_BUS_FMT_ARGB8888_1X32,
VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
1, { 32, 0, 0 }, false, false, 1, 1, true },
- { V4L2_PIX_FMT_XRGB32, V4L2_MBUS_FMT_ARGB8888_1X32,
+ { V4L2_PIX_FMT_XRGB32, MEDIA_BUS_FMT_ARGB8888_1X32,
VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
1, { 32, 0, 0 }, false, false, 1, 1, false },
- { V4L2_PIX_FMT_UYVY, V4L2_MBUS_FMT_AYUV8_1X32,
+ { V4L2_PIX_FMT_UYVY, MEDIA_BUS_FMT_AYUV8_1X32,
VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
1, { 16, 0, 0 }, false, false, 2, 1, false },
- { V4L2_PIX_FMT_VYUY, V4L2_MBUS_FMT_AYUV8_1X32,
+ { V4L2_PIX_FMT_VYUY, MEDIA_BUS_FMT_AYUV8_1X32,
VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
1, { 16, 0, 0 }, false, true, 2, 1, false },
- { V4L2_PIX_FMT_YUYV, V4L2_MBUS_FMT_AYUV8_1X32,
+ { V4L2_PIX_FMT_YUYV, MEDIA_BUS_FMT_AYUV8_1X32,
VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
1, { 16, 0, 0 }, true, false, 2, 1, false },
- { V4L2_PIX_FMT_YVYU, V4L2_MBUS_FMT_AYUV8_1X32,
+ { V4L2_PIX_FMT_YVYU, MEDIA_BUS_FMT_AYUV8_1X32,
VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
1, { 16, 0, 0 }, true, true, 2, 1, false },
- { V4L2_PIX_FMT_NV12M, V4L2_MBUS_FMT_AYUV8_1X32,
+ { V4L2_PIX_FMT_NV12M, MEDIA_BUS_FMT_AYUV8_1X32,
VI6_FMT_Y_UV_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
2, { 8, 16, 0 }, false, false, 2, 2, false },
- { V4L2_PIX_FMT_NV21M, V4L2_MBUS_FMT_AYUV8_1X32,
+ { V4L2_PIX_FMT_NV21M, MEDIA_BUS_FMT_AYUV8_1X32,
VI6_FMT_Y_UV_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
2, { 8, 16, 0 }, false, true, 2, 2, false },
- { V4L2_PIX_FMT_NV16M, V4L2_MBUS_FMT_AYUV8_1X32,
+ { V4L2_PIX_FMT_NV16M, MEDIA_BUS_FMT_AYUV8_1X32,
VI6_FMT_Y_UV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
2, { 8, 16, 0 }, false, false, 2, 1, false },
- { V4L2_PIX_FMT_NV61M, V4L2_MBUS_FMT_AYUV8_1X32,
+ { V4L2_PIX_FMT_NV61M, MEDIA_BUS_FMT_AYUV8_1X32,
VI6_FMT_Y_UV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
2, { 8, 16, 0 }, false, true, 2, 1, false },
- { V4L2_PIX_FMT_YUV420M, V4L2_MBUS_FMT_AYUV8_1X32,
+ { V4L2_PIX_FMT_YUV420M, MEDIA_BUS_FMT_AYUV8_1X32,
VI6_FMT_Y_U_V_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
3, { 8, 8, 8 }, false, false, 2, 2, false },
diff --git a/drivers/media/radio/radio-si476x.c b/drivers/media/radio/radio-si476x.c
index 5215a21..da160f4 100644
--- a/drivers/media/radio/radio-si476x.c
+++ b/drivers/media/radio/radio-si476x.c
@@ -1575,7 +1575,6 @@
static struct platform_driver si476x_radio_driver = {
.driver = {
.name = DRIVER_NAME,
- .owner = THIS_MODULE,
},
.probe = si476x_radio_probe,
.remove = si476x_radio_remove,
diff --git a/drivers/media/radio/radio-timb.c b/drivers/media/radio/radio-timb.c
index b9285e6..e6b55ed 100644
--- a/drivers/media/radio/radio-timb.c
+++ b/drivers/media/radio/radio-timb.c
@@ -174,7 +174,6 @@
static struct platform_driver timbradio_platform_driver = {
.driver = {
.name = DRIVER_NAME,
- .owner = THIS_MODULE,
},
.probe = timbradio_probe,
.remove = timbradio_remove,
diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c
index 9cf6731..b8f3644 100644
--- a/drivers/media/radio/radio-wl1273.c
+++ b/drivers/media/radio/radio-wl1273.c
@@ -1279,10 +1279,12 @@
strlcpy(capability->bus_info, radio->bus_type,
sizeof(capability->bus_info));
- capability->capabilities = V4L2_CAP_HW_FREQ_SEEK |
+ capability->device_caps = V4L2_CAP_HW_FREQ_SEEK |
V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_AUDIO |
V4L2_CAP_RDS_CAPTURE | V4L2_CAP_MODULATOR |
V4L2_CAP_RDS_OUTPUT;
+ capability->capabilities = capability->device_caps |
+ V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -2148,7 +2150,6 @@
.remove = wl1273_fm_radio_remove,
.driver = {
.name = "wl1273_fm_radio",
- .owner = THIS_MODULE,
},
};
diff --git a/drivers/media/radio/si4713/Kconfig b/drivers/media/radio/si4713/Kconfig
index c8aca65..19f9fde 100644
--- a/drivers/media/radio/si4713/Kconfig
+++ b/drivers/media/radio/si4713/Kconfig
@@ -1,4 +1,5 @@
config USB_SI4713
+ depends on !KERNEL_3_13
tristate "Silicon Labs Si4713 FM Radio Transmitter support with USB"
depends on m
depends on USB && I2C && RADIO_SI4713
@@ -15,6 +16,7 @@
module will be called radio-usb-si4713.
config PLATFORM_SI4713
+ depends on !KERNEL_3_13
tristate "Silicon Labs Si4713 FM Radio Transmitter support with I2C"
depends on m
depends on I2C && RADIO_SI4713
@@ -30,6 +32,7 @@
module will be called radio-platform-si4713.
config I2C_SI4713
+ depends on !KERNEL_3_13
tristate "Silicon Labs Si4713 FM Radio Transmitter support"
depends on m
depends on I2C && RADIO_SI4713
diff --git a/drivers/media/radio/si4713/radio-platform-si4713.c b/drivers/media/radio/si4713/radio-platform-si4713.c
index a47502a..6c75973 100644
--- a/drivers/media/radio/si4713/radio-platform-si4713.c
+++ b/drivers/media/radio/si4713/radio-platform-si4713.c
@@ -34,7 +34,7 @@
#include <media/v4l2-fh.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-event.h>
-#include <media/radio-si4713.h>
+#include "si4713.h"
/* module parameters */
static int radio_nr = -1; /* radio device minor (-1 ==> auto assign) */
@@ -153,7 +153,6 @@
{
struct radio_si4713_platform_data *pdata = pdev->dev.platform_data;
struct radio_si4713_device *rsdev;
- struct i2c_adapter *adapter;
struct v4l2_subdev *sd;
int rval = 0;
@@ -177,20 +176,11 @@
goto exit;
}
- adapter = i2c_get_adapter(pdata->i2c_bus);
- if (!adapter) {
- dev_err(&pdev->dev, "Cannot get i2c adapter %d\n",
- pdata->i2c_bus);
- rval = -ENODEV;
- goto unregister_v4l2_dev;
- }
-
- sd = v4l2_i2c_new_subdev_board(&rsdev->v4l2_dev, adapter,
- pdata->subdev_board_info, NULL);
- if (!sd) {
+ sd = i2c_get_clientdata(pdata->subdev);
+ rval = v4l2_device_register_subdev(&rsdev->v4l2_dev, sd);
+ if (rval) {
dev_err(&pdev->dev, "Cannot get v4l2 subdevice\n");
- rval = -ENODEV;
- goto put_adapter;
+ goto unregister_v4l2_dev;
}
rsdev->radio_dev = radio_si4713_vdev_template;
@@ -202,14 +192,12 @@
if (video_register_device(&rsdev->radio_dev, VFL_TYPE_RADIO, radio_nr)) {
dev_err(&pdev->dev, "Could not register video device.\n");
rval = -EIO;
- goto put_adapter;
+ goto unregister_v4l2_dev;
}
dev_info(&pdev->dev, "New device successfully probed\n");
goto exit;
-put_adapter:
- i2c_put_adapter(adapter);
unregister_v4l2_dev:
v4l2_device_unregister(&rsdev->v4l2_dev);
exit:
@@ -220,14 +208,10 @@
static int radio_si4713_pdriver_remove(struct platform_device *pdev)
{
struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
- struct v4l2_subdev *sd = list_entry(v4l2_dev->subdevs.next,
- struct v4l2_subdev, list);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
struct radio_si4713_device *rsdev;
rsdev = container_of(v4l2_dev, struct radio_si4713_device, v4l2_dev);
video_unregister_device(&rsdev->radio_dev);
- i2c_put_adapter(client->adapter);
v4l2_device_unregister(&rsdev->v4l2_dev);
return 0;
@@ -236,7 +220,6 @@
static struct platform_driver radio_si4713_pdriver = {
.driver = {
.name = "radio-si4713",
- .owner = THIS_MODULE,
},
.probe = radio_si4713_pdriver_probe,
.remove = radio_si4713_pdriver_remove,
diff --git a/drivers/media/radio/si4713/si4713.c b/drivers/media/radio/si4713/si4713.c
index b576555..c90004d 100644
--- a/drivers/media/radio/si4713/si4713.c
+++ b/drivers/media/radio/si4713/si4713.c
@@ -23,6 +23,7 @@
#include <linux/completion.h>
#include <linux/delay.h>
+#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/slab.h>
@@ -366,16 +367,25 @@
if (sdev->power_state)
return 0;
- if (sdev->supplies) {
- err = regulator_bulk_enable(sdev->supplies, sdev->supply_data);
+ if (sdev->vdd) {
+ err = regulator_enable(sdev->vdd);
if (err) {
- v4l2_err(&sdev->sd, "Failed to enable supplies: %d\n", err);
+ v4l2_err(&sdev->sd, "Failed to enable vdd: %d\n", err);
return err;
}
}
- if (gpio_is_valid(sdev->gpio_reset)) {
+
+ if (sdev->vio) {
+ err = regulator_enable(sdev->vio);
+ if (err) {
+ v4l2_err(&sdev->sd, "Failed to enable vio: %d\n", err);
+ return err;
+ }
+ }
+
+ if (!IS_ERR(sdev->gpio_reset)) {
udelay(50);
- gpio_set_value(sdev->gpio_reset, 1);
+ gpiod_set_value(sdev->gpio_reset, 1);
}
if (client->irq)
@@ -397,13 +407,20 @@
SI4713_STC_INT | SI4713_CTS);
return err;
}
- if (gpio_is_valid(sdev->gpio_reset))
- gpio_set_value(sdev->gpio_reset, 0);
- if (sdev->supplies) {
- err = regulator_bulk_disable(sdev->supplies, sdev->supply_data);
+ if (!IS_ERR(sdev->gpio_reset))
+ gpiod_set_value(sdev->gpio_reset, 0);
+
+
+ if (sdev->vdd) {
+ err = regulator_disable(sdev->vdd);
if (err)
- v4l2_err(&sdev->sd,
- "Failed to disable supplies: %d\n", err);
+ v4l2_err(&sdev->sd, "Failed to disable vdd: %d\n", err);
+ }
+
+ if (sdev->vio) {
+ err = regulator_disable(sdev->vio);
+ if (err)
+ v4l2_err(&sdev->sd, "Failed to disable vio: %d\n", err);
}
return err;
@@ -430,14 +447,23 @@
v4l2_dbg(1, debug, &sdev->sd, "Power down response: 0x%02x\n",
resp[0]);
v4l2_dbg(1, debug, &sdev->sd, "Device in reset mode\n");
- if (gpio_is_valid(sdev->gpio_reset))
- gpio_set_value(sdev->gpio_reset, 0);
- if (sdev->supplies) {
- err = regulator_bulk_disable(sdev->supplies,
- sdev->supply_data);
- if (err)
+ if (!IS_ERR(sdev->gpio_reset))
+ gpiod_set_value(sdev->gpio_reset, 0);
+
+ if (sdev->vdd) {
+ err = regulator_disable(sdev->vdd);
+ if (err) {
v4l2_err(&sdev->sd,
- "Failed to disable supplies: %d\n", err);
+ "Failed to disable vdd: %d\n", err);
+ }
+ }
+
+ if (sdev->vio) {
+ err = regulator_disable(sdev->vio);
+ if (err) {
+ v4l2_err(&sdev->sd,
+ "Failed to disable vio: %d\n", err);
+ }
}
sdev->power_state = POWER_OFF;
}
@@ -1420,38 +1446,51 @@
const struct i2c_device_id *id)
{
struct si4713_device *sdev;
- struct si4713_platform_data *pdata = client->dev.platform_data;
struct v4l2_ctrl_handler *hdl;
- int rval, i;
+ struct si4713_platform_data *pdata = client->dev.platform_data;
+ struct device_node *np = client->dev.of_node;
+ struct radio_si4713_platform_data si4713_pdev_pdata;
+ struct platform_device *si4713_pdev;
+ int rval;
- sdev = kzalloc(sizeof(*sdev), GFP_KERNEL);
+ sdev = devm_kzalloc(&client->dev, sizeof(*sdev), GFP_KERNEL);
if (!sdev) {
dev_err(&client->dev, "Failed to alloc video device.\n");
rval = -ENOMEM;
goto exit;
}
- sdev->gpio_reset = -1;
- if (pdata && gpio_is_valid(pdata->gpio_reset)) {
- rval = gpio_request(pdata->gpio_reset, "si4713 reset");
- if (rval) {
- dev_err(&client->dev,
- "Failed to request gpio: %d\n", rval);
- goto free_sdev;
- }
- sdev->gpio_reset = pdata->gpio_reset;
- gpio_direction_output(sdev->gpio_reset, 0);
- sdev->supplies = pdata->supplies;
+ sdev->gpio_reset = devm_gpiod_get(&client->dev, "reset");
+ if (!IS_ERR(sdev->gpio_reset)) {
+ gpiod_direction_output(sdev->gpio_reset, 0);
+ } else if (PTR_ERR(sdev->gpio_reset) == -ENOENT) {
+ dev_dbg(&client->dev, "No reset GPIO assigned\n");
+ } else if (PTR_ERR(sdev->gpio_reset) == -ENOSYS) {
+ dev_dbg(&client->dev, "No reset GPIO support\n");
+ } else {
+ rval = PTR_ERR(sdev->gpio_reset);
+ dev_err(&client->dev, "Failed to request gpio: %d\n", rval);
+ goto exit;
}
- for (i = 0; i < sdev->supplies; i++)
- sdev->supply_data[i].supply = pdata->supply_names[i];
+ sdev->vdd = devm_regulator_get_optional(&client->dev, "vdd");
+ if (IS_ERR(sdev->vdd)) {
+ rval = PTR_ERR(sdev->vdd);
+ if (rval == -EPROBE_DEFER)
+ goto exit;
- rval = regulator_bulk_get(&client->dev, sdev->supplies,
- sdev->supply_data);
- if (rval) {
- dev_err(&client->dev, "Cannot get regulators: %d\n", rval);
- goto free_gpio;
+ dev_dbg(&client->dev, "no vdd regulator found: %d\n", rval);
+ sdev->vdd = NULL;
+ }
+
+ sdev->vio = devm_regulator_get_optional(&client->dev, "vio");
+ if (IS_ERR(sdev->vio)) {
+ rval = PTR_ERR(sdev->vio);
+ if (rval == -EPROBE_DEFER)
+ goto exit;
+
+ dev_dbg(&client->dev, "no vio regulator found: %d\n", rval);
+ sdev->vio = NULL;
}
v4l2_i2c_subdev_init(&sdev->sd, client, &si4713_subdev_ops);
@@ -1554,12 +1593,12 @@
sdev->sd.ctrl_handler = hdl;
if (client->irq) {
- rval = request_irq(client->irq,
+ rval = devm_request_irq(&client->dev, client->irq,
si4713_handler, IRQF_TRIGGER_FALLING,
client->name, sdev);
if (rval < 0) {
v4l2_err(&sdev->sd, "Could not request IRQ\n");
- goto put_reg;
+ goto free_ctrls;
}
v4l2_dbg(1, debug, &sdev->sd, "IRQ requested.\n");
} else {
@@ -1569,23 +1608,35 @@
rval = si4713_initialize(sdev);
if (rval < 0) {
v4l2_err(&sdev->sd, "Failed to probe device information.\n");
- goto free_irq;
+ goto free_ctrls;
}
+ if (!np && (!pdata || !pdata->is_platform_device))
+ return 0;
+
+ si4713_pdev = platform_device_alloc("radio-si4713", -1);
+ if (!si4713_pdev)
+ goto put_main_pdev;
+
+ si4713_pdev_pdata.subdev = client;
+ rval = platform_device_add_data(si4713_pdev, &si4713_pdev_pdata,
+ sizeof(si4713_pdev_pdata));
+ if (rval)
+ goto put_main_pdev;
+
+ rval = platform_device_add(si4713_pdev);
+ if (rval)
+ goto put_main_pdev;
+
+ sdev->pd = si4713_pdev;
+
return 0;
-free_irq:
- if (client->irq)
- free_irq(client->irq, sdev);
+put_main_pdev:
+ platform_device_put(si4713_pdev);
+ v4l2_device_unregister_subdev(&sdev->sd);
free_ctrls:
v4l2_ctrl_handler_free(hdl);
-put_reg:
- regulator_bulk_free(sdev->supplies, sdev->supply_data);
-free_gpio:
- if (gpio_is_valid(sdev->gpio_reset))
- gpio_free(sdev->gpio_reset);
-free_sdev:
- kfree(sdev);
exit:
return rval;
}
@@ -1596,18 +1647,13 @@
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct si4713_device *sdev = to_si4713_device(sd);
+ platform_device_unregister(sdev->pd);
+
if (sdev->power_state)
si4713_set_power_state(sdev, POWER_DOWN);
- if (client->irq > 0)
- free_irq(client->irq, sdev);
-
v4l2_device_unregister_subdev(sd);
v4l2_ctrl_handler_free(sd->ctrl_handler);
- regulator_bulk_free(sdev->supplies, sdev->supply_data);
- if (gpio_is_valid(sdev->gpio_reset))
- gpio_free(sdev->gpio_reset);
- kfree(sdev);
return 0;
}
diff --git a/drivers/media/radio/si4713/si4713.h b/drivers/media/radio/si4713/si4713.h
index ed700e3..8a376e1 100644
--- a/drivers/media/radio/si4713/si4713.h
+++ b/drivers/media/radio/si4713/si4713.h
@@ -15,7 +15,9 @@
#ifndef SI4713_I2C_H
#define SI4713_I2C_H
+#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-ctrls.h>
#include <media/si4713.h>
@@ -190,8 +192,6 @@
#define MIN_ACOMP_THRESHOLD (-40)
#define MAX_ACOMP_GAIN 20
-#define SI4713_NUM_SUPPLIES 2
-
/*
* si4713_device - private data
*/
@@ -236,9 +236,10 @@
struct v4l2_ctrl *tune_ant_cap;
};
struct completion work;
- unsigned supplies;
- struct regulator_bulk_data supply_data[SI4713_NUM_SUPPLIES];
- int gpio_reset;
+ struct regulator *vdd;
+ struct regulator *vio;
+ struct gpio_desc *gpio_reset;
+ struct platform_device *pd;
u32 power_state;
u32 rds_enabled;
u32 frequency;
@@ -246,4 +247,8 @@
u32 stereo;
u32 tune_rnl;
};
+
+struct radio_si4713_platform_data {
+ struct i2c_client *subdev;
+};
#endif /* ifndef SI4713_I2C_H */
diff --git a/drivers/media/radio/wl128x/fmdrv_common.c b/drivers/media/radio/wl128x/fmdrv_common.c
index 6f28f6e..704397f 100644
--- a/drivers/media/radio/wl128x/fmdrv_common.c
+++ b/drivers/media/radio/wl128x/fmdrv_common.c
@@ -1256,7 +1256,7 @@
fmerr("Unable to read firmware(%s) content\n", fw_name);
return ret;
}
- fmdbg("Firmware(%s) length : %d bytes\n", fw_name, fw_entry->size);
+ fmdbg("Firmware(%s) length : %zu bytes\n", fw_name, fw_entry->size);
fw_data = (void *)fw_entry->data;
fw_len = fw_entry->size;
diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.c b/drivers/media/radio/wl128x/fmdrv_v4l2.c
index b55012c..a5bd3f6 100644
--- a/drivers/media/radio/wl128x/fmdrv_v4l2.c
+++ b/drivers/media/radio/wl128x/fmdrv_v4l2.c
@@ -198,10 +198,12 @@
strlcpy(capability->card, FM_DRV_CARD_SHORT_NAME,
sizeof(capability->card));
sprintf(capability->bus_info, "UART");
- capability->capabilities = V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_TUNER |
+ capability->device_caps = V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_TUNER |
V4L2_CAP_RADIO | V4L2_CAP_MODULATOR |
V4L2_CAP_AUDIO | V4L2_CAP_READWRITE |
V4L2_CAP_RDS_CAPTURE;
+ capability->capabilities = capability->device_caps |
+ V4L2_CAP_DEVICE_CAPS;
return 0;
}
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
index a4c907d..99c880f 100644
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -179,7 +179,7 @@
module will be called ene_ir.
config IR_HIX5HD2
- depends on !BACKPORT_KERNEL_3_7
+ depends on !KERNEL_3_7
depends on CONFIG_MFD_SYSCON
tristate "Hisilicon hix5hd2 IR remote control"
depends on m
@@ -244,6 +244,18 @@
To compile this driver as a module, choose M here: the
module will be called fintek-cir.
+config IR_MESON
+ tristate "Amlogic Meson IR remote receiver"
+ depends on m
+ depends on RC_CORE
+ depends on ARCH_MESON || COMPILE_TEST
+ ---help---
+ Say Y if you want to use the IR remote receiver available
+ on Amlogic Meson SoCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called meson-ir.
+
config IR_NUVOTON
tristate "Nuvoton w836x7hg Consumer Infrared Transceiver"
depends on m
@@ -263,8 +275,8 @@
depends on m
depends on USB_ARCH_HAS_HCD
depends on RC_CORE
- select BACKPORT_NEW_LEDS
- select BACKPORT_LEDS_CLASS
+ select BPAUTO_NEW_LEDS
+ select BPAUTO_LEDS_CLASS
depends on USB
---help---
Say Y here if you want to use a RedRat3 Infrared Transceiver.
@@ -290,8 +302,8 @@
depends on m
depends on X86 && PNP
depends on RC_CORE
- select BACKPORT_NEW_LEDS
- select BACKPORT_LEDS_CLASS
+ select BPAUTO_NEW_LEDS
+ select BPAUTO_LEDS_CLASS
depends on BITREVERSE
---help---
Say Y here if you want to use the IR remote functionality found
@@ -302,6 +314,22 @@
To compile this driver as a module, choose M here: the module will
be called winbond_cir.
+config IR_IGORPLUGUSB
+ tristate "IgorPlug-USB IR Receiver"
+ depends on m
+ depends on USB_ARCH_HAS_HCD
+ depends on RC_CORE
+ depends on USB
+ ---help---
+ Say Y here if you want to use the IgorPlug-USB IR Receiver by
+ Igor Cesko. This device is included on the Fit-PC2.
+
+ Note that this device can only record bursts of 36 IR pulses and
+ spaces, which is not enough for the NEC, Sanyo and RC-6 protocol.
+
+ To compile this driver as a module, choose M here: the module will
+ be called igorplugusb.
+
config IR_IGUANA
tristate "IguanaWorks USB IR Transceiver"
depends on m
@@ -325,8 +353,8 @@
depends on USB_ARCH_HAS_HCD
depends on RC_CORE
depends on USB
- select BACKPORT_NEW_LEDS
- select BACKPORT_LEDS_CLASS
+ select BPAUTO_NEW_LEDS
+ select BPAUTO_LEDS_CLASS
---help---
Say Y here if you want to use the TechnoTrend USB IR Receiver. The
driver can control the led.
diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
index 9bba1fd..0652848 100644
--- a/drivers/media/rc/Makefile
+++ b/drivers/media/rc/Makefile
@@ -22,6 +22,7 @@
obj-$(CPTCFG_IR_ITE_CIR) += ite-cir.o
obj-$(CPTCFG_IR_MCEUSB) += mceusb.o
obj-$(CPTCFG_IR_FINTEK) += fintek-cir.o
+obj-$(CPTCFG_IR_MESON) += meson-ir.o
obj-$(CPTCFG_IR_NUVOTON) += nuvoton-cir.o
obj-$(CPTCFG_IR_ENE) += ene_ir.o
obj-$(CPTCFG_IR_REDRAT3) += redrat3.o
@@ -30,6 +31,7 @@
obj-$(CPTCFG_IR_WINBOND_CIR) += winbond-cir.o
obj-$(CPTCFG_RC_LOOPBACK) += rc-loopback.o
obj-$(CPTCFG_IR_GPIO_CIR) += gpio-ir-recv.o
+obj-$(CPTCFG_IR_IGORPLUGUSB) += igorplugusb.o
obj-$(CPTCFG_IR_IGUANA) += iguanair.o
obj-$(CPTCFG_IR_TTUSBIR) += ttusbir.o
obj-$(CPTCFG_RC_ST) += st_rc.o
diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c
index 5985308..229853d 100644
--- a/drivers/media/rc/gpio-ir-recv.c
+++ b/drivers/media/rc/gpio-ir-recv.c
@@ -240,7 +240,6 @@
.remove = gpio_ir_recv_remove,
.driver = {
.name = GPIO_IR_DRIVER_NAME,
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(gpio_ir_recv_of_match),
#ifdef CONFIG_PM
.pm = &gpio_ir_recv_pm_ops,
diff --git a/drivers/media/rc/igorplugusb.c b/drivers/media/rc/igorplugusb.c
new file mode 100644
index 0000000..b36e515
--- /dev/null
+++ b/drivers/media/rc/igorplugusb.c
@@ -0,0 +1,261 @@
+/*
+ * IgorPlug-USB IR Receiver
+ *
+ * Copyright (C) 2014 Sean Young <sean@mess.org>
+ *
+ * Supports the standard homebrew IgorPlugUSB receiver with Igor's firmware.
+ * See http://www.cesko.host.sk/IgorPlugUSB/IgorPlug-USB%20(AVR)_eng.htm
+ *
+ * Based on the lirc_igorplugusb.c driver:
+ * Copyright (C) 2004 Jan M. Hochstein
+ * <hochstein@algo.informatik.tu-darmstadt.de>
+ *
+ * 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.
+ */
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <media/rc-core.h>
+
+#define DRIVER_DESC "IgorPlug-USB IR Receiver"
+#define DRIVER_NAME "igorplugusb"
+
+#define HEADERLEN 3
+#define BUFLEN 36
+#define MAX_PACKET (HEADERLEN + BUFLEN)
+
+#define SET_INFRABUFFER_EMPTY 1
+#define GET_INFRACODE 2
+
+
+struct igorplugusb {
+ struct rc_dev *rc;
+ struct device *dev;
+
+ struct urb *urb;
+ struct usb_ctrlrequest request;
+
+ struct timer_list timer;
+
+ uint8_t buf_in[MAX_PACKET];
+
+ char phys[64];
+};
+
+static void igorplugusb_cmd(struct igorplugusb *ir, int cmd);
+
+static void igorplugusb_irdata(struct igorplugusb *ir, unsigned len)
+{
+ DEFINE_IR_RAW_EVENT(rawir);
+ unsigned i, start, overflow;
+
+ dev_dbg(ir->dev, "irdata: %*ph (len=%u)", len, ir->buf_in, len);
+
+ /*
+ * If more than 36 pulses and spaces follow each other, the igorplugusb
+ * overwrites its buffer from the beginning. The overflow value is the
+ * last offset which was not overwritten. Everything from this offset
+ * onwards occurred before everything until this offset.
+ */
+ overflow = ir->buf_in[2];
+ i = start = overflow + HEADERLEN;
+
+ if (start >= len) {
+ dev_err(ir->dev, "receive overflow invalid: %u", overflow);
+ } else {
+ if (overflow > 0)
+ dev_warn(ir->dev, "receive overflow, at least %u lost",
+ overflow);
+
+ do {
+ rawir.duration = ir->buf_in[i] * 85333;
+ rawir.pulse = i & 1;
+
+ ir_raw_event_store_with_filter(ir->rc, &rawir);
+
+ if (++i == len)
+ i = HEADERLEN;
+ } while (i != start);
+
+ /* add a trailing space */
+ rawir.duration = ir->rc->timeout;
+ rawir.pulse = false;
+ ir_raw_event_store_with_filter(ir->rc, &rawir);
+
+ ir_raw_event_handle(ir->rc);
+ }
+
+ igorplugusb_cmd(ir, SET_INFRABUFFER_EMPTY);
+}
+
+static void igorplugusb_callback(struct urb *urb)
+{
+ struct usb_ctrlrequest *req;
+ struct igorplugusb *ir = urb->context;
+
+ req = (struct usb_ctrlrequest *)urb->setup_packet;
+
+ switch (urb->status) {
+ case 0:
+ if (req->bRequest == GET_INFRACODE &&
+ urb->actual_length > HEADERLEN)
+ igorplugusb_irdata(ir, urb->actual_length);
+ else /* request IR */
+ mod_timer(&ir->timer, jiffies + msecs_to_jiffies(50));
+ break;
+ case -EPROTO:
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ usb_unlink_urb(urb);
+ return;
+ default:
+ dev_warn(ir->dev, "Error: urb status = %d\n", urb->status);
+ igorplugusb_cmd(ir, SET_INFRABUFFER_EMPTY);
+ break;
+ }
+}
+
+static void igorplugusb_cmd(struct igorplugusb *ir, int cmd)
+{
+ int ret;
+
+ ir->request.bRequest = cmd;
+ ir->urb->transfer_flags = 0;
+ ret = usb_submit_urb(ir->urb, GFP_ATOMIC);
+ if (ret)
+ dev_err(ir->dev, "submit urb failed: %d", ret);
+}
+
+static void igorplugusb_timer(unsigned long data)
+{
+ struct igorplugusb *ir = (struct igorplugusb *)data;
+
+ igorplugusb_cmd(ir, GET_INFRACODE);
+}
+
+static int igorplugusb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev;
+ struct usb_host_interface *idesc;
+ struct usb_endpoint_descriptor *ep;
+ struct igorplugusb *ir;
+ struct rc_dev *rc;
+ int ret;
+
+ udev = interface_to_usbdev(intf);
+ idesc = intf->cur_altsetting;
+
+ if (idesc->desc.bNumEndpoints != 1) {
+ dev_err(&intf->dev, "incorrect number of endpoints");
+ return -ENODEV;
+ }
+
+ ep = &idesc->endpoint[0].desc;
+ if (!usb_endpoint_dir_in(ep) || !usb_endpoint_xfer_control(ep)) {
+ dev_err(&intf->dev, "endpoint incorrect");
+ return -ENODEV;
+ }
+
+ ir = devm_kzalloc(&intf->dev, sizeof(*ir), GFP_KERNEL);
+ if (!ir)
+ return -ENOMEM;
+
+ ir->dev = &intf->dev;
+
+ setup_timer(&ir->timer, igorplugusb_timer, (unsigned long)ir);
+
+ ir->request.bRequest = GET_INFRACODE;
+ ir->request.bRequestType = USB_TYPE_VENDOR | USB_DIR_IN;
+ ir->request.wLength = cpu_to_le16(sizeof(ir->buf_in));
+
+ ir->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!ir->urb)
+ return -ENOMEM;
+
+ usb_fill_control_urb(ir->urb, udev,
+ usb_rcvctrlpipe(udev, 0), (uint8_t *)&ir->request,
+ ir->buf_in, sizeof(ir->buf_in), igorplugusb_callback, ir);
+
+ usb_make_path(udev, ir->phys, sizeof(ir->phys));
+
+ rc = rc_allocate_device();
+ rc->input_name = DRIVER_DESC;
+ rc->input_phys = ir->phys;
+ usb_to_input_id(udev, &rc->input_id);
+ rc->dev.parent = &intf->dev;
+ rc->driver_type = RC_DRIVER_IR_RAW;
+ /*
+ * This device can only store 36 pulses + spaces, which is not enough
+ * for the NEC protocol and many others.
+ */
+ rc->allowed_protocols = RC_BIT_ALL & ~(RC_BIT_NEC | RC_BIT_RC6_6A_20 |
+ RC_BIT_RC6_6A_24 | RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE |
+ RC_BIT_SONY20 | RC_BIT_MCE_KBD | RC_BIT_SANYO);
+
+ rc->priv = ir;
+ rc->driver_name = DRIVER_NAME;
+ rc->map_name = RC_MAP_HAUPPAUGE;
+ rc->timeout = MS_TO_NS(100);
+ rc->rx_resolution = 85333;
+
+ ir->rc = rc;
+ ret = rc_register_device(rc);
+ if (ret) {
+ dev_err(&intf->dev, "failed to register rc device: %d", ret);
+ rc_free_device(rc);
+ usb_free_urb(ir->urb);
+ return ret;
+ }
+
+ usb_set_intfdata(intf, ir);
+
+ igorplugusb_cmd(ir, SET_INFRABUFFER_EMPTY);
+
+ return 0;
+}
+
+static void igorplugusb_disconnect(struct usb_interface *intf)
+{
+ struct igorplugusb *ir = usb_get_intfdata(intf);
+
+ rc_unregister_device(ir->rc);
+ del_timer_sync(&ir->timer);
+ usb_set_intfdata(intf, NULL);
+ usb_kill_urb(ir->urb);
+ usb_free_urb(ir->urb);
+}
+
+static struct usb_device_id igorplugusb_table[] = {
+ /* Igor Plug USB (Atmel's Manufact. ID) */
+ { USB_DEVICE(0x03eb, 0x0002) },
+ /* Fit PC2 Infrared Adapter */
+ { USB_DEVICE(0x03eb, 0x21fe) },
+ /* Terminating entry */
+ { }
+};
+
+static struct usb_driver igorplugusb_driver = {
+ .name = DRIVER_NAME,
+ .probe = igorplugusb_probe,
+ .disconnect = igorplugusb_disconnect,
+ .id_table = igorplugusb_table
+};
+
+module_usb_driver(igorplugusb_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Sean Young <sean@mess.org>");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, igorplugusb_table);
diff --git a/drivers/media/rc/img-ir/Kconfig b/drivers/media/rc/img-ir/Kconfig
index 2e5bf62..74ba6bb 100644
--- a/drivers/media/rc/img-ir/Kconfig
+++ b/drivers/media/rc/img-ir/Kconfig
@@ -1,8 +1,9 @@
config IR_IMG
- depends on !BACKPORT_KERNEL_3_5
+ depends on !KERNEL_3_5
tristate "ImgTec IR Decoder"
depends on m
depends on RC_CORE
+ depends on METAG || MIPS || COMPILE_TEST
select IR_IMG_HW if !IR_IMG_RAW
help
Say Y or M here if you want to use the ImgTec infrared decoder
diff --git a/drivers/media/rc/img-ir/img-ir-core.c b/drivers/media/rc/img-ir/img-ir-core.c
index a0cac2f..77c78de 100644
--- a/drivers/media/rc/img-ir/img-ir-core.c
+++ b/drivers/media/rc/img-ir/img-ir-core.c
@@ -166,7 +166,6 @@
static struct platform_driver img_ir_driver = {
.driver = {
.name = "img-ir",
- .owner = THIS_MODULE,
.of_match_table = img_ir_match,
.pm = &img_ir_pmops,
},
diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c
index 2ccc3bd..44e3c51 100644
--- a/drivers/media/rc/img-ir/img-ir-hw.c
+++ b/drivers/media/rc/img-ir/img-ir-hw.c
@@ -530,6 +530,22 @@
u32 ir_status, irq_en;
spin_lock_irq(&priv->lock);
+ /*
+ * First record that the protocol is being stopped so that the end timer
+ * isn't restarted while we're trying to stop it.
+ */
+ hw->stopping = true;
+
+ /*
+ * Release the lock to stop the end timer, since the end timer handler
+ * acquires the lock and we don't want to deadlock waiting for it.
+ */
+ spin_unlock_irq(&priv->lock);
+ del_timer_sync(&hw->end_timer);
+ spin_lock_irq(&priv->lock);
+
+ hw->stopping = false;
+
/* switch off and disable interrupts */
img_ir_write(priv, IMG_IR_CONTROL, 0);
irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE);
@@ -541,12 +557,13 @@
if (ir_status & (IMG_IR_RXDVAL | IMG_IR_RXDVALD2)) {
ir_status &= ~(IMG_IR_RXDVAL | IMG_IR_RXDVALD2);
img_ir_write(priv, IMG_IR_STATUS, ir_status);
- img_ir_read(priv, IMG_IR_DATA_LW);
- img_ir_read(priv, IMG_IR_DATA_UP);
}
- /* stop the end timer and switch back to normal mode */
- del_timer_sync(&hw->end_timer);
+ /* always read data to clear buffer if IR wakes the device */
+ img_ir_read(priv, IMG_IR_DATA_LW);
+ img_ir_read(priv, IMG_IR_DATA_UP);
+
+ /* switch back to normal mode */
hw->mode = IMG_IR_M_NORMAL;
/* clear the wakeup scancode filter */
@@ -817,7 +834,8 @@
}
- if (dec->repeat) {
+ /* we mustn't update the end timer while trying to stop it */
+ if (dec->repeat && !hw->stopping) {
unsigned long interval;
img_ir_begin_repeat(priv);
diff --git a/drivers/media/rc/img-ir/img-ir-hw.h b/drivers/media/rc/img-ir/img-ir-hw.h
index 9119bd1..463bddf 100644
--- a/drivers/media/rc/img-ir/img-ir-hw.h
+++ b/drivers/media/rc/img-ir/img-ir-hw.h
@@ -186,9 +186,6 @@
struct img_ir_timing_regvals rtimings;
};
-int img_ir_register_decoder(struct img_ir_decoder *dec);
-void img_ir_unregister_decoder(struct img_ir_decoder *dec);
-
struct img_ir_priv;
#ifdef CPTCFG_IR_IMG_HW
@@ -214,6 +211,8 @@
* @flags: IMG_IR_F_*.
* @filters: HW filters (derived from scancode filters).
* @mode: Current decode mode.
+ * @stopping: Indicates that decoder is being taken down and timers
+ * should not be restarted.
* @suspend_irqen: Saved IRQ enable mask over suspend.
*/
struct img_ir_priv_hw {
@@ -229,6 +228,7 @@
struct img_ir_filter filters[RC_FILTER_MAX];
enum img_ir_mode mode;
+ bool stopping;
u32 suspend_irqen;
};
diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c
index b8837dd..65f80b8 100644
--- a/drivers/media/rc/imon.c
+++ b/drivers/media/rc/imon.c
@@ -1678,7 +1678,8 @@
if (press_type == 0)
rc_keyup(ictx->rdev);
else {
- if (ictx->rc_type == RC_BIT_RC6_MCE)
+ if (ictx->rc_type == RC_BIT_RC6_MCE ||
+ ictx->rc_type == RC_BIT_OTHER)
rc_keydown(ictx->rdev,
ictx->rc_type == RC_BIT_RC6_MCE ? RC_TYPE_RC6_MCE : RC_TYPE_OTHER,
ictx->rc_scancode, ictx->rc_toggle);
diff --git a/drivers/media/rc/ir-hix5hd2.c b/drivers/media/rc/ir-hix5hd2.c
index b6bcf2b..3a3192c 100644
--- a/drivers/media/rc/ir-hix5hd2.c
+++ b/drivers/media/rc/ir-hix5hd2.c
@@ -298,7 +298,7 @@
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int hix5hd2_ir_suspend(struct device *dev)
{
struct hix5hd2_ir_priv *priv = dev_get_drvdata(dev);
diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c
index ed2c8a1..98893a8 100644
--- a/drivers/media/rc/ir-lirc-codec.c
+++ b/drivers/media/rc/ir-lirc-codec.c
@@ -42,11 +42,17 @@
return -EINVAL;
/* Packet start */
- if (ev.reset)
- return 0;
+ if (ev.reset) {
+ /* Userspace expects a long space event before the start of
+ * the signal to use as a sync. This may be done with repeat
+ * packets and normal samples. But if a reset has been sent
+ * then we assume that a long time has passed, so we send a
+ * space with the maximum time value. */
+ sample = LIRC_SPACE(LIRC_VALUE_MASK);
+ IR_dprintk(2, "delivering reset sync space to lirc_dev\n");
/* Carrier reports */
- if (ev.carrier_report) {
+ } else if (ev.carrier_report) {
sample = LIRC_FREQUENCY(ev.carrier);
IR_dprintk(2, "carrier report (freq: %d)\n", sample);
diff --git a/drivers/media/rc/ir-rc5-decoder.c b/drivers/media/rc/ir-rc5-decoder.c
index 2ef7639..84fa6e9 100644
--- a/drivers/media/rc/ir-rc5-decoder.c
+++ b/drivers/media/rc/ir-rc5-decoder.c
@@ -53,7 +53,7 @@
u32 scancode;
enum rc_type protocol;
- if (!(dev->enabled_protocols & (RC_BIT_RC5 | RC_BIT_RC5X)))
+ if (!(dev->enabled_protocols & (RC_BIT_RC5 | RC_BIT_RC5X | RC_BIT_RC5_SZ)))
return 0;
if (!is_timing_event(ev)) {
diff --git a/drivers/media/rc/ir-rc6-decoder.c b/drivers/media/rc/ir-rc6-decoder.c
index f1f098e..d16bc67 100644
--- a/drivers/media/rc/ir-rc6-decoder.c
+++ b/drivers/media/rc/ir-rc6-decoder.c
@@ -259,8 +259,8 @@
case 32:
if ((scancode & RC6_6A_LCC_MASK) == RC6_6A_MCE_CC) {
protocol = RC_TYPE_RC6_MCE;
- scancode &= ~RC6_6A_MCE_TOGGLE_MASK;
toggle = !!(scancode & RC6_6A_MCE_TOGGLE_MASK);
+ scancode &= ~RC6_6A_MCE_TOGGLE_MASK;
} else {
protocol = RC_BIT_RC6_6A_32;
toggle = 0;
diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c
index 249d2fb..1e0545a 100644
--- a/drivers/media/rc/lirc_dev.c
+++ b/drivers/media/rc/lirc_dev.c
@@ -518,8 +518,7 @@
WARN_ON(mutex_lock_killable(&lirc_dev_lock));
- if (ir->d.rdev)
- rc_close(ir->d.rdev);
+ rc_close(ir->d.rdev);
ir->open--;
if (ir->attached) {
diff --git a/drivers/media/rc/meson-ir.c b/drivers/media/rc/meson-ir.c
new file mode 100644
index 0000000..fcc3b82
--- /dev/null
+++ b/drivers/media/rc/meson-ir.c
@@ -0,0 +1,216 @@
+/*
+ * Driver for Amlogic Meson IR remote receiver
+ *
+ * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+#include <media/rc-core.h>
+
+#define DRIVER_NAME "meson-ir"
+
+#define IR_DEC_LDR_ACTIVE 0x00
+#define IR_DEC_LDR_IDLE 0x04
+#define IR_DEC_LDR_REPEAT 0x08
+#define IR_DEC_BIT_0 0x0c
+#define IR_DEC_REG0 0x10
+#define IR_DEC_FRAME 0x14
+#define IR_DEC_STATUS 0x18
+#define IR_DEC_REG1 0x1c
+
+#define REG0_RATE_MASK (BIT(11) - 1)
+
+#define REG1_MODE_MASK (BIT(7) | BIT(8))
+#define REG1_MODE_NEC (0 << 7)
+#define REG1_MODE_GENERAL (2 << 7)
+
+#define REG1_TIME_IV_SHIFT 16
+#define REG1_TIME_IV_MASK ((BIT(13) - 1) << REG1_TIME_IV_SHIFT)
+
+#define REG1_IRQSEL_MASK (BIT(2) | BIT(3))
+#define REG1_IRQSEL_NEC_MODE (0 << 2)
+#define REG1_IRQSEL_RISE_FALL (1 << 2)
+#define REG1_IRQSEL_FALL (2 << 2)
+#define REG1_IRQSEL_RISE (3 << 2)
+
+#define REG1_RESET BIT(0)
+#define REG1_ENABLE BIT(15)
+
+#define STATUS_IR_DEC_IN BIT(8)
+
+#define MESON_TRATE 10 /* us */
+
+struct meson_ir {
+ void __iomem *reg;
+ struct rc_dev *rc;
+ int irq;
+ spinlock_t lock;
+};
+
+static void meson_ir_set_mask(struct meson_ir *ir, unsigned int reg,
+ u32 mask, u32 value)
+{
+ u32 data;
+
+ data = readl(ir->reg + reg);
+ data &= ~mask;
+ data |= (value & mask);
+ writel(data, ir->reg + reg);
+}
+
+static irqreturn_t meson_ir_irq(int irqno, void *dev_id)
+{
+ struct meson_ir *ir = dev_id;
+ u32 duration;
+ DEFINE_IR_RAW_EVENT(rawir);
+
+ spin_lock(&ir->lock);
+
+ duration = readl(ir->reg + IR_DEC_REG1);
+ duration = (duration & REG1_TIME_IV_MASK) >> REG1_TIME_IV_SHIFT;
+ rawir.duration = US_TO_NS(duration * MESON_TRATE);
+
+ rawir.pulse = !!(readl(ir->reg + IR_DEC_STATUS) & STATUS_IR_DEC_IN);
+
+ ir_raw_event_store_with_filter(ir->rc, &rawir);
+ ir_raw_event_handle(ir->rc);
+
+ spin_unlock(&ir->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int meson_ir_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+ struct resource *res;
+ const char *map_name;
+ struct meson_ir *ir;
+ int ret;
+
+ ir = devm_kzalloc(dev, sizeof(struct meson_ir), GFP_KERNEL);
+ if (!ir)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ir->reg = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ir->reg)) {
+ dev_err(dev, "failed to map registers\n");
+ return PTR_ERR(ir->reg);
+ }
+
+ ir->irq = platform_get_irq(pdev, 0);
+ if (ir->irq < 0) {
+ dev_err(dev, "no irq resource\n");
+ return ir->irq;
+ }
+
+ ir->rc = rc_allocate_device();
+ if (!ir->rc) {
+ dev_err(dev, "failed to allocate rc device\n");
+ return -ENOMEM;
+ }
+
+ ir->rc->priv = ir;
+ ir->rc->input_name = DRIVER_NAME;
+ ir->rc->input_phys = DRIVER_NAME "/input0";
+ ir->rc->input_id.bustype = BUS_HOST;
+ map_name = of_get_property(node, "linux,rc-map-name", NULL);
+ ir->rc->map_name = map_name ? map_name : RC_MAP_EMPTY;
+ ir->rc->dev.parent = dev;
+ ir->rc->driver_type = RC_DRIVER_IR_RAW;
+ ir->rc->allowed_protocols = RC_BIT_ALL;
+ ir->rc->rx_resolution = US_TO_NS(MESON_TRATE);
+ ir->rc->timeout = MS_TO_NS(200);
+ ir->rc->driver_name = DRIVER_NAME;
+
+ spin_lock_init(&ir->lock);
+ platform_set_drvdata(pdev, ir);
+
+ ret = rc_register_device(ir->rc);
+ if (ret) {
+ dev_err(dev, "failed to register rc device\n");
+ goto out_free;
+ }
+
+ ret = devm_request_irq(dev, ir->irq, meson_ir_irq, 0, "ir-meson", ir);
+ if (ret) {
+ dev_err(dev, "failed to request irq\n");
+ goto out_unreg;
+ }
+
+ /* Reset the decoder */
+ meson_ir_set_mask(ir, IR_DEC_REG1, REG1_RESET, REG1_RESET);
+ meson_ir_set_mask(ir, IR_DEC_REG1, REG1_RESET, 0);
+ /* Set general operation mode */
+ meson_ir_set_mask(ir, IR_DEC_REG1, REG1_MODE_MASK, REG1_MODE_GENERAL);
+ /* Set rate */
+ meson_ir_set_mask(ir, IR_DEC_REG0, REG0_RATE_MASK, MESON_TRATE - 1);
+ /* IRQ on rising and falling edges */
+ meson_ir_set_mask(ir, IR_DEC_REG1, REG1_IRQSEL_MASK,
+ REG1_IRQSEL_RISE_FALL);
+ /* Enable the decoder */
+ meson_ir_set_mask(ir, IR_DEC_REG1, REG1_ENABLE, REG1_ENABLE);
+
+ dev_info(dev, "receiver initialized\n");
+
+ return 0;
+out_unreg:
+ rc_unregister_device(ir->rc);
+ ir->rc = NULL;
+out_free:
+ rc_free_device(ir->rc);
+
+ return ret;
+}
+
+static int meson_ir_remove(struct platform_device *pdev)
+{
+ struct meson_ir *ir = platform_get_drvdata(pdev);
+ unsigned long flags;
+
+ /* Disable the decoder */
+ spin_lock_irqsave(&ir->lock, flags);
+ meson_ir_set_mask(ir, IR_DEC_REG1, REG1_ENABLE, 0);
+ spin_unlock_irqrestore(&ir->lock, flags);
+
+ rc_unregister_device(ir->rc);
+
+ return 0;
+}
+
+static const struct of_device_id meson_ir_match[] = {
+ { .compatible = "amlogic,meson6-ir" },
+ { },
+};
+
+static struct platform_driver meson_ir_driver = {
+ .probe = meson_ir_probe,
+ .remove = meson_ir_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = meson_ir_match,
+ },
+};
+
+module_platform_driver(meson_ir_driver);
+
+MODULE_DESCRIPTION("Amlogic Meson IR remote receiver driver");
+MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c
index e8fff2a..b732ac6 100644
--- a/drivers/media/rc/rc-ir-raw.c
+++ b/drivers/media/rc/rc-ir-raw.c
@@ -262,7 +262,6 @@
return -ENOMEM;
dev->raw->dev = dev;
- dev->enabled_protocols = ~0;
dev->change_protocol = change_protocol;
rc = kfifo_alloc(&dev->raw->kfifo,
sizeof(struct ir_raw_event) * MAX_IR_EVENT_SIZE,
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
index b59eee2..46ee99c 100644
--- a/drivers/media/rc/rc-main.c
+++ b/drivers/media/rc/rc-main.c
@@ -1302,8 +1302,7 @@
if (!dev)
return;
- if (dev->input_dev)
- input_free_device(dev->input_dev);
+ input_free_device(dev->input_dev);
put_device(&dev->dev);
@@ -1418,13 +1417,18 @@
ir_raw_init();
raw_init = true;
}
+ /* calls ir_register_device so unlock mutex here*/
+ mutex_unlock(&dev->lock);
rc = ir_raw_event_register(dev);
+ mutex_lock(&dev->lock);
if (rc < 0)
goto out_input;
}
if (dev->change_protocol) {
- u64 rc_type = (1 << rc_map->rc_type);
+ u64 rc_type = (1ll << rc_map->rc_type);
+ if (dev->driver_type == RC_DRIVER_IR_RAW)
+ rc_type |= RC_BIT_LIRC;
rc = dev->change_protocol(dev, &rc_type);
if (rc < 0)
goto out_raw;
diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c
index 795b394..c4def66 100644
--- a/drivers/media/rc/redrat3.c
+++ b/drivers/media/rc/redrat3.c
@@ -966,7 +966,7 @@
rr3->ep_in = ep_in;
rr3->bulk_in_buf = usb_alloc_coherent(udev,
- le16_to_cpu(ep_in->wMaxPacketSize), GFP_ATOMIC, &rr3->dma_in);
+ le16_to_cpu(ep_in->wMaxPacketSize), GFP_KERNEL, &rr3->dma_in);
if (!rr3->bulk_in_buf) {
dev_err(dev, "Read buffer allocation failure\n");
goto error;
@@ -975,6 +975,8 @@
pipe = usb_rcvbulkpipe(udev, ep_in->bEndpointAddress);
usb_fill_bulk_urb(rr3->read_urb, udev, pipe, rr3->bulk_in_buf,
le16_to_cpu(ep_in->wMaxPacketSize), redrat3_handle_async, rr3);
+ rr3->read_urb->transfer_dma = rr3->dma_in;
+ rr3->read_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
rr3->ep_out = ep_out;
rr3->udev = udev;
diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c
index bcee8e1..340f7f5 100644
--- a/drivers/media/rc/sunxi-cir.c
+++ b/drivers/media/rc/sunxi-cir.c
@@ -306,7 +306,6 @@
.remove = sunxi_ir_remove,
.driver = {
.name = SUNXI_IR_DEV,
- .owner = THIS_MODULE,
.of_match_table = sunxi_ir_match,
},
};
diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig
index 02f497c..86602cc 100644
--- a/drivers/media/tuners/Kconfig
+++ b/drivers/media/tuners/Kconfig
@@ -228,7 +228,7 @@
Fitipower FC0013 silicon tuner driver.
config MEDIA_TUNER_TDA18212
- depends on !BACKPORT_KERNEL_3_4
+ depends on !KERNEL_3_4
tristate "NXP TDA18212 silicon tuner"
depends on m
depends on MEDIA_SUPPORT && I2C
@@ -238,8 +238,8 @@
NXP TDA18212 silicon tuner driver.
config MEDIA_TUNER_E4000
- depends on !BACKPORT_KERNEL_3_4
- depends on !BACKPORT_KERNEL_3_4
+ depends on !KERNEL_3_4
+ depends on !KERNEL_3_4
tristate "Elonics E4000 silicon tuner"
depends on m
depends on MEDIA_SUPPORT && I2C
@@ -257,7 +257,7 @@
FCI FC2580 silicon tuner driver.
config MEDIA_TUNER_M88TS2022
- depends on !BACKPORT_KERNEL_3_4
+ depends on !KERNEL_3_4
tristate "Montage M88TS2022 silicon tuner"
depends on m
depends on MEDIA_SUPPORT && I2C
@@ -266,6 +266,16 @@
help
Montage M88TS2022 silicon tuner driver.
+config MEDIA_TUNER_M88RS6000T
+ depends on !KERNEL_3_3
+ tristate "Montage M88RS6000 internal tuner"
+ depends on m
+ depends on MEDIA_SUPPORT && I2C
+ depends on REGMAP_I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Montage M88RS6000 internal tuner.
+
config MEDIA_TUNER_TUA9001
tristate "Infineon TUA 9001 silicon tuner"
depends on m
@@ -283,7 +293,7 @@
Silicon Labs Si2157 silicon tuner driver.
config MEDIA_TUNER_IT913X
- depends on !BACKPORT_KERNEL_3_4
+ depends on !KERNEL_3_4
tristate "ITE Tech IT913x silicon tuner"
depends on m
depends on MEDIA_SUPPORT && I2C
diff --git a/drivers/media/tuners/Makefile b/drivers/media/tuners/Makefile
index c3e58a6..bc7b685 100644
--- a/drivers/media/tuners/Makefile
+++ b/drivers/media/tuners/Makefile
@@ -41,6 +41,7 @@
obj-$(CPTCFG_MEDIA_TUNER_R820T) += r820t.o
obj-$(CPTCFG_MEDIA_TUNER_MXL301RF) += mxl301rf.o
obj-$(CPTCFG_MEDIA_TUNER_QM1D1C0042) += qm1d1c0042.o
+obj-$(CPTCFG_MEDIA_TUNER_M88RS6000T) += m88rs6000t.o
ccflags-y += -I$(backport_srctree)/drivers/media/dvb-core
ccflags-y += -I$(backport_srctree)/drivers/media/dvb-frontends
diff --git a/drivers/media/tuners/m88rs6000t.c b/drivers/media/tuners/m88rs6000t.c
new file mode 100644
index 0000000..d4c13fe
--- /dev/null
+++ b/drivers/media/tuners/m88rs6000t.c
@@ -0,0 +1,744 @@
+/*
+ * Driver for the internal tuner of Montage M88RS6000
+ *
+ * Copyright (C) 2014 Max nibble <nibble.max@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "m88rs6000t.h"
+#include <linux/regmap.h>
+
+struct m88rs6000t_dev {
+ struct m88rs6000t_config cfg;
+ struct i2c_client *client;
+ struct regmap *regmap;
+ u32 frequency_khz;
+};
+
+struct m88rs6000t_reg_val {
+ u8 reg;
+ u8 val;
+};
+
+/* set demod main mclk and ts mclk */
+static int m88rs6000t_set_demod_mclk(struct dvb_frontend *fe)
+{
+ struct m88rs6000t_dev *dev = fe->tuner_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u8 reg11, reg15, reg16, reg1D, reg1E, reg1F;
+ u8 N, f0 = 0, f1 = 0, f2 = 0, f3 = 0;
+ u16 pll_div_fb;
+ u32 div, ts_mclk;
+ unsigned int utmp;
+ int ret;
+
+ /* select demod main mclk */
+ ret = regmap_read(dev->regmap, 0x15, &utmp);
+ if (ret)
+ goto err;
+ reg15 = utmp;
+ if (c->symbol_rate > 45010000) {
+ reg11 = 0x0E;
+ reg15 |= 0x02;
+ reg16 = 115; /* mclk = 110.25MHz */
+ } else {
+ reg11 = 0x0A;
+ reg15 &= ~0x02;
+ reg16 = 96; /* mclk = 96MHz */
+ }
+
+ /* set ts mclk */
+ if (c->delivery_system == SYS_DVBS)
+ ts_mclk = 96000;
+ else
+ ts_mclk = 144000;
+
+ pll_div_fb = (reg15 & 0x01) << 8;
+ pll_div_fb += reg16;
+ pll_div_fb += 32;
+
+ div = 36000 * pll_div_fb;
+ div /= ts_mclk;
+
+ if (div <= 32) {
+ N = 2;
+ f0 = 0;
+ f1 = div / 2;
+ f2 = div - f1;
+ f3 = 0;
+ } else if (div <= 48) {
+ N = 3;
+ f0 = div / 3;
+ f1 = (div - f0) / 2;
+ f2 = div - f0 - f1;
+ f3 = 0;
+ } else if (div <= 64) {
+ N = 4;
+ f0 = div / 4;
+ f1 = (div - f0) / 3;
+ f2 = (div - f0 - f1) / 2;
+ f3 = div - f0 - f1 - f2;
+ } else {
+ N = 4;
+ f0 = 16;
+ f1 = 16;
+ f2 = 16;
+ f3 = 16;
+ }
+
+ if (f0 == 16)
+ f0 = 0;
+ if (f1 == 16)
+ f1 = 0;
+ if (f2 == 16)
+ f2 = 0;
+ if (f3 == 16)
+ f3 = 0;
+
+ ret = regmap_read(dev->regmap, 0x1D, &utmp);
+ if (ret)
+ goto err;
+ reg1D = utmp;
+ reg1D &= ~0x03;
+ reg1D |= N - 1;
+ reg1E = ((f3 << 4) + f2) & 0xFF;
+ reg1F = ((f1 << 4) + f0) & 0xFF;
+
+ /* program and recalibrate demod PLL */
+ ret = regmap_write(dev->regmap, 0x05, 0x40);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x11, 0x08);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x15, reg15);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x16, reg16);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x1D, reg1D);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x1E, reg1E);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x1F, reg1F);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x17, 0xc1);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x17, 0x81);
+ if (ret)
+ goto err;
+ usleep_range(5000, 50000);
+ ret = regmap_write(dev->regmap, 0x05, 0x00);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x11, reg11);
+ if (ret)
+ goto err;
+ usleep_range(5000, 50000);
+err:
+ if (ret)
+ dev_dbg(&dev->client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int m88rs6000t_set_pll_freq(struct m88rs6000t_dev *dev,
+ u32 tuner_freq_MHz)
+{
+ u32 fcry_KHz, ulNDiv1, ulNDiv2, ulNDiv;
+ u8 refDiv, ucLoDiv1, ucLomod1, ucLoDiv2, ucLomod2, ucLoDiv, ucLomod;
+ u8 reg27, reg29, reg42, reg42buf;
+ unsigned int utmp;
+ int ret;
+
+ fcry_KHz = 27000; /* in kHz */
+ refDiv = 27;
+
+ ret = regmap_write(dev->regmap, 0x36, (refDiv - 8));
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x31, 0x00);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x2c, 0x02);
+ if (ret)
+ goto err;
+
+ if (tuner_freq_MHz >= 1550) {
+ ucLoDiv1 = 2;
+ ucLomod1 = 0;
+ ucLoDiv2 = 2;
+ ucLomod2 = 0;
+ } else if (tuner_freq_MHz >= 1380) {
+ ucLoDiv1 = 3;
+ ucLomod1 = 16;
+ ucLoDiv2 = 2;
+ ucLomod2 = 0;
+ } else if (tuner_freq_MHz >= 1070) {
+ ucLoDiv1 = 3;
+ ucLomod1 = 16;
+ ucLoDiv2 = 3;
+ ucLomod2 = 16;
+ } else if (tuner_freq_MHz >= 1000) {
+ ucLoDiv1 = 3;
+ ucLomod1 = 16;
+ ucLoDiv2 = 4;
+ ucLomod2 = 64;
+ } else if (tuner_freq_MHz >= 775) {
+ ucLoDiv1 = 4;
+ ucLomod1 = 64;
+ ucLoDiv2 = 4;
+ ucLomod2 = 64;
+ } else if (tuner_freq_MHz >= 700) {
+ ucLoDiv1 = 6;
+ ucLomod1 = 48;
+ ucLoDiv2 = 4;
+ ucLomod2 = 64;
+ } else if (tuner_freq_MHz >= 520) {
+ ucLoDiv1 = 6;
+ ucLomod1 = 48;
+ ucLoDiv2 = 6;
+ ucLomod2 = 48;
+ } else {
+ ucLoDiv1 = 8;
+ ucLomod1 = 96;
+ ucLoDiv2 = 8;
+ ucLomod2 = 96;
+ }
+
+ ulNDiv1 = ((tuner_freq_MHz * ucLoDiv1 * 1000) * refDiv
+ / fcry_KHz - 1024) / 2;
+ ulNDiv2 = ((tuner_freq_MHz * ucLoDiv2 * 1000) * refDiv
+ / fcry_KHz - 1024) / 2;
+
+ reg27 = (((ulNDiv1 >> 8) & 0x0F) + ucLomod1) & 0x7F;
+ ret = regmap_write(dev->regmap, 0x27, reg27);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x28, (u8)(ulNDiv1 & 0xFF));
+ if (ret)
+ goto err;
+ reg29 = (((ulNDiv2 >> 8) & 0x0F) + ucLomod2) & 0x7f;
+ ret = regmap_write(dev->regmap, 0x29, reg29);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x2a, (u8)(ulNDiv2 & 0xFF));
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x2F, 0xf5);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x30, 0x05);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x08, 0x1f);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x08, 0x3f);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x09, 0x20);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x09, 0x00);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x3e, 0x11);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x08, 0x2f);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x08, 0x3f);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x09, 0x10);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x09, 0x00);
+ if (ret)
+ goto err;
+ usleep_range(2000, 50000);
+
+ ret = regmap_read(dev->regmap, 0x42, &utmp);
+ if (ret)
+ goto err;
+ reg42 = utmp;
+
+ ret = regmap_write(dev->regmap, 0x3e, 0x10);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x08, 0x2f);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x08, 0x3f);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x09, 0x10);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x09, 0x00);
+ if (ret)
+ goto err;
+ usleep_range(2000, 50000);
+
+ ret = regmap_read(dev->regmap, 0x42, &utmp);
+ if (ret)
+ goto err;
+ reg42buf = utmp;
+ if (reg42buf < reg42) {
+ ret = regmap_write(dev->regmap, 0x3e, 0x11);
+ if (ret)
+ goto err;
+ }
+ usleep_range(5000, 50000);
+
+ ret = regmap_read(dev->regmap, 0x2d, &utmp);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x2d, utmp);
+ if (ret)
+ goto err;
+ ret = regmap_read(dev->regmap, 0x2e, &utmp);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x2e, utmp);
+ if (ret)
+ goto err;
+
+ ret = regmap_read(dev->regmap, 0x27, &utmp);
+ if (ret)
+ goto err;
+ reg27 = utmp & 0x70;
+ ret = regmap_read(dev->regmap, 0x83, &utmp);
+ if (ret)
+ goto err;
+ if (reg27 == (utmp & 0x70)) {
+ ucLoDiv = ucLoDiv1;
+ ulNDiv = ulNDiv1;
+ ucLomod = ucLomod1 / 16;
+ } else {
+ ucLoDiv = ucLoDiv2;
+ ulNDiv = ulNDiv2;
+ ucLomod = ucLomod2 / 16;
+ }
+
+ if ((ucLoDiv == 3) || (ucLoDiv == 6)) {
+ refDiv = 18;
+ ret = regmap_write(dev->regmap, 0x36, (refDiv - 8));
+ if (ret)
+ goto err;
+ ulNDiv = ((tuner_freq_MHz * ucLoDiv * 1000) * refDiv
+ / fcry_KHz - 1024) / 2;
+ }
+
+ reg27 = (0x80 + ((ucLomod << 4) & 0x70)
+ + ((ulNDiv >> 8) & 0x0F)) & 0xFF;
+ ret = regmap_write(dev->regmap, 0x27, reg27);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x28, (u8)(ulNDiv & 0xFF));
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x29, 0x80);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x31, 0x03);
+ if (ret)
+ goto err;
+
+ if (ucLoDiv == 3)
+ utmp = 0xCE;
+ else
+ utmp = 0x8A;
+ ret = regmap_write(dev->regmap, 0x3b, utmp);
+ if (ret)
+ goto err;
+
+ dev->frequency_khz = fcry_KHz * (ulNDiv * 2 + 1024) / refDiv / ucLoDiv;
+
+ dev_dbg(&dev->client->dev,
+ "actual tune frequency=%d\n", dev->frequency_khz);
+err:
+ if (ret)
+ dev_dbg(&dev->client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int m88rs6000t_set_bb(struct m88rs6000t_dev *dev,
+ u32 symbol_rate_KSs, s32 lpf_offset_KHz)
+{
+ u32 f3dB;
+ u8 reg40;
+
+ f3dB = symbol_rate_KSs * 9 / 14 + 2000;
+ f3dB += lpf_offset_KHz;
+ f3dB = clamp_val(f3dB, 6000U, 43000U);
+ reg40 = f3dB / 1000;
+ return regmap_write(dev->regmap, 0x40, reg40);
+}
+
+static int m88rs6000t_set_params(struct dvb_frontend *fe)
+{
+ struct m88rs6000t_dev *dev = fe->tuner_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret;
+ s32 lpf_offset_KHz;
+ u32 realFreq, freq_MHz;
+
+ dev_dbg(&dev->client->dev,
+ "frequency=%d symbol_rate=%d\n",
+ c->frequency, c->symbol_rate);
+
+ if (c->symbol_rate < 5000000)
+ lpf_offset_KHz = 3000;
+ else
+ lpf_offset_KHz = 0;
+
+ realFreq = c->frequency + lpf_offset_KHz;
+ /* set tuner pll.*/
+ freq_MHz = (realFreq + 500) / 1000;
+ ret = m88rs6000t_set_pll_freq(dev, freq_MHz);
+ if (ret)
+ goto err;
+ ret = m88rs6000t_set_bb(dev, c->symbol_rate / 1000, lpf_offset_KHz);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x00, 0x01);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x00, 0x00);
+ if (ret)
+ goto err;
+ /* set demod mlck */
+ ret = m88rs6000t_set_demod_mclk(fe);
+err:
+ if (ret)
+ dev_dbg(&dev->client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int m88rs6000t_init(struct dvb_frontend *fe)
+{
+ struct m88rs6000t_dev *dev = fe->tuner_priv;
+ int ret;
+
+ dev_dbg(&dev->client->dev, "%s:\n", __func__);
+
+ ret = regmap_update_bits(dev->regmap, 0x11, 0x08, 0x08);
+ if (ret)
+ goto err;
+ usleep_range(5000, 50000);
+ ret = regmap_update_bits(dev->regmap, 0x10, 0x01, 0x01);
+ if (ret)
+ goto err;
+ usleep_range(10000, 50000);
+ ret = regmap_write(dev->regmap, 0x07, 0x7d);
+err:
+ if (ret)
+ dev_dbg(&dev->client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int m88rs6000t_sleep(struct dvb_frontend *fe)
+{
+ struct m88rs6000t_dev *dev = fe->tuner_priv;
+ int ret;
+
+ dev_dbg(&dev->client->dev, "%s:\n", __func__);
+
+ ret = regmap_write(dev->regmap, 0x07, 0x6d);
+ if (ret)
+ goto err;
+ usleep_range(5000, 10000);
+err:
+ if (ret)
+ dev_dbg(&dev->client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static int m88rs6000t_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct m88rs6000t_dev *dev = fe->tuner_priv;
+
+ dev_dbg(&dev->client->dev, "\n");
+
+ *frequency = dev->frequency_khz;
+ return 0;
+}
+
+static int m88rs6000t_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct m88rs6000t_dev *dev = fe->tuner_priv;
+
+ dev_dbg(&dev->client->dev, "\n");
+
+ *frequency = 0; /* Zero-IF */
+ return 0;
+}
+
+
+static int m88rs6000t_get_rf_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct m88rs6000t_dev *dev = fe->tuner_priv;
+ unsigned int val, i;
+ int ret;
+ u16 gain;
+ u32 PGA2_cri_GS = 46, PGA2_crf_GS = 290, TIA_GS = 290;
+ u32 RF_GC = 1200, IF_GC = 1100, BB_GC = 300;
+ u32 PGA2_GC = 300, TIA_GC = 300, PGA2_cri = 0, PGA2_crf = 0;
+ u32 RFG = 0, IFG = 0, BBG = 0, PGA2G = 0, TIAG = 0;
+ u32 RFGS[13] = {0, 245, 266, 268, 270, 285,
+ 298, 295, 283, 285, 285, 300, 300};
+ u32 IFGS[12] = {0, 300, 230, 270, 270, 285,
+ 295, 285, 290, 295, 295, 310};
+ u32 BBGS[14] = {0, 286, 275, 290, 294, 300, 290,
+ 290, 285, 283, 260, 295, 290, 260};
+
+ ret = regmap_read(dev->regmap, 0x5A, &val);
+ if (ret)
+ goto err;
+ RF_GC = val & 0x0f;
+
+ ret = regmap_read(dev->regmap, 0x5F, &val);
+ if (ret)
+ goto err;
+ IF_GC = val & 0x0f;
+
+ ret = regmap_read(dev->regmap, 0x3F, &val);
+ if (ret)
+ goto err;
+ TIA_GC = (val >> 4) & 0x07;
+
+ ret = regmap_read(dev->regmap, 0x77, &val);
+ if (ret)
+ goto err;
+ BB_GC = (val >> 4) & 0x0f;
+
+ ret = regmap_read(dev->regmap, 0x76, &val);
+ if (ret)
+ goto err;
+ PGA2_GC = val & 0x3f;
+ PGA2_cri = PGA2_GC >> 2;
+ PGA2_crf = PGA2_GC & 0x03;
+
+ for (i = 0; i <= RF_GC; i++)
+ RFG += RFGS[i];
+
+ if (RF_GC == 0)
+ RFG += 400;
+ if (RF_GC == 1)
+ RFG += 300;
+ if (RF_GC == 2)
+ RFG += 200;
+ if (RF_GC == 3)
+ RFG += 100;
+
+ for (i = 0; i <= IF_GC; i++)
+ IFG += IFGS[i];
+
+ TIAG = TIA_GC * TIA_GS;
+
+ for (i = 0; i <= BB_GC; i++)
+ BBG += BBGS[i];
+
+ PGA2G = PGA2_cri * PGA2_cri_GS + PGA2_crf * PGA2_crf_GS;
+
+ gain = RFG + IFG - TIAG + BBG + PGA2G;
+
+ /* scale value to 0x0000-0xffff */
+ gain = clamp_val(gain, 1000U, 10500U);
+ *strength = (10500 - gain) * 0xffff / (10500 - 1000);
+err:
+ if (ret)
+ dev_dbg(&dev->client->dev, "failed=%d\n", ret);
+ return ret;
+}
+
+static const struct dvb_tuner_ops m88rs6000t_tuner_ops = {
+ .info = {
+ .name = "Montage M88RS6000 Internal Tuner",
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ },
+
+ .init = m88rs6000t_init,
+ .sleep = m88rs6000t_sleep,
+ .set_params = m88rs6000t_set_params,
+ .get_frequency = m88rs6000t_get_frequency,
+ .get_if_frequency = m88rs6000t_get_if_frequency,
+ .get_rf_strength = m88rs6000t_get_rf_strength,
+};
+
+static int m88rs6000t_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct m88rs6000t_config *cfg = client->dev.platform_data;
+ struct dvb_frontend *fe = cfg->fe;
+ struct m88rs6000t_dev *dev;
+ int ret, i;
+ unsigned int utmp;
+ static const struct regmap_config regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ };
+ static const struct m88rs6000t_reg_val reg_vals[] = {
+ {0x10, 0xfb},
+ {0x24, 0x38},
+ {0x11, 0x0a},
+ {0x12, 0x00},
+ {0x2b, 0x1c},
+ {0x44, 0x48},
+ {0x54, 0x24},
+ {0x55, 0x06},
+ {0x59, 0x00},
+ {0x5b, 0x4c},
+ {0x60, 0x8b},
+ {0x61, 0xf4},
+ {0x65, 0x07},
+ {0x6d, 0x6f},
+ {0x6e, 0x31},
+ {0x3c, 0xf3},
+ {0x37, 0x0f},
+ {0x48, 0x28},
+ {0x49, 0xd8},
+ {0x70, 0x66},
+ {0x71, 0xCF},
+ {0x72, 0x81},
+ {0x73, 0xA7},
+ {0x74, 0x4F},
+ {0x75, 0xFC},
+ };
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ ret = -ENOMEM;
+ dev_err(&client->dev, "kzalloc() failed\n");
+ goto err;
+ }
+
+ memcpy(&dev->cfg, cfg, sizeof(struct m88rs6000t_config));
+ dev->client = client;
+ dev->regmap = devm_regmap_init_i2c(client, ®map_config);
+ if (IS_ERR(dev->regmap)) {
+ ret = PTR_ERR(dev->regmap);
+ goto err;
+ }
+
+ ret = regmap_update_bits(dev->regmap, 0x11, 0x08, 0x08);
+ if (ret)
+ goto err;
+ usleep_range(5000, 50000);
+ ret = regmap_update_bits(dev->regmap, 0x10, 0x01, 0x01);
+ if (ret)
+ goto err;
+ usleep_range(10000, 50000);
+ ret = regmap_write(dev->regmap, 0x07, 0x7d);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x04, 0x01);
+ if (ret)
+ goto err;
+
+ /* check tuner chip id */
+ ret = regmap_read(dev->regmap, 0x01, &utmp);
+ if (ret)
+ goto err;
+ dev_info(&dev->client->dev, "chip_id=%02x\n", utmp);
+ if (utmp != 0x64) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ /* tuner init. */
+ ret = regmap_write(dev->regmap, 0x05, 0x40);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x11, 0x08);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x15, 0x6c);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x17, 0xc1);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x17, 0x81);
+ if (ret)
+ goto err;
+ usleep_range(10000, 50000);
+ ret = regmap_write(dev->regmap, 0x05, 0x00);
+ if (ret)
+ goto err;
+ ret = regmap_write(dev->regmap, 0x11, 0x0a);
+ if (ret)
+ goto err;
+
+ for (i = 0; i < ARRAY_SIZE(reg_vals); i++) {
+ ret = regmap_write(dev->regmap,
+ reg_vals[i].reg, reg_vals[i].val);
+ if (ret)
+ goto err;
+ }
+
+ dev_info(&dev->client->dev, "Montage M88RS6000 internal tuner successfully identified\n");
+
+ fe->tuner_priv = dev;
+ memcpy(&fe->ops.tuner_ops, &m88rs6000t_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+ i2c_set_clientdata(client, dev);
+ return 0;
+err:
+ dev_dbg(&client->dev, "failed=%d\n", ret);
+ kfree(dev);
+ return ret;
+}
+
+static int m88rs6000t_remove(struct i2c_client *client)
+{
+ struct m88rs6000t_dev *dev = i2c_get_clientdata(client);
+ struct dvb_frontend *fe = dev->cfg.fe;
+
+ dev_dbg(&client->dev, "\n");
+
+ memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops));
+ fe->tuner_priv = NULL;
+ kfree(dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id m88rs6000t_id[] = {
+ {"m88rs6000t", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, m88rs6000t_id);
+
+static struct i2c_driver m88rs6000t_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "m88rs6000t",
+ },
+ .probe = m88rs6000t_probe,
+ .remove = m88rs6000t_remove,
+ .id_table = m88rs6000t_id,
+};
+
+module_i2c_driver(m88rs6000t_driver);
+
+MODULE_AUTHOR("Max nibble <nibble.max@gmail.com>");
+MODULE_DESCRIPTION("Montage M88RS6000 internal tuner driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/tuners/m88rs6000t.h b/drivers/media/tuners/m88rs6000t.h
new file mode 100644
index 0000000..264c13e
--- /dev/null
+++ b/drivers/media/tuners/m88rs6000t.h
@@ -0,0 +1,29 @@
+/*
+ * Driver for the internal tuner of Montage M88RS6000
+ *
+ * Copyright (C) 2014 Max nibble <nibble.max@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _M88RS6000T_H_
+#define _M88RS6000T_H_
+
+#include "dvb_frontend.h"
+
+struct m88rs6000t_config {
+ /*
+ * pointer to DVB frontend
+ */
+ struct dvb_frontend *fe;
+};
+
+#endif
diff --git a/drivers/media/tuners/m88ts2022.c b/drivers/media/tuners/m88ts2022.c
index caa5423..066e543 100644
--- a/drivers/media/tuners/m88ts2022.c
+++ b/drivers/media/tuners/m88ts2022.c
@@ -488,6 +488,7 @@
case 0x83:
break;
default:
+ ret = -ENODEV;
goto err;
}
@@ -505,6 +506,7 @@
u8tmp = 0x6c;
break;
default:
+ ret = -EINVAL;
goto err;
}
diff --git a/drivers/media/tuners/mxl5007t.c b/drivers/media/tuners/mxl5007t.c
index 69e453e..1810ad6 100644
--- a/drivers/media/tuners/mxl5007t.c
+++ b/drivers/media/tuners/mxl5007t.c
@@ -374,7 +374,6 @@
mxl5007t_set_if_freq_bits(state, cfg->if_freq_hz, cfg->invert_if);
mxl5007t_set_xtal_freq_bits(state, cfg->xtal_freq_hz);
- set_reg_bits(state->tab_init, 0x04, 0x01, cfg->loop_thru_enable);
set_reg_bits(state->tab_init, 0x03, 0x08, cfg->clk_out_enable << 3);
set_reg_bits(state->tab_init, 0x03, 0x07, cfg->clk_out_amp);
@@ -531,10 +530,6 @@
struct reg_pair_t *init_regs;
int ret;
- ret = mxl5007t_soft_reset(state);
- if (mxl_fail(ret))
- goto fail;
-
/* calculate initialization reg array */
init_regs = mxl5007t_calc_init_regs(state, mode);
@@ -900,7 +895,32 @@
/* existing tuner instance */
break;
}
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ ret = mxl5007t_soft_reset(state);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ if (mxl_fail(ret))
+ goto fail;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ ret = mxl5007t_write_reg(state, 0x04,
+ state->config->loop_thru_enable);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ if (mxl_fail(ret))
+ goto fail;
+
fe->tuner_priv = state;
+
mutex_unlock(&mxl5007t_list_mutex);
memcpy(&fe->ops.tuner_ops, &mxl5007t_tuner_ops,
diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c
index a759742..8e040cf 100644
--- a/drivers/media/tuners/r820t.c
+++ b/drivers/media/tuners/r820t.c
@@ -959,6 +959,18 @@
lt_att = 0x00; /* r31[7], lt att enable */
flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */
polyfil_cur = 0x60; /* r25[6:5]:min */
+ } else if (delsys == SYS_DVBC_ANNEX_A) {
+ if_khz = 5070;
+ filt_cal_lo = 73500;
+ filt_gain = 0x10; /* +3db, 6mhz on */
+ img_r = 0x00; /* image negative */
+ filt_q = 0x10; /* r10[4]:low q(1'b1) */
+ hp_cor = 0x0b; /* 1.7m disable, +0cap, 1.0mhz */
+ ext_enable = 0x40; /* r30[6]=1 ext enable; r30[5]:1 ext at lna max-1 */
+ loop_through = 0x00; /* r5[7], lt on */
+ lt_att = 0x00; /* r31[7], lt att enable */
+ flt_ext_widest = 0x00; /* r15[7]: flt_ext_wide off */
+ polyfil_cur = 0x60; /* r25[6:5]:min */
} else {
if (bw <= 6) {
if_khz = 3570;
diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c
index cf97142..2180de9 100644
--- a/drivers/media/tuners/si2157.c
+++ b/drivers/media/tuners/si2157.c
@@ -1,5 +1,5 @@
/*
- * Silicon Labs Si2147/2157/2158 silicon tuner driver
+ * Silicon Labs Si2146/2147/2148/2157/2158 silicon tuner driver
*
* Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
*
@@ -93,8 +93,13 @@
goto warm;
/* power up */
- memcpy(cmd.args, "\xc0\x00\x0c\x00\x00\x01\x01\x01\x01\x01\x01\x02\x00\x00\x01", 15);
- cmd.wlen = 15;
+ if (s->chiptype == SI2157_CHIPTYPE_SI2146) {
+ memcpy(cmd.args, "\xc0\x05\x01\x00\x00\x0b\x00\x00\x01", 9);
+ cmd.wlen = 9;
+ } else {
+ memcpy(cmd.args, "\xc0\x00\x0c\x00\x00\x01\x01\x01\x01\x01\x01\x02\x00\x00\x01", 15);
+ cmd.wlen = 15;
+ }
cmd.rlen = 1;
ret = si2157_cmd_execute(s, &cmd);
if (ret)
@@ -112,17 +117,20 @@
cmd.args[4] << 0;
#define SI2158_A20 ('A' << 24 | 58 << 16 | '2' << 8 | '0' << 0)
+ #define SI2148_A20 ('A' << 24 | 48 << 16 | '2' << 8 | '0' << 0)
#define SI2157_A30 ('A' << 24 | 57 << 16 | '3' << 8 | '0' << 0)
#define SI2147_A30 ('A' << 24 | 47 << 16 | '3' << 8 | '0' << 0)
+ #define SI2146_A10 ('A' << 24 | 46 << 16 | '1' << 8 | '0' << 0)
switch (chip_id) {
case SI2158_A20:
+ case SI2148_A20:
fw_file = SI2158_A20_FIRMWARE;
break;
case SI2157_A30:
case SI2147_A30:
+ case SI2146_A10:
goto skip_fw_download;
- break;
default:
dev_err(&s->client->dev,
"unknown chip version Si21%d-%c%c%c\n",
@@ -149,7 +157,7 @@
dev_err(&s->client->dev, "firmware file '%s' is invalid\n",
fw_file);
ret = -EINVAL;
- goto err;
+ goto fw_release_exit;
}
dev_info(&s->client->dev, "downloading firmware from file '%s'\n",
@@ -165,7 +173,7 @@
dev_err(&s->client->dev,
"firmware download failed=%d\n",
ret);
- goto err;
+ goto fw_release_exit;
}
}
@@ -187,10 +195,9 @@
s->active = true;
return 0;
+fw_release_exit:
+ release_firmware(fw);
err:
- if (fw)
- release_firmware(fw);
-
dev_dbg(&s->client->dev, "failed=%d\n", ret);
return ret;
}
@@ -250,6 +257,9 @@
case SYS_ATSC:
delivery_system = 0x00;
break;
+ case SYS_DVBC_ANNEX_B:
+ delivery_system = 0x10;
+ break;
case SYS_DVBT:
case SYS_DVBT2: /* it seems DVB-T and DVB-T2 both are 0x20 here */
delivery_system = 0x20;
@@ -272,7 +282,10 @@
if (ret)
goto err;
- memcpy(cmd.args, "\x14\x00\x02\x07\x01\x00", 6);
+ if (s->chiptype == SI2157_CHIPTYPE_SI2146)
+ memcpy(cmd.args, "\x14\x00\x02\x07\x00\x01", 6);
+ else
+ memcpy(cmd.args, "\x14\x00\x02\x07\x01\x00", 6);
cmd.wlen = 6;
cmd.rlen = 4;
ret = si2157_cmd_execute(s, &cmd);
@@ -305,7 +318,7 @@
static const struct dvb_tuner_ops si2157_ops = {
.info = {
- .name = "Silicon Labs Si2157/Si2158",
+ .name = "Silicon Labs Si2146/2147/2148/2157/2158",
.frequency_min = 110000000,
.frequency_max = 862000000,
},
@@ -336,6 +349,7 @@
s->fe = cfg->fe;
s->inversion = cfg->inversion;
s->fw_loaded = false;
+ s->chiptype = (u8)id->driver_data;
mutex_init(&s->i2c_mutex);
/* check if the tuner is there */
@@ -352,7 +366,10 @@
i2c_set_clientdata(client, s);
dev_info(&s->client->dev,
- "Silicon Labs Si2157/Si2158 successfully attached\n");
+ "Silicon Labs %s successfully attached\n",
+ s->chiptype == SI2157_CHIPTYPE_SI2146 ?
+ "Si2146" : "Si2147/2148/2157/2158");
+
return 0;
err:
dev_dbg(&client->dev, "failed=%d\n", ret);
@@ -377,6 +394,7 @@
static const struct i2c_device_id si2157_id[] = {
{"si2157", 0},
+ {"si2146", 1},
{}
};
MODULE_DEVICE_TABLE(i2c, si2157_id);
@@ -393,7 +411,7 @@
module_i2c_driver(si2157_driver);
-MODULE_DESCRIPTION("Silicon Labs Si2157/Si2158 silicon tuner driver");
+MODULE_DESCRIPTION("Silicon Labs Si2146/2147/2148/2157/2158 silicon tuner driver");
MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
MODULE_LICENSE("GPL");
MODULE_FIRMWARE(SI2158_A20_FIRMWARE);
diff --git a/drivers/media/tuners/si2157.h b/drivers/media/tuners/si2157.h
index d3b19ca..a564c4a 100644
--- a/drivers/media/tuners/si2157.h
+++ b/drivers/media/tuners/si2157.h
@@ -1,5 +1,5 @@
/*
- * Silicon Labs Si2147/2157/2158 silicon tuner driver
+ * Silicon Labs Si2146/2147/2148/2157/2158 silicon tuner driver
*
* Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
*
diff --git a/drivers/media/tuners/si2157_priv.h b/drivers/media/tuners/si2157_priv.h
index e71ffaf..d6e07cd 100644
--- a/drivers/media/tuners/si2157_priv.h
+++ b/drivers/media/tuners/si2157_priv.h
@@ -1,5 +1,5 @@
/*
- * Silicon Labs Si2147/2157/2158 silicon tuner driver
+ * Silicon Labs Si2146/2147/2148/2157/2158 silicon tuner driver
*
* Copyright (C) 2014 Antti Palosaari <crope@iki.fi>
*
@@ -28,9 +28,13 @@
bool active;
bool fw_loaded;
bool inversion;
+ u8 chiptype;
};
-/* firmare command struct */
+#define SI2157_CHIPTYPE_SI2157 0
+#define SI2157_CHIPTYPE_SI2146 1
+
+/* firmware command struct */
#define SI2157_ARGLEN 30
struct si2157_cmd {
u8 args[SI2157_ARGLEN];
diff --git a/drivers/media/tuners/tda18271-common.c b/drivers/media/tuners/tda18271-common.c
index 86e5e31..a26bb33 100644
--- a/drivers/media/tuners/tda18271-common.c
+++ b/drivers/media/tuners/tda18271-common.c
@@ -178,7 +178,7 @@
(i != R_EB17) &&
(i != R_EB19) &&
(i != R_EB20))
- regs[i] = regdump[i];
+ regs[i] = regdump[i];
}
if (tda18271_debug & DBG_REG)
diff --git a/drivers/media/tuners/xc5000.c b/drivers/media/tuners/xc5000.c
index e44c8ab..2a039de 100644
--- a/drivers/media/tuners/xc5000.c
+++ b/drivers/media/tuners/xc5000.c
@@ -62,6 +62,7 @@
unsigned int mode;
u8 rf_mode;
u8 radio_input;
+ u16 output_amp;
int chip_id;
u16 pll_register_no;
@@ -744,7 +745,9 @@
return -EIO;
}
- xc_write_reg(priv, XREG_OUTPUT_AMP, 0x8a);
+ dprintk(1, "%s() setting OUTPUT_AMP to 0x%x\n",
+ __func__, priv->output_amp);
+ xc_write_reg(priv, XREG_OUTPUT_AMP, priv->output_amp);
xc_tune_channel(priv, priv->freq_hz, XC_TUNE_DIGITAL);
@@ -1333,9 +1336,8 @@
if (priv) {
cancel_delayed_work(&priv->timer_sleep);
+ release_firmware(priv->firmware);
hybrid_tuner_release_state(priv);
- if (priv->firmware)
- release_firmware(priv->firmware);
}
mutex_unlock(&xc5000_list_mutex);
@@ -1358,6 +1360,9 @@
if (p->radio_input)
priv->radio_input = p->radio_input;
+ if (p->output_amp)
+ priv->output_amp = p->output_amp;
+
return 0;
}
@@ -1438,6 +1443,12 @@
it can be overridden if this is a hybrid driver */
priv->chip_id = (cfg->chip_id) ? cfg->chip_id : 0;
+ /* don't override output_amp if it's already been set
+ unless explicitly specified */
+ if ((priv->output_amp == 0) || (cfg->output_amp))
+ /* use default output_amp value if none specified */
+ priv->output_amp = (cfg->output_amp) ? cfg->output_amp : 0x8a;
+
/* Check if firmware has been loaded. It is possible that another
instance of the driver has loaded the firmware.
*/
diff --git a/drivers/media/tuners/xc5000.h b/drivers/media/tuners/xc5000.h
index 38ff591..d3ffe4b 100644
--- a/drivers/media/tuners/xc5000.h
+++ b/drivers/media/tuners/xc5000.h
@@ -36,6 +36,7 @@
u32 if_khz;
u8 radio_input;
u16 xtal_khz;
+ u16 output_amp;
int chip_id;
};
diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig
index 056181f..7496f33 100644
--- a/drivers/media/usb/Kconfig
+++ b/drivers/media/usb/Kconfig
@@ -24,7 +24,6 @@
comment "Analog TV USB devices"
source "drivers/media/usb/pvrusb2/Kconfig"
source "drivers/media/usb/hdpvr/Kconfig"
-source "drivers/media/usb/tlg2300/Kconfig"
source "drivers/media/usb/usbvision/Kconfig"
source "drivers/media/usb/stk1160/Kconfig"
source "drivers/media/usb/go7007/Kconfig"
diff --git a/drivers/media/usb/Makefile b/drivers/media/usb/Makefile
index 417760e..920c1ed 100644
--- a/drivers/media/usb/Makefile
+++ b/drivers/media/usb/Makefile
@@ -16,7 +16,6 @@
obj-$(CPTCFG_VIDEO_AU0828) += au0828/
obj-$(CPTCFG_VIDEO_HDPVR) += hdpvr/
obj-$(CPTCFG_VIDEO_PVRUSB2) += pvrusb2/
-obj-$(CPTCFG_VIDEO_TLG2300) += tlg2300/
obj-$(CPTCFG_VIDEO_USBVISION) += usbvision/
obj-$(CPTCFG_VIDEO_STK1160) += stk1160/
obj-$(CPTCFG_VIDEO_CX231XX) += cx231xx/
diff --git a/drivers/media/usb/au0828/au0828-cards.c b/drivers/media/usb/au0828/au0828-cards.c
index 6c164d9..b52f470 100644
--- a/drivers/media/usb/au0828/au0828-cards.c
+++ b/drivers/media/usb/au0828/au0828-cards.c
@@ -36,6 +36,11 @@
au0828_clear(dev, REG_000, 0x10);
}
+/*
+ * WARNING: There's a quirks table at sound/usb/quirks-table.h
+ * that should also be updated every time a new device with V4L2 support
+ * is added here.
+ */
struct au0828_board au0828_boards[] = {
[AU0828_BOARD_UNKNOWN] = {
.name = "Unknown board",
diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c
index c697199..73b6093 100644
--- a/drivers/media/usb/au0828/au0828-core.c
+++ b/drivers/media/usb/au0828/au0828-core.c
@@ -153,6 +153,14 @@
dprintk(1, "%s()\n", __func__);
+ /* there is a small window after disconnect, before
+ dev->usbdev is NULL, for poll (e.g: IR) try to access
+ the device and fill the dmesg with error messages.
+ Set the status so poll routines can check and avoid
+ access after disconnect.
+ */
+ dev->dev_state = DEV_DISCONNECTED;
+
au0828_rc_unregister(dev);
/* Digital TV */
au0828_dvb_unregister(dev);
diff --git a/drivers/media/usb/au0828/au0828-dvb.c b/drivers/media/usb/au0828/au0828-dvb.c
index 00ab156..c267d76 100644
--- a/drivers/media/usb/au0828/au0828-dvb.c
+++ b/drivers/media/usb/au0828/au0828-dvb.c
@@ -88,12 +88,14 @@
.i2c_address = 0x61,
.if_khz = 6000,
.chip_id = XC5000A,
+ .output_amp = 0x8f,
};
static struct xc5000_config hauppauge_xc5000c_config = {
.i2c_address = 0x61,
.if_khz = 6000,
.chip_id = XC5000C,
+ .output_amp = 0x8f,
};
static struct mxl5007t_config mxl5007t_hvr950q_config = {
diff --git a/drivers/media/usb/au0828/au0828-input.c b/drivers/media/usb/au0828/au0828-input.c
index 63995f9..b0f0679 100644
--- a/drivers/media/usb/au0828/au0828-input.c
+++ b/drivers/media/usb/au0828/au0828-input.c
@@ -129,6 +129,10 @@
int prv_bit, bit, width;
bool first = true;
+ /* do nothing if device is disconnected */
+ if (ir->dev->dev_state == DEV_DISCONNECTED)
+ return 0;
+
/* Check IR int */
rc = au8522_rc_read(ir, 0xe1, -1, buf, 1);
if (rc < 0 || !(buf[0] & (1 << 4))) {
@@ -255,8 +259,11 @@
cancel_delayed_work_sync(&ir->work);
- /* Disable IR */
- au8522_rc_clear(ir, 0xe0, 1 << 4);
+ /* do nothing if device is disconnected */
+ if (ir->dev->dev_state != DEV_DISCONNECTED) {
+ /* Disable IR */
+ au8522_rc_clear(ir, 0xe0, 1 << 4);
+ }
}
static int au0828_probe_i2c_ir(struct au0828_dev *dev)
@@ -363,8 +370,7 @@
if (!ir)
return;
- if (ir->rc)
- rc_unregister_device(ir->rc);
+ rc_unregister_device(ir->rc);
/* done */
kfree(ir);
diff --git a/drivers/media/usb/cx231xx/Kconfig b/drivers/media/usb/cx231xx/Kconfig
index 47209ab..0d1ff87 100644
--- a/drivers/media/usb/cx231xx/Kconfig
+++ b/drivers/media/usb/cx231xx/Kconfig
@@ -8,6 +8,7 @@
select VIDEOBUF_VMALLOC
select VIDEO_CX25840
select VIDEO_CX2341X
+ depends on I2C_MUX
---help---
This is a video4linux driver for Conexant 231xx USB based TV cards.
diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c
index 6afc620..64f345d 100644
--- a/drivers/media/usb/cx231xx/cx231xx-417.c
+++ b/drivers/media/usb/cx231xx/cx231xx-417.c
@@ -24,6 +24,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include "cx231xx.h"
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
@@ -37,9 +39,6 @@
#include <media/v4l2-event.h>
#include <media/cx2341x.h>
#include <media/tuner.h>
-#include <linux/usb.h>
-
-#include "cx231xx.h"
#define CX231xx_FIRM_IMAGE_SIZE 376836
#define CX231xx_FIRM_IMAGE_NAME "v4l-cx23885-enc.fw"
@@ -90,10 +89,10 @@
module_param(v4l_debug, int, 0644);
MODULE_PARM_DESC(v4l_debug, "enable V4L debug messages");
-#define dprintk(level, fmt, arg...)\
- do { if (v4l_debug >= level) \
- pr_info("%s: " fmt, \
- (dev) ? dev->name : "cx231xx[?]", ## arg); \
+#define dprintk(level, fmt, arg...) \
+ do { \
+ if (v4l_debug >= level) \
+ printk(KERN_DEBUG pr_fmt(fmt), ## arg); \
} while (0)
static struct cx231xx_tvnorm cx231xx_tvnorms[] = {
@@ -988,29 +987,34 @@
IVTV_REG_APU, 0);
if (retval != 0) {
- pr_err("%s: Error with mc417_register_write\n", __func__);
+ dev_err(dev->dev,
+ "%s: Error with mc417_register_write\n", __func__);
return -1;
}
retval = request_firmware(&firmware, CX231xx_FIRM_IMAGE_NAME,
- &dev->udev->dev);
+ dev->dev);
if (retval != 0) {
- pr_err("ERROR: Hotplug firmware request failed (%s).\n",
+ dev_err(dev->dev,
+ "ERROR: Hotplug firmware request failed (%s).\n",
CX231xx_FIRM_IMAGE_NAME);
- pr_err("Please fix your hotplug setup, the board will not work without firmware loaded!\n");
+ dev_err(dev->dev,
+ "Please fix your hotplug setup, the board will not work without firmware loaded!\n");
return -1;
}
if (firmware->size != CX231xx_FIRM_IMAGE_SIZE) {
- pr_err("ERROR: Firmware size mismatch (have %zd, expected %d)\n",
+ dev_err(dev->dev,
+ "ERROR: Firmware size mismatch (have %zd, expected %d)\n",
firmware->size, CX231xx_FIRM_IMAGE_SIZE);
release_firmware(firmware);
return -1;
}
if (0 != memcmp(firmware->data, magic, 8)) {
- pr_err("ERROR: Firmware magic mismatch, wrong file?\n");
+ dev_err(dev->dev,
+ "ERROR: Firmware magic mismatch, wrong file?\n");
release_firmware(firmware);
return -1;
}
@@ -1057,7 +1061,8 @@
retval |= mc417_register_write(dev, IVTV_REG_HW_BLOCKS,
IVTV_CMD_HW_BLOCKS_RST);
if (retval < 0) {
- pr_err("%s: Error with mc417_register_write\n",
+ dev_err(dev->dev,
+ "%s: Error with mc417_register_write\n",
__func__);
return retval;
}
@@ -1069,7 +1074,8 @@
retval |= mc417_register_write(dev, IVTV_REG_VPU, value & 0xFFFFFFE8);
if (retval < 0) {
- pr_err("%s: Error with mc417_register_write\n",
+ dev_err(dev->dev,
+ "%s: Error with mc417_register_write\n",
__func__);
return retval;
}
@@ -1114,28 +1120,31 @@
cx231xx_disable656(dev);
retval = cx231xx_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */
if (retval < 0) {
- dprintk(2, "%s() PING OK\n", __func__);
+ dprintk(2, "%s: PING OK\n", __func__);
retval = cx231xx_load_firmware(dev);
if (retval < 0) {
- pr_err("%s() f/w load failed\n", __func__);
+ dev_err(dev->dev,
+ "%s: f/w load failed\n", __func__);
return retval;
}
retval = cx231xx_find_mailbox(dev);
if (retval < 0) {
- pr_err("%s() mailbox < 0, error\n",
+ dev_err(dev->dev, "%s: mailbox < 0, error\n",
__func__);
return -1;
}
dev->cx23417_mailbox = retval;
retval = cx231xx_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0);
if (retval < 0) {
- pr_err("ERROR: cx23417 firmware ping failed!\n");
+ dev_err(dev->dev,
+ "ERROR: cx23417 firmware ping failed!\n");
return -1;
}
retval = cx231xx_api_cmd(dev, CX2341X_ENC_GET_VERSION, 0, 1,
&version);
if (retval < 0) {
- pr_err("ERROR: cx23417 firmware get encoder: version failed!\n");
+ dev_err(dev->dev,
+ "ERROR: cx23417 firmware get encoder: version failed!\n");
return -1;
}
dprintk(1, "cx23417 firmware version is 0x%08x\n", version);
@@ -1416,8 +1425,9 @@
if (!dev->video_mode.bulk_ctl.num_bufs)
urb_init = 1;
}
- /*cx231xx_info("urb_init=%d dev->video_mode.max_pkt_size=%d\n",
- urb_init, dev->video_mode.max_pkt_size);*/
+ dev_dbg(dev->dev,
+ "urb_init=%d dev->video_mode.max_pkt_size=%d\n",
+ urb_init, dev->video_mode.max_pkt_size);
dev->mode_tv = 1;
if (urb_init) {
@@ -1688,7 +1698,7 @@
sizeof(struct cx231xx_buffer), fh, &dev->lock);
/*
videobuf_queue_sg_init(&fh->vidq, &cx231xx_qops,
- &dev->udev->dev, &dev->ts1.slock,
+ dev->dev, &dev->ts1.slock,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_INTERLACED,
sizeof(struct cx231xx_buffer),
@@ -1798,7 +1808,6 @@
static int mpeg_mmap(struct file *file, struct vm_area_struct *vma)
{
struct cx231xx_fh *fh = file->private_data;
- struct cx231xx *dev = fh->dev;
dprintk(2, "%s()\n", __func__);
@@ -1878,7 +1887,7 @@
/* fix videodecoder resolution */
fmt.width = cxhdl->width / (is_mpeg1 ? 2 : 1);
fmt.height = cxhdl->height;
- fmt.code = V4L2_MBUS_FMT_FIXED;
+ fmt.code = MEDIA_BUS_FMT_FIXED;
v4l2_subdev_call(dev->sd_cx25840, video, s_mbus_fmt, &fmt);
return 0;
}
diff --git a/drivers/media/usb/cx231xx/cx231xx-audio.c b/drivers/media/usb/cx231xx/cx231xx-audio.c
index 9b92587..de4ae5e 100644
--- a/drivers/media/usb/cx231xx/cx231xx-audio.c
+++ b/drivers/media/usb/cx231xx/cx231xx-audio.c
@@ -20,8 +20,8 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include "cx231xx.h"
#include <linux/kernel.h>
-#include <linux/usb.h>
#include <linux/init.h>
#include <linux/sound.h>
#include <linux/spinlock.h>
@@ -37,25 +37,18 @@
#include <sound/initval.h>
#include <sound/control.h>
#include <media/v4l2-common.h>
-#include "cx231xx.h"
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "activates debug info");
-#define dprintk(fmt, arg...) do { \
- if (debug) \
- printk(KERN_INFO "cx231xx-audio %s: " fmt, \
- __func__, ##arg); \
- } while (0)
-
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static int cx231xx_isoc_audio_deinit(struct cx231xx *dev)
{
int i;
- dprintk("Stopping isoc\n");
+ dev_dbg(dev->dev, "Stopping isoc\n");
for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
if (dev->adev.urb[i]) {
@@ -79,7 +72,7 @@
{
int i;
- dprintk("Stopping bulk\n");
+ dev_dbg(dev->dev, "Stopping bulk\n");
for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
if (dev->adev.urb[i]) {
@@ -123,7 +116,8 @@
case -ESHUTDOWN:
return;
default: /* error */
- dprintk("urb completition error %d.\n", urb->status);
+ dev_dbg(dev->dev, "urb completition error %d.\n",
+ urb->status);
break;
}
@@ -182,8 +176,9 @@
status = usb_submit_urb(urb, GFP_ATOMIC);
if (status < 0) {
- cx231xx_errdev("resubmit of audio urb failed (error=%i)\n",
- status);
+ dev_err(dev->dev,
+ "resubmit of audio urb failed (error=%i)\n",
+ status);
}
return;
}
@@ -211,7 +206,8 @@
case -ESHUTDOWN:
return;
default: /* error */
- dprintk("urb completition error %d.\n", urb->status);
+ dev_dbg(dev->dev, "urb completition error %d.\n",
+ urb->status);
break;
}
@@ -266,8 +262,9 @@
status = usb_submit_urb(urb, GFP_ATOMIC);
if (status < 0) {
- cx231xx_errdev("resubmit of audio urb failed (error=%i)\n",
- status);
+ dev_err(dev->dev,
+ "resubmit of audio urb failed (error=%i)\n",
+ status);
}
return;
}
@@ -277,7 +274,8 @@
int i, errCode;
int sb_size;
- cx231xx_info("%s: Starting ISO AUDIO transfers\n", __func__);
+ dev_dbg(dev->dev,
+ "%s: Starting ISO AUDIO transfers\n", __func__);
if (dev->state & DEV_DISCONNECTED)
return -ENODEV;
@@ -295,7 +293,7 @@
memset(dev->adev.transfer_buffer[i], 0x80, sb_size);
urb = usb_alloc_urb(CX231XX_ISO_NUM_AUDIO_PACKETS, GFP_ATOMIC);
if (!urb) {
- cx231xx_errdev("usb_alloc_urb failed!\n");
+ dev_err(dev->dev, "usb_alloc_urb failed!\n");
for (j = 0; j < i; j++) {
usb_free_urb(dev->adev.urb[j]);
kfree(dev->adev.transfer_buffer[j]);
@@ -338,7 +336,8 @@
int i, errCode;
int sb_size;
- cx231xx_info("%s: Starting BULK AUDIO transfers\n", __func__);
+ dev_dbg(dev->dev,
+ "%s: Starting BULK AUDIO transfers\n", __func__);
if (dev->state & DEV_DISCONNECTED)
return -ENODEV;
@@ -356,7 +355,7 @@
memset(dev->adev.transfer_buffer[i], 0x80, sb_size);
urb = usb_alloc_urb(CX231XX_NUM_AUDIO_PACKETS, GFP_ATOMIC);
if (!urb) {
- cx231xx_errdev("usb_alloc_urb failed!\n");
+ dev_err(dev->dev, "usb_alloc_urb failed!\n");
for (j = 0; j < i; j++) {
usb_free_urb(dev->adev.urb[j]);
kfree(dev->adev.transfer_buffer[j]);
@@ -392,8 +391,9 @@
size_t size)
{
struct snd_pcm_runtime *runtime = subs->runtime;
+ struct cx231xx *dev = snd_pcm_substream_chip(subs);
- dprintk("Allocating vbuffer\n");
+ dev_dbg(dev->dev, "Allocating vbuffer\n");
if (runtime->dma_area) {
if (runtime->dma_bytes > size)
return 0;
@@ -436,16 +436,12 @@
struct snd_pcm_runtime *runtime = substream->runtime;
int ret = 0;
- dprintk("opening device and trying to acquire exclusive lock\n");
-
- if (!dev) {
- cx231xx_errdev("BUG: cx231xx can't find device struct."
- " Can't proceed with open\n");
- return -ENODEV;
- }
+ dev_dbg(dev->dev,
+ "opening device and trying to acquire exclusive lock\n");
if (dev->state & DEV_DISCONNECTED) {
- cx231xx_errdev("Can't open. the device was removed.\n");
+ dev_err(dev->dev,
+ "Can't open. the device was removed.\n");
return -ENODEV;
}
@@ -458,7 +454,8 @@
ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 0);
mutex_unlock(&dev->lock);
if (ret < 0) {
- cx231xx_errdev("failed to set alternate setting !\n");
+ dev_err(dev->dev,
+ "failed to set alternate setting !\n");
return ret;
}
@@ -484,7 +481,7 @@
int ret;
struct cx231xx *dev = snd_pcm_substream_chip(substream);
- dprintk("closing device\n");
+ dev_dbg(dev->dev, "closing device\n");
/* inform hardware to stop streaming */
mutex_lock(&dev->lock);
@@ -494,7 +491,8 @@
/* 1 - 48000 samples per sec */
ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 0);
if (ret < 0) {
- cx231xx_errdev("failed to set alternate setting !\n");
+ dev_err(dev->dev,
+ "failed to set alternate setting !\n");
mutex_unlock(&dev->lock);
return ret;
@@ -504,10 +502,10 @@
mutex_unlock(&dev->lock);
if (dev->adev.users == 0 && dev->adev.shutdown == 1) {
- dprintk("audio users: %d\n", dev->adev.users);
- dprintk("disabling audio stream!\n");
+ dev_dbg(dev->dev, "audio users: %d\n", dev->adev.users);
+ dev_dbg(dev->dev, "disabling audio stream!\n");
dev->adev.shutdown = 0;
- dprintk("released lock\n");
+ dev_dbg(dev->dev, "released lock\n");
if (atomic_read(&dev->stream_started) > 0) {
atomic_set(&dev->stream_started, 0);
schedule_work(&dev->wq_trigger);
@@ -519,9 +517,10 @@
static int snd_cx231xx_hw_capture_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
+ struct cx231xx *dev = snd_pcm_substream_chip(substream);
int ret;
- dprintk("Setting capture parameters\n");
+ dev_dbg(dev->dev, "Setting capture parameters\n");
ret = snd_pcm_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
@@ -543,7 +542,7 @@
{
struct cx231xx *dev = snd_pcm_substream_chip(substream);
- dprintk("Stop capture, if needed\n");
+ dev_dbg(dev->dev, "Stop capture, if needed\n");
if (atomic_read(&dev->stream_started) > 0) {
atomic_set(&dev->stream_started, 0);
@@ -568,7 +567,7 @@
struct cx231xx *dev = container_of(work, struct cx231xx, wq_trigger);
if (atomic_read(&dev->stream_started)) {
- dprintk("starting capture");
+ dev_dbg(dev->dev, "starting capture");
if (is_fw_load(dev) == 0)
cx25840_call(dev, core, load_fw);
if (dev->USE_ISO)
@@ -576,7 +575,7 @@
else
cx231xx_init_audio_bulk(dev);
} else {
- dprintk("stopping capture");
+ dev_dbg(dev->dev, "stopping capture");
cx231xx_isoc_audio_deinit(dev);
}
}
@@ -662,10 +661,10 @@
return 0;
}
- cx231xx_info("cx231xx-audio.c: probing for cx231xx "
- "non standard usbaudio\n");
+ dev_dbg(dev->dev,
+ "probing for cx231xx non standard usbaudio\n");
- err = snd_card_new(&dev->udev->dev, index[devnr], "Cx231xx Audio",
+ err = snd_card_new(dev->dev, index[devnr], "Cx231xx Audio",
THIS_MODULE, 0, &card);
if (err < 0)
return err;
@@ -707,14 +706,13 @@
bEndpointAddress;
adev->num_alt = uif->num_altsetting;
- cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
- adev->end_point_addr, adev->num_alt);
+ dev_info(dev->dev,
+ "audio EndPoint Addr 0x%x, Alternate settings: %i\n",
+ adev->end_point_addr, adev->num_alt);
adev->alt_max_pkt_size = kmalloc(32 * adev->num_alt, GFP_KERNEL);
- if (adev->alt_max_pkt_size == NULL) {
- cx231xx_errdev("out of memory!\n");
+ if (adev->alt_max_pkt_size == NULL)
return -ENOMEM;
- }
for (i = 0; i < adev->num_alt; i++) {
u16 tmp =
@@ -722,8 +720,9 @@
wMaxPacketSize);
adev->alt_max_pkt_size[i] =
(tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
- cx231xx_info("Alternate setting %i, max size= %i\n", i,
- adev->alt_max_pkt_size[i]);
+ dev_dbg(dev->dev,
+ "audio alternate setting %i, max size= %i\n", i,
+ adev->alt_max_pkt_size[i]);
}
return 0;
diff --git a/drivers/media/usb/cx231xx/cx231xx-avcore.c b/drivers/media/usb/cx231xx/cx231xx-avcore.c
index 40a6987..39e8879 100644
--- a/drivers/media/usb/cx231xx/cx231xx-avcore.c
+++ b/drivers/media/usb/cx231xx/cx231xx-avcore.c
@@ -22,12 +22,12 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include "cx231xx.h"
#include <linux/init.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/bitmap.h>
-#include <linux/usb.h>
#include <linux/i2c.h>
#include <linux/mm.h>
#include <linux/mutex.h>
@@ -36,7 +36,6 @@
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
-#include "cx231xx.h"
#include "cx231xx-dif.h"
#define TUNER_MODE_FM_RADIO 0
@@ -83,10 +82,10 @@
cx231xx_send_gpio_cmd(dev, _gpio_direction, (u8 *)&value, 4, 0, 0);
verve_read_byte(dev, 0x07, &val);
- cx231xx_info(" verve_read_byte address0x07=0x%x\n", val);
+ dev_dbg(dev->dev, "verve_read_byte address0x07=0x%x\n", val);
verve_write_byte(dev, 0x07, 0xF4);
verve_read_byte(dev, 0x07, &val);
- cx231xx_info(" verve_read_byte address0x07=0x%x\n", val);
+ dev_dbg(dev->dev, "verve_read_byte address0x07=0x%x\n", val);
cx231xx_capture_start(dev, 1, Vbi);
@@ -156,22 +155,25 @@
while (afe_power_status != 0x18) {
status = afe_write_byte(dev, SUP_BLK_PWRDN, 0x18);
if (status < 0) {
- cx231xx_info(
- ": Init Super Block failed in send cmd\n");
+ dev_dbg(dev->dev,
+ "%s: Init Super Block failed in send cmd\n",
+ __func__);
break;
}
status = afe_read_byte(dev, SUP_BLK_PWRDN, &afe_power_status);
afe_power_status &= 0xff;
if (status < 0) {
- cx231xx_info(
- ": Init Super Block failed in receive cmd\n");
+ dev_dbg(dev->dev,
+ "%s: Init Super Block failed in receive cmd\n",
+ __func__);
break;
}
i++;
if (i == 10) {
- cx231xx_info(
- ": Init Super Block force break in loop !!!!\n");
+ dev_dbg(dev->dev,
+ "%s: Init Super Block force break in loop !!!!\n",
+ __func__);
status = -1;
break;
}
@@ -410,7 +412,7 @@
status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3,
0x00);
} else {
- cx231xx_info("Invalid AV mode input\n");
+ dev_dbg(dev->dev, "Invalid AV mode input\n");
status = -1;
}
break;
@@ -467,7 +469,7 @@
status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3,
0x40);
} else {
- cx231xx_info("Invalid AV mode input\n");
+ dev_dbg(dev->dev, "Invalid AV mode input\n");
status = -1;
}
} /* switch */
@@ -573,9 +575,9 @@
status = cx231xx_set_power_mode(dev,
POLARIS_AVMODE_ENXTERNAL_AV);
if (status < 0) {
- cx231xx_errdev("%s: set_power_mode : Failed to"
- " set Power - errCode [%d]!\n",
- __func__, status);
+ dev_err(dev->dev,
+ "%s: Failed to set Power - errCode [%d]!\n",
+ __func__, status);
return status;
}
}
@@ -591,8 +593,8 @@
status = cx231xx_set_power_mode(dev,
POLARIS_AVMODE_ANALOGT_TV);
if (status < 0) {
- cx231xx_errdev("%s: set_power_mode:Failed"
- " to set Power - errCode [%d]!\n",
+ dev_err(dev->dev,
+ "%s: Failed to set Power - errCode [%d]!\n",
__func__, status);
return status;
}
@@ -608,8 +610,8 @@
break;
default:
- cx231xx_errdev("%s: set_power_mode : Unknown Input %d !\n",
- __func__, INPUT(input)->type);
+ dev_err(dev->dev, "%s: Unknown Input %d !\n",
+ __func__, INPUT(input)->type);
break;
}
@@ -628,8 +630,8 @@
if (pin_type != dev->video_input) {
status = cx231xx_afe_adjust_ref_count(dev, pin_type);
if (status < 0) {
- cx231xx_errdev("%s: adjust_ref_count :Failed to set"
- "AFE input mux - errCode [%d]!\n",
+ dev_err(dev->dev,
+ "%s: adjust_ref_count :Failed to set AFE input mux - errCode [%d]!\n",
__func__, status);
return status;
}
@@ -638,9 +640,9 @@
/* call afe block to set video inputs */
status = cx231xx_afe_set_input_mux(dev, input);
if (status < 0) {
- cx231xx_errdev("%s: set_input_mux :Failed to set"
- " AFE input mux - errCode [%d]!\n",
- __func__, status);
+ dev_err(dev->dev,
+ "%s: set_input_mux :Failed to set AFE input mux - errCode [%d]!\n",
+ __func__, status);
return status;
}
@@ -670,8 +672,8 @@
/* Tell DIF object to go to baseband mode */
status = cx231xx_dif_set_standard(dev, DIF_USE_BASEBAND);
if (status < 0) {
- cx231xx_errdev("%s: cx231xx_dif set to By pass"
- " mode- errCode [%d]!\n",
+ dev_err(dev->dev,
+ "%s: cx231xx_dif set to By pass mode- errCode [%d]!\n",
__func__, status);
return status;
}
@@ -715,8 +717,8 @@
/* Tell DIF object to go to baseband mode */
status = cx231xx_dif_set_standard(dev, DIF_USE_BASEBAND);
if (status < 0) {
- cx231xx_errdev("%s: cx231xx_dif set to By pass"
- " mode- errCode [%d]!\n",
+ dev_err(dev->dev,
+ "%s: cx231xx_dif set to By pass mode- errCode [%d]!\n",
__func__, status);
return status;
}
@@ -790,9 +792,9 @@
status = cx231xx_dif_set_standard(dev,
DIF_USE_BASEBAND);
if (status < 0) {
- cx231xx_errdev("%s: cx231xx_dif set to By pass"
- " mode- errCode [%d]!\n",
- __func__, status);
+ dev_err(dev->dev,
+ "%s: cx231xx_dif set to By pass mode- errCode [%d]!\n",
+ __func__, status);
return status;
}
@@ -826,9 +828,9 @@
/* Reinitialize the DIF */
status = cx231xx_dif_set_standard(dev, dev->norm);
if (status < 0) {
- cx231xx_errdev("%s: cx231xx_dif set to By pass"
- " mode- errCode [%d]!\n",
- __func__, status);
+ dev_err(dev->dev,
+ "%s: cx231xx_dif set to By pass mode- errCode [%d]!\n",
+ __func__, status);
return status;
}
@@ -970,14 +972,14 @@
{
int status = 0;
- cx231xx_info("do_mode_ctrl_overrides : 0x%x\n",
- (unsigned int)dev->norm);
+ dev_dbg(dev->dev, "%s: 0x%x\n",
+ __func__, (unsigned int)dev->norm);
/* Change the DFE_CTRL3 bp_percent to fix flagging */
status = vid_blk_write_word(dev, DFE_CTRL3, 0xCD3F0280);
if (dev->norm & (V4L2_STD_NTSC | V4L2_STD_PAL_M)) {
- cx231xx_info("do_mode_ctrl_overrides NTSC\n");
+ dev_dbg(dev->dev, "%s: NTSC\n", __func__);
/* Move the close caption lines out of active video,
adjust the active video start point */
@@ -1004,7 +1006,7 @@
(FLD_HBLANK_CNT, 0x79));
} else if (dev->norm & V4L2_STD_SECAM) {
- cx231xx_info("do_mode_ctrl_overrides SECAM\n");
+ dev_dbg(dev->dev, "%s: SECAM\n", __func__);
status = cx231xx_read_modify_write_i2c_dword(dev,
VID_BLK_I2C_ADDRESS,
VERT_TIM_CTRL,
@@ -1031,7 +1033,7 @@
cx231xx_set_field
(FLD_HBLANK_CNT, 0x85));
} else {
- cx231xx_info("do_mode_ctrl_overrides PAL\n");
+ dev_dbg(dev->dev, "%s: PAL\n", __func__);
status = cx231xx_read_modify_write_i2c_dword(dev,
VID_BLK_I2C_ADDRESS,
VERT_TIM_CTRL,
@@ -1206,7 +1208,8 @@
/* This is just a casual suggestion to people adding
new boards in case they use a tuner type we don't
currently know about */
- printk(KERN_INFO "Unknown tuner type configuring SIF");
+ dev_info(dev->dev,
+ "Unknown tuner type configuring SIF");
break;
}
break;
@@ -1270,8 +1273,13 @@
int status = 0;
bool current_is_port_3;
- if (dev->board.dont_use_port_3)
- is_port_3 = false;
+ /*
+ * Should this code check dev->port_3_switch_enabled first
+ * to skip unnecessary reading of the register?
+ * If yes, the flag dev->port_3_switch_enabled must be initialized
+ * correctly.
+ */
+
status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER,
PWR_CTL_EN, value, 4);
if (status < 0)
@@ -1288,12 +1296,13 @@
else
value[0] &= ~I2C_DEMOD_EN;
- cx231xx_info("Changing the i2c master port to %d\n",
- is_port_3 ? 3 : 1);
-
status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
PWR_CTL_EN, value, 4);
+ /* remember status of the switch for usage in is_tuner */
+ if (status >= 0)
+ dev->port_3_switch_enabled = is_port_3;
+
return status;
}
@@ -1325,113 +1334,131 @@
for (i = 0x100; i < 0x140; i++) {
vid_blk_read_word(dev, i, &value);
- cx231xx_info("reg0x%x=0x%x\n", i, value);
+ dev_dbg(dev->dev, "reg0x%x=0x%x\n", i, value);
i = i+3;
}
for (i = 0x300; i < 0x400; i++) {
vid_blk_read_word(dev, i, &value);
- cx231xx_info("reg0x%x=0x%x\n", i, value);
+ dev_dbg(dev->dev, "reg0x%x=0x%x\n", i, value);
i = i+3;
}
for (i = 0x400; i < 0x440; i++) {
vid_blk_read_word(dev, i, &value);
- cx231xx_info("reg0x%x=0x%x\n", i, value);
+ dev_dbg(dev->dev, "reg0x%x=0x%x\n", i, value);
i = i+3;
}
vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value);
- cx231xx_info("AFE_CTRL_C2HH_SRC_CTRL=0x%x\n", value);
+ dev_dbg(dev->dev, "AFE_CTRL_C2HH_SRC_CTRL=0x%x\n", value);
vid_blk_write_word(dev, AFE_CTRL_C2HH_SRC_CTRL, 0x4485D390);
vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value);
- cx231xx_info("AFE_CTRL_C2HH_SRC_CTRL=0x%x\n", value);
+ dev_dbg(dev->dev, "AFE_CTRL_C2HH_SRC_CTRL=0x%x\n", value);
}
-void cx231xx_dump_SC_reg(struct cx231xx *dev)
+#if 0
+static void cx231xx_dump_SC_reg(struct cx231xx *dev)
{
u8 value[4] = { 0, 0, 0, 0 };
- cx231xx_info("cx231xx_dump_SC_reg!\n");
+ dev_dbg(dev->dev, "%s!\n", __func__);
cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, BOARD_CFG_STAT,
value, 4);
- cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", BOARD_CFG_STAT, value[0],
- value[1], value[2], value[3]);
+ dev_dbg(dev->dev,
+ "reg0x%x=0x%x 0x%x 0x%x 0x%x\n", BOARD_CFG_STAT, value[0],
+ value[1], value[2], value[3]);
cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS_MODE_REG,
value, 4);
- cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS_MODE_REG, value[0],
- value[1], value[2], value[3]);
+ dev_dbg(dev->dev,
+ "reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS_MODE_REG, value[0],
+ value[1], value[2], value[3]);
cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS1_CFG_REG,
value, 4);
- cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS1_CFG_REG, value[0],
- value[1], value[2], value[3]);
+ dev_dbg(dev->dev,
+ "reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS1_CFG_REG, value[0],
+ value[1], value[2], value[3]);
cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS1_LENGTH_REG,
value, 4);
- cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS1_LENGTH_REG, value[0],
- value[1], value[2], value[3]);
+ dev_dbg(dev->dev,
+ "reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS1_LENGTH_REG, value[0],
+ value[1], value[2], value[3]);
cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS2_CFG_REG,
value, 4);
- cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS2_CFG_REG, value[0],
- value[1], value[2], value[3]);
+ dev_dbg(dev->dev,
+ "reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS2_CFG_REG, value[0],
+ value[1], value[2], value[3]);
cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS2_LENGTH_REG,
value, 4);
- cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS2_LENGTH_REG, value[0],
- value[1], value[2], value[3]);
+ dev_dbg(dev->dev,
+ "reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS2_LENGTH_REG, value[0],
+ value[1], value[2], value[3]);
cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, EP_MODE_SET,
value, 4);
- cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", EP_MODE_SET, value[0],
- value[1], value[2], value[3]);
+ dev_dbg(dev->dev,
+ "reg0x%x=0x%x 0x%x 0x%x 0x%x\n", EP_MODE_SET, value[0],
+ value[1], value[2], value[3]);
cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN1,
value, 4);
- cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_PTN1, value[0],
- value[1], value[2], value[3]);
+ dev_dbg(dev->dev,
+ "reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_PTN1, value[0],
+ value[1], value[2], value[3]);
cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN2,
value, 4);
- cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_PTN2, value[0],
- value[1], value[2], value[3]);
+ dev_dbg(dev->dev,
+ "reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_PTN2, value[0],
+ value[1], value[2], value[3]);
cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN3,
value, 4);
- cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_PTN3, value[0],
- value[1], value[2], value[3]);
+ dev_dbg(dev->dev,
+ "reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_PTN3, value[0],
+ value[1], value[2], value[3]);
cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK0,
value, 4);
- cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_MASK0, value[0],
- value[1], value[2], value[3]);
+ dev_dbg(dev->dev,
+ "reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_MASK0, value[0],
+ value[1], value[2], value[3]);
cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK1,
value, 4);
- cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_MASK1, value[0],
- value[1], value[2], value[3]);
+ dev_dbg(dev->dev,
+ "reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_MASK1, value[0],
+ value[1], value[2], value[3]);
cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK2,
value, 4);
- cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_MASK2, value[0],
- value[1], value[2], value[3]);
+ dev_dbg(dev->dev,
+ "reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_MASK2, value[0],
+ value[1], value[2], value[3]);
cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_GAIN,
value, 4);
- cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_GAIN, value[0],
- value[1], value[2], value[3]);
+ dev_dbg(dev->dev,
+ "reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_GAIN, value[0],
+ value[1], value[2], value[3]);
cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_CAR_REG,
value, 4);
- cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_CAR_REG, value[0],
- value[1], value[2], value[3]);
+ dev_dbg(dev->dev,
+ "reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_CAR_REG, value[0],
+ value[1], value[2], value[3]);
cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_OT_CFG1,
value, 4);
- cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_OT_CFG1, value[0],
- value[1], value[2], value[3]);
+ dev_dbg(dev->dev,
+ "reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_OT_CFG1, value[0],
+ value[1], value[2], value[3]);
cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_OT_CFG2,
value, 4);
- cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_OT_CFG2, value[0],
- value[1], value[2], value[3]);
+ dev_dbg(dev->dev,
+ "reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_OT_CFG2, value[0],
+ value[1], value[2], value[3]);
cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN,
value, 4);
- cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", PWR_CTL_EN, value[0],
- value[1], value[2], value[3]);
-
-
+ dev_dbg(dev->dev,
+ "reg0x%x=0x%x 0x%x 0x%x 0x%x\n", PWR_CTL_EN, value[0],
+ value[1], value[2], value[3]);
}
+#endif
void cx231xx_Setup_AFE_for_LowIF(struct cx231xx *dev)
@@ -1497,7 +1524,7 @@
u32 standard = 0;
u8 value[4] = { 0, 0, 0, 0 };
- cx231xx_info("Enter cx231xx_set_Colibri_For_LowIF()\n");
+ dev_dbg(dev->dev, "Enter cx231xx_set_Colibri_For_LowIF()\n");
value[0] = (u8) 0x6F;
value[1] = (u8) 0x6F;
value[2] = (u8) 0x6F;
@@ -1517,7 +1544,7 @@
colibri_carrier_offset = cx231xx_Get_Colibri_CarrierOffset(mode,
standard);
- cx231xx_info("colibri_carrier_offset=%d, standard=0x%x\n",
+ dev_dbg(dev->dev, "colibri_carrier_offset=%d, standard=0x%x\n",
colibri_carrier_offset, standard);
/* Set the band Pass filter for DIF*/
@@ -1551,8 +1578,8 @@
u64 pll_freq_u64 = 0;
u32 i = 0;
- cx231xx_info("if_freq=%d;spectral_invert=0x%x;mode=0x%x\n",
- if_freq, spectral_invert, mode);
+ dev_dbg(dev->dev, "if_freq=%d;spectral_invert=0x%x;mode=0x%x\n",
+ if_freq, spectral_invert, mode);
if (mode == TUNER_MODE_FM_RADIO) {
@@ -1595,8 +1622,7 @@
if_freq = 16000000;
}
- cx231xx_info("Enter IF=%zu\n",
- ARRAY_SIZE(Dif_set_array));
+ dev_dbg(dev->dev, "Enter IF=%zu\n", ARRAY_SIZE(Dif_set_array));
for (i = 0; i < ARRAY_SIZE(Dif_set_array); i++) {
if (Dif_set_array[i].if_freq == if_freq) {
vid_blk_write_word(dev,
@@ -1708,7 +1734,7 @@
u32 dif_misc_ctrl_value = 0;
u32 func_mode = 0;
- cx231xx_info("%s: setStandard to %x\n", __func__, standard);
+ dev_dbg(dev->dev, "%s: setStandard to %x\n", __func__, standard);
status = vid_blk_read_word(dev, DIF_MISC_CTRL, &dif_misc_ctrl_value);
if (standard != DIF_USE_BASEBAND)
@@ -2111,8 +2137,8 @@
{
int status = 0;
u32 dwval;
- cx231xx_info("cx231xx_tuner_post_channel_change dev->tuner_type =0%d\n",
- dev->tuner_type);
+ dev_dbg(dev->dev, "%s: dev->tuner_type =0%d\n",
+ __func__, dev->tuner_type);
/* Set the RF and IF k_agc values to 4 for PAL/NTSC and 8 for
* SECAM L/B/D standards */
status = vid_blk_read_word(dev, DIF_AGC_IF_REF, &dwval);
@@ -2213,8 +2239,8 @@
if (dev->power_mode != mode)
dev->power_mode = mode;
else {
- cx231xx_info(" setPowerMode::mode = %d, No Change req.\n",
- mode);
+ dev_dbg(dev->dev, "%s: mode = %d, No Change req.\n",
+ __func__, mode);
return 0;
}
@@ -2264,7 +2290,6 @@
case POLARIS_AVMODE_ANALOGT_TV:
tmp |= PWR_DEMOD_EN;
- tmp |= (I2C_DEMOD_EN);
value[0] = (u8) tmp;
value[1] = (u8) (tmp >> 8);
value[2] = (u8) (tmp >> 16);
@@ -2317,9 +2342,6 @@
}
if (dev->board.tuner_type != TUNER_ABSENT) {
- /* Enable tuner */
- cx231xx_enable_i2c_port_3(dev, true);
-
/* reset the Tuner */
if (dev->board.tuner_gpio)
cx231xx_gpio_set(dev, dev->board.tuner_gpio);
@@ -2363,7 +2385,7 @@
}
tmp &= (~PWR_AV_MODE);
- tmp |= POLARIS_AVMODE_DIGITAL | I2C_DEMOD_EN;
+ tmp |= POLARIS_AVMODE_DIGITAL;
value[0] = (u8) tmp;
value[1] = (u8) (tmp >> 8);
value[2] = (u8) (tmp >> 16);
@@ -2384,15 +2406,6 @@
}
if (dev->board.tuner_type != TUNER_ABSENT) {
- /*
- * Enable tuner
- * Hauppauge Exeter seems to need to do something different!
- */
- if (dev->model == CX231XX_BOARD_HAUPPAUGE_EXETER)
- cx231xx_enable_i2c_port_3(dev, false);
- else
- cx231xx_enable_i2c_port_3(dev, true);
-
/* reset the Tuner */
if (dev->board.tuner_gpio)
cx231xx_gpio_set(dev, dev->board.tuner_gpio);
@@ -2466,7 +2479,7 @@
u32 tmp = 0;
int status = 0;
- cx231xx_info("cx231xx_start_stream():: ep_mask = %x\n", ep_mask);
+ dev_dbg(dev->dev, "%s: ep_mask = %x\n", __func__, ep_mask);
status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, EP_MODE_SET,
value, 4);
if (status < 0)
@@ -2491,7 +2504,7 @@
u32 tmp = 0;
int status = 0;
- cx231xx_info("cx231xx_stop_stream():: ep_mask = %x\n", ep_mask);
+ dev_dbg(dev->dev, "%s: ep_mask = %x\n", __func__, ep_mask);
status =
cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, EP_MODE_SET, value, 4);
if (status < 0)
@@ -2519,61 +2532,72 @@
if (dev->udev->speed == USB_SPEED_HIGH) {
switch (media_type) {
case Audio:
- cx231xx_info("%s: Audio enter HANC\n", __func__);
+ dev_dbg(dev->dev,
+ "%s: Audio enter HANC\n", __func__);
status =
cx231xx_mode_register(dev, TS_MODE_REG, 0x9300);
break;
case Vbi:
- cx231xx_info("%s: set vanc registers\n", __func__);
+ dev_dbg(dev->dev,
+ "%s: set vanc registers\n", __func__);
status = cx231xx_mode_register(dev, TS_MODE_REG, 0x300);
break;
case Sliced_cc:
- cx231xx_info("%s: set hanc registers\n", __func__);
+ dev_dbg(dev->dev,
+ "%s: set hanc registers\n", __func__);
status =
cx231xx_mode_register(dev, TS_MODE_REG, 0x1300);
break;
case Raw_Video:
- cx231xx_info("%s: set video registers\n", __func__);
+ dev_dbg(dev->dev,
+ "%s: set video registers\n", __func__);
status = cx231xx_mode_register(dev, TS_MODE_REG, 0x100);
break;
case TS1_serial_mode:
- cx231xx_info("%s: set ts1 registers", __func__);
+ dev_dbg(dev->dev,
+ "%s: set ts1 registers", __func__);
- if (dev->board.has_417) {
- cx231xx_info(" MPEG\n");
- value &= 0xFFFFFFFC;
- value |= 0x3;
+ if (dev->board.has_417) {
+ dev_dbg(dev->dev,
+ "%s: MPEG\n", __func__);
+ value &= 0xFFFFFFFC;
+ value |= 0x3;
- status = cx231xx_mode_register(dev, TS_MODE_REG, value);
+ status = cx231xx_mode_register(dev,
+ TS_MODE_REG, value);
- val[0] = 0x04;
- val[1] = 0xA3;
- val[2] = 0x3B;
- val[3] = 0x00;
- status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
- TS1_CFG_REG, val, 4);
+ val[0] = 0x04;
+ val[1] = 0xA3;
+ val[2] = 0x3B;
+ val[3] = 0x00;
+ status = cx231xx_write_ctrl_reg(dev,
+ VRT_SET_REGISTER,
+ TS1_CFG_REG, val, 4);
- val[0] = 0x00;
- val[1] = 0x08;
- val[2] = 0x00;
- val[3] = 0x08;
- status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
- TS1_LENGTH_REG, val, 4);
-
- } else {
- cx231xx_info(" BDA\n");
- status = cx231xx_mode_register(dev, TS_MODE_REG, 0x101);
- status = cx231xx_mode_register(dev, TS1_CFG_REG, 0x010);
- }
+ val[0] = 0x00;
+ val[1] = 0x08;
+ val[2] = 0x00;
+ val[3] = 0x08;
+ status = cx231xx_write_ctrl_reg(dev,
+ VRT_SET_REGISTER,
+ TS1_LENGTH_REG, val, 4);
+ } else {
+ dev_dbg(dev->dev, "%s: BDA\n", __func__);
+ status = cx231xx_mode_register(dev,
+ TS_MODE_REG, 0x101);
+ status = cx231xx_mode_register(dev,
+ TS1_CFG_REG, 0x010);
+ }
break;
case TS1_parallel_mode:
- cx231xx_info("%s: set ts1 parallel mode registers\n",
- __func__);
+ dev_dbg(dev->dev,
+ "%s: set ts1 parallel mode registers\n",
+ __func__);
status = cx231xx_mode_register(dev, TS_MODE_REG, 0x100);
status = cx231xx_mode_register(dev, TS1_CFG_REG, 0x400);
break;
@@ -2926,8 +2950,9 @@
(nCnt > 0));
if (nCnt == 0)
- cx231xx_info("No ACK after %d msec -GPIO I2C failed!",
- nInit * 10);
+ dev_dbg(dev->dev,
+ "No ACK after %d msec -GPIO I2C failed!",
+ nInit * 10);
/*
* readAck
diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c
index 791f00c..ae05d59 100644
--- a/drivers/media/usb/cx231xx/cx231xx-cards.c
+++ b/drivers/media/usb/cx231xx/cx231xx-cards.c
@@ -20,12 +20,12 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include "cx231xx.h"
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/i2c.h>
-#include <linux/usb.h>
#include <media/tuner.h>
#include <media/tveeprom.h>
#include <media/v4l2-common.h>
@@ -35,7 +35,6 @@
#include "xc5000.h"
#include "tda18271.h"
-#include "cx231xx.h"
static int tuner = -1;
module_param(tuner, int, 0444);
@@ -104,8 +103,8 @@
.ctl_pin_status_mask = 0xFFFFFFC4,
.agc_analog_digital_select_gpio = 0x0c,
.gpio_pin_status_mask = 0x4001000,
- .tuner_i2c_master = 1,
- .demod_i2c_master = 2,
+ .tuner_i2c_master = I2C_1_MUX_3,
+ .demod_i2c_master = I2C_2,
.has_dvb = 1,
.demod_addr = 0x02,
.norm = V4L2_STD_PAL,
@@ -144,8 +143,8 @@
.ctl_pin_status_mask = 0xFFFFFFC4,
.agc_analog_digital_select_gpio = 0x0c,
.gpio_pin_status_mask = 0x4001000,
- .tuner_i2c_master = 1,
- .demod_i2c_master = 2,
+ .tuner_i2c_master = I2C_1_MUX_3,
+ .demod_i2c_master = I2C_2,
.has_dvb = 1,
.demod_addr = 0x32,
.norm = V4L2_STD_NTSC,
@@ -184,8 +183,8 @@
.ctl_pin_status_mask = 0xFFFFFFC4,
.agc_analog_digital_select_gpio = 0x1c,
.gpio_pin_status_mask = 0x4001000,
- .tuner_i2c_master = 1,
- .demod_i2c_master = 2,
+ .tuner_i2c_master = I2C_1_MUX_3,
+ .demod_i2c_master = I2C_2,
.has_dvb = 1,
.demod_addr = 0x02,
.norm = V4L2_STD_PAL,
@@ -225,8 +224,8 @@
.ctl_pin_status_mask = 0xFFFFFFC4,
.agc_analog_digital_select_gpio = 0x1c,
.gpio_pin_status_mask = 0x4001000,
- .tuner_i2c_master = 1,
- .demod_i2c_master = 2,
+ .tuner_i2c_master = I2C_1_MUX_3,
+ .demod_i2c_master = I2C_2,
.has_dvb = 1,
.demod_addr = 0x02,
.norm = V4L2_STD_PAL,
@@ -262,7 +261,6 @@
.norm = V4L2_STD_PAL,
.no_alt_vanc = 1,
.external_av = 1,
- .dont_use_port_3 = 1,
/* Actually, it has a 417, but it isn't working correctly.
* So set to 0 for now until someone can manage to get this
* to work reliably. */
@@ -297,8 +295,8 @@
.ctl_pin_status_mask = 0xFFFFFFC4,
.agc_analog_digital_select_gpio = 0x0c,
.gpio_pin_status_mask = 0x4001000,
- .tuner_i2c_master = 1,
- .demod_i2c_master = 2,
+ .tuner_i2c_master = I2C_1_MUX_3,
+ .demod_i2c_master = I2C_2,
.has_dvb = 1,
.demod_addr = 0x02,
.norm = V4L2_STD_PAL,
@@ -325,8 +323,8 @@
.ctl_pin_status_mask = 0xFFFFFFC4,
.agc_analog_digital_select_gpio = 0x0c,
.gpio_pin_status_mask = 0x4001000,
- .tuner_i2c_master = 1,
- .demod_i2c_master = 2,
+ .tuner_i2c_master = I2C_1_MUX_3,
+ .demod_i2c_master = I2C_2,
.has_dvb = 1,
.demod_addr = 0x32,
.norm = V4L2_STD_NTSC,
@@ -353,8 +351,8 @@
.ctl_pin_status_mask = 0xFFFFFFC4,
.agc_analog_digital_select_gpio = 0x0c,
.gpio_pin_status_mask = 0x4001000,
- .tuner_i2c_master = 1,
- .demod_i2c_master = 2,
+ .tuner_i2c_master = I2C_1_MUX_1,
+ .demod_i2c_master = I2C_2,
.has_dvb = 1,
.demod_addr = 0x0e,
.norm = V4L2_STD_NTSC,
@@ -390,7 +388,6 @@
.norm = V4L2_STD_NTSC,
.no_alt_vanc = 1,
.external_av = 1,
- .dont_use_port_3 = 1,
.input = {{
.type = CX231XX_VMUX_COMPOSITE1,
.vmux = CX231XX_VIN_2_1,
@@ -418,9 +415,9 @@
.tuner_scl_gpio = -1,
.tuner_sda_gpio = -1,
.gpio_pin_status_mask = 0x4001000,
- .tuner_i2c_master = 2,
- .demod_i2c_master = 1,
- .ir_i2c_master = 2,
+ .tuner_i2c_master = I2C_2,
+ .demod_i2c_master = I2C_1_MUX_3,
+ .ir_i2c_master = I2C_2,
.has_dvb = 1,
.demod_addr = 0x10,
.norm = V4L2_STD_PAL_M,
@@ -456,9 +453,9 @@
.tuner_scl_gpio = -1,
.tuner_sda_gpio = -1,
.gpio_pin_status_mask = 0x4001000,
- .tuner_i2c_master = 2,
- .demod_i2c_master = 1,
- .ir_i2c_master = 2,
+ .tuner_i2c_master = I2C_2,
+ .demod_i2c_master = I2C_1_MUX_3,
+ .ir_i2c_master = I2C_2,
.has_dvb = 1,
.demod_addr = 0x10,
.norm = V4L2_STD_NTSC_M,
@@ -494,9 +491,9 @@
.tuner_scl_gpio = -1,
.tuner_sda_gpio = -1,
.gpio_pin_status_mask = 0x4001000,
- .tuner_i2c_master = 2,
- .demod_i2c_master = 1,
- .ir_i2c_master = 2,
+ .tuner_i2c_master = I2C_2,
+ .demod_i2c_master = I2C_1_MUX_3,
+ .ir_i2c_master = I2C_2,
.rc_map_name = RC_MAP_PIXELVIEW_002T,
.has_dvb = 1,
.demod_addr = 0x10,
@@ -532,7 +529,6 @@
.norm = V4L2_STD_NTSC,
.no_alt_vanc = 1,
.external_av = 1,
- .dont_use_port_3 = 1,
.input = {{
.type = CX231XX_VMUX_COMPOSITE1,
@@ -587,7 +583,7 @@
.ctl_pin_status_mask = 0xFFFFFFC4,
.agc_analog_digital_select_gpio = 0x0c,
.gpio_pin_status_mask = 0x4001000,
- .tuner_i2c_master = 1,
+ .tuner_i2c_master = I2C_1_MUX_3,
.norm = V4L2_STD_PAL,
.input = {{
@@ -622,7 +618,7 @@
.ctl_pin_status_mask = 0xFFFFFFC4,
.agc_analog_digital_select_gpio = 0x0c,
.gpio_pin_status_mask = 0x4001000,
- .tuner_i2c_master = 1,
+ .tuner_i2c_master = I2C_1_MUX_3,
.norm = V4L2_STD_NTSC,
.input = {{
@@ -656,7 +652,6 @@
.norm = V4L2_STD_NTSC,
.no_alt_vanc = 1,
.external_av = 1,
- .dont_use_port_3 = 1,
.input = {{
.type = CX231XX_VMUX_COMPOSITE1,
.vmux = CX231XX_VIN_2_1,
@@ -677,13 +672,12 @@
.decoder = CX231XX_AVDECODER,
.output_mode = OUT_MODE_VIP11,
.ctl_pin_status_mask = 0xFFFFFFC4,
- .agc_analog_digital_select_gpio = 0x0c,
+ .agc_analog_digital_select_gpio = 0x0c,
/* According with PV CxPlrCAP.inf file */
.gpio_pin_status_mask = 0x4001000,
.norm = V4L2_STD_NTSC,
.no_alt_vanc = 1,
.external_av = 1,
- .dont_use_port_3 = 1,
/*.has_417 = 1, */
/* This board is believed to have a hardware encoding chip
* supporting mpeg1/2/4, but as the 417 is apparently not
@@ -718,8 +712,8 @@
.ctl_pin_status_mask = 0xFFFFFFC4,
.agc_analog_digital_select_gpio = 0x0c,
.gpio_pin_status_mask = 0x4001000,
- .tuner_i2c_master = 1,
- .demod_i2c_master = 2,
+ .tuner_i2c_master = I2C_1_MUX_3,
+ .demod_i2c_master = I2C_2,
.has_dvb = 1,
.demod_addr = 0x0e,
.norm = V4L2_STD_PAL,
@@ -757,8 +751,8 @@
.ctl_pin_status_mask = 0xFFFFFFC4,
.agc_analog_digital_select_gpio = 0x0c,
.gpio_pin_status_mask = 0x4001000,
- .tuner_i2c_master = 1,
- .demod_i2c_master = 2,
+ .tuner_i2c_master = I2C_1_MUX_3,
+ .demod_i2c_master = I2C_2,
.has_dvb = 1,
.demod_addr = 0x0e,
.norm = V4L2_STD_PAL,
@@ -861,9 +855,9 @@
if (dev->tuner_type == TUNER_XC5000) {
if (command == XC5000_TUNER_RESET) {
- cx231xx_info
- ("Tuner CB: RESET: cmd %d : tuner type %d \n",
- command, dev->tuner_type);
+ dev_dbg(dev->dev,
+ "Tuner CB: RESET: cmd %d : tuner type %d\n",
+ command, dev->tuner_type);
cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit,
1);
msleep(10);
@@ -921,8 +915,8 @@
cx231xx_set_model(dev);
- cx231xx_info("Identified as %s (card=%d)\n",
- dev->board.name, dev->model);
+ dev_info(dev->dev, "Identified as %s (card=%d)\n",
+ dev->board.name, dev->model);
/* set the direction for GPIO pins */
if (dev->board.tuner_gpio) {
@@ -980,25 +974,22 @@
}
-static int read_eeprom(struct cx231xx *dev, u8 *eedata, int len)
+static int read_eeprom(struct cx231xx *dev, struct i2c_client *client,
+ u8 *eedata, int len)
{
int ret = 0;
- u8 addr = 0xa0 >> 1;
u8 start_offset = 0;
int len_todo = len;
u8 *eedata_cur = eedata;
int i;
- struct i2c_msg msg_write = { .addr = addr, .flags = 0,
+ struct i2c_msg msg_write = { .addr = client->addr, .flags = 0,
.buf = &start_offset, .len = 1 };
- struct i2c_msg msg_read = { .addr = addr, .flags = I2C_M_RD };
-
- /* mutex_lock(&dev->i2c_lock); */
- cx231xx_enable_i2c_port_3(dev, false);
+ struct i2c_msg msg_read = { .addr = client->addr, .flags = I2C_M_RD };
/* start reading at offset 0 */
- ret = i2c_transfer(&dev->i2c_bus[1].i2c_adap, &msg_write, 1);
+ ret = i2c_transfer(client->adapter, &msg_write, 1);
if (ret < 0) {
- cx231xx_err("Can't read eeprom\n");
+ dev_err(dev->dev, "Can't read eeprom\n");
return ret;
}
@@ -1006,20 +997,18 @@
msg_read.len = (len_todo > 64) ? 64 : len_todo;
msg_read.buf = eedata_cur;
- ret = i2c_transfer(&dev->i2c_bus[1].i2c_adap, &msg_read, 1);
+ ret = i2c_transfer(client->adapter, &msg_read, 1);
if (ret < 0) {
- cx231xx_err("Can't read eeprom\n");
+ dev_err(dev->dev, "Can't read eeprom\n");
return ret;
}
eedata_cur += msg_read.len;
len_todo -= msg_read.len;
}
- cx231xx_enable_i2c_port_3(dev, true);
- /* mutex_unlock(&dev->i2c_lock); */
-
for (i = 0; i + 15 < len; i += 16)
- cx231xx_info("i2c eeprom %02x: %*ph\n", i, 16, &eedata[i]);
+ dev_dbg(dev->dev, "i2c eeprom %02x: %*ph\n",
+ i, 16, &eedata[i]);
return 0;
}
@@ -1036,22 +1025,26 @@
/* request some modules */
if (dev->board.decoder == CX231XX_AVDECODER) {
dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
- &dev->i2c_bus[0].i2c_adap,
+ cx231xx_get_i2c_adap(dev, I2C_0),
"cx25840", 0x88 >> 1, NULL);
if (dev->sd_cx25840 == NULL)
- cx231xx_info("cx25840 subdev registration failure\n");
+ dev_err(dev->dev,
+ "cx25840 subdev registration failure\n");
cx25840_call(dev, core, load_fw);
}
/* Initialize the tuner */
if (dev->board.tuner_type != TUNER_ABSENT) {
+ struct i2c_adapter *tuner_i2c = cx231xx_get_i2c_adap(dev,
+ dev->board.tuner_i2c_master);
dev->sd_tuner = v4l2_i2c_new_subdev(&dev->v4l2_dev,
- &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
+ tuner_i2c,
"tuner",
dev->tuner_addr, NULL);
if (dev->sd_tuner == NULL)
- cx231xx_info("tuner subdev registration failure\n");
+ dev_err(dev->dev,
+ "tuner subdev registration failure\n");
else
cx231xx_config_tuner(dev);
}
@@ -1062,9 +1055,14 @@
{
struct tveeprom tvee;
static u8 eeprom[256];
+ struct i2c_client client;
- read_eeprom(dev, eeprom, sizeof(eeprom));
- tveeprom_hauppauge_analog(&dev->i2c_bus[1].i2c_client,
+ memset(&client, 0, sizeof(client));
+ client.adapter = cx231xx_get_i2c_adap(dev, I2C_1_MUX_1);
+ client.addr = 0xa0 >> 1;
+
+ read_eeprom(dev, &client, eeprom, sizeof(eeprom));
+ tveeprom_hauppauge_analog(&client,
&tvee, eeprom + 0xc0);
break;
}
@@ -1152,7 +1150,7 @@
/* Query cx231xx to find what pcb config it is related to */
retval = initialize_cx231xx(dev);
if (retval < 0) {
- cx231xx_errdev("Failed to read PCB config\n");
+ dev_err(dev->dev, "Failed to read PCB config\n");
return retval;
}
@@ -1168,7 +1166,7 @@
retval = cx231xx_config(dev);
if (retval) {
- cx231xx_errdev("error configuring device\n");
+ dev_err(dev->dev, "error configuring device\n");
return -ENOMEM;
}
@@ -1178,8 +1176,9 @@
/* register i2c bus */
retval = cx231xx_dev_init(dev);
if (retval) {
- cx231xx_errdev("%s: cx231xx_i2c_register - errCode [%d]!\n",
- __func__, retval);
+ dev_err(dev->dev,
+ "%s: cx231xx_i2c_register - errCode [%d]!\n",
+ __func__, retval);
goto err_dev_init;
}
@@ -1200,8 +1199,8 @@
retval = cx231xx_config(dev);
if (retval) {
- cx231xx_errdev("%s: cx231xx_config - errCode [%d]!\n",
- __func__, retval);
+ dev_err(dev->dev, "%s: cx231xx_config - errCode [%d]!\n",
+ __func__, retval);
goto err_dev_init;
}
@@ -1217,11 +1216,11 @@
cx231xx_add_into_devlist(dev);
if (dev->board.has_417) {
- printk(KERN_INFO "attach 417 %d\n", dev->model);
+ dev_info(dev->dev, "attach 417 %d\n", dev->model);
if (cx231xx_417_register(dev) < 0) {
- printk(KERN_ERR
+ dev_err(dev->dev,
"%s() Failed to register 417 on VID_B\n",
- __func__);
+ __func__);
}
}
@@ -1285,7 +1284,8 @@
/* compute alternate max packet sizes for video */
idx = dev->current_pcb_config.hs_config_info[0].interface_info.video_index + 1;
if (idx >= dev->max_iad_interface_count) {
- cx231xx_errdev("Video PCB interface #%d doesn't exist\n", idx);
+ dev_err(dev->dev,
+ "Video PCB interface #%d doesn't exist\n", idx);
return -ENODEV;
}
@@ -1294,28 +1294,29 @@
dev->video_mode.end_point_addr = uif->altsetting[0].endpoint[isoc_pipe].desc.bEndpointAddress;
dev->video_mode.num_alt = uif->num_altsetting;
- cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
- dev->video_mode.end_point_addr,
- dev->video_mode.num_alt);
+ dev_info(dev->dev,
+ "video EndPoint Addr 0x%x, Alternate settings: %i\n",
+ dev->video_mode.end_point_addr,
+ dev->video_mode.num_alt);
dev->video_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->video_mode.num_alt, GFP_KERNEL);
- if (dev->video_mode.alt_max_pkt_size == NULL) {
- cx231xx_errdev("out of memory!\n");
+ if (dev->video_mode.alt_max_pkt_size == NULL)
return -ENOMEM;
- }
for (i = 0; i < dev->video_mode.num_alt; i++) {
u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.wMaxPacketSize);
dev->video_mode.alt_max_pkt_size[i] = (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
- cx231xx_info("Alternate setting %i, max size= %i\n", i,
- dev->video_mode.alt_max_pkt_size[i]);
+ dev_dbg(dev->dev,
+ "Alternate setting %i, max size= %i\n", i,
+ dev->video_mode.alt_max_pkt_size[i]);
}
/* VBI Init */
idx = dev->current_pcb_config.hs_config_info[0].interface_info.vanc_index + 1;
if (idx >= dev->max_iad_interface_count) {
- cx231xx_errdev("VBI PCB interface #%d doesn't exist\n", idx);
+ dev_err(dev->dev,
+ "VBI PCB interface #%d doesn't exist\n", idx);
return -ENODEV;
}
uif = udev->actconfig->interface[idx];
@@ -1325,16 +1326,15 @@
bEndpointAddress;
dev->vbi_mode.num_alt = uif->num_altsetting;
- cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
- dev->vbi_mode.end_point_addr,
- dev->vbi_mode.num_alt);
+ dev_info(dev->dev,
+ "VBI EndPoint Addr 0x%x, Alternate settings: %i\n",
+ dev->vbi_mode.end_point_addr,
+ dev->vbi_mode.num_alt);
/* compute alternate max packet sizes for vbi */
dev->vbi_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->vbi_mode.num_alt, GFP_KERNEL);
- if (dev->vbi_mode.alt_max_pkt_size == NULL) {
- cx231xx_errdev("out of memory!\n");
+ if (dev->vbi_mode.alt_max_pkt_size == NULL)
return -ENOMEM;
- }
for (i = 0; i < dev->vbi_mode.num_alt; i++) {
u16 tmp =
@@ -1342,8 +1342,9 @@
desc.wMaxPacketSize);
dev->vbi_mode.alt_max_pkt_size[i] =
(tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
- cx231xx_info("Alternate setting %i, max size= %i\n", i,
- dev->vbi_mode.alt_max_pkt_size[i]);
+ dev_dbg(dev->dev,
+ "Alternate setting %i, max size= %i\n", i,
+ dev->vbi_mode.alt_max_pkt_size[i]);
}
/* Sliced CC VBI init */
@@ -1351,7 +1352,8 @@
/* compute alternate max packet sizes for sliced CC */
idx = dev->current_pcb_config.hs_config_info[0].interface_info.hanc_index + 1;
if (idx >= dev->max_iad_interface_count) {
- cx231xx_errdev("Sliced CC PCB interface #%d doesn't exist\n", idx);
+ dev_err(dev->dev,
+ "Sliced CC PCB interface #%d doesn't exist\n", idx);
return -ENODEV;
}
uif = udev->actconfig->interface[idx];
@@ -1361,23 +1363,22 @@
bEndpointAddress;
dev->sliced_cc_mode.num_alt = uif->num_altsetting;
- cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
- dev->sliced_cc_mode.end_point_addr,
- dev->sliced_cc_mode.num_alt);
+ dev_info(dev->dev,
+ "sliced CC EndPoint Addr 0x%x, Alternate settings: %i\n",
+ dev->sliced_cc_mode.end_point_addr,
+ dev->sliced_cc_mode.num_alt);
dev->sliced_cc_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->sliced_cc_mode.num_alt, GFP_KERNEL);
-
- if (dev->sliced_cc_mode.alt_max_pkt_size == NULL) {
- cx231xx_errdev("out of memory!\n");
+ if (dev->sliced_cc_mode.alt_max_pkt_size == NULL)
return -ENOMEM;
- }
for (i = 0; i < dev->sliced_cc_mode.num_alt; i++) {
u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].
desc.wMaxPacketSize);
dev->sliced_cc_mode.alt_max_pkt_size[i] =
(tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
- cx231xx_info("Alternate setting %i, max size= %i\n", i,
- dev->sliced_cc_mode.alt_max_pkt_size[i]);
+ dev_dbg(dev->dev,
+ "Alternate setting %i, max size= %i\n", i,
+ dev->sliced_cc_mode.alt_max_pkt_size[i]);
}
return 0;
@@ -1391,6 +1392,7 @@
const struct usb_device_id *id)
{
struct usb_device *udev;
+ struct device *d = &interface->dev;
struct usb_interface *uif;
struct cx231xx *dev = NULL;
int retval = -ENODEV;
@@ -1401,6 +1403,7 @@
struct usb_interface_assoc_descriptor *assoc_desc;
ifnum = interface->altsetting[0].desc.bInterfaceNumber;
+ udev = usb_get_dev(interface_to_usbdev(interface));
/*
* Interface number 0 - IR interface (handled by mceusb driver)
@@ -1414,18 +1417,16 @@
nr = find_first_zero_bit(&cx231xx_devused, CX231XX_MAXBOARDS);
if (nr >= CX231XX_MAXBOARDS) {
/* No free device slots */
- cx231xx_err(DRIVER_NAME ": Supports only %i devices.\n",
- CX231XX_MAXBOARDS);
+ dev_err(d,
+ "Supports only %i devices.\n",
+ CX231XX_MAXBOARDS);
return -ENOMEM;
}
} while (test_and_set_bit(nr, &cx231xx_devused));
- udev = usb_get_dev(interface_to_usbdev(interface));
-
/* allocate memory for our device state and initialize it */
dev = devm_kzalloc(&udev->dev, sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
- cx231xx_err(DRIVER_NAME ": out of memory!\n");
clear_bit(nr, &cx231xx_devused);
return -ENOMEM;
}
@@ -1434,6 +1435,7 @@
dev->devno = nr;
dev->model = id->driver_info;
dev->video_mode.alt = -1;
+ dev->dev = d;
dev->interface_count++;
/* reset gpio dir and value */
@@ -1472,14 +1474,14 @@
speed = "unknown";
}
- cx231xx_info("New device %s %s @ %s Mbps "
- "(%04x:%04x) with %d interfaces\n",
- udev->manufacturer ? udev->manufacturer : "",
- udev->product ? udev->product : "",
- speed,
- le16_to_cpu(udev->descriptor.idVendor),
- le16_to_cpu(udev->descriptor.idProduct),
- dev->max_iad_interface_count);
+ dev_info(d,
+ "New device %s %s @ %s Mbps (%04x:%04x) with %d interfaces\n",
+ udev->manufacturer ? udev->manufacturer : "",
+ udev->product ? udev->product : "",
+ speed,
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct),
+ dev->max_iad_interface_count);
/* increment interface count */
dev->interface_count++;
@@ -1489,13 +1491,12 @@
assoc_desc = udev->actconfig->intf_assoc[0];
if (assoc_desc->bFirstInterface != ifnum) {
- cx231xx_err(DRIVER_NAME ": Not found "
- "matching IAD interface\n");
+ dev_err(d, "Not found matching IAD interface\n");
retval = -ENODEV;
goto err_if;
}
- cx231xx_info("registering interface %d\n", ifnum);
+ dev_dbg(d, "registering interface %d\n", ifnum);
/* save our data pointer in this interface device */
usb_set_intfdata(interface, dev);
@@ -1503,7 +1504,7 @@
/* Create v4l2 device */
retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev);
if (retval) {
- cx231xx_errdev("v4l2_device_register failed\n");
+ dev_err(d, "v4l2_device_register failed\n");
goto err_v4l2;
}
@@ -1520,7 +1521,8 @@
/* compute alternate max packet sizes for TS1 */
idx = dev->current_pcb_config.hs_config_info[0].interface_info.ts1_index + 1;
if (idx >= dev->max_iad_interface_count) {
- cx231xx_errdev("TS1 PCB interface #%d doesn't exist\n", idx);
+ dev_err(d, "TS1 PCB interface #%d doesn't exist\n",
+ idx);
retval = -ENODEV;
goto err_video_alt;
}
@@ -1531,13 +1533,13 @@
desc.bEndpointAddress;
dev->ts1_mode.num_alt = uif->num_altsetting;
- cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
- dev->ts1_mode.end_point_addr,
- dev->ts1_mode.num_alt);
+ dev_info(d,
+ "TS EndPoint Addr 0x%x, Alternate settings: %i\n",
+ dev->ts1_mode.end_point_addr,
+ dev->ts1_mode.num_alt);
dev->ts1_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->ts1_mode.num_alt, GFP_KERNEL);
if (dev->ts1_mode.alt_max_pkt_size == NULL) {
- cx231xx_errdev("out of memory!\n");
retval = -ENOMEM;
goto err_video_alt;
}
@@ -1548,8 +1550,8 @@
wMaxPacketSize);
dev->ts1_mode.alt_max_pkt_size[i] =
(tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
- cx231xx_info("Alternate setting %i, max size= %i\n", i,
- dev->ts1_mode.alt_max_pkt_size[i]);
+ dev_dbg(d, "Alternate setting %i, max size= %i\n",
+ i, dev->ts1_mode.alt_max_pkt_size[i]);
}
}
@@ -1613,10 +1615,9 @@
wake_up_interruptible_all(&dev->open);
if (dev->users) {
- cx231xx_warn
- ("device %s is open! Deregistration and memory "
- "deallocation are deferred on close.\n",
- video_device_node_name(dev->vdev));
+ dev_warn(dev->dev,
+ "device %s is open! Deregistration and memory deallocation are deferred on close.\n",
+ video_device_node_name(dev->vdev));
/* Even having users, it is safe to remove the RC i2c driver */
cx231xx_ir_exit(dev);
diff --git a/drivers/media/usb/cx231xx/cx231xx-core.c b/drivers/media/usb/cx231xx/cx231xx-core.c
index 180103e..4a3f28c 100644
--- a/drivers/media/usb/cx231xx/cx231xx-core.c
+++ b/drivers/media/usb/cx231xx/cx231xx-core.c
@@ -20,16 +20,15 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include "cx231xx.h"
#include <linux/init.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/slab.h>
-#include <linux/usb.h>
#include <linux/vmalloc.h>
#include <media/v4l2-common.h>
#include <media/tuner.h>
-#include "cx231xx.h"
#include "cx231xx-reg.h"
/* #define ENABLE_DEBUG_ISOC_FRAMES */
@@ -99,10 +98,10 @@
mutex_lock(&cx231xx_devlist_mutex);
list_add_tail(&ops->next, &cx231xx_extension_devlist);
- list_for_each_entry(dev, &cx231xx_devlist, devlist)
+ list_for_each_entry(dev, &cx231xx_devlist, devlist) {
ops->init(dev);
-
- printk(KERN_INFO DRIVER_NAME ": %s initialized\n", ops->name);
+ dev_info(dev->dev, "%s initialized\n", ops->name);
+ }
mutex_unlock(&cx231xx_devlist_mutex);
return 0;
}
@@ -113,11 +112,11 @@
struct cx231xx *dev = NULL;
mutex_lock(&cx231xx_devlist_mutex);
- list_for_each_entry(dev, &cx231xx_devlist, devlist)
+ list_for_each_entry(dev, &cx231xx_devlist, devlist) {
ops->fini(dev);
+ dev_info(dev->dev, "%s removed\n", ops->name);
+ }
-
- printk(KERN_INFO DRIVER_NAME ": %s removed\n", ops->name);
list_del(&ops->next);
mutex_unlock(&cx231xx_devlist_mutex);
}
@@ -227,10 +226,9 @@
/* call common vendor command request */
status = cx231xx_send_vendor_cmd(dev, &ven_req);
- if (status < 0) {
- cx231xx_info
- ("UsbInterface::sendCommand, failed with status -%d\n",
- status);
+ if (status < 0 && !dev->i2c_scan_running) {
+ dev_err(dev->dev, "%s: failed with status -%d\n",
+ __func__, status);
}
return status;
@@ -524,9 +522,9 @@
usb_set_interface(dev->udev, usb_interface_index,
dev->video_mode.alt);
if (errCode < 0) {
- cx231xx_errdev
- ("cannot change alt number to %d (error=%i)\n",
- dev->video_mode.alt, errCode);
+ dev_err(dev->dev,
+ "cannot change alt number to %d (error=%i)\n",
+ dev->video_mode.alt, errCode);
return errCode;
}
}
@@ -600,9 +598,9 @@
}
if (alt > 0 && max_pkt_size == 0) {
- cx231xx_errdev
- ("can't change interface %d alt no. to %d: Max. Pkt size = 0\n",
- usb_interface_index, alt);
+ dev_err(dev->dev,
+ "can't change interface %d alt no. to %d: Max. Pkt size = 0\n",
+ usb_interface_index, alt);
/*To workaround error number=-71 on EP0 for videograbber,
need add following codes.*/
if (dev->board.no_alt_vanc)
@@ -616,9 +614,9 @@
if (usb_interface_index > 0) {
status = usb_set_interface(dev->udev, usb_interface_index, alt);
if (status < 0) {
- cx231xx_errdev
- ("can't change interface %d alt no. to %d (err=%i)\n",
- usb_interface_index, alt, status);
+ dev_err(dev->dev,
+ "can't change interface %d alt no. to %d (err=%i)\n",
+ usb_interface_index, alt, status);
return status;
}
}
@@ -767,18 +765,17 @@
u32 *buffer;
buffer = kzalloc(4096, GFP_KERNEL);
- if (buffer == NULL) {
- cx231xx_info("out of mem\n");
+ if (buffer == NULL)
return -ENOMEM;
- }
memcpy(&buffer[0], firmware, 4096);
ret = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 5),
buffer, 4096, &actlen, 2000);
if (ret)
- cx231xx_info("bulk message failed: %d (%d/%d)", ret,
- size, actlen);
+ dev_err(dev->dev,
+ "bulk message failed: %d (%d/%d)", ret,
+ size, actlen);
else {
errCode = actlen != size ? -1 : 0;
}
@@ -987,12 +984,8 @@
cx231xx_uninit_isoc(dev);
dma_q->p_left_data = kzalloc(4096, GFP_KERNEL);
- if (dma_q->p_left_data == NULL) {
- cx231xx_info("out of mem\n");
+ if (dma_q->p_left_data == NULL)
return -ENOMEM;
- }
-
-
dev->video_mode.isoc_ctl.isoc_copy = isoc_copy;
dev->video_mode.isoc_ctl.num_bufs = num_bufs;
@@ -1018,14 +1011,16 @@
dev->video_mode.isoc_ctl.urb =
kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
if (!dev->video_mode.isoc_ctl.urb) {
- cx231xx_errdev("cannot alloc memory for usb buffers\n");
+ dev_err(dev->dev,
+ "cannot alloc memory for usb buffers\n");
return -ENOMEM;
}
dev->video_mode.isoc_ctl.transfer_buffer =
kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
if (!dev->video_mode.isoc_ctl.transfer_buffer) {
- cx231xx_errdev("cannot allocate memory for usbtransfer\n");
+ dev_err(dev->dev,
+ "cannot allocate memory for usbtransfer\n");
kfree(dev->video_mode.isoc_ctl.urb);
return -ENOMEM;
}
@@ -1045,7 +1040,8 @@
for (i = 0; i < dev->video_mode.isoc_ctl.num_bufs; i++) {
urb = usb_alloc_urb(max_packets, GFP_KERNEL);
if (!urb) {
- cx231xx_err("cannot alloc isoc_ctl.urb %i\n", i);
+ dev_err(dev->dev,
+ "cannot alloc isoc_ctl.urb %i\n", i);
cx231xx_uninit_isoc(dev);
return -ENOMEM;
}
@@ -1055,10 +1051,10 @@
usb_alloc_coherent(dev->udev, sb_size, GFP_KERNEL,
&urb->transfer_dma);
if (!dev->video_mode.isoc_ctl.transfer_buffer[i]) {
- cx231xx_err("unable to allocate %i bytes for transfer"
- " buffer %i%s\n",
- sb_size, i,
- in_interrupt() ? " while in int" : "");
+ dev_err(dev->dev,
+ "unable to allocate %i bytes for transfer buffer %i%s\n",
+ sb_size, i,
+ in_interrupt() ? " while in int" : "");
cx231xx_uninit_isoc(dev);
return -ENOMEM;
}
@@ -1090,8 +1086,9 @@
rc = usb_submit_urb(dev->video_mode.isoc_ctl.urb[i],
GFP_ATOMIC);
if (rc) {
- cx231xx_err("submit of urb %i failed (error=%i)\n", i,
- rc);
+ dev_err(dev->dev,
+ "submit of urb %i failed (error=%i)\n", i,
+ rc);
cx231xx_uninit_isoc(dev);
return rc;
}
@@ -1151,14 +1148,16 @@
dev->video_mode.bulk_ctl.urb =
kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
if (!dev->video_mode.bulk_ctl.urb) {
- cx231xx_errdev("cannot alloc memory for usb buffers\n");
+ dev_err(dev->dev,
+ "cannot alloc memory for usb buffers\n");
return -ENOMEM;
}
dev->video_mode.bulk_ctl.transfer_buffer =
kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
if (!dev->video_mode.bulk_ctl.transfer_buffer) {
- cx231xx_errdev("cannot allocate memory for usbtransfer\n");
+ dev_err(dev->dev,
+ "cannot allocate memory for usbtransfer\n");
kfree(dev->video_mode.bulk_ctl.urb);
return -ENOMEM;
}
@@ -1178,7 +1177,8 @@
for (i = 0; i < dev->video_mode.bulk_ctl.num_bufs; i++) {
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
- cx231xx_err("cannot alloc bulk_ctl.urb %i\n", i);
+ dev_err(dev->dev,
+ "cannot alloc bulk_ctl.urb %i\n", i);
cx231xx_uninit_bulk(dev);
return -ENOMEM;
}
@@ -1189,10 +1189,10 @@
usb_alloc_coherent(dev->udev, sb_size, GFP_KERNEL,
&urb->transfer_dma);
if (!dev->video_mode.bulk_ctl.transfer_buffer[i]) {
- cx231xx_err("unable to allocate %i bytes for transfer"
- " buffer %i%s\n",
- sb_size, i,
- in_interrupt() ? " while in int" : "");
+ dev_err(dev->dev,
+ "unable to allocate %i bytes for transfer buffer %i%s\n",
+ sb_size, i,
+ in_interrupt() ? " while in int" : "");
cx231xx_uninit_bulk(dev);
return -ENOMEM;
}
@@ -1212,8 +1212,8 @@
rc = usb_submit_urb(dev->video_mode.bulk_ctl.urb[i],
GFP_ATOMIC);
if (rc) {
- cx231xx_err("submit of urb %i failed (error=%i)\n", i,
- rc);
+ dev_err(dev->dev,
+ "submit of urb %i failed (error=%i)\n", i, rc);
cx231xx_uninit_bulk(dev);
return rc;
}
@@ -1300,6 +1300,15 @@
cx231xx_i2c_register(&dev->i2c_bus[1]);
cx231xx_i2c_register(&dev->i2c_bus[2]);
+ cx231xx_i2c_mux_register(dev, 0);
+ cx231xx_i2c_mux_register(dev, 1);
+
+ /* scan the real bus segments in the order of physical port numbers */
+ cx231xx_do_i2c_scan(dev, I2C_0);
+ cx231xx_do_i2c_scan(dev, I2C_1_MUX_1);
+ cx231xx_do_i2c_scan(dev, I2C_2);
+ cx231xx_do_i2c_scan(dev, I2C_1_MUX_3);
+
/* init hardware */
/* Note : with out calling set power mode function,
afe can not be set up correctly */
@@ -1307,18 +1316,18 @@
errCode = cx231xx_set_power_mode(dev,
POLARIS_AVMODE_ENXTERNAL_AV);
if (errCode < 0) {
- cx231xx_errdev
- ("%s: Failed to set Power - errCode [%d]!\n",
- __func__, errCode);
+ dev_err(dev->dev,
+ "%s: Failed to set Power - errCode [%d]!\n",
+ __func__, errCode);
return errCode;
}
} else {
errCode = cx231xx_set_power_mode(dev,
POLARIS_AVMODE_ANALOGT_TV);
if (errCode < 0) {
- cx231xx_errdev
- ("%s: Failed to set Power - errCode [%d]!\n",
- __func__, errCode);
+ dev_err(dev->dev,
+ "%s: Failed to set Power - errCode [%d]!\n",
+ __func__, errCode);
return errCode;
}
}
@@ -1331,42 +1340,43 @@
/* initialize Colibri block */
errCode = cx231xx_afe_init_super_block(dev, 0x23c);
if (errCode < 0) {
- cx231xx_errdev
- ("%s: cx231xx_afe init super block - errCode [%d]!\n",
- __func__, errCode);
+ dev_err(dev->dev,
+ "%s: cx231xx_afe init super block - errCode [%d]!\n",
+ __func__, errCode);
return errCode;
}
errCode = cx231xx_afe_init_channels(dev);
if (errCode < 0) {
- cx231xx_errdev
- ("%s: cx231xx_afe init channels - errCode [%d]!\n",
- __func__, errCode);
+ dev_err(dev->dev,
+ "%s: cx231xx_afe init channels - errCode [%d]!\n",
+ __func__, errCode);
return errCode;
}
/* Set DIF in By pass mode */
errCode = cx231xx_dif_set_standard(dev, DIF_USE_BASEBAND);
if (errCode < 0) {
- cx231xx_errdev
- ("%s: cx231xx_dif set to By pass mode - errCode [%d]!\n",
- __func__, errCode);
+ dev_err(dev->dev,
+ "%s: cx231xx_dif set to By pass mode - errCode [%d]!\n",
+ __func__, errCode);
return errCode;
}
/* I2S block related functions */
errCode = cx231xx_i2s_blk_initialize(dev);
if (errCode < 0) {
- cx231xx_errdev
- ("%s: cx231xx_i2s block initialize - errCode [%d]!\n",
- __func__, errCode);
+ dev_err(dev->dev,
+ "%s: cx231xx_i2s block initialize - errCode [%d]!\n",
+ __func__, errCode);
return errCode;
}
/* init control pins */
errCode = cx231xx_init_ctrl_pin_status(dev);
if (errCode < 0) {
- cx231xx_errdev("%s: cx231xx_init ctrl pins - errCode [%d]!\n",
- __func__, errCode);
+ dev_err(dev->dev,
+ "%s: cx231xx_init ctrl pins - errCode [%d]!\n",
+ __func__, errCode);
return errCode;
}
@@ -1391,9 +1401,9 @@
break;
}
if (errCode < 0) {
- cx231xx_errdev
- ("%s: cx231xx_AGC mode to Analog - errCode [%d]!\n",
- __func__, errCode);
+ dev_err(dev->dev,
+ "%s: cx231xx_AGC mode to Analog - errCode [%d]!\n",
+ __func__, errCode);
return errCode;
}
@@ -1404,9 +1414,7 @@
if (dev->board.has_dvb)
cx231xx_set_alt_setting(dev, INDEX_TS1, 0);
- /* set the I2C master port to 3 on channel 1 */
- errCode = cx231xx_enable_i2c_port_3(dev, true);
-
+ errCode = 0;
return errCode;
}
EXPORT_SYMBOL_GPL(cx231xx_dev_init);
@@ -1414,6 +1422,8 @@
void cx231xx_dev_uninit(struct cx231xx *dev)
{
/* Un Initialize I2C bus */
+ cx231xx_i2c_mux_unregister(dev, 1);
+ cx231xx_i2c_mux_unregister(dev, 0);
cx231xx_i2c_unregister(&dev->i2c_bus[2]);
cx231xx_i2c_unregister(&dev->i2c_bus[1]);
cx231xx_i2c_unregister(&dev->i2c_bus[0]);
@@ -1468,9 +1478,8 @@
/* call common vendor command request */
status = cx231xx_send_vendor_cmd(dev, &ven_req);
if (status < 0) {
- cx231xx_info
- ("UsbInterface::sendCommand, failed with status -%d\n",
- status);
+ dev_err(dev->dev, "%s: failed with status -%d\n",
+ __func__, status);
}
return status;
diff --git a/drivers/media/usb/cx231xx/cx231xx-dvb.c b/drivers/media/usb/cx231xx/cx231xx-dvb.c
index 6c7b5e2..dd600b9 100644
--- a/drivers/media/usb/cx231xx/cx231xx-dvb.c
+++ b/drivers/media/usb/cx231xx/cx231xx-dvb.c
@@ -19,11 +19,10 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include "cx231xx.h"
#include <linux/kernel.h>
#include <linux/slab.h>
-#include <linux/usb.h>
-#include "cx231xx.h"
#include <media/v4l2-common.h>
#include <media/videobuf-vmalloc.h>
@@ -46,11 +45,6 @@
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-#define dprintk(level, fmt, arg...) do { \
-if (debug >= level) \
- printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->name, ## arg); \
-} while (0)
-
#define CX231XX_DVB_NUM_BUFS 5
#define CX231XX_DVB_MAX_PACKETSIZE 564
#define CX231XX_DVB_MAX_PACKETS 64
@@ -197,9 +191,11 @@
break;
}
if (packet < 0) {
- dprintk(1, "URB status %d [%s].\n", status, errmsg);
+ dev_dbg(dev->dev,
+ "URB status %d [%s].\n", status, errmsg);
} else {
- dprintk(1, "URB packet %d, status %d [%s].\n",
+ dev_dbg(dev->dev,
+ "URB packet %d, status %d [%s].\n",
packet, status, errmsg);
}
}
@@ -265,12 +261,8 @@
struct cx231xx *dev = dvb->adapter.priv;
if (dev->USE_ISO) {
- cx231xx_info("DVB transfer mode is ISO.\n");
- mutex_lock(&dev->i2c_lock);
- cx231xx_enable_i2c_port_3(dev, false);
+ dev_dbg(dev->dev, "DVB transfer mode is ISO.\n");
cx231xx_set_alt_setting(dev, INDEX_TS1, 4);
- cx231xx_enable_i2c_port_3(dev, true);
- mutex_unlock(&dev->i2c_lock);
rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
if (rc < 0)
return rc;
@@ -280,7 +272,7 @@
dev->ts1_mode.max_pkt_size,
dvb_isoc_copy);
} else {
- cx231xx_info("DVB transfer mode is BULK.\n");
+ dev_dbg(dev->dev, "DVB transfer mode is BULK.\n");
cx231xx_set_alt_setting(dev, INDEX_TS1, 0);
rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
if (rc < 0)
@@ -378,24 +370,24 @@
struct xc5000_config cfg;
memset(&cfg, 0, sizeof(cfg));
- cfg.i2c_adap = &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap;
+ cfg.i2c_adap = cx231xx_get_i2c_adap(dev, dev->board.tuner_i2c_master);
cfg.i2c_addr = addr;
if (!dev->dvb->frontend) {
- printk(KERN_ERR "%s/2: dvb frontend not attached. "
+ dev_err(dev->dev, "%s/2: dvb frontend not attached. "
"Can't attach xc5000\n", dev->name);
return -EINVAL;
}
fe = dvb_attach(xc5000_attach, dev->dvb->frontend, &cfg);
if (!fe) {
- printk(KERN_ERR "%s/2: xc5000 attach failed\n", dev->name);
+ dev_err(dev->dev, "%s/2: xc5000 attach failed\n", dev->name);
dvb_frontend_detach(dev->dvb->frontend);
dev->dvb->frontend = NULL;
return -EINVAL;
}
- printk(KERN_INFO "%s/2: xc5000 attached\n", dev->name);
+ dev_info(dev->dev, "%s/2: xc5000 attached\n", dev->name);
return 0;
}
@@ -434,16 +426,17 @@
if (dops->init != NULL && !dev->xc_fw_load_done) {
- cx231xx_info("Reloading firmware for XC5000\n");
+ dev_dbg(dev->dev,
+ "Reloading firmware for XC5000\n");
status = dops->init(dev->dvb->frontend);
if (status == 0) {
dev->xc_fw_load_done = 1;
- cx231xx_info
- ("XC5000 firmware download completed\n");
+ dev_dbg(dev->dev,
+ "XC5000 firmware download completed\n");
} else {
dev->xc_fw_load_done = 0;
- cx231xx_info
- ("XC5000 firmware download failed !!!\n");
+ dev_dbg(dev->dev,
+ "XC5000 firmware download failed !!!\n");
}
}
@@ -466,7 +459,7 @@
result = dvb_register_adapter(&dvb->adapter, dev->name, module, device,
adapter_nr);
if (result < 0) {
- printk(KERN_WARNING
+ dev_warn(dev->dev,
"%s: dvb_register_adapter failed (errno = %d)\n",
dev->name, result);
goto fail_adapter;
@@ -480,7 +473,7 @@
/* register frontend */
result = dvb_register_frontend(&dvb->adapter, dvb->frontend);
if (result < 0) {
- printk(KERN_WARNING
+ dev_warn(dev->dev,
"%s: dvb_register_frontend failed (errno = %d)\n",
dev->name, result);
goto fail_frontend;
@@ -498,7 +491,8 @@
result = dvb_dmx_init(&dvb->demux);
if (result < 0) {
- printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n",
+ dev_warn(dev->dev,
+ "%s: dvb_dmx_init failed (errno = %d)\n",
dev->name, result);
goto fail_dmx;
}
@@ -508,15 +502,16 @@
dvb->dmxdev.capabilities = 0;
result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter);
if (result < 0) {
- printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n",
- dev->name, result);
+ dev_warn(dev->dev,
+ "%s: dvb_dmxdev_init failed (errno = %d)\n",
+ dev->name, result);
goto fail_dmxdev;
}
dvb->fe_hw.source = DMX_FRONTEND_0;
result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw);
if (result < 0) {
- printk(KERN_WARNING
+ dev_warn(dev->dev,
"%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n",
dev->name, result);
goto fail_fe_hw;
@@ -525,17 +520,17 @@
dvb->fe_mem.source = DMX_MEMORY_FE;
result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem);
if (result < 0) {
- printk(KERN_WARNING
- "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n",
- dev->name, result);
+ dev_warn(dev->dev,
+ "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n",
+ dev->name, result);
goto fail_fe_mem;
}
result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
if (result < 0) {
- printk(KERN_WARNING
- "%s: connect_frontend failed (errno = %d)\n", dev->name,
- result);
+ dev_warn(dev->dev,
+ "%s: connect_frontend failed (errno = %d)\n",
+ dev->name, result);
goto fail_fe_conn;
}
@@ -583,6 +578,8 @@
{
int result = 0;
struct cx231xx_dvb *dvb;
+ struct i2c_adapter *tuner_i2c;
+ struct i2c_adapter *demod_i2c;
if (!dev->board.has_dvb) {
/* This device does not support the extension */
@@ -592,13 +589,16 @@
dvb = kzalloc(sizeof(struct cx231xx_dvb), GFP_KERNEL);
if (dvb == NULL) {
- printk(KERN_INFO "cx231xx_dvb: memory allocation failed\n");
+ dev_info(dev->dev,
+ "cx231xx_dvb: memory allocation failed\n");
return -ENOMEM;
}
dev->dvb = dvb;
dev->cx231xx_set_analog_freq = cx231xx_set_analog_freq;
dev->cx231xx_reset_analog_tuner = cx231xx_reset_analog_tuner;
+ tuner_i2c = cx231xx_get_i2c_adap(dev, dev->board.tuner_i2c_master);
+ demod_i2c = cx231xx_get_i2c_adap(dev, dev->board.demod_i2c_master);
mutex_lock(&dev->lock);
cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
cx231xx_demod_reset(dev);
@@ -609,11 +609,11 @@
dev->dvb->frontend = dvb_attach(s5h1432_attach,
&dvico_s5h1432_config,
- &dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap);
+ demod_i2c);
if (dev->dvb->frontend == NULL) {
- printk(DRIVER_NAME
- ": Failed to attach s5h1432 front end\n");
+ dev_err(dev->dev,
+ "Failed to attach s5h1432 front end\n");
result = -EINVAL;
goto out_free;
}
@@ -622,7 +622,7 @@
dvb->frontend->callback = cx231xx_tuner_callback;
if (!dvb_attach(xc5000_attach, dev->dvb->frontend,
- &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
+ tuner_i2c,
&cnxt_rde250_tunerconfig)) {
result = -EINVAL;
goto out_free;
@@ -634,11 +634,11 @@
dev->dvb->frontend = dvb_attach(s5h1411_attach,
&xc5000_s5h1411_config,
- &dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap);
+ demod_i2c);
if (dev->dvb->frontend == NULL) {
- printk(DRIVER_NAME
- ": Failed to attach s5h1411 front end\n");
+ dev_err(dev->dev,
+ "Failed to attach s5h1411 front end\n");
result = -EINVAL;
goto out_free;
}
@@ -647,7 +647,7 @@
dvb->frontend->callback = cx231xx_tuner_callback;
if (!dvb_attach(xc5000_attach, dev->dvb->frontend,
- &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
+ tuner_i2c,
&cnxt_rdu250_tunerconfig)) {
result = -EINVAL;
goto out_free;
@@ -657,11 +657,11 @@
dev->dvb->frontend = dvb_attach(s5h1432_attach,
&dvico_s5h1432_config,
- &dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap);
+ demod_i2c);
if (dev->dvb->frontend == NULL) {
- printk(DRIVER_NAME
- ": Failed to attach s5h1432 front end\n");
+ dev_err(dev->dev,
+ "Failed to attach s5h1432 front end\n");
result = -EINVAL;
goto out_free;
}
@@ -670,7 +670,7 @@
dvb->frontend->callback = cx231xx_tuner_callback;
if (!dvb_attach(tda18271_attach, dev->dvb->frontend,
- 0x60, &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
+ 0x60, tuner_i2c,
&cnxt_rde253s_tunerconfig)) {
result = -EINVAL;
goto out_free;
@@ -681,11 +681,11 @@
dev->dvb->frontend = dvb_attach(s5h1411_attach,
&tda18271_s5h1411_config,
- &dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap);
+ demod_i2c);
if (dev->dvb->frontend == NULL) {
- printk(DRIVER_NAME
- ": Failed to attach s5h1411 front end\n");
+ dev_err(dev->dev,
+ "Failed to attach s5h1411 front end\n");
result = -EINVAL;
goto out_free;
}
@@ -694,7 +694,7 @@
dvb->frontend->callback = cx231xx_tuner_callback;
if (!dvb_attach(tda18271_attach, dev->dvb->frontend,
- 0x60, &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
+ 0x60, tuner_i2c,
&cnxt_rde253s_tunerconfig)) {
result = -EINVAL;
goto out_free;
@@ -702,16 +702,17 @@
break;
case CX231XX_BOARD_HAUPPAUGE_EXETER:
- printk(KERN_INFO "%s: looking for tuner / demod on i2c bus: %d\n",
- __func__, i2c_adapter_id(&dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap));
+ dev_info(dev->dev,
+ "%s: looking for tuner / demod on i2c bus: %d\n",
+ __func__, i2c_adapter_id(tuner_i2c));
dev->dvb->frontend = dvb_attach(lgdt3305_attach,
&hcw_lgdt3305_config,
- &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap);
+ tuner_i2c);
if (dev->dvb->frontend == NULL) {
- printk(DRIVER_NAME
- ": Failed to attach LG3305 front end\n");
+ dev_err(dev->dev,
+ "Failed to attach LG3305 front end\n");
result = -EINVAL;
goto out_free;
}
@@ -720,7 +721,7 @@
dvb->frontend->callback = cx231xx_tuner_callback;
dvb_attach(tda18271_attach, dev->dvb->frontend,
- 0x60, &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
+ 0x60, tuner_i2c,
&hcw_tda18271_config);
break;
@@ -728,12 +729,12 @@
dev->dvb->frontend = dvb_attach(si2165_attach,
&hauppauge_930C_HD_1113xx_si2165_config,
- &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap
+ tuner_i2c
);
if (dev->dvb->frontend == NULL) {
- printk(DRIVER_NAME
- ": Failed to attach SI2165 front end\n");
+ dev_err(dev->dev,
+ "Failed to attach SI2165 front end\n");
result = -EINVAL;
goto out_free;
}
@@ -745,7 +746,7 @@
dvb_attach(tda18271_attach, dev->dvb->frontend,
0x60,
- &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
+ tuner_i2c,
&hcw_tda18271_config);
dev->cx231xx_reset_analog_tuner = NULL;
@@ -761,12 +762,12 @@
dev->dvb->frontend = dvb_attach(si2165_attach,
&pctv_quatro_stick_1114xx_si2165_config,
- &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap
+ tuner_i2c
);
if (dev->dvb->frontend == NULL) {
- printk(DRIVER_NAME
- ": Failed to attach SI2165 front end\n");
+ dev_err(dev->dev,
+ "Failed to attach SI2165 front end\n");
result = -EINVAL;
goto out_free;
}
@@ -786,7 +787,7 @@
request_module("si2157");
client = i2c_new_device(
- &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
+ tuner_i2c,
&info);
if (client == NULL || client->dev.driver == NULL) {
dvb_frontend_detach(dev->dvb->frontend);
@@ -810,16 +811,17 @@
case CX231XX_BOARD_PV_PLAYTV_USB_HYBRID:
case CX231XX_BOARD_KWORLD_UB430_USB_HYBRID:
- printk(KERN_INFO "%s: looking for demod on i2c bus: %d\n",
- __func__, i2c_adapter_id(&dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap));
+ dev_info(dev->dev,
+ "%s: looking for demod on i2c bus: %d\n",
+ __func__, i2c_adapter_id(tuner_i2c));
dev->dvb->frontend = dvb_attach(mb86a20s_attach,
&pv_mb86a20s_config,
- &dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap);
+ demod_i2c);
if (dev->dvb->frontend == NULL) {
- printk(DRIVER_NAME
- ": Failed to attach mb86a20s demod\n");
+ dev_err(dev->dev,
+ "Failed to attach mb86a20s demod\n");
result = -EINVAL;
goto out_free;
}
@@ -828,30 +830,31 @@
dvb->frontend->callback = cx231xx_tuner_callback;
dvb_attach(tda18271_attach, dev->dvb->frontend,
- 0x60, &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
+ 0x60, tuner_i2c,
&pv_tda18271_config);
break;
default:
- printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card"
- " isn't supported yet\n", dev->name);
+ dev_err(dev->dev,
+ "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n",
+ dev->name);
break;
}
if (NULL == dvb->frontend) {
- printk(KERN_ERR
+ dev_err(dev->dev,
"%s/2: frontend initialization failed\n", dev->name);
result = -EINVAL;
goto out_free;
}
/* register everything */
- result = register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev);
+ result = register_dvb(dvb, THIS_MODULE, dev, dev->dev);
if (result < 0)
goto out_free;
- printk(KERN_INFO "Successfully loaded cx231xx-dvb\n");
+ dev_info(dev->dev, "Successfully loaded cx231xx-dvb\n");
ret:
cx231xx_set_mode(dev, CX231XX_SUSPEND);
diff --git a/drivers/media/usb/cx231xx/cx231xx-i2c.c b/drivers/media/usb/cx231xx/cx231xx-i2c.c
index 7c0f797..a29c345 100644
--- a/drivers/media/usb/cx231xx/cx231xx-i2c.c
+++ b/drivers/media/usb/cx231xx/cx231xx-i2c.c
@@ -20,14 +20,14 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include "cx231xx.h"
#include <linux/module.h>
#include <linux/kernel.h>
-#include <linux/usb.h>
#include <linux/i2c.h>
+#include <linux/i2c-mux.h>
#include <media/v4l2-common.h>
#include <media/tuner.h>
-#include "cx231xx.h"
/* ----------------------------------------------------------- */
@@ -54,10 +54,19 @@
} \
} while (0)
+static inline int get_real_i2c_port(struct cx231xx *dev, int bus_nr)
+{
+ if (bus_nr == 1)
+ return dev->port_3_switch_enabled ? I2C_1_MUX_3 : I2C_1_MUX_1;
+ return bus_nr;
+}
+
static inline bool is_tuner(struct cx231xx *dev, struct cx231xx_i2c *bus,
const struct i2c_msg *msg, int tuner_type)
{
- if (bus->nr != dev->board.tuner_i2c_master)
+ int i2c_port = get_real_i2c_port(dev, bus->nr);
+
+ if (i2c_port != dev->board.tuner_i2c_master)
return false;
if (msg->addr != dev->board.tuner_addr)
@@ -340,14 +349,15 @@
struct cx231xx *dev = bus->dev;
struct cx231xx_i2c_xfer_data req_data;
int status = 0;
+ u8 buf[1];
/* prepare xfer_data struct */
req_data.dev_addr = msg->addr;
- req_data.direction = msg->flags;
+ req_data.direction = I2C_M_RD;
req_data.saddr_len = 0;
req_data.saddr_dat = 0;
- req_data.buf_size = 0;
- req_data.p_buffer = NULL;
+ req_data.buf_size = 1;
+ req_data.p_buffer = buf;
/* usb send command */
status = dev->cx231xx_send_usb_command(bus, &req_data);
@@ -455,17 +465,14 @@
.algo = &cx231xx_algo,
};
-static struct i2c_client cx231xx_client_template = {
- .name = "cx231xx internal",
-};
-
/* ----------------------------------------------------------- */
/*
* i2c_devs
* incomplete list of known devices
*/
-static char *i2c_devs[128] = {
+static const char *i2c_devs[128] = {
+ [0x20 >> 1] = "demod",
[0x60 >> 1] = "colibri",
[0x88 >> 1] = "hammerhead",
[0x8e >> 1] = "CIR",
@@ -480,22 +487,34 @@
* cx231xx_do_i2c_scan()
* check i2c address range for devices
*/
-void cx231xx_do_i2c_scan(struct cx231xx *dev, struct i2c_client *c)
+void cx231xx_do_i2c_scan(struct cx231xx *dev, int i2c_port)
{
unsigned char buf;
int i, rc;
+ struct i2c_client client;
- cx231xx_info(": Checking for I2C devices ..\n");
+ if (!i2c_scan)
+ return;
+
+ /* Don't generate I2C errors during scan */
+ dev->i2c_scan_running = true;
+
+ memset(&client, 0, sizeof(client));
+ client.adapter = cx231xx_get_i2c_adap(dev, i2c_port);
+
for (i = 0; i < 128; i++) {
- c->addr = i;
- rc = i2c_master_recv(c, &buf, 0);
+ client.addr = i;
+ rc = i2c_master_recv(&client, &buf, 0);
if (rc < 0)
continue;
- cx231xx_info("%s: i2c scan: found device @ 0x%x [%s]\n",
- dev->name, i << 1,
- i2c_devs[i] ? i2c_devs[i] : "???");
+ dev_info(dev->dev,
+ "i2c scan: found device @ port %d addr 0x%x [%s]\n",
+ i2c_port,
+ i << 1,
+ i2c_devs[i] ? i2c_devs[i] : "???");
}
- cx231xx_info(": Completed Checking for I2C devices.\n");
+
+ dev->i2c_scan_running = false;
}
/*
@@ -509,23 +528,17 @@
BUG_ON(!dev->cx231xx_send_usb_command);
bus->i2c_adap = cx231xx_adap_template;
- bus->i2c_client = cx231xx_client_template;
- bus->i2c_adap.dev.parent = &dev->udev->dev;
+ bus->i2c_adap.dev.parent = dev->dev;
- strlcpy(bus->i2c_adap.name, bus->dev->name, sizeof(bus->i2c_adap.name));
+ snprintf(bus->i2c_adap.name, sizeof(bus->i2c_adap.name), "%s-%d", bus->dev->name, bus->nr);
bus->i2c_adap.algo_data = bus;
i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev);
i2c_add_adapter(&bus->i2c_adap);
- bus->i2c_client.adapter = &bus->i2c_adap;
-
- if (0 == bus->i2c_rc) {
- if (i2c_scan)
- cx231xx_do_i2c_scan(dev, &bus->i2c_client);
- } else
- cx231xx_warn("%s: i2c bus %d register FAILED\n",
- dev->name, bus->nr);
+ if (0 != bus->i2c_rc)
+ dev_warn(dev->dev,
+ "i2c bus %d register FAILED\n", bus->nr);
return bus->i2c_rc;
}
@@ -539,3 +552,62 @@
i2c_del_adapter(&bus->i2c_adap);
return 0;
}
+
+/*
+ * cx231xx_i2c_mux_select()
+ * switch i2c master number 1 between port1 and port3
+ */
+static int cx231xx_i2c_mux_select(struct i2c_adapter *adap,
+ void *mux_priv, u32 chan_id)
+{
+ struct cx231xx *dev = mux_priv;
+
+ return cx231xx_enable_i2c_port_3(dev, chan_id);
+}
+
+int cx231xx_i2c_mux_register(struct cx231xx *dev, int mux_no)
+{
+ struct i2c_adapter *i2c_parent = &dev->i2c_bus[1].i2c_adap;
+ /* what is the correct mux_dev? */
+ struct device *mux_dev = dev->dev;
+
+ dev->i2c_mux_adap[mux_no] = i2c_add_mux_adapter(i2c_parent,
+ mux_dev,
+ dev /* mux_priv */,
+ 0,
+ mux_no /* chan_id */,
+ 0 /* class */,
+ &cx231xx_i2c_mux_select,
+ NULL);
+
+ if (!dev->i2c_mux_adap[mux_no])
+ dev_warn(dev->dev,
+ "i2c mux %d register FAILED\n", mux_no);
+
+ return 0;
+}
+
+void cx231xx_i2c_mux_unregister(struct cx231xx *dev, int mux_no)
+{
+ i2c_del_mux_adapter(dev->i2c_mux_adap[mux_no]);
+ dev->i2c_mux_adap[mux_no] = NULL;
+}
+
+struct i2c_adapter *cx231xx_get_i2c_adap(struct cx231xx *dev, int i2c_port)
+{
+ switch (i2c_port) {
+ case I2C_0:
+ return &dev->i2c_bus[0].i2c_adap;
+ case I2C_1:
+ return &dev->i2c_bus[1].i2c_adap;
+ case I2C_2:
+ return &dev->i2c_bus[2].i2c_adap;
+ case I2C_1_MUX_1:
+ return dev->i2c_mux_adap[0];
+ case I2C_1_MUX_3:
+ return dev->i2c_mux_adap[1];
+ default:
+ return NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(cx231xx_get_i2c_adap);
diff --git a/drivers/media/usb/cx231xx/cx231xx-input.c b/drivers/media/usb/cx231xx/cx231xx-input.c
index 05f0434..15d8d1b 100644
--- a/drivers/media/usb/cx231xx/cx231xx-input.c
+++ b/drivers/media/usb/cx231xx/cx231xx-input.c
@@ -19,7 +19,6 @@
*/
#include "cx231xx.h"
-#include <linux/usb.h>
#include <linux/slab.h>
#include <linux/bitrev.h>
@@ -63,7 +62,7 @@
struct i2c_board_info info;
u8 ir_i2c_bus;
- dev_dbg(&dev->udev->dev, "%s\n", __func__);
+ dev_dbg(dev->dev, "%s\n", __func__);
/* Only initialize if a rc keycode map is defined */
if (!cx231xx_boards[dev->model].rc_map_name)
@@ -98,9 +97,10 @@
/* Load and bind ir-kbd-i2c */
ir_i2c_bus = cx231xx_boards[dev->model].ir_i2c_master;
- dev_dbg(&dev->udev->dev, "Trying to bind ir at bus %d, addr 0x%02x\n",
+ dev_dbg(dev->dev, "Trying to bind ir at bus %d, addr 0x%02x\n",
ir_i2c_bus, info.addr);
- dev->ir_i2c_client = i2c_new_device(&dev->i2c_bus[ir_i2c_bus].i2c_adap, &info);
+ dev->ir_i2c_client = i2c_new_device(
+ cx231xx_get_i2c_adap(dev, ir_i2c_bus), &info);
return 0;
}
diff --git a/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c b/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c
index 3052c4c..5bc7414 100644
--- a/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c
+++ b/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c
@@ -703,9 +703,9 @@
_current_scenario_idx = INDEX_BUSPOWER_DIF_ONLY;
break;
default:
- cx231xx_info("bad config in buspower!!!!\n");
- cx231xx_info("config_info=%x\n",
- (config_info & BUSPOWER_MASK));
+ dev_err(dev->dev,
+ "bad config in buspower!!!!\nconfig_info=%x\n",
+ config_info & BUSPOWER_MASK);
return 1;
}
} else { /* self-power */
@@ -768,9 +768,9 @@
_current_scenario_idx = INDEX_SELFPOWER_COMPRESSOR;
break;
default:
- cx231xx_info("bad senario!!!!!\n");
- cx231xx_info("config_info=%x\n",
- (config_info & SELFPOWER_MASK));
+ dev_err(dev->dev,
+ "bad senario!!!!!\nconfig_info=%x\n",
+ config_info & SELFPOWER_MASK);
return -ENODEV;
}
}
@@ -781,18 +781,29 @@
sizeof(struct pcb_config));
if (pcb_debug) {
- cx231xx_info("SC(0x00) register = 0x%x\n", config_info);
- cx231xx_info("scenario %d\n",
- (dev->current_pcb_config.index) + 1);
- cx231xx_info("type=%x\n", dev->current_pcb_config.type);
- cx231xx_info("mode=%x\n", dev->current_pcb_config.mode);
- cx231xx_info("speed=%x\n", dev->current_pcb_config.speed);
- cx231xx_info("ts1_source=%x\n",
- dev->current_pcb_config.ts1_source);
- cx231xx_info("ts2_source=%x\n",
- dev->current_pcb_config.ts2_source);
- cx231xx_info("analog_source=%x\n",
- dev->current_pcb_config.analog_source);
+ dev_info(dev->dev,
+ "SC(0x00) register = 0x%x\n", config_info);
+ dev_info(dev->dev,
+ "scenario %d\n",
+ (dev->current_pcb_config.index) + 1);
+ dev_info(dev->dev,
+ "type=%x\n",
+ dev->current_pcb_config.type);
+ dev_info(dev->dev,
+ "mode=%x\n",
+ dev->current_pcb_config.mode);
+ dev_info(dev->dev,
+ "speed=%x\n",
+ dev->current_pcb_config.speed);
+ dev_info(dev->dev,
+ "ts1_source=%x\n",
+ dev->current_pcb_config.ts1_source);
+ dev_info(dev->dev,
+ "ts2_source=%x\n",
+ dev->current_pcb_config.ts2_source);
+ dev_info(dev->dev,
+ "analog_source=%x\n",
+ dev->current_pcb_config.analog_source);
}
return 0;
diff --git a/drivers/media/usb/cx231xx/cx231xx-vbi.c b/drivers/media/usb/cx231xx/cx231xx-vbi.c
index c027942..80261ac 100644
--- a/drivers/media/usb/cx231xx/cx231xx-vbi.c
+++ b/drivers/media/usb/cx231xx/cx231xx-vbi.c
@@ -19,12 +19,12 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include "cx231xx.h"
#include <linux/init.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/bitmap.h>
-#include <linux/usb.h>
#include <linux/i2c.h>
#include <linux/mm.h>
#include <linux/mutex.h>
@@ -35,7 +35,6 @@
#include <media/msp3400.h>
#include <media/tuner.h>
-#include "cx231xx.h"
#include "cx231xx-vbi.h"
static inline void print_err_status(struct cx231xx *dev, int packet, int status)
@@ -69,11 +68,12 @@
break;
}
if (packet < 0) {
- cx231xx_err("URB status %d [%s].\n", status,
- errmsg);
+ dev_err(dev->dev,
+ "URB status %d [%s].\n", status, errmsg);
} else {
- cx231xx_err("URB packet %d, status %d [%s].\n",
- packet, status, errmsg);
+ dev_err(dev->dev,
+ "URB packet %d, status %d [%s].\n",
+ packet, status, errmsg);
}
}
@@ -316,8 +316,8 @@
case -ESHUTDOWN:
return;
default: /* error */
- cx231xx_err("urb completition error %d.\n",
- urb->status);
+ dev_err(dev->dev,
+ "urb completition error %d.\n", urb->status);
break;
}
@@ -331,8 +331,8 @@
urb->status = usb_submit_urb(urb, GFP_ATOMIC);
if (urb->status) {
- cx231xx_err("urb resubmit failed (error=%i)\n",
- urb->status);
+ dev_err(dev->dev, "urb resubmit failed (error=%i)\n",
+ urb->status);
}
}
@@ -344,7 +344,7 @@
struct urb *urb;
int i;
- cx231xx_info("called cx231xx_uninit_vbi_isoc\n");
+ dev_dbg(dev->dev, "called cx231xx_uninit_vbi_isoc\n");
dev->vbi_mode.bulk_ctl.nfields = -1;
for (i = 0; i < dev->vbi_mode.bulk_ctl.num_bufs; i++) {
@@ -393,7 +393,7 @@
struct urb *urb;
int rc;
- cx231xx_info("called cx231xx_vbi_isoc\n");
+ dev_dbg(dev->dev, "called cx231xx_vbi_isoc\n");
/* De-allocates all pending stuff */
cx231xx_uninit_vbi_isoc(dev);
@@ -419,14 +419,16 @@
dev->vbi_mode.bulk_ctl.urb = kzalloc(sizeof(void *) * num_bufs,
GFP_KERNEL);
if (!dev->vbi_mode.bulk_ctl.urb) {
- cx231xx_errdev("cannot alloc memory for usb buffers\n");
+ dev_err(dev->dev,
+ "cannot alloc memory for usb buffers\n");
return -ENOMEM;
}
dev->vbi_mode.bulk_ctl.transfer_buffer =
kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
if (!dev->vbi_mode.bulk_ctl.transfer_buffer) {
- cx231xx_errdev("cannot allocate memory for usbtransfer\n");
+ dev_err(dev->dev,
+ "cannot allocate memory for usbtransfer\n");
kfree(dev->vbi_mode.bulk_ctl.urb);
return -ENOMEM;
}
@@ -441,7 +443,8 @@
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
- cx231xx_err("cannot alloc bulk_ctl.urb %i\n", i);
+ dev_err(dev->dev,
+ "cannot alloc bulk_ctl.urb %i\n", i);
cx231xx_uninit_vbi_isoc(dev);
return -ENOMEM;
}
@@ -451,9 +454,10 @@
dev->vbi_mode.bulk_ctl.transfer_buffer[i] =
kzalloc(sb_size, GFP_KERNEL);
if (!dev->vbi_mode.bulk_ctl.transfer_buffer[i]) {
- cx231xx_err("unable to allocate %i bytes for transfer"
- " buffer %i%s\n", sb_size, i,
- in_interrupt() ? " while in int" : "");
+ dev_err(dev->dev,
+ "unable to allocate %i bytes for transfer buffer %i%s\n",
+ sb_size, i,
+ in_interrupt() ? " while in int" : "");
cx231xx_uninit_vbi_isoc(dev);
return -ENOMEM;
}
@@ -470,8 +474,8 @@
for (i = 0; i < dev->vbi_mode.bulk_ctl.num_bufs; i++) {
rc = usb_submit_urb(dev->vbi_mode.bulk_ctl.urb[i], GFP_ATOMIC);
if (rc) {
- cx231xx_err("submit of urb %i failed (error=%i)\n", i,
- rc);
+ dev_err(dev->dev,
+ "submit of urb %i failed (error=%i)\n", i, rc);
cx231xx_uninit_vbi_isoc(dev);
return rc;
}
@@ -522,7 +526,7 @@
struct cx231xx_buffer *buf)
{
/* Advice that buffer was filled */
- /* cx231xx_info("[%p/%d] wakeup\n", buf, buf->vb.i); */
+ /* dev_dbg(dev->dev, "[%p/%d] wakeup\n", buf, buf->vb.i); */
buf->vb.state = VIDEOBUF_DONE;
buf->vb.field_count++;
@@ -614,7 +618,7 @@
char *outp;
if (list_empty(&dma_q->active)) {
- cx231xx_err("No active queue to serve\n");
+ dev_err(dev->dev, "No active queue to serve\n");
dev->vbi_mode.bulk_ctl.buf = NULL;
*buf = NULL;
return;
diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c
index 425080b..d72d74e 100644
--- a/drivers/media/usb/cx231xx/cx231xx-video.c
+++ b/drivers/media/usb/cx231xx/cx231xx-video.c
@@ -22,12 +22,12 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include "cx231xx.h"
#include <linux/init.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/bitmap.h>
-#include <linux/usb.h>
#include <linux/i2c.h>
#include <linux/mm.h>
#include <linux/mutex.h>
@@ -41,10 +41,9 @@
#include "dvb_frontend.h"
-#include "cx231xx.h"
#include "cx231xx-vbi.h"
-#define CX231XX_VERSION "0.0.2"
+#define CX231XX_VERSION "0.0.3"
#define DRIVER_AUTHOR "Srinivasa Deevi <srinivasa.deevi@conexant.com>"
#define DRIVER_DESC "Conexant cx231xx based USB video device driver"
@@ -737,8 +736,9 @@
if (!dev->video_mode.bulk_ctl.num_bufs)
urb_init = 1;
}
- /*cx231xx_info("urb_init=%d dev->video_mode.max_pkt_size=%d\n",
- urb_init, dev->video_mode.max_pkt_size);*/
+ dev_dbg(dev->dev,
+ "urb_init=%d dev->video_mode.max_pkt_size=%d\n",
+ urb_init, dev->video_mode.max_pkt_size);
if (urb_init) {
dev->mode_tv = 0;
if (dev->USE_ISO)
@@ -809,7 +809,7 @@
cx231xx_set_audio_input(dev, dev->ctl_ainput);
- cx231xx_info("video_mux : %d\n", index);
+ dev_dbg(dev->dev, "video_mux : %d\n", index);
/* do mode control overrides if required */
cx231xx_do_mode_ctrl_overrides(dev);
@@ -861,7 +861,7 @@
static int check_dev(struct cx231xx *dev)
{
if (dev->state & DEV_DISCONNECTED) {
- cx231xx_errdev("v4l2 ioctl: device not present\n");
+ dev_err(dev->dev, "v4l2 ioctl: device not present\n");
return -ENODEV;
}
return 0;
@@ -953,12 +953,13 @@
return -EINVAL;
if (videobuf_queue_is_busy(&fh->vb_vidq)) {
- cx231xx_errdev("%s queue busy\n", __func__);
+ dev_err(dev->dev, "%s: queue busy\n", __func__);
return -EBUSY;
}
if (dev->stream_on && !fh->stream_on) {
- cx231xx_errdev("%s device in use by another fh\n", __func__);
+ dev_err(dev->dev,
+ "%s: device in use by another fh\n", __func__);
return -EBUSY;
}
@@ -967,7 +968,7 @@
dev->height = f->fmt.pix.height;
dev->format = fmt;
- v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED);
+ v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, MEDIA_BUS_FMT_FIXED);
call_all(dev, video, s_mbus_fmt, &mbus_fmt);
v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt);
@@ -1012,7 +1013,7 @@
resolution (since a standard change effects things like the number
of lines in VACT, etc) */
memset(&mbus_fmt, 0, sizeof(mbus_fmt));
- mbus_fmt.code = V4L2_MBUS_FMT_FIXED;
+ mbus_fmt.code = MEDIA_BUS_FMT_FIXED;
mbus_fmt.width = dev->width;
mbus_fmt.height = dev->height;
call_all(dev, video, s_mbus_fmt, &mbus_fmt);
@@ -1176,9 +1177,9 @@
int rc;
u32 if_frequency = 5400000;
- cx231xx_info("Enter vidioc_s_frequency()f->frequency=%d;f->type=%d\n",
- f->frequency, f->type);
- /*cx231xx_info("f->type: 1-radio 2-analogTV 3-digitalTV\n");*/
+ dev_dbg(dev->dev,
+ "Enter vidioc_s_frequency()f->frequency=%d;f->type=%d\n",
+ f->frequency, f->type);
rc = check_dev(dev);
if (rc < 0)
@@ -1213,13 +1214,14 @@
else if (dev->norm & V4L2_STD_SECAM_LC)
if_frequency = 1250000; /*1.25MHz */
- cx231xx_info("if_frequency is set to %d\n", if_frequency);
+ dev_dbg(dev->dev,
+ "if_frequency is set to %d\n", if_frequency);
cx231xx_set_Colibri_For_LowIF(dev, if_frequency, 1, 1);
update_HH_register_after_set_DIF(dev);
}
- cx231xx_info("Set New FREQUENCY to %d\n", f->frequency);
+ dev_dbg(dev->dev, "Set New FREQUENCY to %d\n", f->frequency);
return rc;
}
@@ -1523,7 +1525,8 @@
struct cx231xx *dev = fh->dev;
if (dev->vbi_stream_on && !fh->stream_on) {
- cx231xx_errdev("%s device in use by another fh\n", __func__);
+ dev_err(dev->dev,
+ "%s device in use by another fh\n", __func__);
return -EBUSY;
}
return vidioc_try_fmt_vbi_cap(file, priv, f);
@@ -1642,17 +1645,15 @@
#if 0
errCode = cx231xx_set_mode(dev, CX231XX_ANALOG_MODE);
if (errCode < 0) {
- cx231xx_errdev
- ("Device locked on digital mode. Can't open analog\n");
+ dev_err(dev->dev,
+ "Device locked on digital mode. Can't open analog\n");
return -EBUSY;
}
#endif
fh = kzalloc(sizeof(struct cx231xx_fh), GFP_KERNEL);
- if (!fh) {
- cx231xx_errdev("cx231xx-video.c: Out of memory?!\n");
+ if (!fh)
return -ENOMEM;
- }
if (mutex_lock_interruptible(&dev->lock)) {
kfree(fh);
return -ERESTARTSYS;
@@ -1736,8 +1737,8 @@
dev->radio_dev = NULL;
}
if (dev->vbi_dev) {
- cx231xx_info("V4L2 device %s deregistered\n",
- video_device_node_name(dev->vbi_dev));
+ dev_info(dev->dev, "V4L2 device %s deregistered\n",
+ video_device_node_name(dev->vbi_dev));
if (video_is_registered(dev->vbi_dev))
video_unregister_device(dev->vbi_dev);
else
@@ -1745,8 +1746,8 @@
dev->vbi_dev = NULL;
}
if (dev->vdev) {
- cx231xx_info("V4L2 device %s deregistered\n",
- video_device_node_name(dev->vdev));
+ dev_info(dev->dev, "V4L2 device %s deregistered\n",
+ video_device_node_name(dev->vdev));
if (dev->board.has_417)
cx231xx_417_unregister(dev);
@@ -2080,8 +2081,7 @@
{
int ret;
- cx231xx_info("%s: v4l2 driver version %s\n",
- dev->name, CX231XX_VERSION);
+ dev_info(dev->dev, "v4l2 driver version %s\n", CX231XX_VERSION);
/* set default norm */
dev->norm = V4L2_STD_PAL;
@@ -2119,7 +2119,7 @@
/* allocate and fill video video_device struct */
dev->vdev = cx231xx_vdev_init(dev, &cx231xx_video_template, "video");
if (!dev->vdev) {
- cx231xx_errdev("cannot allocate video_device.\n");
+ dev_err(dev->dev, "cannot allocate video_device.\n");
return -ENODEV;
}
@@ -2128,13 +2128,14 @@
ret = video_register_device(dev->vdev, VFL_TYPE_GRABBER,
video_nr[dev->devno]);
if (ret) {
- cx231xx_errdev("unable to register video device (error=%i).\n",
- ret);
+ dev_err(dev->dev,
+ "unable to register video device (error=%i).\n",
+ ret);
return ret;
}
- cx231xx_info("%s/0: registered device %s [v4l2]\n",
- dev->name, video_device_node_name(dev->vdev));
+ dev_info(dev->dev, "Registered video device %s [v4l2]\n",
+ video_device_node_name(dev->vdev));
/* Initialize VBI template */
cx231xx_vbi_template = cx231xx_video_template;
@@ -2144,7 +2145,7 @@
dev->vbi_dev = cx231xx_vdev_init(dev, &cx231xx_vbi_template, "vbi");
if (!dev->vbi_dev) {
- cx231xx_errdev("cannot allocate video_device.\n");
+ dev_err(dev->dev, "cannot allocate video_device.\n");
return -ENODEV;
}
dev->vbi_dev->ctrl_handler = &dev->ctrl_handler;
@@ -2152,34 +2153,32 @@
ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
vbi_nr[dev->devno]);
if (ret < 0) {
- cx231xx_errdev("unable to register vbi device\n");
+ dev_err(dev->dev, "unable to register vbi device\n");
return ret;
}
- cx231xx_info("%s/0: registered device %s\n",
- dev->name, video_device_node_name(dev->vbi_dev));
+ dev_info(dev->dev, "Registered VBI device %s\n",
+ video_device_node_name(dev->vbi_dev));
if (cx231xx_boards[dev->model].radio.type == CX231XX_RADIO) {
dev->radio_dev = cx231xx_vdev_init(dev, &cx231xx_radio_template,
"radio");
if (!dev->radio_dev) {
- cx231xx_errdev("cannot allocate video_device.\n");
+ dev_err(dev->dev,
+ "cannot allocate video_device.\n");
return -ENODEV;
}
dev->radio_dev->ctrl_handler = &dev->radio_ctrl_handler;
ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO,
radio_nr[dev->devno]);
if (ret < 0) {
- cx231xx_errdev("can't register radio device\n");
+ dev_err(dev->dev,
+ "can't register radio device\n");
return ret;
}
- cx231xx_info("Registered radio device as %s\n",
- video_device_node_name(dev->radio_dev));
+ dev_info(dev->dev, "Registered radio device as %s\n",
+ video_device_node_name(dev->radio_dev));
}
- cx231xx_info("V4L2 device registered as %s and %s\n",
- video_device_node_name(dev->vdev),
- video_device_node_name(dev->vbi_dev));
-
return 0;
}
diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h
index d607c5c..2ebe7c4 100644
--- a/drivers/media/usb/cx231xx/cx231xx.h
+++ b/drivers/media/usb/cx231xx/cx231xx.h
@@ -28,6 +28,7 @@
#include <linux/i2c.h>
#include <linux/workqueue.h>
#include <linux/mutex.h>
+#include <linux/usb.h>
#include <media/cx2341x.h>
@@ -322,10 +323,11 @@
};
enum CX231XX_I2C_MASTER_PORT {
- I2C_0 = 0,
- I2C_1 = 1,
- I2C_2 = 2,
- I2C_3 = 3
+ I2C_0 = 0, /* master 0 - internal connection */
+ I2C_1 = 1, /* master 1 - used with mux */
+ I2C_2 = 2, /* master 2 */
+ I2C_1_MUX_1 = 3, /* master 1 - port 1 (I2C_DEMOD_EN = 0) */
+ I2C_1_MUX_3 = 4 /* master 1 - port 3 (I2C_DEMOD_EN = 1) */
};
struct cx231xx_board {
@@ -367,7 +369,6 @@
unsigned int valid:1;
unsigned int no_alt_vanc:1;
unsigned int external_av:1;
- unsigned int dont_use_port_3:1;
unsigned char xclk, i2c_speed;
@@ -472,7 +473,6 @@
/* i2c i/o */
struct i2c_adapter i2c_adap;
- struct i2c_client i2c_client;
u32 i2c_rc;
/* different settings for each bus */
@@ -597,6 +597,7 @@
char name[30]; /* name (including minor) of the device */
int model; /* index in the device_data struct */
int devno; /* marks the number of this device */
+ struct device *dev; /* pointer to USB interface's dev */
struct cx231xx_board board;
@@ -609,6 +610,8 @@
unsigned int has_audio_class:1;
unsigned int has_alsa_audio:1;
+ unsigned int i2c_scan_running:1; /* true only during i2c_scan */
+
struct cx231xx_fmt *format;
struct v4l2_device v4l2_dev;
@@ -628,7 +631,10 @@
/* I2C adapters: Master 1 & 2 (External) & Master 3 (Internal only) */
struct cx231xx_i2c i2c_bus[3];
+ struct i2c_adapter *i2c_mux_adap[2];
+
unsigned int xc_fw_load_done:1;
+ unsigned int port_3_switch_enabled:1;
/* locks */
struct mutex gpio_i2c_lock;
struct mutex i2c_lock;
@@ -751,9 +757,12 @@
int cx231xx_reset_analog_tuner(struct cx231xx *dev);
/* Provided by cx231xx-i2c.c */
-void cx231xx_do_i2c_scan(struct cx231xx *dev, struct i2c_client *c);
+void cx231xx_do_i2c_scan(struct cx231xx *dev, int i2c_port);
int cx231xx_i2c_register(struct cx231xx_i2c *bus);
int cx231xx_i2c_unregister(struct cx231xx_i2c *bus);
+int cx231xx_i2c_mux_register(struct cx231xx *dev, int mux_no);
+void cx231xx_i2c_mux_unregister(struct cx231xx *dev, int mux_no);
+struct i2c_adapter *cx231xx_get_i2c_adap(struct cx231xx *dev, int i2c_port);
/* Internal block control functions */
int cx231xx_read_i2c_master(struct cx231xx *dev, u8 dev_addr, u16 saddr,
@@ -802,7 +811,6 @@
void reset_s5h1432_demod(struct cx231xx *dev);
void cx231xx_dump_HH_reg(struct cx231xx *dev);
void update_HH_register_after_set_DIF(struct cx231xx *dev);
-void cx231xx_dump_SC_reg(struct cx231xx *dev);
@@ -976,23 +984,6 @@
#define cx231xx_ir_exit(dev) (0)
#endif
-
-/* printk macros */
-
-#define cx231xx_err(fmt, arg...) do {\
- printk(KERN_ERR fmt , ##arg); } while (0)
-
-#define cx231xx_errdev(fmt, arg...) do {\
- printk(KERN_ERR "%s: "fmt,\
- dev->name , ##arg); } while (0)
-
-#define cx231xx_info(fmt, arg...) do {\
- printk(KERN_INFO "%s: "fmt,\
- dev->name , ##arg); } while (0)
-#define cx231xx_warn(fmt, arg...) do {\
- printk(KERN_WARNING "%s: "fmt,\
- dev->name , ##arg); } while (0)
-
static inline unsigned int norm_maxw(struct cx231xx *dev)
{
if (dev->board.max_range_640_480)
diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig
index 3d39fb7..cf2482f 100644
--- a/drivers/media/usb/dvb-usb-v2/Kconfig
+++ b/drivers/media/usb/dvb-usb-v2/Kconfig
@@ -31,7 +31,7 @@
Say Y here to support the Afatech AF9015 based DVB-T USB2.0 receiver
config DVB_USB_AF9035
- depends on !BACKPORT_KERNEL_3_4
+ depends on !KERNEL_3_4
tristate "Afatech AF9035 DVB-T USB2.0 support"
depends on m
depends on DVB_USB_V2
@@ -46,7 +46,7 @@
Say Y here to support the Afatech AF9035 based DVB USB receiver.
config DVB_USB_ANYSEE
- depends on !BACKPORT_KERNEL_3_4
+ depends on !KERNEL_3_4
tristate "Anysee DVB-T/C USB2.0 support"
depends on m
depends on DVB_USB_V2
@@ -138,7 +138,7 @@
Say Y here to support the MxL111SF USB2.0 DTV receiver.
config DVB_USB_RTL28XXU
- depends on !BACKPORT_KERNEL_3_4
+ depends on !KERNEL_3_4
tristate "Realtek RTL28xxU DVB USB support"
depends on m
depends on DVB_USB_V2 && I2C_MUX
@@ -157,11 +157,14 @@
Say Y here to support the Realtek RTL28xxU DVB USB receiver.
config DVB_USB_DVBSKY
- depends on !BACKPORT_KERNEL_3_4
+ depends on !KERNEL_3_4
tristate "DVBSky USB support"
depends on m
depends on DVB_USB_V2
select DVB_M88DS3103 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_SI2168 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_M88TS2022 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_SP2 if MEDIA_SUBDRV_AUTOSELECT
help
Say Y here to support the USB receivers from DVBSky.
diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c
index badc331..5541221 100644
--- a/drivers/media/usb/dvb-usb-v2/af9035.c
+++ b/drivers/media/usb/dvb-usb-v2/af9035.c
@@ -193,8 +193,8 @@
return af9035_wr_regs(d, reg, &val, 1);
}
-static int af9035_add_i2c_dev(struct dvb_usb_device *d, char *type, u8 addr,
- void *platform_data, struct i2c_adapter *adapter)
+static int af9035_add_i2c_dev(struct dvb_usb_device *d, const char *type,
+ u8 addr, void *platform_data, struct i2c_adapter *adapter)
{
int ret, num;
struct state *state = d_to_priv(d);
@@ -221,7 +221,7 @@
goto err;
}
- request_module(board_info.type);
+ request_module("%s", board_info.type);
/* register I2C device */
client = i2c_new_device(adapter, &board_info);
@@ -1171,6 +1171,7 @@
dev_dbg(&d->udev->dev, "adap->id=%d\n", adap->id);
+ memset(&si2168_config, 0, sizeof(si2168_config));
si2168_config.i2c_adapter = &adapter;
si2168_config.fe = &adap->fe[0];
si2168_config.ts_mode = SI2168_TS_SERIAL;
diff --git a/drivers/media/usb/dvb-usb-v2/anysee.c b/drivers/media/usb/dvb-usb-v2/anysee.c
index 8c5ec72..fd5d0e6 100644
--- a/drivers/media/usb/dvb-usb-v2/anysee.c
+++ b/drivers/media/usb/dvb-usb-v2/anysee.c
@@ -630,8 +630,8 @@
return ret;
}
-static int anysee_add_i2c_dev(struct dvb_usb_device *d, char *type, u8 addr,
- void *platform_data)
+static int anysee_add_i2c_dev(struct dvb_usb_device *d, const char *type,
+ u8 addr, void *platform_data)
{
int ret, num;
struct anysee_state *state = d_to_priv(d);
@@ -659,7 +659,7 @@
goto err;
}
- request_module(board_info.type);
+ request_module("%s", board_info.type);
/* register I2C device */
client = i2c_new_device(adapter, &board_info);
diff --git a/drivers/media/usb/dvb-usb-v2/dvbsky.c b/drivers/media/usb/dvb-usb-v2/dvbsky.c
index 0b41401..bfc85ed 100644
--- a/drivers/media/usb/dvb-usb-v2/dvbsky.c
+++ b/drivers/media/usb/dvb-usb-v2/dvbsky.c
@@ -21,10 +21,17 @@
#include "dvb_usb.h"
#include "m88ds3103.h"
#include "m88ts2022.h"
+#include "sp2.h"
+#include "si2168.h"
+#include "si2157.h"
#define DVBSKY_MSG_DELAY 0/*2000*/
#define DVBSKY_BUF_LEN 64
+static int dvb_usb_dvbsky_disable_rc;
+module_param_named(disable_rc, dvb_usb_dvbsky_disable_rc, int, 0644);
+MODULE_PARM_DESC(disable_rc, "Disable inbuilt IR receiver.");
+
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
struct dvbsky_state {
@@ -32,7 +39,9 @@
u8 ibuf[DVBSKY_BUF_LEN];
u8 obuf[DVBSKY_BUF_LEN];
u8 last_lock;
+ struct i2c_client *i2c_client_demod;
struct i2c_client *i2c_client_tuner;
+ struct i2c_client *i2c_client_ci;
/* fe hook functions*/
int (*fe_set_voltage)(struct dvb_frontend *fe,
@@ -96,8 +105,7 @@
obuf[2] = value;
ret = dvbsky_usb_generic_rw(d, obuf, 3, ibuf, 1);
if (ret)
- dev_err(&d->udev->dev, "%s: %s() failed=%d\n",
- KBUILD_MODNAME, __func__, ret);
+ dev_err(&d->udev->dev, "failed=%d\n", ret);
return ret;
}
@@ -114,7 +122,7 @@
if (num > 2) {
dev_err(&d->udev->dev,
- "dvbsky_usb: too many i2c messages[%d] than 2.", num);
+ "too many i2c messages[%d], max 2.", num);
ret = -EOPNOTSUPP;
goto i2c_error;
}
@@ -122,7 +130,7 @@
if (num == 1) {
if (msg[0].len > 60) {
dev_err(&d->udev->dev,
- "dvbsky_usb: too many i2c bytes[%d] than 60.",
+ "too many i2c bytes[%d], max 60.",
msg[0].len);
ret = -EOPNOTSUPP;
goto i2c_error;
@@ -136,8 +144,7 @@
ret = dvbsky_usb_generic_rw(d, obuf, 4,
ibuf, msg[0].len + 1);
if (ret)
- dev_err(&d->udev->dev, "%s: %s() failed=%d\n",
- KBUILD_MODNAME, __func__, ret);
+ dev_err(&d->udev->dev, "failed=%d\n", ret);
if (!ret)
memcpy(msg[0].buf, &ibuf[1], msg[0].len);
} else {
@@ -149,13 +156,12 @@
ret = dvbsky_usb_generic_rw(d, obuf,
msg[0].len + 3, ibuf, 1);
if (ret)
- dev_err(&d->udev->dev, "%s: %s() failed=%d\n",
- KBUILD_MODNAME, __func__, ret);
+ dev_err(&d->udev->dev, "failed=%d\n", ret);
}
} else {
if ((msg[0].len > 60) || (msg[1].len > 60)) {
dev_err(&d->udev->dev,
- "dvbsky_usb: too many i2c bytes[w-%d][r-%d] than 60.",
+ "too many i2c bytes[w-%d][r-%d], max 60.",
msg[0].len, msg[1].len);
ret = -EOPNOTSUPP;
goto i2c_error;
@@ -169,8 +175,7 @@
ret = dvbsky_usb_generic_rw(d, obuf,
msg[0].len + 4, ibuf, msg[1].len + 1);
if (ret)
- dev_err(&d->udev->dev, "%s: %s() failed=%d\n",
- KBUILD_MODNAME, __func__, ret);
+ dev_err(&d->udev->dev, "failed=%d\n", ret);
if (!ret)
memcpy(msg[1].buf, &ibuf[1], msg[1].len);
@@ -201,8 +206,7 @@
obuf[0] = 0x10;
ret = dvbsky_usb_generic_rw(d, obuf, 1, ibuf, 2);
if (ret)
- dev_err(&d->udev->dev, "%s: %s() failed=%d\n",
- KBUILD_MODNAME, __func__, ret);
+ dev_err(&d->udev->dev, "failed=%d\n", ret);
if (ret == 0)
code = (ibuf[0] << 8) | ibuf[1];
if (code != 0xffff) {
@@ -218,6 +222,11 @@
static int dvbsky_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc)
{
+ if (dvb_usb_dvbsky_disable_rc) {
+ rc->map_name = NULL;
+ return 0;
+ }
+
rc->allowed_protos = RC_BIT_RC5;
rc->query = dvbsky_rc_query;
rc->interval = 300;
@@ -265,8 +274,6 @@
if (i2c_transfer(&d->i2c_adap, msg, 2) == 2)
memcpy(mac, ibuf, 6);
- dev_info(&d->udev->dev, "dvbsky_usb MAC address=%pM\n", mac);
-
return 0;
}
@@ -362,6 +369,300 @@
return ret;
}
+static int dvbsky_usb_ci_set_voltage(struct dvb_frontend *fe,
+ fe_sec_voltage_t voltage)
+{
+ struct dvb_usb_device *d = fe_to_d(fe);
+ struct dvbsky_state *state = d_to_priv(d);
+ u8 value;
+
+ if (voltage == SEC_VOLTAGE_OFF)
+ value = 0;
+ else
+ value = 1;
+ dvbsky_gpio_ctrl(d, 0x00, value);
+
+ return state->fe_set_voltage(fe, voltage);
+}
+
+static int dvbsky_ci_ctrl(void *priv, u8 read, int addr,
+ u8 data, int *mem)
+{
+ struct dvb_usb_device *d = priv;
+ int ret = 0;
+ u8 command[4], respond[2], command_size, respond_size;
+
+ command[1] = (u8)((addr >> 8) & 0xff); /*high part of address*/
+ command[2] = (u8)(addr & 0xff); /*low part of address*/
+ if (read) {
+ command[0] = 0x71;
+ command_size = 3;
+ respond_size = 2;
+ } else {
+ command[0] = 0x70;
+ command[3] = data;
+ command_size = 4;
+ respond_size = 1;
+ }
+ ret = dvbsky_usb_generic_rw(d, command, command_size,
+ respond, respond_size);
+ if (ret)
+ goto err;
+ if (read)
+ *mem = respond[1];
+ return ret;
+err:
+ dev_err(&d->udev->dev, "ci control failed=%d\n", ret);
+ return ret;
+}
+
+static const struct m88ds3103_config dvbsky_s960c_m88ds3103_config = {
+ .i2c_addr = 0x68,
+ .clock = 27000000,
+ .i2c_wr_max = 33,
+ .clock_out = 0,
+ .ts_mode = M88DS3103_TS_CI,
+ .ts_clk = 10000,
+ .ts_clk_pol = 1,
+ .agc = 0x99,
+ .lnb_hv_pol = 0,
+ .lnb_en_pol = 1,
+};
+
+static int dvbsky_s960c_attach(struct dvb_usb_adapter *adap)
+{
+ struct dvbsky_state *state = adap_to_priv(adap);
+ struct dvb_usb_device *d = adap_to_d(adap);
+ int ret = 0;
+ /* demod I2C adapter */
+ struct i2c_adapter *i2c_adapter;
+ struct i2c_client *client_tuner, *client_ci;
+ struct i2c_board_info info;
+ struct sp2_config sp2_config;
+ struct m88ts2022_config m88ts2022_config = {
+ .clock = 27000000,
+ };
+ memset(&info, 0, sizeof(struct i2c_board_info));
+
+ /* attach demod */
+ adap->fe[0] = dvb_attach(m88ds3103_attach,
+ &dvbsky_s960c_m88ds3103_config,
+ &d->i2c_adap,
+ &i2c_adapter);
+ if (!adap->fe[0]) {
+ dev_err(&d->udev->dev, "dvbsky_s960ci_attach fail.\n");
+ ret = -ENODEV;
+ goto fail_attach;
+ }
+
+ /* attach tuner */
+ m88ts2022_config.fe = adap->fe[0];
+ strlcpy(info.type, "m88ts2022", I2C_NAME_SIZE);
+ info.addr = 0x60;
+ info.platform_data = &m88ts2022_config;
+ request_module("m88ts2022");
+ client_tuner = i2c_new_device(i2c_adapter, &info);
+ if (client_tuner == NULL || client_tuner->dev.driver == NULL) {
+ ret = -ENODEV;
+ goto fail_tuner_device;
+ }
+
+ if (!try_module_get(client_tuner->dev.driver->owner)) {
+ ret = -ENODEV;
+ goto fail_tuner_module;
+ }
+
+ /* attach ci controller */
+ memset(&sp2_config, 0, sizeof(sp2_config));
+ sp2_config.dvb_adap = &adap->dvb_adap;
+ sp2_config.priv = d;
+ sp2_config.ci_control = dvbsky_ci_ctrl;
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "sp2", I2C_NAME_SIZE);
+ info.addr = 0x40;
+ info.platform_data = &sp2_config;
+ request_module("sp2");
+ client_ci = i2c_new_device(&d->i2c_adap, &info);
+ if (client_ci == NULL || client_ci->dev.driver == NULL) {
+ ret = -ENODEV;
+ goto fail_ci_device;
+ }
+
+ if (!try_module_get(client_ci->dev.driver->owner)) {
+ ret = -ENODEV;
+ goto fail_ci_module;
+ }
+
+ /* delegate signal strength measurement to tuner */
+ adap->fe[0]->ops.read_signal_strength =
+ adap->fe[0]->ops.tuner_ops.get_rf_strength;
+
+ /* hook fe: need to resync the slave fifo when signal locks. */
+ state->fe_read_status = adap->fe[0]->ops.read_status;
+ adap->fe[0]->ops.read_status = dvbsky_usb_read_status;
+
+ /* hook fe: LNB off/on is control by Cypress usb chip. */
+ state->fe_set_voltage = adap->fe[0]->ops.set_voltage;
+ adap->fe[0]->ops.set_voltage = dvbsky_usb_ci_set_voltage;
+
+ state->i2c_client_tuner = client_tuner;
+ state->i2c_client_ci = client_ci;
+ return ret;
+fail_ci_module:
+ i2c_unregister_device(client_ci);
+fail_ci_device:
+ module_put(client_tuner->dev.driver->owner);
+fail_tuner_module:
+ i2c_unregister_device(client_tuner);
+fail_tuner_device:
+ dvb_frontend_detach(adap->fe[0]);
+fail_attach:
+ return ret;
+}
+
+static int dvbsky_t680c_attach(struct dvb_usb_adapter *adap)
+{
+ struct dvbsky_state *state = adap_to_priv(adap);
+ struct dvb_usb_device *d = adap_to_d(adap);
+ int ret = 0;
+ struct i2c_adapter *i2c_adapter;
+ struct i2c_client *client_demod, *client_tuner, *client_ci;
+ struct i2c_board_info info;
+ struct si2168_config si2168_config;
+ struct si2157_config si2157_config;
+ struct sp2_config sp2_config;
+
+ /* attach demod */
+ memset(&si2168_config, 0, sizeof(si2168_config));
+ si2168_config.i2c_adapter = &i2c_adapter;
+ si2168_config.fe = &adap->fe[0];
+ si2168_config.ts_mode = SI2168_TS_PARALLEL;
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "si2168", I2C_NAME_SIZE);
+ info.addr = 0x64;
+ info.platform_data = &si2168_config;
+
+ request_module(info.type);
+ client_demod = i2c_new_device(&d->i2c_adap, &info);
+ if (client_demod == NULL ||
+ client_demod->dev.driver == NULL)
+ goto fail_demod_device;
+ if (!try_module_get(client_demod->dev.driver->owner))
+ goto fail_demod_module;
+
+ /* attach tuner */
+ memset(&si2157_config, 0, sizeof(si2157_config));
+ si2157_config.fe = adap->fe[0];
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "si2157", I2C_NAME_SIZE);
+ info.addr = 0x60;
+ info.platform_data = &si2157_config;
+
+ request_module(info.type);
+ client_tuner = i2c_new_device(i2c_adapter, &info);
+ if (client_tuner == NULL ||
+ client_tuner->dev.driver == NULL)
+ goto fail_tuner_device;
+ if (!try_module_get(client_tuner->dev.driver->owner))
+ goto fail_tuner_module;
+
+ /* attach ci controller */
+ memset(&sp2_config, 0, sizeof(sp2_config));
+ sp2_config.dvb_adap = &adap->dvb_adap;
+ sp2_config.priv = d;
+ sp2_config.ci_control = dvbsky_ci_ctrl;
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "sp2", I2C_NAME_SIZE);
+ info.addr = 0x40;
+ info.platform_data = &sp2_config;
+
+ request_module(info.type);
+ client_ci = i2c_new_device(&d->i2c_adap, &info);
+
+ if (client_ci == NULL || client_ci->dev.driver == NULL)
+ goto fail_ci_device;
+
+ if (!try_module_get(client_ci->dev.driver->owner))
+ goto fail_ci_module;
+
+ state->i2c_client_demod = client_demod;
+ state->i2c_client_tuner = client_tuner;
+ state->i2c_client_ci = client_ci;
+ return ret;
+fail_ci_module:
+ i2c_unregister_device(client_ci);
+fail_ci_device:
+ module_put(client_tuner->dev.driver->owner);
+fail_tuner_module:
+ i2c_unregister_device(client_tuner);
+fail_tuner_device:
+ module_put(client_demod->dev.driver->owner);
+fail_demod_module:
+ i2c_unregister_device(client_demod);
+fail_demod_device:
+ ret = -ENODEV;
+ return ret;
+}
+
+static int dvbsky_t330_attach(struct dvb_usb_adapter *adap)
+{
+ struct dvbsky_state *state = adap_to_priv(adap);
+ struct dvb_usb_device *d = adap_to_d(adap);
+ int ret = 0;
+ struct i2c_adapter *i2c_adapter;
+ struct i2c_client *client_demod, *client_tuner;
+ struct i2c_board_info info;
+ struct si2168_config si2168_config;
+ struct si2157_config si2157_config;
+
+ /* attach demod */
+ memset(&si2168_config, 0, sizeof(si2168_config));
+ si2168_config.i2c_adapter = &i2c_adapter;
+ si2168_config.fe = &adap->fe[0];
+ si2168_config.ts_mode = SI2168_TS_PARALLEL | 0x40;
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "si2168", I2C_NAME_SIZE);
+ info.addr = 0x64;
+ info.platform_data = &si2168_config;
+
+ request_module(info.type);
+ client_demod = i2c_new_device(&d->i2c_adap, &info);
+ if (client_demod == NULL ||
+ client_demod->dev.driver == NULL)
+ goto fail_demod_device;
+ if (!try_module_get(client_demod->dev.driver->owner))
+ goto fail_demod_module;
+
+ /* attach tuner */
+ memset(&si2157_config, 0, sizeof(si2157_config));
+ si2157_config.fe = adap->fe[0];
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "si2157", I2C_NAME_SIZE);
+ info.addr = 0x60;
+ info.platform_data = &si2157_config;
+
+ request_module(info.type);
+ client_tuner = i2c_new_device(i2c_adapter, &info);
+ if (client_tuner == NULL ||
+ client_tuner->dev.driver == NULL)
+ goto fail_tuner_device;
+ if (!try_module_get(client_tuner->dev.driver->owner))
+ goto fail_tuner_module;
+
+ state->i2c_client_demod = client_demod;
+ state->i2c_client_tuner = client_tuner;
+ return ret;
+fail_tuner_module:
+ i2c_unregister_device(client_tuner);
+fail_tuner_device:
+ module_put(client_demod->dev.driver->owner);
+fail_demod_module:
+ i2c_unregister_device(client_demod);
+fail_demod_device:
+ ret = -ENODEV;
+ return ret;
+}
+
static int dvbsky_identify_state(struct dvb_usb_device *d, const char **name)
{
dvbsky_gpio_ctrl(d, 0x04, 1);
@@ -404,6 +705,18 @@
module_put(client->dev.driver->owner);
i2c_unregister_device(client);
}
+ client = state->i2c_client_demod;
+ /* remove I2C demod */
+ if (client) {
+ module_put(client->dev.driver->owner);
+ i2c_unregister_device(client);
+ }
+ client = state->i2c_client_ci;
+ /* remove I2C ci */
+ if (client) {
+ module_put(client->dev.driver->owner);
+ i2c_unregister_device(client);
+ }
}
/* DVB USB Driver stuff */
@@ -434,9 +747,104 @@
}
};
+static struct dvb_usb_device_properties dvbsky_s960c_props = {
+ .driver_name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .adapter_nr = adapter_nr,
+ .size_of_priv = sizeof(struct dvbsky_state),
+
+ .generic_bulk_ctrl_endpoint = 0x01,
+ .generic_bulk_ctrl_endpoint_response = 0x81,
+ .generic_bulk_ctrl_delay = DVBSKY_MSG_DELAY,
+
+ .i2c_algo = &dvbsky_i2c_algo,
+ .frontend_attach = dvbsky_s960c_attach,
+ .init = dvbsky_init,
+ .get_rc_config = dvbsky_get_rc_config,
+ .streaming_ctrl = dvbsky_streaming_ctrl,
+ .identify_state = dvbsky_identify_state,
+ .exit = dvbsky_exit,
+ .read_mac_address = dvbsky_read_mac_addr,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .stream = DVB_USB_STREAM_BULK(0x82, 8, 4096),
+ }
+ }
+};
+
+static struct dvb_usb_device_properties dvbsky_t680c_props = {
+ .driver_name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .adapter_nr = adapter_nr,
+ .size_of_priv = sizeof(struct dvbsky_state),
+
+ .generic_bulk_ctrl_endpoint = 0x01,
+ .generic_bulk_ctrl_endpoint_response = 0x81,
+ .generic_bulk_ctrl_delay = DVBSKY_MSG_DELAY,
+
+ .i2c_algo = &dvbsky_i2c_algo,
+ .frontend_attach = dvbsky_t680c_attach,
+ .init = dvbsky_init,
+ .get_rc_config = dvbsky_get_rc_config,
+ .streaming_ctrl = dvbsky_streaming_ctrl,
+ .identify_state = dvbsky_identify_state,
+ .exit = dvbsky_exit,
+ .read_mac_address = dvbsky_read_mac_addr,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .stream = DVB_USB_STREAM_BULK(0x82, 8, 4096),
+ }
+ }
+};
+
+static struct dvb_usb_device_properties dvbsky_t330_props = {
+ .driver_name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .adapter_nr = adapter_nr,
+ .size_of_priv = sizeof(struct dvbsky_state),
+
+ .generic_bulk_ctrl_endpoint = 0x01,
+ .generic_bulk_ctrl_endpoint_response = 0x81,
+ .generic_bulk_ctrl_delay = DVBSKY_MSG_DELAY,
+
+ .i2c_algo = &dvbsky_i2c_algo,
+ .frontend_attach = dvbsky_t330_attach,
+ .init = dvbsky_init,
+ .get_rc_config = dvbsky_get_rc_config,
+ .streaming_ctrl = dvbsky_streaming_ctrl,
+ .identify_state = dvbsky_identify_state,
+ .exit = dvbsky_exit,
+ .read_mac_address = dvbsky_read_mac_addr,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .stream = DVB_USB_STREAM_BULK(0x82, 8, 4096),
+ }
+ }
+};
+
static const struct usb_device_id dvbsky_id_table[] = {
{ DVB_USB_DEVICE(0x0572, 0x6831,
&dvbsky_s960_props, "DVBSky S960/S860", RC_MAP_DVBSKY) },
+ { DVB_USB_DEVICE(0x0572, 0x960c,
+ &dvbsky_s960c_props, "DVBSky S960CI", RC_MAP_DVBSKY) },
+ { DVB_USB_DEVICE(0x0572, 0x680c,
+ &dvbsky_t680c_props, "DVBSky T680CI", RC_MAP_DVBSKY) },
+ { DVB_USB_DEVICE(0x0572, 0x0320,
+ &dvbsky_t330_props, "DVBSky T330", RC_MAP_DVBSKY) },
+ { DVB_USB_DEVICE(USB_VID_TECHNOTREND,
+ USB_PID_TECHNOTREND_TVSTICK_CT2_4400,
+ &dvbsky_t330_props, "TechnoTrend TVStick CT2-4400",
+ RC_MAP_TT_1500) },
+ { DVB_USB_DEVICE(USB_VID_TECHNOTREND,
+ USB_PID_TECHNOTREND_CONNECT_CT2_4650_CI,
+ &dvbsky_t680c_props, "TechnoTrend TT-connect CT2-4650 CI",
+ RC_MAP_TT_1500) },
{ }
};
MODULE_DEVICE_TABLE(usb, dvbsky_id_table);
diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c
index 9f2c545..994de53 100644
--- a/drivers/media/usb/dvb-usb-v2/lmedm04.c
+++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c
@@ -817,20 +817,22 @@
case 0x1122:
switch (st->dvb_usb_lme2510_firmware) {
default:
- st->dvb_usb_lme2510_firmware = TUNER_S0194;
case TUNER_S0194:
fw_lme = fw_s0194;
ret = request_firmware(&fw, fw_lme, &udev->dev);
if (ret == 0) {
+ st->dvb_usb_lme2510_firmware = TUNER_S0194;
cold = 0;
break;
}
- st->dvb_usb_lme2510_firmware = TUNER_LG;
+ /* fall through */
case TUNER_LG:
fw_lme = fw_lg;
ret = request_firmware(&fw, fw_lme, &udev->dev);
- if (ret == 0)
+ if (ret == 0) {
+ st->dvb_usb_lme2510_firmware = TUNER_LG;
break;
+ }
st->dvb_usb_lme2510_firmware = TUNER_DEFAULT;
break;
}
@@ -838,26 +840,30 @@
case 0x1120:
switch (st->dvb_usb_lme2510_firmware) {
default:
- st->dvb_usb_lme2510_firmware = TUNER_S7395;
case TUNER_S7395:
fw_lme = fw_c_s7395;
ret = request_firmware(&fw, fw_lme, &udev->dev);
if (ret == 0) {
+ st->dvb_usb_lme2510_firmware = TUNER_S7395;
cold = 0;
break;
}
- st->dvb_usb_lme2510_firmware = TUNER_LG;
+ /* fall through */
case TUNER_LG:
fw_lme = fw_c_lg;
ret = request_firmware(&fw, fw_lme, &udev->dev);
- if (ret == 0)
+ if (ret == 0) {
+ st->dvb_usb_lme2510_firmware = TUNER_LG;
break;
- st->dvb_usb_lme2510_firmware = TUNER_S0194;
+ }
+ /* fall through */
case TUNER_S0194:
fw_lme = fw_c_s0194;
ret = request_firmware(&fw, fw_lme, &udev->dev);
- if (ret == 0)
+ if (ret == 0) {
+ st->dvb_usb_lme2510_firmware = TUNER_S0194;
break;
+ }
st->dvb_usb_lme2510_firmware = TUNER_DEFAULT;
cold = 0;
break;
diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
index 4bdb7ce..026a68e 100644
--- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
+++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
@@ -24,6 +24,9 @@
#include "rtl2830.h"
#include "rtl2832.h"
+#include "rtl2832_sdr.h"
+#include "mn88472.h"
+#include "mn88473.h"
#include "qt1010.h"
#include "mt2060.h"
@@ -35,25 +38,6 @@
#include "tua9001.h"
#include "r820t.h"
-/*
- * RTL2832_SDR module is in staging. That logic is added in order to avoid any
- * hard dependency to drivers/staging/ directory as we want compile mainline
- * driver even whole staging directory is missing.
- */
-#include <media/v4l2-subdev.h>
-
-#if IS_ENABLED(CPTCFG_DVB_RTL2832_SDR)
-struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c, const struct rtl2832_config *cfg,
- struct v4l2_subdev *sd);
-#else
-static inline struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c, const struct rtl2832_config *cfg,
- struct v4l2_subdev *sd)
-{
- return NULL;
-}
-#endif
#ifdef CPTCFG_MEDIA_ATTACH
#define dvb_attach_sdr(FUNCTION, ARGS...) ({ \
@@ -420,6 +404,8 @@
struct rtl28xxu_req req_tda18272 = {0x00c0, CMD_I2C_RD, 2, buf};
struct rtl28xxu_req req_r820t = {0x0034, CMD_I2C_RD, 1, buf};
struct rtl28xxu_req req_r828d = {0x0074, CMD_I2C_RD, 1, buf};
+ struct rtl28xxu_req req_mn88472 = {0xff38, CMD_I2C_RD, 1, buf};
+ struct rtl28xxu_req req_mn88473 = {0xff38, CMD_I2C_RD, 1, buf};
dev_dbg(&d->udev->dev, "%s:\n", __func__);
@@ -449,7 +435,7 @@
if (ret == 0 && buf[0] == 0xa1) {
priv->tuner = TUNER_RTL2832_FC0012;
priv->tuner_name = "FC0012";
- goto found;
+ goto tuner_found;
}
/* check FC0013 ID register; reg=00 val=a3 */
@@ -457,7 +443,7 @@
if (ret == 0 && buf[0] == 0xa3) {
priv->tuner = TUNER_RTL2832_FC0013;
priv->tuner_name = "FC0013";
- goto found;
+ goto tuner_found;
}
/* check MT2266 ID register; reg=00 val=85 */
@@ -465,7 +451,7 @@
if (ret == 0 && buf[0] == 0x85) {
priv->tuner = TUNER_RTL2832_MT2266;
priv->tuner_name = "MT2266";
- goto found;
+ goto tuner_found;
}
/* check FC2580 ID register; reg=01 val=56 */
@@ -473,7 +459,7 @@
if (ret == 0 && buf[0] == 0x56) {
priv->tuner = TUNER_RTL2832_FC2580;
priv->tuner_name = "FC2580";
- goto found;
+ goto tuner_found;
}
/* check MT2063 ID register; reg=00 val=9e || 9c */
@@ -481,7 +467,7 @@
if (ret == 0 && (buf[0] == 0x9e || buf[0] == 0x9c)) {
priv->tuner = TUNER_RTL2832_MT2063;
priv->tuner_name = "MT2063";
- goto found;
+ goto tuner_found;
}
/* check MAX3543 ID register; reg=00 val=38 */
@@ -489,7 +475,7 @@
if (ret == 0 && buf[0] == 0x38) {
priv->tuner = TUNER_RTL2832_MAX3543;
priv->tuner_name = "MAX3543";
- goto found;
+ goto tuner_found;
}
/* check TUA9001 ID register; reg=7e val=2328 */
@@ -497,7 +483,7 @@
if (ret == 0 && buf[0] == 0x23 && buf[1] == 0x28) {
priv->tuner = TUNER_RTL2832_TUA9001;
priv->tuner_name = "TUA9001";
- goto found;
+ goto tuner_found;
}
/* check MXL5007R ID register; reg=d9 val=14 */
@@ -505,7 +491,7 @@
if (ret == 0 && buf[0] == 0x14) {
priv->tuner = TUNER_RTL2832_MXL5007T;
priv->tuner_name = "MXL5007T";
- goto found;
+ goto tuner_found;
}
/* check E4000 ID register; reg=02 val=40 */
@@ -513,7 +499,7 @@
if (ret == 0 && buf[0] == 0x40) {
priv->tuner = TUNER_RTL2832_E4000;
priv->tuner_name = "E4000";
- goto found;
+ goto tuner_found;
}
/* check TDA18272 ID register; reg=00 val=c760 */
@@ -521,7 +507,7 @@
if (ret == 0 && (buf[0] == 0xc7 || buf[1] == 0x60)) {
priv->tuner = TUNER_RTL2832_TDA18272;
priv->tuner_name = "TDA18272";
- goto found;
+ goto tuner_found;
}
/* check R820T ID register; reg=00 val=69 */
@@ -529,7 +515,7 @@
if (ret == 0 && buf[0] == 0x69) {
priv->tuner = TUNER_RTL2832_R820T;
priv->tuner_name = "R820T";
- goto found;
+ goto tuner_found;
}
/* check R828D ID register; reg=00 val=69 */
@@ -537,13 +523,44 @@
if (ret == 0 && buf[0] == 0x69) {
priv->tuner = TUNER_RTL2832_R828D;
priv->tuner_name = "R828D";
- goto found;
+ goto tuner_found;
}
-
-found:
+tuner_found:
dev_dbg(&d->udev->dev, "%s: tuner=%s\n", __func__, priv->tuner_name);
+ /* probe slave demod */
+ if (priv->tuner == TUNER_RTL2832_R828D) {
+ /* power on MN88472 demod on GPIO0 */
+ ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_VAL, 0x01, 0x01);
+ if (ret)
+ goto err;
+
+ ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_DIR, 0x00, 0x01);
+ if (ret)
+ goto err;
+
+ ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_EN, 0x01, 0x01);
+ if (ret)
+ goto err;
+
+ /* check MN88472 answers */
+ ret = rtl28xxu_ctrl_msg(d, &req_mn88472);
+ if (ret == 0 && buf[0] == 0x02) {
+ dev_dbg(&d->udev->dev, "%s: MN88472 found\n", __func__);
+ priv->slave_demod = SLAVE_DEMOD_MN88472;
+ goto demod_found;
+ }
+
+ ret = rtl28xxu_ctrl_msg(d, &req_mn88473);
+ if (ret == 0 && buf[0] == 0x03) {
+ dev_dbg(&d->udev->dev, "%s: MN88473 found\n", __func__);
+ priv->slave_demod = SLAVE_DEMOD_MN88473;
+ goto demod_found;
+ }
+ }
+
+demod_found:
/* close demod I2C gate */
ret = rtl28xxu_ctrl_msg(d, &req_gate_close);
if (ret < 0)
@@ -818,7 +835,66 @@
/* set fe callback */
adap->fe[0]->callback = rtl2832u_frontend_callback;
+ if (priv->slave_demod) {
+ struct i2c_board_info info = {};
+ struct i2c_client *client;
+
+ /*
+ * We continue on reduced mode, without DVB-T2/C, using master
+ * demod, when slave demod fails.
+ */
+ ret = 0;
+
+ /* attach slave demodulator */
+ if (priv->slave_demod == SLAVE_DEMOD_MN88472) {
+ struct mn88472_config mn88472_config = {};
+
+ mn88472_config.fe = &adap->fe[1];
+ mn88472_config.i2c_wr_max = 22,
+ strlcpy(info.type, "mn88472", I2C_NAME_SIZE);
+ info.addr = 0x18;
+ info.platform_data = &mn88472_config;
+ request_module(info.type);
+ client = i2c_new_device(priv->demod_i2c_adapter, &info);
+ if (client == NULL || client->dev.driver == NULL) {
+ priv->slave_demod = SLAVE_DEMOD_NONE;
+ goto err_slave_demod_failed;
+ }
+
+ if (!try_module_get(client->dev.driver->owner)) {
+ i2c_unregister_device(client);
+ priv->slave_demod = SLAVE_DEMOD_NONE;
+ goto err_slave_demod_failed;
+ }
+
+ priv->i2c_client_slave_demod = client;
+ } else {
+ struct mn88473_config mn88473_config = {};
+
+ mn88473_config.fe = &adap->fe[1];
+ mn88473_config.i2c_wr_max = 22,
+ strlcpy(info.type, "mn88473", I2C_NAME_SIZE);
+ info.addr = 0x18;
+ info.platform_data = &mn88473_config;
+ request_module(info.type);
+ client = i2c_new_device(priv->demod_i2c_adapter, &info);
+ if (client == NULL || client->dev.driver == NULL) {
+ priv->slave_demod = SLAVE_DEMOD_NONE;
+ goto err_slave_demod_failed;
+ }
+
+ if (!try_module_get(client->dev.driver->owner)) {
+ i2c_unregister_device(client);
+ priv->slave_demod = SLAVE_DEMOD_NONE;
+ goto err_slave_demod_failed;
+ }
+
+ priv->i2c_client_slave_demod = client;
+ }
+ }
+
return 0;
+err_slave_demod_failed:
err:
dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
return ret;
@@ -984,7 +1060,7 @@
break;
}
- priv->client = client;
+ priv->i2c_client_tuner = client;
sd = i2c_get_clientdata(client);
i2c_set_adapdata(i2c_adap_internal, d);
@@ -1024,32 +1100,30 @@
&rtl28xxu_rtl2832_r820t_config, NULL);
break;
case TUNER_RTL2832_R828D:
- /* power off mn88472 demod on GPIO0 */
- ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_VAL, 0x00, 0x01);
- if (ret)
- goto err;
-
- ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_DIR, 0x00, 0x01);
- if (ret)
- goto err;
-
- ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_EN, 0x01, 0x01);
- if (ret)
- goto err;
-
- fe = dvb_attach(r820t_attach, adap->fe[0], &d->i2c_adap,
+ fe = dvb_attach(r820t_attach, adap->fe[0],
+ priv->demod_i2c_adapter,
&rtl2832u_r828d_config);
-
- /* Use tuner to get the signal strength */
adap->fe[0]->ops.read_signal_strength =
adap->fe[0]->ops.tuner_ops.get_rf_strength;
+
+ if (adap->fe[1]) {
+ fe = dvb_attach(r820t_attach, adap->fe[1],
+ priv->demod_i2c_adapter,
+ &rtl2832u_r828d_config);
+ adap->fe[1]->ops.read_signal_strength =
+ adap->fe[1]->ops.tuner_ops.get_rf_strength;
+ }
+
+ /* attach SDR */
+ dvb_attach_sdr(rtl2832_sdr_attach, adap->fe[0], &d->i2c_adap,
+ &rtl28xxu_rtl2832_r820t_config, NULL);
break;
default:
dev_err(&d->udev->dev, "%s: unknown tuner=%d\n", KBUILD_MODNAME,
priv->tuner);
}
- if (fe == NULL && priv->client == NULL) {
+ if (fe == NULL && priv->i2c_client_tuner == NULL) {
ret = -ENODEV;
goto err;
}
@@ -1097,11 +1171,19 @@
static void rtl28xxu_exit(struct dvb_usb_device *d)
{
struct rtl28xxu_priv *priv = d->priv;
- struct i2c_client *client = priv->client;
+ struct i2c_client *client;
dev_dbg(&d->udev->dev, "%s:\n", __func__);
/* remove I2C tuner */
+ client = priv->i2c_client_tuner;
+ if (client) {
+ module_put(client->dev.driver->owner);
+ i2c_unregister_device(client);
+ }
+
+ /* remove I2C slave demod */
+ client = priv->i2c_client_slave_demod;
if (client) {
module_put(client->dev.driver->owner);
i2c_unregister_device(client);
@@ -1201,13 +1283,6 @@
if (ret)
goto err;
- mdelay(5);
-
- /* enable ADC */
- ret = rtl28xx_wr_reg_mask(d, SYS_DEMOD_CTL, 0x48, 0x48);
- if (ret)
- goto err;
-
/* streaming EP: clear stall & reset */
ret = rtl28xx_wr_regs(d, USB_EPA_CTL, "\x00\x00", 2);
if (ret)
@@ -1222,11 +1297,6 @@
if (ret)
goto err;
- /* disable ADC */
- ret = rtl28xx_wr_reg_mask(d, SYS_DEMOD_CTL, 0x00, 0x48);
- if (ret)
- goto err;
-
/* disable PLL */
ret = rtl28xx_wr_reg_mask(d, SYS_DEMOD_CTL, 0x00, 0x80);
if (ret)
@@ -1244,6 +1314,38 @@
return ret;
}
+static int rtl2832u_frontend_ctrl(struct dvb_frontend *fe, int onoff)
+{
+ struct dvb_usb_device *d = fe_to_d(fe);
+ struct dvb_usb_adapter *adap = fe_to_adap(fe);
+ int ret;
+ u8 val;
+
+ dev_dbg(&d->udev->dev, "%s: fe=%d onoff=%d\n", __func__, fe->id, onoff);
+
+ /* control internal demod ADC */
+ if (fe->id == 0 && onoff)
+ val = 0x48; /* enable ADC */
+ else
+ val = 0x00; /* disable ADC */
+
+ ret = rtl28xx_wr_reg_mask(d, SYS_DEMOD_CTL, val, 0x48);
+ if (ret)
+ goto err;
+
+ /* bypass slave demod TS through master demod */
+ if (fe->id == 1 && onoff) {
+ ret = rtl2832_enable_external_ts_if(adap->fe[0]);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
#if IS_ENABLED(CPTCFG_RC_CORE)
static int rtl2831u_rc_query(struct dvb_usb_device *d)
{
@@ -1467,6 +1569,7 @@
.size_of_priv = sizeof(struct rtl28xxu_priv),
.power_ctrl = rtl2832u_power_ctrl,
+ .frontend_ctrl = rtl2832u_frontend_ctrl,
.i2c_algo = &rtl28xxu_i2c_algo,
.read_config = rtl2832u_read_config,
.frontend_attach = rtl2832u_frontend_attach,
diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h
index a26cab1..3e3ea9d 100644
--- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h
+++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h
@@ -57,7 +57,12 @@
u8 page; /* integrated demod active register page */
struct i2c_adapter *demod_i2c_adapter;
bool rc_active;
- struct i2c_client *client;
+ struct i2c_client *i2c_client_tuner;
+ struct i2c_client *i2c_client_slave_demod;
+ #define SLAVE_DEMOD_NONE 0
+ #define SLAVE_DEMOD_MN88472 1
+ #define SLAVE_DEMOD_MN88473 2
+ unsigned int slave_demod:2;
};
enum rtl28xxu_chip_id {
diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig
index 25da066..1378eca 100644
--- a/drivers/media/usb/dvb-usb/Kconfig
+++ b/drivers/media/usb/dvb-usb/Kconfig
@@ -137,7 +137,6 @@
Medion MD95700 hybrid USB2.0 device.
DViCO FusionHDTV (Bluebird) USB2.0 devices
- TechnoTrend TVStick CT2-4400 and CT2-4650 CI devices
config DVB_USB_M920X
tristate "Uli m920x DVB-T USB2.0 support"
diff --git a/drivers/media/usb/dvb-usb/af9005.c b/drivers/media/usb/dvb-usb/af9005.c
index 3f4361e..c0ab8c3 100644
--- a/drivers/media/usb/dvb-usb/af9005.c
+++ b/drivers/media/usb/dvb-usb/af9005.c
@@ -1081,9 +1081,12 @@
err("usb_register failed. (%d)", result);
return result;
}
+#if IS_MODULE(CPTCFG_DVB_USB_AF9005) || defined(CPTCFG_DVB_USB_AF9005_REMOTE)
+ /* FIXME: convert to todays kernel IR infrastructure */
rc_decode = symbol_request(af9005_rc_decode);
rc_keys = symbol_request(rc_map_af9005_table);
rc_keys_size = symbol_request(rc_map_af9005_table_size);
+#endif
if (rc_decode == NULL || rc_keys == NULL || rc_keys_size == NULL) {
err("af9005_rc_decode function not found, disabling remote");
af9005_properties.rc.legacy.rc_query = NULL;
diff --git a/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c
index 356abb3..0f345b1 100644
--- a/drivers/media/usb/dvb-usb/cxusb.c
+++ b/drivers/media/usb/dvb-usb/cxusb.c
@@ -44,7 +44,6 @@
#include "atbm8830.h"
#include "si2168.h"
#include "si2157.h"
-#include "sp2.h"
/* Max transfer size done by I2C transfer functions */
#define MAX_XFER_SIZE 80
@@ -147,22 +146,6 @@
}
}
-static int cxusb_tt_ct2_4400_gpio_tuner(struct dvb_usb_device *d, int onoff)
-{
- u8 o[2], i;
- int rc;
-
- o[0] = 0x83;
- o[1] = onoff;
- rc = cxusb_ctrl_msg(d, CMD_GPIO_WRITE, o, 2, &i, 1);
-
- if (rc) {
- deb_info("gpio_write failed.\n");
- return -EIO;
- }
- return 0;
-}
-
/* I2C */
static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
int num)
@@ -524,30 +507,6 @@
return 0;
}
-static int cxusb_tt_ct2_4400_rc_query(struct dvb_usb_device *d)
-{
- u8 i[2];
- int ret;
- u32 cmd, keycode;
- u8 rc5_cmd, rc5_addr, rc5_toggle;
-
- ret = cxusb_ctrl_msg(d, 0x10, NULL, 0, i, 2);
- if (ret)
- return ret;
-
- cmd = (i[0] << 8) | i[1];
-
- if (cmd != 0xffff) {
- rc5_cmd = cmd & 0x3F; /* bits 1-6 for command */
- rc5_addr = (cmd & 0x07C0) >> 6; /* bits 7-11 for address */
- rc5_toggle = (cmd & 0x0800) >> 11; /* bit 12 for toggle */
- keycode = (rc5_addr << 8) | rc5_cmd;
- rc_keydown(d->rc_dev, RC_BIT_RC5, keycode, rc5_toggle);
- }
-
- return 0;
-}
-
static struct rc_map_table rc_map_dvico_mce_table[] = {
{ 0xfe02, KEY_TV },
{ 0xfe0e, KEY_MP3 },
@@ -673,70 +632,6 @@
{ 0x0025, KEY_POWER },
};
-static int cxusb_tt_ct2_4400_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
-{
- u8 wbuf[2];
- u8 rbuf[6];
- int ret;
- struct i2c_msg msg[] = {
- {
- .addr = 0x51,
- .flags = 0,
- .buf = wbuf,
- .len = 2,
- }, {
- .addr = 0x51,
- .flags = I2C_M_RD,
- .buf = rbuf,
- .len = 6,
- }
- };
-
- wbuf[0] = 0x1e;
- wbuf[1] = 0x00;
- ret = cxusb_i2c_xfer(&d->i2c_adap, msg, 2);
-
- if (ret == 2) {
- memcpy(mac, rbuf, 6);
- return 0;
- } else {
- if (ret < 0)
- return ret;
- return -EIO;
- }
-}
-
-static int cxusb_tt_ct2_4650_ci_ctrl(void *priv, u8 read, int addr,
- u8 data, int *mem)
-{
- struct dvb_usb_device *d = priv;
- u8 wbuf[3];
- u8 rbuf[2];
- int ret;
-
- wbuf[0] = (addr >> 8) & 0xff;
- wbuf[1] = addr & 0xff;
-
- if (read) {
- ret = cxusb_ctrl_msg(d, CMD_SP2_CI_READ, wbuf, 2, rbuf, 2);
- } else {
- wbuf[2] = data;
- ret = cxusb_ctrl_msg(d, CMD_SP2_CI_WRITE, wbuf, 3, rbuf, 1);
- }
-
- if (ret)
- goto err;
-
- if (read)
- *mem = rbuf[1];
-
- return 0;
-err:
- deb_info("%s: ci usb write returned %d\n", __func__, ret);
- return ret;
-
-}
-
static int cxusb_dee1601_demod_init(struct dvb_frontend* fe)
{
static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x28 };
@@ -1408,36 +1303,34 @@
return 0;
}
-static int cxusb_tt_ct2_4400_attach(struct dvb_usb_adapter *adap)
+static int cxusb_mygica_t230_frontend_attach(struct dvb_usb_adapter *adap)
{
struct dvb_usb_device *d = adap->dev;
struct cxusb_state *st = d->priv;
struct i2c_adapter *adapter;
struct i2c_client *client_demod;
struct i2c_client *client_tuner;
- struct i2c_client *client_ci;
struct i2c_board_info info;
struct si2168_config si2168_config;
struct si2157_config si2157_config;
- struct sp2_config sp2_config;
- u8 o[2], i;
- /* reset the tuner */
- if (cxusb_tt_ct2_4400_gpio_tuner(d, 0) < 0) {
- err("clear tuner gpio failed");
- return -EIO;
- }
- msleep(100);
- if (cxusb_tt_ct2_4400_gpio_tuner(d, 1) < 0) {
- err("set tuner gpio failed");
- return -EIO;
- }
- msleep(100);
+ /* Select required USB configuration */
+ if (usb_set_interface(d->udev, 0, 0) < 0)
+ err("set interface failed");
+
+ /* Unblock all USB pipes */
+ usb_clear_halt(d->udev,
+ usb_sndbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint));
+ usb_clear_halt(d->udev,
+ usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint));
+ usb_clear_halt(d->udev,
+ usb_rcvbulkpipe(d->udev, d->props.adapter[0].fe[0].stream.endpoint));
/* attach frontend */
si2168_config.i2c_adapter = &adapter;
si2168_config.fe = &adap->fe_adap[0].fe;
si2168_config.ts_mode = SI2168_TS_PARALLEL;
+ si2168_config.ts_clock_inv = 1;
memset(&info, 0, sizeof(struct i2c_board_info));
strlcpy(info.type, "si2168", I2C_NAME_SIZE);
info.addr = 0x64;
@@ -1477,48 +1370,6 @@
st->i2c_client_tuner = client_tuner;
- /* initialize CI */
- if (d->udev->descriptor.idProduct ==
- USB_PID_TECHNOTREND_CONNECT_CT2_4650_CI) {
-
- memcpy(o, "\xc0\x01", 2);
- cxusb_ctrl_msg(d, CMD_GPIO_WRITE, o, 2, &i, 1);
- msleep(100);
-
- memcpy(o, "\xc0\x00", 2);
- cxusb_ctrl_msg(d, CMD_GPIO_WRITE, o, 2, &i, 1);
- msleep(100);
-
- memset(&sp2_config, 0, sizeof(sp2_config));
- sp2_config.dvb_adap = &adap->dvb_adap;
- sp2_config.priv = d;
- sp2_config.ci_control = cxusb_tt_ct2_4650_ci_ctrl;
- memset(&info, 0, sizeof(struct i2c_board_info));
- strlcpy(info.type, "sp2", I2C_NAME_SIZE);
- info.addr = 0x40;
- info.platform_data = &sp2_config;
- request_module(info.type);
- client_ci = i2c_new_device(&d->i2c_adap, &info);
- if (client_ci == NULL || client_ci->dev.driver == NULL) {
- module_put(client_tuner->dev.driver->owner);
- i2c_unregister_device(client_tuner);
- module_put(client_demod->dev.driver->owner);
- i2c_unregister_device(client_demod);
- return -ENODEV;
- }
- if (!try_module_get(client_ci->dev.driver->owner)) {
- i2c_unregister_device(client_ci);
- module_put(client_tuner->dev.driver->owner);
- i2c_unregister_device(client_tuner);
- module_put(client_demod->dev.driver->owner);
- i2c_unregister_device(client_demod);
- return -ENODEV;
- }
-
- st->i2c_client_ci = client_ci;
-
- }
-
return 0;
}
@@ -1603,7 +1454,7 @@
static struct dvb_usb_device_properties cxusb_aver_a868r_properties;
static struct dvb_usb_device_properties cxusb_d680_dmb_properties;
static struct dvb_usb_device_properties cxusb_mygica_d689_properties;
-static struct dvb_usb_device_properties cxusb_tt_ct2_4400_properties;
+static struct dvb_usb_device_properties cxusb_mygica_t230_properties;
static int cxusb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
@@ -1634,7 +1485,7 @@
THIS_MODULE, NULL, adapter_nr) ||
0 == dvb_usb_device_init(intf, &cxusb_mygica_d689_properties,
THIS_MODULE, NULL, adapter_nr) ||
- 0 == dvb_usb_device_init(intf, &cxusb_tt_ct2_4400_properties,
+ 0 == dvb_usb_device_init(intf, &cxusb_mygica_t230_properties,
THIS_MODULE, NULL, adapter_nr) ||
0)
return 0;
@@ -1648,13 +1499,6 @@
struct cxusb_state *st = d->priv;
struct i2c_client *client;
- /* remove I2C client for CI */
- client = st->i2c_client_ci;
- if (client) {
- module_put(client->dev.driver->owner);
- i2c_unregister_device(client);
- }
-
/* remove I2C client for tuner */
client = st->i2c_client_tuner;
if (client) {
@@ -1693,8 +1537,7 @@
{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_4_REV_2) },
{ USB_DEVICE(USB_VID_CONEXANT, USB_PID_CONEXANT_D680_DMB) },
{ USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_D689) },
- { USB_DEVICE(USB_VID_TECHNOTREND, USB_PID_TECHNOTREND_TVSTICK_CT2_4400) },
- { USB_DEVICE(USB_VID_TECHNOTREND, USB_PID_TECHNOTREND_CONNECT_CT2_4650_CI) },
+ { USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230) },
{} /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, cxusb_table);
@@ -2341,7 +2184,7 @@
}
};
-static struct dvb_usb_device_properties cxusb_tt_ct2_4400_properties = {
+static struct dvb_usb_device_properties cxusb_mygica_t230_properties = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
.usb_ctrl = CYPRESS_FX2,
@@ -2349,25 +2192,21 @@
.size_of_priv = sizeof(struct cxusb_state),
.num_adapters = 1,
- .read_mac_address = cxusb_tt_ct2_4400_read_mac_address,
-
.adapter = {
{
.num_frontends = 1,
.fe = {{
.streaming_ctrl = cxusb_streaming_ctrl,
- /* both frontend and tuner attached in the
- same function */
- .frontend_attach = cxusb_tt_ct2_4400_attach,
+ .frontend_attach = cxusb_mygica_t230_frontend_attach,
/* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
- .count = 8,
- .endpoint = 0x82,
+ .count = 5,
+ .endpoint = 0x02,
.u = {
.bulk = {
- .buffersize = 4096,
+ .buffersize = 8192,
}
}
},
@@ -2375,28 +2214,25 @@
},
},
- .i2c_algo = &cxusb_i2c_algo,
- .generic_bulk_ctrl_endpoint = 0x01,
- .generic_bulk_ctrl_endpoint_response = 0x81,
+ .power_ctrl = cxusb_d680_dmb_power_ctrl,
- .rc.core = {
- .rc_codes = RC_MAP_TT_1500,
- .allowed_protos = RC_BIT_RC5,
- .rc_query = cxusb_tt_ct2_4400_rc_query,
- .rc_interval = 150,
+ .i2c_algo = &cxusb_i2c_algo,
+
+ .generic_bulk_ctrl_endpoint = 0x01,
+
+ .rc.legacy = {
+ .rc_interval = 100,
+ .rc_map_table = rc_map_d680_dmb_table,
+ .rc_map_size = ARRAY_SIZE(rc_map_d680_dmb_table),
+ .rc_query = cxusb_d680_dmb_rc_query,
},
- .num_device_descs = 2,
+ .num_device_descs = 1,
.devices = {
{
- "TechnoTrend TVStick CT2-4400",
+ "Mygica T230 DVB-T/T2/C",
{ NULL },
- { &cxusb_table[20], NULL },
- },
- {
- "TechnoTrend TT-connect CT2-4650 CI",
- { NULL },
- { &cxusb_table[21], NULL },
+ { &cxusb_table[22], NULL },
},
}
};
diff --git a/drivers/media/usb/dvb-usb/cxusb.h b/drivers/media/usb/dvb-usb/cxusb.h
index 29f3e2e..527ff79 100644
--- a/drivers/media/usb/dvb-usb/cxusb.h
+++ b/drivers/media/usb/dvb-usb/cxusb.h
@@ -28,14 +28,10 @@
#define CMD_ANALOG 0x50
#define CMD_DIGITAL 0x51
-#define CMD_SP2_CI_WRITE 0x70
-#define CMD_SP2_CI_READ 0x71
-
struct cxusb_state {
u8 gpio_write_state[3];
struct i2c_client *i2c_client_demod;
struct i2c_client *i2c_client_tuner;
- struct i2c_client *i2c_client_ci;
};
#endif
diff --git a/drivers/media/usb/dvb-usb/technisat-usb2.c b/drivers/media/usb/dvb-usb/technisat-usb2.c
index 6b0b8b6..5801ae7 100644
--- a/drivers/media/usb/dvb-usb/technisat-usb2.c
+++ b/drivers/media/usb/dvb-usb/technisat-usb2.c
@@ -449,6 +449,8 @@
return 0;
}
+static struct stv090x_config technisat_usb2_stv090x_config;
+
/* frontend attach */
static int technisat_usb2_set_voltage(struct dvb_frontend *fe,
fe_sec_voltage_t voltage)
@@ -472,7 +474,8 @@
}
for (i = 0; i < 3; i++)
- if (stv090x_set_gpio(fe, i+2, 0, gpio[i], 0) != 0)
+ if (technisat_usb2_stv090x_config.set_gpio(fe, i+2, 0,
+ gpio[i], 0) != 0)
return -EREMOTEIO;
return 0;
}
diff --git a/drivers/media/usb/em28xx/Kconfig b/drivers/media/usb/em28xx/Kconfig
index ddec55a..82fbf57 100644
--- a/drivers/media/usb/em28xx/Kconfig
+++ b/drivers/media/usb/em28xx/Kconfig
@@ -38,7 +38,7 @@
module will be called em28xx-alsa
config VIDEO_EM28XX_DVB
- depends on !BACKPORT_KERNEL_3_4
+ depends on !KERNEL_3_4
tristate "DVB/ATSC Support for em28xx based TV cards"
depends on m
depends on VIDEO_EM28XX && DVB_CORE
diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c
index 957c7ae..44ae1e0 100644
--- a/drivers/media/usb/em28xx/em28xx-audio.c
+++ b/drivers/media/usb/em28xx/em28xx-audio.c
@@ -56,7 +56,7 @@
#define dprintk(fmt, arg...) do { \
if (debug) \
printk(KERN_INFO "em28xx-audio %s: " fmt, \
- __func__, ##arg); \
+ __func__, ##arg); \
} while (0)
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
@@ -232,7 +232,6 @@
.channels_max = 2,
.buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */
-
/*
* The period is 12.288 bytes. Allow a 10% of variation along its
* value, in order to avoid overruns/underruns due to some clock
@@ -361,7 +360,7 @@
dprintk("Setting capture parameters\n");
ret = snd_pcm_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
+ params_buffer_bytes(hw_params));
if (ret < 0)
return ret;
#if 0
@@ -478,7 +477,7 @@
* AC97 volume control support
*/
static int em28xx_vol_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *info)
+ struct snd_ctl_elem_info *info)
{
struct em28xx *dev = snd_kcontrol_chip(kcontrol);
@@ -494,7 +493,7 @@
}
static int em28xx_vol_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *value)
+ struct snd_ctl_elem_value *value)
{
struct em28xx *dev = snd_kcontrol_chip(kcontrol);
struct snd_pcm_substream *substream = dev->adev.capture_pcm_substream;
@@ -534,7 +533,7 @@
}
static int em28xx_vol_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *value)
+ struct snd_ctl_elem_value *value)
{
struct em28xx *dev = snd_kcontrol_chip(kcontrol);
struct snd_pcm_substream *substream = dev->adev.capture_pcm_substream;
@@ -655,7 +654,7 @@
struct snd_kcontrol *kctl;
struct snd_kcontrol_new tmp;
- memset (&tmp, 0, sizeof(tmp));
+ memset(&tmp, 0, sizeof(tmp));
tmp.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
tmp.private_value = id,
tmp.name = ctl_name,
@@ -672,7 +671,7 @@
dprintk("Added control %s for ac97 volume control 0x%04x\n",
ctl_name, id);
- memset (&tmp, 0, sizeof(tmp));
+ memset(&tmp, 0, sizeof(tmp));
tmp.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
tmp.private_value = id,
tmp.name = ctl_name,
@@ -731,7 +730,7 @@
/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */
static int em28xx_audio_ep_packet_size(struct usb_device *udev,
- struct usb_endpoint_descriptor *e)
+ struct usb_endpoint_descriptor *e)
{
int size = le16_to_cpu(e->wMaxPacketSize);
@@ -781,7 +780,7 @@
interval = 1 << (ep->bInterval - 1);
em28xx_info("Endpoint 0x%02x %s on intf %d alt %d interval = %d, size %d\n",
- EM28XX_EP_AUDIO, usb_speed_string(dev->udev->speed),
+ EM28XX_EP_AUDIO, usb_speed_string(dev->udev->speed),
dev->ifnum, alt,
interval,
ep_size);
diff --git a/drivers/media/usb/em28xx/em28xx-camera.c b/drivers/media/usb/em28xx/em28xx-camera.c
index 6d2ea9a..7be661f 100644
--- a/drivers/media/usb/em28xx/em28xx-camera.c
+++ b/drivers/media/usb/em28xx/em28xx-camera.c
@@ -27,7 +27,6 @@
#include "em28xx.h"
-
/* Possible i2c addresses of Micron sensors */
static unsigned short micron_sensor_addrs[] = {
0xb8 >> 1, /* MT9V111, MT9V403 */
@@ -43,7 +42,6 @@
I2C_CLIENT_END
};
-
static struct soc_camera_link camlink = {
.bus_id = 0,
.flags = 0,
@@ -51,7 +49,6 @@
.unbalanced_power = true,
};
-
/* FIXME: Should be replaced by a proper mt9m111 driver */
static int em28xx_initialize_mt9m111(struct em28xx *dev)
{
@@ -70,7 +67,6 @@
return 0;
}
-
/* FIXME: Should be replaced by a proper mt9m001 driver */
static int em28xx_initialize_mt9m001(struct em28xx *dev)
{
@@ -98,7 +94,6 @@
return 0;
}
-
/*
* Probes Micron sensors with 8 bit address and 16 bit register width
*/
@@ -430,7 +425,7 @@
break;
}
- fmt.code = V4L2_MBUS_FMT_YUYV8_2X8;
+ fmt.code = MEDIA_BUS_FMT_YUYV8_2X8;
fmt.width = 640;
fmt.height = 480;
v4l2_subdev_call(subdev, video, s_mbus_fmt, &fmt);
diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c
index 71fa51e..d9704e6 100644
--- a/drivers/media/usb/em28xx/em28xx-cards.c
+++ b/drivers/media/usb/em28xx/em28xx-cards.c
@@ -64,7 +64,6 @@
MODULE_PARM_DESC(usb_xfer_mode,
"USB transfer mode for frame data (-1 = auto, 0 = prefer isoc, 1 = prefer bulk)");
-
/* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS - 1 */
static DECLARE_BITMAP(em28xx_devused, EM28XX_MAXBOARDS);
@@ -190,8 +189,8 @@
};
static struct em28xx_reg_seq kworld_ub435q_v3_digital[] = {
- {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 100},
- {EM2874_R80_GPIO_P0_CTRL, 0xfe, 0xff, 100},
+ {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 100},
+ {EM2874_R80_GPIO_P0_CTRL, 0xfe, 0xff, 100},
{EM2874_R80_GPIO_P0_CTRL, 0xbe, 0xff, 100},
{EM2874_R80_GPIO_P0_CTRL, 0xfe, 0xff, 100},
{ -1, -1, -1, -1},
@@ -301,7 +300,6 @@
{ -1, -1, -1, -1},
};
-
/* Reset for the most [digital] boards */
static struct em28xx_reg_seq leadership_digital[] = {
{EM2874_R80_GPIO_P0_CTRL, 0x70, 0xff, 10},
@@ -479,6 +477,20 @@
{-1, -1, -1, -1},
};
+static struct em28xx_reg_seq terratec_t2_stick_hd[] = {
+ {EM2874_R80_GPIO_P0_CTRL, 0xff, 0xff, 0},
+ {0x0d, 0xff, 0xff, 600},
+ {EM2874_R80_GPIO_P0_CTRL, 0xfc, 0xff, 10},
+ {EM2874_R80_GPIO_P0_CTRL, 0xbc, 0xff, 100},
+ {EM2874_R80_GPIO_P0_CTRL, 0xfc, 0xff, 100},
+ {EM2874_R80_GPIO_P0_CTRL, 0x00, 0xff, 300},
+ {EM2874_R80_GPIO_P0_CTRL, 0xf8, 0xff, 100},
+ {EM2874_R80_GPIO_P0_CTRL, 0xfc, 0xff, 300},
+ {0x0d, 0x42, 0xff, 1000},
+ {EM2874_R5F_TS_ENABLE, 0x85, 0xff, 0},
+ {-1, -1, -1, -1},
+};
+
/*
* Button definitions
*/
@@ -548,7 +560,6 @@
{-1, 0, 0, 0},
};
-
/*
* Board definitions
*/
@@ -1514,7 +1525,7 @@
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
.amux = EM28XX_AMUX_VIDEO,
- .aout = EM28XX_AOUT_MONO | /* I2S */
+ .aout = EM28XX_AOUT_MONO | /* I2S */
EM28XX_AOUT_MASTER, /* Line out pin */
}, {
.type = EM28XX_VMUX_COMPOSITE1,
@@ -1536,7 +1547,7 @@
.type = EM28XX_VMUX_TELEVISION,
.vmux = SAA7115_COMPOSITE2,
.amux = EM28XX_AMUX_VIDEO,
- .aout = EM28XX_AOUT_MONO | /* I2S */
+ .aout = EM28XX_AOUT_MONO | /* I2S */
EM28XX_AOUT_MASTER, /* Line out pin */
}, {
.type = EM28XX_VMUX_COMPOSITE1,
@@ -2243,6 +2254,31 @@
.has_dvb = 1,
.ir_codes = RC_MAP_PINNACLE_PCTV_HD,
},
+ [EM2861_BOARD_LEADTEK_VC100] = {
+ .name = "Leadtek VC100",
+ .tuner_type = TUNER_ABSENT, /* Capture only device */
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ /* eb1a:8179 Terratec Cinergy T2 Stick HD.
+ * Empia EM28178, Silicon Labs Si2168, Silicon Labs Si2146 */
+ [EM28178_BOARD_TERRATEC_T2_STICK_HD] = {
+ .name = "Terratec Cinergy T2 Stick HD",
+ .def_i2c_bus = 1,
+ .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_gpio = terratec_t2_stick_hd,
+ .has_dvb = 1,
+ .ir_codes = RC_MAP_TERRATEC_SLIM_2,
+ },
};
EXPORT_SYMBOL_GPL(em28xx_boards);
@@ -2424,6 +2460,10 @@
.driver_info = EM28178_BOARD_PCTV_461E },
{ USB_DEVICE(0x2013, 0x025f),
.driver_info = EM28178_BOARD_PCTV_292E },
+ { USB_DEVICE(0x0413, 0x6f07),
+ .driver_info = EM2861_BOARD_LEADTEK_VC100 },
+ { USB_DEVICE(0xeb1a, 0x8179),
+ .driver_info = EM28178_BOARD_TERRATEC_T2_STICK_HD },
{ },
};
MODULE_DEVICE_TABLE(usb, em28xx_id_table);
@@ -2453,6 +2493,7 @@
{0x4ba50080, EM2861_BOARD_GADMEI_UTV330PLUS, TUNER_TNF_5335MF},
{0x6b800080, EM2874_BOARD_LEADERSHIP_ISDBT, TUNER_ABSENT},
};
+
/* NOTE: introduce a separate hash table for devices with 16 bit eeproms */
int em28xx_tuner_callback(void *ptr, int component, int command, int arg)
@@ -2695,7 +2736,7 @@
" insmod option:\n");
for (i = 0; i < em28xx_bcount; i++) {
em28xx_errdev(" card=%d -> %s\n",
- i, em28xx_boards[i].name);
+ i, em28xx_boards[i].name);
}
return -1;
}
@@ -3051,6 +3092,7 @@
if (le16_to_cpu(dev->udev->descriptor.idVendor)
== 0xeb1a) {
__le16 idProd = dev->udev->descriptor.idProduct;
+
if (le16_to_cpu(idProd) == 0x2710)
chip_name = "em2710";
else if (le16_to_cpu(idProd) == 0x2820)
@@ -3139,7 +3181,7 @@
retval = em28xx_i2c_register(dev, 0, EM28XX_I2C_ALGO_EM28XX);
if (retval < 0) {
em28xx_errdev("%s: em28xx_i2c_register bus 0 - error [%d]!\n",
- __func__, retval);
+ __func__, retval);
return retval;
}
@@ -3147,13 +3189,13 @@
if (dev->def_i2c_bus) {
if (dev->is_em25xx)
retval = em28xx_i2c_register(dev, 1,
- EM28XX_I2C_ALGO_EM25XX_BUS_B);
+ EM28XX_I2C_ALGO_EM25XX_BUS_B);
else
retval = em28xx_i2c_register(dev, 1,
- EM28XX_I2C_ALGO_EM28XX);
+ EM28XX_I2C_ALGO_EM28XX);
if (retval < 0) {
em28xx_errdev("%s: em28xx_i2c_register bus 1 - error [%d]!\n",
- __func__, retval);
+ __func__, retval);
em28xx_i2c_unregister(dev, 0);
@@ -3193,7 +3235,7 @@
if (nr >= EM28XX_MAXBOARDS) {
/* No free device slots */
printk(DRIVER_NAME ": Supports only %i em28xx boards.\n",
- EM28XX_MAXBOARDS);
+ EM28XX_MAXBOARDS);
retval = -ENOMEM;
goto err_no_slot;
}
@@ -3377,6 +3419,7 @@
/* Checks if audio is provided by a USB Audio Class interface */
for (i = 0; i < udev->config->desc.bNumInterfaces; i++) {
struct usb_interface *uif = udev->config->interface[i];
+
if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
if (has_vendor_audio)
em28xx_err("em28xx: device seems to have vendor AND usb audio class interfaces !\n"
@@ -3487,7 +3530,7 @@
}
static int em28xx_usb_suspend(struct usb_interface *interface,
- pm_message_t message)
+ pm_message_t message)
{
struct em28xx *dev;
diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c
index b5e52fe..86461a7 100644
--- a/drivers/media/usb/em28xx/em28xx-core.c
+++ b/drivers/media/usb/em28xx/em28xx-core.c
@@ -75,7 +75,7 @@
* reads data from the usb device specifying bRequest
*/
int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg,
- char *buf, int len)
+ char *buf, int len)
{
int ret;
int pipe = usb_rcvctrlpipe(dev->udev, 0);
@@ -151,7 +151,7 @@
* sends data to the usb device, specifying bRequest
*/
int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf,
- int len)
+ int len)
{
int ret;
int pipe = usb_sndctrlpipe(dev->udev, 0);
@@ -213,7 +213,7 @@
* the actual value
*/
int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
- u8 bitmask)
+ u8 bitmask)
{
int oldval;
u8 newval;
@@ -222,7 +222,7 @@
if (oldval < 0)
return oldval;
- newval = (((u8) oldval) & ~bitmask) | (val & bitmask);
+ newval = (((u8)oldval) & ~bitmask) | (val & bitmask);
return em28xx_write_regs(dev, reg, &newval, 1);
}
@@ -314,7 +314,7 @@
if (ret < 0)
return ret;
- ret = em28xx_write_regs(dev, EM28XX_R40_AC97LSB, (u8 *) &value, 2);
+ ret = em28xx_write_regs(dev, EM28XX_R40_AC97LSB, (u8 *)&value, 2);
if (ret < 0)
return ret;
@@ -361,7 +361,7 @@
if (ret < 0)
em28xx_warn("couldn't setup AC97 register %d\n",
- inputs[i].reg);
+ inputs[i].reg);
}
return 0;
}
@@ -445,7 +445,7 @@
ret = em28xx_write_ac97(dev, outputs[i].reg, 0x8000);
if (ret < 0)
em28xx_warn("couldn't setup AC97 register %d\n",
- outputs[i].reg);
+ outputs[i].reg);
}
}
@@ -483,7 +483,7 @@
vol);
if (ret < 0)
em28xx_warn("couldn't setup AC97 register %d\n",
- outputs[i].reg);
+ outputs[i].reg);
}
if (dev->ctl_aoutput & EM28XX_AOUT_PCM_IN) {
@@ -504,7 +504,7 @@
int em28xx_audio_setup(struct em28xx *dev)
{
int vid1, vid2, feat, cfg;
- u32 vid;
+ u32 vid = 0;
u8 i2s_samplerates;
if (dev->chip_id == CHIP_ID_EM2870 ||
@@ -531,7 +531,7 @@
} else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) != EM28XX_CHIPCFG_AC97) {
dev->int_audio_type = EM28XX_INT_AUDIO_I2S;
if (dev->chip_id < CHIP_ID_EM2860 &&
- (cfg & EM28XX_CHIPCFG_AUDIOMASK) ==
+ (cfg & EM28XX_CHIPCFG_AUDIOMASK) ==
EM2820_CHIPCFG_I2S_1_SAMPRATE)
i2s_samplerates = 1;
else if (dev->chip_id >= CHIP_ID_EM2860 &&
@@ -541,7 +541,7 @@
else
i2s_samplerates = 3;
em28xx_info("I2S Audio (%d sample rate(s))\n",
- i2s_samplerates);
+ i2s_samplerates);
/* Skip the code that does AC97 vendor detection */
dev->audio_mode.ac97 = EM28XX_NO_AC97;
goto init_audio;
@@ -614,8 +614,9 @@
{
if (dev->board.leds) {
u8 k = 0;
+
while (dev->board.leds[k].role >= 0 &&
- dev->board.leds[k].role < EM28XX_NUM_LED_ROLES) {
+ dev->board.leds[k].role < EM28XX_NUM_LED_ROLES) {
if (dev->board.leds[k].role == role)
return &dev->board.leds[k];
k++;
@@ -658,10 +659,12 @@
if (dev->mode == EM28XX_ANALOG_MODE)
rc = em28xx_write_reg(dev,
- EM28XX_R12_VINENABLE, 0x67);
+ EM28XX_R12_VINENABLE,
+ 0x67);
else
rc = em28xx_write_reg(dev,
- EM28XX_R12_VINENABLE, 0x37);
+ EM28XX_R12_VINENABLE,
+ 0x37);
if (rc < 0)
return rc;
@@ -815,9 +818,9 @@
if (usb_bufs->transfer_buffer[i]) {
usb_free_coherent(dev->udev,
- urb->transfer_buffer_length,
- usb_bufs->transfer_buffer[i],
- urb->transfer_dma);
+ urb->transfer_buffer_length,
+ usb_bufs->transfer_buffer[i],
+ urb->transfer_dma);
}
usb_free_urb(urb);
usb_bufs->urb[i] = NULL;
@@ -889,7 +892,7 @@
if ((xfer_bulk && !dev->analog_ep_bulk) ||
(!xfer_bulk && !dev->analog_ep_isoc)) {
em28xx_errdev("no endpoint for analog mode and transfer type %d\n",
- xfer_bulk > 0);
+ xfer_bulk > 0);
return -EINVAL;
}
usb_bufs = &dev->usb_ctl.analog_bufs;
@@ -988,9 +991,9 @@
* Allocate URBs and start IRQ
*/
int em28xx_init_usb_xfer(struct em28xx *dev, enum em28xx_mode mode,
- int xfer_bulk, int num_bufs, int max_pkt_size,
+ int xfer_bulk, int num_bufs, int max_pkt_size,
int packet_multiplier,
- int (*urb_data_copy) (struct em28xx *dev, struct urb *urb))
+ int (*urb_data_copy)(struct em28xx *dev, struct urb *urb))
{
struct em28xx_dmaqueue *dma_q = &dev->vidq;
struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq;
diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c
index 9682c52..9877b69 100644
--- a/drivers/media/usb/em28xx/em28xx-dvb.c
+++ b/drivers/media/usb/em28xx/em28xx-dvb.c
@@ -63,7 +63,6 @@
MODULE_DESCRIPTION(DRIVER_DESC " - digital TV interface");
MODULE_VERSION(EM28XX_VERSION);
-
static unsigned int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "enable debug messages [dvb]");
@@ -71,7 +70,7 @@
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
#define dprintk(level, fmt, arg...) do { \
-if (debug >= level) \
+if (debug >= level) \
printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->name, ## arg); \
} while (0)
@@ -99,9 +98,8 @@
struct i2c_client *i2c_client_tuner;
};
-
static inline void print_err_status(struct em28xx *dev,
- int packet, int status)
+ int packet, int status)
{
char *errmsg = "Unknown";
@@ -169,7 +167,7 @@
if (!urb->actual_length)
continue;
dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer,
- urb->actual_length);
+ urb->actual_length);
} else {
if (urb->iso_frame_desc[i].status < 0) {
print_err_status(dev, i,
@@ -280,7 +278,6 @@
}
-
/* ------------------------------------------------------------------ */
static int em28xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire)
{
@@ -740,7 +737,7 @@
return ret;
#else
dev_warn(&dev->udev->dev, "%s: LNA control is disabled (lna=%u)\n",
- KBUILD_MODNAME, c->lna);
+ KBUILD_MODNAME, c->lna);
return 0;
#endif
}
@@ -830,6 +827,7 @@
.no_tuner = 1,
.parallel_ts = 1,
};
+
static struct qt1010_config em28xx_qt1010_config = {
.i2c_address = 0x62
};
@@ -861,7 +859,6 @@
.agc = 0x99,
};
-
static struct tda18271_std_map drx_j_std_map = {
.atsc_6 = { .if_freq = 5000, .agc_mode = 3, .std = 0, .if_lvl = 1,
.rfagc_top = 0x37, },
@@ -948,7 +945,7 @@
result = dvb_register_frontend(&dvb->adapter, dvb->fe[1]);
if (result < 0) {
printk(KERN_WARNING "%s: 2nd dvb_register_frontend failed (errno = %d)\n",
- dev->name, result);
+ dev->name, result);
goto fail_frontend1;
}
}
@@ -1047,7 +1044,7 @@
static int em28xx_dvb_init(struct em28xx *dev)
{
- int result = 0, mfe_shared = 0;
+ int result = 0;
struct em28xx_dvb *dvb;
if (dev->is_audio_only) {
@@ -1182,7 +1179,8 @@
&dev->i2c_adap[dev->def_i2c_bus]);
if (dvb->fe[0] != NULL) {
if (!dvb_attach(simple_tuner_attach, dvb->fe[0],
- &dev->i2c_adap[dev->def_i2c_bus], 0x61, TUNER_THOMSON_DTT761X)) {
+ &dev->i2c_adap[dev->def_i2c_bus],
+ 0x61, TUNER_THOMSON_DTT761X)) {
result = -EINVAL;
goto out_free;
}
@@ -1204,7 +1202,8 @@
&dev->i2c_adap[dev->def_i2c_bus], 0x48);
if (dvb->fe[0]) {
if (!dvb_attach(simple_tuner_attach, dvb->fe[0],
- &dev->i2c_adap[dev->def_i2c_bus], 0x60, TUNER_PHILIPS_CU1216L)) {
+ &dev->i2c_adap[dev->def_i2c_bus],
+ 0x60, TUNER_PHILIPS_CU1216L)) {
result = -EINVAL;
goto out_free;
}
@@ -1219,7 +1218,7 @@
goto out_free;
}
if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
- &dev->i2c_adap[dev->def_i2c_bus],
+ &dev->i2c_adap[dev->def_i2c_bus],
&kworld_a340_config)) {
dvb_frontend_detach(dvb->fe[0]);
result = -EINVAL;
@@ -1250,10 +1249,10 @@
#ifdef CONFIG_GPIOLIB
/* enable LNA for DVB-T, DVB-T2 and DVB-C */
result = gpio_request_one(dvb->lna_gpio,
- GPIOF_OUT_INIT_LOW, NULL);
+ GPIOF_OUT_INIT_LOW, NULL);
if (result)
em28xx_errdev("gpio request failed %d\n",
- result);
+ result);
else
gpio_free(dvb->lna_gpio);
@@ -1266,6 +1265,7 @@
case EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C:
{
struct xc5000_config cfg;
+
hauppauge_hvr930c_init(dev);
dvb->fe[0] = dvb_attach(drxk_attach,
@@ -1339,7 +1339,7 @@
/* attach SEC */
if (dvb->fe[0])
dvb_attach(a8293_attach, dvb->fe[0], &dev->i2c_adap[dev->def_i2c_bus],
- &em28xx_a8293_config);
+ &em28xx_a8293_config);
break;
case EM2874_BOARD_DELOCK_61959:
case EM2874_BOARD_MAXMEDIA_UB425_TC:
@@ -1553,6 +1553,7 @@
struct si2157_config si2157_config;
/* attach demod */
+ memset(&si2168_config, 0, sizeof(si2168_config));
si2168_config.i2c_adapter = &adapter;
si2168_config.fe = &dvb->fe[0];
si2168_config.ts_mode = SI2168_TS_PARALLEL;
@@ -1603,6 +1604,65 @@
dvb->fe[0]->ops.set_lna = em28xx_pctv_292e_set_lna;
}
break;
+ case EM28178_BOARD_TERRATEC_T2_STICK_HD:
+ {
+ struct i2c_adapter *adapter;
+ struct i2c_client *client;
+ struct i2c_board_info info;
+ struct si2168_config si2168_config;
+ struct si2157_config si2157_config;
+
+ /* attach demod */
+ memset(&si2168_config, 0, sizeof(si2168_config));
+ si2168_config.i2c_adapter = &adapter;
+ si2168_config.fe = &dvb->fe[0];
+ si2168_config.ts_mode = SI2168_TS_PARALLEL;
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "si2168", I2C_NAME_SIZE);
+ info.addr = 0x64;
+ info.platform_data = &si2168_config;
+ request_module(info.type);
+ client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &info);
+ if (client == NULL || client->dev.driver == NULL) {
+ result = -ENODEV;
+ goto out_free;
+ }
+
+ if (!try_module_get(client->dev.driver->owner)) {
+ i2c_unregister_device(client);
+ result = -ENODEV;
+ goto out_free;
+ }
+
+ dvb->i2c_client_demod = client;
+
+ /* attach tuner */
+ memset(&si2157_config, 0, sizeof(si2157_config));
+ si2157_config.fe = dvb->fe[0];
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ strlcpy(info.type, "si2146", I2C_NAME_SIZE);
+ info.addr = 0x60;
+ info.platform_data = &si2157_config;
+ request_module("si2157");
+ client = i2c_new_device(adapter, &info);
+ if (client == NULL || client->dev.driver == NULL) {
+ module_put(dvb->i2c_client_demod->dev.driver->owner);
+ i2c_unregister_device(dvb->i2c_client_demod);
+ result = -ENODEV;
+ goto out_free;
+ }
+
+ if (!try_module_get(client->dev.driver->owner)) {
+ i2c_unregister_device(client);
+ module_put(dvb->i2c_client_demod->dev.driver->owner);
+ i2c_unregister_device(dvb->i2c_client_demod);
+ result = -ENODEV;
+ goto out_free;
+ }
+
+ dvb->i2c_client_tuner = client;
+ }
+ break;
default:
em28xx_errdev("/2: The frontend of your DVB/ATSC card"
" isn't supported yet\n");
@@ -1624,9 +1684,6 @@
if (result < 0)
goto out_free;
- /* MFE lock */
- dvb->adapter.mfe_shared = mfe_shared;
-
em28xx_info("DVB extension successfully initialized\n");
kref_get(&dev->ref);
diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c
index 1048c1a..a19b5c8 100644
--- a/drivers/media/usb/em28xx/em28xx-i2c.c
+++ b/drivers/media/usb/em28xx/em28xx-i2c.c
@@ -593,6 +593,7 @@
unsigned long l = 0;
int len = 0;
unsigned char c;
+
do {
if (len == length) {
c = (char)len;
@@ -877,6 +878,7 @@
* incomplete list of known devices
*/
static char *i2c_devs[128] = {
+ [0x1c >> 1] = "lgdt330x",
[0x3e >> 1] = "remote IR sensor",
[0x4a >> 1] = "saa7113h",
[0x52 >> 1] = "drxk",
@@ -949,7 +951,7 @@
retval = i2c_add_adapter(&dev->i2c_adap[bus]);
if (retval < 0) {
em28xx_errdev("%s: i2c_add_adapter failed! retval [%d]\n",
- __func__, retval);
+ __func__, retval);
return retval;
}
@@ -961,7 +963,7 @@
retval = em28xx_i2c_eeprom(dev, bus, &dev->eedata, &dev->eedata_len);
if ((retval < 0) && (retval != -ENODEV)) {
em28xx_errdev("%s: em28xx_i2_eeprom failed! retval [%d]\n",
- __func__, retval);
+ __func__, retval);
return retval;
}
diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c
index 581f6da..d8dc03a 100644
--- a/drivers/media/usb/em28xx/em28xx-input.c
+++ b/drivers/media/usb/em28xx/em28xx-input.c
@@ -459,7 +459,7 @@
return em2874_ir_change_protocol(rc_dev, rc_type);
default:
printk("Unrecognized em28xx chip id 0x%02x: IR not supported\n",
- dev->chip_id);
+ dev->chip_id);
return -EINVAL;
}
}
@@ -505,7 +505,7 @@
/* Check states of the buttons and act */
j = 0;
while (dev->board.buttons[j].role >= 0 &&
- dev->board.buttons[j].role < EM28XX_NUM_BUTTON_ROLES) {
+ dev->board.buttons[j].role < EM28XX_NUM_BUTTON_ROLES) {
struct em28xx_button *button = &dev->board.buttons[j];
/* Check if button uses the current address */
if (button->reg_r != dev->button_polling_addresses[i]) {
@@ -607,7 +607,7 @@
dev->button_polling_interval = EM28XX_BUTTONS_DEBOUNCED_QUERY_INTERVAL;
while (dev->board.buttons[i].role >= 0 &&
- dev->board.buttons[i].role < EM28XX_NUM_BUTTON_ROLES) {
+ dev->board.buttons[i].role < EM28XX_NUM_BUTTON_ROLES) {
struct em28xx_button *button = &dev->board.buttons[i];
/* Check if polling address is already on the list */
addr_new = true;
@@ -653,11 +653,11 @@
/* Start polling */
if (dev->num_button_polling_addresses) {
memset(dev->button_polling_last_values, 0,
- EM28XX_NUM_BUTTON_ADDRESSES_MAX);
+ EM28XX_NUM_BUTTON_ADDRESSES_MAX);
INIT_DELAYED_WORK(&dev->buttons_query_work,
- em28xx_query_buttons);
+ em28xx_query_buttons);
schedule_delayed_work(&dev->buttons_query_work,
- msecs_to_jiffies(dev->button_polling_interval));
+ msecs_to_jiffies(dev->button_polling_interval));
}
}
@@ -712,8 +712,10 @@
em28xx_info("Registering input extension\n");
ir = kzalloc(sizeof(*ir), GFP_KERNEL);
+ if (!ir)
+ return -ENOMEM;
rc = rc_allocate_device();
- if (!ir || !rc)
+ if (!rc)
goto error;
/* record handles to ourself */
@@ -839,8 +841,7 @@
if (!ir)
goto ref_put;
- if (ir->rc)
- rc_unregister_device(ir->rc);
+ rc_unregister_device(ir->rc);
kfree(ir->i2c_client);
@@ -885,7 +886,7 @@
schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling));
if (dev->num_button_polling_addresses)
schedule_delayed_work(&dev->buttons_query_work,
- msecs_to_jiffies(dev->button_polling_interval));
+ msecs_to_jiffies(dev->button_polling_interval));
return 0;
}
diff --git a/drivers/media/usb/em28xx/em28xx-reg.h b/drivers/media/usb/em28xx/em28xx-reg.h
index 311fb34..13cbb7f 100644
--- a/drivers/media/usb/em28xx/em28xx-reg.h
+++ b/drivers/media/usb/em28xx/em28xx-reg.h
@@ -49,7 +49,6 @@
#define EM28XX_CHIPCFG2_TS_PACKETSIZE_564 0x02
#define EM28XX_CHIPCFG2_TS_PACKETSIZE_752 0x03
-
/* GPIO/GPO registers */
#define EM2880_R04_GPO 0x04 /* em2880-em2883 only */
#define EM2820_R08_GPIO_CTRL 0x08 /* em2820-em2873/83 only */
@@ -68,7 +67,6 @@
#define EM28XX_I2C_FREQ_400_KHZ 0x01
#define EM28XX_I2C_FREQ_100_KHZ 0x00
-
#define EM28XX_R0A_CHIPID 0x0a
#define EM28XX_R0C_USBSUSP 0x0c
#define EM28XX_R0C_USBSUSP_SNAPSHOT 0x20 /* 1=button pressed, needs reset */
@@ -157,7 +155,6 @@
#define EM28XX_OUTFMT_YUV422_Y1UY0V 0x15
#define EM28XX_OUTFMT_YUV411 0x18
-
#define EM28XX_R28_XMIN 0x28
#define EM28XX_R29_XMAX 0x29
#define EM28XX_R2A_YMIN 0x2a
diff --git a/drivers/media/usb/em28xx/em28xx-v4l.h b/drivers/media/usb/em28xx/em28xx-v4l.h
index 432862c..8dfcb56 100644
--- a/drivers/media/usb/em28xx/em28xx-v4l.h
+++ b/drivers/media/usb/em28xx/em28xx-v4l.h
@@ -14,7 +14,6 @@
GNU General Public License for more details.
*/
-
int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count);
void em28xx_stop_vbi_streaming(struct vb2_queue *vq);
extern struct vb2_ops em28xx_vbi_qops;
diff --git a/drivers/media/usb/em28xx/em28xx-vbi.c b/drivers/media/usb/em28xx/em28xx-vbi.c
index 34ee1e0..744e7ed 100644
--- a/drivers/media/usb/em28xx/em28xx-vbi.c
+++ b/drivers/media/usb/em28xx/em28xx-vbi.c
@@ -92,7 +92,6 @@
spin_unlock_irqrestore(&dev->slock, flags);
}
-
struct vb2_ops em28xx_vbi_qops = {
.queue_setup = vbi_queue_setup,
.buf_prepare = vbi_buffer_prepare,
diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c
index f35083c..bf1be43 100644
--- a/drivers/media/usb/em28xx/em28xx-video.c
+++ b/drivers/media/usb/em28xx/em28xx-video.c
@@ -81,7 +81,6 @@
MODULE_LICENSE("GPL");
MODULE_VERSION(EM28XX_VERSION);
-
#define EM25XX_FRMDATAHDR_BYTE1 0x02
#define EM25XX_FRMDATAHDR_BYTE2_STILL_IMAGE 0x20
#define EM25XX_FRMDATAHDR_BYTE2_FRAME_END 0x02
@@ -90,7 +89,6 @@
EM25XX_FRMDATAHDR_BYTE2_FRAME_END | \
EM25XX_FRMDATAHDR_BYTE2_FRAME_ID)
-
static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = -1U };
static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = -1U };
static unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = -1U };
@@ -194,9 +192,10 @@
static void em28xx_wake_i2c(struct em28xx *dev)
{
struct v4l2_device *v4l2_dev = &dev->v4l2->v4l2_dev;
+
v4l2_device_call_all(v4l2_dev, 0, core, reset, 0);
v4l2_device_call_all(v4l2_dev, 0, video, s_routing,
- INPUT(dev->ctl_input)->vmux, 0, 0);
+ INPUT(dev->ctl_input)->vmux, 0, 0);
v4l2_device_call_all(v4l2_dev, 0, video, s_stream, 0);
}
@@ -275,7 +274,7 @@
}
static void em28xx_capture_area_set(struct em28xx *dev, u8 hstart, u8 vstart,
- u16 width, u16 height)
+ u16 width, u16 height)
{
u8 cwidth = width >> 2;
u8 cheight = height >> 2;
@@ -283,7 +282,7 @@
/* NOTE: size limit: 2047x1023 = 2MPix */
em28xx_videodbg("capture area set to (%d,%d): %dx%d\n",
- hstart, vstart,
+ hstart, vstart,
((overflow & 2) << 9 | cwidth << 2),
((overflow & 1) << 10 | cheight << 2));
@@ -406,13 +405,13 @@
dev->packet_multiplier = EM28XX_BULK_PACKET_MULTIPLIER;
} else { /* isoc */
em28xx_videodbg("minimum isoc packet size: %u (alt=%d)\n",
- min_pkt_size, dev->alt);
+ min_pkt_size, dev->alt);
dev->max_pkt_size =
dev->alt_max_pkt_size_isoc[dev->alt];
dev->packet_multiplier = EM28XX_NUM_ISOC_PACKETS;
}
em28xx_videodbg("setting alternate %d with wMaxPacketSize=%u\n",
- dev->alt, dev->max_pkt_size);
+ dev->alt, dev->max_pkt_size);
errCode = usb_set_interface(dev->udev, dev->ifnum, dev->alt);
if (errCode < 0) {
em28xx_errdev("cannot change alternate number to %d (error=%i)\n",
@@ -482,7 +481,7 @@
if ((char *)startwrite + lencopy > (char *)buf->vb_buf + buf->length) {
em28xx_isocdbg("Overflow of %zu bytes past buffer end (1)\n",
- ((char *)startwrite + lencopy) -
+ ((char *)startwrite + lencopy) -
((char *)buf->vb_buf + buf->length));
remain = (char *)buf->vb_buf + buf->length -
(char *)startwrite;
@@ -548,7 +547,7 @@
}
static inline void print_err_status(struct em28xx *dev,
- int packet, int status)
+ int packet, int status)
{
char *errmsg = "Unknown";
@@ -831,7 +830,6 @@
return 1;
}
-
static int get_ressource(enum v4l2_buf_type f_type)
{
switch (f_type) {
@@ -1003,6 +1001,7 @@
}
while (!list_empty(&vidq->active)) {
struct em28xx_buffer *buf;
+
buf = list_entry(vidq->active.next, struct em28xx_buffer, list);
list_del(&buf->list);
vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
@@ -1033,6 +1032,7 @@
}
while (!list_empty(&vbiq->active)) {
struct em28xx_buffer *buf;
+
buf = list_entry(vbiq->active.next, struct em28xx_buffer, list);
list_del(&buf->list);
vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
@@ -1109,6 +1109,7 @@
static void video_mux(struct em28xx *dev, int index)
{
struct v4l2_device *v4l2_dev = &dev->v4l2->v4l2_dev;
+
dev->ctl_input = index;
dev->ctl_ainput = INPUT(index)->amux;
dev->ctl_aoutput = INPUT(index)->aout;
@@ -1117,21 +1118,22 @@
dev->ctl_aoutput = EM28XX_AOUT_MASTER;
v4l2_device_call_all(v4l2_dev, 0, video, s_routing,
- INPUT(index)->vmux, 0, 0);
+ INPUT(index)->vmux, 0, 0);
if (dev->board.has_msp34xx) {
if (dev->i2s_speed) {
v4l2_device_call_all(v4l2_dev, 0, audio,
- s_i2s_clock_freq, dev->i2s_speed);
+ s_i2s_clock_freq, dev->i2s_speed);
}
/* Note: this is msp3400 specific */
v4l2_device_call_all(v4l2_dev, 0, audio, s_routing,
- dev->ctl_ainput, MSP_OUTPUT(MSP_SC_IN_DSP_SCART1), 0);
+ dev->ctl_ainput,
+ MSP_OUTPUT(MSP_SC_IN_DSP_SCART1), 0);
}
if (dev->board.adecoder != EM28XX_NOADECODER) {
v4l2_device_call_all(v4l2_dev, 0, audio, s_routing,
- dev->ctl_ainput, dev->ctl_aoutput, 0);
+ dev->ctl_ainput, dev->ctl_aoutput, 0);
}
em28xx_audio_analog_set(dev);
@@ -1203,7 +1205,7 @@
};
static void size_to_scale(struct em28xx *dev,
- unsigned int width, unsigned int height,
+ unsigned int width, unsigned int height,
unsigned int *hscale, unsigned int *vscale)
{
unsigned int maxw = norm_maxw(dev);
@@ -1234,7 +1236,7 @@
------------------------------------------------------------------*/
static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
+ struct v4l2_format *f)
{
struct em28xx *dev = video_drvdata(file);
struct em28xx_v4l2 *v4l2 = dev->v4l2;
@@ -1267,7 +1269,7 @@
}
static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
+ struct v4l2_format *f)
{
struct em28xx *dev = video_drvdata(file);
struct em28xx_v4l2 *v4l2 = dev->v4l2;
@@ -1338,7 +1340,7 @@
/* set new image size */
size_to_scale(dev, v4l2->width, v4l2->height,
- &v4l2->hscale, &v4l2->vscale);
+ &v4l2->hscale, &v4l2->vscale);
em28xx_resolution_set(dev);
@@ -1346,7 +1348,7 @@
}
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
+ struct v4l2_format *f)
{
struct em28xx *dev = video_drvdata(file);
struct em28xx_v4l2 *v4l2 = dev->v4l2;
@@ -1401,7 +1403,7 @@
v4l2->width = f.fmt.pix.width;
v4l2->height = f.fmt.pix.height;
size_to_scale(dev, v4l2->width, v4l2->height,
- &v4l2->hscale, &v4l2->vscale);
+ &v4l2->hscale, &v4l2->vscale);
em28xx_resolution_set(dev);
v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_std, v4l2->norm);
@@ -1422,7 +1424,7 @@
video, g_parm, p);
else
v4l2_video_std_frame_period(v4l2->norm,
- &p->parm.capture.timeperframe);
+ &p->parm.capture.timeperframe);
return rc;
}
@@ -1450,7 +1452,7 @@
};
static int vidioc_enum_input(struct file *file, void *priv,
- struct v4l2_input *i)
+ struct v4l2_input *i)
{
struct em28xx *dev = video_drvdata(file);
unsigned int n;
@@ -1467,7 +1469,7 @@
strcpy(i->name, iname[INPUT(n)->type]);
if ((EM28XX_VMUX_TELEVISION == INPUT(n)->type) ||
- (EM28XX_VMUX_CABLE == INPUT(n)->type))
+ (EM28XX_VMUX_CABLE == INPUT(n)->type))
i->type = V4L2_INPUT_TYPE_TUNER;
i->std = dev->v4l2->vdev->tvnorms;
@@ -1558,7 +1560,7 @@
}
static int vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *t)
+ struct v4l2_tuner *t)
{
struct em28xx *dev = video_drvdata(file);
@@ -1572,7 +1574,7 @@
}
static int vidioc_s_tuner(struct file *file, void *priv,
- const struct v4l2_tuner *t)
+ const struct v4l2_tuner *t)
{
struct em28xx *dev = video_drvdata(file);
@@ -1584,7 +1586,7 @@
}
static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
+ struct v4l2_frequency *f)
{
struct em28xx *dev = video_drvdata(file);
struct em28xx_v4l2 *v4l2 = dev->v4l2;
@@ -1597,7 +1599,7 @@
}
static int vidioc_s_frequency(struct file *file, void *priv,
- const struct v4l2_frequency *f)
+ const struct v4l2_frequency *f)
{
struct v4l2_frequency new_freq = *f;
struct em28xx *dev = video_drvdata(file);
@@ -1615,7 +1617,7 @@
#ifdef CPTCFG_VIDEO_ADV_DEBUG
static int vidioc_g_chip_info(struct file *file, void *priv,
- struct v4l2_dbg_chip_info *chip)
+ struct v4l2_dbg_chip_info *chip)
{
struct em28xx *dev = video_drvdata(file);
@@ -1670,6 +1672,7 @@
reg->val = ret;
} else {
__le16 val = 0;
+
ret = dev->em28xx_read_reg_req_len(dev, USB_REQ_GET_STATUS,
reg->reg, (char *)&val, 2);
if (ret < 0)
@@ -1700,9 +1703,8 @@
}
#endif
-
static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
+ struct v4l2_capability *cap)
{
struct video_device *vdev = video_devdata(file);
struct em28xx *dev = video_drvdata(file);
@@ -1736,7 +1738,7 @@
}
static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
+ struct v4l2_fmtdesc *f)
{
if (unlikely(f->index >= ARRAY_SIZE(format)))
return -EINVAL;
@@ -2177,9 +2179,10 @@
/******************************** usb interface ******************************/
-static struct video_device *em28xx_vdev_init(struct em28xx *dev,
- const struct video_device *template,
- const char *type_name)
+static struct video_device
+*em28xx_vdev_init(struct em28xx *dev,
+ const struct video_device *template,
+ const char *type_name)
{
struct video_device *vfd;
@@ -2344,21 +2347,24 @@
if (dev->board.radio.type)
v4l2_i2c_new_subdev(&v4l2->v4l2_dev,
- &dev->i2c_adap[dev->def_i2c_bus],
- "tuner", dev->board.radio_addr, NULL);
+ &dev->i2c_adap[dev->def_i2c_bus],
+ "tuner", dev->board.radio_addr,
+ NULL);
if (has_demod)
v4l2_i2c_new_subdev(&v4l2->v4l2_dev,
- &dev->i2c_adap[dev->def_i2c_bus], "tuner",
- 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
+ &dev->i2c_adap[dev->def_i2c_bus],
+ "tuner", 0,
+ v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
if (tuner_addr == 0) {
enum v4l2_i2c_tuner_type type =
has_demod ? ADDRS_TV_WITH_DEMOD : ADDRS_TV;
struct v4l2_subdev *sd;
sd = v4l2_i2c_new_subdev(&v4l2->v4l2_dev,
- &dev->i2c_adap[dev->def_i2c_bus], "tuner",
- 0, v4l2_i2c_tuner_addrs(type));
+ &dev->i2c_adap[dev->def_i2c_bus],
+ "tuner", 0,
+ v4l2_i2c_tuner_addrs(type));
if (sd)
tuner_addr = v4l2_i2c_subdev_addr(sd);
@@ -2378,20 +2384,20 @@
ret = em28xx_audio_setup(dev);
if (ret < 0) {
em28xx_errdev("%s: Error while setting audio - error [%d]!\n",
- __func__, ret);
+ __func__, ret);
goto unregister_dev;
}
if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops,
- V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops,
- V4L2_CID_AUDIO_VOLUME, 0, 0x1f, 1, 0x1f);
+ V4L2_CID_AUDIO_VOLUME, 0, 0x1f, 1, 0x1f);
} else {
/* install the em28xx notify callback */
v4l2_ctrl_notify(v4l2_ctrl_find(hdl, V4L2_CID_AUDIO_MUTE),
- em28xx_ctrl_notify, dev);
+ em28xx_ctrl_notify, dev);
v4l2_ctrl_notify(v4l2_ctrl_find(hdl, V4L2_CID_AUDIO_VOLUME),
- em28xx_ctrl_notify, dev);
+ em28xx_ctrl_notify, dev);
}
/* wake i2c devices */
@@ -2518,7 +2524,7 @@
/* register v4l2 video video_device */
ret = video_register_device(v4l2->vdev, VFL_TYPE_GRABBER,
- video_nr[dev->devno]);
+ video_nr[dev->devno]);
if (ret) {
em28xx_errdev("unable to register video device (error=%i).\n",
ret);
diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h
index a21a746..9c70753 100644
--- a/drivers/media/usb/em28xx/em28xx.h
+++ b/drivers/media/usb/em28xx/em28xx.h
@@ -141,6 +141,8 @@
#define EM28178_BOARD_PCTV_461E 92
#define EM2874_BOARD_KWORLD_UB435Q_V3 93
#define EM28178_BOARD_PCTV_292E 94
+#define EM2861_BOARD_LEADTEK_VC100 95
+#define EM28178_BOARD_TERRATEC_T2_STICK_HD 96
/* Limits minimum and default number of buffers */
#define EM28XX_MIN_BUF 4
@@ -215,7 +217,6 @@
EM28XX_DIGITAL_MODE,
};
-
struct em28xx;
struct em28xx_usb_bufs {
@@ -243,11 +244,11 @@
struct em28xx_usb_bufs digital_bufs;
/* Stores already requested buffers */
- struct em28xx_buffer *vid_buf;
- struct em28xx_buffer *vbi_buf;
+ struct em28xx_buffer *vid_buf;
+ struct em28xx_buffer *vbi_buf;
/* copy data from URB */
- int (*urb_data_copy) (struct em28xx *dev, struct urb *urb);
+ int (*urb_data_copy)(struct em28xx *dev, struct urb *urb);
};
@@ -695,14 +696,14 @@
char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */
/* helper funcs that call usb_control_msg */
- int (*em28xx_write_regs) (struct em28xx *dev, u16 reg,
- char *buf, int len);
- int (*em28xx_read_reg) (struct em28xx *dev, u16 reg);
- int (*em28xx_read_reg_req_len) (struct em28xx *dev, u8 req, u16 reg,
- char *buf, int len);
- int (*em28xx_write_regs_req) (struct em28xx *dev, u8 req, u16 reg,
- char *buf, int len);
- int (*em28xx_read_reg_req) (struct em28xx *dev, u8 req, u16 reg);
+ int (*em28xx_write_regs)(struct em28xx *dev, u16 reg,
+ char *buf, int len);
+ int (*em28xx_read_reg)(struct em28xx *dev, u16 reg);
+ int (*em28xx_read_reg_req_len)(struct em28xx *dev, u8 req, u16 reg,
+ char *buf, int len);
+ int (*em28xx_write_regs_req)(struct em28xx *dev, u8 req, u16 reg,
+ char *buf, int len);
+ int (*em28xx_read_reg_req)(struct em28xx *dev, u8 req, u16 reg);
enum em28xx_mode mode;
@@ -745,7 +746,7 @@
int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len);
int em28xx_write_reg(struct em28xx *dev, u16 reg, u8 val);
int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
- u8 bitmask);
+ u8 bitmask);
int em28xx_toggle_reg_bits(struct em28xx *dev, u16 reg, u8 bitmask);
int em28xx_read_ac97(struct em28xx *dev, u8 reg);
diff --git a/drivers/media/usb/go7007/go7007-v4l2.c b/drivers/media/usb/go7007/go7007-v4l2.c
index ec799b4..d6bf982 100644
--- a/drivers/media/usb/go7007/go7007-v4l2.c
+++ b/drivers/media/usb/go7007/go7007-v4l2.c
@@ -252,7 +252,7 @@
if (go->board_info->sensor_flags & GO7007_SENSOR_SCALING) {
struct v4l2_mbus_framefmt mbus_fmt;
- mbus_fmt.code = V4L2_MBUS_FMT_FIXED;
+ mbus_fmt.code = MEDIA_BUS_FMT_FIXED;
mbus_fmt.width = fmt ? fmt->fmt.pix.width : width;
mbus_fmt.height = height;
go->encoder_h_halve = 0;
diff --git a/drivers/media/usb/gspca/Kconfig b/drivers/media/usb/gspca/Kconfig
index b016ea7..918af60 100644
--- a/drivers/media/usb/gspca/Kconfig
+++ b/drivers/media/usb/gspca/Kconfig
@@ -168,7 +168,7 @@
module will be called gspca_ov519.
config USB_GSPCA_OV534
- depends on !BACKPORT_KERNEL_3_5
+ depends on !KERNEL_3_5
tristate "OV534 OV772x USB Camera Driver"
depends on m
depends on VIDEO_V4L2 && USB_GSPCA
diff --git a/drivers/media/usb/hackrf/hackrf.c b/drivers/media/usb/hackrf/hackrf.c
index 527e891..f7df15a 100644
--- a/drivers/media/usb/hackrf/hackrf.c
+++ b/drivers/media/usb/hackrf/hackrf.c
@@ -933,7 +933,7 @@
dev->bandwidth->val = bandwidth;
dev->bandwidth->cur.val = bandwidth;
- dev_dbg(dev->dev, "bandwidth selected=%d\n", bandwidth_lut[i].freq);
+ dev_dbg(dev->dev, "bandwidth selected=%d\n", bandwidth);
u16tmp = 0;
u16tmp |= ((bandwidth >> 0) & 0xff) << 0;
diff --git a/drivers/media/usb/msi2500/Kconfig b/drivers/media/usb/msi2500/Kconfig
index bcbee22..73096d6 100644
--- a/drivers/media/usb/msi2500/Kconfig
+++ b/drivers/media/usb/msi2500/Kconfig
@@ -1,5 +1,5 @@
config USB_MSI2500
- depends on !BACKPORT_KERNEL_3_4
+ depends on !KERNEL_3_4
tristate "Mirics MSi2500"
depends on m
depends on VIDEO_V4L2 && SPI
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-context.c b/drivers/media/usb/pvrusb2/pvrusb2-context.c
index 7c19ff7..c8761c7 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-context.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-context.c
@@ -80,7 +80,7 @@
static void pvr2_context_destroy(struct pvr2_context *mp)
{
pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (destroy)",mp);
- if (mp->hdw) pvr2_hdw_destroy(mp->hdw);
+ pvr2_hdw_destroy(mp->hdw);
pvr2_context_set_notify(mp, 0);
mutex_lock(&pvr2_context_mutex);
if (mp->exist_next) {
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c
index 9623b62..2fd9b5e 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c
@@ -2966,7 +2966,7 @@
memset(&fmt, 0, sizeof(fmt));
fmt.width = hdw->res_hor_val;
fmt.height = hdw->res_ver_val;
- fmt.code = V4L2_MBUS_FMT_FIXED;
+ fmt.code = MEDIA_BUS_FMT_FIXED;
pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_size(%dx%d)",
fmt.width, fmt.height);
v4l2_device_call_all(&hdw->v4l2_dev, 0, video, s_mbus_fmt, &fmt);
diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c
index ccc0009..de55e96 100644
--- a/drivers/media/usb/s2255/s2255drv.c
+++ b/drivers/media/usb/s2255/s2255drv.c
@@ -558,27 +558,30 @@
}
-static int s2255_got_frame(struct s2255_vc *vc, int jpgsize)
+static void s2255_got_frame(struct s2255_vc *vc, int jpgsize)
{
struct s2255_buffer *buf;
struct s2255_dev *dev = to_s2255_dev(vc->vdev.v4l2_dev);
unsigned long flags = 0;
- int rc = 0;
+
spin_lock_irqsave(&vc->qlock, flags);
if (list_empty(&vc->buf_list)) {
dprintk(dev, 1, "No active queue to serve\n");
- rc = -1;
- goto unlock;
+ spin_unlock_irqrestore(&vc->qlock, flags);
+ return;
}
buf = list_entry(vc->buf_list.next,
struct s2255_buffer, list);
list_del(&buf->list);
v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
- s2255_fillbuff(vc, buf, jpgsize);
- dprintk(dev, 2, "%s: [buf] [%p]\n", __func__, buf);
-unlock:
+ buf->vb.v4l2_buf.field = vc->field;
+ buf->vb.v4l2_buf.sequence = vc->frame_count;
spin_unlock_irqrestore(&vc->qlock, flags);
- return rc;
+
+ s2255_fillbuff(vc, buf, jpgsize);
+ /* tell v4l buffer was filled */
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
+ dprintk(dev, 2, "%s: [buf] [%p]\n", __func__, buf);
}
static const struct s2255_fmt *format_by_fourcc(int fourcc)
@@ -632,7 +635,7 @@
break;
case V4L2_PIX_FMT_JPEG:
case V4L2_PIX_FMT_MJPEG:
- buf->vb.v4l2_buf.length = jpgsize;
+ vb2_set_plane_payload(&buf->vb, 0, jpgsize);
memcpy(vbuf, tmpbuf, jpgsize);
break;
case V4L2_PIX_FMT_YUV422P:
@@ -649,11 +652,6 @@
}
dprintk(dev, 2, "s2255fill at : Buffer 0x%08lx size= %d\n",
(unsigned long)vbuf, pos);
- /* tell v4l buffer was filled */
- buf->vb.v4l2_buf.field = vc->field;
- buf->vb.v4l2_buf.sequence = vc->frame_count;
- v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
- vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
}
@@ -1976,8 +1974,7 @@
{
unsigned long i;
for (i = 0; i < SYS_FRAMES; i++) {
- if (vc->buffer.frame[i].lpvbits)
- vfree(vc->buffer.frame[i].lpvbits);
+ vfree(vc->buffer.frame[i].lpvbits);
vc->buffer.frame[i].lpvbits = NULL;
}
return 0;
diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c
index 547f749..963eae2 100644
--- a/drivers/media/usb/stk1160/stk1160-v4l.c
+++ b/drivers/media/usb/stk1160/stk1160-v4l.c
@@ -475,7 +475,7 @@
struct stk1160 *dev = video_drvdata(file);
/* Match host */
- return stk1160_write_reg(dev, reg->reg, cpu_to_le16(reg->val));
+ return stk1160_write_reg(dev, reg->reg, reg->val);
}
#endif
diff --git a/drivers/media/usb/tlg2300/Kconfig b/drivers/media/usb/tlg2300/Kconfig
deleted file mode 100644
index 1d60d9b..0000000
--- a/drivers/media/usb/tlg2300/Kconfig
+++ /dev/null
@@ -1,17 +0,0 @@
-config VIDEO_TLG2300
- tristate "Telegent TLG2300 USB video capture support"
- depends on m
- depends on VIDEO_DEV && I2C && SND && DVB_CORE
- select VIDEO_TUNER
- select VIDEO_TVEEPROM
- depends on RC_CORE
- select VIDEOBUF_VMALLOC
- depends on SND_PCM
- select VIDEOBUF_DVB
-
- ---help---
- This is a video4linux driver for Telegent tlg2300 based TV cards.
- The driver supports V4L2, DVB-T and radio.
-
- To compile this driver as a module, choose M here: the
- module will be called poseidon
diff --git a/drivers/media/usb/tlg2300/Makefile b/drivers/media/usb/tlg2300/Makefile
deleted file mode 100644
index 643eb25..0000000
--- a/drivers/media/usb/tlg2300/Makefile
+++ /dev/null
@@ -1,9 +0,0 @@
-poseidon-objs := pd-video.o pd-alsa.o pd-dvb.o pd-radio.o pd-main.o
-
-obj-$(CPTCFG_VIDEO_TLG2300) += poseidon.o
-
-ccflags-y += -I$(backport_srctree)/drivers/media/i2c
-ccflags-y += -I$(backport_srctree)/drivers/media/tuners
-ccflags-y += -I$(backport_srctree)/drivers/media/dvb-core
-ccflags-y += -I$(backport_srctree)/drivers/media/dvb-frontends
-
diff --git a/drivers/media/usb/tlg2300/pd-alsa.c b/drivers/media/usb/tlg2300/pd-alsa.c
deleted file mode 100644
index dd8fe10..0000000
--- a/drivers/media/usb/tlg2300/pd-alsa.c
+++ /dev/null
@@ -1,337 +0,0 @@
-#include <linux/kernel.h>
-#include <linux/usb.h>
-#include <linux/init.h>
-#include <linux/sound.h>
-#include <linux/spinlock.h>
-#include <linux/soundcard.h>
-#include <linux/vmalloc.h>
-#include <linux/proc_fs.h>
-#include <linux/module.h>
-#include <linux/gfp.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/info.h>
-#include <sound/initval.h>
-#include <sound/control.h>
-#include <media/v4l2-common.h>
-#include "pd-common.h"
-#include "vendorcmds.h"
-
-static void complete_handler_audio(struct urb *urb);
-#define AUDIO_EP (0x83)
-#define AUDIO_BUF_SIZE (512)
-#define PERIOD_SIZE (1024 * 8)
-#define PERIOD_MIN (4)
-#define PERIOD_MAX PERIOD_MIN
-
-static struct snd_pcm_hardware snd_pd_hw_capture = {
- .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_MMAP_VALID,
-
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .rates = SNDRV_PCM_RATE_48000,
-
- .rate_min = 48000,
- .rate_max = 48000,
- .channels_min = 2,
- .channels_max = 2,
- .buffer_bytes_max = PERIOD_SIZE * PERIOD_MIN,
- .period_bytes_min = PERIOD_SIZE,
- .period_bytes_max = PERIOD_SIZE,
- .periods_min = PERIOD_MIN,
- .periods_max = PERIOD_MAX,
- /*
- .buffer_bytes_max = 62720 * 8,
- .period_bytes_min = 64,
- .period_bytes_max = 12544,
- .periods_min = 2,
- .periods_max = 98
- */
-};
-
-static int snd_pd_capture_open(struct snd_pcm_substream *substream)
-{
- struct poseidon *p = snd_pcm_substream_chip(substream);
- struct poseidon_audio *pa = &p->audio;
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- if (!p)
- return -ENODEV;
- pa->users++;
- pa->card_close = 0;
- pa->capture_pcm_substream = substream;
- runtime->private_data = p;
-
- runtime->hw = snd_pd_hw_capture;
- snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
- usb_autopm_get_interface(p->interface);
- kref_get(&p->kref);
- return 0;
-}
-
-static int snd_pd_pcm_close(struct snd_pcm_substream *substream)
-{
- struct poseidon *p = snd_pcm_substream_chip(substream);
- struct poseidon_audio *pa = &p->audio;
-
- pa->users--;
- pa->card_close = 1;
- usb_autopm_put_interface(p->interface);
- kref_put(&p->kref, poseidon_delete);
- return 0;
-}
-
-static int snd_pd_hw_capture_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned int size;
-
- size = params_buffer_bytes(hw_params);
- if (runtime->dma_area) {
- if (runtime->dma_bytes > size)
- return 0;
- vfree(runtime->dma_area);
- }
- runtime->dma_area = vmalloc(size);
- if (!runtime->dma_area)
- return -ENOMEM;
- else
- runtime->dma_bytes = size;
- return 0;
-}
-
-static int audio_buf_free(struct poseidon *p)
-{
- struct poseidon_audio *pa = &p->audio;
- int i;
-
- for (i = 0; i < AUDIO_BUFS; i++)
- if (pa->urb_array[i])
- usb_kill_urb(pa->urb_array[i]);
- free_all_urb_generic(pa->urb_array, AUDIO_BUFS);
- logpm();
- return 0;
-}
-
-static int snd_pd_hw_capture_free(struct snd_pcm_substream *substream)
-{
- struct poseidon *p = snd_pcm_substream_chip(substream);
-
- logpm();
- audio_buf_free(p);
- return 0;
-}
-
-static int snd_pd_prepare(struct snd_pcm_substream *substream)
-{
- return 0;
-}
-
-#define AUDIO_TRAILER_SIZE (16)
-static inline void handle_audio_data(struct urb *urb, int *period_elapsed)
-{
- struct poseidon_audio *pa = urb->context;
- struct snd_pcm_runtime *runtime = pa->capture_pcm_substream->runtime;
-
- int stride = runtime->frame_bits >> 3;
- int len = urb->actual_length / stride;
- unsigned char *cp = urb->transfer_buffer;
- unsigned int oldptr = pa->rcv_position;
-
- if (urb->actual_length == AUDIO_BUF_SIZE - 4)
- len -= (AUDIO_TRAILER_SIZE / stride);
-
- /* do the copy */
- if (oldptr + len >= runtime->buffer_size) {
- unsigned int cnt = runtime->buffer_size - oldptr;
-
- memcpy(runtime->dma_area + oldptr * stride, cp, cnt * stride);
- memcpy(runtime->dma_area, (cp + cnt * stride),
- (len * stride - cnt * stride));
- } else
- memcpy(runtime->dma_area + oldptr * stride, cp, len * stride);
-
- /* update the statas */
- snd_pcm_stream_lock(pa->capture_pcm_substream);
- pa->rcv_position += len;
- if (pa->rcv_position >= runtime->buffer_size)
- pa->rcv_position -= runtime->buffer_size;
-
- pa->copied_position += (len);
- if (pa->copied_position >= runtime->period_size) {
- pa->copied_position -= runtime->period_size;
- *period_elapsed = 1;
- }
- snd_pcm_stream_unlock(pa->capture_pcm_substream);
-}
-
-static void complete_handler_audio(struct urb *urb)
-{
- struct poseidon_audio *pa = urb->context;
- struct snd_pcm_substream *substream = pa->capture_pcm_substream;
- int period_elapsed = 0;
- int ret;
-
- if (1 == pa->card_close || pa->capture_stream != STREAM_ON)
- return;
-
- if (urb->status != 0) {
- /*if (urb->status == -ESHUTDOWN)*/
- return;
- }
-
- if (substream) {
- if (urb->actual_length) {
- handle_audio_data(urb, &period_elapsed);
- if (period_elapsed)
- snd_pcm_period_elapsed(substream);
- }
- }
-
- ret = usb_submit_urb(urb, GFP_ATOMIC);
- if (ret < 0)
- log("audio urb failed (errcod = %i)", ret);
- return;
-}
-
-static int fire_audio_urb(struct poseidon *p)
-{
- int i, ret = 0;
- struct poseidon_audio *pa = &p->audio;
-
- alloc_bulk_urbs_generic(pa->urb_array, AUDIO_BUFS,
- p->udev, AUDIO_EP,
- AUDIO_BUF_SIZE, GFP_ATOMIC,
- complete_handler_audio, pa);
-
- for (i = 0; i < AUDIO_BUFS; i++) {
- ret = usb_submit_urb(pa->urb_array[i], GFP_KERNEL);
- if (ret)
- log("urb err : %d", ret);
- }
- log();
- return ret;
-}
-
-static int snd_pd_capture_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct poseidon *p = snd_pcm_substream_chip(substream);
- struct poseidon_audio *pa = &p->audio;
-
- if (debug_mode)
- log("cmd %d, audio stat : %d\n", cmd, pa->capture_stream);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_START:
- if (pa->capture_stream == STREAM_ON)
- return 0;
-
- pa->rcv_position = pa->copied_position = 0;
- pa->capture_stream = STREAM_ON;
-
- if (in_hibernation(p))
- return 0;
- fire_audio_urb(p);
- return 0;
-
- case SNDRV_PCM_TRIGGER_SUSPEND:
- pa->capture_stream = STREAM_SUSPEND;
- return 0;
- case SNDRV_PCM_TRIGGER_STOP:
- pa->capture_stream = STREAM_OFF;
- return 0;
- default:
- return -EINVAL;
- }
-}
-
-static snd_pcm_uframes_t
-snd_pd_capture_pointer(struct snd_pcm_substream *substream)
-{
- struct poseidon *p = snd_pcm_substream_chip(substream);
- struct poseidon_audio *pa = &p->audio;
- return pa->rcv_position;
-}
-
-static struct page *snd_pcm_pd_get_page(struct snd_pcm_substream *subs,
- unsigned long offset)
-{
- void *pageptr = subs->runtime->dma_area + offset;
- return vmalloc_to_page(pageptr);
-}
-
-static struct snd_pcm_ops pcm_capture_ops = {
- .open = snd_pd_capture_open,
- .close = snd_pd_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_pd_hw_capture_params,
- .hw_free = snd_pd_hw_capture_free,
- .prepare = snd_pd_prepare,
- .trigger = snd_pd_capture_trigger,
- .pointer = snd_pd_capture_pointer,
- .page = snd_pcm_pd_get_page,
-};
-
-#ifdef CONFIG_PM
-int pm_alsa_suspend(struct poseidon *p)
-{
- logpm(p);
- audio_buf_free(p);
- return 0;
-}
-
-int pm_alsa_resume(struct poseidon *p)
-{
- logpm(p);
- fire_audio_urb(p);
- return 0;
-}
-#endif
-
-int poseidon_audio_init(struct poseidon *p)
-{
- struct poseidon_audio *pa = &p->audio;
- struct snd_card *card;
- struct snd_pcm *pcm;
- int ret;
-
- ret = snd_card_new(&p->interface->dev, -1, "Telegent",
- THIS_MODULE, 0, &card);
- if (ret != 0)
- return ret;
-
- ret = snd_pcm_new(card, "poseidon audio", 0, 0, 1, &pcm);
- if (ret < 0) {
- snd_card_free(card);
- return ret;
- }
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
- pcm->info_flags = 0;
- pcm->private_data = p;
- strcpy(pcm->name, "poseidon audio capture");
-
- strcpy(card->driver, "ALSA driver");
- strcpy(card->shortname, "poseidon Audio");
- strcpy(card->longname, "poseidon ALSA Audio");
-
- if (snd_card_register(card)) {
- snd_card_free(card);
- return -ENOMEM;
- }
- pa->card = card;
- return 0;
-}
-
-int poseidon_audio_free(struct poseidon *p)
-{
- struct poseidon_audio *pa = &p->audio;
-
- if (pa->card)
- snd_card_free(pa->card);
- return 0;
-}
diff --git a/drivers/media/usb/tlg2300/pd-common.h b/drivers/media/usb/tlg2300/pd-common.h
deleted file mode 100644
index 9e23ad3..0000000
--- a/drivers/media/usb/tlg2300/pd-common.h
+++ /dev/null
@@ -1,271 +0,0 @@
-#ifndef PD_COMMON_H
-#define PD_COMMON_H
-
-#include <linux/fs.h>
-#include <linux/wait.h>
-#include <linux/list.h>
-#include <linux/videodev2.h>
-#include <linux/semaphore.h>
-#include <linux/usb.h>
-#include <linux/poll.h>
-#include <media/videobuf-vmalloc.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ctrls.h>
-
-#include "dvb_frontend.h"
-#include "dvbdev.h"
-#include "dvb_demux.h"
-#include "dmxdev.h"
-
-#define SBUF_NUM 8
-#define MAX_BUFFER_NUM 6
-#define PK_PER_URB 32
-#define ISO_PKT_SIZE 3072
-
-#define POSEIDON_STATE_NONE (0x0000)
-#define POSEIDON_STATE_ANALOG (0x0001)
-#define POSEIDON_STATE_FM (0x0002)
-#define POSEIDON_STATE_DVBT (0x0004)
-#define POSEIDON_STATE_DISCONNECT (0x0080)
-
-#define PM_SUSPEND_DELAY 3
-
-#define V4L_PAL_VBI_LINES 18
-#define V4L_NTSC_VBI_LINES 12
-#define V4L_PAL_VBI_FRAMESIZE (V4L_PAL_VBI_LINES * 1440 * 2)
-#define V4L_NTSC_VBI_FRAMESIZE (V4L_NTSC_VBI_LINES * 1440 * 2)
-
-#define TUNER_FREQ_MIN (45000000U)
-#define TUNER_FREQ_MAX (862000000U)
-
-struct vbi_data {
- struct video_device v_dev;
- struct video_data *video;
- struct front_face *front;
-
- unsigned int copied;
- unsigned int vbi_size; /* the whole size of two fields */
- int users;
-};
-
-/*
- * This is the running context of the video, it is useful for
- * resume()
- */
-struct running_context {
- u32 freq; /* VIDIOC_S_FREQUENCY */
- int audio_idx; /* VIDIOC_S_TUNER */
- v4l2_std_id tvnormid; /* VIDIOC_S_STD */
- int sig_index; /* VIDIOC_S_INPUT */
- struct v4l2_pix_format pix; /* VIDIOC_S_FMT */
-};
-
-struct video_data {
- /* v4l2 video device */
- struct video_device v_dev;
- struct v4l2_ctrl_handler ctrl_handler;
-
- /* the working context */
- struct running_context context;
-
- /* for data copy */
- int field_count;
-
- char *dst;
- int lines_copied;
- int prev_left;
-
- int lines_per_field;
- int lines_size;
-
- /* for communication */
- u8 endpoint_addr;
- struct urb *urb_array[SBUF_NUM];
- struct vbi_data *vbi;
- struct poseidon *pd;
- struct front_face *front;
-
- int is_streaming;
- int users;
-
- /* for bubble handler */
- struct work_struct bubble_work;
-};
-
-enum pcm_stream_state {
- STREAM_OFF,
- STREAM_ON,
- STREAM_SUSPEND,
-};
-
-#define AUDIO_BUFS (3)
-#define CAPTURE_STREAM_EN 1
-struct poseidon_audio {
- struct urb *urb_array[AUDIO_BUFS];
- unsigned int copied_position;
- struct snd_pcm_substream *capture_pcm_substream;
-
- unsigned int rcv_position;
- struct snd_card *card;
- int card_close;
-
- int users;
- int pm_state;
- enum pcm_stream_state capture_stream;
-};
-
-struct radio_data {
- __u32 fm_freq;
- unsigned int is_radio_streaming;
- int pre_emphasis;
- struct video_device fm_dev;
- struct v4l2_ctrl_handler ctrl_handler;
-};
-
-#define DVB_SBUF_NUM 4
-#define DVB_URB_BUF_SIZE 0x2000
-struct pd_dvb_adapter {
- struct dvb_adapter dvb_adap;
- struct dvb_frontend dvb_fe;
- struct dmxdev dmxdev;
- struct dvb_demux demux;
-
- atomic_t users;
- atomic_t active_feed;
-
- /* data transfer */
- s32 is_streaming;
- struct urb *urb_array[DVB_SBUF_NUM];
- struct poseidon *pd_device;
- u8 ep_addr;
- u8 reserved[3];
-
- /* data for power resume*/
- struct dtv_frontend_properties fe_param;
-
- /* for channel scanning */
- int prev_freq;
- int bandwidth;
- unsigned long last_jiffies;
-};
-
-struct front_face {
- /* use this field to distinguish VIDEO and VBI */
- enum v4l2_buf_type type;
-
- /* for host */
- struct videobuf_queue q;
-
- /* the bridge for host and device */
- struct videobuf_buffer *curr_frame;
-
- /* for device */
- spinlock_t queue_lock;
- struct list_head active;
- struct poseidon *pd;
-};
-
-struct poseidon {
- struct list_head device_list;
-
- struct mutex lock;
- struct kref kref;
-
- /* for V4L2 */
- struct v4l2_device v4l2_dev;
-
- /* hardware info */
- struct usb_device *udev;
- struct usb_interface *interface;
- int cur_transfer_mode;
-
- struct video_data video_data; /* video */
- struct vbi_data vbi_data; /* vbi */
- struct poseidon_audio audio; /* audio (alsa) */
- struct radio_data radio_data; /* FM */
- struct pd_dvb_adapter dvb_data; /* DVB */
-
- u32 state;
- struct file *file_for_stream; /* the active stream*/
-
-#ifdef CONFIG_PM
- int (*pm_suspend)(struct poseidon *);
- int (*pm_resume)(struct poseidon *);
- pm_message_t msg;
-
- struct work_struct pm_work;
- u8 portnum;
-#endif
-};
-
-struct poseidon_format {
- char *name;
- int fourcc; /* video4linux 2 */
- int depth; /* bit/pixel */
- int flags;
-};
-
-struct poseidon_tvnorm {
- v4l2_std_id v4l2_id;
- char name[12];
- u32 tlg_tvnorm;
-};
-
-/* video */
-int pd_video_init(struct poseidon *);
-void pd_video_exit(struct poseidon *);
-int stop_all_video_stream(struct poseidon *);
-
-/* alsa audio */
-int poseidon_audio_init(struct poseidon *);
-int poseidon_audio_free(struct poseidon *);
-#ifdef CONFIG_PM
-int pm_alsa_suspend(struct poseidon *);
-int pm_alsa_resume(struct poseidon *);
-#endif
-
-/* dvb */
-int pd_dvb_usb_device_init(struct poseidon *);
-void pd_dvb_usb_device_exit(struct poseidon *);
-void pd_dvb_usb_device_cleanup(struct poseidon *);
-int pd_dvb_get_adapter_num(struct pd_dvb_adapter *);
-void dvb_stop_streaming(struct pd_dvb_adapter *);
-
-/* FM */
-int poseidon_fm_init(struct poseidon *);
-int poseidon_fm_exit(struct poseidon *);
-
-/* vendor command ops */
-int send_set_req(struct poseidon*, u8, s32, s32*);
-int send_get_req(struct poseidon*, u8, s32, void*, s32*, s32);
-s32 set_tuner_mode(struct poseidon*, unsigned char);
-
-/* bulk urb alloc/free */
-int alloc_bulk_urbs_generic(struct urb **urb_array, int num,
- struct usb_device *udev, u8 ep_addr,
- int buf_size, gfp_t gfp_flags,
- usb_complete_t complete_fn, void *context);
-void free_all_urb_generic(struct urb **urb_array, int num);
-
-/* misc */
-void poseidon_delete(struct kref *kref);
-extern int debug_mode;
-void set_debug_mode(struct video_device *vfd, int debug_mode);
-
-#ifdef CONFIG_PM
-#define in_hibernation(pd) (pd->msg.event == PM_EVENT_FREEZE)
-#else
-#define in_hibernation(pd) (0)
-#endif
-#define get_pm_count(p) (atomic_read(&(p)->interface->pm_usage_cnt))
-
-#define log(a, ...) printk(KERN_DEBUG "\t[ %s : %.3d ] "a"\n", \
- __func__, __LINE__, ## __VA_ARGS__)
-
-/* for power management */
-#define logpm(pd) do {\
- if (debug_mode & 0x10)\
- log();\
- } while (0)
-
-#endif
diff --git a/drivers/media/usb/tlg2300/pd-dvb.c b/drivers/media/usb/tlg2300/pd-dvb.c
deleted file mode 100644
index ca4994a..0000000
--- a/drivers/media/usb/tlg2300/pd-dvb.c
+++ /dev/null
@@ -1,597 +0,0 @@
-#include "pd-common.h"
-#include <linux/kernel.h>
-#include <linux/usb.h>
-#include <linux/time.h>
-#include <linux/dvb/dmx.h>
-#include <linux/delay.h>
-#include <linux/gfp.h>
-
-#include "vendorcmds.h"
-#include <linux/sched.h>
-#include <linux/atomic.h>
-
-static void dvb_urb_cleanup(struct pd_dvb_adapter *pd_dvb);
-
-static int dvb_bandwidth[][2] = {
- { TLG_BW_8, 8000000 },
- { TLG_BW_7, 7000000 },
- { TLG_BW_6, 6000000 }
-};
-static int dvb_bandwidth_length = ARRAY_SIZE(dvb_bandwidth);
-
-static s32 dvb_start_streaming(struct pd_dvb_adapter *pd_dvb);
-static int poseidon_check_mode_dvbt(struct poseidon *pd)
-{
- s32 ret = 0, cmd_status = 0;
-
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ/4);
-
- ret = usb_set_interface(pd->udev, 0, BULK_ALTERNATE_IFACE);
- if (ret != 0)
- return ret;
-
- ret = set_tuner_mode(pd, TLG_MODE_CAPS_DVB_T);
- if (ret)
- return ret;
-
- /* signal source */
- ret = send_set_req(pd, SGNL_SRC_SEL, TLG_SIG_SRC_ANTENNA, &cmd_status);
- if (ret|cmd_status)
- return ret;
-
- return 0;
-}
-
-/* acquire :
- * 1 == open
- * 0 == release
- */
-static int poseidon_ts_bus_ctrl(struct dvb_frontend *fe, int acquire)
-{
- struct poseidon *pd = fe->demodulator_priv;
- struct pd_dvb_adapter *pd_dvb;
- int ret = 0;
-
- if (!pd)
- return -ENODEV;
-
- pd_dvb = container_of(fe, struct pd_dvb_adapter, dvb_fe);
- if (acquire) {
- mutex_lock(&pd->lock);
- if (pd->state & POSEIDON_STATE_DISCONNECT) {
- ret = -ENODEV;
- goto open_out;
- }
-
- if (pd->state && !(pd->state & POSEIDON_STATE_DVBT)) {
- ret = -EBUSY;
- goto open_out;
- }
-
- usb_autopm_get_interface(pd->interface);
- if (0 == pd->state) {
- ret = poseidon_check_mode_dvbt(pd);
- if (ret < 0) {
- usb_autopm_put_interface(pd->interface);
- goto open_out;
- }
- pd->state |= POSEIDON_STATE_DVBT;
- pd_dvb->bandwidth = 0;
- pd_dvb->prev_freq = 0;
- }
- atomic_inc(&pd_dvb->users);
- kref_get(&pd->kref);
-open_out:
- mutex_unlock(&pd->lock);
- } else {
- dvb_stop_streaming(pd_dvb);
-
- if (atomic_dec_and_test(&pd_dvb->users)) {
- mutex_lock(&pd->lock);
- pd->state &= ~POSEIDON_STATE_DVBT;
- mutex_unlock(&pd->lock);
- }
- kref_put(&pd->kref, poseidon_delete);
- usb_autopm_put_interface(pd->interface);
- }
- return ret;
-}
-
-#ifdef CONFIG_PM
-static void poseidon_fe_release(struct dvb_frontend *fe)
-{
- struct poseidon *pd = fe->demodulator_priv;
-
- pd->pm_suspend = NULL;
- pd->pm_resume = NULL;
-}
-#else
-#define poseidon_fe_release NULL
-#endif
-
-static s32 poseidon_fe_sleep(struct dvb_frontend *fe)
-{
- return 0;
-}
-
-/*
- * return true if we can satisfy the conditions, else return false.
- */
-static bool check_scan_ok(__u32 freq, int bandwidth,
- struct pd_dvb_adapter *adapter)
-{
- if (bandwidth < 0)
- return false;
-
- if (adapter->prev_freq == freq
- && adapter->bandwidth == bandwidth) {
- long nl = jiffies - adapter->last_jiffies;
- unsigned int msec ;
-
- msec = jiffies_to_msecs(abs(nl));
- return msec > 15000 ? true : false;
- }
- return true;
-}
-
-/*
- * Check if the firmware delays too long for an invalid frequency.
- */
-static int fw_delay_overflow(struct pd_dvb_adapter *adapter)
-{
- long nl = jiffies - adapter->last_jiffies;
- unsigned int msec ;
-
- msec = jiffies_to_msecs(abs(nl));
- return msec > 800 ? true : false;
-}
-
-static int poseidon_set_fe(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *fep = &fe->dtv_property_cache;
- s32 ret = 0, cmd_status = 0;
- s32 i, bandwidth = -1;
- struct poseidon *pd = fe->demodulator_priv;
- struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
-
- if (in_hibernation(pd))
- return -EBUSY;
-
- mutex_lock(&pd->lock);
- for (i = 0; i < dvb_bandwidth_length; i++)
- if (fep->bandwidth_hz == dvb_bandwidth[i][1])
- bandwidth = dvb_bandwidth[i][0];
-
- if (check_scan_ok(fep->frequency, bandwidth, pd_dvb)) {
- ret = send_set_req(pd, TUNE_FREQ_SELECT,
- fep->frequency / 1000, &cmd_status);
- if (ret | cmd_status) {
- log("error line");
- goto front_out;
- }
-
- ret = send_set_req(pd, DVBT_BANDW_SEL,
- bandwidth, &cmd_status);
- if (ret | cmd_status) {
- log("error line");
- goto front_out;
- }
-
- ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
- if (ret | cmd_status) {
- log("error line");
- goto front_out;
- }
-
- /* save the context for future */
- memcpy(&pd_dvb->fe_param, fep, sizeof(*fep));
- pd_dvb->bandwidth = bandwidth;
- pd_dvb->prev_freq = fep->frequency;
- pd_dvb->last_jiffies = jiffies;
- }
-front_out:
- mutex_unlock(&pd->lock);
- return ret;
-}
-
-#ifdef CONFIG_PM
-static int pm_dvb_suspend(struct poseidon *pd)
-{
- struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
- dvb_stop_streaming(pd_dvb);
- dvb_urb_cleanup(pd_dvb);
- msleep(500);
- return 0;
-}
-
-static int pm_dvb_resume(struct poseidon *pd)
-{
- struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
-
- poseidon_check_mode_dvbt(pd);
- msleep(300);
- poseidon_set_fe(&pd_dvb->dvb_fe);
-
- dvb_start_streaming(pd_dvb);
- return 0;
-}
-#endif
-
-static s32 poseidon_fe_init(struct dvb_frontend *fe)
-{
- struct poseidon *pd = fe->demodulator_priv;
- struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
-
-#ifdef CONFIG_PM
- pd->pm_suspend = pm_dvb_suspend;
- pd->pm_resume = pm_dvb_resume;
-#endif
- memset(&pd_dvb->fe_param, 0,
- sizeof(struct dtv_frontend_properties));
- return 0;
-}
-
-static int poseidon_get_fe(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *fep = &fe->dtv_property_cache;
- struct poseidon *pd = fe->demodulator_priv;
- struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
-
- memcpy(fep, &pd_dvb->fe_param, sizeof(*fep));
- return 0;
-}
-
-static int poseidon_fe_get_tune_settings(struct dvb_frontend *fe,
- struct dvb_frontend_tune_settings *tune)
-{
- tune->min_delay_ms = 1000;
- return 0;
-}
-
-static int poseidon_read_status(struct dvb_frontend *fe, fe_status_t *stat)
-{
- struct poseidon *pd = fe->demodulator_priv;
- s32 ret = -1, cmd_status;
- struct tuner_dtv_sig_stat_s status = {};
-
- if (in_hibernation(pd))
- return -EBUSY;
- mutex_lock(&pd->lock);
-
- ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_DVB_T,
- &status, &cmd_status, sizeof(status));
- if (ret | cmd_status) {
- log("get tuner status error");
- goto out;
- }
-
- if (debug_mode)
- log("P : %d, L %d, LB :%d", status.sig_present,
- status.sig_locked, status.sig_lock_busy);
-
- if (status.sig_lock_busy) {
- goto out;
- } else if (status.sig_present || status.sig_locked) {
- *stat |= FE_HAS_LOCK | FE_HAS_SIGNAL | FE_HAS_CARRIER
- | FE_HAS_SYNC | FE_HAS_VITERBI;
- } else {
- if (fw_delay_overflow(&pd->dvb_data))
- *stat |= FE_TIMEDOUT;
- }
-out:
- mutex_unlock(&pd->lock);
- return ret;
-}
-
-static int poseidon_read_ber(struct dvb_frontend *fe, u32 *ber)
-{
- struct poseidon *pd = fe->demodulator_priv;
- struct tuner_ber_rate_s tlg_ber = {};
- s32 ret = -1, cmd_status;
-
- mutex_lock(&pd->lock);
- ret = send_get_req(pd, TUNER_BER_RATE, 0,
- &tlg_ber, &cmd_status, sizeof(tlg_ber));
- if (ret | cmd_status)
- goto out;
- *ber = tlg_ber.ber_rate;
-out:
- mutex_unlock(&pd->lock);
- return ret;
-}
-
-static s32 poseidon_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
-{
- struct poseidon *pd = fe->demodulator_priv;
- struct tuner_dtv_sig_stat_s status = {};
- s32 ret = 0, cmd_status;
-
- mutex_lock(&pd->lock);
- ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_DVB_T,
- &status, &cmd_status, sizeof(status));
- if (ret | cmd_status)
- goto out;
- if ((status.sig_present || status.sig_locked) && !status.sig_strength)
- *strength = 0xFFFF;
- else
- *strength = status.sig_strength;
-out:
- mutex_unlock(&pd->lock);
- return ret;
-}
-
-static int poseidon_read_snr(struct dvb_frontend *fe, u16 *snr)
-{
- return 0;
-}
-
-static int poseidon_read_unc_blocks(struct dvb_frontend *fe, u32 *unc)
-{
- *unc = 0;
- return 0;
-}
-
-static struct dvb_frontend_ops poseidon_frontend_ops = {
- .delsys = { SYS_DVBT },
- .info = {
- .name = "Poseidon DVB-T",
- .frequency_min = 174000000,
- .frequency_max = 862000000,
- .frequency_stepsize = 62500,/* FIXME */
- .caps = FE_CAN_INVERSION_AUTO |
- FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
- FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
- FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
- FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO |
- FE_CAN_GUARD_INTERVAL_AUTO |
- FE_CAN_RECOVER |
- FE_CAN_HIERARCHY_AUTO,
- },
-
- .release = poseidon_fe_release,
-
- .init = poseidon_fe_init,
- .sleep = poseidon_fe_sleep,
-
- .set_frontend = poseidon_set_fe,
- .get_frontend = poseidon_get_fe,
- .get_tune_settings = poseidon_fe_get_tune_settings,
-
- .read_status = poseidon_read_status,
- .read_ber = poseidon_read_ber,
- .read_signal_strength = poseidon_read_signal_strength,
- .read_snr = poseidon_read_snr,
- .read_ucblocks = poseidon_read_unc_blocks,
-
- .ts_bus_ctrl = poseidon_ts_bus_ctrl,
-};
-
-static void dvb_urb_irq(struct urb *urb)
-{
- struct pd_dvb_adapter *pd_dvb = urb->context;
- int len = urb->transfer_buffer_length;
- struct dvb_demux *demux = &pd_dvb->demux;
- s32 ret;
-
- if (!pd_dvb->is_streaming || urb->status) {
- if (urb->status == -EPROTO)
- goto resend;
- return;
- }
-
- if (urb->actual_length == len)
- dvb_dmx_swfilter(demux, urb->transfer_buffer, len);
- else if (urb->actual_length == len - 4) {
- int offset;
- u8 *buf = urb->transfer_buffer;
-
- /*
- * The packet size is 512,
- * last packet contains 456 bytes tsp data
- */
- for (offset = 456; offset < len; offset += 512) {
- if (!strncmp(buf + offset, "DVHS", 4)) {
- dvb_dmx_swfilter(demux, buf, offset);
- if (len > offset + 52 + 4) {
- /*16 bytes trailer + 36 bytes padding */
- buf += offset + 52;
- len -= offset + 52 + 4;
- dvb_dmx_swfilter(demux, buf, len);
- }
- break;
- }
- }
- }
-
-resend:
- ret = usb_submit_urb(urb, GFP_ATOMIC);
- if (ret)
- log(" usb_submit_urb failed: error %d", ret);
-}
-
-static int dvb_urb_init(struct pd_dvb_adapter *pd_dvb)
-{
- if (pd_dvb->urb_array[0])
- return 0;
-
- alloc_bulk_urbs_generic(pd_dvb->urb_array, DVB_SBUF_NUM,
- pd_dvb->pd_device->udev, pd_dvb->ep_addr,
- DVB_URB_BUF_SIZE, GFP_KERNEL,
- dvb_urb_irq, pd_dvb);
- return 0;
-}
-
-static void dvb_urb_cleanup(struct pd_dvb_adapter *pd_dvb)
-{
- free_all_urb_generic(pd_dvb->urb_array, DVB_SBUF_NUM);
-}
-
-static s32 dvb_start_streaming(struct pd_dvb_adapter *pd_dvb)
-{
- struct poseidon *pd = pd_dvb->pd_device;
- int ret = 0;
-
- if (pd->state & POSEIDON_STATE_DISCONNECT)
- return -ENODEV;
-
- mutex_lock(&pd->lock);
- if (!pd_dvb->is_streaming) {
- s32 i, cmd_status = 0;
- /*
- * Once upon a time, there was a difficult bug lying here.
- * ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
- */
-
- ret = send_set_req(pd, PLAY_SERVICE, 1, &cmd_status);
- if (ret | cmd_status)
- goto out;
-
- ret = dvb_urb_init(pd_dvb);
- if (ret < 0)
- goto out;
-
- pd_dvb->is_streaming = 1;
- for (i = 0; i < DVB_SBUF_NUM; i++) {
- ret = usb_submit_urb(pd_dvb->urb_array[i],
- GFP_KERNEL);
- if (ret) {
- log(" submit urb error %d", ret);
- goto out;
- }
- }
- }
-out:
- mutex_unlock(&pd->lock);
- return ret;
-}
-
-void dvb_stop_streaming(struct pd_dvb_adapter *pd_dvb)
-{
- struct poseidon *pd = pd_dvb->pd_device;
-
- mutex_lock(&pd->lock);
- if (pd_dvb->is_streaming) {
- s32 i, ret, cmd_status = 0;
-
- pd_dvb->is_streaming = 0;
-
- for (i = 0; i < DVB_SBUF_NUM; i++)
- if (pd_dvb->urb_array[i])
- usb_kill_urb(pd_dvb->urb_array[i]);
-
- ret = send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP,
- &cmd_status);
- if (ret | cmd_status)
- log("error");
- }
- mutex_unlock(&pd->lock);
-}
-
-static int pd_start_feed(struct dvb_demux_feed *feed)
-{
- struct pd_dvb_adapter *pd_dvb = feed->demux->priv;
- int ret = 0;
-
- if (!pd_dvb)
- return -1;
- if (atomic_inc_return(&pd_dvb->active_feed) == 1)
- ret = dvb_start_streaming(pd_dvb);
- return ret;
-}
-
-static int pd_stop_feed(struct dvb_demux_feed *feed)
-{
- struct pd_dvb_adapter *pd_dvb = feed->demux->priv;
-
- if (!pd_dvb)
- return -1;
- if (atomic_dec_and_test(&pd_dvb->active_feed))
- dvb_stop_streaming(pd_dvb);
- return 0;
-}
-
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-int pd_dvb_usb_device_init(struct poseidon *pd)
-{
- struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
- struct dvb_demux *dvbdemux;
- int ret = 0;
-
- pd_dvb->ep_addr = 0x82;
- atomic_set(&pd_dvb->users, 0);
- atomic_set(&pd_dvb->active_feed, 0);
- pd_dvb->pd_device = pd;
-
- ret = dvb_register_adapter(&pd_dvb->dvb_adap,
- "Poseidon dvbt adapter",
- THIS_MODULE,
- NULL /* for hibernation correctly*/,
- adapter_nr);
- if (ret < 0)
- goto error1;
-
- /* register frontend */
- pd_dvb->dvb_fe.demodulator_priv = pd;
- memcpy(&pd_dvb->dvb_fe.ops, &poseidon_frontend_ops,
- sizeof(struct dvb_frontend_ops));
- ret = dvb_register_frontend(&pd_dvb->dvb_adap, &pd_dvb->dvb_fe);
- if (ret < 0)
- goto error2;
-
- /* register demux device */
- dvbdemux = &pd_dvb->demux;
- dvbdemux->dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING;
- dvbdemux->priv = pd_dvb;
- dvbdemux->feednum = dvbdemux->filternum = 64;
- dvbdemux->start_feed = pd_start_feed;
- dvbdemux->stop_feed = pd_stop_feed;
- dvbdemux->write_to_decoder = NULL;
-
- ret = dvb_dmx_init(dvbdemux);
- if (ret < 0)
- goto error3;
-
- pd_dvb->dmxdev.filternum = pd_dvb->demux.filternum;
- pd_dvb->dmxdev.demux = &pd_dvb->demux.dmx;
- pd_dvb->dmxdev.capabilities = 0;
-
- ret = dvb_dmxdev_init(&pd_dvb->dmxdev, &pd_dvb->dvb_adap);
- if (ret < 0)
- goto error3;
- return 0;
-
-error3:
- dvb_unregister_frontend(&pd_dvb->dvb_fe);
-error2:
- dvb_unregister_adapter(&pd_dvb->dvb_adap);
-error1:
- return ret;
-}
-
-void pd_dvb_usb_device_exit(struct poseidon *pd)
-{
- struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
-
- while (atomic_read(&pd_dvb->users) != 0
- || atomic_read(&pd_dvb->active_feed) != 0) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ);
- }
- dvb_dmxdev_release(&pd_dvb->dmxdev);
- dvb_unregister_frontend(&pd_dvb->dvb_fe);
- dvb_unregister_adapter(&pd_dvb->dvb_adap);
- pd_dvb_usb_device_cleanup(pd);
-}
-
-void pd_dvb_usb_device_cleanup(struct poseidon *pd)
-{
- struct pd_dvb_adapter *pd_dvb = &pd->dvb_data;
-
- dvb_urb_cleanup(pd_dvb);
-}
-
-int pd_dvb_get_adapter_num(struct pd_dvb_adapter *pd_dvb)
-{
- return pd_dvb->dvb_adap.num;
-}
diff --git a/drivers/media/usb/tlg2300/pd-main.c b/drivers/media/usb/tlg2300/pd-main.c
deleted file mode 100644
index b31f479..0000000
--- a/drivers/media/usb/tlg2300/pd-main.c
+++ /dev/null
@@ -1,553 +0,0 @@
-/*
- * device driver for Telegent tlg2300 based TV cards
- *
- * Author :
- * Kang Yong <kangyong@telegent.com>
- * Zhang Xiaobing <xbzhang@telegent.com>
- * Huang Shijie <zyziii@telegent.com> or <shijie8@gmail.com>
- *
- * (c) 2009 Telegent Systems
- * (c) 2010 Telegent Systems
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/kref.h>
-#include <linux/suspend.h>
-#include <linux/usb/quirks.h>
-#include <linux/ctype.h>
-#include <linux/string.h>
-#include <linux/types.h>
-#include <linux/firmware.h>
-
-#include "vendorcmds.h"
-#include "pd-common.h"
-
-#define VENDOR_ID 0x1B24
-#define PRODUCT_ID 0x4001
-static struct usb_device_id id_table[] = {
- { USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ID, PRODUCT_ID, 255, 1, 0) },
- { USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ID, PRODUCT_ID, 255, 1, 1) },
- { },
-};
-MODULE_DEVICE_TABLE(usb, id_table);
-
-int debug_mode;
-module_param(debug_mode, int, 0644);
-MODULE_PARM_DESC(debug_mode, "0 = disable, 1 = enable, 2 = verbose");
-
-#define TLG2300_FIRMWARE "tlg2300_firmware.bin"
-static const char *firmware_name = TLG2300_FIRMWARE;
-static LIST_HEAD(pd_device_list);
-
-/*
- * send set request to USB firmware.
- */
-s32 send_set_req(struct poseidon *pd, u8 cmdid, s32 param, s32 *cmd_status)
-{
- s32 ret;
- s8 data[32] = {};
- u16 lower_16, upper_16;
-
- if (pd->state & POSEIDON_STATE_DISCONNECT)
- return -ENODEV;
-
- mdelay(30);
-
- if (param == 0) {
- upper_16 = lower_16 = 0;
- } else {
- /* send 32 bit param as two 16 bit param,little endian */
- lower_16 = (unsigned short)(param & 0xffff);
- upper_16 = (unsigned short)((param >> 16) & 0xffff);
- }
- ret = usb_control_msg(pd->udev,
- usb_rcvctrlpipe(pd->udev, 0),
- REQ_SET_CMD | cmdid,
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- lower_16,
- upper_16,
- &data,
- sizeof(*cmd_status),
- USB_CTRL_GET_TIMEOUT);
-
- if (!ret) {
- return -ENXIO;
- } else {
- /* 1st 4 bytes into cmd_status */
- memcpy((char *)cmd_status, &(data[0]), sizeof(*cmd_status));
- }
- return 0;
-}
-
-/*
- * send get request to Poseidon firmware.
- */
-s32 send_get_req(struct poseidon *pd, u8 cmdid, s32 param,
- void *buf, s32 *cmd_status, s32 datalen)
-{
- s32 ret;
- s8 data[128] = {};
- u16 lower_16, upper_16;
-
- if (pd->state & POSEIDON_STATE_DISCONNECT)
- return -ENODEV;
-
- mdelay(30);
- if (param == 0) {
- upper_16 = lower_16 = 0;
- } else {
- /*send 32 bit param as two 16 bit param, little endian */
- lower_16 = (unsigned short)(param & 0xffff);
- upper_16 = (unsigned short)((param >> 16) & 0xffff);
- }
- ret = usb_control_msg(pd->udev,
- usb_rcvctrlpipe(pd->udev, 0),
- REQ_GET_CMD | cmdid,
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- lower_16,
- upper_16,
- &data,
- (datalen + sizeof(*cmd_status)),
- USB_CTRL_GET_TIMEOUT);
-
- if (ret < 0) {
- return -ENXIO;
- } else {
- /* 1st 4 bytes into cmd_status, remaining data into cmd_data */
- memcpy((char *)cmd_status, &data[0], sizeof(*cmd_status));
- memcpy((char *)buf, &data[sizeof(*cmd_status)], datalen);
- }
- return 0;
-}
-
-static int pm_notifier_block(struct notifier_block *nb,
- unsigned long event, void *dummy)
-{
- struct poseidon *pd = NULL;
- struct list_head *node, *next;
-
- switch (event) {
- case PM_POST_HIBERNATION:
- list_for_each_safe(node, next, &pd_device_list) {
- struct usb_device *udev;
- struct usb_interface *iface;
- int rc = 0;
-
- pd = container_of(node, struct poseidon, device_list);
- udev = pd->udev;
- iface = pd->interface;
-
- /* It will cause the system to reload the firmware */
- rc = usb_lock_device_for_reset(udev, iface);
- if (rc >= 0) {
- usb_reset_device(udev);
- usb_unlock_device(udev);
- }
- }
- break;
- default:
- break;
- }
- log("event :%ld\n", event);
- return 0;
-}
-
-static struct notifier_block pm_notifer = {
- .notifier_call = pm_notifier_block,
-};
-
-int set_tuner_mode(struct poseidon *pd, unsigned char mode)
-{
- s32 ret, cmd_status;
-
- if (pd->state & POSEIDON_STATE_DISCONNECT)
- return -ENODEV;
-
- ret = send_set_req(pd, TUNE_MODE_SELECT, mode, &cmd_status);
- if (ret || cmd_status)
- return -ENXIO;
- return 0;
-}
-
-void poseidon_delete(struct kref *kref)
-{
- struct poseidon *pd = container_of(kref, struct poseidon, kref);
-
- if (!pd)
- return;
- list_del_init(&pd->device_list);
-
- pd_dvb_usb_device_cleanup(pd);
- /* clean_audio_data(&pd->audio_data);*/
-
- if (pd->udev) {
- usb_put_dev(pd->udev);
- pd->udev = NULL;
- }
- if (pd->interface) {
- usb_put_intf(pd->interface);
- pd->interface = NULL;
- }
- kfree(pd);
- log();
-}
-
-static int firmware_download(struct usb_device *udev)
-{
- int ret = 0, actual_length;
- const struct firmware *fw = NULL;
- void *fwbuf = NULL;
- size_t fwlength = 0, offset;
- size_t max_packet_size;
-
- ret = request_firmware(&fw, firmware_name, &udev->dev);
- if (ret) {
- log("download err : %d", ret);
- return ret;
- }
-
- fwlength = fw->size;
-
- fwbuf = kmemdup(fw->data, fwlength, GFP_KERNEL);
- if (!fwbuf) {
- ret = -ENOMEM;
- goto out;
- }
-
- max_packet_size = le16_to_cpu(udev->ep_out[0x1]->desc.wMaxPacketSize);
- log("\t\t download size : %d", (int)max_packet_size);
-
- for (offset = 0; offset < fwlength; offset += max_packet_size) {
- actual_length = 0;
- ret = usb_bulk_msg(udev,
- usb_sndbulkpipe(udev, 0x01), /* ep 1 */
- fwbuf + offset,
- min(max_packet_size, fwlength - offset),
- &actual_length,
- HZ * 10);
- if (ret)
- break;
- }
- kfree(fwbuf);
-out:
- release_firmware(fw);
- return ret;
-}
-
-static inline struct poseidon *get_pd(struct usb_interface *intf)
-{
- return usb_get_intfdata(intf);
-}
-
-#ifdef CONFIG_PM
-/* one-to-one map : poseidon{} <----> usb_device{}'s port */
-static inline void set_map_flags(struct poseidon *pd, struct usb_device *udev)
-{
- pd->portnum = udev->portnum;
-}
-
-static inline int get_autopm_ref(struct poseidon *pd)
-{
- return pd->video_data.users + pd->vbi_data.users + pd->audio.users
- + atomic_read(&pd->dvb_data.users) +
- !list_empty(&pd->radio_data.fm_dev.fh_list);
-}
-
-/* fixup something for poseidon */
-static inline struct poseidon *fixup(struct poseidon *pd)
-{
- int count;
-
- /* old udev and interface have gone, so put back reference . */
- count = get_autopm_ref(pd);
- log("count : %d, ref count : %d", count, get_pm_count(pd));
- while (count--)
- usb_autopm_put_interface(pd->interface);
- /*usb_autopm_set_interface(pd->interface); */
-
- usb_put_dev(pd->udev);
- usb_put_intf(pd->interface);
- log("event : %d\n", pd->msg.event);
- return pd;
-}
-
-static struct poseidon *find_old_poseidon(struct usb_device *udev)
-{
- struct poseidon *pd;
-
- list_for_each_entry(pd, &pd_device_list, device_list) {
- if (pd->portnum == udev->portnum && in_hibernation(pd))
- return fixup(pd);
- }
- return NULL;
-}
-
-/* Is the card working now ? */
-static inline int is_working(struct poseidon *pd)
-{
- return get_pm_count(pd) > 0;
-}
-
-static int poseidon_suspend(struct usb_interface *intf, pm_message_t msg)
-{
- struct poseidon *pd = get_pd(intf);
-
- if (!pd)
- return 0;
- if (!is_working(pd)) {
- if (get_pm_count(pd) <= 0 && !in_hibernation(pd)) {
- pd->msg.event = PM_EVENT_AUTO_SUSPEND;
- pd->pm_resume = NULL; /* a good guard */
- printk(KERN_DEBUG "TLG2300 auto suspend\n");
- }
- return 0;
- }
- pd->msg = msg; /* save it here */
- logpm(pd);
- return pd->pm_suspend ? pd->pm_suspend(pd) : 0;
-}
-
-static int poseidon_resume(struct usb_interface *intf)
-{
- struct poseidon *pd = get_pd(intf);
-
- if (!pd)
- return 0;
- printk(KERN_DEBUG "TLG2300 resume\n");
-
- if (!is_working(pd)) {
- if (PM_EVENT_AUTO_SUSPEND == pd->msg.event)
- pd->msg = PMSG_ON;
- return 0;
- }
- if (in_hibernation(pd)) {
- logpm(pd);
- return 0;
- }
- logpm(pd);
- return pd->pm_resume ? pd->pm_resume(pd) : 0;
-}
-
-static void hibernation_resume(struct work_struct *w)
-{
- struct poseidon *pd = container_of(w, struct poseidon, pm_work);
- int count;
-
- pd->msg.event = 0; /* clear it here */
- pd->state &= ~POSEIDON_STATE_DISCONNECT;
-
- /* set the new interface's reference */
- count = get_autopm_ref(pd);
- while (count--)
- usb_autopm_get_interface(pd->interface);
-
- /* resume the context */
- logpm(pd);
- if (pd->pm_resume)
- pd->pm_resume(pd);
-}
-#else /* CONFIG_PM is not enabled: */
-static inline struct poseidon *find_old_poseidon(struct usb_device *udev)
-{
- return NULL;
-}
-
-static inline void set_map_flags(struct poseidon *pd, struct usb_device *udev)
-{
-}
-#endif
-
-static int check_firmware(struct usb_device *udev)
-{
- void *buf;
- int ret;
- struct cmd_firmware_vers_s *cmd_firm;
-
- buf = kzalloc(sizeof(*cmd_firm) + sizeof(u32), GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
- ret = usb_control_msg(udev,
- usb_rcvctrlpipe(udev, 0),
- REQ_GET_CMD | GET_FW_ID,
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0,
- 0,
- buf,
- sizeof(*cmd_firm) + sizeof(u32),
- USB_CTRL_GET_TIMEOUT);
- kfree(buf);
-
- if (ret < 0)
- return firmware_download(udev);
- return 0;
-}
-
-static int poseidon_probe(struct usb_interface *interface,
- const struct usb_device_id *id)
-{
- struct usb_device *udev = interface_to_usbdev(interface);
- struct poseidon *pd = NULL;
- int ret = 0;
- int new_one = 0;
-
- /* download firmware */
- ret = check_firmware(udev);
- if (ret)
- return ret;
-
- /* Do I recovery from the hibernate ? */
- pd = find_old_poseidon(udev);
- if (!pd) {
- pd = kzalloc(sizeof(*pd), GFP_KERNEL);
- if (!pd)
- return -ENOMEM;
- kref_init(&pd->kref);
- set_map_flags(pd, udev);
- new_one = 1;
- }
-
- pd->udev = usb_get_dev(udev);
- pd->interface = usb_get_intf(interface);
- usb_set_intfdata(interface, pd);
-
- if (new_one) {
- logpm(pd);
- mutex_init(&pd->lock);
-
- /* register v4l2 device */
- ret = v4l2_device_register(&interface->dev, &pd->v4l2_dev);
- if (ret)
- goto err_v4l2;
-
- /* register devices in directory /dev */
- ret = pd_video_init(pd);
- if (ret)
- goto err_video;
- ret = poseidon_audio_init(pd);
- if (ret)
- goto err_audio;
- ret = poseidon_fm_init(pd);
- if (ret)
- goto err_fm;
- ret = pd_dvb_usb_device_init(pd);
- if (ret)
- goto err_dvb;
-
- INIT_LIST_HEAD(&pd->device_list);
- list_add_tail(&pd->device_list, &pd_device_list);
- }
-
- device_init_wakeup(&udev->dev, 1);
-#ifdef CONFIG_PM
- pm_runtime_set_autosuspend_delay(&pd->udev->dev,
- 1000 * PM_SUSPEND_DELAY);
- usb_enable_autosuspend(pd->udev);
-
- if (in_hibernation(pd)) {
- INIT_WORK(&pd->pm_work, hibernation_resume);
- schedule_work(&pd->pm_work);
- }
-#endif
- return 0;
-err_dvb:
- poseidon_fm_exit(pd);
-err_fm:
- poseidon_audio_free(pd);
-err_audio:
- pd_video_exit(pd);
-err_video:
- v4l2_device_unregister(&pd->v4l2_dev);
-err_v4l2:
- usb_put_intf(pd->interface);
- usb_put_dev(pd->udev);
- kfree(pd);
- return ret;
-}
-
-static void poseidon_disconnect(struct usb_interface *interface)
-{
- struct poseidon *pd = get_pd(interface);
-
- if (!pd)
- return;
- logpm(pd);
- if (in_hibernation(pd))
- return;
-
- mutex_lock(&pd->lock);
- pd->state |= POSEIDON_STATE_DISCONNECT;
- mutex_unlock(&pd->lock);
-
- /* stop urb transferring */
- stop_all_video_stream(pd);
- dvb_stop_streaming(&pd->dvb_data);
-
- /*unregister v4l2 device */
- v4l2_device_unregister(&pd->v4l2_dev);
-
- pd_dvb_usb_device_exit(pd);
- poseidon_fm_exit(pd);
-
- poseidon_audio_free(pd);
- pd_video_exit(pd);
-
- usb_set_intfdata(interface, NULL);
- kref_put(&pd->kref, poseidon_delete);
-}
-
-static struct usb_driver poseidon_driver = {
- .name = "poseidon",
- .probe = poseidon_probe,
- .disconnect = poseidon_disconnect,
- .id_table = id_table,
-#ifdef CONFIG_PM
- .suspend = poseidon_suspend,
- .resume = poseidon_resume,
-#endif
- .supports_autosuspend = 1,
-};
-
-static int __init poseidon_init(void)
-{
- int ret;
-
- ret = usb_register(&poseidon_driver);
- if (ret)
- return ret;
- register_pm_notifier(&pm_notifer);
- return ret;
-}
-
-static void __exit poseidon_exit(void)
-{
- log();
- unregister_pm_notifier(&pm_notifer);
- usb_deregister(&poseidon_driver);
-}
-
-module_init(poseidon_init);
-module_exit(poseidon_exit);
-
-MODULE_AUTHOR("Telegent Systems");
-MODULE_DESCRIPTION("For tlg2300-based USB device");
-MODULE_LICENSE("GPL");
-MODULE_VERSION("0.0.2");
-MODULE_FIRMWARE(TLG2300_FIRMWARE);
diff --git a/drivers/media/usb/tlg2300/pd-radio.c b/drivers/media/usb/tlg2300/pd-radio.c
deleted file mode 100644
index b391194..0000000
--- a/drivers/media/usb/tlg2300/pd-radio.c
+++ /dev/null
@@ -1,339 +0,0 @@
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/bitmap.h>
-#include <linux/usb.h>
-#include <linux/i2c.h>
-#include <media/v4l2-dev.h>
-#include <linux/mm.h>
-#include <linux/mutex.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-fh.h>
-#include <linux/sched.h>
-
-#include "pd-common.h"
-#include "vendorcmds.h"
-
-static int set_frequency(struct poseidon *p, __u32 frequency);
-static int poseidon_fm_close(struct file *filp);
-static int poseidon_fm_open(struct file *filp);
-
-#define TUNER_FREQ_MIN_FM 76000000U
-#define TUNER_FREQ_MAX_FM 108000000U
-
-#define MAX_PREEMPHASIS (V4L2_PREEMPHASIS_75_uS + 1)
-static int preemphasis[MAX_PREEMPHASIS] = {
- TLG_TUNE_ASTD_NONE, /* V4L2_PREEMPHASIS_DISABLED */
- TLG_TUNE_ASTD_FM_EUR, /* V4L2_PREEMPHASIS_50_uS */
- TLG_TUNE_ASTD_FM_US, /* V4L2_PREEMPHASIS_75_uS */
-};
-
-static int poseidon_check_mode_radio(struct poseidon *p)
-{
- int ret;
- u32 status;
-
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ/2);
- ret = usb_set_interface(p->udev, 0, BULK_ALTERNATE_IFACE);
- if (ret < 0)
- goto out;
-
- ret = set_tuner_mode(p, TLG_MODE_FM_RADIO);
- if (ret != 0)
- goto out;
-
- ret = send_set_req(p, SGNL_SRC_SEL, TLG_SIG_SRC_ANTENNA, &status);
- ret = send_set_req(p, TUNER_AUD_ANA_STD,
- p->radio_data.pre_emphasis, &status);
- ret |= send_set_req(p, TUNER_AUD_MODE,
- TLG_TUNE_TVAUDIO_MODE_STEREO, &status);
- ret |= send_set_req(p, AUDIO_SAMPLE_RATE_SEL,
- ATV_AUDIO_RATE_48K, &status);
- ret |= send_set_req(p, TUNE_FREQ_SELECT, TUNER_FREQ_MIN_FM, &status);
-out:
- return ret;
-}
-
-#ifdef CONFIG_PM
-static int pm_fm_suspend(struct poseidon *p)
-{
- logpm(p);
- pm_alsa_suspend(p);
- usb_set_interface(p->udev, 0, 0);
- msleep(300);
- return 0;
-}
-
-static int pm_fm_resume(struct poseidon *p)
-{
- logpm(p);
- poseidon_check_mode_radio(p);
- set_frequency(p, p->radio_data.fm_freq);
- pm_alsa_resume(p);
- return 0;
-}
-#endif
-
-static int poseidon_fm_open(struct file *filp)
-{
- struct poseidon *p = video_drvdata(filp);
- int ret = 0;
-
- mutex_lock(&p->lock);
- if (p->state & POSEIDON_STATE_DISCONNECT) {
- ret = -ENODEV;
- goto out;
- }
-
- if (p->state && !(p->state & POSEIDON_STATE_FM)) {
- ret = -EBUSY;
- goto out;
- }
- ret = v4l2_fh_open(filp);
- if (ret)
- goto out;
-
- usb_autopm_get_interface(p->interface);
- if (0 == p->state) {
- struct video_device *vfd = &p->radio_data.fm_dev;
-
- /* default pre-emphasis */
- if (p->radio_data.pre_emphasis == 0)
- p->radio_data.pre_emphasis = TLG_TUNE_ASTD_FM_EUR;
- set_debug_mode(vfd, debug_mode);
-
- ret = poseidon_check_mode_radio(p);
- if (ret < 0) {
- usb_autopm_put_interface(p->interface);
- goto out;
- }
- p->state |= POSEIDON_STATE_FM;
- }
- kref_get(&p->kref);
-out:
- mutex_unlock(&p->lock);
- return ret;
-}
-
-static int poseidon_fm_close(struct file *filp)
-{
- struct poseidon *p = video_drvdata(filp);
- struct radio_data *fm = &p->radio_data;
- uint32_t status;
-
- mutex_lock(&p->lock);
- if (v4l2_fh_is_singular_file(filp))
- p->state &= ~POSEIDON_STATE_FM;
-
- if (fm->is_radio_streaming && filp == p->file_for_stream) {
- fm->is_radio_streaming = 0;
- send_set_req(p, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP, &status);
- }
- usb_autopm_put_interface(p->interface);
- mutex_unlock(&p->lock);
-
- kref_put(&p->kref, poseidon_delete);
- return v4l2_fh_release(filp);
-}
-
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *v)
-{
- struct poseidon *p = video_drvdata(file);
-
- strlcpy(v->driver, "tele-radio", sizeof(v->driver));
- strlcpy(v->card, "Telegent Poseidon", sizeof(v->card));
- usb_make_path(p->udev, v->bus_info, sizeof(v->bus_info));
- v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
- /* Report all capabilities of the USB device */
- v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS |
- V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VBI_CAPTURE |
- V4L2_CAP_AUDIO | V4L2_CAP_STREAMING |
- V4L2_CAP_READWRITE;
- return 0;
-}
-
-static const struct v4l2_file_operations poseidon_fm_fops = {
- .owner = THIS_MODULE,
- .open = poseidon_fm_open,
- .release = poseidon_fm_close,
- .poll = v4l2_ctrl_poll,
- .unlocked_ioctl = video_ioctl2,
-};
-
-static int tlg_fm_vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *vt)
-{
- struct poseidon *p = video_drvdata(file);
- struct tuner_fm_sig_stat_s fm_stat = {};
- int ret, status, count = 5;
-
- if (vt->index != 0)
- return -EINVAL;
-
- vt->type = V4L2_TUNER_RADIO;
- vt->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW;
- vt->rangelow = TUNER_FREQ_MIN_FM * 2 / 125;
- vt->rangehigh = TUNER_FREQ_MAX_FM * 2 / 125;
- vt->rxsubchans = V4L2_TUNER_SUB_STEREO;
- vt->audmode = V4L2_TUNER_MODE_STEREO;
- vt->signal = 0;
- vt->afc = 0;
- strlcpy(vt->name, "Radio", sizeof(vt->name));
-
- mutex_lock(&p->lock);
- ret = send_get_req(p, TUNER_STATUS, TLG_MODE_FM_RADIO,
- &fm_stat, &status, sizeof(fm_stat));
-
- while (fm_stat.sig_lock_busy && count-- && !ret) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ);
-
- ret = send_get_req(p, TUNER_STATUS, TLG_MODE_FM_RADIO,
- &fm_stat, &status, sizeof(fm_stat));
- }
- mutex_unlock(&p->lock);
-
- if (ret || status) {
- vt->signal = 0;
- } else if ((fm_stat.sig_present || fm_stat.sig_locked)
- && fm_stat.sig_strength == 0) {
- vt->signal = 0xffff;
- } else
- vt->signal = (fm_stat.sig_strength * 255 / 10) << 8;
-
- return 0;
-}
-
-static int fm_get_freq(struct file *file, void *priv,
- struct v4l2_frequency *argp)
-{
- struct poseidon *p = video_drvdata(file);
-
- if (argp->tuner)
- return -EINVAL;
- argp->frequency = p->radio_data.fm_freq;
- return 0;
-}
-
-static int set_frequency(struct poseidon *p, __u32 frequency)
-{
- __u32 freq ;
- int ret, status;
-
- mutex_lock(&p->lock);
-
- ret = send_set_req(p, TUNER_AUD_ANA_STD,
- p->radio_data.pre_emphasis, &status);
-
- freq = (frequency * 125) / 2; /* Hz */
- freq = clamp(freq, TUNER_FREQ_MIN_FM, TUNER_FREQ_MAX_FM);
-
- ret = send_set_req(p, TUNE_FREQ_SELECT, freq, &status);
- if (ret < 0)
- goto error ;
- ret = send_set_req(p, TAKE_REQUEST, 0, &status);
-
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ/4);
- if (!p->radio_data.is_radio_streaming) {
- ret = send_set_req(p, TAKE_REQUEST, 0, &status);
- ret = send_set_req(p, PLAY_SERVICE,
- TLG_TUNE_PLAY_SVC_START, &status);
- p->radio_data.is_radio_streaming = 1;
- }
- p->radio_data.fm_freq = freq * 2 / 125;
-error:
- mutex_unlock(&p->lock);
- return ret;
-}
-
-static int fm_set_freq(struct file *file, void *priv,
- const struct v4l2_frequency *argp)
-{
- struct poseidon *p = video_drvdata(file);
-
- if (argp->tuner)
- return -EINVAL;
- p->file_for_stream = file;
-#ifdef CONFIG_PM
- p->pm_suspend = pm_fm_suspend;
- p->pm_resume = pm_fm_resume;
-#endif
- return set_frequency(p, argp->frequency);
-}
-
-static int tlg_fm_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct poseidon *p = container_of(ctrl->handler, struct poseidon,
- radio_data.ctrl_handler);
- int pre_emphasis;
- u32 status;
-
- switch (ctrl->id) {
- case V4L2_CID_TUNE_PREEMPHASIS:
- pre_emphasis = preemphasis[ctrl->val];
- send_set_req(p, TUNER_AUD_ANA_STD, pre_emphasis, &status);
- p->radio_data.pre_emphasis = pre_emphasis;
- return 0;
- }
- return -EINVAL;
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *vt)
-{
- return vt->index > 0 ? -EINVAL : 0;
-}
-
-static const struct v4l2_ctrl_ops tlg_fm_ctrl_ops = {
- .s_ctrl = tlg_fm_s_ctrl,
-};
-
-static const struct v4l2_ioctl_ops poseidon_fm_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_tuner = tlg_fm_vidioc_g_tuner,
- .vidioc_g_frequency = fm_get_freq,
- .vidioc_s_frequency = fm_set_freq,
- .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
- .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
-};
-
-static struct video_device poseidon_fm_template = {
- .name = "Telegent-Radio",
- .fops = &poseidon_fm_fops,
- .minor = -1,
- .release = video_device_release_empty,
- .ioctl_ops = &poseidon_fm_ioctl_ops,
-};
-
-int poseidon_fm_init(struct poseidon *p)
-{
- struct video_device *vfd = &p->radio_data.fm_dev;
- struct v4l2_ctrl_handler *hdl = &p->radio_data.ctrl_handler;
-
- *vfd = poseidon_fm_template;
-
- set_frequency(p, TUNER_FREQ_MIN_FM);
- v4l2_ctrl_handler_init(hdl, 1);
- v4l2_ctrl_new_std_menu(hdl, &tlg_fm_ctrl_ops, V4L2_CID_TUNE_PREEMPHASIS,
- V4L2_PREEMPHASIS_75_uS, 0, V4L2_PREEMPHASIS_50_uS);
- if (hdl->error) {
- v4l2_ctrl_handler_free(hdl);
- return hdl->error;
- }
- vfd->v4l2_dev = &p->v4l2_dev;
- vfd->ctrl_handler = hdl;
- video_set_drvdata(vfd, p);
- return video_register_device(vfd, VFL_TYPE_RADIO, -1);
-}
-
-int poseidon_fm_exit(struct poseidon *p)
-{
- video_unregister_device(&p->radio_data.fm_dev);
- v4l2_ctrl_handler_free(&p->radio_data.ctrl_handler);
- return 0;
-}
diff --git a/drivers/media/usb/tlg2300/pd-video.c b/drivers/media/usb/tlg2300/pd-video.c
deleted file mode 100644
index 8cd7f02..0000000
--- a/drivers/media/usb/tlg2300/pd-video.c
+++ /dev/null
@@ -1,1570 +0,0 @@
-#include <linux/fs.h>
-#include <linux/vmalloc.h>
-#include <linux/videodev2.h>
-#include <linux/usb.h>
-#include <linux/mm.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-dev.h>
-#include <media/v4l2-ctrls.h>
-
-#include "pd-common.h"
-#include "vendorcmds.h"
-
-#ifdef CONFIG_PM
-static int pm_video_suspend(struct poseidon *pd);
-static int pm_video_resume(struct poseidon *pd);
-#endif
-static void iso_bubble_handler(struct work_struct *w);
-
-static int usb_transfer_mode;
-module_param(usb_transfer_mode, int, 0644);
-MODULE_PARM_DESC(usb_transfer_mode, "0 = Bulk, 1 = Isochronous");
-
-static const struct poseidon_format poseidon_formats[] = {
- { "YUV 422", V4L2_PIX_FMT_YUYV, 16, 0},
- { "RGB565", V4L2_PIX_FMT_RGB565, 16, 0},
-};
-
-static const struct poseidon_tvnorm poseidon_tvnorms[] = {
- { V4L2_STD_PAL_D, "PAL-D", TLG_TUNE_VSTD_PAL_D },
- { V4L2_STD_PAL_B, "PAL-B", TLG_TUNE_VSTD_PAL_B },
- { V4L2_STD_PAL_G, "PAL-G", TLG_TUNE_VSTD_PAL_G },
- { V4L2_STD_PAL_H, "PAL-H", TLG_TUNE_VSTD_PAL_H },
- { V4L2_STD_PAL_I, "PAL-I", TLG_TUNE_VSTD_PAL_I },
- { V4L2_STD_PAL_M, "PAL-M", TLG_TUNE_VSTD_PAL_M },
- { V4L2_STD_PAL_N, "PAL-N", TLG_TUNE_VSTD_PAL_N_COMBO },
- { V4L2_STD_PAL_Nc, "PAL-Nc", TLG_TUNE_VSTD_PAL_N_COMBO },
- { V4L2_STD_NTSC_M, "NTSC-M", TLG_TUNE_VSTD_NTSC_M },
- { V4L2_STD_NTSC_M_JP, "NTSC-JP", TLG_TUNE_VSTD_NTSC_M_J },
- { V4L2_STD_SECAM_B, "SECAM-B", TLG_TUNE_VSTD_SECAM_B },
- { V4L2_STD_SECAM_D, "SECAM-D", TLG_TUNE_VSTD_SECAM_D },
- { V4L2_STD_SECAM_G, "SECAM-G", TLG_TUNE_VSTD_SECAM_G },
- { V4L2_STD_SECAM_H, "SECAM-H", TLG_TUNE_VSTD_SECAM_H },
- { V4L2_STD_SECAM_K, "SECAM-K", TLG_TUNE_VSTD_SECAM_K },
- { V4L2_STD_SECAM_K1, "SECAM-K1", TLG_TUNE_VSTD_SECAM_K1 },
- { V4L2_STD_SECAM_L, "SECAM-L", TLG_TUNE_VSTD_SECAM_L },
- { V4L2_STD_SECAM_LC, "SECAM-LC", TLG_TUNE_VSTD_SECAM_L1 },
-};
-static const unsigned int POSEIDON_TVNORMS = ARRAY_SIZE(poseidon_tvnorms);
-
-struct pd_audio_mode {
- u32 tlg_audio_mode;
- u32 v4l2_audio_sub;
- u32 v4l2_audio_mode;
-};
-
-static const struct pd_audio_mode pd_audio_modes[] = {
- { TLG_TUNE_TVAUDIO_MODE_MONO, V4L2_TUNER_SUB_MONO,
- V4L2_TUNER_MODE_MONO },
- { TLG_TUNE_TVAUDIO_MODE_STEREO, V4L2_TUNER_SUB_STEREO,
- V4L2_TUNER_MODE_STEREO },
- { TLG_TUNE_TVAUDIO_MODE_LANG_A, V4L2_TUNER_SUB_LANG1,
- V4L2_TUNER_MODE_LANG1 },
- { TLG_TUNE_TVAUDIO_MODE_LANG_B, V4L2_TUNER_SUB_LANG2,
- V4L2_TUNER_MODE_LANG2 },
- { TLG_TUNE_TVAUDIO_MODE_LANG_C, V4L2_TUNER_SUB_LANG1,
- V4L2_TUNER_MODE_LANG1_LANG2 }
-};
-static const unsigned int POSEIDON_AUDIOMODS = ARRAY_SIZE(pd_audio_modes);
-
-struct pd_input {
- char *name;
- uint32_t tlg_src;
-};
-
-static const struct pd_input pd_inputs[] = {
- { "TV Antenna", TLG_SIG_SRC_ANTENNA },
- { "TV Cable", TLG_SIG_SRC_CABLE },
- { "TV SVideo", TLG_SIG_SRC_SVIDEO },
- { "TV Composite", TLG_SIG_SRC_COMPOSITE }
-};
-static const unsigned int POSEIDON_INPUTS = ARRAY_SIZE(pd_inputs);
-
-struct video_std_to_audio_std {
- v4l2_std_id video_std;
- int audio_std;
-};
-
-static const struct video_std_to_audio_std video_to_audio_map[] = {
- /* country : { 27, 32, 33, 34, 36, 44, 45, 46, 47, 48, 64,
- 65, 86, 351, 352, 353, 354, 358, 372, 852, 972 } */
- { (V4L2_STD_PAL_I | V4L2_STD_PAL_B | V4L2_STD_PAL_D |
- V4L2_STD_SECAM_L | V4L2_STD_SECAM_D), TLG_TUNE_ASTD_NICAM },
-
- /* country : { 1, 52, 54, 55, 886 } */
- {V4L2_STD_NTSC_M | V4L2_STD_PAL_N | V4L2_STD_PAL_M, TLG_TUNE_ASTD_BTSC},
-
- /* country : { 81 } */
- { V4L2_STD_NTSC_M_JP, TLG_TUNE_ASTD_EIAJ },
-
- /* other country : TLG_TUNE_ASTD_A2 */
-};
-static const unsigned int map_size = ARRAY_SIZE(video_to_audio_map);
-
-static int get_audio_std(v4l2_std_id v4l2_std)
-{
- int i = 0;
-
- for (; i < map_size; i++) {
- if (v4l2_std & video_to_audio_map[i].video_std)
- return video_to_audio_map[i].audio_std;
- }
- return TLG_TUNE_ASTD_A2;
-}
-
-static int vidioc_querycap(struct file *file, void *fh,
- struct v4l2_capability *cap)
-{
- struct video_device *vdev = video_devdata(file);
- struct poseidon *p = video_get_drvdata(vdev);
-
- strcpy(cap->driver, "tele-video");
- strcpy(cap->card, "Telegent Poseidon");
- usb_make_path(p->udev, cap->bus_info, sizeof(cap->bus_info));
- cap->device_caps = V4L2_CAP_TUNER | V4L2_CAP_AUDIO |
- V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
- if (vdev->vfl_type == VFL_TYPE_VBI)
- cap->device_caps |= V4L2_CAP_VBI_CAPTURE;
- else
- cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE;
- cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS |
- V4L2_CAP_RADIO | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_VIDEO_CAPTURE;
- return 0;
-}
-
-/*====================================================================*/
-static void init_copy(struct video_data *video, bool index)
-{
- struct front_face *front = video->front;
-
- video->field_count = index;
- video->lines_copied = 0;
- video->prev_left = 0 ;
- video->dst = (char *)videobuf_to_vmalloc(front->curr_frame)
- + index * video->lines_size;
- video->vbi->copied = 0; /* set it here */
-}
-
-static bool get_frame(struct front_face *front, int *need_init)
-{
- struct videobuf_buffer *vb = front->curr_frame;
-
- if (vb)
- return true;
-
- spin_lock(&front->queue_lock);
- if (!list_empty(&front->active)) {
- vb = list_entry(front->active.next,
- struct videobuf_buffer, queue);
- if (need_init)
- *need_init = 1;
- front->curr_frame = vb;
- list_del_init(&vb->queue);
- }
- spin_unlock(&front->queue_lock);
-
- return !!vb;
-}
-
-/* check if the video's buffer is ready */
-static bool get_video_frame(struct front_face *front, struct video_data *video)
-{
- int need_init = 0;
- bool ret = true;
-
- ret = get_frame(front, &need_init);
- if (ret && need_init)
- init_copy(video, 0);
- return ret;
-}
-
-static void submit_frame(struct front_face *front)
-{
- struct videobuf_buffer *vb = front->curr_frame;
-
- if (vb == NULL)
- return;
-
- front->curr_frame = NULL;
- vb->state = VIDEOBUF_DONE;
- vb->field_count++;
- v4l2_get_timestamp(&vb->ts);
-
- wake_up(&vb->done);
-}
-
-/*
- * A frame is composed of two fields. If we receive all the two fields,
- * call the submit_frame() to submit the whole frame to applications.
- */
-static void end_field(struct video_data *video)
-{
- if (1 == video->field_count)
- submit_frame(video->front);
- else
- init_copy(video, 1);
-}
-
-static void copy_video_data(struct video_data *video, char *src,
- unsigned int count)
-{
-#define copy_data(len) \
- do { \
- if (++video->lines_copied > video->lines_per_field) \
- goto overflow; \
- memcpy(video->dst, src, len);\
- video->dst += len + video->lines_size; \
- src += len; \
- count -= len; \
- } while (0)
-
- while (count && count >= video->lines_size) {
- if (video->prev_left) {
- copy_data(video->prev_left);
- video->prev_left = 0;
- continue;
- }
- copy_data(video->lines_size);
- }
- if (count && count < video->lines_size) {
- memcpy(video->dst, src, count);
-
- video->prev_left = video->lines_size - count;
- video->dst += count;
- }
- return;
-
-overflow:
- end_field(video);
-}
-
-static void check_trailer(struct video_data *video, char *src, int count)
-{
- struct vbi_data *vbi = video->vbi;
- int offset; /* trailer's offset */
- char *buf;
-
- offset = (video->context.pix.sizeimage / 2 + vbi->vbi_size / 2)
- - (vbi->copied + video->lines_size * video->lines_copied);
- if (video->prev_left)
- offset -= (video->lines_size - video->prev_left);
-
- if (offset > count || offset <= 0)
- goto short_package;
-
- buf = src + offset;
-
- /* trailer : (VFHS) + U32 + U32 + field_num */
- if (!strncmp(buf, "VFHS", 4)) {
- int field_num = *((u32 *)(buf + 12));
-
- if ((field_num & 1) ^ video->field_count) {
- init_copy(video, video->field_count);
- return;
- }
- copy_video_data(video, src, offset);
- }
-short_package:
- end_field(video);
-}
-
-/* ========== Check this more carefully! =========== */
-static inline void copy_vbi_data(struct vbi_data *vbi,
- char *src, unsigned int count)
-{
- struct front_face *front = vbi->front;
-
- if (front && get_frame(front, NULL)) {
- char *buf = videobuf_to_vmalloc(front->curr_frame);
-
- if (vbi->video->field_count)
- buf += (vbi->vbi_size / 2);
- memcpy(buf + vbi->copied, src, count);
- }
- vbi->copied += count;
-}
-
-/*
- * Copy the normal data (VBI or VIDEO) without the trailer.
- * VBI is not interlaced, while VIDEO is interlaced.
- */
-static inline void copy_vbi_video_data(struct video_data *video,
- char *src, unsigned int count)
-{
- struct vbi_data *vbi = video->vbi;
- unsigned int vbi_delta = (vbi->vbi_size / 2) - vbi->copied;
-
- if (vbi_delta >= count) {
- copy_vbi_data(vbi, src, count);
- } else {
- if (vbi_delta) {
- copy_vbi_data(vbi, src, vbi_delta);
-
- /* we receive the two fields of the VBI*/
- if (vbi->front && video->field_count)
- submit_frame(vbi->front);
- }
- copy_video_data(video, src + vbi_delta, count - vbi_delta);
- }
-}
-
-static void urb_complete_bulk(struct urb *urb)
-{
- struct front_face *front = urb->context;
- struct video_data *video = &front->pd->video_data;
- char *src = (char *)urb->transfer_buffer;
- int count = urb->actual_length;
- int ret = 0;
-
- if (!video->is_streaming || urb->status) {
- if (urb->status == -EPROTO)
- goto resend_it;
- return;
- }
- if (!get_video_frame(front, video))
- goto resend_it;
-
- if (count == urb->transfer_buffer_length)
- copy_vbi_video_data(video, src, count);
- else
- check_trailer(video, src, count);
-
-resend_it:
- ret = usb_submit_urb(urb, GFP_ATOMIC);
- if (ret)
- log(" submit failed: error %d", ret);
-}
-
-/************************* for ISO *********************/
-#define GET_SUCCESS (0)
-#define GET_TRAILER (1)
-#define GET_TOO_MUCH_BUBBLE (2)
-#define GET_NONE (3)
-static int get_chunk(int start, struct urb *urb,
- int *head, int *tail, int *bubble_err)
-{
- struct usb_iso_packet_descriptor *pkt = NULL;
- int ret = GET_SUCCESS;
-
- for (*head = *tail = -1; start < urb->number_of_packets; start++) {
- pkt = &urb->iso_frame_desc[start];
-
- /* handle the bubble of the Hub */
- if (-EOVERFLOW == pkt->status) {
- if (++*bubble_err > urb->number_of_packets / 3)
- return GET_TOO_MUCH_BUBBLE;
- continue;
- }
-
- /* This is the gap */
- if (pkt->status || pkt->actual_length <= 0
- || pkt->actual_length > ISO_PKT_SIZE) {
- if (*head != -1)
- break;
- continue;
- }
-
- /* a good isochronous packet */
- if (pkt->actual_length == ISO_PKT_SIZE) {
- if (*head == -1)
- *head = start;
- *tail = start;
- continue;
- }
-
- /* trailer is here */
- if (pkt->actual_length < ISO_PKT_SIZE) {
- if (*head == -1) {
- *head = start;
- *tail = start;
- return GET_TRAILER;
- }
- break;
- }
- }
-
- if (*head == -1 && *tail == -1)
- ret = GET_NONE;
- return ret;
-}
-
-/*
- * |__|------|___|-----|_______|
- * ^ ^
- * | |
- * gap gap
- */
-static void urb_complete_iso(struct urb *urb)
-{
- struct front_face *front = urb->context;
- struct video_data *video = &front->pd->video_data;
- int bubble_err = 0, head = 0, tail = 0;
- char *src = (char *)urb->transfer_buffer;
- int ret = 0;
-
- if (!video->is_streaming)
- return;
-
- do {
- if (!get_video_frame(front, video))
- goto out;
-
- switch (get_chunk(head, urb, &head, &tail, &bubble_err)) {
- case GET_SUCCESS:
- copy_vbi_video_data(video, src + (head * ISO_PKT_SIZE),
- (tail - head + 1) * ISO_PKT_SIZE);
- break;
- case GET_TRAILER:
- check_trailer(video, src + (head * ISO_PKT_SIZE),
- ISO_PKT_SIZE);
- break;
- case GET_NONE:
- goto out;
- case GET_TOO_MUCH_BUBBLE:
- log("\t We got too much bubble");
- schedule_work(&video->bubble_work);
- return;
- }
- } while (head = tail + 1, head < urb->number_of_packets);
-
-out:
- ret = usb_submit_urb(urb, GFP_ATOMIC);
- if (ret)
- log("usb_submit_urb err : %d", ret);
-}
-/*============================= [ end ] =====================*/
-
-static int prepare_iso_urb(struct video_data *video)
-{
- struct usb_device *udev = video->pd->udev;
- int i;
-
- if (video->urb_array[0])
- return 0;
-
- for (i = 0; i < SBUF_NUM; i++) {
- struct urb *urb;
- void *mem;
- int j;
-
- urb = usb_alloc_urb(PK_PER_URB, GFP_KERNEL);
- if (urb == NULL)
- goto out;
-
- video->urb_array[i] = urb;
- mem = usb_alloc_coherent(udev,
- ISO_PKT_SIZE * PK_PER_URB,
- GFP_KERNEL,
- &urb->transfer_dma);
-
- urb->complete = urb_complete_iso; /* handler */
- urb->dev = udev;
- urb->context = video->front;
- urb->pipe = usb_rcvisocpipe(udev,
- video->endpoint_addr);
- urb->interval = 1;
- urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
- urb->number_of_packets = PK_PER_URB;
- urb->transfer_buffer = mem;
- urb->transfer_buffer_length = PK_PER_URB * ISO_PKT_SIZE;
-
- for (j = 0; j < PK_PER_URB; j++) {
- urb->iso_frame_desc[j].offset = ISO_PKT_SIZE * j;
- urb->iso_frame_desc[j].length = ISO_PKT_SIZE;
- }
- }
- return 0;
-out:
- for (; i > 0; i--)
- ;
- return -ENOMEM;
-}
-
-/* return the succeeded number of the allocation */
-int alloc_bulk_urbs_generic(struct urb **urb_array, int num,
- struct usb_device *udev, u8 ep_addr,
- int buf_size, gfp_t gfp_flags,
- usb_complete_t complete_fn, void *context)
-{
- int i = 0;
-
- for (; i < num; i++) {
- void *mem;
- struct urb *urb = usb_alloc_urb(0, gfp_flags);
- if (urb == NULL)
- return i;
-
- mem = usb_alloc_coherent(udev, buf_size, gfp_flags,
- &urb->transfer_dma);
- if (mem == NULL) {
- usb_free_urb(urb);
- return i;
- }
-
- usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, ep_addr),
- mem, buf_size, complete_fn, context);
- urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
- urb_array[i] = urb;
- }
- return i;
-}
-
-void free_all_urb_generic(struct urb **urb_array, int num)
-{
- int i;
- struct urb *urb;
-
- for (i = 0; i < num; i++) {
- urb = urb_array[i];
- if (urb) {
- usb_free_coherent(urb->dev,
- urb->transfer_buffer_length,
- urb->transfer_buffer,
- urb->transfer_dma);
- usb_free_urb(urb);
- urb_array[i] = NULL;
- }
- }
-}
-
-static int prepare_bulk_urb(struct video_data *video)
-{
- if (video->urb_array[0])
- return 0;
-
- alloc_bulk_urbs_generic(video->urb_array, SBUF_NUM,
- video->pd->udev, video->endpoint_addr,
- 0x2000, GFP_KERNEL,
- urb_complete_bulk, video->front);
- return 0;
-}
-
-/* free the URBs */
-static void free_all_urb(struct video_data *video)
-{
- free_all_urb_generic(video->urb_array, SBUF_NUM);
-}
-
-static void pd_buf_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
-{
- videobuf_vmalloc_free(vb);
- vb->state = VIDEOBUF_NEEDS_INIT;
-}
-
-static void pd_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
-{
- struct front_face *front = q->priv_data;
- vb->state = VIDEOBUF_QUEUED;
- list_add_tail(&vb->queue, &front->active);
-}
-
-static int pd_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
- enum v4l2_field field)
-{
- struct front_face *front = q->priv_data;
- int rc;
-
- switch (front->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- if (VIDEOBUF_NEEDS_INIT == vb->state) {
- struct v4l2_pix_format *pix;
-
- pix = &front->pd->video_data.context.pix;
- vb->size = pix->sizeimage; /* real frame size */
- vb->width = pix->width;
- vb->height = pix->height;
- rc = videobuf_iolock(q, vb, NULL);
- if (rc < 0)
- return rc;
- }
- break;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- if (VIDEOBUF_NEEDS_INIT == vb->state) {
- vb->size = front->pd->vbi_data.vbi_size;
- rc = videobuf_iolock(q, vb, NULL);
- if (rc < 0)
- return rc;
- }
- break;
- default:
- return -EINVAL;
- }
- vb->field = field;
- vb->state = VIDEOBUF_PREPARED;
- return 0;
-}
-
-static int fire_all_urb(struct video_data *video)
-{
- int i, ret;
-
- video->is_streaming = 1;
-
- for (i = 0; i < SBUF_NUM; i++) {
- ret = usb_submit_urb(video->urb_array[i], GFP_KERNEL);
- if (ret)
- log("(%d) failed: error %d", i, ret);
- }
- return ret;
-}
-
-static int start_video_stream(struct poseidon *pd)
-{
- struct video_data *video = &pd->video_data;
- s32 cmd_status;
-
- send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
- send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_START, &cmd_status);
-
- if (pd->cur_transfer_mode) {
- prepare_iso_urb(video);
- INIT_WORK(&video->bubble_work, iso_bubble_handler);
- } else {
- /* The bulk mode does not need a bubble handler */
- prepare_bulk_urb(video);
- }
- fire_all_urb(video);
- return 0;
-}
-
-static int pd_buf_setup(struct videobuf_queue *q, unsigned int *count,
- unsigned int *size)
-{
- struct front_face *front = q->priv_data;
- struct poseidon *pd = front->pd;
-
- switch (front->type) {
- default:
- return -EINVAL;
- case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
- struct video_data *video = &pd->video_data;
- struct v4l2_pix_format *pix = &video->context.pix;
-
- *size = PAGE_ALIGN(pix->sizeimage);/* page aligned frame size */
- if (*count < 4)
- *count = 4;
- if (1) {
- /* same in different altersetting */
- video->endpoint_addr = 0x82;
- video->vbi = &pd->vbi_data;
- video->vbi->video = video;
- video->pd = pd;
- video->lines_per_field = pix->height / 2;
- video->lines_size = pix->width * 2;
- video->front = front;
- }
- return start_video_stream(pd);
- }
-
- case V4L2_BUF_TYPE_VBI_CAPTURE: {
- struct vbi_data *vbi = &pd->vbi_data;
-
- *size = PAGE_ALIGN(vbi->vbi_size);
- log("size : %d", *size);
- if (*count == 0)
- *count = 4;
- }
- break;
- }
- return 0;
-}
-
-static struct videobuf_queue_ops pd_video_qops = {
- .buf_setup = pd_buf_setup,
- .buf_prepare = pd_buf_prepare,
- .buf_queue = pd_buf_queue,
- .buf_release = pd_buf_release,
-};
-
-static int vidioc_enum_fmt(struct file *file, void *fh,
- struct v4l2_fmtdesc *f)
-{
- if (ARRAY_SIZE(poseidon_formats) <= f->index)
- return -EINVAL;
- f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- f->flags = 0;
- f->pixelformat = poseidon_formats[f->index].fourcc;
- strcpy(f->description, poseidon_formats[f->index].name);
- return 0;
-}
-
-static int vidioc_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
-{
- struct front_face *front = fh;
- struct poseidon *pd = front->pd;
-
- f->fmt.pix = pd->video_data.context.pix;
- return 0;
-}
-
-/*
- * VLC calls VIDIOC_S_STD before VIDIOC_S_FMT, while
- * Mplayer calls them in the reverse order.
- */
-static int pd_vidioc_s_fmt(struct poseidon *pd, struct v4l2_pix_format *pix)
-{
- struct video_data *video = &pd->video_data;
- struct running_context *context = &video->context;
- struct v4l2_pix_format *pix_def = &context->pix;
- s32 ret = 0, cmd_status = 0, vid_resol;
-
- /* set the pixel format to firmware */
- if (pix->pixelformat == V4L2_PIX_FMT_RGB565) {
- vid_resol = TLG_TUNER_VID_FORMAT_RGB_565;
- } else {
- pix->pixelformat = V4L2_PIX_FMT_YUYV;
- vid_resol = TLG_TUNER_VID_FORMAT_YUV;
- }
- ret = send_set_req(pd, VIDEO_STREAM_FMT_SEL,
- vid_resol, &cmd_status);
-
- /* set the resolution to firmware */
- vid_resol = TLG_TUNE_VID_RES_720;
- switch (pix->width) {
- case 704:
- vid_resol = TLG_TUNE_VID_RES_704;
- break;
- default:
- pix->width = 720;
- case 720:
- break;
- }
- ret |= send_set_req(pd, VIDEO_ROSOLU_SEL,
- vid_resol, &cmd_status);
- if (ret || cmd_status)
- return -EBUSY;
-
- pix_def->pixelformat = pix->pixelformat; /* save it */
- pix->height = (context->tvnormid & V4L2_STD_525_60) ? 480 : 576;
-
- /* Compare with the default setting */
- if ((pix_def->width != pix->width)
- || (pix_def->height != pix->height)) {
- pix_def->width = pix->width;
- pix_def->height = pix->height;
- pix_def->bytesperline = pix->width * 2;
- pix_def->sizeimage = pix->width * pix->height * 2;
- }
- *pix = *pix_def;
-
- return 0;
-}
-
-static int vidioc_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
-{
- struct front_face *front = fh;
- struct poseidon *pd = front->pd;
-
- /* stop VBI here */
- if (V4L2_BUF_TYPE_VIDEO_CAPTURE != f->type)
- return -EINVAL;
-
- mutex_lock(&pd->lock);
- if (pd->file_for_stream == NULL)
- pd->file_for_stream = file;
- else if (file != pd->file_for_stream) {
- mutex_unlock(&pd->lock);
- return -EINVAL;
- }
-
- pd_vidioc_s_fmt(pd, &f->fmt.pix);
- mutex_unlock(&pd->lock);
- return 0;
-}
-
-static int vidioc_g_fmt_vbi(struct file *file, void *fh,
- struct v4l2_format *v4l2_f)
-{
- struct front_face *front = fh;
- struct poseidon *pd = front->pd;
- struct v4l2_vbi_format *vbi_fmt = &v4l2_f->fmt.vbi;
-
- vbi_fmt->samples_per_line = 720 * 2;
- vbi_fmt->sampling_rate = 6750000 * 4;
- vbi_fmt->sample_format = V4L2_PIX_FMT_GREY;
- vbi_fmt->offset = 64 * 4; /*FIXME: why offset */
- if (pd->video_data.context.tvnormid & V4L2_STD_525_60) {
- vbi_fmt->start[0] = 10;
- vbi_fmt->start[1] = 264;
- vbi_fmt->count[0] = V4L_NTSC_VBI_LINES;
- vbi_fmt->count[1] = V4L_NTSC_VBI_LINES;
- } else {
- vbi_fmt->start[0] = 6;
- vbi_fmt->start[1] = 314;
- vbi_fmt->count[0] = V4L_PAL_VBI_LINES;
- vbi_fmt->count[1] = V4L_PAL_VBI_LINES;
- }
- vbi_fmt->flags = V4L2_VBI_UNSYNC;
- return 0;
-}
-
-static int set_std(struct poseidon *pd, v4l2_std_id norm)
-{
- struct video_data *video = &pd->video_data;
- struct vbi_data *vbi = &pd->vbi_data;
- struct running_context *context;
- struct v4l2_pix_format *pix;
- s32 i, ret = 0, cmd_status, param;
- int height;
-
- for (i = 0; i < POSEIDON_TVNORMS; i++) {
- if (norm & poseidon_tvnorms[i].v4l2_id) {
- param = poseidon_tvnorms[i].tlg_tvnorm;
- log("name : %s", poseidon_tvnorms[i].name);
- goto found;
- }
- }
- return -EINVAL;
-found:
- mutex_lock(&pd->lock);
- ret = send_set_req(pd, VIDEO_STD_SEL, param, &cmd_status);
- if (ret || cmd_status)
- goto out;
-
- /* Set vbi size and check the height of the frame */
- context = &video->context;
- context->tvnormid = poseidon_tvnorms[i].v4l2_id;
- if (context->tvnormid & V4L2_STD_525_60) {
- vbi->vbi_size = V4L_NTSC_VBI_FRAMESIZE;
- height = 480;
- } else {
- vbi->vbi_size = V4L_PAL_VBI_FRAMESIZE;
- height = 576;
- }
-
- pix = &context->pix;
- if (pix->height != height) {
- pix->height = height;
- pix->sizeimage = pix->width * pix->height * 2;
- }
-
-out:
- mutex_unlock(&pd->lock);
- return ret;
-}
-
-static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id norm)
-{
- struct front_face *front = fh;
-
- return set_std(front->pd, norm);
-}
-
-static int vidioc_g_std(struct file *file, void *fh, v4l2_std_id *norm)
-{
- struct front_face *front = fh;
-
- *norm = front->pd->video_data.context.tvnormid;
- return 0;
-}
-
-static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *in)
-{
- if (in->index >= POSEIDON_INPUTS)
- return -EINVAL;
- strcpy(in->name, pd_inputs[in->index].name);
- in->type = V4L2_INPUT_TYPE_TUNER;
-
- /*
- * the audio input index mixed with this video input,
- * Poseidon only have one audio/video, set to "0"
- */
- in->audioset = 1;
- in->tuner = 0;
- in->std = V4L2_STD_ALL;
- in->status = 0;
- return 0;
-}
-
-static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
-{
- struct front_face *front = fh;
- struct poseidon *pd = front->pd;
- struct running_context *context = &pd->video_data.context;
-
- *i = context->sig_index;
- return 0;
-}
-
-/* We can support several inputs */
-static int vidioc_s_input(struct file *file, void *fh, unsigned int i)
-{
- struct front_face *front = fh;
- struct poseidon *pd = front->pd;
- s32 ret, cmd_status;
-
- if (i >= POSEIDON_INPUTS)
- return -EINVAL;
- ret = send_set_req(pd, SGNL_SRC_SEL,
- pd_inputs[i].tlg_src, &cmd_status);
- if (ret)
- return ret;
-
- pd->video_data.context.sig_index = i;
- return 0;
-}
-
-static int tlg_s_ctrl(struct v4l2_ctrl *c)
-{
- struct poseidon *pd = container_of(c->handler, struct poseidon,
- video_data.ctrl_handler);
- struct tuner_custom_parameter_s param = {0};
- s32 ret = 0, cmd_status, params;
-
- switch (c->id) {
- case V4L2_CID_BRIGHTNESS:
- param.param_id = CUST_PARM_ID_BRIGHTNESS_CTRL;
- break;
- case V4L2_CID_CONTRAST:
- param.param_id = CUST_PARM_ID_CONTRAST_CTRL;
- break;
- case V4L2_CID_HUE:
- param.param_id = CUST_PARM_ID_HUE_CTRL;
- break;
- case V4L2_CID_SATURATION:
- param.param_id = CUST_PARM_ID_SATURATION_CTRL;
- break;
- }
- param.param_value = c->val;
- params = *(s32 *)¶m; /* temp code */
-
- mutex_lock(&pd->lock);
- ret = send_set_req(pd, TUNER_CUSTOM_PARAMETER, params, &cmd_status);
- ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
- mutex_unlock(&pd->lock);
-
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ/4);
- return ret;
-}
-
-/* Audio ioctls */
-static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a)
-{
- if (0 != a->index)
- return -EINVAL;
- a->capability = V4L2_AUDCAP_STEREO;
- strcpy(a->name, "USB audio in");
- /*Poseidon have no AVL function.*/
- a->mode = 0;
- return 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a)
-{
- a->index = 0;
- a->capability = V4L2_AUDCAP_STEREO;
- strcpy(a->name, "USB audio in");
- a->mode = 0;
- return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *a)
-{
- return (0 == a->index) ? 0 : -EINVAL;
-}
-
-/* Tuner ioctls */
-static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *tuner)
-{
- struct front_face *front = fh;
- struct poseidon *pd = front->pd;
- struct tuner_atv_sig_stat_s atv_stat;
- s32 count = 5, ret, cmd_status;
- int index;
-
- if (0 != tuner->index)
- return -EINVAL;
-
- mutex_lock(&pd->lock);
- ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_ANALOG_TV,
- &atv_stat, &cmd_status, sizeof(atv_stat));
-
- while (atv_stat.sig_lock_busy && count-- && !ret) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ);
-
- ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_ANALOG_TV,
- &atv_stat, &cmd_status, sizeof(atv_stat));
- }
- mutex_unlock(&pd->lock);
-
- if (debug_mode)
- log("P:%d,S:%d", atv_stat.sig_present, atv_stat.sig_strength);
-
- if (ret || cmd_status)
- tuner->signal = 0;
- else if (atv_stat.sig_present && !atv_stat.sig_strength)
- tuner->signal = 0xFFFF;
- else
- tuner->signal = (atv_stat.sig_strength * 255 / 10) << 8;
-
- strcpy(tuner->name, "Telegent Systems");
- tuner->type = V4L2_TUNER_ANALOG_TV;
- tuner->rangelow = TUNER_FREQ_MIN / 62500;
- tuner->rangehigh = TUNER_FREQ_MAX / 62500;
- tuner->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO |
- V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
- index = pd->video_data.context.audio_idx;
- tuner->rxsubchans = pd_audio_modes[index].v4l2_audio_sub;
- tuner->audmode = pd_audio_modes[index].v4l2_audio_mode;
- tuner->afc = 0;
- return 0;
-}
-
-static int pd_vidioc_s_tuner(struct poseidon *pd, int index)
-{
- s32 ret = 0, cmd_status, param, audiomode;
-
- mutex_lock(&pd->lock);
- param = pd_audio_modes[index].tlg_audio_mode;
- ret = send_set_req(pd, TUNER_AUD_MODE, param, &cmd_status);
- audiomode = get_audio_std(pd->video_data.context.tvnormid);
- ret |= send_set_req(pd, TUNER_AUD_ANA_STD, audiomode,
- &cmd_status);
- if (!ret)
- pd->video_data.context.audio_idx = index;
- mutex_unlock(&pd->lock);
- return ret;
-}
-
-static int vidioc_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *a)
-{
- struct front_face *front = fh;
- struct poseidon *pd = front->pd;
- int index;
-
- if (0 != a->index)
- return -EINVAL;
- for (index = 0; index < POSEIDON_AUDIOMODS; index++)
- if (a->audmode == pd_audio_modes[index].v4l2_audio_mode)
- return pd_vidioc_s_tuner(pd, index);
- return -EINVAL;
-}
-
-static int vidioc_g_frequency(struct file *file, void *fh,
- struct v4l2_frequency *freq)
-{
- struct front_face *front = fh;
- struct poseidon *pd = front->pd;
- struct running_context *context = &pd->video_data.context;
-
- if (0 != freq->tuner)
- return -EINVAL;
- freq->frequency = context->freq;
- freq->type = V4L2_TUNER_ANALOG_TV;
- return 0;
-}
-
-static int set_frequency(struct poseidon *pd, u32 *frequency)
-{
- s32 ret = 0, param, cmd_status;
- struct running_context *context = &pd->video_data.context;
-
- *frequency = clamp(*frequency,
- TUNER_FREQ_MIN / 62500, TUNER_FREQ_MAX / 62500);
- param = (*frequency) * 62500 / 1000;
-
- mutex_lock(&pd->lock);
- ret = send_set_req(pd, TUNE_FREQ_SELECT, param, &cmd_status);
- ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
-
- msleep(250); /* wait for a while until the hardware is ready. */
- context->freq = *frequency;
- mutex_unlock(&pd->lock);
- return ret;
-}
-
-static int vidioc_s_frequency(struct file *file, void *fh,
- const struct v4l2_frequency *freq)
-{
- struct front_face *front = fh;
- struct poseidon *pd = front->pd;
- u32 frequency = freq->frequency;
-
- if (freq->tuner)
- return -EINVAL;
-#ifdef CONFIG_PM
- pd->pm_suspend = pm_video_suspend;
- pd->pm_resume = pm_video_resume;
-#endif
- return set_frequency(pd, &frequency);
-}
-
-static int vidioc_reqbufs(struct file *file, void *fh,
- struct v4l2_requestbuffers *b)
-{
- struct front_face *front = file->private_data;
- return videobuf_reqbufs(&front->q, b);
-}
-
-static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
-{
- struct front_face *front = file->private_data;
- return videobuf_querybuf(&front->q, b);
-}
-
-static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
-{
- struct front_face *front = file->private_data;
- return videobuf_qbuf(&front->q, b);
-}
-
-static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
-{
- struct front_face *front = file->private_data;
- return videobuf_dqbuf(&front->q, b, file->f_flags & O_NONBLOCK);
-}
-
-/* Just stop the URBs, do not free the URBs */
-static int usb_transfer_stop(struct video_data *video)
-{
- if (video->is_streaming) {
- int i;
- s32 cmd_status;
- struct poseidon *pd = video->pd;
-
- video->is_streaming = 0;
- for (i = 0; i < SBUF_NUM; ++i) {
- if (video->urb_array[i])
- usb_kill_urb(video->urb_array[i]);
- }
-
- send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP,
- &cmd_status);
- }
- return 0;
-}
-
-int stop_all_video_stream(struct poseidon *pd)
-{
- struct video_data *video = &pd->video_data;
- struct vbi_data *vbi = &pd->vbi_data;
-
- mutex_lock(&pd->lock);
- if (video->is_streaming) {
- struct front_face *front = video->front;
-
- /* stop the URBs */
- usb_transfer_stop(video);
- free_all_urb(video);
-
- /* stop the host side of VIDEO */
- videobuf_stop(&front->q);
- videobuf_mmap_free(&front->q);
-
- /* stop the host side of VBI */
- front = vbi->front;
- if (front) {
- videobuf_stop(&front->q);
- videobuf_mmap_free(&front->q);
- }
- }
- mutex_unlock(&pd->lock);
- return 0;
-}
-
-/*
- * The bubbles can seriously damage the video's quality,
- * though it occurs in very rare situation.
- */
-static void iso_bubble_handler(struct work_struct *w)
-{
- struct video_data *video;
- struct poseidon *pd;
-
- video = container_of(w, struct video_data, bubble_work);
- pd = video->pd;
-
- mutex_lock(&pd->lock);
- usb_transfer_stop(video);
- msleep(500);
- start_video_stream(pd);
- mutex_unlock(&pd->lock);
-}
-
-
-static int vidioc_streamon(struct file *file, void *fh,
- enum v4l2_buf_type type)
-{
- struct front_face *front = fh;
-
- if (unlikely(type != front->type))
- return -EINVAL;
- return videobuf_streamon(&front->q);
-}
-
-static int vidioc_streamoff(struct file *file, void *fh,
- enum v4l2_buf_type type)
-{
- struct front_face *front = file->private_data;
-
- if (unlikely(type != front->type))
- return -EINVAL;
- return videobuf_streamoff(&front->q);
-}
-
-/* Set the firmware's default values : need altersetting */
-static int pd_video_checkmode(struct poseidon *pd)
-{
- s32 ret = 0, cmd_status, audiomode;
-
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ/2);
-
- /* choose the altersetting */
- ret = usb_set_interface(pd->udev, 0,
- (pd->cur_transfer_mode ?
- ISO_3K_BULK_ALTERNATE_IFACE :
- BULK_ALTERNATE_IFACE));
- if (ret < 0)
- goto error;
-
- /* set default parameters for PAL-D , with the VBI enabled*/
- ret = set_tuner_mode(pd, TLG_MODE_ANALOG_TV);
- ret |= send_set_req(pd, SGNL_SRC_SEL,
- TLG_SIG_SRC_ANTENNA, &cmd_status);
- ret |= send_set_req(pd, VIDEO_STD_SEL,
- TLG_TUNE_VSTD_PAL_D, &cmd_status);
- ret |= send_set_req(pd, VIDEO_STREAM_FMT_SEL,
- TLG_TUNER_VID_FORMAT_YUV, &cmd_status);
- ret |= send_set_req(pd, VIDEO_ROSOLU_SEL,
- TLG_TUNE_VID_RES_720, &cmd_status);
- ret |= send_set_req(pd, TUNE_FREQ_SELECT, TUNER_FREQ_MIN, &cmd_status);
- ret |= send_set_req(pd, VBI_DATA_SEL, 1, &cmd_status);/* enable vbi */
-
- /* set the audio */
- audiomode = get_audio_std(pd->video_data.context.tvnormid);
- ret |= send_set_req(pd, TUNER_AUD_ANA_STD, audiomode, &cmd_status);
- ret |= send_set_req(pd, TUNER_AUD_MODE,
- TLG_TUNE_TVAUDIO_MODE_STEREO, &cmd_status);
- ret |= send_set_req(pd, AUDIO_SAMPLE_RATE_SEL,
- ATV_AUDIO_RATE_48K, &cmd_status);
-error:
- return ret;
-}
-
-#ifdef CONFIG_PM
-static int pm_video_suspend(struct poseidon *pd)
-{
- /* stop audio */
- pm_alsa_suspend(pd);
-
- /* stop and free all the URBs */
- usb_transfer_stop(&pd->video_data);
- free_all_urb(&pd->video_data);
-
- /* reset the interface */
- usb_set_interface(pd->udev, 0, 0);
- msleep(300);
- return 0;
-}
-
-static int restore_v4l2_context(struct poseidon *pd,
- struct running_context *context)
-{
- struct front_face *front = pd->video_data.front;
-
- pd_video_checkmode(pd);
-
- set_std(pd, context->tvnormid);
- vidioc_s_input(NULL, front, context->sig_index);
- pd_vidioc_s_tuner(pd, context->audio_idx);
- pd_vidioc_s_fmt(pd, &context->pix);
- set_frequency(pd, &context->freq);
- return 0;
-}
-
-static int pm_video_resume(struct poseidon *pd)
-{
- struct video_data *video = &pd->video_data;
-
- /* resume the video */
- /* [1] restore the origin V4L2 parameters */
- restore_v4l2_context(pd, &video->context);
-
- /* [2] initiate video copy variables */
- if (video->front->curr_frame)
- init_copy(video, 0);
-
- /* [3] fire urbs */
- start_video_stream(pd);
-
- /* resume the audio */
- pm_alsa_resume(pd);
- return 0;
-}
-#endif
-
-void set_debug_mode(struct video_device *vfd, int debug_mode)
-{
- vfd->debug = 0;
- if (debug_mode & 0x1)
- vfd->debug = V4L2_DEBUG_IOCTL;
- if (debug_mode & 0x2)
- vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
-}
-
-static void init_video_context(struct running_context *context)
-{
- context->sig_index = 0;
- context->audio_idx = 1; /* stereo */
- context->tvnormid = V4L2_STD_PAL_D;
- context->pix = (struct v4l2_pix_format) {
- .width = 720,
- .height = 576,
- .pixelformat = V4L2_PIX_FMT_YUYV,
- .field = V4L2_FIELD_INTERLACED,
- .bytesperline = 720 * 2,
- .sizeimage = 720 * 576 * 2,
- .colorspace = V4L2_COLORSPACE_SMPTE170M,
- };
-}
-
-static int pd_video_open(struct file *file)
-{
- struct video_device *vfd = video_devdata(file);
- struct poseidon *pd = video_get_drvdata(vfd);
- struct front_face *front = NULL;
- int ret = -ENOMEM;
-
- mutex_lock(&pd->lock);
- usb_autopm_get_interface(pd->interface);
-
- if (pd->state && !(pd->state & POSEIDON_STATE_ANALOG)) {
- ret = -EBUSY;
- goto out;
- }
- front = kzalloc(sizeof(struct front_face), GFP_KERNEL);
- if (!front)
- goto out;
- if (vfd->vfl_type == VFL_TYPE_GRABBER) {
- pd->cur_transfer_mode = usb_transfer_mode;/* bulk or iso */
- init_video_context(&pd->video_data.context);
-
- ret = pd_video_checkmode(pd);
- if (ret < 0) {
- kfree(front);
- ret = -1;
- goto out;
- }
-
- front->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- pd->video_data.users++;
- set_debug_mode(vfd, debug_mode);
-
- videobuf_queue_vmalloc_init(&front->q, &pd_video_qops,
- NULL, &front->queue_lock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_INTERLACED,/* video is interlacd */
- sizeof(struct videobuf_buffer),/*it's enough*/
- front, NULL);
- } else {
- front->type = V4L2_BUF_TYPE_VBI_CAPTURE;
- pd->vbi_data.front = front;
- pd->vbi_data.users++;
-
- videobuf_queue_vmalloc_init(&front->q, &pd_video_qops,
- NULL, &front->queue_lock,
- V4L2_BUF_TYPE_VBI_CAPTURE,
- V4L2_FIELD_NONE, /* vbi is NONE mode */
- sizeof(struct videobuf_buffer),
- front, NULL);
- }
-
- pd->state |= POSEIDON_STATE_ANALOG;
- front->pd = pd;
- front->curr_frame = NULL;
- INIT_LIST_HEAD(&front->active);
- spin_lock_init(&front->queue_lock);
-
- file->private_data = front;
- kref_get(&pd->kref);
-
- mutex_unlock(&pd->lock);
- return 0;
-out:
- usb_autopm_put_interface(pd->interface);
- mutex_unlock(&pd->lock);
- return ret;
-}
-
-static int pd_video_release(struct file *file)
-{
- struct front_face *front = file->private_data;
- struct poseidon *pd = front->pd;
- s32 cmd_status = 0;
-
- mutex_lock(&pd->lock);
-
- if (front->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- /* stop the device, and free the URBs */
- usb_transfer_stop(&pd->video_data);
- free_all_urb(&pd->video_data);
-
- /* stop the firmware */
- send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP,
- &cmd_status);
-
- pd->file_for_stream = NULL;
- pd->video_data.users--;
- } else if (front->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
- pd->vbi_data.front = NULL;
- pd->vbi_data.users--;
- }
- if (!pd->vbi_data.users && !pd->video_data.users)
- pd->state &= ~POSEIDON_STATE_ANALOG;
- videobuf_stop(&front->q);
- videobuf_mmap_free(&front->q);
-
- usb_autopm_put_interface(pd->interface);
- mutex_unlock(&pd->lock);
-
- kfree(front);
- file->private_data = NULL;
- kref_put(&pd->kref, poseidon_delete);
- return 0;
-}
-
-static int pd_video_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct front_face *front = file->private_data;
- return videobuf_mmap_mapper(&front->q, vma);
-}
-
-static unsigned int pd_video_poll(struct file *file, poll_table *table)
-{
- struct front_face *front = file->private_data;
- return videobuf_poll_stream(file, &front->q, table);
-}
-
-static ssize_t pd_video_read(struct file *file, char __user *buffer,
- size_t count, loff_t *ppos)
-{
- struct front_face *front = file->private_data;
- return videobuf_read_stream(&front->q, buffer, count, ppos,
- 0, file->f_flags & O_NONBLOCK);
-}
-
-/* This struct works for both VIDEO and VBI */
-static const struct v4l2_file_operations pd_video_fops = {
- .owner = THIS_MODULE,
- .open = pd_video_open,
- .release = pd_video_release,
- .read = pd_video_read,
- .poll = pd_video_poll,
- .mmap = pd_video_mmap,
- .ioctl = video_ioctl2, /* maybe changed in future */
-};
-
-static const struct v4l2_ioctl_ops pd_video_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
-
- /* Video format */
- .vidioc_g_fmt_vid_cap = vidioc_g_fmt,
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt,
- .vidioc_s_fmt_vid_cap = vidioc_s_fmt,
- .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi, /* VBI */
-
- /* Input */
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_enum_input = vidioc_enum_input,
-
- /* Audio ioctls */
- .vidioc_enumaudio = vidioc_enumaudio,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
-
- /* Tuner ioctls */
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_std = vidioc_g_std,
- .vidioc_s_std = vidioc_s_std,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
-
- /* Buffer handlers */
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
-
- /* Stream on/off */
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
-};
-
-static struct video_device pd_video_template = {
- .name = "Telegent-Video",
- .fops = &pd_video_fops,
- .minor = -1,
- .release = video_device_release_empty,
- .tvnorms = V4L2_STD_ALL,
- .ioctl_ops = &pd_video_ioctl_ops,
-};
-
-static const struct v4l2_ctrl_ops tlg_ctrl_ops = {
- .s_ctrl = tlg_s_ctrl,
-};
-
-void pd_video_exit(struct poseidon *pd)
-{
- struct video_data *video = &pd->video_data;
- struct vbi_data *vbi = &pd->vbi_data;
-
- video_unregister_device(&video->v_dev);
- video_unregister_device(&vbi->v_dev);
- v4l2_ctrl_handler_free(&video->ctrl_handler);
- log();
-}
-
-int pd_video_init(struct poseidon *pd)
-{
- struct video_data *video = &pd->video_data;
- struct vbi_data *vbi = &pd->vbi_data;
- struct v4l2_ctrl_handler *hdl = &video->ctrl_handler;
- u32 freq = TUNER_FREQ_MIN / 62500;
- int ret = -ENOMEM;
-
- v4l2_ctrl_handler_init(hdl, 4);
- v4l2_ctrl_new_std(hdl, &tlg_ctrl_ops, V4L2_CID_BRIGHTNESS,
- 0, 10000, 1, 100);
- v4l2_ctrl_new_std(hdl, &tlg_ctrl_ops, V4L2_CID_CONTRAST,
- 0, 10000, 1, 100);
- v4l2_ctrl_new_std(hdl, &tlg_ctrl_ops, V4L2_CID_HUE,
- 0, 10000, 1, 100);
- v4l2_ctrl_new_std(hdl, &tlg_ctrl_ops, V4L2_CID_SATURATION,
- 0, 10000, 1, 100);
- if (hdl->error) {
- v4l2_ctrl_handler_free(hdl);
- return hdl->error;
- }
- set_frequency(pd, &freq);
- video->v_dev = pd_video_template;
- video->v_dev.v4l2_dev = &pd->v4l2_dev;
- video->v_dev.ctrl_handler = hdl;
- video_set_drvdata(&video->v_dev, pd);
-
- ret = video_register_device(&video->v_dev, VFL_TYPE_GRABBER, -1);
- if (ret != 0)
- goto out;
-
- /* VBI uses the same template as video */
- vbi->v_dev = pd_video_template;
- vbi->v_dev.v4l2_dev = &pd->v4l2_dev;
- vbi->v_dev.ctrl_handler = hdl;
- video_set_drvdata(&vbi->v_dev, pd);
- ret = video_register_device(&vbi->v_dev, VFL_TYPE_VBI, -1);
- if (ret != 0)
- goto out;
- log("register VIDEO/VBI devices");
- return 0;
-out:
- log("VIDEO/VBI devices register failed, : %d", ret);
- pd_video_exit(pd);
- return ret;
-}
diff --git a/drivers/media/usb/tlg2300/vendorcmds.h b/drivers/media/usb/tlg2300/vendorcmds.h
deleted file mode 100644
index ba6f4ae..0000000
--- a/drivers/media/usb/tlg2300/vendorcmds.h
+++ /dev/null
@@ -1,243 +0,0 @@
-#ifndef VENDOR_CMD_H_
-#define VENDOR_CMD_H_
-
-#define BULK_ALTERNATE_IFACE (2)
-#define ISO_3K_BULK_ALTERNATE_IFACE (1)
-#define REQ_SET_CMD (0X00)
-#define REQ_GET_CMD (0X80)
-
-enum tlg__analog_audio_standard {
- TLG_TUNE_ASTD_NONE = 0x00000000,
- TLG_TUNE_ASTD_A2 = 0x00000001,
- TLG_TUNE_ASTD_NICAM = 0x00000002,
- TLG_TUNE_ASTD_EIAJ = 0x00000004,
- TLG_TUNE_ASTD_BTSC = 0x00000008,
- TLG_TUNE_ASTD_FM_US = 0x00000010,
- TLG_TUNE_ASTD_FM_EUR = 0x00000020,
- TLG_TUNE_ASTD_ALL = 0x0000003f
-};
-
-/*
- * identifiers for Custom Parameter messages.
- * @typedef cmd_custom_param_id_t
- */
-enum cmd_custom_param_id {
- CUST_PARM_ID_NONE = 0x00,
- CUST_PARM_ID_BRIGHTNESS_CTRL = 0x01,
- CUST_PARM_ID_CONTRAST_CTRL = 0x02,
- CUST_PARM_ID_HUE_CTRL = 0x03,
- CUST_PARM_ID_SATURATION_CTRL = 0x04,
- CUST_PARM_ID_AUDIO_SNR_THRESHOLD = 0x10,
- CUST_PARM_ID_AUDIO_AGC_THRESHOLD = 0x11,
- CUST_PARM_ID_MAX
-};
-
-struct tuner_custom_parameter_s {
- uint16_t param_id; /* Parameter identifier */
- uint16_t param_value; /* Parameter value */
-};
-
-struct tuner_ber_rate_s {
- uint32_t ber_rate; /* BER sample rate in seconds */
-};
-
-struct tuner_atv_sig_stat_s {
- uint32_t sig_present;
- uint32_t sig_locked;
- uint32_t sig_lock_busy;
- uint32_t sig_strength; /* milliDb */
- uint32_t tv_audio_chan; /* mono/stereo/sap*/
- uint32_t mvision_stat; /* macrovision status */
-};
-
-struct tuner_dtv_sig_stat_s {
- uint32_t sig_present; /* Boolean*/
- uint32_t sig_locked; /* Boolean */
- uint32_t sig_lock_busy; /* Boolean (Can this time-out?) */
- uint32_t sig_strength; /* milliDb*/
-};
-
-struct tuner_fm_sig_stat_s {
- uint32_t sig_present; /* Boolean*/
- uint32_t sig_locked; /* Boolean */
- uint32_t sig_lock_busy; /* Boolean */
- uint32_t sig_stereo_mono;/* TBD*/
- uint32_t sig_strength; /* milliDb*/
-};
-
-enum _tag_tlg_tune_srv_cmd {
- TLG_TUNE_PLAY_SVC_START = 1,
- TLG_TUNE_PLAY_SVC_STOP
-};
-
-enum _tag_tune_atv_audio_mode_caps {
- TLG_TUNE_TVAUDIO_MODE_MONO = 0x00000001,
- TLG_TUNE_TVAUDIO_MODE_STEREO = 0x00000002,
- TLG_TUNE_TVAUDIO_MODE_LANG_A = 0x00000010,/* Primary language*/
- TLG_TUNE_TVAUDIO_MODE_LANG_B = 0x00000020,/* 2nd avail language*/
- TLG_TUNE_TVAUDIO_MODE_LANG_C = 0x00000040
-};
-
-
-enum _tag_tuner_atv_audio_rates {
- ATV_AUDIO_RATE_NONE = 0x00,/* Audio not supported*/
- ATV_AUDIO_RATE_32K = 0x01,/* Audio rate = 32 KHz*/
- ATV_AUDIO_RATE_48K = 0x02, /* Audio rate = 48 KHz*/
- ATV_AUDIO_RATE_31_25K = 0x04 /* Audio rate = 31.25KHz */
-};
-
-enum _tag_tune_atv_vid_res_caps {
- TLG_TUNE_VID_RES_NONE = 0x00000000,
- TLG_TUNE_VID_RES_720 = 0x00000001,
- TLG_TUNE_VID_RES_704 = 0x00000002,
- TLG_TUNE_VID_RES_360 = 0x00000004
-};
-
-enum _tag_tuner_analog_video_format {
- TLG_TUNER_VID_FORMAT_YUV = 0x00000001,
- TLG_TUNER_VID_FORMAT_YCRCB = 0x00000002,
- TLG_TUNER_VID_FORMAT_RGB_565 = 0x00000004,
-};
-
-enum tlg_ext_audio_support {
- TLG_EXT_AUDIO_NONE = 0x00,/* No external audio input supported */
- TLG_EXT_AUDIO_LR = 0x01/* LR external audio inputs supported*/
-};
-
-enum {
- TLG_MODE_NONE = 0x00, /* No Mode specified*/
- TLG_MODE_ANALOG_TV = 0x01, /* Analog Television mode*/
- TLG_MODE_ANALOG_TV_UNCOMP = 0x01, /* Analog Television mode*/
- TLG_MODE_ANALOG_TV_COMP = 0x02, /* Analog TV mode (compressed)*/
- TLG_MODE_FM_RADIO = 0x04, /* FM Radio mode*/
- TLG_MODE_DVB_T = 0x08, /* Digital TV (DVB-T)*/
-};
-
-enum tlg_signal_sources_t {
- TLG_SIG_SRC_NONE = 0x00,/* Signal source not specified */
- TLG_SIG_SRC_ANTENNA = 0x01,/* Signal src is: Antenna */
- TLG_SIG_SRC_CABLE = 0x02,/* Signal src is: Coax Cable*/
- TLG_SIG_SRC_SVIDEO = 0x04,/* Signal src is: S_VIDEO */
- TLG_SIG_SRC_COMPOSITE = 0x08 /* Signal src is: Composite Video */
-};
-
-enum tuner_analog_video_standard {
- TLG_TUNE_VSTD_NONE = 0x00000000,
- TLG_TUNE_VSTD_NTSC_M = 0x00000001,
- TLG_TUNE_VSTD_NTSC_M_J = 0x00000002,/* Japan */
- TLG_TUNE_VSTD_PAL_B = 0x00000010,
- TLG_TUNE_VSTD_PAL_D = 0x00000020,
- TLG_TUNE_VSTD_PAL_G = 0x00000040,
- TLG_TUNE_VSTD_PAL_H = 0x00000080,
- TLG_TUNE_VSTD_PAL_I = 0x00000100,
- TLG_TUNE_VSTD_PAL_M = 0x00000200,
- TLG_TUNE_VSTD_PAL_N = 0x00000400,
- TLG_TUNE_VSTD_SECAM_B = 0x00001000,
- TLG_TUNE_VSTD_SECAM_D = 0x00002000,
- TLG_TUNE_VSTD_SECAM_G = 0x00004000,
- TLG_TUNE_VSTD_SECAM_H = 0x00008000,
- TLG_TUNE_VSTD_SECAM_K = 0x00010000,
- TLG_TUNE_VSTD_SECAM_K1 = 0x00020000,
- TLG_TUNE_VSTD_SECAM_L = 0x00040000,
- TLG_TUNE_VSTD_SECAM_L1 = 0x00080000,
- TLG_TUNE_VSTD_PAL_N_COMBO = 0x00100000
-};
-
-enum tlg_mode_caps {
- TLG_MODE_CAPS_NONE = 0x00, /* No Mode specified */
- TLG_MODE_CAPS_ANALOG_TV_UNCOMP = 0x01, /* Analog TV mode */
- TLG_MODE_CAPS_ANALOG_TV_COMP = 0x02, /* Analog TV (compressed)*/
- TLG_MODE_CAPS_FM_RADIO = 0x04, /* FM Radio mode */
- TLG_MODE_CAPS_DVB_T = 0x08, /* Digital TV (DVB-T) */
-};
-
-enum poseidon_vendor_cmds {
- LAST_CMD_STAT = 0x00,
- GET_CHIP_ID = 0x01,
- GET_FW_ID = 0x02,
- PRODUCT_CAPS = 0x03,
-
- TUNE_MODE_CAP_ATV = 0x10,
- TUNE_MODE_CAP_ATVCOMP = 0X10,
- TUNE_MODE_CAP_DVBT = 0x10,
- TUNE_MODE_CAP_FM = 0x10,
- TUNE_MODE_SELECT = 0x11,
- TUNE_FREQ_SELECT = 0x12,
- SGNL_SRC_SEL = 0x13,
-
- VIDEO_STD_SEL = 0x14,
- VIDEO_STREAM_FMT_SEL = 0x15,
- VIDEO_ROSOLU_AVAIL = 0x16,
- VIDEO_ROSOLU_SEL = 0x17,
- VIDEO_CONT_PROTECT = 0x20,
-
- VCR_TIMING_MODSEL = 0x21,
- EXT_AUDIO_CAP = 0x22,
- EXT_AUDIO_SEL = 0x23,
- TEST_PATTERN_SEL = 0x24,
- VBI_DATA_SEL = 0x25,
- AUDIO_SAMPLE_RATE_CAP = 0x28,
- AUDIO_SAMPLE_RATE_SEL = 0x29,
- TUNER_AUD_MODE = 0x2a,
- TUNER_AUD_MODE_AVAIL = 0x2b,
- TUNER_AUD_ANA_STD = 0x2c,
- TUNER_CUSTOM_PARAMETER = 0x2f,
-
- DVBT_TUNE_MODE_SEL = 0x30,
- DVBT_BANDW_CAP = 0x31,
- DVBT_BANDW_SEL = 0x32,
- DVBT_GUARD_INTERV_CAP = 0x33,
- DVBT_GUARD_INTERV_SEL = 0x34,
- DVBT_MODULATION_CAP = 0x35,
- DVBT_MODULATION_SEL = 0x36,
- DVBT_INNER_FEC_RATE_CAP = 0x37,
- DVBT_INNER_FEC_RATE_SEL = 0x38,
- DVBT_TRANS_MODE_CAP = 0x39,
- DVBT_TRANS_MODE_SEL = 0x3a,
- DVBT_SEARCH_RANG = 0x3c,
-
- TUNER_SETUP_ANALOG = 0x40,
- TUNER_SETUP_DIGITAL = 0x41,
- TUNER_SETUP_FM_RADIO = 0x42,
- TAKE_REQUEST = 0x43, /* Take effect of the command */
- PLAY_SERVICE = 0x44, /* Play start or Play stop */
- TUNER_STATUS = 0x45,
- TUNE_PROP_DVBT = 0x46,
- ERR_RATE_STATS = 0x47,
- TUNER_BER_RATE = 0x48,
-
- SCAN_CAPS = 0x50,
- SCAN_SETUP = 0x51,
- SCAN_SERVICE = 0x52,
- SCAN_STATS = 0x53,
-
- PID_SET = 0x58,
- PID_UNSET = 0x59,
- PID_LIST = 0x5a,
-
- IRD_CAP = 0x60,
- IRD_MODE_SEL = 0x61,
- IRD_SETUP = 0x62,
-
- PTM_MODE_CAP = 0x70,
- PTM_MODE_SEL = 0x71,
- PTM_SERVICE = 0x72,
- TUNER_REG_SCRIPT = 0x73,
- CMD_CHIP_RST = 0x74,
-};
-
-enum tlg_bw {
- TLG_BW_5 = 5,
- TLG_BW_6 = 6,
- TLG_BW_7 = 7,
- TLG_BW_8 = 8,
- TLG_BW_12 = 12,
- TLG_BW_15 = 15
-};
-
-struct cmd_firmware_vers_s {
- uint8_t fw_rev_major;
- uint8_t fw_rev_minor;
- uint16_t fw_patch;
-};
-#endif /* VENDOR_CMD_H_ */
diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c
index 35ee9c3..282f11c 100644
--- a/drivers/media/usb/usbvision/usbvision-video.c
+++ b/drivers/media/usb/usbvision/usbvision-video.c
@@ -446,6 +446,7 @@
if (usbvision->remove_pending) {
printk(KERN_INFO "%s: Final disconnect\n", __func__);
usbvision_release(usbvision);
+ return 0;
}
mutex_unlock(&usbvision->v4l2_lock);
@@ -508,11 +509,12 @@
usbvision_device_data[usbvision->dev_model].model_string,
sizeof(vc->card));
usb_make_path(usbvision->dev, vc->bus_info, sizeof(vc->bus_info));
- vc->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ vc->device_caps = V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_AUDIO |
V4L2_CAP_READWRITE |
V4L2_CAP_STREAMING |
(usbvision->have_tuner ? V4L2_CAP_TUNER : 0);
+ vc->capabilities = vc->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -1221,6 +1223,7 @@
if (usbvision->remove_pending) {
printk(KERN_INFO "%s: Final disconnect\n", __func__);
usbvision_release(usbvision);
+ return err_code;
}
mutex_unlock(&usbvision->v4l2_lock);
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index b578d68..40d6c83 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -331,6 +331,7 @@
struct uvc_format_desc *fmtdesc;
struct uvc_frame *frame;
const unsigned char *start = buffer;
+ unsigned int width_multiplier = 1;
unsigned int interval;
unsigned int i, n;
__u8 ftype;
@@ -366,6 +367,20 @@
}
format->bpp = buffer[21];
+
+ /* Some devices report a format that doesn't match what they
+ * really send.
+ */
+ if (dev->quirks & UVC_QUIRK_FORCE_Y8) {
+ if (format->fcc == V4L2_PIX_FMT_YUYV) {
+ strlcpy(format->name, "Greyscale 8-bit (Y8 )",
+ sizeof(format->name));
+ format->fcc = V4L2_PIX_FMT_GREY;
+ format->bpp = 8;
+ width_multiplier = 2;
+ }
+ }
+
if (buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED) {
ftype = UVC_VS_FRAME_UNCOMPRESSED;
} else {
@@ -474,7 +489,8 @@
frame->bFrameIndex = buffer[3];
frame->bmCapabilities = buffer[4];
- frame->wWidth = get_unaligned_le16(&buffer[5]);
+ frame->wWidth = get_unaligned_le16(&buffer[5])
+ * width_multiplier;
frame->wHeight = get_unaligned_le16(&buffer[7]);
frame->dwMinBitRate = get_unaligned_le32(&buffer[9]);
frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]);
@@ -1623,12 +1639,12 @@
{
struct list_head *p, *n;
- usb_put_intf(dev->intf);
- usb_put_dev(dev->udev);
-
uvc_status_cleanup(dev);
uvc_ctrl_cleanup_device(dev);
+ usb_put_intf(dev->intf);
+ usb_put_dev(dev->udev);
+
if (dev->vdev.dev)
v4l2_device_unregister(&dev->vdev);
#ifdef CPTCFG_MEDIA_CONTROLLER
@@ -1718,6 +1734,11 @@
struct video_device *vdev;
int ret;
+ /* Initialize the video buffers queue. */
+ ret = uvc_queue_init(&stream->queue, stream->type, !uvc_no_drop_param);
+ if (ret)
+ return ret;
+
/* Initialize the streaming interface with default streaming
* parameters.
*/
@@ -1744,6 +1765,7 @@
*/
vdev->v4l2_dev = &dev->vdev;
vdev->fops = &uvc_fops;
+ vdev->ioctl_ops = &uvc_ioctl_ops;
vdev->release = uvc_release;
vdev->prio = &stream->chain->prio;
if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
@@ -1991,14 +2013,13 @@
{
struct uvc_device *dev = usb_get_intfdata(intf);
struct uvc_streaming *stream;
+ int ret = 0;
uvc_trace(UVC_TRACE_SUSPEND, "Resuming interface %u\n",
intf->cur_altsetting->desc.bInterfaceNumber);
if (intf->cur_altsetting->desc.bInterfaceSubClass ==
UVC_SC_VIDEOCONTROL) {
- int ret = 0;
-
if (reset) {
ret = uvc_ctrl_restore_values(dev);
if (ret < 0)
@@ -2014,8 +2035,13 @@
}
list_for_each_entry(stream, &dev->streams, list) {
- if (stream->intf == intf)
- return uvc_video_resume(stream, reset);
+ if (stream->intf == intf) {
+ ret = uvc_video_resume(stream, reset);
+ if (ret < 0)
+ uvc_queue_streamoff(&stream->queue,
+ stream->queue.queue.type);
+ return ret;
+ }
}
uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB interface "
@@ -2504,6 +2530,15 @@
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_MINMAX
| UVC_QUIRK_IGNORE_SELECTOR_UNIT },
+ /* Oculus VR Positional Tracker DK2 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x2833,
+ .idProduct = 0x0201,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_FORCE_Y8 },
/* Generic USB Video Class */
{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) },
{}
diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c
index 6e92d20..cc96072 100644
--- a/drivers/media/usb/uvc/uvc_queue.c
+++ b/drivers/media/usb/uvc/uvc_queue.c
@@ -36,6 +36,34 @@
* the driver.
*/
+static inline struct uvc_streaming *
+uvc_queue_to_stream(struct uvc_video_queue *queue)
+{
+ return container_of(queue, struct uvc_streaming, queue);
+}
+
+/*
+ * Return all queued buffers to videobuf2 in the requested state.
+ *
+ * This function must be called with the queue spinlock held.
+ */
+static void uvc_queue_return_buffers(struct uvc_video_queue *queue,
+ enum uvc_buffer_state state)
+{
+ enum vb2_buffer_state vb2_state = state == UVC_BUF_STATE_ERROR
+ ? VB2_BUF_STATE_ERROR
+ : VB2_BUF_STATE_QUEUED;
+
+ while (!list_empty(&queue->irqqueue)) {
+ struct uvc_buffer *buf = list_first_entry(&queue->irqqueue,
+ struct uvc_buffer,
+ queue);
+ list_del(&buf->queue);
+ buf->state = state;
+ vb2_buffer_done(&buf->buf, vb2_state);
+ }
+}
+
/* -----------------------------------------------------------------------------
* videobuf2 queue operations
*/
@@ -45,8 +73,7 @@
unsigned int sizes[], void *alloc_ctxs[])
{
struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
- struct uvc_streaming *stream =
- container_of(queue, struct uvc_streaming, queue);
+ struct uvc_streaming *stream = uvc_queue_to_stream(queue);
/* Make sure the image size is large enough. */
if (fmt && fmt->fmt.pix.sizeimage < stream->ctrl.dwMaxVideoFrameSize)
@@ -109,8 +136,7 @@
static void uvc_buffer_finish(struct vb2_buffer *vb)
{
struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
- struct uvc_streaming *stream =
- container_of(queue, struct uvc_streaming, queue);
+ struct uvc_streaming *stream = uvc_queue_to_stream(queue);
struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf);
if (vb->state == VB2_BUF_STATE_DONE)
@@ -131,6 +157,39 @@
mutex_lock(&queue->mutex);
}
+static int uvc_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
+ struct uvc_streaming *stream = uvc_queue_to_stream(queue);
+ unsigned long flags;
+ int ret;
+
+ queue->buf_used = 0;
+
+ ret = uvc_video_enable(stream, 1);
+ if (ret == 0)
+ return 0;
+
+ spin_lock_irqsave(&queue->irqlock, flags);
+ uvc_queue_return_buffers(queue, UVC_BUF_STATE_QUEUED);
+ spin_unlock_irqrestore(&queue->irqlock, flags);
+
+ return ret;
+}
+
+static void uvc_stop_streaming(struct vb2_queue *vq)
+{
+ struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
+ struct uvc_streaming *stream = uvc_queue_to_stream(queue);
+ unsigned long flags;
+
+ uvc_video_enable(stream, 0);
+
+ spin_lock_irqsave(&queue->irqlock, flags);
+ uvc_queue_return_buffers(queue, UVC_BUF_STATE_ERROR);
+ spin_unlock_irqrestore(&queue->irqlock, flags);
+}
+
static struct vb2_ops uvc_queue_qops = {
.queue_setup = uvc_queue_setup,
.buf_prepare = uvc_buffer_prepare,
@@ -138,6 +197,8 @@
.buf_finish = uvc_buffer_finish,
.wait_prepare = uvc_wait_prepare,
.wait_finish = uvc_wait_finish,
+ .start_streaming = uvc_start_streaming,
+ .stop_streaming = uvc_stop_streaming,
};
int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type,
@@ -165,12 +226,19 @@
return 0;
}
+void uvc_queue_release(struct uvc_video_queue *queue)
+{
+ mutex_lock(&queue->mutex);
+ vb2_queue_release(&queue->queue);
+ mutex_unlock(&queue->mutex);
+}
+
/* -----------------------------------------------------------------------------
* V4L2 queue operations
*/
-int uvc_alloc_buffers(struct uvc_video_queue *queue,
- struct v4l2_requestbuffers *rb)
+int uvc_request_buffers(struct uvc_video_queue *queue,
+ struct v4l2_requestbuffers *rb)
{
int ret;
@@ -181,13 +249,6 @@
return ret ? ret : rb->count;
}
-void uvc_free_buffers(struct uvc_video_queue *queue)
-{
- mutex_lock(&queue->mutex);
- vb2_queue_release(&queue->queue);
- mutex_unlock(&queue->mutex);
-}
-
int uvc_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
{
int ret;
@@ -234,6 +295,28 @@
return ret;
}
+int uvc_queue_streamon(struct uvc_video_queue *queue, enum v4l2_buf_type type)
+{
+ int ret;
+
+ mutex_lock(&queue->mutex);
+ ret = vb2_streamon(&queue->queue, type);
+ mutex_unlock(&queue->mutex);
+
+ return ret;
+}
+
+int uvc_queue_streamoff(struct uvc_video_queue *queue, enum v4l2_buf_type type)
+{
+ int ret;
+
+ mutex_lock(&queue->mutex);
+ ret = vb2_streamoff(&queue->queue, type);
+ mutex_unlock(&queue->mutex);
+
+ return ret;
+}
+
int uvc_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma)
{
int ret;
@@ -289,49 +372,6 @@
}
/*
- * Enable or disable the video buffers queue.
- *
- * The queue must be enabled before starting video acquisition and must be
- * disabled after stopping it. This ensures that the video buffers queue
- * state can be properly initialized before buffers are accessed from the
- * interrupt handler.
- *
- * Enabling the video queue returns -EBUSY if the queue is already enabled.
- *
- * Disabling the video queue cancels the queue and removes all buffers from
- * the main queue.
- *
- * This function can't be called from interrupt context. Use
- * uvc_queue_cancel() instead.
- */
-int uvc_queue_enable(struct uvc_video_queue *queue, int enable)
-{
- unsigned long flags;
- int ret;
-
- mutex_lock(&queue->mutex);
- if (enable) {
- ret = vb2_streamon(&queue->queue, queue->queue.type);
- if (ret < 0)
- goto done;
-
- queue->buf_used = 0;
- } else {
- ret = vb2_streamoff(&queue->queue, queue->queue.type);
- if (ret < 0)
- goto done;
-
- spin_lock_irqsave(&queue->irqlock, flags);
- INIT_LIST_HEAD(&queue->irqqueue);
- spin_unlock_irqrestore(&queue->irqlock, flags);
- }
-
-done:
- mutex_unlock(&queue->mutex);
- return ret;
-}
-
-/*
* Cancel the video buffers queue.
*
* Cancelling the queue marks all buffers on the irq queue as erroneous,
@@ -345,17 +385,10 @@
*/
void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect)
{
- struct uvc_buffer *buf;
unsigned long flags;
spin_lock_irqsave(&queue->irqlock, flags);
- while (!list_empty(&queue->irqqueue)) {
- buf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
- queue);
- list_del(&buf->queue);
- buf->state = UVC_BUF_STATE_ERROR;
- vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR);
- }
+ uvc_queue_return_buffers(queue, UVC_BUF_STATE_ERROR);
/* This must be protected by the irqlock spinlock to avoid race
* conditions between uvc_buffer_queue and the disconnection event that
* could result in an interruptible wait in uvc_dequeue_buffer. Do not
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 60a8e2c..9c5cbcf 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -318,7 +318,6 @@
stream->ctrl = probe;
stream->cur_format = format;
stream->cur_frame = frame;
- stream->frame_size = fmt->fmt.pix.sizeimage;
done:
mutex_unlock(&stream->mutex);
@@ -531,10 +530,8 @@
uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_release\n");
/* Only free resources if this is a privileged handle. */
- if (uvc_has_privileges(handle)) {
- uvc_video_enable(stream, 0);
- uvc_free_buffers(&stream->queue);
- }
+ if (uvc_has_privileges(handle))
+ uvc_queue_release(&stream->queue);
/* Release the file handle. */
uvc_dismiss_privileges(handle);
@@ -552,553 +549,628 @@
return 0;
}
-static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+static int uvc_ioctl_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
{
struct video_device *vdev = video_devdata(file);
struct uvc_fh *handle = file->private_data;
struct uvc_video_chain *chain = handle->chain;
struct uvc_streaming *stream = handle->stream;
- long ret = 0;
- switch (cmd) {
- /* Query capabilities */
- case VIDIOC_QUERYCAP:
- {
- struct v4l2_capability *cap = arg;
+ strlcpy(cap->driver, "uvcvideo", sizeof(cap->driver));
+ strlcpy(cap->card, vdev->name, sizeof(cap->card));
+ usb_make_path(stream->dev->udev, cap->bus_info, sizeof(cap->bus_info));
+ cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING
+ | chain->caps;
+ if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ else
+ cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
- memset(cap, 0, sizeof *cap);
- strlcpy(cap->driver, "uvcvideo", sizeof cap->driver);
- strlcpy(cap->card, vdev->name, sizeof cap->card);
- usb_make_path(stream->dev->udev,
- cap->bus_info, sizeof(cap->bus_info));
- cap->version = LINUX_VERSION_CODE;
- cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING
- | chain->caps;
- if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- cap->device_caps = V4L2_CAP_VIDEO_CAPTURE
- | V4L2_CAP_STREAMING;
- else
- cap->device_caps = V4L2_CAP_VIDEO_OUTPUT
- | V4L2_CAP_STREAMING;
- break;
+ return 0;
+}
+
+static int uvc_ioctl_enum_fmt(struct uvc_streaming *stream,
+ struct v4l2_fmtdesc *fmt)
+{
+ struct uvc_format *format;
+ enum v4l2_buf_type type = fmt->type;
+ __u32 index = fmt->index;
+
+ if (fmt->type != stream->type || fmt->index >= stream->nformats)
+ return -EINVAL;
+
+ memset(fmt, 0, sizeof(*fmt));
+ fmt->index = index;
+ fmt->type = type;
+
+ format = &stream->format[fmt->index];
+ fmt->flags = 0;
+ if (format->flags & UVC_FMT_FLAG_COMPRESSED)
+ fmt->flags |= V4L2_FMT_FLAG_COMPRESSED;
+ strlcpy(fmt->description, format->name, sizeof(fmt->description));
+ fmt->description[sizeof(fmt->description) - 1] = 0;
+ fmt->pixelformat = format->fcc;
+ return 0;
+}
+
+static int uvc_ioctl_enum_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_fmtdesc *fmt)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
+
+ return uvc_ioctl_enum_fmt(stream, fmt);
+}
+
+static int uvc_ioctl_enum_fmt_vid_out(struct file *file, void *fh,
+ struct v4l2_fmtdesc *fmt)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
+
+ return uvc_ioctl_enum_fmt(stream, fmt);
+}
+
+static int uvc_ioctl_g_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *fmt)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
+
+ return uvc_v4l2_get_format(stream, fmt);
+}
+
+static int uvc_ioctl_g_fmt_vid_out(struct file *file, void *fh,
+ struct v4l2_format *fmt)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
+
+ return uvc_v4l2_get_format(stream, fmt);
+}
+
+static int uvc_ioctl_s_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *fmt)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
+ int ret;
+
+ ret = uvc_acquire_privileges(handle);
+ if (ret < 0)
+ return ret;
+
+ return uvc_v4l2_set_format(stream, fmt);
+}
+
+static int uvc_ioctl_s_fmt_vid_out(struct file *file, void *fh,
+ struct v4l2_format *fmt)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
+ int ret;
+
+ ret = uvc_acquire_privileges(handle);
+ if (ret < 0)
+ return ret;
+
+ return uvc_v4l2_set_format(stream, fmt);
+}
+
+static int uvc_ioctl_try_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *fmt)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
+ struct uvc_streaming_control probe;
+
+ return uvc_v4l2_try_format(stream, fmt, &probe, NULL, NULL);
+}
+
+static int uvc_ioctl_try_fmt_vid_out(struct file *file, void *fh,
+ struct v4l2_format *fmt)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
+ struct uvc_streaming_control probe;
+
+ return uvc_v4l2_try_format(stream, fmt, &probe, NULL, NULL);
+}
+
+static int uvc_ioctl_reqbufs(struct file *file, void *fh,
+ struct v4l2_requestbuffers *rb)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
+ int ret;
+
+ ret = uvc_acquire_privileges(handle);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&stream->mutex);
+ ret = uvc_request_buffers(&stream->queue, rb);
+ mutex_unlock(&stream->mutex);
+ if (ret < 0)
+ return ret;
+
+ if (ret == 0)
+ uvc_dismiss_privileges(handle);
+
+ return 0;
+}
+
+static int uvc_ioctl_querybuf(struct file *file, void *fh,
+ struct v4l2_buffer *buf)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
+
+ if (!uvc_has_privileges(handle))
+ return -EBUSY;
+
+ return uvc_query_buffer(&stream->queue, buf);
+}
+
+static int uvc_ioctl_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
+
+ if (!uvc_has_privileges(handle))
+ return -EBUSY;
+
+ return uvc_queue_buffer(&stream->queue, buf);
+}
+
+static int uvc_ioctl_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
+
+ if (!uvc_has_privileges(handle))
+ return -EBUSY;
+
+ return uvc_dequeue_buffer(&stream->queue, buf,
+ file->f_flags & O_NONBLOCK);
+}
+
+static int uvc_ioctl_create_bufs(struct file *file, void *fh,
+ struct v4l2_create_buffers *cb)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
+ int ret;
+
+ ret = uvc_acquire_privileges(handle);
+ if (ret < 0)
+ return ret;
+
+ return uvc_create_buffers(&stream->queue, cb);
+}
+
+static int uvc_ioctl_streamon(struct file *file, void *fh,
+ enum v4l2_buf_type type)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
+ int ret;
+
+ if (!uvc_has_privileges(handle))
+ return -EBUSY;
+
+ mutex_lock(&stream->mutex);
+ ret = uvc_queue_streamon(&stream->queue, type);
+ mutex_unlock(&stream->mutex);
+
+ return ret;
+}
+
+static int uvc_ioctl_streamoff(struct file *file, void *fh,
+ enum v4l2_buf_type type)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
+
+ if (!uvc_has_privileges(handle))
+ return -EBUSY;
+
+ mutex_lock(&stream->mutex);
+ uvc_queue_streamoff(&stream->queue, type);
+ mutex_unlock(&stream->mutex);
+
+ return 0;
+}
+
+static int uvc_ioctl_enum_input(struct file *file, void *fh,
+ struct v4l2_input *input)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_video_chain *chain = handle->chain;
+ const struct uvc_entity *selector = chain->selector;
+ struct uvc_entity *iterm = NULL;
+ u32 index = input->index;
+ int pin = 0;
+
+ if (selector == NULL ||
+ (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
+ if (index != 0)
+ return -EINVAL;
+ list_for_each_entry(iterm, &chain->entities, chain) {
+ if (UVC_ENTITY_IS_ITERM(iterm))
+ break;
+ }
+ pin = iterm->id;
+ } else if (index < selector->bNrInPins) {
+ pin = selector->baSourceID[index];
+ list_for_each_entry(iterm, &chain->entities, chain) {
+ if (!UVC_ENTITY_IS_ITERM(iterm))
+ continue;
+ if (iterm->id == pin)
+ break;
+ }
}
- /* Priority */
- case VIDIOC_G_PRIORITY:
- *(u32 *)arg = v4l2_prio_max(vdev->prio);
- break;
+ if (iterm == NULL || iterm->id != pin)
+ return -EINVAL;
- case VIDIOC_S_PRIORITY:
- ret = v4l2_prio_check(vdev->prio, handle->vfh.prio);
- if (ret < 0)
- return ret;
+ memset(input, 0, sizeof(*input));
+ input->index = index;
+ strlcpy(input->name, iterm->name, sizeof(input->name));
+ if (UVC_ENTITY_TYPE(iterm) == UVC_ITT_CAMERA)
+ input->type = V4L2_INPUT_TYPE_CAMERA;
- return v4l2_prio_change(vdev->prio, &handle->vfh.prio,
- *(u32 *)arg);
+ return 0;
+}
- /* Get, Set & Query control */
- case VIDIOC_QUERYCTRL:
- return uvc_query_v4l2_ctrl(chain, arg);
+static int uvc_ioctl_g_input(struct file *file, void *fh, unsigned int *input)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_video_chain *chain = handle->chain;
+ int ret;
+ u8 i;
- case VIDIOC_G_CTRL:
- {
- struct v4l2_control *ctrl = arg;
- struct v4l2_ext_control xctrl;
+ if (chain->selector == NULL ||
+ (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
+ *input = 0;
+ return 0;
+ }
- memset(&xctrl, 0, sizeof xctrl);
- xctrl.id = ctrl->id;
+ ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, chain->selector->id,
+ chain->dev->intfnum, UVC_SU_INPUT_SELECT_CONTROL,
+ &i, 1);
+ if (ret < 0)
+ return ret;
- ret = uvc_ctrl_begin(chain);
- if (ret < 0)
- return ret;
+ *input = i - 1;
+ return 0;
+}
- ret = uvc_ctrl_get(chain, &xctrl);
+static int uvc_ioctl_s_input(struct file *file, void *fh, unsigned int input)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_video_chain *chain = handle->chain;
+ int ret;
+ u32 i;
+
+ ret = uvc_acquire_privileges(handle);
+ if (ret < 0)
+ return ret;
+
+ if (chain->selector == NULL ||
+ (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
+ if (input)
+ return -EINVAL;
+ return 0;
+ }
+
+ if (input >= chain->selector->bNrInPins)
+ return -EINVAL;
+
+ i = input + 1;
+ return uvc_query_ctrl(chain->dev, UVC_SET_CUR, chain->selector->id,
+ chain->dev->intfnum, UVC_SU_INPUT_SELECT_CONTROL,
+ &i, 1);
+}
+
+static int uvc_ioctl_queryctrl(struct file *file, void *fh,
+ struct v4l2_queryctrl *qc)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_video_chain *chain = handle->chain;
+
+ return uvc_query_v4l2_ctrl(chain, qc);
+}
+
+static int uvc_ioctl_g_ctrl(struct file *file, void *fh,
+ struct v4l2_control *ctrl)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_video_chain *chain = handle->chain;
+ struct v4l2_ext_control xctrl;
+ int ret;
+
+ memset(&xctrl, 0, sizeof(xctrl));
+ xctrl.id = ctrl->id;
+
+ ret = uvc_ctrl_begin(chain);
+ if (ret < 0)
+ return ret;
+
+ ret = uvc_ctrl_get(chain, &xctrl);
+ uvc_ctrl_rollback(handle);
+ if (ret < 0)
+ return ret;
+
+ ctrl->value = xctrl.value;
+ return 0;
+}
+
+static int uvc_ioctl_s_ctrl(struct file *file, void *fh,
+ struct v4l2_control *ctrl)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_video_chain *chain = handle->chain;
+ struct v4l2_ext_control xctrl;
+ int ret;
+
+ memset(&xctrl, 0, sizeof(xctrl));
+ xctrl.id = ctrl->id;
+ xctrl.value = ctrl->value;
+
+ ret = uvc_ctrl_begin(chain);
+ if (ret < 0)
+ return ret;
+
+ ret = uvc_ctrl_set(chain, &xctrl);
+ if (ret < 0) {
uvc_ctrl_rollback(handle);
- if (ret >= 0)
- ctrl->value = xctrl.value;
- break;
+ return ret;
}
- case VIDIOC_S_CTRL:
- {
- struct v4l2_control *ctrl = arg;
- struct v4l2_ext_control xctrl;
+ ret = uvc_ctrl_commit(handle, &xctrl, 1);
+ if (ret < 0)
+ return ret;
- ret = v4l2_prio_check(vdev->prio, handle->vfh.prio);
- if (ret < 0)
- return ret;
+ ctrl->value = xctrl.value;
+ return 0;
+}
- memset(&xctrl, 0, sizeof xctrl);
- xctrl.id = ctrl->id;
- xctrl.value = ctrl->value;
+static int uvc_ioctl_g_ext_ctrls(struct file *file, void *fh,
+ struct v4l2_ext_controls *ctrls)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_video_chain *chain = handle->chain;
+ struct v4l2_ext_control *ctrl = ctrls->controls;
+ unsigned int i;
+ int ret;
- ret = uvc_ctrl_begin(chain);
- if (ret < 0)
- return ret;
+ ret = uvc_ctrl_begin(chain);
+ if (ret < 0)
+ return ret;
- ret = uvc_ctrl_set(chain, &xctrl);
+ for (i = 0; i < ctrls->count; ++ctrl, ++i) {
+ ret = uvc_ctrl_get(chain, ctrl);
if (ret < 0) {
uvc_ctrl_rollback(handle);
+ ctrls->error_idx = i;
return ret;
}
- ret = uvc_ctrl_commit(handle, &xctrl, 1);
- if (ret == 0)
- ctrl->value = xctrl.value;
- break;
}
- case VIDIOC_QUERYMENU:
- return uvc_query_v4l2_menu(chain, arg);
+ ctrls->error_idx = 0;
- case VIDIOC_G_EXT_CTRLS:
- {
- struct v4l2_ext_controls *ctrls = arg;
- struct v4l2_ext_control *ctrl = ctrls->controls;
- unsigned int i;
+ return uvc_ctrl_rollback(handle);
+}
- ret = uvc_ctrl_begin(chain);
- if (ret < 0)
+static int uvc_ioctl_s_try_ext_ctrls(struct uvc_fh *handle,
+ struct v4l2_ext_controls *ctrls,
+ bool commit)
+{
+ struct v4l2_ext_control *ctrl = ctrls->controls;
+ struct uvc_video_chain *chain = handle->chain;
+ unsigned int i;
+ int ret;
+
+ ret = uvc_ctrl_begin(chain);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ctrls->count; ++ctrl, ++i) {
+ ret = uvc_ctrl_set(chain, ctrl);
+ if (ret < 0) {
+ uvc_ctrl_rollback(handle);
+ ctrls->error_idx = commit ? ctrls->count : i;
return ret;
-
- for (i = 0; i < ctrls->count; ++ctrl, ++i) {
- ret = uvc_ctrl_get(chain, ctrl);
- if (ret < 0) {
- uvc_ctrl_rollback(handle);
- ctrls->error_idx = i;
- return ret;
- }
}
- ctrls->error_idx = 0;
- ret = uvc_ctrl_rollback(handle);
- break;
}
- case VIDIOC_S_EXT_CTRLS:
- ret = v4l2_prio_check(vdev->prio, handle->vfh.prio);
- if (ret < 0)
- return ret;
- /* Fall through */
- case VIDIOC_TRY_EXT_CTRLS:
- {
- struct v4l2_ext_controls *ctrls = arg;
- struct v4l2_ext_control *ctrl = ctrls->controls;
- unsigned int i;
+ ctrls->error_idx = 0;
- ret = uvc_ctrl_begin(chain);
- if (ret < 0)
- return ret;
+ if (commit)
+ return uvc_ctrl_commit(handle, ctrls->controls, ctrls->count);
+ else
+ return uvc_ctrl_rollback(handle);
+}
- for (i = 0; i < ctrls->count; ++ctrl, ++i) {
- ret = uvc_ctrl_set(chain, ctrl);
- if (ret < 0) {
- uvc_ctrl_rollback(handle);
- ctrls->error_idx = cmd == VIDIOC_S_EXT_CTRLS
- ? ctrls->count : i;
- return ret;
- }
- }
+static int uvc_ioctl_s_ext_ctrls(struct file *file, void *fh,
+ struct v4l2_ext_controls *ctrls)
+{
+ struct uvc_fh *handle = fh;
- ctrls->error_idx = 0;
+ return uvc_ioctl_s_try_ext_ctrls(handle, ctrls, true);
+}
- if (cmd == VIDIOC_S_EXT_CTRLS)
- ret = uvc_ctrl_commit(handle,
- ctrls->controls, ctrls->count);
- else
- ret = uvc_ctrl_rollback(handle);
- break;
- }
+static int uvc_ioctl_try_ext_ctrls(struct file *file, void *fh,
+ struct v4l2_ext_controls *ctrls)
+{
+ struct uvc_fh *handle = fh;
- /* Get, Set & Enum input */
- case VIDIOC_ENUMINPUT:
- {
- const struct uvc_entity *selector = chain->selector;
- struct v4l2_input *input = arg;
- struct uvc_entity *iterm = NULL;
- u32 index = input->index;
- int pin = 0;
+ return uvc_ioctl_s_try_ext_ctrls(handle, ctrls, false);
+}
- if (selector == NULL ||
- (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
- if (index != 0)
- return -EINVAL;
- list_for_each_entry(iterm, &chain->entities, chain) {
- if (UVC_ENTITY_IS_ITERM(iterm))
- break;
- }
- pin = iterm->id;
- } else if (index < selector->bNrInPins) {
- pin = selector->baSourceID[index];
- list_for_each_entry(iterm, &chain->entities, chain) {
- if (!UVC_ENTITY_IS_ITERM(iterm))
- continue;
- if (iterm->id == pin)
- break;
- }
- }
+static int uvc_ioctl_querymenu(struct file *file, void *fh,
+ struct v4l2_querymenu *qm)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_video_chain *chain = handle->chain;
- if (iterm == NULL || iterm->id != pin)
- return -EINVAL;
+ return uvc_query_v4l2_menu(chain, qm);
+}
- memset(input, 0, sizeof *input);
- input->index = index;
- strlcpy(input->name, iterm->name, sizeof input->name);
- if (UVC_ENTITY_TYPE(iterm) == UVC_ITT_CAMERA)
- input->type = V4L2_INPUT_TYPE_CAMERA;
- break;
- }
+static int uvc_ioctl_cropcap(struct file *file, void *fh,
+ struct v4l2_cropcap *ccap)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
- case VIDIOC_G_INPUT:
- {
- u8 input;
+ if (ccap->type != stream->type)
+ return -EINVAL;
- if (chain->selector == NULL ||
- (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
- *(int *)arg = 0;
+ ccap->bounds.left = 0;
+ ccap->bounds.top = 0;
+ mutex_lock(&stream->mutex);
+ ccap->bounds.width = stream->cur_frame->wWidth;
+ ccap->bounds.height = stream->cur_frame->wHeight;
+ mutex_unlock(&stream->mutex);
+
+ ccap->defrect = ccap->bounds;
+
+ ccap->pixelaspect.numerator = 1;
+ ccap->pixelaspect.denominator = 1;
+ return 0;
+}
+
+static int uvc_ioctl_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *parm)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
+
+ return uvc_v4l2_get_streamparm(stream, parm);
+}
+
+static int uvc_ioctl_s_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *parm)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
+ int ret;
+
+ ret = uvc_acquire_privileges(handle);
+ if (ret < 0)
+ return ret;
+
+ return uvc_v4l2_set_streamparm(stream, parm);
+}
+
+static int uvc_ioctl_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
+ struct uvc_format *format = NULL;
+ struct uvc_frame *frame;
+ int i;
+
+ /* Look for the given pixel format */
+ for (i = 0; i < stream->nformats; i++) {
+ if (stream->format[i].fcc == fsize->pixel_format) {
+ format = &stream->format[i];
break;
}
-
- ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
- chain->selector->id, chain->dev->intfnum,
- UVC_SU_INPUT_SELECT_CONTROL, &input, 1);
- if (ret < 0)
- return ret;
-
- *(int *)arg = input - 1;
- break;
}
+ if (format == NULL)
+ return -EINVAL;
- case VIDIOC_S_INPUT:
- {
- u32 input = *(u32 *)arg + 1;
+ if (fsize->index >= format->nframes)
+ return -EINVAL;
- ret = v4l2_prio_check(vdev->prio, handle->vfh.prio);
- if (ret < 0)
- return ret;
+ frame = &format->frame[fsize->index];
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = frame->wWidth;
+ fsize->discrete.height = frame->wHeight;
+ return 0;
+}
- if ((ret = uvc_acquire_privileges(handle)) < 0)
- return ret;
+static int uvc_ioctl_enum_frameintervals(struct file *file, void *fh,
+ struct v4l2_frmivalenum *fival)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
+ struct uvc_format *format = NULL;
+ struct uvc_frame *frame = NULL;
+ int i;
- if (chain->selector == NULL ||
- (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
- if (input != 1)
- return -EINVAL;
+ /* Look for the given pixel format and frame size */
+ for (i = 0; i < stream->nformats; i++) {
+ if (stream->format[i].fcc == fival->pixel_format) {
+ format = &stream->format[i];
break;
}
-
- if (input == 0 || input > chain->selector->bNrInPins)
- return -EINVAL;
-
- return uvc_query_ctrl(chain->dev, UVC_SET_CUR,
- chain->selector->id, chain->dev->intfnum,
- UVC_SU_INPUT_SELECT_CONTROL, &input, 1);
}
+ if (format == NULL)
+ return -EINVAL;
- /* Try, Get, Set & Enum format */
- case VIDIOC_ENUM_FMT:
- {
- struct v4l2_fmtdesc *fmt = arg;
- struct uvc_format *format;
- enum v4l2_buf_type type = fmt->type;
- __u32 index = fmt->index;
-
- if (fmt->type != stream->type ||
- fmt->index >= stream->nformats)
- return -EINVAL;
-
- memset(fmt, 0, sizeof(*fmt));
- fmt->index = index;
- fmt->type = type;
-
- format = &stream->format[fmt->index];
- fmt->flags = 0;
- if (format->flags & UVC_FMT_FLAG_COMPRESSED)
- fmt->flags |= V4L2_FMT_FLAG_COMPRESSED;
- strlcpy(fmt->description, format->name,
- sizeof fmt->description);
- fmt->description[sizeof fmt->description - 1] = 0;
- fmt->pixelformat = format->fcc;
- break;
- }
-
- case VIDIOC_TRY_FMT:
- {
- struct uvc_streaming_control probe;
-
- return uvc_v4l2_try_format(stream, arg, &probe, NULL, NULL);
- }
-
- case VIDIOC_S_FMT:
- ret = v4l2_prio_check(vdev->prio, handle->vfh.prio);
- if (ret < 0)
- return ret;
-
- if ((ret = uvc_acquire_privileges(handle)) < 0)
- return ret;
-
- return uvc_v4l2_set_format(stream, arg);
-
- case VIDIOC_G_FMT:
- return uvc_v4l2_get_format(stream, arg);
-
- /* Frame size enumeration */
- case VIDIOC_ENUM_FRAMESIZES:
- {
- struct v4l2_frmsizeenum *fsize = arg;
- struct uvc_format *format = NULL;
- struct uvc_frame *frame;
- int i;
-
- /* Look for the given pixel format */
- for (i = 0; i < stream->nformats; i++) {
- if (stream->format[i].fcc ==
- fsize->pixel_format) {
- format = &stream->format[i];
- break;
- }
- }
- if (format == NULL)
- return -EINVAL;
-
- if (fsize->index >= format->nframes)
- return -EINVAL;
-
- frame = &format->frame[fsize->index];
- fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
- fsize->discrete.width = frame->wWidth;
- fsize->discrete.height = frame->wHeight;
- break;
- }
-
- /* Frame interval enumeration */
- case VIDIOC_ENUM_FRAMEINTERVALS:
- {
- struct v4l2_frmivalenum *fival = arg;
- struct uvc_format *format = NULL;
- struct uvc_frame *frame = NULL;
- int i;
-
- /* Look for the given pixel format and frame size */
- for (i = 0; i < stream->nformats; i++) {
- if (stream->format[i].fcc ==
- fival->pixel_format) {
- format = &stream->format[i];
- break;
- }
- }
- if (format == NULL)
- return -EINVAL;
-
- for (i = 0; i < format->nframes; i++) {
- if (format->frame[i].wWidth == fival->width &&
- format->frame[i].wHeight == fival->height) {
- frame = &format->frame[i];
- break;
- }
- }
- if (frame == NULL)
- return -EINVAL;
-
- if (frame->bFrameIntervalType) {
- if (fival->index >= frame->bFrameIntervalType)
- return -EINVAL;
-
- fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
- fival->discrete.numerator =
- frame->dwFrameInterval[fival->index];
- fival->discrete.denominator = 10000000;
- uvc_simplify_fraction(&fival->discrete.numerator,
- &fival->discrete.denominator, 8, 333);
- } else {
- fival->type = V4L2_FRMIVAL_TYPE_STEPWISE;
- fival->stepwise.min.numerator =
- frame->dwFrameInterval[0];
- fival->stepwise.min.denominator = 10000000;
- fival->stepwise.max.numerator =
- frame->dwFrameInterval[1];
- fival->stepwise.max.denominator = 10000000;
- fival->stepwise.step.numerator =
- frame->dwFrameInterval[2];
- fival->stepwise.step.denominator = 10000000;
- uvc_simplify_fraction(&fival->stepwise.min.numerator,
- &fival->stepwise.min.denominator, 8, 333);
- uvc_simplify_fraction(&fival->stepwise.max.numerator,
- &fival->stepwise.max.denominator, 8, 333);
- uvc_simplify_fraction(&fival->stepwise.step.numerator,
- &fival->stepwise.step.denominator, 8, 333);
- }
- break;
- }
-
- /* Get & Set streaming parameters */
- case VIDIOC_G_PARM:
- return uvc_v4l2_get_streamparm(stream, arg);
-
- case VIDIOC_S_PARM:
- ret = v4l2_prio_check(vdev->prio, handle->vfh.prio);
- if (ret < 0)
- return ret;
-
- if ((ret = uvc_acquire_privileges(handle)) < 0)
- return ret;
-
- return uvc_v4l2_set_streamparm(stream, arg);
-
- /* Cropping and scaling */
- case VIDIOC_CROPCAP:
- {
- struct v4l2_cropcap *ccap = arg;
-
- if (ccap->type != stream->type)
- return -EINVAL;
-
- ccap->bounds.left = 0;
- ccap->bounds.top = 0;
-
- mutex_lock(&stream->mutex);
- ccap->bounds.width = stream->cur_frame->wWidth;
- ccap->bounds.height = stream->cur_frame->wHeight;
- mutex_unlock(&stream->mutex);
-
- ccap->defrect = ccap->bounds;
-
- ccap->pixelaspect.numerator = 1;
- ccap->pixelaspect.denominator = 1;
- break;
- }
-
- case VIDIOC_G_CROP:
- case VIDIOC_S_CROP:
- return -ENOTTY;
-
- /* Buffers & streaming */
- case VIDIOC_REQBUFS:
- ret = v4l2_prio_check(vdev->prio, handle->vfh.prio);
- if (ret < 0)
- return ret;
-
- if ((ret = uvc_acquire_privileges(handle)) < 0)
- return ret;
-
- mutex_lock(&stream->mutex);
- ret = uvc_alloc_buffers(&stream->queue, arg);
- mutex_unlock(&stream->mutex);
- if (ret < 0)
- return ret;
-
- if (ret == 0)
- uvc_dismiss_privileges(handle);
-
- ret = 0;
- break;
-
- case VIDIOC_QUERYBUF:
- {
- struct v4l2_buffer *buf = arg;
-
- if (!uvc_has_privileges(handle))
- return -EBUSY;
-
- return uvc_query_buffer(&stream->queue, buf);
- }
-
- case VIDIOC_CREATE_BUFS:
- {
- struct v4l2_create_buffers *cb = arg;
-
- ret = uvc_acquire_privileges(handle);
- if (ret < 0)
- return ret;
-
- return uvc_create_buffers(&stream->queue, cb);
- }
-
- case VIDIOC_QBUF:
- if (!uvc_has_privileges(handle))
- return -EBUSY;
-
- return uvc_queue_buffer(&stream->queue, arg);
-
- case VIDIOC_DQBUF:
- if (!uvc_has_privileges(handle))
- return -EBUSY;
-
- return uvc_dequeue_buffer(&stream->queue, arg,
- file->f_flags & O_NONBLOCK);
-
- case VIDIOC_STREAMON:
- {
- int *type = arg;
-
- if (*type != stream->type)
- return -EINVAL;
-
- ret = v4l2_prio_check(vdev->prio, handle->vfh.prio);
- if (ret < 0)
- return ret;
-
- if (!uvc_has_privileges(handle))
- return -EBUSY;
-
- mutex_lock(&stream->mutex);
- ret = uvc_video_enable(stream, 1);
- mutex_unlock(&stream->mutex);
- if (ret < 0)
- return ret;
- break;
- }
-
- case VIDIOC_STREAMOFF:
- {
- int *type = arg;
-
- if (*type != stream->type)
- return -EINVAL;
-
- ret = v4l2_prio_check(vdev->prio, handle->vfh.prio);
- if (ret < 0)
- return ret;
-
- if (!uvc_has_privileges(handle))
- return -EBUSY;
-
- return uvc_video_enable(stream, 0);
- }
-
- case VIDIOC_SUBSCRIBE_EVENT:
- {
- struct v4l2_event_subscription *sub = arg;
-
- switch (sub->type) {
- case V4L2_EVENT_CTRL:
- return v4l2_event_subscribe(&handle->vfh, sub, 0,
- &uvc_ctrl_sub_ev_ops);
- default:
- return -EINVAL;
+ for (i = 0; i < format->nframes; i++) {
+ if (format->frame[i].wWidth == fival->width &&
+ format->frame[i].wHeight == fival->height) {
+ frame = &format->frame[i];
+ break;
}
}
+ if (frame == NULL)
+ return -EINVAL;
- case VIDIOC_UNSUBSCRIBE_EVENT:
- return v4l2_event_unsubscribe(&handle->vfh, arg);
+ if (frame->bFrameIntervalType) {
+ if (fival->index >= frame->bFrameIntervalType)
+ return -EINVAL;
- case VIDIOC_DQEVENT:
- return v4l2_event_dequeue(&handle->vfh, arg,
- file->f_flags & O_NONBLOCK);
+ fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+ fival->discrete.numerator =
+ frame->dwFrameInterval[fival->index];
+ fival->discrete.denominator = 10000000;
+ uvc_simplify_fraction(&fival->discrete.numerator,
+ &fival->discrete.denominator, 8, 333);
+ } else {
+ fival->type = V4L2_FRMIVAL_TYPE_STEPWISE;
+ fival->stepwise.min.numerator = frame->dwFrameInterval[0];
+ fival->stepwise.min.denominator = 10000000;
+ fival->stepwise.max.numerator = frame->dwFrameInterval[1];
+ fival->stepwise.max.denominator = 10000000;
+ fival->stepwise.step.numerator = frame->dwFrameInterval[2];
+ fival->stepwise.step.denominator = 10000000;
+ uvc_simplify_fraction(&fival->stepwise.min.numerator,
+ &fival->stepwise.min.denominator, 8, 333);
+ uvc_simplify_fraction(&fival->stepwise.max.numerator,
+ &fival->stepwise.max.denominator, 8, 333);
+ uvc_simplify_fraction(&fival->stepwise.step.numerator,
+ &fival->stepwise.step.denominator, 8, 333);
+ }
- /* Analog video standards make no sense for digital cameras. */
- case VIDIOC_ENUMSTD:
- case VIDIOC_QUERYSTD:
- case VIDIOC_G_STD:
- case VIDIOC_S_STD:
+ return 0;
+}
- case VIDIOC_OVERLAY:
+static int uvc_ioctl_subscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ switch (sub->type) {
+ case V4L2_EVENT_CTRL:
+ return v4l2_event_subscribe(fh, sub, 0, &uvc_ctrl_sub_ev_ops);
+ default:
+ return -EINVAL;
+ }
+}
- case VIDIOC_ENUMAUDIO:
- case VIDIOC_ENUMAUDOUT:
+static long uvc_ioctl_default(struct file *file, void *fh, bool valid_prio,
+ unsigned int cmd, void *arg)
+{
+ struct uvc_fh *handle = fh;
+ struct uvc_video_chain *chain = handle->chain;
- case VIDIOC_ENUMOUTPUT:
- uvc_trace(UVC_TRACE_IOCTL, "Unsupported ioctl 0x%08x\n", cmd);
- return -ENOTTY;
-
+ switch (cmd) {
+ /* Dynamic controls. */
case UVCIOC_CTRL_MAP:
return uvc_ioctl_ctrl_map(chain, arg);
@@ -1106,23 +1178,8 @@
return uvc_xu_ctrl_query(chain, arg);
default:
- uvc_trace(UVC_TRACE_IOCTL, "Unknown ioctl 0x%08x\n", cmd);
return -ENOTTY;
}
-
- return ret;
-}
-
-static long uvc_v4l2_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- if (uvc_trace_param & UVC_TRACE_IOCTL) {
- uvc_printk(KERN_DEBUG, "uvc_v4l2_ioctl(");
- v4l_printk_ioctl(NULL, cmd);
- printk(")\n");
- }
-
- return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl);
}
#ifdef CONFIG_COMPAT
@@ -1305,7 +1362,7 @@
old_fs = get_fs();
set_fs(KERNEL_DS);
- ret = uvc_v4l2_ioctl(file, cmd, (unsigned long)&karg);
+ ret = video_ioctl2(file, cmd, (unsigned long)&karg);
set_fs(old_fs);
if (ret < 0)
@@ -1366,11 +1423,48 @@
}
#endif
+const struct v4l2_ioctl_ops uvc_ioctl_ops = {
+ .vidioc_querycap = uvc_ioctl_querycap,
+ .vidioc_enum_fmt_vid_cap = uvc_ioctl_enum_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_out = uvc_ioctl_enum_fmt_vid_out,
+ .vidioc_g_fmt_vid_cap = uvc_ioctl_g_fmt_vid_cap,
+ .vidioc_g_fmt_vid_out = uvc_ioctl_g_fmt_vid_out,
+ .vidioc_s_fmt_vid_cap = uvc_ioctl_s_fmt_vid_cap,
+ .vidioc_s_fmt_vid_out = uvc_ioctl_s_fmt_vid_out,
+ .vidioc_try_fmt_vid_cap = uvc_ioctl_try_fmt_vid_cap,
+ .vidioc_try_fmt_vid_out = uvc_ioctl_try_fmt_vid_out,
+ .vidioc_reqbufs = uvc_ioctl_reqbufs,
+ .vidioc_querybuf = uvc_ioctl_querybuf,
+ .vidioc_qbuf = uvc_ioctl_qbuf,
+ .vidioc_dqbuf = uvc_ioctl_dqbuf,
+ .vidioc_create_bufs = uvc_ioctl_create_bufs,
+ .vidioc_streamon = uvc_ioctl_streamon,
+ .vidioc_streamoff = uvc_ioctl_streamoff,
+ .vidioc_enum_input = uvc_ioctl_enum_input,
+ .vidioc_g_input = uvc_ioctl_g_input,
+ .vidioc_s_input = uvc_ioctl_s_input,
+ .vidioc_queryctrl = uvc_ioctl_queryctrl,
+ .vidioc_g_ctrl = uvc_ioctl_g_ctrl,
+ .vidioc_s_ctrl = uvc_ioctl_s_ctrl,
+ .vidioc_g_ext_ctrls = uvc_ioctl_g_ext_ctrls,
+ .vidioc_s_ext_ctrls = uvc_ioctl_s_ext_ctrls,
+ .vidioc_try_ext_ctrls = uvc_ioctl_try_ext_ctrls,
+ .vidioc_querymenu = uvc_ioctl_querymenu,
+ .vidioc_cropcap = uvc_ioctl_cropcap,
+ .vidioc_g_parm = uvc_ioctl_g_parm,
+ .vidioc_s_parm = uvc_ioctl_s_parm,
+ .vidioc_enum_framesizes = uvc_ioctl_enum_framesizes,
+ .vidioc_enum_frameintervals = uvc_ioctl_enum_frameintervals,
+ .vidioc_subscribe_event = uvc_ioctl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+ .vidioc_default = uvc_ioctl_default,
+};
+
const struct v4l2_file_operations uvc_fops = {
.owner = THIS_MODULE,
.open = uvc_v4l2_open,
.release = uvc_v4l2_release,
- .unlocked_ioctl = uvc_v4l2_ioctl,
+ .unlocked_ioctl = video_ioctl2,
#ifdef CONFIG_COMPAT
.compat_ioctl32 = uvc_v4l2_compat_ioctl32,
#endif
diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index 9ace520..9637e8b 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -1021,6 +1021,7 @@
uvc_video_get_ts(&ts);
+ buf->buf.v4l2_buf.field = V4L2_FIELD_NONE;
buf->buf.v4l2_buf.sequence = stream->sequence;
buf->buf.v4l2_buf.timestamp.tv_sec = ts.tv_sec;
buf->buf.v4l2_buf.timestamp.tv_usec =
@@ -1143,7 +1144,7 @@
static void uvc_video_validate_buffer(const struct uvc_streaming *stream,
struct uvc_buffer *buf)
{
- if (stream->frame_size != buf->bytesused &&
+ if (stream->ctrl.dwMaxVideoFrameSize != buf->bytesused &&
!(stream->cur_format->flags & UVC_FMT_FLAG_COMPRESSED))
buf->error = 1;
}
@@ -1734,19 +1735,13 @@
uvc_video_clock_reset(stream);
ret = uvc_commit_video(stream, &stream->ctrl);
- if (ret < 0) {
- uvc_queue_enable(&stream->queue, 0);
+ if (ret < 0)
return ret;
- }
if (!uvc_queue_streaming(&stream->queue))
return 0;
- ret = uvc_init_video(stream, GFP_NOIO);
- if (ret < 0)
- uvc_queue_enable(&stream->queue, 0);
-
- return ret;
+ return uvc_init_video(stream, GFP_NOIO);
}
/* ------------------------------------------------------------------------
@@ -1778,11 +1773,6 @@
atomic_set(&stream->active, 0);
- /* Initialize the video buffers queue. */
- ret = uvc_queue_init(&stream->queue, stream->type, !uvc_no_drop_param);
- if (ret)
- return ret;
-
/* Alternate setting 0 should be the default, yet the XBox Live Vision
* Cam (and possibly other devices) crash or otherwise misbehave if
* they don't receive a SET_INTERFACE request before any other video
@@ -1889,7 +1879,6 @@
usb_clear_halt(stream->dev->udev, pipe);
}
- uvc_queue_enable(&stream->queue, 0);
uvc_video_clock_cleanup(stream);
return 0;
}
@@ -1898,10 +1887,6 @@
if (ret < 0)
return ret;
- ret = uvc_queue_enable(&stream->queue, 1);
- if (ret < 0)
- goto error_queue;
-
/* Commit the streaming parameters. */
ret = uvc_commit_video(stream, &stream->ctrl);
if (ret < 0)
@@ -1916,8 +1901,6 @@
error_video:
usb_set_interface(stream->dev->udev, stream->intfnum, 0);
error_commit:
- uvc_queue_enable(&stream->queue, 0);
-error_queue:
uvc_video_clock_cleanup(stream);
return ret;
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 11b6b61..1683a9c 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -148,6 +148,7 @@
#define UVC_QUIRK_PROBE_DEF 0x00000100
#define UVC_QUIRK_RESTRICT_FRAME_RATE 0x00000200
#define UVC_QUIRK_RESTORE_CTRLS_ON_INIT 0x00000400
+#define UVC_QUIRK_FORCE_Y8 0x00000800
/* Format flags */
#define UVC_FMT_FLAG_COMPRESSED 0x00000001
@@ -457,7 +458,6 @@
struct uvc_format *def_format;
struct uvc_format *cur_format;
struct uvc_frame *cur_frame;
- size_t frame_size;
/* Protect access to ctrl, cur_format, cur_frame and hardware video
* probe control.
@@ -580,7 +580,6 @@
#define UVC_TRACE_FORMAT (1 << 3)
#define UVC_TRACE_CAPTURE (1 << 4)
#define UVC_TRACE_CALLS (1 << 5)
-#define UVC_TRACE_IOCTL (1 << 6)
#define UVC_TRACE_FRAME (1 << 7)
#define UVC_TRACE_SUSPEND (1 << 8)
#define UVC_TRACE_STATUS (1 << 9)
@@ -624,9 +623,9 @@
/* Video buffers queue management. */
extern int uvc_queue_init(struct uvc_video_queue *queue,
enum v4l2_buf_type type, int drop_corrupted);
-extern int uvc_alloc_buffers(struct uvc_video_queue *queue,
+extern void uvc_queue_release(struct uvc_video_queue *queue);
+extern int uvc_request_buffers(struct uvc_video_queue *queue,
struct v4l2_requestbuffers *rb);
-extern void uvc_free_buffers(struct uvc_video_queue *queue);
extern int uvc_query_buffer(struct uvc_video_queue *queue,
struct v4l2_buffer *v4l2_buf);
extern int uvc_create_buffers(struct uvc_video_queue *queue,
@@ -635,7 +634,10 @@
struct v4l2_buffer *v4l2_buf);
extern int uvc_dequeue_buffer(struct uvc_video_queue *queue,
struct v4l2_buffer *v4l2_buf, int nonblocking);
-extern int uvc_queue_enable(struct uvc_video_queue *queue, int enable);
+extern int uvc_queue_streamon(struct uvc_video_queue *queue,
+ enum v4l2_buf_type type);
+extern int uvc_queue_streamoff(struct uvc_video_queue *queue,
+ enum v4l2_buf_type type);
extern void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect);
extern struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
struct uvc_buffer *buf);
@@ -654,6 +656,7 @@
}
/* V4L2 interface */
+extern const struct v4l2_ioctl_ops uvc_ioctl_ops;
extern const struct v4l2_file_operations uvc_fops;
/* Media controller */
diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
index 52d7b51..2276750 100644
--- a/drivers/media/v4l2-core/Kconfig
+++ b/drivers/media/v4l2-core/Kconfig
@@ -77,7 +77,7 @@
# Used by drivers that need Videobuf2 modules
config VIDEOBUF2_CORE
- select DMA_SHARED_BUFFER if !BACKPORT_KERNEL_3_5
+ select DMA_SHARED_BUFFER if !KERNEL_3_5
tristate
depends on m
@@ -91,14 +91,14 @@
depends on HAS_DMA
select VIDEOBUF2_CORE
select VIDEOBUF2_MEMOPS
- select DMA_SHARED_BUFFER if !BACKPORT_KERNEL_3_5
+ select DMA_SHARED_BUFFER if !KERNEL_3_5
config VIDEOBUF2_VMALLOC
tristate
depends on m
select VIDEOBUF2_CORE
select VIDEOBUF2_MEMOPS
- select DMA_SHARED_BUFFER if !BACKPORT_KERNEL_3_5
+ select DMA_SHARED_BUFFER if !KERNEL_3_5
config VIDEOBUF2_DMA_SG
tristate
diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
index 46a938b..0d6c1ec 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -80,36 +80,6 @@
/* Helper functions for control handling */
-/* Check for correctness of the ctrl's value based on the data from
- struct v4l2_queryctrl and the available menu items. Note that
- menu_items may be NULL, in that case it is ignored. */
-int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl,
- const char * const *menu_items)
-{
- if (qctrl->flags & V4L2_CTRL_FLAG_DISABLED)
- return -EINVAL;
- if (qctrl->flags & V4L2_CTRL_FLAG_GRABBED)
- return -EBUSY;
- if (qctrl->type == V4L2_CTRL_TYPE_STRING)
- return 0;
- if (qctrl->type == V4L2_CTRL_TYPE_BUTTON ||
- qctrl->type == V4L2_CTRL_TYPE_INTEGER64 ||
- qctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS)
- return 0;
- if (ctrl->value < qctrl->minimum || ctrl->value > qctrl->maximum)
- return -ERANGE;
- if (qctrl->type == V4L2_CTRL_TYPE_MENU && menu_items != NULL) {
- if (menu_items[ctrl->value] == NULL ||
- menu_items[ctrl->value][0] == '\0')
- return -EINVAL;
- }
- if (qctrl->type == V4L2_CTRL_TYPE_BITMASK &&
- (ctrl->value & ~qctrl->maximum))
- return -ERANGE;
- return 0;
-}
-EXPORT_SYMBOL(v4l2_ctrl_check);
-
/* Fill in a struct v4l2_queryctrl */
int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 _min, s32 _max, s32 _step, s32 _def)
{
@@ -135,101 +105,6 @@
}
EXPORT_SYMBOL(v4l2_ctrl_query_fill);
-/* Fill in a struct v4l2_querymenu based on the struct v4l2_queryctrl and
- the menu. The qctrl pointer may be NULL, in which case it is ignored.
- If menu_items is NULL, then the menu items are retrieved using
- v4l2_ctrl_get_menu. */
-int v4l2_ctrl_query_menu(struct v4l2_querymenu *qmenu, struct v4l2_queryctrl *qctrl,
- const char * const *menu_items)
-{
- int i;
-
- qmenu->reserved = 0;
- if (menu_items == NULL)
- menu_items = v4l2_ctrl_get_menu(qmenu->id);
- if (menu_items == NULL ||
- (qctrl && (qmenu->index < qctrl->minimum || qmenu->index > qctrl->maximum)))
- return -EINVAL;
- for (i = 0; i < qmenu->index && menu_items[i]; i++) ;
- if (menu_items[i] == NULL || menu_items[i][0] == '\0')
- return -EINVAL;
- strlcpy(qmenu->name, menu_items[qmenu->index], sizeof(qmenu->name));
- return 0;
-}
-EXPORT_SYMBOL(v4l2_ctrl_query_menu);
-
-/* Fill in a struct v4l2_querymenu based on the specified array of valid
- menu items (terminated by V4L2_CTRL_MENU_IDS_END).
- Use this if there are 'holes' in the list of valid menu items. */
-int v4l2_ctrl_query_menu_valid_items(struct v4l2_querymenu *qmenu, const u32 *ids)
-{
- const char * const *menu_items = v4l2_ctrl_get_menu(qmenu->id);
-
- qmenu->reserved = 0;
- if (menu_items == NULL || ids == NULL)
- return -EINVAL;
- while (*ids != V4L2_CTRL_MENU_IDS_END) {
- if (*ids++ == qmenu->index) {
- strlcpy(qmenu->name, menu_items[qmenu->index],
- sizeof(qmenu->name));
- return 0;
- }
- }
- return -EINVAL;
-}
-EXPORT_SYMBOL(v4l2_ctrl_query_menu_valid_items);
-
-/* ctrl_classes points to an array of u32 pointers, the last element is
- a NULL pointer. Each u32 array is a 0-terminated array of control IDs.
- Each array must be sorted low to high and belong to the same control
- class. The array of u32 pointers must also be sorted, from low class IDs
- to high class IDs.
-
- This function returns the first ID that follows after the given ID.
- When no more controls are available 0 is returned. */
-u32 v4l2_ctrl_next(const u32 * const * ctrl_classes, u32 id)
-{
- u32 ctrl_class = V4L2_CTRL_ID2CLASS(id);
- const u32 *pctrl;
-
- if (ctrl_classes == NULL)
- return 0;
-
- /* if no query is desired, then check if the ID is part of ctrl_classes */
- if ((id & V4L2_CTRL_FLAG_NEXT_CTRL) == 0) {
- /* find class */
- while (*ctrl_classes && V4L2_CTRL_ID2CLASS(**ctrl_classes) != ctrl_class)
- ctrl_classes++;
- if (*ctrl_classes == NULL)
- return 0;
- pctrl = *ctrl_classes;
- /* find control ID */
- while (*pctrl && *pctrl != id) pctrl++;
- return *pctrl ? id : 0;
- }
- id &= V4L2_CTRL_ID_MASK;
- id++; /* select next control */
- /* find first class that matches (or is greater than) the class of
- the ID */
- while (*ctrl_classes && V4L2_CTRL_ID2CLASS(**ctrl_classes) < ctrl_class)
- ctrl_classes++;
- /* no more classes */
- if (*ctrl_classes == NULL)
- return 0;
- pctrl = *ctrl_classes;
- /* find first ctrl within the class that is >= ID */
- while (*pctrl && *pctrl < id) pctrl++;
- if (*pctrl)
- return *pctrl;
- /* we are at the end of the controls of the current class. */
- /* continue with next class if available */
- ctrl_classes++;
- if (*ctrl_classes == NULL)
- return 0;
- return **ctrl_classes;
-}
-EXPORT_SYMBOL(v4l2_ctrl_next);
-
/* I2C Helper functions */
#if IS_ENABLED(CONFIG_I2C)
diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
index e502a5f..af63543 100644
--- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
+++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
@@ -222,6 +222,9 @@
static int __put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
{
+ if (put_user(kp->type, &up->type))
+ return -EFAULT;
+
switch (kp->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
@@ -248,8 +251,7 @@
static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
{
- if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_format32)) ||
- put_user(kp->type, &up->type))
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_format32)))
return -EFAULT;
return __put_v4l2_format32(kp, up);
}
@@ -257,8 +259,8 @@
static int put_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
{
if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_create_buffers32)) ||
- copy_to_user(up, kp, offsetof(struct v4l2_create_buffers32, format.fmt)))
- return -EFAULT;
+ copy_to_user(up, kp, offsetof(struct v4l2_create_buffers32, format)))
+ return -EFAULT;
return __put_v4l2_format32(&kp->format, &up->format);
}
diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
index 8601214..45c5b47 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -1658,10 +1658,8 @@
}
/* Validate a new control */
-static int validate_new(const struct v4l2_ctrl *ctrl,
- struct v4l2_ext_control *c)
+static int validate_new(const struct v4l2_ctrl *ctrl, union v4l2_ctrl_ptr p_new)
{
- union v4l2_ctrl_ptr ptr;
unsigned idx;
int err = 0;
@@ -1674,19 +1672,14 @@
case V4L2_CTRL_TYPE_BOOLEAN:
case V4L2_CTRL_TYPE_BUTTON:
case V4L2_CTRL_TYPE_CTRL_CLASS:
- ptr.p_s32 = &c->value;
- return ctrl->type_ops->validate(ctrl, 0, ptr);
-
case V4L2_CTRL_TYPE_INTEGER64:
- ptr.p_s64 = &c->value64;
- return ctrl->type_ops->validate(ctrl, 0, ptr);
+ return ctrl->type_ops->validate(ctrl, 0, p_new);
default:
break;
}
}
- ptr.p = c->ptr;
- for (idx = 0; !err && idx < c->size / ctrl->elem_size; idx++)
- err = ctrl->type_ops->validate(ctrl, idx, ptr);
+ for (idx = 0; !err && idx < ctrl->elems; idx++)
+ err = ctrl->type_ops->validate(ctrl, idx, p_new);
return err;
}
@@ -3012,6 +3005,7 @@
cs->error_idx = cs->count;
for (i = 0; i < cs->count; i++) {
struct v4l2_ctrl *ctrl = helpers[i].ctrl;
+ union v4l2_ctrl_ptr p_new;
cs->error_idx = i;
@@ -3025,7 +3019,17 @@
best-effort to avoid that. */
if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED))
return -EBUSY;
- ret = validate_new(ctrl, &cs->controls[i]);
+ /*
+ * Skip validation for now if the payload needs to be copied
+ * from userspace into kernelspace. We'll validate those later.
+ */
+ if (ctrl->is_ptr)
+ continue;
+ if (ctrl->type == V4L2_CTRL_TYPE_INTEGER64)
+ p_new.p_s64 = &cs->controls[i].value64;
+ else
+ p_new.p_s32 = &cs->controls[i].value;
+ ret = validate_new(ctrl, p_new);
if (ret)
return ret;
}
@@ -3120,7 +3124,11 @@
/* Copy the new caller-supplied control values.
user_to_new() sets 'is_new' to 1. */
do {
- ret = user_to_new(cs->controls + idx, helpers[idx].ctrl);
+ struct v4l2_ctrl *ctrl = helpers[idx].ctrl;
+
+ ret = user_to_new(cs->controls + idx, ctrl);
+ if (!ret && ctrl->is_ptr)
+ ret = validate_new(ctrl, ctrl->p_new);
idx = helpers[idx].next;
} while (!ret && idx);
@@ -3170,10 +3178,10 @@
EXPORT_SYMBOL(v4l2_subdev_s_ext_ctrls);
/* Helper function for VIDIOC_S_CTRL compatibility */
-static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl,
- struct v4l2_ext_control *c, u32 ch_flags)
+static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 ch_flags)
{
struct v4l2_ctrl *master = ctrl->cluster[0];
+ int ret;
int i;
/* Reset the 'is_new' flags of the cluster */
@@ -3181,8 +3189,9 @@
if (master->cluster[i])
master->cluster[i]->is_new = 0;
- if (c)
- user_to_new(c, ctrl);
+ ret = validate_new(ctrl, ctrl->p_new);
+ if (ret)
+ return ret;
/* For autoclusters with volatiles that are switched from auto to
manual mode we have to update the current volatile values since
@@ -3199,15 +3208,14 @@
static int set_ctrl_lock(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl,
struct v4l2_ext_control *c)
{
- int ret = validate_new(ctrl, c);
+ int ret;
- if (!ret) {
- v4l2_ctrl_lock(ctrl);
- ret = set_ctrl(fh, ctrl, c, 0);
- if (!ret)
- cur_to_user(c, ctrl);
- v4l2_ctrl_unlock(ctrl);
- }
+ v4l2_ctrl_lock(ctrl);
+ user_to_new(c, ctrl);
+ ret = set_ctrl(fh, ctrl, 0);
+ if (!ret)
+ cur_to_user(c, ctrl);
+ v4l2_ctrl_unlock(ctrl);
return ret;
}
@@ -3215,7 +3223,7 @@
struct v4l2_control *control)
{
struct v4l2_ctrl *ctrl = v4l2_ctrl_find(hdl, control->id);
- struct v4l2_ext_control c;
+ struct v4l2_ext_control c = { control->id };
int ret;
if (ctrl == NULL || !ctrl->is_int)
@@ -3244,7 +3252,7 @@
/* It's a driver bug if this happens. */
WARN_ON(!ctrl->is_int);
ctrl->val = val;
- return set_ctrl(NULL, ctrl, NULL, 0);
+ return set_ctrl(NULL, ctrl, 0);
}
EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl);
@@ -3255,7 +3263,7 @@
/* It's a driver bug if this happens. */
WARN_ON(ctrl->is_ptr || ctrl->type != V4L2_CTRL_TYPE_INTEGER64);
*ctrl->p_new.p_s64 = val;
- return set_ctrl(NULL, ctrl, NULL, 0);
+ return set_ctrl(NULL, ctrl, 0);
}
EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_int64);
@@ -3266,7 +3274,7 @@
/* It's a driver bug if this happens. */
WARN_ON(ctrl->type != V4L2_CTRL_TYPE_STRING);
strlcpy(ctrl->p_new.p_char, s, ctrl->maximum + 1);
- return set_ctrl(NULL, ctrl, NULL, 0);
+ return set_ctrl(NULL, ctrl, 0);
}
EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_string);
@@ -3289,8 +3297,8 @@
int __v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl,
s64 min, s64 max, u64 step, s64 def)
{
+ bool changed;
int ret;
- struct v4l2_ext_control c;
lockdep_assert_held(ctrl->handler->lock);
@@ -3317,11 +3325,20 @@
ctrl->maximum = max;
ctrl->step = step;
ctrl->default_value = def;
- c.value = *ctrl->p_cur.p_s32;
- if (validate_new(ctrl, &c))
- c.value = def;
- if (c.value != *ctrl->p_cur.p_s32)
- ret = set_ctrl(NULL, ctrl, &c, V4L2_EVENT_CTRL_CH_RANGE);
+ cur_to_new(ctrl);
+ if (validate_new(ctrl, ctrl->p_new)) {
+ if (ctrl->type == V4L2_CTRL_TYPE_INTEGER64)
+ *ctrl->p_new.p_s64 = def;
+ else
+ *ctrl->p_new.p_s32 = def;
+ }
+
+ if (ctrl->type == V4L2_CTRL_TYPE_INTEGER64)
+ changed = *ctrl->p_new.p_s64 != *ctrl->p_cur.p_s64;
+ else
+ changed = *ctrl->p_new.p_s32 != *ctrl->p_cur.p_s32;
+ if (changed)
+ ret = set_ctrl(NULL, ctrl, V4L2_EVENT_CTRL_CH_RANGE);
else
send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_RANGE);
return ret;
diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
index 7953699..7385fc2 100644
--- a/drivers/media/v4l2-core/v4l2-dev.c
+++ b/drivers/media/v4l2-core/v4l2-dev.c
@@ -199,7 +199,7 @@
mutex_unlock(&videodev_lock);
#if defined(CPTCFG_MEDIA_CONTROLLER)
- if (v4l2_dev && v4l2_dev->mdev &&
+ if (v4l2_dev->mdev &&
vdev->vfl_type != VFL_TYPE_SUBDEV)
media_device_unregister_entity(&vdev->entity);
#endif
@@ -212,7 +212,7 @@
* TODO: In the long run all drivers that use v4l2_device should use the
* v4l2_device release callback. This check will then be unnecessary.
*/
- if (v4l2_dev && v4l2_dev->release == NULL)
+ if (v4l2_dev->release == NULL)
v4l2_dev = NULL;
/* Release video_device and perform other
@@ -369,27 +369,22 @@
* hack but it will have to do for those drivers that are not
* yet converted to use unlocked_ioctl.
*
- * There are two options: if the driver implements struct
- * v4l2_device, then the lock defined there is used to
- * serialize the ioctls. Otherwise the v4l2 core lock defined
- * below is used. This lock is really bad since it serializes
- * completely independent devices.
+ * All drivers implement struct v4l2_device, so we use the
+ * lock defined there to serialize the ioctls.
*
- * Both variants suffer from the same problem: if the driver
- * sleeps, then it blocks all ioctls since the lock is still
- * held. This is very common for VIDIOC_DQBUF since that
- * normally waits for a frame to arrive. As a result any other
- * ioctl calls will proceed very, very slowly since each call
- * will have to wait for the VIDIOC_QBUF to finish. Things that
- * should take 0.01s may now take 10-20 seconds.
+ * However, if the driver sleeps, then it blocks all ioctls
+ * since the lock is still held. This is very common for
+ * VIDIOC_DQBUF since that normally waits for a frame to arrive.
+ * As a result any other ioctl calls will proceed very, very
+ * slowly since each call will have to wait for the VIDIOC_QBUF
+ * to finish. Things that should take 0.01s may now take 10-20
+ * seconds.
*
* The workaround is to *not* take the lock for VIDIOC_DQBUF.
* This actually works OK for videobuf-based drivers, since
* videobuf will take its own internal lock.
*/
- static DEFINE_MUTEX(v4l2_ioctl_mutex);
- struct mutex *m = vdev->v4l2_dev ?
- &vdev->v4l2_dev->ioctl_lock : &v4l2_ioctl_mutex;
+ struct mutex *m = &vdev->v4l2_dev->ioctl_lock;
if (cmd != VIDIOC_DQBUF && mutex_lock_interruptible(m))
return -ERESTARTSYS;
@@ -947,12 +942,11 @@
name_base, nr, video_device_node_name(vdev));
/* Increase v4l2_device refcount */
- if (vdev->v4l2_dev)
- v4l2_device_get(vdev->v4l2_dev);
+ v4l2_device_get(vdev->v4l2_dev);
#if defined(CPTCFG_MEDIA_CONTROLLER)
/* Part 5: Register the entity. */
- if (vdev->v4l2_dev && vdev->v4l2_dev->mdev &&
+ if (vdev->v4l2_dev->mdev &&
vdev->vfl_type != VFL_TYPE_SUBDEV) {
vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
vdev->entity.name = vdev->name;
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index a47079b..e20f1c9 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -257,7 +257,7 @@
pr_cont(", width=%u, height=%u, "
"pixelformat=%c%c%c%c, field=%s, "
"bytesperline=%u, sizeimage=%u, colorspace=%d, "
- "flags %u\n",
+ "flags %x, ycbcr_enc=%u, quantization=%u\n",
pix->width, pix->height,
(pix->pixelformat & 0xff),
(pix->pixelformat >> 8) & 0xff,
@@ -265,21 +265,24 @@
(pix->pixelformat >> 24) & 0xff,
prt_names(pix->field, v4l2_field_names),
pix->bytesperline, pix->sizeimage,
- pix->colorspace, pix->flags);
+ pix->colorspace, pix->flags, pix->ycbcr_enc,
+ pix->quantization);
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
mp = &p->fmt.pix_mp;
pr_cont(", width=%u, height=%u, "
"format=%c%c%c%c, field=%s, "
- "colorspace=%d, num_planes=%u\n",
+ "colorspace=%d, num_planes=%u, flags=%x, "
+ "ycbcr_enc=%u, quantization=%u\n",
mp->width, mp->height,
(mp->pixelformat & 0xff),
(mp->pixelformat >> 8) & 0xff,
(mp->pixelformat >> 16) & 0xff,
(mp->pixelformat >> 24) & 0xff,
prt_names(mp->field, v4l2_field_names),
- mp->colorspace, mp->num_planes);
+ mp->colorspace, mp->num_planes, mp->flags,
+ mp->ycbcr_enc, mp->quantization);
for (i = 0; i < mp->num_planes; i++)
printk(KERN_DEBUG "plane %u: bytesperline=%u sizeimage=%u\n", i,
mp->plane_fmt[i].bytesperline,
@@ -1014,6 +1017,12 @@
ret = ops->vidioc_querycap(file, fh, cap);
cap->capabilities |= V4L2_CAP_EXT_PIX_FORMAT;
+ /*
+ * Drivers MUST fill in device_caps, so check for this and
+ * warn if it was forgotten.
+ */
+ WARN_ON(!(cap->capabilities & V4L2_CAP_DEVICE_CAPS) ||
+ !cap->device_caps);
cap->device_caps |= V4L2_CAP_EXT_PIX_FORMAT;
return ret;
@@ -1040,7 +1049,7 @@
if (ops->vidioc_g_priority)
return ops->vidioc_g_priority(file, fh, arg);
vfd = video_devdata(file);
- *p = v4l2_prio_max(&vfd->v4l2_dev->prio);
+ *p = v4l2_prio_max(vfd->prio);
return 0;
}
@@ -1055,7 +1064,7 @@
return ops->vidioc_s_priority(file, fh, *p);
vfd = video_devdata(file);
vfh = file->private_data;
- return v4l2_prio_change(&vfd->v4l2_dev->prio, &vfh->prio, *p);
+ return v4l2_prio_change(vfd->prio, &vfh->prio, *p);
}
static int v4l_enuminput(const struct v4l2_ioctl_ops *ops,
diff --git a/drivers/media/v4l2-core/videobuf-core.c b/drivers/media/v4l2-core/videobuf-core.c
index b91a266..926836d 100644
--- a/drivers/media/v4l2-core/videobuf-core.c
+++ b/drivers/media/v4l2-core/videobuf-core.c
@@ -51,6 +51,8 @@
#define CALL(q, f, arg...) \
((q->int_ops->f) ? q->int_ops->f(arg) : 0)
+#define CALLPTR(q, f, arg...) \
+ ((q->int_ops->f) ? q->int_ops->f(arg) : NULL)
struct videobuf_buffer *videobuf_alloc_vb(struct videobuf_queue *q)
{
@@ -831,7 +833,7 @@
char __user *data, size_t count,
int nonblocking)
{
- void *vaddr = CALL(q, vaddr, buf);
+ void *vaddr = CALLPTR(q, vaddr, buf);
/* copy to userspace */
if (count > buf->size - q->read_off)
@@ -848,7 +850,7 @@
char __user *data, size_t count, size_t pos,
int vbihack, int nonblocking)
{
- unsigned int *fc = CALL(q, vaddr, buf);
+ unsigned int *fc = CALLPTR(q, vaddr, buf);
if (vbihack) {
/* dirty, undocumented hack -- pass the frame counter
diff --git a/drivers/media/v4l2-core/videobuf-dma-contig.c b/drivers/media/v4l2-core/videobuf-dma-contig.c
index bf80f0f..e02353e 100644
--- a/drivers/media/v4l2-core/videobuf-dma-contig.c
+++ b/drivers/media/v4l2-core/videobuf-dma-contig.c
@@ -305,6 +305,15 @@
/* Try to remap memory */
size = vma->vm_end - vma->vm_start;
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ /* the "vm_pgoff" is just used in v4l2 to find the
+ * corresponding buffer data structure which is allocated
+ * earlier and it does not mean the offset from the physical
+ * buffer start address as usual. So set it to 0 to pass
+ * the sanity check in vm_iomap_memory().
+ */
+ vma->vm_pgoff = 0;
+
retval = vm_iomap_memory(vma, mem->dma_handle, size);
if (retval) {
dev_err(q->dev, "mmap: remap failed with error %d. ",
diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
index e9d733e..c20bdf7 100644
--- a/drivers/media/v4l2-core/videobuf2-core.c
+++ b/drivers/media/v4l2-core/videobuf2-core.c
@@ -189,6 +189,8 @@
static int __vb2_buf_mem_alloc(struct vb2_buffer *vb)
{
struct vb2_queue *q = vb->vb2_queue;
+ enum dma_data_direction dma_dir =
+ V4L2_TYPE_IS_OUTPUT(q->type) ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
void *mem_priv;
int plane;
@@ -200,7 +202,7 @@
unsigned long size = PAGE_ALIGN(q->plane_sizes[plane]);
mem_priv = call_ptr_memop(vb, alloc, q->alloc_ctx[plane],
- size, q->gfp_flags);
+ size, dma_dir, q->gfp_flags);
if (IS_ERR_OR_NULL(mem_priv))
goto free;
@@ -1366,7 +1368,8 @@
void *mem_priv;
unsigned int plane;
int ret;
- int write = !V4L2_TYPE_IS_OUTPUT(q->type);
+ enum dma_data_direction dma_dir =
+ V4L2_TYPE_IS_OUTPUT(q->type) ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
bool reacquired = vb->planes[0].mem_priv == NULL;
memset(planes, 0, sizeof(planes[0]) * vb->num_planes);
@@ -1408,7 +1411,7 @@
/* Acquire each plane's memory */
mem_priv = call_ptr_memop(vb, get_userptr, q->alloc_ctx[plane],
planes[plane].m.userptr,
- planes[plane].length, write);
+ planes[plane].length, dma_dir);
if (IS_ERR_OR_NULL(mem_priv)) {
dprintk(1, "failed acquiring userspace "
"memory for plane %d\n", plane);
@@ -1470,7 +1473,8 @@
void *mem_priv;
unsigned int plane;
int ret;
- int write = !V4L2_TYPE_IS_OUTPUT(q->type);
+ enum dma_data_direction dma_dir =
+ V4L2_TYPE_IS_OUTPUT(q->type) ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
bool reacquired = vb->planes[0].mem_priv == NULL;
memset(planes, 0, sizeof(planes[0]) * vb->num_planes);
@@ -1518,7 +1522,7 @@
/* Acquire each plane's memory */
mem_priv = call_ptr_memop(vb, attach_dmabuf, q->alloc_ctx[plane],
- dbuf, planes[plane].length, write);
+ dbuf, planes[plane].length, dma_dir);
if (IS_ERR(mem_priv)) {
dprintk(1, "failed to attach dmabuf\n");
ret = PTR_ERR(mem_priv);
@@ -3405,14 +3409,14 @@
{
struct video_device *vdev = video_devdata(file);
+ if (lock)
+ mutex_lock(lock);
if (file->private_data == vdev->queue->owner) {
- if (lock)
- mutex_lock(lock);
vb2_queue_release(vdev->queue);
vdev->queue->owner = NULL;
- if (lock)
- mutex_unlock(lock);
}
+ if (lock)
+ mutex_unlock(lock);
return v4l2_fh_release(file);
}
EXPORT_SYMBOL_GPL(_vb2_fop_release);
@@ -3475,27 +3479,16 @@
struct video_device *vdev = video_devdata(file);
struct vb2_queue *q = vdev->queue;
struct mutex *lock = q->lock ? q->lock : vdev->lock;
- unsigned long req_events = poll_requested_events(wait);
unsigned res;
void *fileio;
- bool must_lock = false;
- /* Try to be smart: only lock if polling might start fileio,
- otherwise locking will only introduce unwanted delays. */
- if (q->num_buffers == 0 && !vb2_fileio_is_active(q)) {
- if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ) &&
- (req_events & (POLLIN | POLLRDNORM)))
- must_lock = true;
- else if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE) &&
- (req_events & (POLLOUT | POLLWRNORM)))
- must_lock = true;
- }
+ /*
+ * If this helper doesn't know how to lock, then you shouldn't be using
+ * it but you should write your own.
+ */
+ WARN_ON(!lock);
- /* If locking is needed, but this helper doesn't know how, then you
- shouldn't be using this helper but you should write your own. */
- WARN_ON(must_lock && !lock);
-
- if (must_lock && lock && mutex_lock_interruptible(lock))
+ if (lock && mutex_lock_interruptible(lock))
return POLLERR;
fileio = q->fileio;
@@ -3503,9 +3496,9 @@
res = vb2_poll(vdev->queue, file, wait);
/* If fileio was started, then we have a new queue owner. */
- if (must_lock && !fileio && q->fileio)
+ if (!fileio && q->fileio)
q->owner = file->private_data;
- if (must_lock && lock)
+ if (lock)
mutex_unlock(lock);
return res;
}
diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c
index 058d979..d5e28df 100644
--- a/drivers/media/v4l2-core/videobuf2-dma-contig.c
+++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c
@@ -157,7 +157,8 @@
kfree(buf);
}
-static void *vb2_dc_alloc(void *alloc_ctx, unsigned long size, gfp_t gfp_flags)
+static void *vb2_dc_alloc(void *alloc_ctx, unsigned long size,
+ enum dma_data_direction dma_dir, gfp_t gfp_flags)
{
struct vb2_dc_conf *conf = alloc_ctx;
struct device *dev = conf->dev;
@@ -178,6 +179,7 @@
/* Prevent the device from being released while the buffer is used */
buf->dev = get_device(dev);
buf->size = size;
+ buf->dma_dir = dma_dir;
buf->handler.refcount = &buf->refcount;
buf->handler.put = vb2_dc_put;
@@ -279,7 +281,7 @@
struct vb2_dc_attachment {
struct sg_table sgt;
- enum dma_data_direction dir;
+ enum dma_data_direction dma_dir;
};
static int vb2_dc_dmabuf_ops_attach(struct dma_buf *dbuf, struct device *dev,
@@ -314,7 +316,7 @@
wr = sg_next(wr);
}
- attach->dir = DMA_NONE;
+ attach->dma_dir = DMA_NONE;
dbuf_attach->priv = attach;
return 0;
@@ -332,16 +334,16 @@
sgt = &attach->sgt;
/* release the scatterlist cache */
- if (attach->dir != DMA_NONE)
+ if (attach->dma_dir != DMA_NONE)
dma_unmap_sg(db_attach->dev, sgt->sgl, sgt->orig_nents,
- attach->dir);
+ attach->dma_dir);
sg_free_table(sgt);
kfree(attach);
db_attach->priv = NULL;
}
static struct sg_table *vb2_dc_dmabuf_ops_map(
- struct dma_buf_attachment *db_attach, enum dma_data_direction dir)
+ struct dma_buf_attachment *db_attach, enum dma_data_direction dma_dir)
{
struct vb2_dc_attachment *attach = db_attach->priv;
/* stealing dmabuf mutex to serialize map/unmap operations */
@@ -353,27 +355,27 @@
sgt = &attach->sgt;
/* return previously mapped sg table */
- if (attach->dir == dir) {
+ if (attach->dma_dir == dma_dir) {
mutex_unlock(lock);
return sgt;
}
/* release any previous cache */
- if (attach->dir != DMA_NONE) {
+ if (attach->dma_dir != DMA_NONE) {
dma_unmap_sg(db_attach->dev, sgt->sgl, sgt->orig_nents,
- attach->dir);
- attach->dir = DMA_NONE;
+ attach->dma_dir);
+ attach->dma_dir = DMA_NONE;
}
/* mapping to the client with new direction */
- ret = dma_map_sg(db_attach->dev, sgt->sgl, sgt->orig_nents, dir);
+ ret = dma_map_sg(db_attach->dev, sgt->sgl, sgt->orig_nents, dma_dir);
if (ret <= 0) {
pr_err("failed to map scatterlist\n");
mutex_unlock(lock);
return ERR_PTR(-EIO);
}
- attach->dir = dir;
+ attach->dma_dir = dma_dir;
mutex_unlock(lock);
@@ -381,7 +383,7 @@
}
static void vb2_dc_dmabuf_ops_unmap(struct dma_buf_attachment *db_attach,
- struct sg_table *sgt, enum dma_data_direction dir)
+ struct sg_table *sgt, enum dma_data_direction dma_dir)
{
/* nothing to be done here */
}
@@ -511,7 +513,8 @@
}
static int vb2_dc_get_user_pages(unsigned long start, struct page **pages,
- int n_pages, struct vm_area_struct *vma, int write)
+ int n_pages, struct vm_area_struct *vma,
+ enum dma_data_direction dma_dir)
{
if (vma_is_io(vma)) {
unsigned int i;
@@ -533,7 +536,7 @@
int n;
n = get_user_pages(current, current->mm, start & PAGE_MASK,
- n_pages, write, 1, pages, NULL);
+ n_pages, dma_dir == DMA_FROM_DEVICE, 1, pages, NULL);
/* negative error means that no page was pinned */
n = max(n, 0);
if (n != n_pages) {
@@ -559,7 +562,17 @@
struct sg_table *sgt = buf->dma_sgt;
if (sgt) {
- dma_unmap_sg(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir);
+ DEFINE_DMA_ATTRS(attrs);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)
+ dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs);
+#endif
+ /*
+ * No need to sync to CPU, it's already synced to the CPU
+ * since the finish() memop will have been called before this.
+ */
+ dma_unmap_sg_attrs(buf->dev, sgt->sgl, sgt->orig_nents,
+ buf->dma_dir, &attrs);
if (!vma_is_io(buf->vma))
vb2_dc_sgt_foreach_page(sgt, vb2_dc_put_dirty_page);
@@ -602,7 +615,7 @@
#endif
static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr,
- unsigned long size, int write)
+ unsigned long size, enum dma_data_direction dma_dir)
{
struct vb2_dc_conf *conf = alloc_ctx;
struct vb2_dc_buf *buf;
@@ -616,6 +629,11 @@
struct sg_table *sgt;
unsigned long contig_size;
unsigned long dma_align = dma_get_cache_alignment();
+ DEFINE_DMA_ATTRS(attrs);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)
+ dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs);
+#endif
/* Only cache aligned DMA transfers are reliable */
if (!IS_ALIGNED(vaddr | size, dma_align)) {
@@ -633,7 +651,7 @@
return ERR_PTR(-ENOMEM);
buf->dev = conf->dev;
- buf->dma_dir = write ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+ buf->dma_dir = dma_dir;
start = vaddr & PAGE_MASK;
offset = vaddr & ~PAGE_MASK;
@@ -669,7 +687,8 @@
}
/* extract page list from userspace mapping */
- ret = vb2_dc_get_user_pages(start, pages, n_pages, vma, write);
+ ret = vb2_dc_get_user_pages(start, pages, n_pages, vma,
+ dma_dir == DMA_FROM_DEVICE);
if (ret) {
unsigned long pfn;
if (vb2_dc_get_user_pfn(start, n_pages, vma, &pfn) == 0) {
@@ -701,8 +720,12 @@
kfree(pages);
pages = NULL;
- sgt->nents = dma_map_sg(buf->dev, sgt->sgl, sgt->orig_nents,
- buf->dma_dir);
+ /*
+ * No need to sync to the device, this will happen later when the
+ * prepare() memop is called.
+ */
+ sgt->nents = dma_map_sg_attrs(buf->dev, sgt->sgl, sgt->orig_nents,
+ buf->dma_dir, &attrs);
if (sgt->nents <= 0) {
pr_err("failed to map scatterlist\n");
ret = -EIO;
@@ -724,7 +747,8 @@
return buf;
fail_map_sg:
- dma_unmap_sg(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir);
+ dma_unmap_sg_attrs(buf->dev, sgt->sgl, sgt->orig_nents,
+ buf->dma_dir, &attrs);
fail_sgt_init:
if (!vma_is_io(buf->vma))
@@ -834,7 +858,7 @@
}
static void *vb2_dc_attach_dmabuf(void *alloc_ctx, struct dma_buf *dbuf,
- unsigned long size, int write)
+ unsigned long size, enum dma_data_direction dma_dir)
{
struct vb2_dc_conf *conf = alloc_ctx;
struct vb2_dc_buf *buf;
@@ -856,7 +880,7 @@
return dba;
}
- buf->dma_dir = write ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+ buf->dma_dir = dma_dir;
buf->size = size;
buf->db_attach = dba;
@@ -907,7 +931,8 @@
void vb2_dma_contig_cleanup_ctx(void *alloc_ctx)
{
- kfree(alloc_ctx);
+ if (!IS_ERR_OR_NULL(alloc_ctx))
+ kfree(alloc_ctx);
}
EXPORT_SYMBOL_GPL(vb2_dma_contig_cleanup_ctx);
diff --git a/drivers/media/v4l2-core/videobuf2-dma-sg.c b/drivers/media/v4l2-core/videobuf2-dma-sg.c
index 9b163a4..18f9f42 100644
--- a/drivers/media/v4l2-core/videobuf2-dma-sg.c
+++ b/drivers/media/v4l2-core/videobuf2-dma-sg.c
@@ -30,17 +30,30 @@
printk(KERN_DEBUG "vb2-dma-sg: " fmt, ## arg); \
} while (0)
+struct vb2_dma_sg_conf {
+ struct device *dev;
+};
+
struct vb2_dma_sg_buf {
+ struct device *dev;
void *vaddr;
struct page **pages;
- int write;
int offset;
+ enum dma_data_direction dma_dir;
struct sg_table sg_table;
+ /*
+ * This will point to sg_table when used with the MMAP or USERPTR
+ * memory model, and to the dma_buf sglist when used with the
+ * DMABUF memory model.
+ */
+ struct sg_table *dma_sgt;
size_t size;
unsigned int num_pages;
atomic_t refcount;
struct vb2_vmarea_handler handler;
struct vm_area_struct *vma;
+
+ struct dma_buf_attachment *db_attach;
};
static void vb2_dma_sg_put(void *buf_priv);
@@ -86,22 +99,33 @@
return 0;
}
-static void *vb2_dma_sg_alloc(void *alloc_ctx, unsigned long size, gfp_t gfp_flags)
+static void *vb2_dma_sg_alloc(void *alloc_ctx, unsigned long size,
+ enum dma_data_direction dma_dir, gfp_t gfp_flags)
{
+ struct vb2_dma_sg_conf *conf = alloc_ctx;
struct vb2_dma_sg_buf *buf;
+ struct sg_table *sgt;
int ret;
int num_pages;
+ DEFINE_DMA_ATTRS(attrs);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)
+ dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs);
+#endif
+
+ if (WARN_ON(alloc_ctx == NULL))
+ return NULL;
buf = kzalloc(sizeof *buf, GFP_KERNEL);
if (!buf)
return NULL;
buf->vaddr = NULL;
- buf->write = 0;
+ buf->dma_dir = dma_dir;
buf->offset = 0;
buf->size = size;
/* size is already page aligned */
buf->num_pages = size >> PAGE_SHIFT;
+ buf->dma_sgt = &buf->sg_table;
buf->pages = kzalloc(buf->num_pages * sizeof(struct page *),
GFP_KERNEL);
@@ -112,11 +136,23 @@
if (ret)
goto fail_pages_alloc;
- ret = sg_alloc_table_from_pages(&buf->sg_table, buf->pages,
+ ret = sg_alloc_table_from_pages(buf->dma_sgt, buf->pages,
buf->num_pages, 0, size, GFP_KERNEL);
if (ret)
goto fail_table_alloc;
+ /* Prevent the device from being released while the buffer is used */
+ buf->dev = get_device(conf->dev);
+
+ sgt = &buf->sg_table;
+ /*
+ * No need to sync to the device, this will happen later when the
+ * prepare() memop is called.
+ */
+ if (dma_map_sg_attrs(buf->dev, sgt->sgl, sgt->nents,
+ buf->dma_dir, &attrs) == 0)
+ goto fail_map;
+
buf->handler.refcount = &buf->refcount;
buf->handler.put = vb2_dma_sg_put;
buf->handler.arg = buf;
@@ -127,6 +163,9 @@
__func__, buf->num_pages);
return buf;
+fail_map:
+ put_device(buf->dev);
+ sg_free_table(buf->dma_sgt);
fail_table_alloc:
num_pages = buf->num_pages;
while (num_pages--)
@@ -141,42 +180,85 @@
static void vb2_dma_sg_put(void *buf_priv)
{
struct vb2_dma_sg_buf *buf = buf_priv;
+ struct sg_table *sgt = &buf->sg_table;
int i = buf->num_pages;
if (atomic_dec_and_test(&buf->refcount)) {
+ DEFINE_DMA_ATTRS(attrs);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)
+ dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs);
+#endif
dprintk(1, "%s: Freeing buffer of %d pages\n", __func__,
buf->num_pages);
+ dma_unmap_sg_attrs(buf->dev, sgt->sgl, sgt->nents,
+ buf->dma_dir, &attrs);
if (buf->vaddr)
vm_unmap_ram(buf->vaddr, buf->num_pages);
- sg_free_table(&buf->sg_table);
+ sg_free_table(buf->dma_sgt);
while (--i >= 0)
__free_page(buf->pages[i]);
kfree(buf->pages);
+ put_device(buf->dev);
kfree(buf);
}
}
+static void vb2_dma_sg_prepare(void *buf_priv)
+{
+ struct vb2_dma_sg_buf *buf = buf_priv;
+ struct sg_table *sgt = buf->dma_sgt;
+
+ /* DMABUF exporter will flush the cache for us */
+ if (buf->db_attach)
+ return;
+
+ dma_sync_sg_for_device(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir);
+}
+
+static void vb2_dma_sg_finish(void *buf_priv)
+{
+ struct vb2_dma_sg_buf *buf = buf_priv;
+ struct sg_table *sgt = buf->dma_sgt;
+
+ /* DMABUF exporter will flush the cache for us */
+ if (buf->db_attach)
+ return;
+
+ dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir);
+}
+
static inline int vma_is_io(struct vm_area_struct *vma)
{
return !!(vma->vm_flags & (VM_IO | VM_PFNMAP));
}
static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr,
- unsigned long size, int write)
+ unsigned long size,
+ enum dma_data_direction dma_dir)
{
+ struct vb2_dma_sg_conf *conf = alloc_ctx;
struct vb2_dma_sg_buf *buf;
unsigned long first, last;
int num_pages_from_user;
struct vm_area_struct *vma;
+ struct sg_table *sgt;
+ DEFINE_DMA_ATTRS(attrs);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)
+ dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs);
+#endif
buf = kzalloc(sizeof *buf, GFP_KERNEL);
if (!buf)
return NULL;
buf->vaddr = NULL;
- buf->write = write;
+ buf->dev = conf->dev;
+ buf->dma_dir = dma_dir;
buf->offset = vaddr & ~PAGE_MASK;
buf->size = size;
+ buf->dma_sgt = &buf->sg_table;
first = (vaddr & PAGE_MASK) >> PAGE_SHIFT;
last = ((vaddr + size - 1) & PAGE_MASK) >> PAGE_SHIFT;
@@ -221,7 +303,7 @@
num_pages_from_user = get_user_pages(current, current->mm,
vaddr & PAGE_MASK,
buf->num_pages,
- write,
+ buf->dma_dir == DMA_FROM_DEVICE,
1, /* force */
buf->pages,
NULL);
@@ -229,12 +311,22 @@
if (num_pages_from_user != buf->num_pages)
goto userptr_fail_get_user_pages;
- if (sg_alloc_table_from_pages(&buf->sg_table, buf->pages,
+ if (sg_alloc_table_from_pages(buf->dma_sgt, buf->pages,
buf->num_pages, buf->offset, size, 0))
goto userptr_fail_alloc_table_from_pages;
+ sgt = &buf->sg_table;
+ /*
+ * No need to sync to the device, this will happen later when the
+ * prepare() memop is called.
+ */
+ if (dma_map_sg_attrs(buf->dev, sgt->sgl, sgt->nents,
+ buf->dma_dir, &attrs) == 0)
+ goto userptr_fail_map;
return buf;
+userptr_fail_map:
+ sg_free_table(&buf->sg_table);
userptr_fail_alloc_table_from_pages:
userptr_fail_get_user_pages:
dprintk(1, "get_user_pages requested/got: %d/%d]\n",
@@ -257,15 +349,22 @@
static void vb2_dma_sg_put_userptr(void *buf_priv)
{
struct vb2_dma_sg_buf *buf = buf_priv;
+ struct sg_table *sgt = &buf->sg_table;
int i = buf->num_pages;
+ DEFINE_DMA_ATTRS(attrs);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)
+ dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs);
+#endif
dprintk(1, "%s: Releasing userspace buffer of %d pages\n",
__func__, buf->num_pages);
+ dma_unmap_sg_attrs(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir, &attrs);
if (buf->vaddr)
vm_unmap_ram(buf->vaddr, buf->num_pages);
- sg_free_table(&buf->sg_table);
+ sg_free_table(buf->dma_sgt);
while (--i >= 0) {
- if (buf->write)
+ if (buf->dma_dir == DMA_FROM_DEVICE)
set_page_dirty_lock(buf->pages[i]);
if (!vma_is_io(buf->vma))
put_page(buf->pages[i]);
@@ -281,14 +380,18 @@
BUG_ON(!buf);
- if (!buf->vaddr)
- buf->vaddr = vm_map_ram(buf->pages,
- buf->num_pages,
- -1,
- PAGE_KERNEL);
+ if (!buf->vaddr) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
+ if (buf->db_attach)
+ buf->vaddr = dma_buf_vmap(buf->db_attach->dmabuf);
+ else
+#endif
+ buf->vaddr = vm_map_ram(buf->pages,
+ buf->num_pages, -1, PAGE_KERNEL);
+ }
/* add offset in case userptr is not page-aligned */
- return buf->vaddr + buf->offset;
+ return buf->vaddr ? buf->vaddr + buf->offset : NULL;
}
static unsigned int vb2_dma_sg_num_users(void *buf_priv)
@@ -335,11 +438,281 @@
return 0;
}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
+/*********************************************/
+/* DMABUF ops for exporters */
+/*********************************************/
+
+struct vb2_dma_sg_attachment {
+ struct sg_table sgt;
+ enum dma_data_direction dma_dir;
+};
+
+static int vb2_dma_sg_dmabuf_ops_attach(struct dma_buf *dbuf, struct device *dev,
+ struct dma_buf_attachment *dbuf_attach)
+{
+ struct vb2_dma_sg_attachment *attach;
+ unsigned int i;
+ struct scatterlist *rd, *wr;
+ struct sg_table *sgt;
+ struct vb2_dma_sg_buf *buf = dbuf->priv;
+ int ret;
+
+ attach = kzalloc(sizeof(*attach), GFP_KERNEL);
+ if (!attach)
+ return -ENOMEM;
+
+ sgt = &attach->sgt;
+ /* Copy the buf->base_sgt scatter list to the attachment, as we can't
+ * map the same scatter list to multiple attachments at the same time.
+ */
+ ret = sg_alloc_table(sgt, buf->dma_sgt->orig_nents, GFP_KERNEL);
+ if (ret) {
+ kfree(attach);
+ return -ENOMEM;
+ }
+
+ rd = buf->dma_sgt->sgl;
+ wr = sgt->sgl;
+ for (i = 0; i < sgt->orig_nents; ++i) {
+ sg_set_page(wr, sg_page(rd), rd->length, rd->offset);
+ rd = sg_next(rd);
+ wr = sg_next(wr);
+ }
+
+ attach->dma_dir = DMA_NONE;
+ dbuf_attach->priv = attach;
+
+ return 0;
+}
+
+static void vb2_dma_sg_dmabuf_ops_detach(struct dma_buf *dbuf,
+ struct dma_buf_attachment *db_attach)
+{
+ struct vb2_dma_sg_attachment *attach = db_attach->priv;
+ struct sg_table *sgt;
+
+ if (!attach)
+ return;
+
+ sgt = &attach->sgt;
+
+ /* release the scatterlist cache */
+ if (attach->dma_dir != DMA_NONE)
+ dma_unmap_sg(db_attach->dev, sgt->sgl, sgt->orig_nents,
+ attach->dma_dir);
+ sg_free_table(sgt);
+ kfree(attach);
+ db_attach->priv = NULL;
+}
+
+static struct sg_table *vb2_dma_sg_dmabuf_ops_map(
+ struct dma_buf_attachment *db_attach, enum dma_data_direction dma_dir)
+{
+ struct vb2_dma_sg_attachment *attach = db_attach->priv;
+ /* stealing dmabuf mutex to serialize map/unmap operations */
+ struct mutex *lock = &db_attach->dmabuf->lock;
+ struct sg_table *sgt;
+ int ret;
+
+ mutex_lock(lock);
+
+ sgt = &attach->sgt;
+ /* return previously mapped sg table */
+ if (attach->dma_dir == dma_dir) {
+ mutex_unlock(lock);
+ return sgt;
+ }
+
+ /* release any previous cache */
+ if (attach->dma_dir != DMA_NONE) {
+ dma_unmap_sg(db_attach->dev, sgt->sgl, sgt->orig_nents,
+ attach->dma_dir);
+ attach->dma_dir = DMA_NONE;
+ }
+
+ /* mapping to the client with new direction */
+ ret = dma_map_sg(db_attach->dev, sgt->sgl, sgt->orig_nents, dma_dir);
+ if (ret <= 0) {
+ pr_err("failed to map scatterlist\n");
+ mutex_unlock(lock);
+ return ERR_PTR(-EIO);
+ }
+
+ attach->dma_dir = dma_dir;
+
+ mutex_unlock(lock);
+
+ return sgt;
+}
+
+static void vb2_dma_sg_dmabuf_ops_unmap(struct dma_buf_attachment *db_attach,
+ struct sg_table *sgt, enum dma_data_direction dma_dir)
+{
+ /* nothing to be done here */
+}
+
+static void vb2_dma_sg_dmabuf_ops_release(struct dma_buf *dbuf)
+{
+ /* drop reference obtained in vb2_dma_sg_get_dmabuf */
+ vb2_dma_sg_put(dbuf->priv);
+}
+
+static void *vb2_dma_sg_dmabuf_ops_kmap(struct dma_buf *dbuf, unsigned long pgnum)
+{
+ struct vb2_dma_sg_buf *buf = dbuf->priv;
+
+ return buf->vaddr ? buf->vaddr + pgnum * PAGE_SIZE : NULL;
+}
+
+static void *vb2_dma_sg_dmabuf_ops_vmap(struct dma_buf *dbuf)
+{
+ struct vb2_dma_sg_buf *buf = dbuf->priv;
+
+ return vb2_dma_sg_vaddr(buf);
+}
+
+static int vb2_dma_sg_dmabuf_ops_mmap(struct dma_buf *dbuf,
+ struct vm_area_struct *vma)
+{
+ return vb2_dma_sg_mmap(dbuf->priv, vma);
+}
+
+static struct dma_buf_ops vb2_dma_sg_dmabuf_ops = {
+ .attach = vb2_dma_sg_dmabuf_ops_attach,
+ .detach = vb2_dma_sg_dmabuf_ops_detach,
+ .map_dma_buf = vb2_dma_sg_dmabuf_ops_map,
+ .unmap_dma_buf = vb2_dma_sg_dmabuf_ops_unmap,
+ .kmap = vb2_dma_sg_dmabuf_ops_kmap,
+ .kmap_atomic = vb2_dma_sg_dmabuf_ops_kmap,
+ .vmap = vb2_dma_sg_dmabuf_ops_vmap,
+ .mmap = vb2_dma_sg_dmabuf_ops_mmap,
+ .release = vb2_dma_sg_dmabuf_ops_release,
+};
+
+static struct dma_buf *vb2_dma_sg_get_dmabuf(void *buf_priv, unsigned long flags)
+{
+ struct vb2_dma_sg_buf *buf = buf_priv;
+ struct dma_buf *dbuf;
+
+ if (WARN_ON(!buf->dma_sgt))
+ return NULL;
+
+ dbuf = dma_buf_export(buf, &vb2_dma_sg_dmabuf_ops, buf->size, flags, NULL);
+ if (IS_ERR(dbuf))
+ return NULL;
+
+ /* dmabuf keeps reference to vb2 buffer */
+ atomic_inc(&buf->refcount);
+
+ return dbuf;
+}
+
+/*********************************************/
+/* callbacks for DMABUF buffers */
+/*********************************************/
+
+static int vb2_dma_sg_map_dmabuf(void *mem_priv)
+{
+ struct vb2_dma_sg_buf *buf = mem_priv;
+ struct sg_table *sgt;
+
+ if (WARN_ON(!buf->db_attach)) {
+ pr_err("trying to pin a non attached buffer\n");
+ return -EINVAL;
+ }
+
+ if (WARN_ON(buf->dma_sgt)) {
+ pr_err("dmabuf buffer is already pinned\n");
+ return 0;
+ }
+
+ /* get the associated scatterlist for this buffer */
+ sgt = dma_buf_map_attachment(buf->db_attach, buf->dma_dir);
+ if (IS_ERR(sgt)) {
+ pr_err("Error getting dmabuf scatterlist\n");
+ return -EINVAL;
+ }
+
+ buf->dma_sgt = sgt;
+ buf->vaddr = NULL;
+
+ return 0;
+}
+
+static void vb2_dma_sg_unmap_dmabuf(void *mem_priv)
+{
+ struct vb2_dma_sg_buf *buf = mem_priv;
+ struct sg_table *sgt = buf->dma_sgt;
+
+ if (WARN_ON(!buf->db_attach)) {
+ pr_err("trying to unpin a not attached buffer\n");
+ return;
+ }
+
+ if (WARN_ON(!sgt)) {
+ pr_err("dmabuf buffer is already unpinned\n");
+ return;
+ }
+
+ if (buf->vaddr) {
+ dma_buf_vunmap(buf->db_attach->dmabuf, buf->vaddr);
+ buf->vaddr = NULL;
+ }
+ dma_buf_unmap_attachment(buf->db_attach, sgt, buf->dma_dir);
+
+ buf->dma_sgt = NULL;
+}
+
+static void vb2_dma_sg_detach_dmabuf(void *mem_priv)
+{
+ struct vb2_dma_sg_buf *buf = mem_priv;
+
+ /* if vb2 works correctly you should never detach mapped buffer */
+ if (WARN_ON(buf->dma_sgt))
+ vb2_dma_sg_unmap_dmabuf(buf);
+
+ /* detach this attachment */
+ dma_buf_detach(buf->db_attach->dmabuf, buf->db_attach);
+ kfree(buf);
+}
+
+static void *vb2_dma_sg_attach_dmabuf(void *alloc_ctx, struct dma_buf *dbuf,
+ unsigned long size, enum dma_data_direction dma_dir)
+{
+ struct vb2_dma_sg_conf *conf = alloc_ctx;
+ struct vb2_dma_sg_buf *buf;
+ struct dma_buf_attachment *dba;
+
+ if (dbuf->size < size)
+ return ERR_PTR(-EFAULT);
+
+ buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ buf->dev = conf->dev;
+ /* create attachment for the dmabuf with the user device */
+ dba = dma_buf_attach(dbuf, buf->dev);
+ if (IS_ERR(dba)) {
+ pr_err("failed to attach dmabuf\n");
+ kfree(buf);
+ return dba;
+ }
+
+ buf->dma_dir = dma_dir;
+ buf->size = size;
+ buf->db_attach = dba;
+
+ return buf;
+}
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) */
+
static void *vb2_dma_sg_cookie(void *buf_priv)
{
struct vb2_dma_sg_buf *buf = buf_priv;
- return &buf->sg_table;
+ return buf->dma_sgt;
}
const struct vb2_mem_ops vb2_dma_sg_memops = {
@@ -347,13 +720,43 @@
.put = vb2_dma_sg_put,
.get_userptr = vb2_dma_sg_get_userptr,
.put_userptr = vb2_dma_sg_put_userptr,
+ .prepare = vb2_dma_sg_prepare,
+ .finish = vb2_dma_sg_finish,
.vaddr = vb2_dma_sg_vaddr,
.mmap = vb2_dma_sg_mmap,
.num_users = vb2_dma_sg_num_users,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
+ .get_dmabuf = vb2_dma_sg_get_dmabuf,
+ .map_dmabuf = vb2_dma_sg_map_dmabuf,
+ .unmap_dmabuf = vb2_dma_sg_unmap_dmabuf,
+ .attach_dmabuf = vb2_dma_sg_attach_dmabuf,
+ .detach_dmabuf = vb2_dma_sg_detach_dmabuf,
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) */
.cookie = vb2_dma_sg_cookie,
};
EXPORT_SYMBOL_GPL(vb2_dma_sg_memops);
+void *vb2_dma_sg_init_ctx(struct device *dev)
+{
+ struct vb2_dma_sg_conf *conf;
+
+ conf = kzalloc(sizeof(*conf), GFP_KERNEL);
+ if (!conf)
+ return ERR_PTR(-ENOMEM);
+
+ conf->dev = dev;
+
+ return conf;
+}
+EXPORT_SYMBOL_GPL(vb2_dma_sg_init_ctx);
+
+void vb2_dma_sg_cleanup_ctx(void *alloc_ctx)
+{
+ if (!IS_ERR_OR_NULL(alloc_ctx))
+ kfree(alloc_ctx);
+}
+EXPORT_SYMBOL_GPL(vb2_dma_sg_cleanup_ctx);
+
MODULE_DESCRIPTION("dma scatter/gather memory handling routines for videobuf2");
MODULE_AUTHOR("Andrzej Pietrasiewicz");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/v4l2-core/videobuf2-vmalloc.c b/drivers/media/v4l2-core/videobuf2-vmalloc.c
index 59ef98c..272a83d 100644
--- a/drivers/media/v4l2-core/videobuf2-vmalloc.c
+++ b/drivers/media/v4l2-core/videobuf2-vmalloc.c
@@ -25,7 +25,7 @@
void *vaddr;
struct page **pages;
struct vm_area_struct *vma;
- int write;
+ enum dma_data_direction dma_dir;
unsigned long size;
unsigned int n_pages;
atomic_t refcount;
@@ -37,7 +37,8 @@
static void vb2_vmalloc_put(void *buf_priv);
-static void *vb2_vmalloc_alloc(void *alloc_ctx, unsigned long size, gfp_t gfp_flags)
+static void *vb2_vmalloc_alloc(void *alloc_ctx, unsigned long size,
+ enum dma_data_direction dma_dir, gfp_t gfp_flags)
{
struct vb2_vmalloc_buf *buf;
@@ -47,6 +48,7 @@
buf->size = size;
buf->vaddr = vmalloc_user(buf->size);
+ buf->dma_dir = dma_dir;
buf->handler.refcount = &buf->refcount;
buf->handler.put = vb2_vmalloc_put;
buf->handler.arg = buf;
@@ -72,7 +74,8 @@
}
static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr,
- unsigned long size, int write)
+ unsigned long size,
+ enum dma_data_direction dma_dir)
{
struct vb2_vmalloc_buf *buf;
unsigned long first, last;
@@ -84,7 +87,7 @@
if (!buf)
return NULL;
- buf->write = write;
+ buf->dma_dir = dma_dir;
offset = vaddr & ~PAGE_MASK;
buf->size = size;
@@ -109,7 +112,8 @@
/* current->mm->mmap_sem is taken by videobuf2 core */
n_pages = get_user_pages(current, current->mm,
vaddr & PAGE_MASK, buf->n_pages,
- write, 1, /* force */
+ dma_dir == DMA_FROM_DEVICE,
+ 1, /* force */
buf->pages, NULL);
if (n_pages != buf->n_pages)
goto fail_get_user_pages;
@@ -146,14 +150,13 @@
if (vaddr)
vm_unmap_ram((void *)vaddr, buf->n_pages);
for (i = 0; i < buf->n_pages; ++i) {
- if (buf->write)
+ if (buf->dma_dir == DMA_FROM_DEVICE)
set_page_dirty_lock(buf->pages[i]);
put_page(buf->pages[i]);
}
kfree(buf->pages);
} else {
- if (buf->vma)
- vb2_put_vma(buf->vma);
+ vb2_put_vma(buf->vma);
iounmap(buf->vaddr);
}
kfree(buf);
@@ -210,7 +213,177 @@
return 0;
}
+/*********************************************/
+/* DMABUF ops for exporters */
+/*********************************************/
+
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
+struct vb2_vmalloc_attachment {
+ struct sg_table sgt;
+ enum dma_data_direction dma_dir;
+};
+
+static int vb2_vmalloc_dmabuf_ops_attach(struct dma_buf *dbuf, struct device *dev,
+ struct dma_buf_attachment *dbuf_attach)
+{
+ struct vb2_vmalloc_attachment *attach;
+ struct vb2_vmalloc_buf *buf = dbuf->priv;
+ int num_pages = PAGE_ALIGN(buf->size) / PAGE_SIZE;
+ struct sg_table *sgt;
+ struct scatterlist *sg;
+ void *vaddr = buf->vaddr;
+ int ret;
+ int i;
+
+ attach = kzalloc(sizeof(*attach), GFP_KERNEL);
+ if (!attach)
+ return -ENOMEM;
+
+ sgt = &attach->sgt;
+ ret = sg_alloc_table(sgt, num_pages, GFP_KERNEL);
+ if (ret) {
+ kfree(attach);
+ return ret;
+ }
+ for_each_sg(sgt->sgl, sg, sgt->nents, i) {
+ struct page *page = vmalloc_to_page(vaddr);
+
+ if (!page) {
+ sg_free_table(sgt);
+ kfree(attach);
+ return -ENOMEM;
+ }
+ sg_set_page(sg, page, PAGE_SIZE, 0);
+ vaddr += PAGE_SIZE;
+ }
+
+ attach->dma_dir = DMA_NONE;
+ dbuf_attach->priv = attach;
+ return 0;
+}
+
+static void vb2_vmalloc_dmabuf_ops_detach(struct dma_buf *dbuf,
+ struct dma_buf_attachment *db_attach)
+{
+ struct vb2_vmalloc_attachment *attach = db_attach->priv;
+ struct sg_table *sgt;
+
+ if (!attach)
+ return;
+
+ sgt = &attach->sgt;
+
+ /* release the scatterlist cache */
+ if (attach->dma_dir != DMA_NONE)
+ dma_unmap_sg(db_attach->dev, sgt->sgl, sgt->orig_nents,
+ attach->dma_dir);
+ sg_free_table(sgt);
+ kfree(attach);
+ db_attach->priv = NULL;
+}
+
+static struct sg_table *vb2_vmalloc_dmabuf_ops_map(
+ struct dma_buf_attachment *db_attach, enum dma_data_direction dma_dir)
+{
+ struct vb2_vmalloc_attachment *attach = db_attach->priv;
+ /* stealing dmabuf mutex to serialize map/unmap operations */
+ struct mutex *lock = &db_attach->dmabuf->lock;
+ struct sg_table *sgt;
+ int ret;
+
+ mutex_lock(lock);
+
+ sgt = &attach->sgt;
+ /* return previously mapped sg table */
+ if (attach->dma_dir == dma_dir) {
+ mutex_unlock(lock);
+ return sgt;
+ }
+
+ /* release any previous cache */
+ if (attach->dma_dir != DMA_NONE) {
+ dma_unmap_sg(db_attach->dev, sgt->sgl, sgt->orig_nents,
+ attach->dma_dir);
+ attach->dma_dir = DMA_NONE;
+ }
+
+ /* mapping to the client with new direction */
+ ret = dma_map_sg(db_attach->dev, sgt->sgl, sgt->orig_nents, dma_dir);
+ if (ret <= 0) {
+ pr_err("failed to map scatterlist\n");
+ mutex_unlock(lock);
+ return ERR_PTR(-EIO);
+ }
+
+ attach->dma_dir = dma_dir;
+
+ mutex_unlock(lock);
+
+ return sgt;
+}
+
+static void vb2_vmalloc_dmabuf_ops_unmap(struct dma_buf_attachment *db_attach,
+ struct sg_table *sgt, enum dma_data_direction dma_dir)
+{
+ /* nothing to be done here */
+}
+
+static void vb2_vmalloc_dmabuf_ops_release(struct dma_buf *dbuf)
+{
+ /* drop reference obtained in vb2_vmalloc_get_dmabuf */
+ vb2_vmalloc_put(dbuf->priv);
+}
+
+static void *vb2_vmalloc_dmabuf_ops_kmap(struct dma_buf *dbuf, unsigned long pgnum)
+{
+ struct vb2_vmalloc_buf *buf = dbuf->priv;
+
+ return buf->vaddr + pgnum * PAGE_SIZE;
+}
+
+static void *vb2_vmalloc_dmabuf_ops_vmap(struct dma_buf *dbuf)
+{
+ struct vb2_vmalloc_buf *buf = dbuf->priv;
+
+ return buf->vaddr;
+}
+
+static int vb2_vmalloc_dmabuf_ops_mmap(struct dma_buf *dbuf,
+ struct vm_area_struct *vma)
+{
+ return vb2_vmalloc_mmap(dbuf->priv, vma);
+}
+
+static struct dma_buf_ops vb2_vmalloc_dmabuf_ops = {
+ .attach = vb2_vmalloc_dmabuf_ops_attach,
+ .detach = vb2_vmalloc_dmabuf_ops_detach,
+ .map_dma_buf = vb2_vmalloc_dmabuf_ops_map,
+ .unmap_dma_buf = vb2_vmalloc_dmabuf_ops_unmap,
+ .kmap = vb2_vmalloc_dmabuf_ops_kmap,
+ .kmap_atomic = vb2_vmalloc_dmabuf_ops_kmap,
+ .vmap = vb2_vmalloc_dmabuf_ops_vmap,
+ .mmap = vb2_vmalloc_dmabuf_ops_mmap,
+ .release = vb2_vmalloc_dmabuf_ops_release,
+};
+
+static struct dma_buf *vb2_vmalloc_get_dmabuf(void *buf_priv, unsigned long flags)
+{
+ struct vb2_vmalloc_buf *buf = buf_priv;
+ struct dma_buf *dbuf;
+
+ if (WARN_ON(!buf->vaddr))
+ return NULL;
+
+ dbuf = dma_buf_export(buf, &vb2_vmalloc_dmabuf_ops, buf->size, flags, NULL);
+ if (IS_ERR(dbuf))
+ return NULL;
+
+ /* dmabuf keeps reference to vb2 buffer */
+ atomic_inc(&buf->refcount);
+
+ return dbuf;
+}
+
/*********************************************/
/* callbacks for DMABUF buffers */
/*********************************************/
@@ -243,7 +416,7 @@
}
static void *vb2_vmalloc_attach_dmabuf(void *alloc_ctx, struct dma_buf *dbuf,
- unsigned long size, int write)
+ unsigned long size, enum dma_data_direction dma_dir)
{
struct vb2_vmalloc_buf *buf;
@@ -255,7 +428,7 @@
return ERR_PTR(-ENOMEM);
buf->dbuf = dbuf;
- buf->write = write;
+ buf->dma_dir = dma_dir;
buf->size = size;
return buf;
@@ -269,6 +442,7 @@
.get_userptr = vb2_vmalloc_get_userptr,
.put_userptr = vb2_vmalloc_put_userptr,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
+ .get_dmabuf = vb2_vmalloc_get_dmabuf,
.map_dmabuf = vb2_vmalloc_map_dmabuf,
.unmap_dmabuf = vb2_vmalloc_unmap_dmabuf,
.attach_dmabuf = vb2_vmalloc_attach_dmabuf,
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 0607b55..61deb80 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -90,7 +90,7 @@
depends on m
depends on PCI
depends on CRC32
- select BACKPORT_MII
+ select BPAUTO_MII
---help---
This driver supports the PCI-Express gigabit ethernet adapters
based on JMicron JMC250 chipset.
@@ -128,7 +128,7 @@
depends on m
depends on PCI
depends on CRC32
- select BACKPORT_MII
+ select BPAUTO_MII
---help---
Say Y here to support the Myson MTD-800 family of PCI-based Ethernet
cards. <http://www.myson.com.tw/>
@@ -140,7 +140,7 @@
depends on n
tristate "NetX Ethernet support"
depends on m
- select BACKPORT_MII
+ select BPAUTO_MII
depends on ARCH_NETX
---help---
This is support for the Hilscher netX builtin Ethernet ports
@@ -159,7 +159,7 @@
tristate "OpenCores 10/100 Mbps Ethernet MAC support"
depends on m
depends on HAS_IOMEM && HAS_DMA
- select BACKPORT_MII
+ select BPAUTO_MII
depends on PHYLIB
depends on CRC32
depends on BITREVERSE
@@ -173,6 +173,7 @@
#source "drivers/net/ethernet/realtek/Kconfig"
#source "drivers/net/ethernet/renesas/Kconfig"
#source "drivers/net/ethernet/rdc/Kconfig"
+#source "drivers/net/ethernet/rocker/Kconfig"
config S6GMAC
depends on n
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index ef26a25..be78a41 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -65,6 +65,7 @@
#obj-$(CONFIG_NET_VENDOR_REALTEK) += realtek/
#obj-$(CONFIG_SH_ETH) += renesas/
#obj-$(CONFIG_NET_VENDOR_RDC) += rdc/
+#obj-$(CONFIG_NET_VENDOR_ROCKER) += rocker/
#obj-$(CPTCFG_S6GMAC) += s6gmac.o
#obj-$(CONFIG_NET_VENDOR_SAMSUNG) += samsung/
#obj-$(CONFIG_NET_VENDOR_SEEQ) += seeq/
diff --git a/drivers/net/ethernet/atheros/Kconfig b/drivers/net/ethernet/atheros/Kconfig
index 77f036c..35840c9 100644
--- a/drivers/net/ethernet/atheros/Kconfig
+++ b/drivers/net/ethernet/atheros/Kconfig
@@ -23,7 +23,7 @@
depends on m
depends on PCI
depends on CRC32
- select BACKPORT_MII
+ select BPAUTO_MII
---help---
This driver supports the Atheros L2 fast ethernet adapter.
@@ -35,7 +35,7 @@
depends on m
depends on PCI
depends on CRC32
- select BACKPORT_MII
+ select BPAUTO_MII
---help---
This driver supports the Atheros/Attansic L1 gigabit ethernet
adapter.
@@ -48,7 +48,7 @@
depends on m
depends on PCI
depends on CRC32
- select BACKPORT_MII
+ select BPAUTO_MII
---help---
This driver supports the Atheros L1E gigabit ethernet adapter.
@@ -56,12 +56,12 @@
will be called atl1e.
config ATL1C
- depends on !BACKPORT_KERNEL_3_5
+ depends on !KERNEL_3_5
tristate "Atheros L1C Gigabit Ethernet support"
depends on m
depends on PCI
depends on CRC32
- select BACKPORT_MII
+ select BPAUTO_MII
---help---
This driver supports the Atheros L1C gigabit ethernet adapter.
diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
index d540d29..8ca9e3e 100644
--- a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
+++ b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
@@ -48,7 +48,7 @@
MODULE_AUTHOR("Jie Yang");
MODULE_AUTHOR("Qualcomm Atheros Inc., <nic-devel@qualcomm.com>");
-MODULE_DESCRIPTION("Qualcom Atheros 100/1000M Ethernet Network Driver");
+MODULE_DESCRIPTION("Qualcomm Atheros 100/1000M Ethernet Network Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(ATL1C_DRV_VERSION);
diff --git a/drivers/net/ethernet/broadcom/Kconfig b/drivers/net/ethernet/broadcom/Kconfig
index 276237c..9d17913 100644
--- a/drivers/net/ethernet/broadcom/Kconfig
+++ b/drivers/net/ethernet/broadcom/Kconfig
@@ -23,7 +23,7 @@
depends on m
depends on SSB_POSSIBLE && HAS_DMA
select SSB
- select BACKPORT_MII
+ select BPAUTO_MII
depends on PHYLIB
---help---
If you have a network (Ethernet) controller of this type, say Y
@@ -57,7 +57,7 @@
tristate "Broadcom 63xx internal mac support"
depends on m
depends on BCM63XX
- select BACKPORT_MII
+ select BPAUTO_MII
depends on PHYLIB
help
This driver supports the ethernet MACs in the Broadcom 63xx
@@ -67,10 +67,9 @@
depends on n
tristate "Broadcom GENET internal MAC support"
depends on m
- depends on OF
- select BACKPORT_MII
+ select BPAUTO_MII
depends on PHYLIB
- select FIXED_PHY if BCMGENET=y
+ depends on FIXED_PHY
depends on BCM7XXX_PHY
help
This driver supports the built-in Ethernet MACs found in the
@@ -173,9 +172,9 @@
tristate "Broadcom SYSTEMPORT internal MAC support"
depends on m
depends on OF
- select BACKPORT_MII
+ select BPAUTO_MII
depends on PHYLIB
- select FIXED_PHY if SYSTEMPORT=y
+ depends on FIXED_PHY
help
This driver supports the built-in Ethernet MACs found in the
Broadcom BCM7xxx Set Top Box family chipset using an internal
diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c
index f098a3f..290bf08 100644
--- a/drivers/net/ethernet/broadcom/b44.c
+++ b/drivers/net/ethernet/broadcom/b44.c
@@ -836,7 +836,7 @@
struct sk_buff *copy_skb;
b44_recycle_rx(bp, cons, bp->rx_prod);
- copy_skb = netdev_alloc_skb_ip_align(bp->dev, len);
+ copy_skb = napi_alloc_skb(&bp->napi, len);
if (copy_skb == NULL)
goto drop_it_no_recycle;
@@ -2104,6 +2104,7 @@
bp->flags &= ~B44_FLAG_WOL_ENABLE;
spin_unlock_irq(&bp->lock);
+ device_set_wakeup_enable(bp->sdev->dev, wol->wolopts & WAKE_MAGIC);
return 0;
}
@@ -2452,6 +2453,7 @@
}
}
+ device_set_wakeup_capable(sdev->dev, true);
netdev_info(dev, "%s %pM\n", DRV_DESCRIPTION, dev->dev_addr);
return 0;
diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig
index 3099c28..538d75f 100644
--- a/drivers/net/ethernet/intel/Kconfig
+++ b/drivers/net/ethernet/intel/Kconfig
@@ -22,7 +22,7 @@
tristate "Intel(R) PRO/100+ support"
depends on m
depends on PCI
- select BACKPORT_MII
+ select BPAUTO_MII
---help---
This driver supports Intel(R) PRO/100 family of adapters.
To verify that your adapter is supported, find the board ID number
@@ -92,6 +92,7 @@
will be called e1000e.
config IGB
+ depends on !KERNEL_3_5
tristate "Intel(R) 82575/82576 PCI-Express Gigabit Ethernet support"
depends on m
depends on PCI
diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c
index 196b2bf..813ab10 100644
--- a/drivers/net/ethernet/intel/igb/igb_ethtool.c
+++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c
@@ -2860,7 +2860,10 @@
return IGB_RETA_SIZE;
}
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,19,0)
+static int igb_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
+ u8 *hfunc)
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0)
static int igb_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key)
#else
static int igb_get_rxfh(struct net_device *netdev, u32 *indir)
@@ -2869,6 +2872,12 @@
struct igb_adapter *adapter = netdev_priv(netdev);
int i;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,19,0)
+ if (hfunc)
+ *hfunc = ETH_RSS_HASH_TOP;
+#endif
+ if (!indir)
+ return 0;
for (i = 0; i < IGB_RETA_SIZE; i++)
indir[i] = adapter->rss_indir_tbl[i];
@@ -2912,7 +2921,10 @@
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0)
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,19,0)
+static int igb_set_rxfh(struct net_device *netdev, const u32 *indir,
+ const u8 *key, const u8 hfunc)
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0)
static int igb_set_rxfh(struct net_device *netdev, const u32 *indir,
const u8 *key)
#else
@@ -2924,6 +2936,15 @@
int i;
u32 num_queues;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,19,0)
+ /* We do not allow change in unsupported parameters */
+ if (key ||
+ (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP))
+ return -EOPNOTSUPP;
+#endif
+ if (!indir)
+ return 0;
+
num_queues = adapter->rss_queues;
switch (hw->mac.type) {
diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c
index 18d60a2..84f0acb 100644
--- a/drivers/net/ethernet/intel/igb/igb_main.c
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
@@ -200,11 +200,9 @@
static int igb_suspend(struct device *);
#endif
static int igb_resume(struct device *);
-#ifdef CONFIG_PM_RUNTIME
static int igb_runtime_suspend(struct device *dev);
static int igb_runtime_resume(struct device *dev);
static int igb_runtime_idle(struct device *dev);
-#endif
static const struct dev_pm_ops igb_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(igb_suspend, igb_resume)
SET_RUNTIME_PM_OPS(igb_runtime_suspend, igb_runtime_resume,
@@ -1034,7 +1032,8 @@
/* igb_get_stats64() might access the rings on this vector,
* we must wait a grace period before freeing it.
*/
- kfree_rcu(q_vector, rcu);
+ if (q_vector)
+ kfree_rcu(q_vector, rcu);
}
/**
@@ -1814,8 +1813,10 @@
adapter->flags &= ~IGB_FLAG_NEED_LINK_UPDATE;
for (i = 0; i < adapter->num_q_vectors; i++) {
- napi_synchronize(&(adapter->q_vector[i]->napi));
- napi_disable(&(adapter->q_vector[i]->napi));
+ if (adapter->q_vector[i]) {
+ napi_synchronize(&adapter->q_vector[i]->napi);
+ napi_disable(&adapter->q_vector[i]->napi);
+ }
}
@@ -3400,14 +3401,11 @@
struct e1000_hw *hw = &adapter->hw;
u32 mrqc, rxcsum;
u32 j, num_rx_queues;
- static const u32 rsskey[10] = { 0xDA565A6D, 0xC20E5B25, 0x3D256741,
- 0xB08FA343, 0xCB2BCAD0, 0xB4307BAE,
- 0xA32DCB77, 0x0CF23080, 0x3BB7426A,
- 0xFA01ACBE };
+ u32 rss_key[10];
- /* Fill out hash function seeds */
+ netdev_rss_key_fill(rss_key, sizeof(rss_key));
for (j = 0; j < 10; j++)
- wr32(E1000_RSSRK(j), rsskey[j]);
+ wr32(E1000_RSSRK(j), rss_key[j]);
num_rx_queues = adapter->rss_queues;
@@ -3745,7 +3743,8 @@
int i;
for (i = 0; i < adapter->num_tx_queues; i++)
- igb_free_tx_resources(adapter->tx_ring[i]);
+ if (adapter->tx_ring[i])
+ igb_free_tx_resources(adapter->tx_ring[i]);
}
void igb_unmap_and_free_tx_resource(struct igb_ring *ring,
@@ -3810,7 +3809,8 @@
int i;
for (i = 0; i < adapter->num_tx_queues; i++)
- igb_clean_tx_ring(adapter->tx_ring[i]);
+ if (adapter->tx_ring[i])
+ igb_clean_tx_ring(adapter->tx_ring[i]);
}
/**
@@ -3847,7 +3847,8 @@
int i;
for (i = 0; i < adapter->num_rx_queues; i++)
- igb_free_rx_resources(adapter->rx_ring[i]);
+ if (adapter->rx_ring[i])
+ igb_free_rx_resources(adapter->rx_ring[i]);
}
/**
@@ -3902,7 +3903,8 @@
int i;
for (i = 0; i < adapter->num_rx_queues; i++)
- igb_clean_rx_ring(adapter->rx_ring[i]);
+ if (adapter->rx_ring[i])
+ igb_clean_rx_ring(adapter->rx_ring[i]);
}
/**
@@ -5119,12 +5121,8 @@
/* The minimum packet size with TCTL.PSP set is 17 so pad the skb
* in order to meet this minimum size requirement.
*/
- if (unlikely(skb->len < 17)) {
- if (skb_pad(skb, 17 - skb->len))
- return NETDEV_TX_OK;
- skb->len = 17;
- skb_set_tail_pointer(skb, 17);
- }
+ if (skb_put_padto(skb, 17))
+ return NETDEV_TX_OK;
return igb_xmit_frame_ring(skb, igb_tx_queue_mapping(adapter, skb));
}
@@ -6569,6 +6567,11 @@
if (unlikely(page_to_nid(page) != numa_node_id()))
return false;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)
+ if (unlikely(page->pfmemalloc))
+ return false;
+#endif
+
#if (PAGE_SIZE < 8192)
/* if we are only owner of page we can reuse it */
if (unlikely(page_count(page) != 1))
@@ -6635,7 +6638,12 @@
memcpy(__skb_put(skb, size), va, ALIGN(size, sizeof(long)));
/* we can reuse buffer as-is, just make sure it is local */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)
+ if (likely((page_to_nid(page) == numa_node_id()) &&
+ !page->pfmemalloc))
+#else
if (likely(page_to_nid(page) == numa_node_id()))
+#endif
return true;
/* this page cannot be reused so discard it */
@@ -6672,8 +6680,7 @@
#endif
/* allocate a skb to store the frags */
- skb = netdev_alloc_skb_ip_align(rx_ring->netdev,
- IGB_RX_HDR_LEN);
+ skb = napi_alloc_skb(&rx_ring->q_vector->napi, IGB_RX_HDR_LEN);
if (unlikely(!skb)) {
rx_ring->rx_stats.alloc_failed++;
return NULL;
@@ -6874,14 +6881,9 @@
if (skb_is_nonlinear(skb))
igb_pull_tail(rx_ring, rx_desc, skb);
- /* if skb_pad returns an error the skb was freed */
- if (unlikely(skb->len < 60)) {
- int pad_len = 60 - skb->len;
-
- if (skb_pad(skb, pad_len))
- return true;
- __skb_put(skb, pad_len);
- }
+ /* if eth_skb_pad returns an error the skb was freed */
+ if (eth_skb_pad(skb))
+ return true;
return false;
}
@@ -6946,14 +6948,14 @@
rx_desc = IGB_RX_DESC(rx_ring, rx_ring->next_to_clean);
- if (!igb_test_staterr(rx_desc, E1000_RXD_STAT_DD))
+ if (!rx_desc->wb.upper.status_error)
break;
/* This memory barrier is needed to keep us from reading
* any other fields out of the rx_desc until we know the
- * RXD_STAT_DD bit is set
+ * descriptor has been written back
*/
- rmb();
+ dma_rmb();
/* retrieve a buffer from the ring */
skb = igb_fetch_rx_buffer(rx_ring, rx_desc, skb);
@@ -7016,7 +7018,7 @@
return true;
/* alloc new page for storage */
- page = __skb_alloc_page(GFP_ATOMIC | __GFP_COLD, NULL);
+ page = dev_alloc_page();
if (unlikely(!page)) {
rx_ring->rx_stats.alloc_failed++;
return false;
@@ -7452,6 +7454,8 @@
pci_restore_state(pdev);
pci_save_state(pdev);
+ if (!pci_device_is_present(pdev))
+ return -ENODEV;
err = pci_enable_device_mem(pdev);
if (err) {
dev_err(&pdev->dev,
@@ -7489,7 +7493,6 @@
return 0;
}
-#ifdef CONFIG_PM_RUNTIME
static int igb_runtime_idle(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
@@ -7526,8 +7529,7 @@
{
return igb_resume(dev);
}
-#endif /* CONFIG_PM_RUNTIME */
-#endif
+#endif /* CONFIG_PM */
static void igb_shutdown(struct pci_dev *pdev)
{
diff --git a/drivers/net/ieee802154/Kconfig b/drivers/net/ieee802154/Kconfig
index 47e5bf2..271a167 100644
--- a/drivers/net/ieee802154/Kconfig
+++ b/drivers/net/ieee802154/Kconfig
@@ -11,17 +11,6 @@
If you say N, all options in this submenu will be skipped and
disabled.
-config IEEE802154_FAKEHARD
- tristate "Fake LR-WPAN driver with several interconnected devices"
- depends on m
- depends on IEEE802154_DRIVERS
- ---help---
- Say Y here to enable the fake driver that serves as an example
- of HardMAC device driver.
-
- This driver can also be built as a module. To do so say M here.
- The module will be called 'fakehard'.
-
config IEEE802154_FAKELB
depends on IEEE802154_DRIVERS && MAC802154
tristate "IEEE 802.15.4 loopback driver"
@@ -34,7 +23,7 @@
The module will be called 'fakelb'.
config IEEE802154_AT86RF230
- depends on !BACKPORT_KERNEL_3_4
+ depends on !KERNEL_3_4
depends on IEEE802154_DRIVERS && MAC802154
tristate "AT86RF230/231/233/212 transceiver driver"
depends on m
@@ -48,7 +37,7 @@
the module will be called 'at86rf230'.
config IEEE802154_MRF24J40
- depends on !BACKPORT_KERNEL_3_5
+ depends on !KERNEL_3_5
tristate "Microchip MRF24J40 transceiver driver"
depends on m
depends on IEEE802154_DRIVERS && MAC802154
@@ -61,7 +50,7 @@
the module will be called 'mrf24j40'.
config IEEE802154_CC2520
- depends on !BACKPORT_KERNEL_3_5
+ depends on !KERNEL_3_5
depends on IEEE802154_DRIVERS && MAC802154
tristate "CC2520 transceiver driver"
depends on m
diff --git a/drivers/net/ieee802154/Makefile b/drivers/net/ieee802154/Makefile
index ee1bafc..cf29a74 100644
--- a/drivers/net/ieee802154/Makefile
+++ b/drivers/net/ieee802154/Makefile
@@ -1,4 +1,3 @@
-obj-$(CPTCFG_IEEE802154_FAKEHARD) += fakehard.o
obj-$(CPTCFG_IEEE802154_FAKELB) += fakelb.o
obj-$(CPTCFG_IEEE802154_AT86RF230) += at86rf230.o
obj-$(CPTCFG_IEEE802154_MRF24J40) += mrf24j40.o
diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c
index c9d2a75..1c01356 100644
--- a/drivers/net/ieee802154/at86rf230.c
+++ b/drivers/net/ieee802154/at86rf230.c
@@ -12,10 +12,6 @@
* 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.
- *
* Written by:
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
* Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
@@ -33,10 +29,10 @@
#include <linux/regmap.h>
#include <linux/skbuff.h>
#include <linux/of_gpio.h>
+#include <linux/ieee802154.h>
-#include <net/ieee802154.h>
#include <net/mac802154.h>
-#include <net/wpan-phy.h>
+#include <net/cfg802154.h>
struct at86rf230_local;
/* at86rf2xx chip depend data.
@@ -50,15 +46,11 @@
u16 t_off_to_tx_on;
u16 t_frame;
u16 t_p_ack;
- /* short interframe spacing time */
- u16 t_sifs;
- /* long interframe spacing time */
- u16 t_lifs;
/* completion timeout for tx in msecs */
u16 t_tx_timeout;
int rssi_base_val;
- int (*set_channel)(struct at86rf230_local *, int, int);
+ int (*set_channel)(struct at86rf230_local *, u8, u8);
int (*get_desense_steps)(struct at86rf230_local *, s32);
};
@@ -74,12 +66,14 @@
void (*complete)(void *context);
u8 from_state;
u8 to_state;
+
+ bool irq_enable;
};
struct at86rf230_local {
struct spi_device *spi;
- struct ieee802154_dev *dev;
+ struct ieee802154_hw *hw;
struct at86rf2xx_chip_data *data;
struct regmap *regmap;
@@ -89,10 +83,10 @@
struct at86rf230_state_change irq;
bool tx_aret;
+ s8 max_frame_retries;
bool is_tx;
/* spinlock for is_tx protection */
spinlock_t lock;
- struct completion tx_complete;
struct sk_buff *tx_skb;
struct at86rf230_state_change tx;
};
@@ -291,10 +285,11 @@
#define AT86RF2XX_NUMREGS 0x3F
-static int
+static void
at86rf230_async_state_change(struct at86rf230_local *lp,
struct at86rf230_state_change *ctx,
- const u8 state, void (*complete)(void *context));
+ const u8 state, void (*complete)(void *context),
+ const bool irq_enable);
static inline int
__at86rf230_write(struct at86rf230_local *lp,
@@ -451,7 +446,8 @@
struct at86rf230_state_change *ctx = context;
struct at86rf230_local *lp = ctx->lp;
- at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON, NULL);
+ at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON, NULL, false);
+ ieee802154_wake_queue(lp->hw);
}
static void
@@ -461,21 +457,31 @@
dev_err(&lp->spi->dev, "spi_async error %d\n", rc);
at86rf230_async_state_change(lp, ctx, STATE_FORCE_TRX_OFF,
- at86rf230_async_error_recover);
+ at86rf230_async_error_recover, false);
}
/* Generic function to get some register value in async mode */
-static int
+static void
at86rf230_async_read_reg(struct at86rf230_local *lp, const u8 reg,
struct at86rf230_state_change *ctx,
- void (*complete)(void *context))
+ void (*complete)(void *context),
+ const bool irq_enable)
{
+ int rc;
+
u8 *tx_buf = ctx->buf;
tx_buf[0] = (reg & CMD_REG_MASK) | CMD_REG;
ctx->trx.len = 2;
ctx->msg.complete = complete;
- return spi_async(lp->spi, &ctx->msg);
+ ctx->irq_enable = irq_enable;
+ rc = spi_async(lp->spi, &ctx->msg);
+ if (rc) {
+ if (irq_enable)
+ enable_irq(lp->spi->irq);
+
+ at86rf230_async_error(lp, ctx, rc);
+ }
}
static void
@@ -512,7 +518,8 @@
if (ctx->to_state == STATE_TX_ON) {
at86rf230_async_state_change(lp, ctx,
STATE_FORCE_TX_ON,
- ctx->complete);
+ ctx->complete,
+ ctx->irq_enable);
return;
}
}
@@ -535,7 +542,6 @@
struct at86rf230_local *lp = ctx->lp;
struct at86rf2xx_chip_data *c = lp->data;
bool force = false;
- int rc;
/* The force state changes are will show as normal states in the
* state status subregister. We change the to_state to the
@@ -604,10 +610,9 @@
udelay(1);
change:
- rc = at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
- at86rf230_async_state_assert);
- if (rc)
- dev_err(&lp->spi->dev, "spi_async error %d\n", rc);
+ at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
+ at86rf230_async_state_assert,
+ ctx->irq_enable);
}
static void
@@ -622,10 +627,9 @@
/* Check for "possible" STATE_TRANSITION_IN_PROGRESS */
if (trx_state == STATE_TRANSITION_IN_PROGRESS) {
udelay(1);
- rc = at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
- at86rf230_async_state_change_start);
- if (rc)
- dev_err(&lp->spi->dev, "spi_async error %d\n", rc);
+ at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
+ at86rf230_async_state_change_start,
+ ctx->irq_enable);
return;
}
@@ -647,20 +651,27 @@
ctx->trx.len = 2;
ctx->msg.complete = at86rf230_async_state_delay;
rc = spi_async(lp->spi, &ctx->msg);
- if (rc)
- dev_err(&lp->spi->dev, "spi_async error %d\n", rc);
+ if (rc) {
+ if (ctx->irq_enable)
+ enable_irq(lp->spi->irq);
+
+ at86rf230_async_error(lp, &lp->state, rc);
+ }
}
-static int
+static void
at86rf230_async_state_change(struct at86rf230_local *lp,
struct at86rf230_state_change *ctx,
- const u8 state, void (*complete)(void *context))
+ const u8 state, void (*complete)(void *context),
+ const bool irq_enable)
{
/* Initialization for the state change context */
ctx->to_state = state;
ctx->complete = complete;
- return at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
- at86rf230_async_state_change_start);
+ ctx->irq_enable = irq_enable;
+ at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
+ at86rf230_async_state_change_start,
+ irq_enable);
}
static void
@@ -681,17 +692,16 @@
{
int rc;
- rc = at86rf230_async_state_change(lp, &lp->state, state,
- at86rf230_sync_state_change_complete);
- if (rc) {
- at86rf230_async_error(lp, &lp->state, rc);
- return rc;
- }
+ at86rf230_async_state_change(lp, &lp->state, state,
+ at86rf230_sync_state_change_complete,
+ false);
rc = wait_for_completion_timeout(&lp->state_complete,
msecs_to_jiffies(100));
- if (!rc)
+ if (!rc) {
+ at86rf230_async_error(lp, &lp->state, -ETIMEDOUT);
return -ETIMEDOUT;
+ }
return 0;
}
@@ -701,8 +711,14 @@
{
struct at86rf230_state_change *ctx = context;
struct at86rf230_local *lp = ctx->lp;
+ struct sk_buff *skb = lp->tx_skb;
- complete(&lp->tx_complete);
+ enable_irq(lp->spi->irq);
+
+ if (lp->max_frame_retries <= 0)
+ ieee802154_xmit_complete(lp->hw, skb, true);
+ else
+ ieee802154_xmit_complete(lp->hw, skb, false);
}
static void
@@ -710,12 +726,9 @@
{
struct at86rf230_state_change *ctx = context;
struct at86rf230_local *lp = ctx->lp;
- int rc;
- rc = at86rf230_async_state_change(lp, &lp->irq, STATE_RX_AACK_ON,
- at86rf230_tx_complete);
- if (rc)
- at86rf230_async_error(lp, ctx, rc);
+ at86rf230_async_state_change(lp, &lp->irq, STATE_RX_AACK_ON,
+ at86rf230_tx_complete, true);
}
static void
@@ -723,12 +736,9 @@
{
struct at86rf230_state_change *ctx = context;
struct at86rf230_local *lp = ctx->lp;
- int rc;
- rc = at86rf230_async_state_change(lp, ctx, STATE_TX_ON,
- at86rf230_tx_on);
- if (rc)
- at86rf230_async_error(lp, ctx, rc);
+ at86rf230_async_state_change(lp, ctx, STATE_TX_ON,
+ at86rf230_tx_on, true);
}
static void
@@ -738,17 +748,14 @@
struct at86rf230_local *lp = ctx->lp;
const u8 *buf = ctx->buf;
const u8 trac = (buf[1] & 0xe0) >> 5;
- int rc;
/* If trac status is different than zero we need to do a state change
* to STATE_FORCE_TRX_OFF then STATE_TX_ON to recover the transceiver
* state to TX_ON.
*/
if (trac) {
- rc = at86rf230_async_state_change(lp, ctx, STATE_FORCE_TRX_OFF,
- at86rf230_tx_trac_error);
- if (rc)
- at86rf230_async_error(lp, ctx, rc);
+ at86rf230_async_state_change(lp, ctx, STATE_FORCE_TRX_OFF,
+ at86rf230_tx_trac_error, true);
return;
}
@@ -761,51 +768,29 @@
{
struct at86rf230_state_change *ctx = context;
struct at86rf230_local *lp = ctx->lp;
- int rc;
- rc = at86rf230_async_read_reg(lp, RG_TRX_STATE, ctx,
- at86rf230_tx_trac_check);
- if (rc)
- at86rf230_async_error(lp, ctx, rc);
+ at86rf230_async_read_reg(lp, RG_TRX_STATE, ctx,
+ at86rf230_tx_trac_check, true);
}
static void
at86rf230_rx(struct at86rf230_local *lp,
- const u8 *data, u8 len)
+ const u8 *data, const u8 len, const u8 lqi)
{
- u8 lqi;
struct sk_buff *skb;
u8 rx_local_buf[AT86RF2XX_MAX_BUF];
- if (len < 2)
- return;
-
- /* read full frame buffer and invalid lqi value to lowest
- * indicator if frame was is in a corrupted state.
- */
- if (len > IEEE802154_MTU) {
- lqi = 0;
- len = IEEE802154_MTU;
- dev_vdbg(&lp->spi->dev, "corrupted frame received\n");
- } else {
- lqi = data[len];
- }
-
memcpy(rx_local_buf, data, len);
enable_irq(lp->spi->irq);
- skb = alloc_skb(IEEE802154_MTU, GFP_ATOMIC);
+ skb = dev_alloc_skb(IEEE802154_MTU);
if (!skb) {
dev_vdbg(&lp->spi->dev, "failed to allocate sk_buff\n");
return;
}
memcpy(skb_put(skb, len), rx_local_buf, len);
-
- /* We do not put CRC into the frame */
- skb_trim(skb, len - 2);
-
- ieee802154_rx_irqsafe(lp->dev, skb, lqi);
+ ieee802154_rx_irqsafe(lp->hw, skb, lqi);
}
static void
@@ -814,20 +799,31 @@
struct at86rf230_state_change *ctx = context;
struct at86rf230_local *lp = ctx->lp;
const u8 *buf = lp->irq.buf;
- const u8 len = buf[1];
+ u8 len = buf[1];
- at86rf230_rx(lp, buf + 2, len);
+ if (!ieee802154_is_valid_psdu_len(len)) {
+ dev_vdbg(&lp->spi->dev, "corrupted frame received\n");
+ len = IEEE802154_MTU;
+ }
+
+ at86rf230_rx(lp, buf + 2, len, buf[2 + len]);
}
-static int
+static void
at86rf230_rx_read_frame(struct at86rf230_local *lp)
{
+ int rc;
+
u8 *buf = lp->irq.buf;
buf[0] = CMD_FB;
lp->irq.trx.len = AT86RF2XX_MAX_BUF;
lp->irq.msg.complete = at86rf230_rx_read_frame_complete;
- return spi_async(lp->spi, &lp->irq.msg);
+ rc = spi_async(lp->spi, &lp->irq.msg);
+ if (rc) {
+ enable_irq(lp->spi->irq);
+ at86rf230_async_error(lp, &lp->irq, rc);
+ }
}
static void
@@ -835,7 +831,6 @@
{
struct at86rf230_state_change *ctx = context;
struct at86rf230_local *lp = ctx->lp;
- int rc;
/* Possible check on trac status here. This could be useful to make
* some stats why receive is failed. Not used at the moment, but it's
@@ -843,34 +838,31 @@
* The programming guide say do it so.
*/
- rc = at86rf230_rx_read_frame(lp);
- if (rc) {
- enable_irq(lp->spi->irq);
- at86rf230_async_error(lp, ctx, rc);
- }
+ at86rf230_rx_read_frame(lp);
}
-static int
+static void
at86rf230_irq_trx_end(struct at86rf230_local *lp)
{
spin_lock(&lp->lock);
if (lp->is_tx) {
lp->is_tx = 0;
spin_unlock(&lp->lock);
- enable_irq(lp->spi->irq);
if (lp->tx_aret)
- return at86rf230_async_state_change(lp, &lp->irq,
- STATE_FORCE_TX_ON,
- at86rf230_tx_trac_status);
+ at86rf230_async_state_change(lp, &lp->irq,
+ STATE_FORCE_TX_ON,
+ at86rf230_tx_trac_status,
+ true);
else
- return at86rf230_async_state_change(lp, &lp->irq,
- STATE_RX_AACK_ON,
- at86rf230_tx_complete);
+ at86rf230_async_state_change(lp, &lp->irq,
+ STATE_RX_AACK_ON,
+ at86rf230_tx_complete,
+ true);
} else {
spin_unlock(&lp->lock);
- return at86rf230_async_read_reg(lp, RG_TRX_STATE, &lp->irq,
- at86rf230_rx_trac_check);
+ at86rf230_async_read_reg(lp, RG_TRX_STATE, &lp->irq,
+ at86rf230_rx_trac_check, true);
}
}
@@ -881,12 +873,9 @@
struct at86rf230_local *lp = ctx->lp;
const u8 *buf = lp->irq.buf;
const u8 irq = buf[1];
- int rc;
if (irq & IRQ_TRX_END) {
- rc = at86rf230_irq_trx_end(lp);
- if (rc)
- at86rf230_async_error(lp, ctx, rc);
+ at86rf230_irq_trx_end(lp);
} else {
enable_irq(lp->spi->irq);
dev_err(&lp->spi->dev, "not supported irq %02x received\n",
@@ -901,13 +890,14 @@
u8 *buf = ctx->buf;
int rc;
- disable_irq_nosync(lp->spi->irq);
+ disable_irq_nosync(irq);
buf[0] = (RG_IRQ_STATUS & CMD_REG_MASK) | CMD_REG;
ctx->trx.len = 2;
ctx->msg.complete = at86rf230_irq_status;
rc = spi_async(lp->spi, &ctx->msg);
if (rc) {
+ enable_irq(irq);
at86rf230_async_error(lp, ctx, rc);
return IRQ_NONE;
}
@@ -960,22 +950,18 @@
{
struct at86rf230_state_change *ctx = context;
struct at86rf230_local *lp = ctx->lp;
- int rc;
- rc = at86rf230_async_state_change(lp, ctx, STATE_TX_ARET_ON,
- at86rf230_write_frame);
- if (rc)
- at86rf230_async_error(lp, ctx, rc);
+ at86rf230_async_state_change(lp, ctx, STATE_TX_ARET_ON,
+ at86rf230_write_frame, false);
}
static int
-at86rf230_xmit(struct ieee802154_dev *dev, struct sk_buff *skb)
+at86rf230_xmit(struct ieee802154_hw *hw, struct sk_buff *skb)
{
- struct at86rf230_local *lp = dev->priv;
+ struct at86rf230_local *lp = hw->priv;
struct at86rf230_state_change *ctx = &lp->tx;
void (*tx_complete)(void *context) = at86rf230_write_frame;
- int rc;
lp->tx_skb = skb;
@@ -986,61 +972,39 @@
if (lp->tx_aret)
tx_complete = at86rf230_xmit_tx_on;
- rc = at86rf230_async_state_change(lp, ctx, STATE_TX_ON,
- tx_complete);
- if (rc) {
- at86rf230_async_error(lp, ctx, rc);
- return rc;
- }
- rc = wait_for_completion_interruptible_timeout(&lp->tx_complete,
- msecs_to_jiffies(lp->data->t_tx_timeout));
- if (!rc) {
- at86rf230_async_error(lp, ctx, rc);
- return -ETIMEDOUT;
- }
-
- /* Interfame spacing time, which is phy depend.
- * TODO
- * Move this handling in MAC 802.15.4 layer.
- * This is currently a workaround to avoid fragmenation issues.
- */
- if (skb->len > 18)
- usleep_range(lp->data->t_lifs, lp->data->t_lifs + 10);
- else
- usleep_range(lp->data->t_sifs, lp->data->t_sifs + 10);
+ at86rf230_async_state_change(lp, ctx, STATE_TX_ON, tx_complete, false);
return 0;
}
static int
-at86rf230_ed(struct ieee802154_dev *dev, u8 *level)
+at86rf230_ed(struct ieee802154_hw *hw, u8 *level)
{
- might_sleep();
BUG_ON(!level);
*level = 0xbe;
return 0;
}
static int
-at86rf230_start(struct ieee802154_dev *dev)
+at86rf230_start(struct ieee802154_hw *hw)
{
- return at86rf230_sync_state_change(dev->priv, STATE_RX_AACK_ON);
+ return at86rf230_sync_state_change(hw->priv, STATE_RX_AACK_ON);
}
static void
-at86rf230_stop(struct ieee802154_dev *dev)
+at86rf230_stop(struct ieee802154_hw *hw)
{
- at86rf230_sync_state_change(dev->priv, STATE_FORCE_TRX_OFF);
+ at86rf230_sync_state_change(hw->priv, STATE_FORCE_TRX_OFF);
}
static int
-at86rf23x_set_channel(struct at86rf230_local *lp, int page, int channel)
+at86rf23x_set_channel(struct at86rf230_local *lp, u8 page, u8 channel)
{
return at86rf230_write_subreg(lp, SR_CHANNEL, channel);
}
static int
-at86rf212_set_channel(struct at86rf230_local *lp, int page, int channel)
+at86rf212_set_channel(struct at86rf230_local *lp, u8 page, u8 channel)
{
int rc;
@@ -1061,44 +1025,60 @@
if (rc < 0)
return rc;
+ /* This sets the symbol_duration according frequency on the 212.
+ * TODO move this handling while set channel and page in cfg802154.
+ * We can do that, this timings are according 802.15.4 standard.
+ * If we do that in cfg802154, this is a more generic calculation.
+ *
+ * This should also protected from ifs_timer. Means cancel timer and
+ * init with a new value. For now, this is okay.
+ */
+ if (channel == 0) {
+ if (page == 0) {
+ /* SUB:0 and BPSK:0 -> BPSK-20 */
+ lp->hw->phy->symbol_duration = 50;
+ } else {
+ /* SUB:1 and BPSK:0 -> BPSK-40 */
+ lp->hw->phy->symbol_duration = 25;
+ }
+ } else {
+ if (page == 0)
+ /* SUB:0 and BPSK:1 -> OQPSK-100/200/400 */
+ lp->hw->phy->symbol_duration = 40;
+ else
+ /* SUB:1 and BPSK:1 -> OQPSK-250/500/1000 */
+ lp->hw->phy->symbol_duration = 16;
+ }
+
+ lp->hw->phy->lifs_period = IEEE802154_LIFS_PERIOD *
+ lp->hw->phy->symbol_duration;
+ lp->hw->phy->sifs_period = IEEE802154_SIFS_PERIOD *
+ lp->hw->phy->symbol_duration;
+
return at86rf230_write_subreg(lp, SR_CHANNEL, channel);
}
static int
-at86rf230_channel(struct ieee802154_dev *dev, int page, int channel)
+at86rf230_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
{
- struct at86rf230_local *lp = dev->priv;
+ struct at86rf230_local *lp = hw->priv;
int rc;
- might_sleep();
-
- if (page < 0 || page > 31 ||
- !(lp->dev->phy->channels_supported[page] & BIT(channel))) {
- WARN_ON(1);
- return -EINVAL;
- }
-
rc = lp->data->set_channel(lp, page, channel);
- if (rc < 0)
- return rc;
-
/* Wait for PLL */
usleep_range(lp->data->t_channel_switch,
lp->data->t_channel_switch + 10);
- dev->phy->current_channel = channel;
- dev->phy->current_page = page;
-
- return 0;
+ return rc;
}
static int
-at86rf230_set_hw_addr_filt(struct ieee802154_dev *dev,
+at86rf230_set_hw_addr_filt(struct ieee802154_hw *hw,
struct ieee802154_hw_addr_filt *filt,
unsigned long changed)
{
- struct at86rf230_local *lp = dev->priv;
+ struct at86rf230_local *lp = hw->priv;
- if (changed & IEEE802515_AFILT_SADDR_CHANGED) {
+ if (changed & IEEE802154_AFILT_SADDR_CHANGED) {
u16 addr = le16_to_cpu(filt->short_addr);
dev_vdbg(&lp->spi->dev,
@@ -1107,7 +1087,7 @@
__at86rf230_write(lp, RG_SHORT_ADDR_1, addr >> 8);
}
- if (changed & IEEE802515_AFILT_PANID_CHANGED) {
+ if (changed & IEEE802154_AFILT_PANID_CHANGED) {
u16 pan = le16_to_cpu(filt->pan_id);
dev_vdbg(&lp->spi->dev,
@@ -1116,7 +1096,7 @@
__at86rf230_write(lp, RG_PAN_ID_1, pan >> 8);
}
- if (changed & IEEE802515_AFILT_IEEEADDR_CHANGED) {
+ if (changed & IEEE802154_AFILT_IEEEADDR_CHANGED) {
u8 i, addr[8];
memcpy(addr, &filt->ieee_addr, 8);
@@ -1126,7 +1106,7 @@
__at86rf230_write(lp, RG_IEEE_ADDR_0 + i, addr[i]);
}
- if (changed & IEEE802515_AFILT_PANC_CHANGED) {
+ if (changed & IEEE802154_AFILT_PANC_CHANGED) {
dev_vdbg(&lp->spi->dev,
"at86rf230_set_hw_addr_filt called for panc change\n");
if (filt->pan_coord)
@@ -1139,9 +1119,9 @@
}
static int
-at86rf230_set_txpower(struct ieee802154_dev *dev, int db)
+at86rf230_set_txpower(struct ieee802154_hw *hw, int db)
{
- struct at86rf230_local *lp = dev->priv;
+ struct at86rf230_local *lp = hw->priv;
/* typical maximum output is 5dBm with RG_PHY_TX_PWR 0x60, lower five
* bits decrease power in 1dB steps. 0x60 represents extra PA gain of
@@ -1158,17 +1138,17 @@
}
static int
-at86rf230_set_lbt(struct ieee802154_dev *dev, bool on)
+at86rf230_set_lbt(struct ieee802154_hw *hw, bool on)
{
- struct at86rf230_local *lp = dev->priv;
+ struct at86rf230_local *lp = hw->priv;
return at86rf230_write_subreg(lp, SR_CSMA_LBT_MODE, on);
}
static int
-at86rf230_set_cca_mode(struct ieee802154_dev *dev, u8 mode)
+at86rf230_set_cca_mode(struct ieee802154_hw *hw, u8 mode)
{
- struct at86rf230_local *lp = dev->priv;
+ struct at86rf230_local *lp = hw->priv;
return at86rf230_write_subreg(lp, SR_CCA_MODE, mode);
}
@@ -1186,9 +1166,9 @@
}
static int
-at86rf230_set_cca_ed_level(struct ieee802154_dev *dev, s32 level)
+at86rf230_set_cca_ed_level(struct ieee802154_hw *hw, s32 level)
{
- struct at86rf230_local *lp = dev->priv;
+ struct at86rf230_local *lp = hw->priv;
if (level < lp->data->rssi_base_val || level > 30)
return -EINVAL;
@@ -1198,15 +1178,12 @@
}
static int
-at86rf230_set_csma_params(struct ieee802154_dev *dev, u8 min_be, u8 max_be,
+at86rf230_set_csma_params(struct ieee802154_hw *hw, u8 min_be, u8 max_be,
u8 retries)
{
- struct at86rf230_local *lp = dev->priv;
+ struct at86rf230_local *lp = hw->priv;
int rc;
- if (min_be > max_be || max_be > 8 || retries > 5)
- return -EINVAL;
-
rc = at86rf230_write_subreg(lp, SR_MIN_BE, min_be);
if (rc)
return rc;
@@ -1219,15 +1196,13 @@
}
static int
-at86rf230_set_frame_retries(struct ieee802154_dev *dev, s8 retries)
+at86rf230_set_frame_retries(struct ieee802154_hw *hw, s8 retries)
{
- struct at86rf230_local *lp = dev->priv;
+ struct at86rf230_local *lp = hw->priv;
int rc = 0;
- if (retries < -1 || retries > 15)
- return -EINVAL;
-
lp->tx_aret = retries >= 0;
+ lp->max_frame_retries = retries;
if (retries >= 0)
rc = at86rf230_write_subreg(lp, SR_MAX_FRAME_RETRIES, retries);
@@ -1235,9 +1210,36 @@
return rc;
}
-static struct ieee802154_ops at86rf230_ops = {
+static int
+at86rf230_set_promiscuous_mode(struct ieee802154_hw *hw, const bool on)
+{
+ struct at86rf230_local *lp = hw->priv;
+ int rc;
+
+ if (on) {
+ rc = at86rf230_write_subreg(lp, SR_AACK_DIS_ACK, 1);
+ if (rc < 0)
+ return rc;
+
+ rc = at86rf230_write_subreg(lp, SR_AACK_PROM_MODE, 1);
+ if (rc < 0)
+ return rc;
+ } else {
+ rc = at86rf230_write_subreg(lp, SR_AACK_PROM_MODE, 0);
+ if (rc < 0)
+ return rc;
+
+ rc = at86rf230_write_subreg(lp, SR_AACK_DIS_ACK, 0);
+ if (rc < 0)
+ return rc;
+ }
+
+ return 0;
+}
+
+static const struct ieee802154_ops at86rf230_ops = {
.owner = THIS_MODULE,
- .xmit = at86rf230_xmit,
+ .xmit_async = at86rf230_xmit,
.ed = at86rf230_ed,
.set_channel = at86rf230_channel,
.start = at86rf230_start,
@@ -1249,6 +1251,7 @@
.set_cca_ed_level = at86rf230_set_cca_ed_level,
.set_csma_params = at86rf230_set_csma_params,
.set_frame_retries = at86rf230_set_frame_retries,
+ .set_promiscuous_mode = at86rf230_set_promiscuous_mode,
};
static struct at86rf2xx_chip_data at86rf233_data = {
@@ -1259,8 +1262,6 @@
.t_off_to_tx_on = 80,
.t_frame = 4096,
.t_p_ack = 545,
- .t_sifs = 192,
- .t_lifs = 480,
.t_tx_timeout = 2000,
.rssi_base_val = -91,
.set_channel = at86rf23x_set_channel,
@@ -1275,8 +1276,6 @@
.t_off_to_tx_on = 110,
.t_frame = 4096,
.t_p_ack = 545,
- .t_sifs = 192,
- .t_lifs = 480,
.t_tx_timeout = 2000,
.rssi_base_val = -91,
.set_channel = at86rf23x_set_channel,
@@ -1291,8 +1290,6 @@
.t_off_to_tx_on = 200,
.t_frame = 4096,
.t_p_ack = 545,
- .t_sifs = 192,
- .t_lifs = 480,
.t_tx_timeout = 2000,
.rssi_base_val = -100,
.set_channel = at86rf212_set_channel,
@@ -1354,7 +1351,11 @@
return -EINVAL;
}
- return 0;
+ /* Force setting slotted operation bit to 0. Sometimes the atben
+ * sets this bit and I don't know why. We set this always force
+ * to zero while probing.
+ */
+ return at86rf230_write_subreg(lp, SR_SLOTTED_OPERATION, 0);
}
static struct at86rf230_platform_data *
@@ -1409,9 +1410,10 @@
return -EINVAL;
}
- lp->dev->extra_tx_headroom = 0;
- lp->dev->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AACK |
- IEEE802154_HW_TXPOWER | IEEE802154_HW_CSMA;
+ lp->hw->extra_tx_headroom = 0;
+ lp->hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AACK |
+ IEEE802154_HW_TXPOWER | IEEE802154_HW_ARET |
+ IEEE802154_HW_AFILT | IEEE802154_HW_PROMISCUOUS;
switch (part) {
case 2:
@@ -1421,15 +1423,19 @@
case 3:
chip = "at86rf231";
lp->data = &at86rf231_data;
- lp->dev->phy->channels_supported[0] = 0x7FFF800;
+ lp->hw->phy->channels_supported[0] = 0x7FFF800;
+ lp->hw->phy->current_channel = 11;
+ lp->hw->phy->symbol_duration = 16;
break;
case 7:
chip = "at86rf212";
if (version == 1) {
lp->data = &at86rf212_data;
- lp->dev->flags |= IEEE802154_HW_LBT;
- lp->dev->phy->channels_supported[0] = 0x00007FF;
- lp->dev->phy->channels_supported[2] = 0x00007FF;
+ lp->hw->flags |= IEEE802154_HW_LBT;
+ lp->hw->phy->channels_supported[0] = 0x00007FF;
+ lp->hw->phy->channels_supported[2] = 0x00007FF;
+ lp->hw->phy->current_channel = 5;
+ lp->hw->phy->symbol_duration = 25;
} else {
rc = -ENOTSUPP;
}
@@ -1437,7 +1443,9 @@
case 11:
chip = "at86rf233";
lp->data = &at86rf233_data;
- lp->dev->phy->channels_supported[0] = 0x7FFF800;
+ lp->hw->phy->channels_supported[0] = 0x7FFF800;
+ lp->hw->phy->current_channel = 13;
+ lp->hw->phy->symbol_duration = 16;
break;
default:
chip = "unkown";
@@ -1478,7 +1486,7 @@
static int at86rf230_probe(struct spi_device *spi)
{
struct at86rf230_platform_data *pdata;
- struct ieee802154_dev *dev;
+ struct ieee802154_hw *hw;
struct at86rf230_local *lp;
unsigned int status;
int rc, irq_type;
@@ -1517,14 +1525,16 @@
usleep_range(120, 240);
}
- dev = ieee802154_alloc_device(sizeof(*lp), &at86rf230_ops);
- if (!dev)
+ hw = ieee802154_alloc_hw(sizeof(*lp), &at86rf230_ops);
+ if (!hw)
return -ENOMEM;
- lp = dev->priv;
- lp->dev = dev;
+ lp = hw->priv;
+ lp->hw = hw;
lp->spi = spi;
- dev->parent = &spi->dev;
+ hw->parent = &spi->dev;
+ hw->vif_data_size = sizeof(*lp);
+ ieee802154_random_extended_addr(&hw->phy->perm_extended_addr);
lp->regmap = devm_regmap_init_spi(spi, &at86rf230_regmap_spi_config);
if (IS_ERR(lp->regmap)) {
@@ -1541,7 +1551,6 @@
goto free_dev;
spin_lock_init(&lp->lock);
- init_completion(&lp->tx_complete);
init_completion(&lp->state_complete);
spi_set_drvdata(spi, lp);
@@ -1564,14 +1573,14 @@
if (rc)
goto free_dev;
- rc = ieee802154_register_device(lp->dev);
+ rc = ieee802154_register_hw(lp->hw);
if (rc)
goto free_dev;
return rc;
free_dev:
- ieee802154_free_device(lp->dev);
+ ieee802154_free_hw(lp->hw);
return rc;
}
@@ -1582,8 +1591,8 @@
/* mask all at86rf230 irq's */
at86rf230_write_subreg(lp, SR_IRQ_MASK, 0);
- ieee802154_unregister_device(lp->dev);
- ieee802154_free_device(lp->dev);
+ ieee802154_unregister_hw(lp->hw);
+ ieee802154_free_hw(lp->hw);
dev_dbg(&spi->dev, "unregistered at86rf230\n");
return 0;
diff --git a/drivers/net/ieee802154/cc2520.c b/drivers/net/ieee802154/cc2520.c
index 8a5ac7a..f9df9fa 100644
--- a/drivers/net/ieee802154/cc2520.c
+++ b/drivers/net/ieee802154/cc2520.c
@@ -21,10 +21,10 @@
#include <linux/skbuff.h>
#include <linux/pinctrl/consumer.h>
#include <linux/of_gpio.h>
+#include <linux/ieee802154.h>
#include <net/mac802154.h>
-#include <net/wpan-phy.h>
-#include <net/ieee802154.h>
+#include <net/cfg802154.h>
#define SPI_COMMAND_BUFFER 3
#define HIGH 1
@@ -193,7 +193,7 @@
/* Driver private information */
struct cc2520_private {
struct spi_device *spi; /* SPI device structure */
- struct ieee802154_dev *dev; /* IEEE-802.15.4 device */
+ struct ieee802154_hw *hw; /* IEEE-802.15.4 device */
u8 *buf; /* SPI TX/Rx data buffer */
struct mutex buffer_mutex; /* SPI buffer mutex */
bool is_tx; /* Flag for sync b/w Tx and Rx */
@@ -453,20 +453,20 @@
return status;
}
-static int cc2520_start(struct ieee802154_dev *dev)
+static int cc2520_start(struct ieee802154_hw *hw)
{
- return cc2520_cmd_strobe(dev->priv, CC2520_CMD_SRXON);
+ return cc2520_cmd_strobe(hw->priv, CC2520_CMD_SRXON);
}
-static void cc2520_stop(struct ieee802154_dev *dev)
+static void cc2520_stop(struct ieee802154_hw *hw)
{
- cc2520_cmd_strobe(dev->priv, CC2520_CMD_SRFOFF);
+ cc2520_cmd_strobe(hw->priv, CC2520_CMD_SRFOFF);
}
static int
-cc2520_tx(struct ieee802154_dev *dev, struct sk_buff *skb)
+cc2520_tx(struct ieee802154_hw *hw, struct sk_buff *skb)
{
- struct cc2520_private *priv = dev->priv;
+ struct cc2520_private *priv = hw->priv;
unsigned long flags;
int rc;
u8 status = 0;
@@ -524,7 +524,7 @@
if (len < 2 || len > IEEE802154_MTU)
return -EINVAL;
- skb = alloc_skb(len, GFP_KERNEL);
+ skb = dev_alloc_skb(len);
if (!skb)
return -ENOMEM;
@@ -536,7 +536,7 @@
skb_trim(skb, skb->len - 2);
- ieee802154_rx_irqsafe(priv->dev, skb, lqi);
+ ieee802154_rx_irqsafe(priv->hw, skb, lqi);
dev_vdbg(&priv->spi->dev, "RXFIFO: %x %x\n", len, lqi);
@@ -544,9 +544,9 @@
}
static int
-cc2520_ed(struct ieee802154_dev *dev, u8 *level)
+cc2520_ed(struct ieee802154_hw *hw, u8 *level)
{
- struct cc2520_private *priv = dev->priv;
+ struct cc2520_private *priv = hw->priv;
u8 status = 0xff;
u8 rssi;
int ret;
@@ -569,12 +569,11 @@
}
static int
-cc2520_set_channel(struct ieee802154_dev *dev, int page, int channel)
+cc2520_set_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
{
- struct cc2520_private *priv = dev->priv;
+ struct cc2520_private *priv = hw->priv;
int ret;
- might_sleep();
dev_dbg(&priv->spi->dev, "trying to set channel\n");
BUG_ON(page != 0);
@@ -588,12 +587,12 @@
}
static int
-cc2520_filter(struct ieee802154_dev *dev,
+cc2520_filter(struct ieee802154_hw *hw,
struct ieee802154_hw_addr_filt *filt, unsigned long changed)
{
- struct cc2520_private *priv = dev->priv;
+ struct cc2520_private *priv = hw->priv;
- if (changed & IEEE802515_AFILT_PANID_CHANGED) {
+ if (changed & IEEE802154_AFILT_PANID_CHANGED) {
u16 panid = le16_to_cpu(filt->pan_id);
dev_vdbg(&priv->spi->dev,
@@ -602,7 +601,7 @@
sizeof(panid), (u8 *)&panid);
}
- if (changed & IEEE802515_AFILT_IEEEADDR_CHANGED) {
+ if (changed & IEEE802154_AFILT_IEEEADDR_CHANGED) {
dev_vdbg(&priv->spi->dev,
"cc2520_filter called for IEEE addr\n");
cc2520_write_ram(priv, CC2520RAM_IEEEADDR,
@@ -610,7 +609,7 @@
(u8 *)&filt->ieee_addr);
}
- if (changed & IEEE802515_AFILT_SADDR_CHANGED) {
+ if (changed & IEEE802154_AFILT_SADDR_CHANGED) {
u16 addr = le16_to_cpu(filt->short_addr);
dev_vdbg(&priv->spi->dev,
@@ -619,7 +618,7 @@
sizeof(addr), (u8 *)&addr);
}
- if (changed & IEEE802515_AFILT_PANC_CHANGED) {
+ if (changed & IEEE802154_AFILT_PANC_CHANGED) {
dev_vdbg(&priv->spi->dev,
"cc2520_filter called for panc change\n");
if (filt->pan_coord)
@@ -631,11 +630,11 @@
return 0;
}
-static struct ieee802154_ops cc2520_ops = {
+static const struct ieee802154_ops cc2520_ops = {
.owner = THIS_MODULE,
.start = cc2520_start,
.stop = cc2520_stop,
- .xmit = cc2520_tx,
+ .xmit_sync = cc2520_tx,
.ed = cc2520_ed,
.set_channel = cc2520_set_channel,
.set_hw_addr_filt = cc2520_filter,
@@ -645,27 +644,29 @@
{
int ret = -ENOMEM;
- priv->dev = ieee802154_alloc_device(sizeof(*priv), &cc2520_ops);
- if (!priv->dev)
+ priv->hw = ieee802154_alloc_hw(sizeof(*priv), &cc2520_ops);
+ if (!priv->hw)
goto err_ret;
- priv->dev->priv = priv;
- priv->dev->parent = &priv->spi->dev;
- priv->dev->extra_tx_headroom = 0;
+ priv->hw->priv = priv;
+ priv->hw->parent = &priv->spi->dev;
+ priv->hw->extra_tx_headroom = 0;
+ priv->hw->vif_data_size = sizeof(*priv);
/* We do support only 2.4 Ghz */
- priv->dev->phy->channels_supported[0] = 0x7FFF800;
- priv->dev->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AACK;
+ priv->hw->phy->channels_supported[0] = 0x7FFF800;
+ priv->hw->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AACK |
+ IEEE802154_HW_AFILT;
dev_vdbg(&priv->spi->dev, "registered cc2520\n");
- ret = ieee802154_register_device(priv->dev);
+ ret = ieee802154_register_hw(priv->hw);
if (ret)
goto err_free_device;
return 0;
err_free_device:
- ieee802154_free_device(priv->dev);
+ ieee802154_free_hw(priv->hw);
err_ret:
return ret;
}
@@ -857,7 +858,7 @@
pinctrl = devm_pinctrl_get_select_default(&spi->dev);
if (IS_ERR(pinctrl))
dev_warn(&spi->dev,
- "pinctrl pins are not configured");
+ "pinctrl pins are not configured\n");
pdata = cc2520_get_platform_data(spi);
if (!pdata) {
@@ -1002,8 +1003,8 @@
mutex_destroy(&priv->buffer_mutex);
flush_work(&priv->fifop_irqwork);
- ieee802154_unregister_device(priv->dev);
- ieee802154_free_device(priv->dev);
+ ieee802154_unregister_hw(priv->hw);
+ ieee802154_free_hw(priv->hw);
return 0;
}
diff --git a/drivers/net/ieee802154/fakehard.c b/drivers/net/ieee802154/fakehard.c
deleted file mode 100644
index 9ce854f..0000000
--- a/drivers/net/ieee802154/fakehard.c
+++ /dev/null
@@ -1,427 +0,0 @@
-/*
- * Sample driver for HardMAC IEEE 802.15.4 devices
- *
- * Copyright (C) 2009 Siemens AG
- *
- * 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.
- *
- * 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.
- *
- * Written by:
- * Dmitry Eremin-Solenikov <dmitry.baryshkov@siemens.com>
- */
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <linux/if_arp.h>
-
-#include <net/af_ieee802154.h>
-#include <net/ieee802154_netdev.h>
-#include <net/ieee802154.h>
-#include <net/nl802154.h>
-#include <net/wpan-phy.h>
-
-struct fakehard_priv {
- struct wpan_phy *phy;
-};
-
-static struct wpan_phy *fake_to_phy(const struct net_device *dev)
-{
- struct fakehard_priv *priv = netdev_priv(dev);
- return priv->phy;
-}
-
-/**
- * fake_get_phy - Return a phy corresponding to this device.
- * @dev: The network device for which to return the wan-phy object
- *
- * This function returns a wpan-phy object corresponding to the passed
- * network device. Reference counter for wpan-phy object is incremented,
- * so when the wpan-phy isn't necessary, you should drop the reference
- * via @wpan_phy_put() call.
- */
-static struct wpan_phy *fake_get_phy(const struct net_device *dev)
-{
- struct wpan_phy *phy = fake_to_phy(dev);
- return to_phy(get_device(&phy->dev));
-}
-
-/**
- * fake_get_pan_id - Retrieve the PAN ID of the device.
- * @dev: The network device to retrieve the PAN of.
- *
- * Return the ID of the PAN from the PIB.
- */
-static __le16 fake_get_pan_id(const struct net_device *dev)
-{
- BUG_ON(dev->type != ARPHRD_IEEE802154);
-
- return cpu_to_le16(0xeba1);
-}
-
-/**
- * fake_get_short_addr - Retrieve the short address of the device.
- * @dev: The network device to retrieve the short address of.
- *
- * Returns the IEEE 802.15.4 short-form address cached for this
- * device. If the device has not yet had a short address assigned
- * then this should return 0xFFFF to indicate a lack of association.
- */
-static __le16 fake_get_short_addr(const struct net_device *dev)
-{
- BUG_ON(dev->type != ARPHRD_IEEE802154);
-
- return cpu_to_le16(0x1);
-}
-
-/**
- * fake_get_dsn - Retrieve the DSN of the device.
- * @dev: The network device to retrieve the DSN for.
- *
- * Returns the IEEE 802.15.4 DSN for the network device.
- * The DSN is the sequence number which will be added to each
- * packet or MAC command frame by the MAC during transmission.
- *
- * DSN means 'Data Sequence Number'.
- *
- * Note: This is in section 7.2.1.2 of the IEEE 802.15.4-2006
- * document.
- */
-static u8 fake_get_dsn(const struct net_device *dev)
-{
- BUG_ON(dev->type != ARPHRD_IEEE802154);
-
- return 0x00; /* DSN are implemented in HW, so return just 0 */
-}
-
-/**
- * fake_assoc_req - Make an association request to the HW.
- * @dev: The network device which we are associating to a network.
- * @addr: The coordinator with which we wish to associate.
- * @channel: The channel on which to associate.
- * @cap: The capability information field to use in the association.
- *
- * Start an association with a coordinator. The coordinator's address
- * and PAN ID can be found in @addr.
- *
- * Note: This is in section 7.3.1 and 7.5.3.1 of the IEEE
- * 802.15.4-2006 document.
- */
-static int fake_assoc_req(struct net_device *dev,
- struct ieee802154_addr *addr, u8 channel, u8 page, u8 cap)
-{
- struct wpan_phy *phy = fake_to_phy(dev);
-
- mutex_lock(&phy->pib_lock);
- phy->current_channel = channel;
- phy->current_page = page;
- mutex_unlock(&phy->pib_lock);
-
- /* We simply emulate it here */
- return ieee802154_nl_assoc_confirm(dev, fake_get_short_addr(dev),
- IEEE802154_SUCCESS);
-}
-
-/**
- * fake_assoc_resp - Send an association response to a device.
- * @dev: The network device on which to send the response.
- * @addr: The address of the device to respond to.
- * @short_addr: The assigned short address for the device (if any).
- * @status: The result of the association request.
- *
- * Queue the association response of the coordinator to another
- * device's attempt to associate with the network which we
- * coordinate. This is then added to the indirect-send queue to be
- * transmitted to the end device when it polls for data.
- *
- * Note: This is in section 7.3.2 and 7.5.3.1 of the IEEE
- * 802.15.4-2006 document.
- */
-static int fake_assoc_resp(struct net_device *dev,
- struct ieee802154_addr *addr, __le16 short_addr, u8 status)
-{
- return 0;
-}
-
-/**
- * fake_disassoc_req - Disassociate a device from a network.
- * @dev: The network device on which we're disassociating a device.
- * @addr: The device to disassociate from the network.
- * @reason: The reason to give to the device for being disassociated.
- *
- * This sends a disassociation notification to the device being
- * disassociated from the network.
- *
- * Note: This is in section 7.5.3.2 of the IEEE 802.15.4-2006
- * document, with the reason described in 7.3.3.2.
- */
-static int fake_disassoc_req(struct net_device *dev,
- struct ieee802154_addr *addr, u8 reason)
-{
- return ieee802154_nl_disassoc_confirm(dev, IEEE802154_SUCCESS);
-}
-
-/**
- * fake_start_req - Start an IEEE 802.15.4 PAN.
- * @dev: The network device on which to start the PAN.
- * @addr: The coordinator address to use when starting the PAN.
- * @channel: The channel on which to start the PAN.
- * @bcn_ord: Beacon order.
- * @sf_ord: Superframe order.
- * @pan_coord: Whether or not we are the PAN coordinator or just
- * requesting a realignment perhaps?
- * @blx: Battery Life Extension feature bitfield.
- * @coord_realign: Something to realign something else.
- *
- * If pan_coord is non-zero then this starts a network with the
- * provided parameters, otherwise it attempts a coordinator
- * realignment of the stated network instead.
- *
- * Note: This is in section 7.5.2.3 of the IEEE 802.15.4-2006
- * document, with 7.3.8 describing coordinator realignment.
- */
-static int fake_start_req(struct net_device *dev,
- struct ieee802154_addr *addr, u8 channel, u8 page,
- u8 bcn_ord, u8 sf_ord, u8 pan_coord, u8 blx,
- u8 coord_realign)
-{
- struct wpan_phy *phy = fake_to_phy(dev);
-
- mutex_lock(&phy->pib_lock);
- phy->current_channel = channel;
- phy->current_page = page;
- mutex_unlock(&phy->pib_lock);
-
- /* We don't emulate beacons here at all, so START should fail */
- ieee802154_nl_start_confirm(dev, IEEE802154_INVALID_PARAMETER);
- return 0;
-}
-
-/**
- * fake_scan_req - Start a channel scan.
- * @dev: The network device on which to perform a channel scan.
- * @type: The type of scan to perform.
- * @channels: The channel bitmask to scan.
- * @duration: How long to spend on each channel.
- *
- * This starts either a passive (energy) scan or an active (PAN) scan
- * on the channels indicated in the @channels bitmask. The duration of
- * the scan is measured in terms of superframe duration. Specifically,
- * the scan will spend aBaseSuperFrameDuration * ((2^n) + 1) on each
- * channel.
- *
- * Note: This is in section 7.5.2.1 of the IEEE 802.15.4-2006 document.
- */
-static int fake_scan_req(struct net_device *dev, u8 type, u32 channels,
- u8 page, u8 duration)
-{
- u8 edl[27] = {};
- return ieee802154_nl_scan_confirm(dev, IEEE802154_SUCCESS, type,
- channels, page,
- type == IEEE802154_MAC_SCAN_ED ? edl : NULL);
-}
-
-static struct ieee802154_mlme_ops fake_mlme = {
- .assoc_req = fake_assoc_req,
- .assoc_resp = fake_assoc_resp,
- .disassoc_req = fake_disassoc_req,
- .start_req = fake_start_req,
- .scan_req = fake_scan_req,
-
- .get_phy = fake_get_phy,
-
- .get_pan_id = fake_get_pan_id,
- .get_short_addr = fake_get_short_addr,
- .get_dsn = fake_get_dsn,
-};
-
-static int ieee802154_fake_open(struct net_device *dev)
-{
- netif_start_queue(dev);
- return 0;
-}
-
-static int ieee802154_fake_close(struct net_device *dev)
-{
- netif_stop_queue(dev);
- return 0;
-}
-
-static netdev_tx_t ieee802154_fake_xmit(struct sk_buff *skb,
- struct net_device *dev)
-{
- dev->stats.tx_packets++;
- dev->stats.tx_bytes += skb->len;
-
- /* FIXME: do hardware work here ... */
-
- dev_kfree_skb(skb);
- return NETDEV_TX_OK;
-}
-
-
-static int ieee802154_fake_ioctl(struct net_device *dev, struct ifreq *ifr,
- int cmd)
-{
- struct sockaddr_ieee802154 *sa =
- (struct sockaddr_ieee802154 *)&ifr->ifr_addr;
- u16 pan_id, short_addr;
-
- switch (cmd) {
- case SIOCGIFADDR:
- /* FIXME: fixed here, get from device IRL */
- pan_id = le16_to_cpu(fake_get_pan_id(dev));
- short_addr = le16_to_cpu(fake_get_short_addr(dev));
- if (pan_id == IEEE802154_PANID_BROADCAST ||
- short_addr == IEEE802154_ADDR_BROADCAST)
- return -EADDRNOTAVAIL;
-
- sa->family = AF_IEEE802154;
- sa->addr.addr_type = IEEE802154_ADDR_SHORT;
- sa->addr.pan_id = pan_id;
- sa->addr.short_addr = short_addr;
- return 0;
- }
- return -ENOIOCTLCMD;
-}
-
-static int ieee802154_fake_mac_addr(struct net_device *dev, void *p)
-{
- return -EBUSY; /* HW address is built into the device */
-}
-
-static const struct net_device_ops fake_ops = {
- .ndo_open = ieee802154_fake_open,
- .ndo_stop = ieee802154_fake_close,
- .ndo_start_xmit = ieee802154_fake_xmit,
- .ndo_do_ioctl = ieee802154_fake_ioctl,
- .ndo_set_mac_address = ieee802154_fake_mac_addr,
-};
-
-static void ieee802154_fake_destruct(struct net_device *dev)
-{
- struct wpan_phy *phy = fake_to_phy(dev);
-
- wpan_phy_unregister(phy);
- free_netdev(dev);
- wpan_phy_free(phy);
-}
-
-static void ieee802154_fake_setup(struct net_device *dev)
-{
- dev->addr_len = IEEE802154_ADDR_LEN;
- memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN);
- dev->features = NETIF_F_HW_CSUM;
- dev->needed_tailroom = 2; /* FCS */
- dev->mtu = 127;
- dev->tx_queue_len = 10;
- dev->type = ARPHRD_IEEE802154;
- dev->flags = IFF_NOARP | IFF_BROADCAST;
- dev->watchdog_timeo = 0;
- dev->destructor = ieee802154_fake_destruct;
-}
-
-
-static int ieee802154fake_probe(struct platform_device *pdev)
-{
- struct net_device *dev;
- struct fakehard_priv *priv;
- struct wpan_phy *phy = wpan_phy_alloc(0);
- int err;
-
- if (!phy)
- return -ENOMEM;
-
- dev = alloc_netdev(sizeof(struct fakehard_priv), "hardwpan%d",
- NET_NAME_UNKNOWN, ieee802154_fake_setup);
- if (!dev) {
- wpan_phy_free(phy);
- return -ENOMEM;
- }
-
- memcpy(dev->dev_addr, "\xba\xbe\xca\xfe\xde\xad\xbe\xef",
- dev->addr_len);
-
- /*
- * For now we'd like to emulate 2.4 GHz-only device,
- * both O-QPSK and CSS
- */
- /* 2.4 GHz O-QPSK 802.15.4-2003 */
- phy->channels_supported[0] |= 0x7FFF800;
- /* 2.4 GHz CSS 802.15.4a-2007 */
- phy->channels_supported[3] |= 0x3fff;
-
- phy->transmit_power = 0xbf;
-
- dev->netdev_ops = &fake_ops;
- dev->ml_priv = &fake_mlme;
-
- priv = netdev_priv(dev);
- priv->phy = phy;
-
- wpan_phy_set_dev(phy, &pdev->dev);
- SET_NETDEV_DEV(dev, &phy->dev);
-
- platform_set_drvdata(pdev, dev);
-
- err = wpan_phy_register(phy);
- if (err)
- goto out;
-
- err = register_netdev(dev);
- if (err < 0)
- goto out;
-
- dev_info(&pdev->dev, "Added ieee802154 HardMAC hardware\n");
- return 0;
-
-out:
- unregister_netdev(dev);
- return err;
-}
-
-static int ieee802154fake_remove(struct platform_device *pdev)
-{
- struct net_device *dev = platform_get_drvdata(pdev);
- unregister_netdev(dev);
- return 0;
-}
-
-static struct platform_device *ieee802154fake_dev;
-
-static struct platform_driver ieee802154fake_driver = {
- .probe = ieee802154fake_probe,
- .remove = ieee802154fake_remove,
- .driver = {
- .name = "ieee802154hardmac",
- .owner = THIS_MODULE,
- },
-};
-
-static __init int fake_init(void)
-{
- ieee802154fake_dev = platform_device_register_simple(
- "ieee802154hardmac", -1, NULL, 0);
- return platform_driver_register(&ieee802154fake_driver);
-}
-
-static __exit void fake_exit(void)
-{
- platform_driver_unregister(&ieee802154fake_driver);
- platform_device_unregister(ieee802154fake_dev);
-}
-
-module_init(fake_init);
-module_exit(fake_exit);
-MODULE_LICENSE("GPL");
diff --git a/drivers/net/ieee802154/fakelb.c b/drivers/net/ieee802154/fakelb.c
index 27d8320..dc2bfb6 100644
--- a/drivers/net/ieee802154/fakelb.c
+++ b/drivers/net/ieee802154/fakelb.c
@@ -12,10 +12,6 @@
* 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.
- *
* Written by:
* Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
@@ -29,12 +25,12 @@
#include <linux/device.h>
#include <linux/spinlock.h>
#include <net/mac802154.h>
-#include <net/wpan-phy.h>
+#include <net/cfg802154.h>
static int numlbs = 1;
struct fakelb_dev_priv {
- struct ieee802154_dev *dev;
+ struct ieee802154_hw *hw;
struct list_head list;
struct fakelb_priv *fake;
@@ -49,9 +45,8 @@
};
static int
-fakelb_hw_ed(struct ieee802154_dev *dev, u8 *level)
+fakelb_hw_ed(struct ieee802154_hw *hw, u8 *level)
{
- might_sleep();
BUG_ON(!level);
*level = 0xbe;
@@ -59,14 +54,10 @@
}
static int
-fakelb_hw_channel(struct ieee802154_dev *dev, int page, int channel)
+fakelb_hw_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
{
pr_debug("set channel to %d\n", channel);
- might_sleep();
- dev->phy->current_page = page;
- dev->phy->current_channel = channel;
-
return 0;
}
@@ -78,19 +69,17 @@
spin_lock(&priv->lock);
if (priv->working) {
newskb = pskb_copy(skb, GFP_ATOMIC);
- ieee802154_rx_irqsafe(priv->dev, newskb, 0xcc);
+ ieee802154_rx_irqsafe(priv->hw, newskb, 0xcc);
}
spin_unlock(&priv->lock);
}
static int
-fakelb_hw_xmit(struct ieee802154_dev *dev, struct sk_buff *skb)
+fakelb_hw_xmit(struct ieee802154_hw *hw, struct sk_buff *skb)
{
- struct fakelb_dev_priv *priv = dev->priv;
+ struct fakelb_dev_priv *priv = hw->priv;
struct fakelb_priv *fake = priv->fake;
- might_sleep();
-
read_lock_bh(&fake->lock);
if (priv->list.next == priv->list.prev) {
/* we are the only one device */
@@ -99,8 +88,8 @@
struct fakelb_dev_priv *dp;
list_for_each_entry(dp, &priv->fake->list, list) {
if (dp != priv &&
- (dp->dev->phy->current_channel ==
- priv->dev->phy->current_channel))
+ (dp->hw->phy->current_channel ==
+ priv->hw->phy->current_channel))
fakelb_hw_deliver(dp, skb);
}
}
@@ -110,8 +99,8 @@
}
static int
-fakelb_hw_start(struct ieee802154_dev *dev) {
- struct fakelb_dev_priv *priv = dev->priv;
+fakelb_hw_start(struct ieee802154_hw *hw) {
+ struct fakelb_dev_priv *priv = hw->priv;
int ret = 0;
spin_lock(&priv->lock);
@@ -125,17 +114,17 @@
}
static void
-fakelb_hw_stop(struct ieee802154_dev *dev) {
- struct fakelb_dev_priv *priv = dev->priv;
+fakelb_hw_stop(struct ieee802154_hw *hw) {
+ struct fakelb_dev_priv *priv = hw->priv;
spin_lock(&priv->lock);
priv->working = 0;
spin_unlock(&priv->lock);
}
-static struct ieee802154_ops fakelb_ops = {
+static const struct ieee802154_ops fakelb_ops = {
.owner = THIS_MODULE,
- .xmit = fakelb_hw_xmit,
+ .xmit_sync = fakelb_hw_xmit,
.ed = fakelb_hw_ed,
.set_channel = fakelb_hw_channel,
.start = fakelb_hw_start,
@@ -150,54 +139,54 @@
{
struct fakelb_dev_priv *priv;
int err;
- struct ieee802154_dev *ieee;
+ struct ieee802154_hw *hw;
- ieee = ieee802154_alloc_device(sizeof(*priv), &fakelb_ops);
- if (!ieee)
+ hw = ieee802154_alloc_hw(sizeof(*priv), &fakelb_ops);
+ if (!hw)
return -ENOMEM;
- priv = ieee->priv;
- priv->dev = ieee;
+ priv = hw->priv;
+ priv->hw = hw;
/* 868 MHz BPSK 802.15.4-2003 */
- ieee->phy->channels_supported[0] |= 1;
+ hw->phy->channels_supported[0] |= 1;
/* 915 MHz BPSK 802.15.4-2003 */
- ieee->phy->channels_supported[0] |= 0x7fe;
+ hw->phy->channels_supported[0] |= 0x7fe;
/* 2.4 GHz O-QPSK 802.15.4-2003 */
- ieee->phy->channels_supported[0] |= 0x7FFF800;
+ hw->phy->channels_supported[0] |= 0x7FFF800;
/* 868 MHz ASK 802.15.4-2006 */
- ieee->phy->channels_supported[1] |= 1;
+ hw->phy->channels_supported[1] |= 1;
/* 915 MHz ASK 802.15.4-2006 */
- ieee->phy->channels_supported[1] |= 0x7fe;
+ hw->phy->channels_supported[1] |= 0x7fe;
/* 868 MHz O-QPSK 802.15.4-2006 */
- ieee->phy->channels_supported[2] |= 1;
+ hw->phy->channels_supported[2] |= 1;
/* 915 MHz O-QPSK 802.15.4-2006 */
- ieee->phy->channels_supported[2] |= 0x7fe;
+ hw->phy->channels_supported[2] |= 0x7fe;
/* 2.4 GHz CSS 802.15.4a-2007 */
- ieee->phy->channels_supported[3] |= 0x3fff;
+ hw->phy->channels_supported[3] |= 0x3fff;
/* UWB Sub-gigahertz 802.15.4a-2007 */
- ieee->phy->channels_supported[4] |= 1;
+ hw->phy->channels_supported[4] |= 1;
/* UWB Low band 802.15.4a-2007 */
- ieee->phy->channels_supported[4] |= 0x1e;
+ hw->phy->channels_supported[4] |= 0x1e;
/* UWB High band 802.15.4a-2007 */
- ieee->phy->channels_supported[4] |= 0xffe0;
+ hw->phy->channels_supported[4] |= 0xffe0;
/* 750 MHz O-QPSK 802.15.4c-2009 */
- ieee->phy->channels_supported[5] |= 0xf;
+ hw->phy->channels_supported[5] |= 0xf;
/* 750 MHz MPSK 802.15.4c-2009 */
- ieee->phy->channels_supported[5] |= 0xf0;
+ hw->phy->channels_supported[5] |= 0xf0;
/* 950 MHz BPSK 802.15.4d-2009 */
- ieee->phy->channels_supported[6] |= 0x3ff;
+ hw->phy->channels_supported[6] |= 0x3ff;
/* 950 MHz GFSK 802.15.4d-2009 */
- ieee->phy->channels_supported[6] |= 0x3ffc00;
+ hw->phy->channels_supported[6] |= 0x3ffc00;
INIT_LIST_HEAD(&priv->list);
priv->fake = fake;
spin_lock_init(&priv->lock);
- ieee->parent = dev;
+ hw->parent = dev;
- err = ieee802154_register_device(ieee);
+ err = ieee802154_register_hw(hw);
if (err)
goto err_reg;
@@ -208,7 +197,7 @@
return 0;
err_reg:
- ieee802154_free_device(priv->dev);
+ ieee802154_free_hw(priv->hw);
return err;
}
@@ -218,8 +207,8 @@
list_del(&priv->list);
write_unlock_bh(&priv->fake->lock);
- ieee802154_unregister_device(priv->dev);
- ieee802154_free_device(priv->dev);
+ ieee802154_unregister_hw(priv->hw);
+ ieee802154_free_hw(priv->hw);
}
static int fakelb_probe(struct platform_device *pdev)
@@ -272,7 +261,6 @@
.remove = fakelb_remove,
.driver = {
.name = "ieee802154fakelb",
- .owner = THIS_MODULE,
},
};
diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c
index 07e0b88..a200fa1 100644
--- a/drivers/net/ieee802154/mrf24j40.c
+++ b/drivers/net/ieee802154/mrf24j40.c
@@ -13,18 +13,14 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/spi/spi.h>
#include <linux/interrupt.h>
#include <linux/module.h>
-#include <net/wpan-phy.h>
+#include <linux/ieee802154.h>
+#include <net/cfg802154.h>
#include <net/mac802154.h>
-#include <net/ieee802154.h>
/* MRF24J40 Short Address Registers */
#define REG_RXMCR 0x00 /* Receive MAC control */
@@ -43,6 +39,8 @@
#define REG_TXSTBL 0x2E /* TX Stabilization */
#define REG_INTSTAT 0x31 /* Interrupt Status */
#define REG_INTCON 0x32 /* Interrupt Control */
+#define REG_GPIO 0x33 /* GPIO */
+#define REG_TRISGPIO 0x34 /* GPIO direction */
#define REG_RFCTL 0x36 /* RF Control Mode Register */
#define REG_BBREG1 0x39 /* Baseband Registers */
#define REG_BBREG2 0x3A /* */
@@ -63,6 +61,7 @@
#define REG_SLPCON1 0x220
#define REG_WAKETIMEL 0x222 /* Wake-up Time Match Value Low */
#define REG_WAKETIMEH 0x223 /* Wake-up Time Match Value High */
+#define REG_TESTMODE 0x22F /* Test mode */
#define REG_RX_FIFO 0x300 /* Receive FIFO */
/* Device configuration: Only channels 11-26 on page 0 are supported. */
@@ -75,10 +74,12 @@
#define RX_FIFO_SIZE 144 /* From datasheet */
#define SET_CHANNEL_DELAY_US 192 /* From datasheet */
+enum mrf24j40_modules { MRF24J40, MRF24J40MA, MRF24J40MC };
+
/* Device Private Data */
struct mrf24j40 {
struct spi_device *spi;
- struct ieee802154_dev *dev;
+ struct ieee802154_hw *hw;
struct mutex buffer_mutex; /* only used to protect buf */
struct completion tx_complete;
@@ -331,9 +332,9 @@
return ret;
}
-static int mrf24j40_tx(struct ieee802154_dev *dev, struct sk_buff *skb)
+static int mrf24j40_tx(struct ieee802154_hw *hw, struct sk_buff *skb)
{
- struct mrf24j40 *devrec = dev->priv;
+ struct mrf24j40 *devrec = hw->priv;
u8 val;
int ret = 0;
@@ -382,7 +383,7 @@
return ret;
}
-static int mrf24j40_ed(struct ieee802154_dev *dev, u8 *level)
+static int mrf24j40_ed(struct ieee802154_hw *hw, u8 *level)
{
/* TODO: */
pr_warn("mrf24j40: ed not implemented\n");
@@ -390,9 +391,9 @@
return 0;
}
-static int mrf24j40_start(struct ieee802154_dev *dev)
+static int mrf24j40_start(struct ieee802154_hw *hw)
{
- struct mrf24j40 *devrec = dev->priv;
+ struct mrf24j40 *devrec = hw->priv;
u8 val;
int ret;
@@ -407,9 +408,9 @@
return 0;
}
-static void mrf24j40_stop(struct ieee802154_dev *dev)
+static void mrf24j40_stop(struct ieee802154_hw *hw)
{
- struct mrf24j40 *devrec = dev->priv;
+ struct mrf24j40 *devrec = hw->priv;
u8 val;
int ret;
@@ -422,10 +423,9 @@
write_short_reg(devrec, REG_INTCON, val);
}
-static int mrf24j40_set_channel(struct ieee802154_dev *dev,
- int page, int channel)
+static int mrf24j40_set_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
{
- struct mrf24j40 *devrec = dev->priv;
+ struct mrf24j40 *devrec = hw->priv;
u8 val;
int ret;
@@ -453,15 +453,15 @@
return 0;
}
-static int mrf24j40_filter(struct ieee802154_dev *dev,
+static int mrf24j40_filter(struct ieee802154_hw *hw,
struct ieee802154_hw_addr_filt *filt,
unsigned long changed)
{
- struct mrf24j40 *devrec = dev->priv;
+ struct mrf24j40 *devrec = hw->priv;
dev_dbg(printdev(devrec), "filter\n");
- if (changed & IEEE802515_AFILT_SADDR_CHANGED) {
+ if (changed & IEEE802154_AFILT_SADDR_CHANGED) {
/* Short Addr */
u8 addrh, addrl;
@@ -474,7 +474,7 @@
"Set short addr to %04hx\n", filt->short_addr);
}
- if (changed & IEEE802515_AFILT_IEEEADDR_CHANGED) {
+ if (changed & IEEE802154_AFILT_IEEEADDR_CHANGED) {
/* Device Address */
u8 i, addr[8];
@@ -490,7 +490,7 @@
#endif
}
- if (changed & IEEE802515_AFILT_PANID_CHANGED) {
+ if (changed & IEEE802154_AFILT_PANID_CHANGED) {
/* PAN ID */
u8 panidl, panidh;
@@ -502,7 +502,7 @@
dev_dbg(printdev(devrec), "Set PANID to %04hx\n", filt->pan_id);
}
- if (changed & IEEE802515_AFILT_PANC_CHANGED) {
+ if (changed & IEEE802154_AFILT_PANC_CHANGED) {
/* Pan Coordinator */
u8 val;
int ret;
@@ -543,7 +543,7 @@
val |= 4; /* SET RXDECINV */
write_short_reg(devrec, REG_BBREG1, val);
- skb = alloc_skb(len, GFP_KERNEL);
+ skb = dev_alloc_skb(len);
if (!skb) {
ret = -ENOMEM;
goto out;
@@ -563,7 +563,7 @@
/* TODO: Other drivers call ieee20154_rx_irqsafe() here (eg: cc2040,
* also from a workqueue). I think irqsafe is not necessary here.
* Can someone confirm? */
- ieee802154_rx_irqsafe(devrec->dev, skb, lqi);
+ ieee802154_rx_irqsafe(devrec->hw, skb, lqi);
dev_dbg(printdev(devrec), "RX Handled\n");
@@ -578,9 +578,9 @@
return ret;
}
-static struct ieee802154_ops mrf24j40_ops = {
+static const struct ieee802154_ops mrf24j40_ops = {
.owner = THIS_MODULE,
- .xmit = mrf24j40_tx,
+ .xmit_sync = mrf24j40_tx,
.ed = mrf24j40_ed,
.start = mrf24j40_start,
.stop = mrf24j40_stop,
@@ -691,6 +691,28 @@
if (ret)
goto err_ret;
+ if (spi_get_device_id(devrec->spi)->driver_data == MRF24J40MC) {
+ /* Enable external amplifier.
+ * From MRF24J40MC datasheet section 1.3: Operation.
+ */
+ read_long_reg(devrec, REG_TESTMODE, &val);
+ val |= 0x7; /* Configure GPIO 0-2 to control amplifier */
+ write_long_reg(devrec, REG_TESTMODE, val);
+
+ read_short_reg(devrec, REG_TRISGPIO, &val);
+ val |= 0x8; /* Set GPIO3 as output. */
+ write_short_reg(devrec, REG_TRISGPIO, val);
+
+ read_short_reg(devrec, REG_GPIO, &val);
+ val |= 0x8; /* Set GPIO3 HIGH to enable U5 voltage regulator */
+ write_short_reg(devrec, REG_GPIO, val);
+
+ /* Reduce TX pwr to meet FCC requirements.
+ * From MRF24J40MC datasheet section 3.1.1
+ */
+ write_long_reg(devrec, REG_RFCON3, 0x28);
+ }
+
return 0;
err_ret:
@@ -722,17 +744,18 @@
/* Register with the 802154 subsystem */
- devrec->dev = ieee802154_alloc_device(0, &mrf24j40_ops);
- if (!devrec->dev)
+ devrec->hw = ieee802154_alloc_hw(0, &mrf24j40_ops);
+ if (!devrec->hw)
goto err_ret;
- devrec->dev->priv = devrec;
- devrec->dev->parent = &devrec->spi->dev;
- devrec->dev->phy->channels_supported[0] = CHANNEL_MASK;
- devrec->dev->flags = IEEE802154_HW_OMIT_CKSUM|IEEE802154_HW_AACK;
+ devrec->hw->priv = devrec;
+ devrec->hw->parent = &devrec->spi->dev;
+ devrec->hw->phy->channels_supported[0] = CHANNEL_MASK;
+ devrec->hw->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AACK |
+ IEEE802154_HW_AFILT;
dev_dbg(printdev(devrec), "registered mrf24j40\n");
- ret = ieee802154_register_device(devrec->dev);
+ ret = ieee802154_register_hw(devrec->hw);
if (ret)
goto err_register_device;
@@ -757,9 +780,9 @@
err_irq:
err_hw_init:
- ieee802154_unregister_device(devrec->dev);
+ ieee802154_unregister_hw(devrec->hw);
err_register_device:
- ieee802154_free_device(devrec->dev);
+ ieee802154_free_hw(devrec->hw);
err_ret:
return ret;
}
@@ -770,8 +793,8 @@
dev_dbg(printdev(devrec), "remove\n");
- ieee802154_unregister_device(devrec->dev);
- ieee802154_free_device(devrec->dev);
+ ieee802154_unregister_hw(devrec->hw);
+ ieee802154_free_hw(devrec->hw);
/* TODO: Will ieee802154_free_device() wait until ->xmit() is
* complete? */
@@ -779,8 +802,9 @@
}
static const struct spi_device_id mrf24j40_ids[] = {
- { "mrf24j40", 0 },
- { "mrf24j40ma", 0 },
+ { "mrf24j40", MRF24J40 },
+ { "mrf24j40ma", MRF24J40MA },
+ { "mrf24j40mc", MRF24J40MC },
{ },
};
MODULE_DEVICE_TABLE(spi, mrf24j40_ids);
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index c45c011..d6683dd 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -78,7 +78,7 @@
depends on n
tristate "USB Pegasus/Pegasus-II based ethernet device support"
depends on m
- select BACKPORT_MII
+ select BPAUTO_MII
---help---
Say Y here if you know you have Pegasus or Pegasus-II based adapter.
If in doubt then look at <file:drivers/net/usb/pegasus.h> for the
@@ -95,7 +95,7 @@
depends on n
tristate "USB RTL8150 based ethernet device support"
depends on m
- select BACKPORT_MII
+ select BPAUTO_MII
help
Say Y here if you have RTL8150 based usb-ethernet adapter.
Send me <petkan@users.sourceforge.net> any comments you may have.
@@ -108,7 +108,7 @@
depends on n
tristate "Realtek RTL8152/RTL8153 Based USB Ethernet Adapters"
depends on m
- select BACKPORT_MII
+ select BPAUTO_MII
help
This option adds support for Realtek RTL8152 based USB 2.0
10/100 Ethernet adapters and RTL8153 based USB 3.0 10/100/1000
@@ -120,7 +120,7 @@
config USB_USBNET
tristate "Multi-purpose USB Networking Framework"
depends on m
- select BACKPORT_MII
+ select BPAUTO_MII
---help---
This driver supports several kinds of network links over USB,
with "minidrivers" built around a common network driver core
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index c582a86..80a714b 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -67,6 +67,36 @@
0xa6, 0x07, 0xc0, 0xff, 0xcb, 0x7e, 0x39, 0x2a,
};
+static void usbnet_cdc_update_filter(struct usbnet *dev)
+{
+ struct cdc_state *info = (void *) &dev->data;
+ struct usb_interface *intf = info->control;
+ struct net_device *net = dev->net;
+
+ u16 cdc_filter = USB_CDC_PACKET_TYPE_DIRECTED
+ | USB_CDC_PACKET_TYPE_BROADCAST;
+
+ /* filtering on the device is an optional feature and not worth
+ * the hassle so we just roughly care about snooping and if any
+ * multicast is requested, we take every multicast
+ */
+ if (net->flags & IFF_PROMISC)
+ cdc_filter |= USB_CDC_PACKET_TYPE_PROMISCUOUS;
+ if (!netdev_mc_empty(net) || (net->flags & IFF_ALLMULTI))
+ cdc_filter |= USB_CDC_PACKET_TYPE_ALL_MULTICAST;
+
+ usb_control_msg(dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ USB_CDC_SET_ETHERNET_PACKET_FILTER,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ cdc_filter,
+ intf->cur_altsetting->desc.bInterfaceNumber,
+ NULL,
+ 0,
+ USB_CTRL_SET_TIMEOUT
+ );
+}
+
/* probes control interface, claims data interface, collects the bulk
* endpoints, activates data interface (if needed), maybe sets MTU.
* all pure cdc, except for certain firmware workarounds, and knowing
@@ -347,16 +377,8 @@
* don't do reset all the way. So the packet filter should
* be set to a sane initial value.
*/
- usb_control_msg(dev->udev,
- usb_sndctrlpipe(dev->udev, 0),
- USB_CDC_SET_ETHERNET_PACKET_FILTER,
- USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- USB_CDC_PACKET_TYPE_ALL_MULTICAST | USB_CDC_PACKET_TYPE_DIRECTED | USB_CDC_PACKET_TYPE_BROADCAST,
- intf->cur_altsetting->desc.bInterfaceNumber,
- NULL,
- 0,
- USB_CTRL_SET_TIMEOUT
- );
+ usbnet_cdc_update_filter(dev);
+
return 0;
bad_desc:
@@ -468,10 +490,6 @@
return status;
}
- /* FIXME cdc-ether has some multicast code too, though it complains
- * in routine cases. info->ether describes the multicast support.
- * Implement that here, manipulating the cdc filter as needed.
- */
return 0;
}
EXPORT_SYMBOL_GPL(usbnet_cdc_bind);
@@ -482,6 +500,7 @@
.bind = usbnet_cdc_bind,
.unbind = usbnet_cdc_unbind,
.status = usbnet_cdc_status,
+ .set_rx_mode = usbnet_cdc_update_filter,
.manage_power = usbnet_manage_power,
};
@@ -491,6 +510,7 @@
.bind = usbnet_cdc_bind,
.unbind = usbnet_cdc_unbind,
.status = usbnet_cdc_status,
+ .set_rx_mode = usbnet_cdc_update_filter,
.manage_power = usbnet_manage_power,
};
diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c
index 6fb0384..e330332 100644
--- a/drivers/net/usb/cdc_mbim.c
+++ b/drivers/net/usb/cdc_mbim.c
@@ -422,7 +422,7 @@
/* map MBIM session to VLAN */
if (tci)
- vlan_put_tag(skb, htons(ETH_P_8021Q), tci);
+ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), tci);
err:
return skb;
}
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index 80fa223..c138280 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -780,6 +780,7 @@
{QMI_FIXED_INTF(0x413c, 0x81a4, 8)}, /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */
{QMI_FIXED_INTF(0x413c, 0x81a8, 8)}, /* Dell Wireless 5808 Gobi(TM) 4G LTE Mobile Broadband Card */
{QMI_FIXED_INTF(0x413c, 0x81a9, 8)}, /* Dell Wireless 5808e Gobi(TM) 4G LTE Mobile Broadband Card */
+ {QMI_FIXED_INTF(0x03f0, 0x581d, 4)}, /* HP lt4112 LTE/HSPA+ Gobi 4G Module (Huawei me906e) */
/* 4. Gobi 1000 devices */
{QMI_GOBI1K_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index d8bc646..a0dcb2f 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -1054,6 +1054,21 @@
clear_bit(EVENT_LINK_CHANGE, &dev->flags);
}
+static void usbnet_set_rx_mode(struct net_device *net)
+{
+ struct usbnet *dev = netdev_priv(net);
+
+ usbnet_defer_kevent(dev, EVENT_SET_RX_MODE);
+}
+
+static void __handle_set_rx_mode(struct usbnet *dev)
+{
+ if (dev->driver_info->set_rx_mode)
+ (dev->driver_info->set_rx_mode)(dev);
+
+ clear_bit(EVENT_SET_RX_MODE, &dev->flags);
+}
+
/* work that cannot be done in interrupt context uses keventd.
*
* NOTE: with 2.5 we could do more of this using completion callbacks,
@@ -1159,6 +1174,10 @@
if (test_bit (EVENT_LINK_CHANGE, &dev->flags))
__handle_link_change(dev);
+ if (test_bit (EVENT_SET_RX_MODE, &dev->flags))
+ __handle_set_rx_mode(dev);
+
+
if (dev->flags)
netdev_dbg(dev->net, "kevent done, flags = 0x%lx\n", dev->flags);
}
@@ -1545,6 +1564,7 @@
.ndo_stop = usbnet_stop,
.ndo_start_xmit = usbnet_start_xmit,
.ndo_tx_timeout = usbnet_tx_timeout,
+ .ndo_set_rx_mode = usbnet_set_rx_mode,
.ndo_change_mtu = usbnet_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig
index c937dac..74d4104 100644
--- a/drivers/net/wireless/ath/Kconfig
+++ b/drivers/net/wireless/ath/Kconfig
@@ -28,7 +28,7 @@
Right now only ath9k makes use of this.
config ATH_TRACEPOINTS
- depends on !BACKPORT_KERNEL_3_1
+ depends on !KERNEL_3_1
bool "Atheros wireless tracing"
depends on ATH_DEBUG
depends on EVENT_TRACING
diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h
index 91c1893..6d58a3e 100644
--- a/drivers/net/wireless/ath/ath.h
+++ b/drivers/net/wireless/ath/ath.h
@@ -80,6 +80,7 @@
struct ath_regulatory {
char alpha2[2];
+ enum nl80211_dfs_regions region;
u16 country_code;
u16 max_power_level;
u16 current_rd;
@@ -134,6 +135,11 @@
struct ath_common;
struct ath_bus_ops;
+struct ath_ps_ops {
+ void (*wakeup)(struct ath_common *common);
+ void (*restore)(struct ath_common *common);
+};
+
struct ath_common {
void *ah;
void *priv;
@@ -147,7 +153,7 @@
u16 cachelsz;
u16 curaid;
u8 macaddr[ETH_ALEN];
- u8 curbssid[ETH_ALEN];
+ u8 curbssid[ETH_ALEN] __aligned(2);
u8 bssidmask[ETH_ALEN];
u32 rx_bufsize;
@@ -168,6 +174,7 @@
struct ath_regulatory reg_world_copy;
const struct ath_ops *ops;
const struct ath_bus_ops *bus_ops;
+ const struct ath_ps_ops *ps_ops;
bool btcoex_enabled;
bool disable_ani;
@@ -177,6 +184,11 @@
struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS];
};
+static inline const struct ath_ps_ops *ath_ps_ops(struct ath_common *common)
+{
+ return common->ps_ops;
+}
+
struct sk_buff *ath_rxbuf_alloc(struct ath_common *common,
u32 len,
gfp_t gfp_mask);
diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig
index eb8ecdf..bf01fad 100644
--- a/drivers/net/wireless/ath/ath10k/Kconfig
+++ b/drivers/net/wireless/ath/ath10k/Kconfig
@@ -34,7 +34,7 @@
If unsure, say Y to make it easier to debug problems.
config ATH10K_TRACING
- depends on !BACKPORT_KERNEL_3_4
+ depends on !KERNEL_3_4
bool "Atheros ath10k tracing support"
depends on ATH10K
depends on EVENT_TRACING
diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c
index de7426a..842d941 100644
--- a/drivers/net/wireless/ath/ath10k/ce.c
+++ b/drivers/net/wireless/ath/ath10k/ce.c
@@ -445,12 +445,12 @@
* Guts of ath10k_ce_completed_recv_next.
* The caller takes responsibility for any necessary locking.
*/
-static int ath10k_ce_completed_recv_next_nolock(struct ath10k_ce_pipe *ce_state,
- void **per_transfer_contextp,
- u32 *bufferp,
- unsigned int *nbytesp,
- unsigned int *transfer_idp,
- unsigned int *flagsp)
+int ath10k_ce_completed_recv_next_nolock(struct ath10k_ce_pipe *ce_state,
+ void **per_transfer_contextp,
+ u32 *bufferp,
+ unsigned int *nbytesp,
+ unsigned int *transfer_idp,
+ unsigned int *flagsp)
{
struct ath10k_ce_ring *dest_ring = ce_state->dest_ring;
unsigned int nentries_mask = dest_ring->nentries_mask;
@@ -560,6 +560,7 @@
/* sanity */
dest_ring->per_transfer_context[sw_index] = NULL;
+ desc->nbytes = 0;
/* Update sw_index */
sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index);
@@ -578,11 +579,11 @@
* Guts of ath10k_ce_completed_send_next.
* The caller takes responsibility for any necessary locking.
*/
-static int ath10k_ce_completed_send_next_nolock(struct ath10k_ce_pipe *ce_state,
- void **per_transfer_contextp,
- u32 *bufferp,
- unsigned int *nbytesp,
- unsigned int *transfer_idp)
+int ath10k_ce_completed_send_next_nolock(struct ath10k_ce_pipe *ce_state,
+ void **per_transfer_contextp,
+ u32 *bufferp,
+ unsigned int *nbytesp,
+ unsigned int *transfer_idp)
{
struct ath10k_ce_ring *src_ring = ce_state->src_ring;
u32 ctrl_addr = ce_state->ctrl_addr;
@@ -819,7 +820,10 @@
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
int ce_id;
- for (ce_id = 0; ce_id < CE_COUNT; ce_id++)
+ /* Skip the last copy engine, CE7 the diagnostic window, as that
+ * uses polling and isn't initialized for interrupts.
+ */
+ for (ce_id = 0; ce_id < CE_COUNT - 1; ce_id++)
ath10k_ce_per_engine_handler_adjust(&ar_pci->ce_states[ce_id]);
}
@@ -834,8 +838,8 @@
nentries = roundup_pow_of_two(attr->src_nentries);
- memset(src_ring->per_transfer_context, 0,
- nentries * sizeof(*src_ring->per_transfer_context));
+ memset(src_ring->base_addr_owner_space, 0,
+ nentries * sizeof(struct ce_desc));
src_ring->sw_index = ath10k_ce_src_ring_read_index_get(ar, ctrl_addr);
src_ring->sw_index &= src_ring->nentries_mask;
@@ -871,8 +875,8 @@
nentries = roundup_pow_of_two(attr->dest_nentries);
- memset(dest_ring->per_transfer_context, 0,
- nentries * sizeof(*dest_ring->per_transfer_context));
+ memset(dest_ring->base_addr_owner_space, 0,
+ nentries * sizeof(struct ce_desc));
dest_ring->sw_index = ath10k_ce_dest_ring_read_index_get(ar, ctrl_addr);
dest_ring->sw_index &= dest_ring->nentries_mask;
@@ -1030,37 +1034,10 @@
* initialized by software/firmware.
*/
int ath10k_ce_init_pipe(struct ath10k *ar, unsigned int ce_id,
- const struct ce_attr *attr,
- void (*send_cb)(struct ath10k_ce_pipe *),
- void (*recv_cb)(struct ath10k_ce_pipe *))
+ const struct ce_attr *attr)
{
- struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
- struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
int ret;
- /*
- * Make sure there's enough CE ringbuffer entries for HTT TX to avoid
- * additional TX locking checks.
- *
- * For the lack of a better place do the check here.
- */
- BUILD_BUG_ON(2*TARGET_NUM_MSDU_DESC >
- (CE_HTT_H2T_MSG_SRC_NENTRIES - 1));
- BUILD_BUG_ON(2*TARGET_10X_NUM_MSDU_DESC >
- (CE_HTT_H2T_MSG_SRC_NENTRIES - 1));
-
- spin_lock_bh(&ar_pci->ce_lock);
- ce_state->ar = ar;
- ce_state->id = ce_id;
- ce_state->ctrl_addr = ath10k_ce_base_address(ce_id);
- ce_state->attr_flags = attr->flags;
- ce_state->src_sz_max = attr->src_sz_max;
- if (attr->src_nentries)
- ce_state->send_cb = send_cb;
- if (attr->dest_nentries)
- ce_state->recv_cb = recv_cb;
- spin_unlock_bh(&ar_pci->ce_lock);
-
if (attr->src_nentries) {
ret = ath10k_ce_init_src_ring(ar, ce_id, attr);
if (ret) {
@@ -1108,12 +1085,37 @@
}
int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id,
- const struct ce_attr *attr)
+ const struct ce_attr *attr,
+ void (*send_cb)(struct ath10k_ce_pipe *),
+ void (*recv_cb)(struct ath10k_ce_pipe *))
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id];
int ret;
+ /*
+ * Make sure there's enough CE ringbuffer entries for HTT TX to avoid
+ * additional TX locking checks.
+ *
+ * For the lack of a better place do the check here.
+ */
+ BUILD_BUG_ON(2*TARGET_NUM_MSDU_DESC >
+ (CE_HTT_H2T_MSG_SRC_NENTRIES - 1));
+ BUILD_BUG_ON(2*TARGET_10X_NUM_MSDU_DESC >
+ (CE_HTT_H2T_MSG_SRC_NENTRIES - 1));
+
+ ce_state->ar = ar;
+ ce_state->id = ce_id;
+ ce_state->ctrl_addr = ath10k_ce_base_address(ce_id);
+ ce_state->attr_flags = attr->flags;
+ ce_state->src_sz_max = attr->src_sz_max;
+
+ if (attr->src_nentries)
+ ce_state->send_cb = send_cb;
+
+ if (attr->dest_nentries)
+ ce_state->recv_cb = recv_cb;
+
if (attr->src_nentries) {
ce_state->src_ring = ath10k_ce_alloc_src_ring(ar, ce_id, attr);
if (IS_ERR(ce_state->src_ring)) {
diff --git a/drivers/net/wireless/ath/ath10k/ce.h b/drivers/net/wireless/ath/ath10k/ce.h
index 329b734..617a151 100644
--- a/drivers/net/wireless/ath/ath10k/ce.h
+++ b/drivers/net/wireless/ath/ath10k/ce.h
@@ -192,15 +192,21 @@
unsigned int *nbytesp,
unsigned int *transfer_idp);
+int ath10k_ce_completed_send_next_nolock(struct ath10k_ce_pipe *ce_state,
+ void **per_transfer_contextp,
+ u32 *bufferp,
+ unsigned int *nbytesp,
+ unsigned int *transfer_idp);
+
/*==================CE Engine Initialization=======================*/
int ath10k_ce_init_pipe(struct ath10k *ar, unsigned int ce_id,
- const struct ce_attr *attr,
- void (*send_cb)(struct ath10k_ce_pipe *),
- void (*recv_cb)(struct ath10k_ce_pipe *));
+ const struct ce_attr *attr);
void ath10k_ce_deinit_pipe(struct ath10k *ar, unsigned int ce_id);
int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id,
- const struct ce_attr *attr);
+ const struct ce_attr *attr,
+ void (*send_cb)(struct ath10k_ce_pipe *),
+ void (*recv_cb)(struct ath10k_ce_pipe *));
void ath10k_ce_free_pipe(struct ath10k *ar, int ce_id);
/*==================CE Engine Shutdown=======================*/
@@ -213,6 +219,13 @@
void **per_transfer_contextp,
u32 *bufferp);
+int ath10k_ce_completed_recv_next_nolock(struct ath10k_ce_pipe *ce_state,
+ void **per_transfer_contextp,
+ u32 *bufferp,
+ unsigned int *nbytesp,
+ unsigned int *transfer_idp,
+ unsigned int *flagsp);
+
/*
* Support clean shutdown by allowing the caller to cancel
* pending sends. Target DMA must be stopped before using
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 5a08f55..4a9bfdc 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -31,12 +31,17 @@
unsigned int ath10k_debug_mask;
static bool uart_print;
static unsigned int ath10k_p2p;
+static bool skip_otp;
+
module_param_named(debug_mask, ath10k_debug_mask, uint, 0644);
module_param(uart_print, bool, 0644);
module_param_named(p2p, ath10k_p2p, uint, 0644);
+module_param(skip_otp, bool, 0644);
+
MODULE_PARM_DESC(debug_mask, "Debugging mask");
MODULE_PARM_DESC(uart_print, "Uart target debugging");
MODULE_PARM_DESC(p2p, "Enable ath10k P2P support");
+MODULE_PARM_DESC(skip_otp, "Skip otp failure for calibration in testmode");
static const struct ath10k_hw_params ath10k_hw_params_list[] = {
{
@@ -138,7 +143,8 @@
return fw;
}
-static int ath10k_push_board_ext_data(struct ath10k *ar)
+static int ath10k_push_board_ext_data(struct ath10k *ar, const void *data,
+ size_t data_len)
{
u32 board_data_size = QCA988X_BOARD_DATA_SZ;
u32 board_ext_data_size = QCA988X_BOARD_EXT_DATA_SZ;
@@ -159,14 +165,14 @@
if (board_ext_data_addr == 0)
return 0;
- if (ar->board_len != (board_data_size + board_ext_data_size)) {
+ if (data_len != (board_data_size + board_ext_data_size)) {
ath10k_err(ar, "invalid board (ext) data sizes %zu != %d+%d\n",
- ar->board_len, board_data_size, board_ext_data_size);
+ data_len, board_data_size, board_ext_data_size);
return -EINVAL;
}
ret = ath10k_bmi_write_memory(ar, board_ext_data_addr,
- ar->board_data + board_data_size,
+ data + board_data_size,
board_ext_data_size);
if (ret) {
ath10k_err(ar, "could not write board ext data (%d)\n", ret);
@@ -184,13 +190,14 @@
return 0;
}
-static int ath10k_download_board_data(struct ath10k *ar)
+static int ath10k_download_board_data(struct ath10k *ar, const void *data,
+ size_t data_len)
{
u32 board_data_size = QCA988X_BOARD_DATA_SZ;
u32 address;
int ret;
- ret = ath10k_push_board_ext_data(ar);
+ ret = ath10k_push_board_ext_data(ar, data, data_len);
if (ret) {
ath10k_err(ar, "could not push board ext data (%d)\n", ret);
goto exit;
@@ -202,9 +209,9 @@
goto exit;
}
- ret = ath10k_bmi_write_memory(ar, address, ar->board_data,
+ ret = ath10k_bmi_write_memory(ar, address, data,
min_t(u32, board_data_size,
- ar->board_len));
+ data_len));
if (ret) {
ath10k_err(ar, "could not write board data (%d)\n", ret);
goto exit;
@@ -220,11 +227,39 @@
return ret;
}
+static int ath10k_download_cal_file(struct ath10k *ar)
+{
+ int ret;
+
+ if (!ar->cal_file)
+ return -ENOENT;
+
+ if (IS_ERR(ar->cal_file))
+ return PTR_ERR(ar->cal_file);
+
+ ret = ath10k_download_board_data(ar, ar->cal_file->data,
+ ar->cal_file->size);
+ if (ret) {
+ ath10k_err(ar, "failed to download cal_file data: %d\n", ret);
+ return ret;
+ }
+
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot cal file downloaded\n");
+
+ return 0;
+}
+
static int ath10k_download_and_run_otp(struct ath10k *ar)
{
u32 result, address = ar->hw_params.patch_load_addr;
int ret;
+ ret = ath10k_download_board_data(ar, ar->board_data, ar->board_len);
+ if (ret) {
+ ath10k_err(ar, "failed to download board data: %d\n", ret);
+ return ret;
+ }
+
/* OTP is optional */
if (!ar->otp_data || !ar->otp_len) {
@@ -250,7 +285,7 @@
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot otp execute result %d\n", result);
- if (result != 0) {
+ if (!skip_otp && result != 0) {
ath10k_err(ar, "otp calibration failed: %d", result);
return -EINVAL;
}
@@ -308,6 +343,9 @@
if (ar->firmware && !IS_ERR(ar->firmware))
release_firmware(ar->firmware);
+ if (ar->cal_file && !IS_ERR(ar->cal_file))
+ release_firmware(ar->cal_file);
+
ar->board = NULL;
ar->board_data = NULL;
ar->board_len = 0;
@@ -319,6 +357,27 @@
ar->firmware = NULL;
ar->firmware_data = NULL;
ar->firmware_len = 0;
+
+ ar->cal_file = NULL;
+}
+
+static int ath10k_fetch_cal_file(struct ath10k *ar)
+{
+ char filename[100];
+
+ /* cal-<bus>-<id>.bin */
+ scnprintf(filename, sizeof(filename), "cal-%s-%s.bin",
+ ath10k_bus_str(ar->hif.bus), dev_name(ar->dev));
+
+ ar->cal_file = ath10k_fetch_fw_file(ar, ATH10K_FW_DIR, filename);
+ if (IS_ERR(ar->cal_file))
+ /* calibration file is optional, don't print any warnings */
+ return PTR_ERR(ar->cal_file);
+
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "found calibration file %s/%s\n",
+ ATH10K_FW_DIR, filename);
+
+ return 0;
}
static int ath10k_core_fetch_firmware_api_1(struct ath10k *ar)
@@ -562,6 +621,9 @@
{
int ret;
+ /* calibration file is optional, don't check for any errors */
+ ath10k_fetch_cal_file(ar);
+
ar->fw_api = 3;
ath10k_dbg(ar, ATH10K_DBG_BOOT, "trying fw api %d\n", ar->fw_api);
@@ -589,30 +651,32 @@
return 0;
}
-static int ath10k_init_download_firmware(struct ath10k *ar,
- enum ath10k_firmware_mode mode)
+static int ath10k_download_cal_data(struct ath10k *ar)
{
int ret;
- ret = ath10k_download_board_data(ar);
- if (ret) {
- ath10k_err(ar, "failed to download board data: %d\n", ret);
- return ret;
+ ret = ath10k_download_cal_file(ar);
+ if (ret == 0) {
+ ar->cal_mode = ATH10K_CAL_MODE_FILE;
+ goto done;
}
+ ath10k_dbg(ar, ATH10K_DBG_BOOT,
+ "boot did not find a calibration file, try OTP next: %d\n",
+ ret);
+
ret = ath10k_download_and_run_otp(ar);
if (ret) {
ath10k_err(ar, "failed to run otp: %d\n", ret);
return ret;
}
- ret = ath10k_download_fw(ar, mode);
- if (ret) {
- ath10k_err(ar, "failed to download firmware: %d\n", ret);
- return ret;
- }
+ ar->cal_mode = ATH10K_CAL_MODE_OTP;
- return ret;
+done:
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot using calibration mode %s\n",
+ ath10k_cal_mode_str(ar->cal_mode));
+ return 0;
}
static int ath10k_init_uart(struct ath10k *ar)
@@ -695,6 +759,25 @@
ath10k_err(ar, "failed to execute ath10k-reset. trying driver-level reset.\n");
}
+ set_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags);
+
+ /* Place a barrier to make sure the compiler doesn't reorder
+ * CRASH_FLUSH and calling other functions.
+ */
+ barrier();
+
+ ieee80211_stop_queues(ar->hw);
+ ath10k_drain_tx(ar);
+ complete_all(&ar->scan.started);
+ complete_all(&ar->scan.completed);
+ complete_all(&ar->scan.on_channel);
+ complete_all(&ar->offchan_tx_completed);
+ complete_all(&ar->install_key_done);
+ complete_all(&ar->vdev_setup_done);
+ wake_up(&ar->htt.empty_tx_wq);
+ wake_up(&ar->wmi.tx_credits_wq);
+ wake_up(&ar->peer_mapping_wq);
+
mutex_lock(&ar->conf_mutex);
switch (ar->state) {
@@ -726,12 +809,25 @@
mutex_unlock(&ar->conf_mutex);
}
+static void ath10k_core_init_max_sta_count(struct ath10k *ar)
+{
+ if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
+ ar->max_num_peers = TARGET_10X_NUM_PEERS;
+ ar->max_num_stations = TARGET_10X_NUM_STATIONS;
+ } else {
+ ar->max_num_peers = TARGET_NUM_PEERS;
+ ar->max_num_stations = TARGET_NUM_STATIONS;
+ }
+}
+
int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode)
{
int status;
lockdep_assert_held(&ar->conf_mutex);
+ clear_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags);
+
ath10k_bmi_start(ar);
if (ath10k_init_configure_target(ar)) {
@@ -739,7 +835,11 @@
goto err;
}
- status = ath10k_init_download_firmware(ar, mode);
+ status = ath10k_download_cal_data(ar);
+ if (status)
+ goto err;
+
+ status = ath10k_download_fw(ar, mode);
if (status)
goto err;
@@ -856,9 +956,9 @@
goto err_hif_stop;
if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
- ar->free_vdev_map = (1 << TARGET_10X_NUM_VDEVS) - 1;
+ ar->free_vdev_map = (1LL << TARGET_10X_NUM_VDEVS) - 1;
else
- ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1;
+ ar->free_vdev_map = (1LL << TARGET_NUM_VDEVS) - 1;
INIT_LIST_HEAD(&ar->arvifs);
@@ -956,6 +1056,8 @@
return ret;
}
+ ath10k_core_init_max_sta_count(ar);
+
mutex_lock(&ar->conf_mutex);
ret = ath10k_core_start(ar, ATH10K_FIRMWARE_MODE_NORMAL);
@@ -1094,6 +1196,7 @@
EXPORT_SYMBOL(ath10k_core_unregister);
struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
+ enum ath10k_bus bus,
const struct ath10k_hif_ops *hif_ops)
{
struct ath10k *ar;
@@ -1110,6 +1213,7 @@
ar->dev = dev;
ar->hif.ops = hif_ops;
+ ar->hif.bus = bus;
init_completion(&ar->scan.started);
init_completion(&ar->scan.completed);
@@ -1130,6 +1234,8 @@
INIT_LIST_HEAD(&ar->peers);
init_waitqueue_head(&ar->peer_mapping_wq);
+ init_waitqueue_head(&ar->htt.empty_tx_wq);
+ init_waitqueue_head(&ar->wmi.tx_credits_wq);
init_completion(&ar->offchan_tx_completed);
INIT_WORK(&ar->offchan_tx_work, ath10k_offchan_tx_work);
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index d773aa0..ef0f447 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -63,12 +63,28 @@
struct ath10k;
+enum ath10k_bus {
+ ATH10K_BUS_PCI,
+};
+
+static inline const char *ath10k_bus_str(enum ath10k_bus bus)
+{
+ switch (bus) {
+ case ATH10K_BUS_PCI:
+ return "pci";
+ }
+
+ return "unknown";
+}
+
struct ath10k_skb_cb {
dma_addr_t paddr;
+ u8 eid;
u8 vdev_id;
struct {
u8 tid;
+ u16 freq;
bool is_offchan;
struct ath10k_htt_txbuf *txbuf;
u32 txbuf_paddr;
@@ -96,8 +112,6 @@
bool done_sent;
};
-#define ATH10K_MAX_MEM_REQS 16
-
struct ath10k_mem_chunk {
void *vaddr;
dma_addr_t paddr;
@@ -110,22 +124,27 @@
struct completion service_ready;
struct completion unified_ready;
wait_queue_head_t tx_credits_wq;
+ DECLARE_BITMAP(svc_map, WMI_SERVICE_MAX);
struct wmi_cmd_map *cmd;
struct wmi_vdev_param_map *vdev_param;
struct wmi_pdev_param_map *pdev_param;
u32 num_mem_chunks;
- struct ath10k_mem_chunk mem_chunks[ATH10K_MAX_MEM_REQS];
+ struct ath10k_mem_chunk mem_chunks[WMI_MAX_MEM_REQS];
};
-struct ath10k_peer_stat {
+struct ath10k_fw_stats_peer {
+ struct list_head list;
+
u8 peer_macaddr[ETH_ALEN];
u32 peer_rssi;
u32 peer_tx_rate;
u32 peer_rx_rate; /* 10x only */
};
-struct ath10k_target_stats {
+struct ath10k_fw_stats_pdev {
+ struct list_head list;
+
/* PDEV stats */
s32 ch_noise_floor;
u32 tx_frame_count;
@@ -180,15 +199,11 @@
s32 phy_errs;
s32 phy_err_drop;
s32 mpdu_errs;
+};
- /* VDEV STATS */
-
- /* PEER STATS */
- u8 peers;
- struct ath10k_peer_stat peer_stat[TARGET_NUM_PEERS];
-
- /* TODO: Beacon filter stats */
-
+struct ath10k_fw_stats {
+ struct list_head pdevs;
+ struct list_head peers;
};
struct ath10k_dfs_stats {
@@ -211,6 +226,8 @@
/* protected by data_lock */
DECLARE_BITMAP(peer_ids, ATH10K_MAX_NUM_PEER_IDS);
+
+ /* protected by ar->data_lock */
struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1];
};
@@ -239,6 +256,8 @@
struct sk_buff *beacon;
/* protected by data_lock */
bool beacon_sent;
+ void *beacon_buf;
+ dma_addr_t beacon_paddr;
struct ath10k *ar;
struct ieee80211_vif *vif;
@@ -278,6 +297,7 @@
u8 force_sgi;
bool use_cts_prot;
int num_legacy_stations;
+ int txpower;
};
struct ath10k_vif_iter {
@@ -297,17 +317,19 @@
struct ath10k_debug {
struct dentry *debugfs_phy;
- struct ath10k_target_stats target_stats;
- DECLARE_BITMAP(wmi_service_bitmap, WMI_SERVICE_MAX);
-
- struct completion event_stats_compl;
+ struct ath10k_fw_stats fw_stats;
+ struct completion fw_stats_complete;
+ bool fw_stats_done;
unsigned long htt_stats_mask;
struct delayed_work htt_stats_dwork;
struct ath10k_dfs_stats dfs_stats;
struct ath_dfs_pool_stats dfs_pool_stats;
+ /* protected by conf_mutex */
u32 fw_dbglog_mask;
+ u32 pktlog_filter;
+ u32 reg_addr;
u8 htt_max_amsdu;
u8 htt_max_ampdu;
@@ -326,7 +348,7 @@
* stopped in ath10k_core_restart() work holding conf_mutex. The state
* RESTARTED means that the device is up and mac80211 has started hw
* reconfiguration. Once mac80211 is done with the reconfiguration we
- * set the state to STATE_ON in restart_complete(). */
+ * set the state to STATE_ON in reconfig_complete(). */
ATH10K_STATE_RESTARTING,
ATH10K_STATE_RESTARTED,
@@ -374,8 +396,30 @@
/* Indicates that ath10k device is during CAC phase of DFS */
ATH10K_CAC_RUNNING,
ATH10K_FLAG_CORE_REGISTERED,
+
+ /* Device has crashed and needs to restart. This indicates any pending
+ * waiters should immediately cancel instead of waiting for a time out.
+ */
+ ATH10K_FLAG_CRASH_FLUSH,
};
+enum ath10k_cal_mode {
+ ATH10K_CAL_MODE_FILE,
+ ATH10K_CAL_MODE_OTP,
+};
+
+static inline const char *ath10k_cal_mode_str(enum ath10k_cal_mode mode)
+{
+ switch (mode) {
+ case ATH10K_CAL_MODE_FILE:
+ return "file";
+ case ATH10K_CAL_MODE_OTP:
+ return "otp";
+ }
+
+ return "unknown";
+}
+
enum ath10k_scan_state {
ATH10K_SCAN_IDLE,
ATH10K_SCAN_STARTING,
@@ -426,6 +470,7 @@
bool p2p;
struct {
+ enum ath10k_bus bus;
const struct ath10k_hif_ops *ops;
} hif;
@@ -461,7 +506,10 @@
const void *firmware_data;
size_t firmware_len;
+ const struct firmware *cal_file;
+
int fw_api;
+ enum ath10k_cal_mode cal_mode;
struct {
struct completion started;
@@ -487,7 +535,7 @@
/* current operating channel definition */
struct cfg80211_chan_def chandef;
- int free_vdev_map;
+ unsigned long long free_vdev_map;
bool monitor;
int monitor_vdev_id;
bool monitor_started;
@@ -522,8 +570,12 @@
struct list_head peers;
wait_queue_head_t peer_mapping_wq;
- /* number of created peers; protected by data_lock */
+ /* protected by conf_mutex */
int num_peers;
+ int num_stations;
+
+ int max_num_peers;
+ int max_num_stations;
struct work_struct offchan_tx_work;
struct sk_buff_head offchan_tx_queue;
@@ -569,11 +621,19 @@
bool utf_monitor;
} testmode;
+ struct {
+ /* protected by data_lock */
+ u32 fw_crash_counter;
+ u32 fw_warm_reset_counter;
+ u32 fw_cold_reset_counter;
+ } stats;
+
/* must be last */
u8 drv_priv[0] __aligned(sizeof(void *));
};
struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
+ enum ath10k_bus bus,
const struct ath10k_hif_ops *hif_ops);
void ath10k_core_destroy(struct ath10k *ar);
diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c
index f9a7cc3..ee7f8c1 100644
--- a/drivers/net/wireless/ath/ath10k/debug.c
+++ b/drivers/net/wireless/ath/ath10k/debug.c
@@ -17,12 +17,12 @@
#include <linux/module.h>
#include <linux/debugfs.h>
-#include <linux/version.h>
-#include <linux/vermagic.h>
#include <linux/vmalloc.h>
+#include <linux/utsname.h>
#include "core.h"
#include "debug.h"
+#include "hif.h"
/* ms */
#define ATH10K_DEBUG_HTT_STATS_INTERVAL 1000
@@ -106,34 +106,37 @@
u8 data[0];
} __packed;
-int ath10k_info(struct ath10k *ar, const char *fmt, ...)
+void ath10k_info(struct ath10k *ar, const char *fmt, ...)
{
struct va_format vaf = {
.fmt = fmt,
};
va_list args;
- int ret;
va_start(args, fmt);
vaf.va = &args;
- ret = dev_info(ar->dev, "%pV", &vaf);
+ dev_info(ar->dev, "%pV", &vaf);
trace_ath10k_log_info(ar, &vaf);
va_end(args);
-
- return ret;
}
EXPORT_SYMBOL(ath10k_info);
void ath10k_print_driver_info(struct ath10k *ar)
{
- ath10k_info(ar, "%s (0x%08x, 0x%08x) fw %s api %d htt %d.%d\n",
+ ath10k_info(ar, "%s (0x%08x, 0x%08x) fw %s api %d htt %d.%d wmi %d.%d.%d.%d cal %s max_sta %d\n",
ar->hw_params.name,
ar->target_version,
ar->chip_id,
ar->hw->wiphy->fw_version,
ar->fw_api,
ar->htt.target_version_major,
- ar->htt.target_version_minor);
+ ar->htt.target_version_minor,
+ ar->fw_version_major,
+ ar->fw_version_minor,
+ ar->fw_version_release,
+ ar->fw_version_build,
+ ath10k_cal_mode_str(ar->cal_mode),
+ ar->max_num_stations);
ath10k_info(ar, "debug %d debugfs %d tracing %d dfs %d testmode %d\n",
config_enabled(CPTCFG_ATH10K_DEBUG),
config_enabled(CPTCFG_ATH10K_DEBUGFS),
@@ -143,25 +146,22 @@
}
EXPORT_SYMBOL(ath10k_print_driver_info);
-int ath10k_err(struct ath10k *ar, const char *fmt, ...)
+void ath10k_err(struct ath10k *ar, const char *fmt, ...)
{
struct va_format vaf = {
.fmt = fmt,
};
va_list args;
- int ret;
va_start(args, fmt);
vaf.va = &args;
- ret = dev_err(ar->dev, "%pV", &vaf);
+ dev_err(ar->dev, "%pV", &vaf);
trace_ath10k_log_err(ar, &vaf);
va_end(args);
-
- return ret;
}
EXPORT_SYMBOL(ath10k_err);
-int ath10k_warn(struct ath10k *ar, const char *fmt, ...)
+void ath10k_warn(struct ath10k *ar, const char *fmt, ...)
{
struct va_format vaf = {
.fmt = fmt,
@@ -174,20 +174,11 @@
trace_ath10k_log_warn(ar, &vaf);
va_end(args);
-
- return 0;
}
EXPORT_SYMBOL(ath10k_warn);
#ifdef CPTCFG_ATH10K_DEBUGFS
-void ath10k_debug_read_service_map(struct ath10k *ar,
- void *service_map,
- size_t map_size)
-{
- memcpy(ar->debug.wmi_service_bitmap, service_map, map_size);
-}
-
static ssize_t ath10k_read_wmi_services(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
@@ -209,8 +200,9 @@
if (len > buf_len)
len = buf_len;
+ spin_lock_bh(&ar->data_lock);
for (i = 0; i < WMI_SERVICE_MAX; i++) {
- enabled = test_bit(i, ar->debug.wmi_service_bitmap);
+ enabled = test_bit(i, ar->wmi.svc_map);
name = wmi_service_name(i);
if (!name) {
@@ -226,6 +218,7 @@
"%-40s %s\n",
name, enabled ? "enabled" : "-");
}
+ spin_unlock_bh(&ar->data_lock);
ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
@@ -242,169 +235,182 @@
.llseek = default_llseek,
};
-void ath10k_debug_read_target_stats(struct ath10k *ar,
- struct wmi_stats_event *ev)
+static void ath10k_debug_fw_stats_pdevs_free(struct list_head *head)
{
- u8 *tmp = ev->data;
- struct ath10k_target_stats *stats;
- int num_pdev_stats, num_vdev_stats, num_peer_stats;
- struct wmi_pdev_stats_10x *ps;
- int i;
+ struct ath10k_fw_stats_pdev *i, *tmp;
- spin_lock_bh(&ar->data_lock);
-
- stats = &ar->debug.target_stats;
-
- num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats); /* 0 or 1 */
- num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats); /* 0 or max vdevs */
- num_peer_stats = __le32_to_cpu(ev->num_peer_stats); /* 0 or max peers */
-
- if (num_pdev_stats) {
- ps = (struct wmi_pdev_stats_10x *)tmp;
-
- stats->ch_noise_floor = __le32_to_cpu(ps->chan_nf);
- stats->tx_frame_count = __le32_to_cpu(ps->tx_frame_count);
- stats->rx_frame_count = __le32_to_cpu(ps->rx_frame_count);
- stats->rx_clear_count = __le32_to_cpu(ps->rx_clear_count);
- stats->cycle_count = __le32_to_cpu(ps->cycle_count);
- stats->phy_err_count = __le32_to_cpu(ps->phy_err_count);
- stats->chan_tx_power = __le32_to_cpu(ps->chan_tx_pwr);
-
- stats->comp_queued = __le32_to_cpu(ps->wal.tx.comp_queued);
- stats->comp_delivered =
- __le32_to_cpu(ps->wal.tx.comp_delivered);
- stats->msdu_enqued = __le32_to_cpu(ps->wal.tx.msdu_enqued);
- stats->mpdu_enqued = __le32_to_cpu(ps->wal.tx.mpdu_enqued);
- stats->wmm_drop = __le32_to_cpu(ps->wal.tx.wmm_drop);
- stats->local_enqued = __le32_to_cpu(ps->wal.tx.local_enqued);
- stats->local_freed = __le32_to_cpu(ps->wal.tx.local_freed);
- stats->hw_queued = __le32_to_cpu(ps->wal.tx.hw_queued);
- stats->hw_reaped = __le32_to_cpu(ps->wal.tx.hw_reaped);
- stats->underrun = __le32_to_cpu(ps->wal.tx.underrun);
- stats->tx_abort = __le32_to_cpu(ps->wal.tx.tx_abort);
- stats->mpdus_requed = __le32_to_cpu(ps->wal.tx.mpdus_requed);
- stats->tx_ko = __le32_to_cpu(ps->wal.tx.tx_ko);
- stats->data_rc = __le32_to_cpu(ps->wal.tx.data_rc);
- stats->self_triggers = __le32_to_cpu(ps->wal.tx.self_triggers);
- stats->sw_retry_failure =
- __le32_to_cpu(ps->wal.tx.sw_retry_failure);
- stats->illgl_rate_phy_err =
- __le32_to_cpu(ps->wal.tx.illgl_rate_phy_err);
- stats->pdev_cont_xretry =
- __le32_to_cpu(ps->wal.tx.pdev_cont_xretry);
- stats->pdev_tx_timeout =
- __le32_to_cpu(ps->wal.tx.pdev_tx_timeout);
- stats->pdev_resets = __le32_to_cpu(ps->wal.tx.pdev_resets);
- stats->phy_underrun = __le32_to_cpu(ps->wal.tx.phy_underrun);
- stats->txop_ovf = __le32_to_cpu(ps->wal.tx.txop_ovf);
-
- stats->mid_ppdu_route_change =
- __le32_to_cpu(ps->wal.rx.mid_ppdu_route_change);
- stats->status_rcvd = __le32_to_cpu(ps->wal.rx.status_rcvd);
- stats->r0_frags = __le32_to_cpu(ps->wal.rx.r0_frags);
- stats->r1_frags = __le32_to_cpu(ps->wal.rx.r1_frags);
- stats->r2_frags = __le32_to_cpu(ps->wal.rx.r2_frags);
- stats->r3_frags = __le32_to_cpu(ps->wal.rx.r3_frags);
- stats->htt_msdus = __le32_to_cpu(ps->wal.rx.htt_msdus);
- stats->htt_mpdus = __le32_to_cpu(ps->wal.rx.htt_mpdus);
- stats->loc_msdus = __le32_to_cpu(ps->wal.rx.loc_msdus);
- stats->loc_mpdus = __le32_to_cpu(ps->wal.rx.loc_mpdus);
- stats->oversize_amsdu =
- __le32_to_cpu(ps->wal.rx.oversize_amsdu);
- stats->phy_errs = __le32_to_cpu(ps->wal.rx.phy_errs);
- stats->phy_err_drop = __le32_to_cpu(ps->wal.rx.phy_err_drop);
- stats->mpdu_errs = __le32_to_cpu(ps->wal.rx.mpdu_errs);
-
- if (test_bit(ATH10K_FW_FEATURE_WMI_10X,
- ar->fw_features)) {
- stats->ack_rx_bad = __le32_to_cpu(ps->ack_rx_bad);
- stats->rts_bad = __le32_to_cpu(ps->rts_bad);
- stats->rts_good = __le32_to_cpu(ps->rts_good);
- stats->fcs_bad = __le32_to_cpu(ps->fcs_bad);
- stats->no_beacons = __le32_to_cpu(ps->no_beacons);
- stats->mib_int_count = __le32_to_cpu(ps->mib_int_count);
- tmp += sizeof(struct wmi_pdev_stats_10x);
- } else {
- tmp += sizeof(struct wmi_pdev_stats_old);
- }
+ list_for_each_entry_safe(i, tmp, head, list) {
+ list_del(&i->list);
+ kfree(i);
}
-
- /* 0 or max vdevs */
- /* Currently firmware does not support VDEV stats */
- if (num_vdev_stats) {
- struct wmi_vdev_stats *vdev_stats;
-
- for (i = 0; i < num_vdev_stats; i++) {
- vdev_stats = (struct wmi_vdev_stats *)tmp;
- tmp += sizeof(struct wmi_vdev_stats);
- }
- }
-
- if (num_peer_stats) {
- struct wmi_peer_stats_10x *peer_stats;
- struct ath10k_peer_stat *s;
-
- stats->peers = num_peer_stats;
-
- for (i = 0; i < num_peer_stats; i++) {
- peer_stats = (struct wmi_peer_stats_10x *)tmp;
- s = &stats->peer_stat[i];
-
- memcpy(s->peer_macaddr, &peer_stats->peer_macaddr.addr,
- ETH_ALEN);
- s->peer_rssi = __le32_to_cpu(peer_stats->peer_rssi);
- s->peer_tx_rate =
- __le32_to_cpu(peer_stats->peer_tx_rate);
- if (test_bit(ATH10K_FW_FEATURE_WMI_10X,
- ar->fw_features)) {
- s->peer_rx_rate =
- __le32_to_cpu(peer_stats->peer_rx_rate);
- tmp += sizeof(struct wmi_peer_stats_10x);
-
- } else {
- tmp += sizeof(struct wmi_peer_stats_old);
- }
- }
- }
-
- spin_unlock_bh(&ar->data_lock);
- complete(&ar->debug.event_stats_compl);
}
-static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
+static void ath10k_debug_fw_stats_peers_free(struct list_head *head)
{
- struct ath10k *ar = file->private_data;
- struct ath10k_target_stats *fw_stats;
- char *buf = NULL;
- unsigned int len = 0, buf_len = 8000;
- ssize_t ret_cnt = 0;
- long left;
- int i;
+ struct ath10k_fw_stats_peer *i, *tmp;
+
+ list_for_each_entry_safe(i, tmp, head, list) {
+ list_del(&i->list);
+ kfree(i);
+ }
+}
+
+static void ath10k_debug_fw_stats_reset(struct ath10k *ar)
+{
+ spin_lock_bh(&ar->data_lock);
+ ar->debug.fw_stats_done = false;
+ ath10k_debug_fw_stats_pdevs_free(&ar->debug.fw_stats.pdevs);
+ ath10k_debug_fw_stats_peers_free(&ar->debug.fw_stats.peers);
+ spin_unlock_bh(&ar->data_lock);
+}
+
+static size_t ath10k_debug_fw_stats_num_peers(struct list_head *head)
+{
+ struct ath10k_fw_stats_peer *i;
+ size_t num = 0;
+
+ list_for_each_entry(i, head, list)
+ ++num;
+
+ return num;
+}
+
+void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb)
+{
+ struct ath10k_fw_stats stats = {};
+ bool is_start, is_started, is_end;
+ size_t num_peers;
int ret;
- fw_stats = &ar->debug.target_stats;
-
- mutex_lock(&ar->conf_mutex);
-
- if (ar->state != ATH10K_STATE_ON)
- goto exit;
-
- buf = kzalloc(buf_len, GFP_KERNEL|__GFP_REPEAT|__GFP_HIGH);
- if (!buf)
- goto exit;
-
- ret = ath10k_wmi_request_stats(ar, WMI_REQUEST_PEER_STAT);
- if (ret) {
- ath10k_warn(ar, "could not request stats (%d)\n", ret);
- goto exit;
- }
-
- left = wait_for_completion_timeout(&ar->debug.event_stats_compl, 1*HZ);
- if (left <= 0)
- goto exit;
+ INIT_LIST_HEAD(&stats.pdevs);
+ INIT_LIST_HEAD(&stats.peers);
spin_lock_bh(&ar->data_lock);
+ ret = ath10k_wmi_pull_fw_stats(ar, skb, &stats);
+ if (ret) {
+ ath10k_warn(ar, "failed to pull fw stats: %d\n", ret);
+ goto unlock;
+ }
+
+ /* Stat data may exceed htc-wmi buffer limit. In such case firmware
+ * splits the stats data and delivers it in a ping-pong fashion of
+ * request cmd-update event.
+ *
+ * However there is no explicit end-of-data. Instead start-of-data is
+ * used as an implicit one. This works as follows:
+ * a) discard stat update events until one with pdev stats is
+ * delivered - this skips session started at end of (b)
+ * b) consume stat update events until another one with pdev stats is
+ * delivered which is treated as end-of-data and is itself discarded
+ */
+
+ if (ar->debug.fw_stats_done) {
+ ath10k_warn(ar, "received unsolicited stats update event\n");
+ goto free;
+ }
+
+ num_peers = ath10k_debug_fw_stats_num_peers(&ar->debug.fw_stats.peers);
+ is_start = (list_empty(&ar->debug.fw_stats.pdevs) &&
+ !list_empty(&stats.pdevs));
+ is_end = (!list_empty(&ar->debug.fw_stats.pdevs) &&
+ !list_empty(&stats.pdevs));
+
+ if (is_start)
+ list_splice_tail_init(&stats.pdevs, &ar->debug.fw_stats.pdevs);
+
+ if (is_end)
+ ar->debug.fw_stats_done = true;
+
+ is_started = !list_empty(&ar->debug.fw_stats.pdevs);
+
+ if (is_started && !is_end) {
+ if (num_peers >= ATH10K_MAX_NUM_PEER_IDS) {
+ /* Although this is unlikely impose a sane limit to
+ * prevent firmware from DoS-ing the host.
+ */
+ ath10k_warn(ar, "dropping fw peer stats\n");
+ goto free;
+ }
+
+ list_splice_tail_init(&stats.peers, &ar->debug.fw_stats.peers);
+ }
+
+ complete(&ar->debug.fw_stats_complete);
+
+free:
+ /* In some cases lists have been spliced and cleared. Free up
+ * resources if that is not the case.
+ */
+ ath10k_debug_fw_stats_pdevs_free(&stats.pdevs);
+ ath10k_debug_fw_stats_peers_free(&stats.peers);
+
+unlock:
+ spin_unlock_bh(&ar->data_lock);
+}
+
+static int ath10k_debug_fw_stats_request(struct ath10k *ar)
+{
+ unsigned long timeout;
+ int ret;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+ timeout = jiffies + msecs_to_jiffies(1*HZ);
+
+ ath10k_debug_fw_stats_reset(ar);
+
+ for (;;) {
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
+
+ reinit_completion(&ar->debug.fw_stats_complete);
+
+ ret = ath10k_wmi_request_stats(ar, WMI_REQUEST_PEER_STAT);
+ if (ret) {
+ ath10k_warn(ar, "could not request stats (%d)\n", ret);
+ return ret;
+ }
+
+ ret = wait_for_completion_timeout(&ar->debug.fw_stats_complete,
+ 1*HZ);
+ if (ret <= 0)
+ return -ETIMEDOUT;
+
+ spin_lock_bh(&ar->data_lock);
+ if (ar->debug.fw_stats_done) {
+ spin_unlock_bh(&ar->data_lock);
+ break;
+ }
+ spin_unlock_bh(&ar->data_lock);
+ }
+
+ return 0;
+}
+
+/* FIXME: How to calculate the buffer size sanely? */
+#define ATH10K_FW_STATS_BUF_SIZE (1024*1024)
+
+static void ath10k_fw_stats_fill(struct ath10k *ar,
+ struct ath10k_fw_stats *fw_stats,
+ char *buf)
+{
+ unsigned int len = 0;
+ unsigned int buf_len = ATH10K_FW_STATS_BUF_SIZE;
+ const struct ath10k_fw_stats_pdev *pdev;
+ const struct ath10k_fw_stats_peer *peer;
+ size_t num_peers;
+
+ spin_lock_bh(&ar->data_lock);
+
+ pdev = list_first_entry_or_null(&fw_stats->pdevs,
+ struct ath10k_fw_stats_pdev, list);
+ if (!pdev) {
+ ath10k_warn(ar, "failed to get pdev stats\n");
+ goto unlock;
+ }
+
+ num_peers = ath10k_debug_fw_stats_num_peers(&fw_stats->peers);
+
len += scnprintf(buf + len, buf_len - len, "\n");
len += scnprintf(buf + len, buf_len - len, "%30s\n",
"ath10k PDEV stats");
@@ -412,29 +418,29 @@
"=================");
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Channel noise floor", fw_stats->ch_noise_floor);
+ "Channel noise floor", pdev->ch_noise_floor);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "Channel TX power", fw_stats->chan_tx_power);
+ "Channel TX power", pdev->chan_tx_power);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "TX frame count", fw_stats->tx_frame_count);
+ "TX frame count", pdev->tx_frame_count);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "RX frame count", fw_stats->rx_frame_count);
+ "RX frame count", pdev->rx_frame_count);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "RX clear count", fw_stats->rx_clear_count);
+ "RX clear count", pdev->rx_clear_count);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "Cycle count", fw_stats->cycle_count);
+ "Cycle count", pdev->cycle_count);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "PHY error count", fw_stats->phy_err_count);
+ "PHY error count", pdev->phy_err_count);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "RTS bad count", fw_stats->rts_bad);
+ "RTS bad count", pdev->rts_bad);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "RTS good count", fw_stats->rts_good);
+ "RTS good count", pdev->rts_good);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "FCS bad count", fw_stats->fcs_bad);
+ "FCS bad count", pdev->fcs_bad);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "No beacon count", fw_stats->no_beacons);
+ "No beacon count", pdev->no_beacons);
len += scnprintf(buf + len, buf_len - len, "%30s %10u\n",
- "MIB int count", fw_stats->mib_int_count);
+ "MIB int count", pdev->mib_int_count);
len += scnprintf(buf + len, buf_len - len, "\n");
len += scnprintf(buf + len, buf_len - len, "%30s\n",
@@ -443,51 +449,51 @@
"=================");
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "HTT cookies queued", fw_stats->comp_queued);
+ "HTT cookies queued", pdev->comp_queued);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "HTT cookies disp.", fw_stats->comp_delivered);
+ "HTT cookies disp.", pdev->comp_delivered);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "MSDU queued", fw_stats->msdu_enqued);
+ "MSDU queued", pdev->msdu_enqued);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "MPDU queued", fw_stats->mpdu_enqued);
+ "MPDU queued", pdev->mpdu_enqued);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "MSDUs dropped", fw_stats->wmm_drop);
+ "MSDUs dropped", pdev->wmm_drop);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Local enqued", fw_stats->local_enqued);
+ "Local enqued", pdev->local_enqued);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Local freed", fw_stats->local_freed);
+ "Local freed", pdev->local_freed);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "HW queued", fw_stats->hw_queued);
+ "HW queued", pdev->hw_queued);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "PPDUs reaped", fw_stats->hw_reaped);
+ "PPDUs reaped", pdev->hw_reaped);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Num underruns", fw_stats->underrun);
+ "Num underruns", pdev->underrun);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "PPDUs cleaned", fw_stats->tx_abort);
+ "PPDUs cleaned", pdev->tx_abort);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "MPDUs requed", fw_stats->mpdus_requed);
+ "MPDUs requed", pdev->mpdus_requed);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Excessive retries", fw_stats->tx_ko);
+ "Excessive retries", pdev->tx_ko);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "HW rate", fw_stats->data_rc);
+ "HW rate", pdev->data_rc);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Sched self tiggers", fw_stats->self_triggers);
+ "Sched self tiggers", pdev->self_triggers);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
"Dropped due to SW retries",
- fw_stats->sw_retry_failure);
+ pdev->sw_retry_failure);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
"Illegal rate phy errors",
- fw_stats->illgl_rate_phy_err);
+ pdev->illgl_rate_phy_err);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Pdev continous xretry", fw_stats->pdev_cont_xretry);
+ "Pdev continous xretry", pdev->pdev_cont_xretry);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "TX timeout", fw_stats->pdev_tx_timeout);
+ "TX timeout", pdev->pdev_tx_timeout);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "PDEV resets", fw_stats->pdev_resets);
+ "PDEV resets", pdev->pdev_resets);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "PHY underrun", fw_stats->phy_underrun);
+ "PHY underrun", pdev->phy_underrun);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "MPDU is more than txop limit", fw_stats->txop_ovf);
+ "MPDU is more than txop limit", pdev->txop_ovf);
len += scnprintf(buf + len, buf_len - len, "\n");
len += scnprintf(buf + len, buf_len - len, "%30s\n",
@@ -497,70 +503,161 @@
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
"Mid PPDU route change",
- fw_stats->mid_ppdu_route_change);
+ pdev->mid_ppdu_route_change);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Tot. number of statuses", fw_stats->status_rcvd);
+ "Tot. number of statuses", pdev->status_rcvd);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Extra frags on rings 0", fw_stats->r0_frags);
+ "Extra frags on rings 0", pdev->r0_frags);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Extra frags on rings 1", fw_stats->r1_frags);
+ "Extra frags on rings 1", pdev->r1_frags);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Extra frags on rings 2", fw_stats->r2_frags);
+ "Extra frags on rings 2", pdev->r2_frags);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Extra frags on rings 3", fw_stats->r3_frags);
+ "Extra frags on rings 3", pdev->r3_frags);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "MSDUs delivered to HTT", fw_stats->htt_msdus);
+ "MSDUs delivered to HTT", pdev->htt_msdus);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "MPDUs delivered to HTT", fw_stats->htt_mpdus);
+ "MPDUs delivered to HTT", pdev->htt_mpdus);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "MSDUs delivered to stack", fw_stats->loc_msdus);
+ "MSDUs delivered to stack", pdev->loc_msdus);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "MPDUs delivered to stack", fw_stats->loc_mpdus);
+ "MPDUs delivered to stack", pdev->loc_mpdus);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "Oversized AMSUs", fw_stats->oversize_amsdu);
+ "Oversized AMSUs", pdev->oversize_amsdu);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "PHY errors", fw_stats->phy_errs);
+ "PHY errors", pdev->phy_errs);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "PHY errors drops", fw_stats->phy_err_drop);
+ "PHY errors drops", pdev->phy_err_drop);
len += scnprintf(buf + len, buf_len - len, "%30s %10d\n",
- "MPDU errors (FCS, MIC, ENC)", fw_stats->mpdu_errs);
+ "MPDU errors (FCS, MIC, ENC)", pdev->mpdu_errs);
len += scnprintf(buf + len, buf_len - len, "\n");
- len += scnprintf(buf + len, buf_len - len, "%30s (%d)\n",
- "ath10k PEER stats", fw_stats->peers);
+ len += scnprintf(buf + len, buf_len - len, "%30s (%zu)\n",
+ "ath10k PEER stats", num_peers);
len += scnprintf(buf + len, buf_len - len, "%30s\n\n",
"=================");
- for (i = 0; i < fw_stats->peers; i++) {
+ list_for_each_entry(peer, &fw_stats->peers, list) {
len += scnprintf(buf + len, buf_len - len, "%30s %pM\n",
- "Peer MAC address",
- fw_stats->peer_stat[i].peer_macaddr);
+ "Peer MAC address", peer->peer_macaddr);
len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
- "Peer RSSI", fw_stats->peer_stat[i].peer_rssi);
+ "Peer RSSI", peer->peer_rssi);
len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
- "Peer TX rate",
- fw_stats->peer_stat[i].peer_tx_rate);
+ "Peer TX rate", peer->peer_tx_rate);
len += scnprintf(buf + len, buf_len - len, "%30s %u\n",
- "Peer RX rate",
- fw_stats->peer_stat[i].peer_rx_rate);
+ "Peer RX rate", peer->peer_rx_rate);
len += scnprintf(buf + len, buf_len - len, "\n");
}
+
+unlock:
spin_unlock_bh(&ar->data_lock);
- if (len > buf_len)
- len = buf_len;
+ if (len >= buf_len)
+ buf[len - 1] = 0;
+ else
+ buf[len] = 0;
+}
- ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+static int ath10k_fw_stats_open(struct inode *inode, struct file *file)
+{
+ struct ath10k *ar = inode->i_private;
+ void *buf = NULL;
+ int ret;
-exit:
+ mutex_lock(&ar->conf_mutex);
+
+ if (ar->state != ATH10K_STATE_ON) {
+ ret = -ENETDOWN;
+ goto err_unlock;
+ }
+
+ buf = vmalloc(ATH10K_FW_STATS_BUF_SIZE);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto err_unlock;
+ }
+
+ ret = ath10k_debug_fw_stats_request(ar);
+ if (ret) {
+ ath10k_warn(ar, "failed to request fw stats: %d\n", ret);
+ goto err_free;
+ }
+
+ ath10k_fw_stats_fill(ar, &ar->debug.fw_stats, buf);
+ file->private_data = buf;
+
mutex_unlock(&ar->conf_mutex);
- kfree(buf);
- return ret_cnt;
+ return 0;
+
+err_free:
+ vfree(buf);
+
+err_unlock:
+ mutex_unlock(&ar->conf_mutex);
+ return ret;
+}
+
+static int ath10k_fw_stats_release(struct inode *inode, struct file *file)
+{
+ vfree(file->private_data);
+
+ return 0;
+}
+
+static ssize_t ath10k_fw_stats_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ const char *buf = file->private_data;
+ unsigned int len = strlen(buf);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
static const struct file_operations fops_fw_stats = {
- .read = ath10k_read_fw_stats,
+ .open = ath10k_fw_stats_open,
+ .release = ath10k_fw_stats_release,
+ .read = ath10k_fw_stats_read,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static ssize_t ath10k_debug_fw_reset_stats_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ int ret, len, buf_len;
+ char *buf;
+
+ buf_len = 500;
+ buf = kmalloc(buf_len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ spin_lock_bh(&ar->data_lock);
+
+ len = 0;
+ len += scnprintf(buf + len, buf_len - len,
+ "fw_crash_counter\t\t%d\n", ar->stats.fw_crash_counter);
+ len += scnprintf(buf + len, buf_len - len,
+ "fw_warm_reset_counter\t\t%d\n",
+ ar->stats.fw_warm_reset_counter);
+ len += scnprintf(buf + len, buf_len - len,
+ "fw_cold_reset_counter\t\t%d\n",
+ ar->stats.fw_cold_reset_counter);
+
+ spin_unlock_bh(&ar->data_lock);
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+ kfree(buf);
+
+ return ret;
+}
+
+static const struct file_operations fops_fw_reset_stats = {
.open = simple_open,
+ .read = ath10k_debug_fw_reset_stats_read,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
@@ -593,7 +690,8 @@
"To simulate firmware crash write one of the keywords to this file:\n"
"`soft` - this will send WMI_FORCE_FW_HANG_ASSERT to firmware if FW supports that command.\n"
"`hard` - this will send to firmware command with illegal parameters causing firmware crash.\n"
- "`assert` - this will send special illegal parameter to firmware to cause assert failure and crash.\n";
+ "`assert` - this will send special illegal parameter to firmware to cause assert failure and crash.\n"
+ "`hw-restart` - this will simply queue hw restart without fw/hw actually crashing.\n";
return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
}
@@ -647,6 +745,10 @@
} else if (!strcmp(buf, "assert")) {
ath10k_info(ar, "simulating firmware assert crash\n");
ret = ath10k_debug_fw_assert(ar);
+ } else if (!strcmp(buf, "hw-restart")) {
+ ath10k_info(ar, "user requested hw restart\n");
+ queue_work(ar->workqueue, &ar->restart_work);
+ ret = 0;
} else {
ret = -EINVAL;
goto exit;
@@ -760,8 +862,8 @@
strlcpy(dump_data->fw_ver, ar->hw->wiphy->fw_version,
sizeof(dump_data->fw_ver));
- dump_data->kernel_ver_code = cpu_to_le32(LINUX_VERSION_CODE);
- strlcpy(dump_data->kernel_ver, VERMAGIC_STRING,
+ dump_data->kernel_ver_code = 0;
+ strlcpy(dump_data->kernel_ver, init_utsname()->release,
sizeof(dump_data->kernel_ver));
dump_data->tv_sec = cpu_to_le64(crash_data->timestamp.tv_sec);
@@ -823,6 +925,236 @@
.llseek = default_llseek,
};
+static ssize_t ath10k_reg_addr_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ u8 buf[32];
+ unsigned int len = 0;
+ u32 reg_addr;
+
+ mutex_lock(&ar->conf_mutex);
+ reg_addr = ar->debug.reg_addr;
+ mutex_unlock(&ar->conf_mutex);
+
+ len += scnprintf(buf + len, sizeof(buf) - len, "0x%x\n", reg_addr);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t ath10k_reg_addr_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ u32 reg_addr;
+ int ret;
+
+ ret = kstrtou32_from_user(user_buf, count, 0, ®_addr);
+ if (ret)
+ return ret;
+
+ if (!IS_ALIGNED(reg_addr, 4))
+ return -EFAULT;
+
+ mutex_lock(&ar->conf_mutex);
+ ar->debug.reg_addr = reg_addr;
+ mutex_unlock(&ar->conf_mutex);
+
+ return count;
+}
+
+static const struct file_operations fops_reg_addr = {
+ .read = ath10k_reg_addr_read,
+ .write = ath10k_reg_addr_write,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static ssize_t ath10k_reg_value_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ u8 buf[48];
+ unsigned int len;
+ u32 reg_addr, reg_val;
+ int ret;
+
+ mutex_lock(&ar->conf_mutex);
+
+ if (ar->state != ATH10K_STATE_ON &&
+ ar->state != ATH10K_STATE_UTF) {
+ ret = -ENETDOWN;
+ goto exit;
+ }
+
+ reg_addr = ar->debug.reg_addr;
+
+ reg_val = ath10k_hif_read32(ar, reg_addr);
+ len = scnprintf(buf, sizeof(buf), "0x%08x:0x%08x\n", reg_addr, reg_val);
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+exit:
+ mutex_unlock(&ar->conf_mutex);
+
+ return ret;
+}
+
+static ssize_t ath10k_reg_value_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ u32 reg_addr, reg_val;
+ int ret;
+
+ mutex_lock(&ar->conf_mutex);
+
+ if (ar->state != ATH10K_STATE_ON &&
+ ar->state != ATH10K_STATE_UTF) {
+ ret = -ENETDOWN;
+ goto exit;
+ }
+
+ reg_addr = ar->debug.reg_addr;
+
+ ret = kstrtou32_from_user(user_buf, count, 0, ®_val);
+ if (ret)
+ goto exit;
+
+ ath10k_hif_write32(ar, reg_addr, reg_val);
+
+ ret = count;
+
+exit:
+ mutex_unlock(&ar->conf_mutex);
+
+ return ret;
+}
+
+static const struct file_operations fops_reg_value = {
+ .read = ath10k_reg_value_read,
+ .write = ath10k_reg_value_write,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+static ssize_t ath10k_mem_value_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ u8 *buf;
+ int ret;
+
+ if (*ppos < 0)
+ return -EINVAL;
+
+ if (!count)
+ return 0;
+
+ mutex_lock(&ar->conf_mutex);
+
+ buf = vmalloc(count);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ if (ar->state != ATH10K_STATE_ON &&
+ ar->state != ATH10K_STATE_UTF) {
+ ret = -ENETDOWN;
+ goto exit;
+ }
+
+ ret = ath10k_hif_diag_read(ar, *ppos, buf, count);
+ if (ret) {
+ ath10k_warn(ar, "failed to read address 0x%08x via diagnose window fnrom debugfs: %d\n",
+ (u32)(*ppos), ret);
+ goto exit;
+ }
+
+ ret = copy_to_user(user_buf, buf, count);
+ if (ret) {
+ ret = -EFAULT;
+ goto exit;
+ }
+
+ count -= ret;
+ *ppos += count;
+ ret = count;
+
+exit:
+ vfree(buf);
+ mutex_unlock(&ar->conf_mutex);
+
+ return ret;
+}
+
+static ssize_t ath10k_mem_value_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ u8 *buf;
+ int ret;
+
+ if (*ppos < 0)
+ return -EINVAL;
+
+ if (!count)
+ return 0;
+
+ mutex_lock(&ar->conf_mutex);
+
+ buf = vmalloc(count);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ if (ar->state != ATH10K_STATE_ON &&
+ ar->state != ATH10K_STATE_UTF) {
+ ret = -ENETDOWN;
+ goto exit;
+ }
+
+ ret = copy_from_user(buf, user_buf, count);
+ if (ret) {
+ ret = -EFAULT;
+ goto exit;
+ }
+
+ ret = ath10k_hif_diag_write(ar, *ppos, buf, count);
+ if (ret) {
+ ath10k_warn(ar, "failed to write address 0x%08x via diagnose window from debugfs: %d\n",
+ (u32)(*ppos), ret);
+ goto exit;
+ }
+
+ *ppos += count;
+ ret = count;
+
+exit:
+ vfree(buf);
+ mutex_unlock(&ar->conf_mutex);
+
+ return ret;
+}
+
+static const struct file_operations fops_mem_value = {
+ .read = ath10k_mem_value_read,
+ .write = ath10k_mem_value_write,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
static int ath10k_debug_htt_stats_req(struct ath10k *ar)
{
u64 cookie;
@@ -1030,6 +1362,166 @@
return ret;
}
+/* TODO: Would be nice to always support ethtool stats, would need to
+ * move the stats storage out of ath10k_debug, or always have ath10k_debug
+ * struct available..
+ */
+
+/* This generally cooresponds to the debugfs fw_stats file */
+static const char ath10k_gstrings_stats[][ETH_GSTRING_LEN] = {
+ "tx_pkts_nic",
+ "tx_bytes_nic",
+ "rx_pkts_nic",
+ "rx_bytes_nic",
+ "d_noise_floor",
+ "d_cycle_count",
+ "d_phy_error",
+ "d_rts_bad",
+ "d_rts_good",
+ "d_tx_power", /* in .5 dbM I think */
+ "d_rx_crc_err", /* fcs_bad */
+ "d_no_beacon",
+ "d_tx_mpdus_queued",
+ "d_tx_msdu_queued",
+ "d_tx_msdu_dropped",
+ "d_local_enqued",
+ "d_local_freed",
+ "d_tx_ppdu_hw_queued",
+ "d_tx_ppdu_reaped",
+ "d_tx_fifo_underrun",
+ "d_tx_ppdu_abort",
+ "d_tx_mpdu_requed",
+ "d_tx_excessive_retries",
+ "d_tx_hw_rate",
+ "d_tx_dropped_sw_retries",
+ "d_tx_illegal_rate",
+ "d_tx_continuous_xretries",
+ "d_tx_timeout",
+ "d_tx_mpdu_txop_limit",
+ "d_pdev_resets",
+ "d_rx_mid_ppdu_route_change",
+ "d_rx_status",
+ "d_rx_extra_frags_ring0",
+ "d_rx_extra_frags_ring1",
+ "d_rx_extra_frags_ring2",
+ "d_rx_extra_frags_ring3",
+ "d_rx_msdu_htt",
+ "d_rx_mpdu_htt",
+ "d_rx_msdu_stack",
+ "d_rx_mpdu_stack",
+ "d_rx_phy_err",
+ "d_rx_phy_err_drops",
+ "d_rx_mpdu_errors", /* FCS, MIC, ENC */
+ "d_fw_crash_count",
+ "d_fw_warm_reset_count",
+ "d_fw_cold_reset_count",
+};
+
+#define ATH10K_SSTATS_LEN ARRAY_SIZE(ath10k_gstrings_stats)
+
+void ath10k_debug_get_et_strings(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ u32 sset, u8 *data)
+{
+ if (sset == ETH_SS_STATS)
+ memcpy(data, *ath10k_gstrings_stats,
+ sizeof(ath10k_gstrings_stats));
+}
+
+int ath10k_debug_get_et_sset_count(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, int sset)
+{
+ if (sset == ETH_SS_STATS)
+ return ATH10K_SSTATS_LEN;
+
+ return 0;
+}
+
+void ath10k_debug_get_et_stats(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ethtool_stats *stats, u64 *data)
+{
+ struct ath10k *ar = hw->priv;
+ static const struct ath10k_fw_stats_pdev zero_stats = {};
+ const struct ath10k_fw_stats_pdev *pdev_stats;
+ int i = 0, ret;
+
+ mutex_lock(&ar->conf_mutex);
+
+ if (ar->state == ATH10K_STATE_ON) {
+ ret = ath10k_debug_fw_stats_request(ar);
+ if (ret) {
+ /* just print a warning and try to use older results */
+ ath10k_warn(ar,
+ "failed to get fw stats for ethtool: %d\n",
+ ret);
+ }
+ }
+
+ pdev_stats = list_first_entry_or_null(&ar->debug.fw_stats.pdevs,
+ struct ath10k_fw_stats_pdev,
+ list);
+ if (!pdev_stats) {
+ /* no results available so just return zeroes */
+ pdev_stats = &zero_stats;
+ }
+
+ spin_lock_bh(&ar->data_lock);
+
+ data[i++] = pdev_stats->hw_reaped; /* ppdu reaped */
+ data[i++] = 0; /* tx bytes */
+ data[i++] = pdev_stats->htt_mpdus;
+ data[i++] = 0; /* rx bytes */
+ data[i++] = pdev_stats->ch_noise_floor;
+ data[i++] = pdev_stats->cycle_count;
+ data[i++] = pdev_stats->phy_err_count;
+ data[i++] = pdev_stats->rts_bad;
+ data[i++] = pdev_stats->rts_good;
+ data[i++] = pdev_stats->chan_tx_power;
+ data[i++] = pdev_stats->fcs_bad;
+ data[i++] = pdev_stats->no_beacons;
+ data[i++] = pdev_stats->mpdu_enqued;
+ data[i++] = pdev_stats->msdu_enqued;
+ data[i++] = pdev_stats->wmm_drop;
+ data[i++] = pdev_stats->local_enqued;
+ data[i++] = pdev_stats->local_freed;
+ data[i++] = pdev_stats->hw_queued;
+ data[i++] = pdev_stats->hw_reaped;
+ data[i++] = pdev_stats->underrun;
+ data[i++] = pdev_stats->tx_abort;
+ data[i++] = pdev_stats->mpdus_requed;
+ data[i++] = pdev_stats->tx_ko;
+ data[i++] = pdev_stats->data_rc;
+ data[i++] = pdev_stats->sw_retry_failure;
+ data[i++] = pdev_stats->illgl_rate_phy_err;
+ data[i++] = pdev_stats->pdev_cont_xretry;
+ data[i++] = pdev_stats->pdev_tx_timeout;
+ data[i++] = pdev_stats->txop_ovf;
+ data[i++] = pdev_stats->pdev_resets;
+ data[i++] = pdev_stats->mid_ppdu_route_change;
+ data[i++] = pdev_stats->status_rcvd;
+ data[i++] = pdev_stats->r0_frags;
+ data[i++] = pdev_stats->r1_frags;
+ data[i++] = pdev_stats->r2_frags;
+ data[i++] = pdev_stats->r3_frags;
+ data[i++] = pdev_stats->htt_msdus;
+ data[i++] = pdev_stats->htt_mpdus;
+ data[i++] = pdev_stats->loc_msdus;
+ data[i++] = pdev_stats->loc_mpdus;
+ data[i++] = pdev_stats->phy_errs;
+ data[i++] = pdev_stats->phy_err_drop;
+ data[i++] = pdev_stats->mpdu_errs;
+ data[i++] = ar->stats.fw_crash_counter;
+ data[i++] = ar->stats.fw_warm_reset_counter;
+ data[i++] = ar->stats.fw_cold_reset_counter;
+
+ spin_unlock_bh(&ar->data_lock);
+
+ mutex_unlock(&ar->conf_mutex);
+
+ WARN_ON(i != ATH10K_SSTATS_LEN);
+}
+
static const struct file_operations fops_fw_dbglog = {
.read = ath10k_read_fw_dbglog,
.write = ath10k_write_fw_dbglog,
@@ -1038,6 +1530,84 @@
.llseek = default_llseek,
};
+static int ath10k_debug_cal_data_open(struct inode *inode, struct file *file)
+{
+ struct ath10k *ar = inode->i_private;
+ void *buf;
+ u32 hi_addr;
+ __le32 addr;
+ int ret;
+
+ mutex_lock(&ar->conf_mutex);
+
+ if (ar->state != ATH10K_STATE_ON &&
+ ar->state != ATH10K_STATE_UTF) {
+ ret = -ENETDOWN;
+ goto err;
+ }
+
+ buf = vmalloc(QCA988X_CAL_DATA_LEN);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ hi_addr = host_interest_item_address(HI_ITEM(hi_board_data));
+
+ ret = ath10k_hif_diag_read(ar, hi_addr, &addr, sizeof(addr));
+ if (ret) {
+ ath10k_warn(ar, "failed to read hi_board_data address: %d\n", ret);
+ goto err_vfree;
+ }
+
+ ret = ath10k_hif_diag_read(ar, le32_to_cpu(addr), buf,
+ QCA988X_CAL_DATA_LEN);
+ if (ret) {
+ ath10k_warn(ar, "failed to read calibration data: %d\n", ret);
+ goto err_vfree;
+ }
+
+ file->private_data = buf;
+
+ mutex_unlock(&ar->conf_mutex);
+
+ return 0;
+
+err_vfree:
+ vfree(buf);
+
+err:
+ mutex_unlock(&ar->conf_mutex);
+
+ return ret;
+}
+
+static ssize_t ath10k_debug_cal_data_read(struct file *file,
+ char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ void *buf = file->private_data;
+
+ return simple_read_from_buffer(user_buf, count, ppos,
+ buf, QCA988X_CAL_DATA_LEN);
+}
+
+static int ath10k_debug_cal_data_release(struct inode *inode,
+ struct file *file)
+{
+ vfree(file->private_data);
+
+ return 0;
+}
+
+static const struct file_operations fops_cal_data = {
+ .open = ath10k_debug_cal_data_open,
+ .read = ath10k_debug_cal_data_read,
+ .release = ath10k_debug_cal_data_release,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
int ath10k_debug_start(struct ath10k *ar)
{
int ret;
@@ -1058,7 +1628,22 @@
ret);
}
- return 0;
+ if (ar->debug.pktlog_filter) {
+ ret = ath10k_wmi_pdev_pktlog_enable(ar,
+ ar->debug.pktlog_filter);
+ if (ret)
+ /* not serious */
+ ath10k_warn(ar,
+ "failed to enable pktlog filter %x: %d\n",
+ ar->debug.pktlog_filter, ret);
+ } else {
+ ret = ath10k_wmi_pdev_pktlog_disable(ar);
+ if (ret)
+ /* not serious */
+ ath10k_warn(ar, "failed to disable pktlog: %d\n", ret);
+ }
+
+ return ret;
}
void ath10k_debug_stop(struct ath10k *ar)
@@ -1073,6 +1658,8 @@
ar->debug.htt_max_amsdu = 0;
ar->debug.htt_max_ampdu = 0;
+
+ ath10k_wmi_pdev_pktlog_disable(ar);
}
static ssize_t ath10k_write_simulate_radar(struct file *file,
@@ -1155,12 +1742,78 @@
.llseek = default_llseek,
};
+static ssize_t ath10k_write_pktlog_filter(struct file *file,
+ const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct ath10k *ar = file->private_data;
+ u32 filter;
+ int ret;
+
+ if (kstrtouint_from_user(ubuf, count, 0, &filter))
+ return -EINVAL;
+
+ mutex_lock(&ar->conf_mutex);
+
+ if (ar->state != ATH10K_STATE_ON) {
+ ar->debug.pktlog_filter = filter;
+ ret = count;
+ goto out;
+ }
+
+ if (filter && (filter != ar->debug.pktlog_filter)) {
+ ret = ath10k_wmi_pdev_pktlog_enable(ar, filter);
+ if (ret) {
+ ath10k_warn(ar, "failed to enable pktlog filter %x: %d\n",
+ ar->debug.pktlog_filter, ret);
+ goto out;
+ }
+ } else {
+ ret = ath10k_wmi_pdev_pktlog_disable(ar);
+ if (ret) {
+ ath10k_warn(ar, "failed to disable pktlog: %d\n", ret);
+ goto out;
+ }
+ }
+
+ ar->debug.pktlog_filter = filter;
+ ret = count;
+
+out:
+ mutex_unlock(&ar->conf_mutex);
+ return ret;
+}
+
+static ssize_t ath10k_read_pktlog_filter(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ char buf[32];
+ struct ath10k *ar = file->private_data;
+ int len = 0;
+
+ mutex_lock(&ar->conf_mutex);
+ len = scnprintf(buf, sizeof(buf) - len, "%08x\n",
+ ar->debug.pktlog_filter);
+ mutex_unlock(&ar->conf_mutex);
+
+ return simple_read_from_buffer(ubuf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_pktlog_filter = {
+ .read = ath10k_read_pktlog_filter,
+ .write = ath10k_write_pktlog_filter,
+ .open = simple_open
+};
+
int ath10k_debug_create(struct ath10k *ar)
{
ar->debug.fw_crash_data = vzalloc(sizeof(*ar->debug.fw_crash_data));
if (!ar->debug.fw_crash_data)
return -ENOMEM;
+ INIT_LIST_HEAD(&ar->debug.fw_stats.pdevs);
+ INIT_LIST_HEAD(&ar->debug.fw_stats.peers);
+
return 0;
}
@@ -1168,6 +1821,8 @@
{
vfree(ar->debug.fw_crash_data);
ar->debug.fw_crash_data = NULL;
+
+ ath10k_debug_fw_stats_reset(ar);
}
int ath10k_debug_register(struct ath10k *ar)
@@ -1184,11 +1839,14 @@
INIT_DELAYED_WORK(&ar->debug.htt_stats_dwork,
ath10k_debug_htt_stats_dwork);
- init_completion(&ar->debug.event_stats_compl);
+ init_completion(&ar->debug.fw_stats_complete);
debugfs_create_file("fw_stats", S_IRUSR, ar->debug.debugfs_phy, ar,
&fops_fw_stats);
+ debugfs_create_file("fw_reset_stats", S_IRUSR, ar->debug.debugfs_phy,
+ ar, &fops_fw_reset_stats);
+
debugfs_create_file("wmi_services", S_IRUSR, ar->debug.debugfs_phy, ar,
&fops_wmi_services);
@@ -1198,6 +1856,15 @@
debugfs_create_file("fw_crash_dump", S_IRUSR, ar->debug.debugfs_phy,
ar, &fops_fw_crash_dump);
+ debugfs_create_file("reg_addr", S_IRUSR | S_IWUSR,
+ ar->debug.debugfs_phy, ar, &fops_reg_addr);
+
+ debugfs_create_file("reg_value", S_IRUSR | S_IWUSR,
+ ar->debug.debugfs_phy, ar, &fops_reg_value);
+
+ debugfs_create_file("mem_value", S_IRUSR | S_IWUSR,
+ ar->debug.debugfs_phy, ar, &fops_mem_value);
+
debugfs_create_file("chip_id", S_IRUSR, ar->debug.debugfs_phy,
ar, &fops_chip_id);
@@ -1211,6 +1878,9 @@
debugfs_create_file("fw_dbglog", S_IRUSR, ar->debug.debugfs_phy,
ar, &fops_fw_dbglog);
+ debugfs_create_file("cal_data", S_IRUSR, ar->debug.debugfs_phy,
+ ar, &fops_cal_data);
+
if (config_enabled(CPTCFG_ATH10K_DFS_CERTIFIED)) {
debugfs_create_file("dfs_simulate_radar", S_IWUSR,
ar->debug.debugfs_phy, ar,
@@ -1225,6 +1895,9 @@
&fops_dfs_stats);
}
+ debugfs_create_file("pktlog_filter", S_IRUGO | S_IWUSR,
+ ar->debug.debugfs_phy, ar, &fops_pktlog_filter);
+
return 0;
}
@@ -1261,11 +1934,26 @@
const char *msg, const char *prefix,
const void *buf, size_t len)
{
+ char linebuf[256];
+ unsigned int linebuflen;
+ const void *ptr;
+
if (ath10k_debug_mask & mask) {
if (msg)
ath10k_dbg(ar, mask, "%s\n", msg);
- print_hex_dump_bytes(prefix, DUMP_PREFIX_OFFSET, buf, len);
+ for (ptr = buf; (ptr - buf) < len; ptr += 16) {
+ linebuflen = 0;
+ linebuflen += scnprintf(linebuf + linebuflen,
+ sizeof(linebuf) - linebuflen,
+ "%s%08x: ",
+ (prefix ? prefix : ""),
+ (unsigned int)(ptr - buf));
+ hex_dump_to_buffer(ptr, len - (ptr - buf), 16, 1,
+ linebuf + linebuflen,
+ sizeof(linebuf) - linebuflen, true);
+ dev_printk(KERN_DEBUG, ar->dev, "%s\n", linebuf);
+ }
}
/* tracing code doesn't like null strings :/ */
diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h
index 06923b4..d9408e6 100644
--- a/drivers/net/wireless/ath/ath10k/debug.h
+++ b/drivers/net/wireless/ath/ath10k/debug.h
@@ -35,17 +35,27 @@
ATH10K_DBG_BMI = 0x00000400,
ATH10K_DBG_REGULATORY = 0x00000800,
ATH10K_DBG_TESTMODE = 0x00001000,
+ ATH10K_DBG_WMI_PRINT = 0x00002000,
ATH10K_DBG_ANY = 0xffffffff,
// Use high bit so conflicts are less likely with upstream updates.
ATH10K_DBG_BEACON = 0x10000000,
};
+enum ath10k_pktlog_filter {
+ ATH10K_PKTLOG_RX = 0x000000001,
+ ATH10K_PKTLOG_TX = 0x000000002,
+ ATH10K_PKTLOG_RCFIND = 0x000000004,
+ ATH10K_PKTLOG_RCUPDATE = 0x000000008,
+ ATH10K_PKTLOG_DBG_PRINT = 0x000000010,
+ ATH10K_PKTLOG_ANY = 0x00000001f,
+};
+
extern unsigned int ath10k_debug_mask;
-__printf(2, 3) int ath10k_info(struct ath10k *ar, const char *fmt, ...);
-__printf(2, 3) int ath10k_err(struct ath10k *ar, const char *fmt, ...);
-__printf(2, 3) int ath10k_warn(struct ath10k *ar, const char *fmt, ...);
+__printf(2, 3) void ath10k_info(struct ath10k *ar, const char *fmt, ...);
+__printf(2, 3) void ath10k_err(struct ath10k *ar, const char *fmt, ...);
+__printf(2, 3) void ath10k_warn(struct ath10k *ar, const char *fmt, ...);
void ath10k_print_driver_info(struct ath10k *ar);
#ifdef CPTCFG_ATH10K_DEBUGFS
@@ -55,18 +65,22 @@
void ath10k_debug_destroy(struct ath10k *ar);
int ath10k_debug_register(struct ath10k *ar);
void ath10k_debug_unregister(struct ath10k *ar);
-void ath10k_debug_read_service_map(struct ath10k *ar,
- void *service_map,
- size_t map_size);
-void ath10k_debug_read_target_stats(struct ath10k *ar,
- struct wmi_stats_event *ev);
+void ath10k_debug_fw_stats_process(struct ath10k *ar, struct sk_buff *skb);
struct ath10k_fw_crash_data *
ath10k_debug_get_new_fw_crash_data(struct ath10k *ar);
void ath10k_debug_dbglog_add(struct ath10k *ar, u8 *buffer, int len);
-
#define ATH10K_DFS_STAT_INC(ar, c) (ar->debug.dfs_stats.c++)
+void ath10k_debug_get_et_strings(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ u32 sset, u8 *data);
+int ath10k_debug_get_et_sset_count(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, int sset);
+void ath10k_debug_get_et_stats(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ethtool_stats *stats, u64 *data);
+
#else
static inline int ath10k_debug_start(struct ath10k *ar)
{
@@ -95,14 +109,8 @@
{
}
-static inline void ath10k_debug_read_service_map(struct ath10k *ar,
- void *service_map,
- size_t map_size)
-{
-}
-
-static inline void ath10k_debug_read_target_stats(struct ath10k *ar,
- struct wmi_stats_event *ev)
+static inline void ath10k_debug_fw_stats_process(struct ath10k *ar,
+ struct sk_buff *skb)
{
}
@@ -119,6 +127,10 @@
#define ATH10K_DFS_STAT_INC(ar, c) do { } while (0)
+#define ath10k_debug_get_et_strings NULL
+#define ath10k_debug_get_et_sset_count NULL
+#define ath10k_debug_get_et_stats NULL
+
#endif /* CPTCFG_ATH10K_DEBUGFS */
#ifdef CPTCFG_ATH10K_DEBUG
diff --git a/drivers/net/wireless/ath/ath10k/hif.h b/drivers/net/wireless/ath/ath10k/hif.h
index 62323fe..0c92e02 100644
--- a/drivers/net/wireless/ath/ath10k/hif.h
+++ b/drivers/net/wireless/ath/ath10k/hif.h
@@ -20,6 +20,7 @@
#include <linux/kernel.h>
#include "core.h"
+#include "debug.h"
struct ath10k_hif_sg_item {
u16 transfer_id;
@@ -31,11 +32,9 @@
struct ath10k_hif_cb {
int (*tx_completion)(struct ath10k *ar,
- struct sk_buff *wbuf,
- unsigned transfer_id);
+ struct sk_buff *wbuf);
int (*rx_completion)(struct ath10k *ar,
- struct sk_buff *wbuf,
- u8 pipe_id);
+ struct sk_buff *wbuf);
};
struct ath10k_hif_ops {
@@ -43,6 +42,12 @@
int (*tx_sg)(struct ath10k *ar, u8 pipe_id,
struct ath10k_hif_sg_item *items, int n_items);
+ /* read firmware memory through the diagnose interface */
+ int (*diag_read)(struct ath10k *ar, u32 address, void *buf,
+ size_t buf_len);
+
+ int (*diag_write)(struct ath10k *ar, u32 address, const void *data,
+ int nbytes);
/*
* API to handle HIF-specific BMI message exchanges, this API is
* synchronous and only allowed to be called from a context that
@@ -80,6 +85,10 @@
u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id);
+ u32 (*read32)(struct ath10k *ar, u32 address);
+
+ void (*write32)(struct ath10k *ar, u32 address, u32 value);
+
/* Power up the device and enter BMI transfer mode for FW download */
int (*power_up)(struct ath10k *ar);
@@ -98,6 +107,21 @@
return ar->hif.ops->tx_sg(ar, pipe_id, items, n_items);
}
+static inline int ath10k_hif_diag_read(struct ath10k *ar, u32 address, void *buf,
+ size_t buf_len)
+{
+ return ar->hif.ops->diag_read(ar, address, buf, buf_len);
+}
+
+static inline int ath10k_hif_diag_write(struct ath10k *ar, u32 address,
+ const void *data, int nbytes)
+{
+ if (!ar->hif.ops->diag_write)
+ return -EOPNOTSUPP;
+
+ return ar->hif.ops->diag_write(ar, address, data, nbytes);
+}
+
static inline int ath10k_hif_exchange_bmi_msg(struct ath10k *ar,
void *request, u32 request_len,
void *response, u32 *response_len)
@@ -177,4 +201,25 @@
return ar->hif.ops->resume(ar);
}
+static inline u32 ath10k_hif_read32(struct ath10k *ar, u32 address)
+{
+ if (!ar->hif.ops->read32) {
+ ath10k_warn(ar, "hif read32 not supported\n");
+ return 0xdeaddead;
+ }
+
+ return ar->hif.ops->read32(ar, address);
+}
+
+static inline void ath10k_hif_write32(struct ath10k *ar,
+ u32 address, u32 data)
+{
+ if (!ar->hif.ops->write32) {
+ ath10k_warn(ar, "hif write32 not supported\n");
+ return;
+ }
+
+ ar->hif.ops->write32(ar, address, data);
+}
+
#endif /* _HIF_H_ */
diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c
index 769d594..580917b 100644
--- a/drivers/net/wireless/ath/ath10k/htc.c
+++ b/drivers/net/wireless/ath/ath10k/htc.c
@@ -165,6 +165,7 @@
ath10k_htc_prepare_tx_skb(ep, skb);
+ skb_cb->eid = eid;
skb_cb->paddr = dma_map_single(dev, skb->data, skb->len, DMA_TO_DEVICE);
ret = dma_mapping_error(dev, skb_cb->paddr);
if (ret)
@@ -202,15 +203,18 @@
}
static int ath10k_htc_tx_completion_handler(struct ath10k *ar,
- struct sk_buff *skb,
- unsigned int eid)
+ struct sk_buff *skb)
{
struct ath10k_htc *htc = &ar->htc;
- struct ath10k_htc_ep *ep = &htc->endpoint[eid];
+ struct ath10k_skb_cb *skb_cb;
+ struct ath10k_htc_ep *ep;
if (WARN_ON_ONCE(!skb))
return 0;
+ skb_cb = ATH10K_SKB_CB(skb);
+ ep = &htc->endpoint[skb_cb->eid];
+
ath10k_htc_notify_tx_completion(ep, skb);
/* the skb now belongs to the completion handler */
@@ -322,8 +326,7 @@
}
static int ath10k_htc_rx_completion_handler(struct ath10k *ar,
- struct sk_buff *skb,
- u8 pipe_id)
+ struct sk_buff *skb)
{
int status = 0;
struct ath10k_htc *htc = &ar->htc;
diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h
index 063fdeb..1bd0dbf 100644
--- a/drivers/net/wireless/ath/ath10k/htt.h
+++ b/drivers/net/wireless/ath/ath10k/htt.h
@@ -126,6 +126,7 @@
* (HL hosts manage queues on the host )
* more_in_batch: only for HL hosts. indicates if more packets are
* pending. this allows target to wait and aggregate
+ * freq: 0 means home channel of given vdev. intended for offchannel
*/
struct htt_data_tx_desc {
u8 flags0; /* %HTT_DATA_TX_DESC_FLAGS0_ */
@@ -133,7 +134,8 @@
__le16 len;
__le16 id;
__le32 frags_paddr;
- __le32 peerid;
+ __le16 peerid;
+ __le16 freq;
u8 prefetch[0]; /* start of frame, for FW classification engine */
} __packed;
@@ -156,6 +158,9 @@
HTT_RX_RING_FLAGS_PHY_DATA_RX = 1 << 15
};
+#define HTT_RX_RING_SIZE_MIN 128
+#define HTT_RX_RING_SIZE_MAX 2048
+
struct htt_rx_ring_setup_ring {
__le32 fw_idx_shadow_reg_paddr;
__le32 rx_ring_base_paddr;
@@ -725,7 +730,7 @@
*/
struct htt_pktlog_msg {
u8 pad[3];
- __le32 payload[1 /* or more */];
+ u8 payload[0];
} __packed;
struct htt_dbg_stats_rx_reorder_stats {
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index 18fa269..b999d3f 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -25,19 +25,8 @@
#include <linux/log2.h>
-/* slightly larger than one large A-MPDU */
-#define HTT_RX_RING_SIZE_MIN 128
-
-/* roughly 20 ms @ 1 Gbps of 1500B MSDUs */
-#define HTT_RX_RING_SIZE_MAX 2048
-
-#define HTT_RX_AVG_FRM_BYTES 1000
-
-/* ms, very conservative */
-#define HTT_RX_HOST_LATENCY_MAX_MS 20
-
-/* ms, conservative */
-#define HTT_RX_HOST_LATENCY_WORST_LIKELY_MS 10
+#define HTT_RX_RING_SIZE 1024
+#define HTT_RX_RING_FILL_LEVEL 1000
/* when under memory pressure rx ring refill may fail and needs a retry */
#define HTT_RX_RING_REFILL_RETRY_MS 50
@@ -45,68 +34,6 @@
static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb);
static void ath10k_htt_txrx_compl_task(unsigned long ptr);
-static int ath10k_htt_rx_ring_size(struct ath10k_htt *htt)
-{
- int size;
-
- /*
- * It is expected that the host CPU will typically be able to
- * service the rx indication from one A-MPDU before the rx
- * indication from the subsequent A-MPDU happens, roughly 1-2 ms
- * later. However, the rx ring should be sized very conservatively,
- * to accomodate the worst reasonable delay before the host CPU
- * services a rx indication interrupt.
- *
- * The rx ring need not be kept full of empty buffers. In theory,
- * the htt host SW can dynamically track the low-water mark in the
- * rx ring, and dynamically adjust the level to which the rx ring
- * is filled with empty buffers, to dynamically meet the desired
- * low-water mark.
- *
- * In contrast, it's difficult to resize the rx ring itself, once
- * it's in use. Thus, the ring itself should be sized very
- * conservatively, while the degree to which the ring is filled
- * with empty buffers should be sized moderately conservatively.
- */
-
- /* 1e6 bps/mbps / 1e3 ms per sec = 1000 */
- size =
- htt->max_throughput_mbps *
- 1000 /
- (8 * HTT_RX_AVG_FRM_BYTES) * HTT_RX_HOST_LATENCY_MAX_MS;
-
- if (size < HTT_RX_RING_SIZE_MIN)
- size = HTT_RX_RING_SIZE_MIN;
-
- if (size > HTT_RX_RING_SIZE_MAX)
- size = HTT_RX_RING_SIZE_MAX;
-
- size = roundup_pow_of_two(size);
-
- return size;
-}
-
-static int ath10k_htt_rx_ring_fill_level(struct ath10k_htt *htt)
-{
- int size;
-
- /* 1e6 bps/mbps / 1e3 ms per sec = 1000 */
- size =
- htt->max_throughput_mbps *
- 1000 /
- (8 * HTT_RX_AVG_FRM_BYTES) * HTT_RX_HOST_LATENCY_WORST_LIKELY_MS;
-
- /*
- * Make sure the fill level is at least 1 less than the ring size.
- * Leaving 1 element empty allows the SW to easily distinguish
- * between a full ring vs. an empty ring.
- */
- if (size >= htt->rx_ring.size)
- size = htt->rx_ring.size - 1;
-
- return size;
-}
-
static void ath10k_htt_rx_ring_free(struct ath10k_htt *htt)
{
struct sk_buff *skb;
@@ -296,50 +223,38 @@
htt->rx_ring.sw_rd_idx.msdu_payld = idx;
htt->rx_ring.fill_cnt--;
+ dma_unmap_single(htt->ar->dev,
+ ATH10K_SKB_CB(msdu)->paddr,
+ msdu->len + skb_tailroom(msdu),
+ DMA_FROM_DEVICE);
+ ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx netbuf pop: ",
+ msdu->data, msdu->len + skb_tailroom(msdu));
+
return msdu;
}
-static void ath10k_htt_rx_free_msdu_chain(struct sk_buff *skb)
-{
- struct sk_buff *next;
-
- while (skb) {
- next = skb->next;
- dev_kfree_skb_any(skb);
- skb = next;
- }
-}
-
/* return: < 0 fatal error, 0 - non chained msdu, 1 chained msdu */
static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
u8 **fw_desc, int *fw_desc_len,
- struct sk_buff **head_msdu,
- struct sk_buff **tail_msdu,
- u32 *attention)
+ struct sk_buff_head *amsdu)
{
struct ath10k *ar = htt->ar;
int msdu_len, msdu_chaining = 0;
- struct sk_buff *msdu, *next;
+ struct sk_buff *msdu;
struct htt_rx_desc *rx_desc;
lockdep_assert_held(&htt->rx_ring.lock);
- if (htt->rx_confused) {
- ath10k_warn(ar, "htt is confused. refusing rx\n");
- return -1;
- }
-
- msdu = *head_msdu = ath10k_htt_rx_netbuf_pop(htt);
- while (msdu) {
+ for (;;) {
int last_msdu, msdu_len_invalid, msdu_chained;
- dma_unmap_single(htt->ar->dev,
- ATH10K_SKB_CB(msdu)->paddr,
- msdu->len + skb_tailroom(msdu),
- DMA_FROM_DEVICE);
+ msdu = ath10k_htt_rx_netbuf_pop(htt);
+ if (!msdu) {
+ __skb_queue_purge(amsdu);
+ return -ENOENT;
+ }
- ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx pop: ",
- msdu->data, msdu->len + skb_tailroom(msdu));
+ __skb_queue_tail(amsdu, msdu);
rx_desc = (struct htt_rx_desc *)msdu->data;
@@ -358,19 +273,10 @@
*/
if (!(__le32_to_cpu(rx_desc->attention.flags)
& RX_ATTENTION_FLAGS_MSDU_DONE)) {
- ath10k_htt_rx_free_msdu_chain(*head_msdu);
- *head_msdu = NULL;
- msdu = NULL;
- ath10k_err(ar, "htt rx stopped. cannot recover\n");
- htt->rx_confused = true;
- break;
+ __skb_queue_purge(amsdu);
+ return -EIO;
}
- *attention |= __le32_to_cpu(rx_desc->attention.flags) &
- (RX_ATTENTION_FLAGS_TKIP_MIC_ERR |
- RX_ATTENTION_FLAGS_DECRYPT_ERR |
- RX_ATTENTION_FLAGS_FCS_ERR |
- RX_ATTENTION_FLAGS_MGMT_TYPE);
/*
* Copy the FW rx descriptor for this MSDU from the rx
* indication message into the MSDU's netbuf. HL uses the
@@ -427,43 +333,32 @@
skb_put(msdu, min(msdu_len, HTT_RX_MSDU_SIZE));
msdu_len -= msdu->len;
- /* FIXME: Do chained buffers include htt_rx_desc or not? */
+ /* Note: Chained buffers do not contain rx descriptor */
while (msdu_chained--) {
- struct sk_buff *next = ath10k_htt_rx_netbuf_pop(htt);
+ msdu = ath10k_htt_rx_netbuf_pop(htt);
+ if (!msdu) {
+ __skb_queue_purge(amsdu);
+ return -ENOENT;
+ }
- dma_unmap_single(htt->ar->dev,
- ATH10K_SKB_CB(next)->paddr,
- next->len + skb_tailroom(next),
- DMA_FROM_DEVICE);
-
- ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL,
- "htt rx chained: ", next->data,
- next->len + skb_tailroom(next));
-
- skb_trim(next, 0);
- skb_put(next, min(msdu_len, HTT_RX_BUF_SIZE));
- msdu_len -= next->len;
-
- msdu->next = next;
- msdu = next;
+ __skb_queue_tail(amsdu, msdu);
+ skb_trim(msdu, 0);
+ skb_put(msdu, min(msdu_len, HTT_RX_BUF_SIZE));
+ msdu_len -= msdu->len;
msdu_chaining = 1;
}
last_msdu = __le32_to_cpu(rx_desc->msdu_end.info0) &
RX_MSDU_END_INFO0_LAST_MSDU;
- if (last_msdu) {
- msdu->next = NULL;
+ trace_ath10k_htt_rx_desc(ar, &rx_desc->attention,
+ sizeof(*rx_desc) - sizeof(u32));
+
+ if (last_msdu)
break;
- }
-
- next = ath10k_htt_rx_netbuf_pop(htt);
- msdu->next = next;
- msdu = next;
}
- *tail_msdu = msdu;
- if (*head_msdu == NULL)
+ if (skb_queue_empty(amsdu))
msdu_chaining = -1;
/*
@@ -497,25 +392,20 @@
size_t size;
struct timer_list *timer = &htt->rx_ring.refill_retry_timer;
- htt->rx_ring.size = ath10k_htt_rx_ring_size(htt);
+ htt->rx_confused = false;
+
+ /* XXX: The fill level could be changed during runtime in response to
+ * the host processing latency. Is this really worth it?
+ */
+ htt->rx_ring.size = HTT_RX_RING_SIZE;
+ htt->rx_ring.size_mask = htt->rx_ring.size - 1;
+ htt->rx_ring.fill_level = HTT_RX_RING_FILL_LEVEL;
+
if (!is_power_of_2(htt->rx_ring.size)) {
ath10k_warn(ar, "htt rx ring size is not power of 2\n");
return -EINVAL;
}
- htt->rx_ring.size_mask = htt->rx_ring.size - 1;
-
- /*
- * Set the initial value for the level to which the rx ring
- * should be filled, based on the max throughput and the
- * worst likely latency for the host to fill the rx ring
- * with new buffers. In theory, this fill level can be
- * dynamically adjusted from the initial value set here, to
- * reflect the actual host latency rather than a
- * conservative assumption about the host latency.
- */
- htt->rx_ring.fill_level = ath10k_htt_rx_ring_fill_level(htt);
-
htt->rx_ring.netbufs_ring =
kzalloc(htt->rx_ring.size * sizeof(struct sk_buff *),
GFP_KERNEL|__GFP_REPEAT|__GFP_HIGH);
@@ -587,73 +477,50 @@
enum htt_rx_mpdu_encrypt_type type)
{
switch (type) {
- case HTT_RX_MPDU_ENCRYPT_WEP40:
- case HTT_RX_MPDU_ENCRYPT_WEP104:
- return 4;
- case HTT_RX_MPDU_ENCRYPT_TKIP_WITHOUT_MIC:
- case HTT_RX_MPDU_ENCRYPT_WEP128: /* not tested */
- case HTT_RX_MPDU_ENCRYPT_TKIP_WPA:
- case HTT_RX_MPDU_ENCRYPT_WAPI: /* not tested */
- case HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2:
- return 8;
case HTT_RX_MPDU_ENCRYPT_NONE:
return 0;
+ case HTT_RX_MPDU_ENCRYPT_WEP40:
+ case HTT_RX_MPDU_ENCRYPT_WEP104:
+ return IEEE80211_WEP_IV_LEN;
+ case HTT_RX_MPDU_ENCRYPT_TKIP_WITHOUT_MIC:
+ case HTT_RX_MPDU_ENCRYPT_TKIP_WPA:
+ return IEEE80211_TKIP_IV_LEN;
+ case HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2:
+ return IEEE80211_CCMP_HDR_LEN;
+ case HTT_RX_MPDU_ENCRYPT_WEP128:
+ case HTT_RX_MPDU_ENCRYPT_WAPI:
+ break;
}
- ath10k_warn(ar, "unknown encryption type %d\n", type);
+ ath10k_warn(ar, "unsupported encryption type %d\n", type);
return 0;
}
+#define MICHAEL_MIC_LEN 8
+
static int ath10k_htt_rx_crypto_tail_len(struct ath10k *ar,
enum htt_rx_mpdu_encrypt_type type)
{
switch (type) {
case HTT_RX_MPDU_ENCRYPT_NONE:
+ return 0;
case HTT_RX_MPDU_ENCRYPT_WEP40:
case HTT_RX_MPDU_ENCRYPT_WEP104:
- case HTT_RX_MPDU_ENCRYPT_WEP128:
- case HTT_RX_MPDU_ENCRYPT_WAPI:
- return 0;
+ return IEEE80211_WEP_ICV_LEN;
case HTT_RX_MPDU_ENCRYPT_TKIP_WITHOUT_MIC:
case HTT_RX_MPDU_ENCRYPT_TKIP_WPA:
- return 4;
+ return IEEE80211_TKIP_ICV_LEN;
case HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2:
- return 8;
+ return IEEE80211_CCMP_MIC_LEN;
+ case HTT_RX_MPDU_ENCRYPT_WEP128:
+ case HTT_RX_MPDU_ENCRYPT_WAPI:
+ break;
}
- ath10k_warn(ar, "unknown encryption type %d\n", type);
+ ath10k_warn(ar, "unsupported encryption type %d\n", type);
return 0;
}
-/* Applies for first msdu in chain, before altering it. */
-static struct ieee80211_hdr *ath10k_htt_rx_skb_get_hdr(struct sk_buff *skb)
-{
- struct htt_rx_desc *rxd;
- enum rx_msdu_decap_format fmt;
-
- rxd = (void *)skb->data - sizeof(*rxd);
- fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
- RX_MSDU_START_INFO1_DECAP_FORMAT);
-
- if (fmt == RX_MSDU_DECAP_RAW)
- return (void *)skb->data;
-
- return (void *)skb->data - RX_HTT_HDR_STATUS_LEN;
-}
-
-/* This function only applies for first msdu in an msdu chain */
-static bool ath10k_htt_rx_hdr_is_amsdu(struct ieee80211_hdr *hdr)
-{
- u8 *qc;
-
- if (ieee80211_is_data_qos(hdr->frame_control)) {
- qc = ieee80211_get_qos_ctl(hdr);
- if (qc[0] & 0x80)
- return true;
- }
- return false;
-}
-
struct rfc1042_hdr {
u8 llc_dsap;
u8 llc_ssap;
@@ -688,23 +555,34 @@
};
static void ath10k_htt_rx_h_rates(struct ath10k *ar,
- enum ieee80211_band band,
- u8 info0, u32 info1, u32 info2,
- struct ieee80211_rx_status *status)
+ struct ieee80211_rx_status *status,
+ struct htt_rx_desc *rxd)
{
+ enum ieee80211_band band;
u8 cck, rate, rate_idx, bw, sgi, mcs, nss;
u8 preamble = 0;
+ u32 info1, info2, info3;
- /* Check if valid fields */
- if (!(info0 & HTT_RX_INDICATION_INFO0_START_VALID))
+ /* Band value can't be set as undefined but freq can be 0 - use that to
+ * determine whether band is provided.
+ *
+ * FIXME: Perhaps this can go away if CCK rate reporting is a little
+ * reworked?
+ */
+ if (!status->freq)
return;
- preamble = MS(info1, HTT_RX_INDICATION_INFO1_PREAMBLE_TYPE);
+ band = status->band;
+ info1 = __le32_to_cpu(rxd->ppdu_start.info1);
+ info2 = __le32_to_cpu(rxd->ppdu_start.info2);
+ info3 = __le32_to_cpu(rxd->ppdu_start.info3);
+
+ preamble = MS(info1, RX_PPDU_START_INFO1_PREAMBLE_TYPE);
switch (preamble) {
case HTT_RX_LEGACY:
- cck = info0 & HTT_RX_INDICATION_INFO0_LEGACY_RATE_CCK;
- rate = MS(info0, HTT_RX_INDICATION_INFO0_LEGACY_RATE);
+ cck = info1 & RX_PPDU_START_INFO1_L_SIG_RATE_SELECT;
+ rate = MS(info1, RX_PPDU_START_INFO1_L_SIG_RATE);
rate_idx = 0;
if (rate < 0x08 || rate > 0x0F)
@@ -731,11 +609,11 @@
break;
case HTT_RX_HT:
case HTT_RX_HT_WITH_TXBF:
- /* HT-SIG - Table 20-11 in info1 and info2 */
- mcs = info1 & 0x1F;
+ /* HT-SIG - Table 20-11 in info2 and info3 */
+ mcs = info2 & 0x1F;
nss = mcs >> 3;
- bw = (info1 >> 7) & 1;
- sgi = (info2 >> 7) & 1;
+ bw = (info2 >> 7) & 1;
+ sgi = (info3 >> 7) & 1;
status->rate_idx = mcs;
status->flag |= RX_FLAG_HT;
@@ -746,12 +624,12 @@
break;
case HTT_RX_VHT:
case HTT_RX_VHT_WITH_TXBF:
- /* VHT-SIG-A1 in info 1, VHT-SIG-A2 in info2
+ /* VHT-SIG-A1 in info2, VHT-SIG-A2 in info3
TODO check this */
- mcs = (info2 >> 4) & 0x0F;
- nss = ((info1 >> 10) & 0x07) + 1;
- bw = info1 & 3;
- sgi = info2 & 1;
+ mcs = (info3 >> 4) & 0x0F;
+ nss = ((info2 >> 10) & 0x07) + 1;
+ bw = info2 & 3;
+ sgi = info3 & 1;
status->rate_idx = mcs;
status->vht_nss = nss;
@@ -779,41 +657,6 @@
}
}
-static void ath10k_htt_rx_h_protected(struct ath10k_htt *htt,
- struct ieee80211_rx_status *rx_status,
- struct sk_buff *skb,
- enum htt_rx_mpdu_encrypt_type enctype,
- enum rx_msdu_decap_format fmt,
- bool dot11frag)
-{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-
- rx_status->flag &= ~(RX_FLAG_DECRYPTED |
- RX_FLAG_IV_STRIPPED |
- RX_FLAG_MMIC_STRIPPED);
-
- if (enctype == HTT_RX_MPDU_ENCRYPT_NONE)
- return;
-
- /*
- * There's no explicit rx descriptor flag to indicate whether a given
- * frame has been decrypted or not. We're forced to use the decap
- * format as an implicit indication. However fragmentation rx is always
- * raw and it probably never reports undecrypted raws.
- *
- * This makes sure sniffed frames are reported as-is without stripping
- * the protected flag.
- */
- if (fmt == RX_MSDU_DECAP_RAW && !dot11frag)
- return;
-
- rx_status->flag |= RX_FLAG_DECRYPTED |
- RX_FLAG_IV_STRIPPED |
- RX_FLAG_MMIC_STRIPPED;
- hdr->frame_control = __cpu_to_le16(__le16_to_cpu(hdr->frame_control) &
- ~IEEE80211_FCTL_PROTECTED);
-}
-
static bool ath10k_htt_rx_h_channel(struct ath10k *ar,
struct ieee80211_rx_status *status)
{
@@ -834,6 +677,72 @@
return true;
}
+static void ath10k_htt_rx_h_signal(struct ath10k *ar,
+ struct ieee80211_rx_status *status,
+ struct htt_rx_desc *rxd)
+{
+ /* FIXME: Get real NF */
+ status->signal = ATH10K_DEFAULT_NOISE_FLOOR +
+ rxd->ppdu_start.rssi_comb;
+ status->flag &= ~RX_FLAG_NO_SIGNAL_VAL;
+}
+
+static void ath10k_htt_rx_h_mactime(struct ath10k *ar,
+ struct ieee80211_rx_status *status,
+ struct htt_rx_desc *rxd)
+{
+ /* FIXME: TSF is known only at the end of PPDU, in the last MPDU. This
+ * means all prior MSDUs in a PPDU are reported to mac80211 without the
+ * TSF. Is it worth holding frames until end of PPDU is known?
+ *
+ * FIXME: Can we get/compute 64bit TSF?
+ */
+ status->mactime = __le32_to_cpu(rxd->ppdu_end.tsf_timestamp);
+ status->flag |= RX_FLAG_MACTIME_END;
+}
+
+static void ath10k_htt_rx_h_ppdu(struct ath10k *ar,
+ struct sk_buff_head *amsdu,
+ struct ieee80211_rx_status *status)
+{
+ struct sk_buff *first;
+ struct htt_rx_desc *rxd;
+ bool is_first_ppdu;
+ bool is_last_ppdu;
+
+ if (skb_queue_empty(amsdu))
+ return;
+
+ first = skb_peek(amsdu);
+ rxd = (void *)first->data - sizeof(*rxd);
+
+ is_first_ppdu = !!(rxd->attention.flags &
+ __cpu_to_le32(RX_ATTENTION_FLAGS_FIRST_MPDU));
+ is_last_ppdu = !!(rxd->attention.flags &
+ __cpu_to_le32(RX_ATTENTION_FLAGS_LAST_MPDU));
+
+ if (is_first_ppdu) {
+ /* New PPDU starts so clear out the old per-PPDU status. */
+ status->freq = 0;
+ status->rate_idx = 0;
+ status->vht_nss = 0;
+ status->vht_flag &= ~RX_VHT_FLAG_80MHZ;
+ status->flag &= ~(RX_FLAG_HT |
+ RX_FLAG_VHT |
+ RX_FLAG_SHORT_GI |
+ RX_FLAG_40MHZ |
+ RX_FLAG_MACTIME_END);
+ status->flag |= RX_FLAG_NO_SIGNAL_VAL;
+
+ ath10k_htt_rx_h_signal(ar, status, rxd);
+ ath10k_htt_rx_h_channel(ar, status);
+ ath10k_htt_rx_h_rates(ar, status, rxd);
+ }
+
+ if (is_last_ppdu)
+ ath10k_htt_rx_h_mactime(ar, status, rxd);
+}
+
static const char * const tid_to_ac[] = {
"BE",
"BK",
@@ -898,6 +807,8 @@
!!(status->flag & RX_FLAG_AMSDU_MORE));
ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "rx skb: ",
skb->data, skb->len);
+ trace_ath10k_rx_hdr(ar, skb->data, skb->len);
+ trace_ath10k_rx_payload(ar, skb->data, skb->len);
ieee80211_rx(ar->hw, skb);
}
@@ -908,187 +819,263 @@
return round_up(ieee80211_hdrlen(hdr->frame_control), 4);
}
-static void ath10k_htt_rx_amsdu(struct ath10k_htt *htt,
- struct ieee80211_rx_status *rx_status,
- struct sk_buff *skb_in)
+static void ath10k_htt_rx_h_undecap_raw(struct ath10k *ar,
+ struct sk_buff *msdu,
+ struct ieee80211_rx_status *status,
+ enum htt_rx_mpdu_encrypt_type enctype,
+ bool is_decrypted)
{
- struct ath10k *ar = htt->ar;
- struct htt_rx_desc *rxd;
- struct sk_buff *skb = skb_in;
- struct sk_buff *first;
- enum rx_msdu_decap_format fmt;
- enum htt_rx_mpdu_encrypt_type enctype;
struct ieee80211_hdr *hdr;
- u8 hdr_buf[64], da[ETH_ALEN], sa[ETH_ALEN], *qos;
- unsigned int hdr_len;
+ struct htt_rx_desc *rxd;
+ size_t hdr_len;
+ size_t crypto_len;
+ bool is_first;
+ bool is_last;
- rxd = (void *)skb->data - sizeof(*rxd);
- enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
- RX_MPDU_START_INFO0_ENCRYPT_TYPE);
+ rxd = (void *)msdu->data - sizeof(*rxd);
+ is_first = !!(rxd->msdu_end.info0 &
+ __cpu_to_le32(RX_MSDU_END_INFO0_FIRST_MSDU));
+ is_last = !!(rxd->msdu_end.info0 &
+ __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU));
- hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status;
+ /* Delivered decapped frame:
+ * [802.11 header]
+ * [crypto param] <-- can be trimmed if !fcs_err &&
+ * !decrypt_err && !peer_idx_invalid
+ * [amsdu header] <-- only if A-MSDU
+ * [rfc1042/llc]
+ * [payload]
+ * [FCS] <-- at end, needs to be trimmed
+ */
+
+ /* This probably shouldn't happen but warn just in case */
+ if (unlikely(WARN_ON_ONCE(!is_first)))
+ return;
+
+ /* This probably shouldn't happen but warn just in case */
+ if (unlikely(WARN_ON_ONCE(!(is_first && is_last))))
+ return;
+
+ skb_trim(msdu, msdu->len - FCS_LEN);
+
+ /* In most cases this will be true for sniffed frames. It makes sense
+ * to deliver them as-is without stripping the crypto param. This would
+ * also make sense for software based decryption (which is not
+ * implemented in ath10k).
+ *
+ * If there's no error then the frame is decrypted. At least that is
+ * the case for frames that come in via fragmented rx indication.
+ */
+ if (!is_decrypted)
+ return;
+
+ /* The payload is decrypted so strip crypto params. Start from tail
+ * since hdr is used to compute some stuff.
+ */
+
+ hdr = (void *)msdu->data;
+
+ /* Tail */
+ skb_trim(msdu, msdu->len - ath10k_htt_rx_crypto_tail_len(ar, enctype));
+
+ /* MMIC */
+ if (!ieee80211_has_morefrags(hdr->frame_control) &&
+ enctype == HTT_RX_MPDU_ENCRYPT_TKIP_WPA)
+ skb_trim(msdu, msdu->len - 8);
+
+ /* Head */
hdr_len = ieee80211_hdrlen(hdr->frame_control);
- memcpy(hdr_buf, hdr, hdr_len);
- hdr = (struct ieee80211_hdr *)hdr_buf;
+ crypto_len = ath10k_htt_rx_crypto_param_len(ar, enctype);
- first = skb;
- while (skb) {
- void *decap_hdr;
- int len;
-
- rxd = (void *)skb->data - sizeof(*rxd);
- fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
- RX_MSDU_START_INFO1_DECAP_FORMAT);
- decap_hdr = (void *)rxd->rx_hdr_status;
-
- skb->ip_summed = ath10k_htt_rx_get_csum_state(skb);
-
- /* First frame in an A-MSDU chain has more decapped data. */
- if (skb == first) {
- len = round_up(ieee80211_hdrlen(hdr->frame_control), 4);
- len += round_up(ath10k_htt_rx_crypto_param_len(ar,
- enctype), 4);
- decap_hdr += len;
- }
-
- switch (fmt) {
- case RX_MSDU_DECAP_RAW:
- /* remove trailing FCS */
- skb_trim(skb, skb->len - FCS_LEN);
- break;
- case RX_MSDU_DECAP_NATIVE_WIFI:
- /* pull decapped header and copy SA & DA */
- hdr = (struct ieee80211_hdr *)skb->data;
- hdr_len = ath10k_htt_rx_nwifi_hdrlen(hdr);
- ether_addr_copy(da, ieee80211_get_DA(hdr));
- ether_addr_copy(sa, ieee80211_get_SA(hdr));
- skb_pull(skb, hdr_len);
-
- /* push original 802.11 header */
- hdr = (struct ieee80211_hdr *)hdr_buf;
- hdr_len = ieee80211_hdrlen(hdr->frame_control);
- memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
-
- /* original A-MSDU header has the bit set but we're
- * not including A-MSDU subframe header */
- hdr = (struct ieee80211_hdr *)skb->data;
- qos = ieee80211_get_qos_ctl(hdr);
- qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
-
- /* original 802.11 header has a different DA and in
- * case of 4addr it may also have different SA
- */
- ether_addr_copy(ieee80211_get_DA(hdr), da);
- ether_addr_copy(ieee80211_get_SA(hdr), sa);
- break;
- case RX_MSDU_DECAP_ETHERNET2_DIX:
- /* strip ethernet header and insert decapped 802.11
- * header, amsdu subframe header and rfc1042 header */
-
- len = 0;
- len += sizeof(struct rfc1042_hdr);
- len += sizeof(struct amsdu_subframe_hdr);
-
- skb_pull(skb, sizeof(struct ethhdr));
- memcpy(skb_push(skb, len), decap_hdr, len);
- memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
- break;
- case RX_MSDU_DECAP_8023_SNAP_LLC:
- /* insert decapped 802.11 header making a singly
- * A-MSDU */
- memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
- break;
- }
-
- skb_in = skb;
- ath10k_htt_rx_h_protected(htt, rx_status, skb_in, enctype, fmt,
- false);
- skb = skb->next;
- skb_in->next = NULL;
-
- if (skb)
- rx_status->flag |= RX_FLAG_AMSDU_MORE;
- else
- rx_status->flag &= ~RX_FLAG_AMSDU_MORE;
-
- ath10k_process_rx(htt->ar, rx_status, skb_in);
- }
-
- /* FIXME: It might be nice to re-assemble the A-MSDU when there's a
- * monitor interface active for sniffing purposes. */
+ memmove((void *)msdu->data + crypto_len,
+ (void *)msdu->data, hdr_len);
+ skb_pull(msdu, crypto_len);
}
-static void ath10k_htt_rx_msdu(struct ath10k_htt *htt,
- struct ieee80211_rx_status *rx_status,
- struct sk_buff *skb)
+static void ath10k_htt_rx_h_undecap_nwifi(struct ath10k *ar,
+ struct sk_buff *msdu,
+ struct ieee80211_rx_status *status,
+ const u8 first_hdr[64])
{
- struct ath10k *ar = htt->ar;
- struct htt_rx_desc *rxd;
struct ieee80211_hdr *hdr;
- enum rx_msdu_decap_format fmt;
- enum htt_rx_mpdu_encrypt_type enctype;
- int hdr_len;
- void *rfc1042;
+ size_t hdr_len;
+ u8 da[ETH_ALEN];
+ u8 sa[ETH_ALEN];
- /* This shouldn't happen. If it does than it may be a FW bug. */
- if (skb->next) {
- ath10k_warn(ar, "htt rx received chained non A-MSDU frame\n");
- ath10k_htt_rx_free_msdu_chain(skb->next);
- skb->next = NULL;
+ /* Delivered decapped frame:
+ * [nwifi 802.11 header] <-- replaced with 802.11 hdr
+ * [rfc1042/llc]
+ *
+ * Note: The nwifi header doesn't have QoS Control and is
+ * (always?) a 3addr frame.
+ *
+ * Note2: There's no A-MSDU subframe header. Even if it's part
+ * of an A-MSDU.
+ */
+
+ /* pull decapped header and copy SA & DA */
+ hdr = (struct ieee80211_hdr *)msdu->data;
+ hdr_len = ath10k_htt_rx_nwifi_hdrlen(hdr);
+ ether_addr_copy(da, ieee80211_get_DA(hdr));
+ ether_addr_copy(sa, ieee80211_get_SA(hdr));
+ skb_pull(msdu, hdr_len);
+
+ /* push original 802.11 header */
+ hdr = (struct ieee80211_hdr *)first_hdr;
+ hdr_len = ieee80211_hdrlen(hdr->frame_control);
+ memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
+
+ /* original 802.11 header has a different DA and in
+ * case of 4addr it may also have different SA
+ */
+ hdr = (struct ieee80211_hdr *)msdu->data;
+ ether_addr_copy(ieee80211_get_DA(hdr), da);
+ ether_addr_copy(ieee80211_get_SA(hdr), sa);
+}
+
+static void *ath10k_htt_rx_h_find_rfc1042(struct ath10k *ar,
+ struct sk_buff *msdu,
+ enum htt_rx_mpdu_encrypt_type enctype)
+{
+ struct ieee80211_hdr *hdr;
+ struct htt_rx_desc *rxd;
+ size_t hdr_len, crypto_len;
+ void *rfc1042;
+ bool is_first, is_last, is_amsdu;
+
+ rxd = (void *)msdu->data - sizeof(*rxd);
+ hdr = (void *)rxd->rx_hdr_status;
+
+ is_first = !!(rxd->msdu_end.info0 &
+ __cpu_to_le32(RX_MSDU_END_INFO0_FIRST_MSDU));
+ is_last = !!(rxd->msdu_end.info0 &
+ __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU));
+ is_amsdu = !(is_first && is_last);
+
+ rfc1042 = hdr;
+
+ if (is_first) {
+ hdr_len = ieee80211_hdrlen(hdr->frame_control);
+ crypto_len = ath10k_htt_rx_crypto_param_len(ar, enctype);
+
+ rfc1042 += round_up(hdr_len, 4) +
+ round_up(crypto_len, 4);
}
- rxd = (void *)skb->data - sizeof(*rxd);
- fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
- RX_MSDU_START_INFO1_DECAP_FORMAT);
- enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
- RX_MPDU_START_INFO0_ENCRYPT_TYPE);
- hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status;
+ if (is_amsdu)
+ rfc1042 += sizeof(struct amsdu_subframe_hdr);
+
+ return rfc1042;
+}
+
+static void ath10k_htt_rx_h_undecap_eth(struct ath10k *ar,
+ struct sk_buff *msdu,
+ struct ieee80211_rx_status *status,
+ const u8 first_hdr[64],
+ enum htt_rx_mpdu_encrypt_type enctype)
+{
+ struct ieee80211_hdr *hdr;
+ struct ethhdr *eth;
+ size_t hdr_len;
+ void *rfc1042;
+ u8 da[ETH_ALEN];
+ u8 sa[ETH_ALEN];
+
+ /* Delivered decapped frame:
+ * [eth header] <-- replaced with 802.11 hdr & rfc1042/llc
+ * [payload]
+ */
+
+ rfc1042 = ath10k_htt_rx_h_find_rfc1042(ar, msdu, enctype);
+ if (WARN_ON_ONCE(!rfc1042))
+ return;
+
+ /* pull decapped header and copy SA & DA */
+ eth = (struct ethhdr *)msdu->data;
+ ether_addr_copy(da, eth->h_dest);
+ ether_addr_copy(sa, eth->h_source);
+ skb_pull(msdu, sizeof(struct ethhdr));
+
+ /* push rfc1042/llc/snap */
+ memcpy(skb_push(msdu, sizeof(struct rfc1042_hdr)), rfc1042,
+ sizeof(struct rfc1042_hdr));
+
+ /* push original 802.11 header */
+ hdr = (struct ieee80211_hdr *)first_hdr;
hdr_len = ieee80211_hdrlen(hdr->frame_control);
+ memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
- skb->ip_summed = ath10k_htt_rx_get_csum_state(skb);
+ /* original 802.11 header has a different DA and in
+ * case of 4addr it may also have different SA
+ */
+ hdr = (struct ieee80211_hdr *)msdu->data;
+ ether_addr_copy(ieee80211_get_DA(hdr), da);
+ ether_addr_copy(ieee80211_get_SA(hdr), sa);
+}
- switch (fmt) {
+static void ath10k_htt_rx_h_undecap_snap(struct ath10k *ar,
+ struct sk_buff *msdu,
+ struct ieee80211_rx_status *status,
+ const u8 first_hdr[64])
+{
+ struct ieee80211_hdr *hdr;
+ size_t hdr_len;
+
+ /* Delivered decapped frame:
+ * [amsdu header] <-- replaced with 802.11 hdr
+ * [rfc1042/llc]
+ * [payload]
+ */
+
+ skb_pull(msdu, sizeof(struct amsdu_subframe_hdr));
+
+ hdr = (struct ieee80211_hdr *)first_hdr;
+ hdr_len = ieee80211_hdrlen(hdr->frame_control);
+ memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
+}
+
+static void ath10k_htt_rx_h_undecap(struct ath10k *ar,
+ struct sk_buff *msdu,
+ struct ieee80211_rx_status *status,
+ u8 first_hdr[64],
+ enum htt_rx_mpdu_encrypt_type enctype,
+ bool is_decrypted)
+{
+ struct htt_rx_desc *rxd;
+ enum rx_msdu_decap_format decap;
+ struct ieee80211_hdr *hdr;
+
+ /* First msdu's decapped header:
+ * [802.11 header] <-- padded to 4 bytes long
+ * [crypto param] <-- padded to 4 bytes long
+ * [amsdu header] <-- only if A-MSDU
+ * [rfc1042/llc]
+ *
+ * Other (2nd, 3rd, ..) msdu's decapped header:
+ * [amsdu header] <-- only if A-MSDU
+ * [rfc1042/llc]
+ */
+
+ rxd = (void *)msdu->data - sizeof(*rxd);
+ hdr = (void *)rxd->rx_hdr_status;
+ decap = MS(__le32_to_cpu(rxd->msdu_start.info1),
+ RX_MSDU_START_INFO1_DECAP_FORMAT);
+
+ switch (decap) {
case RX_MSDU_DECAP_RAW:
- /* remove trailing FCS */
- skb_trim(skb, skb->len - FCS_LEN);
+ ath10k_htt_rx_h_undecap_raw(ar, msdu, status, enctype,
+ is_decrypted);
break;
case RX_MSDU_DECAP_NATIVE_WIFI:
- /* Pull decapped header */
- hdr = (struct ieee80211_hdr *)skb->data;
- hdr_len = ath10k_htt_rx_nwifi_hdrlen(hdr);
- skb_pull(skb, hdr_len);
-
- /* Push original header */
- hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status;
- hdr_len = ieee80211_hdrlen(hdr->frame_control);
- memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
+ ath10k_htt_rx_h_undecap_nwifi(ar, msdu, status, first_hdr);
break;
case RX_MSDU_DECAP_ETHERNET2_DIX:
- /* strip ethernet header and insert decapped 802.11 header and
- * rfc1042 header */
-
- rfc1042 = hdr;
- rfc1042 += roundup(hdr_len, 4);
- rfc1042 += roundup(ath10k_htt_rx_crypto_param_len(ar,
- enctype), 4);
-
- skb_pull(skb, sizeof(struct ethhdr));
- memcpy(skb_push(skb, sizeof(struct rfc1042_hdr)),
- rfc1042, sizeof(struct rfc1042_hdr));
- memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
+ ath10k_htt_rx_h_undecap_eth(ar, msdu, status, first_hdr, enctype);
break;
case RX_MSDU_DECAP_8023_SNAP_LLC:
- /* remove A-MSDU subframe header and insert
- * decapped 802.11 header. rfc1042 header is already there */
-
- skb_pull(skb, sizeof(struct amsdu_subframe_hdr));
- memcpy(skb_push(skb, hdr_len), hdr, hdr_len);
+ ath10k_htt_rx_h_undecap_snap(ar, msdu, status, first_hdr);
break;
}
-
- ath10k_htt_rx_h_protected(htt, rx_status, skb, enctype, fmt, false);
-
- ath10k_process_rx(htt->ar, rx_status, skb);
}
static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb)
@@ -1122,10 +1109,128 @@
return CHECKSUM_UNNECESSARY;
}
-static int ath10k_unchain_msdu(struct sk_buff *msdu_head)
+static void ath10k_htt_rx_h_csum_offload(struct sk_buff *msdu)
{
- struct sk_buff *next = msdu_head->next;
- struct sk_buff *to_free = next;
+ msdu->ip_summed = ath10k_htt_rx_get_csum_state(msdu);
+}
+
+static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
+ struct sk_buff_head *amsdu,
+ struct ieee80211_rx_status *status)
+{
+ struct sk_buff *first;
+ struct sk_buff *last;
+ struct sk_buff *msdu;
+ struct htt_rx_desc *rxd;
+ struct ieee80211_hdr *hdr;
+ enum htt_rx_mpdu_encrypt_type enctype;
+ u8 first_hdr[64];
+ u8 *qos;
+ size_t hdr_len;
+ bool has_fcs_err;
+ bool has_crypto_err;
+ bool has_tkip_err;
+ bool has_peer_idx_invalid;
+ bool is_decrypted;
+ u32 attention;
+
+ if (skb_queue_empty(amsdu))
+ return;
+
+ first = skb_peek(amsdu);
+ rxd = (void *)first->data - sizeof(*rxd);
+
+ enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
+ RX_MPDU_START_INFO0_ENCRYPT_TYPE);
+
+ /* First MSDU's Rx descriptor in an A-MSDU contains full 802.11
+ * decapped header. It'll be used for undecapping of each MSDU.
+ */
+ hdr = (void *)rxd->rx_hdr_status;
+ hdr_len = ieee80211_hdrlen(hdr->frame_control);
+ memcpy(first_hdr, hdr, hdr_len);
+
+ /* Each A-MSDU subframe will use the original header as the base and be
+ * reported as a separate MSDU so strip the A-MSDU bit from QoS Ctl.
+ */
+ hdr = (void *)first_hdr;
+ qos = ieee80211_get_qos_ctl(hdr);
+ qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
+
+ /* Some attention flags are valid only in the last MSDU. */
+ last = skb_peek_tail(amsdu);
+ rxd = (void *)last->data - sizeof(*rxd);
+ attention = __le32_to_cpu(rxd->attention.flags);
+
+ has_fcs_err = !!(attention & RX_ATTENTION_FLAGS_FCS_ERR);
+ has_crypto_err = !!(attention & RX_ATTENTION_FLAGS_DECRYPT_ERR);
+ has_tkip_err = !!(attention & RX_ATTENTION_FLAGS_TKIP_MIC_ERR);
+ has_peer_idx_invalid = !!(attention & RX_ATTENTION_FLAGS_PEER_IDX_INVALID);
+
+ /* Note: If hardware captures an encrypted frame that it can't decrypt,
+ * e.g. due to fcs error, missing peer or invalid key data it will
+ * report the frame as raw.
+ */
+ is_decrypted = (enctype != HTT_RX_MPDU_ENCRYPT_NONE &&
+ !has_fcs_err &&
+ !has_crypto_err &&
+ !has_peer_idx_invalid);
+
+ /* Clear per-MPDU flags while leaving per-PPDU flags intact. */
+ status->flag &= ~(RX_FLAG_FAILED_FCS_CRC |
+ RX_FLAG_MMIC_ERROR |
+ RX_FLAG_DECRYPTED |
+ RX_FLAG_IV_STRIPPED |
+ RX_FLAG_MMIC_STRIPPED);
+
+ if (has_fcs_err)
+ status->flag |= RX_FLAG_FAILED_FCS_CRC;
+
+ if (has_tkip_err)
+ status->flag |= RX_FLAG_MMIC_ERROR;
+
+ if (is_decrypted)
+ status->flag |= RX_FLAG_DECRYPTED |
+ RX_FLAG_IV_STRIPPED |
+ RX_FLAG_MMIC_STRIPPED;
+
+ skb_queue_walk(amsdu, msdu) {
+ ath10k_htt_rx_h_csum_offload(msdu);
+ ath10k_htt_rx_h_undecap(ar, msdu, status, first_hdr, enctype,
+ is_decrypted);
+
+ /* Undecapping involves copying the original 802.11 header back
+ * to sk_buff. If frame is protected and hardware has decrypted
+ * it then remove the protected bit.
+ */
+ if (!is_decrypted)
+ continue;
+
+ hdr = (void *)msdu->data;
+ hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+ }
+}
+
+static void ath10k_htt_rx_h_deliver(struct ath10k *ar,
+ struct sk_buff_head *amsdu,
+ struct ieee80211_rx_status *status)
+{
+ struct sk_buff *msdu;
+
+ while ((msdu = __skb_dequeue(amsdu))) {
+ /* Setup per-MSDU flags */
+ if (skb_queue_empty(amsdu))
+ status->flag &= ~RX_FLAG_AMSDU_MORE;
+ else
+ status->flag |= RX_FLAG_AMSDU_MORE;
+
+ ath10k_process_rx(ar, status, msdu);
+ }
+}
+
+static int ath10k_unchain_msdu(struct sk_buff_head *amsdu)
+{
+ struct sk_buff *skb, *first;
int space;
int total_len = 0;
@@ -1136,113 +1241,142 @@
* skb?
*/
- msdu_head->next = NULL;
+ first = __skb_dequeue(amsdu);
/* Allocate total length all at once. */
- while (next) {
- total_len += next->len;
- next = next->next;
- }
+ skb_queue_walk(amsdu, skb)
+ total_len += skb->len;
- space = total_len - skb_tailroom(msdu_head);
+ space = total_len - skb_tailroom(first);
if ((space > 0) &&
- (pskb_expand_head(msdu_head, 0, space, GFP_ATOMIC) < 0)) {
+ (pskb_expand_head(first, 0, space, GFP_ATOMIC) < 0)) {
/* TODO: bump some rx-oom error stat */
/* put it back together so we can free the
* whole list at once.
*/
- msdu_head->next = to_free;
+ __skb_queue_head(amsdu, first);
return -1;
}
/* Walk list again, copying contents into
* msdu_head
*/
- next = to_free;
- while (next) {
- skb_copy_from_linear_data(next, skb_put(msdu_head, next->len),
- next->len);
- next = next->next;
+ while ((skb = __skb_dequeue(amsdu))) {
+ skb_copy_from_linear_data(skb, skb_put(first, skb->len),
+ skb->len);
+ dev_kfree_skb_any(skb);
}
- /* If here, we have consolidated skb. Free the
- * fragments and pass the main skb on up the
- * stack.
- */
- ath10k_htt_rx_free_msdu_chain(to_free);
+ __skb_queue_head(amsdu, first);
return 0;
}
-static bool ath10k_htt_rx_amsdu_allowed(struct ath10k_htt *htt,
- struct sk_buff *head,
- enum htt_rx_mpdu_status status,
- bool channel_set,
- u32 attention)
+static void ath10k_htt_rx_h_unchain(struct ath10k *ar,
+ struct sk_buff_head *amsdu,
+ bool chained)
{
- struct ath10k *ar = htt->ar;
+ struct sk_buff *first;
+ struct htt_rx_desc *rxd;
+ enum rx_msdu_decap_format decap;
- if (head->len == 0) {
- ath10k_dbg(ar, ATH10K_DBG_HTT,
- "htt rx dropping due to zero-len\n");
+ first = skb_peek(amsdu);
+ rxd = (void *)first->data - sizeof(*rxd);
+ decap = MS(__le32_to_cpu(rxd->msdu_start.info1),
+ RX_MSDU_START_INFO1_DECAP_FORMAT);
+
+ if (!chained)
+ return;
+
+ /* FIXME: Current unchaining logic can only handle simple case of raw
+ * msdu chaining. If decapping is other than raw the chaining may be
+ * more complex and this isn't handled by the current code. Don't even
+ * try re-constructing such frames - it'll be pretty much garbage.
+ */
+ if (decap != RX_MSDU_DECAP_RAW ||
+ skb_queue_len(amsdu) != 1 + rxd->frag_info.ring2_more_count) {
+ __skb_queue_purge(amsdu);
+ return;
+ }
+
+ ath10k_unchain_msdu(amsdu);
+}
+
+static bool ath10k_htt_rx_amsdu_allowed(struct ath10k *ar,
+ struct sk_buff_head *amsdu,
+ struct ieee80211_rx_status *rx_status)
+{
+ struct sk_buff *msdu;
+ struct htt_rx_desc *rxd;
+ bool is_mgmt;
+ bool has_fcs_err;
+
+ msdu = skb_peek(amsdu);
+ rxd = (void *)msdu->data - sizeof(*rxd);
+
+ /* FIXME: It might be a good idea to do some fuzzy-testing to drop
+ * invalid/dangerous frames.
+ */
+
+ if (!rx_status->freq) {
+ ath10k_warn(ar, "no channel configured; ignoring frame(s)!\n");
return false;
}
- if (attention & RX_ATTENTION_FLAGS_DECRYPT_ERR) {
- ath10k_dbg(ar, ATH10K_DBG_HTT,
- "htt rx dropping due to decrypt-err\n");
- return false;
- }
+ is_mgmt = !!(rxd->attention.flags &
+ __cpu_to_le32(RX_ATTENTION_FLAGS_MGMT_TYPE));
+ has_fcs_err = !!(rxd->attention.flags &
+ __cpu_to_le32(RX_ATTENTION_FLAGS_FCS_ERR));
- if (!channel_set) {
- ath10k_warn(ar, "no channel configured; ignoring frame!\n");
- return false;
- }
-
- /* Skip mgmt frames while we handle this in WMI */
- if (status == HTT_RX_IND_MPDU_STATUS_MGMT_CTRL ||
- attention & RX_ATTENTION_FLAGS_MGMT_TYPE) {
+ /* Management frames are handled via WMI events. The pros of such
+ * approach is that channel is explicitly provided in WMI events
+ * whereas HTT doesn't provide channel information for Rxed frames.
+ *
+ * However some firmware revisions don't report corrupted frames via
+ * WMI so don't drop them.
+ */
+ if (is_mgmt && !has_fcs_err) {
ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx mgmt ctrl\n");
return false;
}
- if (status != HTT_RX_IND_MPDU_STATUS_OK &&
- status != HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR &&
- status != HTT_RX_IND_MPDU_STATUS_ERR_INV_PEER &&
- !htt->ar->monitor_started) {
- ath10k_dbg(ar, ATH10K_DBG_HTT,
- "htt rx ignoring frame w/ status %d\n",
- status);
- return false;
- }
-
- if (test_bit(ATH10K_CAC_RUNNING, &htt->ar->dev_flags)) {
- ath10k_dbg(ar, ATH10K_DBG_HTT,
- "htt rx CAC running\n");
+ if (test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags)) {
+ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx cac running\n");
return false;
}
return true;
}
+static void ath10k_htt_rx_h_filter(struct ath10k *ar,
+ struct sk_buff_head *amsdu,
+ struct ieee80211_rx_status *rx_status)
+{
+ if (skb_queue_empty(amsdu))
+ return;
+
+ if (ath10k_htt_rx_amsdu_allowed(ar, amsdu, rx_status))
+ return;
+
+ __skb_queue_purge(amsdu);
+}
+
static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
struct htt_rx_indication *rx)
{
struct ath10k *ar = htt->ar;
struct ieee80211_rx_status *rx_status = &htt->rx_status;
struct htt_rx_indication_mpdu_range *mpdu_ranges;
- struct htt_rx_desc *rxd;
- enum htt_rx_mpdu_status status;
- struct ieee80211_hdr *hdr;
+ struct sk_buff_head amsdu;
int num_mpdu_ranges;
- u32 attention;
int fw_desc_len;
u8 *fw_desc;
- bool channel_set;
- int i, j;
- int ret;
+ int i, ret, mpdu_count = 0;
lockdep_assert_held(&htt->rx_ring.lock);
+ if (htt->rx_confused)
+ return;
+
fw_desc_len = __le16_to_cpu(rx->prefix.fw_rx_desc_bytes);
fw_desc = (u8 *)&rx->fw_desc;
@@ -1250,92 +1384,33 @@
HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES);
mpdu_ranges = htt_rx_ind_get_mpdu_ranges(rx);
- /* Fill this once, while this is per-ppdu */
- if (rx->ppdu.info0 & HTT_RX_INDICATION_INFO0_START_VALID) {
- memset(rx_status, 0, sizeof(*rx_status));
- rx_status->signal = ATH10K_DEFAULT_NOISE_FLOOR +
- rx->ppdu.combined_rssi;
- }
-
- if (rx->ppdu.info0 & HTT_RX_INDICATION_INFO0_END_VALID) {
- /* TSF available only in 32-bit */
- rx_status->mactime = __le32_to_cpu(rx->ppdu.tsf) & 0xffffffff;
- rx_status->flag |= RX_FLAG_MACTIME_END;
- }
-
- channel_set = ath10k_htt_rx_h_channel(htt->ar, rx_status);
-
- if (channel_set) {
- ath10k_htt_rx_h_rates(htt->ar, rx_status->band,
- rx->ppdu.info0,
- __le32_to_cpu(rx->ppdu.info1),
- __le32_to_cpu(rx->ppdu.info2),
- rx_status);
- }
-
ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx ind: ",
rx, sizeof(*rx) +
(sizeof(struct htt_rx_indication_mpdu_range) *
num_mpdu_ranges));
- for (i = 0; i < num_mpdu_ranges; i++) {
- status = mpdu_ranges[i].mpdu_range_status;
+ for (i = 0; i < num_mpdu_ranges; i++)
+ mpdu_count += mpdu_ranges[i].mpdu_count;
- for (j = 0; j < mpdu_ranges[i].mpdu_count; j++) {
- struct sk_buff *msdu_head, *msdu_tail;
-
- attention = 0;
- msdu_head = NULL;
- msdu_tail = NULL;
- ret = ath10k_htt_rx_amsdu_pop(htt,
- &fw_desc,
- &fw_desc_len,
- &msdu_head,
- &msdu_tail,
- &attention);
-
- if (ret < 0) {
- ath10k_warn(ar, "failed to pop amsdu from htt rx ring %d\n",
- ret);
- ath10k_htt_rx_free_msdu_chain(msdu_head);
- continue;
- }
-
- rxd = container_of((void *)msdu_head->data,
- struct htt_rx_desc,
- msdu_payload);
-
- if (!ath10k_htt_rx_amsdu_allowed(htt, msdu_head,
- status,
- channel_set,
- attention)) {
- ath10k_htt_rx_free_msdu_chain(msdu_head);
- continue;
- }
-
- if (ret > 0 &&
- ath10k_unchain_msdu(msdu_head) < 0) {
- ath10k_htt_rx_free_msdu_chain(msdu_head);
- continue;
- }
-
- if (attention & RX_ATTENTION_FLAGS_FCS_ERR)
- rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
- else
- rx_status->flag &= ~RX_FLAG_FAILED_FCS_CRC;
-
- if (attention & RX_ATTENTION_FLAGS_TKIP_MIC_ERR)
- rx_status->flag |= RX_FLAG_MMIC_ERROR;
- else
- rx_status->flag &= ~RX_FLAG_MMIC_ERROR;
-
- hdr = ath10k_htt_rx_skb_get_hdr(msdu_head);
-
- if (ath10k_htt_rx_hdr_is_amsdu(hdr))
- ath10k_htt_rx_amsdu(htt, rx_status, msdu_head);
- else
- ath10k_htt_rx_msdu(htt, rx_status, msdu_head);
+ while (mpdu_count--) {
+ __skb_queue_head_init(&amsdu);
+ ret = ath10k_htt_rx_amsdu_pop(htt, &fw_desc,
+ &fw_desc_len, &amsdu);
+ if (ret < 0) {
+ ath10k_warn(ar, "rx ring became corrupted: %d\n", ret);
+ __skb_queue_purge(&amsdu);
+ /* FIXME: It's probably a good idea to reboot the
+ * device instead of leaving it inoperable.
+ */
+ htt->rx_confused = true;
+ break;
}
+
+ ath10k_htt_rx_h_ppdu(ar, &amsdu, rx_status);
+ ath10k_htt_rx_h_unchain(ar, &amsdu, ret > 0);
+ ath10k_htt_rx_h_filter(ar, &amsdu, rx_status);
+ ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status);
+ ath10k_htt_rx_h_deliver(ar, &amsdu, rx_status);
}
tasklet_schedule(&htt->rx_replenish_task);
@@ -1345,108 +1420,44 @@
struct htt_rx_fragment_indication *frag)
{
struct ath10k *ar = htt->ar;
- struct sk_buff *msdu_head, *msdu_tail;
- enum htt_rx_mpdu_encrypt_type enctype;
- struct htt_rx_desc *rxd;
- enum rx_msdu_decap_format fmt;
struct ieee80211_rx_status *rx_status = &htt->rx_status;
- struct ieee80211_hdr *hdr;
+ struct sk_buff_head amsdu;
int ret;
- bool tkip_mic_err;
- bool decrypt_err;
u8 *fw_desc;
- int fw_desc_len, hdrlen, paramlen;
- int trim;
- u32 attention = 0;
+ int fw_desc_len;
fw_desc_len = __le16_to_cpu(frag->fw_rx_desc_bytes);
fw_desc = (u8 *)frag->fw_msdu_rx_desc;
- msdu_head = NULL;
- msdu_tail = NULL;
+ __skb_queue_head_init(&amsdu);
spin_lock_bh(&htt->rx_ring.lock);
ret = ath10k_htt_rx_amsdu_pop(htt, &fw_desc, &fw_desc_len,
- &msdu_head, &msdu_tail,
- &attention);
+ &amsdu);
spin_unlock_bh(&htt->rx_ring.lock);
+ tasklet_schedule(&htt->rx_replenish_task);
+
ath10k_dbg(ar, ATH10K_DBG_HTT_DUMP, "htt rx frag ahead\n");
if (ret) {
ath10k_warn(ar, "failed to pop amsdu from httr rx ring for fragmented rx %d\n",
ret);
- ath10k_htt_rx_free_msdu_chain(msdu_head);
+ __skb_queue_purge(&amsdu);
return;
}
- /* FIXME: implement signal strength */
- rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL;
-
- hdr = (struct ieee80211_hdr *)msdu_head->data;
- rxd = (void *)msdu_head->data - sizeof(*rxd);
- tkip_mic_err = !!(attention & RX_ATTENTION_FLAGS_TKIP_MIC_ERR);
- decrypt_err = !!(attention & RX_ATTENTION_FLAGS_DECRYPT_ERR);
- fmt = MS(__le32_to_cpu(rxd->msdu_start.info1),
- RX_MSDU_START_INFO1_DECAP_FORMAT);
-
- if (fmt != RX_MSDU_DECAP_RAW) {
- ath10k_warn(ar, "we dont support non-raw fragmented rx yet\n");
- dev_kfree_skb_any(msdu_head);
- goto end;
+ if (skb_queue_len(&amsdu) != 1) {
+ ath10k_warn(ar, "failed to pop frag amsdu: too many msdus\n");
+ __skb_queue_purge(&amsdu);
+ return;
}
- enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0),
- RX_MPDU_START_INFO0_ENCRYPT_TYPE);
- ath10k_htt_rx_h_protected(htt, rx_status, msdu_head, enctype, fmt,
- true);
- msdu_head->ip_summed = ath10k_htt_rx_get_csum_state(msdu_head);
+ ath10k_htt_rx_h_ppdu(ar, &amsdu, rx_status);
+ ath10k_htt_rx_h_filter(ar, &amsdu, rx_status);
+ ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status);
+ ath10k_htt_rx_h_deliver(ar, &amsdu, rx_status);
- if (tkip_mic_err)
- ath10k_warn(ar, "tkip mic error\n");
-
- if (decrypt_err) {
- ath10k_warn(ar, "decryption err in fragmented rx\n");
- dev_kfree_skb_any(msdu_head);
- goto end;
- }
-
- if (enctype != HTT_RX_MPDU_ENCRYPT_NONE) {
- hdrlen = ieee80211_hdrlen(hdr->frame_control);
- paramlen = ath10k_htt_rx_crypto_param_len(ar, enctype);
-
- /* It is more efficient to move the header than the payload */
- memmove((void *)msdu_head->data + paramlen,
- (void *)msdu_head->data,
- hdrlen);
- skb_pull(msdu_head, paramlen);
- hdr = (struct ieee80211_hdr *)msdu_head->data;
- }
-
- /* remove trailing FCS */
- trim = 4;
-
- /* remove crypto trailer */
- trim += ath10k_htt_rx_crypto_tail_len(ar, enctype);
-
- /* last fragment of TKIP frags has MIC */
- if (!ieee80211_has_morefrags(hdr->frame_control) &&
- enctype == HTT_RX_MPDU_ENCRYPT_TKIP_WPA)
- trim += 8;
-
- if (trim > msdu_head->len) {
- ath10k_warn(ar, "htt rx fragment: trailer longer than the frame itself? drop\n");
- dev_kfree_skb_any(msdu_head);
- goto end;
- }
-
- skb_trim(msdu_head, msdu_head->len - trim);
-
- ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx frag mpdu: ",
- msdu_head->data, msdu_head->len);
- ath10k_process_rx(htt->ar, rx_status, msdu_head);
-
-end:
if (fw_desc_len > 0) {
ath10k_dbg(ar, ATH10K_DBG_HTT,
"expecting more fragmented rx in one indication %d\n",
@@ -1674,6 +1685,15 @@
case HTT_T2H_MSG_TYPE_RX_DELBA:
ath10k_htt_rx_delba(ar, resp);
break;
+ case HTT_T2H_MSG_TYPE_PKTLOG: {
+ struct ath10k_pktlog_hdr *hdr =
+ (struct ath10k_pktlog_hdr *)resp->pktlog_msg.payload;
+
+ trace_ath10k_htt_pktlog(ar, resp->pktlog_msg.payload,
+ sizeof(*hdr) +
+ __le16_to_cpu(hdr->size));
+ break;
+ }
case HTT_T2H_MSG_TYPE_RX_FLUSH: {
/* Ignore this event because mac80211 takes care of Rx
* aggregation reordering.
@@ -1681,8 +1701,8 @@
break;
}
default:
- ath10k_dbg(ar, ATH10K_DBG_HTT, "htt event (%d) not handled\n",
- resp->hdr.msg_type);
+ ath10k_warn(ar, "htt event (%d) not handled\n",
+ resp->hdr.msg_type);
ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt event: ",
skb->data, skb->len);
break;
diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c
index c6768e2..a0f8a0b 100644
--- a/drivers/net/wireless/ath/ath10k/htt_tx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
@@ -74,8 +74,8 @@
{
struct ath10k *ar = htt->ar;
int tx_idx;
+
spin_lock_init(&htt->tx_msdu_pool_lock);
- init_waitqueue_head(&htt->empty_tx_wq);
if (test_bit(ATH10K_FW_FEATURE_WMI_10X, htt->ar->fw_features))
htt->max_num_pending_tx = TARGET_10X_NUM_MSDU_DESC;
@@ -549,14 +549,18 @@
skb_cb->htt.txbuf->cmd_tx.len = __cpu_to_le16(msdu->len);
skb_cb->htt.txbuf->cmd_tx.id = __cpu_to_le16(msdu_id);
skb_cb->htt.txbuf->cmd_tx.frags_paddr = __cpu_to_le32(frags_paddr);
- skb_cb->htt.txbuf->cmd_tx.peerid = __cpu_to_le32(HTT_INVALID_PEERID);
+ skb_cb->htt.txbuf->cmd_tx.peerid = __cpu_to_le16(HTT_INVALID_PEERID);
+ skb_cb->htt.txbuf->cmd_tx.freq = __cpu_to_le16(skb_cb->htt.freq);
+ trace_ath10k_htt_tx(ar, msdu_id, msdu->len, vdev_id, tid);
ath10k_dbg(ar, ATH10K_DBG_HTT,
- "htt tx flags0 %hhu flags1 %hu len %d id %hu frags_paddr %08x, msdu_paddr %08x vdev %hhu tid %hhu\n",
+ "htt tx flags0 %hhu flags1 %hu len %d id %hu frags_paddr %08x, msdu_paddr %08x vdev %hhu tid %hhu freq %hu\n",
flags0, flags1, msdu->len, msdu_id, frags_paddr,
- (u32)skb_cb->paddr, vdev_id, tid);
+ (u32)skb_cb->paddr, vdev_id, tid, skb_cb->htt.freq);
ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt tx msdu: ",
msdu->data, msdu->len);
+ trace_ath10k_tx_hdr(ar, msdu->data, msdu->len);
+ trace_ath10k_tx_payload(ar, msdu->data, msdu->len);
sg_items[0].transfer_id = 0;
sg_items[0].transfer_context = NULL;
diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h
index 3cf5702..dfedfd0 100644
--- a/drivers/net/wireless/ath/ath10k/hw.h
+++ b/drivers/net/wireless/ath/ath10k/hw.h
@@ -20,15 +20,16 @@
#include "targaddrs.h"
+#define ATH10K_FW_DIR "ath10k"
+
/* QCA988X 1.0 definitions (unsupported) */
#define QCA988X_HW_1_0_CHIP_ID_REV 0x0
/* QCA988X 2.0 definitions */
#define QCA988X_HW_2_0_VERSION 0x4100016c
#define QCA988X_HW_2_0_CHIP_ID_REV 0x2
-#define QCA988X_HW_2_0_FW_DIR "ath10k/QCA988X/hw2.0"
+#define QCA988X_HW_2_0_FW_DIR ATH10K_FW_DIR "/QCA988X/hw2.0"
#define QCA988X_HW_2_0_FW_FILE "firmware.bin"
-#define QCA988X_HW_2_0_FW_3_FILE "firmware-3.bin"
#define QCA988X_HW_2_0_OTP_FILE "otp.bin"
#define QCA988X_HW_2_0_BOARD_DATA_FILE "board.bin"
#define QCA988X_HW_2_0_PATCH_LOAD_ADDR 0x1234
@@ -43,6 +44,8 @@
#define REG_DUMP_COUNT_QCA988X 60
+#define QCA988X_CAL_DATA_LEN 2116
+
struct ath10k_fw_ie {
__le32 id;
__le32 len;
@@ -78,6 +81,15 @@
ATH10K_MCAST2UCAST_ENABLED = 1,
};
+struct ath10k_pktlog_hdr {
+ __le16 flags;
+ __le16 missed_cnt;
+ __le16 log_type;
+ __le16 size;
+ __le32 timestamp;
+ u8 payload[0];
+} __packed;
+
/* Target specific defines for MAIN firmware */
#define TARGET_NUM_VDEVS 8
#define TARGET_NUM_PEER_AST 2
@@ -85,11 +97,13 @@
#define TARGET_DMA_BURST_SIZE 0
#define TARGET_MAC_AGGR_DELIM 0
#define TARGET_AST_SKID_LIMIT 16
-#define TARGET_NUM_PEERS 16
+#define TARGET_NUM_STATIONS 16
+#define TARGET_NUM_PEERS ((TARGET_NUM_STATIONS) + \
+ (TARGET_NUM_VDEVS))
#define TARGET_NUM_OFFLOAD_PEERS 0
#define TARGET_NUM_OFFLOAD_REORDER_BUFS 0
#define TARGET_NUM_PEER_KEYS 2
-#define TARGET_NUM_TIDS (2 * ((TARGET_NUM_PEERS) + (TARGET_NUM_VDEVS)))
+#define TARGET_NUM_TIDS ((TARGET_NUM_PEERS) * 2)
#define TARGET_TX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2))
#define TARGET_RX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2))
#define TARGET_RX_TIMEOUT_LO_PRI 100
@@ -120,12 +134,15 @@
#define TARGET_10X_DMA_BURST_SIZE 0
#define TARGET_10X_MAC_AGGR_DELIM 0
#define TARGET_10X_AST_SKID_LIMIT 16
-#define TARGET_10X_NUM_PEERS (128 + (TARGET_10X_NUM_VDEVS))
-#define TARGET_10X_NUM_PEERS_MAX 128
+#define TARGET_10X_NUM_STATIONS 128
+#define TARGET_10X_NUM_PEERS ((TARGET_10X_NUM_STATIONS) + \
+ (TARGET_10X_NUM_VDEVS))
#define TARGET_10X_NUM_OFFLOAD_PEERS 0
#define TARGET_10X_NUM_OFFLOAD_REORDER_BUFS 0
#define TARGET_10X_NUM_PEER_KEYS 2
-#define TARGET_10X_NUM_TIDS 256
+#define TARGET_10X_NUM_TIDS_MAX 256
+#define TARGET_10X_NUM_TIDS min((TARGET_10X_NUM_TIDS_MAX), \
+ (TARGET_10X_NUM_PEERS) * 2)
#define TARGET_10X_TX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2))
#define TARGET_10X_RX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2))
#define TARGET_10X_RX_TIMEOUT_LO_PRI 100
@@ -279,6 +296,7 @@
#define SI_RX_DATA1_OFFSET 0x00000014
#define CORE_CTRL_CPU_INTR_MASK 0x00002000
+#define CORE_CTRL_PCIE_REG_31_MASK 0x00000800
#define CORE_CTRL_ADDRESS 0x0000
#define PCIE_INTR_ENABLE_ADDRESS 0x0008
#define PCIE_INTR_CAUSE_ADDRESS 0x000c
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 6b02c2d..58ab03e 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -136,7 +136,9 @@
if (ret)
return ret;
+ spin_lock_bh(&ar->data_lock);
peer->keys[i] = arvif->wep_keys[i];
+ spin_unlock_bh(&ar->data_lock);
}
return 0;
@@ -173,12 +175,39 @@
ath10k_warn(ar, "failed to remove peer wep key %d: %d\n",
i, ret);
+ spin_lock_bh(&ar->data_lock);
peer->keys[i] = NULL;
+ spin_unlock_bh(&ar->data_lock);
}
return first_errno;
}
+bool ath10k_mac_is_peer_wep_key_set(struct ath10k *ar, const u8 *addr,
+ u8 keyidx)
+{
+ struct ath10k_peer *peer;
+ int i;
+
+ lockdep_assert_held(&ar->data_lock);
+
+ /* We don't know which vdev this peer belongs to,
+ * since WMI doesn't give us that information.
+ *
+ * FIXME: multi-bss needs to be handled.
+ */
+ peer = ath10k_peer_find(ar, 0, addr);
+ if (!peer)
+ return false;
+
+ for (i = 0; i < ARRAY_SIZE(peer->keys); i++) {
+ if (peer->keys[i] && peer->keys[i]->keyidx == keyidx)
+ return true;
+ }
+
+ return false;
+}
+
static int ath10k_clear_vdev_key(struct ath10k_vif *arvif,
struct ieee80211_key_conf *key)
{
@@ -322,61 +351,30 @@
static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr)
{
- struct ath10k_peer *peer;
int ret;
lockdep_assert_held(&ar->conf_mutex);
- peer = ath10k_peer_find(ar, vdev_id, addr);
- if (peer) {
- ath10k_warn(ar, "peer %pM on vdev %i already exists\n",
- addr, vdev_id);
- return -EINVAL;
- }
-
- peer = kzalloc(sizeof(*peer), GFP_KERNEL);
- if (!peer) {
- ath10k_warn(ar, "failed to allocate peer %pM on vdev %i: not enough memory\n",
- addr, vdev_id);
- return -ENOMEM;
- }
-
- peer->vdev_id = vdev_id;
- memcpy(peer->addr, addr, ETH_ALEN);
-
- spin_lock_bh(&ar->data_lock);
- list_add(&peer->list, &ar->peers);
- spin_unlock_bh(&ar->data_lock);
+ if (ar->num_peers >= ar->max_num_peers)
+ return -ENOBUFS;
ret = ath10k_wmi_peer_create(ar, vdev_id, addr);
if (ret) {
ath10k_warn(ar, "failed to create wmi peer %pM on vdev %i: %i\n",
addr, vdev_id, ret);
- goto err_free;
+ return ret;
}
ret = ath10k_wait_for_peer_created(ar, vdev_id, addr);
if (ret) {
ath10k_warn(ar, "failed to wait for created wmi peer %pM on vdev %i: %i\n",
addr, vdev_id, ret);
- goto err_free;
+ return ret;
}
- spin_lock_bh(&ar->data_lock);
+
ar->num_peers++;
- spin_unlock_bh(&ar->data_lock);
return 0;
-
-err_free:
- spin_lock_bh(&ar->data_lock);
- list_del(&peer->list);
- /* very unlikely, but check anyway */
- if (!bitmap_empty(peer->peer_ids, ATH10K_MAX_NUM_PEER_IDS))
- ath10k_warn(ar, "removing peer %pM on vdev %i still being mapped in firmware\n",
- addr, vdev_id);
- spin_unlock_bh(&ar->data_lock);
- kfree(peer);
- return ret;
}
static int ath10k_mac_set_kickout(struct ath10k_vif *arvif)
@@ -424,15 +422,11 @@
return 0;
}
-static int ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value)
+static int ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value)
{
struct ath10k *ar = arvif->ar;
u32 vdev_param;
- if (value != 0xFFFFFFFF)
- value = min_t(u32, arvif->ar->hw->wiphy->rts_threshold,
- ATH10K_RTS_MAX);
-
vdev_param = ar->wmi.vdev_param->rts_threshold;
return ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, value);
}
@@ -479,9 +473,7 @@
goto out;
}
- spin_lock_bh(&ar->data_lock);
ar->num_peers--;
- spin_unlock_bh(&ar->data_lock);
out:
spin_lock_bh(&ar->data_lock);
@@ -528,20 +520,59 @@
list_del(&peer->list);
kfree(peer);
}
- ar->num_peers = 0;
spin_unlock_bh(&ar->data_lock);
+
+ ar->num_peers = 0;
+ ar->num_stations = 0;
}
/************************/
/* Interface management */
/************************/
+void ath10k_mac_vif_beacon_free(struct ath10k_vif *arvif)
+{
+ struct ath10k *ar = arvif->ar;
+
+ lockdep_assert_held(&ar->data_lock);
+
+ if (!arvif->beacon)
+ return;
+
+ if (!arvif->beacon_buf)
+ dma_unmap_single(ar->dev, ATH10K_SKB_CB(arvif->beacon)->paddr,
+ arvif->beacon->len, DMA_TO_DEVICE);
+
+ dev_kfree_skb_any(arvif->beacon);
+
+ arvif->beacon = NULL;
+ arvif->beacon_sent = false;
+}
+
+static void ath10k_mac_vif_beacon_cleanup(struct ath10k_vif *arvif)
+{
+ struct ath10k *ar = arvif->ar;
+
+ lockdep_assert_held(&ar->data_lock);
+
+ ath10k_mac_vif_beacon_free(arvif);
+
+ if (arvif->beacon_buf) {
+ dma_free_coherent(ar->dev, IEEE80211_MAX_FRAME_LEN,
+ arvif->beacon_buf, arvif->beacon_paddr);
+ arvif->beacon_buf = NULL;
+ }
+}
+
static inline int ath10k_vdev_setup_sync(struct ath10k *ar)
{
int ret;
lockdep_assert_held(&ar->conf_mutex);
+ if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
+ return -ESHUTDOWN;
+
ret = wait_for_completion_timeout(&ar->vdev_setup_done,
ATH10K_VDEV_SETUP_TIMEOUT_HZ);
if (ret == 0)
@@ -574,6 +605,8 @@
arg.channel.max_reg_power = channel->max_reg_power * 2;
arg.channel.max_antenna_gain = channel->max_antenna_gain * 2;
+ reinit_completion(&ar->vdev_setup_done);
+
ret = ath10k_wmi_vdev_start(ar, &arg);
if (ret) {
ath10k_warn(ar, "failed to request monitor vdev %i start: %d\n",
@@ -621,6 +654,8 @@
ath10k_warn(ar, "failed to put down monitor vdev %i: %d\n",
ar->monitor_vdev_id, ret);
+ reinit_completion(&ar->vdev_setup_done);
+
ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id);
if (ret)
ath10k_warn(ar, "failed to to request monitor vdev %i stop: %d\n",
@@ -647,9 +682,9 @@
return -ENOMEM;
}
- bit = ffs(ar->free_vdev_map);
+ bit = __ffs64(ar->free_vdev_map);
- ar->monitor_vdev_id = bit - 1;
+ ar->monitor_vdev_id = bit;
ret = ath10k_wmi_vdev_create(ar, ar->monitor_vdev_id,
WMI_VDEV_TYPE_MONITOR,
@@ -660,7 +695,7 @@
return ret;
}
- ar->free_vdev_map &= ~(1 << ar->monitor_vdev_id);
+ ar->free_vdev_map &= ~(1LL << ar->monitor_vdev_id);
ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor vdev %d created\n",
ar->monitor_vdev_id);
@@ -680,7 +715,7 @@
return ret;
}
- ar->free_vdev_map |= 1 << ar->monitor_vdev_id;
+ ar->free_vdev_map |= 1LL << ar->monitor_vdev_id;
ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor vdev %d deleted\n",
ar->monitor_vdev_id);
@@ -966,15 +1001,7 @@
arvif->is_up = false;
spin_lock_bh(&arvif->ar->data_lock);
- if (arvif->beacon) {
- dma_unmap_single(arvif->ar->dev,
- ATH10K_SKB_CB(arvif->beacon)->paddr,
- arvif->beacon->len, DMA_TO_DEVICE);
- dev_kfree_skb_any(arvif->beacon);
-
- arvif->beacon = NULL;
- arvif->beacon_sent = false;
- }
+ ath10k_mac_vif_beacon_free(arvif);
spin_unlock_bh(&arvif->ar->data_lock);
return;
@@ -1023,14 +1050,6 @@
if (is_zero_ether_addr(arvif->bssid))
return;
- ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id,
- arvif->bssid);
- if (ret) {
- ath10k_warn(ar, "failed to delete IBSS BSSID peer %pM for vdev %d: %d\n",
- arvif->bssid, arvif->vdev_id, ret);
- return;
- }
-
memset(arvif->bssid, 0, ETH_ALEN);
return;
@@ -1099,51 +1118,45 @@
/* Station management */
/**********************/
+static u32 ath10k_peer_assoc_h_listen_intval(struct ath10k *ar,
+ struct ieee80211_vif *vif)
+{
+ /* Some firmware revisions have unstable STA powersave when listen
+ * interval is set too high (e.g. 5). The symptoms are firmware doesn't
+ * generate NullFunc frames properly even if buffered frames have been
+ * indicated in Beacon TIM. Firmware would seldom wake up to pull
+ * buffered frames. Often pinging the device from AP would simply fail.
+ *
+ * As a workaround set it to 1.
+ */
+ if (vif->type == NL80211_IFTYPE_STATION)
+ return 1;
+
+ return ar->hw->conf.listen_interval;
+}
+
static void ath10k_peer_assoc_h_basic(struct ath10k *ar,
- struct ath10k_vif *arvif,
+ struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
- struct ieee80211_bss_conf *bss_conf,
struct wmi_peer_assoc_complete_arg *arg)
{
+ struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+
lockdep_assert_held(&ar->conf_mutex);
ether_addr_copy(arg->addr, sta->addr);
arg->vdev_id = arvif->vdev_id;
arg->peer_aid = sta->aid;
arg->peer_flags |= WMI_PEER_AUTH;
-
- if (arvif->vdev_type == WMI_VDEV_TYPE_STA)
- /*
- * Seems FW have problems with Power Save in STA
- * mode when we setup this parameter to high (eg. 5).
- * Often we see that FW don't send NULL (with clean P flags)
- * frame even there is info about buffered frames in beacons.
- * Sometimes we have to wait more than 10 seconds before FW
- * will wakeup. Often sending one ping from AP to our device
- * just fail (more than 50%).
- *
- * Seems setting this FW parameter to 1 couse FW
- * will check every beacon and will wakup immediately
- * after detection buffered data.
- */
- arg->peer_listen_intval = 1;
- else
- arg->peer_listen_intval = ar->hw->conf.listen_interval;
-
+ arg->peer_listen_intval = ath10k_peer_assoc_h_listen_intval(ar, vif);
arg->peer_num_spatial_streams = 1;
-
- /*
- * The assoc capabilities are available only in managed mode.
- */
- if (arvif->vdev_type == WMI_VDEV_TYPE_STA && bss_conf)
- arg->peer_caps = bss_conf->assoc_capability;
+ arg->peer_caps = vif->bss_conf.assoc_capability;
}
static void ath10k_peer_assoc_h_crypto(struct ath10k *ar,
- struct ath10k_vif *arvif,
+ struct ieee80211_vif *vif,
struct wmi_peer_assoc_complete_arg *arg)
{
- struct ieee80211_vif *vif = arvif->vif;
struct ieee80211_bss_conf *info = &vif->bss_conf;
struct cfg80211_bss *bss;
const u8 *rsnie = NULL;
@@ -1400,11 +1413,12 @@
}
static void ath10k_peer_assoc_h_qos(struct ath10k *ar,
- struct ath10k_vif *arvif,
+ struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
- struct ieee80211_bss_conf *bss_conf,
struct wmi_peer_assoc_complete_arg *arg)
{
+ struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+
switch (arvif->vdev_type) {
case WMI_VDEV_TYPE_AP:
if (sta->wme)
@@ -1416,7 +1430,7 @@
}
break;
case WMI_VDEV_TYPE_STA:
- if (bss_conf->qos)
+ if (vif->bss_conf.qos)
arg->peer_flags |= WMI_PEER_QOS;
break;
default:
@@ -1425,7 +1439,7 @@
}
static void ath10k_peer_assoc_h_phymode(struct ath10k *ar,
- struct ath10k_vif *arvif,
+ struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct wmi_peer_assoc_complete_arg *arg)
{
@@ -1476,22 +1490,21 @@
}
static int ath10k_peer_assoc_prepare(struct ath10k *ar,
- struct ath10k_vif *arvif,
+ struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
- struct ieee80211_bss_conf *bss_conf,
struct wmi_peer_assoc_complete_arg *arg)
{
lockdep_assert_held(&ar->conf_mutex);
memset(arg, 0, sizeof(*arg));
- ath10k_peer_assoc_h_basic(ar, arvif, sta, bss_conf, arg);
- ath10k_peer_assoc_h_crypto(ar, arvif, arg);
+ ath10k_peer_assoc_h_basic(ar, vif, sta, arg);
+ ath10k_peer_assoc_h_crypto(ar, vif, arg);
ath10k_peer_assoc_h_rates(ar, sta, arg);
ath10k_peer_assoc_h_ht(ar, sta, arg);
ath10k_peer_assoc_h_vht(ar, sta, arg);
- ath10k_peer_assoc_h_qos(ar, arvif, sta, bss_conf, arg);
- ath10k_peer_assoc_h_phymode(ar, arvif, sta, arg);
+ ath10k_peer_assoc_h_qos(ar, vif, sta, arg);
+ ath10k_peer_assoc_h_phymode(ar, vif, sta, arg);
return 0;
}
@@ -1537,6 +1550,9 @@
lockdep_assert_held(&ar->conf_mutex);
+ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %i assoc bssid %pM aid %d\n",
+ arvif->vdev_id, arvif->bssid, arvif->aid);
+
rcu_read_lock();
ap_sta = ieee80211_find_sta(vif, bss_conf->bssid);
@@ -1551,8 +1567,7 @@
* before calling ath10k_setup_peer_smps() which might sleep. */
ht_cap = ap_sta->ht_cap;
- ret = ath10k_peer_assoc_prepare(ar, arvif, ap_sta,
- bss_conf, &peer_arg);
+ ret = ath10k_peer_assoc_prepare(ar, vif, ap_sta, &peer_arg);
if (ret) {
ath10k_warn(ar, "failed to prepare peer assoc for %pM vdev %i: %d\n",
bss_conf->bssid, arvif->vdev_id, ret);
@@ -1580,6 +1595,8 @@
"mac vdev %d up (associated) bssid %pM aid %d\n",
arvif->vdev_id, bss_conf->bssid, bss_conf->aid);
+ WARN_ON(arvif->is_up);
+
arvif->aid = bss_conf->aid;
ether_addr_copy(arvif->bssid, bss_conf->bssid);
@@ -1593,9 +1610,6 @@
arvif->is_up = true;
}
-/*
- * FIXME: flush TIDs
- */
static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
@@ -1605,45 +1619,30 @@
lockdep_assert_held(&ar->conf_mutex);
- /*
- * For some reason, calling VDEV-DOWN before VDEV-STOP
- * makes the FW to send frames via HTT after disassociation.
- * No idea why this happens, even though VDEV-DOWN is supposed
- * to be analogous to link down, so just stop the VDEV.
- */
- ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d stop (disassociated\n",
- arvif->vdev_id);
+ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %i disassoc bssid %pM\n",
+ arvif->vdev_id, arvif->bssid);
- /* FIXME: check return value */
- ret = ath10k_vdev_stop(arvif);
-
- /*
- * If we don't call VDEV-DOWN after VDEV-STOP FW will remain active and
- * report beacons from previously associated network through HTT.
- * This in turn would spam mac80211 WARN_ON if we bring down all
- * interfaces as it expects there is no rx when no interface is
- * running.
- */
- ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d down\n", arvif->vdev_id);
-
- /* FIXME: why don't we print error if wmi call fails? */
ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id);
+ if (ret)
+ ath10k_warn(ar, "faield to down vdev %i: %d\n",
+ arvif->vdev_id, ret);
arvif->def_wep_key_idx = 0;
-
- arvif->is_started = false;
arvif->is_up = false;
}
-static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif,
- struct ieee80211_sta *sta, bool reassoc)
+static int ath10k_station_assoc(struct ath10k *ar,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ bool reassoc)
{
+ struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
struct wmi_peer_assoc_complete_arg peer_arg;
int ret = 0;
lockdep_assert_held(&ar->conf_mutex);
- ret = ath10k_peer_assoc_prepare(ar, arvif, sta, NULL, &peer_arg);
+ ret = ath10k_peer_assoc_prepare(ar, vif, sta, &peer_arg);
if (ret) {
ath10k_warn(ar, "failed to prepare WMI peer assoc for %pM vdev %i: %i\n",
sta->addr, arvif->vdev_id, ret);
@@ -1658,43 +1657,51 @@
return ret;
}
- ret = ath10k_setup_peer_smps(ar, arvif, sta->addr, &sta->ht_cap);
- if (ret) {
- ath10k_warn(ar, "failed to setup peer SMPS for vdev %d: %d\n",
- arvif->vdev_id, ret);
- return ret;
- }
-
- if (!sta->wme && !reassoc) {
- arvif->num_legacy_stations++;
- ret = ath10k_recalc_rtscts_prot(arvif);
+ /* Re-assoc is run only to update supported rates for given station. It
+ * doesn't make much sense to reconfigure the peer completely.
+ */
+ if (!reassoc) {
+ ret = ath10k_setup_peer_smps(ar, arvif, sta->addr,
+ &sta->ht_cap);
if (ret) {
- ath10k_warn(ar, "failed to recalculate rts/cts prot for vdev %d: %d\n",
+ ath10k_warn(ar, "failed to setup peer SMPS for vdev %d: %d\n",
+ arvif->vdev_id, ret);
+ return ret;
+ }
+
+ ret = ath10k_peer_assoc_qos_ap(ar, arvif, sta);
+ if (ret) {
+ ath10k_warn(ar, "failed to set qos params for STA %pM for vdev %i: %d\n",
+ sta->addr, arvif->vdev_id, ret);
+ return ret;
+ }
+
+ if (!sta->wme) {
+ arvif->num_legacy_stations++;
+ ret = ath10k_recalc_rtscts_prot(arvif);
+ if (ret) {
+ ath10k_warn(ar, "failed to recalculate rts/cts prot for vdev %d: %d\n",
+ arvif->vdev_id, ret);
+ return ret;
+ }
+ }
+
+ ret = ath10k_install_peer_wep_keys(arvif, sta->addr);
+ if (ret) {
+ ath10k_warn(ar, "failed to install peer wep keys for vdev %i: %d\n",
arvif->vdev_id, ret);
return ret;
}
}
- ret = ath10k_install_peer_wep_keys(arvif, sta->addr);
- if (ret) {
- ath10k_warn(ar, "failed to install peer wep keys for vdev %i: %d\n",
- arvif->vdev_id, ret);
- return ret;
- }
-
- ret = ath10k_peer_assoc_qos_ap(ar, arvif, sta);
- if (ret) {
- ath10k_warn(ar, "failed to set qos params for STA %pM for vdev %i: %d\n",
- sta->addr, arvif->vdev_id, ret);
- return ret;
- }
-
return ret;
}
-static int ath10k_station_disassoc(struct ath10k *ar, struct ath10k_vif *arvif,
+static int ath10k_station_disassoc(struct ath10k *ar,
+ struct ieee80211_vif *vif,
struct ieee80211_sta *sta)
{
+ struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
int ret = 0;
lockdep_assert_held(&ar->conf_mutex);
@@ -1786,6 +1793,7 @@
ch->passive = passive;
ch->freq = channel->center_freq;
+ ch->band_center_freq1 = channel->center_freq;
ch->min_power = 0;
ch->max_power = channel->max_power * 2;
ch->max_reg_power = channel->max_reg_power * 2;
@@ -2040,6 +2048,18 @@
}
}
+static bool ath10k_mac_need_offchan_tx_work(struct ath10k *ar)
+{
+ /* FIXME: Not really sure since when the behaviour changed. At some
+ * point new firmware stopped requiring creation of peer entries for
+ * offchannel tx (and actually creating them causes issues with wmi-htc
+ * tx credit replenishment and reliability). Assuming it's at least 3.4
+ * because that's when the `freq` was introduced to TX_FRM HTT command.
+ */
+ return !(ar->htt.target_version_major >= 3 &&
+ ar->htt.target_version_minor >= 4);
+}
+
static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
@@ -2311,10 +2331,10 @@
case ATH10K_SCAN_IDLE:
break;
case ATH10K_SCAN_RUNNING:
- case ATH10K_SCAN_ABORTING:
if (ar->scan.is_roc)
ieee80211_remain_on_channel_expired(ar->hw);
- else
+ case ATH10K_SCAN_ABORTING:
+ if (!ar->scan.is_roc)
ieee80211_scan_completed(ar->hw,
(ar->scan.state ==
ATH10K_SCAN_ABORTING));
@@ -2480,23 +2500,28 @@
if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
spin_lock_bh(&ar->data_lock);
- ATH10K_SKB_CB(skb)->htt.is_offchan = true;
+ ATH10K_SKB_CB(skb)->htt.freq = ar->scan.roc_freq;
ATH10K_SKB_CB(skb)->vdev_id = ar->scan.vdev_id;
spin_unlock_bh(&ar->data_lock);
- ath10k_dbg(ar, ATH10K_DBG_MAC, "queued offchannel skb %p\n",
- skb);
+ if (ath10k_mac_need_offchan_tx_work(ar)) {
+ ATH10K_SKB_CB(skb)->htt.freq = 0;
+ ATH10K_SKB_CB(skb)->htt.is_offchan = true;
- skb_queue_tail(&ar->offchan_tx_queue, skb);
- ieee80211_queue_work(hw, &ar->offchan_tx_work);
- return;
+ ath10k_dbg(ar, ATH10K_DBG_MAC, "queued offchannel skb %p\n",
+ skb);
+
+ skb_queue_tail(&ar->offchan_tx_queue, skb);
+ ieee80211_queue_work(hw, &ar->offchan_tx_work);
+ return;
+ }
}
ath10k_tx_htt(ar, skb);
}
/* Must not be called with conf_mutex held as workers can use that also. */
-static void ath10k_drain_tx(struct ath10k *ar)
+void ath10k_drain_tx(struct ath10k *ar)
{
/* make sure rcu-protected mac80211 tx path itself is drained */
synchronize_net();
@@ -2529,16 +2554,8 @@
ath10k_hif_power_down(ar);
spin_lock_bh(&ar->data_lock);
- list_for_each_entry(arvif, &ar->arvifs, list) {
- if (!arvif->beacon)
- continue;
-
- dma_unmap_single(arvif->ar->dev,
- ATH10K_SKB_CB(arvif->beacon)->paddr,
- arvif->beacon->len, DMA_TO_DEVICE);
- dev_kfree_skb_any(arvif->beacon);
- arvif->beacon = NULL;
- }
+ list_for_each_entry(arvif, &ar->arvifs, list)
+ ath10k_mac_vif_beacon_cleanup(arvif);
spin_unlock_bh(&ar->data_lock);
}
@@ -2561,12 +2578,28 @@
return 0;
}
+static void ath10k_check_chain_mask(struct ath10k *ar, u32 cm, const char *dbg)
+{
+ /* It is not clear that allowing gaps in chainmask
+ * is helpful. Probably it will not do what user
+ * is hoping for, so warn in that case.
+ */
+ if (cm == 15 || cm == 7 || cm == 3 || cm == 1 || cm == 0)
+ return;
+
+ ath10k_warn(ar, "mac %s antenna chainmask may be invalid: 0x%x. Suggested values: 15, 7, 3, 1 or 0.\n",
+ dbg, cm);
+}
+
static int __ath10k_set_antenna(struct ath10k *ar, u32 tx_ant, u32 rx_ant)
{
int ret;
lockdep_assert_held(&ar->conf_mutex);
+ ath10k_check_chain_mask(ar, tx_ant, "tx");
+ ath10k_check_chain_mask(ar, rx_ant, "rx");
+
ar->cfg_tx_chainmask = tx_ant;
ar->cfg_rx_chainmask = rx_ant;
@@ -2830,12 +2863,68 @@
ath10k_monitor_recalc(ar);
}
+static int ath10k_mac_txpower_setup(struct ath10k *ar, int txpower)
+{
+ int ret;
+ u32 param;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac txpower %d\n", txpower);
+
+ param = ar->wmi.pdev_param->txpower_limit2g;
+ ret = ath10k_wmi_pdev_set_param(ar, param, txpower * 2);
+ if (ret) {
+ ath10k_warn(ar, "failed to set 2g txpower %d: %d\n",
+ txpower, ret);
+ return ret;
+ }
+
+ param = ar->wmi.pdev_param->txpower_limit5g;
+ ret = ath10k_wmi_pdev_set_param(ar, param, txpower * 2);
+ if (ret) {
+ ath10k_warn(ar, "failed to set 5g txpower %d: %d\n",
+ txpower, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ath10k_mac_txpower_recalc(struct ath10k *ar)
+{
+ struct ath10k_vif *arvif;
+ int ret, txpower = -1;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+ list_for_each_entry(arvif, &ar->arvifs, list) {
+ WARN_ON(arvif->txpower < 0);
+
+ if (txpower == -1)
+ txpower = arvif->txpower;
+ else
+ txpower = min(txpower, arvif->txpower);
+ }
+
+ if (WARN_ON(txpower == -1))
+ return -EINVAL;
+
+ ret = ath10k_mac_txpower_setup(ar, txpower);
+ if (ret) {
+ ath10k_warn(ar, "failed to setup tx power %d: %d\n",
+ txpower, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
{
struct ath10k *ar = hw->priv;
struct ieee80211_conf *conf = &hw->conf;
int ret = 0;
- u32 param;
mutex_lock(&ar->conf_mutex);
@@ -2859,25 +2948,6 @@
}
}
- if (changed & IEEE80211_CONF_CHANGE_POWER) {
- ath10k_dbg(ar, ATH10K_DBG_MAC, "mac config power %d\n",
- hw->conf.power_level);
-
- param = ar->wmi.pdev_param->txpower_limit2g;
- ret = ath10k_wmi_pdev_set_param(ar, param,
- hw->conf.power_level * 2);
- if (ret)
- ath10k_warn(ar, "failed to set 2g txpower %d: %d\n",
- hw->conf.power_level, ret);
-
- param = ar->wmi.pdev_param->txpower_limit5g;
- ret = ath10k_wmi_pdev_set_param(ar, param,
- hw->conf.power_level * 2);
- if (ret)
- ath10k_warn(ar, "failed to set 5g txpower %d: %d\n",
- hw->conf.power_level, ret);
- }
-
if (changed & IEEE80211_CONF_CHANGE_PS)
ath10k_config_ps(ar);
@@ -2892,6 +2962,17 @@
return ret;
}
+static u32 get_nss_from_chainmask(u16 chain_mask)
+{
+ if ((chain_mask & 0x15) == 0x15)
+ return 4;
+ else if ((chain_mask & 0x7) == 0x7)
+ return 3;
+ else if ((chain_mask & 0x3) == 0x3)
+ return 2;
+ return 1;
+}
+
/*
* TODO:
* Figure out how to handle WMI_VDEV_SUBTYPE_P2P_DEVICE,
@@ -2925,9 +3006,12 @@
ret = -EBUSY;
goto err;
}
- bit = ffs(ar->free_vdev_map);
+ bit = __ffs64(ar->free_vdev_map);
- arvif->vdev_id = bit - 1;
+ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac create vdev %i map %llx\n",
+ bit, ar->free_vdev_map);
+
+ arvif->vdev_id = bit;
arvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE;
if (ar->p2p)
@@ -2957,8 +3041,39 @@
break;
}
- ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev create %d (add interface) type %d subtype %d\n",
- arvif->vdev_id, arvif->vdev_type, arvif->vdev_subtype);
+ /* Some firmware revisions don't wait for beacon tx completion before
+ * sending another SWBA event. This could lead to hardware using old
+ * (freed) beacon data in some cases, e.g. tx credit starvation
+ * combined with missed TBTT. This is very very rare.
+ *
+ * On non-IOMMU-enabled hosts this could be a possible security issue
+ * because hw could beacon some random data on the air. On
+ * IOMMU-enabled hosts DMAR faults would occur in most cases and target
+ * device would crash.
+ *
+ * Since there are no beacon tx completions (implicit nor explicit)
+ * propagated to host the only workaround for this is to allocate a
+ * DMA-coherent buffer for a lifetime of a vif and use it for all
+ * beacon tx commands. Worst case for this approach is some beacons may
+ * become corrupted, e.g. have garbled IEs or out-of-date TIM bitmap.
+ */
+ if (vif->type == NL80211_IFTYPE_ADHOC ||
+ vif->type == NL80211_IFTYPE_AP) {
+ arvif->beacon_buf = dma_zalloc_coherent(ar->dev,
+ IEEE80211_MAX_FRAME_LEN,
+ &arvif->beacon_paddr,
+ GFP_ATOMIC);
+ if (!arvif->beacon_buf) {
+ ret = -ENOMEM;
+ ath10k_warn(ar, "failed to allocate beacon buffer: %d\n",
+ ret);
+ goto err;
+ }
+ }
+
+ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev create %d (add interface) type %d subtype %d bcnmode %s\n",
+ arvif->vdev_id, arvif->vdev_type, arvif->vdev_subtype,
+ arvif->beacon_buf ? "single-buf" : "per-skb");
ret = ath10k_wmi_vdev_create(ar, arvif->vdev_id, arvif->vdev_type,
arvif->vdev_subtype, vif->addr);
@@ -2968,7 +3083,7 @@
goto err;
}
- ar->free_vdev_map &= ~(1 << arvif->vdev_id);
+ ar->free_vdev_map &= ~(1LL << arvif->vdev_id);
list_add(&arvif->list, &ar->arvifs);
vdev_param = ar->wmi.vdev_param->def_keyid;
@@ -2990,6 +3105,20 @@
goto err_vdev_delete;
}
+ if (ar->cfg_tx_chainmask) {
+ u16 nss = get_nss_from_chainmask(ar->cfg_tx_chainmask);
+
+ vdev_param = ar->wmi.vdev_param->nss;
+ ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
+ nss);
+ if (ret) {
+ ath10k_warn(ar, "failed to set vdev %i chainmask 0x%x, nss %i: %d\n",
+ arvif->vdev_id, ar->cfg_tx_chainmask, nss,
+ ret);
+ goto err_vdev_delete;
+ }
+ }
+
if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
ret = ath10k_peer_create(ar, arvif->vdev_id, vif->addr);
if (ret) {
@@ -3052,6 +3181,13 @@
goto err_peer_delete;
}
+ arvif->txpower = vif->bss_conf.txpower;
+ ret = ath10k_mac_txpower_recalc(ar);
+ if (ret) {
+ ath10k_warn(ar, "failed to recalc tx power: %d\n", ret);
+ goto err_peer_delete;
+ }
+
mutex_unlock(&ar->conf_mutex);
return 0;
@@ -3061,10 +3197,16 @@
err_vdev_delete:
ath10k_wmi_vdev_delete(ar, arvif->vdev_id);
- ar->free_vdev_map |= 1 << arvif->vdev_id;
+ ar->free_vdev_map |= 1LL << arvif->vdev_id;
list_del(&arvif->list);
err:
+ if (arvif->beacon_buf) {
+ dma_free_coherent(ar->dev, IEEE80211_MAX_FRAME_LEN,
+ arvif->beacon_buf, arvif->beacon_paddr);
+ arvif->beacon_buf = NULL;
+ }
+
mutex_unlock(&ar->conf_mutex);
return ret;
@@ -3077,19 +3219,12 @@
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
int ret;
- mutex_lock(&ar->conf_mutex);
-
cancel_work_sync(&arvif->wep_key_work);
- spin_lock_bh(&ar->data_lock);
- if (arvif->beacon) {
- dma_unmap_single(arvif->ar->dev,
- ATH10K_SKB_CB(arvif->beacon)->paddr,
- arvif->beacon->len, DMA_TO_DEVICE);
- dev_kfree_skb_any(arvif->beacon);
- arvif->beacon = NULL;
- }
+ mutex_lock(&ar->conf_mutex);
+ spin_lock_bh(&ar->data_lock);
+ ath10k_mac_vif_beacon_cleanup(arvif);
spin_unlock_bh(&ar->data_lock);
ret = ath10k_spectral_vif_stop(arvif);
@@ -3097,7 +3232,7 @@
ath10k_warn(ar, "failed to stop spectral for vdev %i: %d\n",
arvif->vdev_id, ret);
- ar->free_vdev_map |= 1 << arvif->vdev_id;
+ ar->free_vdev_map |= 1LL << arvif->vdev_id;
list_del(&arvif->list);
if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
@@ -3221,54 +3356,8 @@
arvif->u.ap.hidden_ssid = info->hidden_ssid;
}
- /*
- * Firmware manages AP self-peer internally so make sure to not create
- * it in driver. Otherwise AP self-peer deletion may timeout later.
- */
- if (changed & BSS_CHANGED_BSSID &&
- vif->type != NL80211_IFTYPE_AP) {
- if (!is_zero_ether_addr(info->bssid)) {
- ath10k_dbg(ar, ATH10K_DBG_MAC,
- "mac vdev %d create peer %pM\n",
- arvif->vdev_id, info->bssid);
-
- ret = ath10k_peer_create(ar, arvif->vdev_id,
- info->bssid);
- if (ret)
- ath10k_warn(ar, "failed to add peer %pM for vdev %d when changing bssid: %i\n",
- info->bssid, arvif->vdev_id, ret);
-
- if (vif->type == NL80211_IFTYPE_STATION) {
- /*
- * this is never erased as we it for crypto key
- * clearing; this is FW requirement
- */
- ether_addr_copy(arvif->bssid, info->bssid);
-
- ath10k_dbg(ar, ATH10K_DBG_MAC,
- "mac vdev %d start %pM\n",
- arvif->vdev_id, info->bssid);
-
- ret = ath10k_vdev_start(arvif);
- if (ret) {
- ath10k_warn(ar, "failed to start vdev %i: %d\n",
- arvif->vdev_id, ret);
- goto exit;
- }
-
- arvif->is_started = true;
- }
-
- /*
- * Mac80211 does not keep IBSS bssid when leaving IBSS,
- * so driver need to store it. It is needed when leaving
- * IBSS in order to remove BSSID peer.
- */
- if (vif->type == NL80211_IFTYPE_ADHOC)
- memcpy(arvif->bssid, info->bssid,
- ETH_ALEN);
- }
- }
+ if (changed & BSS_CHANGED_BSSID && !is_zero_ether_addr(info->bssid))
+ ether_addr_copy(arvif->bssid, info->bssid);
if (changed & BSS_CHANGED_BEACON_ENABLED)
ath10k_control_beaconing(arvif, info);
@@ -3330,10 +3419,21 @@
ath10k_monitor_stop(ar);
ath10k_bss_assoc(hw, vif, info);
ath10k_monitor_recalc(ar);
+ } else {
+ ath10k_bss_disassoc(hw, vif);
}
}
-exit:
+ if (changed & BSS_CHANGED_TXPOWER) {
+ ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev_id %i txpower %d\n",
+ arvif->vdev_id, info->txpower);
+
+ arvif->txpower = info->txpower;
+ ret = ath10k_mac_txpower_recalc(ar);
+ if (ret)
+ ath10k_warn(ar, "failed to recalc tx power: %d\n", ret);
+ }
+
mutex_unlock(&ar->conf_mutex);
}
@@ -3419,9 +3519,10 @@
struct ath10k *ar = hw->priv;
mutex_lock(&ar->conf_mutex);
- cancel_delayed_work_sync(&ar->scan.timeout);
ath10k_scan_abort(ar);
mutex_unlock(&ar->conf_mutex);
+
+ cancel_delayed_work_sync(&ar->scan.timeout);
}
static void ath10k_set_key_h_def_keyidx(struct ath10k *ar,
@@ -3606,7 +3707,7 @@
ath10k_dbg(ar, ATH10K_DBG_MAC, "mac update sta %pM supp rates\n",
sta->addr);
- err = ath10k_station_assoc(ar, arvif, sta, true);
+ err = ath10k_station_assoc(ar, arvif->vif, sta, true);
if (err)
ath10k_warn(ar, "failed to reassociate station: %pM\n",
sta->addr);
@@ -3615,6 +3716,37 @@
mutex_unlock(&ar->conf_mutex);
}
+static int ath10k_mac_inc_num_stations(struct ath10k_vif *arvif)
+{
+ struct ath10k *ar = arvif->ar;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+ if (arvif->vdev_type != WMI_VDEV_TYPE_AP &&
+ arvif->vdev_type != WMI_VDEV_TYPE_IBSS)
+ return 0;
+
+ if (ar->num_stations >= ar->max_num_stations)
+ return -ENOBUFS;
+
+ ar->num_stations++;
+
+ return 0;
+}
+
+static void ath10k_mac_dec_num_stations(struct ath10k_vif *arvif)
+{
+ struct ath10k *ar = arvif->ar;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+ if (arvif->vdev_type != WMI_VDEV_TYPE_AP &&
+ arvif->vdev_type != WMI_VDEV_TYPE_IBSS)
+ return;
+
+ ar->num_stations--;
+}
+
static int ath10k_sta_state(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
@@ -3624,7 +3756,6 @@
struct ath10k *ar = hw->priv;
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
- int max_num_peers;
int ret = 0;
if (old_state == IEEE80211_STA_NOTEXIST &&
@@ -3642,31 +3773,46 @@
mutex_lock(&ar->conf_mutex);
if (old_state == IEEE80211_STA_NOTEXIST &&
- new_state == IEEE80211_STA_NONE &&
- vif->type != NL80211_IFTYPE_STATION) {
+ new_state == IEEE80211_STA_NONE) {
/*
* New station addition.
*/
- if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
- max_num_peers = TARGET_10X_NUM_PEERS_MAX - 1;
- else
- max_num_peers = TARGET_NUM_PEERS;
+ ath10k_dbg(ar, ATH10K_DBG_MAC,
+ "mac vdev %d peer create %pM (new sta) sta %d / %d peer %d / %d\n",
+ arvif->vdev_id, sta->addr,
+ ar->num_stations + 1, ar->max_num_stations,
+ ar->num_peers + 1, ar->max_num_peers);
- if (ar->num_peers >= max_num_peers) {
- ath10k_warn(ar, "number of peers exceeded: peers number %d (max peers %d)\n",
- ar->num_peers, max_num_peers);
- ret = -ENOBUFS;
+ ret = ath10k_mac_inc_num_stations(arvif);
+ if (ret) {
+ ath10k_warn(ar, "refusing to associate station: too many connected already (%d)\n",
+ ar->max_num_stations);
goto exit;
}
- ath10k_dbg(ar, ATH10K_DBG_MAC,
- "mac vdev %d peer create %pM (new sta) num_peers %d\n",
- arvif->vdev_id, sta->addr, ar->num_peers);
-
ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr);
- if (ret)
+ if (ret) {
ath10k_warn(ar, "failed to add peer %pM for vdev %d when adding a new sta: %i\n",
sta->addr, arvif->vdev_id, ret);
+ ath10k_mac_dec_num_stations(arvif);
+ goto exit;
+ }
+
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ WARN_ON(arvif->is_started);
+
+ ret = ath10k_vdev_start(arvif);
+ if (ret) {
+ ath10k_warn(ar, "failed to start vdev %i: %d\n",
+ arvif->vdev_id, ret);
+ WARN_ON(ath10k_peer_delete(ar, arvif->vdev_id,
+ sta->addr));
+ ath10k_mac_dec_num_stations(arvif);
+ goto exit;
+ }
+
+ arvif->is_started = true;
+ }
} else if ((old_state == IEEE80211_STA_NONE &&
new_state == IEEE80211_STA_NOTEXIST)) {
/*
@@ -3675,13 +3821,24 @@
ath10k_dbg(ar, ATH10K_DBG_MAC,
"mac vdev %d peer delete %pM (sta gone)\n",
arvif->vdev_id, sta->addr);
+
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ WARN_ON(!arvif->is_started);
+
+ ret = ath10k_vdev_stop(arvif);
+ if (ret)
+ ath10k_warn(ar, "failed to stop vdev %i: %d\n",
+ arvif->vdev_id, ret);
+
+ arvif->is_started = false;
+ }
+
ret = ath10k_peer_delete(ar, arvif->vdev_id, sta->addr);
if (ret)
ath10k_warn(ar, "failed to delete peer %pM for vdev %d: %i\n",
sta->addr, arvif->vdev_id, ret);
- if (vif->type == NL80211_IFTYPE_STATION)
- ath10k_bss_disassoc(hw, vif);
+ ath10k_mac_dec_num_stations(arvif);
} else if (old_state == IEEE80211_STA_AUTH &&
new_state == IEEE80211_STA_ASSOC &&
(vif->type == NL80211_IFTYPE_AP ||
@@ -3692,7 +3849,7 @@
ath10k_dbg(ar, ATH10K_DBG_MAC, "mac sta %pM associated\n",
sta->addr);
- ret = ath10k_station_assoc(ar, arvif, sta, false);
+ ret = ath10k_station_assoc(ar, vif, sta, false);
if (ret)
ath10k_warn(ar, "failed to associate station %pM for vdev %i: %i\n",
sta->addr, arvif->vdev_id, ret);
@@ -3706,7 +3863,7 @@
ath10k_dbg(ar, ATH10K_DBG_MAC, "mac sta %pM disassociated\n",
sta->addr);
- ret = ath10k_station_disassoc(ar, arvif, sta);
+ ret = ath10k_station_disassoc(ar, vif, sta);
if (ret)
ath10k_warn(ar, "failed to disassociate station: %pM vdev %i: %i\n",
sta->addr, arvif->vdev_id, ret);
@@ -3870,6 +4027,8 @@
if (ret)
goto exit;
+ duration = max(duration, WMI_SCAN_CHAN_MIN_TIME_MSEC);
+
memset(&arg, 0, sizeof(arg));
ath10k_wmi_start_scan_init(ar, &arg);
arg.vdev_id = arvif->vdev_id;
@@ -3914,10 +4073,11 @@
struct ath10k *ar = hw->priv;
mutex_lock(&ar->conf_mutex);
- cancel_delayed_work_sync(&ar->scan.timeout);
ath10k_scan_abort(ar);
mutex_unlock(&ar->conf_mutex);
+ cancel_delayed_work_sync(&ar->scan.timeout);
+
return 0;
}
@@ -3960,7 +4120,7 @@
ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d fragmentation threshold %d\n",
arvif->vdev_id, value);
- ret = ath10k_mac_set_rts(arvif, value);
+ ret = ath10k_mac_set_frag(arvif, value);
if (ret) {
ath10k_warn(ar, "failed to set fragmentation threshold for vdev %d: %d\n",
arvif->vdev_id, ret);
@@ -4008,7 +4168,9 @@
spin_unlock_irqrestore(&ar->wmi_mgmt_tx_queue.lock,
flags);
- skip = (ar->state == ATH10K_STATE_WEDGED);
+ skip = (ar->state == ATH10K_STATE_WEDGED) ||
+ test_bit(ATH10K_FLAG_CRASH_FLUSH,
+ &ar->dev_flags);
((htt_empty && wmi_empty) || skip);
}), ATH10K_FLUSH_TIMEOUT_HZ);
@@ -4150,10 +4312,14 @@
}
#endif
-static void ath10k_restart_complete(struct ieee80211_hw *hw)
+static void ath10k_reconfig_complete(struct ieee80211_hw *hw,
+ enum ieee80211_reconfig_type reconfig_type)
{
struct ath10k *ar = hw->priv;
+ if (reconfig_type != IEEE80211_RECONFIG_TYPE_RESTART)
+ return;
+
mutex_lock(&ar->conf_mutex);
/* If device failed to restart it will be in a different state, e.g.
@@ -4161,6 +4327,7 @@
if (ar->state == ATH10K_STATE_RESTARTED) {
ath10k_info(ar, "device successfully recovered\n");
ar->state = ATH10K_STATE_ON;
+ ieee80211_wake_queues(ar->hw);
}
mutex_unlock(&ar->conf_mutex);
@@ -4196,6 +4363,9 @@
survey->channel = &sband->channels[idx];
+ if (ar->rx_channel == survey->channel)
+ survey->filled |= SURVEY_INFO_IN_USE;
+
exit:
mutex_unlock(&ar->conf_mutex);
return ret;
@@ -4243,6 +4413,10 @@
u32 legacy = 0x00ff;
u8 ht = 0xff, i;
u16 vht = 0x3ff;
+ u16 nrf = ar->num_rf_chains;
+
+ if (ar->cfg_tx_chainmask)
+ nrf = get_nss_from_chainmask(ar->cfg_tx_chainmask);
switch (band) {
case IEEE80211_BAND_2GHZ:
@@ -4258,11 +4432,11 @@
if (mask->control[band].legacy != legacy)
return false;
- for (i = 0; i < ar->num_rf_chains; i++)
+ for (i = 0; i < nrf; i++)
if (mask->control[band].ht_mcs[i] != ht)
return false;
- for (i = 0; i < ar->num_rf_chains; i++)
+ for (i = 0; i < nrf; i++)
if (mask->control[band].vht_mcs[i] != vht)
return false;
@@ -4513,6 +4687,9 @@
u8 fixed_nss = ar->num_rf_chains;
u8 force_sgi;
+ if (ar->cfg_tx_chainmask)
+ fixed_nss = get_nss_from_chainmask(ar->cfg_tx_chainmask);
+
force_sgi = mask->control[band].gi;
if (force_sgi == NL80211_TXRATE_FORCE_LGI)
return -EINVAL;
@@ -4671,12 +4848,15 @@
.tx_last_beacon = ath10k_tx_last_beacon,
.set_antenna = ath10k_set_antenna,
.get_antenna = ath10k_get_antenna,
- .restart_complete = ath10k_restart_complete,
+ .reconfig_complete = ath10k_reconfig_complete,
.get_survey = ath10k_get_survey,
.set_bitrate_mask = ath10k_set_bitrate_mask,
.sta_rc_update = ath10k_sta_rc_update,
.get_tsf = ath10k_get_tsf,
.ampdu_action = ath10k_ampdu_action,
+ .get_et_sset_count = ath10k_debug_get_et_sset_count,
+ .get_et_stats = ath10k_debug_get_et_stats,
+ .get_et_strings = ath10k_debug_get_et_strings,
CFG80211_TESTMODE_CMD(ath10k_tm_cmd)
@@ -5021,15 +5201,6 @@
BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_AP);
- if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
- /* TODO: Have to deal with 2x2 chips if/when the come out. */
- ar->supp_tx_chainmask = TARGET_10X_TX_CHAIN_MASK;
- ar->supp_rx_chainmask = TARGET_10X_RX_CHAIN_MASK;
- } else {
- ar->supp_tx_chainmask = TARGET_TX_CHAIN_MASK;
- ar->supp_rx_chainmask = TARGET_RX_CHAIN_MASK;
- }
-
ar->hw->wiphy->available_antennas_rx = ar->supp_rx_chainmask;
ar->hw->wiphy->available_antennas_tx = ar->supp_tx_chainmask;
@@ -5048,10 +5219,6 @@
IEEE80211_HW_AP_LINK_PS |
IEEE80211_HW_SPECTRUM_MGMT;
- /* MSDU can have HTT TX fragment pushed in front. The additional 4
- * bytes is used for padding/alignment if necessary. */
- ar->hw->extra_tx_headroom += sizeof(struct htt_data_tx_desc_frag)*2 + 4;
-
ar->hw->wiphy->features |= NL80211_FEATURE_STATIC_SMPS;
if (ar->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS)
@@ -5075,6 +5242,8 @@
ar->hw->wiphy->max_remain_on_channel_duration = 5000;
ar->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
+ ar->hw->wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE;
+
/*
* on LL hardware queues are managed entirely by the FW
* so we only advertise to mac we can do the queues thing
diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h
index 6c80eea..6829611 100644
--- a/drivers/net/wireless/ath/ath10k/mac.h
+++ b/drivers/net/wireless/ath/ath10k/mac.h
@@ -21,6 +21,8 @@
#include <net/mac80211.h>
#include "core.h"
+#define WEP_KEYID_SHIFT 6
+
struct ath10k_generic_iter {
struct ath10k *ar;
int ret;
@@ -39,6 +41,10 @@
void ath10k_mgmt_over_wmi_tx_purge(struct ath10k *ar);
void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work);
void ath10k_halt(struct ath10k *ar);
+void ath10k_mac_vif_beacon_free(struct ath10k_vif *arvif);
+void ath10k_drain_tx(struct ath10k *ar);
+bool ath10k_mac_is_peer_wep_key_set(struct ath10k *ar, const u8 *addr,
+ u8 keyidx);
static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif)
{
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index 0d70674..7269de6 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -490,6 +490,8 @@
void *data_buf = NULL;
int i;
+ spin_lock_bh(&ar_pci->ce_lock);
+
ce_diag = ar_pci->ce_diag;
/*
@@ -516,7 +518,7 @@
nbytes = min_t(unsigned int, remaining_bytes,
DIAG_TRANSFER_LIMIT);
- ret = ath10k_ce_rx_post_buf(ce_diag, NULL, ce_data);
+ ret = __ath10k_ce_rx_post_buf(ce_diag, NULL, ce_data);
if (ret != 0)
goto done;
@@ -532,15 +534,15 @@
address = TARG_CPU_SPACE_TO_CE_SPACE(ar, ar_pci->mem,
address);
- ret = ath10k_ce_send(ce_diag, NULL, (u32)address, nbytes, 0,
- 0);
+ ret = ath10k_ce_send_nolock(ce_diag, NULL, (u32)address, nbytes, 0,
+ 0);
if (ret)
goto done;
i = 0;
- while (ath10k_ce_completed_send_next(ce_diag, NULL, &buf,
- &completed_nbytes,
- &id) != 0) {
+ while (ath10k_ce_completed_send_next_nolock(ce_diag, NULL, &buf,
+ &completed_nbytes,
+ &id) != 0) {
mdelay(1);
if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
ret = -EBUSY;
@@ -559,9 +561,9 @@
}
i = 0;
- while (ath10k_ce_completed_recv_next(ce_diag, NULL, &buf,
- &completed_nbytes,
- &id, &flags) != 0) {
+ while (ath10k_ce_completed_recv_next_nolock(ce_diag, NULL, &buf,
+ &completed_nbytes,
+ &id, &flags) != 0) {
mdelay(1);
if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
@@ -596,6 +598,8 @@
dma_free_coherent(ar->dev, orig_nbytes, data_buf,
ce_data_base);
+ spin_unlock_bh(&ar_pci->ce_lock);
+
return ret;
}
@@ -653,6 +657,8 @@
dma_addr_t ce_data_base = 0;
int i;
+ spin_lock_bh(&ar_pci->ce_lock);
+
ce_diag = ar_pci->ce_diag;
/*
@@ -693,7 +699,7 @@
nbytes = min_t(int, remaining_bytes, DIAG_TRANSFER_LIMIT);
/* Set up to receive directly into Target(!) address */
- ret = ath10k_ce_rx_post_buf(ce_diag, NULL, address);
+ ret = __ath10k_ce_rx_post_buf(ce_diag, NULL, address);
if (ret != 0)
goto done;
@@ -701,15 +707,15 @@
* Request CE to send caller-supplied data that
* was copied to bounce buffer to Target(!) address.
*/
- ret = ath10k_ce_send(ce_diag, NULL, (u32)ce_data,
- nbytes, 0, 0);
+ ret = ath10k_ce_send_nolock(ce_diag, NULL, (u32)ce_data,
+ nbytes, 0, 0);
if (ret != 0)
goto done;
i = 0;
- while (ath10k_ce_completed_send_next(ce_diag, NULL, &buf,
- &completed_nbytes,
- &id) != 0) {
+ while (ath10k_ce_completed_send_next_nolock(ce_diag, NULL, &buf,
+ &completed_nbytes,
+ &id) != 0) {
mdelay(1);
if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
@@ -729,9 +735,9 @@
}
i = 0;
- while (ath10k_ce_completed_recv_next(ce_diag, NULL, &buf,
- &completed_nbytes,
- &id, &flags) != 0) {
+ while (ath10k_ce_completed_recv_next_nolock(ce_diag, NULL, &buf,
+ &completed_nbytes,
+ &id, &flags) != 0) {
mdelay(1);
if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
@@ -765,6 +771,8 @@
ath10k_warn(ar, "failed to write diag value at 0x%x: %d\n",
address, ret);
+ spin_unlock_bh(&ar_pci->ce_lock);
+
return ret;
}
@@ -820,20 +828,24 @@
struct ath10k *ar = ce_state->ar;
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
struct ath10k_hif_cb *cb = &ar_pci->msg_callbacks_current;
- void *transfer_context;
+ struct sk_buff_head list;
+ struct sk_buff *skb;
u32 ce_data;
unsigned int nbytes;
unsigned int transfer_id;
- while (ath10k_ce_completed_send_next(ce_state, &transfer_context,
- &ce_data, &nbytes,
- &transfer_id) == 0) {
+ __skb_queue_head_init(&list);
+ while (ath10k_ce_completed_send_next(ce_state, (void **)&skb, &ce_data,
+ &nbytes, &transfer_id) == 0) {
/* no need to call tx completion for NULL pointers */
- if (transfer_context == NULL)
+ if (skb == NULL)
continue;
- cb->tx_completion(ar, transfer_context, transfer_id);
+ __skb_queue_tail(&list, skb);
}
+
+ while ((skb = __skb_dequeue(&list)))
+ cb->tx_completion(ar, skb);
}
/* Called by lower (CE) layer when data is received from the Target. */
@@ -844,12 +856,14 @@
struct ath10k_pci_pipe *pipe_info = &ar_pci->pipe_info[ce_state->id];
struct ath10k_hif_cb *cb = &ar_pci->msg_callbacks_current;
struct sk_buff *skb;
+ struct sk_buff_head list;
void *transfer_context;
u32 ce_data;
unsigned int nbytes, max_nbytes;
unsigned int transfer_id;
unsigned int flags;
+ __skb_queue_head_init(&list);
while (ath10k_ce_completed_recv_next(ce_state, &transfer_context,
&ce_data, &nbytes, &transfer_id,
&flags) == 0) {
@@ -866,7 +880,16 @@
}
skb_put(skb, nbytes);
- cb->rx_completion(ar, skb, pipe_info->pipe_num);
+ __skb_queue_tail(&list, skb);
+ }
+
+ while ((skb = __skb_dequeue(&list))) {
+ ath10k_dbg(ar, ATH10K_DBG_PCI, "pci rx ce pipe %d len %d\n",
+ ce_state->id, skb->len);
+ ath10k_dbg_dump(ar, ATH10K_DBG_PCI_DUMP, NULL, "pci rx: ",
+ skb->data, skb->len);
+
+ cb->rx_completion(ar, skb);
}
ath10k_pci_rx_post_pipe(pipe_info);
@@ -941,6 +964,12 @@
return err;
}
+static int ath10k_pci_hif_diag_read(struct ath10k *ar, u32 address, void *buf,
+ size_t buf_len)
+{
+ return ath10k_pci_diag_read_mem(ar, address, buf, buf_len);
+}
+
static u16 ath10k_pci_hif_get_free_queue_number(struct ath10k *ar, u8 pipe)
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
@@ -991,6 +1020,8 @@
spin_lock_bh(&ar->data_lock);
+ ar->stats.fw_crash_counter++;
+
crash_data = ath10k_debug_get_new_fw_crash_data(ar);
if (crash_data)
@@ -1129,15 +1160,38 @@
&dl_is_polled);
}
+static void ath10k_pci_irq_msi_fw_mask(struct ath10k *ar)
+{
+ u32 val;
+
+ val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + CORE_CTRL_ADDRESS);
+ val &= ~CORE_CTRL_PCIE_REG_31_MASK;
+
+ ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + CORE_CTRL_ADDRESS, val);
+}
+
+static void ath10k_pci_irq_msi_fw_unmask(struct ath10k *ar)
+{
+ u32 val;
+
+ val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + CORE_CTRL_ADDRESS);
+ val |= CORE_CTRL_PCIE_REG_31_MASK;
+
+ ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + CORE_CTRL_ADDRESS, val);
+}
+
static void ath10k_pci_irq_disable(struct ath10k *ar)
{
+ ath10k_ce_disable_interrupts(ar);
+ ath10k_pci_disable_and_clear_legacy_irq(ar);
+ ath10k_pci_irq_msi_fw_mask(ar);
+}
+
+static void ath10k_pci_irq_sync(struct ath10k *ar)
+{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
int i;
- ath10k_ce_disable_interrupts(ar);
- ath10k_pci_disable_and_clear_legacy_irq(ar);
- /* FIXME: How to mask all MSI interrupts? */
-
for (i = 0; i < max(1, ar_pci->num_msi_intrs); i++)
synchronize_irq(ar_pci->pdev->irq + i);
}
@@ -1146,7 +1200,7 @@
{
ath10k_ce_enable_interrupts(ar);
ath10k_pci_enable_legacy_irq(ar);
- /* FIXME: How to unmask all MSI interrupts? */
+ ath10k_pci_irq_msi_fw_unmask(ar);
}
static int ath10k_pci_hif_start(struct ath10k *ar)
@@ -1159,64 +1213,74 @@
return 0;
}
-static void ath10k_pci_rx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info)
+static void ath10k_pci_rx_pipe_cleanup(struct ath10k_pci_pipe *pci_pipe)
{
struct ath10k *ar;
- struct ath10k_pci *ar_pci;
- struct ath10k_ce_pipe *ce_hdl;
- u32 buf_sz;
- struct sk_buff *netbuf;
- u32 ce_data;
+ struct ath10k_ce_pipe *ce_pipe;
+ struct ath10k_ce_ring *ce_ring;
+ struct sk_buff *skb;
+ int i;
- buf_sz = pipe_info->buf_sz;
+ ar = pci_pipe->hif_ce_state;
+ ce_pipe = pci_pipe->ce_hdl;
+ ce_ring = ce_pipe->dest_ring;
- /* Unused Copy Engine */
- if (buf_sz == 0)
+ if (!ce_ring)
return;
- ar = pipe_info->hif_ce_state;
- ar_pci = ath10k_pci_priv(ar);
- ce_hdl = pipe_info->ce_hdl;
+ if (!pci_pipe->buf_sz)
+ return;
- while (ath10k_ce_revoke_recv_next(ce_hdl, (void **)&netbuf,
- &ce_data) == 0) {
- dma_unmap_single(ar->dev, ATH10K_SKB_CB(netbuf)->paddr,
- netbuf->len + skb_tailroom(netbuf),
+ for (i = 0; i < ce_ring->nentries; i++) {
+ skb = ce_ring->per_transfer_context[i];
+ if (!skb)
+ continue;
+
+ ce_ring->per_transfer_context[i] = NULL;
+
+ dma_unmap_single(ar->dev, ATH10K_SKB_CB(skb)->paddr,
+ skb->len + skb_tailroom(skb),
DMA_FROM_DEVICE);
- dev_kfree_skb_any(netbuf);
+ dev_kfree_skb_any(skb);
}
}
-static void ath10k_pci_tx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info)
+static void ath10k_pci_tx_pipe_cleanup(struct ath10k_pci_pipe *pci_pipe)
{
struct ath10k *ar;
struct ath10k_pci *ar_pci;
- struct ath10k_ce_pipe *ce_hdl;
- struct sk_buff *netbuf;
- u32 ce_data;
- unsigned int nbytes;
+ struct ath10k_ce_pipe *ce_pipe;
+ struct ath10k_ce_ring *ce_ring;
+ struct ce_desc *ce_desc;
+ struct sk_buff *skb;
unsigned int id;
- u32 buf_sz;
+ int i;
- buf_sz = pipe_info->buf_sz;
+ ar = pci_pipe->hif_ce_state;
+ ar_pci = ath10k_pci_priv(ar);
+ ce_pipe = pci_pipe->ce_hdl;
+ ce_ring = ce_pipe->src_ring;
- /* Unused Copy Engine */
- if (buf_sz == 0)
+ if (!ce_ring)
return;
- ar = pipe_info->hif_ce_state;
- ar_pci = ath10k_pci_priv(ar);
- ce_hdl = pipe_info->ce_hdl;
+ if (!pci_pipe->buf_sz)
+ return;
- while (ath10k_ce_cancel_send_next(ce_hdl, (void **)&netbuf,
- &ce_data, &nbytes, &id) == 0) {
- /* no need to call tx completion for NULL pointers */
- if (!netbuf)
+ ce_desc = ce_ring->shadow_base;
+ if (WARN_ON(!ce_desc))
+ return;
+
+ for (i = 0; i < ce_ring->nentries; i++) {
+ skb = ce_ring->per_transfer_context[i];
+ if (!skb)
continue;
- ar_pci->msg_callbacks_current.tx_completion(ar,
- netbuf,
- id);
+ ce_ring->per_transfer_context[i] = NULL;
+ id = MS(__le16_to_cpu(ce_desc[i].flags),
+ CE_DESC_FLAGS_META_DATA);
+
+ ar_pci->msg_callbacks_current.tx_completion(ar, skb);
}
}
@@ -1274,6 +1338,7 @@
ath10k_pci_warm_reset(ar);
ath10k_pci_irq_disable(ar);
+ ath10k_pci_irq_sync(ar);
ath10k_pci_flush(ar);
}
@@ -1394,6 +1459,9 @@
&nbytes, &transfer_id, &flags))
return;
+ if (WARN_ON_ONCE(!xfer))
+ return;
+
if (!xfer->wait_for_resp) {
ath10k_warn(ar, "unexpected: BMI data received; ignoring\n");
return;
@@ -1577,23 +1645,40 @@
return 0;
}
-static int ath10k_pci_alloc_ce(struct ath10k *ar)
+static int ath10k_pci_alloc_pipes(struct ath10k *ar)
{
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+ struct ath10k_pci_pipe *pipe;
int i, ret;
for (i = 0; i < CE_COUNT; i++) {
- ret = ath10k_ce_alloc_pipe(ar, i, &host_ce_config_wlan[i]);
+ pipe = &ar_pci->pipe_info[i];
+ pipe->ce_hdl = &ar_pci->ce_states[i];
+ pipe->pipe_num = i;
+ pipe->hif_ce_state = ar;
+
+ ret = ath10k_ce_alloc_pipe(ar, i, &host_ce_config_wlan[i],
+ ath10k_pci_ce_send_done,
+ ath10k_pci_ce_recv_data);
if (ret) {
ath10k_err(ar, "failed to allocate copy engine pipe %d: %d\n",
i, ret);
return ret;
}
+
+ /* Last CE is Diagnostic Window */
+ if (i == CE_COUNT - 1) {
+ ar_pci->ce_diag = pipe->ce_hdl;
+ continue;
+ }
+
+ pipe->buf_sz = (size_t)(host_ce_config_wlan[i].src_sz_max);
}
return 0;
}
-static void ath10k_pci_free_ce(struct ath10k *ar)
+static void ath10k_pci_free_pipes(struct ath10k *ar)
{
int i;
@@ -1601,39 +1686,17 @@
ath10k_ce_free_pipe(ar, i);
}
-static int ath10k_pci_ce_init(struct ath10k *ar)
+static int ath10k_pci_init_pipes(struct ath10k *ar)
{
- struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
- struct ath10k_pci_pipe *pipe_info;
- const struct ce_attr *attr;
- int pipe_num, ret;
+ int i, ret;
- for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) {
- pipe_info = &ar_pci->pipe_info[pipe_num];
- pipe_info->ce_hdl = &ar_pci->ce_states[pipe_num];
- pipe_info->pipe_num = pipe_num;
- pipe_info->hif_ce_state = ar;
- attr = &host_ce_config_wlan[pipe_num];
-
- ret = ath10k_ce_init_pipe(ar, pipe_num, attr,
- ath10k_pci_ce_send_done,
- ath10k_pci_ce_recv_data);
+ for (i = 0; i < CE_COUNT; i++) {
+ ret = ath10k_ce_init_pipe(ar, i, &host_ce_config_wlan[i]);
if (ret) {
ath10k_err(ar, "failed to initialize copy engine pipe %d: %d\n",
- pipe_num, ret);
+ i, ret);
return ret;
}
-
- if (pipe_num == CE_COUNT - 1) {
- /*
- * Reserve the ultimate CE for
- * diagnostic Window support
- */
- ar_pci->ce_diag = pipe_info->ce_hdl;
- continue;
- }
-
- pipe_info->buf_sz = (size_t)(attr->src_sz_max);
}
return 0;
@@ -1674,93 +1737,167 @@
msleep(10);
}
-static int ath10k_pci_warm_reset(struct ath10k *ar)
+static void ath10k_pci_warm_reset_cpu(struct ath10k *ar)
{
u32 val;
- ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot warm reset\n");
-
- /* debug */
- val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
- PCIE_INTR_CAUSE_ADDRESS);
- ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot host cpu intr cause: 0x%08x\n",
- val);
-
- val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
- CPU_INTR_ADDRESS);
- ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot target cpu intr cause: 0x%08x\n",
- val);
-
- /* disable pending irqs */
- ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS +
- PCIE_INTR_ENABLE_ADDRESS, 0);
-
- ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS +
- PCIE_INTR_CLR_ADDRESS, ~0);
-
- msleep(100);
-
- /* clear fw indicator */
ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS, 0);
- /* clear target LF timer interrupts */
+ val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
+ SOC_RESET_CONTROL_ADDRESS);
+ ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS,
+ val | SOC_RESET_CONTROL_CPU_WARM_RST_MASK);
+}
+
+static void ath10k_pci_warm_reset_ce(struct ath10k *ar)
+{
+ u32 val;
+
+ val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
+ SOC_RESET_CONTROL_ADDRESS);
+
+ ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS,
+ val | SOC_RESET_CONTROL_CE_RST_MASK);
+ msleep(10);
+ ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS,
+ val & ~SOC_RESET_CONTROL_CE_RST_MASK);
+}
+
+static void ath10k_pci_warm_reset_clear_lf(struct ath10k *ar)
+{
+ u32 val;
+
val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
SOC_LF_TIMER_CONTROL0_ADDRESS);
ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS +
SOC_LF_TIMER_CONTROL0_ADDRESS,
val & ~SOC_LF_TIMER_CONTROL0_ENABLE_MASK);
+}
- /* reset CE */
- val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
- SOC_RESET_CONTROL_ADDRESS);
- ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS,
- val | SOC_RESET_CONTROL_CE_RST_MASK);
- val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
- SOC_RESET_CONTROL_ADDRESS);
- msleep(10);
+static int ath10k_pci_warm_reset(struct ath10k *ar)
+{
+ int ret;
- /* unreset CE */
- ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS,
- val & ~SOC_RESET_CONTROL_CE_RST_MASK);
- val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
- SOC_RESET_CONTROL_ADDRESS);
- msleep(10);
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot warm reset\n");
+ spin_lock_bh(&ar->data_lock);
+ ar->stats.fw_warm_reset_counter++;
+ spin_unlock_bh(&ar->data_lock);
+
+ ath10k_pci_irq_disable(ar);
+
+ /* Make sure the target CPU is not doing anything dangerous, e.g. if it
+ * were to access copy engine while host performs copy engine reset
+ * then it is possible for the device to confuse pci-e controller to
+ * the point of bringing host system to a complete stop (i.e. hang).
+ */
ath10k_pci_warm_reset_si0(ar);
+ ath10k_pci_warm_reset_cpu(ar);
+ ath10k_pci_init_pipes(ar);
+ ath10k_pci_wait_for_target_init(ar);
- /* debug */
- val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
- PCIE_INTR_CAUSE_ADDRESS);
- ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot host cpu intr cause: 0x%08x\n",
- val);
+ ath10k_pci_warm_reset_clear_lf(ar);
+ ath10k_pci_warm_reset_ce(ar);
+ ath10k_pci_warm_reset_cpu(ar);
+ ath10k_pci_init_pipes(ar);
- val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
- CPU_INTR_ADDRESS);
- ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot target cpu intr cause: 0x%08x\n",
- val);
-
- /* CPU warm reset */
- val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
- SOC_RESET_CONTROL_ADDRESS);
- ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS,
- val | SOC_RESET_CONTROL_CPU_WARM_RST_MASK);
-
- val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
- SOC_RESET_CONTROL_ADDRESS);
- ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot target reset state: 0x%08x\n",
- val);
-
- msleep(100);
+ ret = ath10k_pci_wait_for_target_init(ar);
+ if (ret) {
+ ath10k_warn(ar, "failed to wait for target init: %d\n", ret);
+ return ret;
+ }
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot warm reset complete\n");
return 0;
}
-static int __ath10k_pci_hif_power_up(struct ath10k *ar, bool cold_reset)
+static int ath10k_pci_chip_reset(struct ath10k *ar)
+{
+ int i, ret;
+ u32 val;
+
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot chip reset\n");
+
+ /* Some hardware revisions (e.g. CUS223v2) has issues with cold reset.
+ * It is thus preferred to use warm reset which is safer but may not be
+ * able to recover the device from all possible fail scenarios.
+ *
+ * Warm reset doesn't always work on first try so attempt it a few
+ * times before giving up.
+ */
+ for (i = 0; i < ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS; i++) {
+ ret = ath10k_pci_warm_reset(ar);
+ if (ret) {
+ ath10k_warn(ar, "failed to warm reset attempt %d of %d: %d\n",
+ i + 1, ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS,
+ ret);
+ continue;
+ }
+
+ /* FIXME: Sometimes copy engine doesn't recover after warm
+ * reset. In most cases this needs cold reset. In some of these
+ * cases the device is in such a state that a cold reset may
+ * lock up the host.
+ *
+ * Reading any host interest register via copy engine is
+ * sufficient to verify if device is capable of booting
+ * firmware blob.
+ */
+ ret = ath10k_pci_init_pipes(ar);
+ if (ret) {
+ ath10k_warn(ar, "failed to init copy engine: %d\n",
+ ret);
+ continue;
+ }
+
+ ret = ath10k_pci_diag_read32(ar, QCA988X_HOST_INTEREST_ADDRESS,
+ &val);
+ if (ret) {
+ ath10k_warn(ar, "failed to poke copy engine: %d\n",
+ ret);
+ continue;
+ }
+
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot chip reset complete (warm)\n");
+ return 0;
+ }
+
+ if (ath10k_pci_reset_mode == ATH10K_PCI_RESET_WARM_ONLY) {
+ ath10k_warn(ar, "refusing cold reset as requested\n");
+ return -EPERM;
+ }
+
+ ret = ath10k_pci_cold_reset(ar);
+ if (ret) {
+ ath10k_warn(ar, "failed to cold reset: %d\n", ret);
+ return ret;
+ }
+
+ ret = ath10k_pci_wait_for_target_init(ar);
+ if (ret) {
+ ath10k_warn(ar, "failed to wait for target after cold reset: %d\n",
+ ret);
+ return ret;
+ }
+
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot chip reset complete (cold)\n");
+
+ return 0;
+}
+
+static int ath10k_pci_hif_power_up(struct ath10k *ar)
{
int ret;
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif power up\n");
+
+ ret = ath10k_pci_wake(ar);
+ if (ret) {
+ ath10k_err(ar, "failed to wake up target: %d\n", ret);
+ return ret;
+ }
+
/*
* Bring the target up cleanly.
*
@@ -1771,26 +1908,16 @@
* is in an unexpected state. We try to catch that here in order to
* reset the Target and retry the probe.
*/
- if (cold_reset)
- ret = ath10k_pci_cold_reset(ar);
- else
- ret = ath10k_pci_warm_reset(ar);
-
+ ret = ath10k_pci_chip_reset(ar);
if (ret) {
- ath10k_err(ar, "failed to reset target: %d\n", ret);
- goto err;
+ ath10k_err(ar, "failed to reset chip: %d\n", ret);
+ goto err_sleep;
}
- ret = ath10k_pci_ce_init(ar);
+ ret = ath10k_pci_init_pipes(ar);
if (ret) {
ath10k_err(ar, "failed to initialize CE: %d\n", ret);
- goto err;
- }
-
- ret = ath10k_pci_wait_for_target_init(ar);
- if (ret) {
- ath10k_err(ar, "failed to wait for target to init: %d\n", ret);
- goto err_ce;
+ goto err_sleep;
}
ret = ath10k_pci_init_config(ar);
@@ -1809,73 +1936,21 @@
err_ce:
ath10k_pci_ce_deinit(ar);
- ath10k_pci_warm_reset(ar);
-err:
+
+err_sleep:
+ ath10k_pci_sleep(ar);
return ret;
}
-static int ath10k_pci_hif_power_up_warm(struct ath10k *ar)
-{
- int i, ret;
-
- /*
- * Sometime warm reset succeeds after retries.
- *
- * FIXME: It might be possible to tune ath10k_pci_warm_reset() to work
- * at first try.
- */
- for (i = 0; i < ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS; i++) {
- ret = __ath10k_pci_hif_power_up(ar, false);
- if (ret == 0)
- break;
-
- ath10k_warn(ar, "failed to warm reset (attempt %d out of %d): %d\n",
- i + 1, ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS, ret);
- }
-
- return ret;
-}
-
-static int ath10k_pci_hif_power_up(struct ath10k *ar)
-{
- int ret;
-
- ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif power up\n");
-
- /*
- * Hardware CUS232 version 2 has some issues with cold reset and the
- * preferred (and safer) way to perform a device reset is through a
- * warm reset.
- *
- * Warm reset doesn't always work though so fall back to cold reset may
- * be necessary.
- */
- ret = ath10k_pci_hif_power_up_warm(ar);
- if (ret) {
- ath10k_warn(ar, "failed to power up target using warm reset: %d\n",
- ret);
-
- if (ath10k_pci_reset_mode == ATH10K_PCI_RESET_WARM_ONLY)
- return ret;
-
- ath10k_warn(ar, "trying cold reset\n");
-
- ret = __ath10k_pci_hif_power_up(ar, true);
- if (ret) {
- ath10k_err(ar, "failed to power up target using cold reset too (%d)\n",
- ret);
- return ret;
- }
- }
-
- return 0;
-}
-
static void ath10k_pci_hif_power_down(struct ath10k *ar)
{
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif power down\n");
- ath10k_pci_warm_reset(ar);
+ /* Currently hif_power_up performs effectively a reset and hif_stop
+ * resets the chip as well so there's no point in resetting here.
+ */
+
+ ath10k_pci_sleep(ar);
}
#ifdef CONFIG_PM
@@ -1929,6 +2004,8 @@
static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
.tx_sg = ath10k_pci_hif_tx_sg,
+ .diag_read = ath10k_pci_hif_diag_read,
+ .diag_write = ath10k_pci_diag_write_mem,
.exchange_bmi_msg = ath10k_pci_hif_exchange_bmi_msg,
.start = ath10k_pci_hif_start,
.stop = ath10k_pci_hif_stop,
@@ -1939,6 +2016,8 @@
.get_free_queue_number = ath10k_pci_hif_get_free_queue_number,
.power_up = ath10k_pci_hif_power_up,
.power_down = ath10k_pci_hif_power_down,
+ .read32 = ath10k_pci_read32,
+ .write32 = ath10k_pci_write32,
#ifdef CONFIG_PM
.suspend = ath10k_pci_hif_suspend,
.resume = ath10k_pci_hif_resume,
@@ -2258,14 +2337,14 @@
if (ar_pci->num_msi_intrs == 0)
/* Fix potential race by repeating CORE_BASE writes */
- ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS +
- PCIE_INTR_ENABLE_ADDRESS,
- PCIE_INTR_FIRMWARE_MASK |
- PCIE_INTR_CE_MASK_ALL);
+ ath10k_pci_enable_legacy_irq(ar);
mdelay(10);
} while (time_before(jiffies, timeout));
+ ath10k_pci_disable_and_clear_legacy_irq(ar);
+ ath10k_pci_irq_msi_fw_mask(ar);
+
if (val == 0xffffffff) {
ath10k_err(ar, "failed to read device register, device is gone\n");
return -EIO;
@@ -2295,6 +2374,12 @@
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot cold reset\n");
+ spin_lock_bh(&ar->data_lock);
+
+ ar->stats.fw_cold_reset_counter++;
+
+ spin_unlock_bh(&ar->data_lock);
+
/* Put Target, including PCIe, into RESET. */
val = ath10k_pci_reg_read32(ar, SOC_GLOBAL_RESET_ADDRESS);
val |= 1;
@@ -2408,6 +2493,7 @@
u32 chip_id;
ar = ath10k_core_create(sizeof(*ar_pci), &pdev->dev,
+ ATH10K_BUS_PCI,
&ath10k_pci_hif_ops);
if (!ar) {
dev_err(&pdev->dev, "failed to allocate core\n");
@@ -2443,7 +2529,7 @@
goto err_sleep;
}
- ret = ath10k_pci_alloc_ce(ar);
+ ret = ath10k_pci_alloc_pipes(ar);
if (ret) {
ath10k_err(ar, "failed to allocate copy engine pipes: %d\n",
ret);
@@ -2451,25 +2537,12 @@
}
ath10k_pci_ce_deinit(ar);
-
- ret = ath10k_ce_disable_interrupts(ar);
- if (ret) {
- ath10k_err(ar, "failed to disable copy engine interrupts: %d\n",
- ret);
- goto err_free_ce;
- }
-
- /* Workaround: There's no known way to mask all possible interrupts via
- * device CSR. The only way to make sure device doesn't assert
- * interrupts is to reset it. Interrupts are then disabled on host
- * after handlers are registered.
- */
- ath10k_pci_warm_reset(ar);
+ ath10k_pci_irq_disable(ar);
ret = ath10k_pci_init_irq(ar);
if (ret) {
ath10k_err(ar, "failed to init irqs: %d\n", ret);
- goto err_free_ce;
+ goto err_free_pipes;
}
ath10k_info(ar, "pci irq %s interrupts %d irq_mode %d reset_mode %d\n",
@@ -2482,8 +2555,7 @@
goto err_deinit_irq;
}
- /* This shouldn't race as the device has been reset above. */
- ath10k_pci_irq_disable(ar);
+ ath10k_pci_sleep(ar);
ret = ath10k_core_register(ar, chip_id);
if (ret) {
@@ -2500,8 +2572,8 @@
err_deinit_irq:
ath10k_pci_deinit_irq(ar);
-err_free_ce:
- ath10k_pci_free_ce(ar);
+err_free_pipes:
+ ath10k_pci_free_pipes(ar);
err_sleep:
ath10k_pci_sleep(ar);
@@ -2535,8 +2607,7 @@
ath10k_pci_kill_tasklet(ar);
ath10k_pci_deinit_irq(ar);
ath10k_pci_ce_deinit(ar);
- ath10k_pci_free_ce(ar);
- ath10k_pci_sleep(ar);
+ ath10k_pci_free_pipes(ar);
ath10k_pci_release(ar);
ath10k_core_destroy(ar);
}
@@ -2573,5 +2644,7 @@
MODULE_AUTHOR("Qualcomm Atheros");
MODULE_DESCRIPTION("Driver support for Atheros QCA988X PCIe devices");
MODULE_LICENSE("Dual BSD/GPL");
-MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_FW_3_FILE);
+MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_FW_FILE);
+MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" ATH10K_FW_API2_FILE);
+MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" ATH10K_FW_API3_FILE);
MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_BOARD_DATA_FILE);
diff --git a/drivers/net/wireless/ath/ath10k/spectral.c b/drivers/net/wireless/ath/ath10k/spectral.c
index f45b90f..127c8df 100644
--- a/drivers/net/wireless/ath/ath10k/spectral.c
+++ b/drivers/net/wireless/ath/ath10k/spectral.c
@@ -56,14 +56,14 @@
}
int ath10k_spectral_process_fft(struct ath10k *ar,
- struct wmi_single_phyerr_rx_event *event,
- struct phyerr_fft_report *fftr,
+ const struct wmi_phyerr *phyerr,
+ const struct phyerr_fft_report *fftr,
size_t bin_len, u64 tsf)
{
struct fft_sample_ath10k *fft_sample;
u8 buf[sizeof(*fft_sample) + SPECTRAL_ATH10K_MAX_NUM_BINS];
u16 freq1, freq2, total_gain_db, base_pwr_db, length, peak_mag;
- u32 reg0, reg1, nf_list1, nf_list2;
+ u32 reg0, reg1;
u8 chain_idx, *bins;
int dc_pos;
@@ -82,7 +82,7 @@
/* TODO: there might be a reason why the hardware reports 20/40/80 MHz,
* but the results/plots suggest that its actually 22/44/88 MHz.
*/
- switch (event->hdr.chan_width_mhz) {
+ switch (phyerr->chan_width_mhz) {
case 20:
fft_sample->chan_width_mhz = 22;
break;
@@ -101,7 +101,7 @@
fft_sample->chan_width_mhz = 88;
break;
default:
- fft_sample->chan_width_mhz = event->hdr.chan_width_mhz;
+ fft_sample->chan_width_mhz = phyerr->chan_width_mhz;
}
fft_sample->relpwr_db = MS(reg1, SEARCH_FFT_REPORT_REG1_RELPWR_DB);
@@ -110,36 +110,22 @@
peak_mag = MS(reg1, SEARCH_FFT_REPORT_REG1_PEAK_MAG);
fft_sample->max_magnitude = __cpu_to_be16(peak_mag);
fft_sample->max_index = MS(reg0, SEARCH_FFT_REPORT_REG0_PEAK_SIDX);
- fft_sample->rssi = event->hdr.rssi_combined;
+ fft_sample->rssi = phyerr->rssi_combined;
total_gain_db = MS(reg0, SEARCH_FFT_REPORT_REG0_TOTAL_GAIN_DB);
base_pwr_db = MS(reg0, SEARCH_FFT_REPORT_REG0_BASE_PWR_DB);
fft_sample->total_gain_db = __cpu_to_be16(total_gain_db);
fft_sample->base_pwr_db = __cpu_to_be16(base_pwr_db);
- freq1 = __le16_to_cpu(event->hdr.freq1);
- freq2 = __le16_to_cpu(event->hdr.freq2);
+ freq1 = __le16_to_cpu(phyerr->freq1);
+ freq2 = __le16_to_cpu(phyerr->freq2);
fft_sample->freq1 = __cpu_to_be16(freq1);
fft_sample->freq2 = __cpu_to_be16(freq2);
- nf_list1 = __le32_to_cpu(event->hdr.nf_list_1);
- nf_list2 = __le32_to_cpu(event->hdr.nf_list_2);
chain_idx = MS(reg0, SEARCH_FFT_REPORT_REG0_FFT_CHN_IDX);
- switch (chain_idx) {
- case 0:
- fft_sample->noise = __cpu_to_be16(nf_list1 & 0xffffu);
- break;
- case 1:
- fft_sample->noise = __cpu_to_be16((nf_list1 >> 16) & 0xffffu);
- break;
- case 2:
- fft_sample->noise = __cpu_to_be16(nf_list2 & 0xffffu);
- break;
- case 3:
- fft_sample->noise = __cpu_to_be16((nf_list2 >> 16) & 0xffffu);
- break;
- }
+ fft_sample->noise = __cpu_to_be16(
+ __le16_to_cpu(phyerr->nf_chains[chain_idx]));
bins = (u8 *)fftr;
bins += sizeof(*fftr);
diff --git a/drivers/net/wireless/ath/ath10k/spectral.h b/drivers/net/wireless/ath/ath10k/spectral.h
index ee20232..918a1d3 100644
--- a/drivers/net/wireless/ath/ath10k/spectral.h
+++ b/drivers/net/wireless/ath/ath10k/spectral.h
@@ -47,8 +47,8 @@
#ifdef CPTCFG_ATH10K_DEBUGFS
int ath10k_spectral_process_fft(struct ath10k *ar,
- struct wmi_single_phyerr_rx_event *event,
- struct phyerr_fft_report *fftr,
+ const struct wmi_phyerr *phyerr,
+ const struct phyerr_fft_report *fftr,
size_t bin_len, u64 tsf);
int ath10k_spectral_start(struct ath10k *ar);
int ath10k_spectral_vif_stop(struct ath10k_vif *arvif);
@@ -59,8 +59,8 @@
static inline int
ath10k_spectral_process_fft(struct ath10k *ar,
- struct wmi_single_phyerr_rx_event *event,
- struct phyerr_fft_report *fftr,
+ const struct wmi_phyerr *phyerr,
+ const struct phyerr_fft_report *fftr,
size_t bin_len, u64 tsf)
{
return 0;
diff --git a/drivers/net/wireless/ath/ath10k/trace.h b/drivers/net/wireless/ath/ath10k/trace.h
index 31fe5cd..b3bba04 100644
--- a/drivers/net/wireless/ath/ath10k/trace.h
+++ b/drivers/net/wireless/ath/ath10k/trace.h
@@ -20,6 +20,15 @@
#include <linux/tracepoint.h>
#include "core.h"
+#if !defined(_TRACE_H_)
+static inline u32 ath10k_frm_hdr_len(const void *buf)
+{
+ const struct ieee80211_hdr *hdr = buf;
+
+ return ieee80211_hdrlen(hdr->frame_control);
+}
+#endif
+
#define _TRACE_H_
/* create empty functions when tracing is disabled */
@@ -138,7 +147,8 @@
);
TRACE_EVENT(ath10k_wmi_cmd,
- TP_PROTO(struct ath10k *ar, int id, void *buf, size_t buf_len, int ret),
+ TP_PROTO(struct ath10k *ar, int id, const void *buf, size_t buf_len,
+ int ret),
TP_ARGS(ar, id, buf, buf_len, ret),
@@ -171,7 +181,7 @@
);
TRACE_EVENT(ath10k_wmi_event,
- TP_PROTO(struct ath10k *ar, int id, void *buf, size_t buf_len),
+ TP_PROTO(struct ath10k *ar, int id, const void *buf, size_t buf_len),
TP_ARGS(ar, id, buf, buf_len),
@@ -201,7 +211,7 @@
);
TRACE_EVENT(ath10k_htt_stats,
- TP_PROTO(struct ath10k *ar, void *buf, size_t buf_len),
+ TP_PROTO(struct ath10k *ar, const void *buf, size_t buf_len),
TP_ARGS(ar, buf, buf_len),
@@ -228,7 +238,7 @@
);
TRACE_EVENT(ath10k_wmi_dbglog,
- TP_PROTO(struct ath10k *ar, void *buf, size_t buf_len),
+ TP_PROTO(struct ath10k *ar, const void *buf, size_t buf_len),
TP_ARGS(ar, buf, buf_len),
@@ -254,6 +264,195 @@
)
);
+TRACE_EVENT(ath10k_htt_pktlog,
+ TP_PROTO(struct ath10k *ar, const void *buf, u16 buf_len),
+
+ TP_ARGS(ar, buf, buf_len),
+
+ TP_STRUCT__entry(
+ __string(device, dev_name(ar->dev))
+ __string(driver, dev_driver_string(ar->dev))
+ __field(u16, buf_len)
+ __dynamic_array(u8, pktlog, buf_len)
+ ),
+
+ TP_fast_assign(
+ __assign_str(device, dev_name(ar->dev));
+ __assign_str(driver, dev_driver_string(ar->dev));
+ __entry->buf_len = buf_len;
+ memcpy(__get_dynamic_array(pktlog), buf, buf_len);
+ ),
+
+ TP_printk(
+ "%s %s size %hu",
+ __get_str(driver),
+ __get_str(device),
+ __entry->buf_len
+ )
+);
+
+TRACE_EVENT(ath10k_htt_tx,
+ TP_PROTO(struct ath10k *ar, u16 msdu_id, u16 msdu_len,
+ u8 vdev_id, u8 tid),
+
+ TP_ARGS(ar, msdu_id, msdu_len, vdev_id, tid),
+
+ TP_STRUCT__entry(
+ __string(device, dev_name(ar->dev))
+ __string(driver, dev_driver_string(ar->dev))
+ __field(u16, msdu_id)
+ __field(u16, msdu_len)
+ __field(u8, vdev_id)
+ __field(u8, tid)
+ ),
+
+ TP_fast_assign(
+ __assign_str(device, dev_name(ar->dev));
+ __assign_str(driver, dev_driver_string(ar->dev));
+ __entry->msdu_id = msdu_id;
+ __entry->msdu_len = msdu_len;
+ __entry->vdev_id = vdev_id;
+ __entry->tid = tid;
+ ),
+
+ TP_printk(
+ "%s %s msdu_id %d msdu_len %d vdev_id %d tid %d",
+ __get_str(driver),
+ __get_str(device),
+ __entry->msdu_id,
+ __entry->msdu_len,
+ __entry->vdev_id,
+ __entry->tid
+ )
+);
+
+TRACE_EVENT(ath10k_txrx_tx_unref,
+ TP_PROTO(struct ath10k *ar, u16 msdu_id),
+
+ TP_ARGS(ar, msdu_id),
+
+ TP_STRUCT__entry(
+ __string(device, dev_name(ar->dev))
+ __string(driver, dev_driver_string(ar->dev))
+ __field(u16, msdu_id)
+ ),
+
+ TP_fast_assign(
+ __assign_str(device, dev_name(ar->dev));
+ __assign_str(driver, dev_driver_string(ar->dev));
+ __entry->msdu_id = msdu_id;
+ ),
+
+ TP_printk(
+ "%s %s msdu_id %d",
+ __get_str(driver),
+ __get_str(device),
+ __entry->msdu_id
+ )
+);
+
+DECLARE_EVENT_CLASS(ath10k_hdr_event,
+ TP_PROTO(struct ath10k *ar, const void *data, size_t len),
+
+ TP_ARGS(ar, data, len),
+
+ TP_STRUCT__entry(
+ __string(device, dev_name(ar->dev))
+ __string(driver, dev_driver_string(ar->dev))
+ __field(size_t, len)
+ __dynamic_array(u8, data, ath10k_frm_hdr_len(data))
+ ),
+
+ TP_fast_assign(
+ __assign_str(device, dev_name(ar->dev));
+ __assign_str(driver, dev_driver_string(ar->dev));
+ __entry->len = ath10k_frm_hdr_len(data);
+ memcpy(__get_dynamic_array(data), data, __entry->len);
+ ),
+
+ TP_printk(
+ "%s %s len %zu\n",
+ __get_str(driver),
+ __get_str(device),
+ __entry->len
+ )
+);
+
+DECLARE_EVENT_CLASS(ath10k_payload_event,
+ TP_PROTO(struct ath10k *ar, const void *data, size_t len),
+
+ TP_ARGS(ar, data, len),
+
+ TP_STRUCT__entry(
+ __string(device, dev_name(ar->dev))
+ __string(driver, dev_driver_string(ar->dev))
+ __field(size_t, len)
+ __dynamic_array(u8, payload, (len - ath10k_frm_hdr_len(data)))
+ ),
+
+ TP_fast_assign(
+ __assign_str(device, dev_name(ar->dev));
+ __assign_str(driver, dev_driver_string(ar->dev));
+ __entry->len = len - ath10k_frm_hdr_len(data);
+ memcpy(__get_dynamic_array(payload),
+ data + ath10k_frm_hdr_len(data), __entry->len);
+ ),
+
+ TP_printk(
+ "%s %s len %zu\n",
+ __get_str(driver),
+ __get_str(device),
+ __entry->len
+ )
+);
+
+DEFINE_EVENT(ath10k_hdr_event, ath10k_tx_hdr,
+ TP_PROTO(struct ath10k *ar, const void *data, size_t len),
+ TP_ARGS(ar, data, len)
+);
+
+DEFINE_EVENT(ath10k_payload_event, ath10k_tx_payload,
+ TP_PROTO(struct ath10k *ar, const void *data, size_t len),
+ TP_ARGS(ar, data, len)
+);
+
+DEFINE_EVENT(ath10k_hdr_event, ath10k_rx_hdr,
+ TP_PROTO(struct ath10k *ar, const void *data, size_t len),
+ TP_ARGS(ar, data, len)
+);
+
+DEFINE_EVENT(ath10k_payload_event, ath10k_rx_payload,
+ TP_PROTO(struct ath10k *ar, const void *data, size_t len),
+ TP_ARGS(ar, data, len)
+);
+
+TRACE_EVENT(ath10k_htt_rx_desc,
+ TP_PROTO(struct ath10k *ar, const void *data, size_t len),
+
+ TP_ARGS(ar, data, len),
+
+ TP_STRUCT__entry(
+ __string(device, dev_name(ar->dev))
+ __string(driver, dev_driver_string(ar->dev))
+ __field(u16, len)
+ __dynamic_array(u8, rxdesc, len)
+ ),
+
+ TP_fast_assign(
+ __assign_str(device, dev_name(ar->dev));
+ __assign_str(driver, dev_driver_string(ar->dev));
+ __entry->len = len;
+ memcpy(__get_dynamic_array(rxdesc), data, len);
+ ),
+
+ TP_printk(
+ "%s %s rxdesc len %d",
+ __get_str(driver),
+ __get_str(device),
+ __entry->len
+ )
+);
+
#endif /* _TRACE_H_ || TRACE_HEADER_MULTI_READ*/
/* we don't want to use include/trace/events */
diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c
index 7c16b8b..2f774c6 100644
--- a/drivers/net/wireless/ath/ath10k/txrx.c
+++ b/drivers/net/wireless/ath/ath10k/txrx.c
@@ -84,6 +84,7 @@
info = IEEE80211_SKB_CB(msdu);
memset(&info->status, 0, sizeof(info->status));
+ trace_ath10k_txrx_tx_unref(ar, tx_done->msdu_id);
if (tx_done->discard) {
ieee80211_free_txskb(htt->ar->hw, msdu);
@@ -148,7 +149,8 @@
ATH10K_MAX_NUM_PEER_IDS);
spin_unlock_bh(&ar->data_lock);
- mapped == expect_mapped;
+ (mapped == expect_mapped ||
+ test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags));
}), 3*HZ);
if (ret <= 0)
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index fd39c22..a769d15 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -609,6 +609,40 @@
.gpio_output_cmdid = WMI_10_2_GPIO_OUTPUT_CMDID,
};
+static void
+ath10k_wmi_put_wmi_channel(struct wmi_channel *ch,
+ const struct wmi_channel_arg *arg)
+{
+ u32 flags = 0;
+
+ memset(ch, 0, sizeof(*ch));
+
+ if (arg->passive)
+ flags |= WMI_CHAN_FLAG_PASSIVE;
+ if (arg->allow_ibss)
+ flags |= WMI_CHAN_FLAG_ADHOC_ALLOWED;
+ if (arg->allow_ht)
+ flags |= WMI_CHAN_FLAG_ALLOW_HT;
+ if (arg->allow_vht)
+ flags |= WMI_CHAN_FLAG_ALLOW_VHT;
+ if (arg->ht40plus)
+ flags |= WMI_CHAN_FLAG_HT40_PLUS;
+ if (arg->chan_radar)
+ flags |= WMI_CHAN_FLAG_DFS;
+
+ ch->mhz = __cpu_to_le32(arg->freq);
+ ch->band_center_freq1 = __cpu_to_le32(arg->band_center_freq1);
+ ch->band_center_freq2 = 0;
+ ch->min_power = arg->min_power;
+ ch->max_power = arg->max_power;
+ ch->reg_power = arg->max_reg_power;
+ ch->antenna_max = arg->max_antenna_gain;
+
+ /* mode & flags share storage */
+ ch->mode = arg->mode;
+ ch->flags |= __cpu_to_le32(flags);
+}
+
int ath10k_wmi_wait_for_service_ready(struct ath10k *ar)
{
int ret;
@@ -747,6 +781,10 @@
ath10k_wmi_tx_beacons_nowait(ar);
ret = ath10k_wmi_cmd_send_nowait(ar, skb, cmd_id);
+
+ if (ret && test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
+ ret = -ESHUTDOWN;
+
(ret != -EAGAIN);
}), 3*HZ);
@@ -801,6 +839,8 @@
ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi mgmt tx skb %p len %d ftype %02x stype %02x\n",
wmi_skb, wmi_skb->len, fc & IEEE80211_FCTL_FTYPE,
fc & IEEE80211_FCTL_STYPE);
+ trace_ath10k_tx_hdr(ar, skb->data, skb->len);
+ trace_ath10k_tx_payload(ar, skb->data, skb->len);
/* Send the management frame buffer to the target */
ret = ath10k_wmi_cmd_send(ar, wmi_skb, ar->wmi.cmd->mgmt_tx_cmdid);
@@ -1070,13 +1110,46 @@
return rate_idx;
}
+/* If keys are configured, HW decrypts all frames
+ * with protected bit set. Mark such frames as decrypted.
+ */
+static void ath10k_wmi_handle_wep_reauth(struct ath10k *ar,
+ struct sk_buff *skb,
+ struct ieee80211_rx_status *status)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ unsigned int hdrlen;
+ bool peer_key;
+ u8 *addr, keyidx;
+
+ if (!ieee80211_is_auth(hdr->frame_control) ||
+ !ieee80211_has_protected(hdr->frame_control))
+ return;
+
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
+ if (skb->len < (hdrlen + IEEE80211_WEP_IV_LEN))
+ return;
+
+ keyidx = skb->data[hdrlen + (IEEE80211_WEP_IV_LEN - 1)] >> WEP_KEYID_SHIFT;
+ addr = ieee80211_get_SA(hdr);
+
+ spin_lock_bh(&ar->data_lock);
+ peer_key = ath10k_mac_is_peer_wep_key_set(ar, addr, keyidx);
+ spin_unlock_bh(&ar->data_lock);
+
+ if (peer_key) {
+ ath10k_dbg(ar, ATH10K_DBG_MAC,
+ "mac wep key present for peer %pM\n", addr);
+ status->flag |= RX_FLAG_DECRYPTED;
+ }
+}
+
static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
{
struct wmi_mgmt_rx_event_v1 *ev_v1;
struct wmi_mgmt_rx_event_v2 *ev_v2;
struct wmi_mgmt_rx_hdr_v1 *ev_hdr;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
- struct ieee80211_channel *ch;
struct ieee80211_hdr *hdr;
u32 rx_status;
u32 channel;
@@ -1124,30 +1197,34 @@
return 0;
}
- if (rx_status & WMI_RX_STATUS_ERR_CRC)
- status->flag |= RX_FLAG_FAILED_FCS_CRC;
+ if (rx_status & WMI_RX_STATUS_ERR_CRC) {
+ dev_kfree_skb(skb);
+ return 0;
+ }
+
if (rx_status & WMI_RX_STATUS_ERR_MIC)
status->flag |= RX_FLAG_MMIC_ERROR;
- /* HW can Rx CCK rates on 5GHz. In that case phy_mode is set to
+ /* Hardware can Rx CCK rates on 5GHz. In that case phy_mode is set to
* MODE_11B. This means phy_mode is not a reliable source for the band
- * of mgmt rx. */
-
- ch = ar->scan_channel;
- if (!ch)
- ch = ar->rx_channel;
-
- if (ch) {
- status->band = ch->band;
-
- if (phy_mode == MODE_11B &&
- status->band == IEEE80211_BAND_5GHZ)
- ath10k_dbg(ar, ATH10K_DBG_MGMT, "wmi mgmt rx 11b (CCK) on 5GHz\n");
+ * of mgmt rx.
+ */
+ if (channel >= 1 && channel <= 14) {
+ status->band = IEEE80211_BAND_2GHZ;
+ } else if (channel >= 36 && channel <= 165) {
+ status->band = IEEE80211_BAND_5GHZ;
} else {
- ath10k_warn(ar, "using (unreliable) phy_mode to extract band for mgmt rx\n");
- status->band = phy_mode_to_band(phy_mode);
+ /* Shouldn't happen unless list of advertised channels to
+ * mac80211 has been changed.
+ */
+ WARN_ON_ONCE(1);
+ dev_kfree_skb(skb);
+ return 0;
}
+ if (phy_mode == MODE_11B && status->band == IEEE80211_BAND_5GHZ)
+ ath10k_dbg(ar, ATH10K_DBG_MGMT, "wmi mgmt rx 11b (CCK) on 5GHz\n");
+
status->freq = ieee80211_channel_to_frequency(channel, status->band);
status->signal = snr + ATH10K_DEFAULT_NOISE_FLOOR;
status->rate_idx = get_rate_idx(rate, status->band);
@@ -1157,6 +1234,8 @@
hdr = (struct ieee80211_hdr *)skb->data;
fc = le16_to_cpu(hdr->frame_control);
+ ath10k_wmi_handle_wep_reauth(ar, skb, status);
+
/* FW delivers WEP Shared Auth frame with Protected Bit set and
* encrypted payload. However in case of PMF it delivers decrypted
* frames with Protected Bit set. */
@@ -1292,14 +1371,196 @@
return 0;
}
+static void ath10k_wmi_pull_pdev_stats(const struct wmi_pdev_stats *src,
+ struct ath10k_fw_stats_pdev *dst)
+{
+ const struct wal_dbg_tx_stats *tx = &src->wal.tx;
+ const struct wal_dbg_rx_stats *rx = &src->wal.rx;
+
+ dst->ch_noise_floor = __le32_to_cpu(src->chan_nf);
+ dst->tx_frame_count = __le32_to_cpu(src->tx_frame_count);
+ dst->rx_frame_count = __le32_to_cpu(src->rx_frame_count);
+ dst->rx_clear_count = __le32_to_cpu(src->rx_clear_count);
+ dst->cycle_count = __le32_to_cpu(src->cycle_count);
+ dst->phy_err_count = __le32_to_cpu(src->phy_err_count);
+ dst->chan_tx_power = __le32_to_cpu(src->chan_tx_pwr);
+
+ dst->comp_queued = __le32_to_cpu(tx->comp_queued);
+ dst->comp_delivered = __le32_to_cpu(tx->comp_delivered);
+ dst->msdu_enqued = __le32_to_cpu(tx->msdu_enqued);
+ dst->mpdu_enqued = __le32_to_cpu(tx->mpdu_enqued);
+ dst->wmm_drop = __le32_to_cpu(tx->wmm_drop);
+ dst->local_enqued = __le32_to_cpu(tx->local_enqued);
+ dst->local_freed = __le32_to_cpu(tx->local_freed);
+ dst->hw_queued = __le32_to_cpu(tx->hw_queued);
+ dst->hw_reaped = __le32_to_cpu(tx->hw_reaped);
+ dst->underrun = __le32_to_cpu(tx->underrun);
+ dst->tx_abort = __le32_to_cpu(tx->tx_abort);
+ dst->mpdus_requed = __le32_to_cpu(tx->mpdus_requed);
+ dst->tx_ko = __le32_to_cpu(tx->tx_ko);
+ dst->data_rc = __le32_to_cpu(tx->data_rc);
+ dst->self_triggers = __le32_to_cpu(tx->self_triggers);
+ dst->sw_retry_failure = __le32_to_cpu(tx->sw_retry_failure);
+ dst->illgl_rate_phy_err = __le32_to_cpu(tx->illgl_rate_phy_err);
+ dst->pdev_cont_xretry = __le32_to_cpu(tx->pdev_cont_xretry);
+ dst->pdev_tx_timeout = __le32_to_cpu(tx->pdev_tx_timeout);
+ dst->pdev_resets = __le32_to_cpu(tx->pdev_resets);
+ dst->phy_underrun = __le32_to_cpu(tx->phy_underrun);
+ dst->txop_ovf = __le32_to_cpu(tx->txop_ovf);
+
+ dst->mid_ppdu_route_change = __le32_to_cpu(rx->mid_ppdu_route_change);
+ dst->status_rcvd = __le32_to_cpu(rx->status_rcvd);
+ dst->r0_frags = __le32_to_cpu(rx->r0_frags);
+ dst->r1_frags = __le32_to_cpu(rx->r1_frags);
+ dst->r2_frags = __le32_to_cpu(rx->r2_frags);
+ dst->r3_frags = __le32_to_cpu(rx->r3_frags);
+ dst->htt_msdus = __le32_to_cpu(rx->htt_msdus);
+ dst->htt_mpdus = __le32_to_cpu(rx->htt_mpdus);
+ dst->loc_msdus = __le32_to_cpu(rx->loc_msdus);
+ dst->loc_mpdus = __le32_to_cpu(rx->loc_mpdus);
+ dst->oversize_amsdu = __le32_to_cpu(rx->oversize_amsdu);
+ dst->phy_errs = __le32_to_cpu(rx->phy_errs);
+ dst->phy_err_drop = __le32_to_cpu(rx->phy_err_drop);
+ dst->mpdu_errs = __le32_to_cpu(rx->mpdu_errs);
+}
+
+static void ath10k_wmi_pull_peer_stats(const struct wmi_peer_stats *src,
+ struct ath10k_fw_stats_peer *dst)
+{
+ ether_addr_copy(dst->peer_macaddr, src->peer_macaddr.addr);
+ dst->peer_rssi = __le32_to_cpu(src->peer_rssi);
+ dst->peer_tx_rate = __le32_to_cpu(src->peer_tx_rate);
+}
+
+static int ath10k_wmi_main_pull_fw_stats(struct ath10k *ar,
+ struct sk_buff *skb,
+ struct ath10k_fw_stats *stats)
+{
+ const struct wmi_stats_event *ev = (void *)skb->data;
+ u32 num_pdev_stats, num_vdev_stats, num_peer_stats;
+ int i;
+
+ if (!skb_pull(skb, sizeof(*ev)))
+ return -EPROTO;
+
+ num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats);
+ num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats);
+ num_peer_stats = __le32_to_cpu(ev->num_peer_stats);
+
+ for (i = 0; i < num_pdev_stats; i++) {
+ const struct wmi_pdev_stats *src;
+ struct ath10k_fw_stats_pdev *dst;
+
+ src = (void *)skb->data;
+ if (!skb_pull(skb, sizeof(*src)))
+ return -EPROTO;
+
+ dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
+ if (!dst)
+ continue;
+
+ ath10k_wmi_pull_pdev_stats(src, dst);
+ list_add_tail(&dst->list, &stats->pdevs);
+ }
+
+ /* fw doesn't implement vdev stats */
+
+ for (i = 0; i < num_peer_stats; i++) {
+ const struct wmi_peer_stats *src;
+ struct ath10k_fw_stats_peer *dst;
+
+ src = (void *)skb->data;
+ if (!skb_pull(skb, sizeof(*src)))
+ return -EPROTO;
+
+ dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
+ if (!dst)
+ continue;
+
+ ath10k_wmi_pull_peer_stats(src, dst);
+ list_add_tail(&dst->list, &stats->peers);
+ }
+
+ return 0;
+}
+
+static int ath10k_wmi_10x_pull_fw_stats(struct ath10k *ar,
+ struct sk_buff *skb,
+ struct ath10k_fw_stats *stats)
+{
+ const struct wmi_stats_event *ev = (void *)skb->data;
+ u32 num_pdev_stats, num_vdev_stats, num_peer_stats;
+ int i;
+
+ if (!skb_pull(skb, sizeof(*ev)))
+ return -EPROTO;
+
+ num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats);
+ num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats);
+ num_peer_stats = __le32_to_cpu(ev->num_peer_stats);
+
+ for (i = 0; i < num_pdev_stats; i++) {
+ const struct wmi_10x_pdev_stats *src;
+ struct ath10k_fw_stats_pdev *dst;
+
+ src = (void *)skb->data;
+ if (!skb_pull(skb, sizeof(*src)))
+ return -EPROTO;
+
+ dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
+ if (!dst)
+ continue;
+
+ ath10k_wmi_pull_pdev_stats(&src->old, dst);
+
+ dst->ack_rx_bad = __le32_to_cpu(src->ack_rx_bad);
+ dst->rts_bad = __le32_to_cpu(src->rts_bad);
+ dst->rts_good = __le32_to_cpu(src->rts_good);
+ dst->fcs_bad = __le32_to_cpu(src->fcs_bad);
+ dst->no_beacons = __le32_to_cpu(src->no_beacons);
+ dst->mib_int_count = __le32_to_cpu(src->mib_int_count);
+
+ list_add_tail(&dst->list, &stats->pdevs);
+ }
+
+ /* fw doesn't implement vdev stats */
+
+ for (i = 0; i < num_peer_stats; i++) {
+ const struct wmi_10x_peer_stats *src;
+ struct ath10k_fw_stats_peer *dst;
+
+ src = (void *)skb->data;
+ if (!skb_pull(skb, sizeof(*src)))
+ return -EPROTO;
+
+ dst = kzalloc(sizeof(*dst), GFP_ATOMIC);
+ if (!dst)
+ continue;
+
+ ath10k_wmi_pull_peer_stats(&src->old, dst);
+
+ dst->peer_rx_rate = __le32_to_cpu(src->peer_rx_rate);
+
+ list_add_tail(&dst->list, &stats->peers);
+ }
+
+ return 0;
+}
+
+int ath10k_wmi_pull_fw_stats(struct ath10k *ar, struct sk_buff *skb,
+ struct ath10k_fw_stats *stats)
+{
+ if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
+ return ath10k_wmi_10x_pull_fw_stats(ar, skb, stats);
+ else
+ return ath10k_wmi_main_pull_fw_stats(ar, skb, stats);
+}
+
static void ath10k_wmi_event_update_stats(struct ath10k *ar,
struct sk_buff *skb)
{
- struct wmi_stats_event *ev = (struct wmi_stats_event *)skb->data;
-
ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_UPDATE_STATS_EVENTID\n");
-
- ath10k_debug_read_target_stats(ar, ev);
+ ath10k_debug_fw_stats_process(ar, skb);
}
static void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar,
@@ -1576,6 +1837,7 @@
struct wmi_bcn_info *bcn_info;
struct ath10k_vif *arvif;
struct sk_buff *bcn;
+ dma_addr_t paddr;
int ret, vdev_id = 0;
int attempted_to_send_beacon = 0;
@@ -1647,27 +1909,36 @@
ath10k_warn(ar, "SWBA overrun on vdev %d\n",
arvif->vdev_id);
- dma_unmap_single(arvif->ar->dev,
- ATH10K_SKB_CB(arvif->beacon)->paddr,
- arvif->beacon->len, DMA_TO_DEVICE);
- dev_kfree_skb_any(arvif->beacon);
- arvif->beacon = NULL;
+ ath10k_mac_vif_beacon_free(arvif);
}
- ATH10K_SKB_CB(bcn)->paddr = dma_map_single(arvif->ar->dev,
- bcn->data, bcn->len,
- DMA_TO_DEVICE);
- ret = dma_mapping_error(arvif->ar->dev,
- ATH10K_SKB_CB(bcn)->paddr);
- if (ret) {
- ath10k_warn(ar, "failed to map beacon: %d\n", ret);
- dev_kfree_skb_any(bcn);
- goto skip;
+ if (!arvif->beacon_buf) {
+ paddr = dma_map_single(arvif->ar->dev, bcn->data,
+ bcn->len, DMA_TO_DEVICE);
+ ret = dma_mapping_error(arvif->ar->dev, paddr);
+ if (ret) {
+ ath10k_warn(ar, "failed to map beacon: %d\n",
+ ret);
+ dev_kfree_skb_any(bcn);
+ goto skip;
+ }
+
+ ATH10K_SKB_CB(bcn)->paddr = paddr;
+ } else {
+ if (bcn->len > IEEE80211_MAX_FRAME_LEN) {
+ ath10k_warn(ar, "trimming beacon %d -> %d bytes!\n",
+ bcn->len, IEEE80211_MAX_FRAME_LEN);
+ skb_trim(bcn, IEEE80211_MAX_FRAME_LEN);
+ }
+ memcpy(arvif->beacon_buf, bcn->data, bcn->len);
+ ATH10K_SKB_CB(bcn)->paddr = arvif->beacon_paddr;
}
arvif->beacon = bcn;
arvif->beacon_sent = false;
+ trace_ath10k_tx_hdr(ar, bcn->data, bcn->len);
+ trace_ath10k_tx_payload(ar, bcn->data, bcn->len);
ret = ath10k_wmi_tx_beacon_nowait(arvif);
attempted_to_send_beacon = 1;
if (ret == 1) {
@@ -1696,8 +1967,8 @@
}
static void ath10k_dfs_radar_report(struct ath10k *ar,
- struct wmi_single_phyerr_rx_event *event,
- struct phyerr_radar_report *rr,
+ const struct wmi_phyerr *phyerr,
+ const struct phyerr_radar_report *rr,
u64 tsf)
{
u32 reg0, reg1, tsf32l;
@@ -1730,12 +2001,12 @@
return;
/* report event to DFS pattern detector */
- tsf32l = __le32_to_cpu(event->hdr.tsf_timestamp);
+ tsf32l = __le32_to_cpu(phyerr->tsf_timestamp);
tsf64 = tsf & (~0xFFFFFFFFULL);
tsf64 |= tsf32l;
width = MS(reg1, RADAR_REPORT_REG1_PULSE_DUR);
- rssi = event->hdr.rssi_combined;
+ rssi = phyerr->rssi_combined;
/* hardware store this as 8 bit signed value,
* set to zero if negative number
@@ -1774,8 +2045,8 @@
}
static int ath10k_dfs_fft_report(struct ath10k *ar,
- struct wmi_single_phyerr_rx_event *event,
- struct phyerr_fft_report *fftr,
+ const struct wmi_phyerr *phyerr,
+ const struct phyerr_fft_report *fftr,
u64 tsf)
{
u32 reg0, reg1;
@@ -1783,7 +2054,7 @@
reg0 = __le32_to_cpu(fftr->reg0);
reg1 = __le32_to_cpu(fftr->reg1);
- rssi = event->hdr.rssi_combined;
+ rssi = phyerr->rssi_combined;
ath10k_dbg(ar, ATH10K_DBG_REGULATORY,
"wmi phyerr fft report total_gain_db %d base_pwr_db %d fft_chn_idx %d peak_sidx %d\n",
@@ -1812,20 +2083,20 @@
}
static void ath10k_wmi_event_dfs(struct ath10k *ar,
- struct wmi_single_phyerr_rx_event *event,
+ const struct wmi_phyerr *phyerr,
u64 tsf)
{
int buf_len, tlv_len, res, i = 0;
- struct phyerr_tlv *tlv;
- struct phyerr_radar_report *rr;
- struct phyerr_fft_report *fftr;
- u8 *tlv_buf;
+ const struct phyerr_tlv *tlv;
+ const struct phyerr_radar_report *rr;
+ const struct phyerr_fft_report *fftr;
+ const u8 *tlv_buf;
- buf_len = __le32_to_cpu(event->hdr.buf_len);
+ buf_len = __le32_to_cpu(phyerr->buf_len);
ath10k_dbg(ar, ATH10K_DBG_REGULATORY,
"wmi event dfs err_code %d rssi %d tsfl 0x%X tsf64 0x%llX len %d\n",
- event->hdr.phy_err_code, event->hdr.rssi_combined,
- __le32_to_cpu(event->hdr.tsf_timestamp), tsf, buf_len);
+ phyerr->phy_err_code, phyerr->rssi_combined,
+ __le32_to_cpu(phyerr->tsf_timestamp), tsf, buf_len);
/* Skip event if DFS disabled */
if (!config_enabled(CPTCFG_ATH10K_DFS_CERTIFIED))
@@ -1840,9 +2111,9 @@
return;
}
- tlv = (struct phyerr_tlv *)&event->bufp[i];
+ tlv = (struct phyerr_tlv *)&phyerr->buf[i];
tlv_len = __le16_to_cpu(tlv->len);
- tlv_buf = &event->bufp[i + sizeof(*tlv)];
+ tlv_buf = &phyerr->buf[i + sizeof(*tlv)];
ath10k_dbg(ar, ATH10K_DBG_REGULATORY,
"wmi event dfs tlv_len %d tlv_tag 0x%02X tlv_sig 0x%02X\n",
tlv_len, tlv->tag, tlv->sig);
@@ -1856,7 +2127,7 @@
}
rr = (struct phyerr_radar_report *)tlv_buf;
- ath10k_dfs_radar_report(ar, event, rr, tsf);
+ ath10k_dfs_radar_report(ar, phyerr, rr, tsf);
break;
case PHYERR_TLV_TAG_SEARCH_FFT_REPORT:
if (i + sizeof(*tlv) + sizeof(*fftr) > buf_len) {
@@ -1866,7 +2137,7 @@
}
fftr = (struct phyerr_fft_report *)tlv_buf;
- res = ath10k_dfs_fft_report(ar, event, fftr, tsf);
+ res = ath10k_dfs_fft_report(ar, phyerr, fftr, tsf);
if (res)
return;
break;
@@ -1878,16 +2149,16 @@
static void
ath10k_wmi_event_spectral_scan(struct ath10k *ar,
- struct wmi_single_phyerr_rx_event *event,
+ const struct wmi_phyerr *phyerr,
u64 tsf)
{
int buf_len, tlv_len, res, i = 0;
struct phyerr_tlv *tlv;
- u8 *tlv_buf;
- struct phyerr_fft_report *fftr;
+ const void *tlv_buf;
+ const struct phyerr_fft_report *fftr;
size_t fftr_len;
- buf_len = __le32_to_cpu(event->hdr.buf_len);
+ buf_len = __le32_to_cpu(phyerr->buf_len);
while (i < buf_len) {
if (i + sizeof(*tlv) > buf_len) {
@@ -1896,9 +2167,9 @@
return;
}
- tlv = (struct phyerr_tlv *)&event->bufp[i];
+ tlv = (struct phyerr_tlv *)&phyerr->buf[i];
tlv_len = __le16_to_cpu(tlv->len);
- tlv_buf = &event->bufp[i + sizeof(*tlv)];
+ tlv_buf = &phyerr->buf[i + sizeof(*tlv)];
if (i + sizeof(*tlv) + tlv_len > buf_len) {
ath10k_warn(ar, "failed to parse phyerr tlv payload at byte %d\n",
@@ -1915,8 +2186,8 @@
}
fftr_len = tlv_len - sizeof(*fftr);
- fftr = (struct phyerr_fft_report *)tlv_buf;
- res = ath10k_spectral_process_fft(ar, event,
+ fftr = tlv_buf;
+ res = ath10k_spectral_process_fft(ar, phyerr,
fftr, fftr_len,
tsf);
if (res < 0) {
@@ -1933,8 +2204,8 @@
static void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb)
{
- struct wmi_comb_phyerr_rx_event *comb_event;
- struct wmi_single_phyerr_rx_event *event;
+ const struct wmi_phyerr_event *ev;
+ const struct wmi_phyerr *phyerr;
u32 count, i, buf_len, phy_err_code;
u64 tsf;
int left_len = skb->len;
@@ -1942,38 +2213,38 @@
ATH10K_DFS_STAT_INC(ar, phy_errors);
/* Check if combined event available */
- if (left_len < sizeof(*comb_event)) {
+ if (left_len < sizeof(*ev)) {
ath10k_warn(ar, "wmi phyerr combined event wrong len\n");
return;
}
- left_len -= sizeof(*comb_event);
+ left_len -= sizeof(*ev);
/* Check number of included events */
- comb_event = (struct wmi_comb_phyerr_rx_event *)skb->data;
- count = __le32_to_cpu(comb_event->hdr.num_phyerr_events);
+ ev = (const struct wmi_phyerr_event *)skb->data;
+ count = __le32_to_cpu(ev->num_phyerrs);
- tsf = __le32_to_cpu(comb_event->hdr.tsf_u32);
+ tsf = __le32_to_cpu(ev->tsf_u32);
tsf <<= 32;
- tsf |= __le32_to_cpu(comb_event->hdr.tsf_l32);
+ tsf |= __le32_to_cpu(ev->tsf_l32);
ath10k_dbg(ar, ATH10K_DBG_WMI,
"wmi event phyerr count %d tsf64 0x%llX\n",
count, tsf);
- event = (struct wmi_single_phyerr_rx_event *)comb_event->bufp;
+ phyerr = ev->phyerrs;
for (i = 0; i < count; i++) {
/* Check if we can read event header */
- if (left_len < sizeof(*event)) {
+ if (left_len < sizeof(*phyerr)) {
ath10k_warn(ar, "single event (%d) wrong head len\n",
i);
return;
}
- left_len -= sizeof(*event);
+ left_len -= sizeof(*phyerr);
- buf_len = __le32_to_cpu(event->hdr.buf_len);
- phy_err_code = event->hdr.phy_err_code;
+ buf_len = __le32_to_cpu(phyerr->buf_len);
+ phy_err_code = phyerr->phy_err_code;
if (left_len < buf_len) {
ath10k_warn(ar, "single event (%d) wrong buf len\n", i);
@@ -1984,20 +2255,20 @@
switch (phy_err_code) {
case PHY_ERROR_RADAR:
- ath10k_wmi_event_dfs(ar, event, tsf);
+ ath10k_wmi_event_dfs(ar, phyerr, tsf);
break;
case PHY_ERROR_SPECTRAL_SCAN:
- ath10k_wmi_event_spectral_scan(ar, event, tsf);
+ ath10k_wmi_event_spectral_scan(ar, phyerr, tsf);
break;
case PHY_ERROR_FALSE_RADAR_EXT:
- ath10k_wmi_event_dfs(ar, event, tsf);
- ath10k_wmi_event_spectral_scan(ar, event, tsf);
+ ath10k_wmi_event_dfs(ar, phyerr, tsf);
+ ath10k_wmi_event_spectral_scan(ar, phyerr, tsf);
break;
default:
break;
}
- event += sizeof(*event) + buf_len;
+ phyerr = (void *)phyerr + sizeof(*phyerr) + buf_len;
}
}
@@ -2043,7 +2314,7 @@
/* the last byte is always reserved for the null character */
buf[i] = '\0';
- ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi event debug print '%s'\n", buf);
+ ath10k_dbg(ar, ATH10K_DBG_WMI_PRINT, "wmi print '%s'\n", buf);
}
static void ath10k_wmi_event_pdev_qvit(struct ath10k *ar, struct sk_buff *skb)
@@ -2178,30 +2449,117 @@
return 0;
}
-static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar,
- struct sk_buff *skb)
+static int ath10k_wmi_main_pull_svc_rdy_ev(struct sk_buff *skb,
+ struct wmi_svc_rdy_ev_arg *arg)
{
- struct wmi_service_ready_event *ev = (void *)skb->data;
- DECLARE_BITMAP(svc_bmap, WMI_SERVICE_MAX) = {};
+ struct wmi_service_ready_event *ev;
+ size_t i, n;
- if (skb->len < sizeof(*ev)) {
- ath10k_warn(ar, "Service ready event was %d B but expected %zu B. Wrong firmware version?\n",
- skb->len, sizeof(*ev));
+ if (skb->len < sizeof(*ev))
+ return -EPROTO;
+
+ ev = (void *)skb->data;
+ skb_pull(skb, sizeof(*ev));
+ arg->min_tx_power = ev->hw_min_tx_power;
+ arg->max_tx_power = ev->hw_max_tx_power;
+ arg->ht_cap = ev->ht_cap_info;
+ arg->vht_cap = ev->vht_cap_info;
+ arg->sw_ver0 = ev->sw_version;
+ arg->sw_ver1 = ev->sw_version_1;
+ arg->phy_capab = ev->phy_capability;
+ arg->num_rf_chains = ev->num_rf_chains;
+ arg->eeprom_rd = ev->hal_reg_capabilities.eeprom_rd;
+ arg->num_mem_reqs = ev->num_mem_reqs;
+ arg->service_map = ev->wmi_service_bitmap;
+ arg->service_map_len = sizeof(ev->wmi_service_bitmap);
+
+ n = min_t(size_t, __le32_to_cpu(arg->num_mem_reqs),
+ ARRAY_SIZE(arg->mem_reqs));
+ for (i = 0; i < n; i++)
+ arg->mem_reqs[i] = &ev->mem_reqs[i];
+
+ if (skb->len <
+ __le32_to_cpu(arg->num_mem_reqs) * sizeof(arg->mem_reqs[0]))
+ return -EPROTO;
+
+ return 0;
+}
+
+static int ath10k_wmi_10x_pull_svc_rdy_ev(struct sk_buff *skb,
+ struct wmi_svc_rdy_ev_arg *arg)
+{
+ struct wmi_10x_service_ready_event *ev;
+ int i, n;
+
+ if (skb->len < sizeof(*ev))
+ return -EPROTO;
+
+ ev = (void *)skb->data;
+ skb_pull(skb, sizeof(*ev));
+ arg->min_tx_power = ev->hw_min_tx_power;
+ arg->max_tx_power = ev->hw_max_tx_power;
+ arg->ht_cap = ev->ht_cap_info;
+ arg->vht_cap = ev->vht_cap_info;
+ arg->sw_ver0 = ev->sw_version;
+ arg->phy_capab = ev->phy_capability;
+ arg->num_rf_chains = ev->num_rf_chains;
+ arg->eeprom_rd = ev->hal_reg_capabilities.eeprom_rd;
+ arg->num_mem_reqs = ev->num_mem_reqs;
+ arg->service_map = ev->wmi_service_bitmap;
+ arg->service_map_len = sizeof(ev->wmi_service_bitmap);
+
+ n = min_t(size_t, __le32_to_cpu(arg->num_mem_reqs),
+ ARRAY_SIZE(arg->mem_reqs));
+ for (i = 0; i < n; i++)
+ arg->mem_reqs[i] = &ev->mem_reqs[i];
+
+ if (skb->len <
+ __le32_to_cpu(arg->num_mem_reqs) * sizeof(arg->mem_reqs[0]))
+ return -EPROTO;
+
+ return 0;
+}
+
+static void ath10k_wmi_event_service_ready(struct ath10k *ar,
+ struct sk_buff *skb)
+{
+ struct wmi_svc_rdy_ev_arg arg = {};
+ u32 num_units, req_id, unit_size, num_mem_reqs, num_unit_info, i;
+ int ret;
+
+ memset(&ar->wmi.svc_map, 0, sizeof(ar->wmi.svc_map));
+
+ if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
+ ret = ath10k_wmi_10x_pull_svc_rdy_ev(skb, &arg);
+ wmi_10x_svc_map(arg.service_map, ar->wmi.svc_map,
+ arg.service_map_len);
+ } else {
+ ret = ath10k_wmi_main_pull_svc_rdy_ev(skb, &arg);
+ wmi_main_svc_map(arg.service_map, ar->wmi.svc_map,
+ arg.service_map_len);
+ }
+
+ if (ret) {
+ ath10k_warn(ar, "failed to parse service ready: %d\n", ret);
return;
}
- ar->hw_min_tx_power = __le32_to_cpu(ev->hw_min_tx_power);
- ar->hw_max_tx_power = __le32_to_cpu(ev->hw_max_tx_power);
- ar->ht_cap_info = __le32_to_cpu(ev->ht_cap_info);
- ar->vht_cap_info = __le32_to_cpu(ev->vht_cap_info);
+ ar->hw_min_tx_power = __le32_to_cpu(arg.min_tx_power);
+ ar->hw_max_tx_power = __le32_to_cpu(arg.max_tx_power);
+ ar->ht_cap_info = __le32_to_cpu(arg.ht_cap);
+ ar->vht_cap_info = __le32_to_cpu(arg.vht_cap);
ar->fw_version_major =
- (__le32_to_cpu(ev->sw_version) & 0xff000000) >> 24;
- ar->fw_version_minor = (__le32_to_cpu(ev->sw_version) & 0x00ffffff);
+ (__le32_to_cpu(arg.sw_ver0) & 0xff000000) >> 24;
+ ar->fw_version_minor = (__le32_to_cpu(arg.sw_ver0) & 0x00ffffff);
ar->fw_version_release =
- (__le32_to_cpu(ev->sw_version_1) & 0xffff0000) >> 16;
- ar->fw_version_build = (__le32_to_cpu(ev->sw_version_1) & 0x0000ffff);
- ar->phy_capability = __le32_to_cpu(ev->phy_capability);
- ar->num_rf_chains = __le32_to_cpu(ev->num_rf_chains);
+ (__le32_to_cpu(arg.sw_ver1) & 0xffff0000) >> 16;
+ ar->fw_version_build = (__le32_to_cpu(arg.sw_ver1) & 0x0000ffff);
+ ar->phy_capability = __le32_to_cpu(arg.phy_capab);
+ ar->num_rf_chains = __le32_to_cpu(arg.num_rf_chains);
+ ar->ath_common.regulatory.current_rd = __le32_to_cpu(arg.eeprom_rd);
+
+ ath10k_dbg_dump(ar, ATH10K_DBG_WMI, NULL, "wmi svc: ",
+ arg.service_map, arg.service_map_len);
/* only manually set fw features when not using FW IE format */
if (ar->fw_api == 1 && ar->fw_version_build > 636)
@@ -2213,13 +2571,8 @@
ar->num_rf_chains = WMI_MAX_SPATIAL_STREAM;
}
- ar->ath_common.regulatory.current_rd =
- __le32_to_cpu(ev->hal_reg_capabilities.eeprom_rd);
-
- wmi_main_svc_map(ev->wmi_service_bitmap, svc_bmap);
- ath10k_debug_read_service_map(ar, svc_bmap, sizeof(svc_bmap));
- ath10k_dbg_dump(ar, ATH10K_DBG_WMI, NULL, "wmi svc: ",
- ev->wmi_service_bitmap, sizeof(ev->wmi_service_bitmap));
+ ar->supp_tx_chainmask = (1 << ar->num_rf_chains) - 1;
+ ar->supp_rx_chainmask = (1 << ar->num_rf_chains) - 1;
if (strlen(ar->hw->wiphy->fw_version) == 0) {
snprintf(ar->hw->wiphy->fw_version,
@@ -2231,93 +2584,18 @@
ar->fw_version_build);
}
- /* FIXME: it probably should be better to support this */
- if (__le32_to_cpu(ev->num_mem_reqs) > 0) {
- ath10k_warn(ar, "target requested %d memory chunks; ignoring\n",
- __le32_to_cpu(ev->num_mem_reqs));
- }
-
- ath10k_dbg(ar, ATH10K_DBG_WMI,
- "wmi event service ready sw_ver 0x%08x sw_ver1 0x%08x abi_ver %u phy_cap 0x%08x ht_cap 0x%08x vht_cap 0x%08x vht_supp_msc 0x%08x sys_cap_info 0x%08x mem_reqs %u num_rf_chains %u\n",
- __le32_to_cpu(ev->sw_version),
- __le32_to_cpu(ev->sw_version_1),
- __le32_to_cpu(ev->abi_version),
- __le32_to_cpu(ev->phy_capability),
- __le32_to_cpu(ev->ht_cap_info),
- __le32_to_cpu(ev->vht_cap_info),
- __le32_to_cpu(ev->vht_supp_mcs),
- __le32_to_cpu(ev->sys_cap_info),
- __le32_to_cpu(ev->num_mem_reqs),
- __le32_to_cpu(ev->num_rf_chains));
-
- complete(&ar->wmi.service_ready);
-}
-
-static void ath10k_wmi_10x_service_ready_event_rx(struct ath10k *ar,
- struct sk_buff *skb)
-{
- u32 num_units, req_id, unit_size, num_mem_reqs, num_unit_info, i;
- int ret;
- struct wmi_service_ready_event_10x *ev = (void *)skb->data;
- DECLARE_BITMAP(svc_bmap, WMI_SERVICE_MAX) = {};
-
- if (skb->len < sizeof(*ev)) {
- ath10k_warn(ar, "Service ready event was %d B but expected %zu B. Wrong firmware version?\n",
- skb->len, sizeof(*ev));
- return;
- }
-
- ar->hw_min_tx_power = __le32_to_cpu(ev->hw_min_tx_power);
- ar->hw_max_tx_power = __le32_to_cpu(ev->hw_max_tx_power);
- ar->ht_cap_info = __le32_to_cpu(ev->ht_cap_info);
- ar->vht_cap_info = __le32_to_cpu(ev->vht_cap_info);
- ar->fw_version_major =
- (__le32_to_cpu(ev->sw_version) & 0xff000000) >> 24;
- ar->fw_version_minor = (__le32_to_cpu(ev->sw_version) & 0x00ffffff);
- ar->phy_capability = __le32_to_cpu(ev->phy_capability);
- ar->num_rf_chains = __le32_to_cpu(ev->num_rf_chains);
-
- if (ar->num_rf_chains > WMI_MAX_SPATIAL_STREAM) {
- ath10k_warn(ar, "hardware advertises support for more spatial streams than it should (%d > %d)\n",
- ar->num_rf_chains, WMI_MAX_SPATIAL_STREAM);
- ar->num_rf_chains = WMI_MAX_SPATIAL_STREAM;
- }
-
- ar->ath_common.regulatory.current_rd =
- __le32_to_cpu(ev->hal_reg_capabilities.eeprom_rd);
-
- wmi_10x_svc_map(ev->wmi_service_bitmap, svc_bmap);
- ath10k_debug_read_service_map(ar, svc_bmap, sizeof(svc_bmap));
- ath10k_dbg_dump(ar, ATH10K_DBG_WMI, NULL, "wmi svc: ",
- ev->wmi_service_bitmap, sizeof(ev->wmi_service_bitmap));
-
- if (strlen(ar->hw->wiphy->fw_version) == 0) {
- snprintf(ar->hw->wiphy->fw_version,
- sizeof(ar->hw->wiphy->fw_version),
- "%u.%u",
- ar->fw_version_major,
- ar->fw_version_minor);
- }
-
- num_mem_reqs = __le32_to_cpu(ev->num_mem_reqs);
-
- if (num_mem_reqs > ATH10K_MAX_MEM_REQS) {
+ num_mem_reqs = __le32_to_cpu(arg.num_mem_reqs);
+ if (num_mem_reqs > WMI_MAX_MEM_REQS) {
ath10k_warn(ar, "requested memory chunks number (%d) exceeds the limit\n",
num_mem_reqs);
return;
}
- if (!num_mem_reqs)
- goto exit;
-
- ath10k_dbg(ar, ATH10K_DBG_WMI, "firmware has requested %d memory chunks\n",
- num_mem_reqs);
-
for (i = 0; i < num_mem_reqs; ++i) {
- req_id = __le32_to_cpu(ev->mem_reqs[i].req_id);
- num_units = __le32_to_cpu(ev->mem_reqs[i].num_units);
- unit_size = __le32_to_cpu(ev->mem_reqs[i].unit_size);
- num_unit_info = __le32_to_cpu(ev->mem_reqs[i].num_unit_info);
+ req_id = __le32_to_cpu(arg.mem_reqs[i]->req_id);
+ num_units = __le32_to_cpu(arg.mem_reqs[i]->num_units);
+ unit_size = __le32_to_cpu(arg.mem_reqs[i]->unit_size);
+ num_unit_info = __le32_to_cpu(arg.mem_reqs[i]->num_unit_info);
if (num_unit_info & NUM_UNITS_IS_NUM_PEERS)
/* number of units to allocate is number of
@@ -2331,7 +2609,7 @@
ath10k_dbg(ar, ATH10K_DBG_WMI,
"wmi mem_req_id %d num_units %d num_unit_info %d unit size %d actual units %d\n",
req_id,
- __le32_to_cpu(ev->mem_reqs[i].num_units),
+ __le32_to_cpu(arg.mem_reqs[i]->num_units),
num_unit_info,
unit_size,
num_units);
@@ -2342,23 +2620,23 @@
return;
}
-exit:
ath10k_dbg(ar, ATH10K_DBG_WMI,
- "wmi event service ready sw_ver 0x%08x abi_ver %u phy_cap 0x%08x ht_cap 0x%08x vht_cap 0x%08x vht_supp_msc 0x%08x sys_cap_info 0x%08x mem_reqs %u num_rf_chains %u\n",
- __le32_to_cpu(ev->sw_version),
- __le32_to_cpu(ev->abi_version),
- __le32_to_cpu(ev->phy_capability),
- __le32_to_cpu(ev->ht_cap_info),
- __le32_to_cpu(ev->vht_cap_info),
- __le32_to_cpu(ev->vht_supp_mcs),
- __le32_to_cpu(ev->sys_cap_info),
- __le32_to_cpu(ev->num_mem_reqs),
- __le32_to_cpu(ev->num_rf_chains));
+ "wmi event service ready min_tx_power 0x%08x max_tx_power 0x%08x ht_cap 0x%08x vht_cap 0x%08x sw_ver0 0x%08x sw_ver1 0x%08x phy_capab 0x%08x num_rf_chains 0x%08x eeprom_rd 0x%08x num_mem_reqs 0x%08x\n",
+ __le32_to_cpu(arg.min_tx_power),
+ __le32_to_cpu(arg.max_tx_power),
+ __le32_to_cpu(arg.ht_cap),
+ __le32_to_cpu(arg.vht_cap),
+ __le32_to_cpu(arg.sw_ver0),
+ __le32_to_cpu(arg.sw_ver1),
+ __le32_to_cpu(arg.phy_capab),
+ __le32_to_cpu(arg.num_rf_chains),
+ __le32_to_cpu(arg.eeprom_rd),
+ __le32_to_cpu(arg.num_mem_reqs));
complete(&ar->wmi.service_ready);
}
-static int ath10k_wmi_ready_event_rx(struct ath10k *ar, struct sk_buff *skb)
+static int ath10k_wmi_event_ready(struct ath10k *ar, struct sk_buff *skb)
{
struct wmi_ready_event *ev = (struct wmi_ready_event *)skb->data;
@@ -2481,10 +2759,10 @@
ath10k_wmi_event_vdev_install_key_complete(ar, skb);
break;
case WMI_SERVICE_READY_EVENTID:
- ath10k_wmi_service_ready_event_rx(ar, skb);
+ ath10k_wmi_event_service_ready(ar, skb);
break;
case WMI_READY_EVENTID:
- ath10k_wmi_ready_event_rx(ar, skb);
+ ath10k_wmi_event_ready(ar, skb);
break;
default:
ath10k_warn(ar, "Unknown eventid: %d\n", id);
@@ -2601,10 +2879,10 @@
ath10k_wmi_event_vdev_resume_req(ar, skb);
break;
case WMI_10X_SERVICE_READY_EVENTID:
- ath10k_wmi_10x_service_ready_event_rx(ar, skb);
+ ath10k_wmi_event_service_ready(ar, skb);
break;
case WMI_10X_READY_EVENTID:
- ath10k_wmi_ready_event_rx(ar, skb);
+ ath10k_wmi_event_ready(ar, skb);
break;
case WMI_10X_PDEV_UTF_EVENTID:
/* ignore utf events */
@@ -2712,10 +2990,10 @@
ath10k_wmi_event_vdev_resume_req(ar, skb);
break;
case WMI_10_2_SERVICE_READY_EVENTID:
- ath10k_wmi_10x_service_ready_event_rx(ar, skb);
+ ath10k_wmi_event_service_ready(ar, skb);
break;
case WMI_10_2_READY_EVENTID:
- ath10k_wmi_ready_event_rx(ar, skb);
+ ath10k_wmi_event_ready(ar, skb);
break;
case WMI_10_2_RTT_KEEPALIVE_EVENTID:
case WMI_10_2_GPIO_INPUT_EVENTID:
@@ -2747,45 +3025,6 @@
}
}
-/* WMI Initialization functions */
-int ath10k_wmi_attach(struct ath10k *ar)
-{
- if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
- if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features))
- ar->wmi.cmd = &wmi_10_2_cmd_map;
- else
- ar->wmi.cmd = &wmi_10x_cmd_map;
-
- ar->wmi.vdev_param = &wmi_10x_vdev_param_map;
- ar->wmi.pdev_param = &wmi_10x_pdev_param_map;
- } else {
- ar->wmi.cmd = &wmi_cmd_map;
- ar->wmi.vdev_param = &wmi_vdev_param_map;
- ar->wmi.pdev_param = &wmi_pdev_param_map;
- }
-
- init_completion(&ar->wmi.service_ready);
- init_completion(&ar->wmi.unified_ready);
- init_waitqueue_head(&ar->wmi.tx_credits_wq);
-
- return 0;
-}
-
-void ath10k_wmi_detach(struct ath10k *ar)
-{
- int i;
-
- /* free the host memory chunks requested by firmware */
- for (i = 0; i < ar->wmi.num_mem_chunks; i++) {
- dma_free_coherent(ar->dev,
- ar->wmi.mem_chunks[i].len,
- ar->wmi.mem_chunks[i].vaddr,
- ar->wmi.mem_chunks[i].paddr);
- }
-
- ar->wmi.num_mem_chunks = 0;
-}
-
int ath10k_wmi_connect(struct ath10k *ar)
{
int status;
@@ -2880,42 +3119,6 @@
ctl2g, ctl5g);
}
-int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
- const struct wmi_channel_arg *arg)
-{
- struct wmi_set_channel_cmd *cmd;
- struct sk_buff *skb;
- u32 ch_flags = 0;
-
- if (arg->passive)
- return -EINVAL;
-
- skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
- if (!skb)
- return -ENOMEM;
-
- if (arg->chan_radar)
- ch_flags |= WMI_CHAN_FLAG_DFS;
-
- cmd = (struct wmi_set_channel_cmd *)skb->data;
- cmd->chan.mhz = __cpu_to_le32(arg->freq);
- cmd->chan.band_center_freq1 = __cpu_to_le32(arg->freq);
- cmd->chan.mode = arg->mode;
- cmd->chan.flags |= __cpu_to_le32(ch_flags);
- cmd->chan.min_power = arg->min_power;
- cmd->chan.max_power = arg->max_power;
- cmd->chan.reg_power = arg->max_reg_power;
- cmd->chan.reg_classid = arg->reg_class_id;
- cmd->chan.antenna_max = arg->max_antenna_gain;
-
- ath10k_dbg(ar, ATH10K_DBG_WMI,
- "wmi set channel mode %d freq %d\n",
- arg->mode, arg->freq);
-
- return ath10k_wmi_cmd_send(ar, skb,
- ar->wmi.cmd->pdev_set_channel_cmdid);
-}
-
int ath10k_wmi_pdev_suspend_target(struct ath10k *ar, u32 suspend_opt)
{
struct wmi_pdev_suspend_cmd *cmd;
@@ -2966,16 +3169,37 @@
return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_set_param_cmdid);
}
+static void ath10k_wmi_put_host_mem_chunks(struct ath10k *ar,
+ struct wmi_host_mem_chunks *chunks)
+{
+ struct host_memory_chunk *chunk;
+ int i;
+
+ chunks->count = __cpu_to_le32(ar->wmi.num_mem_chunks);
+
+ for (i = 0; i < ar->wmi.num_mem_chunks; i++) {
+ chunk = &chunks->items[i];
+ chunk->ptr = __cpu_to_le32(ar->wmi.mem_chunks[i].paddr);
+ chunk->size = __cpu_to_le32(ar->wmi.mem_chunks[i].len);
+ chunk->req_id = __cpu_to_le32(ar->wmi.mem_chunks[i].req_id);
+
+ ath10k_dbg(ar, ATH10K_DBG_WMI,
+ "wmi chunk %d len %d requested, addr 0x%llx\n",
+ i,
+ ar->wmi.mem_chunks[i].len,
+ (unsigned long long)ar->wmi.mem_chunks[i].paddr);
+ }
+}
+
static int ath10k_wmi_main_cmd_init(struct ath10k *ar)
{
struct wmi_init_cmd *cmd;
struct sk_buff *buf;
struct wmi_resource_config config = {};
u32 len, val;
- int i;
config.num_vdevs = __cpu_to_le32(TARGET_NUM_VDEVS);
- config.num_peers = __cpu_to_le32(TARGET_NUM_PEERS + TARGET_NUM_VDEVS);
+ config.num_peers = __cpu_to_le32(TARGET_NUM_PEERS);
config.num_offload_peers = __cpu_to_le32(TARGET_NUM_OFFLOAD_PEERS);
config.num_offload_reorder_bufs =
@@ -3036,32 +3260,8 @@
cmd = (struct wmi_init_cmd *)buf->data;
- if (ar->wmi.num_mem_chunks == 0) {
- cmd->num_host_mem_chunks = 0;
- goto out;
- }
-
- ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi sending %d memory chunks info.\n",
- ar->wmi.num_mem_chunks);
-
- cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks);
-
- for (i = 0; i < ar->wmi.num_mem_chunks; i++) {
- cmd->host_mem_chunks[i].ptr =
- __cpu_to_le32(ar->wmi.mem_chunks[i].paddr);
- cmd->host_mem_chunks[i].size =
- __cpu_to_le32(ar->wmi.mem_chunks[i].len);
- cmd->host_mem_chunks[i].req_id =
- __cpu_to_le32(ar->wmi.mem_chunks[i].req_id);
-
- ath10k_dbg(ar, ATH10K_DBG_WMI,
- "wmi chunk %d len %d requested, addr 0x%llx\n",
- i,
- ar->wmi.mem_chunks[i].len,
- (unsigned long long)ar->wmi.mem_chunks[i].paddr);
- }
-out:
memcpy(&cmd->resource_config, &config, sizeof(config));
+ ath10k_wmi_put_host_mem_chunks(ar, &cmd->mem_chunks);
ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi init\n");
return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid);
@@ -3073,7 +3273,6 @@
struct sk_buff *buf;
struct wmi_resource_config_10x config = {};
u32 len, val;
- int i;
config.num_vdevs = __cpu_to_le32(TARGET_10X_NUM_VDEVS);
config.num_peers = __cpu_to_le32(TARGET_10X_NUM_PEERS);
@@ -3130,32 +3329,8 @@
cmd = (struct wmi_init_cmd_10x *)buf->data;
- if (ar->wmi.num_mem_chunks == 0) {
- cmd->num_host_mem_chunks = 0;
- goto out;
- }
-
- ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi sending %d memory chunks info.\n",
- ar->wmi.num_mem_chunks);
-
- cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks);
-
- for (i = 0; i < ar->wmi.num_mem_chunks; i++) {
- cmd->host_mem_chunks[i].ptr =
- __cpu_to_le32(ar->wmi.mem_chunks[i].paddr);
- cmd->host_mem_chunks[i].size =
- __cpu_to_le32(ar->wmi.mem_chunks[i].len);
- cmd->host_mem_chunks[i].req_id =
- __cpu_to_le32(ar->wmi.mem_chunks[i].req_id);
-
- ath10k_dbg(ar, ATH10K_DBG_WMI,
- "wmi chunk %d len %d requested, addr 0x%llx\n",
- i,
- ar->wmi.mem_chunks[i].len,
- (unsigned long long)ar->wmi.mem_chunks[i].paddr);
- }
-out:
memcpy(&cmd->resource_config, &config, sizeof(config));
+ ath10k_wmi_put_host_mem_chunks(ar, &cmd->mem_chunks);
ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi init 10x\n");
return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid);
@@ -3167,7 +3342,6 @@
struct sk_buff *buf;
struct wmi_resource_config_10x config = {};
u32 len, val;
- int i;
config.num_vdevs = __cpu_to_le32(TARGET_10X_NUM_VDEVS);
config.num_peers = __cpu_to_le32(TARGET_10X_NUM_PEERS);
@@ -3221,32 +3395,8 @@
cmd = (struct wmi_init_cmd_10_2 *)buf->data;
- if (ar->wmi.num_mem_chunks == 0) {
- cmd->num_host_mem_chunks = 0;
- goto out;
- }
-
- ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi sending %d memory chunks info.\n",
- ar->wmi.num_mem_chunks);
-
- cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks);
-
- for (i = 0; i < ar->wmi.num_mem_chunks; i++) {
- cmd->host_mem_chunks[i].ptr =
- __cpu_to_le32(ar->wmi.mem_chunks[i].paddr);
- cmd->host_mem_chunks[i].size =
- __cpu_to_le32(ar->wmi.mem_chunks[i].len);
- cmd->host_mem_chunks[i].req_id =
- __cpu_to_le32(ar->wmi.mem_chunks[i].req_id);
-
- ath10k_dbg(ar, ATH10K_DBG_WMI,
- "wmi chunk %d len %d requested, addr 0x%llx\n",
- i,
- ar->wmi.mem_chunks[i].len,
- (unsigned long long)ar->wmi.mem_chunks[i].paddr);
- }
-out:
memcpy(&cmd->resource_config.common, &config, sizeof(config));
+ ath10k_wmi_put_host_mem_chunks(ar, &cmd->mem_chunks);
ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi init 10.2\n");
return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid);
@@ -3268,52 +3418,50 @@
return ret;
}
-static int ath10k_wmi_start_scan_calc_len(struct ath10k *ar,
- const struct wmi_start_scan_arg *arg)
+static int ath10k_wmi_start_scan_verify(const struct wmi_start_scan_arg *arg)
{
- int len;
+ if (arg->ie_len && !arg->ie)
+ return -EINVAL;
+ if (arg->n_channels && !arg->channels)
+ return -EINVAL;
+ if (arg->n_ssids && !arg->ssids)
+ return -EINVAL;
+ if (arg->n_bssids && !arg->bssids)
+ return -EINVAL;
- if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
- len = sizeof(struct wmi_start_scan_cmd_10x);
- else
- len = sizeof(struct wmi_start_scan_cmd);
+ if (arg->ie_len > WLAN_SCAN_PARAMS_MAX_IE_LEN)
+ return -EINVAL;
+ if (arg->n_channels > ARRAY_SIZE(arg->channels))
+ return -EINVAL;
+ if (arg->n_ssids > WLAN_SCAN_PARAMS_MAX_SSID)
+ return -EINVAL;
+ if (arg->n_bssids > WLAN_SCAN_PARAMS_MAX_BSSID)
+ return -EINVAL;
+
+ return 0;
+}
+
+static size_t
+ath10k_wmi_start_scan_tlvs_len(const struct wmi_start_scan_arg *arg)
+{
+ int len = 0;
if (arg->ie_len) {
- if (!arg->ie)
- return -EINVAL;
- if (arg->ie_len > WLAN_SCAN_PARAMS_MAX_IE_LEN)
- return -EINVAL;
-
len += sizeof(struct wmi_ie_data);
len += roundup(arg->ie_len, 4);
}
if (arg->n_channels) {
- if (!arg->channels)
- return -EINVAL;
- if (arg->n_channels > ARRAY_SIZE(arg->channels))
- return -EINVAL;
-
len += sizeof(struct wmi_chan_list);
len += sizeof(__le32) * arg->n_channels;
}
if (arg->n_ssids) {
- if (!arg->ssids)
- return -EINVAL;
- if (arg->n_ssids > WLAN_SCAN_PARAMS_MAX_SSID)
- return -EINVAL;
-
len += sizeof(struct wmi_ssid_list);
len += sizeof(struct wmi_ssid) * arg->n_ssids;
}
if (arg->n_bssids) {
- if (!arg->bssids)
- return -EINVAL;
- if (arg->n_bssids > WLAN_SCAN_PARAMS_MAX_BSSID)
- return -EINVAL;
-
len += sizeof(struct wmi_bssid_list);
len += sizeof(struct wmi_mac_addr) * arg->n_bssids;
}
@@ -3321,28 +3469,12 @@
return len;
}
-int ath10k_wmi_start_scan(struct ath10k *ar,
- const struct wmi_start_scan_arg *arg)
+static void
+ath10k_wmi_put_start_scan_common(struct wmi_start_scan_common *cmn,
+ const struct wmi_start_scan_arg *arg)
{
- struct wmi_start_scan_cmd *cmd;
- struct sk_buff *skb;
- struct wmi_ie_data *ie;
- struct wmi_chan_list *channels;
- struct wmi_ssid_list *ssids;
- struct wmi_bssid_list *bssids;
u32 scan_id;
u32 scan_req_id;
- int off;
- int len = 0;
- int i;
-
- len = ath10k_wmi_start_scan_calc_len(ar, arg);
- if (len < 0)
- return len; /* len contains error code here */
-
- skb = ath10k_wmi_alloc_skb(ar, len);
- if (!skb)
- return -ENOMEM;
scan_id = WMI_HOST_SCAN_REQ_ID_PREFIX;
scan_id |= arg->scan_id;
@@ -3350,35 +3482,36 @@
scan_req_id = WMI_HOST_SCAN_REQUESTOR_ID_PREFIX;
scan_req_id |= arg->scan_req_id;
- cmd = (struct wmi_start_scan_cmd *)skb->data;
- cmd->scan_id = __cpu_to_le32(scan_id);
- cmd->scan_req_id = __cpu_to_le32(scan_req_id);
- cmd->vdev_id = __cpu_to_le32(arg->vdev_id);
- cmd->scan_priority = __cpu_to_le32(arg->scan_priority);
- cmd->notify_scan_events = __cpu_to_le32(arg->notify_scan_events);
- cmd->dwell_time_active = __cpu_to_le32(arg->dwell_time_active);
- cmd->dwell_time_passive = __cpu_to_le32(arg->dwell_time_passive);
- cmd->min_rest_time = __cpu_to_le32(arg->min_rest_time);
- cmd->max_rest_time = __cpu_to_le32(arg->max_rest_time);
- cmd->repeat_probe_time = __cpu_to_le32(arg->repeat_probe_time);
- cmd->probe_spacing_time = __cpu_to_le32(arg->probe_spacing_time);
- cmd->idle_time = __cpu_to_le32(arg->idle_time);
- cmd->max_scan_time = __cpu_to_le32(arg->max_scan_time);
- cmd->probe_delay = __cpu_to_le32(arg->probe_delay);
- cmd->scan_ctrl_flags = __cpu_to_le32(arg->scan_ctrl_flags);
+ cmn->scan_id = __cpu_to_le32(scan_id);
+ cmn->scan_req_id = __cpu_to_le32(scan_req_id);
+ cmn->vdev_id = __cpu_to_le32(arg->vdev_id);
+ cmn->scan_priority = __cpu_to_le32(arg->scan_priority);
+ cmn->notify_scan_events = __cpu_to_le32(arg->notify_scan_events);
+ cmn->dwell_time_active = __cpu_to_le32(arg->dwell_time_active);
+ cmn->dwell_time_passive = __cpu_to_le32(arg->dwell_time_passive);
+ cmn->min_rest_time = __cpu_to_le32(arg->min_rest_time);
+ cmn->max_rest_time = __cpu_to_le32(arg->max_rest_time);
+ cmn->repeat_probe_time = __cpu_to_le32(arg->repeat_probe_time);
+ cmn->probe_spacing_time = __cpu_to_le32(arg->probe_spacing_time);
+ cmn->idle_time = __cpu_to_le32(arg->idle_time);
+ cmn->max_scan_time = __cpu_to_le32(arg->max_scan_time);
+ cmn->probe_delay = __cpu_to_le32(arg->probe_delay);
+ cmn->scan_ctrl_flags = __cpu_to_le32(arg->scan_ctrl_flags);
+}
- /* TLV list starts after fields included in the struct */
- /* There's just one filed that differes the two start_scan
- * structures - burst_duration, which we are not using btw,
- no point to make the split here, just shift the buffer to fit with
- given FW */
- if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
- off = sizeof(struct wmi_start_scan_cmd_10x);
- else
- off = sizeof(struct wmi_start_scan_cmd);
+static void
+ath10k_wmi_put_start_scan_tlvs(struct wmi_start_scan_tlvs *tlvs,
+ const struct wmi_start_scan_arg *arg)
+{
+ struct wmi_ie_data *ie;
+ struct wmi_chan_list *channels;
+ struct wmi_ssid_list *ssids;
+ struct wmi_bssid_list *bssids;
+ void *ptr = tlvs->tlvs;
+ int i;
if (arg->n_channels) {
- channels = (void *)skb->data + off;
+ channels = ptr;
channels->tag = __cpu_to_le32(WMI_CHAN_LIST_TAG);
channels->num_chan = __cpu_to_le32(arg->n_channels);
@@ -3386,12 +3519,12 @@
channels->channel_list[i].freq =
__cpu_to_le16(arg->channels[i]);
- off += sizeof(*channels);
- off += sizeof(__le32) * arg->n_channels;
+ ptr += sizeof(*channels);
+ ptr += sizeof(__le32) * arg->n_channels;
}
if (arg->n_ssids) {
- ssids = (void *)skb->data + off;
+ ssids = ptr;
ssids->tag = __cpu_to_le32(WMI_SSID_LIST_TAG);
ssids->num_ssids = __cpu_to_le32(arg->n_ssids);
@@ -3403,12 +3536,12 @@
arg->ssids[i].len);
}
- off += sizeof(*ssids);
- off += sizeof(struct wmi_ssid) * arg->n_ssids;
+ ptr += sizeof(*ssids);
+ ptr += sizeof(struct wmi_ssid) * arg->n_ssids;
}
if (arg->n_bssids) {
- bssids = (void *)skb->data + off;
+ bssids = ptr;
bssids->tag = __cpu_to_le32(WMI_BSSID_LIST_TAG);
bssids->num_bssid = __cpu_to_le32(arg->n_bssids);
@@ -3417,23 +3550,57 @@
arg->bssids[i].bssid,
ETH_ALEN);
- off += sizeof(*bssids);
- off += sizeof(struct wmi_mac_addr) * arg->n_bssids;
+ ptr += sizeof(*bssids);
+ ptr += sizeof(struct wmi_mac_addr) * arg->n_bssids;
}
if (arg->ie_len) {
- ie = (void *)skb->data + off;
+ ie = ptr;
ie->tag = __cpu_to_le32(WMI_IE_TAG);
ie->ie_len = __cpu_to_le32(arg->ie_len);
memcpy(ie->ie_data, arg->ie, arg->ie_len);
- off += sizeof(*ie);
- off += roundup(arg->ie_len, 4);
+ ptr += sizeof(*ie);
+ ptr += roundup(arg->ie_len, 4);
}
+}
- if (off != skb->len) {
- dev_kfree_skb(skb);
- return -EINVAL;
+int ath10k_wmi_start_scan(struct ath10k *ar,
+ const struct wmi_start_scan_arg *arg)
+{
+ struct sk_buff *skb;
+ size_t len;
+ int ret;
+
+ ret = ath10k_wmi_start_scan_verify(arg);
+ if (ret)
+ return ret;
+
+ if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
+ len = sizeof(struct wmi_10x_start_scan_cmd) +
+ ath10k_wmi_start_scan_tlvs_len(arg);
+ else
+ len = sizeof(struct wmi_start_scan_cmd) +
+ ath10k_wmi_start_scan_tlvs_len(arg);
+
+ skb = ath10k_wmi_alloc_skb(ar, len);
+ if (!skb)
+ return -ENOMEM;
+
+ if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
+ struct wmi_10x_start_scan_cmd *cmd;
+
+ cmd = (struct wmi_10x_start_scan_cmd *)skb->data;
+ ath10k_wmi_put_start_scan_common(&cmd->common, arg);
+ ath10k_wmi_put_start_scan_tlvs(&cmd->tlvs, arg);
+ } else {
+ struct wmi_start_scan_cmd *cmd;
+
+ cmd = (struct wmi_start_scan_cmd *)skb->data;
+ cmd->burst_duration_ms = __cpu_to_le32(0);
+
+ ath10k_wmi_put_start_scan_common(&cmd->common, arg);
+ ath10k_wmi_put_start_scan_tlvs(&cmd->tlvs, arg);
}
ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi start scan\n");
@@ -3552,7 +3719,6 @@
struct sk_buff *skb;
const char *cmdname;
u32 flags = 0;
- u32 ch_flags = 0;
if (cmd_id != ar->wmi.cmd->vdev_start_request_cmdid &&
cmd_id != ar->wmi.cmd->vdev_restart_request_cmdid)
@@ -3579,8 +3745,6 @@
flags |= WMI_VDEV_START_HIDDEN_SSID;
if (arg->pmf_enabled)
flags |= WMI_VDEV_START_PMF_ENABLED;
- if (arg->channel.chan_radar)
- ch_flags |= WMI_CHAN_FLAG_DFS;
cmd = (struct wmi_vdev_start_request_cmd *)skb->data;
cmd->vdev_id = __cpu_to_le32(arg->vdev_id);
@@ -3598,18 +3762,7 @@
memcpy(cmd->ssid.ssid, arg->ssid, arg->ssid_len);
}
- cmd->chan.mhz = __cpu_to_le32(arg->channel.freq);
-
- cmd->chan.band_center_freq1 =
- __cpu_to_le32(arg->channel.band_center_freq1);
-
- cmd->chan.mode = arg->channel.mode;
- cmd->chan.flags |= __cpu_to_le32(ch_flags);
- cmd->chan.min_power = arg->channel.min_power;
- cmd->chan.max_power = arg->channel.max_power;
- cmd->chan.reg_power = arg->channel.max_reg_power;
- cmd->chan.reg_classid = arg->channel.reg_class_id;
- cmd->chan.antenna_max = arg->channel.max_antenna_gain;
+ ath10k_wmi_put_wmi_channel(&cmd->chan, &arg->channel);
ath10k_dbg(ar, ATH10K_DBG_WMI,
"wmi vdev %s id 0x%x flags: 0x%0X, freq %d, mode %d, ch_flags: 0x%0X, max_power: %d\n",
@@ -3990,35 +4143,10 @@
cmd->num_scan_chans = __cpu_to_le32(arg->n_channels);
for (i = 0; i < arg->n_channels; i++) {
- u32 flags = 0;
-
ch = &arg->channels[i];
ci = &cmd->chan_info[i];
- if (ch->passive)
- flags |= WMI_CHAN_FLAG_PASSIVE;
- if (ch->allow_ibss)
- flags |= WMI_CHAN_FLAG_ADHOC_ALLOWED;
- if (ch->allow_ht)
- flags |= WMI_CHAN_FLAG_ALLOW_HT;
- if (ch->allow_vht)
- flags |= WMI_CHAN_FLAG_ALLOW_VHT;
- if (ch->ht40plus)
- flags |= WMI_CHAN_FLAG_HT40_PLUS;
- if (ch->chan_radar)
- flags |= WMI_CHAN_FLAG_DFS;
-
- ci->mhz = __cpu_to_le32(ch->freq);
- ci->band_center_freq1 = __cpu_to_le32(ch->freq);
- ci->band_center_freq2 = 0;
- ci->min_power = ch->min_power;
- ci->max_power = ch->max_power;
- ci->reg_power = ch->max_reg_power;
- ci->antenna_max = ch->max_antenna_gain;
-
- /* mode & flags share storage */
- ci->mode = ch->mode;
- ci->flags |= __cpu_to_le32(flags);
+ ath10k_wmi_put_wmi_channel(ci, ch);
}
return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->scan_chan_list_cmdid);
@@ -4130,9 +4258,9 @@
if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features))
- ath10k_wmi_peer_assoc_fill_10_1(ar, skb->data, arg);
- else
ath10k_wmi_peer_assoc_fill_10_2(ar, skb->data, arg);
+ else
+ ath10k_wmi_peer_assoc_fill_10_1(ar, skb->data, arg);
} else {
ath10k_wmi_peer_assoc_fill_main(ar, skb->data, arg);
}
@@ -4289,3 +4417,73 @@
return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->dbglog_cfg_cmdid);
}
+
+int ath10k_wmi_pdev_pktlog_enable(struct ath10k *ar, u32 ev_bitmap)
+{
+ struct wmi_pdev_pktlog_enable_cmd *cmd;
+ struct sk_buff *skb;
+
+ skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ ev_bitmap &= ATH10K_PKTLOG_ANY;
+ ath10k_dbg(ar, ATH10K_DBG_WMI,
+ "wmi enable pktlog filter:%x\n", ev_bitmap);
+
+ cmd = (struct wmi_pdev_pktlog_enable_cmd *)skb->data;
+ cmd->ev_bitmap = __cpu_to_le32(ev_bitmap);
+ return ath10k_wmi_cmd_send(ar, skb,
+ ar->wmi.cmd->pdev_pktlog_enable_cmdid);
+}
+
+int ath10k_wmi_pdev_pktlog_disable(struct ath10k *ar)
+{
+ struct sk_buff *skb;
+
+ skb = ath10k_wmi_alloc_skb(ar, 0);
+ if (!skb)
+ return -ENOMEM;
+
+ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi disable pktlog\n");
+
+ return ath10k_wmi_cmd_send(ar, skb,
+ ar->wmi.cmd->pdev_pktlog_disable_cmdid);
+}
+
+int ath10k_wmi_attach(struct ath10k *ar)
+{
+ if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
+ if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features))
+ ar->wmi.cmd = &wmi_10_2_cmd_map;
+ else
+ ar->wmi.cmd = &wmi_10x_cmd_map;
+
+ ar->wmi.vdev_param = &wmi_10x_vdev_param_map;
+ ar->wmi.pdev_param = &wmi_10x_pdev_param_map;
+ } else {
+ ar->wmi.cmd = &wmi_cmd_map;
+ ar->wmi.vdev_param = &wmi_vdev_param_map;
+ ar->wmi.pdev_param = &wmi_pdev_param_map;
+ }
+
+ init_completion(&ar->wmi.service_ready);
+ init_completion(&ar->wmi.unified_ready);
+
+ return 0;
+}
+
+void ath10k_wmi_detach(struct ath10k *ar)
+{
+ int i;
+
+ /* free the host memory chunks requested by firmware */
+ for (i = 0; i < ar->wmi.num_mem_chunks; i++) {
+ dma_free_coherent(ar->dev,
+ ar->wmi.mem_chunks[i].len,
+ ar->wmi.mem_chunks[i].vaddr,
+ ar->wmi.mem_chunks[i].paddr);
+ }
+
+ ar->wmi.num_mem_chunks = 0;
+}
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
index 048ba28..09daa3e 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.h
+++ b/drivers/net/wireless/ath/ath10k/wmi.h
@@ -222,128 +222,131 @@
#undef SVCSTR
}
-#define WMI_SERVICE_IS_ENABLED(wmi_svc_bmap, svc_id) \
- (__le32_to_cpu((wmi_svc_bmap)[(svc_id)/(sizeof(u32))]) & \
+#define WMI_SERVICE_IS_ENABLED(wmi_svc_bmap, svc_id, len) \
+ ((svc_id) < (len) && \
+ __le32_to_cpu((wmi_svc_bmap)[(svc_id)/(sizeof(u32))]) & \
BIT((svc_id)%(sizeof(u32))))
-#define SVCMAP(x, y) \
+#define SVCMAP(x, y, len) \
do { \
- if (WMI_SERVICE_IS_ENABLED((in), (x))) \
+ if (WMI_SERVICE_IS_ENABLED((in), (x), (len))) \
__set_bit(y, out); \
} while (0)
-static inline void wmi_10x_svc_map(const __le32 *in, unsigned long *out)
+static inline void wmi_10x_svc_map(const __le32 *in, unsigned long *out,
+ size_t len)
{
SVCMAP(WMI_10X_SERVICE_BEACON_OFFLOAD,
- WMI_SERVICE_BEACON_OFFLOAD);
+ WMI_SERVICE_BEACON_OFFLOAD, len);
SVCMAP(WMI_10X_SERVICE_SCAN_OFFLOAD,
- WMI_SERVICE_SCAN_OFFLOAD);
+ WMI_SERVICE_SCAN_OFFLOAD, len);
SVCMAP(WMI_10X_SERVICE_ROAM_OFFLOAD,
- WMI_SERVICE_ROAM_OFFLOAD);
+ WMI_SERVICE_ROAM_OFFLOAD, len);
SVCMAP(WMI_10X_SERVICE_BCN_MISS_OFFLOAD,
- WMI_SERVICE_BCN_MISS_OFFLOAD);
+ WMI_SERVICE_BCN_MISS_OFFLOAD, len);
SVCMAP(WMI_10X_SERVICE_STA_PWRSAVE,
- WMI_SERVICE_STA_PWRSAVE);
+ WMI_SERVICE_STA_PWRSAVE, len);
SVCMAP(WMI_10X_SERVICE_STA_ADVANCED_PWRSAVE,
- WMI_SERVICE_STA_ADVANCED_PWRSAVE);
+ WMI_SERVICE_STA_ADVANCED_PWRSAVE, len);
SVCMAP(WMI_10X_SERVICE_AP_UAPSD,
- WMI_SERVICE_AP_UAPSD);
+ WMI_SERVICE_AP_UAPSD, len);
SVCMAP(WMI_10X_SERVICE_AP_DFS,
- WMI_SERVICE_AP_DFS);
+ WMI_SERVICE_AP_DFS, len);
SVCMAP(WMI_10X_SERVICE_11AC,
- WMI_SERVICE_11AC);
+ WMI_SERVICE_11AC, len);
SVCMAP(WMI_10X_SERVICE_BLOCKACK,
- WMI_SERVICE_BLOCKACK);
+ WMI_SERVICE_BLOCKACK, len);
SVCMAP(WMI_10X_SERVICE_PHYERR,
- WMI_SERVICE_PHYERR);
+ WMI_SERVICE_PHYERR, len);
SVCMAP(WMI_10X_SERVICE_BCN_FILTER,
- WMI_SERVICE_BCN_FILTER);
+ WMI_SERVICE_BCN_FILTER, len);
SVCMAP(WMI_10X_SERVICE_RTT,
- WMI_SERVICE_RTT);
+ WMI_SERVICE_RTT, len);
SVCMAP(WMI_10X_SERVICE_RATECTRL,
- WMI_SERVICE_RATECTRL);
+ WMI_SERVICE_RATECTRL, len);
SVCMAP(WMI_10X_SERVICE_WOW,
- WMI_SERVICE_WOW);
+ WMI_SERVICE_WOW, len);
SVCMAP(WMI_10X_SERVICE_RATECTRL_CACHE,
- WMI_SERVICE_RATECTRL_CACHE);
+ WMI_SERVICE_RATECTRL_CACHE, len);
SVCMAP(WMI_10X_SERVICE_IRAM_TIDS,
- WMI_SERVICE_IRAM_TIDS);
+ WMI_SERVICE_IRAM_TIDS, len);
SVCMAP(WMI_10X_SERVICE_BURST,
- WMI_SERVICE_BURST);
+ WMI_SERVICE_BURST, len);
SVCMAP(WMI_10X_SERVICE_SMART_ANTENNA_SW_SUPPORT,
- WMI_SERVICE_SMART_ANTENNA_SW_SUPPORT);
+ WMI_SERVICE_SMART_ANTENNA_SW_SUPPORT, len);
SVCMAP(WMI_10X_SERVICE_FORCE_FW_HANG,
- WMI_SERVICE_FORCE_FW_HANG);
+ WMI_SERVICE_FORCE_FW_HANG, len);
SVCMAP(WMI_10X_SERVICE_SMART_ANTENNA_HW_SUPPORT,
- WMI_SERVICE_SMART_ANTENNA_HW_SUPPORT);
+ WMI_SERVICE_SMART_ANTENNA_HW_SUPPORT, len);
}
-static inline void wmi_main_svc_map(const __le32 *in, unsigned long *out)
+static inline void wmi_main_svc_map(const __le32 *in, unsigned long *out,
+ size_t len)
{
SVCMAP(WMI_MAIN_SERVICE_BEACON_OFFLOAD,
- WMI_SERVICE_BEACON_OFFLOAD);
+ WMI_SERVICE_BEACON_OFFLOAD, len);
SVCMAP(WMI_MAIN_SERVICE_SCAN_OFFLOAD,
- WMI_SERVICE_SCAN_OFFLOAD);
+ WMI_SERVICE_SCAN_OFFLOAD, len);
SVCMAP(WMI_MAIN_SERVICE_ROAM_OFFLOAD,
- WMI_SERVICE_ROAM_OFFLOAD);
+ WMI_SERVICE_ROAM_OFFLOAD, len);
SVCMAP(WMI_MAIN_SERVICE_BCN_MISS_OFFLOAD,
- WMI_SERVICE_BCN_MISS_OFFLOAD);
+ WMI_SERVICE_BCN_MISS_OFFLOAD, len);
SVCMAP(WMI_MAIN_SERVICE_STA_PWRSAVE,
- WMI_SERVICE_STA_PWRSAVE);
+ WMI_SERVICE_STA_PWRSAVE, len);
SVCMAP(WMI_MAIN_SERVICE_STA_ADVANCED_PWRSAVE,
- WMI_SERVICE_STA_ADVANCED_PWRSAVE);
+ WMI_SERVICE_STA_ADVANCED_PWRSAVE, len);
SVCMAP(WMI_MAIN_SERVICE_AP_UAPSD,
- WMI_SERVICE_AP_UAPSD);
+ WMI_SERVICE_AP_UAPSD, len);
SVCMAP(WMI_MAIN_SERVICE_AP_DFS,
- WMI_SERVICE_AP_DFS);
+ WMI_SERVICE_AP_DFS, len);
SVCMAP(WMI_MAIN_SERVICE_11AC,
- WMI_SERVICE_11AC);
+ WMI_SERVICE_11AC, len);
SVCMAP(WMI_MAIN_SERVICE_BLOCKACK,
- WMI_SERVICE_BLOCKACK);
+ WMI_SERVICE_BLOCKACK, len);
SVCMAP(WMI_MAIN_SERVICE_PHYERR,
- WMI_SERVICE_PHYERR);
+ WMI_SERVICE_PHYERR, len);
SVCMAP(WMI_MAIN_SERVICE_BCN_FILTER,
- WMI_SERVICE_BCN_FILTER);
+ WMI_SERVICE_BCN_FILTER, len);
SVCMAP(WMI_MAIN_SERVICE_RTT,
- WMI_SERVICE_RTT);
+ WMI_SERVICE_RTT, len);
SVCMAP(WMI_MAIN_SERVICE_RATECTRL,
- WMI_SERVICE_RATECTRL);
+ WMI_SERVICE_RATECTRL, len);
SVCMAP(WMI_MAIN_SERVICE_WOW,
- WMI_SERVICE_WOW);
+ WMI_SERVICE_WOW, len);
SVCMAP(WMI_MAIN_SERVICE_RATECTRL_CACHE,
- WMI_SERVICE_RATECTRL_CACHE);
+ WMI_SERVICE_RATECTRL_CACHE, len);
SVCMAP(WMI_MAIN_SERVICE_IRAM_TIDS,
- WMI_SERVICE_IRAM_TIDS);
+ WMI_SERVICE_IRAM_TIDS, len);
SVCMAP(WMI_MAIN_SERVICE_ARPNS_OFFLOAD,
- WMI_SERVICE_ARPNS_OFFLOAD);
+ WMI_SERVICE_ARPNS_OFFLOAD, len);
SVCMAP(WMI_MAIN_SERVICE_NLO,
- WMI_SERVICE_NLO);
+ WMI_SERVICE_NLO, len);
SVCMAP(WMI_MAIN_SERVICE_GTK_OFFLOAD,
- WMI_SERVICE_GTK_OFFLOAD);
+ WMI_SERVICE_GTK_OFFLOAD, len);
SVCMAP(WMI_MAIN_SERVICE_SCAN_SCH,
- WMI_SERVICE_SCAN_SCH);
+ WMI_SERVICE_SCAN_SCH, len);
SVCMAP(WMI_MAIN_SERVICE_CSA_OFFLOAD,
- WMI_SERVICE_CSA_OFFLOAD);
+ WMI_SERVICE_CSA_OFFLOAD, len);
SVCMAP(WMI_MAIN_SERVICE_CHATTER,
- WMI_SERVICE_CHATTER);
+ WMI_SERVICE_CHATTER, len);
SVCMAP(WMI_MAIN_SERVICE_COEX_FREQAVOID,
- WMI_SERVICE_COEX_FREQAVOID);
+ WMI_SERVICE_COEX_FREQAVOID, len);
SVCMAP(WMI_MAIN_SERVICE_PACKET_POWER_SAVE,
- WMI_SERVICE_PACKET_POWER_SAVE);
+ WMI_SERVICE_PACKET_POWER_SAVE, len);
SVCMAP(WMI_MAIN_SERVICE_FORCE_FW_HANG,
- WMI_SERVICE_FORCE_FW_HANG);
+ WMI_SERVICE_FORCE_FW_HANG, len);
SVCMAP(WMI_MAIN_SERVICE_GPIO,
- WMI_SERVICE_GPIO);
+ WMI_SERVICE_GPIO, len);
SVCMAP(WMI_MAIN_SERVICE_STA_DTIM_PS_MODULATED_DTIM,
- WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM);
+ WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM, len);
SVCMAP(WMI_MAIN_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG,
- WMI_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG);
+ WMI_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG, len);
SVCMAP(WMI_MAIN_SERVICE_STA_UAPSD_VAR_AUTO_TRIG,
- WMI_SERVICE_STA_UAPSD_VAR_AUTO_TRIG);
+ WMI_SERVICE_STA_UAPSD_VAR_AUTO_TRIG, len);
SVCMAP(WMI_MAIN_SERVICE_STA_KEEP_ALIVE,
- WMI_SERVICE_STA_KEEP_ALIVE);
+ WMI_SERVICE_STA_KEEP_ALIVE, len);
SVCMAP(WMI_MAIN_SERVICE_TX_ENCAP,
- WMI_SERVICE_TX_ENCAP);
+ WMI_SERVICE_TX_ENCAP, len);
}
#undef SVCMAP
@@ -1428,11 +1431,11 @@
* where FW can access this memory directly (or) by DMA.
*/
__le32 num_mem_reqs;
- struct wlan_host_mem_req mem_reqs[1];
+ struct wlan_host_mem_req mem_reqs[0];
} __packed;
/* This is the definition from 10.X firmware branch */
-struct wmi_service_ready_event_10x {
+struct wmi_10x_service_ready_event {
__le32 sw_version;
__le32 abi_version;
@@ -1467,7 +1470,7 @@
*/
__le32 num_mem_reqs;
- struct wlan_host_mem_req mem_reqs[1];
+ struct wlan_host_mem_req mem_reqs[0];
} __packed;
#define WMI_SERVICE_READY_TIMEOUT_HZ (5*HZ)
@@ -1883,38 +1886,26 @@
__le32 size;
} __packed;
+struct wmi_host_mem_chunks {
+ __le32 count;
+ /* some fw revisions require at least 1 chunk regardless of count */
+ struct host_memory_chunk items[1];
+} __packed;
+
struct wmi_init_cmd {
struct wmi_resource_config resource_config;
- __le32 num_host_mem_chunks;
-
- /*
- * variable number of host memory chunks.
- * This should be the last element in the structure
- */
- struct host_memory_chunk host_mem_chunks[1];
+ struct wmi_host_mem_chunks mem_chunks;
} __packed;
/* _10x stucture is from 10.X FW API */
struct wmi_init_cmd_10x {
struct wmi_resource_config_10x resource_config;
- __le32 num_host_mem_chunks;
-
- /*
- * variable number of host memory chunks.
- * This should be the last element in the structure
- */
- struct host_memory_chunk host_mem_chunks[1];
+ struct wmi_host_mem_chunks mem_chunks;
} __packed;
struct wmi_init_cmd_10_2 {
struct wmi_resource_config_10_2 resource_config;
- __le32 num_host_mem_chunks;
-
- /*
- * variable number of host memory chunks.
- * This should be the last element in the structure
- */
- struct host_memory_chunk host_mem_chunks[1];
+ struct wmi_host_mem_chunks mem_chunks;
} __packed;
struct wmi_chan_list_entry {
@@ -1964,6 +1955,11 @@
#define WLAN_SCAN_PARAMS_MAX_BSSID 4
#define WLAN_SCAN_PARAMS_MAX_IE_LEN 256
+/* Values lower than this may be refused by some firmware revisions with a scan
+ * completion with a timedout reason.
+ */
+#define WMI_SCAN_CHAN_MIN_TIME_MSEC 40
+
/* Scan priority numbers must be sequential, starting with 0 */
enum wmi_scan_priority {
WMI_SCAN_PRIORITY_VERY_LOW = 0,
@@ -1974,7 +1970,7 @@
WMI_SCAN_PRIORITY_COUNT /* number of priorities supported */
};
-struct wmi_start_scan_cmd {
+struct wmi_start_scan_common {
/* Scan ID */
__le32 scan_id;
/* Scan requestor ID */
@@ -2032,95 +2028,25 @@
__le32 probe_delay;
/* Scan control flags */
__le32 scan_ctrl_flags;
+} __packed;
- /* Burst duration time in msecs */
- __le32 burst_duration;
- /*
- * TLV (tag length value ) paramerters follow the scan_cmd structure.
- * TLV can contain channel list, bssid list, ssid list and
- * ie. the TLV tags are defined above;
+struct wmi_start_scan_tlvs {
+ /* TLV parameters. These includes channel list, ssid list, bssid list,
+ * extra ies.
*/
+ u8 tlvs[0];
+} __packed;
+
+struct wmi_start_scan_cmd {
+ struct wmi_start_scan_common common;
+ __le32 burst_duration_ms;
+ struct wmi_start_scan_tlvs tlvs;
} __packed;
/* This is the definition from 10.X firmware branch */
-struct wmi_start_scan_cmd_10x {
- /* Scan ID */
- __le32 scan_id;
-
- /* Scan requestor ID */
- __le32 scan_req_id;
-
- /* VDEV id(interface) that is requesting scan */
- __le32 vdev_id;
-
- /* Scan Priority, input to scan scheduler */
- __le32 scan_priority;
-
- /* Scan events subscription */
- __le32 notify_scan_events;
-
- /* dwell time in msec on active channels */
- __le32 dwell_time_active;
-
- /* dwell time in msec on passive channels */
- __le32 dwell_time_passive;
-
- /*
- * min time in msec on the BSS channel,only valid if atleast one
- * VDEV is active
- */
- __le32 min_rest_time;
-
- /*
- * max rest time in msec on the BSS channel,only valid if at least
- * one VDEV is active
- */
- /*
- * the scanner will rest on the bss channel at least min_rest_time
- * after min_rest_time the scanner will start checking for tx/rx
- * activity on all VDEVs. if there is no activity the scanner will
- * switch to off channel. if there is activity the scanner will let
- * the radio on the bss channel until max_rest_time expires.at
- * max_rest_time scanner will switch to off channel irrespective of
- * activity. activity is determined by the idle_time parameter.
- */
- __le32 max_rest_time;
-
- /*
- * time before sending next set of probe requests.
- * The scanner keeps repeating probe requests transmission with
- * period specified by repeat_probe_time.
- * The number of probe requests specified depends on the ssid_list
- * and bssid_list
- */
- __le32 repeat_probe_time;
-
- /* time in msec between 2 consequetive probe requests with in a set. */
- __le32 probe_spacing_time;
-
- /*
- * data inactivity time in msec on bss channel that will be used by
- * scanner for measuring the inactivity.
- */
- __le32 idle_time;
-
- /* maximum time in msec allowed for scan */
- __le32 max_scan_time;
-
- /*
- * delay in msec before sending first probe request after switching
- * to a channel
- */
- __le32 probe_delay;
-
- /* Scan control flags */
- __le32 scan_ctrl_flags;
-
- /*
- * TLV (tag length value ) paramerters follow the scan_cmd structure.
- * TLV can contain channel list, bssid list, ssid list and
- * ie. the TLV tags are defined above;
- */
+struct wmi_10x_start_scan_cmd {
+ struct wmi_start_scan_common common;
+ struct wmi_start_scan_tlvs tlvs;
} __packed;
struct wmi_ssid_arg {
@@ -2306,94 +2232,25 @@
#define PHY_ERROR_FALSE_RADAR_EXT 0x24
#define PHY_ERROR_RADAR 0x05
-struct wmi_single_phyerr_rx_hdr {
- /* TSF timestamp */
+struct wmi_phyerr {
__le32 tsf_timestamp;
-
- /*
- * Current freq1, freq2
- *
- * [7:0]: freq1[lo]
- * [15:8] : freq1[hi]
- * [23:16]: freq2[lo]
- * [31:24]: freq2[hi]
- */
__le16 freq1;
__le16 freq2;
-
- /*
- * Combined RSSI over all chains and channel width for this PHY error
- *
- * [7:0]: RSSI combined
- * [15:8]: Channel width (MHz)
- * [23:16]: PHY error code
- * [24:16]: reserved (future use)
- */
u8 rssi_combined;
u8 chan_width_mhz;
u8 phy_err_code;
u8 rsvd0;
-
- /*
- * RSSI on chain 0 through 3
- *
- * This is formatted the same as the PPDU_START RX descriptor
- * field:
- *
- * [7:0]: pri20
- * [15:8]: sec20
- * [23:16]: sec40
- * [31:24]: sec80
- */
-
- __le32 rssi_chain0;
- __le32 rssi_chain1;
- __le32 rssi_chain2;
- __le32 rssi_chain3;
-
- /*
- * Last calibrated NF value for chain 0 through 3
- *
- * nf_list_1:
- *
- * + [15:0] - chain 0
- * + [31:16] - chain 1
- *
- * nf_list_2:
- *
- * + [15:0] - chain 2
- * + [31:16] - chain 3
- */
- __le32 nf_list_1;
- __le32 nf_list_2;
-
- /* Length of the frame */
+ __le32 rssi_chains[4];
+ __le16 nf_chains[4];
__le32 buf_len;
+ u8 buf[0];
} __packed;
-struct wmi_single_phyerr_rx_event {
- /* Phy error event header */
- struct wmi_single_phyerr_rx_hdr hdr;
- /* frame buffer */
- u8 bufp[0];
-} __packed;
-
-struct wmi_comb_phyerr_rx_hdr {
- /* Phy error phy error count */
- __le32 num_phyerr_events;
+struct wmi_phyerr_event {
+ __le32 num_phyerrs;
__le32 tsf_l32;
__le32 tsf_u32;
-} __packed;
-
-struct wmi_comb_phyerr_rx_event {
- /* Phy error phy error count */
- struct wmi_comb_phyerr_rx_hdr hdr;
- /*
- * frame buffer - contains multiple payloads in the order:
- * header - payload, header - payload...
- * (The header is of type: wmi_single_phyerr_rx_hdr)
- */
- u8 bufp[0];
+ struct wmi_phyerr phyerrs[0];
} __packed;
#define PHYERR_TLV_SIG 0xBB
@@ -2908,11 +2765,6 @@
WMI_TP_SCALE_SIZE = 5, /* max num of enum */
};
-struct wmi_set_channel_cmd {
- /* channel (only frequency and mode info are used) */
- struct wmi_channel chan;
-} __packed;
-
struct wmi_pdev_chanlist_update_event {
/* number of channels */
__le32 num_chan;
@@ -2943,6 +2795,10 @@
struct wmi_channel chan;
} __packed;
+struct wmi_pdev_pktlog_enable_cmd {
+ __le32 ev_bitmap;
+} __packed;
+
/* Customize the DSCP (bit) to TID (0-7) mapping for QOS */
#define WMI_DSCP_MAP_MAX (64)
struct wmi_pdev_set_dscp_tid_map_cmd {
@@ -3177,7 +3033,7 @@
* PDEV statistics
* TODO: add all PDEV stats here
*/
-struct wmi_pdev_stats_old {
+struct wmi_pdev_stats {
__le32 chan_nf; /* Channel noise floor */
__le32 tx_frame_count; /* TX frame count */
__le32 rx_frame_count; /* RX frame count */
@@ -3188,15 +3044,8 @@
struct wal_dbg_stats wal; /* WAL dbg stats */
} __packed;
-struct wmi_pdev_stats_10x {
- __le32 chan_nf; /* Channel noise floor */
- __le32 tx_frame_count; /* TX frame count */
- __le32 rx_frame_count; /* RX frame count */
- __le32 rx_clear_count; /* rx clear count */
- __le32 cycle_count; /* cycle count */
- __le32 phy_err_count; /* Phy error count */
- __le32 chan_tx_pwr; /* channel tx power */
- struct wal_dbg_stats wal; /* WAL dbg stats */
+struct wmi_10x_pdev_stats {
+ struct wmi_pdev_stats old;
__le32 ack_rx_bad;
__le32 rts_bad;
__le32 rts_good;
@@ -3217,16 +3066,14 @@
* peer statistics.
* TODO: add more stats
*/
-struct wmi_peer_stats_old {
+struct wmi_peer_stats {
struct wmi_mac_addr peer_macaddr;
__le32 peer_rssi;
__le32 peer_tx_rate;
} __packed;
-struct wmi_peer_stats_10x {
- struct wmi_mac_addr peer_macaddr;
- __le32 peer_rssi;
- __le32 peer_tx_rate;
+struct wmi_10x_peer_stats {
+ struct wmi_peer_stats old;
__le32 peer_rx_rate;
} __packed;
@@ -4712,7 +4559,6 @@
__le32 config_valid;
} __packed;
-#define ATH10K_RTS_MAX 2347
#define ATH10K_FRAGMT_THRESHOLD_MIN 540
#define ATH10K_FRAGMT_THRESHOLD_MAX 2346
@@ -4723,8 +4569,27 @@
/* By default disable power save for IBSS */
#define ATH10K_DEFAULT_ATIM 0
+#define WMI_MAX_MEM_REQS 16
+
+struct wmi_svc_rdy_ev_arg {
+ __le32 min_tx_power;
+ __le32 max_tx_power;
+ __le32 ht_cap;
+ __le32 vht_cap;
+ __le32 sw_ver0;
+ __le32 sw_ver1;
+ __le32 phy_capab;
+ __le32 num_rf_chains;
+ __le32 eeprom_rd;
+ __le32 num_mem_reqs;
+ const __le32 *service_map;
+ size_t service_map_len;
+ const struct wlan_host_mem_req *mem_reqs[WMI_MAX_MEM_REQS];
+};
+
struct ath10k;
struct ath10k_vif;
+struct ath10k_fw_stats;
int ath10k_wmi_attach(struct ath10k *ar);
void ath10k_wmi_detach(struct ath10k *ar);
@@ -4736,8 +4601,6 @@
struct sk_buff *ath10k_wmi_alloc_skb(struct ath10k *ar, u32 len);
int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id);
-int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
- const struct wmi_channel_arg *);
int ath10k_wmi_pdev_suspend_target(struct ath10k *ar, u32 suspend_opt);
int ath10k_wmi_pdev_resume_target(struct ath10k *ar);
int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g,
@@ -4798,5 +4661,9 @@
enum wmi_force_fw_hang_type type, u32 delay_ms);
int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb);
int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable);
+int ath10k_wmi_pull_fw_stats(struct ath10k *ar, struct sk_buff *skb,
+ struct ath10k_fw_stats *stats);
+int ath10k_wmi_pdev_pktlog_enable(struct ath10k *ar, u32 ev_list);
+int ath10k_wmi_pdev_pktlog_disable(struct ath10k *ar);
#endif /* _WMI_H_ */
diff --git a/drivers/net/wireless/ath/ath5k/Kconfig b/drivers/net/wireless/ath/ath5k/Kconfig
index 614737b..85fe7d4 100644
--- a/drivers/net/wireless/ath/ath5k/Kconfig
+++ b/drivers/net/wireless/ath/ath5k/Kconfig
@@ -1,13 +1,14 @@
config ATH5K
tristate "Atheros 5xxx wireless cards support"
depends on m
- depends on PCI && MAC80211
+ depends on (PCI || ATH25) && MAC80211
select ATH_COMMON
select MAC80211_LEDS
- select BACKPORT_LEDS_CLASS
- select BACKPORT_NEW_LEDS
- select BACKPORT_AVERAGE
- select ATH5K_PCI
+ select BPAUTO_LEDS_CLASS
+ select BPAUTO_NEW_LEDS
+ select BPAUTO_AVERAGE
+ select ATH5K_AHB if ATH25
+ select ATH5K_PCI if !ATH25
---help---
This module adds support for wireless adapters based on
Atheros 5xxx chipset.
@@ -52,9 +53,16 @@
If unsure, say N.
+config ATH5K_AHB
+ bool "Atheros 5xxx AHB bus support"
+ depends on ATH25
+ ---help---
+ This adds support for WiSoC type chipsets of the 5xxx Atheros
+ family.
+
config ATH5K_PCI
bool "Atheros 5xxx PCI bus support"
- depends on PCI
+ depends on (!ATH25 && PCI)
---help---
This adds support for PCI type chipsets of the 5xxx Atheros
family.
diff --git a/drivers/net/wireless/ath/ath5k/Makefile b/drivers/net/wireless/ath/ath5k/Makefile
index 3c9c2ac..32a0f67 100644
--- a/drivers/net/wireless/ath/ath5k/Makefile
+++ b/drivers/net/wireless/ath/ath5k/Makefile
@@ -17,5 +17,6 @@
ath5k-y += sysfs.o
ath5k-y += mac80211-ops.o
ath5k-$(CPTCFG_ATH5K_DEBUG) += debug.o
+ath5k-$(CPTCFG_ATH5K_AHB) += ahb.o
ath5k-$(CPTCFG_ATH5K_PCI) += pci.o
obj-$(CPTCFG_ATH5K) += ath5k.o
diff --git a/drivers/net/wireless/ath/ath5k/ahb.c b/drivers/net/wireless/ath/ath5k/ahb.c
new file mode 100644
index 0000000..8f387cf
--- /dev/null
+++ b/drivers/net/wireless/ath/ath5k/ahb.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
+ * Copyright (c) 2009 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (c) 2009 Imre Kaloz <kaloz@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/nl80211.h>
+#include <linux/platform_device.h>
+#include <linux/etherdevice.h>
+#include <linux/export.h>
+#include <ath25_platform.h>
+#include "ath5k.h"
+#include "debug.h"
+#include "base.h"
+#include "reg.h"
+
+/* return bus cachesize in 4B word units */
+static void ath5k_ahb_read_cachesize(struct ath_common *common, int *csz)
+{
+ *csz = L1_CACHE_BYTES >> 2;
+}
+
+static bool
+ath5k_ahb_eeprom_read(struct ath_common *common, u32 off, u16 *data)
+{
+ struct ath5k_hw *ah = common->priv;
+ struct platform_device *pdev = to_platform_device(ah->dev);
+ struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev);
+ u16 *eeprom, *eeprom_end;
+
+ eeprom = (u16 *) bcfg->radio;
+ eeprom_end = ((void *) bcfg->config) + BOARD_CONFIG_BUFSZ;
+
+ eeprom += off;
+ if (eeprom > eeprom_end)
+ return false;
+
+ *data = *eeprom;
+ return true;
+}
+
+int ath5k_hw_read_srev(struct ath5k_hw *ah)
+{
+ struct platform_device *pdev = to_platform_device(ah->dev);
+ struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev);
+ ah->ah_mac_srev = bcfg->devid;
+ return 0;
+}
+
+static int ath5k_ahb_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac)
+{
+ struct platform_device *pdev = to_platform_device(ah->dev);
+ struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev);
+ u8 *cfg_mac;
+
+ if (to_platform_device(ah->dev)->id == 0)
+ cfg_mac = bcfg->config->wlan0_mac;
+ else
+ cfg_mac = bcfg->config->wlan1_mac;
+
+ memcpy(mac, cfg_mac, ETH_ALEN);
+ return 0;
+}
+
+static const struct ath_bus_ops ath_ahb_bus_ops = {
+ .ath_bus_type = ATH_AHB,
+ .read_cachesize = ath5k_ahb_read_cachesize,
+ .eeprom_read = ath5k_ahb_eeprom_read,
+ .eeprom_read_mac = ath5k_ahb_eeprom_read_mac,
+};
+
+/*Initialization*/
+static int ath_ahb_probe(struct platform_device *pdev)
+{
+ struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev);
+ struct ath5k_hw *ah;
+ struct ieee80211_hw *hw;
+ struct resource *res;
+ void __iomem *mem;
+ int irq;
+ int ret = 0;
+ u32 reg;
+
+ if (!dev_get_platdata(&pdev->dev)) {
+ dev_err(&pdev->dev, "no platform data specified\n");
+ ret = -EINVAL;
+ goto err_out;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "no memory resource found\n");
+ ret = -ENXIO;
+ goto err_out;
+ }
+
+ mem = ioremap_nocache(res->start, resource_size(res));
+ if (mem == NULL) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ ret = -ENOMEM;
+ goto err_out;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "no IRQ resource found\n");
+ ret = -ENXIO;
+ goto err_iounmap;
+ }
+
+ irq = res->start;
+
+ hw = ieee80211_alloc_hw(sizeof(struct ath5k_hw), &ath5k_hw_ops);
+ if (hw == NULL) {
+ dev_err(&pdev->dev, "no memory for ieee80211_hw\n");
+ ret = -ENOMEM;
+ goto err_iounmap;
+ }
+
+ ah = hw->priv;
+ ah->hw = hw;
+ ah->dev = &pdev->dev;
+ ah->iobase = mem;
+ ah->irq = irq;
+ ah->devid = bcfg->devid;
+
+ if (bcfg->devid >= AR5K_SREV_AR2315_R6) {
+ /* Enable WMAC AHB arbitration */
+ reg = ioread32((void __iomem *) AR5K_AR2315_AHB_ARB_CTL);
+ reg |= AR5K_AR2315_AHB_ARB_CTL_WLAN;
+ iowrite32(reg, (void __iomem *) AR5K_AR2315_AHB_ARB_CTL);
+
+ /* Enable global WMAC swapping */
+ reg = ioread32((void __iomem *) AR5K_AR2315_BYTESWAP);
+ reg |= AR5K_AR2315_BYTESWAP_WMAC;
+ iowrite32(reg, (void __iomem *) AR5K_AR2315_BYTESWAP);
+ } else {
+ /* Enable WMAC DMA access (assuming 5312 or 231x*/
+ /* TODO: check other platforms */
+ reg = ioread32((void __iomem *) AR5K_AR5312_ENABLE);
+ if (to_platform_device(ah->dev)->id == 0)
+ reg |= AR5K_AR5312_ENABLE_WLAN0;
+ else
+ reg |= AR5K_AR5312_ENABLE_WLAN1;
+ iowrite32(reg, (void __iomem *) AR5K_AR5312_ENABLE);
+
+ /*
+ * On a dual-band AR5312, the multiband radio is only
+ * used as pass-through. Disable 2 GHz support in the
+ * driver for it
+ */
+ if (to_platform_device(ah->dev)->id == 0 &&
+ (bcfg->config->flags & (BD_WLAN0 | BD_WLAN1)) ==
+ (BD_WLAN1 | BD_WLAN0))
+ ah->ah_capabilities.cap_needs_2GHz_ovr = true;
+ else
+ ah->ah_capabilities.cap_needs_2GHz_ovr = false;
+ }
+
+ ret = ath5k_init_ah(ah, &ath_ahb_bus_ops);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "failed to attach device, err=%d\n", ret);
+ ret = -ENODEV;
+ goto err_free_hw;
+ }
+
+ platform_set_drvdata(pdev, hw);
+
+ return 0;
+
+ err_free_hw:
+ ieee80211_free_hw(hw);
+ err_iounmap:
+ iounmap(mem);
+ err_out:
+ return ret;
+}
+
+static int ath_ahb_remove(struct platform_device *pdev)
+{
+ struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev);
+ struct ieee80211_hw *hw = platform_get_drvdata(pdev);
+ struct ath5k_hw *ah;
+ u32 reg;
+
+ if (!hw)
+ return 0;
+
+ ah = hw->priv;
+
+ if (bcfg->devid >= AR5K_SREV_AR2315_R6) {
+ /* Disable WMAC AHB arbitration */
+ reg = ioread32((void __iomem *) AR5K_AR2315_AHB_ARB_CTL);
+ reg &= ~AR5K_AR2315_AHB_ARB_CTL_WLAN;
+ iowrite32(reg, (void __iomem *) AR5K_AR2315_AHB_ARB_CTL);
+ } else {
+ /*Stop DMA access */
+ reg = ioread32((void __iomem *) AR5K_AR5312_ENABLE);
+ if (to_platform_device(ah->dev)->id == 0)
+ reg &= ~AR5K_AR5312_ENABLE_WLAN0;
+ else
+ reg &= ~AR5K_AR5312_ENABLE_WLAN1;
+ iowrite32(reg, (void __iomem *) AR5K_AR5312_ENABLE);
+ }
+
+ ath5k_deinit_ah(ah);
+ iounmap(ah->iobase);
+ ieee80211_free_hw(hw);
+
+ return 0;
+}
+
+static struct platform_driver ath_ahb_driver = {
+ .probe = ath_ahb_probe,
+ .remove = ath_ahb_remove,
+ .driver = {
+ .name = "ar231x-wmac",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(ath_ahb_driver);
diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h
index e43e883..cc701f1 100644
--- a/drivers/net/wireless/ath/ath5k/ath5k.h
+++ b/drivers/net/wireless/ath/ath5k/ath5k.h
@@ -1647,6 +1647,32 @@
return &(ath5k_hw_common(ah)->regulatory);
}
+#ifdef CPTCFG_ATH5K_AHB
+#define AR5K_AR2315_PCI_BASE ((void __iomem *)0xb0100000)
+
+static inline void __iomem *ath5k_ahb_reg(struct ath5k_hw *ah, u16 reg)
+{
+ /* On AR2315 and AR2317 the PCI clock domain registers
+ * are outside of the WMAC register space */
+ if (unlikely((reg >= 0x4000) && (reg < 0x5000) &&
+ (ah->ah_mac_srev >= AR5K_SREV_AR2315_R6)))
+ return AR5K_AR2315_PCI_BASE + reg;
+
+ return ah->iobase + reg;
+}
+
+static inline u32 ath5k_hw_reg_read(struct ath5k_hw *ah, u16 reg)
+{
+ return ioread32(ath5k_ahb_reg(ah, reg));
+}
+
+static inline void ath5k_hw_reg_write(struct ath5k_hw *ah, u32 val, u16 reg)
+{
+ iowrite32(val, ath5k_ahb_reg(ah, reg));
+}
+
+#else
+
static inline u32 ath5k_hw_reg_read(struct ath5k_hw *ah, u16 reg)
{
return ioread32(ah->iobase + reg);
@@ -1657,6 +1683,8 @@
iowrite32(val, ah->iobase + reg);
}
+#endif
+
static inline enum ath_bus_type ath5k_get_bus_type(struct ath5k_hw *ah)
{
return ath5k_hw_common(ah)->bus_ops->ath_bus_type;
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index 3f5fae0..65cc4b3 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -99,6 +99,15 @@
/* Known SREVs */
static const struct ath5k_srev_name srev_names[] = {
+#ifdef CPTCFG_ATH5K_AHB
+ { "5312", AR5K_VERSION_MAC, AR5K_SREV_AR5312_R2 },
+ { "5312", AR5K_VERSION_MAC, AR5K_SREV_AR5312_R7 },
+ { "2313", AR5K_VERSION_MAC, AR5K_SREV_AR2313_R8 },
+ { "2315", AR5K_VERSION_MAC, AR5K_SREV_AR2315_R6 },
+ { "2315", AR5K_VERSION_MAC, AR5K_SREV_AR2315_R7 },
+ { "2317", AR5K_VERSION_MAC, AR5K_SREV_AR2317_R1 },
+ { "2317", AR5K_VERSION_MAC, AR5K_SREV_AR2317_R2 },
+#else
{ "5210", AR5K_VERSION_MAC, AR5K_SREV_AR5210 },
{ "5311", AR5K_VERSION_MAC, AR5K_SREV_AR5311 },
{ "5311A", AR5K_VERSION_MAC, AR5K_SREV_AR5311A },
@@ -117,6 +126,7 @@
{ "5418", AR5K_VERSION_MAC, AR5K_SREV_AR5418 },
{ "2425", AR5K_VERSION_MAC, AR5K_SREV_AR2425 },
{ "2417", AR5K_VERSION_MAC, AR5K_SREV_AR2417 },
+#endif
{ "xxxxx", AR5K_VERSION_MAC, AR5K_SREV_UNKNOWN },
{ "5110", AR5K_VERSION_RAD, AR5K_SREV_RAD_5110 },
{ "5111", AR5K_VERSION_RAD, AR5K_SREV_RAD_5111 },
@@ -132,6 +142,10 @@
{ "5413", AR5K_VERSION_RAD, AR5K_SREV_RAD_5413 },
{ "5424", AR5K_VERSION_RAD, AR5K_SREV_RAD_5424 },
{ "5133", AR5K_VERSION_RAD, AR5K_SREV_RAD_5133 },
+#ifdef CPTCFG_ATH5K_AHB
+ { "2316", AR5K_VERSION_RAD, AR5K_SREV_RAD_2316 },
+ { "2317", AR5K_VERSION_RAD, AR5K_SREV_RAD_2317 },
+#endif
{ "xxxxx", AR5K_VERSION_RAD, AR5K_SREV_UNKNOWN },
};
diff --git a/drivers/net/wireless/ath/ath5k/led.c b/drivers/net/wireless/ath/ath5k/led.c
index 0beb7e7..201d1de 100644
--- a/drivers/net/wireless/ath/ath5k/led.c
+++ b/drivers/net/wireless/ath/ath5k/led.c
@@ -163,14 +163,20 @@
{
int ret = 0;
struct ieee80211_hw *hw = ah->hw;
+#ifndef CPTCFG_ATH5K_AHB
struct pci_dev *pdev = ah->pdev;
+#endif
char name[ATH5K_LED_MAX_NAME_LEN + 1];
const struct pci_device_id *match;
if (!ah->pdev)
return 0;
+#ifdef CPTCFG_ATH5K_AHB
+ match = NULL;
+#else
match = pci_match_id(&ath5k_led_devices[0], pdev);
+#endif
if (match) {
__set_bit(ATH_STAT_LEDSOFT, ah->status);
ah->led_pin = ATH_PIN(match->driver_data);
diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
index ab2709a..19eab2a 100644
--- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c
+++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
@@ -547,7 +547,9 @@
static void
-ath5k_sw_scan_start(struct ieee80211_hw *hw)
+ath5k_sw_scan_start(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ const u8 *mac_addr)
{
struct ath5k_hw *ah = hw->priv;
if (!ah->assoc)
@@ -556,7 +558,7 @@
static void
-ath5k_sw_scan_complete(struct ieee80211_hw *hw)
+ath5k_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
struct ath5k_hw *ah = hw->priv;
ath5k_hw_set_ledstate(ah, ah->assoc ?
diff --git a/drivers/net/wireless/ath/ath5k/qcu.c b/drivers/net/wireless/ath/ath5k/qcu.c
index 0583c69..ddaad71 100644
--- a/drivers/net/wireless/ath/ath5k/qcu.c
+++ b/drivers/net/wireless/ath/ath5k/qcu.c
@@ -225,13 +225,7 @@
} else {
switch (queue_type) {
case AR5K_TX_QUEUE_DATA:
- for (queue = AR5K_TX_QUEUE_ID_DATA_MIN;
- ah->ah_txq[queue].tqi_type !=
- AR5K_TX_QUEUE_INACTIVE; queue++) {
-
- if (queue > AR5K_TX_QUEUE_ID_DATA_MAX)
- return -EINVAL;
- }
+ queue = queue_info->tqi_subtype;
break;
case AR5K_TX_QUEUE_UAPSD:
queue = AR5K_TX_QUEUE_ID_UAPSD;
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
index 04d3c2e..03799d2 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
@@ -2976,11 +2976,11 @@
static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev,
- const u8 *mac)
+ struct station_del_parameters *params)
{
struct ath6kl *ar = ath6kl_priv(dev);
struct ath6kl_vif *vif = netdev_priv(dev);
- const u8 *addr = mac ? mac : bcast_addr;
+ const u8 *addr = params->mac ? params->mac : bcast_addr;
return ath6kl_wmi_ap_set_mlme(ar->wmi, vif->fw_vif_idx, WMI_AP_DEAUTH,
addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
diff --git a/drivers/net/wireless/ath/ath6kl/common.h b/drivers/net/wireless/ath/ath6kl/common.h
index 05debf7..4f82e86 100644
--- a/drivers/net/wireless/ath/ath6kl/common.h
+++ b/drivers/net/wireless/ath/ath6kl/common.h
@@ -22,7 +22,7 @@
#define ATH6KL_MAX_IE 256
-__printf(2, 3) int ath6kl_printk(const char *level, const char *fmt, ...);
+__printf(2, 3) void ath6kl_printk(const char *level, const char *fmt, ...);
/*
* Reflects the version of binary interface exposed by ATH6KL target
diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c
index f596535..fc2b18a 100644
--- a/drivers/net/wireless/ath/ath6kl/debug.c
+++ b/drivers/net/wireless/ath/ath6kl/debug.c
@@ -37,76 +37,64 @@
#define ATH6KL_FWLOG_VALID_MASK 0x1ffff
-int ath6kl_printk(const char *level, const char *fmt, ...)
+void ath6kl_printk(const char *level, const char *fmt, ...)
{
struct va_format vaf;
va_list args;
- int rtn;
va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
- rtn = printk("%sath6kl: %pV", level, &vaf);
+ printk("%sath6kl: %pV", level, &vaf);
va_end(args);
-
- return rtn;
}
EXPORT_SYMBOL(ath6kl_printk);
-int ath6kl_info(const char *fmt, ...)
+void ath6kl_info(const char *fmt, ...)
{
struct va_format vaf = {
.fmt = fmt,
};
va_list args;
- int ret;
va_start(args, fmt);
vaf.va = &args;
- ret = ath6kl_printk(KERN_INFO, "%pV", &vaf);
+ ath6kl_printk(KERN_INFO, "%pV", &vaf);
trace_ath6kl_log_info(&vaf);
va_end(args);
-
- return ret;
}
EXPORT_SYMBOL(ath6kl_info);
-int ath6kl_err(const char *fmt, ...)
+void ath6kl_err(const char *fmt, ...)
{
struct va_format vaf = {
.fmt = fmt,
};
va_list args;
- int ret;
va_start(args, fmt);
vaf.va = &args;
- ret = ath6kl_printk(KERN_ERR, "%pV", &vaf);
+ ath6kl_printk(KERN_ERR, "%pV", &vaf);
trace_ath6kl_log_err(&vaf);
va_end(args);
-
- return ret;
}
EXPORT_SYMBOL(ath6kl_err);
-int ath6kl_warn(const char *fmt, ...)
+void ath6kl_warn(const char *fmt, ...)
{
struct va_format vaf = {
.fmt = fmt,
};
va_list args;
- int ret;
va_start(args, fmt);
vaf.va = &args;
- ret = ath6kl_printk(KERN_WARNING, "%pV", &vaf);
+ ath6kl_printk(KERN_WARNING, "%pV", &vaf);
trace_ath6kl_log_warn(&vaf);
va_end(args);
-
- return ret;
}
EXPORT_SYMBOL(ath6kl_warn);
diff --git a/drivers/net/wireless/ath/ath6kl/debug.h b/drivers/net/wireless/ath/ath6kl/debug.h
index 01debd4..5356e42 100644
--- a/drivers/net/wireless/ath/ath6kl/debug.h
+++ b/drivers/net/wireless/ath/ath6kl/debug.h
@@ -50,10 +50,10 @@
};
extern unsigned int debug_mask;
-__printf(2, 3) int ath6kl_printk(const char *level, const char *fmt, ...);
-__printf(1, 2) int ath6kl_info(const char *fmt, ...);
-__printf(1, 2) int ath6kl_err(const char *fmt, ...);
-__printf(1, 2) int ath6kl_warn(const char *fmt, ...);
+__printf(2, 3) void ath6kl_printk(const char *level, const char *fmt, ...);
+__printf(1, 2) void ath6kl_info(const char *fmt, ...);
+__printf(1, 2) void ath6kl_err(const char *fmt, ...);
+__printf(1, 2) void ath6kl_warn(const char *fmt, ...);
enum ath6kl_war {
ATH6KL_WAR_INVALID_RATE,
@@ -81,10 +81,9 @@
void ath6kl_debug_cleanup(struct ath6kl *ar);
#else
-static inline int ath6kl_dbg(enum ATH6K_DEBUG_MASK dbg_mask,
- const char *fmt, ...)
+static inline void ath6kl_dbg(enum ATH6K_DEBUG_MASK dbg_mask,
+ const char *fmt, ...)
{
- return 0;
}
static inline void ath6kl_dbg_dump(enum ATH6K_DEBUG_MASK mask,
diff --git a/drivers/net/wireless/ath/ath6kl/usb.c b/drivers/net/wireless/ath/ath6kl/usb.c
index af439e7..a0b89b5 100644
--- a/drivers/net/wireless/ath/ath6kl/usb.c
+++ b/drivers/net/wireless/ath/ath6kl/usb.c
@@ -1193,18 +1193,10 @@
return 0;
}
-static int ath6kl_usb_pm_reset_resume(struct usb_interface *intf)
-{
- if (usb_get_intfdata(intf))
- ath6kl_usb_remove(intf);
- return 0;
-}
-
#else
#define ath6kl_usb_pm_suspend NULL
#define ath6kl_usb_pm_resume NULL
-#define ath6kl_usb_pm_reset_resume NULL
#endif
@@ -1222,7 +1214,6 @@
.probe = ath6kl_usb_probe,
.suspend = ath6kl_usb_pm_suspend,
.resume = ath6kl_usb_pm_resume,
- .reset_resume = ath6kl_usb_pm_reset_resume,
.disconnect = ath6kl_usb_remove,
.id_table = ath6kl_usb_ids,
.supports_autosuspend = true,
diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig
index 1acecc2..9d490f2 100644
--- a/drivers/net/wireless/ath/ath9k/Kconfig
+++ b/drivers/net/wireless/ath/ath9k/Kconfig
@@ -5,6 +5,8 @@
tristate
depends on m
select ATH_COMMON
+ depends on DEBUG_FS
+ depends on RELAY
config ATH9K_DFS_DEBUGFS
def_bool y
depends on ATH9K_DEBUGFS && ATH9K_DFS_CERTIFIED
@@ -23,8 +25,8 @@
depends on MAC80211 && HAS_DMA
select ATH9K_HW
select MAC80211_LEDS
- select BACKPORT_LEDS_CLASS
- select BACKPORT_NEW_LEDS
+ select BPAUTO_LEDS_CLASS
+ select BPAUTO_NEW_LEDS
select ATH9K_COMMON
---help---
This module adds support for wireless adapters based on
@@ -151,14 +153,19 @@
for multi-channel concurrency. Enable this if P2P PowerSave support
is required.
+config ATH9K_PCOEM
+ bool "Atheros ath9k support for PC OEM cards" if EXPERT
+ depends on ATH9K
+ default y
+
config ATH9K_HTC
tristate "Atheros HTC based wireless cards support"
depends on m
depends on USB && MAC80211
select ATH9K_HW
select MAC80211_LEDS
- select BACKPORT_LEDS_CLASS
- select BACKPORT_NEW_LEDS
+ select BPAUTO_LEDS_CLASS
+ select BPAUTO_NEW_LEDS
select ATH9K_COMMON
---help---
Support for Atheros HTC based cards.
diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile
index ccee747..18e21a8 100644
--- a/drivers/net/wireless/ath/ath9k/Makefile
+++ b/drivers/net/wireless/ath/ath9k/Makefile
@@ -16,8 +16,7 @@
ath9k-$(CPTCFG_ATH9K_TX99) += tx99.o
ath9k-$(CPTCFG_ATH9K_WOW) += wow.o
-ath9k-$(CPTCFG_ATH9K_DEBUGFS) += debug.o \
- spectral.o
+ath9k-$(CPTCFG_ATH9K_DEBUGFS) += debug.o
ath9k-$(CPTCFG_ATH9K_STATION_STATISTICS) += debug_sta.o
@@ -32,7 +31,6 @@
ar5008_phy.o \
ar9002_calib.o \
ar9003_calib.o \
- ar9003_rtt.o \
calib.o \
eeprom.o \
eeprom_def.o \
@@ -50,6 +48,8 @@
ath9k_hw-$(CPTCFG_ATH9K_BTCOEX_SUPPORT) += btcoex.o \
ar9003_mci.o
+ath9k_hw-$(CPTCFG_ATH9K_PCOEM) += ar9003_rtt.o
+
ath9k_hw-$(CPTCFG_ATH9K_DYNACK) += dynack.o
obj-$(CPTCFG_ATH9K_HW) += ath9k_hw.o
@@ -58,7 +58,8 @@
ath9k_common-y:= common.o \
common-init.o \
common-beacon.o \
- common-debug.o
+ common-debug.o \
+ common-spectral.o
ath9k_htc-y += htc_hst.o \
hif_usb.o \
diff --git a/drivers/net/wireless/ath/ath9k/ahb.c b/drivers/net/wireless/ath/ath9k/ahb.c
index 4173838..e000c4c 100644
--- a/drivers/net/wireless/ath/ath9k/ahb.c
+++ b/drivers/net/wireless/ath/ath9k/ahb.c
@@ -175,7 +175,6 @@
.remove = ath_ahb_remove,
.driver = {
.name = "ath9k",
- .owner = THIS_MODULE,
},
.id_table = ath9k_platform_id_table,
};
diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
index b72d0be..5829074 100644
--- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
@@ -1190,7 +1190,7 @@
static void ar5008_hw_set_radar_params(struct ath_hw *ah,
struct ath_hw_radar_conf *conf)
{
- u32 radar_0 = 0, radar_1 = 0;
+ u32 radar_0 = 0, radar_1;
if (!conf) {
REG_CLR_BIT(ah, AR_PHY_RADAR_0, AR_PHY_RADAR_0_ENA);
@@ -1204,6 +1204,9 @@
radar_0 |= SM(conf->pulse_rssi, AR_PHY_RADAR_0_PRSSI);
radar_0 |= SM(conf->pulse_inband, AR_PHY_RADAR_0_INBAND);
+ radar_1 = REG_READ(ah, AR_PHY_RADAR_1);
+ radar_1 &= ~(AR_PHY_RADAR_1_MAXLEN | AR_PHY_RADAR_1_RELSTEP_THRESH |
+ AR_PHY_RADAR_1_RELPWR_THRESH);
radar_1 |= AR_PHY_RADAR_1_MAX_RRSSI;
radar_1 |= AR_PHY_RADAR_1_BLOCK_CHECK;
radar_1 |= SM(conf->pulse_maxlen, AR_PHY_RADAR_1_MAXLEN);
@@ -1225,7 +1228,7 @@
conf->fir_power = -33;
conf->radar_rssi = 20;
conf->pulse_height = 10;
- conf->pulse_rssi = 24;
+ conf->pulse_rssi = 15;
conf->pulse_inband = 15;
conf->pulse_maxlen = 255;
conf->pulse_inband_step = 12;
diff --git a/drivers/net/wireless/ath/ath9k/ar9002_calib.c b/drivers/net/wireless/ath/ath9k/ar9002_calib.c
index cdc7400..42190b6 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_calib.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_calib.c
@@ -657,31 +657,29 @@
ar9280_hw_olc_temp_compensation(ah);
}
-static bool ar9002_hw_calibrate(struct ath_hw *ah,
- struct ath9k_channel *chan,
- u8 rxchainmask,
- bool longcal)
+static int ar9002_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan,
+ u8 rxchainmask, bool longcal)
{
- bool iscaldone = true;
struct ath9k_cal_list *currCal = ah->cal_list_curr;
- bool nfcal, nfcal_pending = false;
+ bool nfcal, nfcal_pending = false, percal_pending;
+ int ret;
nfcal = !!(REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF);
if (ah->caldata)
nfcal_pending = test_bit(NFCAL_PENDING, &ah->caldata->cal_flags);
- if (currCal && !nfcal &&
- (currCal->calState == CAL_RUNNING ||
- currCal->calState == CAL_WAITING)) {
- iscaldone = ar9002_hw_per_calibration(ah, chan,
- rxchainmask, currCal);
- if (iscaldone) {
- ah->cal_list_curr = currCal = currCal->calNext;
+ percal_pending = (currCal &&
+ (currCal->calState == CAL_RUNNING ||
+ currCal->calState == CAL_WAITING));
- if (currCal->calState == CAL_WAITING) {
- iscaldone = false;
- ath9k_hw_reset_calibration(ah, currCal);
- }
+ if (percal_pending && !nfcal) {
+ if (!ar9002_hw_per_calibration(ah, chan, rxchainmask, currCal))
+ return 0;
+
+ ah->cal_list_curr = currCal = currCal->calNext;
+ if (currCal->calState == CAL_WAITING) {
+ ath9k_hw_reset_calibration(ah, currCal);
+ return 0;
}
}
@@ -698,7 +696,9 @@
* NF is slow time-variant, so it is OK to use a
* historical value.
*/
- ath9k_hw_loadnf(ah, ah->curchan);
+ ret = ath9k_hw_loadnf(ah, ah->curchan);
+ if (ret < 0)
+ return ret;
}
if (longcal) {
@@ -709,7 +709,7 @@
}
}
- return iscaldone;
+ return !percal_pending;
}
/* Carrier leakage Calibration fix */
@@ -856,6 +856,8 @@
/* Do PA Calibration */
ar9002_hw_pa_cal(ah, true);
+ ath9k_hw_loadnf(ah, chan);
+ ath9k_hw_start_nfcal(ah, true);
if (ah->caldata)
set_bit(NFCAL_PENDING, &ah->caldata->cal_flags);
diff --git a/drivers/net/wireless/ath/ath9k/ar9002_mac.c b/drivers/net/wireless/ath/ath9k/ar9002_mac.c
index 2a93519..f816909 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_mac.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_mac.c
@@ -281,7 +281,7 @@
ACCESS_ONCE(ads->ds_ctl0) = (i->pkt_len & AR_FrameLen)
| (i->flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0)
- | SM(i->txpower, AR_XmitPower0)
+ | SM(i->txpower[0], AR_XmitPower0)
| (i->flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0)
| (i->flags & ATH9K_TXDESC_INTREQ ? AR_TxIntrReq : 0)
| (i->keyix != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0)
@@ -307,9 +307,9 @@
| set11nRateFlags(i->rates, 3)
| SM(i->rtscts_rate, AR_RTSCTSRate);
- ACCESS_ONCE(ads->ds_ctl9) = SM(i->txpower, AR_XmitPower1);
- ACCESS_ONCE(ads->ds_ctl10) = SM(i->txpower, AR_XmitPower2);
- ACCESS_ONCE(ads->ds_ctl11) = SM(i->txpower, AR_XmitPower3);
+ ACCESS_ONCE(ads->ds_ctl9) = SM(i->txpower[1], AR_XmitPower1);
+ ACCESS_ONCE(ads->ds_ctl10) = SM(i->txpower[2], AR_XmitPower2);
+ ACCESS_ONCE(ads->ds_ctl11) = SM(i->txpower[3], AR_XmitPower3);
}
static int ar9002_hw_proc_txdesc(struct ath_hw *ah, void *ds,
diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.c b/drivers/net/wireless/ath/ath9k/ar9002_phy.c
index 5d71361..757e0e0 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.c
@@ -643,9 +643,12 @@
* and fix otherwise.
*/
count = param->count;
- if (param->endless)
- count = 0x80;
- else if (count & 0x80)
+ if (param->endless) {
+ if (AR_SREV_9271(ah))
+ count = 0;
+ else
+ count = 0x80;
+ } else if (count & 0x80)
count = 0x7f;
REG_RMW_FIELD(ah, AR_PHY_SPECTRAL_SCAN,
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c
index ac8301e..06ab71d 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c
@@ -121,13 +121,12 @@
return iscaldone;
}
-static bool ar9003_hw_calibrate(struct ath_hw *ah,
- struct ath9k_channel *chan,
- u8 rxchainmask,
- bool longcal)
+static int ar9003_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan,
+ u8 rxchainmask, bool longcal)
{
bool iscaldone = true;
struct ath9k_cal_list *currCal = ah->cal_list_curr;
+ int ret;
/*
* For given calibration:
@@ -163,7 +162,9 @@
* NF is slow time-variant, so it is OK to use a historical
* value.
*/
- ath9k_hw_loadnf(ah, ah->curchan);
+ ret = ath9k_hw_loadnf(ah, ah->curchan);
+ if (ret < 0)
+ return ret;
/* start NF calibration, without updating BB NF register */
ath9k_hw_start_nfcal(ah, false);
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
index ceafa9a..278afac 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
@@ -4079,27 +4079,28 @@
static void ar9003_hw_thermometer_apply(struct ath_hw *ah)
{
+ struct ath9k_hw_capabilities *pCap = &ah->caps;
int thermometer = ar9003_hw_get_thermometer(ah);
u8 therm_on = (thermometer < 0) ? 0 : 1;
REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_RXTX4,
AR_PHY_65NM_CH0_RXTX4_THERM_ON_OVR, therm_on);
- if (ah->caps.tx_chainmask & BIT(1))
+ if (pCap->chip_chainmask & BIT(1))
REG_RMW_FIELD(ah, AR_PHY_65NM_CH1_RXTX4,
AR_PHY_65NM_CH0_RXTX4_THERM_ON_OVR, therm_on);
- if (ah->caps.tx_chainmask & BIT(2))
+ if (pCap->chip_chainmask & BIT(2))
REG_RMW_FIELD(ah, AR_PHY_65NM_CH2_RXTX4,
AR_PHY_65NM_CH0_RXTX4_THERM_ON_OVR, therm_on);
therm_on = (thermometer < 0) ? 0 : (thermometer == 0);
REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_RXTX4,
AR_PHY_65NM_CH0_RXTX4_THERM_ON, therm_on);
- if (ah->caps.tx_chainmask & BIT(1)) {
+ if (pCap->chip_chainmask & BIT(1)) {
therm_on = (thermometer < 0) ? 0 : (thermometer == 1);
REG_RMW_FIELD(ah, AR_PHY_65NM_CH1_RXTX4,
AR_PHY_65NM_CH0_RXTX4_THERM_ON, therm_on);
}
- if (ah->caps.tx_chainmask & BIT(2)) {
+ if (pCap->chip_chainmask & BIT(2)) {
therm_on = (thermometer < 0) ? 0 : (thermometer == 2);
REG_RMW_FIELD(ah, AR_PHY_65NM_CH2_RXTX4,
AR_PHY_65NM_CH0_RXTX4_THERM_ON, therm_on);
@@ -4376,6 +4377,25 @@
targetPowerArray, numPiers);
}
+static void ar9003_hw_selfgen_tpc_txpower(struct ath_hw *ah,
+ struct ath9k_channel *chan,
+ u8 *pwr_array)
+{
+ u32 val;
+
+ /* target power values for self generated frames (ACK,RTS/CTS) */
+ if (IS_CHAN_2GHZ(chan)) {
+ val = SM(pwr_array[ALL_TARGET_LEGACY_1L_5L], AR_TPC_ACK) |
+ SM(pwr_array[ALL_TARGET_LEGACY_1L_5L], AR_TPC_CTS) |
+ SM(0x3f, AR_TPC_CHIRP) | SM(0x3f, AR_TPC_RPT);
+ } else {
+ val = SM(pwr_array[ALL_TARGET_LEGACY_6_24], AR_TPC_ACK) |
+ SM(pwr_array[ALL_TARGET_LEGACY_6_24], AR_TPC_CTS) |
+ SM(0x3f, AR_TPC_CHIRP) | SM(0x3f, AR_TPC_RPT);
+ }
+ REG_WRITE(ah, AR_TPC, val);
+}
+
/* Set tx power registers to array of values passed in */
static int ar9003_hw_tx_power_regwrite(struct ath_hw *ah, u8 * pPwrArray)
{
@@ -5311,6 +5331,7 @@
struct ar9300_modal_eep_header *modal_hdr;
u8 targetPowerValT2[ar9300RateSize];
u8 target_power_val_t2_eep[ar9300RateSize];
+ u8 targetPowerValT2_tpc[ar9300RateSize];
unsigned int i = 0, paprd_scale_factor = 0;
u8 pwr_idx, min_pwridx = 0;
@@ -5362,6 +5383,9 @@
twiceAntennaReduction,
powerLimit);
+ memcpy(targetPowerValT2_tpc, targetPowerValT2,
+ sizeof(targetPowerValT2));
+
if (ar9003_is_paprd_enabled(ah)) {
for (i = 0; i < ar9300RateSize; i++) {
if ((ah->paprd_ratemask & (1 << i)) &&
@@ -5395,6 +5419,30 @@
ar9003_hw_tx_power_regwrite(ah, targetPowerValT2);
ar9003_hw_calibration_apply(ah, chan->channel);
ar9003_paprd_set_txpower(ah, chan, targetPowerValT2);
+
+ ar9003_hw_selfgen_tpc_txpower(ah, chan, targetPowerValT2);
+
+ /* TPC initializations */
+ if (ah->tpc_enabled) {
+ u32 val;
+
+ ar9003_hw_init_rate_txpower(ah, targetPowerValT2_tpc, chan);
+
+ /* Enable TPC */
+ REG_WRITE(ah, AR_PHY_PWRTX_MAX,
+ AR_PHY_POWER_TX_RATE_MAX_TPC_ENABLE);
+ /* Disable per chain power reduction */
+ val = REG_READ(ah, AR_PHY_POWER_TX_SUB);
+ if (AR_SREV_9340(ah))
+ REG_WRITE(ah, AR_PHY_POWER_TX_SUB,
+ val & 0xFFFFFFC0);
+ else
+ REG_WRITE(ah, AR_PHY_POWER_TX_SUB,
+ val & 0xFFFFF000);
+ } else {
+ /* Disable TPC */
+ REG_WRITE(ah, AR_PHY_PWRTX_MAX, 0);
+ }
}
static u16 ath9k_hw_ar9300_get_spur_channel(struct ath_hw *ah,
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c
index ddef9ee..06ad217 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c
@@ -333,12 +333,29 @@
qca953x_1p0_soc_preamble);
INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_POST],
qca953x_1p0_soc_postamble);
- INIT_INI_ARRAY(&ah->iniModesRxGain,
- qca953x_1p0_common_wo_xlna_rx_gain_table);
- INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds,
- qca953x_1p0_common_wo_xlna_rx_gain_bounds);
- INIT_INI_ARRAY(&ah->iniModesTxGain,
- qca953x_1p0_modes_no_xpa_tx_gain_table);
+
+ if (AR_SREV_9531_20(ah)) {
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ qca953x_2p0_common_wo_xlna_rx_gain_table);
+ INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds,
+ qca953x_2p0_common_wo_xlna_rx_gain_bounds);
+ } else {
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ qca953x_1p0_common_wo_xlna_rx_gain_table);
+ INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds,
+ qca953x_1p0_common_wo_xlna_rx_gain_bounds);
+ }
+
+ if (AR_SREV_9531_20(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ qca953x_2p0_modes_no_xpa_tx_gain_table);
+ else if (AR_SREV_9531_11(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ qca953x_1p1_modes_no_xpa_tx_gain_table);
+ else
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ qca953x_1p0_modes_no_xpa_tx_gain_table);
+
INIT_INI_ARRAY(&ah->iniModesFastClock,
qca953x_1p0_modes_fast_clock);
} else if (AR_SREV_9580(ah)) {
@@ -518,9 +535,15 @@
else if (AR_SREV_9550(ah))
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar955x_1p0_modes_xpa_tx_gain_table);
- else if (AR_SREV_9531(ah))
+ else if (AR_SREV_9531_10(ah))
INIT_INI_ARRAY(&ah->iniModesTxGain,
- qca953x_1p0_modes_xpa_tx_gain_table);
+ qca953x_1p0_modes_xpa_tx_gain_table);
+ else if (AR_SREV_9531_11(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ qca953x_1p1_modes_xpa_tx_gain_table);
+ else if (AR_SREV_9531_20(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ qca953x_2p0_modes_xpa_tx_gain_table);
else if (AR_SREV_9580(ah))
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar9580_1p0_lowest_ob_db_tx_gain_table);
@@ -562,7 +585,10 @@
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar955x_1p0_modes_no_xpa_tx_gain_table);
else if (AR_SREV_9531(ah)) {
- if (AR_SREV_9531_11(ah))
+ if (AR_SREV_9531_20(ah))
+ INIT_INI_ARRAY(&ah->iniModesTxGain,
+ qca953x_2p0_modes_no_xpa_tx_gain_table);
+ else if (AR_SREV_9531_11(ah))
INIT_INI_ARRAY(&ah->iniModesTxGain,
qca953x_1p1_modes_no_xpa_tx_gain_table);
else
@@ -670,9 +696,6 @@
if (AR_SREV_9485_11_OR_LATER(ah))
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar9485Modes_green_ob_db_tx_gain_1_1);
- else if (AR_SREV_9340(ah))
- INIT_INI_ARRAY(&ah->iniModesTxGain,
- ar9340Modes_ub124_tx_gain_table_1p0);
else if (AR_SREV_9580(ah))
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar9580_1p0_type5_tx_gain_table);
@@ -792,11 +815,16 @@
ar955x_1p0_common_wo_xlna_rx_gain_table);
INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds,
ar955x_1p0_common_wo_xlna_rx_gain_bounds);
- } else if (AR_SREV_9531(ah)) {
+ } else if (AR_SREV_9531_10(ah) || AR_SREV_9531_11(ah)) {
INIT_INI_ARRAY(&ah->iniModesRxGain,
qca953x_1p0_common_wo_xlna_rx_gain_table);
INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds,
qca953x_1p0_common_wo_xlna_rx_gain_bounds);
+ } else if (AR_SREV_9531_20(ah)) {
+ INIT_INI_ARRAY(&ah->iniModesRxGain,
+ qca953x_2p0_common_wo_xlna_rx_gain_table);
+ INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds,
+ qca953x_2p0_common_wo_xlna_rx_gain_bounds);
} else if (AR_SREV_9580(ah))
INIT_INI_ARRAY(&ah->iniModesRxGain,
ar9580_1p0_wo_xlna_rx_gain_table);
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c
index 057b165..da84b70 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c
@@ -101,7 +101,7 @@
ACCESS_ONCE(ads->ctl11) = (i->pkt_len & AR_FrameLen)
| (i->flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0)
- | SM(i->txpower, AR_XmitPower0)
+ | SM(i->txpower[0], AR_XmitPower0)
| (i->flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0)
| (i->keyix != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0)
| (i->flags & ATH9K_TXDESC_LOWRXCHAIN ? AR_LowRxChain : 0)
@@ -152,9 +152,9 @@
ACCESS_ONCE(ads->ctl19) = AR_Not_Sounding;
- ACCESS_ONCE(ads->ctl20) = SM(i->txpower, AR_XmitPower1);
- ACCESS_ONCE(ads->ctl21) = SM(i->txpower, AR_XmitPower2);
- ACCESS_ONCE(ads->ctl22) = SM(i->txpower, AR_XmitPower3);
+ ACCESS_ONCE(ads->ctl20) = SM(i->txpower[1], AR_XmitPower1);
+ ACCESS_ONCE(ads->ctl21) = SM(i->txpower[2], AR_XmitPower2);
+ ACCESS_ONCE(ads->ctl22) = SM(i->txpower[3], AR_XmitPower3);
}
static u16 ar9003_calc_ptr_chksum(struct ar9003_txc *ads)
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
index fbe3059..d20a39e 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
@@ -18,6 +18,21 @@
#include "hw.h"
#include "ar9003_phy.h"
+#define AR9300_OFDM_RATES 8
+#define AR9300_HT_SS_RATES 8
+#define AR9300_HT_DS_RATES 8
+#define AR9300_HT_TS_RATES 8
+
+#define AR9300_11NA_OFDM_SHIFT 0
+#define AR9300_11NA_HT_SS_SHIFT 8
+#define AR9300_11NA_HT_DS_SHIFT 16
+#define AR9300_11NA_HT_TS_SHIFT 24
+
+#define AR9300_11NG_OFDM_SHIFT 4
+#define AR9300_11NG_HT_SS_SHIFT 12
+#define AR9300_11NG_HT_DS_SHIFT 20
+#define AR9300_11NG_HT_TS_SHIFT 28
+
static const int firstep_table[] =
/* level: 0 1 2 3 4 5 6 7 8 */
{ -4, -2, 0, 2, 4, 6, 8, 10, 12 }; /* lvl 0-8, default 2 */
@@ -40,6 +55,71 @@
static const int m1ThreshExt_off = 127;
static const int m2ThreshExt_off = 127;
+static const u8 ofdm2pwr[] = {
+ ALL_TARGET_LEGACY_6_24,
+ ALL_TARGET_LEGACY_6_24,
+ ALL_TARGET_LEGACY_6_24,
+ ALL_TARGET_LEGACY_6_24,
+ ALL_TARGET_LEGACY_6_24,
+ ALL_TARGET_LEGACY_36,
+ ALL_TARGET_LEGACY_48,
+ ALL_TARGET_LEGACY_54
+};
+
+static const u8 mcs2pwr_ht20[] = {
+ ALL_TARGET_HT20_0_8_16,
+ ALL_TARGET_HT20_1_3_9_11_17_19,
+ ALL_TARGET_HT20_1_3_9_11_17_19,
+ ALL_TARGET_HT20_1_3_9_11_17_19,
+ ALL_TARGET_HT20_4,
+ ALL_TARGET_HT20_5,
+ ALL_TARGET_HT20_6,
+ ALL_TARGET_HT20_7,
+ ALL_TARGET_HT20_0_8_16,
+ ALL_TARGET_HT20_1_3_9_11_17_19,
+ ALL_TARGET_HT20_1_3_9_11_17_19,
+ ALL_TARGET_HT20_1_3_9_11_17_19,
+ ALL_TARGET_HT20_12,
+ ALL_TARGET_HT20_13,
+ ALL_TARGET_HT20_14,
+ ALL_TARGET_HT20_15,
+ ALL_TARGET_HT20_0_8_16,
+ ALL_TARGET_HT20_1_3_9_11_17_19,
+ ALL_TARGET_HT20_1_3_9_11_17_19,
+ ALL_TARGET_HT20_1_3_9_11_17_19,
+ ALL_TARGET_HT20_20,
+ ALL_TARGET_HT20_21,
+ ALL_TARGET_HT20_22,
+ ALL_TARGET_HT20_23
+};
+
+static const u8 mcs2pwr_ht40[] = {
+ ALL_TARGET_HT40_0_8_16,
+ ALL_TARGET_HT40_1_3_9_11_17_19,
+ ALL_TARGET_HT40_1_3_9_11_17_19,
+ ALL_TARGET_HT40_1_3_9_11_17_19,
+ ALL_TARGET_HT40_4,
+ ALL_TARGET_HT40_5,
+ ALL_TARGET_HT40_6,
+ ALL_TARGET_HT40_7,
+ ALL_TARGET_HT40_0_8_16,
+ ALL_TARGET_HT40_1_3_9_11_17_19,
+ ALL_TARGET_HT40_1_3_9_11_17_19,
+ ALL_TARGET_HT40_1_3_9_11_17_19,
+ ALL_TARGET_HT40_12,
+ ALL_TARGET_HT40_13,
+ ALL_TARGET_HT40_14,
+ ALL_TARGET_HT40_15,
+ ALL_TARGET_HT40_0_8_16,
+ ALL_TARGET_HT40_1_3_9_11_17_19,
+ ALL_TARGET_HT40_1_3_9_11_17_19,
+ ALL_TARGET_HT40_1_3_9_11_17_19,
+ ALL_TARGET_HT40_20,
+ ALL_TARGET_HT40_21,
+ ALL_TARGET_HT40_22,
+ ALL_TARGET_HT40_23,
+};
+
/**
* ar9003_hw_set_channel - set channel on single-chip device
* @ah: atheros hardware structure
@@ -664,6 +744,19 @@
ah->enabled_cals |= TX_CL_CAL;
else
ah->enabled_cals &= ~TX_CL_CAL;
+
+ if (AR_SREV_9340(ah) || AR_SREV_9531(ah) || AR_SREV_9550(ah)) {
+ if (ah->is_clk_25mhz) {
+ REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x17c << 1);
+ REG_WRITE(ah, AR_SLP32_MODE, 0x0010f3d7);
+ REG_WRITE(ah, AR_SLP32_INC, 0x0001e7ae);
+ } else {
+ REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x261 << 1);
+ REG_WRITE(ah, AR_SLP32_MODE, 0x0010f400);
+ REG_WRITE(ah, AR_SLP32_INC, 0x0001e800);
+ }
+ udelay(100);
+ }
}
static void ar9003_hw_prog_ini(struct ath_hw *ah,
@@ -1348,7 +1441,7 @@
struct ath_hw_radar_conf *conf)
{
unsigned int regWrites = 0;
- u32 radar_0 = 0, radar_1 = 0;
+ u32 radar_0 = 0, radar_1;
if (!conf) {
REG_CLR_BIT(ah, AR_PHY_RADAR_0, AR_PHY_RADAR_0_ENA);
@@ -1362,6 +1455,9 @@
radar_0 |= SM(conf->pulse_rssi, AR_PHY_RADAR_0_PRSSI);
radar_0 |= SM(conf->pulse_inband, AR_PHY_RADAR_0_INBAND);
+ radar_1 = REG_READ(ah, AR_PHY_RADAR_1);
+ radar_1 &= ~(AR_PHY_RADAR_1_MAXLEN | AR_PHY_RADAR_1_RELSTEP_THRESH |
+ AR_PHY_RADAR_1_RELPWR_THRESH);
radar_1 |= AR_PHY_RADAR_1_MAX_RRSSI;
radar_1 |= AR_PHY_RADAR_1_BLOCK_CHECK;
radar_1 |= SM(conf->pulse_maxlen, AR_PHY_RADAR_1_MAXLEN);
@@ -1388,7 +1484,7 @@
conf->fir_power = -28;
conf->radar_rssi = 0;
conf->pulse_height = 10;
- conf->pulse_rssi = 24;
+ conf->pulse_rssi = 15;
conf->pulse_inband = 8;
conf->pulse_maxlen = 255;
conf->pulse_inband_step = 12;
@@ -1783,6 +1879,100 @@
ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_14], 0));
}
+static void ar9003_hw_init_txpower_cck(struct ath_hw *ah, u8 *rate_array)
+{
+ ah->tx_power[0] = rate_array[ALL_TARGET_LEGACY_1L_5L];
+ ah->tx_power[1] = rate_array[ALL_TARGET_LEGACY_1L_5L];
+ ah->tx_power[2] = min(rate_array[ALL_TARGET_LEGACY_1L_5L],
+ rate_array[ALL_TARGET_LEGACY_5S]);
+ ah->tx_power[3] = min(rate_array[ALL_TARGET_LEGACY_11L],
+ rate_array[ALL_TARGET_LEGACY_11S]);
+}
+
+static void ar9003_hw_init_txpower_ofdm(struct ath_hw *ah, u8 *rate_array,
+ int offset)
+{
+ int i, j;
+
+ for (i = offset; i < offset + AR9300_OFDM_RATES; i++) {
+ /* OFDM rate to power table idx */
+ j = ofdm2pwr[i - offset];
+ ah->tx_power[i] = rate_array[j];
+ }
+}
+
+static void ar9003_hw_init_txpower_ht(struct ath_hw *ah, u8 *rate_array,
+ int ss_offset, int ds_offset,
+ int ts_offset, bool is_40)
+{
+ int i, j, mcs_idx = 0;
+ const u8 *mcs2pwr = (is_40) ? mcs2pwr_ht40 : mcs2pwr_ht20;
+
+ for (i = ss_offset; i < ss_offset + AR9300_HT_SS_RATES; i++) {
+ j = mcs2pwr[mcs_idx];
+ ah->tx_power[i] = rate_array[j];
+ mcs_idx++;
+ }
+
+ for (i = ds_offset; i < ds_offset + AR9300_HT_DS_RATES; i++) {
+ j = mcs2pwr[mcs_idx];
+ ah->tx_power[i] = rate_array[j];
+ mcs_idx++;
+ }
+
+ for (i = ts_offset; i < ts_offset + AR9300_HT_TS_RATES; i++) {
+ j = mcs2pwr[mcs_idx];
+ ah->tx_power[i] = rate_array[j];
+ mcs_idx++;
+ }
+}
+
+static void ar9003_hw_init_txpower_stbc(struct ath_hw *ah, int ss_offset,
+ int ds_offset, int ts_offset)
+{
+ memcpy(&ah->tx_power_stbc[ss_offset], &ah->tx_power[ss_offset],
+ AR9300_HT_SS_RATES);
+ memcpy(&ah->tx_power_stbc[ds_offset], &ah->tx_power[ds_offset],
+ AR9300_HT_DS_RATES);
+ memcpy(&ah->tx_power_stbc[ts_offset], &ah->tx_power[ts_offset],
+ AR9300_HT_TS_RATES);
+}
+
+void ar9003_hw_init_rate_txpower(struct ath_hw *ah, u8 *rate_array,
+ struct ath9k_channel *chan)
+{
+ if (IS_CHAN_5GHZ(chan)) {
+ ar9003_hw_init_txpower_ofdm(ah, rate_array,
+ AR9300_11NA_OFDM_SHIFT);
+ if (IS_CHAN_HT20(chan) || IS_CHAN_HT40(chan)) {
+ ar9003_hw_init_txpower_ht(ah, rate_array,
+ AR9300_11NA_HT_SS_SHIFT,
+ AR9300_11NA_HT_DS_SHIFT,
+ AR9300_11NA_HT_TS_SHIFT,
+ IS_CHAN_HT40(chan));
+ ar9003_hw_init_txpower_stbc(ah,
+ AR9300_11NA_HT_SS_SHIFT,
+ AR9300_11NA_HT_DS_SHIFT,
+ AR9300_11NA_HT_TS_SHIFT);
+ }
+ } else {
+ ar9003_hw_init_txpower_cck(ah, rate_array);
+ ar9003_hw_init_txpower_ofdm(ah, rate_array,
+ AR9300_11NG_OFDM_SHIFT);
+ if (IS_CHAN_HT20(chan) || IS_CHAN_HT40(chan)) {
+ ar9003_hw_init_txpower_ht(ah, rate_array,
+ AR9300_11NG_HT_SS_SHIFT,
+ AR9300_11NG_HT_DS_SHIFT,
+ AR9300_11NG_HT_TS_SHIFT,
+ IS_CHAN_HT40(chan));
+ ar9003_hw_init_txpower_stbc(ah,
+ AR9300_11NG_HT_SS_SHIFT,
+ AR9300_11NG_HT_DS_SHIFT,
+ AR9300_11NG_HT_TS_SHIFT);
+ }
+ }
+}
+
void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
{
struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_rtt.h b/drivers/net/wireless/ath/ath9k/ar9003_rtt.h
index a43b30d..1efae69 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_rtt.h
+++ b/drivers/net/wireless/ath/ath9k/ar9003_rtt.h
@@ -17,6 +17,7 @@
#ifndef AR9003_RTT_H
#define AR9003_RTT_H
+#ifdef CPTCFG_ATH9K_PCOEM
void ar9003_hw_rtt_enable(struct ath_hw *ah);
void ar9003_hw_rtt_disable(struct ath_hw *ah);
void ar9003_hw_rtt_set_mask(struct ath_hw *ah, u32 rtt_mask);
@@ -25,5 +26,40 @@
void ar9003_hw_rtt_fill_hist(struct ath_hw *ah);
void ar9003_hw_rtt_clear_hist(struct ath_hw *ah);
bool ar9003_hw_rtt_restore(struct ath_hw *ah, struct ath9k_channel *chan);
+#else
+static inline void ar9003_hw_rtt_enable(struct ath_hw *ah)
+{
+}
+
+static inline void ar9003_hw_rtt_disable(struct ath_hw *ah)
+{
+}
+
+static inline void ar9003_hw_rtt_set_mask(struct ath_hw *ah, u32 rtt_mask)
+{
+}
+
+static inline bool ar9003_hw_rtt_force_restore(struct ath_hw *ah)
+{
+ return false;
+}
+
+static inline void ar9003_hw_rtt_load_hist(struct ath_hw *ah)
+{
+}
+
+static inline void ar9003_hw_rtt_fill_hist(struct ath_hw *ah)
+{
+}
+
+static inline void ar9003_hw_rtt_clear_hist(struct ath_hw *ah)
+{
+}
+
+static inline bool ar9003_hw_rtt_restore(struct ath_hw *ah, struct ath9k_channel *chan)
+{
+ return false;
+}
+#endif
#endif
diff --git a/drivers/net/wireless/ath/ath9k/ar953x_initvals.h b/drivers/net/wireless/ath/ath9k/ar953x_initvals.h
index 812a9d7..159cc6f 100644
--- a/drivers/net/wireless/ath/ath9k/ar953x_initvals.h
+++ b/drivers/net/wireless/ath/ath9k/ar953x_initvals.h
@@ -20,6 +20,8 @@
#define qca953x_1p0_mac_postamble ar9300_2p2_mac_postamble
+#define qca953x_1p0_soc_preamble ar955x_1p0_soc_preamble
+
#define qca953x_1p0_soc_postamble ar9300_2p2_soc_postamble
#define qca953x_1p0_common_rx_gain_table ar9300Common_rx_gain_table_2p2
@@ -28,6 +30,10 @@
#define qca953x_1p0_modes_fast_clock ar9300Modes_fast_clock_2p2
+#define qca953x_1p0_common_wo_xlna_rx_gain_bounds ar955x_1p0_common_wo_xlna_rx_gain_bounds
+
+#define qca953x_1p0_common_rx_gain_bounds ar955x_1p0_common_rx_gain_bounds
+
static const u32 qca953x_1p0_mac_core[][2] = {
/* Addr allmodes */
{0x00000008, 0x00000000},
@@ -490,35 +496,6 @@
{0x00016540, 0x10804008, 0x10804008, 0x50804000, 0x50804000},
};
-static const u32 qca953x_1p0_soc_preamble[][2] = {
- /* Addr allmodes */
- {0x00007000, 0x00000000},
- {0x00007004, 0x00000000},
- {0x00007008, 0x00000000},
- {0x0000700c, 0x00000000},
- {0x0000701c, 0x00000000},
- {0x00007020, 0x00000000},
- {0x00007024, 0x00000000},
- {0x00007028, 0x00000000},
- {0x0000702c, 0x00000000},
- {0x00007030, 0x00000000},
- {0x00007034, 0x00000002},
- {0x00007038, 0x000004c2},
- {0x00007048, 0x00000000},
-};
-
-static const u32 qca953x_1p0_common_rx_gain_bounds[][5] = {
- /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
- {0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27},
- {0x00009e48, 0x5030201a, 0x5030201a, 0x50302018, 0x50302018},
-};
-
-static const u32 qca953x_1p0_common_wo_xlna_rx_gain_bounds[][5] = {
- /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
- {0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27},
- {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012},
-};
-
static const u32 qca953x_1p0_modes_xpa_tx_gain_table[][2] = {
/* Addr allmodes */
{0x0000a2dc, 0xfffd5aaa},
@@ -715,8 +692,73 @@
{0x00016448, 0x6c927a70},
};
+static const u32 qca953x_1p1_modes_xpa_tx_gain_table[][2] = {
+ /* Addr allmodes */
+ {0x0000a2dc, 0xfffb52aa},
+ {0x0000a2e0, 0xfffd64cc},
+ {0x0000a2e4, 0xfffe80f0},
+ {0x0000a2e8, 0xffffff00},
+ {0x0000a410, 0x000050d5},
+ {0x0000a500, 0x00000000},
+ {0x0000a504, 0x04000002},
+ {0x0000a508, 0x08000004},
+ {0x0000a50c, 0x0c000006},
+ {0x0000a510, 0x1000000a},
+ {0x0000a514, 0x1400000c},
+ {0x0000a518, 0x1800000e},
+ {0x0000a51c, 0x1c000048},
+ {0x0000a520, 0x2000004a},
+ {0x0000a524, 0x2400004c},
+ {0x0000a528, 0x2800004e},
+ {0x0000a52c, 0x2b00024a},
+ {0x0000a530, 0x2f00024c},
+ {0x0000a534, 0x3300024e},
+ {0x0000a538, 0x36000668},
+ {0x0000a53c, 0x38000669},
+ {0x0000a540, 0x3a000868},
+ {0x0000a544, 0x3d00086a},
+ {0x0000a548, 0x4000086c},
+ {0x0000a54c, 0x4200086e},
+ {0x0000a550, 0x43000a6e},
+ {0x0000a554, 0x43000a6e},
+ {0x0000a558, 0x43000a6e},
+ {0x0000a55c, 0x43000a6e},
+ {0x0000a560, 0x43000a6e},
+ {0x0000a564, 0x43000a6e},
+ {0x0000a568, 0x43000a6e},
+ {0x0000a56c, 0x43000a6e},
+ {0x0000a570, 0x43000a6e},
+ {0x0000a574, 0x43000a6e},
+ {0x0000a578, 0x43000a6e},
+ {0x0000a57c, 0x43000a6e},
+ {0x0000a600, 0x00000000},
+ {0x0000a604, 0x00000000},
+ {0x0000a608, 0x00000000},
+ {0x0000a60c, 0x03804000},
+ {0x0000a610, 0x03804e01},
+ {0x0000a614, 0x03804e01},
+ {0x0000a618, 0x03804e01},
+ {0x0000a61c, 0x04009002},
+ {0x0000a620, 0x04009002},
+ {0x0000a624, 0x04009002},
+ {0x0000a628, 0x04009002},
+ {0x0000a62c, 0x04009002},
+ {0x0000a630, 0x04009002},
+ {0x0000a634, 0x04009002},
+ {0x0000a638, 0x04009002},
+ {0x0000a63c, 0x04009002},
+ {0x0000b2dc, 0xfffb52aa},
+ {0x0000b2e0, 0xfffd64cc},
+ {0x0000b2e4, 0xfffe80f0},
+ {0x0000b2e8, 0xffffff00},
+ {0x00016044, 0x024922db},
+ {0x00016048, 0x6c927a70},
+ {0x00016444, 0x024922db},
+ {0x00016448, 0x6c927a70},
+};
+
static const u32 qca953x_2p0_baseband_core[][2] = {
- /* Addr allmodes */
+ /* Addr allmodes */
{0x00009800, 0xafe68e30},
{0x00009804, 0xfd14e000},
{0x00009808, 0x9c0a9f6b},
@@ -914,4 +956,400 @@
{0x0000b284, 0x00000000, 0x00000000, 0x00000010, 0x00000010},
};
+static const u32 qca953x_2p0_common_wo_xlna_rx_gain_table[][2] = {
+ /* Addr allmodes */
+ {0x0000a000, 0x00010000},
+ {0x0000a004, 0x00030002},
+ {0x0000a008, 0x00050004},
+ {0x0000a00c, 0x00810080},
+ {0x0000a010, 0x00830082},
+ {0x0000a014, 0x01810180},
+ {0x0000a018, 0x01830182},
+ {0x0000a01c, 0x01850184},
+ {0x0000a020, 0x01890188},
+ {0x0000a024, 0x018b018a},
+ {0x0000a028, 0x018d018c},
+ {0x0000a02c, 0x03820190},
+ {0x0000a030, 0x03840383},
+ {0x0000a034, 0x03880385},
+ {0x0000a038, 0x038a0389},
+ {0x0000a03c, 0x038c038b},
+ {0x0000a040, 0x0390038d},
+ {0x0000a044, 0x03920391},
+ {0x0000a048, 0x03940393},
+ {0x0000a04c, 0x03960395},
+ {0x0000a050, 0x00000000},
+ {0x0000a054, 0x00000000},
+ {0x0000a058, 0x00000000},
+ {0x0000a05c, 0x00000000},
+ {0x0000a060, 0x00000000},
+ {0x0000a064, 0x00000000},
+ {0x0000a068, 0x00000000},
+ {0x0000a06c, 0x00000000},
+ {0x0000a070, 0x00000000},
+ {0x0000a074, 0x00000000},
+ {0x0000a078, 0x00000000},
+ {0x0000a07c, 0x00000000},
+ {0x0000a080, 0x29292929},
+ {0x0000a084, 0x29292929},
+ {0x0000a088, 0x29292929},
+ {0x0000a08c, 0x29292929},
+ {0x0000a090, 0x22292929},
+ {0x0000a094, 0x1d1d2222},
+ {0x0000a098, 0x0c111117},
+ {0x0000a09c, 0x00030303},
+ {0x0000a0a0, 0x00000000},
+ {0x0000a0a4, 0x00000000},
+ {0x0000a0a8, 0x00000000},
+ {0x0000a0ac, 0x00000000},
+ {0x0000a0b0, 0x00000000},
+ {0x0000a0b4, 0x00000000},
+ {0x0000a0b8, 0x00000000},
+ {0x0000a0bc, 0x00000000},
+ {0x0000a0c0, 0x001f0000},
+ {0x0000a0c4, 0x01000101},
+ {0x0000a0c8, 0x011e011f},
+ {0x0000a0cc, 0x011c011d},
+ {0x0000a0d0, 0x02030204},
+ {0x0000a0d4, 0x02010202},
+ {0x0000a0d8, 0x021f0200},
+ {0x0000a0dc, 0x0302021e},
+ {0x0000a0e0, 0x03000301},
+ {0x0000a0e4, 0x031e031f},
+ {0x0000a0e8, 0x0402031d},
+ {0x0000a0ec, 0x04000401},
+ {0x0000a0f0, 0x041e041f},
+ {0x0000a0f4, 0x0502041d},
+ {0x0000a0f8, 0x05000501},
+ {0x0000a0fc, 0x051e051f},
+ {0x0000a100, 0x06010602},
+ {0x0000a104, 0x061f0600},
+ {0x0000a108, 0x061d061e},
+ {0x0000a10c, 0x07020703},
+ {0x0000a110, 0x07000701},
+ {0x0000a114, 0x00000000},
+ {0x0000a118, 0x00000000},
+ {0x0000a11c, 0x00000000},
+ {0x0000a120, 0x00000000},
+ {0x0000a124, 0x00000000},
+ {0x0000a128, 0x00000000},
+ {0x0000a12c, 0x00000000},
+ {0x0000a130, 0x00000000},
+ {0x0000a134, 0x00000000},
+ {0x0000a138, 0x00000000},
+ {0x0000a13c, 0x00000000},
+ {0x0000a140, 0x001f0000},
+ {0x0000a144, 0x01000101},
+ {0x0000a148, 0x011e011f},
+ {0x0000a14c, 0x011c011d},
+ {0x0000a150, 0x02030204},
+ {0x0000a154, 0x02010202},
+ {0x0000a158, 0x021f0200},
+ {0x0000a15c, 0x0302021e},
+ {0x0000a160, 0x03000301},
+ {0x0000a164, 0x031e031f},
+ {0x0000a168, 0x0402031d},
+ {0x0000a16c, 0x04000401},
+ {0x0000a170, 0x041e041f},
+ {0x0000a174, 0x0502041d},
+ {0x0000a178, 0x05000501},
+ {0x0000a17c, 0x051e051f},
+ {0x0000a180, 0x06010602},
+ {0x0000a184, 0x061f0600},
+ {0x0000a188, 0x061d061e},
+ {0x0000a18c, 0x07020703},
+ {0x0000a190, 0x07000701},
+ {0x0000a194, 0x00000000},
+ {0x0000a198, 0x00000000},
+ {0x0000a19c, 0x00000000},
+ {0x0000a1a0, 0x00000000},
+ {0x0000a1a4, 0x00000000},
+ {0x0000a1a8, 0x00000000},
+ {0x0000a1ac, 0x00000000},
+ {0x0000a1b0, 0x00000000},
+ {0x0000a1b4, 0x00000000},
+ {0x0000a1b8, 0x00000000},
+ {0x0000a1bc, 0x00000000},
+ {0x0000a1c0, 0x00000000},
+ {0x0000a1c4, 0x00000000},
+ {0x0000a1c8, 0x00000000},
+ {0x0000a1cc, 0x00000000},
+ {0x0000a1d0, 0x00000000},
+ {0x0000a1d4, 0x00000000},
+ {0x0000a1d8, 0x00000000},
+ {0x0000a1dc, 0x00000000},
+ {0x0000a1e0, 0x00000000},
+ {0x0000a1e4, 0x00000000},
+ {0x0000a1e8, 0x00000000},
+ {0x0000a1ec, 0x00000000},
+ {0x0000a1f0, 0x00000396},
+ {0x0000a1f4, 0x00000396},
+ {0x0000a1f8, 0x00000396},
+ {0x0000a1fc, 0x00000196},
+ {0x0000b000, 0x00010000},
+ {0x0000b004, 0x00030002},
+ {0x0000b008, 0x00050004},
+ {0x0000b00c, 0x00810080},
+ {0x0000b010, 0x00830082},
+ {0x0000b014, 0x01810180},
+ {0x0000b018, 0x01830182},
+ {0x0000b01c, 0x01850184},
+ {0x0000b020, 0x02810280},
+ {0x0000b024, 0x02830282},
+ {0x0000b028, 0x02850284},
+ {0x0000b02c, 0x02890288},
+ {0x0000b030, 0x028b028a},
+ {0x0000b034, 0x0388028c},
+ {0x0000b038, 0x038a0389},
+ {0x0000b03c, 0x038c038b},
+ {0x0000b040, 0x0390038d},
+ {0x0000b044, 0x03920391},
+ {0x0000b048, 0x03940393},
+ {0x0000b04c, 0x03960395},
+ {0x0000b050, 0x00000000},
+ {0x0000b054, 0x00000000},
+ {0x0000b058, 0x00000000},
+ {0x0000b05c, 0x00000000},
+ {0x0000b060, 0x00000000},
+ {0x0000b064, 0x00000000},
+ {0x0000b068, 0x00000000},
+ {0x0000b06c, 0x00000000},
+ {0x0000b070, 0x00000000},
+ {0x0000b074, 0x00000000},
+ {0x0000b078, 0x00000000},
+ {0x0000b07c, 0x00000000},
+ {0x0000b080, 0x32323232},
+ {0x0000b084, 0x2f2f3232},
+ {0x0000b088, 0x23282a2d},
+ {0x0000b08c, 0x1c1e2123},
+ {0x0000b090, 0x14171919},
+ {0x0000b094, 0x0e0e1214},
+ {0x0000b098, 0x03050707},
+ {0x0000b09c, 0x00030303},
+ {0x0000b0a0, 0x00000000},
+ {0x0000b0a4, 0x00000000},
+ {0x0000b0a8, 0x00000000},
+ {0x0000b0ac, 0x00000000},
+ {0x0000b0b0, 0x00000000},
+ {0x0000b0b4, 0x00000000},
+ {0x0000b0b8, 0x00000000},
+ {0x0000b0bc, 0x00000000},
+ {0x0000b0c0, 0x003f0020},
+ {0x0000b0c4, 0x00400041},
+ {0x0000b0c8, 0x0140005f},
+ {0x0000b0cc, 0x0160015f},
+ {0x0000b0d0, 0x017e017f},
+ {0x0000b0d4, 0x02410242},
+ {0x0000b0d8, 0x025f0240},
+ {0x0000b0dc, 0x027f0260},
+ {0x0000b0e0, 0x0341027e},
+ {0x0000b0e4, 0x035f0340},
+ {0x0000b0e8, 0x037f0360},
+ {0x0000b0ec, 0x04400441},
+ {0x0000b0f0, 0x0460045f},
+ {0x0000b0f4, 0x0541047f},
+ {0x0000b0f8, 0x055f0540},
+ {0x0000b0fc, 0x057f0560},
+ {0x0000b100, 0x06400641},
+ {0x0000b104, 0x0660065f},
+ {0x0000b108, 0x067e067f},
+ {0x0000b10c, 0x07410742},
+ {0x0000b110, 0x075f0740},
+ {0x0000b114, 0x077f0760},
+ {0x0000b118, 0x07800781},
+ {0x0000b11c, 0x07a0079f},
+ {0x0000b120, 0x07c107bf},
+ {0x0000b124, 0x000007c0},
+ {0x0000b128, 0x00000000},
+ {0x0000b12c, 0x00000000},
+ {0x0000b130, 0x00000000},
+ {0x0000b134, 0x00000000},
+ {0x0000b138, 0x00000000},
+ {0x0000b13c, 0x00000000},
+ {0x0000b140, 0x003f0020},
+ {0x0000b144, 0x00400041},
+ {0x0000b148, 0x0140005f},
+ {0x0000b14c, 0x0160015f},
+ {0x0000b150, 0x017e017f},
+ {0x0000b154, 0x02410242},
+ {0x0000b158, 0x025f0240},
+ {0x0000b15c, 0x027f0260},
+ {0x0000b160, 0x0341027e},
+ {0x0000b164, 0x035f0340},
+ {0x0000b168, 0x037f0360},
+ {0x0000b16c, 0x04400441},
+ {0x0000b170, 0x0460045f},
+ {0x0000b174, 0x0541047f},
+ {0x0000b178, 0x055f0540},
+ {0x0000b17c, 0x057f0560},
+ {0x0000b180, 0x06400641},
+ {0x0000b184, 0x0660065f},
+ {0x0000b188, 0x067e067f},
+ {0x0000b18c, 0x07410742},
+ {0x0000b190, 0x075f0740},
+ {0x0000b194, 0x077f0760},
+ {0x0000b198, 0x07800781},
+ {0x0000b19c, 0x07a0079f},
+ {0x0000b1a0, 0x07c107bf},
+ {0x0000b1a4, 0x000007c0},
+ {0x0000b1a8, 0x00000000},
+ {0x0000b1ac, 0x00000000},
+ {0x0000b1b0, 0x00000000},
+ {0x0000b1b4, 0x00000000},
+ {0x0000b1b8, 0x00000000},
+ {0x0000b1bc, 0x00000000},
+ {0x0000b1c0, 0x00000000},
+ {0x0000b1c4, 0x00000000},
+ {0x0000b1c8, 0x00000000},
+ {0x0000b1cc, 0x00000000},
+ {0x0000b1d0, 0x00000000},
+ {0x0000b1d4, 0x00000000},
+ {0x0000b1d8, 0x00000000},
+ {0x0000b1dc, 0x00000000},
+ {0x0000b1e0, 0x00000000},
+ {0x0000b1e4, 0x00000000},
+ {0x0000b1e8, 0x00000000},
+ {0x0000b1ec, 0x00000000},
+ {0x0000b1f0, 0x00000396},
+ {0x0000b1f4, 0x00000396},
+ {0x0000b1f8, 0x00000396},
+ {0x0000b1fc, 0x00000196},
+};
+
+static const u32 qca953x_2p0_common_wo_xlna_rx_gain_bounds[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27},
+ {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012},
+};
+
+static const u32 qca953x_2p0_modes_xpa_tx_gain_table[][2] = {
+ /* Addr allmodes */
+ {0x0000a2dc, 0xfffb52aa},
+ {0x0000a2e0, 0xfffd64cc},
+ {0x0000a2e4, 0xfffe80f0},
+ {0x0000a2e8, 0xffffff00},
+ {0x0000a410, 0x000050d5},
+ {0x0000a500, 0x00000000},
+ {0x0000a504, 0x04000002},
+ {0x0000a508, 0x08000004},
+ {0x0000a50c, 0x0c000006},
+ {0x0000a510, 0x1000000a},
+ {0x0000a514, 0x1400000c},
+ {0x0000a518, 0x1800000e},
+ {0x0000a51c, 0x1c000048},
+ {0x0000a520, 0x2000004a},
+ {0x0000a524, 0x2400004c},
+ {0x0000a528, 0x2800004e},
+ {0x0000a52c, 0x2b00024a},
+ {0x0000a530, 0x2f00024c},
+ {0x0000a534, 0x3300024e},
+ {0x0000a538, 0x36000668},
+ {0x0000a53c, 0x38000669},
+ {0x0000a540, 0x3a000868},
+ {0x0000a544, 0x3d00086a},
+ {0x0000a548, 0x4000086c},
+ {0x0000a54c, 0x4200086e},
+ {0x0000a550, 0x43000a6e},
+ {0x0000a554, 0x43000a6e},
+ {0x0000a558, 0x43000a6e},
+ {0x0000a55c, 0x43000a6e},
+ {0x0000a560, 0x43000a6e},
+ {0x0000a564, 0x43000a6e},
+ {0x0000a568, 0x43000a6e},
+ {0x0000a56c, 0x43000a6e},
+ {0x0000a570, 0x43000a6e},
+ {0x0000a574, 0x43000a6e},
+ {0x0000a578, 0x43000a6e},
+ {0x0000a57c, 0x43000a6e},
+ {0x0000a600, 0x00000000},
+ {0x0000a604, 0x00000000},
+ {0x0000a608, 0x00000000},
+ {0x0000a60c, 0x03804000},
+ {0x0000a610, 0x03804e01},
+ {0x0000a614, 0x03804e01},
+ {0x0000a618, 0x03804e01},
+ {0x0000a61c, 0x04009002},
+ {0x0000a620, 0x04009002},
+ {0x0000a624, 0x04009002},
+ {0x0000a628, 0x04009002},
+ {0x0000a62c, 0x04009002},
+ {0x0000a630, 0x04009002},
+ {0x0000a634, 0x04009002},
+ {0x0000a638, 0x04009002},
+ {0x0000a63c, 0x04009002},
+ {0x0000b2dc, 0xfffb52aa},
+ {0x0000b2e0, 0xfffd64cc},
+ {0x0000b2e4, 0xfffe80f0},
+ {0x0000b2e8, 0xffffff00},
+ {0x00016044, 0x024922db},
+ {0x00016048, 0x6c927a70},
+ {0x00016444, 0x024922db},
+ {0x00016448, 0x6c927a70},
+};
+
+static const u32 qca953x_2p0_modes_no_xpa_tx_gain_table[][2] = {
+ /* Addr allmodes */
+ {0x0000a2dc, 0xffd5f552},
+ {0x0000a2e0, 0xffe60664},
+ {0x0000a2e4, 0xfff80780},
+ {0x0000a2e8, 0xfffff800},
+ {0x0000a410, 0x000050de},
+ {0x0000a500, 0x00000061},
+ {0x0000a504, 0x04000063},
+ {0x0000a508, 0x08000065},
+ {0x0000a50c, 0x0c000261},
+ {0x0000a510, 0x10000263},
+ {0x0000a514, 0x14000265},
+ {0x0000a518, 0x18000482},
+ {0x0000a51c, 0x1b000484},
+ {0x0000a520, 0x1f000486},
+ {0x0000a524, 0x240008c2},
+ {0x0000a528, 0x28000cc1},
+ {0x0000a52c, 0x2d000ce3},
+ {0x0000a530, 0x31000ce5},
+ {0x0000a534, 0x350010e5},
+ {0x0000a538, 0x360012e5},
+ {0x0000a53c, 0x380014e5},
+ {0x0000a540, 0x3b0018e5},
+ {0x0000a544, 0x3d001d04},
+ {0x0000a548, 0x3e001d05},
+ {0x0000a54c, 0x40001d07},
+ {0x0000a550, 0x42001f27},
+ {0x0000a554, 0x43001f67},
+ {0x0000a558, 0x46001fe7},
+ {0x0000a55c, 0x47001f2b},
+ {0x0000a560, 0x49001f0d},
+ {0x0000a564, 0x4b001ed2},
+ {0x0000a568, 0x4c001ed4},
+ {0x0000a56c, 0x4e001f15},
+ {0x0000a570, 0x4f001ff6},
+ {0x0000a574, 0x4f001ff6},
+ {0x0000a578, 0x4f001ff6},
+ {0x0000a57c, 0x4f001ff6},
+ {0x0000a600, 0x00000000},
+ {0x0000a604, 0x00000000},
+ {0x0000a608, 0x00000000},
+ {0x0000a60c, 0x00804201},
+ {0x0000a610, 0x01008201},
+ {0x0000a614, 0x0180c402},
+ {0x0000a618, 0x0180c603},
+ {0x0000a61c, 0x0180c603},
+ {0x0000a620, 0x01c10603},
+ {0x0000a624, 0x01c10704},
+ {0x0000a628, 0x02c18b05},
+ {0x0000a62c, 0x02c14c07},
+ {0x0000a630, 0x01008704},
+ {0x0000a634, 0x01c10402},
+ {0x0000a638, 0x0301cc07},
+ {0x0000a63c, 0x0301cc07},
+ {0x0000b2dc, 0xffd5f552},
+ {0x0000b2e0, 0xffe60664},
+ {0x0000b2e4, 0xfff80780},
+ {0x0000b2e8, 0xfffff800},
+ {0x00016044, 0x049242db},
+ {0x00016048, 0x6c927a70},
+ {0x00016444, 0x049242db},
+ {0x00016448, 0x6c927a70},
+};
+
#endif /* INITVALS_953X_H */
diff --git a/drivers/net/wireless/ath/ath9k/ar955x_1p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar955x_1p0_initvals.h
index 74d8bc0..fd6a84c 100644
--- a/drivers/net/wireless/ath/ath9k/ar955x_1p0_initvals.h
+++ b/drivers/net/wireless/ath/ath9k/ar955x_1p0_initvals.h
@@ -507,7 +507,7 @@
{0x00009d04, 0x40206c10},
{0x00009d08, 0x009c4060},
{0x00009d0c, 0x9883800a},
- {0x00009d10, 0x01834061},
+ {0x00009d10, 0x01884061},
{0x00009d14, 0x00c0040b},
{0x00009d18, 0x00000000},
{0x00009e08, 0x0038230c},
@@ -545,9 +545,9 @@
{0x0000a370, 0x00000000},
{0x0000a390, 0x00000001},
{0x0000a394, 0x00000444},
- {0x0000a398, 0x1f020503},
- {0x0000a39c, 0x29180c03},
- {0x0000a3a0, 0x9a8b6844},
+ {0x0000a398, 0x001f0e0f},
+ {0x0000a39c, 0x0075393f},
+ {0x0000a3a0, 0xb79f6427},
{0x0000a3a4, 0x00000000},
{0x0000a3a8, 0xaaaaaaaa},
{0x0000a3ac, 0x3c466478},
diff --git a/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h
index a5ca652..5d4629f 100644
--- a/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h
+++ b/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h
@@ -24,7 +24,149 @@
#define ar9580_1p0_soc_postamble ar9300_2p2_soc_postamble
-#define ar9580_1p0_radio_core ar9300_2p2_radio_core
+static const u32 ar9580_1p0_radio_core[][2] = {
+ /* Addr allmodes */
+ {0x00016000, 0x36db2db6},
+ {0x00016004, 0x6db6db40},
+ {0x00016008, 0x73f00000},
+ {0x0001600c, 0x00000000},
+ {0x00016040, 0x7f80fff8},
+ {0x0001604c, 0x76d005b5},
+ {0x00016050, 0x556cf031},
+ {0x00016054, 0x13449440},
+ {0x00016058, 0x0c51c92c},
+ {0x0001605c, 0x3db7fffc},
+ {0x00016060, 0xfffffffc},
+ {0x00016064, 0x000f0278},
+ {0x0001606c, 0x6db60000},
+ {0x00016080, 0x00000000},
+ {0x00016084, 0x0e48048c},
+ {0x00016088, 0x54214514},
+ {0x0001608c, 0x119f481e},
+ {0x00016090, 0x24926490},
+ {0x00016098, 0xd2888888},
+ {0x000160a0, 0x0a108ffe},
+ {0x000160a4, 0x812fc370},
+ {0x000160a8, 0x423c8000},
+ {0x000160b4, 0x92480080},
+ {0x000160c0, 0x00adb6d0},
+ {0x000160c4, 0x6db6db60},
+ {0x000160c8, 0x6db6db6c},
+ {0x000160cc, 0x01e6c000},
+ {0x00016100, 0x3fffbe01},
+ {0x00016104, 0xfff80000},
+ {0x00016108, 0x00080010},
+ {0x00016144, 0x02084080},
+ {0x00016148, 0x00000000},
+ {0x00016280, 0x058a0001},
+ {0x00016284, 0x3d840208},
+ {0x00016288, 0x05a20408},
+ {0x0001628c, 0x00038c07},
+ {0x00016290, 0x00000004},
+ {0x00016294, 0x458a214f},
+ {0x00016380, 0x00000000},
+ {0x00016384, 0x00000000},
+ {0x00016388, 0x00800700},
+ {0x0001638c, 0x00800700},
+ {0x00016390, 0x00800700},
+ {0x00016394, 0x00000000},
+ {0x00016398, 0x00000000},
+ {0x0001639c, 0x00000000},
+ {0x000163a0, 0x00000001},
+ {0x000163a4, 0x00000001},
+ {0x000163a8, 0x00000000},
+ {0x000163ac, 0x00000000},
+ {0x000163b0, 0x00000000},
+ {0x000163b4, 0x00000000},
+ {0x000163b8, 0x00000000},
+ {0x000163bc, 0x00000000},
+ {0x000163c0, 0x000000a0},
+ {0x000163c4, 0x000c0000},
+ {0x000163c8, 0x14021402},
+ {0x000163cc, 0x00001402},
+ {0x000163d0, 0x00000000},
+ {0x000163d4, 0x00000000},
+ {0x00016400, 0x36db2db6},
+ {0x00016404, 0x6db6db40},
+ {0x00016408, 0x73f00000},
+ {0x0001640c, 0x00000000},
+ {0x00016440, 0x7f80fff8},
+ {0x0001644c, 0x76d005b5},
+ {0x00016450, 0x556cf031},
+ {0x00016454, 0x13449440},
+ {0x00016458, 0x0c51c92c},
+ {0x0001645c, 0x3db7fffc},
+ {0x00016460, 0xfffffffc},
+ {0x00016464, 0x000f0278},
+ {0x0001646c, 0x6db60000},
+ {0x00016500, 0x3fffbe01},
+ {0x00016504, 0xfff80000},
+ {0x00016508, 0x00080010},
+ {0x00016544, 0x02084080},
+ {0x00016548, 0x00000000},
+ {0x00016780, 0x00000000},
+ {0x00016784, 0x00000000},
+ {0x00016788, 0x00800700},
+ {0x0001678c, 0x00800700},
+ {0x00016790, 0x00800700},
+ {0x00016794, 0x00000000},
+ {0x00016798, 0x00000000},
+ {0x0001679c, 0x00000000},
+ {0x000167a0, 0x00000001},
+ {0x000167a4, 0x00000001},
+ {0x000167a8, 0x00000000},
+ {0x000167ac, 0x00000000},
+ {0x000167b0, 0x00000000},
+ {0x000167b4, 0x00000000},
+ {0x000167b8, 0x00000000},
+ {0x000167bc, 0x00000000},
+ {0x000167c0, 0x000000a0},
+ {0x000167c4, 0x000c0000},
+ {0x000167c8, 0x14021402},
+ {0x000167cc, 0x00001402},
+ {0x000167d0, 0x00000000},
+ {0x000167d4, 0x00000000},
+ {0x00016800, 0x36db2db6},
+ {0x00016804, 0x6db6db40},
+ {0x00016808, 0x73f00000},
+ {0x0001680c, 0x00000000},
+ {0x00016840, 0x7f80fff8},
+ {0x0001684c, 0x76d005b5},
+ {0x00016850, 0x556cf031},
+ {0x00016854, 0x13449440},
+ {0x00016858, 0x0c51c92c},
+ {0x0001685c, 0x3db7fffc},
+ {0x00016860, 0xfffffffc},
+ {0x00016864, 0x000f0278},
+ {0x0001686c, 0x6db60000},
+ {0x00016900, 0x3fffbe01},
+ {0x00016904, 0xfff80000},
+ {0x00016908, 0x00080010},
+ {0x00016944, 0x02084080},
+ {0x00016948, 0x00000000},
+ {0x00016b80, 0x00000000},
+ {0x00016b84, 0x00000000},
+ {0x00016b88, 0x00800700},
+ {0x00016b8c, 0x00800700},
+ {0x00016b90, 0x00800700},
+ {0x00016b94, 0x00000000},
+ {0x00016b98, 0x00000000},
+ {0x00016b9c, 0x00000000},
+ {0x00016ba0, 0x00000001},
+ {0x00016ba4, 0x00000001},
+ {0x00016ba8, 0x00000000},
+ {0x00016bac, 0x00000000},
+ {0x00016bb0, 0x00000000},
+ {0x00016bb4, 0x00000000},
+ {0x00016bb8, 0x00000000},
+ {0x00016bbc, 0x00000000},
+ {0x00016bc0, 0x000000a0},
+ {0x00016bc4, 0x000c0000},
+ {0x00016bc8, 0x14021402},
+ {0x00016bcc, 0x00001402},
+ {0x00016bd0, 0x00000000},
+ {0x00016bd4, 0x00000000},
+};
#define ar9580_1p0_mac_postamble ar9300_2p2_mac_postamble
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 4581876..e008569 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -28,7 +28,6 @@
#include "debug.h"
#include "mci.h"
#include "dfs.h"
-#include "spectral.h"
struct ath_node;
struct ath_vif;
@@ -190,6 +189,7 @@
u8 rtscts_rate;
u8 retries : 7;
u8 baw_tracked : 1;
+ u8 tx_power;
};
struct ath_rxbuf {
@@ -345,7 +345,9 @@
u64 tsf_val;
u32 last_beacon;
+ int flush_timeout;
u16 txpower;
+ u16 cur_txpower;
bool offchannel;
bool stopped;
bool active;
@@ -362,7 +364,7 @@
ATH_CHANCTX_EVENT_BEACON_SENT,
ATH_CHANCTX_EVENT_TSF_TIMER,
ATH_CHANCTX_EVENT_BEACON_RECEIVED,
- ATH_CHANCTX_EVENT_ASSOC,
+ ATH_CHANCTX_EVENT_AUTHORIZED,
ATH_CHANCTX_EVENT_SWITCH,
ATH_CHANCTX_EVENT_ASSIGN,
ATH_CHANCTX_EVENT_UNASSIGN,
@@ -380,10 +382,12 @@
struct ath_chanctx_sched {
bool beacon_pending;
+ bool beacon_adjust;
bool offchannel_pending;
bool wait_switch;
bool force_noa_update;
bool extend_absence;
+ bool mgd_prepare_tx;
enum ath_chanctx_state state;
u8 beacon_miss;
@@ -468,6 +472,7 @@
void ath_offchannel_next(struct ath_softc *sc);
void ath_scan_complete(struct ath_softc *sc, bool abort);
void ath_roc_complete(struct ath_softc *sc, bool abort);
+struct ath_chanctx* ath_is_go_chanctx_present(struct ath_softc *sc);
#else
@@ -540,7 +545,6 @@
#endif /* CPTCFG_ATH9K_CHANNEL_CONTEXT */
-int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan);
void ath_startrecv(struct ath_softc *sc);
bool ath_stoprecv(struct ath_softc *sc);
u32 ath_calcrxfilter(struct ath_softc *sc);
@@ -595,7 +599,7 @@
u16 seq_no;
/* BSS info */
- u8 bssid[ETH_ALEN];
+ u8 bssid[ETH_ALEN] __aligned(2);
u16 aid;
bool assoc;
@@ -618,6 +622,7 @@
u32 noa_start;
u32 noa_duration;
bool periodic_noa;
+ bool oneshot_noa;
};
struct ath9k_vif_iter_data {
@@ -715,7 +720,8 @@
void ath_update_survey_nf(struct ath_softc *sc, int channel);
void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type);
void ath_ps_full_sleep(unsigned long data);
-void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop);
+void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop,
+ bool sw_pending, bool timeout_override);
/**********/
/* BTCOEX */
@@ -927,6 +933,7 @@
#define ATH9K_PCI_AR9565_2ANT 0x0100
#define ATH9K_PCI_NO_PLL_PWRSAVE 0x0200
#define ATH9K_PCI_KILLER 0x0400
+#define ATH9K_PCI_LED_ACT_HI 0x0800
/*
* Default cache line size, in bytes.
@@ -975,6 +982,7 @@
struct ath_chanctx_sched sched;
struct ath_offchannel offchannel;
struct ath_chanctx *next_chan;
+ struct completion go_beacon;
#endif
unsigned long driver_data;
@@ -982,7 +990,6 @@
u8 gtt_cnt;
u32 intrstatus;
u16 ps_flags; /* PS_* */
- u16 curtxpow;
bool ps_enabled;
bool ps_idle;
short nbcnvifs;
@@ -1023,10 +1030,8 @@
struct dfs_pattern_detector *dfs_detector;
u64 dfs_prev_pulse_ts;
u32 wow_enabled;
- /* relay(fs) channel for spectral scan */
- struct rchan *rfs_chan_spec_scan;
- enum spectral_mode spectral_mode;
- struct ath_spec_scan spec_config;
+
+ struct ath_spec_scan_priv spec_priv;
struct ieee80211_vif *tx99_vif;
struct sk_buff *tx99_skb;
@@ -1069,7 +1074,7 @@
int ath_cabq_update(struct ath_softc *);
u8 ath9k_parse_mpdudensity(u8 mpdudensity);
irqreturn_t ath_isr(int irq, void *dev);
-int ath_reset(struct ath_softc *sc);
+int ath_reset(struct ath_softc *sc, struct ath9k_channel *hchan);
void ath_cancel_work(struct ath_softc *sc);
void ath_restart_work(struct ath_softc *sc);
int ath9k_init_device(u16 devid, struct ath_softc *sc,
diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c
index ecb783b..cb366ad 100644
--- a/drivers/net/wireless/ath/ath9k/beacon.c
+++ b/drivers/net/wireless/ath/ath9k/beacon.c
@@ -78,7 +78,7 @@
struct ath_tx_info info;
struct ieee80211_supported_band *sband;
u8 chainmask = ah->txchainmask;
- u8 rate = 0;
+ u8 i, rate = 0;
sband = &common->sbands[sc->cur_chandef.chan->band];
rate = sband->bitrates[rateidx].hw_value;
@@ -88,7 +88,8 @@
memset(&info, 0, sizeof(info));
info.pkt_len = skb->len + FCS_LEN;
info.type = ATH9K_PKT_TYPE_BEACON;
- info.txpower = MAX_RATE_POWER;
+ for (i = 0; i < 4; i++)
+ info.txpower[i] = MAX_RATE_POWER;
info.keyix = ATH9K_TXKEYIX_INVALID;
info.keytype = ATH9K_KEY_TYPE_CLEAR;
info.flags = ATH9K_TXDESC_NOACK | ATH9K_TXDESC_CLRDMASK;
diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c
index 278365b..e200a6e 100644
--- a/drivers/net/wireless/ath/ath9k/calib.c
+++ b/drivers/net/wireless/ath/ath9k/calib.c
@@ -234,7 +234,7 @@
REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
}
-void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
+int ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
{
struct ath9k_nfcal_hist *h = NULL;
unsigned i, j;
@@ -301,7 +301,7 @@
ath_dbg(common, ANY,
"Timeout while waiting for nf to load: AR_PHY_AGC_CONTROL=0x%x\n",
REG_READ(ah, AR_PHY_AGC_CONTROL));
- return;
+ return -ETIMEDOUT;
}
/*
@@ -322,6 +322,8 @@
}
}
REGWRITE_BUFFER_FLUSH(ah);
+
+ return 0;
}
diff --git a/drivers/net/wireless/ath/ath9k/calib.h b/drivers/net/wireless/ath/ath9k/calib.h
index b8ed95e..87badf4 100644
--- a/drivers/net/wireless/ath/ath9k/calib.h
+++ b/drivers/net/wireless/ath/ath9k/calib.h
@@ -109,7 +109,7 @@
bool ath9k_hw_reset_calvalid(struct ath_hw *ah);
void ath9k_hw_start_nfcal(struct ath_hw *ah, bool update);
-void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan);
+int ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan);
bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan);
void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah,
struct ath9k_channel *chan);
diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c
index 696152b..239e8a3 100644
--- a/drivers/net/wireless/ath/ath9k/channel.c
+++ b/drivers/net/wireless/ath/ath9k/channel.c
@@ -66,7 +66,7 @@
}
hchan = &sc->sc_ah->channels[pos];
- r = ath_reset_internal(sc, hchan);
+ r = ath_reset(sc, hchan);
if (r)
return r;
@@ -92,8 +92,8 @@
} else {
/* perform spectral scan if requested. */
if (test_bit(ATH_OP_SCANNING, &common->op_flags) &&
- sc->spectral_mode == SPECTRAL_CHANSCAN)
- ath9k_spectral_scan_trigger(hw);
+ sc->spec_priv.spectral_mode == SPECTRAL_CHANSCAN)
+ ath9k_cmn_spectral_scan_trigger(common, &sc->spec_priv);
}
return 0;
@@ -117,6 +117,7 @@
cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20);
INIT_LIST_HEAD(&ctx->vifs);
ctx->txpower = ATH_TXPOWER_MAX;
+ ctx->flush_timeout = HZ / 5; /* 200ms */
for (j = 0; j < ARRAY_SIZE(ctx->acq); j++)
INIT_LIST_HEAD(&ctx->acq[j]);
}
@@ -145,6 +146,36 @@
#ifdef CPTCFG_ATH9K_CHANNEL_CONTEXT
+/*************/
+/* Utilities */
+/*************/
+
+struct ath_chanctx* ath_is_go_chanctx_present(struct ath_softc *sc)
+{
+ struct ath_chanctx *ctx;
+ struct ath_vif *avp;
+ struct ieee80211_vif *vif;
+
+ spin_lock_bh(&sc->chan_lock);
+
+ ath_for_each_chanctx(sc, ctx) {
+ if (!ctx->active)
+ continue;
+
+ list_for_each_entry(avp, &ctx->vifs, list) {
+ vif = avp->vif;
+
+ if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_P2P_GO) {
+ spin_unlock_bh(&sc->chan_lock);
+ return ctx;
+ }
+ }
+ }
+
+ spin_unlock_bh(&sc->chan_lock);
+ return NULL;
+}
+
/**********************************************************/
/* Functions to handle the channel context state machine. */
/**********************************************************/
@@ -171,7 +202,7 @@
case_rtn_string(ATH_CHANCTX_EVENT_BEACON_SENT);
case_rtn_string(ATH_CHANCTX_EVENT_TSF_TIMER);
case_rtn_string(ATH_CHANCTX_EVENT_BEACON_RECEIVED);
- case_rtn_string(ATH_CHANCTX_EVENT_ASSOC);
+ case_rtn_string(ATH_CHANCTX_EVENT_AUTHORIZED);
case_rtn_string(ATH_CHANCTX_EVENT_SWITCH);
case_rtn_string(ATH_CHANCTX_EVENT_ASSIGN);
case_rtn_string(ATH_CHANCTX_EVENT_UNASSIGN);
@@ -198,6 +229,7 @@
void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ struct ath_chanctx *ictx;
struct ath_vif *avp;
bool active = false;
u8 n_active = 0;
@@ -205,6 +237,28 @@
if (!ctx)
return;
+ if (ctx == &sc->offchannel.chan) {
+ spin_lock_bh(&sc->chan_lock);
+
+ if (likely(sc->sched.channel_switch_time))
+ ctx->flush_timeout =
+ usecs_to_jiffies(sc->sched.channel_switch_time);
+ else
+ ctx->flush_timeout =
+ msecs_to_jiffies(10);
+
+ spin_unlock_bh(&sc->chan_lock);
+
+ /*
+ * There is no need to iterate over the
+ * active/assigned channel contexts if
+ * the current context is offchannel.
+ */
+ return;
+ }
+
+ ictx = ctx;
+
list_for_each_entry(avp, &ctx->vifs, list) {
struct ieee80211_vif *vif = avp->vif;
@@ -227,12 +281,23 @@
n_active++;
}
+ spin_lock_bh(&sc->chan_lock);
+
if (n_active <= 1) {
+ ictx->flush_timeout = HZ / 5;
clear_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags);
+ spin_unlock_bh(&sc->chan_lock);
return;
}
- if (test_and_set_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags))
+
+ ictx->flush_timeout = usecs_to_jiffies(sc->sched.channel_switch_time);
+
+ if (test_and_set_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) {
+ spin_unlock_bh(&sc->chan_lock);
return;
+ }
+
+ spin_unlock_bh(&sc->chan_lock);
if (ath9k_is_chanctx_enabled()) {
ath_chanctx_event(sc, NULL,
@@ -301,6 +366,111 @@
"Setup chanctx timer with timeout: %d ms\n", jiffies_to_msecs(tsf_time));
}
+static void ath_chanctx_handle_bmiss(struct ath_softc *sc,
+ struct ath_chanctx *ctx,
+ struct ath_vif *avp)
+{
+ /*
+ * Clear the extend_absence flag if it had been
+ * set during the previous beacon transmission,
+ * since we need to revert to the normal NoA
+ * schedule.
+ */
+ if (ctx->active && sc->sched.extend_absence) {
+ avp->noa_duration = 0;
+ sc->sched.extend_absence = false;
+ }
+
+ /* If at least two consecutive beacons were missed on the STA
+ * chanctx, stay on the STA channel for one extra beacon period,
+ * to resync the timer properly.
+ */
+ if (ctx->active && sc->sched.beacon_miss >= 2) {
+ avp->noa_duration = 0;
+ sc->sched.extend_absence = true;
+ }
+}
+
+static void ath_chanctx_offchannel_noa(struct ath_softc *sc,
+ struct ath_chanctx *ctx,
+ struct ath_vif *avp,
+ u32 tsf_time)
+{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+
+ avp->noa_index++;
+ avp->offchannel_start = tsf_time;
+ avp->offchannel_duration = sc->sched.offchannel_duration;
+
+ ath_dbg(common, CHAN_CTX,
+ "offchannel noa_duration: %d, noa_start: %d, noa_index: %d\n",
+ avp->offchannel_duration,
+ avp->offchannel_start,
+ avp->noa_index);
+
+ /*
+ * When multiple contexts are active, the NoA
+ * has to be recalculated and advertised after
+ * an offchannel operation.
+ */
+ if (ctx->active && avp->noa_duration)
+ avp->noa_duration = 0;
+}
+
+static void ath_chanctx_set_periodic_noa(struct ath_softc *sc,
+ struct ath_vif *avp,
+ struct ath_beacon_config *cur_conf,
+ u32 tsf_time,
+ u32 beacon_int)
+{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+
+ avp->noa_index++;
+ avp->noa_start = tsf_time;
+
+ if (sc->sched.extend_absence)
+ avp->noa_duration = (3 * beacon_int / 2) +
+ sc->sched.channel_switch_time;
+ else
+ avp->noa_duration =
+ TU_TO_USEC(cur_conf->beacon_interval) / 2 +
+ sc->sched.channel_switch_time;
+
+ if (test_bit(ATH_OP_SCANNING, &common->op_flags) ||
+ sc->sched.extend_absence)
+ avp->periodic_noa = false;
+ else
+ avp->periodic_noa = true;
+
+ ath_dbg(common, CHAN_CTX,
+ "noa_duration: %d, noa_start: %d, noa_index: %d, periodic: %d\n",
+ avp->noa_duration,
+ avp->noa_start,
+ avp->noa_index,
+ avp->periodic_noa);
+}
+
+static void ath_chanctx_set_oneshot_noa(struct ath_softc *sc,
+ struct ath_vif *avp,
+ u32 tsf_time,
+ u32 duration)
+{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+
+ avp->noa_index++;
+ avp->noa_start = tsf_time;
+ avp->periodic_noa = false;
+ avp->oneshot_noa = true;
+ avp->noa_duration = duration + sc->sched.channel_switch_time;
+
+ ath_dbg(common, CHAN_CTX,
+ "oneshot noa_duration: %d, noa_start: %d, noa_index: %d, periodic: %d\n",
+ avp->noa_duration,
+ avp->noa_start,
+ avp->noa_index,
+ avp->periodic_noa);
+}
+
void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
enum ath_chanctx_event ev)
{
@@ -327,6 +497,14 @@
if (avp->offchannel_duration)
avp->offchannel_duration = 0;
+ if (avp->oneshot_noa) {
+ avp->noa_duration = 0;
+ avp->oneshot_noa = false;
+
+ ath_dbg(common, CHAN_CTX,
+ "Clearing oneshot NoA\n");
+ }
+
if (avp->chanctx != sc->cur_chan) {
ath_dbg(common, CHAN_CTX,
"Contexts differ, not preparing beacon\n");
@@ -356,6 +534,24 @@
"Move chanctx state from WAIT_FOR_TIMER to WAIT_FOR_BEACON\n");
}
+ if (sc->sched.mgd_prepare_tx)
+ sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON;
+
+ /*
+ * When a context becomes inactive, for example,
+ * disassociation of a station context, the NoA
+ * attribute needs to be removed from subsequent
+ * beacons.
+ */
+ if (!ctx->active && avp->noa_duration &&
+ sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON) {
+ avp->noa_duration = 0;
+ avp->periodic_noa = false;
+
+ ath_dbg(common, CHAN_CTX,
+ "Clearing NoA schedule\n");
+ }
+
if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON)
break;
@@ -378,45 +574,22 @@
* values and increment the index.
*/
if (sc->next_chan == &sc->offchannel.chan) {
- avp->noa_index++;
- avp->offchannel_start = tsf_time;
- avp->offchannel_duration = sc->sched.offchannel_duration;
-
- ath_dbg(common, CHAN_CTX,
- "offchannel noa_duration: %d, noa_start: %d, noa_index: %d\n",
- avp->offchannel_duration,
- avp->offchannel_start,
- avp->noa_index);
-
- /*
- * When multiple contexts are active, the NoA
- * has to be recalculated and advertised after
- * an offchannel operation.
- */
- if (ctx->active && avp->noa_duration)
- avp->noa_duration = 0;
-
+ ath_chanctx_offchannel_noa(sc, ctx, avp, tsf_time);
break;
}
- /*
- * Clear the extend_absence flag if it had been
- * set during the previous beacon transmission,
- * since we need to revert to the normal NoA
- * schedule.
- */
- if (ctx->active && sc->sched.extend_absence) {
- avp->noa_duration = 0;
- sc->sched.extend_absence = false;
- }
+ ath_chanctx_handle_bmiss(sc, ctx, avp);
- /* If at least two consecutive beacons were missed on the STA
- * chanctx, stay on the STA channel for one extra beacon period,
- * to resync the timer properly.
+ /*
+ * If a mgd_prepare_tx() has been called by mac80211,
+ * a one-shot NoA needs to be sent. This can happen
+ * with one or more active channel contexts - in both
+ * cases, a new NoA schedule has to be advertised.
*/
- if (ctx->active && sc->sched.beacon_miss >= 2) {
- avp->noa_duration = 0;
- sc->sched.extend_absence = true;
+ if (sc->sched.mgd_prepare_tx) {
+ ath_chanctx_set_oneshot_noa(sc, avp, tsf_time,
+ jiffies_to_usecs(HZ / 5));
+ break;
}
/* Prevent wrap-around issues */
@@ -429,31 +602,9 @@
* announcement.
*/
if (ctx->active &&
- (!avp->noa_duration || sc->sched.force_noa_update)) {
- avp->noa_index++;
- avp->noa_start = tsf_time;
-
- if (sc->sched.extend_absence)
- avp->noa_duration = (3 * beacon_int / 2) +
- sc->sched.channel_switch_time;
- else
- avp->noa_duration =
- TU_TO_USEC(cur_conf->beacon_interval) / 2 +
- sc->sched.channel_switch_time;
-
- if (test_bit(ATH_OP_SCANNING, &common->op_flags) ||
- sc->sched.extend_absence)
- avp->periodic_noa = false;
- else
- avp->periodic_noa = true;
-
- ath_dbg(common, CHAN_CTX,
- "noa_duration: %d, noa_start: %d, noa_index: %d, periodic: %d\n",
- avp->noa_duration,
- avp->noa_start,
- avp->noa_index,
- avp->periodic_noa);
- }
+ (!avp->noa_duration || sc->sched.force_noa_update))
+ ath_chanctx_set_periodic_noa(sc, avp, cur_conf,
+ tsf_time, beacon_int);
if (ctx->active && sc->sched.force_noa_update)
sc->sched.force_noa_update = false;
@@ -467,6 +618,15 @@
}
sc->sched.beacon_pending = false;
+
+ if (sc->sched.mgd_prepare_tx) {
+ sc->sched.mgd_prepare_tx = false;
+ complete(&sc->go_beacon);
+ ath_dbg(common, CHAN_CTX,
+ "Beacon sent, complete go_beacon\n");
+ break;
+ }
+
if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON)
break;
@@ -495,10 +655,16 @@
sc->cur_chan == &sc->offchannel.chan)
break;
- ath_chanctx_adjust_tbtt_delta(sc);
sc->sched.beacon_pending = false;
sc->sched.beacon_miss = 0;
+ if (sc->sched.state == ATH_CHANCTX_STATE_FORCE_ACTIVE ||
+ !sc->sched.beacon_adjust ||
+ !sc->cur_chan->tsf_val)
+ break;
+
+ ath_chanctx_adjust_tbtt_delta(sc);
+
/* TSF time might have been updated by the incoming beacon,
* need update the channel switch timer to reflect the change.
*/
@@ -507,10 +673,10 @@
ath9k_hw_get_tsf_offset(&sc->cur_chan->tsf_ts, NULL);
tsf_time += ath9k_hw_gettsf32(ah);
-
+ sc->sched.beacon_adjust = false;
ath_chanctx_setup_timer(sc, tsf_time);
break;
- case ATH_CHANCTX_EVENT_ASSOC:
+ case ATH_CHANCTX_EVENT_AUTHORIZED:
if (sc->sched.state != ATH_CHANCTX_STATE_FORCE_ACTIVE ||
avp->chanctx != sc->cur_chan)
break;
@@ -552,6 +718,7 @@
ath_chanctx_setup_timer(sc, tsf_time);
sc->sched.beacon_pending = true;
+ sc->sched.beacon_adjust = true;
break;
case ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL:
if (sc->cur_chan == &sc->offchannel.chan ||
@@ -578,22 +745,6 @@
ieee80211_queue_work(sc->hw, &sc->chanctx_work);
break;
case ATH_CHANCTX_EVENT_ASSIGN:
- /*
- * When adding a new channel context, check if a scan
- * is in progress and abort it since the addition of
- * a new channel context is usually followed by VIF
- * assignment, in which case we have to start multi-channel
- * operation.
- */
- if (test_bit(ATH_OP_SCANNING, &common->op_flags)) {
- ath_dbg(common, CHAN_CTX,
- "Aborting HW scan to add new context\n");
-
- spin_unlock_bh(&sc->chan_lock);
- del_timer_sync(&sc->offchannel.timer);
- ath_scan_complete(sc, true);
- spin_lock_bh(&sc->chan_lock);
- }
break;
case ATH_CHANCTX_EVENT_CHANGE:
break;
@@ -751,6 +902,11 @@
sc->offchannel.state = ATH_OFFCHANNEL_ROC_START;
ath_chanctx_offchan_switch(sc, sc->offchannel.roc_chan);
} else {
+ spin_lock_bh(&sc->chan_lock);
+ sc->sched.offchannel_pending = false;
+ sc->sched.wait_switch = false;
+ spin_unlock_bh(&sc->chan_lock);
+
ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc, false),
NULL);
sc->offchannel.state = ATH_OFFCHANNEL_IDLE;
@@ -770,8 +926,7 @@
sc->offchannel.roc_vif = NULL;
sc->offchannel.roc_chan = NULL;
- if (!abort)
- ieee80211_remain_on_channel_expired(sc->hw);
+ ieee80211_remain_on_channel_expired(sc->hw);
ath_offchannel_next(sc);
ath9k_ps_restore(sc);
}
@@ -808,7 +963,7 @@
struct ieee80211_tx_info *info;
int band = sc->offchannel.chan.chandef.chan->band;
- skb = ieee80211_probereq_get(sc->hw, vif,
+ skb = ieee80211_probereq_get(sc->hw, vif->addr,
ssid->ssid, ssid->ssid_len, req->ie_len);
if (!skb)
return;
@@ -902,9 +1057,8 @@
break;
case ATH_OFFCHANNEL_ROC_START:
case ATH_OFFCHANNEL_ROC_WAIT:
- ctx = ath_chanctx_get_oper_chan(sc, false);
sc->offchannel.state = ATH_OFFCHANNEL_ROC_DONE;
- ath_chanctx_switch(sc, ctx, NULL);
+ ath_roc_complete(sc, false);
break;
default:
break;
@@ -1034,7 +1188,6 @@
ieee80211_ready_on_channel(sc->hw);
break;
case ATH_OFFCHANNEL_ROC_DONE:
- ath_roc_complete(sc, false);
break;
default:
break;
@@ -1082,10 +1235,11 @@
ath9k_chanctx_stop_queues(sc, sc->cur_chan);
queues_stopped = true;
- __ath9k_flush(sc->hw, ~0, true);
+ __ath9k_flush(sc->hw, ~0, true, false, false);
if (ath_chanctx_send_ps_frame(sc, true))
- __ath9k_flush(sc->hw, BIT(IEEE80211_AC_VO), false);
+ __ath9k_flush(sc->hw, BIT(IEEE80211_AC_VO),
+ false, false, false);
send_ps = true;
spin_lock_bh(&sc->chan_lock);
@@ -1177,6 +1331,8 @@
(unsigned long)sc);
setup_timer(&sc->sched.timer, ath_chanctx_timer,
(unsigned long)sc);
+
+ init_completion(&sc->go_beacon);
}
void ath9k_deinit_channel_context(struct ath_softc *sc)
diff --git a/drivers/net/wireless/ath/ath9k/spectral.c b/drivers/net/wireless/ath/ath9k/common-spectral.c
similarity index 74%
rename from drivers/net/wireless/ath/ath9k/spectral.c
rename to drivers/net/wireless/ath/ath9k/common-spectral.c
index fad0c14..5253136 100644
--- a/drivers/net/wireless/ath/ath9k/spectral.c
+++ b/drivers/net/wireless/ath/ath9k/common-spectral.c
@@ -24,23 +24,24 @@
return (s8) rssi_val;
}
-static void ath_debug_send_fft_sample(struct ath_softc *sc,
+static void ath_debug_send_fft_sample(struct ath_spec_scan_priv *spec_priv,
struct fft_sample_tlv *fft_sample_tlv)
{
int length;
- if (!sc->rfs_chan_spec_scan)
+ if (!spec_priv->rfs_chan_spec_scan)
return;
length = __be16_to_cpu(fft_sample_tlv->length) +
sizeof(*fft_sample_tlv);
- relay_write(sc->rfs_chan_spec_scan, fft_sample_tlv, length);
+ relay_write(spec_priv->rfs_chan_spec_scan, fft_sample_tlv, length);
}
/* returns 1 if this was a spectral frame, even if not handled. */
-int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr,
+int ath_cmn_process_fft(struct ath_spec_scan_priv *spec_priv, struct ieee80211_hdr *hdr,
struct ath_rx_status *rs, u64 tsf)
{
- struct ath_hw *ah = sc->sc_ah;
+ struct ath_hw *ah = spec_priv->ah;
+ struct ath_common *common = ath9k_hw_common(spec_priv->ah);
u8 num_bins, *bins, *vdata = (u8 *)hdr;
struct fft_sample_ht20 fft_sample_20;
struct fft_sample_ht20_40 fft_sample_40;
@@ -67,7 +68,7 @@
if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK))
return 0;
- chan_type = cfg80211_get_chandef_type(&sc->hw->conf.chandef);
+ chan_type = cfg80211_get_chandef_type(&common->hw->conf.chandef);
if ((chan_type == NL80211_CHAN_HT40MINUS) ||
(chan_type == NL80211_CHAN_HT40PLUS)) {
fft_len = SPECTRAL_HT20_40_TOTAL_DATA_LEN;
@@ -199,10 +200,11 @@
tlv = (struct fft_sample_tlv *)&fft_sample_20;
}
- ath_debug_send_fft_sample(sc, tlv);
+ ath_debug_send_fft_sample(spec_priv, tlv);
return 1;
}
+EXPORT_SYMBOL(ath_cmn_process_fft);
/*********************/
/* spectral_scan_ctl */
@@ -211,11 +213,11 @@
static ssize_t read_file_spec_scan_ctl(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
- struct ath_softc *sc = file->private_data;
+ struct ath_spec_scan_priv *spec_priv = file->private_data;
char *mode = "";
unsigned int len;
- switch (sc->spectral_mode) {
+ switch (spec_priv->spectral_mode) {
case SPECTRAL_DISABLED:
mode = "disable";
break;
@@ -233,12 +235,84 @@
return simple_read_from_buffer(user_buf, count, ppos, mode, len);
}
+void ath9k_cmn_spectral_scan_trigger(struct ath_common *common,
+ struct ath_spec_scan_priv *spec_priv)
+{
+ struct ath_hw *ah = spec_priv->ah;
+ u32 rxfilter;
+
+ if (config_enabled(CPTCFG_ATH9K_TX99))
+ return;
+
+ if (!ath9k_hw_ops(ah)->spectral_scan_trigger) {
+ ath_err(common, "spectrum analyzer not implemented on this hardware\n");
+ return;
+ }
+
+ ath_ps_ops(common)->wakeup(common);
+ rxfilter = ath9k_hw_getrxfilter(ah);
+ ath9k_hw_setrxfilter(ah, rxfilter |
+ ATH9K_RX_FILTER_PHYRADAR |
+ ATH9K_RX_FILTER_PHYERR);
+
+ /* TODO: usually this should not be neccesary, but for some reason
+ * (or in some mode?) the trigger must be called after the
+ * configuration, otherwise the register will have its values reset
+ * (on my ar9220 to value 0x01002310)
+ */
+ ath9k_cmn_spectral_scan_config(common, spec_priv, spec_priv->spectral_mode);
+ ath9k_hw_ops(ah)->spectral_scan_trigger(ah);
+ ath_ps_ops(common)->restore(common);
+}
+EXPORT_SYMBOL(ath9k_cmn_spectral_scan_trigger);
+
+int ath9k_cmn_spectral_scan_config(struct ath_common *common,
+ struct ath_spec_scan_priv *spec_priv,
+ enum spectral_mode spectral_mode)
+{
+ struct ath_hw *ah = spec_priv->ah;
+
+ if (!ath9k_hw_ops(ah)->spectral_scan_trigger) {
+ ath_err(common, "spectrum analyzer not implemented on this hardware\n");
+ return -1;
+ }
+
+ switch (spectral_mode) {
+ case SPECTRAL_DISABLED:
+ spec_priv->spec_config.enabled = 0;
+ break;
+ case SPECTRAL_BACKGROUND:
+ /* send endless samples.
+ * TODO: is this really useful for "background"?
+ */
+ spec_priv->spec_config.endless = 1;
+ spec_priv->spec_config.enabled = 1;
+ break;
+ case SPECTRAL_CHANSCAN:
+ case SPECTRAL_MANUAL:
+ spec_priv->spec_config.endless = 0;
+ spec_priv->spec_config.enabled = 1;
+ break;
+ default:
+ return -1;
+ }
+
+ ath_ps_ops(common)->wakeup(common);
+ ath9k_hw_ops(ah)->spectral_scan_config(ah, &spec_priv->spec_config);
+ ath_ps_ops(common)->restore(common);
+
+ spec_priv->spectral_mode = spectral_mode;
+
+ return 0;
+}
+EXPORT_SYMBOL(ath9k_cmn_spectral_scan_config);
+
static ssize_t write_file_spec_scan_ctl(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
- struct ath_softc *sc = file->private_data;
- struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ struct ath_spec_scan_priv *spec_priv = file->private_data;
+ struct ath_common *common = ath9k_hw_common(spec_priv->ah);
char buf[32];
ssize_t len;
@@ -252,18 +326,18 @@
buf[len] = '\0';
if (strncmp("trigger", buf, 7) == 0) {
- ath9k_spectral_scan_trigger(sc->hw);
+ ath9k_cmn_spectral_scan_trigger(common, spec_priv);
} else if (strncmp("background", buf, 10) == 0) {
- ath9k_spectral_scan_config(sc->hw, SPECTRAL_BACKGROUND);
+ ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_BACKGROUND);
ath_dbg(common, CONFIG, "spectral scan: background mode enabled\n");
} else if (strncmp("chanscan", buf, 8) == 0) {
- ath9k_spectral_scan_config(sc->hw, SPECTRAL_CHANSCAN);
+ ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_CHANSCAN);
ath_dbg(common, CONFIG, "spectral scan: channel scan mode enabled\n");
} else if (strncmp("manual", buf, 6) == 0) {
- ath9k_spectral_scan_config(sc->hw, SPECTRAL_MANUAL);
+ ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_MANUAL);
ath_dbg(common, CONFIG, "spectral scan: manual mode enabled\n");
} else if (strncmp("disable", buf, 7) == 0) {
- ath9k_spectral_scan_config(sc->hw, SPECTRAL_DISABLED);
+ ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_DISABLED);
ath_dbg(common, CONFIG, "spectral scan: disabled\n");
} else {
return -EINVAL;
@@ -288,11 +362,11 @@
char __user *user_buf,
size_t count, loff_t *ppos)
{
- struct ath_softc *sc = file->private_data;
+ struct ath_spec_scan_priv *spec_priv = file->private_data;
char buf[32];
unsigned int len;
- len = sprintf(buf, "%d\n", sc->spec_config.short_repeat);
+ len = sprintf(buf, "%d\n", spec_priv->spec_config.short_repeat);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
@@ -300,7 +374,7 @@
const char __user *user_buf,
size_t count, loff_t *ppos)
{
- struct ath_softc *sc = file->private_data;
+ struct ath_spec_scan_priv *spec_priv = file->private_data;
unsigned long val;
char buf[32];
ssize_t len;
@@ -316,7 +390,7 @@
if (val > 1)
return -EINVAL;
- sc->spec_config.short_repeat = val;
+ spec_priv->spec_config.short_repeat = val;
return count;
}
@@ -336,11 +410,11 @@
char __user *user_buf,
size_t count, loff_t *ppos)
{
- struct ath_softc *sc = file->private_data;
+ struct ath_spec_scan_priv *spec_priv = file->private_data;
char buf[32];
unsigned int len;
- len = sprintf(buf, "%d\n", sc->spec_config.count);
+ len = sprintf(buf, "%d\n", spec_priv->spec_config.count);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
@@ -348,7 +422,7 @@
const char __user *user_buf,
size_t count, loff_t *ppos)
{
- struct ath_softc *sc = file->private_data;
+ struct ath_spec_scan_priv *spec_priv = file->private_data;
unsigned long val;
char buf[32];
ssize_t len;
@@ -364,7 +438,7 @@
if (val > 255)
return -EINVAL;
- sc->spec_config.count = val;
+ spec_priv->spec_config.count = val;
return count;
}
@@ -384,11 +458,11 @@
char __user *user_buf,
size_t count, loff_t *ppos)
{
- struct ath_softc *sc = file->private_data;
+ struct ath_spec_scan_priv *spec_priv = file->private_data;
char buf[32];
unsigned int len;
- len = sprintf(buf, "%d\n", sc->spec_config.period);
+ len = sprintf(buf, "%d\n", spec_priv->spec_config.period);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
@@ -396,7 +470,7 @@
const char __user *user_buf,
size_t count, loff_t *ppos)
{
- struct ath_softc *sc = file->private_data;
+ struct ath_spec_scan_priv *spec_priv = file->private_data;
unsigned long val;
char buf[32];
ssize_t len;
@@ -412,7 +486,7 @@
if (val > 255)
return -EINVAL;
- sc->spec_config.period = val;
+ spec_priv->spec_config.period = val;
return count;
}
@@ -432,11 +506,11 @@
char __user *user_buf,
size_t count, loff_t *ppos)
{
- struct ath_softc *sc = file->private_data;
+ struct ath_spec_scan_priv *spec_priv = file->private_data;
char buf[32];
unsigned int len;
- len = sprintf(buf, "%d\n", sc->spec_config.fft_period);
+ len = sprintf(buf, "%d\n", spec_priv->spec_config.fft_period);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
@@ -444,7 +518,7 @@
const char __user *user_buf,
size_t count, loff_t *ppos)
{
- struct ath_softc *sc = file->private_data;
+ struct ath_spec_scan_priv *spec_priv = file->private_data;
unsigned long val;
char buf[32];
ssize_t len;
@@ -460,7 +534,7 @@
if (val > 15)
return -EINVAL;
- sc->spec_config.fft_period = val;
+ spec_priv->spec_config.fft_period = val;
return count;
}
@@ -510,38 +584,41 @@
/* Debug Init/Deinit */
/*********************/
-void ath9k_spectral_deinit_debug(struct ath_softc *sc)
+void ath9k_cmn_spectral_deinit_debug(struct ath_spec_scan_priv *spec_priv)
{
- if (config_enabled(CPTCFG_ATH9K_DEBUGFS) && sc->rfs_chan_spec_scan) {
- relay_close(sc->rfs_chan_spec_scan);
- sc->rfs_chan_spec_scan = NULL;
+ if (config_enabled(CPTCFG_ATH9K_DEBUGFS) && spec_priv->rfs_chan_spec_scan) {
+ relay_close(spec_priv->rfs_chan_spec_scan);
+ spec_priv->rfs_chan_spec_scan = NULL;
}
}
+EXPORT_SYMBOL(ath9k_cmn_spectral_deinit_debug);
-void ath9k_spectral_init_debug(struct ath_softc *sc)
+void ath9k_cmn_spectral_init_debug(struct ath_spec_scan_priv *spec_priv,
+ struct dentry *debugfs_phy)
{
- sc->rfs_chan_spec_scan = relay_open("spectral_scan",
- sc->debug.debugfs_phy,
+ spec_priv->rfs_chan_spec_scan = relay_open("spectral_scan",
+ debugfs_phy,
1024, 256, &rfs_spec_scan_cb,
NULL);
debugfs_create_file("spectral_scan_ctl",
S_IRUSR | S_IWUSR,
- sc->debug.debugfs_phy, sc,
+ debugfs_phy, spec_priv,
&fops_spec_scan_ctl);
debugfs_create_file("spectral_short_repeat",
S_IRUSR | S_IWUSR,
- sc->debug.debugfs_phy, sc,
+ debugfs_phy, spec_priv,
&fops_spectral_short_repeat);
debugfs_create_file("spectral_count",
S_IRUSR | S_IWUSR,
- sc->debug.debugfs_phy, sc,
+ debugfs_phy, spec_priv,
&fops_spectral_count);
debugfs_create_file("spectral_period",
S_IRUSR | S_IWUSR,
- sc->debug.debugfs_phy, sc,
+ debugfs_phy, spec_priv,
&fops_spectral_period);
debugfs_create_file("spectral_fft_period",
S_IRUSR | S_IWUSR,
- sc->debug.debugfs_phy, sc,
+ debugfs_phy, spec_priv,
&fops_spectral_fft_period);
}
+EXPORT_SYMBOL(ath9k_cmn_spectral_init_debug);
diff --git a/drivers/net/wireless/ath/ath9k/spectral.h b/drivers/net/wireless/ath/ath9k/common-spectral.h
similarity index 84%
rename from drivers/net/wireless/ath/ath9k/spectral.h
rename to drivers/net/wireless/ath/ath9k/common-spectral.h
index 0b9465e..82d9dd2 100644
--- a/drivers/net/wireless/ath/ath9k/spectral.h
+++ b/drivers/net/wireless/ath/ath9k/common-spectral.h
@@ -92,6 +92,13 @@
struct ath_radar_info radar_info;
} __packed;
+struct ath_spec_scan_priv {
+ struct ath_hw *ah;
+ /* relay(fs) channel for spectral scan */
+ struct rchan *rfs_chan_spec_scan;
+ enum spectral_mode spectral_mode;
+ struct ath_spec_scan spec_config;
+};
#define SPECTRAL_HT20_40_TOTAL_DATA_LEN (sizeof(struct ath_ht20_40_fft_packet))
@@ -123,23 +130,15 @@
return bins[0] & 0x3f;
}
-void ath9k_spectral_init_debug(struct ath_softc *sc);
-void ath9k_spectral_deinit_debug(struct ath_softc *sc);
+void ath9k_cmn_spectral_init_debug(struct ath_spec_scan_priv *spec_priv, struct dentry *debugfs_phy);
+void ath9k_cmn_spectral_deinit_debug(struct ath_spec_scan_priv *spec_priv);
-void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw);
-int ath9k_spectral_scan_config(struct ieee80211_hw *hw,
+void ath9k_cmn_spectral_scan_trigger(struct ath_common *common,
+ struct ath_spec_scan_priv *spec_priv);
+int ath9k_cmn_spectral_scan_config(struct ath_common *common,
+ struct ath_spec_scan_priv *spec_priv,
enum spectral_mode spectral_mode);
-
-#ifdef CPTCFG_ATH9K_DEBUGFS
-int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr,
+int ath_cmn_process_fft(struct ath_spec_scan_priv *spec_priv, struct ieee80211_hdr *hdr,
struct ath_rx_status *rs, u64 tsf);
-#else
-static inline int ath_process_fft(struct ath_softc *sc,
- struct ieee80211_hdr *hdr,
- struct ath_rx_status *rs, u64 tsf)
-{
- return 0;
-}
-#endif /* CPTCFG_ATH9K_DEBUGFS */
#endif /* SPECTRAL_H */
diff --git a/drivers/net/wireless/ath/ath9k/common.c b/drivers/net/wireless/ath/ath9k/common.c
index c6dd7f1..e8c6994 100644
--- a/drivers/net/wireless/ath/ath9k/common.c
+++ b/drivers/net/wireless/ath/ath9k/common.c
@@ -159,7 +159,7 @@
if (test_bit(keyix, common->keymap))
rxs->flag |= RX_FLAG_DECRYPTED;
}
- if (ah->sw_mgmt_crypto &&
+ if (ah->sw_mgmt_crypto_rx &&
(rxs->flag & RX_FLAG_DECRYPTED) &&
ieee80211_is_mgmt(fc))
/* Use software decrypt for management frames. */
@@ -368,11 +368,11 @@
{
struct ath_regulatory *reg = ath9k_hw_regulatory(ah);
- if (reg->power_limit != new_txpow) {
+ if (reg->power_limit != new_txpow)
ath9k_hw_set_txpowerlimit(ah, new_txpow, false);
- /* read back in case value is clamped */
- *txpower = reg->max_power_level;
- }
+
+ /* read back in case value is clamped */
+ *txpower = reg->max_power_level;
}
EXPORT_SYMBOL(ath9k_cmn_update_txpow);
diff --git a/drivers/net/wireless/ath/ath9k/common.h b/drivers/net/wireless/ath/ath9k/common.h
index ffc454b..2b79a56 100644
--- a/drivers/net/wireless/ath/ath9k/common.h
+++ b/drivers/net/wireless/ath/ath9k/common.h
@@ -24,6 +24,7 @@
#include "common-init.h"
#include "common-beacon.h"
#include "common-debug.h"
+#include "common-spectral.h"
/* Common header for Atheros 802.11n base driver cores */
diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
index 947be9a..99ce0db 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -401,22 +401,14 @@
.llseek = default_llseek,
};
-static ssize_t read_file_dma(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
+static int read_file_dma(struct seq_file *file, void *data)
{
- struct ath_softc *sc = file->private_data;
+ struct ath_softc *sc = file->private;
struct ath_hw *ah = sc->sc_ah;
- char *buf;
- int retval;
- unsigned int len = 0;
u32 val[ATH9K_NUM_DMA_DEBUG_REGS];
int i, qcuOffset = 0, dcuOffset = 0;
u32 *qcuBase = &val[0], *dcuBase = &val[4];
- buf = kmalloc(DMA_BUF_LEN, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
ath9k_ps_wakeup(sc);
REG_WRITE_D(ah, AR_MACMISC,
@@ -424,21 +416,18 @@
(AR_MACMISC_MISC_OBS_BUS_1 <<
AR_MACMISC_MISC_OBS_BUS_MSB_S)));
- len += scnprintf(buf + len, DMA_BUF_LEN - len,
- "Raw DMA Debug values:\n");
+ seq_puts(file, "Raw DMA Debug values:\n");
for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++) {
if (i % 4 == 0)
- len += scnprintf(buf + len, DMA_BUF_LEN - len, "\n");
+ seq_puts(file, "\n");
val[i] = REG_READ_D(ah, AR_DMADBG_0 + (i * sizeof(u32)));
- len += scnprintf(buf + len, DMA_BUF_LEN - len, "%d: %08x ",
- i, val[i]);
+ seq_printf(file, "%d: %08x ", i, val[i]);
}
- len += scnprintf(buf + len, DMA_BUF_LEN - len, "\n\n");
- len += scnprintf(buf + len, DMA_BUF_LEN - len,
- "Num QCU: chain_st fsp_ok fsp_st DCU: chain_st\n");
+ seq_puts(file, "\n\n");
+ seq_puts(file, "Num QCU: chain_st fsp_ok fsp_st DCU: chain_st\n");
for (i = 0; i < ATH9K_NUM_QUEUES; i++, qcuOffset += 4, dcuOffset += 5) {
if (i == 8) {
@@ -451,55 +440,47 @@
dcuBase++;
}
- len += scnprintf(buf + len, DMA_BUF_LEN - len,
- "%2d %2x %1x %2x %2x\n",
- i, (*qcuBase & (0x7 << qcuOffset)) >> qcuOffset,
- (*qcuBase & (0x8 << qcuOffset)) >> (qcuOffset + 3),
- val[2] & (0x7 << (i * 3)) >> (i * 3),
- (*dcuBase & (0x1f << dcuOffset)) >> dcuOffset);
+ seq_printf(file, "%2d %2x %1x %2x %2x\n",
+ i, (*qcuBase & (0x7 << qcuOffset)) >> qcuOffset,
+ (*qcuBase & (0x8 << qcuOffset)) >> (qcuOffset + 3),
+ (val[2] & (0x7 << (i * 3))) >> (i * 3),
+ (*dcuBase & (0x1f << dcuOffset)) >> dcuOffset);
}
- len += scnprintf(buf + len, DMA_BUF_LEN - len, "\n");
+ seq_puts(file, "\n");
- len += scnprintf(buf + len, DMA_BUF_LEN - len,
- "qcu_stitch state: %2x qcu_fetch state: %2x\n",
- (val[3] & 0x003c0000) >> 18, (val[3] & 0x03c00000) >> 22);
- len += scnprintf(buf + len, DMA_BUF_LEN - len,
- "qcu_complete state: %2x dcu_complete state: %2x\n",
- (val[3] & 0x1c000000) >> 26, (val[6] & 0x3));
- len += scnprintf(buf + len, DMA_BUF_LEN - len,
- "dcu_arb state: %2x dcu_fp state: %2x\n",
- (val[5] & 0x06000000) >> 25, (val[5] & 0x38000000) >> 27);
- len += scnprintf(buf + len, DMA_BUF_LEN - len,
- "chan_idle_dur: %3d chan_idle_dur_valid: %1d\n",
- (val[6] & 0x000003fc) >> 2, (val[6] & 0x00000400) >> 10);
- len += scnprintf(buf + len, DMA_BUF_LEN - len,
- "txfifo_valid_0: %1d txfifo_valid_1: %1d\n",
- (val[6] & 0x00000800) >> 11, (val[6] & 0x00001000) >> 12);
- len += scnprintf(buf + len, DMA_BUF_LEN - len,
- "txfifo_dcu_num_0: %2d txfifo_dcu_num_1: %2d\n",
- (val[6] & 0x0001e000) >> 13, (val[6] & 0x001e0000) >> 17);
+ seq_printf(file, "qcu_stitch state: %2x qcu_fetch state: %2x\n",
+ (val[3] & 0x003c0000) >> 18, (val[3] & 0x03c00000) >> 22);
+ seq_printf(file, "qcu_complete state: %2x dcu_complete state: %2x\n",
+ (val[3] & 0x1c000000) >> 26, (val[6] & 0x3));
+ seq_printf(file, "dcu_arb state: %2x dcu_fp state: %2x\n",
+ (val[5] & 0x06000000) >> 25, (val[5] & 0x38000000) >> 27);
+ seq_printf(file, "chan_idle_dur: %3d chan_idle_dur_valid: %1d\n",
+ (val[6] & 0x000003fc) >> 2, (val[6] & 0x00000400) >> 10);
+ seq_printf(file, "txfifo_valid_0: %1d txfifo_valid_1: %1d\n",
+ (val[6] & 0x00000800) >> 11, (val[6] & 0x00001000) >> 12);
+ seq_printf(file, "txfifo_dcu_num_0: %2d txfifo_dcu_num_1: %2d\n",
+ (val[6] & 0x0001e000) >> 13, (val[6] & 0x001e0000) >> 17);
- len += scnprintf(buf + len, DMA_BUF_LEN - len, "pcu observe: 0x%x\n",
- REG_READ_D(ah, AR_OBS_BUS_1));
- len += scnprintf(buf + len, DMA_BUF_LEN - len,
- "AR_CR: 0x%x\n", REG_READ_D(ah, AR_CR));
+ seq_printf(file, "pcu observe: 0x%x\n", REG_READ_D(ah, AR_OBS_BUS_1));
+ seq_printf(file, "AR_CR: 0x%x\n", REG_READ_D(ah, AR_CR));
ath9k_ps_restore(sc);
- if (len > DMA_BUF_LEN)
- len = DMA_BUF_LEN;
+ return 0;
+}
- retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
- kfree(buf);
- return retval;
+static int open_file_dma(struct inode *inode, struct file *f)
+{
+ return single_open(f, read_file_dma, inode->i_private);
}
static const struct file_operations fops_dma = {
- .read = read_file_dma,
- .open = simple_open,
+ .open = open_file_dma,
+ .read = seq_read,
.owner = THIS_MODULE,
- .llseek = default_llseek,
+ .llseek = seq_lseek,
+ .release = single_release,
};
@@ -556,22 +537,14 @@
sc->debug.stats.istats.gen_timer++;
}
-static ssize_t read_file_interrupt(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
+static int read_file_interrupt(struct seq_file *file, void *data)
{
- struct ath_softc *sc = file->private_data;
- unsigned int len = 0;
- int rv;
- int mxlen = 4000;
- char *buf = kmalloc(mxlen, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
+ struct ath_softc *sc = file->private;
#define PR_IS(a, s) \
do { \
- len += scnprintf(buf + len, mxlen - len, \
- "%21s: %10u\n", a, \
- sc->debug.stats.istats.s); \
+ seq_printf(file, "%21s: %10u\n", a, \
+ sc->debug.stats.istats.s); \
} while (0)
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
@@ -602,8 +575,7 @@
PR_IS("GENTIMER", gen_timer);
PR_IS("TOTAL", total);
- len += scnprintf(buf + len, mxlen - len,
- "SYNC_CAUSE stats:\n");
+ seq_puts(file, "SYNC_CAUSE stats:\n");
PR_IS("Sync-All", sync_cause_all);
PR_IS("RTC-IRQ", sync_rtc_irq);
@@ -625,35 +597,27 @@
PR_IS("MAC-Asleep", mac_asleep);
PR_IS("MAC-Sleep-Access", mac_sleep_access);
- if (len > mxlen)
- len = mxlen;
+ return 0;
+}
- rv = simple_read_from_buffer(user_buf, count, ppos, buf, len);
- kfree(buf);
- return rv;
+static int open_file_interrupt(struct inode *inode, struct file *f)
+{
+ return single_open(f, read_file_interrupt, inode->i_private);
}
static const struct file_operations fops_interrupt = {
- .read = read_file_interrupt,
- .open = simple_open,
+ .read = seq_read,
+ .open = open_file_interrupt,
.owner = THIS_MODULE,
- .llseek = default_llseek,
+ .llseek = seq_lseek,
+ .release = single_release,
};
-static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
+static int read_file_xmit(struct seq_file *file, void *data)
{
- struct ath_softc *sc = file->private_data;
- char *buf;
- unsigned int len = 0, size = 2048;
- ssize_t retval = 0;
+ struct ath_softc *sc = file->private;
- buf = kzalloc(size, GFP_KERNEL);
- if (buf == NULL)
- return -ENOMEM;
-
- len += sprintf(buf, "%30s %10s%10s%10s\n\n",
- "BE", "BK", "VI", "VO");
+ seq_printf(file, "%30s %10s%10s%10s\n\n", "BE", "BK", "VI", "VO");
PR("MPDUs Queued: ", queued);
PR("MPDUs Completed: ", completed);
@@ -678,215 +642,162 @@
PR("HW-tx-proc-desc: ", txprocdesc);
PR("TX-Failed: ", txfailed);
- if (len > size)
- len = size;
-
- retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
- kfree(buf);
-
- return retval;
+ return 0;
}
-static ssize_t print_queue(struct ath_softc *sc, struct ath_txq *txq,
- char *buf, ssize_t size)
+static void print_queue(struct ath_softc *sc, struct ath_txq *txq,
+ struct seq_file *file)
{
- ssize_t len = 0;
-
ath_txq_lock(sc, txq);
- len += scnprintf(buf + len, size - len, "%s: %d ",
- "qnum", txq->axq_qnum);
- len += scnprintf(buf + len, size - len, "%s: %2d ",
- "qdepth", txq->axq_depth);
- len += scnprintf(buf + len, size - len, "%s: %2d ",
- "ampdu-depth", txq->axq_ampdu_depth);
- len += scnprintf(buf + len, size - len, "%s: %3d ",
- "pending", txq->pending_frames);
- len += scnprintf(buf + len, size - len, "%s: %d\n",
- "stopped", txq->stopped);
+ seq_printf(file, "%s: %d ", "qnum", txq->axq_qnum);
+ seq_printf(file, "%s: %2d ", "qdepth", txq->axq_depth);
+ seq_printf(file, "%s: %2d ", "ampdu-depth", txq->axq_ampdu_depth);
+ seq_printf(file, "%s: %3d ", "pending", txq->pending_frames);
+ seq_printf(file, "%s: %d\n", "stopped", txq->stopped);
ath_txq_unlock(sc, txq);
- return len;
}
-static ssize_t read_file_queues(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
+static int read_file_queues(struct seq_file *file, void *data)
{
- struct ath_softc *sc = file->private_data;
+ struct ath_softc *sc = file->private;
struct ath_txq *txq;
- char *buf;
- unsigned int len = 0;
- const unsigned int size = 1024;
- ssize_t retval = 0;
int i;
static const char *qname[4] = {
"VO", "VI", "BE", "BK"
};
- buf = kzalloc(size, GFP_KERNEL);
- if (buf == NULL)
- return -ENOMEM;
-
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
txq = sc->tx.txq_map[i];
- len += scnprintf(buf + len, size - len, "(%s): ", qname[i]);
- len += print_queue(sc, txq, buf + len, size - len);
+ seq_printf(file, "(%s): ", qname[i]);
+ print_queue(sc, txq, file);
}
- len += scnprintf(buf + len, size - len, "(CAB): ");
- len += print_queue(sc, sc->beacon.cabq, buf + len, size - len);
+ seq_puts(file, "(CAB): ");
+ print_queue(sc, sc->beacon.cabq, file);
- if (len > size)
- len = size;
-
- retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
- kfree(buf);
-
- return retval;
+ return 0;
}
-static ssize_t read_file_misc(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
+static int read_file_misc(struct seq_file *file, void *data)
{
- struct ath_softc *sc = file->private_data;
+ struct ath_softc *sc = file->private;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath9k_vif_iter_data iter_data;
struct ath_chanctx *ctx;
- char buf[512];
- unsigned int len = 0;
- ssize_t retval = 0;
unsigned int reg;
u32 rxfilter, i;
- len += scnprintf(buf + len, sizeof(buf) - len,
- "BSSID: %pM\n", common->curbssid);
- len += scnprintf(buf + len, sizeof(buf) - len,
- "BSSID-MASK: %pM\n", common->bssidmask);
- len += scnprintf(buf + len, sizeof(buf) - len,
- "OPMODE: %s\n",
- ath_opmode_to_string(sc->sc_ah->opmode));
+ seq_printf(file, "BSSID: %pM\n", common->curbssid);
+ seq_printf(file, "BSSID-MASK: %pM\n", common->bssidmask);
+ seq_printf(file, "OPMODE: %s\n",
+ ath_opmode_to_string(sc->sc_ah->opmode));
ath9k_ps_wakeup(sc);
rxfilter = ath9k_hw_getrxfilter(sc->sc_ah);
ath9k_ps_restore(sc);
- len += scnprintf(buf + len, sizeof(buf) - len,
- "RXFILTER: 0x%x", rxfilter);
+ seq_printf(file, "RXFILTER: 0x%x", rxfilter);
if (rxfilter & ATH9K_RX_FILTER_UCAST)
- len += scnprintf(buf + len, sizeof(buf) - len, " UCAST");
+ seq_puts(file, " UCAST");
if (rxfilter & ATH9K_RX_FILTER_MCAST)
- len += scnprintf(buf + len, sizeof(buf) - len, " MCAST");
+ seq_puts(file, " MCAST");
if (rxfilter & ATH9K_RX_FILTER_BCAST)
- len += scnprintf(buf + len, sizeof(buf) - len, " BCAST");
+ seq_puts(file, " BCAST");
if (rxfilter & ATH9K_RX_FILTER_CONTROL)
- len += scnprintf(buf + len, sizeof(buf) - len, " CONTROL");
+ seq_puts(file, " CONTROL");
if (rxfilter & ATH9K_RX_FILTER_BEACON)
- len += scnprintf(buf + len, sizeof(buf) - len, " BEACON");
+ seq_puts(file, " BEACON");
if (rxfilter & ATH9K_RX_FILTER_PROM)
- len += scnprintf(buf + len, sizeof(buf) - len, " PROM");
+ seq_puts(file, " PROM");
if (rxfilter & ATH9K_RX_FILTER_PROBEREQ)
- len += scnprintf(buf + len, sizeof(buf) - len, " PROBEREQ");
+ seq_puts(file, " PROBEREQ");
if (rxfilter & ATH9K_RX_FILTER_PHYERR)
- len += scnprintf(buf + len, sizeof(buf) - len, " PHYERR");
+ seq_puts(file, " PHYERR");
if (rxfilter & ATH9K_RX_FILTER_MYBEACON)
- len += scnprintf(buf + len, sizeof(buf) - len, " MYBEACON");
+ seq_puts(file, " MYBEACON");
if (rxfilter & ATH9K_RX_FILTER_COMP_BAR)
- len += scnprintf(buf + len, sizeof(buf) - len, " COMP_BAR");
+ seq_puts(file, " COMP_BAR");
if (rxfilter & ATH9K_RX_FILTER_PSPOLL)
- len += scnprintf(buf + len, sizeof(buf) - len, " PSPOLL");
+ seq_puts(file, " PSPOLL");
if (rxfilter & ATH9K_RX_FILTER_PHYRADAR)
- len += scnprintf(buf + len, sizeof(buf) - len, " PHYRADAR");
+ seq_puts(file, " PHYRADAR");
if (rxfilter & ATH9K_RX_FILTER_MCAST_BCAST_ALL)
- len += scnprintf(buf + len, sizeof(buf) - len, " MCAST_BCAST_ALL");
+ seq_puts(file, " MCAST_BCAST_ALL");
if (rxfilter & ATH9K_RX_FILTER_CONTROL_WRAPPER)
- len += scnprintf(buf + len, sizeof(buf) - len, " CONTROL_WRAPPER");
+ seq_puts(file, " CONTROL_WRAPPER");
- len += scnprintf(buf + len, sizeof(buf) - len, "\n");
+ seq_puts(file, "\n");
reg = sc->sc_ah->imask;
- len += scnprintf(buf + len, sizeof(buf) - len,
- "INTERRUPT-MASK: 0x%x", reg);
+ seq_printf(file, "INTERRUPT-MASK: 0x%x", reg);
if (reg & ATH9K_INT_SWBA)
- len += scnprintf(buf + len, sizeof(buf) - len, " SWBA");
+ seq_puts(file, " SWBA");
if (reg & ATH9K_INT_BMISS)
- len += scnprintf(buf + len, sizeof(buf) - len, " BMISS");
+ seq_puts(file, " BMISS");
if (reg & ATH9K_INT_CST)
- len += scnprintf(buf + len, sizeof(buf) - len, " CST");
+ seq_puts(file, " CST");
if (reg & ATH9K_INT_RX)
- len += scnprintf(buf + len, sizeof(buf) - len, " RX");
+ seq_puts(file, " RX");
if (reg & ATH9K_INT_RXHP)
- len += scnprintf(buf + len, sizeof(buf) - len, " RXHP");
+ seq_puts(file, " RXHP");
if (reg & ATH9K_INT_RXLP)
- len += scnprintf(buf + len, sizeof(buf) - len, " RXLP");
+ seq_puts(file, " RXLP");
if (reg & ATH9K_INT_BB_WATCHDOG)
- len += scnprintf(buf + len, sizeof(buf) - len, " BB_WATCHDOG");
+ seq_puts(file, " BB_WATCHDOG");
- len += scnprintf(buf + len, sizeof(buf) - len, "\n");
+ seq_puts(file, "\n");
i = 0;
ath_for_each_chanctx(sc, ctx) {
- if (!ctx->assigned || list_empty(&ctx->vifs))
+ if (list_empty(&ctx->vifs))
continue;
ath9k_calculate_iter_data(sc, ctx, &iter_data);
- len += scnprintf(buf + len, sizeof(buf) - len,
- "VIF-COUNTS: CTX %i AP: %i STA: %i MESH: %i WDS: %i",
- i++, iter_data.naps, iter_data.nstations,
- iter_data.nmeshes, iter_data.nwds);
- len += scnprintf(buf + len, sizeof(buf) - len,
- " ADHOC: %i TOTAL: %hi BEACON-VIF: %hi\n",
- iter_data.nadhocs, sc->cur_chan->nvifs, sc->nbcnvifs);
+ seq_printf(file,
+ "VIFS: CTX %i(%i) AP: %i STA: %i MESH: %i WDS: %i",
+ i++, (int)(ctx->assigned), iter_data.naps,
+ iter_data.nstations,
+ iter_data.nmeshes, iter_data.nwds);
+ seq_printf(file, " ADHOC: %i TOTAL: %hi BEACON-VIF: %hi\n",
+ iter_data.nadhocs, sc->cur_chan->nvifs,
+ sc->nbcnvifs);
}
- if (len > sizeof(buf))
- len = sizeof(buf);
-
- retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
- return retval;
+ return 0;
}
-static ssize_t read_file_reset(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
+static int read_file_reset(struct seq_file *file, void *data)
{
- struct ath_softc *sc = file->private_data;
- char buf[512];
- unsigned int len = 0;
+ struct ath_softc *sc = file->private;
+ static const char * const reset_cause[__RESET_TYPE_MAX] = {
+ [RESET_TYPE_BB_HANG] = "Baseband Hang",
+ [RESET_TYPE_BB_WATCHDOG] = "Baseband Watchdog",
+ [RESET_TYPE_FATAL_INT] = "Fatal HW Error",
+ [RESET_TYPE_TX_ERROR] = "TX HW error",
+ [RESET_TYPE_TX_GTT] = "Transmit timeout",
+ [RESET_TYPE_TX_HANG] = "TX Path Hang",
+ [RESET_TYPE_PLL_HANG] = "PLL RX Hang",
+ [RESET_TYPE_MAC_HANG] = "MAC Hang",
+ [RESET_TYPE_BEACON_STUCK] = "Stuck Beacon",
+ [RESET_TYPE_MCI] = "MCI Reset",
+ [RESET_TYPE_CALIBRATION] = "Calibration error",
+ };
+ int i;
- len += scnprintf(buf + len, sizeof(buf) - len,
- "%17s: %2d\n", "Baseband Hang",
- sc->debug.stats.reset[RESET_TYPE_BB_HANG]);
- len += scnprintf(buf + len, sizeof(buf) - len,
- "%17s: %2d\n", "Baseband Watchdog",
- sc->debug.stats.reset[RESET_TYPE_BB_WATCHDOG]);
- len += scnprintf(buf + len, sizeof(buf) - len,
- "%17s: %2d\n", "Fatal HW Error",
- sc->debug.stats.reset[RESET_TYPE_FATAL_INT]);
- len += scnprintf(buf + len, sizeof(buf) - len,
- "%17s: %2d\n", "TX HW error",
- sc->debug.stats.reset[RESET_TYPE_TX_ERROR]);
- len += scnprintf(buf + len, sizeof(buf) - len,
- "%17s: %2d\n", "TX Path Hang",
- sc->debug.stats.reset[RESET_TYPE_TX_HANG]);
- len += scnprintf(buf + len, sizeof(buf) - len,
- "%17s: %2d\n", "PLL RX Hang",
- sc->debug.stats.reset[RESET_TYPE_PLL_HANG]);
- len += scnprintf(buf + len, sizeof(buf) - len,
- "%17s: %2d\n", "MAC Hang",
- sc->debug.stats.reset[RESET_TYPE_MAC_HANG]);
- len += scnprintf(buf + len, sizeof(buf) - len,
- "%17s: %2d\n", "Stuck Beacon",
- sc->debug.stats.reset[RESET_TYPE_BEACON_STUCK]);
- len += scnprintf(buf + len, sizeof(buf) - len,
- "%17s: %2d\n", "MCI Reset",
- sc->debug.stats.reset[RESET_TYPE_MCI]);
+ for (i = 0; i < ARRAY_SIZE(reset_cause); i++) {
+ if (!reset_cause[i])
+ continue;
- if (len > sizeof(buf))
- len = sizeof(buf);
+ seq_printf(file, "%17s: %2d\n", reset_cause[i],
+ sc->debug.stats.reset[i]);
+ }
- return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+ return 0;
}
void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf,
@@ -926,32 +837,56 @@
TX_STAT_INC(qnum, delim_underrun);
}
+static int open_file_xmit(struct inode *inode, struct file *f)
+{
+ return single_open(f, read_file_xmit, inode->i_private);
+}
+
static const struct file_operations fops_xmit = {
- .read = read_file_xmit,
- .open = simple_open,
+ .read = seq_read,
+ .open = open_file_xmit,
.owner = THIS_MODULE,
- .llseek = default_llseek,
+ .llseek = seq_lseek,
+ .release = single_release,
};
+static int open_file_queues(struct inode *inode, struct file *f)
+{
+ return single_open(f, read_file_queues, inode->i_private);
+}
+
static const struct file_operations fops_queues = {
- .read = read_file_queues,
- .open = simple_open,
+ .read = seq_read,
+ .open = open_file_queues,
.owner = THIS_MODULE,
- .llseek = default_llseek,
+ .llseek = seq_lseek,
+ .release = single_release,
};
+static int open_file_misc(struct inode *inode, struct file *f)
+{
+ return single_open(f, read_file_misc, inode->i_private);
+}
+
static const struct file_operations fops_misc = {
- .read = read_file_misc,
- .open = simple_open,
+ .read = seq_read,
+ .open = open_file_misc,
.owner = THIS_MODULE,
- .llseek = default_llseek,
+ .llseek = seq_lseek,
+ .release = single_release,
};
+static int open_file_reset(struct inode *inode, struct file *f)
+{
+ return single_open(f, read_file_reset, inode->i_private);
+}
+
static const struct file_operations fops_reset = {
- .read = read_file_reset,
- .open = simple_open,
+ .read = seq_read,
+ .open = open_file_reset,
.owner = THIS_MODULE,
- .llseek = default_llseek,
+ .llseek = seq_lseek,
+ .release = single_release,
};
void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs)
@@ -960,7 +895,7 @@
}
static ssize_t read_file_regidx(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
+ size_t count, loff_t *ppos)
{
struct ath_softc *sc = file->private_data;
char buf[32];
@@ -971,7 +906,7 @@
}
static ssize_t write_file_regidx(struct file *file, const char __user *user_buf,
- size_t count, loff_t *ppos)
+ size_t count, loff_t *ppos)
{
struct ath_softc *sc = file->private_data;
unsigned long regidx;
@@ -999,7 +934,7 @@
};
static ssize_t read_file_regval(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
+ size_t count, loff_t *ppos)
{
struct ath_softc *sc = file->private_data;
struct ath_hw *ah = sc->sc_ah;
@@ -1015,7 +950,7 @@
}
static ssize_t write_file_regval(struct file *file, const char __user *user_buf,
- size_t count, loff_t *ppos)
+ size_t count, loff_t *ppos)
{
struct ath_softc *sc = file->private_data;
struct ath_hw *ah = sc->sc_ah;
@@ -1081,57 +1016,45 @@
.llseek = default_llseek,/* read accesses f_pos */
};
-static ssize_t read_file_dump_nfcal(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
+static int read_file_dump_nfcal(struct seq_file *file, void *data)
{
- struct ath_softc *sc = file->private_data;
+ struct ath_softc *sc = file->private;
struct ath_hw *ah = sc->sc_ah;
struct ath9k_nfcal_hist *h = sc->cur_chan->caldata.nfCalHist;
struct ath_common *common = ath9k_hw_common(ah);
struct ieee80211_conf *conf = &common->hw->conf;
- u32 len = 0, size = 1500;
u32 i, j;
- ssize_t retval = 0;
- char *buf;
u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
u8 nread;
- buf = kzalloc(size, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- len += scnprintf(buf + len, size - len,
- "Channel Noise Floor : %d\n", ah->noise);
- len += scnprintf(buf + len, size - len,
- "Chain | privNF | # Readings | NF Readings\n");
+ seq_printf(file, "Channel Noise Floor : %d\n", ah->noise);
+ seq_puts(file, "Chain | privNF | # Readings | NF Readings\n");
for (i = 0; i < NUM_NF_READINGS; i++) {
if (!(chainmask & (1 << i)) ||
((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf)))
continue;
nread = AR_PHY_CCA_FILTERWINDOW_LENGTH - h[i].invalidNFcount;
- len += scnprintf(buf + len, size - len, " %d\t %d\t %d\t\t",
- i, h[i].privNF, nread);
+ seq_printf(file, " %d\t %d\t %d\t\t", i, h[i].privNF, nread);
for (j = 0; j < nread; j++)
- len += scnprintf(buf + len, size - len,
- " %d", h[i].nfCalBuffer[j]);
- len += scnprintf(buf + len, size - len, "\n");
+ seq_printf(file, " %d", h[i].nfCalBuffer[j]);
+ seq_puts(file, "\n");
}
- if (len > size)
- len = size;
+ return 0;
+}
- retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
- kfree(buf);
-
- return retval;
+static int open_file_dump_nfcal(struct inode *inode, struct file *f)
+{
+ return single_open(f, read_file_dump_nfcal, inode->i_private);
}
static const struct file_operations fops_dump_nfcal = {
- .read = read_file_dump_nfcal,
- .open = simple_open,
+ .read = seq_read,
+ .open = open_file_dump_nfcal,
.owner = THIS_MODULE,
- .llseek = default_llseek,
+ .llseek = seq_lseek,
+ .release = single_release,
};
#ifdef CPTCFG_ATH9K_BTCOEX_SUPPORT
@@ -1315,7 +1238,7 @@
void ath9k_deinit_debug(struct ath_softc *sc)
{
- ath9k_spectral_deinit_debug(sc);
+ ath9k_cmn_spectral_deinit_debug(&sc->spec_priv);
}
int ath9k_init_debug(struct ath_hw *ah)
@@ -1335,7 +1258,7 @@
ath9k_dfs_init_debug(sc);
ath9k_tx99_init_debug(sc);
- ath9k_spectral_init_debug(sc);
+ ath9k_cmn_spectral_init_debug(&sc->spec_priv, sc->debug.debugfs_phy);
debugfs_create_file("dma", S_IRUSR, sc->debug.debugfs_phy, sc,
&fops_dma);
diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h
index 82847c0..30340f1 100644
--- a/drivers/net/wireless/ath/ath9k/debug.h
+++ b/drivers/net/wireless/ath/ath9k/debug.h
@@ -49,6 +49,7 @@
RESET_TYPE_MAC_HANG,
RESET_TYPE_BEACON_STUCK,
RESET_TYPE_MCI,
+ RESET_TYPE_CALIBRATION,
__RESET_TYPE_MAX
};
@@ -195,12 +196,11 @@
#define TXSTATS sc->debug.stats.txstats
#define PR(str, elem) \
do { \
- len += scnprintf(buf + len, size - len, \
- "%s%13u%11u%10u%10u\n", str, \
- TXSTATS[PR_QNUM(IEEE80211_AC_BE)].elem,\
- TXSTATS[PR_QNUM(IEEE80211_AC_BK)].elem,\
- TXSTATS[PR_QNUM(IEEE80211_AC_VI)].elem,\
- TXSTATS[PR_QNUM(IEEE80211_AC_VO)].elem); \
+ seq_printf(file, "%s%13u%11u%10u%10u\n", str, \
+ TXSTATS[PR_QNUM(IEEE80211_AC_BE)].elem,\
+ TXSTATS[PR_QNUM(IEEE80211_AC_BK)].elem,\
+ TXSTATS[PR_QNUM(IEEE80211_AC_VI)].elem,\
+ TXSTATS[PR_QNUM(IEEE80211_AC_VO)].elem); \
} while(0)
struct ath_rx_rate_stats {
diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c
index 3516bbd..303aa35 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom_def.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c
@@ -262,7 +262,7 @@
{
struct ar5416_eeprom_def *eep = &ah->eeprom.def;
struct ath_common *common = ath9k_hw_common(ah);
- u16 *eepdata, temp, magic, magic2;
+ u16 *eepdata, temp, magic;
u32 sum = 0, el;
bool need_swap = false;
int i, addr, size;
@@ -272,27 +272,16 @@
return false;
}
- if (!ath9k_hw_use_flash(ah)) {
- ath_dbg(common, EEPROM, "Read Magic = 0x%04X\n", magic);
+ if (swab16(magic) == AR5416_EEPROM_MAGIC &&
+ !(ah->ah_flags & AH_NO_EEP_SWAP)) {
+ size = sizeof(struct ar5416_eeprom_def);
+ need_swap = true;
+ eepdata = (u16 *) (&ah->eeprom);
- if (magic != AR5416_EEPROM_MAGIC) {
- magic2 = swab16(magic);
-
- if (magic2 == AR5416_EEPROM_MAGIC) {
- size = sizeof(struct ar5416_eeprom_def);
- need_swap = true;
- eepdata = (u16 *) (&ah->eeprom);
-
- for (addr = 0; addr < size / sizeof(u16); addr++) {
- temp = swab16(*eepdata);
- *eepdata = temp;
- eepdata++;
- }
- } else {
- ath_err(common,
- "Invalid EEPROM Magic. Endianness mismatch.\n");
- return -EINVAL;
- }
+ for (addr = 0; addr < size / sizeof(u16); addr++) {
+ temp = swab16(*eepdata);
+ *eepdata = temp;
+ eepdata++;
}
}
diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c
index 3d9e8e4..48ee417 100644
--- a/drivers/net/wireless/ath/ath9k/gpio.c
+++ b/drivers/net/wireless/ath/ath9k/gpio.c
@@ -25,7 +25,12 @@
enum led_brightness brightness)
{
struct ath_softc *sc = container_of(led_cdev, struct ath_softc, led_cdev);
- ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, (brightness == LED_OFF));
+ u32 val = (brightness == LED_OFF);
+
+ if (sc->sc_ah->config.led_active_high)
+ val = !val;
+
+ ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, val);
}
void ath_deinit_leds(struct ath_softc *sc)
@@ -82,7 +87,7 @@
ath9k_hw_cfg_output(ah, ah->led_pin, AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
/* LED off, active low */
- ath9k_hw_set_gpio(ah, ah->led_pin, 1);
+ ath9k_hw_set_gpio(ah, ah->led_pin, (ah->config.led_active_high) ? 0 : 1);
}
#endif
diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h
index e9278bc..de6d475 100644
--- a/drivers/net/wireless/ath/ath9k/htc.h
+++ b/drivers/net/wireless/ath/ath9k/htc.h
@@ -481,6 +481,7 @@
unsigned long op_flags;
struct ath9k_hw_cal_data caldata;
+ struct ath_spec_scan_priv spec_priv;
spinlock_t beacon_lock;
struct ath_beacon_config cur_beacon_conf;
@@ -625,8 +626,12 @@
#endif
#ifdef CPTCFG_ATH9K_HTC_DEBUGFS
int ath9k_htc_init_debug(struct ath_hw *ah);
+void ath9k_htc_deinit_debug(struct ath9k_htc_priv *priv);
#else
static inline int ath9k_htc_init_debug(struct ath_hw *ah) { return 0; };
+static inline void ath9k_htc_deinit_debug(struct ath9k_htc_priv *priv)
+{
+}
#endif /* CPTCFG_ATH9K_HTC_DEBUGFS */
#endif /* HTC_H */
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
index 8b529e4..8cef1ed 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
@@ -490,6 +490,10 @@
WARN_ON(i != ATH9K_HTC_SSTATS_LEN);
}
+void ath9k_htc_deinit_debug(struct ath9k_htc_priv *priv)
+{
+ ath9k_cmn_spectral_deinit_debug(&priv->spec_priv);
+}
int ath9k_htc_init_debug(struct ath_hw *ah)
{
@@ -501,6 +505,8 @@
if (!priv->debug.debugfs_phy)
return -ENOMEM;
+ ath9k_cmn_spectral_init_debug(&priv->spec_priv, priv->debug.debugfs_phy);
+
debugfs_create_file("tgt_int_stats", S_IRUSR, priv->debug.debugfs_phy,
priv, &fops_tgt_int_stats);
debugfs_create_file("tgt_tx_stats", S_IRUSR, priv->debug.debugfs_phy,
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
index b153389..33d9103 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
@@ -53,6 +53,21 @@
};
#endif
+static void ath9k_htc_op_ps_wakeup(struct ath_common *common)
+{
+ ath9k_htc_ps_wakeup((struct ath9k_htc_priv *) common->priv);
+}
+
+static void ath9k_htc_op_ps_restore(struct ath_common *common)
+{
+ ath9k_htc_ps_restore((struct ath9k_htc_priv *) common->priv);
+}
+
+static struct ath_ps_ops ath9k_htc_ps_ops = {
+ .wakeup = ath9k_htc_op_ps_wakeup,
+ .restore = ath9k_htc_op_ps_restore,
+};
+
static int ath9k_htc_wait_for_target(struct ath9k_htc_priv *priv)
{
int time_left;
@@ -87,6 +102,7 @@
wiphy_rfkill_stop_polling(hw->wiphy);
ath9k_deinit_leds(priv);
+ ath9k_htc_deinit_debug(priv);
ieee80211_unregister_hw(hw);
ath9k_rx_cleanup(priv);
ath9k_tx_cleanup(priv);
@@ -449,6 +465,14 @@
common->last_rssi = ATH_RSSI_DUMMY_MARKER;
priv->ah->opmode = NL80211_IFTYPE_STATION;
+
+ priv->spec_priv.ah = priv->ah;
+ priv->spec_priv.spec_config.enabled = 0;
+ priv->spec_priv.spec_config.short_repeat = false;
+ priv->spec_priv.spec_config.count = 8;
+ priv->spec_priv.spec_config.endless = false;
+ priv->spec_priv.spec_config.period = 0x12;
+ priv->spec_priv.spec_config.fft_period = 0x02;
}
static int ath9k_init_priv(struct ath9k_htc_priv *priv,
@@ -478,6 +502,7 @@
common = ath9k_hw_common(ah);
common->ops = &ah->reg_ops;
+ common->ps_ops = &ath9k_htc_ps_ops;
common->bus_ops = &ath9k_usb_bus_ops;
common->ah = ah;
common->hw = priv->hw;
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
index c03b3fa..ac9563e 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
@@ -314,6 +314,10 @@
mod_timer(&priv->tx.cleanup_timer,
jiffies + msecs_to_jiffies(ATH9K_HTC_TX_CLEANUP_INTERVAL));
+ /* perform spectral scan if requested. */
+ if (test_bit(ATH_OP_SCANNING, &common->op_flags) &&
+ priv->spec_priv.spectral_mode == SPECTRAL_CHANSCAN)
+ ath9k_cmn_spectral_scan_trigger(common, &priv->spec_priv);
err:
ath9k_htc_ps_restore(priv);
return ret;
@@ -1443,7 +1447,7 @@
key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
- if (priv->ah->sw_mgmt_crypto &&
+ if (priv->ah->sw_mgmt_crypto_tx &&
key->cipher == WLAN_CIPHER_SUITE_CCMP)
key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
ret = 0;
@@ -1687,7 +1691,9 @@
return ret;
}
-static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
+static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ const u8 *mac_addr)
{
struct ath9k_htc_priv *priv = hw->priv;
struct ath_common *common = ath9k_hw_common(priv->ah);
@@ -1701,7 +1707,8 @@
mutex_unlock(&priv->mutex);
}
-static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw)
+static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
{
struct ath9k_htc_priv *priv = hw->priv;
struct ath_common *common = ath9k_hw_common(priv->ah);
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
index f0484b1..a0f58e2 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
@@ -946,7 +946,7 @@
static void rx_status_htc_to_ath(struct ath_rx_status *rx_stats,
struct ath_htc_rx_status *rxstatus)
{
- rx_stats->rs_datalen = rxstatus->rs_datalen;
+ rx_stats->rs_datalen = be16_to_cpu(rxstatus->rs_datalen);
rx_stats->rs_status = rxstatus->rs_status;
rx_stats->rs_phyerr = rxstatus->rs_phyerr;
rx_stats->rs_rssi = rxstatus->rs_rssi;
@@ -1012,6 +1012,20 @@
* separately to avoid doing two lookups for a rate for each frame.
*/
hdr = (struct ieee80211_hdr *)skb->data;
+
+ /*
+ * Process PHY errors and return so that the packet
+ * can be dropped.
+ */
+ if (rx_stats.rs_status & ATH9K_RXERR_PHY) {
+ /* TODO: Not using DFS processing now. */
+ if (ath_cmn_process_fft(&priv->spec_priv, hdr,
+ &rx_stats, rx_status->mactime)) {
+ /* TODO: Code to collect spectral scan statistics */
+ }
+ goto rx_next;
+ }
+
if (!ath9k_cmn_rx_accept(common, hdr, rx_status, &rx_stats,
&decrypt_error, priv->rxfilter))
goto rx_next;
diff --git a/drivers/net/wireless/ath/ath9k/hw-ops.h b/drivers/net/wireless/ath/ath9k/hw-ops.h
index 134862c..6dd44d4 100644
--- a/drivers/net/wireless/ath/ath9k/hw-ops.h
+++ b/drivers/net/wireless/ath/ath9k/hw-ops.h
@@ -41,10 +41,9 @@
ath9k_hw_ops(ah)->set_desc_link(ds, link);
}
-static inline bool ath9k_hw_calibrate(struct ath_hw *ah,
- struct ath9k_channel *chan,
- u8 rxchainmask,
- bool longcal)
+static inline int ath9k_hw_calibrate(struct ath_hw *ah,
+ struct ath9k_channel *chan,
+ u8 rxchainmask, bool longcal)
{
return ath9k_hw_ops(ah)->calibrate(ah, chan, rxchainmask, longcal);
}
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 3e83eec..82c7afa 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -19,6 +19,7 @@
#include <linux/module.h>
#include <linux/time.h>
#include <linux/bitops.h>
+#include <linux/etherdevice.h>
#include <asm/unaligned.h>
#include "hw.h"
@@ -446,8 +447,16 @@
common->macaddr[2 * i] = eeval >> 8;
common->macaddr[2 * i + 1] = eeval & 0xff;
}
- if (sum == 0 || sum == 0xffff * 3)
- return -EADDRNOTAVAIL;
+ if (!is_valid_ether_addr(common->macaddr)) {
+ ath_err(common,
+ "eeprom contains invalid mac address: %pM\n",
+ common->macaddr);
+
+ random_ether_addr(common->macaddr);
+ ath_err(common,
+ "random mac address will be used: %pM\n",
+ common->macaddr);
+ }
return 0;
}
@@ -861,19 +870,6 @@
udelay(RTC_PLL_SETTLE_DELAY);
REG_WRITE(ah, AR_RTC_SLEEP_CLK, AR_RTC_FORCE_DERIVED_CLK);
-
- if (AR_SREV_9340(ah) || AR_SREV_9550(ah)) {
- if (ah->is_clk_25mhz) {
- REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x17c << 1);
- REG_WRITE(ah, AR_SLP32_MODE, 0x0010f3d7);
- REG_WRITE(ah, AR_SLP32_INC, 0x0001e7ae);
- } else {
- REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x261 << 1);
- REG_WRITE(ah, AR_SLP32_MODE, 0x0010f400);
- REG_WRITE(ah, AR_SLP32_INC, 0x0001e800);
- }
- udelay(100);
- }
}
static void ath9k_hw_init_interrupt_masks(struct ath_hw *ah,
@@ -1589,16 +1585,22 @@
* frames when constructing CCMP AAD. */
REG_RMW_FIELD(ah, AR_AES_MUTE_MASK1, AR_AES_MUTE_MASK1_FC_MGMT,
0xc7ff);
- ah->sw_mgmt_crypto = false;
+ if (AR_SREV_9271(ah) || AR_DEVID_7010(ah))
+ ah->sw_mgmt_crypto_tx = true;
+ else
+ ah->sw_mgmt_crypto_tx = false;
+ ah->sw_mgmt_crypto_rx = false;
} else if (AR_SREV_9160_10_OR_LATER(ah)) {
/* Disable hardware crypto for management frames */
REG_CLR_BIT(ah, AR_PCU_MISC_MODE2,
AR_PCU_MISC_MODE2_MGMT_CRYPTO_ENABLE);
REG_SET_BIT(ah, AR_PCU_MISC_MODE2,
AR_PCU_MISC_MODE2_NO_CRYPTO_FOR_NON_DATA_PKT);
- ah->sw_mgmt_crypto = true;
+ ah->sw_mgmt_crypto_tx = true;
+ ah->sw_mgmt_crypto_rx = true;
} else {
- ah->sw_mgmt_crypto = true;
+ ah->sw_mgmt_crypto_tx = true;
+ ah->sw_mgmt_crypto_rx = true;
}
}
@@ -1945,6 +1947,8 @@
REGWRITE_BUFFER_FLUSH(ah);
+ ath9k_hw_gen_timer_start_tsf2(ah);
+
ath9k_hw_init_desc(ah);
if (ath9k_hw_btcoex_is_enabled(ah))
@@ -1953,8 +1957,10 @@
if (ath9k_hw_mci_is_enabled(ah))
ar9003_mci_check_bt(ah);
- ath9k_hw_loadnf(ah, chan);
- ath9k_hw_start_nfcal(ah, true);
+ if (AR_SREV_9300_20_OR_LATER(ah)) {
+ ath9k_hw_loadnf(ah, chan);
+ ath9k_hw_start_nfcal(ah, true);
+ }
if (AR_SREV_9300_20_OR_LATER(ah))
ar9003_hw_bb_watchdog_config(ah);
@@ -2322,7 +2328,6 @@
struct ath9k_hw_capabilities *pCap = &ah->caps;
struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah);
struct ath_common *common = ath9k_hw_common(ah);
- unsigned int chip_chainmask;
u16 eeval;
u8 ant_div_ctl1, tx_chainmask, rx_chainmask;
@@ -2342,31 +2347,40 @@
}
eeval = ah->eep_ops->get_eeprom(ah, EEP_OP_MODE);
- if ((eeval & (AR5416_OPFLAGS_11G | AR5416_OPFLAGS_11A)) == 0) {
- ath_err(common,
- "no band has been marked as supported in EEPROM\n");
- return -EINVAL;
+
+ if (eeval & AR5416_OPFLAGS_11A) {
+ if (ah->disable_5ghz)
+ ath_warn(common, "disabling 5GHz band\n");
+ else
+ pCap->hw_caps |= ATH9K_HW_CAP_5GHZ;
}
- if (eeval & AR5416_OPFLAGS_11A)
- pCap->hw_caps |= ATH9K_HW_CAP_5GHZ;
+ if (eeval & AR5416_OPFLAGS_11G) {
+ if (ah->disable_2ghz)
+ ath_warn(common, "disabling 2GHz band\n");
+ else
+ pCap->hw_caps |= ATH9K_HW_CAP_2GHZ;
+ }
- if (eeval & AR5416_OPFLAGS_11G)
- pCap->hw_caps |= ATH9K_HW_CAP_2GHZ;
+ if ((pCap->hw_caps & (ATH9K_HW_CAP_2GHZ | ATH9K_HW_CAP_5GHZ)) == 0) {
+ ath_err(common, "both bands are disabled\n");
+ return -EINVAL;
+ }
if (AR_SREV_9485(ah) ||
AR_SREV_9285(ah) ||
AR_SREV_9330(ah) ||
AR_SREV_9565(ah))
- chip_chainmask = 1;
- else if (AR_SREV_9462(ah))
- chip_chainmask = 3;
+ pCap->chip_chainmask = 1;
else if (!AR_SREV_9280_20_OR_LATER(ah))
- chip_chainmask = 7;
- else if (!AR_SREV_9300_20_OR_LATER(ah) || AR_SREV_9340(ah))
- chip_chainmask = 3;
+ pCap->chip_chainmask = 7;
+ else if (!AR_SREV_9300_20_OR_LATER(ah) ||
+ AR_SREV_9340(ah) ||
+ AR_SREV_9462(ah) ||
+ AR_SREV_9531(ah))
+ pCap->chip_chainmask = 3;
else
- chip_chainmask = 7;
+ pCap->chip_chainmask = 7;
pCap->tx_chainmask = ah->eep_ops->get_eeprom(ah, EEP_TX_MASK);
/*
@@ -2384,8 +2398,8 @@
/* Use rx_chainmask from EEPROM. */
pCap->rx_chainmask = ah->eep_ops->get_eeprom(ah, EEP_RX_MASK);
- pCap->tx_chainmask = fixup_chainmask(chip_chainmask, pCap->tx_chainmask);
- pCap->rx_chainmask = fixup_chainmask(chip_chainmask, pCap->rx_chainmask);
+ pCap->tx_chainmask = fixup_chainmask(pCap->chip_chainmask, pCap->tx_chainmask);
+ pCap->rx_chainmask = fixup_chainmask(pCap->chip_chainmask, pCap->rx_chainmask);
ah->txchainmask = pCap->tx_chainmask;
ah->rxchainmask = pCap->rx_chainmask;
@@ -2899,6 +2913,16 @@
}
EXPORT_SYMBOL(ath9k_hw_gettsf32);
+void ath9k_hw_gen_timer_start_tsf2(struct ath_hw *ah)
+{
+ struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
+
+ if (timer_table->tsf2_enabled) {
+ REG_SET_BIT(ah, AR_DIRECT_CONNECT, AR_DC_AP_STA_EN);
+ REG_SET_BIT(ah, AR_RESET_TSF, AR_RESET_TSF2_ONCE);
+ }
+}
+
struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah,
void (*trigger)(void *),
void (*overflow)(void *),
@@ -2909,7 +2933,11 @@
struct ath_gen_timer *timer;
if ((timer_index < AR_FIRST_NDP_TIMER) ||
- (timer_index >= ATH_MAX_GEN_TIMER))
+ (timer_index >= ATH_MAX_GEN_TIMER))
+ return NULL;
+
+ if ((timer_index > AR_FIRST_NDP_TIMER) &&
+ !AR_SREV_9300_20_OR_LATER(ah))
return NULL;
timer = kzalloc(sizeof(struct ath_gen_timer), GFP_KERNEL);
@@ -2923,6 +2951,11 @@
timer->overflow = overflow;
timer->arg = arg;
+ if ((timer_index > AR_FIRST_NDP_TIMER) && !timer_table->tsf2_enabled) {
+ timer_table->tsf2_enabled = true;
+ ath9k_hw_gen_timer_start_tsf2(ah);
+ }
+
return timer;
}
EXPORT_SYMBOL(ath_gen_timer_alloc);
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index a578e51..fda6cb5 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -217,8 +217,8 @@
#define AH_WOW_BEACON_MISS BIT(3)
enum ath_hw_txq_subtype {
- ATH_TXQ_AC_BE = 0,
- ATH_TXQ_AC_BK = 1,
+ ATH_TXQ_AC_BK = 0,
+ ATH_TXQ_AC_BE = 1,
ATH_TXQ_AC_VI = 2,
ATH_TXQ_AC_VO = 3,
};
@@ -244,13 +244,20 @@
ATH9K_HW_CAP_2GHZ = BIT(11),
ATH9K_HW_CAP_5GHZ = BIT(12),
ATH9K_HW_CAP_APM = BIT(13),
+#ifdef CPTCFG_ATH9K_PCOEM
ATH9K_HW_CAP_RTT = BIT(14),
ATH9K_HW_CAP_MCI = BIT(15),
- ATH9K_HW_CAP_DFS = BIT(16),
- ATH9K_HW_WOW_DEVICE_CAPABLE = BIT(17),
- ATH9K_HW_CAP_PAPRD = BIT(18),
- ATH9K_HW_CAP_FCC_BAND_SWITCH = BIT(19),
- ATH9K_HW_CAP_BT_ANT_DIV = BIT(20),
+ ATH9K_HW_WOW_DEVICE_CAPABLE = BIT(16),
+ ATH9K_HW_CAP_BT_ANT_DIV = BIT(17),
+#else
+ ATH9K_HW_CAP_RTT = 0,
+ ATH9K_HW_CAP_MCI = 0,
+ ATH9K_HW_WOW_DEVICE_CAPABLE = 0,
+ ATH9K_HW_CAP_BT_ANT_DIV = 0,
+#endif
+ ATH9K_HW_CAP_DFS = BIT(18),
+ ATH9K_HW_CAP_PAPRD = BIT(19),
+ ATH9K_HW_CAP_FCC_BAND_SWITCH = BIT(20),
};
/*
@@ -269,6 +276,7 @@
u16 rts_aggr_limit;
u8 tx_chainmask;
u8 rx_chainmask;
+ u8 chip_chainmask;
u8 max_txchains;
u8 max_rxchains;
u8 num_gpio_pins;
@@ -322,6 +330,7 @@
bool alt_mingainidx;
bool no_pll_pwrsave;
bool tx_gain_buffalo;
+ bool led_active_high;
};
enum ath9k_int {
@@ -517,6 +526,7 @@
struct ath_gen_timer_table {
struct ath_gen_timer *timers[ATH_MAX_GEN_TIMER];
u16 timer_mask;
+ bool tsf2_enabled;
};
struct ath_hw_antcomb_conf {
@@ -681,10 +691,8 @@
bool power_off);
void (*rx_enable)(struct ath_hw *ah);
void (*set_desc_link)(void *ds, u32 link);
- bool (*calibrate)(struct ath_hw *ah,
- struct ath9k_channel *chan,
- u8 rxchainmask,
- bool longcal);
+ int (*calibrate)(struct ath_hw *ah, struct ath9k_channel *chan,
+ u8 rxchainmask, bool longcal);
bool (*get_isr)(struct ath_hw *ah, enum ath9k_int *masked,
u32 *sync_cause_p);
void (*set_txdesc)(struct ath_hw *ah, void *ds,
@@ -726,6 +734,7 @@
#define AH_USE_EEPROM 0x1
#define AH_UNPLUGGED 0x2 /* The card has been physically removed. */
#define AH_FASTCC 0x4
+#define AH_NO_EEP_SWAP 0x8 /* Do not swap EEPROM data */
struct ath_hw {
struct ath_ops reg_ops;
@@ -747,7 +756,8 @@
} eeprom;
const struct eeprom_ops *eep_ops;
- bool sw_mgmt_crypto;
+ bool sw_mgmt_crypto_tx;
+ bool sw_mgmt_crypto_rx;
bool is_pciexpress;
bool aspm_enabled;
bool is_monitoring;
@@ -924,10 +934,16 @@
bool is_clk_25mhz;
int (*get_mac_revision)(void);
int (*external_reset)(void);
+ bool disable_2ghz;
+ bool disable_5ghz;
const struct firmware *eeprom_blob;
struct ath_dynack dynack;
+
+ bool tpc_enabled;
+ u8 tx_power[Ar5416RateSize];
+ u8 tx_power_stbc[Ar5416RateSize];
};
struct ath_bus_ops {
@@ -1027,6 +1043,7 @@
struct ath_gen_timer *timer,
u32 timer_next,
u32 timer_period);
+void ath9k_hw_gen_timer_start_tsf2(struct ath_hw *ah);
void ath9k_hw_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer);
void ath_gen_timer_free(struct ath_hw *ah, struct ath_gen_timer *timer);
@@ -1067,6 +1084,8 @@
bool ar9003_paprd_is_done(struct ath_hw *ah);
bool ar9003_is_paprd_enabled(struct ath_hw *ah);
void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx);
+void ar9003_hw_init_rate_txpower(struct ath_hw *ah, u8 *rate_array,
+ struct ath9k_channel *chan);
/* Hardware family op attach helpers */
int ar5008_hw_attach_phy_ops(struct ath_hw *ah);
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 4df734e..7764675 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -104,6 +104,21 @@
static void ath9k_deinit_softc(struct ath_softc *sc);
+static void ath9k_op_ps_wakeup(struct ath_common *common)
+{
+ ath9k_ps_wakeup((struct ath_softc *) common->priv);
+}
+
+static void ath9k_op_ps_restore(struct ath_common *common)
+{
+ ath9k_ps_restore((struct ath_softc *) common->priv);
+}
+
+static struct ath_ps_ops ath9k_ps_ops = {
+ .wakeup = ath9k_op_ps_wakeup,
+ .restore = ath9k_op_ps_restore,
+};
+
/*
* Read and write, they both share the same lock. We do this to serialize
* reads and writes on Atheros 802.11n PCI devices only. This is required
@@ -188,17 +203,20 @@
ath_reg_notifier_apply(wiphy, request, reg);
/* Set tx power */
- if (ah->curchan) {
- sc->cur_chan->txpower = 2 * ah->curchan->chan->max_power;
- ath9k_ps_wakeup(sc);
- ath9k_hw_set_txpowerlimit(ah, sc->cur_chan->txpower, false);
- sc->curtxpow = ath9k_hw_regulatory(ah)->power_limit;
- /* synchronize DFS detector if regulatory domain changed */
- if (sc->dfs_detector != NULL)
- sc->dfs_detector->set_dfs_domain(sc->dfs_detector,
- request->dfs_region);
- ath9k_ps_restore(sc);
- }
+ if (!ah->curchan)
+ return;
+
+ sc->cur_chan->txpower = 2 * ah->curchan->chan->max_power;
+ ath9k_ps_wakeup(sc);
+ ath9k_hw_set_txpowerlimit(ah, sc->cur_chan->txpower, false);
+ ath9k_cmn_update_txpow(ah, sc->cur_chan->cur_txpower,
+ sc->cur_chan->txpower,
+ &sc->cur_chan->cur_txpower);
+ /* synchronize DFS detector if regulatory domain changed */
+ if (sc->dfs_detector != NULL)
+ sc->dfs_detector->set_dfs_domain(sc->dfs_detector,
+ request->dfs_region);
+ ath9k_ps_restore(sc);
}
/*
@@ -364,12 +382,13 @@
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB)
sc->ant_comb.count = ATH_ANT_DIV_COMB_INIT_COUNT;
- sc->spec_config.enabled = 0;
- sc->spec_config.short_repeat = true;
- sc->spec_config.count = 8;
- sc->spec_config.endless = false;
- sc->spec_config.period = 0xFF;
- sc->spec_config.fft_period = 0xF;
+ sc->spec_priv.ah = sc->sc_ah;
+ sc->spec_priv.spec_config.enabled = 0;
+ sc->spec_priv.spec_config.short_repeat = true;
+ sc->spec_priv.spec_config.count = 8;
+ sc->spec_priv.spec_config.endless = false;
+ sc->spec_priv.spec_config.period = 0xFF;
+ sc->spec_priv.spec_config.fft_period = 0xF;
}
static void ath9k_init_pcoem_platform(struct ath_softc *sc)
@@ -378,6 +397,9 @@
struct ath9k_hw_capabilities *pCap = &ah->caps;
struct ath_common *common = ath9k_hw_common(ah);
+ if (!IS_ENABLED(CPTCFG_ATH9K_PCOEM))
+ return;
+
if (common->bus_ops->ath_bus_type != ATH_PCI)
return;
@@ -435,6 +457,9 @@
ah->config.no_pll_pwrsave = true;
ath_info(common, "Disable PLL PowerSave\n");
}
+
+ if (sc->driver_data & ATH9K_PCI_LED_ACT_HI)
+ ah->config.led_active_high = true;
}
static void ath9k_eeprom_request_cb(const struct firmware *eeprom_blob,
@@ -523,10 +548,14 @@
ah->reg_ops.read = ath9k_ioread32;
ah->reg_ops.write = ath9k_iowrite32;
ah->reg_ops.rmw = ath9k_reg_rmw;
- sc->sc_ah = ah;
pCap = &ah->caps;
common = ath9k_hw_common(ah);
+
+ /* Will be cleared in ath9k_start() */
+ set_bit(ATH_OP_INVALID, &common->op_flags);
+
+ sc->sc_ah = ah;
sc->dfs_detector = dfs_pattern_detector_init(common, NL80211_DFS_UNSET);
sc->tx99_power = MAX_RATE_POWER + 1;
init_waitqueue_head(&sc->tx_wait);
@@ -544,10 +573,15 @@
ah->is_clk_25mhz = pdata->is_clk_25mhz;
ah->get_mac_revision = pdata->get_mac_revision;
ah->external_reset = pdata->external_reset;
+ ah->disable_2ghz = pdata->disable_2ghz;
+ ah->disable_5ghz = pdata->disable_5ghz;
+ if (!pdata->endian_check)
+ ah->ah_flags |= AH_NO_EEP_SWAP;
}
common->ops = &ah->reg_ops;
common->bus_ops = bus_ops;
+ common->ps_ops = &ath9k_ps_ops;
common->ah = ah;
common->hw = sc->hw;
common->priv = sc;
@@ -886,9 +920,6 @@
common = ath9k_hw_common(ah);
ath9k_set_hw_capab(sc, hw);
- /* Will be cleared in ath9k_start() */
- set_bit(ATH_OP_INVALID, &common->op_flags);
-
/* Initialize regulatory */
error = ath_regd_init(&common->regulatory, sc->hw->wiphy,
ath9k_reg_notifier);
diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c
index 2343f56..b829263 100644
--- a/drivers/net/wireless/ath/ath9k/link.c
+++ b/drivers/net/wireless/ath/ath9k/link.c
@@ -371,9 +371,15 @@
/* Perform calibration if necessary */
if (longcal || shortcal) {
- common->ani.caldone =
- ath9k_hw_calibrate(ah, ah->curchan,
- ah->rxchainmask, longcal);
+ int ret = ath9k_hw_calibrate(ah, ah->curchan, ah->rxchainmask,
+ longcal);
+ if (ret < 0) {
+ common->ani.caldone = 0;
+ ath9k_queue_reset(sc, RESET_TYPE_CALIBRATION);
+ return;
+ }
+
+ common->ani.caldone = ret;
}
ath_dbg(common, ANI,
diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c
index 275205a..3e58bfa 100644
--- a/drivers/net/wireless/ath/ath9k/mac.c
+++ b/drivers/net/wireless/ath/ath9k/mac.c
@@ -311,14 +311,7 @@
q = ATH9K_NUM_TX_QUEUES - 3;
break;
case ATH9K_TX_QUEUE_DATA:
- for (q = 0; q < ATH9K_NUM_TX_QUEUES; q++)
- if (ah->txq[q].tqi_type ==
- ATH9K_TX_QUEUE_INACTIVE)
- break;
- if (q == ATH9K_NUM_TX_QUEUES) {
- ath_err(common, "No available TX queue\n");
- return -1;
- }
+ q = qinfo->tqi_subtype;
break;
default:
ath_err(common, "Invalid TX queue type: %u\n", type);
diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h
index aa69cea..e55fa11 100644
--- a/drivers/net/wireless/ath/ath9k/mac.h
+++ b/drivers/net/wireless/ath/ath9k/mac.h
@@ -704,7 +704,7 @@
enum ath9k_pkt_type type;
enum ath9k_key_type keytype;
u8 keyix;
- u8 txpower;
+ u8 txpower[4];
};
struct ath_hw;
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index e13ad23..4aeddd7 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -54,7 +54,8 @@
}
}
-static bool ath9k_has_pending_frames(struct ath_softc *sc, struct ath_txq *txq)
+static bool ath9k_has_pending_frames(struct ath_softc *sc, struct ath_txq *txq,
+ bool sw_pending)
{
bool pending = false;
@@ -65,6 +66,9 @@
goto out;
}
+ if (!sw_pending)
+ goto out;
+
if (txq->mac80211_qnum >= 0) {
struct list_head *list;
@@ -229,8 +233,9 @@
ath9k_calculate_summary_state(sc, sc->cur_chan);
ath_startrecv(sc);
- ath9k_cmn_update_txpow(ah, sc->curtxpow,
- sc->cur_chan->txpower, &sc->curtxpow);
+ ath9k_cmn_update_txpow(ah, sc->cur_chan->cur_txpower,
+ sc->cur_chan->txpower,
+ &sc->cur_chan->cur_txpower);
clear_bit(ATH_OP_HW_RESET, &common->op_flags);
if (!sc->cur_chan->offchannel && start) {
@@ -270,7 +275,7 @@
return true;
}
-int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
+static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
@@ -281,6 +286,7 @@
__ath_cancel_work(sc);
tasklet_disable(&sc->intr_tq);
+ tasklet_disable(&sc->bcon_tasklet);
spin_lock_bh(&sc->sc_pcu_lock);
if (!sc->cur_chan->offchannel) {
@@ -326,6 +332,7 @@
out:
spin_unlock_bh(&sc->sc_pcu_lock);
+ tasklet_enable(&sc->bcon_tasklet);
tasklet_enable(&sc->intr_tq);
return r;
@@ -505,15 +512,12 @@
if (!ah || test_bit(ATH_OP_INVALID, &common->op_flags))
return IRQ_NONE;
- /* shared irq, not for us */
-
- if (!ath9k_hw_intrpend(ah))
+ if (!AR_SREV_9100(ah) && test_bit(ATH_OP_HW_RESET, &common->op_flags))
return IRQ_NONE;
- if (test_bit(ATH_OP_HW_RESET, &common->op_flags)) {
- ath9k_hw_kill_interrupts(ah);
- return IRQ_HANDLED;
- }
+ /* shared irq, not for us */
+ if (!ath9k_hw_intrpend(ah))
+ return IRQ_NONE;
/*
* Figure out the reason(s) for the interrupt. Note
@@ -525,6 +529,9 @@
ath9k_debug_sync_cause(sc, sync_cause);
status &= ah->imask; /* discard unasked-for bits */
+ if (AR_SREV_9100(ah) && test_bit(ATH_OP_HW_RESET, &common->op_flags))
+ return IRQ_HANDLED;
+
/*
* If there are no status bits set, then this interrupt was not
* for me (should have been caught above).
@@ -539,11 +546,10 @@
sched = true;
/*
- * If a FATAL or RXORN interrupt is received, we have to reset the
- * chip immediately.
+ * If a FATAL interrupt is received, we have to reset the chip
+ * immediately.
*/
- if ((status & ATH9K_INT_FATAL) || ((status & ATH9K_INT_RXORN) &&
- !(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)))
+ if (status & ATH9K_INT_FATAL)
goto chip_reset;
if ((ah->config.hw_hang_checks & HW_BB_WATCHDOG) &&
@@ -598,23 +604,37 @@
#undef SCHED_INTR
}
-int ath_reset(struct ath_softc *sc)
+/*
+ * This function is called when a HW reset cannot be deferred
+ * and has to be immediate.
+ */
+int ath_reset(struct ath_softc *sc, struct ath9k_channel *hchan)
{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
int r;
+ ath9k_hw_kill_interrupts(sc->sc_ah);
+ set_bit(ATH_OP_HW_RESET, &common->op_flags);
+
ath9k_ps_wakeup(sc);
- r = ath_reset_internal(sc, NULL);
+ r = ath_reset_internal(sc, hchan);
ath9k_ps_restore(sc);
return r;
}
+/*
+ * When a HW reset can be deferred, it is added to the
+ * hw_reset_work workqueue, but we set ATH_OP_HW_RESET before
+ * queueing.
+ */
void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
#ifdef CPTCFG_ATH9K_DEBUGFS
RESET_STAT_INC(sc, type);
#endif
+ ath9k_hw_kill_interrupts(sc->sc_ah);
set_bit(ATH_OP_HW_RESET, &common->op_flags);
ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
}
@@ -623,7 +643,9 @@
{
struct ath_softc *sc = container_of(work, struct ath_softc, hw_reset_work);
- ath_reset(sc);
+ ath9k_ps_wakeup(sc);
+ ath_reset_internal(sc, NULL);
+ ath9k_ps_restore(sc);
}
/**********************/
@@ -707,7 +729,8 @@
if (ah->led_pin >= 0) {
ath9k_hw_cfg_output(ah, ah->led_pin,
AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
- ath9k_hw_set_gpio(ah, ah->led_pin, 0);
+ ath9k_hw_set_gpio(ah, ah->led_pin,
+ (ah->config.led_active_high) ? 1 : 0);
}
/*
@@ -849,7 +872,8 @@
spin_lock_bh(&sc->sc_pcu_lock);
if (ah->led_pin >= 0) {
- ath9k_hw_set_gpio(ah, ah->led_pin, 1);
+ ath9k_hw_set_gpio(ah, ah->led_pin,
+ (ah->config.led_active_high) ? 0 : 1);
ath9k_hw_cfg_gpio_input(ah, ah->led_pin);
}
@@ -865,6 +889,9 @@
&sc->cur_chan->chandef);
ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
+
+ set_bit(ATH_OP_INVALID, &common->op_flags);
+
ath9k_hw_phy_disable(ah);
ath9k_hw_configpcipowersave(ah, true);
@@ -873,7 +900,6 @@
ath9k_ps_restore(sc);
- set_bit(ATH_OP_INVALID, &common->op_flags);
sc->ps_idle = prev_idle;
mutex_unlock(&sc->mutex);
@@ -974,9 +1000,8 @@
struct ath_vif *avp;
/*
- * Pick the MAC address of the first interface as the new hardware
- * MAC address. The hardware will use it together with the BSSID mask
- * when matching addresses.
+ * The hardware will use primary station addr together with the
+ * BSSID mask when matching addresses.
*/
memset(iter_data, 0, sizeof(*iter_data));
memset(&iter_data->mask, 0xff, ETH_ALEN);
@@ -1037,7 +1062,7 @@
eth_zero_addr(common->curbssid);
eth_broadcast_addr(common->bssidmask);
- ether_addr_copy(common->macaddr, vif->addr);
+ memcpy(common->macaddr, vif->addr, ETH_ALEN);
common->curaid = 0;
ah->opmode = vif->type;
ah->imask &= ~ATH9K_INT_SWBA;
@@ -1078,7 +1103,7 @@
ath9k_calculate_iter_data(sc, ctx, &iter_data);
if (iter_data.has_hw_macaddr)
- ether_addr_copy(common->macaddr, iter_data.hw_macaddr);
+ memcpy(common->macaddr, iter_data.hw_macaddr, ETH_ALEN);
memcpy(common->bssidmask, iter_data.mask, ETH_ALEN);
ath_hw_setbssidmask(common);
@@ -1168,7 +1193,8 @@
for (i = 0; i < IEEE80211_NUM_ACS; i++)
vif->hw_queue[i] = i;
- if (vif->type == NL80211_IFTYPE_AP)
+ if (vif->type == NL80211_IFTYPE_AP ||
+ vif->type == NL80211_IFTYPE_MESH_POINT)
vif->cab_queue = hw->queues - 2;
else
vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
@@ -1205,6 +1231,8 @@
list_add_tail(&avp->list, &avp->chanctx->vifs);
}
+ ath9k_calculate_summary_state(sc, avp->chanctx);
+
ath9k_assign_hw_queues(hw, vif);
an->sc = sc;
@@ -1274,6 +1302,8 @@
ath_tx_node_cleanup(sc, &avp->mcast_node);
+ ath9k_calculate_summary_state(sc, avp->chanctx);
+
mutex_unlock(&sc->mutex);
}
@@ -1320,78 +1350,6 @@
ath_dbg(common, PS, "PowerSave disabled\n");
}
-void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw)
-{
- struct ath_softc *sc = hw->priv;
- struct ath_hw *ah = sc->sc_ah;
- struct ath_common *common = ath9k_hw_common(ah);
- u32 rxfilter;
-
- if (config_enabled(CPTCFG_ATH9K_TX99))
- return;
-
- if (!ath9k_hw_ops(ah)->spectral_scan_trigger) {
- ath_err(common, "spectrum analyzer not implemented on this hardware\n");
- return;
- }
-
- ath9k_ps_wakeup(sc);
- rxfilter = ath9k_hw_getrxfilter(ah);
- ath9k_hw_setrxfilter(ah, rxfilter |
- ATH9K_RX_FILTER_PHYRADAR |
- ATH9K_RX_FILTER_PHYERR);
-
- /* TODO: usually this should not be neccesary, but for some reason
- * (or in some mode?) the trigger must be called after the
- * configuration, otherwise the register will have its values reset
- * (on my ar9220 to value 0x01002310)
- */
- ath9k_spectral_scan_config(hw, sc->spectral_mode);
- ath9k_hw_ops(ah)->spectral_scan_trigger(ah);
- ath9k_ps_restore(sc);
-}
-
-int ath9k_spectral_scan_config(struct ieee80211_hw *hw,
- enum spectral_mode spectral_mode)
-{
- struct ath_softc *sc = hw->priv;
- struct ath_hw *ah = sc->sc_ah;
- struct ath_common *common = ath9k_hw_common(ah);
-
- if (!ath9k_hw_ops(ah)->spectral_scan_trigger) {
- ath_err(common, "spectrum analyzer not implemented on this hardware\n");
- return -1;
- }
-
- switch (spectral_mode) {
- case SPECTRAL_DISABLED:
- sc->spec_config.enabled = 0;
- break;
- case SPECTRAL_BACKGROUND:
- /* send endless samples.
- * TODO: is this really useful for "background"?
- */
- sc->spec_config.endless = 1;
- sc->spec_config.enabled = 1;
- break;
- case SPECTRAL_CHANSCAN:
- case SPECTRAL_MANUAL:
- sc->spec_config.endless = 0;
- sc->spec_config.enabled = 1;
- break;
- default:
- return -1;
- }
-
- ath9k_ps_wakeup(sc);
- ath9k_hw_ops(ah)->spectral_scan_config(ah, &sc->spec_config);
- ath9k_ps_restore(sc);
-
- sc->spectral_mode = spectral_mode;
-
- return 0;
-}
-
static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
{
struct ath_softc *sc = hw->priv;
@@ -1452,8 +1410,9 @@
if (changed & IEEE80211_CONF_CHANGE_POWER) {
ath_dbg(common, CONFIG, "Set power: %d\n", conf->power_level);
sc->cur_chan->txpower = 2 * conf->power_level;
- ath9k_cmn_update_txpow(ah, sc->curtxpow,
- sc->cur_chan->txpower, &sc->curtxpow);
+ ath9k_cmn_update_txpow(ah, sc->cur_chan->cur_txpower,
+ sc->cur_chan->txpower,
+ &sc->cur_chan->cur_txpower);
}
mutex_unlock(&sc->mutex);
@@ -1550,6 +1509,40 @@
return 0;
}
+static int ath9k_sta_state(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ enum ieee80211_sta_state old_state,
+ enum ieee80211_sta_state new_state)
+{
+ struct ath_softc *sc = hw->priv;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ int ret = 0;
+
+ if (old_state == IEEE80211_STA_AUTH &&
+ new_state == IEEE80211_STA_ASSOC) {
+ ret = ath9k_sta_add(hw, vif, sta);
+ ath_dbg(common, CONFIG,
+ "Add station: %pM\n", sta->addr);
+ } else if (old_state == IEEE80211_STA_ASSOC &&
+ new_state == IEEE80211_STA_AUTH) {
+ ret = ath9k_sta_remove(hw, vif, sta);
+ ath_dbg(common, CONFIG,
+ "Remove station: %pM\n", sta->addr);
+ }
+
+ if (ath9k_is_chanctx_enabled()) {
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ if (old_state == IEEE80211_STA_ASSOC &&
+ new_state == IEEE80211_STA_AUTHORIZED)
+ ath_chanctx_event(sc, vif,
+ ATH_CHANCTX_EVENT_AUTHORIZED);
+ }
+ }
+
+ return ret;
+}
+
static void ath9k_sta_set_tx_filter(struct ath_hw *ah,
struct ath_node *an,
bool set)
@@ -1674,7 +1667,7 @@
key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
- if (sc->sc_ah->sw_mgmt_crypto &&
+ if (sc->sc_ah->sw_mgmt_crypto_tx &&
key->cipher == WLAN_CIPHER_SUITE_CCMP)
key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
ret = 0;
@@ -1734,17 +1727,11 @@
ath_dbg(common, CONFIG, "BSSID %pM Changed ASSOC %d\n",
bss_conf->bssid, bss_conf->assoc);
- ether_addr_copy(avp->bssid, bss_conf->bssid);
+ memcpy(avp->bssid, bss_conf->bssid, ETH_ALEN);
avp->aid = bss_conf->aid;
avp->assoc = bss_conf->assoc;
ath9k_calculate_summary_state(sc, avp->chanctx);
-
- if (ath9k_is_chanctx_enabled()) {
- if (bss_conf->assoc)
- ath_chanctx_event(sc, vif,
- ATH_CHANCTX_EVENT_ASSOC);
- }
}
if (changed & BSS_CHANGED_IBSS) {
@@ -1840,6 +1827,7 @@
u16 tid, u16 *ssn, u8 buf_size)
{
struct ath_softc *sc = hw->priv;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
bool flush = false;
int ret = 0;
@@ -1851,6 +1839,12 @@
case IEEE80211_AMPDU_RX_STOP:
break;
case IEEE80211_AMPDU_TX_START:
+ if (ath9k_is_chanctx_enabled()) {
+ if (test_bit(ATH_OP_SCANNING, &common->op_flags)) {
+ ret = -EBUSY;
+ break;
+ }
+ }
ath9k_ps_wakeup(sc);
ret = ath_tx_aggr_start(sc, sta, tid, ssn);
if (!ret)
@@ -1964,7 +1958,8 @@
mutex_unlock(&sc->mutex);
}
-static bool ath9k_has_tx_pending(struct ath_softc *sc)
+static bool ath9k_has_tx_pending(struct ath_softc *sc,
+ bool sw_pending)
{
int i, npend = 0;
@@ -1972,7 +1967,8 @@
if (!ATH_TXQ_SETUP(sc, i))
continue;
- npend = ath9k_has_pending_frames(sc, &sc->tx.txq[i]);
+ npend = ath9k_has_pending_frames(sc, &sc->tx.txq[i],
+ sw_pending);
if (npend)
break;
}
@@ -1984,18 +1980,38 @@
u32 queues, bool drop)
{
struct ath_softc *sc = hw->priv;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ if (ath9k_is_chanctx_enabled()) {
+ if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags))
+ goto flush;
+
+ /*
+ * If MCC is active, extend the flush timeout
+ * and wait for the HW/SW queues to become
+ * empty. This needs to be done outside the
+ * sc->mutex lock to allow the channel scheduler
+ * to switch channel contexts.
+ *
+ * The vif queues have been stopped in mac80211,
+ * so there won't be any incoming frames.
+ */
+ __ath9k_flush(hw, queues, drop, true, true);
+ return;
+ }
+flush:
mutex_lock(&sc->mutex);
- __ath9k_flush(hw, queues, drop);
+ __ath9k_flush(hw, queues, drop, true, false);
mutex_unlock(&sc->mutex);
}
-void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop,
+ bool sw_pending, bool timeout_override)
{
struct ath_softc *sc = hw->priv;
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
- int timeout = HZ / 5; /* 200 ms */
+ int timeout;
bool drain_txq;
cancel_delayed_work_sync(&sc->tx_complete_work);
@@ -2010,7 +2026,17 @@
return;
}
- if (wait_event_timeout(sc->tx_wait, !ath9k_has_tx_pending(sc),
+ spin_lock_bh(&sc->chan_lock);
+ if (timeout_override)
+ timeout = HZ / 5;
+ else
+ timeout = sc->cur_chan->flush_timeout;
+ spin_unlock_bh(&sc->chan_lock);
+
+ ath_dbg(common, CHAN_CTX,
+ "Flush timeout: %d\n", jiffies_to_msecs(timeout));
+
+ if (wait_event_timeout(sc->tx_wait, !ath9k_has_tx_pending(sc, sw_pending),
timeout) > 0)
drop = false;
@@ -2021,7 +2047,7 @@
spin_unlock_bh(&sc->sc_pcu_lock);
if (!drain_txq)
- ath_reset(sc);
+ ath_reset(sc, NULL);
ath9k_ps_restore(sc);
}
@@ -2033,7 +2059,7 @@
{
struct ath_softc *sc = hw->priv;
- return ath9k_has_tx_pending(sc);
+ return ath9k_has_tx_pending(sc, true);
}
static int ath9k_tx_last_beacon(struct ieee80211_hw *hw)
@@ -2164,14 +2190,17 @@
return 0;
}
-static void ath9k_sw_scan_start(struct ieee80211_hw *hw)
+static void ath9k_sw_scan_start(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ const u8 *mac_addr)
{
struct ath_softc *sc = hw->priv;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
set_bit(ATH_OP_SCANNING, &common->op_flags);
}
-static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
+static void ath9k_sw_scan_complete(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
{
struct ath_softc *sc = hw->priv;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
@@ -2180,6 +2209,28 @@
#ifdef CPTCFG_ATH9K_CHANNEL_CONTEXT
+static void ath9k_cancel_pending_offchannel(struct ath_softc *sc)
+{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+
+ if (sc->offchannel.roc_vif) {
+ ath_dbg(common, CHAN_CTX,
+ "%s: Aborting RoC\n", __func__);
+
+ del_timer_sync(&sc->offchannel.timer);
+ if (sc->offchannel.state >= ATH_OFFCHANNEL_ROC_START)
+ ath_roc_complete(sc, true);
+ }
+
+ if (test_bit(ATH_OP_SCANNING, &common->op_flags)) {
+ ath_dbg(common, CHAN_CTX,
+ "%s: Aborting HW scan\n", __func__);
+
+ del_timer_sync(&sc->offchannel.timer);
+ ath_scan_complete(sc, true);
+ }
+}
+
static int ath9k_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_scan_request *hw_req)
{
@@ -2310,7 +2361,6 @@
conf->def.chan->center_freq);
ath_chanctx_set_channel(sc, ctx, &conf->def);
- ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_ASSIGN);
mutex_unlock(&sc->mutex);
return 0;
@@ -2367,6 +2417,8 @@
struct ath_chanctx *ctx = ath_chanctx_get(conf);
int i;
+ ath9k_cancel_pending_offchannel(sc);
+
mutex_lock(&sc->mutex);
ath_dbg(common, CHAN_CTX,
@@ -2396,6 +2448,8 @@
struct ath_chanctx *ctx = ath_chanctx_get(conf);
int ac;
+ ath9k_cancel_pending_offchannel(sc);
+
mutex_lock(&sc->mutex);
ath_dbg(common, CHAN_CTX,
@@ -2419,7 +2473,11 @@
struct ath_softc *sc = hw->priv;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_vif *avp = (struct ath_vif *) vif->drv_priv;
+ struct ath_beacon_config *cur_conf;
+ struct ath_chanctx *go_ctx;
+ unsigned long timeout;
bool changed = false;
+ u32 beacon_int;
if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags))
return;
@@ -2430,19 +2488,57 @@
mutex_lock(&sc->mutex);
spin_lock_bh(&sc->chan_lock);
- if (sc->next_chan || (sc->cur_chan != avp->chanctx)) {
- sc->next_chan = avp->chanctx;
+ if (sc->next_chan || (sc->cur_chan != avp->chanctx))
changed = true;
+ spin_unlock_bh(&sc->chan_lock);
+
+ if (!changed)
+ goto out;
+
+ ath9k_cancel_pending_offchannel(sc);
+
+ go_ctx = ath_is_go_chanctx_present(sc);
+
+ if (go_ctx) {
+ /*
+ * Wait till the GO interface gets a chance
+ * to send out an NoA.
+ */
+ spin_lock_bh(&sc->chan_lock);
+ sc->sched.mgd_prepare_tx = true;
+ cur_conf = &go_ctx->beacon;
+ beacon_int = TU_TO_USEC(cur_conf->beacon_interval);
+ spin_unlock_bh(&sc->chan_lock);
+
+ timeout = usecs_to_jiffies(beacon_int * 2);
+ init_completion(&sc->go_beacon);
+
+ mutex_unlock(&sc->mutex);
+
+ if (wait_for_completion_timeout(&sc->go_beacon,
+ timeout) == 0) {
+ ath_dbg(common, CHAN_CTX,
+ "Failed to send new NoA\n");
+
+ spin_lock_bh(&sc->chan_lock);
+ sc->sched.mgd_prepare_tx = false;
+ spin_unlock_bh(&sc->chan_lock);
+ }
+
+ mutex_lock(&sc->mutex);
}
+
ath_dbg(common, CHAN_CTX,
- "%s: Set chanctx state to FORCE_ACTIVE, changed: %d\n",
- __func__, changed);
+ "%s: Set chanctx state to FORCE_ACTIVE for vif: %pM\n",
+ __func__, vif->addr);
+
+ spin_lock_bh(&sc->chan_lock);
+ sc->next_chan = avp->chanctx;
sc->sched.state = ATH_CHANCTX_STATE_FORCE_ACTIVE;
spin_unlock_bh(&sc->chan_lock);
- if (changed)
- ath_chanctx_set_next(sc, true);
-
+ ath_chanctx_set_next(sc, true);
+out:
mutex_unlock(&sc->mutex);
}
@@ -2465,6 +2561,24 @@
#endif
+static int ath9k_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ int *dbm)
+{
+ struct ath_softc *sc = hw->priv;
+ struct ath_vif *avp = (void *)vif->drv_priv;
+
+ mutex_lock(&sc->mutex);
+ if (avp->chanctx)
+ *dbm = avp->chanctx->cur_txpower;
+ else
+ *dbm = sc->cur_chan->cur_txpower;
+ mutex_unlock(&sc->mutex);
+
+ *dbm /= 2;
+
+ return 0;
+}
+
struct ieee80211_ops ath9k_ops = {
.tx = ath9k_tx,
.start = ath9k_start,
@@ -2474,8 +2588,7 @@
.remove_interface = ath9k_remove_interface,
.config = ath9k_config,
.configure_filter = ath9k_configure_filter,
- .sta_add = ath9k_sta_add,
- .sta_remove = ath9k_sta_remove,
+ .sta_state = ath9k_sta_state,
.sta_notify = ath9k_sta_notify,
.conf_tx = ath9k_conf_tx,
.bss_info_changed = ath9k_bss_info_changed,
@@ -2512,4 +2625,5 @@
#endif
.sw_scan_start = ath9k_sw_scan_start,
.sw_scan_complete = ath9k_sw_scan_complete,
+ .get_txpower = ath9k_get_txpower,
};
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index c018dea..6bc24fb 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -30,6 +30,7 @@
{ PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI */
{ PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */
+#ifdef CPTCFG_ATH9K_PCOEM
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
0x002A,
PCI_VENDOR_ID_AZWAVE,
@@ -82,6 +83,7 @@
PCI_VENDOR_ID_AZWAVE,
0x2C37),
.driver_data = ATH9K_PCI_BT_ANT_DIV },
+#endif
{ PCI_VDEVICE(ATHEROS, 0x002B) }, /* PCI-E */
{ PCI_VDEVICE(ATHEROS, 0x002C) }, /* PCI-E 802.11n bonded out */
@@ -102,6 +104,7 @@
{ PCI_VDEVICE(ATHEROS, 0x0030) }, /* PCI-E AR9300 */
+#ifdef CPTCFG_ATH9K_PCOEM
/* PCI-E CUS198 */
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
0x0032,
@@ -294,10 +297,12 @@
PCI_VENDOR_ID_ASUSTEK,
0x850D),
.driver_data = ATH9K_PCI_NO_PLL_PWRSAVE },
+#endif
{ PCI_VDEVICE(ATHEROS, 0x0032) }, /* PCI-E AR9485 */
{ PCI_VDEVICE(ATHEROS, 0x0033) }, /* PCI-E AR9580 */
+#ifdef CPTCFG_ATH9K_PCOEM
/* PCI-E CUS217 */
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
0x0034,
@@ -652,11 +657,14 @@
0x0036,
PCI_VENDOR_ID_DELL,
0x020E),
- .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+ .driver_data = ATH9K_PCI_AR9565_2ANT |
+ ATH9K_PCI_BT_ANT_DIV |
+ ATH9K_PCI_LED_ACT_HI},
/* PCI-E AR9565 (WB335) */
{ PCI_VDEVICE(ATHEROS, 0x0036),
.driver_data = ATH9K_PCI_BT_ANT_DIV },
+#endif
{ 0 }
};
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index b231f21..04da61b 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -870,7 +870,7 @@
*/
if (rx_stats->rs_status & ATH9K_RXERR_PHY) {
ath9k_dfs_process_phyerr(sc, hdr, rx_stats, rx_status->mactime);
- if (ath_process_fft(sc, hdr, rx_stats, rx_status->mactime))
+ if (ath_cmn_process_fft(&sc->spec_priv, hdr, rx_stats, rx_status->mactime))
RX_STAT_INC(rx_spectral);
return -EINVAL;
diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h
index 2a938f4..7743b38 100644
--- a/drivers/net/wireless/ath/ath9k/reg.h
+++ b/drivers/net/wireless/ath/ath9k/reg.h
@@ -892,10 +892,21 @@
(AR_SREV_9330((_ah)) && \
((_ah)->hw_version.macRev == AR_SREV_REVISION_9330_12))
+#ifdef CPTCFG_ATH9K_PCOEM
+#define AR_SREV_9462(_ah) \
+ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462))
#define AR_SREV_9485(_ah) \
(((_ah)->hw_version.macVersion == AR_SREV_VERSION_9485))
+#define AR_SREV_9565(_ah) \
+ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9565))
+#else
+#define AR_SREV_9462(_ah) 0
+#define AR_SREV_9485(_ah) 0
+#define AR_SREV_9565(_ah) 0
+#endif
+
#define AR_SREV_9485_11_OR_LATER(_ah) \
- (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9485) && \
+ (AR_SREV_9485(_ah) && \
((_ah)->hw_version.macRev >= AR_SREV_REVISION_9485_11))
#define AR_SREV_9485_OR_LATER(_ah) \
(((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9485))
@@ -915,34 +926,30 @@
(AR_SREV_9285_12_OR_LATER(_ah) && \
((REG_READ(_ah, AR_AN_SYNTH9) & 0x7) == 0x1))
-#define AR_SREV_9462(_ah) \
- (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462))
#define AR_SREV_9462_20(_ah) \
- (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462) && \
+ (AR_SREV_9462(_ah) && \
((_ah)->hw_version.macRev == AR_SREV_REVISION_9462_20))
#define AR_SREV_9462_21(_ah) \
- (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462) && \
+ (AR_SREV_9462(_ah) && \
((_ah)->hw_version.macRev == AR_SREV_REVISION_9462_21))
#define AR_SREV_9462_20_OR_LATER(_ah) \
- (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462) && \
+ (AR_SREV_9462(_ah) && \
((_ah)->hw_version.macRev >= AR_SREV_REVISION_9462_20))
#define AR_SREV_9462_21_OR_LATER(_ah) \
- (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9462) && \
+ (AR_SREV_9462(_ah) && \
((_ah)->hw_version.macRev >= AR_SREV_REVISION_9462_21))
-#define AR_SREV_9565(_ah) \
- (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9565))
#define AR_SREV_9565_10(_ah) \
- (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9565) && \
+ (AR_SREV_9565(_ah) && \
((_ah)->hw_version.macRev == AR_SREV_REVISION_9565_10))
#define AR_SREV_9565_101(_ah) \
- (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9565) && \
+ (AR_SREV_9565(_ah) && \
((_ah)->hw_version.macRev == AR_SREV_REVISION_9565_101))
#define AR_SREV_9565_11(_ah) \
- (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9565) && \
+ (AR_SREV_9565(_ah) && \
((_ah)->hw_version.macRev == AR_SREV_REVISION_9565_11))
#define AR_SREV_9565_11_OR_LATER(_ah) \
- (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9565) && \
+ (AR_SREV_9565(_ah) && \
((_ah)->hw_version.macRev >= AR_SREV_REVISION_9565_11))
#define AR_SREV_9550(_ah) \
@@ -1598,6 +1605,7 @@
#define AR_RESET_TSF 0x8020
#define AR_RESET_TSF_ONCE 0x01000000
+#define AR_RESET_TSF2_ONCE 0x02000000
#define AR_MAX_CFP_DUR 0x8038
#define AR_CFP_VAL 0x0000FFFF
@@ -1716,6 +1724,8 @@
#define AR_TPC_CTS_S 8
#define AR_TPC_CHIRP 0x003f0000
#define AR_TPC_CHIRP_S 16
+#define AR_TPC_RPT 0x3f000000
+#define AR_TPC_RPT_S 24
#define AR_QUIET1 0x80fc
#define AR_QUIET1_NEXT_QUIET_S 0
@@ -1959,6 +1969,8 @@
#define AR_MAC_PCU_ASYNC_FIFO_REG3_SOFT_RESET 0x80000000
#define AR_MAC_PCU_GEN_TIMER_TSF_SEL 0x83d8
+#define AR_DIRECT_CONNECT 0x83a0
+#define AR_DC_AP_STA_EN 0x00000001
#define AR_AES_MUTE_MASK0 0x805c
#define AR_AES_MUTE_MASK0_FC 0x0000FFFF
diff --git a/drivers/net/wireless/ath/ath9k/tx99.c b/drivers/net/wireless/ath/ath9k/tx99.c
index 40ab65e..ac4781f 100644
--- a/drivers/net/wireless/ath/ath9k/tx99.c
+++ b/drivers/net/wireless/ath/ath9k/tx99.c
@@ -99,7 +99,7 @@
static void ath9k_tx99_deinit(struct ath_softc *sc)
{
- ath_reset(sc);
+ ath_reset(sc, NULL);
ath9k_ps_wakeup(sc);
ath9k_tx99_stop(sc);
@@ -127,7 +127,7 @@
memset(&txctl, 0, sizeof(txctl));
txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO];
- ath_reset(sc);
+ ath_reset(sc, NULL);
ath9k_ps_wakeup(sc);
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index f00fbc9..6bf7bc7 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -1096,6 +1096,37 @@
}
}
+static u8 ath_get_rate_txpower(struct ath_softc *sc, struct ath_buf *bf,
+ u8 rateidx)
+{
+ u8 max_power;
+ struct ath_hw *ah = sc->sc_ah;
+
+ if (sc->tx99_state)
+ return MAX_RATE_POWER;
+
+ if (!AR_SREV_9300_20_OR_LATER(ah)) {
+ /* ar9002 is not sipported for the moment */
+ return MAX_RATE_POWER;
+ }
+
+ if (!bf->bf_state.bfs_paprd) {
+ struct sk_buff *skb = bf->bf_mpdu;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ath_frame_info *fi = get_frame_info(skb);
+
+ if (rateidx < 8 && (info->flags & IEEE80211_TX_CTL_STBC))
+ max_power = min(ah->tx_power_stbc[rateidx],
+ fi->tx_power);
+ else
+ max_power = min(ah->tx_power[rateidx], fi->tx_power);
+ } else {
+ max_power = ah->paprd_training_power;
+ }
+
+ return max_power;
+}
+
static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf,
struct ath_tx_info *info, int len, bool rts)
{
@@ -1166,6 +1197,8 @@
is_40, is_sgi, is_sp);
if (rix < 8 && (tx_info->flags & IEEE80211_TX_CTL_STBC))
info->rates[i].RateFlags |= ATH9K_RATESERIES_STBC;
+
+ info->txpower[i] = ath_get_rate_txpower(sc, bf, rix);
continue;
}
@@ -1193,6 +1226,8 @@
info->rates[i].PktDuration = ath9k_hw_computetxtime(sc->sc_ah,
phy, rate->bitrate * 100, len, rix, is_sp);
+
+ info->txpower[i] = ath_get_rate_txpower(sc, bf, rix);
}
/* For AR5416 - RTS cannot be followed by a frame larger than 8K */
@@ -1239,7 +1274,6 @@
memset(&info, 0, sizeof(info));
info.is_first = true;
info.is_last = true;
- info.txpower = MAX_RATE_POWER;
info.qcu = txq->axq_qnum;
while (bf) {
@@ -2063,6 +2097,7 @@
fi->keyix = ATH9K_TXKEYIX_INVALID;
fi->keytype = keytype;
fi->framelen = framelen;
+ fi->tx_power = MAX_RATE_POWER;
if (!rate)
return;
diff --git a/drivers/net/wireless/ath/carl9170/Kconfig b/drivers/net/wireless/ath/carl9170/Kconfig
index a75b5c6..2a510ba 100644
--- a/drivers/net/wireless/ath/carl9170/Kconfig
+++ b/drivers/net/wireless/ath/carl9170/Kconfig
@@ -20,8 +20,8 @@
bool "SoftLED Support"
depends on CARL9170
select MAC80211_LEDS
- select BACKPORT_LEDS_CLASS
- select BACKPORT_NEW_LEDS
+ select BPAUTO_LEDS_CLASS
+ select BPAUTO_NEW_LEDS
default y
help
This option is necessary, if you want your device' LEDs to blink
diff --git a/drivers/net/wireless/ath/carl9170/phy.c b/drivers/net/wireless/ath/carl9170/phy.c
index b80b213..dca6df1 100644
--- a/drivers/net/wireless/ath/carl9170/phy.c
+++ b/drivers/net/wireless/ath/carl9170/phy.c
@@ -994,7 +994,7 @@
refsel0 = 0;
refsel1 = 1;
}
- chansel = byte_rev_table[chansel];
+ chansel = bitrev8(chansel);
} else {
if (freq == 2484) {
chansel = 10 + (freq - 2274) / 5;
@@ -1002,7 +1002,7 @@
} else
chansel = 16 + (freq - 2272) / 5;
chansel *= 4;
- chansel = byte_rev_table[chansel];
+ chansel = bitrev8(chansel);
}
d1 = chansel;
diff --git a/drivers/net/wireless/ath/dfs_pattern_detector.c b/drivers/net/wireless/ath/dfs_pattern_detector.c
index 7a16686..0e383ef 100644
--- a/drivers/net/wireless/ath/dfs_pattern_detector.c
+++ b/drivers/net/wireless/ath/dfs_pattern_detector.c
@@ -86,7 +86,7 @@
FCC_PATTERN(1, 0, 5, 150, 230, 1, 23),
FCC_PATTERN(2, 6, 10, 200, 500, 1, 16),
FCC_PATTERN(3, 11, 20, 200, 500, 1, 12),
- FCC_PATTERN(4, 50, 100, 1000, 2000, 20, 1),
+ FCC_PATTERN(4, 50, 100, 1000, 2000, 1, 20),
FCC_PATTERN(5, 0, 1, 333, 333, 1, 9),
};
@@ -105,7 +105,7 @@
JP_PATTERN(4, 0, 5, 150, 230, 1, 23),
JP_PATTERN(5, 6, 10, 200, 500, 1, 16),
JP_PATTERN(6, 11, 20, 200, 500, 1, 12),
- JP_PATTERN(7, 50, 100, 1000, 2000, 20, 1),
+ JP_PATTERN(7, 50, 100, 1000, 2000, 1, 20),
JP_PATTERN(5, 0, 1, 333, 333, 1, 9),
};
diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c
index 6253db8..0cd964f 100644
--- a/drivers/net/wireless/ath/regd.c
+++ b/drivers/net/wireless/ath/regd.c
@@ -515,6 +515,7 @@
if (!request)
return;
+ reg->region = request->dfs_region;
switch (request->initiator) {
case NL80211_REGDOM_SET_BY_CORE:
/*
@@ -779,6 +780,19 @@
return SD_NO_CTL;
}
+ if (ath_regd_get_eepromRD(reg) == CTRY_DEFAULT) {
+ switch (reg->region) {
+ case NL80211_DFS_FCC:
+ return CTL_FCC;
+ case NL80211_DFS_ETSI:
+ return CTL_ETSI;
+ case NL80211_DFS_JP:
+ return CTL_MKK;
+ default:
+ break;
+ }
+ }
+
switch (band) {
case IEEE80211_BAND_2GHZ:
return reg->regpair->reg_2ghz_ctl;
diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c
index b71d2b3..7dd8873 100644
--- a/drivers/net/wireless/ath/wcn36xx/main.c
+++ b/drivers/net/wireless/ath/wcn36xx/main.c
@@ -494,7 +494,9 @@
return ret;
}
-static void wcn36xx_sw_scan_start(struct ieee80211_hw *hw)
+static void wcn36xx_sw_scan_start(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ const u8 *mac_addr)
{
struct wcn36xx *wcn = hw->priv;
@@ -502,7 +504,8 @@
wcn36xx_smd_start_scan(wcn);
}
-static void wcn36xx_sw_scan_complete(struct ieee80211_hw *hw)
+static void wcn36xx_sw_scan_complete(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
{
struct wcn36xx *wcn = hw->priv;
@@ -1075,7 +1078,6 @@
.remove = wcn36xx_remove,
.driver = {
.name = "wcn36xx",
- .owner = THIS_MODULE,
},
.id_table = wcn36xx_platform_id_table,
};
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index d9f4b30..38332a6 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -792,12 +792,13 @@
}
static int wil_cfg80211_del_station(struct wiphy *wiphy,
- struct net_device *dev, const u8 *mac)
+ struct net_device *dev,
+ struct station_del_parameters *params)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
mutex_lock(&wil->mutex);
- wil6210_disconnect(wil, mac);
+ wil6210_disconnect(wil, params->mac, params->reason_code, false);
mutex_unlock(&wil->mutex);
return 0;
diff --git a/drivers/net/wireless/ath/wil6210/debug.c b/drivers/net/wireless/ath/wil6210/debug.c
index 8d99021..3249562 100644
--- a/drivers/net/wireless/ath/wil6210/debug.c
+++ b/drivers/net/wireless/ath/wil6210/debug.c
@@ -32,6 +32,23 @@
va_end(args);
}
+void wil_err_ratelimited(struct wil6210_priv *wil, const char *fmt, ...)
+{
+ if (net_ratelimit()) {
+ struct net_device *ndev = wil_to_ndev(wil);
+ struct va_format vaf = {
+ .fmt = fmt,
+ };
+ va_list args;
+
+ va_start(args, fmt);
+ vaf.va = &args;
+ netdev_err(ndev, "%pV", &vaf);
+ trace_wil6210_log_err(&vaf);
+ va_end(args);
+ }
+}
+
void wil_info(struct wil6210_priv *wil, const char *fmt, ...)
{
struct net_device *ndev = wil_to_ndev(wil);
diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c
index 54a6ddc..4e6e145 100644
--- a/drivers/net/wireless/ath/wil6210/debugfs.c
+++ b/drivers/net/wireless/ath/wil6210/debugfs.c
@@ -573,8 +573,10 @@
if (!frame)
return -ENOMEM;
- if (copy_from_user(frame, buf, len))
+ if (copy_from_user(frame, buf, len)) {
+ kfree(frame);
return -EIO;
+ }
params.buf = frame;
params.len = len;
@@ -614,8 +616,10 @@
return -ENOMEM;
rc = simple_write_to_buffer(wmi, len, ppos, buf, len);
- if (rc < 0)
+ if (rc < 0) {
+ kfree(wmi);
return rc;
+ }
cmd = &wmi[1];
cmdid = le16_to_cpu(wmi->id);
diff --git a/drivers/net/wireless/ath/wil6210/fw.c b/drivers/net/wireless/ath/wil6210/fw.c
index 8c6f3b0..93c5cc1 100644
--- a/drivers/net/wireless/ath/wil6210/fw.c
+++ b/drivers/net/wireless/ath/wil6210/fw.c
@@ -15,7 +15,6 @@
*/
#include <linux/firmware.h>
#include <linux/module.h>
-#include <linux/pci.h>
#include <linux/crc32.h>
#include "wil6210.h"
#include "fw.h"
diff --git a/drivers/net/wireless/ath/wil6210/fw_inc.c b/drivers/net/wireless/ath/wil6210/fw_inc.c
index 44cb71f..d4acf93 100644
--- a/drivers/net/wireless/ath/wil6210/fw_inc.c
+++ b/drivers/net/wireless/ath/wil6210/fw_inc.c
@@ -446,7 +446,7 @@
if (size >= sizeof(*hdr)) {
wil_err_fw(wil, "Stop at offset %ld"
" record type %d [%zd bytes]\n",
- (const void *)hdr - data,
+ (long)((const void *)hdr - data),
le16_to_cpu(hdr->type), hdr_sz);
}
return -EINVAL;
@@ -471,7 +471,7 @@
size_t sz;
const void *d;
- rc = request_firmware(&fw, name, wil_to_pcie_dev(wil));
+ rc = request_firmware(&fw, name, wil_to_dev(wil));
if (rc) {
wil_err_fw(wil, "Failed to load firmware %s\n", name);
return rc;
diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c
index b7a0f6f..0b6ac97 100644
--- a/drivers/net/wireless/ath/wil6210/interrupt.c
+++ b/drivers/net/wireless/ath/wil6210/interrupt.c
@@ -36,7 +36,8 @@
*/
#define WIL6210_IRQ_DISABLE (0xFFFFFFFFUL)
-#define WIL6210_IMC_RX BIT_DMA_EP_RX_ICR_RX_DONE
+#define WIL6210_IMC_RX (BIT_DMA_EP_RX_ICR_RX_DONE | \
+ BIT_DMA_EP_RX_ICR_RX_HTRSH)
#define WIL6210_IMC_TX (BIT_DMA_EP_TX_ICR_TX_DONE | \
BIT_DMA_EP_TX_ICR_TX_DONE_N(0))
#define WIL6210_IMC_MISC (ISR_MISC_FW_READY | \
@@ -171,6 +172,7 @@
u32 isr = wil_ioread32_and_clear(wil->csr +
HOSTADDR(RGF_DMA_EP_RX_ICR) +
offsetof(struct RGF_ICR, ICR));
+ bool need_unmask = true;
trace_wil6210_irq_rx(isr);
wil_dbg_irq(wil, "ISR RX 0x%08x\n", isr);
@@ -182,12 +184,24 @@
wil6210_mask_irq_rx(wil);
- if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) {
+ /* RX_DONE and RX_HTRSH interrupts are the same if interrupt
+ * moderation is not used. Interrupt moderation may cause RX
+ * buffer overflow while RX_DONE is delayed. The required
+ * action is always the same - should empty the accumulated
+ * packets from the RX ring.
+ */
+ if (isr & (BIT_DMA_EP_RX_ICR_RX_DONE | BIT_DMA_EP_RX_ICR_RX_HTRSH)) {
wil_dbg_irq(wil, "RX done\n");
- isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE;
+
+ if (isr & BIT_DMA_EP_RX_ICR_RX_HTRSH)
+ wil_err_ratelimited(wil, "Received \"Rx buffer is in risk "
+ "of overflow\" interrupt\n");
+
+ isr &= ~(BIT_DMA_EP_RX_ICR_RX_DONE | BIT_DMA_EP_RX_ICR_RX_HTRSH);
if (test_bit(wil_status_reset_done, &wil->status)) {
if (test_bit(wil_status_napi_en, &wil->status)) {
wil_dbg_txrx(wil, "NAPI(Rx) schedule\n");
+ need_unmask = false;
napi_schedule(&wil->napi_rx);
} else {
wil_err(wil, "Got Rx interrupt while "
@@ -204,6 +218,10 @@
/* Rx IRQ will be enabled when NAPI processing finished */
atomic_inc(&wil->isr_count_rx);
+
+ if (unlikely(need_unmask))
+ wil6210_unmask_irq_rx(wil);
+
return IRQ_HANDLED;
}
@@ -213,6 +231,7 @@
u32 isr = wil_ioread32_and_clear(wil->csr +
HOSTADDR(RGF_DMA_EP_TX_ICR) +
offsetof(struct RGF_ICR, ICR));
+ bool need_unmask = true;
trace_wil6210_irq_tx(isr);
wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr);
@@ -231,6 +250,7 @@
isr &= ~(BIT(25) - 1UL);
if (test_bit(wil_status_reset_done, &wil->status)) {
wil_dbg_txrx(wil, "NAPI(Tx) schedule\n");
+ need_unmask = false;
napi_schedule(&wil->napi_tx);
} else {
wil_err(wil, "Got Tx interrupt while in reset\n");
@@ -243,6 +263,10 @@
/* Tx IRQ will be enabled when NAPI processing finished */
atomic_inc(&wil->isr_count_tx);
+
+ if (unlikely(need_unmask))
+ wil6210_unmask_irq_tx(wil);
+
return IRQ_HANDLED;
}
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 6500caf..8ff3fe3 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -38,6 +38,65 @@
module_param(itr_trsh, uint, S_IRUGO);
MODULE_PARM_DESC(itr_trsh, " Interrupt moderation threshold, usecs.");
+/* We allow allocation of more than 1 page buffers to support large packets.
+ * It is suboptimal behavior performance wise in case MTU above page size.
+ */
+unsigned int mtu_max = TXRX_BUF_LEN_DEFAULT - ETH_HLEN;
+static int mtu_max_set(const char *val, const struct kernel_param *kp)
+{
+ int ret;
+
+ /* sets mtu_max directly. no need to restore it in case of
+ * illegal value since we assume this will fail insmod
+ */
+ ret = param_set_uint(val, kp);
+ if (ret)
+ return ret;
+
+ if (mtu_max < 68 || mtu_max > IEEE80211_MAX_DATA_LEN_DMG)
+ ret = -EINVAL;
+
+ return ret;
+}
+
+static struct kernel_param_ops mtu_max_ops = {
+ .set = mtu_max_set,
+ .get = param_get_uint,
+};
+
+module_param_cb(mtu_max, &mtu_max_ops, &mtu_max, S_IRUGO);
+MODULE_PARM_DESC(mtu_max, " Max MTU value.");
+
+static uint rx_ring_order = WIL_RX_RING_SIZE_ORDER_DEFAULT;
+static uint tx_ring_order = WIL_TX_RING_SIZE_ORDER_DEFAULT;
+
+static int ring_order_set(const char *val, const struct kernel_param *kp)
+{
+ int ret;
+ uint x;
+
+ ret = kstrtouint(val, 0, &x);
+ if (ret)
+ return ret;
+
+ if ((x < WIL_RING_SIZE_ORDER_MIN) || (x > WIL_RING_SIZE_ORDER_MAX))
+ return -EINVAL;
+
+ *((uint *)kp->arg) = x;
+
+ return 0;
+}
+
+static struct kernel_param_ops ring_order_ops = {
+ .set = ring_order_set,
+ .get = param_get_uint,
+};
+
+module_param_cb(rx_ring_order, &ring_order_ops, &rx_ring_order, S_IRUGO);
+MODULE_PARM_DESC(rx_ring_order, " Rx ring order; size = 1 << order");
+module_param_cb(tx_ring_order, &ring_order_ops, &tx_ring_order, S_IRUGO);
+MODULE_PARM_DESC(tx_ring_order, " Tx ring order; size = 1 << order");
+
#define RST_DELAY (20) /* msec, for loop in @wil_target_reset */
#define RST_COUNT (1 + 1000/RST_DELAY) /* round up to be above 1 sec total */
@@ -74,7 +133,8 @@
__raw_writel(*s++, d++);
}
-static void wil_disconnect_cid(struct wil6210_priv *wil, int cid)
+static void wil_disconnect_cid(struct wil6210_priv *wil, int cid,
+ u16 reason_code, bool from_event)
{
uint i;
struct net_device *ndev = wil_to_ndev(wil);
@@ -86,7 +146,9 @@
sta->data_port_open = false;
if (sta->status != wil_sta_unused) {
- wmi_disconnect_sta(wil, sta->addr, WLAN_REASON_DEAUTH_LEAVING);
+ if (!from_event)
+ wmi_disconnect_sta(wil, sta->addr, reason_code);
+
switch (wdev->iftype) {
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO:
@@ -118,7 +180,8 @@
memset(&sta->stats, 0, sizeof(sta->stats));
}
-static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid)
+static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
+ u16 reason_code, bool from_event)
{
int cid = -ENOENT;
struct net_device *ndev = wil_to_ndev(wil);
@@ -133,10 +196,10 @@
}
if (cid >= 0) /* disconnect 1 peer */
- wil_disconnect_cid(wil, cid);
+ wil_disconnect_cid(wil, cid, reason_code, from_event);
else /* disconnect all */
for (cid = 0; cid < WIL6210_MAX_CID; cid++)
- wil_disconnect_cid(wil, cid);
+ wil_disconnect_cid(wil, cid, reason_code, from_event);
/* link state */
switch (wdev->iftype) {
@@ -145,8 +208,7 @@
wil_link_off(wil);
if (test_bit(wil_status_fwconnected, &wil->status)) {
clear_bit(wil_status_fwconnected, &wil->status);
- cfg80211_disconnected(ndev,
- WLAN_STATUS_UNSPECIFIED_FAILURE,
+ cfg80211_disconnected(ndev, reason_code,
NULL, 0, GFP_KERNEL);
} else if (test_bit(wil_status_fwconnecting, &wil->status)) {
cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0,
@@ -166,7 +228,7 @@
struct wil6210_priv, disconnect_worker);
mutex_lock(&wil->mutex);
- _wil6210_disconnect(wil, NULL);
+ _wil6210_disconnect(wil, NULL, WLAN_REASON_UNSPECIFIED, false);
mutex_unlock(&wil->mutex);
}
@@ -188,6 +250,7 @@
clear_bit(wil_status_fwready, &wil->status);
wil_err(wil, "Scan timeout detected, start fw error recovery\n");
+ wil->recovery_state = fw_recovery_pending;
schedule_work(&wil->fw_error_worker);
}
@@ -223,6 +286,11 @@
wil_dbg_misc(wil, "fw error worker\n");
+ if (!netif_running(wil_to_ndev(wil))) {
+ wil_info(wil, "No recovery - interface is down\n");
+ return;
+ }
+
/* increment @recovery_count if less then WIL6210_FW_RECOVERY_TO
* passed since last recovery attempt
*/
@@ -257,9 +325,12 @@
break;
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO:
+ wil_info(wil, "No recovery for AP-like interface\n");
/* recovery in these modes is done by upper layers */
break;
default:
+ wil_err(wil, "No recovery - unknown interface type %d\n",
+ wdev->iftype);
break;
}
mutex_unlock(&wil->mutex);
@@ -291,7 +362,7 @@
wil_dbg_wmi(wil, "Configure for connection CID %d\n", cid);
- rc = wil_vring_init_tx(wil, ringid, WIL6210_TX_RING_SIZE, cid, 0);
+ rc = wil_vring_init_tx(wil, ringid, 1 << tx_ring_order, cid, 0);
wil->pending_connect_cid = -1;
if (rc == 0) {
wil->sta[cid].status = wil_sta_connected;
@@ -346,12 +417,23 @@
return 0;
}
-void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid)
+/**
+ * wil6210_disconnect - disconnect one connection
+ * @wil: driver context
+ * @bssid: peer to disconnect, NULL to disconnect all
+ * @reason_code: Reason code for the Disassociation frame
+ * @from_event: whether is invoked from FW event handler
+ *
+ * Disconnect and release associated resources. If invoked not from the
+ * FW event handler, issue WMI command(s) to trigger MAC disconnect.
+ */
+void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
+ u16 reason_code, bool from_event)
{
wil_dbg_misc(wil, "%s()\n", __func__);
del_timer_sync(&wil->connect_timer);
- _wil6210_disconnect(wil, bssid);
+ _wil6210_disconnect(wil, bssid, reason_code, from_event);
}
void wil_priv_deinit(struct wil6210_priv *wil)
@@ -363,7 +445,7 @@
cancel_work_sync(&wil->disconnect_worker);
cancel_work_sync(&wil->fw_error_worker);
mutex_lock(&wil->mutex);
- wil6210_disconnect(wil, NULL);
+ wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
mutex_unlock(&wil->mutex);
wmi_event_flush(wil);
destroy_workqueue(wil->wmi_wq_conn);
@@ -395,7 +477,7 @@
static int wil_target_reset(struct wil6210_priv *wil)
{
int delay = 0;
- u32 hw_state;
+ u32 x;
u32 rev_id;
bool is_sparrow = (wil->board->board == WIL_BOARD_SPARROW);
@@ -410,9 +492,25 @@
S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT_CAR_PERST_RST);
wil_halt_cpu(wil);
- C(RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_CAR_AHB_SW_SEL); /* 40 MHz */
+
+ /* Clear Fw Download notification */
+ C(RGF_USER_USAGE_6, BIT(0));
if (is_sparrow) {
+ S(RGF_CAF_OSC_CONTROL, BIT_CAF_OSC_XTAL_EN);
+ /* XTAL stabilization should take about 3ms */
+ usleep_range(5000, 7000);
+ x = R(RGF_CAF_PLL_LOCK_STATUS);
+ if (!(x & BIT_CAF_OSC_DIG_XTAL_STABLE)) {
+ wil_err(wil, "Xtal stabilization timeout\n"
+ "RGF_CAF_PLL_LOCK_STATUS = 0x%08x\n", x);
+ return -ETIME;
+ }
+ /* switch 10k to XTAL*/
+ C(RGF_USER_SPARROW_M_4, BIT_SPARROW_M_4_SEL_SLEEP_OR_REF);
+ /* 40 MHz */
+ C(RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_CAR_AHB_SW_SEL);
+
W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x3ff81f);
W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_1, 0xf);
}
@@ -453,13 +551,13 @@
/* wait until device ready. typical time is 200..250 msec */
do {
msleep(RST_DELAY);
- hw_state = R(RGF_USER_HW_MACHINE_STATE);
+ x = R(RGF_USER_HW_MACHINE_STATE);
if (delay++ > RST_COUNT) {
wil_err(wil, "Reset not completed, hw_state 0x%08x\n",
- hw_state);
+ x);
return -ETIME;
}
- } while (hw_state != HW_MACHINE_BOOT_DONE);
+ } while (x != HW_MACHINE_BOOT_DONE);
/* TODO: Erez check rev_id != 1 */
if (!is_sparrow && (rev_id != 1))
@@ -535,7 +633,7 @@
WARN_ON(test_bit(wil_status_napi_en, &wil->status));
cancel_work_sync(&wil->disconnect_worker);
- wil6210_disconnect(wil, NULL);
+ wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
wil->status = 0; /* prevent NAPI from being scheduled */
@@ -640,7 +738,7 @@
return rc;
/* Rx VRING. After MAC and beacon */
- rc = wil_rx_init(wil);
+ rc = wil_rx_init(wil, 1 << rx_ring_order);
if (rc)
return rc;
diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c
index 2399651..e81703c 100644
--- a/drivers/net/wireless/ath/wil6210/netdev.c
+++ b/drivers/net/wireless/ath/wil6210/netdev.c
@@ -41,7 +41,7 @@
{
struct wil6210_priv *wil = ndev_to_wil(ndev);
- if (new_mtu < 68 || new_mtu > (TX_BUF_LEN - ETH_HLEN)) {
+ if (new_mtu < 68 || new_mtu > mtu_max) {
wil_err(wil, "invalid MTU %d\n", new_mtu);
return -EINVAL;
}
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index 2936ef0..e3f8bdc 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -206,12 +206,10 @@
u32 i, int headroom)
{
struct device *dev = wil_to_dev(wil);
- unsigned int sz = RX_BUF_LEN;
+ unsigned int sz = mtu_max + ETH_HLEN;
struct vring_rx_desc dd, *d = ⅆ
volatile struct vring_rx_desc *_d = &vring->va[i].rx;
dma_addr_t pa;
-
- /* TODO align */
struct sk_buff *skb = dev_alloc_skb(sz + headroom);
if (unlikely(!skb))
@@ -385,7 +383,7 @@
struct vring_rx_desc *d;
struct sk_buff *skb;
dma_addr_t pa;
- unsigned int sz = RX_BUF_LEN;
+ unsigned int sz = mtu_max + ETH_HLEN;
u16 dmalen;
u8 ftype;
u8 ds_bits;
@@ -596,7 +594,7 @@
wil_rx_refill(wil, v->size);
}
-int wil_rx_init(struct wil6210_priv *wil)
+int wil_rx_init(struct wil6210_priv *wil, u16 size)
{
struct vring *vring = &wil->vring_rx;
int rc;
@@ -608,7 +606,7 @@
return -EINVAL;
}
- vring->size = WIL6210_RX_RING_SIZE;
+ vring->size = size;
rc = wil_vring_alloc(wil, vring);
if (rc)
return rc;
@@ -646,7 +644,8 @@
.action = cpu_to_le32(WMI_VRING_CMD_ADD),
.vring_cfg = {
.tx_sw_ring = {
- .max_mpdu_size = cpu_to_le16(TX_BUF_LEN),
+ .max_mpdu_size =
+ cpu_to_le16(mtu_max + ETH_HLEN),
.ring_size = cpu_to_le16(size),
},
.ringid = id,
@@ -927,8 +926,9 @@
wil_dbg_txrx(wil, "%s()\n", __func__);
if (avail < 1 + nr_frags) {
- wil_err(wil, "Tx ring full. No space for %d fragments\n",
- 1 + nr_frags);
+ wil_err_ratelimited(wil,
+ "Tx ring full. No space for %d fragments\n",
+ 1 + nr_frags);
return -ENOMEM;
}
_d = &vring->va[i].tx;
diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h
index de04671..630aeb5 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.h
+++ b/drivers/net/wireless/ath/wil6210/txrx.h
@@ -21,8 +21,8 @@
#define BUF_HW_OWNED (0)
/* size of max. Tx/Rx buffers, as supported by FW */
-#define RX_BUF_LEN (2242)
-#define TX_BUF_LEN (2242)
+#define TXRX_BUF_LEN_DEFAULT (2242)
+
/* how many bytes to reserve for rtap header? */
#define WIL6210_RTAP_SIZE (128)
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index ce6488e..c6ec5b9 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -24,6 +24,7 @@
#include "wil_platform.h"
extern bool no_fw_recovery;
+extern unsigned int mtu_max;
#define WIL_NAME "wil6210"
#define WIL_FW_NAME "wil6210.fw"
@@ -48,8 +49,11 @@
#define WIL6210_MEM_SIZE (2*1024*1024UL)
-#define WIL6210_RX_RING_SIZE (128)
-#define WIL6210_TX_RING_SIZE (512)
+#define WIL_RX_RING_SIZE_ORDER_DEFAULT (9)
+#define WIL_TX_RING_SIZE_ORDER_DEFAULT (9)
+/* limit ring size in range [32..32k] */
+#define WIL_RING_SIZE_ORDER_MIN (5)
+#define WIL_RING_SIZE_ORDER_MAX (15)
#define WIL6210_MAX_TX_RINGS (24) /* HW limit */
#define WIL6210_MAX_CID (8) /* HW limit */
#define WIL6210_NAPI_BUDGET (16) /* arbitrary */
@@ -117,12 +121,15 @@
#define BIT_USER_USER_ICR_SW_INT_2 BIT(18)
#define RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0 (0x880c18)
#define RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_1 (0x880c2c)
+#define RGF_USER_SPARROW_M_4 (0x880c50) /* Sparrow */
+ #define BIT_SPARROW_M_4_SEL_SLEEP_OR_REF BIT(2)
#define RGF_DMA_EP_TX_ICR (0x881bb4) /* struct RGF_ICR */
#define BIT_DMA_EP_TX_ICR_TX_DONE BIT(0)
#define BIT_DMA_EP_TX_ICR_TX_DONE_N(n) BIT(n+1) /* n = [0..23] */
#define RGF_DMA_EP_RX_ICR (0x881bd0) /* struct RGF_ICR */
#define BIT_DMA_EP_RX_ICR_RX_DONE BIT(0)
+ #define BIT_DMA_EP_RX_ICR_RX_HTRSH BIT(1)
#define RGF_DMA_EP_MISC_ICR (0x881bec) /* struct RGF_ICR */
#define BIT_DMA_EP_MISC_ICR_RX_HTRSH BIT(0)
#define BIT_DMA_EP_MISC_ICR_TX_NO_ACT BIT(1)
@@ -152,6 +159,10 @@
#define RGF_MAC_MTRL_COUNTER_0 (0x886aa8)
#define RGF_CAF_ICR (0x88946c) /* struct RGF_ICR */
+#define RGF_CAF_OSC_CONTROL (0x88afa4)
+ #define BIT_CAF_OSC_XTAL_EN BIT(0)
+#define RGF_CAF_PLL_LOCK_STATUS (0x88afec)
+ #define BIT_CAF_OSC_DIG_XTAL_STABLE BIT(0)
/* popular locations */
#define HOST_MBOX HOSTADDR(RGF_USER_USER_SCRATCH_PAD)
@@ -461,10 +472,14 @@
#define wdev_to_wil(w) (struct wil6210_priv *)(wdev_priv(w))
#define wil_to_ndev(i) (wil_to_wdev(i)->netdev)
#define ndev_to_wil(n) (wdev_to_wil(n->ieee80211_ptr))
-#define wil_to_pcie_dev(i) (&i->pdev->dev)
+__printf(2, 3)
void wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...);
+__printf(2, 3)
void wil_err(struct wil6210_priv *wil, const char *fmt, ...);
+__printf(2, 3)
+void wil_err_ratelimited(struct wil6210_priv *wil, const char *fmt, ...);
+__printf(2, 3)
void wil_info(struct wil6210_priv *wil, const char *fmt, ...);
#define wil_dbg(wil, fmt, arg...) do { \
netdev_dbg(wil_to_ndev(wil), fmt, ##arg); \
@@ -575,9 +590,10 @@
int wmi_set_mac_address(struct wil6210_priv *wil, void *addr);
int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan);
int wmi_pcp_stop(struct wil6210_priv *wil);
-void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid);
+void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
+ u16 reason_code, bool from_event);
-int wil_rx_init(struct wil6210_priv *wil);
+int wil_rx_init(struct wil6210_priv *wil, u16 size);
void wil_rx_fini(struct wil6210_priv *wil);
/* TX API */
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 4311df9..63476c8 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -478,15 +478,15 @@
void *d, int len)
{
struct wmi_disconnect_event *evt = d;
+ u16 reason_code = le16_to_cpu(evt->protocol_reason_status);
- wil_dbg_wmi(wil, "Disconnect %pM reason %d proto %d wmi\n",
- evt->bssid,
- evt->protocol_reason_status, evt->disconnect_reason);
+ wil_dbg_wmi(wil, "Disconnect %pM reason [proto %d wmi %d]\n",
+ evt->bssid, reason_code, evt->disconnect_reason);
wil->sinfo_gen++;
mutex_lock(&wil->mutex);
- wil6210_disconnect(wil, evt->bssid);
+ wil6210_disconnect(wil, evt->bssid, reason_code, true);
mutex_unlock(&wil->mutex);
}
@@ -1025,7 +1025,7 @@
struct wmi_cfg_rx_chain_cmd cmd = {
.action = WMI_RX_CHAIN_ADD,
.rx_sw_ring = {
- .max_mpdu_size = cpu_to_le16(RX_BUF_LEN),
+ .max_mpdu_size = cpu_to_le16(mtu_max + ETH_HLEN),
.ring_mem_base = cpu_to_le64(vring->pa),
.ring_size = cpu_to_le16(vring->size),
},
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c
index d726f6c..809f01c 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -5110,7 +5110,9 @@
B43_WARN_ON(!vif || wl->vif != vif);
}
-static void b43_op_sw_scan_start_notifier(struct ieee80211_hw *hw)
+static void b43_op_sw_scan_start_notifier(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ const u8 *mac_addr)
{
struct b43_wl *wl = hw_to_b43_wl(hw);
struct b43_wldev *dev;
@@ -5124,7 +5126,8 @@
mutex_unlock(&wl->mutex);
}
-static void b43_op_sw_scan_complete_notifier(struct ieee80211_hw *hw)
+static void b43_op_sw_scan_complete_notifier(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
{
struct b43_wl *wl = hw_to_b43_wl(hw);
struct b43_wldev *dev;
diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/b43/phy_common.c
index 94da49c..9e59f8b 100644
--- a/drivers/net/wireless/b43/phy_common.c
+++ b/drivers/net/wireless/b43/phy_common.c
@@ -300,9 +300,7 @@
void b43_phy_copy(struct b43_wldev *dev, u16 destreg, u16 srcreg)
{
- assert_mac_suspended(dev);
- dev->phy.ops->phy_write(dev, destreg,
- dev->phy.ops->phy_read(dev, srcreg));
+ b43_phy_write(dev, destreg, b43_phy_read(dev, srcreg));
}
void b43_phy_mask(struct b43_wldev *dev, u16 offset, u16 mask)
diff --git a/drivers/net/wireless/brcm80211/Kconfig b/drivers/net/wireless/brcm80211/Kconfig
index 0443fa7..a2f096f 100644
--- a/drivers/net/wireless/brcm80211/Kconfig
+++ b/drivers/net/wireless/brcm80211/Kconfig
@@ -12,7 +12,7 @@
select LEDS_CLASS if BCMA_DRIVER_GPIO
select BRCMUTIL
depends on FW_LOADER
- select BACKPORT_CORDIC
+ select BPAUTO_CORDIC
---help---
This module adds support for PCIe wireless adapters based on Broadcom
IEEE802.11n SoftMAC chipsets. It also has WLAN led support, which will
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile
index 3d168e8..d496643 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile
+++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile
@@ -23,15 +23,15 @@
obj-$(CPTCFG_BRCMFMAC) += brcmfmac.o
brcmfmac-objs += \
- wl_cfg80211.o \
+ cfg80211.o \
chip.o \
fwil.o \
fweh.o \
fwsignal.o \
p2p.o \
proto.o \
- dhd_common.o \
- dhd_linux.o \
+ common.o \
+ core.o \
firmware.o \
feature.o \
btcoex.o \
@@ -43,14 +43,14 @@
flowring.o \
msgbuf.o
brcmfmac-$(CPTCFG_BRCMFMAC_SDIO) += \
- dhd_sdio.o \
+ sdio.o \
bcmsdh.o
brcmfmac-$(CPTCFG_BRCMFMAC_USB) += \
usb.o
brcmfmac-$(CPTCFG_BRCMFMAC_PCIE) += \
pcie.o
brcmfmac-$(CPTCFG_BRCMDBG) += \
- dhd_dbg.o
+ debug.o
brcmfmac-$(CPTCFG_BRCM_TRACING) += \
tracepoint.o
brcmfmac-$(CONFIG_OF) += \
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c
index a159ff3..8e0e91c 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c
@@ -25,10 +25,10 @@
#include <brcmu_utils.h>
#include <brcmu_wifi.h>
-#include "dhd.h"
-#include "dhd_bus.h"
+#include "core.h"
+#include "bus.h"
#include "fwsignal.h"
-#include "dhd_dbg.h"
+#include "debug.h"
#include "tracepoint.h"
#include "proto.h"
#include "bcdc.h"
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
index 8dbd5db..3c06e93 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
@@ -41,9 +41,9 @@
#include <chipcommon.h>
#include <soc.h>
#include "chip.h"
-#include "dhd_bus.h"
-#include "dhd_dbg.h"
-#include "sdio_host.h"
+#include "bus.h"
+#include "debug.h"
+#include "sdio.h"
#include "of.h"
#define SDIOH_API_ACCESS_RETRY_LIMIT 2
@@ -1064,6 +1064,16 @@
if (!sdiodev->pdata)
brcmf_of_probe(sdiodev);
+#ifdef CONFIG_PM_SLEEP
+ /* wowl can be supported when KEEP_POWER is true and (WAKE_SDIO_IRQ
+ * is true or when platform data OOB irq is true).
+ */
+ if ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_KEEP_POWER) &&
+ ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_WAKE_SDIO_IRQ) ||
+ (sdiodev->pdata->oob_irq_supported)))
+ bus_if->wowl_supported = true;
+#endif
+
atomic_set(&sdiodev->suspend, false);
init_waitqueue_head(&sdiodev->request_word_wait);
init_waitqueue_head(&sdiodev->request_buffer_wait);
@@ -1116,34 +1126,39 @@
brcmf_dbg(SDIO, "Exit\n");
}
+void brcmf_sdio_wowl_config(struct device *dev, bool enabled)
+{
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+ struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+
+ brcmf_dbg(SDIO, "Configuring WOWL, enabled=%d\n", enabled);
+ sdiodev->wowl_enabled = enabled;
+}
+
#ifdef CONFIG_PM_SLEEP
static int brcmf_ops_sdio_suspend(struct device *dev)
{
- mmc_pm_flag_t sdio_flags;
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
- int ret = 0;
+ mmc_pm_flag_t sdio_flags;
brcmf_dbg(SDIO, "Enter\n");
- sdio_flags = sdio_get_host_pm_caps(sdiodev->func[1]);
- if (!(sdio_flags & MMC_PM_KEEP_POWER)) {
- brcmf_err("Host can't keep power while suspended\n");
- return -EINVAL;
- }
-
atomic_set(&sdiodev->suspend, true);
- ret = sdio_set_host_pm_flags(sdiodev->func[1], MMC_PM_KEEP_POWER);
- if (ret) {
- brcmf_err("Failed to set pm_flags\n");
- atomic_set(&sdiodev->suspend, false);
- return ret;
+ if (sdiodev->wowl_enabled) {
+ sdio_flags = MMC_PM_KEEP_POWER;
+ if (sdiodev->pdata->oob_irq_supported)
+ enable_irq_wake(sdiodev->pdata->oob_irq_nr);
+ else
+ sdio_flags = MMC_PM_WAKE_SDIO_IRQ;
+ if (sdio_set_host_pm_flags(sdiodev->func[1], sdio_flags))
+ brcmf_err("Failed to set pm_flags %x\n", sdio_flags);
}
brcmf_sdio_wd_timer(sdiodev->bus, 0);
- return ret;
+ return 0;
}
static int brcmf_ops_sdio_resume(struct device *dev)
@@ -1152,6 +1167,8 @@
struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
brcmf_dbg(SDIO, "Enter\n");
+ if (sdiodev->pdata->oob_irq_supported)
+ disable_irq_wake(sdiodev->pdata->oob_irq_nr);
brcmf_sdio_wd_timer(sdiodev->bus, BRCMF_WD_POLL_MS);
atomic_set(&sdiodev->suspend, false);
return 0;
@@ -1204,7 +1221,6 @@
.remove = brcmf_sdio_pd_remove,
.driver = {
.name = BRCMFMAC_SDIO_PDATA_NAME,
- .owner = THIS_MODULE,
}
};
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c
index a29ac49..0445163 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c
@@ -20,13 +20,13 @@
#include <brcmu_wifi.h>
#include <brcmu_utils.h>
#include <defs.h>
-#include <dhd.h>
-#include <dhd_dbg.h>
+#include "core.h"
+#include "debug.h"
#include "fwil.h"
#include "fwil_types.h"
#include "btcoex.h"
#include "p2p.h"
-#include "wl_cfg80211.h"
+#include "cfg80211.h"
/* T1 start SCO/eSCO priority suppression */
#define BRCMF_BTCOEX_OPPR_WIN_TIME 2000
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h b/drivers/net/wireless/brcm80211/brcmfmac/bus.h
similarity index 98%
rename from drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h
rename to drivers/net/wireless/brcm80211/brcmfmac/bus.h
index 027ff83..ba6eb69 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/bus.h
@@ -14,10 +14,10 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifndef _BRCMF_BUS_H_
-#define _BRCMF_BUS_H_
+#ifndef BRCMFMAC_BUS_H
+#define BRCMFMAC_BUS_H
-#include "dhd_dbg.h"
+#include "debug.h"
/* IDs of the 6 default common rings of msgbuf protocol */
#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT 0
@@ -227,8 +227,7 @@
void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success);
int brcmf_bus_start(struct device *dev);
-s32 brcmf_iovar_data_set(struct device *dev, char *name, void *data,
- u32 len);
+s32 brcmf_iovar_data_set(struct device *dev, char *name, void *data, u32 len);
void brcmf_bus_add_txhdrlen(struct device *dev, uint len);
#ifdef CPTCFG_BRCMFMAC_SDIO
@@ -241,4 +240,4 @@
void brcmf_usb_register(void);
#endif
-#endif /* _BRCMF_BUS_H_ */
+#endif /* BRCMFMAC_BUS_H */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
similarity index 95%
rename from drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
rename to drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
index 28fa25b..3aecc5f 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
@@ -26,18 +26,18 @@
#include <brcmu_utils.h>
#include <defs.h>
#include <brcmu_wifi.h>
-#include "dhd.h"
-#include "dhd_dbg.h"
+#include "core.h"
+#include "debug.h"
#include "tracepoint.h"
#include "fwil_types.h"
#include "p2p.h"
#include "btcoex.h"
-#include "wl_cfg80211.h"
+#include "cfg80211.h"
#include "feature.h"
#include "fwil.h"
#include "proto.h"
#include "vendor.h"
-#include "dhd_bus.h"
+#include "bus.h"
#define BRCMF_SCAN_IE_LEN_MAX 2048
#define BRCMF_PNO_VERSION 2
@@ -299,6 +299,7 @@
primary_offset = ch->center_freq1 - ch->chan->center_freq;
switch (ch->width) {
case NL80211_CHAN_WIDTH_20:
+ case NL80211_CHAN_WIDTH_20_NOHT:
ch_inf.bw = BRCMU_CHAN_BW_20;
WARN_ON(primary_offset != 0);
break;
@@ -323,6 +324,10 @@
ch_inf.sb = BRCMU_CHAN_SB_LU;
}
break;
+ case NL80211_CHAN_WIDTH_80P80:
+ case NL80211_CHAN_WIDTH_160:
+ case NL80211_CHAN_WIDTH_5:
+ case NL80211_CHAN_WIDTH_10:
default:
WARN_ON_ONCE(1);
}
@@ -333,6 +338,7 @@
case IEEE80211_BAND_5GHZ:
ch_inf.band = BRCMU_CHAN_BAND_5G;
break;
+ case IEEE80211_BAND_60GHZ:
default:
WARN_ON_ONCE(1);
}
@@ -514,6 +520,95 @@
ADDR_INDIRECT);
}
+static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp)
+{
+ struct brcmf_mbss_ssid_le mbss_ssid_le;
+ int bsscfgidx;
+ int err;
+
+ memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le));
+ bsscfgidx = brcmf_get_next_free_bsscfgidx(ifp->drvr);
+ if (bsscfgidx < 0)
+ return bsscfgidx;
+
+ mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx);
+ mbss_ssid_le.SSID_len = cpu_to_le32(5);
+ sprintf(mbss_ssid_le.SSID, "ssid%d" , bsscfgidx);
+
+ err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le,
+ sizeof(mbss_ssid_le));
+ if (err < 0)
+ brcmf_err("setting ssid failed %d\n", err);
+
+ return err;
+}
+
+/**
+ * brcmf_ap_add_vif() - create a new AP virtual interface for multiple BSS
+ *
+ * @wiphy: wiphy device of new interface.
+ * @name: name of the new interface.
+ * @flags: not used.
+ * @params: contains mac address for AP device.
+ */
+static
+struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name,
+ u32 *flags, struct vif_params *params)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
+ struct brcmf_cfg80211_vif *vif;
+ int err;
+
+ if (brcmf_cfg80211_vif_event_armed(cfg))
+ return ERR_PTR(-EBUSY);
+
+ brcmf_dbg(INFO, "Adding vif \"%s\"\n", name);
+
+ vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_AP, false);
+ if (IS_ERR(vif))
+ return (struct wireless_dev *)vif;
+
+ brcmf_cfg80211_arm_vif_event(cfg, vif);
+
+ err = brcmf_cfg80211_request_ap_if(ifp);
+ if (err) {
+ brcmf_cfg80211_arm_vif_event(cfg, NULL);
+ goto fail;
+ }
+
+ /* wait for firmware event */
+ err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD,
+ msecs_to_jiffies(1500));
+ brcmf_cfg80211_arm_vif_event(cfg, NULL);
+ if (!err) {
+ brcmf_err("timeout occurred\n");
+ err = -EIO;
+ goto fail;
+ }
+
+ /* interface created in firmware */
+ ifp = vif->ifp;
+ if (!ifp) {
+ brcmf_err("no if pointer provided\n");
+ err = -ENOENT;
+ goto fail;
+ }
+
+ strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1);
+ err = brcmf_net_attach(ifp, true);
+ if (err) {
+ brcmf_err("Registering netdevice failed\n");
+ goto fail;
+ }
+
+ return &ifp->vif->wdev;
+
+fail:
+ brcmf_free_vif(vif);
+ return ERR_PTR(err);
+}
+
static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif)
{
enum nl80211_iftype iftype;
@@ -539,12 +634,16 @@
switch (type) {
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_STATION:
- case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_WDS:
case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_MESH_POINT:
return ERR_PTR(-EOPNOTSUPP);
+ case NL80211_IFTYPE_AP:
+ wdev = brcmf_ap_add_vif(wiphy, name, flags, params);
+ if (!IS_ERR(wdev))
+ brcmf_cfg80211_update_proto_addr_mode(wdev);
+ return wdev;
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_P2P_GO:
case NL80211_IFTYPE_P2P_DEVICE:
@@ -1809,6 +1908,7 @@
return -EIO;
clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
+ clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
cfg80211_disconnected(ndev, reason_code, NULL, 0, GFP_KERNEL);
memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
@@ -2779,6 +2879,44 @@
}
}
+static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4],
+ u8 *pattern, u32 patternsize, u8 *mask,
+ u32 packet_offset)
+{
+ struct brcmf_fil_wowl_pattern_le *filter;
+ u32 masksize;
+ u32 patternoffset;
+ u8 *buf;
+ u32 bufsize;
+ s32 ret;
+
+ masksize = (patternsize + 7) / 8;
+ patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize;
+
+ bufsize = sizeof(*filter) + patternsize + masksize;
+ buf = kzalloc(bufsize, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ filter = (struct brcmf_fil_wowl_pattern_le *)buf;
+
+ memcpy(filter->cmd, cmd, 4);
+ filter->masksize = cpu_to_le32(masksize);
+ filter->offset = cpu_to_le32(packet_offset);
+ filter->patternoffset = cpu_to_le32(patternoffset);
+ filter->patternsize = cpu_to_le32(patternsize);
+ filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP);
+
+ if ((mask) && (masksize))
+ memcpy(buf + sizeof(*filter), mask, masksize);
+ if ((pattern) && (patternsize))
+ memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize);
+
+ ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize);
+
+ kfree(buf);
+ return ret;
+}
+
static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
{
struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
@@ -2788,10 +2926,11 @@
brcmf_dbg(TRACE, "Enter\n");
if (cfg->wowl_enabled) {
+ brcmf_configure_arp_offload(ifp, true);
brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM,
cfg->pre_wowl_pmmode);
- brcmf_fil_iovar_data_set(ifp, "wowl_pattern", "clr", 4);
brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0);
+ brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0);
cfg->wowl_enabled = false;
}
return 0;
@@ -2802,21 +2941,29 @@
struct cfg80211_wowlan *wowl)
{
u32 wowl_config;
+ u32 i;
brcmf_dbg(TRACE, "Suspend, wowl config.\n");
+ brcmf_configure_arp_offload(ifp, false);
brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->pre_wowl_pmmode);
brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX);
wowl_config = 0;
if (wowl->disconnect)
- wowl_config |= WL_WOWL_DIS | WL_WOWL_BCN | WL_WOWL_RETR;
- /* Note: if "wowl" target and not "wowlpf" then wowl_bcn_loss
- * should be configured. This paramater is not supported by
- * wowlpf.
- */
+ wowl_config = BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | BRCMF_WOWL_RETR;
if (wowl->magic_pkt)
- wowl_config |= WL_WOWL_MAGIC;
+ wowl_config |= BRCMF_WOWL_MAGIC;
+ if ((wowl->patterns) && (wowl->n_patterns)) {
+ wowl_config |= BRCMF_WOWL_NET;
+ for (i = 0; i < wowl->n_patterns; i++) {
+ brcmf_config_wowl_pattern(ifp, "add",
+ (u8 *)wowl->patterns[i].pattern,
+ wowl->patterns[i].pattern_len,
+ (u8 *)wowl->patterns[i].mask,
+ wowl->patterns[i].pkt_offset);
+ }
+ }
brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config);
brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1);
brcmf_bus_wowl_config(cfg->pub->bus_if, true);
@@ -2879,7 +3026,7 @@
struct brcmf_cfg80211_pmk_list *pmk_list, s32 err)
{
int i, j;
- int pmkid_len;
+ u32 pmkid_len;
pmkid_len = le32_to_cpu(pmk_list->pmkids.npmkid);
@@ -2907,8 +3054,7 @@
struct brcmf_if *ifp = netdev_priv(ndev);
struct pmkid_list *pmkids = &cfg->pmk_list->pmkids;
s32 err = 0;
- int i;
- int pmkid_len;
+ u32 pmkid_len, i;
brcmf_dbg(TRACE, "Enter\n");
if (!check_vif_up(ifp->vif))
@@ -2947,7 +3093,7 @@
struct brcmf_if *ifp = netdev_priv(ndev);
struct pmkid_list pmkid;
s32 err = 0;
- int i, pmkid_len;
+ u32 pmkid_len, i;
brcmf_dbg(TRACE, "Enter\n");
if (!check_vif_up(ifp->vif))
@@ -3308,11 +3454,10 @@
}
static s32
-brcmf_configure_wpaie(struct net_device *ndev,
+brcmf_configure_wpaie(struct brcmf_if *ifp,
const struct brcmf_vs_tlv *wpa_ie,
bool is_rsn_ie)
{
- struct brcmf_if *ifp = netdev_priv(ndev);
u32 auth = 0; /* d11 open authentication */
u16 count;
s32 err = 0;
@@ -3787,6 +3932,7 @@
enum nl80211_iftype dev_role;
struct brcmf_fil_bss_enable_le bss_enable;
u16 chanspec;
+ bool mbss;
brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n",
settings->chandef.chan->hw_value,
@@ -3797,6 +3943,7 @@
settings->inactivity_timeout);
dev_role = ifp->vif->wdev.iftype;
+ mbss = ifp->vif->mbss;
memset(&ssid_le, 0, sizeof(ssid_le));
if (settings->ssid == NULL || settings->ssid_len == 0) {
@@ -3816,8 +3963,10 @@
ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
}
- brcmf_set_mpc(ifp, 0);
- brcmf_configure_arp_offload(ifp, false);
+ if (!mbss) {
+ brcmf_set_mpc(ifp, 0);
+ brcmf_configure_arp_offload(ifp, false);
+ }
/* find the RSN_IE */
rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
@@ -3831,13 +3980,16 @@
brcmf_dbg(TRACE, "WPA(2) IE is found\n");
if (wpa_ie != NULL) {
/* WPA IE */
- err = brcmf_configure_wpaie(ndev, wpa_ie, false);
+ err = brcmf_configure_wpaie(ifp, wpa_ie, false);
if (err < 0)
goto exit;
} else {
+ struct brcmf_vs_tlv *tmp_ie;
+
+ tmp_ie = (struct brcmf_vs_tlv *)rsn_ie;
+
/* RSN IE */
- err = brcmf_configure_wpaie(ndev,
- (struct brcmf_vs_tlv *)rsn_ie, true);
+ err = brcmf_configure_wpaie(ifp, tmp_ie, true);
if (err < 0)
goto exit;
}
@@ -3848,45 +4000,53 @@
brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon);
- chanspec = chandef_to_chanspec(&cfg->d11inf, &settings->chandef);
- err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
- if (err < 0) {
- brcmf_err("Set Channel failed: chspec=%d, %d\n", chanspec, err);
- goto exit;
- }
-
- if (settings->beacon_interval) {
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
- settings->beacon_interval);
+ if (!mbss) {
+ chanspec = chandef_to_chanspec(&cfg->d11inf,
+ &settings->chandef);
+ err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
if (err < 0) {
- brcmf_err("Beacon Interval Set Error, %d\n", err);
+ brcmf_err("Set Channel failed: chspec=%d, %d\n",
+ chanspec, err);
goto exit;
}
- }
- if (settings->dtim_period) {
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,
- settings->dtim_period);
+
+ if (settings->beacon_interval) {
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
+ settings->beacon_interval);
+ if (err < 0) {
+ brcmf_err("Beacon Interval Set Error, %d\n",
+ err);
+ goto exit;
+ }
+ }
+ if (settings->dtim_period) {
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,
+ settings->dtim_period);
+ if (err < 0) {
+ brcmf_err("DTIM Interval Set Error, %d\n", err);
+ goto exit;
+ }
+ }
+
+ if (dev_role == NL80211_IFTYPE_AP) {
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
+ if (err < 0) {
+ brcmf_err("BRCMF_C_DOWN error %d\n", err);
+ goto exit;
+ }
+ brcmf_fil_iovar_int_set(ifp, "apsta", 0);
+ }
+
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
if (err < 0) {
- brcmf_err("DTIM Interval Set Error, %d\n", err);
+ brcmf_err("SET INFRA error %d\n", err);
goto exit;
}
}
-
- if (dev_role == NL80211_IFTYPE_AP) {
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
- if (err < 0) {
- brcmf_err("BRCMF_C_DOWN error %d\n", err);
- goto exit;
- }
- brcmf_fil_iovar_int_set(ifp, "apsta", 0);
- }
-
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
- if (err < 0) {
- brcmf_err("SET INFRA error %d\n", err);
- goto exit;
- }
if (dev_role == NL80211_IFTYPE_AP) {
+ if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss))
+ brcmf_fil_iovar_int_set(ifp, "mbss", 1);
+
err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1);
if (err < 0) {
brcmf_err("setting AP mode failed %d\n", err);
@@ -3931,7 +4091,7 @@
set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
exit:
- if (err) {
+ if ((err) && (!mbss)) {
brcmf_set_mpc(ifp, 1);
brcmf_configure_arp_offload(ifp, true);
}
@@ -3952,20 +4112,31 @@
/* first to make sure they get processed by fw. */
msleep(400);
+ if (ifp->vif->mbss) {
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
+ return err;
+ }
+
memset(&join_params, 0, sizeof(join_params));
err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
&join_params, sizeof(join_params));
if (err < 0)
brcmf_err("SET SSID error (%d)\n", err);
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
if (err < 0)
- brcmf_err("BRCMF_C_UP error %d\n", err);
+ brcmf_err("BRCMF_C_DOWN error %d\n", err);
err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0);
if (err < 0)
brcmf_err("setting AP mode failed %d\n", err);
err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 0);
if (err < 0)
brcmf_err("setting INFRA mode failed %d\n", err);
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS))
+ brcmf_fil_iovar_int_set(ifp, "mbss", 0);
+ /* Bring device back up so it can be used again */
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
+ if (err < 0)
+ brcmf_err("BRCMF_C_UP error %d\n", err);
} else {
bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx);
bss_enable.enable = cpu_to_le32(0);
@@ -3998,24 +4169,24 @@
static int
brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
- const u8 *mac)
+ struct station_del_parameters *params)
{
struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
struct brcmf_scb_val_le scbval;
struct brcmf_if *ifp = netdev_priv(ndev);
s32 err;
- if (!mac)
+ if (!params->mac)
return -EFAULT;
- brcmf_dbg(TRACE, "Enter %pM\n", mac);
+ brcmf_dbg(TRACE, "Enter %pM\n", params->mac);
if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
if (!check_vif_up(ifp->vif))
return -EIO;
- memcpy(&scbval.ea, mac, ETH_ALEN);
+ memcpy(&scbval.ea, params->mac, ETH_ALEN);
scbval.val = cpu_to_le32(WLAN_REASON_DEAUTH_LEAVING);
err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
&scbval, sizeof(scbval));
@@ -4317,7 +4488,9 @@
enum nl80211_iftype type,
bool pm_block)
{
+ struct brcmf_cfg80211_vif *vif_walk;
struct brcmf_cfg80211_vif *vif;
+ bool mbss;
brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n",
sizeof(*vif));
@@ -4333,6 +4506,17 @@
brcmf_init_prof(&vif->profile);
+ if (type == NL80211_IFTYPE_AP) {
+ mbss = false;
+ list_for_each_entry(vif_walk, &cfg->vif_list, list) {
+ if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) {
+ mbss = true;
+ break;
+ }
+ }
+ vif->mbss = mbss;
+ }
+
list_add_tail(&vif->list, &cfg->vif_list);
return vif;
}
@@ -4575,6 +4759,7 @@
struct net_device *ndev,
const struct brcmf_event_msg *e, void *data)
{
+ struct brcmf_if *ifp = netdev_priv(ndev);
static int generation;
u32 event = e->event_code;
u32 reason = e->reason;
@@ -4585,6 +4770,8 @@
ndev != cfg_to_ndev(cfg)) {
brcmf_dbg(CONN, "AP mode link down\n");
complete(&cfg->vif_disabled);
+ if (ifp->vif->mbss)
+ brcmf_remove_interface(ifp->drvr, ifp->bssidx);
return 0;
}
@@ -5376,7 +5563,28 @@
return 0;
}
-static const struct ieee80211_iface_limit brcmf_iface_limits[] = {
+static const struct ieee80211_iface_limit brcmf_iface_limits_mbss[] = {
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_ADHOC)
+ },
+ {
+ .max = 4,
+ .types = BIT(NL80211_IFTYPE_AP)
+ },
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_P2P_GO)
+ },
+ {
+ .max = 1,
+ .types = BIT(NL80211_IFTYPE_P2P_DEVICE)
+ }
+};
+
+static const struct ieee80211_iface_limit brcmf_iface_limits_sbss[] = {
{
.max = 2,
.types = BIT(NL80211_IFTYPE_STATION) |
@@ -5397,8 +5605,8 @@
{
.max_interfaces = BRCMF_IFACE_MAX_CNT,
.num_different_channels = 1,
- .n_limits = ARRAY_SIZE(brcmf_iface_limits),
- .limits = brcmf_iface_limits
+ .n_limits = ARRAY_SIZE(brcmf_iface_limits_sbss),
+ .limits = brcmf_iface_limits_sbss,
}
};
@@ -5440,10 +5648,13 @@
wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
}
-
#ifdef CONFIG_PM
static const struct wiphy_wowlan_support brcmf_wowlan_support = {
.flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
+ .n_patterns = BRCMF_WOWL_MAXPATTERNS,
+ .pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE,
+ .pattern_min_len = 1,
+ .max_pkt_offset = 1500,
};
#endif
@@ -5471,6 +5682,10 @@
ifc_combo = brcmf_iface_combos[0];
if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
ifc_combo.num_different_channels = 2;
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) {
+ ifc_combo.n_limits = ARRAY_SIZE(brcmf_iface_limits_mbss),
+ ifc_combo.limits = brcmf_iface_limits_mbss;
+ }
wiphy->iface_combinations = kmemdup(&ifc_combo,
sizeof(ifc_combo),
GFP_KERNEL);
@@ -5607,7 +5822,8 @@
return wdev->iftype;
}
-bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg, unsigned long state)
+bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg,
+ unsigned long state)
{
struct brcmf_cfg80211_vif *vif;
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h
similarity index 98%
rename from drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h
rename to drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h
index 6abf94e..9e98b8d 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h
@@ -14,8 +14,8 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifndef _wl_cfg80211_h_
-#define _wl_cfg80211_h_
+#ifndef BRCMFMAC_CFG80211_H
+#define BRCMFMAC_CFG80211_H
/* for brcmu_d11inf */
#include <brcmu_d11.h>
@@ -183,6 +183,7 @@
* @pm_block: power-management blocked.
* @list: linked list.
* @mgmt_rx_reg: registered rx mgmt frame types.
+ * @mbss: Multiple BSS type, set if not first AP (not relevant for P2P).
*/
struct brcmf_cfg80211_vif {
struct brcmf_if *ifp;
@@ -194,6 +195,7 @@
struct vif_saved_ie saved_ie;
struct list_head list;
u16 mgmt_rx_reg;
+ bool mbss;
};
/* association inform */
@@ -480,7 +482,8 @@
brcmf_parse_tlvs(const void *buf, int buflen, uint key);
u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
struct ieee80211_channel *ch);
-bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg, unsigned long state);
+bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg,
+ unsigned long state);
void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
struct brcmf_cfg80211_vif *vif);
bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg);
@@ -493,4 +496,4 @@
void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg);
void brcmf_cfg80211_free_netdev(struct net_device *ndev);
-#endif /* _wl_cfg80211_h_ */
+#endif /* BRCMFMAC_CFG80211_H */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c
index 95efde8..ddae0b5 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c
@@ -25,7 +25,7 @@
#include <brcm_hw_ids.h>
#include <brcmu_utils.h>
#include <chipcommon.h>
-#include "dhd_dbg.h"
+#include "debug.h"
#include "chip.h"
/* SOC Interconnect types (aka chip types) */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/common.c b/drivers/net/wireless/brcm80211/brcmfmac/common.c
new file mode 100644
index 0000000..ed8356d
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/common.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/netdevice.h>
+#include <brcmu_wifi.h>
+#include <brcmu_utils.h>
+#include "core.h"
+#include "bus.h"
+#include "debug.h"
+#include "fwil.h"
+#include "fwil_types.h"
+#include "tracepoint.h"
+
+#define BRCMF_DEFAULT_BCN_TIMEOUT 3
+#define BRCMF_DEFAULT_SCAN_CHANNEL_TIME 40
+#define BRCMF_DEFAULT_SCAN_UNASSOC_TIME 40
+
+/* boost value for RSSI_DELTA in preferred join selection */
+#define BRCMF_JOIN_PREF_RSSI_BOOST 8
+
+int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
+{
+ s8 eventmask[BRCMF_EVENTING_MASK_LEN];
+ u8 buf[BRCMF_DCMD_SMLEN];
+ struct brcmf_join_pref_params join_pref_params[2];
+ char *ptr;
+ s32 err;
+
+ /* retreive mac address */
+ err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr,
+ sizeof(ifp->mac_addr));
+ if (err < 0) {
+ brcmf_err("Retreiving cur_etheraddr failed, %d\n",
+ err);
+ goto done;
+ }
+ memcpy(ifp->drvr->mac, ifp->mac_addr, sizeof(ifp->drvr->mac));
+
+ /* query for 'ver' to get version info from firmware */
+ memset(buf, 0, sizeof(buf));
+ strcpy(buf, "ver");
+ err = brcmf_fil_iovar_data_get(ifp, "ver", buf, sizeof(buf));
+ if (err < 0) {
+ brcmf_err("Retreiving version information failed, %d\n",
+ err);
+ goto done;
+ }
+ ptr = (char *)buf;
+ strsep(&ptr, "\n");
+
+ /* Print fw version info */
+ brcmf_err("Firmware version = %s\n", buf);
+
+ /* locate firmware version number for ethtool */
+ ptr = strrchr(buf, ' ') + 1;
+ strlcpy(ifp->drvr->fwver, ptr, sizeof(ifp->drvr->fwver));
+
+ /* set mpc */
+ err = brcmf_fil_iovar_int_set(ifp, "mpc", 1);
+ if (err) {
+ brcmf_err("failed setting mpc\n");
+ goto done;
+ }
+
+ /*
+ * Setup timeout if Beacons are lost and roam is off to report
+ * link down
+ */
+ err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout",
+ BRCMF_DEFAULT_BCN_TIMEOUT);
+ if (err) {
+ brcmf_err("bcn_timeout error (%d)\n", err);
+ goto done;
+ }
+
+ /* Enable/Disable build-in roaming to allowed ext supplicant to take
+ * of romaing
+ */
+ err = brcmf_fil_iovar_int_set(ifp, "roam_off", 1);
+ if (err) {
+ brcmf_err("roam_off error (%d)\n", err);
+ goto done;
+ }
+
+ /* Setup join_pref to select target by RSSI(with boost on 5GHz) */
+ join_pref_params[0].type = BRCMF_JOIN_PREF_RSSI_DELTA;
+ join_pref_params[0].len = 2;
+ join_pref_params[0].rssi_gain = BRCMF_JOIN_PREF_RSSI_BOOST;
+ join_pref_params[0].band = WLC_BAND_5G;
+ join_pref_params[1].type = BRCMF_JOIN_PREF_RSSI;
+ join_pref_params[1].len = 2;
+ join_pref_params[1].rssi_gain = 0;
+ join_pref_params[1].band = 0;
+ err = brcmf_fil_iovar_data_set(ifp, "join_pref", join_pref_params,
+ sizeof(join_pref_params));
+ if (err)
+ brcmf_err("Set join_pref error (%d)\n", err);
+
+ /* Setup event_msgs, enable E_IF */
+ err = brcmf_fil_iovar_data_get(ifp, "event_msgs", eventmask,
+ BRCMF_EVENTING_MASK_LEN);
+ if (err) {
+ brcmf_err("Get event_msgs error (%d)\n", err);
+ goto done;
+ }
+ setbit(eventmask, BRCMF_E_IF);
+ err = brcmf_fil_iovar_data_set(ifp, "event_msgs", eventmask,
+ BRCMF_EVENTING_MASK_LEN);
+ if (err) {
+ brcmf_err("Set event_msgs error (%d)\n", err);
+ goto done;
+ }
+
+ /* Setup default scan channel time */
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
+ BRCMF_DEFAULT_SCAN_CHANNEL_TIME);
+ if (err) {
+ brcmf_err("BRCMF_C_SET_SCAN_CHANNEL_TIME error (%d)\n",
+ err);
+ goto done;
+ }
+
+ /* Setup default scan unassoc time */
+ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
+ BRCMF_DEFAULT_SCAN_UNASSOC_TIME);
+ if (err) {
+ brcmf_err("BRCMF_C_SET_SCAN_UNASSOC_TIME error (%d)\n",
+ err);
+ goto done;
+ }
+
+ /* do bus specific preinit here */
+ err = brcmf_bus_preinit(ifp->drvr->bus_if);
+done:
+ return err;
+}
+
+#if defined(CPTCFG_BRCM_TRACING) || defined(CPTCFG_BRCMDBG)
+void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...)
+{
+ struct va_format vaf = {
+ .fmt = fmt,
+ };
+ va_list args;
+
+ va_start(args, fmt);
+ vaf.va = &args;
+ if (brcmf_msg_level & level)
+ pr_debug("%s %pV", func, &vaf);
+ trace_brcmf_dbg(level, func, &vaf);
+ va_end(args);
+}
+#endif
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/commonring.c b/drivers/net/wireless/brcm80211/brcmfmac/commonring.c
index c6d65b8..77656c7 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/commonring.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/commonring.c
@@ -19,7 +19,7 @@
#include <brcmu_utils.h>
#include <brcmu_wifi.h>
-#include "dhd.h"
+#include "core.h"
#include "commonring.h"
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c
similarity index 96%
rename from drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
rename to drivers/net/wireless/brcm80211/brcmfmac/core.c
index c1a16b1..f8785da 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
@@ -22,12 +22,12 @@
#include <brcmu_utils.h>
#include <brcmu_wifi.h>
-#include "dhd.h"
-#include "dhd_bus.h"
-#include "dhd_dbg.h"
+#include "core.h"
+#include "bus.h"
+#include "debug.h"
#include "fwil_types.h"
#include "p2p.h"
-#include "wl_cfg80211.h"
+#include "cfg80211.h"
#include "fwil.h"
#include "fwsignal.h"
#include "feature.h"
@@ -836,7 +836,7 @@
return ifp;
}
-void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx)
+static void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx)
{
struct brcmf_if *ifp;
@@ -869,6 +869,38 @@
}
}
+void brcmf_remove_interface(struct brcmf_pub *drvr, u32 bssidx)
+{
+ if (drvr->iflist[bssidx]) {
+ brcmf_fws_del_interface(drvr->iflist[bssidx]);
+ brcmf_del_if(drvr, bssidx);
+ }
+}
+
+int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr)
+{
+ int ifidx;
+ int bsscfgidx;
+ bool available;
+ int highest;
+
+ available = false;
+ bsscfgidx = 2;
+ highest = 2;
+ for (ifidx = 0; ifidx < BRCMF_MAX_IFS; ifidx++) {
+ if (drvr->iflist[ifidx]) {
+ if (drvr->iflist[ifidx]->bssidx == bsscfgidx)
+ bsscfgidx = highest + 1;
+ else if (drvr->iflist[ifidx]->bssidx > highest)
+ highest = drvr->iflist[ifidx]->bssidx;
+ } else {
+ available = true;
+ }
+ }
+
+ return available ? bsscfgidx : -ENOMEM;
+}
+
int brcmf_attach(struct device *dev)
{
struct brcmf_pub *drvr = NULL;
@@ -1033,10 +1065,7 @@
/* make sure primary interface removed last */
for (i = BRCMF_MAX_IFS-1; i > -1; i--)
- if (drvr->iflist[i]) {
- brcmf_fws_del_interface(drvr->iflist[i]);
- brcmf_del_if(drvr, i);
- }
+ brcmf_remove_interface(drvr, i);
brcmf_cfg80211_detach(drvr->config);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/core.h
similarity index 96%
rename from drivers/net/wireless/brcm80211/brcmfmac/dhd.h
rename to drivers/net/wireless/brcm80211/brcmfmac/core.h
index 5e4317d..23f74b1 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h
@@ -18,8 +18,8 @@
* Common types *
*/
-#ifndef _BRCMF_H_
-#define _BRCMF_H_
+#ifndef BRCMFMAC_CORE_H
+#define BRCMFMAC_CORE_H
#include "fweh.h"
@@ -83,7 +83,6 @@
/* Internal brcmf items */
uint hdrlen; /* Total BRCMF header length (proto + bus) */
uint rxsz; /* Rx buffer size bus module should use */
- u8 wme_dp; /* wme discard priority */
/* Dongle media info */
char fwver[BRCMF_DRIVER_FIRMWARE_VERSION_LEN];
@@ -176,7 +175,8 @@
int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked);
struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx,
char *name, u8 *mac_addr);
-void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx);
+void brcmf_remove_interface(struct brcmf_pub *drvr, u32 bssidx);
+int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr);
void brcmf_txflowblock_if(struct brcmf_if *ifp,
enum brcmf_netif_stop_reason reason, bool state);
void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx,
@@ -186,4 +186,4 @@
/* Sets dongle media info (drv_version, mac address). */
int brcmf_c_preinit_dcmds(struct brcmf_if *ifp);
-#endif /* _BRCMF_H_ */
+#endif /* BRCMFMAC_CORE_H */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c b/drivers/net/wireless/brcm80211/brcmfmac/debug.c
similarity index 97%
rename from drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c
rename to drivers/net/wireless/brcm80211/brcmfmac/debug.c
index be9f4f8..9b473d5 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/debug.c
@@ -19,9 +19,9 @@
#include <brcmu_wifi.h>
#include <brcmu_utils.h>
-#include "dhd.h"
-#include "dhd_bus.h"
-#include "dhd_dbg.h"
+#include "core.h"
+#include "bus.h"
+#include "debug.h"
static struct dentry *root_folder;
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h b/drivers/net/wireless/brcm80211/brcmfmac/debug.h
similarity index 97%
rename from drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
rename to drivers/net/wireless/brcm80211/brcmfmac/debug.h
index b811d59..a1a9bf4 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/debug.h
@@ -14,8 +14,8 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifndef _BRCMF_DBG_H_
-#define _BRCMF_DBG_H_
+#ifndef BRCMFMAC_DEBUG_H
+#define BRCMFMAC_DEBUG_H
/* message levels */
#define BRCMF_TRACE_VAL 0x00000002
@@ -133,4 +133,4 @@
}
#endif
-#endif /* _BRCMF_DBG_H_ */
+#endif /* BRCMFMAC_DEBUG_H */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
deleted file mode 100644
index 67503e2..0000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
+++ /dev/null
@@ -1,400 +0,0 @@
-/*
- * Copyright (c) 2010 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/netdevice.h>
-#include <brcmu_wifi.h>
-#include <brcmu_utils.h>
-#include "dhd.h"
-#include "dhd_bus.h"
-#include "dhd_dbg.h"
-#include "fwil.h"
-#include "fwil_types.h"
-#include "tracepoint.h"
-
-#define PKTFILTER_BUF_SIZE 128
-#define BRCMF_DEFAULT_BCN_TIMEOUT 3
-#define BRCMF_DEFAULT_SCAN_CHANNEL_TIME 40
-#define BRCMF_DEFAULT_SCAN_UNASSOC_TIME 40
-#define BRCMF_DEFAULT_PACKET_FILTER "100 0 0 0 0x01 0x00"
-
-/* boost value for RSSI_DELTA in preferred join selection */
-#define BRCMF_JOIN_PREF_RSSI_BOOST 8
-
-
-bool brcmf_c_prec_enq(struct device *dev, struct pktq *q,
- struct sk_buff *pkt, int prec)
-{
- struct sk_buff *p;
- int eprec = -1; /* precedence to evict from */
- bool discard_oldest;
- struct brcmf_bus *bus_if = dev_get_drvdata(dev);
- struct brcmf_pub *drvr = bus_if->drvr;
-
- /* Fast case, precedence queue is not full and we are also not
- * exceeding total queue length
- */
- if (!pktq_pfull(q, prec) && !pktq_full(q)) {
- brcmu_pktq_penq(q, prec, pkt);
- return true;
- }
-
- /* Determine precedence from which to evict packet, if any */
- if (pktq_pfull(q, prec))
- eprec = prec;
- else if (pktq_full(q)) {
- p = brcmu_pktq_peek_tail(q, &eprec);
- if (eprec > prec)
- return false;
- }
-
- /* Evict if needed */
- if (eprec >= 0) {
- /* Detect queueing to unconfigured precedence */
- discard_oldest = ac_bitmap_tst(drvr->wme_dp, eprec);
- if (eprec == prec && !discard_oldest)
- return false; /* refuse newer (incoming) packet */
- /* Evict packet according to discard policy */
- p = discard_oldest ? brcmu_pktq_pdeq(q, eprec) :
- brcmu_pktq_pdeq_tail(q, eprec);
- if (p == NULL)
- brcmf_err("brcmu_pktq_penq() failed, oldest %d\n",
- discard_oldest);
-
- brcmu_pkt_buf_free_skb(p);
- }
-
- /* Enqueue */
- p = brcmu_pktq_penq(q, prec, pkt);
- if (p == NULL)
- brcmf_err("brcmu_pktq_penq() failed\n");
-
- return p != NULL;
-}
-
-/* Convert user's input in hex pattern to byte-size mask */
-static int brcmf_c_pattern_atoh(char *src, char *dst)
-{
- int i;
- if (strncmp(src, "0x", 2) != 0 && strncmp(src, "0X", 2) != 0) {
- brcmf_err("Mask invalid format. Needs to start with 0x\n");
- return -EINVAL;
- }
- src = src + 2; /* Skip past 0x */
- if (strlen(src) % 2 != 0) {
- brcmf_err("Mask invalid format. Length must be even.\n");
- return -EINVAL;
- }
- for (i = 0; *src != '\0'; i++) {
- unsigned long res;
- char num[3];
- strncpy(num, src, 2);
- num[2] = '\0';
- if (kstrtoul(num, 16, &res))
- return -EINVAL;
- dst[i] = (u8)res;
- src += 2;
- }
- return i;
-}
-
-static void
-brcmf_c_pktfilter_offload_enable(struct brcmf_if *ifp, char *arg, int enable,
- int master_mode)
-{
- unsigned long res;
- char *argv;
- char *arg_save = NULL, *arg_org = NULL;
- s32 err;
- struct brcmf_pkt_filter_enable_le enable_parm;
-
- arg_save = kstrdup(arg, GFP_ATOMIC);
- if (!arg_save)
- goto fail;
-
- arg_org = arg_save;
-
- argv = strsep(&arg_save, " ");
-
- if (argv == NULL) {
- brcmf_err("No args provided\n");
- goto fail;
- }
-
- /* Parse packet filter id. */
- enable_parm.id = 0;
- if (!kstrtoul(argv, 0, &res))
- enable_parm.id = cpu_to_le32((u32)res);
-
- /* Enable/disable the specified filter. */
- enable_parm.enable = cpu_to_le32(enable);
-
- err = brcmf_fil_iovar_data_set(ifp, "pkt_filter_enable", &enable_parm,
- sizeof(enable_parm));
- if (err)
- brcmf_err("Set pkt_filter_enable error (%d)\n", err);
-
- /* Control the master mode */
- err = brcmf_fil_iovar_int_set(ifp, "pkt_filter_mode", master_mode);
- if (err)
- brcmf_err("Set pkt_filter_mode error (%d)\n", err);
-
-fail:
- kfree(arg_org);
-}
-
-static void brcmf_c_pktfilter_offload_set(struct brcmf_if *ifp, char *arg)
-{
- struct brcmf_pkt_filter_le *pkt_filter;
- unsigned long res;
- int buf_len;
- s32 err;
- u32 mask_size;
- u32 pattern_size;
- char *argv[8], *buf = NULL;
- int i = 0;
- char *arg_save = NULL, *arg_org = NULL;
-
- arg_save = kstrdup(arg, GFP_ATOMIC);
- if (!arg_save)
- goto fail;
-
- arg_org = arg_save;
-
- buf = kmalloc(PKTFILTER_BUF_SIZE, GFP_ATOMIC);
- if (!buf)
- goto fail;
-
- argv[i] = strsep(&arg_save, " ");
- while (argv[i]) {
- i++;
- if (i >= 8) {
- brcmf_err("Too many parameters\n");
- goto fail;
- }
- argv[i] = strsep(&arg_save, " ");
- }
-
- if (i != 6) {
- brcmf_err("Not enough args provided %d\n", i);
- goto fail;
- }
-
- pkt_filter = (struct brcmf_pkt_filter_le *)buf;
-
- /* Parse packet filter id. */
- pkt_filter->id = 0;
- if (!kstrtoul(argv[0], 0, &res))
- pkt_filter->id = cpu_to_le32((u32)res);
-
- /* Parse filter polarity. */
- pkt_filter->negate_match = 0;
- if (!kstrtoul(argv[1], 0, &res))
- pkt_filter->negate_match = cpu_to_le32((u32)res);
-
- /* Parse filter type. */
- pkt_filter->type = 0;
- if (!kstrtoul(argv[2], 0, &res))
- pkt_filter->type = cpu_to_le32((u32)res);
-
- /* Parse pattern filter offset. */
- pkt_filter->u.pattern.offset = 0;
- if (!kstrtoul(argv[3], 0, &res))
- pkt_filter->u.pattern.offset = cpu_to_le32((u32)res);
-
- /* Parse pattern filter mask. */
- mask_size = brcmf_c_pattern_atoh(argv[4],
- (char *)pkt_filter->u.pattern.mask_and_pattern);
-
- /* Parse pattern filter pattern. */
- pattern_size = brcmf_c_pattern_atoh(argv[5],
- (char *)&pkt_filter->u.pattern.mask_and_pattern[mask_size]);
-
- if (mask_size != pattern_size) {
- brcmf_err("Mask and pattern not the same size\n");
- goto fail;
- }
-
- pkt_filter->u.pattern.size_bytes = cpu_to_le32(mask_size);
- buf_len = offsetof(struct brcmf_pkt_filter_le,
- u.pattern.mask_and_pattern);
- buf_len += mask_size + pattern_size;
-
- err = brcmf_fil_iovar_data_set(ifp, "pkt_filter_add", pkt_filter,
- buf_len);
- if (err)
- brcmf_err("Set pkt_filter_add error (%d)\n", err);
-
-fail:
- kfree(arg_org);
-
- kfree(buf);
-}
-
-int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
-{
- s8 eventmask[BRCMF_EVENTING_MASK_LEN];
- u8 buf[BRCMF_DCMD_SMLEN];
- struct brcmf_join_pref_params join_pref_params[2];
- char *ptr;
- s32 err;
-
- /* retreive mac address */
- err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr,
- sizeof(ifp->mac_addr));
- if (err < 0) {
- brcmf_err("Retreiving cur_etheraddr failed, %d\n",
- err);
- goto done;
- }
- memcpy(ifp->drvr->mac, ifp->mac_addr, sizeof(ifp->drvr->mac));
-
- /* query for 'ver' to get version info from firmware */
- memset(buf, 0, sizeof(buf));
- strcpy(buf, "ver");
- err = brcmf_fil_iovar_data_get(ifp, "ver", buf, sizeof(buf));
- if (err < 0) {
- brcmf_err("Retreiving version information failed, %d\n",
- err);
- goto done;
- }
- ptr = (char *)buf;
- strsep(&ptr, "\n");
-
- /* Print fw version info */
- brcmf_err("Firmware version = %s\n", buf);
-
- /* locate firmware version number for ethtool */
- ptr = strrchr(buf, ' ') + 1;
- strlcpy(ifp->drvr->fwver, ptr, sizeof(ifp->drvr->fwver));
-
- /* set mpc */
- err = brcmf_fil_iovar_int_set(ifp, "mpc", 1);
- if (err) {
- brcmf_err("failed setting mpc\n");
- goto done;
- }
-
- /*
- * Setup timeout if Beacons are lost and roam is off to report
- * link down
- */
- err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout",
- BRCMF_DEFAULT_BCN_TIMEOUT);
- if (err) {
- brcmf_err("bcn_timeout error (%d)\n", err);
- goto done;
- }
-
- /* Enable/Disable build-in roaming to allowed ext supplicant to take
- * of romaing
- */
- err = brcmf_fil_iovar_int_set(ifp, "roam_off", 1);
- if (err) {
- brcmf_err("roam_off error (%d)\n", err);
- goto done;
- }
-
- /* Setup join_pref to select target by RSSI(with boost on 5GHz) */
- join_pref_params[0].type = BRCMF_JOIN_PREF_RSSI_DELTA;
- join_pref_params[0].len = 2;
- join_pref_params[0].rssi_gain = BRCMF_JOIN_PREF_RSSI_BOOST;
- join_pref_params[0].band = WLC_BAND_5G;
- join_pref_params[1].type = BRCMF_JOIN_PREF_RSSI;
- join_pref_params[1].len = 2;
- join_pref_params[1].rssi_gain = 0;
- join_pref_params[1].band = 0;
- err = brcmf_fil_iovar_data_set(ifp, "join_pref", join_pref_params,
- sizeof(join_pref_params));
- if (err)
- brcmf_err("Set join_pref error (%d)\n", err);
-
- /* Setup event_msgs, enable E_IF */
- err = brcmf_fil_iovar_data_get(ifp, "event_msgs", eventmask,
- BRCMF_EVENTING_MASK_LEN);
- if (err) {
- brcmf_err("Get event_msgs error (%d)\n", err);
- goto done;
- }
- setbit(eventmask, BRCMF_E_IF);
- err = brcmf_fil_iovar_data_set(ifp, "event_msgs", eventmask,
- BRCMF_EVENTING_MASK_LEN);
- if (err) {
- brcmf_err("Set event_msgs error (%d)\n", err);
- goto done;
- }
-
- /* Setup default scan channel time */
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
- BRCMF_DEFAULT_SCAN_CHANNEL_TIME);
- if (err) {
- brcmf_err("BRCMF_C_SET_SCAN_CHANNEL_TIME error (%d)\n",
- err);
- goto done;
- }
-
- /* Setup default scan unassoc time */
- err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
- BRCMF_DEFAULT_SCAN_UNASSOC_TIME);
- if (err) {
- brcmf_err("BRCMF_C_SET_SCAN_UNASSOC_TIME error (%d)\n",
- err);
- goto done;
- }
-
- /* Setup packet filter */
- brcmf_c_pktfilter_offload_set(ifp, BRCMF_DEFAULT_PACKET_FILTER);
- brcmf_c_pktfilter_offload_enable(ifp, BRCMF_DEFAULT_PACKET_FILTER,
- 0, true);
-
- /* do bus specific preinit here */
- err = brcmf_bus_preinit(ifp->drvr->bus_if);
-done:
- return err;
-}
-
-#ifdef CPTCFG_BRCM_TRACING
-void __brcmf_err(const char *func, const char *fmt, ...)
-{
- struct va_format vaf = {
- .fmt = fmt,
- };
- va_list args;
-
- va_start(args, fmt);
- vaf.va = &args;
- pr_err("%s: %pV", func, &vaf);
- trace_brcmf_err(func, &vaf);
- va_end(args);
-}
-#endif
-#if defined(CPTCFG_BRCM_TRACING) || defined(CPTCFG_BRCMDBG)
-void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...)
-{
- struct va_format vaf = {
- .fmt = fmt,
- };
- va_list args;
-
- va_start(args, fmt);
- vaf.va = &args;
- if (brcmf_msg_level & level)
- pr_debug("%s %pV", func, &vaf);
- trace_brcmf_dbg(level, func, &vaf);
- va_end(args);
-}
-#endif
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/brcm80211/brcmfmac/feature.c
index aed53ac..defb7a4 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c
@@ -17,18 +17,13 @@
#include <linux/netdevice.h>
#include <brcm_hw_ids.h>
-#include "dhd.h"
-#include "dhd_bus.h"
-#include "dhd_dbg.h"
+#include "core.h"
+#include "bus.h"
+#include "debug.h"
#include "fwil.h"
#include "feature.h"
/*
- * firmware error code received if iovar is unsupported.
- */
-#define EBRCMF_FEAT_UNSUPPORTED 23
-
-/*
* expand feature list to array of feature strings.
*/
#define BRCMF_FEAT_DEF(_f) \
@@ -102,6 +97,28 @@
}
}
+/**
+ * brcmf_feat_iovar_int_set() - determine feature through iovar set.
+ *
+ * @ifp: interface to query.
+ * @id: feature id.
+ * @name: iovar name.
+ */
+static void brcmf_feat_iovar_int_set(struct brcmf_if *ifp,
+ enum brcmf_feat_id id, char *name, u32 val)
+{
+ int err;
+
+ err = brcmf_fil_iovar_int_set(ifp, name, val);
+ if (err == 0) {
+ brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]);
+ ifp->drvr->feat_flags |= BIT(id);
+ } else {
+ brcmf_dbg(TRACE, "%s feature check failed: %d\n",
+ brcmf_feat_names[id], err);
+ }
+}
+
void brcmf_feat_attach(struct brcmf_pub *drvr)
{
struct brcmf_if *ifp = drvr->iflist[0];
@@ -109,6 +126,7 @@
brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan");
if (drvr->bus_if->wowl_supported)
brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl");
+ brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0);
/* set chip related quirks */
switch (drvr->bus_if->chip) {
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/brcm80211/brcmfmac/feature.h
index b9a796d..f5832e0 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/feature.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.h
@@ -22,6 +22,7 @@
* MCHAN: multi-channel for concurrent P2P.
*/
#define BRCMF_FEAT_LIST \
+ BRCMF_FEAT_DEF(MBSS) \
BRCMF_FEAT_DEF(MCHAN) \
BRCMF_FEAT_DEF(WOWL)
/*
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
index 8ea9f28..1ff787d 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c
@@ -20,7 +20,7 @@
#include <linux/firmware.h>
#include <linux/module.h>
-#include "dhd_dbg.h"
+#include "debug.h"
#include "firmware.h"
char brcmf_firmware_path[BRCMF_FW_PATH_LEN];
@@ -262,8 +262,7 @@
fail:
brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev));
- if (fwctx->code)
- release_firmware(fwctx->code);
+ release_firmware(fwctx->code);
device_release_driver(fwctx->dev);
kfree(fwctx);
}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c
index 1faa929..44f3a84 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c
@@ -19,9 +19,9 @@
#include <linux/etherdevice.h>
#include <brcmu_utils.h>
-#include "dhd.h"
-#include "dhd_dbg.h"
-#include "dhd_bus.h"
+#include "core.h"
+#include "debug.h"
+#include "bus.h"
#include "proto.h"
#include "flowring.h"
#include "msgbuf.h"
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c
index 44fc85f..ec62492 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c
@@ -18,8 +18,8 @@
#include "brcmu_wifi.h"
#include "brcmu_utils.h"
-#include "dhd.h"
-#include "dhd_dbg.h"
+#include "core.h"
+#include "debug.h"
#include "tracepoint.h"
#include "fwsignal.h"
#include "fweh.h"
@@ -221,10 +221,8 @@
err = brcmf_fweh_call_event_handler(ifp, emsg->event_code, emsg, data);
- if (ifp && ifevent->action == BRCMF_E_IF_DEL) {
- brcmf_fws_del_interface(ifp);
- brcmf_del_if(drvr, ifevent->bssidx);
- }
+ if (ifp && ifevent->action == BRCMF_E_IF_DEL)
+ brcmf_remove_interface(drvr, ifevent->bssidx);
}
/**
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c
index ded328f..03f2c40 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c
@@ -22,9 +22,9 @@
#include <linux/netdevice.h>
#include <brcmu_utils.h>
#include <brcmu_wifi.h>
-#include "dhd.h"
-#include "dhd_bus.h"
-#include "dhd_dbg.h"
+#include "core.h"
+#include "bus.h"
+#include "debug.h"
#include "tracepoint.h"
#include "fwil.h"
#include "proto.h"
@@ -32,6 +32,76 @@
#define MAX_HEX_DUMP_LEN 64
+#ifdef DEBUG
+static const char * const brcmf_fil_errstr[] = {
+ "BCME_OK",
+ "BCME_ERROR",
+ "BCME_BADARG",
+ "BCME_BADOPTION",
+ "BCME_NOTUP",
+ "BCME_NOTDOWN",
+ "BCME_NOTAP",
+ "BCME_NOTSTA",
+ "BCME_BADKEYIDX",
+ "BCME_RADIOOFF",
+ "BCME_NOTBANDLOCKED",
+ "BCME_NOCLK",
+ "BCME_BADRATESET",
+ "BCME_BADBAND",
+ "BCME_BUFTOOSHORT",
+ "BCME_BUFTOOLONG",
+ "BCME_BUSY",
+ "BCME_NOTASSOCIATED",
+ "BCME_BADSSIDLEN",
+ "BCME_OUTOFRANGECHAN",
+ "BCME_BADCHAN",
+ "BCME_BADADDR",
+ "BCME_NORESOURCE",
+ "BCME_UNSUPPORTED",
+ "BCME_BADLEN",
+ "BCME_NOTREADY",
+ "BCME_EPERM",
+ "BCME_NOMEM",
+ "BCME_ASSOCIATED",
+ "BCME_RANGE",
+ "BCME_NOTFOUND",
+ "BCME_WME_NOT_ENABLED",
+ "BCME_TSPEC_NOTFOUND",
+ "BCME_ACM_NOTSUPPORTED",
+ "BCME_NOT_WME_ASSOCIATION",
+ "BCME_SDIO_ERROR",
+ "BCME_DONGLE_DOWN",
+ "BCME_VERSION",
+ "BCME_TXFAIL",
+ "BCME_RXFAIL",
+ "BCME_NODEVICE",
+ "BCME_NMODE_DISABLED",
+ "BCME_NONRESIDENT",
+ "BCME_SCANREJECT",
+ "BCME_USAGE_ERROR",
+ "BCME_IOCTL_ERROR",
+ "BCME_SERIAL_PORT_ERR",
+ "BCME_DISABLED",
+ "BCME_DECERR",
+ "BCME_ENCERR",
+ "BCME_MICERR",
+ "BCME_REPLAY",
+ "BCME_IE_NOTFOUND",
+};
+
+static const char *brcmf_fil_get_errstr(u32 err)
+{
+ if (err >= ARRAY_SIZE(brcmf_fil_errstr))
+ return "(unknown)";
+
+ return brcmf_fil_errstr[err];
+}
+#else
+static const char *brcmf_fil_get_errstr(u32 err)
+{
+ return "";
+}
+#endif /* DEBUG */
static s32
brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set)
@@ -52,11 +122,11 @@
err = brcmf_proto_query_dcmd(drvr, ifp->ifidx, cmd, data, len);
if (err >= 0)
- err = 0;
- else
- brcmf_dbg(FIL, "Failed err=%d\n", err);
+ return 0;
- return err;
+ brcmf_dbg(FIL, "Failed: %s (%d)\n",
+ brcmf_fil_get_errstr((u32)(-err)), err);
+ return -EBADE;
}
s32
@@ -66,7 +136,7 @@
mutex_lock(&ifp->drvr->proto_block);
- brcmf_dbg(FIL, "cmd=%d, len=%d\n", cmd, len);
+ brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d\n", ifp->ifidx, cmd, len);
brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
@@ -84,7 +154,7 @@
mutex_lock(&ifp->drvr->proto_block);
err = brcmf_fil_cmd_data(ifp, cmd, data, len, false);
- brcmf_dbg(FIL, "cmd=%d, len=%d\n", cmd, len);
+ brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d\n", ifp->ifidx, cmd, len);
brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
@@ -101,7 +171,7 @@
__le32 data_le = cpu_to_le32(data);
mutex_lock(&ifp->drvr->proto_block);
- brcmf_dbg(FIL, "cmd=%d, value=%d\n", cmd, data);
+ brcmf_dbg(FIL, "ifidx=%d, cmd=%d, value=%d\n", ifp->ifidx, cmd, data);
err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), true);
mutex_unlock(&ifp->drvr->proto_block);
@@ -118,7 +188,7 @@
err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), false);
mutex_unlock(&ifp->drvr->proto_block);
*data = le32_to_cpu(data_le);
- brcmf_dbg(FIL, "cmd=%d, value=%d\n", cmd, *data);
+ brcmf_dbg(FIL, "ifidx=%d, cmd=%d, value=%d\n", ifp->ifidx, cmd, *data);
return err;
}
@@ -154,7 +224,7 @@
mutex_lock(&drvr->proto_block);
- brcmf_dbg(FIL, "name=%s, len=%d\n", name, len);
+ brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len);
brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
@@ -194,7 +264,7 @@
brcmf_err("Creating iovar failed\n");
}
- brcmf_dbg(FIL, "name=%s, len=%d\n", name, len);
+ brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len);
brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
@@ -277,7 +347,8 @@
mutex_lock(&drvr->proto_block);
- brcmf_dbg(FIL, "bssidx=%d, name=%s, len=%d\n", ifp->bssidx, name, len);
+ brcmf_dbg(FIL, "ifidx=%d, bssidx=%d, name=%s, len=%d\n", ifp->ifidx,
+ ifp->bssidx, name, len);
brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
@@ -316,7 +387,8 @@
err = -EPERM;
brcmf_err("Creating bsscfg failed\n");
}
- brcmf_dbg(FIL, "bssidx=%d, name=%s, len=%d\n", ifp->bssidx, name, len);
+ brcmf_dbg(FIL, "ifidx=%d, bssidx=%d, name=%s, len=%d\n", ifp->ifidx,
+ ifp->bssidx, name, len);
brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h
index 5ff5cd0..50891c0 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h
@@ -55,59 +55,63 @@
/* WOWL bits */
/* Wakeup on Magic packet: */
-#define WL_WOWL_MAGIC (1 << 0)
+#define BRCMF_WOWL_MAGIC (1 << 0)
/* Wakeup on Netpattern */
-#define WL_WOWL_NET (1 << 1)
+#define BRCMF_WOWL_NET (1 << 1)
/* Wakeup on loss-of-link due to Disassoc/Deauth: */
-#define WL_WOWL_DIS (1 << 2)
+#define BRCMF_WOWL_DIS (1 << 2)
/* Wakeup on retrograde TSF: */
-#define WL_WOWL_RETR (1 << 3)
+#define BRCMF_WOWL_RETR (1 << 3)
/* Wakeup on loss of beacon: */
-#define WL_WOWL_BCN (1 << 4)
+#define BRCMF_WOWL_BCN (1 << 4)
/* Wakeup after test: */
-#define WL_WOWL_TST (1 << 5)
+#define BRCMF_WOWL_TST (1 << 5)
/* Wakeup after PTK refresh: */
-#define WL_WOWL_M1 (1 << 6)
+#define BRCMF_WOWL_M1 (1 << 6)
/* Wakeup after receipt of EAP-Identity Req: */
-#define WL_WOWL_EAPID (1 << 7)
+#define BRCMF_WOWL_EAPID (1 << 7)
/* Wakeind via PME(0) or GPIO(1): */
-#define WL_WOWL_PME_GPIO (1 << 8)
+#define BRCMF_WOWL_PME_GPIO (1 << 8)
/* need tkip phase 1 key to be updated by the driver: */
-#define WL_WOWL_NEEDTKIP1 (1 << 9)
+#define BRCMF_WOWL_NEEDTKIP1 (1 << 9)
/* enable wakeup if GTK fails: */
-#define WL_WOWL_GTK_FAILURE (1 << 10)
+#define BRCMF_WOWL_GTK_FAILURE (1 << 10)
/* support extended magic packets: */
-#define WL_WOWL_EXTMAGPAT (1 << 11)
+#define BRCMF_WOWL_EXTMAGPAT (1 << 11)
/* support ARP/NS/keepalive offloading: */
-#define WL_WOWL_ARPOFFLOAD (1 << 12)
+#define BRCMF_WOWL_ARPOFFLOAD (1 << 12)
/* read protocol version for EAPOL frames: */
-#define WL_WOWL_WPA2 (1 << 13)
+#define BRCMF_WOWL_WPA2 (1 << 13)
/* If the bit is set, use key rotaton: */
-#define WL_WOWL_KEYROT (1 << 14)
+#define BRCMF_WOWL_KEYROT (1 << 14)
/* If the bit is set, frm received was bcast frame: */
-#define WL_WOWL_BCAST (1 << 15)
+#define BRCMF_WOWL_BCAST (1 << 15)
/* If the bit is set, scan offload is enabled: */
-#define WL_WOWL_SCANOL (1 << 16)
+#define BRCMF_WOWL_SCANOL (1 << 16)
/* Wakeup on tcpkeep alive timeout: */
-#define WL_WOWL_TCPKEEP_TIME (1 << 17)
+#define BRCMF_WOWL_TCPKEEP_TIME (1 << 17)
/* Wakeup on mDNS Conflict Resolution: */
-#define WL_WOWL_MDNS_CONFLICT (1 << 18)
+#define BRCMF_WOWL_MDNS_CONFLICT (1 << 18)
/* Wakeup on mDNS Service Connect: */
-#define WL_WOWL_MDNS_SERVICE (1 << 19)
+#define BRCMF_WOWL_MDNS_SERVICE (1 << 19)
/* tcp keepalive got data: */
-#define WL_WOWL_TCPKEEP_DATA (1 << 20)
+#define BRCMF_WOWL_TCPKEEP_DATA (1 << 20)
/* Firmware died in wowl mode: */
-#define WL_WOWL_FW_HALT (1 << 21)
+#define BRCMF_WOWL_FW_HALT (1 << 21)
/* Enable detection of radio button changes: */
-#define WL_WOWL_ENAB_HWRADIO (1 << 22)
+#define BRCMF_WOWL_ENAB_HWRADIO (1 << 22)
/* Offloads detected MIC failure(s): */
-#define WL_WOWL_MIC_FAIL (1 << 23)
+#define BRCMF_WOWL_MIC_FAIL (1 << 23)
/* Wakeup in Unassociated state (Net/Magic Pattern): */
-#define WL_WOWL_UNASSOC (1 << 24)
+#define BRCMF_WOWL_UNASSOC (1 << 24)
/* Wakeup if received matched secured pattern: */
-#define WL_WOWL_SECURE (1 << 25)
+#define BRCMF_WOWL_SECURE (1 << 25)
/* Link Down indication in WoWL mode: */
-#define WL_WOWL_LINKDOWN (1 << 31)
+#define BRCMF_WOWL_LINKDOWN (1 << 31)
+
+#define BRCMF_WOWL_MAXPATTERNS 8
+#define BRCMF_WOWL_MAXPATTERNSIZE 128
+
/* join preference types for join_pref iovar */
enum brcmf_join_pref_types {
@@ -124,6 +128,12 @@
BRCMF_FIL_P2P_IF_DEV,
};
+enum brcmf_wowl_pattern_type {
+ BRCMF_WOWL_PATTERN_TYPE_BITMAP = 0,
+ BRCMF_WOWL_PATTERN_TYPE_ARP,
+ BRCMF_WOWL_PATTERN_TYPE_NA
+};
+
struct brcmf_fil_p2p_if_le {
u8 addr[ETH_ALEN];
__le16 type;
@@ -484,4 +494,35 @@
__be32 rate;
};
+/**
+ * struct brcmf_fil_wowl_pattern_le - wowl pattern configuration struct.
+ *
+ * @cmd: "add", "del" or "clr".
+ * @masksize: Size of the mask in #of bytes
+ * @offset: Pattern byte offset in packet
+ * @patternoffset: Offset of start of pattern. Starting from field masksize.
+ * @patternsize: Size of the pattern itself in #of bytes
+ * @id: id
+ * @reasonsize: Size of the wakeup reason code
+ * @type: Type of pattern (enum brcmf_wowl_pattern_type)
+ */
+struct brcmf_fil_wowl_pattern_le {
+ u8 cmd[4];
+ __le32 masksize;
+ __le32 offset;
+ __le32 patternoffset;
+ __le32 patternsize;
+ __le32 id;
+ __le32 reasonsize;
+ __le32 type;
+ /* u8 mask[] - Mask follows the structure above */
+ /* u8 pattern[] - Pattern follows the mask is at 'patternoffset' */
+};
+
+struct brcmf_mbss_ssid_le {
+ __le32 bsscfgidx;
+ __le32 SSID_len;
+ unsigned char SSID[32];
+};
+
#endif /* FWIL_TYPES_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
index 183f08d..f0dda0e 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
@@ -26,15 +26,15 @@
#include <brcmu_utils.h>
#include <brcmu_wifi.h>
-#include "dhd.h"
-#include "dhd_dbg.h"
-#include "dhd_bus.h"
+#include "core.h"
+#include "debug.h"
+#include "bus.h"
#include "fwil.h"
#include "fwil_types.h"
#include "fweh.h"
#include "fwsignal.h"
#include "p2p.h"
-#include "wl_cfg80211.h"
+#include "cfg80211.h"
#include "proto.h"
/**
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
index 11cc051..456944a 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
@@ -24,13 +24,13 @@
#include <brcmu_utils.h>
#include <brcmu_wifi.h>
-#include "dhd.h"
-#include "dhd_dbg.h"
+#include "core.h"
+#include "debug.h"
#include "proto.h"
#include "msgbuf.h"
#include "commonring.h"
#include "flowring.h"
-#include "dhd_bus.h"
+#include "bus.h"
#include "tracepoint.h"
@@ -518,8 +518,7 @@
memcpy(buf, skb->data, (len < msgbuf->ioctl_resp_ret_len) ?
len : msgbuf->ioctl_resp_ret_len);
}
- if (skb)
- brcmu_pkt_buf_free_skb(skb);
+ brcmu_pkt_buf_free_skb(skb);
return msgbuf->ioctl_resp_status;
}
@@ -1081,8 +1080,17 @@
{
struct brcmf_if *ifp;
+ /* The ifidx is the idx to map to matching netdev/ifp. When receiving
+ * events this is easy because it contains the bssidx which maps
+ * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd.
+ * bssidx 1 is used for p2p0 and no data can be received or
+ * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0
+ */
+ if (ifidx)
+ (ifidx)++;
ifp = msgbuf->drvr->iflist[ifidx];
if (!ifp || !ifp->ndev) {
+ brcmf_err("Received pkt for invalid ifidx %d\n", ifidx);
brcmu_pkt_buf_free_skb(skb);
return;
}
@@ -1355,6 +1363,7 @@
}
INIT_WORK(&msgbuf->txflow_work, brcmf_msgbuf_txflow_worker);
count = BITS_TO_LONGS(if_msgbuf->nrof_flowrings);
+ count = count * sizeof(unsigned long);
msgbuf->flow_map = kzalloc(count, GFP_KERNEL);
if (!msgbuf->flow_map)
goto fail;
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/of.c b/drivers/net/wireless/brcm80211/brcmfmac/of.c
index f05f527..c824570 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/of.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/of.c
@@ -21,8 +21,8 @@
#include <linux/mmc/sdio_func.h>
#include <defs.h>
-#include "dhd_dbg.h"
-#include "sdio_host.h"
+#include "debug.h"
+#include "sdio.h"
void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev)
{
@@ -40,8 +40,8 @@
return;
irq = irq_of_parse_and_map(np, 0);
- if (irq < 0) {
- brcmf_err("interrupt could not be mapped: err=%d\n", irq);
+ if (!irq) {
+ brcmf_err("interrupt could not be mapped\n");
devm_kfree(dev, sdiodev->pdata);
return;
}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
index d54c58a..effb48e 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
@@ -21,12 +21,12 @@
#include <brcmu_wifi.h>
#include <brcmu_utils.h>
#include <defs.h>
-#include <dhd.h>
-#include <dhd_dbg.h>
+#include "core.h"
+#include "debug.h"
#include "fwil.h"
#include "fwil_types.h"
#include "p2p.h"
-#include "wl_cfg80211.h"
+#include "cfg80211.h"
/* parameters used for p2p escan */
#define P2PAPI_SCAN_NPROBES 1
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
index 8c0632e..905991f 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c
@@ -19,10 +19,10 @@
#include <linux/pci.h>
#include <linux/vmalloc.h>
#include <linux/delay.h>
-#include <linux/unaligned/access_ok.h>
#include <linux/interrupt.h>
#include <linux/bcma/bcma.h>
#include <linux/sched.h>
+#include <asm/unaligned.h>
#include <soc.h>
#include <chipcommon.h>
@@ -30,8 +30,8 @@
#include <brcmu_wifi.h>
#include <brcm_hw_ids.h>
-#include "dhd_dbg.h"
-#include "dhd_bus.h"
+#include "debug.h"
+#include "bus.h"
#include "commonring.h"
#include "msgbuf.h"
#include "pcie.h"
@@ -798,12 +798,14 @@
brcmf_dbg(PCIE, "Enter\n");
/* is it a v1 or v2 implementation */
devinfo->irq_requested = false;
+ pci_enable_msi(pdev);
if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) {
if (request_threaded_irq(pdev->irq,
brcmf_pcie_quick_check_isr_v1,
brcmf_pcie_isr_thread_v1,
IRQF_SHARED, "brcmf_pcie_intr",
devinfo)) {
+ pci_disable_msi(pdev);
brcmf_err("Failed to request IRQ %d\n", pdev->irq);
return -EIO;
}
@@ -813,6 +815,7 @@
brcmf_pcie_isr_thread_v2,
IRQF_SHARED, "brcmf_pcie_intr",
devinfo)) {
+ pci_disable_msi(pdev);
brcmf_err("Failed to request IRQ %d\n", pdev->irq);
return -EIO;
}
@@ -839,6 +842,7 @@
return;
devinfo->irq_requested = false;
free_irq(pdev->irq, devinfo);
+ pci_disable_msi(pdev);
msleep(50);
count = 0;
@@ -1857,6 +1861,8 @@
BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID),
BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID),
BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_2G_DEVICE_ID),
+ BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_5G_DEVICE_ID),
{ /* end: all zeroes */ }
};
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/proto.c b/drivers/net/wireless/brcm80211/brcmfmac/proto.c
index 62b9407..26b68c3 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/proto.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/proto.c
@@ -20,9 +20,9 @@
#include <linux/netdevice.h>
#include <brcmu_wifi.h>
-#include "dhd.h"
-#include "dhd_bus.h"
-#include "dhd_dbg.h"
+#include "core.h"
+#include "bus.h"
+#include "debug.h"
#include "proto.h"
#include "bcdc.h"
#include "msgbuf.h"
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
similarity index 98%
rename from drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
rename to drivers/net/wireless/brcm80211/brcmfmac/sdio.c
index f55f625..0b0d51a 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
@@ -40,7 +40,7 @@
#include <brcmu_utils.h>
#include <brcm_hw_ids.h>
#include <soc.h>
-#include "sdio_host.h"
+#include "sdio.h"
#include "chip.h"
#include "firmware.h"
@@ -96,8 +96,8 @@
#endif /* DEBUG */
#include <chipcommon.h>
-#include "dhd_bus.h"
-#include "dhd_dbg.h"
+#include "bus.h"
+#include "debug.h"
#include "tracepoint.h"
#define TXQLEN 2048 /* bulk tx queue length */
@@ -670,7 +670,6 @@
struct brcmf_sdio_dev *sdiodev)
{
int i;
- uint fw_len, nv_len;
char end;
for (i = 0; i < ARRAY_SIZE(brcmf_fwname_data); i++) {
@@ -684,25 +683,25 @@
return -ENODEV;
}
- fw_len = sizeof(sdiodev->fw_name) - 1;
- nv_len = sizeof(sdiodev->nvram_name) - 1;
/* check if firmware path is provided by module parameter */
if (brcmf_firmware_path[0] != '\0') {
- strncpy(sdiodev->fw_name, brcmf_firmware_path, fw_len);
- strncpy(sdiodev->nvram_name, brcmf_firmware_path, nv_len);
- fw_len -= strlen(sdiodev->fw_name);
- nv_len -= strlen(sdiodev->nvram_name);
+ strlcpy(sdiodev->fw_name, brcmf_firmware_path,
+ sizeof(sdiodev->fw_name));
+ strlcpy(sdiodev->nvram_name, brcmf_firmware_path,
+ sizeof(sdiodev->nvram_name));
end = brcmf_firmware_path[strlen(brcmf_firmware_path) - 1];
if (end != '/') {
- strncat(sdiodev->fw_name, "/", fw_len);
- strncat(sdiodev->nvram_name, "/", nv_len);
- fw_len--;
- nv_len--;
+ strlcat(sdiodev->fw_name, "/",
+ sizeof(sdiodev->fw_name));
+ strlcat(sdiodev->nvram_name, "/",
+ sizeof(sdiodev->nvram_name));
}
}
- strncat(sdiodev->fw_name, brcmf_fwname_data[i].bin, fw_len);
- strncat(sdiodev->nvram_name, brcmf_fwname_data[i].nv, nv_len);
+ strlcat(sdiodev->fw_name, brcmf_fwname_data[i].bin,
+ sizeof(sdiodev->fw_name));
+ strlcat(sdiodev->nvram_name, brcmf_fwname_data[i].nv,
+ sizeof(sdiodev->nvram_name));
return 0;
}
@@ -2763,6 +2762,48 @@
return &bus->txq;
}
+static bool brcmf_sdio_prec_enq(struct pktq *q, struct sk_buff *pkt, int prec)
+{
+ struct sk_buff *p;
+ int eprec = -1; /* precedence to evict from */
+
+ /* Fast case, precedence queue is not full and we are also not
+ * exceeding total queue length
+ */
+ if (!pktq_pfull(q, prec) && !pktq_full(q)) {
+ brcmu_pktq_penq(q, prec, pkt);
+ return true;
+ }
+
+ /* Determine precedence from which to evict packet, if any */
+ if (pktq_pfull(q, prec)) {
+ eprec = prec;
+ } else if (pktq_full(q)) {
+ p = brcmu_pktq_peek_tail(q, &eprec);
+ if (eprec > prec)
+ return false;
+ }
+
+ /* Evict if needed */
+ if (eprec >= 0) {
+ /* Detect queueing to unconfigured precedence */
+ if (eprec == prec)
+ return false; /* refuse newer (incoming) packet */
+ /* Evict packet according to discard policy */
+ p = brcmu_pktq_pdeq_tail(q, eprec);
+ if (p == NULL)
+ brcmf_err("brcmu_pktq_pdeq_tail() failed\n");
+ brcmu_pkt_buf_free_skb(p);
+ }
+
+ /* Enqueue */
+ p = brcmu_pktq_penq(q, prec, pkt);
+ if (p == NULL)
+ brcmf_err("brcmu_pktq_penq() failed\n");
+
+ return p != NULL;
+}
+
static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt)
{
int ret = -EBADE;
@@ -2788,7 +2829,7 @@
spin_lock_bh(&bus->txq_lock);
/* reset bus_flags in packet cb */
*(u16 *)(pkt->cb) = 0;
- if (!brcmf_c_prec_enq(bus->sdiodev->dev, &bus->txq, pkt, prec)) {
+ if (!brcmf_sdio_prec_enq(&bus->txq, pkt, prec)) {
skb_pull(pkt, bus->tx_hdrlen);
brcmf_err("out of bus->txq !!!\n");
ret = -ENOSR;
@@ -2798,7 +2839,7 @@
if (pktq_len(&bus->txq) >= TXHI) {
bus->txoff = true;
- brcmf_txflowblock(bus->sdiodev->dev, true);
+ brcmf_txflowblock(dev, true);
}
spin_unlock_bh(&bus->txq_lock);
@@ -3949,6 +3990,7 @@
.txctl = brcmf_sdio_bus_txctl,
.rxctl = brcmf_sdio_bus_rxctl,
.gettxq = brcmf_sdio_bus_gettxq,
+ .wowl_config = brcmf_sdio_wowl_config
};
static void brcmf_sdio_firmware_callback(struct device *dev,
@@ -4075,7 +4117,7 @@
/* platform specific configuration:
* alignments must be at least 4 bytes for ADMA
- */
+ */
bus->head_align = ALIGNMENT;
bus->sgentry_align = ALIGNMENT;
if (sdiodev->pdata) {
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio.h
similarity index 98%
rename from drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
rename to drivers/net/wireless/brcm80211/brcmfmac/sdio.h
index f2d06ca..8eb4262 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.h
@@ -14,8 +14,8 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifndef _BRCM_SDH_H_
-#define _BRCM_SDH_H_
+#ifndef BRCMFMAC_SDIO_H
+#define BRCMFMAC_SDIO_H
#include <linux/skbuff.h>
#include <linux/firmware.h>
@@ -186,6 +186,7 @@
struct sg_table sgtable;
char fw_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN];
char nvram_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN];
+ bool wowl_enabled;
};
/* sdio core registers */
@@ -334,5 +335,6 @@
void brcmf_sdio_isr(struct brcmf_sdio *bus);
void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick);
+void brcmf_sdio_wowl_config(struct device *dev, bool enabled);
-#endif /* _BRCM_SDH_H_ */
+#endif /* BRCMFMAC_SDIO_H */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.c b/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.c
index b505db4..a10f35c 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.c
@@ -19,4 +19,19 @@
#ifndef __CHECKER__
#define CREATE_TRACE_POINTS
#include "tracepoint.h"
+
+void __brcmf_err(const char *func, const char *fmt, ...)
+{
+ struct va_format vaf = {
+ .fmt = fmt,
+ };
+ va_list args;
+
+ va_start(args, fmt);
+ vaf.va = &args;
+ pr_err("%s: %pV", func, &vaf);
+ trace_brcmf_err(func, &vaf);
+ va_end(args);
+}
+
#endif
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c
index 99b03a1..a662707 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c
@@ -23,13 +23,12 @@
#include <brcmu_utils.h>
#include <brcm_hw_ids.h>
#include <brcmu_wifi.h>
-#include <dhd_bus.h>
-#include <dhd_dbg.h>
-
+#include "bus.h"
+#include "debug.h"
#include "firmware.h"
-#include "usb_rdl.h"
#include "usb.h"
+
#define IOCTL_RESP_TIMEOUT 2000
#define BRCMF_USB_RESET_GETVER_SPINWAIT 100 /* in unit of ms */
@@ -49,6 +48,71 @@
#define BRCMF_USB_43242_FW_NAME "brcm/brcmfmac43242a.bin"
#define BRCMF_USB_43569_FW_NAME "brcm/brcmfmac43569.bin"
+#define TRX_MAGIC 0x30524448 /* "HDR0" */
+#define TRX_MAX_OFFSET 3 /* Max number of file offsets */
+#define TRX_UNCOMP_IMAGE 0x20 /* Trx holds uncompressed img */
+#define TRX_RDL_CHUNK 1500 /* size of each dl transfer */
+#define TRX_OFFSETS_DLFWLEN_IDX 0
+
+/* Control messages: bRequest values */
+#define DL_GETSTATE 0 /* returns the rdl_state_t struct */
+#define DL_CHECK_CRC 1 /* currently unused */
+#define DL_GO 2 /* execute downloaded image */
+#define DL_START 3 /* initialize dl state */
+#define DL_REBOOT 4 /* reboot the device in 2 seconds */
+#define DL_GETVER 5 /* returns the bootrom_id_t struct */
+#define DL_GO_PROTECTED 6 /* execute the downloaded code and set reset
+ * event to occur in 2 seconds. It is the
+ * responsibility of the downloaded code to
+ * clear this event
+ */
+#define DL_EXEC 7 /* jump to a supplied address */
+#define DL_RESETCFG 8 /* To support single enum on dongle
+ * - Not used by bootloader
+ */
+#define DL_DEFER_RESP_OK 9 /* Potentially defer the response to setup
+ * if resp unavailable
+ */
+
+/* states */
+#define DL_WAITING 0 /* waiting to rx first pkt */
+#define DL_READY 1 /* hdr was good, waiting for more of the
+ * compressed image
+ */
+#define DL_BAD_HDR 2 /* hdr was corrupted */
+#define DL_BAD_CRC 3 /* compressed image was corrupted */
+#define DL_RUNNABLE 4 /* download was successful,waiting for go cmd */
+#define DL_START_FAIL 5 /* failed to initialize correctly */
+#define DL_NVRAM_TOOBIG 6 /* host specified nvram data exceeds DL_NVRAM
+ * value
+ */
+#define DL_IMAGE_TOOBIG 7 /* firmware image too big */
+
+
+struct trx_header_le {
+ __le32 magic; /* "HDR0" */
+ __le32 len; /* Length of file including header */
+ __le32 crc32; /* CRC from flag_version to end of file */
+ __le32 flag_version; /* 0:15 flags, 16:31 version */
+ __le32 offsets[TRX_MAX_OFFSET]; /* Offsets of partitions from start of
+ * header
+ */
+};
+
+struct rdl_state_le {
+ __le32 state;
+ __le32 bytes;
+};
+
+struct bootrom_id_le {
+ __le32 chip; /* Chip id */
+ __le32 chiprev; /* Chip rev */
+ __le32 ramsize; /* Size of RAM */
+ __le32 remapbase; /* Current remap base address */
+ __le32 boardtype; /* Type of board */
+ __le32 boardrev; /* Board revision */
+};
+
struct brcmf_usb_image {
struct list_head list;
s8 *fwname;
@@ -93,6 +157,8 @@
u8 ifnum;
struct urb *bulk_urb; /* used for FW download */
+
+ bool wowl_enabled;
};
static void brcmf_usb_rx_refill(struct brcmf_usbdev_info *devinfo,
@@ -600,6 +666,16 @@
return 0;
}
+static void brcmf_cancel_all_urbs(struct brcmf_usbdev_info *devinfo)
+{
+ if (devinfo->ctl_urb)
+ usb_kill_urb(devinfo->ctl_urb);
+ if (devinfo->bulk_urb)
+ usb_kill_urb(devinfo->bulk_urb);
+ brcmf_usb_free_q(&devinfo->tx_postq, true);
+ brcmf_usb_free_q(&devinfo->rx_postq, true);
+}
+
static void brcmf_usb_down(struct device *dev)
{
struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
@@ -613,14 +689,7 @@
brcmf_usb_state_change(devinfo, BRCMFMAC_USB_STATE_DOWN);
- if (devinfo->ctl_urb)
- usb_kill_urb(devinfo->ctl_urb);
-
- if (devinfo->bulk_urb)
- usb_kill_urb(devinfo->bulk_urb);
- brcmf_usb_free_q(&devinfo->tx_postq, true);
-
- brcmf_usb_free_q(&devinfo->rx_postq, true);
+ brcmf_cancel_all_urbs(devinfo);
}
static void
@@ -669,10 +738,12 @@
goto finalize;
}
- if (!brcmf_usb_ioctl_resp_wait(devinfo))
+ if (!brcmf_usb_ioctl_resp_wait(devinfo)) {
+ usb_kill_urb(devinfo->ctl_urb);
ret = -ETIMEDOUT;
- else
+ } else {
memcpy(buffer, tmpbuf, buflen);
+ }
finalize:
kfree(tmpbuf);
@@ -783,7 +854,7 @@
brcmf_dbg(USB, "Enter, fw %p, len %d\n", fw, fwlen);
- bulkchunk = kmalloc(RDL_CHUNK, GFP_ATOMIC);
+ bulkchunk = kmalloc(TRX_RDL_CHUNK, GFP_ATOMIC);
if (bulkchunk == NULL) {
err = -ENOMEM;
goto fail;
@@ -810,10 +881,10 @@
/* Wait until the usb device reports it received all
* the bytes we sent */
if ((rdlbytes == sent) && (rdlbytes != dllen)) {
- if ((dllen-sent) < RDL_CHUNK)
+ if ((dllen-sent) < TRX_RDL_CHUNK)
sendlen = dllen-sent;
else
- sendlen = RDL_CHUNK;
+ sendlen = TRX_RDL_CHUNK;
/* simply avoid having to send a ZLP by ensuring we
* never have an even
@@ -978,21 +1049,6 @@
kfree(devinfo->rx_reqs);
}
-#define TRX_MAGIC 0x30524448 /* "HDR0" */
-#define TRX_VERSION 1 /* Version 1 */
-#define TRX_MAX_LEN 0x3B0000 /* Max length */
-#define TRX_NO_HEADER 1 /* Do not write TRX header */
-#define TRX_MAX_OFFSET 3 /* Max number of individual files */
-#define TRX_UNCOMP_IMAGE 0x20 /* Trx contains uncompressed image */
-
-struct trx_header_le {
- __le32 magic; /* "HDR0" */
- __le32 len; /* Length of file including header */
- __le32 crc32; /* CRC from flag_version to end of file */
- __le32 flag_version; /* 0:15 flags, 16:31 version */
- __le32 offsets[TRX_MAX_OFFSET]; /* Offsets of partitions from start of
- * header */
-};
static int check_file(const u8 *headers)
{
@@ -1094,11 +1150,24 @@
return NULL;
}
+static void brcmf_usb_wowl_config(struct device *dev, bool enabled)
+{
+ struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
+
+ brcmf_dbg(USB, "Configuring WOWL, enabled=%d\n", enabled);
+ devinfo->wowl_enabled = enabled;
+ if (enabled)
+ device_set_wakeup_enable(devinfo->dev, true);
+ else
+ device_set_wakeup_enable(devinfo->dev, false);
+}
+
static struct brcmf_bus_ops brcmf_usb_bus_ops = {
.txdata = brcmf_usb_tx,
.stop = brcmf_usb_down,
.txctl = brcmf_usb_tx_ctlpkt,
.rxctl = brcmf_usb_rx_ctlpkt,
+ .wowl_config = brcmf_usb_wowl_config,
};
static int brcmf_usb_bus_setup(struct brcmf_usbdev_info *devinfo)
@@ -1186,6 +1255,9 @@
bus->ops = &brcmf_usb_bus_ops;
bus->proto_type = BRCMF_PROTO_BCDC;
bus->always_use_fws_queue = true;
+#ifdef CONFIG_PM
+ bus->wowl_supported = true;
+#endif
if (!brcmf_usb_dlneeded(devinfo)) {
ret = brcmf_usb_bus_setup(devinfo);
@@ -1339,7 +1411,10 @@
brcmf_dbg(USB, "Enter\n");
devinfo->bus_pub.state = BRCMFMAC_USB_STATE_SLEEP;
- brcmf_detach(&usb->dev);
+ if (devinfo->wowl_enabled)
+ brcmf_cancel_all_urbs(devinfo);
+ else
+ brcmf_detach(&usb->dev);
return 0;
}
@@ -1352,7 +1427,12 @@
struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev);
brcmf_dbg(USB, "Enter\n");
- return brcmf_usb_bus_setup(devinfo);
+ if (!devinfo->wowl_enabled)
+ return brcmf_usb_bus_setup(devinfo);
+
+ devinfo->bus_pub.state = BRCMFMAC_USB_STATE_UP;
+ brcmf_usb_rx_fill_all(devinfo);
+ return 0;
}
static int brcmf_usb_reset_resume(struct usb_interface *intf)
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb_rdl.h b/drivers/net/wireless/brcm80211/brcmfmac/usb_rdl.h
deleted file mode 100644
index 0a35c51..0000000
--- a/drivers/net/wireless/brcm80211/brcmfmac/usb_rdl.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (c) 2011 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _USB_RDL_H
-#define _USB_RDL_H
-
-/* Control messages: bRequest values */
-#define DL_GETSTATE 0 /* returns the rdl_state_t struct */
-#define DL_CHECK_CRC 1 /* currently unused */
-#define DL_GO 2 /* execute downloaded image */
-#define DL_START 3 /* initialize dl state */
-#define DL_REBOOT 4 /* reboot the device in 2 seconds */
-#define DL_GETVER 5 /* returns the bootrom_id_t struct */
-#define DL_GO_PROTECTED 6 /* execute the downloaded code and set reset
- * event to occur in 2 seconds. It is the
- * responsibility of the downloaded code to
- * clear this event
- */
-#define DL_EXEC 7 /* jump to a supplied address */
-#define DL_RESETCFG 8 /* To support single enum on dongle
- * - Not used by bootloader
- */
-#define DL_DEFER_RESP_OK 9 /* Potentially defer the response to setup
- * if resp unavailable
- */
-
-/* states */
-#define DL_WAITING 0 /* waiting to rx first pkt */
-#define DL_READY 1 /* hdr was good, waiting for more of the
- * compressed image */
-#define DL_BAD_HDR 2 /* hdr was corrupted */
-#define DL_BAD_CRC 3 /* compressed image was corrupted */
-#define DL_RUNNABLE 4 /* download was successful,waiting for go cmd */
-#define DL_START_FAIL 5 /* failed to initialize correctly */
-#define DL_NVRAM_TOOBIG 6 /* host specified nvram data exceeds DL_NVRAM
- * value */
-#define DL_IMAGE_TOOBIG 7 /* download image too big (exceeds DATA_START
- * for rdl) */
-
-struct rdl_state_le {
- __le32 state;
- __le32 bytes;
-};
-
-struct bootrom_id_le {
- __le32 chip; /* Chip id */
- __le32 chiprev; /* Chip rev */
- __le32 ramsize; /* Size of RAM */
- __le32 remapbase; /* Current remap base address */
- __le32 boardtype; /* Type of board */
- __le32 boardrev; /* Board revision */
-};
-
-#define RDL_CHUNK 1500 /* size of each dl transfer */
-
-#define TRX_OFFSETS_DLFWLEN_IDX 0
-#define TRX_OFFSETS_JUMPTO_IDX 1
-#define TRX_OFFSETS_NVM_LEN_IDX 2
-
-#define TRX_OFFSETS_DLBASE_IDX 0
-
-#endif /* _USB_RDL_H */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c
index 5960d82..50cdf70 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c
@@ -20,10 +20,10 @@
#include <brcmu_wifi.h>
#include "fwil_types.h"
-#include "dhd.h"
+#include "core.h"
#include "p2p.h"
-#include "dhd_dbg.h"
-#include "wl_cfg80211.h"
+#include "debug.h"
+#include "cfg80211.h"
#include "vendor.h"
#include "fwil.h"
@@ -31,8 +31,8 @@
struct wireless_dev *wdev,
const void *data, int len)
{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct net_device *ndev = cfg_to_ndev(cfg);
+ struct brcmf_cfg80211_vif *vif;
+ struct brcmf_if *ifp;
const struct brcmf_vndr_dcmd_hdr *cmdhdr = data;
struct sk_buff *reply;
int ret, payload, ret_len;
@@ -42,6 +42,9 @@
brcmf_dbg(TRACE, "cmd %x set %d len %d\n", cmdhdr->cmd, cmdhdr->set,
cmdhdr->len);
+ vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
+ ifp = vif->ifp;
+
len -= sizeof(struct brcmf_vndr_dcmd_hdr);
ret_len = cmdhdr->len;
if (ret_len > 0 || len > 0) {
@@ -63,11 +66,11 @@
}
if (cmdhdr->set)
- ret = brcmf_fil_cmd_data_set(netdev_priv(ndev), cmdhdr->cmd,
- dcmd_buf, ret_len);
+ ret = brcmf_fil_cmd_data_set(ifp, cmdhdr->cmd, dcmd_buf,
+ ret_len);
else
- ret = brcmf_fil_cmd_data_get(netdev_priv(ndev), cmdhdr->cmd,
- dcmd_buf, ret_len);
+ ret = brcmf_fil_cmd_data_get(ifp, cmdhdr->cmd, dcmd_buf,
+ ret_len);
if (ret != 0)
goto exit;
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/debug.c b/drivers/net/wireless/brcm80211/brcmsmac/debug.c
index 323c9de..d382d94 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/debug.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/debug.c
@@ -30,6 +30,7 @@
#include "main.h"
#include "debug.h"
#include "brcms_trace_events.h"
+#include "phy/phy_int.h"
static struct dentry *root_folder;
@@ -71,48 +72,161 @@
}
static
-ssize_t brcms_debugfs_hardware_read(struct file *f, char __user *data,
- size_t count, loff_t *ppos)
+int brcms_debugfs_hardware_read(struct seq_file *s, void *data)
{
- char buf[128];
- int res;
- struct brcms_pub *drvr = f->private_data;
+ struct brcms_pub *drvr = s->private;
+ struct brcms_hardware *hw = drvr->wlc->hw;
+ struct bcma_device *core = hw->d11core;
+ struct bcma_bus *bus = core->bus;
+ char boardrev[10];
- /* only allow read from start */
- if (*ppos > 0)
- return 0;
-
- res = scnprintf(buf, sizeof(buf),
- "board vendor: %x\n"
- "board type: %x\n"
- "board revision: %x\n"
- "board flags: %x\n"
- "board flags2: %x\n"
- "firmware revision: %x\n",
- drvr->wlc->hw->d11core->bus->boardinfo.vendor,
- drvr->wlc->hw->d11core->bus->boardinfo.type,
- drvr->wlc->hw->boardrev,
- drvr->wlc->hw->boardflags,
- drvr->wlc->hw->boardflags2,
- drvr->wlc->ucode_rev
- );
-
- return simple_read_from_buffer(data, count, ppos, buf, res);
+ seq_printf(s, "chipnum 0x%x\n"
+ "chiprev 0x%x\n"
+ "chippackage 0x%x\n"
+ "corerev 0x%x\n"
+ "boardid 0x%x\n"
+ "boardvendor 0x%x\n"
+ "boardrev %s\n"
+ "boardflags 0x%x\n"
+ "boardflags2 0x%x\n"
+ "ucoderev 0x%x\n"
+ "radiorev 0x%x\n"
+ "phytype 0x%x\n"
+ "phyrev 0x%x\n"
+ "anarev 0x%x\n"
+ "nvramrev %d\n",
+ bus->chipinfo.id, bus->chipinfo.rev, bus->chipinfo.pkg,
+ core->id.rev, bus->boardinfo.type, bus->boardinfo.vendor,
+ brcmu_boardrev_str(hw->boardrev, boardrev),
+ drvr->wlc->hw->boardflags, drvr->wlc->hw->boardflags2,
+ drvr->wlc->ucode_rev, hw->band->radiorev,
+ hw->band->phytype, hw->band->phyrev, hw->band->pi->ana_rev,
+ hw->sromrev);
+ return 0;
}
-static const struct file_operations brcms_debugfs_hardware_ops = {
- .owner = THIS_MODULE,
- .open = simple_open,
- .read = brcms_debugfs_hardware_read
+static int brcms_debugfs_macstat_read(struct seq_file *s, void *data)
+{
+ struct brcms_pub *drvr = s->private;
+ struct brcms_info *wl = drvr->ieee_hw->priv;
+ struct macstat stats;
+ int i;
+
+ spin_lock_bh(&wl->lock);
+ stats = *(drvr->wlc->core->macstat_snapshot);
+ spin_unlock_bh(&wl->lock);
+
+ seq_printf(s, "txallfrm: %d\n", stats.txallfrm);
+ seq_printf(s, "txrtsfrm: %d\n", stats.txrtsfrm);
+ seq_printf(s, "txctsfrm: %d\n", stats.txctsfrm);
+ seq_printf(s, "txackfrm: %d\n", stats.txackfrm);
+ seq_printf(s, "txdnlfrm: %d\n", stats.txdnlfrm);
+ seq_printf(s, "txbcnfrm: %d\n", stats.txbcnfrm);
+ seq_printf(s, "txfunfl[8]:");
+ for (i = 0; i < ARRAY_SIZE(stats.txfunfl); i++)
+ seq_printf(s, " %d", stats.txfunfl[i]);
+ seq_printf(s, "\ntxtplunfl: %d\n", stats.txtplunfl);
+ seq_printf(s, "txphyerr: %d\n", stats.txphyerr);
+ seq_printf(s, "pktengrxducast: %d\n", stats.pktengrxducast);
+ seq_printf(s, "pktengrxdmcast: %d\n", stats.pktengrxdmcast);
+ seq_printf(s, "rxfrmtoolong: %d\n", stats.rxfrmtoolong);
+ seq_printf(s, "rxfrmtooshrt: %d\n", stats.rxfrmtooshrt);
+ seq_printf(s, "rxinvmachdr: %d\n", stats.rxinvmachdr);
+ seq_printf(s, "rxbadfcs: %d\n", stats.rxbadfcs);
+ seq_printf(s, "rxbadplcp: %d\n", stats.rxbadplcp);
+ seq_printf(s, "rxcrsglitch: %d\n", stats.rxcrsglitch);
+ seq_printf(s, "rxstrt: %d\n", stats.rxstrt);
+ seq_printf(s, "rxdfrmucastmbss: %d\n", stats.rxdfrmucastmbss);
+ seq_printf(s, "rxmfrmucastmbss: %d\n", stats.rxmfrmucastmbss);
+ seq_printf(s, "rxcfrmucast: %d\n", stats.rxcfrmucast);
+ seq_printf(s, "rxrtsucast: %d\n", stats.rxrtsucast);
+ seq_printf(s, "rxctsucast: %d\n", stats.rxctsucast);
+ seq_printf(s, "rxackucast: %d\n", stats.rxackucast);
+ seq_printf(s, "rxdfrmocast: %d\n", stats.rxdfrmocast);
+ seq_printf(s, "rxmfrmocast: %d\n", stats.rxmfrmocast);
+ seq_printf(s, "rxcfrmocast: %d\n", stats.rxcfrmocast);
+ seq_printf(s, "rxrtsocast: %d\n", stats.rxrtsocast);
+ seq_printf(s, "rxctsocast: %d\n", stats.rxctsocast);
+ seq_printf(s, "rxdfrmmcast: %d\n", stats.rxdfrmmcast);
+ seq_printf(s, "rxmfrmmcast: %d\n", stats.rxmfrmmcast);
+ seq_printf(s, "rxcfrmmcast: %d\n", stats.rxcfrmmcast);
+ seq_printf(s, "rxbeaconmbss: %d\n", stats.rxbeaconmbss);
+ seq_printf(s, "rxdfrmucastobss: %d\n", stats.rxdfrmucastobss);
+ seq_printf(s, "rxbeaconobss: %d\n", stats.rxbeaconobss);
+ seq_printf(s, "rxrsptmout: %d\n", stats.rxrsptmout);
+ seq_printf(s, "bcntxcancl: %d\n", stats.bcntxcancl);
+ seq_printf(s, "rxf0ovfl: %d\n", stats.rxf0ovfl);
+ seq_printf(s, "rxf1ovfl: %d\n", stats.rxf1ovfl);
+ seq_printf(s, "rxf2ovfl: %d\n", stats.rxf2ovfl);
+ seq_printf(s, "txsfovfl: %d\n", stats.txsfovfl);
+ seq_printf(s, "pmqovfl: %d\n", stats.pmqovfl);
+ seq_printf(s, "rxcgprqfrm: %d\n", stats.rxcgprqfrm);
+ seq_printf(s, "rxcgprsqovfl: %d\n", stats.rxcgprsqovfl);
+ seq_printf(s, "txcgprsfail: %d\n", stats.txcgprsfail);
+ seq_printf(s, "txcgprssuc: %d\n", stats.txcgprssuc);
+ seq_printf(s, "prs_timeout: %d\n", stats.prs_timeout);
+ seq_printf(s, "rxnack: %d\n", stats.rxnack);
+ seq_printf(s, "frmscons: %d\n", stats.frmscons);
+ seq_printf(s, "txnack: %d\n", stats.txnack);
+ seq_printf(s, "txglitch_nack: %d\n", stats.txglitch_nack);
+ seq_printf(s, "txburst: %d\n", stats.txburst);
+ seq_printf(s, "bphy_rxcrsglitch: %d\n", stats.bphy_rxcrsglitch);
+ seq_printf(s, "phywatchdog: %d\n", stats.phywatchdog);
+ seq_printf(s, "bphy_badplcp: %d\n", stats.bphy_badplcp);
+ return 0;
+}
+
+struct brcms_debugfs_entry {
+ int (*read)(struct seq_file *seq, void *data);
+ struct brcms_pub *drvr;
};
+static int brcms_debugfs_entry_open(struct inode *inode, struct file *f)
+{
+ struct brcms_debugfs_entry *entry = inode->i_private;
+
+ return single_open(f, entry->read, entry->drvr);
+}
+
+static const struct file_operations brcms_debugfs_def_ops = {
+ .owner = THIS_MODULE,
+ .open = brcms_debugfs_entry_open,
+ .release = single_release,
+ .read = seq_read,
+ .llseek = seq_lseek
+};
+
+static int
+brcms_debugfs_add_entry(struct brcms_pub *drvr, const char *fn,
+ int (*read_fn)(struct seq_file *seq, void *data))
+{
+ struct device *dev = &drvr->wlc->hw->d11core->dev;
+ struct dentry *dentry = drvr->dbgfs_dir;
+ struct brcms_debugfs_entry *entry;
+
+ if (IS_ERR_OR_NULL(dentry))
+ return -ENOENT;
+
+ entry = devm_kzalloc(dev, sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ entry->read = read_fn;
+ entry->drvr = drvr;
+
+ dentry = debugfs_create_file(fn, S_IRUGO, dentry, entry,
+ &brcms_debugfs_def_ops);
+
+ return PTR_ERR_OR_ZERO(dentry);
+}
+
void brcms_debugfs_create_files(struct brcms_pub *drvr)
{
- struct dentry *dentry = drvr->dbgfs_dir;
+ if (IS_ERR_OR_NULL(drvr->dbgfs_dir))
+ return;
- if (!IS_ERR_OR_NULL(dentry))
- debugfs_create_file("hardware", S_IRUGO, dentry,
- drvr, &brcms_debugfs_hardware_ops);
+ brcms_debugfs_add_entry(drvr, "hardware", brcms_debugfs_hardware_read);
+ brcms_debugfs_add_entry(drvr, "macstat", brcms_debugfs_macstat_read);
}
#define __brcms_fn(fn) \
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
index b6f5f95..ac50a21 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
@@ -764,7 +764,9 @@
return;
}
-static void brcms_ops_sw_scan_start(struct ieee80211_hw *hw)
+static void brcms_ops_sw_scan_start(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ const u8 *mac_addr)
{
struct brcms_info *wl = hw->priv;
spin_lock_bh(&wl->lock);
@@ -773,7 +775,8 @@
return;
}
-static void brcms_ops_sw_scan_complete(struct ieee80211_hw *hw)
+static void brcms_ops_sw_scan_complete(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
{
struct brcms_info *wl = hw->priv;
spin_lock_bh(&wl->lock);
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c
index 1b47482..eb8584a 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/main.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c
@@ -316,7 +316,7 @@
static const char * const fifo_names[] = {
"AC_BK", "AC_BE", "AC_VI", "AC_VO", "BCMC", "ATIM" };
#else
-static const char fifo_names[6][0];
+static const char fifo_names[6][1];
#endif
#ifdef DEBUG
@@ -445,18 +445,18 @@
kfree(wlc->protection);
kfree(wlc->stf);
kfree(wlc->bandstate[0]);
- kfree(wlc->corestate->macstat_snapshot);
+ if (wlc->corestate)
+ kfree(wlc->corestate->macstat_snapshot);
kfree(wlc->corestate);
- kfree(wlc->hw->bandstate[0]);
+ if (wlc->hw)
+ kfree(wlc->hw->bandstate[0]);
kfree(wlc->hw);
if (wlc->beacon)
dev_kfree_skb_any(wlc->beacon);
if (wlc->probe_resp)
dev_kfree_skb_any(wlc->probe_resp);
- /* free the wlc */
kfree(wlc);
- wlc = NULL;
}
static struct brcms_bss_cfg *brcms_c_bsscfg_malloc(uint unit)
@@ -1009,8 +1009,7 @@
if (txh)
trace_brcms_txdesc(&wlc->hw->d11core->dev, txh,
sizeof(*txh));
- if (p)
- brcmu_pkt_buf_free_skb(p);
+ brcmu_pkt_buf_free_skb(p);
}
if (dma && queue < NFIFO) {
@@ -3081,7 +3080,7 @@
static void brcms_c_statsupd(struct brcms_c_info *wlc)
{
int i;
- struct macstat macstats;
+ struct macstat *macstats;
#ifdef DEBUG
u16 delta;
u16 rxf0ovfl;
@@ -3092,31 +3091,31 @@
if (!wlc->pub->up)
return;
+ macstats = wlc->core->macstat_snapshot;
+
#ifdef DEBUG
/* save last rx fifo 0 overflow count */
- rxf0ovfl = wlc->core->macstat_snapshot->rxf0ovfl;
+ rxf0ovfl = macstats->rxf0ovfl;
/* save last tx fifo underflow count */
for (i = 0; i < NFIFO; i++)
- txfunfl[i] = wlc->core->macstat_snapshot->txfunfl[i];
+ txfunfl[i] = macstats->txfunfl[i];
#endif /* DEBUG */
/* Read mac stats from contiguous shared memory */
- brcms_b_copyfrom_objmem(wlc->hw, M_UCODE_MACSTAT, &macstats,
- sizeof(struct macstat), OBJADDR_SHM_SEL);
+ brcms_b_copyfrom_objmem(wlc->hw, M_UCODE_MACSTAT, macstats,
+ sizeof(*macstats), OBJADDR_SHM_SEL);
#ifdef DEBUG
/* check for rx fifo 0 overflow */
- delta = (u16) (wlc->core->macstat_snapshot->rxf0ovfl - rxf0ovfl);
+ delta = (u16)(macstats->rxf0ovfl - rxf0ovfl);
if (delta)
brcms_err(wlc->hw->d11core, "wl%d: %u rx fifo 0 overflows!\n",
wlc->pub->unit, delta);
/* check for tx fifo underflows */
for (i = 0; i < NFIFO; i++) {
- delta =
- (u16) (wlc->core->macstat_snapshot->txfunfl[i] -
- txfunfl[i]);
+ delta = macstats->txfunfl[i] - txfunfl[i];
if (delta)
brcms_err(wlc->hw->d11core,
"wl%d: %u tx fifo %d underflows!\n",
diff --git a/drivers/net/wireless/brcm80211/brcmutil/utils.c b/drivers/net/wireless/brcm80211/brcmutil/utils.c
index 0f7e1c7..906e89d 100644
--- a/drivers/net/wireless/brcm80211/brcmutil/utils.c
+++ b/drivers/net/wireless/brcm80211/brcmutil/utils.c
@@ -261,6 +261,21 @@
}
EXPORT_SYMBOL(brcmu_pktq_mdeq);
+/* Produce a human-readable string for boardrev */
+char *brcmu_boardrev_str(u32 brev, char *buf)
+{
+ char c;
+
+ if (brev < 0x100) {
+ snprintf(buf, 8, "%d.%d", (brev & 0xf0) >> 4, brev & 0xf);
+ } else {
+ c = (brev & 0xf000) == 0x1000 ? 'P' : 'A';
+ snprintf(buf, 8, "%c%03x", c, brev & 0xfff);
+ }
+ return buf;
+}
+EXPORT_SYMBOL(brcmu_boardrev_str);
+
#if defined(DEBUG)
/* pretty hex print a pkt buffer chain */
void brcmu_prpkt(const char *msg, struct sk_buff *p0)
@@ -292,4 +307,5 @@
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, data, size);
}
EXPORT_SYMBOL(brcmu_dbg_hex_dump);
+
#endif /* defined(DEBUG) */
diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h
index af26e0d..6996fcc 100644
--- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h
+++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h
@@ -68,6 +68,8 @@
#define BRCM_PCIE_43567_DEVICE_ID 0x43d3
#define BRCM_PCIE_43570_DEVICE_ID 0x43d9
#define BRCM_PCIE_43602_DEVICE_ID 0x43ba
+#define BRCM_PCIE_43602_2G_DEVICE_ID 0x43bb
+#define BRCM_PCIE_43602_5G_DEVICE_ID 0x43bc
/* brcmsmac IDs */
#define BCM4313_D11N2G_ID 0x4727 /* 4313 802.11n 2.4G device */
diff --git a/drivers/net/wireless/brcm80211/include/brcmu_utils.h b/drivers/net/wireless/brcm80211/include/brcmu_utils.h
index 8ba445b..a043e29 100644
--- a/drivers/net/wireless/brcm80211/include/brcmu_utils.h
+++ b/drivers/net/wireless/brcm80211/include/brcmu_utils.h
@@ -218,4 +218,6 @@
}
#endif
+char *brcmu_boardrev_str(u32 brev, char *buf);
+
#endif /* _BRCMU_UTILS_H_ */
diff --git a/drivers/net/wireless/cw1200/scan.c b/drivers/net/wireless/cw1200/scan.c
index b2fb6c6..f2e276f 100644
--- a/drivers/net/wireless/cw1200/scan.c
+++ b/drivers/net/wireless/cw1200/scan.c
@@ -78,7 +78,7 @@
if (req->n_ssids > WSM_SCAN_MAX_NUM_OF_SSIDS)
return -EINVAL;
- frame.skb = ieee80211_probereq_get(hw, priv->vif, NULL, 0,
+ frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0,
req->ie_len);
if (!frame.skb)
return -ENOMEM;
diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c
index c2b7368..d46df94 100644
--- a/drivers/net/wireless/ipw2x00/ipw2200.c
+++ b/drivers/net/wireless/ipw2x00/ipw2200.c
@@ -1363,7 +1363,7 @@
if (!priv->cmdlog)
return 0;
for (i = (priv->cmdlog_pos + 1) % priv->cmdlog_len;
- (i != priv->cmdlog_pos) && (PAGE_SIZE - len);
+ (i != priv->cmdlog_pos) && (len < PAGE_SIZE);
i = (i + 1) % priv->cmdlog_len) {
len +=
snprintf(buf + len, PAGE_SIZE - len,
diff --git a/drivers/net/wireless/ipw2x00/libipw.h b/drivers/net/wireless/ipw2x00/libipw.h
index e1fccda..fe9e438 100644
--- a/drivers/net/wireless/ipw2x00/libipw.h
+++ b/drivers/net/wireless/ipw2x00/libipw.h
@@ -654,10 +654,6 @@
/* TPC Report - mandatory if spctrm mgmt required */
struct libipw_tpc_report tpc_report;
- /* IBSS DFS - mandatory if spctrm mgmt required and IBSS
- * NOTE: This is variable length and so must be allocated dynamically */
- struct libipw_ibss_dfs *ibss_dfs;
-
/* Channel Switch Announcement - optional if spctrm mgmt required */
struct libipw_csa csa;
@@ -970,7 +966,6 @@
/* make sure to set stats->len */
void libipw_rx_mgt(struct libipw_device *ieee, struct libipw_hdr_4addr *header,
struct libipw_rx_stats *stats);
-void libipw_network_reset(struct libipw_network *network);
/* libipw_geo.c */
const struct libipw_geo *libipw_get_geo(struct libipw_device *ieee);
diff --git a/drivers/net/wireless/ipw2x00/libipw_module.c b/drivers/net/wireless/ipw2x00/libipw_module.c
index 92a935f..1d1a28a 100644
--- a/drivers/net/wireless/ipw2x00/libipw_module.c
+++ b/drivers/net/wireless/ipw2x00/libipw_module.c
@@ -84,25 +84,12 @@
return 0;
}
-void libipw_network_reset(struct libipw_network *network)
-{
- if (!network)
- return;
-
- if (network->ibss_dfs) {
- kfree(network->ibss_dfs);
- network->ibss_dfs = NULL;
- }
-}
-
static inline void libipw_networks_free(struct libipw_device *ieee)
{
int i;
- for (i = 0; i < MAX_NETWORK_COUNT; i++) {
- kfree(ieee->networks[i]->ibss_dfs);
+ for (i = 0; i < MAX_NETWORK_COUNT; i++)
kfree(ieee->networks[i]);
- }
}
void libipw_networks_age(struct libipw_device *ieee,
diff --git a/drivers/net/wireless/ipw2x00/libipw_rx.c b/drivers/net/wireless/ipw2x00/libipw_rx.c
index 651a1ee..3e4d83c 100644
--- a/drivers/net/wireless/ipw2x00/libipw_rx.c
+++ b/drivers/net/wireless/ipw2x00/libipw_rx.c
@@ -1298,13 +1298,6 @@
break;
case WLAN_EID_IBSS_DFS:
- if (network->ibss_dfs)
- break;
- network->ibss_dfs = kmemdup(info_element->data,
- info_element->len,
- GFP_ATOMIC);
- if (!network->ibss_dfs)
- return 1;
network->flags |= NETWORK_HAS_IBSS_DFS;
break;
@@ -1335,9 +1328,7 @@
static int libipw_handle_assoc_resp(struct libipw_device *ieee, struct libipw_assoc_response
*frame, struct libipw_rx_stats *stats)
{
- struct libipw_network network_resp = {
- .ibss_dfs = NULL,
- };
+ struct libipw_network network_resp = { };
struct libipw_network *network = &network_resp;
struct net_device *dev = ieee->dev;
@@ -1472,9 +1463,6 @@
int qos_active;
u8 old_param;
- libipw_network_reset(dst);
- dst->ibss_dfs = src->ibss_dfs;
-
/* We only update the statistics if they were created by receiving
* the network information on the actual channel the network is on.
*
@@ -1548,9 +1536,7 @@
*stats)
{
struct net_device *dev = ieee->dev;
- struct libipw_network network = {
- .ibss_dfs = NULL,
- };
+ struct libipw_network network = { };
struct libipw_network *target;
struct libipw_network *oldest = NULL;
#ifdef CPTCFG_LIBIPW_DEBUG
@@ -1618,7 +1604,6 @@
LIBIPW_DEBUG_SCAN("Expired '%*pE' (%pM) from network list.\n",
target->ssid_len, target->ssid,
target->bssid);
- libipw_network_reset(target);
} else {
/* Otherwise just pull from the free list */
target = list_entry(ieee->network_free_list.next,
@@ -1634,7 +1619,6 @@
"BEACON" : "PROBE RESPONSE");
#endif
memcpy(target, &network, sizeof(*target));
- network.ibss_dfs = NULL;
list_add_tail(&target->list, &ieee->network_list);
} else {
LIBIPW_DEBUG_SCAN("Updating '%*pE' (%pM) via %s.\n",
@@ -1643,7 +1627,6 @@
is_beacon(beacon->header.frame_ctl) ?
"BEACON" : "PROBE RESPONSE");
update_network(target, &network);
- network.ibss_dfs = NULL;
}
spin_unlock_irqrestore(&ieee->lock, flags);
diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c
index 6c9cbb8..cb90853 100644
--- a/drivers/net/wireless/iwlegacy/4965-mac.c
+++ b/drivers/net/wireless/iwlegacy/4965-mac.c
@@ -6063,7 +6063,7 @@
}
void
-il4965_mac_channel_switch(struct ieee80211_hw *hw,
+il4965_mac_channel_switch(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_channel_switch *ch_switch)
{
struct il_priv *il = hw->priv;
diff --git a/drivers/net/wireless/iwlegacy/4965.h b/drivers/net/wireless/iwlegacy/4965.h
index a96696e..db04ecf 100644
--- a/drivers/net/wireless/iwlegacy/4965.h
+++ b/drivers/net/wireless/iwlegacy/4965.h
@@ -187,8 +187,9 @@
u8 buf_size);
int il4965_mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta);
-void il4965_mac_channel_switch(struct ieee80211_hw *hw,
- struct ieee80211_channel_switch *ch_switch);
+void
+il4965_mac_channel_switch(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_channel_switch *ch_switch);
void il4965_led_enable(struct il_priv *il);
diff --git a/drivers/net/wireless/iwlegacy/Kconfig b/drivers/net/wireless/iwlegacy/Kconfig
index 05d8fc7..cdc6baf 100644
--- a/drivers/net/wireless/iwlegacy/Kconfig
+++ b/drivers/net/wireless/iwlegacy/Kconfig
@@ -2,9 +2,9 @@
tristate
depends on m
depends on FW_LOADER
- select BACKPORT_NEW_LEDS
- select BACKPORT_LEDS_CLASS
- select BACKPORT_LEDS_TRIGGERS
+ select BPAUTO_NEW_LEDS
+ select BPAUTO_LEDS_CLASS
+ select BPAUTO_LEDS_TRIGGERS
select MAC80211_LEDS
config IWL4965
diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig
index 3721c07..63f153b 100644
--- a/drivers/net/wireless/iwlwifi/Kconfig
+++ b/drivers/net/wireless/iwlwifi/Kconfig
@@ -46,7 +46,7 @@
config IWLWIFI_LEDS
bool
depends on LEDS_CLASS=y || LEDS_CLASS=IWLWIFI
- select BACKPORT_LEDS_TRIGGERS
+ select BPAUTO_LEDS_TRIGGERS
select MAC80211_LEDS
default y
@@ -62,6 +62,7 @@
config IWLMVM
tristate "Intel Wireless WiFi MVM Firmware support"
depends on m
+ select BPAUTO_WANT_DEV_COREDUMP
help
This is the driver that supports the MVM firmware which is
currently only available for 7260 and 3160 devices.
diff --git a/drivers/net/wireless/iwlwifi/dvm/commands.h b/drivers/net/wireless/iwlwifi/dvm/commands.h
index 751ae1d..7a34e4d 100644
--- a/drivers/net/wireless/iwlwifi/dvm/commands.h
+++ b/drivers/net/wireless/iwlwifi/dvm/commands.h
@@ -966,21 +966,21 @@
/* WiFi queues mask */
-#define IWL_SCD_BK_MSK cpu_to_le32(BIT(0))
-#define IWL_SCD_BE_MSK cpu_to_le32(BIT(1))
-#define IWL_SCD_VI_MSK cpu_to_le32(BIT(2))
-#define IWL_SCD_VO_MSK cpu_to_le32(BIT(3))
-#define IWL_SCD_MGMT_MSK cpu_to_le32(BIT(3))
+#define IWL_SCD_BK_MSK BIT(0)
+#define IWL_SCD_BE_MSK BIT(1)
+#define IWL_SCD_VI_MSK BIT(2)
+#define IWL_SCD_VO_MSK BIT(3)
+#define IWL_SCD_MGMT_MSK BIT(3)
/* PAN queues mask */
-#define IWL_PAN_SCD_BK_MSK cpu_to_le32(BIT(4))
-#define IWL_PAN_SCD_BE_MSK cpu_to_le32(BIT(5))
-#define IWL_PAN_SCD_VI_MSK cpu_to_le32(BIT(6))
-#define IWL_PAN_SCD_VO_MSK cpu_to_le32(BIT(7))
-#define IWL_PAN_SCD_MGMT_MSK cpu_to_le32(BIT(7))
-#define IWL_PAN_SCD_MULTICAST_MSK cpu_to_le32(BIT(8))
+#define IWL_PAN_SCD_BK_MSK BIT(4)
+#define IWL_PAN_SCD_BE_MSK BIT(5)
+#define IWL_PAN_SCD_VI_MSK BIT(6)
+#define IWL_PAN_SCD_VO_MSK BIT(7)
+#define IWL_PAN_SCD_MGMT_MSK BIT(7)
+#define IWL_PAN_SCD_MULTICAST_MSK BIT(8)
-#define IWL_AGG_TX_QUEUE_MSK cpu_to_le32(0xffc00)
+#define IWL_AGG_TX_QUEUE_MSK 0xffc00
#define IWL_DROP_ALL BIT(1)
@@ -1005,12 +1005,17 @@
* 1: Dump multiple MSDU according to PS, INVALID STA, TTL, TID disable.
* 2: Dump all FIFO
*/
-struct iwl_txfifo_flush_cmd {
+struct iwl_txfifo_flush_cmd_v3 {
__le32 queue_control;
__le16 flush_control;
__le16 reserved;
} __packed;
+struct iwl_txfifo_flush_cmd_v2 {
+ __le16 queue_control;
+ __le16 flush_control;
+} __packed;
+
/*
* REPLY_WEP_KEY = 0x20
*/
diff --git a/drivers/net/wireless/iwlwifi/dvm/lib.c b/drivers/net/wireless/iwlwifi/dvm/lib.c
index 2191621..1d2223d 100644
--- a/drivers/net/wireless/iwlwifi/dvm/lib.c
+++ b/drivers/net/wireless/iwlwifi/dvm/lib.c
@@ -137,37 +137,38 @@
*/
int iwlagn_txfifo_flush(struct iwl_priv *priv, u32 scd_q_msk)
{
- struct iwl_txfifo_flush_cmd flush_cmd;
- struct iwl_host_cmd cmd = {
- .id = REPLY_TXFIFO_FLUSH,
- .len = { sizeof(struct iwl_txfifo_flush_cmd), },
- .data = { &flush_cmd, },
+ struct iwl_txfifo_flush_cmd_v3 flush_cmd_v3 = {
+ .flush_control = cpu_to_le16(IWL_DROP_ALL),
+ };
+ struct iwl_txfifo_flush_cmd_v2 flush_cmd_v2 = {
+ .flush_control = cpu_to_le16(IWL_DROP_ALL),
};
- memset(&flush_cmd, 0, sizeof(flush_cmd));
+ u32 queue_control = IWL_SCD_VO_MSK | IWL_SCD_VI_MSK |
+ IWL_SCD_BE_MSK | IWL_SCD_BK_MSK | IWL_SCD_MGMT_MSK;
- flush_cmd.queue_control = IWL_SCD_VO_MSK | IWL_SCD_VI_MSK |
- IWL_SCD_BE_MSK | IWL_SCD_BK_MSK |
- IWL_SCD_MGMT_MSK;
if ((priv->valid_contexts != BIT(IWL_RXON_CTX_BSS)))
- flush_cmd.queue_control |= IWL_PAN_SCD_VO_MSK |
- IWL_PAN_SCD_VI_MSK |
- IWL_PAN_SCD_BE_MSK |
- IWL_PAN_SCD_BK_MSK |
- IWL_PAN_SCD_MGMT_MSK |
- IWL_PAN_SCD_MULTICAST_MSK;
+ queue_control |= IWL_PAN_SCD_VO_MSK | IWL_PAN_SCD_VI_MSK |
+ IWL_PAN_SCD_BE_MSK | IWL_PAN_SCD_BK_MSK |
+ IWL_PAN_SCD_MGMT_MSK |
+ IWL_PAN_SCD_MULTICAST_MSK;
if (priv->nvm_data->sku_cap_11n_enable)
- flush_cmd.queue_control |= IWL_AGG_TX_QUEUE_MSK;
+ queue_control |= IWL_AGG_TX_QUEUE_MSK;
if (scd_q_msk)
- flush_cmd.queue_control = cpu_to_le32(scd_q_msk);
+ queue_control = scd_q_msk;
- IWL_DEBUG_INFO(priv, "queue control: 0x%x\n",
- flush_cmd.queue_control);
- flush_cmd.flush_control = cpu_to_le16(IWL_DROP_ALL);
+ IWL_DEBUG_INFO(priv, "queue control: 0x%x\n", queue_control);
+ flush_cmd_v3.queue_control = cpu_to_le32(queue_control);
+ flush_cmd_v2.queue_control = cpu_to_le16((u16)queue_control);
- return iwl_dvm_send_cmd(priv, &cmd);
+ if (IWL_UCODE_API(priv->fw->ucode_ver) > 2)
+ return iwl_dvm_send_cmd_pdu(priv, REPLY_TXFIFO_FLUSH, 0,
+ sizeof(flush_cmd_v3),
+ &flush_cmd_v3);
+ return iwl_dvm_send_cmd_pdu(priv, REPLY_TXFIFO_FLUSH, 0,
+ sizeof(flush_cmd_v2), &flush_cmd_v2);
}
void iwlagn_dev_txfifo_flush(struct iwl_priv *priv)
@@ -418,8 +419,8 @@
static bool iwlagn_bt_traffic_is_sco(struct iwl_bt_uart_msg *uart_msg)
{
- return BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3 >>
- BT_UART_MSG_FRAME3SCOESCO_POS;
+ return (BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3) >>
+ BT_UART_MSG_FRAME3SCOESCO_POS;
}
static void iwlagn_bt_traffic_change_work(struct work_struct *work)
diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c
index d8ebc7e..dfa0626 100644
--- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c
@@ -941,6 +941,7 @@
}
static void iwlagn_mac_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
struct ieee80211_channel_switch *ch_switch)
{
struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
@@ -1095,6 +1096,7 @@
u32 queues, bool drop)
{
struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+ u32 scd_queues;
mutex_lock(&priv->mutex);
IWL_DEBUG_MAC80211(priv, "enter\n");
@@ -1108,18 +1110,19 @@
goto done;
}
- /*
- * mac80211 will not push any more frames for transmit
- * until the flush is completed
- */
- if (drop) {
- IWL_DEBUG_MAC80211(priv, "send flush command\n");
- if (iwlagn_txfifo_flush(priv, 0)) {
- IWL_ERR(priv, "flush request fail\n");
- goto done;
- }
+ scd_queues = BIT(priv->cfg->base_params->num_of_queues) - 1;
+ scd_queues &= ~(BIT(IWL_IPAN_CMD_QUEUE_NUM) |
+ BIT(IWL_DEFAULT_CMD_QUEUE_NUM));
+
+ if (vif)
+ scd_queues &= ~BIT(vif->hw_queue[IEEE80211_AC_VO]);
+
+ IWL_DEBUG_TX_QUEUES(priv, "Flushing SCD queues: 0x%x\n", scd_queues);
+ if (iwlagn_txfifo_flush(priv, scd_queues)) {
+ IWL_ERR(priv, "flush request fail\n");
+ goto done;
}
- IWL_DEBUG_MAC80211(priv, "wait transmit/flush all frames\n");
+ IWL_DEBUG_TX_QUEUES(priv, "wait transmit/flush all frames\n");
iwl_trans_wait_tx_queue_empty(priv->trans, 0xffffffff);
done:
mutex_unlock(&priv->mutex);
diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c
index b04b885..e5be2d2 100644
--- a/drivers/net/wireless/iwlwifi/iwl-7000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-7000.c
@@ -73,12 +73,12 @@
#define IWL3160_UCODE_API_MAX 10
/* Oldest version we won't warn about */
-#define IWL7260_UCODE_API_OK 9
-#define IWL3160_UCODE_API_OK 9
+#define IWL7260_UCODE_API_OK 10
+#define IWL3160_UCODE_API_OK 10
/* Lowest firmware API version supported */
-#define IWL7260_UCODE_API_MIN 8
-#define IWL3160_UCODE_API_MIN 8
+#define IWL7260_UCODE_API_MIN 9
+#define IWL3160_UCODE_API_MIN 9
/* NVM versions */
#define IWL7260_NVM_VERSION 0x0a1d
@@ -89,6 +89,8 @@
#define IWL3165_TX_POWER_VERSION 0xffff /* meaningless */
#define IWL7265_NVM_VERSION 0x0a1d
#define IWL7265_TX_POWER_VERSION 0xffff /* meaningless */
+#define IWL7265D_NVM_VERSION 0x0c11
+#define IWL7265_TX_POWER_VERSION 0xffff /* meaningless */
#define IWL7260_FW_PRE "iwlwifi-7260-"
#define IWL7260_MODULE_FIRMWARE(api) IWL7260_FW_PRE __stringify(api) ".ucode"
@@ -102,6 +104,9 @@
#define IWL7265_FW_PRE "iwlwifi-7265-"
#define IWL7265_MODULE_FIRMWARE(api) IWL7265_FW_PRE __stringify(api) ".ucode"
+#define IWL7265D_FW_PRE "iwlwifi-7265D-"
+#define IWL7265D_MODULE_FIRMWARE(api) IWL7265_FW_PRE __stringify(api) ".ucode"
+
#define NVM_HW_SECTION_NUM_FAMILY_7000 0
static const struct iwl_base_params iwl7000_base_params = {
@@ -132,8 +137,8 @@
.base_params = &iwl7000_base_params, \
.led_mode = IWL_LED_RF_STATE, \
.nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_7000, \
- .non_shared_ant = ANT_A
-
+ .non_shared_ant = ANT_A, \
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
const struct iwl_cfg iwl7260_2ac_cfg = {
.name = "Intel(R) Dual Band Wireless AC 7260",
@@ -267,7 +272,38 @@
.pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
};
+const struct iwl_cfg iwl7265d_2ac_cfg = {
+ .name = "Intel(R) Dual Band Wireless AC 7265",
+ .fw_name_pre = IWL7265D_FW_PRE,
+ IWL_DEVICE_7000,
+ .ht_params = &iwl7265_ht_params,
+ .nvm_ver = IWL7265D_NVM_VERSION,
+ .nvm_calib_ver = IWL7265_TX_POWER_VERSION,
+ .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
+};
+
+const struct iwl_cfg iwl7265d_2n_cfg = {
+ .name = "Intel(R) Dual Band Wireless N 7265",
+ .fw_name_pre = IWL7265D_FW_PRE,
+ IWL_DEVICE_7000,
+ .ht_params = &iwl7265_ht_params,
+ .nvm_ver = IWL7265D_NVM_VERSION,
+ .nvm_calib_ver = IWL7265_TX_POWER_VERSION,
+ .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
+};
+
+const struct iwl_cfg iwl7265d_n_cfg = {
+ .name = "Intel(R) Wireless N 7265",
+ .fw_name_pre = IWL7265D_FW_PRE,
+ IWL_DEVICE_7000,
+ .ht_params = &iwl7265_ht_params,
+ .nvm_ver = IWL7265D_NVM_VERSION,
+ .nvm_calib_ver = IWL7265_TX_POWER_VERSION,
+ .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs,
+};
+
MODULE_FIRMWARE(IWL7260_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));
MODULE_FIRMWARE(IWL3160_MODULE_FIRMWARE(IWL3160_UCODE_API_OK));
MODULE_FIRMWARE(IWL3165_MODULE_FIRMWARE(IWL3160_UCODE_API_OK));
MODULE_FIRMWARE(IWL7265_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));
+MODULE_FIRMWARE(IWL7265D_MODULE_FIRMWARE(IWL7260_UCODE_API_OK));
diff --git a/drivers/net/wireless/iwlwifi/iwl-8000.c b/drivers/net/wireless/iwlwifi/iwl-8000.c
index e435148..bf0a95c 100644
--- a/drivers/net/wireless/iwlwifi/iwl-8000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-8000.c
@@ -72,17 +72,18 @@
#define IWL8000_UCODE_API_MAX 10
/* Oldest version we won't warn about */
-#define IWL8000_UCODE_API_OK 8
+#define IWL8000_UCODE_API_OK 10
/* Lowest firmware API version supported */
-#define IWL8000_UCODE_API_MIN 8
+#define IWL8000_UCODE_API_MIN 9
/* NVM versions */
#define IWL8000_NVM_VERSION 0x0a1d
#define IWL8000_TX_POWER_VERSION 0xffff /* meaningless */
#define IWL8000_FW_PRE "iwlwifi-8000"
-#define IWL8000_MODULE_FIRMWARE(api) IWL8000_FW_PRE __stringify(api) ".ucode"
+#define IWL8000_MODULE_FIRMWARE(api) \
+ IWL8000_FW_PRE "-" __stringify(api) ".ucode"
#define NVM_HW_SECTION_NUM_FAMILY_8000 10
#define DEFAULT_NVM_FILE_FAMILY_8000 "iwl_nvm_8000.bin"
@@ -90,6 +91,10 @@
/* Max SDIO RX aggregation size of the ADDBA request/response */
#define MAX_RX_AGG_SIZE_8260_SDIO 28
+/* Max A-MPDU exponent for HT and VHT */
+#define MAX_HT_AMPDU_EXPONENT_8260_SDIO IEEE80211_HT_MAX_AMPDU_32K
+#define MAX_VHT_AMPDU_EXPONENT_8260_SDIO IEEE80211_VHT_MAX_AMPDU_32K
+
static const struct iwl_base_params iwl8000_base_params = {
.eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_8000,
.num_of_queues = IWLAGN_NUM_QUEUES,
@@ -103,6 +108,7 @@
};
static const struct iwl_ht_params iwl8000_ht_params = {
+ .stbc = true,
.ldpc = true,
.ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ),
};
@@ -117,6 +123,7 @@
.base_params = &iwl8000_base_params, \
.led_mode = IWL_LED_RF_STATE, \
.nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_8000, \
+ .d0i3 = true, \
.non_shared_ant = ANT_A
const struct iwl_cfg iwl8260_2n_cfg = {
@@ -135,6 +142,7 @@
.ht_params = &iwl8000_ht_params,
.nvm_ver = IWL8000_NVM_VERSION,
.nvm_calib_ver = IWL8000_TX_POWER_VERSION,
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K,
};
const struct iwl_cfg iwl8260_2ac_sdio_cfg = {
@@ -147,6 +155,23 @@
.default_nvm_file = DEFAULT_NVM_FILE_FAMILY_8000,
.max_rx_agg_size = MAX_RX_AGG_SIZE_8260_SDIO,
.disable_dummy_notification = true,
+ .max_ht_ampdu_exponent = MAX_HT_AMPDU_EXPONENT_8260_SDIO,
+ .max_vht_ampdu_exponent = MAX_VHT_AMPDU_EXPONENT_8260_SDIO,
+};
+
+const struct iwl_cfg iwl4165_2ac_sdio_cfg = {
+ .name = "Intel(R) Dual Band Wireless-AC 4165",
+ .fw_name_pre = IWL8000_FW_PRE,
+ IWL_DEVICE_8000,
+ .ht_params = &iwl8000_ht_params,
+ .nvm_ver = IWL8000_NVM_VERSION,
+ .nvm_calib_ver = IWL8000_TX_POWER_VERSION,
+ .default_nvm_file = DEFAULT_NVM_FILE_FAMILY_8000,
+ .max_rx_agg_size = MAX_RX_AGG_SIZE_8260_SDIO,
+ .bt_shared_single_ant = true,
+ .disable_dummy_notification = true,
+ .max_ht_ampdu_exponent = MAX_HT_AMPDU_EXPONENT_8260_SDIO,
+ .max_vht_ampdu_exponent = MAX_VHT_AMPDU_EXPONENT_8260_SDIO,
};
MODULE_FIRMWARE(IWL8000_MODULE_FIRMWARE(IWL8000_UCODE_API_OK));
diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h
index 6926711..ab079ff 100644
--- a/drivers/net/wireless/iwlwifi/iwl-config.h
+++ b/drivers/net/wireless/iwlwifi/iwl-config.h
@@ -87,6 +87,16 @@
IWL_DEVICE_FAMILY_8000,
};
+static inline bool iwl_has_secure_boot(u32 hw_rev,
+ enum iwl_device_family family)
+{
+ /* return 1 only for family 8000 B0 */
+ if ((family == IWL_DEVICE_FAMILY_8000) && (hw_rev & 0xC))
+ return 1;
+
+ return 0;
+}
+
/*
* LED mode
* IWL_LED_DEFAULT: use device default
@@ -246,6 +256,11 @@
* @nvm_hw_section_num: the ID of the HW NVM section
* @pwr_tx_backoffs: translation table between power limits and backoffs
* @max_rx_agg_size: max RX aggregation size of the ADDBA request/response
+ * @max_tx_agg_size: max TX aggregation size of the ADDBA request/response
+ * @max_ht_ampdu_factor: the exponent of the max length of A-MPDU that the
+ * station can receive in HT
+ * @max_vht_ampdu_exponent: the exponent of the max length of A-MPDU that the
+ * station can receive in VHT
*
* We enable the driver to be backward compatible wrt. hardware features.
* API differences in uCode shouldn't be handled here but through TLVs
@@ -285,6 +300,9 @@
const char *default_nvm_file;
unsigned int max_rx_agg_size;
bool disable_dummy_notification;
+ unsigned int max_tx_agg_size;
+ unsigned int max_ht_ampdu_exponent;
+ unsigned int max_vht_ampdu_exponent;
};
/*
@@ -346,9 +364,14 @@
extern const struct iwl_cfg iwl7265_2ac_cfg;
extern const struct iwl_cfg iwl7265_2n_cfg;
extern const struct iwl_cfg iwl7265_n_cfg;
+extern const struct iwl_cfg iwl7265d_2ac_cfg;
+extern const struct iwl_cfg iwl7265d_2n_cfg;
+extern const struct iwl_cfg iwl7265d_n_cfg;
extern const struct iwl_cfg iwl8260_2n_cfg;
extern const struct iwl_cfg iwl8260_2ac_cfg;
extern const struct iwl_cfg iwl8260_2ac_sdio_cfg;
+extern const struct iwl_cfg iwl4265_2ac_sdio_cfg;
+extern const struct iwl_cfg iwl4165_2ac_sdio_cfg;
#endif /* CPTCFG_IWLMVM */
#endif /* __IWL_CONFIG_H__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h
index 3f6f015..aff63c3 100644
--- a/drivers/net/wireless/iwlwifi/iwl-csr.h
+++ b/drivers/net/wireless/iwlwifi/iwl-csr.h
@@ -129,6 +129,8 @@
#define CSR_UCODE_DRV_GP1_CLR (CSR_BASE+0x05c)
#define CSR_UCODE_DRV_GP2 (CSR_BASE+0x060)
+#define CSR_MBOX_SET_REG (CSR_BASE + 0x88)
+
#define CSR_LED_REG (CSR_BASE+0x094)
#define CSR_DRAM_INT_TBL_REG (CSR_BASE+0x0A0)
#define CSR_MAC_SHADOW_REG_CTRL (CSR_BASE+0x0A8) /* 6000 and up */
@@ -184,6 +186,8 @@
#define CSR_HW_IF_CONFIG_REG_PREPARE (0x08000000) /* WAKE_ME */
#define CSR_HW_IF_CONFIG_REG_PERSIST_MODE (0x40000000) /* PERSISTENCE */
+#define CSR_MBOX_SET_REG_OS_ALIVE BIT(5)
+
#define CSR_INT_PERIODIC_DIS (0x00) /* disable periodic int*/
#define CSR_INT_PERIODIC_ENA (0xFF) /* 255*32 usec ~ 8 msec*/
@@ -305,23 +309,24 @@
};
-#define CSR_HW_REV_TYPE_MSK (0x000FFF0)
-#define CSR_HW_REV_TYPE_5300 (0x0000020)
-#define CSR_HW_REV_TYPE_5350 (0x0000030)
-#define CSR_HW_REV_TYPE_5100 (0x0000050)
-#define CSR_HW_REV_TYPE_5150 (0x0000040)
-#define CSR_HW_REV_TYPE_1000 (0x0000060)
-#define CSR_HW_REV_TYPE_6x00 (0x0000070)
-#define CSR_HW_REV_TYPE_6x50 (0x0000080)
-#define CSR_HW_REV_TYPE_6150 (0x0000084)
-#define CSR_HW_REV_TYPE_6x05 (0x00000B0)
-#define CSR_HW_REV_TYPE_6x30 CSR_HW_REV_TYPE_6x05
-#define CSR_HW_REV_TYPE_6x35 CSR_HW_REV_TYPE_6x05
-#define CSR_HW_REV_TYPE_2x30 (0x00000C0)
-#define CSR_HW_REV_TYPE_2x00 (0x0000100)
-#define CSR_HW_REV_TYPE_105 (0x0000110)
-#define CSR_HW_REV_TYPE_135 (0x0000120)
-#define CSR_HW_REV_TYPE_NONE (0x00001F0)
+#define CSR_HW_REV_TYPE_MSK (0x000FFF0)
+#define CSR_HW_REV_TYPE_5300 (0x0000020)
+#define CSR_HW_REV_TYPE_5350 (0x0000030)
+#define CSR_HW_REV_TYPE_5100 (0x0000050)
+#define CSR_HW_REV_TYPE_5150 (0x0000040)
+#define CSR_HW_REV_TYPE_1000 (0x0000060)
+#define CSR_HW_REV_TYPE_6x00 (0x0000070)
+#define CSR_HW_REV_TYPE_6x50 (0x0000080)
+#define CSR_HW_REV_TYPE_6150 (0x0000084)
+#define CSR_HW_REV_TYPE_6x05 (0x00000B0)
+#define CSR_HW_REV_TYPE_6x30 CSR_HW_REV_TYPE_6x05
+#define CSR_HW_REV_TYPE_6x35 CSR_HW_REV_TYPE_6x05
+#define CSR_HW_REV_TYPE_2x30 (0x00000C0)
+#define CSR_HW_REV_TYPE_2x00 (0x0000100)
+#define CSR_HW_REV_TYPE_105 (0x0000110)
+#define CSR_HW_REV_TYPE_135 (0x0000120)
+#define CSR_HW_REV_TYPE_7265D (0x0000210)
+#define CSR_HW_REV_TYPE_NONE (0x00001F0)
/* EEPROM REG */
#define CSR_EEPROM_REG_READ_VALID_MSK (0x00000001)
diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h
index 2eb2346..68b126b 100644
--- a/drivers/net/wireless/iwlwifi/iwl-debug.h
+++ b/drivers/net/wireless/iwlwifi/iwl-debug.h
@@ -143,7 +143,7 @@
#define IWL_DL_INFO 0x00000001
#define IWL_DL_MAC80211 0x00000002
#define IWL_DL_HCMD 0x00000004
-#define IWL_DL_STATE 0x00000008
+#define IWL_DL_TDLS 0x00000008
/* 0x000000F0 - 0x00000010 */
#define IWL_DL_QUOTA 0x00000010
#define IWL_DL_TE 0x00000020
@@ -180,6 +180,7 @@
#define IWL_DL_TX_QUEUES 0x80000000
#define IWL_DEBUG_INFO(p, f, a...) IWL_DEBUG(p, IWL_DL_INFO, f, ## a)
+#define IWL_DEBUG_TDLS(p, f, a...) IWL_DEBUG(p, IWL_DL_TDLS, f, ## a)
#define IWL_DEBUG_MAC80211(p, f, a...) IWL_DEBUG(p, IWL_DL_MAC80211, f, ## a)
#define IWL_DEBUG_EXTERNAL(p, f, a...) IWL_DEBUG(p, IWL_DL_EXTERNAL, f, ## a)
#define IWL_DEBUG_TEMP(p, f, a...) IWL_DEBUG(p, IWL_DL_TEMP, f, ## a)
diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c
index 5e8d279..4322c55 100644
--- a/drivers/net/wireless/iwlwifi/iwl-drv.c
+++ b/drivers/net/wireless/iwlwifi/iwl-drv.c
@@ -78,9 +78,6 @@
#include "iwl-config.h"
#include "iwl-modparams.h"
-/* private includes */
-#include "iwl-fw-file.h"
-
/******************************************************************************
*
* module boiler plate
@@ -187,6 +184,11 @@
static void iwl_dealloc_ucode(struct iwl_drv *drv)
{
int i;
+
+ kfree(drv->fw.dbg_dest_tlv);
+ for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_conf_tlv); i++)
+ kfree(drv->fw.dbg_conf_tlv[i]);
+
for (i = 0; i < IWL_UCODE_TYPE_MAX; i++)
iwl_free_fw_img(drv, drv->fw.img + i);
}
@@ -248,6 +250,9 @@
/*
* Starting 8000B - FW name format has changed. This overwrites the
* previous name and uses the new format.
+ *
+ * TODO:
+ * Once there is only one supported step for 8000 family - delete this!
*/
if (drv->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) {
char rev_step[2] = {
@@ -258,6 +263,13 @@
if (CSR_HW_REV_STEP(drv->trans->hw_rev) == SILICON_A_STEP)
rev_step[0] = 0;
+ /*
+ * If hw_rev wasn't set yet - default as B-step. If it IS A-step
+ * we'll reload that FW later instead.
+ */
+ if (drv->trans->hw_rev == 0)
+ rev_step[0] = 'B';
+
snprintf(drv->firmware_name, sizeof(drv->firmware_name),
"%s%s-%s.ucode", name_pre, rev_step, tag);
}
@@ -301,6 +313,11 @@
u32 init_evtlog_ptr, init_evtlog_size, init_errlog_ptr;
u32 inst_evtlog_ptr, inst_evtlog_size, inst_errlog_ptr;
+
+ /* FW debug data parsed for driver usage */
+ struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
+ struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_MAX];
+ size_t dbg_conf_tlv_len[FW_DBG_MAX];
};
/*
@@ -574,6 +591,8 @@
char buildstr[25];
u32 build;
int num_of_cpus;
+ bool usniffer_images = false;
+ bool usniffer_req = false;
if (len < sizeof(*ucode)) {
IWL_ERR(drv, "uCode has invalid length: %zd\n", len);
@@ -807,19 +826,16 @@
iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_REGULAR,
tlv_len);
drv->fw.mvm_fw = true;
- drv->fw.img[IWL_UCODE_REGULAR].is_secure = true;
break;
case IWL_UCODE_TLV_SECURE_SEC_INIT:
iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_INIT,
tlv_len);
drv->fw.mvm_fw = true;
- drv->fw.img[IWL_UCODE_INIT].is_secure = true;
break;
case IWL_UCODE_TLV_SECURE_SEC_WOWLAN:
iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_WOWLAN,
tlv_len);
drv->fw.mvm_fw = true;
- drv->fw.img[IWL_UCODE_WOWLAN].is_secure = true;
break;
case IWL_UCODE_TLV_NUM_OF_CPU:
if (tlv_len != sizeof(u32))
@@ -849,12 +865,79 @@
capa->n_scan_channels =
le32_to_cpup((__le32 *)tlv_data);
break;
+ case IWL_UCODE_TLV_FW_DBG_DEST: {
+ struct iwl_fw_dbg_dest_tlv *dest = (void *)tlv_data;
+
+ if (pieces->dbg_dest_tlv) {
+ IWL_ERR(drv,
+ "dbg destination ignored, already exists\n");
+ break;
+ }
+
+ pieces->dbg_dest_tlv = dest;
+ IWL_INFO(drv, "Found debug destination: %s\n",
+ get_fw_dbg_mode_string(dest->monitor_mode));
+
+ drv->fw.dbg_dest_reg_num =
+ tlv_len - offsetof(struct iwl_fw_dbg_dest_tlv,
+ reg_ops);
+ drv->fw.dbg_dest_reg_num /=
+ sizeof(drv->fw.dbg_dest_tlv->reg_ops[0]);
+
+ break;
+ }
+ case IWL_UCODE_TLV_FW_DBG_CONF: {
+ struct iwl_fw_dbg_conf_tlv *conf = (void *)tlv_data;
+
+ if (!pieces->dbg_dest_tlv) {
+ IWL_ERR(drv,
+ "Ignore dbg config %d - no destination configured\n",
+ conf->id);
+ break;
+ }
+
+ if (conf->id >= ARRAY_SIZE(drv->fw.dbg_conf_tlv)) {
+ IWL_ERR(drv,
+ "Skip unknown configuration: %d\n",
+ conf->id);
+ break;
+ }
+
+ if (pieces->dbg_conf_tlv[conf->id]) {
+ IWL_ERR(drv,
+ "Ignore duplicate dbg config %d\n",
+ conf->id);
+ break;
+ }
+
+ if (conf->usniffer)
+ usniffer_req = true;
+
+ IWL_INFO(drv, "Found debug configuration: %d\n",
+ conf->id);
+
+ pieces->dbg_conf_tlv[conf->id] = conf;
+ pieces->dbg_conf_tlv_len[conf->id] = tlv_len;
+ break;
+ }
+ case IWL_UCODE_TLV_SEC_RT_USNIFFER:
+ usniffer_images = true;
+ iwl_store_ucode_sec(pieces, tlv_data,
+ IWL_UCODE_REGULAR_USNIFFER,
+ tlv_len);
+ break;
default:
IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type);
break;
}
}
+ if (usniffer_req && !usniffer_images) {
+ IWL_ERR(drv,
+ "user selected to work with usniffer but usniffer image isn't available in ucode package\n");
+ return -EINVAL;
+ }
+
if (len) {
IWL_ERR(drv, "invalid TLV after parsing: %zd\n", len);
iwl_print_hex_dump(drv, IWL_DL_FW, (u8 *)data, len);
@@ -992,13 +1075,14 @@
struct iwl_ucode_header *ucode;
struct iwlwifi_opmode_table *op;
int err;
- struct iwl_firmware_pieces pieces;
+ struct iwl_firmware_pieces *pieces;
const unsigned int api_max = drv->cfg->ucode_api_max;
unsigned int api_ok = drv->cfg->ucode_api_ok;
const unsigned int api_min = drv->cfg->ucode_api_min;
u32 api_ver;
int i;
bool load_module = false;
+ u32 hw_rev = drv->trans->hw_rev;
fw->ucode_capa.max_probe_length = IWL_DEFAULT_MAX_PROBE_LENGTH;
fw->ucode_capa.standard_phy_calibration_size =
@@ -1008,7 +1092,9 @@
if (!api_ok)
api_ok = api_max;
- memset(&pieces, 0, sizeof(pieces));
+ pieces = kzalloc(sizeof(*pieces), GFP_KERNEL);
+ if (!pieces)
+ return;
if (!ucode_raw) {
if (drv->fw_index <= api_ok)
@@ -1031,10 +1117,10 @@
ucode = (struct iwl_ucode_header *)ucode_raw->data;
if (ucode->ver)
- err = iwl_parse_v1_v2_firmware(drv, ucode_raw, &pieces);
+ err = iwl_parse_v1_v2_firmware(drv, ucode_raw, pieces);
else
- err = iwl_parse_tlv_firmware(drv, ucode_raw, &pieces,
- &fw->ucode_capa);
+ err = iwl_parse_tlv_firmware(drv, ucode_raw, pieces,
+ &fw->ucode_capa);
if (err)
goto try_again;
@@ -1074,7 +1160,7 @@
* In mvm uCode there is no difference between data and instructions
* sections.
*/
- if (!fw->mvm_fw && validate_sec_sizes(drv, &pieces, drv->cfg))
+ if (!fw->mvm_fw && validate_sec_sizes(drv, pieces, drv->cfg))
goto try_again;
/* Allocate ucode buffers for card's bus-master loading ... */
@@ -1083,9 +1169,33 @@
* 1) unmodified from disk
* 2) backup cache for save/restore during power-downs */
for (i = 0; i < IWL_UCODE_TYPE_MAX; i++)
- if (iwl_alloc_ucode(drv, &pieces, i))
+ if (iwl_alloc_ucode(drv, pieces, i))
goto out_free_fw;
+ if (pieces->dbg_dest_tlv) {
+ drv->fw.dbg_dest_tlv =
+ kmemdup(pieces->dbg_dest_tlv,
+ sizeof(*pieces->dbg_dest_tlv) +
+ sizeof(pieces->dbg_dest_tlv->reg_ops[0]) *
+ drv->fw.dbg_dest_reg_num, GFP_KERNEL);
+
+ if (!drv->fw.dbg_dest_tlv)
+ goto out_free_fw;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_conf_tlv); i++) {
+ if (pieces->dbg_conf_tlv[i]) {
+ drv->fw.dbg_conf_tlv_len[i] =
+ pieces->dbg_conf_tlv_len[i];
+ drv->fw.dbg_conf_tlv[i] =
+ kmemdup(pieces->dbg_conf_tlv[i],
+ drv->fw.dbg_conf_tlv_len[i],
+ GFP_KERNEL);
+ if (!drv->fw.dbg_conf_tlv[i])
+ goto out_free_fw;
+ }
+ }
+
/* Now that we can no longer fail, copy information */
/*
@@ -1093,20 +1203,20 @@
* for each event, which is of mode 1 (including timestamp) for all
* new microcodes that include this information.
*/
- fw->init_evtlog_ptr = pieces.init_evtlog_ptr;
- if (pieces.init_evtlog_size)
- fw->init_evtlog_size = (pieces.init_evtlog_size - 16)/12;
+ fw->init_evtlog_ptr = pieces->init_evtlog_ptr;
+ if (pieces->init_evtlog_size)
+ fw->init_evtlog_size = (pieces->init_evtlog_size - 16)/12;
else
fw->init_evtlog_size =
drv->cfg->base_params->max_event_log_size;
- fw->init_errlog_ptr = pieces.init_errlog_ptr;
- fw->inst_evtlog_ptr = pieces.inst_evtlog_ptr;
- if (pieces.inst_evtlog_size)
- fw->inst_evtlog_size = (pieces.inst_evtlog_size - 16)/12;
+ fw->init_errlog_ptr = pieces->init_errlog_ptr;
+ fw->inst_evtlog_ptr = pieces->inst_evtlog_ptr;
+ if (pieces->inst_evtlog_size)
+ fw->inst_evtlog_size = (pieces->inst_evtlog_size - 16)/12;
else
fw->inst_evtlog_size =
drv->cfg->base_params->max_event_log_size;
- fw->inst_errlog_ptr = pieces.inst_errlog_ptr;
+ fw->inst_errlog_ptr = pieces->inst_errlog_ptr;
/*
* figure out the offset of chain noise reset and gain commands
@@ -1165,10 +1275,55 @@
op->name, err);
#endif
}
+
+ /*
+ * We may have loaded the wrong FW file in 8000 HW family if it is an
+ * A-step card, and if drv->trans->hw_rev wasn't properly read when
+ * the FW file had been loaded. (This might happen in SDIO.) In such a
+ * case - unload and reload the correct file.
+ *
+ * TODO:
+ * Once there is only one supported step for 8000 family - delete this!
+ */
+ if (drv->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000 &&
+ CSR_HW_REV_STEP(drv->trans->hw_rev) == SILICON_A_STEP &&
+ drv->trans->hw_rev != hw_rev) {
+ char firmware_name[32];
+
+ /* Free previous FW resources */
+ if (drv->op_mode)
+ _iwl_op_mode_stop(drv);
+ iwl_dealloc_ucode(drv);
+
+ /* Build name of correct-step FW */
+ snprintf(firmware_name, sizeof(firmware_name),
+ strrchr(drv->firmware_name, '-'));
+ snprintf(drv->firmware_name, sizeof(drv->firmware_name),
+ "%s%s", drv->cfg->fw_name_pre, firmware_name);
+
+ /* Clear data before loading correct FW */
+ list_del(&drv->list);
+
+ /* Request correct FW file this time */
+ IWL_DEBUG_INFO(drv, "attempting to load A-step FW %s\n",
+ drv->firmware_name);
+ err = request_firmware(&ucode_raw, drv->firmware_name,
+ drv->trans->dev);
+ if (err) {
+ IWL_ERR(drv, "Failed swapping FW!\n");
+ goto out_unbind;
+ }
+
+ /* Redo callback function - this time with right FW */
+ iwl_req_fw_callback(ucode_raw, context);
+ }
+
+ kfree(pieces);
return;
try_again:
/* try next, if any */
+ kfree(pieces);
release_firmware(ucode_raw);
if (iwl_request_firmware(drv, false))
goto out_unbind;
@@ -1179,6 +1334,7 @@
iwl_dealloc_ucode(drv);
release_firmware(ucode_raw);
out_unbind:
+ kfree(pieces);
complete(&drv->request_firmware_complete);
device_release_driver(drv->trans->dev);
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c
index 74b796d..41ff85d 100644
--- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c
+++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c
@@ -764,7 +764,7 @@
if (iwlwifi_mod_params.amsdu_size_8K)
ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU;
- ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
+ ht_info->ampdu_factor = cfg->max_ht_ampdu_exponent;
ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_4;
ht_info->mcs.rx_mask[0] = 0xFF;
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h
index e30a41d..20a8a64 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h
@@ -81,6 +81,7 @@
* @IWL_FW_ERROR_DUMP_FW_MONITOR: firmware monitor
* @IWL_FW_ERROR_DUMP_PRPH: range of periphery registers - there can be several
* sections like this in a single file.
+ * @IWL_FW_ERROR_DUMP_FH_REGS: range of FH registers
*/
enum iwl_fw_error_dump_type {
IWL_FW_ERROR_DUMP_SRAM = 0,
@@ -90,6 +91,8 @@
IWL_FW_ERROR_DUMP_DEV_FW_INFO = 4,
IWL_FW_ERROR_DUMP_FW_MONITOR = 5,
IWL_FW_ERROR_DUMP_PRPH = 6,
+ IWL_FW_ERROR_DUMP_TXF = 7,
+ IWL_FW_ERROR_DUMP_FH_REGS = 8,
IWL_FW_ERROR_DUMP_MAX,
};
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h
index 401f7be..f2a047f 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fw-file.h
@@ -131,6 +131,9 @@
IWL_UCODE_TLV_API_CHANGES_SET = 29,
IWL_UCODE_TLV_ENABLED_CAPABILITIES = 30,
IWL_UCODE_TLV_N_SCAN_CHANNELS = 31,
+ IWL_UCODE_TLV_SEC_RT_USNIFFER = 34,
+ IWL_UCODE_TLV_FW_DBG_DEST = 38,
+ IWL_UCODE_TLV_FW_DBG_CONF = 39,
};
struct iwl_ucode_tlv {
@@ -179,4 +182,309 @@
__le32 api_capa;
} __packed;
+/**
+ * enum iwl_ucode_tlv_flag - ucode API flags
+ * @IWL_UCODE_TLV_FLAGS_PAN: This is PAN capable microcode; this previously
+ * was a separate TLV but moved here to save space.
+ * @IWL_UCODE_TLV_FLAGS_NEWSCAN: new uCode scan behaviour on hidden SSID,
+ * treats good CRC threshold as a boolean
+ * @IWL_UCODE_TLV_FLAGS_MFP: This uCode image supports MFP (802.11w).
+ * @IWL_UCODE_TLV_FLAGS_P2P: This uCode image supports P2P.
+ * @IWL_UCODE_TLV_FLAGS_DW_BC_TABLE: The SCD byte count table is in DWORDS
+ * @IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT: This uCode image supports uAPSD
+ * @IWL_UCODE_TLV_FLAGS_SHORT_BL: 16 entries of black list instead of 64 in scan
+ * offload profile config command.
+ * @IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS: D3 image supports up to six
+ * (rather than two) IPv6 addresses
+ * @IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID: not sending a probe with the SSID element
+ * from the probe request template.
+ * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL: new NS offload (small version)
+ * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE: new NS offload (large version)
+ * @IWL_UCODE_TLV_FLAGS_P2P_PM: P2P client supports PM as a stand alone MAC
+ * @IWL_UCODE_TLV_FLAGS_P2P_BSS_PS_DCM: support power save on BSS station and
+ * P2P client interfaces simultaneously if they are in different bindings.
+ * @IWL_UCODE_TLV_FLAGS_P2P_BSS_PS_SCM: support power save on BSS station and
+ * P2P client interfaces simultaneously if they are in same bindings.
+ * @IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT: General support for uAPSD
+ * @IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD: P2P client supports uAPSD power save
+ * @IWL_UCODE_TLV_FLAGS_BCAST_FILTERING: uCode supports broadcast filtering.
+ * @IWL_UCODE_TLV_FLAGS_GO_UAPSD: AP/GO interfaces support uAPSD clients
+ * @IWL_UCODE_TLV_FLAGS_EBS_SUPPORT: this uCode image supports EBS.
+ */
+enum iwl_ucode_tlv_flag {
+ IWL_UCODE_TLV_FLAGS_PAN = BIT(0),
+ IWL_UCODE_TLV_FLAGS_NEWSCAN = BIT(1),
+ IWL_UCODE_TLV_FLAGS_MFP = BIT(2),
+ IWL_UCODE_TLV_FLAGS_P2P = BIT(3),
+ IWL_UCODE_TLV_FLAGS_DW_BC_TABLE = BIT(4),
+ IWL_UCODE_TLV_FLAGS_SHORT_BL = BIT(7),
+ IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS = BIT(10),
+ IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID = BIT(12),
+ IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL = BIT(15),
+ IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE = BIT(16),
+ IWL_UCODE_TLV_FLAGS_P2P_PM = BIT(21),
+ IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM = BIT(22),
+ IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_SCM = BIT(23),
+ IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT = BIT(24),
+ IWL_UCODE_TLV_FLAGS_EBS_SUPPORT = BIT(25),
+ IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD = BIT(26),
+ IWL_UCODE_TLV_FLAGS_BCAST_FILTERING = BIT(29),
+ IWL_UCODE_TLV_FLAGS_GO_UAPSD = BIT(30),
+};
+
+/**
+ * enum iwl_ucode_tlv_api - ucode api
+ * @IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID: wowlan config includes tid field.
+ * @IWL_UCODE_TLV_CAPA_EXTENDED_BEACON: Support Extended beacon notification
+ * @IWL_UCODE_TLV_API_BT_COEX_SPLIT: new API for BT Coex
+ * @IWL_UCODE_TLV_API_CSA_FLOW: ucode can do unbind-bind flow for CSA.
+ * @IWL_UCODE_TLV_API_DISABLE_STA_TX: ucode supports tx_disable bit.
+ * @IWL_UCODE_TLV_API_LMAC_SCAN: This ucode uses LMAC unified scan API.
+ * @IWL_UCODE_TLV_API_SF_NO_DUMMY_NOTIF: ucode supports disabling dummy notif.
+ * @IWL_UCODE_TLV_API_FRAGMENTED_SCAN: This ucode supports active dwell time
+ * longer than the passive one, which is essential for fragmented scan.
+ */
+enum iwl_ucode_tlv_api {
+ IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID = BIT(0),
+ IWL_UCODE_TLV_CAPA_EXTENDED_BEACON = BIT(1),
+ IWL_UCODE_TLV_API_BT_COEX_SPLIT = BIT(3),
+ IWL_UCODE_TLV_API_CSA_FLOW = BIT(4),
+ IWL_UCODE_TLV_API_DISABLE_STA_TX = BIT(5),
+ IWL_UCODE_TLV_API_LMAC_SCAN = BIT(6),
+ IWL_UCODE_TLV_API_SF_NO_DUMMY_NOTIF = BIT(7),
+ IWL_UCODE_TLV_API_FRAGMENTED_SCAN = BIT(8),
+};
+
+/**
+ * enum iwl_ucode_tlv_capa - ucode capabilities
+ * @IWL_UCODE_TLV_CAPA_D0I3_SUPPORT: supports D0i3
+ * @IWL_UCODE_TLV_CAPA_LAR_SUPPORT: supports Location Aware Regulatory
+ * @IWL_UCODE_TLV_CAPA_UMAC_SCAN: supports UMAC scan.
+ * @IWL_UCODE_TLV_CAPA_TDLS_SUPPORT: support basic TDLS functionality
+ * @IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT: supports insertion of current
+ * tx power value into TPC Report action frame and Link Measurement Report
+ * action frame
+ * @IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT: supports updating current
+ * channel in DS parameter set element in probe requests.
+ * @IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT: supports adding TPC Report IE in
+ * probe requests.
+ * @IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT: supports Quiet Period requests
+ * @IWL_UCODE_TLV_CAPA_DQA_SUPPORT: supports dynamic queue allocation (DQA),
+ * which also implies support for the scheduler configuration command
+ * @IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH: supports TDLS channel switching
+ * @IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT: supports Hot Spot Command
+ */
+enum iwl_ucode_tlv_capa {
+ IWL_UCODE_TLV_CAPA_D0I3_SUPPORT = BIT(0),
+ IWL_UCODE_TLV_CAPA_LAR_SUPPORT = BIT(1),
+ IWL_UCODE_TLV_CAPA_UMAC_SCAN = BIT(2),
+ IWL_UCODE_TLV_CAPA_TDLS_SUPPORT = BIT(6),
+ IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT = BIT(8),
+ IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT = BIT(9),
+ IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT = BIT(10),
+ IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT = BIT(11),
+ IWL_UCODE_TLV_CAPA_DQA_SUPPORT = BIT(12),
+ IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH = BIT(13),
+ IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT = BIT(18),
+};
+
+/* The default calibrate table size if not specified by firmware file */
+#define IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE 18
+#define IWL_MAX_STANDARD_PHY_CALIBRATE_TBL_SIZE 19
+#define IWL_MAX_PHY_CALIBRATE_TBL_SIZE 253
+
+/* The default max probe length if not specified by the firmware file */
+#define IWL_DEFAULT_MAX_PROBE_LENGTH 200
+
+/*
+ * For 16.0 uCode and above, there is no differentiation between sections,
+ * just an offset to the HW address.
+ */
+#define IWL_UCODE_SECTION_MAX 12
+#define IWL_API_ARRAY_SIZE 1
+#define IWL_CAPABILITIES_ARRAY_SIZE 1
+#define CPU1_CPU2_SEPARATOR_SECTION 0xFFFFCCCC
+
+/* uCode version contains 4 values: Major/Minor/API/Serial */
+#define IWL_UCODE_MAJOR(ver) (((ver) & 0xFF000000) >> 24)
+#define IWL_UCODE_MINOR(ver) (((ver) & 0x00FF0000) >> 16)
+#define IWL_UCODE_API(ver) (((ver) & 0x0000FF00) >> 8)
+#define IWL_UCODE_SERIAL(ver) ((ver) & 0x000000FF)
+
+/*
+ * Calibration control struct.
+ * Sent as part of the phy configuration command.
+ * @flow_trigger: bitmap for which calibrations to perform according to
+ * flow triggers.
+ * @event_trigger: bitmap for which calibrations to perform according to
+ * event triggers.
+ */
+struct iwl_tlv_calib_ctrl {
+ __le32 flow_trigger;
+ __le32 event_trigger;
+} __packed;
+
+enum iwl_fw_phy_cfg {
+ FW_PHY_CFG_RADIO_TYPE_POS = 0,
+ FW_PHY_CFG_RADIO_TYPE = 0x3 << FW_PHY_CFG_RADIO_TYPE_POS,
+ FW_PHY_CFG_RADIO_STEP_POS = 2,
+ FW_PHY_CFG_RADIO_STEP = 0x3 << FW_PHY_CFG_RADIO_STEP_POS,
+ FW_PHY_CFG_RADIO_DASH_POS = 4,
+ FW_PHY_CFG_RADIO_DASH = 0x3 << FW_PHY_CFG_RADIO_DASH_POS,
+ FW_PHY_CFG_TX_CHAIN_POS = 16,
+ FW_PHY_CFG_TX_CHAIN = 0xf << FW_PHY_CFG_TX_CHAIN_POS,
+ FW_PHY_CFG_RX_CHAIN_POS = 20,
+ FW_PHY_CFG_RX_CHAIN = 0xf << FW_PHY_CFG_RX_CHAIN_POS,
+};
+
+#define IWL_UCODE_MAX_CS 1
+
+/**
+ * struct iwl_fw_cipher_scheme - a cipher scheme supported by FW.
+ * @cipher: a cipher suite selector
+ * @flags: cipher scheme flags (currently reserved for a future use)
+ * @hdr_len: a size of MPDU security header
+ * @pn_len: a size of PN
+ * @pn_off: an offset of pn from the beginning of the security header
+ * @key_idx_off: an offset of key index byte in the security header
+ * @key_idx_mask: a bit mask of key_idx bits
+ * @key_idx_shift: bit shift needed to get key_idx
+ * @mic_len: mic length in bytes
+ * @hw_cipher: a HW cipher index used in host commands
+ */
+struct iwl_fw_cipher_scheme {
+ __le32 cipher;
+ u8 flags;
+ u8 hdr_len;
+ u8 pn_len;
+ u8 pn_off;
+ u8 key_idx_off;
+ u8 key_idx_mask;
+ u8 key_idx_shift;
+ u8 mic_len;
+ u8 hw_cipher;
+} __packed;
+
+enum iwl_fw_dbg_reg_operator {
+ CSR_ASSIGN,
+ CSR_SETBIT,
+ CSR_CLEARBIT,
+
+ PRPH_ASSIGN,
+ PRPH_SETBIT,
+ PRPH_CLEARBIT,
+};
+
+/**
+ * struct iwl_fw_dbg_reg_op - an operation on a register
+ *
+ * @op: %enum iwl_fw_dbg_reg_operator
+ * @addr: offset of the register
+ * @val: value
+ */
+struct iwl_fw_dbg_reg_op {
+ u8 op;
+ u8 reserved[3];
+ __le32 addr;
+ __le32 val;
+} __packed;
+
+/**
+ * enum iwl_fw_dbg_monitor_mode - available monitor recording modes
+ *
+ * @SMEM_MODE: monitor stores the data in SMEM
+ * @EXTERNAL_MODE: monitor stores the data in allocated DRAM
+ * @MARBH_MODE: monitor stores the data in MARBH buffer
+ */
+enum iwl_fw_dbg_monitor_mode {
+ SMEM_MODE = 0,
+ EXTERNAL_MODE = 1,
+ MARBH_MODE = 2,
+};
+
+/**
+ * struct iwl_fw_dbg_dest_tlv - configures the destination of the debug data
+ *
+ * @version: version of the TLV - currently 0
+ * @monitor_mode: %enum iwl_fw_dbg_monitor_mode
+ * @base_reg: addr of the base addr register (PRPH)
+ * @end_reg: addr of the end addr register (PRPH)
+ * @write_ptr_reg: the addr of the reg of the write pointer
+ * @wrap_count: the addr of the reg of the wrap_count
+ * @base_shift: shift right of the base addr reg
+ * @end_shift: shift right of the end addr reg
+ * @reg_ops: array of registers operations
+ *
+ * This parses IWL_UCODE_TLV_FW_DBG_DEST
+ */
+struct iwl_fw_dbg_dest_tlv {
+ u8 version;
+ u8 monitor_mode;
+ u8 reserved[2];
+ __le32 base_reg;
+ __le32 end_reg;
+ __le32 write_ptr_reg;
+ __le32 wrap_count;
+ u8 base_shift;
+ u8 end_shift;
+ struct iwl_fw_dbg_reg_op reg_ops[0];
+} __packed;
+
+struct iwl_fw_dbg_conf_hcmd {
+ u8 id;
+ u8 reserved;
+ __le16 len;
+ u8 data[0];
+} __packed;
+
+/**
+ * struct iwl_fw_dbg_trigger - a TLV that describes a debug configuration
+ *
+ * @enabled: is this trigger enabled
+ * @reserved:
+ * @len: length, in bytes, of the %trigger field
+ * @trigger: pointer to a trigger struct
+ */
+struct iwl_fw_dbg_trigger {
+ u8 enabled;
+ u8 reserved;
+ u8 len;
+ u8 trigger[0];
+} __packed;
+
+/**
+ * enum iwl_fw_dbg_conf - configurations available
+ *
+ * @FW_DBG_CUSTOM: take this configuration from alive
+ * Note that the trigger is NO-OP for this configuration
+ */
+enum iwl_fw_dbg_conf {
+ FW_DBG_CUSTOM = 0,
+
+ /* must be last */
+ FW_DBG_MAX,
+ FW_DBG_INVALID = 0xff,
+};
+
+/**
+ * struct iwl_fw_dbg_conf_tlv - a TLV that describes a debug configuration
+ *
+ * @id: %enum iwl_fw_dbg_conf
+ * @usniffer: should the uSniffer image be used
+ * @num_of_hcmds: how many HCMDs to send are present here
+ * @hcmd: a variable length host command to be sent to apply the configuration.
+ * If there is more than one HCMD to send, they will appear one after the
+ * other and be sent in the order that they appear in.
+ * This parses IWL_UCODE_TLV_FW_DBG_CONF
+ */
+struct iwl_fw_dbg_conf_tlv {
+ u8 id;
+ u8 usniffer;
+ u8 reserved;
+ u8 num_of_hcmds;
+ struct iwl_fw_dbg_conf_hcmd hcmd;
+
+ /* struct iwl_fw_dbg_trigger sits after all variable length hcmds */
+} __packed;
+
#endif /* __iwl_fw_file_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h
index 4f6e668..e6dc3b8 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fw.h
@@ -70,110 +70,6 @@
#include "iwl-fw-file.h"
/**
- * enum iwl_ucode_tlv_flag - ucode API flags
- * @IWL_UCODE_TLV_FLAGS_PAN: This is PAN capable microcode; this previously
- * was a separate TLV but moved here to save space.
- * @IWL_UCODE_TLV_FLAGS_NEWSCAN: new uCode scan behaviour on hidden SSID,
- * treats good CRC threshold as a boolean
- * @IWL_UCODE_TLV_FLAGS_MFP: This uCode image supports MFP (802.11w).
- * @IWL_UCODE_TLV_FLAGS_P2P: This uCode image supports P2P.
- * @IWL_UCODE_TLV_FLAGS_DW_BC_TABLE: The SCD byte count table is in DWORDS
- * @IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT: This uCode image supports uAPSD
- * @IWL_UCODE_TLV_FLAGS_SHORT_BL: 16 entries of black list instead of 64 in scan
- * offload profile config command.
- * @IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS: D3 image supports up to six
- * (rather than two) IPv6 addresses
- * @IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID: not sending a probe with the SSID element
- * from the probe request template.
- * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL: new NS offload (small version)
- * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE: new NS offload (large version)
- * @IWL_UCODE_TLV_FLAGS_P2P_PM: P2P client supports PM as a stand alone MAC
- * @IWL_UCODE_TLV_FLAGS_P2P_BSS_PS_DCM: support power save on BSS station and
- * P2P client interfaces simultaneously if they are in different bindings.
- * @IWL_UCODE_TLV_FLAGS_P2P_BSS_PS_SCM: support power save on BSS station and
- * P2P client interfaces simultaneously if they are in same bindings.
- * @IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT: General support for uAPSD
- * @IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD: P2P client supports uAPSD power save
- * @IWL_UCODE_TLV_FLAGS_BCAST_FILTERING: uCode supports broadcast filtering.
- * @IWL_UCODE_TLV_FLAGS_GO_UAPSD: AP/GO interfaces support uAPSD clients
- * @IWL_UCODE_TLV_FLAGS_EBS_SUPPORT: this uCode image supports EBS.
- */
-enum iwl_ucode_tlv_flag {
- IWL_UCODE_TLV_FLAGS_PAN = BIT(0),
- IWL_UCODE_TLV_FLAGS_NEWSCAN = BIT(1),
- IWL_UCODE_TLV_FLAGS_MFP = BIT(2),
- IWL_UCODE_TLV_FLAGS_P2P = BIT(3),
- IWL_UCODE_TLV_FLAGS_DW_BC_TABLE = BIT(4),
- IWL_UCODE_TLV_FLAGS_SHORT_BL = BIT(7),
- IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS = BIT(10),
- IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID = BIT(12),
- IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL = BIT(15),
- IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE = BIT(16),
- IWL_UCODE_TLV_FLAGS_P2P_PM = BIT(21),
- IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM = BIT(22),
- IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_SCM = BIT(23),
- IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT = BIT(24),
- IWL_UCODE_TLV_FLAGS_EBS_SUPPORT = BIT(25),
- IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD = BIT(26),
- IWL_UCODE_TLV_FLAGS_BCAST_FILTERING = BIT(29),
- IWL_UCODE_TLV_FLAGS_GO_UAPSD = BIT(30),
-};
-
-/**
- * enum iwl_ucode_tlv_api - ucode api
- * @IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID: wowlan config includes tid field.
- * @IWL_UCODE_TLV_CAPA_EXTENDED_BEACON: Support Extended beacon notification
- * @IWL_UCODE_TLV_API_BT_COEX_SPLIT: new API for BT Coex
- * @IWL_UCODE_TLV_API_CSA_FLOW: ucode can do unbind-bind flow for CSA.
- * @IWL_UCODE_TLV_API_DISABLE_STA_TX: ucode supports tx_disable bit.
- * @IWL_UCODE_TLV_API_LMAC_SCAN: This ucode uses LMAC unified scan API.
- * @IWL_UCODE_TLV_API_SF_NO_DUMMY_NOTIF: ucode supports disabling dummy notif.
- * @IWL_UCODE_TLV_API_FRAGMENTED_SCAN: This ucode supports active dwell time
- * longer than the passive one, which is essential for fragmented scan.
- */
-enum iwl_ucode_tlv_api {
- IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID = BIT(0),
- IWL_UCODE_TLV_CAPA_EXTENDED_BEACON = BIT(1),
- IWL_UCODE_TLV_API_BT_COEX_SPLIT = BIT(3),
- IWL_UCODE_TLV_API_CSA_FLOW = BIT(4),
- IWL_UCODE_TLV_API_DISABLE_STA_TX = BIT(5),
- IWL_UCODE_TLV_API_LMAC_SCAN = BIT(6),
- IWL_UCODE_TLV_API_SF_NO_DUMMY_NOTIF = BIT(7),
- IWL_UCODE_TLV_API_FRAGMENTED_SCAN = BIT(8),
-};
-
-/**
- * enum iwl_ucode_tlv_capa - ucode capabilities
- * @IWL_UCODE_TLV_CAPA_D0I3_SUPPORT: supports D0i3
- * @IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT: supports insertion of current
- * tx power value into TPC Report action frame and Link Measurement Report
- * action frame
- * @IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT: supports adding DS params
- * element in probe requests.
- * @IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT: supports adding TPC Report IE in
- * probe requests.
- * @IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT: supports Quiet Period requests
- * @IWL_UCODE_TLV_CAPA_DQA_SUPPORT: supports dynamic queue allocation (DQA),
- * which also implies support for the scheduler configuration command
- */
-enum iwl_ucode_tlv_capa {
- IWL_UCODE_TLV_CAPA_D0I3_SUPPORT = BIT(0),
- IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT = BIT(8),
- IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT = BIT(9),
- IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT = BIT(10),
- IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT = BIT(11),
- IWL_UCODE_TLV_CAPA_DQA_SUPPORT = BIT(12),
-};
-
-/* The default calibrate table size if not specified by firmware file */
-#define IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE 18
-#define IWL_MAX_STANDARD_PHY_CALIBRATE_TBL_SIZE 19
-#define IWL_MAX_PHY_CALIBRATE_TBL_SIZE 253
-
-/* The default max probe length if not specified by the firmware file */
-#define IWL_DEFAULT_MAX_PROBE_LENGTH 200
-
-/**
* enum iwl_ucode_type
*
* The type of ucode.
@@ -181,11 +77,13 @@
* @IWL_UCODE_REGULAR: Normal runtime ucode
* @IWL_UCODE_INIT: Initial ucode
* @IWL_UCODE_WOWLAN: Wake on Wireless enabled ucode
+ * @IWL_UCODE_REGULAR_USNIFFER: Normal runtime ucode when using usniffer image
*/
enum iwl_ucode_type {
IWL_UCODE_REGULAR,
IWL_UCODE_INIT,
IWL_UCODE_WOWLAN,
+ IWL_UCODE_REGULAR_USNIFFER,
IWL_UCODE_TYPE_MAX,
};
@@ -200,14 +98,6 @@
IWL_UCODE_SECTION_DATA,
IWL_UCODE_SECTION_INST,
};
-/*
- * For 16.0 uCode and above, there is no differentiation between sections,
- * just an offset to the HW address.
- */
-#define IWL_UCODE_SECTION_MAX 12
-#define IWL_API_ARRAY_SIZE 1
-#define IWL_CAPABILITIES_ARRAY_SIZE 1
-#define CPU1_CPU2_SEPARATOR_SECTION 0xFFFFCCCC
struct iwl_ucode_capabilities {
u32 max_probe_length;
@@ -227,7 +117,6 @@
struct fw_img {
struct fw_desc sec[IWL_UCODE_SECTION_MAX];
- bool is_secure;
bool is_dual_cpus;
};
@@ -236,66 +125,6 @@
u32 size;
};
-/* uCode version contains 4 values: Major/Minor/API/Serial */
-#define IWL_UCODE_MAJOR(ver) (((ver) & 0xFF000000) >> 24)
-#define IWL_UCODE_MINOR(ver) (((ver) & 0x00FF0000) >> 16)
-#define IWL_UCODE_API(ver) (((ver) & 0x0000FF00) >> 8)
-#define IWL_UCODE_SERIAL(ver) ((ver) & 0x000000FF)
-
-/*
- * Calibration control struct.
- * Sent as part of the phy configuration command.
- * @flow_trigger: bitmap for which calibrations to perform according to
- * flow triggers.
- * @event_trigger: bitmap for which calibrations to perform according to
- * event triggers.
- */
-struct iwl_tlv_calib_ctrl {
- __le32 flow_trigger;
- __le32 event_trigger;
-} __packed;
-
-enum iwl_fw_phy_cfg {
- FW_PHY_CFG_RADIO_TYPE_POS = 0,
- FW_PHY_CFG_RADIO_TYPE = 0x3 << FW_PHY_CFG_RADIO_TYPE_POS,
- FW_PHY_CFG_RADIO_STEP_POS = 2,
- FW_PHY_CFG_RADIO_STEP = 0x3 << FW_PHY_CFG_RADIO_STEP_POS,
- FW_PHY_CFG_RADIO_DASH_POS = 4,
- FW_PHY_CFG_RADIO_DASH = 0x3 << FW_PHY_CFG_RADIO_DASH_POS,
- FW_PHY_CFG_TX_CHAIN_POS = 16,
- FW_PHY_CFG_TX_CHAIN = 0xf << FW_PHY_CFG_TX_CHAIN_POS,
- FW_PHY_CFG_RX_CHAIN_POS = 20,
- FW_PHY_CFG_RX_CHAIN = 0xf << FW_PHY_CFG_RX_CHAIN_POS,
-};
-
-#define IWL_UCODE_MAX_CS 1
-
-/**
- * struct iwl_fw_cipher_scheme - a cipher scheme supported by FW.
- * @cipher: a cipher suite selector
- * @flags: cipher scheme flags (currently reserved for a future use)
- * @hdr_len: a size of MPDU security header
- * @pn_len: a size of PN
- * @pn_off: an offset of pn from the beginning of the security header
- * @key_idx_off: an offset of key index byte in the security header
- * @key_idx_mask: a bit mask of key_idx bits
- * @key_idx_shift: bit shift needed to get key_idx
- * @mic_len: mic length in bytes
- * @hw_cipher: a HW cipher index used in host commands
- */
-struct iwl_fw_cipher_scheme {
- __le32 cipher;
- u8 flags;
- u8 hdr_len;
- u8 pn_len;
- u8 pn_off;
- u8 key_idx_off;
- u8 key_idx_mask;
- u8 key_idx_shift;
- u8 mic_len;
- u8 hw_cipher;
-} __packed;
-
/**
* struct iwl_fw_cscheme_list - a cipher scheme list
* @size: a number of entries
@@ -322,6 +151,11 @@
* @inst_errlog_ptr: error log offfset for runtime ucode.
* @mvm_fw: indicates this is MVM firmware
* @cipher_scheme: optional external cipher scheme.
+ * @human_readable: human readable version
+ * @dbg_dest_tlv: points to the destination TLV for debug
+ * @dbg_conf_tlv: array of pointers to configuration TLVs for debug
+ * @dbg_conf_tlv_len: lengths of the @dbg_conf_tlv entries
+ * @dbg_dest_reg_num: num of reg_ops in %dbg_dest_tlv
*/
struct iwl_fw {
u32 ucode_ver;
@@ -346,6 +180,68 @@
struct ieee80211_cipher_scheme cs[IWL_UCODE_MAX_CS];
u8 human_readable[FW_VER_HUMAN_READABLE_SZ];
+
+ struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
+ struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_MAX];
+ size_t dbg_conf_tlv_len[FW_DBG_MAX];
+
+ u8 dbg_dest_reg_num;
};
+static inline const char *get_fw_dbg_mode_string(int mode)
+{
+ switch (mode) {
+ case SMEM_MODE:
+ return "SMEM";
+ case EXTERNAL_MODE:
+ return "EXTERNAL_DRAM";
+ case MARBH_MODE:
+ return "MARBH";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static inline const struct iwl_fw_dbg_trigger *
+iwl_fw_dbg_conf_get_trigger(const struct iwl_fw *fw, u8 id)
+{
+ const struct iwl_fw_dbg_conf_tlv *conf_tlv = fw->dbg_conf_tlv[id];
+ u8 *ptr;
+ int i;
+
+ if (!conf_tlv)
+ return NULL;
+
+ ptr = (void *)&conf_tlv->hcmd;
+ for (i = 0; i < conf_tlv->num_of_hcmds; i++) {
+ ptr += sizeof(conf_tlv->hcmd);
+ ptr += le16_to_cpu(conf_tlv->hcmd.len);
+ }
+
+ return (const struct iwl_fw_dbg_trigger *)ptr;
+}
+
+static inline bool
+iwl_fw_dbg_conf_enabled(const struct iwl_fw *fw, u8 id)
+{
+ const struct iwl_fw_dbg_trigger *trigger =
+ iwl_fw_dbg_conf_get_trigger(fw, id);
+
+ if (!trigger)
+ return false;
+
+ return trigger->enabled;
+}
+
+static inline bool
+iwl_fw_dbg_conf_usniffer(const struct iwl_fw *fw, u8 id)
+{
+ const struct iwl_fw_dbg_conf_tlv *conf_tlv = fw->dbg_conf_tlv[id];
+
+ if (!conf_tlv)
+ return false;
+
+ return conf_tlv->usniffer;
+}
+
#endif /* __iwl_fw_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
index c302e74..06e02fc 100644
--- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
+++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
@@ -325,6 +325,8 @@
{
int num_rx_ants = num_of_ant(rx_chains);
int num_tx_ants = num_of_ant(tx_chains);
+ unsigned int max_ampdu_exponent = (cfg->max_vht_ampdu_exponent ?:
+ IEEE80211_VHT_MAX_AMPDU_1024K);
vht_cap->vht_supported = true;
@@ -332,7 +334,8 @@
IEEE80211_VHT_CAP_RXSTBC_1 |
IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT |
- 7 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
+ max_ampdu_exponent <<
+ IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
if (cfg->ht_params->ldpc)
vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC;
diff --git a/drivers/net/wireless/iwlwifi/iwl-op-mode.h b/drivers/net/wireless/iwlwifi/iwl-op-mode.h
index b6d666e..17de6d4 100644
--- a/drivers/net/wireless/iwlwifi/iwl-op-mode.h
+++ b/drivers/net/wireless/iwlwifi/iwl-op-mode.h
@@ -138,7 +138,8 @@
* @nic_config: configure NIC, called before firmware is started.
* May sleep
* @wimax_active: invoked when WiMax becomes active. May sleep
- * @enter_d0i3: configure the fw to enter d0i3. May sleep.
+ * @enter_d0i3: configure the fw to enter d0i3. return 1 to indicate d0i3
+ * entrance is aborted (e.g. due to held reference). May sleep.
* @exit_d0i3: configure the fw to exit d0i3. May sleep.
*/
struct iwl_op_mode_ops {
diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h
index 1560f45..2df51ea 100644
--- a/drivers/net/wireless/iwlwifi/iwl-prph.h
+++ b/drivers/net/wireless/iwlwifi/iwl-prph.h
@@ -322,6 +322,7 @@
LMPM_SECURE_BOOT_CONFIG_INSPECTOR_NOT_REQ = 0x00000002,
};
+#define LMPM_SECURE_BOOT_CPU1_STATUS_ADDR_B0 (0xA01E30)
#define LMPM_SECURE_BOOT_CPU1_STATUS_ADDR (0x1E30)
#define LMPM_SECURE_BOOT_CPU2_STATUS_ADDR (0x1E34)
enum secure_boot_status_reg {
@@ -333,6 +334,7 @@
LMPM_SECURE_BOOT_STATUS_SUCCESS = 0x00000003,
};
+#define FH_UCODE_LOAD_STATUS (0x1AF0)
#define CSR_UCODE_LOAD_STATUS_ADDR (0x1E70)
enum secure_load_status_reg {
LMPM_CPU_UCODE_LOADING_STARTED = 0x00000001,
@@ -352,7 +354,7 @@
#define LMPM_SECURE_CPU1_HDR_MEM_SPACE (0x420000)
#define LMPM_SECURE_CPU2_HDR_MEM_SPACE (0x420400)
-#define LMPM_SECURE_TIME_OUT (100)
+#define LMPM_SECURE_TIME_OUT (100) /* 10 micro */
/* Rx FIFO */
#define RXF_SIZE_ADDR (0xa00c88)
@@ -368,4 +370,10 @@
#define MON_BUFF_WRPTR (0xa03c44)
#define MON_BUFF_CYCLE_CNT (0xa03c48)
+/* FW chicken bits */
+#define LMPM_CHICK 0xA01FF8
+enum {
+ LMPM_CHICK_EXTENDED_ADDR_SPACE = BIT(0),
+};
+
#endif /* __iwl_prph_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h
index 98b4c95..028408a 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans.h
+++ b/drivers/net/wireless/iwlwifi/iwl-trans.h
@@ -534,10 +534,10 @@
u32 value);
void (*ref)(struct iwl_trans *trans);
void (*unref)(struct iwl_trans *trans);
+ void (*suspend)(struct iwl_trans *trans);
+ void (*resume)(struct iwl_trans *trans);
-#ifdef CPTCFG_IWLWIFI_DEBUGFS
struct iwl_trans_dump_data *(*dump_data)(struct iwl_trans *trans);
-#endif
};
/**
@@ -563,6 +563,7 @@
* Set during transport allocation.
* @hw_id_str: a string with info about HW ID. Set during transport allocation.
* @pm_support: set to true in start_hw if link pm is supported
+ * @ltr_enabled: set to true if the LTR is enabled
* @dev_cmd_pool: pool for Tx cmd allocation - for internal use only.
* The user should use iwl_trans_{alloc,free}_tx_cmd.
* @dev_cmd_headroom: room needed for the transport's private use before the
@@ -573,6 +574,9 @@
* @rx_mpdu_cmd_hdr_size: used for tracing, amount of data before the
* start of the 802.11 header in the @rx_mpdu_cmd
* @dflt_pwr_limit: default power limit fetched from the platform (ACPI)
+ * @dbg_dest_tlv: points to the destination TLV for debug
+ * @dbg_conf_tlv: array of pointers to configuration TLVs for debug
+ * @dbg_dest_reg_num: num of reg_ops in %dbg_dest_tlv
*/
struct iwl_trans {
const struct iwl_trans_ops *ops;
@@ -589,6 +593,7 @@
u8 rx_mpdu_cmd, rx_mpdu_cmd_hdr_size;
bool pm_support;
+ bool ltr_enabled;
/* The following fields are internal only */
struct kmem_cache *dev_cmd_pool;
@@ -603,6 +608,10 @@
u64 dflt_pwr_limit;
+ const struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
+ const struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_MAX];
+ u8 dbg_dest_reg_num;
+
/* pointer to trans specific struct */
/*Ensure that this pointer will always be aligned to sizeof pointer */
char trans_specific[0] __aligned(sizeof(void *));
@@ -702,7 +711,18 @@
trans->ops->unref(trans);
}
-#ifdef CPTCFG_IWLWIFI_DEBUGFS
+static inline void iwl_trans_suspend(struct iwl_trans *trans)
+{
+ if (trans->ops->suspend)
+ trans->ops->suspend(trans);
+}
+
+static inline void iwl_trans_resume(struct iwl_trans *trans)
+{
+ if (trans->ops->resume)
+ trans->ops->resume(trans);
+}
+
static inline struct iwl_trans_dump_data *
iwl_trans_dump_data(struct iwl_trans *trans)
{
@@ -710,7 +730,6 @@
return NULL;
return trans->ops->dump_data(trans);
}
-#endif
static inline int iwl_trans_send_cmd(struct iwl_trans *trans,
struct iwl_host_cmd *cmd)
diff --git a/drivers/net/wireless/iwlwifi/mvm/coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c
index 8df2021..a3bfda4 100644
--- a/drivers/net/wireless/iwlwifi/mvm/coex.c
+++ b/drivers/net/wireless/iwlwifi/mvm/coex.c
@@ -72,8 +72,6 @@
#include "mvm.h"
#include "iwl-debug.h"
-#define BT_ANTENNA_COUPLING_THRESHOLD (30)
-
const u32 iwl_bt_ctl_kill_msk[BT_KILL_MSK_MAX] = {
[BT_KILL_MSK_DEFAULT] = 0xfffffc00,
[BT_KILL_MSK_NEVER] = 0xffffffff,
@@ -302,11 +300,6 @@
},
};
-static const __le32 iwl_bt_mprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE] = {
- cpu_to_le32(0x28412201),
- cpu_to_le32(0x11118451),
-};
-
struct corunning_block_luts {
u8 range;
__le32 lut20[BT_COEX_CORUN_LUT_SIZE];
@@ -605,7 +598,7 @@
bt_cmd->max_kill = cpu_to_le32(5);
bt_cmd->bt4_antenna_isolation_thr =
- cpu_to_le32(BT_ANTENNA_COUPLING_THRESHOLD);
+ cpu_to_le32(IWL_MVM_BT_COEX_ANTENNA_COUPLING_THRS);
bt_cmd->bt4_tx_tx_delta_freq_thr = cpu_to_le32(15);
bt_cmd->bt4_tx_rx_max_freq0 = cpu_to_le32(15);
bt_cmd->override_primary_lut = cpu_to_le32(BT_COEX_INVALID_LUT);
@@ -638,8 +631,8 @@
memcpy(&bt_cmd->mplut_prio_boost, iwl_bt_prio_boost,
sizeof(iwl_bt_prio_boost));
- memcpy(&bt_cmd->multiprio_lut, iwl_bt_mprio_lut,
- sizeof(iwl_bt_mprio_lut));
+ bt_cmd->multiprio_lut[0] = cpu_to_le32(IWL_MVM_BT_COEX_MPLUT_REG0);
+ bt_cmd->multiprio_lut[1] = cpu_to_le32(IWL_MVM_BT_COEX_MPLUT_REG1);
send_cmd:
memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
@@ -1144,6 +1137,22 @@
return lut_type != BT_COEX_LOOSE_LUT;
}
+bool iwl_mvm_bt_coex_is_ant_avail(struct iwl_mvm *mvm, u8 ant)
+{
+ /* there is no other antenna, shared antenna is always available */
+ if (mvm->cfg->bt_shared_single_ant)
+ return true;
+
+ if (ant & mvm->cfg->non_shared_ant)
+ return true;
+
+ if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
+ return iwl_mvm_bt_coex_is_shared_ant_avail_old(mvm);
+
+ return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) <
+ BT_HIGH_TRAFFIC;
+}
+
bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm)
{
/* there is no other antenna, shared antenna is always available */
diff --git a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c b/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c
index 585c0ab..b3210cf 100644
--- a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c
+++ b/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c
@@ -102,8 +102,6 @@
#undef EVENT_PRIO_ANT
-#define BT_ANTENNA_COUPLING_THRESHOLD (30)
-
static int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm)
{
if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
@@ -290,11 +288,6 @@
},
};
-static const __le32 iwl_bt_mprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE] = {
- cpu_to_le32(0x28412201),
- cpu_to_le32(0x11118451),
-};
-
struct corunning_block_luts {
u8 range;
__le32 lut20[BT_COEX_CORUN_LUT_SIZE];
@@ -593,7 +586,8 @@
}
bt_cmd->max_kill = 5;
- bt_cmd->bt4_antenna_isolation_thr = BT_ANTENNA_COUPLING_THRESHOLD;
+ bt_cmd->bt4_antenna_isolation_thr =
+ IWL_MVM_BT_COEX_ANTENNA_COUPLING_THRS;
bt_cmd->bt4_antenna_isolation = iwlwifi_mod_params.ant_coupling;
bt_cmd->bt4_tx_tx_delta_freq_thr = 15;
bt_cmd->bt4_tx_rx_max_freq0 = 15;
@@ -618,7 +612,9 @@
BT_VALID_ANT_ISOLATION_THRS |
BT_VALID_TXTX_DELTA_FREQ_THRS |
BT_VALID_TXRX_MAX_FREQ_0 |
- BT_VALID_SYNC_TO_SCO);
+ BT_VALID_SYNC_TO_SCO |
+ BT_VALID_TTC |
+ BT_VALID_RRC);
if (IWL_MVM_BT_COEX_SYNC2SCO)
bt_cmd->flags |= cpu_to_le32(BT_COEX_SYNC2SCO);
@@ -634,6 +630,12 @@
bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_MULTI_PRIO_LUT);
}
+ if (IWL_MVM_BT_COEX_TTC)
+ bt_cmd->flags |= cpu_to_le32(BT_COEX_TTC);
+
+ if (IWL_MVM_BT_COEX_RRC)
+ bt_cmd->flags |= cpu_to_le32(BT_COEX_RRC);
+
if (mvm->cfg->bt_shared_single_ant)
memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant,
sizeof(iwl_single_shared_ant));
@@ -649,8 +651,8 @@
memcpy(&bt_cmd->bt_prio_boost, iwl_bt_prio_boost,
sizeof(iwl_bt_prio_boost));
- memcpy(&bt_cmd->bt4_multiprio_lut, iwl_bt_mprio_lut,
- sizeof(iwl_bt_mprio_lut));
+ bt_cmd->bt4_multiprio_lut[0] = cpu_to_le32(IWL_MVM_BT_COEX_MPLUT_REG0);
+ bt_cmd->bt4_multiprio_lut[1] = cpu_to_le32(IWL_MVM_BT_COEX_MPLUT_REG1);
send_cmd:
memset(&mvm->last_bt_notif_old, 0, sizeof(mvm->last_bt_notif_old));
@@ -830,6 +832,9 @@
if (!vif->bss_conf.assoc)
smps_mode = IEEE80211_SMPS_AUTOMATIC;
+ if (data->notif->rrc_enabled & BIT(mvmvif->phy_ctxt->id))
+ smps_mode = IEEE80211_SMPS_AUTOMATIC;
+
IWL_DEBUG_COEX(data->mvm,
"mac %d: bt_status %d bt_activity_grading %d smps_req %d\n",
mvmvif->id, data->notif->bt_status, bt_activity_grading,
@@ -1162,6 +1167,12 @@
return lut_type != BT_COEX_LOOSE_LUT;
}
+bool iwl_mvm_bt_coex_is_ant_avail_old(struct iwl_mvm *mvm, u8 ant)
+{
+ u32 ag = le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading);
+ return ag < BT_HIGH_TRAFFIC;
+}
+
bool iwl_mvm_bt_coex_is_shared_ant_avail_old(struct iwl_mvm *mvm)
{
u32 ag = le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading);
diff --git a/drivers/net/wireless/iwlwifi/mvm/constants.h b/drivers/net/wireless/iwlwifi/mvm/constants.h
index d4dfbe4..3bd9347 100644
--- a/drivers/net/wireless/iwlwifi/mvm/constants.h
+++ b/drivers/net/wireless/iwlwifi/mvm/constants.h
@@ -92,8 +92,15 @@
#define IWL_MVM_BT_COEX_SYNC2SCO 1
#define IWL_MVM_BT_COEX_CORUNNING 0
#define IWL_MVM_BT_COEX_MPLUT 1
+#define IWL_MVM_BT_COEX_RRC 1
+#define IWL_MVM_BT_COEX_TTC 1
+#define IWL_MVM_BT_COEX_MPLUT_REG0 0x28412201
+#define IWL_MVM_BT_COEX_MPLUT_REG1 0x11118451
+#define IWL_MVM_BT_COEX_ANTENNA_COUPLING_THRS 30
#define IWL_MVM_FW_MCAST_FILTER_PASS_ALL 0
+#define IWL_MVM_FW_BCAST_FILTER_PASS_ALL 0
#define IWL_MVM_QUOTA_THRESHOLD 8
#define IWL_MVM_RS_RSSI_BASED_INIT_RATE 0
+#define IWL_MVM_RS_DISABLE_MIMO 0
#endif /* __MVM_CONSTANTS_H */
diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c
index d45d6df..db454c8 100644
--- a/drivers/net/wireless/iwlwifi/mvm/d3.c
+++ b/drivers/net/wireless/iwlwifi/mvm/d3.c
@@ -601,33 +601,6 @@
return ret;
}
-struct iwl_d3_iter_data {
- struct iwl_mvm *mvm;
- struct ieee80211_vif *vif;
- bool error;
-};
-
-static void iwl_mvm_d3_iface_iterator(void *_data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- struct iwl_d3_iter_data *data = _data;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
- return;
-
- if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)
- return;
-
- if (data->vif) {
- IWL_ERR(data->mvm, "More than one managed interface active!\n");
- data->error = true;
- return;
- }
-
- data->vif = vif;
-}
-
static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
struct ieee80211_sta *ap_sta)
{
@@ -783,144 +756,8 @@
IWL_ERR(mvm, "failed to set non-QoS seqno\n");
}
-static int
-iwl_mvm_send_wowlan_config_cmd(struct iwl_mvm *mvm,
- const struct iwl_wowlan_config_cmd_v3 *cmd)
+static int iwl_mvm_switch_to_d3(struct iwl_mvm *mvm)
{
- /* start only with the v2 part of the command */
- u16 cmd_len = sizeof(cmd->common);
-
- if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID)
- cmd_len = sizeof(*cmd);
-
- return iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, 0,
- cmd_len, cmd);
-}
-
-static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
- struct cfg80211_wowlan *wowlan,
- bool test)
-{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct iwl_d3_iter_data suspend_iter_data = {
- .mvm = mvm,
- };
- struct ieee80211_vif *vif;
- struct iwl_mvm_vif *mvmvif;
- struct ieee80211_sta *ap_sta;
- struct iwl_mvm_sta *mvm_ap_sta;
- struct iwl_wowlan_config_cmd_v3 wowlan_config_cmd = {};
- struct iwl_wowlan_kek_kck_material_cmd kek_kck_cmd = {};
- struct iwl_wowlan_tkip_params_cmd tkip_cmd = {};
- struct iwl_d3_manager_config d3_cfg_cmd_data = {
- /*
- * Program the minimum sleep time to 10 seconds, as many
- * platforms have issues processing a wakeup signal while
- * still being in the process of suspending.
- */
- .min_sleep_time = cpu_to_le32(10 * 1000 * 1000),
- };
- struct iwl_host_cmd d3_cfg_cmd = {
- .id = D3_CONFIG_CMD,
- .flags = CMD_WANT_SKB,
- .data[0] = &d3_cfg_cmd_data,
- .len[0] = sizeof(d3_cfg_cmd_data),
- };
- struct wowlan_key_data key_data = {
- .use_rsc_tsc = false,
- .tkip = &tkip_cmd,
- .use_tkip = false,
- };
- int ret;
- int len __maybe_unused;
-
- if (!wowlan) {
- /*
- * mac80211 shouldn't get here, but for D3 test
- * it doesn't warrant a warning
- */
- WARN_ON(!test);
- return -EINVAL;
- }
-
- key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL);
- if (!key_data.rsc_tsc)
- return -ENOMEM;
-
- mutex_lock(&mvm->mutex);
-
- /* see if there's only a single BSS vif and it's associated */
- ieee80211_iterate_active_interfaces_atomic(
- mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_d3_iface_iterator, &suspend_iter_data);
-
- if (suspend_iter_data.error || !suspend_iter_data.vif) {
- ret = 1;
- goto out_noreset;
- }
-
- vif = suspend_iter_data.vif;
- mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- ap_sta = rcu_dereference_protected(
- mvm->fw_id_to_mac_id[mvmvif->ap_sta_id],
- lockdep_is_held(&mvm->mutex));
- if (IS_ERR_OR_NULL(ap_sta)) {
- ret = -EINVAL;
- goto out_noreset;
- }
-
- mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv;
-
- /* TODO: wowlan_config_cmd.common.wowlan_ba_teardown_tids */
-
- wowlan_config_cmd.common.is_11n_connection =
- ap_sta->ht_cap.ht_supported;
-
- /* Query the last used seqno and set it */
- ret = iwl_mvm_get_last_nonqos_seq(mvm, vif);
- if (ret < 0)
- goto out_noreset;
- wowlan_config_cmd.common.non_qos_seq = cpu_to_le16(ret);
-
- iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, &wowlan_config_cmd.common);
-
- if (wowlan->disconnect)
- wowlan_config_cmd.common.wakeup_filter |=
- cpu_to_le32(IWL_WOWLAN_WAKEUP_BEACON_MISS |
- IWL_WOWLAN_WAKEUP_LINK_CHANGE);
- if (wowlan->magic_pkt)
- wowlan_config_cmd.common.wakeup_filter |=
- cpu_to_le32(IWL_WOWLAN_WAKEUP_MAGIC_PACKET);
- if (wowlan->gtk_rekey_failure)
- wowlan_config_cmd.common.wakeup_filter |=
- cpu_to_le32(IWL_WOWLAN_WAKEUP_GTK_REKEY_FAIL);
- if (wowlan->eap_identity_req)
- wowlan_config_cmd.common.wakeup_filter |=
- cpu_to_le32(IWL_WOWLAN_WAKEUP_EAP_IDENT_REQ);
- if (wowlan->four_way_handshake)
- wowlan_config_cmd.common.wakeup_filter |=
- cpu_to_le32(IWL_WOWLAN_WAKEUP_4WAY_HANDSHAKE);
- if (wowlan->n_patterns)
- wowlan_config_cmd.common.wakeup_filter |=
- cpu_to_le32(IWL_WOWLAN_WAKEUP_PATTERN_MATCH);
-
- if (wowlan->rfkill_release)
- wowlan_config_cmd.common.wakeup_filter |=
- cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT);
-
- if (wowlan->tcp) {
- /*
- * Set the "link change" (really "link lost") flag as well
- * since that implies losing the TCP connection.
- */
- wowlan_config_cmd.common.wakeup_filter |=
- cpu_to_le32(IWL_WOWLAN_WAKEUP_REMOTE_LINK_LOSS |
- IWL_WOWLAN_WAKEUP_REMOTE_SIGNATURE_TABLE |
- IWL_WOWLAN_WAKEUP_REMOTE_WAKEUP_PACKET |
- IWL_WOWLAN_WAKEUP_LINK_CHANGE);
- }
-
iwl_mvm_cancel_scan(mvm);
iwl_trans_stop_device(mvm->trans);
@@ -945,13 +782,99 @@
mvm->ptk_ivlen = 0;
mvm->ptk_icvlen = 0;
- ret = iwl_mvm_load_d3_fw(mvm);
+ return iwl_mvm_load_d3_fw(mvm);
+}
+
+static int
+iwl_mvm_get_wowlan_config(struct iwl_mvm *mvm,
+ struct cfg80211_wowlan *wowlan,
+ struct iwl_wowlan_config_cmd *wowlan_config_cmd,
+ struct ieee80211_vif *vif, struct iwl_mvm_vif *mvmvif,
+ struct ieee80211_sta *ap_sta)
+{
+ int ret;
+ struct iwl_mvm_sta *mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv;
+
+ /* TODO: wowlan_config_cmd->wowlan_ba_teardown_tids */
+
+ wowlan_config_cmd->is_11n_connection =
+ ap_sta->ht_cap.ht_supported;
+
+ /* Query the last used seqno and set it */
+ ret = iwl_mvm_get_last_nonqos_seq(mvm, vif);
+ if (ret < 0)
+ return ret;
+
+ wowlan_config_cmd->non_qos_seq = cpu_to_le16(ret);
+
+ iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, wowlan_config_cmd);
+
+ if (wowlan->disconnect)
+ wowlan_config_cmd->wakeup_filter |=
+ cpu_to_le32(IWL_WOWLAN_WAKEUP_BEACON_MISS |
+ IWL_WOWLAN_WAKEUP_LINK_CHANGE);
+ if (wowlan->magic_pkt)
+ wowlan_config_cmd->wakeup_filter |=
+ cpu_to_le32(IWL_WOWLAN_WAKEUP_MAGIC_PACKET);
+ if (wowlan->gtk_rekey_failure)
+ wowlan_config_cmd->wakeup_filter |=
+ cpu_to_le32(IWL_WOWLAN_WAKEUP_GTK_REKEY_FAIL);
+ if (wowlan->eap_identity_req)
+ wowlan_config_cmd->wakeup_filter |=
+ cpu_to_le32(IWL_WOWLAN_WAKEUP_EAP_IDENT_REQ);
+ if (wowlan->four_way_handshake)
+ wowlan_config_cmd->wakeup_filter |=
+ cpu_to_le32(IWL_WOWLAN_WAKEUP_4WAY_HANDSHAKE);
+ if (wowlan->n_patterns)
+ wowlan_config_cmd->wakeup_filter |=
+ cpu_to_le32(IWL_WOWLAN_WAKEUP_PATTERN_MATCH);
+
+ if (wowlan->rfkill_release)
+ wowlan_config_cmd->wakeup_filter |=
+ cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT);
+
+ if (wowlan->tcp) {
+ /*
+ * Set the "link change" (really "link lost") flag as well
+ * since that implies losing the TCP connection.
+ */
+ wowlan_config_cmd->wakeup_filter |=
+ cpu_to_le32(IWL_WOWLAN_WAKEUP_REMOTE_LINK_LOSS |
+ IWL_WOWLAN_WAKEUP_REMOTE_SIGNATURE_TABLE |
+ IWL_WOWLAN_WAKEUP_REMOTE_WAKEUP_PACKET |
+ IWL_WOWLAN_WAKEUP_LINK_CHANGE);
+ }
+
+ return 0;
+}
+
+static int
+iwl_mvm_wowlan_config(struct iwl_mvm *mvm,
+ struct cfg80211_wowlan *wowlan,
+ struct iwl_wowlan_config_cmd *wowlan_config_cmd,
+ struct ieee80211_vif *vif, struct iwl_mvm_vif *mvmvif,
+ struct ieee80211_sta *ap_sta)
+{
+ struct iwl_wowlan_kek_kck_material_cmd kek_kck_cmd = {};
+ struct iwl_wowlan_tkip_params_cmd tkip_cmd = {};
+ struct wowlan_key_data key_data = {
+ .use_rsc_tsc = false,
+ .tkip = &tkip_cmd,
+ .use_tkip = false,
+ };
+ int ret;
+
+ ret = iwl_mvm_switch_to_d3(mvm);
if (ret)
- goto out;
+ return ret;
ret = iwl_mvm_d3_reprogram(mvm, vif, ap_sta);
if (ret)
- goto out;
+ return ret;
+
+ key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL);
+ if (!key_data.rsc_tsc)
+ return -ENOMEM;
if (!iwlwifi_mod_params.sw_crypto) {
/*
@@ -1010,7 +933,9 @@
}
}
- ret = iwl_mvm_send_wowlan_config_cmd(mvm, &wowlan_config_cmd);
+ ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, 0,
+ sizeof(*wowlan_config_cmd),
+ wowlan_config_cmd);
if (ret)
goto out;
@@ -1023,8 +948,153 @@
goto out;
ret = iwl_mvm_send_remote_wake_cfg(mvm, vif, wowlan->tcp);
+
+out:
+ kfree(key_data.rsc_tsc);
+ return ret;
+}
+
+static int
+iwl_mvm_netdetect_config(struct iwl_mvm *mvm,
+ struct cfg80211_wowlan *wowlan,
+ struct cfg80211_sched_scan_request *nd_config,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_wowlan_config_cmd wowlan_config_cmd = {};
+ int ret;
+
+ ret = iwl_mvm_switch_to_d3(mvm);
if (ret)
- goto out;
+ return ret;
+
+ /* rfkill release can be either for wowlan or netdetect */
+ if (wowlan->rfkill_release)
+ wowlan_config_cmd.wakeup_filter |=
+ cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT);
+
+ ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, 0,
+ sizeof(wowlan_config_cmd),
+ &wowlan_config_cmd);
+ if (ret)
+ return ret;
+
+ ret = iwl_mvm_scan_offload_start(mvm, vif, nd_config, &mvm->nd_ies);
+ if (ret)
+ return ret;
+
+ if (WARN_ON(mvm->nd_match_sets || mvm->nd_channels))
+ return -EBUSY;
+
+ /* save the sched scan matchsets... */
+ if (nd_config->n_match_sets) {
+ mvm->nd_match_sets = kmemdup(nd_config->match_sets,
+ sizeof(*nd_config->match_sets) *
+ nd_config->n_match_sets,
+ GFP_KERNEL);
+ if (mvm->nd_match_sets)
+ mvm->n_nd_match_sets = nd_config->n_match_sets;
+ }
+
+ /* ...and the sched scan channels for later reporting */
+ mvm->nd_channels = kmemdup(nd_config->channels,
+ sizeof(*nd_config->channels) *
+ nd_config->n_channels,
+ GFP_KERNEL);
+ if (mvm->nd_channels)
+ mvm->n_nd_channels = nd_config->n_channels;
+
+ return 0;
+}
+
+static void iwl_mvm_free_nd(struct iwl_mvm *mvm)
+{
+ kfree(mvm->nd_match_sets);
+ mvm->nd_match_sets = NULL;
+ mvm->n_nd_match_sets = 0;
+ kfree(mvm->nd_channels);
+ mvm->nd_channels = NULL;
+ mvm->n_nd_channels = 0;
+}
+
+static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
+ struct cfg80211_wowlan *wowlan,
+ bool test)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct ieee80211_vif *vif = NULL;
+ struct iwl_mvm_vif *mvmvif = NULL;
+ struct ieee80211_sta *ap_sta = NULL;
+ struct iwl_d3_manager_config d3_cfg_cmd_data = {
+ /*
+ * Program the minimum sleep time to 10 seconds, as many
+ * platforms have issues processing a wakeup signal while
+ * still being in the process of suspending.
+ */
+ .min_sleep_time = cpu_to_le32(10 * 1000 * 1000),
+ };
+ struct iwl_host_cmd d3_cfg_cmd = {
+ .id = D3_CONFIG_CMD,
+ .flags = CMD_WANT_SKB,
+ .data[0] = &d3_cfg_cmd_data,
+ .len[0] = sizeof(d3_cfg_cmd_data),
+ };
+ int ret;
+ int len __maybe_unused;
+
+ if (!wowlan) {
+ /*
+ * mac80211 shouldn't get here, but for D3 test
+ * it doesn't warrant a warning
+ */
+ WARN_ON(!test);
+ return -EINVAL;
+ }
+
+ mutex_lock(&mvm->mutex);
+
+ vif = iwl_mvm_get_bss_vif(mvm);
+ if (IS_ERR_OR_NULL(vif)) {
+ ret = 1;
+ goto out_noreset;
+ }
+
+ mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT) {
+ /* if we're not associated, this must be netdetect */
+ if (!wowlan->nd_config && !mvm->nd_config) {
+ ret = 1;
+ goto out_noreset;
+ }
+
+ ret = iwl_mvm_netdetect_config(
+ mvm, wowlan, wowlan->nd_config ?: mvm->nd_config, vif);
+ if (ret)
+ goto out;
+
+ mvm->net_detect = true;
+ } else {
+ struct iwl_wowlan_config_cmd wowlan_config_cmd = {};
+
+ ap_sta = rcu_dereference_protected(
+ mvm->fw_id_to_mac_id[mvmvif->ap_sta_id],
+ lockdep_is_held(&mvm->mutex));
+ if (IS_ERR_OR_NULL(ap_sta)) {
+ ret = -EINVAL;
+ goto out_noreset;
+ }
+
+ ret = iwl_mvm_get_wowlan_config(mvm, wowlan, &wowlan_config_cmd,
+ vif, mvmvif, ap_sta);
+ if (ret)
+ goto out_noreset;
+ ret = iwl_mvm_wowlan_config(mvm, wowlan, &wowlan_config_cmd,
+ vif, mvmvif, ap_sta);
+ if (ret)
+ goto out;
+
+ mvm->net_detect = false;
+ }
ret = iwl_mvm_power_update_device(mvm);
if (ret)
@@ -1057,11 +1127,11 @@
iwl_trans_d3_suspend(mvm->trans, test);
out:
- if (ret < 0)
+ if (ret < 0) {
ieee80211_restart_hw(mvm->hw);
+ iwl_mvm_free_nd(mvm);
+ }
out_noreset:
- kfree(key_data.rsc_tsc);
-
mutex_unlock(&mvm->mutex);
return ret;
@@ -1071,6 +1141,7 @@
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ iwl_trans_suspend(mvm->trans);
if (iwl_mvm_is_d0i3_supported(mvm)) {
mutex_lock(&mvm->d0i3_suspend_mutex);
__set_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags);
@@ -1449,9 +1520,8 @@
return true;
}
-/* releases the MVM mutex */
-static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
+static struct iwl_wowlan_status *
+iwl_mvm_get_wakeup_status(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
u32 base = mvm->error_event_table;
struct error_table_start {
@@ -1463,19 +1533,15 @@
.id = WOWLAN_GET_STATUSES,
.flags = CMD_WANT_SKB,
};
- struct iwl_wowlan_status_data status;
- struct iwl_wowlan_status *fw_status;
- int ret, len, status_size, i;
- bool keep;
- struct ieee80211_sta *ap_sta;
- struct iwl_mvm_sta *mvm_ap_sta;
+ struct iwl_wowlan_status *status, *fw_status;
+ int ret, len, status_size;
iwl_trans_read_mem_bytes(mvm->trans, base,
&err_info, sizeof(err_info));
if (err_info.valid) {
- IWL_INFO(mvm, "error table is valid (%d)\n",
- err_info.valid);
+ IWL_INFO(mvm, "error table is valid (%d) with error (%d)\n",
+ err_info.valid, err_info.error_id);
if (err_info.error_id == RF_KILL_INDICATOR_FOR_WOWLAN) {
struct cfg80211_wowlan_wakeup wakeup = {
.rfkill_release = true,
@@ -1483,7 +1549,7 @@
ieee80211_report_wowlan_wakeup(vif, &wakeup,
GFP_KERNEL);
}
- goto out_unlock;
+ return ERR_PTR(-EIO);
}
/* only for tracing for now */
@@ -1494,22 +1560,53 @@
ret = iwl_mvm_send_cmd(mvm, &cmd);
if (ret) {
IWL_ERR(mvm, "failed to query status (%d)\n", ret);
- goto out_unlock;
+ return ERR_PTR(ret);
}
/* RF-kill already asserted again... */
- if (!cmd.resp_pkt)
- goto out_unlock;
+ if (!cmd.resp_pkt) {
+ ret = -ERFKILL;
+ goto out_free_resp;
+ }
status_size = sizeof(*fw_status);
len = iwl_rx_packet_payload_len(cmd.resp_pkt);
if (len < status_size) {
IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
+ ret = -EIO;
goto out_free_resp;
}
- fw_status = (void *)cmd.resp_pkt->data;
+ status = (void *)cmd.resp_pkt->data;
+ if (len != (status_size +
+ ALIGN(le32_to_cpu(status->wake_packet_bufsize), 4))) {
+ IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
+ ret = -EIO;
+ goto out_free_resp;
+ }
+
+ fw_status = kmemdup(status, len, GFP_KERNEL);
+
+out_free_resp:
+ iwl_free_resp(&cmd);
+ return ret ? ERR_PTR(ret) : fw_status;
+}
+
+/* releases the MVM mutex */
+static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_wowlan_status_data status;
+ struct iwl_wowlan_status *fw_status;
+ int i;
+ bool keep;
+ struct ieee80211_sta *ap_sta;
+ struct iwl_mvm_sta *mvm_ap_sta;
+
+ fw_status = iwl_mvm_get_wakeup_status(mvm, vif);
+ if (IS_ERR_OR_NULL(fw_status))
+ goto out_unlock;
status.pattern_number = le16_to_cpu(fw_status->pattern_number);
for (i = 0; i < 8; i++)
@@ -1522,17 +1619,12 @@
le32_to_cpu(fw_status->wake_packet_bufsize);
status.wake_packet = fw_status->wake_packet;
- if (len != status_size + ALIGN(status.wake_packet_bufsize, 4)) {
- IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
- goto out_free_resp;
- }
-
/* still at hard-coded place 0 for D3 image */
ap_sta = rcu_dereference_protected(
mvm->fw_id_to_mac_id[0],
lockdep_is_held(&mvm->mutex));
if (IS_ERR_OR_NULL(ap_sta))
- goto out_free_resp;
+ goto out_free;
mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv;
for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
@@ -1549,16 +1641,151 @@
keep = iwl_mvm_setup_connection_keep(mvm, vif, fw_status);
- iwl_free_resp(&cmd);
+ kfree(fw_status);
return keep;
- out_free_resp:
- iwl_free_resp(&cmd);
- out_unlock:
+out_free:
+ kfree(fw_status);
+out_unlock:
mutex_unlock(&mvm->mutex);
return false;
}
+struct iwl_mvm_nd_query_results {
+ u32 matched_profiles;
+ struct iwl_scan_offload_profile_match matches[IWL_SCAN_MAX_PROFILES];
+};
+
+static int
+iwl_mvm_netdetect_query_results(struct iwl_mvm *mvm,
+ struct iwl_mvm_nd_query_results *results)
+{
+ struct iwl_scan_offload_profiles_query *query;
+ struct iwl_host_cmd cmd = {
+ .id = SCAN_OFFLOAD_PROFILES_QUERY_CMD,
+ .flags = CMD_WANT_SKB,
+ };
+ int ret, len;
+
+ ret = iwl_mvm_send_cmd(mvm, &cmd);
+ if (ret) {
+ IWL_ERR(mvm, "failed to query matched profiles (%d)\n", ret);
+ return ret;
+ }
+
+ /* RF-kill already asserted again... */
+ if (!cmd.resp_pkt) {
+ ret = -ERFKILL;
+ goto out_free_resp;
+ }
+
+ len = iwl_rx_packet_payload_len(cmd.resp_pkt);
+ if (len < sizeof(*query)) {
+ IWL_ERR(mvm, "Invalid scan offload profiles query response!\n");
+ ret = -EIO;
+ goto out_free_resp;
+ }
+
+ query = (void *)cmd.resp_pkt->data;
+
+ results->matched_profiles = le32_to_cpu(query->matched_profiles);
+ memcpy(results->matches, query->matches, sizeof(results->matches));
+
+out_free_resp:
+ iwl_free_resp(&cmd);
+ return ret;
+}
+
+static void iwl_mvm_query_netdetect_reasons(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
+{
+ struct cfg80211_wowlan_nd_info *net_detect = NULL;
+ struct cfg80211_wowlan_wakeup wakeup = {
+ .pattern_idx = -1,
+ };
+ struct cfg80211_wowlan_wakeup *wakeup_report = &wakeup;
+ struct iwl_mvm_nd_query_results query;
+ struct iwl_wowlan_status *fw_status;
+ unsigned long matched_profiles;
+ u32 reasons = 0;
+ int i, j, n_matches, ret;
+
+ fw_status = iwl_mvm_get_wakeup_status(mvm, vif);
+ if (!IS_ERR_OR_NULL(fw_status))
+ reasons = le32_to_cpu(fw_status->wakeup_reasons);
+
+ if (reasons & IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED)
+ wakeup.rfkill_release = true;
+
+ if (reasons != IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS)
+ goto out;
+
+ ret = iwl_mvm_netdetect_query_results(mvm, &query);
+ if (ret || !query.matched_profiles) {
+ wakeup_report = NULL;
+ goto out;
+ }
+
+ matched_profiles = query.matched_profiles;
+ if (mvm->n_nd_match_sets) {
+ n_matches = hweight_long(matched_profiles);
+ } else {
+ IWL_ERR(mvm, "no net detect match information available\n");
+ n_matches = 0;
+ }
+
+ net_detect = kzalloc(sizeof(*net_detect) +
+ (n_matches * sizeof(net_detect->matches[0])),
+ GFP_KERNEL);
+ if (!net_detect || !n_matches)
+ goto out_report_nd;
+
+ for_each_set_bit(i, &matched_profiles, mvm->n_nd_match_sets) {
+ struct iwl_scan_offload_profile_match *fw_match;
+ struct cfg80211_wowlan_nd_match *match;
+ int n_channels = 0;
+
+ fw_match = &query.matches[i];
+
+ for (j = 0; j < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN; j++)
+ n_channels += hweight8(fw_match->matching_channels[j]);
+
+ match = kzalloc(sizeof(*match) +
+ (n_channels * sizeof(*match->channels)),
+ GFP_KERNEL);
+ if (!match)
+ goto out_report_nd;
+
+ net_detect->matches[net_detect->n_matches++] = match;
+
+ match->ssid.ssid_len = mvm->nd_match_sets[i].ssid.ssid_len;
+ memcpy(match->ssid.ssid, mvm->nd_match_sets[i].ssid.ssid,
+ match->ssid.ssid_len);
+
+ if (mvm->n_nd_channels < n_channels)
+ continue;
+
+ for (j = 0; j < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN * 8; j++)
+ if (fw_match->matching_channels[j / 8] & (BIT(j % 8)))
+ match->channels[match->n_channels++] =
+ mvm->nd_channels[j]->center_freq;
+ }
+
+out_report_nd:
+ wakeup.net_detect = net_detect;
+out:
+ iwl_mvm_free_nd(mvm);
+
+ mutex_unlock(&mvm->mutex);
+ ieee80211_report_wowlan_wakeup(vif, wakeup_report, GFP_KERNEL);
+
+ if (net_detect) {
+ for (i = 0; i < net_detect->n_matches; i++)
+ kfree(net_detect->matches[i]);
+ kfree(net_detect);
+ }
+}
+
static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm)
{
#ifdef CPTCFG_IWLWIFI_DEBUGFS
@@ -1592,9 +1819,6 @@
static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
{
- struct iwl_d3_iter_data resume_iter_data = {
- .mvm = mvm,
- };
struct ieee80211_vif *vif = NULL;
int ret;
enum iwl_d3_status d3_status;
@@ -1603,15 +1827,10 @@
mutex_lock(&mvm->mutex);
/* get the BSS vif pointer again */
- ieee80211_iterate_active_interfaces_atomic(
- mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_d3_iface_iterator, &resume_iter_data);
-
- if (WARN_ON(resume_iter_data.error || !resume_iter_data.vif))
+ vif = iwl_mvm_get_bss_vif(mvm);
+ if (IS_ERR_OR_NULL(vif))
goto out_unlock;
- vif = resume_iter_data.vif;
-
ret = iwl_trans_d3_resume(mvm->trans, &d3_status, test);
if (ret)
goto out_unlock;
@@ -1624,11 +1843,15 @@
/* query SRAM first in case we want event logging */
iwl_mvm_read_d3_sram(mvm);
- keep = iwl_mvm_query_wakeup_reasons(mvm, vif);
+ if (mvm->net_detect) {
+ iwl_mvm_query_netdetect_reasons(mvm, vif);
+ } else {
+ keep = iwl_mvm_query_wakeup_reasons(mvm, vif);
#ifdef CPTCFG_IWLWIFI_DEBUGFS
- if (keep)
- mvm->keep_vif = vif;
+ if (keep)
+ mvm->keep_vif = vif;
#endif
+ }
/* has unlocked the mutex, so skip that */
goto out;
@@ -1643,6 +1866,7 @@
/* return 1 to reconfigure the device */
set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
+ set_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status);
return 1;
}
@@ -1650,18 +1874,10 @@
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- if (iwl_mvm_is_d0i3_supported(mvm)) {
- bool exit_now;
+ iwl_trans_resume(mvm->trans);
- mutex_lock(&mvm->d0i3_suspend_mutex);
- __clear_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags);
- exit_now = __test_and_clear_bit(D0I3_PENDING_WAKEUP,
- &mvm->d0i3_suspend_flags);
- mutex_unlock(&mvm->d0i3_suspend_mutex);
- if (exit_now)
- _iwl_mvm_exit_d0i3(mvm);
+ if (iwl_mvm_is_d0i3_supported(mvm))
return 0;
- }
return __iwl_mvm_resume(mvm, false);
}
@@ -1741,7 +1957,9 @@
int remaining_time = 10;
mvm->d3_test_active = false;
+ rtnl_lock();
__iwl_mvm_resume(mvm, true);
+ rtnl_unlock();
iwl_abort_notification_waits(&mvm->notif_wait);
ieee80211_restart_hw(mvm->hw);
diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c
index 7765c2b..2c87e2d 100644
--- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c
+++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c
@@ -121,78 +121,6 @@
return ret;
}
-static int iwl_dbgfs_fw_error_dump_open(struct inode *inode, struct file *file)
-{
- struct iwl_mvm *mvm = inode->i_private;
- int ret;
-
- if (!mvm)
- return -EINVAL;
-
- mutex_lock(&mvm->mutex);
- if (!mvm->fw_error_dump) {
- ret = -ENODATA;
- goto out;
- }
-
- file->private_data = mvm->fw_error_dump;
- mvm->fw_error_dump = NULL;
- ret = 0;
-
-out:
- mutex_unlock(&mvm->mutex);
- return ret;
-}
-
-static ssize_t iwl_dbgfs_fw_error_dump_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct iwl_mvm_dump_ptrs *dump_ptrs = (void *)file->private_data;
- ssize_t bytes_read = 0;
- ssize_t bytes_read_trans = 0;
-
- if (*ppos < dump_ptrs->op_mode_len)
- bytes_read +=
- simple_read_from_buffer(user_buf, count, ppos,
- dump_ptrs->op_mode_ptr,
- dump_ptrs->op_mode_len);
-
- if (bytes_read < 0 || *ppos < dump_ptrs->op_mode_len)
- return bytes_read;
-
- if (dump_ptrs->trans_ptr) {
- *ppos -= dump_ptrs->op_mode_len;
- bytes_read_trans =
- simple_read_from_buffer(user_buf + bytes_read,
- count - bytes_read, ppos,
- dump_ptrs->trans_ptr->data,
- dump_ptrs->trans_ptr->len);
- *ppos += dump_ptrs->op_mode_len;
-
- if (bytes_read_trans >= 0)
- bytes_read += bytes_read_trans;
- else if (!bytes_read)
- /* propagate the failure */
- return bytes_read_trans;
- }
-
- return bytes_read;
-
-}
-
-static int iwl_dbgfs_fw_error_dump_release(struct inode *inode,
- struct file *file)
-{
- struct iwl_mvm_dump_ptrs *dump_ptrs = (void *)file->private_data;
-
- vfree(dump_ptrs->op_mode_ptr);
- vfree(dump_ptrs->trans_ptr);
- kfree(dump_ptrs);
-
- return 0;
-}
-
static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
@@ -1008,7 +936,11 @@
if (scan_rx_ant & ~mvm->fw->valid_rx_ant)
return -EINVAL;
- mvm->scan_rx_ant = scan_rx_ant;
+ if (mvm->scan_rx_ant != scan_rx_ant) {
+ mvm->scan_rx_ant = scan_rx_ant;
+ if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)
+ iwl_mvm_config_scan(mvm);
+ }
return count;
}
@@ -1250,6 +1182,118 @@
return ret;
}
+
+#define MAX_NUM_ND_MATCHSETS 10
+
+static ssize_t iwl_dbgfs_netdetect_write(struct iwl_mvm *mvm, char *buf,
+ size_t count, loff_t *ppos)
+{
+ const char *seps = ",\n";
+ char *buf_ptr = buf;
+ char *value_str = NULL;
+ int ret, i;
+
+ /* TODO: don't free if write is being called several times in one go */
+ if (mvm->nd_config) {
+ kfree(mvm->nd_config->match_sets);
+ kfree(mvm->nd_config);
+ mvm->nd_config = NULL;
+ }
+
+ mvm->nd_config = kzalloc(sizeof(*mvm->nd_config) +
+ (11 * sizeof(struct ieee80211_channel *)),
+ GFP_KERNEL);
+ if (!mvm->nd_config) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+
+ mvm->nd_config->n_channels = 11;
+ mvm->nd_config->scan_width = NL80211_BSS_CHAN_WIDTH_20;
+ mvm->nd_config->interval = 5;
+ mvm->nd_config->min_rssi_thold = -80;
+ for (i = 0; i < mvm->nd_config->n_channels; i++)
+ mvm->nd_config->channels[i] = &mvm->nvm_data->channels[i];
+
+ mvm->nd_config->match_sets =
+ kcalloc(MAX_NUM_ND_MATCHSETS,
+ sizeof(*mvm->nd_config->match_sets),
+ GFP_KERNEL);
+ if (!mvm->nd_config->match_sets) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+
+ while ((value_str = strsep(&buf_ptr, seps)) &&
+ strlen(value_str)) {
+ struct cfg80211_match_set *set;
+
+ if (mvm->nd_config->n_match_sets >= MAX_NUM_ND_MATCHSETS) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
+ set = &mvm->nd_config->match_sets[mvm->nd_config->n_match_sets];
+ set->ssid.ssid_len = strlen(value_str);
+
+ if (set->ssid.ssid_len > IEEE80211_MAX_SSID_LEN) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
+ memcpy(set->ssid.ssid, value_str, set->ssid.ssid_len);
+
+ mvm->nd_config->n_match_sets++;
+ }
+
+ ret = count;
+
+ if (mvm->nd_config->n_match_sets)
+ goto out;
+
+out_free:
+ if (mvm->nd_config)
+ kfree(mvm->nd_config->match_sets);
+ kfree(mvm->nd_config);
+ mvm->nd_config = NULL;
+out:
+ return ret;
+}
+
+static ssize_t
+iwl_dbgfs_netdetect_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm *mvm = file->private_data;
+ size_t bufsz, ret;
+ char *buf;
+ int i, n_match_sets, pos = 0;
+
+ n_match_sets = mvm->nd_config ? mvm->nd_config->n_match_sets : 0;
+
+ bufsz = n_match_sets * (IEEE80211_MAX_SSID_LEN + 1) + 1;
+ buf = kzalloc(bufsz, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ for (i = 0; i < n_match_sets; i++) {
+ if (pos +
+ mvm->nd_config->match_sets[i].ssid.ssid_len + 2 > bufsz) {
+ ret = -EIO;
+ goto out;
+ }
+
+ memcpy(buf + pos, mvm->nd_config->match_sets[i].ssid.ssid,
+ mvm->nd_config->match_sets[i].ssid.ssid_len);
+ pos += mvm->nd_config->match_sets[i].ssid.ssid_len;
+ buf[pos++] = '\n';
+ }
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+out:
+ kfree(buf);
+ return ret;
+}
#endif
#define PRINT_MVM_REF(ref) do { \
@@ -1295,6 +1339,7 @@
PRINT_MVM_REF(IWL_MVM_REF_NMI);
PRINT_MVM_REF(IWL_MVM_REF_TM_CMD);
PRINT_MVM_REF(IWL_MVM_REF_EXIT_WORK);
+ PRINT_MVM_REF(IWL_MVM_REF_PROTECT_CSA);
return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
}
@@ -1415,12 +1460,6 @@
MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8);
MVM_DEBUGFS_READ_WRITE_FILE_OPS(d0i3_refs, 8);
-static const struct file_operations iwl_dbgfs_fw_error_dump_ops = {
- .open = iwl_dbgfs_fw_error_dump_open,
- .read = iwl_dbgfs_fw_error_dump_read,
- .release = iwl_dbgfs_fw_error_dump_release,
-};
-
#ifdef CPTCFG_IWLWIFI_BCAST_FILTERING
MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters, 256);
MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters_macs, 256);
@@ -1428,6 +1467,7 @@
#ifdef CONFIG_PM_SLEEP
MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram, 8);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(netdetect, 384);
#endif
int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
@@ -1446,7 +1486,6 @@
S_IWUSR | S_IRUSR);
MVM_DEBUGFS_ADD_FILE(nic_temp, dbgfs_dir, S_IRUSR);
MVM_DEBUGFS_ADD_FILE(stations, dbgfs_dir, S_IRUSR);
- MVM_DEBUGFS_ADD_FILE(fw_error_dump, dbgfs_dir, S_IRUSR);
MVM_DEBUGFS_ADD_FILE(bt_notif, dbgfs_dir, S_IRUSR);
MVM_DEBUGFS_ADD_FILE(bt_cmd, dbgfs_dir, S_IRUSR);
MVM_DEBUGFS_ADD_FILE(disable_power_off, mvm->debugfs_dir,
@@ -1487,6 +1526,7 @@
if (!debugfs_create_bool("d3_wake_sysassert", S_IRUSR | S_IWUSR,
mvm->debugfs_dir, &mvm->d3_wake_sysassert))
goto err;
+ MVM_DEBUGFS_ADD_FILE(netdetect, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
#endif
if (!debugfs_create_u8("low_latency_agg_frame_limit", S_IRUSR | S_IWUSR,
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h
index 816883f..f3b1189 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h
@@ -84,6 +84,8 @@
* @BT_COEX_SYNC2SCO:
* @BT_COEX_CORUNNING:
* @BT_COEX_MPLUT:
+ * @BT_COEX_TTC:
+ * @BT_COEX_RRC:
*
* The COEX_MODE must be set for each command. Even if it is not changed.
*/
@@ -100,6 +102,8 @@
BT_COEX_SYNC2SCO = BIT(7),
BT_COEX_CORUNNING = BIT(8),
BT_COEX_MPLUT = BIT(9),
+ BT_COEX_TTC = BIT(20),
+ BT_COEX_RRC = BIT(21),
};
/*
@@ -127,6 +131,8 @@
BT_VALID_TXTX_DELTA_FREQ_THRS = BIT(16),
BT_VALID_TXRX_MAX_FREQ_0 = BIT(17),
BT_VALID_SYNC_TO_SCO = BIT(18),
+ BT_VALID_TTC = BIT(20),
+ BT_VALID_RRC = BIT(21),
};
/**
@@ -506,7 +512,8 @@
u8 bt_agg_traffic_load;
u8 bt_ci_compliance;
u8 ttc_enabled;
- __le16 reserved;
+ u8 rrc_enabled;
+ u8 reserved;
__le32 primary_ch_lut;
__le32 secondary_ch_lut;
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
index e74cdf2..6d3bea5 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
@@ -241,16 +241,12 @@
IWL_WOWLAN_WAKEUP_BCN_FILTERING = BIT(16),
}; /* WOWLAN_WAKEUP_FILTER_API_E_VER_4 */
-struct iwl_wowlan_config_cmd_v2 {
+struct iwl_wowlan_config_cmd {
__le32 wakeup_filter;
__le16 non_qos_seq;
__le16 qos_seq[8];
u8 wowlan_ba_teardown_tids;
u8 is_11n_connection;
-} __packed; /* WOWLAN_CONFIG_API_S_VER_2 */
-
-struct iwl_wowlan_config_cmd_v3 {
- struct iwl_wowlan_config_cmd_v2 common;
u8 offloading_tid;
u8 reserved[3];
} __packed; /* WOWLAN_CONFIG_API_S_VER_3 */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
index 27dd863..4300200 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
@@ -68,13 +68,46 @@
/* Power Management Commands, Responses, Notifications */
+/**
+ * enum iwl_ltr_config_flags - masks for LTR config command flags
+ * @LTR_CFG_FLAG_FEATURE_ENABLE: Feature operational status
+ * @LTR_CFG_FLAG_HW_DIS_ON_SHADOW_REG_ACCESS: allow LTR change on shadow
+ * memory access
+ * @LTR_CFG_FLAG_HW_EN_SHRT_WR_THROUGH: allow LTR msg send on ANY LTR
+ * reg change
+ * @LTR_CFG_FLAG_HW_DIS_ON_D0_2_D3: allow LTR msg send on transition from
+ * D0 to D3
+ * @LTR_CFG_FLAG_SW_SET_SHORT: fixed static short LTR register
+ * @LTR_CFG_FLAG_SW_SET_LONG: fixed static short LONG register
+ * @LTR_CFG_FLAG_DENIE_C10_ON_PD: allow going into C10 on PD
+ */
+enum iwl_ltr_config_flags {
+ LTR_CFG_FLAG_FEATURE_ENABLE = BIT(0),
+ LTR_CFG_FLAG_HW_DIS_ON_SHADOW_REG_ACCESS = BIT(1),
+ LTR_CFG_FLAG_HW_EN_SHRT_WR_THROUGH = BIT(2),
+ LTR_CFG_FLAG_HW_DIS_ON_D0_2_D3 = BIT(3),
+ LTR_CFG_FLAG_SW_SET_SHORT = BIT(4),
+ LTR_CFG_FLAG_SW_SET_LONG = BIT(5),
+ LTR_CFG_FLAG_DENIE_C10_ON_PD = BIT(6),
+};
+
+/**
+ * struct iwl_ltr_config_cmd - configures the LTR
+ * @flags: See %enum iwl_ltr_config_flags
+ */
+struct iwl_ltr_config_cmd {
+ __le32 flags;
+ __le32 static_long;
+ __le32 static_short;
+} __packed;
+
/* Radio LP RX Energy Threshold measured in dBm */
#define POWER_LPRX_RSSI_THRESHOLD 75
#define POWER_LPRX_RSSI_THRESHOLD_MAX 94
#define POWER_LPRX_RSSI_THRESHOLD_MIN 30
/**
- * enum iwl_scan_flags - masks for power table command flags
+ * enum iwl_power_flags - masks for power table command flags
* @POWER_FLAGS_POWER_SAVE_ENA_MSK: '1' Allow to save power by turning off
* receiver and transmitter. '0' - does not allow.
* @POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK: '0' Driver disables power management,
@@ -337,7 +370,7 @@
#define IWL_BF_DEBUG_FLAG_DEFAULT 0
#define IWL_BF_DEBUG_FLAG_D0I3 0
-#define IWL_BF_ESCAPE_TIMER_DEFAULT 50
+#define IWL_BF_ESCAPE_TIMER_DEFAULT 0
#define IWL_BF_ESCAPE_TIMER_D0I3 0
#define IWL_BF_ESCAPE_TIMER_MAX 1024
#define IWL_BF_ESCAPE_TIMER_MIN 0
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
index 1354c68..1f2acf4 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
@@ -794,4 +794,301 @@
__le32 reserved;
} __packed;
+/* UMAC Scan API */
+
+/**
+ * struct iwl_mvm_umac_cmd_hdr - Command header for UMAC commands
+ * @size: size of the command (not including header)
+ * @reserved0: for future use and alignment
+ * @ver: API version number
+ */
+struct iwl_mvm_umac_cmd_hdr {
+ __le16 size;
+ u8 reserved0;
+ u8 ver;
+} __packed;
+
+#define IWL_MVM_MAX_SIMULTANEOUS_SCANS 8
+
+enum scan_config_flags {
+ SCAN_CONFIG_FLAG_ACTIVATE = BIT(0),
+ SCAN_CONFIG_FLAG_DEACTIVATE = BIT(1),
+ SCAN_CONFIG_FLAG_FORBID_CHUB_REQS = BIT(2),
+ SCAN_CONFIG_FLAG_ALLOW_CHUB_REQS = BIT(3),
+ SCAN_CONFIG_FLAG_SET_TX_CHAINS = BIT(8),
+ SCAN_CONFIG_FLAG_SET_RX_CHAINS = BIT(9),
+ SCAN_CONFIG_FLAG_SET_AUX_STA_ID = BIT(10),
+ SCAN_CONFIG_FLAG_SET_ALL_TIMES = BIT(11),
+ SCAN_CONFIG_FLAG_SET_EFFECTIVE_TIMES = BIT(12),
+ SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS = BIT(13),
+ SCAN_CONFIG_FLAG_SET_LEGACY_RATES = BIT(14),
+ SCAN_CONFIG_FLAG_SET_MAC_ADDR = BIT(15),
+ SCAN_CONFIG_FLAG_SET_FRAGMENTED = BIT(16),
+ SCAN_CONFIG_FLAG_CLEAR_FRAGMENTED = BIT(17),
+ SCAN_CONFIG_FLAG_SET_CAM_MODE = BIT(18),
+ SCAN_CONFIG_FLAG_CLEAR_CAM_MODE = BIT(19),
+ SCAN_CONFIG_FLAG_SET_PROMISC_MODE = BIT(20),
+ SCAN_CONFIG_FLAG_CLEAR_PROMISC_MODE = BIT(21),
+
+ /* Bits 26-31 are for num of channels in channel_array */
+#define SCAN_CONFIG_N_CHANNELS(n) ((n) << 26)
+};
+
+enum scan_config_rates {
+ /* OFDM basic rates */
+ SCAN_CONFIG_RATE_6M = BIT(0),
+ SCAN_CONFIG_RATE_9M = BIT(1),
+ SCAN_CONFIG_RATE_12M = BIT(2),
+ SCAN_CONFIG_RATE_18M = BIT(3),
+ SCAN_CONFIG_RATE_24M = BIT(4),
+ SCAN_CONFIG_RATE_36M = BIT(5),
+ SCAN_CONFIG_RATE_48M = BIT(6),
+ SCAN_CONFIG_RATE_54M = BIT(7),
+ /* CCK basic rates */
+ SCAN_CONFIG_RATE_1M = BIT(8),
+ SCAN_CONFIG_RATE_2M = BIT(9),
+ SCAN_CONFIG_RATE_5M = BIT(10),
+ SCAN_CONFIG_RATE_11M = BIT(11),
+
+ /* Bits 16-27 are for supported rates */
+#define SCAN_CONFIG_SUPPORTED_RATE(rate) ((rate) << 16)
+};
+
+enum iwl_channel_flags {
+ IWL_CHANNEL_FLAG_EBS = BIT(0),
+ IWL_CHANNEL_FLAG_ACCURATE_EBS = BIT(1),
+ IWL_CHANNEL_FLAG_EBS_ADD = BIT(2),
+ IWL_CHANNEL_FLAG_PRE_SCAN_PASSIVE2ACTIVE = BIT(3),
+};
+
+/**
+ * struct iwl_scan_config
+ * @hdr: umac command header
+ * @flags: enum scan_config_flags
+ * @tx_chains: valid_tx antenna - ANT_* definitions
+ * @rx_chains: valid_rx antenna - ANT_* definitions
+ * @legacy_rates: default legacy rates - enum scan_config_rates
+ * @out_of_channel_time: default max out of serving channel time
+ * @suspend_time: default max suspend time
+ * @dwell_active: default dwell time for active scan
+ * @dwell_passive: default dwell time for passive scan
+ * @dwell_fragmented: default dwell time for fragmented scan
+ * @reserved: for future use and alignment
+ * @mac_addr: default mac address to be used in probes
+ * @bcast_sta_id: the index of the station in the fw
+ * @channel_flags: default channel flags - enum iwl_channel_flags
+ * scan_config_channel_flag
+ * @channel_array: default supported channels
+ */
+struct iwl_scan_config {
+ struct iwl_mvm_umac_cmd_hdr hdr;
+ __le32 flags;
+ __le32 tx_chains;
+ __le32 rx_chains;
+ __le32 legacy_rates;
+ __le32 out_of_channel_time;
+ __le32 suspend_time;
+ u8 dwell_active;
+ u8 dwell_passive;
+ u8 dwell_fragmented;
+ u8 reserved;
+ u8 mac_addr[ETH_ALEN];
+ u8 bcast_sta_id;
+ u8 channel_flags;
+ u8 channel_array[];
+} __packed; /* SCAN_CONFIG_DB_CMD_API_S */
+
+/**
+ * iwl_umac_scan_flags
+ *@IWL_UMAC_SCAN_FLAG_PREEMPTIVE: scan process triggered by this scan request
+ * can be preempted by other scan requests with higher priority.
+ * The low priority scan is aborted.
+ *@IWL_UMAC_SCAN_FLAG_START_NOTIF: notification will be sent to the driver
+ * when scan starts.
+ */
+enum iwl_umac_scan_flags {
+ IWL_UMAC_SCAN_FLAG_PREEMPTIVE = BIT(0),
+ IWL_UMAC_SCAN_FLAG_START_NOTIF = BIT(1),
+};
+
+enum iwl_umac_scan_uid_offsets {
+ IWL_UMAC_SCAN_UID_TYPE_OFFSET = 0,
+ IWL_UMAC_SCAN_UID_SEQ_OFFSET = 8,
+};
+
+enum iwl_umac_scan_general_flags {
+ IWL_UMAC_SCAN_GEN_FLAGS_PERIODIC = BIT(0),
+ IWL_UMAC_SCAN_GEN_FLAGS_OVER_BT = BIT(1),
+ IWL_UMAC_SCAN_GEN_FLAGS_PASS_ALL = BIT(2),
+ IWL_UMAC_SCAN_GEN_FLAGS_PASSIVE = BIT(3),
+ IWL_UMAC_SCAN_GEN_FLAGS_PRE_CONNECT = BIT(4),
+ IWL_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE = BIT(5),
+ IWL_UMAC_SCAN_GEN_FLAGS_MULTIPLE_SSID = BIT(6),
+ IWL_UMAC_SCAN_GEN_FLAGS_FRAGMENTED = BIT(7),
+ IWL_UMAC_SCAN_GEN_FLAGS_RRM_ENABLED = BIT(8),
+ IWL_UMAC_SCAN_GEN_FLAGS_MATCH = BIT(9)
+};
+
+/**
+ * struct iwl_scan_channel_cfg_umac
+ * @flags: bitmap - 0-19: directed scan to i'th ssid.
+ * @channel_num: channel number 1-13 etc.
+ * @iter_count: repetition count for the channel.
+ * @iter_interval: interval between two scan interations on one channel.
+ */
+struct iwl_scan_channel_cfg_umac {
+ __le32 flags;
+ u8 channel_num;
+ u8 iter_count;
+ __le16 iter_interval;
+} __packed; /* SCAN_CHANNEL_CFG_S_VER2 */
+
+/**
+ * struct iwl_scan_umac_schedule
+ * @interval: interval in seconds between scan iterations
+ * @iter_count: num of scan iterations for schedule plan, 0xff for infinite loop
+ * @reserved: for alignment and future use
+ */
+struct iwl_scan_umac_schedule {
+ __le16 interval;
+ u8 iter_count;
+ u8 reserved;
+} __packed; /* SCAN_SCHED_PARAM_API_S_VER_1 */
+
+/**
+ * struct iwl_scan_req_umac_tail - the rest of the UMAC scan request command
+ * parameters following channels configuration array.
+ * @schedule: two scheduling plans.
+ * @delay: delay in TUs before starting the first scan iteration
+ * @reserved: for future use and alignment
+ * @preq: probe request with IEs blocks
+ * @direct_scan: list of SSIDs for directed active scan
+ */
+struct iwl_scan_req_umac_tail {
+ /* SCAN_PERIODIC_PARAMS_API_S_VER_1 */
+ struct iwl_scan_umac_schedule schedule[2];
+ __le16 delay;
+ __le16 reserved;
+ /* SCAN_PROBE_PARAMS_API_S_VER_1 */
+ struct iwl_scan_probe_req preq;
+ struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX];
+} __packed;
+
+/**
+ * struct iwl_scan_req_umac
+ * @hdr: umac command header
+ * @flags: &enum iwl_umac_scan_flags
+ * @uid: scan id, &enum iwl_umac_scan_uid_offsets
+ * @ooc_priority: out of channel priority - &enum iwl_scan_priority
+ * @general_flags: &enum iwl_umac_scan_general_flags
+ * @reserved1: for future use and alignment
+ * @active_dwell: dwell time for active scan
+ * @passive_dwell: dwell time for passive scan
+ * @fragmented_dwell: dwell time for fragmented passive scan
+ * @max_out_time: max out of serving channel time
+ * @suspend_time: max suspend time
+ * @scan_priority: scan internal prioritization &enum iwl_scan_priority
+ * @channel_flags: &enum iwl_scan_channel_flags
+ * @n_channels: num of channels in scan request
+ * @reserved2: for future use and alignment
+ * @data: &struct iwl_scan_channel_cfg_umac and
+ * &struct iwl_scan_req_umac_tail
+ */
+struct iwl_scan_req_umac {
+ struct iwl_mvm_umac_cmd_hdr hdr;
+ __le32 flags;
+ __le32 uid;
+ __le32 ooc_priority;
+ /* SCAN_GENERAL_PARAMS_API_S_VER_1 */
+ __le32 general_flags;
+ u8 reserved1;
+ u8 active_dwell;
+ u8 passive_dwell;
+ u8 fragmented_dwell;
+ __le32 max_out_time;
+ __le32 suspend_time;
+ __le32 scan_priority;
+ /* SCAN_CHANNEL_PARAMS_API_S_VER_1 */
+ u8 channel_flags;
+ u8 n_channels;
+ __le16 reserved2;
+ u8 data[];
+} __packed; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_1 */
+
+/**
+ * struct iwl_umac_scan_abort
+ * @hdr: umac command header
+ * @uid: scan id, &enum iwl_umac_scan_uid_offsets
+ * @flags: reserved
+ */
+struct iwl_umac_scan_abort {
+ struct iwl_mvm_umac_cmd_hdr hdr;
+ __le32 uid;
+ __le32 flags;
+} __packed; /* SCAN_ABORT_CMD_UMAC_API_S_VER_1 */
+
+/**
+ * struct iwl_umac_scan_complete
+ * @uid: scan id, &enum iwl_umac_scan_uid_offsets
+ * @last_schedule: last scheduling line
+ * @last_iter: last scan iteration number
+ * @scan status: &enum iwl_scan_offload_complete_status
+ * @ebs_status: &enum iwl_scan_ebs_status
+ * @time_from_last_iter: time elapsed from last iteration
+ * @reserved: for future use
+ */
+struct iwl_umac_scan_complete {
+ __le32 uid;
+ u8 last_schedule;
+ u8 last_iter;
+ u8 status;
+ u8 ebs_status;
+ __le32 time_from_last_iter;
+ __le32 reserved;
+} __packed; /* SCAN_COMPLETE_NTF_UMAC_API_S_VER_1 */
+
+#define SCAN_OFFLOAD_MATCHING_CHANNELS_LEN 5
+/**
+ * struct iwl_scan_offload_profile_match - match information
+ * @bssid: matched bssid
+ * @channel: channel where the match occurred
+ * @energy:
+ * @matching_feature:
+ * @matching_channels: bitmap of channels that matched, referencing
+ * the channels passed in tue scan offload request
+ */
+struct iwl_scan_offload_profile_match {
+ u8 bssid[ETH_ALEN];
+ __le16 reserved;
+ u8 channel;
+ u8 energy;
+ u8 matching_feature;
+ u8 matching_channels[SCAN_OFFLOAD_MATCHING_CHANNELS_LEN];
+} __packed; /* SCAN_OFFLOAD_PROFILE_MATCH_RESULTS_S_VER_1 */
+
+/**
+ * struct iwl_scan_offload_profiles_query - match results query response
+ * @matched_profiles: bitmap of matched profiles, referencing the
+ * matches passed in the scan offload request
+ * @last_scan_age: age of the last offloaded scan
+ * @n_scans_done: number of offloaded scans done
+ * @gp2_d0u: GP2 when D0U occurred
+ * @gp2_invoked: GP2 when scan offload was invoked
+ * @resume_while_scanning: not used
+ * @self_recovery: obsolete
+ * @reserved: reserved
+ * @matches: array of match information, one for each match
+ */
+struct iwl_scan_offload_profiles_query {
+ __le32 matched_profiles;
+ __le32 last_scan_age;
+ __le32 n_scans_done;
+ __le32 gp2_d0u;
+ __le32 gp2_invoked;
+ u8 resume_while_scanning;
+ u8 self_recovery;
+ __le16 reserved;
+ struct iwl_scan_offload_profile_match matches[IWL_SCAN_MAX_PROFILES];
+} __packed; /* SCAN_OFFLOAD_PROFILES_QUERY_RSP_S_VER_2 */
+
#endif
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
index 667a922..88af6dd 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
@@ -106,6 +106,12 @@
DBG_CFG = 0x9,
ANTENNA_COUPLING_NOTIFICATION = 0xa,
+ /* UMAC scan commands */
+ SCAN_CFG_CMD = 0xc,
+ SCAN_REQ_UMAC = 0xd,
+ SCAN_ABORT_UMAC = 0xe,
+ SCAN_COMPLETE_UMAC = 0xf,
+
/* station table */
ADD_STA_KEY = 0x17,
ADD_STA = 0x18,
@@ -122,6 +128,11 @@
/* global key */
WEP_KEY = 0x20,
+ /* TDLS */
+ TDLS_CHANNEL_SWITCH_CMD = 0x27,
+ TDLS_CHANNEL_SWITCH_NOTIFICATION = 0xaa,
+ TDLS_CONFIG_CMD = 0xa7,
+
/* MAC and Binding commands */
MAC_CONTEXT_CMD = 0x28,
TIME_EVENT_CMD = 0x29, /* both CMD and response */
@@ -157,6 +168,7 @@
/* Power - legacy power table command */
POWER_TABLE_CMD = 0x77,
PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION = 0x78,
+ LTR_CONFIG = 0xee,
/* Thermal Throttling*/
REPLY_THERMAL_MNG_BACKOFF = 0x7e,
@@ -189,6 +201,8 @@
/* Power - new power table command */
MAC_PM_POWER_TABLE = 0xa9,
+ MFUART_LOAD_NOTIFICATION = 0xb1,
+
REPLY_RX_PHY_CMD = 0xc0,
REPLY_RX_MPDU_CMD = 0xc1,
BA_NOTIF = 0xc5,
@@ -235,11 +249,9 @@
WOWLAN_TX_POWER_PER_DB = 0xe6,
/* and for NetDetect */
- NET_DETECT_CONFIG_CMD = 0x54,
- NET_DETECT_PROFILES_QUERY_CMD = 0x56,
- NET_DETECT_PROFILES_CMD = 0x57,
- NET_DETECT_HOTSPOTS_CMD = 0x58,
- NET_DETECT_HOTSPOTS_QUERY_CMD = 0x59,
+ SCAN_OFFLOAD_PROFILES_QUERY_CMD = 0x56,
+ SCAN_OFFLOAD_HOTSPOTS_CONFIG_CMD = 0x58,
+ SCAN_OFFLOAD_HOTSPOTS_QUERY_CMD = 0x59,
REPLY_MAX = 0xff,
};
@@ -1200,6 +1212,21 @@
} __packed; /* MISSED_BEACON_NTFY_API_S_VER_3 */
/**
+ * struct iwl_mfuart_load_notif - mfuart image version & status
+ * ( MFUART_LOAD_NOTIFICATION = 0xb1 )
+ * @installed_ver: installed image version
+ * @external_ver: external image version
+ * @status: MFUART loading status
+ * @duration: MFUART loading time
+*/
+struct iwl_mfuart_load_notif {
+ __le32 installed_ver;
+ __le32 external_ver;
+ __le32 status;
+ __le32 duration;
+} __packed; /*MFU_LOADER_NTFY_API_S_VER_1*/
+
+/**
* struct iwl_set_calib_default_cmd - set default value for calibration.
* ( SET_CALIB_DEFAULT_CMD = 0x8e )
* @calib_index: the calibration to set value for
@@ -1588,7 +1615,7 @@
#define SF_NUM_TIMEOUT_TYPES 2 /* Aging timer and Idle timer */
/* smart FIFO default values */
-#define SF_W_MARK_SISO 4096
+#define SF_W_MARK_SISO 6144
#define SF_W_MARK_MIMO2 8192
#define SF_W_MARK_MIMO3 6144
#define SF_W_MARK_LEGACY 4096
@@ -1710,4 +1737,145 @@
u8 flags;
} __packed;
+/***********************************
+ * TDLS API
+ ***********************************/
+
+/* Type of TDLS request */
+enum iwl_tdls_channel_switch_type {
+ TDLS_SEND_CHAN_SW_REQ = 0,
+ TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH,
+ TDLS_MOVE_CH,
+}; /* TDLS_STA_CHANNEL_SWITCH_CMD_TYPE_API_E_VER_1 */
+
+/**
+ * Switch timing sub-element in a TDLS channel-switch command
+ * @frame_timestamp: GP2 timestamp of channel-switch request/response packet
+ * received from peer
+ * @max_offchan_duration: What amount of microseconds out of a DTIM is given
+ * to the TDLS off-channel communication. For instance if the DTIM is
+ * 200TU and the TDLS peer is to be given 25% of the time, the value
+ * given will be 50TU, or 50 * 1024 if translated into microseconds.
+ * @switch_time: switch time the peer sent in its channel switch timing IE
+ * @switch_timout: switch timeout the peer sent in its channel switch timing IE
+ */
+struct iwl_tdls_channel_switch_timing {
+ __le32 frame_timestamp; /* GP2 time of peer packet Rx */
+ __le32 max_offchan_duration; /* given in micro-seconds */
+ __le32 switch_time; /* given in micro-seconds */
+ __le32 switch_timeout; /* given in micro-seconds */
+} __packed; /* TDLS_STA_CHANNEL_SWITCH_TIMING_DATA_API_S_VER_1 */
+
+#define IWL_TDLS_CH_SW_FRAME_MAX_SIZE 200
+
+/**
+ * TDLS channel switch frame template
+ *
+ * A template representing a TDLS channel-switch request or response frame
+ *
+ * @switch_time_offset: offset to the channel switch timing IE in the template
+ * @tx_cmd: Tx parameters for the frame
+ * @data: frame data
+ */
+struct iwl_tdls_channel_switch_frame {
+ __le32 switch_time_offset;
+ struct iwl_tx_cmd tx_cmd;
+ u8 data[IWL_TDLS_CH_SW_FRAME_MAX_SIZE];
+} __packed; /* TDLS_STA_CHANNEL_SWITCH_FRAME_API_S_VER_1 */
+
+/**
+ * TDLS channel switch command
+ *
+ * The command is sent to initiate a channel switch and also in response to
+ * incoming TDLS channel-switch request/response packets from remote peers.
+ *
+ * @switch_type: see &enum iwl_tdls_channel_switch_type
+ * @peer_sta_id: station id of TDLS peer
+ * @ci: channel we switch to
+ * @timing: timing related data for command
+ * @frame: channel-switch request/response template, depending to switch_type
+ */
+struct iwl_tdls_channel_switch_cmd {
+ u8 switch_type;
+ __le32 peer_sta_id;
+ struct iwl_fw_channel_info ci;
+ struct iwl_tdls_channel_switch_timing timing;
+ struct iwl_tdls_channel_switch_frame frame;
+} __packed; /* TDLS_STA_CHANNEL_SWITCH_CMD_API_S_VER_1 */
+
+/**
+ * TDLS channel switch start notification
+ *
+ * @status: non-zero on success
+ * @offchannel_duration: duration given in microseconds
+ * @sta_id: peer currently performing the channel-switch with
+ */
+struct iwl_tdls_channel_switch_notif {
+ __le32 status;
+ __le32 offchannel_duration;
+ __le32 sta_id;
+} __packed; /* TDLS_STA_CHANNEL_SWITCH_NTFY_API_S_VER_1 */
+
+/**
+ * TDLS station info
+ *
+ * @sta_id: station id of the TDLS peer
+ * @tx_to_peer_tid: TID reserved vs. the peer for FW based Tx
+ * @tx_to_peer_ssn: initial SSN the FW should use for Tx on its TID vs the peer
+ * @is_initiator: 1 if the peer is the TDLS link initiator, 0 otherwise
+ */
+struct iwl_tdls_sta_info {
+ u8 sta_id;
+ u8 tx_to_peer_tid;
+ __le16 tx_to_peer_ssn;
+ __le32 is_initiator;
+} __packed; /* TDLS_STA_INFO_VER_1 */
+
+/**
+ * TDLS basic config command
+ *
+ * @id_and_color: MAC id and color being configured
+ * @tdls_peer_count: amount of currently connected TDLS peers
+ * @tx_to_ap_tid: TID reverved vs. the AP for FW based Tx
+ * @tx_to_ap_ssn: initial SSN the FW should use for Tx on its TID vs. the AP
+ * @sta_info: per-station info. Only the first tdls_peer_count entries are set
+ * @pti_req_data_offset: offset of network-level data for the PTI template
+ * @pti_req_tx_cmd: Tx parameters for PTI request template
+ * @pti_req_template: PTI request template data
+ */
+struct iwl_tdls_config_cmd {
+ __le32 id_and_color; /* mac id and color */
+ u8 tdls_peer_count;
+ u8 tx_to_ap_tid;
+ __le16 tx_to_ap_ssn;
+ struct iwl_tdls_sta_info sta_info[IWL_MVM_TDLS_STA_COUNT];
+
+ __le32 pti_req_data_offset;
+ struct iwl_tx_cmd pti_req_tx_cmd;
+ u8 pti_req_template[0];
+} __packed; /* TDLS_CONFIG_CMD_API_S_VER_1 */
+
+/**
+ * TDLS per-station config information from FW
+ *
+ * @sta_id: station id of the TDLS peer
+ * @tx_to_peer_last_seq: last sequence number used by FW during FW-based Tx to
+ * the peer
+ */
+struct iwl_tdls_config_sta_info_res {
+ __le16 sta_id;
+ __le16 tx_to_peer_last_seq;
+} __packed; /* TDLS_STA_INFO_RSP_VER_1 */
+
+/**
+ * TDLS config information from FW
+ *
+ * @tx_to_ap_last_seq: last sequence number used by FW during FW-based Tx to AP
+ * @sta_info: per-station TDLS config information
+ */
+struct iwl_tdls_config_res {
+ __le32 tx_to_ap_last_seq;
+ struct iwl_tdls_config_sta_info_res sta_info[IWL_MVM_TDLS_STA_COUNT];
+} __packed; /* TDLS_CONFIG_RSP_API_S_VER_1 */
+
#endif /* __fw_api_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c
index 23fd711..d0fa6e9 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/iwlwifi/mvm/fw.c
@@ -186,7 +186,12 @@
static const u8 alive_cmd[] = { MVM_ALIVE };
struct iwl_sf_region st_fwrd_space;
- fw = iwl_get_ucode_image(mvm, ucode_type);
+ if (ucode_type == IWL_UCODE_REGULAR &&
+ iwl_fw_dbg_conf_usniffer(mvm->fw, FW_DBG_CUSTOM) &&
+ iwl_fw_dbg_conf_enabled(mvm->fw, FW_DBG_CUSTOM))
+ fw = iwl_get_ucode_image(mvm, IWL_UCODE_REGULAR_USNIFFER);
+ else
+ fw = iwl_get_ucode_image(mvm, ucode_type);
if (WARN_ON(!fw))
return -EINVAL;
mvm->cur_ucode = ucode_type;
@@ -227,6 +232,10 @@
st_fwrd_space.addr = mvm->sf_space.addr;
st_fwrd_space.size = mvm->sf_space.size;
ret = iwl_trans_update_sf(mvm->trans, &st_fwrd_space);
+ if (ret) {
+ IWL_ERR(mvm, "Failed to update SF size. ret %d\n", ret);
+ return ret;
+ }
iwl_trans_fw_alive(mvm->trans, alive_data.scd_base_addr);
@@ -284,7 +293,7 @@
lockdep_assert_held(&mvm->mutex);
- if (WARN_ON_ONCE(mvm->init_ucode_complete))
+ if (WARN_ON_ONCE(mvm->init_ucode_complete || mvm->calibrating))
return 0;
iwl_init_notification_wait(&mvm->notif_wait,
@@ -334,6 +343,8 @@
goto out;
}
+ mvm->calibrating = true;
+
/* Send TX valid antennas before triggering calibrations */
ret = iwl_send_tx_ant_cfg(mvm, mvm->fw->valid_tx_ant);
if (ret)
@@ -358,11 +369,17 @@
MVM_UCODE_CALIB_TIMEOUT);
if (!ret)
mvm->init_ucode_complete = true;
+
+ if (ret && iwl_mvm_is_radio_killed(mvm)) {
+ IWL_DEBUG_RF_KILL(mvm, "RFKILL while calibrating.\n");
+ ret = 1;
+ }
goto out;
error:
iwl_remove_notification(&mvm->notif_wait, &calib_wait);
out:
+ mvm->calibrating = false;
if (iwlmvm_mod_params.init_dbg && !mvm->nvm_data) {
/* we want to debug INIT and we have no NVM - fake */
mvm->nvm_data = kzalloc(sizeof(struct iwl_nvm_data) +
@@ -382,6 +399,42 @@
return ret;
}
+static int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm,
+ enum iwl_fw_dbg_conf conf_id)
+{
+ u8 *ptr;
+ int ret;
+ int i;
+
+ if (WARN_ONCE(conf_id >= ARRAY_SIZE(mvm->fw->dbg_conf_tlv),
+ "Invalid configuration %d\n", conf_id))
+ return -EINVAL;
+
+ if (!mvm->fw->dbg_conf_tlv[conf_id])
+ return -EINVAL;
+
+ if (mvm->fw_dbg_conf != FW_DBG_INVALID)
+ IWL_WARN(mvm, "FW already configured (%d) - re-configuring\n",
+ mvm->fw_dbg_conf);
+
+ /* Send all HCMDs for configuring the FW debug */
+ ptr = (void *)&mvm->fw->dbg_conf_tlv[conf_id]->hcmd;
+ for (i = 0; i < mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds; i++) {
+ struct iwl_fw_dbg_conf_hcmd *cmd = (void *)ptr;
+
+ ret = iwl_mvm_send_cmd_pdu(mvm, cmd->id, 0,
+ le16_to_cpu(cmd->len), cmd->data);
+ if (ret)
+ return ret;
+
+ ptr += sizeof(*cmd);
+ ptr += le16_to_cpu(cmd->len);
+ }
+
+ mvm->fw_dbg_conf = conf_id;
+ return ret;
+}
+
int iwl_mvm_up(struct iwl_mvm *mvm)
{
int ret, i;
@@ -433,6 +486,9 @@
if (ret)
IWL_ERR(mvm, "Failed to initialize Smart Fifo\n");
+ mvm->fw_dbg_conf = FW_DBG_INVALID;
+ iwl_mvm_start_fw_dbg_conf(mvm, FW_DBG_CUSTOM);
+
ret = iwl_send_tx_ant_cfg(mvm, mvm->fw->valid_tx_ant);
if (ret)
goto error;
@@ -454,6 +510,8 @@
for (i = 0; i < IWL_MVM_STATION_COUNT; i++)
RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL);
+ mvm->tdls_cs.peer.sta_id = IWL_MVM_STATION_COUNT;
+
/* reset quota debouncing buffer - 0xff will yield invalid data */
memset(&mvm->last_quota_cmd, 0xff, sizeof(mvm->last_quota_cmd));
@@ -480,10 +538,25 @@
/* Initialize tx backoffs to the minimal possible */
iwl_mvm_tt_tx_backoff(mvm, 0);
+ if (mvm->trans->ltr_enabled) {
+ struct iwl_ltr_config_cmd cmd = {
+ .flags = cpu_to_le32(LTR_CFG_FLAG_FEATURE_ENABLE),
+ };
+
+ WARN_ON(iwl_mvm_send_cmd_pdu(mvm, LTR_CONFIG, 0,
+ sizeof(cmd), &cmd));
+ }
+
ret = iwl_mvm_power_update_device(mvm);
if (ret)
goto error;
+ if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) {
+ ret = iwl_mvm_config_scan(mvm);
+ if (ret)
+ goto error;
+ }
+
/* allow FW/transport low power modes if not during restart */
if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN);
@@ -570,3 +643,19 @@
le32_to_cpu(radio_version->radio_dash));
return 0;
}
+
+int iwl_mvm_rx_mfuart_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb,
+ struct iwl_device_cmd *cmd)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_mfuart_load_notif *mfuart_notif = (void *)pkt->data;
+
+ IWL_DEBUG_INFO(mvm,
+ "MFUART: installed ver: 0x%08x, external ver: 0x%08x, status: 0x%08x, duration: 0x%08x\n",
+ le32_to_cpu(mfuart_notif->installed_ver),
+ le32_to_cpu(mfuart_notif->external_ver),
+ le32_to_cpu(mfuart_notif->status),
+ le32_to_cpu(mfuart_notif->duration));
+ return 0;
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
index 0c5c0b0..f6d86cc 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
@@ -83,11 +83,15 @@
struct ieee80211_vif *vif;
unsigned long available_mac_ids[BITS_TO_LONGS(NUM_MAC_INDEX_DRIVER)];
unsigned long available_tsf_ids[BITS_TO_LONGS(NUM_TSF_IDS)];
- u32 used_hw_queues;
enum iwl_tsf_id preferred_tsf;
bool found_vif;
};
+struct iwl_mvm_hw_queues_iface_iterator_data {
+ struct ieee80211_vif *exclude_vif;
+ unsigned long used_hw_queues;
+};
+
static void iwl_mvm_mac_tsf_id_iter(void *_data, u8 *mac,
struct ieee80211_vif *vif)
{
@@ -197,8 +201,7 @@
/*
* Get the mask of the queues used by the vif
*/
-u32 iwl_mvm_mac_get_queues_mask(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
+u32 iwl_mvm_mac_get_queues_mask(struct ieee80211_vif *vif)
{
u32 qmask = 0, ac;
@@ -214,6 +217,54 @@
return qmask;
}
+static void iwl_mvm_iface_hw_queues_iter(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_hw_queues_iface_iterator_data *data = _data;
+
+ /* exclude the given vif */
+ if (vif == data->exclude_vif)
+ return;
+
+ data->used_hw_queues |= iwl_mvm_mac_get_queues_mask(vif);
+}
+
+static void iwl_mvm_mac_sta_hw_queues_iter(void *_data,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_mvm_hw_queues_iface_iterator_data *data = _data;
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+
+ /* Mark the queues used by the sta */
+ data->used_hw_queues |= mvmsta->tfd_queue_msk;
+}
+
+unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm,
+ struct ieee80211_vif *exclude_vif)
+{
+ struct iwl_mvm_hw_queues_iface_iterator_data data = {
+ .exclude_vif = exclude_vif,
+ .used_hw_queues =
+ BIT(IWL_MVM_OFFCHANNEL_QUEUE) |
+ BIT(mvm->aux_queue) |
+ BIT(IWL_MVM_CMD_QUEUE),
+ };
+
+ lockdep_assert_held(&mvm->mutex);
+
+ /* mark all VIF used hw queues */
+ ieee80211_iterate_active_interfaces_atomic(
+ mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+ iwl_mvm_iface_hw_queues_iter, &data);
+
+ /* don't assign the same hw queues as TDLS stations */
+ ieee80211_iterate_stations_atomic(mvm->hw,
+ iwl_mvm_mac_sta_hw_queues_iter,
+ &data);
+
+ return data.used_hw_queues;
+}
+
static void iwl_mvm_mac_iface_iterator(void *_data, u8 *mac,
struct ieee80211_vif *vif)
{
@@ -226,9 +277,6 @@
return;
}
- /* Mark the queues used by the vif */
- data->used_hw_queues |= iwl_mvm_mac_get_queues_mask(data->mvm, vif);
-
/* Mark MAC IDs as used by clearing the available bit, and
* (below) mark TSFs as used if their existing use is not
* compatible with the new interface type.
@@ -275,10 +323,6 @@
.available_tsf_ids = { (1 << NUM_TSF_IDS) - 1 },
/* no preference yet */
.preferred_tsf = NUM_TSF_IDS,
- .used_hw_queues =
- BIT(IWL_MVM_OFFCHANNEL_QUEUE) |
- BIT(mvm->aux_queue) |
- BIT(IWL_MVM_CMD_QUEUE),
.found_vif = false,
};
u32 ac;
@@ -317,6 +361,8 @@
mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
iwl_mvm_mac_iface_iterator, &data);
+ used_hw_queues = iwl_mvm_get_used_hw_queues(mvm, vif);
+
/*
* In the case we're getting here during resume, it's similar to
* firmware restart, and with RESUME_ALL the iterator will find
@@ -366,8 +412,6 @@
return 0;
}
- used_hw_queues = data.used_hw_queues;
-
/* Find available queues, and allocate them to the ACs */
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
u8 queue = find_first_zero_bit(&used_hw_queues,
@@ -1219,17 +1263,25 @@
}
static void iwl_mvm_csa_count_down(struct iwl_mvm *mvm,
- struct ieee80211_vif *csa_vif, u32 gp2)
+ struct ieee80211_vif *csa_vif, u32 gp2,
+ bool tx_success)
{
struct iwl_mvm_vif *mvmvif =
iwl_mvm_vif_from_mac80211(csa_vif);
+ /* Don't start to countdown from a failed beacon */
+ if (!tx_success && !mvmvif->csa_countdown)
+ return;
+
+ mvmvif->csa_countdown = true;
+
if (!ieee80211_csa_is_complete(csa_vif)) {
int c = ieee80211_csa_update_counter(csa_vif);
iwl_mvm_mac_ctxt_beacon_changed(mvm, csa_vif);
if (csa_vif->p2p &&
- !iwl_mvm_te_scheduled(&mvmvif->time_event_data) && gp2) {
+ !iwl_mvm_te_scheduled(&mvmvif->time_event_data) && gp2 &&
+ tx_success) {
u32 rel_time = (c + 1) *
csa_vif->bss_conf.beacon_int -
IWL_MVM_CHANNEL_SWITCH_TIME_GO;
@@ -1252,38 +1304,30 @@
struct iwl_device_cmd *cmd)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_extended_beacon_notif *beacon = (void *)pkt->data;
struct iwl_mvm_tx_resp *beacon_notify_hdr;
struct ieee80211_vif *csa_vif;
struct ieee80211_vif *tx_blocked_vif;
- u64 tsf;
+ u16 status;
lockdep_assert_held(&mvm->mutex);
- if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_CAPA_EXTENDED_BEACON) {
- struct iwl_extended_beacon_notif *beacon = (void *)pkt->data;
+ beacon_notify_hdr = &beacon->beacon_notify_hdr;
+ mvm->ap_last_beacon_gp2 = le32_to_cpu(beacon->gp2);
- beacon_notify_hdr = &beacon->beacon_notify_hdr;
- tsf = le64_to_cpu(beacon->tsf);
- mvm->ap_last_beacon_gp2 = le32_to_cpu(beacon->gp2);
- } else {
- struct iwl_beacon_notif *beacon = (void *)pkt->data;
-
- beacon_notify_hdr = &beacon->beacon_notify_hdr;
- tsf = le64_to_cpu(beacon->tsf);
- }
-
+ status = le16_to_cpu(beacon_notify_hdr->status.status) & TX_STATUS_MSK;
IWL_DEBUG_RX(mvm,
"beacon status %#x retries:%d tsf:0x%16llX gp2:0x%X rate:%d\n",
- le16_to_cpu(beacon_notify_hdr->status.status) &
- TX_STATUS_MSK,
- beacon_notify_hdr->failure_frame, tsf,
+ status, beacon_notify_hdr->failure_frame,
+ le64_to_cpu(beacon->tsf),
mvm->ap_last_beacon_gp2,
le32_to_cpu(beacon_notify_hdr->initial_rate));
csa_vif = rcu_dereference_protected(mvm->csa_vif,
lockdep_is_held(&mvm->mutex));
if (unlikely(csa_vif && csa_vif->csa_active))
- iwl_mvm_csa_count_down(mvm, csa_vif, mvm->ap_last_beacon_gp2);
+ iwl_mvm_csa_count_down(mvm, csa_vif, mvm->ap_last_beacon_gp2,
+ (status == TX_STATUS_SUCCESS));
tx_blocked_vif = rcu_dereference_protected(mvm->csa_tx_blocked_vif,
lockdep_is_held(&mvm->mutex));
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index fec128f..137f3da 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -69,6 +69,7 @@
#include <linux/etherdevice.h>
#include <linux/ip.h>
#include <linux/if_arp.h>
+#include <linux/devcoredump.h>
#include <net/mac80211.h>
#include <net/ieee80211_radiotap.h>
#include <net/tcp.h>
@@ -253,6 +254,26 @@
spin_unlock_bh(&mvm->refs_lock);
}
+bool iwl_mvm_ref_taken(struct iwl_mvm *mvm)
+{
+ int i;
+ bool taken = false;
+
+ if (!iwl_mvm_is_d0i3_supported(mvm))
+ return true;
+
+ spin_lock_bh(&mvm->refs_lock);
+ for (i = 0; i < IWL_MVM_REF_COUNT; i++) {
+ if (mvm->refs[i]) {
+ taken = true;
+ break;
+ }
+ }
+ spin_unlock_bh(&mvm->refs_lock);
+
+ return taken;
+}
+
int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
{
iwl_mvm_ref(mvm, ref_type);
@@ -302,7 +323,8 @@
hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE;
hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FEC |
IEEE80211_RADIOTAP_MCS_HAVE_STBC;
- hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC;
+ hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC |
+ IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED;
hw->rate_control_algorithm = "iwl-mvm-rs";
/*
@@ -315,15 +337,19 @@
hw->flags |= IEEE80211_HW_MFP_CAPABLE;
if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT &&
- IWL_UCODE_API(mvm->fw->ucode_ver) >= 9 &&
!iwlwifi_mod_params.uapsd_disable) {
hw->flags |= IEEE80211_HW_SUPPORTS_UAPSD;
hw->uapsd_queues = IWL_MVM_UAPSD_QUEUES;
hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
}
- if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
+ if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN ||
+ mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) {
hw->flags |= IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS;
+ hw->wiphy->features |=
+ NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR |
+ NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
+ }
hw->sta_data_size = sizeof(struct iwl_mvm_sta);
hw->vif_data_size = sizeof(struct iwl_mvm_vif);
@@ -343,8 +369,7 @@
if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_GO_UAPSD)
hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
- if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_CSA_FLOW)
- hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
+ hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
hw->wiphy->iface_combinations = iwl_mvm_iface_combinations;
hw->wiphy->n_iface_combinations =
@@ -402,7 +427,8 @@
NL80211_FEATURE_LOW_PRIORITY_SCAN |
NL80211_FEATURE_P2P_GO_OPPPS |
NL80211_FEATURE_DYNAMIC_SMPS |
- NL80211_FEATURE_STATIC_SMPS;
+ NL80211_FEATURE_STATIC_SMPS |
+ NL80211_FEATURE_SUPPORTS_WMM_ADMISSION;
if (mvm->fw->ucode_capa.capa[0] &
IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT)
@@ -440,7 +466,8 @@
mvm->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
WIPHY_WOWLAN_DISCONNECT |
WIPHY_WOWLAN_EAP_IDENTITY_REQ |
- WIPHY_WOWLAN_RFKILL_RELEASE;
+ WIPHY_WOWLAN_RFKILL_RELEASE |
+ WIPHY_WOWLAN_NET_DETECT;
if (!iwlwifi_mod_params.sw_crypto)
mvm->wowlan.flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
WIPHY_WOWLAN_GTK_REKEY_FAILURE |
@@ -449,6 +476,7 @@
mvm->wowlan.n_patterns = IWL_WOWLAN_MAX_PATTERNS;
mvm->wowlan.pattern_min_len = IWL_WOWLAN_MIN_PATTERN_LEN;
mvm->wowlan.pattern_max_len = IWL_WOWLAN_MAX_PATTERN_LEN;
+ mvm->wowlan.max_nd_match_sets = IWL_SCAN_MAX_PROFILES;
mvm->wowlan.tcp = &iwl_mvm_wowlan_tcp_support;
hw->wiphy->wowlan = &mvm->wowlan;
}
@@ -463,6 +491,17 @@
if (ret)
return ret;
+ if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_TDLS_SUPPORT) {
+ IWL_DEBUG_TDLS(mvm, "TDLS supported\n");
+ hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
+ }
+
+ if (mvm->fw->ucode_capa.capa[0] &
+ IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH) {
+ IWL_DEBUG_TDLS(mvm, "TDLS channel switch supported\n");
+ hw->wiphy->features |= NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
+ }
+
ret = ieee80211_register_hw(mvm->hw);
if (ret)
iwl_mvm_leds_exit(mvm);
@@ -526,7 +565,8 @@
}
if (IEEE80211_SKB_CB(skb)->hw_queue == IWL_MVM_OFFCHANNEL_QUEUE &&
- !test_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status))
+ !test_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status) &&
+ !test_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status))
goto drop;
/* treat non-bufferable MMPDUs as broadcast if sta is sleeping */
@@ -678,10 +718,51 @@
memset(&mvmvif->bf_data, 0, sizeof(mvmvif->bf_data));
}
-#ifdef CPTCFG_IWLWIFI_DEBUGFS
+static ssize_t iwl_mvm_read_coredump(char *buffer, loff_t offset, size_t count,
+ const void *data, size_t datalen)
+{
+ const struct iwl_mvm_dump_ptrs *dump_ptrs = data;
+ ssize_t bytes_read;
+ ssize_t bytes_read_trans;
+
+ if (offset < dump_ptrs->op_mode_len) {
+ bytes_read = min_t(ssize_t, count,
+ dump_ptrs->op_mode_len - offset);
+ memcpy(buffer, (u8 *)dump_ptrs->op_mode_ptr + offset,
+ bytes_read);
+ offset += bytes_read;
+ count -= bytes_read;
+
+ if (count == 0)
+ return bytes_read;
+ } else {
+ bytes_read = 0;
+ }
+
+ if (!dump_ptrs->trans_ptr)
+ return bytes_read;
+
+ offset -= dump_ptrs->op_mode_len;
+ bytes_read_trans = min_t(ssize_t, count,
+ dump_ptrs->trans_ptr->len - offset);
+ memcpy(buffer + bytes_read,
+ (u8 *)dump_ptrs->trans_ptr->data + offset,
+ bytes_read_trans);
+
+ return bytes_read + bytes_read_trans;
+}
+
+static void iwl_mvm_free_coredump(const void *data)
+{
+ const struct iwl_mvm_dump_ptrs *fw_error_dump = data;
+
+ vfree(fw_error_dump->op_mode_ptr);
+ vfree(fw_error_dump->trans_ptr);
+ kfree(fw_error_dump);
+}
+
void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
{
- static char *env[] = { "DRIVER=iwlwifi", "EVENT=error_dump", NULL };
struct iwl_fw_error_dump_file *dump_file;
struct iwl_fw_error_dump_data *dump_data;
struct iwl_fw_error_dump_info *dump_info;
@@ -694,10 +775,7 @@
lockdep_assert_held(&mvm->mutex);
- if (mvm->fw_error_dump)
- return;
-
- fw_error_dump = kzalloc(sizeof(*mvm->fw_error_dump), GFP_KERNEL);
+ fw_error_dump = kzalloc(sizeof(*fw_error_dump), GFP_KERNEL);
if (!fw_error_dump)
return;
@@ -772,21 +850,25 @@
if (fw_error_dump->trans_ptr)
file_len += fw_error_dump->trans_ptr->len;
dump_file->file_len = cpu_to_le32(file_len);
- mvm->fw_error_dump = fw_error_dump;
- /* notify the userspace about the error we had */
- kobject_uevent_env(&mvm->hw->wiphy->dev.kobj, KOBJ_CHANGE, env);
+ dev_coredumpm(mvm->trans->dev, THIS_MODULE, fw_error_dump, 0,
+ GFP_KERNEL, iwl_mvm_read_coredump, iwl_mvm_free_coredump);
}
-#endif
static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
{
- iwl_mvm_fw_error_dump(mvm);
+ /* clear the D3 reconfig, we only need it to avoid dumping a
+ * firmware coredump on reconfiguration, we shouldn't do that
+ * on D3->D0 transition
+ */
+ if (!test_and_clear_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status))
+ iwl_mvm_fw_error_dump(mvm);
iwl_trans_stop_device(mvm->trans);
mvm->scan_status = IWL_MVM_SCAN_NONE;
mvm->ps_disabled = false;
+ mvm->calibrating = false;
/* just in case one was running */
ieee80211_remain_on_channel_expired(mvm->hw);
@@ -801,6 +883,7 @@
iwl_mvm_reset_phy_ctxts(mvm);
memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table));
memset(mvm->sta_drained, 0, sizeof(mvm->sta_drained));
+ memset(mvm->tfd_drained, 0, sizeof(mvm->tfd_drained));
memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
memset(&mvm->last_bt_notif_old, 0, sizeof(mvm->last_bt_notif_old));
memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd));
@@ -857,9 +940,8 @@
return ret;
}
-static void iwl_mvm_mac_restart_complete(struct ieee80211_hw *hw)
+static void iwl_mvm_restart_complete(struct iwl_mvm *mvm)
{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
int ret;
mutex_lock(&mvm->mutex);
@@ -874,9 +956,50 @@
/* allow transport/FW low power modes */
iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN);
+ /*
+ * If we have TDLS peers, remove them. We don't know the last seqno/PN
+ * of packets the FW sent out, so we must reconnect.
+ */
+ iwl_mvm_teardown_tdls_peers(mvm);
+
mutex_unlock(&mvm->mutex);
}
+static void iwl_mvm_resume_complete(struct iwl_mvm *mvm)
+{
+ bool exit_now;
+
+ if (!iwl_mvm_is_d0i3_supported(mvm))
+ return;
+
+ mutex_lock(&mvm->d0i3_suspend_mutex);
+ __clear_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags);
+ exit_now = __test_and_clear_bit(D0I3_PENDING_WAKEUP,
+ &mvm->d0i3_suspend_flags);
+ mutex_unlock(&mvm->d0i3_suspend_mutex);
+
+ if (exit_now) {
+ IWL_DEBUG_RPM(mvm, "Run deferred d0i3 exit\n");
+ _iwl_mvm_exit_d0i3(mvm);
+ }
+}
+
+static void
+iwl_mvm_mac_reconfig_complete(struct ieee80211_hw *hw,
+ enum ieee80211_reconfig_type reconfig_type)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+ switch (reconfig_type) {
+ case IEEE80211_RECONFIG_TYPE_RESTART:
+ iwl_mvm_restart_complete(mvm);
+ break;
+ case IEEE80211_RECONFIG_TYPE_SUSPEND:
+ iwl_mvm_resume_complete(mvm);
+ break;
+ }
+}
+
void __iwl_mvm_mac_stop(struct iwl_mvm *mvm)
{
lockdep_assert_held(&mvm->mutex);
@@ -1085,7 +1208,7 @@
static void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm,
struct ieee80211_vif *vif)
{
- u32 tfd_msk = iwl_mvm_mac_get_queues_mask(mvm, vif);
+ u32 tfd_msk = iwl_mvm_mac_get_queues_mask(vif);
if (tfd_msk) {
mutex_lock(&mvm->mutex);
@@ -1381,6 +1504,9 @@
.cmd = cmd,
};
+ if (IWL_MVM_FW_BCAST_FILTER_PASS_ALL)
+ return false;
+
memset(cmd, 0, sizeof(*cmd));
cmd->max_bcast_filters = ARRAY_SIZE(cmd->filters);
cmd->max_macs = ARRAY_SIZE(cmd->macs);
@@ -1734,6 +1860,13 @@
if (changes & BSS_CHANGED_BEACON &&
iwl_mvm_mac_ctxt_beacon_changed(mvm, vif))
IWL_WARN(mvm, "Failed updating beacon data\n");
+
+ if (changes & BSS_CHANGED_TXPOWER) {
+ IWL_DEBUG_CALIB(mvm, "Changing TX Power to %d\n",
+ bss_conf->txpower);
+ iwl_mvm_set_tx_power(mvm, vif, bss_conf->txpower);
+ }
+
}
static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,
@@ -1826,9 +1959,11 @@
req->n_channels > mvm->fw->ucode_capa.n_scan_channels)
return -EINVAL;
- ret = iwl_mvm_cancel_scan_wait_notif(mvm, IWL_MVM_SCAN_SCHED);
- if (ret)
- return ret;
+ if (!(mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)) {
+ ret = iwl_mvm_cancel_scan_wait_notif(mvm, IWL_MVM_SCAN_SCHED);
+ if (ret)
+ return ret;
+ }
mutex_lock(&mvm->mutex);
@@ -1839,7 +1974,9 @@
iwl_mvm_ref(mvm, IWL_MVM_REF_SCAN);
- if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
+ if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)
+ ret = iwl_mvm_scan_umac(mvm, vif, hw_req);
+ else if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
ret = iwl_mvm_unified_scan_lmac(mvm, vif, hw_req);
else
ret = iwl_mvm_scan_request(mvm, vif, req);
@@ -2056,6 +2193,15 @@
out_unlock:
mutex_unlock(&mvm->mutex);
+ if (sta->tdls && ret == 0) {
+ if (old_state == IEEE80211_STA_NOTEXIST &&
+ new_state == IEEE80211_STA_NONE)
+ ieee80211_reserve_tid(sta, IWL_MVM_TDLS_FW_TID);
+ else if (old_state == IEEE80211_STA_NONE &&
+ new_state == IEEE80211_STA_NOTEXIST)
+ ieee80211_unreserve_tid(sta, IWL_MVM_TDLS_FW_TID);
+ }
+
return ret;
}
@@ -2138,9 +2284,11 @@
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
int ret;
- ret = iwl_mvm_cancel_scan_wait_notif(mvm, IWL_MVM_SCAN_OS);
- if (ret)
- return ret;
+ if (!(mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)) {
+ ret = iwl_mvm_cancel_scan_wait_notif(mvm, IWL_MVM_SCAN_OS);
+ if (ret)
+ return ret;
+ }
mutex_lock(&mvm->mutex);
@@ -2160,27 +2308,10 @@
goto out;
}
- mvm->scan_status = IWL_MVM_SCAN_SCHED;
-
- if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)) {
- ret = iwl_mvm_config_sched_scan(mvm, vif, req, ies);
- if (ret)
- goto err;
- }
-
- ret = iwl_mvm_config_sched_scan_profiles(mvm, req);
+ ret = iwl_mvm_scan_offload_start(mvm, vif, req, ies);
if (ret)
- goto err;
+ mvm->scan_status = IWL_MVM_SCAN_NONE;
- if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
- ret = iwl_mvm_unified_sched_scan_lmac(mvm, vif, req, ies);
- else
- ret = iwl_mvm_sched_scan_start(mvm, req);
-
- if (!ret)
- goto out;
-err:
- mvm->scan_status = IWL_MVM_SCAN_NONE;
out:
mutex_unlock(&mvm->mutex);
return ret;
@@ -2198,6 +2329,7 @@
iwl_mvm_wait_for_async_handlers(mvm);
return ret;
+
}
static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
@@ -2226,12 +2358,16 @@
break;
case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104:
- /*
- * Support for TX only, at least for now, so accept
- * the key and do nothing else. Then mac80211 will
- * pass it for TX but we don't have to use it for RX.
+ /* For non-client mode, only use WEP keys for TX as we probably
+ * don't have a station yet anyway and would then have to keep
+ * track of the keys, linking them to each of the clients/peers
+ * as they appear. For now, don't do that, for performance WEP
+ * offload doesn't really matter much, but we need it for some
+ * other offload features in client mode.
*/
- return 0;
+ if (vif->type != NL80211_IFTYPE_STATION)
+ return 0;
+ break;
default:
/* currently FW supports only one optional cipher scheme */
if (hw->n_cipher_schemes &&
@@ -2367,14 +2503,19 @@
/* Set the node address */
memcpy(aux_roc_req.node_addr, vif->addr, ETH_ALEN);
+ lockdep_assert_held(&mvm->mutex);
+
+ spin_lock_bh(&mvm->time_event_lock);
+
+ if (WARN_ON(te_data->id == HOT_SPOT_CMD)) {
+ spin_unlock_bh(&mvm->time_event_lock);
+ return -EIO;
+ }
+
te_data->vif = vif;
te_data->duration = duration;
te_data->id = HOT_SPOT_CMD;
- lockdep_assert_held(&mvm->mutex);
-
- spin_lock_bh(&mvm->time_event_lock);
- list_add_tail(&te_data->list, &mvm->time_event_list);
spin_unlock_bh(&mvm->time_event_lock);
/*
@@ -2430,22 +2571,29 @@
IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value,
duration, type);
+ mutex_lock(&mvm->mutex);
+
switch (vif->type) {
case NL80211_IFTYPE_STATION:
- /* Use aux roc framework (HS20) */
- ret = iwl_mvm_send_aux_roc_cmd(mvm, channel,
- vif, duration);
- return ret;
+ if (mvm->fw->ucode_capa.capa[0] &
+ IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT) {
+ /* Use aux roc framework (HS20) */
+ ret = iwl_mvm_send_aux_roc_cmd(mvm, channel,
+ vif, duration);
+ goto out_unlock;
+ }
+ IWL_ERR(mvm, "hotspot not supported\n");
+ ret = -EINVAL;
+ goto out_unlock;
case NL80211_IFTYPE_P2P_DEVICE:
/* handle below */
break;
default:
IWL_ERR(mvm, "vif isn't P2P_DEVICE: %d\n", vif->type);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out_unlock;
}
- mutex_lock(&mvm->mutex);
-
for (i = 0; i < NUM_PHY_CTX; i++) {
phy_ctxt = &mvm->phy_ctxts[i];
if (phy_ctxt->ref == 0 || mvmvif->phy_ctxt == phy_ctxt)
@@ -2542,7 +2690,7 @@
IWL_DEBUG_MAC80211(mvm, "enter\n");
mutex_lock(&mvm->mutex);
- iwl_mvm_stop_p2p_roc(mvm);
+ iwl_mvm_stop_roc(mvm);
mutex_unlock(&mvm->mutex);
IWL_DEBUG_MAC80211(mvm, "leave\n");
@@ -2655,8 +2803,8 @@
switch (vif->type) {
case NL80211_IFTYPE_AP:
- /* Unless it's a CSA flow we have nothing to do here */
- if (vif->csa_active) {
+ /* only needed if we're switching chanctx (i.e. during CSA) */
+ if (switching_chanctx) {
mvmvif->ap_ibss_active = true;
break;
}
@@ -2700,23 +2848,32 @@
}
/* Handle binding during CSA */
- if ((vif->type == NL80211_IFTYPE_AP) ||
- (switching_chanctx && (vif->type == NL80211_IFTYPE_STATION))) {
+ if (vif->type == NL80211_IFTYPE_AP) {
iwl_mvm_update_quotas(mvm, NULL);
iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
}
- if (vif->csa_active && vif->type == NL80211_IFTYPE_STATION) {
- struct iwl_mvm_sta *mvmsta;
+ if (switching_chanctx && vif->type == NL80211_IFTYPE_STATION) {
+ u32 duration = 2 * vif->bss_conf.beacon_int;
- mvmsta = iwl_mvm_sta_from_staid_protected(mvm,
- mvmvif->ap_sta_id);
+ /* iwl_mvm_protect_session() reads directly from the
+ * device (the system time), so make sure it is
+ * available.
+ */
+ ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PROTECT_CSA);
+ if (ret)
+ goto out_remove_binding;
- if (WARN_ON(!mvmsta))
- goto out;
+ /* Protect the session to make sure we hear the first
+ * beacon on the new channel.
+ */
+ iwl_mvm_protect_session(mvm, vif, duration, duration,
+ vif->bss_conf.beacon_int / 2,
+ true);
- /* TODO: only re-enable after the first beacon */
- iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, false);
+ iwl_mvm_unref(mvm, IWL_MVM_REF_PROTECT_CSA);
+
+ iwl_mvm_update_quotas(mvm, NULL);
}
goto out;
@@ -2750,7 +2907,6 @@
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct ieee80211_vif *disabled_vif = NULL;
- struct iwl_mvm_sta *mvmsta;
lockdep_assert_held(&mvm->mutex);
@@ -2765,9 +2921,11 @@
break;
case NL80211_IFTYPE_AP:
/* This part is triggered only during CSA */
- if (!vif->csa_active || !mvmvif->ap_ibss_active)
+ if (!switching_chanctx || !mvmvif->ap_ibss_active)
goto out;
+ mvmvif->csa_countdown = false;
+
/* Set CS bit on all the stations */
iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, true);
@@ -2782,12 +2940,6 @@
disabled_vif = vif;
- mvmsta = iwl_mvm_sta_from_staid_protected(mvm,
- mvmvif->ap_sta_id);
-
- if (!WARN_ON(!mvmsta))
- iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, true);
-
iwl_mvm_mac_ctxt_changed(mvm, vif, true, NULL);
break;
default:
@@ -2813,18 +2965,12 @@
mutex_unlock(&mvm->mutex);
}
-static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw,
- struct ieee80211_vif_chanctx_switch *vifs,
- int n_vifs,
- enum ieee80211_chanctx_switch_mode mode)
+static int
+iwl_mvm_switch_vif_chanctx_swap(struct iwl_mvm *mvm,
+ struct ieee80211_vif_chanctx_switch *vifs)
{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
int ret;
- /* we only support SWAP_CONTEXTS and with a single-vif right now */
- if (mode != CHANCTX_SWMODE_SWAP_CONTEXTS || n_vifs > 1)
- return -EOPNOTSUPP;
-
mutex_lock(&mvm->mutex);
__iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, true);
__iwl_mvm_remove_chanctx(mvm, vifs[0].old_ctx);
@@ -2853,15 +2999,13 @@
__iwl_mvm_remove_chanctx(mvm, vifs[0].new_ctx);
out_reassign:
- ret = __iwl_mvm_add_chanctx(mvm, vifs[0].old_ctx);
- if (ret) {
+ if (__iwl_mvm_add_chanctx(mvm, vifs[0].old_ctx)) {
IWL_ERR(mvm, "failed to add old_ctx back after failure.\n");
goto out_restart;
}
- ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx,
- true);
- if (ret) {
+ if (__iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx,
+ true)) {
IWL_ERR(mvm, "failed to reassign old_ctx after failure.\n");
goto out_restart;
}
@@ -2874,6 +3018,72 @@
out:
mutex_unlock(&mvm->mutex);
+
+ return ret;
+}
+
+static int
+iwl_mvm_switch_vif_chanctx_reassign(struct iwl_mvm *mvm,
+ struct ieee80211_vif_chanctx_switch *vifs)
+{
+ int ret;
+
+ mutex_lock(&mvm->mutex);
+ __iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, true);
+
+ ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].new_ctx,
+ true);
+ if (ret) {
+ IWL_ERR(mvm,
+ "failed to assign new_ctx during channel switch\n");
+ goto out_reassign;
+ }
+
+ goto out;
+
+out_reassign:
+ if (__iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx,
+ true)) {
+ IWL_ERR(mvm, "failed to reassign old_ctx after failure.\n");
+ goto out_restart;
+ }
+
+ goto out;
+
+out_restart:
+ /* things keep failing, better restart the hw */
+ iwl_mvm_nic_restart(mvm, false);
+
+out:
+ mutex_unlock(&mvm->mutex);
+
+ return ret;
+}
+
+static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_vif_chanctx_switch *vifs,
+ int n_vifs,
+ enum ieee80211_chanctx_switch_mode mode)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ int ret;
+
+ /* we only support a single-vif right now */
+ if (n_vifs > 1)
+ return -EOPNOTSUPP;
+
+ switch (mode) {
+ case CHANCTX_SWMODE_SWAP_CONTEXTS:
+ ret = iwl_mvm_switch_vif_chanctx_swap(mvm, vifs);
+ break;
+ case CHANCTX_SWMODE_REASSIGN_VIF:
+ ret = iwl_mvm_switch_vif_chanctx_reassign(mvm, vifs);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
return ret;
}
@@ -2959,27 +3169,134 @@
}
#endif
-static void iwl_mvm_channel_switch_beacon(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct cfg80211_chan_def *chandef)
+static void iwl_mvm_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel_switch *chsw)
+{
+ /* By implementing this operation, we prevent mac80211 from
+ * starting its own channel switch timer, so that we can call
+ * ieee80211_chswitch_done() ourselves at the right time
+ * (which is when the absence time event starts).
+ */
+
+ IWL_DEBUG_MAC80211(IWL_MAC80211_GET_MVM(hw),
+ "dummy channel switch op\n");
+}
+
+static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel_switch *chsw)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct ieee80211_vif *csa_vif;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ u32 apply_time;
+ int ret;
mutex_lock(&mvm->mutex);
- csa_vif = rcu_dereference_protected(mvm->csa_vif,
- lockdep_is_held(&mvm->mutex));
- if (WARN(csa_vif && csa_vif->csa_active,
- "Another CSA is already in progress"))
+ IWL_DEBUG_MAC80211(mvm, "pre CSA to freq %d\n",
+ chsw->chandef.center_freq1);
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_AP:
+ csa_vif =
+ rcu_dereference_protected(mvm->csa_vif,
+ lockdep_is_held(&mvm->mutex));
+ if (WARN_ONCE(csa_vif && csa_vif->csa_active,
+ "Another CSA is already in progress")) {
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+
+ rcu_assign_pointer(mvm->csa_vif, vif);
+
+ if (WARN_ONCE(mvmvif->csa_countdown,
+ "Previous CSA countdown didn't complete")) {
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+
+ break;
+ case NL80211_IFTYPE_STATION:
+ /* Schedule the time event to a bit before beacon 1,
+ * to make sure we're in the new channel when the
+ * GO/AP arrives.
+ */
+ apply_time = chsw->device_timestamp +
+ ((vif->bss_conf.beacon_int * (chsw->count - 1) -
+ IWL_MVM_CHANNEL_SWITCH_TIME_CLIENT) * 1024);
+
+ if (chsw->block_tx)
+ iwl_mvm_csa_client_absent(mvm, vif);
+
+ iwl_mvm_schedule_csa_period(mvm, vif, vif->bss_conf.beacon_int,
+ apply_time);
+ if (mvmvif->bf_data.bf_enabled) {
+ ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0);
+ if (ret)
+ goto out_unlock;
+ }
+
+ break;
+ default:
+ break;
+ }
+
+ mvmvif->ps_disabled = true;
+
+ ret = iwl_mvm_power_update_ps(mvm);
+ if (ret)
goto out_unlock;
- IWL_DEBUG_MAC80211(mvm, "CSA started to freq %d\n",
- chandef->center_freq1);
- rcu_assign_pointer(mvm->csa_vif, vif);
+ /* we won't be on this channel any longer */
+ iwl_mvm_teardown_tdls_peers(mvm);
out_unlock:
mutex_unlock(&mvm->mutex);
+
+ return ret;
+}
+
+static int iwl_mvm_post_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ int ret;
+
+ mutex_lock(&mvm->mutex);
+
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ struct iwl_mvm_sta *mvmsta;
+
+ mvmsta = iwl_mvm_sta_from_staid_protected(mvm,
+ mvmvif->ap_sta_id);
+
+ if (WARN_ON(!mvmsta)) {
+ ret = -EIO;
+ goto out_unlock;
+ }
+
+ iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, false);
+
+ iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL);
+
+ ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0);
+ if (ret)
+ goto out_unlock;
+
+ iwl_mvm_stop_session_protection(mvm, vif);
+ }
+
+ mvmvif->ps_disabled = false;
+
+ ret = iwl_mvm_power_update_ps(mvm);
+
+out_unlock:
+ mutex_unlock(&mvm->mutex);
+
+ return ret;
}
static void iwl_mvm_mac_flush(struct ieee80211_hw *hw,
@@ -2988,33 +3305,52 @@
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct iwl_mvm_vif *mvmvif;
struct iwl_mvm_sta *mvmsta;
+ struct ieee80211_sta *sta;
+ int i;
+ u32 msk = 0;
if (!vif || vif->type != NL80211_IFTYPE_STATION)
return;
mutex_lock(&mvm->mutex);
mvmvif = iwl_mvm_vif_from_mac80211(vif);
- mvmsta = iwl_mvm_sta_from_staid_protected(mvm, mvmvif->ap_sta_id);
- if (WARN_ON_ONCE(!mvmsta))
- goto done;
+ /* flush the AP-station and all TDLS peers */
+ for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
+ sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
+ lockdep_is_held(&mvm->mutex));
+ if (IS_ERR_OR_NULL(sta))
+ continue;
+
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ if (mvmsta->vif != vif)
+ continue;
+
+ /* make sure only TDLS peers or the AP are flushed */
+ WARN_ON(i != mvmvif->ap_sta_id && !sta->tdls);
+
+ msk |= mvmsta->tfd_queue_msk;
+ }
if (drop) {
- if (iwl_mvm_flush_tx_path(mvm, mvmsta->tfd_queue_msk, true))
+ if (iwl_mvm_flush_tx_path(mvm, msk, true))
IWL_ERR(mvm, "flush request fail\n");
+ mutex_unlock(&mvm->mutex);
} else {
- iwl_trans_wait_tx_queue_empty(mvm->trans,
- mvmsta->tfd_queue_msk);
+ mutex_unlock(&mvm->mutex);
+
+ /* this can take a while, and we may need/want other operations
+ * to succeed while doing this, so do it without the mutex held
+ */
+ iwl_trans_wait_tx_queue_empty(mvm->trans, msk);
}
-done:
- mutex_unlock(&mvm->mutex);
}
const struct ieee80211_ops iwl_mvm_hw_ops = {
.tx = iwl_mvm_mac_tx,
.ampdu_action = iwl_mvm_mac_ampdu_action,
.start = iwl_mvm_mac_start,
- .restart_complete = iwl_mvm_mac_restart_complete,
+ .reconfig_complete = iwl_mvm_mac_reconfig_complete,
.stop = iwl_mvm_mac_stop,
.add_interface = iwl_mvm_mac_add_interface,
.remove_interface = iwl_mvm_mac_remove_interface,
@@ -3055,7 +3391,13 @@
.set_tim = iwl_mvm_set_tim,
- .channel_switch_beacon = iwl_mvm_channel_switch_beacon,
+ .channel_switch = iwl_mvm_channel_switch,
+ .pre_channel_switch = iwl_mvm_pre_channel_switch,
+ .post_channel_switch = iwl_mvm_post_channel_switch,
+
+ .tdls_channel_switch = iwl_mvm_tdls_channel_switch,
+ .tdls_cancel_channel_switch = iwl_mvm_tdls_cancel_channel_switch,
+ .tdls_recv_channel_switch = iwl_mvm_tdls_recv_channel_switch,
CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd)
diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h
index a1d1fc2..d4d4d72 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h
@@ -87,12 +87,18 @@
/* A TimeUnit is 1024 microsecond */
#define MSEC_TO_TU(_msec) (_msec*1000/1024)
-/* This value represents the number of TUs before CSA "beacon 0" TBTT
- * when the CSA time-event needs to be scheduled to start. It must be
- * big enough to ensure that we switch in time.
+/* For GO, this value represents the number of TUs before CSA "beacon
+ * 0" TBTT when the CSA time-event needs to be scheduled to start. It
+ * must be big enough to ensure that we switch in time.
*/
#define IWL_MVM_CHANNEL_SWITCH_TIME_GO 40
+/* For client, this value represents the number of TUs before CSA
+ * "beacon 1" TBTT, instead. This is because we don't know when the
+ * GO/AP will be in the new channel, so we switch early enough.
+ */
+#define IWL_MVM_CHANNEL_SWITCH_TIME_CLIENT 10
+
/*
* This value (in TUs) is used to fine tune the CSA NoA end time which should
* be just before "beacon 0" TBTT.
@@ -269,6 +275,7 @@
IWL_MVM_REF_NMI,
IWL_MVM_REF_TM_CMD,
IWL_MVM_REF_EXIT_WORK,
+ IWL_MVM_REF_PROTECT_CSA,
/* update debugfs.c when changing this */
@@ -288,7 +295,6 @@
* struct iwl_mvm_vif_bf_data - beacon filtering related data
* @bf_enabled: indicates if beacon filtering is enabled
* @ba_enabled: indicated if beacon abort is enabled
-* @last_beacon_signal: last beacon rssi signal in dbm
* @ave_beacon_signal: average beacon signal
* @last_cqm_event: rssi of the last cqm event
* @bt_coex_min_thold: minimum threshold for BT coex
@@ -399,6 +405,9 @@
/* FW identified misbehaving AP */
u8 uapsd_misbehaving_bssid[ETH_ALEN];
+
+ /* Indicates that CSA countdown may be started */
+ bool csa_countdown;
};
static inline struct iwl_mvm_vif *
@@ -519,6 +528,13 @@
#define IWL_MVM_DEBUG_SET_TEMPERATURE_MIN -100
#define IWL_MVM_DEBUG_SET_TEMPERATURE_MAX 200
+enum iwl_mvm_tdls_cs_state {
+ IWL_MVM_TDLS_SW_IDLE = 0,
+ IWL_MVM_TDLS_SW_REQ_SENT,
+ IWL_MVM_TDLS_SW_REQ_RCVD,
+ IWL_MVM_TDLS_SW_ACTIVE,
+};
+
struct iwl_mvm {
/* for logger access */
struct device *dev;
@@ -548,6 +564,7 @@
enum iwl_ucode_type cur_ucode;
bool ucode_loaded;
bool init_ucode_complete;
+ bool calibrating;
u32 error_event_table;
u32 log_event_table;
u32 umac_error_event_table;
@@ -577,6 +594,7 @@
struct work_struct sta_drained_wk;
unsigned long sta_drained[BITS_TO_LONGS(IWL_MVM_STATION_COUNT)];
atomic_t pending_frames[IWL_MVM_STATION_COUNT];
+ u32 tfd_drained[IWL_MVM_STATION_COUNT];
u8 rx_ba_sessions;
/* configured by mac80211 */
@@ -587,6 +605,10 @@
void *scan_cmd;
struct iwl_mcast_filter_cmd *mcast_filter_cmd;
+ /* UMAC scan tracking */
+ u32 scan_uid[IWL_MVM_MAX_SIMULTANEOUS_SCANS];
+ u8 scan_seq_num, sched_scan_seq_num;
+
/* rx chain antennas set through debugfs for the scan command */
u8 scan_rx_ant;
@@ -648,7 +670,7 @@
/* -1 for always, 0 for never, >0 for that many times */
s8 restart_fw;
struct work_struct fw_error_dump_wk;
- struct iwl_mvm_dump_ptrs *fw_error_dump;
+ enum iwl_fw_dbg_conf fw_dbg_conf;
#ifdef CPTCFG_IWLWIFI_LEDS
struct led_classdev led;
@@ -659,6 +681,15 @@
#ifdef CONFIG_PM_SLEEP
struct wiphy_wowlan_support wowlan;
int gtk_ivlen, gtk_icvlen, ptk_ivlen, ptk_icvlen;
+
+ /* sched scan settings for net detect */
+ struct cfg80211_sched_scan_request *nd_config;
+ struct ieee80211_scan_ies nd_ies;
+ struct cfg80211_match_set *nd_match_sets;
+ int n_nd_match_sets;
+ struct ieee80211_channel **nd_channels;
+ int n_nd_channels;
+ bool net_detect;
#ifdef CPTCFG_IWLWIFI_DEBUGFS
u32 d3_wake_sysassert; /* must be u32 for debugfs_create_bool */
bool d3_test_active;
@@ -731,6 +762,28 @@
u32 ap_last_beacon_gp2;
u8 low_latency_agg_frame_limit;
+
+ /* TDLS channel switch data */
+ struct {
+ struct delayed_work dwork;
+ enum iwl_mvm_tdls_cs_state state;
+
+ /*
+ * Current cs sta - might be different from periodic cs peer
+ * station. Value is meaningless when the cs-state is idle.
+ */
+ u8 cur_sta_id;
+
+ /* TDLS periodic channel-switch peer */
+ struct {
+ u8 sta_id;
+ u8 op_class;
+ bool initiator; /* are we the link initiator */
+ struct cfg80211_chan_def chandef;
+ struct sk_buff *skb; /* ch sw template */
+ u32 ch_sw_tm_ie;
+ } peer;
+ } tdls_cs;
};
/* Extract MVM priv from op_mode and _hw */
@@ -747,6 +800,7 @@
IWL_MVM_STATUS_IN_HW_RESTART,
IWL_MVM_STATUS_IN_D0I3,
IWL_MVM_STATUS_ROC_AUX_RUNNING,
+ IWL_MVM_STATUS_D3_RECONFIG,
};
static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm)
@@ -755,6 +809,26 @@
test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status);
}
+/* Must be called with rcu_read_lock() held and it can only be
+ * released when mvmsta is not needed anymore.
+ */
+static inline struct iwl_mvm_sta *
+iwl_mvm_sta_from_staid_rcu(struct iwl_mvm *mvm, u8 sta_id)
+{
+ struct ieee80211_sta *sta;
+
+ if (sta_id >= ARRAY_SIZE(mvm->fw_id_to_mac_id))
+ return NULL;
+
+ sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
+
+ /* This can happen if the station has been removed right now */
+ if (IS_ERR_OR_NULL(sta))
+ return NULL;
+
+ return iwl_mvm_sta_from_mac80211(sta);
+}
+
static inline struct iwl_mvm_sta *
iwl_mvm_sta_from_staid_protected(struct iwl_mvm *mvm, u8 sta_id)
{
@@ -828,6 +902,16 @@
int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb,
struct ieee80211_sta *sta);
int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb);
+void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
+ struct iwl_tx_cmd *tx_cmd,
+ struct ieee80211_tx_info *info, u8 sta_id);
+void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm,
+ struct ieee80211_tx_info *info,
+ struct iwl_tx_cmd *tx_cmd,
+ struct sk_buff *skb_frag);
+void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd *tx_cmd,
+ struct ieee80211_tx_info *info,
+ struct ieee80211_sta *sta, __le16 fc);
#ifdef CPTCFG_IWLWIFI_DEBUG
const char *iwl_mvm_get_tx_fail_reason(u32 status);
#else
@@ -884,6 +968,8 @@
struct iwl_device_cmd *cmd);
int iwl_mvm_rx_radio_ver(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
+int iwl_mvm_rx_mfuart_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+ struct iwl_device_cmd *cmd);
/* MVM PHY */
int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
@@ -897,6 +983,8 @@
void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm,
struct iwl_mvm_phy_ctxt *ctxt);
int iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm);
+u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef);
+u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef);
/* MAC (virtual interface) programming */
int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
@@ -905,8 +993,7 @@
int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
bool force_assoc_off, const u8 *bssid_override);
int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
-u32 iwl_mvm_mac_get_queues_mask(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif);
+u32 iwl_mvm_mac_get_queues_mask(struct ieee80211_vif *vif);
int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm,
struct ieee80211_vif *vif);
int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
@@ -917,6 +1004,8 @@
struct iwl_device_cmd *cmd);
void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm,
struct ieee80211_vif *vif);
+unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm,
+ struct ieee80211_vif *exclude_vif);
/* Bindings */
int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
@@ -927,6 +1016,7 @@
struct ieee80211_vif *disabled_vif);
/* Scanning */
+int iwl_mvm_scan_size(struct iwl_mvm *mvm);
int iwl_mvm_scan_request(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct cfg80211_scan_request *req);
@@ -949,6 +1039,10 @@
struct cfg80211_sched_scan_request *req);
int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
struct cfg80211_sched_scan_request *req);
+int iwl_mvm_scan_offload_start(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct cfg80211_sched_scan_request *req,
+ struct ieee80211_scan_ies *ies);
int iwl_mvm_scan_offload_stop(struct iwl_mvm *mvm, bool notify);
int iwl_mvm_rx_scan_offload_results(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb,
@@ -963,6 +1057,17 @@
struct cfg80211_sched_scan_request *req,
struct ieee80211_scan_ies *ies);
+/* UMAC scan */
+int iwl_mvm_config_scan(struct iwl_mvm *mvm);
+int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ struct ieee80211_scan_request *req);
+int iwl_mvm_sched_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ struct cfg80211_sched_scan_request *req,
+ struct ieee80211_scan_ies *ies);
+int iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb,
+ struct iwl_device_cmd *cmd);
+
/* MVM debugfs */
#ifdef CPTCFG_IWLWIFI_DEBUGFS
int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir);
@@ -1042,7 +1147,7 @@
}
#endif
void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta,
- struct iwl_wowlan_config_cmd_v2 *cmd);
+ struct iwl_wowlan_config_cmd *cmd);
int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
bool disable_offloading,
@@ -1052,6 +1157,7 @@
void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
+bool iwl_mvm_ref_taken(struct iwl_mvm *mvm);
void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq);
int _iwl_mvm_exit_d0i3(struct iwl_mvm *mvm);
@@ -1067,12 +1173,14 @@
struct ieee80211_sta *sta);
bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,
struct ieee80211_sta *sta);
+bool iwl_mvm_bt_coex_is_ant_avail(struct iwl_mvm *mvm, u8 ant);
bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm);
bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm,
enum ieee80211_band band);
u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
struct ieee80211_tx_info *info, u8 ac);
+bool iwl_mvm_bt_coex_is_ant_avail_old(struct iwl_mvm *mvm, u8 ant);
bool iwl_mvm_bt_coex_is_shared_ant_avail_old(struct iwl_mvm *mvm);
void iwl_mvm_bt_coex_vif_change_old(struct iwl_mvm *mvm);
int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm);
@@ -1188,6 +1296,10 @@
/* Thermal management and CT-kill */
void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff);
+void iwl_mvm_tt_temp_changed(struct iwl_mvm *mvm, u32 temp);
+int iwl_mvm_temp_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb,
+ struct iwl_device_cmd *cmd);
void iwl_mvm_tt_handler(struct iwl_mvm *mvm);
void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff);
void iwl_mvm_tt_exit(struct iwl_mvm *mvm);
@@ -1199,18 +1311,37 @@
bool added_vif);
/* TDLS */
+
+/*
+ * We use TID 4 (VI) as a FW-used-only TID when TDLS connections are present.
+ * This TID is marked as used vs the AP and all connected TDLS peers.
+ */
+#define IWL_MVM_TDLS_FW_TID 4
+
int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
void iwl_mvm_teardown_tdls_peers(struct iwl_mvm *mvm);
void iwl_mvm_recalc_tdls_state(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
bool sta_added);
void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw,
struct ieee80211_vif *vif);
+int iwl_mvm_tdls_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u8 oper_class,
+ struct cfg80211_chan_def *chandef,
+ struct sk_buff *tmpl_skb, u32 ch_sw_tm_ie);
+void iwl_mvm_tdls_recv_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_tdls_ch_sw_params *params);
+void iwl_mvm_tdls_cancel_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+int iwl_mvm_rx_tdls_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+ struct iwl_device_cmd *cmd);
+void iwl_mvm_tdls_ch_switch_work(struct work_struct *work);
+
+struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm);
void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error);
-#ifdef CPTCFG_IWLWIFI_DEBUGFS
void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm);
-#else
-static inline void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) {}
-#endif
#endif /* __IWL_MVM_H__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c
index f23b8fd..98203ab 100644
--- a/drivers/net/wireless/iwlwifi/mvm/nvm.c
+++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c
@@ -339,11 +339,15 @@
} *file_sec;
const u8 *eof, *temp;
int max_section_size;
+ const __le32 *dword_buff;
#define NVM_WORD1_LEN(x) (8 * (x & 0x03FF))
#define NVM_WORD2_ID(x) (x >> 12)
#define NVM_WORD2_LEN_FAMILY_8000(x) (2 * ((x & 0xFF) << 8 | x >> 8))
#define NVM_WORD1_ID_FAMILY_8000(x) (x >> 4)
+#define NVM_HEADER_0 (0x2A504C54)
+#define NVM_HEADER_1 (0x4E564D2A)
+#define NVM_HEADER_SIZE (4 * sizeof(u32))
IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from external NVM\n");
@@ -372,12 +376,6 @@
IWL_INFO(mvm, "Loaded NVM file %s (%zu bytes)\n",
mvm->nvm_file_name, fw_entry->size);
- if (fw_entry->size < sizeof(*file_sec)) {
- IWL_ERR(mvm, "NVM file too small\n");
- ret = -EINVAL;
- goto out;
- }
-
if (fw_entry->size > MAX_NVM_FILE_LEN) {
IWL_ERR(mvm, "NVM file too large\n");
ret = -EINVAL;
@@ -385,8 +383,25 @@
}
eof = fw_entry->data + fw_entry->size;
+ dword_buff = (__le32 *)fw_entry->data;
- file_sec = (void *)fw_entry->data;
+ /* some NVM file will contain a header.
+ * The header is identified by 2 dwords header as follow:
+ * dword[0] = 0x2A504C54
+ * dword[1] = 0x4E564D2A
+ *
+ * This header must be skipped when providing the NVM data to the FW.
+ */
+ if (fw_entry->size > NVM_HEADER_SIZE &&
+ dword_buff[0] == cpu_to_le32(NVM_HEADER_0) &&
+ dword_buff[1] == cpu_to_le32(NVM_HEADER_1)) {
+ file_sec = (void *)(fw_entry->data + NVM_HEADER_SIZE);
+ IWL_INFO(mvm, "NVM Version %08X\n", le32_to_cpu(dword_buff[2]));
+ IWL_INFO(mvm, "NVM Manufacturing date %08X\n",
+ le32_to_cpu(dword_buff[3]));
+ } else {
+ file_sec = (void *)fw_entry->data;
+ }
while (true) {
if (file_sec->data > eof) {
diff --git a/drivers/net/wireless/iwlwifi/mvm/offloading.c b/drivers/net/wireless/iwlwifi/mvm/offloading.c
index adcbf4c..68b0169 100644
--- a/drivers/net/wireless/iwlwifi/mvm/offloading.c
+++ b/drivers/net/wireless/iwlwifi/mvm/offloading.c
@@ -67,7 +67,7 @@
#include "mvm.h"
void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta,
- struct iwl_wowlan_config_cmd_v2 *cmd)
+ struct iwl_wowlan_config_cmd *cmd)
{
int i;
diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c
index 4de8ec8..f10c327 100644
--- a/drivers/net/wireless/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/iwlwifi/mvm/ops.c
@@ -244,6 +244,8 @@
iwl_mvm_rx_scan_offload_complete_notif, true),
RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_scan_offload_results,
false),
+ RX_HANDLER(SCAN_COMPLETE_UMAC, iwl_mvm_rx_umac_scan_complete_notif,
+ true),
RX_HANDLER(RADIO_VERSION_NOTIFICATION, iwl_mvm_rx_radio_ver, false),
RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif, false),
@@ -254,6 +256,12 @@
RX_HANDLER(REPLY_ERROR, iwl_mvm_rx_fw_error, false),
RX_HANDLER(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION,
iwl_mvm_power_uapsd_misbehaving_ap_notif, false),
+ RX_HANDLER(DTS_MEASUREMENT_NOTIFICATION, iwl_mvm_temp_notif, true),
+
+ RX_HANDLER(TDLS_CHANNEL_SWITCH_NOTIFICATION, iwl_mvm_rx_tdls_notif,
+ true),
+ RX_HANDLER(MFUART_LOAD_NOTIFICATION, iwl_mvm_rx_mfuart_notif, false),
+
};
#undef RX_HANDLER
#define CMD(x) [x] = #x
@@ -317,11 +325,9 @@
CMD(WOWLAN_KEK_KCK_MATERIAL),
CMD(WOWLAN_GET_STATUSES),
CMD(WOWLAN_TX_POWER_PER_DB),
- CMD(NET_DETECT_CONFIG_CMD),
- CMD(NET_DETECT_PROFILES_QUERY_CMD),
- CMD(NET_DETECT_PROFILES_CMD),
- CMD(NET_DETECT_HOTSPOTS_CMD),
- CMD(NET_DETECT_HOTSPOTS_QUERY_CMD),
+ CMD(SCAN_OFFLOAD_PROFILES_QUERY_CMD),
+ CMD(SCAN_OFFLOAD_HOTSPOTS_CONFIG_CMD),
+ CMD(SCAN_OFFLOAD_HOTSPOTS_QUERY_CMD),
CMD(CARD_STATE_NOTIFICATION),
CMD(MISSED_BEACONS_NOTIFICATION),
CMD(BT_COEX_PRIO_TABLE),
@@ -336,6 +342,7 @@
CMD(DTS_MEASUREMENT_NOTIFICATION),
CMD(REPLY_THERMAL_MNG_BACKOFF),
CMD(MAC_PM_POWER_TABLE),
+ CMD(LTR_CONFIG),
CMD(BT_COEX_CI),
CMD(BT_COEX_UPDATE_SW_BOOST),
CMD(BT_COEX_UPDATE_CORUN_LUT),
@@ -343,6 +350,13 @@
CMD(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION),
CMD(ANTENNA_COUPLING_NOTIFICATION),
CMD(SCD_QUEUE_CFG),
+ CMD(SCAN_CFG_CMD),
+ CMD(SCAN_REQ_UMAC),
+ CMD(SCAN_ABORT_UMAC),
+ CMD(SCAN_COMPLETE_UMAC),
+ CMD(TDLS_CHANNEL_SWITCH_CMD),
+ CMD(TDLS_CHANNEL_SWITCH_NOTIFICATION),
+ CMD(TDLS_CONFIG_CMD),
};
#undef CMD
@@ -402,6 +416,9 @@
if (cfg->max_rx_agg_size)
hw->max_rx_aggregation_subframes = cfg->max_rx_agg_size;
+ if (cfg->max_tx_agg_size)
+ hw->max_tx_aggregation_subframes = cfg->max_tx_agg_size;
+
op_mode = hw->priv;
op_mode->ops = &iwl_mvm_ops;
@@ -423,6 +440,7 @@
}
mvm->sf_state = SF_UNINIT;
mvm->low_latency_agg_frame_limit = 6;
+ mvm->cur_ucode = IWL_UCODE_INIT;
mutex_init(&mvm->mutex);
mutex_init(&mvm->d0i3_suspend_mutex);
@@ -437,6 +455,7 @@
INIT_WORK(&mvm->sta_drained_wk, iwl_mvm_sta_drained_wk);
INIT_WORK(&mvm->d0i3_exit_work, iwl_mvm_d0i3_exit_work);
INIT_WORK(&mvm->fw_error_dump_wk, iwl_mvm_fw_error_dump_wk);
+ INIT_DELAYED_WORK(&mvm->tdls_cs.dwork, iwl_mvm_tdls_ch_switch_work);
spin_lock_init(&mvm->d0i3_tx_lock);
spin_lock_init(&mvm->refs_lock);
@@ -477,6 +496,10 @@
trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD;
trans->rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start);
+ trans->dbg_dest_tlv = mvm->fw->dbg_dest_tlv;
+ trans->dbg_dest_reg_num = mvm->fw->dbg_dest_reg_num;
+ memcpy(trans->dbg_conf_tlv, mvm->fw->dbg_conf_tlv,
+ sizeof(trans->dbg_conf_tlv));
/* set up notification wait support */
iwl_notification_wait_init(&mvm->notif_wait);
@@ -520,7 +543,8 @@
mutex_lock(&mvm->mutex);
err = iwl_run_init_mvm_ucode(mvm, true);
- iwl_trans_stop_device(trans);
+ if (!err || !iwlmvm_mod_params.init_dbg)
+ iwl_trans_stop_device(trans);
mutex_unlock(&mvm->mutex);
/* returns 0 if successful, 1 if success but in rfkill */
if (err < 0 && !iwlmvm_mod_params.init_dbg) {
@@ -529,16 +553,7 @@
}
}
- if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
- scan_size = sizeof(struct iwl_scan_req_unified_lmac) +
- sizeof(struct iwl_scan_channel_cfg_lmac) *
- mvm->fw->ucode_capa.n_scan_channels +
- sizeof(struct iwl_scan_probe_req);
- else
- scan_size = sizeof(struct iwl_scan_cmd) +
- mvm->fw->ucode_capa.max_probe_length +
- mvm->fw->ucode_capa.n_scan_channels *
- sizeof(struct iwl_scan_channel);
+ scan_size = iwl_mvm_scan_size(mvm);
mvm->scan_cmd = kmalloc(scan_size, GFP_KERNEL);
if (!mvm->scan_cmd)
@@ -583,16 +598,16 @@
ieee80211_unregister_hw(mvm->hw);
kfree(mvm->scan_cmd);
- if (mvm->fw_error_dump) {
- vfree(mvm->fw_error_dump->op_mode_ptr);
- vfree(mvm->fw_error_dump->trans_ptr);
- kfree(mvm->fw_error_dump);
- }
kfree(mvm->mcast_filter_cmd);
mvm->mcast_filter_cmd = NULL;
#if defined(CONFIG_PM_SLEEP) && defined(CPTCFG_IWLWIFI_DEBUGFS)
kfree(mvm->d3_resume_sram);
+ if (mvm->nd_config) {
+ kfree(mvm->nd_config->match_sets);
+ kfree(mvm->nd_config);
+ mvm->nd_config = NULL;
+ }
#endif
iwl_trans_op_mode_leave(mvm->trans);
@@ -751,6 +766,7 @@
static bool iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
{
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+ bool calibrating = ACCESS_ONCE(mvm->calibrating);
if (state)
set_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status);
@@ -759,7 +775,15 @@
wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm));
- return state && mvm->cur_ucode != IWL_UCODE_INIT;
+ /* iwl_run_init_mvm_ucode is waiting for results, abort it */
+ if (calibrating)
+ iwl_abort_notification_waits(&mvm->notif_wait);
+
+ /*
+ * Stop the device if we run OPERATIONAL firmware or if we are in the
+ * middle of the calibrations.
+ */
+ return state && (mvm->cur_ucode != IWL_UCODE_INIT || calibrating);
}
static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb)
@@ -980,7 +1004,7 @@
}
static void iwl_mvm_set_wowlan_data(struct iwl_mvm *mvm,
- struct iwl_wowlan_config_cmd_v3 *cmd,
+ struct iwl_wowlan_config_cmd *cmd,
struct iwl_d0i3_iter_data *iter_data)
{
struct ieee80211_sta *ap_sta;
@@ -996,14 +1020,14 @@
goto out;
mvm_ap_sta = iwl_mvm_sta_from_mac80211(ap_sta);
- cmd->common.is_11n_connection = ap_sta->ht_cap.ht_supported;
+ cmd->is_11n_connection = ap_sta->ht_cap.ht_supported;
cmd->offloading_tid = iter_data->offloading_tid;
/*
* The d0i3 uCode takes care of the nonqos counters,
* so configure only the qos seq ones.
*/
- iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, &cmd->common);
+ iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, cmd);
out:
rcu_read_unlock();
}
@@ -1015,14 +1039,11 @@
struct iwl_d0i3_iter_data d0i3_iter_data = {
.mvm = mvm,
};
- struct iwl_wowlan_config_cmd_v3 wowlan_config_cmd = {
- .common = {
- .wakeup_filter =
- cpu_to_le32(IWL_WOWLAN_WAKEUP_RX_FRAME |
- IWL_WOWLAN_WAKEUP_BEACON_MISS |
- IWL_WOWLAN_WAKEUP_LINK_CHANGE |
- IWL_WOWLAN_WAKEUP_BCN_FILTERING),
- },
+ struct iwl_wowlan_config_cmd wowlan_config_cmd = {
+ .wakeup_filter = cpu_to_le32(IWL_WOWLAN_WAKEUP_RX_FRAME |
+ IWL_WOWLAN_WAKEUP_BEACON_MISS |
+ IWL_WOWLAN_WAKEUP_LINK_CHANGE |
+ IWL_WOWLAN_WAKEUP_BCN_FILTERING),
};
struct iwl_d3_manager_config d3_cfg_cmd = {
.min_sleep_time = cpu_to_le32(1000),
@@ -1034,6 +1055,19 @@
set_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status);
synchronize_net();
+ /*
+ * iwl_mvm_ref_sync takes a reference before checking the flag.
+ * so by checking there is no held reference we prevent a state
+ * in which iwl_mvm_ref_sync continues successfully while we
+ * configure the firmware to enter d0i3
+ */
+ if (iwl_mvm_ref_taken(mvm)) {
+ IWL_DEBUG_RPM(mvm->trans, "abort d0i3 due to taken ref\n");
+ clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status);
+ wake_up(&mvm->d0i3_exit_waitq);
+ return 1;
+ }
+
ieee80211_iterate_active_interfaces_atomic(mvm->hw,
IEEE80211_IFACE_ITER_NORMAL,
iwl_mvm_enter_d0i3_iterator,
diff --git a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
index 12283b5..1c0d4a4 100644
--- a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
+++ b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
@@ -68,7 +68,7 @@
#include "mvm.h"
/* Maps the driver specific channel width definition to the the fw values */
-static inline u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef)
+u8 iwl_mvm_get_channel_width(struct cfg80211_chan_def *chandef)
{
switch (chandef->width) {
case NL80211_CHAN_WIDTH_20_NOHT:
@@ -90,7 +90,7 @@
* Maps the driver specific control channel position (relative to the center
* freq) definitions to the the fw values
*/
-static inline u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef)
+u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef)
{
switch (chandef->chan->center_freq - chandef->center_freq1) {
case -70:
diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c
index 27db05b..a33a46e 100644
--- a/drivers/net/wireless/iwlwifi/mvm/power.c
+++ b/drivers/net/wireless/iwlwifi/mvm/power.c
@@ -286,6 +286,27 @@
return true;
}
+static int iwl_mvm_power_get_skip_over_dtim(int dtimper, int bi)
+{
+ int numerator;
+ int dtim_interval = dtimper * bi;
+
+ if (WARN_ON(!dtim_interval))
+ return 0;
+
+ if (dtimper == 1) {
+ if (bi > 100)
+ numerator = 408;
+ else
+ numerator = 510;
+ } else if (dtimper < 10) {
+ numerator = 612;
+ } else {
+ return 0;
+ }
+ return max(1, (numerator / dtim_interval));
+}
+
static bool iwl_mvm_power_is_radar(struct ieee80211_vif *vif)
{
struct ieee80211_chanctx_conf *chanctx_conf;
@@ -308,7 +329,7 @@
struct ieee80211_vif *vif,
struct iwl_mac_power_cmd *cmd)
{
- int dtimper, dtimper_msec;
+ int dtimper, bi;
int keep_alive;
bool radar_detect = false;
struct iwl_mvm_vif *mvmvif __maybe_unused =
@@ -317,6 +338,7 @@
cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
mvmvif->color));
dtimper = vif->bss_conf.dtim_period;
+ bi = vif->bss_conf.beacon_int;
/*
* Regardless of power management state the driver must set
@@ -324,10 +346,9 @@
* immediately after association. Check that keep alive period
* is at least 3 * DTIM
*/
- dtimper_msec = dtimper * vif->bss_conf.beacon_int;
- keep_alive = max_t(int, 3 * dtimper_msec,
- MSEC_PER_SEC * POWER_KEEP_ALIVE_PERIOD_SEC);
- keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC);
+ keep_alive = DIV_ROUND_UP(ieee80211_tu_to_usec(3 * dtimper * bi),
+ USEC_PER_SEC);
+ keep_alive = max(keep_alive, POWER_KEEP_ALIVE_PERIOD_SEC);
cmd->keep_alive_seconds = cpu_to_le16(keep_alive);
if (mvm->ps_disabled)
@@ -352,11 +373,14 @@
radar_detect = iwl_mvm_power_is_radar(vif);
/* Check skip over DTIM conditions */
- if (!radar_detect && (dtimper <= 10) &&
+ if (!radar_detect && (dtimper < 10) &&
(iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP ||
mvm->cur_ucode == IWL_UCODE_WOWLAN)) {
- cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
- cmd->skip_dtim_periods = 3;
+ cmd->skip_dtim_periods =
+ iwl_mvm_power_get_skip_over_dtim(dtimper, bi);
+ if (cmd->skip_dtim_periods)
+ cmd->flags |=
+ cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
}
if (mvm->cur_ucode != IWL_UCODE_WOWLAN) {
diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c
index 287e744..cf25e6b 100644
--- a/drivers/net/wireless/iwlwifi/mvm/rs.c
+++ b/drivers/net/wireless/iwlwifi/mvm/rs.c
@@ -158,6 +158,12 @@
allow_column_func_t checks[MAX_COLUMN_CHECKS];
};
+static bool rs_ant_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
+ struct iwl_scale_tbl_info *tbl)
+{
+ return iwl_mvm_bt_coex_is_ant_avail(mvm, tbl->rate.ant);
+}
+
static bool rs_mimo_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
struct iwl_scale_tbl_info *tbl)
{
@@ -218,6 +224,9 @@
RS_COLUMN_INVALID,
RS_COLUMN_INVALID,
},
+ .checks = {
+ rs_ant_allow,
+ },
},
[RS_COLUMN_LEGACY_ANT_B] = {
.mode = RS_LEGACY,
@@ -231,6 +240,9 @@
RS_COLUMN_INVALID,
RS_COLUMN_INVALID,
},
+ .checks = {
+ rs_ant_allow,
+ },
},
[RS_COLUMN_SISO_ANT_A] = {
.mode = RS_SISO,
@@ -246,6 +258,7 @@
},
.checks = {
rs_siso_allow,
+ rs_ant_allow,
},
},
[RS_COLUMN_SISO_ANT_B] = {
@@ -262,6 +275,7 @@
},
.checks = {
rs_siso_allow,
+ rs_ant_allow,
},
},
[RS_COLUMN_SISO_ANT_A_SGI] = {
@@ -279,6 +293,7 @@
},
.checks = {
rs_siso_allow,
+ rs_ant_allow,
rs_sgi_allow,
},
},
@@ -297,6 +312,7 @@
},
.checks = {
rs_siso_allow,
+ rs_ant_allow,
rs_sgi_allow,
},
},
@@ -505,10 +521,11 @@
static inline void rs_dump_rate(struct iwl_mvm *mvm, const struct rs_rate *rate,
const char *prefix)
{
- IWL_DEBUG_RATE(mvm, "%s: (%s: %d) ANT: %s BW: %d SGI: %d LDPC: %d\n",
+ IWL_DEBUG_RATE(mvm,
+ "%s: (%s: %d) ANT: %s BW: %d SGI: %d LDPC: %d STBC: %d\n",
prefix, rs_pretty_lq_type(rate->type),
rate->index, rs_pretty_ant(rate->ant),
- rate->bw, rate->sgi, rate->ldpc);
+ rate->bw, rate->sgi, rate->ldpc, rate->stbc);
}
static void rs_rate_scale_clear_window(struct iwl_rate_scale_data *window)
@@ -741,6 +758,12 @@
IWL_ERR(mvm, "Invalid rate->type %d\n", rate->type);
}
+ if (is_siso(rate) && rate->stbc) {
+ /* To enable STBC we need to set both a flag and ANT_AB */
+ ucode_rate |= RATE_MCS_ANT_AB_MSK;
+ ucode_rate |= RATE_MCS_VHT_STBC_MSK;
+ }
+
ucode_rate |= rate->bw;
if (rate->sgi)
ucode_rate |= RATE_MCS_SGI_MSK;
@@ -785,6 +808,8 @@
rate->sgi = true;
if (ucode_rate & RATE_MCS_LDPC_MSK)
rate->ldpc = true;
+ if (ucode_rate & RATE_MCS_VHT_STBC_MSK)
+ rate->stbc = true;
rate->bw = ucode_rate & RATE_MCS_CHAN_WIDTH_MSK;
@@ -794,7 +819,7 @@
if (nss == 1) {
rate->type = LQ_HT_SISO;
- WARN_ON_ONCE(num_of_ant != 1);
+ WARN_ON_ONCE(!rate->stbc && num_of_ant != 1);
} else if (nss == 2) {
rate->type = LQ_HT_MIMO2;
WARN_ON_ONCE(num_of_ant != 2);
@@ -807,7 +832,7 @@
if (nss == 1) {
rate->type = LQ_VHT_SISO;
- WARN_ON_ONCE(num_of_ant != 1);
+ WARN_ON_ONCE(!rate->stbc && num_of_ant != 1);
} else if (nss == 2) {
rate->type = LQ_VHT_MIMO2;
WARN_ON_ONCE(num_of_ant != 2);
@@ -992,7 +1017,15 @@
static inline bool rs_rate_match(struct rs_rate *a,
struct rs_rate *b)
{
- return (a->type == b->type) && (a->ant == b->ant) && (a->sgi == b->sgi);
+ bool ant_match;
+
+ if (a->stbc)
+ ant_match = (b->ant == ANT_A || b->ant == ANT_B);
+ else
+ ant_match = (a->ant == b->ant);
+
+ return (a->type == b->type) && (a->bw == b->bw) && (a->sgi == b->sgi)
+ && ant_match;
}
static u32 rs_ch_width_from_mac_flags(enum mac80211_rate_control_flags flags)
@@ -1093,10 +1126,11 @@
if (time_after(jiffies,
(unsigned long)(lq_sta->last_tx + RS_IDLE_TIMEOUT))) {
- int tid;
+ int t;
+
IWL_DEBUG_RATE(mvm, "Tx idle for too long. reinit rs\n");
- for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++)
- ieee80211_stop_tx_ba_session(sta, tid);
+ for (t = 0; t < IWL_MAX_TID_COUNT; t++)
+ ieee80211_stop_tx_ba_session(sta, t);
iwl_mvm_rs_rate_init(mvm, sta, info->band, false);
return;
@@ -1137,16 +1171,15 @@
/* Rate did match, so reset the missed_rate_counter */
lq_sta->missed_rate_counter = 0;
- /* Figure out if rate scale algorithm is in active or search table */
- if (rs_rate_match(&rate,
- &(lq_sta->lq_info[lq_sta->active_tbl].rate))) {
+ if (!lq_sta->search_better_tbl) {
curr_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
other_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
- } else if (rs_rate_match(&rate,
- &lq_sta->lq_info[1 - lq_sta->active_tbl].rate)) {
+ } else {
curr_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]);
other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
- } else {
+ }
+
+ if (WARN_ON_ONCE(!rs_rate_match(&rate, &curr_tbl->rate))) {
IWL_DEBUG_RATE(mvm,
"Neither active nor search matches tx rate\n");
tmp_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
@@ -1171,6 +1204,13 @@
* first index into rate scale table.
*/
if (info->flags & IEEE80211_TX_STAT_AMPDU) {
+ /* ampdu_ack_len = 0 marks no BA was received. In this case
+ * treat it as a single frame loss as we don't want the success
+ * ratio to dip too quickly because a BA wasn't received
+ */
+ if (info->status.ampdu_ack_len == 0)
+ info->status.ampdu_len = 1;
+
ucode_rate = le32_to_cpu(table->rs_table[0]);
rs_rate_from_ucode_rate(ucode_rate, info->band, &rate);
rs_collect_tx_data(lq_sta, curr_tbl, rate.index,
@@ -1225,7 +1265,7 @@
IWL_DEBUG_RATE(mvm, "reduced txpower: %d\n", reduced_txp);
done:
/* See if there's a better rate or modulation mode to try. */
- if (sta && sta->supp_rates[info->band])
+ if (sta->supp_rates[info->band])
rs_rate_scale_perform(mvm, sta, lq_sta, tid);
}
@@ -1623,6 +1663,8 @@
else
rate->type = LQ_LEGACY_G;
+ rate->bw = RATE_MCS_CHAN_WIDTH_20;
+ rate->ldpc = false;
rate_mask = lq_sta->active_legacy_rate;
} else if (column->mode == RS_SISO) {
rate->type = lq_sta->is_vht ? LQ_VHT_SISO : LQ_HT_SISO;
@@ -1634,8 +1676,11 @@
WARN_ON_ONCE("Bad column mode");
}
- rate->bw = rs_bw_from_sta_bw(sta);
- rate->ldpc = lq_sta->ldpc;
+ if (column->mode != RS_LEGACY) {
+ rate->bw = rs_bw_from_sta_bw(sta);
+ rate->ldpc = lq_sta->ldpc;
+ }
+
search_tbl->column = col_id;
rs_set_expected_tpt_table(lq_sta, search_tbl);
@@ -1754,6 +1799,29 @@
return action;
}
+static bool rs_stbc_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
+ struct iwl_lq_sta *lq_sta)
+{
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ struct ieee80211_vif *vif = mvmsta->vif;
+ bool sta_ps_disabled = (vif->type == NL80211_IFTYPE_STATION &&
+ !vif->bss_conf.ps);
+
+ /* Our chip supports Tx STBC and the peer is an HT/VHT STA which
+ * supports STBC of at least 1*SS
+ */
+ if (!lq_sta->stbc)
+ return false;
+
+ if (!mvm->ps_disabled && !sta_ps_disabled)
+ return false;
+
+ if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta))
+ return false;
+
+ return true;
+}
+
static void rs_get_adjacent_txp(struct iwl_mvm *mvm, int index,
int *weaker, int *stronger)
{
@@ -2675,6 +2743,11 @@
if (mvm->cfg->ht_params->ldpc &&
(ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING))
lq_sta->ldpc = true;
+
+ if (mvm->cfg->ht_params->stbc &&
+ (num_of_ant(mvm->fw->valid_tx_ant) > 1) &&
+ (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC))
+ lq_sta->stbc = true;
} else {
rs_vht_set_enabled_rates(sta, vht_cap, lq_sta);
lq_sta->is_vht = true;
@@ -2682,8 +2755,16 @@
if (mvm->cfg->ht_params->ldpc &&
(vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC))
lq_sta->ldpc = true;
+
+ if (mvm->cfg->ht_params->stbc &&
+ (num_of_ant(mvm->fw->valid_tx_ant) > 1) &&
+ (vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK))
+ lq_sta->stbc = true;
}
+ if (IWL_MVM_RS_DISABLE_MIMO)
+ lq_sta->active_mimo2_rate = 0;
+
lq_sta->max_legacy_rate_idx = find_last_bit(&lq_sta->active_legacy_rate,
BITS_PER_LONG);
lq_sta->max_siso_rate_idx = find_last_bit(&lq_sta->active_siso_rate,
@@ -2692,11 +2773,11 @@
BITS_PER_LONG);
IWL_DEBUG_RATE(mvm,
- "RATE MASK: LEGACY=%lX SISO=%lX MIMO2=%lX VHT=%d LDPC=%d\n",
+ "RATE MASK: LEGACY=%lX SISO=%lX MIMO2=%lX VHT=%d LDPC=%d STBC%d\n",
lq_sta->active_legacy_rate,
lq_sta->active_siso_rate,
lq_sta->active_mimo2_rate,
- lq_sta->is_vht, lq_sta->ldpc);
+ lq_sta->is_vht, lq_sta->ldpc, lq_sta->stbc);
IWL_DEBUG_RATE(mvm, "MAX RATE: LEGACY=%d SISO=%d MIMO2=%d\n",
lq_sta->max_legacy_rate_idx,
lq_sta->max_siso_rate_idx,
@@ -2820,6 +2901,7 @@
* rate[15] 0x800D Legacy | ANT: B Rate: 6 Mbps
*/
static void rs_build_rates_table(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta,
struct iwl_lq_sta *lq_sta,
const struct rs_rate *initial_rate)
{
@@ -2832,6 +2914,7 @@
memcpy(&rate, initial_rate, sizeof(rate));
valid_tx_ant = mvm->fw->valid_tx_ant;
+ rate.stbc = rs_stbc_allow(mvm, sta, lq_sta);
if (is_siso(&rate)) {
num_rates = RS_INITIAL_SISO_NUM_RATES;
@@ -2903,7 +2986,7 @@
if (WARN_ON_ONCE(!sta || !initial_rate))
return;
- rs_build_rates_table(mvm, lq_sta, initial_rate);
+ rs_build_rates_table(mvm, sta, lq_sta, initial_rate);
if (num_of_ant(initial_rate->ant) == 1)
lq_cmd->single_stream_ant_msk = initial_rate->ant;
diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.h b/drivers/net/wireless/iwlwifi/mvm/rs.h
index bcb4b1d..fdd0da5 100644
--- a/drivers/net/wireless/iwlwifi/mvm/rs.h
+++ b/drivers/net/wireless/iwlwifi/mvm/rs.h
@@ -208,6 +208,7 @@
u32 bw;
bool sgi;
bool ldpc;
+ bool stbc;
};
@@ -331,6 +332,7 @@
u64 last_tx;
bool is_vht;
bool ldpc; /* LDPC Rx is supported by the STA */
+ bool stbc; /* Tx STBC is supported by chip and Rx by STA */
enum ieee80211_band band;
/* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */
diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c
index e61514b..0f6a40d 100644
--- a/drivers/net/wireless/iwlwifi/mvm/rx.c
+++ b/drivers/net/wireless/iwlwifi/mvm/rx.c
@@ -96,27 +96,27 @@
* Adds the rxb to a new skb and give it to mac80211
*/
static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
+ struct sk_buff *skb,
struct ieee80211_hdr *hdr, u16 len,
- u32 ampdu_status,
- struct iwl_rx_cmd_buffer *rxb,
- struct ieee80211_rx_status *stats)
+ u32 ampdu_status, u8 crypt_len,
+ struct iwl_rx_cmd_buffer *rxb)
{
- struct sk_buff *skb;
unsigned int hdrlen, fraglen;
- /* Dont use dev_alloc_skb(), we'll have enough headroom once
- * ieee80211_hdr pulled.
- */
- skb = alloc_skb(128, GFP_ATOMIC);
- if (!skb) {
- IWL_ERR(mvm, "alloc_skb failed\n");
- return;
- }
/* If frame is small enough to fit in skb->head, pull it completely.
- * If not, only pull ieee80211_hdr so that splice() or TCP coalesce
- * are more efficient.
+ * If not, only pull ieee80211_hdr (including crypto if present, and
+ * an additional 8 bytes for SNAP/ethertype, see below) so that
+ * splice() or TCP coalesce are more efficient.
+ *
+ * Since, in addition, ieee80211_data_to_8023() always pull in at
+ * least 8 bytes (possibly more for mesh) we can do the same here
+ * to save the cost of doing it later. That still doesn't pull in
+ * the actual IP header since the typical case has a SNAP header.
+ * If the latter changes (there are efforts in the standards group
+ * to do so) we should revisit this and ieee80211_data_to_8023().
*/
- hdrlen = (len <= skb_tailroom(skb)) ? len : sizeof(*hdr);
+ hdrlen = (len <= skb_tailroom(skb)) ? len :
+ sizeof(*hdr) + crypt_len + 8;
memcpy(skb_put(skb, hdrlen), hdr, hdrlen);
fraglen = len - hdrlen;
@@ -129,8 +129,6 @@
fraglen, rxb->truesize);
}
- memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
-
ieee80211_rx(mvm->hw, skb);
}
@@ -185,7 +183,8 @@
static u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm,
struct ieee80211_hdr *hdr,
struct ieee80211_rx_status *stats,
- u32 rx_pkt_status)
+ u32 rx_pkt_status,
+ u8 *crypt_len)
{
if (!ieee80211_has_protected(hdr->frame_control) ||
(rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) ==
@@ -205,12 +204,14 @@
stats->flag |= RX_FLAG_DECRYPTED;
IWL_DEBUG_WEP(mvm, "hw decrypted CCMP successfully\n");
+ *crypt_len = IEEE80211_CCMP_HDR_LEN;
return 0;
case RX_MPDU_RES_STATUS_SEC_TKIP_ENC:
/* Don't drop the frame and decrypt it in SW */
if (!(rx_pkt_status & RX_MPDU_RES_STATUS_TTAK_OK))
return 0;
+ *crypt_len = IEEE80211_TKIP_IV_LEN;
/* fall through if TTAK OK */
case RX_MPDU_RES_STATUS_SEC_WEP_ENC:
@@ -218,6 +219,9 @@
return -1;
stats->flag |= RX_FLAG_DECRYPTED;
+ if ((rx_pkt_status & RX_MPDU_RES_STATUS_SEC_ENC_MSK) ==
+ RX_MPDU_RES_STATUS_SEC_WEP_ENC)
+ *crypt_len = IEEE80211_WEP_IV_LEN;
return 0;
case RX_MPDU_RES_STATUS_SEC_EXT_ENC:
@@ -242,15 +246,17 @@
struct iwl_device_cmd *cmd)
{
struct ieee80211_hdr *hdr;
- struct ieee80211_rx_status rx_status = {};
+ struct ieee80211_rx_status *rx_status;
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_rx_phy_info *phy_info;
struct iwl_rx_mpdu_res_start *rx_res;
struct ieee80211_sta *sta;
+ struct sk_buff *skb;
u32 len;
u32 ampdu_status;
u32 rate_n_flags;
u32 rx_pkt_status;
+ u8 crypt_len = 0;
phy_info = &mvm->last_phy_info;
rx_res = (struct iwl_rx_mpdu_res_start *)pkt->data;
@@ -259,20 +265,32 @@
rx_pkt_status = le32_to_cpup((__le32 *)
(pkt->data + sizeof(*rx_res) + len));
- memset(&rx_status, 0, sizeof(rx_status));
+ /* Dont use dev_alloc_skb(), we'll have enough headroom once
+ * ieee80211_hdr pulled.
+ */
+ skb = alloc_skb(128, GFP_ATOMIC);
+ if (!skb) {
+ IWL_ERR(mvm, "alloc_skb failed\n");
+ return 0;
+ }
+
+ rx_status = IEEE80211_SKB_RXCB(skb);
/*
* drop the packet if it has failed being decrypted by HW
*/
- if (iwl_mvm_set_mac80211_rx_flag(mvm, hdr, &rx_status, rx_pkt_status)) {
+ if (iwl_mvm_set_mac80211_rx_flag(mvm, hdr, rx_status, rx_pkt_status,
+ &crypt_len)) {
IWL_DEBUG_DROP(mvm, "Bad decryption results 0x%08x\n",
rx_pkt_status);
+ kfree_skb(skb);
return 0;
}
if ((unlikely(phy_info->cfg_phy_cnt > 20))) {
IWL_DEBUG_DROP(mvm, "dsp size out of range [0,20]: %d\n",
phy_info->cfg_phy_cnt);
+ kfree_skb(skb);
return 0;
}
@@ -283,31 +301,31 @@
if (!(rx_pkt_status & RX_MPDU_RES_STATUS_CRC_OK) ||
!(rx_pkt_status & RX_MPDU_RES_STATUS_OVERRUN_OK)) {
IWL_DEBUG_RX(mvm, "Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status);
- rx_status.flag |= RX_FLAG_FAILED_FCS_CRC;
+ rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
}
/* This will be used in several places later */
rate_n_flags = le32_to_cpu(phy_info->rate_n_flags);
/* rx_status carries information about the packet to mac80211 */
- rx_status.mactime = le64_to_cpu(phy_info->timestamp);
- rx_status.device_timestamp = le32_to_cpu(phy_info->system_timestamp);
- rx_status.band =
+ rx_status->mactime = le64_to_cpu(phy_info->timestamp);
+ rx_status->device_timestamp = le32_to_cpu(phy_info->system_timestamp);
+ rx_status->band =
(phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_BAND_24)) ?
IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
- rx_status.freq =
+ rx_status->freq =
ieee80211_channel_to_frequency(le16_to_cpu(phy_info->channel),
- rx_status.band);
+ rx_status->band);
/*
* TSF as indicated by the fw is at INA time, but mac80211 expects the
* TSF at the beginning of the MPDU.
*/
- /*rx_status.flag |= RX_FLAG_MACTIME_MPDU;*/
+ /*rx_status->flag |= RX_FLAG_MACTIME_MPDU;*/
- iwl_mvm_get_signal_strength(mvm, phy_info, &rx_status);
+ iwl_mvm_get_signal_strength(mvm, phy_info, rx_status);
- IWL_DEBUG_STATS_LIMIT(mvm, "Rssi %d, TSF %llu\n", rx_status.signal,
- (unsigned long long)rx_status.mactime);
+ IWL_DEBUG_STATS_LIMIT(mvm, "Rssi %d, TSF %llu\n", rx_status->signal,
+ (unsigned long long)rx_status->mactime);
rcu_read_lock();
/*
@@ -326,15 +344,14 @@
if (sta) {
struct iwl_mvm_sta *mvmsta;
mvmsta = iwl_mvm_sta_from_mac80211(sta);
- rs_update_last_rssi(mvm, &mvmsta->lq_sta,
- &rx_status);
+ rs_update_last_rssi(mvm, &mvmsta->lq_sta, rx_status);
}
rcu_read_unlock();
/* set the preamble flag if appropriate */
if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_SHORT_PREAMBLE))
- rx_status.flag |= RX_FLAG_SHORTPRE;
+ rx_status->flag |= RX_FLAG_SHORTPRE;
if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) {
/*
@@ -342,8 +359,8 @@
* together since we get a single PHY response
* from the firmware for all of them
*/
- rx_status.flag |= RX_FLAG_AMPDU_DETAILS;
- rx_status.ampdu_reference = mvm->ampdu_ref;
+ rx_status->flag |= RX_FLAG_AMPDU_DETAILS;
+ rx_status->ampdu_reference = mvm->ampdu_ref;
}
/* Set up the HT phy flags */
@@ -351,50 +368,50 @@
case RATE_MCS_CHAN_WIDTH_20:
break;
case RATE_MCS_CHAN_WIDTH_40:
- rx_status.flag |= RX_FLAG_40MHZ;
+ rx_status->flag |= RX_FLAG_40MHZ;
break;
case RATE_MCS_CHAN_WIDTH_80:
- rx_status.vht_flag |= RX_VHT_FLAG_80MHZ;
+ rx_status->vht_flag |= RX_VHT_FLAG_80MHZ;
break;
case RATE_MCS_CHAN_WIDTH_160:
- rx_status.vht_flag |= RX_VHT_FLAG_160MHZ;
+ rx_status->vht_flag |= RX_VHT_FLAG_160MHZ;
break;
}
if (rate_n_flags & RATE_MCS_SGI_MSK)
- rx_status.flag |= RX_FLAG_SHORT_GI;
+ rx_status->flag |= RX_FLAG_SHORT_GI;
if (rate_n_flags & RATE_HT_MCS_GF_MSK)
- rx_status.flag |= RX_FLAG_HT_GF;
+ rx_status->flag |= RX_FLAG_HT_GF;
if (rate_n_flags & RATE_MCS_LDPC_MSK)
- rx_status.flag |= RX_FLAG_LDPC;
+ rx_status->flag |= RX_FLAG_LDPC;
if (rate_n_flags & RATE_MCS_HT_MSK) {
u8 stbc = (rate_n_flags & RATE_MCS_HT_STBC_MSK) >>
RATE_MCS_STBC_POS;
- rx_status.flag |= RX_FLAG_HT;
- rx_status.rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK;
- rx_status.flag |= stbc << RX_FLAG_STBC_SHIFT;
+ rx_status->flag |= RX_FLAG_HT;
+ rx_status->rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK;
+ rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT;
} else if (rate_n_flags & RATE_MCS_VHT_MSK) {
u8 stbc = (rate_n_flags & RATE_MCS_VHT_STBC_MSK) >>
RATE_MCS_STBC_POS;
- rx_status.vht_nss =
+ rx_status->vht_nss =
((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >>
RATE_VHT_MCS_NSS_POS) + 1;
- rx_status.rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK;
- rx_status.flag |= RX_FLAG_VHT;
- rx_status.flag |= stbc << RX_FLAG_STBC_SHIFT;
+ rx_status->rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK;
+ rx_status->flag |= RX_FLAG_VHT;
+ rx_status->flag |= stbc << RX_FLAG_STBC_SHIFT;
if (rate_n_flags & RATE_MCS_BF_MSK)
- rx_status.vht_flag |= RX_VHT_FLAG_BF;
+ rx_status->vht_flag |= RX_VHT_FLAG_BF;
} else {
- rx_status.rate_idx =
+ rx_status->rate_idx =
iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags,
- rx_status.band);
+ rx_status->band);
}
#ifdef CPTCFG_IWLWIFI_DEBUGFS
iwl_mvm_update_frame_stats(mvm, &mvm->drv_rx_stats, rate_n_flags,
- rx_status.flag & RX_FLAG_AMPDU_DETAILS);
+ rx_status->flag & RX_FLAG_AMPDU_DETAILS);
#endif
- iwl_mvm_pass_packet_to_mac80211(mvm, hdr, len, ampdu_status,
- rxb, &rx_status);
+ iwl_mvm_pass_packet_to_mac80211(mvm, skb, hdr, len, ampdu_status,
+ crypt_len, rxb);
return 0;
}
@@ -500,29 +517,8 @@
.mvm = mvm,
};
- /*
- * set temperature debug enabled - ignore FW temperature updates
- * and use the user set temperature.
- */
- if (mvm->temperature_test) {
- if (mvm->temperature < le32_to_cpu(common->temperature))
- IWL_DEBUG_TEMP(mvm,
- "Ignoring FW temperature update that is greater than the debug set temperature (debug temp = %d, fw temp = %d)\n",
- mvm->temperature,
- le32_to_cpu(common->temperature));
- /*
- * skip iwl_mvm_tt_handler since we are in
- * temperature debug mode and we are ignoring
- * the new temperature value
- */
- goto update;
- }
+ iwl_mvm_tt_temp_changed(mvm, le32_to_cpu(common->temperature));
- if (mvm->temperature != le32_to_cpu(common->temperature)) {
- mvm->temperature = le32_to_cpu(common->temperature);
- iwl_mvm_tt_handler(mvm);
- }
-update:
iwl_mvm_update_rx_statistics(mvm, stats);
ieee80211_iterate_active_interfaces(mvm->hw,
diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c
index cb85e63..e5294d0 100644
--- a/drivers/net/wireless/iwlwifi/mvm/scan.c
+++ b/drivers/net/wireless/iwlwifi/mvm/scan.c
@@ -83,15 +83,29 @@
} dwell[IEEE80211_NUM_BANDS];
};
+enum iwl_umac_scan_uid_type {
+ IWL_UMAC_SCAN_UID_REG_SCAN = BIT(0),
+ IWL_UMAC_SCAN_UID_SCHED_SCAN = BIT(1),
+ IWL_UMAC_SCAN_UID_ALL = IWL_UMAC_SCAN_UID_REG_SCAN |
+ IWL_UMAC_SCAN_UID_SCHED_SCAN,
+};
+
+static int iwl_umac_scan_stop(struct iwl_mvm *mvm,
+ enum iwl_umac_scan_uid_type type, bool notify);
+
+static u8 iwl_mvm_scan_rx_ant(struct iwl_mvm *mvm)
+{
+ if (mvm->scan_rx_ant != ANT_NONE)
+ return mvm->scan_rx_ant;
+ return mvm->fw->valid_rx_ant;
+}
+
static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm)
{
u16 rx_chain;
u8 rx_ant;
- if (mvm->scan_rx_ant != ANT_NONE)
- rx_ant = mvm->scan_rx_ant;
- else
- rx_ant = mvm->fw->valid_rx_ant;
+ rx_ant = iwl_mvm_scan_rx_ant(mvm);
rx_chain = rx_ant << PHY_RX_CHAIN_VALID_POS;
rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_MIMO_SEL_POS;
rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_SEL_POS;
@@ -270,7 +284,8 @@
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
bool *global_bound = data;
- if (mvmvif->phy_ctxt && mvmvif->phy_ctxt->id < MAX_PHYS)
+ if (vif->type != NL80211_IFTYPE_P2P_DEVICE && mvmvif->phy_ctxt &&
+ mvmvif->phy_ctxt->id < MAX_PHYS)
*global_bound = true;
}
@@ -365,6 +380,10 @@
!is_sched_scan)
max_probe_len -= 32;
+ /* DS parameter set element is added on 2.4GHZ band if required */
+ if (iwl_mvm_rrm_scan_needed(mvm))
+ max_probe_len -= 3;
+
return max_probe_len;
}
@@ -459,7 +478,8 @@
basic_ssid ? 1 : 0);
cmd->tx_cmd.tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL |
- TX_CMD_FLG_BT_DIS);
+ 3 << TX_CMD_FLG_BT_PRIO_POS);
+
cmd->tx_cmd.sta_id = mvm->aux_sta.sta_id;
cmd->tx_cmd.life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE);
cmd->tx_cmd.rate_n_flags =
@@ -535,23 +555,17 @@
struct iwl_device_cmd *cmd)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
- u8 client_bitmap = 0;
- if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)) {
+ if (!(mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) &&
+ !(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)) {
struct iwl_sched_scan_results *notif = (void *)pkt->data;
- client_bitmap = notif->client_bitmap;
+ if (!(notif->client_bitmap & SCAN_CLIENT_SCHED_SCAN))
+ return 0;
}
- if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN ||
- client_bitmap & SCAN_CLIENT_SCHED_SCAN) {
- if (mvm->scan_status == IWL_MVM_SCAN_SCHED) {
- IWL_DEBUG_SCAN(mvm, "Scheduled scan results\n");
- ieee80211_sched_scan_results(mvm->hw);
- } else {
- IWL_DEBUG_SCAN(mvm, "Scan results\n");
- }
- }
+ IWL_DEBUG_SCAN(mvm, "Scheduled scan results\n");
+ ieee80211_sched_scan_results(mvm->hw);
return 0;
}
@@ -601,16 +615,6 @@
SCAN_COMPLETE_NOTIFICATION };
int ret;
- if (mvm->scan_status == IWL_MVM_SCAN_NONE)
- return 0;
-
- if (iwl_mvm_is_radio_killed(mvm)) {
- ieee80211_scan_completed(mvm->hw, true);
- iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
- mvm->scan_status = IWL_MVM_SCAN_NONE;
- return 0;
- }
-
iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_abort,
scan_abort_notif,
ARRAY_SIZE(scan_abort_notif),
@@ -671,6 +675,7 @@
mvm->scan_status = IWL_MVM_SCAN_NONE;
ieee80211_scan_completed(mvm->hw,
status == IWL_SCAN_OFFLOAD_ABORTED);
+ iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
}
mvm->last_ebs_successful = !ebs_status;
@@ -972,6 +977,20 @@
return ret;
}
+static bool iwl_mvm_scan_pass_all(struct iwl_mvm *mvm,
+ struct cfg80211_sched_scan_request *req)
+{
+ if (req->n_match_sets && req->match_sets[0].ssid.ssid_len) {
+ IWL_DEBUG_SCAN(mvm,
+ "Sending scheduled scan with filtering, n_match_sets %d\n",
+ req->n_match_sets);
+ return false;
+ }
+
+ IWL_DEBUG_SCAN(mvm, "Sending Scheduled scan without filtering\n");
+ return true;
+}
+
int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
struct cfg80211_sched_scan_request *req)
{
@@ -987,15 +1006,8 @@
.schedule_line[1].full_scan_mul = IWL_FULL_SCAN_MULTIPLIER,
};
- if (req->n_match_sets && req->match_sets[0].ssid.ssid_len) {
- IWL_DEBUG_SCAN(mvm,
- "Sending scheduled scan with filtering, filter len %d\n",
- req->n_match_sets);
- } else {
- IWL_DEBUG_SCAN(mvm,
- "Sending Scheduled scan without filtering\n");
+ if (iwl_mvm_scan_pass_all(mvm, req))
scan_req.flags |= cpu_to_le16(IWL_SCAN_OFFLOAD_FLAG_PASS_ALL);
- }
if (mvm->last_ebs_successful &&
mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT)
@@ -1006,6 +1018,38 @@
sizeof(scan_req), &scan_req);
}
+int iwl_mvm_scan_offload_start(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct cfg80211_sched_scan_request *req,
+ struct ieee80211_scan_ies *ies)
+{
+ int ret;
+
+ if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) {
+ ret = iwl_mvm_config_sched_scan_profiles(mvm, req);
+ if (ret)
+ return ret;
+ ret = iwl_mvm_sched_scan_umac(mvm, vif, req, ies);
+ } else if ((mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)) {
+ mvm->scan_status = IWL_MVM_SCAN_SCHED;
+ ret = iwl_mvm_config_sched_scan_profiles(mvm, req);
+ if (ret)
+ return ret;
+ ret = iwl_mvm_unified_sched_scan_lmac(mvm, vif, req, ies);
+ } else {
+ mvm->scan_status = IWL_MVM_SCAN_SCHED;
+ ret = iwl_mvm_config_sched_scan(mvm, vif, req, ies);
+ if (ret)
+ return ret;
+ ret = iwl_mvm_config_sched_scan_profiles(mvm, req);
+ if (ret)
+ return ret;
+ ret = iwl_mvm_sched_scan_start(mvm, req);
+ }
+
+ return ret;
+}
+
static int iwl_mvm_send_scan_offload_abort(struct iwl_mvm *mvm)
{
int ret;
@@ -1050,6 +1094,10 @@
lockdep_assert_held(&mvm->mutex);
+ if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)
+ return iwl_umac_scan_stop(mvm, IWL_UMAC_SCAN_UID_SCHED_SCAN,
+ notify);
+
if (mvm->scan_status != IWL_MVM_SCAN_SCHED &&
(!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) ||
mvm->scan_status != IWL_MVM_SCAN_OS)) {
@@ -1080,8 +1128,12 @@
/*
* Clear the scan status so the next scan requests will succeed. This
* also ensures the Rx handler doesn't do anything, as the scan was
- * stopped from above.
+ * stopped from above. Since the rx handler won't do anything now,
+ * we have to release the scan reference here.
*/
+ if (mvm->scan_status == IWL_MVM_SCAN_OS)
+ iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
+
mvm->scan_status = IWL_MVM_SCAN_NONE;
if (notify) {
@@ -1133,20 +1185,64 @@
}
}
+static u8 *iwl_mvm_copy_and_insert_ds_elem(struct iwl_mvm *mvm, const u8 *ies,
+ size_t len, u8 *const pos)
+{
+ static const u8 before_ds_params[] = {
+ WLAN_EID_SSID,
+ WLAN_EID_SUPP_RATES,
+ WLAN_EID_REQUEST,
+ WLAN_EID_EXT_SUPP_RATES,
+ };
+ size_t offs;
+ u8 *newpos = pos;
+
+ if (!iwl_mvm_rrm_scan_needed(mvm)) {
+ memcpy(newpos, ies, len);
+ return newpos + len;
+ }
+
+ offs = ieee80211_ie_split(ies, len,
+ before_ds_params,
+ ARRAY_SIZE(before_ds_params),
+ 0);
+
+ memcpy(newpos, ies, offs);
+ newpos += offs;
+
+ /* Add a placeholder for DS Parameter Set element */
+ *newpos++ = WLAN_EID_DS_PARAMS;
+ *newpos++ = 1;
+ *newpos++ = 0;
+
+ memcpy(newpos, ies + offs, len - offs);
+ newpos += len - offs;
+
+ return newpos;
+}
+
static void
iwl_mvm_build_unified_scan_probe(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
struct ieee80211_scan_ies *ies,
- struct iwl_scan_req_unified_lmac *cmd)
+ struct iwl_scan_probe_req *preq,
+ const u8 *mac_addr, const u8 *mac_addr_mask)
{
- struct iwl_scan_probe_req *preq = (void *)(cmd->data +
- sizeof(struct iwl_scan_channel_cfg_lmac) *
- mvm->fw->ucode_capa.n_scan_channels);
struct ieee80211_mgmt *frame = (struct ieee80211_mgmt *)preq->buf;
- u8 *pos;
+ u8 *pos, *newpos;
+
+ /*
+ * Unfortunately, right now the offload scan doesn't support randomising
+ * within the firmware, so until the firmware API is ready we implement
+ * it in the driver. This means that the scan iterations won't really be
+ * random, only when it's restarted, but at least that helps a bit.
+ */
+ if (mac_addr)
+ get_random_mask_addr(frame->sa, mac_addr, mac_addr_mask);
+ else
+ memcpy(frame->sa, vif->addr, ETH_ALEN);
frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ);
eth_broadcast_addr(frame->da);
- memcpy(frame->sa, vif->addr, ETH_ALEN);
eth_broadcast_addr(frame->bssid);
frame->seq_ctrl = 0;
@@ -1157,11 +1253,14 @@
preq->mac_header.offset = 0;
preq->mac_header.len = cpu_to_le16(24 + 2);
- memcpy(pos, ies->ies[IEEE80211_BAND_2GHZ],
- ies->len[IEEE80211_BAND_2GHZ]);
+ /* Insert ds parameter set element on 2.4 GHz band */
+ newpos = iwl_mvm_copy_and_insert_ds_elem(mvm,
+ ies->ies[IEEE80211_BAND_2GHZ],
+ ies->len[IEEE80211_BAND_2GHZ],
+ pos);
preq->band_data[0].offset = cpu_to_le16(pos - preq->buf);
- preq->band_data[0].len = cpu_to_le16(ies->len[IEEE80211_BAND_2GHZ]);
- pos += ies->len[IEEE80211_BAND_2GHZ];
+ preq->band_data[0].len = cpu_to_le16(newpos - pos);
+ pos = newpos;
memcpy(pos, ies->ies[IEEE80211_BAND_5GHZ],
ies->len[IEEE80211_BAND_5GHZ]);
@@ -1222,9 +1321,10 @@
.dataflags = { IWL_HCMD_DFL_NOCOPY, },
};
struct iwl_scan_req_unified_lmac *cmd = mvm->scan_cmd;
+ struct iwl_scan_probe_req *preq;
struct iwl_mvm_scan_params params = {};
u32 flags;
- int ssid_bitmap = 0;
+ u32 ssid_bitmap = 0;
int ret, i;
lockdep_assert_held(&mvm->mutex);
@@ -1283,7 +1383,13 @@
req->req.n_channels, ssid_bitmap,
cmd);
- iwl_mvm_build_unified_scan_probe(mvm, vif, &req->ies, cmd);
+ preq = (void *)(cmd->data + sizeof(struct iwl_scan_channel_cfg_lmac) *
+ mvm->fw->ucode_capa.n_scan_channels);
+
+ iwl_mvm_build_unified_scan_probe(mvm, vif, &req->ies, preq,
+ req->req.flags & NL80211_SCAN_FLAG_RANDOM_ADDR ?
+ req->req.mac_addr : NULL,
+ req->req.mac_addr_mask);
ret = iwl_mvm_send_cmd(mvm, &hcmd);
if (!ret) {
@@ -1316,6 +1422,7 @@
.dataflags = { IWL_HCMD_DFL_NOCOPY, },
};
struct iwl_scan_req_unified_lmac *cmd = mvm->scan_cmd;
+ struct iwl_scan_probe_req *preq;
struct iwl_mvm_scan_params params = {};
int ret;
u32 flags = 0, ssid_bitmap = 0;
@@ -1339,15 +1446,8 @@
cmd->n_channels = (u8)req->n_channels;
- if (req->n_match_sets && req->match_sets[0].ssid.ssid_len) {
- IWL_DEBUG_SCAN(mvm,
- "Sending scheduled scan with filtering, n_match_sets %d\n",
- req->n_match_sets);
- } else {
- IWL_DEBUG_SCAN(mvm,
- "Sending Scheduled scan without filtering\n");
+ if (iwl_mvm_scan_pass_all(mvm, req))
flags |= IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL;
- }
if (req->n_ssids == 1 && req->ssids[0].ssid_len != 0)
flags |= IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION;
@@ -1377,7 +1477,13 @@
iwl_mvm_lmac_scan_cfg_channels(mvm, req->channels, req->n_channels,
ssid_bitmap, cmd);
- iwl_mvm_build_unified_scan_probe(mvm, vif, ies, cmd);
+ preq = (void *)(cmd->data + sizeof(struct iwl_scan_channel_cfg_lmac) *
+ mvm->fw->ucode_capa.n_scan_channels);
+
+ iwl_mvm_build_unified_scan_probe(mvm, vif, ies, preq,
+ req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR ?
+ req->mac_addr : NULL,
+ req->mac_addr_mask);
ret = iwl_mvm_send_cmd(mvm, &hcmd);
if (!ret) {
@@ -1399,7 +1505,594 @@
int iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
{
+ if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)
+ return iwl_umac_scan_stop(mvm, IWL_UMAC_SCAN_UID_REG_SCAN,
+ true);
+
+ if (mvm->scan_status == IWL_MVM_SCAN_NONE)
+ return 0;
+
+ if (iwl_mvm_is_radio_killed(mvm)) {
+ ieee80211_scan_completed(mvm->hw, true);
+ iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
+ mvm->scan_status = IWL_MVM_SCAN_NONE;
+ return 0;
+ }
+
if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
return iwl_mvm_scan_offload_stop(mvm, true);
return iwl_mvm_cancel_regular_scan(mvm);
}
+
+/* UMAC scan API */
+
+struct iwl_umac_scan_done {
+ struct iwl_mvm *mvm;
+ enum iwl_umac_scan_uid_type type;
+};
+
+static int rate_to_scan_rate_flag(unsigned int rate)
+{
+ static const int rate_to_scan_rate[IWL_RATE_COUNT] = {
+ [IWL_RATE_1M_INDEX] = SCAN_CONFIG_RATE_1M,
+ [IWL_RATE_2M_INDEX] = SCAN_CONFIG_RATE_2M,
+ [IWL_RATE_5M_INDEX] = SCAN_CONFIG_RATE_5M,
+ [IWL_RATE_11M_INDEX] = SCAN_CONFIG_RATE_11M,
+ [IWL_RATE_6M_INDEX] = SCAN_CONFIG_RATE_6M,
+ [IWL_RATE_9M_INDEX] = SCAN_CONFIG_RATE_9M,
+ [IWL_RATE_12M_INDEX] = SCAN_CONFIG_RATE_12M,
+ [IWL_RATE_18M_INDEX] = SCAN_CONFIG_RATE_18M,
+ [IWL_RATE_24M_INDEX] = SCAN_CONFIG_RATE_24M,
+ [IWL_RATE_36M_INDEX] = SCAN_CONFIG_RATE_36M,
+ [IWL_RATE_48M_INDEX] = SCAN_CONFIG_RATE_48M,
+ [IWL_RATE_54M_INDEX] = SCAN_CONFIG_RATE_54M,
+ };
+
+ return rate_to_scan_rate[rate];
+}
+
+static __le32 iwl_mvm_scan_config_rates(struct iwl_mvm *mvm)
+{
+ struct ieee80211_supported_band *band;
+ unsigned int rates = 0;
+ int i;
+
+ band = &mvm->nvm_data->bands[IEEE80211_BAND_2GHZ];
+ for (i = 0; i < band->n_bitrates; i++)
+ rates |= rate_to_scan_rate_flag(band->bitrates[i].hw_value);
+ band = &mvm->nvm_data->bands[IEEE80211_BAND_5GHZ];
+ for (i = 0; i < band->n_bitrates; i++)
+ rates |= rate_to_scan_rate_flag(band->bitrates[i].hw_value);
+
+ /* Set both basic rates and supported rates */
+ rates |= SCAN_CONFIG_SUPPORTED_RATE(rates);
+
+ return cpu_to_le32(rates);
+}
+
+int iwl_mvm_config_scan(struct iwl_mvm *mvm)
+{
+
+ struct iwl_scan_config *scan_config;
+ struct ieee80211_supported_band *band;
+ int num_channels =
+ mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels +
+ mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels;
+ int ret, i, j = 0, cmd_size, data_size;
+ struct iwl_host_cmd cmd = {
+ .id = SCAN_CFG_CMD,
+ };
+
+ if (WARN_ON(num_channels > mvm->fw->ucode_capa.n_scan_channels))
+ return -ENOBUFS;
+
+ cmd_size = sizeof(*scan_config) + mvm->fw->ucode_capa.n_scan_channels;
+
+ scan_config = kzalloc(cmd_size, GFP_KERNEL);
+ if (!scan_config)
+ return -ENOMEM;
+
+ data_size = cmd_size - sizeof(struct iwl_mvm_umac_cmd_hdr);
+ scan_config->hdr.size = cpu_to_le16(data_size);
+ scan_config->flags = cpu_to_le32(SCAN_CONFIG_FLAG_ACTIVATE |
+ SCAN_CONFIG_FLAG_ALLOW_CHUB_REQS |
+ SCAN_CONFIG_FLAG_SET_TX_CHAINS |
+ SCAN_CONFIG_FLAG_SET_RX_CHAINS |
+ SCAN_CONFIG_FLAG_SET_ALL_TIMES |
+ SCAN_CONFIG_FLAG_SET_LEGACY_RATES |
+ SCAN_CONFIG_FLAG_SET_MAC_ADDR |
+ SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS|
+ SCAN_CONFIG_N_CHANNELS(num_channels));
+ scan_config->tx_chains = cpu_to_le32(mvm->fw->valid_tx_ant);
+ scan_config->rx_chains = cpu_to_le32(iwl_mvm_scan_rx_ant(mvm));
+ scan_config->legacy_rates = iwl_mvm_scan_config_rates(mvm);
+ scan_config->out_of_channel_time = cpu_to_le32(170);
+ scan_config->suspend_time = cpu_to_le32(30);
+ scan_config->dwell_active = 20;
+ scan_config->dwell_passive = 110;
+ scan_config->dwell_fragmented = 20;
+
+ memcpy(&scan_config->mac_addr, &mvm->addresses[0].addr, ETH_ALEN);
+
+ scan_config->bcast_sta_id = mvm->aux_sta.sta_id;
+ scan_config->channel_flags = IWL_CHANNEL_FLAG_EBS |
+ IWL_CHANNEL_FLAG_ACCURATE_EBS |
+ IWL_CHANNEL_FLAG_EBS_ADD |
+ IWL_CHANNEL_FLAG_PRE_SCAN_PASSIVE2ACTIVE;
+
+ band = &mvm->nvm_data->bands[IEEE80211_BAND_2GHZ];
+ for (i = 0; i < band->n_channels; i++, j++)
+ scan_config->channel_array[j] = band->channels[i].center_freq;
+ band = &mvm->nvm_data->bands[IEEE80211_BAND_5GHZ];
+ for (i = 0; i < band->n_channels; i++, j++)
+ scan_config->channel_array[j] = band->channels[i].center_freq;
+
+ cmd.data[0] = scan_config;
+ cmd.len[0] = cmd_size;
+ cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY;
+
+ IWL_DEBUG_SCAN(mvm, "Sending UMAC scan config\n");
+
+ ret = iwl_mvm_send_cmd(mvm, &cmd);
+
+ kfree(scan_config);
+ return ret;
+}
+
+static int iwl_mvm_find_scan_uid(struct iwl_mvm *mvm, u32 uid)
+{
+ int i;
+
+ for (i = 0; i < IWL_MVM_MAX_SIMULTANEOUS_SCANS; i++)
+ if (mvm->scan_uid[i] == uid)
+ return i;
+
+ return i;
+}
+
+static int iwl_mvm_find_free_scan_uid(struct iwl_mvm *mvm)
+{
+ return iwl_mvm_find_scan_uid(mvm, 0);
+}
+
+static bool iwl_mvm_find_scan_type(struct iwl_mvm *mvm,
+ enum iwl_umac_scan_uid_type type)
+{
+ int i;
+
+ for (i = 0; i < IWL_MVM_MAX_SIMULTANEOUS_SCANS; i++)
+ if (mvm->scan_uid[i] & type)
+ return true;
+
+ return false;
+}
+
+static u32 iwl_generate_scan_uid(struct iwl_mvm *mvm,
+ enum iwl_umac_scan_uid_type type)
+{
+ u32 uid;
+
+ /* make sure exactly one bit is on in scan type */
+ WARN_ON(hweight8(type) != 1);
+
+ /*
+ * Make sure scan uids are unique. If one scan lasts long time while
+ * others are completing frequently, the seq number will wrap up and
+ * we may have more than one scan with the same uid.
+ */
+ do {
+ uid = type | (mvm->scan_seq_num <<
+ IWL_UMAC_SCAN_UID_SEQ_OFFSET);
+ mvm->scan_seq_num++;
+ } while (iwl_mvm_find_scan_uid(mvm, uid) <
+ IWL_MVM_MAX_SIMULTANEOUS_SCANS);
+
+ IWL_DEBUG_SCAN(mvm, "Generated scan UID %u\n", uid);
+
+ return uid;
+}
+
+static void
+iwl_mvm_build_generic_umac_scan_cmd(struct iwl_mvm *mvm,
+ struct iwl_scan_req_umac *cmd,
+ struct iwl_mvm_scan_params *params)
+{
+ memset(cmd, 0, ksize(cmd));
+ cmd->hdr.size = cpu_to_le16(iwl_mvm_scan_size(mvm) -
+ sizeof(struct iwl_mvm_umac_cmd_hdr));
+ cmd->active_dwell = params->dwell[IEEE80211_BAND_2GHZ].active;
+ cmd->passive_dwell = params->dwell[IEEE80211_BAND_2GHZ].passive;
+ if (params->passive_fragmented)
+ cmd->fragmented_dwell =
+ params->dwell[IEEE80211_BAND_2GHZ].passive;
+ cmd->max_out_time = cpu_to_le32(params->max_out_time);
+ cmd->suspend_time = cpu_to_le32(params->suspend_time);
+ cmd->scan_priority = cpu_to_le32(IWL_SCAN_PRIORITY_HIGH);
+}
+
+static void
+iwl_mvm_umac_scan_cfg_channels(struct iwl_mvm *mvm,
+ struct ieee80211_channel **channels,
+ int n_channels, u32 ssid_bitmap,
+ struct iwl_scan_req_umac *cmd)
+{
+ struct iwl_scan_channel_cfg_umac *channel_cfg = (void *)&cmd->data;
+ int i;
+
+ for (i = 0; i < n_channels; i++) {
+ channel_cfg[i].flags = cpu_to_le32(ssid_bitmap);
+ channel_cfg[i].channel_num = channels[i]->hw_value;
+ channel_cfg[i].iter_count = 1;
+ channel_cfg[i].iter_interval = 0;
+ }
+}
+
+static u32 iwl_mvm_scan_umac_common_flags(struct iwl_mvm *mvm, int n_ssids,
+ struct cfg80211_ssid *ssids,
+ int fragmented)
+{
+ int flags = 0;
+
+ if (n_ssids == 0)
+ flags = IWL_UMAC_SCAN_GEN_FLAGS_PASSIVE;
+
+ if (n_ssids == 1 && ssids[0].ssid_len != 0)
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_PRE_CONNECT;
+
+ if (fragmented)
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_FRAGMENTED;
+
+ if (iwl_mvm_rrm_scan_needed(mvm))
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_RRM_ENABLED;
+
+ return flags;
+}
+
+int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ struct ieee80211_scan_request *req)
+{
+ struct iwl_host_cmd hcmd = {
+ .id = SCAN_REQ_UMAC,
+ .len = { iwl_mvm_scan_size(mvm), },
+ .data = { mvm->scan_cmd, },
+ .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+ };
+ struct iwl_scan_req_umac *cmd = mvm->scan_cmd;
+ struct iwl_scan_req_umac_tail *sec_part = (void *)&cmd->data +
+ sizeof(struct iwl_scan_channel_cfg_umac) *
+ mvm->fw->ucode_capa.n_scan_channels;
+ struct iwl_mvm_scan_params params = {};
+ u32 uid, flags;
+ u32 ssid_bitmap = 0;
+ int ret, i, uid_idx;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ uid_idx = iwl_mvm_find_free_scan_uid(mvm);
+ if (uid_idx >= IWL_MVM_MAX_SIMULTANEOUS_SCANS)
+ return -EBUSY;
+
+ /* we should have failed registration if scan_cmd was NULL */
+ if (WARN_ON(mvm->scan_cmd == NULL))
+ return -ENOMEM;
+
+ if (WARN_ON(req->req.n_ssids > PROBE_OPTION_MAX ||
+ req->ies.common_ie_len +
+ req->ies.len[NL80211_BAND_2GHZ] +
+ req->ies.len[NL80211_BAND_5GHZ] + 24 + 2 >
+ SCAN_OFFLOAD_PROBE_REQ_SIZE || req->req.n_channels >
+ mvm->fw->ucode_capa.n_scan_channels))
+ return -ENOBUFS;
+
+ iwl_mvm_scan_calc_params(mvm, vif, req->req.n_ssids, req->req.flags,
+ ¶ms);
+
+ iwl_mvm_build_generic_umac_scan_cmd(mvm, cmd, ¶ms);
+
+ uid = iwl_generate_scan_uid(mvm, IWL_UMAC_SCAN_UID_REG_SCAN);
+ mvm->scan_uid[uid_idx] = uid;
+ cmd->uid = cpu_to_le32(uid);
+
+ cmd->ooc_priority = cpu_to_le32(IWL_SCAN_PRIORITY_HIGH);
+
+ flags = iwl_mvm_scan_umac_common_flags(mvm, req->req.n_ssids,
+ req->req.ssids,
+ params.passive_fragmented);
+
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_PASS_ALL;
+
+ cmd->general_flags = cpu_to_le32(flags);
+ cmd->n_channels = req->req.n_channels;
+
+ for (i = 0; i < req->req.n_ssids; i++)
+ ssid_bitmap |= BIT(i);
+
+ iwl_mvm_umac_scan_cfg_channels(mvm, req->req.channels,
+ req->req.n_channels, ssid_bitmap, cmd);
+
+ sec_part->schedule[0].iter_count = 1;
+ sec_part->delay = 0;
+
+ iwl_mvm_build_unified_scan_probe(mvm, vif, &req->ies, &sec_part->preq,
+ req->req.flags & NL80211_SCAN_FLAG_RANDOM_ADDR ?
+ req->req.mac_addr : NULL,
+ req->req.mac_addr_mask);
+
+ iwl_mvm_scan_fill_ssids(sec_part->direct_scan, req->req.ssids,
+ req->req.n_ssids, 0);
+
+ ret = iwl_mvm_send_cmd(mvm, &hcmd);
+ if (!ret) {
+ IWL_DEBUG_SCAN(mvm,
+ "Scan request was sent successfully\n");
+ } else {
+ /*
+ * If the scan failed, it usually means that the FW was unable
+ * to allocate the time events. Warn on it, but maybe we
+ * should try to send the command again with different params.
+ */
+ IWL_ERR(mvm, "Scan failed! ret %d\n", ret);
+ }
+ return ret;
+}
+
+int iwl_mvm_sched_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ struct cfg80211_sched_scan_request *req,
+ struct ieee80211_scan_ies *ies)
+{
+
+ struct iwl_host_cmd hcmd = {
+ .id = SCAN_REQ_UMAC,
+ .len = { iwl_mvm_scan_size(mvm), },
+ .data = { mvm->scan_cmd, },
+ .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+ };
+ struct iwl_scan_req_umac *cmd = mvm->scan_cmd;
+ struct iwl_scan_req_umac_tail *sec_part = (void *)&cmd->data +
+ sizeof(struct iwl_scan_channel_cfg_umac) *
+ mvm->fw->ucode_capa.n_scan_channels;
+ struct iwl_mvm_scan_params params = {};
+ u32 uid, flags;
+ u32 ssid_bitmap = 0;
+ int ret, uid_idx;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ uid_idx = iwl_mvm_find_free_scan_uid(mvm);
+ if (uid_idx >= IWL_MVM_MAX_SIMULTANEOUS_SCANS)
+ return -EBUSY;
+
+ /* we should have failed registration if scan_cmd was NULL */
+ if (WARN_ON(mvm->scan_cmd == NULL))
+ return -ENOMEM;
+
+ if (WARN_ON(req->n_ssids > PROBE_OPTION_MAX ||
+ ies->common_ie_len + ies->len[NL80211_BAND_2GHZ] +
+ ies->len[NL80211_BAND_5GHZ] + 24 + 2 >
+ SCAN_OFFLOAD_PROBE_REQ_SIZE || req->n_channels >
+ mvm->fw->ucode_capa.n_scan_channels))
+ return -ENOBUFS;
+
+ iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, req->flags,
+ ¶ms);
+
+ iwl_mvm_build_generic_umac_scan_cmd(mvm, cmd, ¶ms);
+
+ cmd->flags = cpu_to_le32(IWL_UMAC_SCAN_FLAG_PREEMPTIVE);
+
+ uid = iwl_generate_scan_uid(mvm, IWL_UMAC_SCAN_UID_SCHED_SCAN);
+ mvm->scan_uid[uid_idx] = uid;
+ cmd->uid = cpu_to_le32(uid);
+
+ cmd->ooc_priority = cpu_to_le32(IWL_SCAN_PRIORITY_LOW);
+
+ flags = iwl_mvm_scan_umac_common_flags(mvm, req->n_ssids, req->ssids,
+ params.passive_fragmented);
+
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_PERIODIC;
+
+ if (iwl_mvm_scan_pass_all(mvm, req))
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_PASS_ALL;
+ else
+ flags |= IWL_UMAC_SCAN_GEN_FLAGS_MATCH;
+
+ cmd->general_flags = cpu_to_le32(flags);
+
+ if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT &&
+ mvm->last_ebs_successful)
+ cmd->channel_flags = IWL_SCAN_CHANNEL_FLAG_EBS |
+ IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
+ IWL_SCAN_CHANNEL_FLAG_CACHE_ADD;
+
+ cmd->n_channels = req->n_channels;
+
+ iwl_scan_offload_build_ssid(req, sec_part->direct_scan, &ssid_bitmap,
+ false);
+
+ /* This API uses bits 0-19 instead of 1-20. */
+ ssid_bitmap = ssid_bitmap >> 1;
+
+ iwl_mvm_umac_scan_cfg_channels(mvm, req->channels, req->n_channels,
+ ssid_bitmap, cmd);
+
+ sec_part->schedule[0].interval =
+ cpu_to_le16(req->interval / MSEC_PER_SEC);
+ sec_part->schedule[0].iter_count = 0xff;
+
+ sec_part->delay = 0;
+
+ iwl_mvm_build_unified_scan_probe(mvm, vif, ies, &sec_part->preq,
+ req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR ?
+ req->mac_addr : NULL,
+ req->mac_addr_mask);
+
+ ret = iwl_mvm_send_cmd(mvm, &hcmd);
+ if (!ret) {
+ IWL_DEBUG_SCAN(mvm,
+ "Sched scan request was sent successfully\n");
+ } else {
+ /*
+ * If the scan failed, it usually means that the FW was unable
+ * to allocate the time events. Warn on it, but maybe we
+ * should try to send the command again with different params.
+ */
+ IWL_ERR(mvm, "Sched scan failed! ret %d\n", ret);
+ }
+ return ret;
+}
+
+int iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb,
+ struct iwl_device_cmd *cmd)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_umac_scan_complete *notif = (void *)pkt->data;
+ u32 uid = __le32_to_cpu(notif->uid);
+ bool sched = !!(uid & IWL_UMAC_SCAN_UID_SCHED_SCAN);
+ int uid_idx = iwl_mvm_find_scan_uid(mvm, uid);
+
+ /*
+ * Scan uid may be set to zero in case of scan abort request from above.
+ */
+ if (uid_idx >= IWL_MVM_MAX_SIMULTANEOUS_SCANS)
+ return 0;
+
+ IWL_DEBUG_SCAN(mvm,
+ "Scan completed, uid %u type %s, status %s, EBS status %s\n",
+ uid, sched ? "sched" : "regular",
+ notif->status == IWL_SCAN_OFFLOAD_COMPLETED ?
+ "completed" : "aborted",
+ notif->ebs_status == IWL_SCAN_EBS_SUCCESS ?
+ "success" : "failed");
+
+ mvm->last_ebs_successful = !notif->ebs_status;
+ mvm->scan_uid[uid_idx] = 0;
+
+ if (!sched) {
+ ieee80211_scan_completed(mvm->hw,
+ notif->status ==
+ IWL_SCAN_OFFLOAD_ABORTED);
+ iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
+ } else if (!iwl_mvm_find_scan_type(mvm, IWL_UMAC_SCAN_UID_SCHED_SCAN)) {
+ ieee80211_sched_scan_stopped(mvm->hw);
+ } else {
+ IWL_DEBUG_SCAN(mvm, "Another sched scan is running\n");
+ }
+
+ return 0;
+}
+
+static bool iwl_scan_umac_done_check(struct iwl_notif_wait_data *notif_wait,
+ struct iwl_rx_packet *pkt, void *data)
+{
+ struct iwl_umac_scan_done *scan_done = data;
+ struct iwl_umac_scan_complete *notif = (void *)pkt->data;
+ u32 uid = __le32_to_cpu(notif->uid);
+ int uid_idx = iwl_mvm_find_scan_uid(scan_done->mvm, uid);
+
+ if (WARN_ON(pkt->hdr.cmd != SCAN_COMPLETE_UMAC))
+ return false;
+
+ if (uid_idx >= IWL_MVM_MAX_SIMULTANEOUS_SCANS)
+ return false;
+
+ /*
+ * Clear scan uid of scans that was aborted from above and completed
+ * in FW so the RX handler does nothing.
+ */
+ scan_done->mvm->scan_uid[uid_idx] = 0;
+
+ return !iwl_mvm_find_scan_type(scan_done->mvm, scan_done->type);
+}
+
+static int iwl_umac_scan_abort_one(struct iwl_mvm *mvm, u32 uid)
+{
+ struct iwl_umac_scan_abort cmd = {
+ .hdr.size = cpu_to_le16(sizeof(struct iwl_umac_scan_abort) -
+ sizeof(struct iwl_mvm_umac_cmd_hdr)),
+ .uid = cpu_to_le32(uid),
+ };
+
+ lockdep_assert_held(&mvm->mutex);
+
+ IWL_DEBUG_SCAN(mvm, "Sending scan abort, uid %u\n", uid);
+
+ return iwl_mvm_send_cmd_pdu(mvm, SCAN_ABORT_UMAC, 0, sizeof(cmd), &cmd);
+}
+
+static int iwl_umac_scan_stop(struct iwl_mvm *mvm,
+ enum iwl_umac_scan_uid_type type, bool notify)
+{
+ struct iwl_notification_wait wait_scan_done;
+ static const u8 scan_done_notif[] = { SCAN_COMPLETE_UMAC, };
+ struct iwl_umac_scan_done scan_done = {
+ .mvm = mvm,
+ .type = type,
+ };
+ int i, ret = -EIO;
+
+ iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_done,
+ scan_done_notif,
+ ARRAY_SIZE(scan_done_notif),
+ iwl_scan_umac_done_check, &scan_done);
+
+ IWL_DEBUG_SCAN(mvm, "Preparing to stop scan, type %x\n", type);
+
+ for (i = 0; i < IWL_MVM_MAX_SIMULTANEOUS_SCANS; i++) {
+ if (mvm->scan_uid[i] & type) {
+ int err;
+
+ if (iwl_mvm_is_radio_killed(mvm) &&
+ (type & IWL_UMAC_SCAN_UID_REG_SCAN)) {
+ ieee80211_scan_completed(mvm->hw, true);
+ iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
+ break;
+ }
+
+ err = iwl_umac_scan_abort_one(mvm, mvm->scan_uid[i]);
+ if (!err)
+ ret = 0;
+ }
+ }
+
+ if (ret) {
+ IWL_DEBUG_SCAN(mvm, "Couldn't stop scan\n");
+ iwl_remove_notification(&mvm->notif_wait, &wait_scan_done);
+ return ret;
+ }
+
+ ret = iwl_wait_notification(&mvm->notif_wait, &wait_scan_done, 1 * HZ);
+ if (ret)
+ return ret;
+
+ if (notify) {
+ if (type & IWL_UMAC_SCAN_UID_SCHED_SCAN)
+ ieee80211_sched_scan_stopped(mvm->hw);
+ if (type & IWL_UMAC_SCAN_UID_REG_SCAN) {
+ ieee80211_scan_completed(mvm->hw, true);
+ iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
+ }
+ }
+
+ return ret;
+}
+
+int iwl_mvm_scan_size(struct iwl_mvm *mvm)
+{
+ if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)
+ return sizeof(struct iwl_scan_req_umac) +
+ sizeof(struct iwl_scan_channel_cfg_umac) *
+ mvm->fw->ucode_capa.n_scan_channels +
+ sizeof(struct iwl_scan_req_umac_tail);
+
+ if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
+ return sizeof(struct iwl_scan_req_unified_lmac) +
+ sizeof(struct iwl_scan_channel_cfg_lmac) *
+ mvm->fw->ucode_capa.n_scan_channels +
+ sizeof(struct iwl_scan_probe_req);
+
+ return sizeof(struct iwl_scan_cmd) +
+ mvm->fw->ucode_capa.max_probe_length +
+ mvm->fw->ucode_capa.n_scan_channels *
+ sizeof(struct iwl_scan_channel);
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c
index 1731c20..d86fe43 100644
--- a/drivers/net/wireless/iwlwifi/mvm/sta.c
+++ b/drivers/net/wireless/iwlwifi/mvm/sta.c
@@ -204,6 +204,56 @@
return ret;
}
+static int iwl_mvm_tdls_sta_init(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta)
+{
+ unsigned long used_hw_queues;
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ u32 ac;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ used_hw_queues = iwl_mvm_get_used_hw_queues(mvm, NULL);
+
+ /* Find available queues, and allocate them to the ACs */
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+ u8 queue = find_first_zero_bit(&used_hw_queues,
+ mvm->first_agg_queue);
+
+ if (queue >= mvm->first_agg_queue) {
+ IWL_ERR(mvm, "Failed to allocate STA queue\n");
+ return -EBUSY;
+ }
+
+ __set_bit(queue, &used_hw_queues);
+ mvmsta->hw_queue[ac] = queue;
+ }
+
+ /* Found a place for all queues - enable them */
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+ iwl_mvm_enable_ac_txq(mvm, mvmsta->hw_queue[ac],
+ iwl_mvm_ac_to_tx_fifo[ac]);
+ mvmsta->tfd_queue_msk |= BIT(mvmsta->hw_queue[ac]);
+ }
+
+ return 0;
+}
+
+static void iwl_mvm_tdls_sta_deinit(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ unsigned long sta_msk;
+ int i;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ /* disable the TDLS STA-specific queues */
+ sta_msk = mvmsta->tfd_queue_msk;
+ for_each_set_bit(i, &sta_msk, sizeof(sta_msk))
+ iwl_mvm_disable_txq(mvm, i);
+}
+
int iwl_mvm_add_sta(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta)
@@ -237,9 +287,17 @@
atomic_set(&mvm->pending_frames[sta_id], 0);
mvm_sta->tid_disable_agg = 0;
mvm_sta->tfd_queue_msk = 0;
- for (i = 0; i < IEEE80211_NUM_ACS; i++)
- if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE)
- mvm_sta->tfd_queue_msk |= BIT(vif->hw_queue[i]);
+
+ /* allocate new queues for a TDLS station */
+ if (sta->tdls) {
+ ret = iwl_mvm_tdls_sta_init(mvm, sta);
+ if (ret)
+ return ret;
+ } else {
+ for (i = 0; i < IEEE80211_NUM_ACS; i++)
+ if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE)
+ mvm_sta->tfd_queue_msk |= BIT(vif->hw_queue[i]);
+ }
/* for HW restart - reset everything but the sequence number */
for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
@@ -251,7 +309,7 @@
ret = iwl_mvm_sta_send_to_fw(mvm, sta, false);
if (ret)
- return ret;
+ goto err;
if (vif->type == NL80211_IFTYPE_STATION) {
if (!sta->tdls) {
@@ -265,6 +323,10 @@
rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], sta);
return 0;
+
+err:
+ iwl_mvm_tdls_sta_deinit(mvm, sta);
+ return ret;
}
int iwl_mvm_update_sta(struct iwl_mvm *mvm,
@@ -398,6 +460,17 @@
}
RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta_id], NULL);
clear_bit(sta_id, mvm->sta_drained);
+
+ if (mvm->tfd_drained[sta_id]) {
+ unsigned long i, msk = mvm->tfd_drained[sta_id];
+
+ for_each_set_bit(i, &msk, sizeof(msk))
+ iwl_mvm_disable_txq(mvm, i);
+
+ mvm->tfd_drained[sta_id] = 0;
+ IWL_DEBUG_TDLS(mvm, "Drained sta %d, with queues %ld\n",
+ sta_id, msk);
+ }
}
mutex_unlock(&mvm->mutex);
@@ -431,6 +504,15 @@
}
/*
+ * This shouldn't happen - the TDLS channel switch should be canceled
+ * before the STA is removed.
+ */
+ if (WARN_ON_ONCE(mvm->tdls_cs.peer.sta_id == mvm_sta->sta_id)) {
+ mvm->tdls_cs.peer.sta_id = IWL_MVM_STATION_COUNT;
+ cancel_delayed_work(&mvm->tdls_cs.dwork);
+ }
+
+ /*
* Make sure that the tx response code sees the station as -EBUSY and
* calls the drain worker.
*/
@@ -443,9 +525,22 @@
rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id],
ERR_PTR(-EBUSY));
spin_unlock_bh(&mvm_sta->lock);
+
+ /* disable TDLS sta queues on drain complete */
+ if (sta->tdls) {
+ mvm->tfd_drained[mvm_sta->sta_id] =
+ mvm_sta->tfd_queue_msk;
+ IWL_DEBUG_TDLS(mvm, "Draining TDLS sta %d\n",
+ mvm_sta->sta_id);
+ }
+
ret = iwl_mvm_drain_sta(mvm, mvm_sta, true);
} else {
spin_unlock_bh(&mvm_sta->lock);
+
+ if (sta->tdls)
+ iwl_mvm_tdls_sta_deinit(mvm, sta);
+
ret = iwl_mvm_rm_sta_common(mvm, mvm_sta->sta_id);
RCU_INIT_POINTER(mvm->fw_id_to_mac_id[mvm_sta->sta_id], NULL);
}
@@ -609,7 +704,7 @@
lockdep_assert_held(&mvm->mutex);
- qmask = iwl_mvm_mac_get_queues_mask(mvm, vif);
+ qmask = iwl_mvm_mac_get_queues_mask(vif);
/*
* The firmware defines the TFD queue mask to only be relevant
@@ -1071,15 +1166,16 @@
static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
struct iwl_mvm_sta *mvm_sta,
- struct ieee80211_key_conf *keyconf,
- u8 sta_id, u32 tkip_iv32, u16 *tkip_p1k,
- u32 cmd_flags)
+ struct ieee80211_key_conf *keyconf, bool mcast,
+ u32 tkip_iv32, u16 *tkip_p1k, u32 cmd_flags)
{
struct iwl_mvm_add_sta_key_cmd cmd = {};
__le16 key_flags;
- int ret, status;
+ int ret;
+ u32 status;
u16 keyidx;
int i;
+ u8 sta_id = mvm_sta->sta_id;
keyidx = (keyconf->keyidx << STA_KEY_FLG_KEYID_POS) &
STA_KEY_FLG_KEYID_MSK;
@@ -1098,12 +1194,18 @@
key_flags |= cpu_to_le16(STA_KEY_FLG_CCM);
memcpy(cmd.key, keyconf->key, keyconf->keylen);
break;
+ case WLAN_CIPHER_SUITE_WEP104:
+ key_flags |= cpu_to_le16(STA_KEY_FLG_WEP_13BYTES);
+ case WLAN_CIPHER_SUITE_WEP40:
+ key_flags |= cpu_to_le16(STA_KEY_FLG_WEP);
+ memcpy(cmd.key + 3, keyconf->key, keyconf->keylen);
+ break;
default:
key_flags |= cpu_to_le16(STA_KEY_FLG_EXT);
memcpy(cmd.key, keyconf->key, keyconf->keylen);
}
- if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE))
+ if (mcast)
key_flags |= cpu_to_le16(STA_KEY_MULTICAST);
cmd.key_offset = keyconf->hw_key_idx;
@@ -1195,17 +1297,88 @@
return NULL;
}
+static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *keyconf,
+ bool mcast)
+{
+ struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+ int ret;
+ const u8 *addr;
+ struct ieee80211_key_seq seq;
+ u16 p1k[5];
+
+ switch (keyconf->cipher) {
+ case WLAN_CIPHER_SUITE_TKIP:
+ addr = iwl_mvm_get_mac_addr(mvm, vif, sta);
+ /* get phase 1 key from mac80211 */
+ ieee80211_get_key_rx_seq(keyconf, 0, &seq);
+ ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k);
+ ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
+ seq.tkip.iv32, p1k, 0);
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
+ 0, NULL, 0);
+ break;
+ default:
+ ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
+ 0, NULL, 0);
+ }
+
+ return ret;
+}
+
+static int __iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, u8 sta_id,
+ struct ieee80211_key_conf *keyconf,
+ bool mcast)
+{
+ struct iwl_mvm_add_sta_key_cmd cmd = {};
+ __le16 key_flags;
+ int ret;
+ u32 status;
+
+ key_flags = cpu_to_le16((keyconf->keyidx << STA_KEY_FLG_KEYID_POS) &
+ STA_KEY_FLG_KEYID_MSK);
+ key_flags |= cpu_to_le16(STA_KEY_FLG_NO_ENC | STA_KEY_FLG_WEP_KEY_MAP);
+ key_flags |= cpu_to_le16(STA_KEY_NOT_VALID);
+
+ if (mcast)
+ key_flags |= cpu_to_le16(STA_KEY_MULTICAST);
+
+ cmd.key_flags = key_flags;
+ cmd.key_offset = keyconf->hw_key_idx;
+ cmd.sta_id = sta_id;
+
+ status = ADD_STA_SUCCESS;
+ ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, sizeof(cmd),
+ &cmd, &status);
+
+ switch (status) {
+ case ADD_STA_SUCCESS:
+ IWL_DEBUG_WEP(mvm, "MODIFY_STA: remove sta key passed\n");
+ break;
+ default:
+ ret = -EIO;
+ IWL_ERR(mvm, "MODIFY_STA: remove sta key failed\n");
+ break;
+ }
+
+ return ret;
+}
+
int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *keyconf,
bool have_key_offset)
{
- struct iwl_mvm_sta *mvm_sta;
+ bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE);
+ u8 sta_id;
int ret;
- u8 *addr, sta_id;
- struct ieee80211_key_seq seq;
- u16 p1k[5];
lockdep_assert_held(&mvm->mutex);
@@ -1234,8 +1407,7 @@
}
}
- mvm_sta = (struct iwl_mvm_sta *)sta->drv_priv;
- if (WARN_ON_ONCE(mvm_sta->vif != vif))
+ if (WARN_ON_ONCE(iwl_mvm_sta_from_mac80211(sta)->vif != vif))
return -EINVAL;
if (!have_key_offset) {
@@ -1249,26 +1421,26 @@
return -ENOSPC;
}
- switch (keyconf->cipher) {
- case WLAN_CIPHER_SUITE_TKIP:
- addr = iwl_mvm_get_mac_addr(mvm, vif, sta);
- /* get phase 1 key from mac80211 */
- ieee80211_get_key_rx_seq(keyconf, 0, &seq);
- ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k);
- ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, sta_id,
- seq.tkip.iv32, p1k, 0);
- break;
- case WLAN_CIPHER_SUITE_CCMP:
- ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, sta_id,
- 0, NULL, 0);
- break;
- default:
- ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf,
- sta_id, 0, NULL, 0);
+ ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf, mcast);
+ if (ret) {
+ __clear_bit(keyconf->hw_key_idx, mvm->fw_key_table);
+ goto end;
}
- if (ret)
- __clear_bit(keyconf->hw_key_idx, mvm->fw_key_table);
+ /*
+ * For WEP, the same key is used for multicast and unicast. Upload it
+ * again, using the same key offset, and now pointing the other one
+ * to the same key slot (offset).
+ * If this fails, remove the original as well.
+ */
+ if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+ keyconf->cipher == WLAN_CIPHER_SUITE_WEP104) {
+ ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf, !mcast);
+ if (ret) {
+ __clear_bit(keyconf->hw_key_idx, mvm->fw_key_table);
+ __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, mcast);
+ }
+ }
end:
IWL_DEBUG_WEP(mvm, "key: cipher=%x len=%d idx=%d sta=%pM ret=%d\n",
@@ -1282,11 +1454,9 @@
struct ieee80211_sta *sta,
struct ieee80211_key_conf *keyconf)
{
- struct iwl_mvm_sta *mvm_sta;
- struct iwl_mvm_add_sta_key_cmd cmd = {};
- __le16 key_flags;
- int ret, status;
+ bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE);
u8 sta_id;
+ int ret;
lockdep_assert_held(&mvm->mutex);
@@ -1299,8 +1469,7 @@
if (keyconf->cipher == WLAN_CIPHER_SUITE_AES_CMAC)
return iwl_mvm_send_sta_igtk(mvm, keyconf, sta_id, true);
- ret = __test_and_clear_bit(keyconf->hw_key_idx, mvm->fw_key_table);
- if (!ret) {
+ if (!__test_and_clear_bit(keyconf->hw_key_idx, mvm->fw_key_table)) {
IWL_ERR(mvm, "offset %d not used in fw key table.\n",
keyconf->hw_key_idx);
return -ENOENT;
@@ -1326,35 +1495,17 @@
}
}
- mvm_sta = (struct iwl_mvm_sta *)sta->drv_priv;
- if (WARN_ON_ONCE(mvm_sta->vif != vif))
+ if (WARN_ON_ONCE(iwl_mvm_sta_from_mac80211(sta)->vif != vif))
return -EINVAL;
- key_flags = cpu_to_le16((keyconf->keyidx << STA_KEY_FLG_KEYID_POS) &
- STA_KEY_FLG_KEYID_MSK);
- key_flags |= cpu_to_le16(STA_KEY_FLG_NO_ENC | STA_KEY_FLG_WEP_KEY_MAP);
- key_flags |= cpu_to_le16(STA_KEY_NOT_VALID);
+ ret = __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, mcast);
+ if (ret)
+ return ret;
- if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE))
- key_flags |= cpu_to_le16(STA_KEY_MULTICAST);
-
- cmd.key_flags = key_flags;
- cmd.key_offset = keyconf->hw_key_idx;
- cmd.sta_id = sta_id;
-
- status = ADD_STA_SUCCESS;
- ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, sizeof(cmd),
- &cmd, &status);
-
- switch (status) {
- case ADD_STA_SUCCESS:
- IWL_DEBUG_WEP(mvm, "MODIFY_STA: remove sta key passed\n");
- break;
- default:
- ret = -EIO;
- IWL_ERR(mvm, "MODIFY_STA: remove sta key failed\n");
- break;
- }
+ /* delete WEP key twice to get rid of (now useless) offset */
+ if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+ keyconf->cipher == WLAN_CIPHER_SUITE_WEP104)
+ ret = __iwl_mvm_remove_sta_key(mvm, sta_id, keyconf, !mcast);
return ret;
}
@@ -1367,6 +1518,7 @@
{
struct iwl_mvm_sta *mvm_sta;
u8 sta_id = iwl_mvm_get_key_sta_id(vif, sta);
+ bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE);
if (WARN_ON_ONCE(sta_id == IWL_MVM_STATION_COUNT))
return;
@@ -1381,8 +1533,8 @@
}
}
- mvm_sta = (void *)sta->drv_priv;
- iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, sta_id,
+ mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+ iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast,
iv32, phase1key, CMD_ASYNC);
rcu_read_unlock();
}
@@ -1580,3 +1732,18 @@
iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, disable);
}
}
+
+void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm_sta *mvmsta;
+
+ rcu_read_lock();
+
+ mvmsta = iwl_mvm_sta_from_staid_rcu(mvm, mvmvif->ap_sta_id);
+
+ if (!WARN_ON(!mvmsta))
+ iwl_mvm_sta_modify_disable_tx(mvm, mvmsta, true);
+
+ rcu_read_unlock();
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h
index d9c0d7b..d8f4897 100644
--- a/drivers/net/wireless/iwlwifi/mvm/sta.h
+++ b/drivers/net/wireless/iwlwifi/mvm/sta.h
@@ -264,6 +264,7 @@
* the first packet to be sent in legacy HW queue in Tx AGG stop flow.
* Basically when next_reclaimed reaches ssn, we can tell mac80211 that
* we are ready to finish the Tx AGG stop / start flow.
+ * @tx_time: medium time consumed by this A-MPDU
*/
struct iwl_mvm_tid_data {
u16 seq_number;
@@ -274,6 +275,7 @@
enum iwl_mvm_agg_state state;
u16 txq_id;
u16 ssn;
+ u16 tx_time;
};
static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data)
@@ -286,6 +288,7 @@
* struct iwl_mvm_sta - representation of a station in the driver
* @sta_id: the index of the station in the fw (will be replaced by id_n_color)
* @tfd_queue_msk: the tfd queues used by the station
+ * @hw_queue: per-AC mapping of the TFD queues used by station
* @mac_id_n_color: the MAC context this station is linked to
* @tid_disable_agg: bitmap: if bit(tid) is set, the fw won't send ampdus for
* tid.
@@ -309,6 +312,7 @@
struct iwl_mvm_sta {
u32 sta_id;
u32 tfd_queue_msk;
+ u8 hw_queue[IEEE80211_NUM_ACS];
u32 mac_id_n_color;
u16 tid_disable_agg;
u8 max_agg_bufsize;
@@ -418,5 +422,6 @@
void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm,
struct iwl_mvm_vif *mvmvif,
bool disable);
+void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
#endif /* __sta_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/tdls.c b/drivers/net/wireless/iwlwifi/mvm/tdls.c
index 66c82df..c0e00ba 100644
--- a/drivers/net/wireless/iwlwifi/mvm/tdls.c
+++ b/drivers/net/wireless/iwlwifi/mvm/tdls.c
@@ -61,9 +61,13 @@
*
*****************************************************************************/
+#include <linux/etherdevice.h>
#include "mvm.h"
#include "time-event.h"
+#define TU_TO_US(x) (x * 1024)
+#define TU_TO_MS(x) (TU_TO_US(x) / 1000)
+
void iwl_mvm_teardown_tdls_peers(struct iwl_mvm *mvm)
{
struct ieee80211_sta *sta;
@@ -113,17 +117,85 @@
return count;
}
+static void iwl_mvm_tdls_config(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ struct iwl_rx_packet *pkt;
+ struct iwl_tdls_config_res *resp;
+ struct iwl_tdls_config_cmd tdls_cfg_cmd = {};
+ struct iwl_host_cmd cmd = {
+ .id = TDLS_CONFIG_CMD,
+ .flags = CMD_WANT_SKB,
+ .data = { &tdls_cfg_cmd, },
+ .len = { sizeof(struct iwl_tdls_config_cmd), },
+ };
+ struct ieee80211_sta *sta;
+ int ret, i, cnt;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ lockdep_assert_held(&mvm->mutex);
+
+ tdls_cfg_cmd.id_and_color =
+ cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
+ tdls_cfg_cmd.tx_to_ap_tid = IWL_MVM_TDLS_FW_TID;
+ tdls_cfg_cmd.tx_to_ap_ssn = cpu_to_le16(0); /* not used for now */
+
+ /* for now the Tx cmd is empty and unused */
+
+ /* populate TDLS peer data */
+ cnt = 0;
+ for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
+ sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
+ lockdep_is_held(&mvm->mutex));
+ if (IS_ERR_OR_NULL(sta) || !sta->tdls)
+ continue;
+
+ tdls_cfg_cmd.sta_info[cnt].sta_id = i;
+ tdls_cfg_cmd.sta_info[cnt].tx_to_peer_tid =
+ IWL_MVM_TDLS_FW_TID;
+ tdls_cfg_cmd.sta_info[cnt].tx_to_peer_ssn = cpu_to_le16(0);
+ tdls_cfg_cmd.sta_info[cnt].is_initiator =
+ cpu_to_le32(sta->tdls_initiator ? 1 : 0);
+
+ cnt++;
+ }
+
+ tdls_cfg_cmd.tdls_peer_count = cnt;
+ IWL_DEBUG_TDLS(mvm, "send TDLS config to FW for %d peers\n", cnt);
+
+ ret = iwl_mvm_send_cmd(mvm, &cmd);
+ if (WARN_ON_ONCE(ret))
+ return;
+
+ pkt = cmd.resp_pkt;
+ if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
+ IWL_ERR(mvm, "Bad return from TDLS_CONFIG_COMMAND (0x%08X)\n",
+ pkt->hdr.flags);
+ goto exit;
+ }
+
+ if (WARN_ON_ONCE(iwl_rx_packet_payload_len(pkt) != sizeof(*resp)))
+ goto exit;
+
+ /* we don't really care about the response at this point */
+
+exit:
+ iwl_free_resp(&cmd);
+}
+
void iwl_mvm_recalc_tdls_state(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
bool sta_added)
{
int tdls_sta_cnt = iwl_mvm_tdls_sta_count(mvm, vif);
- /*
- * Disable ps when the first TDLS sta is added and re-enable it
- * when the last TDLS sta is removed
- */
- if ((tdls_sta_cnt == 1 && sta_added) ||
- (tdls_sta_cnt == 0 && !sta_added))
+ /* when the first peer joins, send a power update first */
+ if (tdls_sta_cnt == 1 && sta_added)
+ iwl_mvm_power_update_mac(mvm);
+
+ /* configure the FW with TDLS peer info */
+ iwl_mvm_tdls_config(mvm, vif);
+
+ /* when the last peer leaves, send a power update last */
+ if (tdls_sta_cnt == 0 && !sta_added)
iwl_mvm_power_update_mac(mvm);
}
@@ -147,3 +219,488 @@
iwl_mvm_unref(mvm, IWL_MVM_REF_PROTECT_TDLS);
}
+
+static const char *
+iwl_mvm_tdls_cs_state_str(enum iwl_mvm_tdls_cs_state state)
+{
+ switch (state) {
+ case IWL_MVM_TDLS_SW_IDLE:
+ return "IDLE";
+ case IWL_MVM_TDLS_SW_REQ_SENT:
+ return "REQ SENT";
+ case IWL_MVM_TDLS_SW_REQ_RCVD:
+ return "REQ RECEIVED";
+ case IWL_MVM_TDLS_SW_ACTIVE:
+ return "ACTIVE";
+ }
+
+ return NULL;
+}
+
+static void iwl_mvm_tdls_update_cs_state(struct iwl_mvm *mvm,
+ enum iwl_mvm_tdls_cs_state state)
+{
+ if (mvm->tdls_cs.state == state)
+ return;
+
+ IWL_DEBUG_TDLS(mvm, "TDLS channel switch state: %s -> %s\n",
+ iwl_mvm_tdls_cs_state_str(mvm->tdls_cs.state),
+ iwl_mvm_tdls_cs_state_str(state));
+ mvm->tdls_cs.state = state;
+
+ if (state == IWL_MVM_TDLS_SW_IDLE)
+ mvm->tdls_cs.cur_sta_id = IWL_MVM_STATION_COUNT;
+}
+
+int iwl_mvm_rx_tdls_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
+ struct iwl_device_cmd *cmd)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_tdls_channel_switch_notif *notif = (void *)pkt->data;
+ struct ieee80211_sta *sta;
+ unsigned int delay;
+ struct iwl_mvm_sta *mvmsta;
+ struct ieee80211_vif *vif;
+ u32 sta_id = le32_to_cpu(notif->sta_id);
+
+ lockdep_assert_held(&mvm->mutex);
+
+ /* can fail sometimes */
+ if (!le32_to_cpu(notif->status)) {
+ iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_IDLE);
+ goto out;
+ }
+
+ if (WARN_ON(sta_id >= IWL_MVM_STATION_COUNT))
+ goto out;
+
+ sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],
+ lockdep_is_held(&mvm->mutex));
+ /* the station may not be here, but if it is, it must be a TDLS peer */
+ if (IS_ERR_OR_NULL(sta) || WARN_ON(!sta->tdls))
+ goto out;
+
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ vif = mvmsta->vif;
+
+ /*
+ * Update state and possibly switch again after this is over (DTIM).
+ * Also convert TU to msec.
+ */
+ delay = TU_TO_MS(vif->bss_conf.dtim_period * vif->bss_conf.beacon_int);
+ mod_delayed_work(system_wq, &mvm->tdls_cs.dwork,
+ msecs_to_jiffies(delay));
+
+ iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_ACTIVE);
+
+out:
+ return 0;
+}
+
+static int
+iwl_mvm_tdls_check_action(struct iwl_mvm *mvm,
+ enum iwl_tdls_channel_switch_type type,
+ const u8 *peer, bool peer_initiator)
+{
+ bool same_peer = false;
+ int ret = 0;
+
+ /* get the existing peer if it's there */
+ if (mvm->tdls_cs.state != IWL_MVM_TDLS_SW_IDLE &&
+ mvm->tdls_cs.cur_sta_id != IWL_MVM_STATION_COUNT) {
+ struct ieee80211_sta *sta = rcu_dereference_protected(
+ mvm->fw_id_to_mac_id[mvm->tdls_cs.cur_sta_id],
+ lockdep_is_held(&mvm->mutex));
+ if (!IS_ERR_OR_NULL(sta))
+ same_peer = ether_addr_equal(peer, sta->addr);
+ }
+
+ switch (mvm->tdls_cs.state) {
+ case IWL_MVM_TDLS_SW_IDLE:
+ /*
+ * might be spurious packet from the peer after the switch is
+ * already done
+ */
+ if (type == TDLS_MOVE_CH)
+ ret = -EINVAL;
+ break;
+ case IWL_MVM_TDLS_SW_REQ_SENT:
+ /*
+ * We received a ch-switch request while an outgoing one is
+ * pending. Allow it to proceed if the other peer is the same
+ * one we sent to, and we are not the link initiator.
+ */
+ if (type == TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH) {
+ if (!same_peer)
+ ret = -EBUSY;
+ else if (!peer_initiator) /* we are the initiator */
+ ret = -EBUSY;
+ }
+ break;
+ case IWL_MVM_TDLS_SW_REQ_RCVD:
+ /* as above, allow the link initiator to proceed */
+ if (type == TDLS_SEND_CHAN_SW_REQ) {
+ if (!same_peer)
+ ret = -EBUSY;
+ else if (peer_initiator) /* they are the initiator */
+ ret = -EBUSY;
+ } else if (type == TDLS_MOVE_CH) {
+ ret = -EINVAL;
+ }
+ break;
+ case IWL_MVM_TDLS_SW_ACTIVE:
+ /* we don't allow initiations during active channel switch */
+ if (type == TDLS_SEND_CHAN_SW_REQ)
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ret)
+ IWL_DEBUG_TDLS(mvm,
+ "Invalid TDLS action %d state %d peer %pM same_peer %d initiator %d\n",
+ type, mvm->tdls_cs.state, peer, same_peer,
+ peer_initiator);
+
+ return ret;
+}
+
+static int
+iwl_mvm_tdls_config_channel_switch(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ enum iwl_tdls_channel_switch_type type,
+ const u8 *peer, bool peer_initiator,
+ u8 oper_class,
+ struct cfg80211_chan_def *chandef,
+ u32 timestamp, u16 switch_time,
+ u16 switch_timeout, struct sk_buff *skb,
+ u32 ch_sw_tm_ie)
+{
+ struct ieee80211_sta *sta;
+ struct iwl_mvm_sta *mvmsta;
+ struct ieee80211_tx_info *info;
+ struct ieee80211_hdr *hdr;
+ struct iwl_tdls_channel_switch_cmd cmd = {0};
+ int ret;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ ret = iwl_mvm_tdls_check_action(mvm, type, peer, peer_initiator);
+ if (ret)
+ return ret;
+
+ if (!skb || WARN_ON(skb->len > IWL_TDLS_CH_SW_FRAME_MAX_SIZE)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ cmd.switch_type = type;
+ cmd.timing.frame_timestamp = cpu_to_le32(timestamp);
+ cmd.timing.switch_time = cpu_to_le32(switch_time);
+ cmd.timing.switch_timeout = cpu_to_le32(switch_timeout);
+
+ rcu_read_lock();
+ sta = ieee80211_find_sta(vif, peer);
+ if (!sta) {
+ rcu_read_unlock();
+ ret = -ENOENT;
+ goto out;
+ }
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ cmd.peer_sta_id = cpu_to_le32(mvmsta->sta_id);
+
+ if (!chandef) {
+ if (mvm->tdls_cs.state == IWL_MVM_TDLS_SW_REQ_SENT &&
+ mvm->tdls_cs.peer.chandef.chan) {
+ /* actually moving to the channel */
+ chandef = &mvm->tdls_cs.peer.chandef;
+ } else if (mvm->tdls_cs.state == IWL_MVM_TDLS_SW_ACTIVE &&
+ type == TDLS_MOVE_CH) {
+ /* we need to return to base channel */
+ struct ieee80211_chanctx_conf *chanctx =
+ rcu_dereference(vif->chanctx_conf);
+
+ if (WARN_ON_ONCE(!chanctx)) {
+ rcu_read_unlock();
+ goto out;
+ }
+
+ chandef = &chanctx->def;
+ }
+ }
+
+ if (chandef) {
+ cmd.ci.band = (chandef->chan->band == IEEE80211_BAND_2GHZ ?
+ PHY_BAND_24 : PHY_BAND_5);
+ cmd.ci.channel = chandef->chan->hw_value;
+ cmd.ci.width = iwl_mvm_get_channel_width(chandef);
+ cmd.ci.ctrl_pos = iwl_mvm_get_ctrl_pos(chandef);
+ }
+
+ /* keep quota calculation simple for now - 50% of DTIM for TDLS */
+ cmd.timing.max_offchan_duration =
+ cpu_to_le32(TU_TO_US(vif->bss_conf.dtim_period *
+ vif->bss_conf.beacon_int) / 2);
+
+ /* Switch time is the first element in the switch-timing IE. */
+ cmd.frame.switch_time_offset = cpu_to_le32(ch_sw_tm_ie + 2);
+
+ info = IEEE80211_SKB_CB(skb);
+ if (info->control.hw_key)
+ iwl_mvm_set_tx_cmd_crypto(mvm, info, &cmd.frame.tx_cmd, skb);
+
+ iwl_mvm_set_tx_cmd(mvm, skb, &cmd.frame.tx_cmd, info,
+ mvmsta->sta_id);
+
+ hdr = (void *)skb->data;
+ iwl_mvm_set_tx_cmd_rate(mvm, &cmd.frame.tx_cmd, info, sta,
+ hdr->frame_control);
+ rcu_read_unlock();
+
+ memcpy(cmd.frame.data, skb->data, skb->len);
+
+ ret = iwl_mvm_send_cmd_pdu(mvm, TDLS_CHANNEL_SWITCH_CMD, 0,
+ sizeof(cmd), &cmd);
+ if (ret) {
+ IWL_ERR(mvm, "Failed to send TDLS_CHANNEL_SWITCH cmd: %d\n",
+ ret);
+ goto out;
+ }
+
+ /* channel switch has started, update state */
+ if (type != TDLS_MOVE_CH) {
+ mvm->tdls_cs.cur_sta_id = mvmsta->sta_id;
+ iwl_mvm_tdls_update_cs_state(mvm,
+ type == TDLS_SEND_CHAN_SW_REQ ?
+ IWL_MVM_TDLS_SW_REQ_SENT :
+ IWL_MVM_TDLS_SW_REQ_RCVD);
+ }
+
+out:
+
+ /* channel switch failed - we are idle */
+ if (ret)
+ iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_IDLE);
+
+ return ret;
+}
+
+void iwl_mvm_tdls_ch_switch_work(struct work_struct *work)
+{
+ struct iwl_mvm *mvm;
+ struct ieee80211_sta *sta;
+ struct iwl_mvm_sta *mvmsta;
+ struct ieee80211_vif *vif;
+ unsigned int delay;
+ int ret;
+
+ mvm = container_of(work, struct iwl_mvm, tdls_cs.dwork.work);
+ mutex_lock(&mvm->mutex);
+
+ /* called after an active channel switch has finished or timed-out */
+ iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_IDLE);
+
+ /* station might be gone, in that case do nothing */
+ if (mvm->tdls_cs.peer.sta_id == IWL_MVM_STATION_COUNT)
+ goto out;
+
+ sta = rcu_dereference_protected(
+ mvm->fw_id_to_mac_id[mvm->tdls_cs.peer.sta_id],
+ lockdep_is_held(&mvm->mutex));
+ /* the station may not be here, but if it is, it must be a TDLS peer */
+ if (!sta || IS_ERR(sta) || WARN_ON(!sta->tdls))
+ goto out;
+
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ vif = mvmsta->vif;
+ ret = iwl_mvm_tdls_config_channel_switch(mvm, vif,
+ TDLS_SEND_CHAN_SW_REQ,
+ sta->addr,
+ mvm->tdls_cs.peer.initiator,
+ mvm->tdls_cs.peer.op_class,
+ &mvm->tdls_cs.peer.chandef,
+ 0, 0, 0,
+ mvm->tdls_cs.peer.skb,
+ mvm->tdls_cs.peer.ch_sw_tm_ie);
+ if (ret)
+ IWL_ERR(mvm, "Not sending TDLS channel switch: %d\n", ret);
+
+ /* retry after a DTIM if we failed sending now */
+ delay = TU_TO_MS(vif->bss_conf.dtim_period * vif->bss_conf.beacon_int);
+ queue_delayed_work(system_wq, &mvm->tdls_cs.dwork,
+ msecs_to_jiffies(delay));
+out:
+ mutex_unlock(&mvm->mutex);
+}
+
+int
+iwl_mvm_tdls_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u8 oper_class,
+ struct cfg80211_chan_def *chandef,
+ struct sk_buff *tmpl_skb, u32 ch_sw_tm_ie)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct iwl_mvm_sta *mvmsta;
+ unsigned int delay;
+ int ret;
+
+ mutex_lock(&mvm->mutex);
+
+ IWL_DEBUG_TDLS(mvm, "TDLS channel switch with %pM ch %d width %d\n",
+ sta->addr, chandef->chan->center_freq, chandef->width);
+
+ /* we only support a single peer for channel switching */
+ if (mvm->tdls_cs.peer.sta_id != IWL_MVM_STATION_COUNT) {
+ IWL_DEBUG_TDLS(mvm,
+ "Existing peer. Can't start switch with %pM\n",
+ sta->addr);
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ret = iwl_mvm_tdls_config_channel_switch(mvm, vif,
+ TDLS_SEND_CHAN_SW_REQ,
+ sta->addr, sta->tdls_initiator,
+ oper_class, chandef, 0, 0, 0,
+ tmpl_skb, ch_sw_tm_ie);
+ if (ret)
+ goto out;
+
+ /*
+ * Mark the peer as "in tdls switch" for this vif. We only allow a
+ * single such peer per vif.
+ */
+ mvm->tdls_cs.peer.skb = skb_copy(tmpl_skb, GFP_KERNEL);
+ if (!mvm->tdls_cs.peer.skb) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ mvm->tdls_cs.peer.sta_id = mvmsta->sta_id;
+ mvm->tdls_cs.peer.chandef = *chandef;
+ mvm->tdls_cs.peer.initiator = sta->tdls_initiator;
+ mvm->tdls_cs.peer.op_class = oper_class;
+ mvm->tdls_cs.peer.ch_sw_tm_ie = ch_sw_tm_ie;
+
+ /*
+ * Wait for 2 DTIM periods before attempting the next switch. The next
+ * switch will be made sooner if the current one completes before that.
+ */
+ delay = 2 * TU_TO_MS(vif->bss_conf.dtim_period *
+ vif->bss_conf.beacon_int);
+ mod_delayed_work(system_wq, &mvm->tdls_cs.dwork,
+ msecs_to_jiffies(delay));
+
+out:
+ mutex_unlock(&mvm->mutex);
+ return ret;
+}
+
+void iwl_mvm_tdls_cancel_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct ieee80211_sta *cur_sta;
+ bool wait_for_phy = false;
+
+ mutex_lock(&mvm->mutex);
+
+ IWL_DEBUG_TDLS(mvm, "TDLS cancel channel switch with %pM\n", sta->addr);
+
+ /* we only support a single peer for channel switching */
+ if (mvm->tdls_cs.peer.sta_id == IWL_MVM_STATION_COUNT) {
+ IWL_DEBUG_TDLS(mvm, "No ch switch peer - %pM\n", sta->addr);
+ goto out;
+ }
+
+ cur_sta = rcu_dereference_protected(
+ mvm->fw_id_to_mac_id[mvm->tdls_cs.peer.sta_id],
+ lockdep_is_held(&mvm->mutex));
+ /* make sure it's the same peer */
+ if (cur_sta != sta)
+ goto out;
+
+ /*
+ * If we're currently in a switch because of the now canceled peer,
+ * wait a DTIM here to make sure the phy is back on the base channel.
+ * We can't otherwise force it.
+ */
+ if (mvm->tdls_cs.cur_sta_id == mvm->tdls_cs.peer.sta_id &&
+ mvm->tdls_cs.state != IWL_MVM_TDLS_SW_IDLE)
+ wait_for_phy = true;
+
+ mvm->tdls_cs.peer.sta_id = IWL_MVM_STATION_COUNT;
+ dev_kfree_skb(mvm->tdls_cs.peer.skb);
+ mvm->tdls_cs.peer.skb = NULL;
+
+out:
+ mutex_unlock(&mvm->mutex);
+
+ /* make sure the phy is on the base channel */
+ if (wait_for_phy)
+ msleep(TU_TO_MS(vif->bss_conf.dtim_period *
+ vif->bss_conf.beacon_int));
+
+ /* flush the channel switch state */
+ flush_delayed_work(&mvm->tdls_cs.dwork);
+
+ IWL_DEBUG_TDLS(mvm, "TDLS ending channel switch with %pM\n", sta->addr);
+}
+
+void
+iwl_mvm_tdls_recv_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_tdls_ch_sw_params *params)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ enum iwl_tdls_channel_switch_type type;
+ unsigned int delay;
+
+ mutex_lock(&mvm->mutex);
+
+ IWL_DEBUG_TDLS(mvm,
+ "Received TDLS ch switch action %d from %pM status %d\n",
+ params->action_code, params->sta->addr, params->status);
+
+ /*
+ * we got a non-zero status from a peer we were switching to - move to
+ * the idle state and retry again later
+ */
+ if (params->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE &&
+ params->status != 0 &&
+ mvm->tdls_cs.state == IWL_MVM_TDLS_SW_REQ_SENT &&
+ mvm->tdls_cs.cur_sta_id != IWL_MVM_STATION_COUNT) {
+ struct ieee80211_sta *cur_sta;
+
+ /* make sure it's the same peer */
+ cur_sta = rcu_dereference_protected(
+ mvm->fw_id_to_mac_id[mvm->tdls_cs.cur_sta_id],
+ lockdep_is_held(&mvm->mutex));
+ if (cur_sta == params->sta) {
+ iwl_mvm_tdls_update_cs_state(mvm,
+ IWL_MVM_TDLS_SW_IDLE);
+ goto retry;
+ }
+ }
+
+ type = (params->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST) ?
+ TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH : TDLS_MOVE_CH;
+
+ iwl_mvm_tdls_config_channel_switch(mvm, vif, type, params->sta->addr,
+ params->sta->tdls_initiator, 0,
+ params->chandef, params->timestamp,
+ params->switch_time,
+ params->switch_timeout,
+ params->tmpl_skb,
+ params->ch_sw_tm_ie);
+
+retry:
+ /* register a timeout in case we don't succeed in switching */
+ delay = vif->bss_conf.dtim_period * vif->bss_conf.beacon_int *
+ 1024 / 1000;
+ mod_delayed_work(system_wq, &mvm->tdls_cs.dwork,
+ msecs_to_jiffies(delay));
+ mutex_unlock(&mvm->mutex);
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c
index b7f9e61..54fafbf 100644
--- a/drivers/net/wireless/iwlwifi/mvm/time-event.c
+++ b/drivers/net/wireless/iwlwifi/mvm/time-event.c
@@ -191,6 +191,35 @@
return true;
}
+static void
+iwl_mvm_te_handle_notify_csa(struct iwl_mvm *mvm,
+ struct iwl_mvm_time_event_data *te_data,
+ struct iwl_time_event_notif *notif)
+{
+ if (!le32_to_cpu(notif->status)) {
+ IWL_DEBUG_TE(mvm, "CSA time event failed to start\n");
+ iwl_mvm_te_clear_data(mvm, te_data);
+ return;
+ }
+
+ switch (te_data->vif->type) {
+ case NL80211_IFTYPE_AP:
+ iwl_mvm_csa_noa_start(mvm);
+ break;
+ case NL80211_IFTYPE_STATION:
+ iwl_mvm_csa_client_absent(mvm, te_data->vif);
+ ieee80211_chswitch_done(te_data->vif, true);
+ break;
+ default:
+ /* should never happen */
+ WARN_ON_ONCE(1);
+ break;
+ }
+
+ /* we don't need it anymore */
+ iwl_mvm_te_clear_data(mvm, te_data);
+}
+
/*
* Handles a FW notification for an event that is known to the driver.
*
@@ -252,14 +281,8 @@
set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
iwl_mvm_ref(mvm, IWL_MVM_REF_ROC);
ieee80211_ready_on_channel(mvm->hw);
- } else if (te_data->vif->type == NL80211_IFTYPE_AP) {
- if (le32_to_cpu(notif->status))
- iwl_mvm_csa_noa_start(mvm);
- else
- IWL_DEBUG_TE(mvm, "CSA NOA failed to start\n");
-
- /* we don't need it anymore */
- iwl_mvm_te_clear_data(mvm, te_data);
+ } else if (te_data->id == TE_CHANNEL_SWITCH_PERIOD) {
+ iwl_mvm_te_handle_notify_csa(mvm, te_data, notif);
}
} else {
IWL_WARN(mvm, "Got TE with unknown action\n");
@@ -305,8 +328,8 @@
te_data->running = false;
te_data->vif = NULL;
te_data->uid = 0;
+ te_data->id = TE_MAX;
} else if (le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_START) {
- set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
set_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status);
te_data->running = true;
ieee80211_ready_on_channel(mvm->hw); /* Start TE */
@@ -549,18 +572,11 @@
}
}
-/*
- * Explicit request to remove a time event. The removal of a time event needs to
- * be synchronized with the flow of a time event's end notification, which also
- * removes the time event from the op mode data structures.
- */
-void iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
- struct iwl_mvm_vif *mvmvif,
- struct iwl_mvm_time_event_data *te_data)
+static bool __iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
+ struct iwl_mvm_time_event_data *te_data,
+ u32 *uid)
{
- struct iwl_time_event_cmd time_cmd = {};
- u32 id, uid;
- int ret;
+ u32 id;
/*
* It is possible that by the time we got to this point the time
@@ -569,7 +585,7 @@
spin_lock_bh(&mvm->time_event_lock);
/* Save time event uid before clearing its data */
- uid = te_data->uid;
+ *uid = te_data->uid;
id = te_data->id;
/*
@@ -584,10 +600,59 @@
* send a removal command.
*/
if (id == TE_MAX) {
- IWL_DEBUG_TE(mvm, "TE 0x%x has already ended\n", uid);
- return;
+ IWL_DEBUG_TE(mvm, "TE 0x%x has already ended\n", *uid);
+ return false;
}
+ return true;
+}
+
+/*
+ * Explicit request to remove a aux roc time event. The removal of a time
+ * event needs to be synchronized with the flow of a time event's end
+ * notification, which also removes the time event from the op mode
+ * data structures.
+ */
+static void iwl_mvm_remove_aux_roc_te(struct iwl_mvm *mvm,
+ struct iwl_mvm_vif *mvmvif,
+ struct iwl_mvm_time_event_data *te_data)
+{
+ struct iwl_hs20_roc_req aux_cmd = {};
+ u32 uid;
+ int ret;
+
+ if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid))
+ return;
+
+ aux_cmd.event_unique_id = cpu_to_le32(uid);
+ aux_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE);
+ aux_cmd.id_and_color =
+ cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
+ IWL_DEBUG_TE(mvm, "Removing BSS AUX ROC TE 0x%x\n",
+ le32_to_cpu(aux_cmd.event_unique_id));
+ ret = iwl_mvm_send_cmd_pdu(mvm, HOT_SPOT_CMD, 0,
+ sizeof(aux_cmd), &aux_cmd);
+
+ if (WARN_ON(ret))
+ return;
+}
+
+/*
+ * Explicit request to remove a time event. The removal of a time event needs to
+ * be synchronized with the flow of a time event's end notification, which also
+ * removes the time event from the op mode data structures.
+ */
+void iwl_mvm_remove_time_event(struct iwl_mvm *mvm,
+ struct iwl_mvm_vif *mvmvif,
+ struct iwl_mvm_time_event_data *te_data)
+{
+ struct iwl_time_event_cmd time_cmd = {};
+ u32 uid;
+ int ret;
+
+ if (!__iwl_mvm_remove_time_event(mvm, te_data, &uid))
+ return;
+
/* When we remove a TE, the UID is to be set in the id field */
time_cmd.id = cpu_to_le32(uid);
time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE);
@@ -666,13 +731,17 @@
return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
}
-void iwl_mvm_stop_p2p_roc(struct iwl_mvm *mvm)
+void iwl_mvm_stop_roc(struct iwl_mvm *mvm)
{
struct iwl_mvm_vif *mvmvif;
struct iwl_mvm_time_event_data *te_data;
+ bool is_p2p = false;
lockdep_assert_held(&mvm->mutex);
+ mvmvif = NULL;
+ spin_lock_bh(&mvm->time_event_lock);
+
/*
* Iterate over the list of time events and find the time event that is
* associated with a P2P_DEVICE interface.
@@ -680,22 +749,41 @@
* event at any given time and this time event coresponds to a ROC
* request
*/
- mvmvif = NULL;
- spin_lock_bh(&mvm->time_event_lock);
list_for_each_entry(te_data, &mvm->time_event_list, list) {
- if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
+ if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE &&
+ te_data->running) {
mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
- break;
+ is_p2p = true;
+ goto remove_te;
}
}
+
+ /*
+ * Iterate over the list of aux roc time events and find the time
+ * event that is associated with a BSS interface.
+ * This assumes that a BSS interface can have only a single time
+ * event at any given time and this time event coresponds to a ROC
+ * request
+ */
+ list_for_each_entry(te_data, &mvm->aux_roc_te_list, list) {
+ if (te_data->running) {
+ mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
+ goto remove_te;
+ }
+ }
+
+remove_te:
spin_unlock_bh(&mvm->time_event_lock);
if (!mvmvif) {
- IWL_WARN(mvm, "P2P_DEVICE no remain on channel event\n");
+ IWL_WARN(mvm, "No remain on channel event\n");
return;
}
- iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
+ if (is_p2p)
+ iwl_mvm_remove_time_event(mvm, mvmvif, te_data);
+ else
+ iwl_mvm_remove_aux_roc_te(mvm, mvmvif, te_data);
iwl_mvm_roc_finished(mvm);
}
diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.h b/drivers/net/wireless/iwlwifi/mvm/time-event.h
index b350e47..6f6b35d 100644
--- a/drivers/net/wireless/iwlwifi/mvm/time-event.h
+++ b/drivers/net/wireless/iwlwifi/mvm/time-event.h
@@ -182,14 +182,14 @@
int duration, enum ieee80211_roc_type type);
/**
- * iwl_mvm_stop_p2p_roc - stop remain on channel for p2p device functionlity
+ * iwl_mvm_stop_roc - stop remain on channel functionality
* @mvm: the mvm component
*
* This function can be used to cancel an ongoing ROC session.
* The function is async, it will instruct the FW to stop serving the ROC
* session, but will not wait for the actual stopping of the session.
*/
-void iwl_mvm_stop_p2p_roc(struct iwl_mvm *mvm);
+void iwl_mvm_stop_roc(struct iwl_mvm *mvm);
/**
* iwl_mvm_remove_time_event - general function to clean up of time event
diff --git a/drivers/net/wireless/iwlwifi/mvm/tt.c b/drivers/net/wireless/iwlwifi/mvm/tt.c
index acca44a..2b1e61f 100644
--- a/drivers/net/wireless/iwlwifi/mvm/tt.c
+++ b/drivers/net/wireless/iwlwifi/mvm/tt.c
@@ -64,10 +64,6 @@
*****************************************************************************/
#include "mvm.h"
-#include "iwl-config.h"
-#include "iwl-io.h"
-#include "iwl-csr.h"
-#include "iwl-prph.h"
#define IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT HZ
@@ -99,32 +95,81 @@
iwl_mvm_set_hw_ctkill_state(mvm, false);
}
-static bool iwl_mvm_temp_notif(struct iwl_notif_wait_data *notif_wait,
- struct iwl_rx_packet *pkt, void *data)
+void iwl_mvm_tt_temp_changed(struct iwl_mvm *mvm, u32 temp)
{
- struct iwl_mvm *mvm =
- container_of(notif_wait, struct iwl_mvm, notif_wait);
- int *temp = data;
+ /* ignore the notification if we are in test mode */
+ if (mvm->temperature_test)
+ return;
+
+ if (mvm->temperature == temp)
+ return;
+
+ mvm->temperature = temp;
+ iwl_mvm_tt_handler(mvm);
+}
+
+static int iwl_mvm_temp_notif_parse(struct iwl_mvm *mvm,
+ struct iwl_rx_packet *pkt)
+{
struct iwl_dts_measurement_notif *notif;
int len = iwl_rx_packet_payload_len(pkt);
+ int temp;
if (WARN_ON_ONCE(len != sizeof(*notif))) {
IWL_ERR(mvm, "Invalid DTS_MEASUREMENT_NOTIFICATION\n");
- return true;
+ return -EINVAL;
}
notif = (void *)pkt->data;
- *temp = le32_to_cpu(notif->temp);
+ temp = le32_to_cpu(notif->temp);
/* shouldn't be negative, but since it's s32, make sure it isn't */
- if (WARN_ON_ONCE(*temp < 0))
- *temp = 0;
+ if (WARN_ON_ONCE(temp < 0))
+ temp = 0;
- IWL_DEBUG_TEMP(mvm, "DTS_MEASUREMENT_NOTIFICATION - %d\n", *temp);
+ IWL_DEBUG_TEMP(mvm, "DTS_MEASUREMENT_NOTIFICATION - %d\n", temp);
+
+ return temp;
+}
+
+static bool iwl_mvm_temp_notif_wait(struct iwl_notif_wait_data *notif_wait,
+ struct iwl_rx_packet *pkt, void *data)
+{
+ struct iwl_mvm *mvm =
+ container_of(notif_wait, struct iwl_mvm, notif_wait);
+ int *temp = data;
+ int ret;
+
+ ret = iwl_mvm_temp_notif_parse(mvm, pkt);
+ if (ret < 0)
+ return true;
+
+ *temp = ret;
+
return true;
}
+int iwl_mvm_temp_notif(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb,
+ struct iwl_device_cmd *cmd)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ int temp;
+
+ /* the notification is handled synchronously in ctkill, so skip here */
+ if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status))
+ return 0;
+
+ temp = iwl_mvm_temp_notif_parse(mvm, pkt);
+ if (temp < 0)
+ return 0;
+
+ iwl_mvm_tt_temp_changed(mvm, temp);
+
+ return 0;
+}
+
static int iwl_mvm_get_temp_cmd(struct iwl_mvm *mvm)
{
struct iwl_dts_measurement_cmd cmd = {
@@ -145,7 +190,7 @@
iwl_init_notification_wait(&mvm->notif_wait, &wait_temp_notif,
temp_notif, ARRAY_SIZE(temp_notif),
- iwl_mvm_temp_notif, &temp);
+ iwl_mvm_temp_notif_wait, &temp);
ret = iwl_mvm_get_temp_cmd(mvm);
if (ret) {
diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c
index 9877937..178bdce 100644
--- a/drivers/net/wireless/iwlwifi/mvm/tx.c
+++ b/drivers/net/wireless/iwlwifi/mvm/tx.c
@@ -73,9 +73,9 @@
/*
* Sets most of the Tx cmd's fields
*/
-static void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
- struct iwl_tx_cmd *tx_cmd,
- struct ieee80211_tx_info *info, u8 sta_id)
+void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
+ struct iwl_tx_cmd *tx_cmd,
+ struct ieee80211_tx_info *info, u8 sta_id)
{
struct ieee80211_hdr *hdr = (void *)skb->data;
__le16 fc = hdr->frame_control;
@@ -149,11 +149,9 @@
/*
* Sets the fields in the Tx cmd that are rate related
*/
-static void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm,
- struct iwl_tx_cmd *tx_cmd,
- struct ieee80211_tx_info *info,
- struct ieee80211_sta *sta,
- __le16 fc)
+void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd *tx_cmd,
+ struct ieee80211_tx_info *info,
+ struct ieee80211_sta *sta, __le16 fc)
{
u32 rate_flags;
int rate_idx;
@@ -175,14 +173,10 @@
/*
* for data packets, rate info comes from the table inside the fw. This
- * table is controlled by LINK_QUALITY commands. Exclude ctrl port
- * frames like EAPOLs which should be treated as mgmt frames. This
- * avoids them being sent initially in high rates which increases the
- * chances for completion of the 4-Way handshake.
+ * table is controlled by LINK_QUALITY commands
*/
- if (ieee80211_is_data(fc) && sta &&
- !(info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO)) {
+ if (ieee80211_is_data(fc) && sta) {
tx_cmd->initial_rate_index = 0;
tx_cmd->tx_flags |= cpu_to_le32(TX_CMD_FLG_STA_RATE);
return;
@@ -193,8 +187,10 @@
/* HT rate doesn't make sense for a non data frame */
WARN_ONCE(info->control.rates[0].flags & IEEE80211_TX_RC_MCS,
- "Got an HT rate for a non data frame 0x%x\n",
- info->control.rates[0].flags);
+ "Got an HT rate (flags:0x%x/mcs:%d) for a non data frame (fc:0x%x)\n",
+ info->control.rates[0].flags,
+ info->control.rates[0].idx,
+ le16_to_cpu(fc));
rate_idx = info->control.rates[0].idx;
/* if the rate isn't a well known legacy rate, take the lowest one */
@@ -234,10 +230,10 @@
/*
* Sets the fields in the Tx cmd that are crypto related
*/
-static void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm,
- struct ieee80211_tx_info *info,
- struct iwl_tx_cmd *tx_cmd,
- struct sk_buff *skb_frag)
+void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm,
+ struct ieee80211_tx_info *info,
+ struct iwl_tx_cmd *tx_cmd,
+ struct sk_buff *skb_frag)
{
struct ieee80211_key_conf *keyconf = info->control.hw_key;
@@ -428,6 +424,13 @@
WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM);
+ if (sta->tdls) {
+ /* default to TID 0 for non-QoS packets */
+ u8 tdls_tid = tid == IWL_MAX_TID_COUNT ? 0 : tid;
+
+ txq_id = mvmsta->hw_queue[tid_to_mac80211_ac[tdls_tid]];
+ }
+
if (is_ampdu) {
if (WARN_ON_ONCE(mvmsta->tid_data[tid].state != IWL_AGG_ON))
goto drop_unlock_sta;
@@ -662,6 +665,12 @@
seq_ctl = le16_to_cpu(hdr->seq_ctrl);
}
+ /*
+ * TODO: this is not accurate if we are freeing more than one
+ * packet.
+ */
+ info->status.tx_time =
+ le16_to_cpu(tx_resp->wireless_media_time);
BUILD_BUG_ON(ARRAY_SIZE(info->status.status_driver_data) < 1);
info->status.status_driver_data[0] =
(void *)(uintptr_t)tx_resp->reduced_tpc;
@@ -854,6 +863,8 @@
mvmsta->tid_data[tid].rate_n_flags =
le32_to_cpu(tx_resp->initial_rate);
mvmsta->tid_data[tid].reduced_tpc = tx_resp->reduced_tpc;
+ mvmsta->tid_data[tid].tx_time =
+ le16_to_cpu(tx_resp->wireless_media_time);
}
rcu_read_unlock();
@@ -882,6 +893,8 @@
info->status.ampdu_len = ba_notif->txed;
iwl_mvm_hwrate_to_tx_status(tid_data->rate_n_flags,
info);
+ /* TODO: not accounted if the whole A-MPDU failed */
+ info->status.tx_time = tid_data->tx_time;
info->status.status_driver_data[0] =
(void *)(uintptr_t)tid_data->reduced_tpc;
}
diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c
index ee6e5f9..8497660 100644
--- a/drivers/net/wireless/iwlwifi/mvm/utils.c
+++ b/drivers/net/wireless/iwlwifi/mvm/utils.c
@@ -734,3 +734,40 @@
return idle;
}
+
+struct iwl_bss_iter_data {
+ struct ieee80211_vif *vif;
+ bool error;
+};
+
+static void iwl_mvm_bss_iface_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_bss_iter_data *data = _data;
+
+ if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
+ return;
+
+ if (data->vif) {
+ data->error = true;
+ return;
+ }
+
+ data->vif = vif;
+}
+
+struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm)
+{
+ struct iwl_bss_iter_data bss_iter_data = {};
+
+ ieee80211_iterate_active_interfaces_atomic(
+ mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_bss_iface_iterator, &bss_iter_data);
+
+ if (bss_iter_data.error) {
+ IWL_ERR(mvm, "More than one managed interface active!\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ return bss_iter_data.vif;
+}
diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c
index 082d286..14d92d1 100644
--- a/drivers/net/wireless/iwlwifi/pcie/drv.c
+++ b/drivers/net/wireless/iwlwifi/pcie/drv.c
@@ -499,6 +499,7 @@
static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
const struct iwl_cfg *cfg = (struct iwl_cfg *)(ent->driver_data);
+ const struct iwl_cfg *cfg_7265d __maybe_unused = NULL;
struct iwl_trans *iwl_trans;
struct iwl_trans_pcie *trans_pcie;
int ret;
@@ -507,6 +508,25 @@
if (IS_ERR(iwl_trans))
return PTR_ERR(iwl_trans);
+#if IS_ENABLED(CPTCFG_IWLMVM)
+ /*
+ * special-case 7265D, it has the same PCI IDs.
+ *
+ * Note that because we already pass the cfg to the transport above,
+ * all the parameters that the transport uses must, until that is
+ * changed, be identical to the ones in the 7265D configuration.
+ */
+ if (cfg == &iwl7265_2ac_cfg)
+ cfg_7265d = &iwl7265d_2ac_cfg;
+ else if (cfg == &iwl7265_2n_cfg)
+ cfg_7265d = &iwl7265d_2n_cfg;
+ else if (cfg == &iwl7265_n_cfg)
+ cfg_7265d = &iwl7265d_n_cfg;
+ if (cfg_7265d &&
+ (iwl_trans->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_7265D)
+ cfg = cfg_7265d;
+#endif
+
pci_set_drvdata(pdev, iwl_trans);
trans_pcie = IWL_TRANS_GET_PCIE_TRANS(iwl_trans);
diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c
index 9062249..93a9e59 100644
--- a/drivers/net/wireless/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/iwlwifi/pcie/trans.c
@@ -78,6 +78,11 @@
#include "iwl-agn-hw.h"
#include "iwl-fw-error-dump.h"
#include "internal.h"
+#include "iwl-fh.h"
+
+/* extended range in FW SRAM */
+#define IWL_FW_MEM_EXTENDED_START 0x40000
+#define IWL_FW_MEM_EXTENDED_END 0x57FFF
static void iwl_pcie_free_fw_monitor(struct iwl_trans *trans)
{
@@ -133,7 +138,7 @@
break;
}
- if (!page)
+ if (WARN_ON_ONCE(!page))
return;
trans_pcie->fw_mon_page = page;
@@ -174,6 +179,7 @@
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
u16 lctl;
+ u16 cap;
/*
* HW bug W/A for instability in PCIe bus L0S->L1 transition.
@@ -184,16 +190,17 @@
* power savings, even without L1.
*/
pcie_capability_read_word(trans_pcie->pci_dev, PCI_EXP_LNKCTL, &lctl);
- if (lctl & PCI_EXP_LNKCTL_ASPM_L1) {
- /* L1-ASPM enabled; disable(!) L0S */
+ if (lctl & PCI_EXP_LNKCTL_ASPM_L1)
iwl_set_bit(trans, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED);
- dev_info(trans->dev, "L1 Enabled; Disabling L0S\n");
- } else {
- /* L1-ASPM disabled; enable(!) L0S */
+ else
iwl_clear_bit(trans, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED);
- dev_info(trans->dev, "L1 Disabled; Enabling L0S\n");
- }
trans->pm_support = !(lctl & PCI_EXP_LNKCTL_ASPM_L0S);
+
+ pcie_capability_read_word(trans_pcie->pci_dev, PCI_EXP_DEVCTL2, &cap);
+ trans->ltr_enabled = cap & PCI_EXP_DEVCTL2_LTR_EN;
+ dev_info(trans->dev, "L1 %sabled - LTR %sabled\n",
+ (lctl & PCI_EXP_LNKCTL_ASPM_L1) ? "En" : "Dis",
+ trans->ltr_enabled ? "En" : "Dis");
}
/*
@@ -428,7 +435,7 @@
ret = iwl_poll_bit(trans, CSR_RESET,
CSR_RESET_REG_FLAG_MASTER_DISABLED,
CSR_RESET_REG_FLAG_MASTER_DISABLED, 100);
- if (ret)
+ if (ret < 0)
IWL_WARN(trans, "Master Disable Timed Out, 100 usec\n");
IWL_DEBUG_INFO(trans, "stop master\n");
@@ -510,6 +517,9 @@
CSR_HW_IF_CONFIG_REG_BIT_NIC_READY,
HW_READY_TIMEOUT);
+ if (ret >= 0)
+ iwl_set_bit(trans, CSR_MBOX_SET_REG, CSR_MBOX_SET_REG_OS_ALIVE);
+
IWL_DEBUG_INFO(trans, "hardware%s ready\n", ret < 0 ? " not" : "");
return ret;
}
@@ -544,7 +554,7 @@
msleep(25);
}
- IWL_DEBUG_INFO(trans, "got NIC after %d iterations\n", iter);
+ IWL_ERR(trans, "Couldn't prepare the card\n");
return ret;
}
@@ -622,14 +632,28 @@
}
for (offset = 0; offset < section->len; offset += chunk_sz) {
- u32 copy_size;
+ u32 copy_size, dst_addr;
+ bool extended_addr = false;
copy_size = min_t(u32, chunk_sz, section->len - offset);
+ dst_addr = section->offset + offset;
+
+ if (dst_addr >= IWL_FW_MEM_EXTENDED_START &&
+ dst_addr <= IWL_FW_MEM_EXTENDED_END)
+ extended_addr = true;
+
+ if (extended_addr)
+ iwl_set_bits_prph(trans, LMPM_CHICK,
+ LMPM_CHICK_EXTENDED_ADDR_SPACE);
memcpy(v_addr, (u8 *)section->data + offset, copy_size);
- ret = iwl_pcie_load_firmware_chunk(trans,
- section->offset + offset,
- p_addr, copy_size);
+ ret = iwl_pcie_load_firmware_chunk(trans, dst_addr, p_addr,
+ copy_size);
+
+ if (extended_addr)
+ iwl_clear_bits_prph(trans, LMPM_CHICK,
+ LMPM_CHICK_EXTENDED_ADDR_SPACE);
+
if (ret) {
IWL_ERR(trans,
"Could not load the [%d] uCode section\n",
@@ -642,14 +666,14 @@
return ret;
}
-static int iwl_pcie_load_cpu_secured_sections(struct iwl_trans *trans,
- const struct fw_img *image,
- int cpu,
- int *first_ucode_section)
+static int iwl_pcie_load_cpu_sections_8000b(struct iwl_trans *trans,
+ const struct fw_img *image,
+ int cpu,
+ int *first_ucode_section)
{
int shift_param;
- int i, ret = 0;
- u32 last_read_idx = 0;
+ int i, ret = 0, sec_num = 0x1;
+ u32 val, last_read_idx = 0;
if (cpu == 1) {
shift_param = 0;
@@ -670,21 +694,16 @@
break;
}
- if (i == (*first_ucode_section) + 1)
- /* set CPU to started */
- iwl_set_bits_prph(trans,
- CSR_UCODE_LOAD_STATUS_ADDR,
- LMPM_CPU_HDRS_LOADING_COMPLETED
- << shift_param);
-
ret = iwl_pcie_load_section(trans, i, &image->sec[i]);
if (ret)
return ret;
+
+ /* Notify the ucode of the loaded section number and status */
+ val = iwl_read_direct32(trans, FH_UCODE_LOAD_STATUS);
+ val = val | (sec_num << shift_param);
+ iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, val);
+ sec_num = (sec_num << 1) | 0x1;
}
- /* image loading complete */
- iwl_set_bits_prph(trans,
- CSR_UCODE_LOAD_STATUS_ADDR,
- LMPM_CPU_UCODE_LOADING_COMPLETED << shift_param);
*first_ucode_section = last_read_idx;
@@ -737,6 +756,64 @@
return 0;
}
+static void iwl_pcie_apply_destination(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ const struct iwl_fw_dbg_dest_tlv *dest = trans->dbg_dest_tlv;
+ int i;
+
+ if (dest->version)
+ IWL_ERR(trans,
+ "DBG DEST version is %d - expect issues\n",
+ dest->version);
+
+ IWL_INFO(trans, "Applying debug destination %s\n",
+ get_fw_dbg_mode_string(dest->monitor_mode));
+
+ if (dest->monitor_mode == EXTERNAL_MODE)
+ iwl_pcie_alloc_fw_monitor(trans);
+ else
+ IWL_WARN(trans, "PCI should have external buffer debug\n");
+
+ for (i = 0; i < trans->dbg_dest_reg_num; i++) {
+ u32 addr = le32_to_cpu(dest->reg_ops[i].addr);
+ u32 val = le32_to_cpu(dest->reg_ops[i].val);
+
+ switch (dest->reg_ops[i].op) {
+ case CSR_ASSIGN:
+ iwl_write32(trans, addr, val);
+ break;
+ case CSR_SETBIT:
+ iwl_set_bit(trans, addr, BIT(val));
+ break;
+ case CSR_CLEARBIT:
+ iwl_clear_bit(trans, addr, BIT(val));
+ break;
+ case PRPH_ASSIGN:
+ iwl_write_prph(trans, addr, val);
+ break;
+ case PRPH_SETBIT:
+ iwl_set_bits_prph(trans, addr, BIT(val));
+ break;
+ case PRPH_CLEARBIT:
+ iwl_clear_bits_prph(trans, addr, BIT(val));
+ break;
+ default:
+ IWL_ERR(trans, "FW debug - unknown OP %d\n",
+ dest->reg_ops[i].op);
+ break;
+ }
+ }
+
+ if (dest->monitor_mode == EXTERNAL_MODE && trans_pcie->fw_mon_size) {
+ iwl_write_prph(trans, le32_to_cpu(dest->base_reg),
+ trans_pcie->fw_mon_phys >> dest->base_shift);
+ iwl_write_prph(trans, le32_to_cpu(dest->end_reg),
+ (trans_pcie->fw_mon_phys +
+ trans_pcie->fw_mon_size) >> dest->end_shift);
+ }
+}
+
static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,
const struct fw_img *image)
{
@@ -744,42 +821,13 @@
int ret = 0;
int first_ucode_section;
- IWL_DEBUG_FW(trans,
- "working with %s image\n",
- image->is_secure ? "Secured" : "Non Secured");
- IWL_DEBUG_FW(trans,
- "working with %s CPU\n",
+ IWL_DEBUG_FW(trans, "working with %s CPU\n",
image->is_dual_cpus ? "Dual" : "Single");
- /* configure the ucode to be ready to get the secured image */
- if (image->is_secure) {
- /* set secure boot inspector addresses */
- iwl_write_prph(trans,
- LMPM_SECURE_INSPECTOR_CODE_ADDR,
- LMPM_SECURE_INSPECTOR_CODE_MEM_SPACE);
-
- iwl_write_prph(trans,
- LMPM_SECURE_INSPECTOR_DATA_ADDR,
- LMPM_SECURE_INSPECTOR_DATA_MEM_SPACE);
-
- /* set CPU1 header address */
- iwl_write_prph(trans,
- LMPM_SECURE_UCODE_LOAD_CPU1_HDR_ADDR,
- LMPM_SECURE_CPU1_HDR_MEM_SPACE);
-
- /* load to FW the binary Secured sections of CPU1 */
- ret = iwl_pcie_load_cpu_secured_sections(trans, image, 1,
- &first_ucode_section);
- if (ret)
- return ret;
-
- } else {
- /* load to FW the binary Non secured sections of CPU1 */
- ret = iwl_pcie_load_cpu_sections(trans, image, 1,
- &first_ucode_section);
- if (ret)
- return ret;
- }
+ /* load to FW the binary non secured sections of CPU1 */
+ ret = iwl_pcie_load_cpu_sections(trans, image, 1, &first_ucode_section);
+ if (ret)
+ return ret;
if (image->is_dual_cpus) {
/* set CPU2 header address */
@@ -788,13 +836,8 @@
LMPM_SECURE_CPU2_HDR_MEM_SPACE);
/* load to FW the binary sections of CPU2 */
- if (image->is_secure)
- ret = iwl_pcie_load_cpu_secured_sections(
- trans, image, 2,
- &first_ucode_section);
- else
- ret = iwl_pcie_load_cpu_sections(trans, image, 2,
- &first_ucode_section);
+ ret = iwl_pcie_load_cpu_sections(trans, image, 2,
+ &first_ucode_section);
if (ret)
return ret;
}
@@ -811,6 +854,8 @@
(trans_pcie->fw_mon_phys +
trans_pcie->fw_mon_size) >> 4);
}
+ } else if (trans->dbg_dest_tlv) {
+ iwl_pcie_apply_destination(trans);
}
/* release CPU reset */
@@ -819,18 +864,50 @@
else
iwl_write32(trans, CSR_RESET, 0);
- if (image->is_secure) {
- /* wait for image verification to complete */
- ret = iwl_poll_prph_bit(trans,
- LMPM_SECURE_BOOT_CPU1_STATUS_ADDR,
- LMPM_SECURE_BOOT_STATUS_SUCCESS,
- LMPM_SECURE_BOOT_STATUS_SUCCESS,
- LMPM_SECURE_TIME_OUT);
+ return 0;
+}
- if (ret < 0) {
- IWL_ERR(trans, "Time out on secure boot process\n");
- return ret;
- }
+static int iwl_pcie_load_given_ucode_8000b(struct iwl_trans *trans,
+ const struct fw_img *image)
+{
+ int ret = 0;
+ int first_ucode_section;
+ u32 reg;
+
+ IWL_DEBUG_FW(trans, "working with %s CPU\n",
+ image->is_dual_cpus ? "Dual" : "Single");
+
+ /* configure the ucode to be ready to get the secured image */
+ /* release CPU reset */
+ iwl_write_prph(trans, RELEASE_CPU_RESET, RELEASE_CPU_RESET_BIT);
+
+ /* load to FW the binary Secured sections of CPU1 */
+ ret = iwl_pcie_load_cpu_sections_8000b(trans, image, 1,
+ &first_ucode_section);
+ if (ret)
+ return ret;
+
+ /* load to FW the binary sections of CPU2 */
+ ret = iwl_pcie_load_cpu_sections_8000b(trans, image, 2,
+ &first_ucode_section);
+ if (ret)
+ return ret;
+
+ /* Notify FW loading is done */
+ iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, 0xFFFFFFFF);
+
+ /* wait for image verification to complete */
+ ret = iwl_poll_prph_bit(trans, LMPM_SECURE_BOOT_CPU1_STATUS_ADDR_B0,
+ LMPM_SECURE_BOOT_STATUS_SUCCESS,
+ LMPM_SECURE_BOOT_STATUS_SUCCESS,
+ LMPM_SECURE_TIME_OUT);
+ if (ret < 0) {
+ reg = iwl_read_prph(trans,
+ LMPM_SECURE_BOOT_CPU1_STATUS_ADDR_B0);
+
+ IWL_ERR(trans, "Timeout on secure boot process, reg = %x\n",
+ reg);
+ return ret;
}
return 0;
@@ -882,7 +959,11 @@
iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL);
/* Load the given image to the HW */
- return iwl_pcie_load_given_ucode(trans, fw);
+ if ((trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) &&
+ (CSR_HW_REV_STEP(trans->hw_rev) == SILICON_B_STEP))
+ return iwl_pcie_load_given_ucode_8000b(trans, fw);
+ else
+ return iwl_pcie_load_given_ucode(trans, fw);
}
static void iwl_trans_pcie_fw_alive(struct iwl_trans *trans, u32 scd_addr)
@@ -913,7 +994,8 @@
* restart. So don't process again if the device is
* already dead.
*/
- if (test_bit(STATUS_DEVICE_ENABLED, &trans->status)) {
+ if (test_and_clear_bit(STATUS_DEVICE_ENABLED, &trans->status)) {
+ IWL_DEBUG_INFO(trans, "DEVICE_ENABLED bit was set and is now cleared\n");
iwl_pcie_tx_stop(trans);
iwl_pcie_rx_stop(trans);
@@ -938,12 +1020,12 @@
spin_unlock(&trans_pcie->irq_lock);
/* stop and reset the on-board processor */
- iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET);
+ iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
+ udelay(20);
/* clear all status bits */
clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
clear_bit(STATUS_INT_ENABLED, &trans->status);
- clear_bit(STATUS_DEVICE_ENABLED, &trans->status);
clear_bit(STATUS_TPOWER_PMI, &trans->status);
clear_bit(STATUS_RFKILL, &trans->status);
@@ -972,6 +1054,9 @@
clear_bit(STATUS_RFKILL, &trans->status);
if (hw_rfkill != was_hw_rfkill)
iwl_trans_pcie_rf_kill(trans, hw_rfkill);
+
+ /* re-take ownership to prevent other users from stealing the deivce */
+ iwl_pcie_prepare_card_hw(trans);
}
void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state)
@@ -1021,14 +1106,6 @@
return 0;
}
- iwl_pcie_set_pwr(trans, false);
-
- val = iwl_read32(trans, CSR_RESET);
- if (val & CSR_RESET_REG_FLAG_NEVO_RESET) {
- *status = IWL_D3_STATUS_RESET;
- return 0;
- }
-
/*
* Also enables interrupts - none will happen as the device doesn't
* know we're waking it up, only when the opmode actually tells it
@@ -1039,15 +1116,20 @@
iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
+ if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+ udelay(2);
+
ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY,
25000);
- if (ret) {
+ if (ret < 0) {
IWL_ERR(trans, "Failed to resume the device (mac ready)\n");
return ret;
}
+ iwl_pcie_set_pwr(trans, false);
+
iwl_trans_pcie_tx_reset(trans);
ret = iwl_pcie_rx_init(trans);
@@ -1056,7 +1138,12 @@
return ret;
}
- *status = IWL_D3_STATUS_ALIVE;
+ val = iwl_read32(trans, CSR_RESET);
+ if (val & CSR_RESET_REG_FLAG_NEVO_RESET)
+ *status = IWL_D3_STATUS_RESET;
+ else
+ *status = IWL_D3_STATUS_ALIVE;
+
return 0;
}
@@ -1234,6 +1321,8 @@
/* this bit wakes up the NIC */
__iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL,
CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+ if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+ udelay(2);
/*
* These bits say the device is running, and should keep running for
@@ -1765,6 +1854,13 @@
IWL_ERR(trans, "failed to create the trans debugfs entry\n");
return -ENOMEM;
}
+#else
+static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans,
+ struct dentry *dir)
+{
+ return 0;
+}
+#endif /*CPTCFG_IWLWIFI_DEBUGFS */
static u32 iwl_trans_pcie_get_cmdlen(struct iwl_tfd *tfd)
{
@@ -1892,8 +1988,7 @@
int reg;
__le32 *val;
- prph_len += sizeof(*data) + sizeof(*prph) +
- num_bytes_in_chunk;
+ prph_len += sizeof(**data) + sizeof(*prph) + num_bytes_in_chunk;
(*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH);
(*data)->len = cpu_to_le32(sizeof(*prph) +
@@ -1936,6 +2031,31 @@
return csr_len;
}
+static u32 iwl_trans_pcie_fh_regs_dump(struct iwl_trans *trans,
+ struct iwl_fw_error_dump_data **data)
+{
+ u32 fh_regs_len = FH_MEM_UPPER_BOUND - FH_MEM_LOWER_BOUND;
+ unsigned long flags;
+ __le32 *val;
+ int i;
+
+ if (!iwl_trans_grab_nic_access(trans, false, &flags))
+ return 0;
+
+ (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FH_REGS);
+ (*data)->len = cpu_to_le32(fh_regs_len);
+ val = (void *)(*data)->data;
+
+ for (i = FH_MEM_LOWER_BOUND; i < FH_MEM_UPPER_BOUND; i += sizeof(u32))
+ *val++ = cpu_to_le32(iwl_trans_pcie_read32(trans, i));
+
+ iwl_trans_release_nic_access(trans, &flags);
+
+ *data = iwl_fw_error_next_data(*data);
+
+ return sizeof(**data) + fh_regs_len;
+}
+
static
struct iwl_trans_dump_data *iwl_trans_pcie_dump_data(struct iwl_trans *trans)
{
@@ -1945,6 +2065,7 @@
struct iwl_fw_error_dump_txcmd *txcmd;
struct iwl_trans_dump_data *dump_data;
u32 len;
+ u32 monitor_len;
int i, ptr;
/* transport dump header */
@@ -1967,10 +2088,34 @@
num_bytes_in_chunk;
}
+ /* FH registers */
+ len += sizeof(*data) + (FH_MEM_UPPER_BOUND - FH_MEM_LOWER_BOUND);
+
/* FW monitor */
- if (trans_pcie->fw_mon_page)
+ if (trans_pcie->fw_mon_page) {
len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_fw_mon) +
- trans_pcie->fw_mon_size;
+ trans_pcie->fw_mon_size;
+ monitor_len = trans_pcie->fw_mon_size;
+ } else if (trans->dbg_dest_tlv) {
+ u32 base, end;
+
+ base = le32_to_cpu(trans->dbg_dest_tlv->base_reg);
+ end = le32_to_cpu(trans->dbg_dest_tlv->end_reg);
+
+ base = iwl_read_prph(trans, base) <<
+ trans->dbg_dest_tlv->base_shift;
+ end = iwl_read_prph(trans, end) <<
+ trans->dbg_dest_tlv->end_shift;
+
+ /* Make "end" point to the actual end */
+ if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+ end += (1 << trans->dbg_dest_tlv->end_shift);
+ monitor_len = end - base;
+ len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_fw_mon) +
+ monitor_len;
+ } else {
+ monitor_len = 0;
+ }
dump_data = vzalloc(len);
if (!dump_data)
@@ -2007,49 +2152,77 @@
len += iwl_trans_pcie_dump_prph(trans, &data);
len += iwl_trans_pcie_dump_csr(trans, &data);
+ len += iwl_trans_pcie_fh_regs_dump(trans, &data);
/* data is already pointing to the next section */
- if (trans_pcie->fw_mon_page) {
+ if ((trans_pcie->fw_mon_page &&
+ trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) ||
+ trans->dbg_dest_tlv) {
struct iwl_fw_error_dump_fw_mon *fw_mon_data;
+ u32 base, write_ptr, wrap_cnt;
+
+ /* If there was a dest TLV - use the values from there */
+ if (trans->dbg_dest_tlv) {
+ write_ptr =
+ le32_to_cpu(trans->dbg_dest_tlv->write_ptr_reg);
+ wrap_cnt = le32_to_cpu(trans->dbg_dest_tlv->wrap_count);
+ base = le32_to_cpu(trans->dbg_dest_tlv->base_reg);
+ } else {
+ base = MON_BUFF_BASE_ADDR;
+ write_ptr = MON_BUFF_WRPTR;
+ wrap_cnt = MON_BUFF_CYCLE_CNT;
+ }
data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FW_MONITOR);
- data->len = cpu_to_le32(trans_pcie->fw_mon_size +
- sizeof(*fw_mon_data));
fw_mon_data = (void *)data->data;
fw_mon_data->fw_mon_wr_ptr =
- cpu_to_le32(iwl_read_prph(trans, MON_BUFF_WRPTR));
+ cpu_to_le32(iwl_read_prph(trans, write_ptr));
fw_mon_data->fw_mon_cycle_cnt =
- cpu_to_le32(iwl_read_prph(trans, MON_BUFF_CYCLE_CNT));
+ cpu_to_le32(iwl_read_prph(trans, wrap_cnt));
fw_mon_data->fw_mon_base_ptr =
- cpu_to_le32(iwl_read_prph(trans, MON_BUFF_BASE_ADDR));
+ cpu_to_le32(iwl_read_prph(trans, base));
- /*
- * The firmware is now asserted, it won't write anything to
- * the buffer. CPU can take ownership to fetch the data.
- * The buffer will be handed back to the device before the
- * firmware will be restarted.
- */
- dma_sync_single_for_cpu(trans->dev, trans_pcie->fw_mon_phys,
- trans_pcie->fw_mon_size,
- DMA_FROM_DEVICE);
- memcpy(fw_mon_data->data, page_address(trans_pcie->fw_mon_page),
- trans_pcie->fw_mon_size);
+ len += sizeof(*data) + sizeof(*fw_mon_data);
+ if (trans_pcie->fw_mon_page) {
+ data->len = cpu_to_le32(trans_pcie->fw_mon_size +
+ sizeof(*fw_mon_data));
- len += sizeof(*data) + sizeof(*fw_mon_data) +
- trans_pcie->fw_mon_size;
+ /*
+ * The firmware is now asserted, it won't write anything
+ * to the buffer. CPU can take ownership to fetch the
+ * data. The buffer will be handed back to the device
+ * before the firmware will be restarted.
+ */
+ dma_sync_single_for_cpu(trans->dev,
+ trans_pcie->fw_mon_phys,
+ trans_pcie->fw_mon_size,
+ DMA_FROM_DEVICE);
+ memcpy(fw_mon_data->data,
+ page_address(trans_pcie->fw_mon_page),
+ trans_pcie->fw_mon_size);
+
+ len += trans_pcie->fw_mon_size;
+ } else {
+ /* If we are here then the buffer is internal */
+
+ /*
+ * Update pointers to reflect actual values after
+ * shifting
+ */
+ base = iwl_read_prph(trans, base) <<
+ trans->dbg_dest_tlv->base_shift;
+ iwl_trans_read_mem(trans, base, fw_mon_data->data,
+ monitor_len / sizeof(u32));
+ data->len = cpu_to_le32(sizeof(*fw_mon_data) +
+ monitor_len);
+ len += monitor_len;
+ }
}
dump_data->len = len;
return dump_data;
}
-#else
-static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans,
- struct dentry *dir)
-{
- return 0;
-}
-#endif /*CPTCFG_IWLWIFI_DEBUGFS */
static const struct iwl_trans_ops trans_ops_pcie = {
.start_hw = iwl_trans_pcie_start_hw,
@@ -2086,9 +2259,7 @@
.release_nic_access = iwl_trans_pcie_release_nic_access,
.set_bits_mask = iwl_trans_pcie_set_bits_mask,
-#ifdef CPTCFG_IWLWIFI_DEBUGFS
.dump_data = iwl_trans_pcie_dump_data,
-#endif
};
struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c
index eb8e298..8a6c7a0 100644
--- a/drivers/net/wireless/iwlwifi/pcie/tx.c
+++ b/drivers/net/wireless/iwlwifi/pcie/tx.c
@@ -989,6 +989,65 @@
spin_unlock_bh(&txq->lock);
}
+static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ int ret;
+
+ lockdep_assert_held(&trans_pcie->reg_lock);
+
+ if (trans_pcie->cmd_in_flight)
+ return 0;
+
+ trans_pcie->cmd_in_flight = true;
+
+ /*
+ * wake up the NIC to make sure that the firmware will see the host
+ * command - we will let the NIC sleep once all the host commands
+ * returned. This needs to be done only on NICs that have
+ * apmg_wake_up_wa set.
+ */
+ if (trans->cfg->base_params->apmg_wake_up_wa) {
+ __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+ if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+ udelay(2);
+
+ ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN,
+ (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
+ CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP),
+ 15000);
+ if (ret < 0) {
+ __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+ trans_pcie->cmd_in_flight = false;
+ IWL_ERR(trans, "Failed to wake NIC for hcmd\n");
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+static int iwl_pcie_clear_cmd_in_flight(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ lockdep_assert_held(&trans_pcie->reg_lock);
+
+ if (WARN_ON(!trans_pcie->cmd_in_flight))
+ return 0;
+
+ trans_pcie->cmd_in_flight = false;
+
+ if (trans->cfg->base_params->apmg_wake_up_wa)
+ __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
+ CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+
+ return 0;
+}
+
/*
* iwl_pcie_cmdq_reclaim - Reclaim TX command queue entries already Tx'd
*
@@ -1024,14 +1083,9 @@
}
}
- if (trans->cfg->base_params->apmg_wake_up_wa &&
- q->read_ptr == q->write_ptr) {
+ if (q->read_ptr == q->write_ptr) {
spin_lock_irqsave(&trans_pcie->reg_lock, flags);
- WARN_ON(!trans_pcie->cmd_in_flight);
- trans_pcie->cmd_in_flight = false;
- __iwl_trans_pcie_clear_bit(trans,
- CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+ iwl_pcie_clear_cmd_in_flight(trans);
spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
}
@@ -1419,32 +1473,11 @@
mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout);
spin_lock_irqsave(&trans_pcie->reg_lock, flags);
-
- /*
- * wake up the NIC to make sure that the firmware will see the host
- * command - we will let the NIC sleep once all the host commands
- * returned. This needs to be done only on NICs that have
- * apmg_wake_up_wa set.
- */
- if (trans->cfg->base_params->apmg_wake_up_wa &&
- !trans_pcie->cmd_in_flight) {
- trans_pcie->cmd_in_flight = true;
- __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
- ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN,
- (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
- CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP),
- 15000);
- if (ret < 0) {
- __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
- CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
- spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
- trans_pcie->cmd_in_flight = false;
- IWL_ERR(trans, "Failed to wake NIC for hcmd\n");
- idx = -EIO;
- goto out;
- }
+ ret = iwl_pcie_set_cmd_in_flight(trans);
+ if (ret < 0) {
+ idx = ret;
+ spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
+ goto out;
}
/* Increment and update queue's write index */
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index a3f2e06..c6755b8 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -395,7 +395,6 @@
static struct platform_driver mac80211_hwsim_driver = {
.driver = {
.name = "mac80211_hwsim",
- .owner = THIS_MODULE,
},
};
@@ -412,6 +411,11 @@
struct mac_address addresses[2];
int channels, idx;
bool use_chanctx;
+ bool destroy_on_close;
+ struct work_struct destroy_work;
+ u32 portid;
+ char alpha2[2];
+ const struct ieee80211_regdomain *regd;
struct ieee80211_channel *tmp_chan;
struct delayed_work roc_done;
@@ -419,6 +423,7 @@
struct cfg80211_scan_request *hw_scan_request;
struct ieee80211_vif *hw_scan_vif;
int scan_chan_idx;
+ u8 scan_addr[ETH_ALEN];
struct ieee80211_channel *channel;
u64 beacon_int /* beacon interval in us */;
@@ -436,7 +441,7 @@
/*
* Only radios in the same group can communicate together (the
* channel has to match too). Each bit represents a group. A
- * radio can be in more then one group.
+ * radio can be in more than one group.
*/
u64 group;
@@ -447,6 +452,14 @@
s64 bcn_delta;
/* absolute beacon transmission time. Used to cover up "tx" delay. */
u64 abs_bcn_ts;
+
+ /* Stats */
+ u64 tx_pkts;
+ u64 rx_pkts;
+ u64 tx_bytes;
+ u64 rx_bytes;
+ u64 tx_dropped;
+ u64 tx_failed;
};
@@ -476,6 +489,14 @@
.maxattr = HWSIM_ATTR_MAX,
};
+enum hwsim_multicast_groups {
+ HWSIM_MCGRP_CONFIG,
+};
+
+static __genl_const struct genl_multicast_group hwsim_mcgrps[] = {
+ [HWSIM_MCGRP_CONFIG] = { .name = "config", },
+};
+
/* MAC80211_HWSIM netlink policy */
static const struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
@@ -496,6 +517,10 @@
[HWSIM_ATTR_REG_CUSTOM_REG] = { .type = NLA_U32 },
[HWSIM_ATTR_REG_STRICT_REG] = { .type = NLA_FLAG },
[HWSIM_ATTR_SUPPORT_P2P_DEVICE] = { .type = NLA_FLAG },
+ [HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE] = { .type = NLA_FLAG },
+ [HWSIM_ATTR_RADIO_NAME] = { .type = NLA_STRING },
+ [HWSIM_ATTR_NO_VIF] = { .type = NLA_FLAG },
+ [HWSIM_ATTR_FREQ] = { .type = NLA_U32 },
};
static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
@@ -807,6 +832,9 @@
.ret = false,
};
+ if (data->scanning && memcmp(addr, data->scan_addr, ETH_ALEN) == 0)
+ return true;
+
memcpy(md.addr, addr, ETH_ALEN);
ieee80211_iterate_active_interfaces_atomic(data->hw,
@@ -861,8 +889,10 @@
/* If the queue contains MAX_QUEUE skb's drop some */
if (skb_queue_len(&data->pending) >= MAX_QUEUE) {
/* Droping until WARN_QUEUE level */
- while (skb_queue_len(&data->pending) >= WARN_QUEUE)
- skb_dequeue(&data->pending);
+ while (skb_queue_len(&data->pending) >= WARN_QUEUE) {
+ ieee80211_free_txskb(hw, skb_dequeue(&data->pending));
+ data->tx_dropped++;
+ }
}
skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_ATOMIC);
@@ -896,6 +926,9 @@
if (nla_put_u32(skb, HWSIM_ATTR_FLAGS, hwsim_flags))
goto nla_put_failure;
+ if (nla_put_u32(skb, HWSIM_ATTR_FREQ, data->channel->center_freq))
+ goto nla_put_failure;
+
/* We get the tx control (rate and retries) info*/
for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
@@ -917,10 +950,14 @@
/* Enqueue the packet */
skb_queue_tail(&data->pending, my_skb);
+ data->tx_pkts++;
+ data->tx_bytes += my_skb->len;
return;
nla_put_failure:
printk(KERN_DEBUG "mac80211_hwsim: error occurred in %s\n", __func__);
+ ieee80211_free_txskb(hw, my_skb);
+ data->tx_failed++;
}
static bool hwsim_chans_compat(struct ieee80211_channel *c1,
@@ -952,6 +989,53 @@
data->receive = true;
}
+static void mac80211_hwsim_add_vendor_rtap(struct sk_buff *skb)
+{
+ /*
+ * To enable this code, #define the HWSIM_RADIOTAP_OUI,
+ * e.g. like this:
+ * #define HWSIM_RADIOTAP_OUI "\x02\x00\x00"
+ * (but you should use a valid OUI, not that)
+ *
+ * If anyone wants to 'donate' a radiotap OUI/subns code
+ * please send a patch removing this #ifdef and changing
+ * the values accordingly.
+ */
+#ifdef HWSIM_RADIOTAP_OUI
+ struct ieee80211_vendor_radiotap *rtap;
+
+ /*
+ * Note that this code requires the headroom in the SKB
+ * that was allocated earlier.
+ */
+ rtap = (void *)skb_push(skb, sizeof(*rtap) + 8 + 4);
+ rtap->oui[0] = HWSIM_RADIOTAP_OUI[0];
+ rtap->oui[1] = HWSIM_RADIOTAP_OUI[1];
+ rtap->oui[2] = HWSIM_RADIOTAP_OUI[2];
+ rtap->subns = 127;
+
+ /*
+ * Radiotap vendor namespaces can (and should) also be
+ * split into fields by using the standard radiotap
+ * presence bitmap mechanism. Use just BIT(0) here for
+ * the presence bitmap.
+ */
+ rtap->present = BIT(0);
+ /* We have 8 bytes of (dummy) data */
+ rtap->len = 8;
+ /* For testing, also require it to be aligned */
+ rtap->align = 8;
+ /* And also test that padding works, 4 bytes */
+ rtap->pad = 4;
+ /* push the data */
+ memcpy(rtap->data, "ABCDEFGH", 8);
+ /* make sure to clear padding, mac80211 doesn't */
+ memset(rtap->data + 8, 0, 4);
+
+ IEEE80211_SKB_RXCB(skb)->flag |= RX_FLAG_RADIOTAP_VENDOR_DATA;
+#endif
+}
+
static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
struct sk_buff *skb,
struct ieee80211_channel *chan)
@@ -1066,6 +1150,11 @@
rx_status.mactime = now + data2->tsf_offset;
memcpy(IEEE80211_SKB_RXCB(nskb), &rx_status, sizeof(rx_status));
+
+ mac80211_hwsim_add_vendor_rtap(nskb);
+
+ data2->rx_pkts++;
+ data2->rx_bytes += nskb->len;
ieee80211_rx_irqsafe(data2->hw, nskb);
}
spin_unlock(&hwsim_radio_lock);
@@ -1133,6 +1222,8 @@
return mac80211_hwsim_tx_frame_nl(hw, skb, _portid);
/* NO wmediumd detected, perfect medium simulation */
+ data->tx_pkts++;
+ data->tx_bytes += skb->len;
ack = mac80211_hwsim_tx_frame_no_nl(hw, skb, channel);
if (ack && skb->len >= 16) {
@@ -1716,7 +1807,7 @@
struct sk_buff *probe;
probe = ieee80211_probereq_get(hwsim->hw,
- hwsim->hw_scan_vif,
+ hwsim->scan_addr,
req->ssids[i].ssid,
req->ssids[i].ssid_len,
req->ie_len);
@@ -1754,6 +1845,12 @@
hwsim->hw_scan_request = req;
hwsim->hw_scan_vif = vif;
hwsim->scan_chan_idx = 0;
+ if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)
+ get_random_mask_addr(hwsim->scan_addr,
+ hw_req->req.mac_addr,
+ hw_req->req.mac_addr_mask);
+ else
+ memcpy(hwsim->scan_addr, vif->addr, ETH_ALEN);
mutex_unlock(&hwsim->mutex);
wiphy_debug(hw->wiphy, "hwsim hw_scan request\n");
@@ -1780,7 +1877,9 @@
mutex_unlock(&hwsim->mutex);
}
-static void mac80211_hwsim_sw_scan(struct ieee80211_hw *hw)
+static void mac80211_hwsim_sw_scan(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ const u8 *mac_addr)
{
struct mac80211_hwsim_data *hwsim = hw->priv;
@@ -1792,13 +1891,16 @@
}
printk(KERN_DEBUG "hwsim sw_scan request, prepping stuff\n");
+
+ memcpy(hwsim->scan_addr, mac_addr, ETH_ALEN);
hwsim->scanning = true;
out:
mutex_unlock(&hwsim->mutex);
}
-static void mac80211_hwsim_sw_scan_complete(struct ieee80211_hw *hw)
+static void mac80211_hwsim_sw_scan_complete(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
{
struct mac80211_hwsim_data *hwsim = hw->priv;
@@ -1806,6 +1908,7 @@
printk(KERN_DEBUG "hwsim sw_scan_complete\n");
hwsim->scanning = false;
+ memset(hwsim->scan_addr, 0, ETH_ALEN);
mutex_unlock(&hwsim->mutex);
}
@@ -1916,6 +2019,57 @@
hwsim_check_chanctx_magic(ctx);
}
+static const char mac80211_hwsim_gstrings_stats[][ETH_GSTRING_LEN] = {
+ "tx_pkts_nic",
+ "tx_bytes_nic",
+ "rx_pkts_nic",
+ "rx_bytes_nic",
+ "d_tx_dropped",
+ "d_tx_failed",
+ "d_ps_mode",
+ "d_group",
+ "d_tx_power",
+};
+
+#define MAC80211_HWSIM_SSTATS_LEN ARRAY_SIZE(mac80211_hwsim_gstrings_stats)
+
+static void mac80211_hwsim_get_et_strings(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ u32 sset, u8 *data)
+{
+ if (sset == ETH_SS_STATS)
+ memcpy(data, *mac80211_hwsim_gstrings_stats,
+ sizeof(mac80211_hwsim_gstrings_stats));
+}
+
+static int mac80211_hwsim_get_et_sset_count(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif, int sset)
+{
+ if (sset == ETH_SS_STATS)
+ return MAC80211_HWSIM_SSTATS_LEN;
+ return 0;
+}
+
+static void mac80211_hwsim_get_et_stats(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ethtool_stats *stats, u64 *data)
+{
+ struct mac80211_hwsim_data *ar = hw->priv;
+ int i = 0;
+
+ data[i++] = ar->tx_pkts;
+ data[i++] = ar->tx_bytes;
+ data[i++] = ar->rx_pkts;
+ data[i++] = ar->rx_bytes;
+ data[i++] = ar->tx_dropped;
+ data[i++] = ar->tx_failed;
+ data[i++] = ar->ps;
+ data[i++] = ar->group;
+ data[i++] = ar->power_level;
+
+ WARN_ON(i != MAC80211_HWSIM_SSTATS_LEN);
+}
+
static const struct ieee80211_ops mac80211_hwsim_ops = {
.tx = mac80211_hwsim_tx,
.start = mac80211_hwsim_start,
@@ -1939,14 +2093,131 @@
.flush = mac80211_hwsim_flush,
.get_tsf = mac80211_hwsim_get_tsf,
.set_tsf = mac80211_hwsim_set_tsf,
+ .get_et_sset_count = mac80211_hwsim_get_et_sset_count,
+ .get_et_stats = mac80211_hwsim_get_et_stats,
+ .get_et_strings = mac80211_hwsim_get_et_strings,
};
static struct ieee80211_ops mac80211_hwsim_mchan_ops;
-static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
- const struct ieee80211_regdomain *regd,
- bool reg_strict, bool p2p_device,
- bool use_chanctx)
+struct hwsim_new_radio_params {
+ unsigned int channels;
+ const char *reg_alpha2;
+ const struct ieee80211_regdomain *regd;
+ bool reg_strict;
+ bool p2p_device;
+ bool use_chanctx;
+ bool destroy_on_close;
+ const char *hwname;
+ bool no_vif;
+};
+
+static void hwsim_mcast_config_msg(struct sk_buff *mcast_skb,
+ struct genl_info *info)
+{
+ if (info)
+ genl_notify(&hwsim_genl_family, mcast_skb,
+ genl_info_net(info), genl_info_snd_portid(info),
+ HWSIM_MCGRP_CONFIG, info->nlhdr, GFP_KERNEL);
+ else
+ genlmsg_multicast(&hwsim_genl_family, mcast_skb, 0,
+ HWSIM_MCGRP_CONFIG, GFP_KERNEL);
+}
+
+static int append_radio_msg(struct sk_buff *skb, int id,
+ struct hwsim_new_radio_params *param)
+{
+ int ret;
+
+ ret = nla_put_u32(skb, HWSIM_ATTR_RADIO_ID, id);
+ if (ret < 0)
+ return ret;
+
+ if (param->channels) {
+ ret = nla_put_u32(skb, HWSIM_ATTR_CHANNELS, param->channels);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (param->reg_alpha2) {
+ ret = nla_put(skb, HWSIM_ATTR_REG_HINT_ALPHA2, 2,
+ param->reg_alpha2);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (param->regd) {
+ int i;
+
+ for (i = 0; hwsim_world_regdom_custom[i] != param->regd &&
+ i < ARRAY_SIZE(hwsim_world_regdom_custom); i++)
+ ;
+
+ if (i < ARRAY_SIZE(hwsim_world_regdom_custom)) {
+ ret = nla_put_u32(skb, HWSIM_ATTR_REG_CUSTOM_REG, i);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ if (param->reg_strict) {
+ ret = nla_put_flag(skb, HWSIM_ATTR_REG_STRICT_REG);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (param->p2p_device) {
+ ret = nla_put_flag(skb, HWSIM_ATTR_SUPPORT_P2P_DEVICE);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (param->use_chanctx) {
+ ret = nla_put_flag(skb, HWSIM_ATTR_USE_CHANCTX);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (param->hwname) {
+ ret = nla_put(skb, HWSIM_ATTR_RADIO_NAME,
+ strlen(param->hwname), param->hwname);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void hwsim_mcast_new_radio(int id, struct genl_info *info,
+ struct hwsim_new_radio_params *param)
+{
+ struct sk_buff *mcast_skb;
+ void *data;
+
+ mcast_skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!mcast_skb)
+ return;
+
+ data = genlmsg_put(mcast_skb, 0, 0, &hwsim_genl_family, 0,
+ HWSIM_CMD_NEW_RADIO);
+ if (!data)
+ goto out_err;
+
+ if (append_radio_msg(mcast_skb, id, param) < 0)
+ goto out_err;
+
+ genlmsg_end(mcast_skb, data);
+
+ hwsim_mcast_config_msg(mcast_skb, info);
+ return;
+
+out_err:
+ genlmsg_cancel(mcast_skb, data);
+ nlmsg_free(mcast_skb);
+}
+
+static int mac80211_hwsim_new_radio(struct genl_info *info,
+ struct hwsim_new_radio_params *param)
{
int err;
u8 addr[ETH_ALEN];
@@ -1956,16 +2227,16 @@
const struct ieee80211_ops *ops = &mac80211_hwsim_ops;
int idx;
- if (WARN_ON(channels > 1 && !use_chanctx))
+ if (WARN_ON(param->channels > 1 && !param->use_chanctx))
return -EINVAL;
spin_lock_bh(&hwsim_radio_lock);
idx = hwsim_radio_idx++;
spin_unlock_bh(&hwsim_radio_lock);
- if (use_chanctx)
+ if (param->use_chanctx)
ops = &mac80211_hwsim_mchan_ops;
- hw = ieee80211_alloc_hw(sizeof(*data), ops);
+ hw = ieee80211_alloc_hw_nm(sizeof(*data), ops, param->hwname);
if (!hw) {
printk(KERN_DEBUG "mac80211_hwsim: ieee80211_alloc_hw failed\n");
err = -ENOMEM;
@@ -1987,7 +2258,7 @@
if (err != 0) {
printk(KERN_DEBUG "mac80211_hwsim: device_bind_driver failed (%d)\n",
err);
- goto failed_hw;
+ goto failed_bind;
}
skb_queue_head_init(&data->pending);
@@ -2003,9 +2274,12 @@
hw->wiphy->n_addresses = 2;
hw->wiphy->addresses = data->addresses;
- data->channels = channels;
- data->use_chanctx = use_chanctx;
+ data->channels = param->channels;
+ data->use_chanctx = param->use_chanctx;
data->idx = idx;
+ data->destroy_on_close = param->destroy_on_close;
+ if (info)
+ data->portid = genl_info_snd_portid(info);
if (data->use_chanctx) {
hw->wiphy->max_scan_ssids = 255;
@@ -2014,12 +2288,12 @@
/* For channels > 1 DFS is not allowed */
hw->wiphy->n_iface_combinations = 1;
hw->wiphy->iface_combinations = &data->if_combination;
- if (p2p_device)
+ if (param->p2p_device)
data->if_combination = hwsim_if_comb_p2p_dev[0];
else
data->if_combination = hwsim_if_comb[0];
data->if_combination.num_different_channels = data->channels;
- } else if (p2p_device) {
+ } else if (param->p2p_device) {
hw->wiphy->iface_combinations = hwsim_if_comb_p2p_dev;
hw->wiphy->n_iface_combinations =
ARRAY_SIZE(hwsim_if_comb_p2p_dev);
@@ -2040,7 +2314,7 @@
BIT(NL80211_IFTYPE_ADHOC) |
BIT(NL80211_IFTYPE_MESH_POINT);
- if (p2p_device)
+ if (param->p2p_device)
hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_DEVICE);
hw->flags = IEEE80211_HW_MFP_CAPABLE |
@@ -2060,7 +2334,8 @@
hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR |
NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE |
NL80211_FEATURE_STATIC_SMPS |
- NL80211_FEATURE_DYNAMIC_SMPS;
+ NL80211_FEATURE_DYNAMIC_SMPS |
+ NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
/* ask mac80211 to reserve space for magic */
hw->vif_data_size = sizeof(struct hwsim_vif_priv);
@@ -2095,6 +2370,7 @@
sband->ht_cap.ht_supported = true;
sband->ht_cap.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
IEEE80211_HT_CAP_GRN_FLD |
+ IEEE80211_HT_CAP_SGI_20 |
IEEE80211_HT_CAP_SGI_40 |
IEEE80211_HT_CAP_DSSSCCK40;
sband->ht_cap.ampdu_factor = 0x3;
@@ -2111,7 +2387,6 @@
sband->vht_cap.cap =
IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 |
IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ |
- IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ |
IEEE80211_VHT_CAP_RXLDPC |
IEEE80211_VHT_CAP_SHORT_GI_80 |
IEEE80211_VHT_CAP_SHORT_GI_160 |
@@ -2142,15 +2417,19 @@
hw->max_rates = 4;
hw->max_rate_tries = 11;
- if (reg_strict)
+ if (param->reg_strict)
hw->wiphy->regulatory_flags |= REGULATORY_STRICT_REG;
- if (regd) {
+ if (param->regd) {
+ data->regd = param->regd;
hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
- wiphy_apply_custom_regulatory(hw->wiphy, regd);
+ wiphy_apply_custom_regulatory(hw->wiphy, param->regd);
/* give the regulatory workqueue a chance to run */
schedule_timeout_interruptible(1);
}
+ if (param->no_vif)
+ hw->flags |= IEEE80211_HW_NO_AUTO_VIF;
+
err = ieee80211_register_hw(hw);
if (err < 0) {
printk(KERN_DEBUG "mac80211_hwsim: ieee80211_register_hw failed (%d)\n",
@@ -2160,8 +2439,11 @@
wiphy_debug(hw->wiphy, "hwaddr %pM registered\n", hw->wiphy->perm_addr);
- if (reg_alpha2)
- regulatory_hint(hw->wiphy, reg_alpha2);
+ if (param->reg_alpha2) {
+ data->alpha2[0] = param->reg_alpha2[0];
+ data->alpha2[1] = param->reg_alpha2[1];
+ regulatory_hint(hw->wiphy, param->reg_alpha2);
+ }
data->debugfs = debugfs_create_dir("hwsim", hw->wiphy->debugfsdir);
debugfs_create_file("ps", 0666, data->debugfs, data, &hwsim_fops_ps);
@@ -2180,9 +2462,14 @@
list_add_tail(&data->list, &hwsim_radios);
spin_unlock_bh(&hwsim_radio_lock);
+ if (idx > 0)
+ hwsim_mcast_new_radio(idx, info, param);
+
return idx;
failed_hw:
+ device_release_driver(data->dev);
+failed_bind:
device_unregister(data->dev);
failed_drvdata:
ieee80211_free_hw(hw);
@@ -2190,8 +2477,46 @@
return err;
}
-static void mac80211_hwsim_destroy_radio(struct mac80211_hwsim_data *data)
+static void hwsim_mcast_del_radio(int id, const char *hwname,
+ struct genl_info *info)
{
+ struct sk_buff *skb;
+ void *data;
+ int ret;
+
+ skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!skb)
+ return;
+
+ data = genlmsg_put(skb, 0, 0, &hwsim_genl_family, 0,
+ HWSIM_CMD_DEL_RADIO);
+ if (!data)
+ goto error;
+
+ ret = nla_put_u32(skb, HWSIM_ATTR_RADIO_ID, id);
+ if (ret < 0)
+ goto error;
+
+ ret = nla_put(skb, HWSIM_ATTR_RADIO_NAME, strlen(hwname),
+ hwname);
+ if (ret < 0)
+ goto error;
+
+ genlmsg_end(skb, data);
+
+ hwsim_mcast_config_msg(skb, info);
+
+ return;
+
+error:
+ nlmsg_free(skb);
+}
+
+static void mac80211_hwsim_del_radio(struct mac80211_hwsim_data *data,
+ const char *hwname,
+ struct genl_info *info)
+{
+ hwsim_mcast_del_radio(data->idx, hwname, info);
debugfs_remove_recursive(data->debugfs);
ieee80211_unregister_hw(data->hw);
device_release_driver(data->dev);
@@ -2199,6 +2524,46 @@
ieee80211_free_hw(data->hw);
}
+static int mac80211_hwsim_get_radio(struct sk_buff *skb,
+ struct mac80211_hwsim_data *data,
+ u32 portid, u32 seq,
+ struct netlink_callback *cb, int flags)
+{
+ void *hdr;
+ struct hwsim_new_radio_params param = { };
+ int res = -EMSGSIZE;
+
+ hdr = genlmsg_put(skb, portid, seq, &hwsim_genl_family, flags,
+ HWSIM_CMD_GET_RADIO);
+ if (!hdr)
+ return -EMSGSIZE;
+
+ if (cb)
+ genl_dump_check_consistent(cb, hdr, &hwsim_genl_family);
+
+ if (data->alpha2[0] && data->alpha2[1])
+ param.reg_alpha2 = data->alpha2;
+
+ param.reg_strict = !!(data->hw->wiphy->regulatory_flags &
+ REGULATORY_STRICT_REG);
+ param.p2p_device = !!(data->hw->wiphy->interface_modes &
+ BIT(NL80211_IFTYPE_P2P_DEVICE));
+ param.use_chanctx = data->use_chanctx;
+ param.regd = data->regd;
+ param.channels = data->channels;
+ param.hwname = wiphy_name(data->hw->wiphy);
+
+ res = append_radio_msg(skb, data->idx, ¶m);
+ if (res < 0)
+ goto out_err;
+
+ return genlmsg_end(skb, hdr);
+
+out_err:
+ genlmsg_cancel(skb, hdr);
+ return res;
+}
+
static void mac80211_hwsim_free(void)
{
struct mac80211_hwsim_data *data;
@@ -2209,7 +2574,8 @@
list))) {
list_del(&data->list);
spin_unlock_bh(&hwsim_radio_lock);
- mac80211_hwsim_destroy_radio(data);
+ mac80211_hwsim_del_radio(data, wiphy_name(data->hw->wiphy),
+ NULL);
spin_lock_bh(&hwsim_radio_lock);
}
spin_unlock_bh(&hwsim_radio_lock);
@@ -2337,7 +2703,6 @@
static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
struct genl_info *info)
{
-
struct mac80211_hwsim_data *data2;
struct ieee80211_rx_status rx_status;
const u8 *dst;
@@ -2380,18 +2745,22 @@
/* A frame is received from user space */
memset(&rx_status, 0, sizeof(rx_status));
+ /* TODO: Check ATTR_FREQ if it exists, and maybe throw away off-channel
+ * packets?
+ */
rx_status.freq = data2->channel->center_freq;
rx_status.band = data2->channel->band;
rx_status.rate_idx = nla_get_u32(info->attrs[HWSIM_ATTR_RX_RATE]);
rx_status.signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
+ data2->rx_pkts++;
+ data2->rx_bytes += skb->len;
ieee80211_rx_irqsafe(data2->hw, skb);
return 0;
err:
printk(KERN_DEBUG "mac80211_hwsim: error occurred in %s\n", __func__);
- goto out;
out:
dev_kfree_skb(skb);
return -EINVAL;
@@ -2428,42 +2797,84 @@
return 0;
}
-static int hwsim_create_radio_nl(struct sk_buff *msg, struct genl_info *info)
+static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
{
- unsigned int chans = channels;
- const char *alpha2 = NULL;
- const struct ieee80211_regdomain *regd = NULL;
- bool reg_strict = info->attrs[HWSIM_ATTR_REG_STRICT_REG];
- bool p2p_device = info->attrs[HWSIM_ATTR_SUPPORT_P2P_DEVICE];
- bool use_chanctx;
+ struct hwsim_new_radio_params param = { 0 };
+
+ param.reg_strict = info->attrs[HWSIM_ATTR_REG_STRICT_REG];
+ param.p2p_device = info->attrs[HWSIM_ATTR_SUPPORT_P2P_DEVICE];
+ param.channels = channels;
+ param.destroy_on_close =
+ info->attrs[HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE];
if (info->attrs[HWSIM_ATTR_CHANNELS])
- chans = nla_get_u32(info->attrs[HWSIM_ATTR_CHANNELS]);
+ param.channels = nla_get_u32(info->attrs[HWSIM_ATTR_CHANNELS]);
+
+ if (info->attrs[HWSIM_ATTR_NO_VIF])
+ param.no_vif = true;
+
+ if (info->attrs[HWSIM_ATTR_RADIO_NAME])
+ param.hwname = nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]);
if (info->attrs[HWSIM_ATTR_USE_CHANCTX])
- use_chanctx = true;
+ param.use_chanctx = true;
else
- use_chanctx = (chans > 1);
+ param.use_chanctx = (param.channels > 1);
if (info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2])
- alpha2 = nla_data(info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2]);
+ param.reg_alpha2 =
+ nla_data(info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2]);
if (info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]) {
u32 idx = nla_get_u32(info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]);
if (idx >= ARRAY_SIZE(hwsim_world_regdom_custom))
return -EINVAL;
- regd = hwsim_world_regdom_custom[idx];
+ param.regd = hwsim_world_regdom_custom[idx];
}
- return mac80211_hwsim_create_radio(chans, alpha2, regd, reg_strict,
- p2p_device, use_chanctx);
+ return mac80211_hwsim_new_radio(info, ¶m);
}
-static int hwsim_destroy_radio_nl(struct sk_buff *msg, struct genl_info *info)
+static int hwsim_del_radio_nl(struct sk_buff *msg, struct genl_info *info)
{
struct mac80211_hwsim_data *data;
- int idx;
+ s64 idx = -1;
+ const char *hwname = NULL;
+
+ if (info->attrs[HWSIM_ATTR_RADIO_ID])
+ idx = nla_get_u32(info->attrs[HWSIM_ATTR_RADIO_ID]);
+ else if (info->attrs[HWSIM_ATTR_RADIO_NAME])
+ hwname = (void *)nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]);
+ else
+ return -EINVAL;
+
+ spin_lock_bh(&hwsim_radio_lock);
+ list_for_each_entry(data, &hwsim_radios, list) {
+ if (idx >= 0) {
+ if (data->idx != idx)
+ continue;
+ } else {
+ if (strcmp(hwname, wiphy_name(data->hw->wiphy)))
+ continue;
+ }
+
+ list_del(&data->list);
+ spin_unlock_bh(&hwsim_radio_lock);
+ mac80211_hwsim_del_radio(data, wiphy_name(data->hw->wiphy),
+ info);
+ return 0;
+ }
+ spin_unlock_bh(&hwsim_radio_lock);
+
+ return -ENODEV;
+}
+
+static int hwsim_get_radio_nl(struct sk_buff *msg, struct genl_info *info)
+{
+ struct mac80211_hwsim_data *data;
+ struct sk_buff *skb;
+ int idx, res = -ENODEV;
if (!info->attrs[HWSIM_ATTR_RADIO_ID])
return -EINVAL;
@@ -2473,14 +2884,62 @@
list_for_each_entry(data, &hwsim_radios, list) {
if (data->idx != idx)
continue;
- list_del(&data->list);
- spin_unlock_bh(&hwsim_radio_lock);
- mac80211_hwsim_destroy_radio(data);
- return 0;
+
+ skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!skb) {
+ res = -ENOMEM;
+ goto out_err;
+ }
+
+ res = mac80211_hwsim_get_radio(skb, data,
+ genl_info_snd_portid(info),
+ info->snd_seq, NULL, 0);
+ if (res < 0) {
+ nlmsg_free(skb);
+ goto out_err;
+ }
+
+ genlmsg_reply(skb, info);
+ break;
}
+
+out_err:
spin_unlock_bh(&hwsim_radio_lock);
- return -ENODEV;
+ return res;
+}
+
+static int hwsim_dump_radio_nl(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ int idx = cb->args[0];
+ struct mac80211_hwsim_data *data = NULL;
+ int res;
+
+ spin_lock_bh(&hwsim_radio_lock);
+
+ if (idx == hwsim_radio_idx)
+ goto done;
+
+ list_for_each_entry(data, &hwsim_radios, list) {
+ if (data->idx < idx)
+ continue;
+
+ res = mac80211_hwsim_get_radio(skb, data,
+ NETLINK_CB_PORTID(cb->skb),
+ cb->nlh->nlmsg_seq, cb,
+ NLM_F_MULTI);
+ if (res < 0)
+ break;
+
+ idx = data->idx + 1;
+ }
+
+ cb->args[0] = idx;
+
+done:
+ spin_unlock_bh(&hwsim_radio_lock);
+ return skb->len;
}
/* Generic Netlink operations array */
@@ -2502,19 +2961,48 @@
.doit = hwsim_tx_info_frame_received_nl,
},
{
- .cmd = HWSIM_CMD_CREATE_RADIO,
+ .cmd = HWSIM_CMD_NEW_RADIO,
.policy = hwsim_genl_policy,
- .doit = hwsim_create_radio_nl,
+ .doit = hwsim_new_radio_nl,
.flags = GENL_ADMIN_PERM,
},
{
- .cmd = HWSIM_CMD_DESTROY_RADIO,
+ .cmd = HWSIM_CMD_DEL_RADIO,
.policy = hwsim_genl_policy,
- .doit = hwsim_destroy_radio_nl,
+ .doit = hwsim_del_radio_nl,
.flags = GENL_ADMIN_PERM,
},
+ {
+ .cmd = HWSIM_CMD_GET_RADIO,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_get_radio_nl,
+ .dumpit = hwsim_dump_radio_nl,
+ },
};
+static void destroy_radio(struct work_struct *work)
+{
+ struct mac80211_hwsim_data *data =
+ container_of(work, struct mac80211_hwsim_data, destroy_work);
+
+ mac80211_hwsim_del_radio(data, wiphy_name(data->hw->wiphy), NULL);
+}
+
+static void remove_user_radios(u32 portid)
+{
+ struct mac80211_hwsim_data *entry, *tmp;
+
+ spin_lock_bh(&hwsim_radio_lock);
+ list_for_each_entry_safe(entry, tmp, &hwsim_radios, list) {
+ if (entry->destroy_on_close && entry->portid == portid) {
+ list_del(&entry->list);
+ INIT_WORK(&entry->destroy_work, destroy_radio);
+ schedule_work(&entry->destroy_work);
+ }
+ }
+ spin_unlock_bh(&hwsim_radio_lock);
+}
+
static int mac80211_hwsim_netlink_notify(struct notifier_block *nb,
unsigned long state,
void *_notify)
@@ -2524,6 +3012,8 @@
if (state != NETLINK_URELEASE)
return NOTIFY_DONE;
+ remove_user_radios(netlink_notify_portid(notify));
+
if (netlink_notify_portid(notify) == wmediumd_portid) {
printk(KERN_INFO "mac80211_hwsim: wmediumd released netlink"
" socket, switching to perfect channel medium\n");
@@ -2543,7 +3033,9 @@
printk(KERN_INFO "mac80211_hwsim: initializing netlink\n");
- rc = genl_register_family_with_ops(&hwsim_genl_family, hwsim_ops);
+ rc = genl_register_family_with_ops_groups(&hwsim_genl_family,
+ hwsim_ops,
+ hwsim_mcgrps);
if (rc)
goto failure;
@@ -2604,69 +3096,73 @@
goto out_unregister_driver;
}
+ err = hwsim_init_netlink();
+ if (err < 0)
+ goto out_unregister_driver;
+
for (i = 0; i < radios; i++) {
- const char *reg_alpha2 = NULL;
- const struct ieee80211_regdomain *regd = NULL;
- bool reg_strict = false;
+ struct hwsim_new_radio_params param = { 0 };
+
+ param.channels = channels;
switch (regtest) {
case HWSIM_REGTEST_DIFF_COUNTRY:
if (i < ARRAY_SIZE(hwsim_alpha2s))
- reg_alpha2 = hwsim_alpha2s[i];
+ param.reg_alpha2 = hwsim_alpha2s[i];
break;
case HWSIM_REGTEST_DRIVER_REG_FOLLOW:
if (!i)
- reg_alpha2 = hwsim_alpha2s[0];
+ param.reg_alpha2 = hwsim_alpha2s[0];
break;
case HWSIM_REGTEST_STRICT_ALL:
- reg_strict = true;
+ param.reg_strict = true;
case HWSIM_REGTEST_DRIVER_REG_ALL:
- reg_alpha2 = hwsim_alpha2s[0];
+ param.reg_alpha2 = hwsim_alpha2s[0];
break;
case HWSIM_REGTEST_WORLD_ROAM:
if (i == 0)
- regd = &hwsim_world_regdom_custom_01;
+ param.regd = &hwsim_world_regdom_custom_01;
break;
case HWSIM_REGTEST_CUSTOM_WORLD:
- regd = &hwsim_world_regdom_custom_01;
+ param.regd = &hwsim_world_regdom_custom_01;
break;
case HWSIM_REGTEST_CUSTOM_WORLD_2:
if (i == 0)
- regd = &hwsim_world_regdom_custom_01;
+ param.regd = &hwsim_world_regdom_custom_01;
else if (i == 1)
- regd = &hwsim_world_regdom_custom_02;
+ param.regd = &hwsim_world_regdom_custom_02;
break;
case HWSIM_REGTEST_STRICT_FOLLOW:
if (i == 0) {
- reg_strict = true;
- reg_alpha2 = hwsim_alpha2s[0];
+ param.reg_strict = true;
+ param.reg_alpha2 = hwsim_alpha2s[0];
}
break;
case HWSIM_REGTEST_STRICT_AND_DRIVER_REG:
if (i == 0) {
- reg_strict = true;
- reg_alpha2 = hwsim_alpha2s[0];
+ param.reg_strict = true;
+ param.reg_alpha2 = hwsim_alpha2s[0];
} else if (i == 1) {
- reg_alpha2 = hwsim_alpha2s[1];
+ param.reg_alpha2 = hwsim_alpha2s[1];
}
break;
case HWSIM_REGTEST_ALL:
switch (i) {
case 0:
- regd = &hwsim_world_regdom_custom_01;
+ param.regd = &hwsim_world_regdom_custom_01;
break;
case 1:
- regd = &hwsim_world_regdom_custom_02;
+ param.regd = &hwsim_world_regdom_custom_02;
break;
case 2:
- reg_alpha2 = hwsim_alpha2s[0];
+ param.reg_alpha2 = hwsim_alpha2s[0];
break;
case 3:
- reg_alpha2 = hwsim_alpha2s[1];
+ param.reg_alpha2 = hwsim_alpha2s[1];
break;
case 4:
- reg_strict = true;
- reg_alpha2 = hwsim_alpha2s[2];
+ param.reg_strict = true;
+ param.reg_alpha2 = hwsim_alpha2s[2];
break;
}
break;
@@ -2674,10 +3170,10 @@
break;
}
- err = mac80211_hwsim_create_radio(channels, reg_alpha2,
- regd, reg_strict,
- support_p2p_device,
- channels > 1);
+ param.p2p_device = support_p2p_device;
+ param.use_chanctx = channels > 1;
+
+ err = mac80211_hwsim_new_radio(NULL, ¶m);
if (err < 0)
goto out_free_radios;
}
@@ -2703,10 +3199,6 @@
}
rtnl_unlock();
- err = hwsim_init_netlink();
- if (err < 0)
- goto out_free_mon;
-
return 0;
out_free_mon:
diff --git a/drivers/net/wireless/mac80211_hwsim.h b/drivers/net/wireless/mac80211_hwsim.h
index c9d0315..66e1c73 100644
--- a/drivers/net/wireless/mac80211_hwsim.h
+++ b/drivers/net/wireless/mac80211_hwsim.h
@@ -60,14 +60,17 @@
* space, uses:
* %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_ADDR_RECEIVER,
* %HWSIM_ATTR_FRAME, %HWSIM_ATTR_FLAGS, %HWSIM_ATTR_RX_RATE,
- * %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE
+ * %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE, %HWSIM_ATTR_FREQ (optional)
* @HWSIM_CMD_TX_INFO_FRAME: Transmission info report from user space to
* kernel, uses:
* %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_FLAGS,
* %HWSIM_ATTR_TX_INFO, %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE
- * @HWSIM_CMD_CREATE_RADIO: create a new radio with the given parameters,
- * returns the radio ID (>= 0) or negative on errors
- * @HWSIM_CMD_DESTROY_RADIO: destroy a radio
+ * @HWSIM_CMD_NEW_RADIO: create a new radio with the given parameters,
+ * returns the radio ID (>= 0) or negative on errors, if successful
+ * then multicast the result
+ * @HWSIM_CMD_DEL_RADIO: destroy a radio, reply is multicasted
+ * @HWSIM_CMD_GET_RADIO: fetch information about existing radios, uses:
+ * %HWSIM_ATTR_RADIO_ID
* @__HWSIM_CMD_MAX: enum limit
*/
enum {
@@ -75,12 +78,16 @@
HWSIM_CMD_REGISTER,
HWSIM_CMD_FRAME,
HWSIM_CMD_TX_INFO_FRAME,
- HWSIM_CMD_CREATE_RADIO,
- HWSIM_CMD_DESTROY_RADIO,
+ HWSIM_CMD_NEW_RADIO,
+ HWSIM_CMD_DEL_RADIO,
+ HWSIM_CMD_GET_RADIO,
__HWSIM_CMD_MAX,
};
#define HWSIM_CMD_MAX (_HWSIM_CMD_MAX - 1)
+#define HWSIM_CMD_CREATE_RADIO HWSIM_CMD_NEW_RADIO
+#define HWSIM_CMD_DESTROY_RADIO HWSIM_CMD_DEL_RADIO
+
/**
* enum hwsim_attrs - hwsim netlink attributes
*
@@ -111,6 +118,11 @@
* @HWSIM_ATTR_USE_CHANCTX: used with the %HWSIM_CMD_CREATE_RADIO
* command to force use of channel contexts even when only a
* single channel is supported
+ * @HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE: used with the %HWSIM_CMD_CREATE_RADIO
+ * command to force radio removal when process that created the radio dies
+ * @HWSIM_ATTR_RADIO_NAME: Name of radio, e.g. phy666
+ * @HWSIM_ATTR_NO_VIF: Do not create vif (wlanX) when creating radio.
+ * @HWSIM_ATTR_FREQ: Frequency at which packet is transmitted or received.
* @__HWSIM_ATTR_MAX: enum limit
*/
@@ -132,6 +144,10 @@
HWSIM_ATTR_REG_STRICT_REG,
HWSIM_ATTR_SUPPORT_P2P_DEVICE,
HWSIM_ATTR_USE_CHANCTX,
+ HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE,
+ HWSIM_ATTR_RADIO_NAME,
+ HWSIM_ATTR_NO_VIF,
+ HWSIM_ATTR_FREQ,
__HWSIM_ATTR_MAX,
};
#define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1)
diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/mwifiex/11n.c
index 62f5dbe..9d4786e 100644
--- a/drivers/net/wireless/mwifiex/11n.c
+++ b/drivers/net/wireless/mwifiex/11n.c
@@ -544,6 +544,7 @@
u32 tx_win_size = priv->add_ba_param.tx_win_size;
static u8 dialog_tok;
int ret;
+ unsigned long flags;
u16 block_ack_param_set;
dev_dbg(priv->adapter->dev, "cmd: %s: tid %d\n", __func__, tid);
@@ -554,15 +555,18 @@
memcmp(priv->cfg_bssid, peer_mac, ETH_ALEN)) {
struct mwifiex_sta_node *sta_ptr;
+ spin_lock_irqsave(&priv->sta_list_spinlock, flags);
sta_ptr = mwifiex_get_sta_entry(priv, peer_mac);
if (!sta_ptr) {
dev_warn(priv->adapter->dev,
"BA setup with unknown TDLS peer %pM!\n",
peer_mac);
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
return -1;
}
if (sta_ptr->is_11ac_enabled)
tx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE;
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
}
block_ack_param_set = (u16)((tid << BLOCKACKPARAM_TID_POS) |
diff --git a/drivers/net/wireless/mwifiex/11n.h b/drivers/net/wireless/mwifiex/11n.h
index 2ee268b..f275675 100644
--- a/drivers/net/wireless/mwifiex/11n.h
+++ b/drivers/net/wireless/mwifiex/11n.h
@@ -84,6 +84,8 @@
{
struct mwifiex_tx_ba_stream_tbl *tx_tbl;
+ if (is_broadcast_ether_addr(ptr->ra))
+ return false;
tx_tbl = mwifiex_get_ba_tbl(priv, tid, ptr->ra);
if (tx_tbl)
return tx_tbl->amsdu;
@@ -96,6 +98,8 @@
mwifiex_is_ampdu_allowed(struct mwifiex_private *priv,
struct mwifiex_ra_list_tbl *ptr, int tid)
{
+ if (is_broadcast_ether_addr(ptr->ra))
+ return false;
if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
return mwifiex_is_station_ampdu_allowed(priv, ptr, tid);
} else {
diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.c b/drivers/net/wireless/mwifiex/11n_rxreorder.c
index 4005707..d73fda3 100644
--- a/drivers/net/wireless/mwifiex/11n_rxreorder.c
+++ b/drivers/net/wireless/mwifiex/11n_rxreorder.c
@@ -196,6 +196,7 @@
mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win);
del_timer_sync(&tbl->timer_context.timer);
+ tbl->timer_context.timer_is_set = false;
spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
list_del(&tbl->list);
@@ -297,6 +298,7 @@
(struct reorder_tmr_cnxt *) context;
int start_win, seq_num;
+ ctx->timer_is_set = false;
seq_num = mwifiex_11n_find_last_seq_num(ctx);
if (seq_num < 0)
@@ -349,6 +351,7 @@
new_node->init_win = seq_num;
new_node->flags = 0;
+ spin_lock_irqsave(&priv->sta_list_spinlock, flags);
if (mwifiex_queuing_ra_based(priv)) {
dev_dbg(priv->adapter->dev,
"info: AP/ADHOC:last_seq=%d start_win=%d\n",
@@ -365,6 +368,7 @@
else
last_seq = priv->rx_seq[tid];
}
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
if (last_seq != MWIFIEX_DEF_11N_RX_SEQ_NUM &&
last_seq >= new_node->start_win) {
@@ -385,6 +389,7 @@
new_node->timer_context.ptr = new_node;
new_node->timer_context.priv = priv;
+ new_node->timer_context.timer_is_set = false;
init_timer(&new_node->timer_context.timer);
new_node->timer_context.timer.function = mwifiex_flush_data;
@@ -399,6 +404,22 @@
spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
}
+static void
+mwifiex_11n_rxreorder_timer_restart(struct mwifiex_rx_reorder_tbl *tbl)
+{
+ u32 min_flush_time;
+
+ if (tbl->win_size >= MWIFIEX_BA_WIN_SIZE_32)
+ min_flush_time = MIN_FLUSH_TIMER_15_MS;
+ else
+ min_flush_time = MIN_FLUSH_TIMER_MS;
+
+ mod_timer(&tbl->timer_context.timer,
+ jiffies + msecs_to_jiffies(min_flush_time * tbl->win_size));
+
+ tbl->timer_context.timer_is_set = true;
+}
+
/*
* This function prepares command for adding a BA request.
*
@@ -436,22 +457,26 @@
u32 rx_win_size = priv->add_ba_param.rx_win_size;
u8 tid;
int win_size;
+ unsigned long flags;
uint16_t block_ack_param_set;
if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
priv->adapter->is_hw_11ac_capable &&
memcmp(priv->cfg_bssid, cmd_addba_req->peer_mac_addr, ETH_ALEN)) {
+ spin_lock_irqsave(&priv->sta_list_spinlock, flags);
sta_ptr = mwifiex_get_sta_entry(priv,
cmd_addba_req->peer_mac_addr);
if (!sta_ptr) {
dev_warn(priv->adapter->dev,
"BA setup with unknown TDLS peer %pM!\n",
cmd_addba_req->peer_mac_addr);
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
return -1;
}
if (sta_ptr->is_11ac_enabled)
rx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE;
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
}
cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_RSP);
@@ -523,31 +548,31 @@
u8 *ta, u8 pkt_type, void *payload)
{
struct mwifiex_rx_reorder_tbl *tbl;
- int start_win, end_win, win_size;
+ int prev_start_win, start_win, end_win, win_size;
u16 pkt_index;
bool init_window_shift = false;
+ int ret = 0;
tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta);
if (!tbl) {
if (pkt_type != PKT_TYPE_BAR)
mwifiex_11n_dispatch_pkt(priv, payload);
- return 0;
+ return ret;
}
if ((pkt_type == PKT_TYPE_AMSDU) && !tbl->amsdu) {
mwifiex_11n_dispatch_pkt(priv, payload);
- return 0;
+ return ret;
}
start_win = tbl->start_win;
+ prev_start_win = start_win;
win_size = tbl->win_size;
end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1);
if (tbl->flags & RXREOR_INIT_WINDOW_SHIFT) {
init_window_shift = true;
tbl->flags &= ~RXREOR_INIT_WINDOW_SHIFT;
}
- mod_timer(&tbl->timer_context.timer,
- jiffies + msecs_to_jiffies(MIN_FLUSH_TIMER_MS * win_size));
if (tbl->flags & RXREOR_FORCE_NO_DROP) {
dev_dbg(priv->adapter->dev,
@@ -568,11 +593,14 @@
if ((start_win + TWOPOW11) > (MAX_TID_VALUE - 1)) {
if (seq_num >= ((start_win + TWOPOW11) &
(MAX_TID_VALUE - 1)) &&
- seq_num < start_win)
- return -1;
+ seq_num < start_win) {
+ ret = -1;
+ goto done;
+ }
} else if ((seq_num < start_win) ||
- (seq_num > (start_win + TWOPOW11))) {
- return -1;
+ (seq_num >= (start_win + TWOPOW11))) {
+ ret = -1;
+ goto done;
}
}
@@ -601,8 +629,10 @@
else
pkt_index = (seq_num+MAX_TID_VALUE) - start_win;
- if (tbl->rx_reorder_ptr[pkt_index])
- return -1;
+ if (tbl->rx_reorder_ptr[pkt_index]) {
+ ret = -1;
+ goto done;
+ }
tbl->rx_reorder_ptr[pkt_index] = payload;
}
@@ -613,7 +643,11 @@
*/
mwifiex_11n_scan_and_dispatch(priv, tbl);
- return 0;
+done:
+ if (!tbl->timer_context.timer_is_set ||
+ prev_start_win != tbl->start_win)
+ mwifiex_11n_rxreorder_timer_restart(tbl);
+ return ret;
}
/*
diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.h b/drivers/net/wireless/mwifiex/11n_rxreorder.h
index 3a87bb0..63ecea8 100644
--- a/drivers/net/wireless/mwifiex/11n_rxreorder.h
+++ b/drivers/net/wireless/mwifiex/11n_rxreorder.h
@@ -21,6 +21,8 @@
#define _MWIFIEX_11N_RXREORDER_H_
#define MIN_FLUSH_TIMER_MS 50
+#define MIN_FLUSH_TIMER_15_MS 15
+#define MWIFIEX_BA_WIN_SIZE_32 32
#define PKT_TYPE_BAR 0xE7
#define MAX_TID_VALUE (2 << 11)
diff --git a/drivers/net/wireless/mwifiex/Kconfig b/drivers/net/wireless/mwifiex/Kconfig
index 43e31bf..689cf90 100644
--- a/drivers/net/wireless/mwifiex/Kconfig
+++ b/drivers/net/wireless/mwifiex/Kconfig
@@ -34,7 +34,7 @@
mwifiex_pcie.
config MWIFIEX_USB
- tristate "Marvell WiFi-Ex Driver for USB8797/8897"
+ tristate "Marvell WiFi-Ex Driver for USB8766/8797/8897"
depends on m
depends on MWIFIEX && USB
depends on FW_LOADER
diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c
index 0dd6729..4a66a65 100644
--- a/drivers/net/wireless/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/mwifiex/cfg80211.c
@@ -194,10 +194,17 @@
tx_info->pkt_len = pkt_len;
mwifiex_form_mgmt_frame(skb, buf, len);
- mwifiex_queue_tx_pkt(priv, skb);
-
*cookie = prandom_u32() | 1;
- cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true, GFP_ATOMIC);
+
+ if (ieee80211_is_action(mgmt->frame_control))
+ skb = mwifiex_clone_skb_for_tx_status(priv,
+ skb,
+ MWIFIEX_BUF_FLAG_ACTION_TX_STATUS, cookie);
+ else
+ cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
+ GFP_ATOMIC);
+
+ mwifiex_queue_tx_pkt(priv, skb);
wiphy_dbg(wiphy, "info: management frame transmitted\n");
return 0;
@@ -992,6 +999,52 @@
return mwifiex_dump_station_info(priv, sinfo);
}
+static int
+mwifiex_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *dev,
+ int idx, struct survey_info *survey)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+ struct mwifiex_chan_stats *pchan_stats = priv->adapter->chan_stats;
+ enum ieee80211_band band;
+
+ dev_dbg(priv->adapter->dev, "dump_survey idx=%d\n", idx);
+
+ memset(survey, 0, sizeof(struct survey_info));
+
+ if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
+ priv->media_connected && idx == 0) {
+ u8 curr_bss_band = priv->curr_bss_params.band;
+ u32 chan = priv->curr_bss_params.bss_descriptor.channel;
+
+ band = mwifiex_band_to_radio_type(curr_bss_band);
+ survey->channel = ieee80211_get_channel(wiphy,
+ ieee80211_channel_to_frequency(chan, band));
+
+ if (priv->bcn_nf_last) {
+ survey->filled = SURVEY_INFO_NOISE_DBM;
+ survey->noise = priv->bcn_nf_last;
+ }
+ return 0;
+ }
+
+ if (idx >= priv->adapter->num_in_chan_stats)
+ return -ENOENT;
+
+ if (!pchan_stats[idx].cca_scan_dur)
+ return 0;
+
+ band = pchan_stats[idx].bandcfg;
+ survey->channel = ieee80211_get_channel(wiphy,
+ ieee80211_channel_to_frequency(pchan_stats[idx].chan_num, band));
+ survey->filled = SURVEY_INFO_NOISE_DBM |
+ SURVEY_INFO_CHANNEL_TIME | SURVEY_INFO_CHANNEL_TIME_BUSY;
+ survey->noise = pchan_stats[idx].noise;
+ survey->channel_time = pchan_stats[idx].cca_scan_dur;
+ survey->channel_time_busy = pchan_stats[idx].cca_busy_dur;
+
+ return 0;
+}
+
/* Supported rates to be advertised to the cfg80211 */
static struct ieee80211_rate mwifiex_rates[] = {
{.bitrate = 10, .hw_value = 2, },
@@ -1239,36 +1292,34 @@
*/
static int
mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev,
- const u8 *mac)
+ struct station_del_parameters *params)
{
struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
struct mwifiex_sta_node *sta_node;
+ u8 deauth_mac[ETH_ALEN];
unsigned long flags;
if (list_empty(&priv->sta_list) || !priv->bss_started)
return 0;
- if (!mac || is_broadcast_ether_addr(mac)) {
- wiphy_dbg(wiphy, "%s: NULL/broadcast mac address\n", __func__);
- list_for_each_entry(sta_node, &priv->sta_list, list) {
- if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_STA_DEAUTH,
- HostCmd_ACT_GEN_SET, 0,
- sta_node->mac_addr, true))
- return -1;
- mwifiex_uap_del_sta_data(priv, sta_node);
- }
- } else {
- wiphy_dbg(wiphy, "%s: mac address %pM\n", __func__, mac);
- spin_lock_irqsave(&priv->sta_list_spinlock, flags);
- sta_node = mwifiex_get_sta_entry(priv, mac);
- spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
- if (sta_node) {
- if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_STA_DEAUTH,
- HostCmd_ACT_GEN_SET, 0,
- sta_node->mac_addr, true))
- return -1;
- mwifiex_uap_del_sta_data(priv, sta_node);
- }
+ if (!params->mac || is_broadcast_ether_addr(params->mac))
+ return 0;
+
+ wiphy_dbg(wiphy, "%s: mac address %pM\n", __func__, params->mac);
+
+ memset(deauth_mac, 0, ETH_ALEN);
+
+ spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+ sta_node = mwifiex_get_sta_entry(priv, params->mac);
+ if (sta_node)
+ ether_addr_copy(deauth_mac, params->mac);
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
+
+ if (is_valid_ether_addr(deauth_mac)) {
+ if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_STA_DEAUTH,
+ HostCmd_ACT_GEN_SET, 0,
+ deauth_mac, true))
+ return -1;
}
return 0;
@@ -1759,6 +1810,10 @@
dev_dbg(priv->adapter->dev,
"info: associated to bssid %pM successfully\n",
priv->cfg_bssid);
+ if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
+ priv->adapter->auto_tdls &&
+ priv->bss_type == MWIFIEX_BSS_TYPE_STA)
+ mwifiex_setup_auto_tdls_timer(priv);
} else {
dev_dbg(priv->adapter->dev,
"info: association to bssid %pM failed\n",
@@ -2630,11 +2685,13 @@
dev_dbg(priv->adapter->dev,
"Send TDLS Setup Request to %pM status_code=%d\n", peer,
status_code);
+ mwifiex_add_auto_tdls_peer(priv, peer);
ret = mwifiex_send_tdls_data_frame(priv, peer, action_code,
dialog_token, status_code,
extra_ies, extra_ies_len);
break;
case WLAN_TDLS_SETUP_RESPONSE:
+ mwifiex_add_auto_tdls_peer(priv, peer);
dev_dbg(priv->adapter->dev,
"Send TDLS Setup Response to %pM status_code=%d\n",
peer, status_code);
@@ -2779,6 +2836,7 @@
.disconnect = mwifiex_cfg80211_disconnect,
.get_station = mwifiex_cfg80211_get_station,
.dump_station = mwifiex_cfg80211_dump_station,
+ .dump_survey = mwifiex_cfg80211_dump_survey,
.set_wiphy_params = mwifiex_cfg80211_set_wiphy_params,
.join_ibss = mwifiex_cfg80211_join_ibss,
.leave_ibss = mwifiex_cfg80211_leave_ibss,
@@ -2840,6 +2898,25 @@
.max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN,
};
+int mwifiex_init_channel_scan_gap(struct mwifiex_adapter *adapter)
+{
+ u32 n_channels_bg, n_channels_a = 0;
+
+ n_channels_bg = mwifiex_band_2ghz.n_channels;
+
+ if (adapter->config_bands & BAND_A)
+ n_channels_a = mwifiex_band_5ghz.n_channels;
+
+ adapter->num_in_chan_stats = max_t(u32, n_channels_bg, n_channels_a);
+ adapter->chan_stats = vmalloc(sizeof(*adapter->chan_stats) *
+ adapter->num_in_chan_stats);
+
+ if (!adapter->chan_stats)
+ return -ENOMEM;
+
+ return 0;
+}
+
/*
* This function registers the device with CFG802.11 subsystem.
*
@@ -2915,6 +2992,9 @@
NL80211_FEATURE_INACTIVITY_TIMER |
NL80211_FEATURE_NEED_OBSS_SCAN;
+ if (adapter->fw_api_ver == MWIFIEX_FW_V15)
+ wiphy->features |= NL80211_FEATURE_SK_TX_STATUS;
+
/* Reserve space for mwifiex specific private data for BSS */
wiphy->bss_priv_size = sizeof(struct mwifiex_bss_priv);
diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h
index 9c054d2..343240a 100644
--- a/drivers/net/wireless/mwifiex/decl.h
+++ b/drivers/net/wireless/mwifiex/decl.h
@@ -76,6 +76,8 @@
#define MWIFIEX_BUF_FLAG_REQUEUED_PKT BIT(0)
#define MWIFIEX_BUF_FLAG_BRIDGED_PKT BIT(1)
#define MWIFIEX_BUF_FLAG_TDLS_PKT BIT(2)
+#define MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS BIT(3)
+#define MWIFIEX_BUF_FLAG_ACTION_TX_STATUS BIT(4)
#define MWIFIEX_BRIDGED_PKTS_THR_HIGH 1024
#define MWIFIEX_BRIDGED_PKTS_THR_LOW 128
@@ -85,6 +87,11 @@
#define MWIFIEX_TDLS_CREATE_LINK 0x02
#define MWIFIEX_TDLS_CONFIG_LINK 0x03
+#define MWIFIEX_TDLS_RSSI_HIGH 50
+#define MWIFIEX_TDLS_RSSI_LOW 55
+#define MWIFIEX_TDLS_MAX_FAIL_COUNT 4
+#define MWIFIEX_AUTO_TDLS_IDLE_TIME 10
+
enum mwifiex_bss_type {
MWIFIEX_BSS_TYPE_STA = 0,
MWIFIEX_BSS_TYPE_UAP = 1,
@@ -154,6 +161,8 @@
u8 bss_num;
u8 bss_type;
u32 pkt_len;
+ u8 ack_frame_id;
+ u64 cookie;
};
enum mwifiex_wmm_ac_e {
@@ -185,4 +194,14 @@
u8 ar_tha[ETH_ALEN];
u8 ar_tip[4];
} __packed;
+
+struct mwifiex_chan_stats {
+ u8 chan_num;
+ u8 bandcfg;
+ u8 flags;
+ s8 noise;
+ u16 total_bss;
+ u16 cca_scan_dur;
+ u16 cca_busy_dur;
+} __packed;
#endif /* !_MWIFIEX_DECL_H_ */
diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h
index 1eb6173..fb5936e 100644
--- a/drivers/net/wireless/mwifiex/fw.h
+++ b/drivers/net/wireless/mwifiex/fw.h
@@ -172,6 +172,7 @@
#define TLV_TYPE_TDLS_IDLE_TIMEOUT (PROPRIETARY_TLV_BASE_ID + 194)
#define TLV_TYPE_SCAN_CHANNEL_GAP (PROPRIETARY_TLV_BASE_ID + 197)
#define TLV_TYPE_API_REV (PROPRIETARY_TLV_BASE_ID + 199)
+#define TLV_TYPE_CHANNEL_STATS (PROPRIETARY_TLV_BASE_ID + 198)
#define MWIFIEX_TX_DATA_BUF_SIZE_2K 2048
@@ -493,6 +494,7 @@
#define EVENT_TDLS_GENERIC_EVENT 0x00000052
#define EVENT_EXT_SCAN_REPORT 0x00000058
#define EVENT_REMAIN_ON_CHAN_EXPIRED 0x0000005f
+#define EVENT_TX_STATUS_REPORT 0x00000074
#define EVENT_ID_MASK 0xffff
#define BSS_NUM_MASK 0xf
@@ -541,6 +543,7 @@
#define MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET 0x08
#define MWIFIEX_TXPD_FLAGS_TDLS_PACKET 0x10
#define MWIFIEX_RXPD_FLAGS_TDLS_PACKET 0x01
+#define MWIFIEX_TXPD_FLAGS_REQ_TX_STATUS 0x20
struct txpd {
u8 bss_type;
@@ -552,7 +555,9 @@
u8 priority;
u8 flags;
u8 pkt_delay_2ms;
- u8 reserved1;
+ u8 reserved1[2];
+ u8 tx_token_id;
+ u8 reserved[2];
} __packed;
struct rxpd {
@@ -583,6 +588,7 @@
* [Bit 7] Reserved
*/
u8 ht_info;
+ u8 reserved[3];
u8 flags;
} __packed;
@@ -596,8 +602,9 @@
u8 priority;
u8 flags;
u8 pkt_delay_2ms;
- u8 reserved1;
- __le32 reserved2;
+ u8 reserved1[2];
+ u8 tx_token_id;
+ u8 reserved[2];
};
struct uap_rxpd {
@@ -611,6 +618,16 @@
u8 reserved1;
};
+struct mwifiex_fw_chan_stats {
+ u8 chan_num;
+ u8 bandcfg;
+ u8 flags;
+ s8 noise;
+ __le16 total_bss;
+ __le16 cca_scan_dur;
+ __le16 cca_busy_dur;
+} __packed;
+
enum mwifiex_chan_scan_mode_bitmasks {
MWIFIEX_PASSIVE_SCAN = BIT(0),
MWIFIEX_DISABLE_CHAN_FILT = BIT(1),
@@ -660,6 +677,11 @@
__le16 chan_gap;
} __packed;
+struct mwifiex_ietypes_chanstats {
+ struct mwifiex_ie_types_header header;
+ struct mwifiex_fw_chan_stats chanstats[0];
+} __packed;
+
struct mwifiex_ie_types_wildcard_ssid_params {
struct mwifiex_ie_types_header header;
u8 max_ssid_length;
@@ -1207,6 +1229,12 @@
u8 num_of_set;
} __packed;
+struct tx_status_event {
+ u8 packet_type;
+ u8 tx_token_id;
+ u8 status;
+} __packed;
+
#define MWIFIEX_USER_SCAN_CHAN_MAX 50
#define MWIFIEX_MAX_SSID_LIST_LENGTH 10
diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c
index 580aa45..520ad4a 100644
--- a/drivers/net/wireless/mwifiex/init.c
+++ b/drivers/net/wireless/mwifiex/init.c
@@ -137,6 +137,7 @@
priv->csa_expire_time = 0;
priv->del_list_idx = 0;
priv->hs2_enabled = false;
+ priv->check_tdls_tx = false;
memcpy(priv->tos_to_tid_inv, tos_to_tid_inv, MAX_NUM_TID);
return mwifiex_add_bss_prio_tbl(priv);
@@ -366,6 +367,7 @@
list_del(&priv->tx_ba_stream_tbl_ptr);
list_del(&priv->rx_reorder_tbl_ptr);
list_del(&priv->sta_list);
+ list_del(&priv->auto_tdls_list);
}
}
}
@@ -434,6 +436,7 @@
spin_lock_init(&priv->wmm.ra_list_spinlock);
spin_lock_init(&priv->curr_bcn_buf_lock);
spin_lock_init(&priv->sta_list_spinlock);
+ spin_lock_init(&priv->auto_tdls_lock);
}
}
@@ -449,7 +452,6 @@
spin_lock_init(&adapter->scan_pending_q_lock);
spin_lock_init(&adapter->rx_proc_lock);
- skb_queue_head_init(&adapter->usb_rx_data_q);
skb_queue_head_init(&adapter->rx_data_q);
for (i = 0; i < adapter->priv_num; ++i) {
@@ -466,10 +468,14 @@
INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr);
INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr);
INIT_LIST_HEAD(&priv->sta_list);
+ INIT_LIST_HEAD(&priv->auto_tdls_list);
skb_queue_head_init(&priv->tdls_txq);
spin_lock_init(&priv->tx_ba_stream_tbl_lock);
spin_lock_init(&priv->rx_reorder_tbl_lock);
+
+ spin_lock_init(&priv->ack_status_lock);
+ idr_init(&priv->ack_status_frames);
}
return 0;
@@ -646,6 +652,7 @@
if (adapter->priv[i]) {
priv = adapter->priv[i];
+ mwifiex_clean_auto_tdls(priv);
mwifiex_clean_txrx(priv);
mwifiex_delete_bss_prio_tbl(priv);
}
@@ -668,19 +675,6 @@
spin_lock(&adapter->mwifiex_lock);
- if (adapter->if_ops.data_complete) {
- while ((skb = skb_dequeue(&adapter->usb_rx_data_q))) {
- struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb);
-
- priv = adapter->priv[rx_info->bss_num];
- if (priv)
- priv->stats.rx_dropped++;
-
- dev_kfree_skb_any(skb);
- adapter->if_ops.data_complete(adapter);
- }
- }
-
mwifiex_adapter_cleanup(adapter);
spin_unlock(&adapter->mwifiex_lock);
diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c
index 8d6c259..411a6c2 100644
--- a/drivers/net/wireless/mwifiex/join.c
+++ b/drivers/net/wireless/mwifiex/join.c
@@ -880,9 +880,7 @@
/* Set Capability info */
bss_desc->cap_info_bitmap |= WLAN_CAPABILITY_IBSS;
- tmp_cap = le16_to_cpu(adhoc_start->cap_info_bitmap);
- tmp_cap &= ~WLAN_CAPABILITY_ESS;
- tmp_cap |= WLAN_CAPABILITY_IBSS;
+ tmp_cap = WLAN_CAPABILITY_IBSS;
/* Set up privacy in bss_desc */
if (priv->sec_info.encryption_mode) {
diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c
index 6044804..875eb77 100644
--- a/drivers/net/wireless/mwifiex/main.c
+++ b/drivers/net/wireless/mwifiex/main.c
@@ -28,6 +28,11 @@
static char *cal_data_cfg;
module_param(cal_data_cfg, charp, 0);
+static unsigned short driver_mode;
+module_param(driver_mode, ushort, 0);
+MODULE_PARM_DESC(driver_mode,
+ "station=0x1(default), ap-sta=0x3, station-p2p=0x5, ap-sta-p2p=0x7");
+
/*
* This function registers the device and performs all the necessary
* initializations.
@@ -122,6 +127,7 @@
}
}
+ vfree(adapter->chan_stats);
kfree(adapter);
return 0;
}
@@ -143,8 +149,11 @@
/* Check for Rx data */
while ((skb = skb_dequeue(&adapter->rx_data_q))) {
atomic_dec(&adapter->rx_pending);
- if (adapter->delay_main_work &&
+ if ((adapter->delay_main_work ||
+ adapter->iface_type == MWIFIEX_USB) &&
(atomic_read(&adapter->rx_pending) < LOW_RX_PENDING)) {
+ if (adapter->if_ops.submit_rem_rx_urbs)
+ adapter->if_ops.submit_rem_rx_urbs(adapter);
adapter->delay_main_work = false;
queue_work(adapter->workqueue, &adapter->main_work);
}
@@ -177,7 +186,6 @@
{
int ret = 0;
unsigned long flags;
- struct sk_buff *skb;
spin_lock_irqsave(&adapter->main_proc_lock, flags);
@@ -195,12 +203,15 @@
(adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY))
break;
- /* If we process interrupts first, it would increase RX pending
- * even further. Avoid this by checking if rx_pending has
- * crossed high threshold and schedule rx work queue
- * and then process interrupts
+ /* For non-USB interfaces, If we process interrupts first, it
+ * would increase RX pending even further. Avoid this by
+ * checking if rx_pending has crossed high threshold and
+ * schedule rx work queue and then process interrupts.
+ * For USB interface, there are no interrupts. We already have
+ * HIGH_RX_PENDING check in usb.c
*/
- if (atomic_read(&adapter->rx_pending) >= HIGH_RX_PENDING) {
+ if (atomic_read(&adapter->rx_pending) >= HIGH_RX_PENDING &&
+ adapter->iface_type != MWIFIEX_USB) {
adapter->delay_main_work = true;
if (!adapter->rx_processing)
queue_work(adapter->rx_workqueue,
@@ -252,11 +263,6 @@
}
}
- /* Check Rx data for USB */
- if (adapter->iface_type == MWIFIEX_USB)
- while ((skb = skb_dequeue(&adapter->usb_rx_data_q)))
- mwifiex_handle_rx_packet(adapter, skb);
-
/* Check for event */
if (adapter->event_received) {
adapter->event_received = false;
@@ -447,6 +453,16 @@
goto err_init_fw;
}
+ if (mwifiex_init_channel_scan_gap(adapter)) {
+ dev_err(adapter->dev, "could not init channel stats table\n");
+ goto err_init_fw;
+ }
+
+ if (driver_mode) {
+ driver_mode &= MWIFIEX_DRIVER_MODE_BITMASK;
+ driver_mode |= MWIFIEX_DRIVER_MODE_STA;
+ }
+
rtnl_lock();
/* Create station interface by default */
wdev = mwifiex_add_virtual_intf(adapter->wiphy, "mlan%d",
@@ -456,6 +472,28 @@
rtnl_unlock();
goto err_add_intf;
}
+
+ if (driver_mode & MWIFIEX_DRIVER_MODE_UAP) {
+ wdev = mwifiex_add_virtual_intf(adapter->wiphy, "uap%d",
+ NL80211_IFTYPE_AP, NULL, NULL);
+ if (IS_ERR(wdev)) {
+ dev_err(adapter->dev, "cannot create AP interface\n");
+ rtnl_unlock();
+ goto err_add_intf;
+ }
+ }
+
+ if (driver_mode & MWIFIEX_DRIVER_MODE_P2P) {
+ wdev = mwifiex_add_virtual_intf(adapter->wiphy, "p2p%d",
+ NL80211_IFTYPE_P2P_CLIENT, NULL,
+ NULL);
+ if (IS_ERR(wdev)) {
+ dev_err(adapter->dev,
+ "cannot create p2p client interface\n");
+ rtnl_unlock();
+ goto err_add_intf;
+ }
+ }
rtnl_unlock();
mwifiex_drv_get_driver_version(adapter, fmt, sizeof(fmt) - 1);
@@ -570,6 +608,48 @@
return 0;
}
+struct sk_buff *
+mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv,
+ struct sk_buff *skb, u8 flag, u64 *cookie)
+{
+ struct sk_buff *orig_skb = skb;
+ struct mwifiex_txinfo *tx_info, *orig_tx_info;
+
+ skb = skb_clone(skb, GFP_ATOMIC);
+ if (skb) {
+ unsigned long flags;
+ int id;
+
+ spin_lock_irqsave(&priv->ack_status_lock, flags);
+ id = idr_alloc(&priv->ack_status_frames, orig_skb,
+ 1, 0xff, GFP_ATOMIC);
+ spin_unlock_irqrestore(&priv->ack_status_lock, flags);
+
+ if (id >= 0) {
+ tx_info = MWIFIEX_SKB_TXCB(skb);
+ tx_info->ack_frame_id = id;
+ tx_info->flags |= flag;
+ orig_tx_info = MWIFIEX_SKB_TXCB(orig_skb);
+ orig_tx_info->ack_frame_id = id;
+ orig_tx_info->flags |= flag;
+
+ if (flag == MWIFIEX_BUF_FLAG_ACTION_TX_STATUS && cookie)
+ orig_tx_info->cookie = *cookie;
+
+ } else if (skb_shared(skb)) {
+ kfree_skb(orig_skb);
+ } else {
+ kfree_skb(skb);
+ skb = orig_skb;
+ }
+ } else {
+ /* couldn't clone -- lose tx status ... */
+ skb = orig_skb;
+ }
+
+ return skb;
+}
+
/*
* CFG802.11 network device handler for data transmission.
*/
@@ -579,6 +659,9 @@
struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
struct sk_buff *new_skb;
struct mwifiex_txinfo *tx_info;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0))
+ bool multicast;
+#endif
dev_dbg(priv->adapter->dev, "data: %lu BSS(%d-%d): Data <= kernel\n",
jiffies, priv->bss_type, priv->bss_num);
@@ -619,6 +702,17 @@
tx_info->bss_type = priv->bss_type;
tx_info->pkt_len = skb->len;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0))
+ multicast = is_multicast_ether_addr(skb->data);
+
+ if (unlikely(!multicast && skb->sk &&
+ skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS &&
+ priv->adapter->fw_api_ver == MWIFIEX_FW_V15))
+ skb = mwifiex_clone_skb_for_tx_status(priv,
+ skb,
+ MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS, NULL);
+#endif
+
/* Record the current time the packet was queued; used to
* determine the amount of time the packet was queued in
* the driver before it was sent to the firmware.
@@ -628,6 +722,13 @@
*/
__net_timestamp(skb);
+ if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
+ priv->bss_type == MWIFIEX_BSS_TYPE_STA &&
+ !ether_addr_equal_unaligned(priv->cfg_bssid, skb->data)) {
+ if (priv->adapter->auto_tdls && priv->check_tdls_tx)
+ mwifiex_tdls_check_tx(priv, skb);
+ }
+
mwifiex_queue_tx_pkt(priv, skb);
return 0;
@@ -868,7 +969,7 @@
adapter->cmd_wait_q.status = 0;
adapter->scan_wait_q_woken = false;
- if (num_possible_cpus() > 1) {
+ if ((num_possible_cpus() > 1) || adapter->iface_type == MWIFIEX_USB) {
adapter->rx_work_enabled = true;
pr_notice("rx work enabled, cpus %d\n", num_possible_cpus());
}
diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h
index e263574..e66993c 100644
--- a/drivers/net/wireless/mwifiex/main.h
+++ b/drivers/net/wireless/mwifiex/main.h
@@ -34,6 +34,7 @@
#include <linux/firmware.h>
#include <linux/ctype.h>
#include <linux/of.h>
+#include <linux/idr.h>
#include "decl.h"
#include "ioctl.h"
@@ -48,6 +49,11 @@
MWIFIEX_SYNC_CMD
};
+#define MWIFIEX_DRIVER_MODE_STA BIT(0)
+#define MWIFIEX_DRIVER_MODE_UAP BIT(1)
+#define MWIFIEX_DRIVER_MODE_P2P BIT(2)
+#define MWIFIEX_DRIVER_MODE_BITMASK (BIT(0) | BIT(1) | BIT(2))
+
#define MWIFIEX_MAX_AP 64
#define MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT (5 * HZ)
@@ -106,10 +112,7 @@
*/
#define IS_CARD_RX_RCVD(adapter) (adapter->cmd_resp_received || \
adapter->event_received || \
- ((adapter->iface_type != MWIFIEX_USB) && \
- adapter->data_received) || \
- ((adapter->iface_type == MWIFIEX_USB) && \
- !skb_queue_empty(&adapter->usb_rx_data_q)))
+ adapter->data_received)
#define MWIFIEX_TYPE_CMD 1
#define MWIFIEX_TYPE_DATA 0
@@ -504,8 +507,11 @@
struct mwifiex_wmm_desc wmm;
atomic_t wmm_tx_pending[IEEE80211_NUM_ACS];
struct list_head sta_list;
- /* spin lock for associated station list */
+ /* spin lock for associated station/TDLS peers list */
spinlock_t sta_list_spinlock;
+ struct list_head auto_tdls_list;
+ /* spin lock for auto TDLS peer list */
+ spinlock_t auto_tdls_lock;
struct list_head tx_ba_stream_tbl_ptr;
/* spin lock for tx_ba_stream_tbl_ptr queue */
spinlock_t tx_ba_stream_tbl_lock;
@@ -570,6 +576,12 @@
bool hs2_enabled;
struct station_parameters *sta_params;
struct sk_buff_head tdls_txq;
+ u8 check_tdls_tx;
+ struct timer_list auto_tdls_timer;
+ bool auto_tdls_timer_active;
+ struct idr ack_status_frames;
+ /* spin lock for ack status */
+ spinlock_t ack_status_lock;
};
enum mwifiex_ba_status {
@@ -592,6 +604,7 @@
struct timer_list timer;
struct mwifiex_rx_reorder_tbl *ptr;
struct mwifiex_private *priv;
+ u8 timer_is_set;
};
struct mwifiex_rx_reorder_tbl {
@@ -669,6 +682,17 @@
struct mwifiex_tdls_capab tdls_cap;
};
+struct mwifiex_auto_tdls_peer {
+ struct list_head list;
+ u8 mac_addr[ETH_ALEN];
+ u8 tdls_status;
+ int rssi;
+ long rssi_jiffies;
+ u8 failure_count;
+ u8 do_discover;
+ u8 do_setup;
+};
+
struct mwifiex_if_ops {
int (*init_if) (struct mwifiex_adapter *);
void (*cleanup_if) (struct mwifiex_adapter *);
@@ -689,13 +713,13 @@
void (*cleanup_mpa_buf) (struct mwifiex_adapter *);
int (*cmdrsp_complete) (struct mwifiex_adapter *, struct sk_buff *);
int (*event_complete) (struct mwifiex_adapter *, struct sk_buff *);
- int (*data_complete) (struct mwifiex_adapter *);
int (*init_fw_port) (struct mwifiex_adapter *);
int (*dnld_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *);
void (*card_reset) (struct mwifiex_adapter *);
void (*fw_dump)(struct mwifiex_adapter *);
int (*clean_pcie_ring) (struct mwifiex_adapter *adapter);
void (*iface_work)(struct work_struct *work);
+ void (*submit_rem_rx_urbs)(struct mwifiex_adapter *adapter);
};
struct mwifiex_adapter {
@@ -766,7 +790,6 @@
spinlock_t scan_pending_q_lock;
/* spin lock for RX processing routine */
spinlock_t rx_proc_lock;
- struct sk_buff_head usb_rx_data_q;
u32 scan_processing;
u16 region_code;
struct mwifiex_802_11d_domain_reg domain_reg;
@@ -844,6 +867,10 @@
u8 curr_mem_idx;
bool scan_chan_gap_enabled;
struct sk_buff_head rx_data_q;
+ struct mwifiex_chan_stats *chan_stats;
+ u32 num_in_chan_stats;
+ int survey_idx;
+ bool auto_tdls;
};
int mwifiex_init_lock_list(struct mwifiex_adapter *adapter);
@@ -948,6 +975,8 @@
int mwifiex_process_sta_event(struct mwifiex_private *);
int mwifiex_process_uap_event(struct mwifiex_private *);
void mwifiex_delete_all_station_list(struct mwifiex_private *priv);
+void mwifiex_wmm_del_peer_ra_list(struct mwifiex_private *priv,
+ const u8 *ra_addr);
void *mwifiex_process_sta_txpd(struct mwifiex_private *, struct sk_buff *skb);
void *mwifiex_process_uap_txpd(struct mwifiex_private *, struct sk_buff *skb);
int mwifiex_sta_init_cmd(struct mwifiex_private *, u8 first_sta);
@@ -1030,7 +1059,8 @@
int mwifiex_cmd_802_11_scan_ext(struct mwifiex_private *priv,
struct host_cmd_ds_command *cmd,
void *data_buf);
-int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv);
+int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *resp);
int mwifiex_handle_event_ext_scan_report(struct mwifiex_private *priv,
void *buf);
@@ -1300,6 +1330,24 @@
u32 pri_chan, u8 chan_bw);
int mwifiex_init_channel_scan_gap(struct mwifiex_adapter *adapter);
+int mwifiex_tdls_check_tx(struct mwifiex_private *priv, struct sk_buff *skb);
+void mwifiex_flush_auto_tdls_list(struct mwifiex_private *priv);
+void mwifiex_auto_tdls_update_peer_status(struct mwifiex_private *priv,
+ const u8 *mac, u8 link_status);
+void mwifiex_auto_tdls_update_peer_signal(struct mwifiex_private *priv,
+ u8 *mac, s8 snr, s8 nflr);
+void mwifiex_check_auto_tdls(unsigned long context);
+void mwifiex_add_auto_tdls_peer(struct mwifiex_private *priv, const u8 *mac);
+void mwifiex_setup_auto_tdls_timer(struct mwifiex_private *priv);
+void mwifiex_clean_auto_tdls(struct mwifiex_private *priv);
+
+void mwifiex_parse_tx_status_event(struct mwifiex_private *priv,
+ void *event_body);
+
+struct sk_buff *
+mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv,
+ struct sk_buff *skb, u8 flag, u64 *cookie);
+
#ifdef CONFIG_DEBUG_FS
void mwifiex_debugfs_init(void);
void mwifiex_debugfs_remove(void);
diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c
index ca64d4c..984a7a4 100644
--- a/drivers/net/wireless/mwifiex/scan.c
+++ b/drivers/net/wireless/mwifiex/scan.c
@@ -1623,7 +1623,7 @@
if (*bytes_left >= sizeof(beacon_size)) {
/* Extract & convert beacon size from command buffer */
- memcpy(&beacon_size, *bss_info, sizeof(beacon_size));
+ beacon_size = le16_to_cpu(*(__le16 *)(*bss_info));
*bytes_left -= sizeof(beacon_size);
*bss_info += sizeof(beacon_size);
}
@@ -1755,6 +1755,7 @@
{
struct mwifiex_adapter *adapter = priv->adapter;
+ adapter->survey_idx = 0;
if (adapter->curr_cmd->wait_q_enabled) {
adapter->cmd_wait_q.status = 0;
if (!priv->scan_request) {
@@ -1976,10 +1977,53 @@
return 0;
}
-/* This function handles the command response of extended scan */
-int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv)
+static void
+mwifiex_update_chan_statistics(struct mwifiex_private *priv,
+ struct mwifiex_ietypes_chanstats *tlv_stat)
{
struct mwifiex_adapter *adapter = priv->adapter;
+ u8 i, num_chan;
+ struct mwifiex_fw_chan_stats *fw_chan_stats;
+ struct mwifiex_chan_stats chan_stats;
+
+ fw_chan_stats = (void *)((u8 *)tlv_stat +
+ sizeof(struct mwifiex_ie_types_header));
+ num_chan = le16_to_cpu(tlv_stat->header.len) /
+ sizeof(struct mwifiex_chan_stats);
+
+ for (i = 0 ; i < num_chan; i++) {
+ chan_stats.chan_num = fw_chan_stats->chan_num;
+ chan_stats.bandcfg = fw_chan_stats->bandcfg;
+ chan_stats.flags = fw_chan_stats->flags;
+ chan_stats.noise = fw_chan_stats->noise;
+ chan_stats.total_bss = le16_to_cpu(fw_chan_stats->total_bss);
+ chan_stats.cca_scan_dur =
+ le16_to_cpu(fw_chan_stats->cca_scan_dur);
+ chan_stats.cca_busy_dur =
+ le16_to_cpu(fw_chan_stats->cca_busy_dur);
+ dev_dbg(adapter->dev,
+ "chan=%d, noise=%d, total_network=%d scan_duration=%d, busy_duration=%d\n",
+ chan_stats.chan_num,
+ chan_stats.noise,
+ chan_stats.total_bss,
+ chan_stats.cca_scan_dur,
+ chan_stats.cca_busy_dur);
+ memcpy(&adapter->chan_stats[adapter->survey_idx++], &chan_stats,
+ sizeof(struct mwifiex_chan_stats));
+ fw_chan_stats++;
+ }
+}
+
+/* This function handles the command response of extended scan */
+int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *resp)
+{
+ struct mwifiex_adapter *adapter = priv->adapter;
+ struct host_cmd_ds_802_11_scan_ext *ext_scan_resp;
+ struct mwifiex_ie_types_header *tlv;
+ struct mwifiex_ietypes_chanstats *tlv_stat;
+ u16 buf_left, type, len;
+
struct host_cmd_ds_command *cmd_ptr;
struct cmd_ctrl_node *cmd_node;
unsigned long cmd_flags, scan_flags;
@@ -1987,6 +2031,36 @@
dev_dbg(priv->adapter->dev, "info: EXT scan returns successfully\n");
+ ext_scan_resp = &resp->params.ext_scan;
+
+ tlv = (void *)ext_scan_resp->tlv_buffer;
+ buf_left = le16_to_cpu(resp->size) - (sizeof(*ext_scan_resp) + S_DS_GEN
+ - 1);
+
+ while (buf_left >= sizeof(struct mwifiex_ie_types_header)) {
+ type = le16_to_cpu(tlv->type);
+ len = le16_to_cpu(tlv->len);
+
+ if (buf_left < (sizeof(struct mwifiex_ie_types_header) + len)) {
+ dev_err(adapter->dev,
+ "error processing scan response TLVs");
+ break;
+ }
+
+ switch (type) {
+ case TLV_TYPE_CHANNEL_STATS:
+ tlv_stat = (void *)tlv;
+ mwifiex_update_chan_statistics(priv, tlv_stat);
+ break;
+ default:
+ break;
+ }
+
+ buf_left -= len + sizeof(struct mwifiex_ie_types_header);
+ tlv = (void *)((u8 *)tlv + len +
+ sizeof(struct mwifiex_ie_types_header));
+ }
+
spin_lock_irqsave(&adapter->cmd_pending_q_lock, cmd_flags);
spin_lock_irqsave(&adapter->scan_pending_q_lock, scan_flags);
if (list_empty(&adapter->scan_pending_q)) {
diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c
index 78e36e5..a6ab0a8 100644
--- a/drivers/net/wireless/mwifiex/sdio.c
+++ b/drivers/net/wireless/mwifiex/sdio.c
@@ -106,6 +106,7 @@
card->mp_tx_agg_buf_size = data->mp_tx_agg_buf_size;
card->mp_rx_agg_buf_size = data->mp_rx_agg_buf_size;
card->supports_fw_dump = data->supports_fw_dump;
+ card->auto_tdls = data->auto_tdls;
}
sdio_claim_host(func);
@@ -1880,6 +1881,7 @@
return -1;
}
+ adapter->auto_tdls = card->auto_tdls;
return ret;
}
diff --git a/drivers/net/wireless/mwifiex/sdio.h b/drivers/net/wireless/mwifiex/sdio.h
index 20cd9ad..54c0715 100644
--- a/drivers/net/wireless/mwifiex/sdio.h
+++ b/drivers/net/wireless/mwifiex/sdio.h
@@ -246,6 +246,7 @@
u8 curr_wr_port;
u8 *mp_regs;
+ u8 auto_tdls;
struct mwifiex_sdio_mpa_tx mpa_tx;
struct mwifiex_sdio_mpa_rx mpa_rx;
@@ -262,6 +263,7 @@
u16 tx_buf_size;
u32 mp_tx_agg_buf_size;
u32 mp_rx_agg_buf_size;
+ u8 auto_tdls;
};
static const struct mwifiex_sdio_card_reg mwifiex_reg_sd87xx = {
@@ -387,6 +389,7 @@
.mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
.mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
.supports_fw_dump = false,
+ .auto_tdls = false,
};
static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = {
@@ -400,6 +403,7 @@
.mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
.mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
.supports_fw_dump = false,
+ .auto_tdls = false,
};
static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = {
@@ -413,6 +417,7 @@
.mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
.mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
.supports_fw_dump = false,
+ .auto_tdls = false,
};
static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = {
@@ -426,6 +431,7 @@
.mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K,
.mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K,
.supports_fw_dump = true,
+ .auto_tdls = false,
};
static const struct mwifiex_sdio_device mwifiex_sdio_sd8887 = {
@@ -439,6 +445,7 @@
.mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K,
.mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K,
.supports_fw_dump = false,
+ .auto_tdls = true,
};
/*
diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c
index 4aad446..b65e101 100644
--- a/drivers/net/wireless/mwifiex/sta_cmdresp.c
+++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c
@@ -983,7 +983,7 @@
adapter->curr_cmd->wait_q_enabled = false;
break;
case HostCmd_CMD_802_11_SCAN_EXT:
- ret = mwifiex_ret_802_11_scan_ext(priv);
+ ret = mwifiex_ret_802_11_scan_ext(priv, resp);
adapter->curr_cmd->wait_q_enabled = false;
break;
case HostCmd_CMD_802_11_BG_SCAN_QUERY:
diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c
index f1c240e..b8c171d 100644
--- a/drivers/net/wireless/mwifiex/sta_event.c
+++ b/drivers/net/wireless/mwifiex/sta_event.c
@@ -55,9 +55,13 @@
priv->scan_block = false;
if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
- ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info))
+ ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) {
mwifiex_disable_all_tdls_links(priv);
+ if (priv->adapter->auto_tdls)
+ mwifiex_clean_auto_tdls(priv);
+ }
+
/* Free Tx and Rx packets, report disconnect to upper layer */
mwifiex_clean_txrx(priv);
@@ -163,9 +167,6 @@
NL80211_TDLS_TEARDOWN,
le16_to_cpu(tdls_evt->u.reason_code),
GFP_KERNEL);
- ret = mwifiex_tdls_oper(priv, tdls_evt->peer_mac,
- MWIFIEX_TDLS_DISABLE_LINK);
- queue_work(adapter->workqueue, &adapter->main_work);
break;
default:
break;
@@ -503,6 +504,11 @@
ret = mwifiex_parse_tdls_event(priv, adapter->event_skb);
break;
+ case EVENT_TX_STATUS_REPORT:
+ dev_dbg(adapter->dev, "event: TX_STATUS Report\n");
+ mwifiex_parse_tx_status_event(priv, adapter->event_body);
+ break;
+
default:
dev_dbg(adapter->dev, "event: unknown event id: %#x\n",
eventcause);
diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c
index 92f3eb8..1626868 100644
--- a/drivers/net/wireless/mwifiex/sta_ioctl.c
+++ b/drivers/net/wireless/mwifiex/sta_ioctl.c
@@ -1026,12 +1026,12 @@
int max_len)
{
union {
- u32 l;
+ __le32 l;
u8 c[4];
} ver;
char fw_ver[32];
- ver.l = adapter->fw_release_number;
+ ver.l = cpu_to_le32(adapter->fw_release_number);
sprintf(fw_ver, "%u.%u.%u.p%u", ver.c[2], ver.c[1], ver.c[0], ver.c[3]);
snprintf(version, max_len, driver_version, fw_ver);
diff --git a/drivers/net/wireless/mwifiex/sta_rx.c b/drivers/net/wireless/mwifiex/sta_rx.c
index 308e781..860fbb9 100644
--- a/drivers/net/wireless/mwifiex/sta_rx.c
+++ b/drivers/net/wireless/mwifiex/sta_rx.c
@@ -232,6 +232,9 @@
if (sta_ptr)
sta_ptr->rx_seq[local_rx_pd->priority] =
le16_to_cpu(local_rx_pd->seq_num);
+ mwifiex_auto_tdls_update_peer_signal(priv, ta,
+ local_rx_pd->snr,
+ local_rx_pd->nf);
}
} else {
if (rx_pkt_type != PKT_TYPE_BAR)
diff --git a/drivers/net/wireless/mwifiex/sta_tx.c b/drivers/net/wireless/mwifiex/sta_tx.c
index dab7b33..b896d73 100644
--- a/drivers/net/wireless/mwifiex/sta_tx.c
+++ b/drivers/net/wireless/mwifiex/sta_tx.c
@@ -77,6 +77,12 @@
local_tx_pd->pkt_delay_2ms =
mwifiex_wmm_compute_drv_pkt_delay(priv, skb);
+ if (tx_info->flags & MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS ||
+ tx_info->flags & MWIFIEX_BUF_FLAG_ACTION_TX_STATUS) {
+ local_tx_pd->tx_token_id = tx_info->ack_frame_id;
+ local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_REQ_TX_STATUS;
+ }
+
if (local_tx_pd->priority <
ARRAY_SIZE(priv->wmm.user_pri_pkt_tx_ctrl))
/*
diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c
index e294907..22884b4 100644
--- a/drivers/net/wireless/mwifiex/tdls.c
+++ b/drivers/net/wireless/mwifiex/tdls.c
@@ -24,6 +24,7 @@
#define TDLS_REQ_FIX_LEN 6
#define TDLS_RESP_FIX_LEN 8
#define TDLS_CONFIRM_FIX_LEN 6
+#define MWIFIEX_TDLS_WMM_INFO_SIZE 7
static void mwifiex_restore_tdls_packets(struct mwifiex_private *priv,
const u8 *mac, u8 status)
@@ -367,6 +368,55 @@
*pos++ = MWIFIEX_TDLS_DEF_QOS_CAPAB;
}
+static void
+mwifiex_tdls_add_wmm_param_ie(struct mwifiex_private *priv, struct sk_buff *skb)
+{
+ struct ieee80211_wmm_param_ie *wmm;
+ u8 ac_vi[] = {0x42, 0x43, 0x5e, 0x00};
+ u8 ac_vo[] = {0x62, 0x32, 0x2f, 0x00};
+ u8 ac_be[] = {0x03, 0xa4, 0x00, 0x00};
+ u8 ac_bk[] = {0x27, 0xa4, 0x00, 0x00};
+
+ wmm = (void *)skb_put(skb, sizeof(*wmm));
+ memset(wmm, 0, sizeof(*wmm));
+
+ wmm->element_id = WLAN_EID_VENDOR_SPECIFIC;
+ wmm->len = sizeof(*wmm) - 2;
+ wmm->oui[0] = 0x00; /* Microsoft OUI 00:50:F2 */
+ wmm->oui[1] = 0x50;
+ wmm->oui[2] = 0xf2;
+ wmm->oui_type = 2; /* WME */
+ wmm->oui_subtype = 1; /* WME param */
+ wmm->version = 1; /* WME ver */
+ wmm->qos_info = 0; /* U-APSD not in use */
+
+ /* use default WMM AC parameters for TDLS link*/
+ memcpy(&wmm->ac[0], ac_be, sizeof(ac_be));
+ memcpy(&wmm->ac[1], ac_bk, sizeof(ac_bk));
+ memcpy(&wmm->ac[2], ac_vi, sizeof(ac_vi));
+ memcpy(&wmm->ac[3], ac_vo, sizeof(ac_vo));
+}
+
+static void
+mwifiex_add_wmm_info_ie(struct mwifiex_private *priv, struct sk_buff *skb,
+ u8 qosinfo)
+{
+ u8 *buf;
+
+ buf = (void *)skb_put(skb, MWIFIEX_TDLS_WMM_INFO_SIZE +
+ sizeof(struct ieee_types_header));
+
+ *buf++ = WLAN_EID_VENDOR_SPECIFIC;
+ *buf++ = 7; /* len */
+ *buf++ = 0x00; /* Microsoft OUI 00:50:F2 */
+ *buf++ = 0x50;
+ *buf++ = 0xf2;
+ *buf++ = 2; /* WME */
+ *buf++ = 0; /* WME info */
+ *buf++ = 1; /* WME ver */
+ *buf++ = qosinfo; /* U-APSD no in use */
+}
+
static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv,
const u8 *peer, u8 action_code,
u8 dialog_token,
@@ -421,6 +471,7 @@
mwifiex_tdls_add_ext_capab(priv, skb);
mwifiex_tdls_add_qos_capab(skb);
+ mwifiex_add_wmm_info_ie(priv, skb, 0);
break;
case WLAN_TDLS_SETUP_RESPONSE:
@@ -458,6 +509,7 @@
mwifiex_tdls_add_ext_capab(priv, skb);
mwifiex_tdls_add_qos_capab(skb);
+ mwifiex_add_wmm_info_ie(priv, skb, 0);
break;
case WLAN_TDLS_SETUP_CONFIRM:
@@ -466,6 +518,8 @@
skb_put(skb, sizeof(tf->u.setup_cfm));
tf->u.setup_cfm.status_code = cpu_to_le16(status_code);
tf->u.setup_cfm.dialog_token = dialog_token;
+
+ mwifiex_tdls_add_wmm_param_ie(priv, skb);
if (priv->adapter->is_hw_11ac_capable) {
ret = mwifiex_tdls_add_vht_oper(priv, peer, skb);
if (ret) {
@@ -544,6 +598,7 @@
sizeof(struct ieee_types_bss_co_2040) +
sizeof(struct ieee80211_ht_operation) +
sizeof(struct ieee80211_tdls_lnkie) +
+ sizeof(struct ieee80211_wmm_param_ie) +
extra_ies_len;
if (priv->adapter->is_hw_11ac_capable)
@@ -973,6 +1028,7 @@
}
mwifiex_restore_tdls_packets(priv, peer, TDLS_LINK_TEARDOWN);
+ mwifiex_auto_tdls_update_peer_status(priv, peer, TDLS_NOT_SETUP);
memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN);
tdls_oper.tdls_action = MWIFIEX_TDLS_DISABLE_LINK;
return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER,
@@ -1017,6 +1073,8 @@
memset(sta_ptr->rx_seq, 0xff, sizeof(sta_ptr->rx_seq));
mwifiex_restore_tdls_packets(priv, peer, TDLS_SETUP_COMPLETE);
+ mwifiex_auto_tdls_update_peer_status(priv, peer,
+ TDLS_SETUP_COMPLETE);
} else {
dev_dbg(priv->adapter->dev,
"tdls: enable link %pM failed\n", peer);
@@ -1030,6 +1088,8 @@
mwifiex_del_sta_entry(priv, peer);
}
mwifiex_restore_tdls_packets(priv, peer, TDLS_LINK_TEARDOWN);
+ mwifiex_auto_tdls_update_peer_status(priv, peer,
+ TDLS_NOT_SETUP);
return -1;
}
@@ -1097,3 +1157,231 @@
mwifiex_del_all_sta_list(priv);
}
+
+int mwifiex_tdls_check_tx(struct mwifiex_private *priv, struct sk_buff *skb)
+{
+ struct mwifiex_auto_tdls_peer *peer;
+ unsigned long flags;
+ u8 mac[ETH_ALEN];
+
+ ether_addr_copy(mac, skb->data);
+
+ spin_lock_irqsave(&priv->auto_tdls_lock, flags);
+ list_for_each_entry(peer, &priv->auto_tdls_list, list) {
+ if (!memcmp(mac, peer->mac_addr, ETH_ALEN)) {
+ if (peer->rssi <= MWIFIEX_TDLS_RSSI_HIGH &&
+ peer->tdls_status == TDLS_NOT_SETUP &&
+ (peer->failure_count <
+ MWIFIEX_TDLS_MAX_FAIL_COUNT)) {
+ peer->tdls_status = TDLS_SETUP_INPROGRESS;
+ dev_dbg(priv->adapter->dev,
+ "setup TDLS link, peer=%pM rssi=%d\n",
+ peer->mac_addr, peer->rssi);
+
+ cfg80211_tdls_oper_request(priv->netdev,
+ peer->mac_addr,
+ NL80211_TDLS_SETUP,
+ 0, GFP_ATOMIC);
+ peer->do_setup = false;
+ priv->check_tdls_tx = false;
+ } else if (peer->failure_count <
+ MWIFIEX_TDLS_MAX_FAIL_COUNT &&
+ peer->do_discover) {
+ mwifiex_send_tdls_data_frame(priv,
+ peer->mac_addr,
+ WLAN_TDLS_DISCOVERY_REQUEST,
+ 1, 0, NULL, 0);
+ peer->do_discover = false;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&priv->auto_tdls_lock, flags);
+
+ return 0;
+}
+
+void mwifiex_flush_auto_tdls_list(struct mwifiex_private *priv)
+{
+ struct mwifiex_auto_tdls_peer *peer, *tmp_node;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->auto_tdls_lock, flags);
+ list_for_each_entry_safe(peer, tmp_node, &priv->auto_tdls_list, list) {
+ list_del(&peer->list);
+ kfree(peer);
+ }
+
+ INIT_LIST_HEAD(&priv->auto_tdls_list);
+ spin_unlock_irqrestore(&priv->auto_tdls_lock, flags);
+ priv->check_tdls_tx = false;
+}
+
+void mwifiex_add_auto_tdls_peer(struct mwifiex_private *priv, const u8 *mac)
+{
+ struct mwifiex_auto_tdls_peer *tdls_peer;
+ unsigned long flags;
+
+ if (!priv->adapter->auto_tdls)
+ return;
+
+ spin_lock_irqsave(&priv->auto_tdls_lock, flags);
+ list_for_each_entry(tdls_peer, &priv->auto_tdls_list, list) {
+ if (!memcmp(tdls_peer->mac_addr, mac, ETH_ALEN)) {
+ tdls_peer->tdls_status = TDLS_SETUP_INPROGRESS;
+ tdls_peer->rssi_jiffies = jiffies;
+ spin_unlock_irqrestore(&priv->auto_tdls_lock, flags);
+ return;
+ }
+ }
+
+ /* create new TDLS peer */
+ tdls_peer = kzalloc(sizeof(*tdls_peer), GFP_ATOMIC);
+ if (tdls_peer) {
+ ether_addr_copy(tdls_peer->mac_addr, mac);
+ tdls_peer->tdls_status = TDLS_SETUP_INPROGRESS;
+ tdls_peer->rssi_jiffies = jiffies;
+ INIT_LIST_HEAD(&tdls_peer->list);
+ list_add_tail(&tdls_peer->list, &priv->auto_tdls_list);
+ dev_dbg(priv->adapter->dev, "Add auto TDLS peer= %pM to list\n",
+ mac);
+ }
+
+ spin_unlock_irqrestore(&priv->auto_tdls_lock, flags);
+}
+
+void mwifiex_auto_tdls_update_peer_status(struct mwifiex_private *priv,
+ const u8 *mac, u8 link_status)
+{
+ struct mwifiex_auto_tdls_peer *peer;
+ unsigned long flags;
+
+ if (!priv->adapter->auto_tdls)
+ return;
+
+ spin_lock_irqsave(&priv->auto_tdls_lock, flags);
+ list_for_each_entry(peer, &priv->auto_tdls_list, list) {
+ if (!memcmp(peer->mac_addr, mac, ETH_ALEN)) {
+ if ((link_status == TDLS_NOT_SETUP) &&
+ (peer->tdls_status == TDLS_SETUP_INPROGRESS))
+ peer->failure_count++;
+ else if (link_status == TDLS_SETUP_COMPLETE)
+ peer->failure_count = 0;
+
+ peer->tdls_status = link_status;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&priv->auto_tdls_lock, flags);
+}
+
+void mwifiex_auto_tdls_update_peer_signal(struct mwifiex_private *priv,
+ u8 *mac, s8 snr, s8 nflr)
+{
+ struct mwifiex_auto_tdls_peer *peer;
+ unsigned long flags;
+
+ if (!priv->adapter->auto_tdls)
+ return;
+
+ spin_lock_irqsave(&priv->auto_tdls_lock, flags);
+ list_for_each_entry(peer, &priv->auto_tdls_list, list) {
+ if (!memcmp(peer->mac_addr, mac, ETH_ALEN)) {
+ peer->rssi = nflr - snr;
+ peer->rssi_jiffies = jiffies;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&priv->auto_tdls_lock, flags);
+}
+
+void mwifiex_check_auto_tdls(unsigned long context)
+{
+ struct mwifiex_private *priv = (struct mwifiex_private *)context;
+ struct mwifiex_auto_tdls_peer *tdls_peer;
+ unsigned long flags;
+ u16 reason = WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED;
+
+ if (WARN_ON_ONCE(!priv || !priv->adapter)) {
+ pr_err("mwifiex: %s: adapter or private structure is NULL\n",
+ __func__);
+ return;
+ }
+
+ if (unlikely(!priv->adapter->auto_tdls))
+ return;
+
+ if (!priv->auto_tdls_timer_active) {
+ dev_dbg(priv->adapter->dev,
+ "auto TDLS timer inactive; return");
+ return;
+ }
+
+ priv->check_tdls_tx = false;
+
+ if (list_empty(&priv->auto_tdls_list)) {
+ mod_timer(&priv->auto_tdls_timer,
+ jiffies +
+ msecs_to_jiffies(MWIFIEX_TIMER_10S));
+ return;
+ }
+
+ spin_lock_irqsave(&priv->auto_tdls_lock, flags);
+ list_for_each_entry(tdls_peer, &priv->auto_tdls_list, list) {
+ if ((jiffies - tdls_peer->rssi_jiffies) >
+ (MWIFIEX_AUTO_TDLS_IDLE_TIME * HZ)) {
+ tdls_peer->rssi = 0;
+ tdls_peer->do_discover = true;
+ priv->check_tdls_tx = true;
+ }
+
+ if (((tdls_peer->rssi >= MWIFIEX_TDLS_RSSI_LOW) ||
+ !tdls_peer->rssi) &&
+ tdls_peer->tdls_status == TDLS_SETUP_COMPLETE) {
+ tdls_peer->tdls_status = TDLS_LINK_TEARDOWN;
+ dev_dbg(priv->adapter->dev,
+ "teardown TDLS link,peer=%pM rssi=%d\n",
+ tdls_peer->mac_addr, -tdls_peer->rssi);
+ tdls_peer->do_discover = true;
+ priv->check_tdls_tx = true;
+ cfg80211_tdls_oper_request(priv->netdev,
+ tdls_peer->mac_addr,
+ NL80211_TDLS_TEARDOWN,
+ reason, GFP_ATOMIC);
+ } else if (tdls_peer->rssi &&
+ tdls_peer->rssi <= MWIFIEX_TDLS_RSSI_HIGH &&
+ tdls_peer->tdls_status == TDLS_NOT_SETUP &&
+ tdls_peer->failure_count <
+ MWIFIEX_TDLS_MAX_FAIL_COUNT) {
+ priv->check_tdls_tx = true;
+ tdls_peer->do_setup = true;
+ dev_dbg(priv->adapter->dev,
+ "check TDLS with peer=%pM rssi=%d\n",
+ tdls_peer->mac_addr, -tdls_peer->rssi);
+ }
+ }
+ spin_unlock_irqrestore(&priv->auto_tdls_lock, flags);
+
+ mod_timer(&priv->auto_tdls_timer,
+ jiffies + msecs_to_jiffies(MWIFIEX_TIMER_10S));
+}
+
+void mwifiex_setup_auto_tdls_timer(struct mwifiex_private *priv)
+{
+ init_timer(&priv->auto_tdls_timer);
+ priv->auto_tdls_timer.function = mwifiex_check_auto_tdls;
+ priv->auto_tdls_timer.data = (unsigned long)priv;
+ priv->auto_tdls_timer_active = true;
+ mod_timer(&priv->auto_tdls_timer,
+ jiffies + msecs_to_jiffies(MWIFIEX_TIMER_10S));
+}
+
+void mwifiex_clean_auto_tdls(struct mwifiex_private *priv)
+{
+ if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
+ priv->adapter->auto_tdls &&
+ priv->bss_type == MWIFIEX_BSS_TYPE_STA) {
+ priv->auto_tdls_timer_active = false;
+ del_timer(&priv->auto_tdls_timer);
+ mwifiex_flush_auto_tdls_list(priv);
+ }
+}
diff --git a/drivers/net/wireless/mwifiex/txrx.c b/drivers/net/wireless/mwifiex/txrx.c
index 96a2126..6ae1333 100644
--- a/drivers/net/wireless/mwifiex/txrx.c
+++ b/drivers/net/wireless/mwifiex/txrx.c
@@ -64,10 +64,6 @@
else
ret = mwifiex_process_sta_rx_packet(priv, skb);
- /* Decrement RX pending counter for each packet */
- if (adapter->if_ops.data_complete)
- adapter->if_ops.data_complete(adapter);
-
return ret;
}
EXPORT_SYMBOL_GPL(mwifiex_handle_rx_packet);
@@ -207,3 +203,34 @@
}
EXPORT_SYMBOL_GPL(mwifiex_write_data_complete);
+void mwifiex_parse_tx_status_event(struct mwifiex_private *priv,
+ void *event_body)
+{
+ struct tx_status_event *tx_status = (void *)priv->adapter->event_body;
+ struct sk_buff *ack_skb;
+ unsigned long flags;
+ struct mwifiex_txinfo *tx_info;
+
+ if (!tx_status->tx_token_id)
+ return;
+
+ spin_lock_irqsave(&priv->ack_status_lock, flags);
+ ack_skb = idr_find(&priv->ack_status_frames, tx_status->tx_token_id);
+ if (ack_skb)
+ idr_remove(&priv->ack_status_frames, tx_status->tx_token_id);
+ spin_unlock_irqrestore(&priv->ack_status_lock, flags);
+
+ if (ack_skb) {
+ tx_info = MWIFIEX_SKB_TXCB(ack_skb);
+
+ if (tx_info->flags & MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS) {
+ /* consumes ack_skb */
+ skb_complete_wifi_ack(ack_skb, !tx_status->status);
+ } else {
+ cfg80211_mgmt_tx_status(priv->wdev, tx_info->cookie,
+ ack_skb->data, ack_skb->len,
+ !tx_status->status, GFP_ATOMIC);
+ dev_kfree_skb_any(ack_skb);
+ }
+ }
+}
diff --git a/drivers/net/wireless/mwifiex/uap_cmd.c b/drivers/net/wireless/mwifiex/uap_cmd.c
index 300bab4..0f347fd 100644
--- a/drivers/net/wireless/mwifiex/uap_cmd.c
+++ b/drivers/net/wireless/mwifiex/uap_cmd.c
@@ -167,7 +167,7 @@
ht_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, params->beacon.tail,
params->beacon.tail_len);
if (ht_ie) {
- memcpy(&bss_cfg->ht_cap, ht_ie + 2,
+ memcpy(&bss_cfg->ht_cap, ht_ie,
sizeof(struct ieee80211_ht_cap));
cap_info = le16_to_cpu(bss_cfg->ht_cap.cap_info);
memset(&bss_cfg->ht_cap.mcs, 0,
diff --git a/drivers/net/wireless/mwifiex/uap_event.c b/drivers/net/wireless/mwifiex/uap_event.c
index 7c2b976..c54a537 100644
--- a/drivers/net/wireless/mwifiex/uap_event.c
+++ b/drivers/net/wireless/mwifiex/uap_event.c
@@ -110,6 +110,7 @@
mwifiex_11n_del_rx_reorder_tbl_by_ta(priv, deauth_mac);
mwifiex_del_tx_ba_stream_tbl_by_ra(priv, deauth_mac);
}
+ mwifiex_wmm_del_peer_ra_list(priv, deauth_mac);
mwifiex_del_sta_entry(priv, deauth_mac);
break;
case EVENT_UAP_BSS_IDLE:
@@ -172,6 +173,10 @@
return mwifiex_handle_event_ext_scan_report(priv,
adapter->event_skb->data);
break;
+ case EVENT_TX_STATUS_REPORT:
+ dev_dbg(adapter->dev, "event: TX_STATUS Report\n");
+ mwifiex_parse_tx_status_event(priv, adapter->event_body);
+ break;
default:
dev_dbg(adapter->dev, "event: unknown event id: %#x\n",
eventcause);
diff --git a/drivers/net/wireless/mwifiex/uap_txrx.c b/drivers/net/wireless/mwifiex/uap_txrx.c
index ec7309d..be3a203 100644
--- a/drivers/net/wireless/mwifiex/uap_txrx.c
+++ b/drivers/net/wireless/mwifiex/uap_txrx.c
@@ -266,6 +266,7 @@
struct rx_packet_hdr *rx_pkt_hdr;
u16 rx_pkt_type;
u8 ta[ETH_ALEN], pkt_type;
+ unsigned long flags;
struct mwifiex_sta_node *node;
uap_rx_pd = (struct uap_rxpd *)(skb->data);
@@ -294,10 +295,12 @@
memcpy(ta, rx_pkt_hdr->eth803_hdr.h_source, ETH_ALEN);
if (rx_pkt_type != PKT_TYPE_BAR && uap_rx_pd->priority < MAX_NUM_TID) {
+ spin_lock_irqsave(&priv->sta_list_spinlock, flags);
node = mwifiex_get_sta_entry(priv, ta);
if (node)
node->rx_seq[uap_rx_pd->priority] =
le16_to_cpu(uap_rx_pd->seq_num);
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
}
if (!priv->ap_11n_enabled ||
@@ -370,10 +373,16 @@
txpd->bss_num = priv->bss_num;
txpd->bss_type = priv->bss_type;
txpd->tx_pkt_length = cpu_to_le16((u16)(skb->len - len));
-
txpd->priority = (u8)skb->priority;
+
txpd->pkt_delay_2ms = mwifiex_wmm_compute_drv_pkt_delay(priv, skb);
+ if (tx_info->flags & MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS ||
+ tx_info->flags & MWIFIEX_BUF_FLAG_ACTION_TX_STATUS) {
+ txpd->tx_token_id = tx_info->ack_frame_id;
+ txpd->flags |= MWIFIEX_TXPD_FLAGS_REQ_TX_STATUS;
+ }
+
if (txpd->priority < ARRAY_SIZE(priv->wmm.user_pri_pkt_tx_ctrl))
/*
* Set the priority specific tx_control field, setting of 0 will
diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c
index 4371e12..1b56495 100644
--- a/drivers/net/wireless/mwifiex/usb.c
+++ b/drivers/net/wireless/mwifiex/usb.c
@@ -27,6 +27,11 @@
static struct semaphore add_remove_card_sem;
static struct usb_device_id mwifiex_usb_table[] = {
+ /* 8766 */
+ {USB_DEVICE(USB8XXX_VID, USB8766_PID_1)},
+ {USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8766_PID_2,
+ USB_CLASS_VENDOR_SPEC,
+ USB_SUBCLASS_VENDOR_SPEC, 0xff)},
/* 8797 */
{USB_DEVICE(USB8XXX_VID, USB8797_PID_1)},
{USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8797_PID_2,
@@ -125,8 +130,10 @@
dev_err(dev, "DATA: skb->len too large\n");
return -1;
}
- skb_queue_tail(&adapter->usb_rx_data_q, skb);
+
+ skb_queue_tail(&adapter->rx_data_q, skb);
adapter->data_received = true;
+ atomic_inc(&adapter->rx_pending);
break;
default:
dev_err(dev, "%s: unknown endport %#x\n", __func__, ep);
@@ -176,7 +183,6 @@
else
skb_put(skb, recv_length - skb->len);
- atomic_inc(&adapter->rx_pending);
status = mwifiex_usb_recv(adapter, skb, context->ep);
dev_dbg(adapter->dev, "info: recv_length=%d, status=%d\n",
@@ -191,7 +197,6 @@
if (card->rx_cmd_ep == context->ep)
return;
} else {
- atomic_dec(&adapter->rx_pending);
if (status == -1)
dev_err(adapter->dev,
"received data processing failed!\n");
@@ -222,7 +227,13 @@
else
size = MWIFIEX_RX_DATA_BUF_SIZE;
- mwifiex_usb_submit_rx_urb(context, size);
+ if (card->rx_cmd_ep == context->ep) {
+ mwifiex_usb_submit_rx_urb(context, size);
+ } else {
+ context->skb = NULL;
+ if (atomic_read(&adapter->rx_pending) <= HIGH_RX_PENDING)
+ mwifiex_usb_submit_rx_urb(context, size);
+ }
return;
}
@@ -348,10 +359,12 @@
/* PID_1 is used for firmware downloading only */
switch (id_product) {
+ case USB8766_PID_1:
case USB8797_PID_1:
case USB8897_PID_1:
card->usb_boot_state = USB8XXX_FW_DNLD;
break;
+ case USB8766_PID_2:
case USB8797_PID_2:
case USB8897_PID_2:
card->usb_boot_state = USB8XXX_FW_READY;
@@ -780,6 +793,11 @@
adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K;
strcpy(adapter->fw_name, USB8897_DEFAULT_FW_NAME);
break;
+ case USB8766_PID_1:
+ case USB8766_PID_2:
+ adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K;
+ strcpy(adapter->fw_name, USB8766_DEFAULT_FW_NAME);
+ break;
case USB8797_PID_1:
case USB8797_PID_2:
default:
@@ -962,19 +980,11 @@
static int mwifiex_usb_cmd_event_complete(struct mwifiex_adapter *adapter,
struct sk_buff *skb)
{
- atomic_dec(&adapter->rx_pending);
mwifiex_submit_rx_urb(adapter, MWIFIEX_USB_EP_CMD_EVENT);
return 0;
}
-static int mwifiex_usb_data_complete(struct mwifiex_adapter *adapter)
-{
- atomic_dec(&adapter->rx_pending);
-
- return 0;
-}
-
/* This function wakes up the card. */
static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter)
{
@@ -986,6 +996,20 @@
return 0;
}
+static void mwifiex_usb_submit_rem_rx_urbs(struct mwifiex_adapter *adapter)
+{
+ struct usb_card_rec *card = (struct usb_card_rec *)adapter->card;
+ int i;
+ struct urb_context *ctx;
+
+ for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) {
+ if (card->rx_data_list[i].skb)
+ continue;
+ ctx = &card->rx_data_list[i];
+ mwifiex_usb_submit_rx_urb(ctx, MWIFIEX_RX_DATA_BUF_SIZE);
+ }
+}
+
static struct mwifiex_if_ops usb_ops = {
.register_dev = mwifiex_register_dev,
.unregister_dev = mwifiex_unregister_dev,
@@ -996,8 +1020,8 @@
.dnld_fw = mwifiex_usb_dnld_fw,
.cmdrsp_complete = mwifiex_usb_cmd_event_complete,
.event_complete = mwifiex_usb_cmd_event_complete,
- .data_complete = mwifiex_usb_data_complete,
.host_to_card = mwifiex_usb_host_to_card,
+ .submit_rem_rx_urbs = mwifiex_usb_submit_rem_rx_urbs,
};
/* This function initializes the USB driver module.
@@ -1048,5 +1072,6 @@
MODULE_DESCRIPTION("Marvell WiFi-Ex USB Driver version" USB_VERSION);
MODULE_VERSION(USB_VERSION);
MODULE_LICENSE("GPL v2");
+MODULE_FIRMWARE(USB8766_DEFAULT_FW_NAME);
MODULE_FIRMWARE(USB8797_DEFAULT_FW_NAME);
MODULE_FIRMWARE(USB8897_DEFAULT_FW_NAME);
diff --git a/drivers/net/wireless/mwifiex/usb.h b/drivers/net/wireless/mwifiex/usb.h
index 4c41c2a..a7cbba1 100644
--- a/drivers/net/wireless/mwifiex/usb.h
+++ b/drivers/net/wireless/mwifiex/usb.h
@@ -24,6 +24,8 @@
#define USB8XXX_VID 0x1286
+#define USB8766_PID_1 0x2041
+#define USB8766_PID_2 0x2042
#define USB8797_PID_1 0x2043
#define USB8797_PID_2 0x2044
#define USB8897_PID_1 0x2045
@@ -37,6 +39,7 @@
#define MWIFIEX_RX_DATA_URB 6
#define MWIFIEX_USB_TIMEOUT 100
+#define USB8766_DEFAULT_FW_NAME "mrvl/usb8766_uapsta.bin"
#define USB8797_DEFAULT_FW_NAME "mrvl/usb8797_uapsta.bin"
#define USB8897_DEFAULT_FW_NAME "mrvl/usb8897_uapsta.bin"
diff --git a/drivers/net/wireless/mwifiex/util.c b/drivers/net/wireless/mwifiex/util.c
index ec79c49..b1768fb 100644
--- a/drivers/net/wireless/mwifiex/util.c
+++ b/drivers/net/wireless/mwifiex/util.c
@@ -141,6 +141,38 @@
return 0;
}
+static int
+mwifiex_parse_mgmt_packet(struct mwifiex_private *priv, u8 *payload, u16 len,
+ struct rxpd *rx_pd)
+{
+ u16 stype;
+ u8 category, action_code;
+ struct ieee80211_hdr *ieee_hdr = (void *)payload;
+
+ stype = (le16_to_cpu(ieee_hdr->frame_control) & IEEE80211_FCTL_STYPE);
+
+ switch (stype) {
+ case IEEE80211_STYPE_ACTION:
+ category = *(payload + sizeof(struct ieee80211_hdr));
+ action_code = *(payload + sizeof(struct ieee80211_hdr) + 1);
+ if (category == WLAN_CATEGORY_PUBLIC &&
+ action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) {
+ dev_dbg(priv->adapter->dev,
+ "TDLS discovery response %pM nf=%d, snr=%d\n",
+ ieee_hdr->addr2, rx_pd->nf, rx_pd->snr);
+ mwifiex_auto_tdls_update_peer_signal(priv,
+ ieee_hdr->addr2,
+ rx_pd->snr,
+ rx_pd->nf);
+ }
+ break;
+ default:
+ dev_dbg(priv->adapter->dev,
+ "unknown mgmt frame subytpe %#x\n", stype);
+ }
+
+ return 0;
+}
/*
* This function processes the received management packet and send it
* to the kernel.
@@ -151,6 +183,7 @@
{
struct rxpd *rx_pd;
u16 pkt_len;
+ struct ieee80211_hdr *ieee_hdr;
if (!skb)
return -1;
@@ -162,6 +195,11 @@
pkt_len = le16_to_cpu(rx_pd->rx_pkt_length);
+ ieee_hdr = (void *)skb->data;
+ if (ieee80211_is_mgmt(ieee_hdr->frame_control)) {
+ mwifiex_parse_mgmt_packet(priv, (u8 *)ieee_hdr,
+ pkt_len, rx_pd);
+ }
/* Remove address4 */
memmove(skb->data + sizeof(struct ieee80211_hdr_3addr),
skb->data + sizeof(struct ieee80211_hdr),
diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c
index 94c98a8..ffffd2c 100644
--- a/drivers/net/wireless/mwifiex/wmm.c
+++ b/drivers/net/wireless/mwifiex/wmm.c
@@ -147,9 +147,6 @@
struct mwifiex_sta_node *node;
unsigned long flags;
- spin_lock_irqsave(&priv->sta_list_spinlock, flags);
- node = mwifiex_get_sta_entry(priv, ra);
- spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
for (i = 0; i < MAX_NUM_TID; ++i) {
ra_list = mwifiex_wmm_allocate_ralist_node(adapter, ra);
@@ -170,10 +167,13 @@
ra_list->is_11n_enabled = IS_11N_ENABLED(priv);
}
} else {
+ spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+ node = mwifiex_get_sta_entry(priv, ra);
ra_list->is_11n_enabled =
mwifiex_is_sta_11n_enabled(priv, node);
if (ra_list->is_11n_enabled)
ra_list->max_amsdu = node->max_amsdu;
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
}
dev_dbg(adapter->dev, "data: ralist %p: is_11n_enabled=%d\n",
@@ -523,6 +523,13 @@
}
}
+static int mwifiex_free_ack_frame(int id, void *p, void *data)
+{
+ pr_warn("Have pending ack frames!\n");
+ kfree_skb(p);
+ return 0;
+}
+
/*
* This function cleans up the Tx and Rx queues.
*
@@ -558,6 +565,9 @@
skb_queue_walk_safe(&priv->tdls_txq, skb, tmp)
mwifiex_write_data_complete(priv->adapter, skb, 0, -1);
+
+ idr_for_each(&priv->ack_status_frames, mwifiex_free_ack_frame, NULL);
+ idr_destroy(&priv->ack_status_frames);
}
/*
@@ -601,6 +611,32 @@
}
/*
+ * This function deletes RA list nodes for given mac for all TIDs.
+ * Function also decrements TX pending count accordingly.
+ */
+void
+mwifiex_wmm_del_peer_ra_list(struct mwifiex_private *priv, const u8 *ra_addr)
+{
+ struct mwifiex_ra_list_tbl *ra_list;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags);
+
+ for (i = 0; i < MAX_NUM_TID; ++i) {
+ ra_list = mwifiex_wmm_get_ralist_node(priv, i, ra_addr);
+
+ if (!ra_list)
+ continue;
+ mwifiex_wmm_del_pkts_in_ralist_node(priv, ra_list);
+ atomic_sub(ra_list->total_pkt_count, &priv->wmm.tx_pkts_queued);
+ list_del(&ra_list->list);
+ kfree(ra_list);
+ }
+ spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags);
+}
+
+/*
* This function checks if a particular RA list node exists in a given TID
* table index.
*/
diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c
index ef11044..b8d1e04 100644
--- a/drivers/net/wireless/mwl8k.c
+++ b/drivers/net/wireless/mwl8k.c
@@ -5548,7 +5548,9 @@
return rc;
}
-static void mwl8k_sw_scan_start(struct ieee80211_hw *hw)
+static void mwl8k_sw_scan_start(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ const u8 *mac_addr)
{
struct mwl8k_priv *priv = hw->priv;
u8 tmp;
@@ -5565,7 +5567,8 @@
priv->sw_scan_start = true;
}
-static void mwl8k_sw_scan_complete(struct ieee80211_hw *hw)
+static void mwl8k_sw_scan_complete(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
{
struct mwl8k_priv *priv = hw->priv;
u8 tmp;
diff --git a/drivers/net/wireless/p54/net2280.h b/drivers/net/wireless/p54/net2280.h
deleted file mode 100644
index aedfaf2..0000000
--- a/drivers/net/wireless/p54/net2280.h
+++ /dev/null
@@ -1,451 +0,0 @@
-#ifndef NET2280_H
-#define NET2280_H
-/*
- * NetChip 2280 high/full speed USB device controller.
- * Unlike many such controllers, this one talks PCI.
- */
-
-/*
- * Copyright (C) 2002 NetChip Technology, Inc. (http://www.netchip.com)
- * Copyright (C) 2003 David Brownell
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-/*-------------------------------------------------------------------------*/
-
-/* NET2280 MEMORY MAPPED REGISTERS
- *
- * The register layout came from the chip documentation, and the bit
- * number definitions were extracted from chip specification.
- *
- * Use the shift operator ('<<') to build bit masks, with readl/writel
- * to access the registers through PCI.
- */
-
-/* main registers, BAR0 + 0x0000 */
-struct net2280_regs {
- /* offset 0x0000 */
- __le32 devinit;
-#define LOCAL_CLOCK_FREQUENCY 8
-#define FORCE_PCI_RESET 7
-#define PCI_ID 6
-#define PCI_ENABLE 5
-#define FIFO_SOFT_RESET 4
-#define CFG_SOFT_RESET 3
-#define PCI_SOFT_RESET 2
-#define USB_SOFT_RESET 1
-#define M8051_RESET 0
- __le32 eectl;
-#define EEPROM_ADDRESS_WIDTH 23
-#define EEPROM_CHIP_SELECT_ACTIVE 22
-#define EEPROM_PRESENT 21
-#define EEPROM_VALID 20
-#define EEPROM_BUSY 19
-#define EEPROM_CHIP_SELECT_ENABLE 18
-#define EEPROM_BYTE_READ_START 17
-#define EEPROM_BYTE_WRITE_START 16
-#define EEPROM_READ_DATA 8
-#define EEPROM_WRITE_DATA 0
- __le32 eeclkfreq;
- u32 _unused0;
- /* offset 0x0010 */
-
- __le32 pciirqenb0; /* interrupt PCI master ... */
-#define SETUP_PACKET_INTERRUPT_ENABLE 7
-#define ENDPOINT_F_INTERRUPT_ENABLE 6
-#define ENDPOINT_E_INTERRUPT_ENABLE 5
-#define ENDPOINT_D_INTERRUPT_ENABLE 4
-#define ENDPOINT_C_INTERRUPT_ENABLE 3
-#define ENDPOINT_B_INTERRUPT_ENABLE 2
-#define ENDPOINT_A_INTERRUPT_ENABLE 1
-#define ENDPOINT_0_INTERRUPT_ENABLE 0
- __le32 pciirqenb1;
-#define PCI_INTERRUPT_ENABLE 31
-#define POWER_STATE_CHANGE_INTERRUPT_ENABLE 27
-#define PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE 26
-#define PCI_PARITY_ERROR_INTERRUPT_ENABLE 25
-#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE 20
-#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE 19
-#define PCI_TARGET_ABORT_ASSERTED_INTERRUPT_ENABLE 18
-#define PCI_RETRY_ABORT_INTERRUPT_ENABLE 17
-#define PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE 16
-#define GPIO_INTERRUPT_ENABLE 13
-#define DMA_D_INTERRUPT_ENABLE 12
-#define DMA_C_INTERRUPT_ENABLE 11
-#define DMA_B_INTERRUPT_ENABLE 10
-#define DMA_A_INTERRUPT_ENABLE 9
-#define EEPROM_DONE_INTERRUPT_ENABLE 8
-#define VBUS_INTERRUPT_ENABLE 7
-#define CONTROL_STATUS_INTERRUPT_ENABLE 6
-#define ROOT_PORT_RESET_INTERRUPT_ENABLE 4
-#define SUSPEND_REQUEST_INTERRUPT_ENABLE 3
-#define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE 2
-#define RESUME_INTERRUPT_ENABLE 1
-#define SOF_INTERRUPT_ENABLE 0
- __le32 cpu_irqenb0; /* ... or onboard 8051 */
-#define SETUP_PACKET_INTERRUPT_ENABLE 7
-#define ENDPOINT_F_INTERRUPT_ENABLE 6
-#define ENDPOINT_E_INTERRUPT_ENABLE 5
-#define ENDPOINT_D_INTERRUPT_ENABLE 4
-#define ENDPOINT_C_INTERRUPT_ENABLE 3
-#define ENDPOINT_B_INTERRUPT_ENABLE 2
-#define ENDPOINT_A_INTERRUPT_ENABLE 1
-#define ENDPOINT_0_INTERRUPT_ENABLE 0
- __le32 cpu_irqenb1;
-#define CPU_INTERRUPT_ENABLE 31
-#define POWER_STATE_CHANGE_INTERRUPT_ENABLE 27
-#define PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE 26
-#define PCI_PARITY_ERROR_INTERRUPT_ENABLE 25
-#define PCI_INTA_INTERRUPT_ENABLE 24
-#define PCI_PME_INTERRUPT_ENABLE 23
-#define PCI_SERR_INTERRUPT_ENABLE 22
-#define PCI_PERR_INTERRUPT_ENABLE 21
-#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE 20
-#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE 19
-#define PCI_RETRY_ABORT_INTERRUPT_ENABLE 17
-#define PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE 16
-#define GPIO_INTERRUPT_ENABLE 13
-#define DMA_D_INTERRUPT_ENABLE 12
-#define DMA_C_INTERRUPT_ENABLE 11
-#define DMA_B_INTERRUPT_ENABLE 10
-#define DMA_A_INTERRUPT_ENABLE 9
-#define EEPROM_DONE_INTERRUPT_ENABLE 8
-#define VBUS_INTERRUPT_ENABLE 7
-#define CONTROL_STATUS_INTERRUPT_ENABLE 6
-#define ROOT_PORT_RESET_INTERRUPT_ENABLE 4
-#define SUSPEND_REQUEST_INTERRUPT_ENABLE 3
-#define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE 2
-#define RESUME_INTERRUPT_ENABLE 1
-#define SOF_INTERRUPT_ENABLE 0
-
- /* offset 0x0020 */
- u32 _unused1;
- __le32 usbirqenb1;
-#define USB_INTERRUPT_ENABLE 31
-#define POWER_STATE_CHANGE_INTERRUPT_ENABLE 27
-#define PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE 26
-#define PCI_PARITY_ERROR_INTERRUPT_ENABLE 25
-#define PCI_INTA_INTERRUPT_ENABLE 24
-#define PCI_PME_INTERRUPT_ENABLE 23
-#define PCI_SERR_INTERRUPT_ENABLE 22
-#define PCI_PERR_INTERRUPT_ENABLE 21
-#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE 20
-#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE 19
-#define PCI_RETRY_ABORT_INTERRUPT_ENABLE 17
-#define PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE 16
-#define GPIO_INTERRUPT_ENABLE 13
-#define DMA_D_INTERRUPT_ENABLE 12
-#define DMA_C_INTERRUPT_ENABLE 11
-#define DMA_B_INTERRUPT_ENABLE 10
-#define DMA_A_INTERRUPT_ENABLE 9
-#define EEPROM_DONE_INTERRUPT_ENABLE 8
-#define VBUS_INTERRUPT_ENABLE 7
-#define CONTROL_STATUS_INTERRUPT_ENABLE 6
-#define ROOT_PORT_RESET_INTERRUPT_ENABLE 4
-#define SUSPEND_REQUEST_INTERRUPT_ENABLE 3
-#define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE 2
-#define RESUME_INTERRUPT_ENABLE 1
-#define SOF_INTERRUPT_ENABLE 0
- __le32 irqstat0;
-#define INTA_ASSERTED 12
-#define SETUP_PACKET_INTERRUPT 7
-#define ENDPOINT_F_INTERRUPT 6
-#define ENDPOINT_E_INTERRUPT 5
-#define ENDPOINT_D_INTERRUPT 4
-#define ENDPOINT_C_INTERRUPT 3
-#define ENDPOINT_B_INTERRUPT 2
-#define ENDPOINT_A_INTERRUPT 1
-#define ENDPOINT_0_INTERRUPT 0
- __le32 irqstat1;
-#define POWER_STATE_CHANGE_INTERRUPT 27
-#define PCI_ARBITER_TIMEOUT_INTERRUPT 26
-#define PCI_PARITY_ERROR_INTERRUPT 25
-#define PCI_INTA_INTERRUPT 24
-#define PCI_PME_INTERRUPT 23
-#define PCI_SERR_INTERRUPT 22
-#define PCI_PERR_INTERRUPT 21
-#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT 20
-#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT 19
-#define PCI_RETRY_ABORT_INTERRUPT 17
-#define PCI_MASTER_CYCLE_DONE_INTERRUPT 16
-#define GPIO_INTERRUPT 13
-#define DMA_D_INTERRUPT 12
-#define DMA_C_INTERRUPT 11
-#define DMA_B_INTERRUPT 10
-#define DMA_A_INTERRUPT 9
-#define EEPROM_DONE_INTERRUPT 8
-#define VBUS_INTERRUPT 7
-#define CONTROL_STATUS_INTERRUPT 6
-#define ROOT_PORT_RESET_INTERRUPT 4
-#define SUSPEND_REQUEST_INTERRUPT 3
-#define SUSPEND_REQUEST_CHANGE_INTERRUPT 2
-#define RESUME_INTERRUPT 1
-#define SOF_INTERRUPT 0
- /* offset 0x0030 */
- __le32 idxaddr;
- __le32 idxdata;
- __le32 fifoctl;
-#define PCI_BASE2_RANGE 16
-#define IGNORE_FIFO_AVAILABILITY 3
-#define PCI_BASE2_SELECT 2
-#define FIFO_CONFIGURATION_SELECT 0
- u32 _unused2;
- /* offset 0x0040 */
- __le32 memaddr;
-#define START 28
-#define DIRECTION 27
-#define FIFO_DIAGNOSTIC_SELECT 24
-#define MEMORY_ADDRESS 0
- __le32 memdata0;
- __le32 memdata1;
- u32 _unused3;
- /* offset 0x0050 */
- __le32 gpioctl;
-#define GPIO3_LED_SELECT 12
-#define GPIO3_INTERRUPT_ENABLE 11
-#define GPIO2_INTERRUPT_ENABLE 10
-#define GPIO1_INTERRUPT_ENABLE 9
-#define GPIO0_INTERRUPT_ENABLE 8
-#define GPIO3_OUTPUT_ENABLE 7
-#define GPIO2_OUTPUT_ENABLE 6
-#define GPIO1_OUTPUT_ENABLE 5
-#define GPIO0_OUTPUT_ENABLE 4
-#define GPIO3_DATA 3
-#define GPIO2_DATA 2
-#define GPIO1_DATA 1
-#define GPIO0_DATA 0
- __le32 gpiostat;
-#define GPIO3_INTERRUPT 3
-#define GPIO2_INTERRUPT 2
-#define GPIO1_INTERRUPT 1
-#define GPIO0_INTERRUPT 0
-} __packed;
-
-/* usb control, BAR0 + 0x0080 */
-struct net2280_usb_regs {
- /* offset 0x0080 */
- __le32 stdrsp;
-#define STALL_UNSUPPORTED_REQUESTS 31
-#define SET_TEST_MODE 16
-#define GET_OTHER_SPEED_CONFIGURATION 15
-#define GET_DEVICE_QUALIFIER 14
-#define SET_ADDRESS 13
-#define ENDPOINT_SET_CLEAR_HALT 12
-#define DEVICE_SET_CLEAR_DEVICE_REMOTE_WAKEUP 11
-#define GET_STRING_DESCRIPTOR_2 10
-#define GET_STRING_DESCRIPTOR_1 9
-#define GET_STRING_DESCRIPTOR_0 8
-#define GET_SET_INTERFACE 6
-#define GET_SET_CONFIGURATION 5
-#define GET_CONFIGURATION_DESCRIPTOR 4
-#define GET_DEVICE_DESCRIPTOR 3
-#define GET_ENDPOINT_STATUS 2
-#define GET_INTERFACE_STATUS 1
-#define GET_DEVICE_STATUS 0
- __le32 prodvendid;
-#define PRODUCT_ID 16
-#define VENDOR_ID 0
- __le32 relnum;
- __le32 usbctl;
-#define SERIAL_NUMBER_INDEX 16
-#define PRODUCT_ID_STRING_ENABLE 13
-#define VENDOR_ID_STRING_ENABLE 12
-#define USB_ROOT_PORT_WAKEUP_ENABLE 11
-#define VBUS_PIN 10
-#define TIMED_DISCONNECT 9
-#define SUSPEND_IMMEDIATELY 7
-#define SELF_POWERED_USB_DEVICE 6
-#define REMOTE_WAKEUP_SUPPORT 5
-#define PME_POLARITY 4
-#define USB_DETECT_ENABLE 3
-#define PME_WAKEUP_ENABLE 2
-#define DEVICE_REMOTE_WAKEUP_ENABLE 1
-#define SELF_POWERED_STATUS 0
- /* offset 0x0090 */
- __le32 usbstat;
-#define HIGH_SPEED 7
-#define FULL_SPEED 6
-#define GENERATE_RESUME 5
-#define GENERATE_DEVICE_REMOTE_WAKEUP 4
- __le32 xcvrdiag;
-#define FORCE_HIGH_SPEED_MODE 31
-#define FORCE_FULL_SPEED_MODE 30
-#define USB_TEST_MODE 24
-#define LINE_STATE 16
-#define TRANSCEIVER_OPERATION_MODE 2
-#define TRANSCEIVER_SELECT 1
-#define TERMINATION_SELECT 0
- __le32 setup0123;
- __le32 setup4567;
- /* offset 0x0090 */
- u32 _unused0;
- __le32 ouraddr;
-#define FORCE_IMMEDIATE 7
-#define OUR_USB_ADDRESS 0
- __le32 ourconfig;
-} __packed;
-
-/* pci control, BAR0 + 0x0100 */
-struct net2280_pci_regs {
- /* offset 0x0100 */
- __le32 pcimstctl;
-#define PCI_ARBITER_PARK_SELECT 13
-#define PCI_MULTI LEVEL_ARBITER 12
-#define PCI_RETRY_ABORT_ENABLE 11
-#define DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE 10
-#define DMA_READ_MULTIPLE_ENABLE 9
-#define DMA_READ_LINE_ENABLE 8
-#define PCI_MASTER_COMMAND_SELECT 6
-#define MEM_READ_OR_WRITE 0
-#define IO_READ_OR_WRITE 1
-#define CFG_READ_OR_WRITE 2
-#define PCI_MASTER_START 5
-#define PCI_MASTER_READ_WRITE 4
-#define PCI_MASTER_WRITE 0
-#define PCI_MASTER_READ 1
-#define PCI_MASTER_BYTE_WRITE_ENABLES 0
- __le32 pcimstaddr;
- __le32 pcimstdata;
- __le32 pcimststat;
-#define PCI_ARBITER_CLEAR 2
-#define PCI_EXTERNAL_ARBITER 1
-#define PCI_HOST_MODE 0
-} __packed;
-
-/* dma control, BAR0 + 0x0180 ... array of four structs like this,
- * for channels 0..3. see also struct net2280_dma: descriptor
- * that can be loaded into some of these registers.
- */
-struct net2280_dma_regs { /* [11.7] */
- /* offset 0x0180, 0x01a0, 0x01c0, 0x01e0, */
- __le32 dmactl;
-#define DMA_SCATTER_GATHER_DONE_INTERRUPT_ENABLE 25
-#define DMA_CLEAR_COUNT_ENABLE 21
-#define DESCRIPTOR_POLLING_RATE 19
-#define POLL_CONTINUOUS 0
-#define POLL_1_USEC 1
-#define POLL_100_USEC 2
-#define POLL_1_MSEC 3
-#define DMA_VALID_BIT_POLLING_ENABLE 18
-#define DMA_VALID_BIT_ENABLE 17
-#define DMA_SCATTER_GATHER_ENABLE 16
-#define DMA_OUT_AUTO_START_ENABLE 4
-#define DMA_PREEMPT_ENABLE 3
-#define DMA_FIFO_VALIDATE 2
-#define DMA_ENABLE 1
-#define DMA_ADDRESS_HOLD 0
- __le32 dmastat;
-#define DMA_SCATTER_GATHER_DONE_INTERRUPT 25
-#define DMA_TRANSACTION_DONE_INTERRUPT 24
-#define DMA_ABORT 1
-#define DMA_START 0
- u32 _unused0[2];
- /* offset 0x0190, 0x01b0, 0x01d0, 0x01f0, */
- __le32 dmacount;
-#define VALID_BIT 31
-#define DMA_DIRECTION 30
-#define DMA_DONE_INTERRUPT_ENABLE 29
-#define END_OF_CHAIN 28
-#define DMA_BYTE_COUNT_MASK ((1<<24)-1)
-#define DMA_BYTE_COUNT 0
- __le32 dmaaddr;
- __le32 dmadesc;
- u32 _unused1;
-} __packed;
-
-/* dedicated endpoint registers, BAR0 + 0x0200 */
-
-struct net2280_dep_regs { /* [11.8] */
- /* offset 0x0200, 0x0210, 0x220, 0x230, 0x240 */
- __le32 dep_cfg;
- /* offset 0x0204, 0x0214, 0x224, 0x234, 0x244 */
- __le32 dep_rsp;
- u32 _unused[2];
-} __packed;
-
-/* configurable endpoint registers, BAR0 + 0x0300 ... array of seven structs
- * like this, for ep0 then the configurable endpoints A..F
- * ep0 reserved for control; E and F have only 64 bytes of fifo
- */
-struct net2280_ep_regs { /* [11.9] */
- /* offset 0x0300, 0x0320, 0x0340, 0x0360, 0x0380, 0x03a0, 0x03c0 */
- __le32 ep_cfg;
-#define ENDPOINT_BYTE_COUNT 16
-#define ENDPOINT_ENABLE 10
-#define ENDPOINT_TYPE 8
-#define ENDPOINT_DIRECTION 7
-#define ENDPOINT_NUMBER 0
- __le32 ep_rsp;
-#define SET_NAK_OUT_PACKETS 15
-#define SET_EP_HIDE_STATUS_PHASE 14
-#define SET_EP_FORCE_CRC_ERROR 13
-#define SET_INTERRUPT_MODE 12
-#define SET_CONTROL_STATUS_PHASE_HANDSHAKE 11
-#define SET_NAK_OUT_PACKETS_MODE 10
-#define SET_ENDPOINT_TOGGLE 9
-#define SET_ENDPOINT_HALT 8
-#define CLEAR_NAK_OUT_PACKETS 7
-#define CLEAR_EP_HIDE_STATUS_PHASE 6
-#define CLEAR_EP_FORCE_CRC_ERROR 5
-#define CLEAR_INTERRUPT_MODE 4
-#define CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE 3
-#define CLEAR_NAK_OUT_PACKETS_MODE 2
-#define CLEAR_ENDPOINT_TOGGLE 1
-#define CLEAR_ENDPOINT_HALT 0
- __le32 ep_irqenb;
-#define SHORT_PACKET_OUT_DONE_INTERRUPT_ENABLE 6
-#define SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE 5
-#define DATA_PACKET_RECEIVED_INTERRUPT_ENABLE 3
-#define DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE 2
-#define DATA_OUT_PING_TOKEN_INTERRUPT_ENABLE 1
-#define DATA_IN_TOKEN_INTERRUPT_ENABLE 0
- __le32 ep_stat;
-#define FIFO_VALID_COUNT 24
-#define HIGH_BANDWIDTH_OUT_TRANSACTION_PID 22
-#define TIMEOUT 21
-#define USB_STALL_SENT 20
-#define USB_IN_NAK_SENT 19
-#define USB_IN_ACK_RCVD 18
-#define USB_OUT_PING_NAK_SENT 17
-#define USB_OUT_ACK_SENT 16
-#define FIFO_OVERFLOW 13
-#define FIFO_UNDERFLOW 12
-#define FIFO_FULL 11
-#define FIFO_EMPTY 10
-#define FIFO_FLUSH 9
-#define SHORT_PACKET_OUT_DONE_INTERRUPT 6
-#define SHORT_PACKET_TRANSFERRED_INTERRUPT 5
-#define NAK_OUT_PACKETS 4
-#define DATA_PACKET_RECEIVED_INTERRUPT 3
-#define DATA_PACKET_TRANSMITTED_INTERRUPT 2
-#define DATA_OUT_PING_TOKEN_INTERRUPT 1
-#define DATA_IN_TOKEN_INTERRUPT 0
- /* offset 0x0310, 0x0330, 0x0350, 0x0370, 0x0390, 0x03b0, 0x03d0 */
- __le32 ep_avail;
- __le32 ep_data;
- u32 _unused0[2];
-} __packed;
-
-struct net2280_reg_write {
- __le16 port;
- __le32 addr;
- __le32 val;
-} __packed;
-
-struct net2280_reg_read {
- __le16 port;
- __le32 addr;
-} __packed;
-#endif /* NET2280_H */
diff --git a/drivers/net/wireless/p54/p54usb.h b/drivers/net/wireless/p54/p54usb.h
index d273be7..a5f5f0f 100644
--- a/drivers/net/wireless/p54/p54usb.h
+++ b/drivers/net/wireless/p54/p54usb.h
@@ -16,7 +16,7 @@
/* for isl3886 register definitions used on ver 1 devices */
#include "p54pci.h"
-#include "net2280.h"
+#include <linux/usb/net2280.h>
/* pci */
#define NET2280_BASE 0x10000000
@@ -93,6 +93,17 @@
NET2280_DEV_CFG_U16 = 0x0883
};
+struct net2280_reg_write {
+ __le16 port;
+ __le32 addr;
+ __le32 val;
+} __packed;
+
+struct net2280_reg_read {
+ __le16 port;
+ __le32 addr;
+} __packed;
+
#define P54U_FW_BLOCK 2048
#define X2_SIGNATURE "x2 "
diff --git a/drivers/net/wireless/rt2x00/Kconfig b/drivers/net/wireless/rt2x00/Kconfig
index 7d0df2b..b6c87b7 100644
--- a/drivers/net/wireless/rt2x00/Kconfig
+++ b/drivers/net/wireless/rt2x00/Kconfig
@@ -256,7 +256,7 @@
config RT2X00_LIB
tristate
depends on m
- select BACKPORT_AVERAGE
+ select BPAUTO_AVERAGE
config RT2X00_LIB_FIRMWARE
boolean
diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c
index bcee58c..8f446c1 100644
--- a/drivers/net/wireless/rt2x00/rt2500usb.c
+++ b/drivers/net/wireless/rt2x00/rt2500usb.c
@@ -47,7 +47,7 @@
* BBP and RF register require indirect register access,
* and use the CSR registers BBPCSR and RFCSR to achieve this.
* These indirect registers work with busy bits,
- * and we will try maximal REGISTER_BUSY_COUNT times to access
+ * and we will try maximal REGISTER_USB_BUSY_COUNT times to access
* the register while taking a REGISTER_BUSY_DELAY us delay
* between each attampt. When the busy bit is still set at that time,
* the access attempt is considered to have failed,
@@ -62,7 +62,7 @@
__le16 reg;
rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ,
USB_VENDOR_REQUEST_IN, offset,
- ®, sizeof(reg), REGISTER_TIMEOUT);
+ ®, sizeof(reg));
*value = le16_to_cpu(reg);
}
@@ -83,8 +83,7 @@
{
rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ,
USB_VENDOR_REQUEST_IN, offset,
- value, length,
- REGISTER_TIMEOUT16(length));
+ value, length);
}
static inline void rt2500usb_register_write(struct rt2x00_dev *rt2x00dev,
@@ -94,7 +93,7 @@
__le16 reg = cpu_to_le16(value);
rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE,
USB_VENDOR_REQUEST_OUT, offset,
- ®, sizeof(reg), REGISTER_TIMEOUT);
+ ®, sizeof(reg));
}
static inline void rt2500usb_register_write_lock(struct rt2x00_dev *rt2x00dev,
@@ -113,8 +112,7 @@
{
rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE,
USB_VENDOR_REQUEST_OUT, offset,
- value, length,
- REGISTER_TIMEOUT16(length));
+ value, length);
}
static int rt2500usb_regbusy_read(struct rt2x00_dev *rt2x00dev,
@@ -124,7 +122,7 @@
{
unsigned int i;
- for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+ for (i = 0; i < REGISTER_USB_BUSY_COUNT; i++) {
rt2500usb_register_read_lock(rt2x00dev, offset, reg);
if (!rt2x00_get_field16(*reg, field))
return 1;
@@ -906,7 +904,7 @@
unsigned int i;
u8 value;
- for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+ for (i = 0; i < REGISTER_USB_BUSY_COUNT; i++) {
rt2500usb_bbp_read(rt2x00dev, 0, &value);
if ((value != 0xff) && (value != 0x00))
return 0;
@@ -1025,7 +1023,7 @@
* We must wait until the register indicates that the
* device has entered the correct state.
*/
- for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+ for (i = 0; i < REGISTER_USB_BUSY_COUNT; i++) {
rt2500usb_register_read(rt2x00dev, MAC_CSR17, ®2);
bbp_state = rt2x00_get_field16(reg2, MAC_CSR17_BBP_CURR_STATE);
rf_state = rt2x00_get_field16(reg2, MAC_CSR17_RF_CURR_STATE);
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index 5ad3899..b12c863 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -4119,7 +4119,20 @@
* expected. We adjust it, based on TSSI reference and boundaries values
* provided in EEPROM.
*/
- delta += rt2800_get_gain_calibration_delta(rt2x00dev);
+ switch (rt2x00dev->chip.rt) {
+ case RT2860:
+ case RT2872:
+ case RT2883:
+ case RT3070:
+ case RT3071:
+ case RT3090:
+ case RT3572:
+ delta += rt2800_get_gain_calibration_delta(rt2x00dev);
+ break;
+ default:
+ /* TODO: temperature compensation code for other chips. */
+ break;
+ }
/*
* Decrease power according to user settings, on devices with unknown
@@ -4136,25 +4149,19 @@
* TODO: we do not use +6 dBm option to do not increase power beyond
* regulatory limit, however this could be utilized for devices with
* CAPABILITY_POWER_LIMIT.
- *
- * TODO: add different temperature compensation code for RT3290 & RT5390
- * to allow to use BBP_R1 for those chips.
*/
- if (!rt2x00_rt(rt2x00dev, RT3290) &&
- !rt2x00_rt(rt2x00dev, RT5390)) {
- rt2800_bbp_read(rt2x00dev, 1, &r1);
- if (delta <= -12) {
- power_ctrl = 2;
- delta += 12;
- } else if (delta <= -6) {
- power_ctrl = 1;
- delta += 6;
- } else {
- power_ctrl = 0;
- }
- rt2x00_set_field8(&r1, BBP1_TX_POWER_CTRL, power_ctrl);
- rt2800_bbp_write(rt2x00dev, 1, r1);
+ if (delta <= -12) {
+ power_ctrl = 2;
+ delta += 12;
+ } else if (delta <= -6) {
+ power_ctrl = 1;
+ delta += 6;
+ } else {
+ power_ctrl = 0;
}
+ rt2800_bbp_read(rt2x00dev, 1, &r1);
+ rt2x00_set_field8(&r1, BBP1_TX_POWER_CTRL, power_ctrl);
+ rt2800_bbp_write(rt2x00dev, 1, r1);
offset = TX_PWR_CFG_0;
diff --git a/drivers/net/wireless/rt2x00/rt2800soc.c b/drivers/net/wireless/rt2x00/rt2800soc.c
index 71ccac7..e6d747f 100644
--- a/drivers/net/wireless/rt2x00/rt2800soc.c
+++ b/drivers/net/wireless/rt2x00/rt2800soc.c
@@ -244,7 +244,6 @@
static struct platform_driver rt2800soc_driver = {
.driver = {
.name = "rt2800_wmac",
- .owner = THIS_MODULE,
.mod_name = KBUILD_MODNAME,
},
.probe = rt2800soc_probe,
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c
index 7737af9..df74814 100644
--- a/drivers/net/wireless/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/rt2x00/rt2800usb.c
@@ -1111,6 +1111,7 @@
/* Ovislink */
{ USB_DEVICE(0x1b75, 0x3071) },
{ USB_DEVICE(0x1b75, 0x3072) },
+ { USB_DEVICE(0x1b75, 0xa200) },
/* Para */
{ USB_DEVICE(0x20b8, 0x8888) },
/* Pegatron */
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h
index 57b2509..caa6400 100644
--- a/drivers/net/wireless/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/rt2x00/rt2x00.h
@@ -1019,9 +1019,12 @@
* Register defines.
* Some registers require multiple attempts before success,
* in those cases REGISTER_BUSY_COUNT attempts should be
- * taken with a REGISTER_BUSY_DELAY interval.
+ * taken with a REGISTER_BUSY_DELAY interval. Due to USB
+ * bus delays, we do not have to loop so many times to wait
+ * for valid register value on that bus.
*/
#define REGISTER_BUSY_COUNT 100
+#define REGISTER_USB_BUSY_COUNT 20
#define REGISTER_BUSY_DELAY 100
/*
@@ -1437,8 +1440,11 @@
struct ieee80211_sta *sta);
int rt2x00mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta);
-void rt2x00mac_sw_scan_start(struct ieee80211_hw *hw);
-void rt2x00mac_sw_scan_complete(struct ieee80211_hw *hw);
+void rt2x00mac_sw_scan_start(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ const u8 *mac_addr);
+void rt2x00mac_sw_scan_complete(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
int rt2x00mac_get_stats(struct ieee80211_hw *hw,
struct ieee80211_low_level_stats *stats);
void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw,
diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c
index 9206634..7f9e471 100644
--- a/drivers/net/wireless/rt2x00/rt2x00mac.c
+++ b/drivers/net/wireless/rt2x00/rt2x00mac.c
@@ -568,7 +568,9 @@
}
EXPORT_SYMBOL_GPL(rt2x00mac_sta_remove);
-void rt2x00mac_sw_scan_start(struct ieee80211_hw *hw)
+void rt2x00mac_sw_scan_start(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ const u8 *mac_addr)
{
struct rt2x00_dev *rt2x00dev = hw->priv;
set_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags);
@@ -576,7 +578,8 @@
}
EXPORT_SYMBOL_GPL(rt2x00mac_sw_scan_start);
-void rt2x00mac_sw_scan_complete(struct ieee80211_hw *hw)
+void rt2x00mac_sw_scan_complete(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
{
struct rt2x00_dev *rt2x00dev = hw->priv;
clear_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags);
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c
index 8e68f87..66ff364 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.c
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.c
@@ -158,55 +158,29 @@
skb_trim(skb, frame_length);
}
-void rt2x00queue_insert_l2pad(struct sk_buff *skb, unsigned int header_length)
+/*
+ * H/W needs L2 padding between the header and the paylod if header size
+ * is not 4 bytes aligned.
+ */
+void rt2x00queue_insert_l2pad(struct sk_buff *skb, unsigned int hdr_len)
{
- unsigned int payload_length = skb->len - header_length;
- unsigned int header_align = ALIGN_SIZE(skb, 0);
- unsigned int payload_align = ALIGN_SIZE(skb, header_length);
- unsigned int l2pad = payload_length ? L2PAD_SIZE(header_length) : 0;
-
- /*
- * Adjust the header alignment if the payload needs to be moved more
- * than the header.
- */
- if (payload_align > header_align)
- header_align += 4;
-
- /* There is nothing to do if no alignment is needed */
- if (!header_align)
- return;
-
- /* Reserve the amount of space needed in front of the frame */
- skb_push(skb, header_align);
-
- /*
- * Move the header.
- */
- memmove(skb->data, skb->data + header_align, header_length);
-
- /* Move the payload, if present and if required */
- if (payload_length && payload_align)
- memmove(skb->data + header_length + l2pad,
- skb->data + header_length + l2pad + payload_align,
- payload_length);
-
- /* Trim the skb to the correct size */
- skb_trim(skb, header_length + l2pad + payload_length);
-}
-
-void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int header_length)
-{
- /*
- * L2 padding is only present if the skb contains more than just the
- * IEEE 802.11 header.
- */
- unsigned int l2pad = (skb->len > header_length) ?
- L2PAD_SIZE(header_length) : 0;
+ unsigned int l2pad = (skb->len > hdr_len) ? L2PAD_SIZE(hdr_len) : 0;
if (!l2pad)
return;
- memmove(skb->data + l2pad, skb->data, header_length);
+ skb_push(skb, l2pad);
+ memmove(skb->data, skb->data + l2pad, hdr_len);
+}
+
+void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int hdr_len)
+{
+ unsigned int l2pad = (skb->len > hdr_len) ? L2PAD_SIZE(hdr_len) : 0;
+
+ if (!l2pad)
+ return;
+
+ memmove(skb->data + l2pad, skb->data, hdr_len);
skb_pull(skb, l2pad);
}
diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c
index 86c43d1..892270d 100644
--- a/drivers/net/wireless/rt2x00/rt2x00usb.c
+++ b/drivers/net/wireless/rt2x00/rt2x00usb.c
@@ -42,37 +42,27 @@
{
struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev);
int status;
- unsigned int i;
unsigned int pipe =
(requesttype == USB_VENDOR_REQUEST_IN) ?
usb_rcvctrlpipe(usb_dev, 0) : usb_sndctrlpipe(usb_dev, 0);
+ unsigned long expire = jiffies + msecs_to_jiffies(timeout);
if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
return -ENODEV;
- for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+ do {
status = usb_control_msg(usb_dev, pipe, request, requesttype,
value, offset, buffer, buffer_length,
- timeout);
+ timeout / 2);
if (status >= 0)
return 0;
- /*
- * Check for errors
- * -ENODEV: Device has disappeared, no point continuing.
- * All other errors: Try again.
- */
- else if (status == -ENODEV) {
+ if (status == -ENODEV) {
+ /* Device has disappeared. */
clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags);
break;
}
- }
-
- /* If the port is powered down, we get a -EPROTO error, and this
- * leads to a endless loop. So just say that the device is gone.
- */
- if (status == -EPROTO)
- clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags);
+ } while (time_before(jiffies, expire));
rt2x00_err(rt2x00dev,
"Vendor Request 0x%02x failed for offset 0x%04x with error %d\n",
@@ -116,7 +106,7 @@
int rt2x00usb_vendor_request_buff(struct rt2x00_dev *rt2x00dev,
const u8 request, const u8 requesttype,
const u16 offset, void *buffer,
- const u16 buffer_length, const int timeout)
+ const u16 buffer_length)
{
int status = 0;
unsigned char *tb;
@@ -131,7 +121,7 @@
bsize = min_t(u16, CSR_CACHE_SIZE, len);
status = rt2x00usb_vendor_req_buff_lock(rt2x00dev, request,
requesttype, off, tb,
- bsize, timeout);
+ bsize, REGISTER_TIMEOUT);
tb += bsize;
len -= bsize;
@@ -154,7 +144,7 @@
if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
return -ENODEV;
- for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+ for (i = 0; i < REGISTER_USB_BUSY_COUNT; i++) {
rt2x00usb_register_read_lock(rt2x00dev, offset, reg);
if (!rt2x00_get_field32(*reg, field))
return 1;
diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.h b/drivers/net/wireless/rt2x00/rt2x00usb.h
index 831b65f..8f85fbd 100644
--- a/drivers/net/wireless/rt2x00/rt2x00usb.h
+++ b/drivers/net/wireless/rt2x00/rt2x00usb.h
@@ -33,27 +33,14 @@
})
/*
- * For USB vendor requests we need to pass a timeout
- * time in ms, for this we use the REGISTER_TIMEOUT,
- * however when loading firmware a higher value is
- * required. In that case we use the REGISTER_TIMEOUT_FIRMWARE.
+ * For USB vendor requests we need to pass a timeout time in ms, for this we
+ * use the REGISTER_TIMEOUT, however when loading firmware or read EEPROM
+ * a higher value is required. In that case we use the REGISTER_TIMEOUT_FIRMWARE
+ * and EEPROM_TIMEOUT.
*/
-#define REGISTER_TIMEOUT 500
+#define REGISTER_TIMEOUT 100
#define REGISTER_TIMEOUT_FIRMWARE 1000
-
-/**
- * REGISTER_TIMEOUT16 - Determine the timeout for 16bit register access
- * @__datalen: Data length
- */
-#define REGISTER_TIMEOUT16(__datalen) \
- ( REGISTER_TIMEOUT * ((__datalen) / sizeof(u16)) )
-
-/**
- * REGISTER_TIMEOUT32 - Determine the timeout for 32bit register access
- * @__datalen: Data length
- */
-#define REGISTER_TIMEOUT32(__datalen) \
- ( REGISTER_TIMEOUT * ((__datalen) / sizeof(u32)) )
+#define EEPROM_TIMEOUT 2000
/*
* Cache size
@@ -126,7 +113,6 @@
* @offset: Register offset to perform action on
* @buffer: Buffer where information will be read/written to by device
* @buffer_length: Size of &buffer
- * @timeout: Operation timeout
*
* This function will use a previously with kmalloc allocated cache
* to communicate with the device. The contents of the buffer pointer
@@ -139,7 +125,7 @@
int rt2x00usb_vendor_request_buff(struct rt2x00_dev *rt2x00dev,
const u8 request, const u8 requesttype,
const u16 offset, void *buffer,
- const u16 buffer_length, const int timeout);
+ const u16 buffer_length);
/**
* rt2x00usb_vendor_request_buff - Send register command to device (buffered)
@@ -197,8 +183,7 @@
{
return rt2x00usb_vendor_request(rt2x00dev, USB_EEPROM_READ,
USB_VENDOR_REQUEST_IN, 0, 0,
- eeprom, length,
- REGISTER_TIMEOUT16(length));
+ eeprom, length, EEPROM_TIMEOUT);
}
/**
@@ -217,7 +202,7 @@
__le32 reg;
rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ,
USB_VENDOR_REQUEST_IN, offset,
- ®, sizeof(reg), REGISTER_TIMEOUT);
+ ®, sizeof(reg));
*value = le32_to_cpu(reg);
}
@@ -257,8 +242,7 @@
{
rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_READ,
USB_VENDOR_REQUEST_IN, offset,
- value, length,
- REGISTER_TIMEOUT32(length));
+ value, length);
}
/**
@@ -277,7 +261,7 @@
__le32 reg = cpu_to_le32(value);
rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE,
USB_VENDOR_REQUEST_OUT, offset,
- ®, sizeof(reg), REGISTER_TIMEOUT);
+ ®, sizeof(reg));
}
/**
@@ -316,8 +300,7 @@
{
rt2x00usb_vendor_request_buff(rt2x00dev, USB_MULTI_WRITE,
USB_VENDOR_REQUEST_OUT, offset,
- (void *)value, length,
- REGISTER_TIMEOUT32(length));
+ (void *)value, length);
}
/**
diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c
index 80d48e9..b0a75d9 100644
--- a/drivers/net/wireless/rt2x00/rt73usb.c
+++ b/drivers/net/wireless/rt2x00/rt73usb.c
@@ -1295,7 +1295,7 @@
unsigned int i;
u8 value;
- for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+ for (i = 0; i < REGISTER_USB_BUSY_COUNT; i++) {
rt73usb_bbp_read(rt2x00dev, 0, &value);
if ((value != 0xff) && (value != 0x00))
return 0;
diff --git a/drivers/net/wireless/rtlwifi/base.c b/drivers/net/wireless/rtlwifi/base.c
index 58ba718..40b6d1d 100644
--- a/drivers/net/wireless/rtlwifi/base.c
+++ b/drivers/net/wireless/rtlwifi/base.c
@@ -467,7 +467,7 @@
rtl_easy_concurrent_retrytimer_callback, (unsigned long)hw);
/* <2> work queue */
rtlpriv->works.hw = hw;
- rtlpriv->works.rtl_wq = alloc_workqueue(rtlpriv->cfg->name, 0, 0);
+ rtlpriv->works.rtl_wq = alloc_workqueue("%s", 0, 0, rtlpriv->cfg->name);
INIT_DELAYED_WORK(&rtlpriv->works.watchdog_wq,
(void *)rtl_watchdog_wq_callback);
INIT_DELAYED_WORK(&rtlpriv->works.ips_nic_off_wq,
diff --git a/drivers/net/wireless/rtlwifi/core.c b/drivers/net/wireless/rtlwifi/core.c
index f6179bc..5fc6f52 100644
--- a/drivers/net/wireless/rtlwifi/core.c
+++ b/drivers/net/wireless/rtlwifi/core.c
@@ -786,6 +786,7 @@
unsigned int changed_flags,
unsigned int *new_flags, u64 multicast)
{
+ bool update_rcr = false;
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
@@ -806,6 +807,7 @@
RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD,
"Disable receive multicast frame\n");
}
+ update_rcr = true;
}
if (changed_flags & FIF_FCSFAIL) {
@@ -818,6 +820,8 @@
RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD,
"Disable receive FCS error frame\n");
}
+ if (!update_rcr)
+ update_rcr = true;
}
/* if ssid not set to hw don't check bssid
@@ -832,6 +836,8 @@
rtlpriv->cfg->ops->set_chk_bssid(hw, false);
else
rtlpriv->cfg->ops->set_chk_bssid(hw, true);
+ if (update_rcr)
+ update_rcr = false;
}
}
@@ -846,6 +852,8 @@
RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD,
"Disable receive control frame.\n");
}
+ if (!update_rcr)
+ update_rcr = true;
}
if (changed_flags & FIF_OTHER_BSS) {
@@ -858,7 +866,13 @@
RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD,
"Disable receive other BSS's frame.\n");
}
+ if (!update_rcr)
+ update_rcr = true;
}
+
+ if (update_rcr)
+ rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR,
+ (u8 *)(&mac->rx_conf));
}
static int rtl_op_sta_add(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
@@ -1361,7 +1375,9 @@
return 0;
}
-static void rtl_op_sw_scan_start(struct ieee80211_hw *hw)
+static void rtl_op_sw_scan_start(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ const u8 *mac_addr)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
@@ -1396,7 +1412,8 @@
rtlpriv->cfg->ops->scan_operation_backup(hw, SCAN_OPT_BACKUP_BAND0);
}
-static void rtl_op_sw_scan_complete(struct ieee80211_hw *hw)
+static void rtl_op_sw_scan_complete(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
@@ -1828,3 +1845,9 @@
.flush = rtl_op_flush,
};
EXPORT_SYMBOL_GPL(rtl_ops);
+
+bool rtl_btc_status_false(void)
+{
+ return false;
+}
+EXPORT_SYMBOL_GPL(rtl_btc_status_false);
diff --git a/drivers/net/wireless/rtlwifi/core.h b/drivers/net/wireless/rtlwifi/core.h
index 59cd3b9..624e1dc 100644
--- a/drivers/net/wireless/rtlwifi/core.h
+++ b/drivers/net/wireless/rtlwifi/core.h
@@ -42,5 +42,6 @@
u32 mask, u32 data);
void rtl_bb_delay(struct ieee80211_hw *hw, u32 addr, u32 data);
bool rtl_cmd_send_packet(struct ieee80211_hw *hw, struct sk_buff *skb);
+bool rtl_btc_status_false(void);
#endif
diff --git a/drivers/net/wireless/rtlwifi/pci.c b/drivers/net/wireless/rtlwifi/pci.c
index 667aba8..846a2e6 100644
--- a/drivers/net/wireless/rtlwifi/pci.c
+++ b/drivers/net/wireless/rtlwifi/pci.c
@@ -842,7 +842,8 @@
break;
}
/* handle command packet here */
- if (rtlpriv->cfg->ops->rx_command_packet(hw, stats, skb)) {
+ if (rtlpriv->cfg->ops->rx_command_packet &&
+ rtlpriv->cfg->ops->rx_command_packet(hw, stats, skb)) {
dev_kfree_skb_any(skb);
goto end;
}
@@ -1127,9 +1128,14 @@
__skb_queue_tail(&ring->queue, pskb);
- rtlpriv->cfg->ops->set_desc(hw, (u8 *)pdesc, true, HW_DESC_OWN,
- &temp_one);
-
+ if (rtlpriv->use_new_trx_flow) {
+ temp_one = 4;
+ rtlpriv->cfg->ops->set_desc(hw, (u8 *)pbuffer_desc, true,
+ HW_DESC_OWN, (u8 *)&temp_one);
+ } else {
+ rtlpriv->cfg->ops->set_desc(hw, (u8 *)pdesc, true, HW_DESC_OWN,
+ &temp_one);
+ }
return;
}
@@ -1370,9 +1376,9 @@
ring->desc = NULL;
if (rtlpriv->use_new_trx_flow) {
pci_free_consistent(rtlpci->pdev,
- sizeof(*ring->desc) * ring->entries,
+ sizeof(*ring->buffer_desc) * ring->entries,
ring->buffer_desc, ring->buffer_desc_dma);
- ring->desc = NULL;
+ ring->buffer_desc = NULL;
}
}
@@ -1543,7 +1549,6 @@
true,
HW_DESC_TXBUFF_ADDR),
skb->len, PCI_DMA_TODEVICE);
- ring->idx = (ring->idx + 1) % ring->entries;
kfree_skb(skb);
ring->idx = (ring->idx + 1) % ring->entries;
}
@@ -1796,7 +1801,8 @@
rtl_pci_reset_trx_ring(hw);
rtlpci->driver_is_goingto_unload = false;
- if (rtlpriv->cfg->ops->get_btc_status()) {
+ if (rtlpriv->cfg->ops->get_btc_status &&
+ rtlpriv->cfg->ops->get_btc_status()) {
rtlpriv->btcoexist.btc_ops->btc_init_variables(rtlpriv);
rtlpriv->btcoexist.btc_ops->btc_init_hal_vars(rtlpriv);
}
@@ -2243,6 +2249,16 @@
/*like read eeprom and so on */
rtlpriv->cfg->ops->read_eeprom_info(hw);
+ if (rtlpriv->cfg->ops->init_sw_vars(hw)) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Can't init_sw_vars\n");
+ err = -ENODEV;
+ goto fail3;
+ }
+ rtlpriv->cfg->ops->init_sw_leds(hw);
+
+ /*aspm */
+ rtl_pci_init_aspm(hw);
+
/* Init mac80211 sw */
err = rtl_init_core(hw);
if (err) {
@@ -2258,16 +2274,6 @@
goto fail3;
}
- if (rtlpriv->cfg->ops->init_sw_vars(hw)) {
- RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Can't init_sw_vars\n");
- err = -ENODEV;
- goto fail3;
- }
- rtlpriv->cfg->ops->init_sw_leds(hw);
-
- /*aspm */
- rtl_pci_init_aspm(hw);
-
err = ieee80211_register_hw(hw);
if (err) {
RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c b/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c
index a00861b..29983bc 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c
@@ -656,7 +656,8 @@
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
-void rtl92c_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool b_dl_finished)
+void rtl92c_set_fw_rsvdpagepkt(struct ieee80211_hw *hw,
+ bool (*cmd_send_packet)(struct ieee80211_hw *, struct sk_buff *))
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
@@ -722,7 +723,10 @@
memcpy((u8 *)skb_put(skb, totalpacketlen),
&reserved_page_packet, totalpacketlen);
- rtstatus = rtl_cmd_send_packet(hw, skb);
+ if (cmd_send_packet)
+ rtstatus = cmd_send_packet(hw, skb);
+ else
+ rtstatus = rtl_cmd_send_packet(hw, skb);
if (rtstatus)
b_dlok = true;
diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.h b/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.h
index a815bd6..b64ae45 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.h
@@ -109,7 +109,9 @@
u32 cmd_len, u8 *p_cmdbuffer);
void rtl92c_firmware_selfreset(struct ieee80211_hw *hw);
void rtl92c_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode);
-void rtl92c_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool b_dl_finished);
+void rtl92c_set_fw_rsvdpagepkt
+ (struct ieee80211_hw *hw,
+ bool (*cmd_send_packet)(struct ieee80211_hw *, struct sk_buff *));
void rtl92c_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus);
void usb_writeN_async(struct rtl_priv *rtlpriv, u32 addr, void *data, u16 len);
void rtl92c_set_p2p_ps_offload_cmd(struct ieee80211_hw *hw, u8 p2p_ps_state);
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/def.h b/drivers/net/wireless/rtlwifi/rtl8192ce/def.h
index 831df10..9b660df 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/def.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/def.h
@@ -114,6 +114,8 @@
LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 16, 4)
#define GET_C2H_CMD_FEEDBACK_CCX_SEQ(__pcmdfbhdr) \
LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 20, 12)
+#define GET_RX_STATUS_DESC_BUFF_ADDR(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc + 24, 0, 32)
#define CHIP_VER_B BIT(4)
#define CHIP_BONDING_IDENTIFIER(_value) (((_value) >> 22) & 0x3)
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
index 8ec0f03..5c646d5 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
@@ -459,7 +459,7 @@
rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2,
tmp_reg422 & (~BIT(6)));
- rtl92c_set_fw_rsvdpagepkt(hw, 0);
+ rtl92c_set_fw_rsvdpagepkt(hw, NULL);
_rtl92ce_set_bcn_ctrl_reg(hw, BIT(3), 0);
_rtl92ce_set_bcn_ctrl_reg(hw, 0, BIT(4));
@@ -955,6 +955,7 @@
local_save_flags(flags);
local_irq_enable();
+ rtlhal->fw_ready = false;
rtlpriv->intf_ops->disable_aspm(hw);
rtstatus = _rtl92ce_init_mac(hw);
if (!rtstatus) {
@@ -971,6 +972,7 @@
goto exit;
}
+ rtlhal->fw_ready = true;
rtlhal->last_hmeboxnum = 0;
rtl92c_phy_mac_config(hw);
/* because last function modify RCR, so we update
@@ -1287,6 +1289,7 @@
rtl_write_dword(rtlpriv, REG_HIMR, rtlpci->irq_mask[0] & 0xFFFFFFFF);
rtl_write_dword(rtlpriv, REG_HIMRE, rtlpci->irq_mask[1] & 0xFFFFFFFF);
+ rtlpci->irq_enabled = true;
}
void rtl92ce_disable_interrupt(struct ieee80211_hw *hw)
@@ -1296,7 +1299,7 @@
rtl_write_dword(rtlpriv, REG_HIMR, IMR8190_DISABLED);
rtl_write_dword(rtlpriv, REG_HIMRE, IMR8190_DISABLED);
- synchronize_irq(rtlpci->pdev->irq);
+ rtlpci->irq_enabled = false;
}
static void _rtl92ce_poweroff_adapter(struct ieee80211_hw *hw)
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c
index d86b5b5..dd5aa08 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c
@@ -228,6 +228,7 @@
.led_control = rtl92ce_led_control,
.set_desc = rtl92ce_set_desc,
.get_desc = rtl92ce_get_desc,
+ .is_tx_desc_closed = rtl92ce_is_tx_desc_closed,
.tx_polling = rtl92ce_tx_polling,
.enable_hw_sec = rtl92ce_enable_hw_security_config,
.set_key = rtl92ce_set_key,
@@ -244,6 +245,7 @@
.phy_lc_calibrate = _rtl92ce_phy_lc_calibrate,
.phy_set_bw_mode_callback = rtl92ce_phy_set_bw_mode_callback,
.dm_dynamic_txpower = rtl92ce_dm_dynamic_txpower,
+ .get_btc_status = rtl_btc_status_false,
};
static struct rtl_mod_params rtl92ce_mod_params = {
@@ -270,6 +272,8 @@
.maps[MAC_RCR_ACRC32] = ACRC32,
.maps[MAC_RCR_ACF] = ACF,
.maps[MAC_RCR_AAP] = AAP,
+ .maps[MAC_HIMR] = REG_HIMR,
+ .maps[MAC_HIMRE] = REG_HIMRE,
.maps[EFUSE_TEST] = REG_EFUSE_TEST,
.maps[EFUSE_CTRL] = REG_EFUSE_CTRL,
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c
index 2fb9c7a..e88dcd0 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c
@@ -720,13 +720,15 @@
break;
}
} else {
- struct rx_desc_92c *pdesc = (struct rx_desc_92c *)p_desc;
switch (desc_name) {
case HW_DESC_OWN:
- ret = GET_RX_DESC_OWN(pdesc);
+ ret = GET_RX_DESC_OWN(p_desc);
break;
case HW_DESC_RXPKT_LEN:
- ret = GET_RX_DESC_PKT_LEN(pdesc);
+ ret = GET_RX_DESC_PKT_LEN(p_desc);
+ break;
+ case HW_DESC_RXBUFF_ADDR:
+ ret = GET_RX_DESC_BUFF_ADDR(p_desc);
break;
default:
RT_ASSERT(false, "ERR rxdesc :%d not process\n",
@@ -737,6 +739,23 @@
return ret;
}
+bool rtl92ce_is_tx_desc_closed(struct ieee80211_hw *hw,
+ u8 hw_queue, u16 index)
+{
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ struct rtl8192_tx_ring *ring = &rtlpci->tx_ring[hw_queue];
+ u8 *entry = (u8 *)(&ring->desc[ring->idx]);
+ u8 own = (u8)rtl92ce_get_desc(entry, true, HW_DESC_OWN);
+
+ /*beacon packet will only use the first
+ *descriptor defautly,and the own may not
+ *be cleared by the hardware
+ */
+ if (own)
+ return false;
+ return true;
+}
+
void rtl92ce_tx_polling(struct ieee80211_hw *hw, u8 hw_queue)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.h b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.h
index 9a39ec4..4bec4b0 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.h
@@ -723,6 +723,8 @@
void rtl92ce_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx,
u8 desc_name, u8 *val);
u32 rtl92ce_get_desc(u8 *pdesc, bool istx, u8 desc_name);
+bool rtl92ce_is_tx_desc_closed(struct ieee80211_hw *hw,
+ u8 hw_queue, u16 index);
void rtl92ce_tx_polling(struct ieee80211_hw *hw, u8 hw_queue);
void rtl92ce_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc,
bool b_firstseg, bool b_lastseg,
diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c
index 04aa0b5..5513217 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c
@@ -1592,6 +1592,20 @@
}
}
+static bool usb_cmd_send_packet(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+ /* Currently nothing happens here.
+ * Traffic stops after some seconds in WPA2 802.11n mode.
+ * Maybe because rtl8192cu chip should be set from here?
+ * If I understand correctly, the realtek vendor driver sends some urbs
+ * if its "here".
+ *
+ * This is maybe necessary:
+ * rtlpriv->cfg->ops->fill_tx_cmddesc(hw, buffer, 1, 1, skb);
+ */
+ return true;
+}
+
void rtl92cu_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
@@ -1939,7 +1953,8 @@
recover = true;
rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2,
tmp_reg422 & (~BIT(6)));
- rtl92c_set_fw_rsvdpagepkt(hw, 0);
+ rtl92c_set_fw_rsvdpagepkt(hw,
+ &usb_cmd_send_packet);
_rtl92cu_set_bcn_ctrl_reg(hw, BIT(3), 0);
_rtl92cu_set_bcn_ctrl_reg(hw, 0, BIT(4));
if (recover)
diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.h b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.h
index 0f7812e..c1e33b0 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.h
@@ -104,7 +104,6 @@
void rtl92cu_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid);
int rtl92c_download_fw(struct ieee80211_hw *hw);
void rtl92c_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode);
-void rtl92c_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool dl_finished);
void rtl92c_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus);
void rtl92c_fill_h2c_cmd(struct ieee80211_hw *hw,
u8 element_id, u32 cmd_len, u8 *p_cmdbuffer);
diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c
index 18933c0..97f85b7 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c
@@ -101,6 +101,12 @@
}
}
+/* get bt coexist status */
+static bool rtl92cu_get_btc_status(void)
+{
+ return false;
+}
+
static struct rtl_hal_ops rtl8192cu_hal_ops = {
.init_sw_vars = rtl92cu_init_sw_vars,
.deinit_sw_vars = rtl92cu_deinit_sw_vars,
@@ -148,6 +154,7 @@
.phy_set_bw_mode_callback = rtl92cu_phy_set_bw_mode_callback,
.dm_dynamic_txpower = rtl92cu_dm_dynamic_txpower,
.fill_h2c_cmd = rtl92c_fill_h2c_cmd,
+ .get_btc_status = rtl92cu_get_btc_status,
};
static struct rtl_mod_params rtl92cu_mod_params = {
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/sw.c b/drivers/net/wireless/rtlwifi/rtl8192de/sw.c
index edab5a5..a0aba08 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192de/sw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/sw.c
@@ -251,6 +251,7 @@
.get_rfreg = rtl92d_phy_query_rf_reg,
.set_rfreg = rtl92d_phy_set_rf_reg,
.linked_set_reg = rtl92d_linked_set_reg,
+ .get_btc_status = rtl_btc_status_false,
};
static struct rtl_mod_params rtl92de_mod_params = {
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/Makefile b/drivers/net/wireless/rtlwifi/rtl8192ee/Makefile
index aeea8a9..c5e6f97 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ee/Makefile
+++ b/drivers/net/wireless/rtlwifi/rtl8192ee/Makefile
@@ -1,6 +1,3 @@
-obj-m := rtl8192ee.o
-
-
rtl8192ee-objs := \
dm.o \
fw.o \
@@ -14,6 +11,6 @@
trx.o \
-obj-$(CPTCFG_RTL8821AE) += rtl8192ee.o
+obj-$(CPTCFG_RTL8192EE) += rtl8192ee.o
ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/hw.c b/drivers/net/wireless/rtlwifi/rtl8192ee/hw.c
index dfdc9b2..1a87edc 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ee/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ee/hw.c
@@ -362,7 +362,7 @@
}
break;
default:
- RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_DMESG,
"switch case not process %x\n", variable);
break;
}
@@ -591,7 +591,7 @@
acm_ctrl &= (~ACMHW_BEQEN);
break;
default:
- RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_DMESG,
"switch case not process\n");
break;
}
@@ -710,7 +710,7 @@
}
break;
default:
- RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_DMESG,
"switch case not process %x\n", variable);
break;
}
@@ -2424,7 +2424,7 @@
enc_algo = CAM_AES;
break;
default:
- RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_DMESG,
"switch case not process\n");
enc_algo = CAM_TKIP;
break;
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/def.h b/drivers/net/wireless/rtlwifi/rtl8192se/def.h
index 83c9867..6e7a70b 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/def.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/def.h
@@ -446,6 +446,8 @@
/* DWORD 6 */
#define SET_RX_STATUS__DESC_BUFF_ADDR(__pdesc, __val) \
SET_BITS_OFFSET_LE(__pdesc + 24, 0, 32, __val)
+#define GET_RX_STATUS_DESC_BUFF_ADDR(__pdesc) \
+ SHIFT_AND_MASK_LE(__pdesc + 24, 0, 32)
#define SE_RX_HAL_IS_CCK_RATE(_pdesc)\
(GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC92_RATE1M || \
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/hw.c b/drivers/net/wireless/rtlwifi/rtl8192se/hw.c
index 00e0670..5761d5b 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/hw.c
@@ -1201,6 +1201,9 @@
}
+ if (type != NL80211_IFTYPE_AP &&
+ rtlpriv->mac80211.link_state < MAC80211_LINKED)
+ bt_msr = rtl_read_byte(rtlpriv, MSR) & ~MSR_LINK_MASK;
rtl_write_byte(rtlpriv, (MSR), bt_msr);
temp = rtl_read_dword(rtlpriv, TCR);
@@ -1262,6 +1265,7 @@
rtl_write_dword(rtlpriv, INTA_MASK, rtlpci->irq_mask[0]);
/* Support Bit 32-37(Assign as Bit 0-5) interrupt setting now */
rtl_write_dword(rtlpriv, INTA_MASK + 4, rtlpci->irq_mask[1] & 0x3F);
+ rtlpci->irq_enabled = true;
}
void rtl92se_disable_interrupt(struct ieee80211_hw *hw)
@@ -1276,8 +1280,7 @@
rtlpci = rtl_pcidev(rtl_pcipriv(hw));
rtl_write_dword(rtlpriv, INTA_MASK, 0);
rtl_write_dword(rtlpriv, INTA_MASK + 4, 0);
-
- synchronize_irq(rtlpci->pdev->irq);
+ rtlpci->irq_enabled = false;
}
static u8 _rtl92s_set_sysclk(struct ieee80211_hw *hw, u8 data)
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/phy.c b/drivers/net/wireless/rtlwifi/rtl8192se/phy.c
index 77c5b5f..4b4612f 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/phy.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/phy.c
@@ -399,6 +399,8 @@
case 2:
currentcmd = &postcommoncmd[*step];
break;
+ default:
+ return true;
}
if (currentcmd->cmdid == CMDID_END) {
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/sw.c b/drivers/net/wireless/rtlwifi/rtl8192se/sw.c
index 1bff2a0..fb00386 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/sw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/sw.c
@@ -87,11 +87,8 @@
static void rtl92se_fw_cb(const struct firmware *firmware, void *context)
{
struct ieee80211_hw *hw = context;
- struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
struct rtl_priv *rtlpriv = rtl_priv(hw);
- struct rtl_pci *rtlpci = rtl_pcidev(pcipriv);
struct rt_firmware *pfirmware = NULL;
- int err;
RT_TRACE(rtlpriv, COMP_ERR, DBG_LOUD,
"Firmware callback routine entered!\n");
@@ -112,20 +109,6 @@
memcpy(pfirmware->sz_fw_tmpbuffer, firmware->data, firmware->size);
pfirmware->sz_fw_tmpbufferlen = firmware->size;
release_firmware(firmware);
-
- err = ieee80211_register_hw(hw);
- if (err) {
- RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
- "Can't register mac80211 hw\n");
- return;
- } else {
- rtlpriv->mac80211.mac80211_registered = 1;
- }
- rtlpci->irq_alloc = 1;
- set_bit(RTL_STATUS_INTERFACE_START, &rtlpriv->status);
-
- /*init rfkill */
- rtl_init_rfkill(hw);
}
static int rtl92s_init_sw_vars(struct ieee80211_hw *hw)
@@ -226,8 +209,8 @@
if (!rtlpriv->rtlhal.pfirmware)
return 1;
- rtlpriv->max_fw_size = RTL8190_MAX_RAW_FIRMWARE_CODE_SIZE;
-
+ rtlpriv->max_fw_size = RTL8190_MAX_FIRMWARE_CODE_SIZE*2 +
+ sizeof(struct fw_hdr);
pr_info("Driver for Realtek RTL8192SE/RTL8191SE\n"
"Loading firmware %s\n", rtlpriv->cfg->fw_name);
/* request fw */
@@ -253,6 +236,19 @@
}
}
+static bool rtl92se_is_tx_desc_closed(struct ieee80211_hw *hw, u8 hw_queue,
+ u16 index)
+{
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ struct rtl8192_tx_ring *ring = &rtlpci->tx_ring[hw_queue];
+ u8 *entry = (u8 *)(&ring->desc[ring->idx]);
+ u8 own = (u8)rtl92se_get_desc(entry, true, HW_DESC_OWN);
+
+ if (own)
+ return false;
+ return true;
+}
+
static struct rtl_hal_ops rtl8192se_hal_ops = {
.init_sw_vars = rtl92s_init_sw_vars,
.deinit_sw_vars = rtl92s_deinit_sw_vars,
@@ -286,6 +282,7 @@
.led_control = rtl92se_led_control,
.set_desc = rtl92se_set_desc,
.get_desc = rtl92se_get_desc,
+ .is_tx_desc_closed = rtl92se_is_tx_desc_closed,
.tx_polling = rtl92se_tx_polling,
.enable_hw_sec = rtl92se_enable_hw_security_config,
.set_key = rtl92se_set_key,
@@ -294,6 +291,7 @@
.set_bbreg = rtl92s_phy_set_bb_reg,
.get_rfreg = rtl92s_phy_query_rf_reg,
.set_rfreg = rtl92s_phy_set_rf_reg,
+ .get_btc_status = rtl_btc_status_false,
};
static struct rtl_mod_params rtl92se_mod_params = {
@@ -322,6 +320,8 @@
.maps[MAC_RCR_ACRC32] = RCR_ACRC32,
.maps[MAC_RCR_ACF] = RCR_ACF,
.maps[MAC_RCR_AAP] = RCR_AAP,
+ .maps[MAC_HIMR] = INTA_MASK,
+ .maps[MAC_HIMRE] = INTA_MASK + 4,
.maps[EFUSE_TEST] = REG_EFUSE_TEST,
.maps[EFUSE_CTRL] = REG_EFUSE_CTRL,
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c
index b358ebc..672fd3b 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c
@@ -640,6 +640,9 @@
case HW_DESC_RXPKT_LEN:
ret = GET_RX_STATUS_DESC_PKT_LEN(desc);
break;
+ case HW_DESC_RXBUFF_ADDR:
+ ret = GET_RX_STATUS_DESC_BUFF_ADDR(desc);
+ break;
default:
RT_ASSERT(false, "ERR rxdesc :%d not process\n",
desc_name);
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/Makefile b/drivers/net/wireless/rtlwifi/rtl8723ae/Makefile
index e0b1dde..e8ce901 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723ae/Makefile
+++ b/drivers/net/wireless/rtlwifi/rtl8723ae/Makefile
@@ -1,6 +1,3 @@
-obj-m := rtl8723ae.o
-
-
rtl8723ae-objs := \
dm.o \
fw.o \
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/Makefile b/drivers/net/wireless/rtlwifi/rtl8723be/Makefile
index 5d18f04..cd01566 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723be/Makefile
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/Makefile
@@ -1,6 +1,3 @@
-obj-m := rtl8723be.o
-
-
rtl8723be-objs := \
dm.o \
fw.o \
diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/Makefile b/drivers/net/wireless/rtlwifi/rtl8821ae/Makefile
index 445930c..5215004 100644
--- a/drivers/net/wireless/rtlwifi/rtl8821ae/Makefile
+++ b/drivers/net/wireless/rtlwifi/rtl8821ae/Makefile
@@ -1,6 +1,3 @@
-obj-m := rtl8821ae.o
-
-
rtl8821ae-objs := \
dm.o \
fw.o \
diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/dm.c b/drivers/net/wireless/rtlwifi/rtl8821ae/dm.c
index 9be1061..ba30b0d 100644
--- a/drivers/net/wireless/rtlwifi/rtl8821ae/dm.c
+++ b/drivers/net/wireless/rtlwifi/rtl8821ae/dm.c
@@ -2078,8 +2078,7 @@
if (rtldm->tx_rate != 0xFF)
tx_rate = rtl8821ae_hw_rate_to_mrate(hw, rtldm->tx_rate);
- RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
- "===>rtl8812ae_dm_txpwr_track_set_pwr\n");
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "===>%s\n", __func__);
if (tx_rate != 0xFF) { /* Mimic Modify High Rate BBSwing Limit.*/
/*CCK*/
@@ -2128,7 +2127,7 @@
if (method == BBSWING) {
RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
- "===>rtl8812ae_dm_txpwr_track_set_pwr\n");
+ "===>%s\n", __func__);
if (rf_path == RF90_PATH_A) {
final_swing_idx[RF90_PATH_A] =
(rtldm->ofdm_index[RF90_PATH_A] >
@@ -2260,7 +2259,8 @@
rtldm->txpower_trackinginit = true;
RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
- "===>rtl8812ae_dm_txpower_tracking_callback_thermalmeter,\n pDM_Odm->BbSwingIdxCckBase: %d,pDM_Odm->BbSwingIdxOfdmBase[A]:%d, pDM_Odm->DefaultOfdmIndex: %d\n",
+ "===>%s,\n pDM_Odm->BbSwingIdxCckBase: %d,pDM_Odm->BbSwingIdxOfdmBase[A]:%d, pDM_Odm->DefaultOfdmIndex: %d\n",
+ __func__,
rtldm->swing_idx_cck_base,
rtldm->swing_idx_ofdm_base[RF90_PATH_A],
rtldm->default_ofdm_index);
@@ -2539,8 +2539,7 @@
}
}
- RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD,
- "<===rtl8812ae_dm_txpower_tracking_callback_thermalmeter\n");
+ RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "<===%s\n", __func__);
}
void rtl8821ae_dm_check_txpower_tracking_thermalmeter(struct ieee80211_hw *hw)
diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c b/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c
index 310d316..8ec8200 100644
--- a/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c
@@ -3672,8 +3672,9 @@
mac->opmode == NL80211_IFTYPE_ADHOC)
macid = sta->aid + 1;
if (wirelessmode == WIRELESS_MODE_N_5G ||
- wirelessmode == WIRELESS_MODE_AC_5G)
- ratr_bitmap = sta->supp_rates[NL80211_BAND_5GHZ];
+ wirelessmode == WIRELESS_MODE_AC_5G ||
+ wirelessmode == WIRELESS_MODE_A)
+ ratr_bitmap = sta->supp_rates[NL80211_BAND_5GHZ] << 4;
else
ratr_bitmap = sta->supp_rates[NL80211_BAND_2GHZ];
diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/phy.c b/drivers/net/wireless/rtlwifi/rtl8821ae/phy.c
index 9786313..9b4d8a6 100644
--- a/drivers/net/wireless/rtlwifi/rtl8821ae/phy.c
+++ b/drivers/net/wireless/rtlwifi/rtl8821ae/phy.c
@@ -800,7 +800,7 @@
"Invalid RateSection %d in Band 2.4G,Rf Path %d, %dTx in PHY_SetTxPowerByRateBase()\n",
rate_section, path, txnum);
break;
- };
+ }
} else if (band == BAND_ON_5G) {
switch (rate_section) {
case OFDM:
@@ -823,7 +823,7 @@
"Invalid RateSection %d in Band 5G, Rf Path %d, %dTx in PHY_SetTxPowerByRateBase()\n",
rate_section, path, txnum);
break;
- };
+ }
} else {
RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
"Invalid Band %d in PHY_SetTxPowerByRateBase()\n", band);
@@ -870,7 +870,7 @@
"Invalid RateSection %d in Band 2.4G, Rf Path %d, %dTx in PHY_GetTxPowerByRateBase()\n",
rate_section, path, txnum);
break;
- };
+ }
} else if (band == BAND_ON_5G) {
switch (rate_section) {
case OFDM:
@@ -893,7 +893,7 @@
"Invalid RateSection %d in Band 5G, Rf Path %d, %dTx in PHY_GetTxPowerByRateBase()\n",
rate_section, path, txnum);
break;
- };
+ }
} else {
RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
"Invalid Band %d in PHY_GetTxPowerByRateBase()\n", band);
@@ -1889,15 +1889,18 @@
struct rtl_phy *rtlphy = &rtlpriv->phy;
u8 rate_section = _rtl8821ae_get_rate_section_index(regaddr);
- if (band != BAND_ON_2_4G && band != BAND_ON_5G)
+ if (band != BAND_ON_2_4G && band != BAND_ON_5G) {
RT_TRACE(rtlpriv, COMP_INIT, DBG_WARNING, "Invalid Band %d\n", band);
-
- if (rfpath >= MAX_RF_PATH)
+ band = BAND_ON_2_4G;
+ }
+ if (rfpath >= MAX_RF_PATH) {
RT_TRACE(rtlpriv, COMP_INIT, DBG_WARNING, "Invalid RfPath %d\n", rfpath);
-
- if (txnum >= MAX_RF_PATH)
+ rfpath = MAX_RF_PATH - 1;
+ }
+ if (txnum >= MAX_RF_PATH) {
RT_TRACE(rtlpriv, COMP_INIT, DBG_WARNING, "Invalid TxNum %d\n", txnum);
-
+ txnum = MAX_RF_PATH - 1;
+ }
rtlphy->tx_power_by_rate_offset[band][rfpath][txnum][rate_section] = data;
RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
"TxPwrByRateOffset[Band %d][RfPath %d][TxNum %d][RateSection %d] = 0x%x\n",
@@ -3743,7 +3746,7 @@
break;
default:
break;
- };
+ }
}
static void _rtl8821ae_iqk_rx_fill_iqc(struct ieee80211_hw *hw,
@@ -3764,7 +3767,7 @@
break;
default:
break;
- };
+ }
}
#define cal_num 10
diff --git a/drivers/net/wireless/rtlwifi/usb.c b/drivers/net/wireless/rtlwifi/usb.c
index 10cf69c..46ee956 100644
--- a/drivers/net/wireless/rtlwifi/usb.c
+++ b/drivers/net/wireless/rtlwifi/usb.c
@@ -1117,7 +1117,18 @@
}
rtlpriv->cfg->ops->init_sw_leds(hw);
+ err = ieee80211_register_hw(hw);
+ if (err) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ "Can't register mac80211 hw.\n");
+ err = -ENODEV;
+ goto error_out;
+ }
+ rtlpriv->mac80211.mac80211_registered = 1;
+
+ set_bit(RTL_STATUS_INTERFACE_START, &rtlpriv->status);
return 0;
+
error_out:
rtl_deinit_core(hw);
_rtl_usb_io_handler_release(hw);
diff --git a/drivers/net/wireless/ti/wl1251/Kconfig b/drivers/net/wireless/ti/wl1251/Kconfig
index 76649c5..1d9ca90 100644
--- a/drivers/net/wireless/ti/wl1251/Kconfig
+++ b/drivers/net/wireless/ti/wl1251/Kconfig
@@ -12,7 +12,7 @@
N if unsure.
config WL1251_SPI
- depends on !BACKPORT_KERNEL_3_5
+ depends on !KERNEL_3_5
tristate "TI wl1251 SPI support"
depends on m
depends on WL1251 && SPI_MASTER
@@ -25,7 +25,7 @@
Say N if unsure.
config WL1251_SDIO
- depends on !BACKPORT_KERNEL_3_15
+ depends on !KERNEL_3_15
tristate "TI wl1251 SDIO support"
depends on m
depends on WL1251 && MMC
diff --git a/drivers/net/wireless/ti/wl1251/main.c b/drivers/net/wireless/ti/wl1251/main.c
index 3823485..0b30a7b 100644
--- a/drivers/net/wireless/ti/wl1251/main.c
+++ b/drivers/net/wireless/ti/wl1251/main.c
@@ -1029,7 +1029,7 @@
goto out_sleep;
}
- skb = ieee80211_probereq_get(wl->hw, wl->vif, ssid, ssid_len,
+ skb = ieee80211_probereq_get(wl->hw, wl->vif->addr, ssid, ssid_len,
req->ie_len);
if (!skb) {
ret = -ENOMEM;
diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c
index 0bccf12..d6d0d6d 100644
--- a/drivers/net/wireless/ti/wl12xx/main.c
+++ b/drivers/net/wireless/ti/wl12xx/main.c
@@ -1900,7 +1900,6 @@
.id_table = wl12xx_id_table,
.driver = {
.name = "wl12xx_driver",
- .owner = THIS_MODULE,
}
};
diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c
index 7af1936..8e56261 100644
--- a/drivers/net/wireless/ti/wl18xx/main.c
+++ b/drivers/net/wireless/ti/wl18xx/main.c
@@ -1968,7 +1968,6 @@
.id_table = wl18xx_id_table,
.driver = {
.name = "wl18xx_driver",
- .owner = THIS_MODULE,
}
};
diff --git a/drivers/net/wireless/ti/wlcore/Kconfig b/drivers/net/wireless/ti/wlcore/Kconfig
index 4bab094..09bb747 100644
--- a/drivers/net/wireless/ti/wlcore/Kconfig
+++ b/drivers/net/wireless/ti/wlcore/Kconfig
@@ -26,7 +26,7 @@
Say N if unsure.
config WLCORE_SDIO
- depends on !BACKPORT_KERNEL_3_15
+ depends on !KERNEL_3_15
tristate "TI wlcore SDIO support"
depends on m
depends on WLCORE && MMC
diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c
index 05604ee..b826619 100644
--- a/drivers/net/wireless/ti/wlcore/cmd.c
+++ b/drivers/net/wireless/ti/wlcore/cmd.c
@@ -64,6 +64,9 @@
id != CMD_STOP_FWLOGGER))
return -EIO;
+ if (WARN_ON_ONCE(len < sizeof(*cmd)))
+ return -EIO;
+
cmd = buf;
cmd->id = cpu_to_le16(id);
cmd->status = 0;
@@ -128,8 +131,9 @@
* send command to fw and return cmd status on success
* valid_rets contains a bitmap of allowed error codes
*/
-int wlcore_cmd_send_failsafe(struct wl1271 *wl, u16 id, void *buf, size_t len,
- size_t res_len, unsigned long valid_rets)
+static int wlcore_cmd_send_failsafe(struct wl1271 *wl, u16 id, void *buf,
+ size_t len, size_t res_len,
+ unsigned long valid_rets)
{
int ret = __wlcore_cmd_send(wl, id, buf, len, res_len);
@@ -150,7 +154,6 @@
wl12xx_queue_recovery_work(wl);
return ret;
}
-EXPORT_SYMBOL_GPL(wl1271_cmd_send);
/*
* wrapper for wlcore_cmd_send that accept only CMD_STATUS_SUCCESS
@@ -165,6 +168,7 @@
return ret;
return 0;
}
+EXPORT_SYMBOL_GPL(wl1271_cmd_send);
/*
* Poll the mailbox event field until any of the bits in the mask is set or a
@@ -891,6 +895,9 @@
wl1271_debug(DEBUG_CMD, "cmd configure (%d)", id);
+ if (WARN_ON_ONCE(len < sizeof(*acx)))
+ return -EIO;
+
acx->id = cpu_to_le16(id);
/* payload length, does not include any headers */
@@ -1138,7 +1145,7 @@
wl1271_debug(DEBUG_SCAN, "build probe request band %d", band);
- skb = ieee80211_probereq_get(wl->hw, vif, ssid, ssid_len,
+ skb = ieee80211_probereq_get(wl->hw, vif->addr, ssid, ssid_len,
ie0_len + ie1_len);
if (!skb) {
ret = -ENOMEM;
diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h
index ca6a28b..453684a 100644
--- a/drivers/net/wireless/ti/wlcore/cmd.h
+++ b/drivers/net/wireless/ti/wlcore/cmd.h
@@ -31,8 +31,6 @@
int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
size_t res_len);
-int wlcore_cmd_send_failsafe(struct wl1271 *wl, u16 id, void *buf, size_t len,
- size_t res_len, unsigned long valid_rets);
int wl12xx_cmd_role_enable(struct wl1271 *wl, u8 *addr, u8 role_type,
u8 *role_id);
int wl12xx_cmd_role_disable(struct wl1271 *wl, u8 *role_id);
diff --git a/drivers/net/wireless/ti/wlcore/event.c b/drivers/net/wireless/ti/wlcore/event.c
index 16d1028..5153640 100644
--- a/drivers/net/wireless/ti/wlcore/event.c
+++ b/drivers/net/wireless/ti/wlcore/event.c
@@ -259,10 +259,7 @@
&wlvif->connection_loss_work,
msecs_to_jiffies(delay));
- ieee80211_cqm_rssi_notify(
- vif,
- NL80211_CQM_RSSI_BEACON_LOSS_EVENT,
- GFP_KERNEL);
+ ieee80211_cqm_beacon_loss_notify(vif, GFP_KERNEL);
}
}
EXPORT_SYMBOL_GPL(wlcore_event_beacon_loss);
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 575c8f6..6ad3fce 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -5177,10 +5177,11 @@
}
static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
struct ieee80211_channel_switch *ch_switch)
{
struct wl1271 *wl = hw->priv;
- struct wl12xx_vif *wlvif;
+ struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
int ret;
wl1271_debug(DEBUG_MAC80211, "mac80211 channel switch");
@@ -5190,14 +5191,8 @@
mutex_lock(&wl->mutex);
if (unlikely(wl->state == WLCORE_STATE_OFF)) {
- wl12xx_for_each_wlvif_sta(wl, wlvif) {
- struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
-
- if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
- continue;
-
+ if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
ieee80211_chswitch_done(vif, false);
- }
goto out;
} else if (unlikely(wl->state != WLCORE_STATE_ON)) {
goto out;
@@ -5208,11 +5203,9 @@
goto out;
/* TODO: change mac80211 to pass vif as param */
- wl12xx_for_each_wlvif_sta(wl, wlvif) {
- unsigned long delay_usec;
- if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
- continue;
+ if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) {
+ unsigned long delay_usec;
ret = wl->ops->channel_switch(wl, wlvif, ch_switch);
if (ret)
@@ -5222,10 +5215,10 @@
/* indicate failure 5 seconds after channel switch time */
delay_usec = ieee80211_tu_to_usec(wlvif->beacon_int) *
- ch_switch->count;
+ ch_switch->count;
ieee80211_queue_delayed_work(hw, &wlvif->channel_switch_work,
- usecs_to_jiffies(delay_usec) +
- msecs_to_jiffies(5000));
+ usecs_to_jiffies(delay_usec) +
+ msecs_to_jiffies(5000));
}
out_sleep:
diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c
index 73a49b8..07b94ed 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.c
+++ b/drivers/net/wireless/zd1211rw/zd_chip.c
@@ -129,7 +129,7 @@
r = zd_ioread16v_locked(chip, v16, a16, count16);
if (r) {
dev_dbg_f(zd_chip_dev(chip),
- "error: zd_ioread16v_locked. Error number %d\n", r);
+ "error: %s. Error number %d\n", __func__, r);
return r;
}
@@ -256,8 +256,8 @@
if (r) {
zd_usb_iowrite16v_async_end(&chip->usb, 0);
dev_dbg_f(zd_chip_dev(chip),
- "error _zd_iowrite32v_locked."
- " Error number %d\n", r);
+ "error _%s. Error number %d\n", __func__,
+ r);
return r;
}
}
diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig
index 6d0ae8f..5d8f3a9 100644
--- a/drivers/nfc/Kconfig
+++ b/drivers/nfc/Kconfig
@@ -29,7 +29,7 @@
into the kernel or say M to compile it as module.
config NFC_TRF7970A
- depends on !BACKPORT_KERNEL_3_4
+ depends on !KERNEL_3_4
tristate "Texas Instruments TRF7970a NFC driver"
depends on m
depends on SPI && NFC_DIGITAL
@@ -43,7 +43,7 @@
trf7970a.ko.
config NFC_MEI_PHY
- depends on !BACKPORT_KERNEL_3_10
+ depends on !KERNEL_3_10
tristate "MEI bus NFC device support"
depends on m
depends on INTEL_MEI && NFC_HCI
diff --git a/drivers/nfc/nfcwilink.c b/drivers/nfc/nfcwilink.c
index 683671a..ce2e2cf 100644
--- a/drivers/nfc/nfcwilink.c
+++ b/drivers/nfc/nfcwilink.c
@@ -566,7 +566,6 @@
.remove = nfcwilink_remove,
.driver = {
.name = "nfcwilink",
- .owner = THIS_MODULE,
},
};
diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c
index 440291a..fc02e8d 100644
--- a/drivers/nfc/pn544/i2c.c
+++ b/drivers/nfc/pn544/i2c.c
@@ -29,8 +29,8 @@
#include <linux/delay.h>
#include <linux/nfc.h>
#include <linux/firmware.h>
-#include <linux/unaligned/access_ok.h>
#include <linux/platform_data/pn544.h>
+#include <asm/unaligned.h>
#include <net/nfc/hci.h>
#include <net/nfc/llc.h>
diff --git a/drivers/nfc/st21nfca/i2c.c b/drivers/nfc/st21nfca/i2c.c
index 0ea756b..0572208 100644
--- a/drivers/nfc/st21nfca/i2c.c
+++ b/drivers/nfc/st21nfca/i2c.c
@@ -28,8 +28,8 @@
#include <linux/delay.h>
#include <linux/nfc.h>
#include <linux/firmware.h>
-#include <linux/unaligned/access_ok.h>
#include <linux/platform_data/st21nfca.h>
+#include <asm/unaligned.h>
#include <net/nfc/hci.h>
#include <net/nfc/llc.h>
@@ -72,7 +72,6 @@
struct nfc_hci_dev *hdev;
unsigned int gpio_ena;
- unsigned int gpio_irq;
unsigned int irq_polarity;
struct sk_buff *pending_skb;
@@ -531,20 +530,12 @@
"clf_enable");
if (r) {
nfc_err(&client->dev, "Failed to request enable pin\n");
- return -ENODEV;
+ return r;
}
phy->gpio_ena = gpio;
- /* IRQ */
- r = irq_of_parse_and_map(pp, 0);
- if (r < 0) {
- nfc_err(&client->dev, "Unable to get irq, error: %d\n", r);
- return r;
- }
-
- phy->irq_polarity = irq_get_trigger_type(r);
- client->irq = r;
+ phy->irq_polarity = irq_get_trigger_type(client->irq);
return 0;
}
@@ -560,7 +551,6 @@
struct st21nfca_nfc_platform_data *pdata;
struct st21nfca_i2c_phy *phy = i2c_get_clientdata(client);
int r;
- int irq;
pdata = client->dev.platform_data;
if (pdata == NULL) {
@@ -569,36 +559,18 @@
}
/* store for later use */
- phy->gpio_irq = pdata->gpio_irq;
phy->gpio_ena = pdata->gpio_ena;
phy->irq_polarity = pdata->irq_polarity;
- r = devm_gpio_request_one(&client->dev, phy->gpio_irq, GPIOF_IN,
- "wake_up");
- if (r) {
- pr_err("%s : gpio_request failed\n", __FILE__);
- return -ENODEV;
- }
-
if (phy->gpio_ena > 0) {
r = devm_gpio_request_one(&client->dev, phy->gpio_ena,
GPIOF_OUT_INIT_HIGH, "clf_enable");
if (r) {
pr_err("%s : ena gpio_request failed\n", __FILE__);
- return -ENODEV;
+ return r;
}
}
- /* IRQ */
- irq = gpio_to_irq(phy->gpio_irq);
- if (irq < 0) {
- nfc_err(&client->dev,
- "Unable to get irq number for GPIO %d error %d\n",
- phy->gpio_irq, r);
- return -ENODEV;
- }
- client->irq = irq;
-
return 0;
}
@@ -656,7 +628,7 @@
r = st21nfca_hci_platform_init(phy);
if (r < 0) {
nfc_err(&client->dev, "Unable to reboot st21nfca\n");
- return -ENODEV;
+ return r;
}
r = devm_request_threaded_irq(&client->dev, client->irq, NULL,
@@ -687,10 +659,13 @@
return 0;
}
+#ifdef CONFIG_OF
static const struct of_device_id of_st21nfca_i2c_match[] = {
{ .compatible = "st,st21nfca_i2c", },
{}
};
+MODULE_DEVICE_TABLE(of, of_st21nfca_i2c_match);
+#endif
static struct i2c_driver st21nfca_hci_i2c_driver = {
.driver = {
diff --git a/drivers/nfc/st21nfca/st21nfca.c b/drivers/nfc/st21nfca/st21nfca.c
index a89e56c..f2596c8 100644
--- a/drivers/nfc/st21nfca/st21nfca.c
+++ b/drivers/nfc/st21nfca/st21nfca.c
@@ -77,10 +77,6 @@
((p & 0x0f) == (ST21NFCA_DM_PIPE_CREATED | ST21NFCA_DM_PIPE_OPEN))
#define ST21NFCA_NFC_MODE 0x03 /* NFC_MODE parameter*/
-#define ST21NFCA_EVT_FIELD_ON 0x11
-#define ST21NFCA_EVT_CARD_DEACTIVATED 0x12
-#define ST21NFCA_EVT_CARD_ACTIVATED 0x13
-#define ST21NFCA_EVT_FIELD_OFF 0x14
static DECLARE_BITMAP(dev_mask, ST21NFCA_NUM_DEVICES);
@@ -841,31 +837,11 @@
static int st21nfca_hci_event_received(struct nfc_hci_dev *hdev, u8 gate,
u8 event, struct sk_buff *skb)
{
- int r;
- struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+ pr_debug("hci event: %d gate: %x\n", event, gate);
- pr_debug("hci event: %d\n", event);
-
- switch (event) {
- case ST21NFCA_EVT_CARD_ACTIVATED:
- if (gate == ST21NFCA_RF_CARD_F_GATE)
- info->dep_info.curr_nfc_dep_pni = 0;
- break;
- case ST21NFCA_EVT_CARD_DEACTIVATED:
- break;
- case ST21NFCA_EVT_FIELD_ON:
- break;
- case ST21NFCA_EVT_FIELD_OFF:
- break;
- case ST21NFCA_EVT_SEND_DATA:
- if (gate == ST21NFCA_RF_CARD_F_GATE) {
- r = st21nfca_tm_event_send_data(hdev, skb, gate);
- if (r < 0)
- return r;
- return 0;
- }
- info->dep_info.curr_nfc_dep_pni = 0;
- return 1;
+ switch (gate) {
+ case ST21NFCA_RF_CARD_F_GATE:
+ return st21nfca_dep_event_received(hdev, event, skb);
default:
return 1;
}
diff --git a/drivers/nfc/st21nfca/st21nfca.h b/drivers/nfc/st21nfca/st21nfca.h
index a0b77f1..7c2a852 100644
--- a/drivers/nfc/st21nfca/st21nfca.h
+++ b/drivers/nfc/st21nfca/st21nfca.h
@@ -85,6 +85,4 @@
#define ST21NFCA_RF_CARD_F_GATE 0x24
-#define ST21NFCA_EVT_SEND_DATA 0x10
-
#endif /* __LOCAL_ST21NFCA_H_ */
diff --git a/drivers/nfc/st21nfca/st21nfca_dep.c b/drivers/nfc/st21nfca/st21nfca_dep.c
index bfb6df5..8882181 100644
--- a/drivers/nfc/st21nfca/st21nfca_dep.c
+++ b/drivers/nfc/st21nfca/st21nfca_dep.c
@@ -49,6 +49,12 @@
#define ST21NFCA_LR_BITS_PAYLOAD_SIZE_254B 0x30
#define ST21NFCA_GB_BIT 0x02
+#define ST21NFCA_EVT_SEND_DATA 0x10
+#define ST21NFCA_EVT_FIELD_ON 0x11
+#define ST21NFCA_EVT_CARD_DEACTIVATED 0x12
+#define ST21NFCA_EVT_CARD_ACTIVATED 0x13
+#define ST21NFCA_EVT_FIELD_OFF 0x14
+
#define ST21NFCA_EVT_CARD_F_BITRATE 0x16
#define ST21NFCA_EVT_READER_F_BITRATE 0x13
#define ST21NFCA_PSL_REQ_SEND_SPEED(brs) (brs & 0x38)
@@ -372,8 +378,8 @@
return r;
}
-int st21nfca_tm_event_send_data(struct nfc_hci_dev *hdev, struct sk_buff *skb,
- u8 gate)
+static int st21nfca_tm_event_send_data(struct nfc_hci_dev *hdev,
+ struct sk_buff *skb)
{
u8 cmd0, cmd1;
int r;
@@ -400,7 +406,42 @@
}
return r;
}
-EXPORT_SYMBOL(st21nfca_tm_event_send_data);
+
+/*
+ * Returns:
+ * <= 0: driver handled the event, skb consumed
+ * 1: driver does not handle the event, please do standard processing
+ */
+int st21nfca_dep_event_received(struct nfc_hci_dev *hdev,
+ u8 event, struct sk_buff *skb)
+{
+ int r = 0;
+ struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+ pr_debug("dep event: %d\n", event);
+
+ switch (event) {
+ case ST21NFCA_EVT_CARD_ACTIVATED:
+ info->dep_info.curr_nfc_dep_pni = 0;
+ break;
+ case ST21NFCA_EVT_CARD_DEACTIVATED:
+ break;
+ case ST21NFCA_EVT_FIELD_ON:
+ break;
+ case ST21NFCA_EVT_FIELD_OFF:
+ break;
+ case ST21NFCA_EVT_SEND_DATA:
+ r = st21nfca_tm_event_send_data(hdev, skb);
+ if (r < 0)
+ return r;
+ return 0;
+ default:
+ return 1;
+ }
+ kfree_skb(skb);
+ return r;
+}
+EXPORT_SYMBOL(st21nfca_dep_event_received);
static void st21nfca_im_send_psl_req(struct nfc_hci_dev *hdev, u8 did, u8 bsi,
u8 bri, u8 lri)
diff --git a/drivers/nfc/st21nfca/st21nfca_dep.h b/drivers/nfc/st21nfca/st21nfca_dep.h
index ca213de..baf4664 100644
--- a/drivers/nfc/st21nfca/st21nfca_dep.h
+++ b/drivers/nfc/st21nfca/st21nfca_dep.h
@@ -32,8 +32,8 @@
u8 lri;
} __packed;
-int st21nfca_tm_event_send_data(struct nfc_hci_dev *hdev, struct sk_buff *skb,
- u8 gate);
+int st21nfca_dep_event_received(struct nfc_hci_dev *hdev,
+ u8 event, struct sk_buff *skb);
int st21nfca_tm_send_dep_res(struct nfc_hci_dev *hdev, struct sk_buff *skb);
int st21nfca_im_send_atr_req(struct nfc_hci_dev *hdev, u8 *gb, size_t gb_len);
diff --git a/drivers/nfc/st21nfcb/i2c.c b/drivers/nfc/st21nfcb/i2c.c
index c5d2427..01ba865 100644
--- a/drivers/nfc/st21nfcb/i2c.c
+++ b/drivers/nfc/st21nfcb/i2c.c
@@ -50,7 +50,6 @@
struct i2c_client *i2c_dev;
struct llt_ndlc *ndlc;
- unsigned int gpio_irq;
unsigned int gpio_reset;
unsigned int irq_polarity;
@@ -81,8 +80,6 @@
{
struct st21nfcb_i2c_phy *phy = phy_id;
- pr_info("\n");
-
phy->powered = 0;
/* reset chip in order to flush clf */
gpio_set_value(phy->gpio_reset, 0);
@@ -258,19 +255,11 @@
GPIOF_OUT_INIT_HIGH, "clf_reset");
if (r) {
nfc_err(&client->dev, "Failed to request reset pin\n");
- return -ENODEV;
+ return r;
}
phy->gpio_reset = gpio;
- /* IRQ */
- r = irq_of_parse_and_map(pp, 0);
- if (r < 0) {
- nfc_err(&client->dev, "Unable to get irq, error: %d\n", r);
- return r;
- }
-
- phy->irq_polarity = irq_get_trigger_type(r);
- client->irq = r;
+ phy->irq_polarity = irq_get_trigger_type(client->irq);
return 0;
}
@@ -286,7 +275,6 @@
struct st21nfcb_nfc_platform_data *pdata;
struct st21nfcb_i2c_phy *phy = i2c_get_clientdata(client);
int r;
- int irq;
pdata = client->dev.platform_data;
if (pdata == NULL) {
@@ -295,34 +283,16 @@
}
/* store for later use */
- phy->gpio_irq = pdata->gpio_irq;
phy->gpio_reset = pdata->gpio_reset;
phy->irq_polarity = pdata->irq_polarity;
- r = devm_gpio_request_one(&client->dev, phy->gpio_irq,
- GPIOF_IN, "clf_irq");
- if (r) {
- pr_err("%s : gpio_request failed\n", __FILE__);
- return -ENODEV;
- }
-
r = devm_gpio_request_one(&client->dev,
phy->gpio_reset, GPIOF_OUT_INIT_HIGH, "clf_reset");
if (r) {
pr_err("%s : reset gpio_request failed\n", __FILE__);
- return -ENODEV;
+ return r;
}
- /* IRQ */
- irq = gpio_to_irq(phy->gpio_irq);
- if (irq < 0) {
- nfc_err(&client->dev,
- "Unable to get irq number for GPIO %d error %d\n",
- phy->gpio_irq, r);
- return -ENODEV;
- }
- client->irq = irq;
-
return 0;
}
@@ -401,10 +371,13 @@
return 0;
}
+#ifdef CONFIG_OF
static const struct of_device_id of_st21nfcb_i2c_match[] = {
{ .compatible = "st,st21nfcb_i2c", },
{}
};
+MODULE_DEVICE_TABLE(of, of_st21nfcb_i2c_match);
+#endif
static struct i2c_driver st21nfcb_nci_i2c_driver = {
.driver = {
diff --git a/drivers/nfc/st21nfcb/ndlc.c b/drivers/nfc/st21nfcb/ndlc.c
index e7bff89..bac50e8 100644
--- a/drivers/nfc/st21nfcb/ndlc.c
+++ b/drivers/nfc/st21nfcb/ndlc.c
@@ -266,7 +266,7 @@
*ndlc_id = ndlc;
- /* start timers */
+ /* initialize timers */
init_timer(&ndlc->t1_timer);
ndlc->t1_timer.data = (unsigned long)ndlc;
ndlc->t1_timer.function = ndlc_t1_timeout;
diff --git a/drivers/nfc/trf7970a.c b/drivers/nfc/trf7970a.c
index d2ccd28..aa6a333 100644
--- a/drivers/nfc/trf7970a.c
+++ b/drivers/nfc/trf7970a.c
@@ -2154,7 +2154,7 @@
}
#endif
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
static int trf7970a_pm_runtime_suspend(struct device *dev)
{
struct spi_device *spi = container_of(dev, struct spi_device, dev);
diff --git a/drivers/ssb/Kconfig b/drivers/ssb/Kconfig
index 4bc1252..cc86fa1 100644
--- a/drivers/ssb/Kconfig
+++ b/drivers/ssb/Kconfig
@@ -128,7 +128,7 @@
PCIcore hostmode operation (external PCI bus).
config SSB_DRIVER_MIPS
- depends on !BACKPORT_KERNEL_3_3
+ depends on !KERNEL_3_3
bool "SSB Broadcom MIPS core driver"
depends on SSB && MIPS
select SSB_SERIAL
diff --git a/drivers/ssb/driver_mipscore.c b/drivers/ssb/driver_mipscore.c
index 0907706..7b986f9 100644
--- a/drivers/ssb/driver_mipscore.c
+++ b/drivers/ssb/driver_mipscore.c
@@ -15,6 +15,9 @@
#include <linux/serial_core.h>
#include <linux/serial_reg.h>
#include <linux/time.h>
+#ifdef CONFIG_BCM47XX
+#include <bcm47xx_nvram.h>
+#endif
#include "ssb_private.h"
@@ -210,6 +213,7 @@
static void ssb_mips_flash_detect(struct ssb_mipscore *mcore)
{
struct ssb_bus *bus = mcore->dev->bus;
+ struct ssb_sflash *sflash = &mcore->sflash;
struct ssb_pflash *pflash = &mcore->pflash;
/* When there is no chipcommon on the bus there is 4MB flash */
@@ -242,7 +246,15 @@
}
ssb_pflash:
- if (pflash->present) {
+ if (sflash->present) {
+#ifdef CONFIG_BCM47XX
+ bcm47xx_nvram_init_from_mem(sflash->window, sflash->size);
+#endif
+ } else if (pflash->present) {
+#ifdef CONFIG_BCM47XX
+ bcm47xx_nvram_init_from_mem(pflash->window, pflash->window_size);
+#endif
+
ssb_pflash_data.width = pflash->buswidth;
ssb_pflash_resource.start = pflash->window;
ssb_pflash_resource.end = pflash->window + pflash->window_size;
diff --git a/drivers/ssb/pcihost_wrapper.c b/drivers/ssb/pcihost_wrapper.c
index 69161bb..b9ea3fb 100644
--- a/drivers/ssb/pcihost_wrapper.c
+++ b/drivers/ssb/pcihost_wrapper.c
@@ -11,15 +11,17 @@
* Licensed under the GNU/GPL. See COPYING for details.
*/
+#include <linux/pm.h>
#include <linux/pci.h>
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/ssb/ssb.h>
-#ifdef CONFIG_PM
-static int ssb_pcihost_suspend(struct pci_dev *dev, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int ssb_pcihost_suspend(struct device *d)
{
+ struct pci_dev *dev = to_pci_dev(d);
struct ssb_bus *ssb = pci_get_drvdata(dev);
int err;
@@ -28,17 +30,25 @@
return err;
pci_save_state(dev);
pci_disable_device(dev);
- pci_set_power_state(dev, pci_choose_state(dev, state));
+
+ /* if there is a wakeup enabled child device on ssb bus,
+ enable pci wakeup posibility. */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0)
+ device_set_wakeup_enable(d, d->power.wakeup_path);
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0) */
+
+ pci_prepare_to_sleep(dev);
return 0;
}
-static int ssb_pcihost_resume(struct pci_dev *dev)
+static int ssb_pcihost_resume(struct device *d)
{
+ struct pci_dev *dev = to_pci_dev(d);
struct ssb_bus *ssb = pci_get_drvdata(dev);
int err;
- pci_set_power_state(dev, PCI_D0);
+ pci_back_from_sleep(dev);
err = pci_enable_device(dev);
if (err)
return err;
@@ -49,10 +59,12 @@
return 0;
}
-#else /* CONFIG_PM */
-# define ssb_pcihost_suspend NULL
-# define ssb_pcihost_resume NULL
-#endif /* CONFIG_PM */
+
+static const struct dev_pm_ops ssb_pcihost_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(ssb_pcihost_suspend, ssb_pcihost_resume)
+};
+
+#endif /* CONFIG_PM_SLEEP */
static int ssb_pcihost_probe(struct pci_dev *dev,
const struct pci_device_id *id)
@@ -115,8 +127,9 @@
{
driver->probe = ssb_pcihost_probe;
driver->remove = ssb_pcihost_remove;
- driver->suspend = ssb_pcihost_suspend;
- driver->resume = ssb_pcihost_resume;
+#ifdef CONFIG_PM_SLEEP
+ driver->driver.pm = &ssb_pcihost_pm_ops;
+#endif
return pci_register_driver(driver);
}
diff --git a/include/linux/ath9k_platform.h b/include/linux/ath9k_platform.h
index a495a95..33eb274 100644
--- a/include/linux/ath9k_platform.h
+++ b/include/linux/ath9k_platform.h
@@ -31,8 +31,11 @@
u32 gpio_mask;
u32 gpio_val;
+ bool endian_check;
bool is_clk_25mhz;
bool tx_gain_buffalo;
+ bool disable_2ghz;
+ bool disable_5ghz;
int (*get_mac_revision)(void);
int (*external_reset)(void);
diff --git a/include/linux/average.h b/include/linux/average.h
index dc5ba21..e900121 100644
--- a/include/linux/average.h
+++ b/include/linux/average.h
@@ -1,5 +1,5 @@
/* Automatically created during backport process */
-#ifndef CPTCFG_BACKPORT_BUILD_AVERAGE
+#ifndef CPTCFG_BPAUTO_BUILD_AVERAGE
#include_next <linux/average.h>
#else
#undef ewma_init
@@ -7,4 +7,4 @@
#undef ewma_add
#define ewma_add LINUX_BACKPORT(ewma_add)
#include <linux/backport-average.h>
-#endif /* CPTCFG_BACKPORT_BUILD_AVERAGE */
+#endif /* CPTCFG_BPAUTO_BUILD_AVERAGE */
diff --git a/include/linux/backport-devcoredump.h b/include/linux/backport-devcoredump.h
new file mode 100644
index 0000000..c1d2d00
--- /dev/null
+++ b/include/linux/backport-devcoredump.h
@@ -0,0 +1,35 @@
+#ifndef __DEVCOREDUMP_H
+#define __DEVCOREDUMP_H
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+
+#ifdef CPTCFG_BPAUTO_WANT_DEV_COREDUMP
+void dev_coredumpv(struct device *dev, const void *data, size_t datalen,
+ gfp_t gfp);
+
+void dev_coredumpm(struct device *dev, struct module *owner,
+ const void *data, size_t datalen, gfp_t gfp,
+ ssize_t (*read)(char *buffer, loff_t offset, size_t count,
+ const void *data, size_t datalen),
+ void (*free)(const void *data));
+#else
+static inline void dev_coredumpv(struct device *dev, const void *data,
+ size_t datalen, gfp_t gfp)
+{
+ vfree(data);
+}
+
+static inline void
+dev_coredumpm(struct device *dev, struct module *owner,
+ const void *data, size_t datalen, gfp_t gfp,
+ ssize_t (*read)(char *buffer, loff_t offset, size_t count,
+ const void *data, size_t datalen),
+ void (*free)(const void *data))
+{
+ free(data);
+}
+#endif /* CPTCFG_BPAUTO_WANT_DEV_COREDUMP */
+
+#endif /* __DEVCOREDUMP_H */
diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h
index 8356303..2ad74c6 100644
--- a/include/linux/bcma/bcma.h
+++ b/include/linux/bcma/bcma.h
@@ -447,4 +447,6 @@
#define BCMA_DMA_TRANSLATION_DMA64_CMT 0x80000000 /* Client Mode Translation for 64-bit DMA */
extern u32 bcma_core_dma_translation(struct bcma_device *core);
+extern unsigned int bcma_core_irq(struct bcma_device *core, int num);
+
#endif /* LINUX_BCMA_H_ */
diff --git a/include/linux/bcma/bcma_driver_mips.h b/include/linux/bcma/bcma_driver_mips.h
index 73c8a7b..907c48c 100644
--- a/include/linux/bcma/bcma_driver_mips.h
+++ b/include/linux/bcma/bcma_driver_mips.h
@@ -43,12 +43,12 @@
extern void bcma_core_mips_init(struct bcma_drv_mips *mcore);
extern void bcma_core_mips_early_init(struct bcma_drv_mips *mcore);
-extern unsigned int bcma_core_irq(struct bcma_device *core);
+extern unsigned int bcma_core_mips_irq(struct bcma_device *dev);
#else
static inline void bcma_core_mips_init(struct bcma_drv_mips *mcore) { }
static inline void bcma_core_mips_early_init(struct bcma_drv_mips *mcore) { }
-static inline unsigned int bcma_core_irq(struct bcma_device *core)
+static inline unsigned int bcma_core_mips_irq(struct bcma_device *dev)
{
return 0;
}
diff --git a/include/linux/devcoredump.h b/include/linux/devcoredump.h
new file mode 100644
index 0000000..9c215fa
--- /dev/null
+++ b/include/linux/devcoredump.h
@@ -0,0 +1,10 @@
+/* Automatically created during backport process */
+#ifndef CPTCFG_BPAUTO_BUILD_WANT_DEV_COREDUMP
+#include_next <linux/devcoredump.h>
+#else
+#undef dev_coredumpv
+#define dev_coredumpv LINUX_BACKPORT(dev_coredumpv)
+#undef dev_coredumpm
+#define dev_coredumpm LINUX_BACKPORT(dev_coredumpm)
+#include <linux/backport-devcoredump.h>
+#endif /* CPTCFG_BPAUTO_BUILD_WANT_DEV_COREDUMP */
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index b1be39c..4f4eea8 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -19,6 +19,7 @@
#include <linux/types.h>
#include <linux/if_ether.h>
#include <asm/byteorder.h>
+#include <asm/unaligned.h>
/*
* DS bit usage
@@ -1066,6 +1067,12 @@
/* TDLS */
+/* Channel switch timing */
+struct ieee80211_ch_switch_timing {
+ __le16 switch_time;
+ __le16 switch_timeout;
+} __packed;
+
/* Link-id information element */
struct ieee80211_tdls_lnkie {
u8 ie_type; /* Link Identifier IE */
@@ -1107,6 +1114,15 @@
u8 dialog_token;
u8 variable[0];
} __packed discover_req;
+ struct {
+ u8 target_channel;
+ u8 oper_class;
+ u8 variable[0];
+ } __packed chan_switch_req;
+ struct {
+ __le16 status_code;
+ u8 variable[0];
+ } __packed chan_switch_resp;
} u;
} __packed;
@@ -1274,7 +1290,7 @@
#define IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT 2
/*
- * Maximum length of AMPDU that the STA can receive.
+ * Maximum length of AMPDU that the STA can receive in high-throughput (HT).
* Length = 2 ^ (13 + max_ampdu_length_exp) - 1 (octets)
*/
enum ieee80211_max_ampdu_length_exp {
@@ -1284,6 +1300,21 @@
IEEE80211_HT_MAX_AMPDU_64K = 3
};
+/*
+ * Maximum length of AMPDU that the STA can receive in VHT.
+ * Length = 2 ^ (13 + max_ampdu_length_exp) - 1 (octets)
+ */
+enum ieee80211_vht_max_ampdu_length_exp {
+ IEEE80211_VHT_MAX_AMPDU_8K = 0,
+ IEEE80211_VHT_MAX_AMPDU_16K = 1,
+ IEEE80211_VHT_MAX_AMPDU_32K = 2,
+ IEEE80211_VHT_MAX_AMPDU_64K = 3,
+ IEEE80211_VHT_MAX_AMPDU_128K = 4,
+ IEEE80211_VHT_MAX_AMPDU_256K = 5,
+ IEEE80211_VHT_MAX_AMPDU_512K = 6,
+ IEEE80211_VHT_MAX_AMPDU_1024K = 7
+};
+
#define IEEE80211_HT_MAX_AMPDU_FACTOR 13
/* Minimum MPDU start spacing */
@@ -1998,6 +2029,16 @@
WLAN_TDLS_DISCOVERY_REQUEST = 10,
};
+/* Extended Channel Switching capability to be set in the 1st byte of
+ * the @WLAN_EID_EXT_CAPABILITY information element
+ */
+#define WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING BIT(2)
+
+/* TDLS capabilities in the the 4th byte of @WLAN_EID_EXT_CAPABILITY */
+#define WLAN_EXT_CAPA4_TDLS_BUFFER_STA BIT(4)
+#define WLAN_EXT_CAPA4_TDLS_PEER_PSM BIT(5)
+#define WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH BIT(6)
+
/* Interworking capabilities are set in 7th bit of 4th byte of the
* @WLAN_EID_EXT_CAPABILITY information element
*/
@@ -2009,6 +2050,7 @@
*/
#define WLAN_EXT_CAPA5_TDLS_ENABLED BIT(5)
#define WLAN_EXT_CAPA5_TDLS_PROHIBITED BIT(6)
+#define WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED BIT(7)
#define WLAN_EXT_CAPA8_OPMODE_NOTIF BIT(6)
#define WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED BIT(7)
@@ -2016,6 +2058,9 @@
/* TDLS specific payload type in the LLC/SNAP header */
#define WLAN_TDLS_SNAP_RFTYPE 0x2
+/* BSS Coex IE information field bits */
+#define WLAN_BSS_COEX_INFORMATION_REQUEST BIT(0)
+
/**
* enum - mesh synchronization method identifier
*
@@ -2398,6 +2443,30 @@
return !!(tim->virtual_map[index] & mask);
}
+/**
+ * ieee80211_get_tdls_action - get tdls packet action (or -1, if not tdls packet)
+ * @skb: the skb containing the frame, length will not be checked
+ * @hdr_size: the size of the ieee80211_hdr that starts at skb->data
+ *
+ * This function assumes the frame is a data frame, and that the network header
+ * is in the correct place.
+ */
+static inline int ieee80211_get_tdls_action(struct sk_buff *skb, u32 hdr_size)
+{
+ if (!skb_is_nonlinear(skb) &&
+ skb->len > (skb_network_offset(skb) + 2)) {
+ /* Point to where the indication of TDLS should start */
+ const u8 *tdls_data = skb_network_header(skb) - 2;
+
+ if (get_unaligned_be16(tdls_data) == ETH_P_TDLS &&
+ tdls_data[2] == WLAN_TDLS_SNAP_RFTYPE &&
+ tdls_data[3] == WLAN_CATEGORY_TDLS)
+ return tdls_data[4];
+ }
+
+ return -1;
+}
+
/* convert time units */
#define TU_TO_JIFFIES(x) (usecs_to_jiffies((x) * 1024))
#define TU_TO_EXP_TIME(x) (jiffies + TU_TO_JIFFIES(x))
diff --git a/include/net/ieee802154.h b/include/linux/ieee802154.h
similarity index 79%
rename from include/net/ieee802154.h
rename to include/linux/ieee802154.h
index 0aa7122..6e82d88 100644
--- a/include/net/ieee802154.h
+++ b/include/linux/ieee802154.h
@@ -12,10 +12,6 @@
* 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.
- *
* Written by:
* Pavel Smolenskiy <pavel.smolenskiy@gmail.com>
* Maxim Gorbachyov <maxim.gorbachev@siemens.com>
@@ -24,10 +20,27 @@
* Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
*/
-#ifndef NET_IEEE802154_H
-#define NET_IEEE802154_H
+#ifndef LINUX_IEEE802154_H
+#define LINUX_IEEE802154_H
+
+#include <linux/types.h>
+#include <linux/random.h>
+#include <asm/byteorder.h>
#define IEEE802154_MTU 127
+#define IEEE802154_MIN_PSDU_LEN 5
+
+#define IEEE802154_PAN_ID_BROADCAST 0xffff
+#define IEEE802154_ADDR_SHORT_BROADCAST 0xffff
+#define IEEE802154_ADDR_SHORT_UNSPEC 0xfffe
+
+#define IEEE802154_EXTENDED_ADDR_LEN 8
+
+#define IEEE802154_LIFS_PERIOD 40
+#define IEEE802154_SIFS_PERIOD 12
+
+#define IEEE802154_MAX_CHANNEL 26
+#define IEEE802154_MAX_PAGE 31
#define IEEE802154_FC_TYPE_BEACON 0x0 /* Frame is beacon */
#define IEEE802154_FC_TYPE_DATA 0x1 /* Frame is data */
@@ -189,7 +202,41 @@
IEEE802154_SCAN_IN_PROGRESS = 0xfc,
};
+/**
+ * ieee802154_is_valid_psdu_len - check if psdu len is valid
+ * @len: psdu len with (MHR + payload + MFR)
+ */
+static inline bool ieee802154_is_valid_psdu_len(const u8 len)
+{
+ return (len >= IEEE802154_MIN_PSDU_LEN && len <= IEEE802154_MTU);
+}
-#endif
+/**
+ * ieee802154_is_valid_psdu_len - check if extended addr is valid
+ * @addr: extended addr to check
+ */
+static inline bool ieee802154_is_valid_extended_addr(const __le64 addr)
+{
+ /* These EUI-64 addresses are reserved by IEEE. 0xffffffffffffffff
+ * is used internally as extended to short address broadcast mapping.
+ * This is currently a workaround because neighbor discovery can't
+ * deal with short addresses types right now.
+ */
+ return ((addr != cpu_to_le64(0x0000000000000000ULL)) &&
+ (addr != cpu_to_le64(0xffffffffffffffffULL)));
+}
+/**
+ * ieee802154_random_extended_addr - generates a random extended address
+ * @addr: extended addr pointer to place the random address
+ */
+static inline void ieee802154_random_extended_addr(__le64 *addr)
+{
+ get_random_bytes(addr, IEEE802154_EXTENDED_ADDR_LEN);
+ /* toggle some bit if we hit an invalid extended addr */
+ if (!ieee802154_is_valid_extended_addr(*addr))
+ ((u8 *)addr)[IEEE802154_EXTENDED_ADDR_LEN - 1] ^= 0x01;
+}
+
+#endif /* LINUX_IEEE802154_H */
diff --git a/include/linux/nl802154.h b/include/linux/nl802154.h
index 20163b9..167342c 100644
--- a/include/linux/nl802154.h
+++ b/include/linux/nl802154.h
@@ -12,10 +12,6 @@
* 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 NL802154_H
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 1fa99a3..e63c02a 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -522,6 +522,8 @@
#define PCI_DEVICE_ID_AMD_15H_M10H_F3 0x1403
#define PCI_DEVICE_ID_AMD_15H_M30H_NB_F3 0x141d
#define PCI_DEVICE_ID_AMD_15H_M30H_NB_F4 0x141e
+#define PCI_DEVICE_ID_AMD_15H_M60H_NB_F3 0x1573
+#define PCI_DEVICE_ID_AMD_15H_M60H_NB_F4 0x1574
#define PCI_DEVICE_ID_AMD_15H_NB_F0 0x1600
#define PCI_DEVICE_ID_AMD_15H_NB_F1 0x1601
#define PCI_DEVICE_ID_AMD_15H_NB_F2 0x1602
@@ -562,6 +564,7 @@
#define PCI_DEVICE_ID_AMD_8131_BRIDGE 0x7450
#define PCI_DEVICE_ID_AMD_8131_APIC 0x7451
#define PCI_DEVICE_ID_AMD_8132_BRIDGE 0x7458
+#define PCI_DEVICE_ID_AMD_NL_USB 0x7912
#define PCI_DEVICE_ID_AMD_CS5535_IDE 0x208F
#define PCI_DEVICE_ID_AMD_CS5536_ISA 0x2090
#define PCI_DEVICE_ID_AMD_CS5536_FLASH 0x2091
diff --git a/include/linux/platform_data/st21nfca.h b/include/linux/platform_data/st21nfca.h
index 1730312..5087fff 100644
--- a/include/linux/platform_data/st21nfca.h
+++ b/include/linux/platform_data/st21nfca.h
@@ -24,7 +24,6 @@
#define ST21NFCA_HCI_DRIVER_NAME "st21nfca_hci"
struct st21nfca_nfc_platform_data {
- unsigned int gpio_irq;
unsigned int gpio_ena;
unsigned int irq_polarity;
};
diff --git a/include/linux/platform_data/st21nfcb.h b/include/linux/platform_data/st21nfcb.h
index 2d11f1f..c3b432f 100644
--- a/include/linux/platform_data/st21nfcb.h
+++ b/include/linux/platform_data/st21nfcb.h
@@ -24,7 +24,6 @@
#define ST21NFCB_NCI_DRIVER_NAME "st21nfcb_nci"
struct st21nfcb_nfc_platform_data {
- unsigned int gpio_irq;
unsigned int gpio_reset;
unsigned int irq_polarity;
};
diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h
index 26088fe..d9a4905 100644
--- a/include/linux/usb/usbnet.h
+++ b/include/linux/usb/usbnet.h
@@ -78,6 +78,7 @@
# define EVENT_NO_RUNTIME_PM 9
# define EVENT_RX_KILL 10
# define EVENT_LINK_CHANGE 11
+# define EVENT_SET_RX_MODE 12
};
static inline struct usb_driver *driver_of(struct usb_interface *intf)
@@ -159,6 +160,9 @@
/* called by minidriver when receiving indication */
void (*indication)(struct usbnet *dev, void *ind, int indlen);
+ /* rx mode change (device changes address list filtering) */
+ void (*set_rx_mode)(struct usbnet *dev);
+
/* for new devices, use the descriptor-reading code instead */
int in; /* rx endpoint */
int out; /* tx endpoint */
diff --git a/include/media/davinci/vpbe.h b/include/media/davinci/vpbe.h
index 57585c7..4376bee 100644
--- a/include/media/davinci/vpbe.h
+++ b/include/media/davinci/vpbe.h
@@ -63,7 +63,7 @@
* output basis. If per mode is needed, we may have to move this to
* mode_info structure
*/
- enum v4l2_mbus_pixelcode if_params;
+ u32 if_params;
};
/* encoder configuration info */
diff --git a/include/media/davinci/vpbe_display.h b/include/media/davinci/vpbe_display.h
index 637749a..fa0247a 100644
--- a/include/media/davinci/vpbe_display.h
+++ b/include/media/davinci/vpbe_display.h
@@ -70,8 +70,6 @@
/* vpbe display object structure */
struct vpbe_layer {
- /* number of buffers in fbuffers */
- unsigned int numbuffers;
/* Pointer to the vpbe_display */
struct vpbe_display *disp_dev;
/* Pointer pointing to current v4l2_buffer */
@@ -91,10 +89,6 @@
/* V4l2 specific parameters */
/* Identifies video device for this layer */
struct video_device video_dev;
- /* This field keeps track of type of buffer exchange mechanism user
- * has selected
- */
- enum v4l2_memory memory;
/* Used to store pixel format */
struct v4l2_pix_format pix_fmt;
enum v4l2_field buf_field;
@@ -106,12 +100,8 @@
unsigned char window_enable;
/* number of open instances of the layer */
unsigned int usrs;
- /* number of users performing IO */
- unsigned int io_usrs;
/* Indicates id of the field which is being displayed */
unsigned int field_id;
- /* Indicates whether streaming started */
- unsigned char started;
/* Identifies device object */
enum vpbe_display_device_id device_id;
/* facilitation of ioctl ops lock by v4l2*/
@@ -131,17 +121,6 @@
struct osd_state *osd_device;
};
-/* File handle structure */
-struct vpbe_fh {
- struct v4l2_fh fh;
- /* vpbe device structure */
- struct vpbe_display *disp_dev;
- /* pointer to layer object for opened device */
- struct vpbe_layer *layer;
- /* Indicates whether this file handle is doing IO */
- unsigned char io_allowed;
-};
-
struct buf_config_params {
unsigned char min_numbuffers;
unsigned char numbuffers[VPBE_DISPLAY_MAX_DEVICES];
diff --git a/include/media/davinci/vpbe_venc.h b/include/media/davinci/vpbe_venc.h
index 476fafc..3dbd200 100644
--- a/include/media/davinci/vpbe_venc.h
+++ b/include/media/davinci/vpbe_venc.h
@@ -30,11 +30,10 @@
#define VENC_SECOND_FIELD BIT(2)
struct venc_platform_data {
- int (*setup_pinmux)(enum v4l2_mbus_pixelcode if_type,
- int field);
+ int (*setup_pinmux)(u32 if_type, int field);
int (*setup_clock)(enum vpbe_enc_timings_type type,
unsigned int pixclock);
- int (*setup_if_config)(enum v4l2_mbus_pixelcode pixcode);
+ int (*setup_if_config)(u32 pixcode);
/* Number of LCD outputs supported */
int num_lcd_outputs;
struct vpbe_if_params *lcd_if_params;
diff --git a/include/media/exynos-fimc.h b/include/media/exynos-fimc.h
index aa44660..69bcd2a 100644
--- a/include/media/exynos-fimc.h
+++ b/include/media/exynos-fimc.h
@@ -101,7 +101,7 @@
* @flags: flags indicating which operation mode format applies to
*/
struct fimc_fmt {
- enum v4l2_mbus_pixelcode mbus_code;
+ u32 mbus_code;
char *name;
u32 fourcc;
u32 color;
diff --git a/include/media/lirc_dev.h b/include/media/lirc_dev.h
index 78f0637..05e7ad5 100644
--- a/include/media/lirc_dev.h
+++ b/include/media/lirc_dev.h
@@ -29,14 +29,13 @@
/* Using chunks instead of bytes pretends to simplify boundary checking
* And should allow for some performance fine tunning later */
struct kfifo fifo;
- u8 fifo_initialized;
};
static inline void lirc_buffer_clear(struct lirc_buffer *buf)
{
unsigned long flags;
- if (buf->fifo_initialized) {
+ if (kfifo_initialized(&buf->fifo)) {
spin_lock_irqsave(&buf->fifo_lock, flags);
kfifo_reset(&buf->fifo);
spin_unlock_irqrestore(&buf->fifo_lock, flags);
@@ -56,17 +55,14 @@
buf->chunk_size = chunk_size;
buf->size = size;
ret = kfifo_alloc(&buf->fifo, size * chunk_size, GFP_KERNEL);
- if (ret == 0)
- buf->fifo_initialized = 1;
return ret;
}
static inline void lirc_buffer_free(struct lirc_buffer *buf)
{
- if (buf->fifo_initialized) {
+ if (kfifo_initialized(&buf->fifo)) {
kfifo_free(&buf->fifo);
- buf->fifo_initialized = 0;
} else
WARN(1, "calling %s on an uninitialized lirc_buffer\n",
__func__);
diff --git a/include/media/radio-si4713.h b/include/media/radio-si4713.h
deleted file mode 100644
index f6aae29..0000000
--- a/include/media/radio-si4713.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * include/media/radio-si4713.h
- *
- * Board related data definitions for Si4713 radio transmitter chip.
- *
- * Copyright (c) 2009 Nokia Corporation
- * Contact: Eduardo Valentin <eduardo.valentin@nokia.com>
- *
- * This file is licensed under the terms of the GNU General Public License
- * version 2. This program is licensed "as is" without any warranty of any
- * kind, whether express or implied.
- *
- */
-
-#ifndef RADIO_SI4713_H
-#define RADIO_SI4713_H
-
-#include <linux/i2c.h>
-
-#define SI4713_NAME "radio-si4713"
-
-/*
- * Platform dependent definition
- */
-struct radio_si4713_platform_data {
- int i2c_bus;
- struct i2c_board_info *subdev_board_info;
-};
-
-#endif /* ifndef RADIO_SI4713_H*/
diff --git a/include/media/si4713.h b/include/media/si4713.h
index f98a0a7..be4f58e 100644
--- a/include/media/si4713.h
+++ b/include/media/si4713.h
@@ -23,9 +23,7 @@
* Platform dependent definition
*/
struct si4713_platform_data {
- const char * const *supply_names;
- unsigned supplies;
- int gpio_reset; /* < 0 if not used */
+ bool is_platform_device;
};
/*
diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h
index 865246b..2f6261f 100644
--- a/include/media/soc_camera.h
+++ b/include/media/soc_camera.h
@@ -296,7 +296,7 @@
* format setup.
*/
struct soc_camera_format_xlate {
- enum v4l2_mbus_pixelcode code;
+ u32 code;
const struct soc_mbus_pixelfmt *host_fmt;
};
diff --git a/include/media/soc_mediabus.h b/include/media/soc_mediabus.h
index d33f6d0..2ff7737 100644
--- a/include/media/soc_mediabus.h
+++ b/include/media/soc_mediabus.h
@@ -91,16 +91,16 @@
* @fmt: pixel format description
*/
struct soc_mbus_lookup {
- enum v4l2_mbus_pixelcode code;
+ u32 code;
struct soc_mbus_pixelfmt fmt;
};
const struct soc_mbus_pixelfmt *soc_mbus_find_fmtdesc(
- enum v4l2_mbus_pixelcode code,
+ u32 code,
const struct soc_mbus_lookup *lookup,
int n);
const struct soc_mbus_pixelfmt *soc_mbus_get_fmtdesc(
- enum v4l2_mbus_pixelcode code);
+ u32 code);
s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf);
s32 soc_mbus_image_size(const struct soc_mbus_pixelfmt *mf,
u32 bytes_per_line, u32 height);
diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
index 48f9748..1cc0c5b 100644
--- a/include/media/v4l2-common.h
+++ b/include/media/v4l2-common.h
@@ -80,24 +80,9 @@
/* ------------------------------------------------------------------------- */
-/* Control helper functions */
+/* Control helper function */
-int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl,
- const char * const *menu_items);
-const char *v4l2_ctrl_get_name(u32 id);
-const char * const *v4l2_ctrl_get_menu(u32 id);
-const s64 *v4l2_ctrl_get_int_menu(u32 id, u32 *len);
int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def);
-int v4l2_ctrl_query_menu(struct v4l2_querymenu *qmenu,
- struct v4l2_queryctrl *qctrl, const char * const *menu_items);
-#define V4L2_CTRL_MENU_IDS_END (0xffffffff)
-int v4l2_ctrl_query_menu_valid_items(struct v4l2_querymenu *qmenu, const u32 *ids);
-
-/* Note: ctrl_classes points to an array of u32 pointers. Each u32 array is a
- 0-terminated array of control IDs. Each array must be sorted low to high
- and belong to the same control class. The array of u32 pointers must also
- be sorted, from low class IDs to high class IDs. */
-u32 v4l2_ctrl_next(const u32 * const *ctrl_classes, u32 id);
/* ------------------------------------------------------------------------- */
diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
index b7cd7a6..911f3e5 100644
--- a/include/media/v4l2-ctrls.h
+++ b/include/media/v4l2-ctrls.h
@@ -670,6 +670,31 @@
*/
void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void *priv);
+/** v4l2_ctrl_get_name() - Get the name of the control
+ * @id: The control ID.
+ *
+ * This function returns the name of the given control ID or NULL if it isn't
+ * a known control.
+ */
+const char *v4l2_ctrl_get_name(u32 id);
+
+/** v4l2_ctrl_get_menu() - Get the menu string array of the control
+ * @id: The control ID.
+ *
+ * This function returns the NULL-terminated menu string array name of the
+ * given control ID or NULL if it isn't a known menu control.
+ */
+const char * const *v4l2_ctrl_get_menu(u32 id);
+
+/** v4l2_ctrl_get_int_menu() - Get the integer menu array of the control
+ * @id: The control ID.
+ * @len: The size of the integer array.
+ *
+ * This function returns the integer array of the given control ID or NULL if it
+ * if it isn't a known integer menu control.
+ */
+const s64 *v4l2_ctrl_get_int_menu(u32 id, u32 *len);
+
/** v4l2_ctrl_g_ctrl() - Helper function to get the control's value from within a driver.
* @ctrl: The control.
*
diff --git a/include/media/v4l2-image-sizes.h b/include/media/v4l2-image-sizes.h
index 10daf92..a07d7a6 100644
--- a/include/media/v4l2-image-sizes.h
+++ b/include/media/v4l2-image-sizes.h
@@ -25,10 +25,19 @@
#define QVGA_WIDTH 320
#define QVGA_HEIGHT 240
+#define SVGA_WIDTH 800
+#define SVGA_HEIGHT 600
+
#define SXGA_WIDTH 1280
#define SXGA_HEIGHT 1024
#define VGA_WIDTH 640
#define VGA_HEIGHT 480
+#define UXGA_WIDTH 1600
+#define UXGA_HEIGHT 1200
+
+#define XGA_WIDTH 1024
+#define XGA_HEIGHT 768
+
#endif /* _IMAGE_SIZES_H */
diff --git a/include/media/v4l2-mediabus.h b/include/media/v4l2-mediabus.h
index 395c4a9..38d960d 100644
--- a/include/media/v4l2-mediabus.h
+++ b/include/media/v4l2-mediabus.h
@@ -94,16 +94,20 @@
pix_fmt->height = mbus_fmt->height;
pix_fmt->field = mbus_fmt->field;
pix_fmt->colorspace = mbus_fmt->colorspace;
+ pix_fmt->ycbcr_enc = mbus_fmt->ycbcr_enc;
+ pix_fmt->quantization = mbus_fmt->quantization;
}
static inline void v4l2_fill_mbus_format(struct v4l2_mbus_framefmt *mbus_fmt,
const struct v4l2_pix_format *pix_fmt,
- enum v4l2_mbus_pixelcode code)
+ u32 code)
{
mbus_fmt->width = pix_fmt->width;
mbus_fmt->height = pix_fmt->height;
mbus_fmt->field = pix_fmt->field;
mbus_fmt->colorspace = pix_fmt->colorspace;
+ mbus_fmt->ycbcr_enc = pix_fmt->ycbcr_enc;
+ mbus_fmt->quantization = pix_fmt->quantization;
mbus_fmt->code = code;
}
diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
index 8e95674..063a83e 100644
--- a/include/media/v4l2-subdev.h
+++ b/include/media/v4l2-subdev.h
@@ -341,7 +341,7 @@
int (*query_dv_timings)(struct v4l2_subdev *sd,
struct v4l2_dv_timings *timings);
int (*enum_mbus_fmt)(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code);
+ u32 *code);
int (*enum_mbus_fsizes)(struct v4l2_subdev *sd,
struct v4l2_frmsizeenum *fsize);
int (*g_mbus_fmt)(struct v4l2_subdev *sd,
diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
index 69a9595..e320202 100644
--- a/include/media/videobuf2-core.h
+++ b/include/media/videobuf2-core.h
@@ -82,14 +82,17 @@
* unmap_dmabuf.
*/
struct vb2_mem_ops {
- void *(*alloc)(void *alloc_ctx, unsigned long size, gfp_t gfp_flags);
+ void *(*alloc)(void *alloc_ctx, unsigned long size,
+ enum dma_data_direction dma_dir,
+ gfp_t gfp_flags);
void (*put)(void *buf_priv);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
struct dma_buf *(*get_dmabuf)(void *buf_priv, unsigned long flags);
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) */
void *(*get_userptr)(void *alloc_ctx, unsigned long vaddr,
- unsigned long size, int write);
+ unsigned long size,
+ enum dma_data_direction dma_dir);
void (*put_userptr)(void *buf_priv);
void (*prepare)(void *buf_priv);
@@ -97,7 +100,8 @@
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0)
void *(*attach_dmabuf)(void *alloc_ctx, struct dma_buf *dbuf,
- unsigned long size, int write);
+ unsigned long size,
+ enum dma_data_direction dma_dir);
void (*detach_dmabuf)(void *buf_priv);
int (*map_dmabuf)(void *buf_priv);
void (*unmap_dmabuf)(void *buf_priv);
@@ -274,22 +278,24 @@
* queue setup from completing successfully; optional.
* @buf_prepare: called every time the buffer is queued from userspace
* and from the VIDIOC_PREPARE_BUF ioctl; drivers may
- * perform any initialization required before each hardware
- * operation in this callback; drivers that support
- * VIDIOC_CREATE_BUFS must also validate the buffer size;
- * if an error is returned, the buffer will not be queued
- * in driver; optional.
+ * perform any initialization required before each
+ * hardware operation in this callback; drivers can
+ * access/modify the buffer here as it is still synced for
+ * the CPU; drivers that support VIDIOC_CREATE_BUFS must
+ * also validate the buffer size; if an error is returned,
+ * the buffer will not be queued in driver; optional.
* @buf_finish: called before every dequeue of the buffer back to
- * userspace; drivers may perform any operations required
- * before userspace accesses the buffer; optional. The
- * buffer state can be one of the following: DONE and
- * ERROR occur while streaming is in progress, and the
- * PREPARED state occurs when the queue has been canceled
- * and all pending buffers are being returned to their
- * default DEQUEUED state. Typically you only have to do
- * something if the state is VB2_BUF_STATE_DONE, since in
- * all other cases the buffer contents will be ignored
- * anyway.
+ * userspace; the buffer is synced for the CPU, so drivers
+ * can access/modify the buffer contents; drivers may
+ * perform any operations required before userspace
+ * accesses the buffer; optional. The buffer state can be
+ * one of the following: DONE and ERROR occur while
+ * streaming is in progress, and the PREPARED state occurs
+ * when the queue has been canceled and all pending
+ * buffers are being returned to their default DEQUEUED
+ * state. Typically you only have to do something if the
+ * state is VB2_BUF_STATE_DONE, since in all other cases
+ * the buffer contents will be ignored anyway.
* @buf_cleanup: called once before the buffer is freed; drivers may
* perform any additional cleanup; optional.
* @start_streaming: called once to enter 'streaming' state; the driver may
diff --git a/include/media/videobuf2-dma-sg.h b/include/media/videobuf2-dma-sg.h
index 7b89852..14ce306 100644
--- a/include/media/videobuf2-dma-sg.h
+++ b/include/media/videobuf2-dma-sg.h
@@ -21,6 +21,9 @@
return (struct sg_table *)vb2_plane_cookie(vb, plane_no);
}
+void *vb2_dma_sg_init_ctx(struct device *dev);
+void vb2_dma_sg_cleanup_ctx(void *alloc_ctx);
+
extern const struct vb2_mem_ops vb2_dma_sg_memops;
#endif
diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h
index d184df1..dc03d77 100644
--- a/include/net/6lowpan.h
+++ b/include/net/6lowpan.h
@@ -372,12 +372,12 @@
return skb->len + uncomp_header - ret;
}
-typedef int (*skb_delivery_cb)(struct sk_buff *skb, struct net_device *dev);
-
-int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
- const u8 *saddr, const u8 saddr_type, const u8 saddr_len,
- const u8 *daddr, const u8 daddr_type, const u8 daddr_len,
- u8 iphc0, u8 iphc1, skb_delivery_cb skb_deliver);
+int
+lowpan_header_decompress(struct sk_buff *skb, struct net_device *dev,
+ const u8 *saddr, const u8 saddr_type,
+ const u8 saddr_len, const u8 *daddr,
+ const u8 daddr_type, const u8 daddr_len,
+ u8 iphc0, u8 iphc1);
int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
unsigned short type, const void *_daddr,
const void *_saddr, unsigned int len);
diff --git a/include/net/af_ieee802154.h b/include/net/af_ieee802154.h
index 085940f..7d38e2f 100644
--- a/include/net/af_ieee802154.h
+++ b/include/net/af_ieee802154.h
@@ -12,10 +12,6 @@
* 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.
- *
* Written by:
* Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 6e8f249..40129b3 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -129,6 +129,15 @@
* during the hdev->setup vendor callback.
*/
HCI_QUIRK_INVALID_BDADDR,
+
+ /* When this quirk is set, the duplicate filtering during
+ * scanning is based on Bluetooth devices addresses. To allow
+ * RSSI based updates, restart scanning if needed.
+ *
+ * This quirk can be set before hci_register_dev is called or
+ * during the hdev->setup vendor callback.
+ */
+ HCI_QUIRK_STRICT_DUPLICATE_FILTER,
};
/* HCI device flags */
@@ -154,6 +163,7 @@
enum {
HCI_DUT_MODE,
HCI_FORCE_SC,
+ HCI_FORCE_LESC,
HCI_FORCE_STATIC_ADDR,
};
@@ -265,6 +275,7 @@
/* Low Energy links do not have defined link type. Use invented one */
#define LE_LINK 0x80
#define AMP_LINK 0x81
+#define INVALID_LINK 0xff
/* LMP features */
#define LMP_3SLOT 0x01
@@ -332,6 +343,7 @@
#define HCI_LE_ENCRYPTION 0x01
#define HCI_LE_CONN_PARAM_REQ_PROC 0x02
#define HCI_LE_PING 0x10
+#define HCI_LE_EXT_SCAN_POLICY 0x80
/* Connection modes */
#define HCI_CM_ACTIVE 0x0000
@@ -401,6 +413,7 @@
/* The core spec defines 127 as the "not available" value */
#define HCI_TX_POWER_INVALID 127
+#define HCI_RSSI_INVALID 127
#define HCI_ROLE_MASTER 0x00
#define HCI_ROLE_SLAVE 0x01
@@ -629,7 +642,7 @@
struct hci_cp_remote_oob_data_reply {
bdaddr_t bdaddr;
__u8 hash[16];
- __u8 randomizer[16];
+ __u8 rand[16];
} __packed;
#define HCI_OP_REMOTE_OOB_DATA_NEG_REPLY 0x0433
@@ -721,9 +734,9 @@
struct hci_cp_remote_oob_ext_data_reply {
bdaddr_t bdaddr;
__u8 hash192[16];
- __u8 randomizer192[16];
+ __u8 rand192[16];
__u8 hash256[16];
- __u8 randomizer256[16];
+ __u8 rand256[16];
} __packed;
#define HCI_OP_SNIFF_MODE 0x0803
@@ -930,7 +943,7 @@
struct hci_rp_read_local_oob_data {
__u8 status;
__u8 hash[16];
- __u8 randomizer[16];
+ __u8 rand[16];
} __packed;
#define HCI_OP_READ_INQ_RSP_TX_POWER 0x0c58
@@ -1014,9 +1027,9 @@
struct hci_rp_read_local_oob_ext_data {
__u8 status;
__u8 hash192[16];
- __u8 randomizer192[16];
+ __u8 rand192[16];
__u8 hash256[16];
- __u8 randomizer256[16];
+ __u8 rand256[16];
} __packed;
#define HCI_OP_READ_LOCAL_VERSION 0x1001
@@ -1463,6 +1476,11 @@
__le16 opcode;
} __packed;
+#define HCI_EV_HARDWARE_ERROR 0x10
+struct hci_ev_hardware_error {
+ __u8 code;
+} __packed;
+
#define HCI_EV_ROLE_CHANGE 0x12
struct hci_ev_role_change {
__u8 status;
@@ -1734,6 +1752,25 @@
__u8 clk_accurancy;
} __packed;
+/* Advertising report event types */
+#define LE_ADV_IND 0x00
+#define LE_ADV_DIRECT_IND 0x01
+#define LE_ADV_SCAN_IND 0x02
+#define LE_ADV_NONCONN_IND 0x03
+#define LE_ADV_SCAN_RSP 0x04
+
+#define ADDR_LE_DEV_PUBLIC 0x00
+#define ADDR_LE_DEV_RANDOM 0x01
+
+#define HCI_EV_LE_ADVERTISING_REPORT 0x02
+struct hci_ev_le_advertising_info {
+ __u8 evt_type;
+ __u8 bdaddr_type;
+ bdaddr_t bdaddr;
+ __u8 length;
+ __u8 data[0];
+} __packed;
+
#define HCI_EV_LE_CONN_UPDATE_COMPLETE 0x03
struct hci_ev_le_conn_update_complete {
__u8 status;
@@ -1759,23 +1796,14 @@
__le16 timeout;
} __packed;
-/* Advertising report event types */
-#define LE_ADV_IND 0x00
-#define LE_ADV_DIRECT_IND 0x01
-#define LE_ADV_SCAN_IND 0x02
-#define LE_ADV_NONCONN_IND 0x03
-#define LE_ADV_SCAN_RSP 0x04
-
-#define ADDR_LE_DEV_PUBLIC 0x00
-#define ADDR_LE_DEV_RANDOM 0x01
-
-#define HCI_EV_LE_ADVERTISING_REPORT 0x02
-struct hci_ev_le_advertising_info {
+#define HCI_EV_LE_DIRECT_ADV_REPORT 0x0B
+struct hci_ev_le_direct_adv_info {
__u8 evt_type;
__u8 bdaddr_type;
bdaddr_t bdaddr;
- __u8 length;
- __u8 data[0];
+ __u8 direct_addr_type;
+ bdaddr_t direct_addr;
+ __s8 rssi;
} __packed;
/* Internal events generated by Bluetooth stack */
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 37ff1ae..3c78270 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -75,6 +75,10 @@
u32 last_adv_flags;
u8 last_adv_data[HCI_MAX_AD_LENGTH];
u8 last_adv_data_len;
+ bool report_invalid_rssi;
+ s8 rssi;
+ u16 uuid_count;
+ u8 (*uuids)[16];
};
struct hci_conn_hash {
@@ -108,6 +112,7 @@
struct smp_ltk {
struct list_head list;
+ struct rcu_head rcu;
bdaddr_t bdaddr;
u8 bdaddr_type;
u8 authenticated;
@@ -120,6 +125,7 @@
struct smp_irk {
struct list_head list;
+ struct rcu_head rcu;
bdaddr_t rpa;
bdaddr_t bdaddr;
u8 addr_type;
@@ -128,6 +134,7 @@
struct link_key {
struct list_head list;
+ struct rcu_head rcu;
bdaddr_t bdaddr;
u8 type;
u8 val[HCI_LINK_KEY_SIZE];
@@ -137,10 +144,11 @@
struct oob_data {
struct list_head list;
bdaddr_t bdaddr;
+ u8 bdaddr_type;
u8 hash192[16];
- u8 randomizer192[16];
+ u8 rand192[16];
u8 hash256[16];
- u8 randomizer256[16];
+ u8 rand256[16];
};
#define HCI_MAX_SHORT_NAME_LENGTH 10
@@ -303,6 +311,7 @@
__u32 req_result;
void *smp_data;
+ void *smp_bredr_data;
struct discovery_state discovery;
struct hci_conn_hash conn_hash;
@@ -398,6 +407,8 @@
__u16 le_conn_interval;
__u16 le_conn_latency;
__u16 le_supv_timeout;
+ __u8 le_adv_data[HCI_MAX_AD_LENGTH];
+ __u8 le_adv_data_len;
__s8 rssi;
__s8 tx_power;
__s8 max_tx_power;
@@ -496,6 +507,17 @@
INIT_LIST_HEAD(&hdev->discovery.all);
INIT_LIST_HEAD(&hdev->discovery.unknown);
INIT_LIST_HEAD(&hdev->discovery.resolve);
+ hdev->discovery.report_invalid_rssi = true;
+ hdev->discovery.rssi = HCI_RSSI_INVALID;
+}
+
+static inline void hci_discovery_filter_clear(struct hci_dev *hdev)
+{
+ hdev->discovery.report_invalid_rssi = true;
+ hdev->discovery.rssi = HCI_RSSI_INVALID;
+ hdev->discovery.uuid_count = 0;
+ kfree(hdev->discovery.uuids);
+ hdev->discovery.uuids = NULL;
}
bool hci_discovery_active(struct hci_dev *hdev);
@@ -553,6 +575,8 @@
HCI_CONN_STK_ENCRYPT,
HCI_CONN_AUTH_INITIATOR,
HCI_CONN_DROP,
+ HCI_CONN_PARAM_REMOVAL_PEND,
+ HCI_CONN_NEW_LINK_KEY,
};
static inline bool hci_conn_ssp_enabled(struct hci_conn *conn)
@@ -643,6 +667,26 @@
return c->acl_num + c->amp_num + c->sco_num + c->le_num;
}
+static inline __u8 hci_conn_lookup_type(struct hci_dev *hdev, __u16 handle)
+{
+ struct hci_conn_hash *h = &hdev->conn_hash;
+ struct hci_conn *c;
+ __u8 type = INVALID_LINK;
+
+ rcu_read_lock();
+
+ list_for_each_entry_rcu(c, &h->list, list) {
+ if (c->handle == handle) {
+ type = c->type;
+ break;
+ }
+ }
+
+ rcu_read_unlock();
+
+ return type;
+}
+
static inline struct hci_conn *hci_conn_hash_lookup_handle(struct hci_dev *hdev,
__u16 handle)
{
@@ -853,6 +897,7 @@
void hci_unregister_dev(struct hci_dev *hdev);
int hci_suspend_dev(struct hci_dev *hdev);
int hci_resume_dev(struct hci_dev *hdev);
+int hci_reset_dev(struct hci_dev *hdev);
int hci_dev_open(__u16 dev);
int hci_dev_close(__u16 dev);
int hci_dev_reset(__u16 dev);
@@ -894,13 +939,11 @@
struct link_key *hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn,
bdaddr_t *bdaddr, u8 *val, u8 type,
u8 pin_len, bool *persistent);
-struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand,
- u8 role);
struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 addr_type, u8 type, u8 authenticated,
u8 tk[16], u8 enc_size, __le16 ediv, __le64 rand);
-struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
- u8 addr_type, u8 role);
+struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 addr_type, u8 role);
int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type);
void hci_smp_ltks_clear(struct hci_dev *hdev);
int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr);
@@ -915,13 +958,12 @@
void hci_remote_oob_data_clear(struct hci_dev *hdev);
struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev,
- bdaddr_t *bdaddr);
+ bdaddr_t *bdaddr, u8 bdaddr_type);
int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
- u8 *hash, u8 *randomizer);
-int hci_add_remote_oob_ext_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
- u8 *hash192, u8 *randomizer192,
- u8 *hash256, u8 *randomizer256);
-int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr);
+ u8 bdaddr_type, u8 *hash192, u8 *rand192,
+ u8 *hash256, u8 *rand256);
+int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 bdaddr_type);
void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);
@@ -972,6 +1014,9 @@
#define hdev_is_powered(hdev) (test_bit(HCI_UP, &hdev->flags) && \
!test_bit(HCI_AUTO_OFF, &hdev->dev_flags))
+#define bredr_sc_enabled(dev) ((lmp_sc_capable(dev) || \
+ test_bit(HCI_FORCE_SC, &(dev)->dbg_flags)) && \
+ test_bit(HCI_SC_ENABLED, &(dev)->dev_flags))
/* ----- HCI protocols ----- */
#define HCI_PROTO_DEFER 0x01
@@ -1310,9 +1355,8 @@
void mgmt_discoverable_timeout(struct hci_dev *hdev);
void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
bool persistent);
-void mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
- u8 addr_type, u32 flags, u8 *name, u8 name_len,
- u8 *dev_class);
+void mgmt_device_connected(struct hci_dev *hdev, struct hci_conn *conn,
+ u32 flags, u8 *name, u8 name_len);
void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 link_type, u8 addr_type, u8 reason,
bool mgmt_connected);
@@ -1349,8 +1393,8 @@
u8 status);
void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
- u8 *randomizer192, u8 *hash256,
- u8 *randomizer256, u8 status);
+ u8 *rand192, u8 *hash256, u8 *rand256,
+ u8 status);
void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 addr_type, u8 *dev_class, s8 rssi, u32 flags,
u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len);
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index ead99f0..eee3ef5 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -28,6 +28,7 @@
#define __L2CAP_H
#include <asm/unaligned.h>
+#include <linux/atomic.h>
/* L2CAP defaults */
#define L2CAP_DEFAULT_MTU 672
@@ -140,6 +141,7 @@
#define L2CAP_FC_ATT 0x10
#define L2CAP_FC_SIG_LE 0x20
#define L2CAP_FC_SMP_LE 0x40
+#define L2CAP_FC_SMP_BREDR 0x80
/* L2CAP Control Field bit masks */
#define L2CAP_CTRL_SAR 0xC000
@@ -254,6 +256,7 @@
#define L2CAP_CID_ATT 0x0004
#define L2CAP_CID_LE_SIGNALING 0x0005
#define L2CAP_CID_SMP 0x0006
+#define L2CAP_CID_SMP_BREDR 0x0007
#define L2CAP_CID_DYN_START 0x0040
#define L2CAP_CID_DYN_END 0xffff
#define L2CAP_CID_LE_DYN_END 0x007f
@@ -481,6 +484,7 @@
struct hci_conn *hs_hcon;
struct hci_chan *hs_hchan;
struct kref kref;
+ atomic_t nesting;
__u8 state;
@@ -617,8 +621,8 @@
unsigned int mtu;
__u32 feat_mask;
- __u8 fixed_chan_mask;
- bool hs_enabled;
+ __u8 remote_fixed_chan;
+ __u8 local_fixed_chan;
__u8 info_state;
__u8 info_ident;
@@ -713,6 +717,17 @@
FLAG_HOLD_HCI_CONN,
};
+/* Lock nesting levels for L2CAP channels. We need these because lockdep
+ * otherwise considers all channels equal and will e.g. complain about a
+ * connection oriented channel triggering SMP procedures or a listening
+ * channel creating and locking a child channel.
+ */
+enum {
+ L2CAP_NESTING_SMP,
+ L2CAP_NESTING_NORMAL,
+ L2CAP_NESTING_PARENT,
+};
+
enum {
L2CAP_TX_STATE_XMIT,
L2CAP_TX_STATE_WAIT_F,
@@ -778,7 +793,7 @@
static inline void l2cap_chan_lock(struct l2cap_chan *chan)
{
- mutex_lock(&chan->lock);
+ mutex_lock_nested(&chan->lock, atomic_read(&chan->nesting));
}
static inline void l2cap_chan_unlock(struct l2cap_chan *chan)
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index 414cd2f..95c34d5 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -184,6 +184,9 @@
#define MGMT_LTK_UNAUTHENTICATED 0x00
#define MGMT_LTK_AUTHENTICATED 0x01
+#define MGMT_LTK_P256_UNAUTH 0x02
+#define MGMT_LTK_P256_AUTH 0x03
+#define MGMT_LTK_P256_DEBUG 0x04
struct mgmt_ltk_info {
struct mgmt_addr_info addr;
@@ -299,28 +302,28 @@
#define MGMT_READ_LOCAL_OOB_DATA_SIZE 0
struct mgmt_rp_read_local_oob_data {
__u8 hash[16];
- __u8 randomizer[16];
+ __u8 rand[16];
} __packed;
struct mgmt_rp_read_local_oob_ext_data {
__u8 hash192[16];
- __u8 randomizer192[16];
+ __u8 rand192[16];
__u8 hash256[16];
- __u8 randomizer256[16];
+ __u8 rand256[16];
} __packed;
#define MGMT_OP_ADD_REMOTE_OOB_DATA 0x0021
struct mgmt_cp_add_remote_oob_data {
struct mgmt_addr_info addr;
__u8 hash[16];
- __u8 randomizer[16];
+ __u8 rand[16];
} __packed;
#define MGMT_ADD_REMOTE_OOB_DATA_SIZE (MGMT_ADDR_INFO_SIZE + 32)
struct mgmt_cp_add_remote_oob_ext_data {
struct mgmt_addr_info addr;
__u8 hash192[16];
- __u8 randomizer192[16];
+ __u8 rand192[16];
__u8 hash256[16];
- __u8 randomizer256[16];
+ __u8 rand256[16];
} __packed;
#define MGMT_ADD_REMOTE_OOB_EXT_DATA_SIZE (MGMT_ADDR_INFO_SIZE + 64)
@@ -495,6 +498,15 @@
} __packed;
#define MGMT_SET_PUBLIC_ADDRESS_SIZE 6
+#define MGMT_OP_START_SERVICE_DISCOVERY 0x003A
+struct mgmt_cp_start_service_discovery {
+ __u8 type;
+ __s8 rssi;
+ __le16 uuid_count;
+ __u8 uuids[0][16];
+} __packed;
+#define MGMT_START_SERVICE_DISCOVERY_SIZE 4
+
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
__le16 opcode;
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 87362c1..66fc97a 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -319,9 +319,12 @@
/**
* struct vif_params - describes virtual interface parameters
* @use_4addr: use 4-address frames
- * @macaddr: address to use for this virtual interface. This will only
- * be used for non-netdevice interfaces. If this parameter is set
- * to zero address the driver may determine the address as needed.
+ * @macaddr: address to use for this virtual interface.
+ * If this parameter is set to zero address the driver may
+ * determine the address as needed.
+ * This feature is only fully supported by drivers that enable the
+ * %NL80211_FEATURE_MAC_ON_CREATE flag. Others may support creating
+ ** only p2p devices with specified MAC.
*/
struct vif_params {
int use_4addr;
@@ -799,6 +802,22 @@
};
/**
+ * struct station_del_parameters - station deletion parameters
+ *
+ * Used to delete a station entry (or all stations).
+ *
+ * @mac: MAC address of the station to remove or NULL to remove all stations
+ * @subtype: Management frame subtype to use for indicating removal
+ * (10 = Disassociation, 12 = Deauthentication)
+ * @reason_code: Reason code for the Disassociation/Deauthentication frame
+ */
+struct station_del_parameters {
+ const u8 *mac;
+ u8 subtype;
+ u16 reason_code;
+};
+
+/**
* enum cfg80211_station_type - the type of station being modified
* @CFG80211_STA_AP_CLIENT: client of an AP interface
* @CFG80211_STA_AP_MLME_CLIENT: client of an AP interface that has
@@ -1340,6 +1359,16 @@
};
/**
+ * struct ocb_setup - 802.11p OCB mode setup configuration
+ * @chandef: defines the channel to use
+ *
+ * These parameters are fixed when connecting to the network
+ */
+struct ocb_setup {
+ struct cfg80211_chan_def chandef;
+};
+
+/**
* struct ieee80211_txq_params - TX queue parameters
* @ac: AC identifier
* @txop: Maximum burst time in units of 32 usecs, 0 meaning disabled
@@ -1408,6 +1437,10 @@
* @aborted: (internal) scan request was notified as aborted
* @notified: (internal) scan request was notified as done or aborted
* @no_cck: used to send probe requests at non CCK rate in 2GHz band
+ * @mac_addr: MAC address used with randomisation
+ * @mac_addr_mask: MAC address mask used with randomisation, bits that
+ * are 0 in the mask should be randomised, bits that are 1 should
+ * be taken from the @mac_addr
*/
struct cfg80211_scan_request {
struct cfg80211_ssid *ssids;
@@ -1422,6 +1455,9 @@
struct wireless_dev *wdev;
+ u8 mac_addr[ETH_ALEN] __aligned(2);
+ u8 mac_addr_mask[ETH_ALEN] __aligned(2);
+
/* internal */
struct wiphy *wiphy;
unsigned long scan_start;
@@ -1432,6 +1468,17 @@
struct ieee80211_channel *channels[0];
};
+static inline void get_random_mask_addr(u8 *buf, const u8 *addr, const u8 *mask)
+{
+ int i;
+
+ get_random_bytes(buf, ETH_ALEN);
+ for (i = 0; i < ETH_ALEN; i++) {
+ buf[i] &= ~mask[i];
+ buf[i] |= addr[i] & mask[i];
+ }
+}
+
/**
* struct cfg80211_match_set - sets of attributes to match
*
@@ -1465,6 +1512,10 @@
* @channels: channels to scan
* @min_rssi_thold: for drivers only supporting a single threshold, this
* contains the minimum over all matchsets
+ * @mac_addr: MAC address used with randomisation
+ * @mac_addr_mask: MAC address mask used with randomisation, bits that
+ * are 0 in the mask should be randomised, bits that are 1 should
+ * be taken from the @mac_addr
*/
struct cfg80211_sched_scan_request {
struct cfg80211_ssid *ssids;
@@ -1479,6 +1530,9 @@
int n_match_sets;
s32 min_rssi_thold;
+ u8 mac_addr[ETH_ALEN] __aligned(2);
+ u8 mac_addr_mask[ETH_ALEN] __aligned(2);
+
/* internal */
struct wiphy *wiphy;
struct net_device *dev;
@@ -1911,6 +1965,7 @@
* @rfkill_release: wake up when rfkill is released
* @tcp: TCP connection establishment/wakeup parameters, see nl80211.h.
* NULL if not configured.
+ * @nd_config: configuration for the scan to be used for net detect wake.
*/
struct cfg80211_wowlan {
bool any, disconnect, magic_pkt, gtk_rekey_failure,
@@ -1919,6 +1974,7 @@
struct cfg80211_pkt_pattern *patterns;
struct cfg80211_wowlan_tcp *tcp;
int n_patterns;
+ struct cfg80211_sched_scan_request *nd_config;
};
/**
@@ -1951,6 +2007,35 @@
};
/**
+ * struct cfg80211_wowlan_nd_match - information about the match
+ *
+ * @ssid: SSID of the match that triggered the wake up
+ * @n_channels: Number of channels where the match occurred. This
+ * value may be zero if the driver can't report the channels.
+ * @channels: center frequencies of the channels where a match
+ * occurred (in MHz)
+ */
+struct cfg80211_wowlan_nd_match {
+ struct cfg80211_ssid ssid;
+ int n_channels;
+ u32 channels[];
+};
+
+/**
+ * struct cfg80211_wowlan_nd_info - net detect wake up information
+ *
+ * @n_matches: Number of match information instances provided in
+ * @matches. This value may be zero if the driver can't provide
+ * match information.
+ * @matches: Array of pointers to matches containing information about
+ * the matches that triggered the wake up.
+ */
+struct cfg80211_wowlan_nd_info {
+ int n_matches;
+ struct cfg80211_wowlan_nd_match *matches[];
+};
+
+/**
* struct cfg80211_wowlan_wakeup - wakeup report
* @disconnect: woke up by getting disconnected
* @magic_pkt: woke up by receiving magic packet
@@ -1969,6 +2054,7 @@
* @tcp_match: TCP wakeup packet received
* @tcp_connlost: TCP connection lost or failed to establish
* @tcp_nomoretokens: TCP data ran out of tokens
+ * @net_detect: if not %NULL, woke up because of net detect
*/
struct cfg80211_wowlan_wakeup {
bool disconnect, magic_pkt, gtk_rekey_failure,
@@ -1978,6 +2064,7 @@
s32 pattern_idx;
u32 packet_present_len, packet_len;
const void *packet;
+ struct cfg80211_wowlan_nd_info *net_detect;
};
/**
@@ -2132,7 +2219,7 @@
* @stop_ap: Stop being an AP, including stopping beaconing.
*
* @add_station: Add a new station.
- * @del_station: Remove a station; @mac may be NULL to remove all stations.
+ * @del_station: Remove a station
* @change_station: Modify a given station. Note that flags changes are not much
* validated in cfg80211, in particular the auth/assoc/authorized flags
* might come to the driver in invalid combinations -- make sure to check
@@ -2146,6 +2233,8 @@
* @change_mpath: change a given mesh path
* @get_mpath: get a mesh path for the given parameters
* @dump_mpath: dump mesh path callback -- resume dump at index @idx
+ * @get_mpp: get a mesh proxy path for the given parameters
+ * @dump_mpp: dump mesh proxy path callback -- resume dump at index @idx
* @join_mesh: join the mesh network with the specified parameters
* (invoked with the wireless_dev mutex held)
* @leave_mesh: leave the current mesh network
@@ -2331,6 +2420,17 @@
* with the peer followed by immediate teardown when the addition is later
* rejected)
* @del_tx_ts: remove an existing TX TS
+ *
+ * @join_ocb: join the OCB network with the specified parameters
+ * (invoked with the wireless_dev mutex held)
+ * @leave_ocb: leave the current OCB network
+ * (invoked with the wireless_dev mutex held)
+ *
+ * @tdls_channel_switch: Start channel-switching with a TDLS peer. The driver
+ * is responsible for continually initiating channel-switching operations
+ * and returning to the base channel for communication with the AP.
+ * @tdls_cancel_channel_switch: Stop channel-switching with a TDLS peer. Both
+ * peers must be on the base channel when the call completes.
*/
struct cfg80211_ops {
int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@@ -2376,7 +2476,7 @@
const u8 *mac,
struct station_parameters *params);
int (*del_station)(struct wiphy *wiphy, struct net_device *dev,
- const u8 *mac);
+ struct station_del_parameters *params);
int (*change_station)(struct wiphy *wiphy, struct net_device *dev,
const u8 *mac,
struct station_parameters *params);
@@ -2396,6 +2496,11 @@
int (*dump_mpath)(struct wiphy *wiphy, struct net_device *dev,
int idx, u8 *dst, u8 *next_hop,
struct mpath_info *pinfo);
+ int (*get_mpp)(struct wiphy *wiphy, struct net_device *dev,
+ u8 *dst, u8 *mpp, struct mpath_info *pinfo);
+ int (*dump_mpp)(struct wiphy *wiphy, struct net_device *dev,
+ int idx, u8 *dst, u8 *mpp,
+ struct mpath_info *pinfo);
int (*get_mesh_config)(struct wiphy *wiphy,
struct net_device *dev,
struct mesh_config *conf);
@@ -2407,6 +2512,10 @@
const struct mesh_setup *setup);
int (*leave_mesh)(struct wiphy *wiphy, struct net_device *dev);
+ int (*join_ocb)(struct wiphy *wiphy, struct net_device *dev,
+ struct ocb_setup *setup);
+ int (*leave_ocb)(struct wiphy *wiphy, struct net_device *dev);
+
int (*change_bss)(struct wiphy *wiphy, struct net_device *dev,
struct bss_parameters *params);
@@ -2577,6 +2686,14 @@
u16 admitted_time);
int (*del_tx_ts)(struct wiphy *wiphy, struct net_device *dev,
u8 tsid, const u8 *peer);
+
+ int (*tdls_channel_switch)(struct wiphy *wiphy,
+ struct net_device *dev,
+ const u8 *addr, u8 oper_class,
+ struct cfg80211_chan_def *chandef);
+ void (*tdls_cancel_channel_switch)(struct wiphy *wiphy,
+ struct net_device *dev,
+ const u8 *addr);
};
/*
@@ -2623,13 +2740,9 @@
* @WIPHY_FLAG_SUPPORTS_5_10_MHZ: Device supports 5 MHz and 10 MHz channels.
* @WIPHY_FLAG_HAS_CHANNEL_SWITCH: Device supports channel switch in
* beaconing mode (AP, IBSS, Mesh, ...).
- * @WIPHY_FLAG_SUPPORTS_WMM_ADMISSION: the device supports setting up WMM
- * TSPEC sessions (TID aka TSID 0-7) with the NL80211_CMD_ADD_TX_TS
- * command. Standard IEEE 802.11 TSPEC setup is not yet supported, it
- * needs to be able to handle Block-Ack agreements and other things.
*/
enum wiphy_flags {
- WIPHY_FLAG_SUPPORTS_WMM_ADMISSION = BIT(0),
+ /* use hole at 0 */
/* use hole at 1 */
/* use hole at 2 */
WIPHY_FLAG_NETNS_OK = BIT(3),
@@ -2755,6 +2868,7 @@
* @WIPHY_WOWLAN_EAP_IDENTITY_REQ: supports wakeup on EAP identity request
* @WIPHY_WOWLAN_4WAY_HANDSHAKE: supports wakeup on 4-way handshake failure
* @WIPHY_WOWLAN_RFKILL_RELEASE: supports wakeup on RF-kill release
+ * @WIPHY_WOWLAN_NET_DETECT: supports wakeup on network detection
*/
enum wiphy_wowlan_support_flags {
WIPHY_WOWLAN_ANY = BIT(0),
@@ -2765,6 +2879,7 @@
WIPHY_WOWLAN_EAP_IDENTITY_REQ = BIT(5),
WIPHY_WOWLAN_4WAY_HANDSHAKE = BIT(6),
WIPHY_WOWLAN_RFKILL_RELEASE = BIT(7),
+ WIPHY_WOWLAN_NET_DETECT = BIT(8),
};
struct wiphy_wowlan_tcp_support {
@@ -2783,6 +2898,11 @@
* @pattern_max_len: maximum length of each pattern
* @pattern_min_len: minimum length of each pattern
* @max_pkt_offset: maximum Rx packet offset
+ * @max_nd_match_sets: maximum number of matchsets for net-detect,
+ * similar, but not necessarily identical, to max_match_sets for
+ * scheduled scans.
+ * See &struct cfg80211_sched_scan_request.@match_sets for more
+ * details.
* @tcp: TCP wakeup support information
*/
struct wiphy_wowlan_support {
@@ -2791,6 +2911,7 @@
int pattern_max_len;
int pattern_min_len;
int max_pkt_offset;
+ int max_nd_match_sets;
const struct wiphy_wowlan_tcp_support *tcp;
};
@@ -3169,6 +3290,23 @@
}
/**
+ * wiphy_new_nm - create a new wiphy for use with cfg80211
+ *
+ * @ops: The configuration operations for this device
+ * @sizeof_priv: The size of the private area to allocate
+ * @requested_name: Request a particular name.
+ * NULL is valid value, and means use the default phy%d naming.
+ *
+ * Create a new wiphy and associate the given operations with it.
+ * @sizeof_priv bytes are allocated for private use.
+ *
+ * Return: A pointer to the new wiphy. This pointer must be
+ * assigned to each netdev's ieee80211_ptr for proper operation.
+ */
+struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
+ const char *requested_name);
+
+/**
* wiphy_new - create a new wiphy for use with cfg80211
*
* @ops: The configuration operations for this device
@@ -3180,7 +3318,11 @@
* Return: A pointer to the new wiphy. This pointer must be
* assigned to each netdev's ieee80211_ptr for proper operation.
*/
-struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv);
+static inline struct wiphy *wiphy_new(const struct cfg80211_ops *ops,
+ int sizeof_priv)
+{
+ return wiphy_new_nm(ops, sizeof_priv, NULL);
+}
/**
* wiphy_register - register a wiphy with cfg80211
@@ -4504,33 +4646,6 @@
gfp_t gfp);
/**
- * cfg80211_radar_event - radar detection event
- * @wiphy: the wiphy
- * @chandef: chandef for the current channel
- * @gfp: context flags
- *
- * This function is called when a radar is detected on the current chanenl.
- */
-void cfg80211_radar_event(struct wiphy *wiphy,
- struct cfg80211_chan_def *chandef, gfp_t gfp);
-
-/**
- * cfg80211_cac_event - Channel availability check (CAC) event
- * @netdev: network device
- * @chandef: chandef for the current channel
- * @event: type of event
- * @gfp: context flags
- *
- * This function is called when a Channel availability check (CAC) is finished
- * or aborted. This must be called to notify the completion of a CAC process,
- * also by full-MAC drivers.
- */
-void cfg80211_cac_event(struct net_device *netdev,
- const struct cfg80211_chan_def *chandef,
- enum nl80211_radar_event event, gfp_t gfp);
-
-
-/**
* cfg80211_cqm_pktloss_notify - notify userspace about packetloss to peer
* @dev: network device
* @peer: peer's MAC address
@@ -4558,6 +4673,42 @@
u32 num_packets, u32 rate, u32 intvl, gfp_t gfp);
/**
+ * cfg80211_cqm_beacon_loss_notify - beacon loss event
+ * @dev: network device
+ * @gfp: context flags
+ *
+ * Notify userspace about beacon loss from the connected AP.
+ */
+void cfg80211_cqm_beacon_loss_notify(struct net_device *dev, gfp_t gfp);
+
+/**
+ * cfg80211_radar_event - radar detection event
+ * @wiphy: the wiphy
+ * @chandef: chandef for the current channel
+ * @gfp: context flags
+ *
+ * This function is called when a radar is detected on the current chanenl.
+ */
+void cfg80211_radar_event(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef, gfp_t gfp);
+
+/**
+ * cfg80211_cac_event - Channel availability check (CAC) event
+ * @netdev: network device
+ * @chandef: chandef for the current channel
+ * @event: type of event
+ * @gfp: context flags
+ *
+ * This function is called when a Channel availability check (CAC) is finished
+ * or aborted. This must be called to notify the completion of a CAC process,
+ * also by full-MAC drivers.
+ */
+void cfg80211_cac_event(struct net_device *netdev,
+ const struct cfg80211_chan_def *chandef,
+ enum nl80211_radar_event event, gfp_t gfp);
+
+
+/**
* cfg80211_gtk_rekey_notify - notify userspace about driver rekeying
* @dev: network device
* @bssid: BSSID of AP (to avoid races)
@@ -4660,6 +4811,20 @@
void cfg80211_ch_switch_notify(struct net_device *dev,
struct cfg80211_chan_def *chandef);
+/*
+ * cfg80211_ch_switch_started_notify - notify channel switch start
+ * @dev: the device on which the channel switch started
+ * @chandef: the future channel definition
+ * @count: the number of TBTTs until the channel switch happens
+ *
+ * Inform the userspace about the channel switch that has just
+ * started, so that it can take appropriate actions (eg. starting
+ * channel switch on other vifs), if necessary.
+ */
+void cfg80211_ch_switch_started_notify(struct net_device *dev,
+ struct cfg80211_chan_def *chandef,
+ u8 count);
+
/**
* ieee80211_operating_class_to_band - convert operating class to band
*
diff --git a/include/net/cfg802154.h b/include/net/cfg802154.h
new file mode 100644
index 0000000..7f713ac
--- /dev/null
+++ b/include/net/cfg802154.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2007, 2008, 2009 Siemens AG
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * Written by:
+ * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+ */
+
+#ifndef __NET_CFG802154_H
+#define __NET_CFG802154_H
+
+#include <linux/ieee802154.h>
+#include <linux/netdevice.h>
+#include <linux/mutex.h>
+#include <linux/bug.h>
+
+#include <net/nl802154.h>
+
+struct wpan_phy;
+
+struct cfg802154_ops {
+ struct net_device * (*add_virtual_intf_deprecated)(struct wpan_phy *wpan_phy,
+ const char *name,
+ int type);
+ void (*del_virtual_intf_deprecated)(struct wpan_phy *wpan_phy,
+ struct net_device *dev);
+ int (*add_virtual_intf)(struct wpan_phy *wpan_phy,
+ const char *name,
+ enum nl802154_iftype type,
+ __le64 extended_addr);
+ int (*del_virtual_intf)(struct wpan_phy *wpan_phy,
+ struct wpan_dev *wpan_dev);
+ int (*set_channel)(struct wpan_phy *wpan_phy, u8 page, u8 channel);
+ int (*set_pan_id)(struct wpan_phy *wpan_phy,
+ struct wpan_dev *wpan_dev, __le16 pan_id);
+ int (*set_short_addr)(struct wpan_phy *wpan_phy,
+ struct wpan_dev *wpan_dev, __le16 short_addr);
+ int (*set_backoff_exponent)(struct wpan_phy *wpan_phy,
+ struct wpan_dev *wpan_dev, u8 min_be,
+ u8 max_be);
+ int (*set_max_csma_backoffs)(struct wpan_phy *wpan_phy,
+ struct wpan_dev *wpan_dev,
+ u8 max_csma_backoffs);
+ int (*set_max_frame_retries)(struct wpan_phy *wpan_phy,
+ struct wpan_dev *wpan_dev,
+ s8 max_frame_retries);
+ int (*set_lbt_mode)(struct wpan_phy *wpan_phy,
+ struct wpan_dev *wpan_dev, bool mode);
+};
+
+struct wpan_phy {
+ struct mutex pib_lock;
+
+ /* If multiple wpan_phys are registered and you're handed e.g.
+ * a regular netdev with assigned ieee802154_ptr, you won't
+ * know whether it points to a wpan_phy your driver has registered
+ * or not. Assign this to something global to your driver to
+ * help determine whether you own this wpan_phy or not.
+ */
+ const void *privid;
+
+ /*
+ * This is a PIB according to 802.15.4-2011.
+ * We do not provide timing-related variables, as they
+ * aren't used outside of driver
+ */
+ u8 current_channel;
+ u8 current_page;
+ u32 channels_supported[IEEE802154_MAX_PAGE + 1];
+ s8 transmit_power;
+ u8 cca_mode;
+
+ __le64 perm_extended_addr;
+
+ s32 cca_ed_level;
+
+ /* PHY depended MAC PIB values */
+
+ /* 802.15.4 acronym: Tdsym in usec */
+ u8 symbol_duration;
+ /* lifs and sifs periods timing */
+ u16 lifs_period;
+ u16 sifs_period;
+
+ struct device dev;
+
+ char priv[0] __aligned(NETDEV_ALIGN);
+};
+
+struct wpan_dev {
+ struct wpan_phy *wpan_phy;
+ int iftype;
+
+ /* the remainder of this struct should be private to cfg802154 */
+ struct list_head list;
+ struct net_device *netdev;
+
+ u32 identifier;
+
+ /* MAC PIB */
+ __le16 pan_id;
+ __le16 short_addr;
+ __le64 extended_addr;
+
+ /* MAC BSN field */
+ u8 bsn;
+ /* MAC DSN field */
+ u8 dsn;
+
+ u8 min_be;
+ u8 max_be;
+ u8 csma_retries;
+ s8 frame_retries;
+
+ bool lbt;
+
+ bool promiscuous_mode;
+};
+
+#define to_phy(_dev) container_of(_dev, struct wpan_phy, dev)
+
+struct wpan_phy *
+wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size);
+static inline void wpan_phy_set_dev(struct wpan_phy *phy, struct device *dev)
+{
+ phy->dev.parent = dev;
+}
+
+int wpan_phy_register(struct wpan_phy *phy);
+void wpan_phy_unregister(struct wpan_phy *phy);
+void wpan_phy_free(struct wpan_phy *phy);
+/* Same semantics as for class_for_each_device */
+int wpan_phy_for_each(int (*fn)(struct wpan_phy *phy, void *data), void *data);
+
+static inline void *wpan_phy_priv(struct wpan_phy *phy)
+{
+ BUG_ON(!phy);
+ return &phy->priv;
+}
+
+struct wpan_phy *wpan_phy_find(const char *str);
+
+static inline void wpan_phy_put(struct wpan_phy *phy)
+{
+ put_device(&phy->dev);
+}
+
+static inline const char *wpan_phy_name(struct wpan_phy *phy)
+{
+ return dev_name(&phy->dev);
+}
+
+#endif /* __NET_CFG802154_H */
diff --git a/include/net/ieee802154_netdev.h b/include/net/ieee802154_netdev.h
index 3b53c8e..83bb8a7 100644
--- a/include/net/ieee802154_netdev.h
+++ b/include/net/ieee802154_netdev.h
@@ -12,10 +12,6 @@
* 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.
- *
* Written by:
* Pavel Smolenskiy <pavel.smolenskiy@gmail.com>
* Maxim Gorbachyov <maxim.gorbachev@siemens.com>
@@ -27,10 +23,10 @@
#ifndef IEEE802154_NETDEVICE_H
#define IEEE802154_NETDEVICE_H
-#include <net/ieee802154.h>
#include <net/af_ieee802154.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
+#include <linux/ieee802154.h>
struct ieee802154_sechdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
@@ -427,8 +423,6 @@
/* The fields below are required. */
- struct wpan_phy *(*get_phy)(const struct net_device *dev);
-
/*
* FIXME: these should become the part of PIB/MIB interface.
* However we still don't have IB interface of any kind
@@ -438,16 +432,6 @@
u8 (*get_dsn)(const struct net_device *dev);
};
-/* The IEEE 802.15.4 standard defines 2 type of the devices:
- * - FFD - full functionality device
- * - RFD - reduce functionality device
- *
- * So 2 sets of mlme operations are needed
- */
-struct ieee802154_reduced_mlme_ops {
- struct wpan_phy *(*get_phy)(const struct net_device *dev);
-};
-
static inline struct ieee802154_mlme_ops *
ieee802154_mlme_ops(const struct net_device *dev)
{
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index c5ba507..96323df 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -263,6 +263,7 @@
* @BSS_CHANGED_BANDWIDTH: The bandwidth used by this interface changed,
* note that this is only called when it changes after the channel
* context had been assigned.
+ * @BSS_CHANGED_OCB: OCB join status changed
*/
enum ieee80211_bss_change {
BSS_CHANGED_ASSOC = 1<<0,
@@ -287,6 +288,7 @@
BSS_CHANGED_P2P_PS = 1<<19,
BSS_CHANGED_BEACON_INFO = 1<<20,
BSS_CHANGED_BANDWIDTH = 1<<21,
+ BSS_CHANGED_OCB = 1<<22,
/* when adding here, make sure to change ieee80211_reconfig */
};
@@ -739,7 +741,8 @@
u8 ampdu_ack_len;
u8 ampdu_len;
u8 antenna;
- void *status_driver_data[21 / sizeof(void *)];
+ u16 tx_time;
+ void *status_driver_data[19 / sizeof(void *)];
} status;
struct {
struct ieee80211_tx_rate driver_rates[
@@ -879,6 +882,9 @@
* subframes share the same sequence number. Reported subframes can be
* either regular MSDU or singly A-MSDUs. Subframes must not be
* interleaved with other frames.
+ * @RX_FLAG_RADIOTAP_VENDOR_DATA: This frame contains vendor-specific
+ * radiotap data in the skb->data (before the frame) as described by
+ * the &struct ieee80211_vendor_radiotap.
*/
enum mac80211_rx_flags {
RX_FLAG_MMIC_ERROR = BIT(0),
@@ -908,6 +914,7 @@
RX_FLAG_10MHZ = BIT(28),
RX_FLAG_5MHZ = BIT(29),
RX_FLAG_AMSDU_MORE = BIT(30),
+ RX_FLAG_RADIOTAP_VENDOR_DATA = BIT(31),
};
#define RX_FLAG_STBC_SHIFT 26
@@ -979,6 +986,39 @@
};
/**
+ * struct ieee80211_vendor_radiotap - vendor radiotap data information
+ * @present: presence bitmap for this vendor namespace
+ * (this could be extended in the future if any vendor needs more
+ * bits, the radiotap spec does allow for that)
+ * @align: radiotap vendor namespace alignment. This defines the needed
+ * alignment for the @data field below, not for the vendor namespace
+ * description itself (which has a fixed 2-byte alignment)
+ * Must be a power of two, and be set to at least 1!
+ * @oui: radiotap vendor namespace OUI
+ * @subns: radiotap vendor sub namespace
+ * @len: radiotap vendor sub namespace skip length, if alignment is done
+ * then that's added to this, i.e. this is only the length of the
+ * @data field.
+ * @pad: number of bytes of padding after the @data, this exists so that
+ * the skb data alignment can be preserved even if the data has odd
+ * length
+ * @data: the actual vendor namespace data
+ *
+ * This struct, including the vendor data, goes into the skb->data before
+ * the 802.11 header. It's split up in mac80211 using the align/oui/subns
+ * data.
+ */
+struct ieee80211_vendor_radiotap {
+ u32 present;
+ u8 align;
+ u8 oui[3];
+ u8 subns;
+ u8 pad;
+ u16 len;
+ u8 data[];
+} __packed;
+
+/**
* enum ieee80211_conf_flags - configuration flags
*
* Flags to define PHY configuration options
@@ -1117,6 +1157,8 @@
* Function (TSF) timer when the frame containing the channel switch
* announcement was received. This is simply the rx.mactime parameter
* the driver passed into mac80211.
+ * @device_timestamp: arbitrary timestamp for the device, this is the
+ * rx.device_timestamp parameter the driver passed to mac80211.
* @block_tx: Indicates whether transmission must be blocked before the
* scheduled channel switch, as indicated by the AP.
* @chandef: the new channel to switch to
@@ -1124,6 +1166,7 @@
*/
struct ieee80211_channel_switch {
u64 timestamp;
+ u32 device_timestamp;
bool block_tx;
struct cfg80211_chan_def chandef;
u8 count;
@@ -1423,6 +1466,8 @@
* @smps_mode: current SMPS mode (off, static or dynamic)
* @rates: rate control selection table
* @tdls: indicates whether the STA is a TDLS peer
+ * @tdls_initiator: indicates the STA is an initiator of the TDLS link. Only
+ * valid if the STA is a TDLS peer in the first place.
*/
struct ieee80211_sta {
u32 supp_rates[IEEE80211_NUM_BANDS];
@@ -1438,6 +1483,7 @@
enum ieee80211_smps_mode smps_mode;
struct ieee80211_sta_rates __rcu *rates;
bool tdls;
+ bool tdls_initiator;
/* must be last */
u8 drv_priv[0] __aligned(sizeof(void *));
@@ -1576,6 +1622,10 @@
* a virtual monitor interface when monitor interfaces are the only
* active interfaces.
*
+ * @IEEE80211_HW_NO_AUTO_VIF: The driver would like for no wlanX to
+ * be created. It is expected user-space will create vifs as
+ * desired (and thus have them named as desired).
+ *
* @IEEE80211_HW_QUEUE_CONTROL: The driver wants to control per-interface
* queue mapping in order to use different queues (not just one per AC)
* for different virtual interfaces. See the doc section on HW queue
@@ -1622,7 +1672,8 @@
IEEE80211_HW_SUPPORTS_DYNAMIC_PS = 1<<12,
IEEE80211_HW_MFP_CAPABLE = 1<<13,
IEEE80211_HW_WANT_MONITOR_VIF = 1<<14,
- /* free slots */
+ IEEE80211_HW_NO_AUTO_VIF = 1<<15,
+ /* free slot */
IEEE80211_HW_SUPPORTS_UAPSD = 1<<17,
IEEE80211_HW_REPORTS_TX_ACK_STATUS = 1<<18,
IEEE80211_HW_CONNECTION_MONITOR = 1<<19,
@@ -1776,6 +1827,31 @@
};
/**
+ * struct ieee80211_tdls_ch_sw_params - TDLS channel switch parameters
+ *
+ * @sta: peer this TDLS channel-switch request/response came from
+ * @chandef: channel referenced in a TDLS channel-switch request
+ * @action_code: see &enum ieee80211_tdls_actioncode
+ * @status: channel-switch response status
+ * @timestamp: time at which the frame was received
+ * @switch_time: switch-timing parameter received in the frame
+ * @switch_timeout: switch-timing parameter received in the frame
+ * @tmpl_skb: TDLS switch-channel response template
+ * @ch_sw_tm_ie: offset of the channel-switch timing IE inside @tmpl_skb
+ */
+struct ieee80211_tdls_ch_sw_params {
+ struct ieee80211_sta *sta;
+ struct cfg80211_chan_def *chandef;
+ u8 action_code;
+ u32 status;
+ u32 timestamp;
+ u16 switch_time;
+ u16 switch_timeout;
+ struct sk_buff *tmpl_skb;
+ u32 ch_sw_tm_ie;
+};
+
+/**
* wiphy_to_ieee80211_hw - return a mac80211 driver hw struct from a wiphy
*
* @wiphy: the &struct wiphy which we want to query
@@ -2375,6 +2451,22 @@
};
/**
+ * enum ieee80211_reconfig_complete_type - reconfig type
+ *
+ * This enum is used by the reconfig_complete() callback to indicate what
+ * reconfiguration type was completed.
+ *
+ * @IEEE80211_RECONFIG_TYPE_RESTART: hw restart type
+ * (also due to resume() callback returning 1)
+ * @IEEE80211_RECONFIG_TYPE_SUSPEND: suspend type (regardless
+ * of wowlan configuration)
+ */
+enum ieee80211_reconfig_type {
+ IEEE80211_RECONFIG_TYPE_RESTART,
+ IEEE80211_RECONFIG_TYPE_SUSPEND,
+};
+
+/**
* struct ieee80211_ops - callbacks from mac80211 to the driver
*
* This structure contains various callbacks that the driver may
@@ -2530,7 +2622,9 @@
*
* @sw_scan_start: Notifier function that is called just before a software scan
* is started. Can be NULL, if the driver doesn't need this notification.
- * The callback can sleep.
+ * The mac_addr parameter allows supporting NL80211_SCAN_FLAG_RANDOM_ADDR,
+ * the driver may set the NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR flag if it
+ * can use this parameter. The callback can sleep.
*
* @sw_scan_complete: Notifier function that is called just after a
* software scan finished. Can be NULL, if the driver doesn't need
@@ -2601,6 +2695,9 @@
* uses hardware rate control (%IEEE80211_HW_HAS_RATE_CONTROL) since
* otherwise the rate control algorithm is notified directly.
* Must be atomic.
+ * @sta_rate_tbl_update: Notifies the driver that the rate table changed. This
+ * is only used if the configured rate control algorithm actually uses
+ * the new rate table API, and is therefore optional. Must be atomic.
*
* @conf_tx: Configure TX queue parameters (EDCF (aifs, cw_min, cw_max),
* bursting) for a hardware TX queue.
@@ -2809,11 +2906,11 @@
* disabled/enabled via @bss_info_changed.
* @stop_ap: Stop operation on the AP interface.
*
- * @restart_complete: Called after a call to ieee80211_restart_hw(), when the
- * reconfiguration has completed. This can help the driver implement the
- * reconfiguration step. Also called when reconfiguring because the
- * driver's resume function returned 1, as this is just like an "inline"
- * hardware restart. This callback may sleep.
+ * @reconfig_complete: Called after a call to ieee80211_restart_hw() and
+ * during resume, when the reconfiguration has completed.
+ * This can help the driver implement the reconfiguration step (and
+ * indicate mac80211 is ready to receive frames).
+ * This callback may sleep.
*
* @ipv6_addr_change: IPv6 address assignment on the given interface changed.
* Currently, this is only called for managed or P2P client interfaces.
@@ -2829,6 +2926,13 @@
* transmitted and then call ieee80211_csa_finish().
* If the CSA count starts as zero or 1, this function will not be called,
* since there won't be any time to beacon before the switch anyway.
+ * @pre_channel_switch: This is an optional callback that is called
+ * before a channel switch procedure is started (ie. when a STA
+ * gets a CSA or an userspace initiated channel-switch), allowing
+ * the driver to prepare for the channel switch.
+ * @post_channel_switch: This is an optional callback that is called
+ * after a channel switch procedure is completed, allowing the
+ * driver to go back to a normal configuration.
*
* @join_ibss: Join an IBSS (on an IBSS interface); this is called after all
* information in bss_conf is set up and the beacon can be retrieved. A
@@ -2838,6 +2942,26 @@
* @get_expected_throughput: extract the expected throughput towards the
* specified station. The returned value is expressed in Kbps. It returns 0
* if the RC algorithm does not have proper data to provide.
+ *
+ * @get_txpower: get current maximum tx power (in dBm) based on configuration
+ * and hardware limits.
+ *
+ * @tdls_channel_switch: Start channel-switching with a TDLS peer. The driver
+ * is responsible for continually initiating channel-switching operations
+ * and returning to the base channel for communication with the AP. The
+ * driver receives a channel-switch request template and the location of
+ * the switch-timing IE within the template as part of the invocation.
+ * The template is valid only within the call, and the driver can
+ * optionally copy the skb for further re-use.
+ * @tdls_cancel_channel_switch: Stop channel-switching with a TDLS peer. Both
+ * peers must be on the base channel when the call completes.
+ * @tdls_recv_channel_switch: a TDLS channel-switch related frame (request or
+ * response) has been received from a remote peer. The driver gets
+ * parameters parsed from the incoming frame and may use them to continue
+ * an ongoing channel-switch operation. In addition, a channel-switch
+ * response template is provided, together with the location of the
+ * switch-timing IE within the template. The skb can only be used within
+ * the function call.
*/
struct ieee80211_ops {
void (*tx)(struct ieee80211_hw *hw,
@@ -2897,8 +3021,11 @@
struct ieee80211_scan_ies *ies);
int (*sched_scan_stop)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif);
- void (*sw_scan_start)(struct ieee80211_hw *hw);
- void (*sw_scan_complete)(struct ieee80211_hw *hw);
+ void (*sw_scan_start)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ const u8 *mac_addr);
+ void (*sw_scan_complete)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
int (*get_stats)(struct ieee80211_hw *hw,
struct ieee80211_low_level_stats *stats);
void (*get_tkip_seq)(struct ieee80211_hw *hw, u8 hw_key_idx,
@@ -2932,6 +3059,9 @@
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
u32 changed);
+ void (*sta_rate_tbl_update)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
int (*conf_tx)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, u16 ac,
const struct ieee80211_tx_queue_params *params);
@@ -2959,6 +3089,7 @@
void (*flush)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
u32 queues, bool drop);
void (*channel_switch)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
struct ieee80211_channel_switch *ch_switch);
int (*set_antenna)(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant);
int (*get_antenna)(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant);
@@ -3025,7 +3156,8 @@
int n_vifs,
enum ieee80211_chanctx_switch_mode mode);
- void (*restart_complete)(struct ieee80211_hw *hw);
+ void (*reconfig_complete)(struct ieee80211_hw *hw,
+ enum ieee80211_reconfig_type reconfig_type);
#if IS_ENABLED(CONFIG_IPV6)
void (*ipv6_addr_change)(struct ieee80211_hw *hw,
@@ -3035,14 +3167,54 @@
void (*channel_switch_beacon)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct cfg80211_chan_def *chandef);
+ int (*pre_channel_switch)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel_switch *ch_switch);
+
+ int (*post_channel_switch)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
int (*join_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
void (*leave_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
u32 (*get_expected_throughput)(struct ieee80211_sta *sta);
+ int (*get_txpower)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ int *dbm);
+
+ int (*tdls_channel_switch)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, u8 oper_class,
+ struct cfg80211_chan_def *chandef,
+ struct sk_buff *tmpl_skb, u32 ch_sw_tm_ie);
+ void (*tdls_cancel_channel_switch)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+ void (*tdls_recv_channel_switch)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_tdls_ch_sw_params *params);
};
/**
- * ieee80211_alloc_hw - Allocate a new hardware device
+ * ieee80211_alloc_hw_nm - Allocate a new hardware device
+ *
+ * This must be called once for each hardware device. The returned pointer
+ * must be used to refer to this device when calling other functions.
+ * mac80211 allocates a private data area for the driver pointed to by
+ * @priv in &struct ieee80211_hw, the size of this area is given as
+ * @priv_data_len.
+ *
+ * @priv_data_len: length of private data
+ * @ops: callbacks for this device
+ * @requested_name: Requested name for this device.
+ * NULL is valid value, and means use the default naming (phy%d)
+ *
+ * Return: A pointer to the new hardware device, or %NULL on error.
+ */
+struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
+ const struct ieee80211_ops *ops,
+ const char *requested_name);
+
+/**
+ * ieee80211_alloc_hw - Allocate a new hardware device
*
* This must be called once for each hardware device. The returned pointer
* must be used to refer to this device when calling other functions.
@@ -3055,8 +3227,12 @@
*
* Return: A pointer to the new hardware device, or %NULL on error.
*/
+static inline
struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
- const struct ieee80211_ops *ops);
+ const struct ieee80211_ops *ops)
+{
+ return ieee80211_alloc_hw_nm(priv_data_len, ops, NULL);
+}
/**
* ieee80211_register_hw - Register hardware device
@@ -3443,6 +3619,26 @@
struct sk_buff *skb);
/**
+ * ieee80211_tx_status_noskb - transmit status callback without skb
+ *
+ * This function can be used as a replacement for ieee80211_tx_status
+ * in drivers that cannot reliably map tx status information back to
+ * specific skbs.
+ *
+ * Calls to this function for a single hardware must be synchronized
+ * against each other. Calls to this function, ieee80211_tx_status_ni()
+ * and ieee80211_tx_status_irqsafe() may not be mixed for a single hardware.
+ *
+ * @hw: the hardware the frame was transmitted by
+ * @sta: the receiver station to which this packet is sent
+ * (NULL for multicast packets)
+ * @info: tx status information
+ */
+void ieee80211_tx_status_noskb(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta,
+ struct ieee80211_tx_info *info);
+
+/**
* ieee80211_tx_status_ni - transmit status callback (in process context)
*
* Like ieee80211_tx_status() but can be called in process context.
@@ -3655,7 +3851,7 @@
/**
* ieee80211_probereq_get - retrieve a Probe Request template
* @hw: pointer obtained from ieee80211_alloc_hw().
- * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @src_addr: source MAC address
* @ssid: SSID buffer
* @ssid_len: length of SSID
* @tailroom: tailroom to reserve at end of SKB for IEs
@@ -3666,7 +3862,7 @@
* Return: The Probe Request template. %NULL on error.
*/
struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
+ const u8 *src_addr,
const u8 *ssid, size_t ssid_len,
size_t tailroom);
@@ -4172,6 +4368,22 @@
void *data);
/**
+ * ieee80211_iterate_stations_atomic - iterate stations
+ *
+ * This function iterates over all stations associated with a given
+ * hardware that are currently uploaded to the driver and calls the callback
+ * function for them.
+ * This function requires the iterator callback function to be atomic,
+ *
+ * @hw: the hardware struct of which the interfaces should be iterated over
+ * @iterator: the iterator function to call, cannot sleep
+ * @data: first argument of the iterator function
+ */
+void ieee80211_iterate_stations_atomic(struct ieee80211_hw *hw,
+ void (*iterator)(void *data,
+ struct ieee80211_sta *sta),
+ void *data);
+/**
* ieee80211_queue_work - add work onto the mac80211 workqueue
*
* Drivers and mac80211 use this to add work onto the mac80211 workqueue.
@@ -4480,6 +4692,14 @@
gfp_t gfp);
/**
+ * ieee80211_cqm_beacon_loss_notify - inform CQM of beacon loss
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @gfp: context flags
+ */
+void ieee80211_cqm_beacon_loss_notify(struct ieee80211_vif *vif, gfp_t gfp);
+
+/**
* ieee80211_radar_detected - inform that a radar was detected
*
* @hw: pointer as obtained from ieee80211_alloc_hw()
@@ -4637,6 +4857,10 @@
void (*free_sta)(void *priv, struct ieee80211_sta *sta,
void *priv_sta);
+ void (*tx_status_noskb)(void *priv,
+ struct ieee80211_supported_band *sband,
+ struct ieee80211_sta *sta, void *priv_sta,
+ struct ieee80211_tx_info *info);
void (*tx_status)(void *priv, struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta, void *priv_sta,
struct sk_buff *skb);
@@ -4888,4 +5112,69 @@
void ieee80211_tdls_oper_request(struct ieee80211_vif *vif, const u8 *peer,
enum nl80211_tdls_operation oper,
u16 reason_code, gfp_t gfp);
+
+/**
+ * ieee80211_reserve_tid - request to reserve a specific TID
+ *
+ * There is sometimes a need (such as in TDLS) for blocking the driver from
+ * using a specific TID so that the FW can use it for certain operations such
+ * as sending PTI requests. To make sure that the driver doesn't use that TID,
+ * this function must be called as it flushes out packets on this TID and marks
+ * it as blocked, so that any transmit for the station on this TID will be
+ * redirected to the alternative TID in the same AC.
+ *
+ * Note that this function blocks and may call back into the driver, so it
+ * should be called without driver locks held. Also note this function should
+ * only be called from the driver's @sta_state callback.
+ *
+ * @sta: the station to reserve the TID for
+ * @tid: the TID to reserve
+ *
+ * Returns: 0 on success, else on failure
+ */
+int ieee80211_reserve_tid(struct ieee80211_sta *sta, u8 tid);
+
+/**
+ * ieee80211_unreserve_tid - request to unreserve a specific TID
+ *
+ * Once there is no longer any need for reserving a certain TID, this function
+ * should be called, and no longer will packets have their TID modified for
+ * preventing use of this TID in the driver.
+ *
+ * Note that this function blocks and acquires a lock, so it should be called
+ * without driver locks held. Also note this function should only be called
+ * from the driver's @sta_state callback.
+ *
+ * @sta: the station
+ * @tid: the TID to unreserve
+ */
+void ieee80211_unreserve_tid(struct ieee80211_sta *sta, u8 tid);
+
+/**
+ * ieee80211_ie_split - split an IE buffer according to ordering
+ *
+ * @ies: the IE buffer
+ * @ielen: the length of the IE buffer
+ * @ids: an array with element IDs that are allowed before
+ * the split
+ * @n_ids: the size of the element ID array
+ * @offset: offset where to start splitting in the buffer
+ *
+ * This function splits an IE buffer by updating the @offset
+ * variable to point to the location where the buffer should be
+ * split.
+ *
+ * It assumes that the given IE buffer is well-formed, this
+ * has to be guaranteed by the caller!
+ *
+ * It also assumes that the IEs in the buffer are ordered
+ * correctly, if not the result of using this function will not
+ * be ordered correctly either, i.e. it does no reordering.
+ *
+ * The function returns the offset where the next part of the
+ * buffer starts, which may be @ielen if the entire (remainder)
+ * of the buffer should be used.
+ */
+size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
+ const u8 *ids, int n_ids, size_t offset);
#endif /* MAC80211_H */
diff --git a/include/net/mac802154.h b/include/net/mac802154.h
index 2e67cdd..c823d91 100644
--- a/include/net/mac802154.h
+++ b/include/net/mac802154.h
@@ -12,14 +12,12 @@
* 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_MAC802154_H
#define NET_MAC802154_H
#include <net/af_ieee802154.h>
+#include <linux/ieee802154.h>
#include <linux/skbuff.h>
/* General MAC frame format:
@@ -35,13 +33,13 @@
*/
/* indicates that the Short Address changed */
-#define IEEE802515_AFILT_SADDR_CHANGED 0x00000001
+#define IEEE802154_AFILT_SADDR_CHANGED 0x00000001
/* indicates that the IEEE Address changed */
-#define IEEE802515_AFILT_IEEEADDR_CHANGED 0x00000002
+#define IEEE802154_AFILT_IEEEADDR_CHANGED 0x00000002
/* indicates that the PAN ID changed */
-#define IEEE802515_AFILT_PANID_CHANGED 0x00000004
+#define IEEE802154_AFILT_PANID_CHANGED 0x00000004
/* indicates that PAN Coordinator status changed */
-#define IEEE802515_AFILT_PANC_CHANGED 0x00000008
+#define IEEE802154_AFILT_PANC_CHANGED 0x00000008
struct ieee802154_hw_addr_filt {
__le16 pan_id; /* Each independent PAN selects a unique
@@ -55,7 +53,14 @@
u8 pan_coord;
};
-struct ieee802154_dev {
+struct ieee802154_vif {
+ int type;
+
+ /* must be last */
+ u8 drv_priv[0] __aligned(sizeof(void *));
+};
+
+struct ieee802154_hw {
/* filled by the driver */
int extra_tx_headroom;
u32 flags;
@@ -65,6 +70,7 @@
struct ieee802154_hw_addr_filt hw_filt;
void *priv;
struct wpan_phy *phy;
+ size_t vif_data_size;
};
/* Checksum is in hardware and is omitted from a packet
@@ -76,28 +82,43 @@
* however, so you are advised to review these flags carefully.
*/
-/* Indicates that receiver omits FCS and xmitter will add FCS on it's own. */
-#define IEEE802154_HW_OMIT_CKSUM 0x00000001
+/* Indicates that xmitter will add FCS on it's own. */
+#define IEEE802154_HW_TX_OMIT_CKSUM 0x00000001
/* Indicates that receiver will autorespond with ACK frames. */
-#define IEEE802154_HW_AACK 0x00000002
+#define IEEE802154_HW_AACK 0x00000002
/* Indicates that transceiver will support transmit power setting. */
-#define IEEE802154_HW_TXPOWER 0x00000004
+#define IEEE802154_HW_TXPOWER 0x00000004
/* Indicates that transceiver will support listen before transmit. */
-#define IEEE802154_HW_LBT 0x00000008
+#define IEEE802154_HW_LBT 0x00000008
/* Indicates that transceiver will support cca mode setting. */
-#define IEEE802154_HW_CCA_MODE 0x00000010
+#define IEEE802154_HW_CCA_MODE 0x00000010
/* Indicates that transceiver will support cca ed level setting. */
-#define IEEE802154_HW_CCA_ED_LEVEL 0x00000020
+#define IEEE802154_HW_CCA_ED_LEVEL 0x00000020
/* Indicates that transceiver will support csma (max_be, min_be, csma retries)
* settings. */
-#define IEEE802154_HW_CSMA_PARAMS 0x00000040
+#define IEEE802154_HW_CSMA_PARAMS 0x00000040
/* Indicates that transceiver will support ARET frame retries setting. */
-#define IEEE802154_HW_FRAME_RETRIES 0x00000080
+#define IEEE802154_HW_FRAME_RETRIES 0x00000080
+/* Indicates that transceiver will support hardware address filter setting. */
+#define IEEE802154_HW_AFILT 0x00000100
+/* Indicates that transceiver will support promiscuous mode setting. */
+#define IEEE802154_HW_PROMISCUOUS 0x00000200
+/* Indicates that receiver omits FCS. */
+#define IEEE802154_HW_RX_OMIT_CKSUM 0x00000400
+/* Indicates that receiver will not filter frames with bad checksum. */
+#define IEEE802154_HW_RX_DROP_BAD_CKSUM 0x00000800
+
+/* Indicates that receiver omits FCS and xmitter will add FCS on it's own. */
+#define IEEE802154_HW_OMIT_CKSUM (IEEE802154_HW_TX_OMIT_CKSUM | \
+ IEEE802154_HW_RX_OMIT_CKSUM)
/* This groups the most common CSMA support fields into one. */
#define IEEE802154_HW_CSMA (IEEE802154_HW_CCA_MODE | \
IEEE802154_HW_CCA_ED_LEVEL | \
- IEEE802154_HW_CSMA_PARAMS | \
+ IEEE802154_HW_CSMA_PARAMS)
+
+/* This groups the most common ARET support fields into one. */
+#define IEEE802154_HW_ARET (IEEE802154_HW_CSMA | \
IEEE802154_HW_FRAME_RETRIES)
/* struct ieee802154_ops - callbacks from mac802154 to the driver
@@ -112,12 +133,24 @@
* stop: Handler that 802.15.4 module calls for device cleanup.
* This function is called after the last interface is removed.
*
- * xmit: Handler that 802.15.4 module calls for each transmitted frame.
+ * xmit_sync:
+ * Handler that 802.15.4 module calls for each transmitted frame.
+ * skb cntains the buffer starting from the IEEE 802.15.4 header.
+ * The low-level driver should send the frame based on available
+ * configuration. This is called by a workqueue and useful for
+ * synchronous 802.15.4 drivers.
+ * This function should return zero or negative errno.
+ *
+ * WARNING:
+ * This will be deprecated soon. We don't accept synced xmit callbacks
+ * drivers anymore.
+ *
+ * xmit_async:
+ * Handler that 802.15.4 module calls for each transmitted frame.
* skb cntains the buffer starting from the IEEE 802.15.4 header.
* The low-level driver should send the frame based on available
* configuration.
- * This function should return zero or negative errno. Called with
- * pib_lock held.
+ * This function should return zero or negative errno.
*
* ed: Handler that 802.15.4 module calls for Energy Detection.
* This function should place the value for detected energy
@@ -159,40 +192,75 @@
* set_frame_retries
* Sets the retransmission attempt limit. Called with pib_lock held.
* Returns either zero, or negative errno.
+ *
+ * set_promiscuous_mode
+ * Enables or disable promiscuous mode.
*/
struct ieee802154_ops {
struct module *owner;
- int (*start)(struct ieee802154_dev *dev);
- void (*stop)(struct ieee802154_dev *dev);
- int (*xmit)(struct ieee802154_dev *dev,
- struct sk_buff *skb);
- int (*ed)(struct ieee802154_dev *dev, u8 *level);
- int (*set_channel)(struct ieee802154_dev *dev,
- int page,
- int channel);
- int (*set_hw_addr_filt)(struct ieee802154_dev *dev,
- struct ieee802154_hw_addr_filt *filt,
+ int (*start)(struct ieee802154_hw *hw);
+ void (*stop)(struct ieee802154_hw *hw);
+ int (*xmit_sync)(struct ieee802154_hw *hw,
+ struct sk_buff *skb);
+ int (*xmit_async)(struct ieee802154_hw *hw,
+ struct sk_buff *skb);
+ int (*ed)(struct ieee802154_hw *hw, u8 *level);
+ int (*set_channel)(struct ieee802154_hw *hw, u8 page,
+ u8 channel);
+ int (*set_hw_addr_filt)(struct ieee802154_hw *hw,
+ struct ieee802154_hw_addr_filt *filt,
unsigned long changed);
- int (*ieee_addr)(struct ieee802154_dev *dev, __le64 addr);
- int (*set_txpower)(struct ieee802154_dev *dev, int db);
- int (*set_lbt)(struct ieee802154_dev *dev, bool on);
- int (*set_cca_mode)(struct ieee802154_dev *dev, u8 mode);
- int (*set_cca_ed_level)(struct ieee802154_dev *dev,
+ int (*set_txpower)(struct ieee802154_hw *hw, int db);
+ int (*set_lbt)(struct ieee802154_hw *hw, bool on);
+ int (*set_cca_mode)(struct ieee802154_hw *hw, u8 mode);
+ int (*set_cca_ed_level)(struct ieee802154_hw *hw,
s32 level);
- int (*set_csma_params)(struct ieee802154_dev *dev,
+ int (*set_csma_params)(struct ieee802154_hw *hw,
u8 min_be, u8 max_be, u8 retries);
- int (*set_frame_retries)(struct ieee802154_dev *dev,
+ int (*set_frame_retries)(struct ieee802154_hw *hw,
s8 retries);
+ int (*set_promiscuous_mode)(struct ieee802154_hw *hw,
+ const bool on);
};
-/* Basic interface to register ieee802154 device */
-struct ieee802154_dev *
-ieee802154_alloc_device(size_t priv_data_len, struct ieee802154_ops *ops);
-void ieee802154_free_device(struct ieee802154_dev *dev);
-int ieee802154_register_device(struct ieee802154_dev *dev);
-void ieee802154_unregister_device(struct ieee802154_dev *dev);
+/**
+ * ieee802154_be64_to_le64 - copies and convert be64 to le64
+ * @le64_dst: le64 destination pointer
+ * @be64_src: be64 source pointer
+ */
+static inline void ieee802154_be64_to_le64(void *le64_dst, const void *be64_src)
+{
+ __le64 tmp = (__force __le64)swab64p(be64_src);
-void ieee802154_rx_irqsafe(struct ieee802154_dev *dev, struct sk_buff *skb,
+ memcpy(le64_dst, &tmp, IEEE802154_EXTENDED_ADDR_LEN);
+}
+
+/**
+ * ieee802154_le64_to_be64 - copies and convert le64 to be64
+ * @be64_dst: be64 destination pointer
+ * @le64_src: le64 source pointer
+ */
+static inline void ieee802154_le64_to_be64(void *be64_dst, const void *le64_src)
+{
+ __be64 tmp = (__force __be64)swab64p(le64_src);
+
+ memcpy(be64_dst, &tmp, IEEE802154_EXTENDED_ADDR_LEN);
+}
+
+/* Basic interface to register ieee802154 hwice */
+struct ieee802154_hw *
+ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops);
+void ieee802154_free_hw(struct ieee802154_hw *hw);
+int ieee802154_register_hw(struct ieee802154_hw *hw);
+void ieee802154_unregister_hw(struct ieee802154_hw *hw);
+
+void ieee802154_rx(struct ieee802154_hw *hw, struct sk_buff *skb);
+void ieee802154_rx_irqsafe(struct ieee802154_hw *hw, struct sk_buff *skb,
u8 lqi);
+void ieee802154_wake_queue(struct ieee802154_hw *hw);
+void ieee802154_stop_queue(struct ieee802154_hw *hw);
+void ieee802154_xmit_complete(struct ieee802154_hw *hw, struct sk_buff *skb,
+ bool ifs_handling);
+
#endif /* NET_MAC802154_H */
diff --git a/include/net/nfc/digital.h b/include/net/nfc/digital.h
index d9a5cf7..0ae101e 100644
--- a/include/net/nfc/digital.h
+++ b/include/net/nfc/digital.h
@@ -225,6 +225,19 @@
u8 curr_protocol;
u8 curr_rf_tech;
u8 curr_nfc_dep_pni;
+ u8 did;
+
+ u8 local_payload_max;
+ u8 remote_payload_max;
+
+ struct sk_buff *chaining_skb;
+ struct digital_data_exch *data_exch;
+
+ int atn_count;
+ int nack_count;
+
+ struct sk_buff *saved_skb;
+ unsigned int saved_skb_len;
u16 target_fsc;
diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h
index 7ee8f4c..14bd0e1 100644
--- a/include/net/nfc/hci.h
+++ b/include/net/nfc/hci.h
@@ -57,10 +57,14 @@
int (*discover_se)(struct nfc_hci_dev *dev);
int (*enable_se)(struct nfc_hci_dev *dev, u32 se_idx);
int (*disable_se)(struct nfc_hci_dev *dev, u32 se_idx);
+ int (*se_io)(struct nfc_hci_dev *dev, u32 se_idx,
+ u8 *apdu, size_t apdu_length,
+ se_io_cb_t cb, void *cb_context);
};
/* Pipes */
#define NFC_HCI_INVALID_PIPE 0x80
+#define NFC_HCI_DO_NOT_CREATE_PIPE 0x81
#define NFC_HCI_LINK_MGMT_PIPE 0x00
#define NFC_HCI_ADMIN_PIPE 0x01
diff --git a/include/net/nfc/nci.h b/include/net/nfc/nci.h
index 9eca9ae..e7257a4 100644
--- a/include/net/nfc/nci.h
+++ b/include/net/nfc/nci.h
@@ -28,6 +28,8 @@
#ifndef __NCI_H
#define __NCI_H
+#include <net/nfc/nfc.h>
+
/* NCI constants */
#define NCI_MAX_NUM_MAPPING_CONFIGS 10
#define NCI_MAX_NUM_RF_CONFIGS 10
@@ -73,6 +75,8 @@
#define NCI_NFC_A_ACTIVE_LISTEN_MODE 0x83
#define NCI_NFC_F_ACTIVE_LISTEN_MODE 0x85
+#define NCI_RF_TECH_MODE_LISTEN_MASK 0x80
+
/* NCI RF Technologies */
#define NCI_NFC_RF_TECHNOLOGY_A 0x00
#define NCI_NFC_RF_TECHNOLOGY_B 0x01
@@ -106,6 +110,17 @@
/* NCI Configuration Parameter Tags */
#define NCI_PN_ATR_REQ_GEN_BYTES 0x29
+#define NCI_LN_ATR_RES_GEN_BYTES 0x61
+#define NCI_LA_SEL_INFO 0x32
+#define NCI_LF_PROTOCOL_TYPE 0x50
+#define NCI_LF_CON_BITR_F 0x54
+
+/* NCI Configuration Parameters masks */
+#define NCI_LA_SEL_INFO_ISO_DEP_MASK 0x20
+#define NCI_LA_SEL_INFO_NFC_DEP_MASK 0x40
+#define NCI_LF_PROTOCOL_TYPE_NFC_DEP_MASK 0x02
+#define NCI_LF_CON_BITR_F_212 0x02
+#define NCI_LF_CON_BITR_F_424 0x04
/* NCI Reset types */
#define NCI_RESET_TYPE_KEEP_CONFIG 0x00
@@ -314,26 +329,31 @@
struct rf_tech_specific_params_nfca_poll {
__u16 sens_res;
__u8 nfcid1_len; /* 0, 4, 7, or 10 Bytes */
- __u8 nfcid1[10];
+ __u8 nfcid1[NFC_NFCID1_MAXSIZE];
__u8 sel_res_len; /* 0 or 1 Bytes */
__u8 sel_res;
} __packed;
struct rf_tech_specific_params_nfcb_poll {
__u8 sensb_res_len;
- __u8 sensb_res[12]; /* 11 or 12 Bytes */
+ __u8 sensb_res[NFC_SENSB_RES_MAXSIZE]; /* 11 or 12 Bytes */
} __packed;
struct rf_tech_specific_params_nfcf_poll {
__u8 bit_rate;
__u8 sensf_res_len;
- __u8 sensf_res[18]; /* 16 or 18 Bytes */
+ __u8 sensf_res[NFC_SENSF_RES_MAXSIZE]; /* 16 or 18 Bytes */
} __packed;
struct rf_tech_specific_params_nfcv_poll {
__u8 res_flags;
__u8 dsfid;
- __u8 uid[8]; /* 8 Bytes */
+ __u8 uid[NFC_ISO15693_UID_MAXSIZE]; /* 8 Bytes */
+} __packed;
+
+struct rf_tech_specific_params_nfcf_listen {
+ __u8 local_nfcid2_len;
+ __u8 local_nfcid2[NFC_NFCID2_MAXSIZE]; /* 0 or 8 Bytes */
} __packed;
struct nci_rf_discover_ntf {
@@ -365,7 +385,12 @@
struct activation_params_poll_nfc_dep {
__u8 atr_res_len;
- __u8 atr_res[63];
+ __u8 atr_res[NFC_ATR_RES_MAXSIZE - 2]; /* ATR_RES from byte 3 */
+};
+
+struct activation_params_listen_nfc_dep {
+ __u8 atr_req_len;
+ __u8 atr_req[NFC_ATR_REQ_MAXSIZE - 2]; /* ATR_REQ from byte 3 */
};
struct nci_rf_intf_activated_ntf {
@@ -382,6 +407,7 @@
struct rf_tech_specific_params_nfcb_poll nfcb_poll;
struct rf_tech_specific_params_nfcf_poll nfcf_poll;
struct rf_tech_specific_params_nfcv_poll nfcv_poll;
+ struct rf_tech_specific_params_nfcf_listen nfcf_listen;
} rf_tech_specific_params;
__u8 data_exch_rf_tech_and_mode;
@@ -393,6 +419,7 @@
struct activation_params_nfca_poll_iso_dep nfca_poll_iso_dep;
struct activation_params_nfcb_poll_iso_dep nfcb_poll_iso_dep;
struct activation_params_poll_nfc_dep poll_nfc_dep;
+ struct activation_params_listen_nfc_dep listen_nfc_dep;
} activation_params;
} __packed;
diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h
index 75d10e6..9e51bb4 100644
--- a/include/net/nfc/nci_core.h
+++ b/include/net/nfc/nci_core.h
@@ -4,6 +4,7 @@
*
* Copyright (C) 2011 Texas Instruments, Inc.
* Copyright (C) 2013 Intel Corporation. All rights reserved.
+ * Copyright (C) 2014 Marvell International Ltd.
*
* Written by Ilan Elias <ilane@ti.com>
*
@@ -49,6 +50,8 @@
NCI_W4_ALL_DISCOVERIES,
NCI_W4_HOST_SELECT,
NCI_POLL_ACTIVE,
+ NCI_LISTEN_ACTIVE,
+ NCI_LISTEN_SLEEP,
};
/* NCI timeouts */
@@ -69,6 +72,12 @@
int (*send)(struct nci_dev *ndev, struct sk_buff *skb);
int (*setup)(struct nci_dev *ndev);
__u32 (*get_rfprotocol)(struct nci_dev *ndev, __u8 rf_protocol);
+ int (*discover_se)(struct nci_dev *ndev);
+ int (*disable_se)(struct nci_dev *ndev, u32 se_idx);
+ int (*enable_se)(struct nci_dev *ndev, u32 se_idx);
+ int (*se_io)(struct nci_dev *ndev, u32 se_idx,
+ u8 *apdu, size_t apdu_length,
+ se_io_cb_t cb, void *cb_context);
};
#define NCI_MAX_SUPPORTED_RF_INTERFACES 4
diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h
index 6c583e2..12adb81 100644
--- a/include/net/nfc/nfc.h
+++ b/include/net/nfc/nfc.h
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2011 Instituto Nokia de Tecnologia
+ * Copyright (C) 2014 Marvell International Ltd.
*
* Authors:
* Lauro Ramos Venancio <lauro.venancio@openbossa.org>
@@ -87,6 +88,7 @@
#define NFC_TARGET_IDX_ANY -1
#define NFC_MAX_GT_LEN 48
#define NFC_ATR_RES_GT_OFFSET 15
+#define NFC_ATR_REQ_GT_OFFSET 14
/**
* struct nfc_target - NFC target descriptiom
diff --git a/include/net/nl802154.h b/include/net/nl802154.h
index b23548e..6dbd406 100644
--- a/include/net/nl802154.h
+++ b/include/net/nl802154.h
@@ -1,126 +1,122 @@
+#ifndef __NL802154_H
+#define __NL802154_H
/*
- * nl802154.h
+ * 802.15.4 netlink interface public header
*
- * Copyright (C) 2007, 2008, 2009 Siemens AG
+ * Copyright 2014 Alexander Aring <aar@pengutronix.de>
*
- * 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.
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
*
- * 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.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
-#ifndef IEEE802154_NL_H
-#define IEEE802154_NL_H
+#define NL802154_GENL_NAME "nl802154"
-struct net_device;
-struct ieee802154_addr;
+enum nl802154_commands {
+/* don't change the order or add anything between, this is ABI! */
+/* currently we don't shipping this file via uapi, ignore the above one */
+ NL802154_CMD_UNSPEC,
-/**
- * ieee802154_nl_assoc_indic - Notify userland of an association request.
- * @dev: The network device on which this association request was
- * received.
- * @addr: The address of the device requesting association.
- * @cap: The capability information field from the device.
- *
- * This informs a userland coordinator of a device requesting to
- * associate with the PAN controlled by the coordinator.
- *
- * Note: This is in section 7.3.1 of the IEEE 802.15.4-2006 document.
- */
-int ieee802154_nl_assoc_indic(struct net_device *dev,
- struct ieee802154_addr *addr, u8 cap);
+ NL802154_CMD_GET_WPAN_PHY, /* can dump */
+ NL802154_CMD_SET_WPAN_PHY,
+ NL802154_CMD_NEW_WPAN_PHY,
+ NL802154_CMD_DEL_WPAN_PHY,
-/**
- * ieee802154_nl_assoc_confirm - Notify userland of association.
- * @dev: The device which has completed association.
- * @short_addr: The short address assigned to the device.
- * @status: The status of the association.
- *
- * Inform userland of the result of an association request. If the
- * association request included asking the coordinator to allocate
- * a short address then it is returned in @short_addr.
- *
- * Note: This is in section 7.3.2 of the IEEE 802.15.4 document.
- */
-int ieee802154_nl_assoc_confirm(struct net_device *dev,
- __le16 short_addr, u8 status);
+ NL802154_CMD_GET_INTERFACE, /* can dump */
+ NL802154_CMD_SET_INTERFACE,
+ NL802154_CMD_NEW_INTERFACE,
+ NL802154_CMD_DEL_INTERFACE,
-/**
- * ieee802154_nl_disassoc_indic - Notify userland of disassociation.
- * @dev: The device on which disassociation was indicated.
- * @addr: The device which is disassociating.
- * @reason: The reason for the disassociation.
- *
- * Inform userland that a device has disassociated from the network.
- *
- * Note: This is in section 7.3.3 of the IEEE 802.15.4 document.
- */
-int ieee802154_nl_disassoc_indic(struct net_device *dev,
- struct ieee802154_addr *addr, u8 reason);
+ NL802154_CMD_SET_CHANNEL,
-/**
- * ieee802154_nl_disassoc_confirm - Notify userland of disassociation
- * completion.
- * @dev: The device on which disassociation was ordered.
- * @status: The result of the disassociation.
- *
- * Inform userland of the result of requesting that a device
- * disassociate, or the result of requesting that we disassociate from
- * a PAN managed by another coordinator.
- *
- * Note: This is in section 7.1.4.3 of the IEEE 802.15.4 document.
- */
-int ieee802154_nl_disassoc_confirm(struct net_device *dev,
- u8 status);
+ NL802154_CMD_SET_PAN_ID,
+ NL802154_CMD_SET_SHORT_ADDR,
-/**
- * ieee802154_nl_scan_confirm - Notify userland of completion of scan.
- * @dev: The device which was instructed to scan.
- * @status: The status of the scan operation.
- * @scan_type: What type of scan was performed.
- * @unscanned: Any channels that the device was unable to scan.
- * @edl: The energy levels (if a passive scan).
- *
- *
- * Note: This is in section 7.1.11 of the IEEE 802.15.4 document.
- * Note: This API does not permit the return of an active scan result.
- */
-int ieee802154_nl_scan_confirm(struct net_device *dev,
- u8 status, u8 scan_type, u32 unscanned, u8 page,
- u8 *edl/*, struct list_head *pan_desc_list */);
+ NL802154_CMD_SET_TX_POWER,
+ NL802154_CMD_SET_CCA_MODE,
+ NL802154_CMD_SET_CCA_ED_LEVEL,
-/**
- * ieee802154_nl_beacon_indic - Notify userland of a received beacon.
- * @dev: The device on which a beacon was received.
- * @panid: The PAN of the coordinator.
- * @coord_addr: The short address of the coordinator on that PAN.
- *
- * Note: This is in section 7.1.5 of the IEEE 802.15.4 document.
- * Note: This API does not provide extended information such as what
- * channel the PAN is on or what the LQI of the beacon frame was on
- * receipt.
- * Note: This API cannot indicate a beacon frame for a coordinator
- * operating in long addressing mode.
- */
-int ieee802154_nl_beacon_indic(struct net_device *dev, __le16 panid,
- __le16 coord_addr);
+ NL802154_CMD_SET_MAX_FRAME_RETRIES,
-/**
- * ieee802154_nl_start_confirm - Notify userland of completion of start.
- * @dev: The device which was instructed to scan.
- * @status: The status of the scan operation.
- *
- * Note: This is in section 7.1.14 of the IEEE 802.15.4 document.
- */
-int ieee802154_nl_start_confirm(struct net_device *dev, u8 status);
+ NL802154_CMD_SET_BACKOFF_EXPONENT,
+ NL802154_CMD_SET_MAX_CSMA_BACKOFFS,
-#endif
+ NL802154_CMD_SET_LBT_MODE,
+
+ /* add new commands above here */
+
+ /* used to define NL802154_CMD_MAX below */
+ __NL802154_CMD_AFTER_LAST,
+ NL802154_CMD_MAX = __NL802154_CMD_AFTER_LAST - 1
+};
+
+enum nl802154_attrs {
+/* don't change the order or add anything between, this is ABI! */
+/* currently we don't shipping this file via uapi, ignore the above one */
+ NL802154_ATTR_UNSPEC,
+
+ NL802154_ATTR_WPAN_PHY,
+ NL802154_ATTR_WPAN_PHY_NAME,
+
+ NL802154_ATTR_IFINDEX,
+ NL802154_ATTR_IFNAME,
+ NL802154_ATTR_IFTYPE,
+
+ NL802154_ATTR_WPAN_DEV,
+
+ NL802154_ATTR_PAGE,
+ NL802154_ATTR_CHANNEL,
+
+ NL802154_ATTR_PAN_ID,
+ NL802154_ATTR_SHORT_ADDR,
+
+ NL802154_ATTR_TX_POWER,
+
+ NL802154_ATTR_CCA_MODE,
+ NL802154_ATTR_CCA_MODE3_AND,
+ NL802154_ATTR_CCA_ED_LEVEL,
+
+ NL802154_ATTR_MAX_FRAME_RETRIES,
+
+ NL802154_ATTR_MAX_BE,
+ NL802154_ATTR_MIN_BE,
+ NL802154_ATTR_MAX_CSMA_BACKOFFS,
+
+ NL802154_ATTR_LBT_MODE,
+
+ NL802154_ATTR_GENERATION,
+
+ NL802154_ATTR_CHANNELS_SUPPORTED,
+ NL802154_ATTR_SUPPORTED_CHANNEL,
+
+ NL802154_ATTR_EXTENDED_ADDR,
+
+ /* add attributes here, update the policy in nl802154.c */
+
+ __NL802154_ATTR_AFTER_LAST,
+ NL802154_ATTR_MAX = __NL802154_ATTR_AFTER_LAST - 1
+};
+
+enum nl802154_iftype {
+ /* for backwards compatibility TODO */
+ NL802154_IFTYPE_UNSPEC = -1,
+
+ NL802154_IFTYPE_NODE,
+ NL802154_IFTYPE_MONITOR,
+ NL802154_IFTYPE_COORD,
+
+ /* keep last */
+ NUM_NL802154_IFTYPES,
+ NL802154_IFTYPE_MAX = NUM_NL802154_IFTYPES - 1
+};
+
+#endif /* __NL802154_H */
diff --git a/include/net/regulatory.h b/include/net/regulatory.h
index dad7ab2..b776d72 100644
--- a/include/net/regulatory.h
+++ b/include/net/regulatory.h
@@ -136,6 +136,17 @@
* otherwise initiating radiation is not allowed. This will enable the
* relaxations enabled under the CFG80211_REG_RELAX_NO_IR configuration
* option
+ * @REGULATORY_IGNORE_STALE_KICKOFF: the regulatory core will _not_ make sure
+ * all interfaces on this wiphy reside on allowed channels. If this flag
+ * is not set, upon a regdomain change, the interfaces are given a grace
+ * period (currently 60 seconds) to disconnect or move to an allowed
+ * channel. Interfaces on forbidden channels are forcibly disconnected.
+ * Currently these types of interfaces are supported for enforcement:
+ * NL80211_IFTYPE_ADHOC, NL80211_IFTYPE_STATION, NL80211_IFTYPE_AP,
+ * NL80211_IFTYPE_AP_VLAN, NL80211_IFTYPE_MONITOR,
+ * NL80211_IFTYPE_P2P_CLIENT, NL80211_IFTYPE_P2P_GO,
+ * NL80211_IFTYPE_P2P_DEVICE. The flag will be set by default if a device
+ * includes any modes unsupported for enforcement checking.
*/
enum ieee80211_regulatory_flags {
REGULATORY_CUSTOM_REG = BIT(0),
@@ -144,6 +155,7 @@
REGULATORY_COUNTRY_IE_FOLLOW_POWER = BIT(3),
REGULATORY_COUNTRY_IE_IGNORE = BIT(4),
REGULATORY_ENABLE_RELAX_NO_IR = BIT(5),
+ REGULATORY_IGNORE_STALE_KICKOFF = BIT(6),
};
struct ieee80211_freq_range {
diff --git a/include/net/wpan-phy.h b/include/net/wpan-phy.h
deleted file mode 100644
index 10ab0fc..0000000
--- a/include/net/wpan-phy.h
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2007, 2008, 2009 Siemens AG
- *
- * 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.
- *
- * 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.
- *
- * Written by:
- * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
- */
-
-#ifndef WPAN_PHY_H
-#define WPAN_PHY_H
-
-#include <linux/netdevice.h>
-#include <linux/mutex.h>
-#include <linux/bug.h>
-
-/* According to the IEEE 802.15.4 stadard the upper most significant bits of
- * the 32-bit channel bitmaps shall be used as an integer value to specify 32
- * possible channel pages. The lower 27 bits of the channel bit map shall be
- * used as a bit mask to specify channel numbers within a channel page.
- */
-#define WPAN_NUM_CHANNELS 27
-#define WPAN_NUM_PAGES 32
-
-struct wpan_phy {
- struct mutex pib_lock;
-
- /*
- * This is a PIB according to 802.15.4-2011.
- * We do not provide timing-related variables, as they
- * aren't used outside of driver
- */
- u8 current_channel;
- u8 current_page;
- u32 channels_supported[32];
- s8 transmit_power;
- u8 cca_mode;
- u8 min_be;
- u8 max_be;
- u8 csma_retries;
- s8 frame_retries;
-
- bool lbt;
- s32 cca_ed_level;
-
- struct device dev;
- int idx;
-
- struct net_device *(*add_iface)(struct wpan_phy *phy,
- const char *name, int type);
- void (*del_iface)(struct wpan_phy *phy, struct net_device *dev);
-
- int (*set_txpower)(struct wpan_phy *phy, int db);
- int (*set_lbt)(struct wpan_phy *phy, bool on);
- int (*set_cca_mode)(struct wpan_phy *phy, u8 cca_mode);
- int (*set_cca_ed_level)(struct wpan_phy *phy, int level);
- int (*set_csma_params)(struct wpan_phy *phy, u8 min_be, u8 max_be,
- u8 retries);
- int (*set_frame_retries)(struct wpan_phy *phy, s8 retries);
-
- char priv[0] __attribute__((__aligned__(NETDEV_ALIGN)));
-};
-
-#define to_phy(_dev) container_of(_dev, struct wpan_phy, dev)
-
-struct wpan_phy *wpan_phy_alloc(size_t priv_size);
-static inline void wpan_phy_set_dev(struct wpan_phy *phy, struct device *dev)
-{
- phy->dev.parent = dev;
-}
-int wpan_phy_register(struct wpan_phy *phy);
-void wpan_phy_unregister(struct wpan_phy *phy);
-void wpan_phy_free(struct wpan_phy *phy);
-/* Same semantics as for class_for_each_device */
-int wpan_phy_for_each(int (*fn)(struct wpan_phy *phy, void *data), void *data);
-
-static inline void *wpan_phy_priv(struct wpan_phy *phy)
-{
- BUG_ON(!phy);
- return &phy->priv;
-}
-
-struct wpan_phy *wpan_phy_find(const char *str);
-
-static inline void wpan_phy_put(struct wpan_phy *phy)
-{
- put_device(&phy->dev);
-}
-
-static inline const char *wpan_phy_name(struct wpan_phy *phy)
-{
- return dev_name(&phy->dev);
-}
-#endif
diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h
new file mode 100644
index 0000000..23b4090
--- /dev/null
+++ b/include/uapi/linux/media-bus-format.h
@@ -0,0 +1,125 @@
+/*
+ * Media Bus API header
+ *
+ * Copyright (C) 2009, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * 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 __LINUX_MEDIA_BUS_FORMAT_H
+#define __LINUX_MEDIA_BUS_FORMAT_H
+
+/*
+ * These bus formats uniquely identify data formats on the data bus. Format 0
+ * is reserved, MEDIA_BUS_FMT_FIXED shall be used by host-client pairs, where
+ * the data format is fixed. Additionally, "2X8" means that one pixel is
+ * transferred in two 8-bit samples, "BE" or "LE" specify in which order those
+ * samples are transferred over the bus: "LE" means that the least significant
+ * bits are transferred first, "BE" means that the most significant bits are
+ * transferred first, and "PADHI" and "PADLO" define which bits - low or high,
+ * in the incomplete high byte, are filled with padding bits.
+ *
+ * The bus formats are grouped by type, bus_width, bits per component, samples
+ * per pixel and order of subsamples. Numerical values are sorted using generic
+ * numerical sort order (8 thus comes before 10).
+ *
+ * As their value can't change when a new bus format is inserted in the
+ * enumeration, the bus formats are explicitly given a numerical value. The next
+ * free values for each category are listed below, update them when inserting
+ * new pixel codes.
+ */
+
+#define MEDIA_BUS_FMT_FIXED 0x0001
+
+/* RGB - next is 0x100e */
+#define MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE 0x1001
+#define MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE 0x1002
+#define MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE 0x1003
+#define MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE 0x1004
+#define MEDIA_BUS_FMT_BGR565_2X8_BE 0x1005
+#define MEDIA_BUS_FMT_BGR565_2X8_LE 0x1006
+#define MEDIA_BUS_FMT_RGB565_2X8_BE 0x1007
+#define MEDIA_BUS_FMT_RGB565_2X8_LE 0x1008
+#define MEDIA_BUS_FMT_RGB666_1X18 0x1009
+#define MEDIA_BUS_FMT_RGB888_1X24 0x100a
+#define MEDIA_BUS_FMT_RGB888_2X12_BE 0x100b
+#define MEDIA_BUS_FMT_RGB888_2X12_LE 0x100c
+#define MEDIA_BUS_FMT_ARGB8888_1X32 0x100d
+
+/* YUV (including grey) - next is 0x2024 */
+#define MEDIA_BUS_FMT_Y8_1X8 0x2001
+#define MEDIA_BUS_FMT_UV8_1X8 0x2015
+#define MEDIA_BUS_FMT_UYVY8_1_5X8 0x2002
+#define MEDIA_BUS_FMT_VYUY8_1_5X8 0x2003
+#define MEDIA_BUS_FMT_YUYV8_1_5X8 0x2004
+#define MEDIA_BUS_FMT_YVYU8_1_5X8 0x2005
+#define MEDIA_BUS_FMT_UYVY8_2X8 0x2006
+#define MEDIA_BUS_FMT_VYUY8_2X8 0x2007
+#define MEDIA_BUS_FMT_YUYV8_2X8 0x2008
+#define MEDIA_BUS_FMT_YVYU8_2X8 0x2009
+#define MEDIA_BUS_FMT_Y10_1X10 0x200a
+#define MEDIA_BUS_FMT_UYVY10_2X10 0x2018
+#define MEDIA_BUS_FMT_VYUY10_2X10 0x2019
+#define MEDIA_BUS_FMT_YUYV10_2X10 0x200b
+#define MEDIA_BUS_FMT_YVYU10_2X10 0x200c
+#define MEDIA_BUS_FMT_Y12_1X12 0x2013
+#define MEDIA_BUS_FMT_UYVY8_1X16 0x200f
+#define MEDIA_BUS_FMT_VYUY8_1X16 0x2010
+#define MEDIA_BUS_FMT_YUYV8_1X16 0x2011
+#define MEDIA_BUS_FMT_YVYU8_1X16 0x2012
+#define MEDIA_BUS_FMT_YDYUYDYV8_1X16 0x2014
+#define MEDIA_BUS_FMT_UYVY10_1X20 0x201a
+#define MEDIA_BUS_FMT_VYUY10_1X20 0x201b
+#define MEDIA_BUS_FMT_YUYV10_1X20 0x200d
+#define MEDIA_BUS_FMT_YVYU10_1X20 0x200e
+#define MEDIA_BUS_FMT_YUV10_1X30 0x2016
+#define MEDIA_BUS_FMT_AYUV8_1X32 0x2017
+#define MEDIA_BUS_FMT_UYVY12_2X12 0x201c
+#define MEDIA_BUS_FMT_VYUY12_2X12 0x201d
+#define MEDIA_BUS_FMT_YUYV12_2X12 0x201e
+#define MEDIA_BUS_FMT_YVYU12_2X12 0x201f
+#define MEDIA_BUS_FMT_UYVY12_1X24 0x2020
+#define MEDIA_BUS_FMT_VYUY12_1X24 0x2021
+#define MEDIA_BUS_FMT_YUYV12_1X24 0x2022
+#define MEDIA_BUS_FMT_YVYU12_1X24 0x2023
+
+/* Bayer - next is 0x3019 */
+#define MEDIA_BUS_FMT_SBGGR8_1X8 0x3001
+#define MEDIA_BUS_FMT_SGBRG8_1X8 0x3013
+#define MEDIA_BUS_FMT_SGRBG8_1X8 0x3002
+#define MEDIA_BUS_FMT_SRGGB8_1X8 0x3014
+#define MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8 0x3015
+#define MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8 0x3016
+#define MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8 0x3017
+#define MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8 0x3018
+#define MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8 0x300b
+#define MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8 0x300c
+#define MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8 0x3009
+#define MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8 0x300d
+#define MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE 0x3003
+#define MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE 0x3004
+#define MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE 0x3005
+#define MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE 0x3006
+#define MEDIA_BUS_FMT_SBGGR10_1X10 0x3007
+#define MEDIA_BUS_FMT_SGBRG10_1X10 0x300e
+#define MEDIA_BUS_FMT_SGRBG10_1X10 0x300a
+#define MEDIA_BUS_FMT_SRGGB10_1X10 0x300f
+#define MEDIA_BUS_FMT_SBGGR12_1X12 0x3008
+#define MEDIA_BUS_FMT_SGBRG12_1X12 0x3010
+#define MEDIA_BUS_FMT_SGRBG12_1X12 0x3011
+#define MEDIA_BUS_FMT_SRGGB12_1X12 0x3012
+
+/* JPEG compressed formats - next is 0x4002 */
+#define MEDIA_BUS_FMT_JPEG_1X8 0x4001
+
+/* Vendor specific formats - next is 0x5002 */
+
+/* S5C73M3 sensor specific interleaved UYVY and JPEG */
+#define MEDIA_BUS_FMT_S5C_UYVY_JPEG_1X8 0x5001
+
+/* HSV - next is 0x6002 */
+#define MEDIA_BUS_FMT_AHSV8888_1X32 0x6001
+
+#endif /* __LINUX_MEDIA_BUS_FORMAT_H */
diff --git a/include/uapi/linux/nfc.h b/include/uapi/linux/nfc.h
index 9b19b44..8119255 100644
--- a/include/uapi/linux/nfc.h
+++ b/include/uapi/linux/nfc.h
@@ -116,6 +116,7 @@
NFC_EVENT_SE_TRANSACTION,
NFC_CMD_GET_SE,
NFC_CMD_SE_IO,
+ NFC_CMD_ACTIVATE_TARGET,
/* private: internal use only */
__NFC_CMD_AFTER_LAST
};
@@ -196,15 +197,19 @@
};
#define NFC_SDP_ATTR_MAX (__NFC_SDP_ATTR_AFTER_LAST - 1)
-#define NFC_DEVICE_NAME_MAXSIZE 8
-#define NFC_NFCID1_MAXSIZE 10
-#define NFC_NFCID2_MAXSIZE 8
-#define NFC_NFCID3_MAXSIZE 10
-#define NFC_SENSB_RES_MAXSIZE 12
-#define NFC_SENSF_RES_MAXSIZE 18
-#define NFC_GB_MAXSIZE 48
-#define NFC_FIRMWARE_NAME_MAXSIZE 32
-#define NFC_ISO15693_UID_MAXSIZE 8
+#define NFC_DEVICE_NAME_MAXSIZE 8
+#define NFC_NFCID1_MAXSIZE 10
+#define NFC_NFCID2_MAXSIZE 8
+#define NFC_NFCID3_MAXSIZE 10
+#define NFC_SENSB_RES_MAXSIZE 12
+#define NFC_SENSF_RES_MAXSIZE 18
+#define NFC_ATR_REQ_MAXSIZE 64
+#define NFC_ATR_RES_MAXSIZE 64
+#define NFC_ATR_REQ_GB_MAXSIZE 48
+#define NFC_ATR_RES_GB_MAXSIZE 47
+#define NFC_GB_MAXSIZE 48
+#define NFC_FIRMWARE_NAME_MAXSIZE 32
+#define NFC_ISO15693_UID_MAXSIZE 8
/* NFC protocols */
#define NFC_PROTO_JEWEL 1
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 4b28dc0..b37bd5a 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -227,7 +227,11 @@
* the interface identified by %NL80211_ATTR_IFINDEX.
* @NL80211_CMD_DEL_STATION: Remove a station identified by %NL80211_ATTR_MAC
* or, if no MAC address given, all stations, on the interface identified
- * by %NL80211_ATTR_IFINDEX.
+ * by %NL80211_ATTR_IFINDEX. %NL80211_ATTR_MGMT_SUBTYPE and
+ * %NL80211_ATTR_REASON_CODE can optionally be used to specify which type
+ * of disconnection indication should be sent to the station
+ * (Deauthentication or Disassociation frame and reason code for that
+ * frame).
*
* @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to
* destination %NL80211_ATTR_MAC on the interface identified by
@@ -639,7 +643,18 @@
* @NL80211_CMD_CH_SWITCH_NOTIFY: An AP or GO may decide to switch channels
* independently of the userspace SME, send this event indicating
* %NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ and the
- * attributes determining channel width.
+ * attributes determining channel width. This indication may also be
+ * sent when a remotely-initiated switch (e.g., when a STA receives a CSA
+ * from the remote AP) is completed;
+ *
+ * @NL80211_CMD_CH_SWITCH_STARTED_NOTIFY: Notify that a channel switch
+ * has been started on an interface, regardless of the initiator
+ * (ie. whether it was requested from a remote device or
+ * initiated on our own). It indicates that
+ * %NL80211_ATTR_IFINDEX will be on %NL80211_ATTR_WIPHY_FREQ
+ * after %NL80211_ATTR_CH_SWITCH_COUNT TBTT's. The userspace may
+ * decide to react to this indication by requesting other
+ * interfaces to change channel as well.
*
* @NL80211_CMD_START_P2P_DEVICE: Start the given P2P Device, identified by
* its %NL80211_ATTR_WDEV identifier. It must have been created with
@@ -738,6 +753,27 @@
* before removing a station entry entirely, or before disassociating
* or similar, cleanup will happen in the driver/device in this case.
*
+ * @NL80211_CMD_GET_MPP: Get mesh path attributes for mesh proxy path to
+ * destination %NL80211_ATTR_MAC on the interface identified by
+ * %NL80211_ATTR_IFINDEX.
+ *
+ * @NL80211_CMD_JOIN_OCB: Join the OCB network. The center frequency and
+ * bandwidth of a channel must be given.
+ * @NL80211_CMD_LEAVE_OCB: Leave the OCB network -- no special arguments, the
+ * network is determined by the network interface.
+ *
+ * @NL80211_CMD_TDLS_CHANNEL_SWITCH: Start channel-switching with a TDLS peer,
+ * identified by the %NL80211_ATTR_MAC parameter. A target channel is
+ * provided via %NL80211_ATTR_WIPHY_FREQ and other attributes determining
+ * channel width/type. The target operating class is given via
+ * %NL80211_ATTR_OPER_CLASS.
+ * The driver is responsible for continually initiating channel-switching
+ * operations and returning to the base channel for communication with the
+ * AP.
+ * @NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH: Stop channel-switching with a TDLS
+ * peer given by %NL80211_ATTR_MAC. Both peers must be on the base channel
+ * when this command completes.
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -912,6 +948,16 @@
NL80211_CMD_ADD_TX_TS,
NL80211_CMD_DEL_TX_TS,
+ NL80211_CMD_GET_MPP,
+
+ NL80211_CMD_JOIN_OCB,
+ NL80211_CMD_LEAVE_OCB,
+
+ NL80211_CMD_CH_SWITCH_STARTED_NOTIFY,
+
+ NL80211_CMD_TDLS_CHANNEL_SWITCH,
+ NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -1606,9 +1652,9 @@
* @NL80211_ATTR_TDLS_PEER_CAPABILITY: flags for TDLS peer capabilities, u32.
* As specified in the &enum nl80211_tdls_peer_capability.
*
- * @NL80211_ATTR_IFACE_SOCKET_OWNER: flag attribute, if set during interface
+ * @NL80211_ATTR_SOCKET_OWNER: Flag attribute, if set during interface
* creation then the new interface will be owned by the netlink socket
- * that created it and will be destroyed when the socket is closed
+ * that created it and will be destroyed when the socket is closed.
*
* @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is
* the TDLS link initiator.
@@ -1638,6 +1684,11 @@
* @NL80211_ATTR_SMPS_MODE: SMPS mode to use (ap mode). see
* &enum nl80211_smps_mode.
*
+ * @NL80211_ATTR_OPER_CLASS: operating class
+ *
+ * @NL80211_ATTR_MAC_MASK: MAC address mask
+ *
+ * @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
@@ -1973,7 +2024,7 @@
NL80211_ATTR_TDLS_PEER_CAPABILITY,
- NL80211_ATTR_IFACE_SOCKET_OWNER,
+ NL80211_ATTR_SOCKET_OWNER,
NL80211_ATTR_CSA_C_OFFSETS_TX,
NL80211_ATTR_MAX_CSA_COUNTERS,
@@ -1990,15 +2041,21 @@
NL80211_ATTR_SMPS_MODE,
+ NL80211_ATTR_OPER_CLASS,
+
+ NL80211_ATTR_MAC_MASK,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
+ NUM_NL80211_ATTR = __NL80211_ATTR_AFTER_LAST,
NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1
};
/* source-level API compatibility */
#define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION
#define NL80211_ATTR_MESH_PARAMS NL80211_ATTR_MESH_CONFIG
+#define NL80211_ATTR_IFACE_SOCKET_OWNER NL80211_ATTR_SOCKET_OWNER
/*
* Allow user space programs to use #ifdef on new attributes by defining them
@@ -2064,6 +2121,8 @@
* and therefore can't be created in the normal ways, use the
* %NL80211_CMD_START_P2P_DEVICE and %NL80211_CMD_STOP_P2P_DEVICE
* commands to create and destroy one
+ * @NL80211_IF_TYPE_OCB: Outside Context of a BSS
+ * This mode corresponds to the MIB variable dot11OCBActivated=true
* @NL80211_IFTYPE_MAX: highest interface type number currently defined
* @NUM_NL80211_IFTYPES: number of defined interface types
*
@@ -2083,6 +2142,7 @@
NL80211_IFTYPE_P2P_CLIENT,
NL80211_IFTYPE_P2P_GO,
NL80211_IFTYPE_P2P_DEVICE,
+ NL80211_IFTYPE_OCB,
/* keep last */
NUM_NL80211_IFTYPES,
@@ -2631,6 +2691,11 @@
* @NL80211_RRF_AUTO_BW: maximum available bandwidth should be calculated
* base on contiguous rules and wider channels will be allowed to cross
* multiple contiguous/overlapping frequency ranges.
+ * @NL80211_RRF_GO_CONCURRENT: See &NL80211_FREQUENCY_ATTR_GO_CONCURRENT
+ * @NL80211_RRF_NO_HT40MINUS: channels can't be used in HT40- operation
+ * @NL80211_RRF_NO_HT40PLUS: channels can't be used in HT40+ operation
+ * @NL80211_RRF_NO_80MHZ: 80MHz operation not allowed
+ * @NL80211_RRF_NO_160MHZ: 160MHz operation not allowed
*/
enum nl80211_reg_rule_flags {
NL80211_RRF_NO_OFDM = 1<<0,
@@ -2643,11 +2708,18 @@
NL80211_RRF_NO_IR = 1<<7,
__NL80211_RRF_NO_IBSS = 1<<8,
NL80211_RRF_AUTO_BW = 1<<11,
+ NL80211_RRF_GO_CONCURRENT = 1<<12,
+ NL80211_RRF_NO_HT40MINUS = 1<<13,
+ NL80211_RRF_NO_HT40PLUS = 1<<14,
+ NL80211_RRF_NO_80MHZ = 1<<15,
+ NL80211_RRF_NO_160MHZ = 1<<16,
};
#define NL80211_RRF_PASSIVE_SCAN NL80211_RRF_NO_IR
#define NL80211_RRF_NO_IBSS NL80211_RRF_NO_IR
#define NL80211_RRF_NO_IR NL80211_RRF_NO_IR
+#define NL80211_RRF_NO_HT40 (NL80211_RRF_NO_HT40MINUS |\
+ NL80211_RRF_NO_HT40PLUS)
/* For backport compatibility with older userspace */
#define NL80211_RRF_NO_IR_ALL (NL80211_RRF_NO_IR | __NL80211_RRF_NO_IBSS)
@@ -3379,6 +3451,8 @@
* interval in which %NL80211_ATTR_CQM_TXE_PKTS and
* %NL80211_ATTR_CQM_TXE_RATE must be satisfied before generating an
* %NL80211_CMD_NOTIFY_CQM. Set to 0 to turn off TX error reporting.
+ * @NL80211_ATTR_CQM_BEACON_LOSS_EVENT: flag attribute that's set in a beacon
+ * loss event
* @__NL80211_ATTR_CQM_AFTER_LAST: internal
* @NL80211_ATTR_CQM_MAX: highest key attribute
*/
@@ -3391,6 +3465,7 @@
NL80211_ATTR_CQM_TXE_RATE,
NL80211_ATTR_CQM_TXE_PKTS,
NL80211_ATTR_CQM_TXE_INTVL,
+ NL80211_ATTR_CQM_BEACON_LOSS_EVENT,
/* keep last */
__NL80211_ATTR_CQM_AFTER_LAST,
@@ -3403,9 +3478,7 @@
* configured threshold
* @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH: The RSSI is higher than the
* configured threshold
- * @NL80211_CQM_RSSI_BEACON_LOSS_EVENT: The device experienced beacon loss.
- * (Note that deauth/disassoc will still follow if the AP is not
- * available. This event might get used as roaming event, etc.)
+ * @NL80211_CQM_RSSI_BEACON_LOSS_EVENT: (reserved, never sent)
*/
enum nl80211_cqm_rssi_threshold_event {
NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
@@ -3545,6 +3618,25 @@
* @NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS: For wakeup reporting only,
* the TCP connection ran out of tokens to use for data to send to the
* service
+ * @NL80211_WOWLAN_TRIG_NET_DETECT: wake up when a configured network
+ * is detected. This is a nested attribute that contains the
+ * same attributes used with @NL80211_CMD_START_SCHED_SCAN. It
+ * specifies how the scan is performed (e.g. the interval and the
+ * channels to scan) as well as the scan results that will
+ * trigger a wake (i.e. the matchsets).
+ * @NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS: nested attribute
+ * containing an array with information about what triggered the
+ * wake up. If no elements are present in the array, it means
+ * that the information is not available. If more than one
+ * element is present, it means that more than one match
+ * occurred.
+ * Each element in the array is a nested attribute that contains
+ * one optional %NL80211_ATTR_SSID attribute and one optional
+ * %NL80211_ATTR_SCAN_FREQUENCIES attribute. At least one of
+ * these attributes must be present. If
+ * %NL80211_ATTR_SCAN_FREQUENCIES contains more than one
+ * frequency, it means that the match occurred in more than one
+ * channel.
* @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers
* @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number
*
@@ -3570,6 +3662,8 @@
NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH,
NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST,
NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS,
+ NL80211_WOWLAN_TRIG_NET_DETECT,
+ NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS,
/* keep last */
NUM_NL80211_WOWLAN_TRIG,
@@ -4042,6 +4136,27 @@
* multiplexing powersave, ie. can turn off all but one chain
* and then wake the rest up as required after, for example,
* rts/cts handshake.
+ * @NL80211_FEATURE_SUPPORTS_WMM_ADMISSION: the device supports setting up WMM
+ * TSPEC sessions (TID aka TSID 0-7) with the %NL80211_CMD_ADD_TX_TS
+ * command. Standard IEEE 802.11 TSPEC setup is not yet supported, it
+ * needs to be able to handle Block-Ack agreements and other things.
+ * @NL80211_FEATURE_MAC_ON_CREATE: Device supports configuring
+ * the vif's MAC address upon creation.
+ * See 'macaddr' field in the vif_params (cfg80211.h).
+ * @NL80211_FEATURE_TDLS_CHANNEL_SWITCH: Driver supports channel switching when
+ * operating as a TDLS peer.
+ * @NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR: This device/driver supports using a
+ * random MAC address during scan (if the device is unassociated); the
+ * %NL80211_SCAN_FLAG_RANDOM_ADDR flag may be set for scans and the MAC
+ * address mask/value will be used.
+ * @NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR: This device/driver supports
+ * using a random MAC address for every scan iteration during scheduled
+ * scan (while not associated), the %NL80211_SCAN_FLAG_RANDOM_ADDR may
+ * be set for scheduled scan and the MAC address mask/value will be used.
+ * @NL80211_FEATURE_ND_RANDOM_MAC_ADDR: This device/driver supports using a
+ * random MAC address for every scan iteration during "net detect", i.e.
+ * scan in unassociated WoWLAN, the %NL80211_SCAN_FLAG_RANDOM_ADDR may
+ * be set for scheduled scan and the MAC address mask/value will be used.
*/
enum nl80211_feature_flags {
NL80211_FEATURE_SK_TX_STATUS = 1 << 0,
@@ -4070,6 +4185,12 @@
NL80211_FEATURE_ACKTO_ESTIMATION = 1 << 23,
NL80211_FEATURE_STATIC_SMPS = 1 << 24,
NL80211_FEATURE_DYNAMIC_SMPS = 1 << 25,
+ NL80211_FEATURE_SUPPORTS_WMM_ADMISSION = 1 << 26,
+ NL80211_FEATURE_MAC_ON_CREATE = 1 << 27,
+ NL80211_FEATURE_TDLS_CHANNEL_SWITCH = 1 << 28,
+ NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR = 1 << 29,
+ NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR = 1 << 30,
+ NL80211_FEATURE_ND_RANDOM_MAC_ADDR = 1 << 31,
};
/**
@@ -4118,11 +4239,21 @@
* dangerous because will destroy stations performance as a lot of frames
* will be lost while scanning off-channel, therefore it must be used only
* when really needed
+ * @NL80211_SCAN_FLAG_RANDOM_ADDR: use a random MAC address for this scan (or
+ * for scheduled scan: a different one for every scan iteration). When the
+ * flag is set, depending on device capabilities the @NL80211_ATTR_MAC and
+ * @NL80211_ATTR_MAC_MASK attributes may also be given in which case only
+ * the masked bits will be preserved from the MAC address and the remainder
+ * randomised. If the attributes are not given full randomisation (46 bits,
+ * locally administered 1, multicast 0) is assumed.
+ * This flag must not be requested when the feature isn't supported, check
+ * the nl80211 feature flags for the device.
*/
enum nl80211_scan_flags {
NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0,
NL80211_SCAN_FLAG_FLUSH = 1<<1,
NL80211_SCAN_FLAG_AP = 1<<2,
+ NL80211_SCAN_FLAG_RANDOM_ADDR = 1<<3,
};
/**
diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h
new file mode 100644
index 0000000..4a1d0cc
--- /dev/null
+++ b/include/uapi/linux/pci_regs.h
@@ -0,0 +1,903 @@
+/*
+ * pci_regs.h
+ *
+ * PCI standard defines
+ * Copyright 1994, Drew Eckhardt
+ * Copyright 1997--1999 Martin Mares <mj@ucw.cz>
+ *
+ * For more information, please consult the following manuals (look at
+ * http://www.pcisig.com/ for how to get them):
+ *
+ * PCI BIOS Specification
+ * PCI Local Bus Specification
+ * PCI to PCI Bridge Specification
+ * PCI System Design Guide
+ *
+ * For HyperTransport information, please consult the following manuals
+ * from http://www.hypertransport.org
+ *
+ * The HyperTransport I/O Link Specification
+ */
+
+#ifndef LINUX_PCI_REGS_H
+#define LINUX_PCI_REGS_H
+
+/*
+ * Under PCI, each device has 256 bytes of configuration address space,
+ * of which the first 64 bytes are standardized as follows:
+ */
+#define PCI_STD_HEADER_SIZEOF 64
+#define PCI_VENDOR_ID 0x00 /* 16 bits */
+#define PCI_DEVICE_ID 0x02 /* 16 bits */
+#define PCI_COMMAND 0x04 /* 16 bits */
+#define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */
+#define PCI_COMMAND_MEMORY 0x2 /* Enable response in Memory space */
+#define PCI_COMMAND_MASTER 0x4 /* Enable bus mastering */
+#define PCI_COMMAND_SPECIAL 0x8 /* Enable response to special cycles */
+#define PCI_COMMAND_INVALIDATE 0x10 /* Use memory write and invalidate */
+#define PCI_COMMAND_VGA_PALETTE 0x20 /* Enable palette snooping */
+#define PCI_COMMAND_PARITY 0x40 /* Enable parity checking */
+#define PCI_COMMAND_WAIT 0x80 /* Enable address/data stepping */
+#define PCI_COMMAND_SERR 0x100 /* Enable SERR */
+#define PCI_COMMAND_FAST_BACK 0x200 /* Enable back-to-back writes */
+#define PCI_COMMAND_INTX_DISABLE 0x400 /* INTx Emulation Disable */
+
+#define PCI_STATUS 0x06 /* 16 bits */
+#define PCI_STATUS_INTERRUPT 0x08 /* Interrupt status */
+#define PCI_STATUS_CAP_LIST 0x10 /* Support Capability List */
+#define PCI_STATUS_66MHZ 0x20 /* Support 66 MHz PCI 2.1 bus */
+#define PCI_STATUS_UDF 0x40 /* Support User Definable Features [obsolete] */
+#define PCI_STATUS_FAST_BACK 0x80 /* Accept fast-back to back */
+#define PCI_STATUS_PARITY 0x100 /* Detected parity error */
+#define PCI_STATUS_DEVSEL_MASK 0x600 /* DEVSEL timing */
+#define PCI_STATUS_DEVSEL_FAST 0x000
+#define PCI_STATUS_DEVSEL_MEDIUM 0x200
+#define PCI_STATUS_DEVSEL_SLOW 0x400
+#define PCI_STATUS_SIG_TARGET_ABORT 0x800 /* Set on target abort */
+#define PCI_STATUS_REC_TARGET_ABORT 0x1000 /* Master ack of " */
+#define PCI_STATUS_REC_MASTER_ABORT 0x2000 /* Set on master abort */
+#define PCI_STATUS_SIG_SYSTEM_ERROR 0x4000 /* Set when we drive SERR */
+#define PCI_STATUS_DETECTED_PARITY 0x8000 /* Set on parity error */
+
+#define PCI_CLASS_REVISION 0x08 /* High 24 bits are class, low 8 revision */
+#define PCI_REVISION_ID 0x08 /* Revision ID */
+#define PCI_CLASS_PROG 0x09 /* Reg. Level Programming Interface */
+#define PCI_CLASS_DEVICE 0x0a /* Device class */
+
+#define PCI_CACHE_LINE_SIZE 0x0c /* 8 bits */
+#define PCI_LATENCY_TIMER 0x0d /* 8 bits */
+#define PCI_HEADER_TYPE 0x0e /* 8 bits */
+#define PCI_HEADER_TYPE_NORMAL 0
+#define PCI_HEADER_TYPE_BRIDGE 1
+#define PCI_HEADER_TYPE_CARDBUS 2
+
+#define PCI_BIST 0x0f /* 8 bits */
+#define PCI_BIST_CODE_MASK 0x0f /* Return result */
+#define PCI_BIST_START 0x40 /* 1 to start BIST, 2 secs or less */
+#define PCI_BIST_CAPABLE 0x80 /* 1 if BIST capable */
+
+/*
+ * Base addresses specify locations in memory or I/O space.
+ * Decoded size can be determined by writing a value of
+ * 0xffffffff to the register, and reading it back. Only
+ * 1 bits are decoded.
+ */
+#define PCI_BASE_ADDRESS_0 0x10 /* 32 bits */
+#define PCI_BASE_ADDRESS_1 0x14 /* 32 bits [htype 0,1 only] */
+#define PCI_BASE_ADDRESS_2 0x18 /* 32 bits [htype 0 only] */
+#define PCI_BASE_ADDRESS_3 0x1c /* 32 bits */
+#define PCI_BASE_ADDRESS_4 0x20 /* 32 bits */
+#define PCI_BASE_ADDRESS_5 0x24 /* 32 bits */
+#define PCI_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */
+#define PCI_BASE_ADDRESS_SPACE_IO 0x01
+#define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00
+#define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06
+#define PCI_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */
+#define PCI_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M [obsolete] */
+#define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */
+#define PCI_BASE_ADDRESS_MEM_PREFETCH 0x08 /* prefetchable? */
+#define PCI_BASE_ADDRESS_MEM_MASK (~0x0fUL)
+#define PCI_BASE_ADDRESS_IO_MASK (~0x03UL)
+/* bit 1 is reserved if address_space = 1 */
+
+/* Header type 0 (normal devices) */
+#define PCI_CARDBUS_CIS 0x28
+#define PCI_SUBSYSTEM_VENDOR_ID 0x2c
+#define PCI_SUBSYSTEM_ID 0x2e
+#define PCI_ROM_ADDRESS 0x30 /* Bits 31..11 are address, 10..1 reserved */
+#define PCI_ROM_ADDRESS_ENABLE 0x01
+#define PCI_ROM_ADDRESS_MASK (~0x7ffUL)
+
+#define PCI_CAPABILITY_LIST 0x34 /* Offset of first capability list entry */
+
+/* 0x35-0x3b are reserved */
+#define PCI_INTERRUPT_LINE 0x3c /* 8 bits */
+#define PCI_INTERRUPT_PIN 0x3d /* 8 bits */
+#define PCI_MIN_GNT 0x3e /* 8 bits */
+#define PCI_MAX_LAT 0x3f /* 8 bits */
+
+/* Header type 1 (PCI-to-PCI bridges) */
+#define PCI_PRIMARY_BUS 0x18 /* Primary bus number */
+#define PCI_SECONDARY_BUS 0x19 /* Secondary bus number */
+#define PCI_SUBORDINATE_BUS 0x1a /* Highest bus number behind the bridge */
+#define PCI_SEC_LATENCY_TIMER 0x1b /* Latency timer for secondary interface */
+#define PCI_IO_BASE 0x1c /* I/O range behind the bridge */
+#define PCI_IO_LIMIT 0x1d
+#define PCI_IO_RANGE_TYPE_MASK 0x0fUL /* I/O bridging type */
+#define PCI_IO_RANGE_TYPE_16 0x00
+#define PCI_IO_RANGE_TYPE_32 0x01
+#define PCI_IO_RANGE_MASK (~0x0fUL) /* Standard 4K I/O windows */
+#define PCI_IO_1K_RANGE_MASK (~0x03UL) /* Intel 1K I/O windows */
+#define PCI_SEC_STATUS 0x1e /* Secondary status register, only bit 14 used */
+#define PCI_MEMORY_BASE 0x20 /* Memory range behind */
+#define PCI_MEMORY_LIMIT 0x22
+#define PCI_MEMORY_RANGE_TYPE_MASK 0x0fUL
+#define PCI_MEMORY_RANGE_MASK (~0x0fUL)
+#define PCI_PREF_MEMORY_BASE 0x24 /* Prefetchable memory range behind */
+#define PCI_PREF_MEMORY_LIMIT 0x26
+#define PCI_PREF_RANGE_TYPE_MASK 0x0fUL
+#define PCI_PREF_RANGE_TYPE_32 0x00
+#define PCI_PREF_RANGE_TYPE_64 0x01
+#define PCI_PREF_RANGE_MASK (~0x0fUL)
+#define PCI_PREF_BASE_UPPER32 0x28 /* Upper half of prefetchable memory range */
+#define PCI_PREF_LIMIT_UPPER32 0x2c
+#define PCI_IO_BASE_UPPER16 0x30 /* Upper half of I/O addresses */
+#define PCI_IO_LIMIT_UPPER16 0x32
+/* 0x34 same as for htype 0 */
+/* 0x35-0x3b is reserved */
+#define PCI_ROM_ADDRESS1 0x38 /* Same as PCI_ROM_ADDRESS, but for htype 1 */
+/* 0x3c-0x3d are same as for htype 0 */
+#define PCI_BRIDGE_CONTROL 0x3e
+#define PCI_BRIDGE_CTL_PARITY 0x01 /* Enable parity detection on secondary interface */
+#define PCI_BRIDGE_CTL_SERR 0x02 /* The same for SERR forwarding */
+#define PCI_BRIDGE_CTL_ISA 0x04 /* Enable ISA mode */
+#define PCI_BRIDGE_CTL_VGA 0x08 /* Forward VGA addresses */
+#define PCI_BRIDGE_CTL_MASTER_ABORT 0x20 /* Report master aborts */
+#define PCI_BRIDGE_CTL_BUS_RESET 0x40 /* Secondary bus reset */
+#define PCI_BRIDGE_CTL_FAST_BACK 0x80 /* Fast Back2Back enabled on secondary interface */
+
+/* Header type 2 (CardBus bridges) */
+#define PCI_CB_CAPABILITY_LIST 0x14
+/* 0x15 reserved */
+#define PCI_CB_SEC_STATUS 0x16 /* Secondary status */
+#define PCI_CB_PRIMARY_BUS 0x18 /* PCI bus number */
+#define PCI_CB_CARD_BUS 0x19 /* CardBus bus number */
+#define PCI_CB_SUBORDINATE_BUS 0x1a /* Subordinate bus number */
+#define PCI_CB_LATENCY_TIMER 0x1b /* CardBus latency timer */
+#define PCI_CB_MEMORY_BASE_0 0x1c
+#define PCI_CB_MEMORY_LIMIT_0 0x20
+#define PCI_CB_MEMORY_BASE_1 0x24
+#define PCI_CB_MEMORY_LIMIT_1 0x28
+#define PCI_CB_IO_BASE_0 0x2c
+#define PCI_CB_IO_BASE_0_HI 0x2e
+#define PCI_CB_IO_LIMIT_0 0x30
+#define PCI_CB_IO_LIMIT_0_HI 0x32
+#define PCI_CB_IO_BASE_1 0x34
+#define PCI_CB_IO_BASE_1_HI 0x36
+#define PCI_CB_IO_LIMIT_1 0x38
+#define PCI_CB_IO_LIMIT_1_HI 0x3a
+#define PCI_CB_IO_RANGE_MASK (~0x03UL)
+/* 0x3c-0x3d are same as for htype 0 */
+#define PCI_CB_BRIDGE_CONTROL 0x3e
+#define PCI_CB_BRIDGE_CTL_PARITY 0x01 /* Similar to standard bridge control register */
+#define PCI_CB_BRIDGE_CTL_SERR 0x02
+#define PCI_CB_BRIDGE_CTL_ISA 0x04
+#define PCI_CB_BRIDGE_CTL_VGA 0x08
+#define PCI_CB_BRIDGE_CTL_MASTER_ABORT 0x20
+#define PCI_CB_BRIDGE_CTL_CB_RESET 0x40 /* CardBus reset */
+#define PCI_CB_BRIDGE_CTL_16BIT_INT 0x80 /* Enable interrupt for 16-bit cards */
+#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM0 0x100 /* Prefetch enable for both memory regions */
+#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM1 0x200
+#define PCI_CB_BRIDGE_CTL_POST_WRITES 0x400
+#define PCI_CB_SUBSYSTEM_VENDOR_ID 0x40
+#define PCI_CB_SUBSYSTEM_ID 0x42
+#define PCI_CB_LEGACY_MODE_BASE 0x44 /* 16-bit PC Card legacy mode base address (ExCa) */
+/* 0x48-0x7f reserved */
+
+/* Capability lists */
+
+#define PCI_CAP_LIST_ID 0 /* Capability ID */
+#define PCI_CAP_ID_PM 0x01 /* Power Management */
+#define PCI_CAP_ID_AGP 0x02 /* Accelerated Graphics Port */
+#define PCI_CAP_ID_VPD 0x03 /* Vital Product Data */
+#define PCI_CAP_ID_SLOTID 0x04 /* Slot Identification */
+#define PCI_CAP_ID_MSI 0x05 /* Message Signalled Interrupts */
+#define PCI_CAP_ID_CHSWP 0x06 /* CompactPCI HotSwap */
+#define PCI_CAP_ID_PCIX 0x07 /* PCI-X */
+#define PCI_CAP_ID_HT 0x08 /* HyperTransport */
+#define PCI_CAP_ID_VNDR 0x09 /* Vendor-Specific */
+#define PCI_CAP_ID_DBG 0x0A /* Debug port */
+#define PCI_CAP_ID_CCRC 0x0B /* CompactPCI Central Resource Control */
+#define PCI_CAP_ID_SHPC 0x0C /* PCI Standard Hot-Plug Controller */
+#define PCI_CAP_ID_SSVID 0x0D /* Bridge subsystem vendor/device ID */
+#define PCI_CAP_ID_AGP3 0x0E /* AGP Target PCI-PCI bridge */
+#define PCI_CAP_ID_SECDEV 0x0F /* Secure Device */
+#define PCI_CAP_ID_EXP 0x10 /* PCI Express */
+#define PCI_CAP_ID_MSIX 0x11 /* MSI-X */
+#define PCI_CAP_ID_SATA 0x12 /* SATA Data/Index Conf. */
+#define PCI_CAP_ID_AF 0x13 /* PCI Advanced Features */
+#define PCI_CAP_ID_MAX PCI_CAP_ID_AF
+#define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */
+#define PCI_CAP_FLAGS 2 /* Capability defined flags (16 bits) */
+#define PCI_CAP_SIZEOF 4
+
+/* Power Management Registers */
+
+#define PCI_PM_PMC 2 /* PM Capabilities Register */
+#define PCI_PM_CAP_VER_MASK 0x0007 /* Version */
+#define PCI_PM_CAP_PME_CLOCK 0x0008 /* PME clock required */
+#define PCI_PM_CAP_RESERVED 0x0010 /* Reserved field */
+#define PCI_PM_CAP_DSI 0x0020 /* Device specific initialization */
+#define PCI_PM_CAP_AUX_POWER 0x01C0 /* Auxiliary power support mask */
+#define PCI_PM_CAP_D1 0x0200 /* D1 power state support */
+#define PCI_PM_CAP_D2 0x0400 /* D2 power state support */
+#define PCI_PM_CAP_PME 0x0800 /* PME pin supported */
+#define PCI_PM_CAP_PME_MASK 0xF800 /* PME Mask of all supported states */
+#define PCI_PM_CAP_PME_D0 0x0800 /* PME# from D0 */
+#define PCI_PM_CAP_PME_D1 0x1000 /* PME# from D1 */
+#define PCI_PM_CAP_PME_D2 0x2000 /* PME# from D2 */
+#define PCI_PM_CAP_PME_D3 0x4000 /* PME# from D3 (hot) */
+#define PCI_PM_CAP_PME_D3cold 0x8000 /* PME# from D3 (cold) */
+#define PCI_PM_CAP_PME_SHIFT 11 /* Start of the PME Mask in PMC */
+#define PCI_PM_CTRL 4 /* PM control and status register */
+#define PCI_PM_CTRL_STATE_MASK 0x0003 /* Current power state (D0 to D3) */
+#define PCI_PM_CTRL_NO_SOFT_RESET 0x0008 /* No reset for D3hot->D0 */
+#define PCI_PM_CTRL_PME_ENABLE 0x0100 /* PME pin enable */
+#define PCI_PM_CTRL_DATA_SEL_MASK 0x1e00 /* Data select (??) */
+#define PCI_PM_CTRL_DATA_SCALE_MASK 0x6000 /* Data scale (??) */
+#define PCI_PM_CTRL_PME_STATUS 0x8000 /* PME pin status */
+#define PCI_PM_PPB_EXTENSIONS 6 /* PPB support extensions (??) */
+#define PCI_PM_PPB_B2_B3 0x40 /* Stop clock when in D3hot (??) */
+#define PCI_PM_BPCC_ENABLE 0x80 /* Bus power/clock control enable (??) */
+#define PCI_PM_DATA_REGISTER 7 /* (??) */
+#define PCI_PM_SIZEOF 8
+
+/* AGP registers */
+
+#define PCI_AGP_VERSION 2 /* BCD version number */
+#define PCI_AGP_RFU 3 /* Rest of capability flags */
+#define PCI_AGP_STATUS 4 /* Status register */
+#define PCI_AGP_STATUS_RQ_MASK 0xff000000 /* Maximum number of requests - 1 */
+#define PCI_AGP_STATUS_SBA 0x0200 /* Sideband addressing supported */
+#define PCI_AGP_STATUS_64BIT 0x0020 /* 64-bit addressing supported */
+#define PCI_AGP_STATUS_FW 0x0010 /* FW transfers supported */
+#define PCI_AGP_STATUS_RATE4 0x0004 /* 4x transfer rate supported */
+#define PCI_AGP_STATUS_RATE2 0x0002 /* 2x transfer rate supported */
+#define PCI_AGP_STATUS_RATE1 0x0001 /* 1x transfer rate supported */
+#define PCI_AGP_COMMAND 8 /* Control register */
+#define PCI_AGP_COMMAND_RQ_MASK 0xff000000 /* Master: Maximum number of requests */
+#define PCI_AGP_COMMAND_SBA 0x0200 /* Sideband addressing enabled */
+#define PCI_AGP_COMMAND_AGP 0x0100 /* Allow processing of AGP transactions */
+#define PCI_AGP_COMMAND_64BIT 0x0020 /* Allow processing of 64-bit addresses */
+#define PCI_AGP_COMMAND_FW 0x0010 /* Force FW transfers */
+#define PCI_AGP_COMMAND_RATE4 0x0004 /* Use 4x rate */
+#define PCI_AGP_COMMAND_RATE2 0x0002 /* Use 2x rate */
+#define PCI_AGP_COMMAND_RATE1 0x0001 /* Use 1x rate */
+#define PCI_AGP_SIZEOF 12
+
+/* Vital Product Data */
+
+#define PCI_VPD_ADDR 2 /* Address to access (15 bits!) */
+#define PCI_VPD_ADDR_MASK 0x7fff /* Address mask */
+#define PCI_VPD_ADDR_F 0x8000 /* Write 0, 1 indicates completion */
+#define PCI_VPD_DATA 4 /* 32-bits of data returned here */
+#define PCI_CAP_VPD_SIZEOF 8
+
+/* Slot Identification */
+
+#define PCI_SID_ESR 2 /* Expansion Slot Register */
+#define PCI_SID_ESR_NSLOTS 0x1f /* Number of expansion slots available */
+#define PCI_SID_ESR_FIC 0x20 /* First In Chassis Flag */
+#define PCI_SID_CHASSIS_NR 3 /* Chassis Number */
+
+/* Message Signalled Interrupts registers */
+
+#define PCI_MSI_FLAGS 2 /* Message Control */
+#define PCI_MSI_FLAGS_ENABLE 0x0001 /* MSI feature enabled */
+#define PCI_MSI_FLAGS_QMASK 0x000e /* Maximum queue size available */
+#define PCI_MSI_FLAGS_QSIZE 0x0070 /* Message queue size configured */
+#define PCI_MSI_FLAGS_64BIT 0x0080 /* 64-bit addresses allowed */
+#define PCI_MSI_FLAGS_MASKBIT 0x0100 /* Per-vector masking capable */
+#define PCI_MSI_RFU 3 /* Rest of capability flags */
+#define PCI_MSI_ADDRESS_LO 4 /* Lower 32 bits */
+#define PCI_MSI_ADDRESS_HI 8 /* Upper 32 bits (if PCI_MSI_FLAGS_64BIT set) */
+#define PCI_MSI_DATA_32 8 /* 16 bits of data for 32-bit devices */
+#define PCI_MSI_MASK_32 12 /* Mask bits register for 32-bit devices */
+#define PCI_MSI_PENDING_32 16 /* Pending intrs for 32-bit devices */
+#define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */
+#define PCI_MSI_MASK_64 16 /* Mask bits register for 64-bit devices */
+#define PCI_MSI_PENDING_64 20 /* Pending intrs for 64-bit devices */
+
+/* MSI-X registers */
+#define PCI_MSIX_FLAGS 2 /* Message Control */
+#define PCI_MSIX_FLAGS_QSIZE 0x07FF /* Table size */
+#define PCI_MSIX_FLAGS_MASKALL 0x4000 /* Mask all vectors for this function */
+#define PCI_MSIX_FLAGS_ENABLE 0x8000 /* MSI-X enable */
+#define PCI_MSIX_TABLE 4 /* Table offset */
+#define PCI_MSIX_TABLE_BIR 0x00000007 /* BAR index */
+#define PCI_MSIX_TABLE_OFFSET 0xfffffff8 /* Offset into specified BAR */
+#define PCI_MSIX_PBA 8 /* Pending Bit Array offset */
+#define PCI_MSIX_PBA_BIR 0x00000007 /* BAR index */
+#define PCI_MSIX_PBA_OFFSET 0xfffffff8 /* Offset into specified BAR */
+#define PCI_CAP_MSIX_SIZEOF 12 /* size of MSIX registers */
+
+/* MSI-X Table entry format */
+#define PCI_MSIX_ENTRY_SIZE 16
+#define PCI_MSIX_ENTRY_LOWER_ADDR 0
+#define PCI_MSIX_ENTRY_UPPER_ADDR 4
+#define PCI_MSIX_ENTRY_DATA 8
+#define PCI_MSIX_ENTRY_VECTOR_CTRL 12
+#define PCI_MSIX_ENTRY_CTRL_MASKBIT 1
+
+/* CompactPCI Hotswap Register */
+
+#define PCI_CHSWP_CSR 2 /* Control and Status Register */
+#define PCI_CHSWP_DHA 0x01 /* Device Hiding Arm */
+#define PCI_CHSWP_EIM 0x02 /* ENUM# Signal Mask */
+#define PCI_CHSWP_PIE 0x04 /* Pending Insert or Extract */
+#define PCI_CHSWP_LOO 0x08 /* LED On / Off */
+#define PCI_CHSWP_PI 0x30 /* Programming Interface */
+#define PCI_CHSWP_EXT 0x40 /* ENUM# status - extraction */
+#define PCI_CHSWP_INS 0x80 /* ENUM# status - insertion */
+
+/* PCI Advanced Feature registers */
+
+#define PCI_AF_LENGTH 2
+#define PCI_AF_CAP 3
+#define PCI_AF_CAP_TP 0x01
+#define PCI_AF_CAP_FLR 0x02
+#define PCI_AF_CTRL 4
+#define PCI_AF_CTRL_FLR 0x01
+#define PCI_AF_STATUS 5
+#define PCI_AF_STATUS_TP 0x01
+#define PCI_CAP_AF_SIZEOF 6 /* size of AF registers */
+
+/* PCI-X registers (Type 0 (non-bridge) devices) */
+
+#define PCI_X_CMD 2 /* Modes & Features */
+#define PCI_X_CMD_DPERR_E 0x0001 /* Data Parity Error Recovery Enable */
+#define PCI_X_CMD_ERO 0x0002 /* Enable Relaxed Ordering */
+#define PCI_X_CMD_READ_512 0x0000 /* 512 byte maximum read byte count */
+#define PCI_X_CMD_READ_1K 0x0004 /* 1Kbyte maximum read byte count */
+#define PCI_X_CMD_READ_2K 0x0008 /* 2Kbyte maximum read byte count */
+#define PCI_X_CMD_READ_4K 0x000c /* 4Kbyte maximum read byte count */
+#define PCI_X_CMD_MAX_READ 0x000c /* Max Memory Read Byte Count */
+ /* Max # of outstanding split transactions */
+#define PCI_X_CMD_SPLIT_1 0x0000 /* Max 1 */
+#define PCI_X_CMD_SPLIT_2 0x0010 /* Max 2 */
+#define PCI_X_CMD_SPLIT_3 0x0020 /* Max 3 */
+#define PCI_X_CMD_SPLIT_4 0x0030 /* Max 4 */
+#define PCI_X_CMD_SPLIT_8 0x0040 /* Max 8 */
+#define PCI_X_CMD_SPLIT_12 0x0050 /* Max 12 */
+#define PCI_X_CMD_SPLIT_16 0x0060 /* Max 16 */
+#define PCI_X_CMD_SPLIT_32 0x0070 /* Max 32 */
+#define PCI_X_CMD_MAX_SPLIT 0x0070 /* Max Outstanding Split Transactions */
+#define PCI_X_CMD_VERSION(x) (((x) >> 12) & 3) /* Version */
+#define PCI_X_STATUS 4 /* PCI-X capabilities */
+#define PCI_X_STATUS_DEVFN 0x000000ff /* A copy of devfn */
+#define PCI_X_STATUS_BUS 0x0000ff00 /* A copy of bus nr */
+#define PCI_X_STATUS_64BIT 0x00010000 /* 64-bit device */
+#define PCI_X_STATUS_133MHZ 0x00020000 /* 133 MHz capable */
+#define PCI_X_STATUS_SPL_DISC 0x00040000 /* Split Completion Discarded */
+#define PCI_X_STATUS_UNX_SPL 0x00080000 /* Unexpected Split Completion */
+#define PCI_X_STATUS_COMPLEX 0x00100000 /* Device Complexity */
+#define PCI_X_STATUS_MAX_READ 0x00600000 /* Designed Max Memory Read Count */
+#define PCI_X_STATUS_MAX_SPLIT 0x03800000 /* Designed Max Outstanding Split Transactions */
+#define PCI_X_STATUS_MAX_CUM 0x1c000000 /* Designed Max Cumulative Read Size */
+#define PCI_X_STATUS_SPL_ERR 0x20000000 /* Rcvd Split Completion Error Msg */
+#define PCI_X_STATUS_266MHZ 0x40000000 /* 266 MHz capable */
+#define PCI_X_STATUS_533MHZ 0x80000000 /* 533 MHz capable */
+#define PCI_X_ECC_CSR 8 /* ECC control and status */
+#define PCI_CAP_PCIX_SIZEOF_V0 8 /* size of registers for Version 0 */
+#define PCI_CAP_PCIX_SIZEOF_V1 24 /* size for Version 1 */
+#define PCI_CAP_PCIX_SIZEOF_V2 PCI_CAP_PCIX_SIZEOF_V1 /* Same for v2 */
+
+/* PCI-X registers (Type 1 (bridge) devices) */
+
+#define PCI_X_BRIDGE_SSTATUS 2 /* Secondary Status */
+#define PCI_X_SSTATUS_64BIT 0x0001 /* Secondary AD interface is 64 bits */
+#define PCI_X_SSTATUS_133MHZ 0x0002 /* 133 MHz capable */
+#define PCI_X_SSTATUS_FREQ 0x03c0 /* Secondary Bus Mode and Frequency */
+#define PCI_X_SSTATUS_VERS 0x3000 /* PCI-X Capability Version */
+#define PCI_X_SSTATUS_V1 0x1000 /* Mode 2, not Mode 1 */
+#define PCI_X_SSTATUS_V2 0x2000 /* Mode 1 or Modes 1 and 2 */
+#define PCI_X_SSTATUS_266MHZ 0x4000 /* 266 MHz capable */
+#define PCI_X_SSTATUS_533MHZ 0x8000 /* 533 MHz capable */
+#define PCI_X_BRIDGE_STATUS 4 /* Bridge Status */
+
+/* PCI Bridge Subsystem ID registers */
+
+#define PCI_SSVID_VENDOR_ID 4 /* PCI Bridge subsystem vendor ID */
+#define PCI_SSVID_DEVICE_ID 6 /* PCI Bridge subsystem device ID */
+
+/* PCI Express capability registers */
+
+#define PCI_EXP_FLAGS 2 /* Capabilities register */
+#define PCI_EXP_FLAGS_VERS 0x000f /* Capability version */
+#define PCI_EXP_FLAGS_TYPE 0x00f0 /* Device/Port type */
+#define PCI_EXP_TYPE_ENDPOINT 0x0 /* Express Endpoint */
+#define PCI_EXP_TYPE_LEG_END 0x1 /* Legacy Endpoint */
+#define PCI_EXP_TYPE_ROOT_PORT 0x4 /* Root Port */
+#define PCI_EXP_TYPE_UPSTREAM 0x5 /* Upstream Port */
+#define PCI_EXP_TYPE_DOWNSTREAM 0x6 /* Downstream Port */
+#define PCI_EXP_TYPE_PCI_BRIDGE 0x7 /* PCIe to PCI/PCI-X Bridge */
+#define PCI_EXP_TYPE_PCIE_BRIDGE 0x8 /* PCI/PCI-X to PCIe Bridge */
+#define PCI_EXP_TYPE_RC_END 0x9 /* Root Complex Integrated Endpoint */
+#define PCI_EXP_TYPE_RC_EC 0xa /* Root Complex Event Collector */
+#define PCI_EXP_FLAGS_SLOT 0x0100 /* Slot implemented */
+#define PCI_EXP_FLAGS_IRQ 0x3e00 /* Interrupt message number */
+#define PCI_EXP_DEVCAP 4 /* Device capabilities */
+#define PCI_EXP_DEVCAP_PAYLOAD 0x00000007 /* Max_Payload_Size */
+#define PCI_EXP_DEVCAP_PHANTOM 0x00000018 /* Phantom functions */
+#define PCI_EXP_DEVCAP_EXT_TAG 0x00000020 /* Extended tags */
+#define PCI_EXP_DEVCAP_L0S 0x000001c0 /* L0s Acceptable Latency */
+#define PCI_EXP_DEVCAP_L1 0x00000e00 /* L1 Acceptable Latency */
+#define PCI_EXP_DEVCAP_ATN_BUT 0x00001000 /* Attention Button Present */
+#define PCI_EXP_DEVCAP_ATN_IND 0x00002000 /* Attention Indicator Present */
+#define PCI_EXP_DEVCAP_PWR_IND 0x00004000 /* Power Indicator Present */
+#define PCI_EXP_DEVCAP_RBER 0x00008000 /* Role-Based Error Reporting */
+#define PCI_EXP_DEVCAP_PWR_VAL 0x03fc0000 /* Slot Power Limit Value */
+#define PCI_EXP_DEVCAP_PWR_SCL 0x0c000000 /* Slot Power Limit Scale */
+#define PCI_EXP_DEVCAP_FLR 0x10000000 /* Function Level Reset */
+#define PCI_EXP_DEVCTL 8 /* Device Control */
+#define PCI_EXP_DEVCTL_CERE 0x0001 /* Correctable Error Reporting En. */
+#define PCI_EXP_DEVCTL_NFERE 0x0002 /* Non-Fatal Error Reporting Enable */
+#define PCI_EXP_DEVCTL_FERE 0x0004 /* Fatal Error Reporting Enable */
+#define PCI_EXP_DEVCTL_URRE 0x0008 /* Unsupported Request Reporting En. */
+#define PCI_EXP_DEVCTL_RELAX_EN 0x0010 /* Enable relaxed ordering */
+#define PCI_EXP_DEVCTL_PAYLOAD 0x00e0 /* Max_Payload_Size */
+#define PCI_EXP_DEVCTL_EXT_TAG 0x0100 /* Extended Tag Field Enable */
+#define PCI_EXP_DEVCTL_PHANTOM 0x0200 /* Phantom Functions Enable */
+#define PCI_EXP_DEVCTL_AUX_PME 0x0400 /* Auxiliary Power PM Enable */
+#define PCI_EXP_DEVCTL_NOSNOOP_EN 0x0800 /* Enable No Snoop */
+#define PCI_EXP_DEVCTL_READRQ 0x7000 /* Max_Read_Request_Size */
+#define PCI_EXP_DEVCTL_BCR_FLR 0x8000 /* Bridge Configuration Retry / FLR */
+#define PCI_EXP_DEVSTA 10 /* Device Status */
+#define PCI_EXP_DEVSTA_CED 0x0001 /* Correctable Error Detected */
+#define PCI_EXP_DEVSTA_NFED 0x0002 /* Non-Fatal Error Detected */
+#define PCI_EXP_DEVSTA_FED 0x0004 /* Fatal Error Detected */
+#define PCI_EXP_DEVSTA_URD 0x0008 /* Unsupported Request Detected */
+#define PCI_EXP_DEVSTA_AUXPD 0x0010 /* AUX Power Detected */
+#define PCI_EXP_DEVSTA_TRPND 0x0020 /* Transactions Pending */
+#define PCI_EXP_LNKCAP 12 /* Link Capabilities */
+#define PCI_EXP_LNKCAP_SLS 0x0000000f /* Supported Link Speeds */
+#define PCI_EXP_LNKCAP_SLS_2_5GB 0x00000001 /* LNKCAP2 SLS Vector bit 0 */
+#define PCI_EXP_LNKCAP_SLS_5_0GB 0x00000002 /* LNKCAP2 SLS Vector bit 1 */
+#define PCI_EXP_LNKCAP_MLW 0x000003f0 /* Maximum Link Width */
+#define PCI_EXP_LNKCAP_ASPMS 0x00000c00 /* ASPM Support */
+#define PCI_EXP_LNKCAP_L0SEL 0x00007000 /* L0s Exit Latency */
+#define PCI_EXP_LNKCAP_L1EL 0x00038000 /* L1 Exit Latency */
+#define PCI_EXP_LNKCAP_CLKPM 0x00040000 /* Clock Power Management */
+#define PCI_EXP_LNKCAP_SDERC 0x00080000 /* Surprise Down Error Reporting Capable */
+#define PCI_EXP_LNKCAP_DLLLARC 0x00100000 /* Data Link Layer Link Active Reporting Capable */
+#define PCI_EXP_LNKCAP_LBNC 0x00200000 /* Link Bandwidth Notification Capability */
+#define PCI_EXP_LNKCAP_PN 0xff000000 /* Port Number */
+#define PCI_EXP_LNKCTL 16 /* Link Control */
+#define PCI_EXP_LNKCTL_ASPMC 0x0003 /* ASPM Control */
+#define PCI_EXP_LNKCTL_ASPM_L0S 0x0001 /* L0s Enable */
+#define PCI_EXP_LNKCTL_ASPM_L1 0x0002 /* L1 Enable */
+#define PCI_EXP_LNKCTL_RCB 0x0008 /* Read Completion Boundary */
+#define PCI_EXP_LNKCTL_LD 0x0010 /* Link Disable */
+#define PCI_EXP_LNKCTL_RL 0x0020 /* Retrain Link */
+#define PCI_EXP_LNKCTL_CCC 0x0040 /* Common Clock Configuration */
+#define PCI_EXP_LNKCTL_ES 0x0080 /* Extended Synch */
+#define PCI_EXP_LNKCTL_CLKREQ_EN 0x0100 /* Enable clkreq */
+#define PCI_EXP_LNKCTL_HAWD 0x0200 /* Hardware Autonomous Width Disable */
+#define PCI_EXP_LNKCTL_LBMIE 0x0400 /* Link Bandwidth Management Interrupt Enable */
+#define PCI_EXP_LNKCTL_LABIE 0x0800 /* Link Autonomous Bandwidth Interrupt Enable */
+#define PCI_EXP_LNKSTA 18 /* Link Status */
+#define PCI_EXP_LNKSTA_CLS 0x000f /* Current Link Speed */
+#define PCI_EXP_LNKSTA_CLS_2_5GB 0x0001 /* Current Link Speed 2.5GT/s */
+#define PCI_EXP_LNKSTA_CLS_5_0GB 0x0002 /* Current Link Speed 5.0GT/s */
+#define PCI_EXP_LNKSTA_CLS_8_0GB 0x0003 /* Current Link Speed 8.0GT/s */
+#define PCI_EXP_LNKSTA_NLW 0x03f0 /* Negotiated Link Width */
+#define PCI_EXP_LNKSTA_NLW_X1 0x0010 /* Current Link Width x1 */
+#define PCI_EXP_LNKSTA_NLW_X2 0x0020 /* Current Link Width x2 */
+#define PCI_EXP_LNKSTA_NLW_X4 0x0040 /* Current Link Width x4 */
+#define PCI_EXP_LNKSTA_NLW_X8 0x0080 /* Current Link Width x8 */
+#define PCI_EXP_LNKSTA_NLW_SHIFT 4 /* start of NLW mask in link status */
+#define PCI_EXP_LNKSTA_LT 0x0800 /* Link Training */
+#define PCI_EXP_LNKSTA_SLC 0x1000 /* Slot Clock Configuration */
+#define PCI_EXP_LNKSTA_DLLLA 0x2000 /* Data Link Layer Link Active */
+#define PCI_EXP_LNKSTA_LBMS 0x4000 /* Link Bandwidth Management Status */
+#define PCI_EXP_LNKSTA_LABS 0x8000 /* Link Autonomous Bandwidth Status */
+#define PCI_CAP_EXP_ENDPOINT_SIZEOF_V1 20 /* v1 endpoints end here */
+#define PCI_EXP_SLTCAP 20 /* Slot Capabilities */
+#define PCI_EXP_SLTCAP_ABP 0x00000001 /* Attention Button Present */
+#define PCI_EXP_SLTCAP_PCP 0x00000002 /* Power Controller Present */
+#define PCI_EXP_SLTCAP_MRLSP 0x00000004 /* MRL Sensor Present */
+#define PCI_EXP_SLTCAP_AIP 0x00000008 /* Attention Indicator Present */
+#define PCI_EXP_SLTCAP_PIP 0x00000010 /* Power Indicator Present */
+#define PCI_EXP_SLTCAP_HPS 0x00000020 /* Hot-Plug Surprise */
+#define PCI_EXP_SLTCAP_HPC 0x00000040 /* Hot-Plug Capable */
+#define PCI_EXP_SLTCAP_SPLV 0x00007f80 /* Slot Power Limit Value */
+#define PCI_EXP_SLTCAP_SPLS 0x00018000 /* Slot Power Limit Scale */
+#define PCI_EXP_SLTCAP_EIP 0x00020000 /* Electromechanical Interlock Present */
+#define PCI_EXP_SLTCAP_NCCS 0x00040000 /* No Command Completed Support */
+#define PCI_EXP_SLTCAP_PSN 0xfff80000 /* Physical Slot Number */
+#define PCI_EXP_SLTCTL 24 /* Slot Control */
+#define PCI_EXP_SLTCTL_ABPE 0x0001 /* Attention Button Pressed Enable */
+#define PCI_EXP_SLTCTL_PFDE 0x0002 /* Power Fault Detected Enable */
+#define PCI_EXP_SLTCTL_MRLSCE 0x0004 /* MRL Sensor Changed Enable */
+#define PCI_EXP_SLTCTL_PDCE 0x0008 /* Presence Detect Changed Enable */
+#define PCI_EXP_SLTCTL_CCIE 0x0010 /* Command Completed Interrupt Enable */
+#define PCI_EXP_SLTCTL_HPIE 0x0020 /* Hot-Plug Interrupt Enable */
+#define PCI_EXP_SLTCTL_AIC 0x00c0 /* Attention Indicator Control */
+#define PCI_EXP_SLTCTL_ATTN_IND_ON 0x0040 /* Attention Indicator on */
+#define PCI_EXP_SLTCTL_ATTN_IND_BLINK 0x0080 /* Attention Indicator blinking */
+#define PCI_EXP_SLTCTL_ATTN_IND_OFF 0x00c0 /* Attention Indicator off */
+#define PCI_EXP_SLTCTL_PIC 0x0300 /* Power Indicator Control */
+#define PCI_EXP_SLTCTL_PWR_IND_ON 0x0100 /* Power Indicator on */
+#define PCI_EXP_SLTCTL_PWR_IND_BLINK 0x0200 /* Power Indicator blinking */
+#define PCI_EXP_SLTCTL_PWR_IND_OFF 0x0300 /* Power Indicator off */
+#define PCI_EXP_SLTCTL_PCC 0x0400 /* Power Controller Control */
+#define PCI_EXP_SLTCTL_PWR_ON 0x0000 /* Power On */
+#define PCI_EXP_SLTCTL_PWR_OFF 0x0400 /* Power Off */
+#define PCI_EXP_SLTCTL_EIC 0x0800 /* Electromechanical Interlock Control */
+#define PCI_EXP_SLTCTL_DLLSCE 0x1000 /* Data Link Layer State Changed Enable */
+#define PCI_EXP_SLTSTA 26 /* Slot Status */
+#define PCI_EXP_SLTSTA_ABP 0x0001 /* Attention Button Pressed */
+#define PCI_EXP_SLTSTA_PFD 0x0002 /* Power Fault Detected */
+#define PCI_EXP_SLTSTA_MRLSC 0x0004 /* MRL Sensor Changed */
+#define PCI_EXP_SLTSTA_PDC 0x0008 /* Presence Detect Changed */
+#define PCI_EXP_SLTSTA_CC 0x0010 /* Command Completed */
+#define PCI_EXP_SLTSTA_MRLSS 0x0020 /* MRL Sensor State */
+#define PCI_EXP_SLTSTA_PDS 0x0040 /* Presence Detect State */
+#define PCI_EXP_SLTSTA_EIS 0x0080 /* Electromechanical Interlock Status */
+#define PCI_EXP_SLTSTA_DLLSC 0x0100 /* Data Link Layer State Changed */
+#define PCI_EXP_RTCTL 28 /* Root Control */
+#define PCI_EXP_RTCTL_SECEE 0x0001 /* System Error on Correctable Error */
+#define PCI_EXP_RTCTL_SENFEE 0x0002 /* System Error on Non-Fatal Error */
+#define PCI_EXP_RTCTL_SEFEE 0x0004 /* System Error on Fatal Error */
+#define PCI_EXP_RTCTL_PMEIE 0x0008 /* PME Interrupt Enable */
+#define PCI_EXP_RTCTL_CRSSVE 0x0010 /* CRS Software Visibility Enable */
+#define PCI_EXP_RTCAP 30 /* Root Capabilities */
+#define PCI_EXP_RTCAP_CRSVIS 0x0001 /* CRS Software Visibility capability */
+#define PCI_EXP_RTSTA 32 /* Root Status */
+#define PCI_EXP_RTSTA_PME 0x00010000 /* PME status */
+#define PCI_EXP_RTSTA_PENDING 0x00020000 /* PME pending */
+/*
+ * The Device Capabilities 2, Device Status 2, Device Control 2,
+ * Link Capabilities 2, Link Status 2, Link Control 2,
+ * Slot Capabilities 2, Slot Status 2, and Slot Control 2 registers
+ * are only present on devices with PCIe Capability version 2.
+ * Use pcie_capability_read_word() and similar interfaces to use them
+ * safely.
+ */
+#define PCI_EXP_DEVCAP2 36 /* Device Capabilities 2 */
+#define PCI_EXP_DEVCAP2_ARI 0x00000020 /* Alternative Routing-ID */
+#define PCI_EXP_DEVCAP2_LTR 0x00000800 /* Latency tolerance reporting */
+#define PCI_EXP_DEVCAP2_OBFF_MASK 0x000c0000 /* OBFF support mechanism */
+#define PCI_EXP_DEVCAP2_OBFF_MSG 0x00040000 /* New message signaling */
+#define PCI_EXP_DEVCAP2_OBFF_WAKE 0x00080000 /* Re-use WAKE# for OBFF */
+#define PCI_EXP_DEVCTL2 40 /* Device Control 2 */
+#define PCI_EXP_DEVCTL2_COMP_TIMEOUT 0x000f /* Completion Timeout Value */
+#define PCI_EXP_DEVCTL2_ARI 0x0020 /* Alternative Routing-ID */
+#define PCI_EXP_DEVCTL2_IDO_REQ_EN 0x0100 /* Allow IDO for requests */
+#define PCI_EXP_DEVCTL2_IDO_CMP_EN 0x0200 /* Allow IDO for completions */
+#define PCI_EXP_DEVCTL2_LTR_EN 0x0400 /* Enable LTR mechanism */
+#define PCI_EXP_DEVCTL2_OBFF_MSGA_EN 0x2000 /* Enable OBFF Message type A */
+#define PCI_EXP_DEVCTL2_OBFF_MSGB_EN 0x4000 /* Enable OBFF Message type B */
+#define PCI_EXP_DEVCTL2_OBFF_WAKE_EN 0x6000 /* OBFF using WAKE# signaling */
+#define PCI_EXP_DEVSTA2 42 /* Device Status 2 */
+#define PCI_CAP_EXP_ENDPOINT_SIZEOF_V2 44 /* v2 endpoints end here */
+#define PCI_EXP_LNKCAP2 44 /* Link Capabilities 2 */
+#define PCI_EXP_LNKCAP2_SLS_2_5GB 0x00000002 /* Supported Speed 2.5GT/s */
+#define PCI_EXP_LNKCAP2_SLS_5_0GB 0x00000004 /* Supported Speed 5.0GT/s */
+#define PCI_EXP_LNKCAP2_SLS_8_0GB 0x00000008 /* Supported Speed 8.0GT/s */
+#define PCI_EXP_LNKCAP2_CROSSLINK 0x00000100 /* Crosslink supported */
+#define PCI_EXP_LNKCTL2 48 /* Link Control 2 */
+#define PCI_EXP_LNKSTA2 50 /* Link Status 2 */
+#define PCI_EXP_SLTCAP2 52 /* Slot Capabilities 2 */
+#define PCI_EXP_SLTCTL2 56 /* Slot Control 2 */
+#define PCI_EXP_SLTSTA2 58 /* Slot Status 2 */
+
+/* Extended Capabilities (PCI-X 2.0 and Express) */
+#define PCI_EXT_CAP_ID(header) (header & 0x0000ffff)
+#define PCI_EXT_CAP_VER(header) ((header >> 16) & 0xf)
+#define PCI_EXT_CAP_NEXT(header) ((header >> 20) & 0xffc)
+
+#define PCI_EXT_CAP_ID_ERR 0x01 /* Advanced Error Reporting */
+#define PCI_EXT_CAP_ID_VC 0x02 /* Virtual Channel Capability */
+#define PCI_EXT_CAP_ID_DSN 0x03 /* Device Serial Number */
+#define PCI_EXT_CAP_ID_PWR 0x04 /* Power Budgeting */
+#define PCI_EXT_CAP_ID_RCLD 0x05 /* Root Complex Link Declaration */
+#define PCI_EXT_CAP_ID_RCILC 0x06 /* Root Complex Internal Link Control */
+#define PCI_EXT_CAP_ID_RCEC 0x07 /* Root Complex Event Collector */
+#define PCI_EXT_CAP_ID_MFVC 0x08 /* Multi-Function VC Capability */
+#define PCI_EXT_CAP_ID_VC9 0x09 /* same as _VC */
+#define PCI_EXT_CAP_ID_RCRB 0x0A /* Root Complex RB? */
+#define PCI_EXT_CAP_ID_VNDR 0x0B /* Vendor-Specific */
+#define PCI_EXT_CAP_ID_CAC 0x0C /* Config Access - obsolete */
+#define PCI_EXT_CAP_ID_ACS 0x0D /* Access Control Services */
+#define PCI_EXT_CAP_ID_ARI 0x0E /* Alternate Routing ID */
+#define PCI_EXT_CAP_ID_ATS 0x0F /* Address Translation Services */
+#define PCI_EXT_CAP_ID_SRIOV 0x10 /* Single Root I/O Virtualization */
+#define PCI_EXT_CAP_ID_MRIOV 0x11 /* Multi Root I/O Virtualization */
+#define PCI_EXT_CAP_ID_MCAST 0x12 /* Multicast */
+#define PCI_EXT_CAP_ID_PRI 0x13 /* Page Request Interface */
+#define PCI_EXT_CAP_ID_AMD_XXX 0x14 /* Reserved for AMD */
+#define PCI_EXT_CAP_ID_REBAR 0x15 /* Resizable BAR */
+#define PCI_EXT_CAP_ID_DPA 0x16 /* Dynamic Power Allocation */
+#define PCI_EXT_CAP_ID_TPH 0x17 /* TPH Requester */
+#define PCI_EXT_CAP_ID_LTR 0x18 /* Latency Tolerance Reporting */
+#define PCI_EXT_CAP_ID_SECPCI 0x19 /* Secondary PCIe Capability */
+#define PCI_EXT_CAP_ID_PMUX 0x1A /* Protocol Multiplexing */
+#define PCI_EXT_CAP_ID_PASID 0x1B /* Process Address Space ID */
+#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PASID
+
+#define PCI_EXT_CAP_DSN_SIZEOF 12
+#define PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF 40
+
+/* Advanced Error Reporting */
+#define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */
+#define PCI_ERR_UNC_UND 0x00000001 /* Undefined */
+#define PCI_ERR_UNC_DLP 0x00000010 /* Data Link Protocol */
+#define PCI_ERR_UNC_SURPDN 0x00000020 /* Surprise Down */
+#define PCI_ERR_UNC_POISON_TLP 0x00001000 /* Poisoned TLP */
+#define PCI_ERR_UNC_FCP 0x00002000 /* Flow Control Protocol */
+#define PCI_ERR_UNC_COMP_TIME 0x00004000 /* Completion Timeout */
+#define PCI_ERR_UNC_COMP_ABORT 0x00008000 /* Completer Abort */
+#define PCI_ERR_UNC_UNX_COMP 0x00010000 /* Unexpected Completion */
+#define PCI_ERR_UNC_RX_OVER 0x00020000 /* Receiver Overflow */
+#define PCI_ERR_UNC_MALF_TLP 0x00040000 /* Malformed TLP */
+#define PCI_ERR_UNC_ECRC 0x00080000 /* ECRC Error Status */
+#define PCI_ERR_UNC_UNSUP 0x00100000 /* Unsupported Request */
+#define PCI_ERR_UNC_ACSV 0x00200000 /* ACS Violation */
+#define PCI_ERR_UNC_INTN 0x00400000 /* internal error */
+#define PCI_ERR_UNC_MCBTLP 0x00800000 /* MC blocked TLP */
+#define PCI_ERR_UNC_ATOMEG 0x01000000 /* Atomic egress blocked */
+#define PCI_ERR_UNC_TLPPRE 0x02000000 /* TLP prefix blocked */
+#define PCI_ERR_UNCOR_MASK 8 /* Uncorrectable Error Mask */
+ /* Same bits as above */
+#define PCI_ERR_UNCOR_SEVER 12 /* Uncorrectable Error Severity */
+ /* Same bits as above */
+#define PCI_ERR_COR_STATUS 16 /* Correctable Error Status */
+#define PCI_ERR_COR_RCVR 0x00000001 /* Receiver Error Status */
+#define PCI_ERR_COR_BAD_TLP 0x00000040 /* Bad TLP Status */
+#define PCI_ERR_COR_BAD_DLLP 0x00000080 /* Bad DLLP Status */
+#define PCI_ERR_COR_REP_ROLL 0x00000100 /* REPLAY_NUM Rollover */
+#define PCI_ERR_COR_REP_TIMER 0x00001000 /* Replay Timer Timeout */
+#define PCI_ERR_COR_ADV_NFAT 0x00002000 /* Advisory Non-Fatal */
+#define PCI_ERR_COR_INTERNAL 0x00004000 /* Corrected Internal */
+#define PCI_ERR_COR_LOG_OVER 0x00008000 /* Header Log Overflow */
+#define PCI_ERR_COR_MASK 20 /* Correctable Error Mask */
+ /* Same bits as above */
+#define PCI_ERR_CAP 24 /* Advanced Error Capabilities */
+#define PCI_ERR_CAP_FEP(x) ((x) & 31) /* First Error Pointer */
+#define PCI_ERR_CAP_ECRC_GENC 0x00000020 /* ECRC Generation Capable */
+#define PCI_ERR_CAP_ECRC_GENE 0x00000040 /* ECRC Generation Enable */
+#define PCI_ERR_CAP_ECRC_CHKC 0x00000080 /* ECRC Check Capable */
+#define PCI_ERR_CAP_ECRC_CHKE 0x00000100 /* ECRC Check Enable */
+#define PCI_ERR_HEADER_LOG 28 /* Header Log Register (16 bytes) */
+#define PCI_ERR_ROOT_COMMAND 44 /* Root Error Command */
+/* Correctable Err Reporting Enable */
+#define PCI_ERR_ROOT_CMD_COR_EN 0x00000001
+/* Non-fatal Err Reporting Enable */
+#define PCI_ERR_ROOT_CMD_NONFATAL_EN 0x00000002
+/* Fatal Err Reporting Enable */
+#define PCI_ERR_ROOT_CMD_FATAL_EN 0x00000004
+#define PCI_ERR_ROOT_STATUS 48
+#define PCI_ERR_ROOT_COR_RCV 0x00000001 /* ERR_COR Received */
+/* Multi ERR_COR Received */
+#define PCI_ERR_ROOT_MULTI_COR_RCV 0x00000002
+/* ERR_FATAL/NONFATAL Received */
+#define PCI_ERR_ROOT_UNCOR_RCV 0x00000004
+/* Multi ERR_FATAL/NONFATAL Received */
+#define PCI_ERR_ROOT_MULTI_UNCOR_RCV 0x00000008
+#define PCI_ERR_ROOT_FIRST_FATAL 0x00000010 /* First Fatal */
+#define PCI_ERR_ROOT_NONFATAL_RCV 0x00000020 /* Non-Fatal Received */
+#define PCI_ERR_ROOT_FATAL_RCV 0x00000040 /* Fatal Received */
+#define PCI_ERR_ROOT_ERR_SRC 52 /* Error Source Identification */
+
+/* Virtual Channel */
+#define PCI_VC_PORT_CAP1 4
+#define PCI_VC_CAP1_EVCC 0x00000007 /* extended VC count */
+#define PCI_VC_CAP1_LPEVCC 0x00000070 /* low prio extended VC count */
+#define PCI_VC_CAP1_ARB_SIZE 0x00000c00
+#define PCI_VC_PORT_CAP2 8
+#define PCI_VC_CAP2_32_PHASE 0x00000002
+#define PCI_VC_CAP2_64_PHASE 0x00000004
+#define PCI_VC_CAP2_128_PHASE 0x00000008
+#define PCI_VC_CAP2_ARB_OFF 0xff000000
+#define PCI_VC_PORT_CTRL 12
+#define PCI_VC_PORT_CTRL_LOAD_TABLE 0x00000001
+#define PCI_VC_PORT_STATUS 14
+#define PCI_VC_PORT_STATUS_TABLE 0x00000001
+#define PCI_VC_RES_CAP 16
+#define PCI_VC_RES_CAP_32_PHASE 0x00000002
+#define PCI_VC_RES_CAP_64_PHASE 0x00000004
+#define PCI_VC_RES_CAP_128_PHASE 0x00000008
+#define PCI_VC_RES_CAP_128_PHASE_TB 0x00000010
+#define PCI_VC_RES_CAP_256_PHASE 0x00000020
+#define PCI_VC_RES_CAP_ARB_OFF 0xff000000
+#define PCI_VC_RES_CTRL 20
+#define PCI_VC_RES_CTRL_LOAD_TABLE 0x00010000
+#define PCI_VC_RES_CTRL_ARB_SELECT 0x000e0000
+#define PCI_VC_RES_CTRL_ID 0x07000000
+#define PCI_VC_RES_CTRL_ENABLE 0x80000000
+#define PCI_VC_RES_STATUS 26
+#define PCI_VC_RES_STATUS_TABLE 0x00000001
+#define PCI_VC_RES_STATUS_NEGO 0x00000002
+#define PCI_CAP_VC_BASE_SIZEOF 0x10
+#define PCI_CAP_VC_PER_VC_SIZEOF 0x0C
+
+/* Power Budgeting */
+#define PCI_PWR_DSR 4 /* Data Select Register */
+#define PCI_PWR_DATA 8 /* Data Register */
+#define PCI_PWR_DATA_BASE(x) ((x) & 0xff) /* Base Power */
+#define PCI_PWR_DATA_SCALE(x) (((x) >> 8) & 3) /* Data Scale */
+#define PCI_PWR_DATA_PM_SUB(x) (((x) >> 10) & 7) /* PM Sub State */
+#define PCI_PWR_DATA_PM_STATE(x) (((x) >> 13) & 3) /* PM State */
+#define PCI_PWR_DATA_TYPE(x) (((x) >> 15) & 7) /* Type */
+#define PCI_PWR_DATA_RAIL(x) (((x) >> 18) & 7) /* Power Rail */
+#define PCI_PWR_CAP 12 /* Capability */
+#define PCI_PWR_CAP_BUDGET(x) ((x) & 1) /* Included in system budget */
+#define PCI_EXT_CAP_PWR_SIZEOF 16
+
+/* Vendor-Specific (VSEC, PCI_EXT_CAP_ID_VNDR) */
+#define PCI_VNDR_HEADER 4 /* Vendor-Specific Header */
+#define PCI_VNDR_HEADER_ID(x) ((x) & 0xffff)
+#define PCI_VNDR_HEADER_REV(x) (((x) >> 16) & 0xf)
+#define PCI_VNDR_HEADER_LEN(x) (((x) >> 20) & 0xfff)
+
+/*
+ * HyperTransport sub capability types
+ *
+ * Unfortunately there are both 3 bit and 5 bit capability types defined
+ * in the HT spec, catering for that is a little messy. You probably don't
+ * want to use these directly, just use pci_find_ht_capability() and it
+ * will do the right thing for you.
+ */
+#define HT_3BIT_CAP_MASK 0xE0
+#define HT_CAPTYPE_SLAVE 0x00 /* Slave/Primary link configuration */
+#define HT_CAPTYPE_HOST 0x20 /* Host/Secondary link configuration */
+
+#define HT_5BIT_CAP_MASK 0xF8
+#define HT_CAPTYPE_IRQ 0x80 /* IRQ Configuration */
+#define HT_CAPTYPE_REMAPPING_40 0xA0 /* 40 bit address remapping */
+#define HT_CAPTYPE_REMAPPING_64 0xA2 /* 64 bit address remapping */
+#define HT_CAPTYPE_UNITID_CLUMP 0x90 /* Unit ID clumping */
+#define HT_CAPTYPE_EXTCONF 0x98 /* Extended Configuration Space Access */
+#define HT_CAPTYPE_MSI_MAPPING 0xA8 /* MSI Mapping Capability */
+#define HT_MSI_FLAGS 0x02 /* Offset to flags */
+#define HT_MSI_FLAGS_ENABLE 0x1 /* Mapping enable */
+#define HT_MSI_FLAGS_FIXED 0x2 /* Fixed mapping only */
+#define HT_MSI_FIXED_ADDR 0x00000000FEE00000ULL /* Fixed addr */
+#define HT_MSI_ADDR_LO 0x04 /* Offset to low addr bits */
+#define HT_MSI_ADDR_LO_MASK 0xFFF00000 /* Low address bit mask */
+#define HT_MSI_ADDR_HI 0x08 /* Offset to high addr bits */
+#define HT_CAPTYPE_DIRECT_ROUTE 0xB0 /* Direct routing configuration */
+#define HT_CAPTYPE_VCSET 0xB8 /* Virtual Channel configuration */
+#define HT_CAPTYPE_ERROR_RETRY 0xC0 /* Retry on error configuration */
+#define HT_CAPTYPE_GEN3 0xD0 /* Generation 3 HyperTransport configuration */
+#define HT_CAPTYPE_PM 0xE0 /* HyperTransport power management configuration */
+#define HT_CAP_SIZEOF_LONG 28 /* slave & primary */
+#define HT_CAP_SIZEOF_SHORT 24 /* host & secondary */
+
+/* Alternative Routing-ID Interpretation */
+#define PCI_ARI_CAP 0x04 /* ARI Capability Register */
+#define PCI_ARI_CAP_MFVC 0x0001 /* MFVC Function Groups Capability */
+#define PCI_ARI_CAP_ACS 0x0002 /* ACS Function Groups Capability */
+#define PCI_ARI_CAP_NFN(x) (((x) >> 8) & 0xff) /* Next Function Number */
+#define PCI_ARI_CTRL 0x06 /* ARI Control Register */
+#define PCI_ARI_CTRL_MFVC 0x0001 /* MFVC Function Groups Enable */
+#define PCI_ARI_CTRL_ACS 0x0002 /* ACS Function Groups Enable */
+#define PCI_ARI_CTRL_FG(x) (((x) >> 4) & 7) /* Function Group */
+#define PCI_EXT_CAP_ARI_SIZEOF 8
+
+/* Address Translation Service */
+#define PCI_ATS_CAP 0x04 /* ATS Capability Register */
+#define PCI_ATS_CAP_QDEP(x) ((x) & 0x1f) /* Invalidate Queue Depth */
+#define PCI_ATS_MAX_QDEP 32 /* Max Invalidate Queue Depth */
+#define PCI_ATS_CTRL 0x06 /* ATS Control Register */
+#define PCI_ATS_CTRL_ENABLE 0x8000 /* ATS Enable */
+#define PCI_ATS_CTRL_STU(x) ((x) & 0x1f) /* Smallest Translation Unit */
+#define PCI_ATS_MIN_STU 12 /* shift of minimum STU block */
+#define PCI_EXT_CAP_ATS_SIZEOF 8
+
+/* Page Request Interface */
+#define PCI_PRI_CTRL 0x04 /* PRI control register */
+#define PCI_PRI_CTRL_ENABLE 0x01 /* Enable */
+#define PCI_PRI_CTRL_RESET 0x02 /* Reset */
+#define PCI_PRI_STATUS 0x06 /* PRI status register */
+#define PCI_PRI_STATUS_RF 0x001 /* Response Failure */
+#define PCI_PRI_STATUS_UPRGI 0x002 /* Unexpected PRG index */
+#define PCI_PRI_STATUS_STOPPED 0x100 /* PRI Stopped */
+#define PCI_PRI_MAX_REQ 0x08 /* PRI max reqs supported */
+#define PCI_PRI_ALLOC_REQ 0x0c /* PRI max reqs allowed */
+#define PCI_EXT_CAP_PRI_SIZEOF 16
+
+/* Process Address Space ID */
+#define PCI_PASID_CAP 0x04 /* PASID feature register */
+#define PCI_PASID_CAP_EXEC 0x02 /* Exec permissions Supported */
+#define PCI_PASID_CAP_PRIV 0x04 /* Privilege Mode Supported */
+#define PCI_PASID_CTRL 0x06 /* PASID control register */
+#define PCI_PASID_CTRL_ENABLE 0x01 /* Enable bit */
+#define PCI_PASID_CTRL_EXEC 0x02 /* Exec permissions Enable */
+#define PCI_PASID_CTRL_PRIV 0x04 /* Privilege Mode Enable */
+#define PCI_EXT_CAP_PASID_SIZEOF 8
+
+/* Single Root I/O Virtualization */
+#define PCI_SRIOV_CAP 0x04 /* SR-IOV Capabilities */
+#define PCI_SRIOV_CAP_VFM 0x01 /* VF Migration Capable */
+#define PCI_SRIOV_CAP_INTR(x) ((x) >> 21) /* Interrupt Message Number */
+#define PCI_SRIOV_CTRL 0x08 /* SR-IOV Control */
+#define PCI_SRIOV_CTRL_VFE 0x01 /* VF Enable */
+#define PCI_SRIOV_CTRL_VFM 0x02 /* VF Migration Enable */
+#define PCI_SRIOV_CTRL_INTR 0x04 /* VF Migration Interrupt Enable */
+#define PCI_SRIOV_CTRL_MSE 0x08 /* VF Memory Space Enable */
+#define PCI_SRIOV_CTRL_ARI 0x10 /* ARI Capable Hierarchy */
+#define PCI_SRIOV_STATUS 0x0a /* SR-IOV Status */
+#define PCI_SRIOV_STATUS_VFM 0x01 /* VF Migration Status */
+#define PCI_SRIOV_INITIAL_VF 0x0c /* Initial VFs */
+#define PCI_SRIOV_TOTAL_VF 0x0e /* Total VFs */
+#define PCI_SRIOV_NUM_VF 0x10 /* Number of VFs */
+#define PCI_SRIOV_FUNC_LINK 0x12 /* Function Dependency Link */
+#define PCI_SRIOV_VF_OFFSET 0x14 /* First VF Offset */
+#define PCI_SRIOV_VF_STRIDE 0x16 /* Following VF Stride */
+#define PCI_SRIOV_VF_DID 0x1a /* VF Device ID */
+#define PCI_SRIOV_SUP_PGSIZE 0x1c /* Supported Page Sizes */
+#define PCI_SRIOV_SYS_PGSIZE 0x20 /* System Page Size */
+#define PCI_SRIOV_BAR 0x24 /* VF BAR0 */
+#define PCI_SRIOV_NUM_BARS 6 /* Number of VF BARs */
+#define PCI_SRIOV_VFM 0x3c /* VF Migration State Array Offset*/
+#define PCI_SRIOV_VFM_BIR(x) ((x) & 7) /* State BIR */
+#define PCI_SRIOV_VFM_OFFSET(x) ((x) & ~7) /* State Offset */
+#define PCI_SRIOV_VFM_UA 0x0 /* Inactive.Unavailable */
+#define PCI_SRIOV_VFM_MI 0x1 /* Dormant.MigrateIn */
+#define PCI_SRIOV_VFM_MO 0x2 /* Active.MigrateOut */
+#define PCI_SRIOV_VFM_AV 0x3 /* Active.Available */
+#define PCI_EXT_CAP_SRIOV_SIZEOF 64
+
+#define PCI_LTR_MAX_SNOOP_LAT 0x4
+#define PCI_LTR_MAX_NOSNOOP_LAT 0x6
+#define PCI_LTR_VALUE_MASK 0x000003ff
+#define PCI_LTR_SCALE_MASK 0x00001c00
+#define PCI_LTR_SCALE_SHIFT 10
+#define PCI_EXT_CAP_LTR_SIZEOF 8
+
+/* Access Control Service */
+#define PCI_ACS_CAP 0x04 /* ACS Capability Register */
+#define PCI_ACS_SV 0x01 /* Source Validation */
+#define PCI_ACS_TB 0x02 /* Translation Blocking */
+#define PCI_ACS_RR 0x04 /* P2P Request Redirect */
+#define PCI_ACS_CR 0x08 /* P2P Completion Redirect */
+#define PCI_ACS_UF 0x10 /* Upstream Forwarding */
+#define PCI_ACS_EC 0x20 /* P2P Egress Control */
+#define PCI_ACS_DT 0x40 /* Direct Translated P2P */
+#define PCI_ACS_EGRESS_BITS 0x05 /* ACS Egress Control Vector Size */
+#define PCI_ACS_CTRL 0x06 /* ACS Control Register */
+#define PCI_ACS_EGRESS_CTL_V 0x08 /* ACS Egress Control Vector */
+
+#define PCI_VSEC_HDR 4 /* extended cap - vendor-specific */
+#define PCI_VSEC_HDR_LEN_SHIFT 20 /* shift for length field */
+
+/* SATA capability */
+#define PCI_SATA_REGS 4 /* SATA REGs specifier */
+#define PCI_SATA_REGS_MASK 0xF /* location - BAR#/inline */
+#define PCI_SATA_REGS_INLINE 0xF /* REGS in config space */
+#define PCI_SATA_SIZEOF_SHORT 8
+#define PCI_SATA_SIZEOF_LONG 16
+
+/* Resizable BARs */
+#define PCI_REBAR_CTRL 8 /* control register */
+#define PCI_REBAR_CTRL_NBAR_MASK (7 << 5) /* mask for # bars */
+#define PCI_REBAR_CTRL_NBAR_SHIFT 5 /* shift for # bars */
+
+/* Dynamic Power Allocation */
+#define PCI_DPA_CAP 4 /* capability register */
+#define PCI_DPA_CAP_SUBSTATE_MASK 0x1F /* # substates - 1 */
+#define PCI_DPA_BASE_SIZEOF 16 /* size with 0 substates */
+
+/* TPH Requester */
+#define PCI_TPH_CAP 4 /* capability register */
+#define PCI_TPH_CAP_LOC_MASK 0x600 /* location mask */
+#define PCI_TPH_LOC_NONE 0x000 /* no location */
+#define PCI_TPH_LOC_CAP 0x200 /* in capability */
+#define PCI_TPH_LOC_MSIX 0x400 /* in MSI-X */
+#define PCI_TPH_CAP_ST_MASK 0x07FF0000 /* st table mask */
+#define PCI_TPH_CAP_ST_SHIFT 16 /* st table shift */
+#define PCI_TPH_BASE_SIZEOF 12 /* size with no st table */
+
+#endif /* LINUX_PCI_REGS_H */
diff --git a/include/uapi/linux/v4l2-common.h b/include/uapi/linux/v4l2-common.h
index 2f6f8ca..1527398 100644
--- a/include/uapi/linux/v4l2-common.h
+++ b/include/uapi/linux/v4l2-common.h
@@ -43,6 +43,8 @@
#define V4L2_SEL_TGT_CROP_DEFAULT 0x0001
/* Cropping bounds */
#define V4L2_SEL_TGT_CROP_BOUNDS 0x0002
+/* Native frame size */
+#define V4L2_SEL_TGT_NATIVE_SIZE 0x0003
/* Current composing area */
#define V4L2_SEL_TGT_COMPOSE 0x0100
/* Default composing area */
diff --git a/include/uapi/linux/v4l2-dv-timings.h b/include/uapi/linux/v4l2-dv-timings.h
index 6a0764c..6c8f159 100644
--- a/include/uapi/linux/v4l2-dv-timings.h
+++ b/include/uapi/linux/v4l2-dv-timings.h
@@ -21,8 +21,17 @@
#ifndef _V4L2_DV_TIMINGS_H
#define _V4L2_DV_TIMINGS_H
+#if __GNUC__ < 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ < 6))
+/* Sadly gcc versions older than 4.6 have a bug in how they initialize
+ anonymous unions where they require additional curly brackets.
+ This violates the C1x standard. This workaround adds the curly brackets
+ if needed. */
#define V4L2_INIT_BT_TIMINGS(_width, args...) \
{ .bt = { _width , ## args } }
+#else
+#define V4L2_INIT_BT_TIMINGS(_width, args...) \
+ .bt = { _width , ## args }
+#endif
/* CEA-861-E timings (i.e. standard HDTV timings) */
diff --git a/include/uapi/linux/v4l2-mediabus.h b/include/uapi/linux/v4l2-mediabus.h
index 1445e85..26db206 100644
--- a/include/uapi/linux/v4l2-mediabus.h
+++ b/include/uapi/linux/v4l2-mediabus.h
@@ -11,122 +11,10 @@
#ifndef __LINUX_V4L2_MEDIABUS_H
#define __LINUX_V4L2_MEDIABUS_H
+#include <linux/media-bus-format.h>
#include <linux/types.h>
#include <linux/videodev2.h>
-/*
- * These pixel codes uniquely identify data formats on the media bus. Mostly
- * they correspond to similarly named V4L2_PIX_FMT_* formats, format 0 is
- * reserved, V4L2_MBUS_FMT_FIXED shall be used by host-client pairs, where the
- * data format is fixed. Additionally, "2X8" means that one pixel is transferred
- * in two 8-bit samples, "BE" or "LE" specify in which order those samples are
- * transferred over the bus: "LE" means that the least significant bits are
- * transferred first, "BE" means that the most significant bits are transferred
- * first, and "PADHI" and "PADLO" define which bits - low or high, in the
- * incomplete high byte, are filled with padding bits.
- *
- * The pixel codes are grouped by type, bus_width, bits per component, samples
- * per pixel and order of subsamples. Numerical values are sorted using generic
- * numerical sort order (8 thus comes before 10).
- *
- * As their value can't change when a new pixel code is inserted in the
- * enumeration, the pixel codes are explicitly given a numerical value. The next
- * free values for each category are listed below, update them when inserting
- * new pixel codes.
- */
-enum v4l2_mbus_pixelcode {
- V4L2_MBUS_FMT_FIXED = 0x0001,
-
- /* RGB - next is 0x100e */
- V4L2_MBUS_FMT_RGB444_2X8_PADHI_BE = 0x1001,
- V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE = 0x1002,
- V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE = 0x1003,
- V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE = 0x1004,
- V4L2_MBUS_FMT_BGR565_2X8_BE = 0x1005,
- V4L2_MBUS_FMT_BGR565_2X8_LE = 0x1006,
- V4L2_MBUS_FMT_RGB565_2X8_BE = 0x1007,
- V4L2_MBUS_FMT_RGB565_2X8_LE = 0x1008,
- V4L2_MBUS_FMT_RGB666_1X18 = 0x1009,
- V4L2_MBUS_FMT_RGB888_1X24 = 0x100a,
- V4L2_MBUS_FMT_RGB888_2X12_BE = 0x100b,
- V4L2_MBUS_FMT_RGB888_2X12_LE = 0x100c,
- V4L2_MBUS_FMT_ARGB8888_1X32 = 0x100d,
-
- /* YUV (including grey) - next is 0x2024 */
- V4L2_MBUS_FMT_Y8_1X8 = 0x2001,
- V4L2_MBUS_FMT_UV8_1X8 = 0x2015,
- V4L2_MBUS_FMT_UYVY8_1_5X8 = 0x2002,
- V4L2_MBUS_FMT_VYUY8_1_5X8 = 0x2003,
- V4L2_MBUS_FMT_YUYV8_1_5X8 = 0x2004,
- V4L2_MBUS_FMT_YVYU8_1_5X8 = 0x2005,
- V4L2_MBUS_FMT_UYVY8_2X8 = 0x2006,
- V4L2_MBUS_FMT_VYUY8_2X8 = 0x2007,
- V4L2_MBUS_FMT_YUYV8_2X8 = 0x2008,
- V4L2_MBUS_FMT_YVYU8_2X8 = 0x2009,
- V4L2_MBUS_FMT_Y10_1X10 = 0x200a,
- V4L2_MBUS_FMT_UYVY10_2X10 = 0x2018,
- V4L2_MBUS_FMT_VYUY10_2X10 = 0x2019,
- V4L2_MBUS_FMT_YUYV10_2X10 = 0x200b,
- V4L2_MBUS_FMT_YVYU10_2X10 = 0x200c,
- V4L2_MBUS_FMT_Y12_1X12 = 0x2013,
- V4L2_MBUS_FMT_UYVY8_1X16 = 0x200f,
- V4L2_MBUS_FMT_VYUY8_1X16 = 0x2010,
- V4L2_MBUS_FMT_YUYV8_1X16 = 0x2011,
- V4L2_MBUS_FMT_YVYU8_1X16 = 0x2012,
- V4L2_MBUS_FMT_YDYUYDYV8_1X16 = 0x2014,
- V4L2_MBUS_FMT_UYVY10_1X20 = 0x201a,
- V4L2_MBUS_FMT_VYUY10_1X20 = 0x201b,
- V4L2_MBUS_FMT_YUYV10_1X20 = 0x200d,
- V4L2_MBUS_FMT_YVYU10_1X20 = 0x200e,
- V4L2_MBUS_FMT_YUV10_1X30 = 0x2016,
- V4L2_MBUS_FMT_AYUV8_1X32 = 0x2017,
- V4L2_MBUS_FMT_UYVY12_2X12 = 0x201c,
- V4L2_MBUS_FMT_VYUY12_2X12 = 0x201d,
- V4L2_MBUS_FMT_YUYV12_2X12 = 0x201e,
- V4L2_MBUS_FMT_YVYU12_2X12 = 0x201f,
- V4L2_MBUS_FMT_UYVY12_1X24 = 0x2020,
- V4L2_MBUS_FMT_VYUY12_1X24 = 0x2021,
- V4L2_MBUS_FMT_YUYV12_1X24 = 0x2022,
- V4L2_MBUS_FMT_YVYU12_1X24 = 0x2023,
-
- /* Bayer - next is 0x3019 */
- V4L2_MBUS_FMT_SBGGR8_1X8 = 0x3001,
- V4L2_MBUS_FMT_SGBRG8_1X8 = 0x3013,
- V4L2_MBUS_FMT_SGRBG8_1X8 = 0x3002,
- V4L2_MBUS_FMT_SRGGB8_1X8 = 0x3014,
- V4L2_MBUS_FMT_SBGGR10_ALAW8_1X8 = 0x3015,
- V4L2_MBUS_FMT_SGBRG10_ALAW8_1X8 = 0x3016,
- V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8 = 0x3017,
- V4L2_MBUS_FMT_SRGGB10_ALAW8_1X8 = 0x3018,
- V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8 = 0x300b,
- V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8 = 0x300c,
- V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8 = 0x3009,
- V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8 = 0x300d,
- V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE = 0x3003,
- V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE = 0x3004,
- V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE = 0x3005,
- V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE = 0x3006,
- V4L2_MBUS_FMT_SBGGR10_1X10 = 0x3007,
- V4L2_MBUS_FMT_SGBRG10_1X10 = 0x300e,
- V4L2_MBUS_FMT_SGRBG10_1X10 = 0x300a,
- V4L2_MBUS_FMT_SRGGB10_1X10 = 0x300f,
- V4L2_MBUS_FMT_SBGGR12_1X12 = 0x3008,
- V4L2_MBUS_FMT_SGBRG12_1X12 = 0x3010,
- V4L2_MBUS_FMT_SGRBG12_1X12 = 0x3011,
- V4L2_MBUS_FMT_SRGGB12_1X12 = 0x3012,
-
- /* JPEG compressed formats - next is 0x4002 */
- V4L2_MBUS_FMT_JPEG_1X8 = 0x4001,
-
- /* Vendor specific formats - next is 0x5002 */
-
- /* S5C73M3 sensor specific interleaved UYVY and JPEG */
- V4L2_MBUS_FMT_S5C_UYVY_JPEG_1X8 = 0x5001,
-
- /* HSV - next is 0x6002 */
- V4L2_MBUS_FMT_AHSV8888_1X32 = 0x6001,
-};
-
/**
* struct v4l2_mbus_framefmt - frame format on the media bus
* @width: frame width
@@ -134,6 +22,8 @@
* @code: data format code (from enum v4l2_mbus_pixelcode)
* @field: used interlacing type (from enum v4l2_field)
* @colorspace: colorspace of the data (from enum v4l2_colorspace)
+ * @ycbcr_enc: YCbCr encoding of the data (from enum v4l2_ycbcr_encoding)
+ * @quantization: quantization of the data (from enum v4l2_quantization)
*/
struct v4l2_mbus_framefmt {
__u32 width;
@@ -141,7 +31,108 @@
__u32 code;
__u32 field;
__u32 colorspace;
- __u32 reserved[7];
+ __u16 ycbcr_enc;
+ __u16 quantization;
+ __u32 reserved[6];
};
+#ifndef __KERNEL__
+/*
+ * enum v4l2_mbus_pixelcode and its definitions are now deprecated, and
+ * MEDIA_BUS_FMT_ definitions (defined in media-bus-format.h) should be
+ * used instead.
+ *
+ * New defines should only be added to media-bus-format.h. The
+ * v4l2_mbus_pixelcode enum is frozen.
+ */
+
+#define V4L2_MBUS_FROM_MEDIA_BUS_FMT(name) \
+ V4L2_MBUS_FMT_ ## name = MEDIA_BUS_FMT_ ## name
+
+enum v4l2_mbus_pixelcode {
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(FIXED),
+
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(RGB444_2X8_PADHI_BE),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(RGB444_2X8_PADHI_LE),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(RGB555_2X8_PADHI_BE),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(RGB555_2X8_PADHI_LE),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(BGR565_2X8_BE),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(BGR565_2X8_LE),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(RGB565_2X8_BE),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(RGB565_2X8_LE),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(RGB666_1X18),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(RGB888_1X24),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(RGB888_2X12_BE),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(RGB888_2X12_LE),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(ARGB8888_1X32),
+
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(Y8_1X8),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(UV8_1X8),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(UYVY8_1_5X8),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(VYUY8_1_5X8),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(YUYV8_1_5X8),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(YVYU8_1_5X8),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(UYVY8_2X8),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(VYUY8_2X8),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(YUYV8_2X8),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(YVYU8_2X8),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(Y10_1X10),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(UYVY10_2X10),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(VYUY10_2X10),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(YUYV10_2X10),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(YVYU10_2X10),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(Y12_1X12),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(UYVY8_1X16),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(VYUY8_1X16),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(YUYV8_1X16),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(YVYU8_1X16),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(YDYUYDYV8_1X16),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(UYVY10_1X20),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(VYUY10_1X20),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(YUYV10_1X20),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(YVYU10_1X20),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(YUV10_1X30),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(AYUV8_1X32),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(UYVY12_2X12),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(VYUY12_2X12),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(YUYV12_2X12),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(YVYU12_2X12),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(UYVY12_1X24),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(VYUY12_1X24),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(YUYV12_1X24),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(YVYU12_1X24),
+
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(SBGGR8_1X8),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(SGBRG8_1X8),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(SGRBG8_1X8),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(SRGGB8_1X8),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(SBGGR10_ALAW8_1X8),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(SGBRG10_ALAW8_1X8),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(SGRBG10_ALAW8_1X8),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(SRGGB10_ALAW8_1X8),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(SBGGR10_DPCM8_1X8),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(SGBRG10_DPCM8_1X8),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(SGRBG10_DPCM8_1X8),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(SRGGB10_DPCM8_1X8),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(SBGGR10_2X8_PADHI_BE),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(SBGGR10_2X8_PADHI_LE),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(SBGGR10_2X8_PADLO_BE),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(SBGGR10_2X8_PADLO_LE),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(SBGGR10_1X10),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(SGBRG10_1X10),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(SGRBG10_1X10),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(SRGGB10_1X10),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(SBGGR12_1X12),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(SGBRG12_1X12),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(SGRBG12_1X12),
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(SRGGB12_1X12),
+
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(JPEG_1X8),
+
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(S5C_UYVY_JPEG_1X8),
+
+ V4L2_MBUS_FROM_MEDIA_BUS_FMT(AHSV8888_1X32),
+};
+#endif /* __KERNEL__ */
+
#endif
diff --git a/include/uapi/linux/v4l2-subdev.h b/include/uapi/linux/v4l2-subdev.h
index a619cdd..e0a7e3d 100644
--- a/include/uapi/linux/v4l2-subdev.h
+++ b/include/uapi/linux/v4l2-subdev.h
@@ -68,7 +68,7 @@
* struct v4l2_subdev_mbus_code_enum - Media bus format enumeration
* @pad: pad number, as reported by the media API
* @index: format index during enumeration
- * @code: format code (from enum v4l2_mbus_pixelcode)
+ * @code: format code (MEDIA_BUS_FMT_ definitions)
*/
struct v4l2_subdev_mbus_code_enum {
__u32 pad;
@@ -81,7 +81,7 @@
* struct v4l2_subdev_frame_size_enum - Media bus format enumeration
* @pad: pad number, as reported by the media API
* @index: format index during enumeration
- * @code: format code (from enum v4l2_mbus_pixelcode)
+ * @code: format code (MEDIA_BUS_FMT_ definitions)
*/
struct v4l2_subdev_frame_size_enum {
__u32 index;
@@ -109,7 +109,7 @@
* struct v4l2_subdev_frame_interval_enum - Frame interval enumeration
* @pad: pad number, as reported by the media API
* @index: frame interval index during enumeration
- * @code: format code (from enum v4l2_mbus_pixelcode)
+ * @code: format code (MEDIA_BUS_FMT_ definitions)
* @width: frame width in pixels
* @height: frame height in pixels
* @interval: frame interval in seconds
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index accc4ef..7a26b65 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -178,30 +178,103 @@
/* see also http://vektor.theorem.ca/graphics/ycbcr/ */
enum v4l2_colorspace {
- /* ITU-R 601 -- broadcast NTSC/PAL */
+ /* SMPTE 170M: used for broadcast NTSC/PAL SDTV */
V4L2_COLORSPACE_SMPTE170M = 1,
- /* 1125-Line (US) HDTV */
+ /* Obsolete pre-1998 SMPTE 240M HDTV standard, superseded by Rec 709 */
V4L2_COLORSPACE_SMPTE240M = 2,
- /* HD and modern captures. */
+ /* Rec.709: used for HDTV */
V4L2_COLORSPACE_REC709 = 3,
- /* broken BT878 extents (601, luma range 16-253 instead of 16-235) */
+ /*
+ * Deprecated, do not use. No driver will ever return this. This was
+ * based on a misunderstanding of the bt878 datasheet.
+ */
V4L2_COLORSPACE_BT878 = 4,
- /* These should be useful. Assume 601 extents. */
+ /*
+ * NTSC 1953 colorspace. This only makes sense when dealing with
+ * really, really old NTSC recordings. Superseded by SMPTE 170M.
+ */
V4L2_COLORSPACE_470_SYSTEM_M = 5,
+
+ /*
+ * EBU Tech 3213 PAL/SECAM colorspace. This only makes sense when
+ * dealing with really old PAL/SECAM recordings. Superseded by
+ * SMPTE 170M.
+ */
V4L2_COLORSPACE_470_SYSTEM_BG = 6,
- /* I know there will be cameras that send this. So, this is
- * unspecified chromaticities and full 0-255 on each of the
- * Y'CbCr components
+ /*
+ * Effectively shorthand for V4L2_COLORSPACE_SRGB, V4L2_YCBCR_ENC_601
+ * and V4L2_QUANTIZATION_FULL_RANGE. To be used for (Motion-)JPEG.
*/
V4L2_COLORSPACE_JPEG = 7,
- /* For RGB colourspaces, this is probably a good start. */
+ /* For RGB colorspaces such as produces by most webcams. */
V4L2_COLORSPACE_SRGB = 8,
+
+ /* AdobeRGB colorspace */
+ V4L2_COLORSPACE_ADOBERGB = 9,
+
+ /* BT.2020 colorspace, used for UHDTV. */
+ V4L2_COLORSPACE_BT2020 = 10,
+};
+
+enum v4l2_ycbcr_encoding {
+ /*
+ * Mapping of V4L2_YCBCR_ENC_DEFAULT to actual encodings for the
+ * various colorspaces:
+ *
+ * V4L2_COLORSPACE_SMPTE170M, V4L2_COLORSPACE_470_SYSTEM_M,
+ * V4L2_COLORSPACE_470_SYSTEM_BG, V4L2_COLORSPACE_ADOBERGB and
+ * V4L2_COLORSPACE_JPEG: V4L2_YCBCR_ENC_601
+ *
+ * V4L2_COLORSPACE_REC709: V4L2_YCBCR_ENC_709
+ *
+ * V4L2_COLORSPACE_SRGB: V4L2_YCBCR_ENC_SYCC
+ *
+ * V4L2_COLORSPACE_BT2020: V4L2_YCBCR_ENC_BT2020
+ *
+ * V4L2_COLORSPACE_SMPTE240M: V4L2_YCBCR_ENC_SMPTE240M
+ */
+ V4L2_YCBCR_ENC_DEFAULT = 0,
+
+ /* ITU-R 601 -- SDTV */
+ V4L2_YCBCR_ENC_601 = 1,
+
+ /* Rec. 709 -- HDTV */
+ V4L2_YCBCR_ENC_709 = 2,
+
+ /* ITU-R 601/EN 61966-2-4 Extended Gamut -- SDTV */
+ V4L2_YCBCR_ENC_XV601 = 3,
+
+ /* Rec. 709/EN 61966-2-4 Extended Gamut -- HDTV */
+ V4L2_YCBCR_ENC_XV709 = 4,
+
+ /* sYCC (Y'CbCr encoding of sRGB) */
+ V4L2_YCBCR_ENC_SYCC = 5,
+
+ /* BT.2020 Non-constant Luminance Y'CbCr */
+ V4L2_YCBCR_ENC_BT2020 = 6,
+
+ /* BT.2020 Constant Luminance Y'CbcCrc */
+ V4L2_YCBCR_ENC_BT2020_CONST_LUM = 7,
+
+ /* SMPTE 240M -- Obsolete HDTV */
+ V4L2_YCBCR_ENC_SMPTE240M = 8,
+};
+
+enum v4l2_quantization {
+ /*
+ * The default for R'G'B' quantization is always full range. For
+ * Y'CbCr the quantization is always limited range, except for
+ * SYCC, XV601, XV709 or JPEG: those are full range.
+ */
+ V4L2_QUANTIZATION_DEFAULT = 0,
+ V4L2_QUANTIZATION_FULL_RANGE = 1,
+ V4L2_QUANTIZATION_LIM_RANGE = 2,
};
enum v4l2_priority {
@@ -294,6 +367,8 @@
__u32 colorspace; /* enum v4l2_colorspace */
__u32 priv; /* private data, depends on pixelformat */
__u32 flags; /* format flags (V4L2_PIX_FMT_FLAG_*) */
+ __u32 ycbcr_enc; /* enum v4l2_ycbcr_encoding */
+ __u32 quantization; /* enum v4l2_quantization */
};
/* Pixel format FOURCC depth Description */
@@ -1249,6 +1324,7 @@
#define V4L2_IN_CAP_DV_TIMINGS 0x00000002 /* Supports S_DV_TIMINGS */
#define V4L2_IN_CAP_CUSTOM_TIMINGS V4L2_IN_CAP_DV_TIMINGS /* For compatibility */
#define V4L2_IN_CAP_STD 0x00000004 /* Supports S_STD */
+#define V4L2_IN_CAP_NATIVE_SIZE 0x00000008 /* Supports setting native size */
/*
* V I D E O O U T P U T S
@@ -1272,6 +1348,7 @@
#define V4L2_OUT_CAP_DV_TIMINGS 0x00000002 /* Supports S_DV_TIMINGS */
#define V4L2_OUT_CAP_CUSTOM_TIMINGS V4L2_OUT_CAP_DV_TIMINGS /* For compatibility */
#define V4L2_OUT_CAP_STD 0x00000004 /* Supports S_STD */
+#define V4L2_OUT_CAP_NATIVE_SIZE 0x00000008 /* Supports setting native size */
/*
* C O N T R O L S
@@ -1777,6 +1854,8 @@
* @plane_fmt: per-plane information
* @num_planes: number of planes for this format
* @flags: format flags (V4L2_PIX_FMT_FLAG_*)
+ * @ycbcr_enc: enum v4l2_ycbcr_encoding, Y'CbCr encoding
+ * @quantization: enum v4l2_quantization, colorspace quantization
*/
struct v4l2_pix_format_mplane {
__u32 width;
@@ -1788,7 +1867,9 @@
struct v4l2_plane_pix_format plane_fmt[VIDEO_MAX_PLANES];
__u8 num_planes;
__u8 flags;
- __u8 reserved[10];
+ __u8 ycbcr_enc;
+ __u8 quantization;
+ __u8 reserved[8];
} __attribute__ ((packed));
/**
diff --git a/kconf/Makefile b/kconf/Makefile
index b307c65..9edd957 100644
--- a/kconf/Makefile
+++ b/kconf/Makefile
@@ -3,9 +3,12 @@
LXDIALOG := lxdialog/checklist.o lxdialog/inputbox.o lxdialog/menubox.o lxdialog/textbox.o lxdialog/util.o lxdialog/yesno.o
conf: conf.o zconf.tab.o
-mconf: LDFLAGS = -Wl,--add-needed $(shell ./lxdialog/check-lxdialog.sh -ldflags $(CC))
-mconf: CFLAGS += -DCURSES_LOC="<ncurses.h>" -DLOCALE
+mconf_CFLAGS := $(shell ./lxdialog/check-lxdialog.sh -ccflags) -DLOCALE
+mconf_LDFLAGS := $(shell ./lxdialog/check-lxdialog.sh -ldflags $(CC))
+mconf: CFLAGS += $(mconf_CFLAGS)
+
mconf: mconf.o zconf.tab.o $(LXDIALOG)
+ $(CC) -o mconf $^ $(mconf_LDFLAGS)
.PHONY: clean
clean:
diff --git a/net/6lowpan/Kconfig b/net/6lowpan/Kconfig
new file mode 100644
index 0000000..cb540f5
--- /dev/null
+++ b/net/6lowpan/Kconfig
@@ -0,0 +1,7 @@
+config 6LOWPAN
+ tristate "6LoWPAN Support"
+ depends on m
+ depends on IPV6
+ ---help---
+ This enables IPv6 over Low power Wireless Personal Area Network -
+ "6LoWPAN" which is supported by IEEE 802.15.4 or Bluetooth stacks.
diff --git a/net/6lowpan/Makefile b/net/6lowpan/Makefile
new file mode 100644
index 0000000..0f2fd34
--- /dev/null
+++ b/net/6lowpan/Makefile
@@ -0,0 +1,3 @@
+obj-$(CPTCFG_6LOWPAN) := 6lowpan.o
+
+6lowpan-y := iphc.o
diff --git a/net/6lowpan/iphc.c b/net/6lowpan/iphc.c
new file mode 100644
index 0000000..32ffec6
--- /dev/null
+++ b/net/6lowpan/iphc.c
@@ -0,0 +1,764 @@
+/*
+ * Copyright 2011, Siemens AG
+ * written by Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
+ */
+
+/* Based on patches from Jon Smirl <jonsmirl@gmail.com>
+ * Copyright (c) 2011 Jon Smirl <jonsmirl@gmail.com>
+ *
+ * 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.
+ *
+ * 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.
+ *
+ */
+
+/* Jon's code is based on 6lowpan implementation for Contiki which is:
+ * Copyright (c) 2008, Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <linux/bitops.h>
+#include <linux/if_arp.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <net/6lowpan.h>
+#include <net/ipv6.h>
+#include <net/af_ieee802154.h>
+
+/* Uncompress address function for source and
+ * destination address(non-multicast).
+ *
+ * address_mode is sam value or dam value.
+ */
+static int uncompress_addr(struct sk_buff *skb,
+ struct in6_addr *ipaddr, const u8 address_mode,
+ const u8 *lladdr, const u8 addr_type,
+ const u8 addr_len)
+{
+ bool fail;
+
+ switch (address_mode) {
+ case LOWPAN_IPHC_ADDR_00:
+ /* for global link addresses */
+ fail = lowpan_fetch_skb(skb, ipaddr->s6_addr, 16);
+ break;
+ case LOWPAN_IPHC_ADDR_01:
+ /* fe:80::XXXX:XXXX:XXXX:XXXX */
+ ipaddr->s6_addr[0] = 0xFE;
+ ipaddr->s6_addr[1] = 0x80;
+ fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[8], 8);
+ break;
+ case LOWPAN_IPHC_ADDR_02:
+ /* fe:80::ff:fe00:XXXX */
+ ipaddr->s6_addr[0] = 0xFE;
+ ipaddr->s6_addr[1] = 0x80;
+ ipaddr->s6_addr[11] = 0xFF;
+ ipaddr->s6_addr[12] = 0xFE;
+ fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[14], 2);
+ break;
+ case LOWPAN_IPHC_ADDR_03:
+ fail = false;
+ switch (addr_type) {
+ case IEEE802154_ADDR_LONG:
+ /* fe:80::XXXX:XXXX:XXXX:XXXX
+ * \_________________/
+ * hwaddr
+ */
+ ipaddr->s6_addr[0] = 0xFE;
+ ipaddr->s6_addr[1] = 0x80;
+ memcpy(&ipaddr->s6_addr[8], lladdr, addr_len);
+ /* second bit-flip (Universe/Local)
+ * is done according RFC2464
+ */
+ ipaddr->s6_addr[8] ^= 0x02;
+ break;
+ case IEEE802154_ADDR_SHORT:
+ /* fe:80::ff:fe00:XXXX
+ * \__/
+ * short_addr
+ *
+ * Universe/Local bit is zero.
+ */
+ ipaddr->s6_addr[0] = 0xFE;
+ ipaddr->s6_addr[1] = 0x80;
+ ipaddr->s6_addr[11] = 0xFF;
+ ipaddr->s6_addr[12] = 0xFE;
+ ipaddr->s6_addr16[7] = htons(*((u16 *)lladdr));
+ break;
+ default:
+ pr_debug("Invalid addr_type set\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ pr_debug("Invalid address mode value: 0x%x\n", address_mode);
+ return -EINVAL;
+ }
+
+ if (fail) {
+ pr_debug("Failed to fetch skb data\n");
+ return -EIO;
+ }
+
+ raw_dump_inline(NULL, "Reconstructed ipv6 addr is",
+ ipaddr->s6_addr, 16);
+
+ return 0;
+}
+
+/* Uncompress address function for source context
+ * based address(non-multicast).
+ */
+static int uncompress_context_based_src_addr(struct sk_buff *skb,
+ struct in6_addr *ipaddr,
+ const u8 sam)
+{
+ switch (sam) {
+ case LOWPAN_IPHC_ADDR_00:
+ /* unspec address ::
+ * Do nothing, address is already ::
+ */
+ break;
+ case LOWPAN_IPHC_ADDR_01:
+ /* TODO */
+ case LOWPAN_IPHC_ADDR_02:
+ /* TODO */
+ case LOWPAN_IPHC_ADDR_03:
+ /* TODO */
+ netdev_warn(skb->dev, "SAM value 0x%x not supported\n", sam);
+ return -EINVAL;
+ default:
+ pr_debug("Invalid sam value: 0x%x\n", sam);
+ return -EINVAL;
+ }
+
+ raw_dump_inline(NULL,
+ "Reconstructed context based ipv6 src addr is",
+ ipaddr->s6_addr, 16);
+
+ return 0;
+}
+
+/* Uncompress function for multicast destination address,
+ * when M bit is set.
+ */
+static int lowpan_uncompress_multicast_daddr(struct sk_buff *skb,
+ struct in6_addr *ipaddr,
+ const u8 dam)
+{
+ bool fail;
+
+ switch (dam) {
+ case LOWPAN_IPHC_DAM_00:
+ /* 00: 128 bits. The full address
+ * is carried in-line.
+ */
+ fail = lowpan_fetch_skb(skb, ipaddr->s6_addr, 16);
+ break;
+ case LOWPAN_IPHC_DAM_01:
+ /* 01: 48 bits. The address takes
+ * the form ffXX::00XX:XXXX:XXXX.
+ */
+ ipaddr->s6_addr[0] = 0xFF;
+ fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[1], 1);
+ fail |= lowpan_fetch_skb(skb, &ipaddr->s6_addr[11], 5);
+ break;
+ case LOWPAN_IPHC_DAM_10:
+ /* 10: 32 bits. The address takes
+ * the form ffXX::00XX:XXXX.
+ */
+ ipaddr->s6_addr[0] = 0xFF;
+ fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[1], 1);
+ fail |= lowpan_fetch_skb(skb, &ipaddr->s6_addr[13], 3);
+ break;
+ case LOWPAN_IPHC_DAM_11:
+ /* 11: 8 bits. The address takes
+ * the form ff02::00XX.
+ */
+ ipaddr->s6_addr[0] = 0xFF;
+ ipaddr->s6_addr[1] = 0x02;
+ fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[15], 1);
+ break;
+ default:
+ pr_debug("DAM value has a wrong value: 0x%x\n", dam);
+ return -EINVAL;
+ }
+
+ if (fail) {
+ pr_debug("Failed to fetch skb data\n");
+ return -EIO;
+ }
+
+ raw_dump_inline(NULL, "Reconstructed ipv6 multicast addr is",
+ ipaddr->s6_addr, 16);
+
+ return 0;
+}
+
+static int uncompress_udp_header(struct sk_buff *skb, struct udphdr *uh)
+{
+ bool fail;
+ u8 tmp = 0, val = 0;
+
+ fail = lowpan_fetch_skb(skb, &tmp, sizeof(tmp));
+
+ if ((tmp & LOWPAN_NHC_UDP_MASK) == LOWPAN_NHC_UDP_ID) {
+ pr_debug("UDP header uncompression\n");
+ switch (tmp & LOWPAN_NHC_UDP_CS_P_11) {
+ case LOWPAN_NHC_UDP_CS_P_00:
+ fail |= lowpan_fetch_skb(skb, &uh->source,
+ sizeof(uh->source));
+ fail |= lowpan_fetch_skb(skb, &uh->dest,
+ sizeof(uh->dest));
+ break;
+ case LOWPAN_NHC_UDP_CS_P_01:
+ fail |= lowpan_fetch_skb(skb, &uh->source,
+ sizeof(uh->source));
+ fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
+ uh->dest = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
+ break;
+ case LOWPAN_NHC_UDP_CS_P_10:
+ fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
+ uh->source = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
+ fail |= lowpan_fetch_skb(skb, &uh->dest,
+ sizeof(uh->dest));
+ break;
+ case LOWPAN_NHC_UDP_CS_P_11:
+ fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
+ uh->source = htons(LOWPAN_NHC_UDP_4BIT_PORT +
+ (val >> 4));
+ uh->dest = htons(LOWPAN_NHC_UDP_4BIT_PORT +
+ (val & 0x0f));
+ break;
+ default:
+ pr_debug("ERROR: unknown UDP format\n");
+ goto err;
+ }
+
+ pr_debug("uncompressed UDP ports: src = %d, dst = %d\n",
+ ntohs(uh->source), ntohs(uh->dest));
+
+ /* checksum */
+ if (tmp & LOWPAN_NHC_UDP_CS_C) {
+ pr_debug_ratelimited("checksum elided currently not supported\n");
+ goto err;
+ } else {
+ fail |= lowpan_fetch_skb(skb, &uh->check,
+ sizeof(uh->check));
+ }
+
+ /* UDP length needs to be infered from the lower layers
+ * here, we obtain the hint from the remaining size of the
+ * frame
+ */
+ uh->len = htons(skb->len + sizeof(struct udphdr));
+ pr_debug("uncompressed UDP length: src = %d", ntohs(uh->len));
+ } else {
+ pr_debug("ERROR: unsupported NH format\n");
+ goto err;
+ }
+
+ if (fail)
+ goto err;
+
+ return 0;
+err:
+ return -EINVAL;
+}
+
+/* TTL uncompression values */
+static const u8 lowpan_ttl_values[] = { 0, 1, 64, 255 };
+
+int
+lowpan_header_decompress(struct sk_buff *skb, struct net_device *dev,
+ const u8 *saddr, const u8 saddr_type,
+ const u8 saddr_len, const u8 *daddr,
+ const u8 daddr_type, const u8 daddr_len,
+ u8 iphc0, u8 iphc1)
+{
+ struct ipv6hdr hdr = {};
+ u8 tmp, num_context = 0;
+ int err;
+
+ raw_dump_table(__func__, "raw skb data dump uncompressed",
+ skb->data, skb->len);
+
+ /* another if the CID flag is set */
+ if (iphc1 & LOWPAN_IPHC_CID) {
+ pr_debug("CID flag is set, increase header with one\n");
+ if (lowpan_fetch_skb(skb, &num_context, sizeof(num_context)))
+ return -EINVAL;
+ }
+
+ hdr.version = 6;
+
+ /* Traffic Class and Flow Label */
+ switch ((iphc0 & LOWPAN_IPHC_TF) >> 3) {
+ /* Traffic Class and FLow Label carried in-line
+ * ECN + DSCP + 4-bit Pad + Flow Label (4 bytes)
+ */
+ case 0: /* 00b */
+ if (lowpan_fetch_skb(skb, &tmp, sizeof(tmp)))
+ return -EINVAL;
+
+ memcpy(&hdr.flow_lbl, &skb->data[0], 3);
+ skb_pull(skb, 3);
+ hdr.priority = ((tmp >> 2) & 0x0f);
+ hdr.flow_lbl[0] = ((tmp >> 2) & 0x30) | (tmp << 6) |
+ (hdr.flow_lbl[0] & 0x0f);
+ break;
+ /* Traffic class carried in-line
+ * ECN + DSCP (1 byte), Flow Label is elided
+ */
+ case 2: /* 10b */
+ if (lowpan_fetch_skb(skb, &tmp, sizeof(tmp)))
+ return -EINVAL;
+
+ hdr.priority = ((tmp >> 2) & 0x0f);
+ hdr.flow_lbl[0] = ((tmp << 6) & 0xC0) | ((tmp >> 2) & 0x30);
+ break;
+ /* Flow Label carried in-line
+ * ECN + 2-bit Pad + Flow Label (3 bytes), DSCP is elided
+ */
+ case 1: /* 01b */
+ if (lowpan_fetch_skb(skb, &tmp, sizeof(tmp)))
+ return -EINVAL;
+
+ hdr.flow_lbl[0] = (skb->data[0] & 0x0F) | ((tmp >> 2) & 0x30);
+ memcpy(&hdr.flow_lbl[1], &skb->data[0], 2);
+ skb_pull(skb, 2);
+ break;
+ /* Traffic Class and Flow Label are elided */
+ case 3: /* 11b */
+ break;
+ default:
+ break;
+ }
+
+ /* Next Header */
+ if ((iphc0 & LOWPAN_IPHC_NH_C) == 0) {
+ /* Next header is carried inline */
+ if (lowpan_fetch_skb(skb, &hdr.nexthdr, sizeof(hdr.nexthdr)))
+ return -EINVAL;
+
+ pr_debug("NH flag is set, next header carried inline: %02x\n",
+ hdr.nexthdr);
+ }
+
+ /* Hop Limit */
+ if ((iphc0 & 0x03) != LOWPAN_IPHC_TTL_I) {
+ hdr.hop_limit = lowpan_ttl_values[iphc0 & 0x03];
+ } else {
+ if (lowpan_fetch_skb(skb, &hdr.hop_limit,
+ sizeof(hdr.hop_limit)))
+ return -EINVAL;
+ }
+
+ /* Extract SAM to the tmp variable */
+ tmp = ((iphc1 & LOWPAN_IPHC_SAM) >> LOWPAN_IPHC_SAM_BIT) & 0x03;
+
+ if (iphc1 & LOWPAN_IPHC_SAC) {
+ /* Source address context based uncompression */
+ pr_debug("SAC bit is set. Handle context based source address.\n");
+ err = uncompress_context_based_src_addr(skb, &hdr.saddr, tmp);
+ } else {
+ /* Source address uncompression */
+ pr_debug("source address stateless compression\n");
+ err = uncompress_addr(skb, &hdr.saddr, tmp, saddr,
+ saddr_type, saddr_len);
+ }
+
+ /* Check on error of previous branch */
+ if (err)
+ return -EINVAL;
+
+ /* Extract DAM to the tmp variable */
+ tmp = ((iphc1 & LOWPAN_IPHC_DAM_11) >> LOWPAN_IPHC_DAM_BIT) & 0x03;
+
+ /* check for Multicast Compression */
+ if (iphc1 & LOWPAN_IPHC_M) {
+ if (iphc1 & LOWPAN_IPHC_DAC) {
+ pr_debug("dest: context-based mcast compression\n");
+ /* TODO: implement this */
+ } else {
+ err = lowpan_uncompress_multicast_daddr(skb, &hdr.daddr,
+ tmp);
+
+ if (err)
+ return -EINVAL;
+ }
+ } else {
+ err = uncompress_addr(skb, &hdr.daddr, tmp, daddr,
+ daddr_type, daddr_len);
+ pr_debug("dest: stateless compression mode %d dest %pI6c\n",
+ tmp, &hdr.daddr);
+ if (err)
+ return -EINVAL;
+ }
+
+ /* UDP data uncompression */
+ if (iphc0 & LOWPAN_IPHC_NH_C) {
+ struct udphdr uh;
+ const int needed = sizeof(struct udphdr) + sizeof(hdr);
+
+ if (uncompress_udp_header(skb, &uh))
+ return -EINVAL;
+
+ /* replace the compressed UDP head by the uncompressed UDP
+ * header
+ */
+ err = skb_cow(skb, needed);
+ if (unlikely(err))
+ return err;
+
+ skb_push(skb, sizeof(struct udphdr));
+ skb_reset_transport_header(skb);
+ skb_copy_to_linear_data(skb, &uh, sizeof(struct udphdr));
+
+ raw_dump_table(__func__, "raw UDP header dump",
+ (u8 *)&uh, sizeof(uh));
+
+ hdr.nexthdr = UIP_PROTO_UDP;
+ } else {
+ err = skb_cow(skb, sizeof(hdr));
+ if (unlikely(err))
+ return err;
+ }
+
+ hdr.payload_len = htons(skb->len);
+
+ pr_debug("skb headroom size = %d, data length = %d\n",
+ skb_headroom(skb), skb->len);
+
+ pr_debug("IPv6 header dump:\n\tversion = %d\n\tlength = %d\n\t"
+ "nexthdr = 0x%02x\n\thop_lim = %d\n\tdest = %pI6c\n",
+ hdr.version, ntohs(hdr.payload_len), hdr.nexthdr,
+ hdr.hop_limit, &hdr.daddr);
+
+ skb_push(skb, sizeof(hdr));
+ skb_reset_network_header(skb);
+ skb_copy_to_linear_data(skb, &hdr, sizeof(hdr));
+
+ raw_dump_table(__func__, "raw header dump", (u8 *)&hdr, sizeof(hdr));
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(lowpan_header_decompress);
+
+static u8 lowpan_compress_addr_64(u8 **hc_ptr, u8 shift,
+ const struct in6_addr *ipaddr,
+ const unsigned char *lladdr)
+{
+ u8 val = 0;
+
+ if (is_addr_mac_addr_based(ipaddr, lladdr)) {
+ val = 3; /* 0-bits */
+ pr_debug("address compression 0 bits\n");
+ } else if (lowpan_is_iid_16_bit_compressable(ipaddr)) {
+ /* compress IID to 16 bits xxxx::XXXX */
+ lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr16[7], 2);
+ val = 2; /* 16-bits */
+ raw_dump_inline(NULL, "Compressed ipv6 addr is (16 bits)",
+ *hc_ptr - 2, 2);
+ } else {
+ /* do not compress IID => xxxx::IID */
+ lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr16[4], 8);
+ val = 1; /* 64-bits */
+ raw_dump_inline(NULL, "Compressed ipv6 addr is (64 bits)",
+ *hc_ptr - 8, 8);
+ }
+
+ return rol8(val, shift);
+}
+
+static void compress_udp_header(u8 **hc_ptr, struct sk_buff *skb)
+{
+ struct udphdr *uh;
+ u8 tmp;
+
+ /* In the case of RAW sockets the transport header is not set by
+ * the ip6 stack so we must set it ourselves
+ */
+ if (skb->transport_header == skb->network_header)
+ skb_set_transport_header(skb, sizeof(struct ipv6hdr));
+
+ uh = udp_hdr(skb);
+
+ if (((ntohs(uh->source) & LOWPAN_NHC_UDP_4BIT_MASK) ==
+ LOWPAN_NHC_UDP_4BIT_PORT) &&
+ ((ntohs(uh->dest) & LOWPAN_NHC_UDP_4BIT_MASK) ==
+ LOWPAN_NHC_UDP_4BIT_PORT)) {
+ pr_debug("UDP header: both ports compression to 4 bits\n");
+ /* compression value */
+ tmp = LOWPAN_NHC_UDP_CS_P_11;
+ lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
+ /* source and destination port */
+ tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_4BIT_PORT +
+ ((ntohs(uh->source) - LOWPAN_NHC_UDP_4BIT_PORT) << 4);
+ lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
+ } else if ((ntohs(uh->dest) & LOWPAN_NHC_UDP_8BIT_MASK) ==
+ LOWPAN_NHC_UDP_8BIT_PORT) {
+ pr_debug("UDP header: remove 8 bits of dest\n");
+ /* compression value */
+ tmp = LOWPAN_NHC_UDP_CS_P_01;
+ lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
+ /* source port */
+ lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source));
+ /* destination port */
+ tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_8BIT_PORT;
+ lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
+ } else if ((ntohs(uh->source) & LOWPAN_NHC_UDP_8BIT_MASK) ==
+ LOWPAN_NHC_UDP_8BIT_PORT) {
+ pr_debug("UDP header: remove 8 bits of source\n");
+ /* compression value */
+ tmp = LOWPAN_NHC_UDP_CS_P_10;
+ lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
+ /* source port */
+ tmp = ntohs(uh->source) - LOWPAN_NHC_UDP_8BIT_PORT;
+ lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
+ /* destination port */
+ lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest));
+ } else {
+ pr_debug("UDP header: can't compress\n");
+ /* compression value */
+ tmp = LOWPAN_NHC_UDP_CS_P_00;
+ lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
+ /* source port */
+ lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source));
+ /* destination port */
+ lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest));
+ }
+
+ /* checksum is always inline */
+ lowpan_push_hc_data(hc_ptr, &uh->check, sizeof(uh->check));
+
+ /* skip the UDP header */
+ skb_pull(skb, sizeof(struct udphdr));
+}
+
+int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, const void *_daddr,
+ const void *_saddr, unsigned int len)
+{
+ u8 tmp, iphc0, iphc1, *hc_ptr;
+ struct ipv6hdr *hdr;
+ u8 head[100] = {};
+ int addr_type;
+
+ if (type != ETH_P_IPV6)
+ return -EINVAL;
+
+ hdr = ipv6_hdr(skb);
+ hc_ptr = head + 2;
+
+ pr_debug("IPv6 header dump:\n\tversion = %d\n\tlength = %d\n"
+ "\tnexthdr = 0x%02x\n\thop_lim = %d\n\tdest = %pI6c\n",
+ hdr->version, ntohs(hdr->payload_len), hdr->nexthdr,
+ hdr->hop_limit, &hdr->daddr);
+
+ raw_dump_table(__func__, "raw skb network header dump",
+ skb_network_header(skb), sizeof(struct ipv6hdr));
+
+ /* As we copy some bit-length fields, in the IPHC encoding bytes,
+ * we sometimes use |=
+ * If the field is 0, and the current bit value in memory is 1,
+ * this does not work. We therefore reset the IPHC encoding here
+ */
+ iphc0 = LOWPAN_DISPATCH_IPHC;
+ iphc1 = 0;
+
+ /* TODO: context lookup */
+
+ raw_dump_inline(__func__, "saddr",
+ (unsigned char *)_saddr, IEEE802154_ADDR_LEN);
+ raw_dump_inline(__func__, "daddr",
+ (unsigned char *)_daddr, IEEE802154_ADDR_LEN);
+
+ raw_dump_table(__func__, "sending raw skb network uncompressed packet",
+ skb->data, skb->len);
+
+ /* Traffic class, flow label
+ * If flow label is 0, compress it. If traffic class is 0, compress it
+ * We have to process both in the same time as the offset of traffic
+ * class depends on the presence of version and flow label
+ */
+
+ /* hc format of TC is ECN | DSCP , original one is DSCP | ECN */
+ tmp = (hdr->priority << 4) | (hdr->flow_lbl[0] >> 4);
+ tmp = ((tmp & 0x03) << 6) | (tmp >> 2);
+
+ if (((hdr->flow_lbl[0] & 0x0F) == 0) &&
+ (hdr->flow_lbl[1] == 0) && (hdr->flow_lbl[2] == 0)) {
+ /* flow label can be compressed */
+ iphc0 |= LOWPAN_IPHC_FL_C;
+ if ((hdr->priority == 0) &&
+ ((hdr->flow_lbl[0] & 0xF0) == 0)) {
+ /* compress (elide) all */
+ iphc0 |= LOWPAN_IPHC_TC_C;
+ } else {
+ /* compress only the flow label */
+ *hc_ptr = tmp;
+ hc_ptr += 1;
+ }
+ } else {
+ /* Flow label cannot be compressed */
+ if ((hdr->priority == 0) &&
+ ((hdr->flow_lbl[0] & 0xF0) == 0)) {
+ /* compress only traffic class */
+ iphc0 |= LOWPAN_IPHC_TC_C;
+ *hc_ptr = (tmp & 0xc0) | (hdr->flow_lbl[0] & 0x0F);
+ memcpy(hc_ptr + 1, &hdr->flow_lbl[1], 2);
+ hc_ptr += 3;
+ } else {
+ /* compress nothing */
+ memcpy(hc_ptr, hdr, 4);
+ /* replace the top byte with new ECN | DSCP format */
+ *hc_ptr = tmp;
+ hc_ptr += 4;
+ }
+ }
+
+ /* NOTE: payload length is always compressed */
+
+ /* Next Header is compress if UDP */
+ if (hdr->nexthdr == UIP_PROTO_UDP)
+ iphc0 |= LOWPAN_IPHC_NH_C;
+
+ if ((iphc0 & LOWPAN_IPHC_NH_C) == 0)
+ lowpan_push_hc_data(&hc_ptr, &hdr->nexthdr,
+ sizeof(hdr->nexthdr));
+
+ /* Hop limit
+ * if 1: compress, encoding is 01
+ * if 64: compress, encoding is 10
+ * if 255: compress, encoding is 11
+ * else do not compress
+ */
+ switch (hdr->hop_limit) {
+ case 1:
+ iphc0 |= LOWPAN_IPHC_TTL_1;
+ break;
+ case 64:
+ iphc0 |= LOWPAN_IPHC_TTL_64;
+ break;
+ case 255:
+ iphc0 |= LOWPAN_IPHC_TTL_255;
+ break;
+ default:
+ lowpan_push_hc_data(&hc_ptr, &hdr->hop_limit,
+ sizeof(hdr->hop_limit));
+ }
+
+ addr_type = ipv6_addr_type(&hdr->saddr);
+ /* source address compression */
+ if (addr_type == IPV6_ADDR_ANY) {
+ pr_debug("source address is unspecified, setting SAC\n");
+ iphc1 |= LOWPAN_IPHC_SAC;
+ } else {
+ if (addr_type & IPV6_ADDR_LINKLOCAL) {
+ iphc1 |= lowpan_compress_addr_64(&hc_ptr,
+ LOWPAN_IPHC_SAM_BIT,
+ &hdr->saddr, _saddr);
+ pr_debug("source address unicast link-local %pI6c iphc1 0x%02x\n",
+ &hdr->saddr, iphc1);
+ } else {
+ pr_debug("send the full source address\n");
+ lowpan_push_hc_data(&hc_ptr, hdr->saddr.s6_addr, 16);
+ }
+ }
+
+ addr_type = ipv6_addr_type(&hdr->daddr);
+ /* destination address compression */
+ if (addr_type & IPV6_ADDR_MULTICAST) {
+ pr_debug("destination address is multicast: ");
+ iphc1 |= LOWPAN_IPHC_M;
+ if (lowpan_is_mcast_addr_compressable8(&hdr->daddr)) {
+ pr_debug("compressed to 1 octet\n");
+ iphc1 |= LOWPAN_IPHC_DAM_11;
+ /* use last byte */
+ lowpan_push_hc_data(&hc_ptr,
+ &hdr->daddr.s6_addr[15], 1);
+ } else if (lowpan_is_mcast_addr_compressable32(&hdr->daddr)) {
+ pr_debug("compressed to 4 octets\n");
+ iphc1 |= LOWPAN_IPHC_DAM_10;
+ /* second byte + the last three */
+ lowpan_push_hc_data(&hc_ptr,
+ &hdr->daddr.s6_addr[1], 1);
+ lowpan_push_hc_data(&hc_ptr,
+ &hdr->daddr.s6_addr[13], 3);
+ } else if (lowpan_is_mcast_addr_compressable48(&hdr->daddr)) {
+ pr_debug("compressed to 6 octets\n");
+ iphc1 |= LOWPAN_IPHC_DAM_01;
+ /* second byte + the last five */
+ lowpan_push_hc_data(&hc_ptr,
+ &hdr->daddr.s6_addr[1], 1);
+ lowpan_push_hc_data(&hc_ptr,
+ &hdr->daddr.s6_addr[11], 5);
+ } else {
+ pr_debug("using full address\n");
+ iphc1 |= LOWPAN_IPHC_DAM_00;
+ lowpan_push_hc_data(&hc_ptr, hdr->daddr.s6_addr, 16);
+ }
+ } else {
+ if (addr_type & IPV6_ADDR_LINKLOCAL) {
+ /* TODO: context lookup */
+ iphc1 |= lowpan_compress_addr_64(&hc_ptr,
+ LOWPAN_IPHC_DAM_BIT, &hdr->daddr, _daddr);
+ pr_debug("dest address unicast link-local %pI6c "
+ "iphc1 0x%02x\n", &hdr->daddr, iphc1);
+ } else {
+ pr_debug("dest address unicast %pI6c\n", &hdr->daddr);
+ lowpan_push_hc_data(&hc_ptr, hdr->daddr.s6_addr, 16);
+ }
+ }
+
+ /* UDP header compression */
+ if (hdr->nexthdr == UIP_PROTO_UDP)
+ compress_udp_header(&hc_ptr, skb);
+
+ head[0] = iphc0;
+ head[1] = iphc1;
+
+ skb_pull(skb, sizeof(struct ipv6hdr));
+ skb_reset_transport_header(skb);
+ memcpy(skb_push(skb, hc_ptr - head), head, hc_ptr - head);
+ skb_reset_network_header(skb);
+
+ pr_debug("header len %d skb %u\n", (int)(hc_ptr - head), skb->len);
+
+ raw_dump_table(__func__, "raw skb data dump compressed",
+ skb->data, skb->len);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(lowpan_header_compress);
+
+MODULE_LICENSE("GPL");
diff --git a/net/Kconfig b/net/Kconfig
index 6272420..ff9ffc1 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -6,7 +6,7 @@
bool "Networking support"
select NLATTR
select GENERIC_NET_UTILS
- select ANON_INODES
+ select BPF
---help---
Unless you really know what you are doing, you should say Y here.
The reason is that some programs need kernel networking support even
@@ -228,6 +228,7 @@
source "net/netlink/Kconfig"
source "net/mpls/Kconfig"
source "net/hsr/Kconfig"
+source "net/switchdev/Kconfig"
config RPS
boolean
diff --git a/net/Makefile b/net/Makefile
index 23a0b60..76f0349 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -5,8 +5,6 @@
# Rewritten to use lists instead of if-statements.
#
-obj-y := nonet.o
-
obj-$(CONFIG_NET) := socket.o core/
tmp-$(CONFIG_COMPAT) := compat.o
@@ -57,7 +55,7 @@
ifneq ($(CONFIG_DCB),)
obj-y += dcb/
endif
-obj-$(CONFIG_6LOWPAN) += 6lowpan/
+obj-$(CPTCFG_6LOWPAN) += 6lowpan/
obj-$(CPTCFG_IEEE802154) += ieee802154/
obj-$(CPTCFG_MAC802154) += mac802154/
@@ -73,3 +71,6 @@
obj-$(CONFIG_VSOCKETS) += vmw_vsock/
obj-$(CONFIG_NET_MPLS_GSO) += mpls/
obj-$(CONFIG_HSR) += hsr/
+ifneq ($(CONFIG_NET_SWITCHDEV),)
+obj-y += switchdev/
+endif
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index c2e0d14..8ea2d68 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -18,6 +18,7 @@
#include <linux/debugfs.h>
#include <net/ipv6.h>
+#include <net/ip6_fib.h>
#include <net/ip6_route.h>
#include <net/addrconf.h>
@@ -53,7 +54,7 @@
* The list contains struct lowpan_dev elements.
*/
static LIST_HEAD(bt_6lowpan_devices);
-static DEFINE_RWLOCK(devices_lock);
+static DEFINE_SPINLOCK(devices_lock);
/* If psm is set to 0 (default value), then 6lowpan is disabled.
* Other values are used to indicate a Protocol Service Multiplexer
@@ -67,6 +68,7 @@
struct lowpan_peer {
struct list_head list;
+ struct rcu_head rcu;
struct l2cap_chan *chan;
/* peer addresses in various formats */
@@ -93,13 +95,14 @@
static inline void peer_add(struct lowpan_dev *dev, struct lowpan_peer *peer)
{
- list_add(&peer->list, &dev->peers);
+ list_add_rcu(&peer->list, &dev->peers);
atomic_inc(&dev->peer_count);
}
static inline bool peer_del(struct lowpan_dev *dev, struct lowpan_peer *peer)
{
- list_del(&peer->list);
+ list_del_rcu(&peer->list);
+ kfree_rcu(peer, rcu);
module_put(THIS_MODULE);
@@ -114,31 +117,37 @@
static inline struct lowpan_peer *peer_lookup_ba(struct lowpan_dev *dev,
bdaddr_t *ba, __u8 type)
{
- struct lowpan_peer *peer, *tmp;
+ struct lowpan_peer *peer;
BT_DBG("peers %d addr %pMR type %d", atomic_read(&dev->peer_count),
ba, type);
- list_for_each_entry_safe(peer, tmp, &dev->peers, list) {
+ rcu_read_lock();
+
+ list_for_each_entry_rcu(peer, &dev->peers, list) {
BT_DBG("dst addr %pMR dst type %d",
&peer->chan->dst, peer->chan->dst_type);
if (bacmp(&peer->chan->dst, ba))
continue;
- if (type == peer->chan->dst_type)
+ if (type == peer->chan->dst_type) {
+ rcu_read_unlock();
return peer;
+ }
}
+ rcu_read_unlock();
+
return NULL;
}
-static inline struct lowpan_peer *peer_lookup_chan(struct lowpan_dev *dev,
- struct l2cap_chan *chan)
+static inline struct lowpan_peer *__peer_lookup_chan(struct lowpan_dev *dev,
+ struct l2cap_chan *chan)
{
- struct lowpan_peer *peer, *tmp;
+ struct lowpan_peer *peer;
- list_for_each_entry_safe(peer, tmp, &dev->peers, list) {
+ list_for_each_entry_rcu(peer, &dev->peers, list) {
if (peer->chan == chan)
return peer;
}
@@ -146,12 +155,12 @@
return NULL;
}
-static inline struct lowpan_peer *peer_lookup_conn(struct lowpan_dev *dev,
- struct l2cap_conn *conn)
+static inline struct lowpan_peer *__peer_lookup_conn(struct lowpan_dev *dev,
+ struct l2cap_conn *conn)
{
- struct lowpan_peer *peer, *tmp;
+ struct lowpan_peer *peer;
- list_for_each_entry_safe(peer, tmp, &dev->peers, list) {
+ list_for_each_entry_rcu(peer, &dev->peers, list) {
if (peer->chan->conn == conn)
return peer;
}
@@ -163,7 +172,7 @@
struct in6_addr *daddr,
struct sk_buff *skb)
{
- struct lowpan_peer *peer, *tmp;
+ struct lowpan_peer *peer;
struct in6_addr *nexthop;
struct rt6_info *rt = (struct rt6_info *)skb_dst(skb);
int count = atomic_read(&dev->peer_count);
@@ -174,9 +183,13 @@
* send the packet. If only one peer exists, then we can send the
* packet right away.
*/
- if (count == 1)
- return list_first_entry(&dev->peers, struct lowpan_peer,
- list);
+ if (count == 1) {
+ rcu_read_lock();
+ peer = list_first_or_null_rcu(&dev->peers, struct lowpan_peer,
+ list);
+ rcu_read_unlock();
+ return peer;
+ }
if (!rt) {
nexthop = &lowpan_cb(skb)->gw;
@@ -195,53 +208,57 @@
BT_DBG("gw %pI6c", nexthop);
- list_for_each_entry_safe(peer, tmp, &dev->peers, list) {
+ rcu_read_lock();
+
+ list_for_each_entry_rcu(peer, &dev->peers, list) {
BT_DBG("dst addr %pMR dst type %d ip %pI6c",
&peer->chan->dst, peer->chan->dst_type,
&peer->peer_addr);
- if (!ipv6_addr_cmp(&peer->peer_addr, nexthop))
+ if (!ipv6_addr_cmp(&peer->peer_addr, nexthop)) {
+ rcu_read_unlock();
return peer;
+ }
}
+ rcu_read_unlock();
+
return NULL;
}
static struct lowpan_peer *lookup_peer(struct l2cap_conn *conn)
{
- struct lowpan_dev *entry, *tmp;
+ struct lowpan_dev *entry;
struct lowpan_peer *peer = NULL;
- unsigned long flags;
- read_lock_irqsave(&devices_lock, flags);
+ rcu_read_lock();
- list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
- peer = peer_lookup_conn(entry, conn);
+ list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
+ peer = __peer_lookup_conn(entry, conn);
if (peer)
break;
}
- read_unlock_irqrestore(&devices_lock, flags);
+ rcu_read_unlock();
return peer;
}
static struct lowpan_dev *lookup_dev(struct l2cap_conn *conn)
{
- struct lowpan_dev *entry, *tmp;
+ struct lowpan_dev *entry;
struct lowpan_dev *dev = NULL;
- unsigned long flags;
- read_lock_irqsave(&devices_lock, flags);
+ rcu_read_lock();
- list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
+ list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
if (conn->hcon->hdev == entry->hdev) {
dev = entry;
break;
}
}
- read_unlock_irqrestore(&devices_lock, flags);
+ rcu_read_unlock();
return dev;
}
@@ -249,59 +266,49 @@
static int give_skb_to_upper(struct sk_buff *skb, struct net_device *dev)
{
struct sk_buff *skb_cp;
- int ret;
skb_cp = skb_copy(skb, GFP_ATOMIC);
if (!skb_cp)
- return -ENOMEM;
-
- ret = netif_rx(skb_cp);
- if (ret < 0) {
- BT_DBG("receive skb %d", ret);
return NET_RX_DROP;
- }
- return ret;
+ return netif_rx(skb_cp);
}
-static int process_data(struct sk_buff *skb, struct net_device *netdev,
- struct l2cap_chan *chan)
+static int iphc_decompress(struct sk_buff *skb, struct net_device *netdev,
+ struct l2cap_chan *chan)
{
const u8 *saddr, *daddr;
u8 iphc0, iphc1;
struct lowpan_dev *dev;
struct lowpan_peer *peer;
- unsigned long flags;
dev = lowpan_dev(netdev);
- read_lock_irqsave(&devices_lock, flags);
- peer = peer_lookup_chan(dev, chan);
- read_unlock_irqrestore(&devices_lock, flags);
+ rcu_read_lock();
+ peer = __peer_lookup_chan(dev, chan);
+ rcu_read_unlock();
if (!peer)
- goto drop;
+ return -EINVAL;
saddr = peer->eui64_addr;
daddr = dev->netdev->dev_addr;
/* at least two bytes will be used for the encoding */
if (skb->len < 2)
- goto drop;
+ return -EINVAL;
if (lowpan_fetch_skb_u8(skb, &iphc0))
- goto drop;
+ return -EINVAL;
if (lowpan_fetch_skb_u8(skb, &iphc1))
- goto drop;
+ return -EINVAL;
- return lowpan_process_data(skb, netdev,
- saddr, IEEE802154_ADDR_LONG, EUI64_ADDR_LEN,
- daddr, IEEE802154_ADDR_LONG, EUI64_ADDR_LEN,
- iphc0, iphc1, give_skb_to_upper);
+ return lowpan_header_decompress(skb, netdev,
+ saddr, IEEE802154_ADDR_LONG,
+ EUI64_ADDR_LEN, daddr,
+ IEEE802154_ADDR_LONG, EUI64_ADDR_LEN,
+ iphc0, iphc1);
-drop:
- kfree_skb(skb);
- return -EINVAL;
}
static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
@@ -316,6 +323,10 @@
if (dev->type != ARPHRD_6LOWPAN)
goto drop;
+ skb = skb_share_check(skb, GFP_ATOMIC);
+ if (!skb)
+ goto drop;
+
/* check that it's our buffer */
if (skb->data[0] == LOWPAN_DISPATCH_IPV6) {
/* Copy the packet so that the IPv6 header is
@@ -340,8 +351,8 @@
dev->stats.rx_bytes += skb->len;
dev->stats.rx_packets++;
- kfree_skb(local_skb);
- kfree_skb(skb);
+ consume_skb(local_skb);
+ consume_skb(skb);
} else {
switch (skb->data[0] & 0xe0) {
case LOWPAN_DISPATCH_IPHC: /* ipv6 datagram */
@@ -349,14 +360,27 @@
if (!local_skb)
goto drop;
- ret = process_data(local_skb, dev, chan);
- if (ret != NET_RX_SUCCESS)
+ ret = iphc_decompress(local_skb, dev, chan);
+ if (ret < 0) {
+ kfree_skb(local_skb);
goto drop;
+ }
+
+ local_skb->protocol = htons(ETH_P_IPV6);
+ local_skb->pkt_type = PACKET_HOST;
+ local_skb->dev = dev;
+
+ if (give_skb_to_upper(local_skb, dev)
+ != NET_RX_SUCCESS) {
+ kfree_skb(local_skb);
+ goto drop;
+ }
dev->stats.rx_bytes += skb->len;
dev->stats.rx_packets++;
- kfree_skb(skb);
+ consume_skb(local_skb);
+ consume_skb(skb);
break;
default:
break;
@@ -443,7 +467,6 @@
if (ipv6_addr_is_multicast(&ipv6_daddr)) {
lowpan_cb(skb)->chan = NULL;
} else {
- unsigned long flags;
u8 addr_type;
/* Get destination BT device from skb.
@@ -454,19 +477,14 @@
BT_DBG("dest addr %pMR type %d IP %pI6c", &addr,
addr_type, &ipv6_daddr);
- read_lock_irqsave(&devices_lock, flags);
peer = peer_lookup_ba(dev, &addr, addr_type);
- read_unlock_irqrestore(&devices_lock, flags);
-
if (!peer) {
/* The packet might be sent to 6lowpan interface
* because of routing (either via default route
* or user set route) so get peer according to
* the destination address.
*/
- read_lock_irqsave(&devices_lock, flags);
peer = peer_lookup_dst(dev, &ipv6_daddr, skb);
- read_unlock_irqrestore(&devices_lock, flags);
if (!peer) {
BT_DBG("no such peer %pMR found", &addr);
return -ENOENT;
@@ -520,12 +538,19 @@
*/
chan->data = skb;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
memset(&msg, 0, sizeof(msg));
msg.msg_iov = (struct iovec *) &iv;
msg.msg_iovlen = 1;
+#endif
iv.iov_base = skb->data;
iv.iov_len = skb->len;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,19,0)
+ memset(&msg, 0, sizeof(msg));
+ iov_iter_kvec(&msg.msg_iter, WRITE | ITER_KVEC, &iv, 1, skb->len);
+#endif
+
err = l2cap_chan_send(chan, &msg, skb->len);
if (err > 0) {
netdev->stats.tx_bytes += err;
@@ -549,14 +574,13 @@
static int send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev)
{
struct sk_buff *local_skb;
- struct lowpan_dev *entry, *tmp;
- unsigned long flags;
+ struct lowpan_dev *entry;
int err = 0;
- read_lock_irqsave(&devices_lock, flags);
+ rcu_read_lock();
- list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
- struct lowpan_peer *pentry, *ptmp;
+ list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
+ struct lowpan_peer *pentry;
struct lowpan_dev *dev;
if (entry->netdev != netdev)
@@ -564,7 +588,7 @@
dev = lowpan_dev(entry->netdev);
- list_for_each_entry_safe(pentry, ptmp, &dev->peers, list) {
+ list_for_each_entry_rcu(pentry, &dev->peers, list) {
int ret;
local_skb = skb_clone(skb, GFP_ATOMIC);
@@ -581,7 +605,7 @@
}
}
- read_unlock_irqrestore(&devices_lock, flags);
+ rcu_read_unlock();
return err;
}
@@ -591,17 +615,13 @@
int err = 0;
bdaddr_t addr;
u8 addr_type;
- struct sk_buff *tmpskb;
/* We must take a copy of the skb before we modify/replace the ipv6
* header as the header could be used elsewhere
*/
- tmpskb = skb_unshare(skb, GFP_ATOMIC);
- if (!tmpskb) {
- kfree_skb(skb);
+ skb = skb_unshare(skb, GFP_ATOMIC);
+ if (!skb)
return NET_XMIT_DROP;
- }
- skb = tmpskb;
/* Return values from setup_header()
* <0 - error, packet is dropped
@@ -638,7 +658,28 @@
return err < 0 ? NET_XMIT_DROP : err;
}
+static struct lock_class_key bt_tx_busylock;
+static struct lock_class_key bt_netdev_xmit_lock_key;
+
+static void bt_set_lockdep_class_one(struct net_device *dev,
+ struct netdev_queue *txq,
+ void *_unused)
+{
+ lockdep_set_class(&txq->_xmit_lock, &bt_netdev_xmit_lock_key);
+}
+
+static int bt_dev_init(struct net_device *dev)
+{
+ netdev_for_each_tx_queue(dev, bt_set_lockdep_class_one, NULL);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
+ dev->qdisc_tx_busylock = &bt_tx_busylock;
+#endif
+
+ return 0;
+}
+
static const struct net_device_ops netdev_ops = {
+ .ndo_init = bt_dev_init,
.ndo_start_xmit = bt_xmit,
};
@@ -783,7 +824,6 @@
struct lowpan_dev *dev)
{
struct lowpan_peer *peer;
- unsigned long flags;
peer = kzalloc(sizeof(*peer), GFP_ATOMIC);
if (!peer)
@@ -806,10 +846,10 @@
*/
set_ip_addr_bits(chan->dst_type, (u8 *)&peer->peer_addr.s6_addr + 8);
- write_lock_irqsave(&devices_lock, flags);
+ spin_lock(&devices_lock);
INIT_LIST_HEAD(&peer->list);
peer_add(dev, peer);
- write_unlock_irqrestore(&devices_lock, flags);
+ spin_unlock(&devices_lock);
/* Notifying peers about us needs to be done without locks held */
INIT_DELAYED_WORK(&dev->notify_peers, do_notify_peers);
@@ -822,7 +862,6 @@
{
struct net_device *netdev;
int err = 0;
- unsigned long flags;
netdev = alloc_netdev(sizeof(struct lowpan_dev), IFACE_NAME_TEMPLATE,
NET_NAME_UNKNOWN, netdev_setup);
@@ -852,10 +891,10 @@
(*dev)->hdev = chan->conn->hcon->hdev;
INIT_LIST_HEAD(&(*dev)->peers);
- write_lock_irqsave(&devices_lock, flags);
+ spin_lock(&devices_lock);
INIT_LIST_HEAD(&(*dev)->list);
- list_add(&(*dev)->list, &bt_6lowpan_devices);
- write_unlock_irqrestore(&devices_lock, flags);
+ list_add_rcu(&(*dev)->list, &bt_6lowpan_devices);
+ spin_unlock(&devices_lock);
return 0;
@@ -909,11 +948,10 @@
static void chan_close_cb(struct l2cap_chan *chan)
{
- struct lowpan_dev *entry, *tmp;
+ struct lowpan_dev *entry;
struct lowpan_dev *dev = NULL;
struct lowpan_peer *peer;
int err = -ENOENT;
- unsigned long flags;
bool last = false, removed = true;
BT_DBG("chan %p conn %p", chan, chan->conn);
@@ -928,11 +966,11 @@
removed = false;
}
- write_lock_irqsave(&devices_lock, flags);
+ spin_lock(&devices_lock);
- list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
+ list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
dev = lowpan_dev(entry->netdev);
- peer = peer_lookup_chan(dev, chan);
+ peer = __peer_lookup_chan(dev, chan);
if (peer) {
last = peer_del(dev, peer);
err = 0;
@@ -943,13 +981,12 @@
atomic_read(&chan->kref.refcount));
l2cap_chan_put(chan);
- kfree(peer);
break;
}
}
if (!err && last && dev && !atomic_read(&dev->peer_count)) {
- write_unlock_irqrestore(&devices_lock, flags);
+ spin_unlock(&devices_lock);
cancel_delayed_work_sync(&dev->notify_peers);
@@ -960,7 +997,7 @@
schedule_work(&entry->delete_netdev);
}
} else {
- write_unlock_irqrestore(&devices_lock, flags);
+ spin_unlock(&devices_lock);
}
return;
@@ -1103,6 +1140,8 @@
pchan->state = BT_LISTEN;
pchan->src_type = BDADDR_LE_PUBLIC;
+ atomic_set(&pchan->nesting, L2CAP_NESTING_PARENT);
+
BT_DBG("psm 0x%04x chan %p src type %d", psm_6lowpan, pchan,
pchan->src_type);
@@ -1152,10 +1191,9 @@
static void disconnect_all_peers(void)
{
- struct lowpan_dev *entry, *tmp_dev;
+ struct lowpan_dev *entry;
struct lowpan_peer *peer, *tmp_peer, *new_peer;
struct list_head peers;
- unsigned long flags;
INIT_LIST_HEAD(&peers);
@@ -1164,10 +1202,10 @@
* with the same list at the same time.
*/
- read_lock_irqsave(&devices_lock, flags);
+ rcu_read_lock();
- list_for_each_entry_safe(entry, tmp_dev, &bt_6lowpan_devices, list) {
- list_for_each_entry_safe(peer, tmp_peer, &entry->peers, list) {
+ list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
+ list_for_each_entry_rcu(peer, &entry->peers, list) {
new_peer = kmalloc(sizeof(*new_peer), GFP_ATOMIC);
if (!new_peer)
break;
@@ -1179,26 +1217,36 @@
}
}
- read_unlock_irqrestore(&devices_lock, flags);
+ rcu_read_unlock();
+ spin_lock(&devices_lock);
list_for_each_entry_safe(peer, tmp_peer, &peers, list) {
l2cap_chan_close(peer->chan, ENOENT);
- kfree(peer);
+
+ list_del_rcu(&peer->list);
+ kfree_rcu(peer, rcu);
+
+ module_put(THIS_MODULE);
}
+ spin_unlock(&devices_lock);
}
-static int lowpan_psm_set(void *data, u64 val)
-{
+struct set_psm {
+ struct work_struct work;
u16 psm;
+};
- psm = val;
- if (psm == 0 || psm_6lowpan != psm)
+static void do_psm_set(struct work_struct *work)
+{
+ struct set_psm *set_psm = container_of(work, struct set_psm, work);
+
+ if (set_psm->psm == 0 || psm_6lowpan != set_psm->psm)
/* Disconnect existing connections if 6lowpan is
* disabled (psm = 0), or if psm changes.
*/
disconnect_all_peers();
- psm_6lowpan = psm;
+ psm_6lowpan = set_psm->psm;
if (listen_chan) {
l2cap_chan_close(listen_chan, 0);
@@ -1207,6 +1255,22 @@
listen_chan = bt_6lowpan_listen();
+ kfree(set_psm);
+}
+
+static int lowpan_psm_set(void *data, u64 val)
+{
+ struct set_psm *set_psm;
+
+ set_psm = kzalloc(sizeof(*set_psm), GFP_KERNEL);
+ if (!set_psm)
+ return -ENOMEM;
+
+ set_psm->psm = val;
+ INIT_WORK(&set_psm->work, do_psm_set);
+
+ schedule_work(&set_psm->work);
+
return 0;
}
@@ -1288,19 +1352,18 @@
static int lowpan_control_show(struct seq_file *f, void *ptr)
{
- struct lowpan_dev *entry, *tmp_dev;
- struct lowpan_peer *peer, *tmp_peer;
- unsigned long flags;
+ struct lowpan_dev *entry;
+ struct lowpan_peer *peer;
- read_lock_irqsave(&devices_lock, flags);
+ spin_lock(&devices_lock);
- list_for_each_entry_safe(entry, tmp_dev, &bt_6lowpan_devices, list) {
- list_for_each_entry_safe(peer, tmp_peer, &entry->peers, list)
+ list_for_each_entry(entry, &bt_6lowpan_devices, list) {
+ list_for_each_entry(peer, &entry->peers, list)
seq_printf(f, "%pMR (type %u)\n",
&peer->chan->dst, peer->chan->dst_type);
}
- read_unlock_irqrestore(&devices_lock, flags);
+ spin_unlock(&devices_lock);
return 0;
}
@@ -1322,7 +1385,6 @@
{
struct lowpan_dev *entry, *tmp, *new_dev;
struct list_head devices;
- unsigned long flags;
INIT_LIST_HEAD(&devices);
@@ -1331,9 +1393,9 @@
* devices list.
*/
- read_lock_irqsave(&devices_lock, flags);
+ rcu_read_lock();
- list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
+ list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
new_dev = kmalloc(sizeof(*new_dev), GFP_ATOMIC);
if (!new_dev)
break;
@@ -1341,10 +1403,10 @@
new_dev->netdev = entry->netdev;
INIT_LIST_HEAD(&new_dev->list);
- list_add(&new_dev->list, &devices);
+ list_add_rcu(&new_dev->list, &devices);
}
- read_unlock_irqrestore(&devices_lock, flags);
+ rcu_read_unlock();
list_for_each_entry_safe(entry, tmp, &devices, list) {
ifdown(entry->netdev);
@@ -1359,17 +1421,15 @@
unsigned long event, void *ptr)
{
struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
- struct lowpan_dev *entry, *tmp;
- unsigned long flags;
+ struct lowpan_dev *entry;
if (netdev->type != ARPHRD_6LOWPAN)
return NOTIFY_DONE;
switch (event) {
case NETDEV_UNREGISTER:
- write_lock_irqsave(&devices_lock, flags);
- list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices,
- list) {
+ spin_lock(&devices_lock);
+ list_for_each_entry(entry, &bt_6lowpan_devices, list) {
if (entry->netdev == netdev) {
BT_DBG("Unregistered netdev %s %p",
netdev->name, netdev);
@@ -1378,7 +1438,7 @@
break;
}
}
- write_unlock_irqrestore(&devices_lock, flags);
+ spin_unlock(&devices_lock);
break;
}
diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig
index b5df2e3..a68caf3 100644
--- a/net/bluetooth/Kconfig
+++ b/net/bluetooth/Kconfig
@@ -11,6 +11,7 @@
depends on CRYPTO
depends on CRYPTO_BLKCIPHER
depends on CRYPTO_AES
+ depends on CRYPTO_CMAC
depends on CRYPTO_ECB
depends on CRYPTO_SHA256
help
@@ -40,12 +41,10 @@
to Bluetooth kernel modules are provided in the BlueZ packages. For
more information, see <http://www.bluez.org/>.
-config BT_6LOWPAN
- tristate "Bluetooth 6LoWPAN support"
- depends on m
- depends on BT && 6LOWPAN
- help
- IPv6 compression over Bluetooth Low Energy.
+config BT_BREDR
+ bool "Bluetooth Classic (BR/EDR) features"
+ depends on BT
+ default y
source "net/bluetooth/rfcomm/Kconfig"
@@ -55,4 +54,16 @@
source "net/bluetooth/hidp/Kconfig"
+config BT_LE
+ bool "Bluetooth Low Energy (LE) features"
+ depends on BT
+ default y
+
+config BT_6LOWPAN
+ tristate "Bluetooth 6LoWPAN support"
+ depends on m
+ depends on BT_LE && 6LOWPAN
+ help
+ IPv6 compression over Bluetooth Low Energy.
+
source "drivers/bluetooth/Kconfig"
diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
index cb87c99..e3474dd 100644
--- a/net/bluetooth/Makefile
+++ b/net/bluetooth/Makefile
@@ -13,6 +13,6 @@
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \
- a2mp.o amp.o
+ a2mp.o amp.o ecc.o
subdir-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 5dcade5..6fcb9bf 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -60,8 +60,12 @@
memset(&msg, 0, sizeof(msg));
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,19,0)
+ iov_iter_kvec(&msg.msg_iter, WRITE | ITER_KVEC, &iv, 1, total_len);
+#else
msg.msg_iov = (struct iovec *) &iv;
msg.msg_iovlen = 1;
+#endif
l2cap_chan_send(chan, &msg, total_len);
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index 339c74a..012e3b0 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -31,7 +31,7 @@
#include <net/bluetooth/bluetooth.h>
#include <linux/proc_fs.h>
-#define VERSION "2.19"
+#define VERSION "2.20"
/* Bluetooth sockets */
#define BT_MAX_PROTO 8
@@ -237,7 +237,7 @@
}
skb_reset_transport_header(skb);
- err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ err = skb_copy_datagram_msg(skb, 0, msg, copied);
if (err == 0) {
sock_recv_ts_and_drops(msg, sk, skb);
@@ -328,7 +328,7 @@
}
chunk = min_t(unsigned int, skb->len, size);
- if (skb_copy_datagram_iovec(skb, 0, msg->msg_iov, chunk)) {
+ if (skb_copy_datagram_msg(skb, 0, msg, chunk)) {
skb_queue_head(&sk->sk_receive_queue, skb);
if (!copied)
copied = -EFAULT;
diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c
index 2640d78..ee016f0 100644
--- a/net/bluetooth/amp.c
+++ b/net/bluetooth/amp.c
@@ -134,6 +134,7 @@
static int hmac_sha256(u8 *key, u8 ksize, char *plaintext, u8 psize, u8 *output)
{
struct crypto_shash *tfm;
+ struct shash_desc *shash;
int ret;
if (!ksize)
@@ -148,18 +149,24 @@
ret = crypto_shash_setkey(tfm, key, ksize);
if (ret) {
BT_DBG("crypto_ahash_setkey failed: err %d", ret);
- } else {
- char desc[sizeof(struct shash_desc) +
- crypto_shash_descsize(tfm)] CRYPTO_MINALIGN_ATTR;
- struct shash_desc *shash = (struct shash_desc *)desc;
-
- shash->tfm = tfm;
- shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
-
- ret = crypto_shash_digest(shash, plaintext, psize,
- output);
+ goto failed;
}
+ shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(tfm),
+ GFP_KERNEL);
+ if (!shash) {
+ ret = -ENOMEM;
+ goto failed;
+ }
+
+ shash->tfm = tfm;
+ shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+ ret = crypto_shash_digest(shash, plaintext, psize, output);
+
+ kfree(shash);
+
+failed:
crypto_free_shash(tfm);
return ret;
}
diff --git a/net/bluetooth/bnep/Kconfig b/net/bluetooth/bnep/Kconfig
index 7ca2e1c..64ffb40 100644
--- a/net/bluetooth/bnep/Kconfig
+++ b/net/bluetooth/bnep/Kconfig
@@ -1,7 +1,7 @@
config BT_BNEP
tristate "BNEP protocol support"
depends on m
- depends on BT
+ depends on BT_BREDR
depends on CRC32
help
BNEP (Bluetooth Network Encapsulation Protocol) is Ethernet
diff --git a/net/bluetooth/cmtp/Kconfig b/net/bluetooth/cmtp/Kconfig
index accf52d..7acd7ea 100644
--- a/net/bluetooth/cmtp/Kconfig
+++ b/net/bluetooth/cmtp/Kconfig
@@ -1,7 +1,7 @@
config BT_CMTP
tristate "CMTP protocol support"
depends on m
- depends on BT && ISDN_CAPI
+ depends on BT_BREDR && ISDN_CAPI
help
CMTP (CAPI Message Transport Protocol) is a transport layer
for CAPI messages. CMTP is required for the Bluetooth Common
diff --git a/net/bluetooth/ecc.c b/net/bluetooth/ecc.c
new file mode 100644
index 0000000..e1709f8
--- /dev/null
+++ b/net/bluetooth/ecc.c
@@ -0,0 +1,816 @@
+/*
+ * Copyright (c) 2013, Kenneth MacKay
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/random.h>
+
+#include "ecc.h"
+
+/* 256-bit curve */
+#define ECC_BYTES 32
+
+#define MAX_TRIES 16
+
+/* Number of u64's needed */
+#define NUM_ECC_DIGITS (ECC_BYTES / 8)
+
+struct ecc_point {
+ u64 x[NUM_ECC_DIGITS];
+ u64 y[NUM_ECC_DIGITS];
+};
+
+typedef struct {
+ u64 m_low;
+ u64 m_high;
+} uint128_t;
+
+#define CURVE_P_32 { 0xFFFFFFFFFFFFFFFFull, 0x00000000FFFFFFFFull, \
+ 0x0000000000000000ull, 0xFFFFFFFF00000001ull }
+
+#define CURVE_G_32 { \
+ { 0xF4A13945D898C296ull, 0x77037D812DEB33A0ull, \
+ 0xF8BCE6E563A440F2ull, 0x6B17D1F2E12C4247ull }, \
+ { 0xCBB6406837BF51F5ull, 0x2BCE33576B315ECEull, \
+ 0x8EE7EB4A7C0F9E16ull, 0x4FE342E2FE1A7F9Bull } \
+}
+
+#define CURVE_N_32 { 0xF3B9CAC2FC632551ull, 0xBCE6FAADA7179E84ull, \
+ 0xFFFFFFFFFFFFFFFFull, 0xFFFFFFFF00000000ull }
+
+static u64 curve_p[NUM_ECC_DIGITS] = CURVE_P_32;
+static struct ecc_point curve_g = CURVE_G_32;
+static u64 curve_n[NUM_ECC_DIGITS] = CURVE_N_32;
+
+static void vli_clear(u64 *vli)
+{
+ int i;
+
+ for (i = 0; i < NUM_ECC_DIGITS; i++)
+ vli[i] = 0;
+}
+
+/* Returns true if vli == 0, false otherwise. */
+static bool vli_is_zero(const u64 *vli)
+{
+ int i;
+
+ for (i = 0; i < NUM_ECC_DIGITS; i++) {
+ if (vli[i])
+ return false;
+ }
+
+ return true;
+}
+
+/* Returns nonzero if bit bit of vli is set. */
+static u64 vli_test_bit(const u64 *vli, unsigned int bit)
+{
+ return (vli[bit / 64] & ((u64) 1 << (bit % 64)));
+}
+
+/* Counts the number of 64-bit "digits" in vli. */
+static unsigned int vli_num_digits(const u64 *vli)
+{
+ int i;
+
+ /* Search from the end until we find a non-zero digit.
+ * We do it in reverse because we expect that most digits will
+ * be nonzero.
+ */
+ for (i = NUM_ECC_DIGITS - 1; i >= 0 && vli[i] == 0; i--);
+
+ return (i + 1);
+}
+
+/* Counts the number of bits required for vli. */
+static unsigned int vli_num_bits(const u64 *vli)
+{
+ unsigned int i, num_digits;
+ u64 digit;
+
+ num_digits = vli_num_digits(vli);
+ if (num_digits == 0)
+ return 0;
+
+ digit = vli[num_digits - 1];
+ for (i = 0; digit; i++)
+ digit >>= 1;
+
+ return ((num_digits - 1) * 64 + i);
+}
+
+/* Sets dest = src. */
+static void vli_set(u64 *dest, const u64 *src)
+{
+ int i;
+
+ for (i = 0; i < NUM_ECC_DIGITS; i++)
+ dest[i] = src[i];
+}
+
+/* Returns sign of left - right. */
+static int vli_cmp(const u64 *left, const u64 *right)
+{
+ int i;
+
+ for (i = NUM_ECC_DIGITS - 1; i >= 0; i--) {
+ if (left[i] > right[i])
+ return 1;
+ else if (left[i] < right[i])
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Computes result = in << c, returning carry. Can modify in place
+ * (if result == in). 0 < shift < 64.
+ */
+static u64 vli_lshift(u64 *result, const u64 *in,
+ unsigned int shift)
+{
+ u64 carry = 0;
+ int i;
+
+ for (i = 0; i < NUM_ECC_DIGITS; i++) {
+ u64 temp = in[i];
+
+ result[i] = (temp << shift) | carry;
+ carry = temp >> (64 - shift);
+ }
+
+ return carry;
+}
+
+/* Computes vli = vli >> 1. */
+static void vli_rshift1(u64 *vli)
+{
+ u64 *end = vli;
+ u64 carry = 0;
+
+ vli += NUM_ECC_DIGITS;
+
+ while (vli-- > end) {
+ u64 temp = *vli;
+ *vli = (temp >> 1) | carry;
+ carry = temp << 63;
+ }
+}
+
+/* Computes result = left + right, returning carry. Can modify in place. */
+static u64 vli_add(u64 *result, const u64 *left,
+ const u64 *right)
+{
+ u64 carry = 0;
+ int i;
+
+ for (i = 0; i < NUM_ECC_DIGITS; i++) {
+ u64 sum;
+
+ sum = left[i] + right[i] + carry;
+ if (sum != left[i])
+ carry = (sum < left[i]);
+
+ result[i] = sum;
+ }
+
+ return carry;
+}
+
+/* Computes result = left - right, returning borrow. Can modify in place. */
+static u64 vli_sub(u64 *result, const u64 *left, const u64 *right)
+{
+ u64 borrow = 0;
+ int i;
+
+ for (i = 0; i < NUM_ECC_DIGITS; i++) {
+ u64 diff;
+
+ diff = left[i] - right[i] - borrow;
+ if (diff != left[i])
+ borrow = (diff > left[i]);
+
+ result[i] = diff;
+ }
+
+ return borrow;
+}
+
+static uint128_t mul_64_64(u64 left, u64 right)
+{
+ u64 a0 = left & 0xffffffffull;
+ u64 a1 = left >> 32;
+ u64 b0 = right & 0xffffffffull;
+ u64 b1 = right >> 32;
+ u64 m0 = a0 * b0;
+ u64 m1 = a0 * b1;
+ u64 m2 = a1 * b0;
+ u64 m3 = a1 * b1;
+ uint128_t result;
+
+ m2 += (m0 >> 32);
+ m2 += m1;
+
+ /* Overflow */
+ if (m2 < m1)
+ m3 += 0x100000000ull;
+
+ result.m_low = (m0 & 0xffffffffull) | (m2 << 32);
+ result.m_high = m3 + (m2 >> 32);
+
+ return result;
+}
+
+static uint128_t add_128_128(uint128_t a, uint128_t b)
+{
+ uint128_t result;
+
+ result.m_low = a.m_low + b.m_low;
+ result.m_high = a.m_high + b.m_high + (result.m_low < a.m_low);
+
+ return result;
+}
+
+static void vli_mult(u64 *result, const u64 *left, const u64 *right)
+{
+ uint128_t r01 = { 0, 0 };
+ u64 r2 = 0;
+ unsigned int i, k;
+
+ /* Compute each digit of result in sequence, maintaining the
+ * carries.
+ */
+ for (k = 0; k < NUM_ECC_DIGITS * 2 - 1; k++) {
+ unsigned int min;
+
+ if (k < NUM_ECC_DIGITS)
+ min = 0;
+ else
+ min = (k + 1) - NUM_ECC_DIGITS;
+
+ for (i = min; i <= k && i < NUM_ECC_DIGITS; i++) {
+ uint128_t product;
+
+ product = mul_64_64(left[i], right[k - i]);
+
+ r01 = add_128_128(r01, product);
+ r2 += (r01.m_high < product.m_high);
+ }
+
+ result[k] = r01.m_low;
+ r01.m_low = r01.m_high;
+ r01.m_high = r2;
+ r2 = 0;
+ }
+
+ result[NUM_ECC_DIGITS * 2 - 1] = r01.m_low;
+}
+
+static void vli_square(u64 *result, const u64 *left)
+{
+ uint128_t r01 = { 0, 0 };
+ u64 r2 = 0;
+ int i, k;
+
+ for (k = 0; k < NUM_ECC_DIGITS * 2 - 1; k++) {
+ unsigned int min;
+
+ if (k < NUM_ECC_DIGITS)
+ min = 0;
+ else
+ min = (k + 1) - NUM_ECC_DIGITS;
+
+ for (i = min; i <= k && i <= k - i; i++) {
+ uint128_t product;
+
+ product = mul_64_64(left[i], left[k - i]);
+
+ if (i < k - i) {
+ r2 += product.m_high >> 63;
+ product.m_high = (product.m_high << 1) |
+ (product.m_low >> 63);
+ product.m_low <<= 1;
+ }
+
+ r01 = add_128_128(r01, product);
+ r2 += (r01.m_high < product.m_high);
+ }
+
+ result[k] = r01.m_low;
+ r01.m_low = r01.m_high;
+ r01.m_high = r2;
+ r2 = 0;
+ }
+
+ result[NUM_ECC_DIGITS * 2 - 1] = r01.m_low;
+}
+
+/* Computes result = (left + right) % mod.
+ * Assumes that left < mod and right < mod, result != mod.
+ */
+static void vli_mod_add(u64 *result, const u64 *left, const u64 *right,
+ const u64 *mod)
+{
+ u64 carry;
+
+ carry = vli_add(result, left, right);
+
+ /* result > mod (result = mod + remainder), so subtract mod to
+ * get remainder.
+ */
+ if (carry || vli_cmp(result, mod) >= 0)
+ vli_sub(result, result, mod);
+}
+
+/* Computes result = (left - right) % mod.
+ * Assumes that left < mod and right < mod, result != mod.
+ */
+static void vli_mod_sub(u64 *result, const u64 *left, const u64 *right,
+ const u64 *mod)
+{
+ u64 borrow = vli_sub(result, left, right);
+
+ /* In this case, p_result == -diff == (max int) - diff.
+ * Since -x % d == d - x, we can get the correct result from
+ * result + mod (with overflow).
+ */
+ if (borrow)
+ vli_add(result, result, mod);
+}
+
+/* Computes result = product % curve_p
+ from http://www.nsa.gov/ia/_files/nist-routines.pdf */
+static void vli_mmod_fast(u64 *result, const u64 *product)
+{
+ u64 tmp[NUM_ECC_DIGITS];
+ int carry;
+
+ /* t */
+ vli_set(result, product);
+
+ /* s1 */
+ tmp[0] = 0;
+ tmp[1] = product[5] & 0xffffffff00000000ull;
+ tmp[2] = product[6];
+ tmp[3] = product[7];
+ carry = vli_lshift(tmp, tmp, 1);
+ carry += vli_add(result, result, tmp);
+
+ /* s2 */
+ tmp[1] = product[6] << 32;
+ tmp[2] = (product[6] >> 32) | (product[7] << 32);
+ tmp[3] = product[7] >> 32;
+ carry += vli_lshift(tmp, tmp, 1);
+ carry += vli_add(result, result, tmp);
+
+ /* s3 */
+ tmp[0] = product[4];
+ tmp[1] = product[5] & 0xffffffff;
+ tmp[2] = 0;
+ tmp[3] = product[7];
+ carry += vli_add(result, result, tmp);
+
+ /* s4 */
+ tmp[0] = (product[4] >> 32) | (product[5] << 32);
+ tmp[1] = (product[5] >> 32) | (product[6] & 0xffffffff00000000ull);
+ tmp[2] = product[7];
+ tmp[3] = (product[6] >> 32) | (product[4] << 32);
+ carry += vli_add(result, result, tmp);
+
+ /* d1 */
+ tmp[0] = (product[5] >> 32) | (product[6] << 32);
+ tmp[1] = (product[6] >> 32);
+ tmp[2] = 0;
+ tmp[3] = (product[4] & 0xffffffff) | (product[5] << 32);
+ carry -= vli_sub(result, result, tmp);
+
+ /* d2 */
+ tmp[0] = product[6];
+ tmp[1] = product[7];
+ tmp[2] = 0;
+ tmp[3] = (product[4] >> 32) | (product[5] & 0xffffffff00000000ull);
+ carry -= vli_sub(result, result, tmp);
+
+ /* d3 */
+ tmp[0] = (product[6] >> 32) | (product[7] << 32);
+ tmp[1] = (product[7] >> 32) | (product[4] << 32);
+ tmp[2] = (product[4] >> 32) | (product[5] << 32);
+ tmp[3] = (product[6] << 32);
+ carry -= vli_sub(result, result, tmp);
+
+ /* d4 */
+ tmp[0] = product[7];
+ tmp[1] = product[4] & 0xffffffff00000000ull;
+ tmp[2] = product[5];
+ tmp[3] = product[6] & 0xffffffff00000000ull;
+ carry -= vli_sub(result, result, tmp);
+
+ if (carry < 0) {
+ do {
+ carry += vli_add(result, result, curve_p);
+ } while (carry < 0);
+ } else {
+ while (carry || vli_cmp(curve_p, result) != 1)
+ carry -= vli_sub(result, result, curve_p);
+ }
+}
+
+/* Computes result = (left * right) % curve_p. */
+static void vli_mod_mult_fast(u64 *result, const u64 *left, const u64 *right)
+{
+ u64 product[2 * NUM_ECC_DIGITS];
+
+ vli_mult(product, left, right);
+ vli_mmod_fast(result, product);
+}
+
+/* Computes result = left^2 % curve_p. */
+static void vli_mod_square_fast(u64 *result, const u64 *left)
+{
+ u64 product[2 * NUM_ECC_DIGITS];
+
+ vli_square(product, left);
+ vli_mmod_fast(result, product);
+}
+
+#define EVEN(vli) (!(vli[0] & 1))
+/* Computes result = (1 / p_input) % mod. All VLIs are the same size.
+ * See "From Euclid's GCD to Montgomery Multiplication to the Great Divide"
+ * https://labs.oracle.com/techrep/2001/smli_tr-2001-95.pdf
+ */
+static void vli_mod_inv(u64 *result, const u64 *input, const u64 *mod)
+{
+ u64 a[NUM_ECC_DIGITS], b[NUM_ECC_DIGITS];
+ u64 u[NUM_ECC_DIGITS], v[NUM_ECC_DIGITS];
+ u64 carry;
+ int cmp_result;
+
+ if (vli_is_zero(input)) {
+ vli_clear(result);
+ return;
+ }
+
+ vli_set(a, input);
+ vli_set(b, mod);
+ vli_clear(u);
+ u[0] = 1;
+ vli_clear(v);
+
+ while ((cmp_result = vli_cmp(a, b)) != 0) {
+ carry = 0;
+
+ if (EVEN(a)) {
+ vli_rshift1(a);
+
+ if (!EVEN(u))
+ carry = vli_add(u, u, mod);
+
+ vli_rshift1(u);
+ if (carry)
+ u[NUM_ECC_DIGITS - 1] |= 0x8000000000000000ull;
+ } else if (EVEN(b)) {
+ vli_rshift1(b);
+
+ if (!EVEN(v))
+ carry = vli_add(v, v, mod);
+
+ vli_rshift1(v);
+ if (carry)
+ v[NUM_ECC_DIGITS - 1] |= 0x8000000000000000ull;
+ } else if (cmp_result > 0) {
+ vli_sub(a, a, b);
+ vli_rshift1(a);
+
+ if (vli_cmp(u, v) < 0)
+ vli_add(u, u, mod);
+
+ vli_sub(u, u, v);
+ if (!EVEN(u))
+ carry = vli_add(u, u, mod);
+
+ vli_rshift1(u);
+ if (carry)
+ u[NUM_ECC_DIGITS - 1] |= 0x8000000000000000ull;
+ } else {
+ vli_sub(b, b, a);
+ vli_rshift1(b);
+
+ if (vli_cmp(v, u) < 0)
+ vli_add(v, v, mod);
+
+ vli_sub(v, v, u);
+ if (!EVEN(v))
+ carry = vli_add(v, v, mod);
+
+ vli_rshift1(v);
+ if (carry)
+ v[NUM_ECC_DIGITS - 1] |= 0x8000000000000000ull;
+ }
+ }
+
+ vli_set(result, u);
+}
+
+/* ------ Point operations ------ */
+
+/* Returns true if p_point is the point at infinity, false otherwise. */
+static bool ecc_point_is_zero(const struct ecc_point *point)
+{
+ return (vli_is_zero(point->x) && vli_is_zero(point->y));
+}
+
+/* Point multiplication algorithm using Montgomery's ladder with co-Z
+ * coordinates. From http://eprint.iacr.org/2011/338.pdf
+ */
+
+/* Double in place */
+static void ecc_point_double_jacobian(u64 *x1, u64 *y1, u64 *z1)
+{
+ /* t1 = x, t2 = y, t3 = z */
+ u64 t4[NUM_ECC_DIGITS];
+ u64 t5[NUM_ECC_DIGITS];
+
+ if (vli_is_zero(z1))
+ return;
+
+ vli_mod_square_fast(t4, y1); /* t4 = y1^2 */
+ vli_mod_mult_fast(t5, x1, t4); /* t5 = x1*y1^2 = A */
+ vli_mod_square_fast(t4, t4); /* t4 = y1^4 */
+ vli_mod_mult_fast(y1, y1, z1); /* t2 = y1*z1 = z3 */
+ vli_mod_square_fast(z1, z1); /* t3 = z1^2 */
+
+ vli_mod_add(x1, x1, z1, curve_p); /* t1 = x1 + z1^2 */
+ vli_mod_add(z1, z1, z1, curve_p); /* t3 = 2*z1^2 */
+ vli_mod_sub(z1, x1, z1, curve_p); /* t3 = x1 - z1^2 */
+ vli_mod_mult_fast(x1, x1, z1); /* t1 = x1^2 - z1^4 */
+
+ vli_mod_add(z1, x1, x1, curve_p); /* t3 = 2*(x1^2 - z1^4) */
+ vli_mod_add(x1, x1, z1, curve_p); /* t1 = 3*(x1^2 - z1^4) */
+ if (vli_test_bit(x1, 0)) {
+ u64 carry = vli_add(x1, x1, curve_p);
+ vli_rshift1(x1);
+ x1[NUM_ECC_DIGITS - 1] |= carry << 63;
+ } else {
+ vli_rshift1(x1);
+ }
+ /* t1 = 3/2*(x1^2 - z1^4) = B */
+
+ vli_mod_square_fast(z1, x1); /* t3 = B^2 */
+ vli_mod_sub(z1, z1, t5, curve_p); /* t3 = B^2 - A */
+ vli_mod_sub(z1, z1, t5, curve_p); /* t3 = B^2 - 2A = x3 */
+ vli_mod_sub(t5, t5, z1, curve_p); /* t5 = A - x3 */
+ vli_mod_mult_fast(x1, x1, t5); /* t1 = B * (A - x3) */
+ vli_mod_sub(t4, x1, t4, curve_p); /* t4 = B * (A - x3) - y1^4 = y3 */
+
+ vli_set(x1, z1);
+ vli_set(z1, y1);
+ vli_set(y1, t4);
+}
+
+/* Modify (x1, y1) => (x1 * z^2, y1 * z^3) */
+static void apply_z(u64 *x1, u64 *y1, u64 *z)
+{
+ u64 t1[NUM_ECC_DIGITS];
+
+ vli_mod_square_fast(t1, z); /* z^2 */
+ vli_mod_mult_fast(x1, x1, t1); /* x1 * z^2 */
+ vli_mod_mult_fast(t1, t1, z); /* z^3 */
+ vli_mod_mult_fast(y1, y1, t1); /* y1 * z^3 */
+}
+
+/* P = (x1, y1) => 2P, (x2, y2) => P' */
+static void xycz_initial_double(u64 *x1, u64 *y1, u64 *x2, u64 *y2,
+ u64 *p_initial_z)
+{
+ u64 z[NUM_ECC_DIGITS];
+
+ vli_set(x2, x1);
+ vli_set(y2, y1);
+
+ vli_clear(z);
+ z[0] = 1;
+
+ if (p_initial_z)
+ vli_set(z, p_initial_z);
+
+ apply_z(x1, y1, z);
+
+ ecc_point_double_jacobian(x1, y1, z);
+
+ apply_z(x2, y2, z);
+}
+
+/* Input P = (x1, y1, Z), Q = (x2, y2, Z)
+ * Output P' = (x1', y1', Z3), P + Q = (x3, y3, Z3)
+ * or P => P', Q => P + Q
+ */
+static void xycz_add(u64 *x1, u64 *y1, u64 *x2, u64 *y2)
+{
+ /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */
+ u64 t5[NUM_ECC_DIGITS];
+
+ vli_mod_sub(t5, x2, x1, curve_p); /* t5 = x2 - x1 */
+ vli_mod_square_fast(t5, t5); /* t5 = (x2 - x1)^2 = A */
+ vli_mod_mult_fast(x1, x1, t5); /* t1 = x1*A = B */
+ vli_mod_mult_fast(x2, x2, t5); /* t3 = x2*A = C */
+ vli_mod_sub(y2, y2, y1, curve_p); /* t4 = y2 - y1 */
+ vli_mod_square_fast(t5, y2); /* t5 = (y2 - y1)^2 = D */
+
+ vli_mod_sub(t5, t5, x1, curve_p); /* t5 = D - B */
+ vli_mod_sub(t5, t5, x2, curve_p); /* t5 = D - B - C = x3 */
+ vli_mod_sub(x2, x2, x1, curve_p); /* t3 = C - B */
+ vli_mod_mult_fast(y1, y1, x2); /* t2 = y1*(C - B) */
+ vli_mod_sub(x2, x1, t5, curve_p); /* t3 = B - x3 */
+ vli_mod_mult_fast(y2, y2, x2); /* t4 = (y2 - y1)*(B - x3) */
+ vli_mod_sub(y2, y2, y1, curve_p); /* t4 = y3 */
+
+ vli_set(x2, t5);
+}
+
+/* Input P = (x1, y1, Z), Q = (x2, y2, Z)
+ * Output P + Q = (x3, y3, Z3), P - Q = (x3', y3', Z3)
+ * or P => P - Q, Q => P + Q
+ */
+static void xycz_add_c(u64 *x1, u64 *y1, u64 *x2, u64 *y2)
+{
+ /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */
+ u64 t5[NUM_ECC_DIGITS];
+ u64 t6[NUM_ECC_DIGITS];
+ u64 t7[NUM_ECC_DIGITS];
+
+ vli_mod_sub(t5, x2, x1, curve_p); /* t5 = x2 - x1 */
+ vli_mod_square_fast(t5, t5); /* t5 = (x2 - x1)^2 = A */
+ vli_mod_mult_fast(x1, x1, t5); /* t1 = x1*A = B */
+ vli_mod_mult_fast(x2, x2, t5); /* t3 = x2*A = C */
+ vli_mod_add(t5, y2, y1, curve_p); /* t4 = y2 + y1 */
+ vli_mod_sub(y2, y2, y1, curve_p); /* t4 = y2 - y1 */
+
+ vli_mod_sub(t6, x2, x1, curve_p); /* t6 = C - B */
+ vli_mod_mult_fast(y1, y1, t6); /* t2 = y1 * (C - B) */
+ vli_mod_add(t6, x1, x2, curve_p); /* t6 = B + C */
+ vli_mod_square_fast(x2, y2); /* t3 = (y2 - y1)^2 */
+ vli_mod_sub(x2, x2, t6, curve_p); /* t3 = x3 */
+
+ vli_mod_sub(t7, x1, x2, curve_p); /* t7 = B - x3 */
+ vli_mod_mult_fast(y2, y2, t7); /* t4 = (y2 - y1)*(B - x3) */
+ vli_mod_sub(y2, y2, y1, curve_p); /* t4 = y3 */
+
+ vli_mod_square_fast(t7, t5); /* t7 = (y2 + y1)^2 = F */
+ vli_mod_sub(t7, t7, t6, curve_p); /* t7 = x3' */
+ vli_mod_sub(t6, t7, x1, curve_p); /* t6 = x3' - B */
+ vli_mod_mult_fast(t6, t6, t5); /* t6 = (y2 + y1)*(x3' - B) */
+ vli_mod_sub(y1, t6, y1, curve_p); /* t2 = y3' */
+
+ vli_set(x1, t7);
+}
+
+static void ecc_point_mult(struct ecc_point *result,
+ const struct ecc_point *point, u64 *scalar,
+ u64 *initial_z, int num_bits)
+{
+ /* R0 and R1 */
+ u64 rx[2][NUM_ECC_DIGITS];
+ u64 ry[2][NUM_ECC_DIGITS];
+ u64 z[NUM_ECC_DIGITS];
+ int i, nb;
+
+ vli_set(rx[1], point->x);
+ vli_set(ry[1], point->y);
+
+ xycz_initial_double(rx[1], ry[1], rx[0], ry[0], initial_z);
+
+ for (i = num_bits - 2; i > 0; i--) {
+ nb = !vli_test_bit(scalar, i);
+ xycz_add_c(rx[1 - nb], ry[1 - nb], rx[nb], ry[nb]);
+ xycz_add(rx[nb], ry[nb], rx[1 - nb], ry[1 - nb]);
+ }
+
+ nb = !vli_test_bit(scalar, 0);
+ xycz_add_c(rx[1 - nb], ry[1 - nb], rx[nb], ry[nb]);
+
+ /* Find final 1/Z value. */
+ vli_mod_sub(z, rx[1], rx[0], curve_p); /* X1 - X0 */
+ vli_mod_mult_fast(z, z, ry[1 - nb]); /* Yb * (X1 - X0) */
+ vli_mod_mult_fast(z, z, point->x); /* xP * Yb * (X1 - X0) */
+ vli_mod_inv(z, z, curve_p); /* 1 / (xP * Yb * (X1 - X0)) */
+ vli_mod_mult_fast(z, z, point->y); /* yP / (xP * Yb * (X1 - X0)) */
+ vli_mod_mult_fast(z, z, rx[1 - nb]); /* Xb * yP / (xP * Yb * (X1 - X0)) */
+ /* End 1/Z calculation */
+
+ xycz_add(rx[nb], ry[nb], rx[1 - nb], ry[1 - nb]);
+
+ apply_z(rx[0], ry[0], z);
+
+ vli_set(result->x, rx[0]);
+ vli_set(result->y, ry[0]);
+}
+
+static void ecc_bytes2native(const u8 bytes[ECC_BYTES],
+ u64 native[NUM_ECC_DIGITS])
+{
+ int i;
+
+ for (i = 0; i < NUM_ECC_DIGITS; i++) {
+ const u8 *digit = bytes + 8 * (NUM_ECC_DIGITS - 1 - i);
+
+ native[NUM_ECC_DIGITS - 1 - i] =
+ ((u64) digit[0] << 0) |
+ ((u64) digit[1] << 8) |
+ ((u64) digit[2] << 16) |
+ ((u64) digit[3] << 24) |
+ ((u64) digit[4] << 32) |
+ ((u64) digit[5] << 40) |
+ ((u64) digit[6] << 48) |
+ ((u64) digit[7] << 56);
+ }
+}
+
+static void ecc_native2bytes(const u64 native[NUM_ECC_DIGITS],
+ u8 bytes[ECC_BYTES])
+{
+ int i;
+
+ for (i = 0; i < NUM_ECC_DIGITS; i++) {
+ u8 *digit = bytes + 8 * (NUM_ECC_DIGITS - 1 - i);
+
+ digit[0] = native[NUM_ECC_DIGITS - 1 - i] >> 0;
+ digit[1] = native[NUM_ECC_DIGITS - 1 - i] >> 8;
+ digit[2] = native[NUM_ECC_DIGITS - 1 - i] >> 16;
+ digit[3] = native[NUM_ECC_DIGITS - 1 - i] >> 24;
+ digit[4] = native[NUM_ECC_DIGITS - 1 - i] >> 32;
+ digit[5] = native[NUM_ECC_DIGITS - 1 - i] >> 40;
+ digit[6] = native[NUM_ECC_DIGITS - 1 - i] >> 48;
+ digit[7] = native[NUM_ECC_DIGITS - 1 - i] >> 56;
+ }
+}
+
+bool ecc_make_key(u8 public_key[64], u8 private_key[32])
+{
+ struct ecc_point pk;
+ u64 priv[NUM_ECC_DIGITS];
+ unsigned int tries = 0;
+
+ do {
+ if (tries++ >= MAX_TRIES)
+ return false;
+
+ get_random_bytes(priv, ECC_BYTES);
+
+ if (vli_is_zero(priv))
+ continue;
+
+ /* Make sure the private key is in the range [1, n-1]. */
+ if (vli_cmp(curve_n, priv) != 1)
+ continue;
+
+ ecc_point_mult(&pk, &curve_g, priv, NULL, vli_num_bits(priv));
+ } while (ecc_point_is_zero(&pk));
+
+ ecc_native2bytes(priv, private_key);
+ ecc_native2bytes(pk.x, public_key);
+ ecc_native2bytes(pk.y, &public_key[32]);
+
+ return true;
+}
+
+bool ecdh_shared_secret(const u8 public_key[64], const u8 private_key[32],
+ u8 secret[32])
+{
+ u64 priv[NUM_ECC_DIGITS];
+ u64 rand[NUM_ECC_DIGITS];
+ struct ecc_point product, pk;
+
+ get_random_bytes(rand, ECC_BYTES);
+
+ ecc_bytes2native(public_key, pk.x);
+ ecc_bytes2native(&public_key[32], pk.y);
+ ecc_bytes2native(private_key, priv);
+
+ ecc_point_mult(&product, &pk, priv, rand, vli_num_bits(priv));
+
+ ecc_native2bytes(product.x, secret);
+
+ return !ecc_point_is_zero(&product);
+}
diff --git a/net/bluetooth/ecc.h b/net/bluetooth/ecc.h
new file mode 100644
index 0000000..8d6a2f4
--- /dev/null
+++ b/net/bluetooth/ecc.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2013, Kenneth MacKay
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Create a public/private key pair.
+ * Outputs:
+ * public_key - Will be filled in with the public key.
+ * private_key - Will be filled in with the private key.
+ *
+ * Returns true if the key pair was generated successfully, false
+ * if an error occurred. The keys are with the LSB first.
+ */
+bool ecc_make_key(u8 public_key[64], u8 private_key[32]);
+
+/* Compute a shared secret given your secret key and someone else's
+ * public key.
+ * Note: It is recommended that you hash the result of ecdh_shared_secret
+ * before using it for symmetric encryption or HMAC.
+ *
+ * Inputs:
+ * public_key - The public key of the remote party
+ * private_key - Your private key.
+ *
+ * Outputs:
+ * secret - Will be filled in with the shared secret value.
+ *
+ * Returns true if the shared secret was generated successfully, false
+ * if an error occurred. Both input and output parameters are with the
+ * LSB first.
+ */
+bool ecdh_shared_secret(const u8 public_key[64], const u8 private_key[32],
+ u8 secret[32]);
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index b9517bd..fe18825 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -141,10 +141,11 @@
*/
if (conn->type == ACL_LINK && conn->role == HCI_ROLE_MASTER) {
struct hci_dev *hdev = conn->hdev;
- struct hci_cp_read_clock_offset cp;
+ struct hci_cp_read_clock_offset clkoff_cp;
- cp.handle = cpu_to_le16(conn->handle);
- hci_send_cmd(hdev, HCI_OP_READ_CLOCK_OFFSET, sizeof(cp), &cp);
+ clkoff_cp.handle = cpu_to_le16(conn->handle);
+ hci_send_cmd(hdev, HCI_OP_READ_CLOCK_OFFSET, sizeof(clkoff_cp),
+ &clkoff_cp);
}
conn->state = BT_DISCONN;
@@ -415,7 +416,7 @@
* happen with broken hardware or if low duty cycle was used
* (which doesn't have a timeout of its own).
*/
- if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) {
+ if (conn->role == HCI_ROLE_SLAVE) {
u8 enable = 0x00;
hci_send_cmd(hdev, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable),
&enable);
@@ -448,6 +449,7 @@
conn->io_capability = hdev->io_capability;
conn->remote_auth = 0xff;
conn->key_type = 0xff;
+ conn->rssi = HCI_RSSI_INVALID;
conn->tx_power = HCI_TX_POWER_INVALID;
conn->max_tx_power = HCI_TX_POWER_INVALID;
@@ -517,7 +519,7 @@
/* Unacked frames */
hdev->acl_cnt += conn->sent;
} else if (conn->type == LE_LINK) {
- cancel_delayed_work_sync(&conn->le_conn_timeout);
+ cancel_delayed_work(&conn->le_conn_timeout);
if (hdev->le_pkts)
hdev->le_cnt += conn->sent;
@@ -544,6 +546,9 @@
hci_conn_del_sysfs(conn);
+ if (test_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags))
+ hci_conn_params_del(conn->hdev, &conn->dst, conn->dst_type);
+
hci_dev_put(hdev);
hci_conn_put(conn);
@@ -656,7 +661,7 @@
memset(&cp, 0, sizeof(cp));
/* Update random address, but set require_privacy to false so
- * that we never connect with an unresolvable address.
+ * that we never connect with an non-resolvable address.
*/
if (hci_update_random_address(req, false, &own_addr_type))
return;
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index cb05d7f..5dcacf9 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -200,31 +200,6 @@
.release = single_release,
};
-static int whitelist_show(struct seq_file *f, void *p)
-{
- struct hci_dev *hdev = f->private;
- struct bdaddr_list *b;
-
- hci_dev_lock(hdev);
- list_for_each_entry(b, &hdev->whitelist, list)
- seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type);
- hci_dev_unlock(hdev);
-
- return 0;
-}
-
-static int whitelist_open(struct inode *inode, struct file *file)
-{
- return single_open(file, whitelist_show, inode->i_private);
-}
-
-static const struct file_operations whitelist_fops = {
- .open = whitelist_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
static int uuids_show(struct seq_file *f, void *p)
{
struct hci_dev *hdev = f->private;
@@ -299,15 +274,13 @@
static int link_keys_show(struct seq_file *f, void *ptr)
{
struct hci_dev *hdev = f->private;
- struct list_head *p, *n;
+ struct link_key *key;
- hci_dev_lock(hdev);
- list_for_each_safe(p, n, &hdev->link_keys) {
- struct link_key *key = list_entry(p, struct link_key, list);
+ rcu_read_lock();
+ list_for_each_entry_rcu(key, &hdev->link_keys, list)
seq_printf(f, "%pMR %u %*phN %u\n", &key->bdaddr, key->type,
HCI_LINK_KEY_SIZE, key->val, key->pin_len);
- }
- hci_dev_unlock(hdev);
+ rcu_read_unlock();
return 0;
}
@@ -433,6 +406,49 @@
.llseek = default_llseek,
};
+static ssize_t force_lesc_support_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct hci_dev *hdev = file->private_data;
+ char buf[3];
+
+ buf[0] = test_bit(HCI_FORCE_LESC, &hdev->dbg_flags) ? 'Y': 'N';
+ buf[1] = '\n';
+ buf[2] = '\0';
+ return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static ssize_t force_lesc_support_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct hci_dev *hdev = file->private_data;
+ char buf[32];
+ size_t buf_size = min(count, (sizeof(buf)-1));
+ bool enable;
+
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ buf[buf_size] = '\0';
+ if (strtobool(buf, &enable))
+ return -EINVAL;
+
+ if (enable == test_bit(HCI_FORCE_LESC, &hdev->dbg_flags))
+ return -EALREADY;
+
+ change_bit(HCI_FORCE_LESC, &hdev->dbg_flags);
+
+ return count;
+}
+
+static const struct file_operations force_lesc_support_fops = {
+ .open = simple_open,
+ .read = force_lesc_support_read,
+ .write = force_lesc_support_write,
+ .llseek = default_llseek,
+};
+
static ssize_t sc_only_mode_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
@@ -773,16 +789,15 @@
static int identity_resolving_keys_show(struct seq_file *f, void *ptr)
{
struct hci_dev *hdev = f->private;
- struct list_head *p, *n;
+ struct smp_irk *irk;
- hci_dev_lock(hdev);
- list_for_each_safe(p, n, &hdev->identity_resolving_keys) {
- struct smp_irk *irk = list_entry(p, struct smp_irk, list);
+ rcu_read_lock();
+ list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
seq_printf(f, "%pMR (type %u) %*phN %pMR\n",
&irk->bdaddr, irk->addr_type,
16, irk->val, &irk->rpa);
}
- hci_dev_unlock(hdev);
+ rcu_read_unlock();
return 0;
}
@@ -803,17 +818,15 @@
static int long_term_keys_show(struct seq_file *f, void *ptr)
{
struct hci_dev *hdev = f->private;
- struct list_head *p, *n;
+ struct smp_ltk *ltk;
- hci_dev_lock(hdev);
- list_for_each_safe(p, n, &hdev->long_term_keys) {
- struct smp_ltk *ltk = list_entry(p, struct smp_ltk, list);
+ rcu_read_lock();
+ list_for_each_entry_rcu(ltk, &hdev->long_term_keys, list)
seq_printf(f, "%pMR (type %u) %u 0x%02x %u %.4x %.16llx %*phN\n",
<k->bdaddr, ltk->bdaddr_type, ltk->authenticated,
ltk->type, ltk->enc_size, __le16_to_cpu(ltk->ediv),
__le64_to_cpu(ltk->rand), 16, ltk->val);
- }
- hci_dev_unlock(hdev);
+ rcu_read_unlock();
return 0;
}
@@ -1030,10 +1043,13 @@
{
struct hci_dev *hdev = f->private;
struct hci_conn_params *p;
+ struct bdaddr_list *b;
hci_dev_lock(hdev);
+ list_for_each_entry(b, &hdev->whitelist, list)
+ seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type);
list_for_each_entry(p, &hdev->le_conn_params, list) {
- seq_printf(f, "%pMR %u %u\n", &p->addr, p->addr_type,
+ seq_printf(f, "%pMR (type %u) %u\n", &p->addr, p->addr_type,
p->auto_connect);
}
hci_dev_unlock(hdev);
@@ -1147,13 +1163,16 @@
hdev->req_status = HCI_REQ_PEND;
- err = hci_req_run(&req, hci_req_sync_complete);
- if (err < 0)
- return ERR_PTR(err);
-
add_wait_queue(&hdev->req_wait_q, &wait);
set_current_state(TASK_INTERRUPTIBLE);
+ err = hci_req_run(&req, hci_req_sync_complete);
+ if (err < 0) {
+ remove_wait_queue(&hdev->req_wait_q, &wait);
+ set_current_state(TASK_RUNNING);
+ return ERR_PTR(err);
+ }
+
schedule_timeout(timeout);
remove_wait_queue(&hdev->req_wait_q, &wait);
@@ -1211,10 +1230,16 @@
func(&req, opt);
+ add_wait_queue(&hdev->req_wait_q, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+
err = hci_req_run(&req, hci_req_sync_complete);
if (err < 0) {
hdev->req_status = 0;
+ remove_wait_queue(&hdev->req_wait_q, &wait);
+ set_current_state(TASK_RUNNING);
+
/* ENODATA means the HCI request command queue is empty.
* This can happen when a request with conditionals doesn't
* trigger any commands to be sent. This is normal behavior
@@ -1226,9 +1251,6 @@
return err;
}
- add_wait_queue(&hdev->req_wait_q, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
-
schedule_timeout(timeout);
remove_wait_queue(&hdev->req_wait_q, &wait);
@@ -1351,8 +1373,6 @@
static void bredr_setup(struct hci_request *req)
{
- struct hci_dev *hdev = req->hdev;
-
__le16 param;
__u8 flt_type;
@@ -1381,14 +1401,6 @@
/* Connection accept timeout ~20 secs */
param = cpu_to_le16(0x7d00);
hci_req_add(req, HCI_OP_WRITE_CA_TIMEOUT, 2, ¶m);
-
- /* AVM Berlin (31), aka "BlueFRITZ!", reports version 1.2,
- * but it does not support page scan related HCI commands.
- */
- if (hdev->manufacturer != 31 && hdev->hci_ver > BLUETOOTH_VER_1_1) {
- hci_req_add(req, HCI_OP_READ_PAGE_SCAN_ACTIVITY, 0, NULL);
- hci_req_add(req, HCI_OP_READ_PAGE_SCAN_TYPE, 0, NULL);
- }
}
static void le_setup(struct hci_request *req)
@@ -1696,6 +1708,16 @@
if (hdev->commands[5] & 0x10)
hci_setup_link_policy(req);
+ if (hdev->commands[8] & 0x01)
+ hci_req_add(req, HCI_OP_READ_PAGE_SCAN_ACTIVITY, 0, NULL);
+
+ /* Some older Broadcom based Bluetooth 1.2 controllers do not
+ * support the Read Page Scan Type command. Check support for
+ * this command in the bit mask of supported commands.
+ */
+ if (hdev->commands[13] & 0x01)
+ hci_req_add(req, HCI_OP_READ_PAGE_SCAN_TYPE, 0, NULL);
+
if (lmp_le_capable(hdev)) {
u8 events[8];
@@ -1713,6 +1735,28 @@
* Parameter Request
*/
+ /* If the controller supports Extended Scanner Filter
+ * Policies, enable the correspondig event.
+ */
+ if (hdev->le_features[0] & HCI_LE_EXT_SCAN_POLICY)
+ events[1] |= 0x04; /* LE Direct Advertising
+ * Report
+ */
+
+ /* If the controller supports the LE Read Local P-256
+ * Public Key command, enable the corresponding event.
+ */
+ if (hdev->commands[34] & 0x02)
+ events[0] |= 0x80; /* LE Read Local P-256
+ * Public Key Complete
+ */
+
+ /* If the controller supports the LE Generate DHKey
+ * command, enable the corresponding event.
+ */
+ if (hdev->commands[34] & 0x04)
+ events[1] |= 0x01; /* LE Generate DHKey Complete */
+
hci_req_add(req, HCI_OP_LE_SET_EVENT_MASK, sizeof(events),
events);
@@ -1755,9 +1799,7 @@
hci_req_add(req, HCI_OP_READ_SYNC_TRAIN_PARAMS, 0, NULL);
/* Enable Secure Connections if supported and configured */
- if ((lmp_sc_capable(hdev) ||
- test_bit(HCI_FORCE_SC, &hdev->dbg_flags)) &&
- test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) {
+ if (bredr_sc_enabled(hdev)) {
u8 support = 0x01;
hci_req_add(req, HCI_OP_WRITE_SC_SUPPORT,
sizeof(support), &support);
@@ -1811,10 +1853,10 @@
&hdev->manufacturer);
debugfs_create_u8("hci_version", 0444, hdev->debugfs, &hdev->hci_ver);
debugfs_create_u16("hci_revision", 0444, hdev->debugfs, &hdev->hci_rev);
+ debugfs_create_file("device_list", 0444, hdev->debugfs, hdev,
+ &device_list_fops);
debugfs_create_file("blacklist", 0444, hdev->debugfs, hdev,
&blacklist_fops);
- debugfs_create_file("whitelist", 0444, hdev->debugfs, hdev,
- &whitelist_fops);
debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops);
debugfs_create_file("conn_info_min_age", 0644, hdev->debugfs, hdev,
@@ -1840,6 +1882,10 @@
hdev, &force_sc_support_fops);
debugfs_create_file("sc_only_mode", 0444, hdev->debugfs,
hdev, &sc_only_mode_fops);
+ if (lmp_le_capable(hdev))
+ debugfs_create_file("force_lesc_support", 0644,
+ hdev->debugfs, hdev,
+ &force_lesc_support_fops);
}
if (lmp_sniff_capable(hdev)) {
@@ -1893,8 +1939,6 @@
hdev, &adv_min_interval_fops);
debugfs_create_file("adv_max_interval", 0644, hdev->debugfs,
hdev, &adv_max_interval_fops);
- debugfs_create_file("device_list", 0444, hdev->debugfs, hdev,
- &device_list_fops);
debugfs_create_u16("discov_interleaved_timeout", 0644,
hdev->debugfs,
&hdev->discov_interleaved_timeout);
@@ -2138,7 +2182,7 @@
BT_DBG("cache %p, %pMR", cache, &data->bdaddr);
- hci_remove_remote_oob_data(hdev, &data->bdaddr);
+ hci_remove_remote_oob_data(hdev, &data->bdaddr, BDADDR_BREDR);
if (!data->ssp_mode)
flags |= MGMT_DEV_FOUND_LEGACY_PAIRING;
@@ -2584,7 +2628,18 @@
if (test_bit(HCI_MGMT, &hdev->dev_flags))
cancel_delayed_work_sync(&hdev->rpa_expired);
+ /* Avoid potential lockdep warnings from the *_flush() calls by
+ * ensuring the workqueue is empty up front.
+ */
+ drain_workqueue(hdev->workqueue);
+
hci_dev_lock(hdev);
+
+ if (!test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) {
+ if (hdev->dev_type == HCI_BREDR)
+ mgmt_powered(hdev, 0);
+ }
+
hci_inquiry_cache_flush(hdev);
hci_pend_le_actions_clear(hdev);
hci_conn_hash_flush(hdev);
@@ -2632,14 +2687,6 @@
hdev->flags &= BIT(HCI_RAW);
hdev->dev_flags &= ~HCI_PERSISTENT_MASK;
- if (!test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) {
- if (hdev->dev_type == HCI_BREDR) {
- hci_dev_lock(hdev);
- mgmt_powered(hdev, 0);
- hci_dev_unlock(hdev);
- }
- }
-
/* Controller radio is available but is currently powered down */
hdev->amp_status = AMP_STATUS_POWERED_DOWN;
@@ -2707,6 +2754,11 @@
skb_queue_purge(&hdev->rx_q);
skb_queue_purge(&hdev->cmd_q);
+ /* Avoid potential lockdep warnings from the *_flush() calls by
+ * ensuring the workqueue is empty up front.
+ */
+ drain_workqueue(hdev->workqueue);
+
hci_dev_lock(hdev);
hci_inquiry_cache_flush(hdev);
hci_conn_hash_flush(hdev);
@@ -3029,7 +3081,9 @@
err = hci_dev_do_open(hdev);
if (err < 0) {
+ hci_dev_lock(hdev);
mgmt_set_powered_failed(hdev, err);
+ hci_dev_unlock(hdev);
return;
}
@@ -3112,35 +3166,31 @@
void hci_link_keys_clear(struct hci_dev *hdev)
{
- struct list_head *p, *n;
+ struct link_key *key;
- list_for_each_safe(p, n, &hdev->link_keys) {
- struct link_key *key;
-
- key = list_entry(p, struct link_key, list);
-
- list_del(p);
- kfree(key);
+ list_for_each_entry_rcu(key, &hdev->link_keys, list) {
+ list_del_rcu(&key->list);
+ kfree_rcu(key, rcu);
}
}
void hci_smp_ltks_clear(struct hci_dev *hdev)
{
- struct smp_ltk *k, *tmp;
+ struct smp_ltk *k;
- list_for_each_entry_safe(k, tmp, &hdev->long_term_keys, list) {
- list_del(&k->list);
- kfree(k);
+ list_for_each_entry_rcu(k, &hdev->long_term_keys, list) {
+ list_del_rcu(&k->list);
+ kfree_rcu(k, rcu);
}
}
void hci_smp_irks_clear(struct hci_dev *hdev)
{
- struct smp_irk *k, *tmp;
+ struct smp_irk *k;
- list_for_each_entry_safe(k, tmp, &hdev->identity_resolving_keys, list) {
- list_del(&k->list);
- kfree(k);
+ list_for_each_entry_rcu(k, &hdev->identity_resolving_keys, list) {
+ list_del_rcu(&k->list);
+ kfree_rcu(k, rcu);
}
}
@@ -3148,9 +3198,14 @@
{
struct link_key *k;
- list_for_each_entry(k, &hdev->link_keys, list)
- if (bacmp(bdaddr, &k->bdaddr) == 0)
+ rcu_read_lock();
+ list_for_each_entry_rcu(k, &hdev->link_keys, list) {
+ if (bacmp(bdaddr, &k->bdaddr) == 0) {
+ rcu_read_unlock();
return k;
+ }
+ }
+ rcu_read_unlock();
return NULL;
}
@@ -3174,6 +3229,10 @@
if (!conn)
return true;
+ /* BR/EDR key derived using SC from an LE link */
+ if (conn->type == LE_LINK)
+ return true;
+
/* Neither local nor remote side had no-bonding as requirement */
if (conn->auth_type > 0x01 && conn->remote_auth > 0x01)
return true;
@@ -3199,34 +3258,22 @@
return HCI_ROLE_SLAVE;
}
-struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand,
- u8 role)
+struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 addr_type, u8 role)
{
struct smp_ltk *k;
- list_for_each_entry(k, &hdev->long_term_keys, list) {
- if (k->ediv != ediv || k->rand != rand)
+ rcu_read_lock();
+ list_for_each_entry_rcu(k, &hdev->long_term_keys, list) {
+ if (addr_type != k->bdaddr_type || bacmp(bdaddr, &k->bdaddr))
continue;
- if (ltk_role(k->type) != role)
- continue;
-
- return k;
- }
-
- return NULL;
-}
-
-struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
- u8 addr_type, u8 role)
-{
- struct smp_ltk *k;
-
- list_for_each_entry(k, &hdev->long_term_keys, list)
- if (addr_type == k->bdaddr_type &&
- bacmp(bdaddr, &k->bdaddr) == 0 &&
- ltk_role(k->type) == role)
+ if (smp_ltk_is_sc(k) || ltk_role(k->type) == role) {
+ rcu_read_unlock();
return k;
+ }
+ }
+ rcu_read_unlock();
return NULL;
}
@@ -3235,18 +3282,23 @@
{
struct smp_irk *irk;
- list_for_each_entry(irk, &hdev->identity_resolving_keys, list) {
- if (!bacmp(&irk->rpa, rpa))
- return irk;
- }
-
- list_for_each_entry(irk, &hdev->identity_resolving_keys, list) {
- if (smp_irk_matches(hdev, irk->val, rpa)) {
- bacpy(&irk->rpa, rpa);
+ rcu_read_lock();
+ list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
+ if (!bacmp(&irk->rpa, rpa)) {
+ rcu_read_unlock();
return irk;
}
}
+ list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
+ if (smp_irk_matches(hdev, irk->val, rpa)) {
+ bacpy(&irk->rpa, rpa);
+ rcu_read_unlock();
+ return irk;
+ }
+ }
+ rcu_read_unlock();
+
return NULL;
}
@@ -3259,11 +3311,15 @@
if (addr_type == ADDR_LE_DEV_RANDOM && (bdaddr->b[5] & 0xc0) != 0xc0)
return NULL;
- list_for_each_entry(irk, &hdev->identity_resolving_keys, list) {
+ rcu_read_lock();
+ list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
if (addr_type == irk->addr_type &&
- bacmp(bdaddr, &irk->bdaddr) == 0)
+ bacmp(bdaddr, &irk->bdaddr) == 0) {
+ rcu_read_unlock();
return irk;
+ }
}
+ rcu_read_unlock();
return NULL;
}
@@ -3284,7 +3340,7 @@
key = kzalloc(sizeof(*key), GFP_KERNEL);
if (!key)
return NULL;
- list_add(&key->list, &hdev->link_keys);
+ list_add_rcu(&key->list, &hdev->link_keys);
}
BT_DBG("%s key for %pMR type %u", hdev->name, bdaddr, type);
@@ -3322,14 +3378,14 @@
struct smp_ltk *key, *old_key;
u8 role = ltk_role(type);
- old_key = hci_find_ltk_by_addr(hdev, bdaddr, addr_type, role);
+ old_key = hci_find_ltk(hdev, bdaddr, addr_type, role);
if (old_key)
key = old_key;
else {
key = kzalloc(sizeof(*key), GFP_KERNEL);
if (!key)
return NULL;
- list_add(&key->list, &hdev->long_term_keys);
+ list_add_rcu(&key->list, &hdev->long_term_keys);
}
bacpy(&key->bdaddr, bdaddr);
@@ -3358,7 +3414,7 @@
bacpy(&irk->bdaddr, bdaddr);
irk->addr_type = addr_type;
- list_add(&irk->list, &hdev->identity_resolving_keys);
+ list_add_rcu(&irk->list, &hdev->identity_resolving_keys);
}
memcpy(irk->val, val, 16);
@@ -3377,25 +3433,25 @@
BT_DBG("%s removing %pMR", hdev->name, bdaddr);
- list_del(&key->list);
- kfree(key);
+ list_del_rcu(&key->list);
+ kfree_rcu(key, rcu);
return 0;
}
int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type)
{
- struct smp_ltk *k, *tmp;
+ struct smp_ltk *k;
int removed = 0;
- list_for_each_entry_safe(k, tmp, &hdev->long_term_keys, list) {
+ list_for_each_entry_rcu(k, &hdev->long_term_keys, list) {
if (bacmp(bdaddr, &k->bdaddr) || k->bdaddr_type != bdaddr_type)
continue;
BT_DBG("%s removing %pMR", hdev->name, bdaddr);
- list_del(&k->list);
- kfree(k);
+ list_del_rcu(&k->list);
+ kfree_rcu(k, rcu);
removed++;
}
@@ -3404,16 +3460,16 @@
void hci_remove_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type)
{
- struct smp_irk *k, *tmp;
+ struct smp_irk *k;
- list_for_each_entry_safe(k, tmp, &hdev->identity_resolving_keys, list) {
+ list_for_each_entry_rcu(k, &hdev->identity_resolving_keys, list) {
if (bacmp(bdaddr, &k->bdaddr) || k->addr_type != addr_type)
continue;
BT_DBG("%s removing %pMR", hdev->name, bdaddr);
- list_del(&k->list);
- kfree(k);
+ list_del_rcu(&k->list);
+ kfree_rcu(k, rcu);
}
}
@@ -3437,26 +3493,31 @@
}
struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev,
- bdaddr_t *bdaddr)
+ bdaddr_t *bdaddr, u8 bdaddr_type)
{
struct oob_data *data;
- list_for_each_entry(data, &hdev->remote_oob_data, list)
- if (bacmp(bdaddr, &data->bdaddr) == 0)
- return data;
+ list_for_each_entry(data, &hdev->remote_oob_data, list) {
+ if (bacmp(bdaddr, &data->bdaddr) != 0)
+ continue;
+ if (data->bdaddr_type != bdaddr_type)
+ continue;
+ return data;
+ }
return NULL;
}
-int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr)
+int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 bdaddr_type)
{
struct oob_data *data;
- data = hci_find_remote_oob_data(hdev, bdaddr);
+ data = hci_find_remote_oob_data(hdev, bdaddr, bdaddr_type);
if (!data)
return -ENOENT;
- BT_DBG("%s removing %pMR", hdev->name, bdaddr);
+ BT_DBG("%s removing %pMR (%u)", hdev->name, bdaddr, bdaddr_type);
list_del(&data->list);
kfree(data);
@@ -3475,52 +3536,37 @@
}
int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
- u8 *hash, u8 *randomizer)
+ u8 bdaddr_type, u8 *hash192, u8 *rand192,
+ u8 *hash256, u8 *rand256)
{
struct oob_data *data;
- data = hci_find_remote_oob_data(hdev, bdaddr);
+ data = hci_find_remote_oob_data(hdev, bdaddr, bdaddr_type);
if (!data) {
data = kmalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
bacpy(&data->bdaddr, bdaddr);
+ data->bdaddr_type = bdaddr_type;
list_add(&data->list, &hdev->remote_oob_data);
}
- memcpy(data->hash192, hash, sizeof(data->hash192));
- memcpy(data->randomizer192, randomizer, sizeof(data->randomizer192));
-
- memset(data->hash256, 0, sizeof(data->hash256));
- memset(data->randomizer256, 0, sizeof(data->randomizer256));
-
- BT_DBG("%s for %pMR", hdev->name, bdaddr);
-
- return 0;
-}
-
-int hci_add_remote_oob_ext_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
- u8 *hash192, u8 *randomizer192,
- u8 *hash256, u8 *randomizer256)
-{
- struct oob_data *data;
-
- data = hci_find_remote_oob_data(hdev, bdaddr);
- if (!data) {
- data = kmalloc(sizeof(*data), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
- bacpy(&data->bdaddr, bdaddr);
- list_add(&data->list, &hdev->remote_oob_data);
+ if (hash192 && rand192) {
+ memcpy(data->hash192, hash192, sizeof(data->hash192));
+ memcpy(data->rand192, rand192, sizeof(data->rand192));
+ } else {
+ memset(data->hash192, 0, sizeof(data->hash192));
+ memset(data->rand192, 0, sizeof(data->rand192));
}
- memcpy(data->hash192, hash192, sizeof(data->hash192));
- memcpy(data->randomizer192, randomizer192, sizeof(data->randomizer192));
-
- memcpy(data->hash256, hash256, sizeof(data->hash256));
- memcpy(data->randomizer256, randomizer256, sizeof(data->randomizer256));
+ if (hash256 && rand256) {
+ memcpy(data->hash256, hash256, sizeof(data->hash256));
+ memcpy(data->rand256, rand256, sizeof(data->rand256));
+ } else {
+ memset(data->hash256, 0, sizeof(data->hash256));
+ memset(data->rand256, 0, sizeof(data->rand256));
+ }
BT_DBG("%s for %pMR", hdev->name, bdaddr);
@@ -3913,17 +3959,29 @@
}
/* In case of required privacy without resolvable private address,
- * use an unresolvable private address. This is useful for active
+ * use an non-resolvable private address. This is useful for active
* scanning and non-connectable advertising.
*/
if (require_privacy) {
- bdaddr_t urpa;
+ bdaddr_t nrpa;
- get_random_bytes(&urpa, 6);
- urpa.b[5] &= 0x3f; /* Clear two most significant bits */
+ while (true) {
+ /* The non-resolvable private address is generated
+ * from random six bytes with the two most significant
+ * bits cleared.
+ */
+ get_random_bytes(&nrpa, 6);
+ nrpa.b[5] &= 0x3f;
+
+ /* The non-resolvable private address shall not be
+ * equal to the public address.
+ */
+ if (bacmp(&hdev->bdaddr, &nrpa))
+ break;
+ }
*own_addr_type = ADDR_LE_DEV_RANDOM;
- set_random_addr(req, &urpa);
+ set_random_addr(req, &nrpa);
return 0;
}
@@ -4220,6 +4278,7 @@
hci_remote_oob_data_clear(hdev);
hci_bdaddr_list_clear(&hdev->le_white_list);
hci_conn_params_clear_all(hdev);
+ hci_discovery_filter_clear(hdev);
hci_dev_unlock(hdev);
hci_dev_put(hdev);
@@ -4244,6 +4303,24 @@
}
EXPORT_SYMBOL(hci_resume_dev);
+/* Reset HCI device */
+int hci_reset_dev(struct hci_dev *hdev)
+{
+ const u8 hw_err[] = { HCI_EV_HARDWARE_ERROR, 0x01, 0x00 };
+ struct sk_buff *skb;
+
+ skb = bt_skb_alloc(3, GFP_ATOMIC);
+ if (!skb)
+ return -ENOMEM;
+
+ bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
+ memcpy(skb_put(skb, 3), hw_err, 3);
+
+ /* Send Hardware Error to upper stack */
+ return hci_recv_frame(hdev, skb);
+}
+EXPORT_SYMBOL(hci_reset_dev);
+
/* Receive frame from HCI drivers */
int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
@@ -4477,7 +4554,7 @@
BT_DBG("length %u", skb_queue_len(&req->cmd_q));
- /* If an error occured during request building, remove all HCI
+ /* If an error occurred during request building, remove all HCI
* commands queued on the HCI request queue.
*/
if (req->err) {
@@ -4546,7 +4623,7 @@
return -ENOMEM;
}
- /* Stand-alone HCI commands must be flaged as
+ /* Stand-alone HCI commands must be flagged as
* single-command requests.
*/
bt_cb(skb)->req.start = true;
@@ -4566,7 +4643,7 @@
BT_DBG("%s opcode 0x%4.4x plen %d", hdev->name, opcode, plen);
- /* If an error occured during request building, there is no point in
+ /* If an error occurred during request building, there is no point in
* queueing the HCI command. We can simply return.
*/
if (req->err)
@@ -4661,8 +4738,12 @@
skb_shinfo(skb)->frag_list = NULL;
- /* Queue all fragments atomically */
- spin_lock(&queue->lock);
+ /* Queue all fragments atomically. We need to use spin_lock_bh
+ * here because of 6LoWPAN links, as there this function is
+ * called from softirq and using normal spin lock could cause
+ * deadlocks.
+ */
+ spin_lock_bh(&queue->lock);
__skb_queue_tail(queue, skb);
@@ -4679,7 +4760,7 @@
__skb_queue_tail(queue, skb);
} while (list);
- spin_unlock(&queue->lock);
+ spin_unlock_bh(&queue->lock);
}
}
@@ -5556,7 +5637,7 @@
u8 filter_policy;
/* Set require_privacy to false since no SCAN_REQ are send
- * during passive scanning. Not using an unresolvable address
+ * during passive scanning. Not using an non-resolvable address
* here is important so that peer devices using direct
* advertising with our address will be correctly reported
* by the controller.
@@ -5570,6 +5651,19 @@
*/
filter_policy = update_white_list(req);
+ /* When the controller is using random resolvable addresses and
+ * with that having LE privacy enabled, then controllers with
+ * Extended Scanner Filter Policies support can now enable support
+ * for handling directed advertising.
+ *
+ * So instead of using filter polices 0x00 (no whitelist)
+ * and 0x01 (whitelist enabled) use the new filter policies
+ * 0x02 (no whitelist) and 0x03 (whitelist enabled).
+ */
+ if (test_bit(HCI_PRIVACY, &hdev->dev_flags) &&
+ (hdev->le_features[0] & HCI_LE_EXT_SCAN_POLICY))
+ filter_policy |= 0x02;
+
memset(¶m_cp, 0, sizeof(param_cp));
param_cp.type = LE_SCAN_PASSIVE;
param_cp.interval = cpu_to_le16(hdev->le_scan_interval);
@@ -5621,6 +5715,15 @@
if (hdev->discovery.state != DISCOVERY_STOPPED)
return;
+ /* Reset RSSI and UUID filters when starting background scanning
+ * since these filters are meant for service discovery only.
+ *
+ * The Start Discovery and Start Service Discovery operations
+ * ensure to set proper values for RSSI threshold and UUID
+ * filter list. So it is safe to just reset them here.
+ */
+ hci_discovery_filter_clear(hdev);
+
hci_req_init(&req, hdev);
if (list_empty(&hdev->pend_le_conns) &&
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 8b0a2a6..39a5c8a 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -189,6 +189,9 @@
clear_bit(HCI_RESET, &hdev->flags);
+ if (status)
+ return;
+
/* Reset all non-persistent flags */
hdev->dev_flags &= ~HCI_PERSISTENT_MASK;
@@ -205,6 +208,8 @@
hdev->le_scan_type = LE_SCAN_PASSIVE;
hdev->ssp_debug_mode = 0;
+
+ hci_bdaddr_list_clear(&hdev->le_white_list);
}
static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
@@ -252,6 +257,8 @@
if (!sent)
return;
+ hci_dev_lock(hdev);
+
if (!status) {
__u8 param = *((__u8 *) sent);
@@ -263,6 +270,8 @@
if (test_bit(HCI_MGMT, &hdev->dev_flags))
mgmt_auth_enable_complete(hdev, status);
+
+ hci_dev_unlock(hdev);
}
static void hci_cc_write_encrypt_mode(struct hci_dev *hdev, struct sk_buff *skb)
@@ -438,6 +447,8 @@
if (!sent)
return;
+ hci_dev_lock(hdev);
+
if (!status) {
if (sent->mode)
hdev->features[1][0] |= LMP_HOST_SSP;
@@ -453,6 +464,8 @@
else
clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
}
+
+ hci_dev_unlock(hdev);
}
static void hci_cc_write_sc_support(struct hci_dev *hdev, struct sk_buff *skb)
@@ -466,6 +479,8 @@
if (!sent)
return;
+ hci_dev_lock(hdev);
+
if (!status) {
if (sent->support)
hdev->features[1][0] |= LMP_HOST_SC;
@@ -481,6 +496,8 @@
else
clear_bit(HCI_SC_ENABLED, &hdev->dev_flags);
}
+
+ hci_dev_unlock(hdev);
}
static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb)
@@ -989,8 +1006,8 @@
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
hci_dev_lock(hdev);
- mgmt_read_local_oob_data_complete(hdev, rp->hash, rp->randomizer,
- NULL, NULL, rp->status);
+ mgmt_read_local_oob_data_complete(hdev, rp->hash, rp->rand, NULL, NULL,
+ rp->status);
hci_dev_unlock(hdev);
}
@@ -1002,8 +1019,8 @@
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
hci_dev_lock(hdev);
- mgmt_read_local_oob_data_complete(hdev, rp->hash192, rp->randomizer192,
- rp->hash256, rp->randomizer256,
+ mgmt_read_local_oob_data_complete(hdev, rp->hash192, rp->rand192,
+ rp->hash256, rp->rand256,
rp->status);
hci_dev_unlock(hdev);
}
@@ -1045,7 +1062,7 @@
hci_dev_lock(hdev);
- /* If we're doing connection initation as peripheral. Set a
+ /* If we're doing connection initiation as peripheral. Set a
* timeout in case something goes wrong.
*/
if (*sent) {
@@ -1130,6 +1147,8 @@
if (!cp)
return;
+ hci_dev_lock(hdev);
+
switch (cp->enable) {
case LE_SCAN_ENABLE:
set_bit(HCI_LE_SCAN, &hdev->dev_flags);
@@ -1179,6 +1198,8 @@
BT_ERR("Used reserved LE_Scan_Enable param %d", cp->enable);
break;
}
+
+ hci_dev_unlock(hdev);
}
static void hci_cc_le_read_white_list_size(struct hci_dev *hdev,
@@ -1273,6 +1294,8 @@
if (!sent)
return;
+ hci_dev_lock(hdev);
+
if (sent->le) {
hdev->features[1][0] |= LMP_HOST_LE;
set_bit(HCI_LE_ENABLED, &hdev->dev_flags);
@@ -1286,6 +1309,8 @@
hdev->features[1][0] |= LMP_HOST_LE_BREDR;
else
hdev->features[1][0] &= ~LMP_HOST_LE_BREDR;
+
+ hci_dev_unlock(hdev);
}
static void hci_cc_set_adv_param(struct hci_dev *hdev, struct sk_buff *skb)
@@ -1576,9 +1601,15 @@
struct discovery_state *discov = &hdev->discovery;
struct inquiry_entry *e;
- if (conn && !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
- mgmt_device_connected(hdev, bdaddr, ACL_LINK, 0x00, 0, name,
- name_len, conn->dev_class);
+ /* Update the mgmt connected state if necessary. Be careful with
+ * conn objects that exist but are not (yet) connected however.
+ * Only those in BT_CONFIG or BT_CONNECTED states can be
+ * considered connected.
+ */
+ if (conn &&
+ (conn->state == BT_CONFIG || conn->state == BT_CONNECTED) &&
+ !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
+ mgmt_device_connected(hdev, conn, 0, name, name_len);
if (discov->state == DISCOVERY_STOPPED)
return;
@@ -1943,6 +1974,29 @@
hci_dev_unlock(hdev);
}
+static void hci_cs_switch_role(struct hci_dev *hdev, u8 status)
+{
+ struct hci_cp_switch_role *cp;
+ struct hci_conn *conn;
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+ if (!status)
+ return;
+
+ cp = hci_sent_cmd_data(hdev, HCI_OP_SWITCH_ROLE);
+ if (!cp)
+ return;
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
+ if (conn)
+ clear_bit(HCI_CONN_RSWITCH_PEND, &conn->flags);
+
+ hci_dev_unlock(hdev);
+}
+
static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
__u8 status = *((__u8 *) skb->data);
@@ -2009,13 +2063,14 @@
data.pscan_mode = info->pscan_mode;
memcpy(data.dev_class, info->dev_class, 3);
data.clock_offset = info->clock_offset;
- data.rssi = 0x00;
+ data.rssi = HCI_RSSI_INVALID;
data.ssp_mode = 0x00;
flags = hci_inquiry_cache_update(hdev, &data, false);
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
- info->dev_class, 0, flags, NULL, 0, NULL, 0);
+ info->dev_class, HCI_RSSI_INVALID,
+ flags, NULL, 0, NULL, 0);
}
hci_dev_unlock(hdev);
@@ -2536,9 +2591,7 @@
cp.pscan_rep_mode = 0x02;
hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp);
} else if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
- mgmt_device_connected(hdev, &conn->dst, conn->type,
- conn->dst_type, 0, NULL, 0,
- conn->dev_class);
+ mgmt_device_connected(hdev, conn, 0, NULL, 0);
if (!hci_outgoing_auth_needed(hdev, conn)) {
conn->state = BT_CONNECTED;
@@ -2848,6 +2901,10 @@
hci_cs_create_conn(hdev, ev->status);
break;
+ case HCI_OP_DISCONNECT:
+ hci_cs_disconnect(hdev, ev->status);
+ break;
+
case HCI_OP_ADD_SCO:
hci_cs_add_sco(hdev, ev->status);
break;
@@ -2876,6 +2933,14 @@
hci_cs_setup_sync_conn(hdev, ev->status);
break;
+ case HCI_OP_CREATE_PHY_LINK:
+ hci_cs_create_phylink(hdev, ev->status);
+ break;
+
+ case HCI_OP_ACCEPT_PHY_LINK:
+ hci_cs_accept_phylink(hdev, ev->status);
+ break;
+
case HCI_OP_SNIFF_MODE:
hci_cs_sniff_mode(hdev, ev->status);
break;
@@ -2884,16 +2949,8 @@
hci_cs_exit_sniff_mode(hdev, ev->status);
break;
- case HCI_OP_DISCONNECT:
- hci_cs_disconnect(hdev, ev->status);
- break;
-
- case HCI_OP_CREATE_PHY_LINK:
- hci_cs_create_phylink(hdev, ev->status);
- break;
-
- case HCI_OP_ACCEPT_PHY_LINK:
- hci_cs_accept_phylink(hdev, ev->status);
+ case HCI_OP_SWITCH_ROLE:
+ hci_cs_switch_role(hdev, ev->status);
break;
case HCI_OP_LE_CREATE_CONN:
@@ -2923,6 +2980,13 @@
}
}
+static void hci_hardware_error_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_ev_hardware_error *ev = (void *) skb->data;
+
+ BT_ERR("%s hardware error 0x%2.2x", hdev->name, ev->code);
+}
+
static void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_role_change *ev = (void *) skb->data;
@@ -3148,6 +3212,38 @@
hci_dev_unlock(hdev);
}
+static void conn_set_key(struct hci_conn *conn, u8 key_type, u8 pin_len)
+{
+ if (key_type == HCI_LK_CHANGED_COMBINATION)
+ return;
+
+ conn->pin_length = pin_len;
+ conn->key_type = key_type;
+
+ switch (key_type) {
+ case HCI_LK_LOCAL_UNIT:
+ case HCI_LK_REMOTE_UNIT:
+ case HCI_LK_DEBUG_COMBINATION:
+ return;
+ case HCI_LK_COMBINATION:
+ if (pin_len == 16)
+ conn->pending_sec_level = BT_SECURITY_HIGH;
+ else
+ conn->pending_sec_level = BT_SECURITY_MEDIUM;
+ break;
+ case HCI_LK_UNAUTH_COMBINATION_P192:
+ case HCI_LK_UNAUTH_COMBINATION_P256:
+ conn->pending_sec_level = BT_SECURITY_MEDIUM;
+ break;
+ case HCI_LK_AUTH_COMBINATION_P192:
+ conn->pending_sec_level = BT_SECURITY_HIGH;
+ break;
+ case HCI_LK_AUTH_COMBINATION_P256:
+ conn->pending_sec_level = BT_SECURITY_FIPS;
+ break;
+ }
+}
+
static void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_link_key_req *ev = (void *) skb->data;
@@ -3174,6 +3270,8 @@
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
if (conn) {
+ clear_bit(HCI_CONN_NEW_LINK_KEY, &conn->flags);
+
if ((key->type == HCI_LK_UNAUTH_COMBINATION_P192 ||
key->type == HCI_LK_UNAUTH_COMBINATION_P256) &&
conn->auth_type != 0xff && (conn->auth_type & 0x01)) {
@@ -3189,8 +3287,7 @@
goto not_found;
}
- conn->key_type = key->type;
- conn->pin_length = key->pin_len;
+ conn_set_key(conn, key->type, key->pin_len);
}
bacpy(&cp.bdaddr, &ev->bdaddr);
@@ -3220,16 +3317,15 @@
hci_dev_lock(hdev);
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
- if (conn) {
- hci_conn_hold(conn);
- conn->disc_timeout = HCI_DISCONN_TIMEOUT;
- pin_len = conn->pin_length;
+ if (!conn)
+ goto unlock;
- if (ev->key_type != HCI_LK_CHANGED_COMBINATION)
- conn->key_type = ev->key_type;
+ hci_conn_hold(conn);
+ conn->disc_timeout = HCI_DISCONN_TIMEOUT;
+ hci_conn_drop(conn);
- hci_conn_drop(conn);
- }
+ set_bit(HCI_CONN_NEW_LINK_KEY, &conn->flags);
+ conn_set_key(conn, ev->key_type, conn->pin_length);
if (!test_bit(HCI_MGMT, &hdev->dev_flags))
goto unlock;
@@ -3239,6 +3335,12 @@
if (!key)
goto unlock;
+ /* Update connection information since adding the key will have
+ * fixed up the type in the case of changed combination keys.
+ */
+ if (ev->key_type == HCI_LK_CHANGED_COMBINATION)
+ conn_set_key(conn, key->type, key->pin_len);
+
mgmt_new_link_key(hdev, key, persistent);
/* Keep debug keys around only if the HCI_KEEP_DEBUG_KEYS flag
@@ -3248,15 +3350,16 @@
*/
if (key->type == HCI_LK_DEBUG_COMBINATION &&
!test_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags)) {
- list_del(&key->list);
- kfree(key);
- } else if (conn) {
- if (persistent)
- clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags);
- else
- set_bit(HCI_CONN_FLUSH_KEY, &conn->flags);
+ list_del_rcu(&key->list);
+ kfree_rcu(key, rcu);
+ goto unlock;
}
+ if (persistent)
+ clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags);
+ else
+ set_bit(HCI_CONN_FLUSH_KEY, &conn->flags);
+
unlock:
hci_dev_unlock(hdev);
}
@@ -3434,9 +3537,7 @@
cp.pscan_rep_mode = 0x02;
hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp);
} else if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
- mgmt_device_connected(hdev, &conn->dst, conn->type,
- conn->dst_type, 0, NULL, 0,
- conn->dev_class);
+ mgmt_device_connected(hdev, conn, 0, NULL, 0);
if (!hci_outgoing_auth_needed(hdev, conn)) {
conn->state = BT_CONNECTED;
@@ -3693,7 +3794,7 @@
cp.authentication = conn->auth_type;
- if (hci_find_remote_oob_data(hdev, &conn->dst) &&
+ if (hci_find_remote_oob_data(hdev, &conn->dst, BDADDR_BREDR) &&
(conn->out || test_bit(HCI_CONN_REMOTE_OOB, &conn->flags)))
cp.oob_data = 0x01;
else
@@ -3948,18 +4049,16 @@
if (!test_bit(HCI_MGMT, &hdev->dev_flags))
goto unlock;
- data = hci_find_remote_oob_data(hdev, &ev->bdaddr);
+ data = hci_find_remote_oob_data(hdev, &ev->bdaddr, BDADDR_BREDR);
if (data) {
- if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) {
+ if (bredr_sc_enabled(hdev)) {
struct hci_cp_remote_oob_ext_data_reply cp;
bacpy(&cp.bdaddr, &ev->bdaddr);
memcpy(cp.hash192, data->hash192, sizeof(cp.hash192));
- memcpy(cp.randomizer192, data->randomizer192,
- sizeof(cp.randomizer192));
+ memcpy(cp.rand192, data->rand192, sizeof(cp.rand192));
memcpy(cp.hash256, data->hash256, sizeof(cp.hash256));
- memcpy(cp.randomizer256, data->randomizer256,
- sizeof(cp.randomizer256));
+ memcpy(cp.rand256, data->rand256, sizeof(cp.rand256));
hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_EXT_DATA_REPLY,
sizeof(cp), &cp);
@@ -3968,8 +4067,7 @@
bacpy(&cp.bdaddr, &ev->bdaddr);
memcpy(cp.hash, data->hash192, sizeof(cp.hash));
- memcpy(cp.randomizer, data->randomizer192,
- sizeof(cp.randomizer));
+ memcpy(cp.rand, data->rand192, sizeof(cp.rand));
hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_REPLY,
sizeof(cp), &cp);
@@ -4214,8 +4312,7 @@
}
if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
- mgmt_device_connected(hdev, &conn->dst, conn->type,
- conn->dst_type, 0, NULL, 0, NULL);
+ mgmt_device_connected(hdev, conn, 0, NULL, 0);
conn->sec_level = BT_SECURITY_LOW;
conn->handle = __le16_to_cpu(ev->handle);
@@ -4269,25 +4366,26 @@
}
/* This function requires the caller holds hdev->lock */
-static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr,
- u8 addr_type, u8 adv_type)
+static struct hci_conn *check_pending_le_conn(struct hci_dev *hdev,
+ bdaddr_t *addr,
+ u8 addr_type, u8 adv_type)
{
struct hci_conn *conn;
struct hci_conn_params *params;
/* If the event is not connectable don't proceed further */
if (adv_type != LE_ADV_IND && adv_type != LE_ADV_DIRECT_IND)
- return;
+ return NULL;
/* Ignore if the device is blocked */
if (hci_bdaddr_list_lookup(&hdev->blacklist, addr, addr_type))
- return;
+ return NULL;
/* Most controller will fail if we try to create new connections
* while we have an existing one in slave role.
*/
if (hdev->conn_hash.le_num_slave > 0)
- return;
+ return NULL;
/* If we're not connectable only connect devices that we have in
* our pend_le_conns list.
@@ -4295,7 +4393,7 @@
params = hci_pend_le_action_lookup(&hdev->pend_le_conns,
addr, addr_type);
if (!params)
- return;
+ return NULL;
switch (params->auto_connect) {
case HCI_AUTO_CONN_DIRECT:
@@ -4304,7 +4402,7 @@
* incoming connections from slave devices.
*/
if (adv_type != LE_ADV_DIRECT_IND)
- return;
+ return NULL;
break;
case HCI_AUTO_CONN_ALWAYS:
/* Devices advertising with ADV_IND or ADV_DIRECT_IND
@@ -4315,7 +4413,7 @@
*/
break;
default:
- return;
+ return NULL;
}
conn = hci_connect_le(hdev, addr, addr_type, BT_SECURITY_LOW,
@@ -4328,7 +4426,7 @@
* count consistent once the connection is established.
*/
params->conn = hci_conn_get(conn);
- return;
+ return conn;
}
switch (PTR_ERR(conn)) {
@@ -4341,17 +4439,48 @@
break;
default:
BT_DBG("Failed to connect: err %ld", PTR_ERR(conn));
+ return NULL;
}
+
+ return NULL;
}
static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
- u8 bdaddr_type, s8 rssi, u8 *data, u8 len)
+ u8 bdaddr_type, bdaddr_t *direct_addr,
+ u8 direct_addr_type, s8 rssi, u8 *data, u8 len)
{
struct discovery_state *d = &hdev->discovery;
struct smp_irk *irk;
+ struct hci_conn *conn;
bool match;
u32 flags;
+ /* If the direct address is present, then this report is from
+ * a LE Direct Advertising Report event. In that case it is
+ * important to see if the address is matching the local
+ * controller address.
+ */
+ if (direct_addr) {
+ /* Only resolvable random addresses are valid for these
+ * kind of reports and others can be ignored.
+ */
+ if (!hci_bdaddr_is_rpa(direct_addr, direct_addr_type))
+ return;
+
+ /* If the controller is not using resolvable random
+ * addresses, then this report can be ignored.
+ */
+ if (!test_bit(HCI_PRIVACY, &hdev->dev_flags))
+ return;
+
+ /* If the local IRK of the controller does not match
+ * with the resolvable random address provided, then
+ * this report can be ignored.
+ */
+ if (!smp_irk_matches(hdev, hdev->irk, direct_addr))
+ return;
+ }
+
/* Check if we need to convert to identity address */
irk = hci_get_irk(hdev, bdaddr, bdaddr_type);
if (irk) {
@@ -4360,7 +4489,14 @@
}
/* Check if we have been requested to connect to this device */
- check_pending_le_conn(hdev, bdaddr, bdaddr_type, type);
+ conn = check_pending_le_conn(hdev, bdaddr, bdaddr_type, type);
+ if (conn && type == LE_ADV_IND) {
+ /* Store report for later inclusion by
+ * mgmt_device_connected
+ */
+ memcpy(conn->le_adv_data, data, len);
+ conn->le_adv_data_len = len;
+ }
/* Passive scanning shouldn't trigger any device found events,
* except for devices marked as CONN_REPORT for which we do send
@@ -4481,7 +4617,8 @@
rssi = ev->data[ev->length];
process_adv_report(hdev, ev->evt_type, &ev->bdaddr,
- ev->bdaddr_type, rssi, ev->data, ev->length);
+ ev->bdaddr_type, NULL, 0, rssi,
+ ev->data, ev->length);
ptr += sizeof(*ev) + ev->length + 1;
}
@@ -4505,10 +4642,20 @@
if (conn == NULL)
goto not_found;
- ltk = hci_find_ltk(hdev, ev->ediv, ev->rand, conn->role);
- if (ltk == NULL)
+ ltk = hci_find_ltk(hdev, &conn->dst, conn->dst_type, conn->role);
+ if (!ltk)
goto not_found;
+ if (smp_ltk_is_sc(ltk)) {
+ /* With SC both EDiv and Rand are set to zero */
+ if (ev->ediv || ev->rand)
+ goto not_found;
+ } else {
+ /* For non-SC keys check that EDiv and Rand match */
+ if (ev->ediv != ltk->ediv || ev->rand != ltk->rand)
+ goto not_found;
+ }
+
memcpy(cp.ltk, ltk->val, sizeof(ltk->val));
cp.handle = cpu_to_le16(conn->handle);
@@ -4526,8 +4673,8 @@
*/
if (ltk->type == SMP_STK) {
set_bit(HCI_CONN_STK_ENCRYPT, &conn->flags);
- list_del(<k->list);
- kfree(ltk);
+ list_del_rcu(<k->list);
+ kfree_rcu(ltk, rcu);
} else {
clear_bit(HCI_CONN_STK_ENCRYPT, &conn->flags);
}
@@ -4612,6 +4759,27 @@
hci_send_cmd(hdev, HCI_OP_LE_CONN_PARAM_REQ_REPLY, sizeof(cp), &cp);
}
+static void hci_le_direct_adv_report_evt(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ u8 num_reports = skb->data[0];
+ void *ptr = &skb->data[1];
+
+ hci_dev_lock(hdev);
+
+ while (num_reports--) {
+ struct hci_ev_le_direct_adv_info *ev = ptr;
+
+ process_adv_report(hdev, ev->evt_type, &ev->bdaddr,
+ ev->bdaddr_type, &ev->direct_addr,
+ ev->direct_addr_type, ev->rssi, NULL, 0);
+
+ ptr += sizeof(*ev);
+ }
+
+ hci_dev_unlock(hdev);
+}
+
static void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_le_meta *le_ev = (void *) skb->data;
@@ -4639,6 +4807,10 @@
hci_le_remote_conn_param_req_evt(hdev, skb);
break;
+ case HCI_EV_LE_DIRECT_ADV_REPORT:
+ hci_le_direct_adv_report_evt(hdev, skb);
+ break;
+
default:
break;
}
@@ -4735,6 +4907,10 @@
hci_cmd_status_evt(hdev, skb);
break;
+ case HCI_EV_HARDWARE_ERROR:
+ hci_hardware_error_evt(hdev, skb);
+ break;
+
case HCI_EV_ROLE_CHANGE:
hci_role_change_evt(hdev, skb);
break;
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 115f149..2c245fd 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -878,7 +878,7 @@
}
skb_reset_transport_header(skb);
- err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ err = skb_copy_datagram_msg(skb, 0, msg, copied);
switch (hci_pi(sk)->channel) {
case HCI_CHANNEL_RAW:
@@ -947,7 +947,7 @@
if (!skb)
goto done;
- if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
+ if (memcpy_from_msg(skb_put(skb, len), msg, len)) {
err = -EFAULT;
goto drop;
}
@@ -987,7 +987,7 @@
skb_queue_tail(&hdev->raw_q, skb);
queue_work(hdev->workqueue, &hdev->tx_work);
} else {
- /* Stand-alone HCI commands must be flaged as
+ /* Stand-alone HCI commands must be flagged as
* single-command requests.
*/
bt_cb(skb)->req.start = true;
diff --git a/net/bluetooth/hidp/Kconfig b/net/bluetooth/hidp/Kconfig
index 869dd8b..d63ec58 100644
--- a/net/bluetooth/hidp/Kconfig
+++ b/net/bluetooth/hidp/Kconfig
@@ -1,7 +1,7 @@
config BT_HIDP
tristate "HIDP protocol support"
depends on m
- depends on BT && INPUT
+ depends on BT_BREDR && INPUT
depends on HID
help
HIDP (Human Interface Device Protocol) is a transport layer
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index 1687cc7..867f53e 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -740,14 +740,10 @@
struct hid_device *hid;
int err;
- session->rd_data = kzalloc(req->rd_size, GFP_KERNEL);
- if (!session->rd_data)
- return -ENOMEM;
+ session->rd_data = memdup_user(req->rd_data, req->rd_size);
+ if (IS_ERR(session->rd_data))
+ return PTR_ERR(session->rd_data);
- if (copy_from_user(session->rd_data, req->rd_data, req->rd_size)) {
- err = -EFAULT;
- goto fault;
- }
session->rd_size = req->rd_size;
hid = hid_allocate_device();
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index b6f9777..3c6bfb1 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -46,7 +46,6 @@
bool disable_ertm;
static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN | L2CAP_FEAT_UCD;
-static u8 l2cap_fixed_chan[8] = { L2CAP_FC_SIG_BREDR | L2CAP_FC_CONNLESS, };
static LIST_HEAD(chan_list);
static DEFINE_RWLOCK(chan_list_lock);
@@ -424,6 +423,9 @@
mutex_init(&chan->lock);
+ /* Set default lock nesting level */
+ atomic_set(&chan->nesting, L2CAP_NESTING_NORMAL);
+
write_lock(&chan_list_lock);
list_add(&chan->global_l, &chan_list);
write_unlock(&chan_list_lock);
@@ -567,7 +569,8 @@
__clear_chan_timer(chan);
- BT_DBG("chan %p, conn %p, err %d", chan, conn, err);
+ BT_DBG("chan %p, conn %p, err %d, state %s", chan, conn, err,
+ state_to_string(chan->state));
chan->ops->teardown(chan, err);
@@ -836,7 +839,10 @@
if (!skb)
return;
- if (lmp_no_flush_capable(conn->hcon->hdev))
+ /* Use NO_FLUSH if supported or we have an LE link (which does
+ * not support auto-flushing packets) */
+ if (lmp_no_flush_capable(conn->hcon->hdev) ||
+ conn->hcon->type == LE_LINK)
flags = ACL_START_NO_FLUSH;
else
flags = ACL_START;
@@ -870,8 +876,13 @@
return;
}
- if (!test_bit(FLAG_FLUSHABLE, &chan->flags) &&
- lmp_no_flush_capable(hcon->hdev))
+ /* Use NO_FLUSH for LE links (where this is the only option) or
+ * if the BR/EDR link supports it and flushing has not been
+ * explicitly requested (through FLAG_FLUSHABLE).
+ */
+ if (hcon->type == LE_LINK ||
+ (!test_bit(FLAG_FLUSHABLE, &chan->flags) &&
+ lmp_no_flush_capable(hcon->hdev)))
flags = ACL_START_NO_FLUSH;
else
flags = ACL_START;
@@ -1108,10 +1119,10 @@
struct hci_dev *hdev;
bool amp_available = false;
- if (!conn->hs_enabled)
+ if (!(conn->local_fixed_chan & L2CAP_FC_A2MP))
return false;
- if (!(conn->fixed_chan_mask & L2CAP_FC_A2MP))
+ if (!(conn->remote_fixed_chan & L2CAP_FC_A2MP))
return false;
read_lock(&hci_dev_list_lock);
@@ -2092,8 +2103,12 @@
struct sk_buff **frag;
int sent = 0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,19,0)
+ if (copy_from_iter(skb_put(skb, count), count, &msg->msg_iter) != count)
+#else
if (chan->ops->memcpy_fromiovec(chan, skb_put(skb, count),
msg->msg_iov, count))
+#endif
return -EFAULT;
sent += count;
@@ -2113,8 +2128,13 @@
*frag = tmp;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,19,0)
+ if (copy_from_iter(skb_put(*frag, count), count,
+ &msg->msg_iter) != count)
+#else
if (chan->ops->memcpy_fromiovec(chan, skb_put(*frag, count),
msg->msg_iov, count))
+#endif
return -EFAULT;
sent += count;
@@ -3084,12 +3104,14 @@
static inline bool __l2cap_ews_supported(struct l2cap_conn *conn)
{
- return conn->hs_enabled && conn->feat_mask & L2CAP_FEAT_EXT_WINDOW;
+ return ((conn->local_fixed_chan & L2CAP_FC_A2MP) &&
+ (conn->feat_mask & L2CAP_FEAT_EXT_WINDOW));
}
static inline bool __l2cap_efs_supported(struct l2cap_conn *conn)
{
- return conn->hs_enabled && conn->feat_mask & L2CAP_FEAT_EXT_FLOW;
+ return ((conn->local_fixed_chan & L2CAP_FC_A2MP) &&
+ (conn->feat_mask & L2CAP_FEAT_EXT_FLOW));
}
static void __l2cap_set_ertm_timeouts(struct l2cap_chan *chan,
@@ -3318,7 +3340,7 @@
break;
case L2CAP_CONF_EWS:
- if (!chan->conn->hs_enabled)
+ if (!(chan->conn->local_fixed_chan & L2CAP_FC_A2MP))
return -ECONNREFUSED;
set_bit(FLAG_EXT_CTRL, &chan->flags);
@@ -3873,9 +3895,7 @@
hci_dev_lock(hdev);
if (test_bit(HCI_MGMT, &hdev->dev_flags) &&
!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &hcon->flags))
- mgmt_device_connected(hdev, &hcon->dst, hcon->type,
- hcon->dst_type, 0, NULL, 0,
- hcon->dev_class);
+ mgmt_device_connected(hdev, hcon, 0, NULL, 0);
hci_dev_unlock(hdev);
l2cap_connect(conn, cmd, data, L2CAP_CONN_RSP, 0);
@@ -4084,7 +4104,7 @@
chan->num_conf_req++;
}
- /* Got Conf Rsp PENDING from remote side and asume we sent
+ /* Got Conf Rsp PENDING from remote side and assume we sent
Conf Rsp PENDING in the code above */
if (test_bit(CONF_REM_CONF_PEND, &chan->conf_state) &&
test_bit(CONF_LOC_CONF_PEND, &chan->conf_state)) {
@@ -4324,7 +4344,7 @@
if (!disable_ertm)
feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING
| L2CAP_FEAT_FCS;
- if (conn->hs_enabled)
+ if (conn->local_fixed_chan & L2CAP_FC_A2MP)
feat_mask |= L2CAP_FEAT_EXT_FLOW
| L2CAP_FEAT_EXT_WINDOW;
@@ -4335,14 +4355,10 @@
u8 buf[12];
struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf;
- if (conn->hs_enabled)
- l2cap_fixed_chan[0] |= L2CAP_FC_A2MP;
- else
- l2cap_fixed_chan[0] &= ~L2CAP_FC_A2MP;
-
rsp->type = cpu_to_le16(L2CAP_IT_FIXED_CHAN);
rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS);
- memcpy(rsp->data, l2cap_fixed_chan, sizeof(l2cap_fixed_chan));
+ rsp->data[0] = conn->local_fixed_chan;
+ memset(rsp->data + 1, 0, 7);
l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(buf),
buf);
} else {
@@ -4408,7 +4424,7 @@
break;
case L2CAP_IT_FIXED_CHAN:
- conn->fixed_chan_mask = rsp->data[0];
+ conn->remote_fixed_chan = rsp->data[0];
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
conn->info_ident = 0;
@@ -4432,7 +4448,7 @@
if (cmd_len != sizeof(*req))
return -EPROTO;
- if (!conn->hs_enabled)
+ if (!(conn->local_fixed_chan & L2CAP_FC_A2MP))
return -EINVAL;
psm = le16_to_cpu(req->psm);
@@ -4862,7 +4878,7 @@
BT_DBG("icid 0x%4.4x, dest_amp_id %d", icid, req->dest_amp_id);
- if (!conn->hs_enabled)
+ if (!(conn->local_fixed_chan & L2CAP_FC_A2MP))
return -EINVAL;
chan = l2cap_get_chan_by_dcid(conn, icid);
@@ -5217,9 +5233,10 @@
u8 *data)
{
struct l2cap_le_conn_rsp *rsp = (struct l2cap_le_conn_rsp *) data;
+ struct hci_conn *hcon = conn->hcon;
u16 dcid, mtu, mps, credits, result;
struct l2cap_chan *chan;
- int err;
+ int err, sec_level;
if (cmd_len < sizeof(*rsp))
return -EPROTO;
@@ -5258,6 +5275,26 @@
l2cap_chan_ready(chan);
break;
+ case L2CAP_CR_AUTHENTICATION:
+ case L2CAP_CR_ENCRYPTION:
+ /* If we already have MITM protection we can't do
+ * anything.
+ */
+ if (hcon->sec_level > BT_SECURITY_MEDIUM) {
+ l2cap_chan_del(chan, ECONNREFUSED);
+ break;
+ }
+
+ sec_level = hcon->sec_level + 1;
+ if (chan->sec_level < sec_level)
+ chan->sec_level = sec_level;
+
+ /* We'll need to send a new Connect Request */
+ clear_bit(FLAG_LE_CONN_REQ_SENT, &chan->flags);
+
+ smp_conn_security(hcon, chan->sec_level);
+ break;
+
default:
l2cap_chan_del(chan, ECONNREFUSED);
break;
@@ -5390,7 +5427,8 @@
mutex_lock(&conn->chan_lock);
l2cap_chan_lock(pchan);
- if (!smp_sufficient_security(conn->hcon, pchan->sec_level)) {
+ if (!smp_sufficient_security(conn->hcon, pchan->sec_level,
+ SMP_ALLOW_STK)) {
result = L2CAP_CR_AUTHENTICATION;
chan = NULL;
goto response_unlock;
@@ -5494,6 +5532,7 @@
if (credits > max_credits) {
BT_ERR("LE credits overflow");
l2cap_send_disconn_req(chan, ECONNRESET);
+ l2cap_chan_unlock(chan);
/* Return 0 so that we don't trigger an unnecessary
* command reject packet.
@@ -6931,9 +6970,16 @@
conn->feat_mask = 0;
- if (hcon->type == ACL_LINK)
- conn->hs_enabled = test_bit(HCI_HS_ENABLED,
- &hcon->hdev->dev_flags);
+ conn->local_fixed_chan = L2CAP_FC_SIG_BREDR | L2CAP_FC_CONNLESS;
+
+ if (hcon->type == ACL_LINK &&
+ test_bit(HCI_HS_ENABLED, &hcon->hdev->dev_flags))
+ conn->local_fixed_chan |= L2CAP_FC_A2MP;
+
+ if (test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags) &&
+ (bredr_sc_enabled(hcon->hdev) ||
+ test_bit(HCI_FORCE_LESC, &hcon->hdev->dbg_flags)))
+ conn->local_fixed_chan |= L2CAP_FC_SMP_BREDR;
mutex_init(&conn->ident_lock);
mutex_init(&conn->chan_lock);
@@ -7330,7 +7376,8 @@
l2cap_start_connection(chan);
else
__set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
- } else if (chan->state == BT_CONNECT2) {
+ } else if (chan->state == BT_CONNECT2 &&
+ chan->mode != L2CAP_MODE_LE_FLOWCTL) {
struct l2cap_conn_rsp rsp;
__u16 res, stat;
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index c53387f..b2ca901 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -285,6 +285,12 @@
sk->sk_max_ack_backlog = backlog;
sk->sk_ack_backlog = 0;
+ /* Listening channels need to use nested locking in order not to
+ * cause lockdep warnings when the created child channels end up
+ * being locked in the same thread as the parent channel.
+ */
+ atomic_set(&chan->nesting, L2CAP_NESTING_PARENT);
+
chan->state = BT_LISTEN;
sk->sk_state = BT_LISTEN;
@@ -301,7 +307,7 @@
long timeo;
int err = 0;
- lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
+ lock_sock_nested(sk, L2CAP_NESTING_PARENT);
timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
@@ -333,7 +339,7 @@
release_sock(sk);
timeo = schedule_timeout(timeo);
- lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
+ lock_sock_nested(sk, L2CAP_NESTING_PARENT);
}
__set_current_state(TASK_RUNNING);
remove_wait_queue(sk_sleep(sk), &wait);
@@ -1096,6 +1102,8 @@
chan = l2cap_pi(sk)->chan;
conn = chan->conn;
+ BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
+
if (conn)
mutex_lock(&conn->chan_lock);
@@ -1153,12 +1161,16 @@
{
struct sock *sk;
- BT_DBG("parent %p", parent);
+ BT_DBG("parent %p state %s", parent,
+ state_to_string(parent->sk_state));
/* Close not yet accepted channels */
while ((sk = bt_accept_dequeue(parent, NULL))) {
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+ BT_DBG("child chan %p state %s", chan,
+ state_to_string(chan->state));
+
l2cap_chan_lock(chan);
__clear_chan_timer(chan);
l2cap_chan_close(chan, ECONNRESET);
@@ -1246,7 +1258,16 @@
struct sock *sk = chan->data;
struct sock *parent;
- lock_sock(sk);
+ BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
+
+ /* This callback can be called both for server (BT_LISTEN)
+ * sockets as well as "normal" ones. To avoid lockdep warnings
+ * with child socket locking (through l2cap_sock_cleanup_listen)
+ * we need separation into separate nesting levels. The simplest
+ * way to accomplish this is to inherit the nesting level used
+ * for the channel.
+ */
+ lock_sock_nested(sk, atomic_read(&chan->nesting));
parent = bt_sk(sk)->parent;
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index efb71b0..693ce8b 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -35,7 +35,7 @@
#include "smp.h"
#define MGMT_VERSION 1
-#define MGMT_REVISION 7
+#define MGMT_REVISION 8
static const u16 mgmt_commands[] = {
MGMT_OP_READ_INDEX_LIST,
@@ -93,6 +93,7 @@
MGMT_OP_READ_CONFIG_INFO,
MGMT_OP_SET_EXTERNAL_CONFIG,
MGMT_OP_SET_PUBLIC_ADDRESS,
+ MGMT_OP_START_SERVICE_DISCOVERY,
};
static const u16 mgmt_events[] = {
@@ -134,8 +135,10 @@
u16 opcode;
int index;
void *param;
+ size_t param_len;
struct sock *sk;
void *user_data;
+ void (*cmd_complete)(struct pending_cmd *cmd, u8 status);
};
/* HCI to MGMT error code conversion table */
@@ -574,6 +577,7 @@
if (lmp_le_capable(hdev)) {
settings |= MGMT_SETTING_LE;
settings |= MGMT_SETTING_ADVERTISING;
+ settings |= MGMT_SETTING_SECURE_CONN;
settings |= MGMT_SETTING_PRIVACY;
}
@@ -1202,14 +1206,13 @@
cmd->opcode = opcode;
cmd->index = hdev->id;
- cmd->param = kmalloc(len, GFP_KERNEL);
+ cmd->param = kmemdup(data, len, GFP_KERNEL);
if (!cmd->param) {
kfree(cmd);
return NULL;
}
- if (data)
- memcpy(cmd->param, data, len);
+ cmd->param_len = len;
cmd->sk = sk;
sock_hold(sk);
@@ -1469,6 +1472,32 @@
mgmt_pending_remove(cmd);
}
+static void cmd_complete_rsp(struct pending_cmd *cmd, void *data)
+{
+ if (cmd->cmd_complete) {
+ u8 *status = data;
+
+ cmd->cmd_complete(cmd, *status);
+ mgmt_pending_remove(cmd);
+
+ return;
+ }
+
+ cmd_status_rsp(cmd, data);
+}
+
+static void generic_cmd_complete(struct pending_cmd *cmd, u8 status)
+{
+ cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, cmd->param,
+ cmd->param_len);
+}
+
+static void addr_cmd_complete(struct pending_cmd *cmd, u8 status)
+{
+ cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, cmd->param,
+ sizeof(struct mgmt_addr_info));
+}
+
static u8 mgmt_bredr_support(struct hci_dev *hdev)
{
if (!lmp_bredr_capable(hdev))
@@ -2170,12 +2199,14 @@
{
struct cmd_lookup match = { NULL, hdev };
+ hci_dev_lock(hdev);
+
if (status) {
u8 mgmt_err = mgmt_status(status);
mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, cmd_status_rsp,
&mgmt_err);
- return;
+ goto unlock;
}
mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, settings_rsp, &match);
@@ -2193,17 +2224,16 @@
if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
struct hci_request req;
- hci_dev_lock(hdev);
-
hci_req_init(&req, hdev);
update_adv_data(&req);
update_scan_rsp_data(&req);
hci_req_run(&req, NULL);
hci_update_background_scan(hdev);
-
- hci_dev_unlock(hdev);
}
+
+unlock:
+ hci_dev_unlock(hdev);
}
static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
@@ -2725,10 +2755,40 @@
}
if (cp->addr.type == BDADDR_BREDR) {
+ /* If disconnection is requested, then look up the
+ * connection. If the remote device is connected, it
+ * will be later used to terminate the link.
+ *
+ * Setting it to NULL explicitly will cause no
+ * termination of the link.
+ */
+ if (cp->disconnect)
+ conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
+ &cp->addr.bdaddr);
+ else
+ conn = NULL;
+
err = hci_remove_link_key(hdev, &cp->addr.bdaddr);
} else {
u8 addr_type;
+ conn = hci_conn_hash_lookup_ba(hdev, LE_LINK,
+ &cp->addr.bdaddr);
+ if (conn) {
+ /* Defer clearing up the connection parameters
+ * until closing to give a chance of keeping
+ * them if a repairing happens.
+ */
+ set_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags);
+
+ /* If disconnection is not requested, then
+ * clear the connection variable so that the
+ * link is not terminated.
+ */
+ if (!cp->disconnect)
+ conn = NULL;
+ }
+
if (cp->addr.type == BDADDR_LE_PUBLIC)
addr_type = ADDR_LE_DEV_PUBLIC;
else
@@ -2736,8 +2796,6 @@
hci_remove_irk(hdev, &cp->addr.bdaddr, addr_type);
- hci_conn_params_del(hdev, &cp->addr.bdaddr, addr_type);
-
err = hci_remove_ltk(hdev, &cp->addr.bdaddr, addr_type);
}
@@ -2747,17 +2805,9 @@
goto unlock;
}
- if (cp->disconnect) {
- if (cp->addr.type == BDADDR_BREDR)
- conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
- &cp->addr.bdaddr);
- else
- conn = hci_conn_hash_lookup_ba(hdev, LE_LINK,
- &cp->addr.bdaddr);
- } else {
- conn = NULL;
- }
-
+ /* If the connection variable is set, then termination of the
+ * link is requested.
+ */
if (!conn) {
err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, 0,
&rp, sizeof(rp));
@@ -2772,6 +2822,8 @@
goto unlock;
}
+ cmd->cmd_complete = addr_cmd_complete;
+
dc.handle = cpu_to_le16(conn->handle);
dc.reason = 0x13; /* Remote User Terminated Connection */
err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc);
@@ -2835,6 +2887,8 @@
goto failed;
}
+ cmd->cmd_complete = generic_cmd_complete;
+
err = hci_disconnect(conn, HCI_ERROR_REMOTE_USER_TERM);
if (err < 0)
mgmt_pending_remove(cmd);
@@ -2987,6 +3041,8 @@
goto failed;
}
+ cmd->cmd_complete = addr_cmd_complete;
+
bacpy(&reply.bdaddr, &cp->addr.bdaddr);
reply.pin_len = cp->pin_len;
memcpy(reply.pin_code, cp->pin_code, sizeof(reply.pin_code));
@@ -3059,9 +3115,13 @@
conn->disconn_cfm_cb = NULL;
hci_conn_drop(conn);
- hci_conn_put(conn);
- mgmt_pending_remove(cmd);
+ /* The device is paired so there is no need to remove
+ * its connection parameters anymore.
+ */
+ clear_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags);
+
+ hci_conn_put(conn);
}
void mgmt_smp_complete(struct hci_conn *conn, bool complete)
@@ -3070,8 +3130,10 @@
struct pending_cmd *cmd;
cmd = find_pairing(conn);
- if (cmd)
- pairing_complete(cmd, status);
+ if (cmd) {
+ cmd->cmd_complete(cmd, status);
+ mgmt_pending_remove(cmd);
+ }
}
static void pairing_complete_cb(struct hci_conn *conn, u8 status)
@@ -3081,10 +3143,13 @@
BT_DBG("status %u", status);
cmd = find_pairing(conn);
- if (!cmd)
+ if (!cmd) {
BT_DBG("Unable to find a pending command");
- else
- pairing_complete(cmd, mgmt_status(status));
+ return;
+ }
+
+ cmd->cmd_complete(cmd, mgmt_status(status));
+ mgmt_pending_remove(cmd);
}
static void le_pairing_complete_cb(struct hci_conn *conn, u8 status)
@@ -3097,10 +3162,13 @@
return;
cmd = find_pairing(conn);
- if (!cmd)
+ if (!cmd) {
BT_DBG("Unable to find a pending command");
- else
- pairing_complete(cmd, mgmt_status(status));
+ return;
+ }
+
+ cmd->cmd_complete(cmd, mgmt_status(status));
+ mgmt_pending_remove(cmd);
}
static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
@@ -3197,6 +3265,8 @@
goto unlock;
}
+ cmd->cmd_complete = pairing_complete;
+
/* For LE, just connecting isn't a proof that the pairing finished */
if (cp->addr.type == BDADDR_BREDR) {
conn->connect_cfm_cb = pairing_complete_cb;
@@ -3212,8 +3282,10 @@
cmd->user_data = hci_conn_get(conn);
if ((conn->state == BT_CONNECTED || conn->state == BT_CONFIG) &&
- hci_conn_security(conn, sec_level, auth_type, true))
- pairing_complete(cmd, 0);
+ hci_conn_security(conn, sec_level, auth_type, true)) {
+ cmd->cmd_complete(cmd, 0);
+ mgmt_pending_remove(cmd);
+ }
err = 0;
@@ -3255,7 +3327,8 @@
goto unlock;
}
- pairing_complete(cmd, MGMT_STATUS_CANCELLED);
+ cmd->cmd_complete(cmd, MGMT_STATUS_CANCELLED);
+ mgmt_pending_remove(cmd);
err = cmd_complete(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, 0,
addr, sizeof(*addr));
@@ -3313,6 +3386,8 @@
goto done;
}
+ cmd->cmd_complete = addr_cmd_complete;
+
/* Continue with pairing via HCI */
if (hci_op == HCI_OP_USER_PASSKEY_REPLY) {
struct hci_cp_user_passkey_reply cp;
@@ -3537,7 +3612,7 @@
goto unlock;
}
- if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags))
+ if (bredr_sc_enabled(hdev))
err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_EXT_DATA,
0, NULL);
else
@@ -3564,8 +3639,17 @@
struct mgmt_cp_add_remote_oob_data *cp = data;
u8 status;
+ if (cp->addr.type != BDADDR_BREDR) {
+ err = cmd_complete(sk, hdev->id,
+ MGMT_OP_ADD_REMOTE_OOB_DATA,
+ MGMT_STATUS_INVALID_PARAMS,
+ &cp->addr, sizeof(cp->addr));
+ goto unlock;
+ }
+
err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr,
- cp->hash, cp->randomizer);
+ cp->addr.type, cp->hash,
+ cp->rand, NULL, NULL);
if (err < 0)
status = MGMT_STATUS_FAILED;
else
@@ -3575,13 +3659,28 @@
status, &cp->addr, sizeof(cp->addr));
} else if (len == MGMT_ADD_REMOTE_OOB_EXT_DATA_SIZE) {
struct mgmt_cp_add_remote_oob_ext_data *cp = data;
+ u8 *rand192, *hash192;
u8 status;
- err = hci_add_remote_oob_ext_data(hdev, &cp->addr.bdaddr,
- cp->hash192,
- cp->randomizer192,
- cp->hash256,
- cp->randomizer256);
+ if (cp->addr.type != BDADDR_BREDR) {
+ err = cmd_complete(sk, hdev->id,
+ MGMT_OP_ADD_REMOTE_OOB_DATA,
+ MGMT_STATUS_INVALID_PARAMS,
+ &cp->addr, sizeof(cp->addr));
+ goto unlock;
+ }
+
+ if (bdaddr_type_is_le(cp->addr.type)) {
+ rand192 = NULL;
+ hash192 = NULL;
+ } else {
+ rand192 = cp->rand192;
+ hash192 = cp->hash192;
+ }
+
+ err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr,
+ cp->addr.type, hash192, rand192,
+ cp->hash256, cp->rand256);
if (err < 0)
status = MGMT_STATUS_FAILED;
else
@@ -3595,6 +3694,7 @@
MGMT_STATUS_INVALID_PARAMS);
}
+unlock:
hci_dev_unlock(hdev);
return err;
}
@@ -3608,14 +3708,26 @@
BT_DBG("%s", hdev->name);
+ if (cp->addr.type != BDADDR_BREDR)
+ return cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
+ MGMT_STATUS_INVALID_PARAMS,
+ &cp->addr, sizeof(cp->addr));
+
hci_dev_lock(hdev);
- err = hci_remove_remote_oob_data(hdev, &cp->addr.bdaddr);
+ if (!bacmp(&cp->addr.bdaddr, BDADDR_ANY)) {
+ hci_remote_oob_data_clear(hdev);
+ status = MGMT_STATUS_SUCCESS;
+ goto done;
+ }
+
+ err = hci_remove_remote_oob_data(hdev, &cp->addr.bdaddr, cp->addr.type);
if (err < 0)
status = MGMT_STATUS_INVALID_PARAMS;
else
status = MGMT_STATUS_SUCCESS;
+done:
err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
status, &cp->addr, sizeof(cp->addr));
@@ -3623,127 +3735,26 @@
return err;
}
-static int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status)
+static bool trigger_discovery(struct hci_request *req, u8 *status)
{
- struct pending_cmd *cmd;
- u8 type;
- int err;
-
- hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
-
- cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
- if (!cmd)
- return -ENOENT;
-
- type = hdev->discovery.type;
-
- err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status),
- &type, sizeof(type));
- mgmt_pending_remove(cmd);
-
- return err;
-}
-
-static void start_discovery_complete(struct hci_dev *hdev, u8 status)
-{
- unsigned long timeout = 0;
-
- BT_DBG("status %d", status);
-
- if (status) {
- hci_dev_lock(hdev);
- mgmt_start_discovery_failed(hdev, status);
- hci_dev_unlock(hdev);
- return;
- }
-
- hci_dev_lock(hdev);
- hci_discovery_set_state(hdev, DISCOVERY_FINDING);
- hci_dev_unlock(hdev);
-
- switch (hdev->discovery.type) {
- case DISCOV_TYPE_LE:
- timeout = msecs_to_jiffies(DISCOV_LE_TIMEOUT);
- break;
-
- case DISCOV_TYPE_INTERLEAVED:
- timeout = msecs_to_jiffies(hdev->discov_interleaved_timeout);
- break;
-
- case DISCOV_TYPE_BREDR:
- break;
-
- default:
- BT_ERR("Invalid discovery type %d", hdev->discovery.type);
- }
-
- if (!timeout)
- return;
-
- queue_delayed_work(hdev->workqueue, &hdev->le_scan_disable, timeout);
-}
-
-static int start_discovery(struct sock *sk, struct hci_dev *hdev,
- void *data, u16 len)
-{
- struct mgmt_cp_start_discovery *cp = data;
- struct pending_cmd *cmd;
+ struct hci_dev *hdev = req->hdev;
struct hci_cp_le_set_scan_param param_cp;
struct hci_cp_le_set_scan_enable enable_cp;
struct hci_cp_inquiry inq_cp;
- struct hci_request req;
/* General inquiry access code (GIAC) */
u8 lap[3] = { 0x33, 0x8b, 0x9e };
- u8 status, own_addr_type;
+ u8 own_addr_type;
int err;
- BT_DBG("%s", hdev->name);
-
- hci_dev_lock(hdev);
-
- if (!hdev_is_powered(hdev)) {
- err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_NOT_POWERED);
- goto failed;
- }
-
- if (test_bit(HCI_PERIODIC_INQ, &hdev->dev_flags)) {
- err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_BUSY);
- goto failed;
- }
-
- if (hdev->discovery.state != DISCOVERY_STOPPED) {
- err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_BUSY);
- goto failed;
- }
-
- cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, NULL, 0);
- if (!cmd) {
- err = -ENOMEM;
- goto failed;
- }
-
- hdev->discovery.type = cp->type;
-
- hci_req_init(&req, hdev);
-
switch (hdev->discovery.type) {
case DISCOV_TYPE_BREDR:
- status = mgmt_bredr_support(hdev);
- if (status) {
- err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- status);
- mgmt_pending_remove(cmd);
- goto failed;
- }
+ *status = mgmt_bredr_support(hdev);
+ if (*status)
+ return false;
if (test_bit(HCI_INQUIRY, &hdev->flags)) {
- err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_BUSY);
- mgmt_pending_remove(cmd);
- goto failed;
+ *status = MGMT_STATUS_BUSY;
+ return false;
}
hci_inquiry_cache_flush(hdev);
@@ -3751,25 +3762,19 @@
memset(&inq_cp, 0, sizeof(inq_cp));
memcpy(&inq_cp.lap, lap, sizeof(inq_cp.lap));
inq_cp.length = DISCOV_BREDR_INQUIRY_LEN;
- hci_req_add(&req, HCI_OP_INQUIRY, sizeof(inq_cp), &inq_cp);
+ hci_req_add(req, HCI_OP_INQUIRY, sizeof(inq_cp), &inq_cp);
break;
case DISCOV_TYPE_LE:
case DISCOV_TYPE_INTERLEAVED:
- status = mgmt_le_support(hdev);
- if (status) {
- err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- status);
- mgmt_pending_remove(cmd);
- goto failed;
- }
+ *status = mgmt_le_support(hdev);
+ if (*status)
+ return false;
if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED &&
!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
- err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_NOT_SUPPORTED);
- mgmt_pending_remove(cmd);
- goto failed;
+ *status = MGMT_STATUS_NOT_SUPPORTED;
+ return false;
}
if (test_bit(HCI_LE_ADV, &hdev->dev_flags)) {
@@ -3779,14 +3784,11 @@
*/
if (hci_conn_hash_lookup_state(hdev, LE_LINK,
BT_CONNECT)) {
- err = cmd_status(sk, hdev->id,
- MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_REJECTED);
- mgmt_pending_remove(cmd);
- goto failed;
+ *status = MGMT_STATUS_REJECTED;
+ return false;
}
- disable_advertising(&req);
+ disable_advertising(req);
}
/* If controller is scanning, it means the background scanning
@@ -3794,84 +3796,287 @@
* set the discovery scanning parameters.
*/
if (test_bit(HCI_LE_SCAN, &hdev->dev_flags))
- hci_req_add_le_scan_disable(&req);
+ hci_req_add_le_scan_disable(req);
memset(¶m_cp, 0, sizeof(param_cp));
/* All active scans will be done with either a resolvable
* private address (when privacy feature has been enabled)
- * or unresolvable private address.
+ * or non-resolvable private address.
*/
- err = hci_update_random_address(&req, true, &own_addr_type);
+ err = hci_update_random_address(req, true, &own_addr_type);
if (err < 0) {
- err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_FAILED);
- mgmt_pending_remove(cmd);
- goto failed;
+ *status = MGMT_STATUS_FAILED;
+ return false;
}
param_cp.type = LE_SCAN_ACTIVE;
param_cp.interval = cpu_to_le16(DISCOV_LE_SCAN_INT);
param_cp.window = cpu_to_le16(DISCOV_LE_SCAN_WIN);
param_cp.own_address_type = own_addr_type;
- hci_req_add(&req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp),
+ hci_req_add(req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp),
¶m_cp);
memset(&enable_cp, 0, sizeof(enable_cp));
enable_cp.enable = LE_SCAN_ENABLE;
enable_cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
- hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp),
+ hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp),
&enable_cp);
break;
default:
- err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_INVALID_PARAMS);
+ *status = MGMT_STATUS_INVALID_PARAMS;
+ return false;
+ }
+
+ return true;
+}
+
+static void start_discovery_complete(struct hci_dev *hdev, u8 status)
+{
+ struct pending_cmd *cmd;
+ unsigned long timeout;
+
+ BT_DBG("status %d", status);
+
+ hci_dev_lock(hdev);
+
+ cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
+ if (!cmd)
+ cmd = mgmt_pending_find(MGMT_OP_START_SERVICE_DISCOVERY, hdev);
+
+ if (cmd) {
+ cmd->cmd_complete(cmd, mgmt_status(status));
+ mgmt_pending_remove(cmd);
+ }
+
+ if (status) {
+ hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+ goto unlock;
+ }
+
+ hci_discovery_set_state(hdev, DISCOVERY_FINDING);
+
+ switch (hdev->discovery.type) {
+ case DISCOV_TYPE_LE:
+ timeout = msecs_to_jiffies(DISCOV_LE_TIMEOUT);
+ break;
+ case DISCOV_TYPE_INTERLEAVED:
+ timeout = msecs_to_jiffies(hdev->discov_interleaved_timeout);
+ break;
+ case DISCOV_TYPE_BREDR:
+ timeout = 0;
+ break;
+ default:
+ BT_ERR("Invalid discovery type %d", hdev->discovery.type);
+ timeout = 0;
+ break;
+ }
+
+ if (timeout)
+ queue_delayed_work(hdev->workqueue,
+ &hdev->le_scan_disable, timeout);
+
+unlock:
+ hci_dev_unlock(hdev);
+}
+
+static int start_discovery(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
+{
+ struct mgmt_cp_start_discovery *cp = data;
+ struct pending_cmd *cmd;
+ struct hci_request req;
+ u8 status;
+ int err;
+
+ BT_DBG("%s", hdev->name);
+
+ hci_dev_lock(hdev);
+
+ if (!hdev_is_powered(hdev)) {
+ err = cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+ MGMT_STATUS_NOT_POWERED,
+ &cp->type, sizeof(cp->type));
+ goto failed;
+ }
+
+ if (hdev->discovery.state != DISCOVERY_STOPPED ||
+ test_bit(HCI_PERIODIC_INQ, &hdev->dev_flags)) {
+ err = cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+ MGMT_STATUS_BUSY, &cp->type,
+ sizeof(cp->type));
+ goto failed;
+ }
+
+ cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, data, len);
+ if (!cmd) {
+ err = -ENOMEM;
+ goto failed;
+ }
+
+ cmd->cmd_complete = generic_cmd_complete;
+
+ /* Clear the discovery filter first to free any previously
+ * allocated memory for the UUID list.
+ */
+ hci_discovery_filter_clear(hdev);
+
+ hdev->discovery.type = cp->type;
+ hdev->discovery.report_invalid_rssi = false;
+
+ hci_req_init(&req, hdev);
+
+ if (!trigger_discovery(&req, &status)) {
+ err = cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+ status, &cp->type, sizeof(cp->type));
mgmt_pending_remove(cmd);
goto failed;
}
err = hci_req_run(&req, start_discovery_complete);
- if (err < 0)
+ if (err < 0) {
mgmt_pending_remove(cmd);
- else
- hci_discovery_set_state(hdev, DISCOVERY_STARTING);
+ goto failed;
+ }
+
+ hci_discovery_set_state(hdev, DISCOVERY_STARTING);
failed:
hci_dev_unlock(hdev);
return err;
}
-static int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status)
+static void service_discovery_cmd_complete(struct pending_cmd *cmd, u8 status)
{
+ cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, cmd->param, 1);
+}
+
+static int start_service_discovery(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
+{
+ struct mgmt_cp_start_service_discovery *cp = data;
struct pending_cmd *cmd;
+ struct hci_request req;
+ const u16 max_uuid_count = ((U16_MAX - sizeof(*cp)) / 16);
+ u16 uuid_count, expected_len;
+ u8 status;
int err;
- cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
- if (!cmd)
- return -ENOENT;
+ BT_DBG("%s", hdev->name);
- err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status),
- &hdev->discovery.type, sizeof(hdev->discovery.type));
- mgmt_pending_remove(cmd);
+ hci_dev_lock(hdev);
+ if (!hdev_is_powered(hdev)) {
+ err = cmd_complete(sk, hdev->id,
+ MGMT_OP_START_SERVICE_DISCOVERY,
+ MGMT_STATUS_NOT_POWERED,
+ &cp->type, sizeof(cp->type));
+ goto failed;
+ }
+
+ if (hdev->discovery.state != DISCOVERY_STOPPED ||
+ test_bit(HCI_PERIODIC_INQ, &hdev->dev_flags)) {
+ err = cmd_complete(sk, hdev->id,
+ MGMT_OP_START_SERVICE_DISCOVERY,
+ MGMT_STATUS_BUSY, &cp->type,
+ sizeof(cp->type));
+ goto failed;
+ }
+
+ uuid_count = __le16_to_cpu(cp->uuid_count);
+ if (uuid_count > max_uuid_count) {
+ BT_ERR("service_discovery: too big uuid_count value %u",
+ uuid_count);
+ err = cmd_complete(sk, hdev->id,
+ MGMT_OP_START_SERVICE_DISCOVERY,
+ MGMT_STATUS_INVALID_PARAMS, &cp->type,
+ sizeof(cp->type));
+ goto failed;
+ }
+
+ expected_len = sizeof(*cp) + uuid_count * 16;
+ if (expected_len != len) {
+ BT_ERR("service_discovery: expected %u bytes, got %u bytes",
+ expected_len, len);
+ err = cmd_complete(sk, hdev->id,
+ MGMT_OP_START_SERVICE_DISCOVERY,
+ MGMT_STATUS_INVALID_PARAMS, &cp->type,
+ sizeof(cp->type));
+ goto failed;
+ }
+
+ cmd = mgmt_pending_add(sk, MGMT_OP_START_SERVICE_DISCOVERY,
+ hdev, data, len);
+ if (!cmd) {
+ err = -ENOMEM;
+ goto failed;
+ }
+
+ cmd->cmd_complete = service_discovery_cmd_complete;
+
+ /* Clear the discovery filter first to free any previously
+ * allocated memory for the UUID list.
+ */
+ hci_discovery_filter_clear(hdev);
+
+ hdev->discovery.type = cp->type;
+ hdev->discovery.rssi = cp->rssi;
+ hdev->discovery.uuid_count = uuid_count;
+
+ if (uuid_count > 0) {
+ hdev->discovery.uuids = kmemdup(cp->uuids, uuid_count * 16,
+ GFP_KERNEL);
+ if (!hdev->discovery.uuids) {
+ err = cmd_complete(sk, hdev->id,
+ MGMT_OP_START_SERVICE_DISCOVERY,
+ MGMT_STATUS_FAILED,
+ &cp->type, sizeof(cp->type));
+ mgmt_pending_remove(cmd);
+ goto failed;
+ }
+ }
+
+ hci_req_init(&req, hdev);
+
+ if (!trigger_discovery(&req, &status)) {
+ err = cmd_complete(sk, hdev->id,
+ MGMT_OP_START_SERVICE_DISCOVERY,
+ status, &cp->type, sizeof(cp->type));
+ mgmt_pending_remove(cmd);
+ goto failed;
+ }
+
+ err = hci_req_run(&req, start_discovery_complete);
+ if (err < 0) {
+ mgmt_pending_remove(cmd);
+ goto failed;
+ }
+
+ hci_discovery_set_state(hdev, DISCOVERY_STARTING);
+
+failed:
+ hci_dev_unlock(hdev);
return err;
}
static void stop_discovery_complete(struct hci_dev *hdev, u8 status)
{
+ struct pending_cmd *cmd;
+
BT_DBG("status %d", status);
hci_dev_lock(hdev);
- if (status) {
- mgmt_stop_discovery_failed(hdev, status);
- goto unlock;
+ cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
+ if (cmd) {
+ cmd->cmd_complete(cmd, mgmt_status(status));
+ mgmt_pending_remove(cmd);
}
- hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+ if (!status)
+ hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
-unlock:
hci_dev_unlock(hdev);
}
@@ -3901,12 +4106,14 @@
goto unlock;
}
- cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, hdev, NULL, 0);
+ cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, hdev, data, len);
if (!cmd) {
err = -ENOMEM;
goto unlock;
}
+ cmd->cmd_complete = generic_cmd_complete;
+
hci_req_init(&req, hdev);
hci_stop_discovery(&req);
@@ -4083,12 +4290,14 @@
{
struct cmd_lookup match = { NULL, hdev };
+ hci_dev_lock(hdev);
+
if (status) {
u8 mgmt_err = mgmt_status(status);
mgmt_pending_foreach(MGMT_OP_SET_ADVERTISING, hdev,
cmd_status_rsp, &mgmt_err);
- return;
+ goto unlock;
}
if (test_bit(HCI_LE_ADV, &hdev->dev_flags))
@@ -4103,6 +4312,9 @@
if (match.sk)
sock_put(match.sk);
+
+unlock:
+ hci_dev_unlock(hdev);
}
static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data,
@@ -4506,18 +4718,13 @@
{
struct mgmt_mode *cp = data;
struct pending_cmd *cmd;
- u8 val, status;
+ u8 val;
int err;
BT_DBG("request for %s", hdev->name);
- status = mgmt_bredr_support(hdev);
- if (status)
- return cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN,
- status);
-
- if (!lmp_sc_capable(hdev) &&
- !test_bit(HCI_FORCE_SC, &hdev->dbg_flags))
+ if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags) &&
+ !lmp_sc_capable(hdev) && !test_bit(HCI_FORCE_SC, &hdev->dbg_flags))
return cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN,
MGMT_STATUS_NOT_SUPPORTED);
@@ -4527,7 +4734,10 @@
hci_dev_lock(hdev);
- if (!hdev_is_powered(hdev)) {
+ if (!hdev_is_powered(hdev) ||
+ (!lmp_sc_capable(hdev) &&
+ !test_bit(HCI_FORCE_SC, &hdev->dbg_flags)) ||
+ !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
bool changed;
if (cp->val) {
@@ -4844,18 +5054,26 @@
else
addr_type = ADDR_LE_DEV_RANDOM;
- if (key->master)
- type = SMP_LTK;
- else
- type = SMP_LTK_SLAVE;
-
switch (key->type) {
case MGMT_LTK_UNAUTHENTICATED:
authenticated = 0x00;
+ type = key->master ? SMP_LTK : SMP_LTK_SLAVE;
break;
case MGMT_LTK_AUTHENTICATED:
authenticated = 0x01;
+ type = key->master ? SMP_LTK : SMP_LTK_SLAVE;
break;
+ case MGMT_LTK_P256_UNAUTH:
+ authenticated = 0x00;
+ type = SMP_LTK_P256;
+ break;
+ case MGMT_LTK_P256_AUTH:
+ authenticated = 0x01;
+ type = SMP_LTK_P256;
+ break;
+ case MGMT_LTK_P256_DEBUG:
+ authenticated = 0x00;
+ type = SMP_LTK_P256_DEBUG;
default:
continue;
}
@@ -4873,67 +5091,42 @@
return err;
}
-struct cmd_conn_lookup {
- struct hci_conn *conn;
- bool valid_tx_power;
- u8 mgmt_status;
-};
-
-static void get_conn_info_complete(struct pending_cmd *cmd, void *data)
+static void conn_info_cmd_complete(struct pending_cmd *cmd, u8 status)
{
- struct cmd_conn_lookup *match = data;
- struct mgmt_cp_get_conn_info *cp;
- struct mgmt_rp_get_conn_info rp;
struct hci_conn *conn = cmd->user_data;
+ struct mgmt_rp_get_conn_info rp;
- if (conn != match->conn)
- return;
+ memcpy(&rp.addr, cmd->param, sizeof(rp.addr));
- cp = (struct mgmt_cp_get_conn_info *) cmd->param;
-
- memset(&rp, 0, sizeof(rp));
- bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
- rp.addr.type = cp->addr.type;
-
- if (!match->mgmt_status) {
+ if (status == MGMT_STATUS_SUCCESS) {
rp.rssi = conn->rssi;
-
- if (match->valid_tx_power) {
- rp.tx_power = conn->tx_power;
- rp.max_tx_power = conn->max_tx_power;
- } else {
- rp.tx_power = HCI_TX_POWER_INVALID;
- rp.max_tx_power = HCI_TX_POWER_INVALID;
- }
+ rp.tx_power = conn->tx_power;
+ rp.max_tx_power = conn->max_tx_power;
+ } else {
+ rp.rssi = HCI_RSSI_INVALID;
+ rp.tx_power = HCI_TX_POWER_INVALID;
+ rp.max_tx_power = HCI_TX_POWER_INVALID;
}
- cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO,
- match->mgmt_status, &rp, sizeof(rp));
+ cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO, status,
+ &rp, sizeof(rp));
hci_conn_drop(conn);
hci_conn_put(conn);
-
- mgmt_pending_remove(cmd);
}
-static void conn_info_refresh_complete(struct hci_dev *hdev, u8 status)
+static void conn_info_refresh_complete(struct hci_dev *hdev, u8 hci_status)
{
struct hci_cp_read_rssi *cp;
+ struct pending_cmd *cmd;
struct hci_conn *conn;
- struct cmd_conn_lookup match;
u16 handle;
+ u8 status;
- BT_DBG("status 0x%02x", status);
+ BT_DBG("status 0x%02x", hci_status);
hci_dev_lock(hdev);
- /* TX power data is valid in case request completed successfully,
- * otherwise we assume it's not valid. At the moment we assume that
- * either both or none of current and max values are valid to keep code
- * simple.
- */
- match.valid_tx_power = !status;
-
/* Commands sent in request are either Read RSSI or Read Transmit Power
* Level so we check which one was last sent to retrieve connection
* handle. Both commands have handle as first parameter so it's safe to
@@ -4946,29 +5139,29 @@
cp = hci_sent_cmd_data(hdev, HCI_OP_READ_RSSI);
if (!cp) {
cp = hci_sent_cmd_data(hdev, HCI_OP_READ_TX_POWER);
- status = 0;
+ status = MGMT_STATUS_SUCCESS;
+ } else {
+ status = mgmt_status(hci_status);
}
if (!cp) {
- BT_ERR("invalid sent_cmd in response");
+ BT_ERR("invalid sent_cmd in conn_info response");
goto unlock;
}
handle = __le16_to_cpu(cp->handle);
conn = hci_conn_hash_lookup_handle(hdev, handle);
if (!conn) {
- BT_ERR("unknown handle (%d) in response", handle);
+ BT_ERR("unknown handle (%d) in conn_info response", handle);
goto unlock;
}
- match.conn = conn;
- match.mgmt_status = mgmt_status(status);
+ cmd = mgmt_pending_find_data(MGMT_OP_GET_CONN_INFO, hdev, conn);
+ if (!cmd)
+ goto unlock;
- /* Cache refresh is complete, now reply for mgmt request for given
- * connection only.
- */
- mgmt_pending_foreach(MGMT_OP_GET_CONN_INFO, hdev,
- get_conn_info_complete, &match);
+ cmd->cmd_complete(cmd, status);
+ mgmt_pending_remove(cmd);
unlock:
hci_dev_unlock(hdev);
@@ -5014,6 +5207,12 @@
goto unlock;
}
+ if (mgmt_pending_find_data(MGMT_OP_GET_CONN_INFO, hdev, conn)) {
+ err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
+ MGMT_STATUS_BUSY, &rp, sizeof(rp));
+ goto unlock;
+ }
+
/* To avoid client trying to guess when to poll again for information we
* calculate conn info age as random value between min/max set in hdev.
*/
@@ -5069,6 +5268,7 @@
hci_conn_hold(conn);
cmd->user_data = hci_conn_get(conn);
+ cmd->cmd_complete = conn_info_cmd_complete;
conn->conn_info_timestamp = jiffies;
} else {
@@ -5086,10 +5286,40 @@
return err;
}
+static void clock_info_cmd_complete(struct pending_cmd *cmd, u8 status)
+{
+ struct hci_conn *conn = cmd->user_data;
+ struct mgmt_rp_get_clock_info rp;
+ struct hci_dev *hdev;
+
+ memset(&rp, 0, sizeof(rp));
+ memcpy(&rp.addr, &cmd->param, sizeof(rp.addr));
+
+ if (status)
+ goto complete;
+
+ hdev = hci_dev_get(cmd->index);
+ if (hdev) {
+ rp.local_clock = cpu_to_le32(hdev->clock);
+ hci_dev_put(hdev);
+ }
+
+ if (conn) {
+ rp.piconet_clock = cpu_to_le32(conn->clock);
+ rp.accuracy = cpu_to_le16(conn->clock_accuracy);
+ }
+
+complete:
+ cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, &rp, sizeof(rp));
+
+ if (conn) {
+ hci_conn_drop(conn);
+ hci_conn_put(conn);
+ }
+}
+
static void get_clock_info_complete(struct hci_dev *hdev, u8 status)
{
- struct mgmt_cp_get_clock_info *cp;
- struct mgmt_rp_get_clock_info rp;
struct hci_cp_read_clock *hci_cp;
struct pending_cmd *cmd;
struct hci_conn *conn;
@@ -5113,29 +5343,8 @@
if (!cmd)
goto unlock;
- cp = cmd->param;
-
- memset(&rp, 0, sizeof(rp));
- memcpy(&rp.addr, &cp->addr, sizeof(rp.addr));
-
- if (status)
- goto send_rsp;
-
- rp.local_clock = cpu_to_le32(hdev->clock);
-
- if (conn) {
- rp.piconet_clock = cpu_to_le32(conn->clock);
- rp.accuracy = cpu_to_le16(conn->clock_accuracy);
- }
-
-send_rsp:
- cmd_complete(cmd->sk, cmd->index, cmd->opcode, mgmt_status(status),
- &rp, sizeof(rp));
+ cmd->cmd_complete(cmd, mgmt_status(status));
mgmt_pending_remove(cmd);
- if (conn) {
- hci_conn_drop(conn);
- hci_conn_put(conn);
- }
unlock:
hci_dev_unlock(hdev);
@@ -5191,6 +5400,8 @@
goto unlock;
}
+ cmd->cmd_complete = clock_info_cmd_complete;
+
hci_req_init(&req, hdev);
memset(&hci_cp, 0, sizeof(hci_cp));
@@ -5680,6 +5891,7 @@
{ read_config_info, false, MGMT_READ_CONFIG_INFO_SIZE },
{ set_external_config, false, MGMT_SET_EXTERNAL_CONFIG_SIZE },
{ set_public_address, false, MGMT_SET_PUBLIC_ADDRESS_SIZE },
+ { start_service_discovery,true, MGMT_START_SERVICE_DISCOVERY_SIZE },
};
int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
@@ -5701,7 +5913,7 @@
if (!buf)
return -ENOMEM;
- if (memcpy_fromiovec(buf, msg->msg_iov, msglen)) {
+ if (memcpy_from_msg(buf, msg, msglen)) {
err = -EFAULT;
goto done;
}
@@ -5816,7 +6028,7 @@
if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
return;
- mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
+ mgmt_pending_foreach(0, hdev, cmd_complete_rsp, &status);
if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags))
mgmt_event(MGMT_EV_UNCONF_INDEX_REMOVED, hdev, NULL, 0, NULL);
@@ -5885,6 +6097,11 @@
hci_req_add(&req, HCI_OP_WRITE_SSP_MODE, 1, &ssp);
}
+ if (bredr_sc_enabled(hdev) && !lmp_host_sc_capable(hdev)) {
+ u8 sc = 0x01;
+ hci_req_add(&req, HCI_OP_WRITE_SC_SUPPORT, sizeof(sc), &sc);
+ }
+
if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags) &&
lmp_bredr_capable(hdev)) {
struct hci_cp_write_le_host_supported cp;
@@ -5934,8 +6151,7 @@
int mgmt_powered(struct hci_dev *hdev, u8 powered)
{
struct cmd_lookup match = { NULL, hdev };
- u8 status_not_powered = MGMT_STATUS_NOT_POWERED;
- u8 zero_cod[] = { 0, 0, 0 };
+ u8 status, zero_cod[] = { 0, 0, 0 };
int err;
if (!test_bit(HCI_MGMT, &hdev->dev_flags))
@@ -5951,7 +6167,20 @@
}
mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
- mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status_not_powered);
+
+ /* If the power off is because of hdev unregistration let
+ * use the appropriate INVALID_INDEX status. Otherwise use
+ * NOT_POWERED. We cover both scenarios here since later in
+ * mgmt_index_removed() any hci_conn callbacks will have already
+ * been triggered, potentially causing misleading DISCONNECTED
+ * status responses.
+ */
+ if (test_bit(HCI_UNREGISTER, &hdev->dev_flags))
+ status = MGMT_STATUS_INVALID_INDEX;
+ else
+ status = MGMT_STATUS_NOT_POWERED;
+
+ mgmt_pending_foreach(0, hdev, cmd_complete_rsp, &status);
if (memcmp(hdev->dev_class, zero_cod, sizeof(zero_cod)) != 0)
mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev,
@@ -6035,8 +6264,19 @@
static u8 mgmt_ltk_type(struct smp_ltk *ltk)
{
- if (ltk->authenticated)
- return MGMT_LTK_AUTHENTICATED;
+ switch (ltk->type) {
+ case SMP_LTK:
+ case SMP_LTK_SLAVE:
+ if (ltk->authenticated)
+ return MGMT_LTK_AUTHENTICATED;
+ return MGMT_LTK_UNAUTHENTICATED;
+ case SMP_LTK_P256:
+ if (ltk->authenticated)
+ return MGMT_LTK_P256_AUTH;
+ return MGMT_LTK_P256_UNAUTH;
+ case SMP_LTK_P256_DEBUG:
+ return MGMT_LTK_P256_DEBUG;
+ }
return MGMT_LTK_UNAUTHENTICATED;
}
@@ -6171,26 +6411,36 @@
return eir_len;
}
-void mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
- u8 addr_type, u32 flags, u8 *name, u8 name_len,
- u8 *dev_class)
+void mgmt_device_connected(struct hci_dev *hdev, struct hci_conn *conn,
+ u32 flags, u8 *name, u8 name_len)
{
char buf[512];
struct mgmt_ev_device_connected *ev = (void *) buf;
u16 eir_len = 0;
- bacpy(&ev->addr.bdaddr, bdaddr);
- ev->addr.type = link_to_bdaddr(link_type, addr_type);
+ bacpy(&ev->addr.bdaddr, &conn->dst);
+ ev->addr.type = link_to_bdaddr(conn->type, conn->dst_type);
ev->flags = __cpu_to_le32(flags);
- if (name_len > 0)
- eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE,
- name, name_len);
+ /* We must ensure that the EIR Data fields are ordered and
+ * unique. Keep it simple for now and avoid the problem by not
+ * adding any BR/EDR data to the LE adv.
+ */
+ if (conn->le_adv_data_len > 0) {
+ memcpy(&ev->eir[eir_len],
+ conn->le_adv_data, conn->le_adv_data_len);
+ eir_len = conn->le_adv_data_len;
+ } else {
+ if (name_len > 0)
+ eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE,
+ name, name_len);
- if (dev_class && memcmp(dev_class, "\0\0\0", 3) != 0)
- eir_len = eir_append_data(ev->eir, eir_len,
- EIR_CLASS_OF_DEV, dev_class, 3);
+ if (memcmp(conn->dev_class, "\0\0\0", 3) != 0)
+ eir_len = eir_append_data(ev->eir, eir_len,
+ EIR_CLASS_OF_DEV,
+ conn->dev_class, 3);
+ }
ev->eir_len = cpu_to_le16(eir_len);
@@ -6200,15 +6450,9 @@
static void disconnect_rsp(struct pending_cmd *cmd, void *data)
{
- struct mgmt_cp_disconnect *cp = cmd->param;
struct sock **sk = data;
- struct mgmt_rp_disconnect rp;
- bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
- rp.addr.type = cp->addr.type;
-
- cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, 0, &rp,
- sizeof(rp));
+ cmd->cmd_complete(cmd, 0);
*sk = cmd->sk;
sock_hold(*sk);
@@ -6220,16 +6464,10 @@
{
struct hci_dev *hdev = data;
struct mgmt_cp_unpair_device *cp = cmd->param;
- struct mgmt_rp_unpair_device rp;
-
- memset(&rp, 0, sizeof(rp));
- bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
- rp.addr.type = cp->addr.type;
device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, cmd->sk);
- cmd_complete(cmd->sk, cmd->index, cmd->opcode, 0, &rp, sizeof(rp));
-
+ cmd->cmd_complete(cmd, 0);
mgmt_pending_remove(cmd);
}
@@ -6290,7 +6528,6 @@
{
u8 bdaddr_type = link_to_bdaddr(link_type, addr_type);
struct mgmt_cp_disconnect *cp;
- struct mgmt_rp_disconnect rp;
struct pending_cmd *cmd;
mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
@@ -6308,12 +6545,7 @@
if (cp->addr.type != bdaddr_type)
return;
- bacpy(&rp.addr.bdaddr, bdaddr);
- rp.addr.type = bdaddr_type;
-
- cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT,
- mgmt_status(status), &rp, sizeof(rp));
-
+ cmd->cmd_complete(cmd, mgmt_status(status));
mgmt_pending_remove(cmd);
}
@@ -6352,18 +6584,12 @@
u8 status)
{
struct pending_cmd *cmd;
- struct mgmt_rp_pin_code_reply rp;
cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, hdev);
if (!cmd)
return;
- bacpy(&rp.addr.bdaddr, bdaddr);
- rp.addr.type = BDADDR_BREDR;
-
- cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
- mgmt_status(status), &rp, sizeof(rp));
-
+ cmd->cmd_complete(cmd, mgmt_status(status));
mgmt_pending_remove(cmd);
}
@@ -6371,18 +6597,12 @@
u8 status)
{
struct pending_cmd *cmd;
- struct mgmt_rp_pin_code_reply rp;
cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev);
if (!cmd)
return;
- bacpy(&rp.addr.bdaddr, bdaddr);
- rp.addr.type = BDADDR_BREDR;
-
- cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY,
- mgmt_status(status), &rp, sizeof(rp));
-
+ cmd->cmd_complete(cmd, mgmt_status(status));
mgmt_pending_remove(cmd);
}
@@ -6422,21 +6642,15 @@
u8 opcode)
{
struct pending_cmd *cmd;
- struct mgmt_rp_user_confirm_reply rp;
- int err;
cmd = mgmt_pending_find(opcode, hdev);
if (!cmd)
return -ENOENT;
- bacpy(&rp.addr.bdaddr, bdaddr);
- rp.addr.type = link_to_bdaddr(link_type, addr_type);
- err = cmd_complete(cmd->sk, hdev->id, opcode, mgmt_status(status),
- &rp, sizeof(rp));
-
+ cmd->cmd_complete(cmd, mgmt_status(status));
mgmt_pending_remove(cmd);
- return err;
+ return 0;
}
int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
@@ -6500,8 +6714,10 @@
mgmt_event(MGMT_EV_AUTH_FAILED, conn->hdev, &ev, sizeof(ev),
cmd ? cmd->sk : NULL);
- if (cmd)
- pairing_complete(cmd, status);
+ if (cmd) {
+ cmd->cmd_complete(cmd, status);
+ mgmt_pending_remove(cmd);
+ }
}
void mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status)
@@ -6693,8 +6909,8 @@
}
void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
- u8 *randomizer192, u8 *hash256,
- u8 *randomizer256, u8 status)
+ u8 *rand192, u8 *hash256, u8 *rand256,
+ u8 status)
{
struct pending_cmd *cmd;
@@ -6708,17 +6924,14 @@
cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
mgmt_status(status));
} else {
- if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags) &&
- hash256 && randomizer256) {
+ if (bredr_sc_enabled(hdev) && hash256 && rand256) {
struct mgmt_rp_read_local_oob_ext_data rp;
memcpy(rp.hash192, hash192, sizeof(rp.hash192));
- memcpy(rp.randomizer192, randomizer192,
- sizeof(rp.randomizer192));
+ memcpy(rp.rand192, rand192, sizeof(rp.rand192));
memcpy(rp.hash256, hash256, sizeof(rp.hash256));
- memcpy(rp.randomizer256, randomizer256,
- sizeof(rp.randomizer256));
+ memcpy(rp.rand256, rand256, sizeof(rp.rand256));
cmd_complete(cmd->sk, hdev->id,
MGMT_OP_READ_LOCAL_OOB_DATA, 0,
@@ -6727,8 +6940,7 @@
struct mgmt_rp_read_local_oob_data rp;
memcpy(rp.hash, hash192, sizeof(rp.hash));
- memcpy(rp.randomizer, randomizer192,
- sizeof(rp.randomizer));
+ memcpy(rp.rand, rand192, sizeof(rp.rand));
cmd_complete(cmd->sk, hdev->id,
MGMT_OP_READ_LOCAL_OOB_DATA, 0,
@@ -6739,6 +6951,73 @@
mgmt_pending_remove(cmd);
}
+static inline bool has_uuid(u8 *uuid, u16 uuid_count, u8 (*uuids)[16])
+{
+ int i;
+
+ for (i = 0; i < uuid_count; i++) {
+ if (!memcmp(uuid, uuids[i], 16))
+ return true;
+ }
+
+ return false;
+}
+
+static bool eir_has_uuids(u8 *eir, u16 eir_len, u16 uuid_count, u8 (*uuids)[16])
+{
+ u16 parsed = 0;
+
+ while (parsed < eir_len) {
+ u8 field_len = eir[0];
+ u8 uuid[16];
+ int i;
+
+ if (field_len == 0)
+ break;
+
+ if (eir_len - parsed < field_len + 1)
+ break;
+
+ switch (eir[1]) {
+ case EIR_UUID16_ALL:
+ case EIR_UUID16_SOME:
+ for (i = 0; i + 3 <= field_len; i += 2) {
+ memcpy(uuid, bluetooth_base_uuid, 16);
+ uuid[13] = eir[i + 3];
+ uuid[12] = eir[i + 2];
+ if (has_uuid(uuid, uuid_count, uuids))
+ return true;
+ }
+ break;
+ case EIR_UUID32_ALL:
+ case EIR_UUID32_SOME:
+ for (i = 0; i + 5 <= field_len; i += 4) {
+ memcpy(uuid, bluetooth_base_uuid, 16);
+ uuid[15] = eir[i + 5];
+ uuid[14] = eir[i + 4];
+ uuid[13] = eir[i + 3];
+ uuid[12] = eir[i + 2];
+ if (has_uuid(uuid, uuid_count, uuids))
+ return true;
+ }
+ break;
+ case EIR_UUID128_ALL:
+ case EIR_UUID128_SOME:
+ for (i = 0; i + 17 <= field_len; i += 16) {
+ memcpy(uuid, eir + i + 2, 16);
+ if (has_uuid(uuid, uuid_count, uuids))
+ return true;
+ }
+ break;
+ }
+
+ parsed += field_len + 1;
+ eir += field_len + 1;
+ }
+
+ return false;
+}
+
void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 addr_type, u8 *dev_class, s8 rssi, u32 flags,
u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len)
@@ -6746,6 +7025,7 @@
char buf[512];
struct mgmt_ev_device_found *ev = (void *) buf;
size_t ev_size;
+ bool match;
/* Don't send events for a non-kernel initiated discovery. With
* LE one exception is if we have pend_le_reports > 0 in which
@@ -6758,6 +7038,18 @@
return;
}
+ /* When using service discovery with a RSSI threshold, then check
+ * if such a RSSI threshold is specified. If a RSSI threshold has
+ * been specified, then all results with a RSSI smaller than the
+ * RSSI threshold will be dropped.
+ *
+ * For BR/EDR devices (pre 1.2) providing no RSSI during inquiry,
+ * the results are also dropped.
+ */
+ if (hdev->discovery.rssi != HCI_RSSI_INVALID &&
+ (rssi < hdev->discovery.rssi || rssi == HCI_RSSI_INVALID))
+ return;
+
/* Make sure that the buffer is big enough. The 5 extra bytes
* are for the potential CoD field.
*/
@@ -6766,20 +7058,79 @@
memset(buf, 0, sizeof(buf));
+ /* In case of device discovery with BR/EDR devices (pre 1.2), the
+ * RSSI value was reported as 0 when not available. This behavior
+ * is kept when using device discovery. This is required for full
+ * backwards compatibility with the API.
+ *
+ * However when using service discovery, the value 127 will be
+ * returned when the RSSI is not available.
+ */
+ if (rssi == HCI_RSSI_INVALID && !hdev->discovery.report_invalid_rssi)
+ rssi = 0;
+
bacpy(&ev->addr.bdaddr, bdaddr);
ev->addr.type = link_to_bdaddr(link_type, addr_type);
ev->rssi = rssi;
ev->flags = cpu_to_le32(flags);
- if (eir_len > 0)
+ if (eir_len > 0) {
+ /* When using service discovery and a list of UUID is
+ * provided, results with no matching UUID should be
+ * dropped. In case there is a match the result is
+ * kept and checking possible scan response data
+ * will be skipped.
+ */
+ if (hdev->discovery.uuid_count > 0)
+ match = eir_has_uuids(eir, eir_len,
+ hdev->discovery.uuid_count,
+ hdev->discovery.uuids);
+ else
+ match = true;
+
+ if (!match && !scan_rsp_len)
+ return;
+
+ /* Copy EIR or advertising data into event */
memcpy(ev->eir, eir, eir_len);
+ } else {
+ /* When using service discovery and a list of UUID is
+ * provided, results with empty EIR or advertising data
+ * should be dropped since they do not match any UUID.
+ */
+ if (hdev->discovery.uuid_count > 0 && !scan_rsp_len)
+ return;
+
+ match = false;
+ }
if (dev_class && !eir_has_data_type(ev->eir, eir_len, EIR_CLASS_OF_DEV))
eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV,
dev_class, 3);
- if (scan_rsp_len > 0)
+ if (scan_rsp_len > 0) {
+ /* When using service discovery and a list of UUID is
+ * provided, results with no matching UUID should be
+ * dropped if there is no previous match from the
+ * advertising data.
+ */
+ if (hdev->discovery.uuid_count > 0) {
+ if (!match && !eir_has_uuids(scan_rsp, scan_rsp_len,
+ hdev->discovery.uuid_count,
+ hdev->discovery.uuids))
+ return;
+ }
+
+ /* Append scan response data to event */
memcpy(ev->eir + eir_len, scan_rsp, scan_rsp_len);
+ } else {
+ /* When using service discovery and a list of UUID is
+ * provided, results with empty scan response and no
+ * previous matched advertising data should be dropped.
+ */
+ if (hdev->discovery.uuid_count > 0 && !match)
+ return;
+ }
ev->eir_len = cpu_to_le16(eir_len + scan_rsp_len);
ev_size = sizeof(*ev) + eir_len + scan_rsp_len;
@@ -6813,23 +7164,9 @@
void mgmt_discovering(struct hci_dev *hdev, u8 discovering)
{
struct mgmt_ev_discovering ev;
- struct pending_cmd *cmd;
BT_DBG("%s discovering %u", hdev->name, discovering);
- if (discovering)
- cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
- else
- cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
-
- if (cmd != NULL) {
- u8 type = hdev->discovery.type;
-
- cmd_complete(cmd->sk, hdev->id, cmd->opcode, 0, &type,
- sizeof(type));
- mgmt_pending_remove(cmd);
- }
-
memset(&ev, 0, sizeof(ev));
ev.type = hdev->discovery.type;
ev.discovering = discovering;
diff --git a/net/bluetooth/rfcomm/Kconfig b/net/bluetooth/rfcomm/Kconfig
index 1ab7444..073e07f 100644
--- a/net/bluetooth/rfcomm/Kconfig
+++ b/net/bluetooth/rfcomm/Kconfig
@@ -1,7 +1,7 @@
config BT_RFCOMM
tristate "RFCOMM protocol support"
depends on m
- depends on BT
+ depends on BT_BREDR
help
RFCOMM provides connection oriented stream transport. RFCOMM
support is required for Dialup Networking, OBEX and other Bluetooth
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index aa3bee4..8b5b653 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -78,8 +78,10 @@
#define __get_type(b) ((b & 0xef))
#define __test_ea(b) ((b & 0x01))
-#define __test_cr(b) ((b & 0x02))
-#define __test_pf(b) ((b & 0x10))
+#define __test_cr(b) (!!(b & 0x02))
+#define __test_pf(b) (!!(b & 0x10))
+
+#define __session_dir(s) ((s)->initiator ? 0x00 : 0x01)
#define __addr(cr, dlci) (((dlci & 0x3f) << 2) | (cr << 1) | 0x01)
#define __ctrl(type, pf) (((type & 0xef) | (pf << 4)))
@@ -101,11 +103,11 @@
#define __get_rpn_stop_bits(line) (((line) >> 2) & 0x1)
#define __get_rpn_parity(line) (((line) >> 3) & 0x7)
+static DECLARE_WAIT_QUEUE_HEAD(rfcomm_wq);
+
static void rfcomm_schedule(void)
{
- if (!rfcomm_thread)
- return;
- wake_up_process(rfcomm_thread);
+ wake_up_all(&rfcomm_wq);
}
/* ---- RFCOMM FCS computation ---- */
@@ -396,7 +398,7 @@
return err;
}
- dlci = __dlci(!s->initiator, channel);
+ dlci = __dlci(__session_dir(s), channel);
/* Check if DLCI already exists */
if (rfcomm_dlc_get(s, dlci))
@@ -551,7 +553,7 @@
rfcomm_lock();
s = rfcomm_session_get(src, dst);
if (s) {
- dlci = __dlci(!s->initiator, channel);
+ dlci = __dlci(__session_dir(s), channel);
dlc = rfcomm_dlc_get(s, dlci);
}
rfcomm_unlock();
@@ -912,7 +914,7 @@
hdr->len = __len8(sizeof(*mcc) + 1);
mcc = (void *) ptr; ptr += sizeof(*mcc);
- mcc->type = __mcc_type(cr, RFCOMM_NSC);
+ mcc->type = __mcc_type(0, RFCOMM_NSC);
mcc->len = __len8(1);
/* Type that we didn't like */
@@ -2098,24 +2100,22 @@
static int rfcomm_run(void *unused)
{
+ DEFINE_WAIT_FUNC(wait, woken_wake_function);
BT_DBG("");
set_user_nice(current, -10);
rfcomm_add_listener(BDADDR_ANY);
- while (1) {
- set_current_state(TASK_INTERRUPTIBLE);
-
- if (kthread_should_stop())
- break;
+ add_wait_queue(&rfcomm_wq, &wait);
+ while (!kthread_should_stop()) {
/* Process stuff */
rfcomm_process_sessions();
- schedule();
+ wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
}
- __set_current_state(TASK_RUNNING);
+ remove_wait_queue(&rfcomm_wq, &wait);
rfcomm_kill_listener();
diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c
index 4688936..acaac1b 100644
--- a/net/bluetooth/rfcomm/sock.c
+++ b/net/bluetooth/rfcomm/sock.c
@@ -596,7 +596,7 @@
}
skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE);
- err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
+ err = memcpy_from_msg(skb_put(skb, size), msg, size);
if (err) {
kfree_skb(skb);
if (sent == 0)
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index c4db547..73bc2c3 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -285,7 +285,7 @@
if (!skb)
return err;
- if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
+ if (memcpy_from_msg(skb_put(skb, len), msg, len)) {
kfree_skb(skb);
return -EFAULT;
}
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index f09b6b6..ef0de39 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -29,14 +29,34 @@
#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/mgmt.h>
+#include "ecc.h"
#include "smp.h"
+/* Low-level debug macros to be used for stuff that we don't want
+ * accidentially in dmesg, i.e. the values of the various crypto keys
+ * and the inputs & outputs of crypto functions.
+ */
+#ifdef DEBUG
+#define SMP_DBG(fmt, ...) printk(KERN_DEBUG "%s: " fmt, __func__, \
+ ##__VA_ARGS__)
+#else
+#define SMP_DBG(fmt, ...) no_printk(KERN_DEBUG "%s: " fmt, __func__, \
+ ##__VA_ARGS__)
+#endif
+
#define SMP_ALLOW_CMD(smp, code) set_bit(code, &smp->allow_cmd)
+/* Keys which are not distributed with Secure Connections */
+#define SMP_SC_NO_DIST (SMP_DIST_ENC_KEY | SMP_DIST_LINK_KEY);
+
#define SMP_TIMEOUT msecs_to_jiffies(30000)
-#define AUTH_REQ_MASK 0x07
-#define KEY_DIST_MASK 0x07
+#define AUTH_REQ_MASK(dev) (test_bit(HCI_SC_ENABLED, &(dev)->dev_flags) ? \
+ 0x1f : 0x07)
+#define KEY_DIST_MASK 0x07
+
+/* Maximum message length that can be passed to aes_cmac */
+#define CMAC_MSG_MAX 80
enum {
SMP_FLAG_TK_VALID,
@@ -44,6 +64,12 @@
SMP_FLAG_MITM_AUTH,
SMP_FLAG_COMPLETE,
SMP_FLAG_INITIATOR,
+ SMP_FLAG_SC,
+ SMP_FLAG_REMOTE_PK,
+ SMP_FLAG_DEBUG_KEY,
+ SMP_FLAG_WAIT_USER,
+ SMP_FLAG_DHKEY_PENDING,
+ SMP_FLAG_OOB,
};
struct smp_chan {
@@ -57,6 +83,7 @@
u8 rrnd[16]; /* SMP Pairing Random (remote) */
u8 pcnf[16]; /* SMP Pairing Confirm */
u8 tk[16]; /* SMP Temporary Key */
+ u8 rr[16];
u8 enc_key_size;
u8 remote_key_dist;
bdaddr_t id_addr;
@@ -67,9 +94,43 @@
struct smp_ltk *ltk;
struct smp_ltk *slave_ltk;
struct smp_irk *remote_irk;
+ u8 *link_key;
unsigned long flags;
+ u8 method;
+ u8 passkey_round;
+
+ /* Secure Connections variables */
+ u8 local_pk[64];
+ u8 local_sk[32];
+ u8 remote_pk[64];
+ u8 dhkey[32];
+ u8 mackey[16];
struct crypto_blkcipher *tfm_aes;
+ struct crypto_hash *tfm_cmac;
+};
+
+/* These debug key values are defined in the SMP section of the core
+ * specification. debug_pk is the public debug key and debug_sk the
+ * private debug key.
+ */
+static const u8 debug_pk[64] = {
+ 0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc,
+ 0xdb, 0xfd, 0xf4, 0xac, 0x11, 0x91, 0xf4, 0xef,
+ 0xb9, 0xa5, 0xf9, 0xe9, 0xa7, 0x83, 0x2c, 0x5e,
+ 0x2c, 0xbe, 0x97, 0xf2, 0xd2, 0x03, 0xb0, 0x20,
+
+ 0x8b, 0xd2, 0x89, 0x15, 0xd0, 0x8e, 0x1c, 0x74,
+ 0x24, 0x30, 0xed, 0x8f, 0xc2, 0x45, 0x63, 0x76,
+ 0x5c, 0x15, 0x52, 0x5a, 0xbf, 0x9a, 0x32, 0x63,
+ 0x6d, 0xeb, 0x2a, 0x65, 0x49, 0x9c, 0x80, 0xdc,
+};
+
+static const u8 debug_sk[32] = {
+ 0xbd, 0x1a, 0x3c, 0xcd, 0xa6, 0xb8, 0x99, 0x58,
+ 0x99, 0xb7, 0x40, 0xeb, 0x7b, 0x60, 0xff, 0x4a,
+ 0x50, 0x3f, 0x10, 0xd2, 0xe3, 0xb3, 0xc9, 0x74,
+ 0x38, 0x5f, 0xc5, 0xa3, 0xd4, 0xf6, 0x49, 0x3f,
};
static inline void swap_buf(const u8 *src, u8 *dst, size_t len)
@@ -80,6 +141,214 @@
dst[len - 1 - i] = src[i];
}
+/* The following functions map to the LE SC SMP crypto functions
+ * AES-CMAC, f4, f5, f6, g2 and h6.
+ */
+
+static int aes_cmac(struct crypto_hash *tfm, const u8 k[16], const u8 *m,
+ size_t len, u8 mac[16])
+{
+ uint8_t tmp[16], mac_msb[16], msg_msb[CMAC_MSG_MAX];
+ struct hash_desc desc;
+ struct scatterlist sg;
+ int err;
+
+ if (len > CMAC_MSG_MAX)
+ return -EFBIG;
+
+ if (!tfm) {
+ BT_ERR("tfm %p", tfm);
+ return -EINVAL;
+ }
+
+ desc.tfm = tfm;
+ desc.flags = 0;
+
+ crypto_hash_init(&desc);
+
+ /* Swap key and message from LSB to MSB */
+ swap_buf(k, tmp, 16);
+ swap_buf(m, msg_msb, len);
+
+ SMP_DBG("msg (len %zu) %*phN", len, (int) len, m);
+ SMP_DBG("key %16phN", k);
+
+ err = crypto_hash_setkey(tfm, tmp, 16);
+ if (err) {
+ BT_ERR("cipher setkey failed: %d", err);
+ return err;
+ }
+
+ sg_init_one(&sg, msg_msb, len);
+
+ err = crypto_hash_update(&desc, &sg, len);
+ if (err) {
+ BT_ERR("Hash update error %d", err);
+ return err;
+ }
+
+ err = crypto_hash_final(&desc, mac_msb);
+ if (err) {
+ BT_ERR("Hash final error %d", err);
+ return err;
+ }
+
+ swap_buf(mac_msb, mac, 16);
+
+ SMP_DBG("mac %16phN", mac);
+
+ return 0;
+}
+
+static int smp_f4(struct crypto_hash *tfm_cmac, const u8 u[32], const u8 v[32],
+ const u8 x[16], u8 z, u8 res[16])
+{
+ u8 m[65];
+ int err;
+
+ SMP_DBG("u %32phN", u);
+ SMP_DBG("v %32phN", v);
+ SMP_DBG("x %16phN z %02x", x, z);
+
+ m[0] = z;
+ memcpy(m + 1, v, 32);
+ memcpy(m + 33, u, 32);
+
+ err = aes_cmac(tfm_cmac, x, m, sizeof(m), res);
+ if (err)
+ return err;
+
+ SMP_DBG("res %16phN", res);
+
+ return err;
+}
+
+static int smp_f5(struct crypto_hash *tfm_cmac, u8 w[32], u8 n1[16], u8 n2[16],
+ u8 a1[7], u8 a2[7], u8 mackey[16], u8 ltk[16])
+{
+ /* The btle, salt and length "magic" values are as defined in
+ * the SMP section of the Bluetooth core specification. In ASCII
+ * the btle value ends up being 'btle'. The salt is just a
+ * random number whereas length is the value 256 in little
+ * endian format.
+ */
+ const u8 btle[4] = { 0x65, 0x6c, 0x74, 0x62 };
+ const u8 salt[16] = { 0xbe, 0x83, 0x60, 0x5a, 0xdb, 0x0b, 0x37, 0x60,
+ 0x38, 0xa5, 0xf5, 0xaa, 0x91, 0x83, 0x88, 0x6c };
+ const u8 length[2] = { 0x00, 0x01 };
+ u8 m[53], t[16];
+ int err;
+
+ SMP_DBG("w %32phN", w);
+ SMP_DBG("n1 %16phN n2 %16phN", n1, n2);
+ SMP_DBG("a1 %7phN a2 %7phN", a1, a2);
+
+ err = aes_cmac(tfm_cmac, salt, w, 32, t);
+ if (err)
+ return err;
+
+ SMP_DBG("t %16phN", t);
+
+ memcpy(m, length, 2);
+ memcpy(m + 2, a2, 7);
+ memcpy(m + 9, a1, 7);
+ memcpy(m + 16, n2, 16);
+ memcpy(m + 32, n1, 16);
+ memcpy(m + 48, btle, 4);
+
+ m[52] = 0; /* Counter */
+
+ err = aes_cmac(tfm_cmac, t, m, sizeof(m), mackey);
+ if (err)
+ return err;
+
+ SMP_DBG("mackey %16phN", mackey);
+
+ m[52] = 1; /* Counter */
+
+ err = aes_cmac(tfm_cmac, t, m, sizeof(m), ltk);
+ if (err)
+ return err;
+
+ SMP_DBG("ltk %16phN", ltk);
+
+ return 0;
+}
+
+static int smp_f6(struct crypto_hash *tfm_cmac, const u8 w[16],
+ const u8 n1[16], u8 n2[16], const u8 r[16],
+ const u8 io_cap[3], const u8 a1[7], const u8 a2[7],
+ u8 res[16])
+{
+ u8 m[65];
+ int err;
+
+ SMP_DBG("w %16phN", w);
+ SMP_DBG("n1 %16phN n2 %16phN", n1, n2);
+ SMP_DBG("r %16phN io_cap %3phN a1 %7phN a2 %7phN", r, io_cap, a1, a2);
+
+ memcpy(m, a2, 7);
+ memcpy(m + 7, a1, 7);
+ memcpy(m + 14, io_cap, 3);
+ memcpy(m + 17, r, 16);
+ memcpy(m + 33, n2, 16);
+ memcpy(m + 49, n1, 16);
+
+ err = aes_cmac(tfm_cmac, w, m, sizeof(m), res);
+ if (err)
+ return err;
+
+ BT_DBG("res %16phN", res);
+
+ return err;
+}
+
+static int smp_g2(struct crypto_hash *tfm_cmac, const u8 u[32], const u8 v[32],
+ const u8 x[16], const u8 y[16], u32 *val)
+{
+ u8 m[80], tmp[16];
+ int err;
+
+ SMP_DBG("u %32phN", u);
+ SMP_DBG("v %32phN", v);
+ SMP_DBG("x %16phN y %16phN", x, y);
+
+ memcpy(m, y, 16);
+ memcpy(m + 16, v, 32);
+ memcpy(m + 48, u, 32);
+
+ err = aes_cmac(tfm_cmac, x, m, sizeof(m), tmp);
+ if (err)
+ return err;
+
+ *val = get_unaligned_le32(tmp);
+ *val %= 1000000;
+
+ SMP_DBG("val %06u", *val);
+
+ return 0;
+}
+
+static int smp_h6(struct crypto_hash *tfm_cmac, const u8 w[16],
+ const u8 key_id[4], u8 res[16])
+{
+ int err;
+
+ SMP_DBG("w %16phN key_id %4phN", w, key_id);
+
+ err = aes_cmac(tfm_cmac, w, key_id, 4, res);
+ if (err)
+ return err;
+
+ SMP_DBG("res %16phN", res);
+
+ return err;
+}
+
+/* The following functions map to the legacy SMP crypto functions e, c1,
+ * s1 and ah.
+ */
+
static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
{
struct blkcipher_desc desc;
@@ -87,7 +356,7 @@
uint8_t tmp[16], data[16];
int err;
- if (tfm == NULL) {
+ if (!tfm) {
BT_ERR("tfm %p", tfm);
return -EINVAL;
}
@@ -119,7 +388,65 @@
return err;
}
-static int smp_ah(struct crypto_blkcipher *tfm, u8 irk[16], u8 r[3], u8 res[3])
+static int smp_c1(struct crypto_blkcipher *tfm_aes, const u8 k[16],
+ const u8 r[16], const u8 preq[7], const u8 pres[7], u8 _iat,
+ const bdaddr_t *ia, u8 _rat, const bdaddr_t *ra, u8 res[16])
+{
+ u8 p1[16], p2[16];
+ int err;
+
+ memset(p1, 0, 16);
+
+ /* p1 = pres || preq || _rat || _iat */
+ p1[0] = _iat;
+ p1[1] = _rat;
+ memcpy(p1 + 2, preq, 7);
+ memcpy(p1 + 9, pres, 7);
+
+ /* p2 = padding || ia || ra */
+ memcpy(p2, ra, 6);
+ memcpy(p2 + 6, ia, 6);
+ memset(p2 + 12, 0, 4);
+
+ /* res = r XOR p1 */
+ u128_xor((u128 *) res, (u128 *) r, (u128 *) p1);
+
+ /* res = e(k, res) */
+ err = smp_e(tfm_aes, k, res);
+ if (err) {
+ BT_ERR("Encrypt data error");
+ return err;
+ }
+
+ /* res = res XOR p2 */
+ u128_xor((u128 *) res, (u128 *) res, (u128 *) p2);
+
+ /* res = e(k, res) */
+ err = smp_e(tfm_aes, k, res);
+ if (err)
+ BT_ERR("Encrypt data error");
+
+ return err;
+}
+
+static int smp_s1(struct crypto_blkcipher *tfm_aes, const u8 k[16],
+ const u8 r1[16], const u8 r2[16], u8 _r[16])
+{
+ int err;
+
+ /* Just least significant octets from r1 and r2 are considered */
+ memcpy(_r, r2, 8);
+ memcpy(_r + 8, r1, 8);
+
+ err = smp_e(tfm_aes, k, _r);
+ if (err)
+ BT_ERR("Encrypt data error");
+
+ return err;
+}
+
+static int smp_ah(struct crypto_blkcipher *tfm, const u8 irk[16],
+ const u8 r[3], u8 res[3])
{
u8 _res[16];
int err;
@@ -145,7 +472,8 @@
return 0;
}
-bool smp_irk_matches(struct hci_dev *hdev, u8 irk[16], bdaddr_t *bdaddr)
+bool smp_irk_matches(struct hci_dev *hdev, const u8 irk[16],
+ const bdaddr_t *bdaddr)
{
struct l2cap_chan *chan = hdev->smp_data;
struct crypto_blkcipher *tfm;
@@ -166,7 +494,7 @@
return !memcmp(bdaddr->b, hash, 3);
}
-int smp_generate_rpa(struct hci_dev *hdev, u8 irk[16], bdaddr_t *rpa)
+int smp_generate_rpa(struct hci_dev *hdev, const u8 irk[16], bdaddr_t *rpa)
{
struct l2cap_chan *chan = hdev->smp_data;
struct crypto_blkcipher *tfm;
@@ -191,69 +519,6 @@
return 0;
}
-static int smp_c1(struct smp_chan *smp, u8 k[16], u8 r[16], u8 preq[7],
- u8 pres[7], u8 _iat, bdaddr_t *ia, u8 _rat, bdaddr_t *ra,
- u8 res[16])
-{
- struct hci_dev *hdev = smp->conn->hcon->hdev;
- u8 p1[16], p2[16];
- int err;
-
- BT_DBG("%s", hdev->name);
-
- memset(p1, 0, 16);
-
- /* p1 = pres || preq || _rat || _iat */
- p1[0] = _iat;
- p1[1] = _rat;
- memcpy(p1 + 2, preq, 7);
- memcpy(p1 + 9, pres, 7);
-
- /* p2 = padding || ia || ra */
- memcpy(p2, ra, 6);
- memcpy(p2 + 6, ia, 6);
- memset(p2 + 12, 0, 4);
-
- /* res = r XOR p1 */
- u128_xor((u128 *) res, (u128 *) r, (u128 *) p1);
-
- /* res = e(k, res) */
- err = smp_e(smp->tfm_aes, k, res);
- if (err) {
- BT_ERR("Encrypt data error");
- return err;
- }
-
- /* res = res XOR p2 */
- u128_xor((u128 *) res, (u128 *) res, (u128 *) p2);
-
- /* res = e(k, res) */
- err = smp_e(smp->tfm_aes, k, res);
- if (err)
- BT_ERR("Encrypt data error");
-
- return err;
-}
-
-static int smp_s1(struct smp_chan *smp, u8 k[16], u8 r1[16], u8 r2[16],
- u8 _r[16])
-{
- struct hci_dev *hdev = smp->conn->hcon->hdev;
- int err;
-
- BT_DBG("%s", hdev->name);
-
- /* Just least significant octets from r1 and r2 are considered */
- memcpy(_r, r2, 8);
- memcpy(_r + 8, r1, 8);
-
- err = smp_e(smp->tfm_aes, k, _r);
- if (err)
- BT_ERR("Encrypt data error");
-
- return err;
-}
-
static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data)
{
struct l2cap_chan *chan = conn->smp;
@@ -274,8 +539,12 @@
memset(&msg, 0, sizeof(msg));
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,19,0)
+ iov_iter_kvec(&msg.msg_iter, WRITE | ITER_KVEC, iv, 2, 1 + len);
+#else
msg.msg_iov = (struct iovec *) &iv;
msg.msg_iovlen = 2;
+#endif
l2cap_chan_send(chan, &msg, 1 + len);
@@ -288,17 +557,22 @@
schedule_delayed_work(&smp->security_timer, SMP_TIMEOUT);
}
-static __u8 authreq_to_seclevel(__u8 authreq)
+static u8 authreq_to_seclevel(u8 authreq)
{
- if (authreq & SMP_AUTH_MITM)
- return BT_SECURITY_HIGH;
- else
+ if (authreq & SMP_AUTH_MITM) {
+ if (authreq & SMP_AUTH_SC)
+ return BT_SECURITY_FIPS;
+ else
+ return BT_SECURITY_HIGH;
+ } else {
return BT_SECURITY_MEDIUM;
+ }
}
static __u8 seclevel_to_authreq(__u8 sec_level)
{
switch (sec_level) {
+ case BT_SECURITY_FIPS:
case BT_SECURITY_HIGH:
return SMP_AUTH_MITM | SMP_AUTH_BONDING;
case BT_SECURITY_MEDIUM:
@@ -316,7 +590,7 @@
struct smp_chan *smp = chan->data;
struct hci_conn *hcon = conn->hcon;
struct hci_dev *hdev = hcon->hdev;
- u8 local_dist = 0, remote_dist = 0;
+ u8 local_dist = 0, remote_dist = 0, oob_flag = SMP_OOB_NOT_PRESENT;
if (test_bit(HCI_BONDABLE, &conn->hcon->hdev->dev_flags)) {
local_dist = SMP_DIST_ENC_KEY | SMP_DIST_SIGN;
@@ -332,24 +606,52 @@
if (test_bit(HCI_PRIVACY, &hdev->dev_flags))
local_dist |= SMP_DIST_ID_KEY;
+ if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags) &&
+ (authreq & SMP_AUTH_SC)) {
+ struct oob_data *oob_data;
+ u8 bdaddr_type;
+
+ if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
+ local_dist |= SMP_DIST_LINK_KEY;
+ remote_dist |= SMP_DIST_LINK_KEY;
+ }
+
+ if (hcon->dst_type == ADDR_LE_DEV_PUBLIC)
+ bdaddr_type = BDADDR_LE_PUBLIC;
+ else
+ bdaddr_type = BDADDR_LE_RANDOM;
+
+ oob_data = hci_find_remote_oob_data(hdev, &hcon->dst,
+ bdaddr_type);
+ if (oob_data) {
+ set_bit(SMP_FLAG_OOB, &smp->flags);
+ oob_flag = SMP_OOB_PRESENT;
+ memcpy(smp->rr, oob_data->rand256, 16);
+ memcpy(smp->pcnf, oob_data->hash256, 16);
+ }
+
+ } else {
+ authreq &= ~SMP_AUTH_SC;
+ }
+
if (rsp == NULL) {
req->io_capability = conn->hcon->io_capability;
- req->oob_flag = SMP_OOB_NOT_PRESENT;
+ req->oob_flag = oob_flag;
req->max_key_size = SMP_MAX_ENC_KEY_SIZE;
req->init_key_dist = local_dist;
req->resp_key_dist = remote_dist;
- req->auth_req = (authreq & AUTH_REQ_MASK);
+ req->auth_req = (authreq & AUTH_REQ_MASK(hdev));
smp->remote_key_dist = remote_dist;
return;
}
rsp->io_capability = conn->hcon->io_capability;
- rsp->oob_flag = SMP_OOB_NOT_PRESENT;
+ rsp->oob_flag = oob_flag;
rsp->max_key_size = SMP_MAX_ENC_KEY_SIZE;
rsp->init_key_dist = req->init_key_dist & remote_dist;
rsp->resp_key_dist = req->resp_key_dist & local_dist;
- rsp->auth_req = (authreq & AUTH_REQ_MASK);
+ rsp->auth_req = (authreq & AUTH_REQ_MASK(hdev));
smp->remote_key_dist = rsp->init_key_dist;
}
@@ -372,6 +674,7 @@
{
struct l2cap_chan *chan = conn->smp;
struct smp_chan *smp = chan->data;
+ struct hci_conn *hcon = conn->hcon;
bool complete;
BUG_ON(!smp);
@@ -379,34 +682,46 @@
cancel_delayed_work_sync(&smp->security_timer);
complete = test_bit(SMP_FLAG_COMPLETE, &smp->flags);
- mgmt_smp_complete(conn->hcon, complete);
+ mgmt_smp_complete(hcon, complete);
kfree(smp->csrk);
kfree(smp->slave_csrk);
+ kfree(smp->link_key);
crypto_free_blkcipher(smp->tfm_aes);
+ crypto_free_hash(smp->tfm_cmac);
+
+ /* Ensure that we don't leave any debug key around if debug key
+ * support hasn't been explicitly enabled.
+ */
+ if (smp->ltk && smp->ltk->type == SMP_LTK_P256_DEBUG &&
+ !test_bit(HCI_KEEP_DEBUG_KEYS, &hcon->hdev->dev_flags)) {
+ list_del_rcu(&smp->ltk->list);
+ kfree_rcu(smp->ltk, rcu);
+ smp->ltk = NULL;
+ }
/* If pairing failed clean up any keys we might have */
if (!complete) {
if (smp->ltk) {
- list_del(&smp->ltk->list);
- kfree(smp->ltk);
+ list_del_rcu(&smp->ltk->list);
+ kfree_rcu(smp->ltk, rcu);
}
if (smp->slave_ltk) {
- list_del(&smp->slave_ltk->list);
- kfree(smp->slave_ltk);
+ list_del_rcu(&smp->slave_ltk->list);
+ kfree_rcu(smp->slave_ltk, rcu);
}
if (smp->remote_irk) {
- list_del(&smp->remote_irk->list);
- kfree(smp->remote_irk);
+ list_del_rcu(&smp->remote_irk->list);
+ kfree_rcu(smp->remote_irk, rcu);
}
}
chan->data = NULL;
kfree(smp);
- hci_conn_drop(conn->hcon);
+ hci_conn_drop(hcon);
}
static void smp_failure(struct l2cap_conn *conn, u8 reason)
@@ -430,6 +745,7 @@
#define REQ_PASSKEY 0x02
#define CFM_PASSKEY 0x03
#define REQ_OOB 0x04
+#define DSP_PASSKEY 0x05
#define OVERLAP 0xFF
static const u8 gen_method[5][5] = {
@@ -440,6 +756,14 @@
{ CFM_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, OVERLAP },
};
+static const u8 sc_method[5][5] = {
+ { JUST_WORKS, JUST_CFM, REQ_PASSKEY, JUST_WORKS, REQ_PASSKEY },
+ { JUST_WORKS, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, CFM_PASSKEY },
+ { DSP_PASSKEY, DSP_PASSKEY, REQ_PASSKEY, JUST_WORKS, DSP_PASSKEY },
+ { JUST_WORKS, JUST_CFM, JUST_WORKS, JUST_WORKS, JUST_CFM },
+ { DSP_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, CFM_PASSKEY },
+};
+
static u8 get_auth_method(struct smp_chan *smp, u8 local_io, u8 remote_io)
{
/* If either side has unknown io_caps, use JUST_CFM (which gets
@@ -449,6 +773,9 @@
remote_io > SMP_IO_KEYBOARD_DISPLAY)
return JUST_CFM;
+ if (test_bit(SMP_FLAG_SC, &smp->flags))
+ return sc_method[remote_io][local_io];
+
return gen_method[remote_io][local_io];
}
@@ -458,7 +785,6 @@
struct hci_conn *hcon = conn->hcon;
struct l2cap_chan *chan = conn->smp;
struct smp_chan *smp = chan->data;
- u8 method;
u32 passkey = 0;
int ret = 0;
@@ -475,26 +801,28 @@
* table.
*/
if (!(auth & SMP_AUTH_MITM))
- method = JUST_CFM;
+ smp->method = JUST_CFM;
else
- method = get_auth_method(smp, local_io, remote_io);
+ smp->method = get_auth_method(smp, local_io, remote_io);
/* Don't confirm locally initiated pairing attempts */
- if (method == JUST_CFM && test_bit(SMP_FLAG_INITIATOR, &smp->flags))
- method = JUST_WORKS;
+ if (smp->method == JUST_CFM && test_bit(SMP_FLAG_INITIATOR,
+ &smp->flags))
+ smp->method = JUST_WORKS;
/* Don't bother user space with no IO capabilities */
- if (method == JUST_CFM && hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT)
- method = JUST_WORKS;
+ if (smp->method == JUST_CFM &&
+ hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT)
+ smp->method = JUST_WORKS;
/* If Just Works, Continue with Zero TK */
- if (method == JUST_WORKS) {
+ if (smp->method == JUST_WORKS) {
set_bit(SMP_FLAG_TK_VALID, &smp->flags);
return 0;
}
/* Not Just Works/Confirm results in MITM Authentication */
- if (method != JUST_CFM) {
+ if (smp->method != JUST_CFM) {
set_bit(SMP_FLAG_MITM_AUTH, &smp->flags);
if (hcon->pending_sec_level < BT_SECURITY_HIGH)
hcon->pending_sec_level = BT_SECURITY_HIGH;
@@ -503,15 +831,15 @@
/* If both devices have Keyoard-Display I/O, the master
* Confirms and the slave Enters the passkey.
*/
- if (method == OVERLAP) {
+ if (smp->method == OVERLAP) {
if (hcon->role == HCI_ROLE_MASTER)
- method = CFM_PASSKEY;
+ smp->method = CFM_PASSKEY;
else
- method = REQ_PASSKEY;
+ smp->method = REQ_PASSKEY;
}
/* Generate random passkey. */
- if (method == CFM_PASSKEY) {
+ if (smp->method == CFM_PASSKEY) {
memset(smp->tk, 0, sizeof(smp->tk));
get_random_bytes(&passkey, sizeof(passkey));
passkey %= 1000000;
@@ -520,12 +848,10 @@
set_bit(SMP_FLAG_TK_VALID, &smp->flags);
}
- hci_dev_lock(hcon->hdev);
-
- if (method == REQ_PASSKEY)
+ if (smp->method == REQ_PASSKEY)
ret = mgmt_user_passkey_request(hcon->hdev, &hcon->dst,
hcon->type, hcon->dst_type);
- else if (method == JUST_CFM)
+ else if (smp->method == JUST_CFM)
ret = mgmt_user_confirm_request(hcon->hdev, &hcon->dst,
hcon->type, hcon->dst_type,
passkey, 1);
@@ -534,8 +860,6 @@
hcon->type, hcon->dst_type,
passkey, 0);
- hci_dev_unlock(hcon->hdev);
-
return ret;
}
@@ -547,7 +871,7 @@
BT_DBG("conn %p", conn);
- ret = smp_c1(smp, smp->tk, smp->prnd, smp->preq, smp->prsp,
+ ret = smp_c1(smp->tfm_aes, smp->tk, smp->prnd, smp->preq, smp->prsp,
conn->hcon->init_addr_type, &conn->hcon->init_addr,
conn->hcon->resp_addr_type, &conn->hcon->resp_addr,
cp.confirm_val);
@@ -578,7 +902,7 @@
BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
- ret = smp_c1(smp, smp->tk, smp->rrnd, smp->preq, smp->prsp,
+ ret = smp_c1(smp->tfm_aes, smp->tk, smp->rrnd, smp->preq, smp->prsp,
hcon->init_addr_type, &hcon->init_addr,
hcon->resp_addr_type, &hcon->resp_addr, confirm);
if (ret)
@@ -594,7 +918,7 @@
__le64 rand = 0;
__le16 ediv = 0;
- smp_s1(smp, smp->tk, smp->rrnd, smp->prnd, stk);
+ smp_s1(smp->tfm_aes, smp->tk, smp->rrnd, smp->prnd, stk);
memset(stk + smp->enc_key_size, 0,
SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
@@ -613,7 +937,7 @@
smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd),
smp->prnd);
- smp_s1(smp, smp->tk, smp->prnd, smp->rrnd, stk);
+ smp_s1(smp->tfm_aes, smp->tk, smp->prnd, smp->rrnd, stk);
memset(stk + smp->enc_key_size, 0,
SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
@@ -648,11 +972,13 @@
mgmt_new_irk(hdev, smp->remote_irk);
/* Now that user space can be considered to know the
* identity address track the connection based on it
- * from now on.
+ * from now on (assuming this is an LE link).
*/
- bacpy(&hcon->dst, &smp->remote_irk->bdaddr);
- hcon->dst_type = smp->remote_irk->addr_type;
- queue_work(hdev->workqueue, &conn->id_addr_update_work);
+ if (hcon->type == LE_LINK) {
+ bacpy(&hcon->dst, &smp->remote_irk->bdaddr);
+ hcon->dst_type = smp->remote_irk->addr_type;
+ queue_work(hdev->workqueue, &conn->id_addr_update_work);
+ }
/* When receiving an indentity resolving key for
* a remote device that does not use a resolvable
@@ -665,16 +991,26 @@
* just remove it.
*/
if (!bacmp(&smp->remote_irk->rpa, BDADDR_ANY)) {
- list_del(&smp->remote_irk->list);
- kfree(smp->remote_irk);
+ list_del_rcu(&smp->remote_irk->list);
+ kfree_rcu(smp->remote_irk, rcu);
smp->remote_irk = NULL;
}
}
- /* The LTKs and CSRKs should be persistent only if both sides
- * had the bonding bit set in their authentication requests.
- */
- persistent = !!((req->auth_req & rsp->auth_req) & SMP_AUTH_BONDING);
+ if (hcon->type == ACL_LINK) {
+ if (hcon->key_type == HCI_LK_DEBUG_COMBINATION)
+ persistent = false;
+ else
+ persistent = !test_bit(HCI_CONN_FLUSH_KEY,
+ &hcon->flags);
+ } else {
+ /* The LTKs and CSRKs should be persistent only if both sides
+ * had the bonding bit set in their authentication requests.
+ */
+ persistent = !!((req->auth_req & rsp->auth_req) &
+ SMP_AUTH_BONDING);
+ }
+
if (smp->csrk) {
smp->csrk->bdaddr_type = hcon->dst_type;
@@ -699,6 +1035,81 @@
bacpy(&smp->slave_ltk->bdaddr, &hcon->dst);
mgmt_new_ltk(hdev, smp->slave_ltk, persistent);
}
+
+ if (smp->link_key) {
+ struct link_key *key;
+ u8 type;
+
+ if (test_bit(SMP_FLAG_DEBUG_KEY, &smp->flags))
+ type = HCI_LK_DEBUG_COMBINATION;
+ else if (hcon->sec_level == BT_SECURITY_FIPS)
+ type = HCI_LK_AUTH_COMBINATION_P256;
+ else
+ type = HCI_LK_UNAUTH_COMBINATION_P256;
+
+ key = hci_add_link_key(hdev, smp->conn->hcon, &hcon->dst,
+ smp->link_key, type, 0, &persistent);
+ if (key) {
+ mgmt_new_link_key(hdev, key, persistent);
+
+ /* Don't keep debug keys around if the relevant
+ * flag is not set.
+ */
+ if (!test_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags) &&
+ key->type == HCI_LK_DEBUG_COMBINATION) {
+ list_del_rcu(&key->list);
+ kfree_rcu(key, rcu);
+ }
+ }
+ }
+}
+
+static void sc_add_ltk(struct smp_chan *smp)
+{
+ struct hci_conn *hcon = smp->conn->hcon;
+ u8 key_type, auth;
+
+ if (test_bit(SMP_FLAG_DEBUG_KEY, &smp->flags))
+ key_type = SMP_LTK_P256_DEBUG;
+ else
+ key_type = SMP_LTK_P256;
+
+ if (hcon->pending_sec_level == BT_SECURITY_FIPS)
+ auth = 1;
+ else
+ auth = 0;
+
+ memset(smp->tk + smp->enc_key_size, 0,
+ SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
+
+ smp->ltk = hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type,
+ key_type, auth, smp->tk, smp->enc_key_size,
+ 0, 0);
+}
+
+static void sc_generate_link_key(struct smp_chan *smp)
+{
+ /* These constants are as specified in the core specification.
+ * In ASCII they spell out to 'tmp1' and 'lebr'.
+ */
+ const u8 tmp1[4] = { 0x31, 0x70, 0x6d, 0x74 };
+ const u8 lebr[4] = { 0x72, 0x62, 0x65, 0x6c };
+
+ smp->link_key = kzalloc(16, GFP_KERNEL);
+ if (!smp->link_key)
+ return;
+
+ if (smp_h6(smp->tfm_cmac, smp->tk, tmp1, smp->link_key)) {
+ kfree(smp->link_key);
+ smp->link_key = NULL;
+ return;
+ }
+
+ if (smp_h6(smp->tfm_cmac, smp->link_key, lebr, smp->link_key)) {
+ kfree(smp->link_key);
+ smp->link_key = NULL;
+ return;
+ }
}
static void smp_allow_key_dist(struct smp_chan *smp)
@@ -715,6 +1126,35 @@
SMP_ALLOW_CMD(smp, SMP_CMD_SIGN_INFO);
}
+static void sc_generate_ltk(struct smp_chan *smp)
+{
+ /* These constants are as specified in the core specification.
+ * In ASCII they spell out to 'tmp2' and 'brle'.
+ */
+ const u8 tmp2[4] = { 0x32, 0x70, 0x6d, 0x74 };
+ const u8 brle[4] = { 0x65, 0x6c, 0x72, 0x62 };
+ struct hci_conn *hcon = smp->conn->hcon;
+ struct hci_dev *hdev = hcon->hdev;
+ struct link_key *key;
+
+ key = hci_find_link_key(hdev, &hcon->dst);
+ if (!key) {
+ BT_ERR("%s No Link Key found to generate LTK", hdev->name);
+ return;
+ }
+
+ if (key->type == HCI_LK_DEBUG_COMBINATION)
+ set_bit(SMP_FLAG_DEBUG_KEY, &smp->flags);
+
+ if (smp_h6(smp->tfm_cmac, key->val, tmp2, smp->tk))
+ return;
+
+ if (smp_h6(smp->tfm_cmac, smp->tk, brle, smp->tk))
+ return;
+
+ sc_add_ltk(smp);
+}
+
static void smp_distribute_keys(struct smp_chan *smp)
{
struct smp_cmd_pairing *req, *rsp;
@@ -743,6 +1183,16 @@
*keydist &= req->resp_key_dist;
}
+ if (test_bit(SMP_FLAG_SC, &smp->flags)) {
+ if (hcon->type == LE_LINK && (*keydist & SMP_DIST_LINK_KEY))
+ sc_generate_link_key(smp);
+ if (hcon->type == ACL_LINK && (*keydist & SMP_DIST_ENC_KEY))
+ sc_generate_ltk(smp);
+
+ /* Clear the keys which are generated but not distributed */
+ *keydist &= ~SMP_SC_NO_DIST;
+ }
+
BT_DBG("keydist 0x%x", *keydist);
if (*keydist & SMP_DIST_ENC_KEY) {
@@ -854,6 +1304,14 @@
return NULL;
}
+ smp->tfm_cmac = crypto_alloc_hash("cmac(aes)", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(smp->tfm_cmac)) {
+ BT_ERR("Unable to create CMAC crypto context");
+ crypto_free_blkcipher(smp->tfm_aes);
+ kfree(smp);
+ return NULL;
+ }
+
smp->conn = conn;
chan->data = smp;
@@ -866,6 +1324,213 @@
return smp;
}
+static int sc_mackey_and_ltk(struct smp_chan *smp, u8 mackey[16], u8 ltk[16])
+{
+ struct hci_conn *hcon = smp->conn->hcon;
+ u8 *na, *nb, a[7], b[7];
+
+ if (hcon->out) {
+ na = smp->prnd;
+ nb = smp->rrnd;
+ } else {
+ na = smp->rrnd;
+ nb = smp->prnd;
+ }
+
+ memcpy(a, &hcon->init_addr, 6);
+ memcpy(b, &hcon->resp_addr, 6);
+ a[6] = hcon->init_addr_type;
+ b[6] = hcon->resp_addr_type;
+
+ return smp_f5(smp->tfm_cmac, smp->dhkey, na, nb, a, b, mackey, ltk);
+}
+
+static void sc_dhkey_check(struct smp_chan *smp)
+{
+ struct hci_conn *hcon = smp->conn->hcon;
+ struct smp_cmd_dhkey_check check;
+ u8 a[7], b[7], *local_addr, *remote_addr;
+ u8 io_cap[3], r[16];
+
+ memcpy(a, &hcon->init_addr, 6);
+ memcpy(b, &hcon->resp_addr, 6);
+ a[6] = hcon->init_addr_type;
+ b[6] = hcon->resp_addr_type;
+
+ if (hcon->out) {
+ local_addr = a;
+ remote_addr = b;
+ memcpy(io_cap, &smp->preq[1], 3);
+ } else {
+ local_addr = b;
+ remote_addr = a;
+ memcpy(io_cap, &smp->prsp[1], 3);
+ }
+
+ memset(r, 0, sizeof(r));
+
+ if (smp->method == REQ_PASSKEY || smp->method == DSP_PASSKEY)
+ put_unaligned_le32(hcon->passkey_notify, r);
+
+ if (smp->method == REQ_OOB)
+ memcpy(r, smp->rr, 16);
+
+ smp_f6(smp->tfm_cmac, smp->mackey, smp->prnd, smp->rrnd, r, io_cap,
+ local_addr, remote_addr, check.e);
+
+ smp_send_cmd(smp->conn, SMP_CMD_DHKEY_CHECK, sizeof(check), &check);
+}
+
+static u8 sc_passkey_send_confirm(struct smp_chan *smp)
+{
+ struct l2cap_conn *conn = smp->conn;
+ struct hci_conn *hcon = conn->hcon;
+ struct smp_cmd_pairing_confirm cfm;
+ u8 r;
+
+ r = ((hcon->passkey_notify >> smp->passkey_round) & 0x01);
+ r |= 0x80;
+
+ get_random_bytes(smp->prnd, sizeof(smp->prnd));
+
+ if (smp_f4(smp->tfm_cmac, smp->local_pk, smp->remote_pk, smp->prnd, r,
+ cfm.confirm_val))
+ return SMP_UNSPECIFIED;
+
+ smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cfm), &cfm);
+
+ return 0;
+}
+
+static u8 sc_passkey_round(struct smp_chan *smp, u8 smp_op)
+{
+ struct l2cap_conn *conn = smp->conn;
+ struct hci_conn *hcon = conn->hcon;
+ struct hci_dev *hdev = hcon->hdev;
+ u8 cfm[16], r;
+
+ /* Ignore the PDU if we've already done 20 rounds (0 - 19) */
+ if (smp->passkey_round >= 20)
+ return 0;
+
+ switch (smp_op) {
+ case SMP_CMD_PAIRING_RANDOM:
+ r = ((hcon->passkey_notify >> smp->passkey_round) & 0x01);
+ r |= 0x80;
+
+ if (smp_f4(smp->tfm_cmac, smp->remote_pk, smp->local_pk,
+ smp->rrnd, r, cfm))
+ return SMP_UNSPECIFIED;
+
+ if (memcmp(smp->pcnf, cfm, 16))
+ return SMP_CONFIRM_FAILED;
+
+ smp->passkey_round++;
+
+ if (smp->passkey_round == 20) {
+ /* Generate MacKey and LTK */
+ if (sc_mackey_and_ltk(smp, smp->mackey, smp->tk))
+ return SMP_UNSPECIFIED;
+ }
+
+ /* The round is only complete when the initiator
+ * receives pairing random.
+ */
+ if (!hcon->out) {
+ smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM,
+ sizeof(smp->prnd), smp->prnd);
+ if (smp->passkey_round == 20)
+ SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK);
+ else
+ SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+ return 0;
+ }
+
+ /* Start the next round */
+ if (smp->passkey_round != 20)
+ return sc_passkey_round(smp, 0);
+
+ /* Passkey rounds are complete - start DHKey Check */
+ sc_dhkey_check(smp);
+ SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK);
+
+ break;
+
+ case SMP_CMD_PAIRING_CONFIRM:
+ if (test_bit(SMP_FLAG_WAIT_USER, &smp->flags)) {
+ set_bit(SMP_FLAG_CFM_PENDING, &smp->flags);
+ return 0;
+ }
+
+ SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_RANDOM);
+
+ if (hcon->out) {
+ smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM,
+ sizeof(smp->prnd), smp->prnd);
+ return 0;
+ }
+
+ return sc_passkey_send_confirm(smp);
+
+ case SMP_CMD_PUBLIC_KEY:
+ default:
+ /* Initiating device starts the round */
+ if (!hcon->out)
+ return 0;
+
+ BT_DBG("%s Starting passkey round %u", hdev->name,
+ smp->passkey_round + 1);
+
+ SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+
+ return sc_passkey_send_confirm(smp);
+ }
+
+ return 0;
+}
+
+static int sc_user_reply(struct smp_chan *smp, u16 mgmt_op, __le32 passkey)
+{
+ struct l2cap_conn *conn = smp->conn;
+ struct hci_conn *hcon = conn->hcon;
+ u8 smp_op;
+
+ clear_bit(SMP_FLAG_WAIT_USER, &smp->flags);
+
+ switch (mgmt_op) {
+ case MGMT_OP_USER_PASSKEY_NEG_REPLY:
+ smp_failure(smp->conn, SMP_PASSKEY_ENTRY_FAILED);
+ return 0;
+ case MGMT_OP_USER_CONFIRM_NEG_REPLY:
+ smp_failure(smp->conn, SMP_NUMERIC_COMP_FAILED);
+ return 0;
+ case MGMT_OP_USER_PASSKEY_REPLY:
+ hcon->passkey_notify = le32_to_cpu(passkey);
+ smp->passkey_round = 0;
+
+ if (test_and_clear_bit(SMP_FLAG_CFM_PENDING, &smp->flags))
+ smp_op = SMP_CMD_PAIRING_CONFIRM;
+ else
+ smp_op = 0;
+
+ if (sc_passkey_round(smp, smp_op))
+ return -EIO;
+
+ return 0;
+ }
+
+ /* Initiator sends DHKey check first */
+ if (hcon->out) {
+ sc_dhkey_check(smp);
+ SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK);
+ } else if (test_and_clear_bit(SMP_FLAG_DHKEY_PENDING, &smp->flags)) {
+ sc_dhkey_check(smp);
+ sc_add_ltk(smp);
+ }
+
+ return 0;
+}
+
int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey)
{
struct l2cap_conn *conn = hcon->l2cap_data;
@@ -891,6 +1556,11 @@
smp = chan->data;
+ if (test_bit(SMP_FLAG_SC, &smp->flags)) {
+ err = sc_user_reply(smp, mgmt_op, passkey);
+ goto unlock;
+ }
+
switch (mgmt_op) {
case MGMT_OP_USER_PASSKEY_REPLY:
value = le32_to_cpu(passkey);
@@ -926,6 +1596,46 @@
return err;
}
+static void build_bredr_pairing_cmd(struct smp_chan *smp,
+ struct smp_cmd_pairing *req,
+ struct smp_cmd_pairing *rsp)
+{
+ struct l2cap_conn *conn = smp->conn;
+ struct hci_dev *hdev = conn->hcon->hdev;
+ u8 local_dist = 0, remote_dist = 0;
+
+ if (test_bit(HCI_BONDABLE, &hdev->dev_flags)) {
+ local_dist = SMP_DIST_ENC_KEY | SMP_DIST_SIGN;
+ remote_dist = SMP_DIST_ENC_KEY | SMP_DIST_SIGN;
+ }
+
+ if (test_bit(HCI_RPA_RESOLVING, &hdev->dev_flags))
+ remote_dist |= SMP_DIST_ID_KEY;
+
+ if (test_bit(HCI_PRIVACY, &hdev->dev_flags))
+ local_dist |= SMP_DIST_ID_KEY;
+
+ if (!rsp) {
+ memset(req, 0, sizeof(*req));
+
+ req->init_key_dist = local_dist;
+ req->resp_key_dist = remote_dist;
+ req->max_key_size = SMP_MAX_ENC_KEY_SIZE;
+
+ smp->remote_key_dist = remote_dist;
+
+ return;
+ }
+
+ memset(rsp, 0, sizeof(*rsp));
+
+ rsp->max_key_size = SMP_MAX_ENC_KEY_SIZE;
+ rsp->init_key_dist = req->init_key_dist & remote_dist;
+ rsp->resp_key_dist = req->resp_key_dist & local_dist;
+
+ smp->remote_key_dist = rsp->init_key_dist;
+}
+
static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
{
struct smp_cmd_pairing rsp, *req = (void *) skb->data;
@@ -952,16 +1662,50 @@
return SMP_UNSPECIFIED;
/* We didn't start the pairing, so match remote */
- auth = req->auth_req & AUTH_REQ_MASK;
+ auth = req->auth_req & AUTH_REQ_MASK(hdev);
if (!test_bit(HCI_BONDABLE, &hdev->dev_flags) &&
(auth & SMP_AUTH_BONDING))
return SMP_PAIRING_NOTSUPP;
+ if (test_bit(HCI_SC_ONLY, &hdev->dev_flags) && !(auth & SMP_AUTH_SC))
+ return SMP_AUTH_REQUIREMENTS;
+
smp->preq[0] = SMP_CMD_PAIRING_REQ;
memcpy(&smp->preq[1], req, sizeof(*req));
skb_pull(skb, sizeof(*req));
+ /* SMP over BR/EDR requires special treatment */
+ if (conn->hcon->type == ACL_LINK) {
+ /* We must have a BR/EDR SC link */
+ if (!test_bit(HCI_CONN_AES_CCM, &conn->hcon->flags) &&
+ !test_bit(HCI_FORCE_LESC, &hdev->dbg_flags))
+ return SMP_CROSS_TRANSP_NOT_ALLOWED;
+
+ set_bit(SMP_FLAG_SC, &smp->flags);
+
+ build_bredr_pairing_cmd(smp, req, &rsp);
+
+ key_size = min(req->max_key_size, rsp.max_key_size);
+ if (check_enc_key_size(conn, key_size))
+ return SMP_ENC_KEY_SIZE;
+
+ /* Clear bits which are generated but not distributed */
+ smp->remote_key_dist &= ~SMP_SC_NO_DIST;
+
+ smp->prsp[0] = SMP_CMD_PAIRING_RSP;
+ memcpy(&smp->prsp[1], &rsp, sizeof(rsp));
+ smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(rsp), &rsp);
+
+ smp_distribute_keys(smp);
+ return 0;
+ }
+
+ build_pairing_cmd(conn, req, &rsp, auth);
+
+ if (rsp.auth_req & SMP_AUTH_SC)
+ set_bit(SMP_FLAG_SC, &smp->flags);
+
if (conn->hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT)
sec_level = BT_SECURITY_MEDIUM;
else
@@ -970,7 +1714,7 @@
if (sec_level > conn->hcon->pending_sec_level)
conn->hcon->pending_sec_level = sec_level;
- /* If we need MITM check that it can be acheived */
+ /* If we need MITM check that it can be achieved */
if (conn->hcon->pending_sec_level >= BT_SECURITY_HIGH) {
u8 method;
@@ -980,8 +1724,6 @@
return SMP_AUTH_REQUIREMENTS;
}
- build_pairing_cmd(conn, req, &rsp, auth);
-
key_size = min(req->max_key_size, rsp.max_key_size);
if (check_enc_key_size(conn, key_size))
return SMP_ENC_KEY_SIZE;
@@ -992,7 +1734,18 @@
memcpy(&smp->prsp[1], &rsp, sizeof(rsp));
smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(rsp), &rsp);
- SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+
+ clear_bit(SMP_FLAG_INITIATOR, &smp->flags);
+
+ if (test_bit(SMP_FLAG_SC, &smp->flags)) {
+ SMP_ALLOW_CMD(smp, SMP_CMD_PUBLIC_KEY);
+ /* Clear bits which are generated but not distributed */
+ smp->remote_key_dist &= ~SMP_SC_NO_DIST;
+ /* Wait for Public Key from Initiating Device */
+ return 0;
+ } else {
+ SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+ }
/* Request setup of TK */
ret = tk_request(conn, 0, auth, rsp.io_capability, req->io_capability);
@@ -1002,11 +1755,46 @@
return 0;
}
+static u8 sc_send_public_key(struct smp_chan *smp)
+{
+ struct hci_dev *hdev = smp->conn->hcon->hdev;
+
+ BT_DBG("");
+
+ if (test_bit(HCI_USE_DEBUG_KEYS, &hdev->dev_flags)) {
+ BT_DBG("Using debug keys");
+ memcpy(smp->local_pk, debug_pk, 64);
+ memcpy(smp->local_sk, debug_sk, 32);
+ set_bit(SMP_FLAG_DEBUG_KEY, &smp->flags);
+ } else {
+ while (true) {
+ /* Generate local key pair for Secure Connections */
+ if (!ecc_make_key(smp->local_pk, smp->local_sk))
+ return SMP_UNSPECIFIED;
+
+ /* This is unlikely, but we need to check that
+ * we didn't accidentially generate a debug key.
+ */
+ if (memcmp(smp->local_sk, debug_sk, 32))
+ break;
+ }
+ }
+
+ SMP_DBG("Local Public Key X: %32phN", smp->local_pk);
+ SMP_DBG("Local Public Key Y: %32phN", &smp->local_pk[32]);
+ SMP_DBG("Local Private Key: %32phN", smp->local_sk);
+
+ smp_send_cmd(smp->conn, SMP_CMD_PUBLIC_KEY, 64, smp->local_pk);
+
+ return 0;
+}
+
static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
{
struct smp_cmd_pairing *req, *rsp = (void *) skb->data;
struct l2cap_chan *chan = conn->smp;
struct smp_chan *smp = chan->data;
+ struct hci_dev *hdev = conn->hcon->hdev;
u8 key_size, auth;
int ret;
@@ -1026,9 +1814,33 @@
if (check_enc_key_size(conn, key_size))
return SMP_ENC_KEY_SIZE;
- auth = rsp->auth_req & AUTH_REQ_MASK;
+ auth = rsp->auth_req & AUTH_REQ_MASK(hdev);
- /* If we need MITM check that it can be acheived */
+ if (test_bit(HCI_SC_ONLY, &hdev->dev_flags) && !(auth & SMP_AUTH_SC))
+ return SMP_AUTH_REQUIREMENTS;
+
+ smp->prsp[0] = SMP_CMD_PAIRING_RSP;
+ memcpy(&smp->prsp[1], rsp, sizeof(*rsp));
+
+ /* Update remote key distribution in case the remote cleared
+ * some bits that we had enabled in our request.
+ */
+ smp->remote_key_dist &= rsp->resp_key_dist;
+
+ /* For BR/EDR this means we're done and can start phase 3 */
+ if (conn->hcon->type == ACL_LINK) {
+ /* Clear bits which are generated but not distributed */
+ smp->remote_key_dist &= ~SMP_SC_NO_DIST;
+ smp_distribute_keys(smp);
+ return 0;
+ }
+
+ if ((req->auth_req & SMP_AUTH_SC) && (auth & SMP_AUTH_SC))
+ set_bit(SMP_FLAG_SC, &smp->flags);
+ else if (conn->hcon->pending_sec_level > BT_SECURITY_HIGH)
+ conn->hcon->pending_sec_level = BT_SECURITY_HIGH;
+
+ /* If we need MITM check that it can be achieved */
if (conn->hcon->pending_sec_level >= BT_SECURITY_HIGH) {
u8 method;
@@ -1040,14 +1852,18 @@
get_random_bytes(smp->prnd, sizeof(smp->prnd));
- smp->prsp[0] = SMP_CMD_PAIRING_RSP;
- memcpy(&smp->prsp[1], rsp, sizeof(*rsp));
-
/* Update remote key distribution in case the remote cleared
* some bits that we had enabled in our request.
*/
smp->remote_key_dist &= rsp->resp_key_dist;
+ if (test_bit(SMP_FLAG_SC, &smp->flags)) {
+ /* Clear bits which are generated but not distributed */
+ smp->remote_key_dist &= ~SMP_SC_NO_DIST;
+ SMP_ALLOW_CMD(smp, SMP_CMD_PUBLIC_KEY);
+ return sc_send_public_key(smp);
+ }
+
auth |= req->auth_req;
ret = tk_request(conn, 0, auth, req->io_capability, rsp->io_capability);
@@ -1063,6 +1879,28 @@
return 0;
}
+static u8 sc_check_confirm(struct smp_chan *smp)
+{
+ struct l2cap_conn *conn = smp->conn;
+
+ BT_DBG("");
+
+ /* Public Key exchange must happen before any other steps */
+ if (!test_bit(SMP_FLAG_REMOTE_PK, &smp->flags))
+ return SMP_UNSPECIFIED;
+
+ if (smp->method == REQ_PASSKEY || smp->method == DSP_PASSKEY)
+ return sc_passkey_round(smp, SMP_CMD_PAIRING_CONFIRM);
+
+ if (conn->hcon->out) {
+ smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd),
+ smp->prnd);
+ SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_RANDOM);
+ }
+
+ return 0;
+}
+
static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
{
struct l2cap_chan *chan = conn->smp;
@@ -1076,6 +1914,9 @@
memcpy(smp->pcnf, skb->data, sizeof(smp->pcnf));
skb_pull(skb, sizeof(smp->pcnf));
+ if (test_bit(SMP_FLAG_SC, &smp->flags))
+ return sc_check_confirm(smp);
+
if (conn->hcon->out) {
smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd),
smp->prnd);
@@ -1095,6 +1936,10 @@
{
struct l2cap_chan *chan = conn->smp;
struct smp_chan *smp = chan->data;
+ struct hci_conn *hcon = conn->hcon;
+ u8 *pkax, *pkbx, *na, *nb;
+ u32 passkey;
+ int err;
BT_DBG("conn %p", conn);
@@ -1104,7 +1949,75 @@
memcpy(smp->rrnd, skb->data, sizeof(smp->rrnd));
skb_pull(skb, sizeof(smp->rrnd));
- return smp_random(smp);
+ if (!test_bit(SMP_FLAG_SC, &smp->flags))
+ return smp_random(smp);
+
+ if (hcon->out) {
+ pkax = smp->local_pk;
+ pkbx = smp->remote_pk;
+ na = smp->prnd;
+ nb = smp->rrnd;
+ } else {
+ pkax = smp->remote_pk;
+ pkbx = smp->local_pk;
+ na = smp->rrnd;
+ nb = smp->prnd;
+ }
+
+ if (smp->method == REQ_OOB) {
+ if (!hcon->out)
+ smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM,
+ sizeof(smp->prnd), smp->prnd);
+ SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK);
+ goto mackey_and_ltk;
+ }
+
+ /* Passkey entry has special treatment */
+ if (smp->method == REQ_PASSKEY || smp->method == DSP_PASSKEY)
+ return sc_passkey_round(smp, SMP_CMD_PAIRING_RANDOM);
+
+ if (hcon->out) {
+ u8 cfm[16];
+
+ err = smp_f4(smp->tfm_cmac, smp->remote_pk, smp->local_pk,
+ smp->rrnd, 0, cfm);
+ if (err)
+ return SMP_UNSPECIFIED;
+
+ if (memcmp(smp->pcnf, cfm, 16))
+ return SMP_CONFIRM_FAILED;
+ } else {
+ smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd),
+ smp->prnd);
+ SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK);
+ }
+
+mackey_and_ltk:
+ /* Generate MacKey and LTK */
+ err = sc_mackey_and_ltk(smp, smp->mackey, smp->tk);
+ if (err)
+ return SMP_UNSPECIFIED;
+
+ if (smp->method == JUST_WORKS || smp->method == REQ_OOB) {
+ if (hcon->out) {
+ sc_dhkey_check(smp);
+ SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK);
+ }
+ return 0;
+ }
+
+ err = smp_g2(smp->tfm_cmac, pkax, pkbx, na, nb, &passkey);
+ if (err)
+ return SMP_UNSPECIFIED;
+
+ err = mgmt_user_confirm_request(hcon->hdev, &hcon->dst, hcon->type,
+ hcon->dst_type, passkey, 0);
+ if (err)
+ return SMP_UNSPECIFIED;
+
+ set_bit(SMP_FLAG_WAIT_USER, &smp->flags);
+
+ return 0;
}
static bool smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
@@ -1112,8 +2025,7 @@
struct smp_ltk *key;
struct hci_conn *hcon = conn->hcon;
- key = hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type,
- hcon->role);
+ key = hci_find_ltk(hcon->hdev, &hcon->dst, hcon->dst_type, hcon->role);
if (!key)
return false;
@@ -1132,20 +2044,21 @@
return true;
}
-bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level)
+bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level,
+ enum smp_key_pref key_pref)
{
if (sec_level == BT_SECURITY_LOW)
return true;
- /* If we're encrypted with an STK always claim insufficient
- * security. This way we allow the connection to be re-encrypted
- * with an LTK, even if the LTK provides the same level of
- * security. Only exception is if we don't have an LTK (e.g.
- * because of key distribution bits).
+ /* If we're encrypted with an STK but the caller prefers using
+ * LTK claim insufficient security. This way we allow the
+ * connection to be re-encrypted with an LTK, even if the LTK
+ * provides the same level of security. Only exception is if we
+ * don't have an LTK (e.g. because of key distribution bits).
*/
- if (test_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags) &&
- hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type,
- hcon->role))
+ if (key_pref == SMP_USE_LTK &&
+ test_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags) &&
+ hci_find_ltk(hcon->hdev, &hcon->dst, hcon->dst_type, hcon->role))
return false;
if (hcon->sec_level >= sec_level)
@@ -1159,6 +2072,7 @@
struct smp_cmd_security_req *rp = (void *) skb->data;
struct smp_cmd_pairing cp;
struct hci_conn *hcon = conn->hcon;
+ struct hci_dev *hdev = hcon->hdev;
struct smp_chan *smp;
u8 sec_level, auth;
@@ -1170,14 +2084,17 @@
if (hcon->role != HCI_ROLE_MASTER)
return SMP_CMD_NOTSUPP;
- auth = rp->auth_req & AUTH_REQ_MASK;
+ auth = rp->auth_req & AUTH_REQ_MASK(hdev);
+
+ if (test_bit(HCI_SC_ONLY, &hdev->dev_flags) && !(auth & SMP_AUTH_SC))
+ return SMP_AUTH_REQUIREMENTS;
if (hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT)
sec_level = BT_SECURITY_MEDIUM;
else
sec_level = authreq_to_seclevel(auth);
- if (smp_sufficient_security(hcon, sec_level))
+ if (smp_sufficient_security(hcon, sec_level, SMP_USE_LTK))
return 0;
if (sec_level > hcon->pending_sec_level)
@@ -1227,7 +2144,7 @@
if (!test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags))
return 1;
- if (smp_sufficient_security(hcon, sec_level))
+ if (smp_sufficient_security(hcon, sec_level, SMP_USE_LTK))
return 1;
if (sec_level > hcon->pending_sec_level)
@@ -1253,6 +2170,9 @@
authreq = seclevel_to_authreq(sec_level);
+ if (test_bit(HCI_SC_ENABLED, &hcon->hdev->dev_flags))
+ authreq |= SMP_AUTH_SC;
+
/* Require MITM if IO Capability allows or the security level
* requires it.
*/
@@ -1329,7 +2249,6 @@
skb_pull(skb, sizeof(*rp));
- hci_dev_lock(hdev);
authenticated = (hcon->sec_level == BT_SECURITY_HIGH);
ltk = hci_add_ltk(hdev, &hcon->dst, hcon->dst_type, SMP_LTK,
authenticated, smp->tk, smp->enc_key_size,
@@ -1337,7 +2256,6 @@
smp->ltk = ltk;
if (!(smp->remote_key_dist & KEY_DIST_MASK))
smp_distribute_keys(smp);
- hci_dev_unlock(hdev);
return 0;
}
@@ -1384,8 +2302,6 @@
skb_pull(skb, sizeof(*info));
- hci_dev_lock(hcon->hdev);
-
/* Strictly speaking the Core Specification (4.1) allows sending
* an empty address which would force us to rely on just the IRK
* as "identity information". However, since such
@@ -1413,8 +2329,6 @@
if (!(smp->remote_key_dist & KEY_DIST_MASK))
smp_distribute_keys(smp);
- hci_dev_unlock(hcon->hdev);
-
return 0;
}
@@ -1423,7 +2337,6 @@
struct smp_cmd_sign_info *rp = (void *) skb->data;
struct l2cap_chan *chan = conn->smp;
struct smp_chan *smp = chan->data;
- struct hci_dev *hdev = conn->hcon->hdev;
struct smp_csrk *csrk;
BT_DBG("conn %p", conn);
@@ -1436,7 +2349,6 @@
skb_pull(skb, sizeof(*rp));
- hci_dev_lock(hdev);
csrk = kzalloc(sizeof(*csrk), GFP_KERNEL);
if (csrk) {
csrk->master = 0x01;
@@ -1444,7 +2356,234 @@
}
smp->csrk = csrk;
smp_distribute_keys(smp);
- hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+static u8 sc_select_method(struct smp_chan *smp)
+{
+ struct l2cap_conn *conn = smp->conn;
+ struct hci_conn *hcon = conn->hcon;
+ struct smp_cmd_pairing *local, *remote;
+ u8 local_mitm, remote_mitm, local_io, remote_io, method;
+
+ if (test_bit(SMP_FLAG_OOB, &smp->flags))
+ return REQ_OOB;
+
+ /* The preq/prsp contain the raw Pairing Request/Response PDUs
+ * which are needed as inputs to some crypto functions. To get
+ * the "struct smp_cmd_pairing" from them we need to skip the
+ * first byte which contains the opcode.
+ */
+ if (hcon->out) {
+ local = (void *) &smp->preq[1];
+ remote = (void *) &smp->prsp[1];
+ } else {
+ local = (void *) &smp->prsp[1];
+ remote = (void *) &smp->preq[1];
+ }
+
+ local_io = local->io_capability;
+ remote_io = remote->io_capability;
+
+ local_mitm = (local->auth_req & SMP_AUTH_MITM);
+ remote_mitm = (remote->auth_req & SMP_AUTH_MITM);
+
+ /* If either side wants MITM, look up the method from the table,
+ * otherwise use JUST WORKS.
+ */
+ if (local_mitm || remote_mitm)
+ method = get_auth_method(smp, local_io, remote_io);
+ else
+ method = JUST_WORKS;
+
+ /* Don't confirm locally initiated pairing attempts */
+ if (method == JUST_CFM && test_bit(SMP_FLAG_INITIATOR, &smp->flags))
+ method = JUST_WORKS;
+
+ return method;
+}
+
+static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+ struct smp_cmd_public_key *key = (void *) skb->data;
+ struct hci_conn *hcon = conn->hcon;
+ struct l2cap_chan *chan = conn->smp;
+ struct smp_chan *smp = chan->data;
+ struct hci_dev *hdev = hcon->hdev;
+ struct smp_cmd_pairing_confirm cfm;
+ int err;
+
+ BT_DBG("conn %p", conn);
+
+ if (skb->len < sizeof(*key))
+ return SMP_INVALID_PARAMS;
+
+ memcpy(smp->remote_pk, key, 64);
+
+ /* Non-initiating device sends its public key after receiving
+ * the key from the initiating device.
+ */
+ if (!hcon->out) {
+ err = sc_send_public_key(smp);
+ if (err)
+ return err;
+ }
+
+ SMP_DBG("Remote Public Key X: %32phN", smp->remote_pk);
+ SMP_DBG("Remote Public Key Y: %32phN", &smp->remote_pk[32]);
+
+ if (!ecdh_shared_secret(smp->remote_pk, smp->local_sk, smp->dhkey))
+ return SMP_UNSPECIFIED;
+
+ SMP_DBG("DHKey %32phN", smp->dhkey);
+
+ set_bit(SMP_FLAG_REMOTE_PK, &smp->flags);
+
+ smp->method = sc_select_method(smp);
+
+ BT_DBG("%s selected method 0x%02x", hdev->name, smp->method);
+
+ /* JUST_WORKS and JUST_CFM result in an unauthenticated key */
+ if (smp->method == JUST_WORKS || smp->method == JUST_CFM)
+ hcon->pending_sec_level = BT_SECURITY_MEDIUM;
+ else
+ hcon->pending_sec_level = BT_SECURITY_FIPS;
+
+ if (!memcmp(debug_pk, smp->remote_pk, 64))
+ set_bit(SMP_FLAG_DEBUG_KEY, &smp->flags);
+
+ if (smp->method == DSP_PASSKEY) {
+ get_random_bytes(&hcon->passkey_notify,
+ sizeof(hcon->passkey_notify));
+ hcon->passkey_notify %= 1000000;
+ hcon->passkey_entered = 0;
+ smp->passkey_round = 0;
+ if (mgmt_user_passkey_notify(hdev, &hcon->dst, hcon->type,
+ hcon->dst_type,
+ hcon->passkey_notify,
+ hcon->passkey_entered))
+ return SMP_UNSPECIFIED;
+ SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+ return sc_passkey_round(smp, SMP_CMD_PUBLIC_KEY);
+ }
+
+ if (smp->method == REQ_OOB) {
+ err = smp_f4(smp->tfm_cmac, smp->remote_pk, smp->remote_pk,
+ smp->rr, 0, cfm.confirm_val);
+ if (err)
+ return SMP_UNSPECIFIED;
+
+ if (memcmp(cfm.confirm_val, smp->pcnf, 16))
+ return SMP_CONFIRM_FAILED;
+
+ if (hcon->out)
+ smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM,
+ sizeof(smp->prnd), smp->prnd);
+
+ SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_RANDOM);
+
+ return 0;
+ }
+
+ if (hcon->out)
+ SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+
+ if (smp->method == REQ_PASSKEY) {
+ if (mgmt_user_passkey_request(hdev, &hcon->dst, hcon->type,
+ hcon->dst_type))
+ return SMP_UNSPECIFIED;
+ SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+ set_bit(SMP_FLAG_WAIT_USER, &smp->flags);
+ return 0;
+ }
+
+ /* The Initiating device waits for the non-initiating device to
+ * send the confirm value.
+ */
+ if (conn->hcon->out)
+ return 0;
+
+ err = smp_f4(smp->tfm_cmac, smp->local_pk, smp->remote_pk, smp->prnd,
+ 0, cfm.confirm_val);
+ if (err)
+ return SMP_UNSPECIFIED;
+
+ smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cfm), &cfm);
+ SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_RANDOM);
+
+ return 0;
+}
+
+static int smp_cmd_dhkey_check(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+ struct smp_cmd_dhkey_check *check = (void *) skb->data;
+ struct l2cap_chan *chan = conn->smp;
+ struct hci_conn *hcon = conn->hcon;
+ struct smp_chan *smp = chan->data;
+ u8 a[7], b[7], *local_addr, *remote_addr;
+ u8 io_cap[3], r[16], e[16];
+ int err;
+
+ BT_DBG("conn %p", conn);
+
+ if (skb->len < sizeof(*check))
+ return SMP_INVALID_PARAMS;
+
+ memcpy(a, &hcon->init_addr, 6);
+ memcpy(b, &hcon->resp_addr, 6);
+ a[6] = hcon->init_addr_type;
+ b[6] = hcon->resp_addr_type;
+
+ if (hcon->out) {
+ local_addr = a;
+ remote_addr = b;
+ memcpy(io_cap, &smp->prsp[1], 3);
+ } else {
+ local_addr = b;
+ remote_addr = a;
+ memcpy(io_cap, &smp->preq[1], 3);
+ }
+
+ memset(r, 0, sizeof(r));
+
+ if (smp->method == REQ_PASSKEY || smp->method == DSP_PASSKEY)
+ put_unaligned_le32(hcon->passkey_notify, r);
+
+ err = smp_f6(smp->tfm_cmac, smp->mackey, smp->rrnd, smp->prnd, r,
+ io_cap, remote_addr, local_addr, e);
+ if (err)
+ return SMP_UNSPECIFIED;
+
+ if (memcmp(check->e, e, 16))
+ return SMP_DHKEY_CHECK_FAILED;
+
+ if (!hcon->out) {
+ if (test_bit(SMP_FLAG_WAIT_USER, &smp->flags)) {
+ set_bit(SMP_FLAG_DHKEY_PENDING, &smp->flags);
+ return 0;
+ }
+
+ /* Slave sends DHKey check as response to master */
+ sc_dhkey_check(smp);
+ }
+
+ sc_add_ltk(smp);
+
+ if (hcon->out) {
+ hci_le_start_enc(hcon, 0, 0, smp->tk);
+ hcon->enc_key_size = smp->enc_key_size;
+ }
+
+ return 0;
+}
+
+static int smp_cmd_keypress_notify(struct l2cap_conn *conn,
+ struct sk_buff *skb)
+{
+ struct smp_cmd_keypress_notify *kp = (void *) skb->data;
+
+ BT_DBG("value 0x%02x", kp->value);
return 0;
}
@@ -1457,11 +2596,6 @@
__u8 code, reason;
int err = 0;
- if (hcon->type != LE_LINK) {
- kfree_skb(skb);
- return 0;
- }
-
if (skb->len < 1)
return -EILSEQ;
@@ -1533,6 +2667,18 @@
reason = smp_cmd_sign_info(conn, skb);
break;
+ case SMP_CMD_PUBLIC_KEY:
+ reason = smp_cmd_public_key(conn, skb);
+ break;
+
+ case SMP_CMD_DHKEY_CHECK:
+ reason = smp_cmd_dhkey_check(conn, skb);
+ break;
+
+ case SMP_CMD_KEYPRESS_NOTIFY:
+ reason = smp_cmd_keypress_notify(conn, skb);
+ break;
+
default:
BT_DBG("Unknown command code 0x%2.2x", code);
reason = SMP_CMD_NOTSUPP;
@@ -1568,6 +2714,74 @@
l2cap_chan_put(chan);
}
+static void bredr_pairing(struct l2cap_chan *chan)
+{
+ struct l2cap_conn *conn = chan->conn;
+ struct hci_conn *hcon = conn->hcon;
+ struct hci_dev *hdev = hcon->hdev;
+ struct smp_cmd_pairing req;
+ struct smp_chan *smp;
+
+ BT_DBG("chan %p", chan);
+
+ /* Only new pairings are interesting */
+ if (!test_bit(HCI_CONN_NEW_LINK_KEY, &hcon->flags))
+ return;
+
+ /* Don't bother if we're not encrypted */
+ if (!test_bit(HCI_CONN_ENCRYPT, &hcon->flags))
+ return;
+
+ /* Only master may initiate SMP over BR/EDR */
+ if (hcon->role != HCI_ROLE_MASTER)
+ return;
+
+ /* Secure Connections support must be enabled */
+ if (!test_bit(HCI_SC_ENABLED, &hdev->dev_flags))
+ return;
+
+ /* BR/EDR must use Secure Connections for SMP */
+ if (!test_bit(HCI_CONN_AES_CCM, &hcon->flags) &&
+ !test_bit(HCI_FORCE_LESC, &hdev->dbg_flags))
+ return;
+
+ /* If our LE support is not enabled don't do anything */
+ if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+ return;
+
+ /* Don't bother if remote LE support is not enabled */
+ if (!lmp_host_le_capable(hcon))
+ return;
+
+ /* Remote must support SMP fixed chan for BR/EDR */
+ if (!(conn->remote_fixed_chan & L2CAP_FC_SMP_BREDR))
+ return;
+
+ /* Don't bother if SMP is already ongoing */
+ if (chan->data)
+ return;
+
+ smp = smp_chan_create(conn);
+ if (!smp) {
+ BT_ERR("%s unable to create SMP context for BR/EDR",
+ hdev->name);
+ return;
+ }
+
+ set_bit(SMP_FLAG_SC, &smp->flags);
+
+ BT_DBG("%s starting SMP over BR/EDR", hdev->name);
+
+ /* Prepare and send the BR/EDR SMP Pairing Request */
+ build_bredr_pairing_cmd(smp, &req, NULL);
+
+ smp->preq[0] = SMP_CMD_PAIRING_REQ;
+ memcpy(&smp->preq[1], &req, sizeof(req));
+
+ smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(req), &req);
+ SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_RSP);
+}
+
static void smp_resume_cb(struct l2cap_chan *chan)
{
struct smp_chan *smp = chan->data;
@@ -1576,6 +2790,11 @@
BT_DBG("chan %p", chan);
+ if (hcon->type == ACL_LINK) {
+ bredr_pairing(chan);
+ return;
+ }
+
if (!smp)
return;
@@ -1590,11 +2809,15 @@
static void smp_ready_cb(struct l2cap_chan *chan)
{
struct l2cap_conn *conn = chan->conn;
+ struct hci_conn *hcon = conn->hcon;
BT_DBG("chan %p", chan);
conn->smp = chan;
l2cap_chan_hold(chan);
+
+ if (hcon->type == ACL_LINK && test_bit(HCI_CONN_ENCRYPT, &hcon->flags))
+ bredr_pairing(chan);
}
static int smp_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
@@ -1668,6 +2891,13 @@
chan->omtu = pchan->omtu;
chan->mode = pchan->mode;
+ /* Other L2CAP channels may request SMP routines in order to
+ * change the security level. This means that the SMP channel
+ * lock must be considered in its own category to avoid lockdep
+ * warnings.
+ */
+ atomic_set(&chan->nesting, L2CAP_NESTING_SMP);
+
BT_DBG("created chan %p", chan);
return chan;
@@ -1692,53 +2922,56 @@
.memcpy_fromiovec = l2cap_chan_no_memcpy_fromiovec,
};
-int smp_register(struct hci_dev *hdev)
+static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid)
{
struct l2cap_chan *chan;
struct crypto_blkcipher *tfm_aes;
- BT_DBG("%s", hdev->name);
-
- tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
- if (IS_ERR(tfm_aes)) {
- int err = PTR_ERR(tfm_aes);
- BT_ERR("Unable to create crypto context");
- return err;
+ if (cid == L2CAP_CID_SMP_BREDR) {
+ tfm_aes = NULL;
+ goto create_chan;
}
+ tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, 0);
+ if (IS_ERR(tfm_aes)) {
+ BT_ERR("Unable to create crypto context");
+ return ERR_CAST(tfm_aes);
+ }
+
+create_chan:
chan = l2cap_chan_create();
if (!chan) {
crypto_free_blkcipher(tfm_aes);
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
}
chan->data = tfm_aes;
- l2cap_add_scid(chan, L2CAP_CID_SMP);
+ l2cap_add_scid(chan, cid);
l2cap_chan_set_defaults(chan);
bacpy(&chan->src, &hdev->bdaddr);
- chan->src_type = BDADDR_LE_PUBLIC;
+ if (cid == L2CAP_CID_SMP)
+ chan->src_type = BDADDR_LE_PUBLIC;
+ else
+ chan->src_type = BDADDR_BREDR;
chan->state = BT_LISTEN;
chan->mode = L2CAP_MODE_BASIC;
chan->imtu = L2CAP_DEFAULT_MTU;
chan->ops = &smp_root_chan_ops;
- hdev->smp_data = chan;
+ /* Set correct nesting level for a parent/listening channel */
+ atomic_set(&chan->nesting, L2CAP_NESTING_PARENT);
- return 0;
+ return chan;
}
-void smp_unregister(struct hci_dev *hdev)
+static void smp_del_chan(struct l2cap_chan *chan)
{
- struct l2cap_chan *chan = hdev->smp_data;
- struct crypto_blkcipher *tfm_aes;
+ struct crypto_blkcipher *tfm_aes;
- if (!chan)
- return;
-
- BT_DBG("%s chan %p", hdev->name, chan);
+ BT_DBG("chan %p", chan);
tfm_aes = chan->data;
if (tfm_aes) {
@@ -1746,6 +2979,52 @@
crypto_free_blkcipher(tfm_aes);
}
- hdev->smp_data = NULL;
l2cap_chan_put(chan);
}
+
+int smp_register(struct hci_dev *hdev)
+{
+ struct l2cap_chan *chan;
+
+ BT_DBG("%s", hdev->name);
+
+ chan = smp_add_cid(hdev, L2CAP_CID_SMP);
+ if (IS_ERR(chan))
+ return PTR_ERR(chan);
+
+ hdev->smp_data = chan;
+
+ if (!lmp_sc_capable(hdev) &&
+ !test_bit(HCI_FORCE_LESC, &hdev->dbg_flags))
+ return 0;
+
+ chan = smp_add_cid(hdev, L2CAP_CID_SMP_BREDR);
+ if (IS_ERR(chan)) {
+ int err = PTR_ERR(chan);
+ chan = hdev->smp_data;
+ hdev->smp_data = NULL;
+ smp_del_chan(chan);
+ return err;
+ }
+
+ hdev->smp_bredr_data = chan;
+
+ return 0;
+}
+
+void smp_unregister(struct hci_dev *hdev)
+{
+ struct l2cap_chan *chan;
+
+ if (hdev->smp_bredr_data) {
+ chan = hdev->smp_bredr_data;
+ hdev->smp_bredr_data = NULL;
+ smp_del_chan(chan);
+ }
+
+ if (hdev->smp_data) {
+ chan = hdev->smp_data;
+ hdev->smp_data = NULL;
+ smp_del_chan(chan);
+ }
+}
diff --git a/net/bluetooth/smp.h b/net/bluetooth/smp.h
index 86a683a..3296bf4 100644
--- a/net/bluetooth/smp.h
+++ b/net/bluetooth/smp.h
@@ -50,10 +50,13 @@
#define SMP_DIST_ENC_KEY 0x01
#define SMP_DIST_ID_KEY 0x02
#define SMP_DIST_SIGN 0x04
+#define SMP_DIST_LINK_KEY 0x08
#define SMP_AUTH_NONE 0x00
#define SMP_AUTH_BONDING 0x01
#define SMP_AUTH_MITM 0x04
+#define SMP_AUTH_SC 0x08
+#define SMP_AUTH_KEYPRESS 0x10
#define SMP_CMD_PAIRING_CONFIRM 0x03
struct smp_cmd_pairing_confirm {
@@ -102,7 +105,23 @@
__u8 auth_req;
} __packed;
-#define SMP_CMD_MAX 0x0b
+#define SMP_CMD_PUBLIC_KEY 0x0c
+struct smp_cmd_public_key {
+ __u8 x[32];
+ __u8 y[32];
+} __packed;
+
+#define SMP_CMD_DHKEY_CHECK 0x0d
+struct smp_cmd_dhkey_check {
+ __u8 e[16];
+} __packed;
+
+#define SMP_CMD_KEYPRESS_NOTIFY 0x0e
+struct smp_cmd_keypress_notify {
+ __u8 value;
+} __packed;
+
+#define SMP_CMD_MAX 0x0e
#define SMP_PASSKEY_ENTRY_FAILED 0x01
#define SMP_OOB_NOT_AVAIL 0x02
@@ -114,6 +133,10 @@
#define SMP_UNSPECIFIED 0x08
#define SMP_REPEATED_ATTEMPTS 0x09
#define SMP_INVALID_PARAMS 0x0a
+#define SMP_DHKEY_CHECK_FAILED 0x0b
+#define SMP_NUMERIC_COMP_FAILED 0x0c
+#define SMP_BREDR_PAIRING_IN_PROGRESS 0x0d
+#define SMP_CROSS_TRANSP_NOT_ALLOWED 0x0e
#define SMP_MIN_ENC_KEY_SIZE 7
#define SMP_MAX_ENC_KEY_SIZE 16
@@ -123,23 +146,48 @@
SMP_STK,
SMP_LTK,
SMP_LTK_SLAVE,
+ SMP_LTK_P256,
+ SMP_LTK_P256_DEBUG,
};
+static inline bool smp_ltk_is_sc(struct smp_ltk *key)
+{
+ switch (key->type) {
+ case SMP_LTK_P256:
+ case SMP_LTK_P256_DEBUG:
+ return true;
+ }
+
+ return false;
+}
+
static inline u8 smp_ltk_sec_level(struct smp_ltk *key)
{
- if (key->authenticated)
- return BT_SECURITY_HIGH;
+ if (key->authenticated) {
+ if (smp_ltk_is_sc(key))
+ return BT_SECURITY_FIPS;
+ else
+ return BT_SECURITY_HIGH;
+ }
return BT_SECURITY_MEDIUM;
}
+/* Key preferences for smp_sufficient security */
+enum smp_key_pref {
+ SMP_ALLOW_STK,
+ SMP_USE_LTK,
+};
+
/* SMP Commands */
-bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level);
+bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level,
+ enum smp_key_pref key_pref);
int smp_conn_security(struct hci_conn *hcon, __u8 sec_level);
int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey);
-bool smp_irk_matches(struct hci_dev *hdev, u8 irk[16], bdaddr_t *bdaddr);
-int smp_generate_rpa(struct hci_dev *hdev, u8 irk[16], bdaddr_t *rpa);
+bool smp_irk_matches(struct hci_dev *hdev, const u8 irk[16],
+ const bdaddr_t *bdaddr);
+int smp_generate_rpa(struct hci_dev *hdev, const u8 irk[16], bdaddr_t *rpa);
int smp_register(struct hci_dev *hdev);
void smp_unregister(struct hci_dev *hdev);
diff --git a/net/ieee802154/6lowpan_rtnl.c b/net/ieee802154/6lowpan_rtnl.c
index 69cbc62..dc34d84 100644
--- a/net/ieee802154/6lowpan_rtnl.c
+++ b/net/ieee802154/6lowpan_rtnl.c
@@ -49,8 +49,8 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/netdevice.h>
+#include <linux/ieee802154.h>
#include <net/af_ieee802154.h>
-#include <net/ieee802154.h>
#include <net/ieee802154_netdev.h>
#include <net/6lowpan.h>
#include <net/ipv6.h>
@@ -58,12 +58,13 @@
#include "reassembly.h"
static LIST_HEAD(lowpan_devices);
+static int lowpan_open_count;
/* private device info */
struct lowpan_dev_info {
struct net_device *real_dev; /* real WPAN device ptr */
struct mutex dev_list_mtx; /* mutex for list ops */
- __be16 fragment_tag;
+ u16 fragment_tag;
};
struct lowpan_dev_record {
@@ -140,24 +141,33 @@
struct sk_buff *skb_cp;
int stat = NET_RX_SUCCESS;
+ skb->protocol = htons(ETH_P_IPV6);
+ skb->pkt_type = PACKET_HOST;
+
rcu_read_lock();
list_for_each_entry_rcu(entry, &lowpan_devices, list)
if (lowpan_dev_info(entry->ldev)->real_dev == skb->dev) {
skb_cp = skb_copy(skb, GFP_ATOMIC);
if (!skb_cp) {
- stat = -ENOMEM;
- break;
+ kfree_skb(skb);
+ rcu_read_unlock();
+ return NET_RX_DROP;
}
skb_cp->dev = entry->ldev;
stat = netif_rx(skb_cp);
+ if (stat == NET_RX_DROP)
+ break;
}
rcu_read_unlock();
+ consume_skb(skb);
+
return stat;
}
-static int process_data(struct sk_buff *skb, const struct ieee802154_hdr *hdr)
+static int
+iphc_decompress(struct sk_buff *skb, const struct ieee802154_hdr *hdr)
{
u8 iphc0, iphc1;
struct ieee802154_addr_sa sa, da;
@@ -166,13 +176,13 @@
raw_dump_table(__func__, "raw skb data dump", skb->data, skb->len);
/* at least two bytes will be used for the encoding */
if (skb->len < 2)
- goto drop;
+ return -EINVAL;
if (lowpan_fetch_skb_u8(skb, &iphc0))
- goto drop;
+ return -EINVAL;
if (lowpan_fetch_skb_u8(skb, &iphc1))
- goto drop;
+ return -EINVAL;
ieee802154_addr_to_sa(&sa, &hdr->source);
ieee802154_addr_to_sa(&da, &hdr->dest);
@@ -187,27 +197,9 @@
else
dap = &da.hwaddr;
- return lowpan_process_data(skb, skb->dev, sap, sa.addr_type,
- IEEE802154_ADDR_LEN, dap, da.addr_type,
- IEEE802154_ADDR_LEN, iphc0, iphc1,
- lowpan_give_skb_to_devices);
-
-drop:
- kfree_skb(skb);
- return -EINVAL;
-}
-
-static int lowpan_set_address(struct net_device *dev, void *p)
-{
- struct sockaddr *sa = p;
-
- if (netif_running(dev))
- return -EBUSY;
-
- /* TODO: validate addr */
- memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
-
- return 0;
+ return lowpan_header_decompress(skb, skb->dev, sap, sa.addr_type,
+ IEEE802154_ADDR_LEN, dap, da.addr_type,
+ IEEE802154_ADDR_LEN, iphc0, iphc1);
}
static struct sk_buff*
@@ -233,7 +225,7 @@
&master_hdr->source, size);
if (rc < 0) {
kfree_skb(frag);
- return ERR_PTR(-rc);
+ return ERR_PTR(rc);
}
} else {
frag = ERR_PTR(-ENOMEM);
@@ -275,7 +267,8 @@
dgram_size = lowpan_uncompress_size(skb, &dgram_offset) -
skb->mac_len;
- frag_tag = lowpan_dev_info(dev)->fragment_tag++;
+ frag_tag = htons(lowpan_dev_info(dev)->fragment_tag);
+ lowpan_dev_info(dev)->fragment_tag++;
frag_hdr[0] = LOWPAN_DISPATCH_FRAG1 | ((dgram_size >> 8) & 0x07);
frag_hdr[1] = dgram_size & 0xff;
@@ -294,7 +287,7 @@
frag_len + skb_network_header_len(skb));
if (rc) {
pr_debug("%s unable to send FRAG1 packet (tag: %d)",
- __func__, frag_tag);
+ __func__, ntohs(frag_tag));
goto err;
}
@@ -315,7 +308,7 @@
frag_len);
if (rc) {
pr_debug("%s unable to send a FRAGN packet. (tag: %d, offset: %d)\n",
- __func__, frag_tag, skb_offset);
+ __func__, ntohs(frag_tag), skb_offset);
goto err;
}
} while (skb_unprocessed > frag_cap);
@@ -410,13 +403,6 @@
}
}
-static struct wpan_phy *lowpan_get_phy(const struct net_device *dev)
-{
- struct net_device *real_dev = lowpan_dev_info(dev)->real_dev;
-
- return ieee802154_mlme_ops(real_dev)->get_phy(real_dev);
-}
-
static __le16 lowpan_get_pan_id(const struct net_device *dev)
{
struct net_device *real_dev = lowpan_dev_info(dev)->real_dev;
@@ -455,7 +441,6 @@
&lowpan_netdev_xmit_lock_key);
}
-
static int lowpan_dev_init(struct net_device *dev)
{
netdev_for_each_tx_queue(dev, lowpan_set_lockdep_class_one, NULL);
@@ -468,12 +453,10 @@
static const struct net_device_ops lowpan_netdev_ops = {
.ndo_init = lowpan_dev_init,
.ndo_start_xmit = lowpan_xmit,
- .ndo_set_mac_address = lowpan_set_address,
};
static struct ieee802154_mlme_ops lowpan_mlme = {
.get_pan_id = lowpan_get_pan_id,
- .get_phy = lowpan_get_phy,
.get_short_addr = lowpan_get_short_addr,
.get_dsn = lowpan_get_dsn,
};
@@ -519,6 +502,9 @@
if (!netif_running(dev))
goto drop_skb;
+ if (skb->pkt_type == PACKET_OTHERHOST)
+ goto drop_skb;
+
if (dev->type != ARPHRD_IEEE802154)
goto drop_skb;
@@ -527,55 +513,67 @@
/* check that it's our buffer */
if (skb->data[0] == LOWPAN_DISPATCH_IPV6) {
- skb->protocol = htons(ETH_P_IPV6);
- skb->pkt_type = PACKET_HOST;
-
/* Pull off the 1-byte of 6lowpan header. */
skb_pull(skb, 1);
-
- ret = lowpan_give_skb_to_devices(skb, NULL);
- if (ret == NET_RX_DROP)
- goto drop;
+ return lowpan_give_skb_to_devices(skb, NULL);
} else {
switch (skb->data[0] & 0xe0) {
case LOWPAN_DISPATCH_IPHC: /* ipv6 datagram */
- ret = process_data(skb, &hdr);
- if (ret == NET_RX_DROP)
- goto drop;
- break;
+ ret = iphc_decompress(skb, &hdr);
+ if (ret < 0)
+ goto drop_skb;
+
+ return lowpan_give_skb_to_devices(skb, NULL);
case LOWPAN_DISPATCH_FRAG1: /* first fragment header */
ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAG1);
if (ret == 1) {
- ret = process_data(skb, &hdr);
- if (ret == NET_RX_DROP)
- goto drop;
+ ret = iphc_decompress(skb, &hdr);
+ if (ret < 0)
+ goto drop_skb;
+
+ return lowpan_give_skb_to_devices(skb, NULL);
+ } else if (ret == -1) {
+ return NET_RX_DROP;
+ } else {
+ return NET_RX_SUCCESS;
}
- break;
case LOWPAN_DISPATCH_FRAGN: /* next fragments headers */
ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAGN);
if (ret == 1) {
- ret = process_data(skb, &hdr);
- if (ret == NET_RX_DROP)
- goto drop;
+ ret = iphc_decompress(skb, &hdr);
+ if (ret < 0)
+ goto drop_skb;
+
+ return lowpan_give_skb_to_devices(skb, NULL);
+ } else if (ret == -1) {
+ return NET_RX_DROP;
+ } else {
+ return NET_RX_SUCCESS;
}
- break;
default:
break;
}
}
- return NET_RX_SUCCESS;
drop_skb:
kfree_skb(skb);
drop:
return NET_RX_DROP;
}
+static struct packet_type lowpan_packet_type = {
+ .type = htons(ETH_P_IEEE802154),
+ .func = lowpan_rcv,
+};
+
static int lowpan_newlink(struct net *src_net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[])
{
struct net_device *real_dev;
struct lowpan_dev_record *entry;
+ int ret;
+
+ ASSERT_RTNL();
pr_debug("adding new link\n");
@@ -602,7 +600,7 @@
entry->ldev = dev;
- /* Set the lowpan harware address to the wpan hardware address. */
+ /* Set the lowpan hardware address to the wpan hardware address. */
memcpy(dev->dev_addr, real_dev->dev_addr, IEEE802154_ADDR_LEN);
mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx);
@@ -610,9 +608,14 @@
list_add_tail(&entry->list, &lowpan_devices);
mutex_unlock(&lowpan_dev_info(dev)->dev_list_mtx);
- register_netdevice(dev);
+ ret = register_netdevice(dev);
+ if (ret >= 0) {
+ if (!lowpan_open_count)
+ dev_add_pack(&lowpan_packet_type);
+ lowpan_open_count++;
+ }
- return 0;
+ return ret;
}
static void lowpan_dellink(struct net_device *dev, struct list_head *head)
@@ -623,6 +626,10 @@
ASSERT_RTNL();
+ lowpan_open_count--;
+ if (!lowpan_open_count)
+ dev_remove_pack(&lowpan_packet_type);
+
mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx);
list_for_each_entry_safe(entry, tmp, &lowpan_devices, list) {
if (entry->ldev == dev) {
@@ -685,11 +692,6 @@
.notifier_call = lowpan_device_event,
};
-static struct packet_type lowpan_packet_type = {
- .type = htons(ETH_P_IEEE802154),
- .func = lowpan_rcv,
-};
-
static int __init lowpan_init_module(void)
{
int err = 0;
@@ -702,8 +704,6 @@
if (err < 0)
goto out_frag;
- dev_add_pack(&lowpan_packet_type);
-
err = register_netdevice_notifier(&lowpan_dev_notifier);
if (err < 0)
goto out_pack;
@@ -711,7 +711,6 @@
return 0;
out_pack:
- dev_remove_pack(&lowpan_packet_type);
lowpan_netlink_fini();
out_frag:
lowpan_net_frag_exit();
@@ -723,8 +722,6 @@
{
lowpan_netlink_fini();
- dev_remove_pack(&lowpan_packet_type);
-
lowpan_net_frag_exit();
unregister_netdevice_notifier(&lowpan_dev_notifier);
diff --git a/net/ieee802154/Kconfig b/net/ieee802154/Kconfig
index 24fe2a5..c509e93 100644
--- a/net/ieee802154/Kconfig
+++ b/net/ieee802154/Kconfig
@@ -1,5 +1,5 @@
config IEEE802154
- depends on !BACKPORT_KERNEL_3_1
+ depends on !KERNEL_3_19
tristate "IEEE Std 802.15.4 Low-Rate Wireless Personal Area Networks support"
depends on m
---help---
@@ -13,7 +13,7 @@
compile it as modules.
config IEEE802154_6LOWPAN
- depends on !BACKPORT_KERNEL_3_5
+ depends on !KERNEL_3_5
tristate "6lowpan support over IEEE 802.15.4"
depends on m
depends on IEEE802154 && 6LOWPAN
diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile
index 57fdfef..9e6d3d6 100644
--- a/net/ieee802154/Makefile
+++ b/net/ieee802154/Makefile
@@ -2,8 +2,8 @@
obj-$(CPTCFG_IEEE802154_6LOWPAN) += ieee802154_6lowpan.o
ieee802154_6lowpan-y := 6lowpan_rtnl.o reassembly.o
-ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o wpan-class.o \
- header_ops.o
+ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o core.o \
+ header_ops.o sysfs.o nl802154.o
af_802154-y := af_ieee802154.o raw.o dgram.o
ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/ieee802154/af802154.h b/net/ieee802154/af802154.h
index 8330a09..343b63e 100644
--- a/net/ieee802154/af802154.h
+++ b/net/ieee802154/af802154.h
@@ -12,10 +12,6 @@
* 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.
- *
* Written by:
* Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
diff --git a/net/ieee802154/af_ieee802154.c b/net/ieee802154/af_ieee802154.c
index 29e0de6..d0a1282 100644
--- a/net/ieee802154/af_ieee802154.c
+++ b/net/ieee802154/af_ieee802154.c
@@ -12,10 +12,6 @@
* 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.
- *
* Written by:
* Sergey Lapin <slapin@ossfans.org>
* Maxim Gorbachyov <maxim.gorbachev@siemens.com>
@@ -103,6 +99,7 @@
}
return 0;
}
+
static int ieee802154_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t len)
{
@@ -235,7 +232,6 @@
#endif
};
-
/* Create a socket. Initialise the socket, blank the addresses
* set the state.
*/
@@ -324,7 +320,6 @@
return NET_RX_DROP;
}
-
static struct packet_type ieee802154_packet_type = {
.type = htons(ETH_P_IEEE802154),
.func = ieee802154_rcv,
@@ -358,6 +353,7 @@
out:
return rc;
}
+
static void __exit af_ieee802154_remove(void)
{
dev_remove_pack(&ieee802154_packet_type);
diff --git a/net/ieee802154/core.c b/net/ieee802154/core.c
new file mode 100644
index 0000000..18bc7e7
--- /dev/null
+++ b/net/ieee802154/core.c
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2007, 2008, 2009 Siemens AG
+ *
+ * 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.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+
+#include <net/cfg802154.h>
+#include <net/rtnetlink.h>
+
+#include "ieee802154.h"
+#include "nl802154.h"
+#include "sysfs.h"
+#include "core.h"
+
+/* RCU-protected (and RTNL for writers) */
+LIST_HEAD(cfg802154_rdev_list);
+int cfg802154_rdev_list_generation;
+
+static int wpan_phy_match(struct device *dev, const void *data)
+{
+ return !strcmp(dev_name(dev), (const char *)data);
+}
+
+struct wpan_phy *wpan_phy_find(const char *str)
+{
+ struct device *dev;
+
+ if (WARN_ON(!str))
+ return NULL;
+
+ dev = class_find_device(&wpan_phy_class, NULL, str, wpan_phy_match);
+ if (!dev)
+ return NULL;
+
+ return container_of(dev, struct wpan_phy, dev);
+}
+EXPORT_SYMBOL(wpan_phy_find);
+
+struct wpan_phy_iter_data {
+ int (*fn)(struct wpan_phy *phy, void *data);
+ void *data;
+};
+
+static int wpan_phy_iter(struct device *dev, void *_data)
+{
+ struct wpan_phy_iter_data *wpid = _data;
+ struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev);
+
+ return wpid->fn(phy, wpid->data);
+}
+
+int wpan_phy_for_each(int (*fn)(struct wpan_phy *phy, void *data),
+ void *data)
+{
+ struct wpan_phy_iter_data wpid = {
+ .fn = fn,
+ .data = data,
+ };
+
+ return class_for_each_device(&wpan_phy_class, NULL,
+ &wpid, wpan_phy_iter);
+}
+EXPORT_SYMBOL(wpan_phy_for_each);
+
+struct cfg802154_registered_device *
+cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx)
+{
+ struct cfg802154_registered_device *result = NULL, *rdev;
+
+ ASSERT_RTNL();
+
+ list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
+ if (rdev->wpan_phy_idx == wpan_phy_idx) {
+ result = rdev;
+ break;
+ }
+ }
+
+ return result;
+}
+
+struct wpan_phy *
+wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size)
+{
+ static atomic_t wpan_phy_counter = ATOMIC_INIT(0);
+ struct cfg802154_registered_device *rdev;
+ size_t alloc_size;
+
+ alloc_size = sizeof(*rdev) + priv_size;
+ rdev = kzalloc(alloc_size, GFP_KERNEL);
+ if (!rdev)
+ return NULL;
+
+ rdev->ops = ops;
+
+ rdev->wpan_phy_idx = atomic_inc_return(&wpan_phy_counter);
+
+ if (unlikely(rdev->wpan_phy_idx < 0)) {
+ /* ugh, wrapped! */
+ atomic_dec(&wpan_phy_counter);
+ kfree(rdev);
+ return NULL;
+ }
+
+ /* atomic_inc_return makes it start at 1, make it start at 0 */
+ rdev->wpan_phy_idx--;
+
+ mutex_init(&rdev->wpan_phy.pib_lock);
+
+ INIT_LIST_HEAD(&rdev->wpan_dev_list);
+ device_initialize(&rdev->wpan_phy.dev);
+ dev_set_name(&rdev->wpan_phy.dev, "wpan-phy%d", rdev->wpan_phy_idx);
+
+ rdev->wpan_phy.dev.class = &wpan_phy_class;
+ rdev->wpan_phy.dev.platform_data = rdev;
+
+ init_waitqueue_head(&rdev->dev_wait);
+
+ return &rdev->wpan_phy;
+}
+EXPORT_SYMBOL(wpan_phy_new);
+
+int wpan_phy_register(struct wpan_phy *phy)
+{
+ struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy);
+ int ret;
+
+ rtnl_lock();
+ ret = device_add(&phy->dev);
+ if (ret) {
+ rtnl_unlock();
+ return ret;
+ }
+
+ list_add_rcu(&rdev->list, &cfg802154_rdev_list);
+ cfg802154_rdev_list_generation++;
+
+ /* TODO phy registered lock */
+ rtnl_unlock();
+
+ /* TODO nl802154 phy notify */
+
+ return 0;
+}
+EXPORT_SYMBOL(wpan_phy_register);
+
+void wpan_phy_unregister(struct wpan_phy *phy)
+{
+ struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy);
+
+ wait_event(rdev->dev_wait, ({
+ int __count;
+ rtnl_lock();
+ __count = rdev->opencount;
+ rtnl_unlock();
+ __count == 0; }));
+
+ rtnl_lock();
+ /* TODO nl802154 phy notify */
+ /* TODO phy registered lock */
+
+ WARN_ON(!list_empty(&rdev->wpan_dev_list));
+
+ /* First remove the hardware from everywhere, this makes
+ * it impossible to find from userspace.
+ */
+ list_del_rcu(&rdev->list);
+ synchronize_rcu();
+
+ cfg802154_rdev_list_generation++;
+
+ device_del(&phy->dev);
+
+ rtnl_unlock();
+}
+EXPORT_SYMBOL(wpan_phy_unregister);
+
+void wpan_phy_free(struct wpan_phy *phy)
+{
+ put_device(&phy->dev);
+}
+EXPORT_SYMBOL(wpan_phy_free);
+
+void cfg802154_dev_free(struct cfg802154_registered_device *rdev)
+{
+ kfree(rdev);
+}
+
+static void
+cfg802154_update_iface_num(struct cfg802154_registered_device *rdev,
+ int iftype, int num)
+{
+ ASSERT_RTNL();
+
+ rdev->num_running_ifaces += num;
+}
+
+static int cfg802154_netdev_notifier_call(struct notifier_block *nb,
+ unsigned long state, void *ptr)
+{
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+ struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+ struct cfg802154_registered_device *rdev;
+
+ if (!wpan_dev)
+ return NOTIFY_DONE;
+
+ rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
+
+ /* TODO WARN_ON unspec type */
+
+ switch (state) {
+ /* TODO NETDEV_DEVTYPE */
+ case NETDEV_REGISTER:
+ wpan_dev->identifier = ++rdev->wpan_dev_id;
+ list_add_rcu(&wpan_dev->list, &rdev->wpan_dev_list);
+ rdev->devlist_generation++;
+
+ wpan_dev->netdev = dev;
+ break;
+ case NETDEV_DOWN:
+ cfg802154_update_iface_num(rdev, wpan_dev->iftype, -1);
+
+ rdev->opencount--;
+ wake_up(&rdev->dev_wait);
+ break;
+ case NETDEV_UP:
+ cfg802154_update_iface_num(rdev, wpan_dev->iftype, 1);
+
+ rdev->opencount++;
+ break;
+ case NETDEV_UNREGISTER:
+ /* It is possible to get NETDEV_UNREGISTER
+ * multiple times. To detect that, check
+ * that the interface is still on the list
+ * of registered interfaces, and only then
+ * remove and clean it up.
+ */
+ if (!list_empty(&wpan_dev->list)) {
+ list_del_rcu(&wpan_dev->list);
+ rdev->devlist_generation++;
+ }
+ /* synchronize (so that we won't find this netdev
+ * from other code any more) and then clear the list
+ * head so that the above code can safely check for
+ * !list_empty() to avoid double-cleanup.
+ */
+ synchronize_rcu();
+ INIT_LIST_HEAD(&wpan_dev->list);
+ break;
+ default:
+ return NOTIFY_DONE;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block cfg802154_netdev_notifier = {
+ .notifier_call = cfg802154_netdev_notifier_call,
+};
+
+static int __init wpan_phy_class_init(void)
+{
+ int rc;
+
+ rc = wpan_phy_sysfs_init();
+ if (rc)
+ goto err;
+
+ rc = register_netdevice_notifier(&cfg802154_netdev_notifier);
+ if (rc)
+ goto err_nl;
+
+ rc = ieee802154_nl_init();
+ if (rc)
+ goto err_notifier;
+
+ rc = nl802154_init();
+ if (rc)
+ goto err_ieee802154_nl;
+
+ return 0;
+
+err_ieee802154_nl:
+ ieee802154_nl_exit();
+
+err_notifier:
+ unregister_netdevice_notifier(&cfg802154_netdev_notifier);
+err_nl:
+ wpan_phy_sysfs_exit();
+err:
+ return rc;
+}
+subsys_initcall(wpan_phy_class_init);
+
+static void __exit wpan_phy_class_exit(void)
+{
+ nl802154_exit();
+ ieee802154_nl_exit();
+ unregister_netdevice_notifier(&cfg802154_netdev_notifier);
+ wpan_phy_sysfs_exit();
+}
+module_exit(wpan_phy_class_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("IEEE 802.15.4 configuration interface");
+MODULE_AUTHOR("Dmitry Eremin-Solenikov");
+
diff --git a/net/ieee802154/core.h b/net/ieee802154/core.h
new file mode 100644
index 0000000..f3e9558
--- /dev/null
+++ b/net/ieee802154/core.h
@@ -0,0 +1,46 @@
+#ifndef __IEEE802154_CORE_H
+#define __IEEE802154_CORE_H
+
+#include <net/cfg802154.h>
+
+struct cfg802154_registered_device {
+ const struct cfg802154_ops *ops;
+ struct list_head list;
+
+ /* wpan_phy index, internal only */
+ int wpan_phy_idx;
+
+ /* also protected by devlist_mtx */
+ int opencount;
+ wait_queue_head_t dev_wait;
+
+ /* protected by RTNL only */
+ int num_running_ifaces;
+
+ /* associated wpan interfaces, protected by rtnl or RCU */
+ struct list_head wpan_dev_list;
+ int devlist_generation, wpan_dev_id;
+
+ /* must be last because of the way we do wpan_phy_priv(),
+ * and it should at least be aligned to NETDEV_ALIGN
+ */
+ struct wpan_phy wpan_phy __aligned(NETDEV_ALIGN);
+};
+
+static inline struct cfg802154_registered_device *
+wpan_phy_to_rdev(struct wpan_phy *wpan_phy)
+{
+ BUG_ON(!wpan_phy);
+ return container_of(wpan_phy, struct cfg802154_registered_device,
+ wpan_phy);
+}
+
+extern struct list_head cfg802154_rdev_list;
+extern int cfg802154_rdev_list_generation;
+
+/* free object */
+void cfg802154_dev_free(struct cfg802154_registered_device *rdev);
+struct cfg802154_registered_device *
+cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx);
+
+#endif /* __IEEE802154_CORE_H */
diff --git a/net/ieee802154/dgram.c b/net/ieee802154/dgram.c
index 0961336..a866820 100644
--- a/net/ieee802154/dgram.c
+++ b/net/ieee802154/dgram.c
@@ -12,10 +12,6 @@
* 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.
- *
* Written by:
* Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
@@ -27,9 +23,9 @@
#include <linux/if_arp.h>
#include <linux/list.h>
#include <linux/slab.h>
+#include <linux/ieee802154.h>
#include <net/sock.h>
#include <net/af_ieee802154.h>
-#include <net/ieee802154.h>
#include <net/ieee802154_netdev.h>
#include <asm/ioctls.h>
@@ -158,7 +154,6 @@
spin_unlock_bh(&sk->sk_receive_queue.lock);
return put_user(amount, (int __user *)arg);
}
-
}
return -ENOIOCTLCMD;
@@ -280,7 +275,7 @@
if (err < 0)
goto out_skb;
- err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
+ err = memcpy_from_msg(skb_put(skb, size), msg, size);
if (err < 0)
goto out_skb;
@@ -324,7 +319,7 @@
}
/* FIXME: skip headers if necessary ?! */
- err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ err = skb_copy_datagram_msg(skb, 0, msg, copied);
if (err)
goto done;
diff --git a/net/ieee802154/header_ops.c b/net/ieee802154/header_ops.c
index c09294e..a051b69 100644
--- a/net/ieee802154/header_ops.c
+++ b/net/ieee802154/header_ops.c
@@ -14,8 +14,9 @@
* Phoebe Buckheister <phoebe.buckheister@itwm.fraunhofer.de>
*/
+#include <linux/ieee802154.h>
+
#include <net/mac802154.h>
-#include <net/ieee802154.h>
#include <net/ieee802154_netdev.h>
static int
diff --git a/net/ieee802154/ieee802154.h b/net/ieee802154/ieee802154.h
index 5d352f8..a5d7515 100644
--- a/net/ieee802154/ieee802154.h
+++ b/net/ieee802154/ieee802154.h
@@ -10,16 +10,12 @@
* 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 IEEE_802154_LOCAL_H
#define IEEE_802154_LOCAL_H
int __init ieee802154_nl_init(void);
-void __exit ieee802154_nl_exit(void);
+void ieee802154_nl_exit(void);
#define IEEE802154_OP(_cmd, _func) \
{ \
diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c
index 50a167a..78cc7cd 100644
--- a/net/ieee802154/netlink.c
+++ b/net/ieee802154/netlink.c
@@ -1,5 +1,5 @@
/*
- * Netlink inteface for IEEE 802.15.4 stack
+ * Netlink interface for IEEE 802.15.4 stack
*
* Copyright 2007, 2008 Siemens AG
*
@@ -12,10 +12,6 @@
* 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.
- *
* Written by:
* Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
@@ -77,7 +73,7 @@
}
struct sk_buff *ieee802154_nl_new_reply(struct genl_info *info,
- int flags, u8 req)
+ int flags, u8 req)
{
void *hdr;
struct sk_buff *msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
@@ -151,7 +147,6 @@
[IEEE802154_BEACON_MCGRP] = { .name = IEEE802154_MCAST_BEACON_NAME, },
};
-
int __init ieee802154_nl_init(void)
{
return genl_register_family_with_ops_groups(&nl802154_family,
@@ -159,7 +154,7 @@
ieee802154_mcgrps);
}
-void __exit ieee802154_nl_exit(void)
+void ieee802154_nl_exit(void)
{
genl_unregister_family(&nl802154_family);
}
diff --git a/net/ieee802154/nl-mac.c b/net/ieee802154/nl-mac.c
index cd28596..25ff57e 100644
--- a/net/ieee802154/nl-mac.c
+++ b/net/ieee802154/nl-mac.c
@@ -1,5 +1,5 @@
/*
- * Netlink inteface for IEEE 802.15.4 stack
+ * Netlink interface for IEEE 802.15.4 stack
*
* Copyright 2007, 2008 Siemens AG
*
@@ -12,10 +12,6 @@
* 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.
- *
* Written by:
* Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
@@ -26,16 +22,15 @@
#include <linux/kernel.h>
#include <linux/if_arp.h>
#include <linux/netdevice.h>
+#include <linux/ieee802154.h>
#include <net/netlink.h>
#include <net/genetlink.h>
#include <net/sock.h>
#include <linux/nl802154.h>
#include <linux/export.h>
#include <net/af_ieee802154.h>
-#include <net/nl802154.h>
-#include <net/ieee802154.h>
#include <net/ieee802154_netdev.h>
-#include <net/wpan-phy.h>
+#include <net/cfg802154.h>
#include "ieee802154.h"
@@ -59,186 +54,7 @@
return cpu_to_le16(nla_get_u16(nla));
}
-int ieee802154_nl_assoc_indic(struct net_device *dev,
- struct ieee802154_addr *addr,
- u8 cap)
-{
- struct sk_buff *msg;
-
- pr_debug("%s\n", __func__);
-
- if (addr->mode != IEEE802154_ADDR_LONG) {
- pr_err("%s: received non-long source address!\n", __func__);
- return -EINVAL;
- }
-
- msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_INDIC);
- if (!msg)
- return -ENOBUFS;
-
- if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
- nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
- nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
- dev->dev_addr) ||
- nla_put_hwaddr(msg, IEEE802154_ATTR_SRC_HW_ADDR,
- addr->extended_addr) ||
- nla_put_u8(msg, IEEE802154_ATTR_CAPABILITY, cap))
- goto nla_put_failure;
-
- return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
-
-nla_put_failure:
- nlmsg_free(msg);
- return -ENOBUFS;
-}
-EXPORT_SYMBOL(ieee802154_nl_assoc_indic);
-
-int ieee802154_nl_assoc_confirm(struct net_device *dev, __le16 short_addr,
- u8 status)
-{
- struct sk_buff *msg;
-
- pr_debug("%s\n", __func__);
-
- msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_CONF);
- if (!msg)
- return -ENOBUFS;
-
- if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
- nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
- nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
- dev->dev_addr) ||
- nla_put_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr) ||
- nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))
- goto nla_put_failure;
- return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
-
-nla_put_failure:
- nlmsg_free(msg);
- return -ENOBUFS;
-}
-EXPORT_SYMBOL(ieee802154_nl_assoc_confirm);
-
-int ieee802154_nl_disassoc_indic(struct net_device *dev,
- struct ieee802154_addr *addr,
- u8 reason)
-{
- struct sk_buff *msg;
-
- pr_debug("%s\n", __func__);
-
- msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_INDIC);
- if (!msg)
- return -ENOBUFS;
-
- if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
- nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
- nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
- dev->dev_addr))
- goto nla_put_failure;
- if (addr->mode == IEEE802154_ADDR_LONG) {
- if (nla_put_hwaddr(msg, IEEE802154_ATTR_SRC_HW_ADDR,
- addr->extended_addr))
- goto nla_put_failure;
- } else {
- if (nla_put_shortaddr(msg, IEEE802154_ATTR_SRC_SHORT_ADDR,
- addr->short_addr))
- goto nla_put_failure;
- }
- if (nla_put_u8(msg, IEEE802154_ATTR_REASON, reason))
- goto nla_put_failure;
- return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
-
-nla_put_failure:
- nlmsg_free(msg);
- return -ENOBUFS;
-}
-EXPORT_SYMBOL(ieee802154_nl_disassoc_indic);
-
-int ieee802154_nl_disassoc_confirm(struct net_device *dev, u8 status)
-{
- struct sk_buff *msg;
-
- pr_debug("%s\n", __func__);
-
- msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_CONF);
- if (!msg)
- return -ENOBUFS;
-
- if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
- nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
- nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
- dev->dev_addr) ||
- nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))
- goto nla_put_failure;
- return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
-
-nla_put_failure:
- nlmsg_free(msg);
- return -ENOBUFS;
-}
-EXPORT_SYMBOL(ieee802154_nl_disassoc_confirm);
-
-int ieee802154_nl_beacon_indic(struct net_device *dev, __le16 panid,
- __le16 coord_addr)
-{
- struct sk_buff *msg;
-
- pr_debug("%s\n", __func__);
-
- msg = ieee802154_nl_create(0, IEEE802154_BEACON_NOTIFY_INDIC);
- if (!msg)
- return -ENOBUFS;
-
- if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
- nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
- nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
- dev->dev_addr) ||
- nla_put_shortaddr(msg, IEEE802154_ATTR_COORD_SHORT_ADDR,
- coord_addr) ||
- nla_put_shortaddr(msg, IEEE802154_ATTR_COORD_PAN_ID, panid))
- goto nla_put_failure;
- return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
-
-nla_put_failure:
- nlmsg_free(msg);
- return -ENOBUFS;
-}
-EXPORT_SYMBOL(ieee802154_nl_beacon_indic);
-
-int ieee802154_nl_scan_confirm(struct net_device *dev,
- u8 status, u8 scan_type,
- u32 unscanned, u8 page,
- u8 *edl/* , struct list_head *pan_desc_list */)
-{
- struct sk_buff *msg;
-
- pr_debug("%s\n", __func__);
-
- msg = ieee802154_nl_create(0, IEEE802154_SCAN_CONF);
- if (!msg)
- return -ENOBUFS;
-
- if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
- nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
- nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
- dev->dev_addr) ||
- nla_put_u8(msg, IEEE802154_ATTR_STATUS, status) ||
- nla_put_u8(msg, IEEE802154_ATTR_SCAN_TYPE, scan_type) ||
- nla_put_u32(msg, IEEE802154_ATTR_CHANNELS, unscanned) ||
- nla_put_u8(msg, IEEE802154_ATTR_PAGE, page) ||
- (edl &&
- nla_put(msg, IEEE802154_ATTR_ED_LIST, 27, edl)))
- goto nla_put_failure;
- return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
-
-nla_put_failure:
- nlmsg_free(msg);
- return -ENOBUFS;
-}
-EXPORT_SYMBOL(ieee802154_nl_scan_confirm);
-
-int ieee802154_nl_start_confirm(struct net_device *dev, u8 status)
+static int ieee802154_nl_start_confirm(struct net_device *dev, u8 status)
{
struct sk_buff *msg;
@@ -278,8 +94,9 @@
goto out;
ops = ieee802154_mlme_ops(dev);
- phy = ops->get_phy(dev);
+ phy = dev->ieee802154_ptr->wpan_phy;
BUG_ON(!phy);
+ get_device(&phy->dev);
short_addr = ops->get_short_addr(dev);
pan_id = ops->get_pan_id(dev);
@@ -296,7 +113,9 @@
if (ops->get_mac_params) {
struct ieee802154_mac_params params;
+ rtnl_lock();
ops->get_mac_params(dev, ¶ms);
+ rtnl_unlock();
if (nla_put_s8(msg, IEEE802154_ATTR_TXPOWER,
params.transmit_power) ||
@@ -347,7 +166,10 @@
if (!dev)
return NULL;
- if (dev->type != ARPHRD_IEEE802154) {
+ /* Check on mtu is currently a hacked solution because lowpan
+ * and wpan have the same ARPHRD type.
+ */
+ if (dev->type != ARPHRD_IEEE802154 || dev->mtu != IEEE802154_MTU) {
dev_put(dev);
return NULL;
}
@@ -481,7 +303,7 @@
u8 channel, bcn_ord, sf_ord;
u8 page;
int pan_coord, blx, coord_realign;
- int ret = -EOPNOTSUPP;
+ int ret = -EBUSY;
if (!info->attrs[IEEE802154_ATTR_COORD_PAN_ID] ||
!info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR] ||
@@ -497,9 +319,15 @@
dev = ieee802154_nl_get_dev(info);
if (!dev)
return -ENODEV;
- if (!ieee802154_mlme_ops(dev)->start_req)
+
+ if (netif_running(dev))
goto out;
+ if (!ieee802154_mlme_ops(dev)->start_req) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
addr.mode = IEEE802154_ADDR_SHORT;
addr.short_addr = nla_get_shortaddr(
info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]);
@@ -518,15 +346,21 @@
else
page = 0;
-
if (addr.short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST)) {
ieee802154_nl_start_confirm(dev, IEEE802154_NO_SHORT_ADDRESS);
dev_put(dev);
return -EINVAL;
}
+ rtnl_lock();
ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, page,
bcn_ord, sf_ord, pan_coord, blx, coord_realign);
+ rtnl_unlock();
+
+ /* FIXME: add validation for unused parameters to be sane
+ * for SoftMAC
+ */
+ ieee802154_nl_start_confirm(dev, IEEE802154_SUCCESS);
out:
dev_put(dev);
@@ -562,7 +396,6 @@
else
page = 0;
-
ret = ieee802154_mlme_ops(dev)->scan_req(dev, type, channels,
page, duration);
@@ -617,7 +450,11 @@
idx = 0;
for_each_netdev(net, dev) {
- if (idx < s_idx || (dev->type != ARPHRD_IEEE802154))
+ /* Check on mtu is currently a hacked solution because lowpan
+ * and wpan have the same ARPHRD type.
+ */
+ if (idx < s_idx || dev->type != ARPHRD_IEEE802154 ||
+ dev->mtu != IEEE802154_MTU)
goto cont;
if (ieee802154_nl_fill_iface(skb, NETLINK_CB_PORTID(cb->skb),
@@ -667,22 +504,10 @@
!info->attrs[IEEE802154_ATTR_FRAME_RETRIES])
goto out;
- phy = ops->get_phy(dev);
+ phy = dev->ieee802154_ptr->wpan_phy;
+ get_device(&phy->dev);
- if ((!phy->set_lbt && info->attrs[IEEE802154_ATTR_LBT_ENABLED]) ||
- (!phy->set_cca_mode && info->attrs[IEEE802154_ATTR_CCA_MODE]) ||
- (!phy->set_cca_ed_level &&
- info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]) ||
- (!phy->set_csma_params &&
- (info->attrs[IEEE802154_ATTR_CSMA_RETRIES] ||
- info->attrs[IEEE802154_ATTR_CSMA_MIN_BE] ||
- info->attrs[IEEE802154_ATTR_CSMA_MAX_BE])) ||
- (!phy->set_frame_retries &&
- info->attrs[IEEE802154_ATTR_FRAME_RETRIES])) {
- rc = -EOPNOTSUPP;
- goto out_phy;
- }
-
+ rtnl_lock();
ops->get_mac_params(dev, ¶ms);
if (info->attrs[IEEE802154_ATTR_TXPOWER])
@@ -710,20 +535,18 @@
params.frame_retries = nla_get_s8(info->attrs[IEEE802154_ATTR_FRAME_RETRIES]);
rc = ops->set_mac_params(dev, ¶ms);
+ rtnl_unlock();
wpan_phy_put(phy);
dev_put(dev);
- return rc;
-out_phy:
- wpan_phy_put(phy);
+ return 0;
+
out:
dev_put(dev);
return rc;
}
-
-
static int
ieee802154_llsec_parse_key_id(struct genl_info *info,
struct ieee802154_llsec_key_id *desc)
@@ -939,8 +762,6 @@
return rc;
}
-
-
struct llsec_dump_data {
struct sk_buff *skb;
int s_idx, s_idx2;
@@ -963,7 +784,11 @@
int rc;
for_each_netdev(net, dev) {
- if (idx < first_dev || dev->type != ARPHRD_IEEE802154)
+ /* Check on mtu is currently a hacked solution because lowpan
+ * and wpan have the same ARPHRD type.
+ */
+ if (idx < first_dev || dev->type != ARPHRD_IEEE802154 ||
+ dev->mtu != IEEE802154_MTU)
goto skip;
data.ops = ieee802154_mlme_ops(dev);
@@ -1013,8 +838,6 @@
return rc;
}
-
-
static int
ieee802154_llsec_parse_key(struct genl_info *info,
struct ieee802154_llsec_key *key)
@@ -1159,8 +982,6 @@
return ieee802154_llsec_dump_table(skb, cb, llsec_iter_keys);
}
-
-
static int
llsec_parse_dev(struct genl_info *info,
struct ieee802154_llsec_device *dev)
@@ -1291,8 +1112,6 @@
return ieee802154_llsec_dump_table(skb, cb, llsec_iter_devs);
}
-
-
static int llsec_add_devkey(struct net_device *dev, struct genl_info *info)
{
struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
@@ -1407,8 +1226,6 @@
return ieee802154_llsec_dump_table(skb, cb, llsec_iter_devkeys);
}
-
-
static int
llsec_parse_seclevel(struct genl_info *info,
struct ieee802154_llsec_seclevel *sl)
diff --git a/net/ieee802154/nl-phy.c b/net/ieee802154/nl-phy.c
index 07c503f..2b8b724 100644
--- a/net/ieee802154/nl-phy.c
+++ b/net/ieee802154/nl-phy.c
@@ -1,5 +1,5 @@
/*
- * Netlink inteface for IEEE 802.15.4 stack
+ * Netlink interface for IEEE 802.15.4 stack
*
* Copyright 2007, 2008 Siemens AG
*
@@ -12,10 +12,6 @@
* 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.
- *
* Written by:
* Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
@@ -27,13 +23,15 @@
#include <linux/if_arp.h>
#include <net/netlink.h>
#include <net/genetlink.h>
-#include <net/wpan-phy.h>
+#include <net/cfg802154.h>
#include <net/af_ieee802154.h>
#include <net/ieee802154_netdev.h>
#include <net/rtnetlink.h> /* for rtnl_{un,}lock */
#include <linux/nl802154.h>
#include "ieee802154.h"
+#include "rdev-ops.h"
+#include "core.h"
static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 portid,
u32 seq, int flags, struct wpan_phy *phy)
@@ -96,7 +94,6 @@
if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0')
return -EINVAL; /* phy name should be null-terminated */
-
phy = wpan_phy_find(name);
if (!phy)
return -ENODEV;
@@ -208,11 +205,6 @@
if (!msg)
goto out_dev;
- if (!phy->add_iface) {
- rc = -EINVAL;
- goto nla_put_failure;
- }
-
if (info->attrs[IEEE802154_ATTR_HW_ADDR] &&
nla_len(info->attrs[IEEE802154_ATTR_HW_ADDR]) !=
IEEE802154_ADDR_LEN) {
@@ -228,11 +220,13 @@
}
}
- dev = phy->add_iface(phy, devname, type);
+ dev = rdev_add_virtual_intf_deprecated(wpan_phy_to_rdev(phy), devname,
+ type);
if (IS_ERR(dev)) {
rc = PTR_ERR(dev);
goto nla_put_failure;
}
+ dev_hold(dev);
if (info->attrs[IEEE802154_ATTR_HW_ADDR]) {
struct sockaddr addr;
@@ -262,7 +256,7 @@
dev_unregister:
rtnl_lock(); /* del_iface must be called with RTNL lock */
- phy->del_iface(phy, dev);
+ rdev_del_virtual_intf_deprecated(wpan_phy_to_rdev(phy), dev);
dev_put(dev);
rtnl_unlock();
nla_put_failure:
@@ -293,8 +287,9 @@
if (!dev)
return -ENODEV;
- phy = ieee802154_mlme_ops(dev)->get_phy(dev);
+ phy = dev->ieee802154_ptr->wpan_phy;
BUG_ON(!phy);
+ get_device(&phy->dev);
rc = -EINVAL;
/* phy name is optional, but should be checked if it's given */
@@ -324,13 +319,8 @@
if (!msg)
goto out_dev;
- if (!phy->del_iface) {
- rc = -EINVAL;
- goto nla_put_failure;
- }
-
rtnl_lock();
- phy->del_iface(phy, dev);
+ rdev_del_virtual_intf_deprecated(wpan_phy_to_rdev(phy), dev);
/* We don't have device anymore */
dev_put(dev);
diff --git a/net/ieee802154/nl802154.c b/net/ieee802154/nl802154.c
new file mode 100644
index 0000000..47be111
--- /dev/null
+++ b/net/ieee802154/nl802154.c
@@ -0,0 +1,957 @@
+/* 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.
+ *
+ * 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.
+ *
+ * Authors:
+ * Alexander Aring <aar@pengutronix.de>
+ *
+ * Based on: net/wireless/nl80211.c
+ */
+
+#include <linux/rtnetlink.h>
+
+#include <net/cfg802154.h>
+#include <net/genetlink.h>
+#include <net/mac802154.h>
+#include <net/netlink.h>
+#include <net/nl802154.h>
+#include <net/sock.h>
+
+#include "nl802154.h"
+#include "rdev-ops.h"
+#include "core.h"
+
+static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
+ struct genl_info *info);
+
+static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
+ struct genl_info *info);
+
+/* the netlink family */
+static struct genl_family nl802154_fam = {
+ .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */
+ .name = NL802154_GENL_NAME, /* have users key off the name instead */
+ .hdrsize = 0, /* no private header */
+ .version = 1, /* no particular meaning now */
+ .maxattr = NL802154_ATTR_MAX,
+ .netnsok = true,
+ .pre_doit = nl802154_pre_doit,
+ .post_doit = nl802154_post_doit,
+};
+
+/* multicast groups */
+enum nl802154_multicast_groups {
+ NL802154_MCGRP_CONFIG,
+};
+
+static const struct genl_multicast_group nl802154_mcgrps[] = {
+ [NL802154_MCGRP_CONFIG] = { .name = "config", },
+};
+
+/* returns ERR_PTR values */
+static struct wpan_dev *
+__cfg802154_wpan_dev_from_attrs(struct net *netns, struct nlattr **attrs)
+{
+ struct cfg802154_registered_device *rdev;
+ struct wpan_dev *result = NULL;
+ bool have_ifidx = attrs[NL802154_ATTR_IFINDEX];
+ bool have_wpan_dev_id = attrs[NL802154_ATTR_WPAN_DEV];
+ u64 wpan_dev_id;
+ int wpan_phy_idx = -1;
+ int ifidx = -1;
+
+ ASSERT_RTNL();
+
+ if (!have_ifidx && !have_wpan_dev_id)
+ return ERR_PTR(-EINVAL);
+
+ if (have_ifidx)
+ ifidx = nla_get_u32(attrs[NL802154_ATTR_IFINDEX]);
+ if (have_wpan_dev_id) {
+ wpan_dev_id = nla_get_u64(attrs[NL802154_ATTR_WPAN_DEV]);
+ wpan_phy_idx = wpan_dev_id >> 32;
+ }
+
+ list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
+ struct wpan_dev *wpan_dev;
+
+ /* TODO netns compare */
+
+ if (have_wpan_dev_id && rdev->wpan_phy_idx != wpan_phy_idx)
+ continue;
+
+ list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) {
+ if (have_ifidx && wpan_dev->netdev &&
+ wpan_dev->netdev->ifindex == ifidx) {
+ result = wpan_dev;
+ break;
+ }
+ if (have_wpan_dev_id &&
+ wpan_dev->identifier == (u32)wpan_dev_id) {
+ result = wpan_dev;
+ break;
+ }
+ }
+
+ if (result)
+ break;
+ }
+
+ if (result)
+ return result;
+
+ return ERR_PTR(-ENODEV);
+}
+
+static struct cfg802154_registered_device *
+__cfg802154_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
+{
+ struct cfg802154_registered_device *rdev = NULL, *tmp;
+ struct net_device *netdev;
+
+ ASSERT_RTNL();
+
+ if (!attrs[NL802154_ATTR_WPAN_PHY] &&
+ !attrs[NL802154_ATTR_IFINDEX] &&
+ !attrs[NL802154_ATTR_WPAN_DEV])
+ return ERR_PTR(-EINVAL);
+
+ if (attrs[NL802154_ATTR_WPAN_PHY])
+ rdev = cfg802154_rdev_by_wpan_phy_idx(
+ nla_get_u32(attrs[NL802154_ATTR_WPAN_PHY]));
+
+ if (attrs[NL802154_ATTR_WPAN_DEV]) {
+ u64 wpan_dev_id = nla_get_u64(attrs[NL802154_ATTR_WPAN_DEV]);
+ struct wpan_dev *wpan_dev;
+ bool found = false;
+
+ tmp = cfg802154_rdev_by_wpan_phy_idx(wpan_dev_id >> 32);
+ if (tmp) {
+ /* make sure wpan_dev exists */
+ list_for_each_entry(wpan_dev, &tmp->wpan_dev_list, list) {
+ if (wpan_dev->identifier != (u32)wpan_dev_id)
+ continue;
+ found = true;
+ break;
+ }
+
+ if (!found)
+ tmp = NULL;
+
+ if (rdev && tmp != rdev)
+ return ERR_PTR(-EINVAL);
+ rdev = tmp;
+ }
+ }
+
+ if (attrs[NL802154_ATTR_IFINDEX]) {
+ int ifindex = nla_get_u32(attrs[NL802154_ATTR_IFINDEX]);
+
+ netdev = __dev_get_by_index(netns, ifindex);
+ if (netdev) {
+ if (netdev->ieee802154_ptr)
+ tmp = wpan_phy_to_rdev(
+ netdev->ieee802154_ptr->wpan_phy);
+ else
+ tmp = NULL;
+
+ /* not wireless device -- return error */
+ if (!tmp)
+ return ERR_PTR(-EINVAL);
+
+ /* mismatch -- return error */
+ if (rdev && tmp != rdev)
+ return ERR_PTR(-EINVAL);
+
+ rdev = tmp;
+ }
+ }
+
+ if (!rdev)
+ return ERR_PTR(-ENODEV);
+
+ /* TODO netns compare */
+
+ return rdev;
+}
+
+/* This function returns a pointer to the driver
+ * that the genl_info item that is passed refers to.
+ *
+ * The result of this can be a PTR_ERR and hence must
+ * be checked with IS_ERR() for errors.
+ */
+static struct cfg802154_registered_device *
+cfg802154_get_dev_from_info(struct net *netns, struct genl_info *info)
+{
+ return __cfg802154_rdev_from_attrs(netns, info->attrs);
+}
+
+/* policy for the attributes */
+static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = {
+ [NL802154_ATTR_WPAN_PHY] = { .type = NLA_U32 },
+ [NL802154_ATTR_WPAN_PHY_NAME] = { .type = NLA_NUL_STRING,
+ .len = 20-1 },
+
+ [NL802154_ATTR_IFINDEX] = { .type = NLA_U32 },
+ [NL802154_ATTR_IFTYPE] = { .type = NLA_U32 },
+ [NL802154_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
+
+ [NL802154_ATTR_WPAN_DEV] = { .type = NLA_U64 },
+
+ [NL802154_ATTR_PAGE] = { .type = NLA_U8, },
+ [NL802154_ATTR_CHANNEL] = { .type = NLA_U8, },
+
+ [NL802154_ATTR_TX_POWER] = { .type = NLA_S8, },
+
+ [NL802154_ATTR_CCA_MODE] = { .type = NLA_U8, },
+
+ [NL802154_ATTR_SUPPORTED_CHANNEL] = { .type = NLA_U32, },
+
+ [NL802154_ATTR_PAN_ID] = { .type = NLA_U16, },
+ [NL802154_ATTR_EXTENDED_ADDR] = { .type = NLA_U64 },
+ [NL802154_ATTR_SHORT_ADDR] = { .type = NLA_U16, },
+
+ [NL802154_ATTR_MIN_BE] = { .type = NLA_U8, },
+ [NL802154_ATTR_MAX_BE] = { .type = NLA_U8, },
+ [NL802154_ATTR_MAX_CSMA_BACKOFFS] = { .type = NLA_U8, },
+
+ [NL802154_ATTR_MAX_FRAME_RETRIES] = { .type = NLA_S8, },
+
+ [NL802154_ATTR_LBT_MODE] = { .type = NLA_U8, },
+};
+
+/* message building helper */
+static inline void *nl802154hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
+ int flags, u8 cmd)
+{
+ /* since there is no private header just add the generic one */
+ return genlmsg_put(skb, portid, seq, &nl802154_fam, flags, cmd);
+}
+
+static int
+nl802154_send_wpan_phy_channels(struct cfg802154_registered_device *rdev,
+ struct sk_buff *msg)
+{
+ struct nlattr *nl_page;
+ unsigned long page;
+
+ nl_page = nla_nest_start(msg, NL802154_ATTR_CHANNELS_SUPPORTED);
+ if (!nl_page)
+ return -ENOBUFS;
+
+ for (page = 0; page <= IEEE802154_MAX_PAGE; page++) {
+ if (nla_put_u32(msg, NL802154_ATTR_SUPPORTED_CHANNEL,
+ rdev->wpan_phy.channels_supported[page]))
+ return -ENOBUFS;
+ }
+ nla_nest_end(msg, nl_page);
+
+ return 0;
+}
+
+static int nl802154_send_wpan_phy(struct cfg802154_registered_device *rdev,
+ enum nl802154_commands cmd,
+ struct sk_buff *msg, u32 portid, u32 seq,
+ int flags)
+{
+ void *hdr;
+
+ hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
+ if (!hdr)
+ return -ENOBUFS;
+
+ if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx) ||
+ nla_put_string(msg, NL802154_ATTR_WPAN_PHY_NAME,
+ wpan_phy_name(&rdev->wpan_phy)) ||
+ nla_put_u32(msg, NL802154_ATTR_GENERATION,
+ cfg802154_rdev_list_generation))
+ goto nla_put_failure;
+
+ if (cmd != NL802154_CMD_NEW_WPAN_PHY)
+ goto finish;
+
+ /* DUMP PHY PIB */
+
+ /* current channel settings */
+ if (nla_put_u8(msg, NL802154_ATTR_PAGE,
+ rdev->wpan_phy.current_page) ||
+ nla_put_u8(msg, NL802154_ATTR_CHANNEL,
+ rdev->wpan_phy.current_channel))
+ goto nla_put_failure;
+
+ /* supported channels array */
+ if (nl802154_send_wpan_phy_channels(rdev, msg))
+ goto nla_put_failure;
+
+ /* cca mode */
+ if (nla_put_u8(msg, NL802154_ATTR_CCA_MODE,
+ rdev->wpan_phy.cca_mode))
+ goto nla_put_failure;
+
+ if (nla_put_s8(msg, NL802154_ATTR_TX_POWER,
+ rdev->wpan_phy.transmit_power))
+ goto nla_put_failure;
+
+finish:
+ return genlmsg_end(msg, hdr);
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ return -EMSGSIZE;
+}
+
+struct nl802154_dump_wpan_phy_state {
+ s64 filter_wpan_phy;
+ long start;
+
+};
+
+static int nl802154_dump_wpan_phy_parse(struct sk_buff *skb,
+ struct netlink_callback *cb,
+ struct nl802154_dump_wpan_phy_state *state)
+{
+ struct nlattr **tb = nl802154_fam.attrbuf;
+ int ret = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl802154_fam.hdrsize,
+ tb, nl802154_fam.maxattr, nl802154_policy);
+
+ /* TODO check if we can handle error here,
+ * we have no backward compatibility
+ */
+ if (ret)
+ return 0;
+
+ if (tb[NL802154_ATTR_WPAN_PHY])
+ state->filter_wpan_phy = nla_get_u32(tb[NL802154_ATTR_WPAN_PHY]);
+ if (tb[NL802154_ATTR_WPAN_DEV])
+ state->filter_wpan_phy = nla_get_u64(tb[NL802154_ATTR_WPAN_DEV]) >> 32;
+ if (tb[NL802154_ATTR_IFINDEX]) {
+ struct net_device *netdev;
+ struct cfg802154_registered_device *rdev;
+ int ifidx = nla_get_u32(tb[NL802154_ATTR_IFINDEX]);
+
+ /* TODO netns */
+ netdev = __dev_get_by_index(&init_net, ifidx);
+ if (!netdev)
+ return -ENODEV;
+ if (netdev->ieee802154_ptr) {
+ rdev = wpan_phy_to_rdev(
+ netdev->ieee802154_ptr->wpan_phy);
+ state->filter_wpan_phy = rdev->wpan_phy_idx;
+ }
+ }
+
+ return 0;
+}
+
+static int
+nl802154_dump_wpan_phy(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int idx = 0, ret;
+ struct nl802154_dump_wpan_phy_state *state = (void *)cb->args[0];
+ struct cfg802154_registered_device *rdev;
+
+ rtnl_lock();
+ if (!state) {
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state) {
+ rtnl_unlock();
+ return -ENOMEM;
+ }
+ state->filter_wpan_phy = -1;
+ ret = nl802154_dump_wpan_phy_parse(skb, cb, state);
+ if (ret) {
+ kfree(state);
+ rtnl_unlock();
+ return ret;
+ }
+ cb->args[0] = (long)state;
+ }
+
+ list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
+ /* TODO net ns compare */
+ if (++idx <= state->start)
+ continue;
+ if (state->filter_wpan_phy != -1 &&
+ state->filter_wpan_phy != rdev->wpan_phy_idx)
+ continue;
+ /* attempt to fit multiple wpan_phy data chunks into the skb */
+ ret = nl802154_send_wpan_phy(rdev,
+ NL802154_CMD_NEW_WPAN_PHY,
+ skb,
+ NETLINK_CB_PORTID(cb->skb),
+ cb->nlh->nlmsg_seq, NLM_F_MULTI);
+ if (ret < 0) {
+ if ((ret == -ENOBUFS || ret == -EMSGSIZE) &&
+ !skb->len && cb->min_dump_alloc < 4096) {
+ cb->min_dump_alloc = 4096;
+ rtnl_unlock();
+ return 1;
+ }
+ idx--;
+ break;
+ }
+ break;
+ }
+ rtnl_unlock();
+
+ state->start = idx;
+
+ return skb->len;
+}
+
+static int nl802154_dump_wpan_phy_done(struct netlink_callback *cb)
+{
+ kfree((void *)cb->args[0]);
+ return 0;
+}
+
+static int nl802154_get_wpan_phy(struct sk_buff *skb, struct genl_info *info)
+{
+ struct sk_buff *msg;
+ struct cfg802154_registered_device *rdev = info->user_ptr[0];
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ if (nl802154_send_wpan_phy(rdev, NL802154_CMD_NEW_WPAN_PHY, msg,
+ genl_info_snd_portid(info), info->snd_seq, 0) < 0) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ return genlmsg_reply(msg, info);
+}
+
+static inline u64 wpan_dev_id(struct wpan_dev *wpan_dev)
+{
+ return (u64)wpan_dev->identifier |
+ ((u64)wpan_phy_to_rdev(wpan_dev->wpan_phy)->wpan_phy_idx << 32);
+}
+
+static int
+nl802154_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
+ struct cfg802154_registered_device *rdev,
+ struct wpan_dev *wpan_dev)
+{
+ struct net_device *dev = wpan_dev->netdev;
+ void *hdr;
+
+ hdr = nl802154hdr_put(msg, portid, seq, flags,
+ NL802154_CMD_NEW_INTERFACE);
+ if (!hdr)
+ return -1;
+
+ if (dev &&
+ (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex) ||
+ nla_put_string(msg, NL802154_ATTR_IFNAME, dev->name)))
+ goto nla_put_failure;
+
+ if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx) ||
+ nla_put_u32(msg, NL802154_ATTR_IFTYPE, wpan_dev->iftype) ||
+ nla_put_u64(msg, NL802154_ATTR_WPAN_DEV, wpan_dev_id(wpan_dev)) ||
+ nla_put_u32(msg, NL802154_ATTR_GENERATION,
+ rdev->devlist_generation ^
+ (cfg802154_rdev_list_generation << 2)))
+ goto nla_put_failure;
+
+ /* address settings */
+ if (nla_put_le64(msg, NL802154_ATTR_EXTENDED_ADDR,
+ wpan_dev->extended_addr) ||
+ nla_put_le16(msg, NL802154_ATTR_SHORT_ADDR,
+ wpan_dev->short_addr) ||
+ nla_put_le16(msg, NL802154_ATTR_PAN_ID, wpan_dev->pan_id))
+ goto nla_put_failure;
+
+ /* ARET handling */
+ if (nla_put_s8(msg, NL802154_ATTR_MAX_FRAME_RETRIES,
+ wpan_dev->frame_retries) ||
+ nla_put_u8(msg, NL802154_ATTR_MAX_BE, wpan_dev->max_be) ||
+ nla_put_u8(msg, NL802154_ATTR_MAX_CSMA_BACKOFFS,
+ wpan_dev->csma_retries) ||
+ nla_put_u8(msg, NL802154_ATTR_MIN_BE, wpan_dev->min_be))
+ goto nla_put_failure;
+
+ /* listen before transmit */
+ if (nla_put_u8(msg, NL802154_ATTR_LBT_MODE, wpan_dev->lbt))
+ goto nla_put_failure;
+
+ return genlmsg_end(msg, hdr);
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ return -EMSGSIZE;
+}
+
+static int
+nl802154_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int wp_idx = 0;
+ int if_idx = 0;
+ int wp_start = cb->args[0];
+ int if_start = cb->args[1];
+ struct cfg802154_registered_device *rdev;
+ struct wpan_dev *wpan_dev;
+
+ rtnl_lock();
+ list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
+ /* TODO netns compare */
+ if (wp_idx < wp_start) {
+ wp_idx++;
+ continue;
+ }
+ if_idx = 0;
+
+ list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) {
+ if (if_idx < if_start) {
+ if_idx++;
+ continue;
+ }
+ if (nl802154_send_iface(skb, NETLINK_CB_PORTID(cb->skb),
+ cb->nlh->nlmsg_seq, NLM_F_MULTI,
+ rdev, wpan_dev) < 0) {
+ goto out;
+ }
+ if_idx++;
+ }
+
+ wp_idx++;
+ }
+out:
+ rtnl_unlock();
+
+ cb->args[0] = wp_idx;
+ cb->args[1] = if_idx;
+
+ return skb->len;
+}
+
+static int nl802154_get_interface(struct sk_buff *skb, struct genl_info *info)
+{
+ struct sk_buff *msg;
+ struct cfg802154_registered_device *rdev = info->user_ptr[0];
+ struct wpan_dev *wdev = info->user_ptr[1];
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ if (nl802154_send_iface(msg, genl_info_snd_portid(info), info->snd_seq, 0,
+ rdev, wdev) < 0) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ return genlmsg_reply(msg, info);
+}
+
+static int nl802154_new_interface(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg802154_registered_device *rdev = info->user_ptr[0];
+ enum nl802154_iftype type = NL802154_IFTYPE_UNSPEC;
+ __le64 extended_addr = cpu_to_le64(0x0000000000000000ULL);
+
+ /* TODO avoid failing a new interface
+ * creation due to pending removal?
+ */
+
+ if (!info->attrs[NL802154_ATTR_IFNAME])
+ return -EINVAL;
+
+ if (info->attrs[NL802154_ATTR_IFTYPE]) {
+ type = nla_get_u32(info->attrs[NL802154_ATTR_IFTYPE]);
+ if (type > NL802154_IFTYPE_MAX)
+ return -EINVAL;
+ }
+
+ /* TODO add nla_get_le64 to netlink */
+ if (info->attrs[NL802154_ATTR_EXTENDED_ADDR])
+ extended_addr = (__force __le64)nla_get_u64(
+ info->attrs[NL802154_ATTR_EXTENDED_ADDR]);
+
+ if (!rdev->ops->add_virtual_intf)
+ return -EOPNOTSUPP;
+
+ return rdev_add_virtual_intf(rdev,
+ nla_data(info->attrs[NL802154_ATTR_IFNAME]),
+ type, extended_addr);
+}
+
+static int nl802154_del_interface(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg802154_registered_device *rdev = info->user_ptr[0];
+ struct wpan_dev *wpan_dev = info->user_ptr[1];
+
+ if (!rdev->ops->del_virtual_intf)
+ return -EOPNOTSUPP;
+
+ /* If we remove a wpan device without a netdev then clear
+ * user_ptr[1] so that nl802154_post_doit won't dereference it
+ * to check if it needs to do dev_put(). Otherwise it crashes
+ * since the wpan_dev has been freed, unlike with a netdev where
+ * we need the dev_put() for the netdev to really be freed.
+ */
+ if (!wpan_dev->netdev)
+ info->user_ptr[1] = NULL;
+
+ return rdev_del_virtual_intf(rdev, wpan_dev);
+}
+
+static int nl802154_set_channel(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg802154_registered_device *rdev = info->user_ptr[0];
+ u8 channel, page;
+
+ if (!info->attrs[NL802154_ATTR_PAGE] ||
+ !info->attrs[NL802154_ATTR_CHANNEL])
+ return -EINVAL;
+
+ page = nla_get_u8(info->attrs[NL802154_ATTR_PAGE]);
+ channel = nla_get_u8(info->attrs[NL802154_ATTR_CHANNEL]);
+
+ /* check 802.15.4 constraints */
+ if (page > IEEE802154_MAX_PAGE || channel > IEEE802154_MAX_CHANNEL)
+ return -EINVAL;
+
+ return rdev_set_channel(rdev, page, channel);
+}
+
+static int nl802154_set_pan_id(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg802154_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+ __le16 pan_id;
+
+ /* conflict here while tx/rx calls */
+ if (netif_running(dev))
+ return -EBUSY;
+
+ /* don't change address fields on monitor */
+ if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
+ return -EINVAL;
+
+ if (!info->attrs[NL802154_ATTR_PAN_ID])
+ return -EINVAL;
+
+ pan_id = nla_get_le16(info->attrs[NL802154_ATTR_PAN_ID]);
+
+ return rdev_set_pan_id(rdev, wpan_dev, pan_id);
+}
+
+static int nl802154_set_short_addr(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg802154_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+ __le16 short_addr;
+
+ /* conflict here while tx/rx calls */
+ if (netif_running(dev))
+ return -EBUSY;
+
+ /* don't change address fields on monitor */
+ if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
+ return -EINVAL;
+
+ if (!info->attrs[NL802154_ATTR_SHORT_ADDR])
+ return -EINVAL;
+
+ short_addr = nla_get_le16(info->attrs[NL802154_ATTR_SHORT_ADDR]);
+
+ return rdev_set_short_addr(rdev, wpan_dev, short_addr);
+}
+
+static int
+nl802154_set_backoff_exponent(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg802154_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+ u8 min_be, max_be;
+
+ /* should be set on netif open inside phy settings */
+ if (netif_running(dev))
+ return -EBUSY;
+
+ if (!info->attrs[NL802154_ATTR_MIN_BE] ||
+ !info->attrs[NL802154_ATTR_MAX_BE])
+ return -EINVAL;
+
+ min_be = nla_get_u8(info->attrs[NL802154_ATTR_MIN_BE]);
+ max_be = nla_get_u8(info->attrs[NL802154_ATTR_MAX_BE]);
+
+ /* check 802.15.4 constraints */
+ if (max_be < 3 || max_be > 8 || min_be > max_be)
+ return -EINVAL;
+
+ return rdev_set_backoff_exponent(rdev, wpan_dev, min_be, max_be);
+}
+
+static int
+nl802154_set_max_csma_backoffs(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg802154_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+ u8 max_csma_backoffs;
+
+ /* conflict here while other running iface settings */
+ if (netif_running(dev))
+ return -EBUSY;
+
+ if (!info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS])
+ return -EINVAL;
+
+ max_csma_backoffs = nla_get_u8(
+ info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS]);
+
+ /* check 802.15.4 constraints */
+ if (max_csma_backoffs > 5)
+ return -EINVAL;
+
+ return rdev_set_max_csma_backoffs(rdev, wpan_dev, max_csma_backoffs);
+}
+
+static int
+nl802154_set_max_frame_retries(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg802154_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+ s8 max_frame_retries;
+
+ if (netif_running(dev))
+ return -EBUSY;
+
+ if (!info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES])
+ return -EINVAL;
+
+ max_frame_retries = nla_get_s8(
+ info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES]);
+
+ /* check 802.15.4 constraints */
+ if (max_frame_retries < -1 || max_frame_retries > 7)
+ return -EINVAL;
+
+ return rdev_set_max_frame_retries(rdev, wpan_dev, max_frame_retries);
+}
+
+static int nl802154_set_lbt_mode(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg802154_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+ bool mode;
+
+ if (netif_running(dev))
+ return -EBUSY;
+
+ if (!info->attrs[NL802154_ATTR_LBT_MODE])
+ return -EINVAL;
+
+ mode = !!nla_get_u8(info->attrs[NL802154_ATTR_LBT_MODE]);
+ return rdev_set_lbt_mode(rdev, wpan_dev, mode);
+}
+
+#define NL802154_FLAG_NEED_WPAN_PHY 0x01
+#define NL802154_FLAG_NEED_NETDEV 0x02
+#define NL802154_FLAG_NEED_RTNL 0x04
+#define NL802154_FLAG_CHECK_NETDEV_UP 0x08
+#define NL802154_FLAG_NEED_NETDEV_UP (NL802154_FLAG_NEED_NETDEV |\
+ NL802154_FLAG_CHECK_NETDEV_UP)
+#define NL802154_FLAG_NEED_WPAN_DEV 0x10
+#define NL802154_FLAG_NEED_WPAN_DEV_UP (NL802154_FLAG_NEED_WPAN_DEV |\
+ NL802154_FLAG_CHECK_NETDEV_UP)
+
+static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg802154_registered_device *rdev;
+ struct wpan_dev *wpan_dev;
+ struct net_device *dev;
+ bool rtnl = ops->internal_flags & NL802154_FLAG_NEED_RTNL;
+
+ if (rtnl)
+ rtnl_lock();
+
+ if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_PHY) {
+ rdev = cfg802154_get_dev_from_info(genl_info_net(info), info);
+ if (IS_ERR(rdev)) {
+ if (rtnl)
+ rtnl_unlock();
+ return PTR_ERR(rdev);
+ }
+ info->user_ptr[0] = rdev;
+ } else if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV ||
+ ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
+ ASSERT_RTNL();
+ wpan_dev = __cfg802154_wpan_dev_from_attrs(genl_info_net(info),
+ info->attrs);
+ if (IS_ERR(wpan_dev)) {
+ if (rtnl)
+ rtnl_unlock();
+ return PTR_ERR(wpan_dev);
+ }
+
+ dev = wpan_dev->netdev;
+ rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
+
+ if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV) {
+ if (!dev) {
+ if (rtnl)
+ rtnl_unlock();
+ return -EINVAL;
+ }
+
+ info->user_ptr[1] = dev;
+ } else {
+ info->user_ptr[1] = wpan_dev;
+ }
+
+ if (dev) {
+ if (ops->internal_flags & NL802154_FLAG_CHECK_NETDEV_UP &&
+ !netif_running(dev)) {
+ if (rtnl)
+ rtnl_unlock();
+ return -ENETDOWN;
+ }
+
+ dev_hold(dev);
+ }
+
+ info->user_ptr[0] = rdev;
+ }
+
+ return 0;
+}
+
+static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
+ struct genl_info *info)
+{
+ if (info->user_ptr[1]) {
+ if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
+ struct wpan_dev *wpan_dev = info->user_ptr[1];
+
+ if (wpan_dev->netdev)
+ dev_put(wpan_dev->netdev);
+ } else {
+ dev_put(info->user_ptr[1]);
+ }
+ }
+
+ if (ops->internal_flags & NL802154_FLAG_NEED_RTNL)
+ rtnl_unlock();
+}
+
+static const struct genl_ops nl802154_ops[] = {
+ {
+ .cmd = NL802154_CMD_GET_WPAN_PHY,
+ .doit = nl802154_get_wpan_phy,
+ .dumpit = nl802154_dump_wpan_phy,
+ .done = nl802154_dump_wpan_phy_done,
+ .policy = nl802154_policy,
+ /* can be retrieved by unprivileged users */
+ .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
+ NL802154_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL802154_CMD_GET_INTERFACE,
+ .doit = nl802154_get_interface,
+ .dumpit = nl802154_dump_interface,
+ .policy = nl802154_policy,
+ /* can be retrieved by unprivileged users */
+ .internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
+ NL802154_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL802154_CMD_NEW_INTERFACE,
+ .doit = nl802154_new_interface,
+ .policy = nl802154_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
+ NL802154_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL802154_CMD_DEL_INTERFACE,
+ .doit = nl802154_del_interface,
+ .policy = nl802154_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
+ NL802154_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL802154_CMD_SET_CHANNEL,
+ .doit = nl802154_set_channel,
+ .policy = nl802154_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
+ NL802154_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL802154_CMD_SET_PAN_ID,
+ .doit = nl802154_set_pan_id,
+ .policy = nl802154_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL802154_FLAG_NEED_NETDEV |
+ NL802154_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL802154_CMD_SET_SHORT_ADDR,
+ .doit = nl802154_set_short_addr,
+ .policy = nl802154_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL802154_FLAG_NEED_NETDEV |
+ NL802154_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL802154_CMD_SET_BACKOFF_EXPONENT,
+ .doit = nl802154_set_backoff_exponent,
+ .policy = nl802154_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL802154_FLAG_NEED_NETDEV |
+ NL802154_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL802154_CMD_SET_MAX_CSMA_BACKOFFS,
+ .doit = nl802154_set_max_csma_backoffs,
+ .policy = nl802154_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL802154_FLAG_NEED_NETDEV |
+ NL802154_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL802154_CMD_SET_MAX_FRAME_RETRIES,
+ .doit = nl802154_set_max_frame_retries,
+ .policy = nl802154_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL802154_FLAG_NEED_NETDEV |
+ NL802154_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL802154_CMD_SET_LBT_MODE,
+ .doit = nl802154_set_lbt_mode,
+ .policy = nl802154_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL802154_FLAG_NEED_NETDEV |
+ NL802154_FLAG_NEED_RTNL,
+ },
+};
+
+/* initialisation/exit functions */
+int nl802154_init(void)
+{
+ return genl_register_family_with_ops_groups(&nl802154_fam, nl802154_ops,
+ nl802154_mcgrps);
+}
+
+void nl802154_exit(void)
+{
+ genl_unregister_family(&nl802154_fam);
+}
diff --git a/net/ieee802154/nl802154.h b/net/ieee802154/nl802154.h
new file mode 100644
index 0000000..3846a89
--- /dev/null
+++ b/net/ieee802154/nl802154.h
@@ -0,0 +1,7 @@
+#ifndef __IEEE802154_NL802154_H
+#define __IEEE802154_NL802154_H
+
+int nl802154_init(void);
+void nl802154_exit(void);
+
+#endif /* __IEEE802154_NL802154_H */
diff --git a/net/ieee802154/nl_policy.c b/net/ieee802154/nl_policy.c
index 3a703ab..35c4326 100644
--- a/net/ieee802154/nl_policy.c
+++ b/net/ieee802154/nl_policy.c
@@ -12,10 +12,6 @@
* 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 <linux/kernel.h>
diff --git a/net/ieee802154/raw.c b/net/ieee802154/raw.c
index 9d1f648..1674b11 100644
--- a/net/ieee802154/raw.c
+++ b/net/ieee802154/raw.c
@@ -12,10 +12,6 @@
* 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.
- *
* Written by:
* Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
@@ -154,7 +150,7 @@
skb_reset_mac_header(skb);
skb_reset_network_header(skb);
- err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
+ err = memcpy_from_msg(skb_put(skb, size), msg, size);
if (err < 0)
goto out_skb;
@@ -195,7 +191,7 @@
copied = len;
}
- err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ err = skb_copy_datagram_msg(skb, 0, msg, copied);
if (err)
goto done;
@@ -225,7 +221,6 @@
return NET_RX_SUCCESS;
}
-
void ieee802154_raw_deliver(struct net_device *dev, struct sk_buff *skb)
{
struct sock *sk;
diff --git a/net/ieee802154/rdev-ops.h b/net/ieee802154/rdev-ops.h
new file mode 100644
index 0000000..aff54fb
--- /dev/null
+++ b/net/ieee802154/rdev-ops.h
@@ -0,0 +1,89 @@
+#ifndef __CFG802154_RDEV_OPS
+#define __CFG802154_RDEV_OPS
+
+#include <net/cfg802154.h>
+
+#include "core.h"
+
+static inline struct net_device *
+rdev_add_virtual_intf_deprecated(struct cfg802154_registered_device *rdev,
+ const char *name, int type)
+{
+ return rdev->ops->add_virtual_intf_deprecated(&rdev->wpan_phy, name,
+ type);
+}
+
+static inline void
+rdev_del_virtual_intf_deprecated(struct cfg802154_registered_device *rdev,
+ struct net_device *dev)
+{
+ rdev->ops->del_virtual_intf_deprecated(&rdev->wpan_phy, dev);
+}
+
+static inline int
+rdev_add_virtual_intf(struct cfg802154_registered_device *rdev, char *name,
+ enum nl802154_iftype type, __le64 extended_addr)
+{
+ return rdev->ops->add_virtual_intf(&rdev->wpan_phy, name, type,
+ extended_addr);
+}
+
+static inline int
+rdev_del_virtual_intf(struct cfg802154_registered_device *rdev,
+ struct wpan_dev *wpan_dev)
+{
+ return rdev->ops->del_virtual_intf(&rdev->wpan_phy, wpan_dev);
+}
+
+static inline int
+rdev_set_channel(struct cfg802154_registered_device *rdev, u8 page, u8 channel)
+{
+ return rdev->ops->set_channel(&rdev->wpan_phy, page, channel);
+}
+
+static inline int
+rdev_set_pan_id(struct cfg802154_registered_device *rdev,
+ struct wpan_dev *wpan_dev, __le16 pan_id)
+{
+ return rdev->ops->set_pan_id(&rdev->wpan_phy, wpan_dev, pan_id);
+}
+
+static inline int
+rdev_set_short_addr(struct cfg802154_registered_device *rdev,
+ struct wpan_dev *wpan_dev, __le16 short_addr)
+{
+ return rdev->ops->set_short_addr(&rdev->wpan_phy, wpan_dev, short_addr);
+}
+
+static inline int
+rdev_set_backoff_exponent(struct cfg802154_registered_device *rdev,
+ struct wpan_dev *wpan_dev, u8 min_be, u8 max_be)
+{
+ return rdev->ops->set_backoff_exponent(&rdev->wpan_phy, wpan_dev,
+ min_be, max_be);
+}
+
+static inline int
+rdev_set_max_csma_backoffs(struct cfg802154_registered_device *rdev,
+ struct wpan_dev *wpan_dev, u8 max_csma_backoffs)
+{
+ return rdev->ops->set_max_csma_backoffs(&rdev->wpan_phy, wpan_dev,
+ max_csma_backoffs);
+}
+
+static inline int
+rdev_set_max_frame_retries(struct cfg802154_registered_device *rdev,
+ struct wpan_dev *wpan_dev, s8 max_frame_retries)
+{
+ return rdev->ops->set_max_frame_retries(&rdev->wpan_phy, wpan_dev,
+ max_frame_retries);
+}
+
+static inline int
+rdev_set_lbt_mode(struct cfg802154_registered_device *rdev,
+ struct wpan_dev *wpan_dev, bool mode)
+{
+ return rdev->ops->set_lbt_mode(&rdev->wpan_phy, wpan_dev, mode);
+}
+
+#endif /* __CFG802154_RDEV_OPS */
diff --git a/net/ieee802154/reassembly.c b/net/ieee802154/reassembly.c
index 4862bb5..f133b62 100644
--- a/net/ieee802154/reassembly.c
+++ b/net/ieee802154/reassembly.c
@@ -33,7 +33,7 @@
static const char lowpan_frags_cache_name[] = "lowpan-frags";
struct lowpan_frag_info {
- __be16 d_tag;
+ u16 d_tag;
u16 d_size;
u8 d_offset;
};
@@ -48,7 +48,7 @@
static int lowpan_frag_reasm(struct lowpan_frag_queue *fq,
struct sk_buff *prev, struct net_device *dev);
-static unsigned int lowpan_hash_frag(__be16 tag, u16 d_size,
+static unsigned int lowpan_hash_frag(u16 tag, u16 d_size,
const struct ieee802154_addr *saddr,
const struct ieee802154_addr *daddr)
{
@@ -337,11 +337,13 @@
{
bool fail;
u8 pattern = 0, low = 0;
+ __be16 d_tag = 0;
fail = lowpan_fetch_skb(skb, &pattern, 1);
fail |= lowpan_fetch_skb(skb, &low, 1);
frag_info->d_size = (pattern & 7) << 8 | low;
- fail |= lowpan_fetch_skb(skb, &frag_info->d_tag, 2);
+ fail |= lowpan_fetch_skb(skb, &d_tag, 2);
+ frag_info->d_tag = ntohs(d_tag);
if (frag_type == LOWPAN_DISPATCH_FRAGN) {
fail |= lowpan_fetch_skb(skb, &frag_info->d_offset, 1);
diff --git a/net/ieee802154/reassembly.h b/net/ieee802154/reassembly.h
index 32797ac..082e9d4 100644
--- a/net/ieee802154/reassembly.h
+++ b/net/ieee802154/reassembly.h
@@ -5,7 +5,7 @@
#include <linux/spinlock.h>
struct lowpan_create_arg {
- __be16 tag;
+ u16 tag;
u16 d_size;
const struct ieee802154_addr *src;
const struct ieee802154_addr *dst;
@@ -16,7 +16,7 @@
struct lowpan_frag_queue {
struct inet_frag_queue q;
- __be16 tag;
+ u16 tag;
u16 d_size;
struct ieee802154_addr saddr;
struct ieee802154_addr daddr;
diff --git a/net/ieee802154/sysfs.c b/net/ieee802154/sysfs.c
new file mode 100644
index 0000000..2aec368
--- /dev/null
+++ b/net/ieee802154/sysfs.c
@@ -0,0 +1,138 @@
+/* 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.
+ *
+ * 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.
+ *
+ * Authors:
+ * Alexander Aring <aar@pengutronix.de>
+ *
+ * Based on: net/wireless/sysfs.c
+ */
+
+#include <linux/device.h>
+
+#include <net/cfg802154.h>
+
+#include "core.h"
+#include "sysfs.h"
+
+static inline struct cfg802154_registered_device *
+dev_to_rdev(struct device *dev)
+{
+ return container_of(dev, struct cfg802154_registered_device,
+ wpan_phy.dev);
+}
+
+#define SHOW_FMT(name, fmt, member) \
+static ssize_t name ## _show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ return sprintf(buf, fmt "\n", dev_to_rdev(dev)->member); \
+} \
+static DEVICE_ATTR_RO(name)
+
+SHOW_FMT(index, "%d", wpan_phy_idx);
+
+static ssize_t name_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct wpan_phy *wpan_phy = &dev_to_rdev(dev)->wpan_phy;
+
+ return sprintf(buf, "%s\n", dev_name(&wpan_phy->dev));
+}
+static DEVICE_ATTR_RO(name);
+
+#define MASTER_SHOW_COMPLEX(name, format_string, args...) \
+static ssize_t name ## _show(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev); \
+ int ret; \
+ \
+ mutex_lock(&phy->pib_lock); \
+ ret = snprintf(buf, PAGE_SIZE, format_string "\n", args); \
+ mutex_unlock(&phy->pib_lock); \
+ return ret; \
+} \
+static DEVICE_ATTR_RO(name)
+
+#define MASTER_SHOW(field, format_string) \
+ MASTER_SHOW_COMPLEX(field, format_string, phy->field)
+
+MASTER_SHOW(current_channel, "%d");
+MASTER_SHOW(current_page, "%d");
+MASTER_SHOW(transmit_power, "%d +- 1 dB");
+MASTER_SHOW(cca_mode, "%d");
+
+static ssize_t channels_supported_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev);
+ int ret;
+ int i, len = 0;
+
+ mutex_lock(&phy->pib_lock);
+ for (i = 0; i < 32; i++) {
+ ret = snprintf(buf + len, PAGE_SIZE - len,
+ "%#09x\n", phy->channels_supported[i]);
+ if (ret < 0)
+ break;
+ len += ret;
+ }
+ mutex_unlock(&phy->pib_lock);
+ return len;
+}
+static DEVICE_ATTR_RO(channels_supported);
+
+static void wpan_phy_release(struct device *dev)
+{
+ struct cfg802154_registered_device *rdev = dev_to_rdev(dev);
+
+ cfg802154_dev_free(rdev);
+}
+
+static struct attribute *pmib_attrs[] = {
+ &dev_attr_index.attr,
+ &dev_attr_name.attr,
+ /* below will be removed soon */
+ &dev_attr_current_channel.attr,
+ &dev_attr_current_page.attr,
+ &dev_attr_channels_supported.attr,
+ &dev_attr_transmit_power.attr,
+ &dev_attr_cca_mode.attr,
+ NULL,
+};
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
+ATTRIBUTE_GROUPS(pmib);
+#else
+#define BP_ATTR_GRP_STRUCT device_attribute
+ATTRIBUTE_GROUPS_BACKPORT(pmib);
+#endif
+
+struct class wpan_phy_class = {
+ .name = "ieee802154",
+ .dev_release = wpan_phy_release,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
+ .dev_groups = pmib_groups,
+#else
+ .dev_attrs = pmib_dev_attrs,
+#endif
+};
+
+int wpan_phy_sysfs_init(void)
+{
+ init_pmib_attrs();
+ return class_register(&wpan_phy_class);
+}
+
+void wpan_phy_sysfs_exit(void)
+{
+ class_unregister(&wpan_phy_class);
+}
diff --git a/net/ieee802154/sysfs.h b/net/ieee802154/sysfs.h
new file mode 100644
index 0000000..aa42e39
--- /dev/null
+++ b/net/ieee802154/sysfs.h
@@ -0,0 +1,9 @@
+#ifndef __IEEE802154_SYSFS_H
+#define __IEEE802154_SYSFS_H
+
+int wpan_phy_sysfs_init(void);
+void wpan_phy_sysfs_exit(void);
+
+extern struct class wpan_phy_class;
+
+#endif /* __IEEE802154_SYSFS_H */
diff --git a/net/ieee802154/wpan-class.c b/net/ieee802154/wpan-class.c
deleted file mode 100644
index 06ca147..0000000
--- a/net/ieee802154/wpan-class.c
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * Copyright (C) 2007, 2008, 2009 Siemens AG
- *
- * 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.
- *
- * 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 <linux/slab.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/device.h>
-
-#include <net/wpan-phy.h>
-
-#include "ieee802154.h"
-
-#define MASTER_SHOW_COMPLEX(name, format_string, args...) \
-static ssize_t name ## _show(struct device *dev, \
- struct device_attribute *attr, char *buf) \
-{ \
- struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev); \
- int ret; \
- \
- mutex_lock(&phy->pib_lock); \
- ret = snprintf(buf, PAGE_SIZE, format_string "\n", args); \
- mutex_unlock(&phy->pib_lock); \
- return ret; \
-} \
-static DEVICE_ATTR_RO(name);
-
-#define MASTER_SHOW(field, format_string) \
- MASTER_SHOW_COMPLEX(field, format_string, phy->field)
-
-MASTER_SHOW(current_channel, "%d");
-MASTER_SHOW(current_page, "%d");
-MASTER_SHOW(transmit_power, "%d +- 1 dB");
-MASTER_SHOW(cca_mode, "%d");
-
-static ssize_t channels_supported_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev);
- int ret;
- int i, len = 0;
-
- mutex_lock(&phy->pib_lock);
- for (i = 0; i < 32; i++) {
- ret = snprintf(buf + len, PAGE_SIZE - len,
- "%#09x\n", phy->channels_supported[i]);
- if (ret < 0)
- break;
- len += ret;
- }
- mutex_unlock(&phy->pib_lock);
- return len;
-}
-static DEVICE_ATTR_RO(channels_supported);
-
-static struct attribute *pmib_attrs[] = {
- &dev_attr_current_channel.attr,
- &dev_attr_current_page.attr,
- &dev_attr_channels_supported.attr,
- &dev_attr_transmit_power.attr,
- &dev_attr_cca_mode.attr,
- NULL,
-};
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
-ATTRIBUTE_GROUPS(pmib);
-#else
-#define BP_ATTR_GRP_STRUCT device_attribute
-ATTRIBUTE_GROUPS_BACKPORT(pmib);
-#endif
-
-static void wpan_phy_release(struct device *d)
-{
- struct wpan_phy *phy = container_of(d, struct wpan_phy, dev);
-
- kfree(phy);
-}
-
-static struct class wpan_phy_class = {
- .name = "ieee802154",
- .dev_release = wpan_phy_release,
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
- .dev_groups = pmib_groups,
-#else
- .dev_attrs = pmib_dev_attrs,
-#endif
-};
-
-static DEFINE_MUTEX(wpan_phy_mutex);
-static int wpan_phy_idx;
-
-static int wpan_phy_match(struct device *dev, const void *data)
-{
- return !strcmp(dev_name(dev), (const char *)data);
-}
-
-struct wpan_phy *wpan_phy_find(const char *str)
-{
- struct device *dev;
-
- if (WARN_ON(!str))
- return NULL;
-
- dev = class_find_device(&wpan_phy_class, NULL, str, wpan_phy_match);
- if (!dev)
- return NULL;
-
- return container_of(dev, struct wpan_phy, dev);
-}
-EXPORT_SYMBOL(wpan_phy_find);
-
-struct wpan_phy_iter_data {
- int (*fn)(struct wpan_phy *phy, void *data);
- void *data;
-};
-
-static int wpan_phy_iter(struct device *dev, void *_data)
-{
- struct wpan_phy_iter_data *wpid = _data;
- struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev);
-
- return wpid->fn(phy, wpid->data);
-}
-
-int wpan_phy_for_each(int (*fn)(struct wpan_phy *phy, void *data),
- void *data)
-{
- struct wpan_phy_iter_data wpid = {
- .fn = fn,
- .data = data,
- };
-
- return class_for_each_device(&wpan_phy_class, NULL,
- &wpid, wpan_phy_iter);
-}
-EXPORT_SYMBOL(wpan_phy_for_each);
-
-static int wpan_phy_idx_valid(int idx)
-{
- return idx >= 0;
-}
-
-struct wpan_phy *wpan_phy_alloc(size_t priv_size)
-{
- struct wpan_phy *phy = kzalloc(sizeof(*phy) + priv_size,
- GFP_KERNEL);
-
- if (!phy)
- goto out;
- mutex_lock(&wpan_phy_mutex);
- phy->idx = wpan_phy_idx++;
- if (unlikely(!wpan_phy_idx_valid(phy->idx))) {
- wpan_phy_idx--;
- mutex_unlock(&wpan_phy_mutex);
- kfree(phy);
- goto out;
- }
- mutex_unlock(&wpan_phy_mutex);
-
- mutex_init(&phy->pib_lock);
-
- device_initialize(&phy->dev);
- dev_set_name(&phy->dev, "wpan-phy%d", phy->idx);
-
- phy->dev.class = &wpan_phy_class;
-
- phy->current_channel = -1; /* not initialised */
- phy->current_page = 0; /* for compatibility */
-
- return phy;
-
-out:
- return NULL;
-}
-EXPORT_SYMBOL(wpan_phy_alloc);
-
-int wpan_phy_register(struct wpan_phy *phy)
-{
- return device_add(&phy->dev);
-}
-EXPORT_SYMBOL(wpan_phy_register);
-
-void wpan_phy_unregister(struct wpan_phy *phy)
-{
- device_del(&phy->dev);
-}
-EXPORT_SYMBOL(wpan_phy_unregister);
-
-void wpan_phy_free(struct wpan_phy *phy)
-{
- put_device(&phy->dev);
-}
-EXPORT_SYMBOL(wpan_phy_free);
-
-static int __init wpan_phy_class_init(void)
-{
- int rc;
-
- init_pmib_attrs();
- rc = class_register(&wpan_phy_class);
- if (rc)
- goto err;
-
- rc = ieee802154_nl_init();
- if (rc)
- goto err_nl;
-
- return 0;
-err_nl:
- class_unregister(&wpan_phy_class);
-err:
- return rc;
-}
-subsys_initcall(wpan_phy_class_init);
-
-static void __exit wpan_phy_class_exit(void)
-{
- ieee802154_nl_exit();
- class_unregister(&wpan_phy_class);
-}
-module_exit(wpan_phy_class_exit);
-
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("IEEE 802.15.4 configuration interface");
-MODULE_AUTHOR("Dmitry Eremin-Solenikov");
-
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig
index 60677e1..543f13a 100644
--- a/net/mac80211/Kconfig
+++ b/net/mac80211/Kconfig
@@ -5,9 +5,9 @@
depends on CRYPTO
depends on CRYPTO_ARC4
depends on CRYPTO_AES
- select BACKPORT_CRYPTO_CCM
+ select BPAUTO_CRYPTO_CCM
depends on CRC32
- select BACKPORT_AVERAGE
+ select BPAUTO_AVERAGE
---help---
This option enables the hardware independent IEEE 802.11
networking stack.
@@ -34,6 +34,13 @@
---help---
This option enables the 'minstrel_ht' TX rate control algorithm
+config MAC80211_RC_MINSTREL_VHT
+ bool "Minstrel 802.11ac support" if EXPERT
+ depends on MAC80211_RC_MINSTREL_HT
+ default n
+ ---help---
+ This option enables VHT in the 'minstrel_ht' TX rate control algorithm
+
choice
prompt "Default rate control algorithm"
depends on MAC80211_HAS_RC
@@ -78,7 +85,7 @@
bool "Enable LED triggers"
depends on MAC80211
depends on LEDS_CLASS
- select BACKPORT_LEDS_TRIGGERS
+ select BPAUTO_LEDS_TRIGGERS
---help---
This option enables a few LED triggers for different
packet receive/transmit events.
@@ -170,6 +177,17 @@
Do not select this option.
+config MAC80211_OCB_DEBUG
+ bool "Verbose OCB debugging"
+ depends on MAC80211_DEBUG_MENU
+ ---help---
+ Selecting this option causes mac80211 to print out
+ very verbose OCB debugging messages. It should not
+ be selected on production systems as those messages
+ are remotely triggerable.
+
+ Do not select this option.
+
config MAC80211_IBSS_DEBUG
bool "Verbose IBSS debugging"
depends on MAC80211_DEBUG_MENU
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index 285af62..c09aa25 100644
--- a/net/mac80211/Makefile
+++ b/net/mac80211/Makefile
@@ -27,7 +27,8 @@
event.o \
chan.o \
trace.o mlme.o \
- tdls.o
+ tdls.o \
+ ocb.o
mac80211-$(CPTCFG_MAC80211_LEDS) += led.o
mac80211-$(CPTCFG_MAC80211_DEBUGFS) += \
diff --git a/net/mac80211/aes_ccm.c b/net/mac80211/aes_ccm.c
index ec24378..09d9caa 100644
--- a/net/mac80211/aes_ccm.c
+++ b/net/mac80211/aes_ccm.c
@@ -53,6 +53,9 @@
__aligned(__alignof__(struct aead_request));
struct aead_request *aead_req = (void *) aead_req_data;
+ if (data_len == 0)
+ return -EINVAL;
+
memset(aead_req, 0, sizeof(aead_req_data));
sg_init_one(&pt, data, data_len);
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index d6986f3..a360c15 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -149,11 +149,6 @@
rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], tid_tx);
}
-static inline int ieee80211_ac_from_tid(int tid)
-{
- return ieee802_1d_to_ac[tid & 7];
-}
-
/*
* When multiple aggregation sessions on multiple stations
* are being created/destroyed simultaneously, we need to
@@ -514,6 +509,10 @@
struct tid_ampdu_tx *tid_tx;
int ret = 0;
+ if (WARN(sta->reserved_tid == tid,
+ "Requested to start BA session on reserved tid=%d", tid))
+ return -EINVAL;
+
trace_api_start_tx_ba_session(pubsta, tid);
if (WARN_ON_ONCE(!local->ops->ampdu_action))
@@ -770,6 +769,9 @@
goto unlock;
}
+ WARN(sta->reserved_tid == tid,
+ "Requested to stop BA session on reserved tid=%d", tid);
+
if (test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) {
/* already in progress stopping it */
ret = 0;
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index a46ff74..a527ac3 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -20,6 +20,7 @@
#include "cfg.h"
#include "rate.h"
#include "mesh.h"
+#include "wme.h"
static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy,
const char *name,
@@ -190,7 +191,7 @@
* receive the key. When wpa_supplicant has roamed
* using FT, it attempts to set the key before
* association has completed, this rejects that attempt
- * so it will set the key again after assocation.
+ * so it will set the key again after association.
*
* TODO: accept the key if we have a station entry and
* add it to the device after the station.
@@ -229,6 +230,7 @@
case NUM_NL80211_IFTYPES:
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_OCB:
/* shouldn't happen */
WARN_ON_ONCE(1);
break;
@@ -1040,6 +1042,13 @@
clear_sta_flag(sta, WLAN_STA_TDLS_PEER);
}
+ /* mark TDLS channel switch support, if the AP allows it */
+ if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
+ !sdata->u.mgd.tdls_chan_switch_prohibited &&
+ params->ext_capab_len >= 4 &&
+ params->ext_capab[3] & WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH)
+ set_sta_flag(sta, WLAN_STA_TDLS_CHAN_SWITCH);
+
if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD) {
sta->sta.uapsd_queues = params->uapsd_queues;
sta->sta.max_sp = params->max_sp;
@@ -1225,14 +1234,14 @@
}
static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
- const u8 *mac)
+ struct station_del_parameters *params)
{
struct ieee80211_sub_if_data *sdata;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (mac)
- return sta_info_destroy_addr_bss(sdata, mac);
+ if (params->mac)
+ return sta_info_destroy_addr_bss(sdata, params->mac);
sta_info_flush(sdata);
return 0;
@@ -1516,6 +1525,57 @@
return 0;
}
+static void mpp_set_pinfo(struct mesh_path *mpath, u8 *mpp,
+ struct mpath_info *pinfo)
+{
+ memset(pinfo, 0, sizeof(*pinfo));
+ memcpy(mpp, mpath->mpp, ETH_ALEN);
+
+ pinfo->generation = mpp_paths_generation;
+}
+
+static int ieee80211_get_mpp(struct wiphy *wiphy, struct net_device *dev,
+ u8 *dst, u8 *mpp, struct mpath_info *pinfo)
+
+{
+ struct ieee80211_sub_if_data *sdata;
+ struct mesh_path *mpath;
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ rcu_read_lock();
+ mpath = mpp_path_lookup(sdata, dst);
+ if (!mpath) {
+ rcu_read_unlock();
+ return -ENOENT;
+ }
+ memcpy(dst, mpath->dst, ETH_ALEN);
+ mpp_set_pinfo(mpath, mpp, pinfo);
+ rcu_read_unlock();
+ return 0;
+}
+
+static int ieee80211_dump_mpp(struct wiphy *wiphy, struct net_device *dev,
+ int idx, u8 *dst, u8 *mpp,
+ struct mpath_info *pinfo)
+{
+ struct ieee80211_sub_if_data *sdata;
+ struct mesh_path *mpath;
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ rcu_read_lock();
+ mpath = mpp_path_lookup_by_idx(sdata, idx);
+ if (!mpath) {
+ rcu_read_unlock();
+ return -ENOENT;
+ }
+ memcpy(dst, mpath->dst, ETH_ALEN);
+ mpp_set_pinfo(mpath, mpp, pinfo);
+ rcu_read_unlock();
+ return 0;
+}
+
static int ieee80211_get_mesh_config(struct wiphy *wiphy,
struct net_device *dev,
struct mesh_config *conf)
@@ -1966,6 +2026,17 @@
return ieee80211_ibss_leave(IEEE80211_DEV_TO_SUB_IF(dev));
}
+static int ieee80211_join_ocb(struct wiphy *wiphy, struct net_device *dev,
+ struct ocb_setup *setup)
+{
+ return ieee80211_ocb_join(IEEE80211_DEV_TO_SUB_IF(dev), setup);
+}
+
+static int ieee80211_leave_ocb(struct wiphy *wiphy, struct net_device *dev)
+{
+ return ieee80211_ocb_leave(IEEE80211_DEV_TO_SUB_IF(dev));
+}
+
static int ieee80211_set_mcast_rate(struct wiphy *wiphy, struct net_device *dev,
int rate[IEEE80211_NUM_BANDS])
{
@@ -2081,6 +2152,9 @@
struct ieee80211_local *local = wiphy_priv(wiphy);
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+ if (local->ops->get_txpower)
+ return drv_get_txpower(local, sdata, dbm);
+
if (!local->use_chanctx)
*dbm = local->hw.conf.power_level;
else
@@ -2850,11 +2924,7 @@
if (sdata->reserved_ready)
return 0;
- err = ieee80211_vif_use_reserved_context(sdata);
- if (err)
- return err;
-
- return 0;
+ return ieee80211_vif_use_reserved_context(sdata);
}
if (!cfg80211_chandef_identical(&sdata->vif.bss_conf.chandef,
@@ -2868,7 +2938,6 @@
return err;
ieee80211_bss_info_change_notify(sdata, changed);
- cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef);
if (sdata->csa_block_tx) {
ieee80211_wake_vif_queues(local, sdata,
@@ -2876,6 +2945,12 @@
sdata->csa_block_tx = false;
}
+ err = drv_post_channel_switch(sdata);
+ if (err)
+ return err;
+
+ cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef);
+
return 0;
}
@@ -3053,9 +3128,11 @@
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
+ struct ieee80211_channel_switch ch_switch;
struct ieee80211_chanctx_conf *conf;
struct ieee80211_chanctx *chanctx;
- int err, changed = 0;
+ u32 changed = 0;
+ int err;
sdata_assert_lock(sdata);
lockdep_assert_held(&local->mtx);
@@ -3088,6 +3165,16 @@
goto out;
}
+ ch_switch.timestamp = 0;
+ ch_switch.device_timestamp = 0;
+ ch_switch.block_tx = params->block_tx;
+ ch_switch.chandef = params->chandef;
+ ch_switch.count = params->count;
+
+ err = drv_pre_channel_switch(sdata, &ch_switch);
+ if (err)
+ goto out;
+
err = ieee80211_vif_reserve_chanctx(sdata, ¶ms->chandef,
chanctx->mode,
params->radar_required);
@@ -3115,6 +3202,9 @@
ieee80211_stop_vif_queues(local, sdata,
IEEE80211_QUEUE_STOP_REASON_CSA);
+ cfg80211_ch_switch_started_notify(sdata->dev, &sdata->csa_chandef,
+ params->count);
+
if (changed) {
ieee80211_bss_info_change_notify(sdata, changed);
drv_channel_switch_beacon(sdata, ¶ms->chandef);
@@ -3431,6 +3521,7 @@
info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS |
IEEE80211_TX_INTFL_NL80211_FRAME_TX;
+ info->band = band;
skb_set_queue_mapping(skb, IEEE80211_AC_VO);
skb->priority = 7;
@@ -3438,7 +3529,7 @@
nullfunc->qos_ctrl = cpu_to_le16(7);
local_bh_disable();
- ieee80211_xmit(sdata, skb, band);
+ ieee80211_xmit(sdata, skb);
local_bh_enable();
rcu_read_unlock();
@@ -3458,7 +3549,7 @@
rcu_read_lock();
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (chanctx_conf) {
- *chandef = chanctx_conf->def;
+ *chandef = sdata->vif.bss_conf.chandef;
ret = 0;
} else if (local->open_count > 0 &&
local->open_count == local->monitors &&
@@ -3521,6 +3612,76 @@
return ret;
}
+static int ieee80211_add_tx_ts(struct wiphy *wiphy, struct net_device *dev,
+ u8 tsid, const u8 *peer, u8 up,
+ u16 admitted_time)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ int ac = ieee802_1d_to_ac[up];
+
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ return -EOPNOTSUPP;
+
+ if (!(sdata->wmm_acm & BIT(up)))
+ return -EINVAL;
+
+ if (ifmgd->tx_tspec[ac].admitted_time)
+ return -EBUSY;
+
+ if (admitted_time) {
+ ifmgd->tx_tspec[ac].admitted_time = 32 * admitted_time;
+ ifmgd->tx_tspec[ac].tsid = tsid;
+ ifmgd->tx_tspec[ac].up = up;
+ }
+
+ return 0;
+}
+
+static int ieee80211_del_tx_ts(struct wiphy *wiphy, struct net_device *dev,
+ u8 tsid, const u8 *peer)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+ int ac;
+
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+ struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac];
+
+ /* skip unused entries */
+ if (!tx_tspec->admitted_time)
+ continue;
+
+ if (tx_tspec->tsid != tsid)
+ continue;
+
+ /* due to this new packets will be reassigned to non-ACM ACs */
+ tx_tspec->up = -1;
+
+ /* Make sure that all packets have been sent to avoid to
+ * restore the QoS params on packets that are still on the
+ * queues.
+ */
+ synchronize_net();
+ ieee80211_flush_queues(local, sdata);
+
+ /* restore the normal QoS parameters
+ * (unconditionally to avoid races)
+ */
+ tx_tspec->action = TX_TSPEC_ACTION_STOP_DOWNGRADE;
+ tx_tspec->downgraded = false;
+ ieee80211_sta_handle_tspec_ac_params(sdata);
+
+ /* finally clear all the data */
+ memset(tx_tspec, 0, sizeof(*tx_tspec));
+
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
const struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface,
@@ -3547,11 +3708,15 @@
.change_mpath = ieee80211_change_mpath,
.get_mpath = ieee80211_get_mpath,
.dump_mpath = ieee80211_dump_mpath,
+ .get_mpp = ieee80211_get_mpp,
+ .dump_mpp = ieee80211_dump_mpp,
.update_mesh_config = ieee80211_update_mesh_config,
.get_mesh_config = ieee80211_get_mesh_config,
.join_mesh = ieee80211_join_mesh,
.leave_mesh = ieee80211_leave_mesh,
#endif
+ .join_ocb = ieee80211_join_ocb,
+ .leave_ocb = ieee80211_leave_ocb,
.change_bss = ieee80211_change_bss,
.set_txq_params = ieee80211_set_txq_params,
.set_monitor_channel = ieee80211_set_monitor_channel,
@@ -3587,6 +3752,8 @@
.set_rekey_data = ieee80211_set_rekey_data,
.tdls_oper = ieee80211_tdls_oper,
.tdls_mgmt = ieee80211_tdls_mgmt,
+ .tdls_channel_switch = ieee80211_tdls_channel_switch,
+ .tdls_cancel_channel_switch = ieee80211_tdls_cancel_channel_switch,
.probe_client = ieee80211_probe_client,
.set_noack_map = ieee80211_set_noack_map,
#ifdef CONFIG_PM
@@ -3597,4 +3764,6 @@
.channel_switch = ieee80211_channel_switch,
.set_qos_map = ieee80211_set_qos_map,
.set_ap_chanwidth = ieee80211_set_ap_chanwidth,
+ .add_tx_ts = ieee80211_add_tx_ts,
+ .del_tx_ts = ieee80211_del_tx_ts,
};
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 4c74e8d..da1c12c 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -270,6 +270,7 @@
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_WDS:
case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_OCB:
width = vif->bss_conf.chandef.width;
break;
case NL80211_IFTYPE_UNSPECIFIED:
@@ -674,6 +675,7 @@
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_WDS:
case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_OCB:
break;
default:
WARN_ON_ONCE(1);
@@ -909,6 +911,7 @@
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_OCB:
ieee80211_queue_work(&sdata->local->hw,
&sdata->csa_finalize_work);
break;
@@ -929,6 +932,21 @@
}
}
+static void
+ieee80211_vif_update_chandef(struct ieee80211_sub_if_data *sdata,
+ const struct cfg80211_chan_def *chandef)
+{
+ struct ieee80211_sub_if_data *vlan;
+
+ sdata->vif.bss_conf.chandef = *chandef;
+
+ if (sdata->vif.type != NL80211_IFTYPE_AP)
+ return;
+
+ list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
+ vlan->vif.bss_conf.chandef = *chandef;
+}
+
static int
ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data *sdata)
{
@@ -991,7 +1009,11 @@
if (sdata->vif.bss_conf.chandef.width != sdata->reserved_chandef.width)
changed = BSS_CHANGED_BANDWIDTH;
- sdata->vif.bss_conf.chandef = sdata->reserved_chandef;
+ ieee80211_vif_update_chandef(sdata, &sdata->reserved_chandef);
+
+ ieee80211_recalc_smps_chanctx(local, new_ctx);
+ ieee80211_recalc_radar_chanctx(local, new_ctx);
+ ieee80211_recalc_chanctx_min_def(local, new_ctx);
if (changed)
ieee80211_bss_info_change_notify(sdata, changed);
@@ -1333,7 +1355,7 @@
sdata->reserved_chandef.width)
changed = BSS_CHANGED_BANDWIDTH;
- sdata->vif.bss_conf.chandef = sdata->reserved_chandef;
+ ieee80211_vif_update_chandef(sdata, &sdata->reserved_chandef);
if (changed)
ieee80211_bss_info_change_notify(sdata,
changed);
@@ -1504,7 +1526,7 @@
goto out;
}
- sdata->vif.bss_conf.chandef = *chandef;
+ ieee80211_vif_update_chandef(sdata, chandef);
ret = ieee80211_assign_vif_chanctx(sdata, ctx);
if (ret) {
@@ -1634,7 +1656,7 @@
}
break;
case IEEE80211_CHANCTX_WILL_BE_REPLACED:
- /* TODO: Perhaps the bandwith change could be treated as a
+ /* TODO: Perhaps the bandwidth change could be treated as a
* reservation itself? */
ret = -EBUSY;
goto out;
@@ -1646,7 +1668,7 @@
break;
}
- sdata->vif.bss_conf.chandef = *chandef;
+ ieee80211_vif_update_chandef(sdata, chandef);
ieee80211_recalc_chanctx_chantype(local, ctx);
diff --git a/net/mac80211/debug.h b/net/mac80211/debug.h
index ddbc904..c0ba105 100644
--- a/net/mac80211/debug.h
+++ b/net/mac80211/debug.h
@@ -2,6 +2,12 @@
#define __MAC80211_DEBUG_H
#include <net/cfg80211.h>
+#ifdef CPTCFG_MAC80211_OCB_DEBUG
+#define MAC80211_OCB_DEBUG 1
+#else
+#define MAC80211_OCB_DEBUG 0
+#endif
+
#ifdef CPTCFG_MAC80211_IBSS_DEBUG
#define MAC80211_IBSS_DEBUG 1
#else
@@ -131,6 +137,10 @@
_sdata_dbg(MAC80211_HT_DEBUG && net_ratelimit(), \
sdata, fmt, ##__VA_ARGS__)
+#define ocb_dbg(sdata, fmt, ...) \
+ _sdata_dbg(MAC80211_OCB_DEBUG, \
+ sdata, fmt, ##__VA_ARGS__)
+
#define ibss_dbg(sdata, fmt, ...) \
_sdata_dbg(MAC80211_IBSS_DEBUG, \
sdata, fmt, ##__VA_ARGS__)
diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c
index 1521cab..5523b94 100644
--- a/net/mac80211/debugfs_key.c
+++ b/net/mac80211/debugfs_key.c
@@ -300,10 +300,8 @@
lockdep_assert_held(&sdata->local->key_mtx);
- if (sdata->debugfs.default_unicast_key) {
- debugfs_remove(sdata->debugfs.default_unicast_key);
- sdata->debugfs.default_unicast_key = NULL;
- }
+ debugfs_remove(sdata->debugfs.default_unicast_key);
+ sdata->debugfs.default_unicast_key = NULL;
if (sdata->default_unicast_key) {
key = key_mtx_dereference(sdata->local,
@@ -314,10 +312,8 @@
sdata->vif.debugfs_dir, buf);
}
- if (sdata->debugfs.default_multicast_key) {
- debugfs_remove(sdata->debugfs.default_multicast_key);
- sdata->debugfs.default_multicast_key = NULL;
- }
+ debugfs_remove(sdata->debugfs.default_multicast_key);
+ sdata->debugfs.default_multicast_key = NULL;
if (sdata->default_multicast_key) {
key = key_mtx_dereference(sdata->local,
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index bafe489..94c7009 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -74,7 +74,7 @@
test_sta_flag(sta, WLAN_STA_##flg) ? #flg "\n" : ""
int res = scnprintf(buf, sizeof(buf),
- "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
TEST(AUTH), TEST(ASSOC), TEST(PS_STA),
TEST(PS_DRIVER), TEST(AUTHORIZED),
TEST(SHORT_PREAMBLE),
@@ -82,10 +82,11 @@
TEST(WDS), TEST(CLEAR_PS_FILT),
TEST(MFP), TEST(BLOCK_BA), TEST(PSPOLL),
TEST(UAPSD), TEST(SP), TEST(TDLS_PEER),
- TEST(TDLS_PEER_AUTH), TEST(4ADDR_EVENT),
- TEST(INSERTED), TEST(RATE_CONTROL),
- TEST(TOFFSET_KNOWN), TEST(MPSP_OWNER),
- TEST(MPSP_RECIPIENT));
+ TEST(TDLS_PEER_AUTH), TEST(TDLS_INITIATOR),
+ TEST(TDLS_CHAN_SWITCH), TEST(TDLS_OFF_CHANNEL),
+ TEST(4ADDR_EVENT), TEST(INSERTED),
+ TEST(RATE_CONTROL), TEST(TOFFSET_KNOWN),
+ TEST(MPSP_OWNER), TEST(MPSP_RECIPIENT));
#undef TEST
return simple_read_from_buffer(userbuf, count, ppos, buf, res);
}
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 6da526b..754c893 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -214,7 +214,8 @@
BSS_CHANGED_BEACON_ENABLED) &&
sdata->vif.type != NL80211_IFTYPE_AP &&
sdata->vif.type != NL80211_IFTYPE_ADHOC &&
- sdata->vif.type != NL80211_IFTYPE_MESH_POINT))
+ sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
+ sdata->vif.type != NL80211_IFTYPE_OCB))
return;
if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE ||
@@ -379,23 +380,26 @@
return ret;
}
-static inline void drv_sw_scan_start(struct ieee80211_local *local)
+static inline void drv_sw_scan_start(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ const u8 *mac_addr)
{
might_sleep();
- trace_drv_sw_scan_start(local);
+ trace_drv_sw_scan_start(local, sdata, mac_addr);
if (local->ops->sw_scan_start)
- local->ops->sw_scan_start(&local->hw);
+ local->ops->sw_scan_start(&local->hw, &sdata->vif, mac_addr);
trace_drv_return_void(local);
}
-static inline void drv_sw_scan_complete(struct ieee80211_local *local)
+static inline void drv_sw_scan_complete(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata)
{
might_sleep();
- trace_drv_sw_scan_complete(local);
+ trace_drv_sw_scan_complete(local, sdata);
if (local->ops->sw_scan_complete)
- local->ops->sw_scan_complete(&local->hw);
+ local->ops->sw_scan_complete(&local->hw, &sdata->vif);
trace_drv_return_void(local);
}
@@ -620,6 +624,21 @@
trace_drv_return_void(local);
}
+static inline void drv_sta_rate_tbl_update(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta)
+{
+ sdata = get_bss_sdata(sdata);
+ if (!check_sdata_in_driver(sdata))
+ return;
+
+ trace_drv_sta_rate_tbl_update(local, sdata, sta);
+ if (local->ops->sta_rate_tbl_update)
+ local->ops->sta_rate_tbl_update(&local->hw, &sdata->vif, sta);
+
+ trace_drv_return_void(local);
+}
+
static inline int drv_conf_tx(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata, u16 ac,
const struct ieee80211_tx_queue_params *params)
@@ -631,6 +650,12 @@
if (!check_sdata_in_driver(sdata))
return -EIO;
+ if (WARN_ONCE(params->cw_min == 0 ||
+ params->cw_min > params->cw_max,
+ "%s: invalid CW_min/CW_max: %d/%d\n",
+ sdata->name, params->cw_min, params->cw_max))
+ return -EINVAL;
+
trace_drv_conf_tx(local, sdata, ac, params);
if (local->ops->conf_tx)
ret = local->ops->conf_tx(&local->hw, &sdata->vif,
@@ -764,12 +789,13 @@
}
static inline void drv_channel_switch(struct ieee80211_local *local,
- struct ieee80211_channel_switch *ch_switch)
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_channel_switch *ch_switch)
{
might_sleep();
- trace_drv_channel_switch(local, ch_switch);
- local->ops->channel_switch(&local->hw, ch_switch);
+ trace_drv_channel_switch(local, sdata, ch_switch);
+ local->ops->channel_switch(&local->hw, &sdata->vif, ch_switch);
trace_drv_return_void(local);
}
@@ -1144,13 +1170,15 @@
trace_drv_return_void(local);
}
-static inline void drv_restart_complete(struct ieee80211_local *local)
+static inline void
+drv_reconfig_complete(struct ieee80211_local *local,
+ enum ieee80211_reconfig_type reconfig_type)
{
might_sleep();
- trace_drv_restart_complete(local);
- if (local->ops->restart_complete)
- local->ops->restart_complete(&local->hw);
+ trace_drv_reconfig_complete(local, reconfig_type);
+ if (local->ops->reconfig_complete)
+ local->ops->reconfig_complete(&local->hw, reconfig_type);
trace_drv_return_void(local);
}
@@ -1196,6 +1224,40 @@
}
}
+static inline int
+drv_pre_channel_switch(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_channel_switch *ch_switch)
+{
+ struct ieee80211_local *local = sdata->local;
+ int ret = 0;
+
+ if (!check_sdata_in_driver(sdata))
+ return -EIO;
+
+ trace_drv_pre_channel_switch(local, sdata, ch_switch);
+ if (local->ops->pre_channel_switch)
+ ret = local->ops->pre_channel_switch(&local->hw, &sdata->vif,
+ ch_switch);
+ trace_drv_return_int(local, ret);
+ return ret;
+}
+
+static inline int
+drv_post_channel_switch(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_local *local = sdata->local;
+ int ret = 0;
+
+ if (!check_sdata_in_driver(sdata))
+ return -EIO;
+
+ trace_drv_post_channel_switch(local, sdata);
+ if (local->ops->post_channel_switch)
+ ret = local->ops->post_channel_switch(&local->hw, &sdata->vif);
+ trace_drv_return_int(local, ret);
+ return ret;
+}
+
static inline int drv_join_ibss(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata)
{
@@ -1238,4 +1300,71 @@
return ret;
}
+static inline int drv_get_txpower(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata, int *dbm)
+{
+ int ret;
+
+ if (!local->ops->get_txpower)
+ return -EOPNOTSUPP;
+
+ ret = local->ops->get_txpower(&local->hw, &sdata->vif, dbm);
+ trace_drv_get_txpower(local, sdata, *dbm, ret);
+
+ return ret;
+}
+
+static inline int
+drv_tdls_channel_switch(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta, u8 oper_class,
+ struct cfg80211_chan_def *chandef,
+ struct sk_buff *tmpl_skb, u32 ch_sw_tm_ie)
+{
+ int ret;
+
+ might_sleep();
+ if (!check_sdata_in_driver(sdata))
+ return -EIO;
+
+ if (!local->ops->tdls_channel_switch)
+ return -EOPNOTSUPP;
+
+ trace_drv_tdls_channel_switch(local, sdata, sta, oper_class, chandef);
+ ret = local->ops->tdls_channel_switch(&local->hw, &sdata->vif, sta,
+ oper_class, chandef, tmpl_skb,
+ ch_sw_tm_ie);
+ trace_drv_return_int(local, ret);
+ return ret;
+}
+
+static inline void
+drv_tdls_cancel_channel_switch(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta)
+{
+ might_sleep();
+ if (!check_sdata_in_driver(sdata))
+ return;
+
+ if (!local->ops->tdls_cancel_channel_switch)
+ return;
+
+ trace_drv_tdls_cancel_channel_switch(local, sdata, sta);
+ local->ops->tdls_cancel_channel_switch(&local->hw, &sdata->vif, sta);
+ trace_drv_return_void(local);
+}
+
+static inline void
+drv_tdls_recv_channel_switch(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_tdls_ch_sw_params *params)
+{
+ trace_drv_tdls_recv_channel_switch(local, sdata, params);
+ if (local->ops->tdls_recv_channel_switch)
+ local->ops->tdls_recv_channel_switch(&local->hw, &sdata->vif,
+ params);
+ trace_drv_return_void(local);
+}
+
#endif /* __MAC80211_DRIVER_OPS */
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 56b5357..509bc15 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -805,7 +805,7 @@
memset(¶ms, 0, sizeof(params));
memset(&csa_ie, 0, sizeof(csa_ie));
- err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon,
+ err = ieee80211_parse_ch_switch_ie(sdata, elems,
ifibss->chandef.chan->band,
sta_flags, ifibss->bssid, &csa_ie);
/* can't switch to destination channel, fail */
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index a02fa58..f08f4b9 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -131,7 +131,7 @@
*
* These are bss flags that are attached to a bss in the
* @valid_data field of &struct ieee80211_bss. They show which parts
- * of the data structure were recieved as a result of an un-corrupted
+ * of the data structure were received as a result of an un-corrupted
* beacon/probe response.
*/
enum ieee80211_bss_valid_data_flags {
@@ -399,6 +399,24 @@
u8 ie[];
};
+struct ieee80211_sta_tx_tspec {
+ /* timestamp of the first packet in the time slice */
+ unsigned long time_slice_start;
+
+ u32 admitted_time; /* in usecs, unlike over the air */
+ u8 tsid;
+ s8 up; /* signed to be able to invalidate with -1 during teardown */
+
+ /* consumed TX time in microseconds in the time slice */
+ u32 consumed_tx_time;
+ enum {
+ TX_TSPEC_ACTION_NONE = 0,
+ TX_TSPEC_ACTION_DOWNGRADE,
+ TX_TSPEC_ACTION_STOP_DOWNGRADE,
+ } action;
+ bool downgraded;
+};
+
struct ieee80211_if_managed {
struct timer_list timer;
struct timer_list conn_mon_timer;
@@ -434,6 +452,8 @@
unsigned int flags;
+ bool csa_waiting_bcn;
+
bool beacon_crc_valid;
u32 beacon_crc;
@@ -505,8 +525,23 @@
struct ieee80211_vht_cap vht_capa; /* configured VHT overrides */
struct ieee80211_vht_cap vht_capa_mask; /* Valid parts of vht_capa */
+ /* TDLS support */
u8 tdls_peer[ETH_ALEN] __aligned(2);
struct delayed_work tdls_peer_del_work;
+ struct sk_buff *orig_teardown_skb; /* The original teardown skb */
+ struct sk_buff *teardown_skb; /* A copy to send through the AP */
+ spinlock_t teardown_lock; /* To lock changing teardown_skb */
+ bool tdls_chan_switch_prohibited;
+
+ /* WMM-AC TSPEC support */
+ struct ieee80211_sta_tx_tspec tx_tspec[IEEE80211_NUM_ACS];
+ /* Use a separate work struct so that we can do something here
+ * while the sdata->work is flushing the queues, for example.
+ * otherwise, in scenarios where we hardly get any traffic out
+ * on the BE queue, but there's a lot of VO traffic, we might
+ * get stuck in a downgraded situation and flush takes forever.
+ */
+ struct delayed_work tx_tspec_wk;
};
struct ieee80211_if_ibss {
@@ -547,6 +582,25 @@
};
/**
+ * struct ieee80211_if_ocb - OCB mode state
+ *
+ * @housekeeping_timer: timer for periodic invocation of a housekeeping task
+ * @wrkq_flags: OCB deferred task action
+ * @incomplete_lock: delayed STA insertion lock
+ * @incomplete_stations: list of STAs waiting for delayed insertion
+ * @joined: indication if the interface is connected to an OCB network
+ */
+struct ieee80211_if_ocb {
+ struct timer_list housekeeping_timer;
+ unsigned long wrkq_flags;
+
+ spinlock_t incomplete_lock;
+ struct list_head incomplete_stations;
+
+ bool joined;
+};
+
+/**
* struct ieee80211_mesh_sync_ops - Extensible synchronization framework interface
*
* these declarations define the interface, which enables
@@ -839,6 +893,7 @@
struct ieee80211_if_managed mgd;
struct ieee80211_if_ibss ibss;
struct ieee80211_if_mesh mesh;
+ struct ieee80211_if_ocb ocb;
u32 mntr_flags;
} u;
@@ -938,6 +993,7 @@
IEEE80211_SDATA_QUEUE_AGG_STOP = 2,
IEEE80211_SDATA_QUEUE_RX_AGG_START = 3,
IEEE80211_SDATA_QUEUE_RX_AGG_STOP = 4,
+ IEEE80211_SDATA_QUEUE_TDLS_CHSW = 5,
};
enum {
@@ -955,6 +1011,7 @@
IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL,
IEEE80211_QUEUE_STOP_REASON_FLUSH,
IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN,
+ IEEE80211_QUEUE_STOP_REASON_RESERVE_TID,
IEEE80211_QUEUE_STOP_REASONS,
};
@@ -1181,7 +1238,7 @@
unsigned long scanning;
struct cfg80211_ssid scan_ssid;
struct cfg80211_scan_request *int_scan_req;
- struct cfg80211_scan_request *scan_req;
+ struct cfg80211_scan_request __rcu *scan_req;
struct ieee80211_scan_request *hw_scan_req;
struct cfg80211_chan_def scan_chandef;
enum ieee80211_band hw_scan_band;
@@ -1191,7 +1248,8 @@
struct work_struct sched_scan_stopped_work;
struct ieee80211_sub_if_data __rcu *sched_scan_sdata;
- struct cfg80211_sched_scan_request *sched_scan_req;
+ struct cfg80211_sched_scan_request __rcu *sched_scan_req;
+ u8 scan_addr[ETH_ALEN];
unsigned long leave_oper_channel_time;
enum mac80211_scan_state next_scan_state;
@@ -1310,6 +1368,9 @@
/* virtual monitor interface */
struct ieee80211_sub_if_data __rcu *monitor_sdata;
struct cfg80211_chan_def monitor_chandef;
+
+ /* extended capabilities provided by mac80211 */
+ u8 ext_capa[8];
};
static inline struct ieee80211_sub_if_data *
@@ -1345,6 +1406,9 @@
size_t total_len;
/* pointers to IEs */
+ const struct ieee80211_tdls_lnkie *lnk_id;
+ const struct ieee80211_ch_switch_timing *ch_sw_timing;
+ const u8 *ext_capab;
const u8 *ssid;
const u8 *supp_rates;
const u8 *ds_params;
@@ -1379,6 +1443,7 @@
const struct ieee80211_mesh_chansw_params_ie *mesh_chansw_params_ie;
/* length of them, respectively */
+ u8 ext_capab_len;
u8 ssid_len;
u8 supp_rates_len;
u8 tim_len;
@@ -1457,6 +1522,7 @@
__le16 fc, bool acked);
void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata);
void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata);
+void ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata);
/* IBSS code */
void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local);
@@ -1474,6 +1540,15 @@
int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata);
void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata);
+/* OCB code */
+void ieee80211_ocb_work(struct ieee80211_sub_if_data *sdata);
+void ieee80211_ocb_rx_no_sta(struct ieee80211_sub_if_data *sdata,
+ const u8 *bssid, const u8 *addr, u32 supp_rates);
+void ieee80211_ocb_setup_sdata(struct ieee80211_sub_if_data *sdata);
+int ieee80211_ocb_join(struct ieee80211_sub_if_data *sdata,
+ struct ocb_setup *setup);
+int ieee80211_ocb_leave(struct ieee80211_sub_if_data *sdata);
+
/* mesh code */
void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata);
void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
@@ -1565,8 +1640,14 @@
struct net_device *dev);
netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
struct net_device *dev);
+void __ieee80211_subif_start_xmit(struct sk_buff *skb,
+ struct net_device *dev,
+ u32 info_flags);
void ieee80211_purge_tx_queue(struct ieee80211_hw *hw,
struct sk_buff_head *skbs);
+struct sk_buff *
+ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, u32 info_flags);
/* HT */
void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
@@ -1645,7 +1726,6 @@
* ieee80211_parse_ch_switch_ie - parses channel switch IEs
* @sdata: the sdata of the interface which has received the frame
* @elems: parsed 802.11 elements received with the frame
- * @beacon: indicates if the frame was a beacon or probe response
* @current_band: indicates the current band
* @sta_flags: contains information about own capabilities and restrictions
* to decide which channel switch announcements can be accepted. Only the
@@ -1659,7 +1739,7 @@
* Return: 0 on success, <0 on error and >0 if there is nothing to parse.
*/
int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
- struct ieee802_11_elems *elems, bool beacon,
+ struct ieee802_11_elems *elems,
enum ieee80211_band current_band,
u32 sta_flags, u8 *bssid,
struct ieee80211_csa_ie *csa_ie);
@@ -1694,8 +1774,7 @@
gfp_t gfp);
void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
bool bss_notify);
-void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
- enum ieee80211_band band);
+void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb);
void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, int tid,
@@ -1761,6 +1840,13 @@
return true;
}
+extern const int ieee802_1d_to_ac[8];
+
+static inline int ieee80211_ac_from_tid(int tid)
+{
+ return ieee802_1d_to_ac[tid & 7];
+}
+
void ieee80211_dynamic_ps_enable_work(struct work_struct *work);
void ieee80211_dynamic_ps_disable_work(struct work_struct *work);
void ieee80211_dynamic_ps_timer(unsigned long data);
@@ -1770,7 +1856,7 @@
void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
struct ieee80211_hdr *hdr);
void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_hdr *hdr, bool ack);
+ struct ieee80211_hdr *hdr, bool ack, u16 tx_time);
void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
unsigned long queues,
@@ -1799,6 +1885,9 @@
struct sk_buff_head *skbs);
void ieee80211_flush_queues(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata);
+void __ieee80211_flush_queues(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ unsigned int queues);
void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
u16 transaction, u16 auth_alg, u16 status,
@@ -1815,12 +1904,14 @@
u8 bands_used, u32 *rate_masks,
struct cfg80211_chan_def *chandef);
struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
- u8 *dst, u32 ratemask,
+ const u8 *src, const u8 *dst,
+ u32 ratemask,
struct ieee80211_channel *chan,
const u8 *ssid, size_t ssid_len,
const u8 *ie, size_t ie_len,
bool directed);
-void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
+void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata,
+ const u8 *src, const u8 *dst,
const u8 *ssid, size_t ssid_len,
const u8 *ie, size_t ie_len,
u32 ratemask, bool directed, u32 tx_flags,
@@ -1836,8 +1927,10 @@
void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata);
void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata);
-size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
- const u8 *ids, int n_ids, size_t offset);
+size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,
+ const u8 *ids, int n_ids,
+ const u8 *after_ric, int n_after_ric,
+ size_t offset);
size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset);
u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
u16 cap);
@@ -1924,6 +2017,14 @@
int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
const u8 *peer, enum nl80211_tdls_operation oper);
void ieee80211_tdls_peer_del_work(struct work_struct *wk);
+int ieee80211_tdls_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *addr, u8 oper_class,
+ struct cfg80211_chan_def *chandef);
+void ieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy,
+ struct net_device *dev,
+ const u8 *addr);
+void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb);
extern const struct ethtool_ops ieee80211_ethtool_ops;
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index b7402fe..f7c759a 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -259,6 +259,15 @@
list_for_each_entry(nsdata, &local->interfaces, list) {
if (nsdata != sdata && ieee80211_sdata_running(nsdata)) {
/*
+ * Only OCB and monitor mode may coexist
+ */
+ if ((sdata->vif.type == NL80211_IFTYPE_OCB &&
+ nsdata->vif.type != NL80211_IFTYPE_MONITOR) ||
+ (sdata->vif.type != NL80211_IFTYPE_MONITOR &&
+ nsdata->vif.type == NL80211_IFTYPE_OCB))
+ return -EBUSY;
+
+ /*
* Allow only a single IBSS interface to be up at any
* time. This is restricted because beacon distribution
* cannot work properly if both are in the same IBSS.
@@ -511,6 +520,7 @@
sdata->vif.cab_queue = master->vif.cab_queue;
memcpy(sdata->vif.hw_queue, master->vif.hw_queue,
sizeof(sdata->vif.hw_queue));
+ sdata->vif.bss_conf.chandef = master->vif.bss_conf.chandef;
break;
}
case NL80211_IFTYPE_AP:
@@ -521,6 +531,7 @@
case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_P2P_DEVICE:
+ case NL80211_IFTYPE_OCB:
/* no special treatment */
break;
case NL80211_IFTYPE_UNSPECIFIED:
@@ -631,6 +642,7 @@
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_OCB:
netif_carrier_off(dev);
break;
case NL80211_IFTYPE_WDS:
@@ -766,10 +778,12 @@
int i, flushed;
struct ps_data *ps;
struct cfg80211_chan_def chandef;
+ bool cancel_scan;
clear_bit(SDATA_STATE_RUNNING, &sdata->state);
- if (rcu_access_pointer(local->scan_sdata) == sdata)
+ cancel_scan = rcu_access_pointer(local->scan_sdata) == sdata;
+ if (cancel_scan)
ieee80211_scan_cancel(local);
/*
@@ -842,6 +856,8 @@
sdata_lock(sdata);
mutex_lock(&local->mtx);
sdata->vif.csa_active = false;
+ if (sdata->vif.type == NL80211_IFTYPE_STATION)
+ sdata->u.mgd.csa_waiting_bcn = false;
if (sdata->csa_block_tx) {
ieee80211_wake_vif_queues(local, sdata,
IEEE80211_QUEUE_STOP_REASON_CSA);
@@ -898,6 +914,8 @@
list_del(&sdata->u.vlan.list);
mutex_unlock(&local->mtx);
RCU_INIT_POINTER(sdata->vif.chanctx_conf, NULL);
+ /* see comment in the default case below */
+ ieee80211_free_keys(sdata, true);
/* no need to tell driver */
break;
case NL80211_IFTYPE_MONITOR:
@@ -923,17 +941,16 @@
/*
* When we get here, the interface is marked down.
* Free the remaining keys, if there are any
- * (shouldn't be, except maybe in WDS mode?)
+ * (which can happen in AP mode if userspace sets
+ * keys before the interface is operating, and maybe
+ * also in WDS mode)
*
* Force the key freeing to always synchronize_net()
* to wait for the RX path in case it is using this
- * interface enqueuing frames * at this very time on
+ * interface enqueuing frames at this very time on
* another CPU.
*/
ieee80211_free_keys(sdata, true);
-
- /* fall through */
- case NL80211_IFTYPE_AP:
skb_queue_purge(&sdata->skb_queue);
}
@@ -991,6 +1008,9 @@
ieee80211_recalc_ps(local, -1);
+ if (cancel_scan)
+ flush_delayed_work(&local->scan_work);
+
if (local->open_count == 0) {
ieee80211_stop_device(local);
@@ -1209,6 +1229,8 @@
WLAN_BACK_RECIPIENT, 0,
false);
mutex_unlock(&local->sta_mtx);
+ } else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_TDLS_CHSW) {
+ ieee80211_process_tdls_channel_switch(sdata, skb);
} else if (ieee80211_is_action(mgmt->frame_control) &&
mgmt->u.action.category == WLAN_CATEGORY_BACK) {
int len = skb->len;
@@ -1299,6 +1321,9 @@
break;
ieee80211_mesh_work(sdata);
break;
+ case NL80211_IFTYPE_OCB:
+ ieee80211_ocb_work(sdata);
+ break;
default:
break;
}
@@ -1318,6 +1343,9 @@
static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
enum nl80211_iftype type)
{
+ static const u8 bssid_wildcard[ETH_ALEN] = {0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff};
+
/* clear type-dependent union */
memset(&sdata->u, 0, sizeof(sdata->u));
@@ -1369,6 +1397,10 @@
sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid;
ieee80211_sta_setup_sdata(sdata);
break;
+ case NL80211_IFTYPE_OCB:
+ sdata->vif.bss_conf.bssid = bssid_wildcard;
+ ieee80211_ocb_setup_sdata(sdata);
+ break;
case NL80211_IFTYPE_ADHOC:
sdata->vif.bss_conf.bssid = sdata->u.ibss.bssid;
ieee80211_ibss_setup_sdata(sdata);
@@ -1416,6 +1448,7 @@
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_OCB:
/*
* Could maybe also all others here?
* Just not sure how that interacts
@@ -1431,6 +1464,7 @@
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_OCB:
/*
* Could probably support everything
* but WDS here (WDS do_open can fail
@@ -1689,7 +1723,10 @@
}
ieee80211_assign_perm_addr(local, ndev->perm_addr, type);
- memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN);
+ if (params && is_valid_ether_addr(params->macaddr))
+ memcpy(ndev->dev_addr, params->macaddr, ETH_ALEN);
+ else
+ memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN);
SET_NETDEV_DEV(ndev, wiphy_dev(local->hw.wiphy));
/* don't use IEEE80211_DEV_TO_SUB_IF -- it checks too much */
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 4712150..0bb7038 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -94,8 +94,17 @@
might_sleep();
- if (key->flags & KEY_FLAG_TAINTED)
+ if (key->flags & KEY_FLAG_TAINTED) {
+ /* If we get here, it's during resume and the key is
+ * tainted so shouldn't be used/programmed any more.
+ * However, its flags may still indicate that it was
+ * programmed into the device (since we're in resume)
+ * so clear that flag now to avoid trying to remove
+ * it again later.
+ */
+ key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
return -EINVAL;
+ }
if (!key->local->ops->set_key)
goto out_unsupported;
@@ -647,7 +656,7 @@
int i;
mutex_lock(&local->key_mtx);
- for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
+ for (i = 0; i < ARRAY_SIZE(sta->gtk); i++) {
key = key_mtx_dereference(local, sta->gtk[i]);
if (!key)
continue;
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index bc8ae88..8c5a3cf 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -478,13 +478,9 @@
},
};
-static const u8 extended_capabilities[] = {
- 0, 0, 0, 0, 0, 0, 0,
- WLAN_EXT_CAPA8_OPMODE_NOTIF,
-};
-
-struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
- const struct ieee80211_ops *ops)
+struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
+ const struct ieee80211_ops *ops,
+ const char *requested_name)
{
struct ieee80211_local *local;
int priv_size, i;
@@ -524,7 +520,7 @@
*/
priv_size = ALIGN(sizeof(*local), NETDEV_ALIGN) + priv_data_len;
- wiphy = wiphy_new(&mac80211_config_ops, priv_size);
+ wiphy = wiphy_new_nm(&mac80211_config_ops, priv_size, requested_name);
if (!wiphy)
return NULL;
@@ -539,10 +535,6 @@
WIPHY_FLAG_REPORTS_OBSS |
WIPHY_FLAG_OFFCHAN_TX;
- wiphy->extended_capabilities = extended_capabilities;
- wiphy->extended_capabilities_mask = extended_capabilities;
- wiphy->extended_capabilities_len = ARRAY_SIZE(extended_capabilities);
-
if (ops->remain_on_channel)
wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
@@ -550,6 +542,7 @@
NL80211_FEATURE_SAE |
NL80211_FEATURE_HT_IBSS |
NL80211_FEATURE_VIF_TXPOWER |
+ NL80211_FEATURE_MAC_ON_CREATE |
NL80211_FEATURE_USERSPACE_MPM;
if (!ops->hw_scan)
@@ -591,6 +584,13 @@
wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask;
wiphy->vht_capa_mod_mask = &mac80211_vht_capa_mod_mask;
+ local->ext_capa[7] = WLAN_EXT_CAPA8_OPMODE_NOTIF;
+
+ wiphy->extended_capabilities = local->ext_capa;
+ wiphy->extended_capabilities_mask = local->ext_capa;
+ wiphy->extended_capabilities_len =
+ ARRAY_SIZE(local->ext_capa);
+
INIT_LIST_HEAD(&local->interfaces);
__hw_addr_init(&local->mc_list);
@@ -651,7 +651,7 @@
return &local->hw;
}
-EXPORT_SYMBOL(ieee80211_alloc_hw);
+EXPORT_SYMBOL(ieee80211_alloc_hw_nm);
static int ieee80211_init_cipher_suites(struct ieee80211_local *local)
{
@@ -764,6 +764,12 @@
local->hw.offchannel_tx_hw_queue >= local->hw.queues))
return -EINVAL;
+ if ((hw->wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) &&
+ (!local->ops->tdls_channel_switch ||
+ !local->ops->tdls_cancel_channel_switch ||
+ !local->ops->tdls_recv_channel_switch))
+ return -EOPNOTSUPP;
+
#ifdef CONFIG_PM
if (hw->wiphy->wowlan && (!local->ops->suspend || !local->ops->resume))
return -EINVAL;
@@ -787,13 +793,14 @@
if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_WDS))
return -EINVAL;
- /* DFS currently not supported with channel context drivers */
+ /* DFS is not supported with multi-channel combinations yet */
for (i = 0; i < local->hw.wiphy->n_iface_combinations; i++) {
const struct ieee80211_iface_combination *comb;
comb = &local->hw.wiphy->iface_combinations[i];
- if (comb->radar_detect_widths)
+ if (comb->radar_detect_widths &&
+ comb->num_different_channels > 1)
return -EINVAL;
}
}
@@ -958,6 +965,10 @@
if (local->hw.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)
local->hw.wiphy->flags |= WIPHY_FLAG_TDLS_EXTERNAL_SETUP;
+ /* mac80211 supports eCSA, if the driver supports STA CSA at all */
+ if (local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)
+ local->ext_capa[0] |= WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING;
+
local->hw.wiphy->max_num_csa_counters = IEEE80211_MAX_CSA_COUNTERS_NUM;
result = wiphy_register(local->hw.wiphy);
@@ -1019,7 +1030,8 @@
}
/* add one default STA interface if supported */
- if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION)) {
+ if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION) &&
+ !(hw->flags & IEEE80211_HW_NO_AUTO_VIF)) {
result = ieee80211_if_add(local, "wlan%d", NULL,
NL80211_IFTYPE_STATION, NULL);
if (result)
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index e9f99c1..0c8b2a7 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -874,7 +874,7 @@
memset(¶ms, 0, sizeof(params));
memset(&csa_ie, 0, sizeof(csa_ie));
- err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, band,
+ err = ieee80211_parse_ch_switch_ie(sdata, elems, band,
sta_flags, sdata->vif.addr,
&csa_ie);
if (err < 0)
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index af9cb55..b718db3 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -270,6 +270,8 @@
const u8 *dst, const u8 *mpp);
struct mesh_path *
mesh_path_lookup_by_idx(struct ieee80211_sub_if_data *sdata, int idx);
+struct mesh_path *
+mpp_path_lookup_by_idx(struct ieee80211_sub_if_data *sdata, int idx);
void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop);
void mesh_path_expire(struct ieee80211_sub_if_data *sdata);
void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
@@ -317,6 +319,7 @@
bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt);
extern int mesh_paths_generation;
+extern int mpp_paths_generation;
#ifdef CPTCFG_MAC80211_MESH
static inline
diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c
index a6699dc..b890e22 100644
--- a/net/mac80211/mesh_pathtbl.c
+++ b/net/mac80211/mesh_pathtbl.c
@@ -44,6 +44,7 @@
static struct mesh_table __rcu *mpp_paths; /* Store paths for MPP&MAP */
int mesh_paths_generation;
+int mpp_paths_generation;
/* This lock will have the grow table function as writer and add / delete nodes
* as readers. RCU provides sufficient protection only when reading the table
@@ -410,6 +411,33 @@
}
/**
+ * mpp_path_lookup_by_idx - look up a path in the proxy path table by its index
+ * @idx: index
+ * @sdata: local subif, or NULL for all entries
+ *
+ * Returns: pointer to the proxy path structure, or NULL if not found.
+ *
+ * Locking: must be called within a read rcu section.
+ */
+struct mesh_path *
+mpp_path_lookup_by_idx(struct ieee80211_sub_if_data *sdata, int idx)
+{
+ struct mesh_table *tbl = rcu_dereference(mpp_paths);
+ struct mpath_node *node;
+ int i;
+ int j = 0;
+
+ for_each_mesh_entry(tbl, node, i) {
+ if (sdata && node->mpath->sdata != sdata)
+ continue;
+ if (j++ == idx)
+ return node->mpath;
+ }
+
+ return NULL;
+}
+
+/**
* mesh_path_add_gate - add the given mpath to a mesh gate to our path table
* @mpath: gate path to add to table
*/
@@ -691,6 +719,9 @@
spin_unlock(&tbl->hashwlock[hash_idx]);
read_unlock_bh(&pathtbl_resize_lock);
+
+ mpp_paths_generation++;
+
if (grow) {
set_bit(MESH_WORK_GROW_MPP_TABLE, &ifmsh->wrkq_flags);
ieee80211_queue_work(&local->hw, &sdata->work);
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 2de8870..2c36c47 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -174,6 +174,7 @@
if (!(ht_cap->cap_info &
cpu_to_le16(IEEE80211_HT_CAP_SUP_WIDTH_20_40))) {
ret = IEEE80211_STA_DISABLE_40MHZ;
+ vht_chandef = *chandef;
goto out;
}
@@ -552,13 +553,17 @@
cap = vht_cap.cap;
if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_80P80MHZ) {
- cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
- cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+ u32 bw = cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
+
+ cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
+ if (bw == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ ||
+ bw == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)
+ cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
}
if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_160MHZ) {
cap &= ~IEEE80211_VHT_CAP_SHORT_GI_160;
- cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+ cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
}
/*
@@ -775,11 +780,30 @@
WLAN_EID_QOS_CAPA,
WLAN_EID_RRM_ENABLED_CAPABILITIES,
WLAN_EID_MOBILITY_DOMAIN,
+ WLAN_EID_FAST_BSS_TRANSITION, /* reassoc only */
+ WLAN_EID_RIC_DATA, /* reassoc only */
WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
};
- noffset = ieee80211_ie_split(assoc_data->ie, assoc_data->ie_len,
- before_ht, ARRAY_SIZE(before_ht),
- offset);
+ static const u8 after_ric[] = {
+ WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
+ WLAN_EID_HT_CAPABILITY,
+ WLAN_EID_BSS_COEX_2040,
+ WLAN_EID_EXT_CAPABILITY,
+ WLAN_EID_QOS_TRAFFIC_CAPA,
+ WLAN_EID_TIM_BCAST_REQ,
+ WLAN_EID_INTERWORKING,
+ /* 60GHz doesn't happen right now */
+ WLAN_EID_VHT_CAPABILITY,
+ WLAN_EID_OPMODE_NOTIF,
+ };
+
+ noffset = ieee80211_ie_split_ric(assoc_data->ie,
+ assoc_data->ie_len,
+ before_ht,
+ ARRAY_SIZE(before_ht),
+ after_ric,
+ ARRAY_SIZE(after_ric),
+ offset);
pos = skb_put(skb, noffset - offset);
memcpy(pos, assoc_data->ie + offset, noffset - offset);
offset = noffset;
@@ -813,6 +837,8 @@
WLAN_EID_TIM_BCAST_REQ,
WLAN_EID_INTERWORKING,
};
+
+ /* RIC already taken above, so no need to handle here anymore */
noffset = ieee80211_ie_split(assoc_data->ie, assoc_data->ie_len,
before_vht, ARRAY_SIZE(before_vht),
offset);
@@ -1001,14 +1027,7 @@
/* XXX: shouldn't really modify cfg80211-owned data! */
ifmgd->associated->channel = sdata->csa_chandef.chan;
- sdata->vif.csa_active = false;
-
- /* XXX: wait for a beacon first? */
- if (sdata->csa_block_tx) {
- ieee80211_wake_vif_queues(local, sdata,
- IEEE80211_QUEUE_STOP_REASON_CSA);
- sdata->csa_block_tx = false;
- }
+ ifmgd->csa_waiting_bcn = true;
ieee80211_sta_reset_beacon_monitor(sdata);
ieee80211_sta_reset_conn_monitor(sdata);
@@ -1019,6 +1038,37 @@
sdata_unlock(sdata);
}
+static void ieee80211_chswitch_post_beacon(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ int ret;
+
+ sdata_assert_lock(sdata);
+
+ WARN_ON(!sdata->vif.csa_active);
+
+ if (sdata->csa_block_tx) {
+ ieee80211_wake_vif_queues(local, sdata,
+ IEEE80211_QUEUE_STOP_REASON_CSA);
+ sdata->csa_block_tx = false;
+ }
+
+ cfg80211_ch_switch_notify(sdata->dev, &sdata->reserved_chandef);
+
+ sdata->vif.csa_active = false;
+ ifmgd->csa_waiting_bcn = false;
+
+ ret = drv_post_channel_switch(sdata);
+ if (ret) {
+ sdata_info(sdata,
+ "driver post channel switch failed, disconnecting\n");
+ ieee80211_queue_work(&local->hw,
+ &ifmgd->csa_connection_drop_work);
+ return;
+ }
+}
+
void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)
{
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
@@ -1046,7 +1096,8 @@
static void
ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
- u64 timestamp, struct ieee802_11_elems *elems,
+ u64 timestamp, u32 device_timestamp,
+ struct ieee802_11_elems *elems,
bool beacon)
{
struct ieee80211_local *local = sdata->local;
@@ -1056,6 +1107,7 @@
struct ieee80211_chanctx *chanctx;
enum ieee80211_band current_band;
struct ieee80211_csa_ie csa_ie;
+ struct ieee80211_channel_switch ch_switch;
int res;
sdata_assert_lock(sdata);
@@ -1072,7 +1124,7 @@
current_band = cbss->channel->band;
memset(&csa_ie, 0, sizeof(csa_ie));
- res = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, current_band,
+ res = ieee80211_parse_ch_switch_ie(sdata, elems, current_band,
ifmgd->flags,
ifmgd->associated->bssid, &csa_ie);
if (res < 0)
@@ -1110,21 +1162,31 @@
chanctx = container_of(conf, struct ieee80211_chanctx, conf);
- if (local->use_chanctx) {
- u32 num_chanctx = 0;
- list_for_each_entry(chanctx, &local->chanctx_list, list)
- num_chanctx++;
+ if (local->use_chanctx &&
+ !(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) {
+ sdata_info(sdata,
+ "driver doesn't support chan-switch with channel contexts\n");
+ ieee80211_queue_work(&local->hw,
+ &ifmgd->csa_connection_drop_work);
+ mutex_unlock(&local->chanctx_mtx);
+ mutex_unlock(&local->mtx);
+ return;
+ }
- if (num_chanctx > 1 ||
- !(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) {
- sdata_info(sdata,
- "not handling chan-switch with channel contexts\n");
- ieee80211_queue_work(&local->hw,
- &ifmgd->csa_connection_drop_work);
- mutex_unlock(&local->chanctx_mtx);
- mutex_unlock(&local->mtx);
- return;
- }
+ ch_switch.timestamp = timestamp;
+ ch_switch.device_timestamp = device_timestamp;
+ ch_switch.block_tx = csa_ie.mode;
+ ch_switch.chandef = csa_ie.chandef;
+ ch_switch.count = csa_ie.count;
+
+ if (drv_pre_channel_switch(sdata, &ch_switch)) {
+ sdata_info(sdata,
+ "preparing for channel switch failed, disconnecting\n");
+ ieee80211_queue_work(&local->hw,
+ &ifmgd->csa_connection_drop_work);
+ mutex_unlock(&local->chanctx_mtx);
+ mutex_unlock(&local->mtx);
+ return;
}
res = ieee80211_vif_reserve_chanctx(sdata, &csa_ie.chandef,
@@ -1150,16 +1212,12 @@
IEEE80211_QUEUE_STOP_REASON_CSA);
mutex_unlock(&local->mtx);
+ cfg80211_ch_switch_started_notify(sdata->dev, &csa_ie.chandef,
+ csa_ie.count);
+
if (local->ops->channel_switch) {
/* use driver's channel switch callback */
- struct ieee80211_channel_switch ch_switch = {
- .timestamp = timestamp,
- .block_tx = csa_ie.mode,
- .chandef = csa_ie.chandef,
- .count = csa_ie.count,
- };
-
- drv_channel_switch(local, &ch_switch);
+ drv_channel_switch(local, sdata, &ch_switch);
return;
}
@@ -1168,7 +1226,8 @@
ieee80211_queue_work(&local->hw, &ifmgd->chswitch_work);
else
mod_timer(&ifmgd->chswitch_timer,
- TU_TO_EXP_TIME(csa_ie.count * cbss->beacon_interval));
+ TU_TO_EXP_TIME((csa_ie.count - 1) *
+ cbss->beacon_interval));
}
static bool
@@ -1579,6 +1638,95 @@
mutex_unlock(&sdata->local->mtx);
}
+static bool
+__ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ bool ret;
+ int ac;
+
+ if (local->hw.queues < IEEE80211_NUM_ACS)
+ return false;
+
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+ struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac];
+ int non_acm_ac;
+ unsigned long now = jiffies;
+
+ if (tx_tspec->action == TX_TSPEC_ACTION_NONE &&
+ tx_tspec->admitted_time &&
+ time_after(now, tx_tspec->time_slice_start + HZ)) {
+ tx_tspec->consumed_tx_time = 0;
+ tx_tspec->time_slice_start = now;
+
+ if (tx_tspec->downgraded)
+ tx_tspec->action =
+ TX_TSPEC_ACTION_STOP_DOWNGRADE;
+ }
+
+ switch (tx_tspec->action) {
+ case TX_TSPEC_ACTION_STOP_DOWNGRADE:
+ /* take the original parameters */
+ if (drv_conf_tx(local, sdata, ac, &sdata->tx_conf[ac]))
+ sdata_err(sdata,
+ "failed to set TX queue parameters for queue %d\n",
+ ac);
+ tx_tspec->action = TX_TSPEC_ACTION_NONE;
+ tx_tspec->downgraded = false;
+ ret = true;
+ break;
+ case TX_TSPEC_ACTION_DOWNGRADE:
+ if (time_after(now, tx_tspec->time_slice_start + HZ)) {
+ tx_tspec->action = TX_TSPEC_ACTION_NONE;
+ ret = true;
+ break;
+ }
+ /* downgrade next lower non-ACM AC */
+ for (non_acm_ac = ac + 1;
+ non_acm_ac < IEEE80211_NUM_ACS;
+ non_acm_ac++)
+ if (!(sdata->wmm_acm & BIT(7 - 2 * non_acm_ac)))
+ break;
+ /* The loop will result in using BK even if it requires
+ * admission control, such configuration makes no sense
+ * and we have to transmit somehow - the AC selection
+ * does the same thing.
+ */
+ if (drv_conf_tx(local, sdata, ac,
+ &sdata->tx_conf[non_acm_ac]))
+ sdata_err(sdata,
+ "failed to set TX queue parameters for queue %d\n",
+ ac);
+ tx_tspec->action = TX_TSPEC_ACTION_NONE;
+ ret = true;
+ schedule_delayed_work(&ifmgd->tx_tspec_wk,
+ tx_tspec->time_slice_start + HZ - now + 1);
+ break;
+ case TX_TSPEC_ACTION_NONE:
+ /* nothing now */
+ break;
+ }
+ }
+
+ return ret;
+}
+
+void ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata)
+{
+ if (__ieee80211_sta_handle_tspec_ac_params(sdata))
+ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_QOS);
+}
+
+static void ieee80211_sta_handle_tspec_ac_params_wk(struct work_struct *work)
+{
+ struct ieee80211_sub_if_data *sdata;
+
+ sdata = container_of(work, struct ieee80211_sub_if_data,
+ u.mgd.tx_tspec_wk.work);
+ ieee80211_sta_handle_tspec_ac_params(sdata);
+}
+
/* MLME */
static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
@@ -1663,12 +1811,14 @@
params.uapsd = uapsd;
mlme_dbg(sdata,
- "WMM queue=%d aci=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d\n",
+ "WMM queue=%d aci=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d, downgraded=%d\n",
queue, aci, acm,
params.aifs, params.cw_min, params.cw_max,
- params.txop, params.uapsd);
+ params.txop, params.uapsd,
+ ifmgd->tx_tspec[queue].downgraded);
sdata->tx_conf[queue] = params;
- if (drv_conf_tx(local, sdata, queue, ¶ms))
+ if (!ifmgd->tx_tspec[queue].downgraded &&
+ drv_conf_tx(local, sdata, queue, ¶ms))
sdata_err(sdata,
"failed to set TX queue parameters for queue %d\n",
queue);
@@ -1923,6 +2073,7 @@
ieee80211_vif_release_channel(sdata);
sdata->vif.csa_active = false;
+ ifmgd->csa_waiting_bcn = false;
if (sdata->csa_block_tx) {
ieee80211_wake_vif_queues(local, sdata,
IEEE80211_QUEUE_STOP_REASON_CSA);
@@ -1930,6 +2081,10 @@
}
mutex_unlock(&local->mtx);
+ /* existing TX TSPEC sessions no longer exist */
+ memset(ifmgd->tx_tspec, 0, sizeof(ifmgd->tx_tspec));
+ cancel_delayed_work_sync(&ifmgd->tx_tspec_wk);
+
sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
}
@@ -1982,9 +2137,46 @@
mutex_unlock(&local->mtx);
}
-void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_hdr *hdr, bool ack)
+static void ieee80211_sta_tx_wmm_ac_notify(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_hdr *hdr,
+ u16 tx_time)
{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ u16 tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
+ int ac = ieee80211_ac_from_tid(tid);
+ struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac];
+ unsigned long now = jiffies;
+
+ if (likely(!tx_tspec->admitted_time))
+ return;
+
+ if (time_after(now, tx_tspec->time_slice_start + HZ)) {
+ tx_tspec->consumed_tx_time = 0;
+ tx_tspec->time_slice_start = now;
+
+ if (tx_tspec->downgraded) {
+ tx_tspec->action = TX_TSPEC_ACTION_STOP_DOWNGRADE;
+ schedule_delayed_work(&ifmgd->tx_tspec_wk, 0);
+ }
+ }
+
+ if (tx_tspec->downgraded)
+ return;
+
+ tx_tspec->consumed_tx_time += tx_time;
+
+ if (tx_tspec->consumed_tx_time >= tx_tspec->admitted_time) {
+ tx_tspec->downgraded = true;
+ tx_tspec->action = TX_TSPEC_ACTION_DOWNGRADE;
+ schedule_delayed_work(&ifmgd->tx_tspec_wk, 0);
+ }
+}
+
+void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_hdr *hdr, bool ack, u16 tx_time)
+{
+ ieee80211_sta_tx_wmm_ac_notify(sdata, hdr, tx_time);
+
if (!ieee80211_is_data(hdr->frame_control))
return;
@@ -2039,7 +2231,8 @@
else
ssid_len = ssid[1];
- ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL,
+ ieee80211_send_probe_req(sdata, sdata->vif.addr, NULL,
+ ssid + 2, ssid_len, NULL,
0, (u32) -1, true, 0,
ifmgd->associated->channel, false);
rcu_read_unlock();
@@ -2047,8 +2240,6 @@
ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms);
run_again(sdata, ifmgd->probe_timeout);
- if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
- ieee80211_flush_queues(sdata->local, sdata);
}
static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
@@ -2077,9 +2268,7 @@
"detected beacon loss from AP (missed %d beacons) - probing\n",
beacon_loss_count);
- ieee80211_cqm_rssi_notify(&sdata->vif,
- NL80211_CQM_RSSI_BEACON_LOSS_EVENT,
- GFP_KERNEL);
+ ieee80211_cqm_beacon_loss_notify(&sdata->vif, GFP_KERNEL);
}
/*
@@ -2144,7 +2333,7 @@
else
ssid_len = ssid[1];
- skb = ieee80211_build_probe_req(sdata, cbss->bssid,
+ skb = ieee80211_build_probe_req(sdata, sdata->vif.addr, cbss->bssid,
(u32) -1, cbss->channel,
ssid + 2, ssid_len,
NULL, 0, true);
@@ -2171,6 +2360,7 @@
true, frame_buf);
mutex_lock(&local->mtx);
sdata->vif.csa_active = false;
+ ifmgd->csa_waiting_bcn = false;
if (sdata->csa_block_tx) {
ieee80211_wake_vif_queues(local, sdata,
IEEE80211_QUEUE_STOP_REASON_CSA);
@@ -2617,6 +2807,9 @@
}
ifmgd->aid = aid;
+ ifmgd->tdls_chan_switch_prohibited =
+ elems.ext_capab && elems.ext_capab_len >= 5 &&
+ (elems.ext_capab[4] & WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED);
/*
* Some APs are erroneously not including some information in their
@@ -3195,6 +3388,9 @@
}
}
+ if (ifmgd->csa_waiting_bcn)
+ ieee80211_chswitch_post_beacon(sdata);
+
if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid)
return;
ifmgd->beacon_crc = ncrc;
@@ -3203,6 +3399,7 @@
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
ieee80211_sta_process_chanswitch(sdata, rx_status->mactime,
+ rx_status->device_timestamp,
&elems, true);
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) &&
@@ -3334,8 +3531,9 @@
break;
ieee80211_sta_process_chanswitch(sdata,
- rx_status->mactime,
- &elems, false);
+ rx_status->mactime,
+ rx_status->device_timestamp,
+ &elems, false);
} else if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) {
ies_len = skb->len -
offsetof(struct ieee80211_mgmt,
@@ -3356,8 +3554,9 @@
&mgmt->u.action.u.ext_chan_switch.data;
ieee80211_sta_process_chanswitch(sdata,
- rx_status->mactime,
- &elems, false);
+ rx_status->mactime,
+ rx_status->device_timestamp,
+ &elems, false);
}
break;
}
@@ -3455,7 +3654,8 @@
* Direct probe is sent to broadcast address as some APs
* will not answer to direct packet in unassociated state.
*/
- ieee80211_send_probe_req(sdata, NULL, ssidie + 2, ssidie[1],
+ ieee80211_send_probe_req(sdata, sdata->vif.addr, NULL,
+ ssidie + 2, ssidie[1],
NULL, 0, (u32) -1, true, 0,
auth_data->bss->channel, false);
rcu_read_unlock();
@@ -3664,11 +3864,12 @@
struct ieee80211_sub_if_data *sdata =
(struct ieee80211_sub_if_data *) data;
struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
if (local->quiescing)
return;
- if (sdata->vif.csa_active)
+ if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
return;
sdata->u.mgd.connection_loss = false;
@@ -3686,7 +3887,7 @@
if (local->quiescing)
return;
- if (sdata->vif.csa_active)
+ if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
return;
ieee80211_queue_work(&local->hw, &ifmgd->monitor_work);
@@ -3798,6 +3999,8 @@
(unsigned long) sdata);
setup_timer(&ifmgd->chswitch_timer, ieee80211_chswitch_timer,
(unsigned long) sdata);
+ INIT_DELAYED_WORK(&ifmgd->tx_tspec_wk,
+ ieee80211_sta_handle_tspec_ac_params_wk);
ifmgd->flags = 0;
ifmgd->powersave = sdata->wdev.ps;
@@ -3809,6 +4012,11 @@
ifmgd->req_smps = IEEE80211_SMPS_AUTOMATIC;
else
ifmgd->req_smps = IEEE80211_SMPS_OFF;
+
+ /* Setup TDLS data */
+ spin_lock_init(&ifmgd->teardown_lock);
+ ifmgd->teardown_skb = NULL;
+ ifmgd->orig_teardown_skb = NULL;
}
/* scan finished notification */
@@ -4671,6 +4879,13 @@
}
if (ifmgd->auth_data)
ieee80211_destroy_auth_data(sdata, false);
+ spin_lock_bh(&ifmgd->teardown_lock);
+ if (ifmgd->teardown_skb) {
+ kfree_skb(ifmgd->teardown_skb);
+ ifmgd->teardown_skb = NULL;
+ ifmgd->orig_teardown_skb = NULL;
+ }
+ spin_unlock_bh(&ifmgd->teardown_lock);
del_timer_sync(&ifmgd->timer);
sdata_unlock(sdata);
}
@@ -4686,3 +4901,13 @@
cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp);
}
EXPORT_SYMBOL(ieee80211_cqm_rssi_notify);
+
+void ieee80211_cqm_beacon_loss_notify(struct ieee80211_vif *vif, gfp_t gfp)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+ trace_api_cqm_beacon_loss_notify(sdata->local, sdata);
+
+ cfg80211_cqm_beacon_loss_notify(sdata->dev, gfp);
+}
+EXPORT_SYMBOL(ieee80211_cqm_beacon_loss_notify);
diff --git a/net/mac80211/ocb.c b/net/mac80211/ocb.c
new file mode 100644
index 0000000..358d5f9
--- /dev/null
+++ b/net/mac80211/ocb.c
@@ -0,0 +1,250 @@
+/*
+ * OCB mode implementation
+ *
+ * Copyright: (c) 2014 Czech Technical University in Prague
+ * (c) 2014 Volkswagen Group Research
+ * Author: Rostislav Lisovy <rostislav.lisovy@fel.cvut.cz>
+ * Funded by: Volkswagen Group Research
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+
+#include "ieee80211_i.h"
+#include "driver-ops.h"
+#include "rate.h"
+
+#define IEEE80211_OCB_HOUSEKEEPING_INTERVAL (60 * HZ)
+#define IEEE80211_OCB_PEER_INACTIVITY_LIMIT (240 * HZ)
+#define IEEE80211_OCB_MAX_STA_ENTRIES 128
+
+/**
+ * enum ocb_deferred_task_flags - mac80211 OCB deferred tasks
+ * @OCB_WORK_HOUSEKEEPING: run the periodic OCB housekeeping tasks
+ *
+ * These flags are used in @wrkq_flags field of &struct ieee80211_if_ocb
+ */
+enum ocb_deferred_task_flags {
+ OCB_WORK_HOUSEKEEPING,
+};
+
+void ieee80211_ocb_rx_no_sta(struct ieee80211_sub_if_data *sdata,
+ const u8 *bssid, const u8 *addr,
+ u32 supp_rates)
+{
+ struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_supported_band *sband;
+ enum nl80211_bss_scan_width scan_width;
+ struct sta_info *sta;
+ int band;
+
+ /* XXX: Consider removing the least recently used entry and
+ * allow new one to be added.
+ */
+ if (local->num_sta >= IEEE80211_OCB_MAX_STA_ENTRIES) {
+ net_info_ratelimited("%s: No room for a new OCB STA entry %pM\n",
+ sdata->name, addr);
+ return;
+ }
+
+ ocb_dbg(sdata, "Adding new OCB station %pM\n", addr);
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (WARN_ON_ONCE(!chanctx_conf)) {
+ rcu_read_unlock();
+ return;
+ }
+ band = chanctx_conf->def.chan->band;
+ scan_width = cfg80211_chandef_to_scan_width(&chanctx_conf->def);
+ rcu_read_unlock();
+
+ sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
+ if (!sta)
+ return;
+
+ sta->last_rx = jiffies;
+
+ /* Add only mandatory rates for now */
+ sband = local->hw.wiphy->bands[band];
+ sta->sta.supp_rates[band] =
+ ieee80211_mandatory_rates(sband, scan_width);
+
+ spin_lock(&ifocb->incomplete_lock);
+ list_add(&sta->list, &ifocb->incomplete_stations);
+ spin_unlock(&ifocb->incomplete_lock);
+ ieee80211_queue_work(&local->hw, &sdata->work);
+}
+
+static struct sta_info *ieee80211_ocb_finish_sta(struct sta_info *sta)
+ __acquires(RCU)
+{
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+ u8 addr[ETH_ALEN];
+
+ memcpy(addr, sta->sta.addr, ETH_ALEN);
+
+ ocb_dbg(sdata, "Adding new IBSS station %pM (dev=%s)\n",
+ addr, sdata->name);
+
+ sta_info_move_state(sta, IEEE80211_STA_AUTH);
+ sta_info_move_state(sta, IEEE80211_STA_ASSOC);
+ sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
+
+ rate_control_rate_init(sta);
+
+ /* If it fails, maybe we raced another insertion? */
+ if (sta_info_insert_rcu(sta))
+ return sta_info_get(sdata, addr);
+ return sta;
+}
+
+static void ieee80211_ocb_housekeeping(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
+
+ ocb_dbg(sdata, "Running ocb housekeeping\n");
+
+ ieee80211_sta_expire(sdata, IEEE80211_OCB_PEER_INACTIVITY_LIMIT);
+
+ mod_timer(&ifocb->housekeeping_timer,
+ round_jiffies(jiffies + IEEE80211_OCB_HOUSEKEEPING_INTERVAL));
+}
+
+void ieee80211_ocb_work(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
+ struct sta_info *sta;
+
+ if (ifocb->joined != true)
+ return;
+
+ sdata_lock(sdata);
+
+ spin_lock_bh(&ifocb->incomplete_lock);
+ while (!list_empty(&ifocb->incomplete_stations)) {
+ sta = list_first_entry(&ifocb->incomplete_stations,
+ struct sta_info, list);
+ list_del(&sta->list);
+ spin_unlock_bh(&ifocb->incomplete_lock);
+
+ ieee80211_ocb_finish_sta(sta);
+ rcu_read_unlock();
+ spin_lock_bh(&ifocb->incomplete_lock);
+ }
+ spin_unlock_bh(&ifocb->incomplete_lock);
+
+ if (test_and_clear_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags))
+ ieee80211_ocb_housekeeping(sdata);
+
+ sdata_unlock(sdata);
+}
+
+static void ieee80211_ocb_housekeeping_timer(unsigned long data)
+{
+ struct ieee80211_sub_if_data *sdata = (void *)data;
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
+
+ set_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags);
+
+ ieee80211_queue_work(&local->hw, &sdata->work);
+}
+
+void ieee80211_ocb_setup_sdata(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
+
+ setup_timer(&ifocb->housekeeping_timer,
+ ieee80211_ocb_housekeeping_timer,
+ (unsigned long)sdata);
+ INIT_LIST_HEAD(&ifocb->incomplete_stations);
+ spin_lock_init(&ifocb->incomplete_lock);
+}
+
+int ieee80211_ocb_join(struct ieee80211_sub_if_data *sdata,
+ struct ocb_setup *setup)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
+ u32 changed = BSS_CHANGED_OCB;
+ int err;
+
+ if (ifocb->joined == true)
+ return -EINVAL;
+
+ sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
+ sdata->smps_mode = IEEE80211_SMPS_OFF;
+ sdata->needed_rx_chains = sdata->local->rx_chains;
+
+ mutex_lock(&sdata->local->mtx);
+ err = ieee80211_vif_use_channel(sdata, &setup->chandef,
+ IEEE80211_CHANCTX_SHARED);
+ mutex_unlock(&sdata->local->mtx);
+ if (err)
+ return err;
+
+ ieee80211_bss_info_change_notify(sdata, changed);
+
+ ifocb->joined = true;
+
+ set_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags);
+ ieee80211_queue_work(&local->hw, &sdata->work);
+
+ netif_carrier_on(sdata->dev);
+ return 0;
+}
+
+int ieee80211_ocb_leave(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
+ struct ieee80211_local *local = sdata->local;
+ struct sta_info *sta;
+
+ ifocb->joined = false;
+ sta_info_flush(sdata);
+
+ spin_lock_bh(&ifocb->incomplete_lock);
+ while (!list_empty(&ifocb->incomplete_stations)) {
+ sta = list_first_entry(&ifocb->incomplete_stations,
+ struct sta_info, list);
+ list_del(&sta->list);
+ spin_unlock_bh(&ifocb->incomplete_lock);
+
+ sta_info_free(local, sta);
+ spin_lock_bh(&ifocb->incomplete_lock);
+ }
+ spin_unlock_bh(&ifocb->incomplete_lock);
+
+ netif_carrier_off(sdata->dev);
+ clear_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
+ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_OCB);
+
+ mutex_lock(&sdata->local->mtx);
+ ieee80211_vif_release_channel(sdata);
+ mutex_unlock(&sdata->local->mtx);
+
+ skb_queue_purge(&sdata->skb_queue);
+
+ del_timer_sync(&sdata->u.ocb.housekeeping_timer);
+ /* If the timer fired while we waited for it, it will have
+ * requeued the work. Now the work will be running again
+ * but will not rearm the timer again because it checks
+ * whether we are connected to the network or not -- at this
+ * point we shouldn't be anymore.
+ */
+
+ return 0;
+}
diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c
index 2c70248..bc5270e 100644
--- a/net/mac80211/rate.c
+++ b/net/mac80211/rate.c
@@ -385,7 +385,7 @@
*rate = alt_rate;
return;
}
- } else {
+ } else if (!(rate->flags & IEEE80211_TX_RC_VHT_MCS)) {
/* handle legacy rates */
if (rate_idx_match_legacy_mask(rate, sband->n_bitrates, mask))
return;
@@ -446,9 +446,10 @@
*
* XXX: Should this check all retry rates?
*/
- if (!(rates[0].flags & IEEE80211_TX_RC_MCS)) {
+ if (!(rates[0].flags &
+ (IEEE80211_TX_RC_MCS | IEEE80211_TX_RC_VHT_MCS))) {
u32 basic_rates = vif->bss_conf.basic_rates;
- s8 baserate = basic_rates ? ffs(basic_rates - 1) : 0;
+ s8 baserate = basic_rates ? ffs(basic_rates) - 1 : 0;
rate = &sband->bitrates[rates[0].idx];
@@ -696,6 +697,7 @@
struct ieee80211_sta *pubsta,
struct ieee80211_sta_rates *rates)
{
+ struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
struct ieee80211_sta_rates *old;
/*
@@ -709,6 +711,8 @@
if (old)
kfree_rcu(old, rcu_head);
+ drv_sta_rate_tbl_update(hw_to_local(hw), sta->sdata, pubsta);
+
return 0;
}
EXPORT_SYMBOL(rate_control_set_rates);
diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h
index d75147c..a7d5439 100644
--- a/net/mac80211/rate.h
+++ b/net/mac80211/rate.h
@@ -37,13 +37,35 @@
struct rate_control_ref *ref = local->rate_ctrl;
struct ieee80211_sta *ista = &sta->sta;
void *priv_sta = sta->rate_ctrl_priv;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
if (!ref || !test_sta_flag(sta, WLAN_STA_RATE_CONTROL))
return;
- ref->ops->tx_status(ref->priv, sband, ista, priv_sta, skb);
+ if (ref->ops->tx_status)
+ ref->ops->tx_status(ref->priv, sband, ista, priv_sta, skb);
+ else
+ ref->ops->tx_status_noskb(ref->priv, sband, ista, priv_sta, info);
}
+static inline void
+rate_control_tx_status_noskb(struct ieee80211_local *local,
+ struct ieee80211_supported_band *sband,
+ struct sta_info *sta,
+ struct ieee80211_tx_info *info)
+{
+ struct rate_control_ref *ref = local->rate_ctrl;
+ struct ieee80211_sta *ista = &sta->sta;
+ void *priv_sta = sta->rate_ctrl_priv;
+
+ if (!ref || !test_sta_flag(sta, WLAN_STA_RATE_CONTROL))
+ return;
+
+ if (WARN_ON_ONCE(!ref->ops->tx_status_noskb))
+ return;
+
+ ref->ops->tx_status_noskb(ref->priv, sband, ista, priv_sta, info);
+}
static inline void rate_control_rate_init(struct sta_info *sta)
{
diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c
index 2cd8250..217e379 100644
--- a/net/mac80211/rc80211_minstrel.c
+++ b/net/mac80211/rc80211_minstrel.c
@@ -191,7 +191,7 @@
* (1) if any success probabilitiy >= 95%, out of those rates
* choose the maximum throughput rate as max_prob_rate
* (2) if all success probabilities < 95%, the rate with
- * highest success probability is choosen as max_prob_rate */
+ * highest success probability is chosen as max_prob_rate */
if (mrs->probability >= MINSTREL_FRAC(95, 100)) {
if (mrs->cur_tp >= mi->r[tmp_prob_rate].stats.cur_tp)
tmp_prob_rate = i;
@@ -223,11 +223,10 @@
static void
minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta, void *priv_sta,
- struct sk_buff *skb)
+ struct ieee80211_tx_info *info)
{
struct minstrel_priv *mp = priv;
struct minstrel_sta_info *mi = priv_sta;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_tx_rate *ar = info->status.rates;
int i, ndx;
int success;
@@ -674,7 +673,7 @@
const struct rate_control_ops mac80211_minstrel = {
.name = "minstrel",
- .tx_status = minstrel_tx_status,
+ .tx_status_noskb = minstrel_tx_status,
.get_rate = minstrel_get_rate,
.rate_init = minstrel_rate_init,
.alloc = minstrel_alloc,
diff --git a/net/mac80211/rc80211_minstrel_debugfs.c b/net/mac80211/rc80211_minstrel_debugfs.c
index edde723..2acab1b 100644
--- a/net/mac80211/rc80211_minstrel_debugfs.c
+++ b/net/mac80211/rc80211_minstrel_debugfs.c
@@ -62,14 +62,14 @@
unsigned int i, tp, prob, eprob;
char *p;
- ms = kmalloc(sizeof(*ms) + 4096, GFP_KERNEL);
+ ms = kmalloc(2048, GFP_KERNEL);
if (!ms)
return -ENOMEM;
file->private_data = ms;
p = ms->buf;
- p += sprintf(p, "rate throughput ewma prob this prob "
- "this succ/attempt success attempts\n");
+ p += sprintf(p, "rate tpt eprob *prob"
+ " *ok(*cum) ok( cum)\n");
for (i = 0; i < mi->n_rates; i++) {
struct minstrel_rate *mr = &mi->r[i];
struct minstrel_rate_stats *mrs = &mi->r[i].stats;
@@ -86,8 +86,8 @@
prob = MINSTREL_TRUNC(mrs->cur_prob * 1000);
eprob = MINSTREL_TRUNC(mrs->probability * 1000);
- p += sprintf(p, " %6u.%1u %6u.%1u %6u.%1u "
- " %3u(%3u) %8llu %8llu\n",
+ p += sprintf(p, " %4u.%1u %3u.%1u %3u.%1u"
+ " %4u(%4u) %9llu(%9llu)\n",
tp / 10, tp % 10,
eprob / 10, eprob % 10,
prob / 10, prob % 10,
@@ -102,6 +102,8 @@
mi->sample_packets);
ms->len = p - ms->buf;
+ WARN_ON(ms->len + sizeof(*ms) > 2048);
+
return 0;
}
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
index 08f80e8..e8d8bec 100644
--- a/net/mac80211/rc80211_minstrel_ht.c
+++ b/net/mac80211/rc80211_minstrel_ht.c
@@ -10,6 +10,7 @@
#include <linux/skbuff.h>
#include <linux/debugfs.h>
#include <linux/random.h>
+#include <linux/moduleparam.h>
#include <linux/ieee80211.h>
#include <net/mac80211.h>
#include "rate.h"
@@ -34,12 +35,17 @@
/* Transmit duration for the raw data part of an average sized packet */
#define MCS_DURATION(streams, sgi, bps) MCS_SYMBOL_TIME(sgi, MCS_NSYMS((streams) * (bps)))
+#define BW_20 0
+#define BW_40 1
+#define BW_80 2
+
/*
* Define group sort order: HT40 -> SGI -> #streams
*/
#define GROUP_IDX(_streams, _sgi, _ht40) \
+ MINSTREL_HT_GROUP_0 + \
MINSTREL_MAX_STREAMS * 2 * _ht40 + \
- MINSTREL_MAX_STREAMS * _sgi + \
+ MINSTREL_MAX_STREAMS * _sgi + \
_streams - 1
/* MCS rate information for an MCS group */
@@ -47,6 +53,7 @@
[GROUP_IDX(_streams, _sgi, _ht40)] = { \
.streams = _streams, \
.flags = \
+ IEEE80211_TX_RC_MCS | \
(_sgi ? IEEE80211_TX_RC_SHORT_GI : 0) | \
(_ht40 ? IEEE80211_TX_RC_40_MHZ_WIDTH : 0), \
.duration = { \
@@ -61,6 +68,47 @@
} \
}
+#define VHT_GROUP_IDX(_streams, _sgi, _bw) \
+ (MINSTREL_VHT_GROUP_0 + \
+ MINSTREL_MAX_STREAMS * 2 * (_bw) + \
+ MINSTREL_MAX_STREAMS * (_sgi) + \
+ (_streams) - 1)
+
+#define BW2VBPS(_bw, r3, r2, r1) \
+ (_bw == BW_80 ? r3 : _bw == BW_40 ? r2 : r1)
+
+#define VHT_GROUP(_streams, _sgi, _bw) \
+ [VHT_GROUP_IDX(_streams, _sgi, _bw)] = { \
+ .streams = _streams, \
+ .flags = \
+ IEEE80211_TX_RC_VHT_MCS | \
+ (_sgi ? IEEE80211_TX_RC_SHORT_GI : 0) | \
+ (_bw == BW_80 ? IEEE80211_TX_RC_80_MHZ_WIDTH : \
+ _bw == BW_40 ? IEEE80211_TX_RC_40_MHZ_WIDTH : 0), \
+ .duration = { \
+ MCS_DURATION(_streams, _sgi, \
+ BW2VBPS(_bw, 117, 54, 26)), \
+ MCS_DURATION(_streams, _sgi, \
+ BW2VBPS(_bw, 234, 108, 52)), \
+ MCS_DURATION(_streams, _sgi, \
+ BW2VBPS(_bw, 351, 162, 78)), \
+ MCS_DURATION(_streams, _sgi, \
+ BW2VBPS(_bw, 468, 216, 104)), \
+ MCS_DURATION(_streams, _sgi, \
+ BW2VBPS(_bw, 702, 324, 156)), \
+ MCS_DURATION(_streams, _sgi, \
+ BW2VBPS(_bw, 936, 432, 208)), \
+ MCS_DURATION(_streams, _sgi, \
+ BW2VBPS(_bw, 1053, 486, 234)), \
+ MCS_DURATION(_streams, _sgi, \
+ BW2VBPS(_bw, 1170, 540, 260)), \
+ MCS_DURATION(_streams, _sgi, \
+ BW2VBPS(_bw, 1404, 648, 312)), \
+ MCS_DURATION(_streams, _sgi, \
+ BW2VBPS(_bw, 1560, 720, 346)) \
+ } \
+}
+
#define CCK_DURATION(_bitrate, _short, _len) \
(1000 * (10 /* SIFS */ + \
(_short ? 72 + 24 : 144 + 48) + \
@@ -76,70 +124,161 @@
CCK_ACK_DURATION(55, _short), \
CCK_ACK_DURATION(110, _short)
-#define CCK_GROUP \
- [MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS] = { \
- .streams = 0, \
- .duration = { \
- CCK_DURATION_LIST(false), \
- CCK_DURATION_LIST(true) \
- } \
+#define CCK_GROUP \
+ [MINSTREL_CCK_GROUP] = { \
+ .streams = 0, \
+ .flags = 0, \
+ .duration = { \
+ CCK_DURATION_LIST(false), \
+ CCK_DURATION_LIST(true) \
+ } \
}
+#ifdef CPTCFG_MAC80211_RC_MINSTREL_VHT
+static bool minstrel_vht_only = true;
+module_param(minstrel_vht_only, bool, 0644);
+MODULE_PARM_DESC(minstrel_vht_only,
+ "Use only VHT rates when VHT is supported by sta.");
+#endif
+
/*
* To enable sufficiently targeted rate sampling, MCS rates are divided into
* groups, based on the number of streams and flags (HT40, SGI) that they
* use.
*
* Sortorder has to be fixed for GROUP_IDX macro to be applicable:
- * HT40 -> SGI -> #streams
+ * BW -> SGI -> #streams
*/
const struct mcs_group minstrel_mcs_groups[] = {
- MCS_GROUP(1, 0, 0),
- MCS_GROUP(2, 0, 0),
+ MCS_GROUP(1, 0, BW_20),
+ MCS_GROUP(2, 0, BW_20),
#if MINSTREL_MAX_STREAMS >= 3
- MCS_GROUP(3, 0, 0),
+ MCS_GROUP(3, 0, BW_20),
#endif
- MCS_GROUP(1, 1, 0),
- MCS_GROUP(2, 1, 0),
+ MCS_GROUP(1, 1, BW_20),
+ MCS_GROUP(2, 1, BW_20),
#if MINSTREL_MAX_STREAMS >= 3
- MCS_GROUP(3, 1, 0),
+ MCS_GROUP(3, 1, BW_20),
#endif
- MCS_GROUP(1, 0, 1),
- MCS_GROUP(2, 0, 1),
+ MCS_GROUP(1, 0, BW_40),
+ MCS_GROUP(2, 0, BW_40),
#if MINSTREL_MAX_STREAMS >= 3
- MCS_GROUP(3, 0, 1),
+ MCS_GROUP(3, 0, BW_40),
#endif
- MCS_GROUP(1, 1, 1),
- MCS_GROUP(2, 1, 1),
+ MCS_GROUP(1, 1, BW_40),
+ MCS_GROUP(2, 1, BW_40),
#if MINSTREL_MAX_STREAMS >= 3
- MCS_GROUP(3, 1, 1),
+ MCS_GROUP(3, 1, BW_40),
#endif
- /* must be last */
- CCK_GROUP
+ CCK_GROUP,
+
+#ifdef CPTCFG_MAC80211_RC_MINSTREL_VHT
+ VHT_GROUP(1, 0, BW_20),
+ VHT_GROUP(2, 0, BW_20),
+#if MINSTREL_MAX_STREAMS >= 3
+ VHT_GROUP(3, 0, BW_20),
+#endif
+
+ VHT_GROUP(1, 1, BW_20),
+ VHT_GROUP(2, 1, BW_20),
+#if MINSTREL_MAX_STREAMS >= 3
+ VHT_GROUP(3, 1, BW_20),
+#endif
+
+ VHT_GROUP(1, 0, BW_40),
+ VHT_GROUP(2, 0, BW_40),
+#if MINSTREL_MAX_STREAMS >= 3
+ VHT_GROUP(3, 0, BW_40),
+#endif
+
+ VHT_GROUP(1, 1, BW_40),
+ VHT_GROUP(2, 1, BW_40),
+#if MINSTREL_MAX_STREAMS >= 3
+ VHT_GROUP(3, 1, BW_40),
+#endif
+
+ VHT_GROUP(1, 0, BW_80),
+ VHT_GROUP(2, 0, BW_80),
+#if MINSTREL_MAX_STREAMS >= 3
+ VHT_GROUP(3, 0, BW_80),
+#endif
+
+ VHT_GROUP(1, 1, BW_80),
+ VHT_GROUP(2, 1, BW_80),
+#if MINSTREL_MAX_STREAMS >= 3
+ VHT_GROUP(3, 1, BW_80),
+#endif
+#endif
};
-#define MINSTREL_CCK_GROUP (ARRAY_SIZE(minstrel_mcs_groups) - 1)
-
static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES] __read_mostly;
static void
minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi);
/*
+ * Some VHT MCSes are invalid (when Ndbps / Nes is not an integer)
+ * e.g for MCS9@20MHzx1Nss: Ndbps=8x52*(5/6) Nes=1
+ *
+ * Returns the valid mcs map for struct minstrel_mcs_group_data.supported
+ */
+static u16
+minstrel_get_valid_vht_rates(int bw, int nss, __le16 mcs_map)
+{
+ u16 mask = 0;
+
+ if (bw == BW_20) {
+ if (nss != 3 && nss != 6)
+ mask = BIT(9);
+ } else if (bw == BW_80) {
+ if (nss == 3 || nss == 7)
+ mask = BIT(6);
+ else if (nss == 6)
+ mask = BIT(9);
+ } else {
+ WARN_ON(bw != BW_40);
+ }
+
+ switch ((le16_to_cpu(mcs_map) >> (2 * (nss - 1))) & 3) {
+ case IEEE80211_VHT_MCS_SUPPORT_0_7:
+ mask |= 0x300;
+ break;
+ case IEEE80211_VHT_MCS_SUPPORT_0_8:
+ mask |= 0x200;
+ break;
+ case IEEE80211_VHT_MCS_SUPPORT_0_9:
+ break;
+ default:
+ mask = 0x3ff;
+ }
+
+ return 0x3ff & ~mask;
+}
+
+/*
* Look up an MCS group index based on mac80211 rate information
*/
static int
minstrel_ht_get_group_idx(struct ieee80211_tx_rate *rate)
{
- return GROUP_IDX((rate->idx / MCS_GROUP_RATES) + 1,
+ return GROUP_IDX((rate->idx / 8) + 1,
!!(rate->flags & IEEE80211_TX_RC_SHORT_GI),
!!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH));
}
+static int
+minstrel_vht_get_group_idx(struct ieee80211_tx_rate *rate)
+{
+ return VHT_GROUP_IDX(ieee80211_rate_get_vht_nss(rate),
+ !!(rate->flags & IEEE80211_TX_RC_SHORT_GI),
+ !!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) +
+ 2*!!(rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH));
+}
+
static struct minstrel_rate_stats *
minstrel_ht_get_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
struct ieee80211_tx_rate *rate)
@@ -149,6 +288,9 @@
if (rate->flags & IEEE80211_TX_RC_MCS) {
group = minstrel_ht_get_group_idx(rate);
idx = rate->idx % 8;
+ } else if (rate->flags & IEEE80211_TX_RC_VHT_MCS) {
+ group = minstrel_vht_get_group_idx(rate);
+ idx = ieee80211_rate_get_vht_mcs(rate);
} else {
group = MINSTREL_CCK_GROUP;
@@ -240,8 +382,8 @@
* MCS groups, CCK rates do not provide aggregation and are therefore at last.
*/
static void
-minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u8 index,
- u8 *tp_list)
+minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u16 index,
+ u16 *tp_list)
{
int cur_group, cur_idx, cur_thr, cur_prob;
int tmp_group, tmp_idx, tmp_thr, tmp_prob;
@@ -252,19 +394,16 @@
cur_thr = mi->groups[cur_group].rates[cur_idx].cur_tp;
cur_prob = mi->groups[cur_group].rates[cur_idx].probability;
- tmp_group = tp_list[j - 1] / MCS_GROUP_RATES;
- tmp_idx = tp_list[j - 1] % MCS_GROUP_RATES;
- tmp_thr = mi->groups[tmp_group].rates[tmp_idx].cur_tp;
- tmp_prob = mi->groups[tmp_group].rates[tmp_idx].probability;
-
- while (j > 0 && (cur_thr > tmp_thr ||
- (cur_thr == tmp_thr && cur_prob > tmp_prob))) {
- j--;
+ do {
tmp_group = tp_list[j - 1] / MCS_GROUP_RATES;
tmp_idx = tp_list[j - 1] % MCS_GROUP_RATES;
tmp_thr = mi->groups[tmp_group].rates[tmp_idx].cur_tp;
tmp_prob = mi->groups[tmp_group].rates[tmp_idx].probability;
- }
+ if (cur_thr < tmp_thr ||
+ (cur_thr == tmp_thr && cur_prob <= tmp_prob))
+ break;
+ j--;
+ } while (j > 0);
if (j < MAX_THR_RATES - 1) {
memmove(&tp_list[j + 1], &tp_list[j], (sizeof(*tp_list) *
@@ -278,7 +417,7 @@
* Find and set the topmost probability rate per sta and per group
*/
static void
-minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u8 index)
+minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index)
{
struct minstrel_mcs_group_data *mg;
struct minstrel_rate_stats *mr;
@@ -321,8 +460,8 @@
*/
static void
minstrel_ht_assign_best_tp_rates(struct minstrel_ht_sta *mi,
- u8 tmp_mcs_tp_rate[MAX_THR_RATES],
- u8 tmp_cck_tp_rate[MAX_THR_RATES])
+ u16 tmp_mcs_tp_rate[MAX_THR_RATES],
+ u16 tmp_cck_tp_rate[MAX_THR_RATES])
{
unsigned int tmp_group, tmp_idx, tmp_cck_tp, tmp_mcs_tp;
int i;
@@ -386,8 +525,8 @@
struct minstrel_mcs_group_data *mg;
struct minstrel_rate_stats *mr;
int group, i, j;
- u8 tmp_mcs_tp_rate[MAX_THR_RATES], tmp_group_tp_rate[MAX_THR_RATES];
- u8 tmp_cck_tp_rate[MAX_THR_RATES], index;
+ u16 tmp_mcs_tp_rate[MAX_THR_RATES], tmp_group_tp_rate[MAX_THR_RATES];
+ u16 tmp_cck_tp_rate[MAX_THR_RATES], index;
if (mi->ampdu_packets > 0) {
mi->avg_ampdu_len = minstrel_ewma(mi->avg_ampdu_len,
@@ -485,7 +624,8 @@
if (!rate->count)
return false;
- if (rate->flags & IEEE80211_TX_RC_MCS)
+ if (rate->flags & IEEE80211_TX_RC_MCS ||
+ rate->flags & IEEE80211_TX_RC_VHT_MCS)
return true;
return rate->idx == mp->cck_rates[0] ||
@@ -517,7 +657,7 @@
}
static void
-minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u8 *idx, bool primary)
+minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u16 *idx, bool primary)
{
int group, orig_group;
@@ -547,6 +687,9 @@
struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
u16 tid;
+ if (skb_get_queue_mapping(skb) == IEEE80211_AC_VO)
+ return;
+
if (unlikely(!ieee80211_is_data_qos(hdr->frame_control)))
return;
@@ -557,20 +700,16 @@
if (likely(sta->ampdu_mlme.tid_tx[tid]))
return;
- if (skb_get_queue_mapping(skb) == IEEE80211_AC_VO)
- return;
-
ieee80211_start_tx_ba_session(pubsta, tid, 5000);
}
static void
minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta, void *priv_sta,
- struct sk_buff *skb)
+ struct ieee80211_tx_info *info)
{
struct minstrel_ht_sta_priv *msp = priv_sta;
struct minstrel_ht_sta *mi = &msp->ht;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_tx_rate *ar = info->status.rates;
struct minstrel_rate_stats *rate, *rate2;
struct minstrel_priv *mp = priv;
@@ -578,7 +717,8 @@
int i;
if (!msp->is_ht)
- return mac80211_minstrel.tx_status(priv, sband, sta, &msp->legacy, skb);
+ return mac80211_minstrel.tx_status_noskb(priv, sband, sta,
+ &msp->legacy, info);
/* This packet was aggregated but doesn't carry status info */
if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
@@ -639,9 +779,6 @@
if (time_after(jiffies, mi->stats_update + (mp->update_interval / 2 * HZ) / 1000)) {
update = true;
minstrel_ht_update_stats(mp, mi);
- if (!(info->flags & IEEE80211_TX_CTL_AMPDU) &&
- mi->max_prob_rate / MCS_GROUP_RATES != MINSTREL_CCK_GROUP)
- minstrel_aggr_check(sta, skb);
}
if (update)
@@ -714,7 +851,7 @@
const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES];
struct minstrel_rate_stats *mr;
u8 idx;
- u16 flags;
+ u16 flags = group->flags;
mr = minstrel_get_ratestats(mi, index);
if (!mr->retry_updated)
@@ -730,13 +867,13 @@
ratetbl->rate[offset].count_rts = mr->retry_count_rtscts;
}
- if (index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) {
+ if (index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP)
idx = mp->cck_rates[index % ARRAY_SIZE(mp->cck_rates)];
- flags = 0;
- } else {
+ else if (flags & IEEE80211_TX_RC_VHT_MCS)
+ idx = ((group->streams - 1) << 4) |
+ ((index % MCS_GROUP_RATES) & 0xF);
+ else
idx = index % MCS_GROUP_RATES + (group->streams - 1) * 8;
- flags = IEEE80211_TX_RC_MCS | group->flags;
- }
if (offset > 0) {
ratetbl->rate[offset].count = ratetbl->rate[offset].count_rts;
@@ -883,6 +1020,10 @@
if (!msp->is_ht)
return mac80211_minstrel.get_rate(priv, sta, &msp->legacy, txrc);
+ if (!(info->flags & IEEE80211_TX_CTL_AMPDU) &&
+ mi->max_prob_rate / MCS_GROUP_RATES != MINSTREL_CCK_GROUP)
+ minstrel_aggr_check(sta, txrc->skb);
+
info->flags |= mi->tx_flags;
minstrel_ht_check_cck_shortpreamble(mp, mi, txrc->short_preamble);
@@ -916,13 +1057,15 @@
if (sample_idx / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) {
int idx = sample_idx % ARRAY_SIZE(mp->cck_rates);
rate->idx = mp->cck_rates[idx];
- rate->flags = 0;
- return;
+ } else if (sample_group->flags & IEEE80211_TX_RC_VHT_MCS) {
+ ieee80211_rate_set_vht(rate, sample_idx % MCS_GROUP_RATES,
+ sample_group->streams);
+ } else {
+ rate->idx = sample_idx % MCS_GROUP_RATES +
+ (sample_group->streams - 1) * 8;
}
- rate->idx = sample_idx % MCS_GROUP_RATES +
- (sample_group->streams - 1) * 8;
- rate->flags = IEEE80211_TX_RC_MCS | sample_group->flags;
+ rate->flags = sample_group->flags;
}
static void
@@ -962,6 +1105,8 @@
struct minstrel_ht_sta *mi = &msp->ht;
struct ieee80211_mcs_info *mcs = &sta->ht_cap.mcs;
u16 sta_cap = sta->ht_cap.cap;
+ struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
+ int use_vht;
int n_supported = 0;
int ack_dur;
int stbc;
@@ -971,8 +1116,14 @@
if (!sta->ht_cap.ht_supported)
goto use_legacy;
- BUILD_BUG_ON(ARRAY_SIZE(minstrel_mcs_groups) !=
- MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS + 1);
+ BUILD_BUG_ON(ARRAY_SIZE(minstrel_mcs_groups) != MINSTREL_GROUPS_NB);
+
+#ifdef CPTCFG_MAC80211_RC_MINSTREL_VHT
+ if (vht_cap->vht_supported)
+ use_vht = vht_cap->vht_mcs.tx_mcs_map != cpu_to_le16(~0);
+ else
+#endif
+ use_vht = 0;
msp->is_ht = true;
memset(mi, 0, sizeof(*mi));
@@ -997,22 +1148,28 @@
}
mi->sample_tries = 4;
- stbc = (sta_cap & IEEE80211_HT_CAP_RX_STBC) >>
- IEEE80211_HT_CAP_RX_STBC_SHIFT;
- mi->tx_flags |= stbc << IEEE80211_TX_CTL_STBC_SHIFT;
+ /* TODO tx_flags for vht - ATM the RC API is not fine-grained enough */
+ if (!use_vht) {
+ stbc = (sta_cap & IEEE80211_HT_CAP_RX_STBC) >>
+ IEEE80211_HT_CAP_RX_STBC_SHIFT;
+ mi->tx_flags |= stbc << IEEE80211_TX_CTL_STBC_SHIFT;
- if (sta_cap & IEEE80211_HT_CAP_LDPC_CODING)
- mi->tx_flags |= IEEE80211_TX_CTL_LDPC;
+ if (sta_cap & IEEE80211_HT_CAP_LDPC_CODING)
+ mi->tx_flags |= IEEE80211_TX_CTL_LDPC;
+ }
for (i = 0; i < ARRAY_SIZE(mi->groups); i++) {
+ u32 gflags = minstrel_mcs_groups[i].flags;
+ int bw, nss;
+
mi->groups[i].supported = 0;
if (i == MINSTREL_CCK_GROUP) {
minstrel_ht_update_cck(mp, mi, sband, sta);
continue;
}
- if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_SHORT_GI) {
- if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) {
+ if (gflags & IEEE80211_TX_RC_SHORT_GI) {
+ if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH) {
if (!(sta_cap & IEEE80211_HT_CAP_SGI_40))
continue;
} else {
@@ -1021,17 +1178,51 @@
}
}
- if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH &&
+ if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH &&
sta->bandwidth < IEEE80211_STA_RX_BW_40)
continue;
+ nss = minstrel_mcs_groups[i].streams;
+
/* Mark MCS > 7 as unsupported if STA is in static SMPS mode */
- if (sta->smps_mode == IEEE80211_SMPS_STATIC &&
- minstrel_mcs_groups[i].streams > 1)
+ if (sta->smps_mode == IEEE80211_SMPS_STATIC && nss > 1)
continue;
- mi->groups[i].supported =
- mcs->rx_mask[minstrel_mcs_groups[i].streams - 1];
+ /* HT rate */
+ if (gflags & IEEE80211_TX_RC_MCS) {
+#ifdef CPTCFG_MAC80211_RC_MINSTREL_VHT
+ if (use_vht && minstrel_vht_only)
+ continue;
+#endif
+ mi->groups[i].supported = mcs->rx_mask[nss - 1];
+ if (mi->groups[i].supported)
+ n_supported++;
+ continue;
+ }
+
+ /* VHT rate */
+ if (!vht_cap->vht_supported ||
+ WARN_ON(!(gflags & IEEE80211_TX_RC_VHT_MCS)) ||
+ WARN_ON(gflags & IEEE80211_TX_RC_160_MHZ_WIDTH))
+ continue;
+
+ if (gflags & IEEE80211_TX_RC_80_MHZ_WIDTH) {
+ if (sta->bandwidth < IEEE80211_STA_RX_BW_80 ||
+ ((gflags & IEEE80211_TX_RC_SHORT_GI) &&
+ !(vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_80))) {
+ continue;
+ }
+ }
+
+ if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+ bw = BW_40;
+ else if (gflags & IEEE80211_TX_RC_80_MHZ_WIDTH)
+ bw = BW_80;
+ else
+ bw = BW_20;
+
+ mi->groups[i].supported = minstrel_get_valid_vht_rates(bw, nss,
+ vht_cap->vht_mcs.tx_mcs_map);
if (mi->groups[i].supported)
n_supported++;
@@ -1149,7 +1340,7 @@
static const struct rate_control_ops mac80211_minstrel_ht = {
.name = "minstrel_ht",
- .tx_status = minstrel_ht_tx_status,
+ .tx_status_noskb = minstrel_ht_tx_status,
.get_rate = minstrel_ht_get_rate,
.rate_init = minstrel_ht_rate_init,
.rate_update = minstrel_ht_rate_update,
diff --git a/net/mac80211/rc80211_minstrel_ht.h b/net/mac80211/rc80211_minstrel_ht.h
index 0a76823..a1f74f5 100644
--- a/net/mac80211/rc80211_minstrel_ht.h
+++ b/net/mac80211/rc80211_minstrel_ht.h
@@ -13,10 +13,32 @@
* The number of streams can be changed to 2 to reduce code
* size and memory footprint.
*/
-#define MINSTREL_MAX_STREAMS 3
-#define MINSTREL_STREAM_GROUPS 4
+#define MINSTREL_MAX_STREAMS 3
+#define MINSTREL_HT_STREAM_GROUPS 4 /* BW(=2) * SGI(=2) */
+#ifdef CPTCFG_MAC80211_RC_MINSTREL_VHT
+#define MINSTREL_VHT_STREAM_GROUPS 6 /* BW(=3) * SGI(=2) */
+#else
+#define MINSTREL_VHT_STREAM_GROUPS 0
+#endif
-#define MCS_GROUP_RATES 8
+#define MINSTREL_HT_GROUPS_NB (MINSTREL_MAX_STREAMS * \
+ MINSTREL_HT_STREAM_GROUPS)
+#define MINSTREL_VHT_GROUPS_NB (MINSTREL_MAX_STREAMS * \
+ MINSTREL_VHT_STREAM_GROUPS)
+#define MINSTREL_CCK_GROUPS_NB 1
+#define MINSTREL_GROUPS_NB (MINSTREL_HT_GROUPS_NB + \
+ MINSTREL_VHT_GROUPS_NB + \
+ MINSTREL_CCK_GROUPS_NB)
+
+#define MINSTREL_HT_GROUP_0 0
+#define MINSTREL_CCK_GROUP (MINSTREL_HT_GROUP_0 + MINSTREL_HT_GROUPS_NB)
+#define MINSTREL_VHT_GROUP_0 (MINSTREL_CCK_GROUP + 1)
+
+#ifdef CPTCFG_MAC80211_RC_MINSTREL_VHT
+#define MCS_GROUP_RATES 10
+#else
+#define MCS_GROUP_RATES 8
+#endif
struct mcs_group {
u32 flags;
@@ -31,11 +53,11 @@
u8 column;
/* bitfield of supported MCS rates of this group */
- u8 supported;
+ u16 supported;
/* sorted rate set within a MCS group*/
- u8 max_group_tp_rate[MAX_THR_RATES];
- u8 max_group_prob_rate;
+ u16 max_group_tp_rate[MAX_THR_RATES];
+ u16 max_group_prob_rate;
/* MCS rate statistics */
struct minstrel_rate_stats rates[MCS_GROUP_RATES];
@@ -52,8 +74,8 @@
unsigned int avg_ampdu_len;
/* overall sorted rate set */
- u8 max_tp_rate[MAX_THR_RATES];
- u8 max_prob_rate;
+ u16 max_tp_rate[MAX_THR_RATES];
+ u16 max_prob_rate;
/* time of last status update */
unsigned long stats_update;
@@ -80,7 +102,7 @@
u8 cck_supported_short;
/* MCS rate group info and statistics */
- struct minstrel_mcs_group_data groups[MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS + 1];
+ struct minstrel_mcs_group_data groups[MINSTREL_GROUPS_NB];
};
struct minstrel_ht_sta_priv {
diff --git a/net/mac80211/rc80211_minstrel_ht_debugfs.c b/net/mac80211/rc80211_minstrel_ht_debugfs.c
index a72ad46..20c676b 100644
--- a/net/mac80211/rc80211_minstrel_ht_debugfs.c
+++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c
@@ -18,19 +18,23 @@
static char *
minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
{
- unsigned int max_mcs = MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS;
const struct mcs_group *mg;
unsigned int j, tp, prob, eprob;
char htmode = '2';
char gimode = 'L';
+ u32 gflags;
if (!mi->groups[i].supported)
return p;
mg = &minstrel_mcs_groups[i];
- if (mg->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+ gflags = mg->flags;
+
+ if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH)
htmode = '4';
- if (mg->flags & IEEE80211_TX_RC_SHORT_GI)
+ else if (gflags & IEEE80211_TX_RC_80_MHZ_WIDTH)
+ htmode = '8';
+ if (gflags & IEEE80211_TX_RC_SHORT_GI)
gimode = 'S';
for (j = 0; j < MCS_GROUP_RATES; j++) {
@@ -41,10 +45,12 @@
if (!(mi->groups[i].supported & BIT(j)))
continue;
- if (i == max_mcs)
- p += sprintf(p, "CCK/%cP ", j < 4 ? 'L' : 'S');
+ if (gflags & IEEE80211_TX_RC_MCS)
+ p += sprintf(p, " HT%c0/%cGI ", htmode, gimode);
+ else if (gflags & IEEE80211_TX_RC_VHT_MCS)
+ p += sprintf(p, "VHT%c0/%cGI ", htmode, gimode);
else
- p += sprintf(p, "HT%c0/%cGI ", htmode, gimode);
+ p += sprintf(p, " CCK/%cP ", j < 4 ? 'L' : 'S');
*(p++) = (idx == mi->max_tp_rate[0]) ? 'A' : ' ';
*(p++) = (idx == mi->max_tp_rate[1]) ? 'B' : ' ';
@@ -52,19 +58,22 @@
*(p++) = (idx == mi->max_tp_rate[3]) ? 'D' : ' ';
*(p++) = (idx == mi->max_prob_rate) ? 'P' : ' ';
- if (i == max_mcs) {
- int r = bitrates[j % 4];
- p += sprintf(p, " %2u.%1uM", r / 10, r % 10);
+ if (gflags & IEEE80211_TX_RC_MCS) {
+ p += sprintf(p, " MCS%-2u ", (mg->streams - 1) * 8 + j);
+ } else if (gflags & IEEE80211_TX_RC_VHT_MCS) {
+ p += sprintf(p, " MCS%-1u/%1u", j, mg->streams);
} else {
- p += sprintf(p, " MCS%-2u", (mg->streams - 1) * 8 + j);
+ int r = bitrates[j % 4];
+
+ p += sprintf(p, " %2u.%1uM ", r / 10, r % 10);
}
tp = mr->cur_tp / 10;
prob = MINSTREL_TRUNC(mr->cur_prob * 1000);
eprob = MINSTREL_TRUNC(mr->probability * 1000);
- p += sprintf(p, " %6u.%1u %6u.%1u %6u.%1u "
- "%3u %3u(%3u) %8llu %8llu\n",
+ p += sprintf(p, " %4u.%1u %3u.%1u %3u.%1u "
+ "%3u %4u(%4u) %9llu(%9llu)\n",
tp / 10, tp % 10,
eprob / 10, eprob % 10,
prob / 10, prob % 10,
@@ -85,7 +94,6 @@
struct minstrel_ht_sta *mi = &msp->ht;
struct minstrel_debugfs_info *ms;
unsigned int i;
- unsigned int max_mcs = MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS;
char *p;
int ret;
@@ -96,17 +104,19 @@
return ret;
}
- ms = kmalloc(sizeof(*ms) + 8192, GFP_KERNEL);
+ ms = kmalloc(32768, GFP_KERNEL);
if (!ms)
return -ENOMEM;
file->private_data = ms;
p = ms->buf;
- p += sprintf(p, "type rate throughput ewma prob "
- "this prob retry this succ/attempt success attempts\n");
+ p += sprintf(p, " type rate tpt eprob *prob "
+ "ret *ok(*cum) ok( cum)\n");
- p = minstrel_ht_stats_dump(mi, max_mcs, p);
- for (i = 0; i < max_mcs; i++)
+ p = minstrel_ht_stats_dump(mi, MINSTREL_CCK_GROUP, p);
+ for (i = 0; i < MINSTREL_CCK_GROUP; i++)
+ p = minstrel_ht_stats_dump(mi, i, p);
+ for (i++; i < ARRAY_SIZE(mi->groups); i++)
p = minstrel_ht_stats_dump(mi, i, p);
p += sprintf(p, "\nTotal packet count:: ideal %d "
@@ -118,6 +128,8 @@
MINSTREL_TRUNC(mi->avg_ampdu_len * 10) % 10);
ms->len = p - ms->buf;
+ WARN_ON(ms->len + sizeof(*ms) > 32768);
+
return nonseekable_open(inode, file);
}
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 21d7259..7560231 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -39,7 +39,8 @@
* only useful for monitoring.
*/
static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
- struct sk_buff *skb)
+ struct sk_buff *skb,
+ unsigned int rtap_vendor_space)
{
if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) {
if (likely(skb->len > FCS_LEN))
@@ -52,20 +53,25 @@
}
}
+ __pskb_pull(skb, rtap_vendor_space);
+
return skb;
}
-static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len)
+static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len,
+ unsigned int rtap_vendor_space)
{
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
- struct ieee80211_hdr *hdr = (void *)skb->data;
+ struct ieee80211_hdr *hdr;
+
+ hdr = (void *)(skb->data + rtap_vendor_space);
if (status->flag & (RX_FLAG_FAILED_FCS_CRC |
RX_FLAG_FAILED_PLCP_CRC |
RX_FLAG_AMPDU_IS_ZEROLEN))
return true;
- if (unlikely(skb->len < 16 + present_fcs_len))
+ if (unlikely(skb->len < 16 + present_fcs_len + rtap_vendor_space))
return true;
if (ieee80211_is_ctl(hdr->frame_control) &&
@@ -77,8 +83,9 @@
}
static int
-ieee80211_rx_radiotap_space(struct ieee80211_local *local,
- struct ieee80211_rx_status *status)
+ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local,
+ struct ieee80211_rx_status *status,
+ struct sk_buff *skb)
{
int len;
@@ -121,6 +128,21 @@
len += 2 * hweight8(status->chains);
}
+ if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) {
+ struct ieee80211_vendor_radiotap *rtap = (void *)skb->data;
+
+ /* vendor presence bitmap */
+ len += 4;
+ /* alignment for fixed 6-byte vendor data header */
+ len = ALIGN(len, 2);
+ /* vendor data header */
+ len += 6;
+ if (WARN_ON(rtap->align == 0))
+ rtap->align = 1;
+ len = ALIGN(len, rtap->align);
+ len += rtap->len + rtap->pad;
+ }
+
return len;
}
@@ -144,13 +166,20 @@
u16 channel_flags = 0;
int mpdulen, chain;
unsigned long chains = status->chains;
+ struct ieee80211_vendor_radiotap rtap = {};
+
+ if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) {
+ rtap = *(struct ieee80211_vendor_radiotap *)skb->data;
+ /* rtap.len and rtap.pad are undone immediately */
+ skb_pull(skb, sizeof(rtap) + rtap.len + rtap.pad);
+ }
mpdulen = skb->len;
if (!(has_fcs && (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)))
mpdulen += FCS_LEN;
rthdr = (struct ieee80211_radiotap_header *)skb_push(skb, rtap_len);
- memset(rthdr, 0, rtap_len);
+ memset(rthdr, 0, rtap_len - rtap.len - rtap.pad);
it_present = &rthdr->it_present;
/* radiotap header, set always present flags */
@@ -172,6 +201,14 @@
BIT(IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
}
+ if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) {
+ it_present_val |= BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE) |
+ BIT(IEEE80211_RADIOTAP_EXT);
+ put_unaligned_le32(it_present_val, it_present);
+ it_present++;
+ it_present_val = rtap.present;
+ }
+
put_unaligned_le32(it_present_val, it_present);
pos = (void *)(it_present + 1);
@@ -366,6 +403,22 @@
*pos++ = status->chain_signal[chain];
*pos++ = chain;
}
+
+ if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) {
+ /* ensure 2 byte alignment for the vendor field as required */
+ if ((pos - (u8 *)rthdr) & 1)
+ *pos++ = 0;
+ *pos++ = rtap.oui[0];
+ *pos++ = rtap.oui[1];
+ *pos++ = rtap.oui[2];
+ *pos++ = rtap.subns;
+ put_unaligned_le16(rtap.len, pos);
+ pos += 2;
+ /* align the actual payload as requested */
+ while ((pos - (u8 *)rthdr) & (rtap.align - 1))
+ *pos++ = 0;
+ /* data (and possible padding) already follows */
+ }
}
/*
@@ -379,10 +432,17 @@
{
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(origskb);
struct ieee80211_sub_if_data *sdata;
- int needed_headroom;
+ int rt_hdrlen, needed_headroom;
struct sk_buff *skb, *skb2;
struct net_device *prev_dev = NULL;
int present_fcs_len = 0;
+ unsigned int rtap_vendor_space = 0;
+
+ if (unlikely(status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA)) {
+ struct ieee80211_vendor_radiotap *rtap = (void *)origskb->data;
+
+ rtap_vendor_space = sizeof(*rtap) + rtap->len + rtap->pad;
+ }
/*
* First, we may need to make a copy of the skb because
@@ -396,25 +456,27 @@
if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)
present_fcs_len = FCS_LEN;
- /* ensure hdr->frame_control is in skb head */
- if (!pskb_may_pull(origskb, 2)) {
+ /* ensure hdr->frame_control and vendor radiotap data are in skb head */
+ if (!pskb_may_pull(origskb, 2 + rtap_vendor_space)) {
dev_kfree_skb(origskb);
return NULL;
}
if (!local->monitors) {
- if (should_drop_frame(origskb, present_fcs_len)) {
+ if (should_drop_frame(origskb, present_fcs_len,
+ rtap_vendor_space)) {
dev_kfree_skb(origskb);
return NULL;
}
- return remove_monitor_info(local, origskb);
+ return remove_monitor_info(local, origskb, rtap_vendor_space);
}
/* room for the radiotap header based on driver features */
- needed_headroom = ieee80211_rx_radiotap_space(local, status);
+ rt_hdrlen = ieee80211_rx_radiotap_hdrlen(local, status, origskb);
+ needed_headroom = rt_hdrlen - rtap_vendor_space;
- if (should_drop_frame(origskb, present_fcs_len)) {
+ if (should_drop_frame(origskb, present_fcs_len, rtap_vendor_space)) {
/* only need to expand headroom if necessary */
skb = origskb;
origskb = NULL;
@@ -438,15 +500,15 @@
*/
skb = skb_copy_expand(origskb, needed_headroom, 0, GFP_ATOMIC);
- origskb = remove_monitor_info(local, origskb);
+ origskb = remove_monitor_info(local, origskb,
+ rtap_vendor_space);
if (!skb)
return origskb;
}
/* prepend radiotap information */
- ieee80211_add_rx_radiotap_header(local, skb, rate, needed_headroom,
- true);
+ ieee80211_add_rx_radiotap_header(local, skb, rate, rt_hdrlen, true);
skb_reset_mac_header(skb);
skb->ip_summed = CHECKSUM_UNNECESSARY;
@@ -985,7 +1047,7 @@
}
static ieee80211_rx_result debug_noinline
-ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
+ieee80211_rx_h_check_dup(struct ieee80211_rx_data *rx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
@@ -994,10 +1056,16 @@
* Drop duplicate 802.11 retransmissions
* (IEEE 802.11-2012: 9.3.2.10 "Duplicate detection and recovery")
*/
- if (rx->skb->len >= 24 && rx->sta &&
- !ieee80211_is_ctl(hdr->frame_control) &&
- !ieee80211_is_qos_nullfunc(hdr->frame_control) &&
- !is_multicast_ether_addr(hdr->addr1)) {
+
+ if (rx->skb->len < 24)
+ return RX_CONTINUE;
+
+ if (ieee80211_is_ctl(hdr->frame_control) ||
+ ieee80211_is_qos_nullfunc(hdr->frame_control) ||
+ is_multicast_ether_addr(hdr->addr1))
+ return RX_CONTINUE;
+
+ if (rx->sta) {
if (unlikely(ieee80211_has_retry(hdr->frame_control) &&
rx->sta->last_seq_ctrl[rx->seqno_idx] ==
hdr->seq_ctrl)) {
@@ -1011,6 +1079,14 @@
}
}
+ return RX_CONTINUE;
+}
+
+static ieee80211_rx_result debug_noinline
+ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
+
if (unlikely(rx->skb->len < 16)) {
I802_DEBUG_INC(rx->local->rx_handlers_drop_short);
return RX_DROP_MONITOR;
@@ -1032,6 +1108,7 @@
ieee80211_is_pspoll(hdr->frame_control)) &&
rx->sdata->vif.type != NL80211_IFTYPE_ADHOC &&
rx->sdata->vif.type != NL80211_IFTYPE_WDS &&
+ rx->sdata->vif.type != NL80211_IFTYPE_OCB &&
(!rx->sta || !test_sta_flag(rx->sta, WLAN_STA_ASSOC)))) {
/*
* accept port control frames from the AP even when it's not
@@ -1272,6 +1349,12 @@
sta->last_rx_rate_vht_nss = status->vht_nss;
}
}
+ } else if (rx->sdata->vif.type == NL80211_IFTYPE_OCB) {
+ u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len,
+ NL80211_IFTYPE_OCB);
+ /* OCB uses wild-card BSSID */
+ if (is_broadcast_ether_addr(bssid))
+ sta->last_rx = jiffies;
} else if (!is_multicast_ether_addr(hdr->addr1)) {
/*
* Mesh beacons will update last_rx when if they are found to
@@ -1678,11 +1761,14 @@
sc = le16_to_cpu(hdr->seq_ctrl);
frag = sc & IEEE80211_SCTL_FRAG;
- if (likely((!ieee80211_has_morefrags(fc) && frag == 0) ||
- is_multicast_ether_addr(hdr->addr1))) {
- /* not fragmented */
- goto out;
+ if (is_multicast_ether_addr(hdr->addr1)) {
+ rx->local->dot11MulticastReceivedFrameCount++;
+ goto out_no_led;
}
+
+ if (likely(!ieee80211_has_morefrags(fc) && frag == 0))
+ goto out;
+
I802_DEBUG_INC(rx->local->rx_handlers_fragments);
if (skb_linearize(rx->skb))
@@ -1773,12 +1859,10 @@
status->rx_flags |= IEEE80211_RX_FRAGMENTED;
out:
+ ieee80211_led_rx(rx->local);
+ out_no_led:
if (rx->sta)
rx->sta->rx_packets++;
- if (is_multicast_ether_addr(hdr->addr1))
- rx->local->dot11MulticastReceivedFrameCount++;
- else
- ieee80211_led_rx(rx->local);
return RX_CONTINUE;
}
@@ -2261,6 +2345,27 @@
if (!ieee80211_frame_allowed(rx, fc))
return RX_DROP_MONITOR;
+ /* directly handle TDLS channel switch requests/responses */
+ if (unlikely(((struct ethhdr *)rx->skb->data)->h_proto ==
+ cpu_to_be16(ETH_P_TDLS))) {
+ struct ieee80211_tdls_data *tf = (void *)rx->skb->data;
+
+ if (pskb_may_pull(rx->skb,
+ offsetof(struct ieee80211_tdls_data, u)) &&
+ tf->payload_type == WLAN_TDLS_SNAP_RFTYPE &&
+ tf->category == WLAN_CATEGORY_TDLS &&
+ (tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST ||
+ tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE)) {
+ rx->skb->pkt_type = IEEE80211_SDATA_QUEUE_TDLS_CHSW;
+ skb_queue_tail(&sdata->skb_queue, rx->skb);
+ ieee80211_queue_work(&rx->local->hw, &sdata->work);
+ if (rx->sta)
+ rx->sta->rx_packets++;
+
+ return RX_QUEUED;
+ }
+ }
+
if (rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
unlikely(port_control) && sdata->bss) {
sdata = container_of(sdata->bss, struct ieee80211_sub_if_data,
@@ -2831,6 +2936,7 @@
if (!ieee80211_vif_is_mesh(&sdata->vif) &&
sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+ sdata->vif.type != NL80211_IFTYPE_OCB &&
sdata->vif.type != NL80211_IFTYPE_STATION)
return RX_DROP_MONITOR;
@@ -2895,8 +3001,10 @@
if (!local->cooked_mntrs)
goto out_free_skb;
+ /* vendor data is long removed here */
+ status->flag &= ~RX_FLAG_RADIOTAP_VENDOR_DATA;
/* room for the radiotap header based on driver features */
- needed_headroom = ieee80211_rx_radiotap_space(local, status);
+ needed_headroom = ieee80211_rx_radiotap_hdrlen(local, status, skb);
if (skb_headroom(skb) < needed_headroom &&
pskb_expand_head(skb, needed_headroom, 0, GFP_ATOMIC))
@@ -3049,6 +3157,7 @@
goto rxh_next; \
} while (0);
+ CALL_RXH(ieee80211_rx_h_check_dup)
CALL_RXH(ieee80211_rx_h_check)
ieee80211_rx_reorder_ampdu(rx, &reorder_release);
@@ -3141,6 +3250,33 @@
BIT(rate_idx));
}
break;
+ case NL80211_IFTYPE_OCB:
+ if (!bssid)
+ return false;
+ if (ieee80211_is_beacon(hdr->frame_control)) {
+ return false;
+ } else if (!is_broadcast_ether_addr(bssid)) {
+ ocb_dbg(sdata, "BSSID mismatch in OCB mode!\n");
+ return false;
+ } else if (!multicast &&
+ !ether_addr_equal(sdata->dev->dev_addr,
+ hdr->addr1)) {
+ /* if we are in promisc mode we also accept
+ * packets not destined for us
+ */
+ if (!(sdata->dev->flags & IFF_PROMISC))
+ return false;
+ rx->flags &= ~IEEE80211_RX_RA_MATCH;
+ } else if (!rx->sta) {
+ int rate_idx;
+ if (status->flag & RX_FLAG_HT)
+ rate_idx = 0; /* TODO: HT rates */
+ else
+ rate_idx = status->rate_idx;
+ ieee80211_ocb_rx_no_sta(sdata, bssid, hdr->addr2,
+ BIT(rate_idx));
+ }
+ break;
case NL80211_IFTYPE_MESH_POINT:
if (!multicast &&
!ether_addr_equal(sdata->vif.addr, hdr->addr1)) {
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index af0d094..ae84267 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -184,9 +184,21 @@
return;
if (ieee80211_is_probe_resp(mgmt->frame_control)) {
- /* ignore ProbeResp to foreign address */
- if ((!sdata1 || !ether_addr_equal(mgmt->da, sdata1->vif.addr)) &&
- (!sdata2 || !ether_addr_equal(mgmt->da, sdata2->vif.addr)))
+ struct cfg80211_scan_request *scan_req;
+ struct cfg80211_sched_scan_request *sched_scan_req;
+
+ scan_req = rcu_dereference(local->scan_req);
+ sched_scan_req = rcu_dereference(local->sched_scan_req);
+
+ /* ignore ProbeResp to foreign address unless scanning
+ * with randomised address
+ */
+ if (!(sdata1 &&
+ (ether_addr_equal(mgmt->da, sdata1->vif.addr) ||
+ scan_req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)) &&
+ !(sdata2 &&
+ (ether_addr_equal(mgmt->da, sdata2->vif.addr) ||
+ sched_scan_req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)))
return;
elements = mgmt->u.probe_resp.variable;
@@ -234,11 +246,14 @@
/* return false if no more work */
static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
{
- struct cfg80211_scan_request *req = local->scan_req;
+ struct cfg80211_scan_request *req;
struct cfg80211_chan_def chandef;
u8 bands_used = 0;
int i, ielen, n_chans;
+ req = rcu_dereference_protected(local->scan_req,
+ lockdep_is_held(&local->mtx));
+
if (test_bit(SCAN_HW_CANCELLED, &local->scanning))
return false;
@@ -281,6 +296,9 @@
bands_used, req->rates, &chandef);
local->hw_scan_req->req.ie_len = ielen;
local->hw_scan_req->req.no_cck = req->no_cck;
+ ether_addr_copy(local->hw_scan_req->req.mac_addr, req->mac_addr);
+ ether_addr_copy(local->hw_scan_req->req.mac_addr_mask,
+ req->mac_addr_mask);
return true;
}
@@ -290,6 +308,8 @@
struct ieee80211_local *local = hw_to_local(hw);
bool hw_scan = local->ops->hw_scan;
bool was_scanning = local->scanning;
+ struct cfg80211_scan_request *scan_req;
+ struct ieee80211_sub_if_data *scan_sdata;
lockdep_assert_held(&local->mtx);
@@ -322,9 +342,15 @@
kfree(local->hw_scan_req);
local->hw_scan_req = NULL;
- if (local->scan_req != local->int_scan_req)
- cfg80211_scan_done(local->scan_req, aborted);
- local->scan_req = NULL;
+ scan_req = rcu_dereference_protected(local->scan_req,
+ lockdep_is_held(&local->mtx));
+
+ if (scan_req != local->int_scan_req)
+ cfg80211_scan_done(scan_req, aborted);
+ RCU_INIT_POINTER(local->scan_req, NULL);
+
+ scan_sdata = rcu_dereference_protected(local->scan_sdata,
+ lockdep_is_held(&local->mtx));
RCU_INIT_POINTER(local->scan_sdata, NULL);
local->scanning = 0;
@@ -335,7 +361,7 @@
if (!hw_scan) {
ieee80211_configure_filter(local);
- drv_sw_scan_complete(local);
+ drv_sw_scan_complete(local, scan_sdata);
ieee80211_offchannel_return(local);
}
@@ -361,7 +387,8 @@
}
EXPORT_SYMBOL(ieee80211_scan_completed);
-static int ieee80211_start_sw_scan(struct ieee80211_local *local)
+static int ieee80211_start_sw_scan(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata)
{
/* Software scan is not supported in multi-channel cases */
if (local->use_chanctx)
@@ -380,7 +407,7 @@
* nullfunc frames and probe requests will be dropped in
* ieee80211_tx_h_check_assoc().
*/
- drv_sw_scan_start(local);
+ drv_sw_scan_start(local, sdata, local->scan_addr);
local->leave_oper_channel_time = jiffies;
local->next_scan_state = SCAN_DECISION;
@@ -440,23 +467,26 @@
{
int i;
struct ieee80211_sub_if_data *sdata;
+ struct cfg80211_scan_request *scan_req;
enum ieee80211_band band = local->hw.conf.chandef.chan->band;
u32 tx_flags;
+ scan_req = rcu_dereference_protected(local->scan_req,
+ lockdep_is_held(&local->mtx));
+
tx_flags = IEEE80211_TX_INTFL_OFFCHAN_TX_OK;
- if (local->scan_req->no_cck)
+ if (scan_req->no_cck)
tx_flags |= IEEE80211_TX_CTL_NO_CCK_RATE;
sdata = rcu_dereference_protected(local->scan_sdata,
lockdep_is_held(&local->mtx));
- for (i = 0; i < local->scan_req->n_ssids; i++)
+ for (i = 0; i < scan_req->n_ssids; i++)
ieee80211_send_probe_req(
- sdata, NULL,
- local->scan_req->ssids[i].ssid,
- local->scan_req->ssids[i].ssid_len,
- local->scan_req->ie, local->scan_req->ie_len,
- local->scan_req->rates[band], false,
+ sdata, local->scan_addr, NULL,
+ scan_req->ssids[i].ssid, scan_req->ssids[i].ssid_len,
+ scan_req->ie, scan_req->ie_len,
+ scan_req->rates[band], false,
tx_flags, local->hw.conf.chandef.chan, true);
/*
@@ -480,7 +510,7 @@
if (!ieee80211_can_scan(local, sdata)) {
/* wait for the work to finish/time out */
- local->scan_req = req;
+ rcu_assign_pointer(local->scan_req, req);
rcu_assign_pointer(local->scan_sdata, sdata);
return 0;
}
@@ -530,9 +560,16 @@
*/
}
- local->scan_req = req;
+ rcu_assign_pointer(local->scan_req, req);
rcu_assign_pointer(local->scan_sdata, sdata);
+ if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)
+ get_random_mask_addr(local->scan_addr,
+ req->mac_addr,
+ req->mac_addr_mask);
+ else
+ memcpy(local->scan_addr, sdata->vif.addr, ETH_ALEN);
+
if (local->ops->hw_scan) {
__set_bit(SCAN_HW_SCANNING, &local->scanning);
} else if ((req->n_channels == 1) &&
@@ -549,7 +586,7 @@
/* Notify driver scan is starting, keep order of operations
* same as normal software scan, in case that matters. */
- drv_sw_scan_start(local);
+ drv_sw_scan_start(local, sdata, local->scan_addr);
ieee80211_configure_filter(local); /* accept probe-responses */
@@ -558,7 +595,7 @@
if ((req->channels[0]->flags &
IEEE80211_CHAN_NO_IR) ||
- !local->scan_req->n_ssids) {
+ !req->n_ssids) {
next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
} else {
ieee80211_scan_state_send_probe(local, &next_delay);
@@ -579,8 +616,9 @@
if (local->ops->hw_scan) {
WARN_ON(!ieee80211_prep_hw_scan(local));
rc = drv_hw_scan(local, sdata, local->hw_scan_req);
- } else
- rc = ieee80211_start_sw_scan(local);
+ } else {
+ rc = ieee80211_start_sw_scan(local, sdata);
+ }
if (rc) {
kfree(local->hw_scan_req);
@@ -617,6 +655,7 @@
struct ieee80211_sub_if_data *sdata;
struct ieee80211_channel *next_chan;
enum mac80211_scan_state next_scan_state;
+ struct cfg80211_scan_request *scan_req;
/*
* check if at least one STA interface is associated,
@@ -641,7 +680,10 @@
}
mutex_unlock(&local->iflist_mtx);
- next_chan = local->scan_req->channels[local->scan_channel_idx];
+ scan_req = rcu_dereference_protected(local->scan_req,
+ lockdep_is_held(&local->mtx));
+
+ next_chan = scan_req->channels[local->scan_channel_idx];
/*
* we're currently scanning a different channel, let's
@@ -656,7 +698,7 @@
local->leave_oper_channel_time + HZ / 8);
if (associated && !tx_empty) {
- if (local->scan_req->flags & NL80211_SCAN_FLAG_LOW_PRIORITY)
+ if (scan_req->flags & NL80211_SCAN_FLAG_LOW_PRIORITY)
next_scan_state = SCAN_ABORT;
else
next_scan_state = SCAN_SUSPEND;
@@ -677,14 +719,18 @@
int skip;
struct ieee80211_channel *chan;
enum nl80211_bss_scan_width oper_scan_width;
+ struct cfg80211_scan_request *scan_req;
+
+ scan_req = rcu_dereference_protected(local->scan_req,
+ lockdep_is_held(&local->mtx));
skip = 0;
- chan = local->scan_req->channels[local->scan_channel_idx];
+ chan = scan_req->channels[local->scan_channel_idx];
local->scan_chandef.chan = chan;
local->scan_chandef.center_freq1 = chan->center_freq;
local->scan_chandef.center_freq2 = 0;
- switch (local->scan_req->scan_width) {
+ switch (scan_req->scan_width) {
case NL80211_BSS_CHAN_WIDTH_5:
local->scan_chandef.width = NL80211_CHAN_WIDTH_5;
break;
@@ -698,7 +744,7 @@
oper_scan_width = cfg80211_chandef_to_scan_width(
&local->_oper_chandef);
if (chan == local->_oper_chandef.chan &&
- oper_scan_width == local->scan_req->scan_width)
+ oper_scan_width == scan_req->scan_width)
local->scan_chandef = local->_oper_chandef;
else
local->scan_chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
@@ -727,8 +773,7 @@
*
* In any case, it is not necessary for a passive scan.
*/
- if (chan->flags & IEEE80211_CHAN_NO_IR ||
- !local->scan_req->n_ssids) {
+ if (chan->flags & IEEE80211_CHAN_NO_IR || !scan_req->n_ssids) {
*next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
local->next_scan_state = SCAN_DECISION;
return;
@@ -777,6 +822,7 @@
struct ieee80211_local *local =
container_of(work, struct ieee80211_local, scan_work.work);
struct ieee80211_sub_if_data *sdata;
+ struct cfg80211_scan_request *scan_req;
unsigned long next_delay = 0;
bool aborted;
@@ -784,6 +830,8 @@
sdata = rcu_dereference_protected(local->scan_sdata,
lockdep_is_held(&local->mtx));
+ scan_req = rcu_dereference_protected(local->scan_req,
+ lockdep_is_held(&local->mtx));
/* When scanning on-channel, the first-callback means completed. */
if (test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning)) {
@@ -796,20 +844,19 @@
goto out_complete;
}
- if (!sdata || !local->scan_req)
+ if (!sdata || !scan_req)
goto out;
- if (local->scan_req && !local->scanning) {
- struct cfg80211_scan_request *req = local->scan_req;
+ if (!local->scanning) {
int rc;
- local->scan_req = NULL;
+ RCU_INIT_POINTER(local->scan_req, NULL);
RCU_INIT_POINTER(local->scan_sdata, NULL);
- rc = __ieee80211_start_scan(sdata, req);
+ rc = __ieee80211_start_scan(sdata, scan_req);
if (rc) {
/* need to complete scan in cfg80211 */
- local->scan_req = req;
+ rcu_assign_pointer(local->scan_req, scan_req);
aborted = true;
goto out_complete;
} else
@@ -829,7 +876,7 @@
switch (local->next_scan_state) {
case SCAN_DECISION:
/* if no more bands/channels left, complete scan */
- if (local->scan_channel_idx >= local->scan_req->n_channels) {
+ if (local->scan_channel_idx >= scan_req->n_channels) {
aborted = false;
goto out_complete;
}
@@ -1043,7 +1090,7 @@
ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies);
if (ret == 0) {
rcu_assign_pointer(local->sched_scan_sdata, sdata);
- local->sched_scan_req = req;
+ rcu_assign_pointer(local->sched_scan_req, req);
}
kfree(ie);
@@ -1052,7 +1099,7 @@
if (ret) {
/* Clean in case of failure after HW restart or upon resume. */
RCU_INIT_POINTER(local->sched_scan_sdata, NULL);
- local->sched_scan_req = NULL;
+ RCU_INIT_POINTER(local->sched_scan_req, NULL);
}
return ret;
@@ -1090,7 +1137,7 @@
}
/* We don't want to restart sched scan anymore. */
- local->sched_scan_req = NULL;
+ RCU_INIT_POINTER(local->sched_scan_req, NULL);
if (rcu_access_pointer(local->sched_scan_sdata)) {
ret = drv_sched_scan_stop(local, sdata);
@@ -1125,7 +1172,7 @@
RCU_INIT_POINTER(local->sched_scan_sdata, NULL);
/* If sched scan was aborted by the driver. */
- local->sched_scan_req = NULL;
+ RCU_INIT_POINTER(local->sched_scan_req, NULL);
mutex_unlock(&local->mtx);
diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c
index 6ab0090..efeba56 100644
--- a/net/mac80211/spectmgmt.c
+++ b/net/mac80211/spectmgmt.c
@@ -22,7 +22,7 @@
#include "wme.h"
int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
- struct ieee802_11_elems *elems, bool beacon,
+ struct ieee802_11_elems *elems,
enum ieee80211_band current_band,
u32 sta_flags, u8 *bssid,
struct ieee80211_csa_ie *csa_ie)
@@ -91,19 +91,13 @@
return -EINVAL;
}
- if (!beacon && sec_chan_offs) {
+ if (sec_chan_offs) {
secondary_channel_offset = sec_chan_offs->sec_chan_offs;
- } else if (beacon && ht_oper) {
- secondary_channel_offset =
- ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET;
} else if (!(sta_flags & IEEE80211_STA_DISABLE_HT)) {
- /* If it's not a beacon, HT is enabled and the IE not present,
- * it's 20 MHz, 802.11-2012 8.5.2.6:
- * This element [the Secondary Channel Offset Element] is
- * present when switching to a 40 MHz channel. It may be
- * present when switching to a 20 MHz channel (in which
- * case the secondary channel offset is set to SCN).
- */
+ /* If the secondary channel offset IE is not present,
+ * we can't know what's the post-CSA offset, so the
+ * best we can do is use 20MHz.
+ */
secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
}
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 9cefdbd..ca80ce0 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -351,6 +351,9 @@
sta->sta_state = IEEE80211_STA_NONE;
+ /* Mark TID as unreserved */
+ sta->reserved_tid = IEEE80211_TID_UNRESERVED;
+
ktime_get_ts(&uptime);
sta->last_connected = uptime.tv_sec;
ewma_init(&sta->avg_signal, 1024, 8);
@@ -501,7 +504,7 @@
/* make the station visible */
sta_info_hash_add(local, sta);
- list_add_rcu(&sta->list, &local->sta_list);
+ list_add_tail_rcu(&sta->list, &local->sta_list);
/* notify driver */
err = sta_info_insert_drv_state(local, sdata, sta);
@@ -847,6 +850,15 @@
if (WARN_ON(ret))
return ret;
+ /*
+ * for TDLS peers, make sure to return to the base channel before
+ * removal.
+ */
+ if (test_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL)) {
+ drv_tdls_cancel_channel_switch(local, sdata, &sta->sta);
+ clear_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL);
+ }
+
list_del_rcu(&sta->list);
drv_sta_pre_rcu_remove(local, sta->sdata, sta);
@@ -1249,7 +1261,8 @@
return;
}
- ieee80211_xmit(sdata, skb, chanctx_conf->def.chan->band);
+ info->band = chanctx_conf->def.chan->band;
+ ieee80211_xmit(sdata, skb);
rcu_read_unlock();
}
@@ -1531,7 +1544,7 @@
break;
case 0:
/* XXX: what is a good value? */
- n_frames = 8;
+ n_frames = 128;
break;
}
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 3aabd74..4370931 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -49,6 +49,9 @@
* packets. This means the link is enabled.
* @WLAN_STA_TDLS_INITIATOR: We are the initiator of the TDLS link with this
* station.
+ * @WLAN_STA_TDLS_CHAN_SWITCH: This TDLS peer supports TDLS channel-switching
+ * @WLAN_STA_TDLS_OFF_CHANNEL: The local STA is currently off-channel with this
+ * TDLS peer
* @WLAN_STA_UAPSD: Station requested unscheduled SP while driver was
* keeping station in power-save mode, reply when the driver
* unblocks the station.
@@ -78,6 +81,8 @@
WLAN_STA_TDLS_PEER,
WLAN_STA_TDLS_PEER_AUTH,
WLAN_STA_TDLS_INITIATOR,
+ WLAN_STA_TDLS_CHAN_SWITCH,
+ WLAN_STA_TDLS_OFF_CHANNEL,
WLAN_STA_UAPSD,
WLAN_STA_SP,
WLAN_STA_4ADDR_EVENT,
@@ -249,6 +254,9 @@
u32 bin_count;
};
+/* Value to indicate no TID reservation */
+#define IEEE80211_TID_UNRESERVED 0xff
+
/**
* struct sta_info - STA information
*
@@ -336,6 +344,8 @@
* @known_smps_mode: the smps_mode the client thinks we are in. Relevant for
* AP only.
* @cipher_scheme: optional cipher scheme for this station
+ * @last_tdls_pkt_time: holds the time in jiffies of last TDLS pkt ACKed
+ * @reserved_tid: reserved TID (if any, otherwise IEEE80211_TID_UNRESERVED)
*/
struct sta_info {
/* General information, mostly static */
@@ -453,6 +463,8 @@
/* TDLS timeout data */
unsigned long last_tdls_pkt_time;
+ u8 reserved_tid;
+
/* keep last! */
struct ieee80211_sta sta;
};
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 89290e3..bb146f3 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -390,6 +390,46 @@
}
}
+/*
+ * Handles the tx for TDLS teardown frames.
+ * If the frame wasn't ACKed by the peer - it will be re-sent through the AP
+ */
+static void ieee80211_tdls_td_tx_handle(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, u32 flags)
+{
+ struct sk_buff *teardown_skb;
+ struct sk_buff *orig_teardown_skb;
+ bool is_teardown = false;
+
+ /* Get the teardown data we need and free the lock */
+ spin_lock(&sdata->u.mgd.teardown_lock);
+ teardown_skb = sdata->u.mgd.teardown_skb;
+ orig_teardown_skb = sdata->u.mgd.orig_teardown_skb;
+ if ((skb == orig_teardown_skb) && teardown_skb) {
+ sdata->u.mgd.teardown_skb = NULL;
+ sdata->u.mgd.orig_teardown_skb = NULL;
+ is_teardown = true;
+ }
+ spin_unlock(&sdata->u.mgd.teardown_lock);
+
+ if (is_teardown) {
+ /* This mechanism relies on being able to get ACKs */
+ WARN_ON(!(local->hw.flags &
+ IEEE80211_HW_REPORTS_TX_ACK_STATUS));
+
+ /* Check if peer has ACKed */
+ if (flags & IEEE80211_TX_STAT_ACK) {
+ dev_kfree_skb_any(teardown_skb);
+ } else {
+ tdls_dbg(sdata,
+ "TDLS Resending teardown through AP\n");
+
+ ieee80211_subif_start_xmit(teardown_skb, skb->dev);
+ }
+ }
+}
+
static void ieee80211_report_used_skb(struct ieee80211_local *local,
struct sk_buff *skb, bool dropped)
{
@@ -426,8 +466,19 @@
if (!sdata) {
skb->dev = NULL;
} else if (info->flags & IEEE80211_TX_INTFL_MLME_CONN_TX) {
- ieee80211_mgd_conn_tx_status(sdata, hdr->frame_control,
- acked);
+ unsigned int hdr_size =
+ ieee80211_hdrlen(hdr->frame_control);
+
+ /* Check to see if packet is a TDLS teardown packet */
+ if (ieee80211_is_data(hdr->frame_control) &&
+ (ieee80211_get_tdls_action(skb, hdr_size) ==
+ WLAN_TDLS_TEARDOWN))
+ ieee80211_tdls_td_tx_handle(local, sdata, skb,
+ info->flags);
+ else
+ ieee80211_mgd_conn_tx_status(sdata,
+ hdr->frame_control,
+ acked);
} else if (ieee80211_is_nullfunc(hdr->frame_control) ||
ieee80211_is_qos_nullfunc(hdr->frame_control)) {
cfg80211_probe_status(sdata->dev, hdr->addr1,
@@ -541,10 +592,9 @@
#define STA_LOST_TDLS_PKT_THRESHOLD 10
#define STA_LOST_TDLS_PKT_TIME (10*HZ) /* 10secs since last ACK */
-static void ieee80211_lost_packet(struct sta_info *sta, struct sk_buff *skb)
+static void ieee80211_lost_packet(struct sta_info *sta,
+ struct ieee80211_tx_info *info)
{
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-
/* This packet was aggregated but doesn't carry status info */
if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
!(info->flags & IEEE80211_TX_STAT_AMPDU))
@@ -571,24 +621,13 @@
sta->lost_packets = 0;
}
-void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
+static int ieee80211_tx_get_rates(struct ieee80211_hw *hw,
+ struct ieee80211_tx_info *info,
+ int *retry_count)
{
- struct sk_buff *skb2;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- struct ieee80211_local *local = hw_to_local(hw);
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- __le16 fc;
- struct ieee80211_supported_band *sband;
- struct ieee80211_sub_if_data *sdata;
- struct net_device *prev_dev = NULL;
- struct sta_info *sta, *tmp;
- int retry_count = -1, i;
int rates_idx = -1;
- bool send_to_cooked;
- bool acked;
- struct ieee80211_bar *bar;
- int rtap_len;
- int shift = 0;
+ int count = -1;
+ int i;
for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
@@ -606,12 +645,91 @@
break;
}
- retry_count += info->status.rates[i].count;
+ count += info->status.rates[i].count;
}
rates_idx = i - 1;
- if (retry_count < 0)
- retry_count = 0;
+ if (count < 0)
+ count = 0;
+
+ *retry_count = count;
+ return rates_idx;
+}
+
+void ieee80211_tx_status_noskb(struct ieee80211_hw *hw,
+ struct ieee80211_sta *pubsta,
+ struct ieee80211_tx_info *info)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_supported_band *sband;
+ int retry_count;
+ int rates_idx;
+ bool acked;
+
+ rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
+
+ sband = hw->wiphy->bands[info->band];
+
+ acked = !!(info->flags & IEEE80211_TX_STAT_ACK);
+ if (pubsta) {
+ struct sta_info *sta;
+
+ sta = container_of(pubsta, struct sta_info, sta);
+
+ if (!acked)
+ sta->tx_retry_failed++;
+ sta->tx_retry_count += retry_count;
+
+ if (acked) {
+ sta->last_rx = jiffies;
+
+ if (sta->lost_packets)
+ sta->lost_packets = 0;
+
+ /* Track when last TDLS packet was ACKed */
+ if (test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH))
+ sta->last_tdls_pkt_time = jiffies;
+ } else {
+ ieee80211_lost_packet(sta, info);
+ }
+
+ rate_control_tx_status_noskb(local, sband, sta, info);
+ }
+
+ if (acked) {
+ local->dot11TransmittedFrameCount++;
+ if (!pubsta)
+ local->dot11MulticastTransmittedFrameCount++;
+ if (retry_count > 0)
+ local->dot11RetryCount++;
+ if (retry_count > 1)
+ local->dot11MultipleRetryCount++;
+ } else {
+ local->dot11FailedCount++;
+ }
+}
+EXPORT_SYMBOL(ieee80211_tx_status_noskb);
+
+void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+ struct sk_buff *skb2;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ __le16 fc;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_sub_if_data *sdata;
+ struct net_device *prev_dev = NULL;
+ struct sta_info *sta, *tmp;
+ int retry_count;
+ int rates_idx;
+ bool send_to_cooked;
+ bool acked;
+ struct ieee80211_bar *bar;
+ int rtap_len;
+ int shift = 0;
+
+ rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
rcu_read_lock();
@@ -704,7 +822,8 @@
if ((sta->sdata->vif.type == NL80211_IFTYPE_STATION) &&
(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS))
- ieee80211_sta_tx_notify(sta->sdata, (void *) skb->data, acked);
+ ieee80211_sta_tx_notify(sta->sdata, (void *) skb->data,
+ acked, info->status.tx_time);
if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) {
if (info->flags & IEEE80211_TX_STAT_ACK) {
@@ -715,7 +834,7 @@
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH))
sta->last_tdls_pkt_time = jiffies;
} else {
- ieee80211_lost_packet(sta, skb);
+ ieee80211_lost_packet(sta, info);
}
}
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index 4ea25de..55ddd77 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -35,19 +35,101 @@
mutex_unlock(&local->mtx);
}
-static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb)
+static void ieee80211_tdls_add_ext_capab(struct ieee80211_local *local,
+ struct sk_buff *skb)
{
u8 *pos = (void *)skb_put(skb, 7);
+ bool chan_switch = local->hw.wiphy->features &
+ NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
*pos++ = WLAN_EID_EXT_CAPABILITY;
*pos++ = 5; /* len */
*pos++ = 0x0;
*pos++ = 0x0;
*pos++ = 0x0;
- *pos++ = 0x0;
+ *pos++ = chan_switch ? WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH : 0;
*pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED;
}
+static u8
+ieee80211_tdls_add_subband(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, u16 start, u16 end,
+ u16 spacing)
+{
+ u8 subband_cnt = 0, ch_cnt = 0;
+ struct ieee80211_channel *ch;
+ struct cfg80211_chan_def chandef;
+ int i, subband_start;
+
+ for (i = start; i <= end; i += spacing) {
+ if (!ch_cnt)
+ subband_start = i;
+
+ ch = ieee80211_get_channel(sdata->local->hw.wiphy, i);
+ if (ch) {
+ /* we will be active on the channel */
+ u32 flags = IEEE80211_CHAN_DISABLED |
+ IEEE80211_CHAN_NO_IR;
+ cfg80211_chandef_create(&chandef, ch,
+ NL80211_CHAN_HT20);
+ if (cfg80211_chandef_usable(sdata->local->hw.wiphy,
+ &chandef, flags)) {
+ ch_cnt++;
+ continue;
+ }
+ }
+
+ if (ch_cnt) {
+ u8 *pos = skb_put(skb, 2);
+ *pos++ = ieee80211_frequency_to_channel(subband_start);
+ *pos++ = ch_cnt;
+
+ subband_cnt++;
+ ch_cnt = 0;
+ }
+ }
+
+ return subband_cnt;
+}
+
+static void
+ieee80211_tdls_add_supp_channels(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb)
+{
+ /*
+ * Add possible channels for TDLS. These are channels that are allowed
+ * to be active.
+ */
+ u8 subband_cnt;
+ u8 *pos = skb_put(skb, 2);
+
+ *pos++ = WLAN_EID_SUPPORTED_CHANNELS;
+
+ /*
+ * 5GHz and 2GHz channels numbers can overlap. Ignore this for now, as
+ * this doesn't happen in real world scenarios.
+ */
+
+ /* 2GHz, with 5MHz spacing */
+ subband_cnt = ieee80211_tdls_add_subband(sdata, skb, 2412, 2472, 5);
+
+ /* 5GHz, with 20MHz spacing */
+ subband_cnt += ieee80211_tdls_add_subband(sdata, skb, 5000, 5825, 20);
+
+ /* length */
+ *pos = 2 * subband_cnt;
+}
+
+static void ieee80211_tdls_add_bss_coex_ie(struct sk_buff *skb)
+{
+ u8 *pos = (void *)skb_put(skb, 3);
+
+ *pos++ = WLAN_EID_BSS_COEX_2040;
+ *pos++ = 1; /* len */
+
+ *pos++ = WLAN_BSS_COEX_INFORMATION_REQUEST;
+}
+
static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata,
u16 status_code)
{
@@ -190,6 +272,7 @@
ieee80211_add_srates_ie(sdata, skb, false, band);
ieee80211_add_ext_srates_ie(sdata, skb, false, band);
+ ieee80211_tdls_add_supp_channels(sdata, skb);
/* add any custom IEs that go before Extended Capabilities */
if (extra_ies_len) {
@@ -209,7 +292,7 @@
offset = noffset;
}
- ieee80211_tdls_add_ext_capab(skb);
+ ieee80211_tdls_add_ext_capab(local, skb);
/* add the QoS element if we support it */
if (local->hw.queues >= IEEE80211_NUM_ACS &&
@@ -271,6 +354,10 @@
rcu_read_unlock();
+ if (ht_cap.ht_supported &&
+ (ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40))
+ ieee80211_tdls_add_bss_coex_ie(skb);
+
/* add any remaining IEs */
if (extra_ies_len) {
noffset = extra_ies_len;
@@ -362,11 +449,68 @@
ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
}
+static void
+ieee80211_tdls_add_chan_switch_req_ies(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, const u8 *peer,
+ bool initiator, const u8 *extra_ies,
+ size_t extra_ies_len, u8 oper_class,
+ struct cfg80211_chan_def *chandef)
+{
+ struct ieee80211_tdls_data *tf;
+ size_t offset = 0, noffset;
+ u8 *pos;
+
+ if (WARN_ON_ONCE(!chandef))
+ return;
+
+ tf = (void *)skb->data;
+ tf->u.chan_switch_req.target_channel =
+ ieee80211_frequency_to_channel(chandef->chan->center_freq);
+ tf->u.chan_switch_req.oper_class = oper_class;
+
+ if (extra_ies_len) {
+ static const u8 before_lnkie[] = {
+ WLAN_EID_SECONDARY_CHANNEL_OFFSET,
+ };
+ noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
+ before_lnkie,
+ ARRAY_SIZE(before_lnkie),
+ offset);
+ pos = skb_put(skb, noffset - offset);
+ memcpy(pos, extra_ies + offset, noffset - offset);
+ offset = noffset;
+ }
+
+ ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
+
+ /* add any remaining IEs */
+ if (extra_ies_len) {
+ noffset = extra_ies_len;
+ pos = skb_put(skb, noffset - offset);
+ memcpy(pos, extra_ies + offset, noffset - offset);
+ }
+}
+
+static void
+ieee80211_tdls_add_chan_switch_resp_ies(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, const u8 *peer,
+ u16 status_code, bool initiator,
+ const u8 *extra_ies,
+ size_t extra_ies_len)
+{
+ if (status_code == 0)
+ ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
+
+ if (extra_ies_len)
+ memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len);
+}
+
static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, const u8 *peer,
u8 action_code, u16 status_code,
bool initiator, const u8 *extra_ies,
- size_t extra_ies_len)
+ size_t extra_ies_len, u8 oper_class,
+ struct cfg80211_chan_def *chandef)
{
switch (action_code) {
case WLAN_TDLS_SETUP_REQUEST:
@@ -393,6 +537,18 @@
if (status_code == 0 || action_code == WLAN_TDLS_TEARDOWN)
ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
break;
+ case WLAN_TDLS_CHANNEL_SWITCH_REQUEST:
+ ieee80211_tdls_add_chan_switch_req_ies(sdata, skb, peer,
+ initiator, extra_ies,
+ extra_ies_len,
+ oper_class, chandef);
+ break;
+ case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE:
+ ieee80211_tdls_add_chan_switch_resp_ies(sdata, skb, peer,
+ status_code,
+ initiator, extra_ies,
+ extra_ies_len);
+ break;
}
}
@@ -459,6 +615,19 @@
skb_put(skb, sizeof(tf->u.discover_req));
tf->u.discover_req.dialog_token = dialog_token;
break;
+ case WLAN_TDLS_CHANNEL_SWITCH_REQUEST:
+ tf->category = WLAN_CATEGORY_TDLS;
+ tf->action_code = WLAN_TDLS_CHANNEL_SWITCH_REQUEST;
+
+ skb_put(skb, sizeof(tf->u.chan_switch_req));
+ break;
+ case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE:
+ tf->category = WLAN_CATEGORY_TDLS;
+ tf->action_code = WLAN_TDLS_CHANNEL_SWITCH_RESPONSE;
+
+ skb_put(skb, sizeof(tf->u.chan_switch_resp));
+ tf->u.chan_switch_resp.status_code = cpu_to_le16(status_code);
+ break;
default:
return -EINVAL;
}
@@ -502,32 +671,33 @@
return 0;
}
-static int
-ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
- const u8 *peer, u8 action_code,
- u8 dialog_token, u16 status_code,
- u32 peer_capability, bool initiator,
- const u8 *extra_ies, size_t extra_ies_len)
+static struct sk_buff *
+ieee80211_tdls_build_mgmt_packet_data(struct ieee80211_sub_if_data *sdata,
+ const u8 *peer, u8 action_code,
+ u8 dialog_token, u16 status_code,
+ bool initiator, const u8 *extra_ies,
+ size_t extra_ies_len, u8 oper_class,
+ struct cfg80211_chan_def *chandef)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
- struct sk_buff *skb = NULL;
- bool send_direct;
- struct sta_info *sta;
+ struct sk_buff *skb;
int ret;
- skb = dev_alloc_skb(local->hw.extra_tx_headroom +
- max(sizeof(struct ieee80211_mgmt),
- sizeof(struct ieee80211_tdls_data)) +
- 50 + /* supported rates */
- 7 + /* ext capab */
- 26 + /* max(WMM-info, WMM-param) */
- 2 + max(sizeof(struct ieee80211_ht_cap),
- sizeof(struct ieee80211_ht_operation)) +
- extra_ies_len +
- sizeof(struct ieee80211_tdls_lnkie));
+ skb = netdev_alloc_skb(sdata->dev,
+ local->hw.extra_tx_headroom +
+ max(sizeof(struct ieee80211_mgmt),
+ sizeof(struct ieee80211_tdls_data)) +
+ 50 + /* supported rates */
+ 7 + /* ext capab */
+ 26 + /* max(WMM-info, WMM-param) */
+ 2 + max(sizeof(struct ieee80211_ht_cap),
+ sizeof(struct ieee80211_ht_operation)) +
+ 50 + /* supported channels */
+ 3 + /* 40/20 BSS coex */
+ extra_ies_len +
+ sizeof(struct ieee80211_tdls_lnkie));
if (!skb)
- return -ENOMEM;
+ return NULL;
skb_reserve(skb, local->hw.extra_tx_headroom);
@@ -537,16 +707,18 @@
case WLAN_TDLS_SETUP_CONFIRM:
case WLAN_TDLS_TEARDOWN:
case WLAN_TDLS_DISCOVERY_REQUEST:
- ret = ieee80211_prep_tdls_encap_data(wiphy, dev, peer,
+ case WLAN_TDLS_CHANNEL_SWITCH_REQUEST:
+ case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE:
+ ret = ieee80211_prep_tdls_encap_data(local->hw.wiphy,
+ sdata->dev, peer,
action_code, dialog_token,
status_code, skb);
- send_direct = false;
break;
case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
- ret = ieee80211_prep_tdls_direct(wiphy, dev, peer, action_code,
+ ret = ieee80211_prep_tdls_direct(local->hw.wiphy, sdata->dev,
+ peer, action_code,
dialog_token, status_code,
skb);
- send_direct = true;
break;
default:
ret = -ENOTSUPP;
@@ -556,14 +728,40 @@
if (ret < 0)
goto fail;
+ ieee80211_tdls_add_ies(sdata, skb, peer, action_code, status_code,
+ initiator, extra_ies, extra_ies_len, oper_class,
+ chandef);
+ return skb;
+
+fail:
+ dev_kfree_skb(skb);
+ return NULL;
+}
+
+static int
+ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *peer, u8 action_code, u8 dialog_token,
+ u16 status_code, u32 peer_capability,
+ bool initiator, const u8 *extra_ies,
+ size_t extra_ies_len, u8 oper_class,
+ struct cfg80211_chan_def *chandef)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct sk_buff *skb = NULL;
+ struct sta_info *sta;
+ u32 flags = 0;
+ int ret = 0;
+
rcu_read_lock();
sta = sta_info_get(sdata, peer);
/* infer the initiator if we can, to support old userspace */
switch (action_code) {
case WLAN_TDLS_SETUP_REQUEST:
- if (sta)
+ if (sta) {
set_sta_flag(sta, WLAN_STA_TDLS_INITIATOR);
+ sta->sta.tdls_initiator = false;
+ }
/* fall-through */
case WLAN_TDLS_SETUP_CONFIRM:
case WLAN_TDLS_DISCOVERY_REQUEST:
@@ -575,13 +773,17 @@
* Make the last packet sent take effect for the initiator
* value.
*/
- if (sta)
+ if (sta) {
clear_sta_flag(sta, WLAN_STA_TDLS_INITIATOR);
+ sta->sta.tdls_initiator = true;
+ }
/* fall-through */
case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
initiator = false;
break;
case WLAN_TDLS_TEARDOWN:
+ case WLAN_TDLS_CHANNEL_SWITCH_REQUEST:
+ case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE:
/* any value is ok */
break;
default:
@@ -596,9 +798,17 @@
if (ret < 0)
goto fail;
- ieee80211_tdls_add_ies(sdata, skb, peer, action_code, status_code,
- initiator, extra_ies, extra_ies_len);
- if (send_direct) {
+ skb = ieee80211_tdls_build_mgmt_packet_data(sdata, peer, action_code,
+ dialog_token, status_code,
+ initiator, extra_ies,
+ extra_ies_len, oper_class,
+ chandef);
+ if (!skb) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ if (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) {
ieee80211_tx_skb(sdata, skb);
return 0;
}
@@ -619,9 +829,44 @@
break;
}
+ /*
+ * Set the WLAN_TDLS_TEARDOWN flag to indicate a teardown in progress.
+ * Later, if no ACK is returned from peer, we will re-send the teardown
+ * packet through the AP.
+ */
+ if ((action_code == WLAN_TDLS_TEARDOWN) &&
+ (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) {
+ struct sta_info *sta = NULL;
+ bool try_resend; /* Should we keep skb for possible resend */
+
+ /* If not sending directly to peer - no point in keeping skb */
+ rcu_read_lock();
+ sta = sta_info_get(sdata, peer);
+ try_resend = sta && test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
+ rcu_read_unlock();
+
+ spin_lock_bh(&sdata->u.mgd.teardown_lock);
+ if (try_resend && !sdata->u.mgd.teardown_skb) {
+ /* Mark it as requiring TX status callback */
+ flags |= IEEE80211_TX_CTL_REQ_TX_STATUS |
+ IEEE80211_TX_INTFL_MLME_CONN_TX;
+
+ /*
+ * skb is copied since mac80211 will later set
+ * properties that might not be the same as the AP,
+ * such as encryption, QoS, addresses, etc.
+ *
+ * No problem if skb_copy() fails, so no need to check.
+ */
+ sdata->u.mgd.teardown_skb = skb_copy(skb, GFP_ATOMIC);
+ sdata->u.mgd.orig_teardown_skb = skb;
+ }
+ spin_unlock_bh(&sdata->u.mgd.teardown_lock);
+ }
+
/* disable bottom halves when entering the Tx path */
local_bh_disable();
- ret = ieee80211_subif_start_xmit(skb, dev);
+ __ieee80211_subif_start_xmit(skb, dev, flags);
local_bh_enable();
return ret;
@@ -672,7 +917,8 @@
ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code,
dialog_token, status_code,
peer_capability, initiator,
- extra_ies, extra_ies_len);
+ extra_ies, extra_ies_len, 0,
+ NULL);
if (ret < 0)
goto exit;
@@ -711,7 +957,8 @@
ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code,
dialog_token, status_code,
peer_capability, initiator,
- extra_ies, extra_ies_len);
+ extra_ies, extra_ies_len, 0,
+ NULL);
if (ret < 0)
sdata_err(sdata, "Failed sending TDLS teardown packet %d\n",
ret);
@@ -781,7 +1028,7 @@
status_code,
peer_capability,
initiator, extra_ies,
- extra_ies_len);
+ extra_ies_len, 0, NULL);
break;
default:
ret = -EOPNOTSUPP;
@@ -884,3 +1131,480 @@
cfg80211_tdls_oper_request(sdata->dev, peer, oper, reason_code, gfp);
}
EXPORT_SYMBOL(ieee80211_tdls_oper_request);
+
+static void
+iee80211_tdls_add_ch_switch_timing(u8 *buf, u16 switch_time, u16 switch_timeout)
+{
+ struct ieee80211_ch_switch_timing *ch_sw;
+
+ *buf++ = WLAN_EID_CHAN_SWITCH_TIMING;
+ *buf++ = sizeof(struct ieee80211_ch_switch_timing);
+
+ ch_sw = (void *)buf;
+ ch_sw->switch_time = cpu_to_le16(switch_time);
+ ch_sw->switch_timeout = cpu_to_le16(switch_timeout);
+}
+
+/* find switch timing IE in SKB ready for Tx */
+static const u8 *ieee80211_tdls_find_sw_timing_ie(struct sk_buff *skb)
+{
+ struct ieee80211_tdls_data *tf;
+ const u8 *ie_start;
+
+ /*
+ * Get the offset for the new location of the switch timing IE.
+ * The SKB network header will now point to the "payload_type"
+ * element of the TDLS data frame struct.
+ */
+ tf = container_of(skb->data + skb_network_offset(skb),
+ struct ieee80211_tdls_data, payload_type);
+ ie_start = tf->u.chan_switch_req.variable;
+ return cfg80211_find_ie(WLAN_EID_CHAN_SWITCH_TIMING, ie_start,
+ skb->len - (ie_start - skb->data));
+}
+
+static struct sk_buff *
+ieee80211_tdls_ch_sw_tmpl_get(struct sta_info *sta, u8 oper_class,
+ struct cfg80211_chan_def *chandef,
+ u32 *ch_sw_tm_ie_offset)
+{
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+ u8 extra_ies[2 + sizeof(struct ieee80211_sec_chan_offs_ie) +
+ 2 + sizeof(struct ieee80211_ch_switch_timing)];
+ int extra_ies_len = 2 + sizeof(struct ieee80211_ch_switch_timing);
+ u8 *pos = extra_ies;
+ struct sk_buff *skb;
+
+ /*
+ * if chandef points to a wide channel add a Secondary-Channel
+ * Offset information element
+ */
+ if (chandef->width == NL80211_CHAN_WIDTH_40) {
+ struct ieee80211_sec_chan_offs_ie *sec_chan_ie;
+ bool ht40plus;
+
+ *pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;
+ *pos++ = sizeof(*sec_chan_ie);
+ sec_chan_ie = (void *)pos;
+
+ ht40plus = cfg80211_get_chandef_type(chandef) ==
+ NL80211_CHAN_HT40PLUS;
+ sec_chan_ie->sec_chan_offs = ht40plus ?
+ IEEE80211_HT_PARAM_CHA_SEC_ABOVE :
+ IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+ pos += sizeof(*sec_chan_ie);
+
+ extra_ies_len += 2 + sizeof(struct ieee80211_sec_chan_offs_ie);
+ }
+
+ /* just set the values to 0, this is a template */
+ iee80211_tdls_add_ch_switch_timing(pos, 0, 0);
+
+ skb = ieee80211_tdls_build_mgmt_packet_data(sdata, sta->sta.addr,
+ WLAN_TDLS_CHANNEL_SWITCH_REQUEST,
+ 0, 0, !sta->sta.tdls_initiator,
+ extra_ies, extra_ies_len,
+ oper_class, chandef);
+ if (!skb)
+ return NULL;
+
+ skb = ieee80211_build_data_template(sdata, skb, 0);
+ if (IS_ERR(skb)) {
+ tdls_dbg(sdata, "Failed building TDLS channel switch frame\n");
+ return NULL;
+ }
+
+ if (ch_sw_tm_ie_offset) {
+ const u8 *tm_ie = ieee80211_tdls_find_sw_timing_ie(skb);
+
+ if (!tm_ie) {
+ tdls_dbg(sdata, "No switch timing IE in TDLS switch\n");
+ dev_kfree_skb_any(skb);
+ return NULL;
+ }
+
+ *ch_sw_tm_ie_offset = tm_ie - skb->data;
+ }
+
+ tdls_dbg(sdata,
+ "TDLS channel switch request template for %pM ch %d width %d\n",
+ sta->sta.addr, chandef->chan->center_freq, chandef->width);
+ return skb;
+}
+
+int
+ieee80211_tdls_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *addr, u8 oper_class,
+ struct cfg80211_chan_def *chandef)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
+ struct sta_info *sta;
+ struct sk_buff *skb = NULL;
+ u32 ch_sw_tm_ie;
+ int ret;
+
+ mutex_lock(&local->sta_mtx);
+ sta = sta_info_get(sdata, addr);
+ if (!sta) {
+ tdls_dbg(sdata,
+ "Invalid TDLS peer %pM for channel switch request\n",
+ addr);
+ ret = -ENOENT;
+ goto out;
+ }
+
+ if (!test_sta_flag(sta, WLAN_STA_TDLS_CHAN_SWITCH)) {
+ tdls_dbg(sdata, "TDLS channel switch unsupported by %pM\n",
+ addr);
+ ret = -ENOTSUPP;
+ goto out;
+ }
+
+ skb = ieee80211_tdls_ch_sw_tmpl_get(sta, oper_class, chandef,
+ &ch_sw_tm_ie);
+ if (!skb) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ ret = drv_tdls_channel_switch(local, sdata, &sta->sta, oper_class,
+ chandef, skb, ch_sw_tm_ie);
+ if (!ret)
+ set_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL);
+
+out:
+ mutex_unlock(&local->sta_mtx);
+ dev_kfree_skb_any(skb);
+ return ret;
+}
+
+void
+ieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy,
+ struct net_device *dev,
+ const u8 *addr)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
+ struct sta_info *sta;
+
+ mutex_lock(&local->sta_mtx);
+ sta = sta_info_get(sdata, addr);
+ if (!sta) {
+ tdls_dbg(sdata,
+ "Invalid TDLS peer %pM for channel switch cancel\n",
+ addr);
+ goto out;
+ }
+
+ if (!test_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL)) {
+ tdls_dbg(sdata, "TDLS channel switch not initiated by %pM\n",
+ addr);
+ goto out;
+ }
+
+ drv_tdls_cancel_channel_switch(local, sdata, &sta->sta);
+ clear_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL);
+
+out:
+ mutex_unlock(&local->sta_mtx);
+}
+
+static struct sk_buff *
+ieee80211_tdls_ch_sw_resp_tmpl_get(struct sta_info *sta,
+ u32 *ch_sw_tm_ie_offset)
+{
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+ struct sk_buff *skb;
+ u8 extra_ies[2 + sizeof(struct ieee80211_ch_switch_timing)];
+
+ /* initial timing are always zero in the template */
+ iee80211_tdls_add_ch_switch_timing(extra_ies, 0, 0);
+
+ skb = ieee80211_tdls_build_mgmt_packet_data(sdata, sta->sta.addr,
+ WLAN_TDLS_CHANNEL_SWITCH_RESPONSE,
+ 0, 0, !sta->sta.tdls_initiator,
+ extra_ies, sizeof(extra_ies), 0, NULL);
+ if (!skb)
+ return NULL;
+
+ skb = ieee80211_build_data_template(sdata, skb, 0);
+ if (IS_ERR(skb)) {
+ tdls_dbg(sdata,
+ "Failed building TDLS channel switch resp frame\n");
+ return NULL;
+ }
+
+ if (ch_sw_tm_ie_offset) {
+ const u8 *tm_ie = ieee80211_tdls_find_sw_timing_ie(skb);
+
+ if (!tm_ie) {
+ tdls_dbg(sdata,
+ "No switch timing IE in TDLS switch resp\n");
+ dev_kfree_skb_any(skb);
+ return NULL;
+ }
+
+ *ch_sw_tm_ie_offset = tm_ie - skb->data;
+ }
+
+ tdls_dbg(sdata, "TDLS get channel switch response template for %pM\n",
+ sta->sta.addr);
+ return skb;
+}
+
+static int
+ieee80211_process_tdls_channel_switch_resp(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee802_11_elems elems;
+ struct sta_info *sta;
+ struct ieee80211_tdls_data *tf = (void *)skb->data;
+ bool local_initiator;
+ struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
+ int baselen = offsetof(typeof(*tf), u.chan_switch_resp.variable);
+ struct ieee80211_tdls_ch_sw_params params = {};
+ int ret;
+
+ params.action_code = WLAN_TDLS_CHANNEL_SWITCH_RESPONSE;
+ params.timestamp = rx_status->device_timestamp;
+
+ if (skb->len < baselen) {
+ tdls_dbg(sdata, "TDLS channel switch resp too short: %d\n",
+ skb->len);
+ return -EINVAL;
+ }
+
+ mutex_lock(&local->sta_mtx);
+ sta = sta_info_get(sdata, tf->sa);
+ if (!sta || !test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH)) {
+ tdls_dbg(sdata, "TDLS chan switch from non-peer sta %pM\n",
+ tf->sa);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ params.sta = &sta->sta;
+ params.status = le16_to_cpu(tf->u.chan_switch_resp.status_code);
+ if (params.status != 0) {
+ ret = 0;
+ goto call_drv;
+ }
+
+ ieee802_11_parse_elems(tf->u.chan_switch_resp.variable,
+ skb->len - baselen, false, &elems);
+ if (elems.parse_error) {
+ tdls_dbg(sdata, "Invalid IEs in TDLS channel switch resp\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (!elems.ch_sw_timing || !elems.lnk_id) {
+ tdls_dbg(sdata, "TDLS channel switch resp - missing IEs\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* validate the initiator is set correctly */
+ local_initiator =
+ !memcmp(elems.lnk_id->init_sta, sdata->vif.addr, ETH_ALEN);
+ if (local_initiator == sta->sta.tdls_initiator) {
+ tdls_dbg(sdata, "TDLS chan switch invalid lnk-id initiator\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ params.switch_time = le16_to_cpu(elems.ch_sw_timing->switch_time);
+ params.switch_timeout = le16_to_cpu(elems.ch_sw_timing->switch_timeout);
+
+ params.tmpl_skb =
+ ieee80211_tdls_ch_sw_resp_tmpl_get(sta, ¶ms.ch_sw_tm_ie);
+ if (!params.tmpl_skb) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+call_drv:
+ drv_tdls_recv_channel_switch(sdata->local, sdata, ¶ms);
+
+ tdls_dbg(sdata,
+ "TDLS channel switch response received from %pM status %d\n",
+ tf->sa, params.status);
+
+out:
+ mutex_unlock(&local->sta_mtx);
+ dev_kfree_skb_any(params.tmpl_skb);
+ return ret;
+}
+
+static int
+ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee802_11_elems elems;
+ struct cfg80211_chan_def chandef;
+ struct ieee80211_channel *chan;
+ enum nl80211_channel_type chan_type;
+ int freq;
+ u8 target_channel, oper_class;
+ bool local_initiator;
+ struct sta_info *sta;
+ enum ieee80211_band band;
+ struct ieee80211_tdls_data *tf = (void *)skb->data;
+ struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
+ int baselen = offsetof(typeof(*tf), u.chan_switch_req.variable);
+ struct ieee80211_tdls_ch_sw_params params = {};
+ int ret = 0;
+
+ params.action_code = WLAN_TDLS_CHANNEL_SWITCH_REQUEST;
+ params.timestamp = rx_status->device_timestamp;
+
+ if (skb->len < baselen) {
+ tdls_dbg(sdata, "TDLS channel switch req too short: %d\n",
+ skb->len);
+ return -EINVAL;
+ }
+
+ target_channel = tf->u.chan_switch_req.target_channel;
+ oper_class = tf->u.chan_switch_req.oper_class;
+
+ /*
+ * We can't easily infer the channel band. The operating class is
+ * ambiguous - there are multiple tables (US/Europe/JP/Global). The
+ * solution here is to treat channels with number >14 as 5GHz ones,
+ * and specifically check for the (oper_class, channel) combinations
+ * where this doesn't hold. These are thankfully unique according to
+ * IEEE802.11-2012.
+ * We consider only the 2GHz and 5GHz bands and 20MHz+ channels as
+ * valid here.
+ */
+ if ((oper_class == 112 || oper_class == 2 || oper_class == 3 ||
+ oper_class == 4 || oper_class == 5 || oper_class == 6) &&
+ target_channel < 14)
+ band = IEEE80211_BAND_5GHZ;
+ else
+ band = target_channel < 14 ? IEEE80211_BAND_2GHZ :
+ IEEE80211_BAND_5GHZ;
+
+ freq = ieee80211_channel_to_frequency(target_channel, band);
+ if (freq == 0) {
+ tdls_dbg(sdata, "Invalid channel in TDLS chan switch: %d\n",
+ target_channel);
+ return -EINVAL;
+ }
+
+ chan = ieee80211_get_channel(sdata->local->hw.wiphy, freq);
+ if (!chan) {
+ tdls_dbg(sdata,
+ "Unsupported channel for TDLS chan switch: %d\n",
+ target_channel);
+ return -EINVAL;
+ }
+
+ ieee802_11_parse_elems(tf->u.chan_switch_req.variable,
+ skb->len - baselen, false, &elems);
+ if (elems.parse_error) {
+ tdls_dbg(sdata, "Invalid IEs in TDLS channel switch req\n");
+ return -EINVAL;
+ }
+
+ if (!elems.ch_sw_timing || !elems.lnk_id) {
+ tdls_dbg(sdata, "TDLS channel switch req - missing IEs\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&local->sta_mtx);
+ sta = sta_info_get(sdata, tf->sa);
+ if (!sta || !test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH)) {
+ tdls_dbg(sdata, "TDLS chan switch from non-peer sta %pM\n",
+ tf->sa);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ params.sta = &sta->sta;
+
+ /* validate the initiator is set correctly */
+ local_initiator =
+ !memcmp(elems.lnk_id->init_sta, sdata->vif.addr, ETH_ALEN);
+ if (local_initiator == sta->sta.tdls_initiator) {
+ tdls_dbg(sdata, "TDLS chan switch invalid lnk-id initiator\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (!sta->sta.ht_cap.ht_supported) {
+ chan_type = NL80211_CHAN_NO_HT;
+ } else if (!elems.sec_chan_offs) {
+ chan_type = NL80211_CHAN_HT20;
+ } else {
+ switch (elems.sec_chan_offs->sec_chan_offs) {
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+ chan_type = NL80211_CHAN_HT40PLUS;
+ break;
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+ chan_type = NL80211_CHAN_HT40MINUS;
+ break;
+ default:
+ chan_type = NL80211_CHAN_HT20;
+ break;
+ }
+ }
+
+ cfg80211_chandef_create(&chandef, chan, chan_type);
+ params.chandef = &chandef;
+
+ params.switch_time = le16_to_cpu(elems.ch_sw_timing->switch_time);
+ params.switch_timeout = le16_to_cpu(elems.ch_sw_timing->switch_timeout);
+
+ params.tmpl_skb =
+ ieee80211_tdls_ch_sw_resp_tmpl_get(sta,
+ ¶ms.ch_sw_tm_ie);
+ if (!params.tmpl_skb) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ drv_tdls_recv_channel_switch(sdata->local, sdata, ¶ms);
+
+ tdls_dbg(sdata,
+ "TDLS ch switch request received from %pM ch %d width %d\n",
+ tf->sa, params.chandef->chan->center_freq,
+ params.chandef->width);
+out:
+ mutex_unlock(&local->sta_mtx);
+ dev_kfree_skb_any(params.tmpl_skb);
+ return ret;
+}
+
+void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb)
+{
+ struct ieee80211_tdls_data *tf = (void *)skb->data;
+ struct wiphy *wiphy = sdata->local->hw.wiphy;
+
+ /* make sure the driver supports it */
+ if (!(wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH))
+ return;
+
+ /* we want to access the entire packet */
+ if (skb_linearize(skb))
+ return;
+ /*
+ * The packet/size was already validated by mac80211 Rx path, only look
+ * at the action type.
+ */
+ switch (tf->action_code) {
+ case WLAN_TDLS_CHANNEL_SWITCH_REQUEST:
+ ieee80211_process_tdls_channel_switch_req(sdata, skb);
+ break;
+ case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE:
+ ieee80211_process_tdls_channel_switch_resp(sdata, skb);
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return;
+ }
+}
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index 5153d12..3991437 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -16,6 +16,7 @@
#define STA_ENTRY __array(char, sta_addr, ETH_ALEN)
#define STA_ASSIGN (sta ? memcpy(__entry->sta_addr, sta->addr, ETH_ALEN) : memset(__entry->sta_addr, 0, ETH_ALEN))
+#define STA_NAMED_ASSIGN(s) memcpy(__entry->sta_addr, (s)->addr, ETH_ALEN)
#define STA_PR_FMT " sta:%pM"
#define STA_PR_ARG __entry->sta_addr
@@ -595,14 +596,33 @@
TP_ARGS(local, sdata)
);
-DEFINE_EVENT(local_only_evt, drv_sw_scan_start,
- TP_PROTO(struct ieee80211_local *local),
- TP_ARGS(local)
+TRACE_EVENT(drv_sw_scan_start,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ const u8 *mac_addr),
+
+ TP_ARGS(local, sdata, mac_addr),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ __array(char, mac_addr, ETH_ALEN)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ memcpy(__entry->mac_addr, mac_addr, ETH_ALEN);
+ ),
+
+ TP_printk(LOCAL_PR_FMT ", " VIF_PR_FMT ", addr:%pM",
+ LOCAL_PR_ARG, VIF_PR_ARG, __entry->mac_addr)
);
-DEFINE_EVENT(local_only_evt, drv_sw_scan_complete,
- TP_PROTO(struct ieee80211_local *local),
- TP_ARGS(local)
+DEFINE_EVENT(local_sdata_evt, drv_sw_scan_complete,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata),
+ TP_ARGS(local, sdata)
);
TRACE_EVENT(drv_get_stats,
@@ -826,6 +846,13 @@
TP_ARGS(local, sdata, sta)
);
+DEFINE_EVENT(sta_event, drv_sta_rate_tbl_update,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta),
+ TP_ARGS(local, sdata, sta)
+);
+
TRACE_EVENT(drv_conf_tx,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
@@ -987,29 +1014,34 @@
TRACE_EVENT(drv_channel_switch,
TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
struct ieee80211_channel_switch *ch_switch),
- TP_ARGS(local, ch_switch),
+ TP_ARGS(local, sdata, ch_switch),
TP_STRUCT__entry(
LOCAL_ENTRY
+ VIF_ENTRY
CHANDEF_ENTRY
__field(u64, timestamp)
+ __field(u32, device_timestamp)
__field(bool, block_tx)
__field(u8, count)
),
TP_fast_assign(
LOCAL_ASSIGN;
+ VIF_ASSIGN;
CHANDEF_ASSIGN(&ch_switch->chandef)
__entry->timestamp = ch_switch->timestamp;
+ __entry->device_timestamp = ch_switch->device_timestamp;
__entry->block_tx = ch_switch->block_tx;
__entry->count = ch_switch->count;
),
TP_printk(
- LOCAL_PR_FMT " new " CHANDEF_PR_FMT " count:%d",
- LOCAL_PR_ARG, CHANDEF_PR_ARG, __entry->count
+ LOCAL_PR_FMT VIF_PR_FMT " new " CHANDEF_PR_FMT " count:%d",
+ LOCAL_PR_ARG, VIF_PR_ARG, CHANDEF_PR_ARG, __entry->count
)
);
@@ -1557,9 +1589,26 @@
TP_ARGS(local, sdata)
);
-DEFINE_EVENT(local_only_evt, drv_restart_complete,
- TP_PROTO(struct ieee80211_local *local),
- TP_ARGS(local)
+TRACE_EVENT(drv_reconfig_complete,
+ TP_PROTO(struct ieee80211_local *local,
+ enum ieee80211_reconfig_type reconfig_type),
+ TP_ARGS(local, reconfig_type),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ __field(u8, reconfig_type)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ __entry->reconfig_type = reconfig_type;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT " reconfig_type:%d",
+ LOCAL_PR_ARG, __entry->reconfig_type
+ )
+
);
#if IS_ENABLED(CONFIG_IPV6)
@@ -1780,6 +1829,12 @@
)
);
+DEFINE_EVENT(local_sdata_evt, api_cqm_beacon_loss_notify,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata),
+ TP_ARGS(local, sdata)
+);
+
TRACE_EVENT(api_scan_completed,
TP_PROTO(struct ieee80211_local *local, bool aborted),
@@ -2106,6 +2161,175 @@
)
);
+TRACE_EVENT(drv_pre_channel_switch,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_channel_switch *ch_switch),
+
+ TP_ARGS(local, sdata, ch_switch),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ CHANDEF_ENTRY
+ __field(u64, timestamp)
+ __field(u32, device_timestamp)
+ __field(bool, block_tx)
+ __field(u8, count)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ CHANDEF_ASSIGN(&ch_switch->chandef)
+ __entry->timestamp = ch_switch->timestamp;
+ __entry->device_timestamp = ch_switch->device_timestamp;
+ __entry->block_tx = ch_switch->block_tx;
+ __entry->count = ch_switch->count;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT " prepare channel switch to "
+ CHANDEF_PR_FMT " count:%d block_tx:%d timestamp:%llu",
+ LOCAL_PR_ARG, VIF_PR_ARG, CHANDEF_PR_ARG, __entry->count,
+ __entry->block_tx, __entry->timestamp
+ )
+);
+
+DEFINE_EVENT(local_sdata_evt, drv_post_channel_switch,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata),
+ TP_ARGS(local, sdata)
+);
+
+TRACE_EVENT(drv_get_txpower,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ int dbm, int ret),
+
+ TP_ARGS(local, sdata, dbm, ret),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ __field(int, dbm)
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ __entry->dbm = dbm;
+ __entry->ret = ret;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT " dbm:%d ret:%d",
+ LOCAL_PR_ARG, VIF_PR_ARG, __entry->dbm, __entry->ret
+ )
+);
+
+TRACE_EVENT(drv_tdls_channel_switch,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta, u8 oper_class,
+ struct cfg80211_chan_def *chandef),
+
+ TP_ARGS(local, sdata, sta, oper_class, chandef),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ STA_ENTRY
+ __field(u8, oper_class)
+ CHANDEF_ENTRY
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ STA_ASSIGN;
+ __entry->oper_class = oper_class;
+ CHANDEF_ASSIGN(chandef)
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT " tdls channel switch to"
+ CHANDEF_PR_FMT " oper_class:%d " STA_PR_FMT,
+ LOCAL_PR_ARG, VIF_PR_ARG, CHANDEF_PR_ARG, __entry->oper_class,
+ STA_PR_ARG
+ )
+);
+
+TRACE_EVENT(drv_tdls_cancel_channel_switch,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta),
+
+ TP_ARGS(local, sdata, sta),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ STA_ENTRY
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ STA_ASSIGN;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT
+ " tdls cancel channel switch with " STA_PR_FMT,
+ LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG
+ )
+);
+
+TRACE_EVENT(drv_tdls_recv_channel_switch,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_tdls_ch_sw_params *params),
+
+ TP_ARGS(local, sdata, params),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ __field(u8, action_code)
+ STA_ENTRY
+ CHANDEF_ENTRY
+ __field(u32, status)
+ __field(bool, peer_initiator)
+ __field(u32, timestamp)
+ __field(u16, switch_time)
+ __field(u16, switch_timeout)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ STA_NAMED_ASSIGN(params->sta);
+ CHANDEF_ASSIGN(params->chandef)
+ __entry->peer_initiator = params->sta->tdls_initiator;
+ __entry->action_code = params->action_code;
+ __entry->status = params->status;
+ __entry->timestamp = params->timestamp;
+ __entry->switch_time = params->switch_time;
+ __entry->switch_timeout = params->switch_timeout;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT " received tdls channel switch packet"
+ " action:%d status:%d time:%d switch time:%d switch"
+ " timeout:%d initiator: %d chan:" CHANDEF_PR_FMT STA_PR_FMT,
+ LOCAL_PR_ARG, VIF_PR_ARG, __entry->action_code, __entry->status,
+ __entry->timestamp, __entry->switch_time,
+ __entry->switch_timeout, __entry->peer_initiator,
+ CHANDEF_PR_ARG, STA_PR_ARG
+ )
+);
#ifdef CPTCFG_MAC80211_MESSAGE_TRACING
#undef TRACE_SYSTEM
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 86ea121..002f28d 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -60,7 +60,7 @@
rcu_read_unlock();
/* assume HW handles this */
- if (tx->rate.flags & IEEE80211_TX_RC_MCS)
+ if (tx->rate.flags & (IEEE80211_TX_RC_MCS | IEEE80211_TX_RC_VHT_MCS))
return 0;
/* uh huh? */
@@ -296,6 +296,9 @@
*/
return TX_DROP;
+ if (tx->sdata->vif.type == NL80211_IFTYPE_OCB)
+ return TX_CONTINUE;
+
if (tx->sdata->vif.type == NL80211_IFTYPE_WDS)
return TX_CONTINUE;
@@ -1423,8 +1426,7 @@
* Returns false if the frame couldn't be transmitted but was queued instead.
*/
static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, bool txpending,
- enum ieee80211_band band)
+ struct sk_buff *skb, bool txpending)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_data tx;
@@ -1449,8 +1451,6 @@
return true;
}
- info->band = band;
-
/* set up hw_queue value early */
if (!(info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) ||
!(local->hw.flags & IEEE80211_HW_QUEUE_CONTROL))
@@ -1498,8 +1498,7 @@
return 0;
}
-void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
- enum ieee80211_band band)
+void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
@@ -1534,7 +1533,7 @@
}
ieee80211_set_qos_hdr(sdata, skb);
- ieee80211_tx(sdata, skb, false, band);
+ ieee80211_tx(sdata, skb, false);
}
static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb)
@@ -1754,7 +1753,8 @@
sdata->vif.type))
goto fail_rcu;
- ieee80211_xmit(sdata, skb, chandef->chan->band);
+ info->band = chandef->chan->band;
+ ieee80211_xmit(sdata, skb);
rcu_read_unlock();
return NETDEV_TX_OK;
@@ -1784,23 +1784,26 @@
}
/**
- * ieee80211_subif_start_xmit - netif start_xmit function for Ethernet-type
- * subinterfaces (wlan#, WDS, and VLAN interfaces)
- * @skb: packet to be sent
- * @dev: incoming interface
+ * ieee80211_build_hdr - build 802.11 header in the given frame
+ * @sdata: virtual interface to build the header for
+ * @skb: the skb to build the header in
+ * @info_flags: skb flags to set
*
- * Returns: NETDEV_TX_OK both on success and on failure. On failure skb will
- * be freed.
+ * This function takes the skb with 802.3 header and reformats the header to
+ * the appropriate IEEE 802.11 header based on which interface the packet is
+ * being transmitted on.
*
- * This function takes in an Ethernet header and encapsulates it with suitable
- * IEEE 802.11 header based on which interface the packet is coming in. The
- * encapsulated packet will then be passed to master interface, wlan#.11, for
- * transmission (through low-level driver).
+ * Note that this function also takes care of the TX status request and
+ * potential unsharing of the SKB - this needs to be interleaved with the
+ * header building.
+ *
+ * The function requires the read-side RCU lock held
+ *
+ * Returns: the (possibly reallocated) skb or an ERR_PTR() code
*/
-netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
+static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, u32 info_flags)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_info *info;
int head_need;
@@ -1816,25 +1819,17 @@
bool wme_sta = false, authorized = false, tdls_auth = false;
bool tdls_peer = false, tdls_setup_frame = false;
bool multicast;
- u32 info_flags = 0;
u16 info_id = 0;
struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_sub_if_data *ap_sdata;
enum ieee80211_band band;
-
- if (unlikely(skb->len < ETH_HLEN))
- goto fail;
+ int ret;
/* convert Ethernet header to proper 802.11 header (based on
* operation mode) */
ethertype = (skb->data[12] << 8) | skb->data[13];
fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA);
- rcu_read_lock();
-
- /* Measure frame arrival for Tx latency statistics calculation */
- ieee80211_tx_latency_start_msrmnt(local, skb);
-
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP_VLAN:
sta = rcu_dereference(sdata->u.vlan.sta);
@@ -1852,8 +1847,10 @@
ap_sdata = container_of(sdata->bss, struct ieee80211_sub_if_data,
u.ap);
chanctx_conf = rcu_dereference(ap_sdata->vif.chanctx_conf);
- if (!chanctx_conf)
- goto fail_rcu;
+ if (!chanctx_conf) {
+ ret = -ENOTCONN;
+ goto free;
+ }
band = chanctx_conf->def.chan->band;
if (sta)
break;
@@ -1861,8 +1858,10 @@
case NL80211_IFTYPE_AP:
if (sdata->vif.type == NL80211_IFTYPE_AP)
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
- if (!chanctx_conf)
- goto fail_rcu;
+ if (!chanctx_conf) {
+ ret = -ENOTCONN;
+ goto free;
+ }
fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
/* DA BSSID SA */
memcpy(hdr.addr1, skb->data, ETH_ALEN);
@@ -1949,8 +1948,10 @@
}
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
- if (!chanctx_conf)
- goto fail_rcu;
+ if (!chanctx_conf) {
+ ret = -ENOTCONN;
+ goto free;
+ }
band = chanctx_conf->def.chan->band;
break;
#endif
@@ -1980,8 +1981,10 @@
* of a link teardown after a TDLS sta is removed due to being
* unreachable.
*/
- if (tdls_peer && !tdls_auth && !tdls_setup_frame)
- goto fail_rcu;
+ if (tdls_peer && !tdls_auth && !tdls_setup_frame) {
+ ret = -EINVAL;
+ goto free;
+ }
/* send direct packets to authorized TDLS peers */
if (tdls_peer && tdls_auth) {
@@ -2009,8 +2012,23 @@
hdrlen = 24;
}
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
- if (!chanctx_conf)
- goto fail_rcu;
+ if (!chanctx_conf) {
+ ret = -ENOTCONN;
+ goto free;
+ }
+ band = chanctx_conf->def.chan->band;
+ break;
+ case NL80211_IFTYPE_OCB:
+ /* DA SA BSSID */
+ memcpy(hdr.addr1, skb->data, ETH_ALEN);
+ memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
+ eth_broadcast_addr(hdr.addr3);
+ hdrlen = 24;
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (!chanctx_conf) {
+ ret = -ENOTCONN;
+ goto free;
+ }
band = chanctx_conf->def.chan->band;
break;
case NL80211_IFTYPE_ADHOC:
@@ -2020,12 +2038,15 @@
memcpy(hdr.addr3, sdata->u.ibss.bssid, ETH_ALEN);
hdrlen = 24;
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
- if (!chanctx_conf)
- goto fail_rcu;
+ if (!chanctx_conf) {
+ ret = -ENOTCONN;
+ goto free;
+ }
band = chanctx_conf->def.chan->band;
break;
default:
- goto fail_rcu;
+ ret = -EINVAL;
+ goto free;
}
/*
@@ -2057,17 +2078,19 @@
* EAPOL frames from the local station.
*/
if (unlikely(!ieee80211_vif_is_mesh(&sdata->vif) &&
+ (sdata->vif.type != NL80211_IFTYPE_OCB) &&
!multicast && !authorized &&
(cpu_to_be16(ethertype) != sdata->control_port_protocol ||
!ether_addr_equal(sdata->vif.addr, skb->data + ETH_ALEN)))) {
#ifdef CPTCFG_MAC80211_VERBOSE_DEBUG
net_info_ratelimited("%s: dropped frame to %pM (unauthorized port)\n",
- dev->name, hdr.addr1);
+ sdata->name, hdr.addr1);
#endif
I802_DEBUG_INC(local->tx_handlers_drop_unauth_port);
- goto fail_rcu;
+ ret = -EPERM;
+ goto free;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0))
@@ -2106,8 +2129,10 @@
skb = skb_clone(skb, GFP_ATOMIC);
kfree_skb(tmp_skb);
- if (!skb)
- goto fail_rcu;
+ if (!skb) {
+ ret = -ENOMEM;
+ goto free;
+ }
}
hdr.frame_control = fc;
@@ -2156,7 +2181,7 @@
if (ieee80211_skb_resize(sdata, skb, head_need, true)) {
ieee80211_free_txskb(&local->hw, skb);
skb = NULL;
- goto fail_rcu;
+ return ERR_PTR(-ENOMEM);
}
}
@@ -2190,9 +2215,6 @@
nh_pos += hdrlen;
h_pos += hdrlen;
- dev->stats.tx_packets++;
- dev->stats.tx_bytes += skb->len;
-
/* Update skb pointers to various headers since this modified frame
* is going to go through Linux networking code that may potentially
* need things like pointer to IP header. */
@@ -2203,23 +2225,90 @@
info = IEEE80211_SKB_CB(skb);
memset(info, 0, sizeof(*info));
- dev->trans_start = jiffies;
-
info->flags = info_flags;
info->ack_frame_id = info_id;
+ info->band = band;
- ieee80211_xmit(sdata, skb, band);
+ return skb;
+ free:
+ kfree_skb(skb);
+ return ERR_PTR(ret);
+}
+
+void __ieee80211_subif_start_xmit(struct sk_buff *skb,
+ struct net_device *dev,
+ u32 info_flags)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
+
+ if (unlikely(skb->len < ETH_HLEN)) {
+ kfree_skb(skb);
+ return;
+ }
+
+ rcu_read_lock();
+
+ /* Measure frame arrival for Tx latency statistics calculation */
+ ieee80211_tx_latency_start_msrmnt(local, skb);
+
+ skb = ieee80211_build_hdr(sdata, skb, info_flags);
+ if (IS_ERR(skb))
+ goto out;
+
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += skb->len;
+ dev->trans_start = jiffies;
+
+ ieee80211_xmit(sdata, skb);
+ out:
rcu_read_unlock();
+}
- return NETDEV_TX_OK;
-
- fail_rcu:
- rcu_read_unlock();
- fail:
- dev_kfree_skb(skb);
+/**
+ * ieee80211_subif_start_xmit - netif start_xmit function for 802.3 vifs
+ * @skb: packet to be sent
+ * @dev: incoming interface
+ *
+ * On failure skb will be freed.
+ */
+netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ __ieee80211_subif_start_xmit(skb, dev, 0);
return NETDEV_TX_OK;
}
+struct sk_buff *
+ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, u32 info_flags)
+{
+ struct ieee80211_hdr *hdr;
+ struct ieee80211_tx_data tx = {
+ .local = sdata->local,
+ .sdata = sdata,
+ };
+
+ rcu_read_lock();
+
+ skb = ieee80211_build_hdr(sdata, skb, info_flags);
+ if (IS_ERR(skb))
+ goto out;
+
+ hdr = (void *)skb->data;
+ tx.sta = sta_info_get(sdata, hdr->addr1);
+ tx.skb = skb;
+
+ if (ieee80211_tx_h_select_key(&tx) != TX_CONTINUE) {
+ rcu_read_unlock();
+ kfree_skb(skb);
+ return ERR_PTR(-EINVAL);
+ }
+
+out:
+ rcu_read_unlock();
+ return skb;
+}
/*
* ieee80211_clear_tx_pending may not be called in a context where
@@ -2259,8 +2348,8 @@
dev_kfree_skb(skb);
return true;
}
- result = ieee80211_tx(sdata, skb, true,
- chanctx_conf->def.chan->band);
+ info->band = chanctx_conf->def.chan->band;
+ result = ieee80211_tx(sdata, skb, true);
} else {
struct sk_buff_head skbs;
@@ -2874,19 +2963,16 @@
EXPORT_SYMBOL(ieee80211_nullfunc_get);
struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
+ const u8 *src_addr,
const u8 *ssid, size_t ssid_len,
size_t tailroom)
{
- struct ieee80211_sub_if_data *sdata;
- struct ieee80211_local *local;
+ struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_hdr_3addr *hdr;
struct sk_buff *skb;
size_t ie_ssid_len;
u8 *pos;
- sdata = vif_to_sdata(vif);
- local = sdata->local;
ie_ssid_len = 2 + ssid_len;
skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*hdr) +
@@ -2901,7 +2987,7 @@
hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_PROBE_REQ);
eth_broadcast_addr(hdr->addr1);
- memcpy(hdr->addr2, vif->addr, ETH_ALEN);
+ memcpy(hdr->addr2, src_addr, ETH_ALEN);
eth_broadcast_addr(hdr->addr3);
pos = skb_put(skb, ie_ssid_len);
@@ -3020,6 +3106,97 @@
}
EXPORT_SYMBOL(ieee80211_get_buffered_bc);
+int ieee80211_reserve_tid(struct ieee80211_sta *pubsta, u8 tid)
+{
+ struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+ struct ieee80211_local *local = sdata->local;
+ int ret;
+ u32 queues;
+
+ lockdep_assert_held(&local->sta_mtx);
+
+ /* only some cases are supported right now */
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
+ break;
+ default:
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ if (WARN_ON(tid >= IEEE80211_NUM_UPS))
+ return -EINVAL;
+
+ if (sta->reserved_tid == tid) {
+ ret = 0;
+ goto out;
+ }
+
+ if (sta->reserved_tid != IEEE80211_TID_UNRESERVED) {
+ sdata_err(sdata, "TID reservation already active\n");
+ ret = -EALREADY;
+ goto out;
+ }
+
+ ieee80211_stop_vif_queues(sdata->local, sdata,
+ IEEE80211_QUEUE_STOP_REASON_RESERVE_TID);
+
+ synchronize_net();
+
+ /* Tear down BA sessions so we stop aggregating on this TID */
+ if (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION) {
+ set_sta_flag(sta, WLAN_STA_BLOCK_BA);
+ __ieee80211_stop_tx_ba_session(sta, tid,
+ AGG_STOP_LOCAL_REQUEST);
+ }
+
+ queues = BIT(sdata->vif.hw_queue[ieee802_1d_to_ac[tid]]);
+ __ieee80211_flush_queues(local, sdata, queues);
+
+ sta->reserved_tid = tid;
+
+ ieee80211_wake_vif_queues(local, sdata,
+ IEEE80211_QUEUE_STOP_REASON_RESERVE_TID);
+
+ if (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION)
+ clear_sta_flag(sta, WLAN_STA_BLOCK_BA);
+
+ ret = 0;
+ out:
+ return ret;
+}
+EXPORT_SYMBOL(ieee80211_reserve_tid);
+
+void ieee80211_unreserve_tid(struct ieee80211_sta *pubsta, u8 tid)
+{
+ struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+
+ lockdep_assert_held(&sdata->local->sta_mtx);
+
+ /* only some cases are supported right now */
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
+ break;
+ default:
+ WARN_ON(1);
+ return;
+ }
+
+ if (tid != sta->reserved_tid) {
+ sdata_err(sdata, "TID to unreserve (%d) isn't reserved\n", tid);
+ return;
+ }
+
+ sta->reserved_tid = IEEE80211_TID_UNRESERVED;
+}
+EXPORT_SYMBOL(ieee80211_unreserve_tid);
+
void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, int tid,
enum ieee80211_band band)
@@ -3041,6 +3218,7 @@
* requirements are that we do not come into tx with bhs on.
*/
local_bh_disable();
- ieee80211_xmit(sdata, skb, band);
+ IEEE80211_SKB_CB(skb)->band = band;
+ ieee80211_xmit(sdata, skb);
local_bh_enable();
}
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 3c61060..974ebe7 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -576,15 +576,19 @@
return queues;
}
-void ieee80211_flush_queues(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata)
+void __ieee80211_flush_queues(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ unsigned int queues)
{
- unsigned int queues;
-
if (!local->ops->flush)
return;
- queues = ieee80211_get_vif_queues(local, sdata);
+ /*
+ * If no queue was set, or if the HW doesn't support
+ * IEEE80211_HW_QUEUE_CONTROL - flush all queues
+ */
+ if (!queues || !(local->hw.flags & IEEE80211_HW_QUEUE_CONTROL))
+ queues = ieee80211_get_vif_queues(local, sdata);
ieee80211_stop_queues_by_reason(&local->hw, queues,
IEEE80211_QUEUE_STOP_REASON_FLUSH,
@@ -597,6 +601,12 @@
false);
}
+void ieee80211_flush_queues(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata)
+{
+ __ieee80211_flush_queues(local, sdata, 0);
+}
+
void ieee80211_stop_vif_queues(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
enum queue_stop_reason reason)
@@ -693,6 +703,34 @@
}
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl);
+static void __iterate_stations(struct ieee80211_local *local,
+ void (*iterator)(void *data,
+ struct ieee80211_sta *sta),
+ void *data)
+{
+ struct sta_info *sta;
+
+ list_for_each_entry_rcu(sta, &local->sta_list, list) {
+ if (!sta->uploaded)
+ continue;
+
+ iterator(data, &sta->sta);
+ }
+}
+
+void ieee80211_iterate_stations_atomic(struct ieee80211_hw *hw,
+ void (*iterator)(void *data,
+ struct ieee80211_sta *sta),
+ void *data)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+
+ rcu_read_lock();
+ __iterate_stations(local, iterator, data);
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL_GPL(ieee80211_iterate_stations_atomic);
+
struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
@@ -803,6 +841,9 @@
case WLAN_EID_SECONDARY_CHANNEL_OFFSET:
case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
case WLAN_EID_CHAN_SWITCH_PARAM:
+ case WLAN_EID_EXT_CAPABILITY:
+ case WLAN_EID_CHAN_SWITCH_TIMING:
+ case WLAN_EID_LINK_ID:
/*
* not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible
* that if the content gets bigger it might be needed more than once
@@ -822,6 +863,24 @@
elem_parse_failed = false;
switch (id) {
+ case WLAN_EID_LINK_ID:
+ if (elen + 2 != sizeof(struct ieee80211_tdls_lnkie)) {
+ elem_parse_failed = true;
+ break;
+ }
+ elems->lnk_id = (void *)(pos - 2);
+ break;
+ case WLAN_EID_CHAN_SWITCH_TIMING:
+ if (elen != sizeof(struct ieee80211_ch_switch_timing)) {
+ elem_parse_failed = true;
+ break;
+ }
+ elems->ch_sw_timing = (void *)pos;
+ break;
+ case WLAN_EID_EXT_CAPABILITY:
+ elems->ext_capab = pos;
+ elems->ext_capab_len = elen;
+ break;
case WLAN_EID_SSID:
elems->ssid = pos;
elems->ssid_len = elen;
@@ -1073,6 +1132,7 @@
struct ieee80211_chanctx_conf *chanctx_conf;
int ac;
bool use_11b, enable_qos;
+ bool is_ocb; /* Use another EDCA parameters if dot11OCBActivated=true */
int aCWmin, aCWmax;
if (!local->ops->conf_tx)
@@ -1097,6 +1157,8 @@
*/
enable_qos = (sdata->vif.type != NL80211_IFTYPE_STATION);
+ is_ocb = (sdata->vif.type == NL80211_IFTYPE_OCB);
+
/* Set defaults according to 802.11-2007 Table 7-37 */
aCWmax = 1023;
if (use_11b)
@@ -1118,7 +1180,10 @@
qparam.cw_max = aCWmax;
qparam.cw_min = aCWmin;
qparam.txop = 0;
- qparam.aifs = 7;
+ if (is_ocb)
+ qparam.aifs = 9;
+ else
+ qparam.aifs = 7;
break;
/* never happens but let's not leave undefined */
default:
@@ -1126,21 +1191,32 @@
qparam.cw_max = aCWmax;
qparam.cw_min = aCWmin;
qparam.txop = 0;
- qparam.aifs = 3;
+ if (is_ocb)
+ qparam.aifs = 6;
+ else
+ qparam.aifs = 3;
break;
case IEEE80211_AC_VI:
qparam.cw_max = aCWmin;
qparam.cw_min = (aCWmin + 1) / 2 - 1;
- if (use_11b)
+ if (is_ocb)
+ qparam.txop = 0;
+ else if (use_11b)
qparam.txop = 6016/32;
else
qparam.txop = 3008/32;
- qparam.aifs = 2;
+
+ if (is_ocb)
+ qparam.aifs = 3;
+ else
+ qparam.aifs = 2;
break;
case IEEE80211_AC_VO:
qparam.cw_max = (aCWmin + 1) / 2 - 1;
qparam.cw_min = (aCWmin + 1) / 4 - 1;
- if (use_11b)
+ if (is_ocb)
+ qparam.txop = 0;
+ else if (use_11b)
qparam.txop = 3264/32;
else
qparam.txop = 1504/32;
@@ -1263,6 +1339,7 @@
int ext_rates_len;
int shift;
u32 rate_flags;
+ bool have_80mhz = false;
*offset = 0;
@@ -1391,7 +1468,15 @@
*offset = noffset;
}
- if (sband->vht_cap.vht_supported) {
+ /* Check if any channel in this sband supports at least 80 MHz */
+ for (i = 0; i < sband->n_channels; i++) {
+ if (!(sband->channels[i].flags & IEEE80211_CHAN_NO_80MHZ)) {
+ have_80mhz = true;
+ break;
+ }
+ }
+
+ if (sband->vht_cap.vht_supported && have_80mhz) {
if (end - pos < 2 + sizeof(struct ieee80211_vht_cap))
goto out_err;
pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap,
@@ -1447,7 +1532,8 @@
};
struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
- u8 *dst, u32 ratemask,
+ const u8 *src, const u8 *dst,
+ u32 ratemask,
struct ieee80211_channel *chan,
const u8 *ssid, size_t ssid_len,
const u8 *ie, size_t ie_len,
@@ -1472,8 +1558,8 @@
else
chandef.chan = chan;
- skb = ieee80211_probereq_get(&local->hw, &sdata->vif,
- ssid, ssid_len, 100 + ie_len);
+ skb = ieee80211_probereq_get(&local->hw, src, ssid, ssid_len,
+ 100 + ie_len);
if (!skb)
return NULL;
@@ -1495,7 +1581,8 @@
return skb;
}
-void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
+void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata,
+ const u8 *src, const u8 *dst,
const u8 *ssid, size_t ssid_len,
const u8 *ie, size_t ie_len,
u32 ratemask, bool directed, u32 tx_flags,
@@ -1503,7 +1590,7 @@
{
struct sk_buff *skb;
- skb = ieee80211_build_probe_req(sdata, dst, ratemask, channel,
+ skb = ieee80211_build_probe_req(sdata, src, dst, ratemask, channel,
ssid, ssid_len,
ie, ie_len, directed);
if (skb) {
@@ -1645,6 +1732,7 @@
int res, i;
bool reconfig_due_to_wowlan = false;
struct ieee80211_sub_if_data *sched_scan_sdata;
+ struct cfg80211_sched_scan_request *sched_scan_req;
bool sched_scan_stopped = false;
#ifdef CONFIG_PM
@@ -1813,6 +1901,10 @@
ieee80211_bss_info_change_notify(sdata, changed);
sdata_unlock(sdata);
break;
+ case NL80211_IFTYPE_OCB:
+ changed |= BSS_CHANGED_OCB;
+ ieee80211_bss_info_change_notify(sdata, changed);
+ break;
case NL80211_IFTYPE_ADHOC:
changed |= BSS_CHANGED_IBSS;
/* fall through */
@@ -1931,13 +2023,15 @@
mutex_lock(&local->mtx);
sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata,
lockdep_is_held(&local->mtx));
- if (sched_scan_sdata && local->sched_scan_req)
+ sched_scan_req = rcu_dereference_protected(local->sched_scan_req,
+ lockdep_is_held(&local->mtx));
+ if (sched_scan_sdata && sched_scan_req)
/*
* Sched scan stopped, but we don't want to report it. Instead,
* we're trying to reschedule.
*/
if (__ieee80211_request_sched_scan_start(sched_scan_sdata,
- local->sched_scan_req))
+ sched_scan_req))
sched_scan_stopped = true;
mutex_unlock(&local->mtx);
@@ -1949,7 +2043,7 @@
* We may want to change that later, however.
*/
if (!local->suspended || reconfig_due_to_wowlan)
- drv_restart_complete(local);
+ drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_RESTART);
if (!local->suspended)
return 0;
@@ -1960,6 +2054,9 @@
mb();
local->resuming = false;
+ if (!reconfig_due_to_wowlan)
+ drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_SUSPEND);
+
list_for_each_entry(sdata, &local->interfaces, list) {
if (!ieee80211_sdata_running(sdata))
continue;
@@ -2052,42 +2149,36 @@
return false;
}
-/**
- * ieee80211_ie_split - split an IE buffer according to ordering
- *
- * @ies: the IE buffer
- * @ielen: the length of the IE buffer
- * @ids: an array with element IDs that are allowed before
- * the split
- * @n_ids: the size of the element ID array
- * @offset: offset where to start splitting in the buffer
- *
- * This function splits an IE buffer by updating the @offset
- * variable to point to the location where the buffer should be
- * split.
- *
- * It assumes that the given IE buffer is well-formed, this
- * has to be guaranteed by the caller!
- *
- * It also assumes that the IEs in the buffer are ordered
- * correctly, if not the result of using this function will not
- * be ordered correctly either, i.e. it does no reordering.
- *
- * The function returns the offset where the next part of the
- * buffer starts, which may be @ielen if the entire (remainder)
- * of the buffer should be used.
- */
-size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
- const u8 *ids, int n_ids, size_t offset)
+size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,
+ const u8 *ids, int n_ids,
+ const u8 *after_ric, int n_after_ric,
+ size_t offset)
{
size_t pos = offset;
- while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos]))
- pos += 2 + ies[pos + 1];
+ while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos])) {
+ if (ies[pos] == WLAN_EID_RIC_DATA && n_after_ric) {
+ pos += 2 + ies[pos + 1];
+
+ while (pos < ielen &&
+ !ieee80211_id_in_list(after_ric, n_after_ric,
+ ies[pos]))
+ pos += 2 + ies[pos + 1];
+ } else {
+ pos += 2 + ies[pos + 1];
+ }
+ }
return pos;
}
+size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
+ const u8 *ids, int n_ids, size_t offset)
+{
+ return ieee80211_ie_split_ric(ies, ielen, ids, n_ids, NULL, 0, offset);
+}
+EXPORT_SYMBOL(ieee80211_ie_split);
+
size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset)
{
size_t pos = offset;
@@ -2526,11 +2617,23 @@
struct ieee80211_local *local =
container_of(work, struct ieee80211_local, radar_detected_work);
struct cfg80211_chan_def chandef = local->hw.conf.chandef;
+ struct ieee80211_chanctx *ctx;
+ int num_chanctx = 0;
+
+ mutex_lock(&local->chanctx_mtx);
+ list_for_each_entry(ctx, &local->chanctx_list, list) {
+ if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER)
+ continue;
+
+ num_chanctx++;
+ chandef = ctx->conf.def;
+ }
+ mutex_unlock(&local->chanctx_mtx);
ieee80211_dfs_cac_cancel(local);
- if (local->use_chanctx)
- /* currently not handled */
+ if (num_chanctx > 1)
+ /* XXX: multi-channel is not supported yet */
WARN_ON(1);
else
cfg80211_radar_event(local->hw.wiphy, &chandef, GFP_KERNEL);
diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c
index 671ce0d..bc9e8fc 100644
--- a/net/mac80211/vht.c
+++ b/net/mac80211/vht.c
@@ -287,6 +287,8 @@
/* fall through */
case NL80211_CHAN_WIDTH_20_NOHT:
case NL80211_CHAN_WIDTH_20:
+ bw = IEEE80211_STA_RX_BW_20;
+ break;
case NL80211_CHAN_WIDTH_40:
bw = sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c
index 9181fb6..a4220e9 100644
--- a/net/mac80211/wep.c
+++ b/net/mac80211/wep.c
@@ -111,8 +111,6 @@
(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))
return newhdr + hdrlen;
- skb_set_network_header(skb, skb_network_offset(skb) +
- IEEE80211_WEP_IV_LEN);
ieee80211_wep_get_iv(local, keylen, keyidx, newhdr + hdrlen);
return newhdr + hdrlen;
}
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c
index 77509d8..c04e74d 100644
--- a/net/mac80211/wme.c
+++ b/net/mac80211/wme.c
@@ -53,11 +53,49 @@
}
}
-static u16 ieee80211_downgrade_queue(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb)
+/**
+ * ieee80211_fix_reserved_tid - return the TID to use if this one is reserved
+ * @tid: the assumed-reserved TID
+ *
+ * Returns: the alternative TID to use, or 0 on error
+ */
+static inline u8 ieee80211_fix_reserved_tid(u8 tid)
{
+ switch (tid) {
+ case 0:
+ return 3;
+ case 1:
+ return 2;
+ case 2:
+ return 1;
+ case 3:
+ return 0;
+ case 4:
+ return 5;
+ case 5:
+ return 4;
+ case 6:
+ return 7;
+ case 7:
+ return 6;
+ }
+
+ return 0;
+}
+
+static u16 ieee80211_downgrade_queue(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta, struct sk_buff *skb)
+{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+
/* in case we are a client verify acm is not set for this ac */
- while (unlikely(sdata->wmm_acm & BIT(skb->priority))) {
+ while (sdata->wmm_acm & BIT(skb->priority)) {
+ int ac = ieee802_1d_to_ac[skb->priority];
+
+ if (ifmgd->tx_tspec[ac].admitted_time &&
+ skb->priority == ifmgd->tx_tspec[ac].up)
+ return ac;
+
if (wme_downgrade_ac(skb)) {
/*
* This should not really happen. The AP has marked all
@@ -69,6 +107,10 @@
}
}
+ /* Check to see if this is a reserved TID */
+ if (sta && sta->reserved_tid == skb->priority)
+ skb->priority = ieee80211_fix_reserved_tid(skb->priority);
+
/* look up which queue to use for frames with this 1d tag */
return ieee802_1d_to_ac[skb->priority];
}
@@ -96,7 +138,7 @@
p = ieee80211_get_qos_ctl(hdr);
skb->priority = *p & IEEE80211_QOS_CTL_TAG1D_MASK;
- return ieee80211_downgrade_queue(sdata, skb);
+ return ieee80211_downgrade_queue(sdata, NULL, skb);
}
/* Indicate which queue to use. */
@@ -108,6 +150,7 @@
const u8 *ra = NULL;
bool qos = false;
struct mac80211_qos_map *qos_map;
+ u16 ret;
if (local->hw.queues < IEEE80211_NUM_ACS || skb->len < 6) {
skb->priority = 0; /* required for correct WPA/11i MIC */
@@ -134,11 +177,20 @@
break;
#endif
case NL80211_IFTYPE_STATION:
+ /* might be a TDLS station */
+ sta = sta_info_get(sdata, skb->data);
+ if (sta)
+ qos = sta->sta.wme;
+
ra = sdata->u.mgd.bssid;
break;
case NL80211_IFTYPE_ADHOC:
ra = skb->data;
break;
+ case NL80211_IFTYPE_OCB:
+ /* all stations are required to support WME */
+ qos = true;
+ break;
default:
break;
}
@@ -148,27 +200,29 @@
if (sta)
qos = sta->sta.wme;
}
- rcu_read_unlock();
if (!qos) {
skb->priority = 0; /* required for correct WPA/11i MIC */
- return IEEE80211_AC_BE;
+ ret = IEEE80211_AC_BE;
+ goto out;
}
if (skb->protocol == sdata->control_port_protocol) {
skb->priority = 7;
- return ieee80211_downgrade_queue(sdata, skb);
+ goto downgrade;
}
/* use the data classifier to determine what 802.1d tag the
* data frame has */
- rcu_read_lock();
qos_map = rcu_dereference(sdata->qos_map);
skb->priority = cfg80211_classify8021d(skb, qos_map ?
&qos_map->qos_map : NULL);
- rcu_read_unlock();
- return ieee80211_downgrade_queue(sdata, skb);
+ downgrade:
+ ret = ieee80211_downgrade_queue(sdata, sta, skb);
+ out:
+ rcu_read_unlock();
+ return ret;
}
/**
diff --git a/net/mac80211/wme.h b/net/mac80211/wme.h
index 7fea4bb..80151ed 100644
--- a/net/mac80211/wme.h
+++ b/net/mac80211/wme.h
@@ -13,8 +13,6 @@
#include <linux/netdevice.h>
#include "ieee80211_i.h"
-extern const int ieee802_1d_to_ac[8];
-
u16 ieee80211_select_queue_80211(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb,
struct ieee80211_hdr *hdr);
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index 983527a..12398fd 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -209,8 +209,6 @@
pos = skb_push(skb, IEEE80211_TKIP_IV_LEN);
memmove(pos, pos + IEEE80211_TKIP_IV_LEN, hdrlen);
- skb_set_network_header(skb, skb_network_offset(skb) +
- IEEE80211_TKIP_IV_LEN);
pos += hdrlen;
/* the HW only needs room for the IV, but not the actual IV */
@@ -434,8 +432,6 @@
pos = skb_push(skb, IEEE80211_CCMP_HDR_LEN);
memmove(pos, pos + IEEE80211_CCMP_HDR_LEN, hdrlen);
- skb_set_network_header(skb, skb_network_offset(skb) +
- IEEE80211_CCMP_HDR_LEN);
/* the HW only needs room for the IV, but not the actual IV */
if (info->control.hw_key &&
@@ -575,7 +571,6 @@
pos = skb_push(skb, cs->hdr_len);
memmove(pos, pos + cs->hdr_len, hdrlen);
- skb_set_network_header(skb, skb_network_offset(skb) + cs->hdr_len);
return TX_CONTINUE;
}
diff --git a/net/mac802154/Kconfig b/net/mac802154/Kconfig
index e62e73f..4621ecb 100644
--- a/net/mac802154/Kconfig
+++ b/net/mac802154/Kconfig
@@ -4,7 +4,7 @@
depends on IEEE802154
depends on CRC_CCITT
depends on CRYPTO_AUTHENC
- select BACKPORT_CRYPTO_CCM
+ select BPAUTO_CRYPTO_CCM
depends on CRYPTO_CTR
depends on CRYPTO_AES
---help---
@@ -17,5 +17,5 @@
been tested yet!
If you plan to use HardMAC IEEE 802.15.4 devices, you can
- say N here. Alternatievly you can say M to compile it as
+ say N here. Alternatively you can say M to compile it as
module.
diff --git a/net/mac802154/Makefile b/net/mac802154/Makefile
index 562844a..4ea25d0 100644
--- a/net/mac802154/Makefile
+++ b/net/mac802154/Makefile
@@ -1,5 +1,5 @@
obj-$(CPTCFG_MAC802154) += mac802154.o
-mac802154-objs := ieee802154_dev.o rx.o tx.o mac_cmd.o mib.o \
- monitor.o wpan.o llsec.o
+mac802154-objs := main.o rx.o tx.o mac_cmd.o mib.o \
+ iface.o llsec.o util.o cfg.o
ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/mac802154/cfg.c b/net/mac802154/cfg.c
new file mode 100644
index 0000000..c035708
--- /dev/null
+++ b/net/mac802154/cfg.c
@@ -0,0 +1,210 @@
+/* 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.
+ *
+ * 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.
+ *
+ * Authors:
+ * Alexander Aring <aar@pengutronix.de>
+ *
+ * Based on: net/mac80211/cfg.c
+ */
+
+#include <net/rtnetlink.h>
+#include <net/cfg802154.h>
+
+#include "ieee802154_i.h"
+#include "driver-ops.h"
+#include "cfg.h"
+
+static struct net_device *
+ieee802154_add_iface_deprecated(struct wpan_phy *wpan_phy,
+ const char *name, int type)
+{
+ struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
+ struct net_device *dev;
+
+ rtnl_lock();
+ dev = ieee802154_if_add(local, name, type,
+ cpu_to_le64(0x0000000000000000ULL));
+ rtnl_unlock();
+
+ return dev;
+}
+
+static void ieee802154_del_iface_deprecated(struct wpan_phy *wpan_phy,
+ struct net_device *dev)
+{
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+
+ ieee802154_if_remove(sdata);
+}
+
+static int
+ieee802154_add_iface(struct wpan_phy *phy, const char *name,
+ enum nl802154_iftype type, __le64 extended_addr)
+{
+ struct ieee802154_local *local = wpan_phy_priv(phy);
+ struct net_device *err;
+
+ err = ieee802154_if_add(local, name, type, extended_addr);
+ if (IS_ERR(err))
+ return PTR_ERR(err);
+
+ return 0;
+}
+
+static int
+ieee802154_del_iface(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev)
+{
+ ieee802154_if_remove(IEEE802154_WPAN_DEV_TO_SUB_IF(wpan_dev));
+
+ return 0;
+}
+
+static int
+ieee802154_set_channel(struct wpan_phy *wpan_phy, u8 page, u8 channel)
+{
+ struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
+ int ret;
+
+ ASSERT_RTNL();
+
+ /* check if phy support this setting */
+ if (!(wpan_phy->channels_supported[page] & BIT(channel)))
+ return -EINVAL;
+
+ ret = drv_set_channel(local, page, channel);
+ if (!ret) {
+ wpan_phy->current_page = page;
+ wpan_phy->current_channel = channel;
+ }
+
+ return ret;
+}
+
+static int
+ieee802154_set_pan_id(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
+ __le16 pan_id)
+{
+ ASSERT_RTNL();
+
+ /* TODO
+ * I am not sure about to check here on broadcast pan_id.
+ * Broadcast is a valid setting, comment from 802.15.4:
+ * If this value is 0xffff, the device is not associated.
+ *
+ * This could useful to simple deassociate an device.
+ */
+ if (pan_id == cpu_to_le16(IEEE802154_PAN_ID_BROADCAST))
+ return -EINVAL;
+
+ wpan_dev->pan_id = pan_id;
+ return 0;
+}
+
+static int
+ieee802154_set_backoff_exponent(struct wpan_phy *wpan_phy,
+ struct wpan_dev *wpan_dev,
+ u8 min_be, u8 max_be)
+{
+ struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
+
+ ASSERT_RTNL();
+
+ if (!(local->hw.flags & IEEE802154_HW_CSMA_PARAMS))
+ return -EOPNOTSUPP;
+
+ wpan_dev->min_be = min_be;
+ wpan_dev->max_be = max_be;
+ return 0;
+}
+
+static int
+ieee802154_set_short_addr(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
+ __le16 short_addr)
+{
+ ASSERT_RTNL();
+
+ /* TODO
+ * I am not sure about to check here on broadcast short_addr.
+ * Broadcast is a valid setting, comment from 802.15.4:
+ * A value of 0xfffe indicates that the device has
+ * associated but has not been allocated an address. A
+ * value of 0xffff indicates that the device does not
+ * have a short address.
+ *
+ * I think we should allow to set these settings but
+ * don't allow to allow socket communication with it.
+ */
+ if (short_addr == cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC) ||
+ short_addr == cpu_to_le16(IEEE802154_ADDR_SHORT_BROADCAST))
+ return -EINVAL;
+
+ wpan_dev->short_addr = short_addr;
+ return 0;
+}
+
+static int
+ieee802154_set_max_csma_backoffs(struct wpan_phy *wpan_phy,
+ struct wpan_dev *wpan_dev,
+ u8 max_csma_backoffs)
+{
+ struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
+
+ ASSERT_RTNL();
+
+ if (!(local->hw.flags & IEEE802154_HW_CSMA_PARAMS))
+ return -EOPNOTSUPP;
+
+ wpan_dev->csma_retries = max_csma_backoffs;
+ return 0;
+}
+
+static int
+ieee802154_set_max_frame_retries(struct wpan_phy *wpan_phy,
+ struct wpan_dev *wpan_dev,
+ s8 max_frame_retries)
+{
+ struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
+
+ ASSERT_RTNL();
+
+ if (!(local->hw.flags & IEEE802154_HW_FRAME_RETRIES))
+ return -EOPNOTSUPP;
+
+ wpan_dev->frame_retries = max_frame_retries;
+ return 0;
+}
+
+static int
+ieee802154_set_lbt_mode(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
+ bool mode)
+{
+ struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
+
+ ASSERT_RTNL();
+
+ if (!(local->hw.flags & IEEE802154_HW_LBT))
+ return -EOPNOTSUPP;
+
+ wpan_dev->lbt = mode;
+ return 0;
+}
+
+const struct cfg802154_ops mac802154_config_ops = {
+ .add_virtual_intf_deprecated = ieee802154_add_iface_deprecated,
+ .del_virtual_intf_deprecated = ieee802154_del_iface_deprecated,
+ .add_virtual_intf = ieee802154_add_iface,
+ .del_virtual_intf = ieee802154_del_iface,
+ .set_channel = ieee802154_set_channel,
+ .set_pan_id = ieee802154_set_pan_id,
+ .set_short_addr = ieee802154_set_short_addr,
+ .set_backoff_exponent = ieee802154_set_backoff_exponent,
+ .set_max_csma_backoffs = ieee802154_set_max_csma_backoffs,
+ .set_max_frame_retries = ieee802154_set_max_frame_retries,
+ .set_lbt_mode = ieee802154_set_lbt_mode,
+};
diff --git a/net/mac802154/cfg.h b/net/mac802154/cfg.h
new file mode 100644
index 0000000..e2718f9
--- /dev/null
+++ b/net/mac802154/cfg.h
@@ -0,0 +1,9 @@
+/* mac802154 configuration hooks for cfg802154
+ */
+
+#ifndef __CFG_H
+#define __CFG_H
+
+extern const struct cfg802154_ops mac802154_config_ops;
+
+#endif /* __CFG_H */
diff --git a/net/mac802154/driver-ops.h b/net/mac802154/driver-ops.h
new file mode 100644
index 0000000..f21e864
--- /dev/null
+++ b/net/mac802154/driver-ops.h
@@ -0,0 +1,222 @@
+#ifndef __MAC802154_DRVIER_OPS
+#define __MAC802154_DRIVER_OPS
+
+#include <linux/types.h>
+#include <linux/rtnetlink.h>
+
+#include <net/mac802154.h>
+
+#include "ieee802154_i.h"
+
+static inline int
+drv_xmit_async(struct ieee802154_local *local, struct sk_buff *skb)
+{
+ return local->ops->xmit_async(&local->hw, skb);
+}
+
+static inline int
+drv_xmit_sync(struct ieee802154_local *local, struct sk_buff *skb)
+{
+ /* don't allow other operations while sync xmit */
+ ASSERT_RTNL();
+
+ might_sleep();
+
+ return local->ops->xmit_sync(&local->hw, skb);
+}
+
+static inline int drv_start(struct ieee802154_local *local)
+{
+ might_sleep();
+
+ local->started = true;
+ smp_mb();
+
+ return local->ops->start(&local->hw);
+}
+
+static inline void drv_stop(struct ieee802154_local *local)
+{
+ might_sleep();
+
+ local->ops->stop(&local->hw);
+
+ /* sync away all work on the tasklet before clearing started */
+ tasklet_disable(&local->tasklet);
+ tasklet_enable(&local->tasklet);
+
+ barrier();
+
+ local->started = false;
+}
+
+static inline int
+drv_set_channel(struct ieee802154_local *local, u8 page, u8 channel)
+{
+ might_sleep();
+
+ return local->ops->set_channel(&local->hw, page, channel);
+}
+
+static inline int drv_set_tx_power(struct ieee802154_local *local, s8 dbm)
+{
+ might_sleep();
+
+ if (!local->ops->set_txpower) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ return local->ops->set_txpower(&local->hw, dbm);
+}
+
+static inline int drv_set_cca_mode(struct ieee802154_local *local, u8 cca_mode)
+{
+ might_sleep();
+
+ if (!local->ops->set_cca_mode) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ return local->ops->set_cca_mode(&local->hw, cca_mode);
+}
+
+static inline int drv_set_lbt_mode(struct ieee802154_local *local, bool mode)
+{
+ might_sleep();
+
+ if (!local->ops->set_lbt) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ return local->ops->set_lbt(&local->hw, mode);
+}
+
+static inline int
+drv_set_cca_ed_level(struct ieee802154_local *local, s32 ed_level)
+{
+ might_sleep();
+
+ if (!local->ops->set_cca_ed_level) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ return local->ops->set_cca_ed_level(&local->hw, ed_level);
+}
+
+static inline int drv_set_pan_id(struct ieee802154_local *local, __le16 pan_id)
+{
+ struct ieee802154_hw_addr_filt filt;
+
+ might_sleep();
+
+ if (!local->ops->set_hw_addr_filt) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ filt.pan_id = pan_id;
+
+ return local->ops->set_hw_addr_filt(&local->hw, &filt,
+ IEEE802154_AFILT_PANID_CHANGED);
+}
+
+static inline int
+drv_set_extended_addr(struct ieee802154_local *local, __le64 extended_addr)
+{
+ struct ieee802154_hw_addr_filt filt;
+
+ might_sleep();
+
+ if (!local->ops->set_hw_addr_filt) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ filt.ieee_addr = extended_addr;
+
+ return local->ops->set_hw_addr_filt(&local->hw, &filt,
+ IEEE802154_AFILT_IEEEADDR_CHANGED);
+}
+
+static inline int
+drv_set_short_addr(struct ieee802154_local *local, __le16 short_addr)
+{
+ struct ieee802154_hw_addr_filt filt;
+
+ might_sleep();
+
+ if (!local->ops->set_hw_addr_filt) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ filt.short_addr = short_addr;
+
+ return local->ops->set_hw_addr_filt(&local->hw, &filt,
+ IEEE802154_AFILT_SADDR_CHANGED);
+}
+
+static inline int
+drv_set_pan_coord(struct ieee802154_local *local, bool is_coord)
+{
+ struct ieee802154_hw_addr_filt filt;
+
+ might_sleep();
+
+ if (!local->ops->set_hw_addr_filt) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ filt.pan_coord = is_coord;
+
+ return local->ops->set_hw_addr_filt(&local->hw, &filt,
+ IEEE802154_AFILT_PANC_CHANGED);
+}
+
+static inline int
+drv_set_csma_params(struct ieee802154_local *local, u8 min_be, u8 max_be,
+ u8 max_csma_backoffs)
+{
+ might_sleep();
+
+ if (!local->ops->set_csma_params) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ return local->ops->set_csma_params(&local->hw, min_be, max_be,
+ max_csma_backoffs);
+}
+
+static inline int
+drv_set_max_frame_retries(struct ieee802154_local *local, s8 max_frame_retries)
+{
+ might_sleep();
+
+ if (!local->ops->set_frame_retries) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ return local->ops->set_frame_retries(&local->hw, max_frame_retries);
+}
+
+static inline int
+drv_set_promiscuous_mode(struct ieee802154_local *local, bool on)
+{
+ might_sleep();
+
+ if (!local->ops->set_promiscuous_mode) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ return local->ops->set_promiscuous_mode(&local->hw, on);
+}
+
+#endif /* __MAC802154_DRVIER_OPS */
diff --git a/net/mac802154/ieee802154_dev.c b/net/mac802154/ieee802154_dev.c
deleted file mode 100644
index b36b2b9..0000000
--- a/net/mac802154/ieee802154_dev.c
+++ /dev/null
@@ -1,415 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Siemens AG
- *
- * Written by:
- * Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
- *
- * Based on the code from 'linux-zigbee.sourceforge.net' project.
- *
- * 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.
- *
- * 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 <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/netdevice.h>
-
-#include <net/netlink.h>
-#include <linux/nl802154.h>
-#include <net/mac802154.h>
-#include <net/ieee802154_netdev.h>
-#include <net/route.h>
-#include <net/wpan-phy.h>
-
-#include "mac802154.h"
-
-int mac802154_slave_open(struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
- struct mac802154_sub_if_data *subif;
- struct mac802154_priv *ipriv = priv->hw;
- int res = 0;
-
- ASSERT_RTNL();
-
- if (priv->type == IEEE802154_DEV_WPAN) {
- mutex_lock(&priv->hw->slaves_mtx);
- list_for_each_entry(subif, &priv->hw->slaves, list) {
- if (subif != priv && subif->type == priv->type &&
- subif->running) {
- mutex_unlock(&priv->hw->slaves_mtx);
- return -EBUSY;
- }
- }
- mutex_unlock(&priv->hw->slaves_mtx);
- }
-
- mutex_lock(&priv->hw->slaves_mtx);
- priv->running = true;
- mutex_unlock(&priv->hw->slaves_mtx);
-
- if (ipriv->open_count++ == 0) {
- res = ipriv->ops->start(&ipriv->hw);
- WARN_ON(res);
- if (res)
- goto err;
- }
-
- if (ipriv->ops->ieee_addr) {
- __le64 addr = ieee802154_devaddr_from_raw(dev->dev_addr);
-
- res = ipriv->ops->ieee_addr(&ipriv->hw, addr);
- WARN_ON(res);
- if (res)
- goto err;
- mac802154_dev_set_ieee_addr(dev);
- }
-
- netif_start_queue(dev);
- return 0;
-err:
- priv->hw->open_count--;
-
- return res;
-}
-
-int mac802154_slave_close(struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
- struct mac802154_priv *ipriv = priv->hw;
-
- ASSERT_RTNL();
-
- netif_stop_queue(dev);
-
- mutex_lock(&priv->hw->slaves_mtx);
- priv->running = false;
- mutex_unlock(&priv->hw->slaves_mtx);
-
- if (!--ipriv->open_count)
- ipriv->ops->stop(&ipriv->hw);
-
- return 0;
-}
-
-static int
-mac802154_netdev_register(struct wpan_phy *phy, struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv;
- struct mac802154_priv *ipriv;
- int err;
-
- ipriv = wpan_phy_priv(phy);
-
- priv = netdev_priv(dev);
- priv->dev = dev;
- priv->hw = ipriv;
-
- dev->needed_headroom = ipriv->hw.extra_tx_headroom;
-
- SET_NETDEV_DEV(dev, &ipriv->phy->dev);
-
- mutex_lock(&ipriv->slaves_mtx);
- if (!ipriv->running) {
- mutex_unlock(&ipriv->slaves_mtx);
- return -ENODEV;
- }
- mutex_unlock(&ipriv->slaves_mtx);
-
- err = register_netdev(dev);
- if (err < 0)
- return err;
-
- rtnl_lock();
- mutex_lock(&ipriv->slaves_mtx);
- list_add_tail_rcu(&priv->list, &ipriv->slaves);
- mutex_unlock(&ipriv->slaves_mtx);
- rtnl_unlock();
-
- return 0;
-}
-
-static void
-mac802154_del_iface(struct wpan_phy *phy, struct net_device *dev)
-{
- struct mac802154_sub_if_data *sdata;
-
- ASSERT_RTNL();
-
- sdata = netdev_priv(dev);
-
- BUG_ON(sdata->hw->phy != phy);
-
- mutex_lock(&sdata->hw->slaves_mtx);
- list_del_rcu(&sdata->list);
- mutex_unlock(&sdata->hw->slaves_mtx);
-
- synchronize_rcu();
- unregister_netdevice(sdata->dev);
-}
-
-static struct net_device *
-mac802154_add_iface(struct wpan_phy *phy, const char *name, int type)
-{
- struct net_device *dev;
- int err = -ENOMEM;
-
- switch (type) {
- case IEEE802154_DEV_MONITOR:
- dev = alloc_netdev(sizeof(struct mac802154_sub_if_data),
- name, NET_NAME_UNKNOWN,
- mac802154_monitor_setup);
- break;
- case IEEE802154_DEV_WPAN:
- dev = alloc_netdev(sizeof(struct mac802154_sub_if_data),
- name, NET_NAME_UNKNOWN,
- mac802154_wpan_setup);
- break;
- default:
- dev = NULL;
- err = -EINVAL;
- break;
- }
- if (!dev)
- goto err;
-
- err = mac802154_netdev_register(phy, dev);
- if (err)
- goto err_free;
-
- dev_hold(dev); /* we return an incremented device refcount */
- return dev;
-
-err_free:
- free_netdev(dev);
-err:
- return ERR_PTR(err);
-}
-
-static int mac802154_set_txpower(struct wpan_phy *phy, int db)
-{
- struct mac802154_priv *priv = wpan_phy_priv(phy);
-
- return priv->ops->set_txpower(&priv->hw, db);
-}
-
-static int mac802154_set_lbt(struct wpan_phy *phy, bool on)
-{
- struct mac802154_priv *priv = wpan_phy_priv(phy);
-
- return priv->ops->set_lbt(&priv->hw, on);
-}
-
-static int mac802154_set_cca_mode(struct wpan_phy *phy, u8 mode)
-{
- struct mac802154_priv *priv = wpan_phy_priv(phy);
-
- return priv->ops->set_cca_mode(&priv->hw, mode);
-}
-
-static int mac802154_set_cca_ed_level(struct wpan_phy *phy, s32 level)
-{
- struct mac802154_priv *priv = wpan_phy_priv(phy);
-
- return priv->ops->set_cca_ed_level(&priv->hw, level);
-}
-
-static int mac802154_set_csma_params(struct wpan_phy *phy, u8 min_be,
- u8 max_be, u8 retries)
-{
- struct mac802154_priv *priv = wpan_phy_priv(phy);
-
- return priv->ops->set_csma_params(&priv->hw, min_be, max_be, retries);
-}
-
-static int mac802154_set_frame_retries(struct wpan_phy *phy, s8 retries)
-{
- struct mac802154_priv *priv = wpan_phy_priv(phy);
-
- return priv->ops->set_frame_retries(&priv->hw, retries);
-}
-
-struct ieee802154_dev *
-ieee802154_alloc_device(size_t priv_data_len, struct ieee802154_ops *ops)
-{
- struct wpan_phy *phy;
- struct mac802154_priv *priv;
- size_t priv_size;
-
- if (!ops || !ops->xmit || !ops->ed || !ops->start ||
- !ops->stop || !ops->set_channel) {
- pr_err("undefined IEEE802.15.4 device operations\n");
- return NULL;
- }
-
- /* Ensure 32-byte alignment of our private data and hw private data.
- * We use the wpan_phy priv data for both our mac802154_priv and for
- * the driver's private data
- *
- * in memory it'll be like this:
- *
- * +-----------------------+
- * | struct wpan_phy |
- * +-----------------------+
- * | struct mac802154_priv |
- * +-----------------------+
- * | driver's private data |
- * +-----------------------+
- *
- * Due to ieee802154 layer isn't aware of driver and MAC structures,
- * so lets allign them here.
- */
-
- priv_size = ALIGN(sizeof(*priv), NETDEV_ALIGN) + priv_data_len;
-
- phy = wpan_phy_alloc(priv_size);
- if (!phy) {
- pr_err("failure to allocate master IEEE802.15.4 device\n");
- return NULL;
- }
-
- priv = wpan_phy_priv(phy);
- priv->phy = phy;
- priv->hw.phy = priv->phy;
- priv->hw.priv = (char *)priv + ALIGN(sizeof(*priv), NETDEV_ALIGN);
- priv->ops = ops;
-
- INIT_LIST_HEAD(&priv->slaves);
- mutex_init(&priv->slaves_mtx);
-
- return &priv->hw;
-}
-EXPORT_SYMBOL(ieee802154_alloc_device);
-
-void ieee802154_free_device(struct ieee802154_dev *hw)
-{
- struct mac802154_priv *priv = mac802154_to_priv(hw);
-
- BUG_ON(!list_empty(&priv->slaves));
-
- mutex_destroy(&priv->slaves_mtx);
-
- wpan_phy_free(priv->phy);
-}
-EXPORT_SYMBOL(ieee802154_free_device);
-
-int ieee802154_register_device(struct ieee802154_dev *dev)
-{
- struct mac802154_priv *priv = mac802154_to_priv(dev);
- int rc = -ENOSYS;
-
- if (dev->flags & IEEE802154_HW_TXPOWER) {
- if (!priv->ops->set_txpower)
- goto out;
-
- priv->phy->set_txpower = mac802154_set_txpower;
- }
-
- if (dev->flags & IEEE802154_HW_LBT) {
- if (!priv->ops->set_lbt)
- goto out;
-
- priv->phy->set_lbt = mac802154_set_lbt;
- }
-
- if (dev->flags & IEEE802154_HW_CCA_MODE) {
- if (!priv->ops->set_cca_mode)
- goto out;
-
- priv->phy->set_cca_mode = mac802154_set_cca_mode;
- }
-
- if (dev->flags & IEEE802154_HW_CCA_ED_LEVEL) {
- if (!priv->ops->set_cca_ed_level)
- goto out;
-
- priv->phy->set_cca_ed_level = mac802154_set_cca_ed_level;
- }
-
- if (dev->flags & IEEE802154_HW_CSMA_PARAMS) {
- if (!priv->ops->set_csma_params)
- goto out;
-
- priv->phy->set_csma_params = mac802154_set_csma_params;
- }
-
- if (dev->flags & IEEE802154_HW_FRAME_RETRIES) {
- if (!priv->ops->set_frame_retries)
- goto out;
-
- priv->phy->set_frame_retries = mac802154_set_frame_retries;
- }
-
- priv->dev_workqueue =
- create_singlethread_workqueue(wpan_phy_name(priv->phy));
- if (!priv->dev_workqueue) {
- rc = -ENOMEM;
- goto out;
- }
-
- wpan_phy_set_dev(priv->phy, priv->hw.parent);
-
- priv->phy->add_iface = mac802154_add_iface;
- priv->phy->del_iface = mac802154_del_iface;
-
- rc = wpan_phy_register(priv->phy);
- if (rc < 0)
- goto out_wq;
-
- rtnl_lock();
-
- mutex_lock(&priv->slaves_mtx);
- priv->running = MAC802154_DEVICE_RUN;
- mutex_unlock(&priv->slaves_mtx);
-
- rtnl_unlock();
-
- return 0;
-
-out_wq:
- destroy_workqueue(priv->dev_workqueue);
-out:
- return rc;
-}
-EXPORT_SYMBOL(ieee802154_register_device);
-
-void ieee802154_unregister_device(struct ieee802154_dev *dev)
-{
- struct mac802154_priv *priv = mac802154_to_priv(dev);
- struct mac802154_sub_if_data *sdata, *next;
-
- flush_workqueue(priv->dev_workqueue);
- destroy_workqueue(priv->dev_workqueue);
-
- rtnl_lock();
-
- mutex_lock(&priv->slaves_mtx);
- priv->running = MAC802154_DEVICE_STOPPED;
- mutex_unlock(&priv->slaves_mtx);
-
- list_for_each_entry_safe(sdata, next, &priv->slaves, list) {
- mutex_lock(&sdata->hw->slaves_mtx);
- list_del(&sdata->list);
- mutex_unlock(&sdata->hw->slaves_mtx);
-
- unregister_netdevice(sdata->dev);
- }
-
- rtnl_unlock();
-
- wpan_phy_unregister(priv->phy);
-}
-EXPORT_SYMBOL(ieee802154_unregister_device);
-
-MODULE_DESCRIPTION("IEEE 802.15.4 implementation");
-MODULE_LICENSE("GPL v2");
diff --git a/net/mac802154/mac802154.h b/net/mac802154/ieee802154_i.h
similarity index 63%
rename from net/mac802154/mac802154.h
rename to net/mac802154/ieee802154_i.h
index 762a6f8..bebd70f 100644
--- a/net/mac802154/mac802154.h
+++ b/net/mac802154/ieee802154_i.h
@@ -10,29 +10,28 @@
* 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.
- *
* Written by:
* Pavel Smolenskiy <pavel.smolenskiy@gmail.com>
* Maxim Gorbachyov <maxim.gorbachev@siemens.com>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
* Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
*/
-#ifndef MAC802154_H
-#define MAC802154_H
+#ifndef __IEEE802154_I_H
+#define __IEEE802154_I_H
#include <linux/mutex.h>
+#include <linux/hrtimer.h>
+#include <net/cfg802154.h>
#include <net/mac802154.h>
+#include <net/nl802154.h>
#include <net/ieee802154_netdev.h>
#include "llsec.h"
/* mac802154 device private data */
-struct mac802154_priv {
- struct ieee802154_dev hw;
- struct ieee802154_ops *ops;
+struct ieee802154_local {
+ struct ieee802154_hw hw;
+ const struct ieee802154_ops *ops;
/* ieee802154 phy */
struct wpan_phy *phy;
@@ -46,23 +45,29 @@
*
* So atomic readers can use any of this protection methods.
*/
- struct list_head slaves;
- struct mutex slaves_mtx;
+ struct list_head interfaces;
+ struct mutex iflist_mtx;
/* This one is used for scanning and other jobs not to be interfered
* with serial driver.
*/
- struct workqueue_struct *dev_workqueue;
+ struct workqueue_struct *workqueue;
- /* SoftMAC device is registered and running. One can add subinterfaces.
- * This flag should be modified under slaves_mtx and RTNL, so you can
- * read them using any of protection methods.
- */
- bool running;
+ struct hrtimer ifs_timer;
+
+ bool started;
+
+ struct tasklet_struct tasklet;
+ struct sk_buff_head skb_queue;
};
-#define MAC802154_DEVICE_STOPPED 0x00
-#define MAC802154_DEVICE_RUN 0x01
+enum {
+ IEEE802154_RX_MSG = 1,
+};
+
+enum ieee802154_sdata_state_bits {
+ SDATA_STATE_RUNNING,
+};
/* Slave interface definition.
*
@@ -70,72 +75,74 @@
* Each ieee802154 device/transceiver may have several slaves and able
* to be associated with several networks at the same time.
*/
-struct mac802154_sub_if_data {
+struct ieee802154_sub_if_data {
struct list_head list; /* the ieee802154_priv->slaves list */
- struct mac802154_priv *hw;
+ struct wpan_dev wpan_dev;
+
+ struct ieee802154_local *local;
struct net_device *dev;
- int type;
- bool running;
+ unsigned long state;
+ char name[IFNAMSIZ];
spinlock_t mib_lock;
- __le16 pan_id;
- __le16 short_addr;
- __le64 extended_addr;
-
- u8 chan;
- u8 page;
-
- struct ieee802154_mac_params mac_params;
-
- /* MAC BSN field */
- u8 bsn;
- /* MAC DSN field */
- u8 dsn;
-
/* protects sec from concurrent access by netlink. access by
* encrypt/decrypt/header_create safe without additional protection.
*/
struct mutex sec_mtx;
struct mac802154_llsec sec;
+ /* must be last, dynamically sized area in this! */
+ struct ieee802154_vif vif;
};
-#define mac802154_to_priv(_hw) container_of(_hw, struct mac802154_priv, hw)
-
#define MAC802154_CHAN_NONE 0xff /* No channel is assigned */
-extern struct ieee802154_reduced_mlme_ops mac802154_mlme_reduced;
+/* utility functions/constants */
+extern const void *const mac802154_wpan_phy_privid; /* for wpan_phy privid */
+
+static inline struct ieee802154_local *
+hw_to_local(struct ieee802154_hw *hw)
+{
+ return container_of(hw, struct ieee802154_local, hw);
+}
+
+static inline struct ieee802154_sub_if_data *
+IEEE802154_DEV_TO_SUB_IF(const struct net_device *dev)
+{
+ return netdev_priv(dev);
+}
+
+static inline struct ieee802154_sub_if_data *
+IEEE802154_WPAN_DEV_TO_SUB_IF(struct wpan_dev *wpan_dev)
+{
+ return container_of(wpan_dev, struct ieee802154_sub_if_data, wpan_dev);
+}
+
+static inline bool
+ieee802154_sdata_running(struct ieee802154_sub_if_data *sdata)
+{
+ return test_bit(SDATA_STATE_RUNNING, &sdata->state);
+}
+
extern struct ieee802154_mlme_ops mac802154_mlme_wpan;
-int mac802154_slave_open(struct net_device *dev);
-int mac802154_slave_close(struct net_device *dev);
-
-void mac802154_monitors_rx(struct mac802154_priv *priv, struct sk_buff *skb);
-void mac802154_monitor_setup(struct net_device *dev);
-
-void mac802154_wpans_rx(struct mac802154_priv *priv, struct sk_buff *skb);
-void mac802154_wpan_setup(struct net_device *dev);
-
-netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb,
- u8 page, u8 chan);
+netdev_tx_t
+ieee802154_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev);
+netdev_tx_t
+ieee802154_subif_start_xmit(struct sk_buff *skb, struct net_device *dev);
+enum hrtimer_restart ieee802154_xmit_ifs_timer(struct hrtimer *timer);
/* MIB callbacks */
void mac802154_dev_set_short_addr(struct net_device *dev, __le16 val);
__le16 mac802154_dev_get_short_addr(const struct net_device *dev);
-void mac802154_dev_set_ieee_addr(struct net_device *dev);
__le16 mac802154_dev_get_pan_id(const struct net_device *dev);
void mac802154_dev_set_pan_id(struct net_device *dev, __le16 val);
void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan);
u8 mac802154_dev_get_dsn(const struct net_device *dev);
-int mac802154_set_mac_params(struct net_device *dev,
- const struct ieee802154_mac_params *params);
-void mac802154_get_mac_params(struct net_device *dev,
- struct ieee802154_mac_params *params);
-
int mac802154_get_params(struct net_device *dev,
struct ieee802154_llsec_params *params);
int mac802154_set_params(struct net_device *dev,
@@ -169,4 +176,13 @@
struct ieee802154_llsec_table **t);
void mac802154_unlock_table(struct net_device *dev);
-#endif /* MAC802154_H */
+/* interface handling */
+int ieee802154_iface_init(void);
+void ieee802154_iface_exit(void);
+void ieee802154_if_remove(struct ieee802154_sub_if_data *sdata);
+struct net_device *
+ieee802154_if_add(struct ieee802154_local *local, const char *name,
+ enum nl802154_iftype type, __le64 extended_addr);
+void ieee802154_remove_interfaces(struct ieee802154_local *local);
+
+#endif /* __IEEE802154_I_H */
diff --git a/net/mac802154/iface.c b/net/mac802154/iface.c
new file mode 100644
index 0000000..9ae8930
--- /dev/null
+++ b/net/mac802154/iface.c
@@ -0,0 +1,586 @@
+/*
+ * Copyright 2007-2012 Siemens AG
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * Written by:
+ * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+ * Sergey Lapin <slapin@ossfans.org>
+ * Maxim Gorbachyov <maxim.gorbachev@siemens.com>
+ * Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
+ */
+
+#include <linux/netdevice.h>
+#include <linux/module.h>
+#include <linux/if_arp.h>
+#include <linux/ieee802154.h>
+
+#include <net/nl802154.h>
+#include <net/mac802154.h>
+#include <net/ieee802154_netdev.h>
+#include <net/cfg802154.h>
+
+#include "ieee802154_i.h"
+#include "driver-ops.h"
+
+static int mac802154_wpan_update_llsec(struct net_device *dev)
+{
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
+ struct wpan_dev *wpan_dev = &sdata->wpan_dev;
+ int rc = 0;
+
+ if (ops->llsec) {
+ struct ieee802154_llsec_params params;
+ int changed = 0;
+
+ params.pan_id = wpan_dev->pan_id;
+ changed |= IEEE802154_LLSEC_PARAM_PAN_ID;
+
+ params.hwaddr = wpan_dev->extended_addr;
+ changed |= IEEE802154_LLSEC_PARAM_HWADDR;
+
+ rc = ops->llsec->set_params(dev, ¶ms, changed);
+ }
+
+ return rc;
+}
+
+static int
+mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ struct wpan_dev *wpan_dev = &sdata->wpan_dev;
+ struct sockaddr_ieee802154 *sa =
+ (struct sockaddr_ieee802154 *)&ifr->ifr_addr;
+ int err = -ENOIOCTLCMD;
+
+ ASSERT_RTNL();
+
+ spin_lock_bh(&sdata->mib_lock);
+
+ switch (cmd) {
+ case SIOCGIFADDR:
+ {
+ u16 pan_id, short_addr;
+
+ pan_id = le16_to_cpu(wpan_dev->pan_id);
+ short_addr = le16_to_cpu(wpan_dev->short_addr);
+ if (pan_id == IEEE802154_PANID_BROADCAST ||
+ short_addr == IEEE802154_ADDR_BROADCAST) {
+ err = -EADDRNOTAVAIL;
+ break;
+ }
+
+ sa->family = AF_IEEE802154;
+ sa->addr.addr_type = IEEE802154_ADDR_SHORT;
+ sa->addr.pan_id = pan_id;
+ sa->addr.short_addr = short_addr;
+
+ err = 0;
+ break;
+ }
+ case SIOCSIFADDR:
+ if (netif_running(dev)) {
+ spin_unlock_bh(&sdata->mib_lock);
+ return -EBUSY;
+ }
+
+ dev_warn(&dev->dev,
+ "Using DEBUGing ioctl SIOCSIFADDR isn't recommended!\n");
+ if (sa->family != AF_IEEE802154 ||
+ sa->addr.addr_type != IEEE802154_ADDR_SHORT ||
+ sa->addr.pan_id == IEEE802154_PANID_BROADCAST ||
+ sa->addr.short_addr == IEEE802154_ADDR_BROADCAST ||
+ sa->addr.short_addr == IEEE802154_ADDR_UNDEF) {
+ err = -EINVAL;
+ break;
+ }
+
+ wpan_dev->pan_id = cpu_to_le16(sa->addr.pan_id);
+ wpan_dev->short_addr = cpu_to_le16(sa->addr.short_addr);
+
+ err = mac802154_wpan_update_llsec(dev);
+ break;
+ }
+
+ spin_unlock_bh(&sdata->mib_lock);
+ return err;
+}
+
+static int mac802154_wpan_mac_addr(struct net_device *dev, void *p)
+{
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ struct sockaddr *addr = p;
+ __le64 extended_addr;
+
+ if (netif_running(dev))
+ return -EBUSY;
+
+ ieee802154_be64_to_le64(&extended_addr, addr->sa_data);
+ if (!ieee802154_is_valid_extended_addr(extended_addr))
+ return -EINVAL;
+
+ memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+ sdata->wpan_dev.extended_addr = extended_addr;
+
+ return mac802154_wpan_update_llsec(dev);
+}
+
+static int mac802154_slave_open(struct net_device *dev)
+{
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ struct ieee802154_sub_if_data *subif;
+ struct ieee802154_local *local = sdata->local;
+ int res = 0;
+
+ ASSERT_RTNL();
+
+ if (sdata->vif.type == NL802154_IFTYPE_NODE) {
+ mutex_lock(&sdata->local->iflist_mtx);
+ list_for_each_entry(subif, &sdata->local->interfaces, list) {
+ if (subif != sdata &&
+ subif->vif.type == sdata->vif.type &&
+ ieee802154_sdata_running(subif)) {
+ mutex_unlock(&sdata->local->iflist_mtx);
+ return -EBUSY;
+ }
+ }
+ mutex_unlock(&sdata->local->iflist_mtx);
+ }
+
+ set_bit(SDATA_STATE_RUNNING, &sdata->state);
+
+ if (!local->open_count) {
+ res = drv_start(local);
+ WARN_ON(res);
+ if (res)
+ goto err;
+ }
+
+ local->open_count++;
+ netif_start_queue(dev);
+ return 0;
+err:
+ /* might already be clear but that doesn't matter */
+ clear_bit(SDATA_STATE_RUNNING, &sdata->state);
+
+ return res;
+}
+
+static int mac802154_wpan_open(struct net_device *dev)
+{
+ int rc;
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ struct ieee802154_local *local = sdata->local;
+ struct wpan_dev *wpan_dev = &sdata->wpan_dev;
+ struct wpan_phy *phy = sdata->local->phy;
+
+ rc = mac802154_slave_open(dev);
+ if (rc < 0)
+ return rc;
+
+ mutex_lock(&phy->pib_lock);
+
+ if (local->hw.flags & IEEE802154_HW_PROMISCUOUS) {
+ rc = drv_set_promiscuous_mode(local,
+ wpan_dev->promiscuous_mode);
+ if (rc < 0)
+ goto out;
+ }
+
+ if (local->hw.flags & IEEE802154_HW_AFILT) {
+ rc = drv_set_pan_id(local, wpan_dev->pan_id);
+ if (rc < 0)
+ goto out;
+
+ rc = drv_set_extended_addr(local, wpan_dev->extended_addr);
+ if (rc < 0)
+ goto out;
+
+ rc = drv_set_short_addr(local, wpan_dev->short_addr);
+ if (rc < 0)
+ goto out;
+ }
+
+ if (local->hw.flags & IEEE802154_HW_LBT) {
+ rc = drv_set_lbt_mode(local, wpan_dev->lbt);
+ if (rc < 0)
+ goto out;
+ }
+
+ if (local->hw.flags & IEEE802154_HW_CSMA_PARAMS) {
+ rc = drv_set_csma_params(local, wpan_dev->min_be,
+ wpan_dev->max_be,
+ wpan_dev->csma_retries);
+ if (rc < 0)
+ goto out;
+ }
+
+ if (local->hw.flags & IEEE802154_HW_FRAME_RETRIES) {
+ rc = drv_set_max_frame_retries(local, wpan_dev->frame_retries);
+ if (rc < 0)
+ goto out;
+ }
+
+ mutex_unlock(&phy->pib_lock);
+ return 0;
+
+out:
+ mutex_unlock(&phy->pib_lock);
+ return rc;
+}
+
+static int mac802154_slave_close(struct net_device *dev)
+{
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ struct ieee802154_local *local = sdata->local;
+
+ ASSERT_RTNL();
+
+ hrtimer_cancel(&local->ifs_timer);
+
+ netif_stop_queue(dev);
+ local->open_count--;
+
+ clear_bit(SDATA_STATE_RUNNING, &sdata->state);
+
+ if (!local->open_count)
+ drv_stop(local);
+
+ return 0;
+}
+
+static int mac802154_set_header_security(struct ieee802154_sub_if_data *sdata,
+ struct ieee802154_hdr *hdr,
+ const struct ieee802154_mac_cb *cb)
+{
+ struct ieee802154_llsec_params params;
+ u8 level;
+
+ mac802154_llsec_get_params(&sdata->sec, ¶ms);
+
+ if (!params.enabled && cb->secen_override && cb->secen)
+ return -EINVAL;
+ if (!params.enabled ||
+ (cb->secen_override && !cb->secen) ||
+ !params.out_level)
+ return 0;
+ if (cb->seclevel_override && !cb->seclevel)
+ return -EINVAL;
+
+ level = cb->seclevel_override ? cb->seclevel : params.out_level;
+
+ hdr->fc.security_enabled = 1;
+ hdr->sec.level = level;
+ hdr->sec.key_id_mode = params.out_key.mode;
+ if (params.out_key.mode == IEEE802154_SCF_KEY_SHORT_INDEX)
+ hdr->sec.short_src = params.out_key.short_source;
+ else if (params.out_key.mode == IEEE802154_SCF_KEY_HW_INDEX)
+ hdr->sec.extended_src = params.out_key.extended_source;
+ hdr->sec.key_id = params.out_key.id;
+
+ return 0;
+}
+
+static int mac802154_header_create(struct sk_buff *skb,
+ struct net_device *dev,
+ unsigned short type,
+ const void *daddr,
+ const void *saddr,
+ unsigned len)
+{
+ struct ieee802154_hdr hdr;
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ struct wpan_dev *wpan_dev = &sdata->wpan_dev;
+ struct ieee802154_mac_cb *cb = mac_cb(skb);
+ int hlen;
+
+ if (!daddr)
+ return -EINVAL;
+
+ memset(&hdr.fc, 0, sizeof(hdr.fc));
+ hdr.fc.type = cb->type;
+ hdr.fc.security_enabled = cb->secen;
+ hdr.fc.ack_request = cb->ackreq;
+ hdr.seq = ieee802154_mlme_ops(dev)->get_dsn(dev);
+
+ if (mac802154_set_header_security(sdata, &hdr, cb) < 0)
+ return -EINVAL;
+
+ if (!saddr) {
+ spin_lock_bh(&sdata->mib_lock);
+
+ if (wpan_dev->short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST) ||
+ wpan_dev->short_addr == cpu_to_le16(IEEE802154_ADDR_UNDEF) ||
+ wpan_dev->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST)) {
+ hdr.source.mode = IEEE802154_ADDR_LONG;
+ hdr.source.extended_addr = wpan_dev->extended_addr;
+ } else {
+ hdr.source.mode = IEEE802154_ADDR_SHORT;
+ hdr.source.short_addr = wpan_dev->short_addr;
+ }
+
+ hdr.source.pan_id = wpan_dev->pan_id;
+
+ spin_unlock_bh(&sdata->mib_lock);
+ } else {
+ hdr.source = *(const struct ieee802154_addr *)saddr;
+ }
+
+ hdr.dest = *(const struct ieee802154_addr *)daddr;
+
+ hlen = ieee802154_hdr_push(skb, &hdr);
+ if (hlen < 0)
+ return -EINVAL;
+
+ skb_reset_mac_header(skb);
+ skb->mac_len = hlen;
+
+ if (len > ieee802154_max_payload(&hdr))
+ return -EMSGSIZE;
+
+ return hlen;
+}
+
+static int
+mac802154_header_parse(const struct sk_buff *skb, unsigned char *haddr)
+{
+ struct ieee802154_hdr hdr;
+ struct ieee802154_addr *addr = (struct ieee802154_addr *)haddr;
+
+ if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) {
+ pr_debug("malformed packet\n");
+ return 0;
+ }
+
+ *addr = hdr.source;
+ return sizeof(*addr);
+}
+
+static struct header_ops mac802154_header_ops = {
+ .create = mac802154_header_create,
+ .parse = mac802154_header_parse,
+};
+
+static const struct net_device_ops mac802154_wpan_ops = {
+ .ndo_open = mac802154_wpan_open,
+ .ndo_stop = mac802154_slave_close,
+ .ndo_start_xmit = ieee802154_subif_start_xmit,
+ .ndo_do_ioctl = mac802154_wpan_ioctl,
+ .ndo_set_mac_address = mac802154_wpan_mac_addr,
+};
+
+static const struct net_device_ops mac802154_monitor_ops = {
+ .ndo_open = mac802154_wpan_open,
+ .ndo_stop = mac802154_slave_close,
+ .ndo_start_xmit = ieee802154_monitor_start_xmit,
+};
+
+static void mac802154_wpan_free(struct net_device *dev)
+{
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+
+ mac802154_llsec_destroy(&sdata->sec);
+
+ free_netdev(dev);
+}
+
+static void ieee802154_if_setup(struct net_device *dev)
+{
+ dev->addr_len = IEEE802154_EXTENDED_ADDR_LEN;
+ memset(dev->broadcast, 0xff, IEEE802154_EXTENDED_ADDR_LEN);
+
+ dev->hard_header_len = MAC802154_FRAME_HARD_HEADER_LEN;
+ dev->needed_tailroom = 2 + 16; /* FCS + MIC */
+ dev->mtu = IEEE802154_MTU;
+ dev->tx_queue_len = 300;
+ dev->flags = IFF_NOARP | IFF_BROADCAST;
+}
+
+static int
+ieee802154_setup_sdata(struct ieee802154_sub_if_data *sdata,
+ enum nl802154_iftype type)
+{
+ struct wpan_dev *wpan_dev = &sdata->wpan_dev;
+
+ /* set some type-dependent values */
+ sdata->vif.type = type;
+ sdata->wpan_dev.iftype = type;
+
+ get_random_bytes(&wpan_dev->bsn, 1);
+ get_random_bytes(&wpan_dev->dsn, 1);
+
+ /* defaults per 802.15.4-2011 */
+ wpan_dev->min_be = 3;
+ wpan_dev->max_be = 5;
+ wpan_dev->csma_retries = 4;
+ /* for compatibility, actual default is 3 */
+ wpan_dev->frame_retries = -1;
+
+ wpan_dev->pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST);
+ wpan_dev->short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
+
+ switch (type) {
+ case NL802154_IFTYPE_NODE:
+ ieee802154_be64_to_le64(&wpan_dev->extended_addr,
+ sdata->dev->dev_addr);
+
+ sdata->dev->header_ops = &mac802154_header_ops;
+ sdata->dev->destructor = mac802154_wpan_free;
+ sdata->dev->netdev_ops = &mac802154_wpan_ops;
+ sdata->dev->ml_priv = &mac802154_mlme_wpan;
+ wpan_dev->promiscuous_mode = false;
+
+ spin_lock_init(&sdata->mib_lock);
+ mutex_init(&sdata->sec_mtx);
+
+ mac802154_llsec_init(&sdata->sec);
+ break;
+ case NL802154_IFTYPE_MONITOR:
+ sdata->dev->destructor = free_netdev;
+ sdata->dev->netdev_ops = &mac802154_monitor_ops;
+ wpan_dev->promiscuous_mode = true;
+ break;
+ default:
+ BUG();
+ }
+
+ return 0;
+}
+
+struct net_device *
+ieee802154_if_add(struct ieee802154_local *local, const char *name,
+ enum nl802154_iftype type, __le64 extended_addr)
+{
+ struct net_device *ndev = NULL;
+ struct ieee802154_sub_if_data *sdata = NULL;
+ int ret = -ENOMEM;
+
+ ASSERT_RTNL();
+
+ ndev = alloc_netdev(sizeof(*sdata) + local->hw.vif_data_size, name,
+ NET_NAME_UNKNOWN, ieee802154_if_setup);
+ if (!ndev)
+ return ERR_PTR(-ENOMEM);
+
+ ndev->needed_headroom = local->hw.extra_tx_headroom;
+
+ ret = dev_alloc_name(ndev, ndev->name);
+ if (ret < 0)
+ goto err;
+
+ ieee802154_le64_to_be64(ndev->perm_addr,
+ &local->hw.phy->perm_extended_addr);
+ switch (type) {
+ case NL802154_IFTYPE_NODE:
+ ndev->type = ARPHRD_IEEE802154;
+ if (ieee802154_is_valid_extended_addr(extended_addr))
+ ieee802154_le64_to_be64(ndev->dev_addr, &extended_addr);
+ else
+ memcpy(ndev->dev_addr, ndev->perm_addr,
+ IEEE802154_EXTENDED_ADDR_LEN);
+ break;
+ case NL802154_IFTYPE_MONITOR:
+ ndev->type = ARPHRD_IEEE802154_MONITOR;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* TODO check this */
+ SET_NETDEV_DEV(ndev, &local->phy->dev);
+ sdata = netdev_priv(ndev);
+ ndev->ieee802154_ptr = &sdata->wpan_dev;
+ memcpy(sdata->name, ndev->name, IFNAMSIZ);
+ sdata->dev = ndev;
+ sdata->wpan_dev.wpan_phy = local->hw.phy;
+ sdata->local = local;
+
+ /* setup type-dependent data */
+ ret = ieee802154_setup_sdata(sdata, type);
+ if (ret)
+ goto err;
+
+ ret = register_netdevice(ndev);
+ if (ret < 0)
+ goto err;
+
+ mutex_lock(&local->iflist_mtx);
+ list_add_tail_rcu(&sdata->list, &local->interfaces);
+ mutex_unlock(&local->iflist_mtx);
+
+ return ndev;
+
+err:
+ free_netdev(ndev);
+ return ERR_PTR(ret);
+}
+
+void ieee802154_if_remove(struct ieee802154_sub_if_data *sdata)
+{
+ ASSERT_RTNL();
+
+ mutex_lock(&sdata->local->iflist_mtx);
+ list_del_rcu(&sdata->list);
+ mutex_unlock(&sdata->local->iflist_mtx);
+
+ synchronize_rcu();
+ unregister_netdevice(sdata->dev);
+}
+
+void ieee802154_remove_interfaces(struct ieee802154_local *local)
+{
+ struct ieee802154_sub_if_data *sdata, *tmp;
+
+ mutex_lock(&local->iflist_mtx);
+ list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) {
+ list_del(&sdata->list);
+
+ unregister_netdevice(sdata->dev);
+ }
+ mutex_unlock(&local->iflist_mtx);
+}
+
+static int netdev_notify(struct notifier_block *nb,
+ unsigned long state, void *ptr)
+{
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+ struct ieee802154_sub_if_data *sdata;
+
+ if (state != NETDEV_CHANGENAME)
+ return NOTIFY_DONE;
+
+ if (!dev->ieee802154_ptr || !dev->ieee802154_ptr->wpan_phy)
+ return NOTIFY_DONE;
+
+ if (dev->ieee802154_ptr->wpan_phy->privid != mac802154_wpan_phy_privid)
+ return NOTIFY_DONE;
+
+ sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ memcpy(sdata->name, dev->name, IFNAMSIZ);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block mac802154_netdev_notifier = {
+ .notifier_call = netdev_notify,
+};
+
+int ieee802154_iface_init(void)
+{
+ return register_netdevice_notifier(&mac802154_netdev_notifier);
+}
+
+void ieee802154_iface_exit(void)
+{
+ unregister_netdevice_notifier(&mac802154_netdev_notifier);
+}
diff --git a/net/mac802154/llsec.c b/net/mac802154/llsec.c
index 4570581..dcf7395 100644
--- a/net/mac802154/llsec.c
+++ b/net/mac802154/llsec.c
@@ -17,10 +17,10 @@
#include <linux/err.h>
#include <linux/bug.h>
#include <linux/completion.h>
-#include <net/ieee802154.h>
+#include <linux/ieee802154.h>
#include <crypto/algapi.h>
-#include "mac802154.h"
+#include "ieee802154_i.h"
#include "llsec.h"
static void llsec_key_put(struct mac802154_llsec_key *key);
@@ -75,8 +75,6 @@
}
}
-
-
int mac802154_llsec_get_params(struct mac802154_llsec *sec,
struct ieee802154_llsec_params *params)
{
@@ -117,8 +115,6 @@
return 0;
}
-
-
static struct mac802154_llsec_key*
llsec_key_alloc(const struct ieee802154_llsec_key *template)
{
@@ -294,8 +290,6 @@
return -ENOENT;
}
-
-
static bool llsec_dev_use_shortaddr(__le16 short_addr)
{
return short_addr != cpu_to_le16(IEEE802154_ADDR_UNDEF) &&
@@ -304,12 +298,12 @@
static u32 llsec_dev_hash_short(__le16 short_addr, __le16 pan_id)
{
- return ((__force u16) short_addr) << 16 | (__force u16) pan_id;
+ return ((__force u16)short_addr) << 16 | (__force u16)pan_id;
}
static u64 llsec_dev_hash_long(__le64 hwaddr)
{
- return (__force u64) hwaddr;
+ return (__force u64)hwaddr;
}
static struct mac802154_llsec_device*
@@ -411,8 +405,6 @@
return 0;
}
-
-
static struct mac802154_llsec_device_key*
llsec_devkey_find(struct mac802154_llsec_device *dev,
const struct ieee802154_llsec_key_id *key)
@@ -475,8 +467,6 @@
return 0;
}
-
-
static struct mac802154_llsec_seclevel*
llsec_find_seclevel(const struct mac802154_llsec *sec,
const struct ieee802154_llsec_seclevel *sl)
@@ -532,8 +522,6 @@
return 0;
}
-
-
static int llsec_recover_addr(struct mac802154_llsec *sec,
struct ieee802154_addr *addr)
{
@@ -609,7 +597,6 @@
return llsec_key_get(key);
}
-
static void llsec_geniv(u8 iv[16], __le64 addr,
const struct ieee802154_sechdr *sec)
{
@@ -786,8 +773,6 @@
return rc;
}
-
-
static struct mac802154_llsec_device*
llsec_lookup_dev(struct mac802154_llsec *sec,
const struct ieee802154_addr *addr)
diff --git a/net/mac802154/mac_cmd.c b/net/mac802154/mac_cmd.c
index bf80913..6aacb18 100644
--- a/net/mac802154/mac_cmd.c
+++ b/net/mac802154/mac_cmd.c
@@ -12,10 +12,6 @@
* 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.
- *
* Written by:
* Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
@@ -24,14 +20,14 @@
#include <linux/skbuff.h>
#include <linux/if_arp.h>
+#include <linux/ieee802154.h>
-#include <net/ieee802154.h>
#include <net/ieee802154_netdev.h>
-#include <net/wpan-phy.h>
+#include <net/cfg802154.h>
#include <net/mac802154.h>
-#include <net/nl802154.h>
-#include "mac802154.h"
+#include "ieee802154_i.h"
+#include "driver-ops.h"
static int mac802154_mlme_start_req(struct net_device *dev,
struct ieee802154_addr *addr,
@@ -43,11 +39,12 @@
struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
int rc = 0;
+ ASSERT_RTNL();
+
BUG_ON(addr->mode != IEEE802154_ADDR_SHORT);
mac802154_dev_set_pan_id(dev, addr->pan_id);
mac802154_dev_set_short_addr(dev, addr->short_addr);
- mac802154_dev_set_ieee_addr(dev);
mac802154_dev_set_page_channel(dev, page, channel);
if (ops->llsec) {
@@ -69,21 +66,71 @@
rc = ops->llsec->set_params(dev, ¶ms, changed);
}
- /* FIXME: add validation for unused parameters to be sane
- * for SoftMAC
- */
- ieee802154_nl_start_confirm(dev, IEEE802154_SUCCESS);
-
return rc;
}
-static struct wpan_phy *mac802154_get_phy(const struct net_device *dev)
+static int mac802154_set_mac_params(struct net_device *dev,
+ const struct ieee802154_mac_params *params)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ struct ieee802154_local *local = sdata->local;
+ struct wpan_dev *wpan_dev = &sdata->wpan_dev;
+ int ret;
- BUG_ON(dev->type != ARPHRD_IEEE802154);
+ ASSERT_RTNL();
- return to_phy(get_device(&priv->hw->phy->dev));
+ /* PHY */
+ wpan_dev->wpan_phy->transmit_power = params->transmit_power;
+ wpan_dev->wpan_phy->cca_mode = params->cca_mode;
+ wpan_dev->wpan_phy->cca_ed_level = params->cca_ed_level;
+
+ /* MAC */
+ wpan_dev->min_be = params->min_be;
+ wpan_dev->max_be = params->max_be;
+ wpan_dev->csma_retries = params->csma_retries;
+ wpan_dev->frame_retries = params->frame_retries;
+ wpan_dev->lbt = params->lbt;
+
+ if (local->hw.flags & IEEE802154_HW_TXPOWER) {
+ ret = drv_set_tx_power(local, params->transmit_power);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (local->hw.flags & IEEE802154_HW_CCA_MODE) {
+ ret = drv_set_cca_mode(local, params->cca_mode);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (local->hw.flags & IEEE802154_HW_CCA_ED_LEVEL) {
+ ret = drv_set_cca_ed_level(local, params->cca_ed_level);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void mac802154_get_mac_params(struct net_device *dev,
+ struct ieee802154_mac_params *params)
+{
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ struct wpan_dev *wpan_dev = &sdata->wpan_dev;
+
+ ASSERT_RTNL();
+
+ /* PHY */
+ params->transmit_power = wpan_dev->wpan_phy->transmit_power;
+ params->cca_mode = wpan_dev->wpan_phy->cca_mode;
+ params->cca_ed_level = wpan_dev->wpan_phy->cca_ed_level;
+
+ /* MAC */
+ params->min_be = wpan_dev->min_be;
+ params->max_be = wpan_dev->max_be;
+ params->csma_retries = wpan_dev->csma_retries;
+ params->frame_retries = wpan_dev->frame_retries;
+ params->lbt = wpan_dev->lbt;
}
static struct ieee802154_llsec_ops mac802154_llsec_ops = {
@@ -102,12 +149,7 @@
.unlock_table = mac802154_unlock_table,
};
-struct ieee802154_reduced_mlme_ops mac802154_mlme_reduced = {
- .get_phy = mac802154_get_phy,
-};
-
struct ieee802154_mlme_ops mac802154_mlme_wpan = {
- .get_phy = mac802154_get_phy,
.start_req = mac802154_mlme_start_req,
.get_pan_id = mac802154_dev_get_pan_id,
.get_short_addr = mac802154_dev_get_short_addr,
diff --git a/net/mac802154/main.c b/net/mac802154/main.c
new file mode 100644
index 0000000..8500378
--- /dev/null
+++ b/net/mac802154/main.c
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2007-2012 Siemens AG
+ *
+ * Written by:
+ * Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+
+#include <net/netlink.h>
+#include <net/nl802154.h>
+#include <net/mac802154.h>
+#include <net/ieee802154_netdev.h>
+#include <net/route.h>
+#include <net/cfg802154.h>
+
+#include "ieee802154_i.h"
+#include "cfg.h"
+
+static void ieee802154_tasklet_handler(unsigned long data)
+{
+ struct ieee802154_local *local = (struct ieee802154_local *)data;
+ struct sk_buff *skb;
+
+ while ((skb = skb_dequeue(&local->skb_queue))) {
+ switch (skb->pkt_type) {
+ case IEEE802154_RX_MSG:
+ /* Clear skb->pkt_type in order to not confuse kernel
+ * netstack.
+ */
+ skb->pkt_type = 0;
+ ieee802154_rx(&local->hw, skb);
+ break;
+ default:
+ WARN(1, "mac802154: Packet is of unknown type %d\n",
+ skb->pkt_type);
+ kfree_skb(skb);
+ break;
+ }
+ }
+}
+
+struct ieee802154_hw *
+ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops)
+{
+ struct wpan_phy *phy;
+ struct ieee802154_local *local;
+ size_t priv_size;
+
+ if (!ops || !(ops->xmit_async || ops->xmit_sync) || !ops->ed ||
+ !ops->start || !ops->stop || !ops->set_channel) {
+ pr_err("undefined IEEE802.15.4 device operations\n");
+ return NULL;
+ }
+
+ /* Ensure 32-byte alignment of our private data and hw private data.
+ * We use the wpan_phy priv data for both our ieee802154_local and for
+ * the driver's private data
+ *
+ * in memory it'll be like this:
+ *
+ * +-------------------------+
+ * | struct wpan_phy |
+ * +-------------------------+
+ * | struct ieee802154_local |
+ * +-------------------------+
+ * | driver's private data |
+ * +-------------------------+
+ *
+ * Due to ieee802154 layer isn't aware of driver and MAC structures,
+ * so lets align them here.
+ */
+
+ priv_size = ALIGN(sizeof(*local), NETDEV_ALIGN) + priv_data_len;
+
+ phy = wpan_phy_new(&mac802154_config_ops, priv_size);
+ if (!phy) {
+ pr_err("failure to allocate master IEEE802.15.4 device\n");
+ return NULL;
+ }
+
+ phy->privid = mac802154_wpan_phy_privid;
+
+ local = wpan_phy_priv(phy);
+ local->phy = phy;
+ local->hw.phy = local->phy;
+ local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN);
+ local->ops = ops;
+
+ INIT_LIST_HEAD(&local->interfaces);
+ mutex_init(&local->iflist_mtx);
+
+ tasklet_init(&local->tasklet,
+ ieee802154_tasklet_handler,
+ (unsigned long)local);
+
+ skb_queue_head_init(&local->skb_queue);
+
+ return &local->hw;
+}
+EXPORT_SYMBOL(ieee802154_alloc_hw);
+
+void ieee802154_free_hw(struct ieee802154_hw *hw)
+{
+ struct ieee802154_local *local = hw_to_local(hw);
+
+ BUG_ON(!list_empty(&local->interfaces));
+
+ mutex_destroy(&local->iflist_mtx);
+
+ wpan_phy_free(local->phy);
+}
+EXPORT_SYMBOL(ieee802154_free_hw);
+
+static void ieee802154_setup_wpan_phy_pib(struct wpan_phy *wpan_phy)
+{
+ /* TODO warn on empty symbol_duration
+ * Should be done when all drivers sets this value.
+ */
+
+ wpan_phy->lifs_period = IEEE802154_LIFS_PERIOD *
+ wpan_phy->symbol_duration;
+ wpan_phy->sifs_period = IEEE802154_SIFS_PERIOD *
+ wpan_phy->symbol_duration;
+}
+
+int ieee802154_register_hw(struct ieee802154_hw *hw)
+{
+ struct ieee802154_local *local = hw_to_local(hw);
+ struct net_device *dev;
+ int rc = -ENOSYS;
+
+ local->workqueue =
+ create_singlethread_workqueue(wpan_phy_name(local->phy));
+ if (!local->workqueue) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ hrtimer_init(&local->ifs_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ local->ifs_timer.function = ieee802154_xmit_ifs_timer;
+
+ wpan_phy_set_dev(local->phy, local->hw.parent);
+
+ ieee802154_setup_wpan_phy_pib(local->phy);
+
+ rc = wpan_phy_register(local->phy);
+ if (rc < 0)
+ goto out_wq;
+
+ rtnl_lock();
+
+ dev = ieee802154_if_add(local, "wpan%d", NL802154_IFTYPE_NODE,
+ cpu_to_le64(0x0000000000000000ULL));
+ if (IS_ERR(dev)) {
+ rtnl_unlock();
+ rc = PTR_ERR(dev);
+ goto out_wq;
+ }
+
+ rtnl_unlock();
+
+ return 0;
+
+out_wq:
+ destroy_workqueue(local->workqueue);
+out:
+ return rc;
+}
+EXPORT_SYMBOL(ieee802154_register_hw);
+
+void ieee802154_unregister_hw(struct ieee802154_hw *hw)
+{
+ struct ieee802154_local *local = hw_to_local(hw);
+
+ tasklet_kill(&local->tasklet);
+ flush_workqueue(local->workqueue);
+ destroy_workqueue(local->workqueue);
+
+ rtnl_lock();
+
+ ieee802154_remove_interfaces(local);
+
+ rtnl_unlock();
+
+ wpan_phy_unregister(local->phy);
+}
+EXPORT_SYMBOL(ieee802154_unregister_hw);
+
+static int __init ieee802154_init(void)
+{
+ return ieee802154_iface_init();
+}
+
+static void __exit ieee802154_exit(void)
+{
+ ieee802154_iface_exit();
+
+ rcu_barrier();
+}
+
+subsys_initcall(ieee802154_init);
+module_exit(ieee802154_exit);
+
+MODULE_DESCRIPTION("IEEE 802.15.4 subsystem");
+MODULE_LICENSE("GPL v2");
diff --git a/net/mac802154/mib.c b/net/mac802154/mib.c
index 868a040..5cf019a 100644
--- a/net/mac802154/mib.c
+++ b/net/mac802154/mib.c
@@ -10,10 +10,6 @@
* 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.
- *
* Written by:
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
* Sergey Lapin <slapin@ossfans.org>
@@ -25,208 +21,100 @@
#include <net/mac802154.h>
#include <net/ieee802154_netdev.h>
-#include <net/wpan-phy.h>
+#include <net/cfg802154.h>
-#include "mac802154.h"
-
-struct phy_chan_notify_work {
- struct work_struct work;
- struct net_device *dev;
-};
-
-struct hw_addr_filt_notify_work {
- struct work_struct work;
- struct net_device *dev;
- unsigned long changed;
-};
-
-static struct mac802154_priv *mac802154_slave_get_priv(struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
-
- BUG_ON(dev->type != ARPHRD_IEEE802154);
-
- return priv->hw;
-}
-
-static void hw_addr_notify(struct work_struct *work)
-{
- struct hw_addr_filt_notify_work *nw = container_of(work,
- struct hw_addr_filt_notify_work, work);
- struct mac802154_priv *hw = mac802154_slave_get_priv(nw->dev);
- int res;
-
- res = hw->ops->set_hw_addr_filt(&hw->hw,
- &hw->hw.hw_filt,
- nw->changed);
- if (res)
- pr_debug("failed changed mask %lx\n", nw->changed);
-
- kfree(nw);
-}
-
-static void set_hw_addr_filt(struct net_device *dev, unsigned long changed)
-{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
- struct hw_addr_filt_notify_work *work;
-
- work = kzalloc(sizeof(*work), GFP_ATOMIC);
- if (!work)
- return;
-
- INIT_WORK(&work->work, hw_addr_notify);
- work->dev = dev;
- work->changed = changed;
- queue_work(priv->hw->dev_workqueue, &work->work);
-}
+#include "ieee802154_i.h"
+#include "driver-ops.h"
void mac802154_dev_set_short_addr(struct net_device *dev, __le16 val)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
BUG_ON(dev->type != ARPHRD_IEEE802154);
- spin_lock_bh(&priv->mib_lock);
- priv->short_addr = val;
- spin_unlock_bh(&priv->mib_lock);
-
- if ((priv->hw->ops->set_hw_addr_filt) &&
- (priv->hw->hw.hw_filt.short_addr != priv->short_addr)) {
- priv->hw->hw.hw_filt.short_addr = priv->short_addr;
- set_hw_addr_filt(dev, IEEE802515_AFILT_SADDR_CHANGED);
- }
+ spin_lock_bh(&sdata->mib_lock);
+ sdata->wpan_dev.short_addr = val;
+ spin_unlock_bh(&sdata->mib_lock);
}
__le16 mac802154_dev_get_short_addr(const struct net_device *dev)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
__le16 ret;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- spin_lock_bh(&priv->mib_lock);
- ret = priv->short_addr;
- spin_unlock_bh(&priv->mib_lock);
+ spin_lock_bh(&sdata->mib_lock);
+ ret = sdata->wpan_dev.short_addr;
+ spin_unlock_bh(&sdata->mib_lock);
return ret;
}
-void mac802154_dev_set_ieee_addr(struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
- struct mac802154_priv *mac = priv->hw;
-
- priv->extended_addr = ieee802154_devaddr_from_raw(dev->dev_addr);
-
- if (mac->ops->set_hw_addr_filt &&
- mac->hw.hw_filt.ieee_addr != priv->extended_addr) {
- mac->hw.hw_filt.ieee_addr = priv->extended_addr;
- set_hw_addr_filt(dev, IEEE802515_AFILT_IEEEADDR_CHANGED);
- }
-}
-
__le16 mac802154_dev_get_pan_id(const struct net_device *dev)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
__le16 ret;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- spin_lock_bh(&priv->mib_lock);
- ret = priv->pan_id;
- spin_unlock_bh(&priv->mib_lock);
+ spin_lock_bh(&sdata->mib_lock);
+ ret = sdata->wpan_dev.pan_id;
+ spin_unlock_bh(&sdata->mib_lock);
return ret;
}
void mac802154_dev_set_pan_id(struct net_device *dev, __le16 val)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
BUG_ON(dev->type != ARPHRD_IEEE802154);
- spin_lock_bh(&priv->mib_lock);
- priv->pan_id = val;
- spin_unlock_bh(&priv->mib_lock);
-
- if ((priv->hw->ops->set_hw_addr_filt) &&
- (priv->hw->hw.hw_filt.pan_id != priv->pan_id)) {
- priv->hw->hw.hw_filt.pan_id = priv->pan_id;
- set_hw_addr_filt(dev, IEEE802515_AFILT_PANID_CHANGED);
- }
+ spin_lock_bh(&sdata->mib_lock);
+ sdata->wpan_dev.pan_id = val;
+ spin_unlock_bh(&sdata->mib_lock);
}
u8 mac802154_dev_get_dsn(const struct net_device *dev)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
BUG_ON(dev->type != ARPHRD_IEEE802154);
- return priv->dsn++;
-}
-
-static void phy_chan_notify(struct work_struct *work)
-{
- struct phy_chan_notify_work *nw = container_of(work,
- struct phy_chan_notify_work, work);
- struct mac802154_priv *hw = mac802154_slave_get_priv(nw->dev);
- struct mac802154_sub_if_data *priv = netdev_priv(nw->dev);
- int res;
-
- mutex_lock(&priv->hw->phy->pib_lock);
- res = hw->ops->set_channel(&hw->hw, priv->page, priv->chan);
- if (res) {
- pr_debug("set_channel failed\n");
- } else {
- priv->hw->phy->current_channel = priv->chan;
- priv->hw->phy->current_page = priv->page;
- }
- mutex_unlock(&priv->hw->phy->pib_lock);
-
- kfree(nw);
+ return sdata->wpan_dev.dsn++;
}
void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
- struct phy_chan_notify_work *work;
-
- BUG_ON(dev->type != ARPHRD_IEEE802154);
-
- spin_lock_bh(&priv->mib_lock);
- priv->page = page;
- priv->chan = chan;
- spin_unlock_bh(&priv->mib_lock);
-
- mutex_lock(&priv->hw->phy->pib_lock);
- if (priv->hw->phy->current_channel != priv->chan ||
- priv->hw->phy->current_page != priv->page) {
- mutex_unlock(&priv->hw->phy->pib_lock);
-
- work = kzalloc(sizeof(*work), GFP_ATOMIC);
- if (!work)
- return;
-
- INIT_WORK(&work->work, phy_chan_notify);
- work->dev = dev;
- queue_work(priv->hw->dev_workqueue, &work->work);
- } else {
- mutex_unlock(&priv->hw->phy->pib_lock);
- }
-}
-
-
-int mac802154_get_params(struct net_device *dev,
- struct ieee802154_llsec_params *params)
-{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ struct ieee802154_local *local = sdata->local;
int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
- res = mac802154_llsec_get_params(&priv->sec, params);
- mutex_unlock(&priv->sec_mtx);
+ res = drv_set_channel(local, page, chan);
+ if (res) {
+ pr_debug("set_channel failed\n");
+ } else {
+ mutex_lock(&local->phy->pib_lock);
+ local->phy->current_channel = chan;
+ local->phy->current_page = page;
+ mutex_unlock(&local->phy->pib_lock);
+ }
+}
+
+int mac802154_get_params(struct net_device *dev,
+ struct ieee802154_llsec_params *params)
+{
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ int res;
+
+ BUG_ON(dev->type != ARPHRD_IEEE802154);
+
+ mutex_lock(&sdata->sec_mtx);
+ res = mac802154_llsec_get_params(&sdata->sec, params);
+ mutex_unlock(&sdata->sec_mtx);
return res;
}
@@ -235,31 +123,30 @@
const struct ieee802154_llsec_params *params,
int changed)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
- res = mac802154_llsec_set_params(&priv->sec, params, changed);
- mutex_unlock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
+ res = mac802154_llsec_set_params(&sdata->sec, params, changed);
+ mutex_unlock(&sdata->sec_mtx);
return res;
}
-
int mac802154_add_key(struct net_device *dev,
const struct ieee802154_llsec_key_id *id,
const struct ieee802154_llsec_key *key)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
- res = mac802154_llsec_key_add(&priv->sec, id, key);
- mutex_unlock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
+ res = mac802154_llsec_key_add(&sdata->sec, id, key);
+ mutex_unlock(&sdata->sec_mtx);
return res;
}
@@ -267,61 +154,59 @@
int mac802154_del_key(struct net_device *dev,
const struct ieee802154_llsec_key_id *id)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
- res = mac802154_llsec_key_del(&priv->sec, id);
- mutex_unlock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
+ res = mac802154_llsec_key_del(&sdata->sec, id);
+ mutex_unlock(&sdata->sec_mtx);
return res;
}
-
int mac802154_add_dev(struct net_device *dev,
const struct ieee802154_llsec_device *llsec_dev)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
- res = mac802154_llsec_dev_add(&priv->sec, llsec_dev);
- mutex_unlock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
+ res = mac802154_llsec_dev_add(&sdata->sec, llsec_dev);
+ mutex_unlock(&sdata->sec_mtx);
return res;
}
int mac802154_del_dev(struct net_device *dev, __le64 dev_addr)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
- res = mac802154_llsec_dev_del(&priv->sec, dev_addr);
- mutex_unlock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
+ res = mac802154_llsec_dev_del(&sdata->sec, dev_addr);
+ mutex_unlock(&sdata->sec_mtx);
return res;
}
-
int mac802154_add_devkey(struct net_device *dev,
__le64 device_addr,
const struct ieee802154_llsec_device_key *key)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
- res = mac802154_llsec_devkey_add(&priv->sec, device_addr, key);
- mutex_unlock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
+ res = mac802154_llsec_devkey_add(&sdata->sec, device_addr, key);
+ mutex_unlock(&sdata->sec_mtx);
return res;
}
@@ -330,30 +215,29 @@
__le64 device_addr,
const struct ieee802154_llsec_device_key *key)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
- res = mac802154_llsec_devkey_del(&priv->sec, device_addr, key);
- mutex_unlock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
+ res = mac802154_llsec_devkey_del(&sdata->sec, device_addr, key);
+ mutex_unlock(&sdata->sec_mtx);
return res;
}
-
int mac802154_add_seclevel(struct net_device *dev,
const struct ieee802154_llsec_seclevel *sl)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
- res = mac802154_llsec_seclevel_add(&priv->sec, sl);
- mutex_unlock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
+ res = mac802154_llsec_seclevel_add(&sdata->sec, sl);
+ mutex_unlock(&sdata->sec_mtx);
return res;
}
@@ -361,43 +245,42 @@
int mac802154_del_seclevel(struct net_device *dev,
const struct ieee802154_llsec_seclevel *sl)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
- res = mac802154_llsec_seclevel_del(&priv->sec, sl);
- mutex_unlock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
+ res = mac802154_llsec_seclevel_del(&sdata->sec, sl);
+ mutex_unlock(&sdata->sec_mtx);
return res;
}
-
void mac802154_lock_table(struct net_device *dev)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
}
void mac802154_get_table(struct net_device *dev,
struct ieee802154_llsec_table **t)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
BUG_ON(dev->type != ARPHRD_IEEE802154);
- *t = &priv->sec.table;
+ *t = &sdata->sec.table;
}
void mac802154_unlock_table(struct net_device *dev)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_unlock(&priv->sec_mtx);
+ mutex_unlock(&sdata->sec_mtx);
}
diff --git a/net/mac802154/monitor.c b/net/mac802154/monitor.c
deleted file mode 100644
index a68230e..0000000
--- a/net/mac802154/monitor.c
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright 2007, 2008, 2009 Siemens AG
- *
- * 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.
- *
- * 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.
- *
- * Written by:
- * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
- * Sergey Lapin <slapin@ossfans.org>
- * Maxim Gorbachyov <maxim.gorbachev@siemens.com>
- * Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
- */
-
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <linux/if_arp.h>
-#include <linux/crc-ccitt.h>
-
-#include <net/ieee802154.h>
-#include <net/mac802154.h>
-#include <net/netlink.h>
-#include <net/wpan-phy.h>
-#include <linux/nl802154.h>
-
-#include "mac802154.h"
-
-static netdev_tx_t mac802154_monitor_xmit(struct sk_buff *skb,
- struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv;
- u8 chan, page;
-
- priv = netdev_priv(dev);
-
- /* FIXME: locking */
- chan = priv->hw->phy->current_channel;
- page = priv->hw->phy->current_page;
-
- if (chan == MAC802154_CHAN_NONE) /* not initialized */
- return NETDEV_TX_OK;
-
- if (WARN_ON(page >= WPAN_NUM_PAGES) ||
- WARN_ON(chan >= WPAN_NUM_CHANNELS))
- return NETDEV_TX_OK;
-
- skb->skb_iif = dev->ifindex;
- dev->stats.tx_packets++;
- dev->stats.tx_bytes += skb->len;
-
- return mac802154_tx(priv->hw, skb, page, chan);
-}
-
-
-void mac802154_monitors_rx(struct mac802154_priv *priv, struct sk_buff *skb)
-{
- struct sk_buff *skb2;
- struct mac802154_sub_if_data *sdata;
- u16 crc = crc_ccitt(0, skb->data, skb->len);
- u8 *data;
-
- rcu_read_lock();
- list_for_each_entry_rcu(sdata, &priv->slaves, list) {
- if (sdata->type != IEEE802154_DEV_MONITOR ||
- !netif_running(sdata->dev))
- continue;
-
- skb2 = skb_clone(skb, GFP_ATOMIC);
- skb2->dev = sdata->dev;
- skb2->pkt_type = PACKET_HOST;
- data = skb_put(skb2, 2);
- data[0] = crc & 0xff;
- data[1] = crc >> 8;
-
- netif_rx_ni(skb2);
- }
- rcu_read_unlock();
-}
-
-static const struct net_device_ops mac802154_monitor_ops = {
- .ndo_open = mac802154_slave_open,
- .ndo_stop = mac802154_slave_close,
- .ndo_start_xmit = mac802154_monitor_xmit,
-};
-
-void mac802154_monitor_setup(struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv;
-
- dev->addr_len = 0;
- dev->hard_header_len = 0;
- dev->needed_tailroom = 2; /* room for FCS */
- dev->mtu = IEEE802154_MTU;
- dev->tx_queue_len = 10;
- dev->type = ARPHRD_IEEE802154_MONITOR;
- dev->flags = IFF_NOARP | IFF_BROADCAST;
- dev->watchdog_timeo = 0;
-
- dev->destructor = free_netdev;
- dev->netdev_ops = &mac802154_monitor_ops;
- dev->ml_priv = &mac802154_mlme_reduced;
-
- priv = netdev_priv(dev);
- priv->type = IEEE802154_DEV_MONITOR;
-
- priv->chan = MAC802154_CHAN_NONE; /* not initialized */
- priv->page = 0;
-}
diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c
index a14cf9e..c0d67b2 100644
--- a/net/mac802154/rx.c
+++ b/net/mac802154/rx.c
@@ -10,10 +10,6 @@
* 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.
- *
* Written by:
* Pavel Smolenskiy <pavel.smolenskiy@gmail.com>
* Maxim Gorbachyov <maxim.gorbachev@siemens.com>
@@ -23,92 +19,284 @@
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/workqueue.h>
#include <linux/netdevice.h>
#include <linux/crc-ccitt.h>
+#include <asm/unaligned.h>
#include <net/mac802154.h>
#include <net/ieee802154_netdev.h>
+#include <net/nl802154.h>
-#include "mac802154.h"
+#include "ieee802154_i.h"
-/* The IEEE 802.15.4 standard defines 4 MAC packet types:
- * - beacon frame
- * - MAC command frame
- * - acknowledgement frame
- * - data frame
- *
- * and only the data frame should be pushed to the upper layers, other types
- * are just internal MAC layer management information. So only data packets
- * are going to be sent to the networking queue, all other will be processed
- * right here by using the device workqueue.
- */
-struct rx_work {
- struct sk_buff *skb;
- struct work_struct work;
- struct ieee802154_dev *dev;
- u8 lqi;
-};
-
-static void
-mac802154_subif_rx(struct ieee802154_dev *hw, struct sk_buff *skb, u8 lqi)
+static int ieee802154_deliver_skb(struct sk_buff *skb)
{
- struct mac802154_priv *priv = mac802154_to_priv(hw);
-
- mac_cb(skb)->lqi = lqi;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
skb->protocol = htons(ETH_P_IEEE802154);
- skb_reset_mac_header(skb);
- if (!(priv->hw.flags & IEEE802154_HW_OMIT_CKSUM)) {
- u16 crc;
+ return netif_receive_skb(skb);
+}
- if (skb->len < 2) {
- pr_debug("got invalid frame\n");
- goto fail;
- }
- crc = crc_ccitt(0, skb->data, skb->len);
- if (crc) {
- pr_debug("CRC mismatch\n");
- goto fail;
- }
- skb_trim(skb, skb->len - 2); /* CRC */
+static int
+ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata,
+ struct sk_buff *skb, const struct ieee802154_hdr *hdr)
+{
+ struct wpan_dev *wpan_dev = &sdata->wpan_dev;
+ __le16 span, sshort;
+ int rc;
+
+ pr_debug("getting packet via slave interface %s\n", sdata->dev->name);
+
+ spin_lock_bh(&sdata->mib_lock);
+
+ span = wpan_dev->pan_id;
+ sshort = wpan_dev->short_addr;
+
+ switch (mac_cb(skb)->dest.mode) {
+ case IEEE802154_ADDR_NONE:
+ if (mac_cb(skb)->dest.mode != IEEE802154_ADDR_NONE)
+ /* FIXME: check if we are PAN coordinator */
+ skb->pkt_type = PACKET_OTHERHOST;
+ else
+ /* ACK comes with both addresses empty */
+ skb->pkt_type = PACKET_HOST;
+ break;
+ case IEEE802154_ADDR_LONG:
+ if (mac_cb(skb)->dest.pan_id != span &&
+ mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST))
+ skb->pkt_type = PACKET_OTHERHOST;
+ else if (mac_cb(skb)->dest.extended_addr == wpan_dev->extended_addr)
+ skb->pkt_type = PACKET_HOST;
+ else
+ skb->pkt_type = PACKET_OTHERHOST;
+ break;
+ case IEEE802154_ADDR_SHORT:
+ if (mac_cb(skb)->dest.pan_id != span &&
+ mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST))
+ skb->pkt_type = PACKET_OTHERHOST;
+ else if (mac_cb(skb)->dest.short_addr == sshort)
+ skb->pkt_type = PACKET_HOST;
+ else if (mac_cb(skb)->dest.short_addr ==
+ cpu_to_le16(IEEE802154_ADDR_BROADCAST))
+ skb->pkt_type = PACKET_BROADCAST;
+ else
+ skb->pkt_type = PACKET_OTHERHOST;
+ break;
+ default:
+ spin_unlock_bh(&sdata->mib_lock);
+ pr_debug("invalid dest mode\n");
+ goto fail;
}
- mac802154_monitors_rx(priv, skb);
- mac802154_wpans_rx(priv, skb);
+ spin_unlock_bh(&sdata->mib_lock);
- return;
+ skb->dev = sdata->dev;
+
+ rc = mac802154_llsec_decrypt(&sdata->sec, skb);
+ if (rc) {
+ pr_debug("decryption failed: %i\n", rc);
+ goto fail;
+ }
+
+ sdata->dev->stats.rx_packets++;
+ sdata->dev->stats.rx_bytes += skb->len;
+
+ switch (mac_cb(skb)->type) {
+ case IEEE802154_FC_TYPE_DATA:
+ return ieee802154_deliver_skb(skb);
+ default:
+ pr_warn("ieee802154: bad frame received (type = %d)\n",
+ mac_cb(skb)->type);
+ goto fail;
+ }
fail:
kfree_skb(skb);
+ return NET_RX_DROP;
}
-static void mac802154_rx_worker(struct work_struct *work)
+static void
+ieee802154_print_addr(const char *name, const struct ieee802154_addr *addr)
{
- struct rx_work *rw = container_of(work, struct rx_work, work);
+ if (addr->mode == IEEE802154_ADDR_NONE)
+ pr_debug("%s not present\n", name);
- mac802154_subif_rx(rw->dev, rw->skb, rw->lqi);
- kfree(rw);
+ pr_debug("%s PAN ID: %04x\n", name, le16_to_cpu(addr->pan_id));
+ if (addr->mode == IEEE802154_ADDR_SHORT) {
+ pr_debug("%s is short: %04x\n", name,
+ le16_to_cpu(addr->short_addr));
+ } else {
+ u64 hw = swab64((__force u64)addr->extended_addr);
+
+ pr_debug("%s is hardware: %8phC\n", name, &hw);
+ }
}
+static int
+ieee802154_parse_frame_start(struct sk_buff *skb, struct ieee802154_hdr *hdr)
+{
+ int hlen;
+ struct ieee802154_mac_cb *cb = mac_cb_init(skb);
+
+ skb_reset_mac_header(skb);
+
+ hlen = ieee802154_hdr_pull(skb, hdr);
+ if (hlen < 0)
+ return -EINVAL;
+
+ skb->mac_len = hlen;
+
+ pr_debug("fc: %04x dsn: %02x\n", le16_to_cpup((__le16 *)&hdr->fc),
+ hdr->seq);
+
+ cb->type = hdr->fc.type;
+ cb->ackreq = hdr->fc.ack_request;
+ cb->secen = hdr->fc.security_enabled;
+
+ ieee802154_print_addr("destination", &hdr->dest);
+ ieee802154_print_addr("source", &hdr->source);
+
+ cb->source = hdr->source;
+ cb->dest = hdr->dest;
+
+ if (hdr->fc.security_enabled) {
+ u64 key;
+
+ pr_debug("seclevel %i\n", hdr->sec.level);
+
+ switch (hdr->sec.key_id_mode) {
+ case IEEE802154_SCF_KEY_IMPLICIT:
+ pr_debug("implicit key\n");
+ break;
+
+ case IEEE802154_SCF_KEY_INDEX:
+ pr_debug("key %02x\n", hdr->sec.key_id);
+ break;
+
+ case IEEE802154_SCF_KEY_SHORT_INDEX:
+ pr_debug("key %04x:%04x %02x\n",
+ le32_to_cpu(hdr->sec.short_src) >> 16,
+ le32_to_cpu(hdr->sec.short_src) & 0xffff,
+ hdr->sec.key_id);
+ break;
+
+ case IEEE802154_SCF_KEY_HW_INDEX:
+ key = swab64((__force u64)hdr->sec.extended_src);
+ pr_debug("key source %8phC %02x\n", &key,
+ hdr->sec.key_id);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static void
+__ieee802154_rx_handle_packet(struct ieee802154_local *local,
+ struct sk_buff *skb)
+{
+ int ret;
+ struct ieee802154_sub_if_data *sdata;
+ struct ieee802154_hdr hdr;
+
+ ret = ieee802154_parse_frame_start(skb, &hdr);
+ if (ret) {
+ pr_debug("got invalid frame\n");
+ kfree_skb(skb);
+ return;
+ }
+
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ if (sdata->vif.type != NL802154_IFTYPE_NODE ||
+ !netif_running(sdata->dev))
+ continue;
+
+ ieee802154_subif_frame(sdata, skb, &hdr);
+ skb = NULL;
+ break;
+ }
+
+ if (skb)
+ kfree_skb(skb);
+}
+
+static void
+ieee802154_monitors_rx(struct ieee802154_local *local, struct sk_buff *skb)
+{
+ struct sk_buff *skb2;
+ struct ieee802154_sub_if_data *sdata;
+
+ skb_reset_mac_header(skb);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ skb->pkt_type = PACKET_OTHERHOST;
+ skb->protocol = htons(ETH_P_IEEE802154);
+
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ if (sdata->vif.type != NL802154_IFTYPE_MONITOR)
+ continue;
+
+ if (!ieee802154_sdata_running(sdata))
+ continue;
+
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (skb2) {
+ skb2->dev = sdata->dev;
+ ieee802154_deliver_skb(skb2);
+
+ sdata->dev->stats.rx_packets++;
+ sdata->dev->stats.rx_bytes += skb->len;
+ }
+ }
+}
+
+void ieee802154_rx(struct ieee802154_hw *hw, struct sk_buff *skb)
+{
+ struct ieee802154_local *local = hw_to_local(hw);
+ u16 crc;
+
+ WARN_ON_ONCE(softirq_count() == 0);
+
+ /* TODO: When a transceiver omits the checksum here, we
+ * add an own calculated one. This is currently an ugly
+ * solution because the monitor needs a crc here.
+ */
+ if (local->hw.flags & IEEE802154_HW_RX_OMIT_CKSUM) {
+ crc = crc_ccitt(0, skb->data, skb->len);
+ put_unaligned_le16(crc, skb_put(skb, 2));
+ }
+
+ rcu_read_lock();
+
+ ieee802154_monitors_rx(local, skb);
+
+ /* Check if transceiver doesn't validate the checksum.
+ * If not we validate the checksum here.
+ */
+ if (local->hw.flags & IEEE802154_HW_RX_DROP_BAD_CKSUM) {
+ crc = crc_ccitt(0, skb->data, skb->len);
+ if (crc) {
+ rcu_read_unlock();
+ kfree_skb(skb);
+ return;
+ }
+ }
+ /* remove crc */
+ skb_trim(skb, skb->len - 2);
+
+ __ieee802154_rx_handle_packet(local, skb);
+
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL(ieee802154_rx);
+
void
-ieee802154_rx_irqsafe(struct ieee802154_dev *dev, struct sk_buff *skb, u8 lqi)
+ieee802154_rx_irqsafe(struct ieee802154_hw *hw, struct sk_buff *skb, u8 lqi)
{
- struct mac802154_priv *priv = mac802154_to_priv(dev);
- struct rx_work *work;
+ struct ieee802154_local *local = hw_to_local(hw);
- if (!skb)
- return;
-
- work = kzalloc(sizeof(*work), GFP_ATOMIC);
- if (!work)
- return;
-
- INIT_WORK(&work->work, mac802154_rx_worker);
- work->skb = skb;
- work->dev = dev;
- work->lqi = lqi;
-
- queue_work(priv->dev_workqueue, &work->work);
+ mac_cb(skb)->lqi = lqi;
+ skb->pkt_type = IEEE802154_RX_MSG;
+ skb_queue_tail(&local->skb_queue, skb);
+ tasklet_schedule(&local->tasklet);
}
EXPORT_SYMBOL(ieee802154_rx_irqsafe);
diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c
index fdf4c0e..c62e956 100644
--- a/net/mac802154/tx.c
+++ b/net/mac802154/tx.c
@@ -10,10 +10,6 @@
* 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.
- *
* Written by:
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
* Sergey Lapin <slapin@ossfans.org>
@@ -24,106 +20,98 @@
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/crc-ccitt.h>
+#include <asm/unaligned.h>
+#include <net/rtnetlink.h>
#include <net/ieee802154_netdev.h>
#include <net/mac802154.h>
-#include <net/wpan-phy.h>
+#include <net/cfg802154.h>
-#include "mac802154.h"
+#include "ieee802154_i.h"
+#include "driver-ops.h"
/* IEEE 802.15.4 transceivers can sleep during the xmit session, so process
* packets through the workqueue.
*/
-struct xmit_work {
+struct ieee802154_xmit_cb {
struct sk_buff *skb;
struct work_struct work;
- struct mac802154_priv *priv;
- u8 chan;
- u8 page;
+ struct ieee802154_local *local;
};
-static void mac802154_xmit_worker(struct work_struct *work)
+static struct ieee802154_xmit_cb ieee802154_xmit_cb;
+
+static void ieee802154_xmit_worker(struct work_struct *work)
{
- struct xmit_work *xw = container_of(work, struct xmit_work, work);
- struct mac802154_sub_if_data *sdata;
+ struct ieee802154_xmit_cb *cb =
+ container_of(work, struct ieee802154_xmit_cb, work);
+ struct ieee802154_local *local = cb->local;
+ struct sk_buff *skb = cb->skb;
+ struct net_device *dev = skb->dev;
int res;
- mutex_lock(&xw->priv->phy->pib_lock);
- if (xw->priv->phy->current_channel != xw->chan ||
- xw->priv->phy->current_page != xw->page) {
- res = xw->priv->ops->set_channel(&xw->priv->hw,
- xw->page,
- xw->chan);
- if (res) {
- pr_debug("set_channel failed\n");
- goto out;
- }
+ rtnl_lock();
- xw->priv->phy->current_channel = xw->chan;
- xw->priv->phy->current_page = xw->page;
- }
+ /* check if ifdown occurred while schedule */
+ if (!netif_running(dev))
+ goto err_tx;
- res = xw->priv->ops->xmit(&xw->priv->hw, xw->skb);
+ res = drv_xmit_sync(local, skb);
if (res)
- pr_debug("transmission failed\n");
+ goto err_tx;
-out:
- mutex_unlock(&xw->priv->phy->pib_lock);
+ ieee802154_xmit_complete(&local->hw, skb, false);
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += skb->len;
+
+ rtnl_unlock();
+
+ return;
+
+err_tx:
/* Restart the netif queue on each sub_if_data object. */
- rcu_read_lock();
- list_for_each_entry_rcu(sdata, &xw->priv->slaves, list)
- netif_wake_queue(sdata->dev);
- rcu_read_unlock();
-
- dev_kfree_skb(xw->skb);
-
- kfree(xw);
+ ieee802154_wake_queue(&local->hw);
+ rtnl_unlock();
+ kfree_skb(skb);
+ netdev_dbg(dev, "transmission failed\n");
}
-netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb,
- u8 page, u8 chan)
+static netdev_tx_t
+ieee802154_tx(struct ieee802154_local *local, struct sk_buff *skb)
{
- struct xmit_work *work;
- struct mac802154_sub_if_data *sdata;
+ struct net_device *dev = skb->dev;
+ int ret;
- if (!(priv->phy->channels_supported[page] & (1 << chan))) {
- WARN_ON(1);
- goto err_tx;
- }
-
- mac802154_monitors_rx(mac802154_to_priv(&priv->hw), skb);
-
- if (!(priv->hw.flags & IEEE802154_HW_OMIT_CKSUM)) {
+ if (!(local->hw.flags & IEEE802154_HW_TX_OMIT_CKSUM)) {
u16 crc = crc_ccitt(0, skb->data, skb->len);
- u8 *data = skb_put(skb, 2);
- data[0] = crc & 0xff;
- data[1] = crc >> 8;
+ put_unaligned_le16(crc, skb_put(skb, 2));
}
- if (skb_cow_head(skb, priv->hw.extra_tx_headroom))
+ if (skb_cow_head(skb, local->hw.extra_tx_headroom))
goto err_tx;
- work = kzalloc(sizeof(*work), GFP_ATOMIC);
- if (!work) {
- kfree_skb(skb);
- return NETDEV_TX_BUSY;
- }
-
/* Stop the netif queue on each sub_if_data object. */
- rcu_read_lock();
- list_for_each_entry_rcu(sdata, &priv->slaves, list)
- netif_stop_queue(sdata->dev);
- rcu_read_unlock();
+ ieee802154_stop_queue(&local->hw);
- INIT_WORK(&work->work, mac802154_xmit_worker);
- work->skb = skb;
- work->priv = priv;
- work->page = page;
- work->chan = chan;
+ /* async is priority, otherwise sync is fallback */
+ if (local->ops->xmit_async) {
+ ret = drv_xmit_async(local, skb);
+ if (ret) {
+ ieee802154_wake_queue(&local->hw);
+ goto err_tx;
+ }
- queue_work(priv->dev_workqueue, &work->work);
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += skb->len;
+ } else {
+ INIT_WORK(&ieee802154_xmit_cb.work, ieee802154_xmit_worker);
+ ieee802154_xmit_cb.skb = skb;
+ ieee802154_xmit_cb.local = local;
+
+ queue_work(local->workqueue, &ieee802154_xmit_cb.work);
+ }
return NETDEV_TX_OK;
@@ -131,3 +119,31 @@
kfree_skb(skb);
return NETDEV_TX_OK;
}
+
+netdev_tx_t
+ieee802154_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+
+ skb->skb_iif = dev->ifindex;
+
+ return ieee802154_tx(sdata->local, skb);
+}
+
+netdev_tx_t
+ieee802154_subif_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ int rc;
+
+ rc = mac802154_llsec_encrypt(&sdata->sec, skb);
+ if (rc) {
+ netdev_warn(dev, "encryption failed: %i\n", rc);
+ kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ skb->skb_iif = dev->ifindex;
+
+ return ieee802154_tx(sdata->local, skb);
+}
diff --git a/net/mac802154/util.c b/net/mac802154/util.c
new file mode 100644
index 0000000..5fc9790
--- /dev/null
+++ b/net/mac802154/util.c
@@ -0,0 +1,84 @@
+/* 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.
+ *
+ * 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.
+ *
+ * Authors:
+ * Alexander Aring <aar@pengutronix.de>
+ *
+ * Based on: net/mac80211/util.c
+ */
+
+#include "ieee802154_i.h"
+
+/* privid for wpan_phys to determine whether they belong to us or not */
+const void *const mac802154_wpan_phy_privid = &mac802154_wpan_phy_privid;
+
+void ieee802154_wake_queue(struct ieee802154_hw *hw)
+{
+ struct ieee802154_local *local = hw_to_local(hw);
+ struct ieee802154_sub_if_data *sdata;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ if (!sdata->dev)
+ continue;
+
+ netif_wake_queue(sdata->dev);
+ }
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL(ieee802154_wake_queue);
+
+void ieee802154_stop_queue(struct ieee802154_hw *hw)
+{
+ struct ieee802154_local *local = hw_to_local(hw);
+ struct ieee802154_sub_if_data *sdata;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ if (!sdata->dev)
+ continue;
+
+ netif_stop_queue(sdata->dev);
+ }
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL(ieee802154_stop_queue);
+
+enum hrtimer_restart ieee802154_xmit_ifs_timer(struct hrtimer *timer)
+{
+ struct ieee802154_local *local =
+ container_of(timer, struct ieee802154_local, ifs_timer);
+
+ ieee802154_wake_queue(&local->hw);
+
+ return HRTIMER_NORESTART;
+}
+
+void ieee802154_xmit_complete(struct ieee802154_hw *hw, struct sk_buff *skb,
+ bool ifs_handling)
+{
+ if (ifs_handling) {
+ struct ieee802154_local *local = hw_to_local(hw);
+
+ if (skb->len > 18)
+ hrtimer_start(&local->ifs_timer,
+ ktime_set(0, hw->phy->lifs_period * NSEC_PER_USEC),
+ HRTIMER_MODE_REL);
+ else
+ hrtimer_start(&local->ifs_timer,
+ ktime_set(0, hw->phy->sifs_period * NSEC_PER_USEC),
+ HRTIMER_MODE_REL);
+
+ consume_skb(skb);
+ } else {
+ ieee802154_wake_queue(hw);
+ consume_skb(skb);
+ }
+}
+EXPORT_SYMBOL(ieee802154_xmit_complete);
diff --git a/net/mac802154/wpan.c b/net/mac802154/wpan.c
deleted file mode 100644
index 4ab86a5..0000000
--- a/net/mac802154/wpan.c
+++ /dev/null
@@ -1,599 +0,0 @@
-/*
- * Copyright 2007-2012 Siemens AG
- *
- * 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.
- *
- * 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.
- *
- * Written by:
- * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
- * Sergey Lapin <slapin@ossfans.org>
- * Maxim Gorbachyov <maxim.gorbachev@siemens.com>
- * Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
- */
-
-#include <linux/netdevice.h>
-#include <linux/module.h>
-#include <linux/if_arp.h>
-
-#include <net/rtnetlink.h>
-#include <linux/nl802154.h>
-#include <net/af_ieee802154.h>
-#include <net/mac802154.h>
-#include <net/ieee802154_netdev.h>
-#include <net/ieee802154.h>
-#include <net/wpan-phy.h>
-
-#include "mac802154.h"
-
-static int mac802154_wpan_update_llsec(struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
- struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
- int rc = 0;
-
- if (ops->llsec) {
- struct ieee802154_llsec_params params;
- int changed = 0;
-
- params.pan_id = priv->pan_id;
- changed |= IEEE802154_LLSEC_PARAM_PAN_ID;
-
- params.hwaddr = priv->extended_addr;
- changed |= IEEE802154_LLSEC_PARAM_HWADDR;
-
- rc = ops->llsec->set_params(dev, ¶ms, changed);
- }
-
- return rc;
-}
-
-static int
-mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
-{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
- struct sockaddr_ieee802154 *sa =
- (struct sockaddr_ieee802154 *)&ifr->ifr_addr;
- int err = -ENOIOCTLCMD;
-
- spin_lock_bh(&priv->mib_lock);
-
- switch (cmd) {
- case SIOCGIFADDR:
- {
- u16 pan_id, short_addr;
-
- pan_id = le16_to_cpu(priv->pan_id);
- short_addr = le16_to_cpu(priv->short_addr);
- if (pan_id == IEEE802154_PANID_BROADCAST ||
- short_addr == IEEE802154_ADDR_BROADCAST) {
- err = -EADDRNOTAVAIL;
- break;
- }
-
- sa->family = AF_IEEE802154;
- sa->addr.addr_type = IEEE802154_ADDR_SHORT;
- sa->addr.pan_id = pan_id;
- sa->addr.short_addr = short_addr;
-
- err = 0;
- break;
- }
- case SIOCSIFADDR:
- dev_warn(&dev->dev,
- "Using DEBUGing ioctl SIOCSIFADDR isn't recommended!\n");
- if (sa->family != AF_IEEE802154 ||
- sa->addr.addr_type != IEEE802154_ADDR_SHORT ||
- sa->addr.pan_id == IEEE802154_PANID_BROADCAST ||
- sa->addr.short_addr == IEEE802154_ADDR_BROADCAST ||
- sa->addr.short_addr == IEEE802154_ADDR_UNDEF) {
- err = -EINVAL;
- break;
- }
-
- priv->pan_id = cpu_to_le16(sa->addr.pan_id);
- priv->short_addr = cpu_to_le16(sa->addr.short_addr);
-
- err = mac802154_wpan_update_llsec(dev);
- break;
- }
-
- spin_unlock_bh(&priv->mib_lock);
- return err;
-}
-
-static int mac802154_wpan_mac_addr(struct net_device *dev, void *p)
-{
- struct sockaddr *addr = p;
-
- if (netif_running(dev))
- return -EBUSY;
-
- /* FIXME: validate addr */
- memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
- mac802154_dev_set_ieee_addr(dev);
- return mac802154_wpan_update_llsec(dev);
-}
-
-int mac802154_set_mac_params(struct net_device *dev,
- const struct ieee802154_mac_params *params)
-{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
-
- mutex_lock(&priv->hw->slaves_mtx);
- priv->mac_params = *params;
- mutex_unlock(&priv->hw->slaves_mtx);
-
- return 0;
-}
-
-void mac802154_get_mac_params(struct net_device *dev,
- struct ieee802154_mac_params *params)
-{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
-
- mutex_lock(&priv->hw->slaves_mtx);
- *params = priv->mac_params;
- mutex_unlock(&priv->hw->slaves_mtx);
-}
-
-static int mac802154_wpan_open(struct net_device *dev)
-{
- int rc;
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
- struct wpan_phy *phy = priv->hw->phy;
-
- rc = mac802154_slave_open(dev);
- if (rc < 0)
- return rc;
-
- mutex_lock(&phy->pib_lock);
-
- if (phy->set_txpower) {
- rc = phy->set_txpower(phy, priv->mac_params.transmit_power);
- if (rc < 0)
- goto out;
- }
-
- if (phy->set_lbt) {
- rc = phy->set_lbt(phy, priv->mac_params.lbt);
- if (rc < 0)
- goto out;
- }
-
- if (phy->set_cca_mode) {
- rc = phy->set_cca_mode(phy, priv->mac_params.cca_mode);
- if (rc < 0)
- goto out;
- }
-
- if (phy->set_cca_ed_level) {
- rc = phy->set_cca_ed_level(phy, priv->mac_params.cca_ed_level);
- if (rc < 0)
- goto out;
- }
-
- if (phy->set_csma_params) {
- rc = phy->set_csma_params(phy, priv->mac_params.min_be,
- priv->mac_params.max_be,
- priv->mac_params.csma_retries);
- if (rc < 0)
- goto out;
- }
-
- if (phy->set_frame_retries) {
- rc = phy->set_frame_retries(phy,
- priv->mac_params.frame_retries);
- if (rc < 0)
- goto out;
- }
-
- mutex_unlock(&phy->pib_lock);
- return 0;
-
-out:
- mutex_unlock(&phy->pib_lock);
- return rc;
-}
-
-static int mac802154_set_header_security(struct mac802154_sub_if_data *priv,
- struct ieee802154_hdr *hdr,
- const struct ieee802154_mac_cb *cb)
-{
- struct ieee802154_llsec_params params;
- u8 level;
-
- mac802154_llsec_get_params(&priv->sec, ¶ms);
-
- if (!params.enabled && cb->secen_override && cb->secen)
- return -EINVAL;
- if (!params.enabled ||
- (cb->secen_override && !cb->secen) ||
- !params.out_level)
- return 0;
- if (cb->seclevel_override && !cb->seclevel)
- return -EINVAL;
-
- level = cb->seclevel_override ? cb->seclevel : params.out_level;
-
- hdr->fc.security_enabled = 1;
- hdr->sec.level = level;
- hdr->sec.key_id_mode = params.out_key.mode;
- if (params.out_key.mode == IEEE802154_SCF_KEY_SHORT_INDEX)
- hdr->sec.short_src = params.out_key.short_source;
- else if (params.out_key.mode == IEEE802154_SCF_KEY_HW_INDEX)
- hdr->sec.extended_src = params.out_key.extended_source;
- hdr->sec.key_id = params.out_key.id;
-
- return 0;
-}
-
-static int mac802154_header_create(struct sk_buff *skb,
- struct net_device *dev,
- unsigned short type,
- const void *daddr,
- const void *saddr,
- unsigned len)
-{
- struct ieee802154_hdr hdr;
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
- struct ieee802154_mac_cb *cb = mac_cb(skb);
- int hlen;
-
- if (!daddr)
- return -EINVAL;
-
- memset(&hdr.fc, 0, sizeof(hdr.fc));
- hdr.fc.type = cb->type;
- hdr.fc.security_enabled = cb->secen;
- hdr.fc.ack_request = cb->ackreq;
- hdr.seq = ieee802154_mlme_ops(dev)->get_dsn(dev);
-
- if (mac802154_set_header_security(priv, &hdr, cb) < 0)
- return -EINVAL;
-
- if (!saddr) {
- spin_lock_bh(&priv->mib_lock);
-
- if (priv->short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST) ||
- priv->short_addr == cpu_to_le16(IEEE802154_ADDR_UNDEF) ||
- priv->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST)) {
- hdr.source.mode = IEEE802154_ADDR_LONG;
- hdr.source.extended_addr = priv->extended_addr;
- } else {
- hdr.source.mode = IEEE802154_ADDR_SHORT;
- hdr.source.short_addr = priv->short_addr;
- }
-
- hdr.source.pan_id = priv->pan_id;
-
- spin_unlock_bh(&priv->mib_lock);
- } else {
- hdr.source = *(const struct ieee802154_addr *)saddr;
- }
-
- hdr.dest = *(const struct ieee802154_addr *)daddr;
-
- hlen = ieee802154_hdr_push(skb, &hdr);
- if (hlen < 0)
- return -EINVAL;
-
- skb_reset_mac_header(skb);
- skb->mac_len = hlen;
-
- if (len > ieee802154_max_payload(&hdr))
- return -EMSGSIZE;
-
- return hlen;
-}
-
-static int
-mac802154_header_parse(const struct sk_buff *skb, unsigned char *haddr)
-{
- struct ieee802154_hdr hdr;
- struct ieee802154_addr *addr = (struct ieee802154_addr *)haddr;
-
- if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) {
- pr_debug("malformed packet\n");
- return 0;
- }
-
- *addr = hdr.source;
- return sizeof(*addr);
-}
-
-static netdev_tx_t
-mac802154_wpan_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv;
- u8 chan, page;
- int rc;
-
- priv = netdev_priv(dev);
-
- spin_lock_bh(&priv->mib_lock);
- chan = priv->chan;
- page = priv->page;
- spin_unlock_bh(&priv->mib_lock);
-
- if (chan == MAC802154_CHAN_NONE ||
- page >= WPAN_NUM_PAGES ||
- chan >= WPAN_NUM_CHANNELS) {
- kfree_skb(skb);
- return NETDEV_TX_OK;
- }
-
- rc = mac802154_llsec_encrypt(&priv->sec, skb);
- if (rc) {
- pr_warn("encryption failed: %i\n", rc);
- kfree_skb(skb);
- return NETDEV_TX_OK;
- }
-
- skb->skb_iif = dev->ifindex;
- dev->stats.tx_packets++;
- dev->stats.tx_bytes += skb->len;
-
- return mac802154_tx(priv->hw, skb, page, chan);
-}
-
-static struct header_ops mac802154_header_ops = {
- .create = mac802154_header_create,
- .parse = mac802154_header_parse,
-};
-
-static const struct net_device_ops mac802154_wpan_ops = {
- .ndo_open = mac802154_wpan_open,
- .ndo_stop = mac802154_slave_close,
- .ndo_start_xmit = mac802154_wpan_xmit,
- .ndo_do_ioctl = mac802154_wpan_ioctl,
- .ndo_set_mac_address = mac802154_wpan_mac_addr,
-};
-
-static void mac802154_wpan_free(struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
-
- mac802154_llsec_destroy(&priv->sec);
-
- free_netdev(dev);
-}
-
-void mac802154_wpan_setup(struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv;
-
- dev->addr_len = IEEE802154_ADDR_LEN;
- memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN);
-
- dev->hard_header_len = MAC802154_FRAME_HARD_HEADER_LEN;
- dev->header_ops = &mac802154_header_ops;
- dev->needed_tailroom = 2 + 16; /* FCS + MIC */
- dev->mtu = IEEE802154_MTU;
- dev->tx_queue_len = 300;
- dev->type = ARPHRD_IEEE802154;
- dev->flags = IFF_NOARP | IFF_BROADCAST;
- dev->watchdog_timeo = 0;
-
- dev->destructor = mac802154_wpan_free;
- dev->netdev_ops = &mac802154_wpan_ops;
- dev->ml_priv = &mac802154_mlme_wpan;
-
- priv = netdev_priv(dev);
- priv->type = IEEE802154_DEV_WPAN;
-
- priv->chan = MAC802154_CHAN_NONE;
- priv->page = 0;
-
- spin_lock_init(&priv->mib_lock);
- mutex_init(&priv->sec_mtx);
-
- get_random_bytes(&priv->bsn, 1);
- get_random_bytes(&priv->dsn, 1);
-
- /* defaults per 802.15.4-2011 */
- priv->mac_params.min_be = 3;
- priv->mac_params.max_be = 5;
- priv->mac_params.csma_retries = 4;
- priv->mac_params.frame_retries = -1; /* for compatibility, actual default is 3 */
-
- priv->pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST);
- priv->short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
-
- mac802154_llsec_init(&priv->sec);
-}
-
-static int mac802154_process_data(struct net_device *dev, struct sk_buff *skb)
-{
- return netif_rx_ni(skb);
-}
-
-static int
-mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb,
- const struct ieee802154_hdr *hdr)
-{
- __le16 span, sshort;
- int rc;
-
- pr_debug("getting packet via slave interface %s\n", sdata->dev->name);
-
- spin_lock_bh(&sdata->mib_lock);
-
- span = sdata->pan_id;
- sshort = sdata->short_addr;
-
- switch (mac_cb(skb)->dest.mode) {
- case IEEE802154_ADDR_NONE:
- if (mac_cb(skb)->dest.mode != IEEE802154_ADDR_NONE)
- /* FIXME: check if we are PAN coordinator */
- skb->pkt_type = PACKET_OTHERHOST;
- else
- /* ACK comes with both addresses empty */
- skb->pkt_type = PACKET_HOST;
- break;
- case IEEE802154_ADDR_LONG:
- if (mac_cb(skb)->dest.pan_id != span &&
- mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST))
- skb->pkt_type = PACKET_OTHERHOST;
- else if (mac_cb(skb)->dest.extended_addr == sdata->extended_addr)
- skb->pkt_type = PACKET_HOST;
- else
- skb->pkt_type = PACKET_OTHERHOST;
- break;
- case IEEE802154_ADDR_SHORT:
- if (mac_cb(skb)->dest.pan_id != span &&
- mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST))
- skb->pkt_type = PACKET_OTHERHOST;
- else if (mac_cb(skb)->dest.short_addr == sshort)
- skb->pkt_type = PACKET_HOST;
- else if (mac_cb(skb)->dest.short_addr ==
- cpu_to_le16(IEEE802154_ADDR_BROADCAST))
- skb->pkt_type = PACKET_BROADCAST;
- else
- skb->pkt_type = PACKET_OTHERHOST;
- break;
- default:
- spin_unlock_bh(&sdata->mib_lock);
- pr_debug("invalid dest mode\n");
- kfree_skb(skb);
- return NET_RX_DROP;
- }
-
- spin_unlock_bh(&sdata->mib_lock);
-
- skb->dev = sdata->dev;
-
- rc = mac802154_llsec_decrypt(&sdata->sec, skb);
- if (rc) {
- pr_debug("decryption failed: %i\n", rc);
- goto fail;
- }
-
- sdata->dev->stats.rx_packets++;
- sdata->dev->stats.rx_bytes += skb->len;
-
- switch (mac_cb(skb)->type) {
- case IEEE802154_FC_TYPE_DATA:
- return mac802154_process_data(sdata->dev, skb);
- default:
- pr_warn("ieee802154: bad frame received (type = %d)\n",
- mac_cb(skb)->type);
- goto fail;
- }
-
-fail:
- kfree_skb(skb);
- return NET_RX_DROP;
-}
-
-static void mac802154_print_addr(const char *name,
- const struct ieee802154_addr *addr)
-{
- if (addr->mode == IEEE802154_ADDR_NONE)
- pr_debug("%s not present\n", name);
-
- pr_debug("%s PAN ID: %04x\n", name, le16_to_cpu(addr->pan_id));
- if (addr->mode == IEEE802154_ADDR_SHORT) {
- pr_debug("%s is short: %04x\n", name,
- le16_to_cpu(addr->short_addr));
- } else {
- u64 hw = swab64((__force u64) addr->extended_addr);
-
- pr_debug("%s is hardware: %8phC\n", name, &hw);
- }
-}
-
-static int mac802154_parse_frame_start(struct sk_buff *skb,
- struct ieee802154_hdr *hdr)
-{
- int hlen;
- struct ieee802154_mac_cb *cb = mac_cb_init(skb);
-
- hlen = ieee802154_hdr_pull(skb, hdr);
- if (hlen < 0)
- return -EINVAL;
-
- skb->mac_len = hlen;
-
- pr_debug("fc: %04x dsn: %02x\n", le16_to_cpup((__le16 *)&hdr->fc),
- hdr->seq);
-
- cb->type = hdr->fc.type;
- cb->ackreq = hdr->fc.ack_request;
- cb->secen = hdr->fc.security_enabled;
-
- mac802154_print_addr("destination", &hdr->dest);
- mac802154_print_addr("source", &hdr->source);
-
- cb->source = hdr->source;
- cb->dest = hdr->dest;
-
- if (hdr->fc.security_enabled) {
- u64 key;
-
- pr_debug("seclevel %i\n", hdr->sec.level);
-
- switch (hdr->sec.key_id_mode) {
- case IEEE802154_SCF_KEY_IMPLICIT:
- pr_debug("implicit key\n");
- break;
-
- case IEEE802154_SCF_KEY_INDEX:
- pr_debug("key %02x\n", hdr->sec.key_id);
- break;
-
- case IEEE802154_SCF_KEY_SHORT_INDEX:
- pr_debug("key %04x:%04x %02x\n",
- le32_to_cpu(hdr->sec.short_src) >> 16,
- le32_to_cpu(hdr->sec.short_src) & 0xffff,
- hdr->sec.key_id);
- break;
-
- case IEEE802154_SCF_KEY_HW_INDEX:
- key = swab64((__force u64) hdr->sec.extended_src);
- pr_debug("key source %8phC %02x\n", &key,
- hdr->sec.key_id);
- break;
- }
- }
-
- return 0;
-}
-
-void mac802154_wpans_rx(struct mac802154_priv *priv, struct sk_buff *skb)
-{
- int ret;
- struct mac802154_sub_if_data *sdata;
- struct ieee802154_hdr hdr;
-
- ret = mac802154_parse_frame_start(skb, &hdr);
- if (ret) {
- pr_debug("got invalid frame\n");
- kfree_skb(skb);
- return;
- }
-
- rcu_read_lock();
- list_for_each_entry_rcu(sdata, &priv->slaves, list) {
- if (sdata->type != IEEE802154_DEV_WPAN ||
- !netif_running(sdata->dev))
- continue;
-
- mac802154_subif_frame(sdata, skb, &hdr);
- skb = NULL;
- break;
- }
- rcu_read_unlock();
-
- if (skb)
- kfree_skb(skb);
-}
diff --git a/net/nfc/Kconfig b/net/nfc/Kconfig
index 314f31c..5e54818 100644
--- a/net/nfc/Kconfig
+++ b/net/nfc/Kconfig
@@ -3,7 +3,7 @@
#
menuconfig NFC
- depends on !BACKPORT_KERNEL_3_1
+ depends on !KERNEL_3_1
depends on NET
depends on RFKILL || !RFKILL
tristate "NFC subsystem support"
diff --git a/net/nfc/digital_dep.c b/net/nfc/digital_dep.c
index b60aa35..f72be74 100644
--- a/net/nfc/digital_dep.c
+++ b/net/nfc/digital_dep.c
@@ -17,6 +17,9 @@
#include "digital.h"
+#define DIGITAL_NFC_DEP_N_RETRY_NACK 2
+#define DIGITAL_NFC_DEP_N_RETRY_ATN 2
+
#define DIGITAL_NFC_DEP_FRAME_DIR_OUT 0xD4
#define DIGITAL_NFC_DEP_FRAME_DIR_IN 0xD5
@@ -32,20 +35,32 @@
#define DIGITAL_ATR_REQ_MIN_SIZE 16
#define DIGITAL_ATR_REQ_MAX_SIZE 64
-#define DIGITAL_LR_BITS_PAYLOAD_SIZE_254B 0x30
-#define DIGITAL_FSL_BITS_PAYLOAD_SIZE_254B \
- (DIGITAL_LR_BITS_PAYLOAD_SIZE_254B >> 4)
+#define DIGITAL_DID_MAX 14
+
+#define DIGITAL_PAYLOAD_SIZE_MAX 254
+#define DIGITAL_PAYLOAD_BITS_TO_PP(s) (((s) & 0x3) << 4)
+#define DIGITAL_PAYLOAD_PP_TO_BITS(s) (((s) >> 4) & 0x3)
+#define DIGITAL_PAYLOAD_BITS_TO_FSL(s) ((s) & 0x3)
+#define DIGITAL_PAYLOAD_FSL_TO_BITS(s) ((s) & 0x3)
+
#define DIGITAL_GB_BIT 0x02
+#define DIGITAL_NFC_DEP_REQ_RES_HEADROOM 2 /* SoD: [SB (NFC-A)] + LEN */
+#define DIGITAL_NFC_DEP_REQ_RES_TAILROOM 2 /* EoD: 2-byte CRC */
+
#define DIGITAL_NFC_DEP_PFB_TYPE(pfb) ((pfb) & 0xE0)
#define DIGITAL_NFC_DEP_PFB_TIMEOUT_BIT 0x10
+#define DIGITAL_NFC_DEP_PFB_MI_BIT 0x10
+#define DIGITAL_NFC_DEP_PFB_NACK_BIT 0x10
+#define DIGITAL_NFC_DEP_PFB_DID_BIT 0x04
#define DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb) \
((pfb) & DIGITAL_NFC_DEP_PFB_TIMEOUT_BIT)
-#define DIGITAL_NFC_DEP_MI_BIT_SET(pfb) ((pfb) & 0x10)
+#define DIGITAL_NFC_DEP_MI_BIT_SET(pfb) ((pfb) & DIGITAL_NFC_DEP_PFB_MI_BIT)
+#define DIGITAL_NFC_DEP_NACK_BIT_SET(pfb) ((pfb) & DIGITAL_NFC_DEP_PFB_NACK_BIT)
#define DIGITAL_NFC_DEP_NAD_BIT_SET(pfb) ((pfb) & 0x08)
-#define DIGITAL_NFC_DEP_DID_BIT_SET(pfb) ((pfb) & 0x04)
+#define DIGITAL_NFC_DEP_DID_BIT_SET(pfb) ((pfb) & DIGITAL_NFC_DEP_PFB_DID_BIT)
#define DIGITAL_NFC_DEP_PFB_PNI(pfb) ((pfb) & 0x03)
#define DIGITAL_NFC_DEP_PFB_I_PDU 0x00
@@ -97,6 +112,34 @@
static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg,
struct sk_buff *resp);
+static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg,
+ struct sk_buff *resp);
+
+static const u8 digital_payload_bits_map[4] = {
+ [0] = 64,
+ [1] = 128,
+ [2] = 192,
+ [3] = 254
+};
+
+static u8 digital_payload_bits_to_size(u8 payload_bits)
+{
+ if (payload_bits >= ARRAY_SIZE(digital_payload_bits_map))
+ return 0;
+
+ return digital_payload_bits_map[payload_bits];
+}
+
+static u8 digital_payload_size_to_bits(u8 payload_size)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(digital_payload_bits_map); i++)
+ if (digital_payload_bits_map[i] == payload_size)
+ return i;
+
+ return 0xff;
+}
static void digital_skb_push_dep_sod(struct nfc_digital_dev *ddev,
struct sk_buff *skb)
@@ -129,6 +172,106 @@
return 0;
}
+static struct sk_buff *
+digital_send_dep_data_prep(struct nfc_digital_dev *ddev, struct sk_buff *skb,
+ struct digital_dep_req_res *dep_req_res,
+ struct digital_data_exch *data_exch)
+{
+ struct sk_buff *new_skb;
+
+ if (skb->len > ddev->remote_payload_max) {
+ dep_req_res->pfb |= DIGITAL_NFC_DEP_PFB_MI_BIT;
+
+ new_skb = digital_skb_alloc(ddev, ddev->remote_payload_max);
+ if (!new_skb) {
+ kfree_skb(ddev->chaining_skb);
+ ddev->chaining_skb = NULL;
+
+ return ERR_PTR(-ENOMEM);
+ }
+
+ skb_reserve(new_skb, ddev->tx_headroom + NFC_HEADER_SIZE +
+ DIGITAL_NFC_DEP_REQ_RES_HEADROOM);
+ memcpy(skb_put(new_skb, ddev->remote_payload_max), skb->data,
+ ddev->remote_payload_max);
+ skb_pull(skb, ddev->remote_payload_max);
+
+ ddev->chaining_skb = skb;
+ ddev->data_exch = data_exch;
+ } else {
+ ddev->chaining_skb = NULL;
+ new_skb = skb;
+ }
+
+ return new_skb;
+}
+
+static struct sk_buff *
+digital_recv_dep_data_gather(struct nfc_digital_dev *ddev, u8 pfb,
+ struct sk_buff *resp,
+ int (*send_ack)(struct nfc_digital_dev *ddev,
+ struct digital_data_exch
+ *data_exch),
+ struct digital_data_exch *data_exch)
+{
+ struct sk_buff *new_skb;
+ int rc;
+
+ if (DIGITAL_NFC_DEP_MI_BIT_SET(pfb) && (!ddev->chaining_skb)) {
+ ddev->chaining_skb =
+ nfc_alloc_recv_skb(8 * ddev->local_payload_max,
+ GFP_KERNEL);
+ if (!ddev->chaining_skb) {
+ rc = -ENOMEM;
+ goto error;
+ }
+ }
+
+ if (ddev->chaining_skb) {
+ if (resp->len > skb_tailroom(ddev->chaining_skb)) {
+ new_skb = skb_copy_expand(ddev->chaining_skb,
+ skb_headroom(
+ ddev->chaining_skb),
+ 8 * ddev->local_payload_max,
+ GFP_KERNEL);
+ if (!new_skb) {
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ kfree_skb(ddev->chaining_skb);
+ ddev->chaining_skb = new_skb;
+ }
+
+ memcpy(skb_put(ddev->chaining_skb, resp->len), resp->data,
+ resp->len);
+
+ kfree_skb(resp);
+ resp = NULL;
+
+ if (DIGITAL_NFC_DEP_MI_BIT_SET(pfb)) {
+ rc = send_ack(ddev, data_exch);
+ if (rc)
+ goto error;
+
+ return NULL;
+ }
+
+ resp = ddev->chaining_skb;
+ ddev->chaining_skb = NULL;
+ }
+
+ return resp;
+
+error:
+ kfree_skb(resp);
+
+ kfree_skb(ddev->chaining_skb);
+ ddev->chaining_skb = NULL;
+
+ return ERR_PTR(rc);
+}
+
static void digital_in_recv_psl_res(struct nfc_digital_dev *ddev, void *arg,
struct sk_buff *resp)
{
@@ -198,6 +341,8 @@
{
struct sk_buff *skb;
struct digital_psl_req *psl_req;
+ int rc;
+ u8 payload_size, payload_bits;
skb = digital_skb_alloc(ddev, sizeof(*psl_req));
if (!skb)
@@ -211,14 +356,24 @@
psl_req->cmd = DIGITAL_CMD_PSL_REQ;
psl_req->did = 0;
psl_req->brs = (0x2 << 3) | 0x2; /* 424F both directions */
- psl_req->fsl = DIGITAL_FSL_BITS_PAYLOAD_SIZE_254B;
+
+ payload_size = min(ddev->local_payload_max, ddev->remote_payload_max);
+ payload_bits = digital_payload_size_to_bits(payload_size);
+ psl_req->fsl = DIGITAL_PAYLOAD_BITS_TO_FSL(payload_bits);
+
+ ddev->local_payload_max = payload_size;
+ ddev->remote_payload_max = payload_size;
digital_skb_push_dep_sod(ddev, skb);
ddev->skb_add_crc(skb);
- return digital_in_send_cmd(ddev, skb, 500, digital_in_recv_psl_res,
- target);
+ rc = digital_in_send_cmd(ddev, skb, 500, digital_in_recv_psl_res,
+ target);
+ if (rc)
+ kfree_skb(skb);
+
+ return rc;
}
static void digital_in_recv_atr_res(struct nfc_digital_dev *ddev, void *arg,
@@ -226,7 +381,7 @@
{
struct nfc_target *target = arg;
struct digital_atr_res *atr_res;
- u8 gb_len;
+ u8 gb_len, payload_bits;
int rc;
if (IS_ERR(resp)) {
@@ -256,6 +411,14 @@
atr_res = (struct digital_atr_res *)resp->data;
+ payload_bits = DIGITAL_PAYLOAD_PP_TO_BITS(atr_res->pp);
+ ddev->remote_payload_max = digital_payload_bits_to_size(payload_bits);
+
+ if (!ddev->remote_payload_max) {
+ rc = -EINVAL;
+ goto exit;
+ }
+
rc = nfc_set_remote_general_bytes(ddev->nfc_dev, atr_res->gb, gb_len);
if (rc)
goto exit;
@@ -286,6 +449,8 @@
struct sk_buff *skb;
struct digital_atr_req *atr_req;
uint size;
+ int rc;
+ u8 payload_bits;
size = DIGITAL_ATR_REQ_MIN_SIZE + gb_len;
@@ -314,7 +479,9 @@
atr_req->bs = 0;
atr_req->br = 0;
- atr_req->pp = DIGITAL_LR_BITS_PAYLOAD_SIZE_254B;
+ ddev->local_payload_max = DIGITAL_PAYLOAD_SIZE_MAX;
+ payload_bits = digital_payload_size_to_bits(ddev->local_payload_max);
+ atr_req->pp = DIGITAL_PAYLOAD_BITS_TO_PP(payload_bits);
if (gb_len) {
atr_req->pp |= DIGITAL_GB_BIT;
@@ -325,8 +492,113 @@
ddev->skb_add_crc(skb);
- return digital_in_send_cmd(ddev, skb, 500, digital_in_recv_atr_res,
- target);
+ rc = digital_in_send_cmd(ddev, skb, 500, digital_in_recv_atr_res,
+ target);
+ if (rc)
+ kfree_skb(skb);
+
+ return rc;
+}
+
+static int digital_in_send_ack(struct nfc_digital_dev *ddev,
+ struct digital_data_exch *data_exch)
+{
+ struct digital_dep_req_res *dep_req;
+ struct sk_buff *skb;
+ int rc;
+
+ skb = digital_skb_alloc(ddev, 1);
+ if (!skb)
+ return -ENOMEM;
+
+ skb_push(skb, sizeof(struct digital_dep_req_res));
+
+ dep_req = (struct digital_dep_req_res *)skb->data;
+
+ dep_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT;
+ dep_req->cmd = DIGITAL_CMD_DEP_REQ;
+ dep_req->pfb = DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU |
+ ddev->curr_nfc_dep_pni;
+
+ digital_skb_push_dep_sod(ddev, skb);
+
+ ddev->skb_add_crc(skb);
+
+ ddev->saved_skb = skb_get(skb);
+ ddev->saved_skb_len = skb->len;
+
+ rc = digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res,
+ data_exch);
+ if (rc) {
+ kfree_skb(skb);
+ kfree_skb(ddev->saved_skb);
+ ddev->saved_skb = NULL;
+ }
+
+ return rc;
+}
+
+static int digital_in_send_nack(struct nfc_digital_dev *ddev,
+ struct digital_data_exch *data_exch)
+{
+ struct digital_dep_req_res *dep_req;
+ struct sk_buff *skb;
+ int rc;
+
+ skb = digital_skb_alloc(ddev, 1);
+ if (!skb)
+ return -ENOMEM;
+
+ skb_push(skb, sizeof(struct digital_dep_req_res));
+
+ dep_req = (struct digital_dep_req_res *)skb->data;
+
+ dep_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT;
+ dep_req->cmd = DIGITAL_CMD_DEP_REQ;
+ dep_req->pfb = DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU |
+ DIGITAL_NFC_DEP_PFB_NACK_BIT | ddev->curr_nfc_dep_pni;
+
+ digital_skb_push_dep_sod(ddev, skb);
+
+ ddev->skb_add_crc(skb);
+
+ rc = digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res,
+ data_exch);
+ if (rc)
+ kfree_skb(skb);
+
+ return rc;
+}
+
+static int digital_in_send_atn(struct nfc_digital_dev *ddev,
+ struct digital_data_exch *data_exch)
+{
+ struct digital_dep_req_res *dep_req;
+ struct sk_buff *skb;
+ int rc;
+
+ skb = digital_skb_alloc(ddev, 1);
+ if (!skb)
+ return -ENOMEM;
+
+ skb_push(skb, sizeof(struct digital_dep_req_res));
+
+ dep_req = (struct digital_dep_req_res *)skb->data;
+
+ dep_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT;
+ dep_req->cmd = DIGITAL_CMD_DEP_REQ;
+ dep_req->pfb = DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU;
+
+ digital_skb_push_dep_sod(ddev, skb);
+
+ ddev->skb_add_crc(skb);
+
+ rc = digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res,
+ data_exch);
+ if (rc)
+ kfree_skb(skb);
+
+ return rc;
}
static int digital_in_send_rtox(struct nfc_digital_dev *ddev,
@@ -355,12 +627,30 @@
ddev->skb_add_crc(skb);
+ ddev->saved_skb = skb_get(skb);
+ ddev->saved_skb_len = skb->len;
+
rc = digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res,
data_exch);
+ if (rc) {
+ kfree_skb(skb);
+ kfree_skb(ddev->saved_skb);
+ ddev->saved_skb = NULL;
+ }
return rc;
}
+static int digital_in_send_saved_skb(struct nfc_digital_dev *ddev,
+ struct digital_data_exch *data_exch)
+{
+ skb_get(ddev->saved_skb);
+ skb_push(ddev->saved_skb, ddev->saved_skb_len);
+
+ return digital_in_send_cmd(ddev, ddev->saved_skb, 1500,
+ digital_in_recv_dep_res, data_exch);
+}
+
static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg,
struct sk_buff *resp)
{
@@ -373,13 +663,28 @@
if (IS_ERR(resp)) {
rc = PTR_ERR(resp);
resp = NULL;
- goto exit;
- }
- rc = ddev->skb_check_crc(resp);
- if (rc) {
- PROTOCOL_ERR("14.4.1.6");
- goto error;
+ if (((rc != -ETIMEDOUT) || ddev->nack_count) &&
+ (ddev->nack_count++ < DIGITAL_NFC_DEP_N_RETRY_NACK)) {
+ ddev->atn_count = 0;
+
+ rc = digital_in_send_nack(ddev, data_exch);
+ if (rc)
+ goto error;
+
+ return;
+ } else if ((rc == -ETIMEDOUT) &&
+ (ddev->atn_count++ < DIGITAL_NFC_DEP_N_RETRY_ATN)) {
+ ddev->nack_count = 0;
+
+ rc = digital_in_send_atn(ddev, data_exch);
+ if (rc)
+ goto error;
+
+ return;
+ }
+
+ goto exit;
}
rc = digital_skb_pull_dep_sod(ddev, resp);
@@ -388,10 +693,37 @@
goto exit;
}
+ rc = ddev->skb_check_crc(resp);
+ if (rc) {
+ if ((resp->len >= 4) &&
+ (ddev->nack_count++ < DIGITAL_NFC_DEP_N_RETRY_NACK)) {
+ ddev->atn_count = 0;
+
+ rc = digital_in_send_nack(ddev, data_exch);
+ if (rc)
+ goto error;
+
+ kfree_skb(resp);
+
+ return;
+ }
+
+ PROTOCOL_ERR("14.4.1.6");
+ goto error;
+ }
+
+ ddev->atn_count = 0;
+ ddev->nack_count = 0;
+
+ if (resp->len > ddev->local_payload_max) {
+ rc = -EMSGSIZE;
+ goto exit;
+ }
+
+ size = sizeof(struct digital_dep_req_res);
dep_res = (struct digital_dep_req_res *)resp->data;
- if (resp->len < sizeof(struct digital_dep_req_res) ||
- dep_res->dir != DIGITAL_NFC_DEP_FRAME_DIR_IN ||
+ if (resp->len < size || dep_res->dir != DIGITAL_NFC_DEP_FRAME_DIR_IN ||
dep_res->cmd != DIGITAL_CMD_DEP_RES) {
rc = -EIO;
goto error;
@@ -399,6 +731,24 @@
pfb = dep_res->pfb;
+ if (DIGITAL_NFC_DEP_DID_BIT_SET(pfb)) {
+ PROTOCOL_ERR("14.8.2.1");
+ rc = -EIO;
+ goto error;
+ }
+
+ if (DIGITAL_NFC_DEP_NAD_BIT_SET(pfb)) {
+ rc = -EIO;
+ goto exit;
+ }
+
+ if (size > resp->len) {
+ rc = -EIO;
+ goto error;
+ }
+
+ skb_pull(resp, size);
+
switch (DIGITAL_NFC_DEP_PFB_TYPE(pfb)) {
case DIGITAL_NFC_DEP_PFB_I_PDU:
if (DIGITAL_NFC_DEP_PFB_PNI(pfb) != ddev->curr_nfc_dep_pni) {
@@ -409,21 +759,71 @@
ddev->curr_nfc_dep_pni =
DIGITAL_NFC_DEP_PFB_PNI(ddev->curr_nfc_dep_pni + 1);
+
+ kfree_skb(ddev->saved_skb);
+ ddev->saved_skb = NULL;
+
+ resp = digital_recv_dep_data_gather(ddev, pfb, resp,
+ digital_in_send_ack,
+ data_exch);
+ if (IS_ERR(resp)) {
+ rc = PTR_ERR(resp);
+ resp = NULL;
+ goto error;
+ }
+
+ /* If resp is NULL then we're still chaining so return and
+ * wait for the next part of the PDU. Else, the PDU is
+ * complete so pass it up.
+ */
+ if (!resp)
+ return;
+
rc = 0;
break;
case DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU:
- pr_err("Received a ACK/NACK PDU\n");
- rc = -EIO;
- goto error;
-
- case DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU:
- if (!DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb)) {
- rc = -EINVAL;
- goto error;
+ if (DIGITAL_NFC_DEP_PFB_PNI(pfb) != ddev->curr_nfc_dep_pni) {
+ PROTOCOL_ERR("14.12.3.3");
+ rc = -EIO;
+ goto exit;
}
- rc = digital_in_send_rtox(ddev, data_exch, resp->data[3]);
+ ddev->curr_nfc_dep_pni =
+ DIGITAL_NFC_DEP_PFB_PNI(ddev->curr_nfc_dep_pni + 1);
+
+ if (ddev->chaining_skb && !DIGITAL_NFC_DEP_NACK_BIT_SET(pfb)) {
+ kfree_skb(ddev->saved_skb);
+ ddev->saved_skb = NULL;
+
+ rc = digital_in_send_dep_req(ddev, NULL,
+ ddev->chaining_skb,
+ ddev->data_exch);
+ if (rc)
+ goto error;
+
+ return;
+ }
+
+ pr_err("Received a ACK/NACK PDU\n");
+ rc = -EINVAL;
+ goto exit;
+
+ case DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU:
+ if (!DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb)) { /* ATN */
+ rc = digital_in_send_saved_skb(ddev, data_exch);
+ if (rc) {
+ kfree_skb(ddev->saved_skb);
+ goto error;
+ }
+
+ return;
+ }
+
+ kfree_skb(ddev->saved_skb);
+ ddev->saved_skb = NULL;
+
+ rc = digital_in_send_rtox(ddev, data_exch, resp->data[0]);
if (rc)
goto error;
@@ -431,30 +831,18 @@
return;
}
- if (DIGITAL_NFC_DEP_MI_BIT_SET(pfb)) {
- pr_err("MI bit set. Chained PDU not supported\n");
- rc = -EIO;
- goto error;
- }
-
- size = sizeof(struct digital_dep_req_res);
-
- if (DIGITAL_NFC_DEP_DID_BIT_SET(pfb))
- size++;
-
- if (size > resp->len) {
- rc = -EIO;
- goto error;
- }
-
- skb_pull(resp, size);
-
exit:
data_exch->cb(data_exch->cb_context, resp, rc);
error:
kfree(data_exch);
+ kfree_skb(ddev->chaining_skb);
+ ddev->chaining_skb = NULL;
+
+ kfree_skb(ddev->saved_skb);
+ ddev->saved_skb = NULL;
+
if (rc)
kfree_skb(resp);
}
@@ -464,20 +852,47 @@
struct digital_data_exch *data_exch)
{
struct digital_dep_req_res *dep_req;
+ struct sk_buff *chaining_skb, *tmp_skb;
+ int rc;
skb_push(skb, sizeof(struct digital_dep_req_res));
dep_req = (struct digital_dep_req_res *)skb->data;
+
dep_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT;
dep_req->cmd = DIGITAL_CMD_DEP_REQ;
dep_req->pfb = ddev->curr_nfc_dep_pni;
- digital_skb_push_dep_sod(ddev, skb);
+ ddev->atn_count = 0;
+ ddev->nack_count = 0;
- ddev->skb_add_crc(skb);
+ chaining_skb = ddev->chaining_skb;
- return digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res,
- data_exch);
+ tmp_skb = digital_send_dep_data_prep(ddev, skb, dep_req, data_exch);
+ if (IS_ERR(tmp_skb))
+ return PTR_ERR(tmp_skb);
+
+ digital_skb_push_dep_sod(ddev, tmp_skb);
+
+ ddev->skb_add_crc(tmp_skb);
+
+ ddev->saved_skb = skb_get(tmp_skb);
+ ddev->saved_skb_len = tmp_skb->len;
+
+ rc = digital_in_send_cmd(ddev, tmp_skb, 1500, digital_in_recv_dep_res,
+ data_exch);
+ if (rc) {
+ if (tmp_skb != skb)
+ kfree_skb(tmp_skb);
+
+ kfree_skb(chaining_skb);
+ ddev->chaining_skb = NULL;
+
+ kfree_skb(ddev->saved_skb);
+ ddev->saved_skb = NULL;
+ }
+
+ return rc;
}
static void digital_tg_set_rf_tech(struct nfc_digital_dev *ddev, u8 rf_tech)
@@ -507,11 +922,106 @@
}
}
+static int digital_tg_send_ack(struct nfc_digital_dev *ddev,
+ struct digital_data_exch *data_exch)
+{
+ struct digital_dep_req_res *dep_res;
+ struct sk_buff *skb;
+ int rc;
+
+ skb = digital_skb_alloc(ddev, 1);
+ if (!skb)
+ return -ENOMEM;
+
+ skb_push(skb, sizeof(struct digital_dep_req_res));
+
+ dep_res = (struct digital_dep_req_res *)skb->data;
+
+ dep_res->dir = DIGITAL_NFC_DEP_FRAME_DIR_IN;
+ dep_res->cmd = DIGITAL_CMD_DEP_RES;
+ dep_res->pfb = DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU |
+ ddev->curr_nfc_dep_pni;
+
+ if (ddev->did) {
+ dep_res->pfb |= DIGITAL_NFC_DEP_PFB_DID_BIT;
+
+ memcpy(skb_put(skb, sizeof(ddev->did)), &ddev->did,
+ sizeof(ddev->did));
+ }
+
+ ddev->curr_nfc_dep_pni =
+ DIGITAL_NFC_DEP_PFB_PNI(ddev->curr_nfc_dep_pni + 1);
+
+ digital_skb_push_dep_sod(ddev, skb);
+
+ ddev->skb_add_crc(skb);
+
+ ddev->saved_skb = skb_get(skb);
+ ddev->saved_skb_len = skb->len;
+
+ rc = digital_tg_send_cmd(ddev, skb, 1500, digital_tg_recv_dep_req,
+ data_exch);
+ if (rc) {
+ kfree_skb(skb);
+ kfree_skb(ddev->saved_skb);
+ ddev->saved_skb = NULL;
+ }
+
+ return rc;
+}
+
+static int digital_tg_send_atn(struct nfc_digital_dev *ddev)
+{
+ struct digital_dep_req_res *dep_res;
+ struct sk_buff *skb;
+ int rc;
+
+ skb = digital_skb_alloc(ddev, 1);
+ if (!skb)
+ return -ENOMEM;
+
+ skb_push(skb, sizeof(struct digital_dep_req_res));
+
+ dep_res = (struct digital_dep_req_res *)skb->data;
+
+ dep_res->dir = DIGITAL_NFC_DEP_FRAME_DIR_IN;
+ dep_res->cmd = DIGITAL_CMD_DEP_RES;
+ dep_res->pfb = DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU;
+
+ if (ddev->did) {
+ dep_res->pfb |= DIGITAL_NFC_DEP_PFB_DID_BIT;
+
+ memcpy(skb_put(skb, sizeof(ddev->did)), &ddev->did,
+ sizeof(ddev->did));
+ }
+
+ digital_skb_push_dep_sod(ddev, skb);
+
+ ddev->skb_add_crc(skb);
+
+ rc = digital_tg_send_cmd(ddev, skb, 1500, digital_tg_recv_dep_req,
+ NULL);
+ if (rc)
+ kfree_skb(skb);
+
+ return rc;
+}
+
+static int digital_tg_send_saved_skb(struct nfc_digital_dev *ddev)
+{
+ skb_get(ddev->saved_skb);
+ skb_push(ddev->saved_skb, ddev->saved_skb_len);
+
+ return digital_tg_send_cmd(ddev, ddev->saved_skb, 1500,
+ digital_tg_recv_dep_req, NULL);
+}
+
static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg,
struct sk_buff *resp)
{
int rc;
struct digital_dep_req_res *dep_req;
+ u8 pfb;
size_t size;
if (IS_ERR(resp)) {
@@ -532,6 +1042,11 @@
goto exit;
}
+ if (resp->len > ddev->local_payload_max) {
+ rc = -EMSGSIZE;
+ goto exit;
+ }
+
size = sizeof(struct digital_dep_req_res);
dep_req = (struct digital_dep_req_res *)resp->data;
@@ -541,34 +1056,147 @@
goto exit;
}
- if (DIGITAL_NFC_DEP_DID_BIT_SET(dep_req->pfb))
- size++;
+ pfb = dep_req->pfb;
- if (resp->len < size) {
+ if (DIGITAL_NFC_DEP_DID_BIT_SET(pfb)) {
+ if (ddev->did && (ddev->did == resp->data[3])) {
+ size++;
+ } else {
+ rc = -EIO;
+ goto exit;
+ }
+ } else if (ddev->did) {
rc = -EIO;
goto exit;
}
- switch (DIGITAL_NFC_DEP_PFB_TYPE(dep_req->pfb)) {
- case DIGITAL_NFC_DEP_PFB_I_PDU:
- pr_debug("DIGITAL_NFC_DEP_PFB_I_PDU\n");
- ddev->curr_nfc_dep_pni = DIGITAL_NFC_DEP_PFB_PNI(dep_req->pfb);
- break;
- case DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU:
- pr_err("Received a ACK/NACK PDU\n");
- rc = -EINVAL;
+ if (DIGITAL_NFC_DEP_NAD_BIT_SET(pfb)) {
+ rc = -EIO;
goto exit;
- case DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU:
- pr_err("Received a SUPERVISOR PDU\n");
- rc = -EINVAL;
+ }
+
+ if (size > resp->len) {
+ rc = -EIO;
goto exit;
}
skb_pull(resp, size);
+ switch (DIGITAL_NFC_DEP_PFB_TYPE(pfb)) {
+ case DIGITAL_NFC_DEP_PFB_I_PDU:
+ pr_debug("DIGITAL_NFC_DEP_PFB_I_PDU\n");
+
+ if ((ddev->atn_count && (DIGITAL_NFC_DEP_PFB_PNI(pfb - 1) !=
+ ddev->curr_nfc_dep_pni)) ||
+ (DIGITAL_NFC_DEP_PFB_PNI(pfb) != ddev->curr_nfc_dep_pni)) {
+ PROTOCOL_ERR("14.12.3.4");
+ rc = -EIO;
+ goto exit;
+ }
+
+ if (ddev->atn_count) {
+ ddev->atn_count = 0;
+
+ rc = digital_tg_send_saved_skb(ddev);
+ if (rc)
+ goto exit;
+
+ return;
+ }
+
+ kfree_skb(ddev->saved_skb);
+ ddev->saved_skb = NULL;
+
+ resp = digital_recv_dep_data_gather(ddev, pfb, resp,
+ digital_tg_send_ack, NULL);
+ if (IS_ERR(resp)) {
+ rc = PTR_ERR(resp);
+ resp = NULL;
+ goto exit;
+ }
+
+ /* If resp is NULL then we're still chaining so return and
+ * wait for the next part of the PDU. Else, the PDU is
+ * complete so pass it up.
+ */
+ if (!resp)
+ return;
+
+ rc = 0;
+ break;
+ case DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU:
+ if (!DIGITAL_NFC_DEP_NACK_BIT_SET(pfb)) { /* ACK */
+ if ((ddev->atn_count &&
+ (DIGITAL_NFC_DEP_PFB_PNI(pfb - 1) !=
+ ddev->curr_nfc_dep_pni)) ||
+ (DIGITAL_NFC_DEP_PFB_PNI(pfb) !=
+ ddev->curr_nfc_dep_pni) ||
+ !ddev->chaining_skb || !ddev->saved_skb) {
+ rc = -EIO;
+ goto exit;
+ }
+
+ if (ddev->atn_count) {
+ ddev->atn_count = 0;
+
+ rc = digital_tg_send_saved_skb(ddev);
+ if (rc)
+ goto exit;
+
+ return;
+ }
+
+ kfree_skb(ddev->saved_skb);
+ ddev->saved_skb = NULL;
+
+ rc = digital_tg_send_dep_res(ddev, ddev->chaining_skb);
+ if (rc)
+ goto exit;
+ } else { /* NACK */
+ if ((DIGITAL_NFC_DEP_PFB_PNI(pfb + 1) !=
+ ddev->curr_nfc_dep_pni) ||
+ !ddev->saved_skb) {
+ rc = -EIO;
+ goto exit;
+ }
+
+ ddev->atn_count = 0;
+
+ rc = digital_tg_send_saved_skb(ddev);
+ if (rc) {
+ kfree_skb(ddev->saved_skb);
+ goto exit;
+ }
+ }
+
+ return;
+ case DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU:
+ if (DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb)) {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ rc = digital_tg_send_atn(ddev);
+ if (rc)
+ goto exit;
+
+ ddev->atn_count++;
+
+ kfree_skb(resp);
+ return;
+ }
+
rc = nfc_tm_data_received(ddev->nfc_dev, resp);
exit:
+ kfree_skb(ddev->chaining_skb);
+ ddev->chaining_skb = NULL;
+
+ ddev->atn_count = 0;
+
+ kfree_skb(ddev->saved_skb);
+ ddev->saved_skb = NULL;
+
if (rc)
kfree_skb(resp);
}
@@ -576,20 +1204,54 @@
int digital_tg_send_dep_res(struct nfc_digital_dev *ddev, struct sk_buff *skb)
{
struct digital_dep_req_res *dep_res;
+ struct sk_buff *chaining_skb, *tmp_skb;
+ int rc;
skb_push(skb, sizeof(struct digital_dep_req_res));
+
dep_res = (struct digital_dep_req_res *)skb->data;
dep_res->dir = DIGITAL_NFC_DEP_FRAME_DIR_IN;
dep_res->cmd = DIGITAL_CMD_DEP_RES;
dep_res->pfb = ddev->curr_nfc_dep_pni;
- digital_skb_push_dep_sod(ddev, skb);
+ if (ddev->did) {
+ dep_res->pfb |= DIGITAL_NFC_DEP_PFB_DID_BIT;
- ddev->skb_add_crc(skb);
+ memcpy(skb_put(skb, sizeof(ddev->did)), &ddev->did,
+ sizeof(ddev->did));
+ }
- return digital_tg_send_cmd(ddev, skb, 1500, digital_tg_recv_dep_req,
- NULL);
+ ddev->curr_nfc_dep_pni =
+ DIGITAL_NFC_DEP_PFB_PNI(ddev->curr_nfc_dep_pni + 1);
+
+ chaining_skb = ddev->chaining_skb;
+
+ tmp_skb = digital_send_dep_data_prep(ddev, skb, dep_res, NULL);
+ if (IS_ERR(tmp_skb))
+ return PTR_ERR(tmp_skb);
+
+ digital_skb_push_dep_sod(ddev, tmp_skb);
+
+ ddev->skb_add_crc(tmp_skb);
+
+ ddev->saved_skb = skb_get(tmp_skb);
+ ddev->saved_skb_len = tmp_skb->len;
+
+ rc = digital_tg_send_cmd(ddev, tmp_skb, 1500, digital_tg_recv_dep_req,
+ NULL);
+ if (rc) {
+ if (tmp_skb != skb)
+ kfree_skb(tmp_skb);
+
+ kfree_skb(chaining_skb);
+ ddev->chaining_skb = NULL;
+
+ kfree_skb(ddev->saved_skb);
+ ddev->saved_skb = NULL;
+ }
+
+ return rc;
}
static void digital_tg_send_psl_res_complete(struct nfc_digital_dev *ddev,
@@ -632,9 +1294,10 @@
ddev->skb_add_crc(skb);
+ ddev->curr_nfc_dep_pni = 0;
+
rc = digital_tg_send_cmd(ddev, skb, 0, digital_tg_send_psl_res_complete,
(void *)(unsigned long)rf_tech);
-
if (rc)
kfree_skb(skb);
@@ -647,7 +1310,7 @@
int rc;
struct digital_psl_req *psl_req;
u8 rf_tech;
- u8 dsi;
+ u8 dsi, payload_size, payload_bits;
if (IS_ERR(resp)) {
rc = PTR_ERR(resp);
@@ -692,6 +1355,18 @@
goto exit;
}
+ payload_bits = DIGITAL_PAYLOAD_FSL_TO_BITS(psl_req->fsl);
+ payload_size = digital_payload_bits_to_size(payload_bits);
+
+ if (!payload_size || (payload_size > min(ddev->local_payload_max,
+ ddev->remote_payload_max))) {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ ddev->local_payload_max = payload_size;
+ ddev->remote_payload_max = payload_size;
+
rc = digital_tg_send_psl_res(ddev, psl_req->did, rf_tech);
exit:
@@ -712,6 +1387,8 @@
if (resp->data[0] == DIGITAL_NFC_DEP_NFCA_SOD_SB)
offset++;
+ ddev->atn_count = 0;
+
if (resp->data[offset] == DIGITAL_CMD_PSL_REQ)
digital_tg_recv_psl_req(ddev, arg, resp);
else
@@ -723,7 +1400,7 @@
{
struct digital_atr_res *atr_res;
struct sk_buff *skb;
- u8 *gb;
+ u8 *gb, payload_bits;
size_t gb_len;
int rc;
@@ -744,7 +1421,11 @@
atr_res->cmd = DIGITAL_CMD_ATR_RES;
memcpy(atr_res->nfcid3, atr_req->nfcid3, sizeof(atr_req->nfcid3));
atr_res->to = 8;
- atr_res->pp = DIGITAL_LR_BITS_PAYLOAD_SIZE_254B;
+
+ ddev->local_payload_max = DIGITAL_PAYLOAD_SIZE_MAX;
+ payload_bits = digital_payload_size_to_bits(ddev->local_payload_max);
+ atr_res->pp = DIGITAL_PAYLOAD_BITS_TO_PP(payload_bits);
+
if (gb_len) {
skb_put(skb, gb_len);
@@ -756,12 +1437,12 @@
ddev->skb_add_crc(skb);
+ ddev->curr_nfc_dep_pni = 0;
+
rc = digital_tg_send_cmd(ddev, skb, 999,
digital_tg_send_atr_res_complete, NULL);
- if (rc) {
+ if (rc)
kfree_skb(skb);
- return rc;
- }
return rc;
}
@@ -772,7 +1453,7 @@
int rc;
struct digital_atr_req *atr_req;
size_t gb_len, min_size;
- u8 poll_tech_count;
+ u8 poll_tech_count, payload_bits;
if (IS_ERR(resp)) {
rc = PTR_ERR(resp);
@@ -815,11 +1496,22 @@
atr_req = (struct digital_atr_req *)resp->data;
if (atr_req->dir != DIGITAL_NFC_DEP_FRAME_DIR_OUT ||
- atr_req->cmd != DIGITAL_CMD_ATR_REQ) {
+ atr_req->cmd != DIGITAL_CMD_ATR_REQ ||
+ atr_req->did > DIGITAL_DID_MAX) {
rc = -EINVAL;
goto exit;
}
+ payload_bits = DIGITAL_PAYLOAD_PP_TO_BITS(atr_req->pp);
+ ddev->remote_payload_max = digital_payload_bits_to_size(payload_bits);
+
+ if (!ddev->remote_payload_max) {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ ddev->did = atr_req->did;
+
rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING,
NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED);
if (rc)
diff --git a/net/nfc/hci/command.c b/net/nfc/hci/command.c
index 677d24b..91df487 100644
--- a/net/nfc/hci/command.c
+++ b/net/nfc/hci/command.c
@@ -345,6 +345,9 @@
pr_debug("\n");
+ if (hdev->gate2pipe[dest_gate] == NFC_HCI_DO_NOT_CREATE_PIPE)
+ return 0;
+
if (hdev->gate2pipe[dest_gate] != NFC_HCI_INVALID_PIPE)
return -EADDRINUSE;
diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c
index 1177082..ef50e77 100644
--- a/net/nfc/hci/core.c
+++ b/net/nfc/hci/core.c
@@ -167,6 +167,48 @@
void nfc_hci_cmd_received(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
struct sk_buff *skb)
{
+ int r = 0;
+ u8 gate = nfc_hci_pipe2gate(hdev, pipe);
+ u8 local_gate, new_pipe;
+ u8 gate_opened = 0x00;
+
+ pr_debug("from gate %x pipe %x cmd %x\n", gate, pipe, cmd);
+
+ switch (cmd) {
+ case NFC_HCI_ADM_NOTIFY_PIPE_CREATED:
+ if (skb->len != 5) {
+ r = -EPROTO;
+ break;
+ }
+
+ local_gate = skb->data[3];
+ new_pipe = skb->data[4];
+ nfc_hci_send_response(hdev, gate, NFC_HCI_ANY_OK, NULL, 0);
+
+ /* save the new created pipe and bind with local gate,
+ * the description for skb->data[3] is destination gate id
+ * but since we received this cmd from host controller, we
+ * are the destination and it is our local gate
+ */
+ hdev->gate2pipe[local_gate] = new_pipe;
+ break;
+ case NFC_HCI_ANY_OPEN_PIPE:
+ /* if the pipe is already created, we allow remote host to
+ * open it
+ */
+ if (gate != 0xff)
+ nfc_hci_send_response(hdev, gate, NFC_HCI_ANY_OK,
+ &gate_opened, 1);
+ break;
+ case NFC_HCI_ADM_NOTIFY_ALL_PIPE_CLEARED:
+ nfc_hci_send_response(hdev, gate, NFC_HCI_ANY_OK, NULL, 0);
+ break;
+ default:
+ pr_info("Discarded unknown cmd %x to gate %x\n", cmd, gate);
+ r = -EINVAL;
+ break;
+ }
+
kfree_skb(skb);
}
@@ -717,6 +759,19 @@
return 0;
}
+static int hci_se_io(struct nfc_dev *nfc_dev, u32 se_idx,
+ u8 *apdu, size_t apdu_length,
+ se_io_cb_t cb, void *cb_context)
+{
+ struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
+
+ if (hdev->ops->se_io)
+ return hdev->ops->se_io(hdev, se_idx, apdu,
+ apdu_length, cb, cb_context);
+
+ return 0;
+}
+
static void nfc_hci_failure(struct nfc_hci_dev *hdev, int err)
{
mutex_lock(&hdev->msg_tx_mutex);
@@ -830,6 +885,7 @@
.discover_se = hci_discover_se,
.enable_se = hci_enable_se,
.disable_se = hci_disable_se,
+ .se_io = hci_se_io,
};
struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
diff --git a/net/nfc/llcp_commands.c b/net/nfc/llcp_commands.c
index a3ad69a..3621a90 100644
--- a/net/nfc/llcp_commands.c
+++ b/net/nfc/llcp_commands.c
@@ -401,7 +401,8 @@
u8 *miux_tlv = NULL, miux_tlv_length;
u8 *rw_tlv = NULL, rw_tlv_length, rw;
int err;
- u16 size = 0, miux;
+ u16 size = 0;
+ __be16 miux;
pr_debug("Sending CONNECT\n");
@@ -465,7 +466,8 @@
u8 *miux_tlv = NULL, miux_tlv_length;
u8 *rw_tlv = NULL, rw_tlv_length, rw;
int err;
- u16 size = 0, miux;
+ u16 size = 0;
+ __be16 miux;
pr_debug("Sending CC\n");
@@ -665,7 +667,7 @@
if (msg_data == NULL)
return -ENOMEM;
- if (memcpy_fromiovec(msg_data, msg->msg_iov, len)) {
+ if (memcpy_from_msg(msg_data, msg, len)) {
kfree(msg_data);
return -EFAULT;
}
@@ -731,7 +733,7 @@
if (msg_data == NULL)
return -ENOMEM;
- if (memcpy_fromiovec(msg_data, msg->msg_iov, len)) {
+ if (memcpy_from_msg(msg_data, msg, len)) {
kfree(msg_data);
return -EFAULT;
}
diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c
index 5d9a9d8..ecf84de 100644
--- a/net/nfc/llcp_core.c
+++ b/net/nfc/llcp_core.c
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2011 Intel Corporation. All rights reserved.
+ * Copyright (C) 2014 Marvell International 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
@@ -1515,8 +1516,10 @@
struct nfc_llcp_local *local;
local = nfc_llcp_find_local(dev);
- if (local == NULL)
+ if (local == NULL) {
+ kfree_skb(skb);
return -ENODEV;
+ }
__nfc_llcp_recv(local, skb);
diff --git a/net/nfc/llcp_sock.c b/net/nfc/llcp_sock.c
index 51f077a..e181e29 100644
--- a/net/nfc/llcp_sock.c
+++ b/net/nfc/llcp_sock.c
@@ -524,13 +524,13 @@
static inline unsigned int llcp_accept_poll(struct sock *parent)
{
- struct nfc_llcp_sock *llcp_sock, *n, *parent_sock;
+ struct nfc_llcp_sock *llcp_sock, *parent_sock;
struct sock *sk;
parent_sock = nfc_llcp_sock(parent);
- list_for_each_entry_safe(llcp_sock, n, &parent_sock->accept_queue,
- accept_queue) {
+ list_for_each_entry(llcp_sock, &parent_sock->accept_queue,
+ accept_queue) {
sk = &llcp_sock->sk;
if (sk->sk_state == LLCP_CONNECTED)
@@ -832,7 +832,7 @@
copied = min_t(unsigned int, rlen, len);
cskb = skb;
- if (skb_copy_datagram_iovec(cskb, 0, msg->msg_iov, copied)) {
+ if (skb_copy_datagram_msg(cskb, 0, msg, copied)) {
if (!(flags & MSG_PEEK))
skb_queue_head(&sk->sk_receive_queue, skb);
return -EFAULT;
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index 90b16cb..51feb5e 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -3,6 +3,7 @@
* NFC Controller (NFCC) and a Device Host (DH).
*
* Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2014 Marvell International Ltd.
*
* Written by Ilan Elias <ilane@ti.com>
*
@@ -196,18 +197,24 @@
nci_send_cmd(ndev, NCI_OP_CORE_SET_CONFIG_CMD, (3 + param->len), &cmd);
}
+struct nci_rf_discover_param {
+ __u32 im_protocols;
+ __u32 tm_protocols;
+};
+
static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt)
{
+ struct nci_rf_discover_param *param =
+ (struct nci_rf_discover_param *)opt;
struct nci_rf_disc_cmd cmd;
- __u32 protocols = opt;
cmd.num_disc_configs = 0;
if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
- (protocols & NFC_PROTO_JEWEL_MASK ||
- protocols & NFC_PROTO_MIFARE_MASK ||
- protocols & NFC_PROTO_ISO14443_MASK ||
- protocols & NFC_PROTO_NFC_DEP_MASK)) {
+ (param->im_protocols & NFC_PROTO_JEWEL_MASK ||
+ param->im_protocols & NFC_PROTO_MIFARE_MASK ||
+ param->im_protocols & NFC_PROTO_ISO14443_MASK ||
+ param->im_protocols & NFC_PROTO_NFC_DEP_MASK)) {
cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
NCI_NFC_A_PASSIVE_POLL_MODE;
cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
@@ -215,7 +222,7 @@
}
if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
- (protocols & NFC_PROTO_ISO14443_B_MASK)) {
+ (param->im_protocols & NFC_PROTO_ISO14443_B_MASK)) {
cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
NCI_NFC_B_PASSIVE_POLL_MODE;
cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
@@ -223,8 +230,8 @@
}
if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
- (protocols & NFC_PROTO_FELICA_MASK ||
- protocols & NFC_PROTO_NFC_DEP_MASK)) {
+ (param->im_protocols & NFC_PROTO_FELICA_MASK ||
+ param->im_protocols & NFC_PROTO_NFC_DEP_MASK)) {
cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
NCI_NFC_F_PASSIVE_POLL_MODE;
cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
@@ -232,13 +239,25 @@
}
if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
- (protocols & NFC_PROTO_ISO15693_MASK)) {
+ (param->im_protocols & NFC_PROTO_ISO15693_MASK)) {
cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
NCI_NFC_V_PASSIVE_POLL_MODE;
cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
cmd.num_disc_configs++;
}
+ if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS - 1) &&
+ (param->tm_protocols & NFC_PROTO_NFC_DEP_MASK)) {
+ cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
+ NCI_NFC_A_PASSIVE_LISTEN_MODE;
+ cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
+ cmd.num_disc_configs++;
+ cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
+ NCI_NFC_F_PASSIVE_LISTEN_MODE;
+ cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
+ cmd.num_disc_configs++;
+ }
+
nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_CMD,
(1 + (cmd.num_disc_configs * sizeof(struct disc_config))),
&cmd);
@@ -280,7 +299,7 @@
{
struct nci_rf_deactivate_cmd cmd;
- cmd.type = NCI_DEACTIVATE_TYPE_IDLE_MODE;
+ cmd.type = opt;
nci_send_cmd(ndev, NCI_OP_RF_DEACTIVATE_CMD,
sizeof(struct nci_rf_deactivate_cmd), &cmd);
@@ -441,6 +460,7 @@
{
struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
struct nci_set_config_param param;
+ int rc;
param.val = nfc_get_local_general_bytes(nfc_dev, ¶m.len);
if ((param.val == NULL) || (param.len == 0))
@@ -451,14 +471,45 @@
param.id = NCI_PN_ATR_REQ_GEN_BYTES;
+ rc = nci_request(ndev, nci_set_config_req, (unsigned long)¶m,
+ msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
+ if (rc)
+ return rc;
+
+ param.id = NCI_LN_ATR_RES_GEN_BYTES;
+
return nci_request(ndev, nci_set_config_req, (unsigned long)¶m,
msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
}
+static int nci_set_listen_parameters(struct nfc_dev *nfc_dev)
+{
+ struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+ int rc;
+ __u8 val;
+
+ val = NCI_LA_SEL_INFO_NFC_DEP_MASK;
+
+ rc = nci_set_config(ndev, NCI_LA_SEL_INFO, 1, &val);
+ if (rc)
+ return rc;
+
+ val = NCI_LF_PROTOCOL_TYPE_NFC_DEP_MASK;
+
+ rc = nci_set_config(ndev, NCI_LF_PROTOCOL_TYPE, 1, &val);
+ if (rc)
+ return rc;
+
+ val = NCI_LF_CON_BITR_F_212 | NCI_LF_CON_BITR_F_424;
+
+ return nci_set_config(ndev, NCI_LF_CON_BITR_F, 1, &val);
+}
+
static int nci_start_poll(struct nfc_dev *nfc_dev,
__u32 im_protocols, __u32 tm_protocols)
{
struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+ struct nci_rf_discover_param param;
int rc;
if ((atomic_read(&ndev->state) == NCI_DISCOVERY) ||
@@ -476,13 +527,14 @@
(atomic_read(&ndev->state) == NCI_POLL_ACTIVE)) {
pr_debug("target active or w4 select, implicitly deactivate\n");
- rc = nci_request(ndev, nci_rf_deactivate_req, 0,
+ rc = nci_request(ndev, nci_rf_deactivate_req,
+ NCI_DEACTIVATE_TYPE_IDLE_MODE,
msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
if (rc)
return -EBUSY;
}
- if (im_protocols & NFC_PROTO_NFC_DEP_MASK) {
+ if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) {
rc = nci_set_local_general_bytes(nfc_dev);
if (rc) {
pr_err("failed to set local general bytes\n");
@@ -490,7 +542,15 @@
}
}
- rc = nci_request(ndev, nci_rf_discover_req, im_protocols,
+ if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) {
+ rc = nci_set_listen_parameters(nfc_dev);
+ if (rc)
+ pr_err("failed to set listen parameters\n");
+ }
+
+ param.im_protocols = im_protocols;
+ param.tm_protocols = tm_protocols;
+ rc = nci_request(ndev, nci_rf_discover_req, (unsigned long)¶m,
msecs_to_jiffies(NCI_RF_DISC_TIMEOUT));
if (!rc)
@@ -509,7 +569,7 @@
return;
}
- nci_request(ndev, nci_rf_deactivate_req, 0,
+ nci_request(ndev, nci_rf_deactivate_req, NCI_DEACTIVATE_TYPE_IDLE_MODE,
msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
}
@@ -594,7 +654,8 @@
ndev->target_active_prot = 0;
if (atomic_read(&ndev->state) == NCI_POLL_ACTIVE) {
- nci_request(ndev, nci_rf_deactivate_req, 0,
+ nci_request(ndev, nci_rf_deactivate_req,
+ NCI_DEACTIVATE_TYPE_SLEEP_MODE,
msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
}
}
@@ -622,9 +683,24 @@
static int nci_dep_link_down(struct nfc_dev *nfc_dev)
{
+ struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+ int rc;
+
pr_debug("entry\n");
- nci_deactivate_target(nfc_dev, NULL);
+ if (nfc_dev->rf_mode == NFC_RF_INITIATOR) {
+ nci_deactivate_target(nfc_dev, NULL);
+ } else {
+ if (atomic_read(&ndev->state) == NCI_LISTEN_ACTIVE ||
+ atomic_read(&ndev->state) == NCI_DISCOVERY) {
+ nci_request(ndev, nci_rf_deactivate_req, 0,
+ msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
+ }
+
+ rc = nfc_tm_deactivated(nfc_dev);
+ if (rc)
+ pr_err("error when signaling tm deactivation\n");
+ }
return 0;
}
@@ -658,18 +734,58 @@
return rc;
}
+static int nci_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
+{
+ struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+ int rc;
+
+ rc = nci_send_data(ndev, NCI_STATIC_RF_CONN_ID, skb);
+ if (rc)
+ pr_err("unable to send data\n");
+
+ return rc;
+}
+
static int nci_enable_se(struct nfc_dev *nfc_dev, u32 se_idx)
{
+ struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+
+ if (ndev->ops->enable_se)
+ return ndev->ops->enable_se(ndev, se_idx);
+
return 0;
}
static int nci_disable_se(struct nfc_dev *nfc_dev, u32 se_idx)
{
+ struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+
+ if (ndev->ops->disable_se)
+ return ndev->ops->disable_se(ndev, se_idx);
+
return 0;
}
static int nci_discover_se(struct nfc_dev *nfc_dev)
{
+ struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+
+ if (ndev->ops->discover_se)
+ return ndev->ops->discover_se(ndev);
+
+ return 0;
+}
+
+static int nci_se_io(struct nfc_dev *nfc_dev, u32 se_idx,
+ u8 *apdu, size_t apdu_length,
+ se_io_cb_t cb, void *cb_context)
+{
+ struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+
+ if (ndev->ops->se_io)
+ return ndev->ops->se_io(ndev, se_idx, apdu,
+ apdu_length, cb, cb_context);
+
return 0;
}
@@ -683,9 +799,11 @@
.activate_target = nci_activate_target,
.deactivate_target = nci_deactivate_target,
.im_transceive = nci_transceive,
+ .tm_send = nci_tm_send,
.enable_se = nci_enable_se,
.disable_se = nci_disable_se,
.discover_se = nci_discover_se,
+ .se_io = nci_se_io,
};
/* ---- Interface to NCI drivers ---- */
diff --git a/net/nfc/nci/data.c b/net/nfc/nci/data.c
index 427ef2c..a2de2a8 100644
--- a/net/nfc/nci/data.c
+++ b/net/nfc/nci/data.c
@@ -3,6 +3,7 @@
* NFC Controller (NFCC) and a Device Host (DH).
*
* Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2014 Marvell International Ltd.
*
* Written by Ilan Elias <ilane@ti.com>
*
@@ -184,11 +185,16 @@
static void nci_add_rx_data_frag(struct nci_dev *ndev,
struct sk_buff *skb,
- __u8 pbf)
+ __u8 pbf, __u8 status)
{
int reassembly_len;
int err = 0;
+ if (status) {
+ err = status;
+ goto exit;
+ }
+
if (ndev->rx_data_reassembly) {
reassembly_len = ndev->rx_data_reassembly->len;
@@ -223,13 +229,24 @@
}
exit:
- nci_data_exchange_complete(ndev, skb, err);
+ if (ndev->nfc_dev->rf_mode == NFC_RF_INITIATOR) {
+ nci_data_exchange_complete(ndev, skb, err);
+ } else if (ndev->nfc_dev->rf_mode == NFC_RF_TARGET) {
+ /* Data received in Target mode, forward to nfc core */
+ err = nfc_tm_data_received(ndev->nfc_dev, skb);
+ if (err)
+ pr_err("unable to handle received data\n");
+ } else {
+ pr_err("rf mode unknown\n");
+ kfree_skb(skb);
+ }
}
/* Rx Data packet */
void nci_rx_data_packet(struct nci_dev *ndev, struct sk_buff *skb)
{
__u8 pbf = nci_pbf(skb->data);
+ __u8 status = 0;
pr_debug("len %d\n", skb->len);
@@ -247,8 +264,9 @@
ndev->target_active_prot == NFC_PROTO_ISO15693) {
/* frame I/F => remove the status byte */
pr_debug("frame I/F => remove the status byte\n");
+ status = skb->data[skb->len - 1];
skb_trim(skb, (skb->len - 1));
}
- nci_add_rx_data_frag(ndev, skb, pbf);
+ nci_add_rx_data_frag(ndev, skb, pbf, nci_to_errno(status));
}
diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c
index 205b35f..22e453c 100644
--- a/net/nfc/nci/ntf.c
+++ b/net/nfc/nci/ntf.c
@@ -103,7 +103,7 @@
struct rf_tech_specific_params_nfca_poll *nfca_poll,
__u8 *data)
{
- nfca_poll->sens_res = __le16_to_cpu(*((__u16 *)data));
+ nfca_poll->sens_res = __le16_to_cpu(*((__le16 *)data));
data += 2;
nfca_poll->nfcid1_len = min_t(__u8, *data++, NFC_NFCID1_MAXSIZE);
@@ -167,7 +167,19 @@
return data;
}
-__u32 nci_get_prop_rf_protocol(struct nci_dev *ndev, __u8 rf_protocol)
+static __u8 *nci_extract_rf_params_nfcf_passive_listen(struct nci_dev *ndev,
+ struct rf_tech_specific_params_nfcf_listen *nfcf_listen,
+ __u8 *data)
+{
+ nfcf_listen->local_nfcid2_len = min_t(__u8, *data++,
+ NFC_NFCID2_MAXSIZE);
+ memcpy(nfcf_listen->local_nfcid2, data, nfcf_listen->local_nfcid2_len);
+ data += nfcf_listen->local_nfcid2_len;
+
+ return data;
+}
+
+static __u32 nci_get_prop_rf_protocol(struct nci_dev *ndev, __u8 rf_protocol)
{
if (ndev->ops->get_rfprotocol)
return ndev->ops->get_rfprotocol(ndev, rf_protocol);
@@ -401,17 +413,29 @@
struct nci_rf_intf_activated_ntf *ntf, __u8 *data)
{
struct activation_params_poll_nfc_dep *poll;
+ struct activation_params_listen_nfc_dep *listen;
switch (ntf->activation_rf_tech_and_mode) {
case NCI_NFC_A_PASSIVE_POLL_MODE:
case NCI_NFC_F_PASSIVE_POLL_MODE:
poll = &ntf->activation_params.poll_nfc_dep;
- poll->atr_res_len = min_t(__u8, *data++, 63);
+ poll->atr_res_len = min_t(__u8, *data++,
+ NFC_ATR_RES_MAXSIZE - 2);
pr_debug("atr_res_len %d\n", poll->atr_res_len);
if (poll->atr_res_len > 0)
memcpy(poll->atr_res, data, poll->atr_res_len);
break;
+ case NCI_NFC_A_PASSIVE_LISTEN_MODE:
+ case NCI_NFC_F_PASSIVE_LISTEN_MODE:
+ listen = &ntf->activation_params.listen_nfc_dep;
+ listen->atr_req_len = min_t(__u8, *data++,
+ NFC_ATR_REQ_MAXSIZE - 2);
+ pr_debug("atr_req_len %d\n", listen->atr_req_len);
+ if (listen->atr_req_len > 0)
+ memcpy(listen->atr_req, data, listen->atr_req_len);
+ break;
+
default:
pr_err("unsupported activation_rf_tech_and_mode 0x%x\n",
ntf->activation_rf_tech_and_mode);
@@ -444,6 +468,48 @@
nfc_targets_found(ndev->nfc_dev, ndev->targets, ndev->n_targets);
}
+static int nci_store_general_bytes_nfc_dep(struct nci_dev *ndev,
+ struct nci_rf_intf_activated_ntf *ntf)
+{
+ ndev->remote_gb_len = 0;
+
+ if (ntf->activation_params_len <= 0)
+ return NCI_STATUS_OK;
+
+ switch (ntf->activation_rf_tech_and_mode) {
+ case NCI_NFC_A_PASSIVE_POLL_MODE:
+ case NCI_NFC_F_PASSIVE_POLL_MODE:
+ ndev->remote_gb_len = min_t(__u8,
+ (ntf->activation_params.poll_nfc_dep.atr_res_len
+ - NFC_ATR_RES_GT_OFFSET),
+ NFC_ATR_RES_GB_MAXSIZE);
+ memcpy(ndev->remote_gb,
+ (ntf->activation_params.poll_nfc_dep.atr_res
+ + NFC_ATR_RES_GT_OFFSET),
+ ndev->remote_gb_len);
+ break;
+
+ case NCI_NFC_A_PASSIVE_LISTEN_MODE:
+ case NCI_NFC_F_PASSIVE_LISTEN_MODE:
+ ndev->remote_gb_len = min_t(__u8,
+ (ntf->activation_params.listen_nfc_dep.atr_req_len
+ - NFC_ATR_REQ_GT_OFFSET),
+ NFC_ATR_REQ_GB_MAXSIZE);
+ memcpy(ndev->remote_gb,
+ (ntf->activation_params.listen_nfc_dep.atr_req
+ + NFC_ATR_REQ_GT_OFFSET),
+ ndev->remote_gb_len);
+ break;
+
+ default:
+ pr_err("unsupported activation_rf_tech_and_mode 0x%x\n",
+ ntf->activation_rf_tech_and_mode);
+ return NCI_STATUS_RF_PROTOCOL_ERROR;
+ }
+
+ return NCI_STATUS_OK;
+}
+
static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev,
struct sk_buff *skb)
{
@@ -493,6 +559,16 @@
&(ntf.rf_tech_specific_params.nfcv_poll), data);
break;
+ case NCI_NFC_A_PASSIVE_LISTEN_MODE:
+ /* no RF technology specific parameters */
+ break;
+
+ case NCI_NFC_F_PASSIVE_LISTEN_MODE:
+ data = nci_extract_rf_params_nfcf_passive_listen(ndev,
+ &(ntf.rf_tech_specific_params.nfcf_listen),
+ data);
+ break;
+
default:
pr_err("unsupported activation_rf_tech_and_mode 0x%x\n",
ntf.activation_rf_tech_and_mode);
@@ -546,32 +622,39 @@
/* store general bytes to be reported later in dep_link_up */
if (ntf.rf_interface == NCI_RF_INTERFACE_NFC_DEP) {
- ndev->remote_gb_len = 0;
-
- if (ntf.activation_params_len > 0) {
- /* ATR_RES general bytes at offset 15 */
- ndev->remote_gb_len = min_t(__u8,
- (ntf.activation_params
- .poll_nfc_dep.atr_res_len
- - NFC_ATR_RES_GT_OFFSET),
- NFC_MAX_GT_LEN);
- memcpy(ndev->remote_gb,
- (ntf.activation_params.poll_nfc_dep
- .atr_res + NFC_ATR_RES_GT_OFFSET),
- ndev->remote_gb_len);
- }
+ err = nci_store_general_bytes_nfc_dep(ndev, &ntf);
+ if (err != NCI_STATUS_OK)
+ pr_err("unable to store general bytes\n");
}
}
- if (atomic_read(&ndev->state) == NCI_DISCOVERY) {
- /* A single target was found and activated automatically */
- atomic_set(&ndev->state, NCI_POLL_ACTIVE);
- if (err == NCI_STATUS_OK)
- nci_target_auto_activated(ndev, &ntf);
- } else { /* ndev->state == NCI_W4_HOST_SELECT */
- /* A selected target was activated, so complete the request */
- atomic_set(&ndev->state, NCI_POLL_ACTIVE);
- nci_req_complete(ndev, err);
+ if (!(ntf.activation_rf_tech_and_mode & NCI_RF_TECH_MODE_LISTEN_MASK)) {
+ /* Poll mode */
+ if (atomic_read(&ndev->state) == NCI_DISCOVERY) {
+ /* A single target was found and activated
+ * automatically */
+ atomic_set(&ndev->state, NCI_POLL_ACTIVE);
+ if (err == NCI_STATUS_OK)
+ nci_target_auto_activated(ndev, &ntf);
+ } else { /* ndev->state == NCI_W4_HOST_SELECT */
+ /* A selected target was activated, so complete the
+ * request */
+ atomic_set(&ndev->state, NCI_POLL_ACTIVE);
+ nci_req_complete(ndev, err);
+ }
+ } else {
+ /* Listen mode */
+ atomic_set(&ndev->state, NCI_LISTEN_ACTIVE);
+ if (err == NCI_STATUS_OK &&
+ ntf.rf_protocol == NCI_RF_PROTOCOL_NFC_DEP) {
+ err = nfc_tm_activated(ndev->nfc_dev,
+ NFC_PROTO_NFC_DEP_MASK,
+ NFC_COMM_PASSIVE,
+ ndev->remote_gb,
+ ndev->remote_gb_len);
+ if (err != NCI_STATUS_OK)
+ pr_err("error when signaling tm activation\n");
+ }
}
}
@@ -595,8 +678,21 @@
if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags))
nci_data_exchange_complete(ndev, NULL, -EIO);
- nci_clear_target_list(ndev);
- atomic_set(&ndev->state, NCI_IDLE);
+ switch (ntf->type) {
+ case NCI_DEACTIVATE_TYPE_IDLE_MODE:
+ nci_clear_target_list(ndev);
+ atomic_set(&ndev->state, NCI_IDLE);
+ break;
+ case NCI_DEACTIVATE_TYPE_SLEEP_MODE:
+ case NCI_DEACTIVATE_TYPE_SLEEP_AF_MODE:
+ atomic_set(&ndev->state, NCI_W4_HOST_SELECT);
+ break;
+ case NCI_DEACTIVATE_TYPE_DISCOVERY:
+ nci_clear_target_list(ndev);
+ atomic_set(&ndev->state, NCI_DISCOVERY);
+ break;
+ }
+
nci_req_complete(ndev, NCI_STATUS_OK);
}
diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c
index f91cff1..99c7065 100644
--- a/net/nfc/netlink.c
+++ b/net/nfc/netlink.c
@@ -813,6 +813,31 @@
return rc;
}
+static int nfc_genl_activate_target(struct sk_buff *skb, struct genl_info *info)
+{
+ struct nfc_dev *dev;
+ u32 device_idx, target_idx, protocol;
+ int rc;
+
+ if (!info->attrs[NFC_ATTR_DEVICE_INDEX])
+ return -EINVAL;
+
+ device_idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+
+ dev = nfc_get_device(device_idx);
+ if (!dev)
+ return -ENODEV;
+
+ target_idx = nla_get_u32(info->attrs[NFC_ATTR_TARGET_INDEX]);
+ protocol = nla_get_u32(info->attrs[NFC_ATTR_PROTOCOLS]);
+
+ nfc_deactivate_target(dev, target_idx);
+ rc = nfc_activate_target(dev, target_idx, protocol);
+
+ nfc_put_device(dev);
+ return 0;
+}
+
static int nfc_genl_dep_link_up(struct sk_buff *skb, struct genl_info *info)
{
struct nfc_dev *dev;
@@ -1289,6 +1314,51 @@
return 0;
}
+static int nfc_se_io(struct nfc_dev *dev, u32 se_idx,
+ u8 *apdu, size_t apdu_length,
+ se_io_cb_t cb, void *cb_context)
+{
+ struct nfc_se *se;
+ int rc;
+
+ pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx);
+
+ device_lock(&dev->dev);
+
+ if (!device_is_registered(&dev->dev)) {
+ rc = -ENODEV;
+ goto error;
+ }
+
+ if (!dev->dev_up) {
+ rc = -ENODEV;
+ goto error;
+ }
+
+ if (!dev->ops->se_io) {
+ rc = -EOPNOTSUPP;
+ goto error;
+ }
+
+ se = nfc_find_se(dev, se_idx);
+ if (!se) {
+ rc = -EINVAL;
+ goto error;
+ }
+
+ if (se->state != NFC_SE_ENABLED) {
+ rc = -ENODEV;
+ goto error;
+ }
+
+ rc = dev->ops->se_io(dev, se_idx, apdu,
+ apdu_length, cb, cb_context);
+
+error:
+ device_unlock(&dev->dev);
+ return rc;
+}
+
struct se_io_ctx {
u32 dev_idx;
u32 se_idx;
@@ -1371,7 +1441,7 @@
ctx->dev_idx = dev_idx;
ctx->se_idx = se_idx;
- return dev->ops->se_io(dev, se_idx, apdu, apdu_len, se_io_cb, ctx);
+ return nfc_se_io(dev, se_idx, apdu, apdu_len, se_io_cb, ctx);
}
static __genl_const struct genl_ops nfc_genl_ops[] = {
@@ -1459,6 +1529,11 @@
.doit = nfc_genl_se_io,
.policy = nfc_genl_policy,
},
+ {
+ .cmd = NFC_CMD_ACTIVATE_TARGET,
+ .doit = nfc_genl_activate_target,
+ .policy = nfc_genl_policy,
+ },
};
diff --git a/net/nfc/rawsock.c b/net/nfc/rawsock.c
index 11c3544..373e138 100644
--- a/net/nfc/rawsock.c
+++ b/net/nfc/rawsock.c
@@ -231,7 +231,7 @@
if (skb == NULL)
return rc;
- rc = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
+ rc = memcpy_from_msg(skb_put(skb, len), msg, len);
if (rc < 0) {
kfree_skb(skb);
return rc;
@@ -269,7 +269,7 @@
copied = len;
}
- rc = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ rc = skb_copy_datagram_msg(skb, 0, msg, copied);
skb_free_datagram(sk, skb);
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig
index b292436..bc4e435 100644
--- a/net/wireless/Kconfig
+++ b/net/wireless/Kconfig
@@ -158,7 +158,7 @@
Most distributions have a CRDA package. So if unsure, say N.
config CFG80211_WEXT
- bool "cfg80211 wireless extensions compatibility"
+ bool
depends on CFG80211
depends on WEXT_CORE
help
diff --git a/net/wireless/Makefile b/net/wireless/Makefile
index 7730221..bbc27ab 100644
--- a/net/wireless/Makefile
+++ b/net/wireless/Makefile
@@ -10,7 +10,7 @@
obj-$(CONFIG_WEXT_PRIV) += wext-priv.o
cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o
-cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o
+cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o ocb.o
cfg80211-$(CPTCFG_CFG80211_DEBUGFS) += debugfs.o
cfg80211-$(CPTCFG_CFG80211_WEXT) += wext-compat.o wext-sme.o
cfg80211-$(CPTCFG_CFG80211_INTERNAL_REGDB) += regdb.o
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index 8f21d4e..7f9fa4d 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -115,7 +115,7 @@
EXPORT_SYMBOL(cfg80211_chandef_valid);
static void chandef_primary_freqs(const struct cfg80211_chan_def *c,
- int *pri40, int *pri80)
+ u32 *pri40, u32 *pri80)
{
int tmp;
@@ -366,6 +366,7 @@
break;
case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_OCB:
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_AP_VLAN:
@@ -602,7 +603,7 @@
{
struct ieee80211_sta_ht_cap *ht_cap;
struct ieee80211_sta_vht_cap *vht_cap;
- u32 width, control_freq;
+ u32 width, control_freq, cap;
if (WARN_ON(!cfg80211_chandef_valid(chandef)))
return false;
@@ -642,7 +643,8 @@
return false;
break;
case NL80211_CHAN_WIDTH_80P80:
- if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))
+ cap = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
+ if (cap != IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)
return false;
case NL80211_CHAN_WIDTH_80:
if (!vht_cap->vht_supported)
@@ -653,7 +655,9 @@
case NL80211_CHAN_WIDTH_160:
if (!vht_cap->vht_supported)
return false;
- if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ))
+ cap = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
+ if (cap != IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ &&
+ cap != IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)
return false;
prohibited_flags |= IEEE80211_CHAN_NO_160MHZ;
width = 160;
@@ -892,6 +896,13 @@
*radar_detect |= BIT(wdev->chandef.width);
}
return;
+ case NL80211_IFTYPE_OCB:
+ if (wdev->chandef.chan) {
+ *chan = wdev->chandef.chan;
+ *chanmode = CHAN_MODE_SHARED;
+ return;
+ }
+ break;
case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_WDS:
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 45d0435..073bfe4 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -86,11 +86,11 @@
return &rdev->wiphy;
}
-int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
- char *newname)
+static int cfg80211_dev_check_name(struct cfg80211_registered_device *rdev,
+ const char *newname)
{
struct cfg80211_registered_device *rdev2;
- int wiphy_idx, taken = -1, result, digits;
+ int wiphy_idx, taken = -1, digits;
ASSERT_RTNL();
@@ -109,16 +109,29 @@
return -EINVAL;
}
-
- /* Ignore nop renames */
- if (strcmp(newname, dev_name(&rdev->wiphy.dev)) == 0)
- return 0;
-
/* Ensure another device does not already have this name. */
list_for_each_entry(rdev2, &cfg80211_rdev_list, list)
- if (strcmp(newname, dev_name(&rdev2->wiphy.dev)) == 0)
+ if (strcmp(newname, wiphy_name(&rdev2->wiphy)) == 0)
return -EINVAL;
+ return 0;
+}
+
+int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
+ char *newname)
+{
+ int result;
+
+ ASSERT_RTNL();
+
+ /* Ignore nop renames */
+ if (strcmp(newname, wiphy_name(&rdev->wiphy)) == 0)
+ return 0;
+
+ result = cfg80211_dev_check_name(rdev, newname);
+ if (result < 0)
+ return result;
+
result = device_rename(&rdev->wiphy.dev, newname);
if (result)
return result;
@@ -309,7 +322,8 @@
/* exported functions */
-struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
+struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
+ const char *requested_name)
{
static atomic_t wiphy_counter = ATOMIC_INIT(0);
@@ -357,7 +371,31 @@
rdev->wiphy_idx--;
/* give it a proper name */
- dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx);
+ if (requested_name && requested_name[0]) {
+ int rv;
+
+ rtnl_lock();
+ rv = cfg80211_dev_check_name(rdev, requested_name);
+
+ if (rv < 0) {
+ rtnl_unlock();
+ goto use_default_name;
+ }
+
+ rv = dev_set_name(&rdev->wiphy.dev, "%s", requested_name);
+ rtnl_unlock();
+ if (rv)
+ goto use_default_name;
+ } else {
+use_default_name:
+ /* NOTE: This is *probably* safe w/out holding rtnl because of
+ * the restrictions on phy names. Probably this call could
+ * fail if some other part of the kernel (re)named a device
+ * phyX. But, might should add some locking and check return
+ * value, and use a different name if this one exists?
+ */
+ dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx);
+ }
INIT_LIST_HEAD(&rdev->wdev_list);
INIT_LIST_HEAD(&rdev->beacon_registrations);
@@ -413,7 +451,7 @@
return &rdev->wiphy;
}
-EXPORT_SYMBOL(wiphy_new);
+EXPORT_SYMBOL(wiphy_new_nm);
static int wiphy_verify_combinations(struct wiphy *wiphy)
{
@@ -510,6 +548,24 @@
!wiphy->wowlan->tcp))
return -EINVAL;
#endif
+ if (WARN_ON((wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) &&
+ (!rdev->ops->tdls_channel_switch ||
+ !rdev->ops->tdls_cancel_channel_switch)))
+ return -EINVAL;
+
+ /*
+ * if a wiphy has unsupported modes for regulatory channel enforcement,
+ * opt-out of enforcement checking
+ */
+ if (wiphy->interface_modes & ~(BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_AP) |
+ BIT(NL80211_IFTYPE_P2P_GO) |
+ BIT(NL80211_IFTYPE_ADHOC) |
+ BIT(NL80211_IFTYPE_P2P_DEVICE) |
+ BIT(NL80211_IFTYPE_AP_VLAN) |
+ BIT(NL80211_IFTYPE_MONITOR)))
+ wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF;
if (WARN_ON(wiphy->coalesce &&
(!wiphy->coalesce->n_rules ||
@@ -838,7 +894,22 @@
case NL80211_IFTYPE_P2P_GO:
__cfg80211_stop_ap(rdev, dev, true);
break;
- default:
+ case NL80211_IFTYPE_OCB:
+ __cfg80211_leave_ocb(rdev, dev);
+ break;
+ case NL80211_IFTYPE_WDS:
+ /* must be handled by mac80211/driver, has no APIs */
+ break;
+ case NL80211_IFTYPE_P2P_DEVICE:
+ /* cannot happen, has no netdev */
+ break;
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_MONITOR:
+ /* nothing to do */
+ break;
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case NUM_NL80211_IFTYPES:
+ /* invalid */
break;
}
}
diff --git a/net/wireless/core.h b/net/wireless/core.h
index b27f044..df247e3 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -111,6 +111,7 @@
rdev->wiphy.wowlan_config->tcp->sock)
sock_release(rdev->wiphy.wowlan_config->tcp->sock);
kfree(rdev->wiphy.wowlan_config->tcp);
+ kfree(rdev->wiphy.wowlan_config->nd_config);
kfree(rdev->wiphy.wowlan_config);
#endif
}
@@ -290,6 +291,18 @@
struct wireless_dev *wdev,
struct cfg80211_chan_def *chandef);
+/* OCB */
+int __cfg80211_join_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ocb_setup *setup);
+int cfg80211_join_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ocb_setup *setup);
+int __cfg80211_leave_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev);
+int cfg80211_leave_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev);
+
/* AP */
int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
struct net_device *dev, bool notify);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index f84c236..b800567 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -209,7 +209,7 @@
}
/* policy for the attributes */
-static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
+static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
[NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
.len = 20-1 },
@@ -388,13 +388,14 @@
[NL80211_ATTR_MAC_HINT] = { .len = ETH_ALEN },
[NL80211_ATTR_WIPHY_FREQ_HINT] = { .type = NLA_U32 },
[NL80211_ATTR_TDLS_PEER_CAPABILITY] = { .type = NLA_U32 },
- [NL80211_ATTR_IFACE_SOCKET_OWNER] = { .type = NLA_FLAG },
+ [NL80211_ATTR_SOCKET_OWNER] = { .type = NLA_FLAG },
[NL80211_ATTR_CSA_C_OFFSETS_TX] = { .type = NLA_BINARY },
[NL80211_ATTR_USE_RRM] = { .type = NLA_FLAG },
[NL80211_ATTR_TSID] = { .type = NLA_U8 },
[NL80211_ATTR_USER_PRIO] = { .type = NLA_U8 },
[NL80211_ATTR_ADMITTED_TIME] = { .type = NLA_U16 },
[NL80211_ATTR_SMPS_MODE] = { .type = NLA_U8 },
+ [NL80211_ATTR_MAC_MASK] = { .len = ETH_ALEN },
};
/* policy for the key attributes */
@@ -428,6 +429,7 @@
[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_TCP_CONNECTION] = { .type = NLA_NESTED },
+ [NL80211_WOWLAN_TRIG_NET_DETECT] = { .type = NLA_NESTED },
};
static const struct nla_policy
@@ -884,7 +886,12 @@
if (!wdev->current_bss)
return -ENOLINK;
break;
- default:
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case NL80211_IFTYPE_OCB:
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_P2P_DEVICE:
+ case NL80211_IFTYPE_WDS:
+ case NUM_NL80211_IFTYPES:
return -EINVAL;
}
@@ -1083,6 +1090,8 @@
if (large && nl80211_send_wowlan_tcp_caps(rdev, msg))
return -ENOBUFS;
+ /* TODO: send wowlan net detect */
+
nla_nest_end(msg, nl_wowlan);
return 0;
@@ -1514,8 +1523,8 @@
if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)
CMD(channel_switch, CHANNEL_SWITCH);
CMD(set_qos_map, SET_QOS_MAP);
- if (rdev->wiphy.flags &
- WIPHY_FLAG_SUPPORTS_WMM_ADMISSION)
+ if (rdev->wiphy.features &
+ NL80211_FEATURE_SUPPORTS_WMM_ADMISSION)
CMD(add_tx_ts, ADD_TX_TS);
}
/* add into the if now */
@@ -2310,7 +2319,8 @@
static int nl80211_send_chandef(struct sk_buff *msg,
const struct cfg80211_chan_def *chandef)
{
- WARN_ON(!cfg80211_chandef_valid(chandef));
+ if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+ return -EINVAL;
if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
chandef->chan->center_freq))
@@ -2338,12 +2348,16 @@
static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
struct cfg80211_registered_device *rdev,
- struct wireless_dev *wdev)
+ struct wireless_dev *wdev, bool removal)
{
struct net_device *dev = wdev->netdev;
+ u8 cmd = NL80211_CMD_NEW_INTERFACE;
void *hdr;
- hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_INTERFACE);
+ if (removal)
+ cmd = NL80211_CMD_DEL_INTERFACE;
+
+ hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);
if (!hdr)
return -1;
@@ -2410,7 +2424,7 @@
}
if (nl80211_send_iface(skb, NETLINK_CB_PORTID(cb->skb),
cb->nlh->nlmsg_seq, NLM_F_MULTI,
- rdev, wdev) < 0) {
+ rdev, wdev, false) < 0) {
goto out;
}
if_idx++;
@@ -2438,7 +2452,7 @@
return -ENOMEM;
if (nl80211_send_iface(msg, genl_info_snd_portid(info), info->snd_seq, 0,
- rdev, wdev) < 0) {
+ rdev, wdev, false) < 0) {
nlmsg_free(msg);
return -ENOBUFS;
}
@@ -2584,7 +2598,7 @@
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct vif_params params;
struct wireless_dev *wdev;
- struct sk_buff *msg;
+ struct sk_buff *msg, *event;
int err;
enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
u32 flags;
@@ -2607,7 +2621,9 @@
!(rdev->wiphy.interface_modes & (1 << type)))
return -EOPNOTSUPP;
- if (type == NL80211_IFTYPE_P2P_DEVICE && info->attrs[NL80211_ATTR_MAC]) {
+ if ((type == NL80211_IFTYPE_P2P_DEVICE ||
+ rdev->wiphy.features & NL80211_FEATURE_MAC_ON_CREATE) &&
+ info->attrs[NL80211_ATTR_MAC]) {
nla_memcpy(params.macaddr, info->attrs[NL80211_ATTR_MAC],
ETH_ALEN);
if (!is_valid_ether_addr(params.macaddr))
@@ -2636,12 +2652,15 @@
wdev = rdev_add_virtual_intf(rdev,
nla_data(info->attrs[NL80211_ATTR_IFNAME]),
type, err ? NULL : &flags, ¶ms);
- if (IS_ERR(wdev)) {
+ if (WARN_ON(!wdev)) {
+ nlmsg_free(msg);
+ return -EPROTO;
+ } else if (IS_ERR(wdev)) {
nlmsg_free(msg);
return PTR_ERR(wdev);
}
- if (info->attrs[NL80211_ATTR_IFACE_SOCKET_OWNER])
+ if (info->attrs[NL80211_ATTR_SOCKET_OWNER])
wdev->owner_nlportid = genl_info_snd_portid(info);
switch (type) {
@@ -2677,11 +2696,25 @@
}
if (nl80211_send_iface(msg, genl_info_snd_portid(info), info->snd_seq, 0,
- rdev, wdev) < 0) {
+ rdev, wdev, false) < 0) {
nlmsg_free(msg);
return -ENOBUFS;
}
+ event = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (event) {
+ if (nl80211_send_iface(event, 0, 0, 0,
+ rdev, wdev, false) < 0) {
+ nlmsg_free(event);
+ goto out;
+ }
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy),
+ event, 0, NL80211_MCGRP_CONFIG,
+ GFP_KERNEL);
+ }
+
+out:
return genlmsg_reply(msg, info);
}
@@ -2689,10 +2722,18 @@
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct wireless_dev *wdev = info->user_ptr[1];
+ struct sk_buff *msg;
+ int status;
if (!rdev->ops->del_virtual_intf)
return -EOPNOTSUPP;
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (msg && nl80211_send_iface(msg, 0, 0, 0, rdev, wdev, true) < 0) {
+ nlmsg_free(msg);
+ msg = NULL;
+ }
+
/*
* If we remove a wireless device without a netdev then clear
* user_ptr[1] so that nl80211_post_doit won't dereference it
@@ -2703,7 +2744,15 @@
if (!wdev->netdev)
info->user_ptr[1] = NULL;
- return rdev_del_virtual_intf(rdev, wdev);
+ status = rdev_del_virtual_intf(rdev, wdev);
+ if (status >= 0 && msg)
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy),
+ msg, 0, NL80211_MCGRP_CONFIG,
+ GFP_KERNEL);
+ else
+ nlmsg_free(msg);
+
+ return status;
}
static int nl80211_set_noack_map(struct sk_buff *skb, struct genl_info *info)
@@ -4401,10 +4450,12 @@
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct net_device *dev = info->user_ptr[1];
- u8 *mac_addr = NULL;
+ struct station_del_parameters params;
+
+ memset(¶ms, 0, sizeof(params));
if (info->attrs[NL80211_ATTR_MAC])
- mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+ params.mac = nla_data(info->attrs[NL80211_ATTR_MAC]);
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
@@ -4415,7 +4466,28 @@
if (!rdev->ops->del_station)
return -EOPNOTSUPP;
- return rdev_del_station(rdev, dev, mac_addr);
+ if (info->attrs[NL80211_ATTR_MGMT_SUBTYPE]) {
+ params.subtype =
+ nla_get_u8(info->attrs[NL80211_ATTR_MGMT_SUBTYPE]);
+ if (params.subtype != IEEE80211_STYPE_DISASSOC >> 4 &&
+ params.subtype != IEEE80211_STYPE_DEAUTH >> 4)
+ return -EINVAL;
+ } else {
+ /* Default to Deauthentication frame */
+ params.subtype = IEEE80211_STYPE_DEAUTH >> 4;
+ }
+
+ if (info->attrs[NL80211_ATTR_REASON_CODE]) {
+ params.reason_code =
+ nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
+ if (params.reason_code == 0)
+ return -EINVAL; /* 0 is reserved */
+ } else {
+ /* Default to reason code 2 */
+ params.reason_code = WLAN_REASON_PREV_AUTH_NOT_VALID;
+ }
+
+ return rdev_del_station(rdev, dev, ¶ms);
}
static int nl80211_send_mpath(struct sk_buff *msg, u32 portid, u32 seq,
@@ -4426,7 +4498,7 @@
void *hdr;
struct nlattr *pinfoattr;
- hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_STATION);
+ hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_MPATH);
if (!hdr)
return -1;
@@ -4627,6 +4699,96 @@
return rdev_del_mpath(rdev, dev, dst);
}
+static int nl80211_get_mpp(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ int err;
+ struct net_device *dev = info->user_ptr[1];
+ struct mpath_info pinfo;
+ struct sk_buff *msg;
+ u8 *dst = NULL;
+ u8 mpp[ETH_ALEN];
+
+ memset(&pinfo, 0, sizeof(pinfo));
+
+ if (!info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+
+ dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+ if (!rdev->ops->get_mpp)
+ return -EOPNOTSUPP;
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
+ return -EOPNOTSUPP;
+
+ err = rdev_get_mpp(rdev, dev, dst, mpp, &pinfo);
+ if (err)
+ return err;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ if (nl80211_send_mpath(msg, genl_info_snd_portid(info), info->snd_seq, 0,
+ dev, dst, mpp, &pinfo) < 0) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ return genlmsg_reply(msg, info);
+}
+
+static int nl80211_dump_mpp(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ struct mpath_info pinfo;
+ struct cfg80211_registered_device *rdev;
+ struct wireless_dev *wdev;
+ u8 dst[ETH_ALEN];
+ u8 mpp[ETH_ALEN];
+ int path_idx = cb->args[2];
+ int err;
+
+ err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
+ if (err)
+ return err;
+
+ if (!rdev->ops->dump_mpp) {
+ err = -EOPNOTSUPP;
+ goto out_err;
+ }
+
+ if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) {
+ err = -EOPNOTSUPP;
+ goto out_err;
+ }
+
+ while (1) {
+ err = rdev_dump_mpp(rdev, wdev->netdev, path_idx, dst,
+ mpp, &pinfo);
+ if (err == -ENOENT)
+ break;
+ if (err)
+ goto out_err;
+
+ if (nl80211_send_mpath(skb, NETLINK_CB_PORTID(cb->skb),
+ cb->nlh->nlmsg_seq, NLM_F_MULTI,
+ wdev->netdev, dst, mpp,
+ &pinfo) < 0)
+ goto out;
+
+ path_idx++;
+ }
+
+ out:
+ cb->args[2] = path_idx;
+ err = skb->len;
+ out_err:
+ nl80211_finish_wdev_dump(rdev);
+ return err;
+}
+
static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -5265,11 +5427,11 @@
{
struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
struct nlattr *nl_reg_rule;
- char *alpha2 = NULL;
- int rem_reg_rules = 0, r = 0;
+ char *alpha2;
+ int rem_reg_rules, r;
u32 num_rules = 0, rule_idx = 0, size_of_regd;
enum nl80211_dfs_regions dfs_region = NL80211_DFS_UNSET;
- struct ieee80211_regdomain *rd = NULL;
+ struct ieee80211_regdomain *rd;
if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
return -EINVAL;
@@ -5363,6 +5525,43 @@
return n_channels;
}
+static int nl80211_parse_random_mac(struct nlattr **attrs,
+ u8 *mac_addr, u8 *mac_addr_mask)
+{
+ int i;
+
+ if (!attrs[NL80211_ATTR_MAC] && !attrs[NL80211_ATTR_MAC_MASK]) {
+ memset(mac_addr, 0, ETH_ALEN);
+ memset(mac_addr_mask, 0, ETH_ALEN);
+ mac_addr[0] = 0x2;
+ mac_addr_mask[0] = 0x3;
+
+ return 0;
+ }
+
+ /* need both or none */
+ if (!attrs[NL80211_ATTR_MAC] || !attrs[NL80211_ATTR_MAC_MASK])
+ return -EINVAL;
+
+ memcpy(mac_addr, nla_data(attrs[NL80211_ATTR_MAC]), ETH_ALEN);
+ memcpy(mac_addr_mask, nla_data(attrs[NL80211_ATTR_MAC_MASK]), ETH_ALEN);
+
+ /* don't allow or configure an mcast address */
+ if (!is_multicast_ether_addr(mac_addr_mask) ||
+ is_multicast_ether_addr(mac_addr))
+ return -EINVAL;
+
+ /*
+ * allow users to pass a MAC address that has bits set outside
+ * of the mask, but don't bother drivers with having to deal
+ * with such bits
+ */
+ for (i = 0; i < ETH_ALEN; i++)
+ mac_addr[i] &= mac_addr_mask[i];
+
+ return 0;
+}
+
static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -5540,6 +5739,25 @@
err = -EOPNOTSUPP;
goto out_free;
}
+
+ if (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
+ if (!(wiphy->features &
+ NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR)) {
+ err = -EOPNOTSUPP;
+ goto out_free;
+ }
+
+ if (wdev->current_bss) {
+ err = -EOPNOTSUPP;
+ goto out_free;
+ }
+
+ err = nl80211_parse_random_mac(info->attrs,
+ request->mac_addr,
+ request->mac_addr_mask);
+ if (err)
+ goto out_free;
+ }
}
request->no_cck =
@@ -5566,14 +5784,12 @@
return err;
}
-static int nl80211_start_sched_scan(struct sk_buff *skb,
- struct genl_info *info)
+static struct cfg80211_sched_scan_request *
+nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct nlattr **attrs)
{
struct cfg80211_sched_scan_request *request;
- struct cfg80211_registered_device *rdev = info->user_ptr[0];
- struct net_device *dev = info->user_ptr[1];
struct nlattr *attr;
- struct wiphy *wiphy;
int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i;
u32 interval;
enum ieee80211_band band;
@@ -5581,38 +5797,32 @@
struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1];
s32 default_match_rssi = NL80211_SCAN_RSSI_THOLD_OFF;
- if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
- !rdev->ops->sched_scan_start)
- return -EOPNOTSUPP;
+ if (!is_valid_ie_attr(attrs[NL80211_ATTR_IE]))
+ return ERR_PTR(-EINVAL);
- if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
- return -EINVAL;
+ if (!attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
+ return ERR_PTR(-EINVAL);
- if (!info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
- return -EINVAL;
-
- interval = nla_get_u32(info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
+ interval = nla_get_u32(attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
if (interval == 0)
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
- wiphy = &rdev->wiphy;
-
- if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
+ if (attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
n_channels = validate_scan_freqs(
- info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
+ attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
if (!n_channels)
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
} else {
n_channels = ieee80211_get_num_supported_channels(wiphy);
}
- if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
- nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
+ if (attrs[NL80211_ATTR_SCAN_SSIDS])
+ nla_for_each_nested(attr, attrs[NL80211_ATTR_SCAN_SSIDS],
tmp)
n_ssids++;
if (n_ssids > wiphy->max_sched_scan_ssids)
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
/*
* First, count the number of 'real' matchsets. Due to an issue with
@@ -5623,9 +5833,9 @@
* older userspace that treated a matchset with only the RSSI as the
* global RSSI for all other matchsets - if there are other matchsets.
*/
- if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
+ if (attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
nla_for_each_nested(attr,
- info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
+ attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
tmp) {
struct nlattr *rssi;
@@ -5633,7 +5843,7 @@
nla_data(attr), nla_len(attr),
nl80211_match_policy);
if (err)
- return err;
+ return ERR_PTR(err);
/* add other standalone attributes here */
if (tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID]) {
n_match_sets++;
@@ -5650,30 +5860,23 @@
n_match_sets = 1;
if (n_match_sets > wiphy->max_match_sets)
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
- if (info->attrs[NL80211_ATTR_IE])
- ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ if (attrs[NL80211_ATTR_IE])
+ ie_len = nla_len(attrs[NL80211_ATTR_IE]);
else
ie_len = 0;
if (ie_len > wiphy->max_sched_scan_ie_len)
- return -EINVAL;
-
- if (rdev->sched_scan_req) {
- err = -EINPROGRESS;
- goto out;
- }
+ return ERR_PTR(-EINVAL);
request = kzalloc(sizeof(*request)
+ sizeof(*request->ssids) * n_ssids
+ sizeof(*request->match_sets) * n_match_sets
+ sizeof(*request->channels) * n_channels
+ ie_len, GFP_KERNEL);
- if (!request) {
- err = -ENOMEM;
- goto out;
- }
+ if (!request)
+ return ERR_PTR(-ENOMEM);
if (n_ssids)
request->ssids = (void *)&request->channels[n_channels];
@@ -5698,10 +5901,10 @@
request->n_match_sets = n_match_sets;
i = 0;
- if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
+ if (attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
/* user specified, bail out if channel not found */
nla_for_each_nested(attr,
- info->attrs[NL80211_ATTR_SCAN_FREQUENCIES],
+ attrs[NL80211_ATTR_SCAN_FREQUENCIES],
tmp) {
struct ieee80211_channel *chan;
@@ -5747,8 +5950,8 @@
request->n_channels = i;
i = 0;
- if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
- nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
+ if (attrs[NL80211_ATTR_SCAN_SSIDS]) {
+ nla_for_each_nested(attr, attrs[NL80211_ATTR_SCAN_SSIDS],
tmp) {
if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
err = -EINVAL;
@@ -5762,9 +5965,9 @@
}
i = 0;
- if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
+ if (attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
nla_for_each_nested(attr,
- info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
+ attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
tmp) {
struct nlattr *ssid, *rssi;
@@ -5804,7 +6007,7 @@
}
/* there was no other matchset, so the RSSI one is alone */
- if (i == 0)
+ if (i == 0 && n_match_sets)
request->match_sets[0].rssi_thold = default_match_rssi;
request->min_rssi_thold = INT_MAX;
@@ -5819,36 +6022,88 @@
if (ie_len) {
request->ie_len = ie_len;
memcpy((void *)request->ie,
- nla_data(info->attrs[NL80211_ATTR_IE]),
+ nla_data(attrs[NL80211_ATTR_IE]),
request->ie_len);
}
- if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) {
+ if (attrs[NL80211_ATTR_SCAN_FLAGS]) {
request->flags = nla_get_u32(
- info->attrs[NL80211_ATTR_SCAN_FLAGS]);
+ attrs[NL80211_ATTR_SCAN_FLAGS]);
if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
!(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) {
err = -EOPNOTSUPP;
goto out_free;
}
+
+ if (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
+ u32 flg = NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR;
+
+ if (!wdev) /* must be net-detect */
+ flg = NL80211_FEATURE_ND_RANDOM_MAC_ADDR;
+
+ if (!(wiphy->features & flg)) {
+ err = -EOPNOTSUPP;
+ goto out_free;
+ }
+
+ if (wdev && wdev->current_bss) {
+ err = -EOPNOTSUPP;
+ goto out_free;
+ }
+
+ err = nl80211_parse_random_mac(attrs, request->mac_addr,
+ request->mac_addr_mask);
+ if (err)
+ goto out_free;
+ }
}
- request->dev = dev;
- request->wiphy = &rdev->wiphy;
request->interval = interval;
request->scan_start = jiffies;
- err = rdev_sched_scan_start(rdev, dev, request);
- if (!err) {
- rdev->sched_scan_req = request;
- nl80211_send_sched_scan(rdev, dev,
- NL80211_CMD_START_SCHED_SCAN);
- goto out;
- }
+ return request;
out_free:
kfree(request);
-out:
+ return ERR_PTR(err);
+}
+
+static int nl80211_start_sched_scan(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
+ !rdev->ops->sched_scan_start)
+ return -EOPNOTSUPP;
+
+ if (rdev->sched_scan_req)
+ return -EINPROGRESS;
+
+ rdev->sched_scan_req = nl80211_parse_sched_scan(&rdev->wiphy, wdev,
+ info->attrs);
+ err = PTR_ERR_OR_ZERO(rdev->sched_scan_req);
+ if (err)
+ goto out_err;
+
+ err = rdev_sched_scan_start(rdev, dev, rdev->sched_scan_req);
+ if (err)
+ goto out_free;
+
+ rdev->sched_scan_req->dev = dev;
+ rdev->sched_scan_req->wiphy = &rdev->wiphy;
+
+ nl80211_send_sched_scan(rdev, dev,
+ NL80211_CMD_START_SCHED_SCAN);
+ return 0;
+
+out_free:
+ kfree(rdev->sched_scan_req);
+out_err:
+ rdev->sched_scan_req = NULL;
return err;
}
@@ -5928,10 +6183,10 @@
* function is called under RTNL lock, so this should not be a problem.
*/
static struct nlattr *csa_attrs[NL80211_ATTR_MAX+1];
- u8 radar_detect_width = 0;
int err;
bool need_new_beacon = false;
int len, i;
+ u32 cs_count;
if (!rdev->ops->channel_switch ||
!(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH))
@@ -5968,7 +6223,14 @@
if (need_new_beacon && !info->attrs[NL80211_ATTR_CSA_IES])
return -EINVAL;
- params.count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]);
+ /* Even though the attribute is u32, the specification says
+ * u8, so let's make sure we don't overflow.
+ */
+ cs_count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]);
+ if (cs_count > 255)
+ return -EINVAL;
+
+ params.count = cs_count;
if (!need_new_beacon)
goto skip_beacons;
@@ -6056,10 +6318,8 @@
if (err < 0)
return err;
- if (err > 0) {
- radar_detect_width = BIT(params.chandef.width);
+ if (err > 0)
params.radar_required = true;
- }
if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX])
params.block_tx = true;
@@ -6310,8 +6570,6 @@
}
while (1) {
- struct ieee80211_channel *chan;
-
res = rdev_dump_survey(rdev, wdev->netdev, survey_idx, &survey);
if (res == -ENOENT)
break;
@@ -6324,9 +6582,7 @@
goto out;
}
- chan = ieee80211_get_channel(&rdev->wiphy,
- survey.channel->center_freq);
- if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) {
+ if (survey.channel->flags & IEEE80211_CHAN_DISABLED) {
survey_idx++;
continue;
}
@@ -8162,6 +8418,28 @@
return -EINVAL;
}
+static int nl80211_join_ocb(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct ocb_setup setup = {};
+ int err;
+
+ err = nl80211_parse_chandef(rdev, info, &setup.chandef);
+ if (err)
+ return err;
+
+ return cfg80211_join_ocb(rdev, dev, &setup);
+}
+
+static int nl80211_leave_ocb(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+
+ return cfg80211_leave_ocb(rdev, dev);
+}
+
static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -8546,6 +8824,39 @@
return 0;
}
+static int nl80211_parse_wowlan_nd(struct cfg80211_registered_device *rdev,
+ const struct wiphy_wowlan_support *wowlan,
+ struct nlattr *attr,
+ struct cfg80211_wowlan *trig)
+{
+ struct nlattr **tb;
+ int err;
+
+ tb = kzalloc(NUM_NL80211_ATTR * sizeof(*tb), GFP_KERNEL);
+ if (!tb)
+ return -ENOMEM;
+
+ if (!(wowlan->flags & WIPHY_WOWLAN_NET_DETECT)) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ err = nla_parse(tb, NL80211_ATTR_MAX,
+ nla_data(attr), nla_len(attr),
+ nl80211_policy);
+ if (err)
+ goto out;
+
+ trig->nd_config = nl80211_parse_sched_scan(&rdev->wiphy, NULL, tb);
+ err = PTR_ERR_OR_ZERO(trig->nd_config);
+ if (err)
+ trig->nd_config = NULL;
+
+out:
+ kfree(tb);
+ return err;
+}
+
static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -8691,6 +9002,14 @@
goto error;
}
+ if (tb[NL80211_WOWLAN_TRIG_NET_DETECT]) {
+ err = nl80211_parse_wowlan_nd(
+ rdev, wowlan, tb[NL80211_WOWLAN_TRIG_NET_DETECT],
+ &new_triggers);
+ if (err)
+ goto error;
+ }
+
ntrig = kmemdup(&new_triggers, sizeof(new_triggers), GFP_KERNEL);
if (!ntrig) {
err = -ENOMEM;
@@ -9451,7 +9770,7 @@
u16 admitted_time = 0;
int err;
- if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_WMM_ADMISSION))
+ if (!(rdev->wiphy.features & NL80211_FEATURE_SUPPORTS_WMM_ADMISSION))
return -EOPNOTSUPP;
if (!info->attrs[NL80211_ATTR_TSID] || !info->attrs[NL80211_ATTR_MAC] ||
@@ -9467,12 +9786,10 @@
return -EINVAL;
/* WMM uses TIDs 0-7 even for TSPEC */
- if (tsid < IEEE80211_FIRST_TSPEC_TSID) {
- if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_WMM_ADMISSION))
- return -EINVAL;
- } else {
+ if (tsid >= IEEE80211_FIRST_TSPEC_TSID) {
/* TODO: handle 802.11 TSPEC/admission control
- * need more attributes for that (e.g. BA session requirement)
+ * need more attributes for that (e.g. BA session requirement);
+ * change the WMM adminssion test above to allow both then
*/
return -EINVAL;
}
@@ -9528,6 +9845,98 @@
return err;
}
+static int nl80211_tdls_channel_switch(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_chan_def chandef = {};
+ const u8 *addr;
+ u8 oper_class;
+ int err;
+
+ if (!rdev->ops->tdls_channel_switch ||
+ !(rdev->wiphy.features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH))
+ return -EOPNOTSUPP;
+
+ switch (dev->ieee80211_ptr->iftype) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ if (!info->attrs[NL80211_ATTR_MAC] ||
+ !info->attrs[NL80211_ATTR_OPER_CLASS])
+ return -EINVAL;
+
+ err = nl80211_parse_chandef(rdev, info, &chandef);
+ if (err)
+ return err;
+
+ /*
+ * Don't allow wide channels on the 2.4Ghz band, as per IEEE802.11-2012
+ * section 10.22.6.2.1. Disallow 5/10Mhz channels as well for now, the
+ * specification is not defined for them.
+ */
+ if (chandef.chan->band == IEEE80211_BAND_2GHZ &&
+ chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
+ chandef.width != NL80211_CHAN_WIDTH_20)
+ return -EINVAL;
+
+ /* we will be active on the TDLS link */
+ if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef, wdev->iftype))
+ return -EINVAL;
+
+ /* don't allow switching to DFS channels */
+ if (cfg80211_chandef_dfs_required(wdev->wiphy, &chandef, wdev->iftype))
+ return -EINVAL;
+
+ addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+ oper_class = nla_get_u8(info->attrs[NL80211_ATTR_OPER_CLASS]);
+
+ wdev_lock(wdev);
+ err = rdev_tdls_channel_switch(rdev, dev, addr, oper_class, &chandef);
+ wdev_unlock(wdev);
+
+ return err;
+}
+
+static int nl80211_tdls_cancel_channel_switch(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ const u8 *addr;
+
+ if (!rdev->ops->tdls_channel_switch ||
+ !rdev->ops->tdls_cancel_channel_switch ||
+ !(rdev->wiphy.features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH))
+ return -EOPNOTSUPP;
+
+ switch (dev->ieee80211_ptr->iftype) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ if (!info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+
+ addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+ wdev_lock(wdev);
+ rdev_tdls_cancel_channel_switch(rdev, dev, addr);
+ wdev_unlock(wdev);
+
+ return 0;
+}
+
#define NL80211_FLAG_NEED_WIPHY 0x01
#define NL80211_FLAG_NEED_NETDEV 0x02
#define NL80211_FLAG_NEED_RTNL 0x04
@@ -9789,6 +10198,15 @@
NL80211_FLAG_NEED_RTNL,
},
{
+ .cmd = NL80211_CMD_GET_MPP,
+ .doit = nl80211_get_mpp,
+ .dumpit = nl80211_dump_mpp,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
.cmd = NL80211_CMD_SET_MPATH,
.doit = nl80211_set_mpath,
.policy = nl80211_policy,
@@ -10102,6 +10520,22 @@
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
+ {
+ .cmd = NL80211_CMD_JOIN_OCB,
+ .doit = nl80211_join_ocb,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_LEAVE_OCB,
+ .doit = nl80211_leave_ocb,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
#ifdef CONFIG_PM
{
.cmd = NL80211_CMD_GET_WOWLAN,
@@ -10301,6 +10735,22 @@
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
+ {
+ .cmd = NL80211_CMD_TDLS_CHANNEL_SWITCH,
+ .doit = nl80211_tdls_channel_switch,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH,
+ .doit = nl80211_tdls_cancel_channel_switch,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
};
/* notification functions */
@@ -11332,55 +11782,155 @@
}
EXPORT_SYMBOL(cfg80211_mgmt_tx_status);
-void cfg80211_cqm_rssi_notify(struct net_device *dev,
- enum nl80211_cqm_rssi_threshold_event rssi_event,
- gfp_t gfp)
+static struct sk_buff *cfg80211_prepare_cqm(struct net_device *dev,
+ const char *mac, gfp_t gfp)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
- struct wiphy *wiphy = wdev->wiphy;
- struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
- struct sk_buff *msg;
- struct nlattr *pinfoattr;
- void *hdr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct sk_buff *msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ void **cb;
- trace_cfg80211_cqm_rssi_notify(dev, rssi_event);
-
- msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
if (!msg)
- return;
+ return NULL;
- hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
- if (!hdr) {
+ cb = (void **)msg->cb;
+
+ cb[0] = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
+ if (!cb[0]) {
nlmsg_free(msg);
- return;
+ return NULL;
}
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex))
goto nla_put_failure;
- pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
- if (!pinfoattr)
+ if (mac && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac))
goto nla_put_failure;
+ cb[1] = nla_nest_start(msg, NL80211_ATTR_CQM);
+ if (!cb[1])
+ goto nla_put_failure;
+
+ cb[2] = rdev;
+
+ return msg;
+ nla_put_failure:
+ nlmsg_free(msg);
+ return NULL;
+}
+
+static void cfg80211_send_cqm(struct sk_buff *msg, gfp_t gfp)
+{
+ void **cb = (void **)msg->cb;
+ struct cfg80211_registered_device *rdev = cb[2];
+
+ nla_nest_end(msg, cb[1]);
+ genlmsg_end(msg, cb[0]);
+
+ memset(msg->cb, 0, sizeof(msg->cb));
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+ NL80211_MCGRP_MLME, gfp);
+}
+
+void cfg80211_cqm_rssi_notify(struct net_device *dev,
+ enum nl80211_cqm_rssi_threshold_event rssi_event,
+ gfp_t gfp)
+{
+ struct sk_buff *msg;
+
+ trace_cfg80211_cqm_rssi_notify(dev, rssi_event);
+
+ if (WARN_ON(rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW &&
+ rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH))
+ return;
+
+ msg = cfg80211_prepare_cqm(dev, NULL, gfp);
+ if (!msg)
+ return;
+
if (nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
rssi_event))
goto nla_put_failure;
- nla_nest_end(msg, pinfoattr);
+ cfg80211_send_cqm(msg, gfp);
- genlmsg_end(msg, hdr);
-
- genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
- NL80211_MCGRP_MLME, gfp);
return;
nla_put_failure:
- genlmsg_cancel(msg, hdr);
nlmsg_free(msg);
}
EXPORT_SYMBOL(cfg80211_cqm_rssi_notify);
+void cfg80211_cqm_txe_notify(struct net_device *dev,
+ const u8 *peer, u32 num_packets,
+ u32 rate, u32 intvl, gfp_t gfp)
+{
+ struct sk_buff *msg;
+
+ msg = cfg80211_prepare_cqm(dev, peer, gfp);
+ if (!msg)
+ return;
+
+ if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_PKTS, num_packets))
+ goto nla_put_failure;
+
+ if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_RATE, rate))
+ goto nla_put_failure;
+
+ if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_INTVL, intvl))
+ goto nla_put_failure;
+
+ cfg80211_send_cqm(msg, gfp);
+ return;
+
+ nla_put_failure:
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_cqm_txe_notify);
+
+void cfg80211_cqm_pktloss_notify(struct net_device *dev,
+ const u8 *peer, u32 num_packets, gfp_t gfp)
+{
+ struct sk_buff *msg;
+
+ trace_cfg80211_cqm_pktloss_notify(dev, peer, num_packets);
+
+ msg = cfg80211_prepare_cqm(dev, peer, gfp);
+ if (!msg)
+ return;
+
+ if (nla_put_u32(msg, NL80211_ATTR_CQM_PKT_LOSS_EVENT, num_packets))
+ goto nla_put_failure;
+
+ cfg80211_send_cqm(msg, gfp);
+ return;
+
+ nla_put_failure:
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify);
+
+void cfg80211_cqm_beacon_loss_notify(struct net_device *dev, gfp_t gfp)
+{
+ struct sk_buff *msg;
+
+ msg = cfg80211_prepare_cqm(dev, NULL, gfp);
+ if (!msg)
+ return;
+
+ if (nla_put_flag(msg, NL80211_ATTR_CQM_BEACON_LOSS_EVENT))
+ goto nla_put_failure;
+
+ cfg80211_send_cqm(msg, gfp);
+ return;
+
+ nla_put_failure:
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_cqm_beacon_loss_notify);
+
static void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *bssid,
const u8 *replay_ctr, gfp_t gfp)
@@ -11498,7 +12048,9 @@
static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
struct net_device *netdev,
struct cfg80211_chan_def *chandef,
- gfp_t gfp)
+ gfp_t gfp,
+ enum nl80211_commands notif,
+ u8 count)
{
struct sk_buff *msg;
void *hdr;
@@ -11507,7 +12059,7 @@
if (!msg)
return;
- hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CH_SWITCH_NOTIFY);
+ hdr = nl80211hdr_put(msg, 0, 0, 0, notif);
if (!hdr) {
nlmsg_free(msg);
return;
@@ -11519,6 +12071,10 @@
if (nl80211_send_chandef(msg, chandef))
goto nla_put_failure;
+ if ((notif == NL80211_CMD_CH_SWITCH_STARTED_NOTIFY) &&
+ (nla_put_u32(msg, NL80211_ATTR_CH_SWITCH_COUNT, count)))
+ goto nla_put_failure;
+
genlmsg_end(msg, hdr);
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
@@ -11541,70 +12097,27 @@
trace_cfg80211_ch_switch_notify(dev, chandef);
- if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
- wdev->iftype != NL80211_IFTYPE_P2P_GO &&
- wdev->iftype != NL80211_IFTYPE_ADHOC &&
- wdev->iftype != NL80211_IFTYPE_MESH_POINT))
- return;
-
wdev->chandef = *chandef;
wdev->preset_chandef = *chandef;
- nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL);
+ nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL,
+ NL80211_CMD_CH_SWITCH_NOTIFY, 0);
}
EXPORT_SYMBOL(cfg80211_ch_switch_notify);
-void cfg80211_cqm_txe_notify(struct net_device *dev,
- const u8 *peer, u32 num_packets,
- u32 rate, u32 intvl, gfp_t gfp)
+void cfg80211_ch_switch_started_notify(struct net_device *dev,
+ struct cfg80211_chan_def *chandef,
+ u8 count)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
- struct sk_buff *msg;
- struct nlattr *pinfoattr;
- void *hdr;
- msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
- if (!msg)
- return;
+ trace_cfg80211_ch_switch_started_notify(dev, chandef);
- hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
- if (!hdr) {
- nlmsg_free(msg);
- return;
- }
-
- if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
- nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
- nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
- goto nla_put_failure;
-
- pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
- if (!pinfoattr)
- goto nla_put_failure;
-
- if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_PKTS, num_packets))
- goto nla_put_failure;
-
- if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_RATE, rate))
- goto nla_put_failure;
-
- if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_INTVL, intvl))
- goto nla_put_failure;
-
- nla_nest_end(msg, pinfoattr);
-
- genlmsg_end(msg, hdr);
-
- genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
- NL80211_MCGRP_MLME, gfp);
- return;
-
- nla_put_failure:
- genlmsg_cancel(msg, hdr);
- nlmsg_free(msg);
+ nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL,
+ NL80211_CMD_CH_SWITCH_STARTED_NOTIFY, count);
}
-EXPORT_SYMBOL(cfg80211_cqm_txe_notify);
+EXPORT_SYMBOL(cfg80211_ch_switch_started_notify);
void
nl80211_radar_notify(struct cfg80211_registered_device *rdev,
@@ -11654,54 +12167,6 @@
nlmsg_free(msg);
}
-void cfg80211_cqm_pktloss_notify(struct net_device *dev,
- const u8 *peer, u32 num_packets, gfp_t gfp)
-{
- struct wireless_dev *wdev = dev->ieee80211_ptr;
- struct wiphy *wiphy = wdev->wiphy;
- struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
- struct sk_buff *msg;
- struct nlattr *pinfoattr;
- void *hdr;
-
- trace_cfg80211_cqm_pktloss_notify(dev, peer, num_packets);
-
- msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
- if (!msg)
- return;
-
- hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
- if (!hdr) {
- nlmsg_free(msg);
- return;
- }
-
- if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
- nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
- nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
- goto nla_put_failure;
-
- pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
- if (!pinfoattr)
- goto nla_put_failure;
-
- if (nla_put_u32(msg, NL80211_ATTR_CQM_PKT_LOSS_EVENT, num_packets))
- goto nla_put_failure;
-
- nla_nest_end(msg, pinfoattr);
-
- genlmsg_end(msg, hdr);
-
- genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
- NL80211_MCGRP_MLME, gfp);
- return;
-
- nla_put_failure:
- genlmsg_cancel(msg, hdr);
- nlmsg_free(msg);
-}
-EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify);
-
void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
u64 cookie, bool acked, gfp_t gfp)
{
@@ -11789,6 +12254,67 @@
EXPORT_SYMBOL(cfg80211_report_obss_beacon);
#ifdef CONFIG_PM
+static int cfg80211_net_detect_results(struct sk_buff *msg,
+ struct cfg80211_wowlan_wakeup *wakeup)
+{
+ struct cfg80211_wowlan_nd_info *nd = wakeup->net_detect;
+ struct nlattr *nl_results, *nl_match, *nl_freqs;
+ int i, j;
+
+ nl_results = nla_nest_start(
+ msg, NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS);
+ if (!nl_results)
+ return -EMSGSIZE;
+
+ for (i = 0; i < nd->n_matches; i++) {
+ struct cfg80211_wowlan_nd_match *match = nd->matches[i];
+
+ nl_match = nla_nest_start(msg, i);
+ if (!nl_match)
+ break;
+
+ /* The SSID attribute is optional in nl80211, but for
+ * simplicity reasons it's always present in the
+ * cfg80211 structure. If a driver can't pass the
+ * SSID, that needs to be changed. A zero length SSID
+ * is still a valid SSID (wildcard), so it cannot be
+ * used for this purpose.
+ */
+ if (nla_put(msg, NL80211_ATTR_SSID, match->ssid.ssid_len,
+ match->ssid.ssid)) {
+ nla_nest_cancel(msg, nl_match);
+ goto out;
+ }
+
+ if (match->n_channels) {
+ nl_freqs = nla_nest_start(
+ msg, NL80211_ATTR_SCAN_FREQUENCIES);
+ if (!nl_freqs) {
+ nla_nest_cancel(msg, nl_match);
+ goto out;
+ }
+
+ for (j = 0; j < match->n_channels; j++) {
+ if (nla_put_u32(msg,
+ NL80211_ATTR_WIPHY_FREQ,
+ match->channels[j])) {
+ nla_nest_cancel(msg, nl_freqs);
+ nla_nest_cancel(msg, nl_match);
+ goto out;
+ }
+ }
+
+ nla_nest_end(msg, nl_freqs);
+ }
+
+ nla_nest_end(msg, nl_match);
+ }
+
+out:
+ nla_nest_end(msg, nl_results);
+ return 0;
+}
+
void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
struct cfg80211_wowlan_wakeup *wakeup,
gfp_t gfp)
@@ -11883,6 +12409,10 @@
goto free_msg;
}
+ if (wakeup->net_detect &&
+ cfg80211_net_detect_results(msg, wakeup))
+ goto free_msg;
+
nla_nest_end(msg, reasons);
}
diff --git a/net/wireless/ocb.c b/net/wireless/ocb.c
new file mode 100644
index 0000000..c00d4a7
--- /dev/null
+++ b/net/wireless/ocb.c
@@ -0,0 +1,88 @@
+/*
+ * OCB mode implementation
+ *
+ * Copyright: (c) 2014 Czech Technical University in Prague
+ * (c) 2014 Volkswagen Group Research
+ * Author: Rostislav Lisovy <rostislav.lisovy@fel.cvut.cz>
+ * Funded by: Volkswagen Group Research
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/ieee80211.h>
+#include <net/cfg80211.h>
+#include "nl80211.h"
+#include "core.h"
+#include "rdev-ops.h"
+
+int __cfg80211_join_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ocb_setup *setup)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_OCB)
+ return -EOPNOTSUPP;
+
+ if (WARN_ON(!setup->chandef.chan))
+ return -EINVAL;
+
+ err = rdev_join_ocb(rdev, dev, setup);
+ if (!err)
+ wdev->chandef = setup->chandef;
+
+ return err;
+}
+
+int cfg80211_join_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ocb_setup *setup)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ wdev_lock(wdev);
+ err = __cfg80211_join_ocb(rdev, dev, setup);
+ wdev_unlock(wdev);
+
+ return err;
+}
+
+int __cfg80211_leave_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_OCB)
+ return -EOPNOTSUPP;
+
+ if (!rdev->ops->leave_ocb)
+ return -EOPNOTSUPP;
+
+ err = rdev_leave_ocb(rdev, dev);
+ if (!err)
+ memset(&wdev->chandef, 0, sizeof(wdev->chandef));
+
+ return err;
+}
+
+int cfg80211_leave_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ wdev_lock(wdev);
+ err = __cfg80211_leave_ocb(rdev, dev);
+ wdev_unlock(wdev);
+
+ return err;
+}
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index 6a1867f..0336264 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -178,11 +178,12 @@
}
static inline int rdev_del_station(struct cfg80211_registered_device *rdev,
- struct net_device *dev, u8 *mac)
+ struct net_device *dev,
+ struct station_del_parameters *params)
{
int ret;
- trace_rdev_del_station(&rdev->wiphy, dev, mac);
- ret = rdev->ops->del_station(&rdev->wiphy, dev, mac);
+ trace_rdev_del_station(&rdev->wiphy, dev, params);
+ ret = rdev->ops->del_station(&rdev->wiphy, dev, params);
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;
}
@@ -263,6 +264,18 @@
}
+static inline int rdev_get_mpp(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u8 *dst, u8 *mpp,
+ struct mpath_info *pinfo)
+{
+ int ret;
+
+ trace_rdev_get_mpp(&rdev->wiphy, dev, dst, mpp);
+ ret = rdev->ops->get_mpp(&rdev->wiphy, dev, dst, mpp, pinfo);
+ trace_rdev_return_int_mpath_info(&rdev->wiphy, ret, pinfo);
+ return ret;
+}
+
static inline int rdev_dump_mpath(struct cfg80211_registered_device *rdev,
struct net_device *dev, int idx, u8 *dst,
u8 *next_hop, struct mpath_info *pinfo)
@@ -271,7 +284,20 @@
int ret;
trace_rdev_dump_mpath(&rdev->wiphy, dev, idx, dst, next_hop);
ret = rdev->ops->dump_mpath(&rdev->wiphy, dev, idx, dst, next_hop,
- pinfo);
+ pinfo);
+ trace_rdev_return_int_mpath_info(&rdev->wiphy, ret, pinfo);
+ return ret;
+}
+
+static inline int rdev_dump_mpp(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, int idx, u8 *dst,
+ u8 *mpp, struct mpath_info *pinfo)
+
+{
+ int ret;
+
+ trace_rdev_dump_mpp(&rdev->wiphy, dev, idx, dst, mpp);
+ ret = rdev->ops->dump_mpp(&rdev->wiphy, dev, idx, dst, mpp, pinfo);
trace_rdev_return_int_mpath_info(&rdev->wiphy, ret, pinfo);
return ret;
}
@@ -322,6 +348,27 @@
return ret;
}
+static inline int rdev_join_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ocb_setup *setup)
+{
+ int ret;
+ trace_rdev_join_ocb(&rdev->wiphy, dev, setup);
+ ret = rdev->ops->join_ocb(&rdev->wiphy, dev, setup);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_leave_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev)
+{
+ int ret;
+ trace_rdev_leave_ocb(&rdev->wiphy, dev);
+ ret = rdev->ops->leave_ocb(&rdev->wiphy, dev);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
static inline int rdev_change_bss(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct bss_parameters *params)
@@ -946,4 +993,28 @@
return ret;
}
+static inline int
+rdev_tdls_channel_switch(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, const u8 *addr,
+ u8 oper_class, struct cfg80211_chan_def *chandef)
+{
+ int ret;
+
+ trace_rdev_tdls_channel_switch(&rdev->wiphy, dev, addr, oper_class,
+ chandef);
+ ret = rdev->ops->tdls_channel_switch(&rdev->wiphy, dev, addr,
+ oper_class, chandef);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline void
+rdev_tdls_cancel_channel_switch(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, const u8 *addr)
+{
+ trace_rdev_tdls_cancel_channel_switch(&rdev->wiphy, dev, addr);
+ rdev->ops->tdls_cancel_channel_switch(&rdev->wiphy, dev, addr);
+ trace_rdev_return_void(&rdev->wiphy);
+}
+
#endif /* __CFG80211_RDEV_OPS */
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 78d0648..ec2e3b5 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -56,6 +56,7 @@
#include <net/cfg80211.h>
#include "core.h"
#include "reg.h"
+#include "rdev-ops.h"
#include "regdb.h"
#include "nl80211.h"
@@ -66,6 +67,12 @@
#define REG_DBG_PRINT(args...)
#endif
+/*
+ * Grace period we give before making sure all current interfaces reside on
+ * channels allowed by the current regulatory domain.
+ */
+#define REG_ENFORCE_GRACE_MS 60000
+
/**
* enum reg_request_treatment - regulatory request treatment
*
@@ -210,6 +217,9 @@
struct ieee80211_channel chan;
};
+static void reg_check_chans_work(struct work_struct *work);
+static DECLARE_DELAYED_WORK(reg_check_chans, reg_check_chans_work);
+
static void reg_todo(struct work_struct *work);
static DECLARE_WORK(reg_work, reg_todo);
@@ -573,8 +583,9 @@
return get_cfg80211_regdom();
}
-unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd,
- const struct ieee80211_reg_rule *rule)
+static unsigned int
+reg_get_max_bandwidth_from_range(const struct ieee80211_regdomain *rd,
+ const struct ieee80211_reg_rule *rule)
{
const struct ieee80211_freq_range *freq_range = &rule->freq_range;
const struct ieee80211_freq_range *freq_range_tmp;
@@ -622,6 +633,27 @@
return end_freq - start_freq;
}
+unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd,
+ const struct ieee80211_reg_rule *rule)
+{
+ unsigned int bw = reg_get_max_bandwidth_from_range(rd, rule);
+
+ if (rule->flags & NL80211_RRF_NO_160MHZ)
+ bw = min_t(unsigned int, bw, MHZ_TO_KHZ(80));
+ if (rule->flags & NL80211_RRF_NO_80MHZ)
+ bw = min_t(unsigned int, bw, MHZ_TO_KHZ(40));
+
+ /*
+ * HT40+/HT40- limits are handled per-channel. Only limit BW if both
+ * are not allowed.
+ */
+ if (rule->flags & NL80211_RRF_NO_HT40MINUS &&
+ rule->flags & NL80211_RRF_NO_HT40PLUS)
+ bw = min_t(unsigned int, bw, MHZ_TO_KHZ(20));
+
+ return bw;
+}
+
/* Sanity check on a regulatory rule */
static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule)
{
@@ -946,6 +978,16 @@
channel_flags |= IEEE80211_CHAN_NO_OFDM;
if (rd_flags & NL80211_RRF_NO_OUTDOOR)
channel_flags |= IEEE80211_CHAN_INDOOR_ONLY;
+ if (rd_flags & NL80211_RRF_GO_CONCURRENT)
+ channel_flags |= IEEE80211_CHAN_GO_CONCURRENT;
+ if (rd_flags & NL80211_RRF_NO_HT40MINUS)
+ channel_flags |= IEEE80211_CHAN_NO_HT40MINUS;
+ if (rd_flags & NL80211_RRF_NO_HT40PLUS)
+ channel_flags |= IEEE80211_CHAN_NO_HT40PLUS;
+ if (rd_flags & NL80211_RRF_NO_80MHZ)
+ channel_flags |= IEEE80211_CHAN_NO_80MHZ;
+ if (rd_flags & NL80211_RRF_NO_160MHZ)
+ channel_flags |= IEEE80211_CHAN_NO_160MHZ;
return channel_flags;
}
@@ -1486,6 +1528,102 @@
wiphy->reg_notifier(wiphy, request);
}
+static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev)
+{
+ struct ieee80211_channel *ch;
+ struct cfg80211_chan_def chandef;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ bool ret = true;
+
+ wdev_lock(wdev);
+
+ if (!wdev->netdev || !netif_running(wdev->netdev))
+ goto out;
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ if (!wdev->beacon_interval)
+ goto out;
+
+ ret = cfg80211_reg_can_beacon(wiphy,
+ &wdev->chandef, wdev->iftype);
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ if (!wdev->ssid_len)
+ goto out;
+
+ ret = cfg80211_reg_can_beacon(wiphy,
+ &wdev->chandef, wdev->iftype);
+ break;
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ if (!wdev->current_bss ||
+ !wdev->current_bss->pub.channel)
+ goto out;
+
+ ch = wdev->current_bss->pub.channel;
+ if (rdev->ops->get_channel &&
+ !rdev_get_channel(rdev, wdev, &chandef))
+ ret = cfg80211_chandef_usable(wiphy, &chandef,
+ IEEE80211_CHAN_DISABLED);
+ else
+ ret = !(ch->flags & IEEE80211_CHAN_DISABLED);
+ break;
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_P2P_DEVICE:
+ /* no enforcement required */
+ break;
+ default:
+ /* others not implemented for now */
+ WARN_ON(1);
+ break;
+ }
+
+out:
+ wdev_unlock(wdev);
+ return ret;
+}
+
+static void reg_leave_invalid_chans(struct wiphy *wiphy)
+{
+ struct wireless_dev *wdev;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+
+ ASSERT_RTNL();
+
+ list_for_each_entry(wdev, &rdev->wdev_list, list)
+ if (!reg_wdev_chan_valid(wiphy, wdev))
+ cfg80211_leave(rdev, wdev);
+}
+
+static void reg_check_chans_work(struct work_struct *work)
+{
+ struct cfg80211_registered_device *rdev;
+
+ REG_DBG_PRINT("Verifying active interfaces after reg change\n");
+ rtnl_lock();
+
+ list_for_each_entry(rdev, &cfg80211_rdev_list, list)
+ if (!(rdev->wiphy.regulatory_flags &
+ REGULATORY_IGNORE_STALE_KICKOFF))
+ reg_leave_invalid_chans(&rdev->wiphy);
+
+ rtnl_unlock();
+}
+
+static void reg_check_channels(void)
+{
+ /*
+ * Give usermode a chance to do something nicer (move to another
+ * channel, orderly disconnection), before forcing a disconnection.
+ */
+ mod_delayed_work(system_power_efficient_wq,
+ ®_check_chans,
+ msecs_to_jiffies(REG_ENFORCE_GRACE_MS));
+}
+
static void wiphy_update_regulatory(struct wiphy *wiphy,
enum nl80211_reg_initiator initiator)
{
@@ -1525,6 +1663,8 @@
wiphy = &rdev->wiphy;
wiphy_update_regulatory(wiphy, initiator);
}
+
+ reg_check_channels();
}
static void handle_channel_custom(struct wiphy *wiphy,
@@ -1565,10 +1705,23 @@
if (max_bandwidth_khz < MHZ_TO_KHZ(160))
bw_flags |= IEEE80211_CHAN_NO_160MHZ;
+ chan->dfs_state_entered = jiffies;
+ chan->dfs_state = NL80211_DFS_USABLE;
+
+ chan->beacon_found = false;
chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags;
chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain);
chan->max_reg_power = chan->max_power =
(int) MBM_TO_DBM(power_rule->max_eirp);
+
+ if (chan->flags & IEEE80211_CHAN_RADAR) {
+ if (reg_rule->dfs_cac_ms)
+ chan->dfs_cac_ms = reg_rule->dfs_cac_ms;
+ else
+ chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
+ }
+
+ chan->max_power = chan->max_reg_power;
}
static void handle_band_custom(struct wiphy *wiphy,
@@ -1760,7 +1913,7 @@
reg_process_hint_driver(struct wiphy *wiphy,
struct regulatory_request *driver_request)
{
- const struct ieee80211_regdomain *regd;
+ const struct ieee80211_regdomain *regd, *tmp;
enum reg_request_treatment treatment;
treatment = __reg_process_hint_driver(driver_request);
@@ -1780,7 +1933,10 @@
reg_free_request(driver_request);
return REG_REQ_IGNORE;
}
+
+ tmp = get_wiphy_regdom(wiphy);
rcu_assign_pointer(wiphy->regd, regd);
+ rcu_free_regdom(tmp);
}
@@ -1839,11 +1995,8 @@
return REG_REQ_IGNORE;
return REG_REQ_ALREADY_SET;
}
- /*
- * Two consecutive Country IE hints on the same wiphy.
- * This should be picked up early by the driver/stack
- */
- if (WARN_ON(regdom_changes(country_ie_request->alpha2)))
+
+ if (regdom_changes(country_ie_request->alpha2))
return REG_REQ_OK;
return REG_REQ_ALREADY_SET;
}
@@ -1931,8 +2084,10 @@
/* This is required so that the orig_* parameters are saved */
if (treatment == REG_REQ_ALREADY_SET && wiphy &&
- wiphy->regulatory_flags & REGULATORY_STRICT_REG)
+ wiphy->regulatory_flags & REGULATORY_STRICT_REG) {
wiphy_update_regulatory(wiphy, reg_request->initiator);
+ reg_check_channels();
+ }
return;
@@ -2813,6 +2968,7 @@
cancel_work_sync(®_work);
cancel_delayed_work_sync(®_timeout);
+ cancel_delayed_work_sync(®_check_chans);
/* Lock to suppress warnings */
rtnl_lock();
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index f365a0c..969c09f 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -80,9 +80,18 @@
if (!request)
return -ENOMEM;
- if (wdev->conn->params.channel)
+ if (wdev->conn->params.channel) {
+ enum ieee80211_band band = wdev->conn->params.channel->band;
+ struct ieee80211_supported_band *sband =
+ wdev->wiphy->bands[band];
+
+ if (!sband) {
+ kfree(request);
+ return -EINVAL;
+ }
request->channels[0] = wdev->conn->params.channel;
- else {
+ request->rates[band] = (1 << sband->n_bitrates) - 1;
+ } else {
int i = 0, j;
enum ieee80211_band band;
struct ieee80211_supported_band *bands;
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index c9b6b8e..d9d146d 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -600,6 +600,11 @@
TP_ARGS(wiphy, netdev)
);
+DEFINE_EVENT(wiphy_netdev_evt, rdev_leave_ocb,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
+ TP_ARGS(wiphy, netdev)
+);
+
DEFINE_EVENT(wiphy_netdev_evt, rdev_flush_pmksa,
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
TP_ARGS(wiphy, netdev)
@@ -680,9 +685,34 @@
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac))
);
-DEFINE_EVENT(wiphy_netdev_mac_evt, rdev_del_station,
- TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *mac),
- TP_ARGS(wiphy, netdev, mac)
+DECLARE_EVENT_CLASS(station_del,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct station_del_parameters *params),
+ TP_ARGS(wiphy, netdev, params),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(sta_mac)
+ __field(u8, subtype)
+ __field(u16, reason_code)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(sta_mac, params->mac);
+ __entry->subtype = params->subtype;
+ __entry->reason_code = params->reason_code;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", station mac: " MAC_PR_FMT
+ ", subtype: %u, reason_code: %u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac),
+ __entry->subtype, __entry->reason_code)
+);
+
+DEFINE_EVENT(station_del, rdev_del_station,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct station_del_parameters *params),
+ TP_ARGS(wiphy, netdev, params)
);
DEFINE_EVENT(wiphy_netdev_mac_evt, rdev_get_station,
@@ -801,6 +831,51 @@
MAC_PR_ARG(next_hop))
);
+TRACE_EVENT(rdev_get_mpp,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ u8 *dst, u8 *mpp),
+ TP_ARGS(wiphy, netdev, dst, mpp),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(dst)
+ MAC_ENTRY(mpp)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(dst, dst);
+ MAC_ASSIGN(mpp, mpp);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", destination: " MAC_PR_FMT
+ ", mpp: " MAC_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG,
+ MAC_PR_ARG(dst), MAC_PR_ARG(mpp))
+);
+
+TRACE_EVENT(rdev_dump_mpp,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int idx,
+ u8 *dst, u8 *mpp),
+ TP_ARGS(wiphy, netdev, idx, mpp, dst),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(dst)
+ MAC_ENTRY(mpp)
+ __field(int, idx)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(dst, dst);
+ MAC_ASSIGN(mpp, mpp);
+ __entry->idx = idx;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", index: %d, destination: "
+ MAC_PR_FMT ", mpp: " MAC_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->idx, MAC_PR_ARG(dst),
+ MAC_PR_ARG(mpp))
+);
+
TRACE_EVENT(rdev_return_int_mpath_info,
TP_PROTO(struct wiphy *wiphy, int ret, struct mpath_info *pinfo),
TP_ARGS(wiphy, ret, pinfo),
@@ -1246,6 +1321,22 @@
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), __entry->ssid)
);
+TRACE_EVENT(rdev_join_ocb,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ const struct ocb_setup *setup),
+ TP_ARGS(wiphy, netdev, setup),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG)
+);
+
TRACE_EVENT(rdev_set_wiphy_params,
TP_PROTO(struct wiphy *wiphy, u32 changed),
TP_ARGS(wiphy, changed),
@@ -1941,6 +2032,48 @@
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->tsid)
);
+TRACE_EVENT(rdev_tdls_channel_switch,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ const u8 *addr, u8 oper_class,
+ struct cfg80211_chan_def *chandef),
+ TP_ARGS(wiphy, netdev, addr, oper_class, chandef),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(addr)
+ __field(u8, oper_class)
+ CHAN_DEF_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(addr, addr);
+ CHAN_DEF_ASSIGN(chandef);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT
+ " oper class %d, " CHAN_DEF_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(addr),
+ __entry->oper_class, CHAN_DEF_PR_ARG)
+);
+
+TRACE_EVENT(rdev_tdls_cancel_channel_switch,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ const u8 *addr),
+ TP_ARGS(wiphy, netdev, addr),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(addr)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(addr, addr);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(addr))
+);
+
/*************************************************************
* cfg80211 exported functions traces *
*************************************************************/
@@ -2264,6 +2397,22 @@
NETDEV_PR_ARG, CHAN_DEF_PR_ARG)
);
+TRACE_EVENT(cfg80211_ch_switch_started_notify,
+ TP_PROTO(struct net_device *netdev,
+ struct cfg80211_chan_def *chandef),
+ TP_ARGS(netdev, chandef),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ CHAN_DEF_ENTRY
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ CHAN_DEF_ASSIGN(chandef);
+ ),
+ TP_printk(NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT,
+ NETDEV_PR_ARG, CHAN_DEF_PR_ARG)
+);
+
TRACE_EVENT(cfg80211_radar_event,
TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef),
TP_ARGS(wiphy, chandef),
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 2a1733a..0b5bd58 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -442,7 +442,8 @@
break;
case cpu_to_le16(0):
if (iftype != NL80211_IFTYPE_ADHOC &&
- iftype != NL80211_IFTYPE_STATION)
+ iftype != NL80211_IFTYPE_STATION &&
+ iftype != NL80211_IFTYPE_OCB)
return -1;
break;
}
@@ -519,6 +520,7 @@
memcpy(hdr.addr3, skb->data, ETH_ALEN);
hdrlen = 24;
break;
+ case NL80211_IFTYPE_OCB:
case NL80211_IFTYPE_ADHOC:
/* DA SA BSSID */
memcpy(hdr.addr1, skb->data, ETH_ALEN);
@@ -938,6 +940,7 @@
if (dev->ieee80211_ptr->use_4addr)
break;
/* fall through */
+ case NL80211_IFTYPE_OCB:
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_ADHOC:
dev->priv_flags |= IFF_DONT_BRIDGE;
diff --git a/scripts/uninstall.sh b/scripts/uninstall.sh
index 1eb42b0..b46d3fe 100755
--- a/scripts/uninstall.sh
+++ b/scripts/uninstall.sh
@@ -10,8 +10,8 @@
compr=""
fi
-for driver in $(find ${BACKPORT_PWD} -type f -name *.ko); do
- mod_name=${driver/${BACKPORT_PWD}/${KLIB}${KMODDIR}}${compr}
+for driver in $(find ${BACKPORT_DIR} -type f -name *.ko); do
+ mod_name=${driver/${BACKPORT_DIR}/${KLIB}${KMODDIR}}${compr}
echo " uninstall" $mod_name
rm -f $mod_name
done
diff --git a/versions b/versions
index 1be7d6d..012034c 100644
--- a/versions
+++ b/versions
@@ -1,3 +1,3 @@
-BACKPORTS_VERSION="v3.18-rc1-1-0-g1f4af51"
-BACKPORTED_KERNEL_VERSION="v3.18-rc1-0-gf114040"
+BACKPORTS_VERSION="v3.19-rc1-1-0-g74aaf28"
+BACKPORTED_KERNEL_VERSION="v3.19-rc1-0-g97bf6af"
BACKPORTED_KERNEL_NAME="Linux"