Index: projects/building-blocks/bin/pkill/tests/pgrep-j_test.sh =================================================================== --- projects/building-blocks/bin/pkill/tests/pgrep-j_test.sh (revision 278776) +++ projects/building-blocks/bin/pkill/tests/pgrep-j_test.sh (revision 278777) @@ -1,89 +1,90 @@ #!/bin/sh # $FreeBSD$ jail_name_to_jid() { local check_name="$1" - ( - line="$(jls -n 2> /dev/null | grep name=$check_name )" - for nv in $line; do - local name="${nv%=*}" - if [ "${name}" = "jid" ]; then - eval $nv - echo $jid - break - fi - done - ) + jls -j "$check_name" -s 2>/dev/null | tr ' ' '\n' | grep jid= | sed -e 's/.*=//g' } base=pgrep_j_test if [ `id -u` -ne 0 ]; then echo "1..0 # skip Test needs uid 0." exit 0 fi echo "1..3" sleep=$(pwd)/sleep.txt ln -sf /bin/sleep $sleep name="pgrep -j " sleep_amount=5 jail -c path=/ name=${base}_1_1 ip4.addr=127.0.0.1 \ command=daemon -p ${PWD}/${base}_1_1.pid $sleep $sleep_amount & jail -c path=/ name=${base}_1_2 ip4.addr=127.0.0.1 \ command=daemon -p ${PWD}/${base}_1_2.pid $sleep $sleep_amount & -jid1=$(jail_name_to_jid ${base}_1_1) -jid2=$(jail_name_to_jid ${base}_1_2) -jid="${jid1},${jid2}" -pid1="$(pgrep -f -x -j $jid "$sleep $sleep_amount" | sort)" +for i in `seq 1 10`; do + jid1=$(jail_name_to_jid ${base}_1_1) + jid2=$(jail_name_to_jid ${base}_1_2) + jid="${jid1},${jid2}" + case "$jid" in + [0-9]+,[0-9]+) + break + ;; + esac + sleep 0.1 +done + +pid1="$(pgrep -f -x -j "$jid" "$sleep $sleep_amount" | sort)" pid2=$(printf "%s\n%s" "$(cat ${PWD}/${base}_1_1.pid)" \ $(cat ${PWD}/${base}_1_2.pid) | sort) if [ "$pid1" = "$pid2" ]; then echo "ok 1 - $name" else echo "not ok 1 - $name # pgrep output: '$(echo $pid1)', pidfile output: '$(echo $pid2)'" fi [ -f ${PWD}/${base}_1_1.pid ] && kill $(cat ${PWD}/${base}_1_1.pid) [ -f ${PWD}/${base}_1_2.pid ] && kill $(cat ${PWD}/${base}_1_2.pid) +wait name="pgrep -j any" sleep_amount=6 jail -c path=/ name=${base}_2_1 ip4.addr=127.0.0.1 \ command=daemon -p ${PWD}/${base}_2_1.pid $sleep $sleep_amount & jail -c path=/ name=${base}_2_2 ip4.addr=127.0.0.1 \ command=daemon -p ${PWD}/${base}_2_2.pid $sleep $sleep_amount & sleep 2 pid1="$(pgrep -f -x -j any "$sleep $sleep_amount" | sort)" pid2=$(printf "%s\n%s" "$(cat ${PWD}/${base}_2_1.pid)" \ $(cat ${PWD}/${base}_2_2.pid) | sort) if [ "$pid1" = "$pid2" ]; then echo "ok 2 - $name" else echo "not ok 2 - $name # pgrep output: '$(echo $pid1)', pidfile output: '$(echo $pid2)'" fi [ -f ${PWD}/${base}_2_1.pid ] && kill $(cat ${PWD}/${base}_2_1.pid) [ -f ${PWD}/${base}_2_2.pid ] && kill $(cat ${PWD}/${base}_2_2.pid) +wait name="pgrep -j none" sleep_amount=7 daemon -p ${PWD}/${base}_3_1.pid $sleep $sleep_amount & jail -c path=/ name=${base}_3_2 ip4.addr=127.0.0.1 \ command=daemon -p ${PWD}/${base}_3_2.pid $sleep $sleep_amount & sleep 2 pid="$(pgrep -f -x -j none "$sleep $sleep_amount")" if [ "$pid" = "$(cat ${PWD}/${base}_3_1.pid)" ]; then echo "ok 3 - $name" else echo "not ok 3 - $name # pgrep output: '$(echo $pid1)', pidfile output: '$(echo $pid2)'" fi [ -f ${PWD}/${base}_3_1.pid ] && kill $(cat $PWD/${base}_3_1.pid) [ -f ${PWD}/${base}_3_2.pid ] && kill $(cat $PWD/${base}_3_2.pid) rm -f $sleep Index: projects/building-blocks/bin/pkill/tests/pkill-j_test.sh =================================================================== --- projects/building-blocks/bin/pkill/tests/pkill-j_test.sh (revision 278776) +++ projects/building-blocks/bin/pkill/tests/pkill-j_test.sh (revision 278777) @@ -1,102 +1,93 @@ #!/bin/sh # $FreeBSD$ jail_name_to_jid() { local check_name="$1" - ( - line="$(jls -n 2> /dev/null | grep name=$check_name )" - for nv in $line; do - local name="${nv%=*}" - if [ "${name}" = "jid" ]; then - eval $nv - echo $jid - break - fi - done - ) + jls -j "$check_name" -s 2>/dev/null | tr ' ' '\n' | grep jid= | sed -e 's/.*=//g' } base=pkill_j_test +if [ `id -u` -ne 0 ]; then + echo "1..0 # skip Test needs uid 0." + exit 0 +fi + echo "1..3" +sleep=$(pwd)/sleep.txt +ln -sf /bin/sleep $sleep + name="pkill -j " -if [ `id -u` -eq 0 ]; then - sleep=$(pwd)/sleep.txt - ln -sf /bin/sleep $sleep - jail -c path=/ name=${base}_1_1 ip4.addr=127.0.0.1 \ - command=daemon -p ${PWD}/${base}_1_1.pid $sleep 5 & +sleep_amount=5 +jail -c path=/ name=${base}_1_1 ip4.addr=127.0.0.1 \ + command=daemon -p ${PWD}/${base}_1_1.pid $sleep $sleep_amount & - jail -c path=/ name=${base}_1_2 ip4.addr=127.0.0.1 \ - command=daemon -p ${PWD}/${base}_1_2.pid $sleep 5 & +jail -c path=/ name=${base}_1_2 ip4.addr=127.0.0.1 \ + command=daemon -p ${PWD}/${base}_1_2.pid $sleep $sleep_amount & - $sleep 5 & - sleep 0.5 +$sleep $sleep_amount & + +for i in `seq 1 10`; do jid1=$(jail_name_to_jid ${base}_1_1) jid2=$(jail_name_to_jid ${base}_1_2) jid="${jid1},${jid2}" - if pkill -f -j "$jid" $sleep && sleep 0.5 && - ! -f ${PWD}/${base}_1_1.pid && - ! -f ${PWD}/${base}_1_2.pid ; then - echo "ok 1 - $name" - else - echo "not ok 1 - $name" - fi 2>/dev/null - rm -f $sleep - [ -f ${PWD}/${base}_1_1.pid ] && kill $(cat ${PWD}/${base}_1_1.pid) - [ -f ${PWD}/${base}_1_2.pid ] && kill $(cat ${PWD}/${base}_1_2.pid) - wait + case "$jid" in + [0-9]+,[0-9]+) + break + ;; + esac + sleep 0.1 +done + +if pkill -f -j "$jid" $sleep && sleep 0.5 && + ! -f ${PWD}/${base}_1_1.pid && + ! -f ${PWD}/${base}_1_2.pid ; then + echo "ok 1 - $name" else - echo "ok 1 - $name # skip Test needs uid 0." -fi + echo "not ok 1 - $name" +fi 2>/dev/null +[ -f ${PWD}/${base}_1_1.pid ] && kill $(cat ${PWD}/${base}_1_1.pid) +[ -f ${PWD}/${base}_1_2.pid ] && kill $(cat ${PWD}/${base}_1_2.pid) +wait name="pkill -j any" -if [ `id -u` -eq 0 ]; then - sleep=$(pwd)/sleep.txt - ln -sf /bin/sleep $sleep - jail -c path=/ name=${base}_2_1 ip4.addr=127.0.0.1 \ - command=daemon -p ${PWD}/${base}_2_1.pid $sleep 5 & +sleep_amount=6 +jail -c path=/ name=${base}_2_1 ip4.addr=127.0.0.1 \ + command=daemon -p ${PWD}/${base}_2_1.pid $sleep $sleep_amount & - jail -c path=/ name=${base}_2_2 ip4.addr=127.0.0.1 \ - command=daemon -p ${PWD}/${base}_2_2.pid $sleep 5 & +jail -c path=/ name=${base}_2_2 ip4.addr=127.0.0.1 \ + command=daemon -p ${PWD}/${base}_2_2.pid $sleep $sleep_amount & - $sleep 5 & - sleep 0.5 - chpid3=$! - if pkill -f -j any $sleep && sleep 0.5 && - [ ! -f ${PWD}/${base}_2_1.pid -a - ! -f ${PWD}/${base}_2_2.pid ] && kill $chpid3; then - echo "ok 2 - $name" - else - echo "not ok 2 - $name" - fi 2>/dev/null - rm -f $sleep - [ -f ${PWD}/${base}_2_1.pid ] && kill $(cat ${PWD}/${base}_2_1.pid) - [ -f ${PWD}/${base}_2_2.pid ] && kill $(cat ${PWD}/${base}_2_2.pid) - wait +$sleep $sleep_amount & +chpid3=$! +sleep 0.5 +if pkill -f -j any $sleep && sleep 0.5 && + [ ! -f ${PWD}/${base}_2_1.pid -a + ! -f ${PWD}/${base}_2_2.pid ] && kill $chpid3; then + echo "ok 2 - $name" else - echo "ok 2 - $name # skip Test needs uid 0." -fi + echo "not ok 2 - $name" +fi 2>/dev/null +[ -f ${PWD}/${base}_2_1.pid ] && kill $(cat ${PWD}/${base}_2_1.pid) +[ -f ${PWD}/${base}_2_2.pid ] && kill $(cat ${PWD}/${base}_2_2.pid) +wait name="pkill -j none" -if [ `id -u` -eq 0 ]; then - sleep=$(pwd)/sleep.txt - ln -sf /bin/sleep $sleep - daemon -p ${PWD}/${base}_3_1.pid $sleep 5 - jail -c path=/ name=${base}_3_2 ip4.addr=127.0.0.1 \ - command=daemon -p ${PWD}/${base}_3_2.pid $sleep 5 & - sleep 1 - if pkill -f -j none "$sleep 5" && sleep 1 && - [ ! -f ${PWD}/${base}_3_1.pid -a -f ${PWD}/${base}_3_2.pid ] ; then - echo "ok 3 - $name" - else - ls ${PWD}/*.pid - echo "not ok 3 - $name" - fi 2>/dev/null - rm -f $sleep - [ -f ${PWD}/${base}_3_1.pid ] && kill $(cat ${base}_3_1.pid) - [ -f ${PWD}/${base}_3_2.pid ] && kill $(cat ${base}_3_2.pid) +sleep_amount=7 +daemon -p ${PWD}/${base}_3_1.pid $sleep $sleep_amount +jail -c path=/ name=${base}_3_2 ip4.addr=127.0.0.1 \ + command=daemon -p ${PWD}/${base}_3_2.pid $sleep $sleep_amount & +sleep 1 +if pkill -f -j none "$sleep $sleep_amount" && sleep 1 && + [ ! -f ${PWD}/${base}_3_1.pid -a -f ${PWD}/${base}_3_2.pid ] ; then + echo "ok 3 - $name" else - echo "ok 3 - $name # skip Test needs uid 0." -fi + ls ${PWD}/*.pid + echo "not ok 3 - $name" +fi 2>/dev/null +[ -f ${PWD}/${base}_3_1.pid ] && kill $(cat ${base}_3_1.pid) +[ -f ${PWD}/${base}_3_2.pid ] && kill $(cat ${base}_3_2.pid) + +rm -f $sleep Index: projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/io/tst.fds.c =================================================================== --- projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/io/tst.fds.c (revision 278776) +++ projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/io/tst.fds.c (revision 278777) @@ -1,100 +1,101 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" +#include + #include #include #include #include #include #include #include static sigjmp_buf env; static void interrupt(int sig) { siglongjmp(env, sig); } int main(int argc, char *argv[]) { const char *file = "/dev/null"; int i, n, fds[10]; struct sigaction act; if (argc > 1) { (void) fprintf(stderr, "Usage: %s\n", argv[0]); return (EXIT_FAILURE); } act.sa_handler = interrupt; act.sa_flags = 0; (void) sigemptyset(&act.sa_mask); (void) sigaction(SIGUSR1, &act, NULL); closefrom(0); n = 0; /* * With all of our file descriptors closed, wait here spinning in bogus * ioctl() calls until DTrace hits us with a SIGUSR1 to start the test. */ if (sigsetjmp(env, 1) == 0) { for (;;) - (void) ioctl(-1, -1, NULL); + (void) ioctl(-1, 0, NULL); } /* * To test the fds[] array, we open /dev/null (a file with reliable * pathname and properties) using various flags and seek offsets. */ fds[n++] = open(file, O_RDONLY); fds[n++] = open(file, O_WRONLY); fds[n++] = open(file, O_RDWR); - fds[n++] = open(file, O_RDWR | O_APPEND | O_CREAT | O_DSYNC | - O_LARGEFILE | O_NOCTTY | O_NONBLOCK | O_NDELAY | O_RSYNC | - O_SYNC | O_TRUNC | O_XATTR, 0666); + fds[n++] = open(file, O_RDWR | O_APPEND | O_CREAT | + O_NOCTTY | O_NONBLOCK | O_NDELAY | O_SYNC | O_TRUNC | 0666); fds[n++] = open(file, O_RDWR); (void) lseek(fds[n - 1], 123, SEEK_SET); /* * Once we have all the file descriptors in the state we want to test, - * issue a bogus ioctl() on each fd with cmd -1 and arg NULL to whack + * issue a bogus ioctl() on each fd with cmd 0 and arg NULL to whack * our DTrace script into recording the content of the fds[] array. */ for (i = 0; i < n; i++) - (void) ioctl(fds[i], -1, NULL); + (void) ioctl(fds[i], 0, NULL); assert(n <= sizeof (fds) / sizeof (fds[0])); exit(0); } Index: projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/io/tst.fds.d =================================================================== --- projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/io/tst.fds.d (revision 278776) +++ projects/building-blocks/cddl/contrib/opensolaris/cmd/dtrace/test/tst/common/io/tst.fds.d (revision 278777) @@ -1,54 +1,54 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #pragma D option destructive #pragma D option quiet syscall::ioctl:entry /pid == $1 && arg0 == -1u/ { raise(SIGUSR1); /* kick tst.fds.c out of its busy-wait loop */ } syscall::ioctl:entry -/pid == $1 && arg0 != -1u && arg1 == -1u && arg2 == NULL/ +/pid == $1 && arg0 != -1u && arg1 == 0 && arg2 == NULL/ { printf("fds[%d] fi_name = %s\n", arg0, fds[arg0].fi_name); printf("fds[%d] fi_dirname = %s\n", arg0, fds[arg0].fi_dirname); printf("fds[%d] fi_pathname = %s\n", arg0, fds[arg0].fi_pathname); printf("fds[%d] fi_fs = %s\n", arg0, fds[arg0].fi_fs); printf("fds[%d] fi_mount = %s\n", arg0, fds[arg0].fi_mount); printf("fds[%d] fi_offset = %d\n", arg0, fds[arg0].fi_offset); printf("fds[%d] fi_oflags = 0x%x\n", arg0, fds[arg0].fi_oflags); } proc:::exit /pid == $1/ { exit(0); } Index: projects/building-blocks/cddl/contrib/opensolaris =================================================================== --- projects/building-blocks/cddl/contrib/opensolaris (revision 278776) +++ projects/building-blocks/cddl/contrib/opensolaris (revision 278777) Property changes on: projects/building-blocks/cddl/contrib/opensolaris ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/cddl/contrib/opensolaris:r278564-278776 Index: projects/building-blocks/cddl =================================================================== --- projects/building-blocks/cddl (revision 278776) +++ projects/building-blocks/cddl (revision 278777) Property changes on: projects/building-blocks/cddl ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/cddl:r278564-278776 Index: projects/building-blocks/etc/rc.d/LOGIN =================================================================== --- projects/building-blocks/etc/rc.d/LOGIN (revision 278776) +++ projects/building-blocks/etc/rc.d/LOGIN (revision 278777) @@ -1,13 +1,13 @@ #!/bin/sh # # $FreeBSD$ # # PROVIDE: LOGIN -# REQUIRE: DAEMON utx +# REQUIRE: DAEMON # This is a dummy dependency to ensure user services such as xdm, # inetd, cron and kerberos are started after everything else, in case # the administrator has increased the system security level and # wants to delay user logins until the system is (almost) fully # operational. Index: projects/building-blocks/etc/rc.d/local_unbound =================================================================== --- projects/building-blocks/etc/rc.d/local_unbound (revision 278776) +++ projects/building-blocks/etc/rc.d/local_unbound (revision 278777) @@ -1,93 +1,94 @@ #!/bin/sh # # $FreeBSD$ # # PROVIDE: local_unbound DNS # REQUIRE: FILESYSTEMS netif resolv routing +# BEFORE: NETWORKING # KEYWORD: shutdown . /etc/rc.subr name="local_unbound" desc="local caching forwarding resolver" rcvar="local_unbound_enable" command="/usr/sbin/unbound" extra_commands="anchor configtest reload setup" start_precmd="local_unbound_prestart" reload_precmd="local_unbound_configtest" anchor_cmd="local_unbound_anchor" configtest_cmd="local_unbound_configtest" setup_cmd="local_unbound_setup" pidfile="/var/run/${name}.pid" : ${local_unbound_workdir:=/var/unbound} : ${local_unbound_config:=${local_unbound_workdir}/unbound.conf} : ${local_unbound_flags:=-c${local_unbound_config}} : ${local_unbound_forwardconf:=${local_unbound_workdir}/forward.conf} : ${local_unbound_controlconf:=${local_unbound_workdir}/control.conf} : ${local_unbound_anchor:=${local_unbound_workdir}/root.key} : ${local_unbound_forwarders:=} load_rc_config $name do_as_unbound() { echo "$@" | su -m unbound } # # Retrieve or update the DNSSEC root anchor # local_unbound_anchor() { do_as_unbound /usr/sbin/unbound-anchor -a ${local_unbound_anchor} # we can't trust the exit code - check if the file exists [ -f ${local_unbound_anchor} ] } # # Check the unbound configuration file # local_unbound_configtest() { do_as_unbound /usr/sbin/unbound-checkconf ${local_unbound_config} } # # Create the unbound configuration file and update resolv.conf to # point to unbound. # local_unbound_setup() { echo "Performing initial setup." /usr/sbin/local-unbound-setup -n \ -u unbound \ -w ${local_unbound_workdir} \ -c ${local_unbound_config} \ -f ${local_unbound_forwardconf} \ -o ${local_unbound_controlconf} \ -a ${local_unbound_anchor} \ ${local_unbound_forwarders} } # # Before starting, check that the configuration file and root anchor # exist. If not, attempt to generate them. # local_unbound_prestart() { # Create configuration file if [ ! -f ${local_unbound_config} ] ; then run_rc_command setup fi # Retrieve DNSSEC root key if [ ! -f ${local_unbound_anchor} ] ; then run_rc_command anchor fi } load_rc_config $name run_rc_command "$1" Index: projects/building-blocks/etc/rc.d/utx =================================================================== --- projects/building-blocks/etc/rc.d/utx (revision 278776) +++ projects/building-blocks/etc/rc.d/utx (revision 278777) @@ -1,17 +1,18 @@ #!/bin/sh # # $FreeBSD$ # # PROVIDE: utx # REQUIRE: DAEMON FILESYSTEMS +# BEFORE: LOGIN # KEYWORD: shutdown . /etc/rc.subr name="utx" start_cmd="utx boot" stop_cmd="utx shutdown" load_rc_config $name run_rc_command "$1" Index: projects/building-blocks/etc =================================================================== --- projects/building-blocks/etc (revision 278776) +++ projects/building-blocks/etc (revision 278777) Property changes on: projects/building-blocks/etc ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/etc:r278637-278776 Index: projects/building-blocks/lib/libiconv/Makefile =================================================================== --- projects/building-blocks/lib/libiconv/Makefile (revision 278776) +++ projects/building-blocks/lib/libiconv/Makefile (nonexistent) @@ -1,25 +0,0 @@ -# $FreeBSD$ - -.PATH: ${.CURDIR}/../libc/iconv - -LIB= iconv -SHLIB_MAJOR= 4 -MAN= iconv.3 iconvctl.3 iconv_canonicalize.3 iconvlist.3 \ - __iconv_get_list.3 -MLNKS= iconv.3 iconv_open.3 \ - iconv.3 iconv_open_into.3 \ - iconv.3 iconv_close.3 \ - iconv.3 __iconv.3 \ - __iconv_get_list.3 __iconv_free_list.3 -SRCS= citrus_bcs.c citrus_bcs_strtol.c citrus_bcs_strtoul.c \ - citrus_csmapper.c citrus_db.c citrus_db_factory.c \ - citrus_db_hash.c citrus_esdb.c citrus_hash.c \ - citrus_iconv.c citrus_lookup.c citrus_lookup_factory.c \ - citrus_mapper.c citrus_memstream.c citrus_mmap.c \ - citrus_module.c citrus_none.c citrus_pivot_factory.c \ - citrus_prop.c citrus_stdenc.c iconv.c - -CFLAGS.gcc+= --param max-inline-insns-single=128 -CFLAGS+= -I ${.CURDIR}/../../include -I${.CURDIR}/../libc/include - -.include Property changes on: projects/building-blocks/lib/libiconv/Makefile ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: projects/building-blocks/lib/libc/gen/_spinlock_stub.c =================================================================== --- projects/building-blocks/lib/libc/gen/_spinlock_stub.c (revision 278776) +++ projects/building-blocks/lib/libc/gen/_spinlock_stub.c (revision 278777) @@ -1,83 +1,80 @@ /* * Copyright (c) 1998 John Birrell . * 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 author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 REGENTS 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 __FBSDID("$FreeBSD$"); #include #include "spinlock.h" +#include "libc_private.h" long _atomic_lock_stub(volatile long *); void _spinlock_stub(spinlock_t *); void _spinunlock_stub(spinlock_t *); void _spinlock_debug_stub(spinlock_t *, char *, int); -/* - * Declare weak definitions in case the application is not linked - * with libpthread. - */ __weak_reference(_atomic_lock_stub, _atomic_lock); -__weak_reference(_spinlock_stub, _spinlock); -__weak_reference(_spinunlock_stub, _spinunlock); -__weak_reference(_spinlock_debug_stub, _spinlock_debug); -/* - * This function is a stub for the _atomic_lock function in libpthread. - */ long _atomic_lock_stub(volatile long *lck __unused) { return (0L); } +__weak_reference(_spinlock, _spinlock_debug); +#pragma weak _spinlock +void +_spinlock(spinlock_t *lck) +{ -/* - * This function is a stub for the spinlock function in libpthread. - */ + ((void (*)(spinlock_t *lck))__libc_interposing[INTERPOS_spinlock]) + (lck); + +} + +#pragma weak _spinlock void -_spinlock_stub(spinlock_t *lck __unused) +_spinunlock(spinlock_t *lck) { + + ((void (*)(spinlock_t *lck))__libc_interposing[INTERPOS_spinunlock]) + (lck); + } -/* - * This function is a stub for the spinunlock function in libpthread. - */ void -_spinunlock_stub(spinlock_t *lck __unused) +__libc_spinlock_stub(spinlock_t *lck __unused) { } -/* - * This function is a stub for the debug spinlock function in libpthread. - */ void -_spinlock_debug_stub(spinlock_t *lck __unused, char *fname __unused, int lineno __unused) +__libc_spinunlock_stub(spinlock_t *lck __unused) { } Index: projects/building-blocks/lib/libc/gen/dlopen.3 =================================================================== --- projects/building-blocks/lib/libc/gen/dlopen.3 (revision 278776) +++ projects/building-blocks/lib/libc/gen/dlopen.3 (revision 278777) @@ -1,408 +1,408 @@ .\" This source code is a product of Sun Microsystems, Inc. and is provided .\" for unrestricted use provided that this legend is included on all tape .\" media and as a part of the software program in whole or part. Users .\" may copy or modify this source code without charge, but are not authorized .\" to license or distribute it to anyone else except as part of a product or .\" program developed by the user. .\" .\" THIS PROGRAM CONTAINS SOURCE CODE COPYRIGHTED BY SUN MICROSYSTEMS, INC. .\" SUN MICROSYSTEMS, INC., MAKES NO REPRESENTATIONS ABOUT THE SUITABLITY .\" OF SUCH SOURCE CODE FOR ANY PURPOSE. IT IS PROVIDED "AS IS" WITHOUT .\" EXPRESS OR IMPLIED WARRANTY OF ANY KIND. SUN MICROSYSTEMS, INC. DISCLAIMS .\" ALL WARRANTIES WITH REGARD TO SUCH SOURCE CODE, INCLUDING ALL IMPLIED .\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN .\" NO EVENT SHALL SUN MICROSYSTEMS, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT, .\" INCIDENTAL, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING .\" FROM USE OF SUCH SOURCE CODE, REGARDLESS OF THE THEORY OF LIABILITY. .\" .\" This source code is provided with no support and without any obligation on .\" the part of Sun Microsystems, Inc. to assist in its use, correction, .\" modification or enhancement. .\" .\" SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE .\" INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS .\" SOURCE CODE OR ANY PART THEREOF. .\" .\" Sun Microsystems, Inc. .\" 2550 Garcia Avenue .\" Mountain View, California 94043 .\" .\" Copyright (c) 1991 Sun Microsystems, Inc. .\" .\" @(#) dlopen.3 1.6 90/01/31 SMI .\" $FreeBSD$ .\" -.Dd December 21, 2011 +.Dd February 14, 2015 .Dt DLOPEN 3 .Os .Sh NAME .Nm dlopen , .Nm fdlopen , .Nm dlsym , .Nm dlfunc , .Nm dlerror , .Nm dlclose .Nd programmatic interface to the dynamic linker .Sh LIBRARY .Lb libc .Sh SYNOPSIS .In dlfcn.h .Ft void * .Fn dlopen "const char *path" "int mode" .Ft void * .Fn fdlopen "int fd" "int mode" .Ft void * .Fn dlsym "void * restrict handle" "const char * restrict symbol" .Ft dlfunc_t .Fn dlfunc "void * restrict handle" "const char * restrict symbol" .Ft char * .Fn dlerror "void" .Ft int .Fn dlclose "void *handle" .Sh DESCRIPTION These functions provide a simple programmatic interface to the services of the dynamic linker. Operations are provided to add new shared objects to a program's address space, to obtain the address bindings of symbols defined by such objects, and to remove such objects when their use is no longer required. .Pp The .Fn dlopen function provides access to the shared object in .Fa path , returning a descriptor that can be used for later references to the object in calls to .Fn dlsym and .Fn dlclose . If .Fa path was not in the address space prior to the call to .Fn dlopen , it is placed in the address space. When an object is first loaded into the address space in this way, its function .Fn _init , if any, is called by the dynamic linker. If .Fa path has already been placed in the address space in a previous call to .Fn dlopen , it is not added a second time, although a reference count of .Fn dlopen operations on .Fa path is maintained. A null pointer supplied for .Fa path is interpreted as a reference to the main executable of the process. The .Fa mode argument controls the way in which external function references from the loaded object are bound to their referents. It must contain one of the following values, possibly ORed with additional flags which will be described subsequently: .Bl -tag -width RTLD_LAZYX .It Dv RTLD_LAZY Each external function reference is resolved when the function is first called. .It Dv RTLD_NOW All external function references are bound immediately by .Fn dlopen . .El .Pp .Dv RTLD_LAZY is normally preferred, for reasons of efficiency. However, .Dv RTLD_NOW is useful to ensure that any undefined symbols are discovered during the call to .Fn dlopen . .Pp One of the following flags may be ORed into the .Fa mode argument: .Bl -tag -width RTLD_NODELETE .It Dv RTLD_GLOBAL Symbols from this shared object and its directed acyclic graph (DAG) of needed objects will be available for resolving undefined references from all other shared objects. .It Dv RTLD_LOCAL Symbols in this shared object and its DAG of needed objects will be available for resolving undefined references only from other objects in the same DAG. This is the default, but it may be specified explicitly with this flag. .It Dv RTLD_TRACE When set, causes dynamic linker to exit after loading all objects needed by this shared object and printing a summary which includes the absolute pathnames of all objects, to standard output. With this flag .Fn dlopen will return to the caller only in the case of error. .It Dv RTLD_NODELETE Prevents unload of the loaded object on .Fn dlclose . The same behaviour may be requested by .Fl "z nodelete" option of the static linker .Xr ld 1 . .It Dv RTLD_NOLOAD Only return valid handle for the object if it is already loaded in the process address space, otherwise .Dv NULL is returned. Other mode flags may be specified, which will be applied for promotion for the found object. .El .Pp If .Fn dlopen fails, it returns a null pointer, and sets an error condition which may be interrogated with .Fn dlerror . .Pp The .Fn fdlopen function is similar to .Fn dlopen , but it takes the file descriptor argument .Fa fd , which is used for the file operations needed to load an object into the address space. The file descriptor .Fa fd is not closed by the function regardless a result of execution, but a duplicate of the file descriptor is. This may be important if a .Xr lockf 3 lock is held on the passed descriptor. The .Fa fd argument -1 is interpreted as a reference to the main executable of the process, similar to .Va NULL value for the .Fa name argument to .Fn dlopen . The .Fn fdlopen function can be used by the code that needs to perform additional checks on the loaded objects, to prevent races with symlinking or renames. .Pp The .Fn dlsym function returns the address binding of the symbol described in the null-terminated character string .Fa symbol , as it occurs in the shared object identified by .Fa handle . The symbols exported by objects added to the address space by .Fn dlopen can be accessed only through calls to .Fn dlsym . Such symbols do not supersede any definition of those symbols already present in the address space when the object is loaded, nor are they available to satisfy normal dynamic linking references. .Pp If .Fn dlsym is called with the special .Fa handle .Dv NULL , it is interpreted as a reference to the executable or shared object from which the call is being made. Thus a shared object can reference its own symbols. .Pp If .Fn dlsym is called with the special .Fa handle .Dv RTLD_DEFAULT , the search for the symbol follows the algorithm used for resolving undefined symbols when objects are loaded. The objects searched are as follows, in the given order: .Bl -enum .It The referencing object itself (or the object from which the call to .Fn dlsym is made), if that object was linked using the -.Fl Wsymbolic +.Fl Bsymbolic option to .Xr ld 1 . .It All objects loaded at program start-up. .It All objects loaded via .Fn dlopen with the .Dv RTLD_GLOBAL flag set in the .Fa mode argument. .It All objects loaded via .Fn dlopen which are in needed-object DAGs that also contain the referencing object. .El .Pp If .Fn dlsym is called with the special .Fa handle .Dv RTLD_NEXT , then the search for the symbol is limited to the shared objects which were loaded after the one issuing the call to .Fn dlsym . Thus, if the function is called from the main program, all the shared libraries are searched. If it is called from a shared library, all subsequent shared libraries are searched. .Dv RTLD_NEXT is useful for implementing wrappers around library functions. For example, a wrapper function .Fn getpid could access the .Dq real .Fn getpid with .Li dlsym(RTLD_NEXT, \&"getpid\&") . (Actually, the .Fn dlfunc interface, below, should be used, since .Fn getpid is a function and not a data object.) .Pp If .Fn dlsym is called with the special .Fa handle .Dv RTLD_SELF , then the search for the symbol is limited to the shared object issuing the call to .Fn dlsym and those shared objects which were loaded after it. .Pp The .Fn dlsym function returns a null pointer if the symbol cannot be found, and sets an error condition which may be queried with .Fn dlerror . .Pp The .Fn dlfunc function implements all of the behavior of .Fn dlsym , but has a return type which can be cast to a function pointer without triggering compiler diagnostics. (The .Fn dlsym function returns a data pointer; in the C standard, conversions between data and function pointer types are undefined. Some compilers and .Xr lint 1 utilities warn about such casts.) The precise return type of .Fn dlfunc is unspecified; applications must cast it to an appropriate function pointer type. .Pp The .Fn dlerror function returns a null-terminated character string describing the last error that occurred during a call to .Fn dlopen , .Fn dladdr , .Fn dlinfo , .Fn dlsym , .Fn dlfunc , or .Fn dlclose . If no such error has occurred, .Fn dlerror returns a null pointer. At each call to .Fn dlerror , the error indication is reset. Thus in the case of two calls to .Fn dlerror , where the second call follows the first immediately, the second call will always return a null pointer. .Pp The .Fn dlclose function deletes a reference to the shared object referenced by .Fa handle . If the reference count drops to 0, the object is removed from the address space, and .Fa handle is rendered invalid. Just before removing a shared object in this way, the dynamic linker calls the object's .Fn _fini function, if such a function is defined by the object. If .Fn dlclose is successful, it returns a value of 0. Otherwise it returns -1, and sets an error condition that can be interrogated with .Fn dlerror . .Pp The object-intrinsic functions .Fn _init and .Fn _fini are called with no arguments, and are not expected to return values. .Sh NOTES ELF executables need to be linked using the .Fl export-dynamic option to .Xr ld 1 for symbols defined in the executable to become visible to .Fn dlsym . .Pp In previous implementations, it was necessary to prepend an underscore to all external symbols in order to gain symbol compatibility with object code compiled from the C language. This is still the case when using the (obsolete) .Fl aout option to the C language compiler. .Sh ERRORS The .Fn dlopen , .Fn fdlopen , .Fn dlsym , and .Fn dlfunc functions return a null pointer in the event of errors. The .Fn dlclose function returns 0 on success, or -1 if an error occurred. Whenever an error has been detected, a message detailing it can be retrieved via a call to .Fn dlerror . .Sh SEE ALSO .Xr ld 1 , .Xr rtld 1 , .Xr dladdr 3 , .Xr dlinfo 3 , .Xr link 5 Index: projects/building-blocks/lib/libc/gen/ulimit.c =================================================================== --- projects/building-blocks/lib/libc/gen/ulimit.c (revision 278776) +++ projects/building-blocks/lib/libc/gen/ulimit.c (revision 278777) @@ -1,68 +1,68 @@ /*- * Copyright (c) 2002 Kyle Martin * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * * $FreeBSD$ */ #include #include #include #include #include #include #include long ulimit(int cmd, ...) { struct rlimit limit; va_list ap; long arg; if (cmd == UL_GETFSIZE) { if (getrlimit(RLIMIT_FSIZE, &limit) == -1) return (-1); limit.rlim_cur /= 512; if (limit.rlim_cur > LONG_MAX) return (LONG_MAX); return ((long)limit.rlim_cur); } else if (cmd == UL_SETFSIZE) { va_start(ap, cmd); arg = va_arg(ap, long); va_end(ap); - if (arg > RLIM_INFINITY / 512 || arg < 0) - arg = RLIM_INFINITY / 512; limit.rlim_max = limit.rlim_cur = (rlim_t)arg * 512; /* The setrlimit() function sets errno to EPERM if needed. */ if (setrlimit(RLIMIT_FSIZE, &limit) == -1) return (-1); + if (arg * 512 > LONG_MAX) + return (LONG_MAX); return (arg); } else { errno = EINVAL; return (-1); } } Index: projects/building-blocks/lib/libc/include/libc_private.h =================================================================== --- projects/building-blocks/lib/libc/include/libc_private.h (revision 278776) +++ projects/building-blocks/lib/libc/include/libc_private.h (revision 278777) @@ -1,375 +1,380 @@ /* * Copyright (c) 1998 John Birrell . * 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 author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 REGENTS 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. * * $FreeBSD$ * * Private definitions for libc, libc_r and libpthread. * */ #ifndef _LIBC_PRIVATE_H_ #define _LIBC_PRIVATE_H_ #include #include /* * This global flag is non-zero when a process has created one * or more threads. It is used to avoid calling locking functions * when they are not required. */ extern int __isthreaded; /* * Elf_Auxinfo *__elf_aux_vector, the pointer to the ELF aux vector * provided by kernel. Either set for us by rtld, or found at runtime * on stack for static binaries. * * Type is void to avoid polluting whole libc with ELF types. */ extern void *__elf_aux_vector; /* * libc should use libc_dlopen internally, which respects a global * flag where loading of new shared objects can be restricted. */ void *libc_dlopen(const char *, int); /* * For dynamic linker. */ void _rtld_error(const char *fmt, ...); /* * File lock contention is difficult to diagnose without knowing * where locks were set. Allow a debug library to be built which * records the source file and line number of each lock call. */ #ifdef _FLOCK_DEBUG #define _FLOCKFILE(x) _flockfile_debug(x, __FILE__, __LINE__) #else #define _FLOCKFILE(x) _flockfile(x) #endif /* * Macros for locking and unlocking FILEs. These test if the * process is threaded to avoid locking when not required. */ #define FLOCKFILE(fp) if (__isthreaded) _FLOCKFILE(fp) #define FUNLOCKFILE(fp) if (__isthreaded) _funlockfile(fp) struct _spinlock; extern struct _spinlock __stdio_thread_lock __hidden; #define STDIO_THREAD_LOCK() \ do { \ if (__isthreaded) \ _SPINLOCK(&__stdio_thread_lock); \ } while (0) #define STDIO_THREAD_UNLOCK() \ do { \ if (__isthreaded) \ _SPINUNLOCK(&__stdio_thread_lock); \ } while (0) +void __libc_spinlock_stub(struct _spinlock *); +void __libc_spinunlock_stub(struct _spinlock *); + /* * Indexes into the pthread jump table. * * Warning! If you change this type, you must also change the threads * libraries that reference it (libc_r, libpthread). */ typedef enum { PJT_ATFORK, PJT_ATTR_DESTROY, PJT_ATTR_GETDETACHSTATE, PJT_ATTR_GETGUARDSIZE, PJT_ATTR_GETINHERITSCHED, PJT_ATTR_GETSCHEDPARAM, PJT_ATTR_GETSCHEDPOLICY, PJT_ATTR_GETSCOPE, PJT_ATTR_GETSTACKADDR, PJT_ATTR_GETSTACKSIZE, PJT_ATTR_INIT, PJT_ATTR_SETDETACHSTATE, PJT_ATTR_SETGUARDSIZE, PJT_ATTR_SETINHERITSCHED, PJT_ATTR_SETSCHEDPARAM, PJT_ATTR_SETSCHEDPOLICY, PJT_ATTR_SETSCOPE, PJT_ATTR_SETSTACKADDR, PJT_ATTR_SETSTACKSIZE, PJT_CANCEL, PJT_CLEANUP_POP, PJT_CLEANUP_PUSH, PJT_COND_BROADCAST, PJT_COND_DESTROY, PJT_COND_INIT, PJT_COND_SIGNAL, PJT_COND_TIMEDWAIT, PJT_COND_WAIT, PJT_DETACH, PJT_EQUAL, PJT_EXIT, PJT_GETSPECIFIC, PJT_JOIN, PJT_KEY_CREATE, PJT_KEY_DELETE, PJT_KILL, PJT_MAIN_NP, PJT_MUTEXATTR_DESTROY, PJT_MUTEXATTR_INIT, PJT_MUTEXATTR_SETTYPE, PJT_MUTEX_DESTROY, PJT_MUTEX_INIT, PJT_MUTEX_LOCK, PJT_MUTEX_TRYLOCK, PJT_MUTEX_UNLOCK, PJT_ONCE, PJT_RWLOCK_DESTROY, PJT_RWLOCK_INIT, PJT_RWLOCK_RDLOCK, PJT_RWLOCK_TRYRDLOCK, PJT_RWLOCK_TRYWRLOCK, PJT_RWLOCK_UNLOCK, PJT_RWLOCK_WRLOCK, PJT_SELF, PJT_SETCANCELSTATE, PJT_SETCANCELTYPE, PJT_SETSPECIFIC, PJT_SIGMASK, PJT_TESTCANCEL, PJT_CLEANUP_POP_IMP, PJT_CLEANUP_PUSH_IMP, PJT_CANCEL_ENTER, PJT_CANCEL_LEAVE, PJT_MAX } pjt_index_t; typedef int (*pthread_func_t)(void); typedef pthread_func_t pthread_func_entry_t[2]; extern pthread_func_entry_t __thr_jtable[]; void __set_error_selector(int *(*arg)(void)); int _pthread_mutex_init_calloc_cb_stub(pthread_mutex_t *mutex, void *(calloc_cb)(__size_t, __size_t)); typedef int (*interpos_func_t)(void); interpos_func_t *__libc_interposing_slot(int interposno); extern interpos_func_t __libc_interposing[] __hidden; enum { INTERPOS_accept, INTERPOS_accept4, INTERPOS_aio_suspend, INTERPOS_close, INTERPOS_connect, INTERPOS_fcntl, INTERPOS_fsync, INTERPOS_fork, INTERPOS_msync, INTERPOS_nanosleep, INTERPOS_openat, INTERPOS_poll, INTERPOS_pselect, INTERPOS_recvfrom, INTERPOS_recvmsg, INTERPOS_select, INTERPOS_sendmsg, INTERPOS_sendto, INTERPOS_setcontext, INTERPOS_sigaction, INTERPOS_sigprocmask, INTERPOS_sigsuspend, INTERPOS_sigwait, INTERPOS_sigtimedwait, INTERPOS_sigwaitinfo, INTERPOS_swapcontext, INTERPOS_system, INTERPOS_tcdrain, INTERPOS_read, INTERPOS_readv, INTERPOS_wait4, INTERPOS_write, INTERPOS_writev, INTERPOS__pthread_mutex_init_calloc_cb, + INTERPOS_spinlock, + INTERPOS_spinunlock, INTERPOS_MAX }; /* * yplib internal interfaces */ #ifdef YP int _yp_check(char **); #endif /* * Initialise TLS for static programs */ void _init_tls(void); /* * Provides pthread_once()-like functionality for both single-threaded * and multi-threaded applications. */ int _once(pthread_once_t *, void (*)(void)); /* * Set the TLS thread pointer */ void _set_tp(void *tp); /* * This is a pointer in the C run-time startup code. It is used * by getprogname() and setprogname(). */ extern const char *__progname; /* * This function is used by the threading libraries to notify malloc that a * thread is exiting. */ void _malloc_thread_cleanup(void); /* * These functions are used by the threading libraries in order to protect * malloc across fork(). */ void _malloc_prefork(void); void _malloc_postfork(void); void _malloc_first_thread(void); /* * Function to clean up streams, called from abort() and exit(). */ void (*__cleanup)(void) __hidden; /* * Get kern.osreldate to detect ABI revisions. Explicitly * ignores value of $OSVERSION and caches result. Prototypes * for the wrapped "new" pad-less syscalls are here for now. */ int __getosreldate(void); #include #include /* With pad */ __off_t __sys_freebsd6_lseek(int, int, __off_t, int); int __sys_freebsd6_ftruncate(int, int, __off_t); int __sys_freebsd6_truncate(const char *, int, __off_t); __ssize_t __sys_freebsd6_pread(int, void *, __size_t, int, __off_t); __ssize_t __sys_freebsd6_pwrite(int, const void *, __size_t, int, __off_t); void * __sys_freebsd6_mmap(void *, __size_t, int, int, int, int, __off_t); struct aiocb; struct fd_set; struct iovec; struct msghdr; struct pollfd; struct rusage; struct sigaction; struct sockaddr; struct timespec; struct timeval; struct timezone; struct __siginfo; struct __ucontext; int __sys_aio_suspend(const struct aiocb * const[], int, const struct timespec *); int __sys_accept(int, struct sockaddr *, __socklen_t *); int __sys_accept4(int, struct sockaddr *, __socklen_t *, int); int __sys_clock_gettime(__clockid_t, struct timespec *ts); int __sys_close(int); int __sys_connect(int, const struct sockaddr *, __socklen_t); int __sys_fcntl(int, int, ...); int __sys_fsync(int); __pid_t __sys_fork(void); int __sys_ftruncate(int, __off_t); int __sys_gettimeofday(struct timeval *, struct timezone *); __off_t __sys_lseek(int, __off_t, int); void *__sys_mmap(void *, __size_t, int, int, int, __off_t); int __sys_msync(void *, __size_t, int); int __sys_nanosleep(const struct timespec *, struct timespec *); int __sys_open(const char *, int, ...); int __sys_openat(int, const char *, int, ...); int __sys_pselect(int, struct fd_set *, struct fd_set *, struct fd_set *, const struct timespec *, const __sigset_t *); int __sys_poll(struct pollfd *, unsigned, int); __ssize_t __sys_pread(int, void *, __size_t, __off_t); __ssize_t __sys_pwrite(int, const void *, __size_t, __off_t); __ssize_t __sys_read(int, void *, __size_t); __ssize_t __sys_readv(int, const struct iovec *, int); __ssize_t __sys_recv(int, void *, __size_t, int); __ssize_t __sys_recvfrom(int, void *, __size_t, int, struct sockaddr *, __socklen_t *); __ssize_t __sys_recvmsg(int, struct msghdr *, int); int __sys_select(int, struct fd_set *, struct fd_set *, struct fd_set *, struct timeval *); __ssize_t __sys_sendmsg(int, const struct msghdr *, int); __ssize_t __sys_sendto(int, const void *, __size_t, int, const struct sockaddr *, __socklen_t); int __sys_setcontext(const struct __ucontext *); int __sys_sigaction(int, const struct sigaction *, struct sigaction *); int __sys_sigprocmask(int, const __sigset_t *, __sigset_t *); int __sys_sigsuspend(const __sigset_t *); int __sys_sigtimedwait(const __sigset_t *, struct __siginfo *, const struct timespec *); int __sys_sigwait(const __sigset_t *, int *); int __sys_sigwaitinfo(const __sigset_t *, struct __siginfo *); int __sys_swapcontext(struct __ucontext *, const struct __ucontext *); int __sys_thr_kill(long, int); int __sys_thr_self(long *); int __sys_truncate(const char *, __off_t); __pid_t __sys_wait4(__pid_t, int *, int, struct rusage *); __ssize_t __sys_write(int, const void *, __size_t); __ssize_t __sys_writev(int, const struct iovec *, int); int __libc_sigwait(const __sigset_t * __restrict, int * restrict sig); int __libc_system(const char *); int __libc_tcdrain(int); int __fcntl_compat(int fd, int cmd, ...); int __sys_futimens(int fd, const struct timespec *times) __hidden; int __sys_utimensat(int fd, const char *path, const struct timespec *times, int flag) __hidden; /* execve() with PATH processing to implement posix_spawnp() */ int _execvpe(const char *, char * const *, char * const *); int _elf_aux_info(int aux, void *buf, int buflen); struct dl_phdr_info; int __elf_phdr_match_addr(struct dl_phdr_info *, void *); void __init_elf_aux_vector(void); void _pthread_cancel_enter(int); void _pthread_cancel_leave(int); #endif /* _LIBC_PRIVATE_H_ */ Index: projects/building-blocks/lib/libc/regex/regcomp.c =================================================================== --- projects/building-blocks/lib/libc/regex/regcomp.c (revision 278776) +++ projects/building-blocks/lib/libc/regex/regcomp.c (revision 278777) @@ -1,1801 +1,1818 @@ /*- * Copyright (c) 1992, 1993, 1994 Henry Spencer. * Copyright (c) 1992, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Copyright (c) 2011 The FreeBSD Foundation * All rights reserved. * Portions of this software were developed by David Chisnall * under sponsorship from the FreeBSD Foundation. * * This code is derived from software contributed to Berkeley by * Henry Spencer. * * 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. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)regcomp.c 8.5 (Berkeley) 3/20/94 */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)regcomp.c 8.5 (Berkeley) 3/20/94"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include "collate.h" #include "utils.h" #include "regex2.h" #include "cname.h" /* * parse structure, passed up and down to avoid global variables and * other clumsinesses */ struct parse { char *next; /* next character in RE */ char *end; /* end of string (-> NUL normally) */ int error; /* has an error been seen? */ sop *strip; /* malloced strip */ sopno ssize; /* malloced strip size (allocated) */ sopno slen; /* malloced strip length (used) */ int ncsalloc; /* number of csets allocated */ struct re_guts *g; # define NPAREN 10 /* we need to remember () 1-9 for back refs */ sopno pbegin[NPAREN]; /* -> ( ([0] unused) */ sopno pend[NPAREN]; /* -> ) ([0] unused) */ }; /* ========= begin header generated by ./mkh ========= */ #ifdef __cplusplus extern "C" { #endif /* === regcomp.c === */ static void p_ere(struct parse *p, int stop); static void p_ere_exp(struct parse *p); static void p_str(struct parse *p); static void p_bre(struct parse *p, int end1, int end2); static int p_simp_re(struct parse *p, int starordinary); static int p_count(struct parse *p); static void p_bracket(struct parse *p); static void p_b_term(struct parse *p, cset *cs); static void p_b_cclass(struct parse *p, cset *cs); static void p_b_eclass(struct parse *p, cset *cs); static wint_t p_b_symbol(struct parse *p); static wint_t p_b_coll_elem(struct parse *p, wint_t endc); static wint_t othercase(wint_t ch); static void bothcases(struct parse *p, wint_t ch); static void ordinary(struct parse *p, wint_t ch); static void nonnewline(struct parse *p); static void repeat(struct parse *p, sopno start, int from, int to); static int seterr(struct parse *p, int e); static cset *allocset(struct parse *p); static void freeset(struct parse *p, cset *cs); static void CHadd(struct parse *p, cset *cs, wint_t ch); static void CHaddrange(struct parse *p, cset *cs, wint_t min, wint_t max); static void CHaddtype(struct parse *p, cset *cs, wctype_t wct); static wint_t singleton(cset *cs); static sopno dupl(struct parse *p, sopno start, sopno finish); static void doemit(struct parse *p, sop op, size_t opnd); static void doinsert(struct parse *p, sop op, size_t opnd, sopno pos); static void dofwd(struct parse *p, sopno pos, sop value); static int enlarge(struct parse *p, sopno size); static void stripsnug(struct parse *p, struct re_guts *g); static void findmust(struct parse *p, struct re_guts *g); static int altoffset(sop *scan, int offset); static void computejumps(struct parse *p, struct re_guts *g); static void computematchjumps(struct parse *p, struct re_guts *g); static sopno pluscount(struct parse *p, struct re_guts *g); static wint_t wgetnext(struct parse *p); #ifdef __cplusplus } #endif /* ========= end header generated by ./mkh ========= */ static char nuls[10]; /* place to point scanner in event of error */ /* * macros for use with parse structure * BEWARE: these know that the parse structure is named `p' !!! */ #define PEEK() (*p->next) #define PEEK2() (*(p->next+1)) #define MORE() (p->next < p->end) #define MORE2() (p->next+1 < p->end) #define SEE(c) (MORE() && PEEK() == (c)) #define SEETWO(a, b) (MORE() && MORE2() && PEEK() == (a) && PEEK2() == (b)) #define EAT(c) ((SEE(c)) ? (NEXT(), 1) : 0) #define EATTWO(a, b) ((SEETWO(a, b)) ? (NEXT2(), 1) : 0) #define NEXT() (p->next++) #define NEXT2() (p->next += 2) #define NEXTn(n) (p->next += (n)) #define GETNEXT() (*p->next++) #define WGETNEXT() wgetnext(p) #define SETERROR(e) seterr(p, (e)) #define REQUIRE(co, e) ((co) || SETERROR(e)) #define MUSTSEE(c, e) (REQUIRE(MORE() && PEEK() == (c), e)) #define MUSTEAT(c, e) (REQUIRE(MORE() && GETNEXT() == (c), e)) #define MUSTNOTSEE(c, e) (REQUIRE(!MORE() || PEEK() != (c), e)) #define EMIT(op, sopnd) doemit(p, (sop)(op), (size_t)(sopnd)) #define INSERT(op, pos) doinsert(p, (sop)(op), HERE()-(pos)+1, pos) #define AHEAD(pos) dofwd(p, pos, HERE()-(pos)) #define ASTERN(sop, pos) EMIT(sop, HERE()-pos) #define HERE() (p->slen) #define THERE() (p->slen - 1) #define THERETHERE() (p->slen - 2) #define DROP(n) (p->slen -= (n)) #ifndef NDEBUG static int never = 0; /* for use in asserts; shuts lint up */ #else #define never 0 /* some s have bugs too */ #endif /* Macro used by computejump()/computematchjump() */ #define MIN(a,b) ((a)<(b)?(a):(b)) /* - regcomp - interface for parser and compilation = extern int regcomp(regex_t *, const char *, int); = #define REG_BASIC 0000 = #define REG_EXTENDED 0001 = #define REG_ICASE 0002 = #define REG_NOSUB 0004 = #define REG_NEWLINE 0010 = #define REG_NOSPEC 0020 = #define REG_PEND 0040 = #define REG_DUMP 0200 */ int /* 0 success, otherwise REG_something */ regcomp(regex_t * __restrict preg, const char * __restrict pattern, int cflags) { struct parse pa; struct re_guts *g; struct parse *p = &pa; int i; size_t len; + size_t maxlen; #ifdef REDEBUG # define GOODFLAGS(f) (f) #else # define GOODFLAGS(f) ((f)&~REG_DUMP) #endif cflags = GOODFLAGS(cflags); if ((cflags®_EXTENDED) && (cflags®_NOSPEC)) return(REG_INVARG); if (cflags®_PEND) { if (preg->re_endp < pattern) return(REG_INVARG); len = preg->re_endp - pattern; } else len = strlen((char *)pattern); /* do the mallocs early so failure handling is easy */ g = (struct re_guts *)malloc(sizeof(struct re_guts)); if (g == NULL) return(REG_ESPACE); + /* + * Limit the pattern space to avoid a 32-bit overflow on buffer + * extension. Also avoid any signed overflow in case of conversion + * so make the real limit based on a 31-bit overflow. + * + * Likely not applicable on 64-bit systems but handle the case + * generically (who are we to stop people from using ~715MB+ + * patterns?). + */ + maxlen = ((size_t)-1 >> 1) / sizeof(sop) * 2 / 3; + if (len >= maxlen) { + free((char *)g); + return(REG_ESPACE); + } p->ssize = len/(size_t)2*(size_t)3 + (size_t)1; /* ugh */ + assert(p->ssize >= len); + p->strip = (sop *)malloc(p->ssize * sizeof(sop)); p->slen = 0; if (p->strip == NULL) { free((char *)g); return(REG_ESPACE); } /* set things up */ p->g = g; p->next = (char *)pattern; /* convenience; we do not modify it */ p->end = p->next + len; p->error = 0; p->ncsalloc = 0; for (i = 0; i < NPAREN; i++) { p->pbegin[i] = 0; p->pend[i] = 0; } g->sets = NULL; g->ncsets = 0; g->cflags = cflags; g->iflags = 0; g->nbol = 0; g->neol = 0; g->must = NULL; g->moffset = -1; g->charjump = NULL; g->matchjump = NULL; g->mlen = 0; g->nsub = 0; g->backrefs = 0; /* do it */ EMIT(OEND, 0); g->firststate = THERE(); if (cflags®_EXTENDED) p_ere(p, OUT); else if (cflags®_NOSPEC) p_str(p); else p_bre(p, OUT, OUT); EMIT(OEND, 0); g->laststate = THERE(); /* tidy up loose ends and fill things in */ stripsnug(p, g); findmust(p, g); /* only use Boyer-Moore algorithm if the pattern is bigger * than three characters */ if(g->mlen > 3) { computejumps(p, g); computematchjumps(p, g); if(g->matchjump == NULL && g->charjump != NULL) { free(g->charjump); g->charjump = NULL; } } g->nplus = pluscount(p, g); g->magic = MAGIC2; preg->re_nsub = g->nsub; preg->re_g = g; preg->re_magic = MAGIC1; #ifndef REDEBUG /* not debugging, so can't rely on the assert() in regexec() */ if (g->iflags&BAD) SETERROR(REG_ASSERT); #endif /* win or lose, we're done */ if (p->error != 0) /* lose */ regfree(preg); return(p->error); } /* - p_ere - ERE parser top level, concatenation and alternation == static void p_ere(struct parse *p, int_t stop); */ static void p_ere(struct parse *p, int stop) /* character this ERE should end at */ { char c; sopno prevback; sopno prevfwd; sopno conc; int first = 1; /* is this the first alternative? */ for (;;) { /* do a bunch of concatenated expressions */ conc = HERE(); while (MORE() && (c = PEEK()) != '|' && c != stop) p_ere_exp(p); (void)REQUIRE(HERE() != conc, REG_EMPTY); /* require nonempty */ if (!EAT('|')) break; /* NOTE BREAK OUT */ if (first) { INSERT(OCH_, conc); /* offset is wrong */ prevfwd = conc; prevback = conc; first = 0; } ASTERN(OOR1, prevback); prevback = THERE(); AHEAD(prevfwd); /* fix previous offset */ prevfwd = HERE(); EMIT(OOR2, 0); /* offset is very wrong */ } if (!first) { /* tail-end fixups */ AHEAD(prevfwd); ASTERN(O_CH, prevback); } assert(!MORE() || SEE(stop)); } /* - p_ere_exp - parse one subERE, an atom possibly followed by a repetition op == static void p_ere_exp(struct parse *p); */ static void p_ere_exp(struct parse *p) { char c; wint_t wc; sopno pos; int count; int count2; sopno subno; int wascaret = 0; assert(MORE()); /* caller should have ensured this */ c = GETNEXT(); pos = HERE(); switch (c) { case '(': (void)REQUIRE(MORE(), REG_EPAREN); p->g->nsub++; subno = p->g->nsub; if (subno < NPAREN) p->pbegin[subno] = HERE(); EMIT(OLPAREN, subno); if (!SEE(')')) p_ere(p, ')'); if (subno < NPAREN) { p->pend[subno] = HERE(); assert(p->pend[subno] != 0); } EMIT(ORPAREN, subno); (void)MUSTEAT(')', REG_EPAREN); break; #ifndef POSIX_MISTAKE case ')': /* happens only if no current unmatched ( */ /* * You may ask, why the ifndef? Because I didn't notice * this until slightly too late for 1003.2, and none of the * other 1003.2 regular-expression reviewers noticed it at * all. So an unmatched ) is legal POSIX, at least until * we can get it fixed. */ SETERROR(REG_EPAREN); break; #endif case '^': EMIT(OBOL, 0); p->g->iflags |= USEBOL; p->g->nbol++; wascaret = 1; break; case '$': EMIT(OEOL, 0); p->g->iflags |= USEEOL; p->g->neol++; break; case '|': SETERROR(REG_EMPTY); break; case '*': case '+': case '?': SETERROR(REG_BADRPT); break; case '.': if (p->g->cflags®_NEWLINE) nonnewline(p); else EMIT(OANY, 0); break; case '[': p_bracket(p); break; case '\\': (void)REQUIRE(MORE(), REG_EESCAPE); wc = WGETNEXT(); switch (wc) { case '<': EMIT(OBOW, 0); break; case '>': EMIT(OEOW, 0); break; default: ordinary(p, wc); break; } break; case '{': /* okay as ordinary except if digit follows */ (void)REQUIRE(!MORE() || !isdigit((uch)PEEK()), REG_BADRPT); /* FALLTHROUGH */ default: p->next--; wc = WGETNEXT(); ordinary(p, wc); break; } if (!MORE()) return; c = PEEK(); /* we call { a repetition if followed by a digit */ if (!( c == '*' || c == '+' || c == '?' || (c == '{' && MORE2() && isdigit((uch)PEEK2())) )) return; /* no repetition, we're done */ NEXT(); (void)REQUIRE(!wascaret, REG_BADRPT); switch (c) { case '*': /* implemented as +? */ /* this case does not require the (y|) trick, noKLUDGE */ INSERT(OPLUS_, pos); ASTERN(O_PLUS, pos); INSERT(OQUEST_, pos); ASTERN(O_QUEST, pos); break; case '+': INSERT(OPLUS_, pos); ASTERN(O_PLUS, pos); break; case '?': /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */ INSERT(OCH_, pos); /* offset slightly wrong */ ASTERN(OOR1, pos); /* this one's right */ AHEAD(pos); /* fix the OCH_ */ EMIT(OOR2, 0); /* offset very wrong... */ AHEAD(THERE()); /* ...so fix it */ ASTERN(O_CH, THERETHERE()); break; case '{': count = p_count(p); if (EAT(',')) { if (isdigit((uch)PEEK())) { count2 = p_count(p); (void)REQUIRE(count <= count2, REG_BADBR); } else /* single number with comma */ count2 = INFINITY; } else /* just a single number */ count2 = count; repeat(p, pos, count, count2); if (!EAT('}')) { /* error heuristics */ while (MORE() && PEEK() != '}') NEXT(); (void)REQUIRE(MORE(), REG_EBRACE); SETERROR(REG_BADBR); } break; } if (!MORE()) return; c = PEEK(); if (!( c == '*' || c == '+' || c == '?' || (c == '{' && MORE2() && isdigit((uch)PEEK2())) ) ) return; SETERROR(REG_BADRPT); } /* - p_str - string (no metacharacters) "parser" == static void p_str(struct parse *p); */ static void p_str(struct parse *p) { (void)REQUIRE(MORE(), REG_EMPTY); while (MORE()) ordinary(p, WGETNEXT()); } /* - p_bre - BRE parser top level, anchoring and concatenation == static void p_bre(struct parse *p, int end1, \ == int end2); * Giving end1 as OUT essentially eliminates the end1/end2 check. * * This implementation is a bit of a kludge, in that a trailing $ is first * taken as an ordinary character and then revised to be an anchor. * The amount of lookahead needed to avoid this kludge is excessive. */ static void p_bre(struct parse *p, int end1, /* first terminating character */ int end2) /* second terminating character */ { sopno start = HERE(); int first = 1; /* first subexpression? */ int wasdollar = 0; if (EAT('^')) { EMIT(OBOL, 0); p->g->iflags |= USEBOL; p->g->nbol++; } while (MORE() && !SEETWO(end1, end2)) { wasdollar = p_simp_re(p, first); first = 0; } if (wasdollar) { /* oops, that was a trailing anchor */ DROP(1); EMIT(OEOL, 0); p->g->iflags |= USEEOL; p->g->neol++; } (void)REQUIRE(HERE() != start, REG_EMPTY); /* require nonempty */ } /* - p_simp_re - parse a simple RE, an atom possibly followed by a repetition == static int p_simp_re(struct parse *p, int starordinary); */ static int /* was the simple RE an unbackslashed $? */ p_simp_re(struct parse *p, int starordinary) /* is a leading * an ordinary character? */ { int c; int count; int count2; sopno pos; int i; wint_t wc; sopno subno; # define BACKSL (1<g->cflags®_NEWLINE) nonnewline(p); else EMIT(OANY, 0); break; case '[': p_bracket(p); break; case BACKSL|'<': EMIT(OBOW, 0); break; case BACKSL|'>': EMIT(OEOW, 0); break; case BACKSL|'{': SETERROR(REG_BADRPT); break; case BACKSL|'(': p->g->nsub++; subno = p->g->nsub; if (subno < NPAREN) p->pbegin[subno] = HERE(); EMIT(OLPAREN, subno); /* the MORE here is an error heuristic */ if (MORE() && !SEETWO('\\', ')')) p_bre(p, '\\', ')'); if (subno < NPAREN) { p->pend[subno] = HERE(); assert(p->pend[subno] != 0); } EMIT(ORPAREN, subno); (void)REQUIRE(EATTWO('\\', ')'), REG_EPAREN); break; case BACKSL|')': /* should not get here -- must be user */ case BACKSL|'}': SETERROR(REG_EPAREN); break; case BACKSL|'1': case BACKSL|'2': case BACKSL|'3': case BACKSL|'4': case BACKSL|'5': case BACKSL|'6': case BACKSL|'7': case BACKSL|'8': case BACKSL|'9': i = (c&~BACKSL) - '0'; assert(i < NPAREN); if (p->pend[i] != 0) { assert(i <= p->g->nsub); EMIT(OBACK_, i); assert(p->pbegin[i] != 0); assert(OP(p->strip[p->pbegin[i]]) == OLPAREN); assert(OP(p->strip[p->pend[i]]) == ORPAREN); (void) dupl(p, p->pbegin[i]+1, p->pend[i]); EMIT(O_BACK, i); } else SETERROR(REG_ESUBREG); p->g->backrefs = 1; break; case '*': (void)REQUIRE(starordinary, REG_BADRPT); /* FALLTHROUGH */ default: p->next--; wc = WGETNEXT(); ordinary(p, wc); break; } if (EAT('*')) { /* implemented as +? */ /* this case does not require the (y|) trick, noKLUDGE */ INSERT(OPLUS_, pos); ASTERN(O_PLUS, pos); INSERT(OQUEST_, pos); ASTERN(O_QUEST, pos); } else if (EATTWO('\\', '{')) { count = p_count(p); if (EAT(',')) { if (MORE() && isdigit((uch)PEEK())) { count2 = p_count(p); (void)REQUIRE(count <= count2, REG_BADBR); } else /* single number with comma */ count2 = INFINITY; } else /* just a single number */ count2 = count; repeat(p, pos, count, count2); if (!EATTWO('\\', '}')) { /* error heuristics */ while (MORE() && !SEETWO('\\', '}')) NEXT(); (void)REQUIRE(MORE(), REG_EBRACE); SETERROR(REG_BADBR); } } else if (c == '$') /* $ (but not \$) ends it */ return(1); return(0); } /* - p_count - parse a repetition count == static int p_count(struct parse *p); */ static int /* the value */ p_count(struct parse *p) { int count = 0; int ndigits = 0; while (MORE() && isdigit((uch)PEEK()) && count <= DUPMAX) { count = count*10 + (GETNEXT() - '0'); ndigits++; } (void)REQUIRE(ndigits > 0 && count <= DUPMAX, REG_BADBR); return(count); } /* - p_bracket - parse a bracketed character list == static void p_bracket(struct parse *p); */ static void p_bracket(struct parse *p) { cset *cs; wint_t ch; /* Dept of Truly Sickening Special-Case Kludges */ if (p->next + 5 < p->end && strncmp(p->next, "[:<:]]", 6) == 0) { EMIT(OBOW, 0); NEXTn(6); return; } if (p->next + 5 < p->end && strncmp(p->next, "[:>:]]", 6) == 0) { EMIT(OEOW, 0); NEXTn(6); return; } if ((cs = allocset(p)) == NULL) return; if (p->g->cflags®_ICASE) cs->icase = 1; if (EAT('^')) cs->invert = 1; if (EAT(']')) CHadd(p, cs, ']'); else if (EAT('-')) CHadd(p, cs, '-'); while (MORE() && PEEK() != ']' && !SEETWO('-', ']')) p_b_term(p, cs); if (EAT('-')) CHadd(p, cs, '-'); (void)MUSTEAT(']', REG_EBRACK); if (p->error != 0) /* don't mess things up further */ return; if (cs->invert && p->g->cflags®_NEWLINE) cs->bmp['\n' >> 3] |= 1 << ('\n' & 7); if ((ch = singleton(cs)) != OUT) { /* optimize singleton sets */ ordinary(p, ch); freeset(p, cs); } else EMIT(OANYOF, (int)(cs - p->g->sets)); } /* - p_b_term - parse one term of a bracketed character list == static void p_b_term(struct parse *p, cset *cs); */ static void p_b_term(struct parse *p, cset *cs) { char c; wint_t start, finish; wint_t i; struct xlocale_collate *table = (struct xlocale_collate*)__get_locale()->components[XLC_COLLATE]; /* classify what we've got */ switch ((MORE()) ? PEEK() : '\0') { case '[': c = (MORE2()) ? PEEK2() : '\0'; break; case '-': SETERROR(REG_ERANGE); return; /* NOTE RETURN */ default: c = '\0'; break; } switch (c) { case ':': /* character class */ NEXT2(); (void)REQUIRE(MORE(), REG_EBRACK); c = PEEK(); (void)REQUIRE(c != '-' && c != ']', REG_ECTYPE); p_b_cclass(p, cs); (void)REQUIRE(MORE(), REG_EBRACK); (void)REQUIRE(EATTWO(':', ']'), REG_ECTYPE); break; case '=': /* equivalence class */ NEXT2(); (void)REQUIRE(MORE(), REG_EBRACK); c = PEEK(); (void)REQUIRE(c != '-' && c != ']', REG_ECOLLATE); p_b_eclass(p, cs); (void)REQUIRE(MORE(), REG_EBRACK); (void)REQUIRE(EATTWO('=', ']'), REG_ECOLLATE); break; default: /* symbol, ordinary character, or range */ start = p_b_symbol(p); if (SEE('-') && MORE2() && PEEK2() != ']') { /* range */ NEXT(); if (EAT('-')) finish = '-'; else finish = p_b_symbol(p); } else finish = start; if (start == finish) CHadd(p, cs, start); else { if (table->__collate_load_error) { (void)REQUIRE((uch)start <= (uch)finish, REG_ERANGE); CHaddrange(p, cs, start, finish); } else { (void)REQUIRE(__collate_range_cmp(table, start, finish) <= 0, REG_ERANGE); for (i = 0; i <= UCHAR_MAX; i++) { if ( __collate_range_cmp(table, start, i) <= 0 && __collate_range_cmp(table, i, finish) <= 0 ) CHadd(p, cs, i); } } } break; } } /* - p_b_cclass - parse a character-class name and deal with it == static void p_b_cclass(struct parse *p, cset *cs); */ static void p_b_cclass(struct parse *p, cset *cs) { char *sp = p->next; size_t len; wctype_t wct; char clname[16]; while (MORE() && isalpha((uch)PEEK())) NEXT(); len = p->next - sp; if (len >= sizeof(clname) - 1) { SETERROR(REG_ECTYPE); return; } memcpy(clname, sp, len); clname[len] = '\0'; if ((wct = wctype(clname)) == 0) { SETERROR(REG_ECTYPE); return; } CHaddtype(p, cs, wct); } /* - p_b_eclass - parse an equivalence-class name and deal with it == static void p_b_eclass(struct parse *p, cset *cs); * * This implementation is incomplete. xxx */ static void p_b_eclass(struct parse *p, cset *cs) { wint_t c; c = p_b_coll_elem(p, '='); CHadd(p, cs, c); } /* - p_b_symbol - parse a character or [..]ed multicharacter collating symbol == static wint_t p_b_symbol(struct parse *p); */ static wint_t /* value of symbol */ p_b_symbol(struct parse *p) { wint_t value; (void)REQUIRE(MORE(), REG_EBRACK); if (!EATTWO('[', '.')) return(WGETNEXT()); /* collating symbol */ value = p_b_coll_elem(p, '.'); (void)REQUIRE(EATTWO('.', ']'), REG_ECOLLATE); return(value); } /* - p_b_coll_elem - parse a collating-element name and look it up == static wint_t p_b_coll_elem(struct parse *p, wint_t endc); */ static wint_t /* value of collating element */ p_b_coll_elem(struct parse *p, wint_t endc) /* name ended by endc,']' */ { char *sp = p->next; struct cname *cp; int len; mbstate_t mbs; wchar_t wc; size_t clen; while (MORE() && !SEETWO(endc, ']')) NEXT(); if (!MORE()) { SETERROR(REG_EBRACK); return(0); } len = p->next - sp; for (cp = cnames; cp->name != NULL; cp++) if (strncmp(cp->name, sp, len) == 0 && cp->name[len] == '\0') return(cp->code); /* known name */ memset(&mbs, 0, sizeof(mbs)); if ((clen = mbrtowc(&wc, sp, len, &mbs)) == len) return (wc); /* single character */ else if (clen == (size_t)-1 || clen == (size_t)-2) SETERROR(REG_ILLSEQ); else SETERROR(REG_ECOLLATE); /* neither */ return(0); } /* - othercase - return the case counterpart of an alphabetic == static wint_t othercase(wint_t ch); */ static wint_t /* if no counterpart, return ch */ othercase(wint_t ch) { assert(iswalpha(ch)); if (iswupper(ch)) return(towlower(ch)); else if (iswlower(ch)) return(towupper(ch)); else /* peculiar, but could happen */ return(ch); } /* - bothcases - emit a dualcase version of a two-case character == static void bothcases(struct parse *p, wint_t ch); * * Boy, is this implementation ever a kludge... */ static void bothcases(struct parse *p, wint_t ch) { char *oldnext = p->next; char *oldend = p->end; char bracket[3 + MB_LEN_MAX]; size_t n; mbstate_t mbs; assert(othercase(ch) != ch); /* p_bracket() would recurse */ p->next = bracket; memset(&mbs, 0, sizeof(mbs)); n = wcrtomb(bracket, ch, &mbs); assert(n != (size_t)-1); bracket[n] = ']'; bracket[n + 1] = '\0'; p->end = bracket+n+1; p_bracket(p); assert(p->next == p->end); p->next = oldnext; p->end = oldend; } /* - ordinary - emit an ordinary character == static void ordinary(struct parse *p, wint_t ch); */ static void ordinary(struct parse *p, wint_t ch) { cset *cs; if ((p->g->cflags®_ICASE) && iswalpha(ch) && othercase(ch) != ch) bothcases(p, ch); else if ((ch & OPDMASK) == ch) EMIT(OCHAR, ch); else { /* * Kludge: character is too big to fit into an OCHAR operand. * Emit a singleton set. */ if ((cs = allocset(p)) == NULL) return; CHadd(p, cs, ch); EMIT(OANYOF, (int)(cs - p->g->sets)); } } /* - nonnewline - emit REG_NEWLINE version of OANY == static void nonnewline(struct parse *p); * * Boy, is this implementation ever a kludge... */ static void nonnewline(struct parse *p) { char *oldnext = p->next; char *oldend = p->end; char bracket[4]; p->next = bracket; p->end = bracket+3; bracket[0] = '^'; bracket[1] = '\n'; bracket[2] = ']'; bracket[3] = '\0'; p_bracket(p); assert(p->next == bracket+3); p->next = oldnext; p->end = oldend; } /* - repeat - generate code for a bounded repetition, recursively if needed == static void repeat(struct parse *p, sopno start, int from, int to); */ static void repeat(struct parse *p, sopno start, /* operand from here to end of strip */ int from, /* repeated from this number */ int to) /* to this number of times (maybe INFINITY) */ { sopno finish = HERE(); # define N 2 # define INF 3 # define REP(f, t) ((f)*8 + (t)) # define MAP(n) (((n) <= 1) ? (n) : ((n) == INFINITY) ? INF : N) sopno copy; if (p->error != 0) /* head off possible runaway recursion */ return; assert(from <= to); switch (REP(MAP(from), MAP(to))) { case REP(0, 0): /* must be user doing this */ DROP(finish-start); /* drop the operand */ break; case REP(0, 1): /* as x{1,1}? */ case REP(0, N): /* as x{1,n}? */ case REP(0, INF): /* as x{1,}? */ /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */ INSERT(OCH_, start); /* offset is wrong... */ repeat(p, start+1, 1, to); ASTERN(OOR1, start); AHEAD(start); /* ... fix it */ EMIT(OOR2, 0); AHEAD(THERE()); ASTERN(O_CH, THERETHERE()); break; case REP(1, 1): /* trivial case */ /* done */ break; case REP(1, N): /* as x?x{1,n-1} */ /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */ INSERT(OCH_, start); ASTERN(OOR1, start); AHEAD(start); EMIT(OOR2, 0); /* offset very wrong... */ AHEAD(THERE()); /* ...so fix it */ ASTERN(O_CH, THERETHERE()); copy = dupl(p, start+1, finish+1); assert(copy == finish+4); repeat(p, copy, 1, to-1); break; case REP(1, INF): /* as x+ */ INSERT(OPLUS_, start); ASTERN(O_PLUS, start); break; case REP(N, N): /* as xx{m-1,n-1} */ copy = dupl(p, start, finish); repeat(p, copy, from-1, to-1); break; case REP(N, INF): /* as xx{n-1,INF} */ copy = dupl(p, start, finish); repeat(p, copy, from-1, to); break; default: /* "can't happen" */ SETERROR(REG_ASSERT); /* just in case */ break; } } /* - wgetnext - helper function for WGETNEXT() macro. Gets the next wide - character from the parse struct, signals a REG_ILLSEQ error if the - character can't be converted. Returns the number of bytes consumed. */ static wint_t wgetnext(struct parse *p) { mbstate_t mbs; wchar_t wc; size_t n; memset(&mbs, 0, sizeof(mbs)); n = mbrtowc(&wc, p->next, p->end - p->next, &mbs); if (n == (size_t)-1 || n == (size_t)-2) { SETERROR(REG_ILLSEQ); return (0); } if (n == 0) n = 1; p->next += n; return (wc); } /* - seterr - set an error condition == static int seterr(struct parse *p, int e); */ static int /* useless but makes type checking happy */ seterr(struct parse *p, int e) { if (p->error == 0) /* keep earliest error condition */ p->error = e; p->next = nuls; /* try to bring things to a halt */ p->end = nuls; return(0); /* make the return value well-defined */ } /* - allocset - allocate a set of characters for [] == static cset *allocset(struct parse *p); */ static cset * allocset(struct parse *p) { cset *cs, *ncs; ncs = realloc(p->g->sets, (p->g->ncsets + 1) * sizeof(*ncs)); if (ncs == NULL) { SETERROR(REG_ESPACE); return (NULL); } p->g->sets = ncs; cs = &p->g->sets[p->g->ncsets++]; memset(cs, 0, sizeof(*cs)); return(cs); } /* - freeset - free a now-unused set == static void freeset(struct parse *p, cset *cs); */ static void freeset(struct parse *p, cset *cs) { cset *top = &p->g->sets[p->g->ncsets]; free(cs->wides); free(cs->ranges); free(cs->types); memset(cs, 0, sizeof(*cs)); if (cs == top-1) /* recover only the easy case */ p->g->ncsets--; } /* - singleton - Determine whether a set contains only one character, - returning it if so, otherwise returning OUT. */ static wint_t singleton(cset *cs) { wint_t i, s, n; for (i = n = 0; i < NC; i++) if (CHIN(cs, i)) { n++; s = i; } if (n == 1) return (s); if (cs->nwides == 1 && cs->nranges == 0 && cs->ntypes == 0 && cs->icase == 0) return (cs->wides[0]); /* Don't bother handling the other cases. */ return (OUT); } /* - CHadd - add character to character set. */ static void CHadd(struct parse *p, cset *cs, wint_t ch) { wint_t nch, *newwides; assert(ch >= 0); if (ch < NC) cs->bmp[ch >> 3] |= 1 << (ch & 7); else { newwides = realloc(cs->wides, (cs->nwides + 1) * sizeof(*cs->wides)); if (newwides == NULL) { SETERROR(REG_ESPACE); return; } cs->wides = newwides; cs->wides[cs->nwides++] = ch; } if (cs->icase) { if ((nch = towlower(ch)) < NC) cs->bmp[nch >> 3] |= 1 << (nch & 7); if ((nch = towupper(ch)) < NC) cs->bmp[nch >> 3] |= 1 << (nch & 7); } } /* - CHaddrange - add all characters in the range [min,max] to a character set. */ static void CHaddrange(struct parse *p, cset *cs, wint_t min, wint_t max) { crange *newranges; for (; min < NC && min <= max; min++) CHadd(p, cs, min); if (min >= max) return; newranges = realloc(cs->ranges, (cs->nranges + 1) * sizeof(*cs->ranges)); if (newranges == NULL) { SETERROR(REG_ESPACE); return; } cs->ranges = newranges; cs->ranges[cs->nranges].min = min; cs->ranges[cs->nranges].max = max; cs->nranges++; } /* - CHaddtype - add all characters of a certain type to a character set. */ static void CHaddtype(struct parse *p, cset *cs, wctype_t wct) { wint_t i; wctype_t *newtypes; for (i = 0; i < NC; i++) if (iswctype(i, wct)) CHadd(p, cs, i); newtypes = realloc(cs->types, (cs->ntypes + 1) * sizeof(*cs->types)); if (newtypes == NULL) { SETERROR(REG_ESPACE); return; } cs->types = newtypes; cs->types[cs->ntypes++] = wct; } /* - dupl - emit a duplicate of a bunch of sops == static sopno dupl(struct parse *p, sopno start, sopno finish); */ static sopno /* start of duplicate */ dupl(struct parse *p, sopno start, /* from here */ sopno finish) /* to this less one */ { sopno ret = HERE(); sopno len = finish - start; assert(finish >= start); if (len == 0) return(ret); if (!enlarge(p, p->ssize + len)) /* this many unexpected additions */ return(ret); (void) memcpy((char *)(p->strip + p->slen), (char *)(p->strip + start), (size_t)len*sizeof(sop)); p->slen += len; return(ret); } /* - doemit - emit a strip operator == static void doemit(struct parse *p, sop op, size_t opnd); * * It might seem better to implement this as a macro with a function as * hard-case backup, but it's just too big and messy unless there are * some changes to the data structures. Maybe later. */ static void doemit(struct parse *p, sop op, size_t opnd) { /* avoid making error situations worse */ if (p->error != 0) return; /* deal with oversize operands ("can't happen", more or less) */ assert(opnd < 1<slen >= p->ssize) if (!enlarge(p, (p->ssize+1) / 2 * 3)) /* +50% */ return; /* finally, it's all reduced to the easy case */ p->strip[p->slen++] = SOP(op, opnd); } /* - doinsert - insert a sop into the strip == static void doinsert(struct parse *p, sop op, size_t opnd, sopno pos); */ static void doinsert(struct parse *p, sop op, size_t opnd, sopno pos) { sopno sn; sop s; int i; /* avoid making error situations worse */ if (p->error != 0) return; sn = HERE(); EMIT(op, opnd); /* do checks, ensure space */ assert(HERE() == sn+1); s = p->strip[sn]; /* adjust paren pointers */ assert(pos > 0); for (i = 1; i < NPAREN; i++) { if (p->pbegin[i] >= pos) { p->pbegin[i]++; } if (p->pend[i] >= pos) { p->pend[i]++; } } memmove((char *)&p->strip[pos+1], (char *)&p->strip[pos], (HERE()-pos-1)*sizeof(sop)); p->strip[pos] = s; } /* - dofwd - complete a forward reference == static void dofwd(struct parse *p, sopno pos, sop value); */ static void dofwd(struct parse *p, sopno pos, sop value) { /* avoid making error situations worse */ if (p->error != 0) return; assert(value < 1<strip[pos] = OP(p->strip[pos]) | value; } /* - enlarge - enlarge the strip == static int enlarge(struct parse *p, sopno size); */ static int enlarge(struct parse *p, sopno size) { sop *sp; if (p->ssize >= size) return 1; sp = (sop *)realloc(p->strip, size*sizeof(sop)); if (sp == NULL) { SETERROR(REG_ESPACE); return 0; } p->strip = sp; p->ssize = size; return 1; } /* - stripsnug - compact the strip == static void stripsnug(struct parse *p, struct re_guts *g); */ static void stripsnug(struct parse *p, struct re_guts *g) { g->nstates = p->slen; g->strip = (sop *)realloc((char *)p->strip, p->slen * sizeof(sop)); if (g->strip == NULL) { SETERROR(REG_ESPACE); g->strip = p->strip; } } /* - findmust - fill in must and mlen with longest mandatory literal string == static void findmust(struct parse *p, struct re_guts *g); * * This algorithm could do fancy things like analyzing the operands of | * for common subsequences. Someday. This code is simple and finds most * of the interesting cases. * * Note that must and mlen got initialized during setup. */ static void findmust(struct parse *p, struct re_guts *g) { sop *scan; sop *start; sop *newstart; sopno newlen; sop s; char *cp; int offset; char buf[MB_LEN_MAX]; size_t clen; mbstate_t mbs; /* avoid making error situations worse */ if (p->error != 0) return; /* * It's not generally safe to do a ``char'' substring search on * multibyte character strings, but it's safe for at least * UTF-8 (see RFC 3629). */ if (MB_CUR_MAX > 1 && strcmp(_CurrentRuneLocale->__encoding, "UTF-8") != 0) return; /* find the longest OCHAR sequence in strip */ newlen = 0; offset = 0; g->moffset = 0; scan = g->strip + 1; do { s = *scan++; switch (OP(s)) { case OCHAR: /* sequence member */ if (newlen == 0) { /* new sequence */ memset(&mbs, 0, sizeof(mbs)); newstart = scan - 1; } clen = wcrtomb(buf, OPND(s), &mbs); if (clen == (size_t)-1) goto toohard; newlen += clen; break; case OPLUS_: /* things that don't break one */ case OLPAREN: case ORPAREN: break; case OQUEST_: /* things that must be skipped */ case OCH_: offset = altoffset(scan, offset); scan--; do { scan += OPND(s); s = *scan; /* assert() interferes w debug printouts */ if (OP(s) != O_QUEST && OP(s) != O_CH && OP(s) != OOR2) { g->iflags |= BAD; return; } } while (OP(s) != O_QUEST && OP(s) != O_CH); /* FALLTHROUGH */ case OBOW: /* things that break a sequence */ case OEOW: case OBOL: case OEOL: case O_QUEST: case O_CH: case OEND: if (newlen > g->mlen) { /* ends one */ start = newstart; g->mlen = newlen; if (offset > -1) { g->moffset += offset; offset = newlen; } else g->moffset = offset; } else { if (offset > -1) offset += newlen; } newlen = 0; break; case OANY: if (newlen > g->mlen) { /* ends one */ start = newstart; g->mlen = newlen; if (offset > -1) { g->moffset += offset; offset = newlen; } else g->moffset = offset; } else { if (offset > -1) offset += newlen; } if (offset > -1) offset++; newlen = 0; break; case OANYOF: /* may or may not invalidate offset */ /* First, everything as OANY */ if (newlen > g->mlen) { /* ends one */ start = newstart; g->mlen = newlen; if (offset > -1) { g->moffset += offset; offset = newlen; } else g->moffset = offset; } else { if (offset > -1) offset += newlen; } if (offset > -1) offset++; newlen = 0; break; toohard: default: /* Anything here makes it impossible or too hard * to calculate the offset -- so we give up; * save the last known good offset, in case the * must sequence doesn't occur later. */ if (newlen > g->mlen) { /* ends one */ start = newstart; g->mlen = newlen; if (offset > -1) g->moffset += offset; else g->moffset = offset; } offset = -1; newlen = 0; break; } } while (OP(s) != OEND); if (g->mlen == 0) { /* there isn't one */ g->moffset = -1; return; } /* turn it into a character string */ g->must = malloc((size_t)g->mlen + 1); if (g->must == NULL) { /* argh; just forget it */ g->mlen = 0; g->moffset = -1; return; } cp = g->must; scan = start; memset(&mbs, 0, sizeof(mbs)); while (cp < g->must + g->mlen) { while (OP(s = *scan++) != OCHAR) continue; clen = wcrtomb(cp, OPND(s), &mbs); assert(clen != (size_t)-1); cp += clen; } assert(cp == g->must + g->mlen); *cp++ = '\0'; /* just on general principles */ } /* - altoffset - choose biggest offset among multiple choices == static int altoffset(sop *scan, int offset); * * Compute, recursively if necessary, the largest offset among multiple * re paths. */ static int altoffset(sop *scan, int offset) { int largest; int try; sop s; /* If we gave up already on offsets, return */ if (offset == -1) return -1; largest = 0; try = 0; s = *scan++; while (OP(s) != O_QUEST && OP(s) != O_CH) { switch (OP(s)) { case OOR1: if (try > largest) largest = try; try = 0; break; case OQUEST_: case OCH_: try = altoffset(scan, try); if (try == -1) return -1; scan--; do { scan += OPND(s); s = *scan; if (OP(s) != O_QUEST && OP(s) != O_CH && OP(s) != OOR2) return -1; } while (OP(s) != O_QUEST && OP(s) != O_CH); /* We must skip to the next position, or we'll * leave altoffset() too early. */ scan++; break; case OANYOF: case OCHAR: case OANY: try++; case OBOW: case OEOW: case OLPAREN: case ORPAREN: case OOR2: break; default: try = -1; break; } if (try == -1) return -1; s = *scan++; } if (try > largest) largest = try; return largest+offset; } /* - computejumps - compute char jumps for BM scan == static void computejumps(struct parse *p, struct re_guts *g); * * This algorithm assumes g->must exists and is has size greater than * zero. It's based on the algorithm found on Computer Algorithms by * Sara Baase. * * A char jump is the number of characters one needs to jump based on * the value of the character from the text that was mismatched. */ static void computejumps(struct parse *p, struct re_guts *g) { int ch; int mindex; /* Avoid making errors worse */ if (p->error != 0) return; g->charjump = (int*) malloc((NC + 1) * sizeof(int)); if (g->charjump == NULL) /* Not a fatal error */ return; /* Adjust for signed chars, if necessary */ g->charjump = &g->charjump[-(CHAR_MIN)]; /* If the character does not exist in the pattern, the jump * is equal to the number of characters in the pattern. */ for (ch = CHAR_MIN; ch < (CHAR_MAX + 1); ch++) g->charjump[ch] = g->mlen; /* If the character does exist, compute the jump that would * take us to the last character in the pattern equal to it * (notice that we match right to left, so that last character * is the first one that would be matched). */ for (mindex = 0; mindex < g->mlen; mindex++) g->charjump[(int)g->must[mindex]] = g->mlen - mindex - 1; } /* - computematchjumps - compute match jumps for BM scan == static void computematchjumps(struct parse *p, struct re_guts *g); * * This algorithm assumes g->must exists and is has size greater than * zero. It's based on the algorithm found on Computer Algorithms by * Sara Baase. * * A match jump is the number of characters one needs to advance based * on the already-matched suffix. * Notice that all values here are minus (g->mlen-1), because of the way * the search algorithm works. */ static void computematchjumps(struct parse *p, struct re_guts *g) { int mindex; /* General "must" iterator */ int suffix; /* Keeps track of matching suffix */ int ssuffix; /* Keeps track of suffixes' suffix */ int* pmatches; /* pmatches[k] points to the next i * such that i+1...mlen is a substring * of k+1...k+mlen-i-1 */ /* Avoid making errors worse */ if (p->error != 0) return; pmatches = (int*) malloc(g->mlen * sizeof(unsigned int)); if (pmatches == NULL) { g->matchjump = NULL; return; } g->matchjump = (int*) malloc(g->mlen * sizeof(unsigned int)); if (g->matchjump == NULL) { /* Not a fatal error */ free(pmatches); return; } /* Set maximum possible jump for each character in the pattern */ for (mindex = 0; mindex < g->mlen; mindex++) g->matchjump[mindex] = 2*g->mlen - mindex - 1; /* Compute pmatches[] */ for (mindex = g->mlen - 1, suffix = g->mlen; mindex >= 0; mindex--, suffix--) { pmatches[mindex] = suffix; /* If a mismatch is found, interrupting the substring, * compute the matchjump for that position. If no * mismatch is found, then a text substring mismatched * against the suffix will also mismatch against the * substring. */ while (suffix < g->mlen && g->must[mindex] != g->must[suffix]) { g->matchjump[suffix] = MIN(g->matchjump[suffix], g->mlen - mindex - 1); suffix = pmatches[suffix]; } } /* Compute the matchjump up to the last substring found to jump * to the beginning of the largest must pattern prefix matching * it's own suffix. */ for (mindex = 0; mindex <= suffix; mindex++) g->matchjump[mindex] = MIN(g->matchjump[mindex], g->mlen + suffix - mindex); ssuffix = pmatches[suffix]; while (suffix < g->mlen) { while (suffix <= ssuffix && suffix < g->mlen) { g->matchjump[suffix] = MIN(g->matchjump[suffix], g->mlen + ssuffix - suffix); suffix++; } if (suffix < g->mlen) ssuffix = pmatches[ssuffix]; } free(pmatches); } /* - pluscount - count + nesting == static sopno pluscount(struct parse *p, struct re_guts *g); */ static sopno /* nesting depth */ pluscount(struct parse *p, struct re_guts *g) { sop *scan; sop s; sopno plusnest = 0; sopno maxnest = 0; if (p->error != 0) return(0); /* there may not be an OEND */ scan = g->strip + 1; do { s = *scan++; switch (OP(s)) { case OPLUS_: plusnest++; break; case O_PLUS: if (plusnest > maxnest) maxnest = plusnest; plusnest--; break; } } while (OP(s) != OEND); if (plusnest != 0) g->iflags |= BAD; return(maxnest); } Index: projects/building-blocks/lib/libc/sys/interposing_table.c =================================================================== --- projects/building-blocks/lib/libc/sys/interposing_table.c (revision 278776) +++ projects/building-blocks/lib/libc/sys/interposing_table.c (revision 278777) @@ -1,84 +1,86 @@ /* * Copyright (c) 2014 The FreeBSD Foundation. * All rights reserved. * * Portions of this software were developed by Konstantin Belousov * under sponsorship from the FreeBSD Foundation. * * 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(s), this list of conditions and the following disclaimer as * the first lines of this file unmodified other than the possible * addition of one or more copyright notices. * 2. Redistributions in binary form must reproduce the above copyright * notice(s), 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 HOLDER(S) ``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(S) 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 __FBSDID("$FreeBSD$"); #include #include "libc_private.h" #define SLOT(a, b) \ [INTERPOS_##a] = (interpos_func_t)b interpos_func_t __libc_interposing[INTERPOS_MAX] = { SLOT(accept, __sys_accept), SLOT(accept4, __sys_accept4), SLOT(aio_suspend, __sys_aio_suspend), SLOT(close, __sys_close), SLOT(connect, __sys_connect), SLOT(fcntl, __fcntl_compat), SLOT(fsync, __sys_fsync), SLOT(fork, __sys_fork), SLOT(msync, __sys_msync), SLOT(nanosleep, __sys_nanosleep), SLOT(openat, __sys_openat), SLOT(poll, __sys_poll), SLOT(pselect, __sys_pselect), SLOT(read, __sys_read), SLOT(readv, __sys_readv), SLOT(recvfrom, __sys_recvfrom), SLOT(recvmsg, __sys_recvmsg), SLOT(select, __sys_select), SLOT(sendmsg, __sys_sendmsg), SLOT(sendto, __sys_sendto), SLOT(setcontext, __sys_setcontext), SLOT(sigaction, __sys_sigaction), SLOT(sigprocmask, __sys_sigprocmask), SLOT(sigsuspend, __sys_sigsuspend), SLOT(sigwait, __libc_sigwait), SLOT(sigtimedwait, __sys_sigtimedwait), SLOT(sigwaitinfo, __sys_sigwaitinfo), SLOT(swapcontext, __sys_swapcontext), SLOT(system, __libc_system), SLOT(tcdrain, __libc_tcdrain), SLOT(wait4, __sys_wait4), SLOT(write, __sys_write), SLOT(writev, __sys_writev), SLOT(_pthread_mutex_init_calloc_cb, _pthread_mutex_init_calloc_cb_stub), + SLOT(spinlock, __libc_spinlock_stub), + SLOT(spinunlock, __libc_spinunlock_stub), }; #undef SLOT interpos_func_t * __libc_interposing_slot(int interposno) { return (&__libc_interposing[interposno]); } Index: projects/building-blocks/lib/libc =================================================================== --- projects/building-blocks/lib/libc (revision 278776) +++ projects/building-blocks/lib/libc (revision 278777) Property changes on: projects/building-blocks/lib/libc ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/lib/libc:r278637-278776 Index: projects/building-blocks/lib/libproc/proc_sym.c =================================================================== --- projects/building-blocks/lib/libproc/proc_sym.c (revision 278776) +++ projects/building-blocks/lib/libproc/proc_sym.c (revision 278777) @@ -1,616 +1,631 @@ /*- * Copyright (c) 2010 The FreeBSD Foundation * Copyright (c) 2008 John Birrell (jb@freebsd.org) * All rights reserved. * * Portions of this software were developed by Rui Paulo under sponsorship * from the FreeBSD Foundation. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 __FBSDID("$FreeBSD$"); #include #ifndef NO_CTF #include #include #endif #include #include #include #include #include #include #include #include #include #ifndef NO_CTF #include #endif #include #include "_libproc.h" #ifdef NO_CTF typedef struct ctf_file ctf_file_t; #endif #ifndef NO_CXA_DEMANGLE extern char *__cxa_demangle(const char *, char *, size_t *, int *); #endif /* NO_CXA_DEMANGLE */ static void proc_rdl2prmap(rd_loadobj_t *, prmap_t *); static void demangle(const char *symbol, char *buf, size_t len) { #ifndef NO_CXA_DEMANGLE char *dembuf; if (symbol[0] == '_' && symbol[1] == 'Z' && symbol[2]) { dembuf = __cxa_demangle(symbol, NULL, NULL, NULL); if (!dembuf) goto fail; strlcpy(buf, dembuf, len); free(dembuf); return; } fail: #endif /* NO_CXA_DEMANGLE */ strlcpy(buf, symbol, len); } +static int +find_dbg_obj(const char *path) +{ + int fd; + char dbg_path[PATH_MAX]; + + snprintf(dbg_path, sizeof(dbg_path), + "/usr/lib/debug/%s.debug", path); + fd = open(dbg_path, O_RDONLY); + if (fd > 0) + return (fd); + else + return (open(path, O_RDONLY)); +} + static void proc_rdl2prmap(rd_loadobj_t *rdl, prmap_t *map) { map->pr_vaddr = rdl->rdl_saddr; map->pr_size = rdl->rdl_eaddr - rdl->rdl_saddr; map->pr_offset = rdl->rdl_offset; map->pr_mflags = 0; if (rdl->rdl_prot & RD_RDL_R) map->pr_mflags |= MA_READ; if (rdl->rdl_prot & RD_RDL_W) map->pr_mflags |= MA_WRITE; if (rdl->rdl_prot & RD_RDL_X) map->pr_mflags |= MA_EXEC; strlcpy(map->pr_mapname, rdl->rdl_path, sizeof(map->pr_mapname)); } char * proc_objname(struct proc_handle *p, uintptr_t addr, char *objname, size_t objnamesz) { size_t i; rd_loadobj_t *rdl; for (i = 0; i < p->nobjs; i++) { rdl = &p->rdobjs[i]; if (addr >= rdl->rdl_saddr && addr < rdl->rdl_eaddr) { strlcpy(objname, rdl->rdl_path, objnamesz); return (objname); } } return (NULL); } prmap_t * proc_obj2map(struct proc_handle *p, const char *objname) { size_t i; prmap_t *map; rd_loadobj_t *rdl; char path[MAXPATHLEN]; rdl = NULL; for (i = 0; i < p->nobjs; i++) { basename_r(p->rdobjs[i].rdl_path, path); if (strcmp(path, objname) == 0) { rdl = &p->rdobjs[i]; break; } } if (rdl == NULL) { if (strcmp(objname, "a.out") == 0 && p->rdexec != NULL) rdl = p->rdexec; else return (NULL); } if ((map = malloc(sizeof(*map))) == NULL) return (NULL); proc_rdl2prmap(rdl, map); return (map); } int proc_iter_objs(struct proc_handle *p, proc_map_f *func, void *cd) { size_t i; rd_loadobj_t *rdl; prmap_t map; char path[MAXPATHLEN]; char last[MAXPATHLEN]; int error; if (p->nobjs == 0) return (-1); error = 0; memset(last, 0, sizeof(last)); for (i = 0; i < p->nobjs; i++) { rdl = &p->rdobjs[i]; proc_rdl2prmap(rdl, &map); basename_r(rdl->rdl_path, path); /* * We shouldn't call the callback twice with the same object. * To do that we are assuming the fact that if there are * repeated object names (i.e. different mappings for the * same object) they occur next to each other. */ if (strcmp(path, last) == 0) continue; if ((error = (*func)(cd, &map, path)) != 0) break; strlcpy(last, path, sizeof(last)); } return (error); } prmap_t * proc_addr2map(struct proc_handle *p, uintptr_t addr) { size_t i; int cnt, lastvn = 0; prmap_t *map; rd_loadobj_t *rdl; struct kinfo_vmentry *kves, *kve; /* * If we don't have a cache of listed objects, we need to query * it ourselves. */ if (p->nobjs == 0) { if ((kves = kinfo_getvmmap(p->pid, &cnt)) == NULL) return (NULL); for (i = 0; i < (size_t)cnt; i++) { kve = kves + i; if (kve->kve_type == KVME_TYPE_VNODE) lastvn = i; if (addr >= kve->kve_start && addr < kve->kve_end) { if ((map = malloc(sizeof(*map))) == NULL) { free(kves); return (NULL); } map->pr_vaddr = kve->kve_start; map->pr_size = kve->kve_end - kve->kve_start; map->pr_offset = kve->kve_offset; map->pr_mflags = 0; if (kve->kve_protection & KVME_PROT_READ) map->pr_mflags |= MA_READ; if (kve->kve_protection & KVME_PROT_WRITE) map->pr_mflags |= MA_WRITE; if (kve->kve_protection & KVME_PROT_EXEC) map->pr_mflags |= MA_EXEC; if (kve->kve_flags & KVME_FLAG_COW) map->pr_mflags |= MA_COW; if (kve->kve_flags & KVME_FLAG_NEEDS_COPY) map->pr_mflags |= MA_NEEDS_COPY; if (kve->kve_flags & KVME_FLAG_NOCOREDUMP) map->pr_mflags |= MA_NOCOREDUMP; strlcpy(map->pr_mapname, kves[lastvn].kve_path, sizeof(map->pr_mapname)); free(kves); return (map); } } free(kves); return (NULL); } for (i = 0; i < p->nobjs; i++) { rdl = &p->rdobjs[i]; if (addr >= rdl->rdl_saddr && addr < rdl->rdl_eaddr) { if ((map = malloc(sizeof(*map))) == NULL) return (NULL); proc_rdl2prmap(rdl, map); return (map); } } return (NULL); } /* * Look up the symbol at addr, returning a copy of the symbol and its name. */ static int lookup_addr(Elf *e, Elf_Scn *scn, u_long stridx, uintptr_t off, uintptr_t addr, const char **name, GElf_Sym *symcopy) { GElf_Sym sym; Elf_Data *data; const char *s; uint64_t rsym; int i; if ((data = elf_getdata(scn, NULL)) == NULL) { DPRINTFX("ERROR: elf_getdata() failed: %s", elf_errmsg(-1)); return (1); } for (i = 0; gelf_getsym(data, i, &sym) != NULL; i++) { rsym = off + sym.st_value; if (addr >= rsym && addr < rsym + sym.st_size) { s = elf_strptr(e, stridx, sym.st_name); if (s != NULL) { *name = s; memcpy(symcopy, &sym, sizeof(*symcopy)); /* * DTrace expects the st_value to contain * only the address relative to the start of * the function. */ symcopy->st_value = rsym; return (0); } } } return (1); } int proc_addr2sym(struct proc_handle *p, uintptr_t addr, char *name, size_t namesz, GElf_Sym *symcopy) { GElf_Ehdr ehdr; GElf_Shdr shdr; Elf *e; Elf_Scn *scn, *dynsymscn = NULL, *symtabscn = NULL; prmap_t *map; const char *s; uintptr_t off; u_long symtabstridx = 0, dynsymstridx = 0; int fd, error = -1; if ((map = proc_addr2map(p, addr)) == NULL) return (-1); - if ((fd = open(map->pr_mapname, O_RDONLY, 0)) < 0) { + if ((fd = find_dbg_obj(map->pr_mapname)) < 0) { DPRINTF("ERROR: open %s failed", map->pr_mapname); goto err0; } if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { DPRINTFX("ERROR: elf_begin() failed: %s", elf_errmsg(-1)); goto err1; } if (gelf_getehdr(e, &ehdr) == NULL) { DPRINTFX("ERROR: gelf_getehdr() failed: %s", elf_errmsg(-1)); goto err2; } /* * Find the index of the STRTAB and SYMTAB sections to locate * symbol names. */ scn = NULL; while ((scn = elf_nextscn(e, scn)) != NULL) { gelf_getshdr(scn, &shdr); switch (shdr.sh_type) { case SHT_SYMTAB: symtabscn = scn; symtabstridx = shdr.sh_link; break; case SHT_DYNSYM: dynsymscn = scn; dynsymstridx = shdr.sh_link; break; } } off = ehdr.e_type == ET_EXEC ? 0 : map->pr_vaddr; /* * First look up the symbol in the dynsymtab, and fall back to the * symtab if the lookup fails. */ error = lookup_addr(e, dynsymscn, dynsymstridx, off, addr, &s, symcopy); if (error == 0) goto out; error = lookup_addr(e, symtabscn, symtabstridx, off, addr, &s, symcopy); if (error != 0) goto err2; out: demangle(s, name, namesz); err2: elf_end(e); err1: close(fd); err0: free(map); return (error); } prmap_t * proc_name2map(struct proc_handle *p, const char *name) { size_t i; int cnt; prmap_t *map = NULL; char tmppath[MAXPATHLEN]; struct kinfo_vmentry *kves, *kve; rd_loadobj_t *rdl; /* * If we haven't iterated over the list of loaded objects, * librtld_db isn't yet initialized and it's very likely * that librtld_db called us. We need to do the heavy * lifting here to find the symbol librtld_db is looking for. */ if (p->nobjs == 0) { if ((kves = kinfo_getvmmap(proc_getpid(p), &cnt)) == NULL) return (NULL); for (i = 0; i < (size_t)cnt; i++) { kve = kves + i; basename_r(kve->kve_path, tmppath); if (strcmp(tmppath, name) == 0) { map = proc_addr2map(p, kve->kve_start); break; } } free(kves); } else for (i = 0; i < p->nobjs; i++) { rdl = &p->rdobjs[i]; basename_r(rdl->rdl_path, tmppath); if (strcmp(tmppath, name) == 0) { if ((map = malloc(sizeof(*map))) == NULL) return (NULL); proc_rdl2prmap(rdl, map); break; } } if (map == NULL && strcmp(name, "a.out") == 0 && p->rdexec != NULL) map = proc_addr2map(p, p->rdexec->rdl_saddr); return (map); } /* * Look up the symbol with the given name and return a copy of it. */ static int lookup_name(Elf *e, Elf_Scn *scn, u_long stridx, const char *symbol, GElf_Sym *symcopy, prsyminfo_t *si) { GElf_Sym sym; Elf_Data *data; char *s; int i; if ((data = elf_getdata(scn, NULL)) == NULL) { DPRINTFX("ERROR: elf_getdata() failed: %s", elf_errmsg(-1)); return (1); } for (i = 0; gelf_getsym(data, i, &sym) != NULL; i++) { s = elf_strptr(e, stridx, sym.st_name); if (s != NULL && strcmp(s, symbol) == 0) { memcpy(symcopy, &sym, sizeof(*symcopy)); if (si != NULL) si->prs_id = i; return (0); } } return (1); } int proc_name2sym(struct proc_handle *p, const char *object, const char *symbol, GElf_Sym *symcopy, prsyminfo_t *si) { Elf *e; Elf_Scn *scn, *dynsymscn = NULL, *symtabscn = NULL; GElf_Shdr shdr; GElf_Ehdr ehdr; prmap_t *map; uintptr_t off; u_long symtabstridx = 0, dynsymstridx = 0; int fd, error = -1; if ((map = proc_name2map(p, object)) == NULL) { DPRINTFX("ERROR: couldn't find object %s", object); goto err0; } - if ((fd = open(map->pr_mapname, O_RDONLY, 0)) < 0) { + if ((fd = find_dbg_obj(map->pr_mapname)) < 0) { DPRINTF("ERROR: open %s failed", map->pr_mapname); goto err0; } if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { DPRINTFX("ERROR: elf_begin() failed: %s", elf_errmsg(-1)); goto err1; } if (gelf_getehdr(e, &ehdr) == NULL) { DPRINTFX("ERROR: gelf_getehdr() failed: %s", elf_errmsg(-1)); goto err2; } /* * Find the index of the STRTAB and SYMTAB sections to locate * symbol names. */ scn = NULL; while ((scn = elf_nextscn(e, scn)) != NULL) { gelf_getshdr(scn, &shdr); switch (shdr.sh_type) { case SHT_SYMTAB: symtabscn = scn; symtabstridx = shdr.sh_link; break; case SHT_DYNSYM: dynsymscn = scn; dynsymstridx = shdr.sh_link; break; } } /* * First look up the symbol in the dynsymtab, and fall back to the * symtab if the lookup fails. */ error = lookup_name(e, dynsymscn, dynsymstridx, symbol, symcopy, si); if (error == 0) goto out; error = lookup_name(e, symtabscn, symtabstridx, symbol, symcopy, si); if (error == 0) goto out; out: off = ehdr.e_type == ET_EXEC ? 0 : map->pr_vaddr; symcopy->st_value += off; err2: elf_end(e); err1: close(fd); err0: free(map); return (error); } ctf_file_t * proc_name2ctf(struct proc_handle *p, const char *name) { #ifndef NO_CTF ctf_file_t *ctf; prmap_t *map; int error; if ((map = proc_name2map(p, name)) == NULL) return (NULL); ctf = ctf_open(map->pr_mapname, &error); free(map); return (ctf); #else (void)p; (void)name; return (NULL); #endif } int proc_iter_symbyaddr(struct proc_handle *p, const char *object, int which, int mask, proc_sym_f *func, void *cd) { Elf *e; int i, fd; prmap_t *map; Elf_Scn *scn, *foundscn = NULL; Elf_Data *data; GElf_Ehdr ehdr; GElf_Shdr shdr; GElf_Sym sym; unsigned long stridx = -1; char *s; int error = -1; if ((map = proc_name2map(p, object)) == NULL) return (-1); - if ((fd = open(map->pr_mapname, O_RDONLY)) < 0) { + if ((fd = find_dbg_obj(map->pr_mapname)) < 0) { DPRINTF("ERROR: open %s failed", map->pr_mapname); goto err0; } if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { DPRINTFX("ERROR: elf_begin() failed: %s", elf_errmsg(-1)); goto err1; } if (gelf_getehdr(e, &ehdr) == NULL) { DPRINTFX("ERROR: gelf_getehdr() failed: %s", elf_errmsg(-1)); goto err2; } /* * Find the section we are looking for. */ scn = NULL; while ((scn = elf_nextscn(e, scn)) != NULL) { gelf_getshdr(scn, &shdr); if (which == PR_SYMTAB && shdr.sh_type == SHT_SYMTAB) { foundscn = scn; break; } else if (which == PR_DYNSYM && shdr.sh_type == SHT_DYNSYM) { foundscn = scn; break; } } if (!foundscn) return (-1); stridx = shdr.sh_link; if ((data = elf_getdata(foundscn, NULL)) == NULL) { DPRINTFX("ERROR: elf_getdata() failed: %s", elf_errmsg(-1)); goto err2; } for (i = 0; gelf_getsym(data, i, &sym) != NULL; i++) { if (GELF_ST_BIND(sym.st_info) == STB_LOCAL && (mask & BIND_LOCAL) == 0) continue; if (GELF_ST_BIND(sym.st_info) == STB_GLOBAL && (mask & BIND_GLOBAL) == 0) continue; if (GELF_ST_BIND(sym.st_info) == STB_WEAK && (mask & BIND_WEAK) == 0) continue; if (GELF_ST_TYPE(sym.st_info) == STT_NOTYPE && (mask & TYPE_NOTYPE) == 0) continue; if (GELF_ST_TYPE(sym.st_info) == STT_OBJECT && (mask & TYPE_OBJECT) == 0) continue; if (GELF_ST_TYPE(sym.st_info) == STT_FUNC && (mask & TYPE_FUNC) == 0) continue; if (GELF_ST_TYPE(sym.st_info) == STT_SECTION && (mask & TYPE_SECTION) == 0) continue; if (GELF_ST_TYPE(sym.st_info) == STT_FILE && (mask & TYPE_FILE) == 0) continue; s = elf_strptr(e, stridx, sym.st_name); if (ehdr.e_type != ET_EXEC) sym.st_value += map->pr_vaddr; if ((error = (*func)(cd, &sym, s)) != 0) goto err2; } error = 0; err2: elf_end(e); err1: close(fd); err0: free(map); return (error); } Index: projects/building-blocks/lib/libthr/thread/thr_private.h =================================================================== --- projects/building-blocks/lib/libthr/thread/thr_private.h (revision 278776) +++ projects/building-blocks/lib/libthr/thread/thr_private.h (revision 278777) @@ -1,936 +1,940 @@ /* * Copyright (C) 2005 Daniel M. Eischen * Copyright (c) 2005 David Xu * Copyright (c) 1995-1998 John Birrell . * * 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 unmodified, 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. * * $FreeBSD$ */ #ifndef _THR_PRIVATE_H #define _THR_PRIVATE_H /* * Include files. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SYM_FB10(sym) __CONCAT(sym, _fb10) #define SYM_FBP10(sym) __CONCAT(sym, _fbp10) #define WEAK_REF(sym, alias) __weak_reference(sym, alias) #define SYM_COMPAT(sym, impl, ver) __sym_compat(sym, impl, ver) #define SYM_DEFAULT(sym, impl, ver) __sym_default(sym, impl, ver) #define FB10_COMPAT(func, sym) \ WEAK_REF(func, SYM_FB10(sym)); \ SYM_COMPAT(sym, SYM_FB10(sym), FBSD_1.0) #define FB10_COMPAT_PRIVATE(func, sym) \ WEAK_REF(func, SYM_FBP10(sym)); \ SYM_DEFAULT(sym, SYM_FBP10(sym), FBSDprivate_1.0) #include "pthread_md.h" #include "thr_umtx.h" #include "thread_db.h" #ifdef _PTHREAD_FORCED_UNWIND #define _BSD_SOURCE #include #endif typedef TAILQ_HEAD(pthreadlist, pthread) pthreadlist; typedef TAILQ_HEAD(atfork_head, pthread_atfork) atfork_head; TAILQ_HEAD(mutex_queue, pthread_mutex); /* Signal to do cancellation */ #define SIGCANCEL SIGTHR /* * Kernel fatal error handler macro. */ #define PANIC(string) _thread_exit(__FILE__,__LINE__,string) /* Output debug messages like this: */ #define stdout_debug(args...) _thread_printf(STDOUT_FILENO, ##args) #define stderr_debug(args...) _thread_printf(STDERR_FILENO, ##args) #ifdef _PTHREADS_INVARIANTS #define THR_ASSERT(cond, msg) do { \ if (__predict_false(!(cond))) \ PANIC(msg); \ } while (0) #else #define THR_ASSERT(cond, msg) #endif #ifdef PIC # define STATIC_LIB_REQUIRE(name) #else # define STATIC_LIB_REQUIRE(name) __asm (".globl " #name) #endif #define TIMESPEC_ADD(dst, src, val) \ do { \ (dst)->tv_sec = (src)->tv_sec + (val)->tv_sec; \ (dst)->tv_nsec = (src)->tv_nsec + (val)->tv_nsec; \ if ((dst)->tv_nsec >= 1000000000) { \ (dst)->tv_sec++; \ (dst)->tv_nsec -= 1000000000; \ } \ } while (0) #define TIMESPEC_SUB(dst, src, val) \ do { \ (dst)->tv_sec = (src)->tv_sec - (val)->tv_sec; \ (dst)->tv_nsec = (src)->tv_nsec - (val)->tv_nsec; \ if ((dst)->tv_nsec < 0) { \ (dst)->tv_sec--; \ (dst)->tv_nsec += 1000000000; \ } \ } while (0) /* XXX These values should be same as those defined in pthread.h */ #define THR_MUTEX_INITIALIZER ((struct pthread_mutex *)NULL) #define THR_ADAPTIVE_MUTEX_INITIALIZER ((struct pthread_mutex *)1) #define THR_MUTEX_DESTROYED ((struct pthread_mutex *)2) #define THR_COND_INITIALIZER ((struct pthread_cond *)NULL) #define THR_COND_DESTROYED ((struct pthread_cond *)1) #define THR_RWLOCK_INITIALIZER ((struct pthread_rwlock *)NULL) #define THR_RWLOCK_DESTROYED ((struct pthread_rwlock *)1) #define PMUTEX_FLAG_TYPE_MASK 0x0ff #define PMUTEX_FLAG_PRIVATE 0x100 #define PMUTEX_FLAG_DEFERED 0x200 #define PMUTEX_TYPE(mtxflags) ((mtxflags) & PMUTEX_FLAG_TYPE_MASK) #define MAX_DEFER_WAITERS 50 struct pthread_mutex { /* * Lock for accesses to this structure. */ struct umutex m_lock; int m_flags; struct pthread *m_owner; int m_count; int m_spinloops; int m_yieldloops; /* * Link for all mutexes a thread currently owns. */ TAILQ_ENTRY(pthread_mutex) m_qe; }; struct pthread_mutex_attr { enum pthread_mutextype m_type; int m_protocol; int m_ceiling; }; #define PTHREAD_MUTEXATTR_STATIC_INITIALIZER \ { PTHREAD_MUTEX_DEFAULT, PTHREAD_PRIO_NONE, 0, MUTEX_FLAGS_PRIVATE } struct pthread_cond { __uint32_t __has_user_waiters; __uint32_t __has_kern_waiters; __uint32_t __flags; __uint32_t __clock_id; }; struct pthread_cond_attr { int c_pshared; int c_clockid; }; struct pthread_barrier { struct umutex b_lock; struct ucond b_cv; int64_t b_cycle; int b_count; int b_waiters; int b_refcount; int b_destroying; }; struct pthread_barrierattr { int pshared; }; struct pthread_spinlock { struct umutex s_lock; }; /* * Flags for condition variables. */ #define COND_FLAGS_PRIVATE 0x01 #define COND_FLAGS_INITED 0x02 #define COND_FLAGS_BUSY 0x04 /* * Cleanup definitions. */ struct pthread_cleanup { struct pthread_cleanup *prev; void (*routine)(void *); void *routine_arg; int onheap; }; #define THR_CLEANUP_PUSH(td, func, arg) { \ struct pthread_cleanup __cup; \ \ __cup.routine = func; \ __cup.routine_arg = arg; \ __cup.onheap = 0; \ __cup.prev = (td)->cleanup; \ (td)->cleanup = &__cup; #define THR_CLEANUP_POP(td, exec) \ (td)->cleanup = __cup.prev; \ if ((exec) != 0) \ __cup.routine(__cup.routine_arg); \ } struct pthread_atfork { TAILQ_ENTRY(pthread_atfork) qe; void (*prepare)(void); void (*parent)(void); void (*child)(void); }; struct pthread_attr { #define pthread_attr_start_copy sched_policy int sched_policy; int sched_inherit; int prio; int suspend; #define THR_STACK_USER 0x100 /* 0xFF reserved for */ int flags; void *stackaddr_attr; size_t stacksize_attr; size_t guardsize_attr; #define pthread_attr_end_copy cpuset cpuset_t *cpuset; size_t cpusetsize; }; struct wake_addr { struct wake_addr *link; unsigned int value; char pad[12]; }; struct sleepqueue { TAILQ_HEAD(, pthread) sq_blocked; SLIST_HEAD(, sleepqueue) sq_freeq; LIST_ENTRY(sleepqueue) sq_hash; SLIST_ENTRY(sleepqueue) sq_flink; void *sq_wchan; int sq_type; }; /* * Thread creation state attributes. */ #define THR_CREATE_RUNNING 0 #define THR_CREATE_SUSPENDED 1 /* * Miscellaneous definitions. */ #define THR_STACK_DEFAULT (sizeof(void *) / 4 * 1024 * 1024) /* * Maximum size of initial thread's stack. This perhaps deserves to be larger * than the stacks of other threads, since many applications are likely to run * almost entirely on this stack. */ #define THR_STACK_INITIAL (THR_STACK_DEFAULT * 2) /* * Define priorities returned by kernel. */ #define THR_MIN_PRIORITY (_thr_priorities[SCHED_OTHER-1].pri_min) #define THR_MAX_PRIORITY (_thr_priorities[SCHED_OTHER-1].pri_max) #define THR_DEF_PRIORITY (_thr_priorities[SCHED_OTHER-1].pri_default) #define THR_MIN_RR_PRIORITY (_thr_priorities[SCHED_RR-1].pri_min) #define THR_MAX_RR_PRIORITY (_thr_priorities[SCHED_RR-1].pri_max) #define THR_DEF_RR_PRIORITY (_thr_priorities[SCHED_RR-1].pri_default) /* XXX The SCHED_FIFO should have same priority range as SCHED_RR */ #define THR_MIN_FIFO_PRIORITY (_thr_priorities[SCHED_FIFO_1].pri_min) #define THR_MAX_FIFO_PRIORITY (_thr_priorities[SCHED_FIFO-1].pri_max) #define THR_DEF_FIFO_PRIORITY (_thr_priorities[SCHED_FIFO-1].pri_default) struct pthread_prio { int pri_min; int pri_max; int pri_default; }; struct pthread_rwlockattr { int pshared; }; struct pthread_rwlock { struct urwlock lock; struct pthread *owner; }; /* * Thread states. */ enum pthread_state { PS_RUNNING, PS_DEAD }; struct pthread_specific_elem { const void *data; int seqno; }; struct pthread_key { volatile int allocated; int seqno; void (*destructor)(void *); }; /* * lwpid_t is 32bit but kernel thr API exports tid as long type * in very earily date. */ #define TID(thread) ((uint32_t) ((thread)->tid)) /* * Thread structure. */ struct pthread { #define _pthread_startzero tid /* Kernel thread id. */ long tid; #define TID_TERMINATED 1 /* * Lock for accesses to this thread structure. */ struct umutex lock; /* Internal condition variable cycle number. */ uint32_t cycle; /* How many low level locks the thread held. */ int locklevel; /* * Set to non-zero when this thread has entered a critical * region. We allow for recursive entries into critical regions. */ int critical_count; /* Signal blocked counter. */ int sigblock; /* Queue entry for list of all threads. */ TAILQ_ENTRY(pthread) tle; /* link for all threads in process */ /* Queue entry for GC lists. */ TAILQ_ENTRY(pthread) gcle; /* Hash queue entry. */ LIST_ENTRY(pthread) hle; /* Sleep queue entry */ TAILQ_ENTRY(pthread) wle; /* Threads reference count. */ int refcount; /* * Thread start routine, argument, stack pointer and thread * attributes. */ void *(*start_routine)(void *); void *arg; struct pthread_attr attr; #define SHOULD_CANCEL(thr) \ ((thr)->cancel_pending && (thr)->cancel_enable && \ (thr)->no_cancel == 0) /* Cancellation is enabled */ int cancel_enable; /* Cancellation request is pending */ int cancel_pending; /* Thread is at cancellation point */ int cancel_point; /* Cancellation is temporarily disabled */ int no_cancel; /* Asynchronouse cancellation is enabled */ int cancel_async; /* Cancellation is in progress */ int cancelling; /* Thread temporary signal mask. */ sigset_t sigmask; /* Thread should unblock SIGCANCEL. */ int unblock_sigcancel; /* In sigsuspend state */ int in_sigsuspend; /* deferred signal info */ siginfo_t deferred_siginfo; /* signal mask to restore. */ sigset_t deferred_sigmask; /* the sigaction should be used for deferred signal. */ struct sigaction deferred_sigact; /* deferred signal delivery is performed, do not reenter. */ int deferred_run; /* Force new thread to exit. */ int force_exit; /* Thread state: */ enum pthread_state state; /* * Error variable used instead of errno. The function __error() * returns a pointer to this. */ int error; /* * The joiner is the thread that is joining to this thread. The * join status keeps track of a join operation to another thread. */ struct pthread *joiner; /* Miscellaneous flags; only set with scheduling lock held. */ int flags; #define THR_FLAGS_PRIVATE 0x0001 #define THR_FLAGS_NEED_SUSPEND 0x0002 /* thread should be suspended */ #define THR_FLAGS_SUSPENDED 0x0004 /* thread is suspended */ #define THR_FLAGS_DETACHED 0x0008 /* thread is detached */ /* Thread list flags; only set with thread list lock held. */ int tlflags; #define TLFLAGS_GC_SAFE 0x0001 /* thread safe for cleaning */ #define TLFLAGS_IN_TDLIST 0x0002 /* thread in all thread list */ #define TLFLAGS_IN_GCLIST 0x0004 /* thread in gc list */ /* Queue of currently owned NORMAL or PRIO_INHERIT type mutexes. */ struct mutex_queue mutexq; /* Queue of all owned PRIO_PROTECT mutexes. */ struct mutex_queue pp_mutexq; void *ret; struct pthread_specific_elem *specific; int specific_data_count; /* Number rwlocks rdlocks held. */ int rdlock_count; /* * Current locks bitmap for rtld. */ int rtld_bits; /* Thread control block */ struct tcb *tcb; /* Cleanup handlers Link List */ struct pthread_cleanup *cleanup; #ifdef _PTHREAD_FORCED_UNWIND struct _Unwind_Exception ex; void *unwind_stackend; int unwind_disabled; #endif /* * Magic value to help recognize a valid thread structure * from an invalid one: */ #define THR_MAGIC ((u_int32_t) 0xd09ba115) u_int32_t magic; /* Enable event reporting */ int report_events; /* Event mask */ int event_mask; /* Event */ td_event_msg_t event_buf; /* Wait channel */ void *wchan; /* Referenced mutex. */ struct pthread_mutex *mutex_obj; /* Thread will sleep. */ int will_sleep; /* Number of threads deferred. */ int nwaiter_defer; /* Deferred threads from pthread_cond_signal. */ unsigned int *defer_waiters[MAX_DEFER_WAITERS]; #define _pthread_endzero wake_addr struct wake_addr *wake_addr; #define WAKE_ADDR(td) ((td)->wake_addr) /* Sleep queue */ struct sleepqueue *sleepqueue; }; #define THR_SHOULD_GC(thrd) \ ((thrd)->refcount == 0 && (thrd)->state == PS_DEAD && \ ((thrd)->flags & THR_FLAGS_DETACHED) != 0) #define THR_IN_CRITICAL(thrd) \ (((thrd)->locklevel > 0) || \ ((thrd)->critical_count > 0)) #define THR_CRITICAL_ENTER(thrd) \ (thrd)->critical_count++ #define THR_CRITICAL_LEAVE(thrd) \ do { \ (thrd)->critical_count--; \ _thr_ast(thrd); \ } while (0) #define THR_UMUTEX_TRYLOCK(thrd, lck) \ _thr_umutex_trylock((lck), TID(thrd)) #define THR_UMUTEX_LOCK(thrd, lck) \ _thr_umutex_lock((lck), TID(thrd)) #define THR_UMUTEX_TIMEDLOCK(thrd, lck, timo) \ _thr_umutex_timedlock((lck), TID(thrd), (timo)) #define THR_UMUTEX_UNLOCK(thrd, lck) \ _thr_umutex_unlock((lck), TID(thrd)) #define THR_LOCK_ACQUIRE(thrd, lck) \ do { \ (thrd)->locklevel++; \ _thr_umutex_lock(lck, TID(thrd)); \ } while (0) #define THR_LOCK_ACQUIRE_SPIN(thrd, lck) \ do { \ (thrd)->locklevel++; \ _thr_umutex_lock_spin(lck, TID(thrd)); \ } while (0) #ifdef _PTHREADS_INVARIANTS #define THR_ASSERT_LOCKLEVEL(thrd) \ do { \ if (__predict_false((thrd)->locklevel <= 0)) \ _thr_assert_lock_level(); \ } while (0) #else #define THR_ASSERT_LOCKLEVEL(thrd) #endif #define THR_LOCK_RELEASE(thrd, lck) \ do { \ THR_ASSERT_LOCKLEVEL(thrd); \ _thr_umutex_unlock((lck), TID(thrd)); \ (thrd)->locklevel--; \ _thr_ast(thrd); \ } while (0) #define THR_LOCK(curthrd) THR_LOCK_ACQUIRE(curthrd, &(curthrd)->lock) #define THR_UNLOCK(curthrd) THR_LOCK_RELEASE(curthrd, &(curthrd)->lock) #define THR_THREAD_LOCK(curthrd, thr) THR_LOCK_ACQUIRE(curthrd, &(thr)->lock) #define THR_THREAD_UNLOCK(curthrd, thr) THR_LOCK_RELEASE(curthrd, &(thr)->lock) #define THREAD_LIST_RDLOCK(curthrd) \ do { \ (curthrd)->locklevel++; \ _thr_rwl_rdlock(&_thr_list_lock); \ } while (0) #define THREAD_LIST_WRLOCK(curthrd) \ do { \ (curthrd)->locklevel++; \ _thr_rwl_wrlock(&_thr_list_lock); \ } while (0) #define THREAD_LIST_UNLOCK(curthrd) \ do { \ _thr_rwl_unlock(&_thr_list_lock); \ (curthrd)->locklevel--; \ _thr_ast(curthrd); \ } while (0) /* * Macros to insert/remove threads to the all thread list and * the gc list. */ #define THR_LIST_ADD(thrd) do { \ if (((thrd)->tlflags & TLFLAGS_IN_TDLIST) == 0) { \ TAILQ_INSERT_HEAD(&_thread_list, thrd, tle); \ _thr_hash_add(thrd); \ (thrd)->tlflags |= TLFLAGS_IN_TDLIST; \ } \ } while (0) #define THR_LIST_REMOVE(thrd) do { \ if (((thrd)->tlflags & TLFLAGS_IN_TDLIST) != 0) { \ TAILQ_REMOVE(&_thread_list, thrd, tle); \ _thr_hash_remove(thrd); \ (thrd)->tlflags &= ~TLFLAGS_IN_TDLIST; \ } \ } while (0) #define THR_GCLIST_ADD(thrd) do { \ if (((thrd)->tlflags & TLFLAGS_IN_GCLIST) == 0) { \ TAILQ_INSERT_HEAD(&_thread_gc_list, thrd, gcle);\ (thrd)->tlflags |= TLFLAGS_IN_GCLIST; \ _gc_count++; \ } \ } while (0) #define THR_GCLIST_REMOVE(thrd) do { \ if (((thrd)->tlflags & TLFLAGS_IN_GCLIST) != 0) { \ TAILQ_REMOVE(&_thread_gc_list, thrd, gcle); \ (thrd)->tlflags &= ~TLFLAGS_IN_GCLIST; \ _gc_count--; \ } \ } while (0) #define THR_REF_ADD(curthread, pthread) { \ THR_CRITICAL_ENTER(curthread); \ pthread->refcount++; \ } while (0) #define THR_REF_DEL(curthread, pthread) { \ pthread->refcount--; \ THR_CRITICAL_LEAVE(curthread); \ } while (0) #define GC_NEEDED() (_gc_count >= 5) #define SHOULD_REPORT_EVENT(curthr, e) \ (curthr->report_events && \ (((curthr)->event_mask | _thread_event_mask ) & e) != 0) extern int __isthreaded; /* * Global variables for the pthread kernel. */ extern char *_usrstack __hidden; extern struct pthread *_thr_initial __hidden; /* For debugger */ extern int _libthr_debug; extern int _thread_event_mask; extern struct pthread *_thread_last_event; /* List of all threads: */ extern pthreadlist _thread_list; /* List of threads needing GC: */ extern pthreadlist _thread_gc_list __hidden; extern int _thread_active_threads; extern atfork_head _thr_atfork_list __hidden; extern struct urwlock _thr_atfork_lock __hidden; /* Default thread attributes: */ extern struct pthread_attr _pthread_attr_default __hidden; /* Default mutex attributes: */ extern struct pthread_mutex_attr _pthread_mutexattr_default __hidden; extern struct pthread_mutex_attr _pthread_mutexattr_adaptive_default __hidden; /* Default condition variable attributes: */ extern struct pthread_cond_attr _pthread_condattr_default __hidden; extern struct pthread_prio _thr_priorities[] __hidden; extern pid_t _thr_pid __hidden; extern int _thr_is_smp __hidden; extern size_t _thr_guard_default __hidden; extern size_t _thr_stack_default __hidden; extern size_t _thr_stack_initial __hidden; extern int _thr_page_size __hidden; extern int _thr_spinloops __hidden; extern int _thr_yieldloops __hidden; extern int _thr_queuefifo __hidden; /* Garbage thread count. */ extern int _gc_count __hidden; extern struct umutex _mutex_static_lock __hidden; extern struct umutex _cond_static_lock __hidden; extern struct umutex _rwlock_static_lock __hidden; extern struct umutex _keytable_lock __hidden; extern struct urwlock _thr_list_lock __hidden; extern struct umutex _thr_event_lock __hidden; extern struct umutex _suspend_all_lock __hidden; extern int _suspend_all_waiters __hidden; extern int _suspend_all_cycle __hidden; extern struct pthread *_single_thread __hidden; /* * Function prototype definitions. */ __BEGIN_DECLS int _thr_setthreaded(int) __hidden; int _mutex_cv_lock(struct pthread_mutex *, int) __hidden; int _mutex_cv_unlock(struct pthread_mutex *, int *, int *) __hidden; int _mutex_cv_attach(struct pthread_mutex *, int) __hidden; int _mutex_cv_detach(struct pthread_mutex *, int *) __hidden; int _mutex_owned(struct pthread *, const struct pthread_mutex *) __hidden; int _mutex_reinit(pthread_mutex_t *) __hidden; void _mutex_fork(struct pthread *curthread) __hidden; void _libpthread_init(struct pthread *) __hidden; struct pthread *_thr_alloc(struct pthread *) __hidden; void _thread_exit(const char *, int, const char *) __hidden __dead2; int _thr_ref_add(struct pthread *, struct pthread *, int) __hidden; void _thr_ref_delete(struct pthread *, struct pthread *) __hidden; void _thr_ref_delete_unlocked(struct pthread *, struct pthread *) __hidden; int _thr_find_thread(struct pthread *, struct pthread *, int) __hidden; void _thr_rtld_init(void) __hidden; void _thr_rtld_postfork_child(void) __hidden; int _thr_stack_alloc(struct pthread_attr *) __hidden; void _thr_stack_free(struct pthread_attr *) __hidden; void _thr_free(struct pthread *, struct pthread *) __hidden; void _thr_gc(struct pthread *) __hidden; void _thread_cleanupspecific(void) __hidden; void _thread_printf(int, const char *, ...) __hidden; void _thr_spinlock_init(void) __hidden; void _thr_cancel_enter(struct pthread *) __hidden; void _thr_cancel_enter2(struct pthread *, int) __hidden; void _thr_cancel_leave(struct pthread *, int) __hidden; void _thr_testcancel(struct pthread *) __hidden; void _thr_signal_block(struct pthread *) __hidden; void _thr_signal_unblock(struct pthread *) __hidden; void _thr_signal_init(int) __hidden; void _thr_signal_deinit(void) __hidden; int _thr_send_sig(struct pthread *, int sig) __hidden; void _thr_list_init(void) __hidden; void _thr_hash_add(struct pthread *) __hidden; void _thr_hash_remove(struct pthread *) __hidden; struct pthread *_thr_hash_find(struct pthread *) __hidden; void _thr_link(struct pthread *, struct pthread *) __hidden; void _thr_unlink(struct pthread *, struct pthread *) __hidden; void _thr_assert_lock_level(void) __hidden __dead2; void _thr_ast(struct pthread *) __hidden; void _thr_once_init(void) __hidden; void _thr_report_creation(struct pthread *curthread, struct pthread *newthread) __hidden; void _thr_report_death(struct pthread *curthread) __hidden; int _thr_getscheduler(lwpid_t, int *, struct sched_param *) __hidden; int _thr_setscheduler(lwpid_t, int, const struct sched_param *) __hidden; void _thr_signal_prefork(void) __hidden; void _thr_signal_postfork(void) __hidden; void _thr_signal_postfork_child(void) __hidden; void _thr_suspend_all_lock(struct pthread *) __hidden; void _thr_suspend_all_unlock(struct pthread *) __hidden; void _thr_try_gc(struct pthread *, struct pthread *) __hidden; int _rtp_to_schedparam(const struct rtprio *rtp, int *policy, struct sched_param *param) __hidden; int _schedparam_to_rtp(int policy, const struct sched_param *param, struct rtprio *rtp) __hidden; void _thread_bp_create(void); void _thread_bp_death(void); int _sched_yield(void); void _pthread_cleanup_push(void (*)(void *), void *); void _pthread_cleanup_pop(int); void _pthread_exit_mask(void *status, sigset_t *mask) __dead2 __hidden; void _pthread_cancel_enter(int maycancel); void _pthread_cancel_leave(int maycancel); /* #include */ #ifdef _SYS_FCNTL_H_ int __sys_fcntl(int, int, ...); int __sys_open(const char *, int, ...); int __sys_openat(int, const char *, int, ...); #endif /* #include */ #ifdef _SIGNAL_H_ int __sys_kill(pid_t, int); int __sys_sigaction(int, const struct sigaction *, struct sigaction *); int __sys_sigpending(sigset_t *); int __sys_sigprocmask(int, const sigset_t *, sigset_t *); int __sys_sigsuspend(const sigset_t *); int __sys_sigreturn(const ucontext_t *); int __sys_sigaltstack(const struct sigaltstack *, struct sigaltstack *); int __sys_sigwait(const sigset_t *, int *); int __sys_sigtimedwait(const sigset_t *, siginfo_t *, const struct timespec *); int __sys_sigwaitinfo(const sigset_t *set, siginfo_t *info); #endif /* #include */ #ifdef _TIME_H_ int __sys_nanosleep(const struct timespec *, struct timespec *); #endif /* #include */ #ifdef _SYS_UCONTEXT_H_ int __sys_setcontext(const ucontext_t *ucp); int __sys_swapcontext(ucontext_t *oucp, const ucontext_t *ucp); #endif /* #include */ #ifdef _UNISTD_H_ int __sys_close(int); int __sys_fork(void); pid_t __sys_getpid(void); ssize_t __sys_read(int, void *, size_t); void __sys_exit(int); #endif static inline int _thr_isthreaded(void) { return (__isthreaded != 0); } static inline int _thr_is_inited(void) { return (_thr_initial != NULL); } static inline void _thr_check_init(void) { if (_thr_initial == NULL) _libpthread_init(NULL); } struct wake_addr *_thr_alloc_wake_addr(void); void _thr_release_wake_addr(struct wake_addr *); int _thr_sleep(struct pthread *, int, const struct timespec *); void _thr_wake_addr_init(void) __hidden; static inline void _thr_clear_wake(struct pthread *td) { td->wake_addr->value = 0; } static inline int _thr_is_woken(struct pthread *td) { return td->wake_addr->value != 0; } static inline void _thr_set_wake(unsigned int *waddr) { *waddr = 1; _thr_umtx_wake(waddr, INT_MAX, 0); } void _thr_wake_all(unsigned int *waddrs[], int) __hidden; static inline struct pthread * _sleepq_first(struct sleepqueue *sq) { return TAILQ_FIRST(&sq->sq_blocked); } void _sleepq_init(void) __hidden; struct sleepqueue *_sleepq_alloc(void) __hidden; void _sleepq_free(struct sleepqueue *) __hidden; void _sleepq_lock(void *) __hidden; void _sleepq_unlock(void *) __hidden; struct sleepqueue *_sleepq_lookup(void *) __hidden; void _sleepq_add(void *, struct pthread *) __hidden; int _sleepq_remove(struct sleepqueue *, struct pthread *) __hidden; void _sleepq_drop(struct sleepqueue *, void (*cb)(struct pthread *, void *arg), void *) __hidden; int _pthread_mutex_init_calloc_cb(pthread_mutex_t *mutex, void *(calloc_cb)(size_t, size_t)); struct dl_phdr_info; void __pthread_cxa_finalize(struct dl_phdr_info *phdr_info); void _thr_tsd_unload(struct dl_phdr_info *phdr_info) __hidden; void _thr_sigact_unload(struct dl_phdr_info *phdr_info) __hidden; void _thr_stack_fix_protection(struct pthread *thrd); int *__error_threaded(void) __hidden; void __thr_interpose_libc(void) __hidden; pid_t __thr_fork(void); int __thr_setcontext(const ucontext_t *ucp); int __thr_sigaction(int sig, const struct sigaction *act, struct sigaction *oact) __hidden; int __thr_sigprocmask(int how, const sigset_t *set, sigset_t *oset); int __thr_sigsuspend(const sigset_t * set); int __thr_sigtimedwait(const sigset_t *set, siginfo_t *info, const struct timespec * timeout); int __thr_sigwait(const sigset_t *set, int *sig); int __thr_sigwaitinfo(const sigset_t *set, siginfo_t *info); int __thr_swapcontext(ucontext_t *oucp, const ucontext_t *ucp); +struct _spinlock; +void __thr_spinunlock(struct _spinlock *lck); +void __thr_spinlock(struct _spinlock *lck); + struct tcb *_tcb_ctor(struct pthread *, int); void _tcb_dtor(struct tcb *); __END_DECLS #endif /* !_THR_PRIVATE_H */ Index: projects/building-blocks/lib/libthr/thread/thr_spinlock.c =================================================================== --- projects/building-blocks/lib/libthr/thread/thr_spinlock.c (revision 278776) +++ projects/building-blocks/lib/libthr/thread/thr_spinlock.c (revision 278777) @@ -1,130 +1,124 @@ /* * Copyright (c) 1997 John Birrell . * 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 author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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. * * $FreeBSD$ * */ #include #include #include #include #include "thr_private.h" #define MAX_SPINLOCKS 72 /* * These data structures are used to trace all spinlocks * in libc. */ struct spinlock_extra { spinlock_t *owner; struct umutex lock; }; static struct umutex spinlock_static_lock = DEFAULT_UMUTEX; static struct spinlock_extra extra[MAX_SPINLOCKS]; static int spinlock_count; static int initialized; static void init_spinlock(spinlock_t *lck); /* * These are for compatability only. Spinlocks of this type * are deprecated. */ void -_spinunlock(spinlock_t *lck) +__thr_spinunlock(spinlock_t *lck) { struct spinlock_extra *_extra; _extra = (struct spinlock_extra *)lck->fname; THR_UMUTEX_UNLOCK(_get_curthread(), &_extra->lock); } void -_spinlock(spinlock_t *lck) +__thr_spinlock(spinlock_t *lck) { struct spinlock_extra *_extra; if (!__isthreaded) PANIC("Spinlock called when not threaded."); if (!initialized) PANIC("Spinlocks not initialized."); if (lck->fname == NULL) init_spinlock(lck); _extra = (struct spinlock_extra *)lck->fname; THR_UMUTEX_LOCK(_get_curthread(), &_extra->lock); -} - -void -_spinlock_debug(spinlock_t *lck, char *fname __unused, int lineno __unused) -{ - _spinlock(lck); } static void init_spinlock(spinlock_t *lck) { struct pthread *curthread = _get_curthread(); THR_UMUTEX_LOCK(curthread, &spinlock_static_lock); if ((lck->fname == NULL) && (spinlock_count < MAX_SPINLOCKS)) { lck->fname = (char *)&extra[spinlock_count]; _thr_umutex_init(&extra[spinlock_count].lock); extra[spinlock_count].owner = lck; spinlock_count++; } THR_UMUTEX_UNLOCK(curthread, &spinlock_static_lock); if (lck->fname == NULL) PANIC("Warning: exceeded max spinlocks"); } void _thr_spinlock_init(void) { int i; _thr_umutex_init(&spinlock_static_lock); if (initialized != 0) { /* * called after fork() to reset state of libc spin locks, * it is not quite right since libc may be in inconsistent * state, resetting the locks to allow current thread to be * able to hold them may not help things too much, but * anyway, we do our best. * it is better to do pthread_atfork in libc. */ for (i = 0; i < spinlock_count; i++) _thr_umutex_init(&extra[i].lock); } else { initialized = 1; } } Index: projects/building-blocks/lib/libthr/thread/thr_syscalls.c =================================================================== --- projects/building-blocks/lib/libthr/thread/thr_syscalls.c (revision 278776) +++ projects/building-blocks/lib/libthr/thread/thr_syscalls.c (revision 278777) @@ -1,604 +1,606 @@ /* * Copyright (c) 2014 The FreeBSD Foundation. * Copyright (C) 2005 David Xu . * Copyright (c) 2003 Daniel Eischen . * Copyright (C) 2000 Jason Evans . * All rights reserved. * * Portions of this software were developed by Konstantin Belousov * under sponsorship from the FreeBSD Foundation. * * 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(s), this list of conditions and the following disclaimer as * the first lines of this file unmodified other than the possible * addition of one or more copyright notices. * 2. Redistributions in binary form must reproduce the above copyright * notice(s), 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 HOLDER(S) ``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(S) 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. */ /* * Copyright (c) 1995-1998 John Birrell * 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 author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL 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 AUTHOR 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 __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "un-namespace.h" #include "libc_private.h" #include "thr_private.h" #ifdef SYSCALL_COMPAT extern int __fcntl_compat(int, int, ...); #endif static int __thr_accept(int s, struct sockaddr *addr, socklen_t *addrlen) { struct pthread *curthread; int ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_accept(s, addr, addrlen); _thr_cancel_leave(curthread, ret == -1); return (ret); } /* * Cancellation behavior: * If thread is canceled, no socket is created. */ static int __thr_accept4(int s, struct sockaddr *addr, socklen_t *addrlen, int flags) { struct pthread *curthread; int ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_accept4(s, addr, addrlen, flags); _thr_cancel_leave(curthread, ret == -1); return (ret); } static int __thr_aio_suspend(const struct aiocb * const iocbs[], int niocb, const struct timespec *timeout) { struct pthread *curthread; int ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_aio_suspend(iocbs, niocb, timeout); _thr_cancel_leave(curthread, 1); return (ret); } /* * Cancellation behavior: * According to manual of close(), the file descriptor is always deleted. * Here, thread is only canceled after the system call, so the file * descriptor is always deleted despite whether the thread is canceled * or not. */ static int __thr_close(int fd) { struct pthread *curthread; int ret; curthread = _get_curthread(); _thr_cancel_enter2(curthread, 0); ret = __sys_close(fd); _thr_cancel_leave(curthread, 1); return (ret); } /* * Cancellation behavior: * If the thread is canceled, connection is not made. */ static int __thr_connect(int fd, const struct sockaddr *name, socklen_t namelen) { struct pthread *curthread; int ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_connect(fd, name, namelen); _thr_cancel_leave(curthread, ret == -1); return (ret); } /* * Cancellation behavior: * According to specification, only F_SETLKW is a cancellation point. * Thread is only canceled at start, or canceled if the system call * is failure, this means the function does not generate side effect * if it is canceled. */ static int __thr_fcntl(int fd, int cmd, ...) { struct pthread *curthread; int ret; va_list ap; curthread = _get_curthread(); va_start(ap, cmd); if (cmd == F_OSETLKW || cmd == F_SETLKW) { _thr_cancel_enter(curthread); #ifdef SYSCALL_COMPAT ret = __fcntl_compat(fd, cmd, va_arg(ap, void *)); #else ret = __sys_fcntl(fd, cmd, va_arg(ap, void *)); #endif _thr_cancel_leave(curthread, ret == -1); } else { #ifdef SYSCALL_COMPAT ret = __fcntl_compat(fd, cmd, va_arg(ap, void *)); #else ret = __sys_fcntl(fd, cmd, va_arg(ap, void *)); #endif } va_end(ap); return (ret); } /* * Cancellation behavior: * Thread may be canceled after system call. */ static int __thr_fsync(int fd) { struct pthread *curthread; int ret; curthread = _get_curthread(); _thr_cancel_enter2(curthread, 0); ret = __sys_fsync(fd); _thr_cancel_leave(curthread, 1); return (ret); } /* * Cancellation behavior: * Thread may be canceled after system call. */ static int __thr_msync(void *addr, size_t len, int flags) { struct pthread *curthread; int ret; curthread = _get_curthread(); _thr_cancel_enter2(curthread, 0); ret = __sys_msync(addr, len, flags); _thr_cancel_leave(curthread, 1); return (ret); } static int __thr_nanosleep(const struct timespec *time_to_sleep, struct timespec *time_remaining) { struct pthread *curthread; int ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_nanosleep(time_to_sleep, time_remaining); _thr_cancel_leave(curthread, 1); return (ret); } /* * Cancellation behavior: * If the thread is canceled, file is not opened. */ static int __thr_openat(int fd, const char *path, int flags, ...) { struct pthread *curthread; int mode, ret; va_list ap; /* Check if the file is being created: */ if ((flags & O_CREAT) != 0) { /* Get the creation mode: */ va_start(ap, flags); mode = va_arg(ap, int); va_end(ap); } else { mode = 0; } curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_openat(fd, path, flags, mode); _thr_cancel_leave(curthread, ret == -1); return (ret); } /* * Cancellation behavior: * Thread may be canceled at start, but if the system call returns something, * the thread is not canceled. */ static int __thr_poll(struct pollfd *fds, unsigned int nfds, int timeout) { struct pthread *curthread; int ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_poll(fds, nfds, timeout); _thr_cancel_leave(curthread, ret == -1); return (ret); } /* * Cancellation behavior: * Thread may be canceled at start, but if the system call returns something, * the thread is not canceled. */ static int __thr_pselect(int count, fd_set *rfds, fd_set *wfds, fd_set *efds, const struct timespec *timo, const sigset_t *mask) { struct pthread *curthread; int ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_pselect(count, rfds, wfds, efds, timo, mask); _thr_cancel_leave(curthread, ret == -1); return (ret); } /* * Cancellation behavior: * Thread may be canceled at start, but if the system call got some data, * the thread is not canceled. */ static ssize_t __thr_read(int fd, void *buf, size_t nbytes) { struct pthread *curthread; ssize_t ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_read(fd, buf, nbytes); _thr_cancel_leave(curthread, ret == -1); return (ret); } /* * Cancellation behavior: * Thread may be canceled at start, but if the system call got some data, * the thread is not canceled. */ static ssize_t __thr_readv(int fd, const struct iovec *iov, int iovcnt) { struct pthread *curthread; ssize_t ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_readv(fd, iov, iovcnt); _thr_cancel_leave(curthread, ret == -1); return (ret); } /* * Cancellation behavior: * Thread may be canceled at start, but if the system call got some data, * the thread is not canceled. */ static ssize_t __thr_recvfrom(int s, void *b, size_t l, int f, struct sockaddr *from, socklen_t *fl) { struct pthread *curthread; ssize_t ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_recvfrom(s, b, l, f, from, fl); _thr_cancel_leave(curthread, ret == -1); return (ret); } /* * Cancellation behavior: * Thread may be canceled at start, but if the system call got some data, * the thread is not canceled. */ static ssize_t __thr_recvmsg(int s, struct msghdr *m, int f) { struct pthread *curthread; ssize_t ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_recvmsg(s, m, f); _thr_cancel_leave(curthread, ret == -1); return (ret); } /* * Cancellation behavior: * Thread may be canceled at start, but if the system call returns something, * the thread is not canceled. */ static int __thr_select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) { struct pthread *curthread; int ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_select(numfds, readfds, writefds, exceptfds, timeout); _thr_cancel_leave(curthread, ret == -1); return (ret); } /* * Cancellation behavior: * Thread may be canceled at start, but if the system call sent * data, the thread is not canceled. */ static ssize_t __thr_sendmsg(int s, const struct msghdr *m, int f) { struct pthread *curthread; ssize_t ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_sendmsg(s, m, f); _thr_cancel_leave(curthread, ret <= 0); return (ret); } /* * Cancellation behavior: * Thread may be canceled at start, but if the system call sent some * data, the thread is not canceled. */ static ssize_t __thr_sendto(int s, const void *m, size_t l, int f, const struct sockaddr *t, socklen_t tl) { struct pthread *curthread; ssize_t ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_sendto(s, m, l, f, t, tl); _thr_cancel_leave(curthread, ret <= 0); return (ret); } static int __thr_system(const char *string) { struct pthread *curthread; int ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __libc_system(string); _thr_cancel_leave(curthread, 1); return (ret); } /* * Cancellation behavior: * If thread is canceled, the system call is not completed, * this means not all bytes were drained. */ static int __thr_tcdrain(int fd) { struct pthread *curthread; int ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __libc_tcdrain(fd); _thr_cancel_leave(curthread, ret == -1); return (ret); } /* * Cancellation behavior: * Thread may be canceled at start, but if the system call returns * a child pid, the thread is not canceled. */ static pid_t __thr_wait4(pid_t pid, int *status, int options, struct rusage *rusage) { struct pthread *curthread; pid_t ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_wait4(pid, status, options, rusage); _thr_cancel_leave(curthread, ret <= 0); return (ret); } /* * Cancellation behavior: * Thread may be canceled at start, but if the thread wrote some data, * it is not canceled. */ static ssize_t __thr_write(int fd, const void *buf, size_t nbytes) { struct pthread *curthread; ssize_t ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_write(fd, buf, nbytes); _thr_cancel_leave(curthread, (ret <= 0)); return (ret); } /* * Cancellation behavior: * Thread may be canceled at start, but if the thread wrote some data, * it is not canceled. */ static ssize_t __thr_writev(int fd, const struct iovec *iov, int iovcnt) { struct pthread *curthread; ssize_t ret; curthread = _get_curthread(); _thr_cancel_enter(curthread); ret = __sys_writev(fd, iov, iovcnt); _thr_cancel_leave(curthread, (ret <= 0)); return (ret); } void __thr_interpose_libc(void) { __set_error_selector(__error_threaded); #define SLOT(name) \ *(__libc_interposing_slot(INTERPOS_##name)) = \ (interpos_func_t)__thr_##name; SLOT(accept); SLOT(accept4); SLOT(aio_suspend); SLOT(close); SLOT(connect); SLOT(fcntl); SLOT(fsync); SLOT(fork); SLOT(msync); SLOT(nanosleep); SLOT(openat); SLOT(poll); SLOT(pselect); SLOT(read); SLOT(readv); SLOT(recvfrom); SLOT(recvmsg); SLOT(select); SLOT(sendmsg); SLOT(sendto); SLOT(setcontext); SLOT(sigaction); SLOT(sigprocmask); SLOT(sigsuspend); SLOT(sigwait); SLOT(sigtimedwait); SLOT(sigwaitinfo); SLOT(swapcontext); SLOT(system); SLOT(tcdrain); SLOT(wait4); SLOT(write); SLOT(writev); + SLOT(spinlock); + SLOT(spinunlock); #undef SLOT *(__libc_interposing_slot( INTERPOS__pthread_mutex_init_calloc_cb)) = (interpos_func_t)_pthread_mutex_init_calloc_cb; } Index: projects/building-blocks/sbin/sysctl/sysctl.8 =================================================================== --- projects/building-blocks/sbin/sysctl/sysctl.8 (revision 278776) +++ projects/building-blocks/sbin/sysctl/sysctl.8 (revision 278777) @@ -1,313 +1,324 @@ .\" Copyright (c) 1993 .\" The Regents of the University of California. 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. .\" 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. .\" .\" From: @(#)sysctl.8 8.1 (Berkeley) 6/6/93 .\" $FreeBSD$ .\" -.Dd December 13, 2012 +.Dd February 12, 2015 .Dt SYSCTL 8 .Os .Sh NAME .Nm sysctl .Nd get or set kernel state .Sh SYNOPSIS .Nm .Op Fl bdehiNnoRTqx +.Op Fl B Ar bufsize .Op Fl f Ar filename .Ar name Ns Op = Ns Ar value .Ar ... .Nm .Op Fl bdehNnoRTqx +.Op Fl B Ar bufsize .Fl a .Sh DESCRIPTION The .Nm utility retrieves kernel state and allows processes with appropriate privilege to set kernel state. The state to be retrieved or set is described using a .Dq Management Information Base .Pq Dq MIB style name, described as a dotted set of components. .Pp The following options are available: .Bl -tag -width indent .It Fl A Equivalent to .Fl o a (for compatibility). .It Fl a List all the currently available non-opaque values. This option is ignored if one or more variable names are specified on the command line. .It Fl b Force the value of the variable(s) to be output in raw, binary format. No names are printed and no terminating newlines are output. This is mostly useful with a single variable. +.It Fl B Ar bufsize +Set the buffer size to read from the +.Nm +to +.Ar bufsize . +This is necessary for a +.Nm +that has variable length, and the probe value of 0 is a valid length, such as +.Va kern.arandom . .It Fl d Print the description of the variable instead of its value. .It Fl e Separate the name and the value of the variable(s) with .Ql = . This is useful for producing output which can be fed back to the .Nm utility. This option is ignored if either .Fl N or .Fl n is specified, or a variable is being set. .It Fl f Ar filename Specify a file which contains a pair of name and value in each line. .Nm reads and processes the specified file first and then processes the name and value pairs in the command line argument. .It Fl h Format output for human, rather than machine, readability. .It Fl i Ignore unknown OIDs. The purpose is to make use of .Nm for collecting data from a variety of machines (not all of which are necessarily running exactly the same software) easier. .It Fl N Show only variable names, not their values. This is particularly useful with shells that offer programmable completion. To enable completion of variable names in .Xr zsh 1 Pq Pa ports/shells/zsh , use the following code: .Bd -literal -offset indent listsysctls () { set -A reply $(sysctl -AN ${1%.*}) } compctl -K listsysctls sysctl .Ed .Pp To enable completion of variable names in .Xr tcsh 1 , use: .Pp .Dl "complete sysctl 'n/*/`sysctl -Na`/'" .It Fl n Show only variable values, not their names. This option is useful for setting shell variables. For instance, to save the pagesize in variable .Va psize , use: .Pp .Dl "set psize=`sysctl -n hw.pagesize`" .It Fl o Show opaque variables (which are normally suppressed). The format and length are printed, as well as a hex dump of the first sixteen bytes of the value. .It Fl q Suppress some warnings generated by .Nm to standard error. .It Fl T -Display only variables that are setable via loader (CTLFLAG_TUN). +Display only variables that are settable via loader (CTLFLAG_TUN). .It Fl W Display only writable variables that are not statistical. Useful for determining the set of runtime tunable sysctls. .It Fl X Equivalent to .Fl x a (for compatibility). .It Fl x As .Fl o , but prints a hex dump of the entire value instead of just the first few bytes. .El .Pp The information available from .Nm consists of integers, strings, and opaque types. The .Nm utility only knows about a couple of opaque types, and will resort to hexdumps for the rest. The opaque information is much more useful if retrieved by special purpose programs such as .Xr ps 1 , .Xr systat 1 , and .Xr netstat 1 . .Pp Some of the variables which cannot be modified during normal system operation can be initialized via .Xr loader 8 tunables. This can for example be done by setting them in .Xr loader.conf 5 . Please refer to .Xr loader.conf 5 for more information on which tunables are available and how to set them. .Pp The string and integer information is summarized below. For a detailed description of these variable see .Xr sysctl 3 . .Pp The changeable column indicates whether a process with appropriate privilege can change the value. String and integer values can be set using .Nm . .Bl -column security.bsd.unprivileged_read_msgbuf integerxxx .It Sy "Name Type Changeable" .It "kern.ostype string no" .It "kern.osrelease string no" .It "kern.osrevision integer no" .It "kern.version string no" .It "kern.maxvnodes integer yes" .It "kern.maxproc integer no" .It "kern.maxprocperuid integer yes" .It "kern.maxfiles integer yes" .It "kern.maxfilesperproc integer yes" .It "kern.argmax integer no" .It "kern.securelevel integer raise only" .It "kern.hostname string yes" .It "kern.hostid integer yes" .It "kern.clockrate struct no" .It "kern.posix1version integer no" .It "kern.ngroups integer no" .It "kern.job_control integer no" .It "kern.saved_ids integer no" .It "kern.boottime struct no" .It "kern.domainname string yes" .It "kern.filedelay integer yes" .It "kern.dirdelay integer yes" .It "kern.metadelay integer yes" .It "kern.osreldate string no" .It "kern.bootfile string yes" .It "kern.corefile string yes" .It "kern.logsigexit integer yes" .It "security.bsd.suser_enabled integer yes" .It "security.bsd.see_other_uids integer yes" .It "security.bsd.unprivileged_proc_debug integer yes" .It "security.bsd.unprivileged_read_msgbuf integer yes" .It "vm.loadavg struct no" .It "hw.machine string no" .It "hw.model string no" .It "hw.ncpu integer no" .It "hw.byteorder integer no" .It "hw.physmem integer no" .It "hw.usermem integer no" .It "hw.pagesize integer no" .It "hw.floatingpoint integer no" .It "hw.machine_arch string no" .It "hw.realmem integer no" .It "machdep.adjkerntz integer yes" .It "machdep.disable_rtc_set integer yes" .It "machdep.guessed_bootdev string no" .It "user.cs_path string no" .It "user.bc_base_max integer no" .It "user.bc_dim_max integer no" .It "user.bc_scale_max integer no" .It "user.bc_string_max integer no" .It "user.coll_weights_max integer no" .It "user.expr_nest_max integer no" .It "user.line_max integer no" .It "user.re_dup_max integer no" .It "user.posix2_version integer no" .It "user.posix2_c_bind integer no" .It "user.posix2_c_dev integer no" .It "user.posix2_char_term integer no" .It "user.posix2_fort_dev integer no" .It "user.posix2_fort_run integer no" .It "user.posix2_localedef integer no" .It "user.posix2_sw_dev integer no" .It "user.posix2_upe integer no" .It "user.stream_max integer no" .It "user.tzname_max integer no" .El .Sh FILES .Bl -tag -width ".In netinet/icmp_var.h" -compact .It In sys/sysctl.h definitions for top level identifiers, second level kernel and hardware identifiers, and user level identifiers .It In sys/socket.h definitions for second level network identifiers .It In sys/gmon.h definitions for third level profiling identifiers .It In vm/vm_param.h definitions for second level virtual memory identifiers .It In netinet/in.h definitions for third level Internet identifiers and fourth level IP identifiers .It In netinet/icmp_var.h definitions for fourth level ICMP identifiers .It In netinet/udp_var.h definitions for fourth level UDP identifiers .El .Sh EXAMPLES For example, to retrieve the maximum number of processes allowed in the system, one would use the following request: .Pp .Dl "sysctl kern.maxproc" .Pp To set the maximum number of processes allowed per uid to 1000, one would use the following request: .Pp .Dl "sysctl kern.maxprocperuid=1000" .Pp Information about the system clock rate may be obtained with: .Pp .Dl "sysctl kern.clockrate" .Pp Information about the load average history may be obtained with: .Pp .Dl "sysctl vm.loadavg" .Pp More variables than these exist, and the best and likely only place to search for their deeper meaning is undoubtedly the source where they are defined. .Sh COMPATIBILITY The .Fl w option has been deprecated and is silently ignored. .Sh SEE ALSO .Xr sysctl 3 , .Xr loader.conf 5 , .Xr sysctl.conf 5 , .Xr loader 8 .Sh HISTORY A .Nm utility first appeared in .Bx 4.4 . .Pp In .Fx 2.2 , .Nm was significantly remodeled. .Sh BUGS The .Nm utility presently exploits an undocumented interface to the kernel sysctl facility to traverse the sysctl tree and to retrieve format and name information. This correct interface is being thought about for the time being. Index: projects/building-blocks/sbin/sysctl/sysctl.c =================================================================== --- projects/building-blocks/sbin/sysctl/sysctl.c (revision 278776) +++ projects/building-blocks/sbin/sysctl/sysctl.c (revision 278777) @@ -1,990 +1,997 @@ /* * Copyright (c) 1993 * The Regents of the University of California. 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. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)from: sysctl.c 8.1 (Berkeley) 6/6/93"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #ifdef __amd64__ #include #include #endif #if defined(__amd64__) || defined(__i386__) #include #endif #include #include #include #include #include #include #include #include #include #include #include static const char *conffile; -static int aflag, bflag, dflag, eflag, hflag, iflag; +static int aflag, bflag, Bflag, dflag, eflag, hflag, iflag; static int Nflag, nflag, oflag, qflag, Tflag, Wflag, xflag; static int oidfmt(int *, int, char *, u_int *); static int parsefile(const char *); static int parse(const char *, int); static int show_var(int *, int); static int sysctl_all(int *oid, int len); static int name2oid(const char *, int *); static int strIKtoi(const char *, char **); static int ctl_sign[CTLTYPE+1] = { [CTLTYPE_INT] = 1, [CTLTYPE_LONG] = 1, [CTLTYPE_S64] = 1, }; static int ctl_size[CTLTYPE+1] = { [CTLTYPE_INT] = sizeof(int), [CTLTYPE_UINT] = sizeof(u_int), [CTLTYPE_LONG] = sizeof(long), [CTLTYPE_ULONG] = sizeof(u_long), [CTLTYPE_S64] = sizeof(int64_t), [CTLTYPE_U64] = sizeof(uint64_t), }; static const char *ctl_typename[CTLTYPE+1] = { [CTLTYPE_INT] = "integer", [CTLTYPE_UINT] = "unsigned integer", [CTLTYPE_LONG] = "long integer", [CTLTYPE_ULONG] = "unsigned long", [CTLTYPE_S64] = "int64_t", [CTLTYPE_U64] = "uint64_t", }; static void usage(void) { (void)fprintf(stderr, "%s\n%s\n", - "usage: sysctl [-bdehiNnoqTWx] [-f filename] name[=value] ...", - " sysctl [-bdehNnoqTWx] -a"); + "usage: sysctl [-bdehiNnoqTWx] [ -B ] [-f filename] name[=value] ...", + " sysctl [-bdehNnoqTWx] [ -B ] -a"); exit(1); } int main(int argc, char **argv) { int ch; int warncount = 0; setlocale(LC_NUMERIC, ""); setbuf(stdout,0); setbuf(stderr,0); - while ((ch = getopt(argc, argv, "Aabdef:hiNnoqTwWxX")) != -1) { + while ((ch = getopt(argc, argv, "AabB:def:hiNnoqTwWxX")) != -1) { switch (ch) { case 'A': /* compatibility */ aflag = oflag = 1; break; case 'a': aflag = 1; break; case 'b': bflag = 1; break; + case 'B': + Bflag = strtol(optarg, NULL, 0); + break; case 'd': dflag = 1; break; case 'e': eflag = 1; break; case 'f': conffile = optarg; break; case 'h': hflag = 1; break; case 'i': iflag = 1; break; case 'N': Nflag = 1; break; case 'n': nflag = 1; break; case 'o': oflag = 1; break; case 'q': qflag = 1; break; case 'T': Tflag = 1; break; case 'w': /* compatibility */ /* ignored */ break; case 'W': Wflag = 1; break; case 'X': /* compatibility */ aflag = xflag = 1; break; case 'x': xflag = 1; break; default: usage(); } } argc -= optind; argv += optind; if (Nflag && nflag) usage(); if (aflag && argc == 0) exit(sysctl_all(0, 0)); if (argc == 0 && conffile == NULL) usage(); warncount = 0; if (conffile != NULL) warncount += parsefile(conffile); while (argc-- > 0) warncount += parse(*argv++, 0); return (warncount); } /* * Parse a name into a MIB entry. * Lookup and print out the MIB entry if it exists. * Set a new value if requested. */ static int parse(const char *string, int lineno) { int len, i, j; const void *newval; const char *newvalstr = NULL; int intval; unsigned int uintval; long longval; unsigned long ulongval; - size_t newsize = 0; + size_t newsize = Bflag; int64_t i64val; uint64_t u64val; int mib[CTL_MAXNAME]; char *cp, *bufp, buf[BUFSIZ], *endptr = NULL, fmt[BUFSIZ], line[BUFSIZ]; u_int kind; if (lineno) snprintf(line, sizeof(line), " at line %d", lineno); else line[0] = '\0'; cp = buf; if (snprintf(buf, BUFSIZ, "%s", string) >= BUFSIZ) { warnx("oid too long: '%s'%s", string, line); return (1); } bufp = strsep(&cp, "=:"); if (cp != NULL) { /* Tflag just lists tunables, do not allow assignment */ if (Tflag || Wflag) { warnx("Can't set variables when using -T or -W"); usage(); } while (isspace(*cp)) cp++; /* Strip a pair of " or ' if any. */ switch (*cp) { case '\"': case '\'': if (cp[strlen(cp) - 1] == *cp) cp[strlen(cp) - 1] = '\0'; cp++; } newvalstr = cp; newsize = strlen(cp); } len = name2oid(bufp, mib); if (len < 0) { if (iflag) return (0); if (qflag) return (1); else { warn("unknown oid '%s'%s", bufp, line); return (1); } } if (oidfmt(mib, len, fmt, &kind)) { warn("couldn't find format of oid '%s'%s", bufp, line); if (iflag) return (1); else exit(1); } if (newvalstr == NULL || dflag) { if ((kind & CTLTYPE) == CTLTYPE_NODE) { if (dflag) { i = show_var(mib, len); if (!i && !bflag) putchar('\n'); } sysctl_all(mib, len); } else { i = show_var(mib, len); if (!i && !bflag) putchar('\n'); } } else { if ((kind & CTLTYPE) == CTLTYPE_NODE) { warnx("oid '%s' isn't a leaf node%s", bufp, line); return (1); } if (!(kind & CTLFLAG_WR)) { if (kind & CTLFLAG_TUN) { warnx("oid '%s' is a read only tunable%s", bufp, line); warnx("Tunable values are set in /boot/loader.conf"); } else warnx("oid '%s' is read only%s", bufp, line); return (1); } switch (kind & CTLTYPE) { case CTLTYPE_INT: case CTLTYPE_UINT: case CTLTYPE_LONG: case CTLTYPE_ULONG: case CTLTYPE_S64: case CTLTYPE_U64: if (strlen(newvalstr) == 0) { warnx("empty numeric value"); return (1); } /* FALLTHROUGH */ case CTLTYPE_STRING: break; default: warnx("oid '%s' is type %d," " cannot set that%s", bufp, kind & CTLTYPE, line); return (1); } errno = 0; switch (kind & CTLTYPE) { case CTLTYPE_INT: if (strcmp(fmt, "IK") == 0) intval = strIKtoi(newvalstr, &endptr); else intval = (int)strtol(newvalstr, &endptr, 0); newval = &intval; newsize = sizeof(intval); break; case CTLTYPE_UINT: uintval = (int) strtoul(newvalstr, &endptr, 0); newval = &uintval; newsize = sizeof(uintval); break; case CTLTYPE_LONG: longval = strtol(newvalstr, &endptr, 0); newval = &longval; newsize = sizeof(longval); break; case CTLTYPE_ULONG: ulongval = strtoul(newvalstr, &endptr, 0); newval = &ulongval; newsize = sizeof(ulongval); break; case CTLTYPE_STRING: newval = newvalstr; break; case CTLTYPE_S64: i64val = strtoimax(newvalstr, &endptr, 0); newval = &i64val; newsize = sizeof(i64val); break; case CTLTYPE_U64: u64val = strtoumax(newvalstr, &endptr, 0); newval = &u64val; newsize = sizeof(u64val); break; default: /* NOTREACHED */ abort(); } if (errno != 0 || endptr == newvalstr || (endptr != NULL && *endptr != '\0')) { warnx("invalid %s '%s'%s", ctl_typename[kind & CTLTYPE], newvalstr, line); return (1); } i = show_var(mib, len); if (sysctl(mib, len, 0, 0, newval, newsize) == -1) { if (!i && !bflag) putchar('\n'); switch (errno) { case EOPNOTSUPP: warnx("%s: value is not available%s", string, line); return (1); case ENOTDIR: warnx("%s: specification is incomplete%s", string, line); return (1); case ENOMEM: warnx("%s: type is unknown to this program%s", string, line); return (1); default: warn("%s%s", string, line); return (1); } } if (!bflag) printf(" -> "); i = nflag; nflag = 1; j = show_var(mib, len); if (!j && !bflag) putchar('\n'); nflag = i; } return (0); } static int parsefile(const char *filename) { FILE *file; char line[BUFSIZ], *p, *pq, *pdq; int warncount = 0, lineno = 0; file = fopen(filename, "r"); if (file == NULL) err(EX_NOINPUT, "%s", filename); while (fgets(line, sizeof(line), file) != NULL) { lineno++; p = line; pq = strchr(line, '\''); pdq = strchr(line, '\"'); /* Replace the first # with \0. */ while((p = strchr(p, '#')) != NULL) { if (pq != NULL && p > pq) { if ((p = strchr(pq+1, '\'')) != NULL) *(++p) = '\0'; break; } else if (pdq != NULL && p > pdq) { if ((p = strchr(pdq+1, '\"')) != NULL) *(++p) = '\0'; break; } else if (p == line || *(p-1) != '\\') { *p = '\0'; break; } p++; } /* Trim spaces */ p = line + strlen(line) - 1; while (p >= line && isspace((int)*p)) { *p = '\0'; p--; } p = line; while (isspace((int)*p)) p++; if (*p == '\0') continue; else warncount += parse(p, lineno); } fclose(file); return (warncount); } /* These functions will dump out various interesting structures. */ static int S_clockinfo(size_t l2, void *p) { struct clockinfo *ci = (struct clockinfo*)p; if (l2 != sizeof(*ci)) { warnx("S_clockinfo %zu != %zu", l2, sizeof(*ci)); return (1); } printf(hflag ? "{ hz = %'d, tick = %'d, profhz = %'d, stathz = %'d }" : "{ hz = %d, tick = %d, profhz = %d, stathz = %d }", ci->hz, ci->tick, ci->profhz, ci->stathz); return (0); } static int S_loadavg(size_t l2, void *p) { struct loadavg *tv = (struct loadavg*)p; if (l2 != sizeof(*tv)) { warnx("S_loadavg %zu != %zu", l2, sizeof(*tv)); return (1); } printf(hflag ? "{ %'.2f %'.2f %'.2f }" : "{ %.2f %.2f %.2f }", (double)tv->ldavg[0]/(double)tv->fscale, (double)tv->ldavg[1]/(double)tv->fscale, (double)tv->ldavg[2]/(double)tv->fscale); return (0); } static int S_timeval(size_t l2, void *p) { struct timeval *tv = (struct timeval*)p; time_t tv_sec; char *p1, *p2; if (l2 != sizeof(*tv)) { warnx("S_timeval %zu != %zu", l2, sizeof(*tv)); return (1); } printf(hflag ? "{ sec = %'jd, usec = %'ld } " : "{ sec = %jd, usec = %ld } ", (intmax_t)tv->tv_sec, tv->tv_usec); tv_sec = tv->tv_sec; p1 = strdup(ctime(&tv_sec)); for (p2=p1; *p2 ; p2++) if (*p2 == '\n') *p2 = '\0'; fputs(p1, stdout); free(p1); return (0); } static int S_vmtotal(size_t l2, void *p) { struct vmtotal *v = (struct vmtotal *)p; int pageKilo = getpagesize() / 1024; if (l2 != sizeof(*v)) { warnx("S_vmtotal %zu != %zu", l2, sizeof(*v)); return (1); } printf( "\nSystem wide totals computed every five seconds:" " (values in kilobytes)\n"); printf("===============================================\n"); printf( "Processes:\t\t(RUNQ: %hd Disk Wait: %hd Page Wait: " "%hd Sleep: %hd)\n", v->t_rq, v->t_dw, v->t_pw, v->t_sl); printf( "Virtual Memory:\t\t(Total: %dK Active: %dK)\n", v->t_vm * pageKilo, v->t_avm * pageKilo); printf("Real Memory:\t\t(Total: %dK Active: %dK)\n", v->t_rm * pageKilo, v->t_arm * pageKilo); printf("Shared Virtual Memory:\t(Total: %dK Active: %dK)\n", v->t_vmshr * pageKilo, v->t_avmshr * pageKilo); printf("Shared Real Memory:\t(Total: %dK Active: %dK)\n", v->t_rmshr * pageKilo, v->t_armshr * pageKilo); printf("Free Memory:\t%dK", v->t_free * pageKilo); return (0); } #ifdef __amd64__ #define efi_next_descriptor(ptr, size) \ ((struct efi_md *)(((uint8_t *) ptr) + size)) static int S_efi_map(size_t l2, void *p) { struct efi_map_header *efihdr; struct efi_md *map; const char *type; size_t efisz; int ndesc, i; static const char *types[] = { "Reserved", "LoaderCode", "LoaderData", "BootServicesCode", "BootServicesData", "RuntimeServicesCode", "RuntimeServicesData", "ConventionalMemory", "UnusableMemory", "ACPIReclaimMemory", "ACPIMemoryNVS", "MemoryMappedIO", "MemoryMappedIOPortSpace", "PalCode" }; /* * Memory map data provided by UEFI via the GetMemoryMap * Boot Services API. */ if (l2 < sizeof(*efihdr)) { warnx("S_efi_map length less than header"); return (1); } efihdr = p; efisz = (sizeof(struct efi_map_header) + 0xf) & ~0xf; map = (struct efi_md *)((uint8_t *)efihdr + efisz); if (efihdr->descriptor_size == 0) return (0); if (l2 != efisz + efihdr->memory_size) { warnx("S_efi_map length mismatch %zu vs %zu", l2, efisz + efihdr->memory_size); return (1); } ndesc = efihdr->memory_size / efihdr->descriptor_size; printf("\n%23s %12s %12s %8s %4s", "Type", "Physical", "Virtual", "#Pages", "Attr"); for (i = 0; i < ndesc; i++, map = efi_next_descriptor(map, efihdr->descriptor_size)) { if (map->md_type <= EFI_MD_TYPE_PALCODE) type = types[map->md_type]; else type = ""; printf("\n%23s %012lx %12p %08lx ", type, map->md_phys, map->md_virt, map->md_pages); if (map->md_attr & EFI_MD_ATTR_UC) printf("UC "); if (map->md_attr & EFI_MD_ATTR_WC) printf("WC "); if (map->md_attr & EFI_MD_ATTR_WT) printf("WT "); if (map->md_attr & EFI_MD_ATTR_WB) printf("WB "); if (map->md_attr & EFI_MD_ATTR_UCE) printf("UCE "); if (map->md_attr & EFI_MD_ATTR_WP) printf("WP "); if (map->md_attr & EFI_MD_ATTR_RP) printf("RP "); if (map->md_attr & EFI_MD_ATTR_XP) printf("XP "); if (map->md_attr & EFI_MD_ATTR_RT) printf("RUNTIME"); } return (0); } #endif #if defined(__amd64__) || defined(__i386__) static int S_bios_smap_xattr(size_t l2, void *p) { struct bios_smap_xattr *smap, *end; if (l2 % sizeof(*smap) != 0) { warnx("S_bios_smap_xattr %zu is not a multiple of %zu", l2, sizeof(*smap)); return (1); } end = (struct bios_smap_xattr *)((char *)p + l2); for (smap = p; smap < end; smap++) printf("\nSMAP type=%02x, xattr=%02x, base=%016jx, len=%016jx", smap->type, smap->xattr, (uintmax_t)smap->base, (uintmax_t)smap->length); return (0); } #endif static int strIKtoi(const char *str, char **endptrp) { int kelv; float temp; size_t len; const char *p; assert(errno == 0); len = strlen(str); /* caller already checked this */ assert(len > 0); p = &str[len - 1]; if (*p == 'C' || *p == 'F') { temp = strtof(str, endptrp); if (*endptrp != str && *endptrp == p && errno == 0) { if (*p == 'F') temp = (temp - 32) * 5 / 9; *endptrp = NULL; return (temp * 10 + 2732); } } else { kelv = (int)strtol(str, endptrp, 10); if (*endptrp != str && *endptrp == p && errno == 0) { *endptrp = NULL; return (kelv); } } errno = ERANGE; return (0); } /* * These functions uses a presently undocumented interface to the kernel * to walk the tree and get the type so it can print the value. * This interface is under work and consideration, and should probably * be killed with a big axe by the first person who can find the time. * (be aware though, that the proper interface isn't as obvious as it * may seem, there are various conflicting requirements. */ static int name2oid(const char *name, int *oidp) { int oid[2]; int i; size_t j; oid[0] = 0; oid[1] = 3; j = CTL_MAXNAME * sizeof(int); i = sysctl(oid, 2, oidp, &j, name, strlen(name)); if (i < 0) return (i); j /= sizeof(int); return (j); } static int oidfmt(int *oid, int len, char *fmt, u_int *kind) { int qoid[CTL_MAXNAME+2]; u_char buf[BUFSIZ]; int i; size_t j; qoid[0] = 0; qoid[1] = 4; memcpy(qoid + 2, oid, len * sizeof(int)); j = sizeof(buf); i = sysctl(qoid, len + 2, buf, &j, 0, 0); if (i) err(1, "sysctl fmt %d %zu %d", i, j, errno); if (kind) *kind = *(u_int *)buf; if (fmt) strcpy(fmt, (char *)(buf + sizeof(u_int))); return (0); } /* * This formats and outputs the value of one variable * * Returns zero if anything was actually output. * Returns one if didn't know what to do with this. * Return minus one if we had errors. */ static int show_var(int *oid, int nlen) { u_char buf[BUFSIZ], *val, *oval, *p; char name[BUFSIZ], fmt[BUFSIZ]; const char *sep, *sep1; int qoid[CTL_MAXNAME+2]; uintmax_t umv; intmax_t mv; int i, hexlen, sign, ctltype; size_t intlen; size_t j, len; u_int kind; int (*func)(size_t, void *); /* Silence GCC. */ umv = mv = intlen = 0; bzero(buf, BUFSIZ); bzero(fmt, BUFSIZ); bzero(name, BUFSIZ); qoid[0] = 0; memcpy(qoid + 2, oid, nlen * sizeof(int)); qoid[1] = 1; j = sizeof(name); i = sysctl(qoid, nlen + 2, name, &j, 0, 0); if (i || !j) err(1, "sysctl name %d %zu %d", i, j, errno); oidfmt(oid, nlen, fmt, &kind); /* if Wflag then only list sysctls that are writeable and not stats. */ if (Wflag && ((kind & CTLFLAG_WR) == 0 || (kind & CTLFLAG_STATS) != 0)) return 1; /* if Tflag then only list sysctls that are tuneables. */ if (Tflag && (kind & CTLFLAG_TUN) == 0) return 1; if (Nflag) { printf("%s", name); return (0); } if (eflag) sep = "="; else sep = ": "; if (dflag) { /* just print description */ qoid[1] = 5; j = sizeof(buf); i = sysctl(qoid, nlen + 2, buf, &j, 0, 0); if (!nflag) printf("%s%s", name, sep); printf("%s", buf); return (0); } /* find an estimate of how much we need for this var */ - j = 0; - i = sysctl(oid, nlen, 0, &j, 0, 0); - j += j; /* we want to be sure :-) */ + if (Bflag) + j = Bflag; + else { + j = 0; + i = sysctl(oid, nlen, 0, &j, 0, 0); + j += j; /* we want to be sure :-) */ + } val = oval = malloc(j + 1); if (val == NULL) { warnx("malloc failed"); return (1); } ctltype = (kind & CTLTYPE); len = j; i = sysctl(oid, nlen, val, &len, 0, 0); if (i != 0 || (len == 0 && ctltype != CTLTYPE_STRING)) { free(oval); return (1); } if (bflag) { fwrite(val, 1, len, stdout); free(oval); return (0); } val[len] = '\0'; p = val; sign = ctl_sign[ctltype]; intlen = ctl_size[ctltype]; switch (ctltype) { case CTLTYPE_STRING: if (!nflag) printf("%s%s", name, sep); printf("%.*s", (int)len, p); free(oval); return (0); case CTLTYPE_INT: case CTLTYPE_UINT: case CTLTYPE_LONG: case CTLTYPE_ULONG: case CTLTYPE_S64: case CTLTYPE_U64: if (!nflag) printf("%s%s", name, sep); hexlen = 2 + (intlen * CHAR_BIT + 3) / 4; sep1 = ""; while (len >= intlen) { switch (kind & CTLTYPE) { case CTLTYPE_INT: case CTLTYPE_UINT: umv = *(u_int *)p; mv = *(int *)p; break; case CTLTYPE_LONG: case CTLTYPE_ULONG: umv = *(u_long *)p; mv = *(long *)p; break; case CTLTYPE_S64: case CTLTYPE_U64: umv = *(uint64_t *)p; mv = *(int64_t *)p; break; } fputs(sep1, stdout); if (xflag) printf("%#0*jx", hexlen, umv); else if (!sign) printf(hflag ? "%'ju" : "%ju", umv); else if (fmt[1] == 'K') { if (mv < 0) printf("%jd", mv); else printf("%.1fC", (mv - 2732.0) / 10); } else printf(hflag ? "%'jd" : "%jd", mv); sep1 = " "; len -= intlen; p += intlen; } free(oval); return (0); case CTLTYPE_OPAQUE: i = 0; if (strcmp(fmt, "S,clockinfo") == 0) func = S_clockinfo; else if (strcmp(fmt, "S,timeval") == 0) func = S_timeval; else if (strcmp(fmt, "S,loadavg") == 0) func = S_loadavg; else if (strcmp(fmt, "S,vmtotal") == 0) func = S_vmtotal; #ifdef __amd64__ else if (strcmp(fmt, "S,efi_map_header") == 0) func = S_efi_map; #endif #if defined(__amd64__) || defined(__i386__) else if (strcmp(fmt, "S,bios_smap_xattr") == 0) func = S_bios_smap_xattr; #endif else func = NULL; if (func) { if (!nflag) printf("%s%s", name, sep); i = (*func)(len, p); free(oval); return (i); } /* FALLTHROUGH */ default: if (!oflag && !xflag) { free(oval); return (1); } if (!nflag) printf("%s%s", name, sep); printf("Format:%s Length:%zu Dump:0x", fmt, len); while (len-- && (xflag || p < val + 16)) printf("%02x", *p++); if (!xflag && len > 16) printf("..."); free(oval); return (0); } free(oval); return (1); } static int sysctl_all(int *oid, int len) { int name1[22], name2[22]; int i, j; size_t l1, l2; name1[0] = 0; name1[1] = 2; l1 = 2; if (len) { memcpy(name1+2, oid, len * sizeof(int)); l1 += len; } else { name1[2] = 1; l1++; } for (;;) { l2 = sizeof(name2); j = sysctl(name1, l1, name2, &l2, 0, 0); if (j < 0) { if (errno == ENOENT) return (0); else err(1, "sysctl(getnext) %d %zu", j, l2); } l2 /= sizeof(int); if (len < 0 || l2 < (unsigned int)len) return (0); for (i = 0; i < len; i++) if (name2[i] != oid[i]) return (0); i = show_var(name2, l2); if (!i && !bflag) putchar('\n'); memcpy(name1+2, name2, l2 * sizeof(int)); l1 = 2 + l2; } } Index: projects/building-blocks/sbin =================================================================== --- projects/building-blocks/sbin (revision 278776) +++ projects/building-blocks/sbin (revision 278777) Property changes on: projects/building-blocks/sbin ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/sbin:r278312-278776 Index: projects/building-blocks/share/mk/bsd.lib.mk =================================================================== --- projects/building-blocks/share/mk/bsd.lib.mk (revision 278776) +++ projects/building-blocks/share/mk/bsd.lib.mk (revision 278777) @@ -1,473 +1,473 @@ # from: @(#)bsd.lib.mk 5.26 (Berkeley) 5/2/91 # $FreeBSD$ # .include # Set up the variables controlling shared libraries. After this section, # SHLIB_NAME will be defined only if we are to create a shared library. # SHLIB_LINK will be defined only if we are to create a link to it. # INSTALL_PIC_ARCHIVE will be defined only if we are to create a PIC archive. .if defined(NO_PIC) .undef SHLIB_NAME .undef INSTALL_PIC_ARCHIVE .else .if !defined(SHLIB) && defined(LIB) SHLIB= ${LIB} .endif .if !defined(SHLIB_NAME) && defined(SHLIB) && defined(SHLIB_MAJOR) SHLIB_NAME= lib${SHLIB}.so.${SHLIB_MAJOR} .endif .if defined(SHLIB_NAME) && !empty(SHLIB_NAME:M*.so.*) SHLIB_LINK?= ${SHLIB_NAME:R} .endif SONAME?= ${SHLIB_NAME} .endif .if defined(CRUNCH_CFLAGS) CFLAGS+= ${CRUNCH_CFLAGS} .endif .if ${MK_ASSERT_DEBUG} == "no" CFLAGS+= -DNDEBUG NO_WERROR= .endif .if defined(DEBUG_FLAGS) CFLAGS+= ${DEBUG_FLAGS} .if ${MK_CTF} != "no" && ${DEBUG_FLAGS:M-g} != "" CTFFLAGS+= -g .endif .else STRIP?= -s .endif .if ${MK_DEBUG_FILES} != "no" && empty(DEBUG_FLAGS:M-g) && \ empty(DEBUG_FLAGS:M-gdwarf*) SHARED_CFLAGS+= -g SHARED_CXXFLAGS+= -g CTFFLAGS+= -g .endif .include # prefer .s to a .c, add .po, remove stuff not used in the BSD libraries # .So used for PIC object files .SUFFIXES: .SUFFIXES: .out .o .po .So .S .asm .s .c .cc .cpp .cxx .C .f .y .l .ln .if !defined(PICFLAG) .if ${MACHINE_CPUARCH} == "sparc64" PICFLAG=-fPIC .else PICFLAG=-fpic .endif .endif PO_FLAG=-pg .c.o: ${CC} ${STATIC_CFLAGS} ${CFLAGS} -c ${.IMPSRC} -o ${.TARGET} ${CTFCONVERT_CMD} .c.po: ${CC} ${PO_FLAG} ${STATIC_CFLAGS} ${PO_CFLAGS} -c ${.IMPSRC} -o ${.TARGET} ${CTFCONVERT_CMD} .c.So: ${CC} ${PICFLAG} -DPIC ${SHARED_CFLAGS} ${CFLAGS} -c ${.IMPSRC} -o ${.TARGET} ${CTFCONVERT_CMD} .cc.o .C.o .cpp.o .cxx.o: ${CXX} ${STATIC_CXXFLAGS} ${CXXFLAGS} -c ${.IMPSRC} -o ${.TARGET} .cc.po .C.po .cpp.po .cxx.po: ${CXX} ${PO_FLAG} ${STATIC_CXXFLAGS} ${PO_CXXFLAGS} -c ${.IMPSRC} -o ${.TARGET} .cc.So .C.So .cpp.So .cxx.So: ${CXX} ${PICFLAG} -DPIC ${SHARED_CXXFLAGS} ${CXXFLAGS} -c ${.IMPSRC} -o ${.TARGET} .f.po: ${FC} -pg ${FFLAGS} -o ${.TARGET} -c ${.IMPSRC} ${CTFCONVERT_CMD} .f.So: ${FC} ${PICFLAG} -DPIC ${FFLAGS} -o ${.TARGET} -c ${.IMPSRC} ${CTFCONVERT_CMD} .s.po .s.So: ${AS} ${AFLAGS} -o ${.TARGET} ${.IMPSRC} ${CTFCONVERT_CMD} .asm.po: ${CC} -x assembler-with-cpp -DPROF ${PO_CFLAGS} ${ACFLAGS} \ -c ${.IMPSRC} -o ${.TARGET} ${CTFCONVERT_CMD} .asm.So: ${CC} -x assembler-with-cpp ${PICFLAG} -DPIC ${CFLAGS} ${ACFLAGS} \ -c ${.IMPSRC} -o ${.TARGET} ${CTFCONVERT_CMD} .S.po: ${CC} -DPROF ${PO_CFLAGS} ${ACFLAGS} -c ${.IMPSRC} -o ${.TARGET} ${CTFCONVERT_CMD} .S.So: ${CC} ${PICFLAG} -DPIC ${CFLAGS} ${ACFLAGS} -c ${.IMPSRC} -o ${.TARGET} ${CTFCONVERT_CMD} all: beforebuild .WAIT beforebuild: objwarn .if defined(PRIVATELIB) _LIBDIR:=${LIBPRIVATEDIR} _SHLIBDIR:=${LIBPRIVATEDIR} .else _LIBDIR:=${LIBDIR} _SHLIBDIR:=${SHLIBDIR} .endif .if defined(SHLIB_NAME) .if ${MK_DEBUG_FILES} != "no" SHLIB_NAME_FULL=${SHLIB_NAME}.full # Use ${DEBUGDIR} for base system debug files, else .debug subdirectory .if ${_SHLIBDIR} == "/boot" ||\ ${SHLIBDIR:C%/lib(/.*)?$%/lib%} == "/lib" ||\ ${SHLIBDIR:C%/usr/lib(32)?(/.*)?%/usr/lib%} == "/usr/lib" DEBUGFILEDIR=${DEBUGDIR}${_SHLIBDIR} .else DEBUGFILEDIR=${_SHLIBDIR}/.debug DEBUGMKDIR= .endif .else SHLIB_NAME_FULL=${SHLIB_NAME} .endif .endif .include # Allow libraries to specify their own version map or have it # automatically generated (see bsd.symver.mk above). .if ${MK_SYMVER} == "yes" && !empty(VERSION_MAP) ${SHLIB_NAME_FULL}: ${VERSION_MAP} LDFLAGS+= -Wl,--version-script=${VERSION_MAP} .endif .if defined(USEPRIVATELIB) LDFLAGS+= -rpath ${LIBPRIVATEDIR} .endif .if defined(LIB) && !empty(LIB) || defined(SHLIB_NAME) OBJS+= ${SRCS:N*.h:R:S/$/.o/} NOPATH_FILES+= ${OBJS} .endif .if defined(LIB) && !empty(LIB) _LIBS= lib${LIB}.a lib${LIB}.a: ${OBJS} ${STATICOBJS} @${ECHO} building static ${LIB} library @rm -f ${.TARGET} .if !defined(NM) @${AR} ${ARFLAGS} ${.TARGET} `lorder ${OBJS} ${STATICOBJS} | tsort -q` ${ARADD} .else @${AR} ${ARFLAGS} ${.TARGET} `NM='${NM}' lorder ${OBJS} ${STATICOBJS} | tsort -q` ${ARADD} .endif ${RANLIB} ${RANLIBFLAGS} ${.TARGET} .endif .if !defined(INTERNALLIB) .if ${MK_PROFILE} != "no" && defined(LIB) && !empty(LIB) _LIBS+= lib${LIB}_p.a POBJS+= ${OBJS:.o=.po} ${STATICOBJS:.o=.po} NOPATH_FILES+= ${POBJS} lib${LIB}_p.a: ${POBJS} @${ECHO} building profiled ${LIB} library @rm -f ${.TARGET} .if !defined(NM) @${AR} ${ARFLAGS} ${.TARGET} `lorder ${POBJS} | tsort -q` ${ARADD} .else @${AR} ${ARFLAGS} ${.TARGET} `NM='${NM}' lorder ${POBJS} | tsort -q` ${ARADD} .endif ${RANLIB} ${RANLIBFLAGS} ${.TARGET} .endif .if defined(SHLIB_NAME) || \ defined(INSTALL_PIC_ARCHIVE) && defined(LIB) && !empty(LIB) SOBJS+= ${OBJS:.o=.So} NOPATH_FILES+= ${SOBJS} .endif .if defined(SHLIB_NAME) _LIBS+= ${SHLIB_NAME} SOLINKOPTS= -shared -Wl,-x .if !defined(ALLOW_SHARED_TEXTREL) .if defined(LD_FATAL_WARNINGS) && ${LD_FATAL_WARNINGS} == "no" SOLINKOPTS+= -Wl,--no-fatal-warnings .else SOLINKOPTS+= -Wl,--fatal-warnings .endif SOLINKOPTS+= -Wl,--warn-shared-textrel .endif .if target(beforelinking) beforelinking: ${SOBJS} ${SHLIB_NAME_FULL}: beforelinking .endif ${SHLIB_NAME_FULL}: ${SOBJS} @${ECHO} building shared library ${SHLIB_NAME} @rm -f ${SHLIB_NAME} ${SHLIB_LINK} .if defined(SHLIB_LINK) @${INSTALL_SYMLINK} ${SHLIB_NAME} ${SHLIB_LINK} .endif .if !defined(NM) @${CC} ${LDFLAGS} ${SSP_CFLAGS} ${SOLINKOPTS} \ -o ${.TARGET} -Wl,-soname,${SONAME} \ `lorder ${SOBJS} | tsort -q` ${LDADD} .else @${CC} ${LDFLAGS} ${SSP_CFLAGS} ${SOLINKOPTS} \ -o ${.TARGET} -Wl,-soname,${SONAME} \ `NM='${NM}' lorder ${SOBJS} | tsort -q` ${LDADD} .endif .if ${MK_CTF} != "no" ${CTFMERGE} ${CTFFLAGS} -o ${.TARGET} ${SOBJS} .endif .if ${MK_DEBUG_FILES} != "no" CLEANFILES+= ${SHLIB_NAME_FULL} ${SHLIB_NAME}.debug ${SHLIB_NAME}: ${SHLIB_NAME_FULL} ${SHLIB_NAME}.debug ${OBJCOPY} --strip-debug --add-gnu-debuglink=${SHLIB_NAME}.debug \ ${SHLIB_NAME_FULL} ${.TARGET} ${SHLIB_NAME}.debug: ${SHLIB_NAME_FULL} ${OBJCOPY} --only-keep-debug ${SHLIB_NAME_FULL} ${.TARGET} .endif .endif #defined(SHLIB_NAME) .if defined(INSTALL_PIC_ARCHIVE) && defined(LIB) && !empty(LIB) && ${MK_TOOLCHAIN} != "no" _LIBS+= lib${LIB}_pic.a lib${LIB}_pic.a: ${SOBJS} @${ECHO} building special pic ${LIB} library @rm -f ${.TARGET} @${AR} ${ARFLAGS} ${.TARGET} ${SOBJS} ${ARADD} ${RANLIB} ${RANLIBFLAGS} ${.TARGET} .endif .if defined(WANT_LINT) && !defined(NO_LINT) && defined(LIB) && !empty(LIB) LINTLIB= llib-l${LIB}.ln _LIBS+= ${LINTLIB} LINTOBJS+= ${SRCS:M*.c:.c=.ln} NOPATH_FILES+= ${LINTOBJS} ${LINTLIB}: ${LINTOBJS} @${ECHO} building lint library ${.TARGET} @rm -f ${.TARGET} ${LINT} ${LINTLIBFLAGS} ${CFLAGS:M-[DIU]*} ${.ALLSRC} .endif .endif # !defined(INTERNALLIB) all: ${_LIBS} .if ${MK_MAN} != "no" all: _manpages .endif _EXTRADEPEND: @TMP=_depend$$$$; \ sed -e 's/^\([^\.]*\).o[ ]*:/\1.o \1.po \1.So:/' < ${DEPENDFILE} \ > $$TMP; \ mv $$TMP ${DEPENDFILE} .if !defined(NO_EXTRADEPEND) && defined(SHLIB_NAME) .if defined(DPADD) && !empty(DPADD) - echo ${SHLIB_NAME}: ${DPADD} >> ${DEPENDFILE} + echo ${SHLIB_NAME_FULL}: ${DPADD} >> ${DEPENDFILE} .endif .endif .if !target(install) .if defined(PRECIOUSLIB) .if !defined(NO_FSCHG) SHLINSTALLFLAGS+= -fschg .endif SHLINSTALLFLAGS+= -S .endif _INSTALLFLAGS:= ${INSTALLFLAGS} .for ie in ${INSTALLFLAGS_EDIT} _INSTALLFLAGS:= ${_INSTALLFLAGS${ie}} .endfor _SHLINSTALLFLAGS:= ${SHLINSTALLFLAGS} .for ie in ${INSTALLFLAGS_EDIT} _SHLINSTALLFLAGS:= ${_SHLINSTALLFLAGS${ie}} .endfor .if !defined(INTERNALLIB) realinstall: _libinstall .ORDER: beforeinstall _libinstall _libinstall: .if defined(LIB) && !empty(LIB) && ${MK_INSTALLLIB} != "no" && !defined(PRIVATELIB) ${INSTALL} -C -o ${LIBOWN} -g ${LIBGRP} -m ${LIBMODE} \ ${_INSTALLFLAGS} lib${LIB}.a ${DESTDIR}${_LIBDIR} .endif .if ${MK_PROFILE} != "no" && defined(LIB) && !empty(LIB) && !defined(PRIVATELIB) ${INSTALL} -C -o ${LIBOWN} -g ${LIBGRP} -m ${LIBMODE} \ ${_INSTALLFLAGS} lib${LIB}_p.a ${DESTDIR}${_LIBDIR} .endif .if defined(SHLIB_NAME) ${INSTALL} ${STRIP} -o ${LIBOWN} -g ${LIBGRP} -m ${LIBMODE} \ ${_INSTALLFLAGS} ${_SHLINSTALLFLAGS} \ ${SHLIB_NAME} ${DESTDIR}${_SHLIBDIR} .if ${MK_DEBUG_FILES} != "no" .if defined(DEBUGMKDIR) ${INSTALL} -T debug -d ${DESTDIR}${DEBUGFILEDIR} .endif ${INSTALL} -T debug -o ${LIBOWN} -g ${LIBGRP} -m ${DEBUGMODE} \ ${_INSTALLFLAGS} \ ${SHLIB_NAME}.debug ${DESTDIR}${DEBUGFILEDIR} .endif .if defined(SHLIB_LINK) && !defined(PRIVATELIB) # ${_SHLIBDIRPREFIX} and ${_LDSCRIPTROOT} are both needed when cross-building # and when building 32 bits library shims. ${_SHLIBDIRPREFIX} is the directory # prefix where shared objects will be installed by the install target. # # ${_LDSCRIPTROOT} is the directory prefix that will be used when generating # ld(1) scripts. The crosstools' ld is configured to lookup libraries in an # alternative directory which is called "sysroot", so during buildworld binaries # won't be linked against the running system libraries but against the ones of # the current source tree. ${_LDSCRIPTROOT} behavior is twisted because of # the location where we store them: # - 64 bits libs are located under sysroot, so ${_LDSCRIPTROOT} must be empty # because ld(1) will manage to find them from sysroot; # - 32 bits shims are not, so ${_LDSCRIPTROOT} is used to specify their full # path, outside of sysroot. # Note that ld(1) scripts are generated both during buildworld and # installworld; in the later case ${_LDSCRIPTROOT} must be obviously empty # because on the target system, libraries are meant to be looked up from /. .if defined(SHLIB_LDSCRIPT) && !empty(SHLIB_LDSCRIPT) && exists(${.CURDIR}/${SHLIB_LDSCRIPT}) sed -e 's,@@SHLIB@@,${_LDSCRIPTROOT}${_SHLIBDIR}/${SHLIB_NAME},g' \ -e 's,@@LIBDIR@@,${_LDSCRIPTROOT}${_LIBDIR},g' \ ${.CURDIR}/${SHLIB_LDSCRIPT} > ${DESTDIR}${_LIBDIR}/${SHLIB_LINK:R}.ld ${INSTALL} -S -C -o ${LIBOWN} -g ${LIBGRP} -m ${LIBMODE} \ ${_INSTALLFLAGS} ${DESTDIR}${_LIBDIR}/${SHLIB_LINK:R}.ld \ ${DESTDIR}${_LIBDIR}/${SHLIB_LINK} rm -f ${DESTDIR}${_LIBDIR}/${SHLIB_LINK:R}.ld .else .if ${_SHLIBDIR} == ${_LIBDIR} ${INSTALL_SYMLINK} ${SHLIB_NAME} ${DESTDIR}${_LIBDIR}/${SHLIB_LINK} .else ${INSTALL_SYMLINK} ${_SHLIBDIRPREFIX}${_SHLIBDIR}/${SHLIB_NAME} \ ${DESTDIR}${_LIBDIR}/${SHLIB_LINK} .if exists(${DESTDIR}${_LIBDIR}/${SHLIB_NAME}) -chflags noschg ${DESTDIR}${_LIBDIR}/${SHLIB_NAME} rm -f ${DESTDIR}${_LIBDIR}/${SHLIB_NAME} .endif .endif .endif # SHLIB_LDSCRIPT .endif # SHLIB_LINK .endif # SHIB_NAME .if defined(INSTALL_PIC_ARCHIVE) && defined(LIB) && !empty(LIB) && ${MK_TOOLCHAIN} != "no" && !defined(PRIVATELIB) ${INSTALL} -o ${LIBOWN} -g ${LIBGRP} -m ${LIBMODE} \ ${_INSTALLFLAGS} lib${LIB}_pic.a ${DESTDIR}${_LIBDIR} .endif .if defined(WANT_LINT) && !defined(NO_LINT) && defined(LIB) && !empty(LIB) ${INSTALL} -o ${LIBOWN} -g ${LIBGRP} -m ${LIBMODE} \ ${_INSTALLFLAGS} ${LINTLIB} ${DESTDIR}${LINTLIBDIR} .endif .endif # !defined(INTERNALLIB) .if !defined(LIBRARIES_ONLY) .include .include .include .endif .include .if ${MK_MAN} != "no" && !defined(LIBRARIES_ONLY) realinstall: _maninstall .ORDER: beforeinstall _maninstall .endif .endif .if !target(lint) lint: ${SRCS:M*.c} ${LINT} ${LINTFLAGS} ${CFLAGS:M-[DIU]*} ${.ALLSRC} .endif .if ${MK_MAN} != "no" && !defined(LIBRARIES_ONLY) .include .endif .include .if !exists(${.OBJDIR}/${DEPENDFILE}) .if defined(LIB) && !empty(LIB) ${OBJS} ${STATICOBJS} ${POBJS}: ${SRCS:M*.h} .for _S in ${SRCS:N*.[hly]} ${_S:R}.po: ${_S} .endfor .endif .if defined(SHLIB_NAME) || \ defined(INSTALL_PIC_ARCHIVE) && defined(LIB) && !empty(LIB) ${SOBJS}: ${SRCS:M*.h} .for _S in ${SRCS:N*.[hly]} ${_S:R}.So: ${_S} .endfor .endif .endif .if !target(clean) clean: .if defined(CLEANFILES) && !empty(CLEANFILES) rm -f ${CLEANFILES} .endif .if defined(LIB) && !empty(LIB) rm -f a.out ${OBJS} ${OBJS:S/$/.tmp/} ${STATICOBJS} .endif .if !defined(INTERNALLIB) .if ${MK_PROFILE} != "no" && defined(LIB) && !empty(LIB) rm -f ${POBJS} ${POBJS:S/$/.tmp/} .endif .if defined(SHLIB_NAME) || \ defined(INSTALL_PIC_ARCHIVE) && defined(LIB) && !empty(LIB) rm -f ${SOBJS} ${SOBJS:.So=.so} ${SOBJS:S/$/.tmp/} .endif .if defined(SHLIB_NAME) .if defined(SHLIB_LINK) .if defined(SHLIB_LDSCRIPT) && exists(${.CURDIR}/${SHLIB_LDSCRIPT}) rm -f lib${LIB}.ld .endif rm -f ${SHLIB_LINK} .endif .endif # defined(SHLIB_NAME) .if defined(WANT_LINT) && defined(LIB) && !empty(LIB) rm -f ${LINTOBJS} .endif .endif # !defined(INTERNALLIB) .if defined(_LIBS) && !empty(_LIBS) rm -f ${_LIBS} .endif .if defined(CLEANDIRS) && !empty(CLEANDIRS) rm -rf ${CLEANDIRS} .endif .if !empty(VERSION_DEF) && !empty(SYMBOL_MAPS) rm -f ${VERSION_MAP} .endif .endif .if !empty(_LIBS) NOPATH_FILES+= ${_LIBS} .endif .include .include Index: projects/building-blocks/share/mk/bsd.prog.mk =================================================================== --- projects/building-blocks/share/mk/bsd.prog.mk (revision 278776) +++ projects/building-blocks/share/mk/bsd.prog.mk (revision 278777) @@ -1,277 +1,277 @@ # from: @(#)bsd.prog.mk 5.26 (Berkeley) 6/25/91 # $FreeBSD$ .include .include .SUFFIXES: .out .o .c .cc .cpp .cxx .C .m .y .l .ln .s .S .asm # XXX The use of COPTS in modern makefiles is discouraged. .if defined(COPTS) CFLAGS+=${COPTS} .endif .if ${MK_ASSERT_DEBUG} == "no" CFLAGS+= -DNDEBUG NO_WERROR= .endif .if defined(DEBUG_FLAGS) CFLAGS+=${DEBUG_FLAGS} CXXFLAGS+=${DEBUG_FLAGS} .if ${MK_CTF} != "no" && ${DEBUG_FLAGS:M-g} != "" CTFFLAGS+= -g .endif .endif .if defined(PROG_CXX) PROG= ${PROG_CXX} .endif .if !empty(LDFLAGS:M-Wl,*--oformat,*) || !empty(LDFLAGS:M-static) MK_DEBUG_FILES= no .endif .if defined(CRUNCH_CFLAGS) CFLAGS+=${CRUNCH_CFLAGS} .else .if ${MK_DEBUG_FILES} != "no" && empty(DEBUG_FLAGS:M-g) && \ empty(DEBUG_FLAGS:M-gdwarf-*) CFLAGS+= -g CTFFLAGS+= -g .endif .endif .if !defined(DEBUG_FLAGS) STRIP?= -s .endif .if defined(NO_SHARED) && (${NO_SHARED} != "no" && ${NO_SHARED} != "NO") LDFLAGS+= -static .endif .if defined(USEPRIVATELIB) LDFLAGS+= -L${_SHLIBDIRPREFIX}${LIBPRIVATEDIR} -rpath ${LIBPRIVATEDIR} .endif .if ${MK_DEBUG_FILES} != "no" PROG_FULL=${PROG}.full # Use ${DEBUGDIR} for base system debug files, else .debug subdirectory .if defined(BINDIR) && (\ ${BINDIR} == "/bin" ||\ ${BINDIR} == "/libexec" ||\ ${BINDIR} == "/sbin" ||\ ${BINDIR:C%/usr/(bin|bsdinstall|libexec|lpr|sendmail|sm.bin|sbin)(/.*)?%/usr/bin%} == "/usr/bin"\ ) DEBUGFILEDIR= ${DEBUGDIR}${BINDIR} .else DEBUGFILEDIR?= ${BINDIR}/.debug DEBUGMKDIR= .endif .else PROG_FULL= ${PROG} .endif .if defined(PROG) PROGNAME?= ${PROG} .if defined(SRCS) OBJS+= ${SRCS:N*.h:R:S/$/.o/g} .if target(beforelinking) beforelinking: ${OBJS} ${PROG_FULL}: beforelinking .endif ${PROG_FULL}: ${OBJS} .if defined(PROG_CXX) ${CXX} ${CXXFLAGS} ${LDFLAGS} -o ${.TARGET} ${OBJS} ${LDADD} .else ${CC} ${CFLAGS} ${LDFLAGS} -o ${.TARGET} ${OBJS} ${LDADD} .endif .if ${MK_CTF} != "no" ${CTFMERGE} ${CTFFLAGS} -o ${.TARGET} ${OBJS} .endif .else # !defined(SRCS) .if !target(${PROG}) .if defined(PROG_CXX) SRCS= ${PROG}.cc .else SRCS= ${PROG}.c .endif # Always make an intermediate object file because: # - it saves time rebuilding when only the library has changed # - the name of the object gets put into the executable symbol table instead of # the name of a variable temporary object. # - it's useful to keep objects around for crunching. OBJS+= ${PROG}.o .if target(beforelinking) beforelinking: ${OBJS} ${PROG_FULL}: beforelinking .endif ${PROG_FULL}: ${OBJS} .if defined(PROG_CXX) ${CXX} ${CXXFLAGS} ${LDFLAGS} -o ${.TARGET} ${OBJS} ${LDADD} .else ${CC} ${CFLAGS} ${LDFLAGS} -o ${.TARGET} ${OBJS} ${LDADD} .endif .if ${MK_CTF} != "no" ${CTFMERGE} ${CTFFLAGS} -o ${.TARGET} ${OBJS} .endif .endif # !target(${PROG}) .endif # !defined(SRCS) .if ${MK_DEBUG_FILES} != "no" ${PROG}: ${PROG_FULL} ${PROGNAME}.debug ${OBJCOPY} --strip-debug --add-gnu-debuglink=${PROGNAME}.debug \ ${PROG_FULL} ${.TARGET} ${PROGNAME}.debug: ${PROG_FULL} ${OBJCOPY} --only-keep-debug ${PROG_FULL} ${.TARGET} .endif .if ${MK_MAN} != "no" && !defined(MAN) && \ !defined(MAN1) && !defined(MAN2) && !defined(MAN3) && \ !defined(MAN4) && !defined(MAN5) && !defined(MAN6) && \ !defined(MAN7) && !defined(MAN8) && !defined(MAN9) MAN= ${PROG}.1 MAN1= ${MAN} .endif .endif # defined(PROG) all: beforebuild .WAIT ${PROG} ${SCRIPTS} beforebuild: objwarn .if ${MK_MAN} != "no" all: _manpages .endif .if defined(PROG) CLEANFILES+= ${PROG} .if ${MK_DEBUG_FILES} != "no" CLEANFILES+= ${PROG_FULL} ${PROGNAME}.debug .endif .endif .if defined(OBJS) CLEANFILES+= ${OBJS} .endif .include .if defined(PROG) _EXTRADEPEND: .if defined(LDFLAGS) && !empty(LDFLAGS:M-nostdlib) .if defined(DPADD) && !empty(DPADD) - echo ${PROG}: ${DPADD} >> ${DEPENDFILE} + echo ${PROG_FULL}: ${DPADD} >> ${DEPENDFILE} .endif .else - echo ${PROG}: ${LIBC} ${DPADD} >> ${DEPENDFILE} + echo ${PROG_FULL}: ${LIBC} ${DPADD} >> ${DEPENDFILE} .if defined(PROG_CXX) .if ${COMPILER_TYPE} == "clang" && empty(CXXFLAGS:M-stdlib=libstdc++) - echo ${PROG}: ${LIBCPLUSPLUS} >> ${DEPENDFILE} + echo ${PROG_FULL}: ${LIBCPLUSPLUS} >> ${DEPENDFILE} .else - echo ${PROG}: ${LIBSTDCPLUSPLUS} >> ${DEPENDFILE} + echo ${PROG_FULL}: ${LIBSTDCPLUSPLUS} >> ${DEPENDFILE} .endif .endif .endif .endif .if !target(install) .if defined(PRECIOUSPROG) .if !defined(NO_FSCHG) INSTALLFLAGS+= -fschg .endif INSTALLFLAGS+= -S .endif _INSTALLFLAGS:= ${INSTALLFLAGS} .for ie in ${INSTALLFLAGS_EDIT} _INSTALLFLAGS:= ${_INSTALLFLAGS${ie}} .endfor .if !target(realinstall) && !defined(INTERNALPROG) realinstall: _proginstall .ORDER: beforeinstall _proginstall _proginstall: .if defined(PROG) ${INSTALL} ${STRIP} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ ${_INSTALLFLAGS} ${PROG} ${DESTDIR}${BINDIR}/${PROGNAME} .if ${MK_DEBUG_FILES} != "no" .if defined(DEBUGMKDIR) ${INSTALL} -T debug -d ${DESTDIR}${DEBUGFILEDIR} .endif ${INSTALL} -T debug -o ${BINOWN} -g ${BINGRP} -m ${DEBUGMODE} \ ${PROGNAME}.debug ${DESTDIR}${DEBUGFILEDIR}/${PROGNAME}.debug .endif .endif .endif # !target(realinstall) .if defined(SCRIPTS) && !empty(SCRIPTS) realinstall: _scriptsinstall .ORDER: beforeinstall _scriptsinstall SCRIPTSDIR?= ${BINDIR} SCRIPTSOWN?= ${BINOWN} SCRIPTSGRP?= ${BINGRP} SCRIPTSMODE?= ${BINMODE} .for script in ${SCRIPTS} .if defined(SCRIPTSNAME) SCRIPTSNAME_${script:T}?= ${SCRIPTSNAME} .else SCRIPTSNAME_${script:T}?= ${script:T:R} .endif SCRIPTSDIR_${script:T}?= ${SCRIPTSDIR} SCRIPTSOWN_${script:T}?= ${SCRIPTSOWN} SCRIPTSGRP_${script:T}?= ${SCRIPTSGRP} SCRIPTSMODE_${script:T}?= ${SCRIPTSMODE} _scriptsinstall: _SCRIPTSINS_${script:T} _SCRIPTSINS_${script:T}: ${script} ${INSTALL} -o ${SCRIPTSOWN_${.ALLSRC:T}} \ -g ${SCRIPTSGRP_${.ALLSRC:T}} -m ${SCRIPTSMODE_${.ALLSRC:T}} \ ${.ALLSRC} \ ${DESTDIR}${SCRIPTSDIR_${.ALLSRC:T}}/${SCRIPTSNAME_${.ALLSRC:T}} .endfor .endif NLSNAME?= ${PROG} .include .include .include .include .if ${MK_MAN} != "no" realinstall: _maninstall .ORDER: beforeinstall _maninstall .endif .endif .if !target(lint) lint: ${SRCS:M*.c} .if defined(PROG) ${LINT} ${LINTFLAGS} ${CFLAGS:M-[DIU]*} ${.ALLSRC} .endif .endif .if ${MK_MAN} != "no" .include .endif .include .if defined(PROG) && !exists(${.OBJDIR}/${DEPENDFILE}) ${OBJS}: ${SRCS:M*.h} .endif .include .include Index: projects/building-blocks/share =================================================================== --- projects/building-blocks/share (revision 278776) +++ projects/building-blocks/share (revision 278777) Property changes on: projects/building-blocks/share ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/share:r278637-278776 Index: projects/building-blocks/sys/amd64/amd64/db_disasm.c =================================================================== --- projects/building-blocks/sys/amd64/amd64/db_disasm.c (revision 278776) +++ projects/building-blocks/sys/amd64/amd64/db_disasm.c (revision 278777) @@ -1,1709 +1,1729 @@ /*- * Mach Operating System * Copyright (c) 1991,1990 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ #include __FBSDID("$FreeBSD$"); /* * Instruction disassembler. */ #include #include #include #include #include /* * Size attributes */ #define BYTE 0 #define WORD 1 #define LONG 2 #define QUAD 3 #define SNGL 4 #define DBLR 5 #define EXTR 6 #define SDEP 7 #define ADEP 8 #define ESC 9 #define NONE 10 /* * REX prefix and bits */ #define REX_B 1 #define REX_X 2 #define REX_R 4 #define REX_W 8 #define REX 0x40 /* * Addressing modes */ #define E 1 /* general effective address */ #define Eind 2 /* indirect address (jump, call) */ #define Ew 3 /* address, word size */ #define Eb 4 /* address, byte size */ #define R 5 /* register, in 'reg' field */ #define Rw 6 /* word register, in 'reg' field */ #define Rq 39 /* quad register, in 'reg' field */ #define Rv 40 /* register in 'r/m' field */ #define Ri 7 /* register in instruction */ #define S 8 /* segment reg, in 'reg' field */ #define Si 9 /* segment reg, in instruction */ #define A 10 /* accumulator */ #define BX 11 /* (bx) */ #define CL 12 /* cl, for shifts */ #define DX 13 /* dx, for IO */ #define SI 14 /* si */ #define DI 15 /* di */ #define CR 16 /* control register */ #define DR 17 /* debug register */ #define TR 18 /* test register */ #define I 19 /* immediate, unsigned */ #define Is 20 /* immediate, signed */ #define Ib 21 /* byte immediate, unsigned */ #define Ibs 22 /* byte immediate, signed */ #define Iw 23 /* word immediate, unsigned */ #define Ilq 24 /* long/quad immediate, unsigned */ #define O 25 /* direct address */ #define Db 26 /* byte displacement from EIP */ #define Dl 27 /* long displacement from EIP */ #define o1 28 /* constant 1 */ #define o3 29 /* constant 3 */ #define OS 30 /* immediate offset/segment */ #define ST 31 /* FP stack top */ #define STI 32 /* FP stack */ #define X 33 /* extended FP op */ #define XA 34 /* for 'fstcw %ax' */ #define El 35 /* address, long/quad size */ #define Ril 36 /* long register in instruction */ #define Iba 37 /* byte immediate, don't print if 0xa */ #define EL 38 /* address, explicitly long size */ struct inst { const char * i_name; /* name */ short i_has_modrm; /* has regmodrm byte */ short i_size; /* operand size */ int i_mode; /* addressing modes */ const void * i_extra; /* pointer to extra opcode table */ }; #define op1(x) (x) #define op2(x,y) ((x)|((y)<<8)) #define op3(x,y,z) ((x)|((y)<<8)|((z)<<16)) struct finst { const char * f_name; /* name for memory instruction */ int f_size; /* size for memory instruction */ int f_rrmode; /* mode for rr instruction */ const void * f_rrname; /* name for rr instruction (or pointer to table) */ }; static const struct inst db_inst_0f388x[] = { /*80*/ { "", TRUE, SDEP, op2(E, Rq), "invept" }, /*81*/ { "", TRUE, SDEP, op2(E, Rq), "invvpid" }, /*82*/ { "", TRUE, SDEP, op2(E, Rq), "invpcid" }, /*83*/ { "", FALSE, NONE, 0, 0 }, /*84*/ { "", FALSE, NONE, 0, 0 }, /*85*/ { "", FALSE, NONE, 0, 0 }, /*86*/ { "", FALSE, NONE, 0, 0 }, /*87*/ { "", FALSE, NONE, 0, 0 }, /*88*/ { "", FALSE, NONE, 0, 0 }, /*89*/ { "", FALSE, NONE, 0, 0 }, /*8a*/ { "", FALSE, NONE, 0, 0 }, /*8b*/ { "", FALSE, NONE, 0, 0 }, /*8c*/ { "", FALSE, NONE, 0, 0 }, /*8d*/ { "", FALSE, NONE, 0, 0 }, /*8e*/ { "", FALSE, NONE, 0, 0 }, /*8f*/ { "", FALSE, NONE, 0, 0 }, }; static const struct inst * const db_inst_0f38[] = { 0, 0, 0, 0, 0, 0, 0, 0, db_inst_0f388x, 0, 0, 0, 0, 0, 0, 0 }; static const char * const db_Grp6[] = { "sldt", "str", "lldt", "ltr", "verr", "verw", "", "" }; static const char * const db_Grp7[] = { "sgdt", "sidt", "lgdt", "lidt", "smsw", "", "lmsw", "invlpg" }; static const char * const db_Grp8[] = { "", "", "", "", "bt", "bts", "btr", "btc" }; static const char * const db_Grp9[] = { "", "cmpxchg8b", "", "", "", "", "vmptrld", "vmptrst" }; static const char * const db_Grp15[] = { "fxsave", "fxrstor", "ldmxcsr", "stmxcsr", "xsave", "xrstor", "xsaveopt", "clflush" }; static const char * const db_Grp15b[] = { "", "", "", "", "", "lfence", "mfence", "sfence" }; static const struct inst db_inst_0f0x[] = { /*00*/ { "", TRUE, NONE, op1(Ew), db_Grp6 }, /*01*/ { "", TRUE, NONE, op1(Ew), db_Grp7 }, /*02*/ { "lar", TRUE, LONG, op2(E,R), 0 }, /*03*/ { "lsl", TRUE, LONG, op2(E,R), 0 }, /*04*/ { "", FALSE, NONE, 0, 0 }, /*05*/ { "syscall",FALSE,NONE, 0, 0 }, /*06*/ { "clts", FALSE, NONE, 0, 0 }, /*07*/ { "sysret",FALSE, NONE, 0, 0 }, /*08*/ { "invd", FALSE, NONE, 0, 0 }, /*09*/ { "wbinvd",FALSE, NONE, 0, 0 }, /*0a*/ { "", FALSE, NONE, 0, 0 }, /*0b*/ { "", FALSE, NONE, 0, 0 }, /*0c*/ { "", FALSE, NONE, 0, 0 }, /*0d*/ { "", FALSE, NONE, 0, 0 }, /*0e*/ { "", FALSE, NONE, 0, 0 }, /*0f*/ { "", FALSE, NONE, 0, 0 }, }; +static const struct inst db_inst_0f1x[] = { +/*10*/ { "", FALSE, NONE, 0, 0 }, +/*11*/ { "", FALSE, NONE, 0, 0 }, +/*12*/ { "", FALSE, NONE, 0, 0 }, +/*13*/ { "", FALSE, NONE, 0, 0 }, +/*14*/ { "", FALSE, NONE, 0, 0 }, +/*15*/ { "", FALSE, NONE, 0, 0 }, +/*16*/ { "", FALSE, NONE, 0, 0 }, +/*17*/ { "", FALSE, NONE, 0, 0 }, + +/*18*/ { "", FALSE, NONE, 0, 0 }, +/*19*/ { "", FALSE, NONE, 0, 0 }, +/*1a*/ { "", FALSE, NONE, 0, 0 }, +/*1b*/ { "", FALSE, NONE, 0, 0 }, +/*1c*/ { "", FALSE, NONE, 0, 0 }, +/*1d*/ { "", FALSE, NONE, 0, 0 }, +/*1e*/ { "", FALSE, NONE, 0, 0 }, +/*1f*/ { "nopl", TRUE, SDEP, 0, "nopw" }, +}; + static const struct inst db_inst_0f2x[] = { /*20*/ { "mov", TRUE, LONG, op2(CR,El), 0 }, /*21*/ { "mov", TRUE, LONG, op2(DR,El), 0 }, /*22*/ { "mov", TRUE, LONG, op2(El,CR), 0 }, /*23*/ { "mov", TRUE, LONG, op2(El,DR), 0 }, /*24*/ { "mov", TRUE, LONG, op2(TR,El), 0 }, /*25*/ { "", FALSE, NONE, 0, 0 }, /*26*/ { "mov", TRUE, LONG, op2(El,TR), 0 }, /*27*/ { "", FALSE, NONE, 0, 0 }, /*28*/ { "", FALSE, NONE, 0, 0 }, /*29*/ { "", FALSE, NONE, 0, 0 }, /*2a*/ { "", FALSE, NONE, 0, 0 }, /*2b*/ { "", FALSE, NONE, 0, 0 }, /*2c*/ { "", FALSE, NONE, 0, 0 }, /*2d*/ { "", FALSE, NONE, 0, 0 }, /*2e*/ { "", FALSE, NONE, 0, 0 }, /*2f*/ { "", FALSE, NONE, 0, 0 }, }; static const struct inst db_inst_0f3x[] = { /*30*/ { "wrmsr", FALSE, NONE, 0, 0 }, /*31*/ { "rdtsc", FALSE, NONE, 0, 0 }, /*32*/ { "rdmsr", FALSE, NONE, 0, 0 }, /*33*/ { "rdpmc", FALSE, NONE, 0, 0 }, /*34*/ { "sysenter",FALSE,NONE, 0, 0 }, /*35*/ { "sysexit",FALSE,NONE, 0, 0 }, /*36*/ { "", FALSE, NONE, 0, 0 }, /*37*/ { "getsec",FALSE, NONE, 0, 0 }, /*38*/ { "", FALSE, ESC, 0, db_inst_0f38 }, /*39*/ { "", FALSE, NONE, 0, 0 }, /*3a*/ { "", FALSE, NONE, 0, 0 }, /*3b*/ { "", FALSE, NONE, 0, 0 }, /*3c*/ { "", FALSE, NONE, 0, 0 }, /*3d*/ { "", FALSE, NONE, 0, 0 }, /*3e*/ { "", FALSE, NONE, 0, 0 }, /*3f*/ { "", FALSE, NONE, 0, 0 }, }; static const struct inst db_inst_0f4x[] = { /*40*/ { "cmovo", TRUE, NONE, op2(E, R), 0 }, /*41*/ { "cmovno", TRUE, NONE, op2(E, R), 0 }, /*42*/ { "cmovb", TRUE, NONE, op2(E, R), 0 }, /*43*/ { "cmovnb", TRUE, NONE, op2(E, R), 0 }, /*44*/ { "cmovz", TRUE, NONE, op2(E, R), 0 }, /*45*/ { "cmovnz", TRUE, NONE, op2(E, R), 0 }, /*46*/ { "cmovbe", TRUE, NONE, op2(E, R), 0 }, /*47*/ { "cmovnbe",TRUE, NONE, op2(E, R), 0 }, /*48*/ { "cmovs", TRUE, NONE, op2(E, R), 0 }, /*49*/ { "cmovns", TRUE, NONE, op2(E, R), 0 }, /*4a*/ { "cmovp", TRUE, NONE, op2(E, R), 0 }, /*4b*/ { "cmovnp", TRUE, NONE, op2(E, R), 0 }, /*4c*/ { "cmovl", TRUE, NONE, op2(E, R), 0 }, /*4d*/ { "cmovnl", TRUE, NONE, op2(E, R), 0 }, /*4e*/ { "cmovle", TRUE, NONE, op2(E, R), 0 }, /*4f*/ { "cmovnle",TRUE, NONE, op2(E, R), 0 }, }; static const struct inst db_inst_0f7x[] = { /*70*/ { "", FALSE, NONE, 0, 0 }, /*71*/ { "", FALSE, NONE, 0, 0 }, /*72*/ { "", FALSE, NONE, 0, 0 }, /*73*/ { "", FALSE, NONE, 0, 0 }, /*74*/ { "", FALSE, NONE, 0, 0 }, /*75*/ { "", FALSE, NONE, 0, 0 }, /*76*/ { "", FALSE, NONE, 0, 0 }, /*77*/ { "", FALSE, NONE, 0, 0 }, /*78*/ { "vmread", TRUE, NONE, op2(Rq, E), 0 }, /*79*/ { "vmwrite",TRUE, NONE, op2(E, Rq), 0 }, /*7a*/ { "", FALSE, NONE, 0, 0 }, /*7b*/ { "", FALSE, NONE, 0, 0 }, /*7c*/ { "", FALSE, NONE, 0, 0 }, /*7d*/ { "", FALSE, NONE, 0, 0 }, /*7e*/ { "", FALSE, NONE, 0, 0 }, /*7f*/ { "", FALSE, NONE, 0, 0 }, }; static const struct inst db_inst_0f8x[] = { /*80*/ { "jo", FALSE, NONE, op1(Dl), 0 }, /*81*/ { "jno", FALSE, NONE, op1(Dl), 0 }, /*82*/ { "jb", FALSE, NONE, op1(Dl), 0 }, /*83*/ { "jnb", FALSE, NONE, op1(Dl), 0 }, /*84*/ { "jz", FALSE, NONE, op1(Dl), 0 }, /*85*/ { "jnz", FALSE, NONE, op1(Dl), 0 }, /*86*/ { "jbe", FALSE, NONE, op1(Dl), 0 }, /*87*/ { "jnbe", FALSE, NONE, op1(Dl), 0 }, /*88*/ { "js", FALSE, NONE, op1(Dl), 0 }, /*89*/ { "jns", FALSE, NONE, op1(Dl), 0 }, /*8a*/ { "jp", FALSE, NONE, op1(Dl), 0 }, /*8b*/ { "jnp", FALSE, NONE, op1(Dl), 0 }, /*8c*/ { "jl", FALSE, NONE, op1(Dl), 0 }, /*8d*/ { "jnl", FALSE, NONE, op1(Dl), 0 }, /*8e*/ { "jle", FALSE, NONE, op1(Dl), 0 }, /*8f*/ { "jnle", FALSE, NONE, op1(Dl), 0 }, }; static const struct inst db_inst_0f9x[] = { /*90*/ { "seto", TRUE, NONE, op1(Eb), 0 }, /*91*/ { "setno", TRUE, NONE, op1(Eb), 0 }, /*92*/ { "setb", TRUE, NONE, op1(Eb), 0 }, /*93*/ { "setnb", TRUE, NONE, op1(Eb), 0 }, /*94*/ { "setz", TRUE, NONE, op1(Eb), 0 }, /*95*/ { "setnz", TRUE, NONE, op1(Eb), 0 }, /*96*/ { "setbe", TRUE, NONE, op1(Eb), 0 }, /*97*/ { "setnbe",TRUE, NONE, op1(Eb), 0 }, /*98*/ { "sets", TRUE, NONE, op1(Eb), 0 }, /*99*/ { "setns", TRUE, NONE, op1(Eb), 0 }, /*9a*/ { "setp", TRUE, NONE, op1(Eb), 0 }, /*9b*/ { "setnp", TRUE, NONE, op1(Eb), 0 }, /*9c*/ { "setl", TRUE, NONE, op1(Eb), 0 }, /*9d*/ { "setnl", TRUE, NONE, op1(Eb), 0 }, /*9e*/ { "setle", TRUE, NONE, op1(Eb), 0 }, /*9f*/ { "setnle",TRUE, NONE, op1(Eb), 0 }, }; static const struct inst db_inst_0fax[] = { /*a0*/ { "push", FALSE, NONE, op1(Si), 0 }, /*a1*/ { "pop", FALSE, NONE, op1(Si), 0 }, /*a2*/ { "cpuid", FALSE, NONE, 0, 0 }, /*a3*/ { "bt", TRUE, LONG, op2(R,E), 0 }, /*a4*/ { "shld", TRUE, LONG, op3(Ib,R,E), 0 }, /*a5*/ { "shld", TRUE, LONG, op3(CL,R,E), 0 }, /*a6*/ { "", FALSE, NONE, 0, 0 }, /*a7*/ { "", FALSE, NONE, 0, 0 }, /*a8*/ { "push", FALSE, NONE, op1(Si), 0 }, /*a9*/ { "pop", FALSE, NONE, op1(Si), 0 }, /*aa*/ { "rsm", FALSE, NONE, 0, 0 }, /*ab*/ { "bts", TRUE, LONG, op2(R,E), 0 }, /*ac*/ { "shrd", TRUE, LONG, op3(Ib,R,E), 0 }, /*ad*/ { "shrd", TRUE, LONG, op3(CL,R,E), 0 }, /*ae*/ { "", TRUE, LONG, op1(E), db_Grp15 }, /*af*/ { "imul", TRUE, LONG, op2(E,R), 0 }, }; static const struct inst db_inst_0fbx[] = { /*b0*/ { "cmpxchg",TRUE, BYTE, op2(R, E), 0 }, /*b0*/ { "cmpxchg",TRUE, LONG, op2(R, E), 0 }, /*b2*/ { "lss", TRUE, LONG, op2(E, R), 0 }, /*b3*/ { "btr", TRUE, LONG, op2(R, E), 0 }, /*b4*/ { "lfs", TRUE, LONG, op2(E, R), 0 }, /*b5*/ { "lgs", TRUE, LONG, op2(E, R), 0 }, /*b6*/ { "movzb", TRUE, LONG, op2(Eb, R), 0 }, /*b7*/ { "movzw", TRUE, LONG, op2(Ew, R), 0 }, /*b8*/ { "", FALSE, NONE, 0, 0 }, /*b9*/ { "", FALSE, NONE, 0, 0 }, /*ba*/ { "", TRUE, LONG, op2(Ib, E), db_Grp8 }, /*bb*/ { "btc", TRUE, LONG, op2(R, E), 0 }, /*bc*/ { "bsf", TRUE, LONG, op2(E, R), 0 }, /*bd*/ { "bsr", TRUE, LONG, op2(E, R), 0 }, /*be*/ { "movsb", TRUE, LONG, op2(Eb, R), 0 }, /*bf*/ { "movsw", TRUE, LONG, op2(Ew, R), 0 }, }; static const struct inst db_inst_0fcx[] = { /*c0*/ { "xadd", TRUE, BYTE, op2(R, E), 0 }, /*c1*/ { "xadd", TRUE, LONG, op2(R, E), 0 }, /*c2*/ { "", FALSE, NONE, 0, 0 }, /*c3*/ { "", FALSE, NONE, 0, 0 }, /*c4*/ { "", FALSE, NONE, 0, 0 }, /*c5*/ { "", FALSE, NONE, 0, 0 }, /*c6*/ { "", FALSE, NONE, 0, 0 }, /*c7*/ { "", TRUE, NONE, op1(E), db_Grp9 }, /*c8*/ { "bswap", FALSE, LONG, op1(Ril), 0 }, /*c9*/ { "bswap", FALSE, LONG, op1(Ril), 0 }, /*ca*/ { "bswap", FALSE, LONG, op1(Ril), 0 }, /*cb*/ { "bswap", FALSE, LONG, op1(Ril), 0 }, /*cc*/ { "bswap", FALSE, LONG, op1(Ril), 0 }, /*cd*/ { "bswap", FALSE, LONG, op1(Ril), 0 }, /*ce*/ { "bswap", FALSE, LONG, op1(Ril), 0 }, /*cf*/ { "bswap", FALSE, LONG, op1(Ril), 0 }, }; static const struct inst * const db_inst_0f[] = { db_inst_0f0x, - 0, + db_inst_0f1x, db_inst_0f2x, db_inst_0f3x, db_inst_0f4x, 0, 0, db_inst_0f7x, db_inst_0f8x, db_inst_0f9x, db_inst_0fax, db_inst_0fbx, db_inst_0fcx, 0, 0, 0 }; static const char * const db_Esc92[] = { "fnop", "", "", "", "", "", "", "" }; static const char * const db_Esc94[] = { "fchs", "fabs", "", "", "ftst", "fxam", "", "" }; static const char * const db_Esc95[] = { "fld1", "fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz","" }; static const char * const db_Esc96[] = { "f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp", "fincstp" }; static const char * const db_Esc97[] = { "fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos" }; static const char * const db_Esca5[] = { "", "fucompp","", "", "", "", "", "" }; static const char * const db_Escb4[] = { "fneni","fndisi", "fnclex","fninit","fsetpm", "", "", "" }; static const char * const db_Esce3[] = { "", "fcompp","", "", "", "", "", "" }; static const char * const db_Escf4[] = { "fnstsw","", "", "", "", "", "", "" }; static const struct finst db_Esc8[] = { /*0*/ { "fadd", SNGL, op2(STI,ST), 0 }, /*1*/ { "fmul", SNGL, op2(STI,ST), 0 }, /*2*/ { "fcom", SNGL, op2(STI,ST), 0 }, /*3*/ { "fcomp", SNGL, op2(STI,ST), 0 }, /*4*/ { "fsub", SNGL, op2(STI,ST), 0 }, /*5*/ { "fsubr", SNGL, op2(STI,ST), 0 }, /*6*/ { "fdiv", SNGL, op2(STI,ST), 0 }, /*7*/ { "fdivr", SNGL, op2(STI,ST), 0 }, }; static const struct finst db_Esc9[] = { /*0*/ { "fld", SNGL, op1(STI), 0 }, /*1*/ { "", NONE, op1(STI), "fxch" }, /*2*/ { "fst", SNGL, op1(X), db_Esc92 }, /*3*/ { "fstp", SNGL, 0, 0 }, /*4*/ { "fldenv", NONE, op1(X), db_Esc94 }, /*5*/ { "fldcw", NONE, op1(X), db_Esc95 }, /*6*/ { "fnstenv",NONE, op1(X), db_Esc96 }, /*7*/ { "fnstcw", NONE, op1(X), db_Esc97 }, }; static const struct finst db_Esca[] = { /*0*/ { "fiadd", LONG, 0, 0 }, /*1*/ { "fimul", LONG, 0, 0 }, /*2*/ { "ficom", LONG, 0, 0 }, /*3*/ { "ficomp", LONG, 0, 0 }, /*4*/ { "fisub", LONG, 0, 0 }, /*5*/ { "fisubr", LONG, op1(X), db_Esca5 }, /*6*/ { "fidiv", LONG, 0, 0 }, /*7*/ { "fidivr", LONG, 0, 0 } }; static const struct finst db_Escb[] = { /*0*/ { "fild", LONG, 0, 0 }, /*1*/ { "", NONE, 0, 0 }, /*2*/ { "fist", LONG, 0, 0 }, /*3*/ { "fistp", LONG, 0, 0 }, /*4*/ { "", WORD, op1(X), db_Escb4 }, /*5*/ { "fld", EXTR, 0, 0 }, /*6*/ { "", WORD, 0, 0 }, /*7*/ { "fstp", EXTR, 0, 0 }, }; static const struct finst db_Escc[] = { /*0*/ { "fadd", DBLR, op2(ST,STI), 0 }, /*1*/ { "fmul", DBLR, op2(ST,STI), 0 }, /*2*/ { "fcom", DBLR, 0, 0 }, /*3*/ { "fcomp", DBLR, 0, 0 }, /*4*/ { "fsub", DBLR, op2(ST,STI), "fsubr" }, /*5*/ { "fsubr", DBLR, op2(ST,STI), "fsub" }, /*6*/ { "fdiv", DBLR, op2(ST,STI), "fdivr" }, /*7*/ { "fdivr", DBLR, op2(ST,STI), "fdiv" }, }; static const struct finst db_Escd[] = { /*0*/ { "fld", DBLR, op1(STI), "ffree" }, /*1*/ { "", NONE, 0, 0 }, /*2*/ { "fst", DBLR, op1(STI), 0 }, /*3*/ { "fstp", DBLR, op1(STI), 0 }, /*4*/ { "frstor", NONE, op1(STI), "fucom" }, /*5*/ { "", NONE, op1(STI), "fucomp" }, /*6*/ { "fnsave", NONE, 0, 0 }, /*7*/ { "fnstsw", NONE, 0, 0 }, }; static const struct finst db_Esce[] = { /*0*/ { "fiadd", WORD, op2(ST,STI), "faddp" }, /*1*/ { "fimul", WORD, op2(ST,STI), "fmulp" }, /*2*/ { "ficom", WORD, 0, 0 }, /*3*/ { "ficomp", WORD, op1(X), db_Esce3 }, /*4*/ { "fisub", WORD, op2(ST,STI), "fsubrp" }, /*5*/ { "fisubr", WORD, op2(ST,STI), "fsubp" }, /*6*/ { "fidiv", WORD, op2(ST,STI), "fdivrp" }, /*7*/ { "fidivr", WORD, op2(ST,STI), "fdivp" }, }; static const struct finst db_Escf[] = { /*0*/ { "fild", WORD, 0, 0 }, /*1*/ { "", NONE, 0, 0 }, /*2*/ { "fist", WORD, 0, 0 }, /*3*/ { "fistp", WORD, 0, 0 }, /*4*/ { "fbld", NONE, op1(XA), db_Escf4 }, /*5*/ { "fild", QUAD, 0, 0 }, /*6*/ { "fbstp", NONE, 0, 0 }, /*7*/ { "fistp", QUAD, 0, 0 }, }; static const struct finst * const db_Esc_inst[] = { db_Esc8, db_Esc9, db_Esca, db_Escb, db_Escc, db_Escd, db_Esce, db_Escf }; static const char * const db_Grp1[] = { "add", "or", "adc", "sbb", "and", "sub", "xor", "cmp" }; static const char * const db_Grp2[] = { "rol", "ror", "rcl", "rcr", "shl", "shr", "shl", "sar" }; static const struct inst db_Grp3[] = { { "test", TRUE, NONE, op2(I,E), 0 }, { "test", TRUE, NONE, op2(I,E), 0 }, { "not", TRUE, NONE, op1(E), 0 }, { "neg", TRUE, NONE, op1(E), 0 }, { "mul", TRUE, NONE, op2(E,A), 0 }, { "imul", TRUE, NONE, op2(E,A), 0 }, { "div", TRUE, NONE, op2(E,A), 0 }, { "idiv", TRUE, NONE, op2(E,A), 0 }, }; static const struct inst db_Grp4[] = { { "inc", TRUE, BYTE, op1(E), 0 }, { "dec", TRUE, BYTE, op1(E), 0 }, { "", TRUE, NONE, 0, 0 }, { "", TRUE, NONE, 0, 0 }, { "", TRUE, NONE, 0, 0 }, { "", TRUE, NONE, 0, 0 }, { "", TRUE, NONE, 0, 0 }, { "", TRUE, NONE, 0, 0 } }; static const struct inst db_Grp5[] = { { "inc", TRUE, LONG, op1(E), 0 }, { "dec", TRUE, LONG, op1(E), 0 }, { "call", TRUE, LONG, op1(Eind),0 }, { "lcall", TRUE, LONG, op1(Eind),0 }, { "jmp", TRUE, LONG, op1(Eind),0 }, { "ljmp", TRUE, LONG, op1(Eind),0 }, { "push", TRUE, LONG, op1(E), 0 }, { "", TRUE, NONE, 0, 0 } }; static const struct inst db_Grp9b[] = { { "", TRUE, NONE, 0, 0 }, { "", TRUE, NONE, 0, 0 }, { "", TRUE, NONE, 0, 0 }, { "", TRUE, NONE, 0, 0 }, { "", TRUE, NONE, 0, 0 }, { "", TRUE, NONE, 0, 0 }, { "rdrand",TRUE, LONG, op1(Rv), 0 }, { "rdseed",TRUE, LONG, op1(Rv), 0 } }; static const struct inst db_inst_table[256] = { /*00*/ { "add", TRUE, BYTE, op2(R, E), 0 }, /*01*/ { "add", TRUE, LONG, op2(R, E), 0 }, /*02*/ { "add", TRUE, BYTE, op2(E, R), 0 }, /*03*/ { "add", TRUE, LONG, op2(E, R), 0 }, /*04*/ { "add", FALSE, BYTE, op2(I, A), 0 }, /*05*/ { "add", FALSE, LONG, op2(Is, A), 0 }, /*06*/ { "push", FALSE, NONE, op1(Si), 0 }, /*07*/ { "pop", FALSE, NONE, op1(Si), 0 }, /*08*/ { "or", TRUE, BYTE, op2(R, E), 0 }, /*09*/ { "or", TRUE, LONG, op2(R, E), 0 }, /*0a*/ { "or", TRUE, BYTE, op2(E, R), 0 }, /*0b*/ { "or", TRUE, LONG, op2(E, R), 0 }, /*0c*/ { "or", FALSE, BYTE, op2(I, A), 0 }, /*0d*/ { "or", FALSE, LONG, op2(I, A), 0 }, /*0e*/ { "push", FALSE, NONE, op1(Si), 0 }, /*0f*/ { "", FALSE, ESC, 0, db_inst_0f }, /*10*/ { "adc", TRUE, BYTE, op2(R, E), 0 }, /*11*/ { "adc", TRUE, LONG, op2(R, E), 0 }, /*12*/ { "adc", TRUE, BYTE, op2(E, R), 0 }, /*13*/ { "adc", TRUE, LONG, op2(E, R), 0 }, /*14*/ { "adc", FALSE, BYTE, op2(I, A), 0 }, /*15*/ { "adc", FALSE, LONG, op2(Is, A), 0 }, /*16*/ { "push", FALSE, NONE, op1(Si), 0 }, /*17*/ { "pop", FALSE, NONE, op1(Si), 0 }, /*18*/ { "sbb", TRUE, BYTE, op2(R, E), 0 }, /*19*/ { "sbb", TRUE, LONG, op2(R, E), 0 }, /*1a*/ { "sbb", TRUE, BYTE, op2(E, R), 0 }, /*1b*/ { "sbb", TRUE, LONG, op2(E, R), 0 }, /*1c*/ { "sbb", FALSE, BYTE, op2(I, A), 0 }, /*1d*/ { "sbb", FALSE, LONG, op2(Is, A), 0 }, /*1e*/ { "push", FALSE, NONE, op1(Si), 0 }, /*1f*/ { "pop", FALSE, NONE, op1(Si), 0 }, /*20*/ { "and", TRUE, BYTE, op2(R, E), 0 }, /*21*/ { "and", TRUE, LONG, op2(R, E), 0 }, /*22*/ { "and", TRUE, BYTE, op2(E, R), 0 }, /*23*/ { "and", TRUE, LONG, op2(E, R), 0 }, /*24*/ { "and", FALSE, BYTE, op2(I, A), 0 }, /*25*/ { "and", FALSE, LONG, op2(I, A), 0 }, /*26*/ { "", FALSE, NONE, 0, 0 }, /*27*/ { "daa", FALSE, NONE, 0, 0 }, /*28*/ { "sub", TRUE, BYTE, op2(R, E), 0 }, /*29*/ { "sub", TRUE, LONG, op2(R, E), 0 }, /*2a*/ { "sub", TRUE, BYTE, op2(E, R), 0 }, /*2b*/ { "sub", TRUE, LONG, op2(E, R), 0 }, /*2c*/ { "sub", FALSE, BYTE, op2(I, A), 0 }, /*2d*/ { "sub", FALSE, LONG, op2(Is, A), 0 }, /*2e*/ { "", FALSE, NONE, 0, 0 }, /*2f*/ { "das", FALSE, NONE, 0, 0 }, /*30*/ { "xor", TRUE, BYTE, op2(R, E), 0 }, /*31*/ { "xor", TRUE, LONG, op2(R, E), 0 }, /*32*/ { "xor", TRUE, BYTE, op2(E, R), 0 }, /*33*/ { "xor", TRUE, LONG, op2(E, R), 0 }, /*34*/ { "xor", FALSE, BYTE, op2(I, A), 0 }, /*35*/ { "xor", FALSE, LONG, op2(I, A), 0 }, /*36*/ { "", FALSE, NONE, 0, 0 }, /*37*/ { "aaa", FALSE, NONE, 0, 0 }, /*38*/ { "cmp", TRUE, BYTE, op2(R, E), 0 }, /*39*/ { "cmp", TRUE, LONG, op2(R, E), 0 }, /*3a*/ { "cmp", TRUE, BYTE, op2(E, R), 0 }, /*3b*/ { "cmp", TRUE, LONG, op2(E, R), 0 }, /*3c*/ { "cmp", FALSE, BYTE, op2(I, A), 0 }, /*3d*/ { "cmp", FALSE, LONG, op2(Is, A), 0 }, /*3e*/ { "", FALSE, NONE, 0, 0 }, /*3f*/ { "aas", FALSE, NONE, 0, 0 }, /*40*/ { "rex", FALSE, NONE, 0, 0 }, /*41*/ { "rex.b", FALSE, NONE, 0, 0 }, /*42*/ { "rex.x", FALSE, NONE, 0, 0 }, /*43*/ { "rex.xb", FALSE, NONE, 0, 0 }, /*44*/ { "rex.r", FALSE, NONE, 0, 0 }, /*45*/ { "rex.rb", FALSE, NONE, 0, 0 }, /*46*/ { "rex.rx", FALSE, NONE, 0, 0 }, /*47*/ { "rex.rxb", FALSE, NONE, 0, 0 }, /*48*/ { "rex.w", FALSE, NONE, 0, 0 }, /*49*/ { "rex.wb", FALSE, NONE, 0, 0 }, /*4a*/ { "rex.wx", FALSE, NONE, 0, 0 }, /*4b*/ { "rex.wxb", FALSE, NONE, 0, 0 }, /*4c*/ { "rex.wr", FALSE, NONE, 0, 0 }, /*4d*/ { "rex.wrb", FALSE, NONE, 0, 0 }, /*4e*/ { "rex.wrx", FALSE, NONE, 0, 0 }, /*4f*/ { "rex.wrxb", FALSE, NONE, 0, 0 }, /*50*/ { "push", FALSE, LONG, op1(Ri), 0 }, /*51*/ { "push", FALSE, LONG, op1(Ri), 0 }, /*52*/ { "push", FALSE, LONG, op1(Ri), 0 }, /*53*/ { "push", FALSE, LONG, op1(Ri), 0 }, /*54*/ { "push", FALSE, LONG, op1(Ri), 0 }, /*55*/ { "push", FALSE, LONG, op1(Ri), 0 }, /*56*/ { "push", FALSE, LONG, op1(Ri), 0 }, /*57*/ { "push", FALSE, LONG, op1(Ri), 0 }, /*58*/ { "pop", FALSE, LONG, op1(Ri), 0 }, /*59*/ { "pop", FALSE, LONG, op1(Ri), 0 }, /*5a*/ { "pop", FALSE, LONG, op1(Ri), 0 }, /*5b*/ { "pop", FALSE, LONG, op1(Ri), 0 }, /*5c*/ { "pop", FALSE, LONG, op1(Ri), 0 }, /*5d*/ { "pop", FALSE, LONG, op1(Ri), 0 }, /*5e*/ { "pop", FALSE, LONG, op1(Ri), 0 }, /*5f*/ { "pop", FALSE, LONG, op1(Ri), 0 }, /*60*/ { "pusha", FALSE, LONG, 0, 0 }, /*61*/ { "popa", FALSE, LONG, 0, 0 }, /*62*/ { "bound", TRUE, LONG, op2(E, R), 0 }, /*63*/ { "movslq", TRUE, NONE, op2(EL,R), 0 }, /*64*/ { "", FALSE, NONE, 0, 0 }, /*65*/ { "", FALSE, NONE, 0, 0 }, /*66*/ { "", FALSE, NONE, 0, 0 }, /*67*/ { "", FALSE, NONE, 0, 0 }, /*68*/ { "push", FALSE, LONG, op1(I), 0 }, /*69*/ { "imul", TRUE, LONG, op3(I,E,R), 0 }, /*6a*/ { "push", FALSE, LONG, op1(Ibs), 0 }, /*6b*/ { "imul", TRUE, LONG, op3(Ibs,E,R),0 }, /*6c*/ { "ins", FALSE, BYTE, op2(DX, DI), 0 }, /*6d*/ { "ins", FALSE, LONG, op2(DX, DI), 0 }, /*6e*/ { "outs", FALSE, BYTE, op2(SI, DX), 0 }, /*6f*/ { "outs", FALSE, LONG, op2(SI, DX), 0 }, /*70*/ { "jo", FALSE, NONE, op1(Db), 0 }, /*71*/ { "jno", FALSE, NONE, op1(Db), 0 }, /*72*/ { "jb", FALSE, NONE, op1(Db), 0 }, /*73*/ { "jnb", FALSE, NONE, op1(Db), 0 }, /*74*/ { "jz", FALSE, NONE, op1(Db), 0 }, /*75*/ { "jnz", FALSE, NONE, op1(Db), 0 }, /*76*/ { "jbe", FALSE, NONE, op1(Db), 0 }, /*77*/ { "jnbe", FALSE, NONE, op1(Db), 0 }, /*78*/ { "js", FALSE, NONE, op1(Db), 0 }, /*79*/ { "jns", FALSE, NONE, op1(Db), 0 }, /*7a*/ { "jp", FALSE, NONE, op1(Db), 0 }, /*7b*/ { "jnp", FALSE, NONE, op1(Db), 0 }, /*7c*/ { "jl", FALSE, NONE, op1(Db), 0 }, /*7d*/ { "jnl", FALSE, NONE, op1(Db), 0 }, /*7e*/ { "jle", FALSE, NONE, op1(Db), 0 }, /*7f*/ { "jnle", FALSE, NONE, op1(Db), 0 }, /*80*/ { "", TRUE, BYTE, op2(I, E), db_Grp1 }, /*81*/ { "", TRUE, LONG, op2(I, E), db_Grp1 }, /*82*/ { "", TRUE, BYTE, op2(I, E), db_Grp1 }, /*83*/ { "", TRUE, LONG, op2(Ibs,E), db_Grp1 }, /*84*/ { "test", TRUE, BYTE, op2(R, E), 0 }, /*85*/ { "test", TRUE, LONG, op2(R, E), 0 }, /*86*/ { "xchg", TRUE, BYTE, op2(R, E), 0 }, /*87*/ { "xchg", TRUE, LONG, op2(R, E), 0 }, /*88*/ { "mov", TRUE, BYTE, op2(R, E), 0 }, /*89*/ { "mov", TRUE, LONG, op2(R, E), 0 }, /*8a*/ { "mov", TRUE, BYTE, op2(E, R), 0 }, /*8b*/ { "mov", TRUE, LONG, op2(E, R), 0 }, /*8c*/ { "mov", TRUE, NONE, op2(S, Ew), 0 }, /*8d*/ { "lea", TRUE, LONG, op2(E, R), 0 }, /*8e*/ { "mov", TRUE, NONE, op2(Ew, S), 0 }, /*8f*/ { "pop", TRUE, LONG, op1(E), 0 }, /*90*/ { "nop", FALSE, NONE, 0, 0 }, /*91*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 }, /*92*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 }, /*93*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 }, /*94*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 }, /*95*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 }, /*96*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 }, /*97*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 }, /*98*/ { "cwde", FALSE, SDEP, 0, "cbw" }, /*99*/ { "cdq", FALSE, SDEP, 0, "cwd" }, /*9a*/ { "lcall", FALSE, NONE, op1(OS), 0 }, /*9b*/ { "wait", FALSE, NONE, 0, 0 }, /*9c*/ { "pushf", FALSE, LONG, 0, 0 }, /*9d*/ { "popf", FALSE, LONG, 0, 0 }, /*9e*/ { "sahf", FALSE, NONE, 0, 0 }, /*9f*/ { "lahf", FALSE, NONE, 0, 0 }, /*a0*/ { "mov", FALSE, BYTE, op2(O, A), 0 }, /*a1*/ { "mov", FALSE, LONG, op2(O, A), 0 }, /*a2*/ { "mov", FALSE, BYTE, op2(A, O), 0 }, /*a3*/ { "mov", FALSE, LONG, op2(A, O), 0 }, /*a4*/ { "movs", FALSE, BYTE, op2(SI,DI), 0 }, /*a5*/ { "movs", FALSE, LONG, op2(SI,DI), 0 }, /*a6*/ { "cmps", FALSE, BYTE, op2(SI,DI), 0 }, /*a7*/ { "cmps", FALSE, LONG, op2(SI,DI), 0 }, /*a8*/ { "test", FALSE, BYTE, op2(I, A), 0 }, /*a9*/ { "test", FALSE, LONG, op2(I, A), 0 }, /*aa*/ { "stos", FALSE, BYTE, op1(DI), 0 }, /*ab*/ { "stos", FALSE, LONG, op1(DI), 0 }, /*ac*/ { "lods", FALSE, BYTE, op1(SI), 0 }, /*ad*/ { "lods", FALSE, LONG, op1(SI), 0 }, /*ae*/ { "scas", FALSE, BYTE, op1(SI), 0 }, /*af*/ { "scas", FALSE, LONG, op1(SI), 0 }, /*b0*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 }, /*b1*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 }, /*b2*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 }, /*b3*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 }, /*b4*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 }, /*b5*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 }, /*b6*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 }, /*b7*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 }, /*b8*/ { "mov", FALSE, LONG, op2(Ilq, Ri), 0 }, /*b9*/ { "mov", FALSE, LONG, op2(Ilq, Ri), 0 }, /*ba*/ { "mov", FALSE, LONG, op2(Ilq, Ri), 0 }, /*bb*/ { "mov", FALSE, LONG, op2(Ilq, Ri), 0 }, /*bc*/ { "mov", FALSE, LONG, op2(Ilq, Ri), 0 }, /*bd*/ { "mov", FALSE, LONG, op2(Ilq, Ri), 0 }, /*be*/ { "mov", FALSE, LONG, op2(Ilq, Ri), 0 }, /*bf*/ { "mov", FALSE, LONG, op2(Ilq, Ri), 0 }, /*c0*/ { "", TRUE, BYTE, op2(Ib, E), db_Grp2 }, /*c1*/ { "", TRUE, LONG, op2(Ib, E), db_Grp2 }, /*c2*/ { "ret", FALSE, NONE, op1(Iw), 0 }, /*c3*/ { "ret", FALSE, NONE, 0, 0 }, /*c4*/ { "les", TRUE, LONG, op2(E, R), 0 }, /*c5*/ { "lds", TRUE, LONG, op2(E, R), 0 }, /*c6*/ { "mov", TRUE, BYTE, op2(I, E), 0 }, /*c7*/ { "mov", TRUE, LONG, op2(I, E), 0 }, /*c8*/ { "enter", FALSE, NONE, op2(Iw, Ib), 0 }, /*c9*/ { "leave", FALSE, NONE, 0, 0 }, /*ca*/ { "lret", FALSE, NONE, op1(Iw), 0 }, /*cb*/ { "lret", FALSE, NONE, 0, 0 }, /*cc*/ { "int", FALSE, NONE, op1(o3), 0 }, /*cd*/ { "int", FALSE, NONE, op1(Ib), 0 }, /*ce*/ { "into", FALSE, NONE, 0, 0 }, /*cf*/ { "iret", FALSE, NONE, 0, 0 }, /*d0*/ { "", TRUE, BYTE, op2(o1, E), db_Grp2 }, /*d1*/ { "", TRUE, LONG, op2(o1, E), db_Grp2 }, /*d2*/ { "", TRUE, BYTE, op2(CL, E), db_Grp2 }, /*d3*/ { "", TRUE, LONG, op2(CL, E), db_Grp2 }, /*d4*/ { "aam", FALSE, NONE, op1(Iba), 0 }, /*d5*/ { "aad", FALSE, NONE, op1(Iba), 0 }, /*d6*/ { ".byte\t0xd6", FALSE, NONE, 0, 0 }, /*d7*/ { "xlat", FALSE, BYTE, op1(BX), 0 }, /*d8*/ { "", TRUE, NONE, 0, db_Esc8 }, /*d9*/ { "", TRUE, NONE, 0, db_Esc9 }, /*da*/ { "", TRUE, NONE, 0, db_Esca }, /*db*/ { "", TRUE, NONE, 0, db_Escb }, /*dc*/ { "", TRUE, NONE, 0, db_Escc }, /*dd*/ { "", TRUE, NONE, 0, db_Escd }, /*de*/ { "", TRUE, NONE, 0, db_Esce }, /*df*/ { "", TRUE, NONE, 0, db_Escf }, /*e0*/ { "loopne",FALSE, NONE, op1(Db), 0 }, /*e1*/ { "loope", FALSE, NONE, op1(Db), 0 }, /*e2*/ { "loop", FALSE, NONE, op1(Db), 0 }, /*e3*/ { "jrcxz", FALSE, ADEP, op1(Db), "jecxz" }, /*e4*/ { "in", FALSE, BYTE, op2(Ib, A), 0 }, /*e5*/ { "in", FALSE, LONG, op2(Ib, A) , 0 }, /*e6*/ { "out", FALSE, BYTE, op2(A, Ib), 0 }, /*e7*/ { "out", FALSE, LONG, op2(A, Ib) , 0 }, /*e8*/ { "call", FALSE, NONE, op1(Dl), 0 }, /*e9*/ { "jmp", FALSE, NONE, op1(Dl), 0 }, /*ea*/ { "ljmp", FALSE, NONE, op1(OS), 0 }, /*eb*/ { "jmp", FALSE, NONE, op1(Db), 0 }, /*ec*/ { "in", FALSE, BYTE, op2(DX, A), 0 }, /*ed*/ { "in", FALSE, LONG, op2(DX, A) , 0 }, /*ee*/ { "out", FALSE, BYTE, op2(A, DX), 0 }, /*ef*/ { "out", FALSE, LONG, op2(A, DX) , 0 }, /*f0*/ { "", FALSE, NONE, 0, 0 }, /*f1*/ { ".byte\t0xf1", FALSE, NONE, 0, 0 }, /*f2*/ { "", FALSE, NONE, 0, 0 }, /*f3*/ { "", FALSE, NONE, 0, 0 }, /*f4*/ { "hlt", FALSE, NONE, 0, 0 }, /*f5*/ { "cmc", FALSE, NONE, 0, 0 }, /*f6*/ { "", TRUE, BYTE, 0, db_Grp3 }, /*f7*/ { "", TRUE, LONG, 0, db_Grp3 }, /*f8*/ { "clc", FALSE, NONE, 0, 0 }, /*f9*/ { "stc", FALSE, NONE, 0, 0 }, /*fa*/ { "cli", FALSE, NONE, 0, 0 }, /*fb*/ { "sti", FALSE, NONE, 0, 0 }, /*fc*/ { "cld", FALSE, NONE, 0, 0 }, /*fd*/ { "std", FALSE, NONE, 0, 0 }, /*fe*/ { "", TRUE, NONE, 0, db_Grp4 }, /*ff*/ { "", TRUE, NONE, 0, db_Grp5 }, }; static const struct inst db_bad_inst = { "???", FALSE, NONE, 0, 0 } ; #define f_mod(rex, byte) ((byte)>>6) #define f_reg(rex, byte) ((((byte)>>3)&0x7) | (rex & REX_R ? 0x8 : 0x0)) #define f_rm(rex, byte) (((byte)&0x7) | (rex & REX_B ? 0x8 : 0x0)) #define sib_ss(rex, byte) ((byte)>>6) #define sib_index(rex, byte) ((((byte)>>3)&0x7) | (rex & REX_X ? 0x8 : 0x0)) #define sib_base(rex, byte) (((byte)&0x7) | (rex & REX_B ? 0x8 : 0x0)) struct i_addr { int is_reg; /* if reg, reg number is in 'disp' */ int disp; const char * base; const char * index; int ss; }; static const char * const db_reg[2][4][16] = { {{"%al", "%cl", "%dl", "%bl", "%ah", "%ch", "%dh", "%bh", "%r8b", "%r9b", "%r10b", "%r11b", "%r12b", "%r13b", "%r14b", "%r15b" }, { "%ax", "%cx", "%dx", "%bx", "%sp", "%bp", "%si", "%di", "%r8w", "%r9w", "%r10w", "%r11w", "%r12w", "%r13w", "%r14w", "%r15w" }, { "%eax", "%ecx", "%edx", "%ebx", "%esp", "%ebp", "%esi", "%edi", "%r8d", "%r9d", "%r10d", "%r11d", "%r12d", "%r13d", "%r14d", "%r15d" }, { "%rax", "%rcx", "%rdx", "%rbx", "%rsp", "%rbp", "%rsi", "%rdi", "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15" }}, {{"%al", "%cl", "%dl", "%bl", "%spl", "%bpl", "%sil", "%dil", "%r8b", "%r9b", "%r10b", "%r11b", "%r12b", "%r13b", "%r14b", "%r15b" }, { "%ax", "%cx", "%dx", "%bx", "%sp", "%bp", "%si", "%di", "%r8w", "%r9w", "%r10w", "%r11w", "%r12w", "%r13w", "%r14w", "%r15w" }, { "%eax", "%ecx", "%edx", "%ebx", "%esp", "%ebp", "%esi", "%edi", "%r8d", "%r9d", "%r10d", "%r11d", "%r12d", "%r13d", "%r14d", "%r15d" }, { "%rax", "%rcx", "%rdx", "%rbx", "%rsp", "%rbp", "%rsi", "%rdi", "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15" }} }; static const char * const db_seg_reg[8] = { "%es", "%cs", "%ss", "%ds", "%fs", "%gs", "", "" }; /* * lengths for size attributes */ static const int db_lengths[] = { 1, /* BYTE */ 2, /* WORD */ 4, /* LONG */ 8, /* QUAD */ 4, /* SNGL */ 8, /* DBLR */ 10, /* EXTR */ }; #define get_value_inc(result, loc, size, is_signed) \ result = db_get_value((loc), (size), (is_signed)); \ (loc) += (size); static db_addr_t db_disasm_esc(db_addr_t loc, int inst, int rex, int short_addr, int size, const char *seg); static void db_print_address(const char *seg, int size, int rex, struct i_addr *addrp); static db_addr_t db_read_address(db_addr_t loc, int short_addr, int rex, int regmodrm, struct i_addr *addrp); /* * Read address at location and return updated location. */ static db_addr_t db_read_address(loc, short_addr, rex, regmodrm, addrp) db_addr_t loc; int short_addr; int rex; int regmodrm; struct i_addr * addrp; /* out */ { int mod, rm, sib, index, disp, size, have_sib; mod = f_mod(rex, regmodrm); rm = f_rm(rex, regmodrm); if (mod == 3) { addrp->is_reg = TRUE; addrp->disp = rm; return (loc); } addrp->is_reg = FALSE; addrp->index = 0; if (short_addr) size = LONG; else size = QUAD; if ((rm & 0x7) == 4) { get_value_inc(sib, loc, 1, FALSE); rm = sib_base(rex, sib); index = sib_index(rex, sib); if (index != 4) addrp->index = db_reg[1][size][index]; addrp->ss = sib_ss(rex, sib); have_sib = 1; } else have_sib = 0; switch (mod) { case 0: if (rm == 5) { get_value_inc(addrp->disp, loc, 4, FALSE); if (have_sib) addrp->base = 0; else if (short_addr) addrp->base = "%eip"; else addrp->base = "%rip"; } else { addrp->disp = 0; addrp->base = db_reg[1][size][rm]; } break; case 1: get_value_inc(disp, loc, 1, TRUE); addrp->disp = disp; addrp->base = db_reg[1][size][rm]; break; case 2: get_value_inc(disp, loc, 4, FALSE); addrp->disp = disp; addrp->base = db_reg[1][size][rm]; break; } return (loc); } static void db_print_address(seg, size, rex, addrp) const char * seg; int size; int rex; struct i_addr * addrp; { if (addrp->is_reg) { db_printf("%s", db_reg[rex != 0 ? 1 : 0][(size == LONG && (rex & REX_W)) ? QUAD : size][addrp->disp]); return; } if (seg) { db_printf("%s:", seg); } if (addrp->disp != 0 || (addrp->base == 0 && addrp->index == 0)) db_printsym((db_addr_t)addrp->disp, DB_STGY_ANY); if (addrp->base != 0 || addrp->index != 0) { db_printf("("); if (addrp->base) db_printf("%s", addrp->base); if (addrp->index) db_printf(",%s,%d", addrp->index, 1<ss); db_printf(")"); } } /* * Disassemble floating-point ("escape") instruction * and return updated location. */ static db_addr_t db_disasm_esc(loc, inst, rex, short_addr, size, seg) db_addr_t loc; int inst; int rex; int short_addr; int size; const char * seg; { int regmodrm; const struct finst * fp; int mod; struct i_addr address; const char * name; get_value_inc(regmodrm, loc, 1, FALSE); fp = &db_Esc_inst[inst - 0xd8][f_reg(rex, regmodrm)]; mod = f_mod(rex, regmodrm); if (mod != 3) { if (*fp->f_name == '\0') { db_printf(""); return (loc); } /* * Normal address modes. */ loc = db_read_address(loc, short_addr, rex, regmodrm, &address); db_printf("%s", fp->f_name); switch(fp->f_size) { case SNGL: db_printf("s"); break; case DBLR: db_printf("l"); break; case EXTR: db_printf("t"); break; case WORD: db_printf("s"); break; case LONG: db_printf("l"); break; case QUAD: db_printf("q"); break; default: break; } db_printf("\t"); db_print_address(seg, BYTE, rex, &address); } else { /* * 'reg-reg' - special formats */ switch (fp->f_rrmode) { case op2(ST,STI): name = (fp->f_rrname) ? fp->f_rrname : fp->f_name; db_printf("%s\t%%st,%%st(%d)",name,f_rm(rex, regmodrm)); break; case op2(STI,ST): name = (fp->f_rrname) ? fp->f_rrname : fp->f_name; db_printf("%s\t%%st(%d),%%st",name, f_rm(rex, regmodrm)); break; case op1(STI): name = (fp->f_rrname) ? fp->f_rrname : fp->f_name; db_printf("%s\t%%st(%d)",name, f_rm(rex, regmodrm)); break; case op1(X): name = ((const char * const *)fp->f_rrname)[f_rm(rex, regmodrm)]; if (*name == '\0') goto bad; db_printf("%s", name); break; case op1(XA): name = ((const char * const *)fp->f_rrname)[f_rm(rex, regmodrm)]; if (*name == '\0') goto bad; db_printf("%s\t%%ax", name); break; default: bad: db_printf(""); break; } } return (loc); } /* * Disassemble instruction at 'loc'. 'altfmt' specifies an * (optional) alternate format. Return address of start of * next instruction. */ db_addr_t db_disasm(loc, altfmt) db_addr_t loc; boolean_t altfmt; { int inst; int size; int short_addr; const char * seg; const struct inst * ip; const char * i_name; int i_size; int i_mode; int rex = 0; int regmodrm = 0; boolean_t first; int displ; int prefix; int rep; int imm; int imm2; long imm64; int len; struct i_addr address; get_value_inc(inst, loc, 1, FALSE); short_addr = FALSE; size = LONG; seg = 0; /* * Get prefixes */ rep = FALSE; prefix = TRUE; do { switch (inst) { case 0x66: /* data16 */ size = WORD; break; case 0x67: short_addr = TRUE; break; case 0x26: seg = "%es"; break; case 0x36: seg = "%ss"; break; case 0x2e: seg = "%cs"; break; case 0x3e: seg = "%ds"; break; case 0x64: seg = "%fs"; break; case 0x65: seg = "%gs"; break; case 0xf0: db_printf("lock "); break; case 0xf2: db_printf("repne "); break; case 0xf3: rep = TRUE; break; default: prefix = FALSE; break; } if (inst >= 0x40 && inst < 0x50) { rex = inst; prefix = TRUE; } if (prefix) { get_value_inc(inst, loc, 1, FALSE); } } while (prefix); if (inst >= 0xd8 && inst <= 0xdf) { loc = db_disasm_esc(loc, inst, rex, short_addr, size, seg); db_printf("\n"); return (loc); } ip = &db_inst_table[inst]; while (ip->i_size == ESC) { get_value_inc(inst, loc, 1, FALSE); ip = ((const struct inst * const *)ip->i_extra)[inst>>4]; if (ip == 0) { ip = &db_bad_inst; } else { ip = &ip[inst&0xf]; } } if (ip->i_has_modrm) { get_value_inc(regmodrm, loc, 1, FALSE); loc = db_read_address(loc, short_addr, rex, regmodrm, &address); } i_name = ip->i_name; i_size = ip->i_size; i_mode = ip->i_mode; if (ip->i_extra == db_Grp9 && f_mod(rex, regmodrm) == 3) { ip = &db_Grp9b[f_reg(rex, regmodrm)]; i_name = ip->i_name; i_size = ip->i_size; i_mode = ip->i_mode; } else if (ip->i_extra == db_Grp1 || ip->i_extra == db_Grp2 || ip->i_extra == db_Grp6 || ip->i_extra == db_Grp7 || ip->i_extra == db_Grp8 || ip->i_extra == db_Grp9 || ip->i_extra == db_Grp15) { i_name = ((const char * const *)ip->i_extra)[f_reg(rex, regmodrm)]; } else if (ip->i_extra == db_Grp3) { ip = ip->i_extra; ip = &ip[f_reg(rex, regmodrm)]; i_name = ip->i_name; i_mode = ip->i_mode; } else if (ip->i_extra == db_Grp4 || ip->i_extra == db_Grp5) { ip = ip->i_extra; ip = &ip[f_reg(rex, regmodrm)]; i_name = ip->i_name; i_mode = ip->i_mode; i_size = ip->i_size; } /* Special cases that don't fit well in the tables. */ if (ip->i_extra == db_Grp7 && f_mod(rex, regmodrm) == 3) { switch (regmodrm) { case 0xc1: i_name = "vmcall"; i_size = NONE; i_mode = 0; break; case 0xc2: i_name = "vmlaunch"; i_size = NONE; i_mode = 0; break; case 0xc3: i_name = "vmresume"; i_size = NONE; i_mode = 0; break; case 0xc4: i_name = "vmxoff"; i_size = NONE; i_mode = 0; break; case 0xc8: i_name = "monitor"; i_size = NONE; i_mode = 0; break; case 0xc9: i_name = "mwait"; i_size = NONE; i_mode = 0; break; case 0xca: i_name = "clac"; i_size = NONE; i_mode = 0; break; case 0xcb: i_name = "stac"; i_size = NONE; i_mode = 0; break; case 0xd0: i_name = "xgetbv"; i_size = NONE; i_mode = 0; break; case 0xd1: i_name = "xsetbv"; i_size = NONE; i_mode = 0; break; case 0xd8: i_name = "vmrun"; i_size = NONE; i_mode = 0; break; case 0xd9: i_name = "vmmcall"; i_size = NONE; i_mode = 0; break; case 0xda: i_name = "vmload"; i_size = NONE; i_mode = 0; break; case 0xdb: i_name = "vmsave"; i_size = NONE; i_mode = 0; break; case 0xdc: i_name = "stgi"; i_size = NONE; i_mode = 0; break; case 0xdd: i_name = "clgi"; i_size = NONE; i_mode = 0; break; case 0xde: i_name = "skinit"; i_size = NONE; i_mode = 0; break; case 0xdf: i_name = "invlpga"; i_size = NONE; i_mode = 0; break; case 0xf8: i_name = "swapgs"; i_size = NONE; i_mode = 0; break; case 0xf9: i_name = "rdtscp"; i_size = NONE; i_mode = 0; break; } } if (ip->i_extra == db_Grp15 && f_mod(rex, regmodrm) == 3) { i_name = db_Grp15b[f_reg(rex, regmodrm)]; i_size = NONE; i_mode = 0; } /* Handle instructions identified by mandatory prefixes. */ if (rep == TRUE) { if (inst == 0x90) { i_name = "pause"; i_size = NONE; i_mode = 0; rep = FALSE; } else if (ip->i_extra == db_Grp9 && f_mod(rex, regmodrm) != 3 && f_reg(rex, regmodrm) == 0x6) { i_name = "vmxon"; rep = FALSE; } } if (size == WORD) { if (ip->i_extra == db_Grp9 && f_mod(rex, regmodrm) != 3 && f_reg(rex, regmodrm) == 0x6) { i_name = "vmclear"; } } if (rex & REX_W) { if (strcmp(i_name, "cwde") == 0) i_name = "cdqe"; else if (strcmp(i_name, "cmpxchg8b") == 0) i_name = "cmpxchg16b"; } if (rep == TRUE) db_printf("repe "); /* XXX repe VS rep */ if (i_size == SDEP) { if (size == LONG) db_printf("%s", i_name); else db_printf("%s", (const char *)ip->i_extra); } else if (i_size == ADEP) { if (short_addr == FALSE) db_printf("%s", i_name); else db_printf("%s", (const char *)ip->i_extra); } else { db_printf("%s", i_name); if ((inst >= 0x50 && inst <= 0x5f) || inst == 0x68 || inst == 0x6a) { i_size = NONE; db_printf("q"); } if (i_size != NONE) { if (i_size == BYTE) { db_printf("b"); size = BYTE; } else if (i_size == WORD) { db_printf("w"); size = WORD; } else if (size == WORD) db_printf("w"); else { if (rex & REX_W) db_printf("q"); else db_printf("l"); } } } db_printf("\t"); for (first = TRUE; i_mode != 0; i_mode >>= 8, first = FALSE) { if (!first) db_printf(","); switch (i_mode & 0xFF) { case E: db_print_address(seg, size, rex, &address); break; case Eind: db_printf("*"); db_print_address(seg, size, rex, &address); break; case El: db_print_address(seg, (rex & REX_W) ? QUAD : LONG, rex, &address); break; case EL: db_print_address(seg, LONG, 0, &address); break; case Ew: db_print_address(seg, WORD, rex, &address); break; case Eb: db_print_address(seg, BYTE, rex, &address); break; case R: db_printf("%s", db_reg[rex != 0 ? 1 : 0][(size == LONG && (rex & REX_W)) ? QUAD : size][f_reg(rex, regmodrm)]); break; case Rw: db_printf("%s", db_reg[rex != 0 ? 1 : 0][WORD][f_reg(rex, regmodrm)]); break; case Rq: db_printf("%s", db_reg[rex != 0 ? 1 : 0][QUAD][f_reg(rex, regmodrm)]); break; case Ri: db_printf("%s", db_reg[0][QUAD][f_rm(rex, inst)]); break; case Ril: db_printf("%s", db_reg[rex != 0 ? 1 : 0][(rex & REX_R) ? QUAD : LONG][f_rm(rex, inst)]); break; case Rv: db_printf("%s", db_reg[rex != 0 ? 1 : 0][(size == LONG && (rex & REX_W)) ? QUAD : size][f_rm(rex, regmodrm)]); break; case S: db_printf("%s", db_seg_reg[f_reg(rex, regmodrm)]); break; case Si: db_printf("%s", db_seg_reg[f_reg(rex, inst)]); break; case A: db_printf("%s", db_reg[rex != 0 ? 1 : 0][size][0]); /* acc */ break; case BX: if (seg) db_printf("%s:", seg); db_printf("(%s)", short_addr ? "%bx" : "%ebx"); break; case CL: db_printf("%%cl"); break; case DX: db_printf("%%dx"); break; case SI: if (seg) db_printf("%s:", seg); db_printf("(%s)", short_addr ? "%si" : "%rsi"); break; case DI: db_printf("%%es:(%s)", short_addr ? "%di" : "%rdi"); break; case CR: db_printf("%%cr%d", f_reg(rex, regmodrm)); break; case DR: db_printf("%%dr%d", f_reg(rex, regmodrm)); break; case TR: db_printf("%%tr%d", f_reg(rex, regmodrm)); break; case I: len = db_lengths[size]; get_value_inc(imm, loc, len, FALSE); db_printf("$%#r", imm); break; case Is: len = db_lengths[(size == LONG && (rex & REX_W)) ? QUAD : size]; get_value_inc(imm, loc, len, FALSE); db_printf("$%+#r", imm); break; case Ib: get_value_inc(imm, loc, 1, FALSE); db_printf("$%#r", imm); break; case Iba: get_value_inc(imm, loc, 1, FALSE); if (imm != 0x0a) db_printf("$%#r", imm); break; case Ibs: get_value_inc(imm, loc, 1, TRUE); if (size == WORD) imm &= 0xFFFF; db_printf("$%+#r", imm); break; case Iw: get_value_inc(imm, loc, 2, FALSE); db_printf("$%#r", imm); break; case Ilq: len = db_lengths[rex & REX_W ? QUAD : LONG]; get_value_inc(imm64, loc, len, FALSE); db_printf("$%#lr", imm64); break; case O: len = (short_addr ? 2 : 4); get_value_inc(displ, loc, len, FALSE); if (seg) db_printf("%s:%+#r",seg, displ); else db_printsym((db_addr_t)displ, DB_STGY_ANY); break; case Db: get_value_inc(displ, loc, 1, TRUE); displ += loc; if (size == WORD) displ &= 0xFFFF; db_printsym((db_addr_t)displ, DB_STGY_XTRN); break; case Dl: len = db_lengths[(size == LONG && (rex & REX_W)) ? QUAD : size]; get_value_inc(displ, loc, len, FALSE); displ += loc; if (size == WORD) displ &= 0xFFFF; db_printsym((db_addr_t)displ, DB_STGY_XTRN); break; case o1: db_printf("$1"); break; case o3: db_printf("$3"); break; case OS: len = db_lengths[size]; get_value_inc(imm, loc, len, FALSE); /* offset */ get_value_inc(imm2, loc, 2, FALSE); /* segment */ db_printf("$%#r,%#r", imm2, imm); break; } } db_printf("\n"); return (loc); } Index: projects/building-blocks/sys/arm/arm/cpuinfo.c =================================================================== --- projects/building-blocks/sys/arm/arm/cpuinfo.c (revision 278776) +++ projects/building-blocks/sys/arm/arm/cpuinfo.c (revision 278777) @@ -1,121 +1,125 @@ /*- * Copyright 2014 Svatopluk Kraus * Copyright 2014 Michal Meloun * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 __FBSDID("$FreeBSD$"); #include #include #include #include struct cpuinfo cpuinfo; /* Read and parse CPU id scheme */ void cpuinfo_init(void) { cpuinfo.midr = cp15_midr_get(); /* Test old version id schemes first */ if ((cpuinfo.midr & CPU_ID_IMPLEMENTOR_MASK) == CPU_ID_ARM_LTD) { if (CPU_ID_ISOLD(cpuinfo.midr)) { /* obsolete ARMv2 or ARMv3 CPU */ cpuinfo.midr = 0; return; } if (CPU_ID_IS7(cpuinfo.midr)) { if ((cpuinfo.midr & (1 << 23)) == 0) { /* obsolete ARMv3 CPU */ cpuinfo.midr = 0; return; } /* ARMv4T CPU */ cpuinfo.architecture = 1; cpuinfo.revision = (cpuinfo.midr >> 16) & 0x7F; - } + } else { + /* ARM new id scheme */ + cpuinfo.architecture = (cpuinfo.midr >> 16) & 0x0F; + cpuinfo.revision = (cpuinfo.midr >> 20) & 0x0F; + } } else { - /* must be new id scheme */ + /* non ARM -> must be new id scheme */ cpuinfo.architecture = (cpuinfo.midr >> 16) & 0x0F; cpuinfo.revision = (cpuinfo.midr >> 20) & 0x0F; } /* Parse rest of MIDR */ cpuinfo.implementer = (cpuinfo.midr >> 24) & 0xFF; cpuinfo.part_number = (cpuinfo.midr >> 4) & 0xFFF; cpuinfo.patch = cpuinfo.midr & 0x0F; /* CP15 c0,c0 regs 0-7 exist on all CPUs (although aliased with MIDR) */ cpuinfo.ctr = cp15_ctr_get(); cpuinfo.tcmtr = cp15_tcmtr_get(); cpuinfo.tlbtr = cp15_tlbtr_get(); cpuinfo.mpidr = cp15_mpidr_get(); cpuinfo.revidr = cp15_revidr_get(); /* if CPU is not v7 cpu id scheme */ if (cpuinfo.architecture != 0xF) return; cpuinfo.id_pfr0 = cp15_id_pfr0_get(); cpuinfo.id_pfr1 = cp15_id_pfr1_get(); cpuinfo.id_dfr0 = cp15_id_dfr0_get(); cpuinfo.id_afr0 = cp15_id_afr0_get(); cpuinfo.id_mmfr0 = cp15_id_mmfr0_get(); cpuinfo.id_mmfr1 = cp15_id_mmfr1_get(); cpuinfo.id_mmfr2 = cp15_id_mmfr2_get(); cpuinfo.id_mmfr3 = cp15_id_mmfr3_get(); cpuinfo.id_isar0 = cp15_id_isar0_get(); cpuinfo.id_isar1 = cp15_id_isar1_get(); cpuinfo.id_isar2 = cp15_id_isar2_get(); cpuinfo.id_isar3 = cp15_id_isar3_get(); cpuinfo.id_isar4 = cp15_id_isar4_get(); cpuinfo.id_isar5 = cp15_id_isar5_get(); /* Not yet - CBAR only exist on ARM SMP Cortex A CPUs cpuinfo.cbar = cp15_cbar_get(); */ /* Test if revidr is implemented */ if (cpuinfo.revidr == cpuinfo.midr) cpuinfo.revidr = 0; /* parsed bits of above registers */ /* id_mmfr0 */ cpuinfo.outermost_shareability = (cpuinfo.id_mmfr0 >> 8) & 0xF; cpuinfo.shareability_levels = (cpuinfo.id_mmfr0 >> 12) & 0xF; cpuinfo.auxiliary_registers = (cpuinfo.id_mmfr0 >> 20) & 0xF; cpuinfo.innermost_shareability = (cpuinfo.id_mmfr0 >> 28) & 0xF; /* id_mmfr2 */ cpuinfo.mem_barrier = (cpuinfo.id_mmfr2 >> 20) & 0xF; /* id_mmfr3 */ cpuinfo.coherent_walk = (cpuinfo.id_mmfr3 >> 20) & 0xF; cpuinfo.maintenance_broadcast =(cpuinfo.id_mmfr3 >> 12) & 0xF; /* id_pfr1 */ cpuinfo.generic_timer_ext = (cpuinfo.id_pfr1 >> 16) & 0xF; cpuinfo.virtualization_ext = (cpuinfo.id_pfr1 >> 12) & 0xF; cpuinfo.security_ext = (cpuinfo.id_pfr1 >> 4) & 0xF; } Index: projects/building-blocks/sys/cam/ctl/ctl_backend_block.c =================================================================== --- projects/building-blocks/sys/cam/ctl/ctl_backend_block.c (revision 278776) +++ projects/building-blocks/sys/cam/ctl/ctl_backend_block.c (revision 278777) @@ -1,2932 +1,2947 @@ /*- * Copyright (c) 2003 Silicon Graphics International Corp. * Copyright (c) 2009-2011 Spectra Logic Corporation * Copyright (c) 2012 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Edward Tomasz Napierala * under sponsorship from the FreeBSD Foundation. * * 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, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * substantially similar to the "NO WARRANTY" disclaimer below * ("Disclaimer") and any redistribution must be conditioned upon * including a substantially similar Disclaimer requirement for further * binary redistribution. * * NO WARRANTY * 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 MERCHANTIBILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. * * $Id: //depot/users/kenm/FreeBSD-test2/sys/cam/ctl/ctl_backend_block.c#5 $ */ /* * CAM Target Layer driver backend for block devices. * * Author: Ken Merry */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * The idea here is that we'll allocate enough S/G space to hold a 1MB * I/O. If we get an I/O larger than that, we'll split it. */ #define CTLBLK_HALF_IO_SIZE (512 * 1024) #define CTLBLK_MAX_IO_SIZE (CTLBLK_HALF_IO_SIZE * 2) #define CTLBLK_MAX_SEG MAXPHYS #define CTLBLK_HALF_SEGS MAX(CTLBLK_HALF_IO_SIZE / CTLBLK_MAX_SEG, 1) #define CTLBLK_MAX_SEGS (CTLBLK_HALF_SEGS * 2) #ifdef CTLBLK_DEBUG #define DPRINTF(fmt, args...) \ printf("cbb(%s:%d): " fmt, __FUNCTION__, __LINE__, ##args) #else #define DPRINTF(fmt, args...) do {} while(0) #endif #define PRIV(io) \ ((struct ctl_ptr_len_flags *)&(io)->io_hdr.ctl_private[CTL_PRIV_BACKEND]) #define ARGS(io) \ ((struct ctl_lba_len_flags *)&(io)->io_hdr.ctl_private[CTL_PRIV_LBA_LEN]) SDT_PROVIDER_DEFINE(cbb); typedef enum { CTL_BE_BLOCK_LUN_UNCONFIGURED = 0x01, CTL_BE_BLOCK_LUN_CONFIG_ERR = 0x02, CTL_BE_BLOCK_LUN_WAITING = 0x04, CTL_BE_BLOCK_LUN_MULTI_THREAD = 0x08 } ctl_be_block_lun_flags; typedef enum { CTL_BE_BLOCK_NONE, CTL_BE_BLOCK_DEV, CTL_BE_BLOCK_FILE } ctl_be_block_type; struct ctl_be_block_devdata { struct cdev *cdev; struct cdevsw *csw; int dev_ref; }; struct ctl_be_block_filedata { struct ucred *cred; }; union ctl_be_block_bedata { struct ctl_be_block_devdata dev; struct ctl_be_block_filedata file; }; struct ctl_be_block_io; struct ctl_be_block_lun; typedef void (*cbb_dispatch_t)(struct ctl_be_block_lun *be_lun, struct ctl_be_block_io *beio); typedef uint64_t (*cbb_getattr_t)(struct ctl_be_block_lun *be_lun, const char *attrname); /* * Backend LUN structure. There is a 1:1 mapping between a block device * and a backend block LUN, and between a backend block LUN and a CTL LUN. */ struct ctl_be_block_lun { struct ctl_lun_create_params params; struct ctl_block_disk *disk; char lunname[32]; char *dev_path; ctl_be_block_type dev_type; struct vnode *vn; union ctl_be_block_bedata backend; cbb_dispatch_t dispatch; cbb_dispatch_t lun_flush; cbb_dispatch_t unmap; cbb_dispatch_t get_lba_status; cbb_getattr_t getattr; uma_zone_t lun_zone; uint64_t size_blocks; uint64_t size_bytes; uint32_t blocksize; int blocksize_shift; uint16_t pblockexp; uint16_t pblockoff; uint16_t ublockexp; uint16_t ublockoff; uint32_t atomicblock; uint32_t opttxferlen; struct ctl_be_block_softc *softc; struct devstat *disk_stats; ctl_be_block_lun_flags flags; STAILQ_ENTRY(ctl_be_block_lun) links; struct ctl_be_lun ctl_be_lun; struct taskqueue *io_taskqueue; struct task io_task; int num_threads; STAILQ_HEAD(, ctl_io_hdr) input_queue; STAILQ_HEAD(, ctl_io_hdr) config_read_queue; STAILQ_HEAD(, ctl_io_hdr) config_write_queue; STAILQ_HEAD(, ctl_io_hdr) datamove_queue; struct mtx_padalign io_lock; struct mtx_padalign queue_lock; }; /* * Overall softc structure for the block backend module. */ struct ctl_be_block_softc { struct mtx lock; int num_disks; STAILQ_HEAD(, ctl_block_disk) disk_list; int num_luns; STAILQ_HEAD(, ctl_be_block_lun) lun_list; }; static struct ctl_be_block_softc backend_block_softc; /* * Per-I/O information. */ struct ctl_be_block_io { union ctl_io *io; struct ctl_sg_entry sg_segs[CTLBLK_MAX_SEGS]; struct iovec xiovecs[CTLBLK_MAX_SEGS]; int bio_cmd; int num_segs; int num_bios_sent; int num_bios_done; int send_complete; int num_errors; struct bintime ds_t0; devstat_tag_type ds_tag_type; devstat_trans_flags ds_trans_type; uint64_t io_len; uint64_t io_offset; struct ctl_be_block_softc *softc; struct ctl_be_block_lun *lun; void (*beio_cont)(struct ctl_be_block_io *beio); /* to continue processing */ }; static int cbb_num_threads = 14; SYSCTL_NODE(_kern_cam_ctl, OID_AUTO, block, CTLFLAG_RD, 0, "CAM Target Layer Block Backend"); SYSCTL_INT(_kern_cam_ctl_block, OID_AUTO, num_threads, CTLFLAG_RWTUN, &cbb_num_threads, 0, "Number of threads per backing file"); static struct ctl_be_block_io *ctl_alloc_beio(struct ctl_be_block_softc *softc); static void ctl_free_beio(struct ctl_be_block_io *beio); static void ctl_complete_beio(struct ctl_be_block_io *beio); static int ctl_be_block_move_done(union ctl_io *io); static void ctl_be_block_biodone(struct bio *bio); static void ctl_be_block_flush_file(struct ctl_be_block_lun *be_lun, struct ctl_be_block_io *beio); static void ctl_be_block_dispatch_file(struct ctl_be_block_lun *be_lun, struct ctl_be_block_io *beio); static void ctl_be_block_gls_file(struct ctl_be_block_lun *be_lun, struct ctl_be_block_io *beio); static uint64_t ctl_be_block_getattr_file(struct ctl_be_block_lun *be_lun, const char *attrname); static void ctl_be_block_flush_dev(struct ctl_be_block_lun *be_lun, struct ctl_be_block_io *beio); static void ctl_be_block_unmap_dev(struct ctl_be_block_lun *be_lun, struct ctl_be_block_io *beio); static void ctl_be_block_dispatch_dev(struct ctl_be_block_lun *be_lun, struct ctl_be_block_io *beio); static uint64_t ctl_be_block_getattr_dev(struct ctl_be_block_lun *be_lun, const char *attrname); static void ctl_be_block_cr_dispatch(struct ctl_be_block_lun *be_lun, union ctl_io *io); static void ctl_be_block_cw_dispatch(struct ctl_be_block_lun *be_lun, union ctl_io *io); static void ctl_be_block_dispatch(struct ctl_be_block_lun *be_lun, union ctl_io *io); static void ctl_be_block_worker(void *context, int pending); static int ctl_be_block_submit(union ctl_io *io); static int ctl_be_block_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td); static int ctl_be_block_open_file(struct ctl_be_block_lun *be_lun, struct ctl_lun_req *req); static int ctl_be_block_open_dev(struct ctl_be_block_lun *be_lun, struct ctl_lun_req *req); static int ctl_be_block_close(struct ctl_be_block_lun *be_lun); static int ctl_be_block_open(struct ctl_be_block_softc *softc, struct ctl_be_block_lun *be_lun, struct ctl_lun_req *req); static int ctl_be_block_create(struct ctl_be_block_softc *softc, struct ctl_lun_req *req); static int ctl_be_block_rm(struct ctl_be_block_softc *softc, struct ctl_lun_req *req); static int ctl_be_block_modify_file(struct ctl_be_block_lun *be_lun, struct ctl_lun_req *req); static int ctl_be_block_modify_dev(struct ctl_be_block_lun *be_lun, struct ctl_lun_req *req); static int ctl_be_block_modify(struct ctl_be_block_softc *softc, struct ctl_lun_req *req); static void ctl_be_block_lun_shutdown(void *be_lun); static void ctl_be_block_lun_config_status(void *be_lun, ctl_lun_config_status status); static int ctl_be_block_config_write(union ctl_io *io); static int ctl_be_block_config_read(union ctl_io *io); static int ctl_be_block_lun_info(void *be_lun, struct sbuf *sb); static uint64_t ctl_be_block_lun_attr(void *be_lun, const char *attrname); int ctl_be_block_init(void); static struct ctl_backend_driver ctl_be_block_driver = { .name = "block", .flags = CTL_BE_FLAG_HAS_CONFIG, .init = ctl_be_block_init, .data_submit = ctl_be_block_submit, .data_move_done = ctl_be_block_move_done, .config_read = ctl_be_block_config_read, .config_write = ctl_be_block_config_write, .ioctl = ctl_be_block_ioctl, .lun_info = ctl_be_block_lun_info, .lun_attr = ctl_be_block_lun_attr }; MALLOC_DEFINE(M_CTLBLK, "ctlblk", "Memory used for CTL block backend"); CTL_BACKEND_DECLARE(cbb, ctl_be_block_driver); static uma_zone_t beio_zone; static struct ctl_be_block_io * ctl_alloc_beio(struct ctl_be_block_softc *softc) { struct ctl_be_block_io *beio; beio = uma_zalloc(beio_zone, M_WAITOK | M_ZERO); beio->softc = softc; return (beio); } static void ctl_free_beio(struct ctl_be_block_io *beio) { int duplicate_free; int i; duplicate_free = 0; for (i = 0; i < beio->num_segs; i++) { if (beio->sg_segs[i].addr == NULL) duplicate_free++; uma_zfree(beio->lun->lun_zone, beio->sg_segs[i].addr); beio->sg_segs[i].addr = NULL; /* For compare we had two equal S/G lists. */ if (ARGS(beio->io)->flags & CTL_LLF_COMPARE) { uma_zfree(beio->lun->lun_zone, beio->sg_segs[i + CTLBLK_HALF_SEGS].addr); beio->sg_segs[i + CTLBLK_HALF_SEGS].addr = NULL; } } if (duplicate_free > 0) { printf("%s: %d duplicate frees out of %d segments\n", __func__, duplicate_free, beio->num_segs); } uma_zfree(beio_zone, beio); } static void ctl_complete_beio(struct ctl_be_block_io *beio) { union ctl_io *io = beio->io; if (beio->beio_cont != NULL) { beio->beio_cont(beio); } else { ctl_free_beio(beio); ctl_data_submit_done(io); } } static int ctl_be_block_move_done(union ctl_io *io) { struct ctl_be_block_io *beio; struct ctl_be_block_lun *be_lun; struct ctl_lba_len_flags *lbalen; #ifdef CTL_TIME_IO struct bintime cur_bt; #endif int i; beio = (struct ctl_be_block_io *)PRIV(io)->ptr; be_lun = beio->lun; DPRINTF("entered\n"); #ifdef CTL_TIME_IO getbintime(&cur_bt); bintime_sub(&cur_bt, &io->io_hdr.dma_start_bt); bintime_add(&io->io_hdr.dma_bt, &cur_bt); io->io_hdr.num_dmas++; #endif io->scsiio.kern_rel_offset += io->scsiio.kern_data_len; /* * We set status at this point for read commands, and write * commands with errors. */ if (io->io_hdr.flags & CTL_FLAG_ABORT) { ; } else if ((io->io_hdr.port_status == 0) && ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_STATUS_NONE)) { lbalen = ARGS(beio->io); if (lbalen->flags & CTL_LLF_READ) { ctl_set_success(&io->scsiio); } else if (lbalen->flags & CTL_LLF_COMPARE) { /* We have two data blocks ready for comparison. */ for (i = 0; i < beio->num_segs; i++) { if (memcmp(beio->sg_segs[i].addr, beio->sg_segs[i + CTLBLK_HALF_SEGS].addr, beio->sg_segs[i].len) != 0) break; } if (i < beio->num_segs) ctl_set_sense(&io->scsiio, /*current_error*/ 1, /*sense_key*/ SSD_KEY_MISCOMPARE, /*asc*/ 0x1D, /*ascq*/ 0x00, SSD_ELEM_NONE); else ctl_set_success(&io->scsiio); } } else if ((io->io_hdr.port_status != 0) && ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_STATUS_NONE || (io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS)) { /* * For hardware error sense keys, the sense key * specific value is defined to be a retry count, * but we use it to pass back an internal FETD * error code. XXX KDM Hopefully the FETD is only * using 16 bits for an error code, since that's * all the space we have in the sks field. */ ctl_set_internal_failure(&io->scsiio, /*sks_valid*/ 1, /*retry_count*/ io->io_hdr.port_status); } /* * If this is a read, or a write with errors, it is done. */ if ((beio->bio_cmd == BIO_READ) || ((io->io_hdr.flags & CTL_FLAG_ABORT) != 0) || ((io->io_hdr.status & CTL_STATUS_MASK) != CTL_STATUS_NONE)) { ctl_complete_beio(beio); return (0); } /* * At this point, we have a write and the DMA completed * successfully. We now have to queue it to the task queue to * execute the backend I/O. That is because we do blocking * memory allocations, and in the file backing case, blocking I/O. * This move done routine is generally called in the SIM's * interrupt context, and therefore we cannot block. */ mtx_lock(&be_lun->queue_lock); /* * XXX KDM make sure that links is okay to use at this point. * Otherwise, we either need to add another field to ctl_io_hdr, * or deal with resource allocation here. */ STAILQ_INSERT_TAIL(&be_lun->datamove_queue, &io->io_hdr, links); mtx_unlock(&be_lun->queue_lock); taskqueue_enqueue(be_lun->io_taskqueue, &be_lun->io_task); return (0); } static void ctl_be_block_biodone(struct bio *bio) { struct ctl_be_block_io *beio; struct ctl_be_block_lun *be_lun; union ctl_io *io; int error; beio = bio->bio_caller1; be_lun = beio->lun; io = beio->io; DPRINTF("entered\n"); error = bio->bio_error; mtx_lock(&be_lun->io_lock); if (error != 0) beio->num_errors++; beio->num_bios_done++; /* * XXX KDM will this cause WITNESS to complain? Holding a lock * during the free might cause it to complain. */ g_destroy_bio(bio); /* * If the send complete bit isn't set, or we aren't the last I/O to * complete, then we're done. */ if ((beio->send_complete == 0) || (beio->num_bios_done < beio->num_bios_sent)) { mtx_unlock(&be_lun->io_lock); return; } /* * At this point, we've verified that we are the last I/O to * complete, so it's safe to drop the lock. */ devstat_end_transaction(beio->lun->disk_stats, beio->io_len, beio->ds_tag_type, beio->ds_trans_type, /*now*/ NULL, /*then*/&beio->ds_t0); mtx_unlock(&be_lun->io_lock); /* * If there are any errors from the backing device, we fail the * entire I/O with a medium error. */ if (beio->num_errors > 0) { if (error == EOPNOTSUPP) { ctl_set_invalid_opcode(&io->scsiio); } else if (error == ENOSPC) { ctl_set_space_alloc_fail(&io->scsiio); } else if (beio->bio_cmd == BIO_FLUSH) { /* XXX KDM is there is a better error here? */ ctl_set_internal_failure(&io->scsiio, /*sks_valid*/ 1, /*retry_count*/ 0xbad2); } else ctl_set_medium_error(&io->scsiio); ctl_complete_beio(beio); return; } /* * If this is a write, a flush, a delete or verify, we're all done. * If this is a read, we can now send the data to the user. */ if ((beio->bio_cmd == BIO_WRITE) || (beio->bio_cmd == BIO_FLUSH) || (beio->bio_cmd == BIO_DELETE) || (ARGS(io)->flags & CTL_LLF_VERIFY)) { ctl_set_success(&io->scsiio); ctl_complete_beio(beio); } else { if ((ARGS(io)->flags & CTL_LLF_READ) && beio->beio_cont == NULL) ctl_set_success(&io->scsiio); #ifdef CTL_TIME_IO getbintime(&io->io_hdr.dma_start_bt); #endif ctl_datamove(io); } } static void ctl_be_block_flush_file(struct ctl_be_block_lun *be_lun, struct ctl_be_block_io *beio) { union ctl_io *io = beio->io; struct mount *mountpoint; int error, lock_flags; DPRINTF("entered\n"); binuptime(&beio->ds_t0); mtx_lock(&be_lun->io_lock); devstat_start_transaction(beio->lun->disk_stats, &beio->ds_t0); mtx_unlock(&be_lun->io_lock); (void) vn_start_write(be_lun->vn, &mountpoint, V_WAIT); if (MNT_SHARED_WRITES(mountpoint) || ((mountpoint == NULL) && MNT_SHARED_WRITES(be_lun->vn->v_mount))) lock_flags = LK_SHARED; else lock_flags = LK_EXCLUSIVE; vn_lock(be_lun->vn, lock_flags | LK_RETRY); error = VOP_FSYNC(be_lun->vn, MNT_WAIT, curthread); VOP_UNLOCK(be_lun->vn, 0); vn_finished_write(mountpoint); mtx_lock(&be_lun->io_lock); devstat_end_transaction(beio->lun->disk_stats, beio->io_len, beio->ds_tag_type, beio->ds_trans_type, /*now*/ NULL, /*then*/&beio->ds_t0); mtx_unlock(&be_lun->io_lock); if (error == 0) ctl_set_success(&io->scsiio); else { /* XXX KDM is there is a better error here? */ ctl_set_internal_failure(&io->scsiio, /*sks_valid*/ 1, /*retry_count*/ 0xbad1); } ctl_complete_beio(beio); } SDT_PROBE_DEFINE1(cbb, kernel, read, file_start, "uint64_t"); SDT_PROBE_DEFINE1(cbb, kernel, write, file_start, "uint64_t"); SDT_PROBE_DEFINE1(cbb, kernel, read, file_done,"uint64_t"); SDT_PROBE_DEFINE1(cbb, kernel, write, file_done, "uint64_t"); static void ctl_be_block_dispatch_file(struct ctl_be_block_lun *be_lun, struct ctl_be_block_io *beio) { struct ctl_be_block_filedata *file_data; union ctl_io *io; struct uio xuio; struct iovec *xiovec; int flags; int error, i; DPRINTF("entered\n"); file_data = &be_lun->backend.file; io = beio->io; flags = 0; if (ARGS(io)->flags & CTL_LLF_DPO) flags |= IO_DIRECT; if (beio->bio_cmd == BIO_WRITE && ARGS(io)->flags & CTL_LLF_FUA) flags |= IO_SYNC; bzero(&xuio, sizeof(xuio)); if (beio->bio_cmd == BIO_READ) { SDT_PROBE(cbb, kernel, read, file_start, 0, 0, 0, 0, 0); xuio.uio_rw = UIO_READ; } else { SDT_PROBE(cbb, kernel, write, file_start, 0, 0, 0, 0, 0); xuio.uio_rw = UIO_WRITE; } xuio.uio_offset = beio->io_offset; xuio.uio_resid = beio->io_len; xuio.uio_segflg = UIO_SYSSPACE; xuio.uio_iov = beio->xiovecs; xuio.uio_iovcnt = beio->num_segs; xuio.uio_td = curthread; for (i = 0, xiovec = xuio.uio_iov; i < xuio.uio_iovcnt; i++, xiovec++) { xiovec->iov_base = beio->sg_segs[i].addr; xiovec->iov_len = beio->sg_segs[i].len; } binuptime(&beio->ds_t0); mtx_lock(&be_lun->io_lock); devstat_start_transaction(beio->lun->disk_stats, &beio->ds_t0); mtx_unlock(&be_lun->io_lock); if (beio->bio_cmd == BIO_READ) { vn_lock(be_lun->vn, LK_SHARED | LK_RETRY); /* * UFS pays attention to IO_DIRECT for reads. If the * DIRECTIO option is configured into the kernel, it calls * ffs_rawread(). But that only works for single-segment * uios with user space addresses. In our case, with a * kernel uio, it still reads into the buffer cache, but it * will just try to release the buffer from the cache later * on in ffs_read(). * * ZFS does not pay attention to IO_DIRECT for reads. * * UFS does not pay attention to IO_SYNC for reads. * * ZFS pays attention to IO_SYNC (which translates into the * Solaris define FRSYNC for zfs_read()) for reads. It * attempts to sync the file before reading. * * So, to attempt to provide some barrier semantics in the * BIO_ORDERED case, set both IO_DIRECT and IO_SYNC. */ error = VOP_READ(be_lun->vn, &xuio, flags, file_data->cred); VOP_UNLOCK(be_lun->vn, 0); SDT_PROBE(cbb, kernel, read, file_done, 0, 0, 0, 0, 0); } else { struct mount *mountpoint; int lock_flags; (void)vn_start_write(be_lun->vn, &mountpoint, V_WAIT); if (MNT_SHARED_WRITES(mountpoint) || ((mountpoint == NULL) && MNT_SHARED_WRITES(be_lun->vn->v_mount))) lock_flags = LK_SHARED; else lock_flags = LK_EXCLUSIVE; vn_lock(be_lun->vn, lock_flags | LK_RETRY); /* * UFS pays attention to IO_DIRECT for writes. The write * is done asynchronously. (Normally the write would just * get put into cache. * * UFS pays attention to IO_SYNC for writes. It will * attempt to write the buffer out synchronously if that * flag is set. * * ZFS does not pay attention to IO_DIRECT for writes. * * ZFS pays attention to IO_SYNC (a.k.a. FSYNC or FRSYNC) * for writes. It will flush the transaction from the * cache before returning. * * So if we've got the BIO_ORDERED flag set, we want * IO_SYNC in either the UFS or ZFS case. */ error = VOP_WRITE(be_lun->vn, &xuio, flags, file_data->cred); VOP_UNLOCK(be_lun->vn, 0); vn_finished_write(mountpoint); SDT_PROBE(cbb, kernel, write, file_done, 0, 0, 0, 0, 0); } mtx_lock(&be_lun->io_lock); devstat_end_transaction(beio->lun->disk_stats, beio->io_len, beio->ds_tag_type, beio->ds_trans_type, /*now*/ NULL, /*then*/&beio->ds_t0); mtx_unlock(&be_lun->io_lock); /* * If we got an error, set the sense data to "MEDIUM ERROR" and * return the I/O to the user. */ if (error != 0) { char path_str[32]; ctl_scsi_path_string(io, path_str, sizeof(path_str)); printf("%s%s command returned errno %d\n", path_str, (beio->bio_cmd == BIO_READ) ? "READ" : "WRITE", error); if (error == ENOSPC) { ctl_set_space_alloc_fail(&io->scsiio); } else ctl_set_medium_error(&io->scsiio); ctl_complete_beio(beio); return; } /* * If this is a write or a verify, we're all done. * If this is a read, we can now send the data to the user. */ if ((beio->bio_cmd == BIO_WRITE) || (ARGS(io)->flags & CTL_LLF_VERIFY)) { ctl_set_success(&io->scsiio); ctl_complete_beio(beio); } else { if ((ARGS(io)->flags & CTL_LLF_READ) && beio->beio_cont == NULL) ctl_set_success(&io->scsiio); #ifdef CTL_TIME_IO getbintime(&io->io_hdr.dma_start_bt); #endif ctl_datamove(io); } } static void ctl_be_block_gls_file(struct ctl_be_block_lun *be_lun, struct ctl_be_block_io *beio) { union ctl_io *io = beio->io; struct ctl_lba_len_flags *lbalen = ARGS(io); struct scsi_get_lba_status_data *data; off_t roff, off; int error, status; DPRINTF("entered\n"); off = roff = ((off_t)lbalen->lba) << be_lun->blocksize_shift; vn_lock(be_lun->vn, LK_SHARED | LK_RETRY); error = VOP_IOCTL(be_lun->vn, FIOSEEKHOLE, &off, 0, curthread->td_ucred, curthread); if (error == 0 && off > roff) status = 0; /* mapped up to off */ else { error = VOP_IOCTL(be_lun->vn, FIOSEEKDATA, &off, 0, curthread->td_ucred, curthread); if (error == 0 && off > roff) status = 1; /* deallocated up to off */ else { status = 0; /* unknown up to the end */ off = be_lun->size_bytes; } } VOP_UNLOCK(be_lun->vn, 0); off >>= be_lun->blocksize_shift; data = (struct scsi_get_lba_status_data *)io->scsiio.kern_data_ptr; scsi_u64to8b(lbalen->lba, data->descr[0].addr); scsi_ulto4b(MIN(UINT32_MAX, off - lbalen->lba), data->descr[0].length); data->descr[0].status = status; ctl_complete_beio(beio); } static uint64_t ctl_be_block_getattr_file(struct ctl_be_block_lun *be_lun, const char *attrname) { struct vattr vattr; struct statfs statfs; int error; if (be_lun->vn == NULL) return (UINT64_MAX); if (strcmp(attrname, "blocksused") == 0) { error = VOP_GETATTR(be_lun->vn, &vattr, curthread->td_ucred); if (error != 0) return (UINT64_MAX); return (vattr.va_bytes >> be_lun->blocksize_shift); } if (strcmp(attrname, "blocksavail") == 0) { error = VFS_STATFS(be_lun->vn->v_mount, &statfs); if (error != 0) return (UINT64_MAX); return ((statfs.f_bavail * statfs.f_bsize) >> be_lun->blocksize_shift); } return (UINT64_MAX); } static void ctl_be_block_dispatch_zvol(struct ctl_be_block_lun *be_lun, struct ctl_be_block_io *beio) { struct ctl_be_block_devdata *dev_data; union ctl_io *io; struct uio xuio; struct iovec *xiovec; int flags; int error, i; DPRINTF("entered\n"); dev_data = &be_lun->backend.dev; io = beio->io; flags = 0; if (ARGS(io)->flags & CTL_LLF_DPO) flags |= IO_DIRECT; if (beio->bio_cmd == BIO_WRITE && ARGS(io)->flags & CTL_LLF_FUA) flags |= IO_SYNC; bzero(&xuio, sizeof(xuio)); if (beio->bio_cmd == BIO_READ) { SDT_PROBE(cbb, kernel, read, file_start, 0, 0, 0, 0, 0); xuio.uio_rw = UIO_READ; } else { SDT_PROBE(cbb, kernel, write, file_start, 0, 0, 0, 0, 0); xuio.uio_rw = UIO_WRITE; } xuio.uio_offset = beio->io_offset; xuio.uio_resid = beio->io_len; xuio.uio_segflg = UIO_SYSSPACE; xuio.uio_iov = beio->xiovecs; xuio.uio_iovcnt = beio->num_segs; xuio.uio_td = curthread; for (i = 0, xiovec = xuio.uio_iov; i < xuio.uio_iovcnt; i++, xiovec++) { xiovec->iov_base = beio->sg_segs[i].addr; xiovec->iov_len = beio->sg_segs[i].len; } binuptime(&beio->ds_t0); mtx_lock(&be_lun->io_lock); devstat_start_transaction(beio->lun->disk_stats, &beio->ds_t0); mtx_unlock(&be_lun->io_lock); if (beio->bio_cmd == BIO_READ) { error = (*dev_data->csw->d_read)(dev_data->cdev, &xuio, flags); SDT_PROBE(cbb, kernel, read, file_done, 0, 0, 0, 0, 0); } else { error = (*dev_data->csw->d_write)(dev_data->cdev, &xuio, flags); SDT_PROBE(cbb, kernel, write, file_done, 0, 0, 0, 0, 0); } mtx_lock(&be_lun->io_lock); devstat_end_transaction(beio->lun->disk_stats, beio->io_len, beio->ds_tag_type, beio->ds_trans_type, /*now*/ NULL, /*then*/&beio->ds_t0); mtx_unlock(&be_lun->io_lock); /* * If we got an error, set the sense data to "MEDIUM ERROR" and * return the I/O to the user. */ if (error != 0) { if (error == ENOSPC) { ctl_set_space_alloc_fail(&io->scsiio); } else ctl_set_medium_error(&io->scsiio); ctl_complete_beio(beio); return; } /* * If this is a write or a verify, we're all done. * If this is a read, we can now send the data to the user. */ if ((beio->bio_cmd == BIO_WRITE) || (ARGS(io)->flags & CTL_LLF_VERIFY)) { ctl_set_success(&io->scsiio); ctl_complete_beio(beio); } else { if ((ARGS(io)->flags & CTL_LLF_READ) && beio->beio_cont == NULL) ctl_set_success(&io->scsiio); #ifdef CTL_TIME_IO getbintime(&io->io_hdr.dma_start_bt); #endif ctl_datamove(io); } } static void ctl_be_block_gls_zvol(struct ctl_be_block_lun *be_lun, struct ctl_be_block_io *beio) { struct ctl_be_block_devdata *dev_data = &be_lun->backend.dev; union ctl_io *io = beio->io; struct ctl_lba_len_flags *lbalen = ARGS(io); struct scsi_get_lba_status_data *data; off_t roff, off; int error, status; DPRINTF("entered\n"); off = roff = ((off_t)lbalen->lba) << be_lun->blocksize_shift; error = (*dev_data->csw->d_ioctl)(dev_data->cdev, FIOSEEKHOLE, (caddr_t)&off, FREAD, curthread); if (error == 0 && off > roff) status = 0; /* mapped up to off */ else { error = (*dev_data->csw->d_ioctl)(dev_data->cdev, FIOSEEKDATA, (caddr_t)&off, FREAD, curthread); if (error == 0 && off > roff) status = 1; /* deallocated up to off */ else { status = 0; /* unknown up to the end */ off = be_lun->size_bytes; } } off >>= be_lun->blocksize_shift; data = (struct scsi_get_lba_status_data *)io->scsiio.kern_data_ptr; scsi_u64to8b(lbalen->lba, data->descr[0].addr); scsi_ulto4b(MIN(UINT32_MAX, off - lbalen->lba), data->descr[0].length); data->descr[0].status = status; ctl_complete_beio(beio); } static void ctl_be_block_flush_dev(struct ctl_be_block_lun *be_lun, struct ctl_be_block_io *beio) { struct bio *bio; union ctl_io *io; struct ctl_be_block_devdata *dev_data; dev_data = &be_lun->backend.dev; io = beio->io; DPRINTF("entered\n"); /* This can't fail, it's a blocking allocation. */ bio = g_alloc_bio(); bio->bio_cmd = BIO_FLUSH; bio->bio_flags |= BIO_ORDERED; bio->bio_dev = dev_data->cdev; bio->bio_offset = 0; bio->bio_data = 0; bio->bio_done = ctl_be_block_biodone; bio->bio_caller1 = beio; bio->bio_pblkno = 0; /* * We don't need to acquire the LUN lock here, because we are only * sending one bio, and so there is no other context to synchronize * with. */ beio->num_bios_sent = 1; beio->send_complete = 1; binuptime(&beio->ds_t0); mtx_lock(&be_lun->io_lock); devstat_start_transaction(be_lun->disk_stats, &beio->ds_t0); mtx_unlock(&be_lun->io_lock); (*dev_data->csw->d_strategy)(bio); } static void ctl_be_block_unmap_dev_range(struct ctl_be_block_lun *be_lun, struct ctl_be_block_io *beio, uint64_t off, uint64_t len, int last) { struct bio *bio; struct ctl_be_block_devdata *dev_data; uint64_t maxlen; dev_data = &be_lun->backend.dev; maxlen = LONG_MAX - (LONG_MAX % be_lun->blocksize); while (len > 0) { bio = g_alloc_bio(); bio->bio_cmd = BIO_DELETE; bio->bio_dev = dev_data->cdev; bio->bio_offset = off; bio->bio_length = MIN(len, maxlen); bio->bio_data = 0; bio->bio_done = ctl_be_block_biodone; bio->bio_caller1 = beio; bio->bio_pblkno = off / be_lun->blocksize; off += bio->bio_length; len -= bio->bio_length; mtx_lock(&be_lun->io_lock); beio->num_bios_sent++; if (last && len == 0) beio->send_complete = 1; mtx_unlock(&be_lun->io_lock); (*dev_data->csw->d_strategy)(bio); } } static void ctl_be_block_unmap_dev(struct ctl_be_block_lun *be_lun, struct ctl_be_block_io *beio) { union ctl_io *io; struct ctl_be_block_devdata *dev_data; struct ctl_ptr_len_flags *ptrlen; struct scsi_unmap_desc *buf, *end; uint64_t len; dev_data = &be_lun->backend.dev; io = beio->io; DPRINTF("entered\n"); binuptime(&beio->ds_t0); mtx_lock(&be_lun->io_lock); devstat_start_transaction(be_lun->disk_stats, &beio->ds_t0); mtx_unlock(&be_lun->io_lock); if (beio->io_offset == -1) { beio->io_len = 0; ptrlen = (struct ctl_ptr_len_flags *)&io->io_hdr.ctl_private[CTL_PRIV_LBA_LEN]; buf = (struct scsi_unmap_desc *)ptrlen->ptr; end = buf + ptrlen->len / sizeof(*buf); for (; buf < end; buf++) { len = (uint64_t)scsi_4btoul(buf->length) * be_lun->blocksize; beio->io_len += len; ctl_be_block_unmap_dev_range(be_lun, beio, scsi_8btou64(buf->lba) * be_lun->blocksize, len, (end - buf < 2) ? TRUE : FALSE); } } else ctl_be_block_unmap_dev_range(be_lun, beio, beio->io_offset, beio->io_len, TRUE); } static void ctl_be_block_dispatch_dev(struct ctl_be_block_lun *be_lun, struct ctl_be_block_io *beio) { TAILQ_HEAD(, bio) queue = TAILQ_HEAD_INITIALIZER(queue); int i; struct bio *bio; struct ctl_be_block_devdata *dev_data; off_t cur_offset; int max_iosize; DPRINTF("entered\n"); dev_data = &be_lun->backend.dev; /* * We have to limit our I/O size to the maximum supported by the * backend device. Hopefully it is MAXPHYS. If the driver doesn't * set it properly, use DFLTPHYS. */ max_iosize = dev_data->cdev->si_iosize_max; if (max_iosize < PAGE_SIZE) max_iosize = DFLTPHYS; cur_offset = beio->io_offset; for (i = 0; i < beio->num_segs; i++) { size_t cur_size; uint8_t *cur_ptr; cur_size = beio->sg_segs[i].len; cur_ptr = beio->sg_segs[i].addr; while (cur_size > 0) { /* This can't fail, it's a blocking allocation. */ bio = g_alloc_bio(); KASSERT(bio != NULL, ("g_alloc_bio() failed!\n")); bio->bio_cmd = beio->bio_cmd; bio->bio_dev = dev_data->cdev; bio->bio_caller1 = beio; bio->bio_length = min(cur_size, max_iosize); bio->bio_offset = cur_offset; bio->bio_data = cur_ptr; bio->bio_done = ctl_be_block_biodone; bio->bio_pblkno = cur_offset / be_lun->blocksize; cur_offset += bio->bio_length; cur_ptr += bio->bio_length; cur_size -= bio->bio_length; TAILQ_INSERT_TAIL(&queue, bio, bio_queue); beio->num_bios_sent++; } } binuptime(&beio->ds_t0); mtx_lock(&be_lun->io_lock); devstat_start_transaction(be_lun->disk_stats, &beio->ds_t0); beio->send_complete = 1; mtx_unlock(&be_lun->io_lock); /* * Fire off all allocated requests! */ while ((bio = TAILQ_FIRST(&queue)) != NULL) { TAILQ_REMOVE(&queue, bio, bio_queue); (*dev_data->csw->d_strategy)(bio); } } static uint64_t ctl_be_block_getattr_dev(struct ctl_be_block_lun *be_lun, const char *attrname) { struct ctl_be_block_devdata *dev_data = &be_lun->backend.dev; struct diocgattr_arg arg; int error; if (dev_data->csw == NULL || dev_data->csw->d_ioctl == NULL) return (UINT64_MAX); strlcpy(arg.name, attrname, sizeof(arg.name)); arg.len = sizeof(arg.value.off); error = dev_data->csw->d_ioctl(dev_data->cdev, DIOCGATTR, (caddr_t)&arg, FREAD, curthread); if (error != 0) return (UINT64_MAX); return (arg.value.off); } static void ctl_be_block_cw_done_ws(struct ctl_be_block_io *beio) { union ctl_io *io; io = beio->io; ctl_free_beio(beio); if ((io->io_hdr.flags & CTL_FLAG_ABORT) || ((io->io_hdr.status & CTL_STATUS_MASK) != CTL_STATUS_NONE && (io->io_hdr.status & CTL_STATUS_MASK) != CTL_SUCCESS)) { ctl_config_write_done(io); return; } ctl_be_block_config_write(io); } static void ctl_be_block_cw_dispatch_ws(struct ctl_be_block_lun *be_lun, union ctl_io *io) { struct ctl_be_block_io *beio; struct ctl_be_block_softc *softc; struct ctl_lba_len_flags *lbalen; uint64_t len_left, lba; uint32_t pb, pbo, adj; int i, seglen; uint8_t *buf, *end; DPRINTF("entered\n"); beio = (struct ctl_be_block_io *)PRIV(io)->ptr; softc = be_lun->softc; lbalen = ARGS(beio->io); if (lbalen->flags & ~(SWS_LBDATA | SWS_UNMAP | SWS_ANCHOR | SWS_NDOB) || (lbalen->flags & (SWS_UNMAP | SWS_ANCHOR) && be_lun->unmap == NULL)) { ctl_free_beio(beio); ctl_set_invalid_field(&io->scsiio, /*sks_valid*/ 1, /*command*/ 1, /*field*/ 1, /*bit_valid*/ 0, /*bit*/ 0); ctl_config_write_done(io); return; } switch (io->scsiio.tag_type) { case CTL_TAG_ORDERED: beio->ds_tag_type = DEVSTAT_TAG_ORDERED; break; case CTL_TAG_HEAD_OF_QUEUE: beio->ds_tag_type = DEVSTAT_TAG_HEAD; break; case CTL_TAG_UNTAGGED: case CTL_TAG_SIMPLE: case CTL_TAG_ACA: default: beio->ds_tag_type = DEVSTAT_TAG_SIMPLE; break; } if (lbalen->flags & (SWS_UNMAP | SWS_ANCHOR)) { beio->io_offset = lbalen->lba * be_lun->blocksize; beio->io_len = (uint64_t)lbalen->len * be_lun->blocksize; beio->bio_cmd = BIO_DELETE; beio->ds_trans_type = DEVSTAT_FREE; be_lun->unmap(be_lun, beio); return; } beio->bio_cmd = BIO_WRITE; beio->ds_trans_type = DEVSTAT_WRITE; DPRINTF("WRITE SAME at LBA %jx len %u\n", (uintmax_t)lbalen->lba, lbalen->len); pb = be_lun->blocksize << be_lun->pblockexp; if (be_lun->pblockoff > 0) pbo = pb - be_lun->blocksize * be_lun->pblockoff; else pbo = 0; len_left = (uint64_t)lbalen->len * be_lun->blocksize; for (i = 0, lba = 0; i < CTLBLK_MAX_SEGS && len_left > 0; i++) { /* * Setup the S/G entry for this chunk. */ seglen = MIN(CTLBLK_MAX_SEG, len_left); if (pb > be_lun->blocksize) { adj = ((lbalen->lba + lba) * be_lun->blocksize + seglen - pbo) % pb; if (seglen > adj) seglen -= adj; else seglen -= seglen % be_lun->blocksize; } else seglen -= seglen % be_lun->blocksize; beio->sg_segs[i].len = seglen; beio->sg_segs[i].addr = uma_zalloc(be_lun->lun_zone, M_WAITOK); DPRINTF("segment %d addr %p len %zd\n", i, beio->sg_segs[i].addr, beio->sg_segs[i].len); beio->num_segs++; len_left -= seglen; buf = beio->sg_segs[i].addr; end = buf + seglen; for (; buf < end; buf += be_lun->blocksize) { memcpy(buf, io->scsiio.kern_data_ptr, be_lun->blocksize); if (lbalen->flags & SWS_LBDATA) scsi_ulto4b(lbalen->lba + lba, buf); lba++; } } beio->io_offset = lbalen->lba * be_lun->blocksize; beio->io_len = lba * be_lun->blocksize; /* We can not do all in one run. Correct and schedule rerun. */ if (len_left > 0) { lbalen->lba += lba; lbalen->len -= lba; beio->beio_cont = ctl_be_block_cw_done_ws; } be_lun->dispatch(be_lun, beio); } static void ctl_be_block_cw_dispatch_unmap(struct ctl_be_block_lun *be_lun, union ctl_io *io) { struct ctl_be_block_io *beio; struct ctl_be_block_softc *softc; struct ctl_ptr_len_flags *ptrlen; DPRINTF("entered\n"); beio = (struct ctl_be_block_io *)PRIV(io)->ptr; softc = be_lun->softc; ptrlen = (struct ctl_ptr_len_flags *)&io->io_hdr.ctl_private[CTL_PRIV_LBA_LEN]; if ((ptrlen->flags & ~SU_ANCHOR) != 0 || be_lun->unmap == NULL) { ctl_free_beio(beio); ctl_set_invalid_field(&io->scsiio, /*sks_valid*/ 0, /*command*/ 1, /*field*/ 0, /*bit_valid*/ 0, /*bit*/ 0); ctl_config_write_done(io); return; } switch (io->scsiio.tag_type) { case CTL_TAG_ORDERED: beio->ds_tag_type = DEVSTAT_TAG_ORDERED; break; case CTL_TAG_HEAD_OF_QUEUE: beio->ds_tag_type = DEVSTAT_TAG_HEAD; break; case CTL_TAG_UNTAGGED: case CTL_TAG_SIMPLE: case CTL_TAG_ACA: default: beio->ds_tag_type = DEVSTAT_TAG_SIMPLE; break; } beio->io_len = 0; beio->io_offset = -1; beio->bio_cmd = BIO_DELETE; beio->ds_trans_type = DEVSTAT_FREE; DPRINTF("UNMAP\n"); be_lun->unmap(be_lun, beio); } static void ctl_be_block_cr_done(struct ctl_be_block_io *beio) { union ctl_io *io; io = beio->io; ctl_free_beio(beio); ctl_config_read_done(io); } static void ctl_be_block_cr_dispatch(struct ctl_be_block_lun *be_lun, union ctl_io *io) { struct ctl_be_block_io *beio; struct ctl_be_block_softc *softc; DPRINTF("entered\n"); softc = be_lun->softc; beio = ctl_alloc_beio(softc); beio->io = io; beio->lun = be_lun; beio->beio_cont = ctl_be_block_cr_done; PRIV(io)->ptr = (void *)beio; switch (io->scsiio.cdb[0]) { case SERVICE_ACTION_IN: /* GET LBA STATUS */ beio->bio_cmd = -1; beio->ds_trans_type = DEVSTAT_NO_DATA; beio->ds_tag_type = DEVSTAT_TAG_ORDERED; beio->io_len = 0; if (be_lun->get_lba_status) be_lun->get_lba_status(be_lun, beio); else ctl_be_block_cr_done(beio); break; default: panic("Unhandled CDB type %#x", io->scsiio.cdb[0]); break; } } static void ctl_be_block_cw_done(struct ctl_be_block_io *beio) { union ctl_io *io; io = beio->io; ctl_free_beio(beio); ctl_config_write_done(io); } static void ctl_be_block_cw_dispatch(struct ctl_be_block_lun *be_lun, union ctl_io *io) { struct ctl_be_block_io *beio; struct ctl_be_block_softc *softc; DPRINTF("entered\n"); softc = be_lun->softc; beio = ctl_alloc_beio(softc); beio->io = io; beio->lun = be_lun; beio->beio_cont = ctl_be_block_cw_done; PRIV(io)->ptr = (void *)beio; switch (io->scsiio.cdb[0]) { case SYNCHRONIZE_CACHE: case SYNCHRONIZE_CACHE_16: beio->bio_cmd = BIO_FLUSH; beio->ds_trans_type = DEVSTAT_NO_DATA; beio->ds_tag_type = DEVSTAT_TAG_ORDERED; beio->io_len = 0; be_lun->lun_flush(be_lun, beio); break; case WRITE_SAME_10: case WRITE_SAME_16: ctl_be_block_cw_dispatch_ws(be_lun, io); break; case UNMAP: ctl_be_block_cw_dispatch_unmap(be_lun, io); break; default: panic("Unhandled CDB type %#x", io->scsiio.cdb[0]); break; } } SDT_PROBE_DEFINE1(cbb, kernel, read, start, "uint64_t"); SDT_PROBE_DEFINE1(cbb, kernel, write, start, "uint64_t"); SDT_PROBE_DEFINE1(cbb, kernel, read, alloc_done, "uint64_t"); SDT_PROBE_DEFINE1(cbb, kernel, write, alloc_done, "uint64_t"); static void ctl_be_block_next(struct ctl_be_block_io *beio) { struct ctl_be_block_lun *be_lun; union ctl_io *io; io = beio->io; be_lun = beio->lun; ctl_free_beio(beio); if ((io->io_hdr.flags & CTL_FLAG_ABORT) || ((io->io_hdr.status & CTL_STATUS_MASK) != CTL_STATUS_NONE && (io->io_hdr.status & CTL_STATUS_MASK) != CTL_SUCCESS)) { ctl_data_submit_done(io); return; } io->io_hdr.status &= ~CTL_STATUS_MASK; io->io_hdr.status |= CTL_STATUS_NONE; mtx_lock(&be_lun->queue_lock); /* * XXX KDM make sure that links is okay to use at this point. * Otherwise, we either need to add another field to ctl_io_hdr, * or deal with resource allocation here. */ STAILQ_INSERT_TAIL(&be_lun->input_queue, &io->io_hdr, links); mtx_unlock(&be_lun->queue_lock); taskqueue_enqueue(be_lun->io_taskqueue, &be_lun->io_task); } static void ctl_be_block_dispatch(struct ctl_be_block_lun *be_lun, union ctl_io *io) { struct ctl_be_block_io *beio; struct ctl_be_block_softc *softc; struct ctl_lba_len_flags *lbalen; struct ctl_ptr_len_flags *bptrlen; uint64_t len_left, lbas; int i; softc = be_lun->softc; DPRINTF("entered\n"); lbalen = ARGS(io); if (lbalen->flags & CTL_LLF_WRITE) { SDT_PROBE(cbb, kernel, write, start, 0, 0, 0, 0, 0); } else { SDT_PROBE(cbb, kernel, read, start, 0, 0, 0, 0, 0); } beio = ctl_alloc_beio(softc); beio->io = io; beio->lun = be_lun; bptrlen = PRIV(io); bptrlen->ptr = (void *)beio; switch (io->scsiio.tag_type) { case CTL_TAG_ORDERED: beio->ds_tag_type = DEVSTAT_TAG_ORDERED; break; case CTL_TAG_HEAD_OF_QUEUE: beio->ds_tag_type = DEVSTAT_TAG_HEAD; break; case CTL_TAG_UNTAGGED: case CTL_TAG_SIMPLE: case CTL_TAG_ACA: default: beio->ds_tag_type = DEVSTAT_TAG_SIMPLE; break; } if (lbalen->flags & CTL_LLF_WRITE) { beio->bio_cmd = BIO_WRITE; beio->ds_trans_type = DEVSTAT_WRITE; } else { beio->bio_cmd = BIO_READ; beio->ds_trans_type = DEVSTAT_READ; } DPRINTF("%s at LBA %jx len %u @%ju\n", (beio->bio_cmd == BIO_READ) ? "READ" : "WRITE", (uintmax_t)lbalen->lba, lbalen->len, bptrlen->len); if (lbalen->flags & CTL_LLF_COMPARE) lbas = CTLBLK_HALF_IO_SIZE; else lbas = CTLBLK_MAX_IO_SIZE; lbas = MIN(lbalen->len - bptrlen->len, lbas / be_lun->blocksize); beio->io_offset = (lbalen->lba + bptrlen->len) * be_lun->blocksize; beio->io_len = lbas * be_lun->blocksize; bptrlen->len += lbas; for (i = 0, len_left = beio->io_len; len_left > 0; i++) { KASSERT(i < CTLBLK_MAX_SEGS, ("Too many segs (%d >= %d)", i, CTLBLK_MAX_SEGS)); /* * Setup the S/G entry for this chunk. */ beio->sg_segs[i].len = min(CTLBLK_MAX_SEG, len_left); beio->sg_segs[i].addr = uma_zalloc(be_lun->lun_zone, M_WAITOK); DPRINTF("segment %d addr %p len %zd\n", i, beio->sg_segs[i].addr, beio->sg_segs[i].len); /* Set up second segment for compare operation. */ if (lbalen->flags & CTL_LLF_COMPARE) { beio->sg_segs[i + CTLBLK_HALF_SEGS].len = beio->sg_segs[i].len; beio->sg_segs[i + CTLBLK_HALF_SEGS].addr = uma_zalloc(be_lun->lun_zone, M_WAITOK); } beio->num_segs++; len_left -= beio->sg_segs[i].len; } if (bptrlen->len < lbalen->len) beio->beio_cont = ctl_be_block_next; io->scsiio.be_move_done = ctl_be_block_move_done; /* For compare we have separate S/G lists for read and datamove. */ if (lbalen->flags & CTL_LLF_COMPARE) io->scsiio.kern_data_ptr = (uint8_t *)&beio->sg_segs[CTLBLK_HALF_SEGS]; else io->scsiio.kern_data_ptr = (uint8_t *)beio->sg_segs; io->scsiio.kern_data_len = beio->io_len; io->scsiio.kern_data_resid = 0; io->scsiio.kern_sg_entries = beio->num_segs; io->io_hdr.flags |= CTL_FLAG_ALLOCATED | CTL_FLAG_KDPTR_SGLIST; /* * For the read case, we need to read the data into our buffers and * then we can send it back to the user. For the write case, we * need to get the data from the user first. */ if (beio->bio_cmd == BIO_READ) { SDT_PROBE(cbb, kernel, read, alloc_done, 0, 0, 0, 0, 0); be_lun->dispatch(be_lun, beio); } else { SDT_PROBE(cbb, kernel, write, alloc_done, 0, 0, 0, 0, 0); #ifdef CTL_TIME_IO getbintime(&io->io_hdr.dma_start_bt); #endif ctl_datamove(io); } } static void ctl_be_block_worker(void *context, int pending) { struct ctl_be_block_lun *be_lun; struct ctl_be_block_softc *softc; union ctl_io *io; be_lun = (struct ctl_be_block_lun *)context; softc = be_lun->softc; DPRINTF("entered\n"); mtx_lock(&be_lun->queue_lock); for (;;) { io = (union ctl_io *)STAILQ_FIRST(&be_lun->datamove_queue); if (io != NULL) { struct ctl_be_block_io *beio; DPRINTF("datamove queue\n"); STAILQ_REMOVE(&be_lun->datamove_queue, &io->io_hdr, ctl_io_hdr, links); mtx_unlock(&be_lun->queue_lock); beio = (struct ctl_be_block_io *)PRIV(io)->ptr; be_lun->dispatch(be_lun, beio); mtx_lock(&be_lun->queue_lock); continue; } io = (union ctl_io *)STAILQ_FIRST(&be_lun->config_write_queue); if (io != NULL) { DPRINTF("config write queue\n"); STAILQ_REMOVE(&be_lun->config_write_queue, &io->io_hdr, ctl_io_hdr, links); mtx_unlock(&be_lun->queue_lock); ctl_be_block_cw_dispatch(be_lun, io); mtx_lock(&be_lun->queue_lock); continue; } io = (union ctl_io *)STAILQ_FIRST(&be_lun->config_read_queue); if (io != NULL) { DPRINTF("config read queue\n"); STAILQ_REMOVE(&be_lun->config_read_queue, &io->io_hdr, ctl_io_hdr, links); mtx_unlock(&be_lun->queue_lock); ctl_be_block_cr_dispatch(be_lun, io); mtx_lock(&be_lun->queue_lock); continue; } io = (union ctl_io *)STAILQ_FIRST(&be_lun->input_queue); if (io != NULL) { DPRINTF("input queue\n"); STAILQ_REMOVE(&be_lun->input_queue, &io->io_hdr, ctl_io_hdr, links); mtx_unlock(&be_lun->queue_lock); /* * We must drop the lock, since this routine and * its children may sleep. */ ctl_be_block_dispatch(be_lun, io); mtx_lock(&be_lun->queue_lock); continue; } /* * If we get here, there is no work left in the queues, so * just break out and let the task queue go to sleep. */ break; } mtx_unlock(&be_lun->queue_lock); } /* * Entry point from CTL to the backend for I/O. We queue everything to a * work thread, so this just puts the I/O on a queue and wakes up the * thread. */ static int ctl_be_block_submit(union ctl_io *io) { struct ctl_be_block_lun *be_lun; struct ctl_be_lun *ctl_be_lun; DPRINTF("entered\n"); ctl_be_lun = (struct ctl_be_lun *)io->io_hdr.ctl_private[ CTL_PRIV_BACKEND_LUN].ptr; be_lun = (struct ctl_be_block_lun *)ctl_be_lun->be_lun; /* * Make sure we only get SCSI I/O. */ KASSERT(io->io_hdr.io_type == CTL_IO_SCSI, ("Non-SCSI I/O (type " "%#x) encountered", io->io_hdr.io_type)); PRIV(io)->len = 0; mtx_lock(&be_lun->queue_lock); /* * XXX KDM make sure that links is okay to use at this point. * Otherwise, we either need to add another field to ctl_io_hdr, * or deal with resource allocation here. */ STAILQ_INSERT_TAIL(&be_lun->input_queue, &io->io_hdr, links); mtx_unlock(&be_lun->queue_lock); taskqueue_enqueue(be_lun->io_taskqueue, &be_lun->io_task); return (CTL_RETVAL_COMPLETE); } static int ctl_be_block_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td) { struct ctl_be_block_softc *softc; int error; softc = &backend_block_softc; error = 0; switch (cmd) { case CTL_LUN_REQ: { struct ctl_lun_req *lun_req; lun_req = (struct ctl_lun_req *)addr; switch (lun_req->reqtype) { case CTL_LUNREQ_CREATE: error = ctl_be_block_create(softc, lun_req); break; case CTL_LUNREQ_RM: error = ctl_be_block_rm(softc, lun_req); break; case CTL_LUNREQ_MODIFY: error = ctl_be_block_modify(softc, lun_req); break; default: lun_req->status = CTL_LUN_ERROR; snprintf(lun_req->error_str, sizeof(lun_req->error_str), "invalid LUN request type %d", lun_req->reqtype); break; } break; } default: error = ENOTTY; break; } return (error); } static int ctl_be_block_open_file(struct ctl_be_block_lun *be_lun, struct ctl_lun_req *req) { struct ctl_be_block_filedata *file_data; struct ctl_lun_create_params *params; char *value; struct vattr vattr; off_t ps, pss, po, pos, us, uss, uo, uos; int error; error = 0; file_data = &be_lun->backend.file; params = &be_lun->params; be_lun->dev_type = CTL_BE_BLOCK_FILE; be_lun->dispatch = ctl_be_block_dispatch_file; be_lun->lun_flush = ctl_be_block_flush_file; be_lun->get_lba_status = ctl_be_block_gls_file; be_lun->getattr = ctl_be_block_getattr_file; error = VOP_GETATTR(be_lun->vn, &vattr, curthread->td_ucred); if (error != 0) { snprintf(req->error_str, sizeof(req->error_str), "error calling VOP_GETATTR() for file %s", be_lun->dev_path); return (error); } /* * Verify that we have the ability to upgrade to exclusive * access on this file so we can trap errors at open instead * of reporting them during first access. */ if (VOP_ISLOCKED(be_lun->vn) != LK_EXCLUSIVE) { vn_lock(be_lun->vn, LK_UPGRADE | LK_RETRY); if (be_lun->vn->v_iflag & VI_DOOMED) { error = EBADF; snprintf(req->error_str, sizeof(req->error_str), "error locking file %s", be_lun->dev_path); return (error); } } file_data->cred = crhold(curthread->td_ucred); if (params->lun_size_bytes != 0) be_lun->size_bytes = params->lun_size_bytes; else be_lun->size_bytes = vattr.va_size; /* * We set the multi thread flag for file operations because all * filesystems (in theory) are capable of allowing multiple readers * of a file at once. So we want to get the maximum possible * concurrency. */ be_lun->flags |= CTL_BE_BLOCK_LUN_MULTI_THREAD; /* * For files we can use any logical block size. Prefer 512 bytes * for compatibility reasons. If file's vattr.va_blocksize * (preferred I/O block size) is bigger and multiple to chosen * logical block size -- report it as physical block size. */ if (params->blocksize_bytes != 0) be_lun->blocksize = params->blocksize_bytes; else be_lun->blocksize = 512; us = ps = vattr.va_blocksize; uo = po = 0; value = ctl_get_opt(&be_lun->ctl_be_lun.options, "pblocksize"); if (value != NULL) ctl_expand_number(value, &ps); value = ctl_get_opt(&be_lun->ctl_be_lun.options, "pblockoffset"); if (value != NULL) ctl_expand_number(value, &po); pss = ps / be_lun->blocksize; pos = po / be_lun->blocksize; if ((pss > 0) && (pss * be_lun->blocksize == ps) && (pss >= pos) && ((pss & (pss - 1)) == 0) && (pos * be_lun->blocksize == po)) { be_lun->pblockexp = fls(pss) - 1; be_lun->pblockoff = (pss - pos) % pss; } value = ctl_get_opt(&be_lun->ctl_be_lun.options, "ublocksize"); if (value != NULL) ctl_expand_number(value, &us); value = ctl_get_opt(&be_lun->ctl_be_lun.options, "ublockoffset"); if (value != NULL) ctl_expand_number(value, &uo); uss = us / be_lun->blocksize; uos = uo / be_lun->blocksize; if ((uss > 0) && (uss * be_lun->blocksize == us) && (uss >= uos) && ((uss & (uss - 1)) == 0) && (uos * be_lun->blocksize == uo)) { be_lun->ublockexp = fls(uss) - 1; be_lun->ublockoff = (uss - uos) % uss; } /* * Sanity check. The media size has to be at least one * sector long. */ if (be_lun->size_bytes < be_lun->blocksize) { error = EINVAL; snprintf(req->error_str, sizeof(req->error_str), "file %s size %ju < block size %u", be_lun->dev_path, (uintmax_t)be_lun->size_bytes, be_lun->blocksize); } be_lun->opttxferlen = CTLBLK_MAX_IO_SIZE / be_lun->blocksize; return (error); } static int ctl_be_block_open_dev(struct ctl_be_block_lun *be_lun, struct ctl_lun_req *req) { struct ctl_lun_create_params *params; struct vattr vattr; struct cdev *dev; struct cdevsw *devsw; char *value; - int error, atomic, maxio; + int error, atomic, maxio, unmap; off_t ps, pss, po, pos, us, uss, uo, uos; params = &be_lun->params; be_lun->dev_type = CTL_BE_BLOCK_DEV; be_lun->backend.dev.cdev = be_lun->vn->v_rdev; be_lun->backend.dev.csw = dev_refthread(be_lun->backend.dev.cdev, &be_lun->backend.dev.dev_ref); if (be_lun->backend.dev.csw == NULL) panic("Unable to retrieve device switch"); if (strcmp(be_lun->backend.dev.csw->d_name, "zvol") == 0) { be_lun->dispatch = ctl_be_block_dispatch_zvol; be_lun->get_lba_status = ctl_be_block_gls_zvol; atomic = maxio = CTLBLK_MAX_IO_SIZE; } else { be_lun->dispatch = ctl_be_block_dispatch_dev; atomic = 0; maxio = be_lun->backend.dev.cdev->si_iosize_max; if (maxio <= 0) maxio = DFLTPHYS; if (maxio > CTLBLK_MAX_IO_SIZE) maxio = CTLBLK_MAX_IO_SIZE; } be_lun->lun_flush = ctl_be_block_flush_dev; - be_lun->unmap = ctl_be_block_unmap_dev; be_lun->getattr = ctl_be_block_getattr_dev; error = VOP_GETATTR(be_lun->vn, &vattr, NOCRED); if (error) { snprintf(req->error_str, sizeof(req->error_str), "error getting vnode attributes for device %s", be_lun->dev_path); return (error); } dev = be_lun->vn->v_rdev; devsw = dev->si_devsw; if (!devsw->d_ioctl) { snprintf(req->error_str, sizeof(req->error_str), "no d_ioctl for device %s!", be_lun->dev_path); return (ENODEV); } error = devsw->d_ioctl(dev, DIOCGSECTORSIZE, (caddr_t)&be_lun->blocksize, FREAD, curthread); if (error) { snprintf(req->error_str, sizeof(req->error_str), "error %d returned for DIOCGSECTORSIZE ioctl " "on %s!", error, be_lun->dev_path); return (error); } /* * If the user has asked for a blocksize that is greater than the * backing device's blocksize, we can do it only if the blocksize * the user is asking for is an even multiple of the underlying * device's blocksize. */ if ((params->blocksize_bytes != 0) && (params->blocksize_bytes > be_lun->blocksize)) { uint32_t bs_multiple, tmp_blocksize; bs_multiple = params->blocksize_bytes / be_lun->blocksize; tmp_blocksize = bs_multiple * be_lun->blocksize; if (tmp_blocksize == params->blocksize_bytes) { be_lun->blocksize = params->blocksize_bytes; } else { snprintf(req->error_str, sizeof(req->error_str), "requested blocksize %u is not an even " "multiple of backing device blocksize %u", params->blocksize_bytes, be_lun->blocksize); return (EINVAL); } } else if ((params->blocksize_bytes != 0) && (params->blocksize_bytes != be_lun->blocksize)) { snprintf(req->error_str, sizeof(req->error_str), "requested blocksize %u < backing device " "blocksize %u", params->blocksize_bytes, be_lun->blocksize); return (EINVAL); } error = devsw->d_ioctl(dev, DIOCGMEDIASIZE, (caddr_t)&be_lun->size_bytes, FREAD, curthread); if (error) { snprintf(req->error_str, sizeof(req->error_str), "error %d returned for DIOCGMEDIASIZE " " ioctl on %s!", error, be_lun->dev_path); return (error); } if (params->lun_size_bytes != 0) { if (params->lun_size_bytes > be_lun->size_bytes) { snprintf(req->error_str, sizeof(req->error_str), "requested LUN size %ju > backing device " "size %ju", (uintmax_t)params->lun_size_bytes, (uintmax_t)be_lun->size_bytes); return (EINVAL); } be_lun->size_bytes = params->lun_size_bytes; } error = devsw->d_ioctl(dev, DIOCGSTRIPESIZE, (caddr_t)&ps, FREAD, curthread); if (error) ps = po = 0; else { error = devsw->d_ioctl(dev, DIOCGSTRIPEOFFSET, (caddr_t)&po, FREAD, curthread); if (error) po = 0; } us = ps; uo = po; value = ctl_get_opt(&be_lun->ctl_be_lun.options, "pblocksize"); if (value != NULL) ctl_expand_number(value, &ps); value = ctl_get_opt(&be_lun->ctl_be_lun.options, "pblockoffset"); if (value != NULL) ctl_expand_number(value, &po); pss = ps / be_lun->blocksize; pos = po / be_lun->blocksize; if ((pss > 0) && (pss * be_lun->blocksize == ps) && (pss >= pos) && ((pss & (pss - 1)) == 0) && (pos * be_lun->blocksize == po)) { be_lun->pblockexp = fls(pss) - 1; be_lun->pblockoff = (pss - pos) % pss; } value = ctl_get_opt(&be_lun->ctl_be_lun.options, "ublocksize"); if (value != NULL) ctl_expand_number(value, &us); value = ctl_get_opt(&be_lun->ctl_be_lun.options, "ublockoffset"); if (value != NULL) ctl_expand_number(value, &uo); uss = us / be_lun->blocksize; uos = uo / be_lun->blocksize; if ((uss > 0) && (uss * be_lun->blocksize == us) && (uss >= uos) && ((uss & (uss - 1)) == 0) && (uos * be_lun->blocksize == uo)) { be_lun->ublockexp = fls(uss) - 1; be_lun->ublockoff = (uss - uos) % uss; } be_lun->atomicblock = atomic / be_lun->blocksize; be_lun->opttxferlen = maxio / be_lun->blocksize; + + if (be_lun->dispatch == ctl_be_block_dispatch_zvol) { + unmap = 1; + } else { + struct diocgattr_arg arg; + + strlcpy(arg.name, "GEOM::candelete", sizeof(arg.name)); + arg.len = sizeof(arg.value.i); + error = devsw->d_ioctl(dev, DIOCGATTR, + (caddr_t)&arg, FREAD, curthread); + unmap = (error == 0) ? arg.value.i : 0; + } + value = ctl_get_opt(&be_lun->ctl_be_lun.options, "unmap"); + if (value != NULL) + unmap = (strcmp(value, "on") == 0); + if (unmap) + be_lun->unmap = ctl_be_block_unmap_dev; + return (0); } static int ctl_be_block_close(struct ctl_be_block_lun *be_lun) { DROP_GIANT(); if (be_lun->vn) { int flags = FREAD | FWRITE; switch (be_lun->dev_type) { case CTL_BE_BLOCK_DEV: if (be_lun->backend.dev.csw) { dev_relthread(be_lun->backend.dev.cdev, be_lun->backend.dev.dev_ref); be_lun->backend.dev.csw = NULL; be_lun->backend.dev.cdev = NULL; } break; case CTL_BE_BLOCK_FILE: break; case CTL_BE_BLOCK_NONE: break; default: panic("Unexpected backend type."); break; } (void)vn_close(be_lun->vn, flags, NOCRED, curthread); be_lun->vn = NULL; switch (be_lun->dev_type) { case CTL_BE_BLOCK_DEV: break; case CTL_BE_BLOCK_FILE: if (be_lun->backend.file.cred != NULL) { crfree(be_lun->backend.file.cred); be_lun->backend.file.cred = NULL; } break; case CTL_BE_BLOCK_NONE: break; default: panic("Unexpected backend type."); break; } be_lun->dev_type = CTL_BE_BLOCK_NONE; } PICKUP_GIANT(); return (0); } static int ctl_be_block_open(struct ctl_be_block_softc *softc, struct ctl_be_block_lun *be_lun, struct ctl_lun_req *req) { struct nameidata nd; int flags; int error; /* * XXX KDM allow a read-only option? */ flags = FREAD | FWRITE; error = 0; if (rootvnode == NULL) { snprintf(req->error_str, sizeof(req->error_str), "Root filesystem is not mounted"); return (1); } if (!curthread->td_proc->p_fd->fd_cdir) { curthread->td_proc->p_fd->fd_cdir = rootvnode; VREF(rootvnode); } if (!curthread->td_proc->p_fd->fd_rdir) { curthread->td_proc->p_fd->fd_rdir = rootvnode; VREF(rootvnode); } if (!curthread->td_proc->p_fd->fd_jdir) { curthread->td_proc->p_fd->fd_jdir = rootvnode; VREF(rootvnode); } again: NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, be_lun->dev_path, curthread); error = vn_open(&nd, &flags, 0, NULL); if (error) { /* * This is the only reasonable guess we can make as far as * path if the user doesn't give us a fully qualified path. * If they want to specify a file, they need to specify the * full path. */ if (be_lun->dev_path[0] != '/') { char *dev_path = "/dev/"; char *dev_name; /* Try adding device path at beginning of name */ dev_name = malloc(strlen(be_lun->dev_path) + strlen(dev_path) + 1, M_CTLBLK, M_WAITOK); if (dev_name) { sprintf(dev_name, "%s%s", dev_path, be_lun->dev_path); free(be_lun->dev_path, M_CTLBLK); be_lun->dev_path = dev_name; goto again; } } snprintf(req->error_str, sizeof(req->error_str), "error opening %s: %d", be_lun->dev_path, error); return (error); } NDFREE(&nd, NDF_ONLY_PNBUF); be_lun->vn = nd.ni_vp; /* We only support disks and files. */ if (vn_isdisk(be_lun->vn, &error)) { error = ctl_be_block_open_dev(be_lun, req); } else if (be_lun->vn->v_type == VREG) { error = ctl_be_block_open_file(be_lun, req); } else { error = EINVAL; snprintf(req->error_str, sizeof(req->error_str), "%s is not a disk or plain file", be_lun->dev_path); } VOP_UNLOCK(be_lun->vn, 0); if (error != 0) { ctl_be_block_close(be_lun); return (error); } be_lun->blocksize_shift = fls(be_lun->blocksize) - 1; be_lun->size_blocks = be_lun->size_bytes >> be_lun->blocksize_shift; return (0); } static int ctl_be_block_create(struct ctl_be_block_softc *softc, struct ctl_lun_req *req) { struct ctl_be_block_lun *be_lun; struct ctl_lun_create_params *params; char num_thread_str[16]; char tmpstr[32]; char *value; - int retval, num_threads, unmap; + int retval, num_threads; int tmp_num_threads; params = &req->reqdata.create; retval = 0; req->status = CTL_LUN_OK; num_threads = cbb_num_threads; be_lun = malloc(sizeof(*be_lun), M_CTLBLK, M_ZERO | M_WAITOK); be_lun->params = req->reqdata.create; be_lun->softc = softc; STAILQ_INIT(&be_lun->input_queue); STAILQ_INIT(&be_lun->config_read_queue); STAILQ_INIT(&be_lun->config_write_queue); STAILQ_INIT(&be_lun->datamove_queue); sprintf(be_lun->lunname, "cblk%d", softc->num_luns); mtx_init(&be_lun->io_lock, "cblk io lock", NULL, MTX_DEF); mtx_init(&be_lun->queue_lock, "cblk queue lock", NULL, MTX_DEF); ctl_init_opts(&be_lun->ctl_be_lun.options, req->num_be_args, req->kern_be_args); be_lun->lun_zone = uma_zcreate(be_lun->lunname, CTLBLK_MAX_SEG, NULL, NULL, NULL, NULL, /*align*/ 0, /*flags*/0); if (be_lun->lun_zone == NULL) { snprintf(req->error_str, sizeof(req->error_str), "error allocating UMA zone"); goto bailout_error; } if (params->flags & CTL_LUN_FLAG_DEV_TYPE) be_lun->ctl_be_lun.lun_type = params->device_type; else be_lun->ctl_be_lun.lun_type = T_DIRECT; if (be_lun->ctl_be_lun.lun_type == T_DIRECT) { value = ctl_get_opt(&be_lun->ctl_be_lun.options, "file"); if (value == NULL) { snprintf(req->error_str, sizeof(req->error_str), "no file argument specified"); goto bailout_error; } be_lun->dev_path = strdup(value, M_CTLBLK); be_lun->blocksize = 512; be_lun->blocksize_shift = fls(be_lun->blocksize) - 1; retval = ctl_be_block_open(softc, be_lun, req); if (retval != 0) { retval = 0; req->status = CTL_LUN_WARNING; } } else { /* * For processor devices, we don't have any size. */ be_lun->blocksize = 0; be_lun->pblockexp = 0; be_lun->pblockoff = 0; be_lun->ublockexp = 0; be_lun->ublockoff = 0; be_lun->size_blocks = 0; be_lun->size_bytes = 0; be_lun->ctl_be_lun.maxlba = 0; /* * Default to just 1 thread for processor devices. */ num_threads = 1; } /* * XXX This searching loop might be refactored to be combined with * the loop above, */ value = ctl_get_opt(&be_lun->ctl_be_lun.options, "num_threads"); if (value != NULL) { tmp_num_threads = strtol(value, NULL, 0); /* * We don't let the user specify less than one * thread, but hope he's clueful enough not to * specify 1000 threads. */ if (tmp_num_threads < 1) { snprintf(req->error_str, sizeof(req->error_str), "invalid number of threads %s", num_thread_str); goto bailout_error; } num_threads = tmp_num_threads; } - unmap = (be_lun->dispatch == ctl_be_block_dispatch_zvol); - value = ctl_get_opt(&be_lun->ctl_be_lun.options, "unmap"); - if (value != NULL) - unmap = (strcmp(value, "on") == 0); be_lun->flags = CTL_BE_BLOCK_LUN_UNCONFIGURED; be_lun->ctl_be_lun.flags = CTL_LUN_FLAG_PRIMARY; if (be_lun->vn == NULL) be_lun->ctl_be_lun.flags |= CTL_LUN_FLAG_OFFLINE; - if (unmap) + if (be_lun->unmap != NULL) be_lun->ctl_be_lun.flags |= CTL_LUN_FLAG_UNMAP; if (be_lun->dispatch != ctl_be_block_dispatch_dev) be_lun->ctl_be_lun.flags |= CTL_LUN_FLAG_SERSEQ_READ; be_lun->ctl_be_lun.be_lun = be_lun; be_lun->ctl_be_lun.maxlba = (be_lun->size_blocks == 0) ? 0 : (be_lun->size_blocks - 1); be_lun->ctl_be_lun.blocksize = be_lun->blocksize; be_lun->ctl_be_lun.pblockexp = be_lun->pblockexp; be_lun->ctl_be_lun.pblockoff = be_lun->pblockoff; be_lun->ctl_be_lun.ublockexp = be_lun->ublockexp; be_lun->ctl_be_lun.ublockoff = be_lun->ublockoff; be_lun->ctl_be_lun.atomicblock = be_lun->atomicblock; be_lun->ctl_be_lun.opttxferlen = be_lun->opttxferlen; /* Tell the user the blocksize we ended up using */ params->lun_size_bytes = be_lun->size_bytes; params->blocksize_bytes = be_lun->blocksize; if (params->flags & CTL_LUN_FLAG_ID_REQ) { be_lun->ctl_be_lun.req_lun_id = params->req_lun_id; be_lun->ctl_be_lun.flags |= CTL_LUN_FLAG_ID_REQ; } else be_lun->ctl_be_lun.req_lun_id = 0; be_lun->ctl_be_lun.lun_shutdown = ctl_be_block_lun_shutdown; be_lun->ctl_be_lun.lun_config_status = ctl_be_block_lun_config_status; be_lun->ctl_be_lun.be = &ctl_be_block_driver; if ((params->flags & CTL_LUN_FLAG_SERIAL_NUM) == 0) { snprintf(tmpstr, sizeof(tmpstr), "MYSERIAL%4d", softc->num_luns); strncpy((char *)be_lun->ctl_be_lun.serial_num, tmpstr, MIN(sizeof(be_lun->ctl_be_lun.serial_num), sizeof(tmpstr))); /* Tell the user what we used for a serial number */ strncpy((char *)params->serial_num, tmpstr, MIN(sizeof(params->serial_num), sizeof(tmpstr))); } else { strncpy((char *)be_lun->ctl_be_lun.serial_num, params->serial_num, MIN(sizeof(be_lun->ctl_be_lun.serial_num), sizeof(params->serial_num))); } if ((params->flags & CTL_LUN_FLAG_DEVID) == 0) { snprintf(tmpstr, sizeof(tmpstr), "MYDEVID%4d", softc->num_luns); strncpy((char *)be_lun->ctl_be_lun.device_id, tmpstr, MIN(sizeof(be_lun->ctl_be_lun.device_id), sizeof(tmpstr))); /* Tell the user what we used for a device ID */ strncpy((char *)params->device_id, tmpstr, MIN(sizeof(params->device_id), sizeof(tmpstr))); } else { strncpy((char *)be_lun->ctl_be_lun.device_id, params->device_id, MIN(sizeof(be_lun->ctl_be_lun.device_id), sizeof(params->device_id))); } TASK_INIT(&be_lun->io_task, /*priority*/0, ctl_be_block_worker, be_lun); be_lun->io_taskqueue = taskqueue_create(be_lun->lunname, M_WAITOK, taskqueue_thread_enqueue, /*context*/&be_lun->io_taskqueue); if (be_lun->io_taskqueue == NULL) { snprintf(req->error_str, sizeof(req->error_str), "unable to create taskqueue"); goto bailout_error; } /* * Note that we start the same number of threads by default for * both the file case and the block device case. For the file * case, we need multiple threads to allow concurrency, because the * vnode interface is designed to be a blocking interface. For the * block device case, ZFS zvols at least will block the caller's * context in many instances, and so we need multiple threads to * overcome that problem. Other block devices don't need as many * threads, but they shouldn't cause too many problems. * * If the user wants to just have a single thread for a block * device, he can specify that when the LUN is created, or change * the tunable/sysctl to alter the default number of threads. */ retval = taskqueue_start_threads(&be_lun->io_taskqueue, /*num threads*/num_threads, /*priority*/PWAIT, /*thread name*/ "%s taskq", be_lun->lunname); if (retval != 0) goto bailout_error; be_lun->num_threads = num_threads; mtx_lock(&softc->lock); softc->num_luns++; STAILQ_INSERT_TAIL(&softc->lun_list, be_lun, links); mtx_unlock(&softc->lock); retval = ctl_add_lun(&be_lun->ctl_be_lun); if (retval != 0) { mtx_lock(&softc->lock); STAILQ_REMOVE(&softc->lun_list, be_lun, ctl_be_block_lun, links); softc->num_luns--; mtx_unlock(&softc->lock); snprintf(req->error_str, sizeof(req->error_str), "ctl_add_lun() returned error %d, see dmesg for " "details", retval); retval = 0; goto bailout_error; } mtx_lock(&softc->lock); /* * Tell the config_status routine that we're waiting so it won't * clean up the LUN in the event of an error. */ be_lun->flags |= CTL_BE_BLOCK_LUN_WAITING; while (be_lun->flags & CTL_BE_BLOCK_LUN_UNCONFIGURED) { retval = msleep(be_lun, &softc->lock, PCATCH, "ctlblk", 0); if (retval == EINTR) break; } be_lun->flags &= ~CTL_BE_BLOCK_LUN_WAITING; if (be_lun->flags & CTL_BE_BLOCK_LUN_CONFIG_ERR) { snprintf(req->error_str, sizeof(req->error_str), "LUN configuration error, see dmesg for details"); STAILQ_REMOVE(&softc->lun_list, be_lun, ctl_be_block_lun, links); softc->num_luns--; mtx_unlock(&softc->lock); goto bailout_error; } else { params->req_lun_id = be_lun->ctl_be_lun.lun_id; } mtx_unlock(&softc->lock); be_lun->disk_stats = devstat_new_entry("cbb", params->req_lun_id, be_lun->blocksize, DEVSTAT_ALL_SUPPORTED, be_lun->ctl_be_lun.lun_type | DEVSTAT_TYPE_IF_OTHER, DEVSTAT_PRIORITY_OTHER); return (retval); bailout_error: req->status = CTL_LUN_ERROR; if (be_lun->io_taskqueue != NULL) taskqueue_free(be_lun->io_taskqueue); ctl_be_block_close(be_lun); if (be_lun->dev_path != NULL) free(be_lun->dev_path, M_CTLBLK); if (be_lun->lun_zone != NULL) uma_zdestroy(be_lun->lun_zone); ctl_free_opts(&be_lun->ctl_be_lun.options); mtx_destroy(&be_lun->queue_lock); mtx_destroy(&be_lun->io_lock); free(be_lun, M_CTLBLK); return (retval); } static int ctl_be_block_rm(struct ctl_be_block_softc *softc, struct ctl_lun_req *req) { struct ctl_lun_rm_params *params; struct ctl_be_block_lun *be_lun; int retval; params = &req->reqdata.rm; mtx_lock(&softc->lock); be_lun = NULL; STAILQ_FOREACH(be_lun, &softc->lun_list, links) { if (be_lun->ctl_be_lun.lun_id == params->lun_id) break; } mtx_unlock(&softc->lock); if (be_lun == NULL) { snprintf(req->error_str, sizeof(req->error_str), "LUN %u is not managed by the block backend", params->lun_id); goto bailout_error; } retval = ctl_disable_lun(&be_lun->ctl_be_lun); if (retval != 0) { snprintf(req->error_str, sizeof(req->error_str), "error %d returned from ctl_disable_lun() for " "LUN %d", retval, params->lun_id); goto bailout_error; } retval = ctl_invalidate_lun(&be_lun->ctl_be_lun); if (retval != 0) { snprintf(req->error_str, sizeof(req->error_str), "error %d returned from ctl_invalidate_lun() for " "LUN %d", retval, params->lun_id); goto bailout_error; } mtx_lock(&softc->lock); be_lun->flags |= CTL_BE_BLOCK_LUN_WAITING; while ((be_lun->flags & CTL_BE_BLOCK_LUN_UNCONFIGURED) == 0) { retval = msleep(be_lun, &softc->lock, PCATCH, "ctlblk", 0); if (retval == EINTR) break; } be_lun->flags &= ~CTL_BE_BLOCK_LUN_WAITING; if ((be_lun->flags & CTL_BE_BLOCK_LUN_UNCONFIGURED) == 0) { snprintf(req->error_str, sizeof(req->error_str), "interrupted waiting for LUN to be freed"); mtx_unlock(&softc->lock); goto bailout_error; } STAILQ_REMOVE(&softc->lun_list, be_lun, ctl_be_block_lun, links); softc->num_luns--; mtx_unlock(&softc->lock); taskqueue_drain(be_lun->io_taskqueue, &be_lun->io_task); taskqueue_free(be_lun->io_taskqueue); ctl_be_block_close(be_lun); if (be_lun->disk_stats != NULL) devstat_remove_entry(be_lun->disk_stats); uma_zdestroy(be_lun->lun_zone); ctl_free_opts(&be_lun->ctl_be_lun.options); free(be_lun->dev_path, M_CTLBLK); mtx_destroy(&be_lun->queue_lock); mtx_destroy(&be_lun->io_lock); free(be_lun, M_CTLBLK); req->status = CTL_LUN_OK; return (0); bailout_error: req->status = CTL_LUN_ERROR; return (0); } static int ctl_be_block_modify_file(struct ctl_be_block_lun *be_lun, struct ctl_lun_req *req) { struct vattr vattr; int error; struct ctl_lun_create_params *params = &be_lun->params; if (params->lun_size_bytes != 0) { be_lun->size_bytes = params->lun_size_bytes; } else { vn_lock(be_lun->vn, LK_SHARED | LK_RETRY); error = VOP_GETATTR(be_lun->vn, &vattr, curthread->td_ucred); VOP_UNLOCK(be_lun->vn, 0); if (error != 0) { snprintf(req->error_str, sizeof(req->error_str), "error calling VOP_GETATTR() for file %s", be_lun->dev_path); return (error); } be_lun->size_bytes = vattr.va_size; } return (0); } static int ctl_be_block_modify_dev(struct ctl_be_block_lun *be_lun, struct ctl_lun_req *req) { struct ctl_be_block_devdata *dev_data; int error; struct ctl_lun_create_params *params = &be_lun->params; uint64_t size_bytes; dev_data = &be_lun->backend.dev; if (!dev_data->csw->d_ioctl) { snprintf(req->error_str, sizeof(req->error_str), "no d_ioctl for device %s!", be_lun->dev_path); return (ENODEV); } error = dev_data->csw->d_ioctl(dev_data->cdev, DIOCGMEDIASIZE, (caddr_t)&size_bytes, FREAD, curthread); if (error) { snprintf(req->error_str, sizeof(req->error_str), "error %d returned for DIOCGMEDIASIZE ioctl " "on %s!", error, be_lun->dev_path); return (error); } if (params->lun_size_bytes != 0) { if (params->lun_size_bytes > size_bytes) { snprintf(req->error_str, sizeof(req->error_str), "requested LUN size %ju > backing device " "size %ju", (uintmax_t)params->lun_size_bytes, (uintmax_t)size_bytes); return (EINVAL); } be_lun->size_bytes = params->lun_size_bytes; } else { be_lun->size_bytes = size_bytes; } return (0); } static int ctl_be_block_modify(struct ctl_be_block_softc *softc, struct ctl_lun_req *req) { struct ctl_lun_modify_params *params; struct ctl_be_block_lun *be_lun; uint64_t oldsize; int error; params = &req->reqdata.modify; mtx_lock(&softc->lock); be_lun = NULL; STAILQ_FOREACH(be_lun, &softc->lun_list, links) { if (be_lun->ctl_be_lun.lun_id == params->lun_id) break; } mtx_unlock(&softc->lock); if (be_lun == NULL) { snprintf(req->error_str, sizeof(req->error_str), "LUN %u is not managed by the block backend", params->lun_id); goto bailout_error; } be_lun->params.lun_size_bytes = params->lun_size_bytes; oldsize = be_lun->size_bytes; if (be_lun->vn == NULL) error = ctl_be_block_open(softc, be_lun, req); else if (be_lun->vn->v_type == VREG) error = ctl_be_block_modify_file(be_lun, req); else error = ctl_be_block_modify_dev(be_lun, req); if (error == 0 && be_lun->size_bytes != oldsize) { be_lun->size_blocks = be_lun->size_bytes >> be_lun->blocksize_shift; /* * The maximum LBA is the size - 1. * * XXX: Note that this field is being updated without locking, * which might cause problems on 32-bit architectures. */ + if (be_lun->unmap != NULL) + be_lun->ctl_be_lun.flags |= CTL_LUN_FLAG_UNMAP; be_lun->ctl_be_lun.maxlba = (be_lun->size_blocks == 0) ? 0 : (be_lun->size_blocks - 1); be_lun->ctl_be_lun.blocksize = be_lun->blocksize; be_lun->ctl_be_lun.pblockexp = be_lun->pblockexp; be_lun->ctl_be_lun.pblockoff = be_lun->pblockoff; be_lun->ctl_be_lun.ublockexp = be_lun->ublockexp; be_lun->ctl_be_lun.ublockoff = be_lun->ublockoff; be_lun->ctl_be_lun.atomicblock = be_lun->atomicblock; be_lun->ctl_be_lun.opttxferlen = be_lun->opttxferlen; ctl_lun_capacity_changed(&be_lun->ctl_be_lun); if (oldsize == 0 && be_lun->size_blocks != 0) ctl_lun_online(&be_lun->ctl_be_lun); } /* Tell the user the exact size we ended up using */ params->lun_size_bytes = be_lun->size_bytes; req->status = error ? CTL_LUN_WARNING : CTL_LUN_OK; return (0); bailout_error: req->status = CTL_LUN_ERROR; return (0); } static void ctl_be_block_lun_shutdown(void *be_lun) { struct ctl_be_block_lun *lun; struct ctl_be_block_softc *softc; lun = (struct ctl_be_block_lun *)be_lun; softc = lun->softc; mtx_lock(&softc->lock); lun->flags |= CTL_BE_BLOCK_LUN_UNCONFIGURED; if (lun->flags & CTL_BE_BLOCK_LUN_WAITING) wakeup(lun); mtx_unlock(&softc->lock); } static void ctl_be_block_lun_config_status(void *be_lun, ctl_lun_config_status status) { struct ctl_be_block_lun *lun; struct ctl_be_block_softc *softc; lun = (struct ctl_be_block_lun *)be_lun; softc = lun->softc; if (status == CTL_LUN_CONFIG_OK) { mtx_lock(&softc->lock); lun->flags &= ~CTL_BE_BLOCK_LUN_UNCONFIGURED; if (lun->flags & CTL_BE_BLOCK_LUN_WAITING) wakeup(lun); mtx_unlock(&softc->lock); /* * We successfully added the LUN, attempt to enable it. */ if (ctl_enable_lun(&lun->ctl_be_lun) != 0) { printf("%s: ctl_enable_lun() failed!\n", __func__); if (ctl_invalidate_lun(&lun->ctl_be_lun) != 0) { printf("%s: ctl_invalidate_lun() failed!\n", __func__); } } return; } mtx_lock(&softc->lock); lun->flags &= ~CTL_BE_BLOCK_LUN_UNCONFIGURED; lun->flags |= CTL_BE_BLOCK_LUN_CONFIG_ERR; wakeup(lun); mtx_unlock(&softc->lock); } static int ctl_be_block_config_write(union ctl_io *io) { struct ctl_be_block_lun *be_lun; struct ctl_be_lun *ctl_be_lun; int retval; retval = 0; DPRINTF("entered\n"); ctl_be_lun = (struct ctl_be_lun *)io->io_hdr.ctl_private[ CTL_PRIV_BACKEND_LUN].ptr; be_lun = (struct ctl_be_block_lun *)ctl_be_lun->be_lun; switch (io->scsiio.cdb[0]) { case SYNCHRONIZE_CACHE: case SYNCHRONIZE_CACHE_16: case WRITE_SAME_10: case WRITE_SAME_16: case UNMAP: /* * The upper level CTL code will filter out any CDBs with * the immediate bit set and return the proper error. * * We don't really need to worry about what LBA range the * user asked to be synced out. When they issue a sync * cache command, we'll sync out the whole thing. */ mtx_lock(&be_lun->queue_lock); STAILQ_INSERT_TAIL(&be_lun->config_write_queue, &io->io_hdr, links); mtx_unlock(&be_lun->queue_lock); taskqueue_enqueue(be_lun->io_taskqueue, &be_lun->io_task); break; case START_STOP_UNIT: { struct scsi_start_stop_unit *cdb; cdb = (struct scsi_start_stop_unit *)io->scsiio.cdb; if (cdb->how & SSS_START) retval = ctl_start_lun(ctl_be_lun); else { retval = ctl_stop_lun(ctl_be_lun); /* * XXX KDM Copan-specific offline behavior. * Figure out a reasonable way to port this? */ #ifdef NEEDTOPORT if ((retval == 0) && (cdb->byte2 & SSS_ONOFFLINE)) retval = ctl_lun_offline(ctl_be_lun); #endif } /* * In general, the above routines should not fail. They * just set state for the LUN. So we've got something * pretty wrong here if we can't start or stop the LUN. */ if (retval != 0) { ctl_set_internal_failure(&io->scsiio, /*sks_valid*/ 1, /*retry_count*/ 0xf051); retval = CTL_RETVAL_COMPLETE; } else { ctl_set_success(&io->scsiio); } ctl_config_write_done(io); break; } default: ctl_set_invalid_opcode(&io->scsiio); ctl_config_write_done(io); retval = CTL_RETVAL_COMPLETE; break; } return (retval); } static int ctl_be_block_config_read(union ctl_io *io) { struct ctl_be_block_lun *be_lun; struct ctl_be_lun *ctl_be_lun; int retval = 0; DPRINTF("entered\n"); ctl_be_lun = (struct ctl_be_lun *)io->io_hdr.ctl_private[ CTL_PRIV_BACKEND_LUN].ptr; be_lun = (struct ctl_be_block_lun *)ctl_be_lun->be_lun; switch (io->scsiio.cdb[0]) { case SERVICE_ACTION_IN: if (io->scsiio.cdb[1] == SGLS_SERVICE_ACTION) { mtx_lock(&be_lun->queue_lock); STAILQ_INSERT_TAIL(&be_lun->config_read_queue, &io->io_hdr, links); mtx_unlock(&be_lun->queue_lock); taskqueue_enqueue(be_lun->io_taskqueue, &be_lun->io_task); retval = CTL_RETVAL_QUEUED; break; } ctl_set_invalid_field(&io->scsiio, /*sks_valid*/ 1, /*command*/ 1, /*field*/ 1, /*bit_valid*/ 1, /*bit*/ 4); ctl_config_read_done(io); retval = CTL_RETVAL_COMPLETE; break; default: ctl_set_invalid_opcode(&io->scsiio); ctl_config_read_done(io); retval = CTL_RETVAL_COMPLETE; break; } return (retval); } static int ctl_be_block_lun_info(void *be_lun, struct sbuf *sb) { struct ctl_be_block_lun *lun; int retval; lun = (struct ctl_be_block_lun *)be_lun; retval = 0; retval = sbuf_printf(sb, "\t"); if (retval != 0) goto bailout; retval = sbuf_printf(sb, "%d", lun->num_threads); if (retval != 0) goto bailout; retval = sbuf_printf(sb, "\n"); bailout: return (retval); } static uint64_t ctl_be_block_lun_attr(void *be_lun, const char *attrname) { struct ctl_be_block_lun *lun = (struct ctl_be_block_lun *)be_lun; if (lun->getattr == NULL) return (UINT64_MAX); return (lun->getattr(lun, attrname)); } int ctl_be_block_init(void) { struct ctl_be_block_softc *softc; int retval; softc = &backend_block_softc; retval = 0; mtx_init(&softc->lock, "ctlblock", NULL, MTX_DEF); beio_zone = uma_zcreate("beio", sizeof(struct ctl_be_block_io), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); STAILQ_INIT(&softc->disk_list); STAILQ_INIT(&softc->lun_list); return (retval); } Index: projects/building-blocks/sys/cam/ctl/ctl_backend_ramdisk.c =================================================================== --- projects/building-blocks/sys/cam/ctl/ctl_backend_ramdisk.c (revision 278776) +++ projects/building-blocks/sys/cam/ctl/ctl_backend_ramdisk.c (revision 278777) @@ -1,997 +1,997 @@ /*- * Copyright (c) 2003, 2008 Silicon Graphics International Corp. * Copyright (c) 2012 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Edward Tomasz Napierala * under sponsorship from the FreeBSD Foundation. * * 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, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * substantially similar to the "NO WARRANTY" disclaimer below * ("Disclaimer") and any redistribution must be conditioned upon * including a substantially similar Disclaimer requirement for further * binary redistribution. * * NO WARRANTY * 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 MERCHANTIBILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. * * $Id: //depot/users/kenm/FreeBSD-test2/sys/cam/ctl/ctl_backend_ramdisk.c#3 $ */ /* * CAM Target Layer backend for a "fake" ramdisk. * * Author: Ken Merry */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef enum { CTL_BE_RAMDISK_LUN_UNCONFIGURED = 0x01, CTL_BE_RAMDISK_LUN_CONFIG_ERR = 0x02, CTL_BE_RAMDISK_LUN_WAITING = 0x04 } ctl_be_ramdisk_lun_flags; struct ctl_be_ramdisk_lun { char lunname[32]; uint64_t size_bytes; uint64_t size_blocks; struct ctl_be_ramdisk_softc *softc; ctl_be_ramdisk_lun_flags flags; STAILQ_ENTRY(ctl_be_ramdisk_lun) links; struct ctl_be_lun ctl_be_lun; struct taskqueue *io_taskqueue; struct task io_task; STAILQ_HEAD(, ctl_io_hdr) cont_queue; struct mtx_padalign queue_lock; }; struct ctl_be_ramdisk_softc { struct mtx lock; int rd_size; #ifdef CTL_RAMDISK_PAGES uint8_t **ramdisk_pages; int num_pages; #else uint8_t *ramdisk_buffer; #endif int num_luns; STAILQ_HEAD(, ctl_be_ramdisk_lun) lun_list; }; static struct ctl_be_ramdisk_softc rd_softc; int ctl_backend_ramdisk_init(void); void ctl_backend_ramdisk_shutdown(void); static int ctl_backend_ramdisk_move_done(union ctl_io *io); static int ctl_backend_ramdisk_submit(union ctl_io *io); static void ctl_backend_ramdisk_continue(union ctl_io *io); static int ctl_backend_ramdisk_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td); static int ctl_backend_ramdisk_rm(struct ctl_be_ramdisk_softc *softc, struct ctl_lun_req *req); static int ctl_backend_ramdisk_create(struct ctl_be_ramdisk_softc *softc, struct ctl_lun_req *req, int do_wait); static int ctl_backend_ramdisk_modify(struct ctl_be_ramdisk_softc *softc, struct ctl_lun_req *req); static void ctl_backend_ramdisk_worker(void *context, int pending); static void ctl_backend_ramdisk_lun_shutdown(void *be_lun); static void ctl_backend_ramdisk_lun_config_status(void *be_lun, ctl_lun_config_status status); static int ctl_backend_ramdisk_config_write(union ctl_io *io); static int ctl_backend_ramdisk_config_read(union ctl_io *io); static struct ctl_backend_driver ctl_be_ramdisk_driver = { .name = "ramdisk", .flags = CTL_BE_FLAG_HAS_CONFIG, .init = ctl_backend_ramdisk_init, .data_submit = ctl_backend_ramdisk_submit, .data_move_done = ctl_backend_ramdisk_move_done, .config_read = ctl_backend_ramdisk_config_read, .config_write = ctl_backend_ramdisk_config_write, .ioctl = ctl_backend_ramdisk_ioctl }; MALLOC_DEFINE(M_RAMDISK, "ramdisk", "Memory used for CTL RAMdisk"); CTL_BACKEND_DECLARE(cbr, ctl_be_ramdisk_driver); int ctl_backend_ramdisk_init(void) { struct ctl_be_ramdisk_softc *softc; #ifdef CTL_RAMDISK_PAGES int i; #endif softc = &rd_softc; memset(softc, 0, sizeof(*softc)); mtx_init(&softc->lock, "ctlramdisk", NULL, MTX_DEF); STAILQ_INIT(&softc->lun_list); softc->rd_size = 1024 * 1024; #ifdef CTL_RAMDISK_PAGES softc->num_pages = softc->rd_size / PAGE_SIZE; softc->ramdisk_pages = (uint8_t **)malloc(sizeof(uint8_t *) * softc->num_pages, M_RAMDISK, M_WAITOK); for (i = 0; i < softc->num_pages; i++) softc->ramdisk_pages[i] = malloc(PAGE_SIZE, M_RAMDISK,M_WAITOK); #else softc->ramdisk_buffer = (uint8_t *)malloc(softc->rd_size, M_RAMDISK, M_WAITOK); #endif return (0); } void ctl_backend_ramdisk_shutdown(void) { struct ctl_be_ramdisk_softc *softc; struct ctl_be_ramdisk_lun *lun, *next_lun; #ifdef CTL_RAMDISK_PAGES int i; #endif softc = &rd_softc; mtx_lock(&softc->lock); for (lun = STAILQ_FIRST(&softc->lun_list); lun != NULL; lun = next_lun){ /* * Grab the next LUN. The current LUN may get removed by * ctl_invalidate_lun(), which will call our LUN shutdown * routine, if there is no outstanding I/O for this LUN. */ next_lun = STAILQ_NEXT(lun, links); /* * Drop our lock here. Since ctl_invalidate_lun() can call * back into us, this could potentially lead to a recursive * lock of the same mutex, which would cause a hang. */ mtx_unlock(&softc->lock); ctl_disable_lun(&lun->ctl_be_lun); ctl_invalidate_lun(&lun->ctl_be_lun); mtx_lock(&softc->lock); } mtx_unlock(&softc->lock); #ifdef CTL_RAMDISK_PAGES for (i = 0; i < softc->num_pages; i++) free(softc->ramdisk_pages[i], M_RAMDISK); free(softc->ramdisk_pages, M_RAMDISK); #else free(softc->ramdisk_buffer, M_RAMDISK); #endif if (ctl_backend_deregister(&ctl_be_ramdisk_driver) != 0) { printf("ctl_backend_ramdisk_shutdown: " "ctl_backend_deregister() failed!\n"); } } static int ctl_backend_ramdisk_move_done(union ctl_io *io) { struct ctl_be_lun *ctl_be_lun; struct ctl_be_ramdisk_lun *be_lun; #ifdef CTL_TIME_IO struct bintime cur_bt; #endif CTL_DEBUG_PRINT(("ctl_backend_ramdisk_move_done\n")); ctl_be_lun = (struct ctl_be_lun *)io->io_hdr.ctl_private[ CTL_PRIV_BACKEND_LUN].ptr; be_lun = (struct ctl_be_ramdisk_lun *)ctl_be_lun->be_lun; #ifdef CTL_TIME_IO getbintime(&cur_bt); bintime_sub(&cur_bt, &io->io_hdr.dma_start_bt); bintime_add(&io->io_hdr.dma_bt, &cur_bt); io->io_hdr.num_dmas++; #endif if (io->scsiio.kern_sg_entries > 0) free(io->scsiio.kern_data_ptr, M_RAMDISK); io->scsiio.kern_rel_offset += io->scsiio.kern_data_len; if (io->io_hdr.flags & CTL_FLAG_ABORT) { ; } else if ((io->io_hdr.port_status == 0) && ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_STATUS_NONE)) { if (io->io_hdr.ctl_private[CTL_PRIV_BACKEND].integer > 0) { mtx_lock(&be_lun->queue_lock); STAILQ_INSERT_TAIL(&be_lun->cont_queue, &io->io_hdr, links); mtx_unlock(&be_lun->queue_lock); taskqueue_enqueue(be_lun->io_taskqueue, &be_lun->io_task); return (0); } ctl_set_success(&io->scsiio); } else if ((io->io_hdr.port_status != 0) && ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_STATUS_NONE || (io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS)) { /* * For hardware error sense keys, the sense key * specific value is defined to be a retry count, * but we use it to pass back an internal FETD * error code. XXX KDM Hopefully the FETD is only * using 16 bits for an error code, since that's * all the space we have in the sks field. */ ctl_set_internal_failure(&io->scsiio, /*sks_valid*/ 1, /*retry_count*/ io->io_hdr.port_status); } ctl_data_submit_done(io); return(0); } static int ctl_backend_ramdisk_submit(union ctl_io *io) { struct ctl_be_lun *ctl_be_lun; struct ctl_lba_len_flags *lbalen; ctl_be_lun = (struct ctl_be_lun *)io->io_hdr.ctl_private[ CTL_PRIV_BACKEND_LUN].ptr; lbalen = (struct ctl_lba_len_flags *)&io->io_hdr.ctl_private[CTL_PRIV_LBA_LEN]; if (lbalen->flags & CTL_LLF_VERIFY) { ctl_set_success(&io->scsiio); ctl_data_submit_done(io); return (CTL_RETVAL_COMPLETE); } io->io_hdr.ctl_private[CTL_PRIV_BACKEND].integer = lbalen->len * ctl_be_lun->blocksize; ctl_backend_ramdisk_continue(io); return (CTL_RETVAL_COMPLETE); } static void ctl_backend_ramdisk_continue(union ctl_io *io) { struct ctl_be_ramdisk_softc *softc; int len, len_filled, sg_filled; #ifdef CTL_RAMDISK_PAGES struct ctl_sg_entry *sg_entries; int i; #endif softc = &rd_softc; len = io->io_hdr.ctl_private[CTL_PRIV_BACKEND].integer; #ifdef CTL_RAMDISK_PAGES sg_filled = min(btoc(len), softc->num_pages); if (sg_filled > 1) { io->scsiio.kern_data_ptr = malloc(sizeof(struct ctl_sg_entry) * sg_filled, M_RAMDISK, M_WAITOK); sg_entries = (struct ctl_sg_entry *)io->scsiio.kern_data_ptr; for (i = 0, len_filled = 0; i < sg_filled; i++) { sg_entries[i].addr = softc->ramdisk_pages[i]; sg_entries[i].len = MIN(PAGE_SIZE, len - len_filled); len_filled += sg_entries[i].len; } io->io_hdr.flags |= CTL_FLAG_KDPTR_SGLIST; } else { sg_filled = 0; len_filled = len; io->scsiio.kern_data_ptr = softc->ramdisk_pages[0]; } #else sg_filled = 0; len_filled = min(len, softc->rd_size); io->scsiio.kern_data_ptr = softc->ramdisk_buffer; #endif /* CTL_RAMDISK_PAGES */ io->scsiio.be_move_done = ctl_backend_ramdisk_move_done; io->scsiio.kern_data_resid = 0; io->scsiio.kern_data_len = len_filled; io->scsiio.kern_sg_entries = sg_filled; io->io_hdr.flags |= CTL_FLAG_ALLOCATED; io->io_hdr.ctl_private[CTL_PRIV_BACKEND].integer -= len_filled; #ifdef CTL_TIME_IO getbintime(&io->io_hdr.dma_start_bt); #endif ctl_datamove(io); } static void ctl_backend_ramdisk_worker(void *context, int pending) { struct ctl_be_ramdisk_softc *softc; struct ctl_be_ramdisk_lun *be_lun; union ctl_io *io; be_lun = (struct ctl_be_ramdisk_lun *)context; softc = be_lun->softc; mtx_lock(&be_lun->queue_lock); for (;;) { io = (union ctl_io *)STAILQ_FIRST(&be_lun->cont_queue); if (io != NULL) { STAILQ_REMOVE(&be_lun->cont_queue, &io->io_hdr, ctl_io_hdr, links); mtx_unlock(&be_lun->queue_lock); ctl_backend_ramdisk_continue(io); mtx_lock(&be_lun->queue_lock); continue; } /* * If we get here, there is no work left in the queues, so * just break out and let the task queue go to sleep. */ break; } mtx_unlock(&be_lun->queue_lock); } static int ctl_backend_ramdisk_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td) { struct ctl_be_ramdisk_softc *softc; int retval; retval = 0; softc = &rd_softc; switch (cmd) { case CTL_LUN_REQ: { struct ctl_lun_req *lun_req; lun_req = (struct ctl_lun_req *)addr; switch (lun_req->reqtype) { case CTL_LUNREQ_CREATE: retval = ctl_backend_ramdisk_create(softc, lun_req, /*do_wait*/ 1); break; case CTL_LUNREQ_RM: retval = ctl_backend_ramdisk_rm(softc, lun_req); break; case CTL_LUNREQ_MODIFY: retval = ctl_backend_ramdisk_modify(softc, lun_req); break; default: lun_req->status = CTL_LUN_ERROR; snprintf(lun_req->error_str, sizeof(lun_req->error_str), "%s: invalid LUN request type %d", __func__, lun_req->reqtype); break; } break; } default: retval = ENOTTY; break; } return (retval); } static int ctl_backend_ramdisk_rm(struct ctl_be_ramdisk_softc *softc, struct ctl_lun_req *req) { struct ctl_be_ramdisk_lun *be_lun; struct ctl_lun_rm_params *params; int retval; retval = 0; params = &req->reqdata.rm; be_lun = NULL; mtx_lock(&softc->lock); STAILQ_FOREACH(be_lun, &softc->lun_list, links) { if (be_lun->ctl_be_lun.lun_id == params->lun_id) break; } mtx_unlock(&softc->lock); if (be_lun == NULL) { snprintf(req->error_str, sizeof(req->error_str), "%s: LUN %u is not managed by the ramdisk backend", __func__, params->lun_id); goto bailout_error; } retval = ctl_disable_lun(&be_lun->ctl_be_lun); if (retval != 0) { snprintf(req->error_str, sizeof(req->error_str), "%s: error %d returned from ctl_disable_lun() for " "LUN %d", __func__, retval, params->lun_id); goto bailout_error; } /* * Set the waiting flag before we invalidate the LUN. Our shutdown * routine can be called any time after we invalidate the LUN, * and can be called from our context. * * This tells the shutdown routine that we're waiting, or we're * going to wait for the shutdown to happen. */ mtx_lock(&softc->lock); be_lun->flags |= CTL_BE_RAMDISK_LUN_WAITING; mtx_unlock(&softc->lock); retval = ctl_invalidate_lun(&be_lun->ctl_be_lun); if (retval != 0) { snprintf(req->error_str, sizeof(req->error_str), "%s: error %d returned from ctl_invalidate_lun() for " "LUN %d", __func__, retval, params->lun_id); mtx_lock(&softc->lock); be_lun->flags &= ~CTL_BE_RAMDISK_LUN_WAITING; mtx_unlock(&softc->lock); goto bailout_error; } mtx_lock(&softc->lock); while ((be_lun->flags & CTL_BE_RAMDISK_LUN_UNCONFIGURED) == 0) { retval = msleep(be_lun, &softc->lock, PCATCH, "ctlram", 0); if (retval == EINTR) break; } be_lun->flags &= ~CTL_BE_RAMDISK_LUN_WAITING; /* * We only remove this LUN from the list and free it (below) if * retval == 0. If the user interrupted the wait, we just bail out * without actually freeing the LUN. We let the shutdown routine * free the LUN if that happens. */ if (retval == 0) { STAILQ_REMOVE(&softc->lun_list, be_lun, ctl_be_ramdisk_lun, links); softc->num_luns--; } mtx_unlock(&softc->lock); if (retval == 0) { taskqueue_drain(be_lun->io_taskqueue, &be_lun->io_task); taskqueue_free(be_lun->io_taskqueue); ctl_free_opts(&be_lun->ctl_be_lun.options); mtx_destroy(&be_lun->queue_lock); free(be_lun, M_RAMDISK); } req->status = CTL_LUN_OK; return (retval); bailout_error: req->status = CTL_LUN_ERROR; return (0); } static int ctl_backend_ramdisk_create(struct ctl_be_ramdisk_softc *softc, struct ctl_lun_req *req, int do_wait) { struct ctl_be_ramdisk_lun *be_lun; struct ctl_lun_create_params *params; uint32_t blocksize; char *value; char tmpstr[32]; int retval, unmap; retval = 0; params = &req->reqdata.create; if (params->blocksize_bytes != 0) blocksize = params->blocksize_bytes; else blocksize = 512; be_lun = malloc(sizeof(*be_lun), M_RAMDISK, M_ZERO | (do_wait ? M_WAITOK : M_NOWAIT)); if (be_lun == NULL) { snprintf(req->error_str, sizeof(req->error_str), "%s: error allocating %zd bytes", __func__, sizeof(*be_lun)); goto bailout_error; } sprintf(be_lun->lunname, "cram%d", softc->num_luns); ctl_init_opts(&be_lun->ctl_be_lun.options, req->num_be_args, req->kern_be_args); if (params->flags & CTL_LUN_FLAG_DEV_TYPE) be_lun->ctl_be_lun.lun_type = params->device_type; else be_lun->ctl_be_lun.lun_type = T_DIRECT; if (be_lun->ctl_be_lun.lun_type == T_DIRECT) { if (params->lun_size_bytes < blocksize) { snprintf(req->error_str, sizeof(req->error_str), "%s: LUN size %ju < blocksize %u", __func__, params->lun_size_bytes, blocksize); goto bailout_error; } be_lun->size_blocks = params->lun_size_bytes / blocksize; be_lun->size_bytes = be_lun->size_blocks * blocksize; be_lun->ctl_be_lun.maxlba = be_lun->size_blocks - 1; be_lun->ctl_be_lun.atomicblock = UINT32_MAX; be_lun->ctl_be_lun.opttxferlen = softc->rd_size / blocksize; } else { be_lun->ctl_be_lun.maxlba = 0; blocksize = 0; be_lun->size_bytes = 0; be_lun->size_blocks = 0; } be_lun->ctl_be_lun.blocksize = blocksize; /* Tell the user the blocksize we ended up using */ params->blocksize_bytes = blocksize; /* Tell the user the exact size we ended up using */ params->lun_size_bytes = be_lun->size_bytes; be_lun->softc = softc; - unmap = 0; + unmap = 1; value = ctl_get_opt(&be_lun->ctl_be_lun.options, "unmap"); if (value != NULL && strcmp(value, "on") == 0) - unmap = 1; + unmap = (strcmp(value, "on") == 0); be_lun->flags = CTL_BE_RAMDISK_LUN_UNCONFIGURED; be_lun->ctl_be_lun.flags = CTL_LUN_FLAG_PRIMARY; if (unmap) be_lun->ctl_be_lun.flags |= CTL_LUN_FLAG_UNMAP; be_lun->ctl_be_lun.be_lun = be_lun; if (params->flags & CTL_LUN_FLAG_ID_REQ) { be_lun->ctl_be_lun.req_lun_id = params->req_lun_id; be_lun->ctl_be_lun.flags |= CTL_LUN_FLAG_ID_REQ; } else be_lun->ctl_be_lun.req_lun_id = 0; be_lun->ctl_be_lun.lun_shutdown = ctl_backend_ramdisk_lun_shutdown; be_lun->ctl_be_lun.lun_config_status = ctl_backend_ramdisk_lun_config_status; be_lun->ctl_be_lun.be = &ctl_be_ramdisk_driver; if ((params->flags & CTL_LUN_FLAG_SERIAL_NUM) == 0) { snprintf(tmpstr, sizeof(tmpstr), "MYSERIAL%4d", softc->num_luns); strncpy((char *)be_lun->ctl_be_lun.serial_num, tmpstr, MIN(sizeof(be_lun->ctl_be_lun.serial_num), sizeof(tmpstr))); /* Tell the user what we used for a serial number */ strncpy((char *)params->serial_num, tmpstr, MIN(sizeof(params->serial_num), sizeof(tmpstr))); } else { strncpy((char *)be_lun->ctl_be_lun.serial_num, params->serial_num, MIN(sizeof(be_lun->ctl_be_lun.serial_num), sizeof(params->serial_num))); } if ((params->flags & CTL_LUN_FLAG_DEVID) == 0) { snprintf(tmpstr, sizeof(tmpstr), "MYDEVID%4d", softc->num_luns); strncpy((char *)be_lun->ctl_be_lun.device_id, tmpstr, MIN(sizeof(be_lun->ctl_be_lun.device_id), sizeof(tmpstr))); /* Tell the user what we used for a device ID */ strncpy((char *)params->device_id, tmpstr, MIN(sizeof(params->device_id), sizeof(tmpstr))); } else { strncpy((char *)be_lun->ctl_be_lun.device_id, params->device_id, MIN(sizeof(be_lun->ctl_be_lun.device_id), sizeof(params->device_id))); } STAILQ_INIT(&be_lun->cont_queue); mtx_init(&be_lun->queue_lock, "cram queue lock", NULL, MTX_DEF); TASK_INIT(&be_lun->io_task, /*priority*/0, ctl_backend_ramdisk_worker, be_lun); be_lun->io_taskqueue = taskqueue_create(be_lun->lunname, M_WAITOK, taskqueue_thread_enqueue, /*context*/&be_lun->io_taskqueue); if (be_lun->io_taskqueue == NULL) { snprintf(req->error_str, sizeof(req->error_str), "%s: Unable to create taskqueue", __func__); goto bailout_error; } retval = taskqueue_start_threads(&be_lun->io_taskqueue, /*num threads*/1, /*priority*/PWAIT, /*thread name*/ "%s taskq", be_lun->lunname); if (retval != 0) goto bailout_error; mtx_lock(&softc->lock); softc->num_luns++; STAILQ_INSERT_TAIL(&softc->lun_list, be_lun, links); mtx_unlock(&softc->lock); retval = ctl_add_lun(&be_lun->ctl_be_lun); if (retval != 0) { mtx_lock(&softc->lock); STAILQ_REMOVE(&softc->lun_list, be_lun, ctl_be_ramdisk_lun, links); softc->num_luns--; mtx_unlock(&softc->lock); snprintf(req->error_str, sizeof(req->error_str), "%s: ctl_add_lun() returned error %d, see dmesg for " "details", __func__, retval); retval = 0; goto bailout_error; } if (do_wait == 0) return (retval); mtx_lock(&softc->lock); /* * Tell the config_status routine that we're waiting so it won't * clean up the LUN in the event of an error. */ be_lun->flags |= CTL_BE_RAMDISK_LUN_WAITING; while (be_lun->flags & CTL_BE_RAMDISK_LUN_UNCONFIGURED) { retval = msleep(be_lun, &softc->lock, PCATCH, "ctlram", 0); if (retval == EINTR) break; } be_lun->flags &= ~CTL_BE_RAMDISK_LUN_WAITING; if (be_lun->flags & CTL_BE_RAMDISK_LUN_CONFIG_ERR) { snprintf(req->error_str, sizeof(req->error_str), "%s: LUN configuration error, see dmesg for details", __func__); STAILQ_REMOVE(&softc->lun_list, be_lun, ctl_be_ramdisk_lun, links); softc->num_luns--; mtx_unlock(&softc->lock); goto bailout_error; } else { params->req_lun_id = be_lun->ctl_be_lun.lun_id; } mtx_unlock(&softc->lock); req->status = CTL_LUN_OK; return (retval); bailout_error: req->status = CTL_LUN_ERROR; if (be_lun != NULL) { if (be_lun->io_taskqueue != NULL) { taskqueue_free(be_lun->io_taskqueue); } ctl_free_opts(&be_lun->ctl_be_lun.options); mtx_destroy(&be_lun->queue_lock); free(be_lun, M_RAMDISK); } return (retval); } static int ctl_backend_ramdisk_modify(struct ctl_be_ramdisk_softc *softc, struct ctl_lun_req *req) { struct ctl_be_ramdisk_lun *be_lun; struct ctl_lun_modify_params *params; uint32_t blocksize; params = &req->reqdata.modify; be_lun = NULL; mtx_lock(&softc->lock); STAILQ_FOREACH(be_lun, &softc->lun_list, links) { if (be_lun->ctl_be_lun.lun_id == params->lun_id) break; } mtx_unlock(&softc->lock); if (be_lun == NULL) { snprintf(req->error_str, sizeof(req->error_str), "%s: LUN %u is not managed by the ramdisk backend", __func__, params->lun_id); goto bailout_error; } if (params->lun_size_bytes == 0) { snprintf(req->error_str, sizeof(req->error_str), "%s: LUN size \"auto\" not supported " "by the ramdisk backend", __func__); goto bailout_error; } blocksize = be_lun->ctl_be_lun.blocksize; if (params->lun_size_bytes < blocksize) { snprintf(req->error_str, sizeof(req->error_str), "%s: LUN size %ju < blocksize %u", __func__, params->lun_size_bytes, blocksize); goto bailout_error; } be_lun->size_blocks = params->lun_size_bytes / blocksize; be_lun->size_bytes = be_lun->size_blocks * blocksize; /* * The maximum LBA is the size - 1. * * XXX: Note that this field is being updated without locking, * which might cause problems on 32-bit architectures. */ be_lun->ctl_be_lun.maxlba = be_lun->size_blocks - 1; ctl_lun_capacity_changed(&be_lun->ctl_be_lun); /* Tell the user the exact size we ended up using */ params->lun_size_bytes = be_lun->size_bytes; req->status = CTL_LUN_OK; return (0); bailout_error: req->status = CTL_LUN_ERROR; return (0); } static void ctl_backend_ramdisk_lun_shutdown(void *be_lun) { struct ctl_be_ramdisk_lun *lun; struct ctl_be_ramdisk_softc *softc; int do_free; lun = (struct ctl_be_ramdisk_lun *)be_lun; softc = lun->softc; do_free = 0; mtx_lock(&softc->lock); lun->flags |= CTL_BE_RAMDISK_LUN_UNCONFIGURED; if (lun->flags & CTL_BE_RAMDISK_LUN_WAITING) { wakeup(lun); } else { STAILQ_REMOVE(&softc->lun_list, lun, ctl_be_ramdisk_lun, links); softc->num_luns--; do_free = 1; } mtx_unlock(&softc->lock); if (do_free != 0) free(be_lun, M_RAMDISK); } static void ctl_backend_ramdisk_lun_config_status(void *be_lun, ctl_lun_config_status status) { struct ctl_be_ramdisk_lun *lun; struct ctl_be_ramdisk_softc *softc; lun = (struct ctl_be_ramdisk_lun *)be_lun; softc = lun->softc; if (status == CTL_LUN_CONFIG_OK) { mtx_lock(&softc->lock); lun->flags &= ~CTL_BE_RAMDISK_LUN_UNCONFIGURED; if (lun->flags & CTL_BE_RAMDISK_LUN_WAITING) wakeup(lun); mtx_unlock(&softc->lock); /* * We successfully added the LUN, attempt to enable it. */ if (ctl_enable_lun(&lun->ctl_be_lun) != 0) { printf("%s: ctl_enable_lun() failed!\n", __func__); if (ctl_invalidate_lun(&lun->ctl_be_lun) != 0) { printf("%s: ctl_invalidate_lun() failed!\n", __func__); } } return; } mtx_lock(&softc->lock); lun->flags &= ~CTL_BE_RAMDISK_LUN_UNCONFIGURED; /* * If we have a user waiting, let him handle the cleanup. If not, * clean things up here. */ if (lun->flags & CTL_BE_RAMDISK_LUN_WAITING) { lun->flags |= CTL_BE_RAMDISK_LUN_CONFIG_ERR; wakeup(lun); } else { STAILQ_REMOVE(&softc->lun_list, lun, ctl_be_ramdisk_lun, links); softc->num_luns--; free(lun, M_RAMDISK); } mtx_unlock(&softc->lock); } static int ctl_backend_ramdisk_config_write(union ctl_io *io) { struct ctl_be_ramdisk_softc *softc; int retval; retval = 0; softc = &rd_softc; switch (io->scsiio.cdb[0]) { case SYNCHRONIZE_CACHE: case SYNCHRONIZE_CACHE_16: /* * The upper level CTL code will filter out any CDBs with * the immediate bit set and return the proper error. It * will also not allow a sync cache command to go to a LUN * that is powered down. * * We don't really need to worry about what LBA range the * user asked to be synced out. When they issue a sync * cache command, we'll sync out the whole thing. * * This is obviously just a stubbed out implementation. * The real implementation will be in the RAIDCore/CTL * interface, and can only really happen when RAIDCore * implements a per-array cache sync. */ ctl_set_success(&io->scsiio); ctl_config_write_done(io); break; case START_STOP_UNIT: { struct scsi_start_stop_unit *cdb; struct ctl_be_lun *ctl_be_lun; struct ctl_be_ramdisk_lun *be_lun; cdb = (struct scsi_start_stop_unit *)io->scsiio.cdb; ctl_be_lun = (struct ctl_be_lun *)io->io_hdr.ctl_private[ CTL_PRIV_BACKEND_LUN].ptr; be_lun = (struct ctl_be_ramdisk_lun *)ctl_be_lun->be_lun; if (cdb->how & SSS_START) retval = ctl_start_lun(ctl_be_lun); else { retval = ctl_stop_lun(ctl_be_lun); #ifdef NEEDTOPORT if ((retval == 0) && (cdb->byte2 & SSS_ONOFFLINE)) retval = ctl_lun_offline(ctl_be_lun); #endif } /* * In general, the above routines should not fail. They * just set state for the LUN. So we've got something * pretty wrong here if we can't start or stop the LUN. */ if (retval != 0) { ctl_set_internal_failure(&io->scsiio, /*sks_valid*/ 1, /*retry_count*/ 0xf051); retval = CTL_RETVAL_COMPLETE; } else { ctl_set_success(&io->scsiio); } ctl_config_write_done(io); break; } case WRITE_SAME_10: case WRITE_SAME_16: case UNMAP: ctl_set_success(&io->scsiio); ctl_config_write_done(io); break; default: ctl_set_invalid_opcode(&io->scsiio); ctl_config_write_done(io); retval = CTL_RETVAL_COMPLETE; break; } return (retval); } static int ctl_backend_ramdisk_config_read(union ctl_io *io) { int retval = 0; switch (io->scsiio.cdb[0]) { case SERVICE_ACTION_IN: if (io->scsiio.cdb[1] == SGLS_SERVICE_ACTION) { /* We have nothing to tell, leave default data. */ ctl_config_read_done(io); retval = CTL_RETVAL_COMPLETE; break; } ctl_set_invalid_field(&io->scsiio, /*sks_valid*/ 1, /*command*/ 1, /*field*/ 1, /*bit_valid*/ 1, /*bit*/ 4); ctl_config_read_done(io); retval = CTL_RETVAL_COMPLETE; break; default: ctl_set_invalid_opcode(&io->scsiio); ctl_config_read_done(io); retval = CTL_RETVAL_COMPLETE; break; } return (retval); } Index: projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300.h =================================================================== --- projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300.h (revision 278776) +++ projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300.h (revision 278777) @@ -1,1690 +1,1715 @@ /* * Copyright (c) 2013 Qualcomm Atheros, Inc. * * 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 _ATH_AR9300_H_ #define _ATH_AR9300_H_ #include "ar9300_freebsd_inc.h" #define AH_BIG_ENDIAN 4321 #define AH_LITTLE_ENDIAN 1234 #if _BYTE_ORDER == _BIG_ENDIAN #define AH_BYTE_ORDER AH_BIG_ENDIAN #else #define AH_BYTE_ORDER AH_LITTLE_ENDIAN #endif /* XXX doesn't belong here */ #define AR_EEPROM_MODAL_SPURS 5 /* * (a) this should be N(a), * (b) FreeBSD does define nitems, * (c) it doesn't have an AH_ prefix, sigh. */ #define ARRAY_LENGTH(a) (sizeof(a) / sizeof((a)[0])) #include "ah_internal.h" #include "ah_eeprom.h" #include "ah_devid.h" #include "ar9300eep.h" /* For Eeprom definitions */ #define AR9300_MAGIC 0x19741014 /* MAC register values */ #define INIT_CONFIG_STATUS 0x00000000 #define INIT_RSSI_THR 0x7 /* Missed beacon counter initialized to 0x7 (max is 0xff) */ #define INIT_RSSI_BEACON_WEIGHT 8 /* ave beacon rssi weight (0-16) */ /* * Various fifo fill before Tx start, in 64-byte units * i.e. put the frame in the air while still DMAing */ #define MIN_TX_FIFO_THRESHOLD 0x1 #define MAX_TX_FIFO_THRESHOLD (( 4096 / 64) - 1) #define INIT_TX_FIFO_THRESHOLD MIN_TX_FIFO_THRESHOLD #define CHANSEL_DIV 15 #define FCLK 40 #define COEFF ((FCLK * 5) / 2) #define CHANSEL_2G(_freq) (((_freq) * 0x10000) / CHANSEL_DIV) #define CHANSEL_5G(_freq) (((_freq) * 0x8000) / CHANSEL_DIV) #define CHANSEL_5G_DOT5MHZ 2188 /* * Receive Queue Fifo depth. */ enum RX_FIFO_DEPTH { HAL_HP_RXFIFO_DEPTH = 16, HAL_LP_RXFIFO_DEPTH = 128, }; /* * Gain support. */ #define NUM_CORNER_FIX_BITS_2133 7 #define CCK_OFDM_GAIN_DELTA 15 enum GAIN_PARAMS { GP_TXCLIP, GP_PD90, GP_PD84, GP_GSEL }; enum GAIN_PARAMS_2133 { GP_MIXGAIN_OVR, GP_PWD_138, GP_PWD_137, GP_PWD_136, GP_PWD_132, GP_PWD_131, GP_PWD_130, }; enum { HAL_RESET_POWER_ON, HAL_RESET_WARM, HAL_RESET_COLD, }; typedef struct _gain_opt_step { int16_t paramVal[NUM_CORNER_FIX_BITS_2133]; int32_t stepGain; int8_t stepName[16]; } GAIN_OPTIMIZATION_STEP; typedef struct { u_int32_t numStepsInLadder; u_int32_t defaultStepNum; GAIN_OPTIMIZATION_STEP optStep[10]; } GAIN_OPTIMIZATION_LADDER; typedef struct { u_int32_t currStepNum; u_int32_t currGain; u_int32_t targetGain; u_int32_t loTrig; u_int32_t hiTrig; u_int32_t gainFCorrection; u_int32_t active; GAIN_OPTIMIZATION_STEP *curr_step; } GAIN_VALUES; typedef struct { u_int16_t synth_center; u_int16_t ctl_center; u_int16_t ext_center; } CHAN_CENTERS; /* RF HAL structures */ typedef struct rf_hal_funcs { HAL_BOOL (*set_channel)(struct ath_hal *, struct ieee80211_channel *); HAL_BOOL (*get_chip_power_lim)(struct ath_hal *ah, struct ieee80211_channel *chan); } RF_HAL_FUNCS; struct ar9300_ani_default { u_int16_t m1_thresh_low; u_int16_t m2_thresh_low; u_int16_t m1_thresh; u_int16_t m2_thresh; u_int16_t m2_count_thr; u_int16_t m2_count_thr_low; u_int16_t m1_thresh_low_ext; u_int16_t m2_thresh_low_ext; u_int16_t m1_thresh_ext; u_int16_t m2_thresh_ext; u_int16_t firstep; u_int16_t firstep_low; u_int16_t cycpwr_thr1; u_int16_t cycpwr_thr1_ext; }; /* * Per-channel ANI state private to the driver. */ struct ar9300_ani_state { struct ieee80211_channel c; /* XXX ew? */ HAL_BOOL must_restore; HAL_BOOL ofdms_turn; u_int8_t ofdm_noise_immunity_level; u_int8_t cck_noise_immunity_level; u_int8_t spur_immunity_level; u_int8_t firstep_level; u_int8_t ofdm_weak_sig_detect_off; u_int8_t mrc_cck_off; /* Thresholds */ u_int32_t listen_time; u_int32_t ofdm_trig_high; u_int32_t ofdm_trig_low; int32_t cck_trig_high; int32_t cck_trig_low; int32_t rssi_thr_low; int32_t rssi_thr_high; int32_t rssi; /* The current RSSI */ u_int32_t tx_frame_count; /* Last tx_frame_count */ u_int32_t rx_frame_count; /* Last rx Frame count */ u_int32_t cycle_count; /* Last cycle_count (can detect wrap-around) */ u_int32_t ofdm_phy_err_count;/* OFDM err count since last reset */ u_int32_t cck_phy_err_count; /* CCK err count since last reset */ struct ar9300_ani_default ini_def; /* INI default values for ANI registers */ HAL_BOOL phy_noise_spur; /* based on OFDM/CCK Phy errors */ }; #define AR9300_ANI_POLLINTERVAL 1000 /* 1000 milliseconds between ANI poll */ #define AR9300_CHANNEL_SWITCH_TIME_USEC 1000 /* 1 millisecond needed to change channels */ #define HAL_PROCESS_ANI 0x00000001 /* ANI state setup */ #define HAL_RADAR_EN 0x80000000 /* Radar detect is capable */ #define HAL_AR_EN 0x40000000 /* AR detect is capable */ #define DO_ANI(ah) \ ((AH9300(ah)->ah_proc_phy_err & HAL_PROCESS_ANI)) struct ar9300_stats { u_int32_t ast_ani_niup; /* ANI increased noise immunity */ u_int32_t ast_ani_nidown; /* ANI decreased noise immunity */ u_int32_t ast_ani_spurup; /* ANI increased spur immunity */ u_int32_t ast_ani_spurdown;/* ANI descreased spur immunity */ u_int32_t ast_ani_ofdmon; /* ANI OFDM weak signal detect on */ u_int32_t ast_ani_ofdmoff;/* ANI OFDM weak signal detect off */ u_int32_t ast_ani_cckhigh;/* ANI CCK weak signal threshold high */ u_int32_t ast_ani_ccklow; /* ANI CCK weak signal threshold low */ u_int32_t ast_ani_stepup; /* ANI increased first step level */ u_int32_t ast_ani_stepdown;/* ANI decreased first step level */ u_int32_t ast_ani_ofdmerrs;/* ANI cumulative ofdm phy err count */ u_int32_t ast_ani_cckerrs;/* ANI cumulative cck phy err count */ u_int32_t ast_ani_reset; /* ANI parameters zero'd for non-STA */ u_int32_t ast_ani_lzero; /* ANI listen time forced to zero */ u_int32_t ast_ani_lneg; /* ANI listen time calculated < 0 */ HAL_MIB_STATS ast_mibstats; /* MIB counter stats */ HAL_NODE_STATS ast_nodestats; /* Latest rssi stats from driver */ }; struct ar9300_rad_reader { u_int16_t rd_index; u_int16_t rd_expSeq; u_int32_t rd_resetVal; u_int8_t rd_start; }; struct ar9300_rad_writer { u_int16_t wr_index; u_int16_t wr_seq; }; struct ar9300_radar_event { u_int32_t re_ts; /* 32 bit time stamp */ u_int8_t re_rssi; /* rssi of radar event */ u_int8_t re_dur; /* duration of radar pulse */ u_int8_t re_chanIndex; /* Channel of event */ }; struct ar9300_radar_q_elem { u_int32_t rq_seqNum; u_int32_t rq_busy; /* 32 bit to insure atomic read/write */ struct ar9300_radar_event rq_event; /* Radar event */ }; struct ar9300_radar_q_info { u_int16_t ri_qsize; /* q size */ u_int16_t ri_seqSize; /* Size of sequence ring */ struct ar9300_rad_reader ri_reader; /* State for the q reader */ struct ar9300_rad_writer ri_writer; /* state for the q writer */ }; #define HAL_MAX_ACK_RADAR_DUR 511 #define HAL_MAX_NUM_PEAKS 3 #define HAL_ARQ_SIZE 4096 /* 8K AR events for buffer size */ #define HAL_ARQ_SEQSIZE 4097 /* Sequence counter wrap for AR */ #define HAL_RADARQ_SIZE 1024 /* 1K radar events for buffer size */ #define HAL_RADARQ_SEQSIZE 1025 /* Sequence counter wrap for radar */ #define HAL_NUMRADAR_STATES 64 /* Number of radar channels we keep state for */ struct ar9300_ar_state { u_int16_t ar_prev_time_stamp; u_int32_t ar_prev_width; u_int32_t ar_phy_err_count[HAL_MAX_ACK_RADAR_DUR]; u_int32_t ar_ack_sum; u_int16_t ar_peak_list[HAL_MAX_NUM_PEAKS]; u_int32_t ar_packet_threshold; /* Thresh to determine traffic load */ u_int32_t ar_par_threshold; /* Thresh to determine peak */ u_int32_t ar_radar_rssi; /* Rssi threshold for AR event */ }; struct ar9300_radar_state { struct ieee80211_channel *rs_chan; /* Channel info */ u_int8_t rs_chan_index; /* Channel index in radar structure */ u_int32_t rs_num_radar_events; /* Number of radar events */ int32_t rs_firpwr; /* Thresh to check radar sig is gone */ u_int32_t rs_radar_rssi; /* Thresh to start radar det (dB) */ u_int32_t rs_height; /* Thresh for pulse height (dB)*/ u_int32_t rs_pulse_rssi; /* Thresh to check if pulse is gone (dB) */ u_int32_t rs_inband; /* Thresh to check if pusle is inband (0.5 dB) */ }; typedef struct { u_int8_t uc_receiver_errors; u_int8_t uc_bad_tlp_errors; u_int8_t uc_bad_dllp_errors; u_int8_t uc_replay_timeout_errors; u_int8_t uc_replay_number_rollover_errors; } ar_pcie_error_moniter_counters; #define AR9300_OPFLAGS_11A 0x01 /* if set, allow 11a */ #define AR9300_OPFLAGS_11G 0x02 /* if set, allow 11g */ #define AR9300_OPFLAGS_N_5G_HT40 0x04 /* if set, disable 5G HT40 */ #define AR9300_OPFLAGS_N_2G_HT40 0x08 /* if set, disable 2G HT40 */ #define AR9300_OPFLAGS_N_5G_HT20 0x10 /* if set, disable 5G HT20 */ #define AR9300_OPFLAGS_N_2G_HT20 0x20 /* if set, disable 2G HT20 */ /* * For Kite and later chipsets, the following bits are not being programmed in EEPROM * and so need to be enabled always. * Bit 0: en_fcc_mid, Bit 1: en_jap_mid, Bit 2: en_fcc_dfs_ht40 * Bit 3: en_jap_ht40, Bit 4: en_jap_dfs_ht40 */ #define AR9300_RDEXT_DEFAULT 0x1F #define AR9300_MAX_CHAINS 3 #define AR9300_NUM_CHAINS(chainmask) \ (((chainmask >> 2) & 1) + ((chainmask >> 1) & 1) + (chainmask & 1)) #define AR9300_CHAIN0_MASK 0x1 #define AR9300_CHAIN1_MASK 0x2 #define AR9300_CHAIN2_MASK 0x4 /* Support for multiple INIs */ struct ar9300_ini_array { const u_int32_t *ia_array; u_int32_t ia_rows; u_int32_t ia_columns; }; #define INIT_INI_ARRAY(iniarray, array, rows, columns) do { \ (iniarray)->ia_array = (const u_int32_t *)(array); \ (iniarray)->ia_rows = (rows); \ (iniarray)->ia_columns = (columns); \ } while (0) #define INI_RA(iniarray, row, column) (((iniarray)->ia_array)[(row) * ((iniarray)->ia_columns) + (column)]) #define INIT_CAL(_perCal) \ (_perCal)->cal_state = CAL_WAITING; \ (_perCal)->cal_next = AH_NULL; #define INSERT_CAL(_ahp, _perCal) \ do { \ if ((_ahp)->ah_cal_list_last == AH_NULL) { \ (_ahp)->ah_cal_list = (_ahp)->ah_cal_list_last = (_perCal); \ ((_ahp)->ah_cal_list_last)->cal_next = (_perCal); \ } else { \ ((_ahp)->ah_cal_list_last)->cal_next = (_perCal); \ (_ahp)->ah_cal_list_last = (_perCal); \ (_perCal)->cal_next = (_ahp)->ah_cal_list; \ } \ } while (0) typedef enum cal_types { IQ_MISMATCH_CAL = 0x1, TEMP_COMP_CAL = 0x2, } HAL_CAL_TYPES; typedef enum cal_state { CAL_INACTIVE, CAL_WAITING, CAL_RUNNING, CAL_DONE } HAL_CAL_STATE; /* Calibrate state */ #define MIN_CAL_SAMPLES 1 #define MAX_CAL_SAMPLES 64 #define INIT_LOG_COUNT 5 #define PER_MIN_LOG_COUNT 2 #define PER_MAX_LOG_COUNT 10 #define AR9300_NUM_BT_WEIGHTS 4 #define AR9300_NUM_WLAN_WEIGHTS 4 /* Per Calibration data structure */ typedef struct per_cal_data { HAL_CAL_TYPES cal_type; // Type of calibration u_int32_t cal_num_samples; // Number of SW samples to collect u_int32_t cal_count_max; // Number of HW samples to collect void (*cal_collect)(struct ath_hal *, u_int8_t); // Accumulator func void (*cal_post_proc)(struct ath_hal *, u_int8_t); // Post-processing func } HAL_PERCAL_DATA; /* List structure for calibration data */ typedef struct cal_list { const HAL_PERCAL_DATA *cal_data; HAL_CAL_STATE cal_state; struct cal_list *cal_next; } HAL_CAL_LIST; #define AR9300_NUM_CAL_TYPES 2 #define AR9300_PAPRD_TABLE_SZ 24 #define AR9300_PAPRD_GAIN_TABLE_SZ 32 #define AR9382_MAX_GPIO_PIN_NUM (16) #define AR9382_GPIO_PIN_8_RESERVED (8) #define AR9382_GPIO_9_INPUT_ONLY (9) #define AR9382_MAX_GPIO_INPUT_PIN_NUM (13) #define AR9382_GPIO_PIN_11_RESERVED (11) #define AR9382_MAX_JTAG_GPIO_PIN_NUM (3) /* Paprd tx power adjust data structure */ struct ar9300_paprd_pwr_adjust { u_int32_t target_rate; // rate index u_int32_t reg_addr; // register offset u_int32_t reg_mask; // mask of register u_int32_t reg_mask_offset; // mask offset of register u_int32_t sub_db; // offset value unit of dB }; struct ar9300NfLimits { int16_t max; int16_t min; int16_t nominal; }; #define AR9300_MAX_RATES 36 /* legacy(4) + ofdm(8) + HTSS(8) + HTDS(8) + HTTS(8)*/ struct ath_hal_9300 { struct ath_hal_private ah_priv; /* base class */ /* * Information retrieved from EEPROM. */ ar9300_eeprom_t ah_eeprom; GAIN_VALUES ah_gain_values; u_int8_t ah_macaddr[IEEE80211_ADDR_LEN]; u_int8_t ah_bssid[IEEE80211_ADDR_LEN]; u_int8_t ah_bssid_mask[IEEE80211_ADDR_LEN]; u_int16_t ah_assoc_id; /* * Runtime state. */ u_int32_t ah_mask_reg; /* copy of AR_IMR */ u_int32_t ah_mask2Reg; /* copy of AR_IMR_S2 */ u_int32_t ah_msi_reg; /* copy of AR_PCIE_MSI */ os_atomic_t ah_ier_ref_count; /* reference count for enabling interrupts */ struct ar9300_stats ah_stats; /* various statistics */ RF_HAL_FUNCS ah_rf_hal; u_int32_t ah_tx_desc_mask; /* mask for TXDESC */ u_int32_t ah_tx_ok_interrupt_mask; u_int32_t ah_tx_err_interrupt_mask; u_int32_t ah_tx_desc_interrupt_mask; u_int32_t ah_tx_eol_interrupt_mask; u_int32_t ah_tx_urn_interrupt_mask; HAL_TX_QUEUE_INFO ah_txq[HAL_NUM_TX_QUEUES]; HAL_SMPS_MODE ah_sm_power_mode; HAL_BOOL ah_chip_full_sleep; u_int32_t ah_atim_window; HAL_ANT_SETTING ah_diversity_control; /* antenna setting */ u_int16_t ah_antenna_switch_swap; /* Controls mapping of OID request */ u_int8_t ah_tx_chainmask_cfg; /* chain mask config */ u_int8_t ah_rx_chainmask_cfg; u_int32_t ah_beacon_rssi_threshold; /* cache beacon rssi threshold */ /* Calibration related fields */ HAL_CAL_TYPES ah_supp_cals; HAL_CAL_LIST ah_iq_cal_data; /* IQ Cal Data */ HAL_CAL_LIST ah_temp_comp_cal_data; /* Temperature Compensation Cal Data */ HAL_CAL_LIST *ah_cal_list; /* ptr to first cal in list */ HAL_CAL_LIST *ah_cal_list_last; /* ptr to last cal in list */ HAL_CAL_LIST *ah_cal_list_curr; /* ptr to current cal */ // IQ Cal aliases #define ah_total_power_meas_i ah_meas0.unsign #define ah_total_power_meas_q ah_meas1.unsign #define ah_total_iq_corr_meas ah_meas2.sign union { u_int32_t unsign[AR9300_MAX_CHAINS]; int32_t sign[AR9300_MAX_CHAINS]; } ah_meas0; union { u_int32_t unsign[AR9300_MAX_CHAINS]; int32_t sign[AR9300_MAX_CHAINS]; } ah_meas1; union { u_int32_t unsign[AR9300_MAX_CHAINS]; int32_t sign[AR9300_MAX_CHAINS]; } ah_meas2; union { u_int32_t unsign[AR9300_MAX_CHAINS]; int32_t sign[AR9300_MAX_CHAINS]; } ah_meas3; u_int16_t ah_cal_samples; /* end - Calibration related fields */ u_int32_t ah_tx6_power_in_half_dbm; /* power output for 6Mb tx */ u_int32_t ah_sta_id1_defaults; /* STA_ID1 default settings */ u_int32_t ah_misc_mode; /* MISC_MODE settings */ HAL_BOOL ah_get_plcp_hdr; /* setting about MISC_SEL_EVM */ enum { AUTO_32KHZ, /* use it if 32kHz crystal present */ USE_32KHZ, /* do it regardless */ DONT_USE_32KHZ, /* don't use it regardless */ } ah_enable32k_hz_clock; /* whether to sleep at 32kHz */ u_int32_t ah_ofdm_tx_power; int16_t ah_tx_power_index_offset; u_int ah_slot_time; /* user-specified slot time */ u_int ah_ack_timeout; /* user-specified ack timeout */ /* * XXX * 11g-specific stuff; belongs in the driver. */ u_int8_t ah_g_beacon_rate; /* fixed rate for G beacons */ u_int32_t ah_gpio_mask; /* copy of enabled GPIO mask */ u_int32_t ah_gpio_cause; /* copy of GPIO cause (sync and async) */ /* * RF Silent handling; setup according to the EEPROM. */ u_int32_t ah_gpio_select; /* GPIO pin to use */ u_int32_t ah_polarity; /* polarity to disable RF */ u_int32_t ah_gpio_bit; /* after init, prev value */ HAL_BOOL ah_eep_enabled; /* EEPROM bit for capability */ #ifdef ATH_BT_COEX /* * Bluetooth coexistence static setup according to the registry */ HAL_BT_MODULE ah_bt_module; /* Bluetooth module identifier */ u_int8_t ah_bt_coex_config_type; /* BT coex configuration */ u_int8_t ah_bt_active_gpio_select; /* GPIO pin for BT_ACTIVE */ u_int8_t ah_bt_priority_gpio_select; /* GPIO pin for BT_PRIORITY */ u_int8_t ah_wlan_active_gpio_select; /* GPIO pin for WLAN_ACTIVE */ u_int8_t ah_bt_active_polarity; /* Polarity of BT_ACTIVE */ HAL_BOOL ah_bt_coex_single_ant; /* Single or dual antenna configuration */ u_int8_t ah_bt_wlan_isolation; /* Isolation between BT and WLAN in dB */ /* * Bluetooth coexistence runtime settings */ HAL_BOOL ah_bt_coex_enabled; /* If Bluetooth coexistence is enabled */ u_int32_t ah_bt_coex_mode; /* Register setting for AR_BT_COEX_MODE */ u_int32_t ah_bt_coex_bt_weight[AR9300_NUM_BT_WEIGHTS]; /* Register setting for AR_BT_COEX_WEIGHT */ u_int32_t ah_bt_coex_wlan_weight[AR9300_NUM_WLAN_WEIGHTS]; /* Register setting for AR_BT_COEX_WEIGHT */ u_int32_t ah_bt_coex_mode2; /* Register setting for AR_BT_COEX_MODE2 */ u_int32_t ah_bt_coex_flag; /* Special tuning flags for BT coex */ #endif /* * Generic timer support */ u_int32_t ah_avail_gen_timers; /* mask of available timers */ u_int32_t ah_intr_gen_timer_trigger; /* generic timer trigger interrupt state */ u_int32_t ah_intr_gen_timer_thresh; /* generic timer trigger interrupt state */ HAL_BOOL ah_enable_tsf2; /* enable TSF2 for gen timer 8-15. */ /* * ANI & Radar support. */ u_int32_t ah_proc_phy_err; /* Process Phy errs */ u_int32_t ah_ani_period; /* ani update list period */ struct ar9300_ani_state *ah_curani; /* cached last reference */ struct ar9300_ani_state ah_ani[255]; /* per-channel state */ struct ar9300_radar_state ah_radar[HAL_NUMRADAR_STATES]; /* Per-Channel Radar detector state */ struct ar9300_radar_q_elem *ah_radarq; /* radar event queue */ struct ar9300_radar_q_info ah_radarq_info; /* radar event q read/write state */ struct ar9300_ar_state ah_ar; /* AR detector state */ struct ar9300_radar_q_elem *ah_arq; /* AR event queue */ struct ar9300_radar_q_info ah_arq_info; /* AR event q read/write state */ /* * Transmit power state. Note these are maintained * here so they can be retrieved by diagnostic tools. */ u_int16_t ah_rates_array[16]; /* * Tx queue interrupt state. */ u_int32_t ah_intr_txqs; HAL_BOOL ah_intr_mitigation_rx; /* rx Interrupt Mitigation Settings */ HAL_BOOL ah_intr_mitigation_tx; /* tx Interrupt Mitigation Settings */ /* * Extension Channel Rx Clear State */ u_int32_t ah_cycle_count; u_int32_t ah_ctl_busy; u_int32_t ah_ext_busy; /* HT CWM state */ HAL_HT_EXTPROTSPACING ah_ext_prot_spacing; u_int8_t ah_tx_chainmask; /* tx chain mask */ u_int8_t ah_rx_chainmask; /* rx chain mask */ + /* optional tx chainmask */ + u_int8_t ah_tx_chainmaskopt; + u_int8_t ah_tx_cal_chainmask; /* tx cal chain mask */ u_int8_t ah_rx_cal_chainmask; /* rx cal chain mask */ int ah_hwp; void *ah_cal_mem; HAL_BOOL ah_emu_eeprom; HAL_ANI_CMD ah_ani_function; HAL_BOOL ah_rifs_enabled; u_int32_t ah_rifs_reg[11]; u_int32_t ah_rifs_sec_cnt; /* open-loop power control */ u_int32_t original_gain[22]; int32_t init_pdadc; int32_t pdadc_delta; /* cycle counts for beacon stuck diagnostics */ u_int32_t ah_cycles; u_int32_t ah_rx_clear; u_int32_t ah_rx_frame; u_int32_t ah_tx_frame; #define BB_HANG_SIG1 0 #define BB_HANG_SIG2 1 #define BB_HANG_SIG3 2 #define BB_HANG_SIG4 3 #define MAC_HANG_SIG1 4 #define MAC_HANG_SIG2 5 /* bb hang detection */ int ah_hang[6]; hal_hw_hangs_t ah_hang_wars; /* * Keytable type table */ #define AR_KEYTABLE_SIZE 128 /* XXX! */ uint8_t ah_keytype[AR_KEYTABLE_SIZE]; #undef AR_KEYTABLE_SIZE /* * Support for ar9300 multiple INIs */ struct ar9300_ini_array ah_ini_pcie_serdes; struct ar9300_ini_array ah_ini_pcie_serdes_low_power; struct ar9300_ini_array ah_ini_modes_additional; struct ar9300_ini_array ah_ini_modes_additional_40mhz; struct ar9300_ini_array ah_ini_modes_rxgain; struct ar9300_ini_array ah_ini_modes_rxgain_bounds; struct ar9300_ini_array ah_ini_modes_txgain; struct ar9300_ini_array ah_ini_japan2484; struct ar9300_ini_array ah_ini_radio_post_sys2ant; struct ar9300_ini_array ah_ini_BTCOEX_MAX_TXPWR; /* * New INI format starting with Osprey 2.0 INI. * Pre, core, post arrays for each sub-system (mac, bb, radio, soc) */ #define ATH_INI_PRE 0 #define ATH_INI_CORE 1 #define ATH_INI_POST 2 #define ATH_INI_NUM_SPLIT (ATH_INI_POST + 1) struct ar9300_ini_array ah_ini_mac[ATH_INI_NUM_SPLIT]; /* New INI format */ struct ar9300_ini_array ah_ini_bb[ATH_INI_NUM_SPLIT]; /* New INI format */ struct ar9300_ini_array ah_ini_radio[ATH_INI_NUM_SPLIT]; /* New INI format */ struct ar9300_ini_array ah_ini_soc[ATH_INI_NUM_SPLIT]; /* New INI format */ /* * Added to support DFS postamble array in INI that we need to apply * in DFS channels */ struct ar9300_ini_array ah_ini_dfs; #if ATH_WOW struct ar9300_ini_array ah_ini_pcie_serdes_wow; /* SerDes values during WOW sleep */ #endif /* To indicate EEPROM mapping used */ u_int32_t ah_immunity_vals[6]; HAL_BOOL ah_immunity_on; /* * snap shot of counter register for debug purposes */ #ifdef AH_DEBUG u_int32_t last_tf; u_int32_t last_rf; u_int32_t last_rc; u_int32_t last_cc; #endif HAL_BOOL ah_dma_stuck; /* Set to AH_TRUE when RX/TX DMA failed to stop. */ u_int32_t nf_tsf32; /* timestamp for NF calibration duration */ u_int32_t reg_dmn; /* Regulatory Domain */ int16_t twice_antenna_gain; /* Antenna Gain */ u_int16_t twice_antenna_reduction; /* Antenna Gain Allowed */ /* * Upper limit after factoring in the regulatory max, antenna gain and * multichain factor. No TxBF, CDD or STBC gain factored */ int16_t upper_limit[AR9300_MAX_CHAINS]; /* adjusted power for descriptor-based TPC for 1, 2, or 3 chains */ int16_t txpower[AR9300_MAX_RATES][AR9300_MAX_CHAINS]; /* adjusted power for descriptor-based TPC for 1, 2, or 3 chains with STBC*/ int16_t txpower_stbc[AR9300_MAX_RATES][AR9300_MAX_CHAINS]; /* Transmit Status ring support */ struct ar9300_txs *ts_ring; u_int16_t ts_tail; u_int16_t ts_size; u_int32_t ts_paddr_start; u_int32_t ts_paddr_end; /* Receive Buffer size */ #define HAL_RXBUFSIZE_DEFAULT 0xfff u_int16_t rx_buf_size; u_int32_t ah_wa_reg_val; // Store the permanent value of Reg 0x4004 so we dont have to R/M/W. (We should not be reading this register when in sleep states). /* Indicate the PLL source clock rate is 25Mhz or not. * clk_25mhz = 0 by default. */ u_int8_t clk_25mhz; /* For PAPRD uses */ u_int16_t small_signal_gain[AH_MAX_CHAINS]; u_int32_t pa_table[AH_MAX_CHAINS][AR9300_PAPRD_TABLE_SZ]; u_int32_t paprd_gain_table_entries[AR9300_PAPRD_GAIN_TABLE_SZ]; u_int32_t paprd_gain_table_index[AR9300_PAPRD_GAIN_TABLE_SZ]; u_int32_t ah_2g_paprd_rate_mask_ht20; /* Copy of eep->modal_header_2g.paprd_rate_mask_ht20 */ u_int32_t ah_2g_paprd_rate_mask_ht40; /* Copy of eep->modal_header_2g.paprd_rate_mask_ht40 */ u_int32_t ah_5g_paprd_rate_mask_ht20; /* Copy of eep->modal_header_5g.paprd_rate_mask_ht20 */ u_int32_t ah_5g_paprd_rate_mask_ht40; /* Copy of eep->modal_header_5g.paprd_rate_mask_ht40 */ u_int32_t paprd_training_power; /* For GreenTx use to store the default tx power */ u_int8_t ah_default_tx_power[ar9300_rate_size]; HAL_BOOL ah_paprd_broken; /* To store offsets of host interface registers */ struct { u_int32_t AR_RC; u_int32_t AR_WA; u_int32_t AR_PM_STATE; u_int32_t AR_H_INFOL; u_int32_t AR_H_INFOH; u_int32_t AR_PCIE_PM_CTRL; u_int32_t AR_HOST_TIMEOUT; u_int32_t AR_EEPROM; u_int32_t AR_SREV; u_int32_t AR_INTR_SYNC_CAUSE; u_int32_t AR_INTR_SYNC_CAUSE_CLR; u_int32_t AR_INTR_SYNC_ENABLE; u_int32_t AR_INTR_ASYNC_MASK; u_int32_t AR_INTR_SYNC_MASK; u_int32_t AR_INTR_ASYNC_CAUSE_CLR; u_int32_t AR_INTR_ASYNC_CAUSE; u_int32_t AR_INTR_ASYNC_ENABLE; u_int32_t AR_PCIE_SERDES; u_int32_t AR_PCIE_SERDES2; u_int32_t AR_GPIO_OUT; u_int32_t AR_GPIO_IN; u_int32_t AR_GPIO_OE_OUT; u_int32_t AR_GPIO_OE1_OUT; u_int32_t AR_GPIO_INTR_POL; u_int32_t AR_GPIO_INPUT_EN_VAL; u_int32_t AR_GPIO_INPUT_MUX1; u_int32_t AR_GPIO_INPUT_MUX2; u_int32_t AR_GPIO_OUTPUT_MUX1; u_int32_t AR_GPIO_OUTPUT_MUX2; u_int32_t AR_GPIO_OUTPUT_MUX3; u_int32_t AR_INPUT_STATE; u_int32_t AR_SPARE; u_int32_t AR_PCIE_CORE_RESET_EN; u_int32_t AR_CLKRUN; u_int32_t AR_EEPROM_STATUS_DATA; u_int32_t AR_OBS; u_int32_t AR_RFSILENT; u_int32_t AR_GPIO_PDPU; u_int32_t AR_GPIO_DS; u_int32_t AR_MISC; u_int32_t AR_PCIE_MSI; u_int32_t AR_TSF_SNAPSHOT_BT_ACTIVE; u_int32_t AR_TSF_SNAPSHOT_BT_PRIORITY; u_int32_t AR_TSF_SNAPSHOT_BT_CNTL; u_int32_t AR_PCIE_PHY_LATENCY_NFTS_ADJ; u_int32_t AR_TDMA_CCA_CNTL; u_int32_t AR_TXAPSYNC; u_int32_t AR_TXSYNC_INIT_SYNC_TMR; u_int32_t AR_INTR_PRIO_SYNC_CAUSE; u_int32_t AR_INTR_PRIO_SYNC_ENABLE; u_int32_t AR_INTR_PRIO_ASYNC_MASK; u_int32_t AR_INTR_PRIO_SYNC_MASK; u_int32_t AR_INTR_PRIO_ASYNC_CAUSE; u_int32_t AR_INTR_PRIO_ASYNC_ENABLE; } ah_hostifregs; u_int32_t ah_enterprise_mode; u_int32_t ah_radar1; u_int32_t ah_dc_offset; HAL_BOOL ah_hw_green_tx_enable; /* 1:enalbe H/W Green Tx */ HAL_BOOL ah_smartantenna_enable; /* 1:enalbe H/W */ u_int32_t ah_disable_cck; HAL_BOOL ah_lna_div_use_bt_ant_enable; /* 1:enable Rx(LNA) Diversity */ /* * Different types of memory where the calibration data might be stored. * All types are searched in Ar9300EepromRestore() in the order flash, eeprom, otp. * To disable searching a type, set its parameter to 0. */ int try_dram; int try_flash; int try_eeprom; int try_otp; #ifdef ATH_CAL_NAND_FLASH int try_nand; #endif /* * This is where we found the calibration data. */ int calibration_data_source; int calibration_data_source_address; /* * This is where we look for the calibration data. must be set before ath_attach() is called */ int calibration_data_try; int calibration_data_try_address; u_int8_t tx_iq_cal_enable : 1, tx_iq_cal_during_agc_cal : 1, tx_cl_cal_enable : 1; #if ATH_SUPPORT_MCI /* For MCI */ HAL_BOOL ah_mci_ready; u_int32_t ah_mci_int_raw; u_int32_t ah_mci_int_rx_msg; u_int32_t ah_mci_rx_status; u_int32_t ah_mci_cont_status; u_int8_t ah_mci_bt_state; u_int32_t ah_mci_gpm_addr; u_int8_t *ah_mci_gpm_buf; u_int32_t ah_mci_gpm_len; u_int32_t ah_mci_gpm_idx; u_int32_t ah_mci_sched_addr; u_int8_t *ah_mci_sched_buf; u_int8_t ah_mci_coex_major_version_wlan; u_int8_t ah_mci_coex_minor_version_wlan; u_int8_t ah_mci_coex_major_version_bt; u_int8_t ah_mci_coex_minor_version_bt; HAL_BOOL ah_mci_coex_bt_version_known; HAL_BOOL ah_mci_coex_wlan_channels_update; u_int32_t ah_mci_coex_wlan_channels[4]; HAL_BOOL ah_mci_coex_2g5g_update; HAL_BOOL ah_mci_coex_is_2g; HAL_BOOL ah_mci_query_bt; HAL_BOOL ah_mci_unhalt_bt_gpm; /* need send UNHALT */ HAL_BOOL ah_mci_halted_bt_gpm; /* HALT sent */ HAL_BOOL ah_mci_need_flush_btinfo; HAL_BOOL ah_mci_concur_tx_en; u_int8_t ah_mci_stomp_low_tx_pri; u_int8_t ah_mci_stomp_all_tx_pri; u_int8_t ah_mci_stomp_none_tx_pri; u_int32_t ah_mci_wlan_cal_seq; u_int32_t ah_mci_wlan_cal_done; #if ATH_SUPPORT_AIC HAL_BOOL ah_aic_enabled; u_int32_t ah_aic_sram[ATH_AIC_MAX_BT_CHANNEL]; #endif + #endif /* ATH_SUPPORT_MCI */ u_int8_t ah_cac_quiet_enabled; #if ATH_WOW_OFFLOAD u_int32_t ah_mcast_filter_l32_set; u_int32_t ah_mcast_filter_u32_set; #endif HAL_BOOL ah_reduced_self_gen_mask; + HAL_BOOL ah_chip_reset_done; + HAL_BOOL ah_abort_txdma_norx; + /* store previous passive RX Cal info */ + HAL_BOOL ah_skip_rx_iq_cal; + HAL_BOOL ah_rx_cal_complete; /* previous rx cal completed or not */ + u_int32_t ah_rx_cal_chan; /* chan on which rx cal is done */ + u_int32_t ah_rx_cal_chan_flag; + u_int32_t ah_rx_cal_corr[AR9300_MAX_CHAINS]; /* Local additions for FreeBSD */ /* * These fields are in the top level HAL in the atheros * codebase; here we place them in the AR9300 HAL and * access them via accessor methods if the driver requires them. */ u_int32_t ah_ob_db1[3]; u_int32_t ah_db2[3]; u_int32_t ah_bb_panic_timeout_ms; u_int32_t ah_bb_panic_last_status; u_int32_t ah_tx_trig_level; u_int16_t ath_hal_spur_chans[AR_EEPROM_MODAL_SPURS][2]; int16_t nf_cw_int_delta; /* diff btwn nominal NF and CW interf threshold */ int ah_phyrestart_disabled; HAL_RSSI_TX_POWER green_tx_status; int green_ap_ps_on; int ah_enable_keysearch_always; int ah_fccaifs; int ah_reset_reason; int ah_dcs_enable; struct ar9300NfLimits nf_2GHz; struct ar9300NfLimits nf_5GHz; struct ar9300NfLimits *nfp; - }; #define AH9300(_ah) ((struct ath_hal_9300 *)(_ah)) #define IS_9300_EMU(ah) \ (AH_PRIVATE(ah)->ah_devid == AR9300_DEVID_EMU_PCIE) #define ar9300_eep_data_in_flash(_ah) \ (!(AH_PRIVATE(_ah)->ah_flags & AH_USE_EEPROM)) #ifdef notyet // Need these additional conditions for IS_5GHZ_FAST_CLOCK_EN when we have valid eeprom contents. && \ ((ar9300_eeprom_get(AH9300(_ah), EEP_MINOR_REV) <= AR9300_EEP_MINOR_VER_16) || \ (ar9300_eeprom_get(AH9300(_ah), EEP_FSTCLK_5G)))) #endif /* * WAR for bug 6773. OS_DELAY() does a PIO READ on the PCI bus which allows * other cards' DMA reads to complete in the middle of our reset. */ #define WAR_6773(x) do { \ if ((++(x) % 64) == 0) \ OS_DELAY(1); \ } while (0) #define REG_WRITE_ARRAY(iniarray, column, regWr) do { \ int r; \ for (r = 0; r < ((iniarray)->ia_rows); r++) { \ OS_REG_WRITE(ah, INI_RA((iniarray), (r), 0), INI_RA((iniarray), r, (column)));\ WAR_6773(regWr); \ } \ } while (0) #define UPPER_5G_SUB_BANDSTART 5700 #define MID_5G_SUB_BANDSTART 5400 #define TRAINPOWER_DB_OFFSET 6 #define AH_PAPRD_GET_SCALE_FACTOR(_scale, _eep, _is2G, _channel) do{ if(_is2G) { _scale = (_eep->modal_header_2g.paprd_rate_mask_ht20>>25)&0x7; \ } else { \ if(_channel >= UPPER_5G_SUB_BANDSTART){ _scale = (_eep->modal_header_5g.paprd_rate_mask_ht20>>25)&0x7;} \ else if((UPPER_5G_SUB_BANDSTART < _channel) && (_channel >= MID_5G_SUB_BANDSTART)) \ { _scale = (_eep->modal_header_5g.paprd_rate_mask_ht40>>28)&0x7;} \ else { _scale = (_eep->modal_header_5g.paprd_rate_mask_ht40>>25)&0x7;} } }while(0) #ifdef AH_ASSERT #define ar9300FeatureNotSupported(feature, ah, func) \ ath_hal_printf(ah, # feature \ " not supported but called from %s\n", (func)), \ hal_assert(0) #else #define ar9300FeatureNotSupported(feature, ah, func) \ ath_hal_printf(ah, # feature \ " not supported but called from %s\n", (func)) #endif /* AH_ASSERT */ /* * Green Tx, Based on different RSSI of Received Beacon thresholds, * using different tx power by modified register tx power related values. * The thresholds are decided by system team. */ #define WB225_SW_GREEN_TX_THRES1_DB 56 /* in dB */ #define WB225_SW_GREEN_TX_THRES2_DB 41 /* in dB */ #define WB225_OB_CALIBRATION_VALUE 5 /* For Green Tx OLPC Delta Calibration Offset */ #define WB225_OB_GREEN_TX_SHORT_VALUE 1 /* For Green Tx OB value in short distance*/ #define WB225_OB_GREEN_TX_MIDDLE_VALUE 3 /* For Green Tx OB value in middle distance */ #define WB225_OB_GREEN_TX_LONG_VALUE 5 /* For Green Tx OB value in long distance */ #define WB225_BBPWRTXRATE9_SW_GREEN_TX_SHORT_VALUE 0x06060606 /* For SwGreen Tx BB_powertx_rate9 reg value in short distance */ #define WB225_BBPWRTXRATE9_SW_GREEN_TX_MIDDLE_VALUE 0x0E0E0E0E /* For SwGreen Tx BB_powertx_rate9 reg value in middle distance */ /* Tx power for short distacnce in SwGreenTx.*/ static const u_int8_t wb225_sw_gtx_tp_distance_short[ar9300_rate_size] = { 6, /*ALL_TARGET_LEGACY_6_24*/ 6, /*ALL_TARGET_LEGACY_36*/ 6, /*ALL_TARGET_LEGACY_48*/ 4, /*ALL_TARGET_LEGACY_54*/ 6, /*ALL_TARGET_LEGACY_1L_5L*/ 6, /*ALL_TARGET_LEGACY_5S*/ 6, /*ALL_TARGET_LEGACY_11L*/ 6, /*ALL_TARGET_LEGACY_11S*/ 6, /*ALL_TARGET_HT20_0_8_16*/ 6, /*ALL_TARGET_HT20_1_3_9_11_17_19*/ 4, /*ALL_TARGET_HT20_4*/ 4, /*ALL_TARGET_HT20_5*/ 4, /*ALL_TARGET_HT20_6*/ 2, /*ALL_TARGET_HT20_7*/ 0, /*ALL_TARGET_HT20_12*/ 0, /*ALL_TARGET_HT20_13*/ 0, /*ALL_TARGET_HT20_14*/ 0, /*ALL_TARGET_HT20_15*/ 0, /*ALL_TARGET_HT20_20*/ 0, /*ALL_TARGET_HT20_21*/ 0, /*ALL_TARGET_HT20_22*/ 0, /*ALL_TARGET_HT20_23*/ 6, /*ALL_TARGET_HT40_0_8_16*/ 6, /*ALL_TARGET_HT40_1_3_9_11_17_19*/ 4, /*ALL_TARGET_HT40_4*/ 4, /*ALL_TARGET_HT40_5*/ 4, /*ALL_TARGET_HT40_6*/ 2, /*ALL_TARGET_HT40_7*/ 0, /*ALL_TARGET_HT40_12*/ 0, /*ALL_TARGET_HT40_13*/ 0, /*ALL_TARGET_HT40_14*/ 0, /*ALL_TARGET_HT40_15*/ 0, /*ALL_TARGET_HT40_20*/ 0, /*ALL_TARGET_HT40_21*/ 0, /*ALL_TARGET_HT40_22*/ 0 /*ALL_TARGET_HT40_23*/ }; /* Tx power for middle distacnce in SwGreenTx.*/ static const u_int8_t wb225_sw_gtx_tp_distance_middle[ar9300_rate_size] = { 14, /*ALL_TARGET_LEGACY_6_24*/ 14, /*ALL_TARGET_LEGACY_36*/ 14, /*ALL_TARGET_LEGACY_48*/ 12, /*ALL_TARGET_LEGACY_54*/ 14, /*ALL_TARGET_LEGACY_1L_5L*/ 14, /*ALL_TARGET_LEGACY_5S*/ 14, /*ALL_TARGET_LEGACY_11L*/ 14, /*ALL_TARGET_LEGACY_11S*/ 14, /*ALL_TARGET_HT20_0_8_16*/ 14, /*ALL_TARGET_HT20_1_3_9_11_17_19*/ 14, /*ALL_TARGET_HT20_4*/ 14, /*ALL_TARGET_HT20_5*/ 12, /*ALL_TARGET_HT20_6*/ 10, /*ALL_TARGET_HT20_7*/ 0, /*ALL_TARGET_HT20_12*/ 0, /*ALL_TARGET_HT20_13*/ 0, /*ALL_TARGET_HT20_14*/ 0, /*ALL_TARGET_HT20_15*/ 0, /*ALL_TARGET_HT20_20*/ 0, /*ALL_TARGET_HT20_21*/ 0, /*ALL_TARGET_HT20_22*/ 0, /*ALL_TARGET_HT20_23*/ 14, /*ALL_TARGET_HT40_0_8_16*/ 14, /*ALL_TARGET_HT40_1_3_9_11_17_19*/ 14, /*ALL_TARGET_HT40_4*/ 14, /*ALL_TARGET_HT40_5*/ 12, /*ALL_TARGET_HT40_6*/ 10, /*ALL_TARGET_HT40_7*/ 0, /*ALL_TARGET_HT40_12*/ 0, /*ALL_TARGET_HT40_13*/ 0, /*ALL_TARGET_HT40_14*/ 0, /*ALL_TARGET_HT40_15*/ 0, /*ALL_TARGET_HT40_20*/ 0, /*ALL_TARGET_HT40_21*/ 0, /*ALL_TARGET_HT40_22*/ 0 /*ALL_TARGET_HT40_23*/ }; /* OLPC DeltaCalibration Offset unit in half dB.*/ static const u_int8_t wb225_gtx_olpc_cal_offset[6] = { 0, /* OB0*/ 16, /* OB1*/ 9, /* OB2*/ 5, /* OB3*/ 2, /* OB4*/ 0, /* OB5*/ }; /* * Definitions for HwGreenTx */ #define AR9485_HW_GREEN_TX_THRES1_DB 56 /* in dB */ #define AR9485_HW_GREEN_TX_THRES2_DB 41 /* in dB */ #define AR9485_BBPWRTXRATE9_HW_GREEN_TX_SHORT_VALUE 0x0C0C0A0A /* For HwGreen Tx BB_powertx_rate9 reg value in short distance */ #define AR9485_BBPWRTXRATE9_HW_GREEN_TX_MIDDLE_VALUE 0x10100E0E /* For HwGreenTx BB_powertx_rate9 reg value in middle distance */ /* Tx power for short distacnce in HwGreenTx.*/ static const u_int8_t ar9485_hw_gtx_tp_distance_short[ar9300_rate_size] = { 14, /*ALL_TARGET_LEGACY_6_24*/ 14, /*ALL_TARGET_LEGACY_36*/ 8, /*ALL_TARGET_LEGACY_48*/ 2, /*ALL_TARGET_LEGACY_54*/ 14, /*ALL_TARGET_LEGACY_1L_5L*/ 14, /*ALL_TARGET_LEGACY_5S*/ 14, /*ALL_TARGET_LEGACY_11L*/ 14, /*ALL_TARGET_LEGACY_11S*/ 12, /*ALL_TARGET_HT20_0_8_16*/ 12, /*ALL_TARGET_HT20_1_3_9_11_17_19*/ 12, /*ALL_TARGET_HT20_4*/ 12, /*ALL_TARGET_HT20_5*/ 8, /*ALL_TARGET_HT20_6*/ 2, /*ALL_TARGET_HT20_7*/ 0, /*ALL_TARGET_HT20_12*/ 0, /*ALL_TARGET_HT20_13*/ 0, /*ALL_TARGET_HT20_14*/ 0, /*ALL_TARGET_HT20_15*/ 0, /*ALL_TARGET_HT20_20*/ 0, /*ALL_TARGET_HT20_21*/ 0, /*ALL_TARGET_HT20_22*/ 0, /*ALL_TARGET_HT20_23*/ 10, /*ALL_TARGET_HT40_0_8_16*/ 10, /*ALL_TARGET_HT40_1_3_9_11_17_19*/ 10, /*ALL_TARGET_HT40_4*/ 10, /*ALL_TARGET_HT40_5*/ 6, /*ALL_TARGET_HT40_6*/ 2, /*ALL_TARGET_HT40_7*/ 0, /*ALL_TARGET_HT40_12*/ 0, /*ALL_TARGET_HT40_13*/ 0, /*ALL_TARGET_HT40_14*/ 0, /*ALL_TARGET_HT40_15*/ 0, /*ALL_TARGET_HT40_20*/ 0, /*ALL_TARGET_HT40_21*/ 0, /*ALL_TARGET_HT40_22*/ 0 /*ALL_TARGET_HT40_23*/ }; /* Tx power for middle distacnce in HwGreenTx.*/ static const u_int8_t ar9485_hw_gtx_tp_distance_middle[ar9300_rate_size] = { 18, /*ALL_TARGET_LEGACY_6_24*/ 18, /*ALL_TARGET_LEGACY_36*/ 14, /*ALL_TARGET_LEGACY_48*/ 12, /*ALL_TARGET_LEGACY_54*/ 18, /*ALL_TARGET_LEGACY_1L_5L*/ 18, /*ALL_TARGET_LEGACY_5S*/ 18, /*ALL_TARGET_LEGACY_11L*/ 18, /*ALL_TARGET_LEGACY_11S*/ 16, /*ALL_TARGET_HT20_0_8_16*/ 16, /*ALL_TARGET_HT20_1_3_9_11_17_19*/ 16, /*ALL_TARGET_HT20_4*/ 16, /*ALL_TARGET_HT20_5*/ 14, /*ALL_TARGET_HT20_6*/ 12, /*ALL_TARGET_HT20_7*/ 0, /*ALL_TARGET_HT20_12*/ 0, /*ALL_TARGET_HT20_13*/ 0, /*ALL_TARGET_HT20_14*/ 0, /*ALL_TARGET_HT20_15*/ 0, /*ALL_TARGET_HT20_20*/ 0, /*ALL_TARGET_HT20_21*/ 0, /*ALL_TARGET_HT20_22*/ 0, /*ALL_TARGET_HT20_23*/ 14, /*ALL_TARGET_HT40_0_8_16*/ 14, /*ALL_TARGET_HT40_1_3_9_11_17_19*/ 14, /*ALL_TARGET_HT40_4*/ 14, /*ALL_TARGET_HT40_5*/ 14, /*ALL_TARGET_HT40_6*/ 12, /*ALL_TARGET_HT40_7*/ 0, /*ALL_TARGET_HT40_12*/ 0, /*ALL_TARGET_HT40_13*/ 0, /*ALL_TARGET_HT40_14*/ 0, /*ALL_TARGET_HT40_15*/ 0, /*ALL_TARGET_HT40_20*/ 0, /*ALL_TARGET_HT40_21*/ 0, /*ALL_TARGET_HT40_22*/ 0 /*ALL_TARGET_HT40_23*/ }; /* MIMO Modes used in TPC calculations */ typedef enum { AR9300_DEF_MODE = 0, /* Could be CDD or Direct */ AR9300_TXBF_MODE, AR9300_STBC_MODE } AR9300_TXMODES; typedef enum { POSEIDON_STORED_REG_OBDB = 0, /* default OB/DB setting from ini */ POSEIDON_STORED_REG_TPC = 1, /* default txpower value in TPC reg */ POSEIDON_STORED_REG_BB_PWRTX_RATE9 = 2, /* default txpower value in * BB_powertx_rate9 reg */ POSEIDON_STORED_REG_SZ /* Can not add anymore */ } POSEIDON_STORED_REGS; typedef enum { POSEIDON_STORED_REG_G2_OLPC_OFFSET = 0,/* default OB/DB setting from ini */ POSEIDON_STORED_REG_G2_SZ /* should not exceed 3 */ } POSEIDON_STORED_REGS_G2; #if AH_NEED_TX_DATA_SWAP #if AH_NEED_RX_DATA_SWAP #define ar9300_init_cfg_reg(ah) OS_REG_RMW(ah, AR_CFG, AR_CFG_SWTB | AR_CFG_SWRB,0) #else #define ar9300_init_cfg_reg(ah) OS_REG_RMW(ah, AR_CFG, AR_CFG_SWTB,0) #endif #elif AH_NEED_RX_DATA_SWAP #define ar9300_init_cfg_reg(ah) OS_REG_RMW(ah, AR_CFG, AR_CFG_SWRB,0) #else #define ar9300_init_cfg_reg(ah) OS_REG_RMW(ah, AR_CFG, AR_CFG_SWTD | AR_CFG_SWRD,0) #endif extern HAL_BOOL ar9300_rf_attach(struct ath_hal *, HAL_STATUS *); struct ath_hal; extern struct ath_hal_9300 * ar9300_new_state(u_int16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, HAL_OPS_CONFIG *ah_config, HAL_STATUS *status); extern struct ath_hal * ar9300_attach(u_int16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, HAL_OPS_CONFIG *ah_config, HAL_STATUS *status); extern void ar9300_detach(struct ath_hal *ah); extern void ar9300_read_revisions(struct ath_hal *ah); extern HAL_BOOL ar9300_chip_test(struct ath_hal *ah); extern HAL_BOOL ar9300_get_channel_edges(struct ath_hal *ah, u_int16_t flags, u_int16_t *low, u_int16_t *high); extern HAL_BOOL ar9300_fill_capability_info(struct ath_hal *ah); extern void ar9300_beacon_init(struct ath_hal *ah, - u_int32_t next_beacon, u_int32_t beacon_period, HAL_OPMODE opmode); + u_int32_t next_beacon, u_int32_t beacon_period, + u_int32_t beacon_period_fraction, HAL_OPMODE opmode); extern void ar9300_set_sta_beacon_timers(struct ath_hal *ah, const HAL_BEACON_STATE *); extern HAL_BOOL ar9300_is_interrupt_pending(struct ath_hal *ah); extern HAL_BOOL ar9300_get_pending_interrupts(struct ath_hal *ah, HAL_INT *, HAL_INT_TYPE, u_int8_t, HAL_BOOL); extern HAL_INT ar9300_get_interrupts(struct ath_hal *ah); extern HAL_INT ar9300_set_interrupts(struct ath_hal *ah, HAL_INT ints, HAL_BOOL); extern void ar9300_set_intr_mitigation_timer(struct ath_hal* ah, HAL_INT_MITIGATION reg, u_int32_t value); extern u_int32_t ar9300_get_intr_mitigation_timer(struct ath_hal* ah, HAL_INT_MITIGATION reg); extern u_int32_t ar9300_get_key_cache_size(struct ath_hal *); extern HAL_BOOL ar9300_is_key_cache_entry_valid(struct ath_hal *, u_int16_t entry); extern HAL_BOOL ar9300_reset_key_cache_entry(struct ath_hal *ah, u_int16_t entry); extern HAL_CHANNEL_INTERNAL * ar9300_check_chan(struct ath_hal *ah, const struct ieee80211_channel *chan); extern HAL_BOOL ar9300_set_key_cache_entry_mac(struct ath_hal *, u_int16_t entry, const u_int8_t *mac); extern HAL_BOOL ar9300_set_key_cache_entry(struct ath_hal *ah, u_int16_t entry, const HAL_KEYVAL *k, const u_int8_t *mac, int xor_key); extern HAL_BOOL ar9300_print_keycache(struct ath_hal *ah); +#if ATH_SUPPORT_KEYPLUMB_WAR +extern HAL_BOOL ar9300_check_key_cache_entry(struct ath_hal *ah, u_int16_t entry, + const HAL_KEYVAL *k, int xorKey); +#endif extern void ar9300_get_mac_address(struct ath_hal *ah, u_int8_t *mac); extern HAL_BOOL ar9300_set_mac_address(struct ath_hal *ah, const u_int8_t *); extern void ar9300_get_bss_id_mask(struct ath_hal *ah, u_int8_t *mac); extern HAL_BOOL ar9300_set_bss_id_mask(struct ath_hal *, const u_int8_t *); extern HAL_STATUS ar9300_select_ant_config(struct ath_hal *ah, u_int32_t cfg); +#if 0 +extern u_int32_t ar9300_ant_ctrl_common_get(struct ath_hal *ah, HAL_BOOL is_2ghz); +#endif +extern HAL_BOOL ar9300_ant_swcom_sel(struct ath_hal *ah, u_int8_t ops, + u_int32_t *common_tbl1, u_int32_t *common_tbl2); extern HAL_BOOL ar9300_set_regulatory_domain(struct ath_hal *ah, u_int16_t reg_domain, HAL_STATUS *stats); extern u_int ar9300_get_wireless_modes(struct ath_hal *ah); extern void ar9300_enable_rf_kill(struct ath_hal *); extern HAL_BOOL ar9300_gpio_cfg_output(struct ath_hal *, u_int32_t gpio, HAL_GPIO_MUX_TYPE signalType); extern HAL_BOOL ar9300_gpio_cfg_output_led_off(struct ath_hal *, u_int32_t gpio, HAL_GPIO_MUX_TYPE signalType); extern HAL_BOOL ar9300_gpio_cfg_input(struct ath_hal *, u_int32_t gpio); extern HAL_BOOL ar9300_gpio_set(struct ath_hal *, u_int32_t gpio, u_int32_t val); extern u_int32_t ar9300_gpio_get(struct ath_hal *ah, u_int32_t gpio); extern u_int32_t ar9300_gpio_get_intr(struct ath_hal *ah); extern void ar9300_gpio_set_intr(struct ath_hal *ah, u_int, u_int32_t ilevel); extern u_int32_t ar9300_gpio_get_polarity(struct ath_hal *ah); extern void ar9300_gpio_set_polarity(struct ath_hal *ah, u_int32_t, u_int32_t); extern u_int32_t ar9300_gpio_get_mask(struct ath_hal *ah); extern int ar9300_gpio_set_mask(struct ath_hal *ah, u_int32_t mask, u_int32_t pol_map); extern void ar9300_set_led_state(struct ath_hal *ah, HAL_LED_STATE state); extern void ar9300_set_power_led_state(struct ath_hal *ah, u_int8_t enable); extern void ar9300_set_network_led_state(struct ath_hal *ah, u_int8_t enable); extern void ar9300_write_associd(struct ath_hal *ah, const u_int8_t *bssid, u_int16_t assoc_id); extern u_int32_t ar9300_ppm_get_rssi_dump(struct ath_hal *); extern u_int32_t ar9300_ppm_arm_trigger(struct ath_hal *); extern int ar9300_ppm_get_trigger(struct ath_hal *); extern u_int32_t ar9300_ppm_force(struct ath_hal *); extern void ar9300_ppm_un_force(struct ath_hal *); extern u_int32_t ar9300_ppm_get_force_state(struct ath_hal *); extern void ar9300_set_dcs_mode(struct ath_hal *ah, u_int32_t); extern u_int32_t ar9300_get_dcs_mode(struct ath_hal *ah); extern u_int32_t ar9300_get_tsf32(struct ath_hal *ah); extern u_int64_t ar9300_get_tsf64(struct ath_hal *ah); extern u_int32_t ar9300_get_tsf2_32(struct ath_hal *ah); extern void ar9300_set_tsf64(struct ath_hal *ah, u_int64_t tsf); extern void ar9300_reset_tsf(struct ath_hal *ah); extern void ar9300_set_basic_rate(struct ath_hal *ah, HAL_RATE_SET *pSet); extern u_int32_t ar9300_get_random_seed(struct ath_hal *ah); extern HAL_BOOL ar9300_detect_card_present(struct ath_hal *ah); extern void ar9300_update_mib_mac_stats(struct ath_hal *ah); extern void ar9300_get_mib_mac_stats(struct ath_hal *ah, HAL_MIB_STATS* stats); extern HAL_BOOL ar9300_is_japan_channel_spread_supported(struct ath_hal *ah); extern u_int32_t ar9300_get_cur_rssi(struct ath_hal *ah); extern u_int32_t ar9300_get_rssi_chain0(struct ath_hal *ah); extern u_int ar9300_get_def_antenna(struct ath_hal *ah); extern void ar9300_set_def_antenna(struct ath_hal *ah, u_int antenna); extern HAL_BOOL ar9300_set_antenna_switch(struct ath_hal *ah, HAL_ANT_SETTING settings, const struct ieee80211_channel *chan, u_int8_t *, u_int8_t *, u_int8_t *); extern HAL_BOOL ar9300_is_sleep_after_beacon_broken(struct ath_hal *ah); extern HAL_BOOL ar9300_set_slot_time(struct ath_hal *, u_int); extern HAL_BOOL ar9300_set_ack_timeout(struct ath_hal *, u_int); extern u_int ar9300_get_ack_timeout(struct ath_hal *); extern HAL_STATUS ar9300_set_quiet(struct ath_hal *ah, u_int32_t period, u_int32_t duration, u_int32_t next_start, HAL_QUIET_FLAG flag); extern void ar9300_set_pcu_config(struct ath_hal *); extern HAL_STATUS ar9300_get_capability(struct ath_hal *, HAL_CAPABILITY_TYPE, u_int32_t, u_int32_t *); extern HAL_BOOL ar9300_set_capability(struct ath_hal *, HAL_CAPABILITY_TYPE, u_int32_t, u_int32_t, HAL_STATUS *); extern HAL_BOOL ar9300_get_diag_state(struct ath_hal *ah, int request, const void *args, u_int32_t argsize, void **result, u_int32_t *resultsize); extern void ar9300_get_desc_info(struct ath_hal *ah, HAL_DESC_INFO *desc_info); extern uint32_t ar9300_get_11n_ext_busy(struct ath_hal *ah); extern void ar9300_set_11n_mac2040(struct ath_hal *ah, HAL_HT_MACMODE mode); extern HAL_HT_RXCLEAR ar9300_get_11n_rx_clear(struct ath_hal *ah); extern void ar9300_set_11n_rx_clear(struct ath_hal *ah, HAL_HT_RXCLEAR rxclear); extern HAL_BOOL ar9300_set_power_mode(struct ath_hal *ah, HAL_POWER_MODE mode, int set_chip); extern HAL_POWER_MODE ar9300_get_power_mode(struct ath_hal *ah); extern HAL_BOOL ar9300_set_power_mode_awake(struct ath_hal *ah, int set_chip); extern void ar9300_set_sm_power_mode(struct ath_hal *ah, HAL_SMPS_MODE mode); extern void ar9300_config_pci_power_save(struct ath_hal *ah, int restore, int power_off); extern void ar9300_force_tsf_sync(struct ath_hal *ah, const u_int8_t *bssid, u_int16_t assoc_id); #if ATH_WOW extern void ar9300_wow_apply_pattern(struct ath_hal *ah, u_int8_t *p_ath_pattern, u_int8_t *p_ath_mask, int32_t pattern_count, u_int32_t ath_pattern_len); //extern u_int32_t ar9300_wow_wake_up(struct ath_hal *ah,u_int8_t *chipPatternBytes); extern u_int32_t ar9300_wow_wake_up(struct ath_hal *ah, HAL_BOOL offloadEnable); extern bool ar9300_wow_enable(struct ath_hal *ah, u_int32_t pattern_enable, u_int32_t timeout_in_seconds, int clearbssid, HAL_BOOL offloadEnable); #if ATH_WOW_OFFLOAD /* ARP offload */ #define WOW_OFFLOAD_ARP_INFO_MAX 2 struct hal_wow_offload_arp_info { u_int32_t valid; u_int32_t id; u_int32_t Flags; union { u_int8_t u8[4]; u_int32_t u32; } RemoteIPv4Address; union { u_int8_t u8[4]; u_int32_t u32; } HostIPv4Address; union { u_int8_t u8[6]; u_int32_t u32[2]; } MacAddress; }; /* NS offload */ #define WOW_OFFLOAD_NS_INFO_MAX 2 struct hal_wow_offload_ns_info { u_int32_t valid; u_int32_t id; u_int32_t Flags; union { u_int8_t u8[16]; u_int32_t u32[4]; } RemoteIPv6Address; union { u_int8_t u8[16]; u_int32_t u32[4]; } SolicitedNodeIPv6Address; union { u_int8_t u8[6]; u_int32_t u32[2]; } MacAddress; union { u_int8_t u8[16]; u_int32_t u32[4]; } TargetIPv6Addresses[2]; }; extern void ar9300_wowoffload_prep(struct ath_hal *ah); extern void ar9300_wowoffload_post(struct ath_hal *ah); extern u_int32_t ar9300_wowoffload_download_rekey_data(struct ath_hal *ah, u_int32_t *data, u_int32_t size); extern void ar9300_wowoffload_retrieve_data(struct ath_hal *ah, void *buf, u_int32_t param); extern void ar9300_wowoffload_download_acer_magic(struct ath_hal *ah, HAL_BOOL valid, u_int8_t* datap, u_int32_t bytes); extern void ar9300_wowoffload_download_acer_swka(struct ath_hal *ah, u_int32_t id, HAL_BOOL valid, u_int32_t period, u_int32_t size, u_int32_t* datap); extern void ar9300_wowoffload_download_arp_info(struct ath_hal *ah, u_int32_t id, u_int32_t *data); extern void ar9300_wowoffload_download_ns_info(struct ath_hal *ah, u_int32_t id, u_int32_t *data); #endif /* ATH_WOW_OFFLOAD */ #endif extern HAL_BOOL ar9300_reset(struct ath_hal *ah, HAL_OPMODE opmode, struct ieee80211_channel *chan, HAL_HT_MACMODE macmode, u_int8_t txchainmask, u_int8_t rxchainmask, HAL_HT_EXTPROTSPACING extprotspacing, HAL_BOOL b_channel_change, HAL_STATUS *status, int is_scan); extern HAL_BOOL ar9300_lean_channel_change(struct ath_hal *ah, HAL_OPMODE opmode, struct ieee80211_channel *chan, HAL_HT_MACMODE macmode, u_int8_t txchainmask, u_int8_t rxchainmask); extern HAL_BOOL ar9300_set_reset_reg(struct ath_hal *ah, u_int32_t type); extern void ar9300_init_pll(struct ath_hal *ah, struct ieee80211_channel *chan); extern void ar9300_green_ap_ps_on_off( struct ath_hal *ah, u_int16_t rxMask); extern u_int16_t ar9300_is_single_ant_power_save_possible(struct ath_hal *ah); extern void ar9300_set_operating_mode(struct ath_hal *ah, int opmode); extern HAL_BOOL ar9300_phy_disable(struct ath_hal *ah); extern HAL_BOOL ar9300_disable(struct ath_hal *ah); extern HAL_BOOL ar9300_chip_reset(struct ath_hal *ah, struct ieee80211_channel *); extern HAL_BOOL ar9300_calibration(struct ath_hal *ah, struct ieee80211_channel *chan, u_int8_t rxchainmask, HAL_BOOL longcal, HAL_BOOL *isIQdone, int is_scan, u_int32_t *sched_cals); extern void ar9300_reset_cal_valid(struct ath_hal *ah, const struct ieee80211_channel *chan, HAL_BOOL *isIQdone, u_int32_t cal_type); extern void ar9300_iq_cal_collect(struct ath_hal *ah, u_int8_t num_chains); extern void ar9300_iq_calibration(struct ath_hal *ah, u_int8_t num_chains); extern void ar9300_temp_comp_cal_collect(struct ath_hal *ah); extern void ar9300_temp_comp_calibration(struct ath_hal *ah, u_int8_t num_chains); extern int16_t ar9300_get_min_cca_pwr(struct ath_hal *ah); extern void ar9300_upload_noise_floor(struct ath_hal *ah, int is2G, int16_t nfarray[]); extern HAL_BOOL ar9300_set_tx_power_limit(struct ath_hal *ah, u_int32_t limit, u_int16_t extra_txpow, u_int16_t tpc_in_db); extern void ar9300_chain_noise_floor(struct ath_hal *ah, int16_t *nf_buf, struct ieee80211_channel *chan, int is_scan); +extern int16_t ar9300_get_nf_from_reg(struct ath_hal *ah, struct ieee80211_channel *chan, int wait_time); +extern int ar9300_get_rx_nf_offset(struct ath_hal *ah, struct ieee80211_channel *chan, int8_t *nf_pwr, int8_t *nf_cal); extern HAL_BOOL ar9300_load_nf(struct ath_hal *ah, int16_t nf[]); extern HAL_RFGAIN ar9300_get_rfgain(struct ath_hal *ah); extern const HAL_RATE_TABLE *ar9300_get_rate_table(struct ath_hal *, u_int mode); extern int16_t ar9300_get_rate_txpower(struct ath_hal *ah, u_int mode, u_int8_t rate_index, u_int8_t chainmask, u_int8_t mimo_mode); extern void ar9300_init_rate_txpower(struct ath_hal *ah, u_int mode, const struct ieee80211_channel *chan, u_int8_t powerPerRate[], u_int8_t chainmask); extern void ar9300_adjust_reg_txpower_cdd(struct ath_hal *ah, u_int8_t powerPerRate[]); extern HAL_STATUS ath_hal_get_rate_power_limit_from_eeprom(struct ath_hal *ah, u_int16_t freq, int8_t *max_rate_power, int8_t *min_rate_power); extern void ar9300_reset_tx_status_ring(struct ath_hal *ah); extern void ar9300_enable_mib_counters(struct ath_hal *); extern void ar9300_disable_mib_counters(struct ath_hal *); extern void ar9300_ani_attach(struct ath_hal *); extern void ar9300_ani_detach(struct ath_hal *); extern struct ar9300_ani_state *ar9300_ani_get_current_state(struct ath_hal *); extern struct ar9300_stats *ar9300_ani_get_current_stats(struct ath_hal *); extern HAL_BOOL ar9300_ani_control(struct ath_hal *, HAL_ANI_CMD cmd, int param); struct ath_rx_status; extern void ar9300_process_mib_intr(struct ath_hal *, const HAL_NODE_STATS *); extern void ar9300_ani_ar_poll(struct ath_hal *, const HAL_NODE_STATS *, const struct ieee80211_channel *, HAL_ANISTATS *); extern void ar9300_ani_reset(struct ath_hal *, HAL_BOOL is_scanning); extern void ar9300_ani_init_defaults(struct ath_hal *ah, HAL_HT_MACMODE macmode); extern void ar9300_enable_tpc(struct ath_hal *); extern HAL_BOOL ar9300_rf_gain_cap_apply(struct ath_hal *ah, int is2GHz); extern void ar9300_rx_gain_table_apply(struct ath_hal *ah); extern void ar9300_tx_gain_table_apply(struct ath_hal *ah); extern void ar9300_mat_enable(struct ath_hal *ah, int enable); extern void ar9300_dump_keycache(struct ath_hal *ah, int n, u_int32_t *entry); extern HAL_BOOL ar9300_ant_ctrl_set_lna_div_use_bt_ant(struct ath_hal * ah, HAL_BOOL enable, const struct ieee80211_channel * chan); /* BB Panic Watchdog declarations */ #define HAL_BB_PANIC_WD_TMO 25 /* in ms, 0 to disable */ #define HAL_BB_PANIC_WD_TMO_HORNET 85 extern void ar9300_config_bb_panic_watchdog(struct ath_hal *); extern void ar9300_handle_bb_panic(struct ath_hal *); extern int ar9300_get_bb_panic_info(struct ath_hal *ah, struct hal_bb_panic_info *bb_panic); extern HAL_BOOL ar9300_handle_radar_bb_panic(struct ath_hal *ah); extern void ar9300_set_hal_reset_reason(struct ath_hal *ah, u_int8_t resetreason); /* DFS declarations */ extern void ar9300_check_dfs(struct ath_hal *ah, struct ieee80211_channel *chan); extern void ar9300_dfs_found(struct ath_hal *ah, struct ieee80211_channel *chan, u_int64_t nolTime); extern void ar9300_enable_dfs(struct ath_hal *ah, HAL_PHYERR_PARAM *pe); extern void ar9300_get_dfs_thresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe); extern HAL_BOOL ar9300_radar_wait(struct ath_hal *ah, struct ieee80211_channel *chan); extern struct dfs_pulse * ar9300_get_dfs_radars(struct ath_hal *ah, u_int32_t dfsdomain, int *numradars, struct dfs_bin5pulse **bin5pulses, int *numb5radars, HAL_PHYERR_PARAM *pe); extern void ar9300_adjust_difs(struct ath_hal *ah, u_int32_t val); extern u_int32_t ar9300_dfs_config_fft(struct ath_hal *ah, HAL_BOOL is_enable); extern void ar9300_cac_tx_quiet(struct ath_hal *ah, HAL_BOOL enable); extern void ar9300_dfs_cac_war(struct ath_hal *ah, u_int32_t start); extern struct ieee80211_channel * ar9300_get_extension_channel(struct ath_hal *ah); extern HAL_BOOL ar9300_is_fast_clock_enabled(struct ath_hal *ah); extern void ar9300_mark_phy_inactive(struct ath_hal *ah); /* Spectral scan declarations */ extern void ar9300_configure_spectral_scan(struct ath_hal *ah, HAL_SPECTRAL_PARAM *ss); extern void ar9300_set_cca_threshold(struct ath_hal *ah, u_int8_t thresh62); extern void ar9300_get_spectral_params(struct ath_hal *ah, HAL_SPECTRAL_PARAM *ss); extern HAL_BOOL ar9300_is_spectral_active(struct ath_hal *ah); extern HAL_BOOL ar9300_is_spectral_enabled(struct ath_hal *ah); extern void ar9300_start_spectral_scan(struct ath_hal *ah); extern void ar9300_stop_spectral_scan(struct ath_hal *ah); extern u_int32_t ar9300_get_spectral_config(struct ath_hal *ah); extern void ar9300_restore_spectral_config(struct ath_hal *ah, u_int32_t restoreval); int16_t ar9300_get_ctl_chan_nf(struct ath_hal *ah); int16_t ar9300_get_ext_chan_nf(struct ath_hal *ah); /* End spectral scan declarations */ /* Raw ADC capture functions */ extern void ar9300_enable_test_addac_mode(struct ath_hal *ah); extern void ar9300_disable_test_addac_mode(struct ath_hal *ah); extern void ar9300_begin_adc_capture(struct ath_hal *ah, int auto_agc_gain); extern HAL_STATUS ar9300_retrieve_capture_data(struct ath_hal *ah, u_int16_t chain_mask, int disable_dc_filter, void *sample_buf, u_int32_t *max_samples); extern HAL_STATUS ar9300_calc_adc_ref_powers(struct ath_hal *ah, int freq_mhz, int16_t *sample_min, int16_t *sample_max, int32_t *chain_ref_pwr, int num_chain_ref_pwr); extern HAL_STATUS ar9300_get_min_agc_gain(struct ath_hal *ah, int freq_mhz, int32_t *chain_gain, int num_chain_gain); extern HAL_BOOL ar9300_reset_11n(struct ath_hal *ah, HAL_OPMODE opmode, struct ieee80211_channel *chan, HAL_BOOL b_channel_change, HAL_STATUS *status); extern void ar9300_set_coverage_class(struct ath_hal *ah, u_int8_t coverageclass, int now); extern void ar9300_get_channel_centers(struct ath_hal *ah, const struct ieee80211_channel *chan, CHAN_CENTERS *centers); extern u_int16_t ar9300_get_ctl_center(struct ath_hal *ah, const struct ieee80211_channel *chan); extern u_int16_t ar9300_get_ext_center(struct ath_hal *ah, const struct ieee80211_channel *chan); extern u_int32_t ar9300_get_mib_cycle_counts_pct(struct ath_hal *, u_int32_t*, u_int32_t*, u_int32_t*); extern void ar9300_dma_reg_dump(struct ath_hal *); extern HAL_BOOL ar9300_set_11n_rx_rifs(struct ath_hal *ah, HAL_BOOL enable); extern HAL_BOOL ar9300_set_rifs_delay(struct ath_hal *ah, HAL_BOOL enable); extern HAL_BOOL ar9300_set_smart_antenna(struct ath_hal *ah, HAL_BOOL enable); extern HAL_BOOL ar9300_detect_bb_hang(struct ath_hal *ah); extern HAL_BOOL ar9300_detect_mac_hang(struct ath_hal *ah); #ifdef ATH_BT_COEX extern void ar9300_set_bt_coex_info(struct ath_hal *ah, HAL_BT_COEX_INFO *btinfo); extern void ar9300_bt_coex_config(struct ath_hal *ah, HAL_BT_COEX_CONFIG *btconf); extern void ar9300_bt_coex_set_qcu_thresh(struct ath_hal *ah, int qnum); extern void ar9300_bt_coex_set_weights(struct ath_hal *ah, u_int32_t stomp_type); extern void ar9300_bt_coex_setup_bmiss_thresh(struct ath_hal *ah, u_int32_t thresh); extern void ar9300_bt_coex_set_parameter(struct ath_hal *ah, u_int32_t type, u_int32_t value); extern void ar9300_bt_coex_disable(struct ath_hal *ah); extern int ar9300_bt_coex_enable(struct ath_hal *ah); extern void ar9300_init_bt_coex(struct ath_hal *ah); extern u_int32_t ar9300_get_bt_active_gpio(struct ath_hal *ah, u_int32_t reg); extern u_int32_t ar9300_get_wlan_active_gpio(struct ath_hal *ah, u_int32_t reg,u_int32_t bOn); #endif extern int ar9300_alloc_generic_timer(struct ath_hal *ah, HAL_GEN_TIMER_DOMAIN tsf); extern void ar9300_free_generic_timer(struct ath_hal *ah, int index); extern void ar9300_start_generic_timer(struct ath_hal *ah, int index, u_int32_t timer_next, u_int32_t timer_period); extern void ar9300_stop_generic_timer(struct ath_hal *ah, int index); extern void ar9300_get_gen_timer_interrupts(struct ath_hal *ah, u_int32_t *trigger, u_int32_t *thresh); extern void ar9300_start_tsf2(struct ath_hal *ah); extern void ar9300_chk_rssi_update_tx_pwr(struct ath_hal *ah, int rssi); extern HAL_BOOL ar9300_is_skip_paprd_by_greentx(struct ath_hal *ah); extern void ar9300_control_signals_for_green_tx_mode(struct ath_hal *ah); extern void ar9300_hwgreentx_set_pal_spare(struct ath_hal *ah, int value); extern HAL_BOOL ar9300_is_ani_noise_spur(struct ath_hal *ah); extern void ar9300_reset_hw_beacon_proc_crc(struct ath_hal *ah); extern int32_t ar9300_get_hw_beacon_rssi(struct ath_hal *ah); extern void ar9300_set_hw_beacon_rssi_threshold(struct ath_hal *ah, u_int32_t rssi_threshold); extern void ar9300_reset_hw_beacon_rssi(struct ath_hal *ah); extern void ar9300_set_hw_beacon_proc(struct ath_hal *ah, HAL_BOOL on); extern void ar9300_get_vow_stats(struct ath_hal *ah, HAL_VOWSTATS *p_stats, u_int8_t); extern int ar9300_get_spur_info(struct ath_hal * ah, int *enable, int len, u_int16_t *freq); extern int ar9300_set_spur_info(struct ath_hal * ah, int enable, int len, u_int16_t *freq); extern void ar9300_wow_set_gpio_reset_low(struct ath_hal * ah); extern HAL_BOOL ar9300_get_mib_cycle_counts(struct ath_hal *, HAL_SURVEY_SAMPLE *); extern void ar9300_clear_mib_counters(struct ath_hal *ah); /* EEPROM interface functions */ /* Common Interface functions */ extern HAL_STATUS ar9300_eeprom_attach(struct ath_hal *); extern u_int32_t ar9300_eeprom_get(struct ath_hal_9300 *ahp, EEPROM_PARAM param); extern u_int32_t ar9300_ini_fixup(struct ath_hal *ah, ar9300_eeprom_t *p_eep_data, u_int32_t reg, u_int32_t val); extern HAL_STATUS ar9300_eeprom_set_transmit_power(struct ath_hal *ah, ar9300_eeprom_t *p_eep_data, const struct ieee80211_channel *chan, u_int16_t cfg_ctl, u_int16_t twice_antenna_reduction, u_int16_t twice_max_regulatory_power, u_int16_t power_limit); extern void ar9300_eeprom_set_addac(struct ath_hal *, struct ieee80211_channel *); extern HAL_BOOL ar9300_eeprom_set_param(struct ath_hal *ah, EEPROM_PARAM param, u_int32_t value); extern HAL_BOOL ar9300_eeprom_set_board_values(struct ath_hal *, const struct ieee80211_channel *); extern HAL_BOOL ar9300_eeprom_read_word(struct ath_hal *, u_int off, u_int16_t *data); extern HAL_BOOL ar9300_eeprom_read(struct ath_hal *ah, long address, u_int8_t *buffer, int many); extern HAL_BOOL ar9300_otp_read(struct ath_hal *ah, u_int off, u_int32_t *data, HAL_BOOL is_wifi); extern HAL_BOOL ar9300_flash_read(struct ath_hal *, u_int off, u_int16_t *data); extern HAL_BOOL ar9300_flash_write(struct ath_hal *, u_int off, u_int16_t data); extern u_int ar9300_eeprom_dump_support(struct ath_hal *ah, void **pp_e); extern u_int8_t ar9300_eeprom_get_num_ant_config(struct ath_hal_9300 *ahp, HAL_FREQ_BAND freq_band); extern HAL_STATUS ar9300_eeprom_get_ant_cfg(struct ath_hal_9300 *ahp, const struct ieee80211_channel *chan, u_int8_t index, u_int16_t *config); extern u_int8_t* ar9300_eeprom_get_cust_data(struct ath_hal_9300 *ahp); extern u_int8_t *ar9300_eeprom_get_spur_chans_ptr(struct ath_hal *ah, HAL_BOOL is_2ghz); extern HAL_BOOL ar9300_interference_is_present(struct ath_hal *ah); extern HAL_BOOL ar9300_tuning_caps_apply(struct ath_hal *ah); extern void ar9300_disp_tpc_tables(struct ath_hal *ah); extern u_int8_t *ar9300_get_tpc_tables(struct ath_hal *ah); extern u_int8_t ar9300_eeprom_set_tx_gain_cap(struct ath_hal *ah, int *tx_gain_max); extern u_int8_t ar9300_eeprom_tx_gain_table_index_max_apply(struct ath_hal *ah, u_int16_t channel); /* Common EEPROM Help function */ extern void ar9300_set_immunity(struct ath_hal *ah, HAL_BOOL enable); extern void ar9300_get_hw_hangs(struct ath_hal *ah, hal_hw_hangs_t *hangs); extern u_int ar9300_mac_to_clks(struct ath_hal *ah, u_int clks); /* tx_bf interface */ #define ar9300_init_txbf(ah) #define ar9300_set_11n_txbf_sounding(ah, ds, series, cec, opt) #define ar9300_set_11n_txbf_cal(ah, ds, cal_pos, code_rate, cec, opt) #define ar9300_txbf_save_cv_from_compress( \ ah, key_idx, mimo_control, compress_rpt) \ false #define ar9300_txbf_save_cv_from_non_compress( \ ah, key_idx, mimo_control, non_compress_rpt) \ false #define ar9300_txbf_rc_update( \ ah, rx_status, local_h, csi_frame, ness_a, ness_b, bw) \ false #define ar9300_fill_csi_frame( \ ah, rx_status, bandwidth, local_h, csi_frame_body) \ 0 #define ar9300_fill_txbf_capabilities(ah) #define ar9300_get_txbf_capabilities(ah) NULL #define ar9300_txbf_set_key( \ ah, entry, rx_staggered_sounding, channel_estimation_cap, mmss) #define ar9300_read_key_cache_mac(ah, entry, mac) false #define ar9300_txbf_get_cv_cache_nr(ah, key_idx, nr) #define ar9300_set_selfgenrate_limit(ah, ts_ratecode) #define ar9300_reset_lowest_txrate(ah) #define ar9300_txbf_set_basic_set(ah) extern void ar9300_crdc_rx_notify(struct ath_hal *ah, struct ath_rx_status *rxs); extern void ar9300_chain_rssi_diff_compensation(struct ath_hal *ah); #if ATH_SUPPORT_MCI extern void ar9300_mci_bt_coex_set_weights(struct ath_hal *ah, u_int32_t stomp_type); extern void ar9300_mci_bt_coex_disable(struct ath_hal *ah); extern int ar9300_mci_bt_coex_enable(struct ath_hal *ah); extern void ar9300_mci_setup (struct ath_hal *ah, u_int32_t gpm_addr, void *gpm_buf, u_int16_t len, u_int32_t sched_addr); extern void ar9300_mci_remote_reset(struct ath_hal *ah, HAL_BOOL wait_done); extern void ar9300_mci_send_lna_transfer(struct ath_hal *ah, HAL_BOOL wait_done); extern void ar9300_mci_send_sys_waking(struct ath_hal *ah, HAL_BOOL wait_done); extern HAL_BOOL ar9300_mci_send_message (struct ath_hal *ah, u_int8_t header, u_int32_t flag, u_int32_t *payload, u_int8_t len, HAL_BOOL wait_done, HAL_BOOL check_bt); extern u_int32_t ar9300_mci_get_interrupt (struct ath_hal *ah, u_int32_t *mci_int, u_int32_t *mci_int_rx_msg); extern u_int32_t ar9300_mci_state (struct ath_hal *ah, u_int32_t state_type, u_int32_t *p_data); extern void ar9300_mci_reset (struct ath_hal *ah, HAL_BOOL en_int, HAL_BOOL is_2g, HAL_BOOL is_full_sleep); extern void ar9300_mci_send_coex_halt_bt_gpm(struct ath_hal *ah, HAL_BOOL halt, HAL_BOOL wait_done); extern void ar9300_mci_mute_bt(struct ath_hal *ah); extern u_int32_t ar9300_mci_wait_for_gpm(struct ath_hal *ah, u_int8_t gpm_type, u_int8_t gpm_opcode, int32_t time_out); extern void ar9300_mci_enable_interrupt(struct ath_hal *ah); extern void ar9300_mci_disable_interrupt(struct ath_hal *ah); extern void ar9300_mci_detach (struct ath_hal *ah); extern u_int32_t ar9300_mci_check_int (struct ath_hal *ah, u_int32_t ints); extern void ar9300_mci_sync_bt_state (struct ath_hal *ah); extern void ar9300_mci_2g5g_changed(struct ath_hal *ah, HAL_BOOL is_2g); extern void ar9300_mci_2g5g_switch(struct ath_hal *ah, HAL_BOOL wait_done); #if ATH_SUPPORT_AIC extern u_int32_t ar9300_aic_calibration (struct ath_hal *ah); extern u_int32_t ar9300_aic_start_normal (struct ath_hal *ah); #endif #endif extern HAL_STATUS ar9300_set_proxy_sta(struct ath_hal *ah, HAL_BOOL enable); extern HAL_BOOL ar9300_regulatory_domain_override( struct ath_hal *ah, u_int16_t regdmn); #if ATH_ANT_DIV_COMB extern void ar9300_ant_div_comb_get_config(struct ath_hal *ah, HAL_ANT_COMB_CONFIG* div_comb_conf); extern void ar9300_ant_div_comb_set_config(struct ath_hal *ah, HAL_ANT_COMB_CONFIG* div_comb_conf); #endif /* ATH_ANT_DIV_COMB */ extern void ar9300_disable_phy_restart(struct ath_hal *ah, int disable_phy_restart); extern void ar9300_enable_keysearch_always(struct ath_hal *ah, int enable); extern HAL_BOOL ar9300ForceVCS( struct ath_hal *ah); extern HAL_BOOL ar9300SetDfs3StreamFix(struct ath_hal *ah, u_int32_t val); extern HAL_BOOL ar9300Get3StreamSignature( struct ath_hal *ah); #ifdef ATH_TX99_DIAG #ifndef ATH_SUPPORT_HTC extern void ar9300_tx99_channel_pwr_update(struct ath_hal *ah, struct ieee80211_channel *c, u_int32_t txpower); extern void ar9300_tx99_chainmsk_setup(struct ath_hal *ah, int tx_chainmask); extern void ar9300_tx99_set_single_carrier(struct ath_hal *ah, int tx_chain_mask, int chtype); extern void ar9300_tx99_start(struct ath_hal *ah, u_int8_t *data); extern void ar9300_tx99_stop(struct ath_hal *ah); #endif /* ATH_SUPPORT_HTC */ #endif /* ATH_TX99_DIAG */ +extern HAL_BOOL ar9300_set_ctl_pwr(struct ath_hal *ah, u_int8_t *ctl_array); +extern void ar9300_set_txchainmaskopt(struct ath_hal *ah, u_int8_t mask); enum { AR9300_COEFF_TX_TYPE = 0, AR9300_COEFF_RX_TYPE }; #endif /* _ATH_AR9300_H_ */ Index: projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_attach.c =================================================================== --- projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_attach.c (revision 278776) +++ projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_attach.c (revision 278777) @@ -1,4129 +1,4148 @@ /* * Copyright (c) 2013 Qualcomm Atheros, Inc. * * 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 "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ah_devid.h" #include "ar9300/ar9300desc.h" #include "ar9300/ar9300.h" #include "ar9300/ar9300reg.h" #include "ar9300/ar9300phy.h" #include "ar9300/ar9300paprd.h" #include "ar9300/ar9300_stub.h" #include "ar9300/ar9300_stub_funcs.h" /* Add static register initialization vectors */ #include "ar9300/ar9300_osprey22.ini" #include "ar9300/ar9330_11.ini" #include "ar9300/ar9330_12.ini" #include "ar9300/ar9340.ini" #include "ar9300/ar9485.ini" #include "ar9300/ar9485_1_1.ini" #include "ar9300/ar9300_jupiter10.ini" #include "ar9300/ar9300_jupiter20.ini" #include "ar9300/ar9580.ini" #include "ar9300/ar955x.ini" #include "ar9300/ar9300_aphrodite10.ini" /* Include various freebsd specific HAL methods */ #include "ar9300/ar9300_freebsd.h" /* XXX duplicate in ar9300_radio.c ? */ static HAL_BOOL ar9300_get_chip_power_limits(struct ath_hal *ah, struct ieee80211_channel *chan); static inline HAL_STATUS ar9300_init_mac_addr(struct ath_hal *ah); static inline HAL_STATUS ar9300_hw_attach(struct ath_hal *ah); static inline void ar9300_hw_detach(struct ath_hal *ah); static int16_t ar9300_get_nf_adjust(struct ath_hal *ah, const HAL_CHANNEL_INTERNAL *c); #if 0 int ar9300_get_cal_intervals(struct ath_hal *ah, HAL_CALIBRATION_TIMER **timerp, HAL_CAL_QUERY query); #endif #if ATH_TRAFFIC_FAST_RECOVER unsigned long ar9300_get_pll3_sqsum_dvc(struct ath_hal *ah); #endif static int ar9300_init_offsets(struct ath_hal *ah, u_int16_t devid); static void ar9300_disable_pcie_phy(struct ath_hal *ah); static const HAL_PERCAL_DATA iq_cal_single_sample = {IQ_MISMATCH_CAL, MIN_CAL_SAMPLES, PER_MAX_LOG_COUNT, ar9300_iq_cal_collect, ar9300_iq_calibration}; #if 0 static HAL_CALIBRATION_TIMER ar9300_cals[] = { {IQ_MISMATCH_CAL, /* Cal type */ 1200000, /* Cal interval */ 0 /* Cal timestamp */ }, {TEMP_COMP_CAL, 5000, 0 }, }; #endif #if ATH_PCIE_ERROR_MONITOR int ar9300_start_pcie_error_monitor(struct ath_hal *ah, int b_auto_stop) { u_int32_t val; /* Clear the counters */ OS_REG_WRITE(ah, PCIE_CO_ERR_CTR_CTR0, 0); OS_REG_WRITE(ah, PCIE_CO_ERR_CTR_CTR1, 0); /* Read the previous value */ val = OS_REG_READ(ah, PCIE_CO_ERR_CTR_CTRL); /* Set auto_stop */ if (b_auto_stop) { val |= RCVD_ERR_CTR_AUTO_STOP | BAD_TLP_ERR_CTR_AUTO_STOP | BAD_DLLP_ERR_CTR_AUTO_STOP | RPLY_TO_ERR_CTR_AUTO_STOP | RPLY_NUM_RO_ERR_CTR_AUTO_STOP; } else { val &= ~( RCVD_ERR_CTR_AUTO_STOP | BAD_TLP_ERR_CTR_AUTO_STOP | BAD_DLLP_ERR_CTR_AUTO_STOP | RPLY_TO_ERR_CTR_AUTO_STOP | RPLY_NUM_RO_ERR_CTR_AUTO_STOP); } OS_REG_WRITE(ah, PCIE_CO_ERR_CTR_CTRL, val ); /* * Start to run. * This has to be done separately from the above auto_stop flag setting, * to avoid a HW race condition. */ val |= RCVD_ERR_CTR_RUN | BAD_TLP_ERR_CTR_RUN | BAD_DLLP_ERR_CTR_RUN | RPLY_TO_ERR_CTR_RUN | RPLY_NUM_RO_ERR_CTR_RUN; OS_REG_WRITE(ah, PCIE_CO_ERR_CTR_CTRL, val); return 0; } int ar9300_read_pcie_error_monitor(struct ath_hal *ah, void* p_read_counters) { u_int32_t val; ar_pcie_error_moniter_counters *p_counters = (ar_pcie_error_moniter_counters*) p_read_counters; val = OS_REG_READ(ah, PCIE_CO_ERR_CTR_CTR0); p_counters->uc_receiver_errors = MS(val, RCVD_ERR_MASK); p_counters->uc_bad_tlp_errors = MS(val, BAD_TLP_ERR_MASK); p_counters->uc_bad_dllp_errors = MS(val, BAD_DLLP_ERR_MASK); val = OS_REG_READ(ah, PCIE_CO_ERR_CTR_CTR1); p_counters->uc_replay_timeout_errors = MS(val, RPLY_TO_ERR_MASK); p_counters->uc_replay_number_rollover_errors= MS(val, RPLY_NUM_RO_ERR_MASK); return 0; } int ar9300_stop_pcie_error_monitor(struct ath_hal *ah) { u_int32_t val; /* Read the previous value */ val = OS_REG_READ(ah, PCIE_CO_ERR_CTR_CTRL); val &= ~( RCVD_ERR_CTR_RUN | BAD_TLP_ERR_CTR_RUN | BAD_DLLP_ERR_CTR_RUN | RPLY_TO_ERR_CTR_RUN | RPLY_NUM_RO_ERR_CTR_RUN); /* Start to stop */ OS_REG_WRITE(ah, PCIE_CO_ERR_CTR_CTRL, val ); return 0; } #endif /* ATH_PCIE_ERROR_MONITOR */ #if 0 /* WIN32 does not support C99 */ static const struct ath_hal_private ar9300hal = { { ar9300_get_rate_table, /* ah_get_rate_table */ ar9300_detach, /* ah_detach */ /* Reset Functions */ ar9300_reset, /* ah_reset */ ar9300_phy_disable, /* ah_phy_disable */ ar9300_disable, /* ah_disable */ ar9300_config_pci_power_save, /* ah_config_pci_power_save */ ar9300_set_pcu_config, /* ah_set_pcu_config */ ar9300_calibration, /* ah_per_calibration */ ar9300_reset_cal_valid, /* ah_reset_cal_valid */ ar9300_set_tx_power_limit, /* ah_set_tx_power_limit */ #if ATH_ANT_DIV_COMB ar9300_ant_ctrl_set_lna_div_use_bt_ant, /* ah_ant_ctrl_set_lna_div_use_bt_ant */ #endif /* ATH_ANT_DIV_COMB */ #ifdef ATH_SUPPORT_DFS ar9300_radar_wait, /* ah_radar_wait */ /* New DFS functions */ ar9300_check_dfs, /* ah_ar_check_dfs */ ar9300_dfs_found, /* ah_ar_dfs_found */ ar9300_enable_dfs, /* ah_ar_enable_dfs */ ar9300_get_dfs_thresh, /* ah_ar_get_dfs_thresh */ ar9300_get_dfs_radars, /* ah_ar_get_dfs_radars */ ar9300_adjust_difs, /* ah_adjust_difs */ ar9300_dfs_config_fft, /* ah_dfs_config_fft */ ar9300_dfs_cac_war, /* ah_dfs_cac_war */ ar9300_cac_tx_quiet, /* ah_cac_tx_quiet */ #endif ar9300_get_extension_channel, /* ah_get_extension_channel */ ar9300_is_fast_clock_enabled, /* ah_is_fast_clock_enabled */ /* Transmit functions */ ar9300_update_tx_trig_level, /* ah_update_tx_trig_level */ ar9300_get_tx_trig_level, /* ah_get_tx_trig_level */ ar9300_setup_tx_queue, /* ah_setup_tx_queue */ ar9300_set_tx_queue_props, /* ah_set_tx_queue_props */ ar9300_get_tx_queue_props, /* ah_get_tx_queue_props */ ar9300_release_tx_queue, /* ah_release_tx_queue */ ar9300_reset_tx_queue, /* ah_reset_tx_queue */ ar9300_get_tx_dp, /* ah_get_tx_dp */ ar9300_set_tx_dp, /* ah_set_tx_dp */ ar9300_num_tx_pending, /* ah_num_tx_pending */ ar9300_start_tx_dma, /* ah_start_tx_dma */ ar9300_stop_tx_dma, /* ah_stop_tx_dma */ ar9300_stop_tx_dma_indv_que, /* ah_stop_tx_dma_indv_que */ ar9300_abort_tx_dma, /* ah_abort_tx_dma */ ar9300_fill_tx_desc, /* ah_fill_tx_desc */ ar9300_set_desc_link, /* ah_set_desc_link */ ar9300_get_desc_link_ptr, /* ah_get_desc_link_ptr */ ar9300_clear_tx_desc_status, /* ah_clear_tx_desc_status */ #ifdef ATH_SWRETRY ar9300_clear_dest_mask, /* ah_clear_dest_mask */ #endif ar9300_proc_tx_desc, /* ah_proc_tx_desc */ ar9300_get_raw_tx_desc, /* ah_get_raw_tx_desc */ ar9300_get_tx_rate_code, /* ah_get_tx_rate_code */ AH_NULL, /* ah_get_tx_intr_queue */ ar9300_tx_req_intr_desc, /* ah_req_tx_intr_desc */ ar9300_calc_tx_airtime, /* ah_calc_tx_airtime */ ar9300_setup_tx_status_ring, /* ah_setup_tx_status_ring */ /* RX Functions */ ar9300_get_rx_dp, /* ah_get_rx_dp */ ar9300_set_rx_dp, /* ah_set_rx_dp */ ar9300_enable_receive, /* ah_enable_receive */ ar9300_stop_dma_receive, /* ah_stop_dma_receive */ ar9300_start_pcu_receive, /* ah_start_pcu_receive */ ar9300_stop_pcu_receive, /* ah_stop_pcu_receive */ ar9300_set_multicast_filter, /* ah_set_multicast_filter */ ar9300_get_rx_filter, /* ah_get_rx_filter */ ar9300_set_rx_filter, /* ah_set_rx_filter */ ar9300_set_rx_sel_evm, /* ah_set_rx_sel_evm */ ar9300_set_rx_abort, /* ah_set_rx_abort */ AH_NULL, /* ah_setup_rx_desc */ ar9300_proc_rx_desc, /* ah_proc_rx_desc */ ar9300_get_rx_key_idx, /* ah_get_rx_key_idx */ ar9300_proc_rx_desc_fast, /* ah_proc_rx_desc_fast */ ar9300_ani_ar_poll, /* ah_rx_monitor */ ar9300_process_mib_intr, /* ah_proc_mib_event */ /* Misc Functions */ ar9300_get_capability, /* ah_get_capability */ ar9300_set_capability, /* ah_set_capability */ ar9300_get_diag_state, /* ah_get_diag_state */ ar9300_get_mac_address, /* ah_get_mac_address */ ar9300_set_mac_address, /* ah_set_mac_address */ ar9300_get_bss_id_mask, /* ah_get_bss_id_mask */ ar9300_set_bss_id_mask, /* ah_set_bss_id_mask */ ar9300_set_regulatory_domain, /* ah_set_regulatory_domain */ ar9300_set_led_state, /* ah_set_led_state */ ar9300_set_power_led_state, /* ah_setpowerledstate */ ar9300_set_network_led_state, /* ah_setnetworkledstate */ ar9300_write_associd, /* ah_write_associd */ ar9300_force_tsf_sync, /* ah_force_tsf_sync */ ar9300_gpio_cfg_input, /* ah_gpio_cfg_input */ ar9300_gpio_cfg_output, /* ah_gpio_cfg_output */ ar9300_gpio_cfg_output_led_off, /* ah_gpio_cfg_output_led_off */ ar9300_gpio_get, /* ah_gpio_get */ ar9300_gpio_set, /* ah_gpio_set */ ar9300_gpio_get_intr, /* ah_gpio_get_intr */ ar9300_gpio_set_intr, /* ah_gpio_set_intr */ ar9300_gpio_get_polarity, /* ah_gpio_get_polarity */ ar9300_gpio_set_polarity, /* ah_gpio_set_polarity */ ar9300_gpio_get_mask, /* ah_gpio_get_mask */ ar9300_gpio_set_mask, /* ah_gpio_set_mask */ ar9300_get_tsf32, /* ah_get_tsf32 */ ar9300_get_tsf64, /* ah_get_tsf64 */ ar9300_get_tsf2_32, /* ah_get_tsf2_32 */ ar9300_reset_tsf, /* ah_reset_tsf */ ar9300_detect_card_present, /* ah_detect_card_present */ ar9300_update_mib_mac_stats, /* ah_update_mib_mac_stats */ ar9300_get_mib_mac_stats, /* ah_get_mib_mac_stats */ ar9300_get_rfgain, /* ah_get_rf_gain */ ar9300_get_def_antenna, /* ah_get_def_antenna */ ar9300_set_def_antenna, /* ah_set_def_antenna */ ar9300_set_slot_time, /* ah_set_slot_time */ ar9300_set_ack_timeout, /* ah_set_ack_timeout */ ar9300_get_ack_timeout, /* ah_get_ack_timeout */ ar9300_set_coverage_class, /* ah_set_coverage_class */ ar9300_set_quiet, /* ah_set_quiet */ ar9300_set_antenna_switch, /* ah_set_antenna_switch */ ar9300_get_desc_info, /* ah_get_desc_info */ ar9300_select_ant_config, /* ah_select_ant_config */ ar9300_ant_ctrl_common_get, /* ah_ant_ctrl_common_get */ + ar9300_ant_swcom_sel, /* ah_ant_swcom_sel */ ar9300_enable_tpc, /* ah_enable_tpc */ AH_NULL, /* ah_olpc_temp_compensation */ #if ATH_SUPPORT_CRDC ar9300_chain_rssi_diff_compensation,/*ah_chain_rssi_diff_compensation*/ #endif ar9300_disable_phy_restart, /* ah_disable_phy_restart */ ar9300_enable_keysearch_always, ar9300_interference_is_present, /* ah_interference_is_present */ ar9300_disp_tpc_tables, /* ah_disp_tpc_tables */ ar9300_get_tpc_tables, /* ah_get_tpc_tables */ /* Key Cache Functions */ ar9300_get_key_cache_size, /* ah_get_key_cache_size */ ar9300_reset_key_cache_entry, /* ah_reset_key_cache_entry */ ar9300_is_key_cache_entry_valid, /* ah_is_key_cache_entry_valid */ ar9300_set_key_cache_entry, /* ah_set_key_cache_entry */ ar9300_set_key_cache_entry_mac, /* ah_set_key_cache_entry_mac */ ar9300_print_keycache, /* ah_print_key_cache */ - +#if ATH_SUPPORT_KEYPLUMB_WAR + ar9300_check_key_cache_entry, /* ah_check_key_cache_entry */ +#endif /* Power Management Functions */ ar9300_set_power_mode, /* ah_set_power_mode */ ar9300_set_sm_power_mode, /* ah_set_sm_ps_mode */ #if ATH_WOW ar9300_wow_apply_pattern, /* ah_wow_apply_pattern */ ar9300_wow_enable, /* ah_wow_enable */ ar9300_wow_wake_up, /* ah_wow_wake_up */ #if ATH_WOW_OFFLOAD ar9300_wowoffload_prep, /* ah_wow_offload_prep */ ar9300_wowoffload_post, /* ah_wow_offload_post */ ar9300_wowoffload_download_rekey_data, /* ah_wow_offload_download_rekey_data */ ar9300_wowoffload_retrieve_data, /* ah_wow_offload_retrieve_data */ ar9300_wowoffload_download_acer_magic, /* ah_wow_offload_download_acer_magic */ ar9300_wowoffload_download_acer_swka, /* ah_wow_offload_download_acer_swka */ ar9300_wowoffload_download_arp_info, /* ah_wow_offload_download_arp_info */ ar9300_wowoffload_download_ns_info, /* ah_wow_offload_download_ns_info */ #endif /* ATH_WOW_OFFLOAD */ #endif /* Get Channel Noise */ ath_hal_get_chan_noise, /* ah_get_chan_noise */ ar9300_chain_noise_floor, /* ah_get_chain_noise_floor */ + ar9300_get_nf_from_reg, /* ah_get_nf_from_reg */ + ar9300_get_rx_nf_offset, /* ah_get_rx_nf_offset */ /* Beacon Functions */ ar9300_beacon_init, /* ah_beacon_init */ ar9300_set_sta_beacon_timers, /* ah_set_station_beacon_timers */ /* Interrupt Functions */ ar9300_is_interrupt_pending, /* ah_is_interrupt_pending */ ar9300_get_pending_interrupts, /* ah_get_pending_interrupts */ ar9300_get_interrupts, /* ah_get_interrupts */ ar9300_set_interrupts, /* ah_set_interrupts */ ar9300_set_intr_mitigation_timer, /* ah_set_intr_mitigation_timer */ ar9300_get_intr_mitigation_timer, /* ah_get_intr_mitigation_timer */ ar9300ForceVCS, ar9300SetDfs3StreamFix, ar9300Get3StreamSignature, /* 11n specific functions (NOT applicable to ar9300) */ ar9300_set_11n_tx_desc, /* ah_set_11n_tx_desc */ /* Update rxchain */ ar9300_set_rx_chainmask, /*ah_set_rx_chainmask*/ /*Updating locationing register */ ar9300_update_loc_ctl_reg, /*ah_update_loc_ctl_reg*/ /* Start PAPRD functions */ ar9300_set_paprd_tx_desc, /* ah_set_paprd_tx_desc */ ar9300_paprd_init_table, /* ah_paprd_init_table */ ar9300_paprd_setup_gain_table, /* ah_paprd_setup_gain_table */ ar9300_paprd_create_curve, /* ah_paprd_create_curve */ ar9300_paprd_is_done, /* ah_paprd_is_done */ ar9300_enable_paprd, /* ah_PAPRDEnable */ ar9300_populate_paprd_single_table,/* ah_paprd_populate_table */ ar9300_is_tx_done, /* ah_is_tx_done */ ar9300_paprd_dec_tx_pwr, /* ah_paprd_dec_tx_pwr*/ ar9300_paprd_thermal_send, /* ah_paprd_thermal_send */ /* End PAPRD functions */ ar9300_set_11n_rate_scenario, /* ah_set_11n_rate_scenario */ ar9300_set_11n_aggr_first, /* ah_set_11n_aggr_first */ ar9300_set_11n_aggr_middle, /* ah_set_11n_aggr_middle */ ar9300_set_11n_aggr_last, /* ah_set_11n_aggr_last */ ar9300_clr_11n_aggr, /* ah_clr_11n_aggr */ ar9300_set_11n_rifs_burst_middle, /* ah_set_11n_rifs_burst_middle */ ar9300_set_11n_rifs_burst_last, /* ah_set_11n_rifs_burst_last */ ar9300_clr_11n_rifs_burst, /* ah_clr_11n_rifs_burst */ ar9300_set_11n_aggr_rifs_burst, /* ah_set_11n_aggr_rifs_burst */ ar9300_set_11n_rx_rifs, /* ah_set_11n_rx_rifs */ ar9300_set_smart_antenna, /* ah_setSmartAntenna */ ar9300_detect_bb_hang, /* ah_detect_bb_hang */ ar9300_detect_mac_hang, /* ah_detect_mac_hang */ ar9300_set_immunity, /* ah_immunity */ ar9300_get_hw_hangs, /* ah_get_hang_types */ ar9300_set_11n_burst_duration, /* ah_set_11n_burst_duration */ ar9300_set_11n_virtual_more_frag, /* ah_set_11n_virtual_more_frag */ ar9300_get_11n_ext_busy, /* ah_get_11n_ext_busy */ ar9300_set_11n_mac2040, /* ah_set_11n_mac2040 */ ar9300_get_11n_rx_clear, /* ah_get_11n_rx_clear */ ar9300_set_11n_rx_clear, /* ah_set_11n_rx_clear */ ar9300_get_mib_cycle_counts_pct, /* ah_get_mib_cycle_counts_pct */ ar9300_dma_reg_dump, /* ah_dma_reg_dump */ /* force_ppm specific functions */ ar9300_ppm_get_rssi_dump, /* ah_ppm_get_rssi_dump */ ar9300_ppm_arm_trigger, /* ah_ppm_arm_trigger */ ar9300_ppm_get_trigger, /* ah_ppm_get_trigger */ ar9300_ppm_force, /* ah_ppm_force */ ar9300_ppm_un_force, /* ah_ppm_un_force */ ar9300_ppm_get_force_state, /* ah_ppm_get_force_state */ ar9300_get_spur_info, /* ah_get_spur_info */ ar9300_set_spur_info, /* ah_get_spur_info */ ar9300_get_min_cca_pwr, /* ah_ar_get_noise_floor_val */ ar9300_green_ap_ps_on_off, /* ah_set_rx_green_ap_ps_on_off */ ar9300_is_single_ant_power_save_possible, /* ah_is_single_ant_power_save_possible */ /* radio measurement specific functions */ ar9300_get_mib_cycle_counts, /* ah_get_mib_cycle_counts */ ar9300_get_vow_stats, /* ah_get_vow_stats */ ar9300_clear_mib_counters, /* ah_clear_mib_counters */ #if ATH_GEN_RANDOMNESS ar9300_get_rssi_chain0, /* ah_get_rssi_chain0 */ #endif #ifdef ATH_BT_COEX /* Bluetooth Coexistence functions */ ar9300_set_bt_coex_info, /* ah_set_bt_coex_info */ ar9300_bt_coex_config, /* ah_bt_coex_config */ ar9300_bt_coex_set_qcu_thresh, /* ah_bt_coex_set_qcu_thresh */ ar9300_bt_coex_set_weights, /* ah_bt_coex_set_weights */ ar9300_bt_coex_setup_bmiss_thresh, /* ah_bt_coex_set_bmiss_thresh */ ar9300_bt_coex_set_parameter, /* ah_bt_coex_set_parameter */ ar9300_bt_coex_disable, /* ah_bt_coex_disable */ ar9300_bt_coex_enable, /* ah_bt_coex_enable */ ar9300_get_bt_active_gpio, /* ah_bt_coex_info*/ ar9300_get_wlan_active_gpio, /* ah__coex_wlan_info*/ #endif /* Generic Timer functions */ ar9300_alloc_generic_timer, /* ah_gentimer_alloc */ ar9300_free_generic_timer, /* ah_gentimer_free */ ar9300_start_generic_timer, /* ah_gentimer_start */ ar9300_stop_generic_timer, /* ah_gentimer_stop */ ar9300_get_gen_timer_interrupts, /* ah_gentimer_get_intr */ ar9300_set_dcs_mode, /* ah_set_dcs_mode */ ar9300_get_dcs_mode, /* ah_get_dcs_mode */ #if ATH_ANT_DIV_COMB ar9300_ant_div_comb_get_config, /* ah_get_ant_dvi_comb_conf */ ar9300_ant_div_comb_set_config, /* ah_set_ant_dvi_comb_conf */ #endif ar9300_get_bb_panic_info, /* ah_get_bb_panic_info */ ar9300_handle_radar_bb_panic, /* ah_handle_radar_bb_panic */ ar9300_set_hal_reset_reason, /* ah_set_hal_reset_reason */ #if ATH_PCIE_ERROR_MONITOR ar9300_start_pcie_error_monitor, /* ah_start_pcie_error_monitor */ ar9300_read_pcie_error_monitor, /* ah_read_pcie_error_monitor*/ ar9300_stop_pcie_error_monitor, /* ah_stop_pcie_error_monitor*/ #endif /* ATH_PCIE_ERROR_MONITOR */ #if ATH_SUPPORT_SPECTRAL /* Spectral scan */ ar9300_configure_spectral_scan, /* ah_ar_configure_spectral */ ar9300_get_spectral_params, /* ah_ar_get_spectral_config */ ar9300_start_spectral_scan, /* ah_ar_start_spectral_scan */ ar9300_stop_spectral_scan, /* ah_ar_stop_spectral_scan */ ar9300_is_spectral_enabled, /* ah_ar_is_spectral_enabled */ ar9300_is_spectral_active, /* ah_ar_is_spectral_active */ ar9300_get_ctl_chan_nf, /* ah_ar_get_ctl_nf */ ar9300_get_ext_chan_nf, /* ah_ar_get_ext_nf */ #endif /* ATH_SUPPORT_SPECTRAL */ ar9300_promisc_mode, /* ah_promisc_mode */ ar9300_read_pktlog_reg, /* ah_read_pktlog_reg */ ar9300_write_pktlog_reg, /* ah_write_pktlog_reg */ ar9300_set_proxy_sta, /* ah_set_proxy_sta */ ar9300_get_cal_intervals, /* ah_get_cal_intervals */ #if ATH_TRAFFIC_FAST_RECOVER ar9300_get_pll3_sqsum_dvc, /* ah_get_pll3_sqsum_dvc */ #endif #ifdef ATH_SUPPORT_HTC AH_NULL, #endif #ifdef ATH_TX99_DIAG /* Tx99 functions */ #ifdef ATH_SUPPORT_HTC AH_NULL, AH_NULL, AH_NULL, AH_NULL, AH_NULL, AH_NULL, AH_NULL, #else AH_NULL, AH_NULL, - ar9300TX99TgtChannelPwrUpdate, /* ah_tx99channelpwrupdate */ - ar9300TX99TgtStart, /* ah_tx99start */ - ar9300TX99TgtStop, /* ah_tx99stop */ - ar9300TX99TgtChainmskSetup, /* ah_tx99_chainmsk_setup */ - ar9300TX99SetSingleCarrier, /* ah_tx99_set_single_carrier */ + ar9300_tx99_channel_pwr_update, /* ah_tx99channelpwrupdate */ + ar9300_tx99_start, /* ah_tx99start */ + ar9300_tx99_stop, /* ah_tx99stop */ + ar9300_tx99_chainmsk_setup, /* ah_tx99_chainmsk_setup */ + ar9300_tx99_set_single_carrier, /* ah_tx99_set_single_carrier */ #endif #endif ar9300_chk_rssi_update_tx_pwr, ar9300_is_skip_paprd_by_greentx, /* ah_is_skip_paprd_by_greentx */ ar9300_hwgreentx_set_pal_spare, /* ah_hwgreentx_set_pal_spare */ #if ATH_SUPPORT_MCI /* MCI Coexistence Functions */ ar9300_mci_setup, /* ah_mci_setup */ ar9300_mci_send_message, /* ah_mci_send_message */ ar9300_mci_get_interrupt, /* ah_mci_get_interrupt */ ar9300_mci_state, /* ah_mci_state */ ar9300_mci_detach, /* ah_mci_detach */ #endif ar9300_reset_hw_beacon_proc_crc, /* ah_reset_hw_beacon_proc_crc */ ar9300_get_hw_beacon_rssi, /* ah_get_hw_beacon_rssi */ ar9300_set_hw_beacon_rssi_threshold,/*ah_set_hw_beacon_rssi_threshold*/ ar9300_reset_hw_beacon_rssi, /* ah_reset_hw_beacon_rssi */ ar9300_mat_enable, /* ah_mat_enable */ ar9300_dump_keycache, /* ah_dump_keycache */ ar9300_is_ani_noise_spur, /* ah_is_ani_noise_spur */ ar9300_set_hw_beacon_proc, /* ah_set_hw_beacon_proc */ + ar9300_set_ctl_pwr, /* ah_set_ctl_pwr */ + ar9300_set_txchainmaskopt, /* ah_set_txchainmaskopt */ }, ar9300_get_channel_edges, /* ah_get_channel_edges */ ar9300_get_wireless_modes, /* ah_get_wireless_modes */ ar9300_eeprom_read_word, /* ah_eeprom_read */ AH_NULL, ar9300_eeprom_dump_support, /* ah_eeprom_dump */ ar9300_get_chip_power_limits, /* ah_get_chip_power_limits */ ar9300_get_nf_adjust, /* ah_get_nf_adjust */ /* rest is zero'd by compiler */ }; #endif /* * Read MAC version/revision information from Chip registers and initialize * local data structures. */ void ar9300_read_revisions(struct ath_hal *ah) { u_int32_t val; /* XXX verify if this is the correct way to read revision on Osprey */ /* new SREV format for Sowl and later */ val = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_SREV)); if (AH_PRIVATE(ah)->ah_devid == AR9300_DEVID_AR9340) { /* XXX: AR_SREV register in Wasp reads 0 */ AH_PRIVATE(ah)->ah_macVersion = AR_SREV_VERSION_WASP; } else if(AH_PRIVATE(ah)->ah_devid == AR9300_DEVID_QCA955X) { /* XXX: AR_SREV register in Scorpion reads 0 */ AH_PRIVATE(ah)->ah_macVersion = AR_SREV_VERSION_SCORPION; } else { /* * Include 6-bit Chip Type (masked to 0) * to differentiate from pre-Sowl versions */ AH_PRIVATE(ah)->ah_macVersion = (val & AR_SREV_VERSION2) >> AR_SREV_TYPE2_S; } #ifdef AH_SUPPORT_HORNET /* * EV74984, due to Hornet 1.1 didn't update WMAC revision, * so that have to read SoC's revision ID instead */ if (AH_PRIVATE(ah)->ah_macVersion == AR_SREV_VERSION_HORNET) { #define AR_SOC_RST_REVISION_ID 0xB8060090 #define REG_READ(_reg) *((volatile u_int32_t *)(_reg)) if ((REG_READ(AR_SOC_RST_REVISION_ID) & AR_SREV_REVISION_HORNET_11_MASK) == AR_SREV_REVISION_HORNET_11) { AH_PRIVATE(ah)->ah_macRev = AR_SREV_REVISION_HORNET_11; } else { AH_PRIVATE(ah)->ah_macRev = MS(val, AR_SREV_REVISION2); } #undef REG_READ #undef AR_SOC_RST_REVISION_ID } else #endif if (AH_PRIVATE(ah)->ah_macVersion == AR_SREV_VERSION_WASP) { #define AR_SOC_RST_REVISION_ID 0xB8060090 #define REG_READ(_reg) *((volatile u_int32_t *)(_reg)) AH_PRIVATE(ah)->ah_macRev = REG_READ(AR_SOC_RST_REVISION_ID) & AR_SREV_REVISION_WASP_MASK; #undef REG_READ #undef AR_SOC_RST_REVISION_ID } else AH_PRIVATE(ah)->ah_macRev = MS(val, AR_SREV_REVISION2); if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) { AH_PRIVATE(ah)->ah_ispcie = AH_TRUE; } else { AH_PRIVATE(ah)->ah_ispcie = (val & AR_SREV_TYPE2_HOST_MODE) ? 0 : 1; } } /* * Attach for an AR9300 part. */ struct ath_hal * ar9300_attach(u_int16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, HAL_OPS_CONFIG *ah_config, HAL_STATUS *status) { struct ath_hal_9300 *ahp; struct ath_hal *ah; struct ath_hal_private *ahpriv; HAL_STATUS ecode; HAL_NO_INTERSPERSED_READS; /* NB: memory is returned zero'd */ ahp = ar9300_new_state(devid, sc, st, sh, eepromdata, ah_config, status); if (ahp == AH_NULL) { return AH_NULL; } ah = &ahp->ah_priv.h; ar9300_init_offsets(ah, devid); ahpriv = AH_PRIVATE(ah); // AH_PRIVATE(ah)->ah_bustype = bustype; /* FreeBSD: to make OTP work for now, provide this.. */ AH9300(ah)->ah_cal_mem = ath_hal_malloc(HOST_CALDATA_SIZE); if (AH9300(ah)->ah_cal_mem == NULL) { ath_hal_printf(ah, "%s: caldata malloc failed!\n", __func__); ecode = HAL_EIO; goto bad; } /* * If eepromdata is not NULL, copy it it into ah_cal_mem. */ if (eepromdata != NULL) OS_MEMCPY(AH9300(ah)->ah_cal_mem, eepromdata, HOST_CALDATA_SIZE); /* XXX FreeBSD: enable RX mitigation */ ah->ah_config.ath_hal_intr_mitigation_rx = 1; /* interrupt mitigation */ #ifdef AR5416_INT_MITIGATION if (ah->ah_config.ath_hal_intr_mitigation_rx != 0) { ahp->ah_intr_mitigation_rx = AH_TRUE; } #else /* Enable Rx mitigation (default) */ ahp->ah_intr_mitigation_rx = AH_TRUE; ah->ah_config.ath_hal_intr_mitigation_rx = 1; #endif #ifdef HOST_OFFLOAD /* Reset default Rx mitigation values for Hornet */ if (AR_SREV_HORNET(ah)) { ahp->ah_intr_mitigation_rx = AH_FALSE; #ifdef AR5416_INT_MITIGATION ah->ah_config.ath_hal_intr_mitigation_rx = 0; #endif } #endif if (ah->ah_config.ath_hal_intr_mitigation_tx != 0) { ahp->ah_intr_mitigation_tx = AH_TRUE; } /* * Read back AR_WA into a permanent copy and set bits 14 and 17. * We need to do this to avoid RMW of this register. * Do this before calling ar9300_set_reset_reg. * If not, the AR_WA register which was inited via EEPROM * will get wiped out. */ ahp->ah_wa_reg_val = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_WA)); /* Set Bits 14 and 17 in the AR_WA register. */ ahp->ah_wa_reg_val |= AR_WA_D3_TO_L1_DISABLE | AR_WA_ASPM_TIMER_BASED_DISABLE; if (!ar9300_set_reset_reg(ah, HAL_RESET_POWER_ON)) { /* reset chip */ HALDEBUG(ah, HAL_DEBUG_RESET, "%s: couldn't reset chip\n", __func__); ecode = HAL_EIO; goto bad; } if (AR_SREV_JUPITER(ah) #if ATH_WOW_OFFLOAD && !HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_SET_4004_BIT14) #endif ) { /* Jupiter doesn't need bit 14 to be set. */ ahp->ah_wa_reg_val &= ~AR_WA_D3_TO_L1_DISABLE; OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA), ahp->ah_wa_reg_val); } #if ATH_SUPPORT_MCI if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) { #if 0 ah->ah_bt_coex_set_weights = ar9300_mci_bt_coex_set_weights; ah->ah_bt_coex_disable = ar9300_mci_bt_coex_disable; ah->ah_bt_coex_enable = ar9300_mci_bt_coex_enable; #endif ahp->ah_mci_ready = AH_FALSE; ahp->ah_mci_bt_state = MCI_BT_SLEEP; ahp->ah_mci_coex_major_version_wlan = MCI_GPM_COEX_MAJOR_VERSION_WLAN; ahp->ah_mci_coex_minor_version_wlan = MCI_GPM_COEX_MINOR_VERSION_WLAN; ahp->ah_mci_coex_major_version_bt = MCI_GPM_COEX_MAJOR_VERSION_DEFAULT; ahp->ah_mci_coex_minor_version_bt = MCI_GPM_COEX_MINOR_VERSION_DEFAULT; ahp->ah_mci_coex_bt_version_known = AH_FALSE; ahp->ah_mci_coex_2g5g_update = AH_TRUE; /* track if 2g5g status sent */ /* will be updated before boot up sequence */ ahp->ah_mci_coex_is_2g = AH_TRUE; ahp->ah_mci_coex_wlan_channels_update = AH_FALSE; ahp->ah_mci_coex_wlan_channels[0] = 0x00000000; ahp->ah_mci_coex_wlan_channels[1] = 0xffffffff; ahp->ah_mci_coex_wlan_channels[2] = 0xffffffff; ahp->ah_mci_coex_wlan_channels[3] = 0x7fffffff; ahp->ah_mci_query_bt = AH_TRUE; /* In case WLAN start after BT */ ahp->ah_mci_unhalt_bt_gpm = AH_TRUE; /* Send UNHALT at beginning */ ahp->ah_mci_halted_bt_gpm = AH_FALSE; /* Allow first HALT */ ahp->ah_mci_need_flush_btinfo = AH_FALSE; ahp->ah_mci_wlan_cal_seq = 0; ahp->ah_mci_wlan_cal_done = 0; } #endif /* ATH_SUPPORT_MCI */ #if ATH_WOW_OFFLOAD ahp->ah_mcast_filter_l32_set = 0; ahp->ah_mcast_filter_u32_set = 0; #endif if (AR_SREV_HORNET(ah)) { #ifdef AH_SUPPORT_HORNET if (!AR_SREV_HORNET_11(ah)) { /* * Do not check bootstrap register, which cannot be trusted * due to s26 switch issue on CUS164/AP121. */ ahp->clk_25mhz = 1; HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE, "Bootstrap clock 25MHz\n"); } else { /* check bootstrap clock setting */ #define AR_SOC_SEL_25M_40M 0xB80600AC #define REG_WRITE(_reg, _val) *((volatile u_int32_t *)(_reg)) = (_val); #define REG_READ(_reg) (*((volatile u_int32_t *)(_reg))) if (REG_READ(AR_SOC_SEL_25M_40M) & 0x1) { ahp->clk_25mhz = 0; HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE, "Bootstrap clock 40MHz\n"); } else { ahp->clk_25mhz = 1; HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE, "Bootstrap clock 25MHz\n"); } #undef REG_READ #undef REG_WRITE #undef AR_SOC_SEL_25M_40M } #endif /* AH_SUPPORT_HORNET */ } if (AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah)) { /* check bootstrap clock setting */ #define AR9340_SOC_SEL_25M_40M 0xB80600B0 #define AR9340_REF_CLK_40 (1 << 4) /* 0 - 25MHz 1 - 40 MHz */ #define REG_READ(_reg) (*((volatile u_int32_t *)(_reg))) if (REG_READ(AR9340_SOC_SEL_25M_40M) & AR9340_REF_CLK_40) { ahp->clk_25mhz = 0; HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE, "Bootstrap clock 40MHz\n"); } else { ahp->clk_25mhz = 1; HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE, "Bootstrap clock 25MHz\n"); } #undef REG_READ #undef AR9340_SOC_SEL_25M_40M #undef AR9340_REF_CLK_40 } ar9300_init_pll(ah, AH_NULL); if (!ar9300_set_power_mode(ah, HAL_PM_AWAKE, AH_TRUE)) { HALDEBUG(ah, HAL_DEBUG_RESET, "%s: couldn't wakeup chip\n", __func__); ecode = HAL_EIO; goto bad; } /* No serialization of Register Accesses needed. */ ah->ah_config.ah_serialise_reg_war = SER_REG_MODE_OFF; HALDEBUG(ah, HAL_DEBUG_RESET, "%s: ah_serialise_reg_war is %d\n", __func__, ah->ah_config.ah_serialise_reg_war); /* * Add mac revision check when needed. * - Osprey 1.0 and 2.0 no longer supported. */ if (((ahpriv->ah_macVersion == AR_SREV_VERSION_OSPREY) && (ahpriv->ah_macRev <= AR_SREV_REVISION_OSPREY_20)) || (ahpriv->ah_macVersion != AR_SREV_VERSION_OSPREY && ahpriv->ah_macVersion != AR_SREV_VERSION_WASP && ahpriv->ah_macVersion != AR_SREV_VERSION_HORNET && ahpriv->ah_macVersion != AR_SREV_VERSION_POSEIDON && ahpriv->ah_macVersion != AR_SREV_VERSION_SCORPION && ahpriv->ah_macVersion != AR_SREV_VERSION_JUPITER && ahpriv->ah_macVersion != AR_SREV_VERSION_APHRODITE) ) { HALDEBUG(ah, HAL_DEBUG_RESET, "%s: Mac Chip Rev 0x%02x.%x is not supported by this driver\n", __func__, ahpriv->ah_macVersion, ahpriv->ah_macRev); ecode = HAL_ENOTSUPP; goto bad; } AH_PRIVATE(ah)->ah_phyRev = OS_REG_READ(ah, AR_PHY_CHIP_ID); /* Setup supported calibrations */ ahp->ah_iq_cal_data.cal_data = &iq_cal_single_sample; ahp->ah_supp_cals = IQ_MISMATCH_CAL; /* Enable ANI */ ahp->ah_ani_function = HAL_ANI_ALL; /* Enable RIFS */ ahp->ah_rifs_enabled = AH_TRUE; + + /* by default, stop RX also in abort txdma, due to + "Unable to stop TxDMA" msg observed */ + ahp->ah_abort_txdma_norx = AH_TRUE; + + /* do not use optional tx chainmask by default */ + ahp->ah_tx_chainmaskopt = 0; + + ahp->ah_skip_rx_iq_cal = AH_FALSE; + ahp->ah_rx_cal_complete = AH_FALSE; + ahp->ah_rx_cal_chan = 0; + ahp->ah_rx_cal_chan_flag = 0; HALDEBUG(ah, HAL_DEBUG_RESET, "%s: This Mac Chip Rev 0x%02x.%x is \n", __func__, ahpriv->ah_macVersion, ahpriv->ah_macRev); if (AR_SREV_HORNET_12(ah)) { /* mac */ INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_PRE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_CORE], ar9331_hornet1_2_mac_core, ARRAY_LENGTH(ar9331_hornet1_2_mac_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_POST], ar9331_hornet1_2_mac_postamble, ARRAY_LENGTH(ar9331_hornet1_2_mac_postamble), 5); /* bb */ INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_PRE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_CORE], ar9331_hornet1_2_baseband_core, ARRAY_LENGTH(ar9331_hornet1_2_baseband_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_POST], ar9331_hornet1_2_baseband_postamble, ARRAY_LENGTH(ar9331_hornet1_2_baseband_postamble), 5); /* radio */ INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_PRE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_CORE], ar9331_hornet1_2_radio_core, ARRAY_LENGTH(ar9331_hornet1_2_radio_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_POST], NULL, 0, 0); /* soc */ INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_PRE], ar9331_hornet1_2_soc_preamble, ARRAY_LENGTH(ar9331_hornet1_2_soc_preamble), 2); INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_CORE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_POST], ar9331_hornet1_2_soc_postamble, ARRAY_LENGTH(ar9331_hornet1_2_soc_postamble), 2); /* rx/tx gain */ INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar9331_common_rx_gain_hornet1_2, ARRAY_LENGTH(ar9331_common_rx_gain_hornet1_2), 2); INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9331_modes_lowest_ob_db_tx_gain_hornet1_2, ARRAY_LENGTH(ar9331_modes_lowest_ob_db_tx_gain_hornet1_2), 5); ah->ah_config.ath_hal_pcie_power_save_enable = 0; /* Japan 2484Mhz CCK settings */ INIT_INI_ARRAY(&ahp->ah_ini_japan2484, ar9331_hornet1_2_baseband_core_txfir_coeff_japan_2484, ARRAY_LENGTH( ar9331_hornet1_2_baseband_core_txfir_coeff_japan_2484), 2); #if 0 /* ATH_WOW */ /* SerDes values during WOW sleep */ INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_wow, ar9300_pcie_phy_awow, ARRAY_LENGTH(ar9300_pcie_phy_awow), 2); #endif /* additional clock settings */ if (AH9300(ah)->clk_25mhz) { INIT_INI_ARRAY(&ahp->ah_ini_modes_additional, ar9331_hornet1_2_xtal_25M, ARRAY_LENGTH(ar9331_hornet1_2_xtal_25M), 2); } else { INIT_INI_ARRAY(&ahp->ah_ini_modes_additional, ar9331_hornet1_2_xtal_40M, ARRAY_LENGTH(ar9331_hornet1_2_xtal_40M), 2); } } else if (AR_SREV_HORNET_11(ah)) { /* mac */ INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_PRE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_CORE], ar9331_hornet1_1_mac_core, ARRAY_LENGTH(ar9331_hornet1_1_mac_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_POST], ar9331_hornet1_1_mac_postamble, ARRAY_LENGTH(ar9331_hornet1_1_mac_postamble), 5); /* bb */ INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_PRE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_CORE], ar9331_hornet1_1_baseband_core, ARRAY_LENGTH(ar9331_hornet1_1_baseband_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_POST], ar9331_hornet1_1_baseband_postamble, ARRAY_LENGTH(ar9331_hornet1_1_baseband_postamble), 5); /* radio */ INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_PRE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_CORE], ar9331_hornet1_1_radio_core, ARRAY_LENGTH(ar9331_hornet1_1_radio_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_POST], NULL, 0, 0); /* soc */ INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_PRE], ar9331_hornet1_1_soc_preamble, ARRAY_LENGTH(ar9331_hornet1_1_soc_preamble), 2); INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_CORE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_POST], ar9331_hornet1_1_soc_postamble, ARRAY_LENGTH(ar9331_hornet1_1_soc_postamble), 2); /* rx/tx gain */ INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar9331_common_rx_gain_hornet1_1, ARRAY_LENGTH(ar9331_common_rx_gain_hornet1_1), 2); INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9331_modes_lowest_ob_db_tx_gain_hornet1_1, ARRAY_LENGTH(ar9331_modes_lowest_ob_db_tx_gain_hornet1_1), 5); ah->ah_config.ath_hal_pcie_power_save_enable = 0; /* Japan 2484Mhz CCK settings */ INIT_INI_ARRAY(&ahp->ah_ini_japan2484, ar9331_hornet1_1_baseband_core_txfir_coeff_japan_2484, ARRAY_LENGTH( ar9331_hornet1_1_baseband_core_txfir_coeff_japan_2484), 2); #if 0 /* ATH_WOW */ /* SerDes values during WOW sleep */ INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_wow, ar9300_pcie_phy_awow, N(ar9300_pcie_phy_awow), 2); #endif /* additional clock settings */ if (AH9300(ah)->clk_25mhz) { INIT_INI_ARRAY(&ahp->ah_ini_modes_additional, ar9331_hornet1_1_xtal_25M, ARRAY_LENGTH(ar9331_hornet1_1_xtal_25M), 2); } else { INIT_INI_ARRAY(&ahp->ah_ini_modes_additional, ar9331_hornet1_1_xtal_40M, ARRAY_LENGTH(ar9331_hornet1_1_xtal_40M), 2); } } else if (AR_SREV_POSEIDON_11_OR_LATER(ah)) { /* mac */ INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_PRE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_CORE], ar9485_poseidon1_1_mac_core, ARRAY_LENGTH( ar9485_poseidon1_1_mac_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_POST], ar9485_poseidon1_1_mac_postamble, ARRAY_LENGTH(ar9485_poseidon1_1_mac_postamble), 5); /* bb */ INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_PRE], ar9485_poseidon1_1, ARRAY_LENGTH(ar9485_poseidon1_1), 2); INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_CORE], ar9485_poseidon1_1_baseband_core, ARRAY_LENGTH(ar9485_poseidon1_1_baseband_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_POST], ar9485_poseidon1_1_baseband_postamble, ARRAY_LENGTH(ar9485_poseidon1_1_baseband_postamble), 5); /* radio */ INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_PRE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_CORE], ar9485_poseidon1_1_radio_core, ARRAY_LENGTH(ar9485_poseidon1_1_radio_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_POST], ar9485_poseidon1_1_radio_postamble, ARRAY_LENGTH(ar9485_poseidon1_1_radio_postamble), 2); /* soc */ INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_PRE], ar9485_poseidon1_1_soc_preamble, ARRAY_LENGTH(ar9485_poseidon1_1_soc_preamble), 2); INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_CORE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_POST], NULL, 0, 0); /* rx/tx gain */ INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar9485_common_wo_xlna_rx_gain_poseidon1_1, ARRAY_LENGTH(ar9485_common_wo_xlna_rx_gain_poseidon1_1), 2); INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9485_modes_lowest_ob_db_tx_gain_poseidon1_1, ARRAY_LENGTH(ar9485_modes_lowest_ob_db_tx_gain_poseidon1_1), 5); /* Japan 2484Mhz CCK settings */ INIT_INI_ARRAY(&ahp->ah_ini_japan2484, ar9485_poseidon1_1_baseband_core_txfir_coeff_japan_2484, ARRAY_LENGTH( ar9485_poseidon1_1_baseband_core_txfir_coeff_japan_2484), 2); /* Load PCIE SERDES settings from INI */ if (ah->ah_config.ath_hal_pcie_clock_req) { /* Pci-e Clock Request = 1 */ if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_CONTROL) { /* Sleep Setting */ if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_ON_D3) { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes, ar9485_poseidon1_1_pcie_phy_clkreq_enable_L1, ARRAY_LENGTH( ar9485_poseidon1_1_pcie_phy_clkreq_enable_L1), 2); } else { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes, ar9485_poseidon1_1_pcie_phy_pll_on_clkreq_enable_L1, ARRAY_LENGTH( ar9485_poseidon1_1_pcie_phy_pll_on_clkreq_enable_L1), 2); } /* Awake Setting */ if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_ON_D0) { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_low_power, ar9485_poseidon1_1_pcie_phy_clkreq_enable_L1, ARRAY_LENGTH( ar9485_poseidon1_1_pcie_phy_clkreq_enable_L1), 2); } else { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_low_power, ar9485_poseidon1_1_pcie_phy_pll_on_clkreq_enable_L1, ARRAY_LENGTH( ar9485_poseidon1_1_pcie_phy_pll_on_clkreq_enable_L1), 2); } } else { /*Use driver default setting*/ /* Sleep Setting */ INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes, ar9485_poseidon1_1_pcie_phy_clkreq_enable_L1, ARRAY_LENGTH(ar9485_poseidon1_1_pcie_phy_clkreq_enable_L1), 2); /* Awake Setting */ INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_low_power, ar9485_poseidon1_1_pcie_phy_clkreq_enable_L1, ARRAY_LENGTH(ar9485_poseidon1_1_pcie_phy_clkreq_enable_L1), 2); } } else { /* Pci-e Clock Request = 0 */ if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_CONTROL) { /* Sleep Setting */ if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_ON_D3) { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes, ar9485_poseidon1_1_pcie_phy_clkreq_disable_L1, ARRAY_LENGTH( ar9485_poseidon1_1_pcie_phy_clkreq_disable_L1), 2); } else { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes, ar9485_poseidon1_1_pcie_phy_pll_on_clkreq_disable_L1, ARRAY_LENGTH( ar9485_poseidon1_1_pcie_phy_pll_on_clkreq_disable_L1), 2); } /* Awake Setting */ if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_ON_D0) { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_low_power, ar9485_poseidon1_1_pcie_phy_clkreq_disable_L1, ARRAY_LENGTH( ar9485_poseidon1_1_pcie_phy_clkreq_disable_L1), 2); } else { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_low_power, ar9485_poseidon1_1_pcie_phy_pll_on_clkreq_disable_L1, ARRAY_LENGTH( ar9485_poseidon1_1_pcie_phy_pll_on_clkreq_disable_L1), 2); } } else { /*Use driver default setting*/ /* Sleep Setting */ INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes, ar9485_poseidon1_1_pcie_phy_clkreq_disable_L1, ARRAY_LENGTH(ar9485_poseidon1_1_pcie_phy_clkreq_disable_L1), 2); /* Awake Setting */ INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_low_power, ar9485_poseidon1_1_pcie_phy_clkreq_disable_L1, ARRAY_LENGTH(ar9485_poseidon1_1_pcie_phy_clkreq_disable_L1), 2); } } /* pcie ps setting will honor registry setting, default is 0 */ //ah->ah_config.ath_hal_pciePowerSaveEnable = 0; } else if (AR_SREV_POSEIDON(ah)) { /* mac */ INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_PRE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_CORE], ar9485_poseidon1_0_mac_core, ARRAY_LENGTH(ar9485_poseidon1_0_mac_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_POST], ar9485_poseidon1_0_mac_postamble, ARRAY_LENGTH(ar9485_poseidon1_0_mac_postamble), 5); /* bb */ INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_PRE], ar9485_poseidon1_0, ARRAY_LENGTH(ar9485_poseidon1_0), 2); INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_CORE], ar9485_poseidon1_0_baseband_core, ARRAY_LENGTH(ar9485_poseidon1_0_baseband_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_POST], ar9485_poseidon1_0_baseband_postamble, ARRAY_LENGTH(ar9485_poseidon1_0_baseband_postamble), 5); /* radio */ INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_PRE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_CORE], ar9485_poseidon1_0_radio_core, ARRAY_LENGTH(ar9485_poseidon1_0_radio_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_POST], ar9485_poseidon1_0_radio_postamble, ARRAY_LENGTH(ar9485_poseidon1_0_radio_postamble), 2); /* soc */ INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_PRE], ar9485_poseidon1_0_soc_preamble, ARRAY_LENGTH(ar9485_poseidon1_0_soc_preamble), 2); INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_CORE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_POST], NULL, 0, 0); /* rx/tx gain */ INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar9485Common_wo_xlna_rx_gain_poseidon1_0, ARRAY_LENGTH(ar9485Common_wo_xlna_rx_gain_poseidon1_0), 2); INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9485Modes_lowest_ob_db_tx_gain_poseidon1_0, ARRAY_LENGTH(ar9485Modes_lowest_ob_db_tx_gain_poseidon1_0), 5); /* Japan 2484Mhz CCK settings */ INIT_INI_ARRAY(&ahp->ah_ini_japan2484, ar9485_poseidon1_0_baseband_core_txfir_coeff_japan_2484, ARRAY_LENGTH( ar9485_poseidon1_0_baseband_core_txfir_coeff_japan_2484), 2); /* Load PCIE SERDES settings from INI */ if (ah->ah_config.ath_hal_pcie_clock_req) { /* Pci-e Clock Request = 1 */ if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_CONTROL) { /* Sleep Setting */ if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_ON_D3) { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes, ar9485_poseidon1_0_pcie_phy_clkreq_enable_L1, ARRAY_LENGTH( ar9485_poseidon1_0_pcie_phy_clkreq_enable_L1), 2); } else { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes, ar9485_poseidon1_0_pcie_phy_pll_on_clkreq_enable_L1, ARRAY_LENGTH( ar9485_poseidon1_0_pcie_phy_pll_on_clkreq_enable_L1), 2); } /* Awake Setting */ if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_ON_D0) { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_low_power, ar9485_poseidon1_0_pcie_phy_clkreq_enable_L1, ARRAY_LENGTH( ar9485_poseidon1_0_pcie_phy_clkreq_enable_L1), 2); } else { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_low_power, ar9485_poseidon1_0_pcie_phy_pll_on_clkreq_enable_L1, ARRAY_LENGTH( ar9485_poseidon1_0_pcie_phy_pll_on_clkreq_enable_L1), 2); } } else { /*Use driver default setting*/ /* Sleep Setting */ INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes, ar9485_poseidon1_0_pcie_phy_pll_on_clkreq_enable_L1, ARRAY_LENGTH( ar9485_poseidon1_0_pcie_phy_pll_on_clkreq_enable_L1), 2); /* Awake Setting */ INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_low_power, ar9485_poseidon1_0_pcie_phy_pll_on_clkreq_enable_L1, ARRAY_LENGTH( ar9485_poseidon1_0_pcie_phy_pll_on_clkreq_enable_L1), 2); } } else { /* Pci-e Clock Request = 0 */ if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_CONTROL) { /* Sleep Setting */ if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_ON_D3) { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes, ar9485_poseidon1_0_pcie_phy_clkreq_disable_L1, ARRAY_LENGTH( ar9485_poseidon1_0_pcie_phy_clkreq_disable_L1), 2); } else { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes, ar9485_poseidon1_0_pcie_phy_pll_on_clkreq_disable_L1, ARRAY_LENGTH( ar9485_poseidon1_0_pcie_phy_pll_on_clkreq_disable_L1), 2); } /* Awake Setting */ if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_ON_D0) { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_low_power, ar9485_poseidon1_0_pcie_phy_clkreq_disable_L1, ARRAY_LENGTH( ar9485_poseidon1_0_pcie_phy_clkreq_disable_L1), 2); } else { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_low_power, ar9485_poseidon1_0_pcie_phy_pll_on_clkreq_disable_L1, ARRAY_LENGTH( ar9485_poseidon1_0_pcie_phy_pll_on_clkreq_disable_L1), 2); } } else { /*Use driver default setting*/ /* Sleep Setting */ INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes, ar9485_poseidon1_0_pcie_phy_pll_on_clkreq_disable_L1, ARRAY_LENGTH( ar9485_poseidon1_0_pcie_phy_pll_on_clkreq_disable_L1), 2); /* Awake Setting */ INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_low_power, ar9485_poseidon1_0_pcie_phy_pll_on_clkreq_disable_L1, ARRAY_LENGTH( ar9485_poseidon1_0_pcie_phy_pll_on_clkreq_disable_L1), 2); } } /* pcie ps setting will honor registry setting, default is 0 */ /*ah->ah_config.ath_hal_pcie_power_save_enable = 0;*/ #if 0 /* ATH_WOW */ /* SerDes values during WOW sleep */ INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_wow, ar9300_pcie_phy_awow, ARRAY_LENGTH(ar9300_pcie_phy_awow), 2); #endif } else if (AR_SREV_WASP(ah)) { /* mac */ INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_PRE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_CORE], ar9340_wasp_1p0_mac_core, ARRAY_LENGTH(ar9340_wasp_1p0_mac_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_POST], ar9340_wasp_1p0_mac_postamble, ARRAY_LENGTH(ar9340_wasp_1p0_mac_postamble), 5); /* bb */ INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_PRE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_CORE], ar9340_wasp_1p0_baseband_core, ARRAY_LENGTH(ar9340_wasp_1p0_baseband_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_POST], ar9340_wasp_1p0_baseband_postamble, ARRAY_LENGTH(ar9340_wasp_1p0_baseband_postamble), 5); /* radio */ INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_PRE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_CORE], ar9340_wasp_1p0_radio_core, ARRAY_LENGTH(ar9340_wasp_1p0_radio_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_POST], ar9340_wasp_1p0_radio_postamble, ARRAY_LENGTH(ar9340_wasp_1p0_radio_postamble), 5); /* soc */ INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_PRE], ar9340_wasp_1p0_soc_preamble, ARRAY_LENGTH(ar9340_wasp_1p0_soc_preamble), 2); INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_CORE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_POST], ar9340_wasp_1p0_soc_postamble, ARRAY_LENGTH(ar9340_wasp_1p0_soc_postamble), 5); /* rx/tx gain */ INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar9340Common_wo_xlna_rx_gain_table_wasp_1p0, ARRAY_LENGTH(ar9340Common_wo_xlna_rx_gain_table_wasp_1p0), 2); INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9340Modes_high_ob_db_tx_gain_table_wasp_1p0, ARRAY_LENGTH(ar9340Modes_high_ob_db_tx_gain_table_wasp_1p0), 5); ah->ah_config.ath_hal_pcie_power_save_enable = 0; /* Fast clock modal settings */ INIT_INI_ARRAY(&ahp->ah_ini_modes_additional, ar9340Modes_fast_clock_wasp_1p0, ARRAY_LENGTH(ar9340Modes_fast_clock_wasp_1p0), 3); /* Additional setttings for 40Mhz */ INIT_INI_ARRAY(&ahp->ah_ini_modes_additional_40mhz, ar9340_wasp_1p0_radio_core_40M, ARRAY_LENGTH(ar9340_wasp_1p0_radio_core_40M), 2); /* DFS */ INIT_INI_ARRAY(&ahp->ah_ini_dfs, ar9340_wasp_1p0_baseband_postamble_dfs_channel, ARRAY_LENGTH(ar9340_wasp_1p0_baseband_postamble_dfs_channel), 3); } else if (AR_SREV_SCORPION(ah)) { /* mac */ INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_PRE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_CORE], ar955x_scorpion_1p0_mac_core, ARRAY_LENGTH(ar955x_scorpion_1p0_mac_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_POST], ar955x_scorpion_1p0_mac_postamble, ARRAY_LENGTH(ar955x_scorpion_1p0_mac_postamble), 5); /* bb */ INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_PRE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_CORE], ar955x_scorpion_1p0_baseband_core, ARRAY_LENGTH(ar955x_scorpion_1p0_baseband_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_POST], ar955x_scorpion_1p0_baseband_postamble, ARRAY_LENGTH(ar955x_scorpion_1p0_baseband_postamble), 5); /* radio */ INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_PRE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_CORE], ar955x_scorpion_1p0_radio_core, ARRAY_LENGTH(ar955x_scorpion_1p0_radio_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_POST], ar955x_scorpion_1p0_radio_postamble, ARRAY_LENGTH(ar955x_scorpion_1p0_radio_postamble), 5); /* soc */ INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_PRE], ar955x_scorpion_1p0_soc_preamble, ARRAY_LENGTH(ar955x_scorpion_1p0_soc_preamble), 2); INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_CORE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_POST], ar955x_scorpion_1p0_soc_postamble, ARRAY_LENGTH(ar955x_scorpion_1p0_soc_postamble), 5); /* rx/tx gain */ INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar955xCommon_wo_xlna_rx_gain_table_scorpion_1p0, ARRAY_LENGTH(ar955xCommon_wo_xlna_rx_gain_table_scorpion_1p0), 2); INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain_bounds, ar955xCommon_wo_xlna_rx_gain_bounds_scorpion_1p0, ARRAY_LENGTH(ar955xCommon_wo_xlna_rx_gain_bounds_scorpion_1p0), 5); INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar955xModes_no_xpa_tx_gain_table_scorpion_1p0, ARRAY_LENGTH(ar955xModes_no_xpa_tx_gain_table_scorpion_1p0), 5); /*ath_hal_pciePowerSaveEnable should be 2 for OWL/Condor and 0 for merlin */ ah->ah_config.ath_hal_pcie_power_save_enable = 0; /* Fast clock modal settings */ INIT_INI_ARRAY(&ahp->ah_ini_modes_additional, ar955xModes_fast_clock_scorpion_1p0, ARRAY_LENGTH(ar955xModes_fast_clock_scorpion_1p0), 3); /* Additional setttings for 40Mhz */ //INIT_INI_ARRAY(&ahp->ah_ini_modes_additional_40M, // ar955x_scorpion_1p0_radio_core_40M, // ARRAY_LENGTH(ar955x_scorpion_1p0_radio_core_40M), 2); } else if (AR_SREV_JUPITER_10(ah)) { /* Jupiter: new INI format (pre, core, post arrays per subsystem) */ /* mac */ INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_PRE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_CORE], ar9300_jupiter_1p0_mac_core, ARRAY_LENGTH(ar9300_jupiter_1p0_mac_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_POST], ar9300_jupiter_1p0_mac_postamble, ARRAY_LENGTH(ar9300_jupiter_1p0_mac_postamble), 5); /* bb */ INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_PRE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_CORE], ar9300_jupiter_1p0_baseband_core, ARRAY_LENGTH(ar9300_jupiter_1p0_baseband_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_POST], ar9300_jupiter_1p0_baseband_postamble, ARRAY_LENGTH(ar9300_jupiter_1p0_baseband_postamble), 5); /* radio */ INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_PRE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_CORE], ar9300_jupiter_1p0_radio_core, ARRAY_LENGTH(ar9300_jupiter_1p0_radio_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_POST], ar9300_jupiter_1p0_radio_postamble, ARRAY_LENGTH(ar9300_jupiter_1p0_radio_postamble), 5); /* soc */ INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_PRE], ar9300_jupiter_1p0_soc_preamble, ARRAY_LENGTH(ar9300_jupiter_1p0_soc_preamble), 2); INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_CORE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_POST], ar9300_jupiter_1p0_soc_postamble, ARRAY_LENGTH(ar9300_jupiter_1p0_soc_postamble), 5); /* rx/tx gain */ INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar9300_common_rx_gain_table_jupiter_1p0, ARRAY_LENGTH(ar9300_common_rx_gain_table_jupiter_1p0), 2); /* Load PCIE SERDES settings from INI */ if (ah->ah_config.ath_hal_pcie_clock_req) { /* Pci-e Clock Request = 1 */ /* * PLL ON + clkreq enable is not a valid combination, * thus to ignore ath_hal_pll_pwr_save, use PLL OFF. */ { /*Use driver default setting*/ /* Awake -> Sleep Setting */ INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes, ar9300_pcie_phy_clkreq_enable_L1_jupiter_1p0, ARRAY_LENGTH(ar9300_pcie_phy_clkreq_enable_L1_jupiter_1p0), 2); /* Sleep -> Awake Setting */ INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_low_power, ar9300_pcie_phy_clkreq_enable_L1_jupiter_1p0, ARRAY_LENGTH(ar9300_pcie_phy_clkreq_enable_L1_jupiter_1p0), 2); } } else { /* * Since Jupiter 1.0 and 2.0 share the same device id and will be * installed with same INF, but Jupiter 1.0 has issue with PLL OFF. * * Force Jupiter 1.0 to use ON/ON setting. */ ah->ah_config.ath_hal_pll_pwr_save = 0; /* Pci-e Clock Request = 0 */ if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_CONTROL) { /* Awake -> Sleep Setting */ if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_ON_D3) { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes, ar9300_pcie_phy_clkreq_disable_L1_jupiter_1p0, ARRAY_LENGTH( ar9300_pcie_phy_clkreq_disable_L1_jupiter_1p0), 2); } else { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes, ar9300_pcie_phy_pll_on_clkreq_disable_L1_jupiter_1p0, ARRAY_LENGTH( ar9300_pcie_phy_pll_on_clkreq_disable_L1_jupiter_1p0), 2); } /* Sleep -> Awake Setting */ if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_ON_D0) { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_low_power, ar9300_pcie_phy_clkreq_disable_L1_jupiter_1p0, ARRAY_LENGTH( ar9300_pcie_phy_clkreq_disable_L1_jupiter_1p0), 2); } else { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_low_power, ar9300_pcie_phy_pll_on_clkreq_disable_L1_jupiter_1p0, ARRAY_LENGTH( ar9300_pcie_phy_pll_on_clkreq_disable_L1_jupiter_1p0), 2); } } else { /*Use driver default setting*/ /* Awake -> Sleep Setting */ INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes, ar9300_pcie_phy_pll_on_clkreq_disable_L1_jupiter_1p0, ARRAY_LENGTH( ar9300_pcie_phy_pll_on_clkreq_disable_L1_jupiter_1p0), 2); /* Sleep -> Awake Setting */ INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_low_power, ar9300_pcie_phy_pll_on_clkreq_disable_L1_jupiter_1p0, ARRAY_LENGTH( ar9300_pcie_phy_pll_on_clkreq_disable_L1_jupiter_1p0), 2); } } /* * ath_hal_pcie_power_save_enable should be 2 for OWL/Condor and * 0 for merlin */ ah->ah_config.ath_hal_pcie_power_save_enable = 0; #if 0 // ATH_WOW /* SerDes values during WOW sleep */ INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_wow, ar9300_pcie_phy_AWOW, ARRAY_LENGTH(ar9300_pcie_phy_AWOW), 2); #endif /* Fast clock modal settings */ INIT_INI_ARRAY(&ahp->ah_ini_modes_additional, ar9300_modes_fast_clock_jupiter_1p0, ARRAY_LENGTH(ar9300_modes_fast_clock_jupiter_1p0), 3); INIT_INI_ARRAY(&ahp->ah_ini_japan2484, ar9300_jupiter_1p0_baseband_core_txfir_coeff_japan_2484, ARRAY_LENGTH( ar9300_jupiter_1p0_baseband_core_txfir_coeff_japan_2484), 2); } else if (AR_SREV_JUPITER_20(ah)) { /* Jupiter: new INI format (pre, core, post arrays per subsystem) */ /* mac */ INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_PRE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_CORE], ar9300_jupiter_2p0_mac_core, ARRAY_LENGTH(ar9300_jupiter_2p0_mac_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_POST], ar9300_jupiter_2p0_mac_postamble, ARRAY_LENGTH(ar9300_jupiter_2p0_mac_postamble), 5); /* bb */ INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_PRE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_CORE], ar9300_jupiter_2p0_baseband_core, ARRAY_LENGTH(ar9300_jupiter_2p0_baseband_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_POST], ar9300_jupiter_2p0_baseband_postamble, ARRAY_LENGTH(ar9300_jupiter_2p0_baseband_postamble), 5); /* radio */ INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_PRE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_CORE], ar9300_jupiter_2p0_radio_core, ARRAY_LENGTH(ar9300_jupiter_2p0_radio_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_POST], ar9300_jupiter_2p0_radio_postamble, ARRAY_LENGTH(ar9300_jupiter_2p0_radio_postamble), 5); INIT_INI_ARRAY(&ahp->ah_ini_radio_post_sys2ant, ar9300_jupiter_2p0_radio_postamble_sys2ant, ARRAY_LENGTH(ar9300_jupiter_2p0_radio_postamble_sys2ant), 5); /* soc */ INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_PRE], ar9300_jupiter_2p0_soc_preamble, ARRAY_LENGTH(ar9300_jupiter_2p0_soc_preamble), 2); INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_CORE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_POST], ar9300_jupiter_2p0_soc_postamble, ARRAY_LENGTH(ar9300_jupiter_2p0_soc_postamble), 5); /* rx/tx gain */ INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar9300Common_rx_gain_table_jupiter_2p0, ARRAY_LENGTH(ar9300Common_rx_gain_table_jupiter_2p0), 2); /* BTCOEX */ INIT_INI_ARRAY(&ahp->ah_ini_BTCOEX_MAX_TXPWR, ar9300_jupiter_2p0_BTCOEX_MAX_TXPWR_table, ARRAY_LENGTH(ar9300_jupiter_2p0_BTCOEX_MAX_TXPWR_table), 2); /* Load PCIE SERDES settings from INI */ if (ah->ah_config.ath_hal_pcie_clock_req) { /* Pci-e Clock Request = 1 */ /* * PLL ON + clkreq enable is not a valid combination, * thus to ignore ath_hal_pll_pwr_save, use PLL OFF. */ { /*Use driver default setting*/ /* Awake -> Sleep Setting */ INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes, ar9300_PciePhy_clkreq_enable_L1_jupiter_2p0, ARRAY_LENGTH(ar9300_PciePhy_clkreq_enable_L1_jupiter_2p0), 2); /* Sleep -> Awake Setting */ INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_low_power, ar9300_PciePhy_clkreq_enable_L1_jupiter_2p0, ARRAY_LENGTH(ar9300_PciePhy_clkreq_enable_L1_jupiter_2p0), 2); } } else { /* Pci-e Clock Request = 0 */ if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_CONTROL) { /* Awake -> Sleep Setting */ if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_ON_D3) { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes, ar9300_PciePhy_clkreq_disable_L1_jupiter_2p0, ARRAY_LENGTH( ar9300_PciePhy_clkreq_disable_L1_jupiter_2p0), 2); } else { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes, ar9300_PciePhy_pll_on_clkreq_disable_L1_jupiter_2p0, ARRAY_LENGTH( ar9300_PciePhy_pll_on_clkreq_disable_L1_jupiter_2p0), 2); } /* Sleep -> Awake Setting */ if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_ON_D0) { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_low_power, ar9300_PciePhy_clkreq_disable_L1_jupiter_2p0, ARRAY_LENGTH( ar9300_PciePhy_clkreq_disable_L1_jupiter_2p0), 2); } else { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_low_power, ar9300_PciePhy_pll_on_clkreq_disable_L1_jupiter_2p0, ARRAY_LENGTH( ar9300_PciePhy_pll_on_clkreq_disable_L1_jupiter_2p0), 2); } } else { /*Use driver default setting*/ /* Awake -> Sleep Setting */ INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes, ar9300_PciePhy_pll_on_clkreq_disable_L1_jupiter_2p0, ARRAY_LENGTH( ar9300_PciePhy_pll_on_clkreq_disable_L1_jupiter_2p0), 2); /* Sleep -> Awake Setting */ INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_low_power, ar9300_PciePhy_pll_on_clkreq_disable_L1_jupiter_2p0, ARRAY_LENGTH( ar9300_PciePhy_pll_on_clkreq_disable_L1_jupiter_2p0), 2); } } /* * ath_hal_pcie_power_save_enable should be 2 for OWL/Condor and * 0 for merlin */ ah->ah_config.ath_hal_pcie_power_save_enable = 0; #if 0 // ATH_WOW /* SerDes values during WOW sleep */ INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_wow, ar9300_pcie_phy_AWOW, ARRAY_LENGTH(ar9300_pcie_phy_AWOW), 2); #endif /* Fast clock modal settings */ INIT_INI_ARRAY(&ahp->ah_ini_modes_additional, ar9300Modes_fast_clock_jupiter_2p0, ARRAY_LENGTH(ar9300Modes_fast_clock_jupiter_2p0), 3); INIT_INI_ARRAY(&ahp->ah_ini_japan2484, ar9300_jupiter_2p0_baseband_core_txfir_coeff_japan_2484, ARRAY_LENGTH( ar9300_jupiter_2p0_baseband_core_txfir_coeff_japan_2484), 2); } else if (AR_SREV_APHRODITE(ah)) { /* Aphrodite: new INI format (pre, core, post arrays per subsystem) */ /* mac */ INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_PRE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_CORE], ar956X_aphrodite_1p0_mac_core, ARRAY_LENGTH(ar956X_aphrodite_1p0_mac_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_POST], ar956X_aphrodite_1p0_mac_postamble, ARRAY_LENGTH(ar956X_aphrodite_1p0_mac_postamble), 5); /* bb */ INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_PRE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_CORE], ar956X_aphrodite_1p0_baseband_core, ARRAY_LENGTH(ar956X_aphrodite_1p0_baseband_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_POST], ar956X_aphrodite_1p0_baseband_postamble, ARRAY_LENGTH(ar956X_aphrodite_1p0_baseband_postamble), 5); //mark jupiter have but aphrodite don't have // /* radio */ // INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_PRE], NULL, 0, 0); // INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_CORE], // ar9300_aphrodite_1p0_radio_core, // ARRAY_LENGTH(ar9300_aphrodite_1p0_radio_core), 2); // INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_POST], // ar9300_aphrodite_1p0_radio_postamble, // ARRAY_LENGTH(ar9300_aphrodite_1p0_radio_postamble), 5); /* soc */ INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_PRE], ar956X_aphrodite_1p0_soc_preamble, ARRAY_LENGTH(ar956X_aphrodite_1p0_soc_preamble), 2); INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_CORE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_POST], ar956X_aphrodite_1p0_soc_postamble, ARRAY_LENGTH(ar956X_aphrodite_1p0_soc_postamble), 5); /* rx/tx gain */ INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar956XCommon_rx_gain_table_aphrodite_1p0, ARRAY_LENGTH(ar956XCommon_rx_gain_table_aphrodite_1p0), 2); //INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, // ar956XModes_lowest_ob_db_tx_gain_table_aphrodite_1p0, // ARRAY_LENGTH(ar956XModes_lowest_ob_db_tx_gain_table_aphrodite_1p0), // 5); /* * ath_hal_pcie_power_save_enable should be 2 for OWL/Condor and * 0 for merlin */ ah->ah_config.ath_hal_pcie_power_save_enable = 0; #if 0 // ATH_WOW /* SerDes values during WOW sleep */ INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_wow, ar9300_pcie_phy_AWOW, ARRAY_LENGTH(ar9300_pcie_phy_AWOW), 2); #endif /* Fast clock modal settings */ INIT_INI_ARRAY(&ahp->ah_ini_modes_additional, ar956XModes_fast_clock_aphrodite_1p0, ARRAY_LENGTH(ar956XModes_fast_clock_aphrodite_1p0), 3); } else if (AR_SREV_AR9580(ah)) { /* * AR9580/Peacock - * new INI format (pre, core, post arrays per subsystem) */ /* mac */ INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_PRE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_CORE], ar9300_ar9580_1p0_mac_core, ARRAY_LENGTH(ar9300_ar9580_1p0_mac_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_POST], ar9300_ar9580_1p0_mac_postamble, ARRAY_LENGTH(ar9300_ar9580_1p0_mac_postamble), 5); /* bb */ INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_PRE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_CORE], ar9300_ar9580_1p0_baseband_core, ARRAY_LENGTH(ar9300_ar9580_1p0_baseband_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_POST], ar9300_ar9580_1p0_baseband_postamble, ARRAY_LENGTH(ar9300_ar9580_1p0_baseband_postamble), 5); /* radio */ INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_PRE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_CORE], ar9300_ar9580_1p0_radio_core, ARRAY_LENGTH(ar9300_ar9580_1p0_radio_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_POST], ar9300_ar9580_1p0_radio_postamble, ARRAY_LENGTH(ar9300_ar9580_1p0_radio_postamble), 5); /* soc */ INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_PRE], ar9300_ar9580_1p0_soc_preamble, ARRAY_LENGTH(ar9300_ar9580_1p0_soc_preamble), 2); INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_CORE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_POST], ar9300_ar9580_1p0_soc_postamble, ARRAY_LENGTH(ar9300_ar9580_1p0_soc_postamble), 5); /* rx/tx gain */ INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar9300_common_rx_gain_table_ar9580_1p0, ARRAY_LENGTH(ar9300_common_rx_gain_table_ar9580_1p0), 2); INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9300Modes_lowest_ob_db_tx_gain_table_ar9580_1p0, ARRAY_LENGTH(ar9300Modes_lowest_ob_db_tx_gain_table_ar9580_1p0), 5); /* DFS */ INIT_INI_ARRAY(&ahp->ah_ini_dfs, ar9300_ar9580_1p0_baseband_postamble_dfs_channel, ARRAY_LENGTH(ar9300_ar9580_1p0_baseband_postamble_dfs_channel), 3); /* Load PCIE SERDES settings from INI */ /*D3 Setting */ if (ah->ah_config.ath_hal_pcie_clock_req) { if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_CONTROL) { //registry control if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_ON_D3) { //bit1, in to D3 INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes, ar9300PciePhy_clkreq_enable_L1_ar9580_1p0, ARRAY_LENGTH(ar9300PciePhy_clkreq_enable_L1_ar9580_1p0), 2); } else { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes, ar9300PciePhy_pll_on_clkreq_disable_L1_ar9580_1p0, ARRAY_LENGTH( ar9300PciePhy_pll_on_clkreq_disable_L1_ar9580_1p0), 2); } } else {//no registry control, default is pll on INIT_INI_ARRAY( &ahp->ah_ini_pcie_serdes, ar9300PciePhy_pll_on_clkreq_disable_L1_ar9580_1p0, ARRAY_LENGTH( ar9300PciePhy_pll_on_clkreq_disable_L1_ar9580_1p0), 2); } } else { if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_CONTROL) { //registry control if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_ON_D3) { //bit1, in to D3 INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes, ar9300PciePhy_clkreq_disable_L1_ar9580_1p0, ARRAY_LENGTH( ar9300PciePhy_clkreq_disable_L1_ar9580_1p0), 2); } else { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes, ar9300PciePhy_pll_on_clkreq_disable_L1_ar9580_1p0, ARRAY_LENGTH( ar9300PciePhy_pll_on_clkreq_disable_L1_ar9580_1p0), 2); } } else {//no registry control, default is pll on INIT_INI_ARRAY( &ahp->ah_ini_pcie_serdes, ar9300PciePhy_pll_on_clkreq_disable_L1_ar9580_1p0, ARRAY_LENGTH( ar9300PciePhy_pll_on_clkreq_disable_L1_ar9580_1p0), 2); } } /*D0 Setting */ if (ah->ah_config.ath_hal_pcie_clock_req) { if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_CONTROL) { //registry control if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_ON_D0) { //bit2, out of D3 INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_low_power, ar9300PciePhy_clkreq_enable_L1_ar9580_1p0, ARRAY_LENGTH(ar9300PciePhy_clkreq_enable_L1_ar9580_1p0), 2); } else { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_low_power, ar9300PciePhy_pll_on_clkreq_disable_L1_ar9580_1p0, ARRAY_LENGTH( ar9300PciePhy_pll_on_clkreq_disable_L1_ar9580_1p0), 2); } } else { //no registry control, default is pll on INIT_INI_ARRAY( &ahp->ah_ini_pcie_serdes_low_power, ar9300PciePhy_pll_on_clkreq_disable_L1_ar9580_1p0, ARRAY_LENGTH( ar9300PciePhy_pll_on_clkreq_disable_L1_ar9580_1p0), 2); } } else { if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_CONTROL) {//registry control if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_ON_D0) {//bit2, out of D3 INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_low_power, ar9300PciePhy_clkreq_disable_L1_ar9580_1p0, ARRAY_LENGTH(ar9300PciePhy_clkreq_disable_L1_ar9580_1p0), 2); } else { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_low_power, ar9300PciePhy_pll_on_clkreq_disable_L1_ar9580_1p0, ARRAY_LENGTH( ar9300PciePhy_pll_on_clkreq_disable_L1_ar9580_1p0), 2); } } else { //no registry control, default is pll on INIT_INI_ARRAY( &ahp->ah_ini_pcie_serdes_low_power, ar9300PciePhy_pll_on_clkreq_disable_L1_ar9580_1p0, ARRAY_LENGTH( ar9300PciePhy_pll_on_clkreq_disable_L1_ar9580_1p0), 2); } } ah->ah_config.ath_hal_pcie_power_save_enable = 0; #if 0 /* ATH_WOW */ /* SerDes values during WOW sleep */ INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_wow, ar9300_pcie_phy_awow, ARRAY_LENGTH(ar9300_pcie_phy_awow), 2); #endif /* Fast clock modal settings */ INIT_INI_ARRAY(&ahp->ah_ini_modes_additional, ar9300Modes_fast_clock_ar9580_1p0, ARRAY_LENGTH(ar9300Modes_fast_clock_ar9580_1p0), 3); INIT_INI_ARRAY(&ahp->ah_ini_japan2484, ar9300_ar9580_1p0_baseband_core_txfir_coeff_japan_2484, ARRAY_LENGTH( ar9300_ar9580_1p0_baseband_core_txfir_coeff_japan_2484), 2); } else { /* * Osprey 2.2 - new INI format (pre, core, post arrays per subsystem) */ /* mac */ INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_PRE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_CORE], ar9300_osprey_2p2_mac_core, ARRAY_LENGTH(ar9300_osprey_2p2_mac_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_mac[ATH_INI_POST], ar9300_osprey_2p2_mac_postamble, ARRAY_LENGTH(ar9300_osprey_2p2_mac_postamble), 5); /* bb */ INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_PRE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_CORE], ar9300_osprey_2p2_baseband_core, ARRAY_LENGTH(ar9300_osprey_2p2_baseband_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_bb[ATH_INI_POST], ar9300_osprey_2p2_baseband_postamble, ARRAY_LENGTH(ar9300_osprey_2p2_baseband_postamble), 5); /* radio */ INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_PRE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_CORE], ar9300_osprey_2p2_radio_core, ARRAY_LENGTH(ar9300_osprey_2p2_radio_core), 2); INIT_INI_ARRAY(&ahp->ah_ini_radio[ATH_INI_POST], ar9300_osprey_2p2_radio_postamble, ARRAY_LENGTH(ar9300_osprey_2p2_radio_postamble), 5); /* soc */ INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_PRE], ar9300_osprey_2p2_soc_preamble, ARRAY_LENGTH(ar9300_osprey_2p2_soc_preamble), 2); INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_CORE], NULL, 0, 0); INIT_INI_ARRAY(&ahp->ah_ini_soc[ATH_INI_POST], ar9300_osprey_2p2_soc_postamble, ARRAY_LENGTH(ar9300_osprey_2p2_soc_postamble), 5); /* rx/tx gain */ INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar9300_common_rx_gain_table_osprey_2p2, ARRAY_LENGTH(ar9300_common_rx_gain_table_osprey_2p2), 2); INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9300_modes_lowest_ob_db_tx_gain_table_osprey_2p2, ARRAY_LENGTH(ar9300_modes_lowest_ob_db_tx_gain_table_osprey_2p2), 5); /* DFS */ INIT_INI_ARRAY(&ahp->ah_ini_dfs, ar9300_osprey_2p2_baseband_postamble_dfs_channel, ARRAY_LENGTH(ar9300_osprey_2p2_baseband_postamble_dfs_channel), 3); /* Load PCIE SERDES settings from INI */ /*D3 Setting */ if (ah->ah_config.ath_hal_pcie_clock_req) { if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_CONTROL) { //registry control if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_ON_D3) { //bit1, in to D3 INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes, ar9300PciePhy_clkreq_enable_L1_osprey_2p2, ARRAY_LENGTH(ar9300PciePhy_clkreq_enable_L1_osprey_2p2), 2); } else { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes, ar9300PciePhy_pll_on_clkreq_disable_L1_osprey_2p2, ARRAY_LENGTH( ar9300PciePhy_pll_on_clkreq_disable_L1_osprey_2p2), 2); } } else {//no registry control, default is pll on #ifndef ATH_BUS_PM INIT_INI_ARRAY( &ahp->ah_ini_pcie_serdes, ar9300PciePhy_pll_on_clkreq_disable_L1_osprey_2p2, ARRAY_LENGTH( ar9300PciePhy_pll_on_clkreq_disable_L1_osprey_2p2), 2); #else //no registry control, default is pll off INIT_INI_ARRAY( &ahp->ah_ini_pcie_serdes, ar9300PciePhy_clkreq_disable_L1_osprey_2p2, ARRAY_LENGTH( ar9300PciePhy_clkreq_disable_L1_osprey_2p2), 2); #endif } } else { if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_CONTROL) { //registry control if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_ON_D3) { //bit1, in to D3 INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes, ar9300PciePhy_clkreq_disable_L1_osprey_2p2, ARRAY_LENGTH( ar9300PciePhy_clkreq_disable_L1_osprey_2p2), 2); } else { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes, ar9300PciePhy_pll_on_clkreq_disable_L1_osprey_2p2, ARRAY_LENGTH( ar9300PciePhy_pll_on_clkreq_disable_L1_osprey_2p2), 2); } } else { #ifndef ATH_BUS_PM //no registry control, default is pll on INIT_INI_ARRAY( &ahp->ah_ini_pcie_serdes, ar9300PciePhy_pll_on_clkreq_disable_L1_osprey_2p2, ARRAY_LENGTH( ar9300PciePhy_pll_on_clkreq_disable_L1_osprey_2p2), 2); #else //no registry control, default is pll off INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes, ar9300PciePhy_clkreq_disable_L1_osprey_2p2, ARRAY_LENGTH(ar9300PciePhy_clkreq_disable_L1_osprey_2p2), 2); #endif } } /*D0 Setting */ if (ah->ah_config.ath_hal_pcie_clock_req) { if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_CONTROL) { //registry control if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_ON_D0) { //bit2, out of D3 INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_low_power, ar9300PciePhy_clkreq_enable_L1_osprey_2p2, ARRAY_LENGTH(ar9300PciePhy_clkreq_enable_L1_osprey_2p2), 2); } else { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_low_power, ar9300PciePhy_pll_on_clkreq_disable_L1_osprey_2p2, ARRAY_LENGTH( ar9300PciePhy_pll_on_clkreq_disable_L1_osprey_2p2), 2); } } else { //no registry control, default is pll on INIT_INI_ARRAY( &ahp->ah_ini_pcie_serdes_low_power, ar9300PciePhy_pll_on_clkreq_disable_L1_osprey_2p2, ARRAY_LENGTH( ar9300PciePhy_pll_on_clkreq_disable_L1_osprey_2p2), 2); } } else { if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_CONTROL) {//registry control if (ah->ah_config.ath_hal_pll_pwr_save & AR_PCIE_PLL_PWRSAVE_ON_D0) {//bit2, out of D3 INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_low_power, ar9300PciePhy_clkreq_disable_L1_osprey_2p2, ARRAY_LENGTH(ar9300PciePhy_clkreq_disable_L1_osprey_2p2), 2); } else { INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_low_power, ar9300PciePhy_pll_on_clkreq_disable_L1_osprey_2p2, ARRAY_LENGTH( ar9300PciePhy_pll_on_clkreq_disable_L1_osprey_2p2), 2); } } else { //no registry control, default is pll on INIT_INI_ARRAY( &ahp->ah_ini_pcie_serdes_low_power, ar9300PciePhy_pll_on_clkreq_disable_L1_osprey_2p2, ARRAY_LENGTH( ar9300PciePhy_pll_on_clkreq_disable_L1_osprey_2p2), 2); } } ah->ah_config.ath_hal_pcie_power_save_enable = 0; #ifdef ATH_BUS_PM /*Use HAL to config PCI powersave by writing into the SerDes Registers */ ah->ah_config.ath_hal_pcie_ser_des_write = 1; #endif #if 0 /* ATH_WOW */ /* SerDes values during WOW sleep */ INIT_INI_ARRAY(&ahp->ah_ini_pcie_serdes_wow, ar9300_pcie_phy_awow, ARRAY_LENGTH(ar9300_pcie_phy_awow), 2); #endif /* Fast clock modal settings */ INIT_INI_ARRAY(&ahp->ah_ini_modes_additional, ar9300Modes_fast_clock_osprey_2p2, ARRAY_LENGTH(ar9300Modes_fast_clock_osprey_2p2), 3); INIT_INI_ARRAY(&ahp->ah_ini_japan2484, ar9300_osprey_2p2_baseband_core_txfir_coeff_japan_2484, ARRAY_LENGTH( ar9300_osprey_2p2_baseband_core_txfir_coeff_japan_2484), 2); } if(AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah)) { #define AR_SOC_RST_OTP_INTF 0xB80600B4 #define REG_READ(_reg) *((volatile u_int32_t *)(_reg)) ahp->ah_enterprise_mode = REG_READ(AR_SOC_RST_OTP_INTF); if (AR_SREV_SCORPION(ah)) { ahp->ah_enterprise_mode = ahp->ah_enterprise_mode << 12; } ath_hal_printf (ah, "Enterprise mode: 0x%08x\n", ahp->ah_enterprise_mode); #undef REG_READ #undef AR_SOC_RST_OTP_INTF } else { ahp->ah_enterprise_mode = OS_REG_READ(ah, AR_ENT_OTP); } if (ahpriv->ah_ispcie) { ar9300_config_pci_power_save(ah, 0, 0); } else { ar9300_disable_pcie_phy(ah); } ath_hal_printf(ah, "%s: calling ar9300_hw_attach\n", __func__); ecode = ar9300_hw_attach(ah); if (ecode != HAL_OK) { goto bad; } /* set gain table pointers according to values read from the eeprom */ ar9300_tx_gain_table_apply(ah); ar9300_rx_gain_table_apply(ah); /* ** ** Got everything we need now to setup the capabilities. */ if (!ar9300_fill_capability_info(ah)) { HALDEBUG(ah, HAL_DEBUG_RESET, "%s:failed ar9300_fill_capability_info\n", __func__); ecode = HAL_EEREAD; goto bad; } ecode = ar9300_init_mac_addr(ah); if (ecode != HAL_OK) { HALDEBUG(ah, HAL_DEBUG_RESET, "%s: failed initializing mac address\n", __func__); goto bad; } /* * Initialize receive buffer size to MAC default */ ahp->rx_buf_size = HAL_RXBUFSIZE_DEFAULT; #if ATH_WOW #if 0 /* * Needs to be removed once we stop using XB92 XXX * FIXME: Check with latest boards too - SriniK */ ar9300_wow_set_gpio_reset_low(ah); #endif /* * Clear the Wow Status. */ OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL), OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL)) | AR_PMCTRL_WOW_PME_CLR); OS_REG_WRITE(ah, AR_WOW_PATTERN_REG, AR_WOW_CLEAR_EVENTS(OS_REG_READ(ah, AR_WOW_PATTERN_REG))); #endif /* * Set the cur_trig_level to a value that works all modes - 11a/b/g or 11n * with aggregation enabled or disabled. */ ahp->ah_tx_trig_level = (AR_FTRIG_512B >> AR_FTRIG_S); if (AR_SREV_HORNET(ah)) { ahp->nf_2GHz.nominal = AR_PHY_CCA_NOM_VAL_HORNET_2GHZ; ahp->nf_2GHz.max = AR_PHY_CCA_MAX_GOOD_VAL_OSPREY_2GHZ; ahp->nf_2GHz.min = AR_PHY_CCA_MIN_GOOD_VAL_OSPREY_2GHZ; ahp->nf_5GHz.nominal = AR_PHY_CCA_NOM_VAL_OSPREY_5GHZ; ahp->nf_5GHz.max = AR_PHY_CCA_MAX_GOOD_VAL_OSPREY_5GHZ; ahp->nf_5GHz.min = AR_PHY_CCA_MIN_GOOD_VAL_OSPREY_5GHZ; ahp->nf_cw_int_delta = AR_PHY_CCA_CW_INT_DELTA; } else if(AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)){ ahp->nf_2GHz.nominal = AR_PHY_CCA_NOM_VAL_JUPITER_2GHZ; ahp->nf_2GHz.max = AR_PHY_CCA_MAX_GOOD_VAL_OSPREY_2GHZ; ahp->nf_2GHz.min = AR_PHY_CCA_MIN_GOOD_VAL_JUPITER_2GHZ; ahp->nf_5GHz.nominal = AR_PHY_CCA_NOM_VAL_JUPITER_5GHZ; ahp->nf_5GHz.max = AR_PHY_CCA_MAX_GOOD_VAL_OSPREY_5GHZ; ahp->nf_5GHz.min = AR_PHY_CCA_MIN_GOOD_VAL_JUPITER_5GHZ; ahp->nf_cw_int_delta = AR_PHY_CCA_CW_INT_DELTA; } else { ahp->nf_2GHz.nominal = AR_PHY_CCA_NOM_VAL_OSPREY_2GHZ; ahp->nf_2GHz.max = AR_PHY_CCA_MAX_GOOD_VAL_OSPREY_2GHZ; ahp->nf_2GHz.min = AR_PHY_CCA_MIN_GOOD_VAL_OSPREY_2GHZ; if (AR_SREV_AR9580(ah) || AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah)) { ahp->nf_5GHz.nominal = AR_PHY_CCA_NOM_VAL_PEACOCK_5GHZ; } else { ahp->nf_5GHz.nominal = AR_PHY_CCA_NOM_VAL_OSPREY_5GHZ; } ahp->nf_5GHz.max = AR_PHY_CCA_MAX_GOOD_VAL_OSPREY_5GHZ; ahp->nf_5GHz.min = AR_PHY_CCA_MIN_GOOD_VAL_OSPREY_5GHZ; ahp->nf_cw_int_delta = AR_PHY_CCA_CW_INT_DELTA; } /* init BB Panic Watchdog timeout */ if (AR_SREV_HORNET(ah)) { ahp->ah_bb_panic_timeout_ms = HAL_BB_PANIC_WD_TMO_HORNET; } else { ahp->ah_bb_panic_timeout_ms = HAL_BB_PANIC_WD_TMO; } /* * Determine whether tx IQ calibration HW should be enabled, * and whether tx IQ calibration should be performed during * AGC calibration, or separately. */ if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) { /* * Register not initialized yet. This flag will be re-initialized * after INI loading following each reset. */ ahp->tx_iq_cal_enable = 1; /* if tx IQ cal is enabled, do it together with AGC cal */ ahp->tx_iq_cal_during_agc_cal = 1; } else if (AR_SREV_POSEIDON_OR_LATER(ah) && !AR_SREV_WASP(ah)) { ahp->tx_iq_cal_enable = 1; ahp->tx_iq_cal_during_agc_cal = 1; } else { /* osprey, hornet, wasp */ ahp->tx_iq_cal_enable = 1; ahp->tx_iq_cal_during_agc_cal = 0; } return ah; bad: if (ahp) { ar9300_detach((struct ath_hal *) ahp); } if (status) { *status = ecode; } return AH_NULL; } void ar9300_detach(struct ath_hal *ah) { HALASSERT(ah != AH_NULL); HALASSERT(ah->ah_magic == AR9300_MAGIC); /* Make sure that chip is awake before writing to it */ if (!ar9300_set_power_mode(ah, HAL_PM_AWAKE, AH_TRUE)) { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: failed to wake up chip\n", __func__); } ar9300_hw_detach(ah); ar9300_set_power_mode(ah, HAL_PM_FULL_SLEEP, AH_TRUE); // ath_hal_hdprintf_deregister(ah); if (AH9300(ah)->ah_cal_mem) ath_hal_free(AH9300(ah)->ah_cal_mem); AH9300(ah)->ah_cal_mem = AH_NULL; ath_hal_free(ah); } struct ath_hal_9300 * ar9300_new_state(u_int16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, HAL_OPS_CONFIG *ah_config, HAL_STATUS *status) { static const u_int8_t defbssidmask[IEEE80211_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; struct ath_hal_9300 *ahp; struct ath_hal *ah; /* NB: memory is returned zero'd */ ahp = ath_hal_malloc(sizeof(struct ath_hal_9300)); if (ahp == AH_NULL) { HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE, "%s: cannot allocate memory for state block\n", __func__); *status = HAL_ENOMEM; return AH_NULL; } ah = &ahp->ah_priv.h; /* set initial values */ /* stub everything first */ ar9300_set_stub_functions(ah); /* setup the FreeBSD HAL methods */ ar9300_attach_freebsd_ops(ah); /* These are private to this particular file, so .. */ ah->ah_disablePCIE = ar9300_disable_pcie_phy; AH_PRIVATE(ah)->ah_getNfAdjust = ar9300_get_nf_adjust; AH_PRIVATE(ah)->ah_getChipPowerLimits = ar9300_get_chip_power_limits; #if 0 /* Attach Osprey structure as default hal structure */ OS_MEMCPY(&ahp->ah_priv.priv, &ar9300hal, sizeof(ahp->ah_priv.priv)); #endif #if 0 AH_PRIVATE(ah)->amem_handle = amem_handle; AH_PRIVATE(ah)->ah_osdev = osdev; #endif ah->ah_sc = sc; ah->ah_st = st; ah->ah_sh = sh; ah->ah_magic = AR9300_MAGIC; AH_PRIVATE(ah)->ah_devid = devid; AH_PRIVATE(ah)->ah_flags = 0; /* ** Initialize factory defaults in the private space */ // ath_hal_factory_defaults(AH_PRIVATE(ah), hal_conf_parm); ar9300_config_defaults_freebsd(ah, ah_config); /* XXX FreeBSD: cal is always in EEPROM */ #if 0 if (!hal_conf_parm->calInFlash) { AH_PRIVATE(ah)->ah_flags |= AH_USE_EEPROM; } #endif AH_PRIVATE(ah)->ah_flags |= AH_USE_EEPROM; #if 0 if (ar9300_eep_data_in_flash(ah)) { ahp->ah_priv.priv.ah_eeprom_read = ar9300_flash_read; ahp->ah_priv.priv.ah_eeprom_dump = AH_NULL; } else { ahp->ah_priv.priv.ah_eeprom_read = ar9300_eeprom_read_word; } #endif /* XXX FreeBSD - for now, just supports EEPROM reading */ ahp->ah_priv.ah_eepromRead = ar9300_eeprom_read_word; AH_PRIVATE(ah)->ah_powerLimit = MAX_RATE_POWER; AH_PRIVATE(ah)->ah_tpScale = HAL_TP_SCALE_MAX; /* no scaling */ ahp->ah_atim_window = 0; /* [0..1000] */ ahp->ah_diversity_control = ah->ah_config.ath_hal_diversity_control; ahp->ah_antenna_switch_swap = ah->ah_config.ath_hal_antenna_switch_swap; /* * Enable MIC handling. */ ahp->ah_sta_id1_defaults = AR_STA_ID1_CRPT_MIC_ENABLE; ahp->ah_enable32k_hz_clock = DONT_USE_32KHZ;/* XXX */ ahp->ah_slot_time = (u_int) -1; ahp->ah_ack_timeout = (u_int) -1; OS_MEMCPY(&ahp->ah_bssid_mask, defbssidmask, IEEE80211_ADDR_LEN); /* * 11g-specific stuff */ ahp->ah_g_beacon_rate = 0; /* adhoc beacon fixed rate */ /* SM power mode: Attach time, disable any setting */ ahp->ah_sm_power_mode = HAL_SMPS_DEFAULT; return ahp; } HAL_BOOL ar9300_chip_test(struct ath_hal *ah) { /*u_int32_t reg_addr[2] = { AR_STA_ID0, AR_PHY_BASE+(8 << 2) };*/ u_int32_t reg_addr[2] = { AR_STA_ID0 }; u_int32_t reg_hold[2]; u_int32_t pattern_data[4] = { 0x55555555, 0xaaaaaaaa, 0x66666666, 0x99999999 }; int i, j; /* Test PHY & MAC registers */ for (i = 0; i < 1; i++) { u_int32_t addr = reg_addr[i]; u_int32_t wr_data, rd_data; reg_hold[i] = OS_REG_READ(ah, addr); for (j = 0; j < 0x100; j++) { wr_data = (j << 16) | j; OS_REG_WRITE(ah, addr, wr_data); rd_data = OS_REG_READ(ah, addr); if (rd_data != wr_data) { HALDEBUG(ah, HAL_DEBUG_REGIO, "%s: address test failed addr: " "0x%08x - wr:0x%08x != rd:0x%08x\n", __func__, addr, wr_data, rd_data); return AH_FALSE; } } for (j = 0; j < 4; j++) { wr_data = pattern_data[j]; OS_REG_WRITE(ah, addr, wr_data); rd_data = OS_REG_READ(ah, addr); if (wr_data != rd_data) { HALDEBUG(ah, HAL_DEBUG_REGIO, "%s: address test failed addr: " "0x%08x - wr:0x%08x != rd:0x%08x\n", __func__, addr, wr_data, rd_data); return AH_FALSE; } } OS_REG_WRITE(ah, reg_addr[i], reg_hold[i]); } OS_DELAY(100); return AH_TRUE; } /* * Store the channel edges for the requested operational mode */ HAL_BOOL ar9300_get_channel_edges(struct ath_hal *ah, u_int16_t flags, u_int16_t *low, u_int16_t *high) { struct ath_hal_private *ahpriv = AH_PRIVATE(ah); HAL_CAPABILITIES *p_cap = &ahpriv->ah_caps; if (flags & IEEE80211_CHAN_5GHZ) { *low = p_cap->halLow5GhzChan; *high = p_cap->halHigh5GhzChan; return AH_TRUE; } if ((flags & IEEE80211_CHAN_2GHZ)) { *low = p_cap->halLow2GhzChan; *high = p_cap->halHigh2GhzChan; return AH_TRUE; } return AH_FALSE; } HAL_BOOL ar9300_regulatory_domain_override(struct ath_hal *ah, u_int16_t regdmn) { AH_PRIVATE(ah)->ah_currentRD = regdmn; return AH_TRUE; } /* * Fill all software cached or static hardware state information. * Return failure if capabilities are to come from EEPROM and * cannot be read. */ HAL_BOOL ar9300_fill_capability_info(struct ath_hal *ah) { #define AR_KEYTABLE_SIZE 128 struct ath_hal_9300 *ahp = AH9300(ah); struct ath_hal_private *ahpriv = AH_PRIVATE(ah); HAL_CAPABILITIES *p_cap = &ahpriv->ah_caps; u_int16_t cap_field = 0, eeval; ahpriv->ah_devType = (u_int16_t)ar9300_eeprom_get(ahp, EEP_DEV_TYPE); eeval = ar9300_eeprom_get(ahp, EEP_REG_0); /* XXX record serial number */ AH_PRIVATE(ah)->ah_currentRD = eeval; /* Always enable fast clock; leave it up to EEPROM and channel */ p_cap->halSupportsFastClock5GHz = AH_TRUE; p_cap->halIntrMitigation = AH_TRUE; eeval = ar9300_eeprom_get(ahp, EEP_REG_1); AH_PRIVATE(ah)->ah_currentRDext = eeval | AR9300_RDEXT_DEFAULT; /* Read the capability EEPROM location */ cap_field = ar9300_eeprom_get(ahp, EEP_OP_CAP); /* Construct wireless mode from EEPROM */ p_cap->halWirelessModes = 0; eeval = ar9300_eeprom_get(ahp, EEP_OP_MODE); /* * XXX FreeBSD specific: for now, set ath_hal_ht_enable to 1, * or we won't have 11n support. */ ah->ah_config.ath_hal_ht_enable = 1; if (eeval & AR9300_OPFLAGS_11A) { p_cap->halWirelessModes |= HAL_MODE_11A | ((!ah->ah_config.ath_hal_ht_enable || (eeval & AR9300_OPFLAGS_N_5G_HT20)) ? 0 : (HAL_MODE_11NA_HT20 | ((eeval & AR9300_OPFLAGS_N_5G_HT40) ? 0 : (HAL_MODE_11NA_HT40PLUS | HAL_MODE_11NA_HT40MINUS)))); } if (eeval & AR9300_OPFLAGS_11G) { p_cap->halWirelessModes |= HAL_MODE_11B | HAL_MODE_11G | ((!ah->ah_config.ath_hal_ht_enable || (eeval & AR9300_OPFLAGS_N_2G_HT20)) ? 0 : (HAL_MODE_11NG_HT20 | ((eeval & AR9300_OPFLAGS_N_2G_HT40) ? 0 : (HAL_MODE_11NG_HT40PLUS | HAL_MODE_11NG_HT40MINUS)))); } /* Get chainamsks from eeprom */ p_cap->halTxChainMask = ar9300_eeprom_get(ahp, EEP_TX_MASK); p_cap->halRxChainMask = ar9300_eeprom_get(ahp, EEP_RX_MASK); #define owl_get_ntxchains(_txchainmask) \ (((_txchainmask >> 2) & 1) + ((_txchainmask >> 1) & 1) + (_txchainmask & 1)) /* FreeBSD: Update number of TX/RX streams */ p_cap->halTxStreams = owl_get_ntxchains(p_cap->halTxChainMask); p_cap->halRxStreams = owl_get_ntxchains(p_cap->halRxChainMask); /* * This being a newer chip supports TKIP non-splitmic mode. * */ ahp->ah_misc_mode |= AR_PCU_MIC_NEW_LOC_ENA; p_cap->halTkipMicTxRxKeySupport = AH_TRUE; p_cap->halLow2GhzChan = 2312; p_cap->halHigh2GhzChan = 2732; p_cap->halLow5GhzChan = 4920; p_cap->halHigh5GhzChan = 6100; p_cap->halCipherCkipSupport = AH_FALSE; p_cap->halCipherTkipSupport = AH_TRUE; p_cap->halCipherAesCcmSupport = AH_TRUE; p_cap->halMicCkipSupport = AH_FALSE; p_cap->halMicTkipSupport = AH_TRUE; p_cap->halMicAesCcmSupport = AH_TRUE; p_cap->halChanSpreadSupport = AH_TRUE; p_cap->halSleepAfterBeaconBroken = AH_TRUE; p_cap->halBurstSupport = AH_TRUE; p_cap->halChapTuningSupport = AH_TRUE; p_cap->halTurboPrimeSupport = AH_TRUE; p_cap->halFastFramesSupport = AH_FALSE; p_cap->halTurboGSupport = p_cap->halWirelessModes & HAL_MODE_108G; // p_cap->hal_xr_support = AH_FALSE; p_cap->halHTSupport = ah->ah_config.ath_hal_ht_enable ? AH_TRUE : AH_FALSE; p_cap->halGTTSupport = AH_TRUE; p_cap->halPSPollBroken = AH_TRUE; /* XXX fixed in later revs? */ p_cap->halNumMRRetries = 4; /* Hardware supports 4 MRR */ p_cap->halHTSGI20Support = AH_TRUE; p_cap->halVEOLSupport = AH_TRUE; p_cap->halBssIdMaskSupport = AH_TRUE; /* Bug 26802, fixed in later revs? */ p_cap->halMcastKeySrchSupport = AH_TRUE; p_cap->halTsfAddSupport = AH_TRUE; if (cap_field & AR_EEPROM_EEPCAP_MAXQCU) { p_cap->halTotalQueues = MS(cap_field, AR_EEPROM_EEPCAP_MAXQCU); } else { p_cap->halTotalQueues = HAL_NUM_TX_QUEUES; } if (cap_field & AR_EEPROM_EEPCAP_KC_ENTRIES) { p_cap->halKeyCacheSize = 1 << MS(cap_field, AR_EEPROM_EEPCAP_KC_ENTRIES); } else { p_cap->halKeyCacheSize = AR_KEYTABLE_SIZE; } p_cap->halFastCCSupport = AH_TRUE; // p_cap->hal_num_mr_retries = 4; // ahp->hal_tx_trig_level_max = MAX_TX_FIFO_THRESHOLD; p_cap->halNumGpioPins = AR9382_MAX_GPIO_PIN_NUM; #if 0 /* XXX Verify support in Osprey */ if (AR_SREV_MERLIN_10_OR_LATER(ah)) { p_cap->halWowSupport = AH_TRUE; p_cap->hal_wow_match_pattern_exact = AH_TRUE; if (AR_SREV_MERLIN(ah)) { p_cap->hal_wow_pattern_match_dword = AH_TRUE; } } else { p_cap->halWowSupport = AH_FALSE; p_cap->hal_wow_match_pattern_exact = AH_FALSE; } #endif p_cap->halWowSupport = AH_TRUE; p_cap->halWowMatchPatternExact = AH_TRUE; if (AR_SREV_POSEIDON(ah)) { p_cap->halWowMatchPatternExact = AH_TRUE; } p_cap->halCSTSupport = AH_TRUE; p_cap->halRifsRxSupport = AH_TRUE; p_cap->halRifsTxSupport = AH_TRUE; #define IEEE80211_AMPDU_LIMIT_MAX (65536) p_cap->halRtsAggrLimit = IEEE80211_AMPDU_LIMIT_MAX; #undef IEEE80211_AMPDU_LIMIT_MAX p_cap->halMfpSupport = ah->ah_config.ath_hal_mfp_support; p_cap->halForcePpmSupport = AH_TRUE; p_cap->halHwBeaconProcSupport = AH_TRUE; /* ar9300 - has the HW UAPSD trigger support, * but it has the following limitations * The power state change from the following * frames are not put in High priority queue. * i) Mgmt frames * ii) NoN QoS frames * iii) QoS frames form the access categories for which * UAPSD is not enabled. * so we can not enable this feature currently. * could be enabled, if these limitations are fixed * in later versions of ar9300 chips */ p_cap->halHasUapsdSupport = AH_FALSE; /* Number of buffers that can be help in a single TxD */ p_cap->halNumTxMaps = 4; p_cap->halTxDescLen = sizeof(struct ar9300_txc); p_cap->halTxStatusLen = sizeof(struct ar9300_txs); p_cap->halRxStatusLen = sizeof(struct ar9300_rxs); p_cap->halRxHpFifoDepth = HAL_HP_RXFIFO_DEPTH; p_cap->halRxLpFifoDepth = HAL_LP_RXFIFO_DEPTH; /* Enable extension channel DFS support */ p_cap->halUseCombinedRadarRssi = AH_TRUE; p_cap->halExtChanDfsSupport = AH_TRUE; #if ATH_SUPPORT_SPECTRAL p_cap->halSpectralScanSupport = AH_TRUE; #endif ahpriv->ah_rfsilent = ar9300_eeprom_get(ahp, EEP_RF_SILENT); if (ahpriv->ah_rfsilent & EEP_RFSILENT_ENABLED) { ahp->ah_gpio_select = MS(ahpriv->ah_rfsilent, EEP_RFSILENT_GPIO_SEL); ahp->ah_polarity = MS(ahpriv->ah_rfsilent, EEP_RFSILENT_POLARITY); ath_hal_enable_rfkill(ah, AH_TRUE); p_cap->halRfSilentSupport = AH_TRUE; } /* XXX */ p_cap->halWpsPushButtonSupport = AH_FALSE; #ifdef ATH_BT_COEX p_cap->halBtCoexSupport = AH_TRUE; p_cap->halBtCoexApsmWar = AH_FALSE; #endif p_cap->halGenTimerSupport = AH_TRUE; ahp->ah_avail_gen_timers = ~((1 << AR_FIRST_NDP_TIMER) - 1); ahp->ah_avail_gen_timers &= (1 << AR_NUM_GEN_TIMERS) - 1; /* * According to Kyungwan, generic timer 0 and 8 are special * timers. Remove timer 8 from the available gen timer list. * Jupiter testing shows timer won't trigger with timer 8. */ ahp->ah_avail_gen_timers &= ~(1 << AR_GEN_TIMER_RESERVED); if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) { #if ATH_SUPPORT_MCI if (ah->ah_config.ath_hal_mci_config & ATH_MCI_CONFIG_DISABLE_MCI) { p_cap->halMciSupport = AH_FALSE; } else #endif { p_cap->halMciSupport = (ahp->ah_enterprise_mode & AR_ENT_OTP_49GHZ_DISABLE) ? AH_FALSE: AH_TRUE; } HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE, "%s: (MCI) MCI support = %d\n", __func__, p_cap->halMciSupport); } else { p_cap->halMciSupport = AH_FALSE; } if (AR_SREV_JUPITER_20(ah)) { p_cap->halRadioRetentionSupport = AH_TRUE; } else { p_cap->halRadioRetentionSupport = AH_FALSE; } p_cap->halAutoSleepSupport = AH_TRUE; p_cap->halMbssidAggrSupport = AH_TRUE; // p_cap->hal_proxy_sta_support = AH_TRUE; /* XXX Mark it true after it is verfied as fixed */ p_cap->hal4kbSplitTransSupport = AH_FALSE; /* Read regulatory domain flag */ if (AH_PRIVATE(ah)->ah_currentRDext & (1 << REG_EXT_JAPAN_MIDBAND)) { /* * If REG_EXT_JAPAN_MIDBAND is set, turn on U1 EVEN, U2, and MIDBAND. */ p_cap->halRegCap = AR_EEPROM_EEREGCAP_EN_KK_NEW_11A | AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN | AR_EEPROM_EEREGCAP_EN_KK_U2 | AR_EEPROM_EEREGCAP_EN_KK_MIDBAND; } else { p_cap->halRegCap = AR_EEPROM_EEREGCAP_EN_KK_NEW_11A | AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN; } /* For AR9300 and above, midband channels are always supported */ p_cap->halRegCap |= AR_EEPROM_EEREGCAP_EN_FCC_MIDBAND; p_cap->halNumAntCfg5GHz = ar9300_eeprom_get_num_ant_config(ahp, HAL_FREQ_BAND_5GHZ); p_cap->halNumAntCfg2GHz = ar9300_eeprom_get_num_ant_config(ahp, HAL_FREQ_BAND_2GHZ); /* STBC supported */ p_cap->halRxStbcSupport = 1; /* number of streams for STBC recieve. */ if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah) || AR_SREV_APHRODITE(ah)) { p_cap->halTxStbcSupport = 0; } else { p_cap->halTxStbcSupport = 1; } p_cap->halEnhancedDmaSupport = AH_TRUE; p_cap->halEnhancedDfsSupport = AH_TRUE; /* * EV61133 (missing interrupts due to AR_ISR_RAC). * Fixed in Osprey 2.0. */ p_cap->halIsrRacSupport = AH_TRUE; /* XXX FreeBSD won't support TKIP and WEP aggregation */ #if 0 p_cap->hal_wep_tkip_aggr_support = AH_TRUE; p_cap->hal_wep_tkip_aggr_num_tx_delim = 10; /* TBD */ p_cap->hal_wep_tkip_aggr_num_rx_delim = 10; /* TBD */ p_cap->hal_wep_tkip_max_ht_rate = 15; /* TBD */ #endif /* * XXX FreeBSD won't need these; but eventually add them * and add the WARs - AGGR extra delim WAR is useful to know * about. */ #if 0 p_cap->hal_cfend_fix_support = AH_FALSE; p_cap->hal_aggr_extra_delim_war = AH_FALSE; #endif p_cap->halHasLongRxDescTsf = AH_TRUE; // p_cap->hal_rx_desc_timestamp_bits = 32; p_cap->halRxTxAbortSupport = AH_TRUE; p_cap->hal_ani_poll_interval = AR9300_ANI_POLLINTERVAL; p_cap->hal_channel_switch_time_usec = AR9300_CHANNEL_SWITCH_TIME_USEC; /* Transmit Beamforming supported, fill capabilities */ p_cap->halPaprdEnabled = ar9300_eeprom_get(ahp, EEP_PAPRD_ENABLED); p_cap->halChanHalfRate = !(ahp->ah_enterprise_mode & AR_ENT_OTP_10MHZ_DISABLE); p_cap->halChanQuarterRate = !(ahp->ah_enterprise_mode & AR_ENT_OTP_5MHZ_DISABLE); if(AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)){ /* There is no AR_ENT_OTP_49GHZ_DISABLE feature in Jupiter, now the bit is used to disable BT. */ p_cap->hal49GhzSupport = 1; } else { p_cap->hal49GhzSupport = !(ahp->ah_enterprise_mode & AR_ENT_OTP_49GHZ_DISABLE); } if (AR_SREV_POSEIDON(ah) || AR_SREV_HORNET(ah) || AR_SREV_APHRODITE(ah)) { /* LDPC supported */ /* Poseidon doesn't support LDPC, or it will cause receiver CRC Error */ p_cap->halLDPCSupport = AH_FALSE; /* PCI_E LCR offset */ if (AR_SREV_POSEIDON(ah)) { p_cap->hal_pcie_lcr_offset = 0x80; /*for Poseidon*/ } /*WAR method for APSM L0s with Poseidon 1.0*/ if (AR_SREV_POSEIDON_10(ah)) { p_cap->hal_pcie_lcr_extsync_en = AH_TRUE; } } else { p_cap->halLDPCSupport = AH_TRUE; } /* XXX is this a flag, or a chainmask number? */ p_cap->halApmEnable = !! ar9300_eeprom_get(ahp, EEP_CHAIN_MASK_REDUCE); #if ATH_ANT_DIV_COMB if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON_11_OR_LATER(ah)) { if (ahp->ah_diversity_control == HAL_ANT_VARIABLE) { u_int8_t ant_div_control1 = ar9300_eeprom_get(ahp, EEP_ANTDIV_control); /* if enable_lnadiv is 0x1 and enable_fast_div is 0x1, * we enable the diversity-combining algorithm. */ if ((ant_div_control1 >> 0x6) == 0x3) { p_cap->halAntDivCombSupport = AH_TRUE; } p_cap->halAntDivCombSupportOrg = p_cap->halAntDivCombSupport; } } #endif /* ATH_ANT_DIV_COMB */ /* * FreeBSD: enable LNA mixing if the chip is Hornet or Poseidon. */ if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON_11_OR_LATER(ah)) { p_cap->halRxUsingLnaMixing = AH_TRUE; } /* * AR5416 and later NICs support MYBEACON filtering. */ p_cap->halRxDoMyBeacon = AH_TRUE; #if ATH_WOW_OFFLOAD if (AR_SREV_JUPITER_20_OR_LATER(ah) || AR_SREV_APHRODITE(ah)) { p_cap->hal_wow_gtk_offload_support = AH_TRUE; p_cap->hal_wow_arp_offload_support = AH_TRUE; p_cap->hal_wow_ns_offload_support = AH_TRUE; p_cap->hal_wow_4way_hs_wakeup_support = AH_TRUE; p_cap->hal_wow_acer_magic_support = AH_TRUE; p_cap->hal_wow_acer_swka_support = AH_TRUE; } else { p_cap->hal_wow_gtk_offload_support = AH_FALSE; p_cap->hal_wow_arp_offload_support = AH_FALSE; p_cap->hal_wow_ns_offload_support = AH_FALSE; p_cap->hal_wow_4way_hs_wakeup_support = AH_FALSE; p_cap->hal_wow_acer_magic_support = AH_FALSE; p_cap->hal_wow_acer_swka_support = AH_FALSE; } #endif /* ATH_WOW_OFFLOAD */ return AH_TRUE; #undef AR_KEYTABLE_SIZE } #if 0 static HAL_BOOL ar9300_get_chip_power_limits(struct ath_hal *ah, HAL_CHANNEL *chans, u_int32_t nchans) { struct ath_hal_9300 *ahp = AH9300(ah); return ahp->ah_rf_hal.get_chip_power_lim(ah, chans, nchans); } #endif /* XXX FreeBSD */ static HAL_BOOL ar9300_get_chip_power_limits(struct ath_hal *ah, struct ieee80211_channel *chan) { chan->ic_maxpower = AR9300_MAX_RATE_POWER; chan->ic_minpower = 0; return AH_TRUE; } /* * Disable PLL when in L0s as well as receiver clock when in L1. * This power saving option must be enabled through the Serdes. * * Programming the Serdes must go through the same 288 bit serial shift * register as the other analog registers. Hence the 9 writes. * * XXX Clean up the magic numbers. */ void ar9300_config_pci_power_save(struct ath_hal *ah, int restore, int power_off) { struct ath_hal_9300 *ahp = AH9300(ah); int i; if (AH_PRIVATE(ah)->ah_ispcie != AH_TRUE) { return; } /* * Increase L1 Entry Latency. Some WB222 boards don't have * this change in eeprom/OTP. */ if (AR_SREV_JUPITER(ah)) { u_int32_t val = ah->ah_config.ath_hal_war70c; if ((val & 0xff000000) == 0x17000000) { val &= 0x00ffffff; val |= 0x27000000; OS_REG_WRITE(ah, 0x570c, val); } } /* Do not touch SERDES registers */ if (ah->ah_config.ath_hal_pcie_power_save_enable == 2) { return; } /* Nothing to do on restore for 11N */ if (!restore) { /* set bit 19 to allow forcing of pcie core into L1 state */ OS_REG_SET_BIT(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL), AR_PCIE_PM_CTRL_ENA); /* * Set PCIE workaround config only if requested, else use the reset * value of this register. */ if (ah->ah_config.ath_hal_pcie_waen) { OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA), ah->ah_config.ath_hal_pcie_waen); } else { /* Set Bits 17 and 14 in the AR_WA register. */ OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA), ahp->ah_wa_reg_val); } } /* Configure PCIE after Ini init. SERDES values now come from ini file */ if (ah->ah_config.ath_hal_pcie_ser_des_write) { if (power_off) { for (i = 0; i < ahp->ah_ini_pcie_serdes.ia_rows; i++) { OS_REG_WRITE(ah, INI_RA(&ahp->ah_ini_pcie_serdes, i, 0), INI_RA(&ahp->ah_ini_pcie_serdes, i, 1)); } } else { for (i = 0; i < ahp->ah_ini_pcie_serdes_low_power.ia_rows; i++) { OS_REG_WRITE(ah, INI_RA(&ahp->ah_ini_pcie_serdes_low_power, i, 0), INI_RA(&ahp->ah_ini_pcie_serdes_low_power, i, 1)); } } } } /* * Recipe from charles to turn off PCIe PHY in PCI mode for power savings */ void ar9300_disable_pcie_phy(struct ath_hal *ah) { /* Osprey does not support PCI mode */ } static inline HAL_STATUS ar9300_init_mac_addr(struct ath_hal *ah) { u_int32_t sum; int i; u_int16_t eeval; struct ath_hal_9300 *ahp = AH9300(ah); u_int32_t EEP_MAC [] = { EEP_MAC_LSW, EEP_MAC_MID, EEP_MAC_MSW }; sum = 0; for (i = 0; i < 3; i++) { eeval = ar9300_eeprom_get(ahp, EEP_MAC[i]); sum += eeval; ahp->ah_macaddr[2*i] = eeval >> 8; ahp->ah_macaddr[2*i + 1] = eeval & 0xff; } if (sum == 0 || sum == 0xffff*3) { HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: mac address read failed: %s\n", __func__, ath_hal_ether_sprintf(ahp->ah_macaddr)); return HAL_EEBADMAC; } return HAL_OK; } /* * Code for the "real" chip i.e. non-emulation. Review and revisit * when actual hardware is at hand. */ static inline HAL_STATUS ar9300_hw_attach(struct ath_hal *ah) { HAL_STATUS ecode; if (!ar9300_chip_test(ah)) { HALDEBUG(ah, HAL_DEBUG_REGIO, "%s: hardware self-test failed\n", __func__); return HAL_ESELFTEST; } ath_hal_printf(ah, "%s: calling ar9300_eeprom_attach\n", __func__); ecode = ar9300_eeprom_attach(ah); ath_hal_printf(ah, "%s: ar9300_eeprom_attach returned %d\n", __func__, ecode); if (ecode != HAL_OK) { return ecode; } if (!ar9300_rf_attach(ah, &ecode)) { HALDEBUG(ah, HAL_DEBUG_RESET, "%s: RF setup failed, status %u\n", __func__, ecode); } if (ecode != HAL_OK) { return ecode; } ar9300_ani_attach(ah); return HAL_OK; } static inline void ar9300_hw_detach(struct ath_hal *ah) { /* XXX EEPROM allocated state */ ar9300_ani_detach(ah); } static int16_t ar9300_get_nf_adjust(struct ath_hal *ah, const HAL_CHANNEL_INTERNAL *c) { return 0; } void ar9300_set_immunity(struct ath_hal *ah, HAL_BOOL enable) { struct ath_hal_9300 *ahp = AH9300(ah); u_int32_t m1_thresh_low = enable ? 127 : ahp->ah_immunity_vals[0], m2_thresh_low = enable ? 127 : ahp->ah_immunity_vals[1], m1_thresh = enable ? 127 : ahp->ah_immunity_vals[2], m2_thresh = enable ? 127 : ahp->ah_immunity_vals[3], m2_count_thr = enable ? 31 : ahp->ah_immunity_vals[4], m2_count_thr_low = enable ? 63 : ahp->ah_immunity_vals[5]; if (ahp->ah_immunity_on == enable) { return; } ahp->ah_immunity_on = enable; OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_M1_THRESH_LOW, m1_thresh_low); OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_M2_THRESH_LOW, m2_thresh_low); OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, AR_PHY_SFCORR_M1_THRESH, m1_thresh); OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, AR_PHY_SFCORR_M2_THRESH, m2_thresh); OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, AR_PHY_SFCORR_M2COUNT_THR, m2_count_thr); OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW, m2_count_thr_low); OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, AR_PHY_SFCORR_EXT_M1_THRESH_LOW, m1_thresh_low); OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, AR_PHY_SFCORR_EXT_M2_THRESH_LOW, m2_thresh_low); OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, AR_PHY_SFCORR_EXT_M1_THRESH, m1_thresh); OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, AR_PHY_SFCORR_EXT_M2_THRESH, m2_thresh); if (!enable) { OS_REG_SET_BIT(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); } else { OS_REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); } } /* XXX FreeBSD: I'm not sure how to implement this.. */ #if 0 int ar9300_get_cal_intervals(struct ath_hal *ah, HAL_CALIBRATION_TIMER **timerp, HAL_CAL_QUERY query) { #define AR9300_IS_CHAIN_RX_IQCAL_INVALID(_ah, _reg) \ ((OS_REG_READ((_ah), _reg) & 0x3fff) == 0) #define AR9300_IS_RX_IQCAL_DISABLED(_ah) \ (!(OS_REG_READ((_ah), AR_PHY_RX_IQCAL_CORR_B0) & \ AR_PHY_RX_IQCAL_CORR_IQCORR_ENABLE)) /* Avoid comilation warnings. Variables are not used when EMULATION. */ struct ath_hal_9300 *ahp = AH9300(ah); u_int8_t rxchainmask = ahp->ah_rx_chainmask, i; int rx_iqcal_invalid = 0, num_chains = 0; static const u_int32_t offset_array[3] = { AR_PHY_RX_IQCAL_CORR_B0, AR_PHY_RX_IQCAL_CORR_B1, AR_PHY_RX_IQCAL_CORR_B2}; *timerp = ar9300_cals; switch (query) { case HAL_QUERY_CALS: return AR9300_NUM_CAL_TYPES; case HAL_QUERY_RERUN_CALS: for (i = 0; i < AR9300_MAX_CHAINS; i++) { if (rxchainmask & (1 << i)) { num_chains++; } } for (i = 0; i < num_chains; i++) { if (AR_SREV_POSEIDON(ah) || AR_SREV_APHRODITE(ah)) { HALASSERT(num_chains == 0x1); } if (AR9300_IS_CHAIN_RX_IQCAL_INVALID(ah, offset_array[i])) { rx_iqcal_invalid = 1; } } if (AR9300_IS_RX_IQCAL_DISABLED(ah)) { rx_iqcal_invalid = 1; } return rx_iqcal_invalid; default: HALASSERT(0); } return 0; } #endif #if ATH_TRAFFIC_FAST_RECOVER #define PLL3 0x16188 #define PLL3_DO_MEAS_MASK 0x40000000 #define PLL4 0x1618c #define PLL4_MEAS_DONE 0x8 #define SQSUM_DVC_MASK 0x007ffff8 unsigned long ar9300_get_pll3_sqsum_dvc(struct ath_hal *ah) { if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah) || AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah)) { OS_REG_WRITE(ah, PLL3, (OS_REG_READ(ah, PLL3) & ~(PLL3_DO_MEAS_MASK))); OS_DELAY(100); OS_REG_WRITE(ah, PLL3, (OS_REG_READ(ah, PLL3) | PLL3_DO_MEAS_MASK)); while ( (OS_REG_READ(ah, PLL4) & PLL4_MEAS_DONE) == 0) { OS_DELAY(100); } return (( OS_REG_READ(ah, PLL3) & SQSUM_DVC_MASK ) >> 3); } else { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: unable to get pll3_sqsum_dvc\n", __func__); return 0; } } #endif #define RX_GAIN_TABLE_LENGTH 128 // this will be called if rfGainCAP is enabled and rfGainCAP setting is changed, // or rxGainTable setting is changed HAL_BOOL ar9300_rf_gain_cap_apply(struct ath_hal *ah, int is_2GHz) { int i, done = 0, i_rx_gain = 32; u_int32_t rf_gain_cap; u_int32_t rx_gain_value, a_Byte, rx_gain_value_caped; static u_int32_t rx_gain_table[RX_GAIN_TABLE_LENGTH * 2][2]; ar9300_eeprom_t *eep = &AH9300(ah)->ah_eeprom; struct ath_hal_9300 *ahp = AH9300(ah); if ( !((eep->base_eep_header.misc_configuration & 0x80) >> 7) ) return AH_FALSE; if (is_2GHz) { rf_gain_cap = (u_int32_t) eep->modal_header_2g.rf_gain_cap; } else { rf_gain_cap = (u_int32_t) eep->modal_header_5g.rf_gain_cap; } if (rf_gain_cap == 0) return AH_FALSE; for (i = 0; i< RX_GAIN_TABLE_LENGTH * 2; i++) { if (AR_SREV_AR9580(ah)) { // BB_rx_ocgain2 i_rx_gain = 128 + 32; switch (ar9300_rx_gain_index_get(ah)) { case 0: rx_gain_table[i][0] = ar9300_common_rx_gain_table_ar9580_1p0[i][0]; rx_gain_table[i][1] = ar9300_common_rx_gain_table_ar9580_1p0[i][1]; break; case 1: rx_gain_table[i][0] = ar9300_common_wo_xlna_rx_gain_table_ar9580_1p0[i][0]; rx_gain_table[i][1] = ar9300_common_wo_xlna_rx_gain_table_ar9580_1p0[i][1]; break; } } else if (AR_SREV_OSPREY_22(ah)) { i_rx_gain = 128 + 32; switch (ar9300_rx_gain_index_get(ah)) { case 0: rx_gain_table[i][0] = ar9300_common_rx_gain_table_osprey_2p2[i][0]; rx_gain_table[i][1] = ar9300_common_rx_gain_table_osprey_2p2[i][1]; break; case 1: rx_gain_table[i][0] = ar9300Common_wo_xlna_rx_gain_table_osprey_2p2[i][0]; rx_gain_table[i][1] = ar9300Common_wo_xlna_rx_gain_table_osprey_2p2[i][1]; break; } } else { return AH_FALSE; } } while (1) { rx_gain_value = rx_gain_table[i_rx_gain][1]; rx_gain_value_caped = rx_gain_value; a_Byte = rx_gain_value & (0x000000FF); if (a_Byte>rf_gain_cap) { rx_gain_value_caped = (rx_gain_value_caped & (0xFFFFFF00)) + rf_gain_cap; } a_Byte = rx_gain_value & (0x0000FF00); if ( a_Byte > ( rf_gain_cap << 8 ) ) { rx_gain_value_caped = (rx_gain_value_caped & (0xFFFF00FF)) + (rf_gain_cap<<8); } a_Byte = rx_gain_value & (0x00FF0000); if ( a_Byte > ( rf_gain_cap << 16 ) ) { rx_gain_value_caped = (rx_gain_value_caped & (0xFF00FFFF)) + (rf_gain_cap<<16); } a_Byte = rx_gain_value & (0xFF000000); if ( a_Byte > ( rf_gain_cap << 24 ) ) { rx_gain_value_caped = (rx_gain_value_caped & (0x00FFFFFF)) + (rf_gain_cap<<24); } else { done = 1; } HALDEBUG(ah, HAL_DEBUG_RESET, "%s: rx_gain_address: %x, rx_gain_value: %x rx_gain_value_caped: %x\n", __func__, rx_gain_table[i_rx_gain][0], rx_gain_value, rx_gain_value_caped); if (rx_gain_value_caped != rx_gain_value) { rx_gain_table[i_rx_gain][1] = rx_gain_value_caped; } if (done == 1) break; i_rx_gain ++; } INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, rx_gain_table, ARRAY_LENGTH(rx_gain_table), 2); return AH_TRUE; } void ar9300_rx_gain_table_apply(struct ath_hal *ah) { struct ath_hal_9300 *ahp = AH9300(ah); //struct ath_hal_private *ahpriv = AH_PRIVATE(ah); u_int32_t xlan_gpio_cfg; u_int8_t i; if (AR_SREV_OSPREY(ah) || AR_SREV_AR9580(ah)) { // this will be called if rxGainTable setting is changed if (ar9300_rf_gain_cap_apply(ah, 1)) return; } switch (ar9300_rx_gain_index_get(ah)) { case 2: if (AR_SREV_JUPITER_10(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar9300_common_mixed_rx_gain_table_jupiter_1p0, ARRAY_LENGTH(ar9300_common_mixed_rx_gain_table_jupiter_1p0), 2); break; } else if (AR_SREV_JUPITER_20(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar9300Common_mixed_rx_gain_table_jupiter_2p0, ARRAY_LENGTH(ar9300Common_mixed_rx_gain_table_jupiter_2p0), 2); break; } case 0: default: if (AR_SREV_HORNET_12(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar9331_common_rx_gain_hornet1_2, ARRAY_LENGTH(ar9331_common_rx_gain_hornet1_2), 2); } else if (AR_SREV_HORNET_11(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar9331_common_rx_gain_hornet1_1, ARRAY_LENGTH(ar9331_common_rx_gain_hornet1_1), 2); } else if (AR_SREV_POSEIDON_11_OR_LATER(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar9485_common_wo_xlna_rx_gain_poseidon1_1, ARRAY_LENGTH(ar9485_common_wo_xlna_rx_gain_poseidon1_1), 2); /* XXX FreeBSD: this needs to be revisited!! */ xlan_gpio_cfg = ah->ah_config.ath_hal_ext_lna_ctl_gpio; if (xlan_gpio_cfg) { for (i = 0; i < 32; i++) { if (xlan_gpio_cfg & (1 << i)) { /* * XXX FreeBSD: definitely make sure this * results in the correct value being written * to the hardware, or weird crap is very likely * to occur! */ ath_hal_gpioCfgOutput(ah, i, HAL_GPIO_OUTPUT_MUX_PCIE_ATTENTION_LED); } } } } else if (AR_SREV_POSEIDON(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar9485Common_wo_xlna_rx_gain_poseidon1_0, ARRAY_LENGTH(ar9485Common_wo_xlna_rx_gain_poseidon1_0), 2); } else if (AR_SREV_JUPITER_10(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar9300_common_rx_gain_table_jupiter_1p0, ARRAY_LENGTH(ar9300_common_rx_gain_table_jupiter_1p0), 2); } else if (AR_SREV_JUPITER_20(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar9300Common_rx_gain_table_jupiter_2p0, ARRAY_LENGTH(ar9300Common_rx_gain_table_jupiter_2p0), 2); } else if (AR_SREV_AR9580(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar9300_common_rx_gain_table_ar9580_1p0, ARRAY_LENGTH(ar9300_common_rx_gain_table_ar9580_1p0), 2); } else if (AR_SREV_WASP(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar9340Common_rx_gain_table_wasp_1p0, ARRAY_LENGTH(ar9340Common_rx_gain_table_wasp_1p0), 2); } else if (AR_SREV_SCORPION(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar955xCommon_rx_gain_table_scorpion_1p0, ARRAY_LENGTH(ar955xCommon_rx_gain_table_scorpion_1p0), 2); INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain_bounds, ar955xCommon_rx_gain_bounds_scorpion_1p0, ARRAY_LENGTH(ar955xCommon_rx_gain_bounds_scorpion_1p0), 5); } else { INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar9300_common_rx_gain_table_osprey_2p2, ARRAY_LENGTH(ar9300_common_rx_gain_table_osprey_2p2), 2); } break; case 1: if (AR_SREV_HORNET_12(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar9331_common_wo_xlna_rx_gain_hornet1_2, ARRAY_LENGTH(ar9331_common_wo_xlna_rx_gain_hornet1_2), 2); } else if (AR_SREV_HORNET_11(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar9331_common_wo_xlna_rx_gain_hornet1_1, ARRAY_LENGTH(ar9331_common_wo_xlna_rx_gain_hornet1_1), 2); } else if (AR_SREV_POSEIDON_11_OR_LATER(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar9485_common_wo_xlna_rx_gain_poseidon1_1, ARRAY_LENGTH(ar9485_common_wo_xlna_rx_gain_poseidon1_1), 2); } else if (AR_SREV_POSEIDON(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar9485Common_wo_xlna_rx_gain_poseidon1_0, ARRAY_LENGTH(ar9485Common_wo_xlna_rx_gain_poseidon1_0), 2); } else if (AR_SREV_JUPITER_10(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar9300_common_wo_xlna_rx_gain_table_jupiter_1p0, ARRAY_LENGTH(ar9300_common_wo_xlna_rx_gain_table_jupiter_1p0), 2); } else if (AR_SREV_JUPITER_20(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar9300Common_wo_xlna_rx_gain_table_jupiter_2p0, ARRAY_LENGTH(ar9300Common_wo_xlna_rx_gain_table_jupiter_2p0), 2); } else if (AR_SREV_APHRODITE(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar956XCommon_wo_xlna_rx_gain_table_aphrodite_1p0, ARRAY_LENGTH(ar956XCommon_wo_xlna_rx_gain_table_aphrodite_1p0), 2); } else if (AR_SREV_AR9580(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar9300_common_wo_xlna_rx_gain_table_ar9580_1p0, ARRAY_LENGTH(ar9300_common_wo_xlna_rx_gain_table_ar9580_1p0), 2); } else if (AR_SREV_WASP(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar9340Common_wo_xlna_rx_gain_table_wasp_1p0, ARRAY_LENGTH(ar9340Common_wo_xlna_rx_gain_table_wasp_1p0), 2); } else if (AR_SREV_SCORPION(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar955xCommon_wo_xlna_rx_gain_table_scorpion_1p0, ARRAY_LENGTH(ar955xCommon_wo_xlna_rx_gain_table_scorpion_1p0), 2); INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain_bounds, ar955xCommon_wo_xlna_rx_gain_bounds_scorpion_1p0, ARRAY_LENGTH(ar955xCommon_wo_xlna_rx_gain_bounds_scorpion_1p0), 5); } else { INIT_INI_ARRAY(&ahp->ah_ini_modes_rxgain, ar9300Common_wo_xlna_rx_gain_table_osprey_2p2, ARRAY_LENGTH(ar9300Common_wo_xlna_rx_gain_table_osprey_2p2), 2); } break; } } void ar9300_tx_gain_table_apply(struct ath_hal *ah) { struct ath_hal_9300 *ahp = AH9300(ah); switch (ar9300_tx_gain_index_get(ah)) { case 0: default: if (AR_SREV_HORNET_12(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9331_modes_lowest_ob_db_tx_gain_hornet1_2, ARRAY_LENGTH(ar9331_modes_lowest_ob_db_tx_gain_hornet1_2), 5); } else if (AR_SREV_HORNET_11(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9331_modes_lowest_ob_db_tx_gain_hornet1_1, ARRAY_LENGTH(ar9331_modes_lowest_ob_db_tx_gain_hornet1_1), 5); } else if (AR_SREV_POSEIDON_11_OR_LATER(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9485_modes_lowest_ob_db_tx_gain_poseidon1_1, ARRAY_LENGTH(ar9485_modes_lowest_ob_db_tx_gain_poseidon1_1), 5); } else if (AR_SREV_POSEIDON(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9485Modes_lowest_ob_db_tx_gain_poseidon1_0, ARRAY_LENGTH(ar9485Modes_lowest_ob_db_tx_gain_poseidon1_0), 5); } else if (AR_SREV_AR9580(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9300Modes_lowest_ob_db_tx_gain_table_ar9580_1p0, ARRAY_LENGTH(ar9300Modes_lowest_ob_db_tx_gain_table_ar9580_1p0), 5); } else if (AR_SREV_WASP(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9340Modes_lowest_ob_db_tx_gain_table_wasp_1p0, ARRAY_LENGTH(ar9340Modes_lowest_ob_db_tx_gain_table_wasp_1p0), 5); } else if (AR_SREV_SCORPION(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar955xModes_xpa_tx_gain_table_scorpion_1p0, ARRAY_LENGTH(ar955xModes_xpa_tx_gain_table_scorpion_1p0), 9); } else if (AR_SREV_JUPITER_10(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9300_modes_low_ob_db_tx_gain_table_jupiter_1p0, ARRAY_LENGTH(ar9300_modes_low_ob_db_tx_gain_table_jupiter_1p0), 5); } else if (AR_SREV_JUPITER_20(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9300Modes_low_ob_db_tx_gain_table_jupiter_2p0, ARRAY_LENGTH(ar9300Modes_low_ob_db_tx_gain_table_jupiter_2p0), 5); } else if (AR_SREV_APHRODITE(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar956XModes_low_ob_db_tx_gain_table_aphrodite_1p0, ARRAY_LENGTH(ar956XModes_low_ob_db_tx_gain_table_aphrodite_1p0), 5); } else { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9300_modes_lowest_ob_db_tx_gain_table_osprey_2p2, ARRAY_LENGTH(ar9300_modes_lowest_ob_db_tx_gain_table_osprey_2p2), 5); } break; case 1: if (AR_SREV_HORNET_12(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9331_modes_high_ob_db_tx_gain_hornet1_2, ARRAY_LENGTH(ar9331_modes_high_ob_db_tx_gain_hornet1_2), 5); } else if (AR_SREV_HORNET_11(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9331_modes_high_ob_db_tx_gain_hornet1_1, ARRAY_LENGTH(ar9331_modes_high_ob_db_tx_gain_hornet1_1), 5); } else if (AR_SREV_POSEIDON_11_OR_LATER(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9485_modes_high_ob_db_tx_gain_poseidon1_1, ARRAY_LENGTH(ar9485_modes_high_ob_db_tx_gain_poseidon1_1), 5); } else if (AR_SREV_POSEIDON(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9485Modes_high_ob_db_tx_gain_poseidon1_0, ARRAY_LENGTH(ar9485Modes_high_ob_db_tx_gain_poseidon1_0), 5); } else if (AR_SREV_AR9580(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9300Modes_high_ob_db_tx_gain_table_ar9580_1p0, ARRAY_LENGTH(ar9300Modes_high_ob_db_tx_gain_table_ar9580_1p0), 5); } else if (AR_SREV_WASP(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9340Modes_high_ob_db_tx_gain_table_wasp_1p0, ARRAY_LENGTH(ar9340Modes_high_ob_db_tx_gain_table_wasp_1p0), 5); } else if (AR_SREV_SCORPION(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar955xModes_no_xpa_tx_gain_table_scorpion_1p0, ARRAY_LENGTH(ar955xModes_no_xpa_tx_gain_table_scorpion_1p0), 9); } else if (AR_SREV_JUPITER_10(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9300_modes_high_ob_db_tx_gain_table_jupiter_1p0, ARRAY_LENGTH( ar9300_modes_high_ob_db_tx_gain_table_jupiter_1p0), 5); } else if (AR_SREV_JUPITER_20(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9300Modes_high_ob_db_tx_gain_table_jupiter_2p0, ARRAY_LENGTH( ar9300Modes_high_ob_db_tx_gain_table_jupiter_2p0), 5); } else if (AR_SREV_APHRODITE(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar956XModes_high_ob_db_tx_gain_table_aphrodite_1p0, ARRAY_LENGTH( ar956XModes_high_ob_db_tx_gain_table_aphrodite_1p0), 5); } else { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9300Modes_high_ob_db_tx_gain_table_osprey_2p2, ARRAY_LENGTH(ar9300Modes_high_ob_db_tx_gain_table_osprey_2p2), 5); } break; case 2: if (AR_SREV_HORNET_12(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9331_modes_low_ob_db_tx_gain_hornet1_2, ARRAY_LENGTH(ar9331_modes_low_ob_db_tx_gain_hornet1_2), 5); } else if (AR_SREV_HORNET_11(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9331_modes_low_ob_db_tx_gain_hornet1_1, ARRAY_LENGTH(ar9331_modes_low_ob_db_tx_gain_hornet1_1), 5); } else if (AR_SREV_POSEIDON_11_OR_LATER(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9485_modes_low_ob_db_tx_gain_poseidon1_1, ARRAY_LENGTH(ar9485_modes_low_ob_db_tx_gain_poseidon1_1), 5); } else if (AR_SREV_POSEIDON(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9485Modes_low_ob_db_tx_gain_poseidon1_0, ARRAY_LENGTH(ar9485Modes_low_ob_db_tx_gain_poseidon1_0), 5); } else if (AR_SREV_AR9580(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9300Modes_low_ob_db_tx_gain_table_ar9580_1p0, ARRAY_LENGTH(ar9300Modes_low_ob_db_tx_gain_table_ar9580_1p0), 5); } else if (AR_SREV_WASP(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9340Modes_low_ob_db_tx_gain_table_wasp_1p0, ARRAY_LENGTH(ar9340Modes_low_ob_db_tx_gain_table_wasp_1p0), 5); } else if (AR_SREV_APHRODITE(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar956XModes_low_ob_db_tx_gain_table_aphrodite_1p0, ARRAY_LENGTH(ar956XModes_low_ob_db_tx_gain_table_aphrodite_1p0), 5); } else { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9300Modes_low_ob_db_tx_gain_table_osprey_2p2, ARRAY_LENGTH(ar9300Modes_low_ob_db_tx_gain_table_osprey_2p2), 5); } break; case 3: if (AR_SREV_HORNET_12(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9331_modes_high_power_tx_gain_hornet1_2, ARRAY_LENGTH(ar9331_modes_high_power_tx_gain_hornet1_2), 5); } else if (AR_SREV_HORNET_11(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9331_modes_high_power_tx_gain_hornet1_1, ARRAY_LENGTH(ar9331_modes_high_power_tx_gain_hornet1_1), 5); } else if (AR_SREV_POSEIDON_11_OR_LATER(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9485_modes_high_power_tx_gain_poseidon1_1, ARRAY_LENGTH(ar9485_modes_high_power_tx_gain_poseidon1_1), 5); } else if (AR_SREV_POSEIDON(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9485Modes_high_power_tx_gain_poseidon1_0, ARRAY_LENGTH(ar9485Modes_high_power_tx_gain_poseidon1_0), 5); } else if (AR_SREV_AR9580(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9300Modes_high_power_tx_gain_table_ar9580_1p0, ARRAY_LENGTH(ar9300Modes_high_power_tx_gain_table_ar9580_1p0), 5); } else if (AR_SREV_WASP(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9340Modes_high_power_tx_gain_table_wasp_1p0, ARRAY_LENGTH(ar9340Modes_high_power_tx_gain_table_wasp_1p0), 5); } else if (AR_SREV_APHRODITE(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar956XModes_high_power_tx_gain_table_aphrodite_1p0, ARRAY_LENGTH(ar956XModes_high_power_tx_gain_table_aphrodite_1p0), 5); } else { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9300Modes_high_power_tx_gain_table_osprey_2p2, ARRAY_LENGTH(ar9300Modes_high_power_tx_gain_table_osprey_2p2), 5); } break; case 4: if (AR_SREV_WASP(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9340Modes_mixed_ob_db_tx_gain_table_wasp_1p0, ARRAY_LENGTH(ar9340Modes_mixed_ob_db_tx_gain_table_wasp_1p0), 5); } else if (AR_SREV_AR9580(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9300_modes_mixed_ob_db_tx_gain_table_ar9580_1p0, ARRAY_LENGTH(ar9300_modes_mixed_ob_db_tx_gain_table_ar9580_1p0), 5); } else { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9300Modes_mixed_ob_db_tx_gain_table_osprey_2p2, ARRAY_LENGTH(ar9300Modes_mixed_ob_db_tx_gain_table_osprey_2p2), 5); } break; case 5: /* HW Green TX */ if (AR_SREV_POSEIDON(ah)) { if (AR_SREV_POSEIDON_11_OR_LATER(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9485_modes_green_ob_db_tx_gain_poseidon1_1, sizeof(ar9485_modes_green_ob_db_tx_gain_poseidon1_1) / sizeof(ar9485_modes_green_ob_db_tx_gain_poseidon1_1[0]), 5); } else { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9485_modes_green_ob_db_tx_gain_poseidon1_0, sizeof(ar9485_modes_green_ob_db_tx_gain_poseidon1_0) / sizeof(ar9485_modes_green_ob_db_tx_gain_poseidon1_0[0]), 5); } ahp->ah_hw_green_tx_enable = 1; } else if (AR_SREV_WASP(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9340_modes_ub124_tx_gain_table_wasp_1p0, sizeof(ar9340_modes_ub124_tx_gain_table_wasp_1p0) / sizeof(ar9340_modes_ub124_tx_gain_table_wasp_1p0[0]), 5); } else if (AR_SREV_AR9580(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9300_modes_type5_tx_gain_table_ar9580_1p0, ARRAY_LENGTH( ar9300_modes_type5_tx_gain_table_ar9580_1p0), 5); } else if (AR_SREV_OSPREY_22(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9300_modes_number_5_tx_gain_table_osprey_2p2, ARRAY_LENGTH( ar9300_modes_number_5_tx_gain_table_osprey_2p2), 5); } break; case 6: if (AR_SREV_WASP(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9340_modes_low_ob_db_and_spur_tx_gain_table_wasp_1p0, sizeof(ar9340_modes_low_ob_db_and_spur_tx_gain_table_wasp_1p0) / sizeof(ar9340_modes_low_ob_db_and_spur_tx_gain_table_wasp_1p0[0]), 5); } /* HW Green TX */ else if (AR_SREV_POSEIDON(ah)) { if (AR_SREV_POSEIDON_11_OR_LATER(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9485_modes_green_spur_ob_db_tx_gain_poseidon1_1, sizeof(ar9485_modes_green_spur_ob_db_tx_gain_poseidon1_1) / sizeof(ar9485_modes_green_spur_ob_db_tx_gain_poseidon1_1[0]), 5); } ahp->ah_hw_green_tx_enable = 1; } else if (AR_SREV_AR9580(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9300_modes_type6_tx_gain_table_ar9580_1p0, ARRAY_LENGTH( ar9300_modes_type6_tx_gain_table_ar9580_1p0), 5); } break; case 7: if (AR_SREV_WASP(ah)) { INIT_INI_ARRAY(&ahp->ah_ini_modes_txgain, ar9340Modes_cus227_tx_gain_table_wasp_1p0, sizeof(ar9340Modes_cus227_tx_gain_table_wasp_1p0) / sizeof(ar9340Modes_cus227_tx_gain_table_wasp_1p0[0]), 5); } break; } } #if ATH_ANT_DIV_COMB void ar9300_ant_div_comb_get_config(struct ath_hal *ah, HAL_ANT_COMB_CONFIG *div_comb_conf) { u_int32_t reg_val = OS_REG_READ(ah, AR_PHY_MC_GAIN_CTRL); div_comb_conf->main_lna_conf = MULTICHAIN_GAIN_CTRL__ANT_DIV_MAIN_LNACONF__READ(reg_val); div_comb_conf->alt_lna_conf = MULTICHAIN_GAIN_CTRL__ANT_DIV_ALT_LNACONF__READ(reg_val); div_comb_conf->fast_div_bias = MULTICHAIN_GAIN_CTRL__ANT_FAST_DIV_BIAS__READ(reg_val); if (AR_SREV_HORNET_11(ah)) { div_comb_conf->antdiv_configgroup = HAL_ANTDIV_CONFIG_GROUP_1; } else if (AR_SREV_POSEIDON_11_OR_LATER(ah)) { div_comb_conf->antdiv_configgroup = HAL_ANTDIV_CONFIG_GROUP_2; } else { div_comb_conf->antdiv_configgroup = DEFAULT_ANTDIV_CONFIG_GROUP; } /* * XXX TODO: allow the HAL to override the rssithres and fast_div_bias * values (eg CUS198.) */ } void ar9300_ant_div_comb_set_config(struct ath_hal *ah, HAL_ANT_COMB_CONFIG *div_comb_conf) { u_int32_t reg_val; struct ath_hal_9300 *ahp = AH9300(ah); /* DO NOTHING when set to fixed antenna for manufacturing purpose */ if (AR_SREV_POSEIDON(ah) && ( ahp->ah_diversity_control == HAL_ANT_FIXED_A || ahp->ah_diversity_control == HAL_ANT_FIXED_B)) { return; } reg_val = OS_REG_READ(ah, AR_PHY_MC_GAIN_CTRL); reg_val &= ~(MULTICHAIN_GAIN_CTRL__ANT_DIV_MAIN_LNACONF__MASK | MULTICHAIN_GAIN_CTRL__ANT_DIV_ALT_LNACONF__MASK | MULTICHAIN_GAIN_CTRL__ANT_FAST_DIV_BIAS__MASK | MULTICHAIN_GAIN_CTRL__ANT_DIV_MAIN_GAINTB__MASK | MULTICHAIN_GAIN_CTRL__ANT_DIV_ALT_GAINTB__MASK ); reg_val |= MULTICHAIN_GAIN_CTRL__ANT_DIV_MAIN_GAINTB__WRITE( div_comb_conf->main_gaintb); reg_val |= MULTICHAIN_GAIN_CTRL__ANT_DIV_ALT_GAINTB__WRITE( div_comb_conf->alt_gaintb); reg_val |= MULTICHAIN_GAIN_CTRL__ANT_DIV_MAIN_LNACONF__WRITE( div_comb_conf->main_lna_conf); reg_val |= MULTICHAIN_GAIN_CTRL__ANT_DIV_ALT_LNACONF__WRITE( div_comb_conf->alt_lna_conf); reg_val |= MULTICHAIN_GAIN_CTRL__ANT_FAST_DIV_BIAS__WRITE( div_comb_conf->fast_div_bias); OS_REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, reg_val); } #endif /* ATH_ANT_DIV_COMB */ static void ar9300_init_hostif_offsets(struct ath_hal *ah) { AR_HOSTIF_REG(ah, AR_RC) = AR9300_HOSTIF_OFFSET(HOST_INTF_RESET_CONTROL); AR_HOSTIF_REG(ah, AR_WA) = AR9300_HOSTIF_OFFSET(HOST_INTF_WORK_AROUND); AR_HOSTIF_REG(ah, AR_PM_STATE) = AR9300_HOSTIF_OFFSET(HOST_INTF_PM_STATE); AR_HOSTIF_REG(ah, AR_H_INFOL) = AR9300_HOSTIF_OFFSET(HOST_INTF_CXPL_DEBUG_INFOL); AR_HOSTIF_REG(ah, AR_H_INFOH) = AR9300_HOSTIF_OFFSET(HOST_INTF_CXPL_DEBUG_INFOH); AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL) = AR9300_HOSTIF_OFFSET(HOST_INTF_PM_CTRL); AR_HOSTIF_REG(ah, AR_HOST_TIMEOUT) = AR9300_HOSTIF_OFFSET(HOST_INTF_TIMEOUT); AR_HOSTIF_REG(ah, AR_EEPROM) = AR9300_HOSTIF_OFFSET(HOST_INTF_EEPROM_CTRL); AR_HOSTIF_REG(ah, AR_SREV) = AR9300_HOSTIF_OFFSET(HOST_INTF_SREV); AR_HOSTIF_REG(ah, AR_INTR_SYNC_CAUSE) = AR9300_HOSTIF_OFFSET(HOST_INTF_INTR_SYNC_CAUSE); AR_HOSTIF_REG(ah, AR_INTR_SYNC_CAUSE_CLR) = AR9300_HOSTIF_OFFSET(HOST_INTF_INTR_SYNC_CAUSE); AR_HOSTIF_REG(ah, AR_INTR_SYNC_ENABLE) = AR9300_HOSTIF_OFFSET(HOST_INTF_INTR_SYNC_ENABLE); AR_HOSTIF_REG(ah, AR_INTR_ASYNC_MASK) = AR9300_HOSTIF_OFFSET(HOST_INTF_INTR_ASYNC_MASK); AR_HOSTIF_REG(ah, AR_INTR_SYNC_MASK) = AR9300_HOSTIF_OFFSET(HOST_INTF_INTR_SYNC_MASK); AR_HOSTIF_REG(ah, AR_INTR_ASYNC_CAUSE_CLR) = AR9300_HOSTIF_OFFSET(HOST_INTF_INTR_ASYNC_CAUSE); AR_HOSTIF_REG(ah, AR_INTR_ASYNC_CAUSE) = AR9300_HOSTIF_OFFSET(HOST_INTF_INTR_ASYNC_CAUSE); AR_HOSTIF_REG(ah, AR_INTR_ASYNC_ENABLE) = AR9300_HOSTIF_OFFSET(HOST_INTF_INTR_ASYNC_ENABLE); AR_HOSTIF_REG(ah, AR_PCIE_SERDES) = AR9300_HOSTIF_OFFSET(HOST_INTF_PCIE_PHY_RW); AR_HOSTIF_REG(ah, AR_PCIE_SERDES2) = AR9300_HOSTIF_OFFSET(HOST_INTF_PCIE_PHY_LOAD); AR_HOSTIF_REG(ah, AR_GPIO_OUT) = AR9300_HOSTIF_OFFSET(HOST_INTF_GPIO_OUT); AR_HOSTIF_REG(ah, AR_GPIO_IN) = AR9300_HOSTIF_OFFSET(HOST_INTF_GPIO_IN); AR_HOSTIF_REG(ah, AR_GPIO_OE_OUT) = AR9300_HOSTIF_OFFSET(HOST_INTF_GPIO_OE); AR_HOSTIF_REG(ah, AR_GPIO_OE1_OUT) = AR9300_HOSTIF_OFFSET(HOST_INTF_GPIO_OE1); AR_HOSTIF_REG(ah, AR_GPIO_INTR_POL) = AR9300_HOSTIF_OFFSET(HOST_INTF_GPIO_INTR_POLAR); AR_HOSTIF_REG(ah, AR_GPIO_INPUT_EN_VAL) = AR9300_HOSTIF_OFFSET(HOST_INTF_GPIO_INPUT_VALUE); AR_HOSTIF_REG(ah, AR_GPIO_INPUT_MUX1) = AR9300_HOSTIF_OFFSET(HOST_INTF_GPIO_INPUT_MUX1); AR_HOSTIF_REG(ah, AR_GPIO_INPUT_MUX2) = AR9300_HOSTIF_OFFSET(HOST_INTF_GPIO_INPUT_MUX2); AR_HOSTIF_REG(ah, AR_GPIO_OUTPUT_MUX1) = AR9300_HOSTIF_OFFSET(HOST_INTF_GPIO_OUTPUT_MUX1); AR_HOSTIF_REG(ah, AR_GPIO_OUTPUT_MUX2) = AR9300_HOSTIF_OFFSET(HOST_INTF_GPIO_OUTPUT_MUX2); AR_HOSTIF_REG(ah, AR_GPIO_OUTPUT_MUX3) = AR9300_HOSTIF_OFFSET(HOST_INTF_GPIO_OUTPUT_MUX3); AR_HOSTIF_REG(ah, AR_INPUT_STATE) = AR9300_HOSTIF_OFFSET(HOST_INTF_GPIO_INPUT_STATE); AR_HOSTIF_REG(ah, AR_SPARE) = AR9300_HOSTIF_OFFSET(HOST_INTF_SPARE); AR_HOSTIF_REG(ah, AR_PCIE_CORE_RESET_EN) = AR9300_HOSTIF_OFFSET(HOST_INTF_PCIE_CORE_RST_EN); AR_HOSTIF_REG(ah, AR_CLKRUN) = AR9300_HOSTIF_OFFSET(HOST_INTF_CLKRUN); AR_HOSTIF_REG(ah, AR_EEPROM_STATUS_DATA) = AR9300_HOSTIF_OFFSET(HOST_INTF_EEPROM_STS); AR_HOSTIF_REG(ah, AR_OBS) = AR9300_HOSTIF_OFFSET(HOST_INTF_OBS_CTRL); AR_HOSTIF_REG(ah, AR_RFSILENT) = AR9300_HOSTIF_OFFSET(HOST_INTF_RFSILENT); AR_HOSTIF_REG(ah, AR_GPIO_PDPU) = AR9300_HOSTIF_OFFSET(HOST_INTF_GPIO_PDPU); AR_HOSTIF_REG(ah, AR_GPIO_DS) = AR9300_HOSTIF_OFFSET(HOST_INTF_GPIO_DS); AR_HOSTIF_REG(ah, AR_MISC) = AR9300_HOSTIF_OFFSET(HOST_INTF_MISC); AR_HOSTIF_REG(ah, AR_PCIE_MSI) = AR9300_HOSTIF_OFFSET(HOST_INTF_PCIE_MSI); #if 0 /* Offsets are not defined in reg_map structure */ AR_HOSTIF_REG(ah, AR_TSF_SNAPSHOT_BT_ACTIVE) = AR9300_HOSTIF_OFFSET(HOST_INTF_TSF_SNAPSHOT_BT_ACTIVE); AR_HOSTIF_REG(ah, AR_TSF_SNAPSHOT_BT_PRIORITY) = AR9300_HOSTIF_OFFSET(HOST_INTF_TSF_SNAPSHOT_BT_PRIORITY); AR_HOSTIF_REG(ah, AR_TSF_SNAPSHOT_BT_CNTL) = AR9300_HOSTIF_OFFSET(HOST_INTF_MAC_TSF_SNAPSHOT_BT_CNTL); #endif AR_HOSTIF_REG(ah, AR_PCIE_PHY_LATENCY_NFTS_ADJ) = AR9300_HOSTIF_OFFSET(HOST_INTF_PCIE_PHY_LATENCY_NFTS_ADJ); AR_HOSTIF_REG(ah, AR_TDMA_CCA_CNTL) = AR9300_HOSTIF_OFFSET(HOST_INTF_MAC_TDMA_CCA_CNTL); AR_HOSTIF_REG(ah, AR_TXAPSYNC) = AR9300_HOSTIF_OFFSET(HOST_INTF_MAC_TXAPSYNC); AR_HOSTIF_REG(ah, AR_TXSYNC_INIT_SYNC_TMR) = AR9300_HOSTIF_OFFSET(HOST_INTF_MAC_TXSYNC_INITIAL_SYNC_TMR); AR_HOSTIF_REG(ah, AR_INTR_PRIO_SYNC_CAUSE) = AR9300_HOSTIF_OFFSET(HOST_INTF_INTR_PRIORITY_SYNC_CAUSE); AR_HOSTIF_REG(ah, AR_INTR_PRIO_SYNC_ENABLE) = AR9300_HOSTIF_OFFSET(HOST_INTF_INTR_PRIORITY_SYNC_ENABLE); AR_HOSTIF_REG(ah, AR_INTR_PRIO_ASYNC_MASK) = AR9300_HOSTIF_OFFSET(HOST_INTF_INTR_PRIORITY_ASYNC_MASK); AR_HOSTIF_REG(ah, AR_INTR_PRIO_SYNC_MASK) = AR9300_HOSTIF_OFFSET(HOST_INTF_INTR_PRIORITY_SYNC_MASK); AR_HOSTIF_REG(ah, AR_INTR_PRIO_ASYNC_CAUSE) = AR9300_HOSTIF_OFFSET(HOST_INTF_INTR_PRIORITY_ASYNC_CAUSE); AR_HOSTIF_REG(ah, AR_INTR_PRIO_ASYNC_ENABLE) = AR9300_HOSTIF_OFFSET(HOST_INTF_INTR_PRIORITY_ASYNC_ENABLE); } static void ar9340_init_hostif_offsets(struct ath_hal *ah) { AR_HOSTIF_REG(ah, AR_RC) = AR9340_HOSTIF_OFFSET(HOST_INTF_RESET_CONTROL); AR_HOSTIF_REG(ah, AR_WA) = AR9340_HOSTIF_OFFSET(HOST_INTF_WORK_AROUND); AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL) = AR9340_HOSTIF_OFFSET(HOST_INTF_PM_CTRL); AR_HOSTIF_REG(ah, AR_HOST_TIMEOUT) = AR9340_HOSTIF_OFFSET(HOST_INTF_TIMEOUT); AR_HOSTIF_REG(ah, AR_SREV) = AR9340_HOSTIF_OFFSET(HOST_INTF_SREV); AR_HOSTIF_REG(ah, AR_INTR_SYNC_CAUSE) = AR9340_HOSTIF_OFFSET(HOST_INTF_INTR_SYNC_CAUSE); AR_HOSTIF_REG(ah, AR_INTR_SYNC_CAUSE_CLR) = AR9340_HOSTIF_OFFSET(HOST_INTF_INTR_SYNC_CAUSE); AR_HOSTIF_REG(ah, AR_INTR_SYNC_ENABLE) = AR9340_HOSTIF_OFFSET(HOST_INTF_INTR_SYNC_ENABLE); AR_HOSTIF_REG(ah, AR_INTR_ASYNC_MASK) = AR9340_HOSTIF_OFFSET(HOST_INTF_INTR_ASYNC_MASK); AR_HOSTIF_REG(ah, AR_INTR_SYNC_MASK) = AR9340_HOSTIF_OFFSET(HOST_INTF_INTR_SYNC_MASK); AR_HOSTIF_REG(ah, AR_INTR_ASYNC_CAUSE_CLR) = AR9340_HOSTIF_OFFSET(HOST_INTF_INTR_ASYNC_CAUSE); AR_HOSTIF_REG(ah, AR_INTR_ASYNC_CAUSE) = AR9340_HOSTIF_OFFSET(HOST_INTF_INTR_ASYNC_CAUSE); AR_HOSTIF_REG(ah, AR_INTR_ASYNC_ENABLE) = AR9340_HOSTIF_OFFSET(HOST_INTF_INTR_ASYNC_ENABLE); AR_HOSTIF_REG(ah, AR_GPIO_OUT) = AR9340_HOSTIF_OFFSET(HOST_INTF_GPIO_OUT); AR_HOSTIF_REG(ah, AR_GPIO_IN) = AR9340_HOSTIF_OFFSET(HOST_INTF_GPIO_IN); AR_HOSTIF_REG(ah, AR_GPIO_OE_OUT) = AR9340_HOSTIF_OFFSET(HOST_INTF_GPIO_OE); AR_HOSTIF_REG(ah, AR_GPIO_OE1_OUT) = AR9340_HOSTIF_OFFSET(HOST_INTF_GPIO_OE1); AR_HOSTIF_REG(ah, AR_GPIO_INTR_POL) = AR9340_HOSTIF_OFFSET(HOST_INTF_GPIO_INTR_POLAR); AR_HOSTIF_REG(ah, AR_GPIO_INPUT_EN_VAL) = AR9340_HOSTIF_OFFSET(HOST_INTF_GPIO_INPUT_VALUE); AR_HOSTIF_REG(ah, AR_GPIO_INPUT_MUX1) = AR9340_HOSTIF_OFFSET(HOST_INTF_GPIO_INPUT_MUX1); AR_HOSTIF_REG(ah, AR_GPIO_INPUT_MUX2) = AR9340_HOSTIF_OFFSET(HOST_INTF_GPIO_INPUT_MUX2); AR_HOSTIF_REG(ah, AR_GPIO_OUTPUT_MUX1) = AR9340_HOSTIF_OFFSET(HOST_INTF_GPIO_OUTPUT_MUX1); AR_HOSTIF_REG(ah, AR_GPIO_OUTPUT_MUX2) = AR9340_HOSTIF_OFFSET(HOST_INTF_GPIO_OUTPUT_MUX2); AR_HOSTIF_REG(ah, AR_GPIO_OUTPUT_MUX3) = AR9340_HOSTIF_OFFSET(HOST_INTF_GPIO_OUTPUT_MUX3); AR_HOSTIF_REG(ah, AR_INPUT_STATE) = AR9340_HOSTIF_OFFSET(HOST_INTF_GPIO_INPUT_STATE); AR_HOSTIF_REG(ah, AR_CLKRUN) = AR9340_HOSTIF_OFFSET(HOST_INTF_CLKRUN); AR_HOSTIF_REG(ah, AR_EEPROM_STATUS_DATA) = AR9340_HOSTIF_OFFSET(HOST_INTF_EEPROM_STS); AR_HOSTIF_REG(ah, AR_OBS) = AR9340_HOSTIF_OFFSET(HOST_INTF_OBS_CTRL); AR_HOSTIF_REG(ah, AR_RFSILENT) = AR9340_HOSTIF_OFFSET(HOST_INTF_RFSILENT); AR_HOSTIF_REG(ah, AR_MISC) = AR9340_HOSTIF_OFFSET(HOST_INTF_MISC); AR_HOSTIF_REG(ah, AR_PCIE_MSI) = AR9340_HOSTIF_OFFSET(HOST_INTF_PCIE_MSI); AR_HOSTIF_REG(ah, AR_TDMA_CCA_CNTL) = AR9340_HOSTIF_OFFSET(HOST_INTF_MAC_TDMA_CCA_CNTL); AR_HOSTIF_REG(ah, AR_TXAPSYNC) = AR9340_HOSTIF_OFFSET(HOST_INTF_MAC_TXAPSYNC); AR_HOSTIF_REG(ah, AR_TXSYNC_INIT_SYNC_TMR) = AR9340_HOSTIF_OFFSET(HOST_INTF_MAC_TXSYNC_INITIAL_SYNC_TMR); AR_HOSTIF_REG(ah, AR_INTR_PRIO_SYNC_CAUSE) = AR9340_HOSTIF_OFFSET(HOST_INTF_INTR_PRIORITY_SYNC_CAUSE); AR_HOSTIF_REG(ah, AR_INTR_PRIO_SYNC_ENABLE) = AR9340_HOSTIF_OFFSET(HOST_INTF_INTR_PRIORITY_SYNC_ENABLE); AR_HOSTIF_REG(ah, AR_INTR_PRIO_ASYNC_MASK) = AR9340_HOSTIF_OFFSET(HOST_INTF_INTR_PRIORITY_ASYNC_MASK); AR_HOSTIF_REG(ah, AR_INTR_PRIO_SYNC_MASK) = AR9340_HOSTIF_OFFSET(HOST_INTF_INTR_PRIORITY_SYNC_MASK); AR_HOSTIF_REG(ah, AR_INTR_PRIO_ASYNC_CAUSE) = AR9340_HOSTIF_OFFSET(HOST_INTF_INTR_PRIORITY_ASYNC_CAUSE); AR_HOSTIF_REG(ah, AR_INTR_PRIO_ASYNC_ENABLE) = AR9340_HOSTIF_OFFSET(HOST_INTF_INTR_PRIORITY_ASYNC_ENABLE); } /* * Host interface register offsets are different for Osprey and Wasp * and hence store the offsets in hal structure */ static int ar9300_init_offsets(struct ath_hal *ah, u_int16_t devid) { if (devid == AR9300_DEVID_AR9340) { ar9340_init_hostif_offsets(ah); } else { ar9300_init_hostif_offsets(ah); } return 0; } static const char* ar9300_probe(uint16_t vendorid, uint16_t devid) { if (vendorid != ATHEROS_VENDOR_ID) return AH_NULL; switch (devid) { case AR9300_DEVID_AR9380_PCIE: /* PCIE (Osprey) */ return "Atheros AR938x"; case AR9300_DEVID_AR9340: /* Wasp */ return "Atheros AR934x"; case AR9300_DEVID_AR9485_PCIE: /* Poseidon */ return "Atheros AR9485"; case AR9300_DEVID_AR9580_PCIE: /* Peacock */ return "Atheros AR9580"; case AR9300_DEVID_AR946X_PCIE: /* AR9462, AR9463, AR9482 */ return "Atheros AR946x/AR948x"; case AR9300_DEVID_AR9330: /* Hornet */ return "Atheros AR933x"; case AR9300_DEVID_QCA955X: /* Scorpion */ return "Qualcomm Atheros QCA955x"; case AR9300_DEVID_QCA9565: /* Aphrodite */ return "Qualcomm Atheros AR9565"; case AR9300_DEVID_AR1111_PCIE: return "Atheros AR1111"; default: return AH_NULL; } return AH_NULL; } AH_CHIP(AR9300, ar9300_probe, ar9300_attach); Index: projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_beacon.c =================================================================== --- projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_beacon.c (revision 278776) +++ projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_beacon.c (revision 278777) @@ -1,195 +1,201 @@ /* * Copyright (c) 2013 Qualcomm Atheros, Inc. * * 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 "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ar9300/ar9300.h" #include "ar9300/ar9300reg.h" #define TU_TO_USEC(_tu) ((_tu) << 10) #define ONE_EIGHTH_TU_TO_USEC(_tu8) ((_tu8) << 7) extern u_int32_t ar9300_num_tx_pending(struct ath_hal *ah, u_int q); /* * Initializes all of the hardware registers used to * send beacons. Note that for station operation the * driver calls ar9300_set_sta_beacon_timers instead. */ void ar9300_beacon_init(struct ath_hal *ah, - u_int32_t next_beacon, u_int32_t beacon_period, HAL_OPMODE opmode) + u_int32_t next_beacon, u_int32_t beacon_period, + u_int32_t beacon_period_fraction, HAL_OPMODE opmode) { u_int32_t beacon_period_usec; HALASSERT(opmode == HAL_M_IBSS || opmode == HAL_M_HOSTAP); if (opmode == HAL_M_IBSS) { OS_REG_SET_BIT(ah, AR_TXCFG, AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY); } OS_REG_WRITE(ah, AR_NEXT_TBTT_TIMER, ONE_EIGHTH_TU_TO_USEC(next_beacon)); OS_REG_WRITE(ah, AR_NEXT_DMA_BEACON_ALERT, (ONE_EIGHTH_TU_TO_USEC(next_beacon) - ah->ah_config.ah_dma_beacon_response_time)); OS_REG_WRITE(ah, AR_NEXT_SWBA, (ONE_EIGHTH_TU_TO_USEC(next_beacon) - ah->ah_config.ah_sw_beacon_response_time)); beacon_period_usec = ONE_EIGHTH_TU_TO_USEC(beacon_period & HAL_BEACON_PERIOD_TU8); + + /* Add the fraction adjustment lost due to unit conversions. */ + beacon_period_usec += beacon_period_fraction; + + OS_REG_WRITE(ah, AR_BEACON_PERIOD, beacon_period_usec); OS_REG_WRITE(ah, AR_DMA_BEACON_PERIOD, beacon_period_usec); OS_REG_WRITE(ah, AR_SWBA_PERIOD, beacon_period_usec); /* reset TSF if required */ if (beacon_period & HAL_BEACON_RESET_TSF) { ar9300_reset_tsf(ah); } /* enable timers */ OS_REG_SET_BIT(ah, AR_TIMER_MODE, AR_TBTT_TIMER_EN | AR_DBA_TIMER_EN | AR_SWBA_TIMER_EN); } /* * Set all the beacon related bits on the h/w for stations * i.e. initializes the corresponding h/w timers; */ void ar9300_set_sta_beacon_timers(struct ath_hal *ah, const HAL_BEACON_STATE *bs) { u_int32_t next_tbtt, beaconintval, dtimperiod, beacontimeout; HAL_CAPABILITIES *p_cap = &AH_PRIVATE(ah)->ah_caps; HALASSERT(bs->bs_intval != 0); /* no cfp setting since h/w automatically takes care */ OS_REG_WRITE(ah, AR_NEXT_TBTT_TIMER, TU_TO_USEC(bs->bs_nexttbtt)); /* * Start the beacon timers by setting the BEACON register * to the beacon interval; no need to write tim offset since * h/w parses IEs. */ OS_REG_WRITE(ah, AR_BEACON_PERIOD, TU_TO_USEC(bs->bs_intval & HAL_BEACON_PERIOD)); OS_REG_WRITE(ah, AR_DMA_BEACON_PERIOD, TU_TO_USEC(bs->bs_intval & HAL_BEACON_PERIOD)); /* * Configure the BMISS interrupt. Note that we * assume the caller blocks interrupts while enabling * the threshold. */ HALASSERT(bs->bs_bmissthreshold <= (AR_RSSI_THR_BM_THR >> AR_RSSI_THR_BM_THR_S)); OS_REG_RMW_FIELD(ah, AR_RSSI_THR, AR_RSSI_THR_BM_THR, bs->bs_bmissthreshold); /* * Program the sleep registers to correlate with the beacon setup. */ /* * Current implementation assumes sw processing of beacons - * assuming an interrupt is generated every beacon which * causes the hardware to become awake until the sw tells * it to go to sleep again; beacon timeout is to allow for * beacon jitter; cab timeout is max time to wait for cab * after seeing the last DTIM or MORE CAB bit */ #define CAB_TIMEOUT_VAL 10 /* in TU */ #define BEACON_TIMEOUT_VAL 10 /* in TU */ #define MIN_BEACON_TIMEOUT_VAL 1 /* in 1/8 TU */ #define SLEEP_SLOP 3 /* in TU */ /* * For max powersave mode we may want to sleep for longer than a * beacon period and not want to receive all beacons; modify the * timers accordingly; make sure to align the next TIM to the * next DTIM if we decide to wake for DTIMs only */ beaconintval = bs->bs_intval & HAL_BEACON_PERIOD; HALASSERT(beaconintval != 0); if (bs->bs_sleepduration > beaconintval) { HALASSERT(roundup(bs->bs_sleepduration, beaconintval) == bs->bs_sleepduration); beaconintval = bs->bs_sleepduration; } dtimperiod = bs->bs_dtimperiod; if (bs->bs_sleepduration > dtimperiod) { HALASSERT(dtimperiod == 0 || roundup(bs->bs_sleepduration, dtimperiod) == bs->bs_sleepduration); dtimperiod = bs->bs_sleepduration; } HALASSERT(beaconintval <= dtimperiod); if (beaconintval == dtimperiod) { next_tbtt = bs->bs_nextdtim; } else { next_tbtt = bs->bs_nexttbtt; } HALDEBUG(ah, HAL_DEBUG_BEACON, "%s: next DTIM %d\n", __func__, bs->bs_nextdtim); HALDEBUG(ah, HAL_DEBUG_BEACON, "%s: next beacon %d\n", __func__, next_tbtt); HALDEBUG(ah, HAL_DEBUG_BEACON, "%s: beacon period %d\n", __func__, beaconintval); HALDEBUG(ah, HAL_DEBUG_BEACON, "%s: DTIM period %d\n", __func__, dtimperiod); OS_REG_WRITE(ah, AR_NEXT_DTIM, TU_TO_USEC(bs->bs_nextdtim - SLEEP_SLOP)); OS_REG_WRITE(ah, AR_NEXT_TIM, TU_TO_USEC(next_tbtt - SLEEP_SLOP)); /* cab timeout is now in 1/8 TU */ OS_REG_WRITE(ah, AR_SLEEP1, SM((CAB_TIMEOUT_VAL << 3), AR_SLEEP1_CAB_TIMEOUT) | AR_SLEEP1_ASSUME_DTIM); /* beacon timeout is now in 1/8 TU */ if (p_cap->halAutoSleepSupport) { beacontimeout = (BEACON_TIMEOUT_VAL << 3); } else { /* * Use a very small value to make sure the timeout occurs before * the TBTT. In this case the chip will not go back to sleep * automatically, instead it will wait for the SW to explicitly * set it to that mode. */ beacontimeout = MIN_BEACON_TIMEOUT_VAL; } OS_REG_WRITE(ah, AR_SLEEP2, SM(beacontimeout, AR_SLEEP2_BEACON_TIMEOUT)); OS_REG_WRITE(ah, AR_TIM_PERIOD, TU_TO_USEC(beaconintval)); OS_REG_WRITE(ah, AR_DTIM_PERIOD, TU_TO_USEC(dtimperiod)); /* clear HOST AP related timers first */ OS_REG_CLR_BIT(ah, AR_TIMER_MODE, (AR_DBA_TIMER_EN | AR_SWBA_TIMER_EN)); OS_REG_SET_BIT(ah, AR_TIMER_MODE, AR_TBTT_TIMER_EN | AR_TIM_TIMER_EN | AR_DTIM_TIMER_EN); /* TSF out of range threshold */ OS_REG_WRITE(ah, AR_TSFOOR_THRESHOLD, bs->bs_tsfoor_threshold); #undef CAB_TIMEOUT_VAL #undef BEACON_TIMEOUT_VAL #undef SLEEP_SLOP } Index: projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_eeprom.c =================================================================== --- projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_eeprom.c (revision 278776) +++ projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_eeprom.c (revision 278777) @@ -1,4611 +1,4720 @@ /* * Copyright (c) 2013 Qualcomm Atheros, Inc. * * 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 "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ah_devid.h" #ifdef AH_DEBUG #include "ah_desc.h" /* NB: for HAL_PHYERR* */ #endif #include "ar9300/ar9300.h" #include "ar9300/ar9300eep.h" #include "ar9300/ar9300template_generic.h" #include "ar9300/ar9300template_xb112.h" #include "ar9300/ar9300template_hb116.h" #include "ar9300/ar9300template_xb113.h" #include "ar9300/ar9300template_hb112.h" #include "ar9300/ar9300template_ap121.h" #include "ar9300/ar9300template_osprey_k31.h" #include "ar9300/ar9300template_wasp_2.h" #include "ar9300/ar9300template_wasp_k31.h" #include "ar9300/ar9300template_aphrodite.h" #include "ar9300/ar9300reg.h" #include "ar9300/ar9300phy.h" #if AH_BYTE_ORDER == AH_BIG_ENDIAN void ar9300_swap_eeprom(ar9300_eeprom_t *eep); void ar9300_eeprom_template_swap(void); #endif static u_int16_t ar9300_eeprom_get_spur_chan(struct ath_hal *ah, int spur_chan, HAL_BOOL is_2ghz); #ifdef UNUSED static inline HAL_BOOL ar9300_fill_eeprom(struct ath_hal *ah); static inline HAL_STATUS ar9300_check_eeprom(struct ath_hal *ah); #endif static ar9300_eeprom_t *default9300[] = { &ar9300_template_generic, &ar9300_template_xb112, &ar9300_template_hb116, &ar9300_template_hb112, &ar9300_template_xb113, &ar9300_template_ap121, &ar9300_template_wasp_2, &ar9300_template_wasp_k31, &ar9300_template_osprey_k31, &ar9300_template_aphrodite, }; /* * Different types of memory where the calibration data might be stored. * All types are searched in ar9300_eeprom_restore() * in the order flash, eeprom, otp. * To disable searching a type, set its parameter to 0. */ /* * This is where we look for the calibration data. * must be set before ath_attach() is called */ static int calibration_data_try = calibration_data_none; static int calibration_data_try_address = 0; /* * Set the type of memory used to store calibration data. * Used by nart to force reading/writing of a specific type. * The driver can normally allow autodetection * by setting source to calibration_data_none=0. */ void ar9300_calibration_data_set(struct ath_hal *ah, int32_t source) { if (ah != 0) { AH9300(ah)->calibration_data_source = source; } else { calibration_data_try = source; } } int32_t ar9300_calibration_data_get(struct ath_hal *ah) { if (ah != 0) { return AH9300(ah)->calibration_data_source; } else { return calibration_data_try; } } /* * Set the address of first byte used to store calibration data. * Used by nart to force reading/writing at a specific address. * The driver can normally allow autodetection by setting size=0. */ void ar9300_calibration_data_address_set(struct ath_hal *ah, int32_t size) { if (ah != 0) { AH9300(ah)->calibration_data_source_address = size; } else { calibration_data_try_address = size; } } int32_t ar9300_calibration_data_address_get(struct ath_hal *ah) { if (ah != 0) { return AH9300(ah)->calibration_data_source_address; } else { return calibration_data_try_address; } } /* * This is the template that is loaded if ar9300_eeprom_restore() * can't find valid data in the memory. */ static int Ar9300_eeprom_template_preference = ar9300_eeprom_template_generic; void ar9300_eeprom_template_preference(int32_t value) { Ar9300_eeprom_template_preference = value; } /* * Install the specified default template. * Overwrites any existing calibration and configuration information in memory. */ int32_t ar9300_eeprom_template_install(struct ath_hal *ah, int32_t value) { struct ath_hal_9300 *ahp = AH9300(ah); ar9300_eeprom_t *mptr, *dptr; int mdata_size; mptr = &ahp->ah_eeprom; mdata_size = ar9300_eeprom_struct_size(); if (mptr != 0) { #if 0 calibration_data_source = calibration_data_none; calibration_data_source_address = 0; #endif dptr = ar9300_eeprom_struct_default_find_by_id(value); if (dptr != 0) { OS_MEMCPY(mptr, dptr, mdata_size); return 0; } } return -1; } static int ar9300_eeprom_restore_something(struct ath_hal *ah, ar9300_eeprom_t *mptr, int mdata_size) { int it; ar9300_eeprom_t *dptr; int nptr; nptr = -1; /* * if we didn't find any blocks in the memory, * put the prefered template in place */ if (nptr < 0) { AH9300(ah)->calibration_data_source = calibration_data_none; AH9300(ah)->calibration_data_source_address = 0; dptr = ar9300_eeprom_struct_default_find_by_id( Ar9300_eeprom_template_preference); if (dptr != 0) { OS_MEMCPY(mptr, dptr, mdata_size); nptr = 0; } } /* * if we didn't find the prefered one, * put the normal default template in place */ if (nptr < 0) { AH9300(ah)->calibration_data_source = calibration_data_none; AH9300(ah)->calibration_data_source_address = 0; dptr = ar9300_eeprom_struct_default_find_by_id( ar9300_eeprom_template_default); if (dptr != 0) { OS_MEMCPY(mptr, dptr, mdata_size); nptr = 0; } } /* * if we can't find the best template, put any old template in place * presume that newer ones are better, so search backwards */ if (nptr < 0) { AH9300(ah)->calibration_data_source = calibration_data_none; AH9300(ah)->calibration_data_source_address = 0; for (it = ar9300_eeprom_struct_default_many() - 1; it >= 0; it--) { dptr = ar9300_eeprom_struct_default(it); if (dptr != 0) { OS_MEMCPY(mptr, dptr, mdata_size); nptr = 0; break; } } } return nptr; } /* * Read 16 bits of data from offset into *data */ HAL_BOOL ar9300_eeprom_read_word(struct ath_hal *ah, u_int off, u_int16_t *data) { if (AR_SREV_OSPREY(ah) || AR_SREV_POSEIDON(ah)) { (void) OS_REG_READ(ah, AR9300_EEPROM_OFFSET + (off << AR9300_EEPROM_S)); if (!ath_hal_wait(ah, AR_HOSTIF_REG(ah, AR_EEPROM_STATUS_DATA), AR_EEPROM_STATUS_DATA_BUSY | AR_EEPROM_STATUS_DATA_PROT_ACCESS, 0)) { return AH_FALSE; } *data = MS(OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_EEPROM_STATUS_DATA)), AR_EEPROM_STATUS_DATA_VAL); return AH_TRUE; } else { *data = 0; return AH_FALSE; } } HAL_BOOL ar9300_otp_read(struct ath_hal *ah, u_int off, u_int32_t *data, HAL_BOOL is_wifi) { int time_out = 1000; int status = 0; u_int32_t addr; addr = (AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah))? OTP_MEM_START_ADDRESS_WASP : OTP_MEM_START_ADDRESS; if (!is_wifi) { addr = BTOTP_MEM_START_ADDRESS; } addr += off * 4; /* OTP is 32 bit addressable */ (void) OS_REG_READ(ah, addr); addr = (AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah)) ? OTP_STATUS0_OTP_SM_BUSY_WASP : OTP_STATUS0_OTP_SM_BUSY; if (!is_wifi) { addr = BTOTP_STATUS0_OTP_SM_BUSY; } while ((time_out > 0) && (!status)) { /* wait for access complete */ /* Read data valid, access not busy, sm not busy */ status = ((OS_REG_READ(ah, addr) & 0x7) == 0x4) ? 1 : 0; time_out--; } if (time_out == 0) { HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: Timed out during OTP Status0 validation\n", __func__); return AH_FALSE; } addr = (AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah)) ? OTP_STATUS1_EFUSE_READ_DATA_WASP : OTP_STATUS1_EFUSE_READ_DATA; if (!is_wifi) { addr = BTOTP_STATUS1_EFUSE_READ_DATA; } *data = OS_REG_READ(ah, addr); return AH_TRUE; } static HAL_STATUS ar9300_flash_map(struct ath_hal *ah) { /* XXX disable flash remapping for now (ie, SoC support) */ ath_hal_printf(ah, "%s: unimplemented for now\n", __func__); #if 0 struct ath_hal_9300 *ahp = AH9300(ah); #if defined(AR9100) || defined(__NetBSD__) ahp->ah_cal_mem = OS_REMAP(ah, AR9300_EEPROM_START_ADDR, AR9300_EEPROM_MAX); #else ahp->ah_cal_mem = OS_REMAP((uintptr_t)(AH_PRIVATE(ah)->ah_st), (AR9300_EEPROM_MAX + AR9300_FLASH_CAL_START_OFFSET)); #endif if (!ahp->ah_cal_mem) { HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: cannot remap eeprom region \n", __func__); return HAL_EIO; } #endif return HAL_OK; } HAL_BOOL ar9300_flash_read(struct ath_hal *ah, u_int off, u_int16_t *data) { struct ath_hal_9300 *ahp = AH9300(ah); *data = ((u_int16_t *)ahp->ah_cal_mem)[off]; return AH_TRUE; } HAL_BOOL ar9300_flash_write(struct ath_hal *ah, u_int off, u_int16_t data) { struct ath_hal_9300 *ahp = AH9300(ah); ((u_int16_t *)ahp->ah_cal_mem)[off] = data; return AH_TRUE; } HAL_STATUS ar9300_eeprom_attach(struct ath_hal *ah) { struct ath_hal_9300 *ahp = AH9300(ah); ahp->try_dram = 1; ahp->try_eeprom = 1; ahp->try_otp = 1; #ifdef ATH_CAL_NAND_FLASH ahp->try_nand = 1; #else ahp->try_flash = 1; #endif ahp->calibration_data_source = calibration_data_none; ahp->calibration_data_source_address = 0; ahp->calibration_data_try = calibration_data_try; ahp->calibration_data_try_address = 0; /* * In case flash will be used for EEPROM. Otherwise ahp->ah_cal_mem * must be set to NULL or the real EEPROM address. */ ar9300_flash_map(ah); /* * ###### This function always return NO SPUR. * This is not AH_TRUE for many board designs. * Does anyone use this? */ AH_PRIVATE(ah)->ah_getSpurChan = ar9300_eeprom_get_spur_chan; #ifdef OLDCODE /* XXX Needs to be moved for dynamic selection */ ahp->ah_eeprom = *(default9300[ar9300_eeprom_template_default]); if (AR_SREV_HORNET(ah)) { /* Set default values for Hornet. */ ahp->ah_eeprom.base_eep_header.op_cap_flags.op_flags = AR9300_OPFLAGS_11G; ahp->ah_eeprom.base_eep_header.txrx_mask = 0x11; } else if (AR_SREV_POSEIDON(ah)) { /* Set default values for Poseidon. */ ahp->ah_eeprom.base_eep_header.op_cap_flags.op_flags = AR9300_OPFLAGS_11G; ahp->ah_eeprom.base_eep_header.txrx_mask = 0x11; } if (AH_PRIVATE(ah)->ah_config.ath_hal_skip_eeprom_read) { ahp->ah_emu_eeprom = 1; return HAL_OK; } ahp->ah_emu_eeprom = 1; #ifdef UNUSED #endif if (!ar9300_fill_eeprom(ah)) { return HAL_EIO; } return HAL_OK; /* return ar9300_check_eeprom(ah); */ #else ahp->ah_emu_eeprom = 1; #if 0 /*#ifdef MDK_AP*/ /* MDK_AP is defined only in NART AP build */ u_int8_t buffer[10]; int caldata_check = 0; ar9300_calibration_data_read_flash( ah, FLASH_BASE_CALDATA_OFFSET, buffer, 4); printf("flash caldata:: %x\n", buffer[0]); if (buffer[0] != 0xff) { caldata_check = 1; } if (!caldata_check) { ar9300_eeprom_t *mptr; int mdata_size; if (AR_SREV_HORNET(ah)) { /* XXX: For initial testing */ mptr = &ahp->ah_eeprom; mdata_size = ar9300_eeprom_struct_size(); ahp->ah_eeprom = ar9300_template_ap121; ahp->ah_emu_eeprom = 1; /* need it to let art save in to flash ????? */ calibration_data_source = calibration_data_flash; } else if (AR_SREV_WASP(ah)) { /* XXX: For initial testing */ ath_hal_printf(ah, " wasp eep attach\n"); mptr = &ahp->ah_eeprom; mdata_size = ar9300_eeprom_struct_size(); ahp->ah_eeprom = ar9300_template_generic; ahp->ah_eeprom.mac_addr[0] = 0x00; ahp->ah_eeprom.mac_addr[1] = 0x03; ahp->ah_eeprom.mac_addr[2] = 0x7F; ahp->ah_eeprom.mac_addr[3] = 0xBA; ahp->ah_eeprom.mac_addr[4] = 0xD0; ahp->ah_eeprom.mac_addr[5] = 0x00; ahp->ah_emu_eeprom = 1; ahp->ah_eeprom.base_eep_header.txrx_mask = 0x33; ahp->ah_eeprom.base_eep_header.txrxgain = 0x10; /* need it to let art save in to flash ????? */ calibration_data_source = calibration_data_flash; } return HAL_OK; } #endif if (AR_SREV_HORNET(ah) || AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah)) { ahp->try_eeprom = 0; } if (!ar9300_eeprom_restore(ah)) { return HAL_EIO; } return HAL_OK; #endif } u_int32_t ar9300_eeprom_get(struct ath_hal_9300 *ahp, EEPROM_PARAM param) { ar9300_eeprom_t *eep = &ahp->ah_eeprom; OSPREY_BASE_EEP_HEADER *p_base = &eep->base_eep_header; OSPREY_BASE_EXTENSION_1 *base_ext1 = &eep->base_ext1; switch (param) { #ifdef NOTYET case EEP_NFTHRESH_5: return p_modal[0].noise_floor_thresh_ch[0]; case EEP_NFTHRESH_2: return p_modal[1].noise_floor_thresh_ch[0]; #endif case EEP_MAC_LSW: return eep->mac_addr[0] << 8 | eep->mac_addr[1]; case EEP_MAC_MID: return eep->mac_addr[2] << 8 | eep->mac_addr[3]; case EEP_MAC_MSW: return eep->mac_addr[4] << 8 | eep->mac_addr[5]; case EEP_REG_0: return p_base->reg_dmn[0]; case EEP_REG_1: return p_base->reg_dmn[1]; case EEP_OP_CAP: return p_base->device_cap; case EEP_OP_MODE: return p_base->op_cap_flags.op_flags; case EEP_RF_SILENT: return p_base->rf_silent; #ifdef NOTYET case EEP_OB_5: return p_modal[0].ob; case EEP_DB_5: return p_modal[0].db; case EEP_OB_2: return p_modal[1].ob; case EEP_DB_2: return p_modal[1].db; case EEP_MINOR_REV: return p_base->eeprom_version & AR9300_EEP_VER_MINOR_MASK; #endif case EEP_TX_MASK: return (p_base->txrx_mask >> 4) & 0xf; case EEP_RX_MASK: return p_base->txrx_mask & 0xf; #ifdef NOTYET case EEP_FSTCLK_5G: return p_base->fast_clk5g; case EEP_RXGAIN_TYPE: return p_base->rx_gain_type; #endif case EEP_DRIVE_STRENGTH: #define AR9300_EEP_BASE_DRIVE_STRENGTH 0x1 return p_base->misc_configuration & AR9300_EEP_BASE_DRIVE_STRENGTH; case EEP_INTERNAL_REGULATOR: /* Bit 4 is internal regulator flag */ return ((p_base->feature_enable & 0x10) >> 4); case EEP_SWREG: return (p_base->swreg); case EEP_PAPRD_ENABLED: /* Bit 5 is paprd flag */ return ((p_base->feature_enable & 0x20) >> 5); case EEP_ANTDIV_control: return (u_int32_t)(base_ext1->ant_div_control); case EEP_CHAIN_MASK_REDUCE: return ((p_base->misc_configuration >> 3) & 0x1); case EEP_OL_PWRCTRL: return 0; case EEP_DEV_TYPE: return p_base->device_type; default: HALASSERT(0); return 0; } } /******************************************************************************/ /*! ** \brief EEPROM fixup code for INI values ** ** This routine provides a place to insert "fixup" code for specific devices ** that need to modify INI values based on EEPROM values, BEFORE the INI values ** are written. ** Certain registers in the INI file can only be written once without ** undesired side effects, and this provides a place for EEPROM overrides ** in these cases. ** ** This is called at attach time once. It should not affect run time ** performance at all ** ** \param ah Pointer to HAL object (this) ** \param p_eep_data Pointer to (filled in) eeprom data structure ** \param reg register being inspected on this call ** \param value value in INI file ** ** \return Updated value for INI file. */ u_int32_t ar9300_ini_fixup(struct ath_hal *ah, ar9300_eeprom_t *p_eep_data, u_int32_t reg, u_int32_t value) { HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE, "ar9300_eeprom_def_ini_fixup: FIXME\n"); #if 0 BASE_EEPDEF_HEADER *p_base = &(p_eep_data->base_eep_header); switch (AH_PRIVATE(ah)->ah_devid) { case AR9300_DEVID_AR9300_PCI: /* ** Need to set the external/internal regulator bit to the proper value. ** Can only write this ONCE. */ if ( reg == 0x7894 ) { /* ** Check for an EEPROM data structure of "0x0b" or better */ HALDEBUG(ah, HAL_DEBUG_EEPROM, "ini VAL: %x EEPROM: %x\n", value, (p_base->version & 0xff)); if ( (p_base->version & 0xff) > 0x0a) { HALDEBUG(ah, HAL_DEBUG_EEPROM, "PWDCLKIND: %d\n", p_base->pwdclkind); value &= ~AR_AN_TOP2_PWDCLKIND; value |= AR_AN_TOP2_PWDCLKIND & (p_base->pwdclkind << AR_AN_TOP2_PWDCLKIND_S); } else { HALDEBUG(ah, HAL_DEBUG_EEPROM, "PWDCLKIND Earlier Rev\n"); } HALDEBUG(ah, HAL_DEBUG_EEPROM, "final ini VAL: %x\n", value); } break; } return (value); #else return 0; #endif } /* * Returns the interpolated y value corresponding to the specified x value * from the np ordered pairs of data (px,py). * The pairs do not have to be in any order. * If the specified x value is less than any of the px, * the returned y value is equal to the py for the lowest px. * If the specified x value is greater than any of the px, * the returned y value is equal to the py for the highest px. */ static int interpolate(int32_t x, int32_t *px, int32_t *py, u_int16_t np) { int ip = 0; int lx = 0, ly = 0, lhave = 0; int hx = 0, hy = 0, hhave = 0; int dx = 0; int y = 0; int bf, factor, plus; lhave = 0; hhave = 0; /* * identify best lower and higher x calibration measurement */ for (ip = 0; ip < np; ip++) { dx = x - px[ip]; /* this measurement is higher than our desired x */ if (dx <= 0) { if (!hhave || dx > (x - hx)) { /* new best higher x measurement */ hx = px[ip]; hy = py[ip]; hhave = 1; } } /* this measurement is lower than our desired x */ if (dx >= 0) { if (!lhave || dx < (x - lx)) { /* new best lower x measurement */ lx = px[ip]; ly = py[ip]; lhave = 1; } } } /* the low x is good */ if (lhave) { /* so is the high x */ if (hhave) { /* they're the same, so just pick one */ if (hx == lx) { y = ly; } else { /* interpolate with round off */ bf = (2 * (hy - ly) * (x - lx)) / (hx - lx); plus = (bf % 2); factor = bf / 2; y = ly + factor + plus; } } else { /* only low is good, use it */ y = ly; } } else if (hhave) { /* only high is good, use it */ y = hy; } else { /* nothing is good,this should never happen unless np=0, ???? */ y = -(1 << 30); } return y; } u_int8_t ar9300_eeprom_get_legacy_trgt_pwr(struct ath_hal *ah, u_int16_t rate_index, u_int16_t freq, HAL_BOOL is_2ghz) { u_int16_t num_piers, i; int32_t target_power_array[OSPREY_NUM_5G_20_TARGET_POWERS]; int32_t freq_array[OSPREY_NUM_5G_20_TARGET_POWERS]; u_int8_t *p_freq_bin; ar9300_eeprom_t *eep = &AH9300(ah)->ah_eeprom; CAL_TARGET_POWER_LEG *p_eeprom_target_pwr; if (is_2ghz) { num_piers = OSPREY_NUM_2G_20_TARGET_POWERS; p_eeprom_target_pwr = eep->cal_target_power_2g; p_freq_bin = eep->cal_target_freqbin_2g; } else { num_piers = OSPREY_NUM_5G_20_TARGET_POWERS; p_eeprom_target_pwr = eep->cal_target_power_5g; p_freq_bin = eep->cal_target_freqbin_5g; } /* * create array of channels and targetpower from * targetpower piers stored on eeprom */ for (i = 0; i < num_piers; i++) { freq_array[i] = FBIN2FREQ(p_freq_bin[i], is_2ghz); target_power_array[i] = p_eeprom_target_pwr[i].t_pow2x[rate_index]; } /* interpolate to get target power for given frequency */ return ((u_int8_t)interpolate( (int32_t)freq, freq_array, target_power_array, num_piers)); } u_int8_t ar9300_eeprom_get_ht20_trgt_pwr(struct ath_hal *ah, u_int16_t rate_index, u_int16_t freq, HAL_BOOL is_2ghz) { u_int16_t num_piers, i; int32_t target_power_array[OSPREY_NUM_5G_20_TARGET_POWERS]; int32_t freq_array[OSPREY_NUM_5G_20_TARGET_POWERS]; u_int8_t *p_freq_bin; ar9300_eeprom_t *eep = &AH9300(ah)->ah_eeprom; OSP_CAL_TARGET_POWER_HT *p_eeprom_target_pwr; if (is_2ghz) { num_piers = OSPREY_NUM_2G_20_TARGET_POWERS; p_eeprom_target_pwr = eep->cal_target_power_2g_ht20; p_freq_bin = eep->cal_target_freqbin_2g_ht20; } else { num_piers = OSPREY_NUM_5G_20_TARGET_POWERS; p_eeprom_target_pwr = eep->cal_target_power_5g_ht20; p_freq_bin = eep->cal_target_freqbin_5g_ht20; } /* * create array of channels and targetpower from * targetpower piers stored on eeprom */ for (i = 0; i < num_piers; i++) { freq_array[i] = FBIN2FREQ(p_freq_bin[i], is_2ghz); target_power_array[i] = p_eeprom_target_pwr[i].t_pow2x[rate_index]; } /* interpolate to get target power for given frequency */ return ((u_int8_t)interpolate( (int32_t)freq, freq_array, target_power_array, num_piers)); } u_int8_t ar9300_eeprom_get_ht40_trgt_pwr(struct ath_hal *ah, u_int16_t rate_index, u_int16_t freq, HAL_BOOL is_2ghz) { u_int16_t num_piers, i; int32_t target_power_array[OSPREY_NUM_5G_40_TARGET_POWERS]; int32_t freq_array[OSPREY_NUM_5G_40_TARGET_POWERS]; u_int8_t *p_freq_bin; ar9300_eeprom_t *eep = &AH9300(ah)->ah_eeprom; OSP_CAL_TARGET_POWER_HT *p_eeprom_target_pwr; if (is_2ghz) { num_piers = OSPREY_NUM_2G_40_TARGET_POWERS; p_eeprom_target_pwr = eep->cal_target_power_2g_ht40; p_freq_bin = eep->cal_target_freqbin_2g_ht40; } else { num_piers = OSPREY_NUM_5G_40_TARGET_POWERS; p_eeprom_target_pwr = eep->cal_target_power_5g_ht40; p_freq_bin = eep->cal_target_freqbin_5g_ht40; } /* * create array of channels and targetpower from * targetpower piers stored on eeprom */ for (i = 0; i < num_piers; i++) { freq_array[i] = FBIN2FREQ(p_freq_bin[i], is_2ghz); target_power_array[i] = p_eeprom_target_pwr[i].t_pow2x[rate_index]; } /* interpolate to get target power for given frequency */ return ((u_int8_t)interpolate( (int32_t)freq, freq_array, target_power_array, num_piers)); } u_int8_t ar9300_eeprom_get_cck_trgt_pwr(struct ath_hal *ah, u_int16_t rate_index, u_int16_t freq) { u_int16_t num_piers = OSPREY_NUM_2G_CCK_TARGET_POWERS, i; int32_t target_power_array[OSPREY_NUM_2G_CCK_TARGET_POWERS]; int32_t freq_array[OSPREY_NUM_2G_CCK_TARGET_POWERS]; ar9300_eeprom_t *eep = &AH9300(ah)->ah_eeprom; u_int8_t *p_freq_bin = eep->cal_target_freqbin_cck; CAL_TARGET_POWER_LEG *p_eeprom_target_pwr = eep->cal_target_power_cck; /* * create array of channels and targetpower from * targetpower piers stored on eeprom */ for (i = 0; i < num_piers; i++) { freq_array[i] = FBIN2FREQ(p_freq_bin[i], 1); target_power_array[i] = p_eeprom_target_pwr[i].t_pow2x[rate_index]; } /* interpolate to get target power for given frequency */ return ((u_int8_t)interpolate( (int32_t)freq, freq_array, target_power_array, num_piers)); } /* * Set tx power registers to array of values passed in */ int ar9300_transmit_power_reg_write(struct ath_hal *ah, u_int8_t *p_pwr_array) { #define POW_SM(_r, _s) (((_r) & 0x3f) << (_s)) /* make sure forced gain is not set */ #if 0 field_write("force_dac_gain", 0); OS_REG_WRITE(ah, 0xa3f8, 0); field_write("force_tx_gain", 0); #endif OS_REG_WRITE(ah, 0xa458, 0); /* Write the OFDM power per rate set */ /* 6 (LSB), 9, 12, 18 (MSB) */ OS_REG_WRITE(ah, 0xa3c0, POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 24) | POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 16) | POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 8) | POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 0) ); /* 24 (LSB), 36, 48, 54 (MSB) */ OS_REG_WRITE(ah, 0xa3c4, POW_SM(p_pwr_array[ALL_TARGET_LEGACY_54], 24) | POW_SM(p_pwr_array[ALL_TARGET_LEGACY_48], 16) | POW_SM(p_pwr_array[ALL_TARGET_LEGACY_36], 8) | POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 0) ); /* Write the CCK power per rate set */ /* 1L (LSB), reserved, 2L, 2S (MSB) */ OS_REG_WRITE(ah, 0xa3c8, POW_SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], 24) | POW_SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], 16) /* | POW_SM(tx_power_times2, 8)*/ /* this is reserved for Osprey */ | POW_SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], 0) ); /* 5.5L (LSB), 5.5S, 11L, 11S (MSB) */ OS_REG_WRITE(ah, 0xa3cc, POW_SM(p_pwr_array[ALL_TARGET_LEGACY_11S], 24) | POW_SM(p_pwr_array[ALL_TARGET_LEGACY_11L], 16) | POW_SM(p_pwr_array[ALL_TARGET_LEGACY_5S], 8) | POW_SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], 0) ); /* write the power for duplicated frames - HT40 */ /* dup40_cck (LSB), dup40_ofdm, ext20_cck, ext20_ofdm (MSB) */ OS_REG_WRITE(ah, 0xa3e0, POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 24) | POW_SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], 16) | POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 8) | POW_SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], 0) ); /* Write the HT20 power per rate set */ /* 0/8/16 (LSB), 1-3/9-11/17-19, 4, 5 (MSB) */ OS_REG_WRITE(ah, 0xa3d0, POW_SM(p_pwr_array[ALL_TARGET_HT20_5], 24) | POW_SM(p_pwr_array[ALL_TARGET_HT20_4], 16) | POW_SM(p_pwr_array[ALL_TARGET_HT20_1_3_9_11_17_19], 8) | POW_SM(p_pwr_array[ALL_TARGET_HT20_0_8_16], 0) ); /* 6 (LSB), 7, 12, 13 (MSB) */ OS_REG_WRITE(ah, 0xa3d4, POW_SM(p_pwr_array[ALL_TARGET_HT20_13], 24) | POW_SM(p_pwr_array[ALL_TARGET_HT20_12], 16) | POW_SM(p_pwr_array[ALL_TARGET_HT20_7], 8) | POW_SM(p_pwr_array[ALL_TARGET_HT20_6], 0) ); /* 14 (LSB), 15, 20, 21 */ OS_REG_WRITE(ah, 0xa3e4, POW_SM(p_pwr_array[ALL_TARGET_HT20_21], 24) | POW_SM(p_pwr_array[ALL_TARGET_HT20_20], 16) | POW_SM(p_pwr_array[ALL_TARGET_HT20_15], 8) | POW_SM(p_pwr_array[ALL_TARGET_HT20_14], 0) ); /* Mixed HT20 and HT40 rates */ /* HT20 22 (LSB), HT20 23, HT40 22, HT40 23 (MSB) */ OS_REG_WRITE(ah, 0xa3e8, POW_SM(p_pwr_array[ALL_TARGET_HT40_23], 24) | POW_SM(p_pwr_array[ALL_TARGET_HT40_22], 16) | POW_SM(p_pwr_array[ALL_TARGET_HT20_23], 8) | POW_SM(p_pwr_array[ALL_TARGET_HT20_22], 0) ); /* Write the HT40 power per rate set */ /* correct PAR difference between HT40 and HT20/LEGACY */ /* 0/8/16 (LSB), 1-3/9-11/17-19, 4, 5 (MSB) */ OS_REG_WRITE(ah, 0xa3d8, POW_SM(p_pwr_array[ALL_TARGET_HT40_5], 24) | POW_SM(p_pwr_array[ALL_TARGET_HT40_4], 16) | POW_SM(p_pwr_array[ALL_TARGET_HT40_1_3_9_11_17_19], 8) | POW_SM(p_pwr_array[ALL_TARGET_HT40_0_8_16], 0) ); /* 6 (LSB), 7, 12, 13 (MSB) */ OS_REG_WRITE(ah, 0xa3dc, POW_SM(p_pwr_array[ALL_TARGET_HT40_13], 24) | POW_SM(p_pwr_array[ALL_TARGET_HT40_12], 16) | POW_SM(p_pwr_array[ALL_TARGET_HT40_7], 8) | POW_SM(p_pwr_array[ALL_TARGET_HT40_6], 0) ); /* 14 (LSB), 15, 20, 21 */ OS_REG_WRITE(ah, 0xa3ec, POW_SM(p_pwr_array[ALL_TARGET_HT40_21], 24) | POW_SM(p_pwr_array[ALL_TARGET_HT40_20], 16) | POW_SM(p_pwr_array[ALL_TARGET_HT40_15], 8) | POW_SM(p_pwr_array[ALL_TARGET_HT40_14], 0) ); return 0; #undef POW_SM } static void ar9300_selfgen_tpc_reg_write(struct ath_hal *ah, const struct ieee80211_channel *chan, u_int8_t *p_pwr_array) { u_int32_t tpc_reg_val; /* Set the target power values for self generated frames (ACK,RTS/CTS) to * be within limits. This is just a safety measure.With per packet TPC mode * enabled the target power value used with self generated frames will be * MIN( TPC reg, BB_powertx_rate register) */ if (IEEE80211_IS_CHAN_2GHZ(chan)) { tpc_reg_val = (SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], AR_TPC_ACK) | SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], AR_TPC_CTS) | SM(0x3f, AR_TPC_CHIRP) | SM(0x3f, AR_TPC_RPT)); } else { tpc_reg_val = (SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], AR_TPC_ACK) | SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], AR_TPC_CTS) | SM(0x3f, AR_TPC_CHIRP) | SM(0x3f, AR_TPC_RPT)); } OS_REG_WRITE(ah, AR_TPC, tpc_reg_val); } void ar9300_set_target_power_from_eeprom(struct ath_hal *ah, u_int16_t freq, u_int8_t *target_power_val_t2) { /* hard code for now, need to get from eeprom struct */ u_int8_t ht40_power_inc_for_pdadc = 0; HAL_BOOL is_2ghz = 0; if (freq < 4000) { is_2ghz = 1; } target_power_val_t2[ALL_TARGET_LEGACY_6_24] = ar9300_eeprom_get_legacy_trgt_pwr( ah, LEGACY_TARGET_RATE_6_24, freq, is_2ghz); target_power_val_t2[ALL_TARGET_LEGACY_36] = ar9300_eeprom_get_legacy_trgt_pwr( ah, LEGACY_TARGET_RATE_36, freq, is_2ghz); target_power_val_t2[ALL_TARGET_LEGACY_48] = ar9300_eeprom_get_legacy_trgt_pwr( ah, LEGACY_TARGET_RATE_48, freq, is_2ghz); target_power_val_t2[ALL_TARGET_LEGACY_54] = ar9300_eeprom_get_legacy_trgt_pwr( ah, LEGACY_TARGET_RATE_54, freq, is_2ghz); target_power_val_t2[ALL_TARGET_LEGACY_1L_5L] = ar9300_eeprom_get_cck_trgt_pwr( ah, LEGACY_TARGET_RATE_1L_5L, freq); target_power_val_t2[ALL_TARGET_LEGACY_5S] = ar9300_eeprom_get_cck_trgt_pwr( ah, LEGACY_TARGET_RATE_5S, freq); target_power_val_t2[ALL_TARGET_LEGACY_11L] = ar9300_eeprom_get_cck_trgt_pwr( ah, LEGACY_TARGET_RATE_11L, freq); target_power_val_t2[ALL_TARGET_LEGACY_11S] = ar9300_eeprom_get_cck_trgt_pwr( ah, LEGACY_TARGET_RATE_11S, freq); target_power_val_t2[ALL_TARGET_HT20_0_8_16] = ar9300_eeprom_get_ht20_trgt_pwr( ah, HT_TARGET_RATE_0_8_16, freq, is_2ghz); target_power_val_t2[ALL_TARGET_HT20_1_3_9_11_17_19] = ar9300_eeprom_get_ht20_trgt_pwr( ah, HT_TARGET_RATE_1_3_9_11_17_19, freq, is_2ghz); target_power_val_t2[ALL_TARGET_HT20_4] = ar9300_eeprom_get_ht20_trgt_pwr( ah, HT_TARGET_RATE_4, freq, is_2ghz); target_power_val_t2[ALL_TARGET_HT20_5] = ar9300_eeprom_get_ht20_trgt_pwr( ah, HT_TARGET_RATE_5, freq, is_2ghz); target_power_val_t2[ALL_TARGET_HT20_6] = ar9300_eeprom_get_ht20_trgt_pwr( ah, HT_TARGET_RATE_6, freq, is_2ghz); target_power_val_t2[ALL_TARGET_HT20_7] = ar9300_eeprom_get_ht20_trgt_pwr( ah, HT_TARGET_RATE_7, freq, is_2ghz); target_power_val_t2[ALL_TARGET_HT20_12] = ar9300_eeprom_get_ht20_trgt_pwr( ah, HT_TARGET_RATE_12, freq, is_2ghz); target_power_val_t2[ALL_TARGET_HT20_13] = ar9300_eeprom_get_ht20_trgt_pwr( ah, HT_TARGET_RATE_13, freq, is_2ghz); target_power_val_t2[ALL_TARGET_HT20_14] = ar9300_eeprom_get_ht20_trgt_pwr( ah, HT_TARGET_RATE_14, freq, is_2ghz); target_power_val_t2[ALL_TARGET_HT20_15] = ar9300_eeprom_get_ht20_trgt_pwr( ah, HT_TARGET_RATE_15, freq, is_2ghz); target_power_val_t2[ALL_TARGET_HT20_20] = ar9300_eeprom_get_ht20_trgt_pwr( ah, HT_TARGET_RATE_20, freq, is_2ghz); target_power_val_t2[ALL_TARGET_HT20_21] = ar9300_eeprom_get_ht20_trgt_pwr( ah, HT_TARGET_RATE_21, freq, is_2ghz); target_power_val_t2[ALL_TARGET_HT20_22] = ar9300_eeprom_get_ht20_trgt_pwr( ah, HT_TARGET_RATE_22, freq, is_2ghz); target_power_val_t2[ALL_TARGET_HT20_23] = ar9300_eeprom_get_ht20_trgt_pwr( ah, HT_TARGET_RATE_23, freq, is_2ghz); target_power_val_t2[ALL_TARGET_HT40_0_8_16] = ar9300_eeprom_get_ht40_trgt_pwr( ah, HT_TARGET_RATE_0_8_16, freq, is_2ghz) + ht40_power_inc_for_pdadc; target_power_val_t2[ALL_TARGET_HT40_1_3_9_11_17_19] = ar9300_eeprom_get_ht40_trgt_pwr( ah, HT_TARGET_RATE_1_3_9_11_17_19, freq, is_2ghz) + ht40_power_inc_for_pdadc; target_power_val_t2[ALL_TARGET_HT40_4] = ar9300_eeprom_get_ht40_trgt_pwr( ah, HT_TARGET_RATE_4, freq, is_2ghz) + ht40_power_inc_for_pdadc; target_power_val_t2[ALL_TARGET_HT40_5] = ar9300_eeprom_get_ht40_trgt_pwr( ah, HT_TARGET_RATE_5, freq, is_2ghz) + ht40_power_inc_for_pdadc; target_power_val_t2[ALL_TARGET_HT40_6] = ar9300_eeprom_get_ht40_trgt_pwr( ah, HT_TARGET_RATE_6, freq, is_2ghz) + ht40_power_inc_for_pdadc; target_power_val_t2[ALL_TARGET_HT40_7] = ar9300_eeprom_get_ht40_trgt_pwr( ah, HT_TARGET_RATE_7, freq, is_2ghz) + ht40_power_inc_for_pdadc; target_power_val_t2[ALL_TARGET_HT40_12] = ar9300_eeprom_get_ht40_trgt_pwr( ah, HT_TARGET_RATE_12, freq, is_2ghz) + ht40_power_inc_for_pdadc; target_power_val_t2[ALL_TARGET_HT40_13] = ar9300_eeprom_get_ht40_trgt_pwr( ah, HT_TARGET_RATE_13, freq, is_2ghz) + ht40_power_inc_for_pdadc; target_power_val_t2[ALL_TARGET_HT40_14] = ar9300_eeprom_get_ht40_trgt_pwr( ah, HT_TARGET_RATE_14, freq, is_2ghz) + ht40_power_inc_for_pdadc; target_power_val_t2[ALL_TARGET_HT40_15] = ar9300_eeprom_get_ht40_trgt_pwr( ah, HT_TARGET_RATE_15, freq, is_2ghz) + ht40_power_inc_for_pdadc; target_power_val_t2[ALL_TARGET_HT40_20] = ar9300_eeprom_get_ht40_trgt_pwr( ah, HT_TARGET_RATE_20, freq, is_2ghz) + ht40_power_inc_for_pdadc; target_power_val_t2[ALL_TARGET_HT40_21] = ar9300_eeprom_get_ht40_trgt_pwr( ah, HT_TARGET_RATE_21, freq, is_2ghz) + ht40_power_inc_for_pdadc; target_power_val_t2[ALL_TARGET_HT40_22] = ar9300_eeprom_get_ht40_trgt_pwr( ah, HT_TARGET_RATE_22, freq, is_2ghz) + ht40_power_inc_for_pdadc; target_power_val_t2[ALL_TARGET_HT40_23] = ar9300_eeprom_get_ht40_trgt_pwr( ah, HT_TARGET_RATE_23, freq, is_2ghz) + ht40_power_inc_for_pdadc; #ifdef AH_DEBUG { int i = 0; HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: APPLYING TARGET POWERS\n", __func__); while (i < ar9300_rate_size) { HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: TPC[%02d] 0x%08x ", __func__, i, target_power_val_t2[i]); i++; if (i == ar9300_rate_size) { break; } HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: TPC[%02d] 0x%08x ", __func__, i, target_power_val_t2[i]); i++; if (i == ar9300_rate_size) { break; } HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: TPC[%02d] 0x%08x ", __func__, i, target_power_val_t2[i]); i++; if (i == ar9300_rate_size) { break; } HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: TPC[%02d] 0x%08x \n", __func__, i, target_power_val_t2[i]); i++; } } #endif } u_int16_t *ar9300_regulatory_domain_get(struct ath_hal *ah) { ar9300_eeprom_t *eep = &AH9300(ah)->ah_eeprom; return eep->base_eep_header.reg_dmn; } int32_t ar9300_eeprom_write_enable_gpio_get(struct ath_hal *ah) { ar9300_eeprom_t *eep = &AH9300(ah)->ah_eeprom; return eep->base_eep_header.eeprom_write_enable_gpio; } int32_t ar9300_wlan_disable_gpio_get(struct ath_hal *ah) { ar9300_eeprom_t *eep = &AH9300(ah)->ah_eeprom; return eep->base_eep_header.wlan_disable_gpio; } int32_t ar9300_wlan_led_gpio_get(struct ath_hal *ah) { ar9300_eeprom_t *eep = &AH9300(ah)->ah_eeprom; return eep->base_eep_header.wlan_led_gpio; } int32_t ar9300_rx_band_select_gpio_get(struct ath_hal *ah) { ar9300_eeprom_t *eep = &AH9300(ah)->ah_eeprom; return eep->base_eep_header.rx_band_select_gpio; } /* * since valid noise floor values are negative, returns 1 on error */ int32_t ar9300_noise_floor_cal_or_power_get(struct ath_hal *ah, int32_t frequency, int32_t ichain, HAL_BOOL use_cal) { int nf_use = 1; /* start with an error return value */ int32_t fx[OSPREY_NUM_5G_CAL_PIERS + OSPREY_NUM_2G_CAL_PIERS]; int32_t nf[OSPREY_NUM_5G_CAL_PIERS + OSPREY_NUM_2G_CAL_PIERS]; int nnf; int is_2ghz; int ipier, npier; ar9300_eeprom_t *eep = &AH9300(ah)->ah_eeprom; u_int8_t *p_cal_pier; OSP_CAL_DATA_PER_FREQ_OP_LOOP *p_cal_pier_struct; /* * check chain value */ if (ichain < 0 || ichain >= OSPREY_MAX_CHAINS) { return 1; } /* figure out which band we're using */ is_2ghz = (frequency < 4000); if (is_2ghz) { npier = OSPREY_NUM_2G_CAL_PIERS; p_cal_pier = eep->cal_freq_pier_2g; p_cal_pier_struct = eep->cal_pier_data_2g[ichain]; } else { npier = OSPREY_NUM_5G_CAL_PIERS; p_cal_pier = eep->cal_freq_pier_5g; p_cal_pier_struct = eep->cal_pier_data_5g[ichain]; } /* look for valid noise floor values */ nnf = 0; for (ipier = 0; ipier < npier; ipier++) { fx[nnf] = FBIN2FREQ(p_cal_pier[ipier], is_2ghz); nf[nnf] = use_cal ? p_cal_pier_struct[ipier].rx_noisefloor_cal : p_cal_pier_struct[ipier].rx_noisefloor_power; if (nf[nnf] < 0) { nnf++; } } /* * If we have some valid values, interpolate to find the value * at the desired frequency. */ if (nnf > 0) { nf_use = interpolate(frequency, fx, nf, nnf); } return nf_use; } +/* + * Return the Rx NF offset for specific channel. + * The values saved in EEPROM/OTP/Flash is converted through the following way: + * ((_p) - NOISE_PWR_DATA_OFFSET) << 2 + * So we need to convert back to the original values. + */ +int ar9300_get_rx_nf_offset(struct ath_hal *ah, struct ieee80211_channel *chan, int8_t *nf_pwr, int8_t *nf_cal) { + HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); + int8_t rx_nf_pwr, rx_nf_cal; + int i; + //HALASSERT(ichan); + + /* Fill 0 if valid internal channel is not found */ + if (ichan == AH_NULL) { + OS_MEMZERO(nf_pwr, sizeof(nf_pwr[0])*OSPREY_MAX_CHAINS); + OS_MEMZERO(nf_cal, sizeof(nf_cal[0])*OSPREY_MAX_CHAINS); + return -1; + } + + for (i = 0; i < OSPREY_MAX_CHAINS; i++) { + if ((rx_nf_pwr = ar9300_noise_floor_cal_or_power_get(ah, ichan->channel, i, 0)) == 1) { + nf_pwr[i] = 0; + } else { + //printk("%s: raw nf_pwr[%d] = %d\n", __func__, i, rx_nf_pwr); + nf_pwr[i] = NOISE_PWR_DBM_2_INT(rx_nf_pwr); + } + + if ((rx_nf_cal = ar9300_noise_floor_cal_or_power_get(ah, ichan->channel, i, 1)) == 1) { + nf_cal[i] = 0; + } else { + //printk("%s: raw nf_cal[%d] = %d\n", __func__, i, rx_nf_cal); + nf_cal[i] = NOISE_PWR_DBM_2_INT(rx_nf_cal); + } + } + + return 0; +} + int32_t ar9300_rx_gain_index_get(struct ath_hal *ah) { ar9300_eeprom_t *eep = &AH9300(ah)->ah_eeprom; return (eep->base_eep_header.txrxgain) & 0xf; /* bits 3:0 */ } int32_t ar9300_tx_gain_index_get(struct ath_hal *ah) { ar9300_eeprom_t *eep = &AH9300(ah)->ah_eeprom; return (eep->base_eep_header.txrxgain >> 4) & 0xf; /* bits 7:4 */ } HAL_BOOL ar9300_internal_regulator_apply(struct ath_hal *ah) { struct ath_hal_9300 *ahp = AH9300(ah); int internal_regulator = ar9300_eeprom_get(ahp, EEP_INTERNAL_REGULATOR); int reg_pmu1, reg_pmu2, reg_pmu1_set, reg_pmu2_set; u_int32_t reg_PMU1, reg_PMU2; unsigned long eep_addr; u_int32_t reg_val, reg_usb = 0, reg_pmu = 0; int usb_valid = 0, pmu_valid = 0; unsigned char pmu_refv; if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) { reg_PMU1 = AR_PHY_PMU1_JUPITER; reg_PMU2 = AR_PHY_PMU2_JUPITER; } else { reg_PMU1 = AR_PHY_PMU1; reg_PMU2 = AR_PHY_PMU2; } if (internal_regulator) { if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah)) { if (AR_SREV_HORNET(ah)) { /* Read OTP first */ for (eep_addr = 0x14; ; eep_addr -= 0x10) { ar9300_otp_read(ah, eep_addr / 4, ®_val, 1); if ((reg_val & 0x80) == 0x80){ usb_valid = 1; reg_usb = reg_val & 0x000000ff; } if ((reg_val & 0x80000000) == 0x80000000){ pmu_valid = 1; reg_pmu = (reg_val & 0xff000000) >> 24; } if (eep_addr == 0x4) { break; } } if (pmu_valid) { pmu_refv = reg_pmu & 0xf; } else { pmu_refv = 0x8; } /* * If (valid) { * Usb_phy_ctrl2_tx_cal_en -> 0 * Usb_phy_ctrl2_tx_cal_sel -> 0 * Usb_phy_ctrl2_tx_man_cal -> 0, 1, 3, 7 or 15 from OTP * } */ if (usb_valid) { OS_REG_RMW_FIELD(ah, 0x16c88, AR_PHY_CTRL2_TX_CAL_EN, 0x0); OS_REG_RMW_FIELD(ah, 0x16c88, AR_PHY_CTRL2_TX_CAL_SEL, 0x0); OS_REG_RMW_FIELD(ah, 0x16c88, AR_PHY_CTRL2_TX_MAN_CAL, (reg_usb & 0xf)); } } else { pmu_refv = 0x8; } /*#ifndef USE_HIF*/ /* Follow the MDK settings for Hornet PMU. * my $pwd = 0x0; * my $Nfdiv = 0x3; # xtal_freq = 25MHz * my $Nfdiv = 0x4; # xtal_freq = 40MHz * my $Refv = 0x7; # 0x5:1.22V; 0x8:1.29V * my $Gm1 = 0x3; #Poseidon $Gm1=1 * my $classb = 0x0; * my $Cc = 0x1; #Poseidon $Cc=7 * my $Rc = 0x6; * my $ramp_slope = 0x1; * my $Segm = 0x3; * my $use_local_osc = 0x0; * my $force_xosc_stable = 0x0; * my $Selfb = 0x0; #Poseidon $Selfb=1 * my $Filterfb = 0x3; #Poseidon $Filterfb=0 * my $Filtervc = 0x0; * my $disc = 0x0; * my $discdel = 0x4; * my $spare = 0x0; * $reg_PMU1 = * $pwd | ($Nfdiv<<1) | ($Refv<<4) | ($Gm1<<8) | * ($classb<<11) | ($Cc<<14) | ($Rc<<17) | ($ramp_slope<<20) | * ($Segm<<24) | ($use_local_osc<<26) | * ($force_xosc_stable<<27) | ($Selfb<<28) | ($Filterfb<<29); * $reg_PMU2 = $handle->reg_rd("ch0_PMU2"); * $reg_PMU2 = ($reg_PMU2 & 0xfe3fffff) | ($Filtervc<<22); * $reg_PMU2 = ($reg_PMU2 & 0xe3ffffff) | ($discdel<<26); * $reg_PMU2 = ($reg_PMU2 & 0x1fffffff) | ($spare<<29); */ if (ahp->clk_25mhz) { reg_pmu1_set = 0 | (3 << 1) | (pmu_refv << 4) | (3 << 8) | (0 << 11) | (1 << 14) | (6 << 17) | (1 << 20) | (3 << 24) | (0 << 26) | (0 << 27) | (0 << 28) | (0 << 29); } else { if (AR_SREV_POSEIDON(ah)) { reg_pmu1_set = 0 | (5 << 1) | (7 << 4) | (2 << 8) | (0 << 11) | (2 << 14) | (6 << 17) | (1 << 20) | (3 << 24) | (0 << 26) | (0 << 27) | (1 << 28) | (0 << 29) ; } else { reg_pmu1_set = 0 | (4 << 1) | (7 << 4) | (3 << 8) | (0 << 11) | (1 << 14) | (6 << 17) | (1 << 20) | (3 << 24) | (0 << 26) | (0 << 27) | (0 << 28) | (0 << 29) ; } } OS_REG_RMW_FIELD(ah, reg_PMU2, AR_PHY_PMU2_PGM, 0x0); OS_REG_WRITE(ah, reg_PMU1, reg_pmu1_set); /* 0x638c8376 */ reg_pmu1 = OS_REG_READ(ah, reg_PMU1); while (reg_pmu1 != reg_pmu1_set) { OS_REG_WRITE(ah, reg_PMU1, reg_pmu1_set); /* 0x638c8376 */ OS_DELAY(10); reg_pmu1 = OS_REG_READ(ah, reg_PMU1); } reg_pmu2_set = (OS_REG_READ(ah, reg_PMU2) & (~0xFFC00000)) | (4 << 26); OS_REG_WRITE(ah, reg_PMU2, reg_pmu2_set); reg_pmu2 = OS_REG_READ(ah, reg_PMU2); while (reg_pmu2 != reg_pmu2_set) { OS_REG_WRITE(ah, reg_PMU2, reg_pmu2_set); OS_DELAY(10); reg_pmu2 = OS_REG_READ(ah, reg_PMU2); } reg_pmu2_set = (OS_REG_READ(ah, reg_PMU2) & (~0x00200000)) | (1 << 21); OS_REG_WRITE(ah, reg_PMU2, reg_pmu2_set); reg_pmu2 = OS_REG_READ(ah, reg_PMU2); while (reg_pmu2 != reg_pmu2_set) { OS_REG_WRITE(ah, reg_PMU2, reg_pmu2_set); OS_DELAY(10); reg_pmu2 = OS_REG_READ(ah, reg_PMU2); } /*#endif*/ } else if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) { /* Internal regulator is ON. Write swreg register. */ int swreg = ar9300_eeprom_get(ahp, EEP_SWREG); OS_REG_WRITE(ah, reg_PMU1, swreg); } else { /* Internal regulator is ON. Write swreg register. */ int swreg = ar9300_eeprom_get(ahp, EEP_SWREG); OS_REG_WRITE(ah, AR_RTC_REG_CONTROL1, OS_REG_READ(ah, AR_RTC_REG_CONTROL1) & (~AR_RTC_REG_CONTROL1_SWREG_PROGRAM)); OS_REG_WRITE(ah, AR_RTC_REG_CONTROL0, swreg); /* Set REG_CONTROL1.SWREG_PROGRAM */ OS_REG_WRITE(ah, AR_RTC_REG_CONTROL1, OS_REG_READ(ah, AR_RTC_REG_CONTROL1) | AR_RTC_REG_CONTROL1_SWREG_PROGRAM); } } else { if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah)) { OS_REG_RMW_FIELD(ah, reg_PMU2, AR_PHY_PMU2_PGM, 0x0); reg_pmu2 = OS_REG_READ_FIELD(ah, reg_PMU2, AR_PHY_PMU2_PGM); while (reg_pmu2) { OS_DELAY(10); reg_pmu2 = OS_REG_READ_FIELD(ah, reg_PMU2, AR_PHY_PMU2_PGM); } OS_REG_RMW_FIELD(ah, reg_PMU1, AR_PHY_PMU1_PWD, 0x1); reg_pmu1 = OS_REG_READ_FIELD(ah, reg_PMU1, AR_PHY_PMU1_PWD); while (!reg_pmu1) { OS_DELAY(10); reg_pmu1 = OS_REG_READ_FIELD(ah, reg_PMU1, AR_PHY_PMU1_PWD); } OS_REG_RMW_FIELD(ah, reg_PMU2, AR_PHY_PMU2_PGM, 0x1); reg_pmu2 = OS_REG_READ_FIELD(ah, reg_PMU2, AR_PHY_PMU2_PGM); while (!reg_pmu2) { OS_DELAY(10); reg_pmu2 = OS_REG_READ_FIELD(ah, reg_PMU2, AR_PHY_PMU2_PGM); } } else if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) { OS_REG_RMW_FIELD(ah, reg_PMU1, AR_PHY_PMU1_PWD, 0x1); } else { OS_REG_WRITE(ah, AR_RTC_SLEEP_CLK, (OS_REG_READ(ah, AR_RTC_SLEEP_CLK) | AR_RTC_FORCE_SWREG_PRD | AR_RTC_PCIE_RST_PWDN_EN)); } } return 0; } HAL_BOOL ar9300_drive_strength_apply(struct ath_hal *ah) { struct ath_hal_9300 *ahp = AH9300(ah); int drive_strength; unsigned long reg; drive_strength = ar9300_eeprom_get(ahp, EEP_DRIVE_STRENGTH); if (drive_strength) { reg = OS_REG_READ(ah, AR_PHY_65NM_CH0_BIAS1); reg &= ~0x00ffffc0; reg |= 0x5 << 21; reg |= 0x5 << 18; reg |= 0x5 << 15; reg |= 0x5 << 12; reg |= 0x5 << 9; reg |= 0x5 << 6; OS_REG_WRITE(ah, AR_PHY_65NM_CH0_BIAS1, reg); reg = OS_REG_READ(ah, AR_PHY_65NM_CH0_BIAS2); reg &= ~0xffffffe0; reg |= 0x5 << 29; reg |= 0x5 << 26; reg |= 0x5 << 23; reg |= 0x5 << 20; reg |= 0x5 << 17; reg |= 0x5 << 14; reg |= 0x5 << 11; reg |= 0x5 << 8; reg |= 0x5 << 5; OS_REG_WRITE(ah, AR_PHY_65NM_CH0_BIAS2, reg); reg = OS_REG_READ(ah, AR_PHY_65NM_CH0_BIAS4); reg &= ~0xff800000; reg |= 0x5 << 29; reg |= 0x5 << 26; reg |= 0x5 << 23; OS_REG_WRITE(ah, AR_PHY_65NM_CH0_BIAS4, reg); } return 0; } int32_t ar9300_xpa_bias_level_get(struct ath_hal *ah, HAL_BOOL is_2ghz) { ar9300_eeprom_t *eep = &AH9300(ah)->ah_eeprom; if (is_2ghz) { return eep->modal_header_2g.xpa_bias_lvl; } else { return eep->modal_header_5g.xpa_bias_lvl; } } HAL_BOOL ar9300_xpa_bias_level_apply(struct ath_hal *ah, HAL_BOOL is_2ghz) { /* * In ar9330 emu, we can't access radio registers, * merlin is used for radio part. */ int bias; bias = ar9300_xpa_bias_level_get(ah, is_2ghz); if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah) || AR_SREV_WASP(ah)) { OS_REG_RMW_FIELD(ah, AR_HORNET_CH0_TOP2, AR_HORNET_CH0_TOP2_XPABIASLVL, bias); } else if (AR_SREV_SCORPION(ah)) { OS_REG_RMW_FIELD(ah, AR_SCORPION_CH0_TOP, AR_SCORPION_CH0_TOP_XPABIASLVL, bias); } else if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) { OS_REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_TOP_JUPITER, AR_PHY_65NM_CH0_TOP_XPABIASLVL, bias); } else { OS_REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_TOP, AR_PHY_65NM_CH0_TOP_XPABIASLVL, bias); OS_REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_THERM, AR_PHY_65NM_CH0_THERM_XPABIASLVL_MSB, bias >> 2); OS_REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_THERM, AR_PHY_65NM_CH0_THERM_XPASHORT2GND, 1); } return 0; } u_int32_t ar9300_ant_ctrl_common_get(struct ath_hal *ah, HAL_BOOL is_2ghz) { ar9300_eeprom_t *eep = &AH9300(ah)->ah_eeprom; if (is_2ghz) { return eep->modal_header_2g.ant_ctrl_common; } else { return eep->modal_header_5g.ant_ctrl_common; } } static u_int16_t ar9300_switch_com_spdt_get(struct ath_hal *ah, HAL_BOOL is_2ghz) { ar9300_eeprom_t *eep = &AH9300(ah)->ah_eeprom; if (is_2ghz) { return eep->modal_header_2g.switchcomspdt; } else { return eep->modal_header_5g.switchcomspdt; } } u_int32_t ar9300_ant_ctrl_common2_get(struct ath_hal *ah, HAL_BOOL is_2ghz) { ar9300_eeprom_t *eep = &AH9300(ah)->ah_eeprom; if (is_2ghz) { return eep->modal_header_2g.ant_ctrl_common2; } else { return eep->modal_header_5g.ant_ctrl_common2; } } u_int16_t ar9300_ant_ctrl_chain_get(struct ath_hal *ah, int chain, HAL_BOOL is_2ghz) { ar9300_eeprom_t *eep = &AH9300(ah)->ah_eeprom; if (chain >= 0 && chain < OSPREY_MAX_CHAINS) { if (is_2ghz) { return eep->modal_header_2g.ant_ctrl_chain[chain]; } else { return eep->modal_header_5g.ant_ctrl_chain[chain]; } } return 0; } +/* + * Select the usage of antenna via the RF switch. + * Default values are loaded from eeprom. + */ +HAL_BOOL ar9300_ant_swcom_sel(struct ath_hal *ah, u_int8_t ops, + u_int32_t *common_tbl1, u_int32_t *common_tbl2) +{ + ar9300_eeprom_t *eep = &AH9300(ah)->ah_eeprom; + struct ath_hal_private *ap = AH_PRIVATE(ah); + const struct ieee80211_channel *curchan = ap->ah_curchan; + enum { + ANT_SELECT_OPS_GET, + ANT_SELECT_OPS_SET, + }; + + if (AR_SREV_JUPITER(ah) || AR_SREV_SCORPION(ah)) + return AH_FALSE; + + if (!curchan) + return AH_FALSE; + +#define AR_SWITCH_TABLE_COM_ALL (0xffff) +#define AR_SWITCH_TABLE_COM_ALL_S (0) +#define AR_SWITCH_TABLE_COM2_ALL (0xffffff) +#define AR_SWITCH_TABLE_COM2_ALL_S (0) + switch (ops) { + case ANT_SELECT_OPS_GET: + *common_tbl1 = OS_REG_READ_FIELD(ah, AR_PHY_SWITCH_COM, + AR_SWITCH_TABLE_COM_ALL); + *common_tbl2 = OS_REG_READ_FIELD(ah, AR_PHY_SWITCH_COM_2, + AR_SWITCH_TABLE_COM2_ALL); + break; + case ANT_SELECT_OPS_SET: + OS_REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM, + AR_SWITCH_TABLE_COM_ALL, *common_tbl1); + OS_REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM_2, + AR_SWITCH_TABLE_COM2_ALL, *common_tbl2); + + /* write back to eeprom */ + if (IEEE80211_IS_CHAN_2GHZ(curchan)) { + eep->modal_header_2g.ant_ctrl_common = *common_tbl1; + eep->modal_header_2g.ant_ctrl_common2 = *common_tbl2; + } else { + eep->modal_header_5g.ant_ctrl_common = *common_tbl1; + eep->modal_header_5g.ant_ctrl_common2 = *common_tbl2; + } + + break; + default: + break; + } + + return AH_TRUE; +} + HAL_BOOL ar9300_ant_ctrl_apply(struct ath_hal *ah, HAL_BOOL is_2ghz) { u_int32_t value; struct ath_hal_9300 *ahp = AH9300(ah); u_int32_t regval; struct ath_hal_private *ahpriv = AH_PRIVATE(ah); #if ATH_ANT_DIV_COMB HAL_CAPABILITIES *pcap = &ahpriv->ah_caps; #endif /* ATH_ANT_DIV_COMB */ u_int32_t xlan_gpio_cfg; u_int8_t i; if (AR_SREV_POSEIDON(ah)) { xlan_gpio_cfg = ah->ah_config.ath_hal_ext_lna_ctl_gpio; if (xlan_gpio_cfg) { for (i = 0; i < 32; i++) { if (xlan_gpio_cfg & (1 << i)) { ath_hal_gpioCfgOutput(ah, i, HAL_GPIO_OUTPUT_MUX_PCIE_ATTENTION_LED); } } } } #define AR_SWITCH_TABLE_COM_ALL (0xffff) #define AR_SWITCH_TABLE_COM_ALL_S (0) #define AR_SWITCH_TABLE_COM_JUPITER_ALL (0xffffff) #define AR_SWITCH_TABLE_COM_JUPITER_ALL_S (0) #define AR_SWITCH_TABLE_COM_SCORPION_ALL (0xffffff) #define AR_SWITCH_TABLE_COM_SCORPION_ALL_S (0) #define AR_SWITCH_TABLE_COM_SPDT (0x00f00000) value = ar9300_ant_ctrl_common_get(ah, is_2ghz); if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) { if (AR_SREV_JUPITER_10(ah)) { /* Force SPDT setting for Jupiter 1.0 chips. */ value &= ~AR_SWITCH_TABLE_COM_SPDT; value |= 0x00100000; } OS_REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM, AR_SWITCH_TABLE_COM_JUPITER_ALL, value); } else if (AR_SREV_SCORPION(ah)) { OS_REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM, AR_SWITCH_TABLE_COM_SCORPION_ALL, value); } else { OS_REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM, AR_SWITCH_TABLE_COM_ALL, value); } /* * Jupiter2.0 defines new switch table for BT/WLAN, * here's new field name in WB222.ref for both 2G and 5G. * Register: [GLB_CONTROL] GLB_CONTROL (@0x20044) * 15:12 R/W SWITCH_TABLE_COM_SPDT_WLAN_RX SWITCH_TABLE_COM_SPDT_WLAN_RX * 11:8 R/W SWITCH_TABLE_COM_SPDT_WLAN_TX SWITCH_TABLE_COM_SPDT_WLAN_TX * 7:4 R/W SWITCH_TABLE_COM_SPDT_WLAN_IDLE SWITCH_TABLE_COM_SPDT_WLAN_IDLE */ #define AR_SWITCH_TABLE_COM_SPDT_ALL (0x0000fff0) #define AR_SWITCH_TABLE_COM_SPDT_ALL_S (4) if (AR_SREV_JUPITER_20_OR_LATER(ah) || AR_SREV_APHRODITE(ah)) { value = ar9300_switch_com_spdt_get(ah, is_2ghz); OS_REG_RMW_FIELD(ah, AR_GLB_CONTROL, AR_SWITCH_TABLE_COM_SPDT_ALL, value); OS_REG_SET_BIT(ah, AR_GLB_CONTROL, AR_BTCOEX_CTRL_SPDT_ENABLE); //OS_REG_SET_BIT(ah, AR_GLB_CONTROL, // AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL); } #define AR_SWITCH_TABLE_COM2_ALL (0xffffff) #define AR_SWITCH_TABLE_COM2_ALL_S (0) value = ar9300_ant_ctrl_common2_get(ah, is_2ghz); #if ATH_ANT_DIV_COMB if ( AR_SREV_POSEIDON(ah) && (ahp->ah_lna_div_use_bt_ant_enable == TRUE) ) { value &= ~AR_SWITCH_TABLE_COM2_ALL; value |= ah->ah_config.ath_hal_ant_ctrl_comm2g_switch_enable; HALDEBUG(ah, HAL_DEBUG_RESET, "%s: com2=0x%08x\n", __func__, value) } #endif /* ATH_ANT_DIV_COMB */ OS_REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM_2, AR_SWITCH_TABLE_COM2_ALL, value); #define AR_SWITCH_TABLE_ALL (0xfff) #define AR_SWITCH_TABLE_ALL_S (0) value = ar9300_ant_ctrl_chain_get(ah, 0, is_2ghz); OS_REG_RMW_FIELD(ah, AR_PHY_SWITCH_CHAIN_0, AR_SWITCH_TABLE_ALL, value); if (!AR_SREV_HORNET(ah) && !AR_SREV_POSEIDON(ah) && !AR_SREV_APHRODITE(ah)) { value = ar9300_ant_ctrl_chain_get(ah, 1, is_2ghz); OS_REG_RMW_FIELD(ah, AR_PHY_SWITCH_CHAIN_1, AR_SWITCH_TABLE_ALL, value); if (!AR_SREV_WASP(ah) && !AR_SREV_JUPITER(ah)) { value = ar9300_ant_ctrl_chain_get(ah, 2, is_2ghz); OS_REG_RMW_FIELD(ah, AR_PHY_SWITCH_CHAIN_2, AR_SWITCH_TABLE_ALL, value); } } if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah)) { value = ar9300_eeprom_get(ahp, EEP_ANTDIV_control); /* main_lnaconf, alt_lnaconf, main_tb, alt_tb */ regval = OS_REG_READ(ah, AR_PHY_MC_GAIN_CTRL); regval &= (~ANT_DIV_CONTROL_ALL); /* clear bit 25~30 */ regval |= (value & 0x3f) << ANT_DIV_CONTROL_ALL_S; /* enable_lnadiv */ regval &= (~MULTICHAIN_GAIN_CTRL__ENABLE_ANT_DIV_LNADIV__MASK); regval |= ((value >> 6) & 0x1) << MULTICHAIN_GAIN_CTRL__ENABLE_ANT_DIV_LNADIV__SHIFT; #if ATH_ANT_DIV_COMB if ( AR_SREV_POSEIDON(ah) && (ahp->ah_lna_div_use_bt_ant_enable == TRUE) ) { regval |= ANT_DIV_ENABLE; } #endif /* ATH_ANT_DIV_COMB */ OS_REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval); /* enable fast_div */ regval = OS_REG_READ(ah, AR_PHY_CCK_DETECT); regval &= (~BBB_SIG_DETECT__ENABLE_ANT_FAST_DIV__MASK); regval |= ((value >> 7) & 0x1) << BBB_SIG_DETECT__ENABLE_ANT_FAST_DIV__SHIFT; #if ATH_ANT_DIV_COMB if ( AR_SREV_POSEIDON(ah) && (ahp->ah_lna_div_use_bt_ant_enable == TRUE) ) { regval |= FAST_DIV_ENABLE; } #endif /* ATH_ANT_DIV_COMB */ OS_REG_WRITE(ah, AR_PHY_CCK_DETECT, regval); } #if ATH_ANT_DIV_COMB if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON_11_OR_LATER(ah)) { if (pcap->halAntDivCombSupport) { /* If support DivComb, set MAIN to LNA1, ALT to LNA2 at beginning */ regval = OS_REG_READ(ah, AR_PHY_MC_GAIN_CTRL); /* clear bit 25~30 main_lnaconf, alt_lnaconf, main_tb, alt_tb */ regval &= (~(MULTICHAIN_GAIN_CTRL__ANT_DIV_MAIN_LNACONF__MASK | MULTICHAIN_GAIN_CTRL__ANT_DIV_ALT_LNACONF__MASK | MULTICHAIN_GAIN_CTRL__ANT_DIV_ALT_GAINTB__MASK | MULTICHAIN_GAIN_CTRL__ANT_DIV_MAIN_GAINTB__MASK)); regval |= (HAL_ANT_DIV_COMB_LNA1 << MULTICHAIN_GAIN_CTRL__ANT_DIV_MAIN_LNACONF__SHIFT); regval |= (HAL_ANT_DIV_COMB_LNA2 << MULTICHAIN_GAIN_CTRL__ANT_DIV_ALT_LNACONF__SHIFT); OS_REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval); } } #endif /* ATH_ANT_DIV_COMB */ if (AR_SREV_POSEIDON(ah) && ( ahp->ah_diversity_control == HAL_ANT_FIXED_A || ahp->ah_diversity_control == HAL_ANT_FIXED_B)) { u_int32_t reg_val = OS_REG_READ(ah, AR_PHY_MC_GAIN_CTRL); reg_val &= ~(MULTICHAIN_GAIN_CTRL__ANT_DIV_MAIN_LNACONF__MASK | MULTICHAIN_GAIN_CTRL__ANT_DIV_ALT_LNACONF__MASK | MULTICHAIN_GAIN_CTRL__ANT_FAST_DIV_BIAS__MASK | MULTICHAIN_GAIN_CTRL__ANT_DIV_MAIN_GAINTB__MASK | MULTICHAIN_GAIN_CTRL__ANT_DIV_ALT_GAINTB__MASK ); switch (ahp->ah_diversity_control) { case HAL_ANT_FIXED_A: /* Enable first antenna only */ reg_val |= (HAL_ANT_DIV_COMB_LNA1 << MULTICHAIN_GAIN_CTRL__ANT_DIV_MAIN_LNACONF__SHIFT); reg_val |= (HAL_ANT_DIV_COMB_LNA2 << MULTICHAIN_GAIN_CTRL__ANT_DIV_ALT_LNACONF__SHIFT); /* main/alt gain table and Fast Div Bias all set to 0 */ OS_REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, reg_val); regval = OS_REG_READ(ah, AR_PHY_CCK_DETECT); regval &= (~BBB_SIG_DETECT__ENABLE_ANT_FAST_DIV__MASK); OS_REG_WRITE(ah, AR_PHY_CCK_DETECT, regval); break; case HAL_ANT_FIXED_B: /* Enable second antenna only, after checking capability */ reg_val |= (HAL_ANT_DIV_COMB_LNA2 << MULTICHAIN_GAIN_CTRL__ANT_DIV_MAIN_LNACONF__SHIFT); reg_val |= (HAL_ANT_DIV_COMB_LNA1 << MULTICHAIN_GAIN_CTRL__ANT_DIV_ALT_LNACONF__SHIFT); /* main/alt gain table and Fast Div all set to 0 */ OS_REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, reg_val); regval = OS_REG_READ(ah, AR_PHY_CCK_DETECT); regval &= (~BBB_SIG_DETECT__ENABLE_ANT_FAST_DIV__MASK); OS_REG_WRITE(ah, AR_PHY_CCK_DETECT, regval); /* For WB225, need to swith ANT2 from BT to Wifi * This will not affect HB125 LNA diversity feature. */ HALDEBUG(ah, HAL_DEBUG_RESET, "%s: com2=0x%08x\n", __func__, ah->ah_config.ath_hal_ant_ctrl_comm2g_switch_enable) OS_REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM_2, AR_SWITCH_TABLE_COM2_ALL, ah->ah_config.ath_hal_ant_ctrl_comm2g_switch_enable); break; default: break; } } return 0; } static u_int16_t ar9300_attenuation_chain_get(struct ath_hal *ah, int chain, u_int16_t channel) { int32_t f[3], t[3]; u_int16_t value; ar9300_eeprom_t *eep = &AH9300(ah)->ah_eeprom; if (chain >= 0 && chain < OSPREY_MAX_CHAINS) { if (channel < 4000) { return eep->modal_header_2g.xatten1_db[chain]; } else { if (eep->base_ext2.xatten1_db_low[chain] != 0) { t[0] = eep->base_ext2.xatten1_db_low[chain]; f[0] = 5180; t[1] = eep->modal_header_5g.xatten1_db[chain]; f[1] = 5500; t[2] = eep->base_ext2.xatten1_db_high[chain]; f[2] = 5785; value = interpolate(channel, f, t, 3); return value; } else { return eep->modal_header_5g.xatten1_db[chain]; } } } return 0; } static u_int16_t ar9300_attenuation_margin_chain_get(struct ath_hal *ah, int chain, u_int16_t channel) { int32_t f[3], t[3]; u_int16_t value; ar9300_eeprom_t *eep = &AH9300(ah)->ah_eeprom; if (chain >= 0 && chain < OSPREY_MAX_CHAINS) { if (channel < 4000) { return eep->modal_header_2g.xatten1_margin[chain]; } else { if (eep->base_ext2.xatten1_margin_low[chain] != 0) { t[0] = eep->base_ext2.xatten1_margin_low[chain]; f[0] = 5180; t[1] = eep->modal_header_5g.xatten1_margin[chain]; f[1] = 5500; t[2] = eep->base_ext2.xatten1_margin_high[chain]; f[2] = 5785; value = interpolate(channel, f, t, 3); return value; } else { return eep->modal_header_5g.xatten1_margin[chain]; } } } return 0; } #if 0 HAL_BOOL ar9300_attenuation_apply(struct ath_hal *ah, u_int16_t channel) { u_int32_t value; // struct ath_hal_private *ahpriv = AH_PRIVATE(ah); /* Test value. if 0 then attenuation is unused. Don't load anything. */ value = ar9300_attenuation_chain_get(ah, 0, channel); OS_REG_RMW_FIELD(ah, AR_PHY_EXT_ATTEN_CTL_0, AR_PHY_EXT_ATTEN_CTL_XATTEN1_DB, value); value = ar9300_attenuation_margin_chain_get(ah, 0, channel); if (ar9300_rx_gain_index_get(ah) == 0 && ah->ah_config.ath_hal_ext_atten_margin_cfg) { value = 5; } OS_REG_RMW_FIELD(ah, AR_PHY_EXT_ATTEN_CTL_0, AR_PHY_EXT_ATTEN_CTL_XATTEN1_MARGIN, value); if (!AR_SREV_HORNET(ah) && !AR_SREV_POSEIDON(ah)) { value = ar9300_attenuation_chain_get(ah, 1, channel); OS_REG_RMW_FIELD(ah, AR_PHY_EXT_ATTEN_CTL_1, AR_PHY_EXT_ATTEN_CTL_XATTEN1_DB, value); value = ar9300_attenuation_margin_chain_get(ah, 1, channel); OS_REG_RMW_FIELD(ah, AR_PHY_EXT_ATTEN_CTL_1, AR_PHY_EXT_ATTEN_CTL_XATTEN1_MARGIN, value); if (!AR_SREV_WASP(ah) && !AR_SREV_JUPITER(ah)) { value = ar9300_attenuation_chain_get(ah, 2, channel); OS_REG_RMW_FIELD(ah, AR_PHY_EXT_ATTEN_CTL_2, AR_PHY_EXT_ATTEN_CTL_XATTEN1_DB, value); value = ar9300_attenuation_margin_chain_get(ah, 2, channel); OS_REG_RMW_FIELD(ah, AR_PHY_EXT_ATTEN_CTL_2, AR_PHY_EXT_ATTEN_CTL_XATTEN1_MARGIN, value); } } return 0; } #endif HAL_BOOL ar9300_attenuation_apply(struct ath_hal *ah, u_int16_t channel) { int i; uint32_t value; uint32_t ext_atten_reg[3] = { AR_PHY_EXT_ATTEN_CTL_0, AR_PHY_EXT_ATTEN_CTL_1, AR_PHY_EXT_ATTEN_CTL_2 }; /* * If it's an AR9462 and we're receiving on the second * chain only, set the chain 0 details from chain 1 * calibration. * * This is from ath9k. */ if (AR_SREV_JUPITER(ah) && (AH9300(ah)->ah_rx_chainmask == 0x2)) { value = ar9300_attenuation_chain_get(ah, 1, channel); OS_REG_RMW_FIELD(ah, ext_atten_reg[0], AR_PHY_EXT_ATTEN_CTL_XATTEN1_DB, value); value = ar9300_attenuation_margin_chain_get(ah, 1, channel); OS_REG_RMW_FIELD(ah, ext_atten_reg[0], AR_PHY_EXT_ATTEN_CTL_XATTEN1_MARGIN, value); } /* * Now, loop over the configured transmit chains and * load in the attenuation/margin settings as appropriate. */ for (i = 0; i < 3; i++) { if ((AH9300(ah)->ah_tx_chainmask & (1 << i)) == 0) continue; value = ar9300_attenuation_chain_get(ah, i, channel); OS_REG_RMW_FIELD(ah, ext_atten_reg[i], AR_PHY_EXT_ATTEN_CTL_XATTEN1_DB, value); if (AR_SREV_POSEIDON(ah) && (ar9300_rx_gain_index_get(ah) == 0) && ah->ah_config.ath_hal_ext_atten_margin_cfg) { value = 5; } else { value = ar9300_attenuation_margin_chain_get(ah, 0, channel); } /* * I'm not sure why it's loading in this setting into * the chain 0 margin regardless of the current chain. */ if (ah->ah_config.ath_hal_min_gainidx) OS_REG_RMW_FIELD(ah, AR_PHY_EXT_ATTEN_CTL_0, AR_PHY_EXT_ATTEN_CTL_XATTEN1_MARGIN, value); OS_REG_RMW_FIELD(ah, ext_atten_reg[i], AR_PHY_EXT_ATTEN_CTL_XATTEN1_MARGIN, value); } return (0); } static u_int16_t ar9300_quick_drop_get(struct ath_hal *ah, int chain, u_int16_t channel) { int32_t f[3], t[3]; u_int16_t value; ar9300_eeprom_t *eep = &AH9300(ah)->ah_eeprom; if (channel < 4000) { return eep->modal_header_2g.quick_drop; } else { t[0] = eep->base_ext1.quick_drop_low; f[0] = 5180; t[1] = eep->modal_header_5g.quick_drop; f[1] = 5500; t[2] = eep->base_ext1.quick_drop_high; f[2] = 5785; value = interpolate(channel, f, t, 3); return value; } } static HAL_BOOL ar9300_quick_drop_apply(struct ath_hal *ah, u_int16_t channel) { ar9300_eeprom_t *eep = &AH9300(ah)->ah_eeprom; u_int32_t value; // // Test value. if 0 then quickDrop is unused. Don't load anything. // if (eep->base_eep_header.misc_configuration & 0x10) { if (AR_SREV_OSPREY(ah) || AR_SREV_AR9580(ah) || AR_SREV_WASP(ah)) { value = ar9300_quick_drop_get(ah, 0, channel); OS_REG_RMW_FIELD(ah, AR_PHY_AGC, AR_PHY_AGC_QUICK_DROP, value); } } return 0; } static u_int16_t ar9300_tx_end_to_xpa_off_get(struct ath_hal *ah, u_int16_t channel) { ar9300_eeprom_t *eep = &AH9300(ah)->ah_eeprom; if (channel < 4000) { return eep->modal_header_2g.tx_end_to_xpa_off; } else { return eep->modal_header_5g.tx_end_to_xpa_off; } } static HAL_BOOL ar9300_tx_end_to_xpab_off_apply(struct ath_hal *ah, u_int16_t channel) { u_int32_t value; value = ar9300_tx_end_to_xpa_off_get(ah, channel); /* Apply to both xpaa and xpab */ if (AR_SREV_OSPREY(ah) || AR_SREV_AR9580(ah) || AR_SREV_WASP(ah)) { OS_REG_RMW_FIELD(ah, AR_PHY_XPA_TIMING_CTL, AR_PHY_XPA_TIMING_CTL_TX_END_XPAB_OFF, value); OS_REG_RMW_FIELD(ah, AR_PHY_XPA_TIMING_CTL, AR_PHY_XPA_TIMING_CTL_TX_END_XPAA_OFF, value); } return 0; } static int ar9300_eeprom_cal_pier_get(struct ath_hal *ah, int mode, int ipier, int ichain, int *pfrequency, int *pcorrection, int *ptemperature, int *pvoltage) { u_int8_t *p_cal_pier; OSP_CAL_DATA_PER_FREQ_OP_LOOP *p_cal_pier_struct; int is_2ghz; ar9300_eeprom_t *eep = &AH9300(ah)->ah_eeprom; if (ichain >= OSPREY_MAX_CHAINS) { HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: Invalid chain index, must be less than %d\n", __func__, OSPREY_MAX_CHAINS); return -1; } if (mode) {/* 5GHz */ if (ipier >= OSPREY_NUM_5G_CAL_PIERS){ HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: Invalid 5GHz cal pier index, must be less than %d\n", __func__, OSPREY_NUM_5G_CAL_PIERS); return -1; } p_cal_pier = &(eep->cal_freq_pier_5g[ipier]); p_cal_pier_struct = &(eep->cal_pier_data_5g[ichain][ipier]); is_2ghz = 0; } else { if (ipier >= OSPREY_NUM_2G_CAL_PIERS){ HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: Invalid 2GHz cal pier index, must be less than %d\n", __func__, OSPREY_NUM_2G_CAL_PIERS); return -1; } p_cal_pier = &(eep->cal_freq_pier_2g[ipier]); p_cal_pier_struct = &(eep->cal_pier_data_2g[ichain][ipier]); is_2ghz = 1; } *pfrequency = FBIN2FREQ(*p_cal_pier, is_2ghz); *pcorrection = p_cal_pier_struct->ref_power; *ptemperature = p_cal_pier_struct->temp_meas; *pvoltage = p_cal_pier_struct->volt_meas; return 0; } /* * Apply the recorded correction values. */ static int ar9300_calibration_apply(struct ath_hal *ah, int frequency) { struct ath_hal_9300 *ahp = AH9300(ah); int ichain, ipier, npier; int mode; int fdiff; int pfrequency, pcorrection, ptemperature, pvoltage; int bf, factor, plus; int lfrequency[AR9300_MAX_CHAINS]; int hfrequency[AR9300_MAX_CHAINS]; int lcorrection[AR9300_MAX_CHAINS]; int hcorrection[AR9300_MAX_CHAINS]; int correction[AR9300_MAX_CHAINS]; int ltemperature[AR9300_MAX_CHAINS]; int htemperature[AR9300_MAX_CHAINS]; int temperature[AR9300_MAX_CHAINS]; int lvoltage[AR9300_MAX_CHAINS]; int hvoltage[AR9300_MAX_CHAINS]; int voltage[AR9300_MAX_CHAINS]; mode = (frequency >= 4000); npier = (mode) ? OSPREY_NUM_5G_CAL_PIERS : OSPREY_NUM_2G_CAL_PIERS; for (ichain = 0; ichain < AR9300_MAX_CHAINS; ichain++) { lfrequency[ichain] = 0; hfrequency[ichain] = 100000; } /* * identify best lower and higher frequency calibration measurement */ for (ichain = 0; ichain < AR9300_MAX_CHAINS; ichain++) { for (ipier = 0; ipier < npier; ipier++) { if (ar9300_eeprom_cal_pier_get( ah, mode, ipier, ichain, &pfrequency, &pcorrection, &ptemperature, &pvoltage) == 0) { fdiff = frequency - pfrequency; /* * this measurement is higher than our desired frequency */ if (fdiff <= 0) { if (hfrequency[ichain] <= 0 || hfrequency[ichain] >= 100000 || fdiff > (frequency - hfrequency[ichain])) { /* * new best higher frequency measurement */ hfrequency[ichain] = pfrequency; hcorrection[ichain] = pcorrection; htemperature[ichain] = ptemperature; hvoltage[ichain] = pvoltage; } } if (fdiff >= 0) { if (lfrequency[ichain] <= 0 || fdiff < (frequency - lfrequency[ichain])) { /* * new best lower frequency measurement */ lfrequency[ichain] = pfrequency; lcorrection[ichain] = pcorrection; ltemperature[ichain] = ptemperature; lvoltage[ichain] = pvoltage; } } } } } /* interpolate */ for (ichain = 0; ichain < AR9300_MAX_CHAINS; ichain++) { HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: ch=%d f=%d low=%d %d h=%d %d\n", __func__, ichain, frequency, lfrequency[ichain], lcorrection[ichain], hfrequency[ichain], hcorrection[ichain]); /* * they're the same, so just pick one */ if (hfrequency[ichain] == lfrequency[ichain]) { correction[ichain] = lcorrection[ichain]; voltage[ichain] = lvoltage[ichain]; temperature[ichain] = ltemperature[ichain]; } else if (frequency - lfrequency[ichain] < 1000) { /* the low frequency is good */ if (hfrequency[ichain] - frequency < 1000) { /* * The high frequency is good too - * interpolate with round off. */ int mult, div, diff; mult = frequency - lfrequency[ichain]; div = hfrequency[ichain] - lfrequency[ichain]; diff = hcorrection[ichain] - lcorrection[ichain]; bf = 2 * diff * mult / div; plus = (bf % 2); factor = bf / 2; correction[ichain] = lcorrection[ichain] + factor + plus; diff = htemperature[ichain] - ltemperature[ichain]; bf = 2 * diff * mult / div; plus = (bf % 2); factor = bf / 2; temperature[ichain] = ltemperature[ichain] + factor + plus; diff = hvoltage[ichain] - lvoltage[ichain]; bf = 2 * diff * mult / div; plus = (bf % 2); factor = bf / 2; voltage[ichain] = lvoltage[ichain] + factor + plus; } else { /* only low is good, use it */ correction[ichain] = lcorrection[ichain]; temperature[ichain] = ltemperature[ichain]; voltage[ichain] = lvoltage[ichain]; } } else if (hfrequency[ichain] - frequency < 1000) { /* only high is good, use it */ correction[ichain] = hcorrection[ichain]; temperature[ichain] = htemperature[ichain]; voltage[ichain] = hvoltage[ichain]; } else { /* nothing is good, presume 0???? */ correction[ichain] = 0; temperature[ichain] = 0; voltage[ichain] = 0; } } /* GreenTx isn't currently supported */ /* GreenTx */ if (ah->ah_config.ath_hal_sta_update_tx_pwr_enable) { if (AR_SREV_POSEIDON(ah)) { /* Get calibrated OLPC gain delta value for GreenTx */ ahp->ah_db2[POSEIDON_STORED_REG_G2_OLPC_OFFSET] = (u_int32_t) correction[0]; } } ar9300_power_control_override( ah, frequency, correction, voltage, temperature); HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: for frequency=%d, calibration correction = %d %d %d\n", __func__, frequency, correction[0], correction[1], correction[2]); return 0; } int ar9300_power_control_override(struct ath_hal *ah, int frequency, int *correction, int *voltage, int *temperature) { int temp_slope = 0; int temp_slope_1 = 0; int temp_slope_2 = 0; ar9300_eeprom_t *eep = &AH9300(ah)->ah_eeprom; int32_t f[8], t[8],t1[3], t2[3]; int i; OS_REG_RMW(ah, AR_PHY_TPC_11_B0, (correction[0] << AR_PHY_TPC_OLPC_GAIN_DELTA_S), AR_PHY_TPC_OLPC_GAIN_DELTA); if (!AR_SREV_POSEIDON(ah)) { OS_REG_RMW(ah, AR_PHY_TPC_11_B1, (correction[1] << AR_PHY_TPC_OLPC_GAIN_DELTA_S), AR_PHY_TPC_OLPC_GAIN_DELTA); if (!AR_SREV_WASP(ah) && !AR_SREV_JUPITER(ah)) { OS_REG_RMW(ah, AR_PHY_TPC_11_B2, (correction[2] << AR_PHY_TPC_OLPC_GAIN_DELTA_S), AR_PHY_TPC_OLPC_GAIN_DELTA); } } /* * enable open loop power control on chip */ OS_REG_RMW(ah, AR_PHY_TPC_6_B0, (3 << AR_PHY_TPC_6_ERROR_EST_MODE_S), AR_PHY_TPC_6_ERROR_EST_MODE); if (!AR_SREV_POSEIDON(ah)) { OS_REG_RMW(ah, AR_PHY_TPC_6_B1, (3 << AR_PHY_TPC_6_ERROR_EST_MODE_S), AR_PHY_TPC_6_ERROR_EST_MODE); if (!AR_SREV_WASP(ah) && !AR_SREV_JUPITER(ah)) { OS_REG_RMW(ah, AR_PHY_TPC_6_B2, (3 << AR_PHY_TPC_6_ERROR_EST_MODE_S), AR_PHY_TPC_6_ERROR_EST_MODE); } } /* * Enable temperature compensation * Need to use register names */ if (frequency < 4000) { temp_slope = eep->modal_header_2g.temp_slope; } else { if ((eep->base_eep_header.misc_configuration & 0x20) != 0) { for(i=0;i<8;i++) { t[i]=eep->base_ext1.tempslopextension[i]; f[i]=FBIN2FREQ(eep->cal_freq_pier_5g[i], 0); } temp_slope=interpolate(frequency,f,t,8); } else { if(!AR_SREV_SCORPION(ah)) { if (eep->base_ext2.temp_slope_low != 0) { t[0] = eep->base_ext2.temp_slope_low; f[0] = 5180; t[1] = eep->modal_header_5g.temp_slope; f[1] = 5500; t[2] = eep->base_ext2.temp_slope_high; f[2] = 5785; temp_slope = interpolate(frequency, f, t, 3); } else { temp_slope = eep->modal_header_5g.temp_slope; } } else { /* * Scorpion has individual chain tempslope values */ t[0] = eep->base_ext1.tempslopextension[2]; t1[0]= eep->base_ext1.tempslopextension[3]; t2[0]= eep->base_ext1.tempslopextension[4]; f[0] = 5180; t[1] = eep->modal_header_5g.temp_slope; t1[1]= eep->base_ext1.tempslopextension[0]; t2[1]= eep->base_ext1.tempslopextension[1]; f[1] = 5500; t[2] = eep->base_ext1.tempslopextension[5]; t1[2]= eep->base_ext1.tempslopextension[6]; t2[2]= eep->base_ext1.tempslopextension[7]; f[2] = 5785; temp_slope = interpolate(frequency, f, t, 3); temp_slope_1=interpolate(frequency, f, t1,3); temp_slope_2=interpolate(frequency, f, t2,3); } } } if (!AR_SREV_SCORPION(ah)) { OS_REG_RMW_FIELD(ah, AR_PHY_TPC_19, AR_PHY_TPC_19_ALPHA_THERM, temp_slope); } else { /*Scorpion has tempSlope register for each chain*/ /*Check whether temp_compensation feature is enabled or not*/ if (eep->base_eep_header.feature_enable & 0x1){ if(frequency < 4000) { OS_REG_RMW_FIELD(ah, AR_PHY_TPC_19, AR_PHY_TPC_19_ALPHA_THERM, eep->base_ext2.temp_slope_low); OS_REG_RMW_FIELD(ah, AR_SCORPION_PHY_TPC_19_B1, AR_PHY_TPC_19_ALPHA_THERM, temp_slope); OS_REG_RMW_FIELD(ah, AR_SCORPION_PHY_TPC_19_B2, AR_PHY_TPC_19_ALPHA_THERM, eep->base_ext2.temp_slope_high); } else { OS_REG_RMW_FIELD(ah, AR_PHY_TPC_19, AR_PHY_TPC_19_ALPHA_THERM, temp_slope); OS_REG_RMW_FIELD(ah, AR_SCORPION_PHY_TPC_19_B1, AR_PHY_TPC_19_ALPHA_THERM, temp_slope_1); OS_REG_RMW_FIELD(ah, AR_SCORPION_PHY_TPC_19_B2, AR_PHY_TPC_19_ALPHA_THERM, temp_slope_2); } }else { /* If temp compensation is not enabled, set all registers to 0*/ OS_REG_RMW_FIELD(ah, AR_PHY_TPC_19, AR_PHY_TPC_19_ALPHA_THERM, 0); OS_REG_RMW_FIELD(ah, AR_SCORPION_PHY_TPC_19_B1, AR_PHY_TPC_19_ALPHA_THERM, 0); OS_REG_RMW_FIELD(ah, AR_SCORPION_PHY_TPC_19_B2, AR_PHY_TPC_19_ALPHA_THERM, 0); } } OS_REG_RMW_FIELD(ah, AR_PHY_TPC_18, AR_PHY_TPC_18_THERM_CAL_VALUE, temperature[0]); return 0; } /************************************************************** * ar9300_eep_def_get_max_edge_power * * Find the maximum conformance test limit for the given channel and CTL info */ static inline u_int16_t ar9300_eep_def_get_max_edge_power(ar9300_eeprom_t *p_eep_data, u_int16_t freq, int idx, HAL_BOOL is_2ghz) { u_int16_t twice_max_edge_power = AR9300_MAX_RATE_POWER; u_int8_t *ctl_freqbin = is_2ghz ? &p_eep_data->ctl_freqbin_2G[idx][0] : &p_eep_data->ctl_freqbin_5G[idx][0]; u_int16_t num_edges = is_2ghz ? OSPREY_NUM_BAND_EDGES_2G : OSPREY_NUM_BAND_EDGES_5G; int i; /* Get the edge power */ for (i = 0; (i < num_edges) && (ctl_freqbin[i] != AR9300_BCHAN_UNUSED); i++) { /* * If there's an exact channel match or an inband flag set * on the lower channel use the given rd_edge_power */ if (freq == fbin2freq(ctl_freqbin[i], is_2ghz)) { if (is_2ghz) { twice_max_edge_power = p_eep_data->ctl_power_data_2g[idx].ctl_edges[i].t_power; } else { twice_max_edge_power = p_eep_data->ctl_power_data_5g[idx].ctl_edges[i].t_power; } break; } else if ((i > 0) && (freq < fbin2freq(ctl_freqbin[i], is_2ghz))) { if (is_2ghz) { if (fbin2freq(ctl_freqbin[i - 1], 1) < freq && p_eep_data->ctl_power_data_2g[idx].ctl_edges[i - 1].flag) { twice_max_edge_power = p_eep_data->ctl_power_data_2g[idx]. ctl_edges[i - 1].t_power; } } else { if (fbin2freq(ctl_freqbin[i - 1], 0) < freq && p_eep_data->ctl_power_data_5g[idx].ctl_edges[i - 1].flag) { twice_max_edge_power = p_eep_data->ctl_power_data_5g[idx]. ctl_edges[i - 1].t_power; } } /* * Leave loop - no more affecting edges possible * in this monotonic increasing list */ break; } } /* * EV89475: EEPROM might contain 0 txpower in CTL table for certain * 2.4GHz channels. We workaround it by overwriting 60 (30 dBm) here. */ if (is_2ghz && (twice_max_edge_power == 0)) { twice_max_edge_power = 60; } HALASSERT(twice_max_edge_power > 0); return twice_max_edge_power; } HAL_BOOL ar9300_eeprom_set_power_per_rate_table( struct ath_hal *ah, ar9300_eeprom_t *p_eep_data, const struct ieee80211_channel *chan, u_int8_t *p_pwr_array, u_int16_t cfg_ctl, u_int16_t antenna_reduction, u_int16_t twice_max_regulatory_power, u_int16_t power_limit, u_int8_t chainmask) { /* Local defines to distinguish between extension and control CTL's */ #define EXT_ADDITIVE (0x8000) #define CTL_11A_EXT (CTL_11A | EXT_ADDITIVE) #define CTL_11G_EXT (CTL_11G | EXT_ADDITIVE) #define CTL_11B_EXT (CTL_11B | EXT_ADDITIVE) #define REDUCE_SCALED_POWER_BY_TWO_CHAIN 6 /* 10*log10(2)*2 */ #define REDUCE_SCALED_POWER_BY_THREE_CHAIN 10 /* 10*log10(3)*2 */ #define PWRINCR_3_TO_1_CHAIN 9 /* 10*log(3)*2 */ #define PWRINCR_3_TO_2_CHAIN 3 /* floor(10*log(3/2)*2) */ #define PWRINCR_2_TO_1_CHAIN 6 /* 10*log(2)*2 */ static const u_int16_t tp_scale_reduction_table[5] = { 0, 3, 6, 9, AR9300_MAX_RATE_POWER }; int i; int16_t twice_largest_antenna; u_int16_t twice_antenna_reduction = 2*antenna_reduction ; int16_t scaled_power = 0, min_ctl_power, max_reg_allowed_power; #define SUB_NUM_CTL_MODES_AT_5G_40 2 /* excluding HT40, EXT-OFDM */ #define SUB_NUM_CTL_MODES_AT_2G_40 3 /* excluding HT40, EXT-OFDM, EXT-CCK */ u_int16_t ctl_modes_for11a[] = {CTL_11A, CTL_5GHT20, CTL_11A_EXT, CTL_5GHT40}; u_int16_t ctl_modes_for11g[] = {CTL_11B, CTL_11G, CTL_2GHT20, CTL_11B_EXT, CTL_11G_EXT, CTL_2GHT40}; u_int16_t num_ctl_modes, *p_ctl_mode, ctl_mode, freq; CHAN_CENTERS centers; int tx_chainmask; struct ath_hal_9300 *ahp = AH9300(ah); u_int8_t *ctl_index; u_int8_t ctl_num; u_int16_t twice_min_edge_power; u_int16_t twice_max_edge_power = AR9300_MAX_RATE_POWER; #ifdef AH_DEBUG HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); #endif - tx_chainmask = chainmask ? chainmask : ahp->ah_tx_chainmask; + if (chainmask) + tx_chainmask = chainmask; + else + tx_chainmask = ahp->ah_tx_chainmaskopt ? + ahp->ah_tx_chainmaskopt :ahp->ah_tx_chainmask; ar9300_get_channel_centers(ah, chan, ¢ers); +#if 1 if (IEEE80211_IS_CHAN_2GHZ(chan)) { ahp->twice_antenna_gain = p_eep_data->modal_header_2g.antenna_gain; } else { ahp->twice_antenna_gain = p_eep_data->modal_header_5g.antenna_gain; } +#else + if (IEEE80211_IS_CHAN_2GHZ(chan)) { + ahp->twice_antenna_gain = AH_MAX(p_eep_data->modal_header_2g.antenna_gain, + AH_PRIVATE(ah)->ah_antenna_gain_2g); + } else { + ahp->twice_antenna_gain = AH_MAX(p_eep_data->modal_header_5g.antenna_gain, + AH_PRIVATE(ah)->ah_antenna_gain_5g); + } +#endif + /* Save max allowed antenna gain to ease future lookups */ ahp->twice_antenna_reduction = twice_antenna_reduction; /* Deduct antenna gain from EIRP to get the upper limit */ twice_largest_antenna = (int16_t)AH_MIN((twice_antenna_reduction - ahp->twice_antenna_gain), 0); max_reg_allowed_power = twice_max_regulatory_power + twice_largest_antenna; /* Use ah_tp_scale - see bug 30070. */ if (AH_PRIVATE(ah)->ah_tpScale != HAL_TP_SCALE_MAX) { max_reg_allowed_power -= (tp_scale_reduction_table[(AH_PRIVATE(ah)->ah_tpScale)] * 2); } scaled_power = AH_MIN(power_limit, max_reg_allowed_power); /* * Reduce scaled Power by number of chains active to get to * per chain tx power level */ /* TODO: better value than these? */ switch (ar9300_get_ntxchains(tx_chainmask)) { case 1: ahp->upper_limit[0] = AH_MAX(0, scaled_power); break; case 2: scaled_power -= REDUCE_SCALED_POWER_BY_TWO_CHAIN; ahp->upper_limit[1] = AH_MAX(0, scaled_power); break; case 3: scaled_power -= REDUCE_SCALED_POWER_BY_THREE_CHAIN; ahp->upper_limit[2] = AH_MAX(0, scaled_power); break; default: HALASSERT(0); /* Unsupported number of chains */ } scaled_power = AH_MAX(0, scaled_power); /* Get target powers from EEPROM - our baseline for TX Power */ if (IEEE80211_IS_CHAN_2GHZ(chan)) { /* Setup for CTL modes */ /* CTL_11B, CTL_11G, CTL_2GHT20 */ num_ctl_modes = ARRAY_LENGTH(ctl_modes_for11g) - SUB_NUM_CTL_MODES_AT_2G_40; p_ctl_mode = ctl_modes_for11g; if (IEEE80211_IS_CHAN_HT40(chan)) { num_ctl_modes = ARRAY_LENGTH(ctl_modes_for11g); /* All 2G CTL's */ } } else { /* Setup for CTL modes */ /* CTL_11A, CTL_5GHT20 */ num_ctl_modes = ARRAY_LENGTH(ctl_modes_for11a) - SUB_NUM_CTL_MODES_AT_5G_40; p_ctl_mode = ctl_modes_for11a; if (IEEE80211_IS_CHAN_HT40(chan)) { num_ctl_modes = ARRAY_LENGTH(ctl_modes_for11a); /* All 5G CTL's */ } } /* * For MIMO, need to apply regulatory caps individually across dynamically * running modes: CCK, OFDM, HT20, HT40 * * The outer loop walks through each possible applicable runtime mode. * The inner loop walks through each ctl_index entry in EEPROM. * The ctl value is encoded as [7:4] == test group, [3:0] == test mode. * */ for (ctl_mode = 0; ctl_mode < num_ctl_modes; ctl_mode++) { HAL_BOOL is_ht40_ctl_mode = (p_ctl_mode[ctl_mode] == CTL_5GHT40) || (p_ctl_mode[ctl_mode] == CTL_2GHT40); if (is_ht40_ctl_mode) { freq = centers.synth_center; } else if (p_ctl_mode[ctl_mode] & EXT_ADDITIVE) { freq = centers.ext_center; } else { freq = centers.ctl_center; } HALDEBUG(ah, HAL_DEBUG_POWER_MGMT, "LOOP-Mode ctl_mode %d < %d, " "is_ht40_ctl_mode %d, EXT_ADDITIVE %d\n", ctl_mode, num_ctl_modes, is_ht40_ctl_mode, (p_ctl_mode[ctl_mode] & EXT_ADDITIVE)); /* walk through each CTL index stored in EEPROM */ if (IEEE80211_IS_CHAN_2GHZ(chan)) { ctl_index = p_eep_data->ctl_index_2g; ctl_num = OSPREY_NUM_CTLS_2G; } else { ctl_index = p_eep_data->ctl_index_5g; ctl_num = OSPREY_NUM_CTLS_5G; } for (i = 0; (i < ctl_num) && ctl_index[i]; i++) { HALDEBUG(ah, HAL_DEBUG_POWER_MGMT, " LOOP-Ctlidx %d: cfg_ctl 0x%2.2x p_ctl_mode 0x%2.2x " "ctl_index 0x%2.2x chan %d chanctl 0x%x\n", i, cfg_ctl, p_ctl_mode[ctl_mode], ctl_index[i], ichan->channel, ath_hal_getctl(ah, chan)); /* * compare test group from regulatory channel list * with test mode from p_ctl_mode list */ if ((((cfg_ctl & ~CTL_MODE_M) | (p_ctl_mode[ctl_mode] & CTL_MODE_M)) == ctl_index[i]) || (((cfg_ctl & ~CTL_MODE_M) | (p_ctl_mode[ctl_mode] & CTL_MODE_M)) == ((ctl_index[i] & CTL_MODE_M) | SD_NO_CTL))) { twice_min_edge_power = ar9300_eep_def_get_max_edge_power( p_eep_data, freq, i, IEEE80211_IS_CHAN_2GHZ(chan)); HALDEBUG(ah, HAL_DEBUG_POWER_MGMT, " MATCH-EE_IDX %d: ch %d is2 %d " "2xMinEdge %d chainmask %d chains %d\n", i, freq, IEEE80211_IS_CHAN_2GHZ(chan), twice_min_edge_power, tx_chainmask, ar9300_get_ntxchains(tx_chainmask)); if ((cfg_ctl & ~CTL_MODE_M) == SD_NO_CTL) { /* * Find the minimum of all CTL edge powers * that apply to this channel */ twice_max_edge_power = AH_MIN(twice_max_edge_power, twice_min_edge_power); } else { /* specific */ twice_max_edge_power = twice_min_edge_power; break; } } } min_ctl_power = (u_int8_t)AH_MIN(twice_max_edge_power, scaled_power); HALDEBUG(ah, HAL_DEBUG_POWER_MGMT, " SEL-Min ctl_mode %d p_ctl_mode %d " "2xMaxEdge %d sP %d min_ctl_pwr %d\n", ctl_mode, p_ctl_mode[ctl_mode], twice_max_edge_power, scaled_power, min_ctl_power); /* Apply ctl mode to correct target power set */ switch (p_ctl_mode[ctl_mode]) { case CTL_11B: for (i = ALL_TARGET_LEGACY_1L_5L; i <= ALL_TARGET_LEGACY_11S; i++) { p_pwr_array[i] = (u_int8_t)AH_MIN(p_pwr_array[i], min_ctl_power); } break; case CTL_11A: case CTL_11G: for (i = ALL_TARGET_LEGACY_6_24; i <= ALL_TARGET_LEGACY_54; i++) { p_pwr_array[i] = (u_int8_t)AH_MIN(p_pwr_array[i], min_ctl_power); #ifdef ATH_BT_COEX if ((ahp->ah_bt_coex_config_type == HAL_BT_COEX_CFG_3WIRE) || (ahp->ah_bt_coex_config_type == HAL_BT_COEX_CFG_MCI)) { if ((ahp->ah_bt_coex_flag & HAL_BT_COEX_FLAG_LOWER_TX_PWR) && (ahp->ah_bt_wlan_isolation < HAL_BT_COEX_ISOLATION_FOR_NO_COEX)) { u_int8_t reduce_pow; reduce_pow = (HAL_BT_COEX_ISOLATION_FOR_NO_COEX - ahp->ah_bt_wlan_isolation) << 1; if (reduce_pow <= p_pwr_array[i]) { p_pwr_array[i] -= reduce_pow; } } if ((ahp->ah_bt_coex_flag & HAL_BT_COEX_FLAG_LOW_ACK_PWR) && (i != ALL_TARGET_LEGACY_36) && (i != ALL_TARGET_LEGACY_48) && (i != ALL_TARGET_LEGACY_54) && (p_ctl_mode[ctl_mode] == CTL_11G)) { p_pwr_array[i] = 0; } } #endif } break; case CTL_5GHT20: case CTL_2GHT20: for (i = ALL_TARGET_HT20_0_8_16; i <= ALL_TARGET_HT20_23; i++) { p_pwr_array[i] = (u_int8_t)AH_MIN(p_pwr_array[i], min_ctl_power); #ifdef ATH_BT_COEX if (((ahp->ah_bt_coex_config_type == HAL_BT_COEX_CFG_3WIRE) || (ahp->ah_bt_coex_config_type == HAL_BT_COEX_CFG_MCI)) && (ahp->ah_bt_coex_flag & HAL_BT_COEX_FLAG_LOWER_TX_PWR) && (ahp->ah_bt_wlan_isolation < HAL_BT_COEX_ISOLATION_FOR_NO_COEX)) { u_int8_t reduce_pow = (HAL_BT_COEX_ISOLATION_FOR_NO_COEX - ahp->ah_bt_wlan_isolation) << 1; if (reduce_pow <= p_pwr_array[i]) { p_pwr_array[i] -= reduce_pow; } } #if ATH_SUPPORT_MCI else if ((ahp->ah_bt_coex_flag & HAL_BT_COEX_FLAG_MCI_MAX_TX_PWR) && (p_ctl_mode[ctl_mode] == CTL_2GHT20) && (ahp->ah_bt_coex_config_type == HAL_BT_COEX_CFG_MCI)) { u_int8_t max_pwr; max_pwr = MS(mci_concur_tx_max_pwr[2][1], ATH_MCI_CONCUR_TX_LOWEST_PWR_MASK); if (p_pwr_array[i] > max_pwr) { p_pwr_array[i] = max_pwr; } } #endif #endif } break; case CTL_11B_EXT: #ifdef NOT_YET target_power_cck_ext.t_pow2x[0] = (u_int8_t) AH_MIN(target_power_cck_ext.t_pow2x[0], min_ctl_power); #endif /* NOT_YET */ break; case CTL_11A_EXT: case CTL_11G_EXT: #ifdef NOT_YET target_power_ofdm_ext.t_pow2x[0] = (u_int8_t) AH_MIN(target_power_ofdm_ext.t_pow2x[0], min_ctl_power); #endif /* NOT_YET */ break; case CTL_5GHT40: case CTL_2GHT40: for (i = ALL_TARGET_HT40_0_8_16; i <= ALL_TARGET_HT40_23; i++) { p_pwr_array[i] = (u_int8_t) AH_MIN(p_pwr_array[i], min_ctl_power); #ifdef ATH_BT_COEX if (((ahp->ah_bt_coex_config_type == HAL_BT_COEX_CFG_3WIRE) || (ahp->ah_bt_coex_config_type == HAL_BT_COEX_CFG_MCI)) && (ahp->ah_bt_coex_flag & HAL_BT_COEX_FLAG_LOWER_TX_PWR) && (ahp->ah_bt_wlan_isolation < HAL_BT_COEX_ISOLATION_FOR_NO_COEX)) { u_int8_t reduce_pow = (HAL_BT_COEX_ISOLATION_FOR_NO_COEX - ahp->ah_bt_wlan_isolation) << 1; if (reduce_pow <= p_pwr_array[i]) { p_pwr_array[i] -= reduce_pow; } } #if ATH_SUPPORT_MCI else if ((ahp->ah_bt_coex_flag & HAL_BT_COEX_FLAG_MCI_MAX_TX_PWR) && (p_ctl_mode[ctl_mode] == CTL_2GHT40) && (ahp->ah_bt_coex_config_type == HAL_BT_COEX_CFG_MCI)) { u_int8_t max_pwr; max_pwr = MS(mci_concur_tx_max_pwr[3][1], ATH_MCI_CONCUR_TX_LOWEST_PWR_MASK); if (p_pwr_array[i] > max_pwr) { p_pwr_array[i] = max_pwr; } } #endif #endif } break; default: HALASSERT(0); break; } } /* end ctl mode checking */ return AH_TRUE; #undef EXT_ADDITIVE #undef CTL_11A_EXT #undef CTL_11G_EXT #undef CTL_11B_EXT #undef REDUCE_SCALED_POWER_BY_TWO_CHAIN #undef REDUCE_SCALED_POWER_BY_THREE_CHAIN } /************************************************************** * ar9300_eeprom_set_transmit_power * * Set the transmit power in the baseband for the given * operating channel and mode. */ HAL_STATUS ar9300_eeprom_set_transmit_power(struct ath_hal *ah, ar9300_eeprom_t *p_eep_data, const struct ieee80211_channel *chan, u_int16_t cfg_ctl, u_int16_t antenna_reduction, u_int16_t twice_max_regulatory_power, u_int16_t power_limit) { #define ABS(_x, _y) ((int)_x > (int)_y ? (int)_x - (int)_y : (int)_y - (int)_x) #define INCREASE_MAXPOW_BY_TWO_CHAIN 6 /* 10*log10(2)*2 */ #define INCREASE_MAXPOW_BY_THREE_CHAIN 10 /* 10*log10(3)*2 */ u_int8_t target_power_val_t2[ar9300_rate_size]; u_int8_t target_power_val_t2_eep[ar9300_rate_size]; int16_t twice_array_gain = 0, max_power_level = 0; struct ath_hal_9300 *ahp = AH9300(ah); int i = 0; u_int32_t tmp_paprd_rate_mask = 0, *tmp_ptr = NULL; int paprd_scale_factor = 5; HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); u_int8_t *ptr_mcs_rate2power_table_index; u_int8_t mcs_rate2power_table_index_ht20[24] = { 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 }; u_int8_t mcs_rate2power_table_index_ht40[24] = { 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, }; HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s[%d] +++chan %d,cfgctl 0x%04x " "antenna_reduction 0x%04x, twice_max_regulatory_power 0x%04x " "power_limit 0x%04x\n", __func__, __LINE__, ichan->channel, cfg_ctl, antenna_reduction, twice_max_regulatory_power, power_limit); ar9300_set_target_power_from_eeprom(ah, ichan->channel, target_power_val_t2); if (ar9300_eeprom_get(ahp, EEP_PAPRD_ENABLED)) { if (IEEE80211_IS_CHAN_2GHZ(chan)) { if (IEEE80211_IS_CHAN_HT40(chan)) { tmp_paprd_rate_mask = p_eep_data->modal_header_2g.paprd_rate_mask_ht40; tmp_ptr = &AH9300(ah)->ah_2g_paprd_rate_mask_ht40; } else { tmp_paprd_rate_mask = p_eep_data->modal_header_2g.paprd_rate_mask_ht20; tmp_ptr = &AH9300(ah)->ah_2g_paprd_rate_mask_ht20; } } else { if (IEEE80211_IS_CHAN_HT40(chan)) { tmp_paprd_rate_mask = p_eep_data->modal_header_5g.paprd_rate_mask_ht40; tmp_ptr = &AH9300(ah)->ah_5g_paprd_rate_mask_ht40; } else { tmp_paprd_rate_mask = p_eep_data->modal_header_5g.paprd_rate_mask_ht20; tmp_ptr = &AH9300(ah)->ah_5g_paprd_rate_mask_ht20; } } AH_PAPRD_GET_SCALE_FACTOR( paprd_scale_factor, p_eep_data, IEEE80211_IS_CHAN_2GHZ(chan), ichan->channel); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s[%d] paprd_scale_factor %d\n", __func__, __LINE__, paprd_scale_factor); /* PAPRD is not done yet, Scale down the EEP power */ if (IEEE80211_IS_CHAN_HT40(chan)) { ptr_mcs_rate2power_table_index = &mcs_rate2power_table_index_ht40[0]; } else { ptr_mcs_rate2power_table_index = &mcs_rate2power_table_index_ht20[0]; } if (! ichan->paprd_table_write_done) { for (i = 0; i < 24; i++) { /* PAPRD is done yet, so Scale down Power for PAPRD Rates*/ if (tmp_paprd_rate_mask & (1 << i)) { target_power_val_t2[ptr_mcs_rate2power_table_index[i]] -= paprd_scale_factor; HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s[%d]: Chan %d " "Scale down target_power_val_t2[%d] = 0x%04x\n", __func__, __LINE__, ichan->channel, i, target_power_val_t2[i]); } } } else { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s[%d]: PAPRD Done No TGT PWR Scaling\n", __func__, __LINE__); } } /* Save the Target power for future use */ OS_MEMCPY(target_power_val_t2_eep, target_power_val_t2, sizeof(target_power_val_t2)); ar9300_eeprom_set_power_per_rate_table(ah, p_eep_data, chan, target_power_val_t2, cfg_ctl, antenna_reduction, twice_max_regulatory_power, power_limit, 0); /* Save this for quick lookup */ ahp->reg_dmn = ath_hal_getctl(ah, chan); /* * Always use CDD/direct per rate power table for register based approach. * For FCC, CDD calculations should factor in the array gain, hence * this adjust call. ETSI and MKK does not have this requirement. */ if (is_reg_dmn_fcc(ahp->reg_dmn)) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: FCC regdomain, calling reg_txpower_cdd\n", __func__); ar9300_adjust_reg_txpower_cdd(ah, target_power_val_t2); } if (ar9300_eeprom_get(ahp, EEP_PAPRD_ENABLED)) { for (i = 0; i < ar9300_rate_size; i++) { /* * EEPROM TGT PWR is not same as current TGT PWR, * so Disable PAPRD for this rate. * Some of APs might ask to reduce Target Power, * if target power drops significantly, * disable PAPRD for that rate. */ if (tmp_paprd_rate_mask & (1 << i)) { if (ABS(target_power_val_t2_eep[i], target_power_val_t2[i]) > paprd_scale_factor) { tmp_paprd_rate_mask &= ~(1 << i); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: EEP TPC[%02d] 0x%08x " "Curr TPC[%02d] 0x%08x mask = 0x%08x\n", __func__, i, target_power_val_t2_eep[i], i, target_power_val_t2[i], tmp_paprd_rate_mask); } } } HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Chan %d After tmp_paprd_rate_mask = 0x%08x\n", __func__, ichan->channel, tmp_paprd_rate_mask); if (tmp_ptr) { *tmp_ptr = tmp_paprd_rate_mask; } } /* Write target power array to registers */ ar9300_transmit_power_reg_write(ah, target_power_val_t2); /* Write target power for self generated frames to the TPC register */ ar9300_selfgen_tpc_reg_write(ah, chan, target_power_val_t2); /* GreenTx or Paprd */ if (ah->ah_config.ath_hal_sta_update_tx_pwr_enable || AH_PRIVATE(ah)->ah_caps.halPaprdEnabled) { if (AR_SREV_POSEIDON(ah)) { /*For HAL_RSSI_TX_POWER_NONE array*/ OS_MEMCPY(ahp->ah_default_tx_power, target_power_val_t2, sizeof(target_power_val_t2)); /* Get defautl tx related register setting for GreenTx */ /* Record OB/DB */ ahp->ah_ob_db1[POSEIDON_STORED_REG_OBDB] = OS_REG_READ(ah, AR_PHY_65NM_CH0_TXRF2); /* Record TPC settting */ ahp->ah_ob_db1[POSEIDON_STORED_REG_TPC] = OS_REG_READ(ah, AR_TPC); /* Record BB_powertx_rate9 setting */ ahp->ah_ob_db1[POSEIDON_STORED_REG_BB_PWRTX_RATE9] = OS_REG_READ(ah, AR_PHY_BB_POWERTX_RATE9); } } /* * Return tx power used to iwconfig. * Since power is rate dependent, use one of the indices from the * AR9300_Rates enum to select an entry from target_power_val_t2[] * to report. * Currently returns the power for HT40 MCS 0, HT20 MCS 0, or OFDM 6 Mbps * as CCK power is less interesting (?). */ i = ALL_TARGET_LEGACY_6_24; /* legacy */ if (IEEE80211_IS_CHAN_HT40(chan)) { i = ALL_TARGET_HT40_0_8_16; /* ht40 */ } else if (IEEE80211_IS_CHAN_HT20(chan)) { i = ALL_TARGET_HT20_0_8_16; /* ht20 */ } max_power_level = target_power_val_t2[i]; /* Adjusting the ah_max_power_level based on chains and antennaGain*/ - switch (ar9300_get_ntxchains(ahp->ah_tx_chainmask)) + switch (ar9300_get_ntxchains(((ahp->ah_tx_chainmaskopt > 0) ? + ahp->ah_tx_chainmaskopt : ahp->ah_tx_chainmask))) { case 1: break; case 2: twice_array_gain = (ahp->twice_antenna_gain >= ahp->twice_antenna_reduction)? 0: ((int16_t)AH_MIN((ahp->twice_antenna_reduction - (ahp->twice_antenna_gain + INCREASE_MAXPOW_BY_TWO_CHAIN)), 0)); /* Adjusting maxpower with antennaGain */ max_power_level -= twice_array_gain; /* Adjusting maxpower based on chain */ max_power_level += INCREASE_MAXPOW_BY_TWO_CHAIN; break; case 3: twice_array_gain = (ahp->twice_antenna_gain >= ahp->twice_antenna_reduction)? 0: ((int16_t)AH_MIN((ahp->twice_antenna_reduction - (ahp->twice_antenna_gain + INCREASE_MAXPOW_BY_THREE_CHAIN)), 0)); /* Adjusting maxpower with antennaGain */ max_power_level -= twice_array_gain; /* Adjusting maxpower based on chain */ max_power_level += INCREASE_MAXPOW_BY_THREE_CHAIN; break; default: HALASSERT(0); /* Unsupported number of chains */ } AH_PRIVATE(ah)->ah_maxPowerLevel = (int8_t)max_power_level; ar9300_calibration_apply(ah, ichan->channel); #undef ABS /* Handle per packet TPC initializations */ if (ah->ah_config.ath_hal_desc_tpc) { /* Transmit Power per-rate per-chain are computed here. A separate * power table is maintained for different MIMO modes (i.e. TXBF ON, * STBC) to enable easy lookup during packet transmit. * The reason for maintaing each of these tables per chain is that * the transmit power used for different number of chains is different * depending on whether the power has been limited by the target power, * the regulatory domain or the CTL limits. */ u_int mode = ath_hal_get_curmode(ah, chan); u_int32_t val = 0; u_int8_t chainmasks[AR9300_MAX_CHAINS] = {OSPREY_1_CHAINMASK, OSPREY_2LOHI_CHAINMASK, OSPREY_3_CHAINMASK}; for (i = 0; i < AR9300_MAX_CHAINS; i++) { OS_MEMCPY(target_power_val_t2, target_power_val_t2_eep, sizeof(target_power_val_t2_eep)); ar9300_eeprom_set_power_per_rate_table(ah, p_eep_data, chan, target_power_val_t2, cfg_ctl, antenna_reduction, twice_max_regulatory_power, power_limit, chainmasks[i]); HALDEBUG(ah, HAL_DEBUG_POWER_MGMT, " Channel = %d Chainmask = %d, Upper Limit = [%2d.%1d dBm]\n", ichan->channel, i, ahp->upper_limit[i]/2, ahp->upper_limit[i]%2 * 5); ar9300_init_rate_txpower(ah, mode, chan, target_power_val_t2, chainmasks[i]); } /* Enable TPC */ OS_REG_WRITE(ah, AR_PHY_PWRTX_MAX, AR_PHY_PWRTX_MAX_TPC_ENABLE); /* * Disable per chain power reduction since we are already * accounting for this in our calculations */ val = OS_REG_READ(ah, AR_PHY_POWER_TX_SUB); if (AR_SREV_WASP(ah)) { OS_REG_WRITE(ah, AR_PHY_POWER_TX_SUB, val & AR_PHY_POWER_TX_SUB_2_DISABLE); } else { OS_REG_WRITE(ah, AR_PHY_POWER_TX_SUB, val & AR_PHY_POWER_TX_SUB_3_DISABLE); } } return HAL_OK; } /************************************************************** * ar9300_eeprom_set_addac * * Set the ADDAC from eeprom. */ void ar9300_eeprom_set_addac(struct ath_hal *ah, struct ieee80211_channel *chan) { HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE, "FIXME: ar9300_eeprom_def_set_addac called\n"); #if 0 MODAL_EEPDEF_HEADER *p_modal; struct ath_hal_9300 *ahp = AH9300(ah); ar9300_eeprom_t *eep = &ahp->ah_eeprom.def; u_int8_t biaslevel; if (AH_PRIVATE(ah)->ah_macVersion != AR_SREV_VERSION_SOWL) { return; } HALASSERT(owl_get_eepdef_ver(ahp) == AR9300_EEP_VER); /* Xpa bias levels in eeprom are valid from rev 14.7 */ if (owl_get_eepdef_rev(ahp) < AR9300_EEP_MINOR_VER_7) { return; } if (ahp->ah_emu_eeprom) { return; } p_modal = &(eep->modal_header[IEEE80211_IS_CHAN_2GHZ(chan)]); if (p_modal->xpa_bias_lvl != 0xff) { biaslevel = p_modal->xpa_bias_lvl; } else { /* Use freqeuncy specific xpa bias level */ u_int16_t reset_freq_bin, freq_bin, freq_count = 0; CHAN_CENTERS centers; ar9300_get_channel_centers(ah, chan, ¢ers); reset_freq_bin = FREQ2FBIN(centers.synth_center, IEEE80211_IS_CHAN_2GHZ(chan)); freq_bin = p_modal->xpa_bias_lvl_freq[0] & 0xff; biaslevel = (u_int8_t)(p_modal->xpa_bias_lvl_freq[0] >> 14); freq_count++; while (freq_count < 3) { if (p_modal->xpa_bias_lvl_freq[freq_count] == 0x0) { break; } freq_bin = p_modal->xpa_bias_lvl_freq[freq_count] & 0xff; if (reset_freq_bin >= freq_bin) { biaslevel = (u_int8_t)(p_modal->xpa_bias_lvl_freq[freq_count] >> 14); } else { break; } freq_count++; } } /* Apply bias level to the ADDAC values in the INI array */ if (IEEE80211_IS_CHAN_2GHZ(chan)) { INI_RA(&ahp->ah_ini_addac, 7, 1) = (INI_RA(&ahp->ah_ini_addac, 7, 1) & (~0x18)) | biaslevel << 3; } else { INI_RA(&ahp->ah_ini_addac, 6, 1) = (INI_RA(&ahp->ah_ini_addac, 6, 1) & (~0xc0)) | biaslevel << 6; } #endif } u_int ar9300_eeprom_dump_support(struct ath_hal *ah, void **pp_e) { *pp_e = &(AH9300(ah)->ah_eeprom); return sizeof(ar9300_eeprom_t); } u_int8_t ar9300_eeprom_get_num_ant_config(struct ath_hal_9300 *ahp, HAL_FREQ_BAND freq_band) { #if 0 ar9300_eeprom_t *eep = &ahp->ah_eeprom.def; MODAL_EEPDEF_HEADER *p_modal = &(eep->modal_header[HAL_FREQ_BAND_2GHZ == freq_band]); BASE_EEPDEF_HEADER *p_base = &eep->base_eep_header; u_int8_t num_ant_config; num_ant_config = 1; /* default antenna configuration */ if (p_base->version >= 0x0E0D) { if (p_modal->use_ant1) { num_ant_config += 1; } } return num_ant_config; #else return 1; #endif } HAL_STATUS ar9300_eeprom_get_ant_cfg(struct ath_hal_9300 *ahp, const struct ieee80211_channel *chan, u_int8_t index, u_int16_t *config) { #if 0 ar9300_eeprom_t *eep = &ahp->ah_eeprom.def; MODAL_EEPDEF_HEADER *p_modal = &(eep->modal_header[IEEE80211_IS_CHAN_2GHZ(chan)]); BASE_EEPDEF_HEADER *p_base = &eep->base_eep_header; switch (index) { case 0: *config = p_modal->ant_ctrl_common & 0xFFFF; return HAL_OK; case 1: if (p_base->version >= 0x0E0D) { if (p_modal->use_ant1) { *config = ((p_modal->ant_ctrl_common & 0xFFFF0000) >> 16); return HAL_OK; } } break; default: break; } #endif return HAL_EINVAL; } u_int8_t* ar9300_eeprom_get_cust_data(struct ath_hal_9300 *ahp) { return (u_int8_t *)ahp; } #ifdef UNUSED static inline HAL_STATUS ar9300_check_eeprom(struct ath_hal *ah) { #if 0 u_int32_t sum = 0, el; u_int16_t *eepdata; int i; struct ath_hal_9300 *ahp = AH9300(ah); HAL_BOOL need_swap = AH_FALSE; ar9300_eeprom_t *eep = (ar9300_eeprom_t *)&ahp->ah_eeprom.def; u_int16_t magic, magic2; int addr; u_int16_t temp; /* ** We need to check the EEPROM data regardless of if it's in flash or ** in EEPROM. */ if (!ahp->ah_priv.priv.ah_eeprom_read( ah, AR9300_EEPROM_MAGIC_OFFSET, &magic)) { HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: Reading Magic # failed\n", __func__); return AH_FALSE; } HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: Read Magic = 0x%04X\n", __func__, magic); if (!ar9300_eep_data_in_flash(ah)) { if (magic != AR9300_EEPROM_MAGIC) { magic2 = SWAP16(magic); if (magic2 == AR9300_EEPROM_MAGIC) { need_swap = AH_TRUE; eepdata = (u_int16_t *)(&ahp->ah_eeprom); for (addr = 0; addr < sizeof(ar9300_eeprom_t) / sizeof(u_int16_t); addr++) { temp = SWAP16(*eepdata); *eepdata = temp; eepdata++; HALDEBUG(ah, HAL_DEBUG_EEPROM_DUMP, "0x%04X ", *eepdata); if (((addr + 1) % 6) == 0) { HALDEBUG(ah, HAL_DEBUG_EEPROM_DUMP, "\n"); } } } else { HALDEBUG(ah, HAL_DEBUG_EEPROM, "Invalid EEPROM Magic. endianness missmatch.\n"); return HAL_EEBADSUM; } } } else { HALDEBUG(ah, HAL_DEBUG_EEPROM, "EEPROM being read from flash @0x%p\n", AH_PRIVATE(ah)->ah_st); } HALDEBUG(ah, HAL_DEBUG_EEPROM, "need_swap = %s.\n", need_swap?"True":"False"); if (need_swap) { el = SWAP16(ahp->ah_eeprom.def.base_eep_header.length); } else { el = ahp->ah_eeprom.def.base_eep_header.length; } eepdata = (u_int16_t *)(&ahp->ah_eeprom.def); for (i = 0; i < AH_MIN(el, sizeof(ar9300_eeprom_t)) / sizeof(u_int16_t); i++) { sum ^= *eepdata++; } if (need_swap) { /* * preddy: EEPROM endianness does not match. So change it * 8bit values in eeprom data structure does not need to be swapped * Only >8bits (16 & 32) values need to be swapped * If a new 16 or 32 bit field is added to the EEPROM contents, * please make sure to swap the field here */ u_int32_t integer, j; u_int16_t word; HALDEBUG(ah, HAL_DEBUG_EEPROM, "EEPROM Endianness is not native.. Changing \n"); /* convert Base Eep header */ word = SWAP16(eep->base_eep_header.length); eep->base_eep_header.length = word; word = SWAP16(eep->base_eep_header.checksum); eep->base_eep_header.checksum = word; word = SWAP16(eep->base_eep_header.version); eep->base_eep_header.version = word; word = SWAP16(eep->base_eep_header.reg_dmn[0]); eep->base_eep_header.reg_dmn[0] = word; word = SWAP16(eep->base_eep_header.reg_dmn[1]); eep->base_eep_header.reg_dmn[1] = word; word = SWAP16(eep->base_eep_header.rf_silent); eep->base_eep_header.rf_silent = word; word = SWAP16(eep->base_eep_header.blue_tooth_options); eep->base_eep_header.blue_tooth_options = word; word = SWAP16(eep->base_eep_header.device_cap); eep->base_eep_header.device_cap = word; /* convert Modal Eep header */ for (j = 0; j < ARRAY_LENGTH(eep->modal_header); j++) { MODAL_EEPDEF_HEADER *p_modal = &eep->modal_header[j]; integer = SWAP32(p_modal->ant_ctrl_common); p_modal->ant_ctrl_common = integer; for (i = 0; i < AR9300_MAX_CHAINS; i++) { integer = SWAP32(p_modal->ant_ctrl_chain[i]); p_modal->ant_ctrl_chain[i] = integer; } for (i = 0; i < AR9300_EEPROM_MODAL_SPURS; i++) { word = SWAP16(p_modal->spur_chans[i].spur_chan); p_modal->spur_chans[i].spur_chan = word; } } } /* Check CRC - Attach should fail on a bad checksum */ if (sum != 0xffff || owl_get_eepdef_ver(ahp) != AR9300_EEP_VER || owl_get_eepdef_rev(ahp) < AR9300_EEP_NO_BACK_VER) { HALDEBUG(ah, HAL_DEBUG_EEPROM, "Bad EEPROM checksum 0x%x or revision 0x%04x\n", sum, owl_get_eepdef_ver(ahp)); return HAL_EEBADSUM; } #ifdef EEPROM_DUMP ar9300_eeprom_def_dump(ah, eep); #endif #if 0 #ifdef AH_AR9300_OVRD_TGT_PWR /* * 14.4 EEPROM contains low target powers. * Hardcode until EEPROM > 14.4 */ if (owl_get_eepdef_ver(ahp) == 14 && owl_get_eepdef_rev(ahp) <= 4) { MODAL_EEPDEF_HEADER *p_modal; #ifdef EEPROM_DUMP HALDEBUG(ah, HAL_DEBUG_POWER_OVERRIDE, "Original Target Powers\n"); ar9300_eep_def_dump_tgt_power(ah, eep); #endif HALDEBUG(ah, HAL_DEBUG_POWER_OVERRIDE, "Override Target Powers. EEPROM Version is %d.%d, " "Device Type %d\n", owl_get_eepdef_ver(ahp), owl_get_eepdef_rev(ahp), eep->base_eep_header.device_type); ar9300_eep_def_override_tgt_power(ah, eep); if (eep->base_eep_header.device_type == 5) { /* for xb72 only: improve transmit EVM for interop */ p_modal = &eep->modal_header[1]; p_modal->tx_frame_to_data_start = 0x23; p_modal->tx_frame_to_xpa_on = 0x23; p_modal->tx_frame_to_pa_on = 0x23; } #ifdef EEPROM_DUMP HALDEBUG(ah, HAL_DEBUG_POWER_OVERRIDE, "Modified Target Powers\n"); ar9300_eep_def_dump_tgt_power(ah, eep); #endif } #endif /* AH_AR9300_OVRD_TGT_PWR */ #endif #endif return HAL_OK; } #endif static u_int16_t ar9300_eeprom_get_spur_chan(struct ath_hal *ah, int i, HAL_BOOL is_2ghz) { u_int16_t spur_val = AR_NO_SPUR; #if 0 struct ath_hal_9300 *ahp = AH9300(ah); ar9300_eeprom_t *eep = (ar9300_eeprom_t *)&ahp->ah_eeprom; HALASSERT(i < AR_EEPROM_MODAL_SPURS ); HALDEBUG(ah, HAL_DEBUG_ANI, "Getting spur idx %d is2Ghz. %d val %x\n", i, is_2ghz, AH_PRIVATE(ah)->ah_config.ath_hal_spur_chans[i][is_2ghz]); switch (AH_PRIVATE(ah)->ah_config.ath_hal_spur_mode) { case SPUR_DISABLE: /* returns AR_NO_SPUR */ break; case SPUR_ENABLE_IOCTL: spur_val = AH_PRIVATE(ah)->ah_config.ath_hal_spur_chans[i][is_2ghz]; HALDEBUG(ah, HAL_DEBUG_ANI, "Getting spur val from new loc. %d\n", spur_val); break; case SPUR_ENABLE_EEPROM: spur_val = eep->modal_header[is_2ghz].spur_chans[i].spur_chan; break; } #endif return spur_val; } #ifdef UNUSED static inline HAL_BOOL ar9300_fill_eeprom(struct ath_hal *ah) { return ar9300_eeprom_restore(ah); } #endif u_int16_t ar9300_eeprom_struct_size(void) { return sizeof(ar9300_eeprom_t); } int ar9300_eeprom_struct_default_many(void) { return ARRAY_LENGTH(default9300); } ar9300_eeprom_t * ar9300_eeprom_struct_default(int default_index) { if (default_index >= 0 && default_index < ARRAY_LENGTH(default9300)) { return default9300[default_index]; } else { return 0; } } ar9300_eeprom_t * ar9300_eeprom_struct_default_find_by_id(int id) { int it; for (it = 0; it < ARRAY_LENGTH(default9300); it++) { if (default9300[it] != 0 && default9300[it]->template_version == id) { return default9300[it]; } } return 0; } HAL_BOOL ar9300_calibration_data_read_flash(struct ath_hal *ah, long address, u_int8_t *buffer, int many) { if (((address) < 0) || ((address + many) > AR9300_EEPROM_SIZE - 1)) { return AH_FALSE; } return AH_FALSE; } HAL_BOOL ar9300_calibration_data_read_eeprom(struct ath_hal *ah, long address, u_int8_t *buffer, int many) { int i; u_int8_t value[2]; unsigned long eep_addr; unsigned long byte_addr; u_int16_t *svalue; if (((address) < 0) || ((address + many) > AR9300_EEPROM_SIZE)) { return AH_FALSE; } for (i = 0; i < many; i++) { eep_addr = (u_int16_t) (address + i) / 2; byte_addr = (u_int16_t) (address + i) % 2; svalue = (u_int16_t *) value; if (! ath_hal_eepromRead(ah, eep_addr, svalue)) { HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: Unable to read eeprom region \n", __func__); return AH_FALSE; } buffer[i] = (*svalue >> (8 * byte_addr)) & 0xff; } return AH_TRUE; } HAL_BOOL ar9300_calibration_data_read_otp(struct ath_hal *ah, long address, u_int8_t *buffer, int many, HAL_BOOL is_wifi) { int i; unsigned long eep_addr; unsigned long byte_addr; u_int32_t svalue; if (((address) < 0) || ((address + many) > 0x400)) { return AH_FALSE; } for (i = 0; i < many; i++) { eep_addr = (u_int16_t) (address + i) / 4; /* otp is 4 bytes long???? */ byte_addr = (u_int16_t) (address + i) % 4; if (!ar9300_otp_read(ah, eep_addr, &svalue, is_wifi)) { HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: Unable to read otp region \n", __func__); return AH_FALSE; } buffer[i] = (svalue >> (8 * byte_addr)) & 0xff; } return AH_TRUE; } #ifdef ATH_CAL_NAND_FLASH HAL_BOOL ar9300_calibration_data_read_nand(struct ath_hal *ah, long address, u_int8_t *buffer, int many) { int ret_len; int ret_val = 1; /* Calling OS based API to read NAND */ ret_val = OS_NAND_FLASH_READ(ATH_CAL_NAND_PARTITION, address, many, &ret_len, buffer); return (ret_val ? AH_FALSE: AH_TRUE); } #endif HAL_BOOL ar9300_calibration_data_read(struct ath_hal *ah, long address, u_int8_t *buffer, int many) { switch (AH9300(ah)->calibration_data_source) { case calibration_data_flash: return ar9300_calibration_data_read_flash(ah, address, buffer, many); case calibration_data_eeprom: return ar9300_calibration_data_read_eeprom(ah, address, buffer, many); case calibration_data_otp: return ar9300_calibration_data_read_otp(ah, address, buffer, many, 1); #ifdef ATH_CAL_NAND_FLASH case calibration_data_nand: return ar9300_calibration_data_read_nand(ah,address,buffer,many); #endif } return AH_FALSE; } HAL_BOOL ar9300_calibration_data_read_array(struct ath_hal *ah, int address, u_int8_t *buffer, int many) { int it; for (it = 0; it < many; it++) { (void)ar9300_calibration_data_read(ah, address - it, buffer + it, 1); } return AH_TRUE; } /* * the address where the first configuration block is written */ static const int base_address = 0x3ff; /* 1KB */ static const int base_address_512 = 0x1ff; /* 512Bytes */ /* * the address where the NAND first configuration block is written */ #ifdef ATH_CAL_NAND_FLASH static const int base_address_nand = AR9300_FLASH_CAL_START_OFFSET; #endif /* * the lower limit on configuration data */ static const int low_limit = 0x040; /* * returns size of the physical eeprom in bytes. * 1024 and 2048 are normal sizes. * 0 means there is no eeprom. */ int32_t ar9300_eeprom_size(struct ath_hal *ah) { u_int16_t data; /* * first we'll try for 4096 bytes eeprom */ if (ar9300_eeprom_read_word(ah, 2047, &data)) { if (data != 0) { return 4096; } } /* * then we'll try for 2048 bytes eeprom */ if (ar9300_eeprom_read_word(ah, 1023, &data)) { if (data != 0) { return 2048; } } /* * then we'll try for 1024 bytes eeprom */ if (ar9300_eeprom_read_word(ah, 511, &data)) { if (data != 0) { return 1024; } } return 0; } /* * returns size of the physical otp in bytes. * 1024 and 2048 are normal sizes. * 0 means there is no eeprom. */ int32_t ar9300_otp_size(struct ath_hal *ah) { if (AR_SREV_POSEIDON(ah) || AR_SREV_HORNET(ah)) { return base_address_512+1; } else { return base_address+1; } } /* * find top of memory */ int ar9300_eeprom_base_address(struct ath_hal *ah) { int size; if (AH9300(ah)->calibration_data_source == calibration_data_otp) { return ar9300_otp_size(ah)-1; } else { size = ar9300_eeprom_size(ah); if (size > 0) { return size - 1; } else { return ar9300_otp_size(ah)-1; } } } int ar9300_eeprom_volatile(struct ath_hal *ah) { if (AH9300(ah)->calibration_data_source == calibration_data_otp) { return 0; /* no eeprom, use otp */ } else { return 1; /* board has eeprom or flash */ } } /* * need to change this to look for the pcie data in the low parts of memory * cal data needs to stop a few locations above */ int ar9300_eeprom_low_limit(struct ath_hal *ah) { return low_limit; } u_int16_t ar9300_compression_checksum(u_int8_t *data, int dsize) { int it; int checksum = 0; for (it = 0; it < dsize; it++) { checksum += data[it]; checksum &= 0xffff; } return checksum; } int ar9300_compression_header_unpack(u_int8_t *best, int *code, int *reference, int *length, int *major, int *minor) { unsigned long value[4]; value[0] = best[0]; value[1] = best[1]; value[2] = best[2]; value[3] = best[3]; *code = ((value[0] >> 5) & 0x0007); *reference = (value[0] & 0x001f) | ((value[1] >> 2) & 0x0020); *length = ((value[1] << 4) & 0x07f0) | ((value[2] >> 4) & 0x000f); *major = (value[2] & 0x000f); *minor = (value[3] & 0x00ff); return 4; } static HAL_BOOL ar9300_uncompress_block(struct ath_hal *ah, u_int8_t *mptr, int mdata_size, u_int8_t *block, int size) { int it; int spot; int offset; int length; spot = 0; for (it = 0; it < size; it += (length + 2)) { offset = block[it]; offset &= 0xff; spot += offset; length = block[it + 1]; length &= 0xff; if (length > 0 && spot >= 0 && spot + length <= mdata_size) { HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: Restore at %d: spot=%d offset=%d length=%d\n", __func__, it, spot, offset, length); OS_MEMCPY(&mptr[spot], &block[it + 2], length); spot += length; } else if (length > 0) { HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: Bad restore at %d: spot=%d offset=%d length=%d\n", __func__, it, spot, offset, length); return AH_FALSE; } } return AH_TRUE; } static int ar9300_eeprom_restore_internal_address(struct ath_hal *ah, ar9300_eeprom_t *mptr, int mdata_size, int cptr, u_int8_t blank) { u_int8_t word[MOUTPUT]; ar9300_eeprom_t *dptr; /* was uint8 */ int code; int reference, length, major, minor; int osize; int it; int restored; u_int16_t checksum, mchecksum; restored = 0; for (it = 0; it < MSTATE; it++) { (void) ar9300_calibration_data_read_array( ah, cptr, word, compression_header_length); if (word[0] == blank && word[1] == blank && word[2] == blank && word[3] == blank) { break; } ar9300_compression_header_unpack( word, &code, &reference, &length, &major, &minor); HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: Found block at %x: " "code=%d ref=%d length=%d major=%d minor=%d\n", __func__, cptr, code, reference, length, major, minor); #ifdef DONTUSE if (length >= 1024) { HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: Skipping bad header\n", __func__); cptr -= compression_header_length; continue; } #endif osize = length; (void) ar9300_calibration_data_read_array( ah, cptr, word, compression_header_length + osize + compression_checksum_length); checksum = ar9300_compression_checksum( &word[compression_header_length], length); mchecksum = word[compression_header_length + osize] | (word[compression_header_length + osize + 1] << 8); HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: checksum %x %x\n", __func__, checksum, mchecksum); if (checksum == mchecksum) { switch (code) { case _compress_none: if (length != mdata_size) { HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: EEPROM structure size mismatch " "memory=%d eeprom=%d\n", __func__, mdata_size, length); return -1; } OS_MEMCPY((u_int8_t *)mptr, (u_int8_t *)(word + compression_header_length), length); HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: restored eeprom %d: uncompressed, length %d\n", __func__, it, length); restored = 1; break; #ifdef UNUSED case _compress_lzma: if (reference == reference_current) { dptr = mptr; } else { dptr = (u_int8_t *)ar9300_eeprom_struct_default_find_by_id( reference); if (dptr == 0) { HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: Can't find reference eeprom struct %d\n", __func__, reference); goto done; } } usize = -1; if (usize != mdata_size) { HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: uncompressed data is wrong size %d %d\n", __func__, usize, mdata_size); goto done; } for (ib = 0; ib < mdata_size; ib++) { mptr[ib] = dptr[ib] ^ word[ib + overhead]; } HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: restored eeprom %d: compressed, " "reference %d, length %d\n", __func__, it, reference, length); break; case _compress_pairs: if (reference == reference_current) { dptr = mptr; } else { dptr = (u_int8_t *)ar9300_eeprom_struct_default_find_by_id( reference); if (dptr == 0) { HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: Can't find the reference " "eeprom structure %d\n", __func__, reference); goto done; } } HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: restored eeprom %d: " "pairs, reference %d, length %d,\n", __func__, it, reference, length); break; #endif case _compress_block: if (reference == reference_current) { dptr = mptr; } else { dptr = ar9300_eeprom_struct_default_find_by_id(reference); if (dptr == 0) { HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: cant find reference eeprom struct %d\n", __func__, reference); break; } OS_MEMCPY(mptr, dptr, mdata_size); } HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: restore eeprom %d: block, reference %d, length %d\n", __func__, it, reference, length); (void) ar9300_uncompress_block(ah, (u_int8_t *) mptr, mdata_size, (u_int8_t *) (word + compression_header_length), length); restored = 1; break; default: HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: unknown compression code %d\n", __func__, code); break; } } else { HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: skipping block with bad checksum\n", __func__); } cptr -= compression_header_length + osize + compression_checksum_length; } if (!restored) { cptr = -1; } return cptr; } static int ar9300_eeprom_restore_from_dram(struct ath_hal *ah, ar9300_eeprom_t *mptr, int mdata_size) { struct ath_hal_9300 *ahp = AH9300(ah); #if !defined(USE_PLATFORM_FRAMEWORK) char *cal_ptr; #endif HALASSERT(mdata_size > 0); /* if cal_in_flash is AH_TRUE, the address sent by LMAC to HAL (i.e. ah->ah_st) is corresponding to Flash. so return from here if ar9300_eep_data_in_flash(ah) returns AH_TRUE */ if(ar9300_eep_data_in_flash(ah)) return -1; #if 0 /* check if LMAC sent DRAM address is valid */ if (!(uintptr_t)(AH_PRIVATE(ah)->ah_st)) { return -1; } #endif /* When calibration data is from host, Host will copy the compressed data to the predefined DRAM location saved at ah->ah_st */ #if 0 ath_hal_printf(ah, "Restoring Cal data from DRAM\n"); ahp->ah_cal_mem = OS_REMAP((uintptr_t)(AH_PRIVATE(ah)->ah_st), HOST_CALDATA_SIZE); #endif if (!ahp->ah_cal_mem) { HALDEBUG(ah, HAL_DEBUG_EEPROM,"%s: can't remap dram region\n", __func__); return -1; } #if !defined(USE_PLATFORM_FRAMEWORK) cal_ptr = &((char *)(ahp->ah_cal_mem))[AR9300_FLASH_CAL_START_OFFSET]; OS_MEMCPY(mptr, cal_ptr, mdata_size); #else OS_MEMCPY(mptr, ahp->ah_cal_mem, mdata_size); #endif if (mptr->eeprom_version == 0xff || mptr->template_version == 0xff || mptr->eeprom_version == 0 || mptr->template_version == 0) { /* The board is uncalibrated */ return -1; } if (mptr->eeprom_version != 0x2) { return -1; } return mdata_size; } static int ar9300_eeprom_restore_from_flash(struct ath_hal *ah, ar9300_eeprom_t *mptr, int mdata_size) { struct ath_hal_9300 *ahp = AH9300(ah); char *cal_ptr; HALASSERT(mdata_size > 0); if (!ahp->ah_cal_mem) { return -1; } ath_hal_printf(ah, "Restoring Cal data from Flash\n"); /* * When calibration data is saved in flash, read * uncompressed eeprom structure from flash and return */ cal_ptr = &((char *)(ahp->ah_cal_mem))[AR9300_FLASH_CAL_START_OFFSET]; OS_MEMCPY(mptr, cal_ptr, mdata_size); #if 0 ar9300_swap_eeprom((ar9300_eeprom_t *)mptr); DONE IN ar9300_restore() #endif if (mptr->eeprom_version == 0xff || mptr->template_version == 0xff || mptr->eeprom_version == 0 || mptr->template_version == 0) { /* The board is uncalibrated */ return -1; } if (mptr->eeprom_version != 0x2) { return -1; } return mdata_size; } /* * Read the configuration data from the storage. We try the order with: * EEPROM, Flash, OTP. If all of above failed, use the default template. * The data can be put in any specified memory buffer. * * Returns -1 on error. * Returns address of next memory location on success. */ int ar9300_eeprom_restore_internal(struct ath_hal *ah, ar9300_eeprom_t *mptr, int mdata_size) { int nptr; nptr = -1; if ((AH9300(ah)->calibration_data_try == calibration_data_none || AH9300(ah)->calibration_data_try == calibration_data_dram) && AH9300(ah)->try_dram && nptr < 0) { ath_hal_printf(ah, "Restoring Cal data from DRAM\n"); AH9300(ah)->calibration_data_source = calibration_data_dram; AH9300(ah)->calibration_data_source_address = 0; nptr = ar9300_eeprom_restore_from_dram(ah, mptr, mdata_size); if (nptr < 0) { AH9300(ah)->calibration_data_source = calibration_data_none; AH9300(ah)->calibration_data_source_address = 0; } } if ((AH9300(ah)->calibration_data_try == calibration_data_none || AH9300(ah)->calibration_data_try == calibration_data_eeprom) && AH9300(ah)->try_eeprom && nptr < 0) { /* * need to look at highest eeprom address as well as at * base_address=0x3ff where we used to write the data */ ath_hal_printf(ah, "Restoring Cal data from EEPROM\n"); AH9300(ah)->calibration_data_source = calibration_data_eeprom; if (AH9300(ah)->calibration_data_try_address != 0) { AH9300(ah)->calibration_data_source_address = AH9300(ah)->calibration_data_try_address; nptr = ar9300_eeprom_restore_internal_address( ah, mptr, mdata_size, AH9300(ah)->calibration_data_source_address, 0xff); } else { AH9300(ah)->calibration_data_source_address = ar9300_eeprom_base_address(ah); nptr = ar9300_eeprom_restore_internal_address( ah, mptr, mdata_size, AH9300(ah)->calibration_data_source_address, 0xff); if (nptr < 0 && AH9300(ah)->calibration_data_source_address != base_address) { AH9300(ah)->calibration_data_source_address = base_address; nptr = ar9300_eeprom_restore_internal_address( ah, mptr, mdata_size, AH9300(ah)->calibration_data_source_address, 0xff); } } if (nptr < 0) { AH9300(ah)->calibration_data_source = calibration_data_none; AH9300(ah)->calibration_data_source_address = 0; } } /* * ##### should be an ifdef test for any AP usage, * either in driver or in nart */ if ((AH9300(ah)->calibration_data_try == calibration_data_none || AH9300(ah)->calibration_data_try == calibration_data_flash) && AH9300(ah)->try_flash && nptr < 0) { ath_hal_printf(ah, "Restoring Cal data from Flash\n"); AH9300(ah)->calibration_data_source = calibration_data_flash; /* how are we supposed to set this for flash? */ AH9300(ah)->calibration_data_source_address = 0; nptr = ar9300_eeprom_restore_from_flash(ah, mptr, mdata_size); if (nptr < 0) { AH9300(ah)->calibration_data_source = calibration_data_none; AH9300(ah)->calibration_data_source_address = 0; } } if ((AH9300(ah)->calibration_data_try == calibration_data_none || AH9300(ah)->calibration_data_try == calibration_data_otp) && AH9300(ah)->try_otp && nptr < 0) { ath_hal_printf(ah, "Restoring Cal data from OTP\n"); AH9300(ah)->calibration_data_source = calibration_data_otp; if (AH9300(ah)->calibration_data_try_address != 0) { AH9300(ah)->calibration_data_source_address = AH9300(ah)->calibration_data_try_address; } else { AH9300(ah)->calibration_data_source_address = ar9300_eeprom_base_address(ah); } nptr = ar9300_eeprom_restore_internal_address( ah, mptr, mdata_size, AH9300(ah)->calibration_data_source_address, 0); if (nptr < 0) { AH9300(ah)->calibration_data_source = calibration_data_none; AH9300(ah)->calibration_data_source_address = 0; } } #ifdef ATH_CAL_NAND_FLASH if ((AH9300(ah)->calibration_data_try == calibration_data_none || AH9300(ah)->calibration_data_try == calibration_data_nand) && AH9300(ah)->try_nand && nptr < 0) { AH9300(ah)->calibration_data_source = calibration_data_nand; AH9300(ah)->calibration_data_source_address = ((unsigned int)(AH_PRIVATE(ah)->ah_st)) + base_address_nand; if(ar9300_calibration_data_read( ah, AH9300(ah)->calibration_data_source_address, (u_int8_t *)mptr, mdata_size) == AH_TRUE) { nptr = mdata_size; } /*nptr=ar9300EepromRestoreInternalAddress(ah, mptr, mdataSize, CalibrationDataSourceAddress);*/ if(nptr < 0) { AH9300(ah)->calibration_data_source = calibration_data_none; AH9300(ah)->calibration_data_source_address = 0; } } #endif if (nptr < 0) { ath_hal_printf(ah, "%s[%d] No vaid CAL, calling default template\n", __func__, __LINE__); nptr = ar9300_eeprom_restore_something(ah, mptr, mdata_size); } return nptr; } /******************************************************************************/ /*! ** \brief Eeprom Swapping Function ** ** This function will swap the contents of the "longer" EEPROM data items ** to ensure they are consistent with the endian requirements for the platform ** they are being compiled for ** ** \param eh Pointer to the EEPROM data structure ** \return N/A */ #if AH_BYTE_ORDER == AH_BIG_ENDIAN void ar9300_swap_eeprom(ar9300_eeprom_t *eep) { u_int32_t dword; u_int16_t word; int i; word = __bswap16(eep->base_eep_header.reg_dmn[0]); eep->base_eep_header.reg_dmn[0] = word; word = __bswap16(eep->base_eep_header.reg_dmn[1]); eep->base_eep_header.reg_dmn[1] = word; dword = __bswap32(eep->base_eep_header.swreg); eep->base_eep_header.swreg = dword; dword = __bswap32(eep->modal_header_2g.ant_ctrl_common); eep->modal_header_2g.ant_ctrl_common = dword; dword = __bswap32(eep->modal_header_2g.ant_ctrl_common2); eep->modal_header_2g.ant_ctrl_common2 = dword; dword = __bswap32(eep->modal_header_2g.paprd_rate_mask_ht20); eep->modal_header_2g.paprd_rate_mask_ht20 = dword; dword = __bswap32(eep->modal_header_2g.paprd_rate_mask_ht40); eep->modal_header_2g.paprd_rate_mask_ht40 = dword; dword = __bswap32(eep->modal_header_5g.ant_ctrl_common); eep->modal_header_5g.ant_ctrl_common = dword; dword = __bswap32(eep->modal_header_5g.ant_ctrl_common2); eep->modal_header_5g.ant_ctrl_common2 = dword; dword = __bswap32(eep->modal_header_5g.paprd_rate_mask_ht20); eep->modal_header_5g.paprd_rate_mask_ht20 = dword; dword = __bswap32(eep->modal_header_5g.paprd_rate_mask_ht40); eep->modal_header_5g.paprd_rate_mask_ht40 = dword; for (i = 0; i < OSPREY_MAX_CHAINS; i++) { word = __bswap16(eep->modal_header_2g.ant_ctrl_chain[i]); eep->modal_header_2g.ant_ctrl_chain[i] = word; word = __bswap16(eep->modal_header_5g.ant_ctrl_chain[i]); eep->modal_header_5g.ant_ctrl_chain[i] = word; } } void ar9300_eeprom_template_swap(void) { int it; ar9300_eeprom_t *dptr; for (it = 0; it < ARRAY_LENGTH(default9300); it++) { dptr = ar9300_eeprom_struct_default(it); if (dptr != 0) { ar9300_swap_eeprom(dptr); } } } #endif /* * Restore the configuration structure by reading the eeprom. * This function destroys any existing in-memory structure content. */ HAL_BOOL ar9300_eeprom_restore(struct ath_hal *ah) { struct ath_hal_9300 *ahp = AH9300(ah); ar9300_eeprom_t *mptr; int mdata_size; HAL_BOOL status = AH_FALSE; mptr = &ahp->ah_eeprom; mdata_size = ar9300_eeprom_struct_size(); if (mptr != 0 && mdata_size > 0) { #if AH_BYTE_ORDER == AH_BIG_ENDIAN ar9300_eeprom_template_swap(); ar9300_swap_eeprom(mptr); #endif /* * At this point, mptr points to the eeprom data structure * in it's "default" state. If this is big endian, swap the * data structures back to "little endian" form. */ if (ar9300_eeprom_restore_internal(ah, mptr, mdata_size) >= 0) { status = AH_TRUE; } #if AH_BYTE_ORDER == AH_BIG_ENDIAN /* Second Swap, back to Big Endian */ ar9300_eeprom_template_swap(); ar9300_swap_eeprom(mptr); #endif } ahp->ah_2g_paprd_rate_mask_ht40 = mptr->modal_header_2g.paprd_rate_mask_ht40; ahp->ah_2g_paprd_rate_mask_ht20 = mptr->modal_header_2g.paprd_rate_mask_ht20; ahp->ah_5g_paprd_rate_mask_ht40 = mptr->modal_header_5g.paprd_rate_mask_ht40; ahp->ah_5g_paprd_rate_mask_ht20 = mptr->modal_header_5g.paprd_rate_mask_ht20; return status; } int32_t ar9300_thermometer_get(struct ath_hal *ah) { struct ath_hal_9300 *ahp = AH9300(ah); int thermometer; thermometer = (ahp->ah_eeprom.base_eep_header.misc_configuration >> 1) & 0x3; thermometer--; return thermometer; } HAL_BOOL ar9300_thermometer_apply(struct ath_hal *ah) { int thermometer = ar9300_thermometer_get(ah); /* ch0_RXTX4 */ /*#define AR_PHY_65NM_CH0_RXTX4 AR_PHY_65NM(ch0_RXTX4)*/ #define AR_PHY_65NM_CH1_RXTX4 AR_PHY_65NM(ch1_RXTX4) #define AR_PHY_65NM_CH2_RXTX4 AR_PHY_65NM(ch2_RXTX4) /*#define AR_PHY_65NM_CH0_RXTX4_THERM_ON 0x10000000*/ /*#define AR_PHY_65NM_CH0_RXTX4_THERM_ON_S 28*/ #define AR_PHY_65NM_CH0_RXTX4_THERM_ON_OVR_S 29 #define AR_PHY_65NM_CH0_RXTX4_THERM_ON_OVR \ (0x1<ah_eeprom; tuning_caps_params = eep->base_eep_header.params_for_tuning_caps[0]; return tuning_caps_params; } /* * Read the tuning caps params from eeprom and set to correct register. * To regulation the frequency accuracy. */ HAL_BOOL ar9300_tuning_caps_apply(struct ath_hal *ah) { int tuning_caps_params; ar9300_eeprom_t *eep = &AH9300(ah)->ah_eeprom; tuning_caps_params = ar9300_tuning_caps_params_get(ah); if ((eep->base_eep_header.feature_enable & 0x40) >> 6) { tuning_caps_params &= 0x7f; if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah) || AR_SREV_WASP(ah)) { return AH_TRUE; } else if (AR_SREV_SCORPION(ah)) { OS_REG_RMW_FIELD(ah, AR_SCORPION_CH0_XTAL, AR_OSPREY_CHO_XTAL_CAPINDAC, tuning_caps_params); OS_REG_RMW_FIELD(ah, AR_SCORPION_CH0_XTAL, AR_OSPREY_CHO_XTAL_CAPOUTDAC, tuning_caps_params); } else { OS_REG_RMW_FIELD(ah, AR_OSPREY_CH0_XTAL, AR_OSPREY_CHO_XTAL_CAPINDAC, tuning_caps_params); OS_REG_RMW_FIELD(ah, AR_OSPREY_CH0_XTAL, AR_OSPREY_CHO_XTAL_CAPOUTDAC, tuning_caps_params); } } return AH_TRUE; } /* * Read the tx_frame_to_xpa_on param from eeprom and apply the value to * correct register. */ HAL_BOOL ar9300_xpa_timing_control_apply(struct ath_hal *ah, HAL_BOOL is_2ghz) { u_int8_t xpa_timing_control; ar9300_eeprom_t *eep = &AH9300(ah)->ah_eeprom; if ((eep->base_eep_header.feature_enable & 0x80) >> 7) { if (AR_SREV_OSPREY(ah) || AR_SREV_AR9580(ah) || AR_SREV_WASP(ah)) { if (is_2ghz) { xpa_timing_control = eep->modal_header_2g.tx_frame_to_xpa_on; OS_REG_RMW_FIELD(ah, AR_PHY_XPA_TIMING_CTL, AR_PHY_XPA_TIMING_CTL_FRAME_XPAB_ON, xpa_timing_control); } else { xpa_timing_control = eep->modal_header_5g.tx_frame_to_xpa_on; OS_REG_RMW_FIELD(ah, AR_PHY_XPA_TIMING_CTL, AR_PHY_XPA_TIMING_CTL_FRAME_XPAA_ON, xpa_timing_control); } } } return AH_TRUE; } /* * Read the xLNA_bias_strength param from eeprom and apply the value to * correct register. */ HAL_BOOL ar9300_x_lNA_bias_strength_apply(struct ath_hal *ah, HAL_BOOL is_2ghz) { u_int8_t x_lNABias; u_int32_t value = 0; ar9300_eeprom_t *eep = &AH9300(ah)->ah_eeprom; if ((eep->base_eep_header.misc_configuration & 0x40) >> 6) { if (AR_SREV_OSPREY(ah)) { if (is_2ghz) { x_lNABias = eep->modal_header_2g.xLNA_bias_strength; } else { x_lNABias = eep->modal_header_5g.xLNA_bias_strength; } value = x_lNABias & ( 0x03 ); // bit0,1 for chain0 OS_REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_RXTX4, AR_PHY_65NM_RXTX4_XLNA_BIAS, value); value = (x_lNABias >> 2) & ( 0x03 ); // bit2,3 for chain1 OS_REG_RMW_FIELD(ah, AR_PHY_65NM_CH1_RXTX4, AR_PHY_65NM_RXTX4_XLNA_BIAS, value); value = (x_lNABias >> 4) & ( 0x03 ); // bit4,5 for chain2 OS_REG_RMW_FIELD(ah, AR_PHY_65NM_CH2_RXTX4, AR_PHY_65NM_RXTX4_XLNA_BIAS, value); } } return AH_TRUE; } /* * Read EEPROM header info and program the device for correct operation * given the channel value. */ HAL_BOOL ar9300_eeprom_set_board_values(struct ath_hal *ah, const struct ieee80211_channel *chan) { HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); ar9300_xpa_bias_level_apply(ah, IEEE80211_IS_CHAN_2GHZ(chan)); ar9300_xpa_timing_control_apply(ah, IEEE80211_IS_CHAN_2GHZ(chan)); ar9300_ant_ctrl_apply(ah, IEEE80211_IS_CHAN_2GHZ(chan)); ar9300_drive_strength_apply(ah); ar9300_x_lNA_bias_strength_apply(ah, IEEE80211_IS_CHAN_2GHZ(chan)); /* wait for Poseidon internal regular turnning */ /* for Hornet we move it before initPLL to avoid an access issue */ /* Function not used when EMULATION. */ if (!AR_SREV_HORNET(ah) && !AR_SREV_WASP(ah)) { ar9300_internal_regulator_apply(ah); } ar9300_attenuation_apply(ah, ichan->channel); ar9300_quick_drop_apply(ah, ichan->channel); ar9300_thermometer_apply(ah); if(!AR_SREV_WASP(ah)) { ar9300_tuning_caps_apply(ah); } ar9300_tx_end_to_xpab_off_apply(ah, ichan->channel); return AH_TRUE; } u_int8_t * ar9300_eeprom_get_spur_chans_ptr(struct ath_hal *ah, HAL_BOOL is_2ghz) { ar9300_eeprom_t *eep = &AH9300(ah)->ah_eeprom; if (is_2ghz) { return &(eep->modal_header_2g.spur_chans[0]); } else { return &(eep->modal_header_5g.spur_chans[0]); } } static u_int8_t ar9300_eeprom_get_tx_gain_table_number_max(struct ath_hal *ah) { unsigned long tx_gain_table_max; tx_gain_table_max = OS_REG_READ_FIELD(ah, AR_PHY_TPC_7, AR_PHY_TPC_7_TX_GAIN_TABLE_MAX); return tx_gain_table_max; } u_int8_t ar9300_eeprom_tx_gain_table_index_max_apply(struct ath_hal *ah, u_int16_t channel) { unsigned int index; ar9300_eeprom_t *ahp_Eeprom; struct ath_hal_9300 *ahp = AH9300(ah); ahp_Eeprom = &ahp->ah_eeprom; if (ahp_Eeprom->base_ext1.misc_enable == 0) return AH_FALSE; if (channel < 4000) { index = ahp_Eeprom->modal_header_2g.tx_gain_cap; } else { index = ahp_Eeprom->modal_header_5g.tx_gain_cap; } OS_REG_RMW_FIELD(ah, AR_PHY_TPC_7, AR_PHY_TPC_7_TX_GAIN_TABLE_MAX, index); return AH_TRUE; } static u_int8_t ar9300_eeprom_get_pcdac_tx_gain_table_i(struct ath_hal *ah, int i, u_int8_t *pcdac) { unsigned long tx_gain; u_int8_t tx_gain_table_max; tx_gain_table_max = ar9300_eeprom_get_tx_gain_table_number_max(ah); if (i <= 0 || i > tx_gain_table_max) { *pcdac = 0; return AH_FALSE; } tx_gain = OS_REG_READ(ah, AR_PHY_TXGAIN_TAB(1) + i * 4); *pcdac = ((tx_gain >> 24) & 0xff); return AH_TRUE; } u_int8_t ar9300_eeprom_set_tx_gain_cap(struct ath_hal *ah, int *tx_gain_max) // pcdac read back from reg, read back value depends on reset 2GHz/5GHz ini // tx_gain_table, this function will be called twice after each // band's calibration. // after 2GHz cal, tx_gain_max[0] has 2GHz, calibration max txgain, // tx_gain_max[1]=-100 // after 5GHz cal, tx_gain_max[0],tx_gain_max[1] have calibration // value for both band // reset is on 5GHz, reg reading from tx_gain_table is for 5GHz, // so program can't recalculate 2g.tx_gain_cap at this point. { int i = 0, ig, im = 0; u_int8_t pcdac = 0; u_int8_t tx_gain_table_max; ar9300_eeprom_t *ahp_Eeprom; struct ath_hal_9300 *ahp = AH9300(ah); ahp_Eeprom = &ahp->ah_eeprom; if (ahp_Eeprom->base_ext1.misc_enable == 0) return AH_FALSE; tx_gain_table_max = ar9300_eeprom_get_tx_gain_table_number_max(ah); for (i = 0; i < 2; i++) { if (tx_gain_max[i]>-100) { // -100 didn't cal that band. if ( i== 0) { if (tx_gain_max[1]>-100) { continue; // both band are calibrated, skip 2GHz 2g.tx_gain_cap reset } } for (ig = 1; ig <= tx_gain_table_max; ig++) { if (ah != 0 && ah->ah_reset != 0) { ar9300_eeprom_get_pcdac_tx_gain_table_i(ah, ig, &pcdac); if (pcdac >= tx_gain_max[i]) break; } } if (ig+1 <= tx_gain_table_max) { if (pcdac == tx_gain_max[i]) im = ig; else im = ig + 1; if (i == 0) { ahp_Eeprom->modal_header_2g.tx_gain_cap = im; } else { ahp_Eeprom->modal_header_5g.tx_gain_cap = im; } } else { if (i == 0) { ahp_Eeprom->modal_header_2g.tx_gain_cap = ig; } else { ahp_Eeprom->modal_header_5g.tx_gain_cap = ig; } } } } return AH_TRUE; } Index: projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_freebsd.c =================================================================== --- projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_freebsd.c (revision 278776) +++ projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_freebsd.c (revision 278777) @@ -1,691 +1,691 @@ /* * Copyright (c) 2012, 2013 Adrian Chadd . * * 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 "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ah_devid.h" #include "ah_desc.h" #include "ar9300.h" #include "ar9300reg.h" #include "ar9300phy.h" #include "ar9300desc.h" #include "ar9300_freebsd.h" #include "ar9300_stub.h" #include "ar9300_stub_funcs.h" #define FIX_NOISE_FLOOR 1 #define NEXT_TBTT_NOW 5 static HAL_BOOL ar9300ClrMulticastFilterIndex(struct ath_hal *ah, uint32_t ix); static HAL_BOOL ar9300SetMulticastFilterIndex(struct ath_hal *ah, uint32_t ix); static void ar9300SetChainMasks(struct ath_hal *ah, uint32_t tx_chainmask, uint32_t rx_chainmask) { AH9300(ah)->ah_tx_chainmask = tx_chainmask & AH_PRIVATE(ah)->ah_caps.halTxChainMask; AH9300(ah)->ah_rx_chainmask = rx_chainmask & AH_PRIVATE(ah)->ah_caps.halRxChainMask; } static u_int ar9300GetSlotTime(struct ath_hal *ah) { u_int clks = OS_REG_READ(ah, AR_D_GBL_IFS_SLOT) & 0xffff; return (ath_hal_mac_usec(ah, clks)); /* convert from system clocks */ } static HAL_BOOL ar9300_freebsd_set_tx_power_limit(struct ath_hal *ah, uint32_t limit) { return (ar9300_set_tx_power_limit(ah, limit, 0, 0)); } static uint64_t ar9300_get_next_tbtt(struct ath_hal *ah) { return (OS_REG_READ(ah, AR_NEXT_TBTT_TIMER)); } void ar9300_attach_freebsd_ops(struct ath_hal *ah) { /* Global functions */ ah->ah_detach = ar9300_detach; ah->ah_getRateTable = ar9300_get_rate_table; /* Reset functions */ ah->ah_reset = ar9300_reset_freebsd; ah->ah_phyDisable = ar9300_phy_disable; ah->ah_disable = ar9300_disable; ah->ah_configPCIE = ar9300_config_pcie_freebsd; // ah->ah_disablePCIE = ar9300_disable_pcie_phy; ah->ah_setPCUConfig = ar9300_set_pcu_config; // perCalibration ah->ah_perCalibrationN = ar9300_per_calibration_freebsd; ah->ah_resetCalValid = ar9300_reset_cal_valid_freebsd; ah->ah_setTxPowerLimit = ar9300_freebsd_set_tx_power_limit; ah->ah_getChanNoise = ath_hal_getChanNoise; /* Transmit functions */ ah->ah_setupTxQueue = ar9300_setup_tx_queue; ah->ah_setTxQueueProps = ar9300_set_tx_queue_props; ah->ah_getTxQueueProps = ar9300_get_tx_queue_props; ah->ah_releaseTxQueue = ar9300_release_tx_queue; ah->ah_resetTxQueue = ar9300_reset_tx_queue; ah->ah_getTxDP = ar9300_get_tx_dp; ah->ah_setTxDP = ar9300_set_tx_dp; ah->ah_numTxPending = ar9300_num_tx_pending; ah->ah_startTxDma = ar9300_start_tx_dma; ah->ah_stopTxDma = ar9300_stop_tx_dma_freebsd; ah->ah_setupTxDesc = ar9300_freebsd_setup_tx_desc; ah->ah_setupXTxDesc = ar9300_freebsd_setup_x_tx_desc; ah->ah_fillTxDesc = ar9300_freebsd_fill_tx_desc; ah->ah_procTxDesc = ar9300_freebsd_proc_tx_desc; ah->ah_getTxIntrQueue = ar9300_get_tx_intr_queue; // reqTxIntrDesc ah->ah_getTxCompletionRates = ar9300_freebsd_get_tx_completion_rates; ah->ah_setTxDescLink = ar9300_set_desc_link; ah->ah_getTxDescLink = ar9300_freebsd_get_desc_link; ah->ah_getTxDescLinkPtr = ar9300_get_desc_link_ptr; ah->ah_setupTxStatusRing = ar9300_setup_tx_status_ring; ah->ah_getTxRawTxDesc = ar9300_get_raw_tx_desc; ah->ah_updateTxTrigLevel = ar9300_update_tx_trig_level; /* RX functions */ ah->ah_getRxDP = ar9300_get_rx_dp; ah->ah_setRxDP = ar9300_set_rx_dp; ah->ah_enableReceive = ar9300_enable_receive; ah->ah_stopDmaReceive = ar9300_stop_dma_receive_freebsd; ah->ah_startPcuReceive = ar9300_start_pcu_receive_freebsd; ah->ah_stopPcuReceive = ar9300_stop_pcu_receive; ah->ah_setMulticastFilter = ar9300_set_multicast_filter; ah->ah_setMulticastFilterIndex = ar9300SetMulticastFilterIndex; ah->ah_clrMulticastFilterIndex = ar9300ClrMulticastFilterIndex; ah->ah_getRxFilter = ar9300_get_rx_filter; ah->ah_setRxFilter = ar9300_set_rx_filter; /* setupRxDesc */ ah->ah_procRxDesc = ar9300_proc_rx_desc_freebsd; ah->ah_rxMonitor = ar9300_ani_rxmonitor_freebsd; ah->ah_aniPoll = ar9300_ani_poll_freebsd; ah->ah_procMibEvent = ar9300_process_mib_intr; /* Misc functions */ ah->ah_getCapability = ar9300_get_capability; ah->ah_setCapability = ar9300_set_capability; ah->ah_getDiagState = ar9300_get_diag_state; ah->ah_getMacAddress = ar9300_get_mac_address; ah->ah_setMacAddress = ar9300_set_mac_address; ah->ah_getBssIdMask = ar9300_get_bss_id_mask; ah->ah_setBssIdMask = ar9300_set_bss_id_mask; ah->ah_setRegulatoryDomain = ar9300_set_regulatory_domain; ah->ah_setLedState = ar9300_set_led_state; ah->ah_writeAssocid = ar9300_write_associd; ah->ah_gpioCfgInput = ar9300_gpio_cfg_input; ah->ah_gpioCfgOutput = ar9300_gpio_cfg_output; ah->ah_gpioGet = ar9300_gpio_get; ah->ah_gpioSet = ar9300_gpio_set; ah->ah_gpioSetIntr = ar9300_gpio_set_intr; /* polarity */ /* mask */ ah->ah_getTsf32 = ar9300_get_tsf32; ah->ah_getTsf64 = ar9300_get_tsf64; ah->ah_resetTsf = ar9300_reset_tsf; ah->ah_detectCardPresent = ar9300_detect_card_present; // ah->ah_updateMibCounters = ar9300_update_mib_counters; ah->ah_getRfGain = ar9300_get_rfgain; ah->ah_getDefAntenna = ar9300_get_def_antenna; ah->ah_setDefAntenna = ar9300_set_def_antenna; // ah->ah_getAntennaSwitch = ar9300_get_antenna_switch; // ah->ah_setAntennaSwitch = ar9300_set_antenna_switch; // ah->ah_setSifsTime = ar9300_set_sifs_time; // ah->ah_getSifsTime = ar9300_get_sifs_time; ah->ah_setSlotTime = ar9300_set_slot_time; ah->ah_getSlotTime = ar9300GetSlotTime; ah->ah_getAckTimeout = ar9300_get_ack_timeout; ah->ah_setAckTimeout = ar9300_set_ack_timeout; // XXX ack/ctsrate // XXX CTS timeout // XXX decompmask // coverageclass ah->ah_setQuiet = ar9300_set_quiet; ah->ah_getMibCycleCounts = ar9300_freebsd_get_mib_cycle_counts; /* DFS functions */ ah->ah_enableDfs = ar9300_enable_dfs; ah->ah_getDfsThresh = ar9300_get_dfs_thresh; ah->ah_getDfsDefaultThresh = ar9300_freebsd_get_dfs_default_thresh; // procradarevent ah->ah_isFastClockEnabled = ar9300_is_fast_clock_enabled; ah->ah_get11nExtBusy = ar9300_get_11n_ext_busy; /* Key cache functions */ ah->ah_getKeyCacheSize = ar9300_get_key_cache_size; ah->ah_resetKeyCacheEntry = ar9300_reset_key_cache_entry; ah->ah_isKeyCacheEntryValid = ar9300_is_key_cache_entry_valid; ah->ah_setKeyCacheEntry = ar9300_set_key_cache_entry; ah->ah_setKeyCacheEntryMac = ar9300_set_key_cache_entry_mac; /* Power management functions */ ah->ah_setPowerMode = ar9300_set_power_mode; ah->ah_getPowerMode = ar9300_get_power_mode; /* Beacon functions */ /* ah_setBeaconTimers */ ah->ah_beaconInit = ar9300_freebsd_beacon_init; /* ah_setBeaconTimers */ ah->ah_setStationBeaconTimers = ar9300_set_sta_beacon_timers; /* ah_resetStationBeaconTimers */ /* ah_getNextTBTT */ ah->ah_getNextTBTT = ar9300_get_next_tbtt; /* Interrupt functions */ ah->ah_isInterruptPending = ar9300_is_interrupt_pending; ah->ah_getPendingInterrupts = ar9300_get_pending_interrupts_freebsd; ah->ah_getInterrupts = ar9300_get_interrupts; ah->ah_setInterrupts = ar9300_set_interrupts_freebsd; /* Regulatory/internal functions */ // AH_PRIVATE(ah)->ah_getNfAdjust = ar9300_get_nf_adjust; AH_PRIVATE(ah)->ah_eepromRead = ar9300_eeprom_read_word; // AH_PRIVATE(ah)->ah_getChipPowerLimits = ar9300_get_chip_power_limits; AH_PRIVATE(ah)->ah_getWirelessModes = ar9300_get_wireless_modes; AH_PRIVATE(ah)->ah_getChannelEdges = ar9300_get_channel_edges; AH_PRIVATE(ah)->ah_eepromRead = ar9300_eeprom_read_word; /* XXX ah_eeprom */ /* XXX ah_eeversion */ /* XXX ah_eepromDetach */ /* XXX ah_eepromGet */ AH_PRIVATE(ah)->ah_eepromGet = ar9300_eeprom_get_freebsd; /* XXX ah_eepromSet */ /* XXX ah_getSpurChan */ /* XXX ah_eepromDiag */ /* 802.11n functions */ ah->ah_chainTxDesc = ar9300_freebsd_chain_tx_desc; ah->ah_setupFirstTxDesc= ar9300_freebsd_setup_first_tx_desc; ah->ah_setupLastTxDesc = ar9300_freebsd_setup_last_tx_desc; ah->ah_set11nRateScenario = ar9300_freebsd_set_11n_rate_scenario; ah->ah_set11nTxDesc = ar9300_freebsd_setup_11n_desc; ah->ah_set11nAggrFirst = ar9300_set_11n_aggr_first; ah->ah_set11nAggrMiddle = ar9300_set_11n_aggr_middle; ah->ah_set11nAggrLast = ar9300_set_11n_aggr_last; ah->ah_clr11nAggr = ar9300_clr_11n_aggr; ah->ah_set11nBurstDuration = ar9300_set_11n_burst_duration; /* ah_get11nExtBusy */ ah->ah_set11nMac2040 = ar9300_set_11n_mac2040; ah->ah_setChainMasks = ar9300SetChainMasks; /* ah_get11nRxClear */ /* ah_set11nRxClear */ /* bluetooth coexistence functions */ ah->ah_btCoexSetInfo = ar9300_set_bt_coex_info; ah->ah_btCoexSetConfig = ar9300_bt_coex_config; ah->ah_btCoexSetQcuThresh = ar9300_bt_coex_set_qcu_thresh; ah->ah_btCoexSetWeights = ar9300_bt_coex_set_weights; ah->ah_btCoexSetBmissThresh = ar9300_bt_coex_setup_bmiss_thresh; ah->ah_btCoexSetParameter = ar9300_bt_coex_set_parameter; ah->ah_btCoexDisable = ar9300_bt_coex_disable; ah->ah_btCoexEnable = ar9300_bt_coex_enable; /* MCI bluetooth functions */ if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) { ah->ah_btCoexSetWeights = ar9300_mci_bt_coex_set_weights; ah->ah_btCoexDisable = ar9300_mci_bt_coex_disable; ah->ah_btCoexEnable = ar9300_mci_bt_coex_enable; } ah->ah_btMciSetup = ar9300_mci_setup; ah->ah_btMciSendMessage = ar9300_mci_send_message; ah->ah_btMciGetInterrupt = ar9300_mci_get_interrupt; ah->ah_btMciGetState = ar9300_mci_state; ah->ah_btMciDetach = ar9300_mci_detach; /* LNA diversity functions */ ah->ah_divLnaConfGet = ar9300_ant_div_comb_get_config; ah->ah_divLnaConfSet = ar9300_ant_div_comb_set_config; } HAL_BOOL ar9300_reset_freebsd(struct ath_hal *ah, HAL_OPMODE opmode, struct ieee80211_channel *chan, HAL_BOOL bChannelChange, HAL_STATUS *status) { HAL_BOOL r; HAL_HT_MACMODE macmode; struct ath_hal_private *ap = AH_PRIVATE(ah); macmode = IEEE80211_IS_CHAN_HT40(chan) ? HAL_HT_MACMODE_2040 : HAL_HT_MACMODE_20; r = ar9300_reset(ah, opmode, chan, macmode, ap->ah_caps.halTxChainMask, ap->ah_caps.halRxChainMask, HAL_HT_EXTPROTSPACING_20, /* always 20Mhz channel spacing */ bChannelChange, status, AH_FALSE); /* XXX should really extend ath_hal_reset() */ return (r); } void ar9300_config_pcie_freebsd(struct ath_hal *ah, HAL_BOOL restore, HAL_BOOL powerOff) { ar9300_config_pci_power_save(ah, restore ? 1 : 0, powerOff ? 1 : 0); } /* * This is a copy from ar9300_eeprom_get(), purely because the FreeBSD * API is very silly and inconsistent. * * The AR93xx HAL doesn't call the eepromGetFlag() function, so this * only occurs for FreeBSD code. * * When I fix this particular API, I'll undo this. */ HAL_STATUS ar9300_eeprom_get_freebsd(struct ath_hal *ah, int param, void *val) { switch (param) { case AR_EEP_FSTCLK_5G: return HAL_OK; default: ath_hal_printf(ah, "%s: called, param=%d\n", __func__, param); return HAL_EIO; } } HAL_BOOL ar9300_stop_tx_dma_freebsd(struct ath_hal *ah, u_int q) { return ar9300_stop_tx_dma(ah, q, 1000); } void ar9300_ani_poll_freebsd(struct ath_hal *ah, const struct ieee80211_channel *chan) { HAL_NODE_STATS stats; HAL_ANISTATS anistats; OS_MEMZERO(&stats, sizeof(stats)); OS_MEMZERO(&anistats, sizeof(anistats)); ar9300_ani_ar_poll(ah, &stats, chan, &anistats); } /* * Setup the configuration parameters in the style the AR9300 HAL * wants. */ void ar9300_config_defaults_freebsd(struct ath_hal *ah, HAL_OPS_CONFIG *ah_config) { /* Until FreeBSD's HAL does this by default - just copy */ OS_MEMCPY(&ah->ah_config, ah_config, sizeof(HAL_OPS_CONFIG)); ah->ah_config.ath_hal_enable_ani = AH_TRUE; } HAL_BOOL ar9300_stop_dma_receive_freebsd(struct ath_hal *ah) { return ar9300_stop_dma_receive(ah, 1000); } HAL_BOOL ar9300_get_pending_interrupts_freebsd(struct ath_hal *ah, HAL_INT *masked) { /* Non-MSI, so no MSI vector; and 'nortc' = 0 */ return ar9300_get_pending_interrupts(ah, masked, HAL_INT_LINE, 0, 0); } HAL_INT ar9300_set_interrupts_freebsd(struct ath_hal *ah, HAL_INT ints) { /* nortc = 0 */ return ar9300_set_interrupts(ah, ints, 0); } HAL_BOOL ar9300_per_calibration_freebsd(struct ath_hal *ah, struct ieee80211_channel *chan, u_int rxchainmask, HAL_BOOL long_cal, HAL_BOOL *isCalDone) { /* XXX fake scheduled calibrations for now */ u_int32_t sched_cals = 0xfffffff; return ar9300_calibration(ah, chan, AH_PRIVATE(ah)->ah_caps.halRxChainMask, long_cal, isCalDone, 0, /* is_scan */ &sched_cals); } HAL_BOOL ar9300_reset_cal_valid_freebsd(struct ath_hal *ah, const struct ieee80211_channel *chan) { HAL_BOOL is_cal_done = AH_TRUE; ar9300_reset_cal_valid(ah, chan, &is_cal_done, 0xffffffff); return (is_cal_done); } void ar9300_start_pcu_receive_freebsd(struct ath_hal *ah) { /* is_scanning flag == NULL */ ar9300_start_pcu_receive(ah, AH_FALSE); } /* * FreeBSD will just pass in the descriptor value as 'pa'. * The Atheros HAL treats 'pa' as the physical address of the RX * descriptor and 'bufaddr' as the physical address of the RX buffer. * I'm not sure why they didn't collapse them - the AR9300 RX descriptor * routine doesn't check 'pa'. */ HAL_STATUS ar9300_proc_rx_desc_freebsd(struct ath_hal *ah, struct ath_desc *ds, uint32_t pa, struct ath_desc *ds_next, uint64_t tsf, struct ath_rx_status *rxs) { return (ar9300_proc_rx_desc_fast(ah, ds, 0, ds_next, rxs, (void *) ds)); } void ar9300_ani_rxmonitor_freebsd(struct ath_hal *ah, const HAL_NODE_STATS *stats, const struct ieee80211_channel *chan) { } void ar9300_freebsd_get_desc_link(struct ath_hal *ah, void *ds, uint32_t *link) { struct ar9300_txc *ads = AR9300TXC(ds); (*link) = ads->ds_link; } /* * TX descriptor field setting wrappers - eek. */ HAL_BOOL ar9300_freebsd_setup_tx_desc(struct ath_hal *ah, struct ath_desc *ds, u_int pktLen, u_int hdrLen, HAL_PKT_TYPE type, u_int txPower, u_int txRate0, u_int txTries0, u_int keyIx, u_int antMode, u_int flags, u_int rtsctsRate, u_int rtsCtsDuration, u_int compicvLen, u_int compivLen, u_int comp) { struct ath_hal_9300 *ahp = AH9300(ah); HAL_KEY_TYPE keyType = 0; /* XXX No padding */ if (keyIx != HAL_TXKEYIX_INVALID) keyType = ahp->ah_keytype[keyIx]; /* XXX bounds check keyix */ ar9300_set_11n_tx_desc(ah, ds, pktLen, type, txPower, keyIx, keyType, flags); return AH_TRUE; } HAL_BOOL ar9300_freebsd_setup_x_tx_desc(struct ath_hal *ah, struct ath_desc *ds, u_int txRate1, u_int txTries1, u_int txRate2, u_int txTries2, u_int txRate3, u_int txTries3) { #if 0 ath_hal_printf(ah, "%s: called, 0x%x/%d, 0x%x/%d, 0x%x/%d\n", __func__, txRate1, txTries1, txRate2, txTries2, txRate3, txTries3); #endif /* XXX should only be called during probe */ return (AH_TRUE); } HAL_BOOL ar9300_freebsd_fill_tx_desc(struct ath_hal *ah, struct ath_desc *ds, HAL_DMA_ADDR *bufListPtr, uint32_t *segLenPtr, u_int descId, u_int qid, HAL_BOOL firstSeg, HAL_BOOL lastSeg, const struct ath_desc *ds0) { HAL_KEY_TYPE keyType = 0; const struct ar9300_txc *ads = AR9300TXC_CONST(ds0); /* * FreeBSD's HAL doesn't pass the keytype to fill_tx_desc(); * it's copied as part of the descriptor chaining. * * So, extract it from ds0. */ keyType = MS(ads->ds_ctl17, AR_encr_type); return ar9300_fill_tx_desc(ah, ds, bufListPtr, segLenPtr, descId, qid, keyType, firstSeg, lastSeg, ds0); } HAL_BOOL ar9300_freebsd_get_tx_completion_rates(struct ath_hal *ah, const struct ath_desc *ds0, int *rates, int *tries) { ath_hal_printf(ah, "%s: called\n", __func__); return AH_FALSE; /* XXX for now */ } /* * 802.11n TX descriptor wrappers */ void ar9300_freebsd_set_11n_rate_scenario(struct ath_hal *ah, struct ath_desc *ds, u_int durUpdateEn, u_int rtsctsRate, HAL_11N_RATE_SERIES series[], u_int nseries, u_int flags) { /* lastds=NULL, rtscts_duration is 0, smart antenna is 0 */ ar9300_set_11n_rate_scenario(ah, (void *) ds, (void *)ds, durUpdateEn, rtsctsRate, 0, series, nseries, flags, 0); } /* chaintxdesc */ HAL_BOOL ar9300_freebsd_chain_tx_desc(struct ath_hal *ah, struct ath_desc *ds, HAL_DMA_ADDR *bufLenList, uint32_t *segLenList, u_int pktLen, u_int hdrLen, HAL_PKT_TYPE type, u_int keyIx, HAL_CIPHER cipher, uint8_t numDelims, HAL_BOOL firstSeg, HAL_BOOL lastSeg, HAL_BOOL lastAggr) { ath_hal_printf(ah, "%s: called\n", __func__); return AH_FALSE; } /* setupfirsttxdesc */ HAL_BOOL ar9300_freebsd_setup_first_tx_desc(struct ath_hal *ah, struct ath_desc *ds, u_int aggrLen, u_int flags, u_int txPower, u_int txRate0, u_int txTries0, u_int antMode, u_int rtsctsRate, u_int rtsctsDuration) { ath_hal_printf(ah, "%s: called\n", __func__); return AH_FALSE; } /* setuplasttxdesc */ /* * This gets called but for now let's not log anything; * it's only used to update the rate control information. */ HAL_BOOL ar9300_freebsd_setup_last_tx_desc(struct ath_hal *ah, struct ath_desc *ds, const struct ath_desc *ds0) { // ath_hal_printf(ah, "%s: called\n", __func__); return AH_FALSE; } void ar9300_freebsd_setup_11n_desc(struct ath_hal *ah, void *ds, u_int pktLen, HAL_PKT_TYPE type, u_int txPower, u_int keyIx, u_int flags) { ath_hal_printf(ah, "%s: called\n", __func__); #if 0 struct ath_hal_9300 *ahp = AH9300(ah); HAL_KEY_TYPE keyType = 0; /* XXX No padding */ if (keyIx != HAL_TXKEYIX_INVALID) keyType = ahp->ah_keytype[keyIx]; /* XXX bounds check keyix */ ar9300_set_11n_tx_desc(ah, ds, pktLen, type, txPower, keyIx, keyType, flags); #endif } HAL_STATUS ar9300_freebsd_proc_tx_desc(struct ath_hal *ah, struct ath_desc *ds, struct ath_tx_status *ts) { return ar9300_proc_tx_desc(ah, ts); } void ar9300_freebsd_beacon_init(struct ath_hal *ah, uint32_t next_beacon, uint32_t beacon_period) { - ar9300_beacon_init(ah, next_beacon, beacon_period, + ar9300_beacon_init(ah, next_beacon, beacon_period, 0, AH_PRIVATE(ah)->ah_opmode); } HAL_BOOL ar9300_freebsd_get_mib_cycle_counts(struct ath_hal *ah, HAL_SURVEY_SAMPLE *hs) { return (AH_FALSE); } HAL_BOOL ar9300_freebsd_get_dfs_default_thresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe) { /* XXX not yet */ return (AH_FALSE); } /* * Clear multicast filter by index - from FreeBSD ar5212_recv.c */ static HAL_BOOL ar9300ClrMulticastFilterIndex(struct ath_hal *ah, uint32_t ix) { uint32_t val; if (ix >= 64) return (AH_FALSE); if (ix >= 32) { val = OS_REG_READ(ah, AR_MCAST_FIL1); OS_REG_WRITE(ah, AR_MCAST_FIL1, (val &~ (1<<(ix-32)))); } else { val = OS_REG_READ(ah, AR_MCAST_FIL0); OS_REG_WRITE(ah, AR_MCAST_FIL0, (val &~ (1<= 64) return (AH_FALSE); if (ix >= 32) { val = OS_REG_READ(ah, AR_MCAST_FIL1); OS_REG_WRITE(ah, AR_MCAST_FIL1, (val | (1<<(ix-32)))); } else { val = OS_REG_READ(ah, AR_MCAST_FIL0); OS_REG_WRITE(ah, AR_MCAST_FIL0, (val | (1< -122dBm to -59dBm * resolution (2 bits) is 0.25dBm */ #define NOISE_PWR_DATA_OFFSET -90 /* dbm - all pwr report data is represented offset by this */ #define INT_2_NOISE_PWR_DBM(_p) (((_p) - NOISE_PWR_DATA_OFFSET) << 2) #define NOISE_PWR_DBM_2_INT(_p) ((((_p) + 3) >> 2) + NOISE_PWR_DATA_OFFSET) #define NOISE_PWR_DBM_2_DEC(_p) (((-(_p)) & 3) * 25) #define N2DBM(_x,_y) ((((_x) - NOISE_PWR_DATA_OFFSET) << 2) - (_y)/25) /* SPECTRAL SCAN defines end */ typedef struct halvowstats { u_int32_t tx_frame_count; u_int32_t rx_frame_count; u_int32_t rx_clear_count; u_int32_t cycle_count; u_int32_t ext_cycle_count; } HAL_VOWSTATS; #define HAL_BT_COEX_FLAG_LOW_ACK_PWR 0x00000001 #define HAL_BT_COEX_FLAG_LOWER_TX_PWR 0x00000002 #define HAL_BT_COEX_FLAG_ANT_DIV_ALLOW 0x00000004 /* Check Rx Diversity is allowed */ #define HAL_BT_COEX_FLAG_ANT_DIV_ENABLE 0x00000008 /* Check Diversity is on or off */ #define HAL_BT_COEX_FLAG_MCI_MAX_TX_PWR 0x00000010 #define HAL_BT_COEX_FLAG_MCI_FTP_STOMP_RX 0x00000020 /* * Weight table configurations. */ #define AR9300_BT_WGHT 0xcccc4444 #define AR9300_STOMP_ALL_WLAN_WGHT0 0xfffffff0 #define AR9300_STOMP_ALL_WLAN_WGHT1 0xfffffff0 #define AR9300_STOMP_LOW_WLAN_WGHT0 0x88888880 #define AR9300_STOMP_LOW_WLAN_WGHT1 0x88888880 #define AR9300_STOMP_NONE_WLAN_WGHT0 0x00000000 #define AR9300_STOMP_NONE_WLAN_WGHT1 0x00000000 #define AR9300_STOMP_ALL_FORCE_WLAN_WGHT0 0xffffffff // Stomp BT even when WLAN is idle #define AR9300_STOMP_ALL_FORCE_WLAN_WGHT1 0xffffffff #define AR9300_STOMP_LOW_FORCE_WLAN_WGHT0 0x88888888 // Stomp BT even when WLAN is idle #define AR9300_STOMP_LOW_FORCE_WLAN_WGHT1 0x88888888 #define JUPITER_STOMP_ALL_WLAN_WGHT0 0x01017d01 #define JUPITER_STOMP_ALL_WLAN_WGHT1 0x41414101 #define JUPITER_STOMP_ALL_WLAN_WGHT2 0x41414101 #define JUPITER_STOMP_ALL_WLAN_WGHT3 0x41414141 #define JUPITER_STOMP_LOW_WLAN_WGHT0 0x01017d01 #define JUPITER_STOMP_LOW_WLAN_WGHT1 0x3b3b3b01 #define JUPITER_STOMP_LOW_WLAN_WGHT2 0x3b3b3b01 #define JUPITER_STOMP_LOW_WLAN_WGHT3 0x3b3b3b3b #define JUPITER_STOMP_LOW_FTP_WLAN_WGHT0 0x01017d01 #define JUPITER_STOMP_LOW_FTP_WLAN_WGHT1 0x013b0101 #define JUPITER_STOMP_LOW_FTP_WLAN_WGHT2 0x3b3b0101 #define JUPITER_STOMP_LOW_FTP_WLAN_WGHT3 0x3b3b013b #define JUPITER_STOMP_NONE_WLAN_WGHT0 0x01017d01 #define JUPITER_STOMP_NONE_WLAN_WGHT1 0x01010101 #define JUPITER_STOMP_NONE_WLAN_WGHT2 0x01010101 #define JUPITER_STOMP_NONE_WLAN_WGHT3 0x01010101 #define JUPITER_STOMP_ALL_FORCE_WLAN_WGHT0 0x01017d7d #define JUPITER_STOMP_ALL_FORCE_WLAN_WGHT1 0x7d7d7d01 #define JUPITER_STOMP_ALL_FORCE_WLAN_WGHT2 0x7d7d7d7d #define JUPITER_STOMP_ALL_FORCE_WLAN_WGHT3 0x7d7d7d7d #define JUPITER_STOMP_LOW_FORCE_WLAN_WGHT0 0x01013b3b #define JUPITER_STOMP_LOW_FORCE_WLAN_WGHT1 0x3b3b3b01 #define JUPITER_STOMP_LOW_FORCE_WLAN_WGHT2 0x3b3b3b3b #define JUPITER_STOMP_LOW_FORCE_WLAN_WGHT3 0x3b3b3b3b #define MCI_CONCUR_TX_WLAN_WGHT1_MASK 0xff000000 #define MCI_CONCUR_TX_WLAN_WGHT1_MASK_S 24 #define MCI_CONCUR_TX_WLAN_WGHT2_MASK 0x00ff0000 #define MCI_CONCUR_TX_WLAN_WGHT2_MASK_S 16 #define MCI_CONCUR_TX_WLAN_WGHT3_MASK 0x000000ff #define MCI_CONCUR_TX_WLAN_WGHT3_MASK_S 0 #define MCI_CONCUR_TX_WLAN_WGHT3_MASK2 0x00ff0000 #define MCI_CONCUR_TX_WLAN_WGHT3_MASK2_S 16 #define MCI_QUERY_BT_VERSION_VERBOSE 0 #define MCI_LINKID_INDEX_MGMT_PENDING 1 #define HAL_MCI_FLAG_DISABLE_TIMESTAMP 0x00000001 /* Disable time stamp */ typedef enum mci_message_header { MCI_LNA_CTRL = 0x10, /* len = 0 */ MCI_CONT_NACK = 0x20, /* len = 0 */ MCI_CONT_INFO = 0x30, /* len = 4 */ MCI_CONT_RST = 0x40, /* len = 0 */ MCI_SCHD_INFO = 0x50, /* len = 16 */ MCI_CPU_INT = 0x60, /* len = 4 */ MCI_SYS_WAKING = 0x70, /* len = 0 */ MCI_GPM = 0x80, /* len = 16 */ MCI_LNA_INFO = 0x90, /* len = 1 */ MCI_LNA_STATE = 0x94, MCI_LNA_TAKE = 0x98, MCI_LNA_TRANS = 0x9c, MCI_SYS_SLEEPING = 0xa0, /* len = 0 */ MCI_REQ_WAKE = 0xc0, /* len = 0 */ MCI_DEBUG_16 = 0xfe, /* len = 2 */ MCI_REMOTE_RESET = 0xff /* len = 16 */ } MCI_MESSAGE_HEADER; /* Default remote BT device MCI COEX version */ #define MCI_GPM_COEX_MAJOR_VERSION_DEFAULT 3 #define MCI_GPM_COEX_MINOR_VERSION_DEFAULT 0 /* Local WLAN MCI COEX version */ #define MCI_GPM_COEX_MAJOR_VERSION_WLAN 3 #define MCI_GPM_COEX_MINOR_VERSION_WLAN 0 typedef enum mci_gpm_subtype { MCI_GPM_BT_CAL_REQ = 0, MCI_GPM_BT_CAL_GRANT = 1, MCI_GPM_BT_CAL_DONE = 2, MCI_GPM_WLAN_CAL_REQ = 3, MCI_GPM_WLAN_CAL_GRANT = 4, MCI_GPM_WLAN_CAL_DONE = 5, MCI_GPM_COEX_AGENT = 0x0C, MCI_GPM_RSVD_PATTERN = 0xFE, MCI_GPM_RSVD_PATTERN32 = 0xFEFEFEFE, MCI_GPM_BT_DEBUG = 0xFF } MCI_GPM_SUBTYPE_T; typedef enum mci_gpm_coex_opcode { MCI_GPM_COEX_VERSION_QUERY = 0, MCI_GPM_COEX_VERSION_RESPONSE = 1, MCI_GPM_COEX_STATUS_QUERY = 2, MCI_GPM_COEX_HALT_BT_GPM = 3, MCI_GPM_COEX_WLAN_CHANNELS = 4, MCI_GPM_COEX_BT_PROFILE_INFO = 5, MCI_GPM_COEX_BT_STATUS_UPDATE = 6, MCI_GPM_COEX_BT_UPDATE_FLAGS = 7 } MCI_GPM_COEX_OPCODE_T; typedef enum mci_gpm_coex_query_type { /* WLAN information */ MCI_GPM_COEX_QUERY_WLAN_ALL_INFO = 0x01, /* BT information */ MCI_GPM_COEX_QUERY_BT_ALL_INFO = 0x01, MCI_GPM_COEX_QUERY_BT_TOPOLOGY = 0x02, MCI_GPM_COEX_QUERY_BT_DEBUG = 0x04 } MCI_GPM_COEX_QUERY_TYPE_T; typedef enum mci_gpm_coex_halt_bt_gpm { MCI_GPM_COEX_BT_GPM_UNHALT = 0, MCI_GPM_COEX_BT_GPM_HALT = 1 } MCI_GPM_COEX_HALT_BT_GPM_T; typedef enum mci_gpm_coex_profile_type { MCI_GPM_COEX_PROFILE_UNKNOWN = 0, MCI_GPM_COEX_PROFILE_RFCOMM = 1, MCI_GPM_COEX_PROFILE_A2DP = 2, MCI_GPM_COEX_PROFILE_HID = 3, MCI_GPM_COEX_PROFILE_BNEP = 4, MCI_GPM_COEX_PROFILE_VOICE = 5, MCI_GPM_COEX_PROFILE_MAX } MCI_GPM_COEX_PROFILE_TYPE_T; typedef enum mci_gpm_coex_profile_state { MCI_GPM_COEX_PROFILE_STATE_END = 0, MCI_GPM_COEX_PROFILE_STATE_START = 1 } MCI_GPM_COEX_PROFILE_STATE_T; typedef enum mci_gpm_coex_profile_role { MCI_GPM_COEX_PROFILE_SLAVE = 0, MCI_GPM_COEX_PROFILE_MASTER = 1 } MCI_GPM_COEX_PROFILE_ROLE_T; typedef enum mci_gpm_coex_bt_status_type { MCI_GPM_COEX_BT_NONLINK_STATUS = 0, MCI_GPM_COEX_BT_LINK_STATUS = 1 } MCI_GPM_COEX_BT_STATUS_TYPE_T; typedef enum mci_gpm_coex_bt_status_state { MCI_GPM_COEX_BT_NORMAL_STATUS = 0, MCI_GPM_COEX_BT_CRITICAL_STATUS = 1 } MCI_GPM_COEX_BT_STATUS_STATE_T; #define MCI_GPM_INVALID_PROFILE_HANDLE 0xff typedef enum mci_gpm_coex_bt_updata_flags_op { MCI_GPM_COEX_BT_FLAGS_READ = 0x00, MCI_GPM_COEX_BT_FLAGS_SET = 0x01, MCI_GPM_COEX_BT_FLAGS_CLEAR = 0x02 } MCI_GPM_COEX_BT_FLAGS_OP_T; /* MCI GPM/Coex opcode/type definitions */ enum { MCI_GPM_COEX_W_GPM_PAYLOAD = 1, MCI_GPM_COEX_B_GPM_TYPE = 4, MCI_GPM_COEX_B_GPM_OPCODE = 5, /* MCI_GPM_WLAN_CAL_REQ, MCI_GPM_WLAN_CAL_DONE */ MCI_GPM_WLAN_CAL_W_SEQUENCE = 2, /* MCI_GPM_COEX_VERSION_QUERY */ /* MCI_GPM_COEX_VERSION_RESPONSE */ MCI_GPM_COEX_B_MAJOR_VERSION = 6, MCI_GPM_COEX_B_MINOR_VERSION = 7, /* MCI_GPM_COEX_STATUS_QUERY */ MCI_GPM_COEX_B_BT_BITMAP = 6, MCI_GPM_COEX_B_WLAN_BITMAP = 7, /* MCI_GPM_COEX_HALT_BT_GPM */ MCI_GPM_COEX_B_HALT_STATE = 6, /* MCI_GPM_COEX_WLAN_CHANNELS */ MCI_GPM_COEX_B_CHANNEL_MAP = 6, /* MCI_GPM_COEX_BT_PROFILE_INFO */ MCI_GPM_COEX_B_PROFILE_TYPE = 6, MCI_GPM_COEX_B_PROFILE_LINKID = 7, MCI_GPM_COEX_B_PROFILE_STATE = 8, MCI_GPM_COEX_B_PROFILE_ROLE = 9, MCI_GPM_COEX_B_PROFILE_RATE = 10, MCI_GPM_COEX_B_PROFILE_VOTYPE = 11, MCI_GPM_COEX_H_PROFILE_T = 12, MCI_GPM_COEX_B_PROFILE_W = 14, MCI_GPM_COEX_B_PROFILE_A = 15, /* MCI_GPM_COEX_BT_STATUS_UPDATE */ MCI_GPM_COEX_B_STATUS_TYPE = 6, MCI_GPM_COEX_B_STATUS_LINKID = 7, MCI_GPM_COEX_B_STATUS_STATE = 8, /* MCI_GPM_COEX_BT_UPDATE_FLAGS */ MCI_GPM_COEX_B_BT_FLAGS_OP = 10, MCI_GPM_COEX_W_BT_FLAGS = 6 }; #define MCI_GPM_RECYCLE(_p_gpm) \ { \ *(((u_int32_t *)(_p_gpm)) + MCI_GPM_COEX_W_GPM_PAYLOAD) = MCI_GPM_RSVD_PATTERN32; \ } #define MCI_GPM_TYPE(_p_gpm) \ (*(((u_int8_t *)(_p_gpm)) + MCI_GPM_COEX_B_GPM_TYPE) & 0xff) #define MCI_GPM_OPCODE(_p_gpm) \ (*(((u_int8_t *)(_p_gpm)) + MCI_GPM_COEX_B_GPM_OPCODE) & 0xff) #define MCI_GPM_SET_CAL_TYPE(_p_gpm, _cal_type) \ { \ *(((u_int8_t *)(_p_gpm)) + MCI_GPM_COEX_B_GPM_TYPE) = (_cal_type) & 0xff; \ } #define MCI_GPM_SET_TYPE_OPCODE(_p_gpm, _type, _opcode) \ { \ *(((u_int8_t *)(_p_gpm)) + MCI_GPM_COEX_B_GPM_TYPE) = (_type) & 0xff; \ *(((u_int8_t *)(_p_gpm)) + MCI_GPM_COEX_B_GPM_OPCODE) = (_opcode) & 0xff; \ } #define MCI_GPM_IS_CAL_TYPE(_type) ((_type) <= MCI_GPM_WLAN_CAL_DONE) #define MCI_NUM_BT_CHANNELS 79 #define MCI_GPM_SET_CHANNEL_BIT(_p_gpm, _bt_chan) \ { \ if (_bt_chan < MCI_NUM_BT_CHANNELS) { \ *(((u_int8_t *)(_p_gpm)) + MCI_GPM_COEX_B_CHANNEL_MAP + \ (_bt_chan / 8)) |= 1 << (_bt_chan & 7); \ } \ } #define MCI_GPM_CLR_CHANNEL_BIT(_p_gpm, _bt_chan) \ { \ if (_bt_chan < MCI_NUM_BT_CHANNELS) { \ *(((u_int8_t *)(_p_gpm)) + MCI_GPM_COEX_B_CHANNEL_MAP + \ (_bt_chan / 8)) &= ~(1 << (_bt_chan & 7)); \ } \ } #define HAL_MCI_INTERRUPT_SW_MSG_DONE 0x00000001 #define HAL_MCI_INTERRUPT_CPU_INT_MSG 0x00000002 #define HAL_MCI_INTERRUPT_RX_CHKSUM_FAIL 0x00000004 #define HAL_MCI_INTERRUPT_RX_INVALID_HDR 0x00000008 #define HAL_MCI_INTERRUPT_RX_HW_MSG_FAIL 0x00000010 #define HAL_MCI_INTERRUPT_RX_SW_MSG_FAIL 0x00000020 #define HAL_MCI_INTERRUPT_TX_HW_MSG_FAIL 0x00000080 #define HAL_MCI_INTERRUPT_TX_SW_MSG_FAIL 0x00000100 #define HAL_MCI_INTERRUPT_RX_MSG 0x00000200 #define HAL_MCI_INTERRUPT_REMOTE_SLEEP_UPDATE 0x00000400 #define HAL_MCI_INTERRUPT_CONT_INFO_TIMEOUT 0x80000000 #define HAL_MCI_INTERRUPT_MSG_FAIL_MASK ( HAL_MCI_INTERRUPT_RX_HW_MSG_FAIL | \ HAL_MCI_INTERRUPT_RX_SW_MSG_FAIL | \ HAL_MCI_INTERRUPT_TX_HW_MSG_FAIL | \ HAL_MCI_INTERRUPT_TX_SW_MSG_FAIL ) #define HAL_MCI_INTERRUPT_RX_MSG_REMOTE_RESET 0x00000001 #define HAL_MCI_INTERRUPT_RX_MSG_LNA_CONTROL 0x00000002 #define HAL_MCI_INTERRUPT_RX_MSG_CONT_NACK 0x00000004 #define HAL_MCI_INTERRUPT_RX_MSG_CONT_INFO 0x00000008 #define HAL_MCI_INTERRUPT_RX_MSG_CONT_RST 0x00000010 #define HAL_MCI_INTERRUPT_RX_MSG_SCHD_INFO 0x00000020 #define HAL_MCI_INTERRUPT_RX_MSG_CPU_INT 0x00000040 #define HAL_MCI_INTERRUPT_RX_MSG_GPM 0x00000100 #define HAL_MCI_INTERRUPT_RX_MSG_LNA_INFO 0x00000200 #define HAL_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING 0x00000400 #define HAL_MCI_INTERRUPT_RX_MSG_SYS_WAKING 0x00000800 #define HAL_MCI_INTERRUPT_RX_MSG_REQ_WAKE 0x00001000 #define HAL_MCI_INTERRUPT_RX_MSG_MONITOR (HAL_MCI_INTERRUPT_RX_MSG_LNA_CONTROL | \ HAL_MCI_INTERRUPT_RX_MSG_LNA_INFO | \ HAL_MCI_INTERRUPT_RX_MSG_CONT_NACK | \ HAL_MCI_INTERRUPT_RX_MSG_CONT_INFO | \ HAL_MCI_INTERRUPT_RX_MSG_CONT_RST) typedef enum mci_bt_state { MCI_BT_SLEEP, MCI_BT_AWAKE, MCI_BT_CAL_START, MCI_BT_CAL } MCI_BT_STATE_T; /* Type of state query */ typedef enum mci_state_type { HAL_MCI_STATE_ENABLE, HAL_MCI_STATE_INIT_GPM_OFFSET, HAL_MCI_STATE_NEXT_GPM_OFFSET, HAL_MCI_STATE_LAST_GPM_OFFSET, HAL_MCI_STATE_BT, HAL_MCI_STATE_SET_BT_SLEEP, HAL_MCI_STATE_SET_BT_AWAKE, HAL_MCI_STATE_SET_BT_CAL_START, HAL_MCI_STATE_SET_BT_CAL, HAL_MCI_STATE_LAST_SCHD_MSG_OFFSET, HAL_MCI_STATE_REMOTE_SLEEP, HAL_MCI_STATE_CONT_RSSI_POWER, HAL_MCI_STATE_CONT_PRIORITY, HAL_MCI_STATE_CONT_TXRX, HAL_MCI_STATE_RESET_REQ_WAKE, HAL_MCI_STATE_SEND_WLAN_COEX_VERSION, HAL_MCI_STATE_SET_BT_COEX_VERSION, HAL_MCI_STATE_SEND_WLAN_CHANNELS, HAL_MCI_STATE_SEND_VERSION_QUERY, HAL_MCI_STATE_SEND_STATUS_QUERY, HAL_MCI_STATE_NEED_FLUSH_BT_INFO, HAL_MCI_STATE_SET_CONCUR_TX_PRI, HAL_MCI_STATE_RECOVER_RX, HAL_MCI_STATE_NEED_FTP_STOMP, HAL_MCI_STATE_NEED_TUNING, HAL_MCI_STATE_SHARED_CHAIN_CONCUR_TX, HAL_MCI_STATE_DEBUG, HAL_MCI_STATE_MAX } HAL_MCI_STATE_TYPE; #define HAL_MCI_STATE_DEBUG_REQ_BT_DEBUG 1 #define HAL_MCI_BT_MCI_FLAGS_UPDATE_CORR 0x00000002 #define HAL_MCI_BT_MCI_FLAGS_UPDATE_HDR 0x00000004 #define HAL_MCI_BT_MCI_FLAGS_UPDATE_PLD 0x00000008 #define HAL_MCI_BT_MCI_FLAGS_LNA_CTRL 0x00000010 #define HAL_MCI_BT_MCI_FLAGS_DEBUG 0x00000020 #define HAL_MCI_BT_MCI_FLAGS_SCHED_MSG 0x00000040 #define HAL_MCI_BT_MCI_FLAGS_CONT_MSG 0x00000080 #define HAL_MCI_BT_MCI_FLAGS_COEX_GPM 0x00000100 #define HAL_MCI_BT_MCI_FLAGS_CPU_INT_MSG 0x00000200 #define HAL_MCI_BT_MCI_FLAGS_MCI_MODE 0x00000400 #define HAL_MCI_BT_MCI_FLAGS_EGRET_MODE 0x00000800 #define HAL_MCI_BT_MCI_FLAGS_JUPITER_MODE 0x00001000 #define HAL_MCI_BT_MCI_FLAGS_OTHER 0x00010000 #define HAL_MCI_DEFAULT_BT_MCI_FLAGS 0x00011dde /* HAL_MCI_BT_MCI_FLAGS_UPDATE_CORR = 1 HAL_MCI_BT_MCI_FLAGS_UPDATE_HDR = 1 HAL_MCI_BT_MCI_FLAGS_UPDATE_PLD = 1 HAL_MCI_BT_MCI_FLAGS_LNA_CTRL = 1 HAL_MCI_BT_MCI_FLAGS_DEBUG = 0 HAL_MCI_BT_MCI_FLAGS_SCHED_MSG = 1 HAL_MCI_BT_MCI_FLAGS_CONT_MSG = 1 HAL_MCI_BT_MCI_FLAGS_COEX_GPM = 1 HAL_MCI_BT_MCI_FLAGS_CPU_INT_MSG = 0 HAL_MCI_BT_MCI_FLAGS_MCI_MODE = 1 HAL_MCI_BT_MCI_FLAGS_EGRET_MODE = 1 HAL_MCI_BT_MCI_FLAGS_JUPITER_MODE = 1 HAL_MCI_BT_MCI_FLAGS_OTHER = 1 */ #define HAL_MCI_TOGGLE_BT_MCI_FLAGS \ ( HAL_MCI_BT_MCI_FLAGS_UPDATE_CORR | \ HAL_MCI_BT_MCI_FLAGS_UPDATE_HDR | \ HAL_MCI_BT_MCI_FLAGS_UPDATE_PLD | \ HAL_MCI_BT_MCI_FLAGS_MCI_MODE ) #define HAL_MCI_2G_FLAGS_CLEAR_MASK 0x00000000 #define HAL_MCI_2G_FLAGS_SET_MASK HAL_MCI_TOGGLE_BT_MCI_FLAGS #define HAL_MCI_2G_FLAGS HAL_MCI_DEFAULT_BT_MCI_FLAGS #define HAL_MCI_5G_FLAGS_CLEAR_MASK HAL_MCI_TOGGLE_BT_MCI_FLAGS #define HAL_MCI_5G_FLAGS_SET_MASK 0x00000000 #define HAL_MCI_5G_FLAGS (HAL_MCI_DEFAULT_BT_MCI_FLAGS & \ ~HAL_MCI_TOGGLE_BT_MCI_FLAGS) #define HAL_MCI_GPM_NOMORE 0 #define HAL_MCI_GPM_MORE 1 #define HAL_MCI_GPM_INVALID 0xffffffff #define ATH_AIC_MAX_BT_CHANNEL 79 /* * Default value for Jupiter is 0x00002201 * Default value for Aphrodite is 0x00002282 */ #define ATH_MCI_CONFIG_CONCUR_TX 0x00000003 #define ATH_MCI_CONFIG_MCI_OBS_MCI 0x00000004 #define ATH_MCI_CONFIG_MCI_OBS_TXRX 0x00000008 #define ATH_MCI_CONFIG_MCI_OBS_BT 0x00000010 #define ATH_MCI_CONFIG_DISABLE_MCI_CAL 0x00000020 #define ATH_MCI_CONFIG_DISABLE_OSLA 0x00000040 #define ATH_MCI_CONFIG_DISABLE_FTP_STOMP 0x00000080 #define ATH_MCI_CONFIG_AGGR_THRESH 0x00000700 #define ATH_MCI_CONFIG_AGGR_THRESH_S 8 #define ATH_MCI_CONFIG_DISABLE_AGGR_THRESH 0x00000800 #define ATH_MCI_CONFIG_CLK_DIV 0x00003000 #define ATH_MCI_CONFIG_CLK_DIV_S 12 #define ATH_MCI_CONFIG_DISABLE_TUNING 0x00004000 #define ATH_MCI_CONFIG_MCI_WEIGHT_DBG 0x40000000 #define ATH_MCI_CONFIG_DISABLE_MCI 0x80000000 #define ATH_MCI_CONFIG_MCI_OBS_MASK ( ATH_MCI_CONFIG_MCI_OBS_MCI | \ ATH_MCI_CONFIG_MCI_OBS_TXRX | \ ATH_MCI_CONFIG_MCI_OBS_BT ) #define ATH_MCI_CONFIG_MCI_OBS_GPIO 0x0000002F #define ATH_MCI_CONCUR_TX_SHARED_CHN 0x01 #define ATH_MCI_CONCUR_TX_UNSHARED_CHN 0x02 #define ATH_MCI_CONCUR_TX_DEBUG 0x03 /* * The values below come from the system team test result. * For Jupiter, BT tx power level is from 0(-20dBm) to 6(4dBm). * Lowest WLAN tx power would be in bit[23:16] of dword 1. */ static const u_int32_t mci_concur_tx_max_pwr[4][8] = { /* No limit */ {0x7f7f7f7f, 0x7f7f7f7f, 0x7f7f7f7f, 0x7f7f7f7f, 0x7f7f7f7f, 0x7f7f7f7f, 0x7f7f7f7f, 0x7f7f7f7f}, /* 11G */ {0x16161616, 0x12121516, 0x12121212, 0x12121212, 0x12121212, 0x12121212, 0x12121212, 0x7f121212}, /* HT20 */ {0x15151515, 0x14141515, 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0x7f141414}, /* HT40 */ {0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x7f101010}}; #define ATH_MCI_CONCUR_TX_LOWEST_PWR_MASK 0x00ff0000 #define ATH_MCI_CONCUR_TX_LOWEST_PWR_MASK_S 16 #endif /* __AR9300_FREEBSD_INC_H__ */ Index: projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_gpio.c =================================================================== --- projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_gpio.c (revision 278776) +++ projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_gpio.c (revision 278777) @@ -1,647 +1,640 @@ /* * Copyright (c) 2013 Qualcomm Atheros, Inc. * * 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 "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ah_devid.h" #ifdef AH_DEBUG #include "ah_desc.h" /* NB: for HAL_PHYERR* */ #endif #include "ar9300/ar9300.h" #include "ar9300/ar9300reg.h" #include "ar9300/ar9300phy.h" #define AR_GPIO_BIT(_gpio) (1 << (_gpio)) /* * Configure GPIO Output Mux control */ #if UMAC_SUPPORT_SMARTANTENNA static void ar9340_soc_gpio_cfg_output_mux( struct ath_hal *ah, u_int32_t gpio, u_int32_t ah_signal_type) { #define ADDR_READ(addr) (*((volatile u_int32_t *)(addr))) #define ADDR_WRITE(addr, b) (void)((*(volatile u_int32_t *) (addr)) = (b)) #define AR9340_SOC_GPIO_FUN0 0xB804002c #define AR9340_SOC_GPIO_OE 0xB8040000 #if ATH_SMARTANTENNA_DISABLE_JTAG #define AR9340_SOC_GPIO_FUNCTION (volatile u_int32_t*) 0xB804006c #define WASP_DISABLE_JTAG 0x2 #define MAX_JTAG_GPIO_PIN 1 #endif u_int8_t out_func, shift; u_int32_t flags; volatile u_int32_t* address; if (!ah_signal_type){ return; } #if ATH_SMARTANTENNA_DISABLE_JTAG /* * To use GPIO pins 0 and 1 for controling antennas, JTAG needs to disabled. */ if (gpio <= MAX_JTAG_GPIO_PIN) { flags = ADDR_READ(AR9340_SOC_GPIO_FUNCTION); flags |= WASP_DISABLE_JTAG; ADDR_WRITE(AR9340_SOC_GPIO_FUNCTION, flags); } #endif out_func = gpio / 4; shift = (gpio % 4); address = (volatile u_int32_t *)(AR9340_SOC_GPIO_FUN0 + (out_func*4)); flags = ADDR_READ(address); flags |= ah_signal_type << (8*shift); ADDR_WRITE(address, flags); flags = ADDR_READ(AR9340_SOC_GPIO_OE); flags &= ~(1 << gpio); ADDR_WRITE(AR9340_SOC_GPIO_OE, flags); } #endif static void ar9300_gpio_cfg_output_mux(struct ath_hal *ah, u_int32_t gpio, u_int32_t type) { int addr; u_int32_t gpio_shift; /* each MUX controls 6 GPIO pins */ if (gpio > 11) { addr = AR_HOSTIF_REG(ah, AR_GPIO_OUTPUT_MUX3); } else if (gpio > 5) { addr = AR_HOSTIF_REG(ah, AR_GPIO_OUTPUT_MUX2); } else { addr = AR_HOSTIF_REG(ah, AR_GPIO_OUTPUT_MUX1); } /* * 5 bits per GPIO pin. * Bits 0..4 for 1st pin in that mux, * bits 5..9 for 2nd pin, etc. */ gpio_shift = (gpio % 6) * 5; OS_REG_RMW(ah, addr, (type << gpio_shift), (0x1f << gpio_shift)); } /* * Configure GPIO Output lines */ HAL_BOOL ar9300_gpio_cfg_output( struct ath_hal *ah, u_int32_t gpio, HAL_GPIO_MUX_TYPE hal_signal_type) { u_int32_t ah_signal_type; u_int32_t gpio_shift; u_int8_t smart_ant = 0; static const u_int32_t mux_signal_conversion_table[] = { /* HAL_GPIO_OUTPUT_MUX_AS_OUTPUT */ AR_GPIO_OUTPUT_MUX_AS_OUTPUT, /* HAL_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED */ AR_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED, /* HAL_GPIO_OUTPUT_MUX_AS_PCIE_POWER_LED */ AR_GPIO_OUTPUT_MUX_AS_PCIE_POWER_LED, /* HAL_GPIO_OUTPUT_MUX_AS_MAC_NETWORK_LED */ AR_GPIO_OUTPUT_MUX_AS_MAC_NETWORK_LED, /* HAL_GPIO_OUTPUT_MUX_AS_MAC_POWER_LED */ AR_GPIO_OUTPUT_MUX_AS_MAC_POWER_LED, /* HAL_GPIO_OUTPUT_MUX_AS_WLAN_ACTIVE */ AR_GPIO_OUTPUT_MUX_AS_RX_CLEAR_EXTERNAL, /* HAL_GPIO_OUTPUT_MUX_AS_TX_FRAME */ AR_GPIO_OUTPUT_MUX_AS_TX_FRAME, /* HAL_GPIO_OUTPUT_MUX_AS_MCI_WLAN_DATA */ AR_GPIO_OUTPUT_MUX_AS_MCI_WLAN_DATA, /* HAL_GPIO_OUTPUT_MUX_AS_MCI_WLAN_CLK */ AR_GPIO_OUTPUT_MUX_AS_MCI_WLAN_CLK, /* HAL_GPIO_OUTPUT_MUX_AS_MCI_BT_DATA */ AR_GPIO_OUTPUT_MUX_AS_MCI_BT_DATA, /* HAL_GPIO_OUTPUT_MUX_AS_MCI_BT_CLK */ AR_GPIO_OUTPUT_MUX_AS_MCI_BT_CLK, /* HAL_GPIO_OUTPUT_MUX_AS_WL_IN_TX */ AR_GPIO_OUTPUT_MUX_AS_WL_IN_TX, /* HAL_GPIO_OUTPUT_MUX_AS_WL_IN_RX */ AR_GPIO_OUTPUT_MUX_AS_WL_IN_RX, /* HAL_GPIO_OUTPUT_MUX_AS_BT_IN_TX */ AR_GPIO_OUTPUT_MUX_AS_BT_IN_TX, /* HAL_GPIO_OUTPUT_MUX_AS_BT_IN_RX */ AR_GPIO_OUTPUT_MUX_AS_BT_IN_RX, /* HAL_GPIO_OUTPUT_MUX_AS_RUCKUS_STROBE */ AR_GPIO_OUTPUT_MUX_AS_RUCKUS_STROBE, /* HAL_GPIO_OUTPUT_MUX_AS_RUCKUS_DATA */ AR_GPIO_OUTPUT_MUX_AS_RUCKUS_DATA, /* HAL_GPIO_OUTPUT_MUX_AS_SMARTANT_CTRL0 */ AR_GPIO_OUTPUT_MUX_AS_SMARTANT_CTRL0, /* HAL_GPIO_OUTPUT_MUX_AS_SMARTANT_CTRL1 */ AR_GPIO_OUTPUT_MUX_AS_SMARTANT_CTRL1, /* HAL_GPIO_OUTPUT_MUX_AS_SMARTANT_CTRL2 */ AR_GPIO_OUTPUT_MUX_AS_SMARTANT_CTRL2, /* HAL_GPIO_OUTPUT_MUX_AS_SMARTANT_SWCOM3 */ AR_GPIO_OUTPUT_MUX_AS_SWCOM3, }; HALASSERT(gpio < AH_PRIVATE(ah)->ah_caps.halNumGpioPins); if ((gpio == AR9382_GPIO_PIN_8_RESERVED) || - (gpio == AR9382_GPIO_PIN_11_RESERVED) || (gpio == AR9382_GPIO_9_INPUT_ONLY)) { return AH_FALSE; } /* Convert HAL signal type definitions to hardware-specific values. */ if ((int) hal_signal_type < ARRAY_LENGTH(mux_signal_conversion_table)) { ah_signal_type = mux_signal_conversion_table[hal_signal_type]; } else { return AH_FALSE; } if (gpio <= AR9382_MAX_JTAG_GPIO_PIN_NUM) { OS_REG_SET_BIT(ah, AR_HOSTIF_REG(ah, AR_GPIO_INPUT_EN_VAL), AR_GPIO_JTAG_DISABLE); } #if UMAC_SUPPORT_SMARTANTENNA /* Get the pin and func values for smart antenna */ switch (ah_signal_type) { case AR_GPIO_OUTPUT_MUX_AS_SMARTANT_CTRL0: gpio = ATH_GPIOPIN_ANTCHAIN0; ah_signal_type = ATH_GPIOFUNC_ANTCHAIN0; smart_ant = 1; break; case AR_GPIO_OUTPUT_MUX_AS_SMARTANT_CTRL1: gpio = ATH_GPIOPIN_ANTCHAIN1; ah_signal_type = ATH_GPIOFUNC_ANTCHAIN1; smart_ant = 1; break; case AR_GPIO_OUTPUT_MUX_AS_SMARTANT_CTRL2: gpio = ATH_GPIOPIN_ANTCHAIN2; ah_signal_type = ATH_GPIOFUNC_ANTCHAIN2; smart_ant = 1; break; #if ATH_SMARTANTENNA_ROUTE_SWCOM_TO_GPIO case AR_GPIO_OUTPUT_MUX_AS_SWCOM3: gpio = ATH_GPIOPIN_ROUTE_SWCOM3; ah_signal_type = ATH_GPIOFUNC_ROUTE_SWCOM3; smart_ant = 1; break; #endif default: break; } #endif if (smart_ant && (AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah))) { #if UMAC_SUPPORT_SMARTANTENNA ar9340_soc_gpio_cfg_output_mux(ah, gpio, ah_signal_type); #endif return AH_TRUE; } else { /* Configure the MUX */ ar9300_gpio_cfg_output_mux(ah, gpio, ah_signal_type); } /* 2 bits per output mode */ gpio_shift = 2 * gpio; OS_REG_RMW(ah, AR_HOSTIF_REG(ah, AR_GPIO_OE_OUT), (AR_GPIO_OE_OUT_DRV_ALL << gpio_shift), (AR_GPIO_OE_OUT_DRV << gpio_shift)); return AH_TRUE; } /* * Configure GPIO Output lines -LED off */ HAL_BOOL ar9300_gpio_cfg_output_led_off( struct ath_hal *ah, u_int32_t gpio, HAL_GPIO_MUX_TYPE halSignalType) { #define N(a) (sizeof(a) / sizeof(a[0])) u_int32_t ah_signal_type; u_int32_t gpio_shift; u_int8_t smart_ant = 0; static const u_int32_t mux_signal_conversion_table[] = { /* HAL_GPIO_OUTPUT_MUX_AS_OUTPUT */ AR_GPIO_OUTPUT_MUX_AS_OUTPUT, /* HAL_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED */ AR_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED, /* HAL_GPIO_OUTPUT_MUX_AS_PCIE_POWER_LED */ AR_GPIO_OUTPUT_MUX_AS_PCIE_POWER_LED, /* HAL_GPIO_OUTPUT_MUX_AS_MAC_NETWORK_LED */ AR_GPIO_OUTPUT_MUX_AS_MAC_NETWORK_LED, /* HAL_GPIO_OUTPUT_MUX_AS_MAC_POWER_LED */ AR_GPIO_OUTPUT_MUX_AS_MAC_POWER_LED, /* HAL_GPIO_OUTPUT_MUX_AS_WLAN_ACTIVE */ AR_GPIO_OUTPUT_MUX_AS_RX_CLEAR_EXTERNAL, /* HAL_GPIO_OUTPUT_MUX_AS_TX_FRAME */ AR_GPIO_OUTPUT_MUX_AS_TX_FRAME, /* HAL_GPIO_OUTPUT_MUX_AS_MCI_WLAN_DATA */ AR_GPIO_OUTPUT_MUX_AS_MCI_WLAN_DATA, /* HAL_GPIO_OUTPUT_MUX_AS_MCI_WLAN_CLK */ AR_GPIO_OUTPUT_MUX_AS_MCI_WLAN_CLK, /* HAL_GPIO_OUTPUT_MUX_AS_MCI_BT_DATA */ AR_GPIO_OUTPUT_MUX_AS_MCI_BT_DATA, /* HAL_GPIO_OUTPUT_MUX_AS_MCI_BT_CLK */ AR_GPIO_OUTPUT_MUX_AS_MCI_BT_CLK, /* HAL_GPIO_OUTPUT_MUX_AS_WL_IN_TX */ AR_GPIO_OUTPUT_MUX_AS_WL_IN_TX, /* HAL_GPIO_OUTPUT_MUX_AS_WL_IN_RX */ AR_GPIO_OUTPUT_MUX_AS_WL_IN_RX, /* HAL_GPIO_OUTPUT_MUX_AS_BT_IN_TX */ AR_GPIO_OUTPUT_MUX_AS_BT_IN_TX, /* HAL_GPIO_OUTPUT_MUX_AS_BT_IN_RX */ AR_GPIO_OUTPUT_MUX_AS_BT_IN_RX, AR_GPIO_OUTPUT_MUX_AS_RUCKUS_STROBE, AR_GPIO_OUTPUT_MUX_AS_RUCKUS_DATA, AR_GPIO_OUTPUT_MUX_AS_SMARTANT_CTRL0, AR_GPIO_OUTPUT_MUX_AS_SMARTANT_CTRL1, AR_GPIO_OUTPUT_MUX_AS_SMARTANT_CTRL2 }; HALASSERT(gpio < AH_PRIVATE(ah)->ah_caps.hal_num_gpio_pins); /* Convert HAL signal type definitions to hardware-specific values. */ if ((int) halSignalType < ARRAY_LENGTH(mux_signal_conversion_table)) { ah_signal_type = mux_signal_conversion_table[halSignalType]; } else { return AH_FALSE; } #if UMAC_SUPPORT_SMARTANTENNA /* Get the pin and func values for smart antenna */ switch (halSignalType) { case AR_GPIO_OUTPUT_MUX_AS_SMARTANT_CTRL0: gpio = ATH_GPIOPIN_ANTCHAIN0; ah_signal_type = ATH_GPIOFUNC_ANTCHAIN0; smart_ant = 1; break; case AR_GPIO_OUTPUT_MUX_AS_SMARTANT_CTRL1: gpio = ATH_GPIOPIN_ANTCHAIN1; ah_signal_type = ATH_GPIOFUNC_ANTCHAIN1; smart_ant = 1; break; case AR_GPIO_OUTPUT_MUX_AS_SMARTANT_CTRL2: gpio = ATH_GPIOPIN_ANTCHAIN2; ah_signal_type = ATH_GPIOFUNC_ANTCHAIN2; smart_ant = 1; break; default: break; } #endif if (smart_ant && AR_SREV_WASP(ah)) { return AH_FALSE; } // Configure the MUX ar9300_gpio_cfg_output_mux(ah, gpio, ah_signal_type); // 2 bits per output mode gpio_shift = 2*gpio; OS_REG_RMW(ah, AR_HOSTIF_REG(ah, AR_GPIO_OE_OUT), (AR_GPIO_OE_OUT_DRV_NO << gpio_shift), (AR_GPIO_OE_OUT_DRV << gpio_shift)); return AH_TRUE; #undef N } /* * Configure GPIO Input lines */ HAL_BOOL ar9300_gpio_cfg_input(struct ath_hal *ah, u_int32_t gpio) { u_int32_t gpio_shift; HALASSERT(gpio < AH_PRIVATE(ah)->ah_caps.halNumGpioPins); if ((gpio == AR9382_GPIO_PIN_8_RESERVED) || - (gpio == AR9382_GPIO_PIN_11_RESERVED) || (gpio > AR9382_MAX_GPIO_INPUT_PIN_NUM)) { return AH_FALSE; } if (gpio <= AR9382_MAX_JTAG_GPIO_PIN_NUM) { OS_REG_SET_BIT(ah, AR_HOSTIF_REG(ah, AR_GPIO_INPUT_EN_VAL), AR_GPIO_JTAG_DISABLE); } /* TODO: configure input mux for AR9300 */ /* If configured as input, set output to tristate */ gpio_shift = 2 * gpio; OS_REG_RMW(ah, AR_HOSTIF_REG(ah, AR_GPIO_OE_OUT), (AR_GPIO_OE_OUT_DRV_NO << gpio_shift), (AR_GPIO_OE_OUT_DRV << gpio_shift)); return AH_TRUE; } /* * Once configured for I/O - set output lines * output the level of GPio PIN without care work mode */ HAL_BOOL ar9300_gpio_set(struct ath_hal *ah, u_int32_t gpio, u_int32_t val) { HALASSERT(gpio < AH_PRIVATE(ah)->ah_caps.halNumGpioPins); if ((gpio == AR9382_GPIO_PIN_8_RESERVED) || - (gpio == AR9382_GPIO_PIN_11_RESERVED) || (gpio == AR9382_GPIO_9_INPUT_ONLY)) { return AH_FALSE; } OS_REG_RMW(ah, AR_HOSTIF_REG(ah, AR_GPIO_OUT), ((val & 1) << gpio), AR_GPIO_BIT(gpio)); return AH_TRUE; } /* * Once configured for I/O - get input lines */ u_int32_t ar9300_gpio_get(struct ath_hal *ah, u_int32_t gpio) { u_int32_t gpio_in; HALASSERT(gpio < AH_PRIVATE(ah)->ah_caps.halNumGpioPins); - if ((gpio == AR9382_GPIO_PIN_8_RESERVED) || - (gpio == AR9382_GPIO_PIN_11_RESERVED)) + if (gpio == AR9382_GPIO_PIN_8_RESERVED) { return 0xffffffff; } gpio_in = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_GPIO_IN)); OS_REG_RMW(ah, AR_HOSTIF_REG(ah, AR_GPIO_IN), (1 << gpio), AR_GPIO_BIT(gpio)); return (MS(gpio_in, AR_GPIO_IN_VAL) & AR_GPIO_BIT(gpio)) != 0; } u_int32_t ar9300_gpio_get_intr(struct ath_hal *ah) { unsigned int mask = 0; struct ath_hal_9300 *ahp = AH9300(ah); mask = ahp->ah_gpio_cause; return mask; } /* * Set the GPIO Interrupt * Sync and Async interrupts are both set/cleared. * Async GPIO interrupts may not be raised when the chip is put to sleep. */ void ar9300_gpio_set_intr(struct ath_hal *ah, u_int gpio, u_int32_t ilevel) { int i, reg_bit; u_int32_t reg_val; u_int32_t regs[2], shifts[2]; #ifdef AH_ASSERT u_int32_t gpio_mask; u_int32_t old_field_val = 0, field_val = 0; #endif #ifdef ATH_GPIO_USE_ASYNC_CAUSE regs[0] = AR_HOSTIF_REG(ah, AR_INTR_ASYNC_ENABLE); regs[1] = AR_HOSTIF_REG(ah, AR_INTR_ASYNC_MASK); shifts[0] = AR_INTR_ASYNC_ENABLE_GPIO_S; shifts[1] = AR_INTR_ASYNC_MASK_GPIO_S; #else regs[0] = AR_HOSTIF_REG(ah, AR_INTR_SYNC_ENABLE); regs[1] = AR_HOSTIF_REG(ah, AR_INTR_SYNC_MASK); shifts[0] = AR_INTR_SYNC_ENABLE_GPIO_S; shifts[1] = AR_INTR_SYNC_MASK_GPIO_S; #endif HALASSERT(gpio < AH_PRIVATE(ah)->ah_caps.halNumGpioPins); if ((gpio == AR9382_GPIO_PIN_8_RESERVED) || - (gpio == AR9382_GPIO_PIN_11_RESERVED) || (gpio > AR9382_MAX_GPIO_INPUT_PIN_NUM)) { return; } #ifdef AH_ASSERT gpio_mask = (1 << AH_PRIVATE(ah)->ah_caps.halNumGpioPins) - 1; #endif if (ilevel == HAL_GPIO_INTR_DISABLE) { /* clear this GPIO's bit in the interrupt registers */ for (i = 0; i < ARRAY_LENGTH(regs); i++) { reg_val = OS_REG_READ(ah, regs[i]); reg_bit = shifts[i] + gpio; reg_val &= ~(1 << reg_bit); OS_REG_WRITE(ah, regs[i], reg_val); /* check that each register has same GPIOs enabled */ #ifdef AH_ASSERT field_val = (reg_val >> shifts[i]) & gpio_mask; HALASSERT(i == 0 || old_field_val == field_val); old_field_val = field_val; #endif } } else { reg_val = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_GPIO_INTR_POL)); reg_bit = gpio; if (ilevel == HAL_GPIO_INTR_HIGH) { /* 0 == interrupt on pin high */ reg_val &= ~(1 << reg_bit); } else if (ilevel == HAL_GPIO_INTR_LOW) { /* 1 == interrupt on pin low */ reg_val |= (1 << reg_bit); } OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_GPIO_INTR_POL), reg_val); /* set this GPIO's bit in the interrupt registers */ for (i = 0; i < ARRAY_LENGTH(regs); i++) { reg_val = OS_REG_READ(ah, regs[i]); reg_bit = shifts[i] + gpio; reg_val |= (1 << reg_bit); OS_REG_WRITE(ah, regs[i], reg_val); /* check that each register has same GPIOs enabled */ #ifdef AH_ASSERT field_val = (reg_val >> shifts[i]) & gpio_mask; HALASSERT(i == 0 || old_field_val == field_val); old_field_val = field_val; #endif } } } u_int32_t ar9300_gpio_get_polarity(struct ath_hal *ah) { return OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_GPIO_INTR_POL)); } void ar9300_gpio_set_polarity(struct ath_hal *ah, u_int32_t pol_map, u_int32_t changed_mask) { u_int32_t gpio_mask; gpio_mask = (1 << AH_PRIVATE(ah)->ah_caps.halNumGpioPins) - 1; OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_GPIO_INTR_POL), gpio_mask & pol_map); #ifndef ATH_GPIO_USE_ASYNC_CAUSE /* * For SYNC_CAUSE type interrupts, we need to clear the cause register * explicitly. Otherwise an interrupt with the original polarity setting * will come up immediately (if there is already an interrupt source), * which is not what we want usually. */ OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_SYNC_CAUSE_CLR), changed_mask << AR_INTR_SYNC_ENABLE_GPIO_S); OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_INTR_SYNC_CAUSE_CLR)); #endif } /* * get the GPIO input pin mask * gpio0 - gpio13 * gpio8, gpio11, regard as reserved by the chip ar9382 */ u_int32_t ar9300_gpio_get_mask(struct ath_hal *ah) { u_int32_t mask = (1 << (AR9382_MAX_GPIO_INPUT_PIN_NUM + 1) ) - 1; if (AH_PRIVATE(ah)->ah_devid == AR9300_DEVID_AR9380_PCIE) { mask = (1 << AR9382_MAX_GPIO_PIN_NUM) - 1; - mask &= ~(1 << AR9382_GPIO_PIN_8_RESERVED | - 1 << AR9382_GPIO_PIN_11_RESERVED); + mask &= ~(1 << AR9382_GPIO_PIN_8_RESERVED); } return mask; } int ar9300_gpio_set_mask(struct ath_hal *ah, u_int32_t mask, u_int32_t pol_map) { u_int32_t invalid = ~((1 << (AR9382_MAX_GPIO_INPUT_PIN_NUM + 1)) - 1); if (AH_PRIVATE(ah)->ah_devid == AR9300_DEVID_AR9380_PCIE) { invalid = ~((1 << AR9382_MAX_GPIO_PIN_NUM) - 1); - invalid |= 1 << AR9382_GPIO_PIN_8_RESERVED | - 1 << AR9382_GPIO_PIN_11_RESERVED; + invalid |= 1 << AR9382_GPIO_PIN_8_RESERVED; } if (mask & invalid) { ath_hal_printf(ah, "%s: invalid GPIO mask 0x%x\n", __func__, mask); return -1; } AH9300(ah)->ah_gpio_mask = mask; OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_GPIO_INTR_POL), mask & pol_map); return 0; } #ifdef AH_DEBUG void ar9300_gpio_show(struct ath_hal *ah); void ar9300_gpio_show(struct ath_hal *ah) { ath_hal_printf(ah, "--- 9382 GPIOs ---(ah=%p)\n", ah ); ath_hal_printf(ah, "AH9300(_ah)->ah_hostifregs:%p\r\n", &(AH9300(ah)->ah_hostifregs)); ath_hal_printf(ah, "GPIO_OUT: 0x%08X\n", OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_GPIO_OUT))); ath_hal_printf(ah, "GPIO_IN: 0x%08X\n", OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_GPIO_IN))); ath_hal_printf(ah, "GPIO_OE: 0x%08X\n", OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_GPIO_OE_OUT))); ath_hal_printf(ah, "GPIO_OE1_OUT: 0x%08X\n", OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_GPIO_OE1_OUT))); ath_hal_printf(ah, "GPIO_INTR_POLAR: 0x%08X\n", OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_GPIO_INTR_POL))); ath_hal_printf(ah, "GPIO_INPUT_VALUE: 0x%08X\n", OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_GPIO_INPUT_EN_VAL))); ath_hal_printf(ah, "GPIO_INPUT_MUX1: 0x%08X\n", OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_GPIO_INPUT_MUX1))); ath_hal_printf(ah, "GPIO_INPUT_MUX2: 0x%08X\n", OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_GPIO_INPUT_MUX2))); ath_hal_printf(ah, "GPIO_OUTPUT_MUX1: 0x%08X\n", OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_GPIO_OUTPUT_MUX1))); ath_hal_printf(ah, "GPIO_OUTPUT_MUX2: 0x%08X\n", OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_GPIO_OUTPUT_MUX2))); ath_hal_printf(ah, "GPIO_OUTPUT_MUX3: 0x%08X\n", OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_GPIO_OUTPUT_MUX3))); ath_hal_printf(ah, "GPIO_INPUT_STATE: 0x%08X\n", OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_INPUT_STATE))); ath_hal_printf(ah, "GPIO_PDPU: 0x%08X\n", OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_GPIO_PDPU))); ath_hal_printf(ah, "GPIO_DS: 0x%08X\n", OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_GPIO_DS))); ath_hal_printf(ah, "AR_INTR_ASYNC_ENABLE: 0x%08X\n", OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_INTR_ASYNC_ENABLE))); ath_hal_printf(ah, "AR_INTR_ASYNC_MASK: 0x%08X\n", OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_INTR_ASYNC_MASK))); ath_hal_printf(ah, "AR_INTR_SYNC_ENABLE: 0x%08X\n", OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_INTR_SYNC_ENABLE))); ath_hal_printf(ah, "AR_INTR_SYNC_MASK: 0x%08X\n", OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_INTR_SYNC_MASK))); ath_hal_printf(ah, "AR_INTR_ASYNC_CAUSE: 0x%08X\n", OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_INTR_ASYNC_CAUSE))); ath_hal_printf(ah, "AR_INTR_SYNC_CAUSE: 0x%08X\n", OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_INTR_SYNC_CAUSE))); } #endif /*AH_DEBUG*/ Index: projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_keycache.c =================================================================== --- projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_keycache.c (revision 278776) +++ projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_keycache.c (revision 278777) @@ -1,438 +1,591 @@ /* * Copyright (c) 2013 Qualcomm Atheros, Inc. * * 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 "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ar9300/ar9300.h" #include "ar9300/ar9300reg.h" /* * Note: The key cache hardware requires that each double-word * pair be written in even/odd order (since the destination is * a 64-bit register). Don't reorder the writes in this code * w/o considering this! */ #define KEY_XOR 0xaa #define IS_MIC_ENABLED(ah) \ (AH9300(ah)->ah_sta_id1_defaults & AR_STA_ID1_CRPT_MIC_ENABLE) /* * This isn't the keytable type; this is actually something separate * for the TX descriptor. */ static const int keyType[] = { 1, /* HAL_CIPHER_WEP */ 0, /* HAL_CIPHER_AES_OCB */ 2, /* HAL_CIPHER_AES_CCM */ 0, /* HAL_CIPHER_CKIP */ 3, /* HAL_CIPHER_TKIP */ 0 /* HAL_CIPHER_CLR */ }; /* * Return the size of the hardware key cache. */ u_int32_t ar9300_get_key_cache_size(struct ath_hal *ah) { return AH_PRIVATE(ah)->ah_caps.halKeyCacheSize; } /* * Return AH_TRUE if the specific key cache entry is valid. */ HAL_BOOL ar9300_is_key_cache_entry_valid(struct ath_hal *ah, u_int16_t entry) { if (entry < AH_PRIVATE(ah)->ah_caps.halKeyCacheSize) { u_int32_t val = OS_REG_READ(ah, AR_KEYTABLE_MAC1(entry)); if (val & AR_KEYTABLE_VALID) { return AH_TRUE; } } return AH_FALSE; } /* * Clear the specified key cache entry and any associated MIC entry. */ HAL_BOOL ar9300_reset_key_cache_entry(struct ath_hal *ah, u_int16_t entry) { u_int32_t key_type; struct ath_hal_9300 *ahp = AH9300(ah); if (entry >= AH_PRIVATE(ah)->ah_caps.halKeyCacheSize) { HALDEBUG(ah, HAL_DEBUG_KEYCACHE, "%s: entry %u out of range\n", __func__, entry); return AH_FALSE; } ahp->ah_keytype[entry] = keyType[HAL_CIPHER_CLR]; key_type = OS_REG_READ(ah, AR_KEYTABLE_TYPE(entry)); /* XXX why not clear key type/valid bit first? */ OS_REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), 0); OS_REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), 0); OS_REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), 0); OS_REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), 0); OS_REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), 0); OS_REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), AR_KEYTABLE_TYPE_CLR); OS_REG_WRITE(ah, AR_KEYTABLE_MAC0(entry), 0); OS_REG_WRITE(ah, AR_KEYTABLE_MAC1(entry), 0); if (key_type == AR_KEYTABLE_TYPE_TKIP && IS_MIC_ENABLED(ah)) { u_int16_t micentry = entry + 64; /* MIC goes at slot+64 */ HALASSERT(micentry < AH_PRIVATE(ah)->ah_caps.halKeyCacheSize); OS_REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), 0); OS_REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), 0); OS_REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), 0); OS_REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), 0); /* NB: key type and MAC are known to be ok */ } if (AH_PRIVATE(ah)->ah_curchan == AH_NULL) { return AH_TRUE; } if (ar9300_get_capability(ah, HAL_CAP_BB_RIFS_HANG, 0, AH_NULL) == HAL_OK) { if (key_type == AR_KEYTABLE_TYPE_TKIP || key_type == AR_KEYTABLE_TYPE_40 || key_type == AR_KEYTABLE_TYPE_104 || key_type == AR_KEYTABLE_TYPE_128) { /* SW WAR for Bug 31602 */ if (--ahp->ah_rifs_sec_cnt == 0) { HALDEBUG(ah, HAL_DEBUG_KEYCACHE, "%s: Count = %d, enabling RIFS\n", __func__, ahp->ah_rifs_sec_cnt); ar9300_set_rifs_delay(ah, AH_TRUE); } } } return AH_TRUE; } /* * Sets the mac part of the specified key cache entry (and any * associated MIC entry) and mark them valid. */ HAL_BOOL ar9300_set_key_cache_entry_mac( struct ath_hal *ah, u_int16_t entry, const u_int8_t *mac) { u_int32_t mac_hi, mac_lo; u_int32_t unicast_addr = AR_KEYTABLE_VALID; if (entry >= AH_PRIVATE(ah)->ah_caps.halKeyCacheSize) { HALDEBUG(ah, HAL_DEBUG_KEYCACHE, "%s: entry %u out of range\n", __func__, entry); return AH_FALSE; } /* * Set MAC address -- shifted right by 1. mac_lo is * the 4 MSBs, and mac_hi is the 2 LSBs. */ if (mac != AH_NULL) { /* * If upper layers have requested mcast MACaddr lookup, then * signify this to the hw by setting the (poorly named) valid_bit * to 0. Yes, really 0. The hardware specs, pcu_registers.txt, is * has incorrectly named valid_bit. It should be called "Unicast". * When the Key Cache entry is to decrypt Unicast frames, this bit * should be '1'; for multicast and broadcast frames, this bit is '0'. */ if (mac[0] & 0x01) { unicast_addr = 0; /* Not an unicast address */ } mac_hi = (mac[5] << 8) | mac[4]; mac_lo = (mac[3] << 24) | (mac[2] << 16) | (mac[1] << 8) | mac[0]; mac_lo >>= 1; /* Note that the bit 0 is shifted out. This bit is used to * indicate that this is a multicast key cache. */ mac_lo |= (mac_hi & 1) << 31; /* carry */ mac_hi >>= 1; } else { mac_lo = mac_hi = 0; } OS_REG_WRITE(ah, AR_KEYTABLE_MAC0(entry), mac_lo); OS_REG_WRITE(ah, AR_KEYTABLE_MAC1(entry), mac_hi | unicast_addr); return AH_TRUE; } /* * Sets the contents of the specified key cache entry * and any associated MIC entry. */ HAL_BOOL ar9300_set_key_cache_entry(struct ath_hal *ah, u_int16_t entry, const HAL_KEYVAL *k, const u_int8_t *mac, int xor_key) { const HAL_CAPABILITIES *p_cap = &AH_PRIVATE(ah)->ah_caps; u_int32_t key0, key1, key2, key3, key4; u_int32_t key_type; u_int32_t xor_mask = xor_key ? (KEY_XOR << 24 | KEY_XOR << 16 | KEY_XOR << 8 | KEY_XOR) : 0; struct ath_hal_9300 *ahp = AH9300(ah); u_int32_t pwrmgt, pwrmgt_mic, uapsd_cfg, psta = 0; int is_proxysta_key = k->kv_type & HAL_KEY_PROXY_STA_MASK; if (entry >= p_cap->halKeyCacheSize) { HALDEBUG(ah, HAL_DEBUG_KEYCACHE, "%s: entry %u out of range\n", __func__, entry); return AH_FALSE; } HALDEBUG(ah, HAL_DEBUG_KEYCACHE, "%s[%d] mac %s proxy %d\n", __func__, __LINE__, mac ? ath_hal_ether_sprintf(mac) : "null", is_proxysta_key); switch (k->kv_type & AH_KEYTYPE_MASK) { case HAL_CIPHER_AES_OCB: key_type = AR_KEYTABLE_TYPE_AES; break; case HAL_CIPHER_AES_CCM: if (!p_cap->halCipherAesCcmSupport) { HALDEBUG(ah, HAL_DEBUG_KEYCACHE, "%s: AES-CCM not supported by " "mac rev 0x%x\n", __func__, AH_PRIVATE(ah)->ah_macRev); return AH_FALSE; } key_type = AR_KEYTABLE_TYPE_CCM; break; case HAL_CIPHER_TKIP: key_type = AR_KEYTABLE_TYPE_TKIP; if (IS_MIC_ENABLED(ah) && entry + 64 >= p_cap->halKeyCacheSize) { HALDEBUG(ah, HAL_DEBUG_KEYCACHE, "%s: entry %u inappropriate for TKIP\n", __func__, entry); return AH_FALSE; } break; case HAL_CIPHER_WEP: if (k->kv_len < 40 / NBBY) { HALDEBUG(ah, HAL_DEBUG_KEYCACHE, "%s: WEP key length %u too small\n", __func__, k->kv_len); return AH_FALSE; } if (k->kv_len <= 40 / NBBY) { key_type = AR_KEYTABLE_TYPE_40; } else if (k->kv_len <= 104 / NBBY) { key_type = AR_KEYTABLE_TYPE_104; } else { key_type = AR_KEYTABLE_TYPE_128; } break; case HAL_CIPHER_CLR: key_type = AR_KEYTABLE_TYPE_CLR; break; default: HALDEBUG(ah, HAL_DEBUG_KEYCACHE, "%s: cipher %u not supported\n", __func__, k->kv_type); return AH_FALSE; } key0 = LE_READ_4(k->kv_val + 0) ^ xor_mask; key1 = (LE_READ_2(k->kv_val + 4) ^ xor_mask) & 0xffff; key2 = LE_READ_4(k->kv_val + 6) ^ xor_mask; key3 = (LE_READ_2(k->kv_val + 10) ^ xor_mask) & 0xffff; key4 = LE_READ_4(k->kv_val + 12) ^ xor_mask; if (k->kv_len <= 104 / NBBY) { key4 &= 0xff; } /* Extract the UAPSD AC bits and shift it appropriately */ uapsd_cfg = k->kv_apsd; uapsd_cfg = (u_int32_t) SM(uapsd_cfg, AR_KEYTABLE_UAPSD); /* Need to preserve the power management bit used by MAC */ pwrmgt = OS_REG_READ(ah, AR_KEYTABLE_TYPE(entry)) & AR_KEYTABLE_PWRMGT; if (is_proxysta_key) { u_int8_t bcast_mac[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; if (!mac || OS_MEMCMP(mac, bcast_mac, 6)) { psta = AR_KEYTABLE_DIR_ACK_BIT; } } /* * Note: key cache hardware requires that each double-word * pair be written in even/odd order (since the destination is * a 64-bit register). Don't reorder these writes w/o * considering this! */ if (key_type == AR_KEYTABLE_TYPE_TKIP && IS_MIC_ENABLED(ah)) { u_int16_t micentry = entry + 64; /* MIC goes at slot+64 */ /* Need to preserve the power management bit used by MAC */ pwrmgt_mic = OS_REG_READ(ah, AR_KEYTABLE_TYPE(micentry)) & AR_KEYTABLE_PWRMGT; /* * Invalidate the encrypt/decrypt key until the MIC * key is installed so pending rx frames will fail * with decrypt errors rather than a MIC error. */ OS_REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), ~key0); OS_REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), ~key1); OS_REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), key2); OS_REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), key3); OS_REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), key4); OS_REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), key_type | pwrmgt | uapsd_cfg | psta); ar9300_set_key_cache_entry_mac(ah, entry, mac); /* * since the AR_MISC_MODE register was written with the contents of * ah_misc_mode (if any) in ar9300_attach, just check ah_misc_mode and * save a pci read per key set. */ if (ahp->ah_misc_mode & AR_PCU_MIC_NEW_LOC_ENA) { u_int32_t mic0, mic1, mic2, mic3, mic4; /* * both RX and TX mic values can be combined into * one cache slot entry. * 8*N + 800 31:0 RX Michael key 0 * 8*N + 804 15:0 TX Michael key 0 [31:16] * 8*N + 808 31:0 RX Michael key 1 * 8*N + 80C 15:0 TX Michael key 0 [15:0] * 8*N + 810 31:0 TX Michael key 1 * 8*N + 814 15:0 reserved * 8*N + 818 31:0 reserved * 8*N + 81C 14:0 reserved * 15 key valid == 0 */ /* RX mic */ mic0 = LE_READ_4(k->kv_mic + 0); mic2 = LE_READ_4(k->kv_mic + 4); /* TX mic */ mic1 = LE_READ_2(k->kv_txmic + 2) & 0xffff; mic3 = LE_READ_2(k->kv_txmic + 0) & 0xffff; mic4 = LE_READ_4(k->kv_txmic + 4); OS_REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), mic0); OS_REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), mic1); OS_REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), mic2); OS_REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), mic3); OS_REG_WRITE(ah, AR_KEYTABLE_KEY4(micentry), mic4); OS_REG_WRITE(ah, AR_KEYTABLE_TYPE(micentry), AR_KEYTABLE_TYPE_CLR | pwrmgt_mic | uapsd_cfg); } else { u_int32_t mic0, mic2; mic0 = LE_READ_4(k->kv_mic + 0); mic2 = LE_READ_4(k->kv_mic + 4); OS_REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), mic0); OS_REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), 0); OS_REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), mic2); OS_REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), 0); OS_REG_WRITE(ah, AR_KEYTABLE_KEY4(micentry), 0); OS_REG_WRITE(ah, AR_KEYTABLE_TYPE(micentry | pwrmgt_mic | uapsd_cfg), AR_KEYTABLE_TYPE_CLR); } /* NB: MIC key is not marked valid and has no MAC address */ OS_REG_WRITE(ah, AR_KEYTABLE_MAC0(micentry), 0); OS_REG_WRITE(ah, AR_KEYTABLE_MAC1(micentry), 0); /* correct intentionally corrupted key */ OS_REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), key0); OS_REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), key1); } else { OS_REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), key0); OS_REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), key1); OS_REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), key2); OS_REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), key3); OS_REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), key4); OS_REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), key_type | pwrmgt | uapsd_cfg | psta); /* ath_hal_printf(ah, "%s[%d] mac %s proxy %d\n", __func__, __LINE__, mac ? ath_hal_ether_sprintf(mac) : "null", is_proxysta_key); */ ar9300_set_key_cache_entry_mac(ah, entry, mac); } ahp->ah_keytype[entry] = keyType[k->kv_type]; HALDEBUG(ah, HAL_DEBUG_KEYCACHE, "%s: entry=%d, k->kv_type=%d," "keyType=%d\n", __func__, entry, k->kv_type, keyType[k->kv_type]); if (AH_PRIVATE(ah)->ah_curchan == AH_NULL) { return AH_TRUE; } if (ar9300_get_capability(ah, HAL_CAP_BB_RIFS_HANG, 0, AH_NULL) == HAL_OK) { if (key_type == AR_KEYTABLE_TYPE_TKIP || key_type == AR_KEYTABLE_TYPE_40 || key_type == AR_KEYTABLE_TYPE_104 || key_type == AR_KEYTABLE_TYPE_128) { /* SW WAR for Bug 31602 */ ahp->ah_rifs_sec_cnt++; HALDEBUG(ah, HAL_DEBUG_KEYCACHE, "%s: Count = %d, disabling RIFS\n", __func__, ahp->ah_rifs_sec_cnt); ar9300_set_rifs_delay(ah, AH_FALSE); } } HALDEBUG(ah, HAL_DEBUG_KEYCACHE, "%s[%d] mac %s proxy %d\n", __func__, __LINE__, mac ? ath_hal_ether_sprintf(mac) : "null", is_proxysta_key); return AH_TRUE; } /* * Enable the Keysearch for every subframe of an aggregate */ void ar9300_enable_keysearch_always(struct ath_hal *ah, int enable) { u_int32_t val; if (!ah) { return; } val = OS_REG_READ(ah, AR_PCU_MISC); if (enable) { val |= AR_PCU_ALWAYS_PERFORM_KEYSEARCH; } else { val &= ~AR_PCU_ALWAYS_PERFORM_KEYSEARCH; } OS_REG_WRITE(ah, AR_PCU_MISC, val); } void ar9300_dump_keycache(struct ath_hal *ah, int n, u_int32_t *entry) { #define AH_KEY_REG_SIZE 8 int i; for (i = 0; i < AH_KEY_REG_SIZE; i++) { entry[i] = OS_REG_READ(ah, AR_KEYTABLE_KEY0(n) + i * 4); } #undef AH_KEY_REG_SIZE } + +#if ATH_SUPPORT_KEYPLUMB_WAR +/* + * Check the contents of the specified key cache entry + * and any associated MIC entry. + */ + HAL_BOOL +ar9300_check_key_cache_entry(struct ath_hal *ah, u_int16_t entry, + const HAL_KEYVAL *k, int xorKey) +{ + const HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; + u_int32_t key0, key1, key2, key3, key4; + u_int32_t keyType; + u_int32_t xorMask = xorKey ? + (KEY_XOR << 24 | KEY_XOR << 16 | KEY_XOR << 8 | KEY_XOR) : 0; + struct ath_hal_9300 *ahp = AH9300(ah); + + + if (entry >= pCap->hal_key_cache_size) { + HALDEBUG(ah, HAL_DEBUG_KEYCACHE, + "%s: entry %u out of range\n", __func__, entry); + return AH_FALSE; + } + switch (k->kv_type) { + case HAL_CIPHER_AES_OCB: + keyType = AR_KEYTABLE_TYPE_AES; + break; + case HAL_CIPHER_AES_CCM: + if (!pCap->hal_cipher_aes_ccm_support) { + HALDEBUG(ah, HAL_DEBUG_KEYCACHE, "%s: AES-CCM not supported by " + "mac rev 0x%x\n", + __func__, AH_PRIVATE(ah)->ah_macRev); + return AH_FALSE; + } + keyType = AR_KEYTABLE_TYPE_CCM; + break; + case HAL_CIPHER_TKIP: + keyType = AR_KEYTABLE_TYPE_TKIP; + if (IS_MIC_ENABLED(ah) && entry + 64 >= pCap->hal_key_cache_size) { + HALDEBUG(ah, HAL_DEBUG_KEYCACHE, + "%s: entry %u inappropriate for TKIP\n", + __func__, entry); + return AH_FALSE; + } + break; + case HAL_CIPHER_WEP: + if (k->kv_len < 40 / NBBY) { + HALDEBUG(ah, HAL_DEBUG_KEYCACHE, "%s: WEP key length %u too small\n", + __func__, k->kv_len); + return AH_FALSE; + } + if (k->kv_len <= 40 / NBBY) { + keyType = AR_KEYTABLE_TYPE_40; + } else if (k->kv_len <= 104 / NBBY) { + keyType = AR_KEYTABLE_TYPE_104; + } else { + keyType = AR_KEYTABLE_TYPE_128; + } + break; + case HAL_CIPHER_CLR: + keyType = AR_KEYTABLE_TYPE_CLR; + return AH_TRUE; + default: + HALDEBUG(ah, HAL_DEBUG_KEYCACHE, "%s: cipher %u not supported\n", + __func__, k->kv_type); + return AH_TRUE; + } + + key0 = LE_READ_4(k->kv_val + 0) ^ xorMask; + key1 = (LE_READ_2(k->kv_val + 4) ^ xorMask) & 0xffff; + key2 = LE_READ_4(k->kv_val + 6) ^ xorMask; + key3 = (LE_READ_2(k->kv_val + 10) ^ xorMask) & 0xffff; + key4 = LE_READ_4(k->kv_val + 12) ^ xorMask; + if (k->kv_len <= 104 / NBBY) { + key4 &= 0xff; + } + + /* + * Note: key cache hardware requires that each double-word + * pair be written in even/odd order (since the destination is + * a 64-bit register). Don't reorder these writes w/o + * considering this! + */ + if (keyType == AR_KEYTABLE_TYPE_TKIP && IS_MIC_ENABLED(ah)) { + u_int16_t micentry = entry + 64; /* MIC goes at slot+64 */ + + + /* + * Invalidate the encrypt/decrypt key until the MIC + * key is installed so pending rx frames will fail + * with decrypt errors rather than a MIC error. + */ + if ((OS_REG_READ(ah, AR_KEYTABLE_KEY0(entry)) == key0) && + (OS_REG_READ(ah, AR_KEYTABLE_KEY1(entry)) == key1) && + (OS_REG_READ(ah, AR_KEYTABLE_KEY2(entry)) == key2) && + (OS_REG_READ(ah, AR_KEYTABLE_KEY3(entry)) == key3) && + (OS_REG_READ(ah, AR_KEYTABLE_KEY4(entry)) == key4) && + ((OS_REG_READ(ah, AR_KEYTABLE_TYPE(entry)) & AR_KEY_TYPE) == (keyType & AR_KEY_TYPE))) + { + + /* + * since the AR_MISC_MODE register was written with the contents of + * ah_miscMode (if any) in ar9300Attach, just check ah_miscMode and + * save a pci read per key set. + */ + if (ahp->ah_misc_mode & AR_PCU_MIC_NEW_LOC_ENA) { + u_int32_t mic0,mic1,mic2,mic3,mic4; + /* + * both RX and TX mic values can be combined into + * one cache slot entry. + * 8*N + 800 31:0 RX Michael key 0 + * 8*N + 804 15:0 TX Michael key 0 [31:16] + * 8*N + 808 31:0 RX Michael key 1 + * 8*N + 80C 15:0 TX Michael key 0 [15:0] + * 8*N + 810 31:0 TX Michael key 1 + * 8*N + 814 15:0 reserved + * 8*N + 818 31:0 reserved + * 8*N + 81C 14:0 reserved + * 15 key valid == 0 + */ + /* RX mic */ + mic0 = LE_READ_4(k->kv_mic + 0); + mic2 = LE_READ_4(k->kv_mic + 4); + /* TX mic */ + mic1 = LE_READ_2(k->kv_txmic + 2) & 0xffff; + mic3 = LE_READ_2(k->kv_txmic + 0) & 0xffff; + mic4 = LE_READ_4(k->kv_txmic + 4); + if ((OS_REG_READ(ah, AR_KEYTABLE_KEY0(micentry)) == mic0) && + (OS_REG_READ(ah, AR_KEYTABLE_KEY1(micentry)) == mic1) && + (OS_REG_READ(ah, AR_KEYTABLE_KEY2(micentry)) == mic2) && + (OS_REG_READ(ah, AR_KEYTABLE_KEY3(micentry)) == mic3) && + (OS_REG_READ(ah, AR_KEYTABLE_KEY4(micentry)) == mic4) && + ((OS_REG_READ(ah, AR_KEYTABLE_TYPE(micentry)) & AR_KEY_TYPE) == (AR_KEYTABLE_TYPE_CLR & AR_KEY_TYPE))) { + return AH_TRUE; + } + + } else { + return AH_TRUE; + } + } + } else { + if ((OS_REG_READ(ah, AR_KEYTABLE_KEY0(entry)) == key0) && + (OS_REG_READ(ah, AR_KEYTABLE_KEY1(entry)) == key1) && + (OS_REG_READ(ah, AR_KEYTABLE_KEY2(entry)) == key2) && + (OS_REG_READ(ah, AR_KEYTABLE_KEY3(entry)) == key3) && + (OS_REG_READ(ah, AR_KEYTABLE_KEY4(entry)) == key4) && + ((OS_REG_READ(ah, AR_KEYTABLE_TYPE(entry)) & AR_KEY_TYPE) == (keyType & AR_KEY_TYPE))) { + return AH_TRUE; + } + } + return AH_FALSE; +} +#endif Index: projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_misc.c =================================================================== --- projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_misc.c (revision 278776) +++ projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_misc.c (revision 278777) @@ -1,3765 +1,3807 @@ /* * Copyright (c) 2013 Qualcomm Atheros, Inc. * * 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 "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ah_devid.h" #ifdef AH_DEBUG #include "ah_desc.h" /* NB: for HAL_PHYERR* */ #endif #include "ar9300/ar9300.h" #include "ar9300/ar9300reg.h" #include "ar9300/ar9300phy.h" void ar9300_get_hw_hangs(struct ath_hal *ah, hal_hw_hangs_t *hangs) { struct ath_hal_9300 *ahp = AH9300(ah); *hangs = 0; if (ar9300_get_capability(ah, HAL_CAP_BB_RIFS_HANG, 0, AH_NULL) == HAL_OK) { *hangs |= HAL_RIFS_BB_HANG_WAR; } if (ar9300_get_capability(ah, HAL_CAP_BB_DFS_HANG, 0, AH_NULL) == HAL_OK) { *hangs |= HAL_DFS_BB_HANG_WAR; } if (ar9300_get_capability(ah, HAL_CAP_BB_RX_CLEAR_STUCK_HANG, 0, AH_NULL) == HAL_OK) { *hangs |= HAL_RX_STUCK_LOW_BB_HANG_WAR; } if (ar9300_get_capability(ah, HAL_CAP_MAC_HANG, 0, AH_NULL) == HAL_OK) { *hangs |= HAL_MAC_HANG_WAR; } if (ar9300_get_capability(ah, HAL_CAP_PHYRESTART_CLR_WAR, 0, AH_NULL) == HAL_OK) { *hangs |= HAL_PHYRESTART_CLR_WAR; } ahp->ah_hang_wars = *hangs; } /* * XXX FreeBSD: the HAL version of ath_hal_mac_usec() knows about * HT20, HT40, fast-clock, turbo mode, etc. */ static u_int ar9300_mac_to_usec(struct ath_hal *ah, u_int clks) { #if 0 const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan; if (chan && IEEE80211_IS_CHAN_HT40(chan)) { return (ath_hal_mac_usec(ah, clks) / 2); } else { return (ath_hal_mac_usec(ah, clks)); } #endif return (ath_hal_mac_usec(ah, clks)); } u_int ar9300_mac_to_clks(struct ath_hal *ah, u_int usecs) { #if 0 const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan; if (chan && IEEE80211_IS_CHAN_HT40(chan)) { return (ath_hal_mac_clks(ah, usecs) * 2); } else { return (ath_hal_mac_clks(ah, usecs)); } #endif return (ath_hal_mac_clks(ah, usecs)); } void ar9300_get_mac_address(struct ath_hal *ah, u_int8_t *mac) { struct ath_hal_9300 *ahp = AH9300(ah); OS_MEMCPY(mac, ahp->ah_macaddr, IEEE80211_ADDR_LEN); } HAL_BOOL ar9300_set_mac_address(struct ath_hal *ah, const u_int8_t *mac) { struct ath_hal_9300 *ahp = AH9300(ah); OS_MEMCPY(ahp->ah_macaddr, mac, IEEE80211_ADDR_LEN); return AH_TRUE; } void ar9300_get_bss_id_mask(struct ath_hal *ah, u_int8_t *mask) { struct ath_hal_9300 *ahp = AH9300(ah); OS_MEMCPY(mask, ahp->ah_bssid_mask, IEEE80211_ADDR_LEN); } HAL_BOOL ar9300_set_bss_id_mask(struct ath_hal *ah, const u_int8_t *mask) { struct ath_hal_9300 *ahp = AH9300(ah); /* save it since it must be rewritten on reset */ OS_MEMCPY(ahp->ah_bssid_mask, mask, IEEE80211_ADDR_LEN); OS_REG_WRITE(ah, AR_BSSMSKL, LE_READ_4(ahp->ah_bssid_mask)); OS_REG_WRITE(ah, AR_BSSMSKU, LE_READ_2(ahp->ah_bssid_mask + 4)); return AH_TRUE; } /* * Attempt to change the cards operating regulatory domain to the given value * Returns: A_EINVAL for an unsupported regulatory domain. * A_HARDWARE for an unwritable EEPROM or bad EEPROM version */ HAL_BOOL ar9300_set_regulatory_domain(struct ath_hal *ah, u_int16_t reg_domain, HAL_STATUS *status) { HAL_STATUS ecode; if (AH_PRIVATE(ah)->ah_currentRD == 0) { AH_PRIVATE(ah)->ah_currentRD = reg_domain; return AH_TRUE; } ecode = HAL_EIO; #if 0 bad: #endif if (status) { *status = ecode; } return AH_FALSE; } /* * Return the wireless modes (a,b,g,t) supported by hardware. * * This value is what is actually supported by the hardware * and is unaffected by regulatory/country code settings. * */ u_int ar9300_get_wireless_modes(struct ath_hal *ah) { return AH_PRIVATE(ah)->ah_caps.halWirelessModes; } /* * Set the interrupt and GPIO values so the ISR can disable RF * on a switch signal. Assumes GPIO port and interrupt polarity * are set prior to call. */ void ar9300_enable_rf_kill(struct ath_hal *ah) { /* TODO - can this really be above the hal on the GPIO interface for * TODO - the client only? */ struct ath_hal_9300 *ahp = AH9300(ah); if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) { /* Check RF kill GPIO before set/clear RFSILENT bits. */ if (ar9300_gpio_get(ah, ahp->ah_gpio_select) == ahp->ah_polarity) { OS_REG_SET_BIT(ah, AR_HOSTIF_REG(ah, AR_RFSILENT), AR_RFSILENT_FORCE); OS_REG_SET_BIT(ah, AR_PHY_TEST, RFSILENT_BB); } else { OS_REG_CLR_BIT(ah, AR_HOSTIF_REG(ah, AR_RFSILENT), AR_RFSILENT_FORCE); OS_REG_CLR_BIT(ah, AR_PHY_TEST, RFSILENT_BB); } } else { /* Connect rfsilent_bb_l to baseband */ OS_REG_SET_BIT(ah, AR_HOSTIF_REG(ah, AR_GPIO_INPUT_EN_VAL), AR_GPIO_INPUT_EN_VAL_RFSILENT_BB); /* Set input mux for rfsilent_bb_l to GPIO #0 */ OS_REG_CLR_BIT(ah, AR_HOSTIF_REG(ah, AR_GPIO_INPUT_MUX2), AR_GPIO_INPUT_MUX2_RFSILENT); OS_REG_SET_BIT(ah, AR_HOSTIF_REG(ah, AR_GPIO_INPUT_MUX2), (ahp->ah_gpio_select & 0x0f) << 4); /* * Configure the desired GPIO port for input and * enable baseband rf silence */ ath_hal_gpioCfgInput(ah, ahp->ah_gpio_select); OS_REG_SET_BIT(ah, AR_PHY_TEST, RFSILENT_BB); } /* * If radio disable switch connection to GPIO bit x is enabled * program GPIO interrupt. * If rfkill bit on eeprom is 1, setupeeprommap routine has already * verified that it is a later version of eeprom, it has a place for * rfkill bit and it is set to 1, indicating that GPIO bit x hardware * connection is present. */ /* * RFKill uses polling not interrupt, * disable interrupt to avoid Eee PC 2.6.21.4 hang up issue */ if (ath_hal_hasrfkill_int(ah)) { if (ahp->ah_gpio_bit == ar9300_gpio_get(ah, ahp->ah_gpio_select)) { /* switch already closed, set to interrupt upon open */ ar9300_gpio_set_intr(ah, ahp->ah_gpio_select, !ahp->ah_gpio_bit); } else { ar9300_gpio_set_intr(ah, ahp->ah_gpio_select, ahp->ah_gpio_bit); } } } /* * Change the LED blinking pattern to correspond to the connectivity */ void ar9300_set_led_state(struct ath_hal *ah, HAL_LED_STATE state) { static const u_int32_t ledbits[8] = { AR_CFG_LED_ASSOC_NONE, /* HAL_LED_RESET */ AR_CFG_LED_ASSOC_PENDING, /* HAL_LED_INIT */ AR_CFG_LED_ASSOC_PENDING, /* HAL_LED_READY */ AR_CFG_LED_ASSOC_PENDING, /* HAL_LED_SCAN */ AR_CFG_LED_ASSOC_PENDING, /* HAL_LED_AUTH */ AR_CFG_LED_ASSOC_ACTIVE, /* HAL_LED_ASSOC */ AR_CFG_LED_ASSOC_ACTIVE, /* HAL_LED_RUN */ AR_CFG_LED_ASSOC_NONE, }; OS_REG_RMW_FIELD(ah, AR_CFG_LED, AR_CFG_LED_ASSOC_CTL, ledbits[state]); } /* * Sets the Power LED on the cardbus without affecting the Network LED. */ void ar9300_set_power_led_state(struct ath_hal *ah, u_int8_t enabled) { u_int32_t val; val = enabled ? AR_CFG_LED_MODE_POWER_ON : AR_CFG_LED_MODE_POWER_OFF; OS_REG_RMW_FIELD(ah, AR_CFG_LED, AR_CFG_LED_POWER, val); } /* * Sets the Network LED on the cardbus without affecting the Power LED. */ void ar9300_set_network_led_state(struct ath_hal *ah, u_int8_t enabled) { u_int32_t val; val = enabled ? AR_CFG_LED_MODE_NETWORK_ON : AR_CFG_LED_MODE_NETWORK_OFF; OS_REG_RMW_FIELD(ah, AR_CFG_LED, AR_CFG_LED_NETWORK, val); } /* * Change association related fields programmed into the hardware. * Writing a valid BSSID to the hardware effectively enables the hardware * to synchronize its TSF to the correct beacons and receive frames coming * from that BSSID. It is called by the SME JOIN operation. */ void ar9300_write_associd(struct ath_hal *ah, const u_int8_t *bssid, u_int16_t assoc_id) { struct ath_hal_9300 *ahp = AH9300(ah); /* save bssid and assoc_id for restore on reset */ OS_MEMCPY(ahp->ah_bssid, bssid, IEEE80211_ADDR_LEN); ahp->ah_assoc_id = assoc_id; OS_REG_WRITE(ah, AR_BSS_ID0, LE_READ_4(ahp->ah_bssid)); OS_REG_WRITE(ah, AR_BSS_ID1, LE_READ_2(ahp->ah_bssid + 4) | ((assoc_id & 0x3fff) << AR_BSS_ID1_AID_S)); } /* * Get the current hardware tsf for stamlme */ u_int64_t ar9300_get_tsf64(struct ath_hal *ah) { u_int64_t tsf; /* XXX sync multi-word read? */ tsf = OS_REG_READ(ah, AR_TSF_U32); tsf = (tsf << 32) | OS_REG_READ(ah, AR_TSF_L32); return tsf; } void ar9300_set_tsf64(struct ath_hal *ah, u_int64_t tsf) { OS_REG_WRITE(ah, AR_TSF_L32, (tsf & 0xffffffff)); OS_REG_WRITE(ah, AR_TSF_U32, ((tsf >> 32) & 0xffffffff)); } /* * Get the current hardware tsf for stamlme */ u_int32_t ar9300_get_tsf32(struct ath_hal *ah) { return OS_REG_READ(ah, AR_TSF_L32); } u_int32_t ar9300_get_tsf2_32(struct ath_hal *ah) { return OS_REG_READ(ah, AR_TSF2_L32); } /* * Reset the current hardware tsf for stamlme. */ void ar9300_reset_tsf(struct ath_hal *ah) { int count; count = 0; while (OS_REG_READ(ah, AR_SLP32_MODE) & AR_SLP32_TSF_WRITE_STATUS) { count++; if (count > 10) { HALDEBUG(ah, HAL_DEBUG_RESET, "%s: AR_SLP32_TSF_WRITE_STATUS limit exceeded\n", __func__); break; } OS_DELAY(10); } OS_REG_WRITE(ah, AR_RESET_TSF, AR_RESET_TSF_ONCE); } /* * Set or clear hardware basic rate bit * Set hardware basic rate set if basic rate is found * and basic rate is equal or less than 2Mbps */ void ar9300_set_basic_rate(struct ath_hal *ah, HAL_RATE_SET *rs) { const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan; u_int32_t reg; u_int8_t xset; int i; if (chan == AH_NULL || !IEEE80211_IS_CHAN_CCK(chan)) { return; } xset = 0; for (i = 0; i < rs->rs_count; i++) { u_int8_t rset = rs->rs_rates[i]; /* Basic rate defined? */ if ((rset & 0x80) && (rset &= 0x7f) >= xset) { xset = rset; } } /* * Set the h/w bit to reflect whether or not the basic * rate is found to be equal or less than 2Mbps. */ reg = OS_REG_READ(ah, AR_STA_ID1); if (xset && xset / 2 <= 2) { OS_REG_WRITE(ah, AR_STA_ID1, reg | AR_STA_ID1_BASE_RATE_11B); } else { OS_REG_WRITE(ah, AR_STA_ID1, reg &~ AR_STA_ID1_BASE_RATE_11B); } } /* * Grab a semi-random value from hardware registers - may not * change often */ u_int32_t ar9300_get_random_seed(struct ath_hal *ah) { u_int32_t nf; nf = (OS_REG_READ(ah, AR_PHY(25)) >> 19) & 0x1ff; if (nf & 0x100) { nf = 0 - ((nf ^ 0x1ff) + 1); } return (OS_REG_READ(ah, AR_TSF_U32) ^ OS_REG_READ(ah, AR_TSF_L32) ^ nf); } /* * Detect if our card is present */ HAL_BOOL ar9300_detect_card_present(struct ath_hal *ah) { u_int16_t mac_version, mac_rev; u_int32_t v; /* * Read the Silicon Revision register and compare that * to what we read at attach time. If the same, we say * a card/device is present. */ v = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_SREV)) & AR_SREV_ID; if (v == 0xFF) { /* new SREV format */ v = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_SREV)); /* * Include 6-bit Chip Type (masked to 0) to differentiate * from pre-Sowl versions */ mac_version = (v & AR_SREV_VERSION2) >> AR_SREV_TYPE2_S; mac_rev = MS(v, AR_SREV_REVISION2); } else { mac_version = MS(v, AR_SREV_VERSION); mac_rev = v & AR_SREV_REVISION; } return (AH_PRIVATE(ah)->ah_macVersion == mac_version && AH_PRIVATE(ah)->ah_macRev == mac_rev); } /* * Update MIB Counters */ void ar9300_update_mib_mac_stats(struct ath_hal *ah) { struct ath_hal_9300 *ahp = AH9300(ah); HAL_MIB_STATS* stats = &ahp->ah_stats.ast_mibstats; stats->ackrcv_bad += OS_REG_READ(ah, AR_ACK_FAIL); stats->rts_bad += OS_REG_READ(ah, AR_RTS_FAIL); stats->fcs_bad += OS_REG_READ(ah, AR_FCS_FAIL); stats->rts_good += OS_REG_READ(ah, AR_RTS_OK); stats->beacons += OS_REG_READ(ah, AR_BEACON_CNT); } void ar9300_get_mib_mac_stats(struct ath_hal *ah, HAL_MIB_STATS* stats) { struct ath_hal_9300 *ahp = AH9300(ah); HAL_MIB_STATS* istats = &ahp->ah_stats.ast_mibstats; stats->ackrcv_bad = istats->ackrcv_bad; stats->rts_bad = istats->rts_bad; stats->fcs_bad = istats->fcs_bad; stats->rts_good = istats->rts_good; stats->beacons = istats->beacons; } /* * Detect if the HW supports spreading a CCK signal on channel 14 */ HAL_BOOL ar9300_is_japan_channel_spread_supported(struct ath_hal *ah) { return AH_TRUE; } /* * Get the rssi of frame curently being received. */ u_int32_t ar9300_get_cur_rssi(struct ath_hal *ah) { /* XXX return (OS_REG_READ(ah, AR_PHY_CURRENT_RSSI) & 0xff); */ /* get combined RSSI */ return (OS_REG_READ(ah, AR_PHY_RSSI_3) & 0xff); } #if ATH_GEN_RANDOMNESS /* * Get the rssi value from BB on ctl chain0. */ u_int32_t ar9300_get_rssi_chain0(struct ath_hal *ah) { /* get ctl chain0 RSSI */ return OS_REG_READ(ah, AR_PHY_RSSI_0) & 0xff; } #endif u_int ar9300_get_def_antenna(struct ath_hal *ah) { return (OS_REG_READ(ah, AR_DEF_ANTENNA) & 0x7); } /* Setup coverage class */ void ar9300_set_coverage_class(struct ath_hal *ah, u_int8_t coverageclass, int now) { } void ar9300_set_def_antenna(struct ath_hal *ah, u_int antenna) { OS_REG_WRITE(ah, AR_DEF_ANTENNA, (antenna & 0x7)); } HAL_BOOL ar9300_set_antenna_switch(struct ath_hal *ah, HAL_ANT_SETTING settings, const struct ieee80211_channel *chan, u_int8_t *tx_chainmask, u_int8_t *rx_chainmask, u_int8_t *antenna_cfgd) { struct ath_hal_9300 *ahp = AH9300(ah); /* * Owl does not support diversity or changing antennas. * * Instead this API and function are defined differently for AR9300. * To support Tablet PC's, this interface allows the system * to dramatically reduce the TX power on a particular chain. * * Based on the value of (redefined) diversity_control, the * reset code will decrease power on chain 0 or chain 1/2. * * Based on the value of bit 0 of antenna_switch_swap, * the mapping between OID call and chain is defined as: * 0: map A -> 0, B -> 1; * 1: map A -> 1, B -> 0; * * NOTE: * The devices that use this OID should use a tx_chain_mask and * tx_chain_select_legacy setting of 5 or 3 if ANTENNA_FIXED_B is * used in order to ensure an active transmit antenna. This * API will allow the host to turn off the only transmitting * antenna to ensure the antenna closest to the user's body is * powered-down. */ /* * Set antenna control for use during reset sequence by * ar9300_decrease_chain_power() */ ahp->ah_diversity_control = settings; return AH_TRUE; } HAL_BOOL ar9300_is_sleep_after_beacon_broken(struct ath_hal *ah) { return AH_TRUE; } HAL_BOOL ar9300_set_slot_time(struct ath_hal *ah, u_int us) { struct ath_hal_9300 *ahp = AH9300(ah); if (us < HAL_SLOT_TIME_9 || us > ar9300_mac_to_usec(ah, 0xffff)) { HALDEBUG(ah, HAL_DEBUG_RESET, "%s: bad slot time %u\n", __func__, us); ahp->ah_slot_time = (u_int) -1; /* restore default handling */ return AH_FALSE; } else { /* convert to system clocks */ OS_REG_WRITE(ah, AR_D_GBL_IFS_SLOT, ar9300_mac_to_clks(ah, us)); ahp->ah_slot_time = us; return AH_TRUE; } } HAL_BOOL ar9300_set_ack_timeout(struct ath_hal *ah, u_int us) { struct ath_hal_9300 *ahp = AH9300(ah); if (us > ar9300_mac_to_usec(ah, MS(0xffffffff, AR_TIME_OUT_ACK))) { HALDEBUG(ah, HAL_DEBUG_RESET, "%s: bad ack timeout %u\n", __func__, us); ahp->ah_ack_timeout = (u_int) -1; /* restore default handling */ return AH_FALSE; } else { /* convert to system clocks */ OS_REG_RMW_FIELD(ah, AR_TIME_OUT, AR_TIME_OUT_ACK, ar9300_mac_to_clks(ah, us)); ahp->ah_ack_timeout = us; return AH_TRUE; } } u_int ar9300_get_ack_timeout(struct ath_hal *ah) { u_int clks = MS(OS_REG_READ(ah, AR_TIME_OUT), AR_TIME_OUT_ACK); return ar9300_mac_to_usec(ah, clks); /* convert from system clocks */ } HAL_STATUS ar9300_set_quiet(struct ath_hal *ah, u_int32_t period, u_int32_t duration, u_int32_t next_start, HAL_QUIET_FLAG flag) { #define TU_TO_USEC(_tu) ((_tu) << 10) HAL_STATUS status = HAL_EIO; u_int32_t tsf = 0, j, next_start_us = 0; if (flag & HAL_QUIET_ENABLE) { for (j = 0; j < 2; j++) { next_start_us = TU_TO_USEC(next_start); tsf = OS_REG_READ(ah, AR_TSF_L32); if ((!next_start) || (flag & HAL_QUIET_ADD_CURRENT_TSF)) { next_start_us += tsf; } if (flag & HAL_QUIET_ADD_SWBA_RESP_TIME) { next_start_us += ah->ah_config.ah_sw_beacon_response_time; } OS_REG_RMW_FIELD(ah, AR_QUIET1, AR_QUIET1_QUIET_ACK_CTS_ENABLE, 1); OS_REG_WRITE(ah, AR_QUIET2, SM(duration, AR_QUIET2_QUIET_DUR)); OS_REG_WRITE(ah, AR_QUIET_PERIOD, TU_TO_USEC(period)); OS_REG_WRITE(ah, AR_NEXT_QUIET_TIMER, next_start_us); OS_REG_SET_BIT(ah, AR_TIMER_MODE, AR_QUIET_TIMER_EN); if ((OS_REG_READ(ah, AR_TSF_L32) >> 10) == tsf >> 10) { status = HAL_OK; break; } HALDEBUG(ah, HAL_DEBUG_QUEUE, "%s: TSF have moved " "while trying to set quiet time TSF: 0x%08x\n", __func__, tsf); /* TSF shouldn't count twice or reg access is taking forever */ HALASSERT(j < 1); } } else { OS_REG_CLR_BIT(ah, AR_TIMER_MODE, AR_QUIET_TIMER_EN); status = HAL_OK; } return status; #undef TU_TO_USEC } #ifdef ATH_SUPPORT_DFS void ar9300_cac_tx_quiet(struct ath_hal *ah, HAL_BOOL enable) { u32 reg1, reg2; reg1 = OS_REG_READ(ah, AR_MAC_PCU_OFFSET(MAC_PCU_MISC_MODE)); reg2 = OS_REG_READ(ah, AR_MAC_PCU_OFFSET(MAC_PCU_QUIET_TIME_1)); AH9300(ah)->ah_cac_quiet_enabled = enable; if (enable) { OS_REG_WRITE(ah, AR_MAC_PCU_OFFSET(MAC_PCU_MISC_MODE), reg1 | AR_PCU_FORCE_QUIET_COLL); OS_REG_WRITE(ah, AR_MAC_PCU_OFFSET(MAC_PCU_QUIET_TIME_1), reg2 & ~AR_QUIET1_QUIET_ACK_CTS_ENABLE); } else { OS_REG_WRITE(ah, AR_MAC_PCU_OFFSET(MAC_PCU_MISC_MODE), reg1 & ~AR_PCU_FORCE_QUIET_COLL); OS_REG_WRITE(ah, AR_MAC_PCU_OFFSET(MAC_PCU_QUIET_TIME_1), reg2 | AR_QUIET1_QUIET_ACK_CTS_ENABLE); } } #endif /* ATH_SUPPORT_DFS */ void ar9300_set_pcu_config(struct ath_hal *ah) { ar9300_set_operating_mode(ah, AH_PRIVATE(ah)->ah_opmode); } HAL_STATUS ar9300_get_capability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, u_int32_t capability, u_int32_t *result) { struct ath_hal_9300 *ahp = AH9300(ah); const HAL_CAPABILITIES *p_cap = &AH_PRIVATE(ah)->ah_caps; switch (type) { case HAL_CAP_CIPHER: /* cipher handled in hardware */ switch (capability) { case HAL_CIPHER_AES_CCM: case HAL_CIPHER_AES_OCB: case HAL_CIPHER_TKIP: case HAL_CIPHER_WEP: case HAL_CIPHER_MIC: case HAL_CIPHER_CLR: return HAL_OK; default: return HAL_ENOTSUPP; } case HAL_CAP_TKIP_MIC: /* handle TKIP MIC in hardware */ switch (capability) { case 0: /* hardware capability */ return HAL_OK; case 1: return (ahp->ah_sta_id1_defaults & AR_STA_ID1_CRPT_MIC_ENABLE) ? HAL_OK : HAL_ENXIO; default: return HAL_ENOTSUPP; } case HAL_CAP_TKIP_SPLIT: /* hardware TKIP uses split keys */ switch (capability) { case 0: /* hardware capability */ return p_cap->halTkipMicTxRxKeySupport ? HAL_ENXIO : HAL_OK; case 1: /* current setting */ return (ahp->ah_misc_mode & AR_PCU_MIC_NEW_LOC_ENA) ? HAL_ENXIO : HAL_OK; default: return HAL_ENOTSUPP; } case HAL_CAP_WME_TKIPMIC: /* hardware can do TKIP MIC when WMM is turned on */ return HAL_OK; case HAL_CAP_PHYCOUNTERS: /* hardware PHY error counters */ return HAL_OK; case HAL_CAP_DIVERSITY: /* hardware supports fast diversity */ switch (capability) { case 0: /* hardware capability */ return HAL_OK; case 1: /* current setting */ return (OS_REG_READ(ah, AR_PHY_CCK_DETECT) & AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV) ? HAL_OK : HAL_ENXIO; } return HAL_EINVAL; case HAL_CAP_TPC: switch (capability) { case 0: /* hardware capability */ return HAL_OK; case 1: return ah->ah_config.ath_hal_desc_tpc ? HAL_OK : HAL_ENXIO; } return HAL_OK; case HAL_CAP_PHYDIAG: /* radar pulse detection capability */ return HAL_OK; case HAL_CAP_MCAST_KEYSRCH: /* multicast frame keycache search */ switch (capability) { case 0: /* hardware capability */ return HAL_OK; case 1: if (OS_REG_READ(ah, AR_STA_ID1) & AR_STA_ID1_ADHOC) { /* * Owl and Merlin have problems in mcast key search. * Disable this cap. in Ad-hoc mode. see Bug 25776 and * 26802 */ return HAL_ENXIO; } else { return (ahp->ah_sta_id1_defaults & AR_STA_ID1_MCAST_KSRCH) ? HAL_OK : HAL_ENXIO; } } return HAL_EINVAL; case HAL_CAP_TSF_ADJUST: /* hardware has beacon tsf adjust */ switch (capability) { case 0: /* hardware capability */ return p_cap->halTsfAddSupport ? HAL_OK : HAL_ENOTSUPP; case 1: return (ahp->ah_misc_mode & AR_PCU_TX_ADD_TSF) ? HAL_OK : HAL_ENXIO; } return HAL_EINVAL; case HAL_CAP_RFSILENT: /* rfsilent support */ if (capability == 3) { /* rfkill interrupt */ /* * XXX: Interrupt-based notification of RF Kill state * changes not working yet. Report that this feature * is not supported so that polling is used instead. */ return (HAL_ENOTSUPP); } return ath_hal_getcapability(ah, type, capability, result); case HAL_CAP_4ADDR_AGGR: return HAL_OK; case HAL_CAP_BB_RIFS_HANG: return HAL_ENOTSUPP; case HAL_CAP_BB_DFS_HANG: return HAL_ENOTSUPP; case HAL_CAP_BB_RX_CLEAR_STUCK_HANG: /* Track chips that are known to have BB hangs related * to rx_clear stuck low. */ return HAL_ENOTSUPP; case HAL_CAP_MAC_HANG: /* Track chips that are known to have MAC hangs. */ return HAL_OK; case HAL_CAP_RIFS_RX_ENABLED: /* Is RIFS RX currently enabled */ return (ahp->ah_rifs_enabled == AH_TRUE) ? HAL_OK : HAL_ENOTSUPP; #if 0 case HAL_CAP_ANT_CFG_2GHZ: *result = p_cap->halNumAntCfg2Ghz; return HAL_OK; case HAL_CAP_ANT_CFG_5GHZ: *result = p_cap->halNumAntCfg5Ghz; return HAL_OK; case HAL_CAP_RX_STBC: *result = p_cap->hal_rx_stbc_support; return HAL_OK; case HAL_CAP_TX_STBC: *result = p_cap->hal_tx_stbc_support; return HAL_OK; #endif case HAL_CAP_LDPC: *result = p_cap->halLDPCSupport; return HAL_OK; case HAL_CAP_DYNAMIC_SMPS: return HAL_OK; case HAL_CAP_DS: return (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah) || AR_SREV_APHRODITE(ah) || (p_cap->halTxChainMask & 0x3) != 0x3 || (p_cap->halRxChainMask & 0x3) != 0x3) ? HAL_ENOTSUPP : HAL_OK; case HAL_CAP_TS: return (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah) || AR_SREV_APHRODITE(ah) || (p_cap->halTxChainMask & 0x7) != 0x7 || (p_cap->halRxChainMask & 0x7) != 0x7) ? HAL_ENOTSUPP : HAL_OK; case HAL_CAP_OL_PWRCTRL: return (ar9300_eeprom_get(ahp, EEP_OL_PWRCTRL)) ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_CRDC: #if ATH_SUPPORT_CRDC return (AR_SREV_WASP(ah) && ah->ah_config.ath_hal_crdc_enable) ? HAL_OK : HAL_ENOTSUPP; #else return HAL_ENOTSUPP; #endif #if 0 case HAL_CAP_MAX_WEP_TKIP_HT20_TX_RATEKBPS: *result = (u_int32_t)(-1); return HAL_OK; case HAL_CAP_MAX_WEP_TKIP_HT40_TX_RATEKBPS: *result = (u_int32_t)(-1); return HAL_OK; #endif case HAL_CAP_BB_PANIC_WATCHDOG: return HAL_OK; case HAL_CAP_PHYRESTART_CLR_WAR: if ((AH_PRIVATE((ah))->ah_macVersion == AR_SREV_VERSION_OSPREY) && (AH_PRIVATE((ah))->ah_macRev < AR_SREV_REVISION_AR9580_10)) { return HAL_OK; } else { return HAL_ENOTSUPP; } case HAL_CAP_ENTERPRISE_MODE: *result = ahp->ah_enterprise_mode >> 16; /* * WAR for EV 77658 - Add delimiters to first sub-frame when using * RTS/CTS with aggregation and non-enterprise Osprey. * * Bug fixed in AR9580/Peacock, Wasp1.1 and later */ if ((ahp->ah_enterprise_mode & AR_ENT_OTP_MIN_PKT_SIZE_DISABLE) && !AR_SREV_AR9580_10_OR_LATER(ah) && (!AR_SREV_WASP(ah) || AR_SREV_WASP_10(ah))) { *result |= AH_ENT_RTSCTS_DELIM_WAR; } return HAL_OK; case HAL_CAP_LDPCWAR: /* WAR for RIFS+LDPC issue is required for all chips currently * supported by ar9300 HAL. */ return HAL_OK; case HAL_CAP_ENABLE_APM: *result = p_cap->halApmEnable; return HAL_OK; case HAL_CAP_PCIE_LCR_EXTSYNC_EN: return (p_cap->hal_pcie_lcr_extsync_en == AH_TRUE) ? HAL_OK : HAL_ENOTSUPP; case HAL_CAP_PCIE_LCR_OFFSET: *result = p_cap->hal_pcie_lcr_offset; return HAL_OK; case HAL_CAP_SMARTANTENNA: /* FIXME A request is pending with h/w team to add feature bit in * caldata to detect if board has smart antenna or not, once added * we need to fix his piece of code to read and return value without * any compile flags */ #if UMAC_SUPPORT_SMARTANTENNA /* enable smart antenna for Peacock, Wasp and scorpion for future chips need to modify */ if (AR_SREV_AR9580_10(ah) || (AR_SREV_WASP(ah)) || AR_SREV_SCORPION(ah)) { return HAL_OK; } else { return HAL_ENOTSUPP; } #else return HAL_ENOTSUPP; #endif #ifdef ATH_TRAFFIC_FAST_RECOVER case HAL_CAP_TRAFFIC_FAST_RECOVER: if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah) || AR_SREV_WASP_11(ah)) { return HAL_OK; } else { return HAL_ENOTSUPP; } #endif default: return ath_hal_getcapability(ah, type, capability, result); } } HAL_BOOL ar9300_set_capability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, u_int32_t capability, u_int32_t setting, HAL_STATUS *status) { struct ath_hal_9300 *ahp = AH9300(ah); const HAL_CAPABILITIES *p_cap = &AH_PRIVATE(ah)->ah_caps; u_int32_t v; switch (type) { case HAL_CAP_TKIP_SPLIT: /* hardware TKIP uses split keys */ if (! p_cap->halTkipMicTxRxKeySupport) return AH_FALSE; if (setting) ahp->ah_misc_mode &= ~AR_PCU_MIC_NEW_LOC_ENA; else ahp->ah_misc_mode |= AR_PCU_MIC_NEW_LOC_ENA; OS_REG_WRITE(ah, AR_PCU_MISC, ahp->ah_misc_mode); return AH_TRUE; case HAL_CAP_TKIP_MIC: /* handle TKIP MIC in hardware */ if (setting) { ahp->ah_sta_id1_defaults |= AR_STA_ID1_CRPT_MIC_ENABLE; } else { ahp->ah_sta_id1_defaults &= ~AR_STA_ID1_CRPT_MIC_ENABLE; } return AH_TRUE; case HAL_CAP_DIVERSITY: v = OS_REG_READ(ah, AR_PHY_CCK_DETECT); if (setting) { v |= AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV; } else { v &= ~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV; } OS_REG_WRITE(ah, AR_PHY_CCK_DETECT, v); return AH_TRUE; case HAL_CAP_DIAG: /* hardware diagnostic support */ /* * NB: could split this up into virtual capabilities, * (e.g. 1 => ACK, 2 => CTS, etc.) but it hardly * seems worth the additional complexity. */ #ifdef AH_DEBUG AH_PRIVATE(ah)->ah_diagreg = setting; #else AH_PRIVATE(ah)->ah_diagreg = setting & 0x6; /* ACK+CTS */ #endif OS_REG_WRITE(ah, AR_DIAG_SW, AH_PRIVATE(ah)->ah_diagreg); return AH_TRUE; case HAL_CAP_TPC: ah->ah_config.ath_hal_desc_tpc = (setting != 0); return AH_TRUE; case HAL_CAP_MCAST_KEYSRCH: /* multicast frame keycache search */ if (setting) { ahp->ah_sta_id1_defaults |= AR_STA_ID1_MCAST_KSRCH; } else { ahp->ah_sta_id1_defaults &= ~AR_STA_ID1_MCAST_KSRCH; } return AH_TRUE; case HAL_CAP_TSF_ADJUST: /* hardware has beacon tsf adjust */ if (p_cap->halTsfAddSupport) { if (setting) { ahp->ah_misc_mode |= AR_PCU_TX_ADD_TSF; } else { ahp->ah_misc_mode &= ~AR_PCU_TX_ADD_TSF; } return AH_TRUE; } return AH_FALSE; case HAL_CAP_RXBUFSIZE: /* set MAC receive buffer size */ ahp->rx_buf_size = setting & AR_DATABUF_MASK; OS_REG_WRITE(ah, AR_DATABUF, ahp->rx_buf_size); return AH_TRUE; /* fall thru... */ default: return ath_hal_setcapability(ah, type, capability, setting, status); } } #ifdef AH_DEBUG static void ar9300_print_reg(struct ath_hal *ah, u_int32_t args) { u_int32_t i = 0; /* Read 0x80d0 to trigger pcie analyzer */ HALDEBUG(ah, HAL_DEBUG_PRINT_REG, "0x%04x 0x%08x\n", 0x80d0, OS_REG_READ(ah, 0x80d0)); if (args & HAL_DIAG_PRINT_REG_COUNTER) { struct ath_hal_9300 *ahp = AH9300(ah); u_int32_t tf, rf, rc, cc; tf = OS_REG_READ(ah, AR_TFCNT); rf = OS_REG_READ(ah, AR_RFCNT); rc = OS_REG_READ(ah, AR_RCCNT); cc = OS_REG_READ(ah, AR_CCCNT); HALDEBUG(ah, HAL_DEBUG_PRINT_REG, "AR_TFCNT Diff= 0x%x\n", tf - ahp->last_tf); HALDEBUG(ah, HAL_DEBUG_PRINT_REG, "AR_RFCNT Diff= 0x%x\n", rf - ahp->last_rf); HALDEBUG(ah, HAL_DEBUG_PRINT_REG, "AR_RCCNT Diff= 0x%x\n", rc - ahp->last_rc); HALDEBUG(ah, HAL_DEBUG_PRINT_REG, "AR_CCCNT Diff= 0x%x\n", cc - ahp->last_cc); ahp->last_tf = tf; ahp->last_rf = rf; ahp->last_rc = rc; ahp->last_cc = cc; HALDEBUG(ah, HAL_DEBUG_PRINT_REG, "DMADBG0 = 0x%x\n", OS_REG_READ(ah, AR_DMADBG_0)); HALDEBUG(ah, HAL_DEBUG_PRINT_REG, "DMADBG1 = 0x%x\n", OS_REG_READ(ah, AR_DMADBG_1)); HALDEBUG(ah, HAL_DEBUG_PRINT_REG, "DMADBG2 = 0x%x\n", OS_REG_READ(ah, AR_DMADBG_2)); HALDEBUG(ah, HAL_DEBUG_PRINT_REG, "DMADBG3 = 0x%x\n", OS_REG_READ(ah, AR_DMADBG_3)); HALDEBUG(ah, HAL_DEBUG_PRINT_REG, "DMADBG4 = 0x%x\n", OS_REG_READ(ah, AR_DMADBG_4)); HALDEBUG(ah, HAL_DEBUG_PRINT_REG, "DMADBG5 = 0x%x\n", OS_REG_READ(ah, AR_DMADBG_5)); HALDEBUG(ah, HAL_DEBUG_PRINT_REG, "DMADBG6 = 0x%x\n", OS_REG_READ(ah, AR_DMADBG_6)); HALDEBUG(ah, HAL_DEBUG_PRINT_REG, "DMADBG7 = 0x%x\n", OS_REG_READ(ah, AR_DMADBG_7)); } if (args & HAL_DIAG_PRINT_REG_ALL) { for (i = 0x8; i <= 0xB8; i += sizeof(u_int32_t)) { HALDEBUG(ah, HAL_DEBUG_PRINT_REG, "0x%04x 0x%08x\n", i, OS_REG_READ(ah, i)); } for (i = 0x800; i <= (0x800 + (10 << 2)); i += sizeof(u_int32_t)) { HALDEBUG(ah, HAL_DEBUG_PRINT_REG, "0x%04x 0x%08x\n", i, OS_REG_READ(ah, i)); } HALDEBUG(ah, HAL_DEBUG_PRINT_REG, "0x%04x 0x%08x\n", 0x840, OS_REG_READ(ah, i)); HALDEBUG(ah, HAL_DEBUG_PRINT_REG, "0x%04x 0x%08x\n", 0x880, OS_REG_READ(ah, i)); for (i = 0x8C0; i <= (0x8C0 + (10 << 2)); i += sizeof(u_int32_t)) { HALDEBUG(ah, HAL_DEBUG_PRINT_REG, "0x%04x 0x%08x\n", i, OS_REG_READ(ah, i)); } for (i = 0x1F00; i <= 0x1F04; i += sizeof(u_int32_t)) { HALDEBUG(ah, HAL_DEBUG_PRINT_REG, "0x%04x 0x%08x\n", i, OS_REG_READ(ah, i)); } for (i = 0x4000; i <= 0x408C; i += sizeof(u_int32_t)) { HALDEBUG(ah, HAL_DEBUG_PRINT_REG, "0x%04x 0x%08x\n", i, OS_REG_READ(ah, i)); } for (i = 0x5000; i <= 0x503C; i += sizeof(u_int32_t)) { HALDEBUG(ah, HAL_DEBUG_PRINT_REG, "0x%04x 0x%08x\n", i, OS_REG_READ(ah, i)); } for (i = 0x7040; i <= 0x7058; i += sizeof(u_int32_t)) { HALDEBUG(ah, HAL_DEBUG_PRINT_REG, "0x%04x 0x%08x\n", i, OS_REG_READ(ah, i)); } for (i = 0x8000; i <= 0x8098; i += sizeof(u_int32_t)) { HALDEBUG(ah, HAL_DEBUG_PRINT_REG, "0x%04x 0x%08x\n", i, OS_REG_READ(ah, i)); } for (i = 0x80D4; i <= 0x8200; i += sizeof(u_int32_t)) { HALDEBUG(ah, HAL_DEBUG_PRINT_REG, "0x%04x 0x%08x\n", i, OS_REG_READ(ah, i)); } for (i = 0x8240; i <= 0x97FC; i += sizeof(u_int32_t)) { HALDEBUG(ah, HAL_DEBUG_PRINT_REG, "0x%04x 0x%08x\n", i, OS_REG_READ(ah, i)); } for (i = 0x9800; i <= 0x99f0; i += sizeof(u_int32_t)) { HALDEBUG(ah, HAL_DEBUG_PRINT_REG, "0x%04x 0x%08x\n", i, OS_REG_READ(ah, i)); } for (i = 0x9c10; i <= 0x9CFC; i += sizeof(u_int32_t)) { HALDEBUG(ah, HAL_DEBUG_PRINT_REG, "0x%04x 0x%08x\n", i, OS_REG_READ(ah, i)); } for (i = 0xA200; i <= 0xA26C; i += sizeof(u_int32_t)) { HALDEBUG(ah, HAL_DEBUG_PRINT_REG, "0x%04x 0x%08x\n", i, OS_REG_READ(ah, i)); } } } #endif HAL_BOOL ar9300_get_diag_state(struct ath_hal *ah, int request, const void *args, u_int32_t argsize, void **result, u_int32_t *resultsize) { struct ath_hal_9300 *ahp = AH9300(ah); (void) ahp; if (ath_hal_getdiagstate(ah, request, args, argsize, result, resultsize)) { return AH_TRUE; } switch (request) { #ifdef AH_PRIVATE_DIAG case HAL_DIAG_EEPROM: *result = &ahp->ah_eeprom; *resultsize = sizeof(ar9300_eeprom_t); return AH_TRUE; #if 0 /* XXX - TODO */ case HAL_DIAG_EEPROM_EXP_11A: case HAL_DIAG_EEPROM_EXP_11B: case HAL_DIAG_EEPROM_EXP_11G: pe = &ahp->ah_mode_power_array2133[request - HAL_DIAG_EEPROM_EXP_11A]; *result = pe->p_channels; *resultsize = (*result == AH_NULL) ? 0 : roundup(sizeof(u_int16_t) * pe->num_channels, sizeof(u_int32_t)) + sizeof(EXPN_DATA_PER_CHANNEL_2133) * pe->num_channels; return AH_TRUE; #endif case HAL_DIAG_RFGAIN: *result = &ahp->ah_gain_values; *resultsize = sizeof(GAIN_VALUES); return AH_TRUE; case HAL_DIAG_RFGAIN_CURSTEP: *result = (void *) ahp->ah_gain_values.curr_step; *resultsize = (*result == AH_NULL) ? 0 : sizeof(GAIN_OPTIMIZATION_STEP); return AH_TRUE; #if 0 /* XXX - TODO */ case HAL_DIAG_PCDAC: *result = ahp->ah_pcdac_table; *resultsize = ahp->ah_pcdac_table_size; return AH_TRUE; #endif case HAL_DIAG_ANI_CURRENT: *result = ar9300_ani_get_current_state(ah); *resultsize = (*result == AH_NULL) ? 0 : sizeof(struct ar9300_ani_state); return AH_TRUE; case HAL_DIAG_ANI_STATS: *result = ar9300_ani_get_current_stats(ah); *resultsize = (*result == AH_NULL) ? 0 : sizeof(struct ar9300_stats); return AH_TRUE; case HAL_DIAG_ANI_CMD: if (argsize != 2*sizeof(u_int32_t)) { return AH_FALSE; } ar9300_ani_control( ah, ((const u_int32_t *)args)[0], ((const u_int32_t *)args)[1]); return AH_TRUE; #if 0 case HAL_DIAG_TXCONT: /*AR9300_CONTTXMODE(ah, (struct ath_desc *)args, argsize );*/ return AH_TRUE; #endif /* 0 */ #endif /* AH_PRIVATE_DIAG */ case HAL_DIAG_CHANNELS: #if 0 *result = &(ahp->ah_priv.ah_channels[0]); *resultsize = sizeof(ahp->ah_priv.ah_channels[0]) * ahp->ah_priv.priv.ah_nchan; #endif return AH_TRUE; #ifdef AH_DEBUG case HAL_DIAG_PRINT_REG: ar9300_print_reg(ah, *((const u_int32_t *)args)); return AH_TRUE; #endif default: break; } return AH_FALSE; } void ar9300_dma_reg_dump(struct ath_hal *ah) { #ifdef AH_DEBUG #define NUM_DMA_DEBUG_REGS 8 #define NUM_QUEUES 10 u_int32_t val[NUM_DMA_DEBUG_REGS]; int qcu_offset = 0, dcu_offset = 0; u_int32_t *qcu_base = &val[0], *dcu_base = &val[4], reg; int i, j, k; int16_t nfarray[HAL_NUM_NF_READINGS]; #ifdef ATH_NF_PER_CHAN HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, AH_PRIVATE(ah)->ah_curchan); #endif /* ATH_NF_PER_CHAN */ HAL_NFCAL_HIST_FULL *h = AH_HOME_CHAN_NFCAL_HIST(ah, ichan); /* selecting DMA OBS 8 */ OS_REG_WRITE(ah, AR_MACMISC, ((AR_MACMISC_DMA_OBS_LINE_8 << AR_MACMISC_DMA_OBS_S) | (AR_MACMISC_MISC_OBS_BUS_1 << AR_MACMISC_MISC_OBS_BUS_MSB_S))); ath_hal_printf(ah, "Raw DMA Debug values:\n"); for (i = 0; i < NUM_DMA_DEBUG_REGS; i++) { if (i % 4 == 0) { ath_hal_printf(ah, "\n"); } val[i] = OS_REG_READ(ah, AR_DMADBG_0 + (i * sizeof(u_int32_t))); ath_hal_printf(ah, "%d: %08x ", i, val[i]); } ath_hal_printf(ah, "\n\n"); ath_hal_printf(ah, "Num QCU: chain_st fsp_ok fsp_st DCU: chain_st\n"); for (i = 0; i < NUM_QUEUES; i++, qcu_offset += 4, dcu_offset += 5) { if (i == 8) { /* only 8 QCU entries in val[0] */ qcu_offset = 0; qcu_base++; } if (i == 6) { /* only 6 DCU entries in val[4] */ dcu_offset = 0; dcu_base++; } ath_hal_printf(ah, "%2d %2x %1x %2x %2x\n", i, (*qcu_base & (0x7 << qcu_offset)) >> qcu_offset, (*qcu_base & (0x8 << qcu_offset)) >> (qcu_offset + 3), val[2] & (0x7 << (i * 3)) >> (i * 3), (*dcu_base & (0x1f << dcu_offset)) >> dcu_offset); } ath_hal_printf(ah, "\n"); ath_hal_printf(ah, "qcu_stitch state: %2x qcu_fetch state: %2x\n", (val[3] & 0x003c0000) >> 18, (val[3] & 0x03c00000) >> 22); ath_hal_printf(ah, "qcu_complete state: %2x dcu_complete state: %2x\n", (val[3] & 0x1c000000) >> 26, (val[6] & 0x3)); ath_hal_printf(ah, "dcu_arb state: %2x dcu_fp state: %2x\n", (val[5] & 0x06000000) >> 25, (val[5] & 0x38000000) >> 27); ath_hal_printf(ah, "chan_idle_dur: %3d chan_idle_dur_valid: %1d\n", (val[6] & 0x000003fc) >> 2, (val[6] & 0x00000400) >> 10); ath_hal_printf(ah, "txfifo_valid_0: %1d txfifo_valid_1: %1d\n", (val[6] & 0x00000800) >> 11, (val[6] & 0x00001000) >> 12); ath_hal_printf(ah, "txfifo_dcu_num_0: %2d txfifo_dcu_num_1: %2d\n", (val[6] & 0x0001e000) >> 13, (val[6] & 0x001e0000) >> 17); ath_hal_printf(ah, "pcu observe 0x%x \n", OS_REG_READ(ah, AR_OBS_BUS_1)); ath_hal_printf(ah, "AR_CR 0x%x \n", OS_REG_READ(ah, AR_CR)); ar9300_upload_noise_floor(ah, 1, nfarray); ath_hal_printf(ah, "2G:\n"); ath_hal_printf(ah, "Min CCA Out:\n"); ath_hal_printf(ah, "\t\tChain 0\t\tChain 1\t\tChain 2\n"); ath_hal_printf(ah, "Control:\t%8d\t%8d\t%8d\n", nfarray[0], nfarray[1], nfarray[2]); ath_hal_printf(ah, "Extension:\t%8d\t%8d\t%8d\n\n", nfarray[3], nfarray[4], nfarray[5]); ar9300_upload_noise_floor(ah, 0, nfarray); ath_hal_printf(ah, "5G:\n"); ath_hal_printf(ah, "Min CCA Out:\n"); ath_hal_printf(ah, "\t\tChain 0\t\tChain 1\t\tChain 2\n"); ath_hal_printf(ah, "Control:\t%8d\t%8d\t%8d\n", nfarray[0], nfarray[1], nfarray[2]); ath_hal_printf(ah, "Extension:\t%8d\t%8d\t%8d\n\n", nfarray[3], nfarray[4], nfarray[5]); for (i = 0; i < HAL_NUM_NF_READINGS; i++) { ath_hal_printf(ah, "%s Chain %d NF History:\n", ((i < 3) ? "Control " : "Extension "), i%3); for (j = 0, k = h->base.curr_index; j < HAL_NF_CAL_HIST_LEN_FULL; j++, k++) { ath_hal_printf(ah, "Element %d: %d\n", j, h->nf_cal_buffer[k % HAL_NF_CAL_HIST_LEN_FULL][i]); } ath_hal_printf(ah, "Last Programmed NF: %d\n\n", h->base.priv_nf[i]); } reg = OS_REG_READ(ah, AR_PHY_FIND_SIG_LOW); ath_hal_printf(ah, "FIRStep Low = 0x%x (%d)\n", MS(reg, AR_PHY_FIND_SIG_LOW_FIRSTEP_LOW), MS(reg, AR_PHY_FIND_SIG_LOW_FIRSTEP_LOW)); reg = OS_REG_READ(ah, AR_PHY_DESIRED_SZ); ath_hal_printf(ah, "Total Desired = 0x%x (%d)\n", MS(reg, AR_PHY_DESIRED_SZ_TOT_DES), MS(reg, AR_PHY_DESIRED_SZ_TOT_DES)); ath_hal_printf(ah, "ADC Desired = 0x%x (%d)\n", MS(reg, AR_PHY_DESIRED_SZ_ADC), MS(reg, AR_PHY_DESIRED_SZ_ADC)); reg = OS_REG_READ(ah, AR_PHY_FIND_SIG); ath_hal_printf(ah, "FIRStep = 0x%x (%d)\n", MS(reg, AR_PHY_FIND_SIG_FIRSTEP), MS(reg, AR_PHY_FIND_SIG_FIRSTEP)); reg = OS_REG_READ(ah, AR_PHY_AGC); ath_hal_printf(ah, "Coarse High = 0x%x (%d)\n", MS(reg, AR_PHY_AGC_COARSE_HIGH), MS(reg, AR_PHY_AGC_COARSE_HIGH)); ath_hal_printf(ah, "Coarse Low = 0x%x (%d)\n", MS(reg, AR_PHY_AGC_COARSE_LOW), MS(reg, AR_PHY_AGC_COARSE_LOW)); ath_hal_printf(ah, "Coarse Power Constant = 0x%x (%d)\n", MS(reg, AR_PHY_AGC_COARSE_PWR_CONST), MS(reg, AR_PHY_AGC_COARSE_PWR_CONST)); reg = OS_REG_READ(ah, AR_PHY_TIMING5); ath_hal_printf(ah, "Enable Cyclic Power Thresh = %d\n", MS(reg, AR_PHY_TIMING5_CYCPWR_THR1_ENABLE)); ath_hal_printf(ah, "Cyclic Power Thresh = 0x%x (%d)\n", MS(reg, AR_PHY_TIMING5_CYCPWR_THR1), MS(reg, AR_PHY_TIMING5_CYCPWR_THR1)); ath_hal_printf(ah, "Cyclic Power Thresh 1A= 0x%x (%d)\n", MS(reg, AR_PHY_TIMING5_CYCPWR_THR1A), MS(reg, AR_PHY_TIMING5_CYCPWR_THR1A)); reg = OS_REG_READ(ah, AR_PHY_DAG_CTRLCCK); ath_hal_printf(ah, "Barker RSSI Thresh Enable = %d\n", MS(reg, AR_PHY_DAG_CTRLCCK_EN_RSSI_THR)); ath_hal_printf(ah, "Barker RSSI Thresh = 0x%x (%d)\n", MS(reg, AR_PHY_DAG_CTRLCCK_RSSI_THR), MS(reg, AR_PHY_DAG_CTRLCCK_RSSI_THR)); /* Step 1a: Set bit 23 of register 0xa360 to 0 */ reg = OS_REG_READ(ah, 0xa360); reg &= ~0x00800000; OS_REG_WRITE(ah, 0xa360, reg); /* Step 2a: Set register 0xa364 to 0x1000 */ reg = 0x1000; OS_REG_WRITE(ah, 0xa364, reg); /* Step 3a: Read bits 17:0 of register 0x9c20 */ reg = OS_REG_READ(ah, 0x9c20); reg &= 0x0003ffff; ath_hal_printf(ah, "%s: Test Control Status [0x1000] 0x9c20[17:0] = 0x%x\n", __func__, reg); /* Step 1b: Set bit 23 of register 0xa360 to 0 */ reg = OS_REG_READ(ah, 0xa360); reg &= ~0x00800000; OS_REG_WRITE(ah, 0xa360, reg); /* Step 2b: Set register 0xa364 to 0x1400 */ reg = 0x1400; OS_REG_WRITE(ah, 0xa364, reg); /* Step 3b: Read bits 17:0 of register 0x9c20 */ reg = OS_REG_READ(ah, 0x9c20); reg &= 0x0003ffff; ath_hal_printf(ah, "%s: Test Control Status [0x1400] 0x9c20[17:0] = 0x%x\n", __func__, reg); /* Step 1c: Set bit 23 of register 0xa360 to 0 */ reg = OS_REG_READ(ah, 0xa360); reg &= ~0x00800000; OS_REG_WRITE(ah, 0xa360, reg); /* Step 2c: Set register 0xa364 to 0x3C00 */ reg = 0x3c00; OS_REG_WRITE(ah, 0xa364, reg); /* Step 3c: Read bits 17:0 of register 0x9c20 */ reg = OS_REG_READ(ah, 0x9c20); reg &= 0x0003ffff; ath_hal_printf(ah, "%s: Test Control Status [0x3C00] 0x9c20[17:0] = 0x%x\n", __func__, reg); /* Step 1d: Set bit 24 of register 0xa360 to 0 */ reg = OS_REG_READ(ah, 0xa360); reg &= ~0x001040000; OS_REG_WRITE(ah, 0xa360, reg); /* Step 2d: Set register 0xa364 to 0x5005D */ reg = 0x5005D; OS_REG_WRITE(ah, 0xa364, reg); /* Step 3d: Read bits 17:0 of register 0xa368 */ reg = OS_REG_READ(ah, 0xa368); reg &= 0x0003ffff; ath_hal_printf(ah, "%s: Test Control Status [0x5005D] 0xa368[17:0] = 0x%x\n", __func__, reg); /* Step 1e: Set bit 24 of register 0xa360 to 0 */ reg = OS_REG_READ(ah, 0xa360); reg &= ~0x001040000; OS_REG_WRITE(ah, 0xa360, reg); /* Step 2e: Set register 0xa364 to 0x7005D */ reg = 0x7005D; OS_REG_WRITE(ah, 0xa364, reg); /* Step 3e: Read bits 17:0 of register 0xa368 */ reg = OS_REG_READ(ah, 0xa368); reg &= 0x0003ffff; ath_hal_printf(ah, "%s: Test Control Status [0x7005D] 0xa368[17:0] = 0x%x\n", __func__, reg); /* Step 1f: Set bit 24 of register 0xa360 to 0 */ reg = OS_REG_READ(ah, 0xa360); reg &= ~0x001000000; reg |= 0x40000; OS_REG_WRITE(ah, 0xa360, reg); /* Step 2f: Set register 0xa364 to 0x3005D */ reg = 0x3005D; OS_REG_WRITE(ah, 0xa364, reg); /* Step 3f: Read bits 17:0 of register 0xa368 */ reg = OS_REG_READ(ah, 0xa368); reg &= 0x0003ffff; ath_hal_printf(ah, "%s: Test Control Status [0x3005D] 0xa368[17:0] = 0x%x\n", __func__, reg); /* Step 1g: Set bit 24 of register 0xa360 to 0 */ reg = OS_REG_READ(ah, 0xa360); reg &= ~0x001000000; reg |= 0x40000; OS_REG_WRITE(ah, 0xa360, reg); /* Step 2g: Set register 0xa364 to 0x6005D */ reg = 0x6005D; OS_REG_WRITE(ah, 0xa364, reg); /* Step 3g: Read bits 17:0 of register 0xa368 */ reg = OS_REG_READ(ah, 0xa368); reg &= 0x0003ffff; ath_hal_printf(ah, "%s: Test Control Status [0x6005D] 0xa368[17:0] = 0x%x\n", __func__, reg); #endif /* AH_DEBUG */ } /* * Return the busy for rx_frame, rx_clear, and tx_frame */ u_int32_t ar9300_get_mib_cycle_counts_pct(struct ath_hal *ah, u_int32_t *rxc_pcnt, u_int32_t *rxf_pcnt, u_int32_t *txf_pcnt) { struct ath_hal_9300 *ahp = AH9300(ah); u_int32_t good = 1; u_int32_t rc = OS_REG_READ(ah, AR_RCCNT); u_int32_t rf = OS_REG_READ(ah, AR_RFCNT); u_int32_t tf = OS_REG_READ(ah, AR_TFCNT); u_int32_t cc = OS_REG_READ(ah, AR_CCCNT); /* read cycles last */ if (ahp->ah_cycles == 0 || ahp->ah_cycles > cc) { /* * Cycle counter wrap (or initial call); it's not possible * to accurately calculate a value because the registers * right shift rather than wrap--so punt and return 0. */ HALDEBUG(ah, HAL_DEBUG_CHANNEL, "%s: cycle counter wrap. ExtBusy = 0\n", __func__); good = 0; } else { u_int32_t cc_d = cc - ahp->ah_cycles; u_int32_t rc_d = rc - ahp->ah_rx_clear; u_int32_t rf_d = rf - ahp->ah_rx_frame; u_int32_t tf_d = tf - ahp->ah_tx_frame; if (cc_d != 0) { *rxc_pcnt = rc_d * 100 / cc_d; *rxf_pcnt = rf_d * 100 / cc_d; *txf_pcnt = tf_d * 100 / cc_d; } else { good = 0; } } ahp->ah_cycles = cc; ahp->ah_rx_frame = rf; ahp->ah_rx_clear = rc; ahp->ah_tx_frame = tf; return good; } /* * Return approximation of extension channel busy over an time interval * 0% (clear) -> 100% (busy) * -1 for invalid estimate */ uint32_t ar9300_get_11n_ext_busy(struct ath_hal *ah) { /* * Overflow condition to check before multiplying to get % * (x * 100 > 0xFFFFFFFF ) => (x > 0x28F5C28) */ #define OVERFLOW_LIMIT 0x28F5C28 #define ERROR_CODE -1 struct ath_hal_9300 *ahp = AH9300(ah); u_int32_t busy = 0; /* percentage */ int8_t busyper = 0; u_int32_t cycle_count, ctl_busy, ext_busy; /* cycle_count will always be the first to wrap; therefore, read it last * This sequence of reads is not atomic, and MIB counter wrap * could happen during it ? */ ctl_busy = OS_REG_READ(ah, AR_RCCNT); ext_busy = OS_REG_READ(ah, AR_EXTRCCNT); cycle_count = OS_REG_READ(ah, AR_CCCNT); if ((ahp->ah_cycle_count == 0) || (ahp->ah_cycle_count > cycle_count) || (ahp->ah_ctl_busy > ctl_busy) || (ahp->ah_ext_busy > ext_busy)) { /* * Cycle counter wrap (or initial call); it's not possible * to accurately calculate a value because the registers * right shift rather than wrap--so punt and return 0. */ busyper = ERROR_CODE; HALDEBUG(ah, HAL_DEBUG_CHANNEL, "%s: cycle counter wrap. ExtBusy = 0\n", __func__); } else { u_int32_t cycle_delta = cycle_count - ahp->ah_cycle_count; u_int32_t ext_busy_delta = ext_busy - ahp->ah_ext_busy; /* * Compute extension channel busy percentage * Overflow condition: 0xFFFFFFFF < ext_busy_delta * 100 * Underflow condition/Divide-by-zero: check that cycle_delta >> 7 != 0 * Will never happen, since (ext_busy_delta < cycle_delta) always, * and shift necessitated by large ext_busy_delta. * Due to timing difference to read the registers and counter overflow, * it may still happen that cycle_delta >> 7 = 0. * */ if (cycle_delta) { if (ext_busy_delta > OVERFLOW_LIMIT) { if (cycle_delta >> 7) { busy = ((ext_busy_delta >> 7) * 100) / (cycle_delta >> 7); } else { busyper = ERROR_CODE; } } else { busy = (ext_busy_delta * 100) / cycle_delta; } } else { busyper = ERROR_CODE; } if (busy > 100) { busy = 100; } if ( busyper != ERROR_CODE ) { busyper = busy; } } ahp->ah_cycle_count = cycle_count; ahp->ah_ctl_busy = ctl_busy; ahp->ah_ext_busy = ext_busy; return busyper; #undef OVERFLOW_LIMIT #undef ERROR_CODE } /* BB Panic Watchdog declarations */ #define HAL_BB_PANIC_WD_HT20_FACTOR 74 /* 0.74 */ #define HAL_BB_PANIC_WD_HT40_FACTOR 37 /* 0.37 */ void ar9300_config_bb_panic_watchdog(struct ath_hal *ah) { #define HAL_BB_PANIC_IDLE_TIME_OUT 0x0a8c0000 const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan; u_int32_t idle_tmo_ms = AH9300(ah)->ah_bb_panic_timeout_ms; u_int32_t val, idle_count; if (idle_tmo_ms != 0) { /* enable IRQ, disable chip-reset for BB panic */ val = OS_REG_READ(ah, AR_PHY_PANIC_WD_CTL_2) & AR_PHY_BB_PANIC_CNTL2_MASK; OS_REG_WRITE(ah, AR_PHY_PANIC_WD_CTL_2, (val | AR_PHY_BB_PANIC_IRQ_ENABLE) & ~AR_PHY_BB_PANIC_RST_ENABLE); /* bound limit to 10 secs */ if (idle_tmo_ms > 10000) { idle_tmo_ms = 10000; } if (chan != AH_NULL && IEEE80211_IS_CHAN_HT40(chan)) { idle_count = (100 * idle_tmo_ms) / HAL_BB_PANIC_WD_HT40_FACTOR; } else { idle_count = (100 * idle_tmo_ms) / HAL_BB_PANIC_WD_HT20_FACTOR; } /* * enable panic in non-IDLE mode, * disable in IDLE mode, * set idle time-out */ // EV92527 : Enable IDLE mode panic OS_REG_WRITE(ah, AR_PHY_PANIC_WD_CTL_1, AR_PHY_BB_PANIC_NON_IDLE_ENABLE | AR_PHY_BB_PANIC_IDLE_ENABLE | (AR_PHY_BB_PANIC_IDLE_MASK & HAL_BB_PANIC_IDLE_TIME_OUT) | (AR_PHY_BB_PANIC_NON_IDLE_MASK & (idle_count << 2))); } else { /* disable IRQ, disable chip-reset for BB panic */ OS_REG_WRITE(ah, AR_PHY_PANIC_WD_CTL_2, OS_REG_READ(ah, AR_PHY_PANIC_WD_CTL_2) & ~(AR_PHY_BB_PANIC_RST_ENABLE | AR_PHY_BB_PANIC_IRQ_ENABLE)); /* disable panic in non-IDLE mode, disable in IDLE mode */ OS_REG_WRITE(ah, AR_PHY_PANIC_WD_CTL_1, OS_REG_READ(ah, AR_PHY_PANIC_WD_CTL_1) & ~(AR_PHY_BB_PANIC_NON_IDLE_ENABLE | AR_PHY_BB_PANIC_IDLE_ENABLE)); } HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: %s BB Panic Watchdog tmo=%ums\n", __func__, idle_tmo_ms ? "Enabled" : "Disabled", idle_tmo_ms); #undef HAL_BB_PANIC_IDLE_TIME_OUT } void ar9300_handle_bb_panic(struct ath_hal *ah) { u_int32_t status; /* * we want to avoid printing in ISR context so we save * panic watchdog status to be printed later in DPC context */ AH9300(ah)->ah_bb_panic_last_status = status = OS_REG_READ(ah, AR_PHY_PANIC_WD_STATUS); /* * panic watchdog timer should reset on status read * but to make sure we write 0 to the watchdog status bit */ OS_REG_WRITE(ah, AR_PHY_PANIC_WD_STATUS, status & ~AR_PHY_BB_WD_STATUS_CLR); } int ar9300_get_bb_panic_info(struct ath_hal *ah, struct hal_bb_panic_info *bb_panic) { bb_panic->status = AH9300(ah)->ah_bb_panic_last_status; /* * For signature 04000539 do not print anything. * This is a very common occurence as a compromise between * BB Panic and AH_FALSE detects (EV71009). It indicates * radar hang, which can be cleared by reprogramming * radar related register and does not requre a chip reset */ /* Suppress BB Status mesg following signature */ switch (bb_panic->status) { - case 0x04000539: - case 0x04008009: - case 0x04000b09: - case 0x1300000a: + case 0x04000539: + case 0x04008009: + case 0x04000b09: + case 0x1300000a: return -1; } bb_panic->tsf = ar9300_get_tsf32(ah); bb_panic->wd = MS(bb_panic->status, AR_PHY_BB_WD_STATUS); bb_panic->det = MS(bb_panic->status, AR_PHY_BB_WD_DET_HANG); bb_panic->rdar = MS(bb_panic->status, AR_PHY_BB_WD_RADAR_SM); bb_panic->r_odfm = MS(bb_panic->status, AR_PHY_BB_WD_RX_OFDM_SM); bb_panic->r_cck = MS(bb_panic->status, AR_PHY_BB_WD_RX_CCK_SM); bb_panic->t_odfm = MS(bb_panic->status, AR_PHY_BB_WD_TX_OFDM_SM); bb_panic->t_cck = MS(bb_panic->status, AR_PHY_BB_WD_TX_CCK_SM); bb_panic->agc = MS(bb_panic->status, AR_PHY_BB_WD_AGC_SM); bb_panic->src = MS(bb_panic->status, AR_PHY_BB_WD_SRCH_SM); bb_panic->phy_panic_wd_ctl1 = OS_REG_READ(ah, AR_PHY_PANIC_WD_CTL_1); bb_panic->phy_panic_wd_ctl2 = OS_REG_READ(ah, AR_PHY_PANIC_WD_CTL_2); bb_panic->phy_gen_ctrl = OS_REG_READ(ah, AR_PHY_GEN_CTRL); bb_panic->rxc_pcnt = bb_panic->rxf_pcnt = bb_panic->txf_pcnt = 0; bb_panic->cycles = ar9300_get_mib_cycle_counts_pct(ah, &bb_panic->rxc_pcnt, &bb_panic->rxf_pcnt, &bb_panic->txf_pcnt); if (ah->ah_config.ath_hal_show_bb_panic) { ath_hal_printf(ah, "\n==== BB update: BB status=0x%08x, " "tsf=0x%08x ====\n", bb_panic->status, bb_panic->tsf); ath_hal_printf(ah, "** BB state: wd=%u det=%u rdar=%u rOFDM=%d " "rCCK=%u tOFDM=%u tCCK=%u agc=%u src=%u **\n", bb_panic->wd, bb_panic->det, bb_panic->rdar, bb_panic->r_odfm, bb_panic->r_cck, bb_panic->t_odfm, bb_panic->t_cck, bb_panic->agc, bb_panic->src); ath_hal_printf(ah, "** BB WD cntl: cntl1=0x%08x cntl2=0x%08x **\n", bb_panic->phy_panic_wd_ctl1, bb_panic->phy_panic_wd_ctl2); ath_hal_printf(ah, "** BB mode: BB_gen_controls=0x%08x **\n", bb_panic->phy_gen_ctrl); if (bb_panic->cycles) { ath_hal_printf(ah, "** BB busy times: rx_clear=%d%%, " "rx_frame=%d%%, tx_frame=%d%% **\n", bb_panic->rxc_pcnt, bb_panic->rxf_pcnt, bb_panic->txf_pcnt); } ath_hal_printf(ah, "==== BB update: done ====\n\n"); } return 0; //The returned data will be stored for athstats to retrieve it } /* set the reason for HAL reset */ void ar9300_set_hal_reset_reason(struct ath_hal *ah, u_int8_t resetreason) { AH9300(ah)->ah_reset_reason = resetreason; } /* * Configure 20/40 operation * * 20/40 = joint rx clear (control and extension) * 20 = rx clear (control) * * - NOTE: must stop MAC (tx) and requeue 40 MHz packets as 20 MHz * when changing from 20/40 => 20 only */ void ar9300_set_11n_mac2040(struct ath_hal *ah, HAL_HT_MACMODE mode) { u_int32_t macmode; /* Configure MAC for 20/40 operation */ if (mode == HAL_HT_MACMODE_2040 && !ah->ah_config.ath_hal_cwm_ignore_ext_cca) { macmode = AR_2040_JOINED_RX_CLEAR; } else { macmode = 0; } OS_REG_WRITE(ah, AR_2040_MODE, macmode); } /* * Get Rx clear (control/extension channel) * * Returns active low (busy) for ctrl/ext channel * Owl 2.0 */ HAL_HT_RXCLEAR ar9300_get_11n_rx_clear(struct ath_hal *ah) { HAL_HT_RXCLEAR rxclear = 0; u_int32_t val; val = OS_REG_READ(ah, AR_DIAG_SW); /* control channel */ if (val & AR_DIAG_RX_CLEAR_CTL_LOW) { rxclear |= HAL_RX_CLEAR_CTL_LOW; } /* extension channel */ if (val & AR_DIAG_RX_CLEAR_EXT_LOW) { rxclear |= HAL_RX_CLEAR_EXT_LOW; } return rxclear; } /* * Set Rx clear (control/extension channel) * * Useful for forcing the channel to appear busy for * debugging/diagnostics * Owl 2.0 */ void ar9300_set_11n_rx_clear(struct ath_hal *ah, HAL_HT_RXCLEAR rxclear) { /* control channel */ if (rxclear & HAL_RX_CLEAR_CTL_LOW) { OS_REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_CLEAR_CTL_LOW); } else { OS_REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_CLEAR_CTL_LOW); } /* extension channel */ if (rxclear & HAL_RX_CLEAR_EXT_LOW) { OS_REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_CLEAR_EXT_LOW); } else { OS_REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_CLEAR_EXT_LOW); } } /* * HAL support code for force ppm tracking workaround. */ u_int32_t ar9300_ppm_get_rssi_dump(struct ath_hal *ah) { u_int32_t retval; u_int32_t off1; u_int32_t off2; if (OS_REG_READ(ah, AR_PHY_ANALOG_SWAP) & AR_PHY_SWAP_ALT_CHAIN) { off1 = 0x2000; off2 = 0x1000; } else { off1 = 0x1000; off2 = 0x2000; } retval = ((0xff & OS_REG_READ(ah, AR_PHY_CHAN_INFO_GAIN_0 )) << 0) | ((0xff & OS_REG_READ(ah, AR_PHY_CHAN_INFO_GAIN_0 + off1)) << 8) | ((0xff & OS_REG_READ(ah, AR_PHY_CHAN_INFO_GAIN_0 + off2)) << 16); return retval; } u_int32_t ar9300_ppm_force(struct ath_hal *ah) { u_int32_t data_fine; u_int32_t data4; //u_int32_t off1; //u_int32_t off2; HAL_BOOL signed_val = AH_FALSE; // if (OS_REG_READ(ah, AR_PHY_ANALOG_SWAP) & AR_PHY_SWAP_ALT_CHAIN) { // off1 = 0x2000; // off2 = 0x1000; // } else { // off1 = 0x1000; // off2 = 0x2000; // } data_fine = AR_PHY_CHAN_INFO_GAIN_DIFF_PPM_MASK & OS_REG_READ(ah, AR_PHY_CHNINFO_GAINDIFF); /* * bit [11-0] is new ppm value. bit 11 is the signed bit. * So check value from bit[10:0]. * Now get the abs val of the ppm value read in bit[0:11]. * After that do bound check on abs value. * if value is off limit, CAP the value and and restore signed bit. */ if (data_fine & AR_PHY_CHAN_INFO_GAIN_DIFF_PPM_SIGNED_BIT) { /* get the positive value */ data_fine = (~data_fine + 1) & AR_PHY_CHAN_INFO_GAIN_DIFF_PPM_MASK; signed_val = AH_TRUE; } if (data_fine > AR_PHY_CHAN_INFO_GAIN_DIFF_UPPER_LIMIT) { HALDEBUG(ah, HAL_DEBUG_REGIO, "%s Correcting ppm out of range %x\n", __func__, (data_fine & 0x7ff)); data_fine = AR_PHY_CHAN_INFO_GAIN_DIFF_UPPER_LIMIT; } /* * Restore signed value if changed above. * Use typecast to avoid compilation errors */ if (signed_val) { data_fine = (-(int32_t)data_fine) & AR_PHY_CHAN_INFO_GAIN_DIFF_PPM_MASK; } /* write value */ data4 = OS_REG_READ(ah, AR_PHY_TIMING2) & ~(AR_PHY_TIMING2_USE_FORCE_PPM | AR_PHY_TIMING2_FORCE_PPM_VAL); OS_REG_WRITE(ah, AR_PHY_TIMING2, data4 | data_fine | AR_PHY_TIMING2_USE_FORCE_PPM); return data_fine; } void ar9300_ppm_un_force(struct ath_hal *ah) { u_int32_t data4; data4 = OS_REG_READ(ah, AR_PHY_TIMING2) & ~AR_PHY_TIMING2_USE_FORCE_PPM; OS_REG_WRITE(ah, AR_PHY_TIMING2, data4); } u_int32_t ar9300_ppm_arm_trigger(struct ath_hal *ah) { u_int32_t val; u_int32_t ret; val = OS_REG_READ(ah, AR_PHY_CHAN_INFO_MEMORY); ret = OS_REG_READ(ah, AR_TSF_L32); OS_REG_WRITE(ah, AR_PHY_CHAN_INFO_MEMORY, val | AR_PHY_CHAN_INFO_MEMORY_CAPTURE_MASK); /* return low word of TSF at arm time */ return ret; } int ar9300_ppm_get_trigger(struct ath_hal *ah) { if (OS_REG_READ(ah, AR_PHY_CHAN_INFO_MEMORY) & AR_PHY_CHAN_INFO_MEMORY_CAPTURE_MASK) { /* has not triggered yet, return AH_FALSE */ return 0; } /* else triggered, return AH_TRUE */ return 1; } void ar9300_mark_phy_inactive(struct ath_hal *ah) { OS_REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS); } /* DEBUG */ u_int32_t ar9300_ppm_get_force_state(struct ath_hal *ah) { return OS_REG_READ(ah, AR_PHY_TIMING2) & (AR_PHY_TIMING2_USE_FORCE_PPM | AR_PHY_TIMING2_FORCE_PPM_VAL); } /* * Return the Cycle counts for rx_frame, rx_clear, and tx_frame */ HAL_BOOL ar9300_get_mib_cycle_counts(struct ath_hal *ah, HAL_SURVEY_SAMPLE *hs) { /* * XXX FreeBSD todo: reimplement this */ #if 0 p_cnts->tx_frame_count = OS_REG_READ(ah, AR_TFCNT); p_cnts->rx_frame_count = OS_REG_READ(ah, AR_RFCNT); p_cnts->rx_clear_count = OS_REG_READ(ah, AR_RCCNT); p_cnts->cycle_count = OS_REG_READ(ah, AR_CCCNT); p_cnts->is_tx_active = (OS_REG_READ(ah, AR_TFCNT) == p_cnts->tx_frame_count) ? AH_FALSE : AH_TRUE; p_cnts->is_rx_active = (OS_REG_READ(ah, AR_RFCNT) == p_cnts->rx_frame_count) ? AH_FALSE : AH_TRUE; #endif return AH_FALSE; } void ar9300_clear_mib_counters(struct ath_hal *ah) { u_int32_t reg_val; reg_val = OS_REG_READ(ah, AR_MIBC); OS_REG_WRITE(ah, AR_MIBC, reg_val | AR_MIBC_CMC); OS_REG_WRITE(ah, AR_MIBC, reg_val & ~AR_MIBC_CMC); } /* Enable or Disable RIFS Rx capability as part of SW WAR for Bug 31602 */ HAL_BOOL ar9300_set_rifs_delay(struct ath_hal *ah, HAL_BOOL enable) { struct ath_hal_9300 *ahp = AH9300(ah); HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, AH_PRIVATE(ah)->ah_curchan); HAL_BOOL is_chan_2g = IS_CHAN_2GHZ(ichan); u_int32_t tmp = 0; if (enable) { if (ahp->ah_rifs_enabled == AH_TRUE) { return AH_TRUE; } OS_REG_WRITE(ah, AR_PHY_SEARCH_START_DELAY, ahp->ah_rifs_reg[0]); OS_REG_WRITE(ah, AR_PHY_RIFS_SRCH, ahp->ah_rifs_reg[1]); ahp->ah_rifs_enabled = AH_TRUE; OS_MEMZERO(ahp->ah_rifs_reg, sizeof(ahp->ah_rifs_reg)); } else { if (ahp->ah_rifs_enabled == AH_TRUE) { ahp->ah_rifs_reg[0] = OS_REG_READ(ah, AR_PHY_SEARCH_START_DELAY); ahp->ah_rifs_reg[1] = OS_REG_READ(ah, AR_PHY_RIFS_SRCH); } /* Change rifs init delay to 0 */ OS_REG_WRITE(ah, AR_PHY_RIFS_SRCH, (ahp->ah_rifs_reg[1] & ~(AR_PHY_RIFS_INIT_DELAY))); tmp = 0xfffff000 & OS_REG_READ(ah, AR_PHY_SEARCH_START_DELAY); if (is_chan_2g) { if (IEEE80211_IS_CHAN_HT40(AH_PRIVATE(ah)->ah_curchan)) { OS_REG_WRITE(ah, AR_PHY_SEARCH_START_DELAY, tmp | 500); } else { /* Sowl 2G HT-20 default is 0x134 for search start delay */ OS_REG_WRITE(ah, AR_PHY_SEARCH_START_DELAY, tmp | 250); } } else { if (IEEE80211_IS_CHAN_HT40(AH_PRIVATE(ah)->ah_curchan)) { OS_REG_WRITE(ah, AR_PHY_SEARCH_START_DELAY, tmp | 0x370); } else { /* Sowl 5G HT-20 default is 0x1b8 for search start delay */ OS_REG_WRITE(ah, AR_PHY_SEARCH_START_DELAY, tmp | 0x1b8); } } ahp->ah_rifs_enabled = AH_FALSE; } return AH_TRUE; } /* ar9300_set_rifs_delay () */ /* Set the current RIFS Rx setting */ HAL_BOOL ar9300_set_11n_rx_rifs(struct ath_hal *ah, HAL_BOOL enable) { /* Non-Owl 11n chips */ if ((ath_hal_getcapability(ah, HAL_CAP_RIFS_RX, 0, AH_NULL) == HAL_OK)) { if (ar9300_get_capability(ah, HAL_CAP_LDPCWAR, 0, AH_NULL) == HAL_OK) { return ar9300_set_rifs_delay(ah, enable); } return AH_FALSE; } return AH_TRUE; } /* ar9300_set_11n_rx_rifs () */ static hal_mac_hangs_t ar9300_compare_dbg_hang(struct ath_hal *ah, mac_dbg_regs_t mac_dbg, hal_mac_hang_check_t hang_check, hal_mac_hangs_t hangs, u_int8_t *dcu_chain) { int i = 0; hal_mac_hangs_t found_hangs = 0; if (hangs & dcu_chain_state) { for (i = 0; i < 6; i++) { if (((mac_dbg.dma_dbg_4 >> (5 * i)) & 0x1f) == hang_check.dcu_chain_state) { found_hangs |= dcu_chain_state; *dcu_chain = i; } } for (i = 0; i < 4; i++) { if (((mac_dbg.dma_dbg_5 >> (5 * i)) & 0x1f) == hang_check.dcu_chain_state) { found_hangs |= dcu_chain_state; *dcu_chain = i + 6; } } } if (hangs & dcu_complete_state) { if ((mac_dbg.dma_dbg_6 & 0x3) == hang_check.dcu_complete_state) { found_hangs |= dcu_complete_state; } } return found_hangs; } /* end - ar9300_compare_dbg_hang */ #define NUM_STATUS_READS 50 HAL_BOOL ar9300_detect_mac_hang(struct ath_hal *ah) { struct ath_hal_9300 *ahp = AH9300(ah); mac_dbg_regs_t mac_dbg; hal_mac_hang_check_t hang_sig1_val = {0x6, 0x1, 0, 0, 0, 0, 0, 0}; hal_mac_hangs_t hang_sig1 = (dcu_chain_state | dcu_complete_state); int i = 0; u_int8_t dcu_chain = 0, current_dcu_chain_state, shift_val; if (!(ahp->ah_hang_wars & HAL_MAC_HANG_WAR)) { return AH_FALSE; } OS_MEMZERO(&mac_dbg, sizeof(mac_dbg)); mac_dbg.dma_dbg_4 = OS_REG_READ(ah, AR_DMADBG_4); mac_dbg.dma_dbg_5 = OS_REG_READ(ah, AR_DMADBG_5); mac_dbg.dma_dbg_6 = OS_REG_READ(ah, AR_DMADBG_6); HALDEBUG(ah, HAL_DEBUG_DFS, " dma regs: %X %X %X \n", mac_dbg.dma_dbg_4, mac_dbg.dma_dbg_5, mac_dbg.dma_dbg_6); if (hang_sig1 != ar9300_compare_dbg_hang(ah, mac_dbg, hang_sig1_val, hang_sig1, &dcu_chain)) { HALDEBUG(ah, HAL_DEBUG_DFS, " hang sig1 not found \n"); return AH_FALSE; } shift_val = (dcu_chain >= 6) ? (dcu_chain-6) : (dcu_chain); shift_val *= 5; for (i = 1; i <= NUM_STATUS_READS; i++) { if (dcu_chain < 6) { mac_dbg.dma_dbg_4 = OS_REG_READ(ah, AR_DMADBG_4); current_dcu_chain_state = ((mac_dbg.dma_dbg_4 >> shift_val) & 0x1f); } else { mac_dbg.dma_dbg_5 = OS_REG_READ(ah, AR_DMADBG_5); current_dcu_chain_state = ((mac_dbg.dma_dbg_5 >> shift_val) & 0x1f); } mac_dbg.dma_dbg_6 = OS_REG_READ(ah, AR_DMADBG_6); if (((mac_dbg.dma_dbg_6 & 0x3) != hang_sig1_val.dcu_complete_state) || (current_dcu_chain_state != hang_sig1_val.dcu_chain_state)) { return AH_FALSE; } } HALDEBUG(ah, HAL_DEBUG_DFS, "%s sig5count=%d sig6count=%d ", __func__, ahp->ah_hang[MAC_HANG_SIG1], ahp->ah_hang[MAC_HANG_SIG2]); ahp->ah_hang[MAC_HANG_SIG1]++; return AH_TRUE; } /* end - ar9300_detect_mac_hang */ /* Determine if the baseband is hung by reading the Observation Bus Register */ HAL_BOOL ar9300_detect_bb_hang(struct ath_hal *ah) { #define N(a) (sizeof(a) / sizeof(a[0])) struct ath_hal_9300 *ahp = AH9300(ah); u_int32_t hang_sig = 0; int i = 0; /* Check the PCU Observation Bus 1 register (0x806c) NUM_STATUS_READS times * * 4 known BB hang signatures - * [1] bits 8,9,11 are 0. State machine state (bits 25-31) is 0x1E * [2] bits 8,9 are 1, bit 11 is 0. State machine state (bits 25-31) is 0x52 * [3] bits 8,9 are 1, bit 11 is 0. State machine state (bits 25-31) is 0x18 * [4] bit 10 is 1, bit 11 is 0. WEP state (bits 12-17) is 0x2, * Rx State (bits 20-24) is 0x7. */ hal_hw_hang_check_t hang_list [] = { /* Offset Reg Value Reg Mask Hang Offset */ {AR_OBS_BUS_1, 0x1E000000, 0x7E000B00, BB_HANG_SIG1}, {AR_OBS_BUS_1, 0x52000B00, 0x7E000B00, BB_HANG_SIG2}, {AR_OBS_BUS_1, 0x18000B00, 0x7E000B00, BB_HANG_SIG3}, {AR_OBS_BUS_1, 0x00702400, 0x7E7FFFEF, BB_HANG_SIG4} }; if (!(ahp->ah_hang_wars & (HAL_RIFS_BB_HANG_WAR | HAL_DFS_BB_HANG_WAR | HAL_RX_STUCK_LOW_BB_HANG_WAR))) { return AH_FALSE; } hang_sig = OS_REG_READ(ah, AR_OBS_BUS_1); for (i = 1; i <= NUM_STATUS_READS; i++) { if (hang_sig != OS_REG_READ(ah, AR_OBS_BUS_1)) { return AH_FALSE; } } for (i = 0; i < N(hang_list); i++) { if ((hang_sig & hang_list[i].hang_mask) == hang_list[i].hang_val) { ahp->ah_hang[hang_list[i].hang_offset]++; HALDEBUG(ah, HAL_DEBUG_DFS, "%s sig1count=%d sig2count=%d " "sig3count=%d sig4count=%d\n", __func__, ahp->ah_hang[BB_HANG_SIG1], ahp->ah_hang[BB_HANG_SIG2], ahp->ah_hang[BB_HANG_SIG3], ahp->ah_hang[BB_HANG_SIG4]); return AH_TRUE; } } HALDEBUG(ah, HAL_DEBUG_DFS, "%s Found an unknown BB hang signature! " "<0x806c>=0x%x\n", __func__, hang_sig); return AH_FALSE; #undef N } /* end - ar9300_detect_bb_hang () */ #undef NUM_STATUS_READS HAL_STATUS ar9300_select_ant_config(struct ath_hal *ah, u_int32_t cfg) { struct ath_hal_9300 *ahp = AH9300(ah); const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan; HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); const HAL_CAPABILITIES *p_cap = &AH_PRIVATE(ah)->ah_caps; u_int16_t ant_config; u_int32_t hal_num_ant_config; hal_num_ant_config = IS_CHAN_2GHZ(ichan) ? p_cap->halNumAntCfg2GHz: p_cap->halNumAntCfg5GHz; if (cfg < hal_num_ant_config) { if (HAL_OK == ar9300_eeprom_get_ant_cfg(ahp, chan, cfg, &ant_config)) { OS_REG_WRITE(ah, AR_PHY_SWITCH_COM, ant_config); return HAL_OK; } } return HAL_EINVAL; } /* * Functions to get/set DCS mode */ void ar9300_set_dcs_mode(struct ath_hal *ah, u_int32_t mode) { AH9300(ah)->ah_dcs_enable = mode; } u_int32_t ar9300_get_dcs_mode(struct ath_hal *ah) { return AH9300(ah)->ah_dcs_enable; } #if ATH_BT_COEX void ar9300_set_bt_coex_info(struct ath_hal *ah, HAL_BT_COEX_INFO *btinfo) { struct ath_hal_9300 *ahp = AH9300(ah); ahp->ah_bt_module = btinfo->bt_module; ahp->ah_bt_coex_config_type = btinfo->bt_coex_config; ahp->ah_bt_active_gpio_select = btinfo->bt_gpio_bt_active; ahp->ah_bt_priority_gpio_select = btinfo->bt_gpio_bt_priority; ahp->ah_wlan_active_gpio_select = btinfo->bt_gpio_wlan_active; ahp->ah_bt_active_polarity = btinfo->bt_active_polarity; ahp->ah_bt_coex_single_ant = btinfo->bt_single_ant; ahp->ah_bt_wlan_isolation = btinfo->bt_isolation; } void ar9300_bt_coex_config(struct ath_hal *ah, HAL_BT_COEX_CONFIG *btconf) { struct ath_hal_9300 *ahp = AH9300(ah); HAL_BOOL rx_clear_polarity; /* * For Kiwi and Osprey, the polarity of rx_clear is active high. * The bt_rxclear_polarity flag from ath_dev needs to be inverted. */ rx_clear_polarity = !btconf->bt_rxclear_polarity; ahp->ah_bt_coex_mode = (ahp->ah_bt_coex_mode & AR_BT_QCU_THRESH) | SM(btconf->bt_time_extend, AR_BT_TIME_EXTEND) | SM(btconf->bt_txstate_extend, AR_BT_TXSTATE_EXTEND) | SM(btconf->bt_txframe_extend, AR_BT_TX_FRAME_EXTEND) | SM(btconf->bt_mode, AR_BT_MODE) | SM(btconf->bt_quiet_collision, AR_BT_QUIET) | SM(rx_clear_polarity, AR_BT_RX_CLEAR_POLARITY) | SM(btconf->bt_priority_time, AR_BT_PRIORITY_TIME) | SM(btconf->bt_first_slot_time, AR_BT_FIRST_SLOT_TIME); ahp->ah_bt_coex_mode2 |= SM(btconf->bt_hold_rxclear, AR_BT_HOLD_RX_CLEAR); if (ahp->ah_bt_coex_single_ant == AH_FALSE) { /* Enable ACK to go out even though BT has higher priority. */ ahp->ah_bt_coex_mode2 |= AR_BT_DISABLE_BT_ANT; } } void ar9300_bt_coex_set_qcu_thresh(struct ath_hal *ah, int qnum) { struct ath_hal_9300 *ahp = AH9300(ah); /* clear the old value, then set the new value */ ahp->ah_bt_coex_mode &= ~AR_BT_QCU_THRESH; ahp->ah_bt_coex_mode |= SM(qnum, AR_BT_QCU_THRESH); } void ar9300_bt_coex_set_weights(struct ath_hal *ah, u_int32_t stomp_type) { struct ath_hal_9300 *ahp = AH9300(ah); ahp->ah_bt_coex_bt_weight[0] = AR9300_BT_WGHT; ahp->ah_bt_coex_bt_weight[1] = AR9300_BT_WGHT; ahp->ah_bt_coex_bt_weight[2] = AR9300_BT_WGHT; ahp->ah_bt_coex_bt_weight[3] = AR9300_BT_WGHT; switch (stomp_type) { case HAL_BT_COEX_STOMP_ALL: ahp->ah_bt_coex_wlan_weight[0] = AR9300_STOMP_ALL_WLAN_WGHT0; ahp->ah_bt_coex_wlan_weight[1] = AR9300_STOMP_ALL_WLAN_WGHT1; break; case HAL_BT_COEX_STOMP_LOW: ahp->ah_bt_coex_wlan_weight[0] = AR9300_STOMP_LOW_WLAN_WGHT0; ahp->ah_bt_coex_wlan_weight[1] = AR9300_STOMP_LOW_WLAN_WGHT1; break; case HAL_BT_COEX_STOMP_ALL_FORCE: ahp->ah_bt_coex_wlan_weight[0] = AR9300_STOMP_ALL_FORCE_WLAN_WGHT0; ahp->ah_bt_coex_wlan_weight[1] = AR9300_STOMP_ALL_FORCE_WLAN_WGHT1; break; case HAL_BT_COEX_STOMP_LOW_FORCE: ahp->ah_bt_coex_wlan_weight[0] = AR9300_STOMP_LOW_FORCE_WLAN_WGHT0; ahp->ah_bt_coex_wlan_weight[1] = AR9300_STOMP_LOW_FORCE_WLAN_WGHT1; break; case HAL_BT_COEX_STOMP_NONE: case HAL_BT_COEX_NO_STOMP: ahp->ah_bt_coex_wlan_weight[0] = AR9300_STOMP_NONE_WLAN_WGHT0; ahp->ah_bt_coex_wlan_weight[1] = AR9300_STOMP_NONE_WLAN_WGHT1; break; default: /* There is a force_weight from registry */ ahp->ah_bt_coex_wlan_weight[0] = stomp_type; ahp->ah_bt_coex_wlan_weight[1] = stomp_type; break; } } void ar9300_bt_coex_setup_bmiss_thresh(struct ath_hal *ah, u_int32_t thresh) { struct ath_hal_9300 *ahp = AH9300(ah); /* clear the old value, then set the new value */ ahp->ah_bt_coex_mode2 &= ~AR_BT_BCN_MISS_THRESH; ahp->ah_bt_coex_mode2 |= SM(thresh, AR_BT_BCN_MISS_THRESH); } static void ar9300_bt_coex_antenna_diversity(struct ath_hal *ah, u_int32_t value) { struct ath_hal_9300 *ahp = AH9300(ah); #if ATH_ANT_DIV_COMB //struct ath_hal_private *ahpriv = AH_PRIVATE(ah); const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan; #endif if (ahp->ah_bt_coex_flag & HAL_BT_COEX_FLAG_ANT_DIV_ALLOW) { if (ahp->ah_diversity_control == HAL_ANT_VARIABLE) { /* Config antenna diversity */ #if ATH_ANT_DIV_COMB ar9300_ant_ctrl_set_lna_div_use_bt_ant(ah, value, chan); #endif } } } void ar9300_bt_coex_set_parameter(struct ath_hal *ah, u_int32_t type, u_int32_t value) { struct ath_hal_9300 *ahp = AH9300(ah); struct ath_hal_private *ahpriv = AH_PRIVATE(ah); switch (type) { case HAL_BT_COEX_SET_ACK_PWR: if (value) { ahp->ah_bt_coex_flag |= HAL_BT_COEX_FLAG_LOW_ACK_PWR; } else { ahp->ah_bt_coex_flag &= ~HAL_BT_COEX_FLAG_LOW_ACK_PWR; } ar9300_set_tx_power_limit(ah, ahpriv->ah_powerLimit, ahpriv->ah_extraTxPow, 0); break; case HAL_BT_COEX_ANTENNA_DIVERSITY: if (AR_SREV_POSEIDON(ah)) { ahp->ah_bt_coex_flag |= HAL_BT_COEX_FLAG_ANT_DIV_ALLOW; if (value) { ahp->ah_bt_coex_flag |= HAL_BT_COEX_FLAG_ANT_DIV_ENABLE; } else { ahp->ah_bt_coex_flag &= ~HAL_BT_COEX_FLAG_ANT_DIV_ENABLE; } ar9300_bt_coex_antenna_diversity(ah, value); } break; case HAL_BT_COEX_LOWER_TX_PWR: if (value) { ahp->ah_bt_coex_flag |= HAL_BT_COEX_FLAG_LOWER_TX_PWR; } else { ahp->ah_bt_coex_flag &= ~HAL_BT_COEX_FLAG_LOWER_TX_PWR; } ar9300_set_tx_power_limit(ah, ahpriv->ah_powerLimit, ahpriv->ah_extraTxPow, 0); break; #if ATH_SUPPORT_MCI case HAL_BT_COEX_MCI_MAX_TX_PWR: if ((ah->ah_config.ath_hal_mci_config & ATH_MCI_CONFIG_CONCUR_TX) == ATH_MCI_CONCUR_TX_SHARED_CHN) { if (value) { ahp->ah_bt_coex_flag |= HAL_BT_COEX_FLAG_MCI_MAX_TX_PWR; ahp->ah_mci_concur_tx_en = AH_TRUE; } else { ahp->ah_bt_coex_flag &= ~HAL_BT_COEX_FLAG_MCI_MAX_TX_PWR; ahp->ah_mci_concur_tx_en = AH_FALSE; } ar9300_set_tx_power_limit(ah, ahpriv->ah_powerLimit, ahpriv->ah_extraTxPow, 0); } HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) concur_tx_en = %d\n", ahp->ah_mci_concur_tx_en); break; case HAL_BT_COEX_MCI_FTP_STOMP_RX: if (value) { ahp->ah_bt_coex_flag |= HAL_BT_COEX_FLAG_MCI_FTP_STOMP_RX; } else { ahp->ah_bt_coex_flag &= ~HAL_BT_COEX_FLAG_MCI_FTP_STOMP_RX; } break; #endif default: break; } } void ar9300_bt_coex_disable(struct ath_hal *ah) { struct ath_hal_9300 *ahp = AH9300(ah); /* Always drive rx_clear_external output as 0 */ ath_hal_gpioCfgOutput(ah, ahp->ah_wlan_active_gpio_select, HAL_GPIO_OUTPUT_MUX_AS_OUTPUT); if (ahp->ah_bt_coex_single_ant == AH_TRUE) { OS_REG_RMW_FIELD(ah, AR_QUIET1, AR_QUIET1_QUIET_ACK_CTS_ENABLE, 1); OS_REG_RMW_FIELD(ah, AR_PCU_MISC, AR_PCU_BT_ANT_PREVENT_RX, 0); } OS_REG_WRITE(ah, AR_BT_COEX_MODE, AR_BT_QUIET | AR_BT_MODE); OS_REG_WRITE(ah, AR_BT_COEX_MODE2, 0); OS_REG_WRITE(ah, AR_BT_COEX_WL_WEIGHTS0, 0); OS_REG_WRITE(ah, AR_BT_COEX_WL_WEIGHTS1, 0); OS_REG_WRITE(ah, AR_BT_COEX_BT_WEIGHTS0, 0); OS_REG_WRITE(ah, AR_BT_COEX_BT_WEIGHTS1, 0); OS_REG_WRITE(ah, AR_BT_COEX_BT_WEIGHTS2, 0); OS_REG_WRITE(ah, AR_BT_COEX_BT_WEIGHTS3, 0); ahp->ah_bt_coex_enabled = AH_FALSE; } int ar9300_bt_coex_enable(struct ath_hal *ah) { struct ath_hal_9300 *ahp = AH9300(ah); /* Program coex mode and weight registers to actually enable coex */ OS_REG_WRITE(ah, AR_BT_COEX_MODE, ahp->ah_bt_coex_mode); OS_REG_WRITE(ah, AR_BT_COEX_MODE2, ahp->ah_bt_coex_mode2); OS_REG_WRITE(ah, AR_BT_COEX_WL_WEIGHTS0, ahp->ah_bt_coex_wlan_weight[0]); OS_REG_WRITE(ah, AR_BT_COEX_WL_WEIGHTS1, ahp->ah_bt_coex_wlan_weight[1]); OS_REG_WRITE(ah, AR_BT_COEX_BT_WEIGHTS0, ahp->ah_bt_coex_bt_weight[0]); OS_REG_WRITE(ah, AR_BT_COEX_BT_WEIGHTS1, ahp->ah_bt_coex_bt_weight[1]); OS_REG_WRITE(ah, AR_BT_COEX_BT_WEIGHTS2, ahp->ah_bt_coex_bt_weight[2]); OS_REG_WRITE(ah, AR_BT_COEX_BT_WEIGHTS3, ahp->ah_bt_coex_bt_weight[3]); if (ahp->ah_bt_coex_flag & HAL_BT_COEX_FLAG_LOW_ACK_PWR) { OS_REG_WRITE(ah, AR_TPC, HAL_BT_COEX_LOW_ACK_POWER); } else { OS_REG_WRITE(ah, AR_TPC, HAL_BT_COEX_HIGH_ACK_POWER); } OS_REG_RMW_FIELD(ah, AR_QUIET1, AR_QUIET1_QUIET_ACK_CTS_ENABLE, 1); if (ahp->ah_bt_coex_single_ant == AH_TRUE) { OS_REG_RMW_FIELD(ah, AR_PCU_MISC, AR_PCU_BT_ANT_PREVENT_RX, 1); } else { OS_REG_RMW_FIELD(ah, AR_PCU_MISC, AR_PCU_BT_ANT_PREVENT_RX, 0); } if (ahp->ah_bt_coex_config_type == HAL_BT_COEX_CFG_3WIRE) { /* For 3-wire, configure the desired GPIO port for rx_clear */ ath_hal_gpioCfgOutput(ah, ahp->ah_wlan_active_gpio_select, HAL_GPIO_OUTPUT_MUX_AS_WLAN_ACTIVE); } else if ((ahp->ah_bt_coex_config_type >= HAL_BT_COEX_CFG_2WIRE_2CH) && (ahp->ah_bt_coex_config_type <= HAL_BT_COEX_CFG_2WIRE_CH0)) { /* For 2-wire, configure the desired GPIO port for TX_FRAME output */ ath_hal_gpioCfgOutput(ah, ahp->ah_wlan_active_gpio_select, HAL_GPIO_OUTPUT_MUX_AS_TX_FRAME); } /* * Enable a weak pull down on BT_ACTIVE. * When BT device is disabled, BT_ACTIVE might be floating. */ OS_REG_RMW(ah, AR_HOSTIF_REG(ah, AR_GPIO_PDPU), (AR_GPIO_PULL_DOWN << (ahp->ah_bt_active_gpio_select * 2)), (AR_GPIO_PDPU_OPTION << (ahp->ah_bt_active_gpio_select * 2))); ahp->ah_bt_coex_enabled = AH_TRUE; return 0; } u_int32_t ar9300_get_bt_active_gpio(struct ath_hal *ah, u_int32_t reg) { return 0; } u_int32_t ar9300_get_wlan_active_gpio(struct ath_hal *ah, u_int32_t reg,u_int32_t bOn) { return bOn; } void ar9300_init_bt_coex(struct ath_hal *ah) { struct ath_hal_9300 *ahp = AH9300(ah); if (ahp->ah_bt_coex_config_type == HAL_BT_COEX_CFG_3WIRE) { OS_REG_SET_BIT(ah, AR_HOSTIF_REG(ah, AR_GPIO_INPUT_EN_VAL), (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB | AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB)); /* * Set input mux for bt_prority_async and * bt_active_async to GPIO pins */ OS_REG_RMW_FIELD(ah, AR_HOSTIF_REG(ah, AR_GPIO_INPUT_MUX1), AR_GPIO_INPUT_MUX1_BT_ACTIVE, ahp->ah_bt_active_gpio_select); OS_REG_RMW_FIELD(ah, AR_HOSTIF_REG(ah, AR_GPIO_INPUT_MUX1), AR_GPIO_INPUT_MUX1_BT_PRIORITY, ahp->ah_bt_priority_gpio_select); /* Configure the desired GPIO ports for input */ ath_hal_gpioCfgInput(ah, ahp->ah_bt_active_gpio_select); ath_hal_gpioCfgInput(ah, ahp->ah_bt_priority_gpio_select); if (ahp->ah_bt_coex_enabled) { ar9300_bt_coex_enable(ah); } else { ar9300_bt_coex_disable(ah); } } else if ((ahp->ah_bt_coex_config_type >= HAL_BT_COEX_CFG_2WIRE_2CH) && (ahp->ah_bt_coex_config_type <= HAL_BT_COEX_CFG_2WIRE_CH0)) { /* 2-wire */ if (ahp->ah_bt_coex_enabled) { /* Connect bt_active_async to baseband */ OS_REG_CLR_BIT(ah, AR_HOSTIF_REG(ah, AR_GPIO_INPUT_EN_VAL), (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_DEF | AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_DEF)); OS_REG_SET_BIT(ah, AR_HOSTIF_REG(ah, AR_GPIO_INPUT_EN_VAL), AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB); /* * Set input mux for bt_prority_async and * bt_active_async to GPIO pins */ OS_REG_RMW_FIELD(ah, AR_HOSTIF_REG(ah, AR_GPIO_INPUT_MUX1), AR_GPIO_INPUT_MUX1_BT_ACTIVE, ahp->ah_bt_active_gpio_select); /* Configure the desired GPIO ports for input */ ath_hal_gpioCfgInput(ah, ahp->ah_bt_active_gpio_select); /* Enable coexistence on initialization */ ar9300_bt_coex_enable(ah); } } #if ATH_SUPPORT_MCI else if (ahp->ah_bt_coex_config_type == HAL_BT_COEX_CFG_MCI) { if (ahp->ah_bt_coex_enabled) { ar9300_mci_bt_coex_enable(ah); } else { ar9300_mci_bt_coex_disable(ah); } } #endif /* ATH_SUPPORT_MCI */ } #endif /* ATH_BT_COEX */ HAL_STATUS ar9300_set_proxy_sta(struct ath_hal *ah, HAL_BOOL enable) { u_int32_t val; int wasp_mm_rev; #define AR_SOC_RST_REVISION_ID 0xB8060090 #define REG_READ(_reg) *((volatile u_int32_t *)(_reg)) wasp_mm_rev = (REG_READ(AR_SOC_RST_REVISION_ID) & AR_SREV_REVISION_WASP_MINOR_MINOR_MASK) >> AR_SREV_REVISION_WASP_MINOR_MINOR_SHIFT; #undef AR_SOC_RST_REVISION_ID #undef REG_READ /* * Azimuth (ProxySTA) Mode is only supported correctly by * Peacock or WASP 1.3.0.1 or later (hopefully) chips. * * Enable this feature for Scorpion at this time. The silicon * still needs to be validated. */ if (!(AH_PRIVATE((ah))->ah_macVersion == AR_SREV_VERSION_AR9580) && !(AH_PRIVATE((ah))->ah_macVersion == AR_SREV_VERSION_SCORPION) && !((AH_PRIVATE((ah))->ah_macVersion == AR_SREV_VERSION_WASP) && ((AH_PRIVATE((ah))->ah_macRev > AR_SREV_REVISION_WASP_13) || (AH_PRIVATE((ah))->ah_macRev == AR_SREV_REVISION_WASP_13 && wasp_mm_rev >= 0 /* 1 */)))) { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s error: current chip (ver 0x%x, " "rev 0x%x, minor minor rev 0x%x) cannot support Azimuth Mode\n", __func__, AH_PRIVATE((ah))->ah_macVersion, AH_PRIVATE((ah))->ah_macRev, wasp_mm_rev); return HAL_ENOTSUPP; } OS_REG_WRITE(ah, AR_MAC_PCU_LOGIC_ANALYZER, AR_MAC_PCU_LOGIC_ANALYZER_PSTABUG75996); /* turn on mode bit[24] for proxy sta */ OS_REG_WRITE(ah, AR_PCU_MISC_MODE2, OS_REG_READ(ah, AR_PCU_MISC_MODE2) | AR_PCU_MISC_MODE2_PROXY_STA); val = OS_REG_READ(ah, AR_AZIMUTH_MODE); if (enable) { val |= AR_AZIMUTH_KEY_SEARCH_AD1 | AR_AZIMUTH_CTS_MATCH_TX_AD2 | AR_AZIMUTH_BA_USES_AD1; /* turn off filter pass hold (bit 9) */ val &= ~AR_AZIMUTH_FILTER_PASS_HOLD; } else { val &= ~(AR_AZIMUTH_KEY_SEARCH_AD1 | AR_AZIMUTH_CTS_MATCH_TX_AD2 | AR_AZIMUTH_BA_USES_AD1); } OS_REG_WRITE(ah, AR_AZIMUTH_MODE, val); /* enable promiscous mode */ OS_REG_WRITE(ah, AR_RX_FILTER, OS_REG_READ(ah, AR_RX_FILTER) | HAL_RX_FILTER_PROM); /* enable promiscous in azimuth mode */ OS_REG_WRITE(ah, AR_PCU_MISC_MODE2, AR_PCU_MISC_MODE2_PROM_VC_MODE); OS_REG_WRITE(ah, AR_MAC_PCU_LOGIC_ANALYZER, AR_MAC_PCU_LOGIC_ANALYZER_VC_MODE); /* turn on filter pass hold (bit 9) */ OS_REG_WRITE(ah, AR_AZIMUTH_MODE, OS_REG_READ(ah, AR_AZIMUTH_MODE) | AR_AZIMUTH_FILTER_PASS_HOLD); return HAL_OK; } #if 0 void ar9300_mat_enable(struct ath_hal *ah, int enable) { /* * MAT (s/w ProxySTA) implementation requires to turn off interrupt * mitigation and turn on key search always for better performance. */ struct ath_hal_9300 *ahp = AH9300(ah); struct ath_hal_private *ap = AH_PRIVATE(ah); ahp->ah_intr_mitigation_rx = !enable; if (ahp->ah_intr_mitigation_rx) { /* * Enable Interrupt Mitigation for Rx. * If no build-specific limits for the rx interrupt mitigation * timer have been specified, use conservative defaults. */ #ifndef AH_RIMT_VAL_LAST #define AH_RIMT_LAST_MICROSEC 500 #endif #ifndef AH_RIMT_VAL_FIRST #define AH_RIMT_FIRST_MICROSEC 2000 #endif OS_REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_LAST, AH_RIMT_LAST_MICROSEC); OS_REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_FIRST, AH_RIMT_FIRST_MICROSEC); } else { OS_REG_WRITE(ah, AR_RIMT, 0); } ahp->ah_enable_keysearch_always = !!enable; ar9300_enable_keysearch_always(ah, ahp->ah_enable_keysearch_always); } #endif void ar9300_enable_tpc(struct ath_hal *ah) { u_int32_t val = 0; ah->ah_config.ath_hal_desc_tpc = 1; /* Enable TPC */ OS_REG_RMW_FIELD(ah, AR_PHY_PWRTX_MAX, AR_PHY_PER_PACKET_POWERTX_MAX, 1); /* * Disable per chain power reduction since we are already * accounting for this in our calculations */ val = OS_REG_READ(ah, AR_PHY_POWER_TX_SUB); if (AR_SREV_WASP(ah)) { OS_REG_WRITE(ah, AR_PHY_POWER_TX_SUB, val & AR_PHY_POWER_TX_SUB_2_DISABLE); } else { OS_REG_WRITE(ah, AR_PHY_POWER_TX_SUB, val & AR_PHY_POWER_TX_SUB_3_DISABLE); } } /* * ar9300_force_tsf_sync * This function forces the TSF sync to the given bssid, this is implemented * as a temp hack to get the AoW demo, and is primarily used in the WDS client * mode of operation, where we sync the TSF to RootAP TSF values */ void ar9300_force_tsf_sync(struct ath_hal *ah, const u_int8_t *bssid, u_int16_t assoc_id) { ar9300_set_operating_mode(ah, HAL_M_STA); ar9300_write_associd(ah, bssid, assoc_id); } void ar9300_chk_rssi_update_tx_pwr(struct ath_hal *ah, int rssi) { struct ath_hal_9300 *ahp = AH9300(ah); u_int32_t temp_obdb_reg_val = 0, temp_tcp_reg_val; u_int32_t temp_powertx_rate9_reg_val; int8_t olpc_power_offset = 0; int8_t tmp_olpc_val = 0; HAL_RSSI_TX_POWER old_greentx_status; u_int8_t target_power_val_t[ar9300_rate_size]; int8_t tmp_rss1_thr1, tmp_rss1_thr2; if ((AH_PRIVATE(ah)->ah_opmode != HAL_M_STA) || !ah->ah_config.ath_hal_sta_update_tx_pwr_enable) { return; } old_greentx_status = AH9300(ah)->green_tx_status; if (ahp->ah_hw_green_tx_enable) { tmp_rss1_thr1 = AR9485_HW_GREEN_TX_THRES1_DB; tmp_rss1_thr2 = AR9485_HW_GREEN_TX_THRES2_DB; } else { tmp_rss1_thr1 = WB225_SW_GREEN_TX_THRES1_DB; tmp_rss1_thr2 = WB225_SW_GREEN_TX_THRES2_DB; } if ((ah->ah_config.ath_hal_sta_update_tx_pwr_enable_S1) && (rssi > tmp_rss1_thr1)) { if (old_greentx_status != HAL_RSSI_TX_POWER_SHORT) { AH9300(ah)->green_tx_status = HAL_RSSI_TX_POWER_SHORT; } } else if (ah->ah_config.ath_hal_sta_update_tx_pwr_enable_S2 && (rssi > tmp_rss1_thr2)) { if (old_greentx_status != HAL_RSSI_TX_POWER_MIDDLE) { AH9300(ah)->green_tx_status = HAL_RSSI_TX_POWER_MIDDLE; } } else if (ah->ah_config.ath_hal_sta_update_tx_pwr_enable_S3) { if (old_greentx_status != HAL_RSSI_TX_POWER_LONG) { AH9300(ah)->green_tx_status = HAL_RSSI_TX_POWER_LONG; } } /* If status is not change, don't do anything */ if (old_greentx_status == AH9300(ah)->green_tx_status) { return; } /* for Poseidon which ath_hal_sta_update_tx_pwr_enable is enabled */ if ((AH9300(ah)->green_tx_status != HAL_RSSI_TX_POWER_NONE) && AR_SREV_POSEIDON(ah)) { if (ahp->ah_hw_green_tx_enable) { switch (AH9300(ah)->green_tx_status) { case HAL_RSSI_TX_POWER_SHORT: /* 1. TxPower Config */ OS_MEMCPY(target_power_val_t, ar9485_hw_gtx_tp_distance_short, sizeof(target_power_val_t)); /* 1.1 Store OLPC Delta Calibration Offset*/ olpc_power_offset = 0; /* 2. Store OB/DB */ /* 3. Store TPC settting */ temp_tcp_reg_val = (SM(14, AR_TPC_ACK) | SM(14, AR_TPC_CTS) | SM(14, AR_TPC_CHIRP) | SM(14, AR_TPC_RPT)); /* 4. Store BB_powertx_rate9 value */ temp_powertx_rate9_reg_val = AR9485_BBPWRTXRATE9_HW_GREEN_TX_SHORT_VALUE; break; case HAL_RSSI_TX_POWER_MIDDLE: /* 1. TxPower Config */ OS_MEMCPY(target_power_val_t, ar9485_hw_gtx_tp_distance_middle, sizeof(target_power_val_t)); /* 1.1 Store OLPC Delta Calibration Offset*/ olpc_power_offset = 0; /* 2. Store OB/DB */ /* 3. Store TPC settting */ temp_tcp_reg_val = (SM(18, AR_TPC_ACK) | SM(18, AR_TPC_CTS) | SM(18, AR_TPC_CHIRP) | SM(18, AR_TPC_RPT)); /* 4. Store BB_powertx_rate9 value */ temp_powertx_rate9_reg_val = AR9485_BBPWRTXRATE9_HW_GREEN_TX_MIDDLE_VALUE; break; case HAL_RSSI_TX_POWER_LONG: default: /* 1. TxPower Config */ OS_MEMCPY(target_power_val_t, ahp->ah_default_tx_power, sizeof(target_power_val_t)); /* 1.1 Store OLPC Delta Calibration Offset*/ olpc_power_offset = 0; /* 2. Store OB/DB1/DB2 */ /* 3. Store TPC settting */ temp_tcp_reg_val = AH9300(ah)->ah_ob_db1[POSEIDON_STORED_REG_TPC]; /* 4. Store BB_powertx_rate9 value */ temp_powertx_rate9_reg_val = AH9300(ah)->ah_ob_db1[POSEIDON_STORED_REG_BB_PWRTX_RATE9]; break; } } else { switch (AH9300(ah)->green_tx_status) { case HAL_RSSI_TX_POWER_SHORT: /* 1. TxPower Config */ OS_MEMCPY(target_power_val_t, wb225_sw_gtx_tp_distance_short, sizeof(target_power_val_t)); /* 1.1 Store OLPC Delta Calibration Offset*/ olpc_power_offset = wb225_gtx_olpc_cal_offset[WB225_OB_GREEN_TX_SHORT_VALUE] - wb225_gtx_olpc_cal_offset[WB225_OB_CALIBRATION_VALUE]; /* 2. Store OB/DB */ temp_obdb_reg_val = AH9300(ah)->ah_ob_db1[POSEIDON_STORED_REG_OBDB]; temp_obdb_reg_val &= ~(AR_PHY_65NM_CH0_TXRF2_DB2G | AR_PHY_65NM_CH0_TXRF2_OB2G_CCK | AR_PHY_65NM_CH0_TXRF2_OB2G_PSK | AR_PHY_65NM_CH0_TXRF2_OB2G_QAM); temp_obdb_reg_val |= (SM(5, AR_PHY_65NM_CH0_TXRF2_DB2G) | SM(WB225_OB_GREEN_TX_SHORT_VALUE, AR_PHY_65NM_CH0_TXRF2_OB2G_CCK) | SM(WB225_OB_GREEN_TX_SHORT_VALUE, AR_PHY_65NM_CH0_TXRF2_OB2G_PSK) | SM(WB225_OB_GREEN_TX_SHORT_VALUE, AR_PHY_65NM_CH0_TXRF2_OB2G_QAM)); /* 3. Store TPC settting */ temp_tcp_reg_val = (SM(6, AR_TPC_ACK) | SM(6, AR_TPC_CTS) | SM(6, AR_TPC_CHIRP) | SM(6, AR_TPC_RPT)); /* 4. Store BB_powertx_rate9 value */ temp_powertx_rate9_reg_val = WB225_BBPWRTXRATE9_SW_GREEN_TX_SHORT_VALUE; break; case HAL_RSSI_TX_POWER_MIDDLE: /* 1. TxPower Config */ OS_MEMCPY(target_power_val_t, wb225_sw_gtx_tp_distance_middle, sizeof(target_power_val_t)); /* 1.1 Store OLPC Delta Calibration Offset*/ olpc_power_offset = wb225_gtx_olpc_cal_offset[WB225_OB_GREEN_TX_MIDDLE_VALUE] - wb225_gtx_olpc_cal_offset[WB225_OB_CALIBRATION_VALUE]; /* 2. Store OB/DB */ temp_obdb_reg_val = AH9300(ah)->ah_ob_db1[POSEIDON_STORED_REG_OBDB]; temp_obdb_reg_val &= ~(AR_PHY_65NM_CH0_TXRF2_DB2G | AR_PHY_65NM_CH0_TXRF2_OB2G_CCK | AR_PHY_65NM_CH0_TXRF2_OB2G_PSK | AR_PHY_65NM_CH0_TXRF2_OB2G_QAM); temp_obdb_reg_val |= (SM(5, AR_PHY_65NM_CH0_TXRF2_DB2G) | SM(WB225_OB_GREEN_TX_MIDDLE_VALUE, AR_PHY_65NM_CH0_TXRF2_OB2G_CCK) | SM(WB225_OB_GREEN_TX_MIDDLE_VALUE, AR_PHY_65NM_CH0_TXRF2_OB2G_PSK) | SM(WB225_OB_GREEN_TX_MIDDLE_VALUE, AR_PHY_65NM_CH0_TXRF2_OB2G_QAM)); /* 3. Store TPC settting */ temp_tcp_reg_val = (SM(14, AR_TPC_ACK) | SM(14, AR_TPC_CTS) | SM(14, AR_TPC_CHIRP) | SM(14, AR_TPC_RPT)); /* 4. Store BB_powertx_rate9 value */ temp_powertx_rate9_reg_val = WB225_BBPWRTXRATE9_SW_GREEN_TX_MIDDLE_VALUE; break; case HAL_RSSI_TX_POWER_LONG: default: /* 1. TxPower Config */ OS_MEMCPY(target_power_val_t, ahp->ah_default_tx_power, sizeof(target_power_val_t)); /* 1.1 Store OLPC Delta Calibration Offset*/ olpc_power_offset = wb225_gtx_olpc_cal_offset[WB225_OB_GREEN_TX_LONG_VALUE] - wb225_gtx_olpc_cal_offset[WB225_OB_CALIBRATION_VALUE]; /* 2. Store OB/DB1/DB2 */ temp_obdb_reg_val = AH9300(ah)->ah_ob_db1[POSEIDON_STORED_REG_OBDB]; /* 3. Store TPC settting */ temp_tcp_reg_val = AH9300(ah)->ah_ob_db1[POSEIDON_STORED_REG_TPC]; /* 4. Store BB_powertx_rate9 value */ temp_powertx_rate9_reg_val = AH9300(ah)->ah_ob_db1[POSEIDON_STORED_REG_BB_PWRTX_RATE9]; break; } } /* 1.1 Do OLPC Delta Calibration Offset */ tmp_olpc_val = (int8_t) AH9300(ah)->ah_db2[POSEIDON_STORED_REG_G2_OLPC_OFFSET]; tmp_olpc_val += olpc_power_offset; OS_REG_RMW(ah, AR_PHY_TPC_11_B0, (tmp_olpc_val << AR_PHY_TPC_OLPC_GAIN_DELTA_S), AR_PHY_TPC_OLPC_GAIN_DELTA); /* 1.2 TxPower Config */ ar9300_transmit_power_reg_write(ah, target_power_val_t); /* 2. Config OB/DB */ if (!ahp->ah_hw_green_tx_enable) { OS_REG_WRITE(ah, AR_PHY_65NM_CH0_TXRF2, temp_obdb_reg_val); } /* 3. config TPC settting */ OS_REG_WRITE(ah, AR_TPC, temp_tcp_reg_val); /* 4. config BB_powertx_rate9 value */ OS_REG_WRITE(ah, AR_PHY_BB_POWERTX_RATE9, temp_powertx_rate9_reg_val); } } #if 0 void ar9300_get_vow_stats( struct ath_hal *ah, HAL_VOWSTATS* p_stats, u_int8_t vow_reg_flags) { if (vow_reg_flags & AR_REG_TX_FRM_CNT) { p_stats->tx_frame_count = OS_REG_READ(ah, AR_TFCNT); } if (vow_reg_flags & AR_REG_RX_FRM_CNT) { p_stats->rx_frame_count = OS_REG_READ(ah, AR_RFCNT); } if (vow_reg_flags & AR_REG_RX_CLR_CNT) { p_stats->rx_clear_count = OS_REG_READ(ah, AR_RCCNT); } if (vow_reg_flags & AR_REG_CYCLE_CNT) { p_stats->cycle_count = OS_REG_READ(ah, AR_CCCNT); } if (vow_reg_flags & AR_REG_EXT_CYCLE_CNT) { p_stats->ext_cycle_count = OS_REG_READ(ah, AR_EXTRCCNT); } } #endif /* * ar9300_is_skip_paprd_by_greentx * * This function check if we need to skip PAPRD tuning * when GreenTx in specific state. */ HAL_BOOL ar9300_is_skip_paprd_by_greentx(struct ath_hal *ah) { if (AR_SREV_POSEIDON(ah) && ah->ah_config.ath_hal_sta_update_tx_pwr_enable && ((AH9300(ah)->green_tx_status == HAL_RSSI_TX_POWER_SHORT) || (AH9300(ah)->green_tx_status == HAL_RSSI_TX_POWER_MIDDLE))) { return AH_TRUE; } return AH_FALSE; } void ar9300_control_signals_for_green_tx_mode(struct ath_hal *ah) { unsigned int valid_obdb_0_b0 = 0x2d; // 5,5 - dB[0:2],oB[5:3] unsigned int valid_obdb_1_b0 = 0x25; // 4,5 - dB[0:2],oB[5:3] unsigned int valid_obdb_2_b0 = 0x1d; // 3,5 - dB[0:2],oB[5:3] unsigned int valid_obdb_3_b0 = 0x15; // 2,5 - dB[0:2],oB[5:3] unsigned int valid_obdb_4_b0 = 0xd; // 1,5 - dB[0:2],oB[5:3] struct ath_hal_9300 *ahp = AH9300(ah); if (AR_SREV_POSEIDON(ah) && ahp->ah_hw_green_tx_enable) { OS_REG_RMW_FIELD_ALT(ah, AR_PHY_PAPRD_VALID_OBDB_POSEIDON, AR_PHY_PAPRD_VALID_OBDB_0, valid_obdb_0_b0); OS_REG_RMW_FIELD_ALT(ah, AR_PHY_PAPRD_VALID_OBDB_POSEIDON, AR_PHY_PAPRD_VALID_OBDB_1, valid_obdb_1_b0); OS_REG_RMW_FIELD_ALT(ah, AR_PHY_PAPRD_VALID_OBDB_POSEIDON, AR_PHY_PAPRD_VALID_OBDB_2, valid_obdb_2_b0); OS_REG_RMW_FIELD_ALT(ah, AR_PHY_PAPRD_VALID_OBDB_POSEIDON, AR_PHY_PAPRD_VALID_OBDB_3, valid_obdb_3_b0); OS_REG_RMW_FIELD_ALT(ah, AR_PHY_PAPRD_VALID_OBDB_POSEIDON, AR_PHY_PAPRD_VALID_OBDB_4, valid_obdb_4_b0); } } void ar9300_hwgreentx_set_pal_spare(struct ath_hal *ah, int value) { struct ath_hal_9300 *ahp = AH9300(ah); if (AR_SREV_POSEIDON(ah) && ahp->ah_hw_green_tx_enable) { if ((value == 0) || (value == 1)) { OS_REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_TXRF3, AR_PHY_65NM_CH0_TXRF3_OLD_PAL_SPARE, value); } } } void ar9300_reset_hw_beacon_proc_crc(struct ath_hal *ah) { OS_REG_SET_BIT(ah, AR_HWBCNPROC1, AR_HWBCNPROC1_RESET_CRC); } int32_t ar9300_get_hw_beacon_rssi(struct ath_hal *ah) { int32_t val = OS_REG_READ_FIELD(ah, AR_BCN_RSSI_AVE, AR_BCN_RSSI_AVE_VAL); /* RSSI format is 8.4. Ignore lowest four bits */ val = val >> 4; return val; } void ar9300_set_hw_beacon_rssi_threshold(struct ath_hal *ah, u_int32_t rssi_threshold) { struct ath_hal_9300 *ahp = AH9300(ah); OS_REG_RMW_FIELD(ah, AR_RSSI_THR, AR_RSSI_THR_VAL, rssi_threshold); /* save value for restoring after chip reset */ ahp->ah_beacon_rssi_threshold = rssi_threshold; } void ar9300_reset_hw_beacon_rssi(struct ath_hal *ah) { OS_REG_SET_BIT(ah, AR_RSSI_THR, AR_RSSI_BCN_RSSI_RST); } void ar9300_set_hw_beacon_proc(struct ath_hal *ah, HAL_BOOL on) { if (on) { OS_REG_SET_BIT(ah, AR_HWBCNPROC1, AR_HWBCNPROC1_CRC_ENABLE | AR_HWBCNPROC1_EXCLUDE_TIM_ELM); } else { OS_REG_CLR_BIT(ah, AR_HWBCNPROC1, AR_HWBCNPROC1_CRC_ENABLE | AR_HWBCNPROC1_EXCLUDE_TIM_ELM); } } /* * Gets the contents of the specified key cache entry. */ HAL_BOOL ar9300_print_keycache(struct ath_hal *ah) { const HAL_CAPABILITIES *p_cap = &AH_PRIVATE(ah)->ah_caps; u_int32_t key0, key1, key2, key3, key4; u_int32_t mac_hi, mac_lo; u_int16_t entry = 0; u_int32_t valid = 0; u_int32_t key_type; ath_hal_printf(ah, "Slot Key\t\t\t Valid Type Mac \n"); for (entry = 0 ; entry < p_cap->halKeyCacheSize; entry++) { key0 = OS_REG_READ(ah, AR_KEYTABLE_KEY0(entry)); key1 = OS_REG_READ(ah, AR_KEYTABLE_KEY1(entry)); key2 = OS_REG_READ(ah, AR_KEYTABLE_KEY2(entry)); key3 = OS_REG_READ(ah, AR_KEYTABLE_KEY3(entry)); key4 = OS_REG_READ(ah, AR_KEYTABLE_KEY4(entry)); key_type = OS_REG_READ(ah, AR_KEYTABLE_TYPE(entry)); mac_lo = OS_REG_READ(ah, AR_KEYTABLE_MAC0(entry)); mac_hi = OS_REG_READ(ah, AR_KEYTABLE_MAC1(entry)); if (mac_hi & AR_KEYTABLE_VALID) { valid = 1; } else { valid = 0; } if ((mac_hi != 0) && (mac_lo != 0)) { mac_hi &= ~0x8000; mac_hi <<= 1; mac_hi |= ((mac_lo & (1 << 31) )) >> 31; mac_lo <<= 1; } ath_hal_printf(ah, "%03d " "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" " %02d %02d " "%02x:%02x:%02x:%02x:%02x:%02x \n", entry, (key0 << 24) >> 24, (key0 << 16) >> 24, (key0 << 8) >> 24, key0 >> 24, (key1 << 24) >> 24, (key1 << 16) >> 24, //(key1 << 8) >> 24, key1 >> 24, (key2 << 24) >> 24, (key2 << 16) >> 24, (key2 << 8) >> 24, key2 >> 24, (key3 << 24) >> 24, (key3 << 16) >> 24, //(key3 << 8) >> 24, key3 >> 24, (key4 << 24) >> 24, (key4 << 16) >> 24, (key4 << 8) >> 24, key4 >> 24, valid, key_type, (mac_lo << 24) >> 24, (mac_lo << 16) >> 24, (mac_lo << 8) >> 24, (mac_lo) >> 24, (mac_hi << 24) >> 24, (mac_hi << 16) >> 24 ); } return AH_TRUE; } /* enable/disable smart antenna mode */ HAL_BOOL ar9300_set_smart_antenna(struct ath_hal *ah, HAL_BOOL enable) { struct ath_hal_9300 *ahp = AH9300(ah); if (enable) { OS_REG_SET_BIT(ah, AR_XRTO, AR_ENABLE_SMARTANTENNA); } else { OS_REG_CLR_BIT(ah, AR_XRTO, AR_ENABLE_SMARTANTENNA); } /* if scropion and smart antenna is enabled, write swcom1 with 0x440 * and swcom2 with 0 * FIXME Ideally these registers need to be made read from caldata. * Until the calibration team gets them, keep them along with board * configuration. */ if (enable && AR_SREV_SCORPION(ah) && (HAL_OK == ar9300_get_capability(ah, HAL_CAP_SMARTANTENNA, 0,0))) { OS_REG_WRITE(ah, AR_PHY_SWITCH_COM, 0x440); OS_REG_WRITE(ah, AR_PHY_SWITCH_COM_2, 0); } ahp->ah_smartantenna_enable = enable; return 1; } #ifdef ATH_TX99_DIAG #ifndef ATH_SUPPORT_HTC void ar9300_tx99_channel_pwr_update(struct ath_hal *ah, HAL_CHANNEL *c, u_int32_t txpower) { #define PWR_MAS(_r, _s) (((_r) & 0x3f) << (_s)) static int16_t p_pwr_array[ar9300_rate_size] = { 0 }; int32_t i; /* The max power is limited to 63 */ if (txpower <= AR9300_MAX_RATE_POWER) { for (i = 0; i < ar9300_rate_size; i++) { p_pwr_array[i] = txpower; } } else { for (i = 0; i < ar9300_rate_size; i++) { p_pwr_array[i] = AR9300_MAX_RATE_POWER; } } OS_REG_WRITE(ah, 0xa458, 0); /* Write the OFDM power per rate set */ /* 6 (LSB), 9, 12, 18 (MSB) */ OS_REG_WRITE(ah, 0xa3c0, PWR_MAS(p_pwr_array[ALL_TARGET_LEGACY_6_24], 24) | PWR_MAS(p_pwr_array[ALL_TARGET_LEGACY_6_24], 16) | PWR_MAS(p_pwr_array[ALL_TARGET_LEGACY_6_24], 8) | PWR_MAS(p_pwr_array[ALL_TARGET_LEGACY_6_24], 0) ); /* 24 (LSB), 36, 48, 54 (MSB) */ OS_REG_WRITE(ah, 0xa3c4, PWR_MAS(p_pwr_array[ALL_TARGET_LEGACY_54], 24) | PWR_MAS(p_pwr_array[ALL_TARGET_LEGACY_48], 16) | PWR_MAS(p_pwr_array[ALL_TARGET_LEGACY_36], 8) | PWR_MAS(p_pwr_array[ALL_TARGET_LEGACY_6_24], 0) ); /* Write the CCK power per rate set */ /* 1L (LSB), reserved, 2L, 2S (MSB) */ OS_REG_WRITE(ah, 0xa3c8, PWR_MAS(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], 24) | PWR_MAS(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], 16) /* | PWR_MAS(txPowerTimes2, 8) */ /* this is reserved for Osprey */ | PWR_MAS(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], 0) ); /* 5.5L (LSB), 5.5S, 11L, 11S (MSB) */ OS_REG_WRITE(ah, 0xa3cc, PWR_MAS(p_pwr_array[ALL_TARGET_LEGACY_11S], 24) | PWR_MAS(p_pwr_array[ALL_TARGET_LEGACY_11L], 16) | PWR_MAS(p_pwr_array[ALL_TARGET_LEGACY_5S], 8) | PWR_MAS(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], 0) ); /* Write the HT20 power per rate set */ /* 0/8/16 (LSB), 1-3/9-11/17-19, 4, 5 (MSB) */ OS_REG_WRITE(ah, 0xa3d0, PWR_MAS(p_pwr_array[ALL_TARGET_HT20_5], 24) | PWR_MAS(p_pwr_array[ALL_TARGET_HT20_4], 16) | PWR_MAS(p_pwr_array[ALL_TARGET_HT20_1_3_9_11_17_19], 8) | PWR_MAS(p_pwr_array[ALL_TARGET_HT20_0_8_16], 0) ); /* 6 (LSB), 7, 12, 13 (MSB) */ OS_REG_WRITE(ah, 0xa3d4, PWR_MAS(p_pwr_array[ALL_TARGET_HT20_13], 24) | PWR_MAS(p_pwr_array[ALL_TARGET_HT20_12], 16) | PWR_MAS(p_pwr_array[ALL_TARGET_HT20_7], 8) | PWR_MAS(p_pwr_array[ALL_TARGET_HT20_6], 0) ); /* 14 (LSB), 15, 20, 21 */ OS_REG_WRITE(ah, 0xa3e4, PWR_MAS(p_pwr_array[ALL_TARGET_HT20_21], 24) | PWR_MAS(p_pwr_array[ALL_TARGET_HT20_20], 16) | PWR_MAS(p_pwr_array[ALL_TARGET_HT20_15], 8) | PWR_MAS(p_pwr_array[ALL_TARGET_HT20_14], 0) ); /* Mixed HT20 and HT40 rates */ /* HT20 22 (LSB), HT20 23, HT40 22, HT40 23 (MSB) */ OS_REG_WRITE(ah, 0xa3e8, PWR_MAS(p_pwr_array[ALL_TARGET_HT40_23], 24) | PWR_MAS(p_pwr_array[ALL_TARGET_HT40_22], 16) | PWR_MAS(p_pwr_array[ALL_TARGET_HT20_23], 8) | PWR_MAS(p_pwr_array[ALL_TARGET_HT20_22], 0) ); /* Write the HT40 power per rate set */ /* correct PAR difference between HT40 and HT20/LEGACY */ /* 0/8/16 (LSB), 1-3/9-11/17-19, 4, 5 (MSB) */ OS_REG_WRITE(ah, 0xa3d8, PWR_MAS(p_pwr_array[ALL_TARGET_HT40_5], 24) | PWR_MAS(p_pwr_array[ALL_TARGET_HT40_4], 16) | PWR_MAS(p_pwr_array[ALL_TARGET_HT40_1_3_9_11_17_19], 8) | PWR_MAS(p_pwr_array[ALL_TARGET_HT40_0_8_16], 0) ); /* 6 (LSB), 7, 12, 13 (MSB) */ OS_REG_WRITE(ah, 0xa3dc, PWR_MAS(p_pwr_array[ALL_TARGET_HT40_13], 24) | PWR_MAS(p_pwr_array[ALL_TARGET_HT40_12], 16) | PWR_MAS(p_pwr_array[ALL_TARGET_HT40_7], 8) | PWR_MAS(p_pwr_array[ALL_TARGET_HT40_6], 0) ); /* 14 (LSB), 15, 20, 21 */ OS_REG_WRITE(ah, 0xa3ec, PWR_MAS(p_pwr_array[ALL_TARGET_HT40_21], 24) | PWR_MAS(p_pwr_array[ALL_TARGET_HT40_20], 16) | PWR_MAS(p_pwr_array[ALL_TARGET_HT40_15], 8) | PWR_MAS(p_pwr_array[ALL_TARGET_HT40_14], 0) ); #undef PWR_MAS } void ar9300_tx99_chainmsk_setup(struct ath_hal *ah, int tx_chainmask) { if (tx_chainmask == 0x5) { OS_REG_WRITE(ah, AR_PHY_ANALOG_SWAP, OS_REG_READ(ah, AR_PHY_ANALOG_SWAP) | AR_PHY_SWAP_ALT_CHAIN); } OS_REG_WRITE(ah, AR_PHY_RX_CHAINMASK, tx_chainmask); OS_REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, tx_chainmask); OS_REG_WRITE(ah, AR_SELFGEN_MASK, tx_chainmask); if (tx_chainmask == 0x5) { OS_REG_WRITE(ah, AR_PHY_ANALOG_SWAP, OS_REG_READ(ah, AR_PHY_ANALOG_SWAP) | AR_PHY_SWAP_ALT_CHAIN); } } void ar9300_tx99_set_single_carrier(struct ath_hal *ah, int tx_chain_mask, int chtype) { OS_REG_WRITE(ah, 0x98a4, OS_REG_READ(ah, 0x98a4) | (0x7ff << 11) | 0x7ff); OS_REG_WRITE(ah, 0xa364, OS_REG_READ(ah, 0xa364) | (1 << 7) | (1 << 1)); OS_REG_WRITE(ah, 0xa350, (OS_REG_READ(ah, 0xa350) | (1 << 31) | (1 << 15)) & ~(1 << 13)); /* 11G mode */ if (!chtype) { OS_REG_WRITE(ah, AR_PHY_65NM_CH0_RXTX2, OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX2) | (0x1 << 3) | (0x1 << 2)); if (AR_SREV_OSPREY(ah) || AR_SREV_WASP(ah)) { OS_REG_WRITE(ah, AR_PHY_65NM_CH0_TOP, OS_REG_READ(ah, AR_PHY_65NM_CH0_TOP) & ~(0x1 << 4)); OS_REG_WRITE(ah, AR_PHY_65NM_CH0_TOP2, (OS_REG_READ(ah, AR_PHY_65NM_CH0_TOP2) | (0x1 << 26) | (0x7 << 24)) & ~(0x1 << 22)); } else { OS_REG_WRITE(ah, AR_HORNET_CH0_TOP, OS_REG_READ(ah, AR_HORNET_CH0_TOP) & ~(0x1 << 4)); OS_REG_WRITE(ah, AR_HORNET_CH0_TOP2, (OS_REG_READ(ah, AR_HORNET_CH0_TOP2) | (0x1 << 26) | (0x7 << 24)) & ~(0x1 << 22)); } /* chain zero */ if ((tx_chain_mask & 0x01) == 0x01) { OS_REG_WRITE(ah, AR_PHY_65NM_CH0_RXTX1, (OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX1) | (0x1 << 31) | (0x5 << 15) | (0x3 << 9)) & ~(0x1 << 27) & ~(0x1 << 12)); OS_REG_WRITE(ah, AR_PHY_65NM_CH0_RXTX2, (OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX2) | (0x1 << 12) | (0x1 << 10) | (0x1 << 9) | (0x1 << 8) | (0x1 << 7)) & ~(0x1 << 11)); OS_REG_WRITE(ah, AR_PHY_65NM_CH0_RXTX3, (OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX3) | (0x1 << 29) | (0x1 << 25) | (0x1 << 23) | (0x1 << 19) | (0x1 << 10) | (0x1 << 9) | (0x1 << 8) | (0x1 << 3)) & ~(0x1 << 28)& ~(0x1 << 24) & ~(0x1 << 22)& ~(0x1 << 7)); OS_REG_WRITE(ah, AR_PHY_65NM_CH0_TXRF1, (OS_REG_READ(ah, AR_PHY_65NM_CH0_TXRF1) | (0x1 << 23))& ~(0x1 << 21)); OS_REG_WRITE(ah, AR_PHY_65NM_CH0_BB1, OS_REG_READ(ah, AR_PHY_65NM_CH0_BB1) | (0x1 << 12) | (0x1 << 10) | (0x1 << 9) | (0x1 << 8) | (0x1 << 6) | (0x1 << 5) | (0x1 << 4) | (0x1 << 3) | (0x1 << 2)); OS_REG_WRITE(ah, AR_PHY_65NM_CH0_BB2, OS_REG_READ(ah, AR_PHY_65NM_CH0_BB2) | (0x1 << 31)); } if (AR_SREV_OSPREY(ah) || AR_SREV_WASP(ah)) { /* chain one */ if ((tx_chain_mask & 0x02) == 0x02 ) { OS_REG_WRITE(ah, AR_PHY_65NM_CH1_RXTX1, (OS_REG_READ(ah, AR_PHY_65NM_CH1_RXTX1) | (0x1 << 31) | (0x5 << 15) | (0x3 << 9)) & ~(0x1 << 27) & ~(0x1 << 12)); OS_REG_WRITE(ah, AR_PHY_65NM_CH1_RXTX2, (OS_REG_READ(ah, AR_PHY_65NM_CH1_RXTX2) | (0x1 << 12) | (0x1 << 10) | (0x1 << 9) | (0x1 << 8) | (0x1 << 7)) & ~(0x1 << 11)); OS_REG_WRITE(ah, AR_PHY_65NM_CH1_RXTX3, (OS_REG_READ(ah, AR_PHY_65NM_CH1_RXTX3) | (0x1 << 29) | (0x1 << 25) | (0x1 << 23) | (0x1 << 19) | (0x1 << 10) | (0x1 << 9) | (0x1 << 8) | (0x1 << 3)) & ~(0x1 << 28)& ~(0x1 << 24) & ~(0x1 << 22)& ~(0x1 << 7)); OS_REG_WRITE(ah, AR_PHY_65NM_CH1_TXRF1, (OS_REG_READ(ah, AR_PHY_65NM_CH1_TXRF1) | (0x1 << 23))& ~(0x1 << 21)); OS_REG_WRITE(ah, AR_PHY_65NM_CH1_BB1, OS_REG_READ(ah, AR_PHY_65NM_CH1_BB1) | (0x1 << 12) | (0x1 << 10) | (0x1 << 9) | (0x1 << 8) | (0x1 << 6) | (0x1 << 5) | (0x1 << 4) | (0x1 << 3) | (0x1 << 2)); OS_REG_WRITE(ah, AR_PHY_65NM_CH1_BB2, OS_REG_READ(ah, AR_PHY_65NM_CH1_BB2) | (0x1 << 31)); } } if (AR_SREV_OSPREY(ah)) { /* chain two */ if ((tx_chain_mask & 0x04) == 0x04 ) { OS_REG_WRITE(ah, AR_PHY_65NM_CH2_RXTX1, (OS_REG_READ(ah, AR_PHY_65NM_CH2_RXTX1) | (0x1 << 31) | (0x5 << 15) | (0x3 << 9)) & ~(0x1 << 27) & ~(0x1 << 12)); OS_REG_WRITE(ah, AR_PHY_65NM_CH2_RXTX2, (OS_REG_READ(ah, AR_PHY_65NM_CH2_RXTX2) | (0x1 << 12) | (0x1 << 10) | (0x1 << 9) | (0x1 << 8) | (0x1 << 7)) & ~(0x1 << 11)); OS_REG_WRITE(ah, AR_PHY_65NM_CH2_RXTX3, (OS_REG_READ(ah, AR_PHY_65NM_CH2_RXTX3) | (0x1 << 29) | (0x1 << 25) | (0x1 << 23) | (0x1 << 19) | (0x1 << 10) | (0x1 << 9) | (0x1 << 8) | (0x1 << 3)) & ~(0x1 << 28)& ~(0x1 << 24) & ~(0x1 << 22)& ~(0x1 << 7)); OS_REG_WRITE(ah, AR_PHY_65NM_CH2_TXRF1, (OS_REG_READ(ah, AR_PHY_65NM_CH2_TXRF1) | (0x1 << 23))& ~(0x1 << 21)); OS_REG_WRITE(ah, AR_PHY_65NM_CH2_BB1, OS_REG_READ(ah, AR_PHY_65NM_CH2_BB1) | (0x1 << 12) | (0x1 << 10) | (0x1 << 9) | (0x1 << 8) | (0x1 << 6) | (0x1 << 5) | (0x1 << 4) | (0x1 << 3) | (0x1 << 2)); OS_REG_WRITE(ah, AR_PHY_65NM_CH2_BB2, OS_REG_READ(ah, AR_PHY_65NM_CH2_BB2) | (0x1 << 31)); } } OS_REG_WRITE(ah, 0xa28c, 0x11111); OS_REG_WRITE(ah, 0xa288, 0x111); } else { /* chain zero */ if ((tx_chain_mask & 0x01) == 0x01) { OS_REG_WRITE(ah, AR_PHY_65NM_CH0_RXTX1, (OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX1) | (0x1 << 31) | (0x1 << 27) | (0x3 << 23) | (0x1 << 19) | (0x1 << 15) | (0x3 << 9)) & ~(0x1 << 12)); OS_REG_WRITE(ah, AR_PHY_65NM_CH0_RXTX2, (OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX2) | (0x1 << 12) | (0x1 << 10) | (0x1 << 9) | (0x1 << 8) | (0x1 << 7) | (0x1 << 3) | (0x1 << 2) | (0x1 << 1)) & ~(0x1 << 11)& ~(0x1 << 0)); OS_REG_WRITE(ah, AR_PHY_65NM_CH0_RXTX3, (OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX3) | (0x1 << 29) | (0x1 << 25) | (0x1 << 23) | (0x1 << 19) | (0x1 << 10) | (0x1 << 9) | (0x1 << 8) | (0x1 << 3)) & ~(0x1 << 28)& ~(0x1 << 24) & ~(0x1 << 22)& ~(0x1 << 7)); OS_REG_WRITE(ah, AR_PHY_65NM_CH0_TXRF1, (OS_REG_READ(ah, AR_PHY_65NM_CH0_TXRF1) | (0x1 << 23))& ~(0x1 << 21)); OS_REG_WRITE(ah, AR_PHY_65NM_CH0_TXRF2, OS_REG_READ(ah, AR_PHY_65NM_CH0_TXRF2) | (0x3 << 3) | (0x3 << 0)); OS_REG_WRITE(ah, AR_PHY_65NM_CH0_TXRF3, (OS_REG_READ(ah, AR_PHY_65NM_CH0_TXRF3) | (0x3 << 29) | (0x3 << 26) | (0x2 << 23) | (0x2 << 20) | (0x2 << 17))& ~(0x1 << 14)); OS_REG_WRITE(ah, AR_PHY_65NM_CH0_BB1, OS_REG_READ(ah, AR_PHY_65NM_CH0_BB1) | (0x1 << 12) | (0x1 << 10) | (0x1 << 9) | (0x1 << 8) | (0x1 << 6) | (0x1 << 5) | (0x1 << 4) | (0x1 << 3) | (0x1 << 2)); OS_REG_WRITE(ah, AR_PHY_65NM_CH0_BB2, OS_REG_READ(ah, AR_PHY_65NM_CH0_BB2) | (0x1 << 31)); if (AR_SREV_OSPREY(ah) || AR_SREV_WASP(ah)) { OS_REG_WRITE(ah, AR_PHY_65NM_CH0_TOP, OS_REG_READ(ah, AR_PHY_65NM_CH0_TOP) & ~(0x1 << 4)); OS_REG_WRITE(ah, AR_PHY_65NM_CH0_TOP2, OS_REG_READ(ah, AR_PHY_65NM_CH0_TOP2) | (0x1 << 26) | (0x7 << 24) | (0x3 << 22)); } else { OS_REG_WRITE(ah, AR_HORNET_CH0_TOP, OS_REG_READ(ah, AR_HORNET_CH0_TOP) & ~(0x1 << 4)); OS_REG_WRITE(ah, AR_HORNET_CH0_TOP2, OS_REG_READ(ah, AR_HORNET_CH0_TOP2) | (0x1 << 26) | (0x7 << 24) | (0x3 << 22)); } if (AR_SREV_OSPREY(ah) || AR_SREV_WASP(ah)) { OS_REG_WRITE(ah, AR_PHY_65NM_CH1_RXTX2, (OS_REG_READ(ah, AR_PHY_65NM_CH1_RXTX2) | (0x1 << 3) | (0x1 << 2) | (0x1 << 1)) & ~(0x1 << 0)); OS_REG_WRITE(ah, AR_PHY_65NM_CH1_RXTX3, OS_REG_READ(ah, AR_PHY_65NM_CH1_RXTX3) | (0x1 << 19) | (0x1 << 3)); OS_REG_WRITE(ah, AR_PHY_65NM_CH1_TXRF1, OS_REG_READ(ah, AR_PHY_65NM_CH1_TXRF1) | (0x1 << 23)); } if (AR_SREV_OSPREY(ah)) { OS_REG_WRITE(ah, AR_PHY_65NM_CH2_RXTX2, (OS_REG_READ(ah, AR_PHY_65NM_CH2_RXTX2) | (0x1 << 3) | (0x1 << 2) | (0x1 << 1)) & ~(0x1 << 0)); OS_REG_WRITE(ah, AR_PHY_65NM_CH2_RXTX3, OS_REG_READ(ah, AR_PHY_65NM_CH2_RXTX3) | (0x1 << 19) | (0x1 << 3)); OS_REG_WRITE(ah, AR_PHY_65NM_CH2_TXRF1, OS_REG_READ(ah, AR_PHY_65NM_CH2_TXRF1) | (0x1 << 23)); } } if (AR_SREV_OSPREY(ah) || AR_SREV_WASP(ah)) { /* chain one */ if ((tx_chain_mask & 0x02) == 0x02 ) { OS_REG_WRITE(ah, AR_PHY_65NM_CH0_RXTX2, (OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX2) | (0x1 << 3) | (0x1 << 2) | (0x1 << 1)) & ~(0x1 << 0)); OS_REG_WRITE(ah, AR_PHY_65NM_CH0_RXTX3, OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX3) | (0x1 << 19) | (0x1 << 3)); OS_REG_WRITE(ah, AR_PHY_65NM_CH0_TXRF1, OS_REG_READ(ah, AR_PHY_65NM_CH0_TXRF1) | (0x1 << 23)); if (AR_SREV_OSPREY(ah) || AR_SREV_WASP(ah)) { OS_REG_WRITE(ah, AR_PHY_65NM_CH0_TOP, OS_REG_READ(ah, AR_PHY_65NM_CH0_TOP) & ~(0x1 << 4)); OS_REG_WRITE(ah, AR_PHY_65NM_CH0_TOP2, OS_REG_READ(ah, AR_PHY_65NM_CH0_TOP2) | (0x1 << 26) | (0x7 << 24) | (0x3 << 22)); } else { OS_REG_WRITE(ah, AR_HORNET_CH0_TOP, OS_REG_READ(ah, AR_HORNET_CH0_TOP) & ~(0x1 << 4)); OS_REG_WRITE(ah, AR_HORNET_CH0_TOP2, OS_REG_READ(ah, AR_HORNET_CH0_TOP2) | (0x1 << 26) | (0x7 << 24) | (0x3 << 22)); } OS_REG_WRITE(ah, AR_PHY_65NM_CH1_RXTX1, (OS_REG_READ(ah, AR_PHY_65NM_CH1_RXTX1) | (0x1 << 31) | (0x1 << 27) | (0x3 << 23) | (0x1 << 19) | (0x1 << 15) | (0x3 << 9)) & ~(0x1 << 12)); OS_REG_WRITE(ah, AR_PHY_65NM_CH1_RXTX2, (OS_REG_READ(ah, AR_PHY_65NM_CH1_RXTX2) | (0x1 << 12) | (0x1 << 10) | (0x1 << 9) | (0x1 << 8) | (0x1 << 7) | (0x1 << 3) | (0x1 << 2) | (0x1 << 1)) & ~(0x1 << 11)& ~(0x1 << 0)); OS_REG_WRITE(ah, AR_PHY_65NM_CH1_RXTX3, (OS_REG_READ(ah, AR_PHY_65NM_CH1_RXTX3) | (0x1 << 29) | (0x1 << 25) | (0x1 << 23) | (0x1 << 19) | (0x1 << 10) | (0x1 << 9) | (0x1 << 8) | (0x1 << 3)) & ~(0x1 << 28)& ~(0x1 << 24) & ~(0x1 << 22)& ~(0x1 << 7)); OS_REG_WRITE(ah, AR_PHY_65NM_CH1_TXRF1, (OS_REG_READ(ah, AR_PHY_65NM_CH1_TXRF1) | (0x1 << 23))& ~(0x1 << 21)); OS_REG_WRITE(ah, AR_PHY_65NM_CH1_TXRF2, OS_REG_READ(ah, AR_PHY_65NM_CH1_TXRF2) | (0x3 << 3) | (0x3 << 0)); OS_REG_WRITE(ah, AR_PHY_65NM_CH1_TXRF3, (OS_REG_READ(ah, AR_PHY_65NM_CH1_TXRF3) | (0x3 << 29) | (0x3 << 26) | (0x2 << 23) | (0x2 << 20) | (0x2 << 17))& ~(0x1 << 14)); OS_REG_WRITE(ah, AR_PHY_65NM_CH1_BB1, OS_REG_READ(ah, AR_PHY_65NM_CH1_BB1) | (0x1 << 12) | (0x1 << 10) | (0x1 << 9) | (0x1 << 8) | (0x1 << 6) | (0x1 << 5) | (0x1 << 4) | (0x1 << 3) | (0x1 << 2)); OS_REG_WRITE(ah, AR_PHY_65NM_CH1_BB2, OS_REG_READ(ah, AR_PHY_65NM_CH1_BB2) | (0x1 << 31)); if (AR_SREV_OSPREY(ah)) { OS_REG_WRITE(ah, AR_PHY_65NM_CH2_RXTX2, (OS_REG_READ(ah, AR_PHY_65NM_CH2_RXTX2) | (0x1 << 3) | (0x1 << 2) | (0x1 << 1)) & ~(0x1 << 0)); OS_REG_WRITE(ah, AR_PHY_65NM_CH2_RXTX3, OS_REG_READ(ah, AR_PHY_65NM_CH2_RXTX3) | (0x1 << 19) | (0x1 << 3)); OS_REG_WRITE(ah, AR_PHY_65NM_CH2_TXRF1, OS_REG_READ(ah, AR_PHY_65NM_CH2_TXRF1) | (0x1 << 23)); } } } if (AR_SREV_OSPREY(ah)) { /* chain two */ if ((tx_chain_mask & 0x04) == 0x04 ) { OS_REG_WRITE(ah, AR_PHY_65NM_CH0_RXTX2, (OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX2) | (0x1 << 3) | (0x1 << 2) | (0x1 << 1)) & ~(0x1 << 0)); OS_REG_WRITE(ah, AR_PHY_65NM_CH0_RXTX3, OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX3) | (0x1 << 19) | (0x1 << 3)); OS_REG_WRITE(ah, AR_PHY_65NM_CH0_TXRF1, OS_REG_READ(ah, AR_PHY_65NM_CH0_TXRF1) | (0x1 << 23)); if (AR_SREV_OSPREY(ah) || AR_SREV_WASP(ah)) { OS_REG_WRITE(ah, AR_PHY_65NM_CH0_TOP, OS_REG_READ(ah, AR_PHY_65NM_CH0_TOP) & ~(0x1 << 4)); OS_REG_WRITE(ah, AR_PHY_65NM_CH0_TOP2, OS_REG_READ(ah, AR_PHY_65NM_CH0_TOP2) | (0x1 << 26) | (0x7 << 24) | (0x3 << 22)); } else { OS_REG_WRITE(ah, AR_HORNET_CH0_TOP, OS_REG_READ(ah, AR_HORNET_CH0_TOP) & ~(0x1 << 4)); OS_REG_WRITE(ah, AR_HORNET_CH0_TOP2, OS_REG_READ(ah, AR_HORNET_CH0_TOP2) | (0x1 << 26) | (0x7 << 24) | (0x3 << 22)); } OS_REG_WRITE(ah, AR_PHY_65NM_CH1_RXTX2, (OS_REG_READ(ah, AR_PHY_65NM_CH1_RXTX2) | (0x1 << 3) | (0x1 << 2) | (0x1 << 1)) & ~(0x1 << 0)); OS_REG_WRITE(ah, AR_PHY_65NM_CH1_RXTX3, OS_REG_READ(ah, AR_PHY_65NM_CH1_RXTX3) | (0x1 << 19) | (0x1 << 3)); OS_REG_WRITE(ah, AR_PHY_65NM_CH1_TXRF1, OS_REG_READ(ah, AR_PHY_65NM_CH1_TXRF1) | (0x1 << 23)); OS_REG_WRITE(ah, AR_PHY_65NM_CH2_RXTX1, (OS_REG_READ(ah, AR_PHY_65NM_CH2_RXTX1) | (0x1 << 31) | (0x1 << 27) | (0x3 << 23) | (0x1 << 19) | (0x1 << 15) | (0x3 << 9)) & ~(0x1 << 12)); OS_REG_WRITE(ah, AR_PHY_65NM_CH2_RXTX2, (OS_REG_READ(ah, AR_PHY_65NM_CH2_RXTX2) | (0x1 << 12) | (0x1 << 10) | (0x1 << 9) | (0x1 << 8) | (0x1 << 7) | (0x1 << 3) | (0x1 << 2) | (0x1 << 1)) & ~(0x1 << 11)& ~(0x1 << 0)); OS_REG_WRITE(ah, AR_PHY_65NM_CH2_RXTX3, (OS_REG_READ(ah, AR_PHY_65NM_CH2_RXTX3) | (0x1 << 29) | (0x1 << 25) | (0x1 << 23) | (0x1 << 19) | (0x1 << 10) | (0x1 << 9) | (0x1 << 8) | (0x1 << 3)) & ~(0x1 << 28)& ~(0x1 << 24) & ~(0x1 << 22)& ~(0x1 << 7)); OS_REG_WRITE(ah, AR_PHY_65NM_CH2_TXRF1, (OS_REG_READ(ah, AR_PHY_65NM_CH2_TXRF1) | (0x1 << 23))& ~(0x1 << 21)); OS_REG_WRITE(ah, AR_PHY_65NM_CH2_TXRF2, OS_REG_READ(ah, AR_PHY_65NM_CH2_TXRF2) | (0x3 << 3) | (0x3 << 0)); OS_REG_WRITE(ah, AR_PHY_65NM_CH2_TXRF3, (OS_REG_READ(ah, AR_PHY_65NM_CH2_TXRF3) | (0x3 << 29) | (0x3 << 26) | (0x2 << 23) | (0x2 << 20) | (0x2 << 17))& ~(0x1 << 14)); OS_REG_WRITE(ah, AR_PHY_65NM_CH2_BB1, OS_REG_READ(ah, AR_PHY_65NM_CH2_BB1) | (0x1 << 12) | (0x1 << 10) | (0x1 << 9) | (0x1 << 8) | (0x1 << 6) | (0x1 << 5) | (0x1 << 4) | (0x1 << 3) | (0x1 << 2)); OS_REG_WRITE(ah, AR_PHY_65NM_CH2_BB2, OS_REG_READ(ah, AR_PHY_65NM_CH2_BB2) | (0x1 << 31)); } } OS_REG_WRITE(ah, 0xa28c, 0x22222); OS_REG_WRITE(ah, 0xa288, 0x222); } } void ar9300_tx99_start(struct ath_hal *ah, u_int8_t *data) { u_int32_t val; u_int32_t qnum = (u_int32_t)data; /* Disable AGC to A2 */ OS_REG_WRITE(ah, AR_PHY_TEST, (OS_REG_READ(ah, AR_PHY_TEST) | PHY_AGC_CLR)); - OS_REG_WRITE(ah, 0x9864, OS_REG_READ(ah, 0x9864) | 0x7f000); - OS_REG_WRITE(ah, 0x9924, OS_REG_READ(ah, 0x9924) | 0x7f00fe); OS_REG_WRITE(ah, AR_DIAG_SW, OS_REG_READ(ah, AR_DIAG_SW) &~ AR_DIAG_RX_DIS); OS_REG_WRITE(ah, AR_CR, AR_CR_RXD); /* set receive disable */ /* set CW_MIN and CW_MAX both to 0, AIFS=2 */ OS_REG_WRITE(ah, AR_DLCL_IFS(qnum), 0); OS_REG_WRITE(ah, AR_D_GBL_IFS_SIFS, 20); /* 50 OK */ OS_REG_WRITE(ah, AR_D_GBL_IFS_EIFS, 20); /* 200 ok for HT20, 400 ok for HT40 */ OS_REG_WRITE(ah, AR_TIME_OUT, 0x00000400); OS_REG_WRITE(ah, AR_DRETRY_LIMIT(qnum), 0xffffffff); /* set QCU modes to early termination */ val = OS_REG_READ(ah, AR_QMISC(qnum)); OS_REG_WRITE(ah, AR_QMISC(qnum), val | AR_Q_MISC_DCU_EARLY_TERM_REQ); } void ar9300_tx99_stop(struct ath_hal *ah) { /* this should follow the setting of start */ OS_REG_WRITE(ah, AR_PHY_TEST, OS_REG_READ(ah, AR_PHY_TEST) &~ PHY_AGC_CLR); OS_REG_WRITE(ah, AR_DIAG_SW, OS_REG_READ(ah, AR_DIAG_SW) | AR_DIAG_RX_DIS); } #endif /* ATH_TX99_DIAG */ #endif /* ATH_SUPPORT_HTC */ HAL_BOOL ar9300Get3StreamSignature(struct ath_hal *ah) { return AH_FALSE; } HAL_BOOL ar9300ForceVCS(struct ath_hal *ah) { return AH_FALSE; } HAL_BOOL ar9300SetDfs3StreamFix(struct ath_hal *ah, u_int32_t val) { return AH_FALSE; +} + +HAL_BOOL +ar9300_set_ctl_pwr(struct ath_hal *ah, u_int8_t *ctl_array) +{ + struct ath_hal_9300 *ahp = AH9300(ah); + ar9300_eeprom_t *p_eep_data = &ahp->ah_eeprom; + u_int8_t *ctl_index; + u_int32_t offset = 0; + + if (!ctl_array) + return AH_FALSE; + + /* copy 2G ctl freqbin and power data */ + ctl_index = p_eep_data->ctl_index_2g; + OS_MEMCPY(ctl_index + OSPREY_NUM_CTLS_2G, ctl_array, + OSPREY_NUM_CTLS_2G * OSPREY_NUM_BAND_EDGES_2G + /* ctl_freqbin_2G */ + OSPREY_NUM_CTLS_2G * sizeof(OSP_CAL_CTL_DATA_2G)); /* ctl_power_data_2g */ + offset = (OSPREY_NUM_CTLS_2G * OSPREY_NUM_BAND_EDGES_2G) + + ( OSPREY_NUM_CTLS_2G * sizeof(OSP_CAL_CTL_DATA_2G)); + + + /* copy 2G ctl freqbin and power data */ + ctl_index = p_eep_data->ctl_index_5g; + OS_MEMCPY(ctl_index + OSPREY_NUM_CTLS_5G, ctl_array + offset, + OSPREY_NUM_CTLS_5G * OSPREY_NUM_BAND_EDGES_5G + /* ctl_freqbin_5G */ + OSPREY_NUM_CTLS_5G * sizeof(OSP_CAL_CTL_DATA_5G)); /* ctl_power_data_5g */ + + return AH_FALSE; +} + +void +ar9300_set_txchainmaskopt(struct ath_hal *ah, u_int8_t mask) +{ + struct ath_hal_9300 *ahp = AH9300(ah); + + /* optional txchainmask should be subset of primary txchainmask */ + if ((mask & ahp->ah_tx_chainmask) != mask) { + ahp->ah_tx_chainmaskopt = 0; + ath_hal_printf(ah, "Error: ah_tx_chainmask=%d, mask=%d\n", ahp->ah_tx_chainmask, mask); + return; + } + + ahp->ah_tx_chainmaskopt = mask; } Index: projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_osprey22.ini =================================================================== --- projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_osprey22.ini (revision 278776) +++ projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_osprey22.ini (revision 278777) @@ -1,2188 +1,2188 @@ /* * Copyright (c) 2013 Qualcomm Atheros, Inc. * * 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. */ static const u_int32_t ar9300_osprey_2p2_mac_postamble_emulation[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x00008014 , 0x10f810f8 , 0x10f810f8 , 0x10f810f8 , 0x10f810f8 }, { 0x0000801c , 0x0e8d8017 , 0x0e8d8017 , 0x0e8d8017 , 0x0e8d8017 }, }; static const u_int32_t ar9300Modes_low_ob_db_tx_gain_table_osprey_2p2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x0000a2dc , 0x00033800 , 0x00033800 , 0x03aaa352 , 0x03aaa352 }, { 0x0000a2e0 , 0x0003c000 , 0x0003c000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000a2e4 , 0x03fc0000 , 0x03fc0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000a2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000a410 , 0x000050d9 , 0x000050d9 , 0x000050d9 , 0x000050d9 }, { 0x0000a500 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a504 , 0x06000003 , 0x06000003 , 0x04000002 , 0x04000002 }, { 0x0000a508 , 0x0a000020 , 0x0a000020 , 0x08000004 , 0x08000004 }, { 0x0000a50c , 0x10000023 , 0x10000023 , 0x0b000200 , 0x0b000200 }, { 0x0000a510 , 0x16000220 , 0x16000220 , 0x0f000202 , 0x0f000202 }, { 0x0000a514 , 0x1c000223 , 0x1c000223 , 0x12000400 , 0x12000400 }, { 0x0000a518 , 0x21002220 , 0x21002220 , 0x16000402 , 0x16000402 }, { 0x0000a51c , 0x27002223 , 0x27002223 , 0x19000404 , 0x19000404 }, { 0x0000a520 , 0x2b022220 , 0x2b022220 , 0x1c000603 , 0x1c000603 }, { 0x0000a524 , 0x2f022222 , 0x2f022222 , 0x21000a02 , 0x21000a02 }, { 0x0000a528 , 0x34022225 , 0x34022225 , 0x25000a04 , 0x25000a04 }, { 0x0000a52c , 0x3a02222a , 0x3a02222a , 0x28000a20 , 0x28000a20 }, { 0x0000a530 , 0x3e02222c , 0x3e02222c , 0x2c000e20 , 0x2c000e20 }, { 0x0000a534 , 0x4202242a , 0x4202242a , 0x30000e22 , 0x30000e22 }, { 0x0000a538 , 0x4702244a , 0x4702244a , 0x34000e24 , 0x34000e24 }, { 0x0000a53c , 0x4b02244c , 0x4b02244c , 0x38001640 , 0x38001640 }, { 0x0000a540 , 0x4e02246c , 0x4e02246c , 0x3c001660 , 0x3c001660 }, { 0x0000a544 , 0x52022470 , 0x52022470 , 0x3f001861 , 0x3f001861 }, { 0x0000a548 , 0x55022490 , 0x55022490 , 0x43001a81 , 0x43001a81 }, { 0x0000a54c , 0x59022492 , 0x59022492 , 0x47001a83 , 0x47001a83 }, { 0x0000a550 , 0x5d022692 , 0x5d022692 , 0x4a001c84 , 0x4a001c84 }, { 0x0000a554 , 0x61022892 , 0x61022892 , 0x4e001ce3 , 0x4e001ce3 }, { 0x0000a558 , 0x65024890 , 0x65024890 , 0x52001ce5 , 0x52001ce5 }, { 0x0000a55c , 0x69024892 , 0x69024892 , 0x56001ce9 , 0x56001ce9 }, { 0x0000a560 , 0x6e024c92 , 0x6e024c92 , 0x5a001ceb , 0x5a001ceb }, { 0x0000a564 , 0x74026e92 , 0x74026e92 , 0x5d001eec , 0x5d001eec }, { 0x0000a568 , 0x74026e92 , 0x74026e92 , 0x5d001eec , 0x5d001eec }, { 0x0000a56c , 0x74026e92 , 0x74026e92 , 0x5d001eec , 0x5d001eec }, { 0x0000a570 , 0x74026e92 , 0x74026e92 , 0x5d001eec , 0x5d001eec }, { 0x0000a574 , 0x74026e92 , 0x74026e92 , 0x5d001eec , 0x5d001eec }, { 0x0000a578 , 0x74026e92 , 0x74026e92 , 0x5d001eec , 0x5d001eec }, { 0x0000a57c , 0x74026e92 , 0x74026e92 , 0x5d001eec , 0x5d001eec }, { 0x0000a580 , 0x00800000 , 0x00800000 , 0x00800000 , 0x00800000 }, { 0x0000a584 , 0x06800003 , 0x06800003 , 0x04800002 , 0x04800002 }, { 0x0000a588 , 0x0a800020 , 0x0a800020 , 0x08800004 , 0x08800004 }, { 0x0000a58c , 0x10800023 , 0x10800023 , 0x0b800200 , 0x0b800200 }, { 0x0000a590 , 0x16800220 , 0x16800220 , 0x0f800202 , 0x0f800202 }, { 0x0000a594 , 0x1c800223 , 0x1c800223 , 0x12800400 , 0x12800400 }, { 0x0000a598 , 0x21802220 , 0x21802220 , 0x16800402 , 0x16800402 }, { 0x0000a59c , 0x27802223 , 0x27802223 , 0x19800404 , 0x19800404 }, { 0x0000a5a0 , 0x2b822220 , 0x2b822220 , 0x1c800603 , 0x1c800603 }, { 0x0000a5a4 , 0x2f822222 , 0x2f822222 , 0x21800a02 , 0x21800a02 }, { 0x0000a5a8 , 0x34822225 , 0x34822225 , 0x25800a04 , 0x25800a04 }, { 0x0000a5ac , 0x3a82222a , 0x3a82222a , 0x28800a20 , 0x28800a20 }, { 0x0000a5b0 , 0x3e82222c , 0x3e82222c , 0x2c800e20 , 0x2c800e20 }, { 0x0000a5b4 , 0x4282242a , 0x4282242a , 0x30800e22 , 0x30800e22 }, { 0x0000a5b8 , 0x4782244a , 0x4782244a , 0x34800e24 , 0x34800e24 }, { 0x0000a5bc , 0x4b82244c , 0x4b82244c , 0x38801640 , 0x38801640 }, { 0x0000a5c0 , 0x4e82246c , 0x4e82246c , 0x3c801660 , 0x3c801660 }, { 0x0000a5c4 , 0x52822470 , 0x52822470 , 0x3f801861 , 0x3f801861 }, { 0x0000a5c8 , 0x55822490 , 0x55822490 , 0x43801a81 , 0x43801a81 }, { 0x0000a5cc , 0x59822492 , 0x59822492 , 0x47801a83 , 0x47801a83 }, { 0x0000a5d0 , 0x5d822692 , 0x5d822692 , 0x4a801c84 , 0x4a801c84 }, { 0x0000a5d4 , 0x61822892 , 0x61822892 , 0x4e801ce3 , 0x4e801ce3 }, { 0x0000a5d8 , 0x65824890 , 0x65824890 , 0x52801ce5 , 0x52801ce5 }, { 0x0000a5dc , 0x69824892 , 0x69824892 , 0x56801ce9 , 0x56801ce9 }, { 0x0000a5e0 , 0x6e824c92 , 0x6e824c92 , 0x5a801ceb , 0x5a801ceb }, { 0x0000a5e4 , 0x74826e92 , 0x74826e92 , 0x5d801eec , 0x5d801eec }, { 0x0000a5e8 , 0x74826e92 , 0x74826e92 , 0x5d801eec , 0x5d801eec }, { 0x0000a5ec , 0x74826e92 , 0x74826e92 , 0x5d801eec , 0x5d801eec }, { 0x0000a5f0 , 0x74826e92 , 0x74826e92 , 0x5d801eec , 0x5d801eec }, { 0x0000a5f4 , 0x74826e92 , 0x74826e92 , 0x5d801eec , 0x5d801eec }, { 0x0000a5f8 , 0x74826e92 , 0x74826e92 , 0x5d801eec , 0x5d801eec }, { 0x0000a5fc , 0x74826e92 , 0x74826e92 , 0x5d801eec , 0x5d801eec }, { 0x0000a600 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a604 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a608 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a60c , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a610 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a614 , 0x02004000 , 0x02004000 , 0x01404000 , 0x01404000 }, { 0x0000a618 , 0x02004801 , 0x02004801 , 0x01404501 , 0x01404501 }, { 0x0000a61c , 0x02808a02 , 0x02808a02 , 0x02008501 , 0x02008501 }, { 0x0000a620 , 0x0380ce03 , 0x0380ce03 , 0x0280ca03 , 0x0280ca03 }, { 0x0000a624 , 0x04411104 , 0x04411104 , 0x03010c04 , 0x03010c04 }, { 0x0000a628 , 0x04411104 , 0x04411104 , 0x04014c04 , 0x04014c04 }, { 0x0000a62c , 0x04411104 , 0x04411104 , 0x04015005 , 0x04015005 }, { 0x0000a630 , 0x04411104 , 0x04411104 , 0x04015005 , 0x04015005 }, { 0x0000a634 , 0x04411104 , 0x04411104 , 0x04015005 , 0x04015005 }, { 0x0000a638 , 0x04411104 , 0x04411104 , 0x04015005 , 0x04015005 }, { 0x0000a63c , 0x04411104 , 0x04411104 , 0x04015005 , 0x04015005 }, { 0x0000b2dc , 0x00033800 , 0x00033800 , 0x03aaa352 , 0x03aaa352 }, { 0x0000b2e0 , 0x0003c000 , 0x0003c000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000b2e4 , 0x03fc0000 , 0x03fc0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000b2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000c2dc , 0x00033800 , 0x00033800 , 0x03aaa352 , 0x03aaa352 }, { 0x0000c2e0 , 0x0003c000 , 0x0003c000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000c2e4 , 0x03fc0000 , 0x03fc0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000c2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x00016044 , 0x012492d4 , 0x012492d4 , 0x012492d4 , 0x012492d4 }, { 0x00016048 , 0x66480001 , 0x66480001 , 0x66480001 , 0x66480001 }, { 0x00016068 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, { 0x00016444 , 0x012492d4 , 0x012492d4 , 0x012492d4 , 0x012492d4 }, { 0x00016448 , 0x66480001 , 0x66480001 , 0x66480001 , 0x66480001 }, { 0x00016468 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, { 0x00016844 , 0x012492d4 , 0x012492d4 , 0x012492d4 , 0x012492d4 }, { 0x00016848 , 0x66480001 , 0x66480001 , 0x66480001 , 0x66480001 }, { 0x00016868 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, }; static const u_int32_t ar9300_osprey_2p2_baseband_core_emulation[][2] = { /* Addr allmodes */ { 0x00009800 , 0xafa68e30 }, { 0x00009884 , 0x00002842 }, { 0x00009c04 , 0xff55ff55 }, { 0x00009c08 , 0x0320ff55 }, { 0x00009e50 , 0x00000000 }, { 0x00009fcc , 0x00000014 }, { 0x0000a344 , 0x00000010 }, { 0x0000a398 , 0x00000000 }, { 0x0000a39c , 0x71733d01 }, { 0x0000a3a0 , 0xd0ad5c12 }, { 0x0000a3c0 , 0x22222220 }, { 0x0000a3c4 , 0x22222222 }, { 0x0000a404 , 0x00418a11 }, { 0x0000a418 , 0x050001ce }, { 0x0000a438 , 0x00001800 }, { 0x0000a458 , 0x01444452 }, { 0x0000a690 , 0x00000038 }, { 0x0000b8dc , 0x00400000 }, }; static const u_int32_t ar9300_osprey_2p2_radio_core[][2] = { /* Addr allmodes */ { 0x00016000 , 0x36db6db6 }, { 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 , 0x36db6db6 }, { 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 , 0x36db6db6 }, { 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 }, }; static const u_int32_t ar9300_osprey_2p2_tx_gain_table_baseband_postamble_emulation[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x0000a410 , 0x000000d5 , 0x000000d5 , 0x000000d5 , 0x000000d5 }, { 0x0000a500 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a504 , 0x00004002 , 0x00004002 , 0x00004002 , 0x00004002 }, { 0x0000a508 , 0x00008004 , 0x00008004 , 0x00008004 , 0x00008004 }, { 0x0000a510 , 0x0001000c , 0x0001000c , 0x0001000c , 0x0001000c }, { 0x0000a514 , 0x0001420b , 0x0001420b , 0x0001420b , 0x0001420b }, { 0x0000a518 , 0x0001824a , 0x0001824a , 0x0001824a , 0x0001824a }, { 0x0000a51c , 0x0001c44a , 0x0001c44a , 0x0001c44a , 0x0001c44a }, { 0x0000a520 , 0x0002064a , 0x0002064a , 0x0002064a , 0x0002064a }, { 0x0000a524 , 0x0002484a , 0x0002484a , 0x0002484a , 0x0002484a }, { 0x0000a528 , 0x00028a4a , 0x00028a4a , 0x00028a4a , 0x00028a4a }, { 0x0000a52c , 0x0002cc4a , 0x0002cc4a , 0x0002cc4a , 0x0002cc4a }, { 0x0000a530 , 0x00030e4a , 0x00030e4a , 0x00030e4a , 0x00030e4a }, { 0x0000a534 , 0x00034e8a , 0x00034e8a , 0x00034e8a , 0x00034e8a }, }; static const u_int32_t ar9300_osprey_2p2_baseband_postamble_dfs_channel[][3] = { /* Addr 5G_HT20 5G_HT40 */ { 0x00009824 , 0x5ac668d0 , 0x5ac668d0 }, { 0x00009e0c , 0x6d4000e2 , 0x6d4000e2 }, { 0x00009e14 , 0x37b9625e , 0x37b9625e }, }; static const u_int32_t ar9300Modes_fast_clock_osprey_2p2[][3] = { /* Addr 5G_HT20 5G_HT40 */ { 0x00001030 , 0x00000268 , 0x000004d0 }, { 0x00001070 , 0x0000018c , 0x00000318 }, { 0x000010b0 , 0x00000fd0 , 0x00001fa0 }, { 0x00008014 , 0x044c044c , 0x08980898 }, { 0x0000801c , 0x148ec02b , 0x148ec057 }, { 0x00008318 , 0x000044c0 , 0x00008980 }, { 0x00009e00 , 0x0372131c , 0x0372131c }, { 0x0000a230 , 0x0000000b , 0x00000016 }, { 0x0000a254 , 0x00000898 , 0x00001130 }, }; static const u_int32_t ar9300_osprey_2p2_radio_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x0001609c , 0x0dd08f29 , 0x0dd08f29 , 0x0b283f31 , 0x0b283f31 }, { 0x000160ac , 0xa4653c00 , 0xa4653c00 , 0x24652800 , 0x24652800 }, { 0x000160b0 , 0x03284f3e , 0x03284f3e , 0x05d08f20 , 0x05d08f20 }, { 0x0001610c , 0xc8000000 , 0xc0000000 , 0xc0000000 , 0xc0000000 }, { 0x00016140 , 0x10804008 , 0x10804008 , 0x50804008 , 0x50804008 }, { 0x0001650c , 0xc8000000 , 0xc0000000 , 0xc0000000 , 0xc0000000 }, { 0x00016540 , 0x10804008 , 0x10804008 , 0x50804008 , 0x50804008 }, { 0x0001690c , 0xc8000000 , 0xc0000000 , 0xc0000000 , 0xc0000000 }, { 0x00016940 , 0x10804008 , 0x10804008 , 0x50804008 , 0x50804008 }, }; static const u_int32_t ar9300_modes_number_5_tx_gain_table_osprey_2p2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x0000a2dc , 0x000cfff0 , 0x000cfff0 , 0x03aaa352 , 0x03aaa352 }, { 0x0000a2e0 , 0x000f0000 , 0x000f0000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000a2e4 , 0x03f00000 , 0x03f00000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000a2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000a410 , 0x000050d9 , 0x000050d9 , 0x000050d9 , 0x000050d9 }, { 0x0000a500 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a504 , 0x06000003 , 0x06000003 , 0x04000002 , 0x04000002 }, { 0x0000a508 , 0x0a000020 , 0x0a000020 , 0x08000004 , 0x08000004 }, { 0x0000a50c , 0x10000023 , 0x10000023 , 0x0b000200 , 0x0b000200 }, { 0x0000a510 , 0x15000028 , 0x15000028 , 0x0f000202 , 0x0f000202 }, { 0x0000a514 , 0x1b00002b , 0x1b00002b , 0x12000400 , 0x12000400 }, { 0x0000a518 , 0x1f020028 , 0x1f020028 , 0x16000402 , 0x16000402 }, { 0x0000a51c , 0x2502002b , 0x2502002b , 0x19000404 , 0x19000404 }, { 0x0000a520 , 0x2a04002a , 0x2a04002a , 0x1c000603 , 0x1c000603 }, { 0x0000a524 , 0x2e06002a , 0x2e06002a , 0x21000a02 , 0x21000a02 }, { 0x0000a528 , 0x3302202d , 0x3302202d , 0x25000a04 , 0x25000a04 }, { 0x0000a52c , 0x3804202c , 0x3804202c , 0x28000a20 , 0x28000a20 }, { 0x0000a530 , 0x3c06202c , 0x3c06202c , 0x2c000e20 , 0x2c000e20 }, { 0x0000a534 , 0x4108202d , 0x4108202d , 0x30000e22 , 0x30000e22 }, { 0x0000a538 , 0x4506402d , 0x4506402d , 0x34000e24 , 0x34000e24 }, { 0x0000a53c , 0x4906222d , 0x4906222d , 0x38001640 , 0x38001640 }, { 0x0000a540 , 0x4d062231 , 0x4d062231 , 0x3c001660 , 0x3c001660 }, { 0x0000a544 , 0x50082231 , 0x50082231 , 0x3f001861 , 0x3f001861 }, { 0x0000a548 , 0x5608422e , 0x5608422e , 0x43001a81 , 0x43001a81 }, { 0x0000a54c , 0x5e08442e , 0x5e08442e , 0x47001a83 , 0x47001a83 }, { 0x0000a550 , 0x620a4431 , 0x620a4431 , 0x4a001c84 , 0x4a001c84 }, { 0x0000a554 , 0x640a4432 , 0x640a4432 , 0x4e001ce3 , 0x4e001ce3 }, { 0x0000a558 , 0x680a4434 , 0x680a4434 , 0x52001ce5 , 0x52001ce5 }, { 0x0000a55c , 0x6c0a6434 , 0x6c0a6434 , 0x56001ce9 , 0x56001ce9 }, { 0x0000a560 , 0x6f0a6633 , 0x6f0a6633 , 0x5a001ceb , 0x5a001ceb }, { 0x0000a564 , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a568 , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a56c , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a570 , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a574 , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a578 , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a57c , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a600 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a604 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a608 , 0x01804601 , 0x01804601 , 0x00000000 , 0x00000000 }, { 0x0000a60c , 0x01804601 , 0x01804601 , 0x00000000 , 0x00000000 }, { 0x0000a610 , 0x01804601 , 0x01804601 , 0x00000000 , 0x00000000 }, { 0x0000a614 , 0x01804601 , 0x01804601 , 0x01404000 , 0x01404000 }, { 0x0000a618 , 0x01804601 , 0x01804601 , 0x01404501 , 0x01404501 }, { 0x0000a61c , 0x01804601 , 0x01804601 , 0x02008501 , 0x02008501 }, { 0x0000a620 , 0x03408d02 , 0x03408d02 , 0x0280ca03 , 0x0280ca03 }, { 0x0000a624 , 0x0300cc03 , 0x0300cc03 , 0x03010c04 , 0x03010c04 }, { 0x0000a628 , 0x03410d04 , 0x03410d04 , 0x04014c04 , 0x04014c04 }, { 0x0000a62c , 0x03410d04 , 0x03410d04 , 0x04015005 , 0x04015005 }, { 0x0000a630 , 0x03410d04 , 0x03410d04 , 0x04015005 , 0x04015005 }, { 0x0000a634 , 0x03410d04 , 0x03410d04 , 0x04015005 , 0x04015005 }, { 0x0000a638 , 0x03410d04 , 0x03410d04 , 0x04015005 , 0x04015005 }, { 0x0000a63c , 0x03410d04 , 0x03410d04 , 0x04015005 , 0x04015005 }, { 0x0000b2dc , 0x000cfff0 , 0x000cfff0 , 0x03aaa352 , 0x03aaa352 }, { 0x0000b2e0 , 0x000f0000 , 0x000f0000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000b2e4 , 0x03f00000 , 0x03f00000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000b2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000c2dc , 0x000cfff0 , 0x000cfff0 , 0x03aaa352 , 0x03aaa352 }, { 0x0000c2e0 , 0x000f0000 , 0x000f0000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000c2e4 , 0x03f00000 , 0x03f00000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000c2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x00016044 , 0x012492d4 , 0x012492d4 , 0x012492d4 , 0x012492d4 }, { 0x00016048 , 0x65240001 , 0x65240001 , 0x66480001 , 0x66480001 }, { 0x00016068 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, { 0x00016444 , 0x012492d4 , 0x012492d4 , 0x012492d4 , 0x012492d4 }, { 0x00016448 , 0x65240001 , 0x65240001 , 0x66480001 , 0x66480001 }, { 0x00016468 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, { 0x00016844 , 0x012492d4 , 0x012492d4 , 0x012492d4 , 0x012492d4 }, { 0x00016848 , 0x65240001 , 0x65240001 , 0x66480001 , 0x66480001 }, { 0x00016868 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, }; static const u_int32_t ar9300_osprey_2p2_mac_core_emulation[][2] = { /* Addr allmodes */ { 0x00000030 , 0x00020085 }, { 0x00000044 , 0x00000008 }, { 0x0000805c , 0xffffc7ff }, { 0x00008344 , 0xaa4a105b }, }; static const u_int32_t ar9300_osprey_2p2_baseband_postamble_emulation[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x00009e18 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x00009e3c , 0xcf946221 , 0xcf946221 , 0xcf946221 , 0xcf946221 }, { 0x00009e44 , 0x005c0000 , 0x005c0000 , 0x005c0000 , 0x005c0000 }, { 0x0000a258 , 0x02020200 , 0x02020200 , 0x02020200 , 0x02020200 }, { 0x0000a25c , 0x00000e0e , 0x00000e0e , 0x00000e0e , 0x00000e0e }, { 0x0000a28c , 0x00011111 , 0x00011111 , 0x00011111 , 0x00011111 }, { 0x0000a2c4 , 0x00148d18 , 0x00148d18 , 0x00148d20 , 0x00148d20 }, { 0x0000a2d8 , 0xf999a801 , 0xf999a801 , 0xf999a80d , 0xf999a80d }, { 0x0000a50c , 0x0000c00a , 0x0000c00a , 0x0000c00a , 0x0000c00a }, { 0x0000a538 , 0x00038e8c , 0x00038e8c , 0x00038e8c , 0x00038e8c }, { 0x0000a53c , 0x0003cecc , 0x0003cecc , 0x0003cecc , 0x0003cecc }, { 0x0000a540 , 0x00040ed4 , 0x00040ed4 , 0x00040ed4 , 0x00040ed4 }, { 0x0000a544 , 0x00044edc , 0x00044edc , 0x00044edc , 0x00044edc }, { 0x0000a548 , 0x00048ede , 0x00048ede , 0x00048ede , 0x00048ede }, { 0x0000a54c , 0x0004cf1e , 0x0004cf1e , 0x0004cf1e , 0x0004cf1e }, { 0x0000a550 , 0x00050f5e , 0x00050f5e , 0x00050f5e , 0x00050f5e }, { 0x0000a554 , 0x00054f9e , 0x00054f9e , 0x00054f9e , 0x00054f9e }, { 0x0000ae18 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000be18 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, }; static const u_int32_t ar9300_osprey_2p2_baseband_core[][2] = { /* Addr allmodes */ { 0x00009800 , 0xafe68e30 }, { 0x00009804 , 0xfd14e000 }, { 0x00009808 , 0x9c0a9f6b }, { 0x0000980c , 0x04900000 }, { 0x00009814 , 0x9280c00a }, { 0x00009818 , 0x00000000 }, { 0x0000981c , 0x00020028 }, { 0x00009834 , 0x6400a190 }, { 0x00009838 , 0x0108ecff }, { 0x0000983c , 0x14000600 }, { 0x00009880 , 0x201fff00 }, { 0x00009884 , 0x00001042 }, { 0x000098a4 , 0x00200400 }, { 0x000098b0 , 0x32840bbe }, { 0x000098d0 , 0x004b6a8e }, { 0x000098d4 , 0x00000820 }, { 0x000098dc , 0x00000000 }, { 0x000098f0 , 0x00000000 }, { 0x000098f4 , 0x00000000 }, { 0x00009c04 , 0xff55ff55 }, { 0x00009c08 , 0x0320ff55 }, { 0x00009c0c , 0x00000000 }, { 0x00009c10 , 0x00000000 }, { 0x00009c14 , 0x00046384 }, { 0x00009c18 , 0x05b6b440 }, { 0x00009c1c , 0x00b6b440 }, { 0x00009d00 , 0xc080a333 }, { 0x00009d04 , 0x40206c10 }, { 0x00009d08 , 0x009c4060 }, { 0x00009d0c , 0x9883800a }, { 0x00009d10 , 0x01834061 }, { 0x00009d14 , 0x00c0040b }, { 0x00009d18 , 0x00000000 }, { 0x00009e08 , 0x0038230c }, { 0x00009e24 , 0x990bb515 }, { 0x00009e28 , 0x0c6f0000 }, { 0x00009e30 , 0x06336f77 }, { 0x00009e34 , 0x6af6532f }, { 0x00009e38 , 0x0cc80c00 }, { 0x00009e40 , 0x0d261820 }, { 0x00009e4c , 0x00001004 }, { 0x00009e50 , 0x00ff03f1 }, { 0x00009e54 , 0x00000000 }, { 0x00009fc0 , 0x803e4788 }, { 0x00009fc4 , 0x0001efb5 }, { 0x00009fcc , 0x40000014 }, { 0x00009fd0 , 0x01193b93 }, { 0x0000a20c , 0x00000000 }, { 0x0000a220 , 0x00000000 }, { 0x0000a224 , 0x00000000 }, { 0x0000a228 , 0x10002310 }, { 0x0000a23c , 0x00000000 }, { 0x0000a244 , 0x0c000000 }, { 0x0000a2a0 , 0x00000001 }, { 0x0000a2c0 , 0x00000001 }, { 0x0000a2c8 , 0x00000000 }, { 0x0000a2cc , 0x18c43433 }, { 0x0000a2d4 , 0x00000000 }, { 0x0000a2ec , 0x00000000 }, { 0x0000a2f0 , 0x00000000 }, { 0x0000a2f4 , 0x00000000 }, { 0x0000a2f8 , 0x00000000 }, { 0x0000a344 , 0x00000000 }, { 0x0000a34c , 0x00000000 }, { 0x0000a350 , 0x0000a000 }, { 0x0000a364 , 0x00000000 }, { 0x0000a370 , 0x00000000 }, { 0x0000a390 , 0x00000001 }, { 0x0000a394 , 0x00000444 }, { 0x0000a398 , 0x001f0e0f }, { 0x0000a39c , 0x0075393f }, { 0x0000a3a0 , 0xb79f6427 }, { 0x0000a3a4 , 0x00000000 }, { 0x0000a3a8 , 0xaaaaaaaa }, { 0x0000a3ac , 0x3c466478 }, { 0x0000a3c0 , 0x20202020 }, { 0x0000a3c4 , 0x22222220 }, { 0x0000a3c8 , 0x20200020 }, { 0x0000a3cc , 0x20202020 }, { 0x0000a3d0 , 0x20202020 }, { 0x0000a3d4 , 0x20202020 }, { 0x0000a3d8 , 0x20202020 }, { 0x0000a3dc , 0x20202020 }, { 0x0000a3e0 , 0x20202020 }, { 0x0000a3e4 , 0x20202020 }, { 0x0000a3e8 , 0x20202020 }, { 0x0000a3ec , 0x20202020 }, { 0x0000a3f0 , 0x00000000 }, { 0x0000a3f4 , 0x00000000 }, { 0x0000a3f8 , 0x0c9bd380 }, { 0x0000a3fc , 0x000f0f01 }, { 0x0000a400 , 0x8fa91f01 }, { 0x0000a404 , 0x00000000 }, { 0x0000a408 , 0x0e79e5c6 }, { 0x0000a40c , 0x00820820 }, { 0x0000a414 , 0x1ce739ce }, { 0x0000a418 , 0x2d001dce }, { 0x0000a41c , 0x1ce739ce }, { 0x0000a420 , 0x000001ce }, { 0x0000a424 , 0x1ce739ce }, { 0x0000a428 , 0x000001ce }, { 0x0000a42c , 0x1ce739ce }, { 0x0000a430 , 0x1ce739ce }, { 0x0000a434 , 0x00000000 }, { 0x0000a438 , 0x00001801 }, { 0x0000a43c , 0x00100000 }, { 0x0000a440 , 0x00000000 }, { 0x0000a444 , 0x00000000 }, { 0x0000a448 , 0x05000080 }, { 0x0000a44c , 0x00000001 }, { 0x0000a450 , 0x00010000 }, { 0x0000a458 , 0x00000000 }, { 0x0000a640 , 0x00000000 }, { 0x0000a644 , 0x3fad9d74 }, { 0x0000a648 , 0x0048060a }, { 0x0000a64c , 0x00003c37 }, { 0x0000a670 , 0x03020100 }, { 0x0000a674 , 0x09080504 }, { 0x0000a678 , 0x0d0c0b0a }, { 0x0000a67c , 0x13121110 }, { 0x0000a680 , 0x31301514 }, { 0x0000a684 , 0x35343332 }, { 0x0000a688 , 0x00000036 }, { 0x0000a690 , 0x00000838 }, { 0x0000a7c0 , 0x00000000 }, { 0x0000a7c4 , 0xfffffffc }, { 0x0000a7c8 , 0x00000000 }, { 0x0000a7cc , 0x00000000 }, { 0x0000a7d0 , 0x00000000 }, { 0x0000a7d4 , 0x00000004 }, { 0x0000a7dc , 0x00000001 }, { 0x0000a8d0 , 0x004b6a8e }, { 0x0000a8d4 , 0x00000820 }, { 0x0000a8dc , 0x00000000 }, { 0x0000a8f0 , 0x00000000 }, { 0x0000a8f4 , 0x00000000 }, { 0x0000b2d0 , 0x00000080 }, { 0x0000b2d4 , 0x00000000 }, { 0x0000b2ec , 0x00000000 }, { 0x0000b2f0 , 0x00000000 }, { 0x0000b2f4 , 0x00000000 }, { 0x0000b2f8 , 0x00000000 }, { 0x0000b408 , 0x0e79e5c0 }, { 0x0000b40c , 0x00820820 }, { 0x0000b420 , 0x00000000 }, { 0x0000b8d0 , 0x004b6a8e }, { 0x0000b8d4 , 0x00000820 }, { 0x0000b8dc , 0x00000000 }, { 0x0000b8f0 , 0x00000000 }, { 0x0000b8f4 , 0x00000000 }, { 0x0000c2d0 , 0x00000080 }, { 0x0000c2d4 , 0x00000000 }, { 0x0000c2ec , 0x00000000 }, { 0x0000c2f0 , 0x00000000 }, { 0x0000c2f4 , 0x00000000 }, { 0x0000c2f8 , 0x00000000 }, { 0x0000c408 , 0x0e79e5c0 }, { 0x0000c40c , 0x00820820 }, { 0x0000c420 , 0x00000000 }, }; static const u_int32_t ar9300Modes_high_ob_db_tx_gain_table_osprey_2p2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x0000a2dc , 0x01feee00 , 0x01feee00 , 0x03aaa352 , 0x03aaa352 }, { 0x0000a2e0 , 0x0000f000 , 0x0000f000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000a2e4 , 0x01ff0000 , 0x01ff0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000a2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000a410 , 0x000050d4 , 0x000050d4 , 0x000050d9 , 0x000050d9 }, { 0x0000a500 , 0x00002220 , 0x00002220 , 0x00000000 , 0x00000000 }, { 0x0000a504 , 0x04002222 , 0x04002222 , 0x04000002 , 0x04000002 }, { 0x0000a508 , 0x09002421 , 0x09002421 , 0x08000004 , 0x08000004 }, { 0x0000a50c , 0x0d002621 , 0x0d002621 , 0x0b000200 , 0x0b000200 }, { 0x0000a510 , 0x13004620 , 0x13004620 , 0x0f000202 , 0x0f000202 }, { 0x0000a514 , 0x19004a20 , 0x19004a20 , 0x11000400 , 0x11000400 }, { 0x0000a518 , 0x1d004e20 , 0x1d004e20 , 0x15000402 , 0x15000402 }, { 0x0000a51c , 0x21005420 , 0x21005420 , 0x19000404 , 0x19000404 }, { 0x0000a520 , 0x26005e20 , 0x26005e20 , 0x1b000603 , 0x1b000603 }, { 0x0000a524 , 0x2b005e40 , 0x2b005e40 , 0x1f000a02 , 0x1f000a02 }, { 0x0000a528 , 0x2f005e42 , 0x2f005e42 , 0x23000a04 , 0x23000a04 }, { 0x0000a52c , 0x33005e44 , 0x33005e44 , 0x26000a20 , 0x26000a20 }, { 0x0000a530 , 0x38005e65 , 0x38005e65 , 0x2a000e20 , 0x2a000e20 }, { 0x0000a534 , 0x3c005e69 , 0x3c005e69 , 0x2e000e22 , 0x2e000e22 }, { 0x0000a538 , 0x40005e6b , 0x40005e6b , 0x31000e24 , 0x31000e24 }, { 0x0000a53c , 0x44005e6d , 0x44005e6d , 0x34001640 , 0x34001640 }, { 0x0000a540 , 0x49005e72 , 0x49005e72 , 0x38001660 , 0x38001660 }, { 0x0000a544 , 0x4e005eb2 , 0x4e005eb2 , 0x3b001861 , 0x3b001861 }, { 0x0000a548 , 0x53005f12 , 0x53005f12 , 0x3e001a81 , 0x3e001a81 }, { 0x0000a54c , 0x59025eb2 , 0x59025eb2 , 0x42001a83 , 0x42001a83 }, { 0x0000a550 , 0x5e025f12 , 0x5e025f12 , 0x44001c84 , 0x44001c84 }, { 0x0000a554 , 0x61027f12 , 0x61027f12 , 0x48001ce3 , 0x48001ce3 }, { 0x0000a558 , 0x6702bf12 , 0x6702bf12 , 0x4c001ce5 , 0x4c001ce5 }, { 0x0000a55c , 0x6b02bf14 , 0x6b02bf14 , 0x50001ce9 , 0x50001ce9 }, { 0x0000a560 , 0x6f02bf16 , 0x6f02bf16 , 0x54001ceb , 0x54001ceb }, { 0x0000a564 , 0x6f02bf16 , 0x6f02bf16 , 0x56001eec , 0x56001eec }, { 0x0000a568 , 0x6f02bf16 , 0x6f02bf16 , 0x56001eec , 0x56001eec }, { 0x0000a56c , 0x6f02bf16 , 0x6f02bf16 , 0x56001eec , 0x56001eec }, { 0x0000a570 , 0x6f02bf16 , 0x6f02bf16 , 0x56001eec , 0x56001eec }, { 0x0000a574 , 0x6f02bf16 , 0x6f02bf16 , 0x56001eec , 0x56001eec }, { 0x0000a578 , 0x6f02bf16 , 0x6f02bf16 , 0x56001eec , 0x56001eec }, { 0x0000a57c , 0x6f02bf16 , 0x6f02bf16 , 0x56001eec , 0x56001eec }, { 0x0000a580 , 0x00802220 , 0x00802220 , 0x00800000 , 0x00800000 }, { 0x0000a584 , 0x04802222 , 0x04802222 , 0x04800002 , 0x04800002 }, { 0x0000a588 , 0x09802421 , 0x09802421 , 0x08800004 , 0x08800004 }, { 0x0000a58c , 0x0d802621 , 0x0d802621 , 0x0b800200 , 0x0b800200 }, { 0x0000a590 , 0x13804620 , 0x13804620 , 0x0f800202 , 0x0f800202 }, { 0x0000a594 , 0x19804a20 , 0x19804a20 , 0x11800400 , 0x11800400 }, { 0x0000a598 , 0x1d804e20 , 0x1d804e20 , 0x15800402 , 0x15800402 }, { 0x0000a59c , 0x21805420 , 0x21805420 , 0x19800404 , 0x19800404 }, { 0x0000a5a0 , 0x26805e20 , 0x26805e20 , 0x1b800603 , 0x1b800603 }, { 0x0000a5a4 , 0x2b805e40 , 0x2b805e40 , 0x1f800a02 , 0x1f800a02 }, { 0x0000a5a8 , 0x2f805e42 , 0x2f805e42 , 0x23800a04 , 0x23800a04 }, { 0x0000a5ac , 0x33805e44 , 0x33805e44 , 0x26800a20 , 0x26800a20 }, { 0x0000a5b0 , 0x38805e65 , 0x38805e65 , 0x2a800e20 , 0x2a800e20 }, { 0x0000a5b4 , 0x3c805e69 , 0x3c805e69 , 0x2e800e22 , 0x2e800e22 }, { 0x0000a5b8 , 0x40805e6b , 0x40805e6b , 0x31800e24 , 0x31800e24 }, { 0x0000a5bc , 0x44805e6d , 0x44805e6d , 0x34801640 , 0x34801640 }, { 0x0000a5c0 , 0x49805e72 , 0x49805e72 , 0x38801660 , 0x38801660 }, { 0x0000a5c4 , 0x4e805eb2 , 0x4e805eb2 , 0x3b801861 , 0x3b801861 }, { 0x0000a5c8 , 0x53805f12 , 0x53805f12 , 0x3e801a81 , 0x3e801a81 }, { 0x0000a5cc , 0x59825eb2 , 0x59825eb2 , 0x42801a83 , 0x42801a83 }, { 0x0000a5d0 , 0x5e825f12 , 0x5e825f12 , 0x44801c84 , 0x44801c84 }, { 0x0000a5d4 , 0x61827f12 , 0x61827f12 , 0x48801ce3 , 0x48801ce3 }, { 0x0000a5d8 , 0x6782bf12 , 0x6782bf12 , 0x4c801ce5 , 0x4c801ce5 }, { 0x0000a5dc , 0x6b82bf14 , 0x6b82bf14 , 0x50801ce9 , 0x50801ce9 }, { 0x0000a5e0 , 0x6f82bf16 , 0x6f82bf16 , 0x54801ceb , 0x54801ceb }, { 0x0000a5e4 , 0x6f82bf16 , 0x6f82bf16 , 0x56801eec , 0x56801eec }, { 0x0000a5e8 , 0x6f82bf16 , 0x6f82bf16 , 0x56801eec , 0x56801eec }, { 0x0000a5ec , 0x6f82bf16 , 0x6f82bf16 , 0x56801eec , 0x56801eec }, { 0x0000a5f0 , 0x6f82bf16 , 0x6f82bf16 , 0x56801eec , 0x56801eec }, { 0x0000a5f4 , 0x6f82bf16 , 0x6f82bf16 , 0x56801eec , 0x56801eec }, { 0x0000a5f8 , 0x6f82bf16 , 0x6f82bf16 , 0x56801eec , 0x56801eec }, { 0x0000a5fc , 0x6f82bf16 , 0x6f82bf16 , 0x56801eec , 0x56801eec }, { 0x0000a600 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a604 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a608 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a60c , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a610 , 0x00804000 , 0x00804000 , 0x00000000 , 0x00000000 }, { 0x0000a614 , 0x00804201 , 0x00804201 , 0x01404000 , 0x01404000 }, { 0x0000a618 , 0x0280c802 , 0x0280c802 , 0x01404501 , 0x01404501 }, { 0x0000a61c , 0x0280ca03 , 0x0280ca03 , 0x02008501 , 0x02008501 }, { 0x0000a620 , 0x04c15104 , 0x04c15104 , 0x0280ca03 , 0x0280ca03 }, { 0x0000a624 , 0x04c15305 , 0x04c15305 , 0x03010c04 , 0x03010c04 }, { 0x0000a628 , 0x04c15305 , 0x04c15305 , 0x04014c04 , 0x04014c04 }, { 0x0000a62c , 0x04c15305 , 0x04c15305 , 0x04015005 , 0x04015005 }, { 0x0000a630 , 0x04c15305 , 0x04c15305 , 0x04015005 , 0x04015005 }, { 0x0000a634 , 0x04c15305 , 0x04c15305 , 0x04015005 , 0x04015005 }, { 0x0000a638 , 0x04c15305 , 0x04c15305 , 0x04015005 , 0x04015005 }, { 0x0000a63c , 0x04c15305 , 0x04c15305 , 0x04015005 , 0x04015005 }, { 0x0000b2dc , 0x01feee00 , 0x01feee00 , 0x03aaa352 , 0x03aaa352 }, { 0x0000b2e0 , 0x0000f000 , 0x0000f000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000b2e4 , 0x01ff0000 , 0x01ff0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000b2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000c2dc , 0x01feee00 , 0x01feee00 , 0x03aaa352 , 0x03aaa352 }, { 0x0000c2e0 , 0x0000f000 , 0x0000f000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000c2e4 , 0x01ff0000 , 0x01ff0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000c2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x00016044 , 0x056db2e4 , 0x056db2e4 , 0x056db2e4 , 0x056db2e4 }, { 0x00016048 , 0x8e480001 , 0x8e480001 , 0x8e480001 , 0x8e480001 }, { 0x00016068 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, { 0x00016444 , 0x056db2e4 , 0x056db2e4 , 0x056db2e4 , 0x056db2e4 }, { 0x00016448 , 0x8e480001 , 0x8e480001 , 0x8e480001 , 0x8e480001 }, { 0x00016468 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, { 0x00016844 , 0x056db2e4 , 0x056db2e4 , 0x056db2e4 , 0x056db2e4 }, { 0x00016848 , 0x8e480001 , 0x8e480001 , 0x8e480001 , 0x8e480001 }, { 0x00016868 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, }; static const u_int32_t ar9300_osprey_2p2_mac_core[][2] = { /* Addr allmodes */ { 0x00000008 , 0x00000000 }, { 0x00000030 , 0x00020085 }, { 0x00000034 , 0x00000005 }, { 0x00000040 , 0x00000000 }, { 0x00000044 , 0x00000000 }, { 0x00000048 , 0x00000008 }, { 0x0000004c , 0x00000010 }, { 0x00000050 , 0x00000000 }, { 0x00001040 , 0x002ffc0f }, { 0x00001044 , 0x002ffc0f }, { 0x00001048 , 0x002ffc0f }, { 0x0000104c , 0x002ffc0f }, { 0x00001050 , 0x002ffc0f }, { 0x00001054 , 0x002ffc0f }, { 0x00001058 , 0x002ffc0f }, { 0x0000105c , 0x002ffc0f }, { 0x00001060 , 0x002ffc0f }, { 0x00001064 , 0x002ffc0f }, { 0x000010f0 , 0x00000100 }, { 0x00001270 , 0x00000000 }, { 0x000012b0 , 0x00000000 }, { 0x000012f0 , 0x00000000 }, { 0x0000143c , 0x00000000 }, { 0x0000147c , 0x00000000 }, { 0x00008000 , 0x00000000 }, { 0x00008004 , 0x00000000 }, { 0x00008008 , 0x00000000 }, { 0x0000800c , 0x00000000 }, { 0x00008018 , 0x00000000 }, { 0x00008020 , 0x00000000 }, { 0x00008038 , 0x00000000 }, { 0x0000803c , 0x00000000 }, { 0x00008040 , 0x00000000 }, { 0x00008044 , 0x00000000 }, { 0x00008048 , 0x00000000 }, { 0x0000804c , 0xffffffff }, { 0x00008054 , 0x00000000 }, { 0x00008058 , 0x00000000 }, { 0x0000805c , 0x000fc78f }, { 0x00008060 , 0x0000000f }, { 0x00008064 , 0x00000000 }, { 0x00008070 , 0x00000310 }, { 0x00008074 , 0x00000020 }, { 0x00008078 , 0x00000000 }, { 0x0000809c , 0x0000000f }, { 0x000080a0 , 0x00000000 }, { 0x000080a4 , 0x02ff0000 }, { 0x000080a8 , 0x0e070605 }, { 0x000080ac , 0x0000000d }, { 0x000080b0 , 0x00000000 }, { 0x000080b4 , 0x00000000 }, { 0x000080b8 , 0x00000000 }, { 0x000080bc , 0x00000000 }, { 0x000080c0 , 0x2a800000 }, { 0x000080c4 , 0x06900168 }, { 0x000080c8 , 0x13881c20 }, { 0x000080cc , 0x01f40000 }, { 0x000080d0 , 0x00252500 }, { 0x000080d4 , 0x00a00000 }, { 0x000080d8 , 0x00400000 }, { 0x000080dc , 0x00000000 }, { 0x000080e0 , 0xffffffff }, { 0x000080e4 , 0x0000ffff }, { 0x000080e8 , 0x3f3f3f3f }, { 0x000080ec , 0x00000000 }, { 0x000080f0 , 0x00000000 }, { 0x000080f4 , 0x00000000 }, { 0x000080fc , 0x00020000 }, { 0x00008100 , 0x00000000 }, { 0x00008108 , 0x00000052 }, { 0x0000810c , 0x00000000 }, { 0x00008110 , 0x00000000 }, { 0x00008114 , 0x000007ff }, { 0x00008118 , 0x000000aa }, { 0x0000811c , 0x00003210 }, { 0x00008124 , 0x00000000 }, { 0x00008128 , 0x00000000 }, { 0x0000812c , 0x00000000 }, { 0x00008130 , 0x00000000 }, { 0x00008134 , 0x00000000 }, { 0x00008138 , 0x00000000 }, { 0x0000813c , 0x0000ffff }, { 0x00008144 , 0xffffffff }, { 0x00008168 , 0x00000000 }, { 0x0000816c , 0x00000000 }, { 0x000081c0 , 0x00000000 }, { 0x000081c4 , 0x33332210 }, { 0x000081ec , 0x00000000 }, { 0x000081f0 , 0x00000000 }, { 0x000081f4 , 0x00000000 }, { 0x000081f8 , 0x00000000 }, { 0x000081fc , 0x00000000 }, { 0x00008240 , 0x00100000 }, { 0x00008244 , 0x0010f400 }, { 0x00008248 , 0x00000800 }, { 0x0000824c , 0x0001e800 }, { 0x00008250 , 0x00000000 }, { 0x00008254 , 0x00000000 }, { 0x00008258 , 0x00000000 }, { 0x0000825c , 0x40000000 }, { 0x00008260 , 0x00080922 }, - { 0x00008264 , 0x9bc00010 }, + { 0x00008264 , 0x9d400010 }, { 0x00008268 , 0xffffffff }, { 0x0000826c , 0x0000ffff }, { 0x00008270 , 0x00000000 }, { 0x00008274 , 0x40000000 }, { 0x00008278 , 0x003e4180 }, { 0x0000827c , 0x00000004 }, { 0x00008284 , 0x0000002c }, { 0x00008288 , 0x0000002c }, { 0x0000828c , 0x000000ff }, { 0x00008294 , 0x00000000 }, { 0x00008298 , 0x00000000 }, { 0x0000829c , 0x00000000 }, { 0x00008300 , 0x00000140 }, { 0x00008314 , 0x00000000 }, { 0x0000831c , 0x0000010d }, { 0x00008328 , 0x00000000 }, { 0x0000832c , 0x00000007 }, { 0x00008330 , 0x00000302 }, { 0x00008334 , 0x00000700 }, { 0x00008338 , 0x00ff0000 }, { 0x0000833c , 0x02400000 }, { 0x00008340 , 0x000107ff }, { 0x00008344 , 0xaa48105b }, { 0x00008348 , 0x008f0000 }, { 0x0000835c , 0x00000000 }, { 0x00008360 , 0xffffffff }, { 0x00008364 , 0xffffffff }, { 0x00008368 , 0x00000000 }, { 0x00008370 , 0x00000000 }, { 0x00008374 , 0x000000ff }, { 0x00008378 , 0x00000000 }, { 0x0000837c , 0x00000000 }, { 0x00008380 , 0xffffffff }, { 0x00008384 , 0xffffffff }, { 0x00008390 , 0xffffffff }, { 0x00008394 , 0xffffffff }, { 0x00008398 , 0x00000000 }, { 0x0000839c , 0x00000000 }, { 0x000083a0 , 0x00000000 }, { 0x000083a4 , 0x0000fa14 }, { 0x000083a8 , 0x000f0c00 }, { 0x000083ac , 0x33332210 }, { 0x000083b0 , 0x33332210 }, { 0x000083b4 , 0x33332210 }, { 0x000083b8 , 0x33332210 }, { 0x000083bc , 0x00000000 }, { 0x000083c0 , 0x00000000 }, { 0x000083c4 , 0x00000000 }, { 0x000083c8 , 0x00000000 }, { 0x000083cc , 0x00000200 }, { 0x000083d0 , 0x000301ff }, }; static const u_int32_t ar9300_osprey_2p2_soc_preamble[][2] = { /* Addr allmodes */ { 0x000040a4 , 0x00a0c1c9 }, { 0x00007008 , 0x00000000 }, { 0x00007020 , 0x00000000 }, { 0x00007034 , 0x00000002 }, { 0x00007038 , 0x000004c2 }, { 0x00007048 , 0x00000008 }, }; static const u_int32_t ar9300_modes_lowest_ob_db_tx_gain_table_osprey_2p2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x0000a2dc , 0x00033800 , 0x00033800 , 0x03aaa352 , 0x03aaa352 }, { 0x0000a2e0 , 0x0003c000 , 0x0003c000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000a2e4 , 0x03fc0000 , 0x03fc0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000a2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000a410 , 0x000050d9 , 0x000050d9 , 0x000050d9 , 0x000050d9 }, { 0x0000a500 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a504 , 0x06000003 , 0x06000003 , 0x04000002 , 0x04000002 }, { 0x0000a508 , 0x0a000020 , 0x0a000020 , 0x08000004 , 0x08000004 }, { 0x0000a50c , 0x10000023 , 0x10000023 , 0x0b000200 , 0x0b000200 }, { 0x0000a510 , 0x16000220 , 0x16000220 , 0x0f000202 , 0x0f000202 }, { 0x0000a514 , 0x1c000223 , 0x1c000223 , 0x12000400 , 0x12000400 }, { 0x0000a518 , 0x21002220 , 0x21002220 , 0x16000402 , 0x16000402 }, { 0x0000a51c , 0x27002223 , 0x27002223 , 0x19000404 , 0x19000404 }, { 0x0000a520 , 0x2b022220 , 0x2b022220 , 0x1c000603 , 0x1c000603 }, { 0x0000a524 , 0x2f022222 , 0x2f022222 , 0x21000a02 , 0x21000a02 }, { 0x0000a528 , 0x34022225 , 0x34022225 , 0x25000a04 , 0x25000a04 }, { 0x0000a52c , 0x3a02222a , 0x3a02222a , 0x28000a20 , 0x28000a20 }, { 0x0000a530 , 0x3e02222c , 0x3e02222c , 0x2c000e20 , 0x2c000e20 }, { 0x0000a534 , 0x4202242a , 0x4202242a , 0x30000e22 , 0x30000e22 }, { 0x0000a538 , 0x4702244a , 0x4702244a , 0x34000e24 , 0x34000e24 }, { 0x0000a53c , 0x4b02244c , 0x4b02244c , 0x38001640 , 0x38001640 }, { 0x0000a540 , 0x4e02246c , 0x4e02246c , 0x3c001660 , 0x3c001660 }, { 0x0000a544 , 0x52022470 , 0x52022470 , 0x3f001861 , 0x3f001861 }, { 0x0000a548 , 0x55022490 , 0x55022490 , 0x43001a81 , 0x43001a81 }, { 0x0000a54c , 0x59022492 , 0x59022492 , 0x47001a83 , 0x47001a83 }, { 0x0000a550 , 0x5d022692 , 0x5d022692 , 0x4a001c84 , 0x4a001c84 }, { 0x0000a554 , 0x61022892 , 0x61022892 , 0x4e001ce3 , 0x4e001ce3 }, { 0x0000a558 , 0x65024890 , 0x65024890 , 0x52001ce5 , 0x52001ce5 }, { 0x0000a55c , 0x69024892 , 0x69024892 , 0x56001ce9 , 0x56001ce9 }, { 0x0000a560 , 0x6e024c92 , 0x6e024c92 , 0x5a001ceb , 0x5a001ceb }, { 0x0000a564 , 0x74026e92 , 0x74026e92 , 0x5d001eec , 0x5d001eec }, { 0x0000a568 , 0x74026e92 , 0x74026e92 , 0x5d001eec , 0x5d001eec }, { 0x0000a56c , 0x74026e92 , 0x74026e92 , 0x5d001eec , 0x5d001eec }, { 0x0000a570 , 0x74026e92 , 0x74026e92 , 0x5d001eec , 0x5d001eec }, { 0x0000a574 , 0x74026e92 , 0x74026e92 , 0x5d001eec , 0x5d001eec }, { 0x0000a578 , 0x74026e92 , 0x74026e92 , 0x5d001eec , 0x5d001eec }, { 0x0000a57c , 0x74026e92 , 0x74026e92 , 0x5d001eec , 0x5d001eec }, { 0x0000a580 , 0x00800000 , 0x00800000 , 0x00800000 , 0x00800000 }, { 0x0000a584 , 0x06800003 , 0x06800003 , 0x04800002 , 0x04800002 }, { 0x0000a588 , 0x0a800020 , 0x0a800020 , 0x08800004 , 0x08800004 }, { 0x0000a58c , 0x10800023 , 0x10800023 , 0x0b800200 , 0x0b800200 }, { 0x0000a590 , 0x16800220 , 0x16800220 , 0x0f800202 , 0x0f800202 }, { 0x0000a594 , 0x1c800223 , 0x1c800223 , 0x12800400 , 0x12800400 }, { 0x0000a598 , 0x21802220 , 0x21802220 , 0x16800402 , 0x16800402 }, { 0x0000a59c , 0x27802223 , 0x27802223 , 0x19800404 , 0x19800404 }, { 0x0000a5a0 , 0x2b822220 , 0x2b822220 , 0x1c800603 , 0x1c800603 }, { 0x0000a5a4 , 0x2f822222 , 0x2f822222 , 0x21800a02 , 0x21800a02 }, { 0x0000a5a8 , 0x34822225 , 0x34822225 , 0x25800a04 , 0x25800a04 }, { 0x0000a5ac , 0x3a82222a , 0x3a82222a , 0x28800a20 , 0x28800a20 }, { 0x0000a5b0 , 0x3e82222c , 0x3e82222c , 0x2c800e20 , 0x2c800e20 }, { 0x0000a5b4 , 0x4282242a , 0x4282242a , 0x30800e22 , 0x30800e22 }, { 0x0000a5b8 , 0x4782244a , 0x4782244a , 0x34800e24 , 0x34800e24 }, { 0x0000a5bc , 0x4b82244c , 0x4b82244c , 0x38801640 , 0x38801640 }, { 0x0000a5c0 , 0x4e82246c , 0x4e82246c , 0x3c801660 , 0x3c801660 }, { 0x0000a5c4 , 0x52822470 , 0x52822470 , 0x3f801861 , 0x3f801861 }, { 0x0000a5c8 , 0x55822490 , 0x55822490 , 0x43801a81 , 0x43801a81 }, { 0x0000a5cc , 0x59822492 , 0x59822492 , 0x47801a83 , 0x47801a83 }, { 0x0000a5d0 , 0x5d822692 , 0x5d822692 , 0x4a801c84 , 0x4a801c84 }, { 0x0000a5d4 , 0x61822892 , 0x61822892 , 0x4e801ce3 , 0x4e801ce3 }, { 0x0000a5d8 , 0x65824890 , 0x65824890 , 0x52801ce5 , 0x52801ce5 }, { 0x0000a5dc , 0x69824892 , 0x69824892 , 0x56801ce9 , 0x56801ce9 }, { 0x0000a5e0 , 0x6e824c92 , 0x6e824c92 , 0x5a801ceb , 0x5a801ceb }, { 0x0000a5e4 , 0x74826e92 , 0x74826e92 , 0x5d801eec , 0x5d801eec }, { 0x0000a5e8 , 0x74826e92 , 0x74826e92 , 0x5d801eec , 0x5d801eec }, { 0x0000a5ec , 0x74826e92 , 0x74826e92 , 0x5d801eec , 0x5d801eec }, { 0x0000a5f0 , 0x74826e92 , 0x74826e92 , 0x5d801eec , 0x5d801eec }, { 0x0000a5f4 , 0x74826e92 , 0x74826e92 , 0x5d801eec , 0x5d801eec }, { 0x0000a5f8 , 0x74826e92 , 0x74826e92 , 0x5d801eec , 0x5d801eec }, { 0x0000a5fc , 0x74826e92 , 0x74826e92 , 0x5d801eec , 0x5d801eec }, { 0x0000a600 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a604 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a608 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a60c , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a610 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a614 , 0x02004000 , 0x02004000 , 0x01404000 , 0x01404000 }, { 0x0000a618 , 0x02004801 , 0x02004801 , 0x01404501 , 0x01404501 }, { 0x0000a61c , 0x02808a02 , 0x02808a02 , 0x02008501 , 0x02008501 }, { 0x0000a620 , 0x0380ce03 , 0x0380ce03 , 0x0280ca03 , 0x0280ca03 }, { 0x0000a624 , 0x04411104 , 0x04411104 , 0x03010c04 , 0x03010c04 }, { 0x0000a628 , 0x04411104 , 0x04411104 , 0x04014c04 , 0x04014c04 }, { 0x0000a62c , 0x04411104 , 0x04411104 , 0x04015005 , 0x04015005 }, { 0x0000a630 , 0x04411104 , 0x04411104 , 0x04015005 , 0x04015005 }, { 0x0000a634 , 0x04411104 , 0x04411104 , 0x04015005 , 0x04015005 }, { 0x0000a638 , 0x04411104 , 0x04411104 , 0x04015005 , 0x04015005 }, { 0x0000a63c , 0x04411104 , 0x04411104 , 0x04015005 , 0x04015005 }, { 0x0000b2dc , 0x00033800 , 0x00033800 , 0x03aaa352 , 0x03aaa352 }, { 0x0000b2e0 , 0x0003c000 , 0x0003c000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000b2e4 , 0x03fc0000 , 0x03fc0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000b2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000c2dc , 0x00033800 , 0x00033800 , 0x03aaa352 , 0x03aaa352 }, { 0x0000c2e0 , 0x0003c000 , 0x0003c000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000c2e4 , 0x03fc0000 , 0x03fc0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000c2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x00016044 , 0x012492d4 , 0x012492d4 , 0x012492d4 , 0x012492d4 }, { 0x00016048 , 0x62480001 , 0x62480001 , 0x62480001 , 0x62480001 }, { 0x00016068 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, { 0x00016444 , 0x012492d4 , 0x012492d4 , 0x012492d4 , 0x012492d4 }, { 0x00016448 , 0x62480001 , 0x62480001 , 0x62480001 , 0x62480001 }, { 0x00016468 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, { 0x00016844 , 0x012492d4 , 0x012492d4 , 0x012492d4 , 0x012492d4 }, { 0x00016848 , 0x62480001 , 0x62480001 , 0x62480001 , 0x62480001 }, { 0x00016868 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, }; static const u_int32_t ar9300Modes_mixed_ob_db_tx_gain_table_osprey_2p2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x0000a2dc , 0x00033800 , 0x00033800 , 0x03aaa352 , 0x03aaa352 }, { 0x0000a2e0 , 0x0003c000 , 0x0003c000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000a2e4 , 0x03fc0000 , 0x03fc0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000a2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000a410 , 0x000050d9 , 0x000050d9 , 0x000050d9 , 0x000050d9 }, { 0x0000a500 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a504 , 0x06000003 , 0x06000003 , 0x04000002 , 0x04000002 }, { 0x0000a508 , 0x0a000020 , 0x0a000020 , 0x08000004 , 0x08000004 }, { 0x0000a50c , 0x10000023 , 0x10000023 , 0x0b000200 , 0x0b000200 }, { 0x0000a510 , 0x16000220 , 0x16000220 , 0x0f000202 , 0x0f000202 }, { 0x0000a514 , 0x1c000223 , 0x1c000223 , 0x11000400 , 0x11000400 }, { 0x0000a518 , 0x21002220 , 0x21002220 , 0x15000402 , 0x15000402 }, { 0x0000a51c , 0x27002223 , 0x27002223 , 0x19000404 , 0x19000404 }, { 0x0000a520 , 0x2b022220 , 0x2b022220 , 0x1b000603 , 0x1b000603 }, { 0x0000a524 , 0x2f022222 , 0x2f022222 , 0x1f000a02 , 0x1f000a02 }, { 0x0000a528 , 0x34022225 , 0x34022225 , 0x23000a04 , 0x23000a04 }, { 0x0000a52c , 0x3a02222a , 0x3a02222a , 0x26000a20 , 0x26000a20 }, { 0x0000a530 , 0x3e02222c , 0x3e02222c , 0x2a000e20 , 0x2a000e20 }, { 0x0000a534 , 0x4202242a , 0x4202242a , 0x2e000e22 , 0x2e000e22 }, { 0x0000a538 , 0x4702244a , 0x4702244a , 0x31000e24 , 0x31000e24 }, { 0x0000a53c , 0x4b02244c , 0x4b02244c , 0x34001640 , 0x34001640 }, { 0x0000a540 , 0x4e02246c , 0x4e02246c , 0x38001660 , 0x38001660 }, { 0x0000a544 , 0x52022470 , 0x52022470 , 0x3b001861 , 0x3b001861 }, { 0x0000a548 , 0x55022490 , 0x55022490 , 0x3e001a81 , 0x3e001a81 }, { 0x0000a54c , 0x59022492 , 0x59022492 , 0x42001a83 , 0x42001a83 }, { 0x0000a550 , 0x5d022692 , 0x5d022692 , 0x44001c84 , 0x44001c84 }, { 0x0000a554 , 0x61022892 , 0x61022892 , 0x48001ce3 , 0x48001ce3 }, { 0x0000a558 , 0x65024890 , 0x65024890 , 0x4c001ce5 , 0x4c001ce5 }, { 0x0000a55c , 0x69024892 , 0x69024892 , 0x50001ce9 , 0x50001ce9 }, { 0x0000a560 , 0x6e024c92 , 0x6e024c92 , 0x54001ceb , 0x54001ceb }, { 0x0000a564 , 0x74026e92 , 0x74026e92 , 0x56001eec , 0x56001eec }, { 0x0000a568 , 0x74026e92 , 0x74026e92 , 0x56001eec , 0x56001eec }, { 0x0000a56c , 0x74026e92 , 0x74026e92 , 0x56001eec , 0x56001eec }, { 0x0000a570 , 0x74026e92 , 0x74026e92 , 0x56001eec , 0x56001eec }, { 0x0000a574 , 0x74026e92 , 0x74026e92 , 0x56001eec , 0x56001eec }, { 0x0000a578 , 0x74026e92 , 0x74026e92 , 0x56001eec , 0x56001eec }, { 0x0000a57c , 0x74026e92 , 0x74026e92 , 0x56001eec , 0x56001eec }, { 0x0000a580 , 0x00800000 , 0x00800000 , 0x00800000 , 0x00800000 }, { 0x0000a584 , 0x06800003 , 0x06800003 , 0x04800002 , 0x04800002 }, { 0x0000a588 , 0x0a800020 , 0x0a800020 , 0x08800004 , 0x08800004 }, { 0x0000a58c , 0x10800023 , 0x10800023 , 0x0b800200 , 0x0b800200 }, { 0x0000a590 , 0x16800220 , 0x16800220 , 0x0f800202 , 0x0f800202 }, { 0x0000a594 , 0x1c800223 , 0x1c800223 , 0x11800400 , 0x11800400 }, { 0x0000a598 , 0x21802220 , 0x21802220 , 0x15800402 , 0x15800402 }, { 0x0000a59c , 0x27802223 , 0x27802223 , 0x19800404 , 0x19800404 }, { 0x0000a5a0 , 0x2b822220 , 0x2b822220 , 0x1b800603 , 0x1b800603 }, { 0x0000a5a4 , 0x2f822222 , 0x2f822222 , 0x1f800a02 , 0x1f800a02 }, { 0x0000a5a8 , 0x34822225 , 0x34822225 , 0x23800a04 , 0x23800a04 }, { 0x0000a5ac , 0x3a82222a , 0x3a82222a , 0x26800a20 , 0x26800a20 }, { 0x0000a5b0 , 0x3e82222c , 0x3e82222c , 0x2a800e20 , 0x2a800e20 }, { 0x0000a5b4 , 0x4282242a , 0x4282242a , 0x2e800e22 , 0x2e800e22 }, { 0x0000a5b8 , 0x4782244a , 0x4782244a , 0x31800e24 , 0x31800e24 }, { 0x0000a5bc , 0x4b82244c , 0x4b82244c , 0x34801640 , 0x34801640 }, { 0x0000a5c0 , 0x4e82246c , 0x4e82246c , 0x38801660 , 0x38801660 }, { 0x0000a5c4 , 0x52822470 , 0x52822470 , 0x3b801861 , 0x3b801861 }, { 0x0000a5c8 , 0x55822490 , 0x55822490 , 0x3e801a81 , 0x3e801a81 }, { 0x0000a5cc , 0x59822492 , 0x59822492 , 0x42801a83 , 0x42801a83 }, { 0x0000a5d0 , 0x5d822692 , 0x5d822692 , 0x44801c84 , 0x44801c84 }, { 0x0000a5d4 , 0x61822892 , 0x61822892 , 0x48801ce3 , 0x48801ce3 }, { 0x0000a5d8 , 0x65824890 , 0x65824890 , 0x4c801ce5 , 0x4c801ce5 }, { 0x0000a5dc , 0x69824892 , 0x69824892 , 0x50801ce9 , 0x50801ce9 }, { 0x0000a5e0 , 0x6e824c92 , 0x6e824c92 , 0x54801ceb , 0x54801ceb }, { 0x0000a5e4 , 0x74826e92 , 0x74826e92 , 0x56801eec , 0x56801eec }, { 0x0000a5e8 , 0x74826e92 , 0x74826e92 , 0x56801eec , 0x56801eec }, { 0x0000a5ec , 0x74826e92 , 0x74826e92 , 0x56801eec , 0x56801eec }, { 0x0000a5f0 , 0x74826e92 , 0x74826e92 , 0x56801eec , 0x56801eec }, { 0x0000a5f4 , 0x74826e92 , 0x74826e92 , 0x56801eec , 0x56801eec }, { 0x0000a5f8 , 0x74826e92 , 0x74826e92 , 0x56801eec , 0x56801eec }, { 0x0000a5fc , 0x74826e92 , 0x74826e92 , 0x56801eec , 0x56801eec }, { 0x0000a600 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a604 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a608 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a60c , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a610 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a614 , 0x02004000 , 0x02004000 , 0x01404000 , 0x01404000 }, { 0x0000a618 , 0x02004801 , 0x02004801 , 0x01404501 , 0x01404501 }, { 0x0000a61c , 0x02808a02 , 0x02808a02 , 0x02008501 , 0x02008501 }, { 0x0000a620 , 0x0380ce03 , 0x0380ce03 , 0x0280ca03 , 0x0280ca03 }, { 0x0000a624 , 0x04411104 , 0x04411104 , 0x03010c04 , 0x03010c04 }, { 0x0000a628 , 0x04411104 , 0x04411104 , 0x04014c04 , 0x04014c04 }, { 0x0000a62c , 0x04411104 , 0x04411104 , 0x04015005 , 0x04015005 }, { 0x0000a630 , 0x04411104 , 0x04411104 , 0x04015005 , 0x04015005 }, { 0x0000a634 , 0x04411104 , 0x04411104 , 0x04015005 , 0x04015005 }, { 0x0000a638 , 0x04411104 , 0x04411104 , 0x04015005 , 0x04015005 }, { 0x0000a63c , 0x04411104 , 0x04411104 , 0x04015005 , 0x04015005 }, { 0x0000b2dc , 0x00033800 , 0x00033800 , 0x03aaa352 , 0x03aaa352 }, { 0x0000b2e0 , 0x0003c000 , 0x0003c000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000b2e4 , 0x03fc0000 , 0x03fc0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000b2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000c2dc , 0x00033800 , 0x00033800 , 0x03aaa352 , 0x03aaa352 }, { 0x0000c2e0 , 0x0003c000 , 0x0003c000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000c2e4 , 0x03fc0000 , 0x03fc0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000c2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x00016044 , 0x012492d4 , 0x012492d4 , 0x056db2e4 , 0x056db2e4 }, { 0x00016048 , 0x66480001 , 0x66480001 , 0x8e480001 , 0x8e480001 }, { 0x00016068 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, { 0x00016444 , 0x012492d4 , 0x012492d4 , 0x056db2e4 , 0x056db2e4 }, { 0x00016448 , 0x66480001 , 0x66480001 , 0x8e480001 , 0x8e480001 }, { 0x00016468 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, { 0x00016844 , 0x012492d4 , 0x012492d4 , 0x056db2e4 , 0x056db2e4 }, { 0x00016848 , 0x66480001 , 0x66480001 , 0x8e480001 , 0x8e480001 }, { 0x00016868 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, }; static const u_int32_t ar9300_osprey_2p2_baseband_core_txfir_coeff_japan_2484[][2] = { /* Addr allmodes */ { 0x0000a398 , 0x00000000 }, { 0x0000a39c , 0x6f7f0301 }, { 0x0000a3a0 , 0xca9228ee }, }; static const u_int32_t ar9300_osprey_2p2_mac_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x00001030 , 0x00000230 , 0x00000460 , 0x000002c0 , 0x00000160 }, { 0x00001070 , 0x00000168 , 0x000002d0 , 0x00000318 , 0x0000018c }, { 0x000010b0 , 0x00000e60 , 0x00001cc0 , 0x00007c70 , 0x00003e38 }, { 0x00008014 , 0x03e803e8 , 0x07d007d0 , 0x10801600 , 0x08400b00 }, { 0x0000801c , 0x128d8027 , 0x128d804f , 0x12e00057 , 0x12e0002b }, - { 0x00008120 , 0x08f04800 , 0x08f04800 , 0x08f04810 , 0x08f04810 }, + { 0x00008120 , 0x18f04800 , 0x18f04800 , 0x18f04810 , 0x18f04810 }, { 0x000081d0 , 0x00003210 , 0x00003210 , 0x0000320a , 0x0000320a }, { 0x00008318 , 0x00003e80 , 0x00007d00 , 0x00006880 , 0x00003440 }, }; static const u_int32_t ar9300Modes_high_power_tx_gain_table_osprey_2p2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x0000a2dc , 0x000cfff0 , 0x000cfff0 , 0x03aaa352 , 0x03aaa352 }, { 0x0000a2e0 , 0x000f0000 , 0x000f0000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000a2e4 , 0x03f00000 , 0x03f00000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000a2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000a410 , 0x000050d9 , 0x000050d9 , 0x000050d9 , 0x000050d9 }, { 0x0000a500 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a504 , 0x06000003 , 0x06000003 , 0x04000002 , 0x04000002 }, { 0x0000a508 , 0x0a000020 , 0x0a000020 , 0x08000004 , 0x08000004 }, { 0x0000a50c , 0x10000023 , 0x10000023 , 0x0b000200 , 0x0b000200 }, { 0x0000a510 , 0x15000028 , 0x15000028 , 0x0f000202 , 0x0f000202 }, { 0x0000a514 , 0x1b00002b , 0x1b00002b , 0x12000400 , 0x12000400 }, { 0x0000a518 , 0x1f020028 , 0x1f020028 , 0x16000402 , 0x16000402 }, { 0x0000a51c , 0x2502002b , 0x2502002b , 0x19000404 , 0x19000404 }, { 0x0000a520 , 0x2a04002a , 0x2a04002a , 0x1c000603 , 0x1c000603 }, { 0x0000a524 , 0x2e06002a , 0x2e06002a , 0x21000a02 , 0x21000a02 }, { 0x0000a528 , 0x3302202d , 0x3302202d , 0x25000a04 , 0x25000a04 }, { 0x0000a52c , 0x3804202c , 0x3804202c , 0x28000a20 , 0x28000a20 }, { 0x0000a530 , 0x3c06202c , 0x3c06202c , 0x2c000e20 , 0x2c000e20 }, { 0x0000a534 , 0x4108202d , 0x4108202d , 0x30000e22 , 0x30000e22 }, { 0x0000a538 , 0x4506402d , 0x4506402d , 0x34000e24 , 0x34000e24 }, { 0x0000a53c , 0x4906222d , 0x4906222d , 0x38001640 , 0x38001640 }, { 0x0000a540 , 0x4d062231 , 0x4d062231 , 0x3c001660 , 0x3c001660 }, { 0x0000a544 , 0x50082231 , 0x50082231 , 0x3f001861 , 0x3f001861 }, { 0x0000a548 , 0x5608422e , 0x5608422e , 0x43001a81 , 0x43001a81 }, { 0x0000a54c , 0x5e08442e , 0x5e08442e , 0x47001a83 , 0x47001a83 }, { 0x0000a550 , 0x620a4431 , 0x620a4431 , 0x4a001c84 , 0x4a001c84 }, { 0x0000a554 , 0x640a4432 , 0x640a4432 , 0x4e001ce3 , 0x4e001ce3 }, { 0x0000a558 , 0x680a4434 , 0x680a4434 , 0x52001ce5 , 0x52001ce5 }, { 0x0000a55c , 0x6c0a6434 , 0x6c0a6434 , 0x56001ce9 , 0x56001ce9 }, { 0x0000a560 , 0x6f0a6633 , 0x6f0a6633 , 0x5a001ceb , 0x5a001ceb }, { 0x0000a564 , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a568 , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a56c , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a570 , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a574 , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a578 , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a57c , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a580 , 0x00800000 , 0x00800000 , 0x00800000 , 0x00800000 }, { 0x0000a584 , 0x06800003 , 0x06800003 , 0x04800002 , 0x04800002 }, { 0x0000a588 , 0x0a800020 , 0x0a800020 , 0x08800004 , 0x08800004 }, { 0x0000a58c , 0x10800023 , 0x10800023 , 0x0b800200 , 0x0b800200 }, { 0x0000a590 , 0x15800028 , 0x15800028 , 0x0f800202 , 0x0f800202 }, { 0x0000a594 , 0x1b80002b , 0x1b80002b , 0x12800400 , 0x12800400 }, { 0x0000a598 , 0x1f820028 , 0x1f820028 , 0x16800402 , 0x16800402 }, { 0x0000a59c , 0x2582002b , 0x2582002b , 0x19800404 , 0x19800404 }, { 0x0000a5a0 , 0x2a84002a , 0x2a84002a , 0x1c800603 , 0x1c800603 }, { 0x0000a5a4 , 0x2e86002a , 0x2e86002a , 0x21800a02 , 0x21800a02 }, { 0x0000a5a8 , 0x3382202d , 0x3382202d , 0x25800a04 , 0x25800a04 }, { 0x0000a5ac , 0x3884202c , 0x3884202c , 0x28800a20 , 0x28800a20 }, { 0x0000a5b0 , 0x3c86202c , 0x3c86202c , 0x2c800e20 , 0x2c800e20 }, { 0x0000a5b4 , 0x4188202d , 0x4188202d , 0x30800e22 , 0x30800e22 }, { 0x0000a5b8 , 0x4586402d , 0x4586402d , 0x34800e24 , 0x34800e24 }, { 0x0000a5bc , 0x4986222d , 0x4986222d , 0x38801640 , 0x38801640 }, { 0x0000a5c0 , 0x4d862231 , 0x4d862231 , 0x3c801660 , 0x3c801660 }, { 0x0000a5c4 , 0x50882231 , 0x50882231 , 0x3f801861 , 0x3f801861 }, { 0x0000a5c8 , 0x5688422e , 0x5688422e , 0x43801a81 , 0x43801a81 }, { 0x0000a5cc , 0x5e88442e , 0x5e88442e , 0x47801a83 , 0x47801a83 }, { 0x0000a5d0 , 0x628a4431 , 0x628a4431 , 0x4a801c84 , 0x4a801c84 }, { 0x0000a5d4 , 0x648a4432 , 0x648a4432 , 0x4e801ce3 , 0x4e801ce3 }, { 0x0000a5d8 , 0x688a4434 , 0x688a4434 , 0x52801ce5 , 0x52801ce5 }, { 0x0000a5dc , 0x6c8a6434 , 0x6c8a6434 , 0x56801ce9 , 0x56801ce9 }, { 0x0000a5e0 , 0x6f8a6633 , 0x6f8a6633 , 0x5a801ceb , 0x5a801ceb }, { 0x0000a5e4 , 0x738c6634 , 0x738c6634 , 0x5d801eec , 0x5d801eec }, { 0x0000a5e8 , 0x738c6634 , 0x738c6634 , 0x5d801eec , 0x5d801eec }, { 0x0000a5ec , 0x738c6634 , 0x738c6634 , 0x5d801eec , 0x5d801eec }, { 0x0000a5f0 , 0x738c6634 , 0x738c6634 , 0x5d801eec , 0x5d801eec }, { 0x0000a5f4 , 0x738c6634 , 0x738c6634 , 0x5d801eec , 0x5d801eec }, { 0x0000a5f8 , 0x738c6634 , 0x738c6634 , 0x5d801eec , 0x5d801eec }, { 0x0000a5fc , 0x738c6634 , 0x738c6634 , 0x5d801eec , 0x5d801eec }, { 0x0000a600 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a604 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a608 , 0x01804601 , 0x01804601 , 0x00000000 , 0x00000000 }, { 0x0000a60c , 0x01804601 , 0x01804601 , 0x00000000 , 0x00000000 }, { 0x0000a610 , 0x01804601 , 0x01804601 , 0x00000000 , 0x00000000 }, { 0x0000a614 , 0x01804601 , 0x01804601 , 0x01404000 , 0x01404000 }, { 0x0000a618 , 0x01804601 , 0x01804601 , 0x01404501 , 0x01404501 }, { 0x0000a61c , 0x01804601 , 0x01804601 , 0x02008501 , 0x02008501 }, { 0x0000a620 , 0x03408d02 , 0x03408d02 , 0x0280ca03 , 0x0280ca03 }, { 0x0000a624 , 0x0300cc03 , 0x0300cc03 , 0x03010c04 , 0x03010c04 }, { 0x0000a628 , 0x03410d04 , 0x03410d04 , 0x04014c04 , 0x04014c04 }, { 0x0000a62c , 0x03410d04 , 0x03410d04 , 0x04015005 , 0x04015005 }, { 0x0000a630 , 0x03410d04 , 0x03410d04 , 0x04015005 , 0x04015005 }, { 0x0000a634 , 0x03410d04 , 0x03410d04 , 0x04015005 , 0x04015005 }, { 0x0000a638 , 0x03410d04 , 0x03410d04 , 0x04015005 , 0x04015005 }, { 0x0000a63c , 0x03410d04 , 0x03410d04 , 0x04015005 , 0x04015005 }, { 0x0000b2dc , 0x000cfff0 , 0x000cfff0 , 0x03aaa352 , 0x03aaa352 }, { 0x0000b2e0 , 0x000f0000 , 0x000f0000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000b2e4 , 0x03f00000 , 0x03f00000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000b2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000c2dc , 0x000cfff0 , 0x000cfff0 , 0x03aaa352 , 0x03aaa352 }, { 0x0000c2e0 , 0x000f0000 , 0x000f0000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000c2e4 , 0x03f00000 , 0x03f00000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000c2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x00016044 , 0x012492d4 , 0x012492d4 , 0x012492d4 , 0x012492d4 }, { 0x00016048 , 0x61200001 , 0x61200001 , 0x66480001 , 0x66480001 }, { 0x00016068 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, { 0x00016444 , 0x012492d4 , 0x012492d4 , 0x012492d4 , 0x012492d4 }, { 0x00016448 , 0x61200001 , 0x61200001 , 0x66480001 , 0x66480001 }, { 0x00016468 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, { 0x00016844 , 0x012492d4 , 0x012492d4 , 0x012492d4 , 0x012492d4 }, { 0x00016848 , 0x61200001 , 0x61200001 , 0x66480001 , 0x66480001 }, { 0x00016868 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, }; static const u_int32_t ar9200_merlin_2p2_radio_core[][2] = { /* Addr common */ { 0x00007800 , 0x00040000 }, { 0x00007804 , 0xdb005012 }, { 0x00007808 , 0x04924914 }, { 0x0000780c , 0x21084210 }, { 0x00007810 , 0x6d801300 }, { 0x00007814 , 0x0019beff }, { 0x00007818 , 0x07e41000 }, { 0x0000781c , 0x00392000 }, { 0x00007820 , 0x92592480 }, { 0x00007824 , 0x00040000 }, { 0x00007828 , 0xdb005012 }, { 0x0000782c , 0x04924914 }, { 0x00007830 , 0x21084210 }, { 0x00007834 , 0x6d801300 }, { 0x00007838 , 0x0019beff }, { 0x0000783c , 0x07e40000 }, { 0x00007840 , 0x00392000 }, { 0x00007844 , 0x92592480 }, { 0x00007848 , 0x00100000 }, { 0x0000784c , 0x773f0567 }, { 0x00007850 , 0x54214514 }, { 0x00007854 , 0x12035828 }, { 0x00007858 , 0x92592692 }, { 0x0000785c , 0x00000000 }, { 0x00007860 , 0x56400000 }, { 0x00007864 , 0x0a8e370e }, { 0x00007868 , 0xc0102850 }, { 0x0000786c , 0x812d4000 }, { 0x00007870 , 0x807ec400 }, { 0x00007874 , 0x001b6db0 }, { 0x00007878 , 0x00376b63 }, { 0x0000787c , 0x06db6db6 }, { 0x00007880 , 0x006d8000 }, { 0x00007884 , 0xffeffffe }, { 0x00007888 , 0xffeffffe }, { 0x0000788c , 0x00010000 }, { 0x00007890 , 0x02060aeb }, { 0x00007894 , 0x5a108000 }, }; static const u_int32_t ar9300_osprey_2p2_baseband_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x00009810 , 0xd00a8005 , 0xd00a8005 , 0xd00a8011 , 0xd00a8011 }, { 0x00009820 , 0x206a022e , 0x206a022e , 0x206a012e , 0x206a012e }, { 0x00009824 , 0x5ac640d0 , 0x5ac640d0 , 0x5ac640d0 , 0x5ac640d0 }, { 0x00009828 , 0x06903081 , 0x06903081 , 0x06903881 , 0x06903881 }, { 0x0000982c , 0x05eea6d4 , 0x05eea6d4 , 0x05eea6d4 , 0x05eea6d4 }, { 0x00009830 , 0x0000059c , 0x0000059c , 0x0000119c , 0x0000119c }, { 0x00009c00 , 0x000000c4 , 0x000000c4 , 0x000000c4 , 0x000000c4 }, { 0x00009e00 , 0x0372111a , 0x0372111a , 0x037216a0 , 0x037216a0 }, { 0x00009e04 , 0x001c2020 , 0x001c2020 , 0x001c2020 , 0x001c2020 }, { 0x00009e0c , 0x6c4000e2 , 0x6d4000e2 , 0x6d4000e2 , 0x6c4000e2 }, { 0x00009e10 , 0x7ec88d2e , 0x7ec88d2e , 0x7ec84d2e , 0x7ec84d2e }, { 0x00009e14 , 0x37b95d5e , 0x37b9605e , 0x3379605e , 0x33795d5e }, { 0x00009e18 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x00009e1c , 0x0001cf9c , 0x0001cf9c , 0x00021f9c , 0x00021f9c }, { 0x00009e20 , 0x000003b5 , 0x000003b5 , 0x000003ce , 0x000003ce }, { 0x00009e2c , 0x0000001c , 0x0000001c , 0x00000021 , 0x00000021 }, { 0x00009e3c , 0xcf946220 , 0xcf946220 , 0xcf946222 , 0xcf946222 }, { 0x00009e44 , 0x02321e27 , 0x02321e27 , 0x02291e27 , 0x02291e27 }, { 0x00009e48 , 0x5030201a , 0x5030201a , 0x50302012 , 0x50302012 }, { 0x00009fc8 , 0x0003f000 , 0x0003f000 , 0x0001a000 , 0x0001a000 }, { 0x0000a204 , 0x000036c0 , 0x000036c4 , 0x000036c4 , 0x000036c0 }, { 0x0000a208 , 0x00000104 , 0x00000104 , 0x00000004 , 0x00000004 }, { 0x0000a22c , 0x01026a2f , 0x01026a2f , 0x01026a2f , 0x01026a2f }, { 0x0000a230 , 0x0000000a , 0x00000014 , 0x00000016 , 0x0000000b }, { 0x0000a234 , 0x00000fff , 0x10000fff , 0x10000fff , 0x00000fff }, { 0x0000a238 , 0xffb81018 , 0xffb81018 , 0xffb81018 , 0xffb81018 }, { 0x0000a250 , 0x00000000 , 0x00000000 , 0x00000210 , 0x00000108 }, { 0x0000a254 , 0x000007d0 , 0x00000fa0 , 0x00001130 , 0x00000898 }, { 0x0000a258 , 0x02020002 , 0x02020002 , 0x02020002 , 0x02020002 }, { 0x0000a25c , 0x01000e0e , 0x01000e0e , 0x01000e0e , 0x01000e0e }, { 0x0000a260 , 0x0a021501 , 0x0a021501 , 0x3a021501 , 0x3a021501 }, { 0x0000a264 , 0x00000e0e , 0x00000e0e , 0x00000e0e , 0x00000e0e }, { 0x0000a280 , 0x00000007 , 0x00000007 , 0x0000000b , 0x0000000b }, { 0x0000a284 , 0x00000000 , 0x00000000 , 0x00000150 , 0x00000150 }, { 0x0000a288 , 0x00000110 , 0x00000110 , 0x00000110 , 0x00000110 }, { 0x0000a28c , 0x00022222 , 0x00022222 , 0x00022222 , 0x00022222 }, { 0x0000a2c4 , 0x00158d18 , 0x00158d18 , 0x00158d18 , 0x00158d18 }, { 0x0000a2d0 , 0x00041983 , 0x00041983 , 0x00041981 , 0x00041982 }, { 0x0000a2d8 , 0x7999a83b , 0x7999a83b , 0x7999a83b , 0x7999a83b }, { 0x0000a358 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a830 , 0x0000019c , 0x0000019c , 0x0000019c , 0x0000019c }, { 0x0000ae04 , 0x001c0000 , 0x001c0000 , 0x001c0000 , 0x001c0000 }, { 0x0000ae18 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000ae1c , 0x0000019c , 0x0000019c , 0x0000019c , 0x0000019c }, { 0x0000ae20 , 0x000001b5 , 0x000001b5 , 0x000001ce , 0x000001ce }, { 0x0000b284 , 0x00000000 , 0x00000000 , 0x00000150 , 0x00000150 }, { 0x0000b830 , 0x0000019c , 0x0000019c , 0x0000019c , 0x0000019c }, { 0x0000be04 , 0x001c0000 , 0x001c0000 , 0x001c0000 , 0x001c0000 }, { 0x0000be18 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000be1c , 0x0000019c , 0x0000019c , 0x0000019c , 0x0000019c }, { 0x0000be20 , 0x000001b5 , 0x000001b5 , 0x000001ce , 0x000001ce }, { 0x0000c284 , 0x00000000 , 0x00000000 , 0x00000150 , 0x00000150 }, }; static const u_int32_t ar9300Common_wo_xlna_rx_gain_table_osprey_2p2[][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 u_int32_t ar9300_osprey_2p2_soc_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x00007010 , 0x00000023 , 0x00000023 , 0x00000023 , 0x00000023 }, }; static const u_int32_t ar9300Common_rx_gain_table_merlin_2p2[][2] = { /* Addr allmodes */ { 0x0000a000 , 0x02000101 }, { 0x0000a004 , 0x02000102 }, { 0x0000a008 , 0x02000103 }, { 0x0000a00c , 0x02000104 }, { 0x0000a010 , 0x02000200 }, { 0x0000a014 , 0x02000201 }, { 0x0000a018 , 0x02000202 }, { 0x0000a01c , 0x02000203 }, { 0x0000a020 , 0x02000204 }, { 0x0000a024 , 0x02000205 }, { 0x0000a028 , 0x02000208 }, { 0x0000a02c , 0x02000302 }, { 0x0000a030 , 0x02000303 }, { 0x0000a034 , 0x02000304 }, { 0x0000a038 , 0x02000400 }, { 0x0000a03c , 0x02010300 }, { 0x0000a040 , 0x02010301 }, { 0x0000a044 , 0x02010302 }, { 0x0000a048 , 0x02000500 }, { 0x0000a04c , 0x02010400 }, { 0x0000a050 , 0x02020300 }, { 0x0000a054 , 0x02020301 }, { 0x0000a058 , 0x02020302 }, { 0x0000a05c , 0x02020303 }, { 0x0000a060 , 0x02020400 }, { 0x0000a064 , 0x02030300 }, { 0x0000a068 , 0x02030301 }, { 0x0000a06c , 0x02030302 }, { 0x0000a070 , 0x02030303 }, { 0x0000a074 , 0x02030400 }, { 0x0000a078 , 0x02040300 }, { 0x0000a07c , 0x02040301 }, { 0x0000a080 , 0x02040302 }, { 0x0000a084 , 0x02040303 }, { 0x0000a088 , 0x02030500 }, { 0x0000a08c , 0x02040400 }, { 0x0000a090 , 0x02050203 }, { 0x0000a094 , 0x02050204 }, { 0x0000a098 , 0x02050205 }, { 0x0000a09c , 0x02040500 }, { 0x0000a0a0 , 0x02050301 }, { 0x0000a0a4 , 0x02050302 }, { 0x0000a0a8 , 0x02050303 }, { 0x0000a0ac , 0x02050400 }, { 0x0000a0b0 , 0x02050401 }, { 0x0000a0b4 , 0x02050402 }, { 0x0000a0b8 , 0x02050403 }, { 0x0000a0bc , 0x02050500 }, { 0x0000a0c0 , 0x02050501 }, { 0x0000a0c4 , 0x02050502 }, { 0x0000a0c8 , 0x02050503 }, { 0x0000a0cc , 0x02050504 }, { 0x0000a0d0 , 0x02050600 }, { 0x0000a0d4 , 0x02050601 }, { 0x0000a0d8 , 0x02050602 }, { 0x0000a0dc , 0x02050603 }, { 0x0000a0e0 , 0x02050604 }, { 0x0000a0e4 , 0x02050700 }, { 0x0000a0e8 , 0x02050701 }, { 0x0000a0ec , 0x02050702 }, { 0x0000a0f0 , 0x02050703 }, { 0x0000a0f4 , 0x02050704 }, { 0x0000a0f8 , 0x02050705 }, { 0x0000a0fc , 0x02050708 }, { 0x0000a100 , 0x02050709 }, { 0x0000a104 , 0x0205070a }, { 0x0000a108 , 0x0205070b }, { 0x0000a10c , 0x0205070c }, { 0x0000a110 , 0x0205070d }, { 0x0000a114 , 0x02050710 }, { 0x0000a118 , 0x02050711 }, { 0x0000a11c , 0x02050712 }, { 0x0000a120 , 0x02050713 }, { 0x0000a124 , 0x02050714 }, { 0x0000a128 , 0x02050715 }, { 0x0000a12c , 0x02050730 }, { 0x0000a130 , 0x02050731 }, { 0x0000a134 , 0x02050732 }, { 0x0000a138 , 0x02050733 }, { 0x0000a13c , 0x02050734 }, { 0x0000a140 , 0x02050735 }, { 0x0000a144 , 0x02050750 }, { 0x0000a148 , 0x02050751 }, { 0x0000a14c , 0x02050752 }, { 0x0000a150 , 0x02050753 }, { 0x0000a154 , 0x02050754 }, { 0x0000a158 , 0x02050755 }, { 0x0000a15c , 0x02050770 }, { 0x0000a160 , 0x02050771 }, { 0x0000a164 , 0x02050772 }, { 0x0000a168 , 0x02050773 }, { 0x0000a16c , 0x02050774 }, { 0x0000a170 , 0x02050775 }, { 0x0000a174 , 0x00000776 }, { 0x0000a178 , 0x00000776 }, { 0x0000a17c , 0x00000776 }, { 0x0000a180 , 0x00000776 }, { 0x0000a184 , 0x00000776 }, { 0x0000a188 , 0x00000776 }, { 0x0000a18c , 0x00000776 }, { 0x0000a190 , 0x00000776 }, { 0x0000a194 , 0x00000776 }, { 0x0000a198 , 0x00000776 }, { 0x0000a19c , 0x00000776 }, { 0x0000a1a0 , 0x00000776 }, { 0x0000a1a4 , 0x00000776 }, { 0x0000a1a8 , 0x00000776 }, { 0x0000a1ac , 0x00000776 }, { 0x0000a1b0 , 0x00000776 }, { 0x0000a1b4 , 0x00000776 }, { 0x0000a1b8 , 0x00000776 }, { 0x0000a1bc , 0x00000776 }, { 0x0000a1c0 , 0x00000776 }, { 0x0000a1c4 , 0x00000776 }, { 0x0000a1c8 , 0x00000776 }, { 0x0000a1cc , 0x00000776 }, { 0x0000a1d0 , 0x00000776 }, { 0x0000a1d4 , 0x00000776 }, { 0x0000a1d8 , 0x00000776 }, { 0x0000a1dc , 0x00000776 }, { 0x0000a1e0 , 0x00000776 }, { 0x0000a1e4 , 0x00000776 }, { 0x0000a1e8 , 0x00000776 }, { 0x0000a1ec , 0x00000776 }, { 0x0000a1f0 , 0x00000776 }, { 0x0000a1f4 , 0x00000776 }, { 0x0000a1f8 , 0x00000776 }, { 0x0000a1fc , 0x00000776 }, { 0x0000b000 , 0x02000101 }, { 0x0000b004 , 0x02000102 }, { 0x0000b008 , 0x02000103 }, { 0x0000b00c , 0x02000104 }, { 0x0000b010 , 0x02000200 }, { 0x0000b014 , 0x02000201 }, { 0x0000b018 , 0x02000202 }, { 0x0000b01c , 0x02000203 }, { 0x0000b020 , 0x02000204 }, { 0x0000b024 , 0x02000205 }, { 0x0000b028 , 0x02000208 }, { 0x0000b02c , 0x02000302 }, { 0x0000b030 , 0x02000303 }, { 0x0000b034 , 0x02000304 }, { 0x0000b038 , 0x02000400 }, { 0x0000b03c , 0x02010300 }, { 0x0000b040 , 0x02010301 }, { 0x0000b044 , 0x02010302 }, { 0x0000b048 , 0x02000500 }, { 0x0000b04c , 0x02010400 }, { 0x0000b050 , 0x02020300 }, { 0x0000b054 , 0x02020301 }, { 0x0000b058 , 0x02020302 }, { 0x0000b05c , 0x02020303 }, { 0x0000b060 , 0x02020400 }, { 0x0000b064 , 0x02030300 }, { 0x0000b068 , 0x02030301 }, { 0x0000b06c , 0x02030302 }, { 0x0000b070 , 0x02030303 }, { 0x0000b074 , 0x02030400 }, { 0x0000b078 , 0x02040300 }, { 0x0000b07c , 0x02040301 }, { 0x0000b080 , 0x02040302 }, { 0x0000b084 , 0x02040303 }, { 0x0000b088 , 0x02030500 }, { 0x0000b08c , 0x02040400 }, { 0x0000b090 , 0x02050203 }, { 0x0000b094 , 0x02050204 }, { 0x0000b098 , 0x02050205 }, { 0x0000b09c , 0x02040500 }, { 0x0000b0a0 , 0x02050301 }, { 0x0000b0a4 , 0x02050302 }, { 0x0000b0a8 , 0x02050303 }, { 0x0000b0ac , 0x02050400 }, { 0x0000b0b0 , 0x02050401 }, { 0x0000b0b4 , 0x02050402 }, { 0x0000b0b8 , 0x02050403 }, { 0x0000b0bc , 0x02050500 }, { 0x0000b0c0 , 0x02050501 }, { 0x0000b0c4 , 0x02050502 }, { 0x0000b0c8 , 0x02050503 }, { 0x0000b0cc , 0x02050504 }, { 0x0000b0d0 , 0x02050600 }, { 0x0000b0d4 , 0x02050601 }, { 0x0000b0d8 , 0x02050602 }, { 0x0000b0dc , 0x02050603 }, { 0x0000b0e0 , 0x02050604 }, { 0x0000b0e4 , 0x02050700 }, { 0x0000b0e8 , 0x02050701 }, { 0x0000b0ec , 0x02050702 }, { 0x0000b0f0 , 0x02050703 }, { 0x0000b0f4 , 0x02050704 }, { 0x0000b0f8 , 0x02050705 }, { 0x0000b0fc , 0x02050708 }, { 0x0000b100 , 0x02050709 }, { 0x0000b104 , 0x0205070a }, { 0x0000b108 , 0x0205070b }, { 0x0000b10c , 0x0205070c }, { 0x0000b110 , 0x0205070d }, { 0x0000b114 , 0x02050710 }, { 0x0000b118 , 0x02050711 }, { 0x0000b11c , 0x02050712 }, { 0x0000b120 , 0x02050713 }, { 0x0000b124 , 0x02050714 }, { 0x0000b128 , 0x02050715 }, { 0x0000b12c , 0x02050730 }, { 0x0000b130 , 0x02050731 }, { 0x0000b134 , 0x02050732 }, { 0x0000b138 , 0x02050733 }, { 0x0000b13c , 0x02050734 }, { 0x0000b140 , 0x02050735 }, { 0x0000b144 , 0x02050750 }, { 0x0000b148 , 0x02050751 }, { 0x0000b14c , 0x02050752 }, { 0x0000b150 , 0x02050753 }, { 0x0000b154 , 0x02050754 }, { 0x0000b158 , 0x02050755 }, { 0x0000b15c , 0x02050770 }, { 0x0000b160 , 0x02050771 }, { 0x0000b164 , 0x02050772 }, { 0x0000b168 , 0x02050773 }, { 0x0000b16c , 0x02050774 }, { 0x0000b170 , 0x02050775 }, { 0x0000b174 , 0x00000776 }, { 0x0000b178 , 0x00000776 }, { 0x0000b17c , 0x00000776 }, { 0x0000b180 , 0x00000776 }, { 0x0000b184 , 0x00000776 }, { 0x0000b188 , 0x00000776 }, { 0x0000b18c , 0x00000776 }, { 0x0000b190 , 0x00000776 }, { 0x0000b194 , 0x00000776 }, { 0x0000b198 , 0x00000776 }, { 0x0000b19c , 0x00000776 }, { 0x0000b1a0 , 0x00000776 }, { 0x0000b1a4 , 0x00000776 }, { 0x0000b1a8 , 0x00000776 }, { 0x0000b1ac , 0x00000776 }, { 0x0000b1b0 , 0x00000776 }, { 0x0000b1b4 , 0x00000776 }, { 0x0000b1b8 , 0x00000776 }, { 0x0000b1bc , 0x00000776 }, { 0x0000b1c0 , 0x00000776 }, { 0x0000b1c4 , 0x00000776 }, { 0x0000b1c8 , 0x00000776 }, { 0x0000b1cc , 0x00000776 }, { 0x0000b1d0 , 0x00000776 }, { 0x0000b1d4 , 0x00000776 }, { 0x0000b1d8 , 0x00000776 }, { 0x0000b1dc , 0x00000776 }, { 0x0000b1e0 , 0x00000776 }, { 0x0000b1e4 , 0x00000776 }, { 0x0000b1e8 , 0x00000776 }, { 0x0000b1ec , 0x00000776 }, { 0x0000b1f0 , 0x00000776 }, { 0x0000b1f4 , 0x00000776 }, { 0x0000b1f8 , 0x00000776 }, { 0x0000b1fc , 0x00000776 }, }; static const u_int32_t ar9300_common_rx_gain_table_osprey_2p2[][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 , 0x01910190 }, { 0x0000a030 , 0x01930192 }, { 0x0000a034 , 0x01950194 }, { 0x0000a038 , 0x038a0196 }, { 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 , 0x22222229 }, { 0x0000a084 , 0x1d1d1d1d }, { 0x0000a088 , 0x1d1d1d1d }, { 0x0000a08c , 0x1d1d1d1d }, { 0x0000a090 , 0x171d1d1d }, { 0x0000a094 , 0x11111717 }, { 0x0000a098 , 0x00030311 }, { 0x0000a09c , 0x00000000 }, { 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 , 0x23232323 }, { 0x0000b084 , 0x21232323 }, { 0x0000b088 , 0x19191c1e }, { 0x0000b08c , 0x12141417 }, { 0x0000b090 , 0x07070e0e }, { 0x0000b094 , 0x03030305 }, { 0x0000b098 , 0x00000003 }, { 0x0000b09c , 0x00000000 }, { 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 }, }; /* PCIE-PHY programming array */ static const u_int32_t ar9300PciePhy_pll_on_clkreq_disable_L1_osprey_2p2[][2] = { {0x00004040, 0x0821265e}, {0x00004040, 0x0008003b}, {0x00004044, 0x00000000}, }; /* PCIE-PHY programming array */ static const u_int32_t ar9300PciePhy_clkreq_enable_L1_osprey_2p2[][2] = { {0x00004040, 0x0825365e}, {0x00004040, 0x0008003b}, {0x00004044, 0x00000000}, }; /* PCIE-PHY programming array */ static const u_int32_t ar9300PciePhy_clkreq_disable_L1_osprey_2p2[][2] = { {0x00004040, 0x0821365e}, {0x00004040, 0x0008003b}, {0x00004044, 0x00000000}, }; Index: projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_power.c =================================================================== --- projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_power.c (revision 278776) +++ projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_power.c (revision 278777) @@ -1,1565 +1,1566 @@ /* * Copyright (c) 2013 Qualcomm Atheros, Inc. * * 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 "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ar9300/ar9300.h" #include "ar9300/ar9300reg.h" #if ATH_WOW_OFFLOAD void ar9300_wowoffload_prep(struct ath_hal *ah) { struct ath_hal_9300 *ahp = AH9300(ah); ahp->ah_mcast_filter_l32_set = 0; ahp->ah_mcast_filter_u32_set = 0; } void ar9300_wowoffload_post(struct ath_hal *ah) { struct ath_hal_9300 *ahp = AH9300(ah); u_int32_t val; if (ahp->ah_mcast_filter_l32_set != 0) { val = OS_REG_READ(ah, AR_MCAST_FIL0); val &= ~ahp->ah_mcast_filter_l32_set; OS_REG_WRITE(ah, AR_MCAST_FIL0, val); } if (ahp->ah_mcast_filter_u32_set != 0) { val = OS_REG_READ(ah, AR_MCAST_FIL1); val &= ~ahp->ah_mcast_filter_u32_set; OS_REG_WRITE(ah, AR_MCAST_FIL1, val); } ahp->ah_mcast_filter_l32_set = 0; ahp->ah_mcast_filter_u32_set = 0; } static void ar9300_wowoffload_add_mcast_filter(struct ath_hal *ah, u_int8_t *mc_addr) { struct ath_hal_9300 *ahp = AH9300(ah); u_int32_t reg, val; u_int8_t pos, high32; memcpy((u_int8_t *) &val, &mc_addr[0], 3); pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; memcpy((u_int8_t *) &val, &mc_addr[3], 3); pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; high32 = pos & 0x20; reg = high32 ? AR_MCAST_FIL1 : AR_MCAST_FIL0; pos &= 0x1F; val = OS_REG_READ(ah, reg); if ((val & (1 << pos)) == 0) { val |= (1 << pos); if (high32) { ahp->ah_mcast_filter_u32_set |= (1 << pos); } else { ahp->ah_mcast_filter_l32_set |= (1 << pos); } OS_REG_WRITE(ah, reg, val); } } /* * DeviceID SWAR - EV91928 * * During SW WOW, 0x4004[13] is set to allow BT eCPU to access WLAN MAC * registers. Setting 00x4004[13] will prevent eeprom state machine to * load customizable PCIE configuration registers, which lead to the PCIE * device id stay as default 0xABCD. The SWAR to have BT eCPU to write * to PCIE registers as soon as it detects PCIE reset is deasserted. */ void ar9300_wowoffload_download_devid_swar(struct ath_hal *ah) { u_int32_t addr = AR_WOW_OFFLOAD_WLAN_REGSET_NUM; OS_REG_WRITE(ah, addr, 8); addr += 4; OS_REG_WRITE(ah, addr, 0x5000); addr += 4; HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) pcie_000 = %08x\n", AH_PRIVATE(ah)->ah_config.ath_hal_pcie_000); OS_REG_WRITE(ah, addr, AH_PRIVATE(ah)->ah_config.ath_hal_pcie_000); addr += 4; OS_REG_WRITE(ah, addr, 0x5008); addr += 4; HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) pcie_008 = %08x\n", AH_PRIVATE(ah)->ah_config.ath_hal_pcie_008); OS_REG_WRITE(ah, addr, AH_PRIVATE(ah)->ah_config.ath_hal_pcie_008); addr += 4; OS_REG_WRITE(ah, addr, 0x502c); addr += 4; HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) pcie_02c = %08x\n", AH_PRIVATE(ah)->ah_config.ath_hal_pcie_02c); OS_REG_WRITE(ah, addr, AH_PRIVATE(ah)->ah_config.ath_hal_pcie_02c); addr += 4; OS_REG_WRITE(ah, addr, 0x18c00); addr += 4; OS_REG_WRITE(ah, addr, 0x18212ede); addr += 4; OS_REG_WRITE(ah, addr, 0x18c04); addr += 4; OS_REG_WRITE(ah, addr, 0x008001d8); addr += 4; OS_REG_WRITE(ah, addr, 0x18c08); addr += 4; OS_REG_WRITE(ah, addr, 0x0003580c); addr += 4; OS_REG_WRITE(ah, addr, 0x570c); addr += 4; HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) pcie_70c = %08x\n", AH_PRIVATE(ah)->ah_config.ath_hal_pcie_70c); OS_REG_WRITE(ah, addr, AH_PRIVATE(ah)->ah_config.ath_hal_pcie_70c); addr += 4; OS_REG_WRITE(ah, addr, 0x5040); addr += 4; HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) pcie_040 = %08x\n", AH_PRIVATE(ah)->ah_config.ath_hal_pcie_040); OS_REG_WRITE(ah, addr, AH_PRIVATE(ah)->ah_config.ath_hal_pcie_040); addr += 4; /* A_SOC_REG_WRITE(0x45000, 0x0034168c); A_SOC_REG_WRITE(0x45008, 0x02800001); A_SOC_REG_WRITE(0x4502c, 0x3117168c); A_SOC_REG_WRITE(0x58c00, 0x18212ede); A_SOC_REG_WRITE(0x58c04, 0x000801d8); A_SOC_REG_WRITE(0x58c08, 0x0003580c); A_SOC_REG_WRITE(0x4570c, 0x275f3f01); A_SOC_REG_WRITE(0x45040, 0xffc25001); */ } /* Retrieve updated information from MAC PCU buffer. * Embedded CPU would have written the value before exiting WoW * */ void ar9300_wowoffload_retrieve_data(struct ath_hal *ah, void *buf, u_int32_t param) { u_int32_t rc_lower, rc_upper; if (param == WOW_PARAM_REPLAY_CNTR) { rc_lower = OS_REG_READ(ah, AR_WOW_TXBUF(0)); rc_upper = OS_REG_READ(ah, AR_WOW_TXBUF(1)); *(u_int64_t *)buf = rc_lower + (rc_upper << 32); } else if (param == WOW_PARAM_KEY_TSC) { rc_lower = OS_REG_READ(ah, AR_WOW_TXBUF(2)); rc_upper = OS_REG_READ(ah, AR_WOW_TXBUF(3)); *(u_int64_t *)buf = rc_lower + (rc_upper << 32); } else if (param == WOW_PARAM_TX_SEQNUM) { *(u_int32_t *)buf = OS_REG_READ(ah, AR_WOW_TXBUF(4)); } } /* Download GTK rekey related information to the embedded CPU */ u_int32_t ar9300_wowoffload_download_rekey_data(struct ath_hal *ah, u_int32_t *data, u_int32_t bytes) { int i; int mbox_status = OS_REG_READ(ah, AR_MBOX_CTRL_STATUS); u_int32_t gtk_data_start; HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) %s, bytes=%d\n", __func__, bytes); if (AR_SREV_JUPITER(ah) && (bytes > (AR_WOW_OFFLOAD_GTK_DATA_WORDS_JUPITER * 4))) { bytes = AR_WOW_OFFLOAD_GTK_DATA_WORDS_JUPITER * 4; HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) bytes truncated to %d\n", bytes); } /* Check if mailbox is busy */ if (mbox_status != 0) { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: Mailbox register busy! Reg = 0x%x", __func__, mbox_status); return 1; } /* Clear status */ OS_REG_WRITE(ah, AR_EMB_CPU_WOW_STATUS, 0x0); OS_REG_WRITE(ah, AR_WLAN_WOW_ENABLE, 0); OS_REG_WRITE(ah, AR_WLAN_WOW_STATUS, 0xFFFFFFFF); if (AR_SREV_JUPITER(ah)) { gtk_data_start = AR_WOW_OFFLOAD_GTK_DATA_START_JUPITER; } else { gtk_data_start = AR_WOW_OFFLOAD_GTK_DATA_START; } for (i = 0;i < bytes/4; i++) { OS_REG_WRITE(ah, gtk_data_start + i * 4, data[i]); } return 0; } void ar9300_wowoffload_download_acer_magic( struct ath_hal *ah, HAL_BOOL valid, u_int8_t* datap, u_int32_t bytes) { u_int32_t *p32 = (u_int32_t *) datap; u_int32_t l = 0, u = 0; if (valid) { l = *p32; p32++; u = *(u_int16_t *) p32; } OS_REG_WRITE(ah, AR_WOW_OFFLOAD_ACER_MAGIC_START, l); OS_REG_WRITE(ah, AR_WOW_OFFLOAD_ACER_MAGIC_START + 4, u); HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: Aer Magic: %02x-%02x-%02x-%02x-%02x-%02x\n", __func__, datap[0], datap[1], datap[2], datap[3], datap[4], datap[5]); } void ar9300_wowoffload_download_acer_swka( struct ath_hal *ah, u_int32_t id, HAL_BOOL valid, u_int32_t period, u_int32_t size, u_int32_t* datap) { u_int32_t ka_period[2] = { AR_WOW_OFFLOAD_ACER_KA0_PERIOD_MS, AR_WOW_OFFLOAD_ACER_KA1_PERIOD_MS }; u_int32_t ka_size[2] = { AR_WOW_OFFLOAD_ACER_KA0_SIZE, AR_WOW_OFFLOAD_ACER_KA1_SIZE }; u_int32_t ka_data[2] = { AR_WOW_OFFLOAD_ACER_KA0_DATA, AR_WOW_OFFLOAD_ACER_KA1_DATA }; u_int32_t n_data = AR_WOW_OFFLOAD_ACER_KA0_DATA_WORDS; int i; if (id >= 2) { return; } if (valid) { OS_REG_WRITE(ah, ka_period[id], period); OS_REG_WRITE(ah, ka_size[id], size); } else { OS_REG_WRITE(ah, ka_period[id], 0); OS_REG_WRITE(ah, ka_size[id], 0); } HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: id=%d, period=%d ms, size=%d bytes\n", __func__, id, period, size); if (size < (n_data * 4)) { n_data = (size + 3) / 4; } for (i=0; ivalid) { OS_REG_WRITE(ah, addr, 0x1); addr += 4; OS_REG_WRITE(ah, addr, p_info->RemoteIPv4Address.u32); addr += 4; OS_REG_WRITE(ah, addr, p_info->HostIPv4Address.u32); addr += 4; OS_REG_WRITE(ah, addr, p_info->MacAddress.u32[0]); addr += 4; OS_REG_WRITE(ah, addr, p_info->MacAddress.u32[1]); } else { OS_REG_WRITE(ah, addr, 0x0); } } #define WOW_WRITE_NS_IPV6_ADDRESS(_ah, _buf_addr, _p_ipv6_addr) \ { \ u_int32_t offset = (_buf_addr); \ u_int32_t *p_ipv6_addr = (u_int32_t *) (_p_ipv6_addr); \ int i; \ for (i = 0; i < 4; i++) { \ OS_REG_WRITE((_ah), offset, *p_ipv6_addr); \ offset += 4; \ p_ipv6_addr ++; \ } \ } void ar9300_wowoffload_download_ns_info(struct ath_hal *ah, u_int32_t id, u_int32_t *data) { u_int32_t addr; struct hal_wow_offload_ns_info *p_info = (struct hal_wow_offload_ns_info *) data; u_int8_t mc_addr[6]; if (id == 0) { addr = AR_WOW_OFFLOAD_NS0_VALID; } else if (id == 1) { addr = AR_WOW_OFFLOAD_NS1_VALID; } else { return; } if (p_info->valid) { OS_REG_WRITE(ah, addr, 0x1); addr += 4; WOW_WRITE_NS_IPV6_ADDRESS(ah, addr, &p_info->RemoteIPv6Address.u32[0]); addr += 4 * 4; WOW_WRITE_NS_IPV6_ADDRESS(ah, addr, &p_info->SolicitedNodeIPv6Address.u32[0]); addr += 4 * 4; OS_REG_WRITE(ah, addr, p_info->MacAddress.u32[0]); addr += 4; OS_REG_WRITE(ah, addr, p_info->MacAddress.u32[1]); addr += 4; WOW_WRITE_NS_IPV6_ADDRESS(ah, addr, &p_info->TargetIPv6Addresses[0].u32[0]); addr += 4 * 4; WOW_WRITE_NS_IPV6_ADDRESS(ah, addr, &p_info->TargetIPv6Addresses[1].u32[0]); mc_addr[0] = 0x33; mc_addr[1] = 0x33; mc_addr[2] = 0xFF; mc_addr[3] = p_info->SolicitedNodeIPv6Address.u8[13]; mc_addr[4] = p_info->SolicitedNodeIPv6Address.u8[14]; mc_addr[5] = p_info->SolicitedNodeIPv6Address.u8[15]; ar9300_wowoffload_add_mcast_filter(ah, mc_addr); } else { OS_REG_WRITE(ah, addr, 0x0); } } /* Download transmit parameters for GTK response frame during WoW * offload */ u_int32_t ar9300_wow_offload_download_hal_params(struct ath_hal *ah) { u_int32_t tpc = 0x3f; /* Transmit Power Control */ u_int32_t tx_tries_series = 7; u_int32_t tx_rate_series, transmit_rate; u_int32_t gtk_txdesc_param_start; if (AH_PRIVATE(ah)->ah_curchan->channel_flags & CHANNEL_CCK) { transmit_rate = 0x1B; /* CCK_1M */ } else { transmit_rate = 0xB; /* OFDM_6M */ } /* Use single rate for now. Change later as need be */ tx_rate_series = transmit_rate; tx_tries_series = 7; if (AR_SREV_JUPITER(ah)) { gtk_txdesc_param_start = AR_WOW_OFFLOAD_GTK_TXDESC_PARAM_START_JUPITER; } else { gtk_txdesc_param_start = AR_WOW_OFFLOAD_GTK_TXDESC_PARAM_START; } #define AR_WOW_OFFLOAD_GTK_TXDESC_PARAM(x) (gtk_txdesc_param_start + ((x) * 4)) /* Do not change the data order unless firmware code on embedded * CPU is changed correspondingly */ OS_REG_WRITE(ah, AR_WOW_OFFLOAD_GTK_TXDESC_PARAM(0), tx_rate_series); OS_REG_WRITE(ah, AR_WOW_OFFLOAD_GTK_TXDESC_PARAM(1), tx_tries_series); OS_REG_WRITE(ah, AR_WOW_OFFLOAD_GTK_TXDESC_PARAM(2), AH9300(ah)->ah_tx_chainmask); OS_REG_WRITE(ah, AR_WOW_OFFLOAD_GTK_TXDESC_PARAM(3), tpc); return 0; } /* Indicate to the embedded CPU that host is ready to enter WoW mode. * Embedded CPU will copy relevant information from the MAC PCU buffer */ u_int32_t ar9300_wow_offload_handshake(struct ath_hal *ah, u_int32_t pattern_enable) { int val; int mbox_status = OS_REG_READ(ah, AR_MBOX_CTRL_STATUS); #if ATH_WOW_OFFLOAD u_int32_t bt_handshake_timeout_us = HAL_WOW_CTRL_WAIT_BT_TO(ah) * 100000; #define AH_DEFAULT_BT_WAIT_TIMEOUT 3000000; /* 3 sec */ if (bt_handshake_timeout_us == 0) { bt_handshake_timeout_us = AH_DEFAULT_BT_WAIT_TIMEOUT; } HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) TIMEOUT: %d us\n", bt_handshake_timeout_us); #endif /* ATH_WOW_OFFLOAD */ if (mbox_status & AR_MBOX_WOW_REQ) { /* WOW mode request handshake is already in progress. * Do nothing */ return 0; } /* Clear status */ OS_REG_WRITE(ah, AR_MBOX_CTRL_STATUS, 0); OS_REG_WRITE(ah, AR_EMB_CPU_WOW_STATUS, 0x0); OS_REG_WRITE(ah, AR_WLAN_WOW_ENABLE, 0); OS_REG_WRITE(ah, AR_WLAN_WOW_STATUS, 0xFFFFFFFF); OS_REG_WRITE(ah, AR_RIMT, 0); OS_REG_WRITE(ah, AR_TIMT, 0); val = 0; if (pattern_enable & AH_WOW_USER_PATTERN_EN) { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - User pattern\n"); val |= AR_EMB_CPU_WOW_ENABLE_PATTERN_MATCH; } else { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - User pattern\n"); } if ((pattern_enable & AH_WOW_MAGIC_PATTERN_EN) #if ATH_WOW_OFFLOAD || (pattern_enable & AH_WOW_ACER_MAGIC_EN) #endif ) { val |= AR_EMB_CPU_WOW_ENABLE_MAGIC_PATTERN; HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - Magic pattern\n"); } else { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - Magic pattern\n"); } if ((pattern_enable & AH_WOW_LINK_CHANGE) #if ATH_WOW_OFFLOAD || HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_KAFAIL_ENABLE) #endif ) { val |= AR_EMB_CPU_WOW_ENABLE_KEEP_ALIVE_FAIL; HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - Kepp alive fail\n"); } else { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - Kepp alive fail\n"); } if (pattern_enable & AH_WOW_BEACON_MISS) { val |= AR_EMB_CPU_WOW_ENABLE_BEACON_MISS; HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - Becon Miss\n"); } else { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - Becon Miss\n"); } OS_REG_WRITE(ah, AR_EMB_CPU_WOW_ENABLE, val); OS_REG_CLR_BIT(ah, AR_MBOX_CTRL_STATUS, AR_MBOX_WOW_CONF); OS_REG_SET_BIT(ah, AR_MBOX_CTRL_STATUS, AR_MBOX_WOW_REQ); OS_REG_SET_BIT(ah, AR_MBOX_CTRL_STATUS, AR_MBOX_INT_EMB_CPU); - if (!ath_hal_wait(ah, AR_MBOX_CTRL_STATUS, AR_MBOX_WOW_CONF, AR_MBOX_WOW_CONF, bt_handshake_timeout_us)) { + if (!ath_hal_waitfor(ah, AR_MBOX_CTRL_STATUS, AR_MBOX_WOW_CONF, AR_MBOX_WOW_CONF, bt_handshake_timeout_us)) { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: WoW offload handshake failed", __func__); return 0; } else { OS_REG_CLR_BIT(ah, AR_MBOX_CTRL_STATUS, AR_MBOX_WOW_CONF); HALDEBUG(ah, HAL_DEBUG_POWER_MGMT, "%s: WoW offload handshake successful",__func__); } return 1; } #endif /* ATH_WOW_OFFLOAD */ /* * Notify Power Mgt is enabled in self-generated frames. * If requested, force chip awake. * * Returns A_OK if chip is awake or successfully forced awake. * * WARNING WARNING WARNING * There is a problem with the chip where sometimes it will not wake up. */ HAL_BOOL ar9300_set_power_mode_awake(struct ath_hal *ah, int set_chip) { struct ath_hal_9300 *ahp = AH9300(ah); #define POWER_UP_TIME 10000 u_int32_t val; int i; /* Set Bits 14 and 17 of AR_WA before powering on the chip. */ OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA), ahp->ah_wa_reg_val); OS_DELAY(10); /* delay to allow the write to take effect. */ if (set_chip) { /* Do a Power-On-Reset if MAC is shutdown */ if ((OS_REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_SHUTDOWN)) { if (ar9300_set_reset_reg(ah, HAL_RESET_POWER_ON) != AH_TRUE) { HALASSERT(0); return AH_FALSE; } } OS_REG_SET_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN); OS_DELAY(50); for (i = POWER_UP_TIME / 50; i > 0; i--) { val = OS_REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M; if (val == AR_RTC_STATUS_ON) { break; } OS_DELAY(50); OS_REG_SET_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN); } if (i == 0) { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: Failed to wakeup in %uus\n", __func__, POWER_UP_TIME / 20); return AH_FALSE; } } OS_REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV); return AH_TRUE; #undef POWER_UP_TIME } /* * Notify Power Mgt is disabled in self-generated frames. * If requested, force chip to sleep. */ static void ar9300_set_power_mode_sleep(struct ath_hal *ah, int set_chip) { struct ath_hal_9300 *ahp = AH9300(ah); OS_REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV); if (set_chip ) { if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) { OS_REG_WRITE(ah, AR_TIMER_MODE, OS_REG_READ(ah, AR_TIMER_MODE) & 0xFFFFFF00); OS_REG_WRITE(ah, AR_GEN_TIMERS2_MODE, OS_REG_READ(ah, AR_GEN_TIMERS2_MODE) & 0xFFFFFF00); OS_REG_WRITE(ah, AR_SLP32_INC, OS_REG_READ(ah, AR_SLP32_INC) & 0xFFF00000); OS_REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_EN, 0); OS_DELAY(100); } /* Clear the RTC force wake bit to allow the mac to go to sleep */ OS_REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN); if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) { /* * In Jupiter, after enter sleep mode, hardware will send * a SYS_SLEEPING message through MCI interface. Add a * few us delay to make sure the message can reach BT side. */ OS_DELAY(100); } if (!AR_SREV_JUPITER_10(ah)) { /* Shutdown chip. Active low */ OS_REG_CLR_BIT(ah, AR_RTC_RESET, AR_RTC_RESET_EN); /* Settle time */ OS_DELAY(2); } } #if ATH_WOW_OFFLOAD if (!AR_SREV_JUPITER(ah) || !HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_SET_4004_BIT14)) #endif /* ATH_WOW_OFFLOAD */ { /* Clear Bit 14 of AR_WA after putting chip into Full Sleep mode. */ OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA), ahp->ah_wa_reg_val & ~AR_WA_D3_TO_L1_DISABLE); } } /* * Notify Power Management is enabled in self-generating * frames. If request, set power mode of chip to * auto/normal. Duration in units of 128us (1/8 TU). */ static void ar9300_set_power_mode_network_sleep(struct ath_hal *ah, int set_chip) { struct ath_hal_9300 *ahp = AH9300(ah); OS_REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV); if (set_chip) { HAL_CAPABILITIES *p_cap = &AH_PRIVATE(ah)->ah_caps; if (! p_cap->halAutoSleepSupport) { /* Set wake_on_interrupt bit; clear force_wake bit */ OS_REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON_INT); } else { /* * When chip goes into network sleep, it could be waken up by * MCI_INT interrupt caused by BT's HW messages (LNA_xxx, CONT_xxx) * which chould be in a very fast rate (~100us). This will cause * chip to leave and re-enter network sleep mode frequently, which * in consequence will have WLAN MCI HW to generate lots of * SYS_WAKING and SYS_SLEEPING messages which will make BT CPU * to busy to process. */ if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) { OS_REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_EN, OS_REG_READ(ah, AR_MCI_INTERRUPT_RX_MSG_EN) & ~AR_MCI_INTERRUPT_RX_HW_MSG_MASK); } /* Clear the RTC force wake bit to allow the mac to go to sleep */ OS_REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN); if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) { /* * In Jupiter, after enter sleep mode, hardware will send * a SYS_SLEEPING message through MCI interface. Add a * few us delay to make sure the message can reach BT side. */ OS_DELAY(30); } } } #if ATH_WOW_OFFLOAD if (!AR_SREV_JUPITER(ah) || !HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_SET_4004_BIT14)) #endif /* ATH_WOW_OFFLOAD */ { /* Clear Bit 14 of AR_WA after putting chip into Sleep mode. */ OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA), ahp->ah_wa_reg_val & ~AR_WA_D3_TO_L1_DISABLE); } } /* * Set power mgt to the requested mode, and conditionally set * the chip as well */ HAL_BOOL ar9300_set_power_mode(struct ath_hal *ah, HAL_POWER_MODE mode, int set_chip) { struct ath_hal_9300 *ahp = AH9300(ah); #if defined(AH_DEBUG) || defined(AH_PRINT_FILTER) static const char* modes[] = { "AWAKE", "FULL-SLEEP", "NETWORK SLEEP", "UNDEFINED" }; #endif int status = AH_TRUE; HALDEBUG(ah, HAL_DEBUG_POWER_MGMT, "%s: %s -> %s (%s)\n", __func__, modes[ar9300_get_power_mode(ah)], modes[mode], set_chip ? "set chip " : ""); OS_MARK(ah, AH_MARK_CHIP_POWER, mode); switch (mode) { case HAL_PM_AWAKE: if (set_chip) ah->ah_powerMode = mode; status = ar9300_set_power_mode_awake(ah, set_chip); #if ATH_SUPPORT_MCI if (AH_PRIVATE(ah)->ah_caps.halMciSupport) { OS_REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2); } #endif + ahp->ah_chip_full_sleep = AH_FALSE; break; case HAL_PM_FULL_SLEEP: #if ATH_SUPPORT_MCI if (AH_PRIVATE(ah)->ah_caps.halMciSupport) { if (ar9300_get_power_mode(ah) == HAL_PM_AWAKE) { if ((ar9300_mci_state(ah, HAL_MCI_STATE_ENABLE, NULL) != 0) && (ahp->ah_mci_bt_state != MCI_BT_SLEEP) && !ahp->ah_mci_halted_bt_gpm) { HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) %s: HALT BT GPM (full_sleep)\n", __func__); ar9300_mci_send_coex_halt_bt_gpm(ah, AH_TRUE, AH_TRUE); } } ahp->ah_mci_ready = AH_FALSE; } #endif #if ATH_SUPPORT_MCI if (AH_PRIVATE(ah)->ah_caps.halMciSupport) { OS_REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2); } #endif ar9300_set_power_mode_sleep(ah, set_chip); if (set_chip) { ahp->ah_chip_full_sleep = AH_TRUE; ah->ah_powerMode = mode; } break; case HAL_PM_NETWORK_SLEEP: #if ATH_SUPPORT_MCI if (AH_PRIVATE(ah)->ah_caps.halMciSupport) { OS_REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2); } #endif ar9300_set_power_mode_network_sleep(ah, set_chip); if (set_chip) { ah->ah_powerMode = mode; } break; default: HALDEBUG(ah, HAL_DEBUG_POWER_MGMT, "%s: unknown power mode %u\n", __func__, mode); OS_MARK(ah, AH_MARK_CHIP_POWER_DONE, -1); return AH_FALSE; } OS_MARK(ah, AH_MARK_CHIP_POWER_DONE, status); return status; } /* * Return the current sleep mode of the chip */ HAL_POWER_MODE ar9300_get_power_mode(struct ath_hal *ah) { int mode = OS_REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M; switch (mode) { case AR_RTC_STATUS_ON: case AR_RTC_STATUS_WAKEUP: return HAL_PM_AWAKE; break; case AR_RTC_STATUS_SLEEP: return HAL_PM_NETWORK_SLEEP; break; case AR_RTC_STATUS_SHUTDOWN: return HAL_PM_FULL_SLEEP; break; default: HALDEBUG(ah, HAL_DEBUG_POWER_MGMT, "%s: unknown power mode 0x%x\n", __func__, mode); return HAL_PM_UNDEFINED; } } /* * Set SM power save mode */ void ar9300_set_sm_power_mode(struct ath_hal *ah, HAL_SMPS_MODE mode) { int regval; struct ath_hal_9300 *ahp = AH9300(ah); if (ar9300_get_capability(ah, HAL_CAP_DYNAMIC_SMPS, 0, AH_NULL) != HAL_OK) { return; } /* Program low & high power chainmask settings and enable MAC control */ regval = SM(AR_PCU_SMPS_LPWR_CHNMSK_VAL, AR_PCU_SMPS_LPWR_CHNMSK) | SM(ahp->ah_rx_chainmask, AR_PCU_SMPS_HPWR_CHNMSK) | AR_PCU_SMPS_MAC_CHAINMASK; /* Program registers according to required SM power mode.*/ switch (mode) { case HAL_SMPS_SW_CTRL_LOW_PWR: OS_REG_WRITE(ah, AR_PCU_SMPS, regval); break; case HAL_SMPS_SW_CTRL_HIGH_PWR: OS_REG_WRITE(ah, AR_PCU_SMPS, regval | AR_PCU_SMPS_SW_CTRL_HPWR); break; case HAL_SMPS_HW_CTRL: OS_REG_WRITE(ah, AR_PCU_SMPS, regval | AR_PCU_SMPS_HW_CTRL_EN); break; case HAL_SMPS_DEFAULT: OS_REG_WRITE(ah, AR_PCU_SMPS, 0); break; default: break; } ahp->ah_sm_power_mode = mode; } #if ATH_WOW #if NOT_NEEDED_FOR_OSPREY /* not compiled for darwin */ /* * This routine is called to configure the SerDes register for the * Merlin 2.0 and above chip during WOW sleep. */ static void ar9280_config_ser_des__wow_sleep(struct ath_hal *ah) { int i; struct ath_hal_9300 *ahp = AH9300(ah); /* * For WOW sleep, we reprogram the SerDes so that the PLL and CHK REQ * are both enabled. This uses more power but the Maverick team reported * that otherwise, WOW sleep is unstable and chip may disappears. */ for (i = 0; i < ahp->ah_ini_pcie_serdes_wow.ia_rows; i++) { OS_REG_WRITE(ah, INI_RA(&ahp->ah_ini_pcie_serdes_wow, i, 0), INI_RA(&ahp->ah_ini_pcie_serdes_wow, i, 1)); } OS_DELAY(1000); } #endif /* if NOT_NEEDED_FOR_OSPREY */ static HAL_BOOL ar9300_wow_create_keep_alive_pattern(struct ath_hal *ah) { struct ath_hal_9300 *ahp = AH9300(ah); u_int32_t frame_len = 28; u_int32_t tpc = 0x3f; u_int32_t transmit_rate; u_int32_t frame_type = 0x2; /* Frame Type -> Data; */ u_int32_t sub_type = 0x4; /* Subtype -> Null Data */ u_int32_t to_ds = 1; u_int32_t duration_id = 0x3d; u_int8_t *sta_mac_addr, *ap_mac_addr; u_int8_t *addr1, *addr2, *addr3; u_int32_t ctl[13] = { 0, }; #define NUM_KA_DATA_WORDS 6 u_int32_t data_word[NUM_KA_DATA_WORDS]; u_int32_t i; u_int32_t wow_ka_dataword0; sta_mac_addr = (u_int8_t *)ahp->ah_macaddr; ap_mac_addr = (u_int8_t *)ahp->ah_bssid; addr2 = sta_mac_addr; addr1 = addr3 = ap_mac_addr; if (AH_PRIVATE(ah)->ah_curchan->channel_flags & CHANNEL_CCK) { transmit_rate = 0x1B; /* CCK_1M */ } else { transmit_rate = 0xB; /* OFDM_6M */ } /* Set the Transmit Buffer. */ ctl[0] = (frame_len | (tpc << 16)); ctl[1] = 0; ctl[2] = (0x7 << 16); /* tx_tries0 */ ctl[3] = transmit_rate; ctl[4] = 0; ctl[7] = ahp->ah_tx_chainmask << 2; for (i = 0; i < 13; i++) { OS_REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]); } data_word[0] = (frame_type << 2) | (sub_type << 4) | (to_ds << 8) | (duration_id << 16); data_word[1] = (((u_int32_t)addr1[3] << 24) | ((u_int32_t)addr1[2] << 16) | ((u_int32_t)addr1[1]) << 8 | ((u_int32_t)addr1[0])); data_word[2] = (((u_int32_t)addr2[1] << 24) | ((u_int32_t)addr2[0] << 16) | ((u_int32_t)addr1[5]) << 8 | ((u_int32_t)addr1[4])); data_word[3] = (((u_int32_t)addr2[5] << 24) | ((u_int32_t)addr2[4] << 16) | ((u_int32_t)addr2[3]) << 8 | ((u_int32_t)addr2[2])); data_word[4] = (((u_int32_t)addr3[3] << 24) | ((u_int32_t)addr3[2] << 16) | ((u_int32_t)addr3[1]) << 8 | (u_int32_t)addr3[0]); data_word[5] = (((u_int32_t)addr3[5]) << 8 | ((u_int32_t)addr3[4])); if (AR_SREV_JUPITER_20_OR_LATER(ah) || AR_SREV_APHRODITE(ah)) { /* Jupiter 2.0 has an extra descriptor word (Time based * discard) compared to other chips */ OS_REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + 12 * 4), 0); wow_ka_dataword0 = AR_WOW_TXBUF(13); } else { wow_ka_dataword0 = AR_WOW_TXBUF(12); } for (i = 0; i < NUM_KA_DATA_WORDS; i++) { OS_REG_WRITE(ah, (wow_ka_dataword0 + i * 4), data_word[i]); } return AH_TRUE; } /* TBD: Should querying hal for hardware capability */ #define MAX_PATTERN_SIZE 256 #define MAX_PATTERN_MASK_SIZE 32 #define MAX_NUM_USER_PATTERN 6 /* Deducting the disassoc/deauth packets */ void ar9300_wow_apply_pattern( struct ath_hal *ah, u_int8_t *p_ath_pattern, u_int8_t *p_ath_mask, int32_t pattern_count, u_int32_t ath_pattern_len) { int i; u_int32_t reg_pat[] = { AR_WOW_TB_PATTERN0, AR_WOW_TB_PATTERN1, AR_WOW_TB_PATTERN2, AR_WOW_TB_PATTERN3, AR_WOW_TB_PATTERN4, AR_WOW_TB_PATTERN5, AR_WOW_TB_PATTERN6, AR_WOW_TB_PATTERN7 }; u_int32_t reg_mask[] = { AR_WOW_TB_MASK0, AR_WOW_TB_MASK1, AR_WOW_TB_MASK2, AR_WOW_TB_MASK3, AR_WOW_TB_MASK4, AR_WOW_TB_MASK5, AR_WOW_TB_MASK6, AR_WOW_TB_MASK7 }; u_int32_t pattern_val; u_int32_t mask_val; u_int32_t val; u_int8_t mask_bit = 0x1; u_int8_t pattern; /* TBD: should check count by querying the hardware capability */ if (pattern_count >= MAX_NUM_USER_PATTERN) { return; } pattern = (u_int8_t)OS_REG_READ(ah, AR_WOW_PATTERN_REG); pattern = pattern | (mask_bit << pattern_count); OS_REG_WRITE(ah, AR_WOW_PATTERN_REG, pattern); /* Set the registers for pattern */ for (i = 0; i < MAX_PATTERN_SIZE; i += 4) { pattern_val = (((u_int32_t)p_ath_pattern[i + 0]) | ((u_int32_t)p_ath_pattern[i + 1] << 8) | ((u_int32_t)p_ath_pattern[i + 2] << 16) | ((u_int32_t)p_ath_pattern[i + 3] << 24)); OS_REG_WRITE(ah, (reg_pat[pattern_count] + i), pattern_val); } /* Set the registers for mask */ for (i = 0; i < MAX_PATTERN_MASK_SIZE; i += 4) { mask_val = (((u_int32_t)p_ath_mask[i + 0]) | ((u_int32_t)p_ath_mask[i + 1] << 8) | ((u_int32_t)p_ath_mask[i + 2] << 16) | ((u_int32_t)p_ath_mask[i + 3] << 24)); OS_REG_WRITE(ah, (reg_mask[pattern_count] + i), mask_val); } /* XXX */ /* Set the pattern length to be matched */ if (pattern_count < 4) { /* Pattern 0-3 uses AR_WOW_LENGTH1_REG register */ val = OS_REG_READ(ah, AR_WOW_LENGTH1_REG); val = ((val & (~AR_WOW_LENGTH1_MASK(pattern_count))) | ((ath_pattern_len & AR_WOW_LENGTH_MAX) << AR_WOW_LENGTH1_SHIFT(pattern_count))); OS_REG_WRITE(ah, AR_WOW_LENGTH1_REG, val); } else { /* Pattern 4-7 uses AR_WOW_LENGTH2_REG register */ val = OS_REG_READ(ah, AR_WOW_LENGTH2_REG); val = ((val & (~AR_WOW_LENGTH2_MASK(pattern_count))) | ((ath_pattern_len & AR_WOW_LENGTH_MAX) << AR_WOW_LENGTH2_SHIFT(pattern_count))); OS_REG_WRITE(ah, AR_WOW_LENGTH2_REG, val); } AH_PRIVATE(ah)->ah_wow_event_mask |= (1 << (pattern_count + AR_WOW_PATTERN_FOUND_SHIFT)); return; } HAL_BOOL ar9300_set_power_mode_wow_sleep(struct ath_hal *ah) { OS_REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV); OS_REG_WRITE(ah, AR_CR, AR_CR_RXD); /* Set receive disable bit */ - if (!ath_hal_wait(ah, AR_CR, AR_CR_RXE, 0, AH_WAIT_TIMEOUT)) { + if (!ath_hal_waitfor(ah, AR_CR, AR_CR_RXE, 0, AH_WAIT_TIMEOUT)) { HALDEBUG(ah, HAL_DEBUG_POWER_MGMT, "%s: dma failed to stop in 10ms\n" "AR_CR=0x%08x\nAR_DIAG_SW=0x%08x\n", __func__, OS_REG_READ(ah, AR_CR), OS_REG_READ(ah, AR_DIAG_SW)); return AH_FALSE; } else { #if 0 OS_REG_WRITE(ah, AR_RXDP, 0x0); #endif HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE, "%s: TODO How to disable RXDP!!\n", __func__); #if ATH_SUPPORT_MCI if (AH_PRIVATE(ah)->ah_caps.halMciSupport) { OS_REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2); } #endif OS_REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON_INT); return AH_TRUE; } } HAL_BOOL ar9300_wow_enable( struct ath_hal *ah, u_int32_t pattern_enable, u_int32_t timeout_in_seconds, int clearbssid, HAL_BOOL offloadEnable) { uint32_t init_val, val, rval = 0; const int ka_delay = 4; /* Delay of 4 millisec between two keep_alive's */ uint32_t wow_event_mask; #if ATH_WOW_OFFLOAD uint32_t wow_feature_enable = //AR_WOW_OFFLOAD_ENA_GTK | //AR_WOW_OFFLOAD_ENA_ARP_OFFLOAD | //AR_WOW_OFFLOAD_ENA_NS_OFFLOAD | //AR_WOW_OFFLOAD_ENA_ACER_MAGIC | //AR_WOW_OFFLOAD_ENA_STD_MAGIC | //AR_WOW_OFFLOAD_ENA_4WAY_WAKE | //AR_WOW_OFFLOAD_ENA_SWKA | //AR_WOW_OFFLOAD_ENA_BT_SLEEP | AR_WOW_OFFLOAD_ENA_SW_NULL; #endif /* * ah_wow_event_mask is a mask to the AR_WOW_PATTERN_REG register to * indicate which WOW events that we have enabled. The WOW Events are * from the pattern_enable in this function and pattern_count of * ar9300_wow_apply_pattern() */ wow_event_mask = AH_PRIVATE(ah)->ah_wow_event_mask; HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE, "%s: offload: %d, pattern: %08x, event_mask: %08x\n", __func__, offloadEnable, pattern_enable, wow_event_mask); /* * Untie Power-On-Reset from the PCI-E Reset. When we are in WOW sleep, * we do not want the Reset from the PCI-E to disturb our hw state. */ if (AH_PRIVATE(ah)->ah_is_pci_express == AH_TRUE) { u_int32_t wa_reg_val; /* * We need to untie the internal POR (power-on-reset) to the external * PCI-E reset. We also need to tie the PCI-E Phy reset to the PCI-E * reset. */ HAL_DEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE, "%s: Untie POR and PCIE reset\n", __func__); wa_reg_val = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_WA)); wa_reg_val = wa_reg_val & ~(AR_WA_UNTIE_RESET_EN); wa_reg_val = wa_reg_val | AR_WA_RESET_EN | AR_WA_POR_SHORT; /* * This bit is to bypass the EEPROM/OTP state machine, (by clearing its * busy state while PCIE_rst is asserted), to allow BT embedded CPU * be able to access WLAN registers. Otherwise the eCPU access will be * stalled as eeprom_sm is held in busy state. * * EV91928 is that when this bit is set, after host wakeup and PCIE_rst * deasserted, PCIE configuration registers will be reset and DeviceID * SubsystemID etc. registers will be different from values before * entering sleep. This will cause Windows to detect a device removal. * * For HW WOW, this bit should keep as cleared. */ if (offloadEnable) { HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE, "%s: Set AR_WA.13 COLD_RESET_OVERRIDE\n", __func__); wa_reg_val = wa_reg_val | AR_WA_COLD_RESET_OVERRIDE; #if ATH_WOW_OFFLOAD if (AR_SREV_JUPITER(ah)) { wa_reg_val = wa_reg_val | AR_WA_D3_TO_L1_DISABLE; } #endif } OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA), wa_reg_val); } /* * Set the power states appropriately and enable pme. */ val = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL)); val |= AR_PMCTRL_HOST_PME_EN | AR_PMCTRL_PWR_PM_CTRL_ENA | AR_PMCTRL_AUX_PWR_DET; /* * Set and clear WOW_PME_CLEAR registers for the chip to generate next * wow signal. */ val |= AR_PMCTRL_WOW_PME_CLR; OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL), val); val &= ~AR_PMCTRL_WOW_PME_CLR; OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL), val); /* * Setup for for: * - beacon misses * - magic pattern * - keep alive timeout * - pattern matching */ /* * Program some default values for keep-alives, beacon misses, etc. */ init_val = OS_REG_READ(ah, AR_WOW_PATTERN_REG); val = AR_WOW_BACK_OFF_SHIFT(AR_WOW_PAT_BACKOFF) | init_val; OS_REG_WRITE(ah, AR_WOW_PATTERN_REG, val); rval = OS_REG_READ(ah, AR_WOW_PATTERN_REG); val = AR_WOW_AIFS_CNT(AR_WOW_CNT_AIFS_CNT) | AR_WOW_SLOT_CNT(AR_WOW_CNT_SLOT_CNT) | AR_WOW_KEEP_ALIVE_CNT(AR_WOW_CNT_KA_CNT); OS_REG_WRITE(ah, AR_WOW_COUNT_REG, val); rval = OS_REG_READ(ah, AR_WOW_COUNT_REG); if (pattern_enable & AH_WOW_BEACON_MISS) { val = AR_WOW_BEACON_TIMO; } else { /* We are not using the beacon miss. Program a large value. */ val = AR_WOW_BEACON_TIMO_MAX; } OS_REG_WRITE(ah, AR_WOW_BCN_TIMO_REG, val); rval = OS_REG_READ(ah, AR_WOW_BCN_TIMO_REG); /* * Keep Alive Timo in ms. */ if (pattern_enable == 0) { val = AR_WOW_KEEP_ALIVE_NEVER; } else { val = AH_PRIVATE(ah)->ah_config.ath_hal_keep_alive_timeout * 32; } OS_REG_WRITE(ah, AR_WOW_KEEP_ALIVE_TIMO_REG, val); rval = OS_REG_READ(ah, AR_WOW_KEEP_ALIVE_TIMO_REG); /* * Keep Alive delay in us. */ val = ka_delay * 1000; OS_REG_WRITE(ah, AR_WOW_KEEP_ALIVE_DELAY_REG, val); rval = OS_REG_READ(ah, AR_WOW_KEEP_ALIVE_DELAY_REG); /* * Create keep_alive Pattern to respond to beacons. */ ar9300_wow_create_keep_alive_pattern(ah); /* * Configure Mac Wow Registers. */ val = OS_REG_READ(ah, AR_WOW_KEEP_ALIVE_REG); /* * Send keep alive timeouts anyway. */ val &= ~AR_WOW_KEEP_ALIVE_AUTO_DIS; if (pattern_enable & AH_WOW_LINK_CHANGE) { val &= ~ AR_WOW_KEEP_ALIVE_FAIL_DIS; wow_event_mask |= AR_WOW_KEEP_ALIVE_FAIL; } else { val |= AR_WOW_KEEP_ALIVE_FAIL_DIS; } #if ATH_WOW_OFFLOAD if (offloadEnable) { /* Don't enable KA frames yet. BT CPU is not * yet ready. */ } else #endif /* ATH_WOW_OFFLOAD */ { OS_REG_WRITE(ah, AR_WOW_KEEP_ALIVE_REG, val); val = OS_REG_READ(ah, AR_WOW_KEEP_ALIVE_REG); } /* * We are relying on a bmiss failure. Ensure we have enough * threshold to prevent AH_FALSE positives. */ OS_REG_RMW_FIELD(ah, AR_RSSI_THR, AR_RSSI_THR_BM_THR, AR_WOW_BMISSTHRESHOLD); val = OS_REG_READ(ah, AR_WOW_BCN_EN_REG); if (pattern_enable & AH_WOW_BEACON_MISS) { val |= AR_WOW_BEACON_FAIL_EN; wow_event_mask |= AR_WOW_BEACON_FAIL; } else { val &= ~AR_WOW_BEACON_FAIL_EN; } OS_REG_WRITE(ah, AR_WOW_BCN_EN_REG, val); val = OS_REG_READ(ah, AR_WOW_BCN_EN_REG); /* * Enable the magic packet registers. */ val = OS_REG_READ(ah, AR_WOW_PATTERN_REG); if ((pattern_enable & AH_WOW_MAGIC_PATTERN_EN) #if ATH_WOW_OFFLOAD || (pattern_enable & AH_WOW_ACER_MAGIC_EN) #endif ) { val |= AR_WOW_MAGIC_EN; wow_event_mask |= AR_WOW_MAGIC_PAT_FOUND; } else { val &= ~AR_WOW_MAGIC_EN; } val |= AR_WOW_MAC_INTR_EN; OS_REG_WRITE(ah, AR_WOW_PATTERN_REG, val); val = OS_REG_READ(ah, AR_WOW_PATTERN_REG); #if ATH_WOW_OFFLOAD if (HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_FORCE_BT_SLEEP)) { wow_feature_enable |= AR_WOW_OFFLOAD_ENA_BT_SLEEP; HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - BT SLEEP\n"); } else { wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_BT_SLEEP; HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - BT SLEEP\n"); } if (HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_SW_NULL_DISABLE)) { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - SW NULL\n"); wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_SW_NULL; } else { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - SW NULL\n"); wow_feature_enable |= AR_WOW_OFFLOAD_ENA_SW_NULL; } if (HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_DEVID_SWAR_DISABLE)) { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - DevID SWAR\n"); wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_DEVID_SWAR; } else { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - DevID SWAR\n"); wow_feature_enable |= AR_WOW_OFFLOAD_ENA_DEVID_SWAR; } if (pattern_enable & AH_WOW_ACER_KEEP_ALIVE_EN) { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - Acer SWKA\n"); wow_feature_enable |= AR_WOW_OFFLOAD_ENA_SWKA; } else { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - Acer SWKA\n"); wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_SWKA; } if (pattern_enable & AH_WOW_ACER_MAGIC_EN) { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - Standard Magic\n"); wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_STD_MAGIC; HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - Acer Magic\n"); wow_feature_enable |= AR_WOW_OFFLOAD_ENA_ACER_MAGIC; } else { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - Standard Magic\n"); wow_feature_enable |= AR_WOW_OFFLOAD_ENA_STD_MAGIC; HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - Acer Magic\n"); wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_ACER_MAGIC; } if ((pattern_enable & AH_WOW_4WAY_HANDSHAKE_EN) || HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_FORCE_4WAY_HS_WAKE)) { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - 4Way Handshake\n"); wow_feature_enable |= AR_WOW_OFFLOAD_ENA_4WAY_WAKE; } else { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - 4Way Handshake\n"); wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_4WAY_WAKE; } if((pattern_enable & AH_WOW_AP_ASSOCIATION_LOST_EN) || HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_FORCE_AP_LOSS_WAKE)) { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - AP loss wake\n"); wow_feature_enable |= AR_WOW_OFFLOAD_ENA_AP_LOSS_WAKE; } else { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - AP loss wake\n"); wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_AP_LOSS_WAKE; } if((pattern_enable & AH_WOW_GTK_HANDSHAKE_ERROR_EN) || HAL_WOW_CTRL(ah, HAL_WOW_OFFLOAD_FORCE_GTK_ERR_WAKE)) { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - GTK error wake\n"); wow_feature_enable |= AR_WOW_OFFLOAD_ENA_GTK_ERROR_WAKE; } else { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - GTK error wake\n"); wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_GTK_ERROR_WAKE; } if (pattern_enable & AH_WOW_GTK_OFFLOAD_EN) { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - GTK offload\n"); wow_feature_enable |= AR_WOW_OFFLOAD_ENA_GTK; } else { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - GTK offload\n"); wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_GTK; } if (pattern_enable & AH_WOW_ARP_OFFLOAD_EN) { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - ARP offload\n"); wow_feature_enable |= AR_WOW_OFFLOAD_ENA_ARP_OFFLOAD; } else { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - ARP offload\n"); wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_ARP_OFFLOAD; } if (pattern_enable & AH_WOW_NS_OFFLOAD_EN) { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) ENA - NS offload\n"); wow_feature_enable |= AR_WOW_OFFLOAD_ENA_NS_OFFLOAD; } else { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) DIS - NS offload\n"); wow_feature_enable &= ~AR_WOW_OFFLOAD_ENA_NS_OFFLOAD; } #endif /* ATH_WOW_OFFLOAD */ /* For Kite and later version of the chips * enable wow pattern match for packets less than * 256 bytes for all patterns. */ /* XXX */ OS_REG_WRITE( ah, AR_WOW_PATTERN_MATCH_LT_256B_REG, AR_WOW_PATTERN_SUPPORTED); /* * Set the power states appropriately and enable PME. */ val = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL)); val |= AR_PMCTRL_PWR_STATE_D1D3 | AR_PMCTRL_HOST_PME_EN | AR_PMCTRL_PWR_PM_CTRL_ENA; val &= ~AR_PCIE_PM_CTRL_ENA; OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL), val); /* Wake on Timer Interrupt. Test mode only. Used in Manufacturing line. */ if (timeout_in_seconds) { /* convert Timeout to u_secs */ OS_REG_WRITE(ah, AR_NEXT_NDP_TIMER, OS_REG_READ(ah, AR_TSF_L32) + timeout_in_seconds * 1000000 ); /* timer_period = 30 seconds always */ OS_REG_WRITE(ah, AR_NDP_PERIOD, 30 * 1000000); OS_REG_WRITE(ah, AR_TIMER_MODE, OS_REG_READ(ah, AR_TIMER_MODE) | 0x80); OS_REG_WRITE(ah, AR_IMR_S5, OS_REG_READ(ah, AR_IMR_S5) | 0x80); OS_REG_WRITE(ah, AR_IMR, OS_REG_READ(ah, AR_IMR) | AR_IMR_GENTMR); if (clearbssid) { OS_REG_WRITE(ah, AR_BSS_ID0, 0); OS_REG_WRITE(ah, AR_BSS_ID1, 0); } } /* Enable Seq# generation when asleep. */ OS_REG_WRITE(ah, AR_STA_ID1, OS_REG_READ(ah, AR_STA_ID1) & ~AR_STA_ID1_PRESERVE_SEQNUM); AH_PRIVATE(ah)->ah_wow_event_mask = wow_event_mask; #if ATH_WOW_OFFLOAD if (offloadEnable) { /* Force MAC awake before entering SW WoW mode */ OS_REG_SET_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN); #if ATH_SUPPORT_MCI if (AH_PRIVATE(ah)->ah_caps.halMciSupport) { OS_REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2); } #endif OS_REG_WRITE(ah, AR_WOW_OFFLOAD_COMMAND_JUPITER, wow_feature_enable); OS_REG_WRITE(ah, AR_WOW_OFFLOAD_STATUS_JUPITER, 0x0); if (wow_feature_enable & AR_WOW_OFFLOAD_ENA_SW_NULL) { OS_REG_WRITE(ah, AR_WOW_SW_NULL_PARAMETER, ((1000) | (4 << AR_WOW_SW_NULL_SHORT_PERIOD_MASK_S))); } if (wow_feature_enable & AR_WOW_OFFLOAD_ENA_DEVID_SWAR) { ar9300_wowoffload_download_devid_swar(ah); } ar9300_wow_offload_download_hal_params(ah); ar9300_wow_offload_handshake(ah, pattern_enable); AH9300(ah)->ah_chip_full_sleep = AH_FALSE; //OS_REG_SET_BIT(ah, AR_SW_WOW_CONTROL, AR_HW_WOW_DISABLE); } else #endif /* ATH_WOW_OFFLOAD */ { #if ATH_SUPPORT_MCI if (AH_PRIVATE(ah)->ah_caps.halMciSupport) { OS_REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2); } #endif ar9300_set_power_mode_wow_sleep(ah); AH9300(ah)->ah_chip_full_sleep = AH_TRUE; } return (AH_TRUE); } u_int32_t //ar9300_wow_wake_up(struct ath_hal *ah, u_int8_t *chipPatternBytes) ar9300_wow_wake_up(struct ath_hal *ah, HAL_BOOL offloadEnabled) { uint32_t wow_status = 0; uint32_t val = 0, rval; OS_REG_CLR_BIT(ah, AR_SW_WOW_CONTROL, AR_HW_WOW_DISABLE); OS_REG_CLR_BIT(ah, AR_SW_WOW_CONTROL, AR_SW_WOW_ENABLE); #if ATH_WOW_OFFLOAD /* If WoW was offloaded to embedded CPU, use the global * shared register to know the wakeup reason */ if (offloadEnabled) { val = OS_REG_READ(ah, AR_EMB_CPU_WOW_STATUS); if (val) { if (val & AR_EMB_CPU_WOW_STATUS_MAGIC_PATTERN) { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) SW MAGIC_PATTERN\n"); wow_status |= AH_WOW_MAGIC_PATTERN_EN; } if (val & AR_EMB_CPU_WOW_STATUS_PATTERN_MATCH) { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) SW USER_PATTERN\n"); wow_status |= AH_WOW_USER_PATTERN_EN; } if (val & AR_EMB_CPU_WOW_STATUS_KEEP_ALIVE_FAIL) { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) SW KEEP_ALIVE_FAIL\n"); wow_status |= AH_WOW_LINK_CHANGE; } if (val & AR_EMB_CPU_WOW_STATUS_BEACON_MISS) { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) SW BEACON_FAIL\n"); wow_status |= AH_WOW_BEACON_MISS; } } /* Clear status and mask registers */ OS_REG_WRITE(ah, AR_EMB_CPU_WOW_STATUS, 0x0); OS_REG_WRITE(ah, AR_EMB_CPU_WOW_ENABLE, 0); OS_REG_WRITE(ah, AR_MBOX_CTRL_STATUS, 0); } else #endif /* ATH_WOW_OFFLOAD */ { /* * Read the WOW Status register to know the wakeup reason. */ rval = OS_REG_READ(ah, AR_WOW_PATTERN_REG); val = AR_WOW_STATUS(rval); /* * Mask only the WOW events that we have enabled. * Sometimes we have spurious WOW events from the AR_WOW_PATTERN_REG * register. This mask will clean it up. */ val &= AH_PRIVATE(ah)->ah_wow_event_mask; if (val) { if (val & AR_WOW_MAGIC_PAT_FOUND) { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) HW MAGIC_PATTERN\n"); wow_status |= AH_WOW_MAGIC_PATTERN_EN; } if (AR_WOW_PATTERN_FOUND(val)) { //int i, offset; //offset = OS_REG_READ(ah, AR_WOW_RXBUF_START_ADDR); //// Read matched pattern for wake packet detection indication. //for( i = 0; i< MAX_PATTERN_SIZE/4; i+=4) //{ // // RX FIFO is only 8K wrapping. // if(offset >= 8 * 1024 / 4) offset = 0; // *(u_int32_t*)(chipPatternBytes + i) = OS_REG_READ( ah,offset ); // offset++; //} wow_status |= AH_WOW_USER_PATTERN_EN; HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) HW USER_PATTERN\n"); } if (val & AR_WOW_KEEP_ALIVE_FAIL) { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) HW KEEP_ALIVE_FAIL\n"); wow_status |= AH_WOW_LINK_CHANGE; } if (val & AR_WOW_BEACON_FAIL) { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "(WOW) HW BEACON_FAIL\n"); wow_status |= AH_WOW_BEACON_MISS; } } } /* * Set and clear WOW_PME_CLEAR registers for the chip to generate next * wow signal. * Disable D3 before accessing other registers ? */ val = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL)); /* Check the bit value 0x01000000 (7-10)? */ val &= ~AR_PMCTRL_PWR_STATE_D1D3; val |= AR_PMCTRL_WOW_PME_CLR; OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL), val); /* * Clear all events. */ OS_REG_WRITE(ah, AR_WOW_PATTERN_REG, AR_WOW_CLEAR_EVENTS(OS_REG_READ(ah, AR_WOW_PATTERN_REG))); //HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE, // "%s: Skip PCIE WA programming\n", __func__); #if 0 /* * Tie reset register. * FIXME: Per David Quan not tieing it back might have some repurcussions. */ /* XXX */ OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA), OS_REG_READ(ah, AR_WA) | AR_WA_UNTIE_RESET_EN | AR_WA_POR_SHORT | AR_WA_RESET_EN); #endif /* Restore the Beacon Threshold to init value */ OS_REG_RMW_FIELD(ah, AR_RSSI_THR, AR_RSSI_THR_BM_THR, INIT_RSSI_THR); /* * Restore the way the PCI-E Reset, Power-On-Reset, external PCIE_POR_SHORT * pins are tied to its original value. Previously just before WOW sleep, * we untie the PCI-E Reset to our Chip's Power On Reset so that * any PCI-E reset from the bus will not reset our chip. */ HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE, "%s: restore AR_WA\n", __func__); if (AH_PRIVATE(ah)->ah_is_pci_express == AH_TRUE) { ar9300_config_pci_power_save(ah, 0, 0); } AH_PRIVATE(ah)->ah_wow_event_mask = 0; HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE, "(WOW) wow_status=%08x\n", wow_status); return (wow_status); } void ar9300_wow_set_gpio_reset_low(struct ath_hal *ah) { uint32_t val; val = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_GPIO_OE_OUT)); val |= (1 << (2 * 2)); OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_GPIO_OE_OUT), val); val = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_GPIO_OE_OUT)); /* val = OS_REG_READ(ah,AR_GPIO_IN_OUT ); */ } #endif /* ATH_WOW */ Index: projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_recv_ds.c =================================================================== --- projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_recv_ds.c (revision 278776) +++ projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_recv_ds.c (revision 278777) @@ -1,195 +1,197 @@ /* * Copyright (c) 2013 Qualcomm Atheros, Inc. * * 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 "opt_ah.h" #include "ah.h" #include "ah_desc.h" #include "ah_internal.h" #include "ar9300/ar9300.h" #include "ar9300/ar9300reg.h" #include "ar9300/ar9300desc.h" /* * Process an RX descriptor, and return the status to the caller. * Copy some hardware specific items into the software portion * of the descriptor. * * NB: the caller is responsible for validating the memory contents * of the descriptor (e.g. flushing any cached copy). */ HAL_STATUS ar9300_proc_rx_desc_fast(struct ath_hal *ah, struct ath_desc *ds, u_int32_t pa, struct ath_desc *nds, struct ath_rx_status *rxs, void *buf_addr) { struct ar9300_rxs *rxsp = AR9300RXS(buf_addr); /* ath_hal_printf(ah,"CHH=RX: ds_info 0x%x status1: 0x%x status11: 0x%x\n", rxsp->ds_info,rxsp->status1,rxsp->status11); */ if ((rxsp->status11 & AR_rx_done) == 0) { return HAL_EINPROGRESS; } if (MS(rxsp->ds_info, AR_desc_id) != 0x168c) { #if __PKT_SERIOUS_ERRORS__ /*BUG: 63564-HT */ HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE, "%s: Rx Descriptor error 0x%x\n", __func__, rxsp->ds_info); #endif return HAL_EINVAL; } if ((rxsp->ds_info & (AR_tx_rx_desc | AR_ctrl_stat)) != 0) { #if __PKT_SERIOUS_ERRORS__ HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE, "%s: Rx Descriptor wrong info 0x%x\n", __func__, rxsp->ds_info); #endif return HAL_EINPROGRESS; } rxs->rs_status = 0; rxs->rs_flags = 0; + rxs->rs_phyerr = 0; rxs->rs_datalen = rxsp->status2 & AR_data_len; rxs->rs_tstamp = rxsp->status3; /* XXX what about key_cache_miss? */ rxs->rs_rssi = MS(rxsp->status5, AR_rx_rssi_combined); rxs->rs_rssi_ctl[0] = MS(rxsp->status1, AR_rx_rssi_ant00); rxs->rs_rssi_ctl[1] = MS(rxsp->status1, AR_rx_rssi_ant01); rxs->rs_rssi_ctl[2] = MS(rxsp->status1, AR_rx_rssi_ant02); rxs->rs_rssi_ext[0] = MS(rxsp->status5, AR_rx_rssi_ant10); rxs->rs_rssi_ext[1] = MS(rxsp->status5, AR_rx_rssi_ant11); rxs->rs_rssi_ext[2] = MS(rxsp->status5, AR_rx_rssi_ant12); if (rxsp->status11 & AR_rx_key_idx_valid) { rxs->rs_keyix = MS(rxsp->status11, AR_key_idx); } else { rxs->rs_keyix = HAL_RXKEYIX_INVALID; } /* NB: caller expected to do rate table mapping */ rxs->rs_rate = MS(rxsp->status1, AR_rx_rate); rxs->rs_more = (rxsp->status2 & AR_rx_more) ? 1 : 0; rxs->rs_isaggr = (rxsp->status11 & AR_rx_aggr) ? 1 : 0; rxs->rs_moreaggr = (rxsp->status11 & AR_rx_more_aggr) ? 1 : 0; rxs->rs_antenna = (MS(rxsp->status4, AR_rx_antenna) & 0x7); rxs->rs_flags = (rxsp->status11 & AR_apsd_trig) ? HAL_RX_IS_APSD : 0; rxs->rs_flags |= (rxsp->status4 & AR_gi) ? HAL_RX_GI : 0; rxs->rs_flags |= (rxsp->status4 & AR_2040) ? HAL_RX_2040 : 0; /* Copy EVM information */ rxs->rs_evm0 = rxsp->status6; rxs->rs_evm1 = rxsp->status7; rxs->rs_evm2 = rxsp->status8; rxs->rs_evm3 = rxsp->status9; rxs->rs_evm4 = (rxsp->status10 & 0xffff); if (rxsp->status11 & AR_pre_delim_crc_err) { rxs->rs_flags |= HAL_RX_DELIM_CRC_PRE; } if (rxsp->status11 & AR_post_delim_crc_err) { rxs->rs_flags |= HAL_RX_DELIM_CRC_POST; } if (rxsp->status11 & AR_decrypt_busy_err) { rxs->rs_flags |= HAL_RX_DECRYPT_BUSY; } if (rxsp->status11 & AR_hi_rx_chain) { rxs->rs_flags |= HAL_RX_HI_RX_CHAIN; } if (rxsp->status11 & AR_key_miss) { rxs->rs_status |= HAL_RXERR_KEYMISS; } if ((rxsp->status11 & AR_rx_frame_ok) == 0) { /* * These four bits should not be set together. The * 9300 spec states a Michael error can only occur if * decrypt_crc_err not set (and TKIP is used). Experience * indicates however that you can also get Michael errors * when a CRC error is detected, but these are specious. * Consequently we filter them out here so we don't * confuse and/or complicate drivers. */ + if (rxsp->status11 & AR_crc_err) { rxs->rs_status |= HAL_RXERR_CRC; /* - * ignore CRC flag for spectral phy reports + * ignore CRC flag for phy reports */ if (rxsp->status11 & AR_phyerr) { u_int phyerr = MS(rxsp->status11, AR_phy_err_code); - if (phyerr == HAL_PHYERR_SPECTRAL) { - rxs->rs_status |= HAL_RXERR_PHY; - rxs->rs_phyerr = phyerr; - } + rxs->rs_status |= HAL_RXERR_PHY; + rxs->rs_phyerr = phyerr; } } else if (rxsp->status11 & AR_phyerr) { u_int phyerr; /* * Packets with OFDM_RESTART on post delimiter are CRC OK and * usable and MAC ACKs them. * To avoid packet from being lost, we remove the PHY Err flag * so that lmac layer does not drop them. * (EV 70071) */ phyerr = MS(rxsp->status11, AR_phy_err_code); if ((phyerr == HAL_PHYERR_OFDM_RESTART) && (rxsp->status11 & AR_post_delim_crc_err)) { rxs->rs_phyerr = 0; } else { rxs->rs_status |= HAL_RXERR_PHY; rxs->rs_phyerr = phyerr; } } else if (rxsp->status11 & AR_decrypt_crc_err) { rxs->rs_status |= HAL_RXERR_DECRYPT; } else if (rxsp->status11 & AR_michael_err) { rxs->rs_status |= HAL_RXERR_MIC; } } - +#if 0 + rxs->rs_channel = AH_PRIVATE(ah)->ah_curchan->channel; +#endif return HAL_OK; } HAL_STATUS ar9300_proc_rx_desc(struct ath_hal *ah, struct ath_desc *ds, u_int32_t pa, struct ath_desc *nds, u_int64_t tsf, struct ath_rx_status *rxs) { return HAL_ENOTSUPP; } /* * rx path in ISR is different for ar9300 from ar5416, and * ath_rx_proc_descfast will not be called if edmasupport is true. * So this function ath_hal_get_rxkeyidx will not be * called for ar9300. * This function in ar9300's HAL is just a stub one because we need * to link something to the callback interface of the HAL module. */ HAL_STATUS ar9300_get_rx_key_idx(struct ath_hal *ah, struct ath_desc *ds, u_int8_t *keyix, u_int8_t *status) { *status = 0; *keyix = HAL_RXKEYIX_INVALID; return HAL_ENOTSUPP; } Index: projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_reset.c =================================================================== --- projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_reset.c (revision 278776) +++ projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_reset.c (revision 278777) @@ -1,6203 +1,6404 @@ /* * Copyright (c) 2013 Qualcomm Atheros, Inc. * * 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 "opt_ah.h" #include "ah.h" #include "ah_internal.h" #include "ah_devid.h" #include "ah_desc.h" #include "ar9300.h" #include "ar9300reg.h" #include "ar9300phy.h" #include "ar9300desc.h" #define FIX_NOISE_FLOOR 1 - /* Additional Time delay to wait after activiting the Base band */ #define BASE_ACTIVATE_DELAY 100 /* usec */ #define RTC_PLL_SETTLE_DELAY 100 /* usec */ #define COEF_SCALE_S 24 #define HT40_CHANNEL_CENTER_SHIFT 10 /* MHz */ #define DELPT 32 /* XXX Duplicates! (in ar9300desc.h) */ #if 0 extern HAL_BOOL ar9300_reset_tx_queue(struct ath_hal *ah, u_int q); extern u_int32_t ar9300_num_tx_pending(struct ath_hal *ah, u_int q); #endif #define MAX_MEASUREMENT 8 #define MAXIQCAL 3 struct coeff_t { int32_t mag_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT][MAXIQCAL]; int32_t phs_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT][MAXIQCAL]; int32_t iqc_coeff[2]; int last_nmeasurement; HAL_BOOL last_cal; }; static HAL_BOOL ar9300_tx_iq_cal_hw_run(struct ath_hal *ah); static void ar9300_tx_iq_cal_post_proc(struct ath_hal *ah,HAL_CHANNEL_INTERNAL *ichan, int iqcal_idx, int max_iqcal, HAL_BOOL is_cal_reusable, HAL_BOOL apply_last_corr); static void ar9300_tx_iq_cal_outlier_detection(struct ath_hal *ah,HAL_CHANNEL_INTERNAL *ichan, u_int32_t num_chains, struct coeff_t *coeff, HAL_BOOL is_cal_reusable); #if ATH_SUPPORT_CAL_REUSE static void ar9300_tx_iq_cal_apply(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *ichan); #endif static inline void ar9300_prog_ini(struct ath_hal *ah, struct ar9300_ini_array *ini_arr, int column); static inline void ar9300_set_rf_mode(struct ath_hal *ah, struct ieee80211_channel *chan); static inline HAL_BOOL ar9300_init_cal(struct ath_hal *ah, struct ieee80211_channel *chan, HAL_BOOL skip_if_none, HAL_BOOL apply_last_corr); static inline void ar9300_init_user_settings(struct ath_hal *ah); #ifdef HOST_OFFLOAD /* * For usb offload solution, some USB registers must be tuned * to gain better stability/performance but these registers * might be changed while doing wlan reset so do this here */ #define WAR_USB_DISABLE_PLL_LOCK_DETECT(__ah) \ do { \ if (AR_SREV_HORNET(__ah) || AR_SREV_WASP(__ah)) { \ volatile u_int32_t *usb_ctrl_r1 = (u_int32_t *) 0xb8116c84; \ volatile u_int32_t *usb_ctrl_r2 = (u_int32_t *) 0xb8116c88; \ *usb_ctrl_r1 = (*usb_ctrl_r1 & 0xffefffff); \ *usb_ctrl_r2 = (*usb_ctrl_r2 & 0xfc1fffff) | (1 << 21) | (3 << 22); \ } \ } while (0) #else #define WAR_USB_DISABLE_PLL_LOCK_DETECT(__ah) #endif static inline void ar9300_attach_hw_platform(struct ath_hal *ah) { struct ath_hal_9300 *ahp = AH9300(ah); ahp->ah_hwp = HAL_TRUE_CHIP; return; } /* Adjust various register settings based on half/quarter rate clock setting. * This includes: +USEC, TX/RX latency, * + IFS params: slot, eifs, misc etc. * SIFS stays the same. */ static void ar9300_set_ifs_timing(struct ath_hal *ah, struct ieee80211_channel *chan) { u_int32_t tx_lat, rx_lat, usec, slot, regval, eifs; regval = OS_REG_READ(ah, AR_USEC); regval &= ~(AR_USEC_RX_LATENCY | AR_USEC_TX_LATENCY | AR_USEC_USEC); if (IEEE80211_IS_CHAN_HALF(chan)) { /* half rates */ slot = ar9300_mac_to_clks(ah, AR_SLOT_HALF); eifs = ar9300_mac_to_clks(ah, AR_EIFS_HALF); if (IS_5GHZ_FAST_CLOCK_EN(ah, chan)) { /* fast clock */ rx_lat = SM(AR_RX_LATENCY_HALF_FAST_CLOCK, AR_USEC_RX_LATENCY); tx_lat = SM(AR_TX_LATENCY_HALF_FAST_CLOCK, AR_USEC_TX_LATENCY); usec = SM(AR_USEC_HALF_FAST_CLOCK, AR_USEC_USEC); } else { rx_lat = SM(AR_RX_LATENCY_HALF, AR_USEC_RX_LATENCY); tx_lat = SM(AR_TX_LATENCY_HALF, AR_USEC_TX_LATENCY); usec = SM(AR_USEC_HALF, AR_USEC_USEC); } } else { /* quarter rate */ slot = ar9300_mac_to_clks(ah, AR_SLOT_QUARTER); eifs = ar9300_mac_to_clks(ah, AR_EIFS_QUARTER); if (IS_5GHZ_FAST_CLOCK_EN(ah, chan)) { /* fast clock */ rx_lat = SM(AR_RX_LATENCY_QUARTER_FAST_CLOCK, AR_USEC_RX_LATENCY); tx_lat = SM(AR_TX_LATENCY_QUARTER_FAST_CLOCK, AR_USEC_TX_LATENCY); usec = SM(AR_USEC_QUARTER_FAST_CLOCK, AR_USEC_USEC); } else { rx_lat = SM(AR_RX_LATENCY_QUARTER, AR_USEC_RX_LATENCY); tx_lat = SM(AR_TX_LATENCY_QUARTER, AR_USEC_TX_LATENCY); usec = SM(AR_USEC_QUARTER, AR_USEC_USEC); } } OS_REG_WRITE(ah, AR_USEC, (usec | regval | tx_lat | rx_lat)); OS_REG_WRITE(ah, AR_D_GBL_IFS_SLOT, slot); OS_REG_WRITE(ah, AR_D_GBL_IFS_EIFS, eifs); } /* * This inline function configures the chip either * to encrypt/decrypt management frames or pass thru */ static inline void ar9300_init_mfp(struct ath_hal * ah) { u_int32_t mfpcap, mfp_qos; ath_hal_getcapability(ah, HAL_CAP_MFP, 0, &mfpcap); if (mfpcap == HAL_MFP_QOSDATA) { /* Treat like legacy hardware. Do not touch the MFP registers. */ HALDEBUG(ah, HAL_DEBUG_RESET, "%s forced to use QOSDATA\n", __func__); return; } /* MFP support (Sowl 1.0 or greater) */ if (mfpcap == HAL_MFP_HW_CRYPTO) { /* configure hardware MFP support */ HALDEBUG(ah, HAL_DEBUG_RESET, "%s using HW crypto\n", __func__); OS_REG_RMW_FIELD(ah, AR_AES_MUTE_MASK1, AR_AES_MUTE_MASK1_FC_MGMT, AR_AES_MUTE_MASK1_FC_MGMT_MFP); OS_REG_RMW(ah, AR_PCU_MISC_MODE2, AR_PCU_MISC_MODE2_MGMT_CRYPTO_ENABLE, AR_PCU_MISC_MODE2_NO_CRYPTO_FOR_NON_DATA_PKT); /* * Mask used to construct AAD for CCMP-AES * Cisco spec defined bits 0-3 as mask * IEEE802.11w defined as bit 4. */ if (ath_hal_get_mfp_qos(ah)) { mfp_qos = AR_MFP_QOS_MASK_IEEE; } else { mfp_qos = AR_MFP_QOS_MASK_CISCO; } OS_REG_RMW_FIELD(ah, AR_PCU_MISC_MODE2, AR_PCU_MISC_MODE2_MGMT_QOS, mfp_qos); } else if (mfpcap == HAL_MFP_PASSTHRU) { /* Disable en/decrypt by hardware */ HALDEBUG(ah, HAL_DEBUG_RESET, "%s using passthru\n", __func__); OS_REG_RMW(ah, AR_PCU_MISC_MODE2, AR_PCU_MISC_MODE2_NO_CRYPTO_FOR_NON_DATA_PKT, AR_PCU_MISC_MODE2_MGMT_CRYPTO_ENABLE); } } void ar9300_get_channel_centers(struct ath_hal *ah, const struct ieee80211_channel *chan, CHAN_CENTERS *centers) { int8_t extoff; struct ath_hal_9300 *ahp = AH9300(ah); HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); if (!IEEE80211_IS_CHAN_HT40(chan)) { centers->ctl_center = centers->ext_center = centers->synth_center = ichan->channel; return; } HALASSERT(IEEE80211_IS_CHAN_HT40(chan)); /* * In 20/40 phy mode, the center frequency is * "between" the primary and extension channels. */ if (IEEE80211_IS_CHAN_HT40U(chan)) { centers->synth_center = ichan->channel + HT40_CHANNEL_CENTER_SHIFT; extoff = 1; } else { centers->synth_center = ichan->channel - HT40_CHANNEL_CENTER_SHIFT; extoff = -1; } centers->ctl_center = centers->synth_center - (extoff * HT40_CHANNEL_CENTER_SHIFT); centers->ext_center = centers->synth_center + (extoff * ((ahp->ah_ext_prot_spacing == HAL_HT_EXTPROTSPACING_20) ? HT40_CHANNEL_CENTER_SHIFT : 15)); } /* * Read the noise-floor values from the HW. * Specifically, read the minimum clear-channel assessment value for * each chain, for both the control and extension channels. * (The received power level during clear-channel periods is the * noise floor.) * These noise floor values computed by the HW will be stored in the * NF history buffer. * The HW sometimes produces bogus NF values. To avoid using these * bogus values, the NF data is (a) range-limited, and (b) filtered. * However, this data-processing is done when reading the NF values * out of the history buffer. The history buffer stores the raw values. * This allows the NF history buffer to be used to check for interference. * A single high NF reading might be a bogus HW value, but if the NF * readings are consistently high, it must be due to interference. * This is the purpose of storing raw NF values in the history buffer, * rather than processed values. By looking at a history of NF values * that have not been range-limited, we can check if they are consistently * high (due to interference). */ #define AH_NF_SIGN_EXTEND(nf) \ ((nf) & 0x100) ? \ 0 - (((nf) ^ 0x1ff) + 1) : \ (nf) void ar9300_upload_noise_floor(struct ath_hal *ah, int is_2g, int16_t nfarray[HAL_NUM_NF_READINGS]) { int16_t nf; int chan, chain; u_int32_t regs[HAL_NUM_NF_READINGS] = { /* control channel */ AR_PHY_CCA_0, /* chain 0 */ AR_PHY_CCA_1, /* chain 1 */ AR_PHY_CCA_2, /* chain 2 */ /* extension channel */ AR_PHY_EXT_CCA, /* chain 0 */ AR_PHY_EXT_CCA_1, /* chain 1 */ AR_PHY_EXT_CCA_2, /* chain 2 */ }; u_int8_t chainmask; /* * Within a given channel (ctl vs. ext), the CH0, CH1, and CH2 * masks and shifts are the same, though they differ for the * control vs. extension channels. */ u_int32_t masks[2] = { AR_PHY_MINCCA_PWR, /* control channel */ AR_PHY_EXT_MINCCA_PWR, /* extention channel */ }; u_int8_t shifts[2] = { AR_PHY_MINCCA_PWR_S, /* control channel */ AR_PHY_EXT_MINCCA_PWR_S, /* extention channel */ }; /* * Force NF calibration for all chains. */ if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah) || AR_SREV_APHRODITE(ah)) { chainmask = 0x01; } else if (AR_SREV_WASP(ah) || AR_SREV_JUPITER(ah)) { chainmask = 0x03; } else { chainmask = 0x07; } for (chan = 0; chan < 2 /*ctl,ext*/; chan++) { for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { int i; if (!((chainmask >> chain) & 0x1)) { continue; } i = chan * AR9300_MAX_CHAINS + chain; nf = (OS_REG_READ(ah, regs[i]) & masks[chan]) >> shifts[chan]; nfarray[i] = AH_NF_SIGN_EXTEND(nf); } } } /* ar9300_get_min_cca_pwr - * Used by the scan function for a quick read of the noise floor. * This is used to detect presence of CW interference such as video bridge. * The noise floor is assumed to have been already started during reset * called during channel change. The function checks if the noise floor * reading is done. In case it has been done, it reads the noise floor value. * If the noise floor calibration has not been finished, it assumes this is * due to presence of CW interference an returns a high value for noise floor, * derived from the CW interference threshold + margin fudge factor. */ #define BAD_SCAN_NF_MARGIN (30) int16_t ar9300_get_min_cca_pwr(struct ath_hal *ah) { int16_t nf; // struct ath_hal_private *ahpriv = AH_PRIVATE(ah); + if ((OS_REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) == 0) { nf = MS(OS_REG_READ(ah, AR_PHY_CCA_0), AR9280_PHY_MINCCA_PWR); if (nf & 0x100) { nf = 0 - ((nf ^ 0x1ff) + 1); } } else { /* NF calibration is not done, assume CW interference */ nf = AH9300(ah)->nfp->nominal + AH9300(ah)->nf_cw_int_delta + BAD_SCAN_NF_MARGIN; } return nf; } /* * Noise Floor values for all chains. * Most recently updated values from the NF history buffer are used. */ void ar9300_chain_noise_floor(struct ath_hal *ah, int16_t *nf_buf, struct ieee80211_channel *chan, int is_scan) { struct ath_hal_9300 *ahp = AH9300(ah); int i, nf_hist_len, recent_nf_index = 0; HAL_NFCAL_HIST_FULL *h; u_int8_t rx_chainmask = ahp->ah_rx_chainmask | (ahp->ah_rx_chainmask << 3); HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); HALASSERT(ichan); #ifdef ATH_NF_PER_CHAN /* Fill 0 if valid internal channel is not found */ if (ichan == AH_NULL) { OS_MEMZERO(nf_buf, sizeof(nf_buf[0])*HAL_NUM_NF_READINGS); return; } h = &ichan->nf_cal_hist; nf_hist_len = HAL_NF_CAL_HIST_LEN_FULL; #else /* * If a scan is not in progress, then the most recent value goes * into ahpriv->nf_cal_hist. If a scan is in progress, then * the most recent value goes into ichan->nf_cal_hist. * Thus, return the value from ahpriv->nf_cal_hist if there's * no scan, and if the specified channel is the current channel. * Otherwise, return the noise floor from ichan->nf_cal_hist. */ if ((!is_scan) && chan == AH_PRIVATE(ah)->ah_curchan) { h = &AH_PRIVATE(ah)->nf_cal_hist; nf_hist_len = HAL_NF_CAL_HIST_LEN_FULL; } else { /* Fill 0 if valid internal channel is not found */ if (ichan == AH_NULL) { OS_MEMZERO(nf_buf, sizeof(nf_buf[0])*HAL_NUM_NF_READINGS); return; } /* * It is okay to treat a HAL_NFCAL_HIST_SMALL struct as if it were a * HAL_NFCAL_HIST_FULL struct, as long as only the index 0 of the * nf_cal_buffer is used (nf_cal_buffer[0][0:HAL_NUM_NF_READINGS-1]) */ h = (HAL_NFCAL_HIST_FULL *) &ichan->nf_cal_hist; nf_hist_len = HAL_NF_CAL_HIST_LEN_SMALL; } #endif /* Get most recently updated values from nf cal history buffer */ recent_nf_index = (h->base.curr_index) ? h->base.curr_index - 1 : nf_hist_len - 1; for (i = 0; i < HAL_NUM_NF_READINGS; i++) { /* Fill 0 for unsupported chains */ if (!(rx_chainmask & (1 << i))) { nf_buf[i] = 0; continue; } nf_buf[i] = h->nf_cal_buffer[recent_nf_index][i]; } } +/* + * Return the current NF value in register. + * If the current NF cal is not completed, return 0. + */ +int16_t ar9300_get_nf_from_reg(struct ath_hal *ah, struct ieee80211_channel *chan, int wait_time) +{ + int16_t nfarray[HAL_NUM_NF_READINGS] = {0}; + int is_2g = 0; + HAL_CHANNEL_INTERNAL *ichan = NULL; + ichan = ath_hal_checkchannel(ah, chan); + if (ichan == NULL) + return (0); + + if (wait_time <= 0) { + return 0; + } + + if (!ath_hal_waitfor(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF, 0, wait_time)) { + ath_hal_printf(ah, "%s: NF cal is not complete in %dus", __func__, wait_time); + return 0; + } + is_2g = !! (IS_CHAN_2GHZ(ichan)); + ar9300_upload_noise_floor(ah, is_2g, nfarray); + + return nfarray[0]; +} + /* * Pick up the medium one in the noise floor buffer and update the * corresponding range for valid noise floor values */ static int16_t ar9300_get_nf_hist_mid(struct ath_hal *ah, HAL_NFCAL_HIST_FULL *h, int reading, int hist_len) { int16_t nfval; int16_t sort[HAL_NF_CAL_HIST_LEN_FULL]; /* upper bound for hist_len */ int i, j; + for (i = 0; i < hist_len; i++) { sort[i] = h->nf_cal_buffer[i][reading]; HALDEBUG(ah, HAL_DEBUG_NFCAL, "nf_cal_buffer[%d][%d] = %d\n", i, reading, (int)sort[i]); } for (i = 0; i < hist_len - 1; i++) { for (j = 1; j < hist_len - i; j++) { if (sort[j] > sort[j - 1]) { nfval = sort[j]; sort[j] = sort[j - 1]; sort[j - 1] = nfval; } } } nfval = sort[(hist_len - 1) >> 1]; return nfval; } static int16_t ar9300_limit_nf_range(struct ath_hal *ah, int16_t nf) { if (nf < AH9300(ah)->nfp->min) { return AH9300(ah)->nfp->nominal; } else if (nf > AH9300(ah)->nfp->max) { return AH9300(ah)->nfp->max; } return nf; } #ifndef ATH_NF_PER_CHAN inline static void ar9300_reset_nf_hist_buff(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *ichan) { HAL_CHAN_NFCAL_HIST *h = &ichan->nf_cal_hist; HAL_NFCAL_HIST_FULL *home = &AH_PRIVATE(ah)->nf_cal_hist; int i; - + /* * Copy the value for the channel in question into the home-channel * NF history buffer. The channel NF is probably a value filled in by * a prior background channel scan, but if no scan has been done then * it is the nominal noise floor filled in by ath_hal_init_NF_buffer * for this chip and the channel's band. * Replicate this channel NF into all entries of the home-channel NF * history buffer. * If the channel NF was filled in by a channel scan, it has not had * bounds limits applied to it yet - do so now. It is important to * apply bounds limits to the priv_nf value that gets loaded into the * WLAN chip's min_cca_pwr register field. It is also necessary to * apply bounds limits to the nf_cal_buffer[] elements. Since we are * replicating a single NF reading into all nf_cal_buffer elements, * if the single reading were above the CW_INT threshold, the CW_INT * check in ar9300_get_nf would immediately conclude that CW interference * is present, even though we're not supposed to set CW_INT unless * NF values are _consistently_ above the CW_INT threshold. * Applying the bounds limits to the nf_cal_buffer contents fixes this * problem. */ for (i = 0; i < HAL_NUM_NF_READINGS; i ++) { int j; int16_t nf; /* * No need to set curr_index, since it already has a value in * the range [0..HAL_NF_CAL_HIST_LEN_FULL), and all nf_cal_buffer * values will be the same. */ nf = ar9300_limit_nf_range(ah, h->nf_cal_buffer[0][i]); for (j = 0; j < HAL_NF_CAL_HIST_LEN_FULL; j++) { home->nf_cal_buffer[j][i] = nf; } AH_PRIVATE(ah)->nf_cal_hist.base.priv_nf[i] = nf; } } #endif /* * Update the noise floor buffer as a ring buffer */ static int16_t ar9300_update_nf_hist_buff(struct ath_hal *ah, HAL_NFCAL_HIST_FULL *h, int16_t *nfarray, int hist_len) { int i, nr; int16_t nf_no_lim_chain0; nf_no_lim_chain0 = ar9300_get_nf_hist_mid(ah, h, 0, hist_len); HALDEBUG(ah, HAL_DEBUG_NFCAL, "%s[%d] BEFORE\n", __func__, __LINE__); for (nr = 0; nr < HAL_NF_CAL_HIST_LEN_FULL; nr++) { for (i = 0; i < HAL_NUM_NF_READINGS; i++) { HALDEBUG(ah, HAL_DEBUG_NFCAL, "nf_cal_buffer[%d][%d] = %d\n", nr, i, (int)h->nf_cal_buffer[nr][i]); } } for (i = 0; i < HAL_NUM_NF_READINGS; i++) { h->nf_cal_buffer[h->base.curr_index][i] = nfarray[i]; h->base.priv_nf[i] = ar9300_limit_nf_range( ah, ar9300_get_nf_hist_mid(ah, h, i, hist_len)); } HALDEBUG(ah, HAL_DEBUG_NFCAL, "%s[%d] AFTER\n", __func__, __LINE__); for (nr = 0; nr < HAL_NF_CAL_HIST_LEN_FULL; nr++) { for (i = 0; i < HAL_NUM_NF_READINGS; i++) { HALDEBUG(ah, HAL_DEBUG_NFCAL, "nf_cal_buffer[%d][%d] = %d\n", nr, i, (int)h->nf_cal_buffer[nr][i]); } } if (++h->base.curr_index >= hist_len) { h->base.curr_index = 0; } return nf_no_lim_chain0; } #ifdef UNUSED static HAL_BOOL get_noise_floor_thresh(struct ath_hal *ah, const HAL_CHANNEL_INTERNAL *chan, int16_t *nft) { struct ath_hal_9300 *ahp = AH9300(ah); + switch (chan->channel_flags & CHANNEL_ALL_NOTURBO) { case CHANNEL_A: case CHANNEL_A_HT20: case CHANNEL_A_HT40PLUS: case CHANNEL_A_HT40MINUS: *nft = (int8_t)ar9300_eeprom_get(ahp, EEP_NFTHRESH_5); break; case CHANNEL_B: case CHANNEL_G: case CHANNEL_G_HT20: case CHANNEL_G_HT40PLUS: case CHANNEL_G_HT40MINUS: *nft = (int8_t)ar9300_eeprom_get(ahp, EEP_NFTHRESH_2); break; default: HALDEBUG(ah, HAL_DEBUG_CHANNEL, "%s: invalid channel flags 0x%x\n", __func__, chan->channel_flags); return AH_FALSE; } return AH_TRUE; } #endif /* * Read the NF and check it against the noise floor threshhold */ #define IS(_c, _f) (((_c)->channel_flags & _f) || 0) static int ar9300_store_new_nf(struct ath_hal *ah, struct ieee80211_channel *chan, int is_scan) { // struct ath_hal_private *ahpriv = AH_PRIVATE(ah); int nf_hist_len; int16_t nf_no_lim; int16_t nfarray[HAL_NUM_NF_READINGS] = {0}; HAL_NFCAL_HIST_FULL *h; int is_2g = 0; HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); struct ath_hal_9300 *ahp = AH9300(ah); if (OS_REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) { u_int32_t tsf32, nf_cal_dur_tsf; /* * The reason the NF calibration did not complete may just be that * not enough time has passed since the NF calibration was started, * because under certain conditions (when first moving to a new * channel) the NF calibration may be checked very repeatedly. * Or, there may be CW interference keeping the NF calibration * from completing. Check the delta time between when the NF * calibration was started and now to see whether the NF calibration * should have already completed (but hasn't, probably due to CW * interference), or hasn't had enough time to finish yet. */ /* * AH_NF_CAL_DUR_MAX_TSF - A conservative maximum time that the * HW should need to finish a NF calibration. If the HW * does not complete a NF calibration within this time period, * there must be a problem - probably CW interference. * AH_NF_CAL_PERIOD_MAX_TSF - A conservative maximum time between * check of the HW's NF calibration being finished. * If the difference between the current TSF and the TSF * recorded when the NF calibration started is larger than this * value, the TSF must have been reset. * In general, we expect the TSF to only be reset during * regular operation for STAs, not for APs. However, an * AP's TSF could be reset when joining an IBSS. * There's an outside chance that this could result in the * CW_INT flag being erroneously set, if the TSF adjustment * is smaller than AH_NF_CAL_PERIOD_MAX_TSF but larger than * AH_NF_CAL_DUR_TSF. However, even if this does happen, * it shouldn't matter, as the IBSS case shouldn't be * concerned about CW_INT. */ /* AH_NF_CAL_DUR_TSF - 90 sec in usec units */ #define AH_NF_CAL_DUR_TSF (90 * 1000 * 1000) /* AH_NF_CAL_PERIOD_MAX_TSF - 180 sec in usec units */ #define AH_NF_CAL_PERIOD_MAX_TSF (180 * 1000 * 1000) /* wraparound handled by using unsigned values */ tsf32 = ar9300_get_tsf32(ah); nf_cal_dur_tsf = tsf32 - AH9300(ah)->nf_tsf32; if (nf_cal_dur_tsf > AH_NF_CAL_PERIOD_MAX_TSF) { /* * The TSF must have gotten reset during the NF cal - * just reset the NF TSF timestamp, so the next time * this function is called, the timestamp comparison * will be valid. */ AH9300(ah)->nf_tsf32 = tsf32; } else if (nf_cal_dur_tsf > AH_NF_CAL_DUR_TSF) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: NF did not complete in calibration window\n", __func__); /* the NF incompletion is probably due to CW interference */ chan->ic_state |= IEEE80211_CHANSTATE_CWINT; } return 0; /* HW's NF measurement not finished */ } HALDEBUG(ah, HAL_DEBUG_NFCAL, "%s[%d] chan %d\n", __func__, __LINE__, ichan->channel); is_2g = !! IS_CHAN_2GHZ(ichan); ar9300_upload_noise_floor(ah, is_2g, nfarray); /* Update the NF buffer for each chain masked by chainmask */ #ifdef ATH_NF_PER_CHAN h = &ichan->nf_cal_hist; nf_hist_len = HAL_NF_CAL_HIST_LEN_FULL; #else if (is_scan) { /* * This channel's NF cal info is just a HAL_NFCAL_HIST_SMALL struct * rather than a HAL_NFCAL_HIST_FULL struct. * As long as we only use the first history element of nf_cal_buffer * (nf_cal_buffer[0][0:HAL_NUM_NF_READINGS-1]), we can use * HAL_NFCAL_HIST_SMALL and HAL_NFCAL_HIST_FULL interchangeably. */ h = (HAL_NFCAL_HIST_FULL *) &ichan->nf_cal_hist; nf_hist_len = HAL_NF_CAL_HIST_LEN_SMALL; } else { h = &AH_PRIVATE(ah)->nf_cal_hist; nf_hist_len = HAL_NF_CAL_HIST_LEN_FULL; } #endif /* * nf_no_lim = median value from NF history buffer without bounds limits, * priv_nf = median value from NF history buffer with bounds limits. */ nf_no_lim = ar9300_update_nf_hist_buff(ah, h, nfarray, nf_hist_len); ichan->rawNoiseFloor = h->base.priv_nf[0]; /* check if there is interference */ // ichan->channel_flags &= (~CHANNEL_CW_INT); /* * Use AR9300_EMULATION to check for emulation purpose as PCIE Device ID * 0xABCD is recognized as valid Osprey as WAR in some EVs. */ if (nf_no_lim > ahp->nfp->nominal + ahp->nf_cw_int_delta) { /* * Since this CW interference check is being applied to the * median element of the NF history buffer, this indicates that * the CW interference is persistent. A single high NF reading * will not show up in the median, and thus will not cause the * CW_INT flag to be set. */ HALDEBUG(ah, HAL_DEBUG_NFCAL, "%s: NF Cal: CW interferer detected through NF: %d\n", __func__, nf_no_lim); chan->ic_state |= IEEE80211_CHANSTATE_CWINT; } return 1; /* HW's NF measurement finished */ } #undef IS static inline void ar9300_get_delta_slope_values(struct ath_hal *ah, u_int32_t coef_scaled, u_int32_t *coef_mantissa, u_int32_t *coef_exponent) { u_int32_t coef_exp, coef_man; /* * ALGO -> coef_exp = 14-floor(log2(coef)); * floor(log2(x)) is the highest set bit position */ for (coef_exp = 31; coef_exp > 0; coef_exp--) { if ((coef_scaled >> coef_exp) & 0x1) { break; } } /* A coef_exp of 0 is a legal bit position but an unexpected coef_exp */ HALASSERT(coef_exp); coef_exp = 14 - (coef_exp - COEF_SCALE_S); /* * ALGO -> coef_man = floor(coef* 2^coef_exp+0.5); * The coefficient is already shifted up for scaling */ coef_man = coef_scaled + (1 << (COEF_SCALE_S - coef_exp - 1)); *coef_mantissa = coef_man >> (COEF_SCALE_S - coef_exp); *coef_exponent = coef_exp - 16; } #define MAX_ANALOG_START 319 /* XXX */ /* * Delta slope coefficient computation. * Required for OFDM operation. */ static void ar9300_set_delta_slope(struct ath_hal *ah, struct ieee80211_channel *chan) { u_int32_t coef_scaled, ds_coef_exp, ds_coef_man; u_int32_t fclk = COEFF; /* clock * 2.5 */ u_int32_t clock_mhz_scaled = 0x1000000 * fclk; CHAN_CENTERS centers; /* * half and quarter rate can divide the scaled clock by 2 or 4 * scale for selected channel bandwidth */ if (IEEE80211_IS_CHAN_HALF(chan)) { clock_mhz_scaled = clock_mhz_scaled >> 1; } else if (IEEE80211_IS_CHAN_QUARTER(chan)) { clock_mhz_scaled = clock_mhz_scaled >> 2; } /* * ALGO -> coef = 1e8/fcarrier*fclock/40; * scaled coef to provide precision for this floating calculation */ ar9300_get_channel_centers(ah, chan, ¢ers); coef_scaled = clock_mhz_scaled / centers.synth_center; ar9300_get_delta_slope_values(ah, coef_scaled, &ds_coef_man, &ds_coef_exp); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING3, AR_PHY_TIMING3_DSC_MAN, ds_coef_man); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING3, AR_PHY_TIMING3_DSC_EXP, ds_coef_exp); /* * For Short GI, * scaled coeff is 9/10 that of normal coeff */ coef_scaled = (9 * coef_scaled) / 10; ar9300_get_delta_slope_values(ah, coef_scaled, &ds_coef_man, &ds_coef_exp); /* for short gi */ OS_REG_RMW_FIELD(ah, AR_PHY_SGI_DELTA, AR_PHY_SGI_DSC_MAN, ds_coef_man); OS_REG_RMW_FIELD(ah, AR_PHY_SGI_DELTA, AR_PHY_SGI_DSC_EXP, ds_coef_exp); } #define IS(_c, _f) (IEEE80211_IS_ ## _f(_c)) /* * XXX FreeBSD: This should be turned into something generic in ath_hal! */ HAL_CHANNEL_INTERNAL * ar9300_check_chan(struct ath_hal *ah, const struct ieee80211_channel *chan) { if (chan == NULL) { return AH_NULL; } if ((IS(chan, CHAN_2GHZ) ^ IS(chan, CHAN_5GHZ)) == 0) { HALDEBUG(ah, HAL_DEBUG_CHANNEL, "%s: invalid channel %u/0x%x; not marked as 2GHz or 5GHz\n", __func__, chan->ic_freq , chan->ic_flags); return AH_NULL; } /* * FreeBSD sets multiple flags, so this will fail. */ #if 0 if ((IS(chan, CHAN_OFDM) ^ IS(chan, CHAN_CCK) ^ IS(chan, CHAN_DYN) ^ IS(chan, CHAN_HT20) ^ IS(chan, CHAN_HT40U) ^ IS(chan, CHAN_HT40D)) == 0) { HALDEBUG(ah, HAL_DEBUG_CHANNEL, "%s: invalid channel %u/0x%x; not marked as " "OFDM or CCK or DYN or HT20 or HT40PLUS or HT40MINUS\n", __func__, chan->ic_freq , chan->ic_flags); return AH_NULL; } #endif return (ath_hal_checkchannel(ah, chan)); } #undef IS static void ar9300_set_11n_regs(struct ath_hal *ah, struct ieee80211_channel *chan, HAL_HT_MACMODE macmode) { u_int32_t phymode; // struct ath_hal_9300 *ahp = AH9300(ah); u_int32_t enable_dac_fifo; /* XXX */ enable_dac_fifo = OS_REG_READ(ah, AR_PHY_GEN_CTRL) & AR_PHY_GC_ENABLE_DAC_FIFO; /* Enable 11n HT, 20 MHz */ phymode = AR_PHY_GC_HT_EN | AR_PHY_GC_SINGLE_HT_LTF1 | AR_PHY_GC_SHORT_GI_40 | enable_dac_fifo; /* Configure baseband for dynamic 20/40 operation */ if (IEEE80211_IS_CHAN_HT40(chan)) { phymode |= AR_PHY_GC_DYN2040_EN; /* Configure control (primary) channel at +-10MHz */ if (IEEE80211_IS_CHAN_HT40U(chan)) { phymode |= AR_PHY_GC_DYN2040_PRI_CH; } #if 0 /* Configure 20/25 spacing */ if (ahp->ah_ext_prot_spacing == HAL_HT_EXTPROTSPACING_25) { phymode |= AR_PHY_GC_DYN2040_EXT_CH; } #endif } /* make sure we preserve INI settings */ phymode |= OS_REG_READ(ah, AR_PHY_GEN_CTRL); /* EV 62881/64991 - turn off Green Field detection for Maverick STA beta */ phymode &= ~AR_PHY_GC_GF_DETECT_EN; OS_REG_WRITE(ah, AR_PHY_GEN_CTRL, phymode); /* Set IFS timing for half/quarter rates */ if (IEEE80211_IS_CHAN_HALF(chan) || IEEE80211_IS_CHAN_QUARTER(chan)) { u_int32_t modeselect = OS_REG_READ(ah, AR_PHY_MODE); if (IEEE80211_IS_CHAN_HALF(chan)) { modeselect |= AR_PHY_MS_HALF_RATE; } else if (IEEE80211_IS_CHAN_QUARTER(chan)) { modeselect |= AR_PHY_MS_QUARTER_RATE; } OS_REG_WRITE(ah, AR_PHY_MODE, modeselect); ar9300_set_ifs_timing(ah, chan); OS_REG_RMW_FIELD( ah, AR_PHY_FRAME_CTL, AR_PHY_FRAME_CTL_CF_OVERLAP_WINDOW, 0x3); } /* Configure MAC for 20/40 operation */ ar9300_set_11n_mac2040(ah, macmode); /* global transmit timeout (25 TUs default)*/ /* XXX - put this elsewhere??? */ OS_REG_WRITE(ah, AR_GTXTO, 25 << AR_GTXTO_TIMEOUT_LIMIT_S); /* carrier sense timeout */ OS_REG_WRITE(ah, AR_CST, 0xF << AR_CST_TIMEOUT_LIMIT_S); } /* * Spur mitigation for MRC CCK */ static void ar9300_spur_mitigate_mrc_cck(struct ath_hal *ah, struct ieee80211_channel *chan) { int i; /* spur_freq_for_osprey - hardcoded by Systems team for now. */ u_int32_t spur_freq_for_osprey[4] = { 2420, 2440, 2464, 2480 }; u_int32_t spur_freq_for_jupiter[2] = { 2440, 2464}; int cur_bb_spur, negative = 0, cck_spur_freq; u_int8_t* spur_fbin_ptr = NULL; int synth_freq; int range = 10; int max_spurcounts = OSPREY_EEPROM_MODAL_SPURS; HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); /* * Need to verify range +/- 10 MHz in control channel, otherwise spur * is out-of-band and can be ignored. */ if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah) || AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah)) { spur_fbin_ptr = ar9300_eeprom_get_spur_chans_ptr(ah, 1); if (spur_fbin_ptr[0] == 0) { return; /* No spur in the mode */ } if (IEEE80211_IS_CHAN_HT40(chan)) { range = 19; if (OS_REG_READ_FIELD(ah, AR_PHY_GEN_CTRL, AR_PHY_GC_DYN2040_PRI_CH) == 0x0) { synth_freq = ichan->channel + 10; } else { synth_freq = ichan->channel - 10; } } else { range = 10; synth_freq = ichan->channel; } } else if(AR_SREV_JUPITER(ah)) { range = 5; max_spurcounts = 2; /* Hardcoded by Jupiter Systems team for now. */ synth_freq = ichan->channel; } else { range = 10; max_spurcounts = 4; /* Hardcoded by Osprey Systems team for now. */ synth_freq = ichan->channel; } for (i = 0; i < max_spurcounts; i++) { negative = 0; if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah) || AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah)) { cur_bb_spur = FBIN2FREQ(spur_fbin_ptr[i], HAL_FREQ_BAND_2GHZ) - synth_freq; } else if(AR_SREV_JUPITER(ah)) { cur_bb_spur = spur_freq_for_jupiter[i] - synth_freq; } else { cur_bb_spur = spur_freq_for_osprey[i] - synth_freq; } if (cur_bb_spur < 0) { negative = 1; cur_bb_spur = -cur_bb_spur; } if (cur_bb_spur < range) { cck_spur_freq = (int)((cur_bb_spur << 19) / 11); if (negative == 1) { cck_spur_freq = -cck_spur_freq; } cck_spur_freq = cck_spur_freq & 0xfffff; /*OS_REG_WRITE_field(ah, BB_agc_control.ycok_max, 0x7);*/ OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_YCOK_MAX, 0x7); /*OS_REG_WRITE_field(ah, BB_cck_spur_mit.spur_rssi_thr, 0x7f);*/ OS_REG_RMW_FIELD(ah, AR_PHY_CCK_SPUR_MIT, AR_PHY_CCK_SPUR_MIT_SPUR_RSSI_THR, 0x7f); /*OS_REG_WRITE(ah, BB_cck_spur_mit.spur_filter_type, 0x2);*/ OS_REG_RMW_FIELD(ah, AR_PHY_CCK_SPUR_MIT, AR_PHY_CCK_SPUR_MIT_SPUR_FILTER_TYPE, 0x2); /*OS_REG_WRITE(ah, BB_cck_spur_mit.use_cck_spur_mit, 0x1);*/ OS_REG_RMW_FIELD(ah, AR_PHY_CCK_SPUR_MIT, AR_PHY_CCK_SPUR_MIT_USE_CCK_SPUR_MIT, 0x1); /*OS_REG_WRITE(ah, BB_cck_spur_mit.cck_spur_freq, cck_spur_freq);*/ OS_REG_RMW_FIELD(ah, AR_PHY_CCK_SPUR_MIT, AR_PHY_CCK_SPUR_MIT_CCK_SPUR_FREQ, cck_spur_freq); return; } } /*OS_REG_WRITE(ah, BB_agc_control.ycok_max, 0x5);*/ OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_YCOK_MAX, 0x5); /*OS_REG_WRITE(ah, BB_cck_spur_mit.use_cck_spur_mit, 0x0);*/ OS_REG_RMW_FIELD(ah, AR_PHY_CCK_SPUR_MIT, AR_PHY_CCK_SPUR_MIT_USE_CCK_SPUR_MIT, 0x0); /*OS_REG_WRITE(ah, BB_cck_spur_mit.cck_spur_freq, 0x0);*/ OS_REG_RMW_FIELD(ah, AR_PHY_CCK_SPUR_MIT, AR_PHY_CCK_SPUR_MIT_CCK_SPUR_FREQ, 0x0); } /* Spur mitigation for OFDM */ static void ar9300_spur_mitigate_ofdm(struct ath_hal *ah, struct ieee80211_channel *chan) { int synth_freq; int range = 10; int freq_offset = 0; int spur_freq_sd = 0; int spur_subchannel_sd = 0; int spur_delta_phase = 0; int mask_index = 0; int i; int mode; u_int8_t* spur_chans_ptr; struct ath_hal_9300 *ahp; ahp = AH9300(ah); HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); if (IS_CHAN_5GHZ(ichan)) { spur_chans_ptr = ar9300_eeprom_get_spur_chans_ptr(ah, 0); mode = 0; } else { spur_chans_ptr = ar9300_eeprom_get_spur_chans_ptr(ah, 1); mode = 1; } if (IEEE80211_IS_CHAN_HT40(chan)) { range = 19; if (OS_REG_READ_FIELD(ah, AR_PHY_GEN_CTRL, AR_PHY_GC_DYN2040_PRI_CH) == 0x0) { synth_freq = ichan->channel - 10; } else { synth_freq = ichan->channel + 10; } } else { range = 10; synth_freq = ichan->channel; } /* Clean all spur register fields */ OS_REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_SPUR_FILTER, 0); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING11, AR_PHY_TIMING11_SPUR_FREQ_SD, 0); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING11, AR_PHY_TIMING11_SPUR_DELTA_PHASE, 0); OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, AR_PHY_SFCORR_EXT_SPUR_SUBCHANNEL_SD, 0); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING11, AR_PHY_TIMING11_USE_SPUR_FILTER_IN_AGC, 0); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING11, AR_PHY_TIMING11_USE_SPUR_FILTER_IN_SELFCOR, 0); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_SPUR_RSSI, 0); OS_REG_RMW_FIELD(ah, AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_EN_VIT_SPUR_RSSI, 0); OS_REG_RMW_FIELD(ah, AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_ENABLE_NF_RSSI_SPUR_MIT, 0); OS_REG_RMW_FIELD(ah, AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_ENABLE_MASK_PPM, 0); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_PILOT_MASK, 0); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_CHAN_MASK, 0); OS_REG_RMW_FIELD(ah, AR_PHY_PILOT_SPUR_MASK, AR_PHY_PILOT_SPUR_MASK_CF_PILOT_MASK_IDX_A, 0); OS_REG_RMW_FIELD(ah, AR_PHY_SPUR_MASK_A, AR_PHY_SPUR_MASK_A_CF_PUNC_MASK_IDX_A, 0); OS_REG_RMW_FIELD(ah, AR_PHY_CHAN_SPUR_MASK, AR_PHY_CHAN_SPUR_MASK_CF_CHAN_MASK_IDX_A, 0); OS_REG_RMW_FIELD(ah, AR_PHY_PILOT_SPUR_MASK, AR_PHY_PILOT_SPUR_MASK_CF_PILOT_MASK_A, 0); OS_REG_RMW_FIELD(ah, AR_PHY_CHAN_SPUR_MASK, AR_PHY_CHAN_SPUR_MASK_CF_CHAN_MASK_A, 0); OS_REG_RMW_FIELD(ah, AR_PHY_SPUR_MASK_A, AR_PHY_SPUR_MASK_A_CF_PUNC_MASK_A, 0); OS_REG_RMW_FIELD(ah, AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_MASK_RATE_CNTL, 0); i = 0; while (spur_chans_ptr[i] && i < 5) { freq_offset = FBIN2FREQ(spur_chans_ptr[i], mode) - synth_freq; if (abs(freq_offset) < range) { /* printf( "Spur Mitigation for OFDM: Synth Frequency = %d, " "Spur Frequency = %d\n", synth_freq, FBIN2FREQ(spur_chans_ptr[i], mode)); */ if (IEEE80211_IS_CHAN_HT40(chan)) { if (freq_offset < 0) { if (OS_REG_READ_FIELD( ah, AR_PHY_GEN_CTRL, AR_PHY_GC_DYN2040_PRI_CH) == 0x0) { spur_subchannel_sd = 1; } else { spur_subchannel_sd = 0; } spur_freq_sd = ((freq_offset + 10) << 9) / 11; } else { if (OS_REG_READ_FIELD(ah, AR_PHY_GEN_CTRL, AR_PHY_GC_DYN2040_PRI_CH) == 0x0) { spur_subchannel_sd = 0; } else { spur_subchannel_sd = 1; } spur_freq_sd = ((freq_offset - 10) << 9) / 11; } spur_delta_phase = (freq_offset << 17) / 5; } else { spur_subchannel_sd = 0; spur_freq_sd = (freq_offset << 9) / 11; spur_delta_phase = (freq_offset << 18) / 5; } spur_freq_sd = spur_freq_sd & 0x3ff; spur_delta_phase = spur_delta_phase & 0xfffff; /* printf( "spur_subchannel_sd = %d, spur_freq_sd = 0x%x, " "spur_delta_phase = 0x%x\n", spur_subchannel_sd, spur_freq_sd, spur_delta_phase); */ /* OFDM Spur mitigation */ OS_REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_SPUR_FILTER, 0x1); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING11, AR_PHY_TIMING11_SPUR_FREQ_SD, spur_freq_sd); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING11, AR_PHY_TIMING11_SPUR_DELTA_PHASE, spur_delta_phase); OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, AR_PHY_SFCORR_EXT_SPUR_SUBCHANNEL_SD, spur_subchannel_sd); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING11, AR_PHY_TIMING11_USE_SPUR_FILTER_IN_AGC, 0x1); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING11, AR_PHY_TIMING11_USE_SPUR_FILTER_IN_SELFCOR, 0x1); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_SPUR_RSSI, 0x1); OS_REG_RMW_FIELD(ah, AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_SPUR_RSSI_THRESH, 34); OS_REG_RMW_FIELD(ah, AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_EN_VIT_SPUR_RSSI, 1); /* * Do not subtract spur power from noise floor for wasp. * This causes the maximum client test (on Veriwave) to fail * when run on spur channel (2464 MHz). * Refer to ev#82746 and ev#82744. */ if (!AR_SREV_WASP(ah) && (OS_REG_READ_FIELD(ah, AR_PHY_MODE, AR_PHY_MODE_DYNAMIC) == 0x1)) { OS_REG_RMW_FIELD(ah, AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_ENABLE_NF_RSSI_SPUR_MIT, 1); } mask_index = (freq_offset << 4) / 5; if (mask_index < 0) { mask_index = mask_index - 1; } mask_index = mask_index & 0x7f; /*printf("Bin 0x%x\n", mask_index);*/ OS_REG_RMW_FIELD(ah, AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_ENABLE_MASK_PPM, 0x1); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_PILOT_MASK, 0x1); OS_REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_CHAN_MASK, 0x1); OS_REG_RMW_FIELD(ah, AR_PHY_PILOT_SPUR_MASK, AR_PHY_PILOT_SPUR_MASK_CF_PILOT_MASK_IDX_A, mask_index); OS_REG_RMW_FIELD(ah, AR_PHY_SPUR_MASK_A, AR_PHY_SPUR_MASK_A_CF_PUNC_MASK_IDX_A, mask_index); OS_REG_RMW_FIELD(ah, AR_PHY_CHAN_SPUR_MASK, AR_PHY_CHAN_SPUR_MASK_CF_CHAN_MASK_IDX_A, mask_index); OS_REG_RMW_FIELD(ah, AR_PHY_PILOT_SPUR_MASK, AR_PHY_PILOT_SPUR_MASK_CF_PILOT_MASK_A, 0xc); OS_REG_RMW_FIELD(ah, AR_PHY_CHAN_SPUR_MASK, AR_PHY_CHAN_SPUR_MASK_CF_CHAN_MASK_A, 0xc); OS_REG_RMW_FIELD(ah, AR_PHY_SPUR_MASK_A, AR_PHY_SPUR_MASK_A_CF_PUNC_MASK_A, 0xa0); OS_REG_RMW_FIELD(ah, AR_PHY_SPUR_REG, AR_PHY_SPUR_REG_MASK_RATE_CNTL, 0xff); /* printf("BB_timing_control_4 = 0x%x\n", OS_REG_READ(ah, AR_PHY_TIMING4)); printf("BB_timing_control_11 = 0x%x\n", OS_REG_READ(ah, AR_PHY_TIMING11)); printf("BB_ext_chan_scorr_thr = 0x%x\n", OS_REG_READ(ah, AR_PHY_SFCORR_EXT)); printf("BB_spur_mask_controls = 0x%x\n", OS_REG_READ(ah, AR_PHY_SPUR_REG)); printf("BB_pilot_spur_mask = 0x%x\n", OS_REG_READ(ah, AR_PHY_PILOT_SPUR_MASK)); printf("BB_chan_spur_mask = 0x%x\n", OS_REG_READ(ah, AR_PHY_CHAN_SPUR_MASK)); printf("BB_vit_spur_mask_A = 0x%x\n", OS_REG_READ(ah, AR_PHY_SPUR_MASK_A)); */ break; } i++; } } /* * Convert to baseband spur frequency given input channel frequency * and compute register settings below. */ static void ar9300_spur_mitigate(struct ath_hal *ah, struct ieee80211_channel *chan) { ar9300_spur_mitigate_ofdm(ah, chan); ar9300_spur_mitigate_mrc_cck(ah, chan); } /************************************************************** * ar9300_channel_change * Assumes caller wants to change channel, and not reset. */ static inline HAL_BOOL ar9300_channel_change(struct ath_hal *ah, struct ieee80211_channel *chan, HAL_CHANNEL_INTERNAL *ichan, HAL_HT_MACMODE macmode) { u_int32_t synth_delay, qnum; struct ath_hal_9300 *ahp = AH9300(ah); /* TX must be stopped by now */ for (qnum = 0; qnum < AR_NUM_QCU; qnum++) { if (ar9300_num_tx_pending(ah, qnum)) { HALDEBUG(ah, HAL_DEBUG_QUEUE, "%s: Transmit frames pending on queue %d\n", __func__, qnum); HALASSERT(0); return AH_FALSE; } } /* * Kill last Baseband Rx Frame - Request analog bus grant */ OS_REG_WRITE(ah, AR_PHY_RFBUS_REQ, AR_PHY_RFBUS_REQ_EN); if (!ath_hal_wait(ah, AR_PHY_RFBUS_GRANT, AR_PHY_RFBUS_GRANT_EN, AR_PHY_RFBUS_GRANT_EN)) { HALDEBUG(ah, HAL_DEBUG_PHYIO, "%s: Could not kill baseband RX\n", __func__); return AH_FALSE; } /* Setup 11n MAC/Phy mode registers */ ar9300_set_11n_regs(ah, chan, macmode); /* * Change the synth */ if (!ahp->ah_rf_hal.set_channel(ah, chan)) { HALDEBUG(ah, HAL_DEBUG_CHANNEL, "%s: failed to set channel\n", __func__); return AH_FALSE; } /* * Some registers get reinitialized during ATH_INI_POST INI programming. */ ar9300_init_user_settings(ah); /* * Setup the transmit power values. * * After the public to private hal channel mapping, ichan contains the * valid regulatory power value. * ath_hal_getctl and ath_hal_getantennaallowed look up ichan from chan. */ if (ar9300_eeprom_set_transmit_power( ah, &ahp->ah_eeprom, chan, ath_hal_getctl(ah, chan), ath_hal_getantennaallowed(ah, chan), ath_hal_get_twice_max_regpower(AH_PRIVATE(ah), ichan, chan), AH_MIN(MAX_RATE_POWER, AH_PRIVATE(ah)->ah_powerLimit)) != HAL_OK) { HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: error init'ing transmit power\n", __func__); return AH_FALSE; } /* * Release the RFBus Grant. */ OS_REG_WRITE(ah, AR_PHY_RFBUS_REQ, 0); /* * Write spur immunity and delta slope for OFDM enabled modes (A, G, Turbo) */ if (IEEE80211_IS_CHAN_OFDM(chan) || IEEE80211_IS_CHAN_HT(chan)) { ar9300_set_delta_slope(ah, chan); } else { /* Set to Ini default */ OS_REG_WRITE(ah, AR_PHY_TIMING3, 0x9c0a9f6b); OS_REG_WRITE(ah, AR_PHY_SGI_DELTA, 0x00046384); } ar9300_spur_mitigate(ah, chan); /* * Wait for the frequency synth to settle (synth goes on via PHY_ACTIVE_EN). * Read the phy active delay register. Value is in 100ns increments. */ synth_delay = OS_REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY; if (IEEE80211_IS_CHAN_CCK(chan)) { synth_delay = (4 * synth_delay) / 22; } else { synth_delay /= 10; } OS_DELAY(synth_delay + BASE_ACTIVATE_DELAY); /* * Do calibration. */ return AH_TRUE; } void ar9300_set_operating_mode(struct ath_hal *ah, int opmode) { u_int32_t val; val = OS_REG_READ(ah, AR_STA_ID1); val &= ~(AR_STA_ID1_STA_AP | AR_STA_ID1_ADHOC); switch (opmode) { case HAL_M_HOSTAP: OS_REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_STA_AP | AR_STA_ID1_KSRCH_MODE); OS_REG_CLR_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION); break; case HAL_M_IBSS: OS_REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_ADHOC | AR_STA_ID1_KSRCH_MODE); OS_REG_SET_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION); break; case HAL_M_STA: case HAL_M_MONITOR: OS_REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_KSRCH_MODE); break; } } /* XXX need the logic for Osprey */ void ar9300_init_pll(struct ath_hal *ah, struct ieee80211_channel *chan) { u_int32_t pll; u_int8_t clk_25mhz = AH9300(ah)->clk_25mhz; HAL_CHANNEL_INTERNAL *ichan = NULL; if (chan) ichan = ath_hal_checkchannel(ah, chan); if (AR_SREV_HORNET(ah)) { if (clk_25mhz) { /* Hornet uses PLL_CONTROL_2. Xtal is 25MHz for Hornet. * REFDIV set to 0x1. * $xtal_freq = 25; * $PLL2_div = (704/$xtal_freq); # 176 * 4 = 704. * MAC and BB run at 176 MHz. * $PLL2_divint = int($PLL2_div); * $PLL2_divfrac = $PLL2_div - $PLL2_divint; * $PLL2_divfrac = int($PLL2_divfrac * 0x4000); # 2^14 * $PLL2_Val = ($PLL2_divint & 0x3f) << 19 | (0x1) << 14 | * $PLL2_divfrac & 0x3fff; * Therefore, $PLL2_Val = 0xe04a3d */ #define DPLL2_KD_VAL 0x1D #define DPLL2_KI_VAL 0x06 #define DPLL3_PHASE_SHIFT_VAL 0x1 /* Rewrite DDR PLL2 and PLL3 */ /* program DDR PLL ki and kd value, ki=0x6, kd=0x1d */ OS_REG_WRITE(ah, AR_HORNET_CH0_DDR_DPLL2, 0x18e82f01); /* program DDR PLL phase_shift to 0x1 */ OS_REG_RMW_FIELD(ah, AR_HORNET_CH0_DDR_DPLL3, AR_PHY_BB_DPLL3_PHASE_SHIFT, DPLL3_PHASE_SHIFT_VAL); OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL, 0x1142c); OS_DELAY(1000); /* program refdiv, nint, frac to RTC register */ OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL2, 0xe04a3d); /* program BB PLL ki and kd value, ki=0x6, kd=0x1d */ OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, AR_PHY_BB_DPLL2_KD, DPLL2_KD_VAL); OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, AR_PHY_BB_DPLL2_KI, DPLL2_KI_VAL); /* program BB PLL phase_shift to 0x1 */ OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL3, AR_PHY_BB_DPLL3_PHASE_SHIFT, DPLL3_PHASE_SHIFT_VAL); } else { /* 40MHz */ #undef DPLL2_KD_VAL #undef DPLL2_KI_VAL #define DPLL2_KD_VAL 0x3D #define DPLL2_KI_VAL 0x06 /* Rewrite DDR PLL2 and PLL3 */ /* program DDR PLL ki and kd value, ki=0x6, kd=0x3d */ OS_REG_WRITE(ah, AR_HORNET_CH0_DDR_DPLL2, 0x19e82f01); /* program DDR PLL phase_shift to 0x1 */ OS_REG_RMW_FIELD(ah, AR_HORNET_CH0_DDR_DPLL3, AR_PHY_BB_DPLL3_PHASE_SHIFT, DPLL3_PHASE_SHIFT_VAL); OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL, 0x1142c); OS_DELAY(1000); /* program refdiv, nint, frac to RTC register */ OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL2, 0x886666); /* program BB PLL ki and kd value, ki=0x6, kd=0x3d */ OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, AR_PHY_BB_DPLL2_KD, DPLL2_KD_VAL); OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, AR_PHY_BB_DPLL2_KI, DPLL2_KI_VAL); /* program BB PLL phase_shift to 0x1 */ OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL3, AR_PHY_BB_DPLL3_PHASE_SHIFT, DPLL3_PHASE_SHIFT_VAL); } OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL, 0x142c); OS_DELAY(1000); } else if (AR_SREV_POSEIDON(ah) || AR_SREV_APHRODITE(ah)) { OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, AR_PHY_BB_DPLL2_PLL_PWD, 0x1); /* program BB PLL ki and kd value, ki=0x4, kd=0x40 */ OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, AR_PHY_BB_DPLL2_KD, 0x40); OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, AR_PHY_BB_DPLL2_KI, 0x4); OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL1, AR_PHY_BB_DPLL1_REFDIV, 0x5); OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL1, AR_PHY_BB_DPLL1_NINI, 0x58); OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL1, AR_PHY_BB_DPLL1_NFRAC, 0x0); OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, AR_PHY_BB_DPLL2_OUTDIV, 0x1); OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, AR_PHY_BB_DPLL2_LOCAL_PLL, 0x1); OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, AR_PHY_BB_DPLL2_EN_NEGTRIG, 0x1); /* program BB PLL phase_shift to 0x6 */ OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL3, AR_PHY_BB_DPLL3_PHASE_SHIFT, 0x6); OS_REG_RMW_FIELD(ah, AR_PHY_BB_DPLL2, AR_PHY_BB_DPLL2_PLL_PWD, 0x0); OS_DELAY(1000); OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL, 0x142c); OS_DELAY(1000); } else if (AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah)) { #define SRIF_PLL 1 u_int32_t regdata, pll2_divint, pll2_divfrac; #ifndef SRIF_PLL u_int32_t pll2_clkmode; #endif #ifdef SRIF_PLL u_int32_t refdiv; #endif if (clk_25mhz) { #ifndef SRIF_PLL pll2_divint = 0x1c; pll2_divfrac = 0xa3d7; #else pll2_divint = 0x54; pll2_divfrac = 0x1eb85; refdiv = 3; #endif } else { #ifndef SRIF_PLL pll2_divint = 0x11; pll2_divfrac = 0x26666; #else if (AR_SREV_WASP(ah)) { pll2_divint = 88; pll2_divfrac = 0; refdiv = 5; } else { pll2_divint = 0x11; pll2_divfrac = 0x26666; refdiv = 1; } #endif } #ifndef SRIF_PLL pll2_clkmode = 0x3d; #endif /* PLL programming through SRIF Local Mode */ OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL, 0x1142c); /* Bypass mode */ OS_DELAY(1000); do { regdata = OS_REG_READ(ah, AR_PHY_PLL_MODE); regdata = regdata | (0x1 << 16); OS_REG_WRITE(ah, AR_PHY_PLL_MODE, regdata); /* PWD_PLL set to 1 */ OS_DELAY(100); /* override int, frac, refdiv */ #ifndef SRIF_PLL OS_REG_WRITE(ah, AR_PHY_PLL_CONTROL, ((1 << 27) | (pll2_divint << 18) | pll2_divfrac)); #else OS_REG_WRITE(ah, AR_PHY_PLL_CONTROL, ((refdiv << 27) | (pll2_divint << 18) | pll2_divfrac)); #endif OS_DELAY(100); regdata = OS_REG_READ(ah, AR_PHY_PLL_MODE); #ifndef SRIF_PLL regdata = (regdata & 0x80071fff) | (0x1 << 30) | (0x1 << 13) | (0x6 << 26) | (pll2_clkmode << 19); #else if (AR_SREV_WASP(ah)) { regdata = (regdata & 0x80071fff) | (0x1 << 30) | (0x1 << 13) | (0x4 << 26) | (0x18 << 19); } else { regdata = (regdata & 0x80071fff) | (0x3 << 30) | (0x1 << 13) | (0x4 << 26) | (0x60 << 19); } #endif /* Ki, Kd, Local PLL, Outdiv */ OS_REG_WRITE(ah, AR_PHY_PLL_MODE, regdata); regdata = OS_REG_READ(ah, AR_PHY_PLL_MODE); regdata = (regdata & 0xfffeffff); OS_REG_WRITE(ah, AR_PHY_PLL_MODE, regdata); /* PWD_PLL set to 0 */ OS_DELAY(1000); if (AR_SREV_WASP(ah)) { /* clear do measure */ regdata = OS_REG_READ(ah, AR_PHY_PLL_BB_DPLL3); regdata &= ~(1 << 30); OS_REG_WRITE(ah, AR_PHY_PLL_BB_DPLL3, regdata); OS_DELAY(100); /* set do measure */ regdata = OS_REG_READ(ah, AR_PHY_PLL_BB_DPLL3); regdata |= (1 << 30); OS_REG_WRITE(ah, AR_PHY_PLL_BB_DPLL3, regdata); /* wait for measure done */ do { regdata = OS_REG_READ(ah, AR_PHY_PLL_BB_DPLL4); } while ((regdata & (1 << 3)) == 0); /* clear do measure */ regdata = OS_REG_READ(ah, AR_PHY_PLL_BB_DPLL3); regdata &= ~(1 << 30); OS_REG_WRITE(ah, AR_PHY_PLL_BB_DPLL3, regdata); /* get measure sqsum dvc */ regdata = (OS_REG_READ(ah, AR_PHY_PLL_BB_DPLL3) & 0x007FFFF8) >> 3; } else { break; } } while (regdata >= 0x40000); /* Remove from Bypass mode */ OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL, 0x142c); OS_DELAY(1000); } else { pll = SM(0x5, AR_RTC_PLL_REFDIV); /* Supposedly not needed on Osprey */ #if 0 if (chan && IS_CHAN_HALF_RATE(chan)) { pll |= SM(0x1, AR_RTC_PLL_CLKSEL); } else if (chan && IS_CHAN_QUARTER_RATE(chan)) { pll |= SM(0x2, AR_RTC_PLL_CLKSEL); } #endif if (ichan && IS_CHAN_5GHZ(ichan)) { pll |= SM(0x28, AR_RTC_PLL_DIV); /* * When doing fast clock, set PLL to 0x142c */ if (IS_5GHZ_FAST_CLOCK_EN(ah, chan)) { pll = 0x142c; } } else { pll |= SM(0x2c, AR_RTC_PLL_DIV); } OS_REG_WRITE(ah, AR_RTC_PLL_CONTROL, pll); } /* TODO: * For multi-band owl, switch between bands by reiniting the PLL. */ OS_DELAY(RTC_PLL_SETTLE_DELAY); OS_REG_WRITE(ah, AR_RTC_SLEEP_CLK, AR_RTC_FORCE_DERIVED_CLK | AR_RTC_PCIE_RST_PWDN_EN); if (AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah)) { if (clk_25mhz) { OS_REG_WRITE(ah, AR_RTC_DERIVED_RTC_CLK, (0x17c << 1)); /* 32KHz sleep clk */ OS_REG_WRITE(ah, AR_SLP32_MODE, 0x0010f3d7); OS_REG_WRITE(ah, AR_SLP32_INC, 0x0001e7ae); } else { OS_REG_WRITE(ah, AR_RTC_DERIVED_RTC_CLK, (0x261 << 1)); /* 32KHz sleep clk */ OS_REG_WRITE(ah, AR_SLP32_MODE, 0x0010f400); OS_REG_WRITE(ah, AR_SLP32_INC, 0x0001e800); } OS_DELAY(100); } } static inline HAL_BOOL ar9300_set_reset(struct ath_hal *ah, int type) { u_int32_t rst_flags; u_int32_t tmp_reg; + struct ath_hal_9300 *ahp = AH9300(ah); HALASSERT(type == HAL_RESET_WARM || type == HAL_RESET_COLD); /* * RTC Force wake should be done before resetting the MAC. * MDK/ART does it that way. */ OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA), AH9300(ah)->ah_wa_reg_val); OS_DELAY(10); /* delay to allow AR_WA reg write to kick in */ OS_REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN | AR_RTC_FORCE_WAKE_ON_INT); /* Reset AHB */ /* Bug26871 */ tmp_reg = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_INTR_SYNC_CAUSE)); if (AR_SREV_WASP(ah)) { if (tmp_reg & (AR9340_INTR_SYNC_LOCAL_TIMEOUT)) { OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_SYNC_ENABLE), 0); OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_RC), AR_RC_HOSTIF); } } else { if (tmp_reg & (AR9300_INTR_SYNC_LOCAL_TIMEOUT | AR9300_INTR_SYNC_RADM_CPL_TIMEOUT)) { OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_SYNC_ENABLE), 0); OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_RC), AR_RC_HOSTIF); } else { /* NO AR_RC_AHB in Osprey */ /*OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_RC), AR_RC_AHB);*/ } } rst_flags = AR_RTC_RC_MAC_WARM; if (type == HAL_RESET_COLD) { rst_flags |= AR_RTC_RC_MAC_COLD; } #ifdef AH_SUPPORT_HORNET /* Hornet WAR: trigger SoC to reset WMAC if ... * (1) doing cold reset. Ref: EV 69254 * (2) beacon pending. Ref: EV 70983 */ if (AR_SREV_HORNET(ah) && (ar9300_num_tx_pending( ah, AH_PRIVATE(ah)->ah_caps.halTotalQueues - 1) != 0 || type == HAL_RESET_COLD)) { u_int32_t time_out; #define AR_SOC_RST_RESET 0xB806001C #define AR_SOC_BOOT_STRAP 0xB80600AC #define AR_SOC_WLAN_RST 0x00000800 /* WLAN reset */ #define REG_WRITE(_reg, _val) *((volatile u_int32_t *)(_reg)) = (_val); #define REG_READ(_reg) *((volatile u_int32_t *)(_reg)) HALDEBUG(ah, HAL_DEBUG_RESET, "%s: Hornet SoC reset WMAC.\n", __func__); REG_WRITE(AR_SOC_RST_RESET, REG_READ(AR_SOC_RST_RESET) | AR_SOC_WLAN_RST); REG_WRITE(AR_SOC_RST_RESET, REG_READ(AR_SOC_RST_RESET) & (~AR_SOC_WLAN_RST)); time_out = 0; while (1) { tmp_reg = REG_READ(AR_SOC_BOOT_STRAP); if ((tmp_reg & 0x10) == 0) { break; } if (time_out > 20) { break; } OS_DELAY(10000); time_out++; } OS_REG_WRITE(ah, AR_RTC_RESET, 1); #undef REG_READ #undef REG_WRITE #undef AR_SOC_WLAN_RST #undef AR_SOC_RST_RESET #undef AR_SOC_BOOT_STRAP } #endif /* AH_SUPPORT_HORNET */ #ifdef AH_SUPPORT_SCORPION if (AR_SREV_SCORPION(ah)) { #define DDR_CTL_CONFIG_ADDRESS 0xb8000000 #define DDR_CTL_CONFIG_OFFSET 0x0108 #define DDR_CTL_CONFIG_CLIENT_ACTIVITY_MSB 29 #define DDR_CTL_CONFIG_CLIENT_ACTIVITY_LSB 21 #define DDR_CTL_CONFIG_CLIENT_ACTIVITY_MASK 0x3fe00000 #define DDR_CTL_CONFIG_CLIENT_ACTIVITY_GET(x) (((x) & DDR_CTL_CONFIG_CLIENT_ACTIVITY_MASK) >> DDR_CTL_CONFIG_CLIENT_ACTIVITY_LSB) #define DDR_CTL_CONFIG_CLIENT_ACTIVITY_SET(x) (((x) << DDR_CTL_CONFIG_CLIENT_ACTIVITY_LSB) & DDR_CTL_CONFIG_CLIENT_ACTIVITY_MASK) #define MAC_DMA_CFG_ADDRESS 0xb8100000 #define MAC_DMA_CFG_OFFSET 0x0014 #define MAC_DMA_CFG_HALT_REQ_MSB 11 #define MAC_DMA_CFG_HALT_REQ_LSB 11 #define MAC_DMA_CFG_HALT_REQ_MASK 0x00000800 #define MAC_DMA_CFG_HALT_REQ_GET(x) (((x) & MAC_DMA_CFG_HALT_REQ_MASK) >> MAC_DMA_CFG_HALT_REQ_LSB) #define MAC_DMA_CFG_HALT_REQ_SET(x) (((x) << MAC_DMA_CFG_HALT_REQ_LSB) & MAC_DMA_CFG_HALT_REQ_MASK) #define MAC_DMA_CFG_HALT_ACK_MSB 12 #define MAC_DMA_CFG_HALT_ACK_LSB 12 #define MAC_DMA_CFG_HALT_ACK_MASK 0x00001000 #define MAC_DMA_CFG_HALT_ACK_GET(x) (((x) & MAC_DMA_CFG_HALT_ACK_MASK) >> MAC_DMA_CFG_HALT_ACK_LSB) #define MAC_DMA_CFG_HALT_ACK_SET(x) (((x) << MAC_DMA_CFG_HALT_ACK_LSB) & MAC_DMA_CFG_HALT_ACK_MASK) #define RST_RESET 0xB806001c #define RTC_RESET (1<<27) #define REG_READ(_reg) *((volatile u_int32_t *)(_reg)) #define REG_WRITE(_reg, _val) *((volatile u_int32_t *)(_reg)) = (_val); #define DDR_REG_READ(_ah, _reg) \ *((volatile u_int32_t *)( DDR_CTL_CONFIG_ADDRESS + (_reg))) #define DDR_REG_WRITE(_ah, _reg, _val) \ *((volatile u_int32_t *)(DDR_CTL_CONFIG_ADDRESS + (_reg))) = (_val) OS_REG_WRITE(ah,MAC_DMA_CFG_OFFSET, (OS_REG_READ(ah,MAC_DMA_CFG_OFFSET) & ~MAC_DMA_CFG_HALT_REQ_MASK) | MAC_DMA_CFG_HALT_REQ_SET(1)); { int count; u_int32_t data; count = 0; while (!MAC_DMA_CFG_HALT_ACK_GET(OS_REG_READ(ah, MAC_DMA_CFG_OFFSET) )) { count++; if (count > 10) { ath_hal_printf(ah, "Halt ACK timeout\n"); break; } OS_DELAY(10); } data = DDR_REG_READ(ah,DDR_CTL_CONFIG_OFFSET); ath_hal_printf(ah, "check DDR Activity - HIGH\n"); count = 0; while (DDR_CTL_CONFIG_CLIENT_ACTIVITY_GET(data)) { // AVE_DEBUG(0,"DDR Activity - HIGH\n"); ath_hal_printf(ah, "DDR Activity - HIGH\n"); count++; OS_DELAY(10); data = DDR_REG_READ(ah,DDR_CTL_CONFIG_OFFSET); if (count > 10) { ath_hal_printf(ah, "DDR Activity timeout\n"); break; } } } { //Force RTC reset REG_WRITE(RST_RESET, (REG_READ(RST_RESET) | RTC_RESET)); OS_DELAY(10); REG_WRITE(RST_RESET, (REG_READ(RST_RESET) & ~RTC_RESET)); OS_DELAY(10); OS_REG_WRITE(ah, AR_RTC_RESET, 0); OS_DELAY(10); OS_REG_WRITE(ah, AR_RTC_RESET, 1); OS_DELAY(10); ath_hal_printf(ah,"%s: Scorpion SoC RTC reset done.\n", __func__); } #undef REG_READ #undef REG_WRITE } #endif /* AH_SUPPORT_SCORPION */ /* * Set Mac(BB,Phy) Warm Reset */ OS_REG_WRITE(ah, AR_RTC_RC, rst_flags); OS_DELAY(50); /* XXX 50 usec */ /* * Clear resets and force wakeup */ OS_REG_WRITE(ah, AR_RTC_RC, 0); if (!ath_hal_wait(ah, AR_RTC_RC, AR_RTC_RC_M, 0)) { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: RTC stuck in MAC reset\n", __FUNCTION__); HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: AR_RTC_RC = 0x%x\n", __func__, OS_REG_READ(ah, AR_RTC_RC)); return AH_FALSE; } /* Clear AHB reset */ OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_RC), 0); ar9300_attach_hw_platform(ah); + ahp->ah_chip_reset_done = 1; return AH_TRUE; } static inline HAL_BOOL ar9300_set_reset_power_on(struct ath_hal *ah) { /* Force wake */ OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA), AH9300(ah)->ah_wa_reg_val); OS_DELAY(10); /* delay to allow AR_WA reg write to kick in */ OS_REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN | AR_RTC_FORCE_WAKE_ON_INT); /* * RTC reset and clear. Some delay in between is needed * to give the chip time to settle. */ OS_REG_WRITE(ah, AR_RTC_RESET, 0); OS_DELAY(2); OS_REG_WRITE(ah, AR_RTC_RESET, 1); /* * Poll till RTC is ON */ if (!ath_hal_wait(ah, AR_RTC_STATUS, AR_RTC_STATUS_M, AR_RTC_STATUS_ON)) { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: RTC not waking up for %d\n", __FUNCTION__, 1000); return AH_FALSE; } /* * Read Revisions from Chip right after RTC is on for the first time. * This helps us detect the chip type early and initialize it accordingly. */ ar9300_read_revisions(ah); /* * Warm reset if we aren't really powering on, * just restarting the driver. */ return ar9300_set_reset(ah, HAL_RESET_WARM); } /* * Write the given reset bit mask into the reset register */ HAL_BOOL ar9300_set_reset_reg(struct ath_hal *ah, u_int32_t type) { HAL_BOOL ret = AH_FALSE; /* * Set force wake */ OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_WA), AH9300(ah)->ah_wa_reg_val); OS_DELAY(10); /* delay to allow AR_WA reg write to kick in */ OS_REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN | AR_RTC_FORCE_WAKE_ON_INT); switch (type) { case HAL_RESET_POWER_ON: ret = ar9300_set_reset_power_on(ah); break; case HAL_RESET_WARM: case HAL_RESET_COLD: ret = ar9300_set_reset(ah, type); break; default: break; } #if ATH_SUPPORT_MCI if (AH_PRIVATE(ah)->ah_caps.halMciSupport) { OS_REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2); } #endif return ret; } /* * Places the PHY and Radio chips into reset. A full reset * must be called to leave this state. The PCI/MAC/PCU are * not placed into reset as we must receive interrupt to * re-enable the hardware. */ HAL_BOOL ar9300_phy_disable(struct ath_hal *ah) { if (!ar9300_set_reset_reg(ah, HAL_RESET_WARM)) { return AH_FALSE; } #ifdef ATH_SUPPORT_LED #define REG_READ(_reg) *((volatile u_int32_t *)(_reg)) #define REG_WRITE(_reg, _val) *((volatile u_int32_t *)(_reg)) = (_val); #define ATH_GPIO_OE 0xB8040000 #define ATH_GPIO_OUT 0xB8040008 /* GPIO Ouput Value reg.*/ if (AR_SREV_WASP(ah)) { if (IS_CHAN_2GHZ((AH_PRIVATE(ah)->ah_curchan))) { REG_WRITE(ATH_GPIO_OE, (REG_READ(ATH_GPIO_OE) | (0x1 << 13))); } else { REG_WRITE(ATH_GPIO_OE, (REG_READ(ATH_GPIO_OE) | (0x1 << 12))); } } else if (AR_SREV_SCORPION(ah)) { if (IS_CHAN_2GHZ((AH_PRIVATE(ah)->ah_curchan))) { REG_WRITE(ATH_GPIO_OE, (REG_READ(ATH_GPIO_OE) | (0x1 << 13))); } else { REG_WRITE(ATH_GPIO_OE, (REG_READ(ATH_GPIO_OE) | (0x1 << 12))); } /* Turn off JMPST led */ REG_WRITE(ATH_GPIO_OUT, (REG_READ(ATH_GPIO_OUT) | (0x1 << 15))); } #undef REG_READ #undef REG_WRITE #endif if ( AR_SREV_OSPREY(ah) ) { OS_REG_RMW(ah, AR_HOSTIF_REG(ah, AR_GPIO_OUTPUT_MUX1), 0x0, 0x1f); } ar9300_init_pll(ah, AH_NULL); return AH_TRUE; } /* * Places all of hardware into reset */ HAL_BOOL ar9300_disable(struct ath_hal *ah) { if (!ar9300_set_power_mode(ah, HAL_PM_AWAKE, AH_TRUE)) { return AH_FALSE; } if (!ar9300_set_reset_reg(ah, HAL_RESET_COLD)) { return AH_FALSE; } ar9300_init_pll(ah, AH_NULL); return AH_TRUE; } /* * TODO: Only write the PLL if we're changing to or from CCK mode * * WARNING: The order of the PLL and mode registers must be correct. */ static inline void ar9300_set_rf_mode(struct ath_hal *ah, struct ieee80211_channel *chan) { u_int32_t rf_mode = 0; if (chan == AH_NULL) { return; } switch (AH9300(ah)->ah_hwp) { case HAL_TRUE_CHIP: rf_mode |= (IEEE80211_IS_CHAN_B(chan) || IEEE80211_IS_CHAN_G(chan)) ? AR_PHY_MODE_DYNAMIC : AR_PHY_MODE_OFDM; break; default: HALASSERT(0); break; } /* Phy mode bits for 5GHz channels requiring Fast Clock */ if ( IS_5GHZ_FAST_CLOCK_EN(ah, chan)) { rf_mode |= (AR_PHY_MODE_DYNAMIC | AR_PHY_MODE_DYN_CCK_DISABLE); } OS_REG_WRITE(ah, AR_PHY_MODE, rf_mode); } /* * Places the hardware into reset and then pulls it out of reset */ HAL_BOOL ar9300_chip_reset(struct ath_hal *ah, struct ieee80211_channel *chan) { struct ath_hal_9300 *ahp = AH9300(ah); int type = HAL_RESET_WARM; OS_MARK(ah, AH_MARK_CHIPRESET, chan ? chan->ic_freq : 0); /* * Warm reset is optimistic. * * If the TX/RX DMA engines aren't shut down (eg, they're * wedged) then we're better off doing a full cold reset * to try and shake that condition. */ if (ahp->ah_chip_full_sleep || (ah->ah_config.ah_force_full_reset == 1) || OS_REG_READ(ah, AR_Q_TXE) || (OS_REG_READ(ah, AR_CR) & AR_CR_RXE)) { type = HAL_RESET_COLD; } if (!ar9300_set_reset_reg(ah, type)) { return AH_FALSE; } /* Bring out of sleep mode (AGAIN) */ if (!ar9300_set_power_mode(ah, HAL_PM_AWAKE, AH_TRUE)) { return AH_FALSE; } ahp->ah_chip_full_sleep = AH_FALSE; if (AR_SREV_HORNET(ah)) { ar9300_internal_regulator_apply(ah); } ar9300_init_pll(ah, chan); /* * Perform warm reset before the mode/PLL/turbo registers * are changed in order to deactivate the radio. Mode changes * with an active radio can result in corrupted shifts to the * radio device. */ ar9300_set_rf_mode(ah, chan); return AH_TRUE; } /* ar9300_setup_calibration * Setup HW to collect samples used for current cal */ inline static void ar9300_setup_calibration(struct ath_hal *ah, HAL_CAL_LIST *curr_cal) { /* Select calibration to run */ switch (curr_cal->cal_data->cal_type) { case IQ_MISMATCH_CAL: /* Start calibration w/ 2^(INIT_IQCAL_LOG_COUNT_MAX+1) samples */ OS_REG_RMW_FIELD(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_IQCAL_LOG_COUNT_MAX, curr_cal->cal_data->cal_count_max); OS_REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_IQ); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: starting IQ Mismatch Calibration\n", __func__); /* Kick-off cal */ OS_REG_SET_BIT(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_DO_CAL); break; case TEMP_COMP_CAL: if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah) || AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah)) { OS_REG_RMW_FIELD(ah, AR_HORNET_CH0_THERM, AR_PHY_65NM_CH0_THERM_LOCAL, 1); OS_REG_RMW_FIELD(ah, AR_HORNET_CH0_THERM, AR_PHY_65NM_CH0_THERM_START, 1); } else if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) { OS_REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_THERM_JUPITER, AR_PHY_65NM_CH0_THERM_LOCAL, 1); OS_REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_THERM_JUPITER, AR_PHY_65NM_CH0_THERM_START, 1); } else { OS_REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_THERM, AR_PHY_65NM_CH0_THERM_LOCAL, 1); OS_REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_THERM, AR_PHY_65NM_CH0_THERM_START, 1); } HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: starting Temperature Compensation Calibration\n", __func__); break; default: HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s called with incorrect calibration type.\n", __func__); } } /* ar9300_reset_calibration * Initialize shared data structures and prepare a cal to be run. */ inline static void ar9300_reset_calibration(struct ath_hal *ah, HAL_CAL_LIST *curr_cal) { struct ath_hal_9300 *ahp = AH9300(ah); int i; /* Setup HW for new calibration */ ar9300_setup_calibration(ah, curr_cal); /* Change SW state to RUNNING for this calibration */ curr_cal->cal_state = CAL_RUNNING; /* Reset data structures shared between different calibrations */ for (i = 0; i < AR9300_MAX_CHAINS; i++) { ahp->ah_meas0.sign[i] = 0; ahp->ah_meas1.sign[i] = 0; ahp->ah_meas2.sign[i] = 0; ahp->ah_meas3.sign[i] = 0; } ahp->ah_cal_samples = 0; } #ifdef XXX_UNUSED_FUNCTION /* * Find out which of the RX chains are enabled */ static u_int32_t ar9300_get_rx_chain_mask(struct ath_hal *ah) { u_int32_t ret_val = OS_REG_READ(ah, AR_PHY_RX_CHAINMASK); /* The bits [2:0] indicate the rx chain mask and are to be * interpreted as follows: * 00x => Only chain 0 is enabled * 01x => Chain 1 and 0 enabled * 1xx => Chain 2,1 and 0 enabled */ return (ret_val & 0x7); } #endif static void ar9300_get_nf_hist_base(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *chan, int is_scan, int16_t nf[]) { HAL_NFCAL_BASE *h_base; #ifdef ATH_NF_PER_CHAN h_base = &chan->nf_cal_hist.base; #else if (is_scan) { /* * The channel we are currently on is not the home channel, * so we shouldn't use the home channel NF buffer's values on * this channel. Instead, use the NF single value already * read for this channel. (Or, if we haven't read the NF for * this channel yet, the SW default for this chip/band will * be used.) */ h_base = &chan->nf_cal_hist.base; } else { /* use the home channel NF info */ h_base = &AH_PRIVATE(ah)->nf_cal_hist.base; } #endif OS_MEMCPY(nf, h_base->priv_nf, sizeof(h_base->priv_nf)); } HAL_BOOL ar9300_load_nf(struct ath_hal *ah, int16_t nf[]) { int i, j; int32_t val; /* XXX where are EXT regs defined */ const u_int32_t ar9300_cca_regs[] = { AR_PHY_CCA_0, AR_PHY_CCA_1, AR_PHY_CCA_2, AR_PHY_EXT_CCA, AR_PHY_EXT_CCA_1, AR_PHY_EXT_CCA_2, }; u_int8_t chainmask; /* * Force NF calibration for all chains, otherwise Vista station * would conduct a bad performance */ if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah) || AR_SREV_APHRODITE(ah)) { chainmask = 0x9; } else if (AR_SREV_WASP(ah) || AR_SREV_JUPITER(ah)) { chainmask = 0x1b; } else { chainmask = 0x3F; } /* * Write filtered NF values into max_cca_pwr register parameter * so we can load below. */ for (i = 0; i < HAL_NUM_NF_READINGS; i++) { if (chainmask & (1 << i)) { val = OS_REG_READ(ah, ar9300_cca_regs[i]); val &= 0xFFFFFE00; val |= (((u_int32_t)(nf[i]) << 1) & 0x1ff); OS_REG_WRITE(ah, ar9300_cca_regs[i], val); } } HALDEBUG(ah, HAL_DEBUG_NFCAL, "%s: load %d %d %d %d %d %d\n", __func__, nf[0], nf[1], nf[2], nf[3], nf[4], nf[5]); /* * Load software filtered NF value into baseband internal min_cca_pwr * variable. */ OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF); OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF); OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); /* Wait for load to complete, should be fast, a few 10s of us. */ /* Changed the max delay 250us back to 10000us, since 250us often * results in NF load timeout and causes deaf condition * during stress testing 12/12/2009 */ for (j = 0; j < 10000; j++) { if ((OS_REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) == 0){ break; } OS_DELAY(10); } if (j == 10000) { /* * We timed out waiting for the noisefloor to load, probably * due to an in-progress rx. Simply return here and allow * the load plenty of time to complete before the next * calibration interval. We need to avoid trying to load -50 * (which happens below) while the previous load is still in * progress as this can cause rx deafness (see EV 66368,62830). * Instead by returning here, the baseband nf cal will * just be capped by our present noisefloor until the next * calibration timer. */ HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE, "%s: *** TIMEOUT while waiting for nf to load: " "AR_PHY_AGC_CONTROL=0x%x ***\n", __func__, OS_REG_READ(ah, AR_PHY_AGC_CONTROL)); return AH_FALSE; } /* * Restore max_cca_power register parameter again so that we're not capped * by the median we just loaded. This will be initial (and max) value * of next noise floor calibration the baseband does. */ for (i = 0; i < HAL_NUM_NF_READINGS; i++) { if (chainmask & (1 << i)) { val = OS_REG_READ(ah, ar9300_cca_regs[i]); val &= 0xFFFFFE00; val |= (((u_int32_t)(-50) << 1) & 0x1ff); OS_REG_WRITE(ah, ar9300_cca_regs[i], val); } } return AH_TRUE; } /* ar9300_per_calibration * Generic calibration routine. * Recalibrate the lower PHY chips to account for temperature/environment * changes. */ inline static void ar9300_per_calibration(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *ichan, u_int8_t rxchainmask, HAL_CAL_LIST *curr_cal, HAL_BOOL *is_cal_done) { struct ath_hal_9300 *ahp = AH9300(ah); /* Cal is assumed not done until explicitly set below */ *is_cal_done = AH_FALSE; /* Calibration in progress. */ if (curr_cal->cal_state == CAL_RUNNING) { /* Check to see if it has finished. */ if (!(OS_REG_READ(ah, AR_PHY_TIMING4) & AR_PHY_TIMING4_DO_CAL)) { int i, num_chains = 0; for (i = 0; i < AR9300_MAX_CHAINS; i++) { if (rxchainmask & (1 << i)) { num_chains++; } } /* * Accumulate cal measures for active chains */ curr_cal->cal_data->cal_collect(ah, num_chains); ahp->ah_cal_samples++; if (ahp->ah_cal_samples >= curr_cal->cal_data->cal_num_samples) { /* * Process accumulated data */ curr_cal->cal_data->cal_post_proc(ah, num_chains); /* Calibration has finished. */ ichan->calValid |= curr_cal->cal_data->cal_type; curr_cal->cal_state = CAL_DONE; *is_cal_done = AH_TRUE; } else { /* Set-up collection of another sub-sample until we * get desired number */ ar9300_setup_calibration(ah, curr_cal); } } } else if (!(ichan->calValid & curr_cal->cal_data->cal_type)) { /* If current cal is marked invalid in channel, kick it off */ ar9300_reset_calibration(ah, curr_cal); } } static void ar9300_start_nf_cal(struct ath_hal *ah) { + struct ath_hal_9300 *ahp = AH9300(ah); OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF); OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF); OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); AH9300(ah)->nf_tsf32 = ar9300_get_tsf32(ah); + +/* + * We are reading the NF values before we start the NF operation, because + * of that we are getting very high values like -45. + * This triggers the CW_INT detected and EACS module triggers the channel change + * chip_reset_done value is used to fix this issue. + * chip_reset_flag is set during the RTC reset. + * chip_reset_flag is cleared during the starting NF operation. + * if flag is set we will clear the flag and will not read the NF values. + */ + ahp->ah_chip_reset_done = 0; } /* ar9300_calibration * Wrapper for a more generic Calibration routine. Primarily to abstract to * upper layers whether there is 1 or more calibrations to be run. */ HAL_BOOL ar9300_calibration(struct ath_hal *ah, struct ieee80211_channel *chan, u_int8_t rxchainmask, HAL_BOOL do_nf_cal, HAL_BOOL *is_cal_done, int is_scan, u_int32_t *sched_cals) { struct ath_hal_9300 *ahp = AH9300(ah); HAL_CAL_LIST *curr_cal = ahp->ah_cal_list_curr; HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); int16_t nf_buf[HAL_NUM_NF_READINGS]; *is_cal_done = AH_TRUE; /* XXX: For initial wasp bringup - disable periodic calibration */ /* Invalid channel check */ if (ichan == AH_NULL) { HALDEBUG(ah, HAL_DEBUG_CHANNEL, "%s: invalid channel %u/0x%x; no mapping\n", __func__, chan->ic_freq, chan->ic_flags); return AH_FALSE; } HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Entering, Doing NF Cal = %d\n", __func__, do_nf_cal); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Chain 0 Rx IQ Cal Correction 0x%08x\n", __func__, OS_REG_READ(ah, AR_PHY_RX_IQCAL_CORR_B0)); if (!AR_SREV_HORNET(ah) && !AR_SREV_POSEIDON(ah) && !AR_SREV_APHRODITE(ah)) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Chain 1 Rx IQ Cal Correction 0x%08x\n", __func__, OS_REG_READ(ah, AR_PHY_RX_IQCAL_CORR_B1)); if (!AR_SREV_WASP(ah) && !AR_SREV_JUPITER(ah)) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Chain 2 Rx IQ Cal Correction 0x%08x\n", __func__, OS_REG_READ(ah, AR_PHY_RX_IQCAL_CORR_B2)); } } OS_MARK(ah, AH_MARK_PERCAL, chan->ic_freq); /* For given calibration: * 1. Call generic cal routine * 2. When this cal is done (is_cal_done) if we have more cals waiting * (eg after reset), mask this to upper layers by not propagating * is_cal_done if it is set to TRUE. * Instead, change is_cal_done to FALSE and setup the waiting cal(s) * to be run. */ if (curr_cal && (curr_cal->cal_data->cal_type & *sched_cals) && (curr_cal->cal_state == CAL_RUNNING || curr_cal->cal_state == CAL_WAITING)) { ar9300_per_calibration(ah, ichan, rxchainmask, curr_cal, is_cal_done); if (*is_cal_done == AH_TRUE) { ahp->ah_cal_list_curr = curr_cal = curr_cal->cal_next; if (curr_cal && curr_cal->cal_state == CAL_WAITING) { *is_cal_done = AH_FALSE; ar9300_reset_calibration(ah, curr_cal); } else { *sched_cals &= ~IQ_MISMATCH_CAL; } } } /* Do NF cal only at longer intervals */ if (do_nf_cal) { int nf_done; /* Get the value from the previous NF cal and update history buffer */ nf_done = ar9300_store_new_nf(ah, chan, is_scan); #if 0 if (ichan->channel_flags & CHANNEL_CW_INT) { chan->channel_flags |= CHANNEL_CW_INT; } #endif chan->ic_state &= ~IEEE80211_CHANSTATE_CWINT; if (nf_done) { /* * Load the NF from history buffer of the current channel. * NF is slow time-variant, so it is OK to use a historical value. */ ar9300_get_nf_hist_base(ah, ichan, is_scan, nf_buf); ar9300_load_nf(ah, nf_buf); /* start NF calibration, without updating BB NF register*/ - ar9300_start_nf_cal(ah); + ar9300_start_nf_cal(ah); } } return AH_TRUE; } /* ar9300_iq_cal_collect * Collect data from HW to later perform IQ Mismatch Calibration */ void ar9300_iq_cal_collect(struct ath_hal *ah, u_int8_t num_chains) { struct ath_hal_9300 *ahp = AH9300(ah); int i; /* * Accumulate IQ cal measures for active chains */ for (i = 0; i < num_chains; i++) { ahp->ah_total_power_meas_i[i] = OS_REG_READ(ah, AR_PHY_CAL_MEAS_0(i)); ahp->ah_total_power_meas_q[i] = OS_REG_READ(ah, AR_PHY_CAL_MEAS_1(i)); ahp->ah_total_iq_corr_meas[i] = (int32_t) OS_REG_READ(ah, AR_PHY_CAL_MEAS_2(i)); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%d: Chn %d " "Reg Offset(0x%04x)pmi=0x%08x; " "Reg Offset(0x%04x)pmq=0x%08x; " "Reg Offset (0x%04x)iqcm=0x%08x;\n", ahp->ah_cal_samples, i, (unsigned) AR_PHY_CAL_MEAS_0(i), ahp->ah_total_power_meas_i[i], (unsigned) AR_PHY_CAL_MEAS_1(i), ahp->ah_total_power_meas_q[i], (unsigned) AR_PHY_CAL_MEAS_2(i), ahp->ah_total_iq_corr_meas[i]); } } /* ar9300_iq_calibration * Use HW data to perform IQ Mismatch Calibration */ void ar9300_iq_calibration(struct ath_hal *ah, u_int8_t num_chains) { struct ath_hal_9300 *ahp = AH9300(ah); u_int32_t power_meas_q, power_meas_i, iq_corr_meas; u_int32_t q_coff_denom, i_coff_denom; int32_t q_coff, i_coff; int iq_corr_neg, i; + HAL_CHANNEL_INTERNAL *ichan; static const u_int32_t offset_array[3] = { AR_PHY_RX_IQCAL_CORR_B0, AR_PHY_RX_IQCAL_CORR_B1, AR_PHY_RX_IQCAL_CORR_B2, }; + ichan = ath_hal_checkchannel(ah, AH_PRIVATE(ah)->ah_curchan); + for (i = 0; i < num_chains; i++) { power_meas_i = ahp->ah_total_power_meas_i[i]; power_meas_q = ahp->ah_total_power_meas_q[i]; iq_corr_meas = ahp->ah_total_iq_corr_meas[i]; HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "Starting IQ Cal and Correction for Chain %d\n", i); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "Orignal: Chn %diq_corr_meas = 0x%08x\n", i, ahp->ah_total_iq_corr_meas[i]); iq_corr_neg = 0; /* iq_corr_meas is always negative. */ if (iq_corr_meas > 0x80000000) { iq_corr_meas = (0xffffffff - iq_corr_meas) + 1; iq_corr_neg = 1; } HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "Chn %d pwr_meas_i = 0x%08x\n", i, power_meas_i); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "Chn %d pwr_meas_q = 0x%08x\n", i, power_meas_q); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "iq_corr_neg is 0x%08x\n", iq_corr_neg); i_coff_denom = (power_meas_i / 2 + power_meas_q / 2) / 256; q_coff_denom = power_meas_q / 64; /* Protect against divide-by-0 */ if ((i_coff_denom != 0) && (q_coff_denom != 0)) { /* IQ corr_meas is already negated if iqcorr_neg == 1 */ i_coff = iq_corr_meas / i_coff_denom; q_coff = power_meas_i / q_coff_denom - 64; HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "Chn %d i_coff = 0x%08x\n", i, i_coff); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "Chn %d q_coff = 0x%08x\n", i, q_coff); /* Force bounds on i_coff */ if (i_coff >= 63) { i_coff = 63; } else if (i_coff <= -63) { i_coff = -63; } /* Negate i_coff if iq_corr_neg == 0 */ if (iq_corr_neg == 0x0) { i_coff = -i_coff; } /* Force bounds on q_coff */ if (q_coff >= 63) { q_coff = 63; } else if (q_coff <= -63) { q_coff = -63; } i_coff = i_coff & 0x7f; q_coff = q_coff & 0x7f; HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "Chn %d : i_coff = 0x%x q_coff = 0x%x\n", i, i_coff, q_coff); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "Register offset (0x%04x) before update = 0x%x\n", offset_array[i], OS_REG_READ(ah, offset_array[i])); OS_REG_RMW_FIELD(ah, offset_array[i], AR_PHY_RX_IQCAL_CORR_IQCORR_Q_I_COFF, i_coff); OS_REG_RMW_FIELD(ah, offset_array[i], AR_PHY_RX_IQCAL_CORR_IQCORR_Q_Q_COFF, q_coff); + /* store the RX cal results */ + if (ichan != NULL) { + ahp->ah_rx_cal_corr[i] = OS_REG_READ(ah, offset_array[i]) & 0x7fff; + ahp->ah_rx_cal_complete = AH_TRUE; + ahp->ah_rx_cal_chan = ichan->channel; +// ahp->ah_rx_cal_chan_flag = ichan->channel_flags &~ CHANNEL_PASSIVE; + ahp->ah_rx_cal_chan_flag = 0; /* XXX */ + } else { + /* XXX? Is this what I should do? */ + ahp->ah_rx_cal_complete = AH_FALSE; + + } + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "Register offset (0x%04x) QI COFF (bitfields 0x%08x) " "after update = 0x%x\n", offset_array[i], AR_PHY_RX_IQCAL_CORR_IQCORR_Q_I_COFF, OS_REG_READ(ah, offset_array[i])); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "Register offset (0x%04x) QQ COFF (bitfields 0x%08x) " "after update = 0x%x\n", offset_array[i], AR_PHY_RX_IQCAL_CORR_IQCORR_Q_Q_COFF, OS_REG_READ(ah, offset_array[i])); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "IQ Cal and Correction done for Chain %d\n", i); } } OS_REG_SET_BIT(ah, AR_PHY_RX_IQCAL_CORR_B0, AR_PHY_RX_IQCAL_CORR_IQCORR_ENABLE); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "IQ Cal and Correction (offset 0x%04x) enabled " "(bit position 0x%08x). New Value 0x%08x\n", (unsigned) (AR_PHY_RX_IQCAL_CORR_B0), AR_PHY_RX_IQCAL_CORR_IQCORR_ENABLE, OS_REG_READ(ah, AR_PHY_RX_IQCAL_CORR_B0)); } /* + * When coming back from offchan, we do not perform RX IQ Cal. + * But the chip reset will clear all previous results + * We store the previous results and restore here. + */ +static void +ar9300_rx_iq_cal_restore(struct ath_hal *ah) +{ + struct ath_hal_9300 *ahp = AH9300(ah); + u_int32_t i_coff, q_coff; + HAL_BOOL is_restore = AH_FALSE; + int i; + static const u_int32_t offset_array[3] = { + AR_PHY_RX_IQCAL_CORR_B0, + AR_PHY_RX_IQCAL_CORR_B1, + AR_PHY_RX_IQCAL_CORR_B2, + }; + + for (i=0; iah_rx_cal_corr[i]) { + i_coff = (ahp->ah_rx_cal_corr[i] & + AR_PHY_RX_IQCAL_CORR_IQCORR_Q_I_COFF) >> + AR_PHY_RX_IQCAL_CORR_IQCORR_Q_I_COFF_S; + q_coff = (ahp->ah_rx_cal_corr[i] & + AR_PHY_RX_IQCAL_CORR_IQCORR_Q_Q_COFF) >> + AR_PHY_RX_IQCAL_CORR_IQCORR_Q_Q_COFF_S; + + OS_REG_RMW_FIELD(ah, offset_array[i], + AR_PHY_RX_IQCAL_CORR_IQCORR_Q_I_COFF, i_coff); + OS_REG_RMW_FIELD(ah, offset_array[i], + AR_PHY_RX_IQCAL_CORR_IQCORR_Q_Q_COFF, q_coff); + + is_restore = AH_TRUE; + } + } + + if (is_restore) + OS_REG_SET_BIT(ah, + AR_PHY_RX_IQCAL_CORR_B0, AR_PHY_RX_IQCAL_CORR_IQCORR_ENABLE); + + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "%s: IQ Cal and Correction (offset 0x%04x) enabled " + "(bit position 0x%08x). New Value 0x%08x\n", + __func__, + (unsigned) (AR_PHY_RX_IQCAL_CORR_B0), + AR_PHY_RX_IQCAL_CORR_IQCORR_ENABLE, + OS_REG_READ(ah, AR_PHY_RX_IQCAL_CORR_B0)); +} + +/* * Set a limit on the overall output power. Used for dynamic * transmit power control and the like. * * NB: limit is in units of 0.5 dbM. */ HAL_BOOL ar9300_set_tx_power_limit(struct ath_hal *ah, u_int32_t limit, u_int16_t extra_txpow, u_int16_t tpc_in_db) { struct ath_hal_9300 *ahp = AH9300(ah); struct ath_hal_private *ahpriv = AH_PRIVATE(ah); const struct ieee80211_channel *chan = ahpriv->ah_curchan; HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); if (NULL == chan) { return AH_FALSE; } ahpriv->ah_powerLimit = AH_MIN(limit, MAX_RATE_POWER); ahpriv->ah_extraTxPow = extra_txpow; if(chan == NULL) { return AH_FALSE; } if (ar9300_eeprom_set_transmit_power(ah, &ahp->ah_eeprom, chan, ath_hal_getctl(ah, chan), ath_hal_getantennaallowed(ah, chan), ath_hal_get_twice_max_regpower(ahpriv, ichan, chan), AH_MIN(MAX_RATE_POWER, ahpriv->ah_powerLimit)) != HAL_OK) { return AH_FALSE; } return AH_TRUE; } /* * Exported call to check for a recent gain reading and return * the current state of the thermal calibration gain engine. */ HAL_RFGAIN ar9300_get_rfgain(struct ath_hal *ah) { return HAL_RFGAIN_INACTIVE; } #define HAL_GREEN_AP_RX_MASK 0x1 static inline void ar9300_init_chain_masks(struct ath_hal *ah, int rx_chainmask, int tx_chainmask) { if (AH9300(ah)->green_ap_ps_on) { rx_chainmask = HAL_GREEN_AP_RX_MASK; } if (rx_chainmask == 0x5) { OS_REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, AR_PHY_SWAP_ALT_CHAIN); } OS_REG_WRITE(ah, AR_PHY_RX_CHAINMASK, rx_chainmask); OS_REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, rx_chainmask); /* * Adaptive Power Management: * Some 3 stream chips exceed the PCIe power requirements. * This workaround will reduce power consumption by using 2 tx chains * for 1 and 2 stream rates (5 GHz only). * * Set the self gen mask to 2 tx chains when APM is enabled. * */ if (AH_PRIVATE(ah)->ah_caps.halApmEnable && (tx_chainmask == 0x7)) { OS_REG_WRITE(ah, AR_SELFGEN_MASK, 0x3); } else { OS_REG_WRITE(ah, AR_SELFGEN_MASK, tx_chainmask); } if (tx_chainmask == 0x5) { OS_REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, AR_PHY_SWAP_ALT_CHAIN); } } /* * Override INI values with chip specific configuration. */ static inline void ar9300_override_ini(struct ath_hal *ah, struct ieee80211_channel *chan) { u_int32_t val; HAL_CAPABILITIES *p_cap = &AH_PRIVATE(ah)->ah_caps; /* * Set the RX_ABORT and RX_DIS and clear it only after * RXE is set for MAC. This prevents frames with * corrupted descriptor status. */ OS_REG_SET_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); /* * For Merlin and above, there is a new feature that allows Multicast * search based on both MAC Address and Key ID. * By default, this feature is enabled. * But since the driver is not using this feature, we switch it off; * otherwise multicast search based on MAC addr only will fail. */ val = OS_REG_READ(ah, AR_PCU_MISC_MODE2) & (~AR_ADHOC_MCAST_KEYID_ENABLE); OS_REG_WRITE(ah, AR_PCU_MISC_MODE2, val | AR_BUG_58603_FIX_ENABLE | AR_AGG_WEP_ENABLE); /* Osprey revision specific configuration */ /* Osprey 2.0+ - if SW RAC support is disabled, must also disable * the Osprey 2.0 hardware RAC fix. */ if (p_cap->halIsrRacSupport == AH_FALSE) { OS_REG_CLR_BIT(ah, AR_CFG, AR_CFG_MISSING_TX_INTR_FIX_ENABLE); } /* try to enable old pal if it is needed for h/w green tx */ ar9300_hwgreentx_set_pal_spare(ah, 1); } static inline void ar9300_prog_ini(struct ath_hal *ah, struct ar9300_ini_array *ini_arr, int column) { int i, reg_writes = 0; /* New INI format: Array may be undefined (pre, core, post arrays) */ if (ini_arr->ia_array == NULL) { return; } /* * New INI format: Pre, core, and post arrays for a given subsystem may be * modal (> 2 columns) or non-modal (2 columns). * Determine if the array is non-modal and force the column to 1. */ if (column >= ini_arr->ia_columns) { column = 1; } for (i = 0; i < ini_arr->ia_rows; i++) { u_int32_t reg = INI_RA(ini_arr, i, 0); u_int32_t val = INI_RA(ini_arr, i, column); /* ** Determine if this is a shift register value ** (reg >= 0x16000 && reg < 0x17000 for Osprey) , ** and insert the configured delay if so. ** -this delay is not required for Osprey (EV#71410) */ OS_REG_WRITE(ah, reg, val); WAR_6773(reg_writes); } } static inline HAL_STATUS ar9300_process_ini(struct ath_hal *ah, struct ieee80211_channel *chan, HAL_CHANNEL_INTERNAL *ichan, HAL_HT_MACMODE macmode) { int reg_writes = 0; struct ath_hal_9300 *ahp = AH9300(ah); u_int modes_index, modes_txgaintable_index = 0; int i; HAL_STATUS status; struct ath_hal_private *ahpriv = AH_PRIVATE(ah); /* Setup the indices for the next set of register array writes */ /* TODO: * If the channel marker is indicative of the current mode rather * than capability, we do not need to check the phy mode below. */ #if 0 switch (chan->channel_flags & CHANNEL_ALL) { case CHANNEL_A: case CHANNEL_A_HT20: if (AR_SREV_SCORPION(ah)){ if (chan->channel <= 5350){ modes_txgaintable_index = 1; }else if ((chan->channel > 5350) && (chan->channel <= 5600)){ modes_txgaintable_index = 3; }else if (chan->channel > 5600){ modes_txgaintable_index = 5; } } modes_index = 1; break; case CHANNEL_A_HT40PLUS: case CHANNEL_A_HT40MINUS: if (AR_SREV_SCORPION(ah)){ if (chan->channel <= 5350){ modes_txgaintable_index = 2; }else if ((chan->channel > 5350) && (chan->channel <= 5600)){ modes_txgaintable_index = 4; }else if (chan->channel > 5600){ modes_txgaintable_index = 6; } } modes_index = 2; break; case CHANNEL_PUREG: case CHANNEL_G_HT20: case CHANNEL_B: if (AR_SREV_SCORPION(ah)){ modes_txgaintable_index = 8; } modes_index = 4; break; case CHANNEL_G_HT40PLUS: case CHANNEL_G_HT40MINUS: if (AR_SREV_SCORPION(ah)){ modes_txgaintable_index = 7; } modes_index = 3; break; case CHANNEL_108G: modes_index = 5; break; default: HALASSERT(0); return HAL_EINVAL; } #endif /* FreeBSD */ if (IS_CHAN_5GHZ(ichan)) { if (IEEE80211_IS_CHAN_HT40U(chan) || IEEE80211_IS_CHAN_HT40D(chan)) { if (AR_SREV_SCORPION(ah)){ if (ichan->channel <= 5350){ modes_txgaintable_index = 2; }else if ((ichan->channel > 5350) && (ichan->channel <= 5600)){ modes_txgaintable_index = 4; }else if (ichan->channel > 5600){ modes_txgaintable_index = 6; } } modes_index = 2; } else if (IEEE80211_IS_CHAN_A(chan) || IEEE80211_IS_CHAN_HT20(chan)) { if (AR_SREV_SCORPION(ah)){ if (ichan->channel <= 5350){ modes_txgaintable_index = 1; }else if ((ichan->channel > 5350) && (ichan->channel <= 5600)){ modes_txgaintable_index = 3; }else if (ichan->channel > 5600){ modes_txgaintable_index = 5; } } modes_index = 1; } else return HAL_EINVAL; } else if (IS_CHAN_2GHZ(ichan)) { if (IEEE80211_IS_CHAN_108G(chan)) { modes_index = 5; } else if (IEEE80211_IS_CHAN_HT40U(chan) || IEEE80211_IS_CHAN_HT40D(chan)) { if (AR_SREV_SCORPION(ah)){ modes_txgaintable_index = 7; } modes_index = 3; } else if (IEEE80211_IS_CHAN_HT20(chan) || IEEE80211_IS_CHAN_G(chan) || IEEE80211_IS_CHAN_B(chan) || IEEE80211_IS_CHAN_PUREG(chan)) { if (AR_SREV_SCORPION(ah)){ modes_txgaintable_index = 8; } modes_index = 4; } else return HAL_EINVAL; } else return HAL_EINVAL; #if 0 /* Set correct Baseband to analog shift setting to access analog chips. */ OS_REG_WRITE(ah, AR_PHY(0), 0x00000007); #endif HALDEBUG(ah, HAL_DEBUG_RESET, "ar9300_process_ini: " "Skipping OS-REG-WRITE(ah, AR-PHY(0), 0x00000007)\n"); HALDEBUG(ah, HAL_DEBUG_RESET, "ar9300_process_ini: no ADDac programming\n"); /* * Osprey 2.0+ - new INI format. * Each subsystem has a pre, core, and post array. */ for (i = 0; i < ATH_INI_NUM_SPLIT; i++) { ar9300_prog_ini(ah, &ahp->ah_ini_soc[i], modes_index); ar9300_prog_ini(ah, &ahp->ah_ini_mac[i], modes_index); ar9300_prog_ini(ah, &ahp->ah_ini_bb[i], modes_index); ar9300_prog_ini(ah, &ahp->ah_ini_radio[i], modes_index); if ((i == ATH_INI_POST) && (AR_SREV_JUPITER_20(ah) || AR_SREV_APHRODITE(ah))) { ar9300_prog_ini(ah, &ahp->ah_ini_radio_post_sys2ant, modes_index); } } if (!(AR_SREV_SOC(ah))) { /* Doubler issue : Some board doesn't work well with MCS15. Turn off doubler after freq locking is complete*/ //ath_hal_printf(ah, "%s[%d] ==== before reg[0x%08x] = 0x%08x\n", __func__, __LINE__, AR_PHY_65NM_CH0_RXTX2, OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX2)); OS_REG_RMW(ah, AR_PHY_65NM_CH0_RXTX2, 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S | 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S, 0); /*Set synthon, synthover */ //ath_hal_printf(ah, "%s[%d] ==== after reg[0x%08x] = 0x%08x\n", __func__, __LINE__, AR_PHY_65NM_CH0_RXTX2, OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX2)); OS_REG_RMW(ah, AR_PHY_65NM_CH1_RXTX2, 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S | 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S, 0); /*Set synthon, synthover */ OS_REG_RMW(ah, AR_PHY_65NM_CH2_RXTX2, 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S | 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S, 0); /*Set synthon, synthover */ OS_DELAY(200); //ath_hal_printf(ah, "%s[%d] ==== before reg[0x%08x] = 0x%08x\n", __func__, __LINE__, AR_PHY_65NM_CH0_RXTX2, OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX2)); OS_REG_CLR_BIT(ah, AR_PHY_65NM_CH0_RXTX2, AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK); /* clr synthon */ OS_REG_CLR_BIT(ah, AR_PHY_65NM_CH1_RXTX2, AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK); /* clr synthon */ OS_REG_CLR_BIT(ah, AR_PHY_65NM_CH2_RXTX2, AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK); /* clr synthon */ //ath_hal_printf(ah, "%s[%d] ==== after reg[0x%08x] = 0x%08x\n", __func__, __LINE__, AR_PHY_65NM_CH0_RXTX2, OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX2)); OS_DELAY(1); //ath_hal_printf(ah, "%s[%d] ==== before reg[0x%08x] = 0x%08x\n", __func__, __LINE__, AR_PHY_65NM_CH0_RXTX2, OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX2)); OS_REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_RXTX2, AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK, 1); /* set synthon */ OS_REG_RMW_FIELD(ah, AR_PHY_65NM_CH1_RXTX2, AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK, 1); /* set synthon */ OS_REG_RMW_FIELD(ah, AR_PHY_65NM_CH2_RXTX2, AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK, 1); /* set synthon */ //ath_hal_printf(ah, "%s[%d] ==== after reg[0x%08x] = 0x%08x\n", __func__, __LINE__, AR_PHY_65NM_CH0_RXTX2, OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX2)); OS_DELAY(200); //ath_hal_printf(ah, "%s[%d] ==== before reg[0x%08x] = 0x%08x\n", __func__, __LINE__, AR_PHY_65NM_CH0_SYNTH12, OS_REG_READ(ah, AR_PHY_65NM_CH0_SYNTH12)); OS_REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_SYNTH12, AR_PHY_65NM_CH0_SYNTH12_VREFMUL3, 0xf); //OS_REG_CLR_BIT(ah, AR_PHY_65NM_CH0_SYNTH12, 1<< 16); /* clr charge pump */ //ath_hal_printf(ah, "%s[%d] ==== After reg[0x%08x] = 0x%08x\n", __func__, __LINE__, AR_PHY_65NM_CH0_SYNTH12, OS_REG_READ(ah, AR_PHY_65NM_CH0_SYNTH12)); OS_REG_RMW(ah, AR_PHY_65NM_CH0_RXTX2, 0, 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S | 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S); /*Clr synthon, synthover */ OS_REG_RMW(ah, AR_PHY_65NM_CH1_RXTX2, 0, 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S | 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S); /*Clr synthon, synthover */ OS_REG_RMW(ah, AR_PHY_65NM_CH2_RXTX2, 0, 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S | 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S); /*Clr synthon, synthover */ //ath_hal_printf(ah, "%s[%d] ==== after reg[0x%08x] = 0x%08x\n", __func__, __LINE__, AR_PHY_65NM_CH0_RXTX2, OS_REG_READ(ah, AR_PHY_65NM_CH0_RXTX2)); } /* Write rxgain Array Parameters */ REG_WRITE_ARRAY(&ahp->ah_ini_modes_rxgain, 1, reg_writes); HALDEBUG(ah, HAL_DEBUG_RESET, "ar9300_process_ini: Rx Gain programming\n"); if (AR_SREV_SCORPION(ah)) { /* Write rxgain bounds Array */ REG_WRITE_ARRAY(&ahp->ah_ini_modes_rxgain_bounds, modes_index, reg_writes); HALDEBUG(ah, HAL_DEBUG_RESET, "ar9300_process_ini: Rx Gain table bounds programming\n"); } /* UB124 xLNA settings */ if (AR_SREV_WASP(ah) && ar9300_rx_gain_index_get(ah) == 2) { #define REG_WRITE(_reg,_val) *((volatile u_int32_t *)(_reg)) = (_val); #define REG_READ(_reg) *((volatile u_int32_t *)(_reg)) u_int32_t val; /* B8040000: bit[0]=0, bit[3]=0; */ val = REG_READ(0xB8040000); val &= 0xfffffff6; REG_WRITE(0xB8040000, val); /* B804002c: bit[31:24]=0x2e; bit[7:0]=0x2f; */ val = REG_READ(0xB804002c); val &= 0x00ffff00; val |= 0x2e00002f; REG_WRITE(0xB804002c, val); /* B804006c: bit[1]=1; */ val = REG_READ(0xB804006c); val |= 0x2; REG_WRITE(0xB804006c, val); #undef REG_READ #undef REG_WRITE } /* Write txgain Array Parameters */ if (AR_SREV_SCORPION(ah)) { REG_WRITE_ARRAY(&ahp->ah_ini_modes_txgain, modes_txgaintable_index, reg_writes); }else{ REG_WRITE_ARRAY(&ahp->ah_ini_modes_txgain, modes_index, reg_writes); } HALDEBUG(ah, HAL_DEBUG_RESET, "ar9300_process_ini: Tx Gain programming\n"); /* For 5GHz channels requiring Fast Clock, apply different modal values */ if (IS_5GHZ_FAST_CLOCK_EN(ah, chan)) { HALDEBUG(ah, HAL_DEBUG_RESET, "%s: Fast clock enabled, use special ini values\n", __func__); REG_WRITE_ARRAY(&ahp->ah_ini_modes_additional, modes_index, reg_writes); } if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah)) { HALDEBUG(ah, HAL_DEBUG_RESET, "%s: use xtal ini for AH9300(ah)->clk_25mhz: %d\n", __func__, AH9300(ah)->clk_25mhz); REG_WRITE_ARRAY( &ahp->ah_ini_modes_additional, 1/*modes_index*/, reg_writes); } if (AR_SREV_WASP(ah) && (AH9300(ah)->clk_25mhz == 0)) { HALDEBUG(ah, HAL_DEBUG_RESET, "%s: Apply 40MHz ini settings\n", __func__); REG_WRITE_ARRAY( &ahp->ah_ini_modes_additional_40mhz, 1/*modesIndex*/, reg_writes); } /* Handle Japan Channel 14 channel spreading */ if (2484 == ichan->channel) { ar9300_prog_ini(ah, &ahp->ah_ini_japan2484, 1); } #if 0 if (AR_SREV_JUPITER_20(ah) || AR_SREV_APHRODITE(ah)) { ar9300_prog_ini(ah, &ahp->ah_ini_BTCOEX_MAX_TXPWR, 1); } #endif /* Override INI with chip specific configuration */ ar9300_override_ini(ah, chan); /* Setup 11n MAC/Phy mode registers */ ar9300_set_11n_regs(ah, chan, macmode); /* * Moved ar9300_init_chain_masks() here to ensure the swap bit is set before * the pdadc table is written. Swap must occur before any radio dependent * replicated register access. The pdadc curve addressing in particular * depends on the consistent setting of the swap bit. */ ar9300_init_chain_masks(ah, ahp->ah_rx_chainmask, ahp->ah_tx_chainmask); /* * Setup the transmit power values. * * After the public to private hal channel mapping, ichan contains the * valid regulatory power value. * ath_hal_getctl and ath_hal_getantennaallowed look up ichan from chan. */ status = ar9300_eeprom_set_transmit_power(ah, &ahp->ah_eeprom, chan, ath_hal_getctl(ah, chan), ath_hal_getantennaallowed(ah, chan), ath_hal_get_twice_max_regpower(ahpriv, ichan, chan), AH_MIN(MAX_RATE_POWER, ahpriv->ah_powerLimit)); if (status != HAL_OK) { HALDEBUG(ah, HAL_DEBUG_POWER_MGMT, "%s: error init'ing transmit power\n", __func__); return HAL_EIO; } return HAL_OK; #undef N } /* ar9300_is_cal_supp * Determine if calibration is supported by device and channel flags */ inline static HAL_BOOL ar9300_is_cal_supp(struct ath_hal *ah, const struct ieee80211_channel *chan, HAL_CAL_TYPES cal_type) { struct ath_hal_9300 *ahp = AH9300(ah); HAL_BOOL retval = AH_FALSE; switch (cal_type & ahp->ah_supp_cals) { case IQ_MISMATCH_CAL: /* Run IQ Mismatch for non-CCK only */ if (!IEEE80211_IS_CHAN_B(chan)) { retval = AH_TRUE; } break; case TEMP_COMP_CAL: retval = AH_TRUE; break; } return retval; } #if 0 /* ar9285_pa_cal * PA Calibration for Kite 1.1 and later versions of Kite. * - from system's team. */ static inline void ar9285_pa_cal(struct ath_hal *ah) { u_int32_t reg_val; int i, lo_gn, offs_6_1, offs_0; u_int8_t reflo; u_int32_t phy_test2_reg_val, phy_adc_ctl_reg_val; u_int32_t an_top2_reg_val, phy_tst_dac_reg_val; /* Kite 1.1 WAR for Bug 35666 * Increase the LDO value to 1.28V before accessing analog Reg */ if (AR_SREV_KITE_11(ah)) { OS_REG_WRITE(ah, AR9285_AN_TOP4, (AR9285_AN_TOP4_DEFAULT | 0x14) ); } an_top2_reg_val = OS_REG_READ(ah, AR9285_AN_TOP2); /* set pdv2i pdrxtxbb */ reg_val = OS_REG_READ(ah, AR9285_AN_RXTXBB1); reg_val |= ((0x1 << 5) | (0x1 << 7)); OS_REG_WRITE(ah, AR9285_AN_RXTXBB1, reg_val); /* clear pwddb */ reg_val = OS_REG_READ(ah, AR9285_AN_RF2G7); reg_val &= 0xfffffffd; OS_REG_WRITE(ah, AR9285_AN_RF2G7, reg_val); /* clear enpacal */ reg_val = OS_REG_READ(ah, AR9285_AN_RF2G1); reg_val &= 0xfffff7ff; OS_REG_WRITE(ah, AR9285_AN_RF2G1, reg_val); /* set offcal */ reg_val = OS_REG_READ(ah, AR9285_AN_RF2G2); reg_val |= (0x1 << 12); OS_REG_WRITE(ah, AR9285_AN_RF2G2, reg_val); /* set pdpadrv1=pdpadrv2=pdpaout=1 */ reg_val = OS_REG_READ(ah, AR9285_AN_RF2G1); reg_val |= (0x7 << 23); OS_REG_WRITE(ah, AR9285_AN_RF2G1, reg_val); /* Read back reflo, increase it by 1 and write it. */ reg_val = OS_REG_READ(ah, AR9285_AN_RF2G3); reflo = ((reg_val >> 26) & 0x7); if (reflo < 0x7) { reflo++; } reg_val = ((reg_val & 0xe3ffffff) | (reflo << 26)); OS_REG_WRITE(ah, AR9285_AN_RF2G3, reg_val); reg_val = OS_REG_READ(ah, AR9285_AN_RF2G3); reflo = ((reg_val >> 26) & 0x7); /* use TX single carrier to transmit * dac const * reg. 15 */ phy_tst_dac_reg_val = OS_REG_READ(ah, AR_PHY_TSTDAC_CONST); OS_REG_WRITE(ah, AR_PHY_TSTDAC_CONST, ((0x7ff << 11) | 0x7ff)); reg_val = OS_REG_READ(ah, AR_PHY_TSTDAC_CONST); /* source is dac const * reg. 2 */ phy_test2_reg_val = OS_REG_READ(ah, AR_PHY_TEST2); OS_REG_WRITE(ah, AR_PHY_TEST2, ((0x1 << 7) | (0x1 << 1))); reg_val = OS_REG_READ(ah, AR_PHY_TEST2); /* set dac on * reg. 11 */ phy_adc_ctl_reg_val = OS_REG_READ(ah, AR_PHY_ADC_CTL); OS_REG_WRITE(ah, AR_PHY_ADC_CTL, 0x80008000); reg_val = OS_REG_READ(ah, AR_PHY_ADC_CTL); OS_REG_WRITE(ah, AR9285_AN_TOP2, (0x1 << 27) | (0x1 << 17) | (0x1 << 16) | (0x1 << 14) | (0x1 << 12) | (0x1 << 11) | (0x1 << 7) | (0x1 << 5)); OS_DELAY(10); /* 10 usec */ /* clear off[6:0] */ reg_val = OS_REG_READ(ah, AR9285_AN_RF2G6); reg_val &= 0xfc0fffff; OS_REG_WRITE(ah, AR9285_AN_RF2G6, reg_val); reg_val = OS_REG_READ(ah, AR9285_AN_RF2G3); reg_val &= 0xfdffffff; OS_REG_WRITE(ah, AR9285_AN_RF2G3, reg_val); offs_6_1 = 0; for (i = 6; i > 0; i--) { /* sef off[$k]==1 */ reg_val = OS_REG_READ(ah, AR9285_AN_RF2G6); reg_val &= 0xfc0fffff; reg_val = reg_val | (0x1 << (19 + i)) | ((offs_6_1) << 20); OS_REG_WRITE(ah, AR9285_AN_RF2G6, reg_val); lo_gn = (OS_REG_READ(ah, AR9285_AN_RF2G9)) & 0x1; offs_6_1 = offs_6_1 | (lo_gn << (i - 1)); } reg_val = OS_REG_READ(ah, AR9285_AN_RF2G6); reg_val &= 0xfc0fffff; reg_val = reg_val | ((offs_6_1 - 1) << 20); OS_REG_WRITE(ah, AR9285_AN_RF2G6, reg_val); /* set off_0=1; */ reg_val = OS_REG_READ(ah, AR9285_AN_RF2G3); reg_val &= 0xfdffffff; reg_val = reg_val | (0x1 << 25); OS_REG_WRITE(ah, AR9285_AN_RF2G3, reg_val); lo_gn = OS_REG_READ(ah, AR9285_AN_RF2G9) & 0x1; offs_0 = lo_gn; reg_val = OS_REG_READ(ah, AR9285_AN_RF2G3); reg_val &= 0xfdffffff; reg_val = reg_val | (offs_0 << 25); OS_REG_WRITE(ah, AR9285_AN_RF2G3, reg_val); /* clear pdv2i */ reg_val = OS_REG_READ(ah, AR9285_AN_RXTXBB1); reg_val &= 0xffffff5f; OS_REG_WRITE(ah, AR9285_AN_RXTXBB1, reg_val); /* set enpacal */ reg_val = OS_REG_READ(ah, AR9285_AN_RF2G1); reg_val |= (0x1 << 11); OS_REG_WRITE(ah, AR9285_AN_RF2G1, reg_val); /* clear offcal */ reg_val = OS_REG_READ(ah, AR9285_AN_RF2G2); reg_val &= 0xffffefff; OS_REG_WRITE(ah, AR9285_AN_RF2G2, reg_val); /* set pdpadrv1=pdpadrv2=pdpaout=0 */ reg_val = OS_REG_READ(ah, AR9285_AN_RF2G1); reg_val &= 0xfc7fffff; OS_REG_WRITE(ah, AR9285_AN_RF2G1, reg_val); /* Read back reflo, decrease it by 1 and write it. */ reg_val = OS_REG_READ(ah, AR9285_AN_RF2G3); reflo = (reg_val >> 26) & 0x7; if (reflo) { reflo--; } reg_val = ((reg_val & 0xe3ffffff) | (reflo << 26)); OS_REG_WRITE(ah, AR9285_AN_RF2G3, reg_val); reg_val = OS_REG_READ(ah, AR9285_AN_RF2G3); reflo = (reg_val >> 26) & 0x7; /* write back registers */ OS_REG_WRITE(ah, AR_PHY_TSTDAC_CONST, phy_tst_dac_reg_val); OS_REG_WRITE(ah, AR_PHY_TEST2, phy_test2_reg_val); OS_REG_WRITE(ah, AR_PHY_ADC_CTL, phy_adc_ctl_reg_val); OS_REG_WRITE(ah, AR9285_AN_TOP2, an_top2_reg_val); /* Kite 1.1 WAR for Bug 35666 * Decrease the LDO value back to 1.20V */ if (AR_SREV_KITE_11(ah)) { OS_REG_WRITE(ah, AR9285_AN_TOP4, AR9285_AN_TOP4_DEFAULT); } } #endif /* ar9300_run_init_cals * Runs non-periodic calibrations */ inline static HAL_BOOL ar9300_run_init_cals(struct ath_hal *ah, int init_cal_count) { struct ath_hal_9300 *ahp = AH9300(ah); HAL_CHANNEL_INTERNAL ichan; /* bogus */ HAL_BOOL is_cal_done; HAL_CAL_LIST *curr_cal; const HAL_PERCAL_DATA *cal_data; int i; curr_cal = ahp->ah_cal_list_curr; if (curr_cal == AH_NULL) { return AH_FALSE; } cal_data = curr_cal->cal_data; ichan.calValid = 0; for (i = 0; i < init_cal_count; i++) { /* Reset this Cal */ ar9300_reset_calibration(ah, curr_cal); /* Poll for offset calibration complete */ if (!ath_hal_wait( ah, AR_PHY_TIMING4, AR_PHY_TIMING4_DO_CAL, 0)) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Cal %d failed to complete in 100ms.\n", __func__, curr_cal->cal_data->cal_type); /* Re-initialize list pointers for periodic cals */ ahp->ah_cal_list = ahp->ah_cal_list_last = ahp->ah_cal_list_curr = AH_NULL; return AH_FALSE; } /* Run this cal */ ar9300_per_calibration( ah, &ichan, ahp->ah_rx_chainmask, curr_cal, &is_cal_done); if (is_cal_done == AH_FALSE) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Not able to run Init Cal %d.\n", __func__, curr_cal->cal_data->cal_type); } if (curr_cal->cal_next) { curr_cal = curr_cal->cal_next; } } /* Re-initialize list pointers for periodic cals */ ahp->ah_cal_list = ahp->ah_cal_list_last = ahp->ah_cal_list_curr = AH_NULL; return AH_TRUE; } #if 0 static void ar9300_tx_carrier_leak_war(struct ath_hal *ah) { unsigned long tx_gain_table_max; unsigned long reg_bb_cl_map_0_b0 = 0xffffffff; unsigned long reg_bb_cl_map_1_b0 = 0xffffffff; unsigned long reg_bb_cl_map_2_b0 = 0xffffffff; unsigned long reg_bb_cl_map_3_b0 = 0xffffffff; unsigned long tx_gain, cal_run = 0; unsigned long cal_gain[AR_PHY_TPC_7_TX_GAIN_TABLE_MAX + 1]; unsigned long cal_gain_index[AR_PHY_TPC_7_TX_GAIN_TABLE_MAX + 1]; unsigned long new_gain[AR_PHY_TPC_7_TX_GAIN_TABLE_MAX + 1]; int i, j; OS_MEMSET(new_gain, 0, sizeof(new_gain)); /*printf(" Running TxCarrierLeakWAR\n");*/ /* process tx gain table, we use cl_map_hw_gen=0. */ OS_REG_RMW_FIELD(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_MAP_HW_GEN, 0); //the table we used is txbb_gc[2:0], 1dB[2:1]. tx_gain_table_max = OS_REG_READ_FIELD(ah, AR_PHY_TPC_7, AR_PHY_TPC_7_TX_GAIN_TABLE_MAX); for (i = 0; i <= tx_gain_table_max; i++) { tx_gain = OS_REG_READ(ah, AR_PHY_TXGAIN_TAB(1) + i * 4); cal_gain[i] = (((tx_gain >> 5)& 0x7) << 2) | (((tx_gain >> 1) & 0x3) << 0); if (i == 0) { cal_gain_index[i] = cal_run; new_gain[i] = 1; cal_run++; } else { new_gain[i] = 1; for (j = 0; j < i; j++) { /* printf("i=%d, j=%d cal_gain[$i]=0x%04x\n", i, j, cal_gain[i]); */ if (new_gain[i]) { if ((cal_gain[i] != cal_gain[j])) { new_gain[i] = 1; } else { /* if old gain found, use old cal_run value. */ new_gain[i] = 0; cal_gain_index[i] = cal_gain_index[j]; } } } /* if new gain found, increase cal_run */ if (new_gain[i] == 1) { cal_gain_index[i] = cal_run; cal_run++; } } reg_bb_cl_map_0_b0 = (reg_bb_cl_map_0_b0 & ~(0x1 << i)) | ((cal_gain_index[i] >> 0 & 0x1) << i); reg_bb_cl_map_1_b0 = (reg_bb_cl_map_1_b0 & ~(0x1 << i)) | ((cal_gain_index[i] >> 1 & 0x1) << i); reg_bb_cl_map_2_b0 = (reg_bb_cl_map_2_b0 & ~(0x1 << i)) | ((cal_gain_index[i] >> 2 & 0x1) << i); reg_bb_cl_map_3_b0 = (reg_bb_cl_map_3_b0 & ~(0x1 << i)) | ((cal_gain_index[i] >> 3 & 0x1) << i); /* printf("i=%2d, cal_gain[$i]= 0x%04x, cal_run= %d, " "cal_gain_index[i]=%d, new_gain[i] = %d\n", i, cal_gain[i], cal_run, cal_gain_index[i], new_gain[i]); */ } OS_REG_WRITE(ah, AR_PHY_CL_MAP_0_B0, reg_bb_cl_map_0_b0); OS_REG_WRITE(ah, AR_PHY_CL_MAP_1_B0, reg_bb_cl_map_1_b0); OS_REG_WRITE(ah, AR_PHY_CL_MAP_2_B0, reg_bb_cl_map_2_b0); OS_REG_WRITE(ah, AR_PHY_CL_MAP_3_B0, reg_bb_cl_map_3_b0); if (AR_SREV_WASP(ah)) { OS_REG_WRITE(ah, AR_PHY_CL_MAP_0_B1, reg_bb_cl_map_0_b0); OS_REG_WRITE(ah, AR_PHY_CL_MAP_1_B1, reg_bb_cl_map_1_b0); OS_REG_WRITE(ah, AR_PHY_CL_MAP_2_B1, reg_bb_cl_map_2_b0); OS_REG_WRITE(ah, AR_PHY_CL_MAP_3_B1, reg_bb_cl_map_3_b0); } } #endif static inline void ar9300_invalidate_saved_cals(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *ichan) { #if ATH_SUPPORT_CAL_REUSE if (AH_PRIVATE(ah)->ah_config.ath_hal_cal_reuse & ATH_CAL_REUSE_REDO_IN_FULL_RESET) { ichan->one_time_txiqcal_done = AH_FALSE; ichan->one_time_txclcal_done = AH_FALSE; } #endif } static inline HAL_BOOL ar9300_restore_rtt_cals(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *ichan) { HAL_BOOL restore_status = AH_FALSE; return restore_status; } /* ar9300_init_cal * Initialize Calibration infrastructure */ static inline HAL_BOOL ar9300_init_cal_internal(struct ath_hal *ah, struct ieee80211_channel *chan, HAL_CHANNEL_INTERNAL *ichan, HAL_BOOL enable_rtt, HAL_BOOL do_rtt_cal, HAL_BOOL skip_if_none, HAL_BOOL apply_last_iqcorr) { struct ath_hal_9300 *ahp = AH9300(ah); HAL_BOOL txiqcal_success_flag = AH_FALSE; HAL_BOOL cal_done = AH_FALSE; int iqcal_idx = 0; HAL_BOOL do_sep_iq_cal = AH_FALSE; HAL_BOOL do_agc_cal = do_rtt_cal; HAL_BOOL is_cal_reusable = AH_TRUE; #if ATH_SUPPORT_CAL_REUSE HAL_BOOL cal_reuse_enable = AH_PRIVATE(ah)->ah_config.ath_hal_cal_reuse & ATH_CAL_REUSE_ENABLE; HAL_BOOL clc_success = AH_FALSE; int32_t ch_idx, j, cl_tab_reg; u_int32_t BB_cl_tab_entry = MAX_BB_CL_TABLE_ENTRY; u_int32_t BB_cl_tab_b[AR9300_MAX_CHAINS] = { AR_PHY_CL_TAB_0, AR_PHY_CL_TAB_1, AR_PHY_CL_TAB_2 }; #endif if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah) || AR_SREV_APHRODITE(ah)) { /* Hornet: 1 x 1 */ ahp->ah_rx_cal_chainmask = 0x1; ahp->ah_tx_cal_chainmask = 0x1; } else if (AR_SREV_WASP(ah) || AR_SREV_JUPITER(ah)) { /* Wasp/Jupiter: 2 x 2 */ ahp->ah_rx_cal_chainmask = 0x3; ahp->ah_tx_cal_chainmask = 0x3; } else { /* * Osprey needs to be configured for the correct chain mode * before running AGC/TxIQ cals. */ if (ahp->ah_enterprise_mode & AR_ENT_OTP_CHAIN2_DISABLE) { /* chain 2 disabled - 2 chain mode */ ahp->ah_rx_cal_chainmask = 0x3; ahp->ah_tx_cal_chainmask = 0x3; } else { ahp->ah_rx_cal_chainmask = 0x7; ahp->ah_tx_cal_chainmask = 0x7; } } ar9300_init_chain_masks(ah, ahp->ah_rx_cal_chainmask, ahp->ah_tx_cal_chainmask); if (ahp->tx_cl_cal_enable) { #if ATH_SUPPORT_CAL_REUSE /* disable Carrie Leak or set do_agc_cal accordingly */ if (cal_reuse_enable && ichan->one_time_txclcal_done) { OS_REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); } else #endif /* ATH_SUPPORT_CAL_REUSE */ { OS_REG_SET_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); do_agc_cal = AH_TRUE; } } /* Do Tx IQ Calibration here for osprey hornet and wasp */ /* XXX: For initial wasp bringup - check and enable this */ /* EV 74233: Tx IQ fails to complete for half/quarter rates */ if (!(IEEE80211_IS_CHAN_HALF(chan) || IEEE80211_IS_CHAN_QUARTER(chan))) { if (ahp->tx_iq_cal_enable) { /* this should be eventually moved to INI file */ OS_REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_1(ah), AR_PHY_TX_IQCAL_CONTROL_1_IQCORR_I_Q_COFF_DELPT, DELPT); /* * For poseidon and later chips, * Tx IQ cal HW run will be a part of AGC calibration */ if (ahp->tx_iq_cal_during_agc_cal) { /* * txiqcal_success_flag always set to 1 to run * ar9300_tx_iq_cal_post_proc * if following AGC cal passes */ #if ATH_SUPPORT_CAL_REUSE if (!cal_reuse_enable || !ichan->one_time_txiqcal_done) { txiqcal_success_flag = AH_TRUE; OS_REG_WRITE(ah, AR_PHY_TX_IQCAL_CONTROL_0(ah), OS_REG_READ(ah, AR_PHY_TX_IQCAL_CONTROL_0(ah)) | AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL); } else { OS_REG_WRITE(ah, AR_PHY_TX_IQCAL_CONTROL_0(ah), OS_REG_READ(ah, AR_PHY_TX_IQCAL_CONTROL_0(ah)) & (~AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL)); } #else if (OS_REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_0(ah), AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL)){ if (apply_last_iqcorr == AH_TRUE) { OS_REG_CLR_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0(ah), AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL); txiqcal_success_flag = AH_FALSE; } else { txiqcal_success_flag = AH_TRUE; } }else{ txiqcal_success_flag = AH_FALSE; } #endif if (txiqcal_success_flag) { do_agc_cal = AH_TRUE; } } else #if ATH_SUPPORT_CAL_REUSE if (!cal_reuse_enable || !ichan->one_time_txiqcal_done) #endif { do_sep_iq_cal = AH_TRUE; do_agc_cal = AH_TRUE; } } } #if ATH_SUPPORT_MCI if (AH_PRIVATE(ah)->ah_caps.halMciSupport && IS_CHAN_2GHZ(ichan) && (ahp->ah_mci_bt_state == MCI_BT_AWAKE) && do_agc_cal && !(ah->ah_config.ath_hal_mci_config & ATH_MCI_CONFIG_DISABLE_MCI_CAL)) { u_int32_t payload[4] = {0, 0, 0, 0}; /* Send CAL_REQ only when BT is AWAKE. */ HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) %s: Send WLAN_CAL_REQ 0x%X\n", __func__, ahp->ah_mci_wlan_cal_seq); MCI_GPM_SET_CAL_TYPE(payload, MCI_GPM_WLAN_CAL_REQ); payload[MCI_GPM_WLAN_CAL_W_SEQUENCE] = ahp->ah_mci_wlan_cal_seq++; ar9300_mci_send_message(ah, MCI_GPM, 0, payload, 16, AH_TRUE, AH_FALSE); /* Wait BT_CAL_GRANT for 50ms */ HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) %s: Wait for BT_CAL_GRANT\n", __func__); if (ar9300_mci_wait_for_gpm(ah, MCI_GPM_BT_CAL_GRANT, 0, 50000)) { HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) %s: Got BT_CAL_GRANT.\n", __func__); } else { is_cal_reusable = AH_FALSE; HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) %s: BT is not responding.\n", __func__); } } #endif /* ATH_SUPPORT_MCI */ if (do_sep_iq_cal) { /* enable Tx IQ Calibration HW for osprey/hornet/wasp */ txiqcal_success_flag = ar9300_tx_iq_cal_hw_run(ah); OS_REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS); OS_DELAY(5); OS_REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); } #if 0 if (AR_SREV_HORNET(ah) || AR_SREV_POSEIDON(ah)) { ar9300_tx_carrier_leak_war(ah); } #endif /* * Calibrate the AGC * * Tx IQ cal is a part of AGC cal for Jupiter/Poseidon, etc. * please enable the bit of txiqcal_control_0[31] in INI file * for Jupiter/Poseidon/etc. */ if(!AR_SREV_SCORPION(ah)) { if (do_agc_cal || !skip_if_none) { OS_REG_WRITE(ah, AR_PHY_AGC_CONTROL, OS_REG_READ(ah, AR_PHY_AGC_CONTROL) | AR_PHY_AGC_CONTROL_CAL); /* Poll for offset calibration complete */ cal_done = ath_hal_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0); if (!cal_done) { HALDEBUG(ah, HAL_DEBUG_FCS_RTT, "(FCS) CAL NOT DONE!!! - %d\n", ichan->channel); } } else { cal_done = AH_TRUE; } /* * Tx IQ cal post-processing in SW * This part of code should be common to all chips, * no chip specific code for Jupiter/Posdeion except for register names. */ if (txiqcal_success_flag) { ar9300_tx_iq_cal_post_proc(ah,ichan, 1, 1,is_cal_reusable, AH_FALSE); } } else { if (!txiqcal_success_flag) { OS_REG_WRITE(ah, AR_PHY_AGC_CONTROL, OS_REG_READ(ah, AR_PHY_AGC_CONTROL) | AR_PHY_AGC_CONTROL_CAL); if (!ath_hal_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0)) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: offset calibration failed to complete in 1ms; " "noisy environment?\n", __func__); return AH_FALSE; } if (apply_last_iqcorr == AH_TRUE) { ar9300_tx_iq_cal_post_proc(ah, ichan, 0, 0, is_cal_reusable, AH_TRUE); } } else { for (iqcal_idx=0;iqcal_idxah_caps.halMciSupport && IS_CHAN_2GHZ(ichan) && (ahp->ah_mci_bt_state == MCI_BT_AWAKE) && do_agc_cal && !(ah->ah_config.ath_hal_mci_config & ATH_MCI_CONFIG_DISABLE_MCI_CAL)) { u_int32_t payload[4] = {0, 0, 0, 0}; HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) %s: Send WLAN_CAL_DONE 0x%X\n", __func__, ahp->ah_mci_wlan_cal_done); MCI_GPM_SET_CAL_TYPE(payload, MCI_GPM_WLAN_CAL_DONE); payload[MCI_GPM_WLAN_CAL_W_SEQUENCE] = ahp->ah_mci_wlan_cal_done++; ar9300_mci_send_message(ah, MCI_GPM, 0, payload, 16, AH_TRUE, AH_FALSE); } #endif /* ATH_SUPPORT_MCI */ if (!cal_done && !AR_SREV_SCORPION(ah) ) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: offset calibration failed to complete in 1ms; " "noisy environment?\n", __func__); return AH_FALSE; } #if 0 /* Beacon stuck fix, refer to EV 120056 */ if(IS_CHAN_2GHZ(chan) && AR_SREV_SCORPION(ah)) OS_REG_WRITE(ah, AR_PHY_TIMING5, OS_REG_READ(ah,AR_PHY_TIMING5) & ~AR_PHY_TIMING5_CYCPWR_THR1_ENABLE); #endif #if 0 /* Do PA Calibration */ if (AR_SREV_KITE(ah) && AR_SREV_KITE_11_OR_LATER(ah)) { ar9285_pa_cal(ah); } #endif #if ATH_SUPPORT_CAL_REUSE if (ichan->one_time_txiqcal_done) { ar9300_tx_iq_cal_apply(ah, ichan); HALDEBUG(ah, HAL_DEBUG_FCS_RTT, "(FCS) TXIQCAL applied - %d\n", ichan->channel); } #endif /* ATH_SUPPORT_CAL_REUSE */ #if ATH_SUPPORT_CAL_REUSE if (cal_reuse_enable && ahp->tx_cl_cal_enable) { clc_success = (OS_REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_CLC_SUCCESS) ? 1 : 0; if (ichan->one_time_txclcal_done) { /* reapply CL cal results */ for (ch_idx = 0; ch_idx < AR9300_MAX_CHAINS; ch_idx++) { if ((ahp->ah_tx_cal_chainmask & (1 << ch_idx)) == 0) { continue; } cl_tab_reg = BB_cl_tab_b[ch_idx]; for (j = 0; j < BB_cl_tab_entry; j++) { OS_REG_WRITE(ah, cl_tab_reg, ichan->tx_clcal[ch_idx][j]); cl_tab_reg += 4;; } } HALDEBUG(ah, HAL_DEBUG_FCS_RTT, "(FCS) TX CL CAL applied - %d\n", ichan->channel); } else if (is_cal_reusable && clc_success) { /* save CL cal results */ for (ch_idx = 0; ch_idx < AR9300_MAX_CHAINS; ch_idx++) { if ((ahp->ah_tx_cal_chainmask & (1 << ch_idx)) == 0) { continue; } cl_tab_reg = BB_cl_tab_b[ch_idx]; for (j = 0; j < BB_cl_tab_entry; j++) { ichan->tx_clcal[ch_idx][j] = OS_REG_READ(ah, cl_tab_reg); cl_tab_reg += 4; } } ichan->one_time_txclcal_done = AH_TRUE; HALDEBUG(ah, HAL_DEBUG_FCS_RTT, "(FCS) TX CL CAL saved - %d\n", ichan->channel); } } #endif /* ATH_SUPPORT_CAL_REUSE */ /* Revert chainmasks to their original values before NF cal */ ar9300_init_chain_masks(ah, ahp->ah_rx_chainmask, ahp->ah_tx_chainmask); #if !FIX_NOISE_FLOOR /* * Do NF calibration after DC offset and other CALs. * Per system engineers, noise floor value can sometimes be 20 dB * higher than normal value if DC offset and noise floor cal are * triggered at the same time. */ OS_REG_WRITE(ah, AR_PHY_AGC_CONTROL, OS_REG_READ(ah, AR_PHY_AGC_CONTROL) | AR_PHY_AGC_CONTROL_NF); #endif /* Initialize list pointers */ ahp->ah_cal_list = ahp->ah_cal_list_last = ahp->ah_cal_list_curr = AH_NULL; /* * Enable IQ, ADC Gain, ADC DC Offset Cals */ /* Setup all non-periodic, init time only calibrations */ /* XXX: Init DC Offset not working yet */ #ifdef not_yet if (AH_TRUE == ar9300_is_cal_supp(ah, chan, ADC_DC_INIT_CAL)) { INIT_CAL(&ahp->ah_adc_dc_cal_init_data); INSERT_CAL(ahp, &ahp->ah_adc_dc_cal_init_data); } /* Initialize current pointer to first element in list */ ahp->ah_cal_list_curr = ahp->ah_cal_list; if (ahp->ah_cal_list_curr) { if (ar9300_run_init_cals(ah, 0) == AH_FALSE) { return AH_FALSE; } } #endif /* end - Init time calibrations */ + /* Do not do RX cal in case of offchan, or cal data already exists on same channel*/ + if (ahp->ah_skip_rx_iq_cal) { + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "Skip RX IQ Cal\n"); + return AH_TRUE; + } + /* If Cals are supported, add them to list via INIT/INSERT_CAL */ if (AH_TRUE == ar9300_is_cal_supp(ah, chan, IQ_MISMATCH_CAL)) { INIT_CAL(&ahp->ah_iq_cal_data); INSERT_CAL(ahp, &ahp->ah_iq_cal_data); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: enabling IQ Calibration.\n", __func__); } if (AH_TRUE == ar9300_is_cal_supp(ah, chan, TEMP_COMP_CAL)) { INIT_CAL(&ahp->ah_temp_comp_cal_data); INSERT_CAL(ahp, &ahp->ah_temp_comp_cal_data); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: enabling Temperature Compensation Calibration.\n", __func__); } /* Initialize current pointer to first element in list */ ahp->ah_cal_list_curr = ahp->ah_cal_list; /* Reset state within current cal */ if (ahp->ah_cal_list_curr) { ar9300_reset_calibration(ah, ahp->ah_cal_list_curr); } /* Mark all calibrations on this channel as being invalid */ ichan->calValid = 0; return AH_TRUE; } static inline HAL_BOOL ar9300_init_cal(struct ath_hal *ah, struct ieee80211_channel *chan, HAL_BOOL skip_if_none, HAL_BOOL apply_last_iqcorr) { HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); HAL_BOOL do_rtt_cal = AH_TRUE; HAL_BOOL enable_rtt = AH_FALSE; HALASSERT(ichan); return ar9300_init_cal_internal(ah, chan, ichan, enable_rtt, do_rtt_cal, skip_if_none, apply_last_iqcorr); } /* ar9300_reset_cal_valid * Entry point for upper layers to restart current cal. * Reset the calibration valid bit in channel. */ void ar9300_reset_cal_valid(struct ath_hal *ah, const struct ieee80211_channel *chan, HAL_BOOL *is_cal_done, u_int32_t cal_type) { struct ath_hal_9300 *ahp = AH9300(ah); HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); HAL_CAL_LIST *curr_cal = ahp->ah_cal_list_curr; *is_cal_done = AH_TRUE; if (curr_cal == AH_NULL) { return; } if (ichan == AH_NULL) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: invalid channel %u/0x%x; no mapping\n", __func__, chan->ic_freq, chan->ic_flags); return; } if (!(cal_type & IQ_MISMATCH_CAL)) { *is_cal_done = AH_FALSE; return; } /* Expected that this calibration has run before, post-reset. * Current state should be done */ if (curr_cal->cal_state != CAL_DONE) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Calibration state incorrect, %d\n", __func__, curr_cal->cal_state); return; } /* Verify Cal is supported on this channel */ if (ar9300_is_cal_supp(ah, chan, curr_cal->cal_data->cal_type) == AH_FALSE) { return; } HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Resetting Cal %d state for channel %u/0x%x\n", __func__, curr_cal->cal_data->cal_type, chan->ic_freq, chan->ic_flags); /* Disable cal validity in channel */ ichan->calValid &= ~curr_cal->cal_data->cal_type; curr_cal->cal_state = CAL_WAITING; /* Indicate to upper layers that we need polling */ *is_cal_done = AH_FALSE; } static inline void ar9300_set_dma(struct ath_hal *ah) { u_int32_t regval; struct ath_hal_9300 *ahp = AH9300(ah); + struct ath_hal_private *ahpriv = AH_PRIVATE(ah); + HAL_CAPABILITIES *pCap = &ahpriv->ah_caps; #if 0 /* * set AHB_MODE not to do cacheline prefetches */ regval = OS_REG_READ(ah, AR_AHB_MODE); OS_REG_WRITE(ah, AR_AHB_MODE, regval | AR_AHB_PREFETCH_RD_EN); #endif /* * let mac dma reads be in 128 byte chunks */ regval = OS_REG_READ(ah, AR_TXCFG) & ~AR_TXCFG_DMASZ_MASK; OS_REG_WRITE(ah, AR_TXCFG, regval | AR_TXCFG_DMASZ_128B); /* * Restore TX Trigger Level to its pre-reset value. * The initial value depends on whether aggregation is enabled, and is * adjusted whenever underruns are detected. */ /* OS_REG_RMW_FIELD(ah, AR_TXCFG, AR_FTRIG, AH_PRIVATE(ah)->ah_tx_trig_level); */ /* * Osprey 1.0 bug (EV 61936). Don't change trigger level from .ini default. * Osprey 2.0 - hardware recommends using the default INI settings. */ #if 0 OS_REG_RMW_FIELD(ah, AR_TXCFG, AR_FTRIG, 0x3f); #endif /* * let mac dma writes be in 128 byte chunks */ regval = OS_REG_READ(ah, AR_RXCFG) & ~AR_RXCFG_DMASZ_MASK; OS_REG_WRITE(ah, AR_RXCFG, regval | AR_RXCFG_DMASZ_128B); /* * Setup receive FIFO threshold to hold off TX activities */ OS_REG_WRITE(ah, AR_RXFIFO_CFG, 0x200); /* * reduce the number of usable entries in PCU TXBUF to avoid * wrap around bugs. (bug 20428) */ if (AR_SREV_WASP(ah) && (AH_PRIVATE((ah))->ah_macRev > AR_SREV_REVISION_WASP_12)) { /* Wasp 1.3 fix for EV#85395 requires usable entries * to be set to 0x500 */ OS_REG_WRITE(ah, AR_PCU_TXBUF_CTRL, 0x500); } else { OS_REG_WRITE(ah, AR_PCU_TXBUF_CTRL, AR_PCU_TXBUF_CTRL_USABLE_SIZE); } /* * Enable HPQ for UAPSD */ - if (AH_PRIVATE(ah)->ah_opmode == HAL_M_HOSTAP) { - OS_REG_WRITE(ah, AR_HP_Q_CONTROL, - AR_HPQ_ENABLE | AR_HPQ_UAPSD | AR_HPQ_UAPSD_TRIGGER_EN); + if (pCap->halHwUapsdTrig == AH_TRUE) { + /* Only enable this if HAL capabilities says it is OK */ + if (AH_PRIVATE(ah)->ah_opmode == HAL_M_HOSTAP) { + OS_REG_WRITE(ah, AR_HP_Q_CONTROL, + AR_HPQ_ENABLE | AR_HPQ_UAPSD | AR_HPQ_UAPSD_TRIGGER_EN); + } + } else { + /* use default value from ini file - which disable HPQ queue usage */ } /* * set the transmit status ring */ ar9300_reset_tx_status_ring(ah); /* * set rxbp threshold. Must be non-zero for RX_EOL to occur. * For Osprey 2.0+, keep the original thresholds * otherwise performance is lost due to excessive RX EOL interrupts. */ OS_REG_RMW_FIELD(ah, AR_RXBP_THRESH, AR_RXBP_THRESH_HP, 0x1); OS_REG_RMW_FIELD(ah, AR_RXBP_THRESH, AR_RXBP_THRESH_LP, 0x1); /* * set receive buffer size. */ if (ahp->rx_buf_size) { OS_REG_WRITE(ah, AR_DATABUF, ahp->rx_buf_size); } } static inline void ar9300_init_bb(struct ath_hal *ah, struct ieee80211_channel *chan) { u_int32_t synth_delay; /* * Wait for the frequency synth to settle (synth goes on * via AR_PHY_ACTIVE_EN). Read the phy active delay register. * Value is in 100ns increments. */ synth_delay = OS_REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY; if (IEEE80211_IS_CHAN_CCK(chan)) { synth_delay = (4 * synth_delay) / 22; } else { synth_delay /= 10; } /* Activate the PHY (includes baseband activate + synthesizer on) */ OS_REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); /* * There is an issue if the AP starts the calibration before * the base band timeout completes. This could result in the * rx_clear AH_FALSE triggering. As a workaround we add delay an * extra BASE_ACTIVATE_DELAY usecs to ensure this condition * does not happen. */ OS_DELAY(synth_delay + BASE_ACTIVATE_DELAY); } static inline void ar9300_init_interrupt_masks(struct ath_hal *ah, HAL_OPMODE opmode) { struct ath_hal_9300 *ahp = AH9300(ah); u_int32_t msi_cfg = 0; u_int32_t sync_en_def = AR9300_INTR_SYNC_DEFAULT; /* * Setup interrupt handling. Note that ar9300_reset_tx_queue * manipulates the secondary IMR's as queues are enabled * and disabled. This is done with RMW ops to insure the * settings we make here are preserved. */ ahp->ah_mask_reg = AR_IMR_TXERR | AR_IMR_TXURN | AR_IMR_RXERR | AR_IMR_RXORN | AR_IMR_BCNMISC; if (ahp->ah_intr_mitigation_rx) { /* enable interrupt mitigation for rx */ ahp->ah_mask_reg |= AR_IMR_RXINTM | AR_IMR_RXMINTR | AR_IMR_RXOK_HP; msi_cfg |= AR_INTCFG_MSI_RXINTM | AR_INTCFG_MSI_RXMINTR; } else { ahp->ah_mask_reg |= AR_IMR_RXOK_LP | AR_IMR_RXOK_HP; msi_cfg |= AR_INTCFG_MSI_RXOK; } if (ahp->ah_intr_mitigation_tx) { /* enable interrupt mitigation for tx */ ahp->ah_mask_reg |= AR_IMR_TXINTM | AR_IMR_TXMINTR; msi_cfg |= AR_INTCFG_MSI_TXINTM | AR_INTCFG_MSI_TXMINTR; } else { ahp->ah_mask_reg |= AR_IMR_TXOK; msi_cfg |= AR_INTCFG_MSI_TXOK; } if (opmode == HAL_M_HOSTAP) { ahp->ah_mask_reg |= AR_IMR_MIB; } OS_REG_WRITE(ah, AR_IMR, ahp->ah_mask_reg); OS_REG_WRITE(ah, AR_IMR_S2, OS_REG_READ(ah, AR_IMR_S2) | AR_IMR_S2_GTT); ahp->ah_mask2Reg = OS_REG_READ(ah, AR_IMR_S2); if (ah->ah_config.ath_hal_enable_msi) { /* Cache MSI register value */ ahp->ah_msi_reg = OS_REG_READ(ah, AR_HOSTIF_REG(ah, AR_PCIE_MSI)); ahp->ah_msi_reg |= AR_PCIE_MSI_HW_DBI_WR_EN; if (AR_SREV_POSEIDON(ah)) { ahp->ah_msi_reg &= AR_PCIE_MSI_HW_INT_PENDING_ADDR_MSI_64; } else { ahp->ah_msi_reg &= AR_PCIE_MSI_HW_INT_PENDING_ADDR; } /* Program MSI configuration */ OS_REG_WRITE(ah, AR_INTCFG, msi_cfg); } /* * debug - enable to see all synchronous interrupts status */ /* Clear any pending sync cause interrupts */ OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_SYNC_CAUSE), 0xFFFFFFFF); /* Allow host interface sync interrupt sources to set cause bit */ if (AR_SREV_POSEIDON(ah)) { sync_en_def = AR9300_INTR_SYNC_DEF_NO_HOST1_PERR; } else if (AR_SREV_WASP(ah)) { sync_en_def = AR9340_INTR_SYNC_DEFAULT; } OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_SYNC_ENABLE), sync_en_def); /* _Disable_ host interface sync interrupt when cause bits set */ OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_SYNC_MASK), 0); OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_PRIO_ASYNC_ENABLE), 0); OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_PRIO_ASYNC_MASK), 0); OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_PRIO_SYNC_ENABLE), 0); OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_INTR_PRIO_SYNC_MASK), 0); } static inline void ar9300_init_qos(struct ath_hal *ah) { OS_REG_WRITE(ah, AR_MIC_QOS_CONTROL, 0x100aa); /* XXX magic */ OS_REG_WRITE(ah, AR_MIC_QOS_SELECT, 0x3210); /* XXX magic */ /* Turn on NOACK Support for QoS packets */ OS_REG_WRITE(ah, AR_QOS_NO_ACK, SM(2, AR_QOS_NO_ACK_TWO_BIT) | SM(5, AR_QOS_NO_ACK_BIT_OFF) | SM(0, AR_QOS_NO_ACK_BYTE_OFF)); /* * initialize TXOP for all TIDs */ OS_REG_WRITE(ah, AR_TXOP_X, AR_TXOP_X_VAL); OS_REG_WRITE(ah, AR_TXOP_0_3, 0xFFFFFFFF); OS_REG_WRITE(ah, AR_TXOP_4_7, 0xFFFFFFFF); OS_REG_WRITE(ah, AR_TXOP_8_11, 0xFFFFFFFF); OS_REG_WRITE(ah, AR_TXOP_12_15, 0xFFFFFFFF); } static inline void ar9300_init_user_settings(struct ath_hal *ah) { struct ath_hal_9300 *ahp = AH9300(ah); /* Restore user-specified settings */ HALDEBUG(ah, HAL_DEBUG_RESET, "--AP %s ahp->ah_misc_mode 0x%x\n", __func__, ahp->ah_misc_mode); if (ahp->ah_misc_mode != 0) { OS_REG_WRITE(ah, AR_PCU_MISC, OS_REG_READ(ah, AR_PCU_MISC) | ahp->ah_misc_mode); } if (ahp->ah_get_plcp_hdr) { OS_REG_CLR_BIT(ah, AR_PCU_MISC, AR_PCU_SEL_EVM); } if (ahp->ah_slot_time != (u_int) -1) { ar9300_set_slot_time(ah, ahp->ah_slot_time); } if (ahp->ah_ack_timeout != (u_int) -1) { ar9300_set_ack_timeout(ah, ahp->ah_ack_timeout); } if (AH_PRIVATE(ah)->ah_diagreg != 0) { OS_REG_SET_BIT(ah, AR_DIAG_SW, AH_PRIVATE(ah)->ah_diagreg); } if (ahp->ah_beacon_rssi_threshold != 0) { ar9300_set_hw_beacon_rssi_threshold(ah, ahp->ah_beacon_rssi_threshold); } #ifdef ATH_SUPPORT_DFS if (ahp->ah_cac_quiet_enabled) { ar9300_cac_tx_quiet(ah, 1); } #endif /* ATH_SUPPORT_DFS */ } int ar9300_get_spur_info(struct ath_hal * ah, int *enable, int len, u_int16_t *freq) { // struct ath_hal_private *ap = AH_PRIVATE(ah); int i, j; for (i = 0; i < len; i++) { freq[i] = 0; } *enable = ah->ah_config.ath_hal_spur_mode; for (i = 0, j = 0; i < AR_EEPROM_MODAL_SPURS; i++) { if (AH9300(ah)->ath_hal_spur_chans[i][0] != AR_NO_SPUR) { freq[j++] = AH9300(ah)->ath_hal_spur_chans[i][0]; HALDEBUG(ah, HAL_DEBUG_ANI, "1. get spur %d\n", AH9300(ah)->ath_hal_spur_chans[i][0]); } if (AH9300(ah)->ath_hal_spur_chans[i][1] != AR_NO_SPUR) { freq[j++] = AH9300(ah)->ath_hal_spur_chans[i][1]; HALDEBUG(ah, HAL_DEBUG_ANI, "2. get spur %d\n", AH9300(ah)->ath_hal_spur_chans[i][1]); } } return 0; } #define ATH_HAL_2GHZ_FREQ_MIN 20000 #define ATH_HAL_2GHZ_FREQ_MAX 29999 #define ATH_HAL_5GHZ_FREQ_MIN 50000 #define ATH_HAL_5GHZ_FREQ_MAX 59999 #if 0 int ar9300_set_spur_info(struct ath_hal * ah, int enable, int len, u_int16_t *freq) { struct ath_hal_private *ap = AH_PRIVATE(ah); int i, j, k; ap->ah_config.ath_hal_spur_mode = enable; if (ap->ah_config.ath_hal_spur_mode == SPUR_ENABLE_IOCTL) { for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { AH9300(ah)->ath_hal_spur_chans[i][0] = AR_NO_SPUR; AH9300(ah)->ath_hal_spur_chans[i][1] = AR_NO_SPUR; } for (i = 0, j = 0, k = 0; i < len; i++) { if (freq[i] > ATH_HAL_2GHZ_FREQ_MIN && freq[i] < ATH_HAL_2GHZ_FREQ_MAX) { /* 2GHz Spur */ if (j < AR_EEPROM_MODAL_SPURS) { AH9300(ah)->ath_hal_spur_chans[j++][1] = freq[i]; HALDEBUG(ah, HAL_DEBUG_ANI, "1 set spur %d\n", freq[i]); } } else if (freq[i] > ATH_HAL_5GHZ_FREQ_MIN && freq[i] < ATH_HAL_5GHZ_FREQ_MAX) { /* 5Ghz Spur */ if (k < AR_EEPROM_MODAL_SPURS) { AH9300(ah)->ath_hal_spur_chans[k++][0] = freq[i]; HALDEBUG(ah, HAL_DEBUG_ANI, "2 set spur %d\n", freq[i]); } } } } return 0; } #endif #define ar9300_check_op_mode(_opmode) \ ((_opmode == HAL_M_STA) || (_opmode == HAL_M_IBSS) ||\ (_opmode == HAL_M_HOSTAP) || (_opmode == HAL_M_MONITOR)) #ifndef ATH_NF_PER_CHAN /* * To fixed first reset noise floor value not correct issue * For ART need it to fixed low rate sens too low issue */ static int First_NFCal(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *ichan, int is_scan, struct ieee80211_channel *chan) { HAL_NFCAL_HIST_FULL *nfh; int i, j, k; int16_t nfarray[HAL_NUM_NF_READINGS] = {0}; int is_2g = 0; int nf_hist_len; int stats = 0; int16_t nf_buf[HAL_NUM_NF_READINGS]; #define IS(_c, _f) (((_c)->channel_flags & _f) || 0) if ((!is_scan) && chan->ic_freq == AH_PRIVATE(ah)->ah_curchan->ic_freq) { nfh = &AH_PRIVATE(ah)->nf_cal_hist; } else { nfh = (HAL_NFCAL_HIST_FULL *) &ichan->nf_cal_hist; } ar9300_start_nf_cal(ah); for (j = 0; j < 10000; j++) { if ((OS_REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) == 0){ break; } OS_DELAY(10); } if (j < 10000) { is_2g = IEEE80211_IS_CHAN_2GHZ(chan); ar9300_upload_noise_floor(ah, is_2g, nfarray); if (is_scan) { /* * This channel's NF cal info is just a HAL_NFCAL_HIST_SMALL struct * rather than a HAL_NFCAL_HIST_FULL struct. * As long as we only use the first history element of nf_cal_buffer * (nf_cal_buffer[0][0:HAL_NUM_NF_READINGS-1]), we can use * HAL_NFCAL_HIST_SMALL and HAL_NFCAL_HIST_FULL interchangeably. */ nfh = (HAL_NFCAL_HIST_FULL *) &ichan->nf_cal_hist; nf_hist_len = HAL_NF_CAL_HIST_LEN_SMALL; } else { nfh = &AH_PRIVATE(ah)->nf_cal_hist; nf_hist_len = HAL_NF_CAL_HIST_LEN_FULL; } for (i = 0; i < HAL_NUM_NF_READINGS; i ++) { for (k = 0; k < HAL_NF_CAL_HIST_LEN_FULL; k++) { nfh->nf_cal_buffer[k][i] = nfarray[i]; } nfh->base.priv_nf[i] = ar9300_limit_nf_range(ah, ar9300_get_nf_hist_mid(ah, nfh, i, nf_hist_len)); } //ar9300StoreNewNf(ah, ichan, is_scan); /* * See if the NF value from the old channel should be * retained when switching to a new channel. * TBD: this may need to be changed, as it wipes out the * purpose of saving NF values for each channel. */ for (i = 0; i < HAL_NUM_NF_READINGS; i++) { if (IEEE80211_IS_CHAN_2GHZ(chan)) { if (nfh->nf_cal_buffer[0][i] < AR_PHY_CCA_MAX_GOOD_VAL_OSPREY_2GHZ) { ichan->nf_cal_hist.nf_cal_buffer[0][i] = AH_PRIVATE(ah)->nf_cal_hist.nf_cal_buffer[0][i]; } } else { if (AR_SREV_AR9580(ah)) { if (nfh->nf_cal_buffer[0][i] < AR_PHY_CCA_NOM_VAL_PEACOCK_5GHZ) { ichan->nf_cal_hist.nf_cal_buffer[0][i] = AH_PRIVATE(ah)->nf_cal_hist.nf_cal_buffer[0][i]; } } else { if (nfh->nf_cal_buffer[0][i] < AR_PHY_CCA_NOM_VAL_OSPREY_5GHZ) { ichan->nf_cal_hist.nf_cal_buffer[0][i] = AH_PRIVATE(ah)->nf_cal_hist.nf_cal_buffer[0][i]; } } } } /* * Copy the channel's NF buffer, which may have been modified * just above here, to the full NF history buffer. */ ar9300_reset_nf_hist_buff(ah, ichan); ar9300_get_nf_hist_base(ah, ichan, is_scan, nf_buf); ar9300_load_nf(ah, nf_buf); stats = 0; } else { stats = 1; } #undef IS return stats; } #endif /* * Places the device in and out of reset and then places sane * values in the registers based on EEPROM config, initialization * vectors (as determined by the mode), and station configuration * * b_channel_change is used to preserve DMA/PCU registers across * a HW Reset during channel change. */ HAL_BOOL ar9300_reset(struct ath_hal *ah, HAL_OPMODE opmode, struct ieee80211_channel *chan, HAL_HT_MACMODE macmode, u_int8_t txchainmask, u_int8_t rxchainmask, HAL_HT_EXTPROTSPACING extprotspacing, HAL_BOOL b_channel_change, HAL_STATUS *status, int is_scan) { #define FAIL(_code) do { ecode = _code; goto bad; } while (0) u_int32_t save_led_state; struct ath_hal_9300 *ahp = AH9300(ah); struct ath_hal_private *ap = AH_PRIVATE(ah); HAL_CHANNEL_INTERNAL *ichan; //const struct ieee80211_channel *curchan = ap->ah_curchan; #if ATH_SUPPORT_MCI HAL_BOOL save_full_sleep = ahp->ah_chip_full_sleep; #endif u_int32_t save_def_antenna; u_int32_t mac_sta_id1; HAL_STATUS ecode; int i, rx_chainmask; int nf_hist_buff_reset = 0; int16_t nf_buf[HAL_NUM_NF_READINGS]; #ifdef ATH_FORCE_PPM u_int32_t save_force_val, tmp_reg; #endif HAL_BOOL stopped, cal_ret; HAL_BOOL apply_last_iqcorr = AH_FALSE; + if (OS_REG_READ(ah, AR_IER) == AR_IER_ENABLE) { HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE, "** Reset called with WLAN " "interrupt enabled %08x **\n", ar9300_get_interrupts(ah)); } /* * Set the status to "ok" by default to cover the cases * where we return AH_FALSE without going to "bad" */ HALASSERT(status); *status = HAL_OK; if ((ah->ah_config.ath_hal_sta_update_tx_pwr_enable)) { AH9300(ah)->green_tx_status = HAL_RSSI_TX_POWER_NONE; } #if ATH_SUPPORT_MCI if (AH_PRIVATE(ah)->ah_caps.halMciSupport && (AR_SREV_JUPITER_20(ah) || AR_SREV_APHRODITE(ah))) { ar9300_mci_2g5g_changed(ah, IEEE80211_IS_CHAN_2GHZ(chan)); } #endif ahp->ah_ext_prot_spacing = extprotspacing; ahp->ah_tx_chainmask = txchainmask & ap->ah_caps.halTxChainMask; ahp->ah_rx_chainmask = rxchainmask & ap->ah_caps.halRxChainMask; ahp->ah_tx_cal_chainmask = ap->ah_caps.halTxChainMask; ahp->ah_rx_cal_chainmask = ap->ah_caps.halRxChainMask; + + /* + * Keep the previous optinal txchainmask value + */ + HALASSERT(ar9300_check_op_mode(opmode)); OS_MARK(ah, AH_MARK_RESET, b_channel_change); /* * Map public channel to private. */ ichan = ar9300_check_chan(ah, chan); if (ichan == AH_NULL) { HALDEBUG(ah, HAL_DEBUG_CHANNEL, "%s: invalid channel %u/0x%x; no mapping\n", __func__, chan->ic_freq, chan->ic_flags); FAIL(HAL_EINVAL); } ichan->paprd_table_write_done = 0; /* Clear PAPRD table write flag */ #if 0 chan->paprd_table_write_done = 0; /* Clear PAPRD table write flag */ #endif if (ar9300_get_power_mode(ah) != HAL_PM_FULL_SLEEP) { /* Need to stop RX DMA before reset otherwise chip might hang */ stopped = ar9300_set_rx_abort(ah, AH_TRUE); /* abort and disable PCU */ ar9300_set_rx_filter(ah, 0); stopped &= ar9300_stop_dma_receive(ah, 0); /* stop and disable RX DMA */ if (!stopped) { /* * During the transition from full sleep to reset, * recv DMA regs are not available to be read */ HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s[%d]: ar9300_stop_dma_receive failed\n", __func__, __LINE__); b_channel_change = AH_FALSE; } } else { HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s[%d]: Chip is already in full sleep\n", __func__, __LINE__); } #if ATH_SUPPORT_MCI if ((AH_PRIVATE(ah)->ah_caps.halMciSupport) && (ahp->ah_mci_bt_state == MCI_BT_CAL_START)) { u_int32_t payload[4] = {0, 0, 0, 0}; HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) %s: Stop rx for BT cal.\n", __func__); ahp->ah_mci_bt_state = MCI_BT_CAL; /* * MCIFIX: disable mci interrupt here. This is to avoid SW_MSG_DONE or * RX_MSG bits to trigger MCI_INT and lead to mci_intr reentry. */ ar9300_mci_disable_interrupt(ah); HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) %s: Send WLAN_CAL_GRANT\n", __func__); MCI_GPM_SET_CAL_TYPE(payload, MCI_GPM_WLAN_CAL_GRANT); ar9300_mci_send_message(ah, MCI_GPM, 0, payload, 16, AH_TRUE, AH_FALSE); /* Wait BT calibration to be completed for 25ms */ HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) %s: BT is calibrating.\n", __func__); if (ar9300_mci_wait_for_gpm(ah, MCI_GPM_BT_CAL_DONE, 0, 25000)) { HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) %s: Got BT_CAL_DONE.\n", __func__); } else { HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) %s: ### BT cal takes too long. Force bt_state to be bt_awake.\n", __func__); } ahp->ah_mci_bt_state = MCI_BT_AWAKE; /* MCIFIX: enable mci interrupt here */ ar9300_mci_enable_interrupt(ah); return AH_TRUE; } #endif /* Bring out of sleep mode */ if (!ar9300_set_power_mode(ah, HAL_PM_AWAKE, AH_TRUE)) { *status = HAL_INV_PMODE; return AH_FALSE; } /* Check the Rx mitigation config again, it might have changed * during attach in ath_vap_attach. */ if (ah->ah_config.ath_hal_intr_mitigation_rx != 0) { ahp->ah_intr_mitigation_rx = AH_TRUE; } else { ahp->ah_intr_mitigation_rx = AH_FALSE; } /* * XXX TODO FreeBSD: * * This is painful because we don't have a non-const channel pointer * at this stage. * * Make sure this gets fixed! */ #if 0 /* Get the value from the previous NF cal and update history buffer */ if (curchan && (ahp->ah_chip_full_sleep != AH_TRUE)) { - ar9300_store_new_nf(ah, curchan, is_scan); + + if(ahp->ah_chip_reset_done){ + ahp->ah_chip_reset_done = 0; + } else { + /* + * is_scan controls updating NF for home channel or off channel. + * Home -> Off, update home channel + * Off -> Home, update off channel + * Home -> Home, uppdate home channel + */ + if (ap->ah_curchan->channel != chan->channel) + ar9300_store_new_nf(ah, curchan, !is_scan); + else + ar9300_store_new_nf(ah, curchan, is_scan); + } } #endif /* * Account for the effect of being in either the 2 GHz or 5 GHz band * on the nominal, max allowable, and min allowable noise floor values. */ AH9300(ah)->nfp = IS_CHAN_2GHZ(ichan) ? &ahp->nf_2GHz : &ahp->nf_5GHz; /* - * XXX For now, don't apply the last IQ correction. + * XXX FreeBSD For now, don't apply the last IQ correction. * * This should be done when scorpion is enabled on FreeBSD; just be * sure to fix this channel match code so it uses net80211 flags * instead. */ #if 0 if (AR_SREV_SCORPION(ah) && curchan && (chan->channel == curchan->channel) && ((chan->channel_flags & (CHANNEL_ALL|CHANNEL_HALF|CHANNEL_QUARTER)) == (curchan->channel_flags & (CHANNEL_ALL | CHANNEL_HALF | CHANNEL_QUARTER)))) { apply_last_iqcorr = AH_TRUE; } #endif apply_last_iqcorr = AH_FALSE; #ifndef ATH_NF_PER_CHAN /* * If there's only one full-size home-channel NF history buffer * rather than a full-size NF history buffer per channel, decide * whether to (re)initialize the home-channel NF buffer. * If this is just a channel change for a scan, or if the channel * is not being changed, don't mess up the home channel NF history * buffer with NF values from this scanned channel. If we're * changing the home channel to a new channel, reset the home-channel * NF history buffer with the most accurate NF known for the new channel. */ if (!is_scan && (!ap->ah_curchan || ap->ah_curchan->ic_freq != chan->ic_freq)) // || // ap->ah_curchan->channel_flags != chan->channel_flags)) { nf_hist_buff_reset = 1; ar9300_reset_nf_hist_buff(ah, ichan); } #endif /* + * In case of + * - offchan scan, or + * - same channel and RX IQ Cal already available + * disable RX IQ Cal. + */ + if (is_scan) { + ahp->ah_skip_rx_iq_cal = AH_TRUE; + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "Skip RX IQ Cal due to scanning\n"); + } else { +#if 0 + /* XXX FreeBSD: always just do the RX IQ cal */ + /* XXX I think it's just going to speed things up; I don't think it's to avoid chan bugs */ + if (ahp->ah_rx_cal_complete && + ahp->ah_rx_cal_chan == ichan->channel && + ahp->ah_rx_cal_chan_flag == chan->channel_flags) { + ahp->ah_skip_rx_iq_cal = AH_TRUE; + HALDEBUG(ah, HAL_DEBUG_CALIBRATE, + "Skip RX IQ Cal due to same channel with completed RX IQ Cal\n"); + } else +#endif + ahp->ah_skip_rx_iq_cal = AH_FALSE; + } + + /* * Fast channel change (Change synthesizer based on channel freq * without resetting chip) * Don't do it when * - Flag is not set * - Chip is just coming out of full sleep * - Channel to be set is same as current channel * - Channel flags are different, like when moving from 2GHz to 5GHz * channels * - Merlin: Switching in/out of fast clock enabled channels * (not currently coded, since fast clock is enabled * across the 5GHz band * and we already do a full reset when switching in/out * of 5GHz channels) */ #if 0 if (b_channel_change && (ahp->ah_chip_full_sleep != AH_TRUE) && (AH_PRIVATE(ah)->ah_curchan != AH_NULL) && ((chan->channel != AH_PRIVATE(ah)->ah_curchan->channel) && (((CHANNEL_ALL|CHANNEL_HALF|CHANNEL_QUARTER) & chan->channel_flags) == ((CHANNEL_ALL|CHANNEL_HALF|CHANNEL_QUARTER) & AH_PRIVATE(ah)->ah_curchan->channel_flags)))) { if (ar9300_channel_change(ah, chan, ichan, macmode)) { chan->channel_flags = ichan->channel_flags; chan->priv_flags = ichan->priv_flags; AH_PRIVATE(ah)->ah_curchan->ah_channel_time = 0; AH_PRIVATE(ah)->ah_curchan->ah_tsf_last = ar9300_get_tsf64(ah); /* * Load the NF from history buffer of the current channel. * NF is slow time-variant, so it is OK to use a historical value. */ ar9300_get_nf_hist_base(ah, AH_PRIVATE(ah)->ah_curchan, is_scan, nf_buf); ar9300_load_nf(ah, nf_buf); /* start NF calibration, without updating BB NF register*/ ar9300_start_nf_cal(ah); /* * If channel_change completed and DMA was stopped * successfully - skip the rest of reset */ if (AH9300(ah)->ah_dma_stuck != AH_TRUE) { WAR_USB_DISABLE_PLL_LOCK_DETECT(ah); #if ATH_SUPPORT_MCI if (AH_PRIVATE(ah)->ah_caps.halMciSupport && ahp->ah_mci_ready) { ar9300_mci_2g5g_switch(ah, AH_TRUE); } #endif return HAL_OK; } } } #endif /* #if 0 */ #if ATH_SUPPORT_MCI if (AH_PRIVATE(ah)->ah_caps.halMciSupport) { ar9300_mci_disable_interrupt(ah); if (ahp->ah_mci_ready && !save_full_sleep) { ar9300_mci_mute_bt(ah); OS_DELAY(20); OS_REG_WRITE(ah, AR_BTCOEX_CTRL, 0); } ahp->ah_mci_bt_state = MCI_BT_SLEEP; ahp->ah_mci_ready = AH_FALSE; } #endif AH9300(ah)->ah_dma_stuck = AH_FALSE; #ifdef ATH_FORCE_PPM /* Preserve force ppm state */ save_force_val = OS_REG_READ(ah, AR_PHY_TIMING2) & (AR_PHY_TIMING2_USE_FORCE | AR_PHY_TIMING2_FORCE_VAL); #endif /* * Preserve the antenna on a channel change */ save_def_antenna = OS_REG_READ(ah, AR_DEF_ANTENNA); if (0 == ahp->ah_smartantenna_enable ) { if (save_def_antenna == 0) { save_def_antenna = 1; } } /* Save hardware flag before chip reset clears the register */ mac_sta_id1 = OS_REG_READ(ah, AR_STA_ID1) & AR_STA_ID1_BASE_RATE_11B; /* Save led state from pci config register */ save_led_state = OS_REG_READ(ah, AR_CFG_LED) & (AR_CFG_LED_ASSOC_CTL | AR_CFG_LED_MODE_SEL | AR_CFG_LED_BLINK_THRESH_SEL | AR_CFG_LED_BLINK_SLOW); /* Mark PHY inactive prior to reset, to be undone in ar9300_init_bb () */ ar9300_mark_phy_inactive(ah); if (!ar9300_chip_reset(ah, chan)) { HALDEBUG(ah, HAL_DEBUG_RESET, "%s: chip reset failed\n", __func__); FAIL(HAL_EIO); } OS_MARK(ah, AH_MARK_RESET_LINE, __LINE__); /* Disable JTAG */ OS_REG_SET_BIT(ah, AR_HOSTIF_REG(ah, AR_GPIO_INPUT_EN_VAL), AR_GPIO_JTAG_DISABLE); /* * Note that ar9300_init_chain_masks() is called from within * ar9300_process_ini() to ensure the swap bit is set before * the pdadc table is written. */ ecode = ar9300_process_ini(ah, chan, ichan, macmode); if (ecode != HAL_OK) { goto bad; } ahp->ah_immunity_on = AH_FALSE; if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah)) { ahp->tx_iq_cal_enable = OS_REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_0(ah), AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL) ? 1 : 0; } ahp->tx_cl_cal_enable = (OS_REG_READ(ah, AR_PHY_CL_CAL_CTL) & AR_PHY_CL_CAL_ENABLE) ? 1 : 0; /* For devices with full HW RIFS Rx support (Sowl/Howl/Merlin, etc), * restore register settings from prior to reset. */ if ((AH_PRIVATE(ah)->ah_curchan != AH_NULL) && (ar9300_get_capability(ah, HAL_CAP_LDPCWAR, 0, AH_NULL) == HAL_OK)) { /* Re-program RIFS Rx policy after reset */ ar9300_set_rifs_delay(ah, ahp->ah_rifs_enabled); } #if ATH_SUPPORT_MCI if (AH_PRIVATE(ah)->ah_caps.halMciSupport) { ar9300_mci_reset(ah, AH_FALSE, IS_CHAN_2GHZ(ichan), save_full_sleep); } #endif /* Initialize Management Frame Protection */ ar9300_init_mfp(ah); ahp->ah_immunity_vals[0] = OS_REG_READ_FIELD(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_M1_THRESH_LOW); ahp->ah_immunity_vals[1] = OS_REG_READ_FIELD(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_M2_THRESH_LOW); ahp->ah_immunity_vals[2] = OS_REG_READ_FIELD(ah, AR_PHY_SFCORR, AR_PHY_SFCORR_M1_THRESH); ahp->ah_immunity_vals[3] = OS_REG_READ_FIELD(ah, AR_PHY_SFCORR, AR_PHY_SFCORR_M2_THRESH); ahp->ah_immunity_vals[4] = OS_REG_READ_FIELD(ah, AR_PHY_SFCORR, AR_PHY_SFCORR_M2COUNT_THR); ahp->ah_immunity_vals[5] = OS_REG_READ_FIELD(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW); /* Write delta slope for OFDM enabled modes (A, G, Turbo) */ if (IEEE80211_IS_CHAN_OFDM(chan) || IEEE80211_IS_CHAN_HT(chan)) { ar9300_set_delta_slope(ah, chan); } ar9300_spur_mitigate(ah, chan); if (!ar9300_eeprom_set_board_values(ah, chan)) { HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: error setting board options\n", __func__); FAIL(HAL_EIO); } #ifdef ATH_HAL_WAR_REG16284_APH128 /* temp work around, will be removed. */ if (AR_SREV_WASP(ah)) { OS_REG_WRITE(ah, 0x16284, 0x1553e000); } #endif OS_MARK(ah, AH_MARK_RESET_LINE, __LINE__); OS_REG_WRITE(ah, AR_STA_ID0, LE_READ_4(ahp->ah_macaddr)); OS_REG_WRITE(ah, AR_STA_ID1, LE_READ_2(ahp->ah_macaddr + 4) | mac_sta_id1 | AR_STA_ID1_RTS_USE_DEF | (ah->ah_config.ath_hal_6mb_ack ? AR_STA_ID1_ACKCTS_6MB : 0) | ahp->ah_sta_id1_defaults ); ar9300_set_operating_mode(ah, opmode); /* Set Venice BSSID mask according to current state */ OS_REG_WRITE(ah, AR_BSSMSKL, LE_READ_4(ahp->ah_bssid_mask)); OS_REG_WRITE(ah, AR_BSSMSKU, LE_READ_2(ahp->ah_bssid_mask + 4)); /* Restore previous antenna */ OS_REG_WRITE(ah, AR_DEF_ANTENNA, save_def_antenna); #ifdef ATH_FORCE_PPM /* Restore force ppm state */ tmp_reg = OS_REG_READ(ah, AR_PHY_TIMING2) & ~(AR_PHY_TIMING2_USE_FORCE | AR_PHY_TIMING2_FORCE_VAL); OS_REG_WRITE(ah, AR_PHY_TIMING2, tmp_reg | save_force_val); #endif /* then our BSSID and assocID */ OS_REG_WRITE(ah, AR_BSS_ID0, LE_READ_4(ahp->ah_bssid)); OS_REG_WRITE(ah, AR_BSS_ID1, LE_READ_2(ahp->ah_bssid + 4) | ((ahp->ah_assoc_id & 0x3fff) << AR_BSS_ID1_AID_S)); OS_REG_WRITE(ah, AR_ISR, ~0); /* cleared on write */ OS_REG_RMW_FIELD(ah, AR_RSSI_THR, AR_RSSI_THR_BM_THR, INIT_RSSI_THR); /* HW beacon processing */ /* * XXX what happens if I just leave filter_interval=0? * it stays disabled? */ OS_REG_RMW_FIELD(ah, AR_RSSI_THR, AR_RSSI_BCN_WEIGHT, INIT_RSSI_BEACON_WEIGHT); OS_REG_SET_BIT(ah, AR_HWBCNPROC1, AR_HWBCNPROC1_CRC_ENABLE | AR_HWBCNPROC1_EXCLUDE_TIM_ELM); if (ah->ah_config.ath_hal_beacon_filter_interval) { OS_REG_RMW_FIELD(ah, AR_HWBCNPROC2, AR_HWBCNPROC2_FILTER_INTERVAL, ah->ah_config.ath_hal_beacon_filter_interval); OS_REG_SET_BIT(ah, AR_HWBCNPROC2, AR_HWBCNPROC2_FILTER_INTERVAL_ENABLE); } /* * Set Channel now modifies bank 6 parameters for FOWL workaround * to force rf_pwd_icsyndiv bias current as function of synth * frequency.Thus must be called after ar9300_process_ini() to ensure * analog register cache is valid. */ if (!ahp->ah_rf_hal.set_channel(ah, chan)) { FAIL(HAL_EIO); } OS_MARK(ah, AH_MARK_RESET_LINE, __LINE__); /* Set 1:1 QCU to DCU mapping for all queues */ for (i = 0; i < AR_NUM_DCU; i++) { OS_REG_WRITE(ah, AR_DQCUMASK(i), 1 << i); } ahp->ah_intr_txqs = 0; for (i = 0; i < AH_PRIVATE(ah)->ah_caps.halTotalQueues; i++) { ar9300_reset_tx_queue(ah, i); } ar9300_init_interrupt_masks(ah, opmode); /* Reset ier reference count to disabled */ // OS_ATOMIC_SET(&ahp->ah_ier_ref_count, 1); if (ath_hal_isrfkillenabled(ah)) { ar9300_enable_rf_kill(ah); } /* must be called AFTER ini is processed */ ar9300_ani_init_defaults(ah, macmode); ar9300_init_qos(ah); ar9300_init_user_settings(ah); AH_PRIVATE(ah)->ah_opmode = opmode; /* record operating mode */ OS_MARK(ah, AH_MARK_RESET_DONE, 0); /* * disable seq number generation in hw */ OS_REG_WRITE(ah, AR_STA_ID1, OS_REG_READ(ah, AR_STA_ID1) | AR_STA_ID1_PRESERVE_SEQNUM); ar9300_set_dma(ah); /* * program OBS bus to see MAC interrupts */ #if ATH_SUPPORT_MCI if (!AH_PRIVATE(ah)->ah_caps.halMciSupport) { OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_OBS), 8); } #else OS_REG_WRITE(ah, AR_HOSTIF_REG(ah, AR_OBS), 8); #endif /* enabling AR_GTTM_IGNORE_IDLE in GTTM register so that GTT timer will not increment if the channel idle indicates the air is busy or NAV is still counting down */ OS_REG_WRITE(ah, AR_GTTM, AR_GTTM_IGNORE_IDLE); /* * GTT debug mode setting */ /* OS_REG_WRITE(ah, 0x64, 0x00320000); OS_REG_WRITE(ah, 0x68, 7); OS_REG_WRITE(ah, 0x4080, 0xC); */ /* * Disable general interrupt mitigation by setting MIRT = 0x0 * Rx and tx interrupt mitigation are conditionally enabled below. */ OS_REG_WRITE(ah, AR_MIRT, 0); if (ahp->ah_intr_mitigation_rx) { /* * Enable Interrupt Mitigation for Rx. * If no build-specific limits for the rx interrupt mitigation * timer have been specified, use conservative defaults. */ #ifndef AH_RIMT_VAL_LAST #define AH_RIMT_LAST_MICROSEC 500 #endif #ifndef AH_RIMT_VAL_FIRST #define AH_RIMT_FIRST_MICROSEC 2000 #endif #ifndef HOST_OFFLOAD OS_REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_LAST, AH_RIMT_LAST_MICROSEC); OS_REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_FIRST, AH_RIMT_FIRST_MICROSEC); #else /* lower mitigation level to reduce latency for offload arch. */ OS_REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_LAST, (AH_RIMT_LAST_MICROSEC >> 2)); OS_REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_FIRST, (AH_RIMT_FIRST_MICROSEC >> 2)); #endif } if (ahp->ah_intr_mitigation_tx) { /* * Enable Interrupt Mitigation for Tx. * If no build-specific limits for the tx interrupt mitigation * timer have been specified, use the values preferred for * the carrier group's products. */ #ifndef AH_TIMT_LAST #define AH_TIMT_LAST_MICROSEC 300 #endif #ifndef AH_TIMT_FIRST #define AH_TIMT_FIRST_MICROSEC 750 #endif OS_REG_RMW_FIELD(ah, AR_TIMT, AR_TIMT_LAST, AH_TIMT_LAST_MICROSEC); OS_REG_RMW_FIELD(ah, AR_TIMT, AR_TIMT_FIRST, AH_TIMT_FIRST_MICROSEC); } rx_chainmask = ahp->ah_rx_chainmask; OS_REG_WRITE(ah, AR_PHY_RX_CHAINMASK, rx_chainmask); OS_REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, rx_chainmask); ar9300_init_bb(ah, chan); /* BB Step 7: Calibration */ + /* + * Only kick off calibration not on offchan. + * If coming back from offchan, restore prevous Cal results + * since chip reset will clear existings. + */ + if (!ahp->ah_skip_rx_iq_cal) { + int i; + /* clear existing RX cal data */ + for (i=0; iah_rx_cal_corr[i] = 0; + + ahp->ah_rx_cal_complete = AH_FALSE; +// ahp->ah_rx_cal_chan = chan->channel; +// ahp->ah_rx_cal_chan_flag = ichan->channel_flags; + ahp->ah_rx_cal_chan = 0; + ahp->ah_rx_cal_chan_flag = 0; /* XXX FreeBSD */ + } ar9300_invalidate_saved_cals(ah, ichan); cal_ret = ar9300_init_cal(ah, chan, AH_FALSE, apply_last_iqcorr); #if ATH_SUPPORT_MCI if (AH_PRIVATE(ah)->ah_caps.halMciSupport && ahp->ah_mci_ready) { if (IS_CHAN_2GHZ(ichan) && (ahp->ah_mci_bt_state == MCI_BT_SLEEP)) { if (ar9300_mci_check_int(ah, AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET) || ar9300_mci_check_int(ah, AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE)) { /* * BT is sleeping. Check if BT wakes up duing WLAN * calibration. If BT wakes up during WLAN calibration, need * to go through all message exchanges again and recal. */ HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) ### %s: BT wakes up during WLAN calibration.\n", __func__); OS_REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET | AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE); HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) send REMOTE_RESET\n"); ar9300_mci_remote_reset(ah, AH_TRUE); ar9300_mci_send_sys_waking(ah, AH_TRUE); OS_DELAY(1); if (IS_CHAN_2GHZ(ichan)) { ar9300_mci_send_lna_transfer(ah, AH_TRUE); } ahp->ah_mci_bt_state = MCI_BT_AWAKE; /* Redo calibration */ HALDEBUG(ah, HAL_DEBUG_BT_COEX, "(MCI) %s: Re-calibrate.\n", __func__); ar9300_invalidate_saved_cals(ah, ichan); cal_ret = ar9300_init_cal(ah, chan, AH_FALSE, apply_last_iqcorr); } } ar9300_mci_enable_interrupt(ah); } #endif if (!cal_ret) { HALDEBUG(ah, HAL_DEBUG_RESET, "%s: Init Cal Failed\n", __func__); FAIL(HAL_ESELFTEST); } ar9300_init_txbf(ah); #if 0 /* * WAR for owl 1.0 - restore chain mask for 2-chain cfgs after cal */ rx_chainmask = ahp->ah_rx_chainmask; if ((rx_chainmask == 0x5) || (rx_chainmask == 0x3)) { OS_REG_WRITE(ah, AR_PHY_RX_CHAINMASK, rx_chainmask); OS_REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, rx_chainmask); } #endif /* Restore previous led state */ OS_REG_WRITE(ah, AR_CFG_LED, save_led_state | AR_CFG_SCLK_32KHZ); #if ATH_BT_COEX if (ahp->ah_bt_coex_config_type != HAL_BT_COEX_CFG_NONE) { ar9300_init_bt_coex(ah); #if ATH_SUPPORT_MCI if (AH_PRIVATE(ah)->ah_caps.halMciSupport && ahp->ah_mci_ready) { /* Check BT state again to make sure it's not changed. */ ar9300_mci_sync_bt_state(ah); ar9300_mci_2g5g_switch(ah, AH_TRUE); if ((ahp->ah_mci_bt_state == MCI_BT_AWAKE) && (ahp->ah_mci_query_bt == AH_TRUE)) { ahp->ah_mci_need_flush_btinfo = AH_TRUE; } } #endif } #endif /* Start TSF2 for generic timer 8-15. */ ar9300_start_tsf2(ah); /* MIMO Power save setting */ if (ar9300_get_capability(ah, HAL_CAP_DYNAMIC_SMPS, 0, AH_NULL) == HAL_OK) { ar9300_set_sm_power_mode(ah, ahp->ah_sm_power_mode); } /* * For big endian systems turn on swapping for descriptors */ #if AH_BYTE_ORDER == AH_BIG_ENDIAN if (AR_SREV_HORNET(ah) || AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah)) { OS_REG_RMW(ah, AR_CFG, AR_CFG_SWTB | AR_CFG_SWRB, 0); } else { ar9300_init_cfg_reg(ah); } #endif if ( AR_SREV_OSPREY(ah) || AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah)) { OS_REG_RMW(ah, AR_CFG_LED, AR_CFG_LED_ASSOC_CTL, AR_CFG_LED_ASSOC_CTL); } #if !(defined(ART_BUILD)) && defined(ATH_SUPPORT_LED) #define REG_WRITE(_reg, _val) *((volatile u_int32_t *)(_reg)) = (_val); #define REG_READ(_reg) *((volatile u_int32_t *)(_reg)) #define ATH_GPIO_OUT_FUNCTION3 0xB8040038 #define ATH_GPIO_OE 0xB8040000 if ( AR_SREV_WASP(ah)) { if (IS_CHAN_2GHZ((AH_PRIVATE(ah)->ah_curchan))) { REG_WRITE(ATH_GPIO_OUT_FUNCTION3, ( REG_READ(ATH_GPIO_OUT_FUNCTION3) & (~(0xff << 8))) | (0x33 << 8) ); REG_WRITE(ATH_GPIO_OE, ( REG_READ(ATH_GPIO_OE) & (~(0x1 << 13) ))); } else { /* Disable 2G WLAN LED. During ath_open, reset function is called even before channel is set. So 2GHz is taken as default and it also blinks. Hence to avoid both from blinking, disable 2G led while in 5G mode */ REG_WRITE(ATH_GPIO_OE, ( REG_READ(ATH_GPIO_OE) | (1 << 13) )); REG_WRITE(ATH_GPIO_OUT_FUNCTION3, ( REG_READ(ATH_GPIO_OUT_FUNCTION3) & (~(0xff))) | (0x33) ); REG_WRITE(ATH_GPIO_OE, ( REG_READ(ATH_GPIO_OE) & (~(0x1 << 12) ))); } } else if (AR_SREV_SCORPION(ah)) { if (IS_CHAN_2GHZ((AH_PRIVATE(ah)->ah_curchan))) { REG_WRITE(ATH_GPIO_OUT_FUNCTION3, ( REG_READ(ATH_GPIO_OUT_FUNCTION3) & (~(0xff << 8))) | (0x2F << 8) ); REG_WRITE(ATH_GPIO_OE, (( REG_READ(ATH_GPIO_OE) & (~(0x1 << 13) )) | (0x1 << 12))); } else if (IS_CHAN_5GHZ((AH_PRIVATE(ah)->ah_curchan))) { REG_WRITE(ATH_GPIO_OUT_FUNCTION3, ( REG_READ(ATH_GPIO_OUT_FUNCTION3) & (~(0xff))) | (0x2F) ); REG_WRITE(ATH_GPIO_OE, (( REG_READ(ATH_GPIO_OE) & (~(0x1 << 12) )) | (0x1 << 13))); } } #undef REG_READ #undef REG_WRITE #endif /* XXX FreeBSD What's this? -adrian */ #if 0 chan->channel_flags = ichan->channel_flags; chan->priv_flags = ichan->priv_flags; #endif #if FIX_NOISE_FLOOR /* XXX FreeBSD is ichan appropariate? It was curchan.. */ ar9300_get_nf_hist_base(ah, ichan, is_scan, nf_buf); ar9300_load_nf(ah, nf_buf); if (nf_hist_buff_reset == 1) { nf_hist_buff_reset = 0; #ifndef ATH_NF_PER_CHAN if (First_NFCal(ah, ichan, is_scan, chan)){ + if (ahp->ah_skip_rx_iq_cal && !is_scan) { + /* restore RX Cal result if existing */ + ar9300_rx_iq_cal_restore(ah); + ahp->ah_skip_rx_iq_cal = AH_FALSE; + } } #endif /* ATH_NF_PER_CHAN */ } else{ ar9300_start_nf_cal(ah); } #endif #ifdef AH_SUPPORT_AR9300 /* BB Panic Watchdog */ if (ar9300_get_capability(ah, HAL_CAP_BB_PANIC_WATCHDOG, 0, AH_NULL) == HAL_OK) { ar9300_config_bb_panic_watchdog(ah); } #endif /* While receiving unsupported rate frame receive state machine * gets into a state 0xb and if phy_restart happens when rx * state machine is in 0xb state, BB would go hang, if we * see 0xb state after first bb panic, make sure that we * disable the phy_restart. * * There may be multiple panics, make sure that we always do * this if we see this panic at least once. This is required * because reset seems to be writing from INI file. */ if ((ar9300_get_capability(ah, HAL_CAP_PHYRESTART_CLR_WAR, 0, AH_NULL) == HAL_OK) && (((MS((AH9300(ah)->ah_bb_panic_last_status), AR_PHY_BB_WD_RX_OFDM_SM)) == 0xb) || AH9300(ah)->ah_phyrestart_disabled) ) { ar9300_disable_phy_restart(ah, 1); } ahp->ah_radar1 = MS(OS_REG_READ(ah, AR_PHY_RADAR_1), AR_PHY_RADAR_1_CF_BIN_THRESH); ahp->ah_dc_offset = MS(OS_REG_READ(ah, AR_PHY_TIMING2), AR_PHY_TIMING2_DC_OFFSET); ahp->ah_disable_cck = MS(OS_REG_READ(ah, AR_PHY_MODE), AR_PHY_MODE_DISABLE_CCK); if (AH9300(ah)->ah_enable_keysearch_always) { ar9300_enable_keysearch_always(ah, 1); } #if ATH_LOW_POWER_ENABLE #define REG_WRITE(_reg, _val) *((volatile u_int32_t *)(_reg)) = (_val) #define REG_READ(_reg) *((volatile u_int32_t *)(_reg)) if (AR_SREV_OSPREY(ah)) { REG_WRITE(0xb4000080, REG_READ(0xb4000080) | 3); OS_REG_WRITE(ah, AR_RTC_RESET, 1); OS_REG_SET_BIT(ah, AR_HOSTIF_REG(ah, AR_PCIE_PM_CTRL), AR_PCIE_PM_CTRL_ENA); OS_REG_SET_BIT(ah, AR_HOSTIF_REG(ah, AR_SPARE), 0xffffffff); } #undef REG_READ #undef REG_WRITE #endif /* ATH_LOW_POWER_ENABLE */ WAR_USB_DISABLE_PLL_LOCK_DETECT(ah); /* H/W Green TX */ ar9300_control_signals_for_green_tx_mode(ah); /* Smart Antenna, only for 5GHz on Scropion */ if (IEEE80211_IS_CHAN_2GHZ((AH_PRIVATE(ah)->ah_curchan)) && AR_SREV_SCORPION(ah)) { ahp->ah_smartantenna_enable = 0; } ar9300_set_smart_antenna(ah, ahp->ah_smartantenna_enable); + if (ahp->ah_skip_rx_iq_cal && !is_scan) { + /* restore RX Cal result if existing */ + ar9300_rx_iq_cal_restore(ah); + ahp->ah_skip_rx_iq_cal = AH_FALSE; + } + return AH_TRUE; bad: OS_MARK(ah, AH_MARK_RESET_DONE, ecode); *status = ecode; + + if (ahp->ah_skip_rx_iq_cal && !is_scan) { + /* restore RX Cal result if existing */ + ar9300_rx_iq_cal_restore(ah); + ahp->ah_skip_rx_iq_cal = AH_FALSE; + } return AH_FALSE; #undef FAIL } void ar9300_green_ap_ps_on_off( struct ath_hal *ah, u_int16_t on_off) { /* Set/reset the ps flag */ AH9300(ah)->green_ap_ps_on = !!on_off; } /* * This function returns 1, where it is possible to do * single-chain power save. */ u_int16_t ar9300_is_single_ant_power_save_possible(struct ath_hal *ah) { return AH_TRUE; } /* To avoid compilation warnings. Functions not used when EMULATION. */ /* * ar9300_find_mag_approx() */ static int32_t ar9300_find_mag_approx(struct ath_hal *ah, int32_t in_re, int32_t in_im) { int32_t abs_i = abs(in_re); int32_t abs_q = abs(in_im); int32_t max_abs, min_abs; if (abs_i > abs_q) { max_abs = abs_i; min_abs = abs_q; } else { max_abs = abs_q; min_abs = abs_i; } return (max_abs - (max_abs / 32) + (min_abs / 8) + (min_abs / 4)); } /* * ar9300_solve_iq_cal() * solve 4x4 linear equation used in loopback iq cal. */ static HAL_BOOL ar9300_solve_iq_cal( struct ath_hal *ah, int32_t sin_2phi_1, int32_t cos_2phi_1, int32_t sin_2phi_2, int32_t cos_2phi_2, int32_t mag_a0_d0, int32_t phs_a0_d0, int32_t mag_a1_d0, int32_t phs_a1_d0, int32_t solved_eq[]) { int32_t f1 = cos_2phi_1 - cos_2phi_2; int32_t f3 = sin_2phi_1 - sin_2phi_2; int32_t f2; int32_t mag_tx, phs_tx, mag_rx, phs_rx; const int32_t result_shift = 1 << 15; f2 = (((int64_t)f1 * (int64_t)f1) / result_shift) + (((int64_t)f3 * (int64_t)f3) / result_shift); if (0 == f2) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Divide by 0(%d).\n", __func__, __LINE__); return AH_FALSE; } /* magnitude mismatch, tx */ mag_tx = f1 * (mag_a0_d0 - mag_a1_d0) + f3 * (phs_a0_d0 - phs_a1_d0); /* phase mismatch, tx */ phs_tx = f3 * (-mag_a0_d0 + mag_a1_d0) + f1 * (phs_a0_d0 - phs_a1_d0); mag_tx = (mag_tx / f2); phs_tx = (phs_tx / f2); /* magnitude mismatch, rx */ mag_rx = mag_a0_d0 - (cos_2phi_1 * mag_tx + sin_2phi_1 * phs_tx) / result_shift; /* phase mismatch, rx */ phs_rx = phs_a0_d0 + (sin_2phi_1 * mag_tx - cos_2phi_1 * phs_tx) / result_shift; solved_eq[0] = mag_tx; solved_eq[1] = phs_tx; solved_eq[2] = mag_rx; solved_eq[3] = phs_rx; return AH_TRUE; } /* * ar9300_calc_iq_corr() */ static HAL_BOOL ar9300_calc_iq_corr(struct ath_hal *ah, int32_t chain_idx, const int32_t iq_res[], int32_t iqc_coeff[]) { int32_t i2_m_q2_a0_d0, i2_p_q2_a0_d0, iq_corr_a0_d0; int32_t i2_m_q2_a0_d1, i2_p_q2_a0_d1, iq_corr_a0_d1; int32_t i2_m_q2_a1_d0, i2_p_q2_a1_d0, iq_corr_a1_d0; int32_t i2_m_q2_a1_d1, i2_p_q2_a1_d1, iq_corr_a1_d1; int32_t mag_a0_d0, mag_a1_d0, mag_a0_d1, mag_a1_d1; int32_t phs_a0_d0, phs_a1_d0, phs_a0_d1, phs_a1_d1; int32_t sin_2phi_1, cos_2phi_1, sin_2phi_2, cos_2phi_2; int32_t mag_tx, phs_tx, mag_rx, phs_rx; int32_t solved_eq[4], mag_corr_tx, phs_corr_tx, mag_corr_rx, phs_corr_rx; int32_t q_q_coff, q_i_coff; const int32_t res_scale = 1 << 15; const int32_t delpt_shift = 1 << 8; int32_t mag1, mag2; i2_m_q2_a0_d0 = iq_res[0] & 0xfff; i2_p_q2_a0_d0 = (iq_res[0] >> 12) & 0xfff; iq_corr_a0_d0 = ((iq_res[0] >> 24) & 0xff) + ((iq_res[1] & 0xf) << 8); if (i2_m_q2_a0_d0 > 0x800) { i2_m_q2_a0_d0 = -((0xfff - i2_m_q2_a0_d0) + 1); } if (iq_corr_a0_d0 > 0x800) { iq_corr_a0_d0 = -((0xfff - iq_corr_a0_d0) + 1); } i2_m_q2_a0_d1 = (iq_res[1] >> 4) & 0xfff; i2_p_q2_a0_d1 = (iq_res[2] & 0xfff); iq_corr_a0_d1 = (iq_res[2] >> 12) & 0xfff; if (i2_m_q2_a0_d1 > 0x800) { i2_m_q2_a0_d1 = -((0xfff - i2_m_q2_a0_d1) + 1); } if (iq_corr_a0_d1 > 0x800) { iq_corr_a0_d1 = -((0xfff - iq_corr_a0_d1) + 1); } i2_m_q2_a1_d0 = ((iq_res[2] >> 24) & 0xff) + ((iq_res[3] & 0xf) << 8); i2_p_q2_a1_d0 = (iq_res[3] >> 4) & 0xfff; iq_corr_a1_d0 = iq_res[4] & 0xfff; if (i2_m_q2_a1_d0 > 0x800) { i2_m_q2_a1_d0 = -((0xfff - i2_m_q2_a1_d0) + 1); } if (iq_corr_a1_d0 > 0x800) { iq_corr_a1_d0 = -((0xfff - iq_corr_a1_d0) + 1); } i2_m_q2_a1_d1 = (iq_res[4] >> 12) & 0xfff; i2_p_q2_a1_d1 = ((iq_res[4] >> 24) & 0xff) + ((iq_res[5] & 0xf) << 8); iq_corr_a1_d1 = (iq_res[5] >> 4) & 0xfff; if (i2_m_q2_a1_d1 > 0x800) { i2_m_q2_a1_d1 = -((0xfff - i2_m_q2_a1_d1) + 1); } if (iq_corr_a1_d1 > 0x800) { iq_corr_a1_d1 = -((0xfff - iq_corr_a1_d1) + 1); } if ((i2_p_q2_a0_d0 == 0) || (i2_p_q2_a0_d1 == 0) || (i2_p_q2_a1_d0 == 0) || (i2_p_q2_a1_d1 == 0)) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Divide by 0(%d):\na0_d0=%d\na0_d1=%d\na2_d0=%d\na1_d1=%d\n", __func__, __LINE__, i2_p_q2_a0_d0, i2_p_q2_a0_d1, i2_p_q2_a1_d0, i2_p_q2_a1_d1); return AH_FALSE; } if ((i2_p_q2_a0_d0 <= 1024) || (i2_p_q2_a0_d0 > 2047) || (i2_p_q2_a1_d0 < 0) || (i2_p_q2_a1_d1 < 0) || (i2_p_q2_a0_d0 <= i2_m_q2_a0_d0) || (i2_p_q2_a0_d0 <= iq_corr_a0_d0) || (i2_p_q2_a0_d1 <= i2_m_q2_a0_d1) || (i2_p_q2_a0_d1 <= iq_corr_a0_d1) || (i2_p_q2_a1_d0 <= i2_m_q2_a1_d0) || (i2_p_q2_a1_d0 <= iq_corr_a1_d0) || (i2_p_q2_a1_d1 <= i2_m_q2_a1_d1) || (i2_p_q2_a1_d1 <= iq_corr_a1_d1)) { return AH_FALSE; } mag_a0_d0 = (i2_m_q2_a0_d0 * res_scale) / i2_p_q2_a0_d0; phs_a0_d0 = (iq_corr_a0_d0 * res_scale) / i2_p_q2_a0_d0; mag_a0_d1 = (i2_m_q2_a0_d1 * res_scale) / i2_p_q2_a0_d1; phs_a0_d1 = (iq_corr_a0_d1 * res_scale) / i2_p_q2_a0_d1; mag_a1_d0 = (i2_m_q2_a1_d0 * res_scale) / i2_p_q2_a1_d0; phs_a1_d0 = (iq_corr_a1_d0 * res_scale) / i2_p_q2_a1_d0; mag_a1_d1 = (i2_m_q2_a1_d1 * res_scale) / i2_p_q2_a1_d1; phs_a1_d1 = (iq_corr_a1_d1 * res_scale) / i2_p_q2_a1_d1; /* without analog phase shift */ sin_2phi_1 = (((mag_a0_d0 - mag_a0_d1) * delpt_shift) / DELPT); /* without analog phase shift */ cos_2phi_1 = (((phs_a0_d1 - phs_a0_d0) * delpt_shift) / DELPT); /* with analog phase shift */ sin_2phi_2 = (((mag_a1_d0 - mag_a1_d1) * delpt_shift) / DELPT); /* with analog phase shift */ cos_2phi_2 = (((phs_a1_d1 - phs_a1_d0) * delpt_shift) / DELPT); /* force sin^2 + cos^2 = 1; */ /* find magnitude by approximation */ mag1 = ar9300_find_mag_approx(ah, cos_2phi_1, sin_2phi_1); mag2 = ar9300_find_mag_approx(ah, cos_2phi_2, sin_2phi_2); if ((mag1 == 0) || (mag2 == 0)) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Divide by 0(%d): mag1=%d, mag2=%d\n", __func__, __LINE__, mag1, mag2); return AH_FALSE; } /* normalization sin and cos by mag */ sin_2phi_1 = (sin_2phi_1 * res_scale / mag1); cos_2phi_1 = (cos_2phi_1 * res_scale / mag1); sin_2phi_2 = (sin_2phi_2 * res_scale / mag2); cos_2phi_2 = (cos_2phi_2 * res_scale / mag2); /* calculate IQ mismatch */ if (AH_FALSE == ar9300_solve_iq_cal(ah, sin_2phi_1, cos_2phi_1, sin_2phi_2, cos_2phi_2, mag_a0_d0, phs_a0_d0, mag_a1_d0, phs_a1_d0, solved_eq)) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Call to ar9300_solve_iq_cal failed.\n", __func__); return AH_FALSE; } mag_tx = solved_eq[0]; phs_tx = solved_eq[1]; mag_rx = solved_eq[2]; phs_rx = solved_eq[3]; HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: chain %d: mag mismatch=%d phase mismatch=%d\n", __func__, chain_idx, mag_tx / res_scale, phs_tx / res_scale); if (res_scale == mag_tx) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Divide by 0(%d): mag_tx=%d, res_scale=%d\n", __func__, __LINE__, mag_tx, res_scale); return AH_FALSE; } /* calculate and quantize Tx IQ correction factor */ mag_corr_tx = (mag_tx * res_scale) / (res_scale - mag_tx); phs_corr_tx = -phs_tx; q_q_coff = (mag_corr_tx * 128 / res_scale); q_i_coff = (phs_corr_tx * 256 / res_scale); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: tx chain %d: mag corr=%d phase corr=%d\n", __func__, chain_idx, q_q_coff, q_i_coff); if (q_i_coff < -63) { q_i_coff = -63; } if (q_i_coff > 63) { q_i_coff = 63; } if (q_q_coff < -63) { q_q_coff = -63; } if (q_q_coff > 63) { q_q_coff = 63; } iqc_coeff[0] = (q_q_coff * 128) + (0x7f & q_i_coff); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: tx chain %d: iq corr coeff=%x\n", __func__, chain_idx, iqc_coeff[0]); if (-mag_rx == res_scale) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Divide by 0(%d): mag_rx=%d, res_scale=%d\n", __func__, __LINE__, mag_rx, res_scale); return AH_FALSE; } /* calculate and quantize Rx IQ correction factors */ mag_corr_rx = (-mag_rx * res_scale) / (res_scale + mag_rx); phs_corr_rx = -phs_rx; q_q_coff = (mag_corr_rx * 128 / res_scale); q_i_coff = (phs_corr_rx * 256 / res_scale); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: rx chain %d: mag corr=%d phase corr=%d\n", __func__, chain_idx, q_q_coff, q_i_coff); if (q_i_coff < -63) { q_i_coff = -63; } if (q_i_coff > 63) { q_i_coff = 63; } if (q_q_coff < -63) { q_q_coff = -63; } if (q_q_coff > 63) { q_q_coff = 63; } iqc_coeff[1] = (q_q_coff * 128) + (0x7f & q_i_coff); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: rx chain %d: iq corr coeff=%x\n", __func__, chain_idx, iqc_coeff[1]); return AH_TRUE; } #define MAX_MAG_DELTA 11 //maximum magnitude mismatch delta across gains #define MAX_PHS_DELTA 10 //maximum phase mismatch delta across gains #define ABS(x) ((x) >= 0 ? (x) : (-(x))) u_int32_t tx_corr_coeff[MAX_MEASUREMENT][AR9300_MAX_CHAINS] = { { AR_PHY_TX_IQCAL_CORR_COEFF_01_B0, AR_PHY_TX_IQCAL_CORR_COEFF_01_B1, AR_PHY_TX_IQCAL_CORR_COEFF_01_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_01_B0, AR_PHY_TX_IQCAL_CORR_COEFF_01_B1, AR_PHY_TX_IQCAL_CORR_COEFF_01_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_23_B0, AR_PHY_TX_IQCAL_CORR_COEFF_23_B1, AR_PHY_TX_IQCAL_CORR_COEFF_23_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_23_B0, AR_PHY_TX_IQCAL_CORR_COEFF_23_B1, AR_PHY_TX_IQCAL_CORR_COEFF_23_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_45_B0, AR_PHY_TX_IQCAL_CORR_COEFF_45_B1, AR_PHY_TX_IQCAL_CORR_COEFF_45_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_45_B0, AR_PHY_TX_IQCAL_CORR_COEFF_45_B1, AR_PHY_TX_IQCAL_CORR_COEFF_45_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_67_B0, AR_PHY_TX_IQCAL_CORR_COEFF_67_B1, AR_PHY_TX_IQCAL_CORR_COEFF_67_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_67_B0, AR_PHY_TX_IQCAL_CORR_COEFF_67_B1, AR_PHY_TX_IQCAL_CORR_COEFF_67_B2}, }; static void ar9300_tx_iq_cal_outlier_detection(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *ichan, u_int32_t num_chains, struct coeff_t *coeff, HAL_BOOL is_cal_reusable) { int nmeasurement, ch_idx, im; int32_t magnitude, phase; int32_t magnitude_max, phase_max; int32_t magnitude_min, phase_min; int32_t magnitude_max_idx, phase_max_idx; int32_t magnitude_min_idx, phase_min_idx; int32_t magnitude_avg, phase_avg; int32_t outlier_mag_idx = 0; int32_t outlier_phs_idx = 0; if (AR_SREV_POSEIDON(ah)) { HALASSERT(num_chains == 0x1); tx_corr_coeff[0][0] = AR_PHY_TX_IQCAL_CORR_COEFF_01_B0_POSEIDON; tx_corr_coeff[1][0] = AR_PHY_TX_IQCAL_CORR_COEFF_01_B0_POSEIDON; tx_corr_coeff[2][0] = AR_PHY_TX_IQCAL_CORR_COEFF_23_B0_POSEIDON; tx_corr_coeff[3][0] = AR_PHY_TX_IQCAL_CORR_COEFF_23_B0_POSEIDON; tx_corr_coeff[4][0] = AR_PHY_TX_IQCAL_CORR_COEFF_45_B0_POSEIDON; tx_corr_coeff[5][0] = AR_PHY_TX_IQCAL_CORR_COEFF_45_B0_POSEIDON; tx_corr_coeff[6][0] = AR_PHY_TX_IQCAL_CORR_COEFF_67_B0_POSEIDON; tx_corr_coeff[7][0] = AR_PHY_TX_IQCAL_CORR_COEFF_67_B0_POSEIDON; } for (ch_idx = 0; ch_idx < num_chains; ch_idx++) { nmeasurement = OS_REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_STATUS_B0(ah), AR_PHY_CALIBRATED_GAINS_0); if (nmeasurement > MAX_MEASUREMENT) { nmeasurement = MAX_MEASUREMENT; } if (!AR_SREV_SCORPION(ah)) { /* * reset max/min variable to min/max values so that * we always start with 1st calibrated gain value */ magnitude_max = -64; phase_max = -64; magnitude_min = 63; phase_min = 63; magnitude_avg = 0; phase_avg = 0; magnitude_max_idx = 0; magnitude_min_idx = 0; phase_max_idx = 0; phase_min_idx = 0; /* detect outlier only if nmeasurement > 1 */ if (nmeasurement > 1) { /* printf("----------- start outlier detection -----------\n"); */ /* * find max/min and phase/mag mismatch across all calibrated gains */ for (im = 0; im < nmeasurement; im++) { magnitude = coeff->mag_coeff[ch_idx][im][0]; phase = coeff->phs_coeff[ch_idx][im][0]; magnitude_avg = magnitude_avg + magnitude; phase_avg = phase_avg + phase; if (magnitude > magnitude_max) { magnitude_max = magnitude; magnitude_max_idx = im; } if (magnitude < magnitude_min) { magnitude_min = magnitude; magnitude_min_idx = im; } if (phase > phase_max) { phase_max = phase; phase_max_idx = im; } if (phase < phase_min) { phase_min = phase; phase_min_idx = im; } } /* find average (exclude max abs value) */ for (im = 0; im < nmeasurement; im++) { magnitude = coeff->mag_coeff[ch_idx][im][0]; phase = coeff->phs_coeff[ch_idx][im][0]; if ((ABS(magnitude) < ABS(magnitude_max)) || (ABS(magnitude) < ABS(magnitude_min))) { magnitude_avg = magnitude_avg + magnitude; } if ((ABS(phase) < ABS(phase_max)) || (ABS(phase) < ABS(phase_min))) { phase_avg = phase_avg + phase; } } magnitude_avg = magnitude_avg / (nmeasurement - 1); phase_avg = phase_avg / (nmeasurement - 1); /* detect magnitude outlier */ if (ABS(magnitude_max - magnitude_min) > MAX_MAG_DELTA) { if (ABS(magnitude_max - magnitude_avg) > ABS(magnitude_min - magnitude_avg)) { /* max is outlier, force to avg */ outlier_mag_idx = magnitude_max_idx; } else { /* min is outlier, force to avg */ outlier_mag_idx = magnitude_min_idx; } coeff->mag_coeff[ch_idx][outlier_mag_idx][0] = magnitude_avg; coeff->phs_coeff[ch_idx][outlier_mag_idx][0] = phase_avg; HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "[ch%d][outlier mag gain%d]:: " "mag_avg = %d (/128), phase_avg = %d (/256)\n", ch_idx, outlier_mag_idx, magnitude_avg, phase_avg); } /* detect phase outlier */ if (ABS(phase_max - phase_min) > MAX_PHS_DELTA) { if (ABS(phase_max-phase_avg) > ABS(phase_min - phase_avg)) { /* max is outlier, force to avg */ outlier_phs_idx = phase_max_idx; } else{ /* min is outlier, force to avg */ outlier_phs_idx = phase_min_idx; } coeff->mag_coeff[ch_idx][outlier_phs_idx][0] = magnitude_avg; coeff->phs_coeff[ch_idx][outlier_phs_idx][0] = phase_avg; HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "[ch%d][outlier phs gain%d]:: " "mag_avg = %d (/128), phase_avg = %d (/256)\n", ch_idx, outlier_phs_idx, magnitude_avg, phase_avg); } } } /*printf("------------ after outlier detection -------------\n");*/ for (im = 0; im < nmeasurement; im++) { magnitude = coeff->mag_coeff[ch_idx][im][0]; phase = coeff->phs_coeff[ch_idx][im][0]; #if 0 printf("[ch%d][gain%d]:: mag = %d (/128), phase = %d (/256)\n", ch_idx, im, magnitude, phase); #endif coeff->iqc_coeff[0] = (phase & 0x7f) | ((magnitude & 0x7f) << 7); if ((im % 2) == 0) { OS_REG_RMW_FIELD(ah, tx_corr_coeff[im][ch_idx], AR_PHY_TX_IQCAL_CORR_COEFF_00_COEFF_TABLE, coeff->iqc_coeff[0]); } else { OS_REG_RMW_FIELD(ah, tx_corr_coeff[im][ch_idx], AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE, coeff->iqc_coeff[0]); } #if ATH_SUPPORT_CAL_REUSE ichan->tx_corr_coeff[im][ch_idx] = coeff->iqc_coeff[0]; #endif } #if ATH_SUPPORT_CAL_REUSE ichan->num_measures[ch_idx] = nmeasurement; #endif } OS_REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_3, AR_PHY_TX_IQCAL_CONTROL_3_IQCORR_EN, 0x1); OS_REG_RMW_FIELD(ah, AR_PHY_RX_IQCAL_CORR_B0, AR_PHY_RX_IQCAL_CORR_B0_LOOPBACK_IQCORR_EN, 0x1); #if ATH_SUPPORT_CAL_REUSE if (is_cal_reusable) { ichan->one_time_txiqcal_done = AH_TRUE; HALDEBUG(ah, HAL_DEBUG_FCS_RTT, "(FCS) TXIQCAL saved - %d\n", ichan->channel); } #endif } #if ATH_SUPPORT_CAL_REUSE static void ar9300_tx_iq_cal_apply(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *ichan) { struct ath_hal_9300 *ahp = AH9300(ah); int nmeasurement, ch_idx, im; u_int32_t tx_corr_coeff[MAX_MEASUREMENT][AR9300_MAX_CHAINS] = { { AR_PHY_TX_IQCAL_CORR_COEFF_01_B0, AR_PHY_TX_IQCAL_CORR_COEFF_01_B1, AR_PHY_TX_IQCAL_CORR_COEFF_01_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_01_B0, AR_PHY_TX_IQCAL_CORR_COEFF_01_B1, AR_PHY_TX_IQCAL_CORR_COEFF_01_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_23_B0, AR_PHY_TX_IQCAL_CORR_COEFF_23_B1, AR_PHY_TX_IQCAL_CORR_COEFF_23_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_23_B0, AR_PHY_TX_IQCAL_CORR_COEFF_23_B1, AR_PHY_TX_IQCAL_CORR_COEFF_23_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_45_B0, AR_PHY_TX_IQCAL_CORR_COEFF_45_B1, AR_PHY_TX_IQCAL_CORR_COEFF_45_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_45_B0, AR_PHY_TX_IQCAL_CORR_COEFF_45_B1, AR_PHY_TX_IQCAL_CORR_COEFF_45_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_67_B0, AR_PHY_TX_IQCAL_CORR_COEFF_67_B1, AR_PHY_TX_IQCAL_CORR_COEFF_67_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_67_B0, AR_PHY_TX_IQCAL_CORR_COEFF_67_B1, AR_PHY_TX_IQCAL_CORR_COEFF_67_B2}, }; if (AR_SREV_POSEIDON(ah)) { HALASSERT(ahp->ah_tx_cal_chainmask == 0x1); tx_corr_coeff[0][0] = AR_PHY_TX_IQCAL_CORR_COEFF_01_B0_POSEIDON; tx_corr_coeff[1][0] = AR_PHY_TX_IQCAL_CORR_COEFF_01_B0_POSEIDON; tx_corr_coeff[2][0] = AR_PHY_TX_IQCAL_CORR_COEFF_23_B0_POSEIDON; tx_corr_coeff[3][0] = AR_PHY_TX_IQCAL_CORR_COEFF_23_B0_POSEIDON; tx_corr_coeff[4][0] = AR_PHY_TX_IQCAL_CORR_COEFF_45_B0_POSEIDON; tx_corr_coeff[5][0] = AR_PHY_TX_IQCAL_CORR_COEFF_45_B0_POSEIDON; tx_corr_coeff[6][0] = AR_PHY_TX_IQCAL_CORR_COEFF_67_B0_POSEIDON; tx_corr_coeff[7][0] = AR_PHY_TX_IQCAL_CORR_COEFF_67_B0_POSEIDON; } for (ch_idx = 0; ch_idx < AR9300_MAX_CHAINS; ch_idx++) { if ((ahp->ah_tx_cal_chainmask & (1 << ch_idx)) == 0) { continue; } nmeasurement = ichan->num_measures[ch_idx]; for (im = 0; im < nmeasurement; im++) { if ((im % 2) == 0) { OS_REG_RMW_FIELD(ah, tx_corr_coeff[im][ch_idx], AR_PHY_TX_IQCAL_CORR_COEFF_00_COEFF_TABLE, ichan->tx_corr_coeff[im][ch_idx]); } else { OS_REG_RMW_FIELD(ah, tx_corr_coeff[im][ch_idx], AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE, ichan->tx_corr_coeff[im][ch_idx]); } } } OS_REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_3, AR_PHY_TX_IQCAL_CONTROL_3_IQCORR_EN, 0x1); OS_REG_RMW_FIELD(ah, AR_PHY_RX_IQCAL_CORR_B0, AR_PHY_RX_IQCAL_CORR_B0_LOOPBACK_IQCORR_EN, 0x1); } #endif /* * ar9300_tx_iq_cal_hw_run is only needed for osprey/wasp/hornet * It is not needed for jupiter/poseidon. */ HAL_BOOL ar9300_tx_iq_cal_hw_run(struct ath_hal *ah) { int is_tx_gain_forced; is_tx_gain_forced = OS_REG_READ_FIELD(ah, AR_PHY_TX_FORCED_GAIN, AR_PHY_TXGAIN_FORCE); if (is_tx_gain_forced) { /*printf("Tx gain can not be forced during tx I/Q cal!\n");*/ OS_REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, AR_PHY_TXGAIN_FORCE, 0); } /* enable tx IQ cal */ OS_REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_START(ah), AR_PHY_TX_IQCAL_START_DO_CAL, AR_PHY_TX_IQCAL_START_DO_CAL); if (!ath_hal_wait(ah, AR_PHY_TX_IQCAL_START(ah), AR_PHY_TX_IQCAL_START_DO_CAL, 0)) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Tx IQ Cal is never completed.\n", __func__); return AH_FALSE; } return AH_TRUE; } static void ar9300_tx_iq_cal_post_proc(struct ath_hal *ah,HAL_CHANNEL_INTERNAL *ichan, int iqcal_idx, int max_iqcal,HAL_BOOL is_cal_reusable, HAL_BOOL apply_last_corr) { int nmeasurement=0, im, ix, iy, temp; struct ath_hal_9300 *ahp = AH9300(ah); u_int32_t txiqcal_status[AR9300_MAX_CHAINS] = { AR_PHY_TX_IQCAL_STATUS_B0(ah), AR_PHY_TX_IQCAL_STATUS_B1, AR_PHY_TX_IQCAL_STATUS_B2, }; const u_int32_t chan_info_tab[] = { AR_PHY_CHAN_INFO_TAB_0, AR_PHY_CHAN_INFO_TAB_1, AR_PHY_CHAN_INFO_TAB_2, }; int32_t iq_res[6]; int32_t ch_idx, j; u_int32_t num_chains = 0; static struct coeff_t coeff; txiqcal_status[0] = AR_PHY_TX_IQCAL_STATUS_B0(ah); for (ch_idx = 0; ch_idx < AR9300_MAX_CHAINS; ch_idx++) { if (ahp->ah_tx_chainmask & (1 << ch_idx)) { num_chains++; } } if (apply_last_corr) { if (coeff.last_cal == AH_TRUE) { int32_t magnitude, phase; int ch_idx, im; u_int32_t tx_corr_coeff[MAX_MEASUREMENT][AR9300_MAX_CHAINS] = { { AR_PHY_TX_IQCAL_CORR_COEFF_01_B0, AR_PHY_TX_IQCAL_CORR_COEFF_01_B1, AR_PHY_TX_IQCAL_CORR_COEFF_01_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_01_B0, AR_PHY_TX_IQCAL_CORR_COEFF_01_B1, AR_PHY_TX_IQCAL_CORR_COEFF_01_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_23_B0, AR_PHY_TX_IQCAL_CORR_COEFF_23_B1, AR_PHY_TX_IQCAL_CORR_COEFF_23_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_23_B0, AR_PHY_TX_IQCAL_CORR_COEFF_23_B1, AR_PHY_TX_IQCAL_CORR_COEFF_23_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_45_B0, AR_PHY_TX_IQCAL_CORR_COEFF_45_B1, AR_PHY_TX_IQCAL_CORR_COEFF_45_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_45_B0, AR_PHY_TX_IQCAL_CORR_COEFF_45_B1, AR_PHY_TX_IQCAL_CORR_COEFF_45_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_67_B0, AR_PHY_TX_IQCAL_CORR_COEFF_67_B1, AR_PHY_TX_IQCAL_CORR_COEFF_67_B2}, { AR_PHY_TX_IQCAL_CORR_COEFF_67_B0, AR_PHY_TX_IQCAL_CORR_COEFF_67_B1, AR_PHY_TX_IQCAL_CORR_COEFF_67_B2}, }; for (ch_idx = 0; ch_idx < num_chains; ch_idx++) { for (im = 0; im < coeff.last_nmeasurement; im++) { magnitude = coeff.mag_coeff[ch_idx][im][0]; phase = coeff.phs_coeff[ch_idx][im][0]; #if 0 printf("[ch%d][gain%d]:: mag = %d (/128), phase = %d (/256)\n", ch_idx, im, magnitude, phase); #endif coeff.iqc_coeff[0] = (phase & 0x7f) | ((magnitude & 0x7f) << 7); if ((im % 2) == 0) { OS_REG_RMW_FIELD(ah, tx_corr_coeff[im][ch_idx], AR_PHY_TX_IQCAL_CORR_COEFF_00_COEFF_TABLE, coeff.iqc_coeff[0]); } else { OS_REG_RMW_FIELD(ah, tx_corr_coeff[im][ch_idx], AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE, coeff.iqc_coeff[0]); } } } OS_REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_3, AR_PHY_TX_IQCAL_CONTROL_3_IQCORR_EN, 0x1); } return; } for (ch_idx = 0; ch_idx < num_chains; ch_idx++) { nmeasurement = OS_REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_STATUS_B0(ah), AR_PHY_CALIBRATED_GAINS_0); if (nmeasurement > MAX_MEASUREMENT) { nmeasurement = MAX_MEASUREMENT; } for (im = 0; im < nmeasurement; im++) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Doing Tx IQ Cal for chain %d.\n", __func__, ch_idx); if (OS_REG_READ(ah, txiqcal_status[ch_idx]) & AR_PHY_TX_IQCAL_STATUS_FAILED) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Tx IQ Cal failed for chain %d.\n", __func__, ch_idx); goto TX_IQ_CAL_FAILED_; } for (j = 0; j < 3; j++) { u_int32_t idx = 2 * j; /* 3 registers for each calibration result */ u_int32_t offset = 4 * (3 * im + j); OS_REG_RMW_FIELD(ah, AR_PHY_CHAN_INFO_MEMORY, AR_PHY_CHAN_INFO_TAB_S2_READ, 0); /* 32 bits */ iq_res[idx] = OS_REG_READ(ah, chan_info_tab[ch_idx] + offset); OS_REG_RMW_FIELD(ah, AR_PHY_CHAN_INFO_MEMORY, AR_PHY_CHAN_INFO_TAB_S2_READ, 1); /* 16 bits */ iq_res[idx + 1] = 0xffff & OS_REG_READ(ah, chan_info_tab[ch_idx] + offset); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: IQ RES[%d]=0x%x IQ_RES[%d]=0x%x\n", __func__, idx, iq_res[idx], idx + 1, iq_res[idx + 1]); } if (AH_FALSE == ar9300_calc_iq_corr( ah, ch_idx, iq_res, coeff.iqc_coeff)) { HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "%s: Failed in calculation of IQ correction.\n", __func__); goto TX_IQ_CAL_FAILED_; } coeff.phs_coeff[ch_idx][im][iqcal_idx-1] = coeff.iqc_coeff[0] & 0x7f; coeff.mag_coeff[ch_idx][im][iqcal_idx-1] = (coeff.iqc_coeff[0] >> 7) & 0x7f; if (coeff.mag_coeff[ch_idx][im][iqcal_idx-1] > 63) { coeff.mag_coeff[ch_idx][im][iqcal_idx-1] -= 128; } if (coeff.phs_coeff[ch_idx][im][iqcal_idx-1] > 63) { coeff.phs_coeff[ch_idx][im][iqcal_idx-1] -= 128; } #if 0 ath_hal_printf(ah, "IQCAL::[ch%d][gain%d]:: mag = %d phase = %d \n", ch_idx, im, coeff.mag_coeff[ch_idx][im][iqcal_idx-1], coeff.phs_coeff[ch_idx][im][iqcal_idx-1]); #endif } } //last iteration; calculate mag and phs if (iqcal_idx == max_iqcal) { if (max_iqcal>1) { for (ch_idx = 0; ch_idx < num_chains; ch_idx++) { for (im = 0; im < nmeasurement; im++) { //sort mag and phs for( ix=0;ixah_phyrestart_disabled = 1; } else { val |= AR_PHY_RESTART_ENA; AH9300(ah)->ah_phyrestart_disabled = 0; } OS_REG_WRITE(ah, AR_PHY_RESTART, val); val = OS_REG_READ(ah, AR_PHY_RESTART); } HAL_BOOL ar9300_interference_is_present(struct ath_hal *ah) { int i; struct ath_hal_private *ahpriv = AH_PRIVATE(ah); const struct ieee80211_channel *chan = ahpriv->ah_curchan; HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan); if (ichan == NULL) { ath_hal_printf(ah, "%s: called with ichan=NULL\n", __func__); return AH_FALSE; } /* This function is called after a stuck beacon, if EACS is enabled. * If CW interference is severe, then HW goes into a loop of continuous * stuck beacons and resets. On reset the NF cal history is cleared. * So the median value of the history cannot be used - * hence check if any value (Chain 0/Primary Channel) * is outside the bounds. */ HAL_NFCAL_HIST_FULL *h = AH_HOME_CHAN_NFCAL_HIST(ah, ichan); for (i = 0; i < HAL_NF_CAL_HIST_LEN_FULL; i++) { if (h->nf_cal_buffer[i][0] > AH9300(ah)->nfp->nominal + AH9300(ah)->nf_cw_int_delta) { return AH_TRUE; } } return AH_FALSE; } #if ATH_SUPPORT_CRDC void ar9300_crdc_rx_notify(struct ath_hal *ah, struct ath_rx_status *rxs) { struct ath_hal_private *ahpriv = AH_PRIVATE(ah); int rssi_index; if ((!AR_SREV_WASP(ah)) || (!ahpriv->ah_config.ath_hal_crdc_enable)) { return; } if (rxs->rs_isaggr && rxs->rs_moreaggr) { return; } if ((rxs->rs_rssi_ctl0 >= HAL_RSSI_BAD) || (rxs->rs_rssi_ctl1 >= HAL_RSSI_BAD)) { return; } rssi_index = ah->ah_crdc_rssi_ptr % HAL_MAX_CRDC_RSSI_SAMPLE; ah->ah_crdc_rssi_sample[0][rssi_index] = rxs->rs_rssi_ctl0; ah->ah_crdc_rssi_sample[1][rssi_index] = rxs->rs_rssi_ctl1; ah->ah_crdc_rssi_ptr++; } static int ar9300_crdc_avg_rssi(struct ath_hal *ah, int chain) { int crdc_rssi_sum = 0; int crdc_rssi_ptr = ah->ah_crdc_rssi_ptr, i; struct ath_hal_private *ahpriv = AH_PRIVATE(ah); int crdc_window = ahpriv->ah_config.ath_hal_crdc_window; if (crdc_window > HAL_MAX_CRDC_RSSI_SAMPLE) { crdc_window = HAL_MAX_CRDC_RSSI_SAMPLE; } for (i = 1; i <= crdc_window; i++) { crdc_rssi_sum += ah->ah_crdc_rssi_sample[chain] [(crdc_rssi_ptr - i) % HAL_MAX_CRDC_RSSI_SAMPLE]; } return crdc_rssi_sum / crdc_window; } static void ar9300_crdc_activate(struct ath_hal *ah, int rssi_diff, int enable) { int val, orig_val; struct ath_hal_private *ahpriv = AH_PRIVATE(ah); int crdc_numerator = ahpriv->ah_config.ath_hal_crdc_numerator; int crdc_denominator = ahpriv->ah_config.ath_hal_crdc_denominator; int c = (rssi_diff * crdc_numerator) / crdc_denominator; val = orig_val = OS_REG_READ(ah, AR_PHY_MULTICHAIN_CTRL); val &= 0xffffff00; if (enable) { val |= 0x1; val |= ((c << 1) & 0xff); } OS_REG_WRITE(ah, AR_PHY_MULTICHAIN_CTRL, val); HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "diff: %02d comp: %02d reg: %08x %08x\n", rssi_diff, c, orig_val, val); } void ar9300_chain_rssi_diff_compensation(struct ath_hal *ah) { struct ath_hal_private *ahpriv = AH_PRIVATE(ah); int crdc_window = ahpriv->ah_config.ath_hal_crdc_window; int crdc_rssi_ptr = ah->ah_crdc_rssi_ptr; int crdc_rssi_thresh = ahpriv->ah_config.ath_hal_crdc_rssithresh; int crdc_diff_thresh = ahpriv->ah_config.ath_hal_crdc_diffthresh; int avg_rssi[2], avg_rssi_diff; if ((!AR_SREV_WASP(ah)) || (!ahpriv->ah_config.ath_hal_crdc_enable)) { if (ah->ah_crdc_rssi_ptr) { ar9300_crdc_activate(ah, 0, 0); ah->ah_crdc_rssi_ptr = 0; } return; } if (crdc_window > HAL_MAX_CRDC_RSSI_SAMPLE) { crdc_window = HAL_MAX_CRDC_RSSI_SAMPLE; } if (crdc_rssi_ptr < crdc_window) { return; } avg_rssi[0] = ar9300_crdc_avg_rssi(ah, 0); avg_rssi[1] = ar9300_crdc_avg_rssi(ah, 1); avg_rssi_diff = avg_rssi[1] - avg_rssi[0]; HALDEBUG(ah, HAL_DEBUG_CALIBRATE, "crdc: avg: %02d %02d ", avg_rssi[0], avg_rssi[1]); if ((avg_rssi[0] < crdc_rssi_thresh) && (avg_rssi[1] < crdc_rssi_thresh)) { ar9300_crdc_activate(ah, 0, 0); } else { if (ABS(avg_rssi_diff) >= crdc_diff_thresh) { ar9300_crdc_activate(ah, avg_rssi_diff, 1); } else { ar9300_crdc_activate(ah, 0, 1); } } } #endif #if ATH_ANT_DIV_COMB HAL_BOOL ar9300_ant_ctrl_set_lna_div_use_bt_ant(struct ath_hal *ah, HAL_BOOL enable, const struct ieee80211_channel *chan) { u_int32_t value; u_int32_t regval; struct ath_hal_9300 *ahp = AH9300(ah); HAL_CHANNEL_INTERNAL *ichan; struct ath_hal_private *ahpriv = AH_PRIVATE(ah); HAL_CAPABILITIES *pcap = &ahpriv->ah_caps; if (AR_SREV_POSEIDON(ah)) { // Make sure this scheme is only used for WB225(Astra) ahp->ah_lna_div_use_bt_ant_enable = enable; ichan = ar9300_check_chan(ah, chan); if ( ichan == AH_NULL ) { HALDEBUG(ah, HAL_DEBUG_CHANNEL, "%s: invalid channel %u/0x%x; no mapping\n", __func__, chan->ic_freq, chan->ic_flags); return AH_FALSE; } if ( enable == TRUE ) { pcap->halAntDivCombSupport = TRUE; } else { pcap->halAntDivCombSupport = pcap->halAntDivCombSupportOrg; } #define AR_SWITCH_TABLE_COM2_ALL (0xffffff) #define AR_SWITCH_TABLE_COM2_ALL_S (0) value = ar9300_ant_ctrl_common2_get(ah, IS_CHAN_2GHZ(ichan)); if ( enable == TRUE ) { value &= ~AR_SWITCH_TABLE_COM2_ALL; value |= ah->ah_config.ath_hal_ant_ctrl_comm2g_switch_enable; } HALDEBUG(ah, HAL_DEBUG_RESET, "%s: com2=0x%08x\n", __func__, value); OS_REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM_2, AR_SWITCH_TABLE_COM2_ALL, value); value = ar9300_eeprom_get(ahp, EEP_ANTDIV_control); /* main_lnaconf, alt_lnaconf, main_tb, alt_tb */ regval = OS_REG_READ(ah, AR_PHY_MC_GAIN_CTRL); regval &= (~ANT_DIV_CONTROL_ALL); /* clear bit 25~30 */ regval |= (value & 0x3f) << ANT_DIV_CONTROL_ALL_S; /* enable_lnadiv */ regval &= (~MULTICHAIN_GAIN_CTRL__ENABLE_ANT_DIV_LNADIV__MASK); regval |= ((value >> 6) & 0x1) << MULTICHAIN_GAIN_CTRL__ENABLE_ANT_DIV_LNADIV__SHIFT; if ( enable == TRUE ) { regval |= ANT_DIV_ENABLE; } OS_REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval); /* enable fast_div */ regval = OS_REG_READ(ah, AR_PHY_CCK_DETECT); regval &= (~BBB_SIG_DETECT__ENABLE_ANT_FAST_DIV__MASK); regval |= ((value >> 7) & 0x1) << BBB_SIG_DETECT__ENABLE_ANT_FAST_DIV__SHIFT; if ( enable == TRUE ) { regval |= FAST_DIV_ENABLE; } OS_REG_WRITE(ah, AR_PHY_CCK_DETECT, regval); if ( AR_SREV_POSEIDON_11_OR_LATER(ah) ) { if (pcap->halAntDivCombSupport) { /* If support DivComb, set MAIN to LNA1 and ALT to LNA2 at the first beginning */ regval = OS_REG_READ(ah, AR_PHY_MC_GAIN_CTRL); /* clear bit 25~30 main_lnaconf, alt_lnaconf, main_tb, alt_tb */ regval &= (~(MULTICHAIN_GAIN_CTRL__ANT_DIV_MAIN_LNACONF__MASK | MULTICHAIN_GAIN_CTRL__ANT_DIV_ALT_LNACONF__MASK | MULTICHAIN_GAIN_CTRL__ANT_DIV_ALT_GAINTB__MASK | MULTICHAIN_GAIN_CTRL__ANT_DIV_MAIN_GAINTB__MASK)); regval |= (HAL_ANT_DIV_COMB_LNA1 << MULTICHAIN_GAIN_CTRL__ANT_DIV_MAIN_LNACONF__SHIFT); regval |= (HAL_ANT_DIV_COMB_LNA2 << MULTICHAIN_GAIN_CTRL__ANT_DIV_ALT_LNACONF__SHIFT); OS_REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval); } } return AH_TRUE; } else { return AH_TRUE; } } #endif /* ATH_ANT_DIV_COMB */ Index: projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_xmit.c =================================================================== --- projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_xmit.c (revision 278776) +++ projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_xmit.c (revision 278777) @@ -1,909 +1,973 @@ /* * Copyright (c) 2013 Qualcomm Atheros, Inc. * * 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 "opt_ah.h" #include "ah.h" #include "ah_desc.h" #include "ah_internal.h" #include "ar9300/ar9300.h" #include "ar9300/ar9300reg.h" #include "ar9300/ar9300phy.h" #include "ar9300/ar9300desc.h" #define TU_TO_USEC(_tu) ((_tu) << 10) #define ONE_EIGHTH_TU_TO_USEC(_tu8) ((_tu8) << 7) /* * Update Tx FIFO trigger level. * * Set b_inc_trig_level to TRUE to increase the trigger level. * Set b_inc_trig_level to FALSE to decrease the trigger level. * * Returns TRUE if the trigger level was updated */ HAL_BOOL ar9300_update_tx_trig_level(struct ath_hal *ah, HAL_BOOL b_inc_trig_level) { struct ath_hal_9300 *ahp = AH9300(ah); u_int32_t txcfg, cur_level, new_level; HAL_INT omask; if (AH9300(ah)->ah_tx_trig_level >= MAX_TX_FIFO_THRESHOLD && b_inc_trig_level) { return AH_FALSE; } /* * Disable interrupts while futzing with the fifo level. */ omask = ar9300_set_interrupts(ah, ahp->ah_mask_reg &~ HAL_INT_GLOBAL, 0); txcfg = OS_REG_READ(ah, AR_TXCFG); cur_level = MS(txcfg, AR_FTRIG); new_level = cur_level; if (b_inc_trig_level) { /* increase the trigger level */ if (cur_level < MAX_TX_FIFO_THRESHOLD) { new_level++; } } else if (cur_level > MIN_TX_FIFO_THRESHOLD) { new_level--; } if (new_level != cur_level) { /* Update the trigger level */ OS_REG_WRITE(ah, AR_TXCFG, (txcfg &~ AR_FTRIG) | SM(new_level, AR_FTRIG)); } /* re-enable chip interrupts */ ar9300_set_interrupts(ah, omask, 0); AH9300(ah)->ah_tx_trig_level = new_level; return (new_level != cur_level); } /* * Returns the value of Tx Trigger Level */ u_int16_t ar9300_get_tx_trig_level(struct ath_hal *ah) { return (AH9300(ah)->ah_tx_trig_level); } /* * Set the properties of the tx queue with the parameters * from q_info. */ HAL_BOOL ar9300_set_tx_queue_props(struct ath_hal *ah, int q, const HAL_TXQ_INFO *q_info) { struct ath_hal_9300 *ahp = AH9300(ah); HAL_CAPABILITIES *p_cap = &AH_PRIVATE(ah)->ah_caps; if (q >= p_cap->halTotalQueues) { HALDEBUG(ah, HAL_DEBUG_QUEUE, "%s: invalid queue num %u\n", __func__, q); return AH_FALSE; } return ath_hal_setTxQProps(ah, &ahp->ah_txq[q], q_info); } /* * Return the properties for the specified tx queue. */ HAL_BOOL ar9300_get_tx_queue_props(struct ath_hal *ah, int q, HAL_TXQ_INFO *q_info) { struct ath_hal_9300 *ahp = AH9300(ah); HAL_CAPABILITIES *p_cap = &AH_PRIVATE(ah)->ah_caps; if (q >= p_cap->halTotalQueues) { HALDEBUG(ah, HAL_DEBUG_QUEUE, "%s: invalid queue num %u\n", __func__, q); return AH_FALSE; } return ath_hal_getTxQProps(ah, q_info, &ahp->ah_txq[q]); } enum { AH_TX_QUEUE_MINUS_OFFSET_BEACON = 1, AH_TX_QUEUE_MINUS_OFFSET_CAB = 2, AH_TX_QUEUE_MINUS_OFFSET_UAPSD = 3, AH_TX_QUEUE_MINUS_OFFSET_PAPRD = 4, }; /* * Allocate and initialize a tx DCU/QCU combination. */ int ar9300_setup_tx_queue(struct ath_hal *ah, HAL_TX_QUEUE type, const HAL_TXQ_INFO *q_info) { struct ath_hal_9300 *ahp = AH9300(ah); HAL_TX_QUEUE_INFO *qi; HAL_CAPABILITIES *p_cap = &AH_PRIVATE(ah)->ah_caps; int q; /* XXX move queue assignment to driver */ switch (type) { case HAL_TX_QUEUE_BEACON: /* highest priority */ q = p_cap->halTotalQueues - AH_TX_QUEUE_MINUS_OFFSET_BEACON; break; case HAL_TX_QUEUE_CAB: /* next highest priority */ q = p_cap->halTotalQueues - AH_TX_QUEUE_MINUS_OFFSET_CAB; break; case HAL_TX_QUEUE_UAPSD: q = p_cap->halTotalQueues - AH_TX_QUEUE_MINUS_OFFSET_UAPSD; break; case HAL_TX_QUEUE_PAPRD: q = p_cap->halTotalQueues - AH_TX_QUEUE_MINUS_OFFSET_PAPRD; break; case HAL_TX_QUEUE_DATA: /* * don't infringe on top 4 queues, reserved for: * beacon, CAB, UAPSD, PAPRD */ for (q = 0; q < p_cap->halTotalQueues - AH_TX_QUEUE_MINUS_OFFSET_PAPRD; q++) { if (ahp->ah_txq[q].tqi_type == HAL_TX_QUEUE_INACTIVE) { break; } } if (q == p_cap->halTotalQueues - 3) { HALDEBUG(ah, HAL_DEBUG_QUEUE, "%s: no available tx queue\n", __func__); return -1; } break; default: HALDEBUG(ah, HAL_DEBUG_QUEUE, "%s: bad tx queue type %u\n", __func__, type); return -1; } HALDEBUG(ah, HAL_DEBUG_QUEUE, "%s: queue %u\n", __func__, q); qi = &ahp->ah_txq[q]; if (qi->tqi_type != HAL_TX_QUEUE_INACTIVE) { HALDEBUG(ah, HAL_DEBUG_QUEUE, "%s: tx queue %u already active\n", __func__, q); return -1; } OS_MEMZERO(qi, sizeof(HAL_TX_QUEUE_INFO)); qi->tqi_type = type; if (q_info == AH_NULL) { /* by default enable OK+ERR+DESC+URN interrupts */ qi->tqi_qflags = HAL_TXQ_TXOKINT_ENABLE | HAL_TXQ_TXERRINT_ENABLE | HAL_TXQ_TXDESCINT_ENABLE | HAL_TXQ_TXURNINT_ENABLE; qi->tqi_aifs = INIT_AIFS; qi->tqi_cwmin = HAL_TXQ_USEDEFAULT; /* NB: do at reset */ qi->tqi_cwmax = INIT_CWMAX; qi->tqi_shretry = INIT_SH_RETRY; qi->tqi_lgretry = INIT_LG_RETRY; qi->tqi_physCompBuf = 0; } else { qi->tqi_physCompBuf = q_info->tqi_compBuf; (void) ar9300_set_tx_queue_props(ah, q, q_info); } /* NB: must be followed by ar9300_reset_tx_queue */ return q; } /* * Update the h/w interrupt registers to reflect a tx q's configuration. */ static void set_tx_q_interrupts(struct ath_hal *ah, HAL_TX_QUEUE_INFO *qi) { struct ath_hal_9300 *ahp = AH9300(ah); HALDEBUG(ah, HAL_DEBUG_INTERRUPT, "%s: tx ok 0x%x err 0x%x eol 0x%x urn 0x%x\n", __func__, ahp->ah_tx_ok_interrupt_mask, ahp->ah_tx_err_interrupt_mask, ahp->ah_tx_eol_interrupt_mask, ahp->ah_tx_urn_interrupt_mask); OS_REG_WRITE(ah, AR_IMR_S0, SM(ahp->ah_tx_ok_interrupt_mask, AR_IMR_S0_QCU_TXOK)); OS_REG_WRITE(ah, AR_IMR_S1, SM(ahp->ah_tx_err_interrupt_mask, AR_IMR_S1_QCU_TXERR) | SM(ahp->ah_tx_eol_interrupt_mask, AR_IMR_S1_QCU_TXEOL)); OS_REG_RMW_FIELD(ah, AR_IMR_S2, AR_IMR_S2_QCU_TXURN, ahp->ah_tx_urn_interrupt_mask); ahp->ah_mask2Reg = OS_REG_READ(ah, AR_IMR_S2); } /* * Free a tx DCU/QCU combination. */ HAL_BOOL ar9300_release_tx_queue(struct ath_hal *ah, u_int q) { struct ath_hal_9300 *ahp = AH9300(ah); HAL_CAPABILITIES *p_cap = &AH_PRIVATE(ah)->ah_caps; HAL_TX_QUEUE_INFO *qi; if (q >= p_cap->halTotalQueues) { HALDEBUG(ah, HAL_DEBUG_QUEUE, "%s: invalid queue num %u\n", __func__, q); return AH_FALSE; } qi = &ahp->ah_txq[q]; if (qi->tqi_type == HAL_TX_QUEUE_INACTIVE) { HALDEBUG(ah, HAL_DEBUG_QUEUE, "%s: inactive queue %u\n", __func__, q); return AH_FALSE; } HALDEBUG(ah, HAL_DEBUG_QUEUE, "%s: release queue %u\n", __func__, q); qi->tqi_type = HAL_TX_QUEUE_INACTIVE; ahp->ah_tx_ok_interrupt_mask &= ~(1 << q); ahp->ah_tx_err_interrupt_mask &= ~(1 << q); ahp->ah_tx_eol_interrupt_mask &= ~(1 << q); ahp->ah_tx_urn_interrupt_mask &= ~(1 << q); set_tx_q_interrupts(ah, qi); return AH_TRUE; } /* * Set the retry, aifs, cwmin/max, ready_time regs for specified queue * Assumes: * phw_channel has been set to point to the current channel */ HAL_BOOL ar9300_reset_tx_queue(struct ath_hal *ah, u_int q) { struct ath_hal_9300 *ahp = AH9300(ah); // struct ath_hal_private *ap = AH_PRIVATE(ah); HAL_CAPABILITIES *p_cap = &AH_PRIVATE(ah)->ah_caps; const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan; HAL_TX_QUEUE_INFO *qi; u_int32_t cw_min, chan_cw_min, value; if (q >= p_cap->halTotalQueues) { HALDEBUG(ah, HAL_DEBUG_QUEUE, "%s: invalid queue num %u\n", __func__, q); return AH_FALSE; } qi = &ahp->ah_txq[q]; if (qi->tqi_type == HAL_TX_QUEUE_INACTIVE) { HALDEBUG(ah, HAL_DEBUG_QUEUE, "%s: inactive queue %u\n", __func__, q); return AH_TRUE; /* XXX??? */ } HALDEBUG(ah, HAL_DEBUG_QUEUE, "%s: reset queue %u\n", __func__, q); if (qi->tqi_cwmin == HAL_TXQ_USEDEFAULT) { /* * Select cwmin according to channel type. * NB: chan can be NULL during attach */ if (chan && IEEE80211_IS_CHAN_B(chan)) { chan_cw_min = INIT_CWMIN_11B; } else { chan_cw_min = INIT_CWMIN; } /* make sure that the CWmin is of the form (2^n - 1) */ for (cw_min = 1; cw_min < chan_cw_min; cw_min = (cw_min << 1) | 1) {} } else { cw_min = qi->tqi_cwmin; } /* set cw_min/Max and AIFS values */ if (q > 3 || (!AH9300(ah)->ah_fccaifs)) /* values should not be overwritten if domain is FCC and manual rate less than 24Mb is set, this check is making sure this */ { OS_REG_WRITE(ah, AR_DLCL_IFS(q), SM(cw_min, AR_D_LCL_IFS_CWMIN) | SM(qi->tqi_cwmax, AR_D_LCL_IFS_CWMAX) | SM(qi->tqi_aifs, AR_D_LCL_IFS_AIFS)); } /* Set retry limit values */ OS_REG_WRITE(ah, AR_DRETRY_LIMIT(q), SM(INIT_SSH_RETRY, AR_D_RETRY_LIMIT_STA_SH) | SM(INIT_SLG_RETRY, AR_D_RETRY_LIMIT_STA_LG) | SM(qi->tqi_shretry, AR_D_RETRY_LIMIT_FR_SH)); /* enable early termination on the QCU */ OS_REG_WRITE(ah, AR_QMISC(q), AR_Q_MISC_DCU_EARLY_TERM_REQ); /* enable DCU to wait for next fragment from QCU */ if (AR_SREV_WASP(ah) && (AH_PRIVATE((ah))->ah_macRev <= AR_SREV_REVISION_WASP_12)) { /* WAR for EV#85395: Wasp Rx overrun issue - reduces Tx queue backoff * threshold to 1 to avoid Rx overruns - Fixed in Wasp 1.3 */ OS_REG_WRITE(ah, AR_DMISC(q), AR_D_MISC_CW_BKOFF_EN | AR_D_MISC_FRAG_WAIT_EN | 0x1); } else { OS_REG_WRITE(ah, AR_DMISC(q), AR_D_MISC_CW_BKOFF_EN | AR_D_MISC_FRAG_WAIT_EN | 0x2); } /* multiqueue support */ if (qi->tqi_cbrPeriod) { OS_REG_WRITE(ah, AR_QCBRCFG(q), SM(qi->tqi_cbrPeriod, AR_Q_CBRCFG_INTERVAL) | SM(qi->tqi_cbrOverflowLimit, AR_Q_CBRCFG_OVF_THRESH)); OS_REG_WRITE(ah, AR_QMISC(q), OS_REG_READ(ah, AR_QMISC(q)) | AR_Q_MISC_FSP_CBR | (qi->tqi_cbrOverflowLimit ? AR_Q_MISC_CBR_EXP_CNTR_LIMIT_EN : 0)); } if (qi->tqi_readyTime && (qi->tqi_type != HAL_TX_QUEUE_CAB)) { OS_REG_WRITE(ah, AR_QRDYTIMECFG(q), SM(qi->tqi_readyTime, AR_Q_RDYTIMECFG_DURATION) | AR_Q_RDYTIMECFG_EN); } OS_REG_WRITE(ah, AR_DCHNTIME(q), SM(qi->tqi_burstTime, AR_D_CHNTIME_DUR) | (qi->tqi_burstTime ? AR_D_CHNTIME_EN : 0)); if (qi->tqi_burstTime && (qi->tqi_qflags & HAL_TXQ_RDYTIME_EXP_POLICY_ENABLE)) { OS_REG_WRITE(ah, AR_QMISC(q), OS_REG_READ(ah, AR_QMISC(q)) | AR_Q_MISC_RDYTIME_EXP_POLICY); } if (qi->tqi_qflags & HAL_TXQ_BACKOFF_DISABLE) { OS_REG_WRITE(ah, AR_DMISC(q), OS_REG_READ(ah, AR_DMISC(q)) | AR_D_MISC_POST_FR_BKOFF_DIS); } if (qi->tqi_qflags & HAL_TXQ_FRAG_BURST_BACKOFF_ENABLE) { OS_REG_WRITE(ah, AR_DMISC(q), OS_REG_READ(ah, AR_DMISC(q)) | AR_D_MISC_FRAG_BKOFF_EN); } switch (qi->tqi_type) { case HAL_TX_QUEUE_BEACON: /* beacon frames */ OS_REG_WRITE(ah, AR_QMISC(q), OS_REG_READ(ah, AR_QMISC(q)) | AR_Q_MISC_FSP_DBA_GATED | AR_Q_MISC_BEACON_USE | AR_Q_MISC_CBR_INCR_DIS1); OS_REG_WRITE(ah, AR_DMISC(q), OS_REG_READ(ah, AR_DMISC(q)) | (AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL << AR_D_MISC_ARB_LOCKOUT_CNTRL_S) | AR_D_MISC_BEACON_USE | AR_D_MISC_POST_FR_BKOFF_DIS); /* XXX cwmin and cwmax should be 0 for beacon queue */ if (AH_PRIVATE(ah)->ah_opmode != HAL_M_IBSS) { OS_REG_WRITE(ah, AR_DLCL_IFS(q), SM(0, AR_D_LCL_IFS_CWMIN) | SM(0, AR_D_LCL_IFS_CWMAX) | SM(qi->tqi_aifs, AR_D_LCL_IFS_AIFS)); } break; case HAL_TX_QUEUE_CAB: /* CAB frames */ /* * No longer Enable AR_Q_MISC_RDYTIME_EXP_POLICY, * bug #6079. There is an issue with the CAB Queue * not properly refreshing the Tx descriptor if * the TXE clear setting is used. */ OS_REG_WRITE(ah, AR_QMISC(q), OS_REG_READ(ah, AR_QMISC(q)) | AR_Q_MISC_FSP_DBA_GATED | AR_Q_MISC_CBR_INCR_DIS1 | AR_Q_MISC_CBR_INCR_DIS0); value = TU_TO_USEC(qi->tqi_readyTime) - (ah->ah_config.ah_sw_beacon_response_time - ah->ah_config.ah_dma_beacon_response_time) - ah->ah_config.ah_additional_swba_backoff; OS_REG_WRITE(ah, AR_QRDYTIMECFG(q), value | AR_Q_RDYTIMECFG_EN); OS_REG_WRITE(ah, AR_DMISC(q), OS_REG_READ(ah, AR_DMISC(q)) | (AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL << AR_D_MISC_ARB_LOCKOUT_CNTRL_S)); break; case HAL_TX_QUEUE_PSPOLL: /* * We may configure ps_poll QCU to be TIM-gated in the * future; TIM_GATED bit is not enabled currently because * of a hardware problem in Oahu that overshoots the TIM * bitmap in beacon and may find matching associd bit in * non-TIM elements and send PS-poll PS poll processing * will be done in software */ OS_REG_WRITE(ah, AR_QMISC(q), OS_REG_READ(ah, AR_QMISC(q)) | AR_Q_MISC_CBR_INCR_DIS1); break; case HAL_TX_QUEUE_UAPSD: OS_REG_WRITE(ah, AR_DMISC(q), OS_REG_READ(ah, AR_DMISC(q)) | AR_D_MISC_POST_FR_BKOFF_DIS); break; default: /* NB: silence compiler */ break; } #ifndef AH_DISABLE_WME /* * Yes, this is a hack and not the right way to do it, but * it does get the lockout bits and backoff set for the * high-pri WME queues for testing. We need to either extend * the meaning of queue_info->mode, or create something like * queue_info->dcumode. */ if (qi->tqi_intFlags & HAL_TXQ_USE_LOCKOUT_BKOFF_DIS) { OS_REG_WRITE(ah, AR_DMISC(q), OS_REG_READ(ah, AR_DMISC(q)) | SM(AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL, AR_D_MISC_ARB_LOCKOUT_CNTRL) | AR_D_MISC_POST_FR_BKOFF_DIS); } #endif OS_REG_WRITE(ah, AR_Q_DESC_CRCCHK, AR_Q_DESC_CRCCHK_EN); /* * Always update the secondary interrupt mask registers - this * could be a new queue getting enabled in a running system or * hw getting re-initialized during a reset! * * Since we don't differentiate between tx interrupts corresponding * to individual queues - secondary tx mask regs are always unmasked; * tx interrupts are enabled/disabled for all queues collectively * using the primary mask reg */ if (qi->tqi_qflags & HAL_TXQ_TXOKINT_ENABLE) { ahp->ah_tx_ok_interrupt_mask |= (1 << q); } else { ahp->ah_tx_ok_interrupt_mask &= ~(1 << q); } if (qi->tqi_qflags & HAL_TXQ_TXERRINT_ENABLE) { ahp->ah_tx_err_interrupt_mask |= (1 << q); } else { ahp->ah_tx_err_interrupt_mask &= ~(1 << q); } if (qi->tqi_qflags & HAL_TXQ_TXEOLINT_ENABLE) { ahp->ah_tx_eol_interrupt_mask |= (1 << q); } else { ahp->ah_tx_eol_interrupt_mask &= ~(1 << q); } if (qi->tqi_qflags & HAL_TXQ_TXURNINT_ENABLE) { ahp->ah_tx_urn_interrupt_mask |= (1 << q); } else { ahp->ah_tx_urn_interrupt_mask &= ~(1 << q); } set_tx_q_interrupts(ah, qi); return AH_TRUE; } /* * Get the TXDP for the specified queue */ u_int32_t ar9300_get_tx_dp(struct ath_hal *ah, u_int q) { HALASSERT(q < AH_PRIVATE(ah)->ah_caps.halTotalQueues); return OS_REG_READ(ah, AR_QTXDP(q)); } /* * Set the tx_dp for the specified queue */ HAL_BOOL ar9300_set_tx_dp(struct ath_hal *ah, u_int q, u_int32_t txdp) { HALASSERT(q < AH_PRIVATE(ah)->ah_caps.halTotalQueues); HALASSERT(AH9300(ah)->ah_txq[q].tqi_type != HAL_TX_QUEUE_INACTIVE); HALASSERT(txdp != 0); OS_REG_WRITE(ah, AR_QTXDP(q), txdp); return AH_TRUE; } /* * Transmit Enable is read-only now */ HAL_BOOL ar9300_start_tx_dma(struct ath_hal *ah, u_int q) { return AH_TRUE; } /* * Return the number of pending frames or 0 if the specified * queue is stopped. */ u_int32_t ar9300_num_tx_pending(struct ath_hal *ah, u_int q) { u_int32_t npend; HALASSERT(q < AH_PRIVATE(ah)->ah_caps.halTotalQueues); npend = OS_REG_READ(ah, AR_QSTS(q)) & AR_Q_STS_PEND_FR_CNT; if (npend == 0) { /* * Pending frame count (PFC) can momentarily go to zero * while TXE remains asserted. In other words a PFC of * zero is not sufficient to say that the queue has stopped. */ if (OS_REG_READ(ah, AR_Q_TXE) & (1 << q)) { npend = 1; /* arbitrarily return 1 */ } } #ifdef DEBUG if (npend && (AH9300(ah)->ah_txq[q].tqi_type == HAL_TX_QUEUE_CAB)) { if (OS_REG_READ(ah, AR_Q_RDYTIMESHDN) & (1 << q)) { HALDEBUG(ah, HAL_DEBUG_QUEUE, "RTSD on CAB queue\n"); /* Clear the ready_time shutdown status bits */ OS_REG_WRITE(ah, AR_Q_RDYTIMESHDN, 1 << q); } } #endif HALASSERT((npend == 0) || (AH9300(ah)->ah_txq[q].tqi_type != HAL_TX_QUEUE_INACTIVE)); return npend; } /* * Stop transmit on the specified queue */ HAL_BOOL ar9300_stop_tx_dma(struct ath_hal *ah, u_int q, u_int timeout) { + struct ath_hal_9300 *ahp = AH9300(ah); + /* + * If we call abort txdma instead, no need to stop RX. + * Otherwise, the RX logic might not be restarted properly. + */ + ahp->ah_abort_txdma_norx = AH_FALSE; + + /* * Directly call abort. It is better, hardware-wise, to stop all * queues at once than individual ones. */ return ar9300_abort_tx_dma(ah); #if 0 #define AH_TX_STOP_DMA_TIMEOUT 4000 /* usec */ #define AH_TIME_QUANTUM 100 /* usec */ u_int wait; HALASSERT(q < AH_PRIVATE(ah)->ah_caps.hal_total_queues); HALASSERT(AH9300(ah)->ah_txq[q].tqi_type != HAL_TX_QUEUE_INACTIVE); if (timeout == 0) { timeout = AH_TX_STOP_DMA_TIMEOUT; } OS_REG_WRITE(ah, AR_Q_TXD, 1 << q); for (wait = timeout / AH_TIME_QUANTUM; wait != 0; wait--) { if (ar9300_num_tx_pending(ah, q) == 0) { break; } OS_DELAY(AH_TIME_QUANTUM); /* XXX get actual value */ } #ifdef AH_DEBUG if (wait == 0) { HALDEBUG(ah, HAL_DEBUG_QUEUE, "%s: queue %u DMA did not stop in 100 msec\n", __func__, q); HALDEBUG(ah, HAL_DEBUG_QUEUE, "%s: QSTS 0x%x Q_TXE 0x%x Q_TXD 0x%x Q_CBR 0x%x\n", __func__, OS_REG_READ(ah, AR_QSTS(q)), OS_REG_READ(ah, AR_Q_TXE), OS_REG_READ(ah, AR_Q_TXD), OS_REG_READ(ah, AR_QCBRCFG(q))); HALDEBUG(ah, HAL_DEBUG_QUEUE, "%s: Q_MISC 0x%x Q_RDYTIMECFG 0x%x Q_RDYTIMESHDN 0x%x\n", __func__, OS_REG_READ(ah, AR_QMISC(q)), OS_REG_READ(ah, AR_QRDYTIMECFG(q)), OS_REG_READ(ah, AR_Q_RDYTIMESHDN)); } #endif /* AH_DEBUG */ /* 2413+ and up can kill packets at the PCU level */ if (ar9300_num_tx_pending(ah, q)) { u_int32_t tsf_low, j; HALDEBUG(ah, HAL_DEBUG_QUEUE, "%s: Num of pending TX Frames %d on Q %d\n", __func__, ar9300_num_tx_pending(ah, q), q); /* Kill last PCU Tx Frame */ /* TODO - save off and restore current values of Q1/Q2? */ for (j = 0; j < 2; j++) { tsf_low = OS_REG_READ(ah, AR_TSF_L32); OS_REG_WRITE(ah, AR_QUIET2, SM(10, AR_QUIET2_QUIET_DUR)); OS_REG_WRITE(ah, AR_QUIET_PERIOD, 100); OS_REG_WRITE(ah, AR_NEXT_QUIET_TIMER, tsf_low >> 10); OS_REG_SET_BIT(ah, AR_TIMER_MODE, AR_QUIET_TIMER_EN); if ((OS_REG_READ(ah, AR_TSF_L32) >> 10) == (tsf_low >> 10)) { break; } HALDEBUG(ah, HAL_DEBUG_QUEUE, "%s: TSF have moved while trying to set " "quiet time TSF: 0x%08x\n", __func__, tsf_low); /* TSF shouldn't count twice or reg access is taking forever */ HALASSERT(j < 1); } OS_REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH); /* Allow the quiet mechanism to do its work */ OS_DELAY(200); OS_REG_CLR_BIT(ah, AR_TIMER_MODE, AR_QUIET_TIMER_EN); /* Verify all transmit is dead */ wait = timeout / AH_TIME_QUANTUM; while (ar9300_num_tx_pending(ah, q)) { if ((--wait) == 0) { HALDEBUG(ah, HAL_DEBUG_TX, "%s: Failed to stop Tx DMA in %d msec " "after killing last frame\n", __func__, timeout / 1000); break; } OS_DELAY(AH_TIME_QUANTUM); } OS_REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH); } OS_REG_WRITE(ah, AR_Q_TXD, 0); return (wait != 0); #undef AH_TX_STOP_DMA_TIMEOUT #undef AH_TIME_QUANTUM #endif } /* * Really Stop transmit on the specified queue */ HAL_BOOL ar9300_stop_tx_dma_indv_que(struct ath_hal *ah, u_int q, u_int timeout) { #define AH_TX_STOP_DMA_TIMEOUT 4000 /* usec */ #define AH_TIME_QUANTUM 100 /* usec */ u_int wait; HALASSERT(q < AH_PRIVATE(ah)->ah_caps.hal_total_queues); HALASSERT(AH9300(ah)->ah_txq[q].tqi_type != HAL_TX_QUEUE_INACTIVE); if (timeout == 0) { timeout = AH_TX_STOP_DMA_TIMEOUT; } OS_REG_WRITE(ah, AR_Q_TXD, 1 << q); for (wait = timeout / AH_TIME_QUANTUM; wait != 0; wait--) { if (ar9300_num_tx_pending(ah, q) == 0) { break; } OS_DELAY(AH_TIME_QUANTUM); /* XXX get actual value */ } #ifdef AH_DEBUG if (wait == 0) { HALDEBUG(ah, HAL_DEBUG_QUEUE, "%s: queue %u DMA did not stop in 100 msec\n", __func__, q); HALDEBUG(ah, HAL_DEBUG_QUEUE, "%s: QSTS 0x%x Q_TXE 0x%x Q_TXD 0x%x Q_CBR 0x%x\n", __func__, OS_REG_READ(ah, AR_QSTS(q)), OS_REG_READ(ah, AR_Q_TXE), OS_REG_READ(ah, AR_Q_TXD), OS_REG_READ(ah, AR_QCBRCFG(q))); HALDEBUG(ah, HAL_DEBUG_QUEUE, "%s: Q_MISC 0x%x Q_RDYTIMECFG 0x%x Q_RDYTIMESHDN 0x%x\n", __func__, OS_REG_READ(ah, AR_QMISC(q)), OS_REG_READ(ah, AR_QRDYTIMECFG(q)), OS_REG_READ(ah, AR_Q_RDYTIMESHDN)); } #endif /* AH_DEBUG */ /* 2413+ and up can kill packets at the PCU level */ if (ar9300_num_tx_pending(ah, q)) { u_int32_t tsf_low, j; HALDEBUG(ah, HAL_DEBUG_QUEUE, "%s: Num of pending TX Frames %d on Q %d\n", __func__, ar9300_num_tx_pending(ah, q), q); /* Kill last PCU Tx Frame */ /* TODO - save off and restore current values of Q1/Q2? */ for (j = 0; j < 2; j++) { tsf_low = OS_REG_READ(ah, AR_TSF_L32); OS_REG_WRITE(ah, AR_QUIET2, SM(10, AR_QUIET2_QUIET_DUR)); OS_REG_WRITE(ah, AR_QUIET_PERIOD, 100); OS_REG_WRITE(ah, AR_NEXT_QUIET_TIMER, tsf_low >> 10); OS_REG_SET_BIT(ah, AR_TIMER_MODE, AR_QUIET_TIMER_EN); if ((OS_REG_READ(ah, AR_TSF_L32) >> 10) == (tsf_low >> 10)) { break; } HALDEBUG(ah, HAL_DEBUG_QUEUE, "%s: TSF have moved while trying to set " "quiet time TSF: 0x%08x\n", __func__, tsf_low); /* TSF shouldn't count twice or reg access is taking forever */ HALASSERT(j < 1); } OS_REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH); /* Allow the quiet mechanism to do its work */ OS_DELAY(200); OS_REG_CLR_BIT(ah, AR_TIMER_MODE, AR_QUIET_TIMER_EN); /* Verify all transmit is dead */ wait = timeout / AH_TIME_QUANTUM; while (ar9300_num_tx_pending(ah, q)) { if ((--wait) == 0) { HALDEBUG(ah, HAL_DEBUG_TX, "%s: Failed to stop Tx DMA in %d msec " "after killing last frame\n", __func__, timeout / 1000); break; } OS_DELAY(AH_TIME_QUANTUM); } OS_REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH); } OS_REG_WRITE(ah, AR_Q_TXD, 0); return (wait != 0); #undef AH_TX_STOP_DMA_TIMEOUT #undef AH_TIME_QUANTUM } /* * Abort transmit on all queues */ #define AR9300_ABORT_LOOPS 1000 #define AR9300_ABORT_WAIT 5 +#define NEXT_TBTT_NOW 10 HAL_BOOL ar9300_abort_tx_dma(struct ath_hal *ah) { + struct ath_hal_9300 *ahp = AH9300(ah); int i, q; + u_int32_t nexttbtt, nextdba, tsf_tbtt, tbtt, dba; + HAL_BOOL stopped; + HAL_BOOL status = AH_TRUE; + if (ahp->ah_abort_txdma_norx) { + /* + * First of all, make sure RX has been stopped + */ + if (ar9300_get_power_mode(ah) != HAL_PM_FULL_SLEEP) { + /* Need to stop RX DMA before reset otherwise chip might hang */ + stopped = ar9300_set_rx_abort(ah, AH_TRUE); /* abort and disable PCU */ + ar9300_set_rx_filter(ah, 0); + stopped &= ar9300_stop_dma_receive(ah, 0); /* stop and disable RX DMA */ + if (!stopped) { + /* + * During the transition from full sleep to reset, + * recv DMA regs are not available to be read + */ + HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, + "%s[%d]: ar9300_stop_dma_receive failed\n", __func__, __LINE__); + //We still continue to stop TX dma + //return AH_FALSE; + } + } else { + HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, + "%s[%d]: Chip is already in full sleep\n", __func__, __LINE__); + } + } + /* * set txd on all queues */ OS_REG_WRITE(ah, AR_Q_TXD, AR_Q_TXD_M); /* * set tx abort bits (also disable rx) */ OS_REG_SET_BIT(ah, AR_PCU_MISC, AR_PCU_FORCE_QUIET_COLL | AR_PCU_CLEAR_VMF); - OS_REG_SET_BIT(ah, AR_DIAG_SW, (AR_DIAG_FORCE_CH_IDLE_HIGH | AR_DIAG_RX_DIS | - AR_DIAG_RX_ABORT | AR_DIAG_FORCE_RX_CLEAR)); + /* Add a new receipe from K31 code */ + OS_REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH | AR_DIAG_RX_DIS | + AR_DIAG_RX_ABORT | AR_DIAG_FORCE_RX_CLEAR); + /* beacon Q flush */ + nexttbtt = OS_REG_READ(ah, AR_NEXT_TBTT_TIMER); + nextdba = OS_REG_READ(ah, AR_NEXT_DMA_BEACON_ALERT); + //printk("%s[%d]:dba: %d, nt: %d \n", __func__, __LINE__, nextdba, nexttbtt); + tsf_tbtt = OS_REG_READ(ah, AR_TSF_L32); + tbtt = tsf_tbtt + NEXT_TBTT_NOW; + dba = tsf_tbtt; + OS_REG_WRITE(ah, AR_NEXT_DMA_BEACON_ALERT, dba); + OS_REG_WRITE(ah, AR_NEXT_TBTT_TIMER, tbtt); OS_REG_SET_BIT(ah, AR_D_GBL_IFS_MISC, AR_D_GBL_IFS_MISC_IGNORE_BACKOFF); - /* Let TXE (all queues) clear before waiting on any pending frames */ - for (i = 0; i < AR9300_ABORT_LOOPS; i++) { - if (OS_REG_READ(ah, AR_Q_TXE) == 0) { + /* + * Let TXE (all queues) clear before waiting for any pending frames + * This is needed before starting the RF_BUS GRANT sequence other wise causes kernel + * panic + */ + for(i = 0; i < AR9300_ABORT_LOOPS; i++) { + if(OS_REG_READ(ah, AR_Q_TXE) == 0) { break; } OS_DELAY(AR9300_ABORT_WAIT); } if (i == AR9300_ABORT_LOOPS) { HALDEBUG(ah, HAL_DEBUG_TX, "%s[%d] reached max wait on TXE\n", __func__, __LINE__); } /* * wait on all tx queues + * This need to be checked in the last to gain extra 50 usec. on avg. + * Currently checked first since we dont have a previous channel information currently. + * Which is needed to revert the rf changes. */ - for (q = 0; q < AR_NUM_QCU; q++) { + for (q = AR_NUM_QCU - 1; q >= 0; q--) { for (i = 0; i < AR9300_ABORT_LOOPS; i++) { - if (!ar9300_num_tx_pending(ah, q)) { + if (!(ar9300_num_tx_pending(ah, q))) { break; } OS_DELAY(AR9300_ABORT_WAIT); } if (i == AR9300_ABORT_LOOPS) { - HALDEBUG(ah, HAL_DEBUG_TX, - "%s[%d] reached max wait on pending tx, q %d\n", - __func__, __LINE__, q); - return AH_FALSE; + status = AH_FALSE; + HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, + "ABORT LOOP finsihsed for Q: %d, num_pending: %d \n", + q, ar9300_num_tx_pending(ah, q)); + goto exit; } } + /* Updating the beacon alert register with correct value */ + OS_REG_WRITE(ah, AR_NEXT_DMA_BEACON_ALERT, nextdba); + OS_REG_WRITE(ah, AR_NEXT_TBTT_TIMER, nexttbtt); + +exit: /* * clear tx abort bits */ OS_REG_CLR_BIT(ah, AR_PCU_MISC, AR_PCU_FORCE_QUIET_COLL | AR_PCU_CLEAR_VMF); - OS_REG_CLR_BIT(ah, AR_DIAG_SW, (AR_DIAG_FORCE_CH_IDLE_HIGH | AR_DIAG_RX_DIS | - AR_DIAG_RX_ABORT | AR_DIAG_FORCE_RX_CLEAR)); + /* Added a new receipe from K31 code */ + OS_REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH | AR_DIAG_RX_DIS | + AR_DIAG_RX_ABORT | AR_DIAG_FORCE_RX_CLEAR); OS_REG_CLR_BIT(ah, AR_D_GBL_IFS_MISC, AR_D_GBL_IFS_MISC_IGNORE_BACKOFF); /* * clear txd */ OS_REG_WRITE(ah, AR_Q_TXD, 0); - return AH_TRUE; + ahp->ah_abort_txdma_norx = AH_TRUE; + + return status; } /* * Determine which tx queues need interrupt servicing. */ void ar9300_get_tx_intr_queue(struct ath_hal *ah, u_int32_t *txqs) { HALDEBUG(AH_NULL, HAL_DEBUG_UNMASKABLE, "ar9300_get_tx_intr_queue: Should not be called\n"); #if 0 struct ath_hal_9300 *ahp = AH9300(ah); *txqs &= ahp->ah_intr_txqs; ahp->ah_intr_txqs &= ~(*txqs); #endif } void ar9300_reset_tx_status_ring(struct ath_hal *ah) { struct ath_hal_9300 *ahp = AH9300(ah); ahp->ts_tail = 0; /* Zero out the status descriptors */ OS_MEMZERO((void *)ahp->ts_ring, ahp->ts_size * sizeof(struct ar9300_txs)); HALDEBUG(ah, HAL_DEBUG_QUEUE, "%s: TS Start 0x%x End 0x%x Virt %p, Size %d\n", __func__, ahp->ts_paddr_start, ahp->ts_paddr_end, ahp->ts_ring, ahp->ts_size); OS_REG_WRITE(ah, AR_Q_STATUS_RING_START, ahp->ts_paddr_start); OS_REG_WRITE(ah, AR_Q_STATUS_RING_END, ahp->ts_paddr_end); } void ar9300_setup_tx_status_ring(struct ath_hal *ah, void *ts_start, u_int32_t ts_paddr_start, u_int16_t size) { struct ath_hal_9300 *ahp = AH9300(ah); ahp->ts_paddr_start = ts_paddr_start; ahp->ts_paddr_end = ts_paddr_start + (size * sizeof(struct ar9300_txs)); ahp->ts_size = size; ahp->ts_ring = (struct ar9300_txs *)ts_start; ar9300_reset_tx_status_ring(ah); } Index: projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9340.ini =================================================================== --- projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9340.ini (revision 278776) +++ projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9340.ini (revision 278777) @@ -1,4330 +1,4334 @@ /* * Copyright (c) 2013 Qualcomm Atheros, Inc. * * 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. */ static const u_int32_t ar9340_wasp_1p0_baseband_postamble_emulation[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x00009e18 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x00009e44 , 0x005c0000 , 0x005c0000 , 0x005c0000 , 0x005c0000 }, { 0x0000a258 , 0x02020200 , 0x02020200 , 0x02020200 , 0x02020200 }, { 0x0000a25c , 0x00000e0e , 0x00000e0e , 0x00000e0e , 0x00000e0e }, { 0x0000a28c , 0x00011111 , 0x00011111 , 0x00011111 , 0x00011111 }, { 0x0000a2c4 , 0x00148d18 , 0x00148d18 , 0x00148d20 , 0x00148d20 }, { 0x0000a2d8 , 0xf999a800 , 0xf999a800 , 0xf999a80c , 0xf999a80c }, { 0x0000a50c , 0x0000c00a , 0x0000c00a , 0x0000c00a , 0x0000c00a }, { 0x0000a538 , 0x00038e8c , 0x00038e8c , 0x00038e8c , 0x00038e8c }, { 0x0000a53c , 0x0003cecc , 0x0003cecc , 0x0003cecc , 0x0003cecc }, { 0x0000a540 , 0x00040ed4 , 0x00040ed4 , 0x00040ed4 , 0x00040ed4 }, { 0x0000a544 , 0x00044edc , 0x00044edc , 0x00044edc , 0x00044edc }, { 0x0000a548 , 0x00048ede , 0x00048ede , 0x00048ede , 0x00048ede }, { 0x0000a54c , 0x0004cf1e , 0x0004cf1e , 0x0004cf1e , 0x0004cf1e }, { 0x0000a550 , 0x00050f5e , 0x00050f5e , 0x00050f5e , 0x00050f5e }, { 0x0000a554 , 0x00054f9e , 0x00054f9e , 0x00054f9e , 0x00054f9e }, { 0x0000ae18 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, }; static const u_int32_t ar9340_wasp_1p0_mac_core_emulation[][2] = { /* Addr allmodes */ { 0x00000030 , 0x00020085 }, { 0x00000044 , 0x00000008 }, { 0x0000805c , 0xffffc7ff }, { 0x00008344 , 0xaa4a105b }, }; static const u_int32_t ar9340_wasp_1p0_radio_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x000160ac , 0xa4646800 , 0xa4646800 , 0xa4646800 , 0xa4646800 }, { 0x0001610c , 0x08000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x00016140 , 0x10804000 , 0x10804000 , 0x50804000 , 0x50804000 }, { 0x0001650c , 0x08000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x00016540 , 0x10804000 , 0x10804000 , 0x50804000 , 0x50804000 }, }; static const u_int32_t ar9340Modes_lowest_ob_db_tx_gain_table_wasp_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x0000a410 , 0x000050d9 , 0x000050d9 , 0x000050d9 , 0x000050d9 }, { 0x0000a500 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a504 , 0x06000003 , 0x06000003 , 0x04000002 , 0x04000002 }, { 0x0000a508 , 0x0a000020 , 0x0a000020 , 0x08000004 , 0x08000004 }, { 0x0000a50c , 0x10000023 , 0x10000023 , 0x0b000200 , 0x0b000200 }, { 0x0000a510 , 0x16000220 , 0x16000220 , 0x0f000202 , 0x0f000202 }, { 0x0000a514 , 0x1c000223 , 0x1c000223 , 0x12000400 , 0x12000400 }, { 0x0000a518 , 0x21020220 , 0x21020220 , 0x16000402 , 0x16000402 }, { 0x0000a51c , 0x27020223 , 0x27020223 , 0x19000404 , 0x19000404 }, { 0x0000a520 , 0x2b022220 , 0x2b022220 , 0x1c000603 , 0x1c000603 }, { 0x0000a524 , 0x2f022222 , 0x2f022222 , 0x21000a02 , 0x21000a02 }, { 0x0000a528 , 0x34022225 , 0x34022225 , 0x25000a04 , 0x25000a04 }, { 0x0000a52c , 0x3a02222a , 0x3a02222a , 0x28000a20 , 0x28000a20 }, { 0x0000a530 , 0x3e02222c , 0x3e02222c , 0x2c000e20 , 0x2c000e20 }, { 0x0000a534 , 0x4202242a , 0x4202242a , 0x30000e22 , 0x30000e22 }, { 0x0000a538 , 0x4702244a , 0x4702244a , 0x34000e24 , 0x34000e24 }, { 0x0000a53c , 0x4b02244c , 0x4b02244c , 0x38001640 , 0x38001640 }, { 0x0000a540 , 0x4e02246c , 0x4e02246c , 0x3c001660 , 0x3c001660 }, { 0x0000a544 , 0x5302266c , 0x5302266c , 0x3f001861 , 0x3f001861 }, { 0x0000a548 , 0x5702286c , 0x5702286c , 0x43001a81 , 0x43001a81 }, { 0x0000a54c , 0x5c04286b , 0x5c04286b , 0x47001a83 , 0x47001a83 }, { 0x0000a550 , 0x61042a6c , 0x61042a6c , 0x4a001c84 , 0x4a001c84 }, { 0x0000a554 , 0x66062a6c , 0x66062a6c , 0x4e001ce3 , 0x4e001ce3 }, { 0x0000a558 , 0x6b062e6c , 0x6b062e6c , 0x52001ce5 , 0x52001ce5 }, { 0x0000a55c , 0x7006308c , 0x7006308c , 0x56001ce9 , 0x56001ce9 }, { 0x0000a560 , 0x730a308a , 0x730a308a , 0x5a001ceb , 0x5a001ceb }, { 0x0000a564 , 0x770a308c , 0x770a308c , 0x5d001eec , 0x5d001eec }, { 0x0000a568 , 0x770a308c , 0x770a308c , 0x5d001eec , 0x5d001eec }, { 0x0000a56c , 0x770a308c , 0x770a308c , 0x5d001eec , 0x5d001eec }, { 0x0000a570 , 0x770a308c , 0x770a308c , 0x5d001eec , 0x5d001eec }, { 0x0000a574 , 0x770a308c , 0x770a308c , 0x5d001eec , 0x5d001eec }, { 0x0000a578 , 0x770a308c , 0x770a308c , 0x5d001eec , 0x5d001eec }, { 0x0000a57c , 0x770a308c , 0x770a308c , 0x5d001eec , 0x5d001eec }, { 0x0000a580 , 0x00800000 , 0x00800000 , 0x00800000 , 0x00800000 }, { 0x0000a584 , 0x06800003 , 0x06800003 , 0x04800002 , 0x04800002 }, { 0x0000a588 , 0x0a800020 , 0x0a800020 , 0x08800004 , 0x08800004 }, { 0x0000a58c , 0x10800023 , 0x10800023 , 0x0b800200 , 0x0b800200 }, { 0x0000a590 , 0x16800220 , 0x16800220 , 0x0f800202 , 0x0f800202 }, { 0x0000a594 , 0x1c800223 , 0x1c800223 , 0x12800400 , 0x12800400 }, { 0x0000a598 , 0x21820220 , 0x21820220 , 0x16800402 , 0x16800402 }, { 0x0000a59c , 0x27820223 , 0x27820223 , 0x19800404 , 0x19800404 }, { 0x0000a5a0 , 0x2b822220 , 0x2b822220 , 0x1c800603 , 0x1c800603 }, { 0x0000a5a4 , 0x2f822222 , 0x2f822222 , 0x21800a02 , 0x21800a02 }, { 0x0000a5a8 , 0x34822225 , 0x34822225 , 0x25800a04 , 0x25800a04 }, { 0x0000a5ac , 0x3a82222a , 0x3a82222a , 0x28800a20 , 0x28800a20 }, { 0x0000a5b0 , 0x3e82222c , 0x3e82222c , 0x2c800e20 , 0x2c800e20 }, { 0x0000a5b4 , 0x4282242a , 0x4282242a , 0x30800e22 , 0x30800e22 }, { 0x0000a5b8 , 0x4782244a , 0x4782244a , 0x34800e24 , 0x34800e24 }, { 0x0000a5bc , 0x4b82244c , 0x4b82244c , 0x38801640 , 0x38801640 }, { 0x0000a5c0 , 0x4e82246c , 0x4e82246c , 0x3c801660 , 0x3c801660 }, { 0x0000a5c4 , 0x5382266c , 0x5382266c , 0x3f801861 , 0x3f801861 }, { 0x0000a5c8 , 0x5782286c , 0x5782286c , 0x43801a81 , 0x43801a81 }, { 0x0000a5cc , 0x5c84286b , 0x5c84286b , 0x47801a83 , 0x47801a83 }, { 0x0000a5d0 , 0x61842a6c , 0x61842a6c , 0x4a801c84 , 0x4a801c84 }, { 0x0000a5d4 , 0x66862a6c , 0x66862a6c , 0x4e801ce3 , 0x4e801ce3 }, { 0x0000a5d8 , 0x6b862e6c , 0x6b862e6c , 0x52801ce5 , 0x52801ce5 }, { 0x0000a5dc , 0x7086308c , 0x7086308c , 0x56801ce9 , 0x56801ce9 }, { 0x0000a5e0 , 0x738a308a , 0x738a308a , 0x5a801ceb , 0x5a801ceb }, { 0x0000a5e4 , 0x778a308c , 0x778a308c , 0x5d801eec , 0x5d801eec }, { 0x0000a5e8 , 0x778a308c , 0x778a308c , 0x5d801eec , 0x5d801eec }, { 0x0000a5ec , 0x778a308c , 0x778a308c , 0x5d801eec , 0x5d801eec }, { 0x0000a5f0 , 0x778a308c , 0x778a308c , 0x5d801eec , 0x5d801eec }, { 0x0000a5f4 , 0x778a308c , 0x778a308c , 0x5d801eec , 0x5d801eec }, { 0x0000a5f8 , 0x778a308c , 0x778a308c , 0x5d801eec , 0x5d801eec }, { 0x0000a5fc , 0x778a308c , 0x778a308c , 0x5d801eec , 0x5d801eec }, { 0x00016044 , 0x056db2db , 0x056db2db , 0x056db2db , 0x056db2db }, { 0x00016048 , 0x24925266 , 0x24925266 , 0x24925266 , 0x24925266 }, { 0x00016444 , 0x056db2db , 0x056db2db , 0x056db2db , 0x056db2db }, { 0x00016448 , 0x24925266 , 0x24925266 , 0x24925266 , 0x24925266 }, }; static const u_int32_t ar9340_wasp_1p0_baseband_core_emulation[][2] = { /* Addr allmodes */ { 0x00009800 , 0xafa68e30 }, { 0x00009884 , 0x00002842 }, { 0x00009c04 , 0xff55ff55 }, { 0x00009c08 , 0x0320ff55 }, { 0x00009e3c , 0xcf946221 }, { 0x00009e50 , 0x00000000 }, { 0x00009fcc , 0x00000014 }, { 0x0000a344 , 0x00000010 }, { 0x0000a398 , 0x00000000 }, { 0x0000a39c , 0x71733d01 }, { 0x0000a3a0 , 0xd0ad5c12 }, { 0x0000a3c0 , 0x22222220 }, { 0x0000a3c4 , 0x22222222 }, { 0x0000a404 , 0x00418a11 }, { 0x0000a418 , 0x050001ce }, { 0x0000a438 , 0x00001800 }, { 0x0000a458 , 0x01444452 }, { 0x0000a690 , 0x00000038 }, }; static const u_int32_t ar9340Modes_fast_clock_wasp_1p0[][3] = { /* Addr 5G_HT20 5G_HT40 */ { 0x00001030 , 0x00000268 , 0x000004d0 }, { 0x00001070 , 0x0000018c , 0x00000318 }, { 0x000010b0 , 0x00000fd0 , 0x00001fa0 }, { 0x00008014 , 0x044c044c , 0x08980898 }, { 0x0000801c , 0x148ec02b , 0x148ec057 }, { 0x00008318 , 0x000044c0 , 0x00008980 }, { 0x00009e00 , 0x0372131c , 0x0372131c }, { 0x0000a230 , 0x0000000b , 0x00000016 }, { 0x0000a254 , 0x00000898 , 0x00001130 }, }; static const u_int32_t ar9340_wasp_1p0_radio_core[][2] = { /* Addr allmodes */ { 0x00016000 , 0x36db6db6 }, //rxrf_bias1 { 0x00016004 , 0x6db6db40 }, //rxrf_bias2 { 0x00016008 , 0x73f00000 }, //rxrf_gainstages { 0x0001600c , 0x00000000 }, //rxrf_agc { 0x00016040 , 0x7f80fff8 }, //txrf1 { 0x00016044 , 0x03b6d2db }, //txrf2 { 0x00016048 , 0x24925266 }, //txrf3 { 0x0001604c , 0x000f0278 }, //txrf4 { 0x00016050 , 0x6db6db6c }, //txrf5 { 0x00016054 , 0x6db60000 }, //txrf6 { 0x00016080 , 0x00080000 }, //synth1 { 0x00016084 , 0x0e48048c }, //synth2 { 0x00016088 , 0x14214514 }, //synth3 { 0x0001608c , 0x119f081c }, //synth4 (beta4 -> beta6 change) ===> Revert to Beta4 setting { 0x00016090 , 0x24926490 }, //synth5 { 0x00016094 , 0x00000000 }, //synth6 { 0x00016098 , 0xd411eb84 }, //synth7 { 0x0001609c , 0x03e47f32 }, //synth8 (beta4 -> beta6 change) { 0x000160a0 , 0xc2108ffe }, //synth9 { 0x000160a4 , 0x812fc370 }, //synth10 { 0x000160a8 , 0x423c8000 }, //synth11 { 0x000160ac , 0xa4646800 }, //synth12 { 0x000160b0 , 0x00fe7f46 }, //synth13 (beta4 -> beta6 change) { 0x000160b4 , 0x92480000 }, //synth14 { 0x000160c0 , 0x006db6db }, //bias1 { 0x000160c4 , 0x6db6db60 }, //bias2 { 0x000160c8 , 0x6db6db6c }, //bias3 { 0x000160cc , 0x6de6db6c }, //bias4 { 0x000160d0 , 0xb6da4924 }, //bias5 { 0x00016100 , 0x04cb0001 }, //rxtx1 { 0x00016104 , 0xfff80000 }, //rxtx2 { 0x00016108 , 0x00080010 }, //rxtx3 { 0x00016140 , 0x50804008 }, //bb1 { 0x00016144 , 0x01884080 }, //bb2 { 0x00016148 , 0x000080c0 }, //bb3 { 0x00016280 , 0x01000015 }, //top { 0x00016284 , 0x15530000 }, //top2 (beta4 -> beta6 change to address the 2G RX reset-reset variation) { 0x00016288 , 0x00318000 }, //top3 { 0x0001628c , 0x50000000 }, //therm { 0x00016380 , 0x00000000 }, //rbist_cntrl { 0x00016384 , 0x00000000 }, //tx_dc_offset { 0x00016388 , 0x00800700 }, //tx_tonegen0 { 0x0001638c , 0x00800700 }, //tx_tonegen1 { 0x00016390 , 0x00800700 }, //tx_lftonegen0 { 0x00016394 , 0x00000000 }, //tx_linear_ramp_i { 0x00016398 , 0x00000000 }, //tx_linear_ramp_q { 0x0001639c , 0x00000000 }, //tx_prbs_mag { 0x000163a0 , 0x00000001 }, //tx_prbs_seed_i { 0x000163a4 , 0x00000001 }, //tx_prbs_seed_q { 0x000163a8 , 0x00000000 }, //cmac_dc_cancel { 0x000163ac , 0x00000000 }, //cmac_dc_offset { 0x000163b0 , 0x00000000 }, //cmac_corr { 0x000163b4 , 0x00000000 }, //cmac_power { 0x000163b8 , 0x00000000 }, //cmac_cross_corr { 0x000163bc , 0x00000000 }, //cmac_i2q2 { 0x000163c0 , 0x000000a0 }, //cmac_power_hpf { 0x000163c4 , 0x000c0000 }, //rxdac_set1 { 0x000163c8 , 0x14021402 }, //rxdac_set2 { 0x000163cc , 0x00001402 }, //rxdac_long_shift { 0x000163d0 , 0x00000000 }, //cmac_results_i { 0x000163d4 , 0x00000000 }, //cmac_results_q { 0x00016400 , 0x36db6db6 }, //ch1_rxrf_bias1 { 0x00016404 , 0x6db6db40 }, //ch1_rxrf_bias2 { 0x00016408 , 0x73f00000 }, //ch1_rxrf_gainstages { 0x0001640c , 0x00000000 }, //ch1_rxrf_agc { 0x00016440 , 0x7f80fff8 }, //ch1_txrf1 { 0x00016444 , 0x03b6d2db }, //ch1_txrf2 { 0x00016448 , 0x24927266 }, //ch1_txrf3 { 0x0001644c , 0x000f0278 }, //ch1_txrf4 { 0x00016450 , 0x6db6db6c }, //ch1_txrf5 { 0x00016454 , 0x6db60000 }, //ch1_txrf6 { 0x00016500 , 0x04cb0001 }, //ch1_rxtx1 { 0x00016504 , 0xfff80000 }, //ch1_rxtx2 { 0x00016508 , 0x00080010 }, //ch1_rxtx3 { 0x0001650c , 0x00000000 }, //ch1_rxtx4 { 0x00016540 , 0x50804008 }, //ch1_bb1 { 0x00016544 , 0x01884080 }, //ch1_bb2 { 0x00016548 , 0x000080c0 }, //ch1_bb3 { 0x00016780 , 0x00000000 }, //ch1_rbist_cntrl { 0x00016784 , 0x00000000 }, //ch1_tx_dc_offset { 0x00016788 , 0x00800700 }, //ch1_tx_tonegen0 { 0x0001678c , 0x00800700 }, //ch1_tx_tonegen1 { 0x00016790 , 0x00800700 }, //ch1_tx_lftonegen0 { 0x00016794 , 0x00000000 }, //ch1_tx_linear_ramp_i { 0x00016798 , 0x00000000 }, //ch1_tx_linear_ramp_q { 0x0001679c , 0x00000000 }, //ch1_tx_prbs_mag { 0x000167a0 , 0x00000001 }, //ch1_tx_prbs_seed_i { 0x000167a4 , 0x00000001 }, //ch1_tx_prbs_seed_q { 0x000167a8 , 0x00000000 }, //ch1_cmac_dc_cancel { 0x000167ac , 0x00000000 }, //ch1_cmac_dc_offset { 0x000167b0 , 0x00000000 }, //ch1_cmac_corr { 0x000167b4 , 0x00000000 }, //ch1_cmac_power { 0x000167b8 , 0x00000000 }, //ch1_cmac_cross_corr { 0x000167bc , 0x00000000 }, //ch1_cmac_i2q2 { 0x000167c0 , 0x000000a0 }, //ch1_cmac_power_hpf { 0x000167c4 , 0x000c0000 }, //ch1_rxdac_set1 { 0x000167c8 , 0x14021402 }, //ch1_rxdac_set2 { 0x000167cc , 0x00001402 }, //ch1_rxdac_long_shift { 0x000167d0 , 0x00000000 }, //ch1_cmac_results_i { 0x000167d4 , 0x00000000 }, //ch1_cmac_results_q }; static const u_int32_t ar9340_wasp_1p0_radio_core_40M[][2] = { { 0x0001609c , 0x02566f3a }, //synth8 (beta4 -> beta6 change) { 0x000160ac , 0xa4647c00 }, //synth12 (beta4 -> beta6 change) { 0x000160b0 , 0x01885f5a }, //synth13 (beta4 -> beta6 change) { 0x00008244 , 0x0010f400 }, // MAC_PCU_SLP32_MODE { 0x0000824c , 0x0001e800 }, // MAC_PCU_SLP32_INC }; static const u_int32_t ar9340Common_rx_gain_table_merlin_2p0[][2] = { /* Addr allmodes */ { 0x0000a000 , 0x02000101 }, { 0x0000a004 , 0x02000102 }, { 0x0000a008 , 0x02000103 }, { 0x0000a00c , 0x02000104 }, { 0x0000a010 , 0x02000200 }, { 0x0000a014 , 0x02000201 }, { 0x0000a018 , 0x02000202 }, { 0x0000a01c , 0x02000203 }, { 0x0000a020 , 0x02000204 }, { 0x0000a024 , 0x02000205 }, { 0x0000a028 , 0x02000208 }, { 0x0000a02c , 0x02000302 }, { 0x0000a030 , 0x02000303 }, { 0x0000a034 , 0x02000304 }, { 0x0000a038 , 0x02000400 }, { 0x0000a03c , 0x02010300 }, { 0x0000a040 , 0x02010301 }, { 0x0000a044 , 0x02010302 }, { 0x0000a048 , 0x02000500 }, { 0x0000a04c , 0x02010400 }, { 0x0000a050 , 0x02020300 }, { 0x0000a054 , 0x02020301 }, { 0x0000a058 , 0x02020302 }, { 0x0000a05c , 0x02020303 }, { 0x0000a060 , 0x02020400 }, { 0x0000a064 , 0x02030300 }, { 0x0000a068 , 0x02030301 }, { 0x0000a06c , 0x02030302 }, { 0x0000a070 , 0x02030303 }, { 0x0000a074 , 0x02030400 }, { 0x0000a078 , 0x02040300 }, { 0x0000a07c , 0x02040301 }, { 0x0000a080 , 0x02040302 }, { 0x0000a084 , 0x02040303 }, { 0x0000a088 , 0x02030500 }, { 0x0000a08c , 0x02040400 }, { 0x0000a090 , 0x02050203 }, { 0x0000a094 , 0x02050204 }, { 0x0000a098 , 0x02050205 }, { 0x0000a09c , 0x02040500 }, { 0x0000a0a0 , 0x02050301 }, { 0x0000a0a4 , 0x02050302 }, { 0x0000a0a8 , 0x02050303 }, { 0x0000a0ac , 0x02050400 }, { 0x0000a0b0 , 0x02050401 }, { 0x0000a0b4 , 0x02050402 }, { 0x0000a0b8 , 0x02050403 }, { 0x0000a0bc , 0x02050500 }, { 0x0000a0c0 , 0x02050501 }, { 0x0000a0c4 , 0x02050502 }, { 0x0000a0c8 , 0x02050503 }, { 0x0000a0cc , 0x02050504 }, { 0x0000a0d0 , 0x02050600 }, { 0x0000a0d4 , 0x02050601 }, { 0x0000a0d8 , 0x02050602 }, { 0x0000a0dc , 0x02050603 }, { 0x0000a0e0 , 0x02050604 }, { 0x0000a0e4 , 0x02050700 }, { 0x0000a0e8 , 0x02050701 }, { 0x0000a0ec , 0x02050702 }, { 0x0000a0f0 , 0x02050703 }, { 0x0000a0f4 , 0x02050704 }, { 0x0000a0f8 , 0x02050705 }, { 0x0000a0fc , 0x02050708 }, { 0x0000a100 , 0x02050709 }, { 0x0000a104 , 0x0205070a }, { 0x0000a108 , 0x0205070b }, { 0x0000a10c , 0x0205070c }, { 0x0000a110 , 0x0205070d }, { 0x0000a114 , 0x02050710 }, { 0x0000a118 , 0x02050711 }, { 0x0000a11c , 0x02050712 }, { 0x0000a120 , 0x02050713 }, { 0x0000a124 , 0x02050714 }, { 0x0000a128 , 0x02050715 }, { 0x0000a12c , 0x02050730 }, { 0x0000a130 , 0x02050731 }, { 0x0000a134 , 0x02050732 }, { 0x0000a138 , 0x02050733 }, { 0x0000a13c , 0x02050734 }, { 0x0000a140 , 0x02050735 }, { 0x0000a144 , 0x02050750 }, { 0x0000a148 , 0x02050751 }, { 0x0000a14c , 0x02050752 }, { 0x0000a150 , 0x02050753 }, { 0x0000a154 , 0x02050754 }, { 0x0000a158 , 0x02050755 }, { 0x0000a15c , 0x02050770 }, { 0x0000a160 , 0x02050771 }, { 0x0000a164 , 0x02050772 }, { 0x0000a168 , 0x02050773 }, { 0x0000a16c , 0x02050774 }, { 0x0000a170 , 0x02050775 }, { 0x0000a174 , 0x00000776 }, { 0x0000a178 , 0x00000776 }, { 0x0000a17c , 0x00000776 }, { 0x0000a180 , 0x00000776 }, { 0x0000a184 , 0x00000776 }, { 0x0000a188 , 0x00000776 }, { 0x0000a18c , 0x00000776 }, { 0x0000a190 , 0x00000776 }, { 0x0000a194 , 0x00000776 }, { 0x0000a198 , 0x00000776 }, { 0x0000a19c , 0x00000776 }, { 0x0000a1a0 , 0x00000776 }, { 0x0000a1a4 , 0x00000776 }, { 0x0000a1a8 , 0x00000776 }, { 0x0000a1ac , 0x00000776 }, { 0x0000a1b0 , 0x00000776 }, { 0x0000a1b4 , 0x00000776 }, { 0x0000a1b8 , 0x00000776 }, { 0x0000a1bc , 0x00000776 }, { 0x0000a1c0 , 0x00000776 }, { 0x0000a1c4 , 0x00000776 }, { 0x0000a1c8 , 0x00000776 }, { 0x0000a1cc , 0x00000776 }, { 0x0000a1d0 , 0x00000776 }, { 0x0000a1d4 , 0x00000776 }, { 0x0000a1d8 , 0x00000776 }, { 0x0000a1dc , 0x00000776 }, { 0x0000a1e0 , 0x00000776 }, { 0x0000a1e4 , 0x00000776 }, { 0x0000a1e8 , 0x00000776 }, { 0x0000a1ec , 0x00000776 }, { 0x0000a1f0 , 0x00000776 }, { 0x0000a1f4 , 0x00000776 }, { 0x0000a1f8 , 0x00000776 }, { 0x0000a1fc , 0x00000776 }, { 0x0000b000 , 0x02000101 }, { 0x0000b004 , 0x02000102 }, { 0x0000b008 , 0x02000103 }, { 0x0000b00c , 0x02000104 }, { 0x0000b010 , 0x02000200 }, { 0x0000b014 , 0x02000201 }, { 0x0000b018 , 0x02000202 }, { 0x0000b01c , 0x02000203 }, { 0x0000b020 , 0x02000204 }, { 0x0000b024 , 0x02000205 }, { 0x0000b028 , 0x02000208 }, { 0x0000b02c , 0x02000302 }, { 0x0000b030 , 0x02000303 }, { 0x0000b034 , 0x02000304 }, { 0x0000b038 , 0x02000400 }, { 0x0000b03c , 0x02010300 }, { 0x0000b040 , 0x02010301 }, { 0x0000b044 , 0x02010302 }, { 0x0000b048 , 0x02000500 }, { 0x0000b04c , 0x02010400 }, { 0x0000b050 , 0x02020300 }, { 0x0000b054 , 0x02020301 }, { 0x0000b058 , 0x02020302 }, { 0x0000b05c , 0x02020303 }, { 0x0000b060 , 0x02020400 }, { 0x0000b064 , 0x02030300 }, { 0x0000b068 , 0x02030301 }, { 0x0000b06c , 0x02030302 }, { 0x0000b070 , 0x02030303 }, { 0x0000b074 , 0x02030400 }, { 0x0000b078 , 0x02040300 }, { 0x0000b07c , 0x02040301 }, { 0x0000b080 , 0x02040302 }, { 0x0000b084 , 0x02040303 }, { 0x0000b088 , 0x02030500 }, { 0x0000b08c , 0x02040400 }, { 0x0000b090 , 0x02050203 }, { 0x0000b094 , 0x02050204 }, { 0x0000b098 , 0x02050205 }, { 0x0000b09c , 0x02040500 }, { 0x0000b0a0 , 0x02050301 }, { 0x0000b0a4 , 0x02050302 }, { 0x0000b0a8 , 0x02050303 }, { 0x0000b0ac , 0x02050400 }, { 0x0000b0b0 , 0x02050401 }, { 0x0000b0b4 , 0x02050402 }, { 0x0000b0b8 , 0x02050403 }, { 0x0000b0bc , 0x02050500 }, { 0x0000b0c0 , 0x02050501 }, { 0x0000b0c4 , 0x02050502 }, { 0x0000b0c8 , 0x02050503 }, { 0x0000b0cc , 0x02050504 }, { 0x0000b0d0 , 0x02050600 }, { 0x0000b0d4 , 0x02050601 }, { 0x0000b0d8 , 0x02050602 }, { 0x0000b0dc , 0x02050603 }, { 0x0000b0e0 , 0x02050604 }, { 0x0000b0e4 , 0x02050700 }, { 0x0000b0e8 , 0x02050701 }, { 0x0000b0ec , 0x02050702 }, { 0x0000b0f0 , 0x02050703 }, { 0x0000b0f4 , 0x02050704 }, { 0x0000b0f8 , 0x02050705 }, { 0x0000b0fc , 0x02050708 }, { 0x0000b100 , 0x02050709 }, { 0x0000b104 , 0x0205070a }, { 0x0000b108 , 0x0205070b }, { 0x0000b10c , 0x0205070c }, { 0x0000b110 , 0x0205070d }, { 0x0000b114 , 0x02050710 }, { 0x0000b118 , 0x02050711 }, { 0x0000b11c , 0x02050712 }, { 0x0000b120 , 0x02050713 }, { 0x0000b124 , 0x02050714 }, { 0x0000b128 , 0x02050715 }, { 0x0000b12c , 0x02050730 }, { 0x0000b130 , 0x02050731 }, { 0x0000b134 , 0x02050732 }, { 0x0000b138 , 0x02050733 }, { 0x0000b13c , 0x02050734 }, { 0x0000b140 , 0x02050735 }, { 0x0000b144 , 0x02050750 }, { 0x0000b148 , 0x02050751 }, { 0x0000b14c , 0x02050752 }, { 0x0000b150 , 0x02050753 }, { 0x0000b154 , 0x02050754 }, { 0x0000b158 , 0x02050755 }, { 0x0000b15c , 0x02050770 }, { 0x0000b160 , 0x02050771 }, { 0x0000b164 , 0x02050772 }, { 0x0000b168 , 0x02050773 }, { 0x0000b16c , 0x02050774 }, { 0x0000b170 , 0x02050775 }, { 0x0000b174 , 0x00000776 }, { 0x0000b178 , 0x00000776 }, { 0x0000b17c , 0x00000776 }, { 0x0000b180 , 0x00000776 }, { 0x0000b184 , 0x00000776 }, { 0x0000b188 , 0x00000776 }, { 0x0000b18c , 0x00000776 }, { 0x0000b190 , 0x00000776 }, { 0x0000b194 , 0x00000776 }, { 0x0000b198 , 0x00000776 }, { 0x0000b19c , 0x00000776 }, { 0x0000b1a0 , 0x00000776 }, { 0x0000b1a4 , 0x00000776 }, { 0x0000b1a8 , 0x00000776 }, { 0x0000b1ac , 0x00000776 }, { 0x0000b1b0 , 0x00000776 }, { 0x0000b1b4 , 0x00000776 }, { 0x0000b1b8 , 0x00000776 }, { 0x0000b1bc , 0x00000776 }, { 0x0000b1c0 , 0x00000776 }, { 0x0000b1c4 , 0x00000776 }, { 0x0000b1c8 , 0x00000776 }, { 0x0000b1cc , 0x00000776 }, { 0x0000b1d0 , 0x00000776 }, { 0x0000b1d4 , 0x00000776 }, { 0x0000b1d8 , 0x00000776 }, { 0x0000b1dc , 0x00000776 }, { 0x0000b1e0 , 0x00000776 }, { 0x0000b1e4 , 0x00000776 }, { 0x0000b1e8 , 0x00000776 }, { 0x0000b1ec , 0x00000776 }, { 0x0000b1f0 , 0x00000776 }, { 0x0000b1f4 , 0x00000776 }, { 0x0000b1f8 , 0x00000776 }, { 0x0000b1fc , 0x00000776 }, }; static const u_int32_t ar9340_wasp_1p0_mac_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x00001030 , 0x00000230 , 0x00000460 , 0x000002c0 , 0x00000160 }, { 0x00001070 , 0x00000168 , 0x000002d0 , 0x00000318 , 0x0000018c }, { 0x000010b0 , 0x00000e60 , 0x00001cc0 , 0x00007c70 , 0x00003e38 }, { 0x00008014 , 0x03e803e8 , 0x07d007d0 , 0x10801600 , 0x08400b00 }, { 0x0000801c , 0x128d8027 , 0x128d804f , 0x12e00057 , 0x12e0002b }, - { 0x00008120 , 0x08f04800 , 0x08f04800 , 0x08f04810 , 0x08f04810 }, + { 0x00008120 , 0x18f04800 , 0x18f04800 , 0x18f04810 , 0x18f04810 }, { 0x000081d0 , 0x00003210 , 0x00003210 , 0x0000320a , 0x0000320a }, { 0x00008318 , 0x00003e80 , 0x00007d00 , 0x00006880 , 0x00003440 }, }; static const u_int32_t ar9340_wasp_1p0_soc_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x00007010 , 0x00000023 , 0x00000023 , 0x00000023 , 0x00000023 }, }; static const u_int32_t ar9340_merlin_2p0_radio_core[][2] = { /* Addr common */ { 0x00007800 , 0x00040000 }, { 0x00007804 , 0xdb005012 }, { 0x00007808 , 0x04924914 }, { 0x0000780c , 0x21084210 }, { 0x00007810 , 0x6d801300 }, { 0x00007814 , 0x0019beff }, { 0x00007818 , 0x07e41000 }, { 0x0000781c , 0x00392000 }, { 0x00007820 , 0x92592480 }, { 0x00007824 , 0x00040000 }, { 0x00007828 , 0xdb005012 }, { 0x0000782c , 0x04924914 }, { 0x00007830 , 0x21084210 }, { 0x00007834 , 0x6d801300 }, { 0x00007838 , 0x0019beff }, { 0x0000783c , 0x07e40000 }, { 0x00007840 , 0x00392000 }, { 0x00007844 , 0x92592480 }, { 0x00007848 , 0x00100000 }, { 0x0000784c , 0x773f0567 }, { 0x00007850 , 0x54214514 }, { 0x00007854 , 0x12035828 }, { 0x00007858 , 0x92592692 }, { 0x0000785c , 0x00000000 }, { 0x00007860 , 0x56400000 }, { 0x00007864 , 0x0a8e370e }, { 0x00007868 , 0xc0102850 }, { 0x0000786c , 0x812d4000 }, { 0x00007870 , 0x807ec400 }, { 0x00007874 , 0x001b6db0 }, { 0x00007878 , 0x00376b63 }, { 0x0000787c , 0x06db6db6 }, { 0x00007880 , 0x006d8000 }, { 0x00007884 , 0xffeffffe }, { 0x00007888 , 0xffeffffe }, { 0x0000788c , 0x00010000 }, { 0x00007890 , 0x02060aeb }, { 0x00007894 , 0x5a108000 }, }; static const u_int32_t ar9340_wasp_1p0_baseband_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x00009810 , 0xd00a8005 , 0xd00a8005 , 0xd00a8011 , 0xd00a8011 }, { 0x00009820 , 0x206a022e , 0x206a022e , 0x206a022e , 0x206a022e }, { 0x00009824 , 0x5ac640d0 , 0x5ac640d0 , 0x5ac640d0 , 0x5ac640d0 }, { 0x00009828 , 0x06903081 , 0x06903081 , 0x09103881 , 0x09103881 }, { 0x0000982c , 0x05eea6d4 , 0x05eea6d4 , 0x05eea6d4 , 0x05eea6d4 }, { 0x00009830 , 0x0000059c , 0x0000059c , 0x0000119c , 0x0000119c }, { 0x00009c00 , 0x000000c4 , 0x000000c4 , 0x000000c4 , 0x000000c4 }, { 0x00009e00 , 0x0372111a , 0x0372111a , 0x037216a0 , 0x037216a0 }, { 0x00009e04 , 0x001c2020 , 0x001c2020 , 0x001c2020 , 0x001c2020 }, { 0x00009e0c , 0x6c4000e2 , 0x6d4000e2 , 0x6d4000e2 , 0x6c4000e2 }, { 0x00009e10 , 0x7ec88d2e , 0x7ec88d2e , 0x7ec88d2e , 0x7ec88d2e }, { 0x00009e14 , 0x37b95d5e , 0x37b9605e , 0x3379605e , 0x33795d5e }, { 0x00009e18 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, - { 0x00009e1c , 0x0001cf9c , 0x0001cf9c , 0x00021f9c , 0x00021f9c }, + { 0x00009e1c , 0x0001c59c , 0x0001c59c , 0x0002159c , 0x0002159c }, { 0x00009e20 , 0x000003b5 , 0x000003b5 , 0x000003ce , 0x000003ce }, { 0x00009e2c , 0x0000001c , 0x0000001c , 0x00000021 , 0x00000021 }, { 0x00009e3c , 0xcf946220 , 0xcf946220 , 0xcf946222 , 0xcf946222 }, { 0x00009e44 , 0x02321e27 , 0x02321e27 , 0x02291e27 , 0x02291e27 }, { 0x00009e48 , 0x5030201a , 0x5030201a , 0x50302012 , 0x50302012 }, { 0x00009fc8 , 0x0003f000 , 0x0003f000 , 0x0001a000 , 0x0001a000 }, { 0x0000a204 , 0x00003ec0 , 0x00003ec4 , 0x00003ec4 , 0x00003ec0 }, { 0x0000a208 , 0x00000104 , 0x00000104 , 0x00000004 , 0x00000004 }, { 0x0000a22c , 0x07e26a2f , 0x07e26a2f , 0x01026a2f , 0x01026a2f }, { 0x0000a230 , 0x0000000a , 0x00000014 , 0x00000016 , 0x0000000b }, { 0x0000a234 , 0x00000fff , 0x10000fff , 0x10000fff , 0x00000fff }, { 0x0000a238 , 0xffb81018 , 0xffb81018 , 0xffb81018 , 0xffb81018 }, { 0x0000a250 , 0x00000000 , 0x00000000 , 0x00000210 , 0x00000108 }, { 0x0000a254 , 0x000007d0 , 0x00000fa0 , 0x00001130 , 0x00000898 }, { 0x0000a258 , 0x02020002 , 0x02020002 , 0x02020002 , 0x02020002 }, { 0x0000a25c , 0x01000e0e , 0x01000e0e , 0x01000e0e , 0x01000e0e }, { 0x0000a260 , 0x0a021501 , 0x0a021501 , 0x3a021501 , 0x3a021501 }, { 0x0000a264 , 0x00000e0e , 0x00000e0e , 0x00000e0e , 0x00000e0e }, { 0x0000a280 , 0x00000007 , 0x00000007 , 0x0000000b , 0x0000000b }, { 0x0000a284 , 0x00000000 , 0x00000000 , 0x00000150 , 0x00000150 }, { 0x0000a288 , 0x00000220 , 0x00000220 , 0x00000110 , 0x00000110 }, { 0x0000a28c , 0x00011111 , 0x00011111 , 0x00022222 , 0x00022222 }, { 0x0000a2c4 , 0x00158d18 , 0x00158d18 , 0x00158d18 , 0x00158d18 }, { 0x0000a2d0 , 0x00041983 , 0x00041983 , 0x00041982 , 0x00041982 }, { 0x0000a2d8 , 0x7999a83a , 0x7999a83a , 0x7999a83a , 0x7999a83a }, { 0x0000a358 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a830 , 0x0000019c , 0x0000019c , 0x0000019c , 0x0000019c }, { 0x0000ae04 , 0x001c0000 , 0x001c0000 , 0x001c0000 , 0x001c0000 }, { 0x0000ae18 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000ae1c , 0x0000019c , 0x0000019c , 0x0000019c , 0x0000019c }, { 0x0000ae20 , 0x000001b5 , 0x000001b5 , 0x000001ce , 0x000001ce }, { 0x0000b284 , 0x00000000 , 0x00000000 , 0x00000150 , 0x00000150 }, }; static const u_int32_t ar9340_wasp_1p0_baseband_core[][2] = { /* Addr allmodes */ { 0x00009800 , 0xafe68e30 }, { 0x00009804 , 0xfd14e000 }, { 0x00009808 , 0x9c0a9f6b }, { 0x0000980c , 0x04900000 }, { 0x00009814 , 0x3280c00a }, { 0x00009818 , 0x00000000 }, { 0x0000981c , 0x00020028 }, { 0x00009834 , 0x6400a190 }, { 0x00009838 , 0x0108ecff }, { 0x0000983c , 0x14000600 }, { 0x00009880 , 0x201fff00 }, { 0x00009884 , 0x00001042 }, { 0x000098a4 , 0x00200400 }, { 0x000098b0 , 0x32840bbe }, { 0x000098d0 , 0x004b6a8e }, { 0x000098d4 , 0x00000820 }, { 0x000098dc , 0x00000000 }, { 0x000098f0 , 0x00000000 }, { 0x000098f4 , 0x00000000 }, { 0x00009c04 , 0xff55ff55 }, { 0x00009c08 , 0x0320ff55 }, { 0x00009c0c , 0x00000000 }, { 0x00009c10 , 0x00000000 }, { 0x00009c14 , 0x00046384 }, { 0x00009c18 , 0x05b6b440 }, { 0x00009c1c , 0x00b6b440 }, { 0x00009d00 , 0xc080a333 }, { 0x00009d04 , 0x40206c10 }, { 0x00009d08 , 0x009c4060 }, { 0x00009d0c , 0x9883800a }, { 0x00009d10 , 0x01834061 }, { 0x00009d14 , 0x00c0040b }, { 0x00009d18 , 0x00000000 }, { 0x00009e08 , 0x0038230c }, { 0x00009e24 , 0x990bb515 }, { 0x00009e28 , 0x0c6f0000 }, { 0x00009e30 , 0x06336f77 }, { 0x00009e34 , 0x6af6532f }, { 0x00009e38 , 0x0cc80c00 }, { 0x00009e40 , 0x0d261820 }, { 0x00009e4c , 0x00001004 }, { 0x00009e50 , 0x00ff03f1 }, { 0x00009e54 , 0x00000000 }, { 0x00009fc0 , 0x803e4788 }, { 0x00009fc4 , 0x0001efb5 }, { 0x00009fcc , 0x40000014 }, { 0x00009fd0 , 0x01193b93 }, { 0x0000a20c , 0x00000000 }, { 0x0000a220 , 0x00000000 }, { 0x0000a224 , 0x00000000 }, { 0x0000a228 , 0x10002310 }, { 0x0000a23c , 0x00000000 }, { 0x0000a244 , 0x0c000000 }, { 0x0000a2a0 , 0x00000001 }, { 0x0000a2c0 , 0x00000001 }, { 0x0000a2c8 , 0x00000000 }, { 0x0000a2cc , 0x18c43433 }, { 0x0000a2d4 , 0x00000000 }, { 0x0000a2ec , 0x00000000 }, { 0x0000a2f0 , 0x00000000 }, { 0x0000a2f4 , 0x00000000 }, { 0x0000a2f8 , 0x00000000 }, { 0x0000a344 , 0x00000000 }, { 0x0000a34c , 0x00000000 }, { 0x0000a350 , 0x0000a000 }, { 0x0000a364 , 0x00000000 }, { 0x0000a370 , 0x00000000 }, { 0x0000a390 , 0x00000001 }, { 0x0000a394 , 0x00000444 }, { 0x0000a398 , 0x00000000 }, { 0x0000a39c , 0x210d0401 }, { 0x0000a3a0 , 0xab9a7144 }, { 0x0000a3a4 , 0x00000000 }, { 0x0000a3a8 , 0xaaaaaaaa }, { 0x0000a3ac , 0x3c466478 }, { 0x0000a3c0 , 0x20202020 }, { 0x0000a3c4 , 0x22222220 }, { 0x0000a3c8 , 0x20200020 }, { 0x0000a3cc , 0x20202020 }, { 0x0000a3d0 , 0x20202020 }, { 0x0000a3d4 , 0x20202020 }, { 0x0000a3d8 , 0x20202020 }, { 0x0000a3dc , 0x20202020 }, { 0x0000a3e0 , 0x20202020 }, { 0x0000a3e4 , 0x20202020 }, { 0x0000a3e8 , 0x20202020 }, { 0x0000a3ec , 0x20202020 }, { 0x0000a3f0 , 0x00000000 }, { 0x0000a3f4 , 0x00000000 }, { 0x0000a3f8 , 0x0cdbd380 }, { 0x0000a3fc , 0x000f0f01 }, { 0x0000a400 , 0x8fa91f01 }, { 0x0000a404 , 0x00000000 }, { 0x0000a408 , 0x0e79e5c6 }, { 0x0000a40c , 0x00820820 }, { 0x0000a414 , 0x1ce739ce }, { 0x0000a418 , 0x2d001dce }, { 0x0000a41c , 0x1ce739ce }, { 0x0000a420 , 0x000001ce }, { 0x0000a424 , 0x1ce739ce }, { 0x0000a428 , 0x000001ce }, { 0x0000a42c , 0x1ce739ce }, { 0x0000a430 , 0x1ce739ce }, { 0x0000a434 , 0x00000000 }, { 0x0000a438 , 0x00001801 }, { 0x0000a43c , 0x00100000 }, { 0x0000a440 , 0x00000000 }, { 0x0000a444 , 0x00000000 }, { 0x0000a448 , 0x05000080 }, { 0x0000a44c , 0x00000001 }, { 0x0000a450 , 0x00010000 }, { 0x0000a458 , 0x00000000 }, { 0x0000a640 , 0x00000000 }, { 0x0000a644 , 0x3fad9d74 }, { 0x0000a648 , 0x0048060a }, { 0x0000a64c , 0x00003c37 }, { 0x0000a670 , 0x03020100 }, { 0x0000a674 , 0x09080504 }, { 0x0000a678 , 0x0d0c0b0a }, { 0x0000a67c , 0x13121110 }, { 0x0000a680 , 0x31301514 }, { 0x0000a684 , 0x35343332 }, { 0x0000a688 , 0x00000036 }, { 0x0000a690 , 0x00000838 }, { 0x0000a7c0 , 0x00000000 }, { 0x0000a7c4 , 0xfffffffc }, { 0x0000a7c8 , 0x00000000 }, { 0x0000a7cc , 0x00000000 }, { 0x0000a7d0 , 0x00000000 }, { 0x0000a7d4 , 0x00000004 }, { 0x0000a7dc , 0x00000000 }, { 0x0000a8d0 , 0x004b6a8e }, { 0x0000a8d4 , 0x00000820 }, { 0x0000a8dc , 0x00000000 }, { 0x0000a8f0 , 0x00000000 }, { 0x0000a8f4 , 0x00000000 }, { 0x0000b2d0 , 0x00000080 }, { 0x0000b2d4 , 0x00000000 }, { 0x0000b2ec , 0x00000000 }, { 0x0000b2f0 , 0x00000000 }, { 0x0000b2f4 , 0x00000000 }, { 0x0000b2f8 , 0x00000000 }, { 0x0000b408 , 0x0e79e5c0 }, { 0x0000b40c , 0x00820820 }, { 0x0000b420 , 0x00000000 }, }; static const u_int32_t ar9340_wasp_1p0_baseband_postamble_dfs_channel[][3] = { /* Addr 5G_HT20 5G_HT40 */ { 0x00009824 , 0x5ac668d0 , 0x5ac668d0 }, + { 0x00009828 , 0x06903080 , 0x06903080 }, + { 0x00009e0c , 0x6d4000e2 , 0x6d4000e2 }, { 0x00009e14 , 0x37b9625e , 0x37b9625e }, + + { 0x00009814 , 0x3400c00f , 0x3400c00f }, }; static const u_int32_t ar9340_wasp_1p0_mac_postamble_emulation[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x00008014 , 0x10f810f8 , 0x10f810f8 , 0x10f810f8 , 0x10f810f8 }, { 0x0000801c , 0x0e8d8017 , 0x0e8d8017 , 0x0e8d8017 , 0x0e8d8017 }, }; static const u_int32_t ar9340Modes_high_power_tx_gain_table_wasp_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x0000a2dc , 0x0380c7fc , 0x0380c7fc , 0x03aaa352 , 0x03aaa352 }, { 0x0000a2e0 , 0x0000f800 , 0x0000f800 , 0x03ccc584 , 0x03ccc584 }, { 0x0000a2e4 , 0x03ff0000 , 0x03ff0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000a2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000a600 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a604 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a608 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a60c , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a610 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a614 , 0x01404000 , 0x01404000 , 0x01404000 , 0x01404000 }, { 0x0000a618 , 0x01404501 , 0x01404501 , 0x01404501 , 0x01404501 }, { 0x0000a61c , 0x02008802 , 0x02008802 , 0x02008501 , 0x02008501 }, { 0x0000a620 , 0x0300cc03 , 0x0300cc03 , 0x0280ca03 , 0x0280ca03 }, { 0x0000a624 , 0x0300cc03 , 0x0300cc03 , 0x03010c04 , 0x03010c04 }, { 0x0000a628 , 0x0300cc03 , 0x0300cc03 , 0x04014c04 , 0x04014c04 }, { 0x0000a62c , 0x03810c03 , 0x03810c03 , 0x04015005 , 0x04015005 }, { 0x0000a630 , 0x03810e04 , 0x03810e04 , 0x04015005 , 0x04015005 }, { 0x0000a634 , 0x03810e04 , 0x03810e04 , 0x04015005 , 0x04015005 }, { 0x0000a638 , 0x03810e04 , 0x03810e04 , 0x04015005 , 0x04015005 }, { 0x0000a63c , 0x03810e04 , 0x03810e04 , 0x04015005 , 0x04015005 }, { 0x0000b2dc , 0x0380c7fc , 0x0380c7fc , 0x03aaa352 , 0x03aaa352 }, { 0x0000b2e0 , 0x0000f800 , 0x0000f800 , 0x03ccc584 , 0x03ccc584 }, { 0x0000b2e4 , 0x03ff0000 , 0x03ff0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000b2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000a410 , 0x000050d8 , 0x000050d8 , 0x000050d9 , 0x000050d9 }, { 0x0000a500 , 0x00002220 , 0x00002220 , 0x00000000 , 0x00000000 }, { 0x0000a504 , 0x04002222 , 0x04002222 , 0x02000001 , 0x02000001 }, { 0x0000a508 , 0x09002421 , 0x09002421 , 0x05000003 , 0x05000003 }, { 0x0000a50c , 0x0d002621 , 0x0d002621 , 0x0a000005 , 0x0a000005 }, { 0x0000a510 , 0x13004620 , 0x13004620 , 0x0e000201 , 0x0e000201 }, { 0x0000a514 , 0x19004a20 , 0x19004a20 , 0x11000203 , 0x11000203 }, { 0x0000a518 , 0x1d004e20 , 0x1d004e20 , 0x14000401 , 0x14000401 }, { 0x0000a51c , 0x21005420 , 0x21005420 , 0x18000403 , 0x18000403 }, { 0x0000a520 , 0x26005e20 , 0x26005e20 , 0x1b000602 , 0x1b000602 }, { 0x0000a524 , 0x2b005e40 , 0x2b005e40 , 0x1f000802 , 0x1f000802 }, { 0x0000a528 , 0x2f005e42 , 0x2f005e42 , 0x21000620 , 0x21000620 }, { 0x0000a52c , 0x33005e44 , 0x33005e44 , 0x25000820 , 0x25000820 }, { 0x0000a530 , 0x38005e65 , 0x38005e65 , 0x29000822 , 0x29000822 }, { 0x0000a534 , 0x3c005e69 , 0x3c005e69 , 0x2d000824 , 0x2d000824 }, { 0x0000a538 , 0x40005e6b , 0x40005e6b , 0x30000828 , 0x30000828 }, { 0x0000a53c , 0x44005e6d , 0x44005e6d , 0x3400082a , 0x3400082a }, { 0x0000a540 , 0x49005e72 , 0x49005e72 , 0x38000849 , 0x38000849 }, { 0x0000a544 , 0x4e005eb2 , 0x4e005eb2 , 0x3b000a2c , 0x3b000a2c }, { 0x0000a548 , 0x53005f12 , 0x53005f12 , 0x3e000e2b , 0x3e000e2b }, { 0x0000a54c , 0x59025eb5 , 0x59025eb5 , 0x42000e2d , 0x42000e2d }, { 0x0000a550 , 0x5e025f12 , 0x5e025f12 , 0x4500124a , 0x4500124a }, { 0x0000a554 , 0x61027f12 , 0x61027f12 , 0x4900124c , 0x4900124c }, { 0x0000a558 , 0x6702bf12 , 0x6702bf12 , 0x4c00126c , 0x4c00126c }, { 0x0000a55c , 0x6b02bf14 , 0x6b02bf14 , 0x4f00128c , 0x4f00128c }, { 0x0000a560 , 0x6f02bf16 , 0x6f02bf16 , 0x52001290 , 0x52001290 }, { 0x0000a564 , 0x6f02bf16 , 0x6f02bf16 , 0x56001292 , 0x56001292 }, { 0x0000a568 , 0x6f02bf16 , 0x6f02bf16 , 0x56001292 , 0x56001292 }, { 0x0000a56c , 0x6f02bf16 , 0x6f02bf16 , 0x56001292 , 0x56001292 }, { 0x0000a570 , 0x6f02bf16 , 0x6f02bf16 , 0x56001292 , 0x56001292 }, { 0x0000a574 , 0x6f02bf16 , 0x6f02bf16 , 0x56001292 , 0x56001292 }, { 0x0000a578 , 0x6f02bf16 , 0x6f02bf16 , 0x56001292 , 0x56001292 }, { 0x0000a57c , 0x6f02bf16 , 0x6f02bf16 , 0x56001292 , 0x56001292 }, { 0x0000a580 , 0x00802220 , 0x00802220 , 0x00800000 , 0x00800000 }, { 0x0000a584 , 0x04802222 , 0x04802222 , 0x02800001 , 0x02800001 }, { 0x0000a588 , 0x09802421 , 0x09802421 , 0x05800003 , 0x05800003 }, { 0x0000a58c , 0x0d802621 , 0x0d802621 , 0x0a800005 , 0x0a800005 }, { 0x0000a590 , 0x13804620 , 0x13804620 , 0x0e800201 , 0x0e800201 }, { 0x0000a594 , 0x19804a20 , 0x19804a20 , 0x11800203 , 0x11800203 }, { 0x0000a598 , 0x1d804e20 , 0x1d804e20 , 0x14800401 , 0x14800401 }, { 0x0000a59c , 0x21805420 , 0x21805420 , 0x18800403 , 0x18800403 }, { 0x0000a5a0 , 0x26805e20 , 0x26805e20 , 0x1b800602 , 0x1b800602 }, { 0x0000a5a4 , 0x2b805e40 , 0x2b805e40 , 0x1f800802 , 0x1f800802 }, { 0x0000a5a8 , 0x2f805e42 , 0x2f805e42 , 0x21800620 , 0x21800620 }, { 0x0000a5ac , 0x33805e44 , 0x33805e44 , 0x25800820 , 0x25800820 }, { 0x0000a5b0 , 0x38805e65 , 0x38805e65 , 0x29800822 , 0x29800822 }, { 0x0000a5b4 , 0x3c805e69 , 0x3c805e69 , 0x2d800824 , 0x2d800824 }, { 0x0000a5b8 , 0x40805e6b , 0x40805e6b , 0x30800828 , 0x30800828 }, { 0x0000a5bc , 0x44805e6d , 0x44805e6d , 0x3480082a , 0x3480082a }, { 0x0000a5c0 , 0x49805e72 , 0x49805e72 , 0x38800849 , 0x38800849 }, { 0x0000a5c4 , 0x4e805eb2 , 0x4e805eb2 , 0x3b800a2c , 0x3b800a2c }, { 0x0000a5c8 , 0x53805f12 , 0x53805f12 , 0x3e800e2b , 0x3e800e2b }, { 0x0000a5cc , 0x59825eb2 , 0x59825eb2 , 0x42800e2d , 0x42800e2d }, { 0x0000a5d0 , 0x5e825f12 , 0x5e825f12 , 0x4580124a , 0x4580124a }, { 0x0000a5d4 , 0x61827f12 , 0x61827f12 , 0x4980124c , 0x4980124c }, { 0x0000a5d8 , 0x6782bf12 , 0x6782bf12 , 0x4c80126c , 0x4c80126c }, { 0x0000a5dc , 0x6b82bf14 , 0x6b82bf14 , 0x4f80128c , 0x4f80128c }, { 0x0000a5e0 , 0x6f82bf16 , 0x6f82bf16 , 0x52801290 , 0x52801290 }, { 0x0000a5e4 , 0x6f82bf16 , 0x6f82bf16 , 0x56801292 , 0x56801292 }, { 0x0000a5e8 , 0x6f82bf16 , 0x6f82bf16 , 0x56801292 , 0x56801292 }, { 0x0000a5ec , 0x6f82bf16 , 0x6f82bf16 , 0x56801292 , 0x56801292 }, { 0x0000a5f0 , 0x6f82bf16 , 0x6f82bf16 , 0x56801292 , 0x56801292 }, { 0x0000a5f4 , 0x6f82bf16 , 0x6f82bf16 , 0x56801292 , 0x56801292 }, { 0x0000a5f8 , 0x6f82bf16 , 0x6f82bf16 , 0x56801292 , 0x56801292 }, { 0x0000a5fc , 0x6f82bf16 , 0x6f82bf16 , 0x56801292 , 0x56801292 }, { 0x00016044 , 0x056db2db , 0x056db2db , 0x022492db , 0x022492db }, { 0x00016048 , 0x24925266 , 0x24925266 , 0x24925266 , 0x24925266 }, { 0x00016444 , 0x056db2db , 0x056db2db , 0x022492db , 0x022492db }, { 0x00016448 , 0x24925266 , 0x24925266 , 0x24925266 , 0x24925266 }, }; static const u_int32_t ar9340Modes_high_ob_db_tx_gain_table_wasp_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x0000a2dc , 0x01feee00 , 0x01feee00 , 0x03aaa352 , 0x03aaa352 }, { 0x0000a2e0 , 0x0000f000 , 0x0000f000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000a2e4 , 0x01ff0000 , 0x01ff0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000a2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000a410 , 0x000050d8 , 0x000050d8 , 0x000050d9 , 0x000050d9 }, { 0x0000a500 , 0x00002220 , 0x00002220 , 0x00000000 , 0x00000000 }, { 0x0000a504 , 0x04002222 , 0x04002222 , 0x04000002 , 0x04000002 }, { 0x0000a508 , 0x09002421 , 0x09002421 , 0x08000004 , 0x08000004 }, { 0x0000a50c , 0x0d002621 , 0x0d002621 , 0x0b000200 , 0x0b000200 }, { 0x0000a510 , 0x13004620 , 0x13004620 , 0x0f000202 , 0x0f000202 }, { 0x0000a514 , 0x19004a20 , 0x19004a20 , 0x11000400 , 0x11000400 }, { 0x0000a518 , 0x1d004e20 , 0x1d004e20 , 0x15000402 , 0x15000402 }, { 0x0000a51c , 0x21005420 , 0x21005420 , 0x19000404 , 0x19000404 }, { 0x0000a520 , 0x26005e20 , 0x26005e20 , 0x1b000603 , 0x1b000603 }, { 0x0000a524 , 0x2b005e40 , 0x2b005e40 , 0x1f000a02 , 0x1f000a02 }, { 0x0000a528 , 0x2f005e42 , 0x2f005e42 , 0x23000a04 , 0x23000a04 }, { 0x0000a52c , 0x33005e44 , 0x33005e44 , 0x26000a20 , 0x26000a20 }, { 0x0000a530 , 0x38005e65 , 0x38005e65 , 0x2a000e20 , 0x2a000e20 }, { 0x0000a534 , 0x3c005e69 , 0x3c005e69 , 0x2e000e22 , 0x2e000e22 }, { 0x0000a538 , 0x40005e6b , 0x40005e6b , 0x31000e24 , 0x31000e24 }, { 0x0000a53c , 0x44005e6d , 0x44005e6d , 0x34001640 , 0x34001640 }, { 0x0000a540 , 0x49005e72 , 0x49005e72 , 0x38001660 , 0x38001660 }, { 0x0000a544 , 0x4e005eb2 , 0x4e005eb2 , 0x3b001861 , 0x3b001861 }, { 0x0000a548 , 0x53005f12 , 0x53005f12 , 0x3e001a81 , 0x3e001a81 }, { 0x0000a54c , 0x59025eb2 , 0x59025eb2 , 0x42001a83 , 0x42001a83 }, { 0x0000a550 , 0x5e025f12 , 0x5e025f12 , 0x44001c84 , 0x44001c84 }, { 0x0000a554 , 0x61027f12 , 0x61027f12 , 0x48001ce3 , 0x48001ce3 }, { 0x0000a558 , 0x6702bf12 , 0x6702bf12 , 0x4c001ce5 , 0x4c001ce5 }, { 0x0000a55c , 0x6b02bf14 , 0x6b02bf14 , 0x50001ce9 , 0x50001ce9 }, { 0x0000a560 , 0x6f02bf16 , 0x6f02bf16 , 0x54001ceb , 0x54001ceb }, { 0x0000a564 , 0x6f02bf16 , 0x6f02bf16 , 0x56001eec , 0x56001eec }, { 0x0000a568 , 0x6f02bf16 , 0x6f02bf16 , 0x56001eec , 0x56001eec }, { 0x0000a56c , 0x6f02bf16 , 0x6f02bf16 , 0x56001eec , 0x56001eec }, { 0x0000a570 , 0x6f02bf16 , 0x6f02bf16 , 0x56001eec , 0x56001eec }, { 0x0000a574 , 0x6f02bf16 , 0x6f02bf16 , 0x56001eec , 0x56001eec }, { 0x0000a578 , 0x6f02bf16 , 0x6f02bf16 , 0x56001eec , 0x56001eec }, { 0x0000a57c , 0x6f02bf16 , 0x6f02bf16 , 0x56001eec , 0x56001eec }, { 0x0000a580 , 0x00802220 , 0x00802220 , 0x00800000 , 0x00800000 }, { 0x0000a584 , 0x04802222 , 0x04802222 , 0x04800002 , 0x04800002 }, { 0x0000a588 , 0x09802421 , 0x09802421 , 0x08800004 , 0x08800004 }, { 0x0000a58c , 0x0d802621 , 0x0d802621 , 0x0b800200 , 0x0b800200 }, { 0x0000a590 , 0x13804620 , 0x13804620 , 0x0f800202 , 0x0f800202 }, { 0x0000a594 , 0x19804a20 , 0x19804a20 , 0x11800400 , 0x11800400 }, { 0x0000a598 , 0x1d804e20 , 0x1d804e20 , 0x15800402 , 0x15800402 }, { 0x0000a59c , 0x21805420 , 0x21805420 , 0x19800404 , 0x19800404 }, { 0x0000a5a0 , 0x26805e20 , 0x26805e20 , 0x1b800603 , 0x1b800603 }, { 0x0000a5a4 , 0x2b805e40 , 0x2b805e40 , 0x1f800a02 , 0x1f800a02 }, { 0x0000a5a8 , 0x2f805e42 , 0x2f805e42 , 0x23800a04 , 0x23800a04 }, { 0x0000a5ac , 0x33805e44 , 0x33805e44 , 0x26800a20 , 0x26800a20 }, { 0x0000a5b0 , 0x38805e65 , 0x38805e65 , 0x2a800e20 , 0x2a800e20 }, { 0x0000a5b4 , 0x3c805e69 , 0x3c805e69 , 0x2e800e22 , 0x2e800e22 }, { 0x0000a5b8 , 0x40805e6b , 0x40805e6b , 0x31800e24 , 0x31800e24 }, { 0x0000a5bc , 0x44805e6d , 0x44805e6d , 0x34801640 , 0x34801640 }, { 0x0000a5c0 , 0x49805e72 , 0x49805e72 , 0x38801660 , 0x38801660 }, { 0x0000a5c4 , 0x4e805eb2 , 0x4e805eb2 , 0x3b801861 , 0x3b801861 }, { 0x0000a5c8 , 0x53805f12 , 0x53805f12 , 0x3e801a81 , 0x3e801a81 }, { 0x0000a5cc , 0x59825eb2 , 0x59825eb2 , 0x42801a83 , 0x42801a83 }, { 0x0000a5d0 , 0x5e825f12 , 0x5e825f12 , 0x44801c84 , 0x44801c84 }, { 0x0000a5d4 , 0x61827f12 , 0x61827f12 , 0x48801ce3 , 0x48801ce3 }, { 0x0000a5d8 , 0x6782bf12 , 0x6782bf12 , 0x4c801ce5 , 0x4c801ce5 }, { 0x0000a5dc , 0x6b82bf14 , 0x6b82bf14 , 0x50801ce9 , 0x50801ce9 }, { 0x0000a5e0 , 0x6f82bf16 , 0x6f82bf16 , 0x54801ceb , 0x54801ceb }, { 0x0000a5e4 , 0x6f82bf16 , 0x6f82bf16 , 0x56801eec , 0x56801eec }, { 0x0000a5e8 , 0x6f82bf16 , 0x6f82bf16 , 0x56801eec , 0x56801eec }, { 0x0000a5ec , 0x6f82bf16 , 0x6f82bf16 , 0x56801eec , 0x56801eec }, { 0x0000a5f0 , 0x6f82bf16 , 0x6f82bf16 , 0x56801eec , 0x56801eec }, { 0x0000a5f4 , 0x6f82bf16 , 0x6f82bf16 , 0x56801eec , 0x56801eec }, { 0x0000a5f8 , 0x6f82bf16 , 0x6f82bf16 , 0x56801eec , 0x56801eec }, { 0x0000a5fc , 0x6f82bf16 , 0x6f82bf16 , 0x56801eec , 0x56801eec }, { 0x0000a600 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a604 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a608 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a60c , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a610 , 0x00804000 , 0x00804000 , 0x00000000 , 0x00000000 }, { 0x0000a614 , 0x00804201 , 0x00804201 , 0x01404000 , 0x01404000 }, { 0x0000a618 , 0x0280c802 , 0x0280c802 , 0x01404501 , 0x01404501 }, { 0x0000a61c , 0x0280ca03 , 0x0280ca03 , 0x02008501 , 0x02008501 }, { 0x0000a620 , 0x04c15104 , 0x04c15104 , 0x0280ca03 , 0x0280ca03 }, { 0x0000a624 , 0x04c15305 , 0x04c15305 , 0x03010c04 , 0x03010c04 }, { 0x0000a628 , 0x04c15305 , 0x04c15305 , 0x04014c04 , 0x04014c04 }, { 0x0000a62c , 0x04c15305 , 0x04c15305 , 0x04015005 , 0x04015005 }, { 0x0000a630 , 0x04c15305 , 0x04c15305 , 0x04015005 , 0x04015005 }, { 0x0000a634 , 0x04c15305 , 0x04c15305 , 0x04015005 , 0x04015005 }, { 0x0000a638 , 0x04c15305 , 0x04c15305 , 0x04015005 , 0x04015005 }, { 0x0000a63c , 0x04c15305 , 0x04c15305 , 0x04015005 , 0x04015005 }, { 0x0000b2dc , 0x01feee00 , 0x01feee00 , 0x03aaa352 , 0x03aaa352 }, { 0x0000b2e0 , 0x0000f000 , 0x0000f000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000b2e4 , 0x01ff0000 , 0x01ff0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000b2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x00016044 , 0x03b6d2e4 , 0x03b6d2e4 , 0x03b6d2e4 , 0x03b6d2e4 }, { 0x00016048 , 0x8e481666 , 0x8e481666 , 0x8e481266 , 0x8e481266 }, { 0x00016280 , 0x01000015 , 0x01000015 , 0x01001015 , 0x01001015 }, { 0x00016444 , 0x03b6d2e4 , 0x03b6d2e4 , 0x03b6d2e4 , 0x03b6d2e4 }, { 0x00016448 , 0x8e481666 , 0x8e481666 , 0x8e481266 , 0x8e481266 }, }; static const u_int32_t ar9340_modes_ub124_tx_gain_table_wasp_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x00009810 , 0xd00a8005 , 0xd00a8005 , 0xd00a8005 , 0xd00a8005 }, { 0x00009820 , 0x206a022e , 0x206a022e , 0x206a00ae , 0x206a00ae }, { 0x00009830 , 0x0000059c , 0x0000059c , 0x0000059c , 0x0000059c }, { 0x00009e10 , 0x7ec88d2e , 0x7ec88d2e , 0x7ec82d2e , 0x7ec82d2e }, { 0x0000a2dc , 0xfef5d402 , 0xfef5d402 , 0xfdab5b52 , 0xfdab5b52 }, { 0x0000a2e0 , 0xfe896600 , 0xfe896600 , 0xfd339c84 , 0xfd339c84 }, { 0x0000a2e4 , 0xff01f800 , 0xff01f800 , 0xfec3e000 , 0xfec3e000 }, { 0x0000a2e8 , 0xfffe0000 , 0xfffe0000 , 0xfffc0000 , 0xfffc0000 }, { 0x0000a410 , 0x000050d8 , 0x000050d8 , 0x000050d9 , 0x000050d9 }, { 0x0000a500 , 0x00002220 , 0x00002220 , 0x00000000 , 0x00000000 }, { 0x0000a504 , 0x04002222 , 0x04002222 , 0x04000002 , 0x04000002 }, { 0x0000a508 , 0x09002421 , 0x09002421 , 0x08000004 , 0x08000004 }, { 0x0000a50c , 0x0d002621 , 0x0d002621 , 0x0b000200 , 0x0b000200 }, { 0x0000a510 , 0x13004620 , 0x13004620 , 0x0f000202 , 0x0f000202 }, { 0x0000a514 , 0x19004a20 , 0x19004a20 , 0x11000400 , 0x11000400 }, { 0x0000a518 , 0x1d004e20 , 0x1d004e20 , 0x15000402 , 0x15000402 }, { 0x0000a51c , 0x21005420 , 0x21005420 , 0x19000404 , 0x19000404 }, { 0x0000a520 , 0x26005e20 , 0x26005e20 , 0x1b000603 , 0x1b000603 }, { 0x0000a524 , 0x2b005e40 , 0x2b005e40 , 0x1f000a02 , 0x1f000a02 }, { 0x0000a528 , 0x2f005e42 , 0x2f005e42 , 0x23000a04 , 0x23000a04 }, { 0x0000a52c , 0x33005e44 , 0x33005e44 , 0x26000a20 , 0x26000a20 }, { 0x0000a530 , 0x38005e65 , 0x38005e65 , 0x2a000e20 , 0x2a000e20 }, { 0x0000a534 , 0x3c005e69 , 0x3c005e69 , 0x2e000e22 , 0x2e000e22 }, { 0x0000a538 , 0x40005e6b , 0x40005e6b , 0x31000e24 , 0x31000e24 }, { 0x0000a53c , 0x44005e6d , 0x44005e6d , 0x34001640 , 0x34001640 }, { 0x0000a540 , 0x49005e72 , 0x49005e72 , 0x38001660 , 0x38001660 }, { 0x0000a544 , 0x4e005eb2 , 0x4e005eb2 , 0x3b001861 , 0x3b001861 }, { 0x0000a548 , 0x53005f12 , 0x53005f12 , 0x3e001a81 , 0x3e001a81 }, { 0x0000a54c , 0x59025eb5 , 0x59025eb5 , 0x42001a83 , 0x42001a83 }, { 0x0000a550 , 0x5e025f12 , 0x5e025f12 , 0x44001c84 , 0x44001c84 }, { 0x0000a554 , 0x61027f12 , 0x61027f12 , 0x48001ce3 , 0x48001ce3 }, { 0x0000a558 , 0x6702bf12 , 0x6702bf12 , 0x4c001ce5 , 0x4c001ce5 }, { 0x0000a55c , 0x6b02bf14 , 0x6b02bf14 , 0x50001ce9 , 0x50001ce9 }, { 0x0000a560 , 0x6f02bf16 , 0x6f02bf16 , 0x54001ceb , 0x54001ceb }, { 0x0000a564 , 0x6f02bf16 , 0x6f02bf16 , 0x56001eec , 0x56001eec }, { 0x0000a568 , 0x6f02bf16 , 0x6f02bf16 , 0x56001eec , 0x56001eec }, { 0x0000a56c , 0x6f02bf16 , 0x6f02bf16 , 0x56001eec , 0x56001eec }, { 0x0000a570 , 0x6f02bf16 , 0x6f02bf16 , 0x56001eec , 0x56001eec }, { 0x0000a574 , 0x6f02bf16 , 0x6f02bf16 , 0x56001eec , 0x56001eec }, { 0x0000a578 , 0x6f02bf16 , 0x6f02bf16 , 0x56001eec , 0x56001eec }, { 0x0000a57c , 0x6f02bf16 , 0x6f02bf16 , 0x56001eec , 0x56001eec }, { 0x0000a580 , 0x00802220 , 0x00802220 , 0x00800000 , 0x00800000 }, { 0x0000a584 , 0x04802222 , 0x04802222 , 0x04800002 , 0x04800002 }, { 0x0000a588 , 0x09802421 , 0x09802421 , 0x08800004 , 0x08800004 }, { 0x0000a58c , 0x0d802621 , 0x0d802621 , 0x0b800200 , 0x0b800200 }, { 0x0000a590 , 0x13804620 , 0x13804620 , 0x0f800202 , 0x0f800202 }, { 0x0000a594 , 0x19804a20 , 0x19804a20 , 0x11800400 , 0x11800400 }, { 0x0000a598 , 0x1d804e20 , 0x1d804e20 , 0x15800402 , 0x15800402 }, { 0x0000a59c , 0x21805420 , 0x21805420 , 0x19800404 , 0x19800404 }, { 0x0000a5a0 , 0x26805e20 , 0x26805e20 , 0x1b800603 , 0x1b800603 }, { 0x0000a5a4 , 0x2b805e40 , 0x2b805e40 , 0x1f800a02 , 0x1f800a02 }, { 0x0000a5a8 , 0x2f805e42 , 0x2f805e42 , 0x23800a04 , 0x23800a04 }, { 0x0000a5ac , 0x33805e44 , 0x33805e44 , 0x26800a20 , 0x26800a20 }, { 0x0000a5b0 , 0x38805e65 , 0x38805e65 , 0x2a800e20 , 0x2a800e20 }, { 0x0000a5b4 , 0x3c805e69 , 0x3c805e69 , 0x2e800e22 , 0x2e800e22 }, { 0x0000a5b8 , 0x40805e6b , 0x40805e6b , 0x31800e24 , 0x31800e24 }, { 0x0000a5bc , 0x44805e6d , 0x44805e6d , 0x34801640 , 0x34801640 }, { 0x0000a5c0 , 0x49805e72 , 0x49805e72 , 0x38801660 , 0x38801660 }, { 0x0000a5c4 , 0x4e805eb2 , 0x4e805eb2 , 0x3b801861 , 0x3b801861 }, { 0x0000a5c8 , 0x53805f12 , 0x53805f12 , 0x3e801a81 , 0x3e801a81 }, { 0x0000a5cc , 0x59825eb2 , 0x59825eb2 , 0x42801a83 , 0x42801a83 }, { 0x0000a5d0 , 0x5e825f12 , 0x5e825f12 , 0x44801c84 , 0x44801c84 }, { 0x0000a5d4 , 0x61827f12 , 0x61827f12 , 0x48801ce3 , 0x48801ce3 }, { 0x0000a5d8 , 0x6782bf12 , 0x6782bf12 , 0x4c801ce5 , 0x4c801ce5 }, { 0x0000a5dc , 0x6b82bf14 , 0x6b82bf14 , 0x50801ce9 , 0x50801ce9 }, { 0x0000a5e0 , 0x6f82bf16 , 0x6f82bf16 , 0x54801ceb , 0x54801ceb }, { 0x0000a5e4 , 0x6f82bf16 , 0x6f82bf16 , 0x56801eec , 0x56801eec }, { 0x0000a5e8 , 0x6f82bf16 , 0x6f82bf16 , 0x56801eec , 0x56801eec }, { 0x0000a5ec , 0x6f82bf16 , 0x6f82bf16 , 0x56801eec , 0x56801eec }, { 0x0000a5f0 , 0x6f82bf16 , 0x6f82bf16 , 0x56801eec , 0x56801eec }, { 0x0000a5f4 , 0x6f82bf16 , 0x6f82bf16 , 0x56801eec , 0x56801eec }, { 0x0000a5f8 , 0x6f82bf16 , 0x6f82bf16 , 0x56801eec , 0x56801eec }, { 0x0000a5fc , 0x6f82bf16 , 0x6f82bf16 , 0x56801eec , 0x56801eec }, { 0x00016044 , 0x03b6d2e4 , 0x03b6d2e4 , 0x03b6d2e4 , 0x03b6d2e4 }, { 0x00016048 , 0x8e480086 , 0x8e480086 , 0x8e480086 , 0x8e480086 }, { 0x00016444 , 0x03b6d2e4 , 0x03b6d2e4 , 0x03b6d2e4 , 0x03b6d2e4 }, { 0x00016448 , 0x8e480086 , 0x8e480086 , 0x8e480086 , 0x8e480086 }, { 0x0000a600 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a604 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a608 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a60c , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a610 , 0x00804000 , 0x00804000 , 0x00000000 , 0x00000000 }, { 0x0000a614 , 0x00804201 , 0x00804201 , 0x01404000 , 0x01404000 }, { 0x0000a618 , 0x0280c802 , 0x0280c802 , 0x01404501 , 0x01404501 }, { 0x0000a61c , 0x0280ca03 , 0x0280ca03 , 0x02008501 , 0x02008501 }, { 0x0000a620 , 0x04c15104 , 0x04c15104 , 0x0280ca03 , 0x0280ca03 }, { 0x0000a624 , 0x04c15305 , 0x04c15305 , 0x03010c04 , 0x03010c04 }, { 0x0000a628 , 0x04c15305 , 0x04c15305 , 0x04014c04 , 0x04014c04 }, { 0x0000a62c , 0x04c15305 , 0x04c15305 , 0x04015005 , 0x04015005 }, { 0x0000a630 , 0x04c15305 , 0x04c15305 , 0x04015005 , 0x04015005 }, { 0x0000a634 , 0x04c15305 , 0x04c15305 , 0x04015005 , 0x04015005 }, { 0x0000a638 , 0x04c15305 , 0x04c15305 , 0x04015005 , 0x04015005 }, { 0x0000a63c , 0x04c15305 , 0x04c15305 , 0x04015005 , 0x04015005 }, { 0x0000b2dc , 0xfef5d402 , 0xfef5d402 , 0xfdab5b52 , 0xfdab5b52 }, { 0x0000b2e0 , 0xfe896600 , 0xfe896600 , 0xfd339c84 , 0xfd339c84 }, { 0x0000b2e4 , 0xff01f800 , 0xff01f800 , 0xfec3e000 , 0xfec3e000 }, { 0x0000b2e8 , 0xfffe0000 , 0xfffe0000 , 0xfffc0000 , 0xfffc0000 }, }; static const u_int32_t ar9340Common_rx_gain_table_wasp_1p0[][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 , 0x01910190 }, { 0x0000a030 , 0x01930192 }, { 0x0000a034 , 0x01950194 }, { 0x0000a038 , 0x038a0196 }, { 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 , 0x22222229 }, { 0x0000a084 , 0x1d1d1d1d }, { 0x0000a088 , 0x1d1d1d1d }, { 0x0000a08c , 0x1d1d1d1d }, { 0x0000a090 , 0x171d1d1d }, { 0x0000a094 , 0x11111717 }, { 0x0000a098 , 0x00030311 }, { 0x0000a09c , 0x00000000 }, { 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 , 0x23232323 }, { 0x0000b084 , 0x21232323 }, { 0x0000b088 , 0x19191c1e }, { 0x0000b08c , 0x12141417 }, { 0x0000b090 , 0x07070e0e }, { 0x0000b094 , 0x03030305 }, { 0x0000b098 , 0x00000003 }, { 0x0000b09c , 0x00000000 }, { 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 u_int32_t ar9340Modes_low_ob_db_tx_gain_table_wasp_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x0000a2dc , 0x0380c7fc , 0x0380c7fc , 0x03aaa352 , 0x03aaa352 }, { 0x0000a2e0 , 0x0000f800 , 0x0000f800 , 0x03ccc584 , 0x03ccc584 }, { 0x0000a2e4 , 0x03ff0000 , 0x03ff0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000a2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000a410 , 0x000050d9 , 0x000050d9 , 0x000050d9 , 0x000050d9 }, { 0x0000a500 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a504 , 0x06000003 , 0x06000003 , 0x04000002 , 0x04000002 }, { 0x0000a508 , 0x0a000020 , 0x0a000020 , 0x08000004 , 0x08000004 }, { 0x0000a50c , 0x10000023 , 0x10000023 , 0x0b000200 , 0x0b000200 }, { 0x0000a510 , 0x16000220 , 0x16000220 , 0x0f000202 , 0x0f000202 }, { 0x0000a514 , 0x1c000223 , 0x1c000223 , 0x12000400 , 0x12000400 }, { 0x0000a518 , 0x21002220 , 0x21002220 , 0x16000402 , 0x16000402 }, { 0x0000a51c , 0x27002223 , 0x27002223 , 0x19000404 , 0x19000404 }, { 0x0000a520 , 0x2b022220 , 0x2b022220 , 0x1c000603 , 0x1c000603 }, { 0x0000a524 , 0x2f022222 , 0x2f022222 , 0x21000a02 , 0x21000a02 }, { 0x0000a528 , 0x34022225 , 0x34022225 , 0x25000a04 , 0x25000a04 }, { 0x0000a52c , 0x3a02222a , 0x3a02222a , 0x28000a20 , 0x28000a20 }, { 0x0000a530 , 0x3e02222c , 0x3e02222c , 0x2c000e20 , 0x2c000e20 }, { 0x0000a534 , 0x4202242a , 0x4202242a , 0x30000e22 , 0x30000e22 }, { 0x0000a538 , 0x4702244a , 0x4702244a , 0x34000e24 , 0x34000e24 }, { 0x0000a53c , 0x4b02244c , 0x4b02244c , 0x38001640 , 0x38001640 }, { 0x0000a540 , 0x4e02246c , 0x4e02246c , 0x3c001660 , 0x3c001660 }, { 0x0000a544 , 0x5302266c , 0x5302266c , 0x3f001861 , 0x3f001861 }, { 0x0000a548 , 0x5702286c , 0x5702286c , 0x43001a81 , 0x43001a81 }, { 0x0000a54c , 0x5c02486b , 0x5c02486b , 0x47001a83 , 0x47001a83 }, { 0x0000a550 , 0x61024a6c , 0x61024a6c , 0x4a001c84 , 0x4a001c84 }, { 0x0000a554 , 0x66026a6c , 0x66026a6c , 0x4e001ce3 , 0x4e001ce3 }, { 0x0000a558 , 0x6b026e6c , 0x6b026e6c , 0x52001ce5 , 0x52001ce5 }, { 0x0000a55c , 0x7002708c , 0x7002708c , 0x56001ce9 , 0x56001ce9 }, { 0x0000a560 , 0x7302b08a , 0x7302b08a , 0x5a001ceb , 0x5a001ceb }, { 0x0000a564 , 0x7702b08c , 0x7702b08c , 0x5d001eec , 0x5d001eec }, { 0x0000a568 , 0x7702b08c , 0x7702b08c , 0x5d001eec , 0x5d001eec }, { 0x0000a56c , 0x7702b08c , 0x7702b08c , 0x5d001eec , 0x5d001eec }, { 0x0000a570 , 0x7702b08c , 0x7702b08c , 0x5d001eec , 0x5d001eec }, { 0x0000a574 , 0x7702b08c , 0x7702b08c , 0x5d001eec , 0x5d001eec }, { 0x0000a578 , 0x7702b08c , 0x7702b08c , 0x5d001eec , 0x5d001eec }, { 0x0000a57c , 0x7702b08c , 0x7702b08c , 0x5d001eec , 0x5d001eec }, { 0x0000a580 , 0x00800000 , 0x00800000 , 0x00800000 , 0x00800000 }, { 0x0000a584 , 0x06800003 , 0x06800003 , 0x04800002 , 0x04800002 }, { 0x0000a588 , 0x0a800020 , 0x0a800020 , 0x08800004 , 0x08800004 }, { 0x0000a58c , 0x10800023 , 0x10800023 , 0x0b800200 , 0x0b800200 }, { 0x0000a590 , 0x16800220 , 0x16800220 , 0x0f800202 , 0x0f800202 }, { 0x0000a594 , 0x1c800223 , 0x1c800223 , 0x12800400 , 0x12800400 }, { 0x0000a598 , 0x21820220 , 0x21820220 , 0x16800402 , 0x16800402 }, { 0x0000a59c , 0x27820223 , 0x27820223 , 0x19800404 , 0x19800404 }, { 0x0000a5a0 , 0x2b822220 , 0x2b822220 , 0x1c800603 , 0x1c800603 }, { 0x0000a5a4 , 0x2f822222 , 0x2f822222 , 0x21800a02 , 0x21800a02 }, { 0x0000a5a8 , 0x34822225 , 0x34822225 , 0x25800a04 , 0x25800a04 }, { 0x0000a5ac , 0x3a82222a , 0x3a82222a , 0x28800a20 , 0x28800a20 }, { 0x0000a5b0 , 0x3e82222c , 0x3e82222c , 0x2c800e20 , 0x2c800e20 }, { 0x0000a5b4 , 0x4282242a , 0x4282242a , 0x30800e22 , 0x30800e22 }, { 0x0000a5b8 , 0x4782244a , 0x4782244a , 0x34800e24 , 0x34800e24 }, { 0x0000a5bc , 0x4b82244c , 0x4b82244c , 0x38801640 , 0x38801640 }, { 0x0000a5c0 , 0x4e82246c , 0x4e82246c , 0x3c801660 , 0x3c801660 }, { 0x0000a5c4 , 0x5382266c , 0x5382266c , 0x3f801861 , 0x3f801861 }, { 0x0000a5c8 , 0x5782286c , 0x5782286c , 0x43801a81 , 0x43801a81 }, { 0x0000a5cc , 0x5c84286b , 0x5c84286b , 0x47801a83 , 0x47801a83 }, { 0x0000a5d0 , 0x61842a6c , 0x61842a6c , 0x4a801c84 , 0x4a801c84 }, { 0x0000a5d4 , 0x66862a6c , 0x66862a6c , 0x4e801ce3 , 0x4e801ce3 }, { 0x0000a5d8 , 0x6b862e6c , 0x6b862e6c , 0x52801ce5 , 0x52801ce5 }, { 0x0000a5dc , 0x7086308c , 0x7086308c , 0x56801ce9 , 0x56801ce9 }, { 0x0000a5e0 , 0x738a308a , 0x738a308a , 0x5a801ceb , 0x5a801ceb }, { 0x0000a5e4 , 0x778a308c , 0x778a308c , 0x5d801eec , 0x5d801eec }, { 0x0000a5e8 , 0x778a308c , 0x778a308c , 0x5d801eec , 0x5d801eec }, { 0x0000a5ec , 0x778a308c , 0x778a308c , 0x5d801eec , 0x5d801eec }, { 0x0000a5f0 , 0x778a308c , 0x778a308c , 0x5d801eec , 0x5d801eec }, { 0x0000a5f4 , 0x778a308c , 0x778a308c , 0x5d801eec , 0x5d801eec }, { 0x0000a5f8 , 0x778a308c , 0x778a308c , 0x5d801eec , 0x5d801eec }, { 0x0000a5fc , 0x778a308c , 0x778a308c , 0x5d801eec , 0x5d801eec }, { 0x0000a600 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a604 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a608 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a60c , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a610 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a614 , 0x01404000 , 0x01404000 , 0x01404000 , 0x01404000 }, { 0x0000a618 , 0x01404501 , 0x01404501 , 0x01404501 , 0x01404501 }, { 0x0000a61c , 0x02008802 , 0x02008802 , 0x02008501 , 0x02008501 }, { 0x0000a620 , 0x0300cc03 , 0x0300cc03 , 0x0280ca03 , 0x0280ca03 }, { 0x0000a624 , 0x0300cc03 , 0x0300cc03 , 0x03010c04 , 0x03010c04 }, { 0x0000a628 , 0x0300cc03 , 0x0300cc03 , 0x04014c04 , 0x04014c04 }, { 0x0000a62c , 0x03810c03 , 0x03810c03 , 0x04015005 , 0x04015005 }, { 0x0000a630 , 0x03810e04 , 0x03810e04 , 0x04015005 , 0x04015005 }, { 0x0000a634 , 0x03810e04 , 0x03810e04 , 0x04015005 , 0x04015005 }, { 0x0000a638 , 0x03810e04 , 0x03810e04 , 0x04015005 , 0x04015005 }, { 0x0000a63c , 0x03810e04 , 0x03810e04 , 0x04015005 , 0x04015005 }, { 0x0000b2dc , 0x0380c7fc , 0x0380c7fc , 0x03aaa352 , 0x03aaa352 }, { 0x0000b2e0 , 0x0000f800 , 0x0000f800 , 0x03ccc584 , 0x03ccc584 }, { 0x0000b2e4 , 0x03ff0000 , 0x03ff0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000b2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x00016044 , 0x056db2db , 0x056db2db , 0x056db2db , 0x056db2db }, { 0x00016048 , 0x24925666 , 0x24925666 , 0x24925266 , 0x24925266 }, { 0x00016280 , 0x01000015 , 0x01000015 , 0x01001015 , 0x01001015 }, { 0x00016288 , 0xf0318000 , 0xf0318000 , 0xf0318000 , 0xf0318000 }, //top3 { 0x00016444 , 0x056db2db , 0x056db2db , 0x056db2db , 0x056db2db }, { 0x00016448 , 0x24925666 , 0x24925666 , 0x24925266 , 0x24925266 }, }; static const u_int32_t ar9340_modes_low_ob_db_and_spur_tx_gain_table_wasp_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x0000a2dc , 0x0380c7fc , 0x0380c7fc , 0x03eaac5a , 0x03eaac5a }, { 0x0000a2e0 , 0x0000f800 , 0x0000f800 , 0x03f330ac , 0x03f330ac }, { 0x0000a2e4 , 0x03ff0000 , 0x03ff0000 , 0x03fc3f00 , 0x03fc3f00 }, { 0x0000a2e8 , 0x00000000 , 0x00000000 , 0x03ffc000 , 0x03ffc000 }, { 0x0000a394 , 0x00000444 , 0x00000444 , 0x00000404 , 0x00000404 }, { 0x0000a410 , 0x000050d9 , 0x000050d9 , 0x000050d9 , 0x000050d9 }, { 0x0000a500 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a504 , 0x06000003 , 0x06000003 , 0x02000001 , 0x02000001 }, { 0x0000a508 , 0x0a000020 , 0x0a000020 , 0x05000003 , 0x05000003 }, { 0x0000a50c , 0x10000023 , 0x10000023 , 0x0a000005 , 0x0a000005 }, { 0x0000a510 , 0x16000220 , 0x16000220 , 0x0e000201 , 0x0e000201 }, { 0x0000a514 , 0x1c000223 , 0x1c000223 , 0x11000203 , 0x11000203 }, { 0x0000a518 , 0x21002220 , 0x21002220 , 0x14000401 , 0x14000401 }, { 0x0000a51c , 0x27002223 , 0x27002223 , 0x18000403 , 0x18000403 }, { 0x0000a520 , 0x2b022220 , 0x2b022220 , 0x1b000602 , 0x1b000602 }, { 0x0000a524 , 0x2f022222 , 0x2f022222 , 0x1f000802 , 0x1f000802 }, { 0x0000a528 , 0x34022225 , 0x34022225 , 0x21000620 , 0x21000620 }, { 0x0000a52c , 0x3a02222a , 0x3a02222a , 0x25000820 , 0x25000820 }, { 0x0000a530 , 0x3e02222c , 0x3e02222c , 0x29000822 , 0x29000822 }, { 0x0000a534 , 0x4202242a , 0x4202242a , 0x2d000824 , 0x2d000824 }, { 0x0000a538 , 0x4702244a , 0x4702244a , 0x30000828 , 0x30000828 }, { 0x0000a53c , 0x4b02244c , 0x4b02244c , 0x3400082a , 0x3400082a }, { 0x0000a540 , 0x4e02246c , 0x4e02246c , 0x38000849 , 0x38000849 }, { 0x0000a544 , 0x5302266c , 0x5302266c , 0x3b000a2c , 0x3b000a2c }, { 0x0000a548 , 0x5702286c , 0x5702286c , 0x3e000e2b , 0x3e000e2b }, { 0x0000a54c , 0x5c02486b , 0x5c02486b , 0x42000e2d , 0x42000e2d }, { 0x0000a550 , 0x61024a6c , 0x61024a6c , 0x4500124a , 0x4500124a }, { 0x0000a554 , 0x66026a6c , 0x66026a6c , 0x4900124c , 0x4900124c }, { 0x0000a558 , 0x6b026e6c , 0x6b026e6c , 0x4c00126c , 0x4c00126c }, { 0x0000a55c , 0x7002708c , 0x7002708c , 0x4f00128c , 0x4f00128c }, { 0x0000a560 , 0x7302b08a , 0x7302b08a , 0x52001290 , 0x52001290 }, { 0x0000a564 , 0x7702b08c , 0x7702b08c , 0x56001292 , 0x56001292 }, { 0x0000a568 , 0x7702b08c , 0x7702b08c , 0x56001292 , 0x56001292 }, { 0x0000a56c , 0x7702b08c , 0x7702b08c , 0x56001292 , 0x56001292 }, { 0x0000a570 , 0x7702b08c , 0x7702b08c , 0x56001292 , 0x56001292 }, { 0x0000a574 , 0x7702b08c , 0x7702b08c , 0x56001292 , 0x56001292 }, { 0x0000a578 , 0x7702b08c , 0x7702b08c , 0x56001292 , 0x56001292 }, { 0x0000a57c , 0x7702b08c , 0x7702b08c , 0x56001292 , 0x56001292 }, { 0x0000a580 , 0x00800000 , 0x00800000 , 0x00800000 , 0x00800000 }, { 0x0000a584 , 0x06800003 , 0x06800003 , 0x02800001 , 0x02800001 }, { 0x0000a588 , 0x0a800020 , 0x0a800020 , 0x05800003 , 0x05800003 }, { 0x0000a58c , 0x10800023 , 0x10800023 , 0x0a800005 , 0x0a800005 }, { 0x0000a590 , 0x16800220 , 0x16800220 , 0x0e800201 , 0x0e800201 }, { 0x0000a594 , 0x1c800223 , 0x1c800223 , 0x11800203 , 0x11800203 }, { 0x0000a598 , 0x21820220 , 0x21820220 , 0x14800401 , 0x14800401 }, { 0x0000a59c , 0x27820223 , 0x27820223 , 0x18800403 , 0x18800403 }, { 0x0000a5a0 , 0x2b822220 , 0x2b822220 , 0x1b800602 , 0x1b800602 }, { 0x0000a5a4 , 0x2f822222 , 0x2f822222 , 0x1f800802 , 0x1f800802 }, { 0x0000a5a8 , 0x34822225 , 0x34822225 , 0x21800620 , 0x21800620 }, { 0x0000a5ac , 0x3a82222a , 0x3a82222a , 0x25800820 , 0x25800820 }, { 0x0000a5b0 , 0x3e82222c , 0x3e82222c , 0x29800822 , 0x29800822 }, { 0x0000a5b4 , 0x4282242a , 0x4282242a , 0x2d800824 , 0x2d800824 }, { 0x0000a5b8 , 0x4782244a , 0x4782244a , 0x30800828 , 0x30800828 }, { 0x0000a5bc , 0x4b82244c , 0x4b82244c , 0x3480082a , 0x3480082a }, { 0x0000a5c0 , 0x4e82246c , 0x4e82246c , 0x38800849 , 0x38800849 }, { 0x0000a5c4 , 0x5382266c , 0x5382266c , 0x3b800a2c , 0x3b800a2c }, { 0x0000a5c8 , 0x5782286c , 0x5782286c , 0x3e800e2b , 0x3e800e2b }, { 0x0000a5cc , 0x5c84286b , 0x5c84286b , 0x42800e2d , 0x42800e2d }, { 0x0000a5d0 , 0x61842a6c , 0x61842a6c , 0x4580124a , 0x4580124a }, { 0x0000a5d4 , 0x66862a6c , 0x66862a6c , 0x4980124c , 0x4980124c }, { 0x0000a5d8 , 0x6b862e6c , 0x6b862e6c , 0x4c80126c , 0x4c80126c }, { 0x0000a5dc , 0x7086308c , 0x7086308c , 0x4f80128c , 0x4f80128c }, { 0x0000a5e0 , 0x738a308a , 0x738a308a , 0x52801290 , 0x52801290 }, { 0x0000a5e4 , 0x778a308c , 0x778a308c , 0x56801292 , 0x56801292 }, { 0x0000a5e8 , 0x778a308c , 0x778a308c , 0x56801292 , 0x56801292 }, { 0x0000a5ec , 0x778a308c , 0x778a308c , 0x56801292 , 0x56801292 }, { 0x0000a5f0 , 0x778a308c , 0x778a308c , 0x56801292 , 0x56801292 }, { 0x0000a5f4 , 0x778a308c , 0x778a308c , 0x56801292 , 0x56801292 }, { 0x0000a5f8 , 0x778a308c , 0x778a308c , 0x56801292 , 0x56801292 }, { 0x0000a5fc , 0x778a308c , 0x778a308c , 0x56801292 , 0x56801292 }, { 0x0000a600 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a604 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a608 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a60c , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a610 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a614 , 0x01404000 , 0x01404000 , 0x01404501 , 0x01404501 }, { 0x0000a618 , 0x01404501 , 0x01404501 , 0x01404501 , 0x01404501 }, { 0x0000a61c , 0x02008802 , 0x02008802 , 0x01404501 , 0x01404501 }, { 0x0000a620 , 0x0300cc03 , 0x0300cc03 , 0x03c0cf02 , 0x03c0cf02 }, { 0x0000a624 , 0x0300cc03 , 0x0300cc03 , 0x03c0cf03 , 0x03c0cf03 }, { 0x0000a628 , 0x0300cc03 , 0x0300cc03 , 0x04011004 , 0x04011004 }, { 0x0000a62c , 0x03810c03 , 0x03810c03 , 0x05419405 , 0x05419405 }, { 0x0000a630 , 0x03810e04 , 0x03810e04 , 0x05419506 , 0x05419506 }, { 0x0000a634 , 0x03810e04 , 0x03810e04 , 0x05419506 , 0x05419506 }, { 0x0000a638 , 0x03810e04 , 0x03810e04 , 0x05419506 , 0x05419506 }, { 0x0000a63c , 0x03810e04 , 0x03810e04 , 0x05419506 , 0x05419506 }, { 0x0000b2dc , 0x0380c7fc , 0x0380c7fc , 0x03eaac5a , 0x03eaac5a }, { 0x0000b2e0 , 0x0000f800 , 0x0000f800 , 0x03f330ac , 0x03f330ac }, { 0x0000b2e4 , 0x03ff0000 , 0x03ff0000 , 0x03fc3f00 , 0x03fc3f00 }, { 0x0000b2e8 , 0x00000000 , 0x00000000 , 0x03ffc000 , 0x03ffc000 }, { 0x00016044 , 0x022492db , 0x022492db , 0x022492db , 0x022492db }, { 0x00016048 , 0x24925666 , 0x24925666 , 0x24925266 , 0x24925266 }, { 0x00016280 , 0x01000015 , 0x01000015 , 0x01001015 , 0x01001015 }, { 0x00016288 , 0xf0318000 , 0xf0318000 , 0xf0318000 , 0xf0318000 }, //top3 { 0x00016444 , 0x022492db , 0x022492db , 0x022492db , 0x022492db }, { 0x00016448 , 0x24925666 , 0x24925666 , 0x24925266 , 0x24925266 }, }; static const u_int32_t ar9340Modes_mixed_ob_db_tx_gain_table_wasp_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x0000a2dc , 0x0380c7fc , 0x0380c7fc , 0x03aaa352 , 0x03aaa352 }, { 0x0000a2e0 , 0x0000f800 , 0x0000f800 , 0x03ccc584 , 0x03ccc584 }, { 0x0000a2e4 , 0x03ff0000 , 0x03ff0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000a2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000a410 , 0x000050d9 , 0x000050d9 , 0x000050d9 , 0x000050d9 }, { 0x0000a500 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a504 , 0x06000003 , 0x06000003 , 0x04000002 , 0x04000002 }, { 0x0000a508 , 0x0a000020 , 0x0a000020 , 0x08000004 , 0x08000004 }, { 0x0000a50c , 0x10000023 , 0x10000023 , 0x0b000200 , 0x0b000200 }, { 0x0000a510 , 0x16000220 , 0x16000220 , 0x0f000202 , 0x0f000202 }, { 0x0000a514 , 0x1c000223 , 0x1c000223 , 0x11000400 , 0x11000400 }, { 0x0000a518 , 0x21002220 , 0x21002220 , 0x15000402 , 0x15000402 }, { 0x0000a51c , 0x27002223 , 0x27002223 , 0x19000404 , 0x19000404 }, { 0x0000a520 , 0x2b022220 , 0x2b022220 , 0x1b000603 , 0x1b000603 }, { 0x0000a524 , 0x2f022222 , 0x2f022222 , 0x1f000a02 , 0x1f000a02 }, { 0x0000a528 , 0x34022225 , 0x34022225 , 0x23000a04 , 0x23000a04 }, { 0x0000a52c , 0x3a02222a , 0x3a02222a , 0x26000a20 , 0x26000a20 }, { 0x0000a530 , 0x3e02222c , 0x3e02222c , 0x2a000e20 , 0x2a000e20 }, { 0x0000a534 , 0x4202242a , 0x4202242a , 0x2e000e22 , 0x2e000e22 }, { 0x0000a538 , 0x4702244a , 0x4702244a , 0x31000e24 , 0x31000e24 }, { 0x0000a53c , 0x4b02244c , 0x4b02244c , 0x34001640 , 0x34001640 }, { 0x0000a540 , 0x4e02246c , 0x4e02246c , 0x38001660 , 0x38001660 }, { 0x0000a544 , 0x5302266c , 0x5302266c , 0x3b001861 , 0x3b001861 }, { 0x0000a548 , 0x5702286c , 0x5702286c , 0x3e001a81 , 0x3e001a81 }, { 0x0000a54c , 0x5c02486b , 0x5c02486b , 0x42001a83 , 0x42001a83 }, { 0x0000a550 , 0x61024a6c , 0x61024a6c , 0x44001c84 , 0x44001c84 }, { 0x0000a554 , 0x66026a6c , 0x66026a6c , 0x48001ce3 , 0x48001ce3 }, { 0x0000a558 , 0x6b026e6c , 0x6b026e6c , 0x4c001ce5 , 0x4c001ce5 }, { 0x0000a55c , 0x7002708c , 0x7002708c , 0x50001ce9 , 0x50001ce9 }, { 0x0000a560 , 0x7302b08a , 0x7302b08a , 0x54001ceb , 0x54001ceb }, { 0x0000a564 , 0x7702b08c , 0x7702b08c , 0x56001eec , 0x56001eec }, { 0x0000a568 , 0x7702b08c , 0x7702b08c , 0x56001eec , 0x56001eec }, { 0x0000a56c , 0x7702b08c , 0x7702b08c , 0x56001eec , 0x56001eec }, { 0x0000a570 , 0x7702b08c , 0x7702b08c , 0x56001eec , 0x56001eec }, { 0x0000a574 , 0x7702b08c , 0x7702b08c , 0x56001eec , 0x56001eec }, { 0x0000a578 , 0x7702b08c , 0x7702b08c , 0x56001eec , 0x56001eec }, { 0x0000a57c , 0x7702b08c , 0x7702b08c , 0x56001eec , 0x56001eec }, { 0x0000a580 , 0x00800000 , 0x00800000 , 0x00800000 , 0x00800000 }, { 0x0000a584 , 0x06800003 , 0x06800003 , 0x04800002 , 0x04800002 }, { 0x0000a588 , 0x0a800020 , 0x0a800020 , 0x08800004 , 0x08800004 }, { 0x0000a58c , 0x10800023 , 0x10800023 , 0x0b800200 , 0x0b800200 }, { 0x0000a590 , 0x16800220 , 0x16800220 , 0x0f800202 , 0x0f800202 }, { 0x0000a594 , 0x1c800223 , 0x1c800223 , 0x11800400 , 0x11800400 }, { 0x0000a598 , 0x21820220 , 0x21820220 , 0x15800402 , 0x15800402 }, { 0x0000a59c , 0x27820223 , 0x27820223 , 0x19800404 , 0x19800404 }, { 0x0000a5a0 , 0x2b822220 , 0x2b822220 , 0x1b800603 , 0x1b800603 }, { 0x0000a5a4 , 0x2f822222 , 0x2f822222 , 0x1f800a02 , 0x1f800a02 }, { 0x0000a5a8 , 0x34822225 , 0x34822225 , 0x23800a04 , 0x23800a04 }, { 0x0000a5ac , 0x3a82222a , 0x3a82222a , 0x26800a20 , 0x26800a20 }, { 0x0000a5b0 , 0x3e82222c , 0x3e82222c , 0x2a800e20 , 0x2a800e20 }, { 0x0000a5b4 , 0x4282242a , 0x4282242a , 0x2e800e22 , 0x2e800e22 }, { 0x0000a5b8 , 0x4782244a , 0x4782244a , 0x31800e24 , 0x31800e24 }, { 0x0000a5bc , 0x4b82244c , 0x4b82244c , 0x34801640 , 0x34801640 }, { 0x0000a5c0 , 0x4e82246c , 0x4e82246c , 0x38801660 , 0x38801660 }, { 0x0000a5c4 , 0x5382266c , 0x5382266c , 0x3b801861 , 0x3b801861 }, { 0x0000a5c8 , 0x5782286c , 0x5782286c , 0x3e801a81 , 0x3e801a81 }, { 0x0000a5cc , 0x5c84286b , 0x5c84286b , 0x42801a83 , 0x42801a83 }, { 0x0000a5d0 , 0x61842a6c , 0x61842a6c , 0x44801c84 , 0x44801c84 }, { 0x0000a5d4 , 0x66862a6c , 0x66862a6c , 0x48801ce3 , 0x48801ce3 }, { 0x0000a5d8 , 0x6b862e6c , 0x6b862e6c , 0x4c801ce5 , 0x4c801ce5 }, { 0x0000a5dc , 0x7086308c , 0x7086308c , 0x50801ce9 , 0x50801ce9 }, { 0x0000a5e0 , 0x738a308a , 0x738a308a , 0x54801ceb , 0x54801ceb }, { 0x0000a5e4 , 0x778a308c , 0x778a308c , 0x56801eec , 0x56801eec }, { 0x0000a5e8 , 0x778a308c , 0x778a308c , 0x56801eec , 0x56801eec }, { 0x0000a5ec , 0x778a308c , 0x778a308c , 0x56801eec , 0x56801eec }, { 0x0000a5f0 , 0x778a308c , 0x778a308c , 0x56801eec , 0x56801eec }, { 0x0000a5f4 , 0x778a308c , 0x778a308c , 0x56801eec , 0x56801eec }, { 0x0000a5f8 , 0x778a308c , 0x778a308c , 0x56801eec , 0x56801eec }, { 0x0000a5fc , 0x778a308c , 0x778a308c , 0x56801eec , 0x56801eec }, { 0x0000a600 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a604 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a608 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a60c , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a610 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a614 , 0x01404000 , 0x01404000 , 0x01404000 , 0x01404000 }, { 0x0000a618 , 0x01404501 , 0x01404501 , 0x01404501 , 0x01404501 }, { 0x0000a61c , 0x02008802 , 0x02008802 , 0x02008501 , 0x02008501 }, { 0x0000a620 , 0x0300cc03 , 0x0300cc03 , 0x0280ca03 , 0x0280ca03 }, { 0x0000a624 , 0x0300cc03 , 0x0300cc03 , 0x03010c04 , 0x03010c04 }, { 0x0000a628 , 0x0300cc03 , 0x0300cc03 , 0x04014c04 , 0x04014c04 }, { 0x0000a62c , 0x03810c03 , 0x03810c03 , 0x04015005 , 0x04015005 }, { 0x0000a630 , 0x03810e04 , 0x03810e04 , 0x04015005 , 0x04015005 }, { 0x0000a634 , 0x03810e04 , 0x03810e04 , 0x04015005 , 0x04015005 }, { 0x0000a638 , 0x03810e04 , 0x03810e04 , 0x04015005 , 0x04015005 }, { 0x0000a63c , 0x03810e04 , 0x03810e04 , 0x04015005 , 0x04015005 }, { 0x0000b2dc , 0x0380c7fc , 0x0380c7fc , 0x03aaa352 , 0x03aaa352 }, { 0x0000b2e0 , 0x0000f800 , 0x0000f800 , 0x03ccc584 , 0x03ccc584 }, { 0x0000b2e4 , 0x03ff0000 , 0x03ff0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000b2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x00016044 , 0x056db2db , 0x056db2db , 0x03b6d2e4 , 0x03b6d2e4 }, { 0x00016048 , 0x24925666 , 0x24925666 , 0x8e481266 , 0x8e481266 }, { 0x00016280 , 0x01000015 , 0x01000015 , 0x01001015 , 0x01001015 }, { 0x00016288 , 0x30318000 , 0x30318000 , 0x00318000 , 0x00318000 }, //top3 { 0x00016444 , 0x056db2db , 0x056db2db , 0x03b6d2e4 , 0x03b6d2e4 }, { 0x00016448 , 0x24925666 , 0x24925666 , 0x8e481266 , 0x8e481266 }, }; static const u_int32_t ar9340Modes_cus227_tx_gain_table_wasp_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x0000a2dc , 0x0380c7fc , 0x0380c7fc , 0x03aaa352 , 0x03aaa352 }, { 0x0000a2e0 , 0x0000f800 , 0x0000f800 , 0x03ccc584 , 0x03ccc584 }, { 0x0000a2e4 , 0x03ff0000 , 0x03ff0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000a2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000a410 , 0x000050d9 , 0x000050d9 , 0x000050d9 , 0x000050d9 }, { 0x0000a500 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a504 , 0x06000003 , 0x06000003 , 0x04000002 , 0x04000002 }, { 0x0000a508 , 0x0a000020 , 0x0a000020 , 0x08000004 , 0x08000004 }, { 0x0000a50c , 0x10000023 , 0x10000023 , 0x0b000200 , 0x0b000200 }, { 0x0000a510 , 0x16000220 , 0x16000220 , 0x0f000202 , 0x0f000202 }, { 0x0000a514 , 0x1c000223 , 0x1c000223 , 0x11000400 , 0x11000400 }, { 0x0000a518 , 0x21002220 , 0x21002220 , 0x15000402 , 0x15000402 }, { 0x0000a51c , 0x27002223 , 0x27002223 , 0x19000404 , 0x19000404 }, { 0x0000a520 , 0x2c022220 , 0x2c022220 , 0x1b000603 , 0x1b000603 }, { 0x0000a524 , 0x30022222 , 0x30022222 , 0x1f000a02 , 0x1f000a02 }, { 0x0000a528 , 0x35022225 , 0x35022225 , 0x23000a04 , 0x23000a04 }, { 0x0000a52c , 0x3b02222a , 0x3b02222a , 0x26000a20 , 0x26000a20 }, { 0x0000a530 , 0x3f02222c , 0x3f02222c , 0x2a000e20 , 0x2a000e20 }, { 0x0000a534 , 0x4202242a , 0x4202242a , 0x2e000e22 , 0x2e000e22 }, { 0x0000a538 , 0x4702244a , 0x4702244a , 0x31000e24 , 0x31000e24 }, { 0x0000a53c , 0x4b02244c , 0x4b02244c , 0x34001640 , 0x34001640 }, { 0x0000a540 , 0x4e02246c , 0x4e02246c , 0x38001660 , 0x38001660 }, { 0x0000a544 , 0x5302266c , 0x5302266c , 0x3b001861 , 0x3b001861 }, { 0x0000a548 , 0x5702286c , 0x5702286c , 0x3e001a81 , 0x3e001a81 }, { 0x0000a54c , 0x5c02486b , 0x5c02486b , 0x42001a83 , 0x42001a83 }, { 0x0000a550 , 0x61024a6c , 0x61024a6c , 0x44001c84 , 0x44001c84 }, { 0x0000a554 , 0x66026a6c , 0x66026a6c , 0x48001ce3 , 0x48001ce3 }, { 0x0000a558 , 0x6b026e6c , 0x6b026e6c , 0x4c001ce5 , 0x4c001ce5 }, { 0x0000a55c , 0x7002708c , 0x7002708c , 0x50001ce9 , 0x50001ce9 }, { 0x0000a560 , 0x7302b08a , 0x7302b08a , 0x54001ceb , 0x54001ceb }, { 0x0000a564 , 0x7702b08c , 0x7702b08c , 0x56001eec , 0x56001eec }, { 0x0000a568 , 0x7702b08c , 0x7702b08c , 0x56001eec , 0x56001eec }, { 0x0000a56c , 0x7702b08c , 0x7702b08c , 0x56001eec , 0x56001eec }, { 0x0000a570 , 0x7702b08c , 0x7702b08c , 0x56001eec , 0x56001eec }, { 0x0000a574 , 0x7702b08c , 0x7702b08c , 0x56001eec , 0x56001eec }, { 0x0000a578 , 0x7702b08c , 0x7702b08c , 0x56001eec , 0x56001eec }, { 0x0000a57c , 0x7702b08c , 0x7702b08c , 0x56001eec , 0x56001eec }, { 0x0000a580 , 0x00800000 , 0x00800000 , 0x00800000 , 0x00800000 }, { 0x0000a584 , 0x06800003 , 0x06800003 , 0x04800002 , 0x04800002 }, { 0x0000a588 , 0x0a800020 , 0x0a800020 , 0x08800004 , 0x08800004 }, { 0x0000a58c , 0x10800023 , 0x10800023 , 0x0b800200 , 0x0b800200 }, { 0x0000a590 , 0x16800220 , 0x16800220 , 0x0f800202 , 0x0f800202 }, { 0x0000a594 , 0x1c800223 , 0x1c800223 , 0x11800400 , 0x11800400 }, { 0x0000a598 , 0x21820220 , 0x21820220 , 0x15800402 , 0x15800402 }, { 0x0000a59c , 0x27820223 , 0x27820223 , 0x19800404 , 0x19800404 }, { 0x0000a5a0 , 0x2b822220 , 0x2b822220 , 0x1b800603 , 0x1b800603 }, { 0x0000a5a4 , 0x2f822222 , 0x2f822222 , 0x1f800a02 , 0x1f800a02 }, { 0x0000a5a8 , 0x34822225 , 0x34822225 , 0x23800a04 , 0x23800a04 }, { 0x0000a5ac , 0x3a82222a , 0x3a82222a , 0x26800a20 , 0x26800a20 }, { 0x0000a5b0 , 0x3e82222c , 0x3e82222c , 0x2a800e20 , 0x2a800e20 }, { 0x0000a5b4 , 0x4282242a , 0x4282242a , 0x2e800e22 , 0x2e800e22 }, { 0x0000a5b8 , 0x4782244a , 0x4782244a , 0x31800e24 , 0x31800e24 }, { 0x0000a5bc , 0x4b82244c , 0x4b82244c , 0x34801640 , 0x34801640 }, { 0x0000a5c0 , 0x4e82246c , 0x4e82246c , 0x38801660 , 0x38801660 }, { 0x0000a5c4 , 0x5382266c , 0x5382266c , 0x3b801861 , 0x3b801861 }, { 0x0000a5c8 , 0x5782286c , 0x5782286c , 0x3e801a81 , 0x3e801a81 }, { 0x0000a5cc , 0x5c84286b , 0x5c84286b , 0x42801a83 , 0x42801a83 }, { 0x0000a5d0 , 0x61842a6c , 0x61842a6c , 0x44801c84 , 0x44801c84 }, { 0x0000a5d4 , 0x66862a6c , 0x66862a6c , 0x48801ce3 , 0x48801ce3 }, { 0x0000a5d8 , 0x6b862e6c , 0x6b862e6c , 0x4c801ce5 , 0x4c801ce5 }, { 0x0000a5dc , 0x7086308c , 0x7086308c , 0x50801ce9 , 0x50801ce9 }, { 0x0000a5e0 , 0x738a308a , 0x738a308a , 0x54801ceb , 0x54801ceb }, { 0x0000a5e4 , 0x778a308c , 0x778a308c , 0x56801eec , 0x56801eec }, { 0x0000a5e8 , 0x778a308c , 0x778a308c , 0x56801eec , 0x56801eec }, { 0x0000a5ec , 0x778a308c , 0x778a308c , 0x56801eec , 0x56801eec }, { 0x0000a5f0 , 0x778a308c , 0x778a308c , 0x56801eec , 0x56801eec }, { 0x0000a5f4 , 0x778a308c , 0x778a308c , 0x56801eec , 0x56801eec }, { 0x0000a5f8 , 0x778a308c , 0x778a308c , 0x56801eec , 0x56801eec }, { 0x0000a5fc , 0x778a308c , 0x778a308c , 0x56801eec , 0x56801eec }, { 0x0000a600 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a604 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a608 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a60c , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a610 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a614 , 0x01404000 , 0x01404000 , 0x01404000 , 0x01404000 }, { 0x0000a618 , 0x01404501 , 0x01404501 , 0x01404501 , 0x01404501 }, { 0x0000a61c , 0x02008802 , 0x02008802 , 0x02008501 , 0x02008501 }, { 0x0000a620 , 0x0300cc03 , 0x0300cc03 , 0x0280ca03 , 0x0280ca03 }, { 0x0000a624 , 0x0300cc03 , 0x0300cc03 , 0x03010c04 , 0x03010c04 }, { 0x0000a628 , 0x0300cc03 , 0x0300cc03 , 0x04014c04 , 0x04014c04 }, { 0x0000a62c , 0x03810c03 , 0x03810c03 , 0x04015005 , 0x04015005 }, { 0x0000a630 , 0x03810e04 , 0x03810e04 , 0x04015005 , 0x04015005 }, { 0x0000a634 , 0x03810e04 , 0x03810e04 , 0x04015005 , 0x04015005 }, { 0x0000a638 , 0x03810e04 , 0x03810e04 , 0x04015005 , 0x04015005 }, { 0x0000a63c , 0x03810e04 , 0x03810e04 , 0x04015005 , 0x04015005 }, { 0x0000b2dc , 0x0380c7fc , 0x0380c7fc , 0x03aaa352 , 0x03aaa352 }, { 0x0000b2e0 , 0x0000f800 , 0x0000f800 , 0x03ccc584 , 0x03ccc584 }, { 0x0000b2e4 , 0x03ff0000 , 0x03ff0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000b2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x00016044 , 0x056db2db , 0x056db2db , 0x03b6d2e4 , 0x03b6d2e4 }, { 0x00016048 , 0x24925666 , 0x24925666 , 0x8e481266 , 0x8e481266 }, { 0x00016280 , 0x01000015 , 0x01000015 , 0x01001015 , 0x01001015 }, { 0x00016288 , 0x30318000 , 0x30318000 , 0x00318000 , 0x00318000 }, //top3 { 0x00016444 , 0x056db2db , 0x056db2db , 0x03b6d2e4 , 0x03b6d2e4 }, { 0x00016448 , 0x24925666 , 0x24925666 , 0x8e481266 , 0x8e481266 }, { 0x0000a3a4 , 0x00000011 , 0x00000011 , 0x00000011 , 0x00000011 }, { 0x0000a3a8 , 0x3c3c3c3c , 0x3c3c3c3c , 0x3c3c3c3c , 0x3c3c3c3c }, { 0x0000a3ac , 0x30303030 , 0x30303030 , 0x30303030 , 0x30303030 }, }; static const u_int32_t ar9340_wasp_1p0_mac_core[][2] = { /* Addr allmodes */ { 0x00000008 , 0x00000000 }, { 0x00000030 , 0x00020085 }, { 0x00000034 , 0x00000005 }, { 0x00000040 , 0x00000000 }, { 0x00000044 , 0x00000000 }, { 0x00000048 , 0x00000008 }, { 0x0000004c , 0x00000010 }, { 0x00000050 , 0x00000000 }, { 0x00001040 , 0x002ffc0f }, { 0x00001044 , 0x002ffc0f }, { 0x00001048 , 0x002ffc0f }, { 0x0000104c , 0x002ffc0f }, { 0x00001050 , 0x002ffc0f }, { 0x00001054 , 0x002ffc0f }, { 0x00001058 , 0x002ffc0f }, { 0x0000105c , 0x002ffc0f }, { 0x00001060 , 0x002ffc0f }, { 0x00001064 , 0x002ffc0f }, { 0x000010f0 , 0x00000100 }, { 0x00001270 , 0x00000000 }, { 0x000012b0 , 0x00000000 }, { 0x000012f0 , 0x00000000 }, { 0x0000143c , 0x00000000 }, { 0x0000147c , 0x00000000 }, { 0x00008000 , 0x00000000 }, { 0x00008004 , 0x00000000 }, { 0x00008008 , 0x00000000 }, { 0x0000800c , 0x00000000 }, { 0x00008010 , 0x00080800 }, { 0x00008018 , 0x00000000 }, { 0x00008020 , 0x00000000 }, { 0x00008038 , 0x00000000 }, { 0x0000803c , 0x00000000 }, { 0x00008040 , 0x00000000 }, { 0x00008044 , 0x00000000 }, { 0x00008048 , 0x00000000 }, { 0x0000804c , 0xffffffff }, { 0x00008054 , 0x00000000 }, { 0x00008058 , 0x00000000 }, { 0x0000805c , 0x000fc78f }, { 0x00008060 , 0x0000000f }, { 0x00008064 , 0x00000000 }, { 0x00008070 , 0x00000310 }, { 0x00008074 , 0x00000020 }, { 0x00008078 , 0x00000000 }, { 0x0000809c , 0x0000000f }, { 0x000080a0 , 0x00000000 }, { 0x000080a4 , 0x02ff0000 }, { 0x000080a8 , 0x0e070605 }, { 0x000080ac , 0x0000000d }, { 0x000080b0 , 0x00000000 }, { 0x000080b4 , 0x00000000 }, { 0x000080b8 , 0x00000000 }, { 0x000080bc , 0x00000000 }, { 0x000080c0 , 0x2a800000 }, { 0x000080c4 , 0x06900168 }, { 0x000080c8 , 0x13881c22 }, { 0x000080cc , 0x01f40000 }, { 0x000080d0 , 0x00252500 }, { 0x000080d4 , 0x00a00000 }, { 0x000080d8 , 0x00400000 }, { 0x000080dc , 0x00000000 }, { 0x000080e0 , 0xffffffff }, { 0x000080e4 , 0x0000ffff }, { 0x000080e8 , 0x3f3f3f3f }, { 0x000080ec , 0x00000000 }, { 0x000080f0 , 0x00000000 }, { 0x000080f4 , 0x00000000 }, { 0x000080fc , 0x00020000 }, { 0x00008100 , 0x00000000 }, { 0x00008108 , 0x00000052 }, { 0x0000810c , 0x00000000 }, { 0x00008110 , 0x00000000 }, { 0x00008114 , 0x000007ff }, { 0x00008118 , 0x000000aa }, { 0x0000811c , 0x00003210 }, { 0x00008124 , 0x00000000 }, { 0x00008128 , 0x00000000 }, { 0x0000812c , 0x00000000 }, { 0x00008130 , 0x00000000 }, { 0x00008134 , 0x00000000 }, { 0x00008138 , 0x00000000 }, { 0x0000813c , 0x0000ffff }, { 0x00008144 , 0xffffffff }, { 0x00008168 , 0x00000000 }, { 0x0000816c , 0x00000000 }, { 0x00008170 , 0x18486200 }, { 0x00008174 , 0x33332210 }, { 0x00008178 , 0x00000000 }, { 0x0000817c , 0x00020000 }, { 0x000081c0 , 0x00000000 }, { 0x000081c4 , 0x33332210 }, { 0x000081c8 , 0x00000000 }, { 0x000081cc , 0x00000000 }, { 0x000081d4 , 0x00000000 }, { 0x000081ec , 0x00000000 }, { 0x000081f0 , 0x00000000 }, { 0x000081f4 , 0x00000000 }, { 0x000081f8 , 0x00000000 }, { 0x000081fc , 0x00000000 }, { 0x00008240 , 0x00100000 }, { 0x00008244 , 0x0010f3d7 }, { 0x00008248 , 0x00000800 }, { 0x0000824c , 0x0001e7ae }, { 0x00008250 , 0x00000000 }, { 0x00008254 , 0x00000000 }, { 0x00008258 , 0x00000000 }, { 0x0000825c , 0x40000000 }, { 0x00008260 , 0x00080922 }, { 0x00008264 , 0x9d400010 }, { 0x00008268 , 0xffffffff }, { 0x0000826c , 0x0000ffff }, { 0x00008270 , 0x00000000 }, { 0x00008274 , 0x40000000 }, { 0x00008278 , 0x003e4180 }, { 0x0000827c , 0x00000004 }, { 0x00008284 , 0x0000002c }, { 0x00008288 , 0x0000002c }, { 0x0000828c , 0x000000ff }, { 0x00008294 , 0x00000000 }, { 0x00008298 , 0x00000000 }, { 0x0000829c , 0x00000000 }, { 0x00008300 , 0x00000140 }, { 0x00008314 , 0x00000000 }, { 0x0000831c , 0x0000010d }, { 0x00008328 , 0x00000000 }, { 0x0000832c , 0x00000007 }, { 0x00008330 , 0x00000302 }, { 0x00008334 , 0x00000700 }, { 0x00008338 , 0x00ff0000 }, { 0x0000833c , 0x02400000 }, { 0x00008340 , 0x000107ff }, { 0x00008344 , 0xaa48105b }, { 0x00008348 , 0x008f0000 }, { 0x0000835c , 0x00000000 }, { 0x00008360 , 0xffffffff }, { 0x00008364 , 0xffffffff }, { 0x00008368 , 0x00000000 }, { 0x00008370 , 0x00000000 }, { 0x00008374 , 0x000000ff }, { 0x00008378 , 0x00000000 }, { 0x0000837c , 0x00000000 }, { 0x00008380 , 0xffffffff }, { 0x00008384 , 0xffffffff }, { 0x00008390 , 0xffffffff }, { 0x00008394 , 0xffffffff }, { 0x00008398 , 0x00000000 }, { 0x0000839c , 0x00000000 }, { 0x000083a0 , 0x00000000 }, { 0x000083a4 , 0x0000fa14 }, { 0x000083a8 , 0x000f0c00 }, { 0x000083ac , 0x33332210 }, { 0x000083b0 , 0x33332210 }, { 0x000083b4 , 0x33332210 }, { 0x000083b8 , 0x33332210 }, { 0x000083bc , 0x00000000 }, { 0x000083c0 , 0x00000000 }, { 0x000083c4 , 0x00000000 }, { 0x000083c8 , 0x00000000 }, { 0x000083cc , 0x00000200 }, { 0x000083d0 , 0x000101ff }, }; static const u_int32_t ar9340Common_wo_xlna_rx_gain_table_wasp_1p0[][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 u_int32_t ar9340_wasp_1p0_tx_gain_table_baseband_postamble_emulation[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x0000a410 , 0x000000d5 , 0x000000d5 , 0x000000d5 , 0x000000d5 }, { 0x0000a500 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a504 , 0x00004002 , 0x00004002 , 0x00004002 , 0x00004002 }, { 0x0000a508 , 0x00008004 , 0x00008004 , 0x00008004 , 0x00008004 }, { 0x0000a510 , 0x0001000c , 0x0001000c , 0x0001000c , 0x0001000c }, { 0x0000a514 , 0x0001420b , 0x0001420b , 0x0001420b , 0x0001420b }, { 0x0000a518 , 0x0001824a , 0x0001824a , 0x0001824a , 0x0001824a }, { 0x0000a51c , 0x0001c44a , 0x0001c44a , 0x0001c44a , 0x0001c44a }, { 0x0000a520 , 0x0002064a , 0x0002064a , 0x0002064a , 0x0002064a }, { 0x0000a524 , 0x0002484a , 0x0002484a , 0x0002484a , 0x0002484a }, { 0x0000a528 , 0x00028a4a , 0x00028a4a , 0x00028a4a , 0x00028a4a }, { 0x0000a52c , 0x0002cc4a , 0x0002cc4a , 0x0002cc4a , 0x0002cc4a }, { 0x0000a530 , 0x00030e4a , 0x00030e4a , 0x00030e4a , 0x00030e4a }, { 0x0000a534 , 0x00034e8a , 0x00034e8a , 0x00034e8a , 0x00034e8a }, }; static const u_int32_t ar9340_wasp_1p0_soc_preamble[][2] = { /* Addr allmodes */ { 0x00007008 , 0x00000000 }, { 0x00007020 , 0x00000000 }, { 0x00007034 , 0x00000002 }, { 0x00007038 , 0x000004c2 }, }; Index: projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9580.ini =================================================================== --- projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9580.ini (revision 278776) +++ projects/building-blocks/sys/contrib/dev/ath/ath_hal/ar9300/ar9580.ini (revision 278777) @@ -1,2219 +1,2221 @@ /* * Copyright (c) 2013 Qualcomm Atheros, Inc. * * 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. */ static const u_int32_t ar9300_ar9580_1p0_baseband_postamble_dfs_channel[][3] = { /* Addr 5G_HT20 5G_HT40 */ { 0x00009824 , 0x5ac668d0 , 0x5ac668d0 }, + { 0x00009828 , 0x06903080 , 0x06903080 }, { 0x00009e0c , 0x6d4000e2 , 0x6d4000e2 }, { 0x00009e14 , 0x37b9625e , 0x37b9625e }, + { 0x00009814 , 0x3400c00f , 0x3400c00f }, }; static const u_int32_t ar9300Modes_fast_clock_ar9580_1p0[][3] = { /* Addr 5G_HT20 5G_HT40 */ { 0x00001030 , 0x00000268 , 0x000004d0 }, { 0x00001070 , 0x0000018c , 0x00000318 }, { 0x000010b0 , 0x00000fd0 , 0x00001fa0 }, { 0x00008014 , 0x044c044c , 0x08980898 }, { 0x0000801c , 0x148ec02b , 0x148ec057 }, { 0x00008318 , 0x000044c0 , 0x00008980 }, { 0x00009e00 , 0x0372131c , 0x0372131c }, { 0x0000a230 , 0x0000000b , 0x00000016 }, { 0x0000a254 , 0x00000898 , 0x00001130 }, }; static const u_int32_t ar9300_ar9580_1p0_baseband_postamble_emulation[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x00009e18 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x00009e3c , 0xcf946221 , 0xcf946221 , 0xcf946221 , 0xcf946221 }, { 0x00009e44 , 0x005c0000 , 0x005c0000 , 0x005c0000 , 0x005c0000 }, { 0x0000a258 , 0x02020200 , 0x02020200 , 0x02020200 , 0x02020200 }, { 0x0000a25c , 0x00000e0e , 0x00000e0e , 0x00000e0e , 0x00000e0e }, { 0x0000a28c , 0x00011111 , 0x00011111 , 0x00011111 , 0x00011111 }, { 0x0000a2c4 , 0x00148d18 , 0x00148d18 , 0x00148d20 , 0x00148d20 }, { 0x0000a2d8 , 0xf999a801 , 0xf999a801 , 0xf999a80d , 0xf999a80d }, { 0x0000a50c , 0x0000c00a , 0x0000c00a , 0x0000c00a , 0x0000c00a }, { 0x0000a538 , 0x00038e8c , 0x00038e8c , 0x00038e8c , 0x00038e8c }, { 0x0000a53c , 0x0003cecc , 0x0003cecc , 0x0003cecc , 0x0003cecc }, { 0x0000a540 , 0x00040ed4 , 0x00040ed4 , 0x00040ed4 , 0x00040ed4 }, { 0x0000a544 , 0x00044edc , 0x00044edc , 0x00044edc , 0x00044edc }, { 0x0000a548 , 0x00048ede , 0x00048ede , 0x00048ede , 0x00048ede }, { 0x0000a54c , 0x0004cf1e , 0x0004cf1e , 0x0004cf1e , 0x0004cf1e }, { 0x0000a550 , 0x00050f5e , 0x00050f5e , 0x00050f5e , 0x00050f5e }, { 0x0000a554 , 0x00054f9e , 0x00054f9e , 0x00054f9e , 0x00054f9e }, { 0x0000ae18 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000be18 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, }; static const u_int32_t ar9300_ar9580_1p0_radio_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x0001609c , 0x0dd08f29 , 0x0dd08f29 , 0x0b283f31 , 0x0b283f31 }, { 0x000160ac , 0xa4653c00 , 0xa4653c00 , 0x24652800 , 0x24652800 }, { 0x000160b0 , 0x03284f3e , 0x03284f3e , 0x05d08f20 , 0x05d08f20 }, { 0x0001610c , 0xc8000000 , 0xc0000000 , 0xc0000000 , 0xc0000000 }, { 0x00016140 , 0x10804008 , 0x10804008 , 0x50804008 , 0x50804008 }, { 0x0001650c , 0xc8000000 , 0xc0000000 , 0xc0000000 , 0xc0000000 }, { 0x00016540 , 0x10804008 , 0x10804008 , 0x50804008 , 0x50804008 }, { 0x0001690c , 0xc8000000 , 0xc0000000 , 0xc0000000 , 0xc0000000 }, { 0x00016940 , 0x10804008 , 0x10804008 , 0x50804008 , 0x50804008 }, }; static const u_int32_t ar9300_ar9580_1p0_baseband_core[][2] = { /* Addr allmodes */ { 0x00009800 , 0xafe68e30 }, { 0x00009804 , 0xfd14e000 }, { 0x00009808 , 0x9c0a9f6b }, { 0x0000980c , 0x04900000 }, { 0x00009814 , 0x3280c00a }, { 0x00009818 , 0x00000000 }, { 0x0000981c , 0x00020028 }, { 0x00009834 , 0x6400a190 }, { 0x00009838 , 0x0108ecff }, { 0x0000983c , 0x14000600 }, { 0x00009880 , 0x201fff00 }, { 0x00009884 , 0x00001042 }, { 0x000098a4 , 0x00200400 }, { 0x000098b0 , 0x32840bbe }, { 0x000098d0 , 0x004b6a8e }, { 0x000098d4 , 0x00000820 }, { 0x000098dc , 0x00000000 }, { 0x000098f0 , 0x00000000 }, { 0x000098f4 , 0x00000000 }, { 0x00009c04 , 0xff55ff55 }, { 0x00009c08 , 0x0320ff55 }, { 0x00009c0c , 0x00000000 }, { 0x00009c10 , 0x00000000 }, { 0x00009c14 , 0x00046384 }, { 0x00009c18 , 0x05b6b440 }, { 0x00009c1c , 0x00b6b440 }, { 0x00009d00 , 0xc080a333 }, { 0x00009d04 , 0x40206c10 }, { 0x00009d08 , 0x009c4060 }, { 0x00009d0c , 0x9883800a }, { 0x00009d10 , 0x01834061 }, { 0x00009d14 , 0x00c0040b }, { 0x00009d18 , 0x00000000 }, { 0x00009e08 , 0x0038230c }, { 0x00009e24 , 0x990bb515 }, { 0x00009e28 , 0x0c6f0000 }, { 0x00009e30 , 0x06336f77 }, { 0x00009e34 , 0x6af6532f }, { 0x00009e38 , 0x0cc80c00 }, { 0x00009e40 , 0x0d261820 }, { 0x00009e4c , 0x00001004 }, { 0x00009e50 , 0x00ff03f1 }, { 0x00009e54 , 0x00000000 }, { 0x00009fc0 , 0x803e4788 }, { 0x00009fc4 , 0x0001efb5 }, { 0x00009fcc , 0x40000014 }, { 0x00009fd0 , 0x01193b93 }, { 0x0000a20c , 0x00000000 }, { 0x0000a220 , 0x00000000 }, { 0x0000a224 , 0x00000000 }, { 0x0000a228 , 0x10002310 }, { 0x0000a23c , 0x00000000 }, { 0x0000a244 , 0x0c000000 }, { 0x0000a2a0 , 0x00000001 }, { 0x0000a2c0 , 0x00000001 }, { 0x0000a2c8 , 0x00000000 }, { 0x0000a2cc , 0x18c43433 }, { 0x0000a2d4 , 0x00000000 }, { 0x0000a2ec , 0x00000000 }, { 0x0000a2f0 , 0x00000000 }, { 0x0000a2f4 , 0x00000000 }, { 0x0000a2f8 , 0x00000000 }, { 0x0000a344 , 0x00000000 }, { 0x0000a34c , 0x00000000 }, { 0x0000a350 , 0x0000a000 }, { 0x0000a364 , 0x00000000 }, { 0x0000a370 , 0x00000000 }, { 0x0000a390 , 0x00000001 }, { 0x0000a394 , 0x00000444 }, { 0x0000a398 , 0x001f0e0f }, { 0x0000a39c , 0x0075393f }, { 0x0000a3a0 , 0xb79f6427 }, { 0x0000a3a4 , 0x00000000 }, { 0x0000a3a8 , 0xaaaaaaaa }, { 0x0000a3ac , 0x3c466478 }, { 0x0000a3c0 , 0x20202020 }, { 0x0000a3c4 , 0x22222220 }, { 0x0000a3c8 , 0x20200020 }, { 0x0000a3cc , 0x20202020 }, { 0x0000a3d0 , 0x20202020 }, { 0x0000a3d4 , 0x20202020 }, { 0x0000a3d8 , 0x20202020 }, { 0x0000a3dc , 0x20202020 }, { 0x0000a3e0 , 0x20202020 }, { 0x0000a3e4 , 0x20202020 }, { 0x0000a3e8 , 0x20202020 }, { 0x0000a3ec , 0x20202020 }, { 0x0000a3f0 , 0x00000000 }, { 0x0000a3f4 , 0x00000000 }, { 0x0000a3f8 , 0x0c9bd380 }, { 0x0000a3fc , 0x000f0f01 }, { 0x0000a400 , 0x8fa91f01 }, { 0x0000a404 , 0x00000000 }, { 0x0000a408 , 0x0e79e5c6 }, { 0x0000a40c , 0x00820820 }, { 0x0000a414 , 0x1ce739ce }, { 0x0000a418 , 0x2d001dce }, { 0x0000a41c , 0x1ce739ce }, { 0x0000a420 , 0x000001ce }, { 0x0000a424 , 0x1ce739ce }, { 0x0000a428 , 0x000001ce }, { 0x0000a42c , 0x1ce739ce }, { 0x0000a430 , 0x1ce739ce }, { 0x0000a434 , 0x00000000 }, { 0x0000a438 , 0x00001801 }, { 0x0000a43c , 0x00100000 }, { 0x0000a440 , 0x00000000 }, { 0x0000a444 , 0x00000000 }, { 0x0000a448 , 0x05000080 }, { 0x0000a44c , 0x00000001 }, { 0x0000a450 , 0x00010000 }, { 0x0000a458 , 0x00000000 }, { 0x0000a640 , 0x00000000 }, { 0x0000a644 , 0x3fad9d74 }, { 0x0000a648 , 0x0048060a }, { 0x0000a64c , 0x00003c37 }, { 0x0000a670 , 0x03020100 }, { 0x0000a674 , 0x09080504 }, { 0x0000a678 , 0x0d0c0b0a }, { 0x0000a67c , 0x13121110 }, { 0x0000a680 , 0x31301514 }, { 0x0000a684 , 0x35343332 }, { 0x0000a688 , 0x00000036 }, { 0x0000a690 , 0x00000838 }, { 0x0000a7c0 , 0x00000000 }, { 0x0000a7c4 , 0xfffffffc }, { 0x0000a7c8 , 0x00000000 }, { 0x0000a7cc , 0x00000000 }, { 0x0000a7d0 , 0x00000000 }, { 0x0000a7d4 , 0x00000004 }, { 0x0000a7dc , 0x00000000 }, { 0x0000a8d0 , 0x004b6a8e }, { 0x0000a8d4 , 0x00000820 }, { 0x0000a8dc , 0x00000000 }, { 0x0000a8f0 , 0x00000000 }, { 0x0000a8f4 , 0x00000000 }, { 0x0000b2d0 , 0x00000080 }, { 0x0000b2d4 , 0x00000000 }, { 0x0000b2ec , 0x00000000 }, { 0x0000b2f0 , 0x00000000 }, { 0x0000b2f4 , 0x00000000 }, { 0x0000b2f8 , 0x00000000 }, { 0x0000b408 , 0x0e79e5c0 }, { 0x0000b40c , 0x00820820 }, { 0x0000b420 , 0x00000000 }, { 0x0000b8d0 , 0x004b6a8e }, { 0x0000b8d4 , 0x00000820 }, { 0x0000b8dc , 0x00000000 }, { 0x0000b8f0 , 0x00000000 }, { 0x0000b8f4 , 0x00000000 }, { 0x0000c2d0 , 0x00000080 }, { 0x0000c2d4 , 0x00000000 }, { 0x0000c2ec , 0x00000000 }, { 0x0000c2f0 , 0x00000000 }, { 0x0000c2f4 , 0x00000000 }, { 0x0000c2f8 , 0x00000000 }, { 0x0000c408 , 0x0e79e5c0 }, { 0x0000c40c , 0x00820820 }, { 0x0000c420 , 0x00000000 }, }; static const u_int32_t ar9300_ar9580_1p0_mac_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x00001030 , 0x00000230 , 0x00000460 , 0x000002c0 , 0x00000160 }, { 0x00001070 , 0x00000168 , 0x000002d0 , 0x00000318 , 0x0000018c }, { 0x000010b0 , 0x00000e60 , 0x00001cc0 , 0x00007c70 , 0x00003e38 }, { 0x00008014 , 0x03e803e8 , 0x07d007d0 , 0x10801600 , 0x08400b00 }, { 0x0000801c , 0x128d8027 , 0x128d804f , 0x12e00057 , 0x12e0002b }, - { 0x00008120 , 0x08f04800 , 0x08f04800 , 0x08f04810 , 0x08f04810 }, + { 0x00008120 , 0x18f04800 , 0x18f04800 , 0x18f04810 , 0x18f04810 }, { 0x000081d0 , 0x00003210 , 0x00003210 , 0x0000320a , 0x0000320a }, { 0x00008318 , 0x00003e80 , 0x00007d00 , 0x00006880 , 0x00003440 }, }; static const u_int32_t ar9300Modes_low_ob_db_tx_gain_table_ar9580_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x0000a2dc , 0x0380c7fc , 0x0380c7fc , 0x03aaa352 , 0x03aaa352 }, { 0x0000a2e0 , 0x0000f800 , 0x0000f800 , 0x03ccc584 , 0x03ccc584 }, { 0x0000a2e4 , 0x03ff0000 , 0x03ff0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000a2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000a410 , 0x000050d9 , 0x000050d9 , 0x000050d9 , 0x000050d9 }, { 0x0000a500 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a504 , 0x06000003 , 0x06000003 , 0x04000002 , 0x04000002 }, { 0x0000a508 , 0x0a000020 , 0x0a000020 , 0x08000004 , 0x08000004 }, { 0x0000a50c , 0x10000023 , 0x10000023 , 0x0b000200 , 0x0b000200 }, { 0x0000a510 , 0x16000220 , 0x16000220 , 0x0f000202 , 0x0f000202 }, { 0x0000a514 , 0x1c000223 , 0x1c000223 , 0x12000400 , 0x12000400 }, { 0x0000a518 , 0x21002220 , 0x21002220 , 0x16000402 , 0x16000402 }, { 0x0000a51c , 0x27002223 , 0x27002223 , 0x19000404 , 0x19000404 }, { 0x0000a520 , 0x2b022220 , 0x2b022220 , 0x1c000603 , 0x1c000603 }, { 0x0000a524 , 0x2f022222 , 0x2f022222 , 0x21000a02 , 0x21000a02 }, { 0x0000a528 , 0x34022225 , 0x34022225 , 0x25000a04 , 0x25000a04 }, { 0x0000a52c , 0x3a02222a , 0x3a02222a , 0x28000a20 , 0x28000a20 }, { 0x0000a530 , 0x3e02222c , 0x3e02222c , 0x2c000e20 , 0x2c000e20 }, { 0x0000a534 , 0x4202242a , 0x4202242a , 0x30000e22 , 0x30000e22 }, { 0x0000a538 , 0x4702244a , 0x4702244a , 0x34000e24 , 0x34000e24 }, { 0x0000a53c , 0x4b02244c , 0x4b02244c , 0x38001640 , 0x38001640 }, { 0x0000a540 , 0x4e02246c , 0x4e02246c , 0x3c001660 , 0x3c001660 }, { 0x0000a544 , 0x5302266c , 0x5302266c , 0x3f001861 , 0x3f001861 }, { 0x0000a548 , 0x5702286c , 0x5702286c , 0x43001a81 , 0x43001a81 }, { 0x0000a54c , 0x5c02486b , 0x5c02486b , 0x47001a83 , 0x47001a83 }, { 0x0000a550 , 0x61024a6c , 0x61024a6c , 0x4a001c84 , 0x4a001c84 }, { 0x0000a554 , 0x66026a6c , 0x66026a6c , 0x4e001ce3 , 0x4e001ce3 }, { 0x0000a558 , 0x6b026e6c , 0x6b026e6c , 0x52001ce5 , 0x52001ce5 }, { 0x0000a55c , 0x7002708c , 0x7002708c , 0x56001ce9 , 0x56001ce9 }, { 0x0000a560 , 0x7302b08a , 0x7302b08a , 0x5a001ceb , 0x5a001ceb }, { 0x0000a564 , 0x7702b08c , 0x7702b08c , 0x5d001eec , 0x5d001eec }, { 0x0000a568 , 0x7702b08c , 0x7702b08c , 0x5d001eec , 0x5d001eec }, { 0x0000a56c , 0x7702b08c , 0x7702b08c , 0x5d001eec , 0x5d001eec }, { 0x0000a570 , 0x7702b08c , 0x7702b08c , 0x5d001eec , 0x5d001eec }, { 0x0000a574 , 0x7702b08c , 0x7702b08c , 0x5d001eec , 0x5d001eec }, { 0x0000a578 , 0x7702b08c , 0x7702b08c , 0x5d001eec , 0x5d001eec }, { 0x0000a57c , 0x7702b08c , 0x7702b08c , 0x5d001eec , 0x5d001eec }, { 0x0000a580 , 0x00800000 , 0x00800000 , 0x00800000 , 0x00800000 }, { 0x0000a584 , 0x06800003 , 0x06800003 , 0x04800002 , 0x04800002 }, { 0x0000a588 , 0x0a800020 , 0x0a800020 , 0x08800004 , 0x08800004 }, { 0x0000a58c , 0x10800023 , 0x10800023 , 0x0b800200 , 0x0b800200 }, { 0x0000a590 , 0x16800220 , 0x16800220 , 0x0f800202 , 0x0f800202 }, { 0x0000a594 , 0x1c800223 , 0x1c800223 , 0x12800400 , 0x12800400 }, { 0x0000a598 , 0x21802220 , 0x21802220 , 0x16800402 , 0x16800402 }, { 0x0000a59c , 0x27802223 , 0x27802223 , 0x19800404 , 0x19800404 }, { 0x0000a5a0 , 0x2b822220 , 0x2b822220 , 0x1c800603 , 0x1c800603 }, { 0x0000a5a4 , 0x2f822222 , 0x2f822222 , 0x21800a02 , 0x21800a02 }, { 0x0000a5a8 , 0x34822225 , 0x34822225 , 0x25800a04 , 0x25800a04 }, { 0x0000a5ac , 0x3a82222a , 0x3a82222a , 0x28800a20 , 0x28800a20 }, { 0x0000a5b0 , 0x3e82222c , 0x3e82222c , 0x2c800e20 , 0x2c800e20 }, { 0x0000a5b4 , 0x4282242a , 0x4282242a , 0x30800e22 , 0x30800e22 }, { 0x0000a5b8 , 0x4782244a , 0x4782244a , 0x34800e24 , 0x34800e24 }, { 0x0000a5bc , 0x4b82244c , 0x4b82244c , 0x38801640 , 0x38801640 }, { 0x0000a5c0 , 0x4e82246c , 0x4e82246c , 0x3c801660 , 0x3c801660 }, { 0x0000a5c4 , 0x5382266c , 0x5382266c , 0x3f801861 , 0x3f801861 }, { 0x0000a5c8 , 0x5782286c , 0x5782286c , 0x43801a81 , 0x43801a81 }, { 0x0000a5cc , 0x5c82486b , 0x5c82486b , 0x47801a83 , 0x47801a83 }, { 0x0000a5d0 , 0x61824a6c , 0x61824a6c , 0x4a801c84 , 0x4a801c84 }, { 0x0000a5d4 , 0x66826a6c , 0x66826a6c , 0x4e801ce3 , 0x4e801ce3 }, { 0x0000a5d8 , 0x6b826e6c , 0x6b826e6c , 0x52801ce5 , 0x52801ce5 }, { 0x0000a5dc , 0x7082708c , 0x7082708c , 0x56801ce9 , 0x56801ce9 }, { 0x0000a5e0 , 0x7382b08a , 0x7382b08a , 0x5a801ceb , 0x5a801ceb }, { 0x0000a5e4 , 0x7782b08c , 0x7782b08c , 0x5d801eec , 0x5d801eec }, { 0x0000a5e8 , 0x7782b08c , 0x7782b08c , 0x5d801eec , 0x5d801eec }, { 0x0000a5ec , 0x7782b08c , 0x7782b08c , 0x5d801eec , 0x5d801eec }, { 0x0000a5f0 , 0x7782b08c , 0x7782b08c , 0x5d801eec , 0x5d801eec }, { 0x0000a5f4 , 0x7782b08c , 0x7782b08c , 0x5d801eec , 0x5d801eec }, { 0x0000a5f8 , 0x7782b08c , 0x7782b08c , 0x5d801eec , 0x5d801eec }, { 0x0000a5fc , 0x7782b08c , 0x7782b08c , 0x5d801eec , 0x5d801eec }, { 0x0000a600 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a604 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a608 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a60c , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a610 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a614 , 0x01404000 , 0x01404000 , 0x01404000 , 0x01404000 }, { 0x0000a618 , 0x01404501 , 0x01404501 , 0x01404501 , 0x01404501 }, { 0x0000a61c , 0x02008802 , 0x02008802 , 0x02008501 , 0x02008501 }, { 0x0000a620 , 0x0300cc03 , 0x0300cc03 , 0x0280ca03 , 0x0280ca03 }, { 0x0000a624 , 0x0300cc03 , 0x0300cc03 , 0x03010c04 , 0x03010c04 }, { 0x0000a628 , 0x0300cc03 , 0x0300cc03 , 0x04014c04 , 0x04014c04 }, { 0x0000a62c , 0x03810c03 , 0x03810c03 , 0x04015005 , 0x04015005 }, { 0x0000a630 , 0x03810e04 , 0x03810e04 , 0x04015005 , 0x04015005 }, { 0x0000a634 , 0x03810e04 , 0x03810e04 , 0x04015005 , 0x04015005 }, { 0x0000a638 , 0x03810e04 , 0x03810e04 , 0x04015005 , 0x04015005 }, { 0x0000a63c , 0x03810e04 , 0x03810e04 , 0x04015005 , 0x04015005 }, { 0x0000b2dc , 0x0380c7fc , 0x0380c7fc , 0x03aaa352 , 0x03aaa352 }, { 0x0000b2e0 , 0x0000f800 , 0x0000f800 , 0x03ccc584 , 0x03ccc584 }, { 0x0000b2e4 , 0x03ff0000 , 0x03ff0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000b2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000c2dc , 0x0380c7fc , 0x0380c7fc , 0x03aaa352 , 0x03aaa352 }, { 0x0000c2e0 , 0x0000f800 , 0x0000f800 , 0x03ccc584 , 0x03ccc584 }, { 0x0000c2e4 , 0x03ff0000 , 0x03ff0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000c2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x00016044 , 0x012492d4 , 0x012492d4 , 0x012492d4 , 0x012492d4 }, { 0x00016048 , 0x66480001 , 0x66480001 , 0x66480001 , 0x66480001 }, { 0x00016068 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, { 0x00016444 , 0x012492d4 , 0x012492d4 , 0x012492d4 , 0x012492d4 }, { 0x00016448 , 0x66480001 , 0x66480001 , 0x66480001 , 0x66480001 }, { 0x00016468 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, { 0x00016844 , 0x012492d4 , 0x012492d4 , 0x012492d4 , 0x012492d4 }, { 0x00016848 , 0x66480001 , 0x66480001 , 0x66480001 , 0x66480001 }, { 0x00016868 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, }; static const u_int32_t ar9300Modes_high_power_tx_gain_table_ar9580_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x0000a2dc , 0x000cfff0 , 0x000cfff0 , 0x03aaa352 , 0x03aaa352 }, { 0x0000a2e0 , 0x000f0000 , 0x000f0000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000a2e4 , 0x03f00000 , 0x03f00000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000a2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000a410 , 0x000050d9 , 0x000050d9 , 0x000050d9 , 0x000050d9 }, { 0x0000a500 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a504 , 0x06000003 , 0x06000003 , 0x04000002 , 0x04000002 }, { 0x0000a508 , 0x0a000020 , 0x0a000020 , 0x08000004 , 0x08000004 }, { 0x0000a50c , 0x10000023 , 0x10000023 , 0x0b000200 , 0x0b000200 }, { 0x0000a510 , 0x15000028 , 0x15000028 , 0x0f000202 , 0x0f000202 }, { 0x0000a514 , 0x1b00002b , 0x1b00002b , 0x12000400 , 0x12000400 }, { 0x0000a518 , 0x1f020028 , 0x1f020028 , 0x16000402 , 0x16000402 }, { 0x0000a51c , 0x2502002b , 0x2502002b , 0x19000404 , 0x19000404 }, { 0x0000a520 , 0x2a04002a , 0x2a04002a , 0x1c000603 , 0x1c000603 }, { 0x0000a524 , 0x2e06002a , 0x2e06002a , 0x21000a02 , 0x21000a02 }, { 0x0000a528 , 0x3302202d , 0x3302202d , 0x25000a04 , 0x25000a04 }, { 0x0000a52c , 0x3804202c , 0x3804202c , 0x28000a20 , 0x28000a20 }, { 0x0000a530 , 0x3c06202c , 0x3c06202c , 0x2c000e20 , 0x2c000e20 }, { 0x0000a534 , 0x4108202d , 0x4108202d , 0x30000e22 , 0x30000e22 }, { 0x0000a538 , 0x4506402d , 0x4506402d , 0x34000e24 , 0x34000e24 }, { 0x0000a53c , 0x4906222d , 0x4906222d , 0x38001640 , 0x38001640 }, { 0x0000a540 , 0x4d062231 , 0x4d062231 , 0x3c001660 , 0x3c001660 }, { 0x0000a544 , 0x50082231 , 0x50082231 , 0x3f001861 , 0x3f001861 }, { 0x0000a548 , 0x5608422e , 0x5608422e , 0x43001a81 , 0x43001a81 }, { 0x0000a54c , 0x5e08442e , 0x5e08442e , 0x47001a83 , 0x47001a83 }, { 0x0000a550 , 0x620a4431 , 0x620a4431 , 0x4a001c84 , 0x4a001c84 }, { 0x0000a554 , 0x640a4432 , 0x640a4432 , 0x4e001ce3 , 0x4e001ce3 }, { 0x0000a558 , 0x680a4434 , 0x680a4434 , 0x52001ce5 , 0x52001ce5 }, { 0x0000a55c , 0x6c0a6434 , 0x6c0a6434 , 0x56001ce9 , 0x56001ce9 }, { 0x0000a560 , 0x6f0a6633 , 0x6f0a6633 , 0x5a001ceb , 0x5a001ceb }, { 0x0000a564 , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a568 , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a56c , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a570 , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a574 , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a578 , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a57c , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a580 , 0x00800000 , 0x00800000 , 0x00800000 , 0x00800000 }, { 0x0000a584 , 0x06800003 , 0x06800003 , 0x04800002 , 0x04800002 }, { 0x0000a588 , 0x0a800020 , 0x0a800020 , 0x08800004 , 0x08800004 }, { 0x0000a58c , 0x10800023 , 0x10800023 , 0x0b800200 , 0x0b800200 }, { 0x0000a590 , 0x15800028 , 0x15800028 , 0x0f800202 , 0x0f800202 }, { 0x0000a594 , 0x1b80002b , 0x1b80002b , 0x12800400 , 0x12800400 }, { 0x0000a598 , 0x1f820028 , 0x1f820028 , 0x16800402 , 0x16800402 }, { 0x0000a59c , 0x2582002b , 0x2582002b , 0x19800404 , 0x19800404 }, { 0x0000a5a0 , 0x2a84002a , 0x2a84002a , 0x1c800603 , 0x1c800603 }, { 0x0000a5a4 , 0x2e86002a , 0x2e86002a , 0x21800a02 , 0x21800a02 }, { 0x0000a5a8 , 0x3382202d , 0x3382202d , 0x25800a04 , 0x25800a04 }, { 0x0000a5ac , 0x3884202c , 0x3884202c , 0x28800a20 , 0x28800a20 }, { 0x0000a5b0 , 0x3c86202c , 0x3c86202c , 0x2c800e20 , 0x2c800e20 }, { 0x0000a5b4 , 0x4188202d , 0x4188202d , 0x30800e22 , 0x30800e22 }, { 0x0000a5b8 , 0x4586402d , 0x4586402d , 0x34800e24 , 0x34800e24 }, { 0x0000a5bc , 0x4986222d , 0x4986222d , 0x38801640 , 0x38801640 }, { 0x0000a5c0 , 0x4d862231 , 0x4d862231 , 0x3c801660 , 0x3c801660 }, { 0x0000a5c4 , 0x50882231 , 0x50882231 , 0x3f801861 , 0x3f801861 }, { 0x0000a5c8 , 0x5688422e , 0x5688422e , 0x43801a81 , 0x43801a81 }, { 0x0000a5cc , 0x5a88442e , 0x5a88442e , 0x47801a83 , 0x47801a83 }, { 0x0000a5d0 , 0x5e8a4431 , 0x5e8a4431 , 0x4a801c84 , 0x4a801c84 }, { 0x0000a5d4 , 0x648a4432 , 0x648a4432 , 0x4e801ce3 , 0x4e801ce3 }, { 0x0000a5d8 , 0x688a4434 , 0x688a4434 , 0x52801ce5 , 0x52801ce5 }, { 0x0000a5dc , 0x6c8a6434 , 0x6c8a6434 , 0x56801ce9 , 0x56801ce9 }, { 0x0000a5e0 , 0x6f8a6633 , 0x6f8a6633 , 0x5a801ceb , 0x5a801ceb }, { 0x0000a5e4 , 0x738c6634 , 0x738c6634 , 0x5d801eec , 0x5d801eec }, { 0x0000a5e8 , 0x738c6634 , 0x738c6634 , 0x5d801eec , 0x5d801eec }, { 0x0000a5ec , 0x738c6634 , 0x738c6634 , 0x5d801eec , 0x5d801eec }, { 0x0000a5f0 , 0x738c6634 , 0x738c6634 , 0x5d801eec , 0x5d801eec }, { 0x0000a5f4 , 0x738c6634 , 0x738c6634 , 0x5d801eec , 0x5d801eec }, { 0x0000a5f8 , 0x738c6634 , 0x738c6634 , 0x5d801eec , 0x5d801eec }, { 0x0000a5fc , 0x738c6634 , 0x738c6634 , 0x5d801eec , 0x5d801eec }, { 0x0000a600 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a604 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a608 , 0x01804601 , 0x01804601 , 0x00000000 , 0x00000000 }, { 0x0000a60c , 0x01804601 , 0x01804601 , 0x00000000 , 0x00000000 }, { 0x0000a610 , 0x01804601 , 0x01804601 , 0x00000000 , 0x00000000 }, { 0x0000a614 , 0x01804601 , 0x01804601 , 0x01404000 , 0x01404000 }, { 0x0000a618 , 0x01804601 , 0x01804601 , 0x01404501 , 0x01404501 }, { 0x0000a61c , 0x01804601 , 0x01804601 , 0x02008501 , 0x02008501 }, { 0x0000a620 , 0x03408d02 , 0x03408d02 , 0x0280ca03 , 0x0280ca03 }, { 0x0000a624 , 0x0300cc03 , 0x0300cc03 , 0x03010c04 , 0x03010c04 }, { 0x0000a628 , 0x03410d04 , 0x03410d04 , 0x04014c04 , 0x04014c04 }, { 0x0000a62c , 0x03410d04 , 0x03410d04 , 0x04015005 , 0x04015005 }, { 0x0000a630 , 0x03410d04 , 0x03410d04 , 0x04015005 , 0x04015005 }, { 0x0000a634 , 0x03410d04 , 0x03410d04 , 0x04015005 , 0x04015005 }, { 0x0000a638 , 0x03410d04 , 0x03410d04 , 0x04015005 , 0x04015005 }, { 0x0000a63c , 0x03410d04 , 0x03410d04 , 0x04015005 , 0x04015005 }, { 0x0000b2dc , 0x000cfff0 , 0x000cfff0 , 0x03aaa352 , 0x03aaa352 }, { 0x0000b2e0 , 0x000f0000 , 0x000f0000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000b2e4 , 0x03f00000 , 0x03f00000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000b2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000c2dc , 0x000cfff0 , 0x000cfff0 , 0x03aaa352 , 0x03aaa352 }, { 0x0000c2e0 , 0x000f0000 , 0x000f0000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000c2e4 , 0x03f00000 , 0x03f00000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000c2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x00016044 , 0x012492d4 , 0x012492d4 , 0x012492d4 , 0x012492d4 }, { 0x00016048 , 0x65240001 , 0x65240001 , 0x66480001 , 0x66480001 }, { 0x00016068 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, { 0x00016288 , 0x05a2040a , 0x05a2040a , 0x05a20408 , 0x05a20408 }, { 0x00016444 , 0x012492d4 , 0x012492d4 , 0x012492d4 , 0x012492d4 }, { 0x00016448 , 0x65240001 , 0x65240001 , 0x66480001 , 0x66480001 }, { 0x00016468 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, { 0x00016844 , 0x012492d4 , 0x012492d4 , 0x012492d4 , 0x012492d4 }, { 0x00016848 , 0x65240001 , 0x65240001 , 0x66480001 , 0x66480001 }, { 0x00016868 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, }; static const u_int32_t ar9300Common_rx_gain_table_merlin_1p0[][2] = { /* Addr allmodes */ { 0x0000a000 , 0x02000101 }, { 0x0000a004 , 0x02000102 }, { 0x0000a008 , 0x02000103 }, { 0x0000a00c , 0x02000104 }, { 0x0000a010 , 0x02000200 }, { 0x0000a014 , 0x02000201 }, { 0x0000a018 , 0x02000202 }, { 0x0000a01c , 0x02000203 }, { 0x0000a020 , 0x02000204 }, { 0x0000a024 , 0x02000205 }, { 0x0000a028 , 0x02000208 }, { 0x0000a02c , 0x02000302 }, { 0x0000a030 , 0x02000303 }, { 0x0000a034 , 0x02000304 }, { 0x0000a038 , 0x02000400 }, { 0x0000a03c , 0x02010300 }, { 0x0000a040 , 0x02010301 }, { 0x0000a044 , 0x02010302 }, { 0x0000a048 , 0x02000500 }, { 0x0000a04c , 0x02010400 }, { 0x0000a050 , 0x02020300 }, { 0x0000a054 , 0x02020301 }, { 0x0000a058 , 0x02020302 }, { 0x0000a05c , 0x02020303 }, { 0x0000a060 , 0x02020400 }, { 0x0000a064 , 0x02030300 }, { 0x0000a068 , 0x02030301 }, { 0x0000a06c , 0x02030302 }, { 0x0000a070 , 0x02030303 }, { 0x0000a074 , 0x02030400 }, { 0x0000a078 , 0x02040300 }, { 0x0000a07c , 0x02040301 }, { 0x0000a080 , 0x02040302 }, { 0x0000a084 , 0x02040303 }, { 0x0000a088 , 0x02030500 }, { 0x0000a08c , 0x02040400 }, { 0x0000a090 , 0x02050203 }, { 0x0000a094 , 0x02050204 }, { 0x0000a098 , 0x02050205 }, { 0x0000a09c , 0x02040500 }, { 0x0000a0a0 , 0x02050301 }, { 0x0000a0a4 , 0x02050302 }, { 0x0000a0a8 , 0x02050303 }, { 0x0000a0ac , 0x02050400 }, { 0x0000a0b0 , 0x02050401 }, { 0x0000a0b4 , 0x02050402 }, { 0x0000a0b8 , 0x02050403 }, { 0x0000a0bc , 0x02050500 }, { 0x0000a0c0 , 0x02050501 }, { 0x0000a0c4 , 0x02050502 }, { 0x0000a0c8 , 0x02050503 }, { 0x0000a0cc , 0x02050504 }, { 0x0000a0d0 , 0x02050600 }, { 0x0000a0d4 , 0x02050601 }, { 0x0000a0d8 , 0x02050602 }, { 0x0000a0dc , 0x02050603 }, { 0x0000a0e0 , 0x02050604 }, { 0x0000a0e4 , 0x02050700 }, { 0x0000a0e8 , 0x02050701 }, { 0x0000a0ec , 0x02050702 }, { 0x0000a0f0 , 0x02050703 }, { 0x0000a0f4 , 0x02050704 }, { 0x0000a0f8 , 0x02050705 }, { 0x0000a0fc , 0x02050708 }, { 0x0000a100 , 0x02050709 }, { 0x0000a104 , 0x0205070a }, { 0x0000a108 , 0x0205070b }, { 0x0000a10c , 0x0205070c }, { 0x0000a110 , 0x0205070d }, { 0x0000a114 , 0x02050710 }, { 0x0000a118 , 0x02050711 }, { 0x0000a11c , 0x02050712 }, { 0x0000a120 , 0x02050713 }, { 0x0000a124 , 0x02050714 }, { 0x0000a128 , 0x02050715 }, { 0x0000a12c , 0x02050730 }, { 0x0000a130 , 0x02050731 }, { 0x0000a134 , 0x02050732 }, { 0x0000a138 , 0x02050733 }, { 0x0000a13c , 0x02050734 }, { 0x0000a140 , 0x02050735 }, { 0x0000a144 , 0x02050750 }, { 0x0000a148 , 0x02050751 }, { 0x0000a14c , 0x02050752 }, { 0x0000a150 , 0x02050753 }, { 0x0000a154 , 0x02050754 }, { 0x0000a158 , 0x02050755 }, { 0x0000a15c , 0x02050770 }, { 0x0000a160 , 0x02050771 }, { 0x0000a164 , 0x02050772 }, { 0x0000a168 , 0x02050773 }, { 0x0000a16c , 0x02050774 }, { 0x0000a170 , 0x02050775 }, { 0x0000a174 , 0x00000776 }, { 0x0000a178 , 0x00000776 }, { 0x0000a17c , 0x00000776 }, { 0x0000a180 , 0x00000776 }, { 0x0000a184 , 0x00000776 }, { 0x0000a188 , 0x00000776 }, { 0x0000a18c , 0x00000776 }, { 0x0000a190 , 0x00000776 }, { 0x0000a194 , 0x00000776 }, { 0x0000a198 , 0x00000776 }, { 0x0000a19c , 0x00000776 }, { 0x0000a1a0 , 0x00000776 }, { 0x0000a1a4 , 0x00000776 }, { 0x0000a1a8 , 0x00000776 }, { 0x0000a1ac , 0x00000776 }, { 0x0000a1b0 , 0x00000776 }, { 0x0000a1b4 , 0x00000776 }, { 0x0000a1b8 , 0x00000776 }, { 0x0000a1bc , 0x00000776 }, { 0x0000a1c0 , 0x00000776 }, { 0x0000a1c4 , 0x00000776 }, { 0x0000a1c8 , 0x00000776 }, { 0x0000a1cc , 0x00000776 }, { 0x0000a1d0 , 0x00000776 }, { 0x0000a1d4 , 0x00000776 }, { 0x0000a1d8 , 0x00000776 }, { 0x0000a1dc , 0x00000776 }, { 0x0000a1e0 , 0x00000776 }, { 0x0000a1e4 , 0x00000776 }, { 0x0000a1e8 , 0x00000776 }, { 0x0000a1ec , 0x00000776 }, { 0x0000a1f0 , 0x00000776 }, { 0x0000a1f4 , 0x00000776 }, { 0x0000a1f8 , 0x00000776 }, { 0x0000a1fc , 0x00000776 }, { 0x0000b000 , 0x02000101 }, { 0x0000b004 , 0x02000102 }, { 0x0000b008 , 0x02000103 }, { 0x0000b00c , 0x02000104 }, { 0x0000b010 , 0x02000200 }, { 0x0000b014 , 0x02000201 }, { 0x0000b018 , 0x02000202 }, { 0x0000b01c , 0x02000203 }, { 0x0000b020 , 0x02000204 }, { 0x0000b024 , 0x02000205 }, { 0x0000b028 , 0x02000208 }, { 0x0000b02c , 0x02000302 }, { 0x0000b030 , 0x02000303 }, { 0x0000b034 , 0x02000304 }, { 0x0000b038 , 0x02000400 }, { 0x0000b03c , 0x02010300 }, { 0x0000b040 , 0x02010301 }, { 0x0000b044 , 0x02010302 }, { 0x0000b048 , 0x02000500 }, { 0x0000b04c , 0x02010400 }, { 0x0000b050 , 0x02020300 }, { 0x0000b054 , 0x02020301 }, { 0x0000b058 , 0x02020302 }, { 0x0000b05c , 0x02020303 }, { 0x0000b060 , 0x02020400 }, { 0x0000b064 , 0x02030300 }, { 0x0000b068 , 0x02030301 }, { 0x0000b06c , 0x02030302 }, { 0x0000b070 , 0x02030303 }, { 0x0000b074 , 0x02030400 }, { 0x0000b078 , 0x02040300 }, { 0x0000b07c , 0x02040301 }, { 0x0000b080 , 0x02040302 }, { 0x0000b084 , 0x02040303 }, { 0x0000b088 , 0x02030500 }, { 0x0000b08c , 0x02040400 }, { 0x0000b090 , 0x02050203 }, { 0x0000b094 , 0x02050204 }, { 0x0000b098 , 0x02050205 }, { 0x0000b09c , 0x02040500 }, { 0x0000b0a0 , 0x02050301 }, { 0x0000b0a4 , 0x02050302 }, { 0x0000b0a8 , 0x02050303 }, { 0x0000b0ac , 0x02050400 }, { 0x0000b0b0 , 0x02050401 }, { 0x0000b0b4 , 0x02050402 }, { 0x0000b0b8 , 0x02050403 }, { 0x0000b0bc , 0x02050500 }, { 0x0000b0c0 , 0x02050501 }, { 0x0000b0c4 , 0x02050502 }, { 0x0000b0c8 , 0x02050503 }, { 0x0000b0cc , 0x02050504 }, { 0x0000b0d0 , 0x02050600 }, { 0x0000b0d4 , 0x02050601 }, { 0x0000b0d8 , 0x02050602 }, { 0x0000b0dc , 0x02050603 }, { 0x0000b0e0 , 0x02050604 }, { 0x0000b0e4 , 0x02050700 }, { 0x0000b0e8 , 0x02050701 }, { 0x0000b0ec , 0x02050702 }, { 0x0000b0f0 , 0x02050703 }, { 0x0000b0f4 , 0x02050704 }, { 0x0000b0f8 , 0x02050705 }, { 0x0000b0fc , 0x02050708 }, { 0x0000b100 , 0x02050709 }, { 0x0000b104 , 0x0205070a }, { 0x0000b108 , 0x0205070b }, { 0x0000b10c , 0x0205070c }, { 0x0000b110 , 0x0205070d }, { 0x0000b114 , 0x02050710 }, { 0x0000b118 , 0x02050711 }, { 0x0000b11c , 0x02050712 }, { 0x0000b120 , 0x02050713 }, { 0x0000b124 , 0x02050714 }, { 0x0000b128 , 0x02050715 }, { 0x0000b12c , 0x02050730 }, { 0x0000b130 , 0x02050731 }, { 0x0000b134 , 0x02050732 }, { 0x0000b138 , 0x02050733 }, { 0x0000b13c , 0x02050734 }, { 0x0000b140 , 0x02050735 }, { 0x0000b144 , 0x02050750 }, { 0x0000b148 , 0x02050751 }, { 0x0000b14c , 0x02050752 }, { 0x0000b150 , 0x02050753 }, { 0x0000b154 , 0x02050754 }, { 0x0000b158 , 0x02050755 }, { 0x0000b15c , 0x02050770 }, { 0x0000b160 , 0x02050771 }, { 0x0000b164 , 0x02050772 }, { 0x0000b168 , 0x02050773 }, { 0x0000b16c , 0x02050774 }, { 0x0000b170 , 0x02050775 }, { 0x0000b174 , 0x00000776 }, { 0x0000b178 , 0x00000776 }, { 0x0000b17c , 0x00000776 }, { 0x0000b180 , 0x00000776 }, { 0x0000b184 , 0x00000776 }, { 0x0000b188 , 0x00000776 }, { 0x0000b18c , 0x00000776 }, { 0x0000b190 , 0x00000776 }, { 0x0000b194 , 0x00000776 }, { 0x0000b198 , 0x00000776 }, { 0x0000b19c , 0x00000776 }, { 0x0000b1a0 , 0x00000776 }, { 0x0000b1a4 , 0x00000776 }, { 0x0000b1a8 , 0x00000776 }, { 0x0000b1ac , 0x00000776 }, { 0x0000b1b0 , 0x00000776 }, { 0x0000b1b4 , 0x00000776 }, { 0x0000b1b8 , 0x00000776 }, { 0x0000b1bc , 0x00000776 }, { 0x0000b1c0 , 0x00000776 }, { 0x0000b1c4 , 0x00000776 }, { 0x0000b1c8 , 0x00000776 }, { 0x0000b1cc , 0x00000776 }, { 0x0000b1d0 , 0x00000776 }, { 0x0000b1d4 , 0x00000776 }, { 0x0000b1d8 , 0x00000776 }, { 0x0000b1dc , 0x00000776 }, { 0x0000b1e0 , 0x00000776 }, { 0x0000b1e4 , 0x00000776 }, { 0x0000b1e8 , 0x00000776 }, { 0x0000b1ec , 0x00000776 }, { 0x0000b1f0 , 0x00000776 }, { 0x0000b1f4 , 0x00000776 }, { 0x0000b1f8 , 0x00000776 }, { 0x0000b1fc , 0x00000776 }, }; static const u_int32_t ar9300_modes_type5_tx_gain_table_ar9580_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x0000a2dc , 0x000cfff0 , 0x000cfff0 , 0x03aaa352 , 0x03aaa352 }, { 0x0000a2e0 , 0x000f0000 , 0x000f0000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000a2e4 , 0x03f00000 , 0x03f00000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000a2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000a410 , 0x000050d9 , 0x000050d9 , 0x000050d9 , 0x000050d9 }, { 0x0000a500 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a504 , 0x06000003 , 0x06000003 , 0x04000002 , 0x04000002 }, { 0x0000a508 , 0x0a000020 , 0x0a000020 , 0x08000004 , 0x08000004 }, { 0x0000a50c , 0x10000023 , 0x10000023 , 0x0b000200 , 0x0b000200 }, { 0x0000a510 , 0x15000028 , 0x15000028 , 0x0f000202 , 0x0f000202 }, { 0x0000a514 , 0x1b00002b , 0x1b00002b , 0x12000400 , 0x12000400 }, { 0x0000a518 , 0x1f020028 , 0x1f020028 , 0x16000402 , 0x16000402 }, { 0x0000a51c , 0x2502002b , 0x2502002b , 0x19000404 , 0x19000404 }, { 0x0000a520 , 0x2a04002a , 0x2a04002a , 0x1c000603 , 0x1c000603 }, { 0x0000a524 , 0x2e06002a , 0x2e06002a , 0x21000a02 , 0x21000a02 }, { 0x0000a528 , 0x3302202d , 0x3302202d , 0x25000a04 , 0x25000a04 }, { 0x0000a52c , 0x3804202c , 0x3804202c , 0x28000a20 , 0x28000a20 }, { 0x0000a530 , 0x3c06202c , 0x3c06202c , 0x2c000e20 , 0x2c000e20 }, { 0x0000a534 , 0x4108202d , 0x4108202d , 0x30000e22 , 0x30000e22 }, { 0x0000a538 , 0x4506402d , 0x4506402d , 0x34000e24 , 0x34000e24 }, { 0x0000a53c , 0x4906222d , 0x4906222d , 0x38001640 , 0x38001640 }, { 0x0000a540 , 0x4d062231 , 0x4d062231 , 0x3c001660 , 0x3c001660 }, { 0x0000a544 , 0x50082231 , 0x50082231 , 0x3f001861 , 0x3f001861 }, { 0x0000a548 , 0x5608422e , 0x5608422e , 0x43001a81 , 0x43001a81 }, { 0x0000a54c , 0x5e08442e , 0x5e08442e , 0x47001a83 , 0x47001a83 }, { 0x0000a550 , 0x620a4431 , 0x620a4431 , 0x4a001c84 , 0x4a001c84 }, { 0x0000a554 , 0x640a4432 , 0x640a4432 , 0x4e001ce3 , 0x4e001ce3 }, { 0x0000a558 , 0x680a4434 , 0x680a4434 , 0x52001ce5 , 0x52001ce5 }, { 0x0000a55c , 0x6c0a6434 , 0x6c0a6434 , 0x56001ce9 , 0x56001ce9 }, { 0x0000a560 , 0x6f0a6633 , 0x6f0a6633 , 0x5a001ceb , 0x5a001ceb }, { 0x0000a564 , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a568 , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a56c , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a570 , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a574 , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a578 , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a57c , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a600 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a604 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a608 , 0x01804601 , 0x01804601 , 0x00000000 , 0x00000000 }, { 0x0000a60c , 0x01804601 , 0x01804601 , 0x00000000 , 0x00000000 }, { 0x0000a610 , 0x01804601 , 0x01804601 , 0x00000000 , 0x00000000 }, { 0x0000a614 , 0x01804601 , 0x01804601 , 0x01404000 , 0x01404000 }, { 0x0000a618 , 0x01804601 , 0x01804601 , 0x01404501 , 0x01404501 }, { 0x0000a61c , 0x01804601 , 0x01804601 , 0x02008501 , 0x02008501 }, { 0x0000a620 , 0x03408d02 , 0x03408d02 , 0x0280ca03 , 0x0280ca03 }, { 0x0000a624 , 0x0300cc03 , 0x0300cc03 , 0x03010c04 , 0x03010c04 }, { 0x0000a628 , 0x03410d04 , 0x03410d04 , 0x04014c04 , 0x04014c04 }, { 0x0000a62c , 0x03410d04 , 0x03410d04 , 0x04015005 , 0x04015005 }, { 0x0000a630 , 0x03410d04 , 0x03410d04 , 0x04015005 , 0x04015005 }, { 0x0000a634 , 0x03410d04 , 0x03410d04 , 0x04015005 , 0x04015005 }, { 0x0000a638 , 0x03410d04 , 0x03410d04 , 0x04015005 , 0x04015005 }, { 0x0000a63c , 0x03410d04 , 0x03410d04 , 0x04015005 , 0x04015005 }, { 0x0000b2dc , 0x000cfff0 , 0x000cfff0 , 0x03aaa352 , 0x03aaa352 }, { 0x0000b2e0 , 0x000f0000 , 0x000f0000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000b2e4 , 0x03f00000 , 0x03f00000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000b2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000c2dc , 0x000cfff0 , 0x000cfff0 , 0x03aaa352 , 0x03aaa352 }, { 0x0000c2e0 , 0x000f0000 , 0x000f0000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000c2e4 , 0x03f00000 , 0x03f00000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000c2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x00016044 , 0x012492d4 , 0x012492d4 , 0x012492d4 , 0x012492d4 }, { 0x00016048 , 0x65240001 , 0x65240001 , 0x66480001 , 0x66480001 }, { 0x00016068 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, { 0x00016444 , 0x012492d4 , 0x012492d4 , 0x012492d4 , 0x012492d4 }, { 0x00016448 , 0x65240001 , 0x65240001 , 0x66480001 , 0x66480001 }, { 0x00016468 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, { 0x00016844 , 0x012492d4 , 0x012492d4 , 0x012492d4 , 0x012492d4 }, { 0x00016848 , 0x65240001 , 0x65240001 , 0x66480001 , 0x66480001 }, { 0x00016868 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, }; static const u_int32_t ar9300Modes_lowest_ob_db_tx_gain_table_ar9580_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x0000a2dc , 0x0380c7fc , 0x0380c7fc , 0x03aaa352 , 0x03aaa352 }, { 0x0000a2e0 , 0x0000f800 , 0x0000f800 , 0x03ccc584 , 0x03ccc584 }, { 0x0000a2e4 , 0x03ff0000 , 0x03ff0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000a2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000a410 , 0x000050d9 , 0x000050d9 , 0x000050d9 , 0x000050d9 }, { 0x0000a500 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a504 , 0x06000003 , 0x06000003 , 0x04000002 , 0x04000002 }, { 0x0000a508 , 0x0a000020 , 0x0a000020 , 0x08000004 , 0x08000004 }, { 0x0000a50c , 0x10000023 , 0x10000023 , 0x0b000200 , 0x0b000200 }, { 0x0000a510 , 0x16000220 , 0x16000220 , 0x0f000202 , 0x0f000202 }, { 0x0000a514 , 0x1c000223 , 0x1c000223 , 0x12000400 , 0x12000400 }, { 0x0000a518 , 0x21002220 , 0x21002220 , 0x16000402 , 0x16000402 }, { 0x0000a51c , 0x27002223 , 0x27002223 , 0x19000404 , 0x19000404 }, { 0x0000a520 , 0x2b022220 , 0x2b022220 , 0x1c000603 , 0x1c000603 }, { 0x0000a524 , 0x2f022222 , 0x2f022222 , 0x21000a02 , 0x21000a02 }, { 0x0000a528 , 0x34022225 , 0x34022225 , 0x25000a04 , 0x25000a04 }, { 0x0000a52c , 0x3a02222a , 0x3a02222a , 0x28000a20 , 0x28000a20 }, { 0x0000a530 , 0x3e02222c , 0x3e02222c , 0x2c000e20 , 0x2c000e20 }, { 0x0000a534 , 0x4202242a , 0x4202242a , 0x30000e22 , 0x30000e22 }, { 0x0000a538 , 0x4702244a , 0x4702244a , 0x34000e24 , 0x34000e24 }, { 0x0000a53c , 0x4b02244c , 0x4b02244c , 0x38001640 , 0x38001640 }, { 0x0000a540 , 0x4e02246c , 0x4e02246c , 0x3c001660 , 0x3c001660 }, { 0x0000a544 , 0x5302266c , 0x5302266c , 0x3f001861 , 0x3f001861 }, { 0x0000a548 , 0x5702286c , 0x5702286c , 0x43001a81 , 0x43001a81 }, { 0x0000a54c , 0x5c02486b , 0x5c02486b , 0x47001a83 , 0x47001a83 }, { 0x0000a550 , 0x61024a6c , 0x61024a6c , 0x4a001c84 , 0x4a001c84 }, { 0x0000a554 , 0x66026a6c , 0x66026a6c , 0x4e001ce3 , 0x4e001ce3 }, { 0x0000a558 , 0x6b026e6c , 0x6b026e6c , 0x52001ce5 , 0x52001ce5 }, { 0x0000a55c , 0x7002708c , 0x7002708c , 0x56001ce9 , 0x56001ce9 }, { 0x0000a560 , 0x7302b08a , 0x7302b08a , 0x5a001ceb , 0x5a001ceb }, { 0x0000a564 , 0x7702b08c , 0x7702b08c , 0x5d001eec , 0x5d001eec }, { 0x0000a568 , 0x7702b08c , 0x7702b08c , 0x5d001eec , 0x5d001eec }, { 0x0000a56c , 0x7702b08c , 0x7702b08c , 0x5d001eec , 0x5d001eec }, { 0x0000a570 , 0x7702b08c , 0x7702b08c , 0x5d001eec , 0x5d001eec }, { 0x0000a574 , 0x7702b08c , 0x7702b08c , 0x5d001eec , 0x5d001eec }, { 0x0000a578 , 0x7702b08c , 0x7702b08c , 0x5d001eec , 0x5d001eec }, { 0x0000a57c , 0x7702b08c , 0x7702b08c , 0x5d001eec , 0x5d001eec }, { 0x0000a580 , 0x00800000 , 0x00800000 , 0x00800000 , 0x00800000 }, { 0x0000a584 , 0x06800003 , 0x06800003 , 0x04800002 , 0x04800002 }, { 0x0000a588 , 0x0a800020 , 0x0a800020 , 0x08800004 , 0x08800004 }, { 0x0000a58c , 0x10800023 , 0x10800023 , 0x0b800200 , 0x0b800200 }, { 0x0000a590 , 0x16800220 , 0x16800220 , 0x0f800202 , 0x0f800202 }, { 0x0000a594 , 0x1c800223 , 0x1c800223 , 0x12800400 , 0x12800400 }, { 0x0000a598 , 0x21802220 , 0x21802220 , 0x16800402 , 0x16800402 }, { 0x0000a59c , 0x27802223 , 0x27802223 , 0x19800404 , 0x19800404 }, { 0x0000a5a0 , 0x2b822220 , 0x2b822220 , 0x1c800603 , 0x1c800603 }, { 0x0000a5a4 , 0x2f822222 , 0x2f822222 , 0x21800a02 , 0x21800a02 }, { 0x0000a5a8 , 0x34822225 , 0x34822225 , 0x25800a04 , 0x25800a04 }, { 0x0000a5ac , 0x3a82222a , 0x3a82222a , 0x28800a20 , 0x28800a20 }, { 0x0000a5b0 , 0x3e82222c , 0x3e82222c , 0x2c800e20 , 0x2c800e20 }, { 0x0000a5b4 , 0x4282242a , 0x4282242a , 0x30800e22 , 0x30800e22 }, { 0x0000a5b8 , 0x4782244a , 0x4782244a , 0x34800e24 , 0x34800e24 }, { 0x0000a5bc , 0x4b82244c , 0x4b82244c , 0x38801640 , 0x38801640 }, { 0x0000a5c0 , 0x4e82246c , 0x4e82246c , 0x3c801660 , 0x3c801660 }, { 0x0000a5c4 , 0x5382266c , 0x5382266c , 0x3f801861 , 0x3f801861 }, { 0x0000a5c8 , 0x5782286c , 0x5782286c , 0x43801a81 , 0x43801a81 }, { 0x0000a5cc , 0x5c82486b , 0x5c82486b , 0x47801a83 , 0x47801a83 }, { 0x0000a5d0 , 0x61824a6c , 0x61824a6c , 0x4a801c84 , 0x4a801c84 }, { 0x0000a5d4 , 0x66826a6c , 0x66826a6c , 0x4e801ce3 , 0x4e801ce3 }, { 0x0000a5d8 , 0x6b826e6c , 0x6b826e6c , 0x52801ce5 , 0x52801ce5 }, { 0x0000a5dc , 0x7082708c , 0x7082708c , 0x56801ce9 , 0x56801ce9 }, { 0x0000a5e0 , 0x7382b08a , 0x7382b08a , 0x5a801ceb , 0x5a801ceb }, { 0x0000a5e4 , 0x7782b08c , 0x7782b08c , 0x5d801eec , 0x5d801eec }, { 0x0000a5e8 , 0x7782b08c , 0x7782b08c , 0x5d801eec , 0x5d801eec }, { 0x0000a5ec , 0x7782b08c , 0x7782b08c , 0x5d801eec , 0x5d801eec }, { 0x0000a5f0 , 0x7782b08c , 0x7782b08c , 0x5d801eec , 0x5d801eec }, { 0x0000a5f4 , 0x7782b08c , 0x7782b08c , 0x5d801eec , 0x5d801eec }, { 0x0000a5f8 , 0x7782b08c , 0x7782b08c , 0x5d801eec , 0x5d801eec }, { 0x0000a5fc , 0x7782b08c , 0x7782b08c , 0x5d801eec , 0x5d801eec }, { 0x0000a600 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a604 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a608 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a60c , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a610 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a614 , 0x01404000 , 0x01404000 , 0x01404000 , 0x01404000 }, { 0x0000a618 , 0x01404501 , 0x01404501 , 0x01404501 , 0x01404501 }, { 0x0000a61c , 0x02008802 , 0x02008802 , 0x02008501 , 0x02008501 }, { 0x0000a620 , 0x0300cc03 , 0x0300cc03 , 0x0280ca03 , 0x0280ca03 }, { 0x0000a624 , 0x0300cc03 , 0x0300cc03 , 0x03010c04 , 0x03010c04 }, { 0x0000a628 , 0x0300cc03 , 0x0300cc03 , 0x04014c04 , 0x04014c04 }, { 0x0000a62c , 0x03810c03 , 0x03810c03 , 0x04015005 , 0x04015005 }, { 0x0000a630 , 0x03810e04 , 0x03810e04 , 0x04015005 , 0x04015005 }, { 0x0000a634 , 0x03810e04 , 0x03810e04 , 0x04015005 , 0x04015005 }, { 0x0000a638 , 0x03810e04 , 0x03810e04 , 0x04015005 , 0x04015005 }, { 0x0000a63c , 0x03810e04 , 0x03810e04 , 0x04015005 , 0x04015005 }, { 0x0000b2dc , 0x0380c7fc , 0x0380c7fc , 0x03aaa352 , 0x03aaa352 }, { 0x0000b2e0 , 0x0000f800 , 0x0000f800 , 0x03ccc584 , 0x03ccc584 }, { 0x0000b2e4 , 0x03ff0000 , 0x03ff0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000b2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000c2dc , 0x0380c7fc , 0x0380c7fc , 0x03aaa352 , 0x03aaa352 }, { 0x0000c2e0 , 0x0000f800 , 0x0000f800 , 0x03ccc584 , 0x03ccc584 }, { 0x0000c2e4 , 0x03ff0000 , 0x03ff0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000c2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x00016044 , 0x012492d4 , 0x012492d4 , 0x012492d4 , 0x012492d4 }, { 0x00016048 , 0x62480001 , 0x62480001 , 0x62480001 , 0x62480001 }, { 0x00016068 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, { 0x00016444 , 0x012492d4 , 0x012492d4 , 0x012492d4 , 0x012492d4 }, { 0x00016448 , 0x62480001 , 0x62480001 , 0x62480001 , 0x62480001 }, { 0x00016468 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, { 0x00016844 , 0x012492d4 , 0x012492d4 , 0x012492d4 , 0x012492d4 }, { 0x00016848 , 0x62480001 , 0x62480001 , 0x62480001 , 0x62480001 }, { 0x00016868 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, }; static const u_int32_t ar9300_ar9580_1p0_baseband_core_txfir_coeff_japan_2484[][2] = { /* Addr allmodes */ { 0x0000a398 , 0x00000000 }, { 0x0000a39c , 0x6f7f0301 }, { 0x0000a3a0 , 0xca9228ee }, }; static const u_int32_t ar9300_ar9580_1p0_mac_postamble_emulation[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x00008014 , 0x10f810f8 , 0x10f810f8 , 0x10f810f8 , 0x10f810f8 }, { 0x0000801c , 0x0e8d8017 , 0x0e8d8017 , 0x0e8d8017 , 0x0e8d8017 }, }; static const u_int32_t ar9200_merlin_1p0_radio_core[][2] = { /* Addr common */ { 0x00007800 , 0x00040000 }, { 0x00007804 , 0xdb005012 }, { 0x00007808 , 0x04924914 }, { 0x0000780c , 0x21084210 }, { 0x00007810 , 0x6d801300 }, { 0x00007814 , 0x0019beff }, { 0x00007818 , 0x07e41000 }, { 0x0000781c , 0x00392000 }, { 0x00007820 , 0x92592480 }, { 0x00007824 , 0x00040000 }, { 0x00007828 , 0xdb005012 }, { 0x0000782c , 0x04924914 }, { 0x00007830 , 0x21084210 }, { 0x00007834 , 0x6d801300 }, { 0x00007838 , 0x0019beff }, { 0x0000783c , 0x07e40000 }, { 0x00007840 , 0x00392000 }, { 0x00007844 , 0x92592480 }, { 0x00007848 , 0x00100000 }, { 0x0000784c , 0x773f0567 }, { 0x00007850 , 0x54214514 }, { 0x00007854 , 0x12035828 }, { 0x00007858 , 0x92592692 }, { 0x0000785c , 0x00000000 }, { 0x00007860 , 0x56400000 }, { 0x00007864 , 0x0a8e370e }, { 0x00007868 , 0xc0102850 }, { 0x0000786c , 0x812d4000 }, { 0x00007870 , 0x807ec400 }, { 0x00007874 , 0x001b6db0 }, { 0x00007878 , 0x00376b63 }, { 0x0000787c , 0x06db6db6 }, { 0x00007880 , 0x006d8000 }, { 0x00007884 , 0xffeffffe }, { 0x00007888 , 0xffeffffe }, { 0x0000788c , 0x00010000 }, { 0x00007890 , 0x02060aeb }, { 0x00007894 , 0x5a108000 }, }; static const u_int32_t ar9300_ar9580_1p0_tx_gain_table_baseband_postamble_emulation[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x0000a410 , 0x000000d5 , 0x000000d5 , 0x000000d5 , 0x000000d5 }, { 0x0000a500 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a504 , 0x00004002 , 0x00004002 , 0x00004002 , 0x00004002 }, { 0x0000a508 , 0x00008004 , 0x00008004 , 0x00008004 , 0x00008004 }, { 0x0000a510 , 0x0001000c , 0x0001000c , 0x0001000c , 0x0001000c }, { 0x0000a514 , 0x0001420b , 0x0001420b , 0x0001420b , 0x0001420b }, { 0x0000a518 , 0x0001824a , 0x0001824a , 0x0001824a , 0x0001824a }, { 0x0000a51c , 0x0001c44a , 0x0001c44a , 0x0001c44a , 0x0001c44a }, { 0x0000a520 , 0x0002064a , 0x0002064a , 0x0002064a , 0x0002064a }, { 0x0000a524 , 0x0002484a , 0x0002484a , 0x0002484a , 0x0002484a }, { 0x0000a528 , 0x00028a4a , 0x00028a4a , 0x00028a4a , 0x00028a4a }, { 0x0000a52c , 0x0002cc4a , 0x0002cc4a , 0x0002cc4a , 0x0002cc4a }, { 0x0000a530 , 0x00030e4a , 0x00030e4a , 0x00030e4a , 0x00030e4a }, { 0x0000a534 , 0x00034e8a , 0x00034e8a , 0x00034e8a , 0x00034e8a }, }; static const u_int32_t ar9300_ar9580_1p0_mac_core[][2] = { /* Addr allmodes */ { 0x00000008 , 0x00000000 }, { 0x00000030 , 0x00020085 }, { 0x00000034 , 0x00000005 }, { 0x00000040 , 0x00000000 }, { 0x00000044 , 0x00000000 }, { 0x00000048 , 0x00000008 }, { 0x0000004c , 0x00000010 }, { 0x00000050 , 0x00000000 }, { 0x00001040 , 0x002ffc0f }, { 0x00001044 , 0x002ffc0f }, { 0x00001048 , 0x002ffc0f }, { 0x0000104c , 0x002ffc0f }, { 0x00001050 , 0x002ffc0f }, { 0x00001054 , 0x002ffc0f }, { 0x00001058 , 0x002ffc0f }, { 0x0000105c , 0x002ffc0f }, { 0x00001060 , 0x002ffc0f }, { 0x00001064 , 0x002ffc0f }, { 0x000010f0 , 0x00000100 }, { 0x00001270 , 0x00000000 }, { 0x000012b0 , 0x00000000 }, { 0x000012f0 , 0x00000000 }, { 0x0000143c , 0x00000000 }, { 0x0000147c , 0x00000000 }, { 0x00008000 , 0x00000000 }, { 0x00008004 , 0x00000000 }, { 0x00008008 , 0x00000000 }, { 0x0000800c , 0x00000000 }, { 0x00008018 , 0x00000000 }, { 0x00008020 , 0x00000000 }, { 0x00008038 , 0x00000000 }, { 0x0000803c , 0x00000000 }, { 0x00008040 , 0x00000000 }, { 0x00008044 , 0x00000000 }, { 0x00008048 , 0x00000000 }, { 0x0000804c , 0xffffffff }, { 0x00008054 , 0x00000000 }, { 0x00008058 , 0x00000000 }, { 0x0000805c , 0x000fc78f }, { 0x00008060 , 0x0000000f }, { 0x00008064 , 0x00000000 }, { 0x00008070 , 0x00000310 }, { 0x00008074 , 0x00000020 }, { 0x00008078 , 0x00000000 }, { 0x0000809c , 0x0000000f }, { 0x000080a0 , 0x00000000 }, { 0x000080a4 , 0x02ff0000 }, { 0x000080a8 , 0x0e070605 }, { 0x000080ac , 0x0000000d }, { 0x000080b0 , 0x00000000 }, { 0x000080b4 , 0x00000000 }, { 0x000080b8 , 0x00000000 }, { 0x000080bc , 0x00000000 }, { 0x000080c0 , 0x2a800000 }, { 0x000080c4 , 0x06900168 }, { 0x000080c8 , 0x13881c22 }, { 0x000080cc , 0x01f40000 }, { 0x000080d0 , 0x00252500 }, { 0x000080d4 , 0x00a00000 }, { 0x000080d8 , 0x00400000 }, { 0x000080dc , 0x00000000 }, { 0x000080e0 , 0xffffffff }, { 0x000080e4 , 0x0000ffff }, { 0x000080e8 , 0x3f3f3f3f }, { 0x000080ec , 0x00000000 }, { 0x000080f0 , 0x00000000 }, { 0x000080f4 , 0x00000000 }, { 0x000080fc , 0x00020000 }, { 0x00008100 , 0x00000000 }, { 0x00008108 , 0x00000052 }, { 0x0000810c , 0x00000000 }, { 0x00008110 , 0x00000000 }, { 0x00008114 , 0x000007ff }, { 0x00008118 , 0x000000aa }, { 0x0000811c , 0x00003210 }, { 0x00008124 , 0x00000000 }, { 0x00008128 , 0x00000000 }, { 0x0000812c , 0x00000000 }, { 0x00008130 , 0x00000000 }, { 0x00008134 , 0x00000000 }, { 0x00008138 , 0x00000000 }, { 0x0000813c , 0x0000ffff }, { 0x00008144 , 0xffffffff }, { 0x00008168 , 0x00000000 }, { 0x0000816c , 0x00000000 }, { 0x000081c0 , 0x00000000 }, { 0x000081c4 , 0x33332210 }, { 0x000081ec , 0x00000000 }, { 0x000081f0 , 0x00000000 }, { 0x000081f4 , 0x00000000 }, { 0x000081f8 , 0x00000000 }, { 0x000081fc , 0x00000000 }, { 0x00008240 , 0x00100000 }, { 0x00008244 , 0x0010f400 }, { 0x00008248 , 0x00000800 }, { 0x0000824c , 0x0001e800 }, { 0x00008250 , 0x00000000 }, { 0x00008254 , 0x00000000 }, { 0x00008258 , 0x00000000 }, { 0x0000825c , 0x40000000 }, { 0x00008260 , 0x00080922 }, - { 0x00008264 , 0x9bc00010 }, + { 0x00008264 , 0x9d400010 }, { 0x00008268 , 0xffffffff }, { 0x0000826c , 0x0000ffff }, { 0x00008270 , 0x00000000 }, { 0x00008274 , 0x40000000 }, { 0x00008278 , 0x003e4180 }, { 0x0000827c , 0x00000004 }, { 0x00008284 , 0x0000002c }, { 0x00008288 , 0x0000002c }, { 0x0000828c , 0x000000ff }, { 0x00008294 , 0x00000000 }, { 0x00008298 , 0x00000000 }, { 0x0000829c , 0x00000000 }, { 0x00008300 , 0x00000140 }, { 0x00008314 , 0x00000000 }, { 0x0000831c , 0x0000010d }, { 0x00008328 , 0x00000000 }, { 0x0000832c , 0x00000007 }, { 0x00008330 , 0x00000302 }, { 0x00008334 , 0x00000700 }, { 0x00008338 , 0x00ff0000 }, { 0x0000833c , 0x02400000 }, { 0x00008340 , 0x000107ff }, { 0x00008344 , 0xaa48105b }, { 0x00008348 , 0x008f0000 }, { 0x0000835c , 0x00000000 }, { 0x00008360 , 0xffffffff }, { 0x00008364 , 0xffffffff }, { 0x00008368 , 0x00000000 }, { 0x00008370 , 0x00000000 }, { 0x00008374 , 0x000000ff }, { 0x00008378 , 0x00000000 }, { 0x0000837c , 0x00000000 }, { 0x00008380 , 0xffffffff }, { 0x00008384 , 0xffffffff }, { 0x00008390 , 0xffffffff }, { 0x00008394 , 0xffffffff }, { 0x00008398 , 0x00000000 }, { 0x0000839c , 0x00000000 }, { 0x000083a0 , 0x00000000 }, { 0x000083a4 , 0x0000fa14 }, { 0x000083a8 , 0x000f0c00 }, { 0x000083ac , 0x33332210 }, { 0x000083b0 , 0x33332210 }, { 0x000083b4 , 0x33332210 }, { 0x000083b8 , 0x33332210 }, { 0x000083bc , 0x00000000 }, { 0x000083c0 , 0x00000000 }, { 0x000083c4 , 0x00000000 }, { 0x000083c8 , 0x00000000 }, { 0x000083cc , 0x00000200 }, { 0x000083d0 , 0x000301ff }, }; static const u_int32_t ar9300_modes_mixed_ob_db_tx_gain_table_ar9580_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x0000a2dc , 0x0380c7fc , 0x0380c7fc , 0x03aaa352 , 0x03aaa352 }, { 0x0000a2e0 , 0x0000f800 , 0x0000f800 , 0x03ccc584 , 0x03ccc584 }, { 0x0000a2e4 , 0x03ff0000 , 0x03ff0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000a2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000a410 , 0x000050d9 , 0x000050d9 , 0x000050d9 , 0x000050d9 }, { 0x0000a500 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a504 , 0x06000003 , 0x06000003 , 0x04000002 , 0x04000002 }, { 0x0000a508 , 0x0a000020 , 0x0a000020 , 0x08000004 , 0x08000004 }, { 0x0000a50c , 0x10000023 , 0x10000023 , 0x0b000200 , 0x0b000200 }, { 0x0000a510 , 0x16000220 , 0x16000220 , 0x0f000202 , 0x0f000202 }, { 0x0000a514 , 0x1c000223 , 0x1c000223 , 0x11000400 , 0x11000400 }, { 0x0000a518 , 0x21002220 , 0x21002220 , 0x15000402 , 0x15000402 }, { 0x0000a51c , 0x27002223 , 0x27002223 , 0x19000404 , 0x19000404 }, { 0x0000a520 , 0x2b022220 , 0x2b022220 , 0x1b000603 , 0x1b000603 }, { 0x0000a524 , 0x2f022222 , 0x2f022222 , 0x1f000a02 , 0x1f000a02 }, { 0x0000a528 , 0x34022225 , 0x34022225 , 0x23000a04 , 0x23000a04 }, { 0x0000a52c , 0x3a02222a , 0x3a02222a , 0x26000a20 , 0x26000a20 }, { 0x0000a530 , 0x3e02222c , 0x3e02222c , 0x2a000e20 , 0x2a000e20 }, { 0x0000a534 , 0x4202242a , 0x4202242a , 0x2e000e22 , 0x2e000e22 }, { 0x0000a538 , 0x4702244a , 0x4702244a , 0x31000e24 , 0x31000e24 }, { 0x0000a53c , 0x4b02244c , 0x4b02244c , 0x34001640 , 0x34001640 }, { 0x0000a540 , 0x4e02246c , 0x4e02246c , 0x38001660 , 0x38001660 }, { 0x0000a544 , 0x5302266c , 0x5302266c , 0x3b001861 , 0x3b001861 }, { 0x0000a548 , 0x5702286c , 0x5702286c , 0x3e001a81 , 0x3e001a81 }, { 0x0000a54c , 0x5c02486b , 0x5c02486b , 0x42001a83 , 0x42001a83 }, { 0x0000a550 , 0x61024a6c , 0x61024a6c , 0x44001c84 , 0x44001c84 }, { 0x0000a554 , 0x66026a6c , 0x66026a6c , 0x48001ce3 , 0x48001ce3 }, { 0x0000a558 , 0x6b026e6c , 0x6b026e6c , 0x4c001ce5 , 0x4c001ce5 }, { 0x0000a55c , 0x7002708c , 0x7002708c , 0x50001ce9 , 0x50001ce9 }, { 0x0000a560 , 0x7302b08a , 0x7302b08a , 0x54001ceb , 0x54001ceb }, { 0x0000a564 , 0x7702b08c , 0x7702b08c , 0x56001eec , 0x56001eec }, { 0x0000a568 , 0x7702b08c , 0x7702b08c , 0x56001eec , 0x56001eec }, { 0x0000a56c , 0x7702b08c , 0x7702b08c , 0x56001eec , 0x56001eec }, { 0x0000a570 , 0x7702b08c , 0x7702b08c , 0x56001eec , 0x56001eec }, { 0x0000a574 , 0x7702b08c , 0x7702b08c , 0x56001eec , 0x56001eec }, { 0x0000a578 , 0x7702b08c , 0x7702b08c , 0x56001eec , 0x56001eec }, { 0x0000a57c , 0x7702b08c , 0x7702b08c , 0x56001eec , 0x56001eec }, { 0x0000a580 , 0x00800000 , 0x00800000 , 0x00800000 , 0x00800000 }, { 0x0000a584 , 0x06800003 , 0x06800003 , 0x04800002 , 0x04800002 }, { 0x0000a588 , 0x0a800020 , 0x0a800020 , 0x08800004 , 0x08800004 }, { 0x0000a58c , 0x10800023 , 0x10800023 , 0x0b800200 , 0x0b800200 }, { 0x0000a590 , 0x16800220 , 0x16800220 , 0x0f800202 , 0x0f800202 }, { 0x0000a594 , 0x1c800223 , 0x1c800223 , 0x11800400 , 0x11800400 }, { 0x0000a598 , 0x21802220 , 0x21802220 , 0x15800402 , 0x15800402 }, { 0x0000a59c , 0x27802223 , 0x27802223 , 0x19800404 , 0x19800404 }, { 0x0000a5a0 , 0x2b822220 , 0x2b822220 , 0x1b800603 , 0x1b800603 }, { 0x0000a5a4 , 0x2f822222 , 0x2f822222 , 0x1f800a02 , 0x1f800a02 }, { 0x0000a5a8 , 0x34822225 , 0x34822225 , 0x23800a04 , 0x23800a04 }, { 0x0000a5ac , 0x3a82222a , 0x3a82222a , 0x26800a20 , 0x26800a20 }, { 0x0000a5b0 , 0x3e82222c , 0x3e82222c , 0x2a800e20 , 0x2a800e20 }, { 0x0000a5b4 , 0x4282242a , 0x4282242a , 0x2e800e22 , 0x2e800e22 }, { 0x0000a5b8 , 0x4782244a , 0x4782244a , 0x31800e24 , 0x31800e24 }, { 0x0000a5bc , 0x4b82244c , 0x4b82244c , 0x34801640 , 0x34801640 }, { 0x0000a5c0 , 0x4e82246c , 0x4e82246c , 0x38801660 , 0x38801660 }, { 0x0000a5c4 , 0x5382266c , 0x5382266c , 0x3b801861 , 0x3b801861 }, { 0x0000a5c8 , 0x5782286c , 0x5782286c , 0x3e801a81 , 0x3e801a81 }, { 0x0000a5cc , 0x5c82486b , 0x5c82486b , 0x42801a83 , 0x42801a83 }, { 0x0000a5d0 , 0x61824a6c , 0x61824a6c , 0x44801c84 , 0x44801c84 }, { 0x0000a5d4 , 0x66826a6c , 0x66826a6c , 0x48801ce3 , 0x48801ce3 }, { 0x0000a5d8 , 0x6b826e6c , 0x6b826e6c , 0x4c801ce5 , 0x4c801ce5 }, { 0x0000a5dc , 0x7082708c , 0x7082708c , 0x50801ce9 , 0x50801ce9 }, { 0x0000a5e0 , 0x7382b08a , 0x7382b08a , 0x54801ceb , 0x54801ceb }, { 0x0000a5e4 , 0x7782b08c , 0x7782b08c , 0x56801eec , 0x56801eec }, { 0x0000a5e8 , 0x7782b08c , 0x7782b08c , 0x56801eec , 0x56801eec }, { 0x0000a5ec , 0x7782b08c , 0x7782b08c , 0x56801eec , 0x56801eec }, { 0x0000a5f0 , 0x7782b08c , 0x7782b08c , 0x56801eec , 0x56801eec }, { 0x0000a5f4 , 0x7782b08c , 0x7782b08c , 0x56801eec , 0x56801eec }, { 0x0000a5f8 , 0x7782b08c , 0x7782b08c , 0x56801eec , 0x56801eec }, { 0x0000a5fc , 0x7782b08c , 0x7782b08c , 0x56801eec , 0x56801eec }, { 0x0000a600 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a604 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a608 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a60c , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a610 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a614 , 0x01404000 , 0x01404000 , 0x01404000 , 0x01404000 }, { 0x0000a618 , 0x01404501 , 0x01404501 , 0x01404501 , 0x01404501 }, { 0x0000a61c , 0x02008802 , 0x02008802 , 0x02008501 , 0x02008501 }, { 0x0000a620 , 0x0300cc03 , 0x0300cc03 , 0x0280ca03 , 0x0280ca03 }, { 0x0000a624 , 0x0300cc03 , 0x0300cc03 , 0x03010c04 , 0x03010c04 }, { 0x0000a628 , 0x0300cc03 , 0x0300cc03 , 0x04014c04 , 0x04014c04 }, { 0x0000a62c , 0x03810c03 , 0x03810c03 , 0x04015005 , 0x04015005 }, { 0x0000a630 , 0x03810e04 , 0x03810e04 , 0x04015005 , 0x04015005 }, { 0x0000a634 , 0x03810e04 , 0x03810e04 , 0x04015005 , 0x04015005 }, { 0x0000a638 , 0x03810e04 , 0x03810e04 , 0x04015005 , 0x04015005 }, { 0x0000a63c , 0x03810e04 , 0x03810e04 , 0x04015005 , 0x04015005 }, { 0x0000b2dc , 0x0380c7fc , 0x0380c7fc , 0x03aaa352 , 0x03aaa352 }, { 0x0000b2e0 , 0x0000f800 , 0x0000f800 , 0x03ccc584 , 0x03ccc584 }, { 0x0000b2e4 , 0x03ff0000 , 0x03ff0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000b2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000c2dc , 0x0380c7fc , 0x0380c7fc , 0x03aaa352 , 0x03aaa352 }, { 0x0000c2e0 , 0x0000f800 , 0x0000f800 , 0x03ccc584 , 0x03ccc584 }, { 0x0000c2e4 , 0x03ff0000 , 0x03ff0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000c2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x00016044 , 0x012492d4 , 0x012492d4 , 0x056db2e4 , 0x056db2e4 }, { 0x00016048 , 0x66480001 , 0x66480001 , 0x8e480001 , 0x8e480001 }, { 0x00016068 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, { 0x00016444 , 0x012492d4 , 0x012492d4 , 0x056db2e4 , 0x056db2e4 }, { 0x00016448 , 0x66480001 , 0x66480001 , 0x8e480001 , 0x8e480001 }, { 0x00016468 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, { 0x00016844 , 0x012492d4 , 0x012492d4 , 0x056db2e4 , 0x056db2e4 }, { 0x00016848 , 0x66480001 , 0x66480001 , 0x8e480001 , 0x8e480001 }, { 0x00016868 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, }; static const u_int32_t ar9300_ar9580_1p0_mac_core_emulation[][2] = { /* Addr allmodes */ { 0x00000030 , 0x00020085 }, { 0x00000044 , 0x00000008 }, { 0x0000805c , 0xffffc7ff }, { 0x00008344 , 0xaa4a105b }, }; static const u_int32_t ar9300_common_wo_xlna_rx_gain_table_ar9580_1p0[][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 u_int32_t ar9300_ar9580_1p0_soc_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x00007010 , 0x00000023 , 0x00000023 , 0x00000023 , 0x00000023 }, }; static const u_int32_t ar9300_modes_type6_tx_gain_table_ar9580_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x0000a2dc , 0x000cfff0 , 0x000cfff0 , 0x03aaa352 , 0x03aaa352 }, { 0x0000a2e0 , 0x000f0000 , 0x000f0000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000a2e4 , 0x03f00000 , 0x03f00000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000a2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000a410 , 0x000050d9 , 0x000050d9 , 0x000050d9 , 0x000050d9 }, { 0x0000a500 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a504 , 0x06000003 , 0x06000003 , 0x04000002 , 0x04000002 }, { 0x0000a508 , 0x0a000020 , 0x0a000020 , 0x08000004 , 0x08000004 }, { 0x0000a50c , 0x10000023 , 0x10000023 , 0x0b000200 , 0x0b000200 }, { 0x0000a510 , 0x15000028 , 0x15000028 , 0x0f000202 , 0x0f000202 }, { 0x0000a514 , 0x1b00002b , 0x1b00002b , 0x12000400 , 0x12000400 }, { 0x0000a518 , 0x1f020028 , 0x1f020028 , 0x16000402 , 0x16000402 }, { 0x0000a51c , 0x2502002b , 0x2502002b , 0x19000404 , 0x19000404 }, { 0x0000a520 , 0x2a04002a , 0x2a04002a , 0x1c000603 , 0x1c000603 }, { 0x0000a524 , 0x2e06002a , 0x2e06002a , 0x21000a02 , 0x21000a02 }, { 0x0000a528 , 0x3302202d , 0x3302202d , 0x25000a04 , 0x25000a04 }, { 0x0000a52c , 0x3804202c , 0x3804202c , 0x28000a20 , 0x28000a20 }, { 0x0000a530 , 0x3c06202c , 0x3c06202c , 0x2c000e20 , 0x2c000e20 }, { 0x0000a534 , 0x4108202d , 0x4108202d , 0x30000e22 , 0x30000e22 }, { 0x0000a538 , 0x4506402d , 0x4506402d , 0x34000e24 , 0x34000e24 }, { 0x0000a53c , 0x4906222d , 0x4906222d , 0x38001640 , 0x38001640 }, { 0x0000a540 , 0x4d062231 , 0x4d062231 , 0x3c001660 , 0x3c001660 }, { 0x0000a544 , 0x50082231 , 0x50082231 , 0x3f001861 , 0x3f001861 }, { 0x0000a548 , 0x5608422e , 0x5608422e , 0x43001a81 , 0x43001a81 }, { 0x0000a54c , 0x5e08442e , 0x5e08442e , 0x47001a83 , 0x47001a83 }, { 0x0000a550 , 0x620a4431 , 0x620a4431 , 0x4a001c84 , 0x4a001c84 }, { 0x0000a554 , 0x640a4432 , 0x640a4432 , 0x4e001ce3 , 0x4e001ce3 }, { 0x0000a558 , 0x680a4434 , 0x680a4434 , 0x52001ce5 , 0x52001ce5 }, { 0x0000a55c , 0x6c0a6434 , 0x6c0a6434 , 0x56001ce9 , 0x56001ce9 }, { 0x0000a560 , 0x6f0a6633 , 0x6f0a6633 , 0x5a001ceb , 0x5a001ceb }, { 0x0000a564 , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a568 , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a56c , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a570 , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a574 , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a578 , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a57c , 0x730c6634 , 0x730c6634 , 0x5d001eec , 0x5d001eec }, { 0x0000a600 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a604 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a608 , 0x01804601 , 0x01804601 , 0x00000000 , 0x00000000 }, { 0x0000a60c , 0x01804601 , 0x01804601 , 0x00000000 , 0x00000000 }, { 0x0000a610 , 0x01804601 , 0x01804601 , 0x00000000 , 0x00000000 }, { 0x0000a614 , 0x01804601 , 0x01804601 , 0x01404000 , 0x01404000 }, { 0x0000a618 , 0x01804601 , 0x01804601 , 0x01404501 , 0x01404501 }, { 0x0000a61c , 0x01804601 , 0x01804601 , 0x02008501 , 0x02008501 }, { 0x0000a620 , 0x03408d02 , 0x03408d02 , 0x0280ca03 , 0x0280ca03 }, { 0x0000a624 , 0x0300cc03 , 0x0300cc03 , 0x03010c04 , 0x03010c04 }, { 0x0000a628 , 0x03410d04 , 0x03410d04 , 0x04014c04 , 0x04014c04 }, { 0x0000a62c , 0x03410d04 , 0x03410d04 , 0x04015005 , 0x04015005 }, { 0x0000a630 , 0x03410d04 , 0x03410d04 , 0x04015005 , 0x04015005 }, { 0x0000a634 , 0x03410d04 , 0x03410d04 , 0x04015005 , 0x04015005 }, { 0x0000a638 , 0x03410d04 , 0x03410d04 , 0x04015005 , 0x04015005 }, { 0x0000a63c , 0x03410d04 , 0x03410d04 , 0x04015005 , 0x04015005 }, { 0x0000b2dc , 0x000cfff0 , 0x000cfff0 , 0x03aaa352 , 0x03aaa352 }, { 0x0000b2e0 , 0x000f0000 , 0x000f0000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000b2e4 , 0x03f00000 , 0x03f00000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000b2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000c2dc , 0x000cfff0 , 0x000cfff0 , 0x03aaa352 , 0x03aaa352 }, { 0x0000c2e0 , 0x000f0000 , 0x000f0000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000c2e4 , 0x03f00000 , 0x03f00000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000c2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x00016044 , 0x012492d4 , 0x012492d4 , 0x012492d4 , 0x012492d4 }, { 0x00016048 , 0x61200001 , 0x61200001 , 0x66480001 , 0x66480001 }, { 0x00016068 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, { 0x00016444 , 0x012492d4 , 0x012492d4 , 0x012492d4 , 0x012492d4 }, { 0x00016448 , 0x61200001 , 0x61200001 , 0x66480001 , 0x66480001 }, { 0x00016468 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, { 0x00016844 , 0x012492d4 , 0x012492d4 , 0x012492d4 , 0x012492d4 }, { 0x00016848 , 0x61200001 , 0x61200001 , 0x66480001 , 0x66480001 }, { 0x00016868 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, }; static const u_int32_t ar9300Modes_high_ob_db_tx_gain_table_ar9580_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x0000a2dc , 0x01feee00 , 0x01feee00 , 0x03aaa352 , 0x03aaa352 }, { 0x0000a2e0 , 0x0000f000 , 0x0000f000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000a2e4 , 0x01ff0000 , 0x01ff0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000a2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000a410 , 0x000050d4 , 0x000050d4 , 0x000050d9 , 0x000050d9 }, { 0x0000a500 , 0x00002220 , 0x00002220 , 0x00000000 , 0x00000000 }, { 0x0000a504 , 0x04002222 , 0x04002222 , 0x04000002 , 0x04000002 }, { 0x0000a508 , 0x09002421 , 0x09002421 , 0x08000004 , 0x08000004 }, { 0x0000a50c , 0x0d002621 , 0x0d002621 , 0x0b000200 , 0x0b000200 }, { 0x0000a510 , 0x13004620 , 0x13004620 , 0x0f000202 , 0x0f000202 }, { 0x0000a514 , 0x19004a20 , 0x19004a20 , 0x11000400 , 0x11000400 }, { 0x0000a518 , 0x1d004e20 , 0x1d004e20 , 0x15000402 , 0x15000402 }, { 0x0000a51c , 0x21005420 , 0x21005420 , 0x19000404 , 0x19000404 }, { 0x0000a520 , 0x26005e20 , 0x26005e20 , 0x1b000603 , 0x1b000603 }, { 0x0000a524 , 0x2b005e40 , 0x2b005e40 , 0x1f000a02 , 0x1f000a02 }, { 0x0000a528 , 0x2f005e42 , 0x2f005e42 , 0x23000a04 , 0x23000a04 }, { 0x0000a52c , 0x33005e44 , 0x33005e44 , 0x26000a20 , 0x26000a20 }, { 0x0000a530 , 0x38005e65 , 0x38005e65 , 0x2a000e20 , 0x2a000e20 }, { 0x0000a534 , 0x3c005e69 , 0x3c005e69 , 0x2e000e22 , 0x2e000e22 }, { 0x0000a538 , 0x40005e6b , 0x40005e6b , 0x31000e24 , 0x31000e24 }, { 0x0000a53c , 0x44005e6d , 0x44005e6d , 0x34001640 , 0x34001640 }, { 0x0000a540 , 0x49005e72 , 0x49005e72 , 0x38001660 , 0x38001660 }, { 0x0000a544 , 0x4e005eb2 , 0x4e005eb2 , 0x3b001861 , 0x3b001861 }, { 0x0000a548 , 0x53005f12 , 0x53005f12 , 0x3e001a81 , 0x3e001a81 }, { 0x0000a54c , 0x59025eb2 , 0x59025eb2 , 0x42001a83 , 0x42001a83 }, { 0x0000a550 , 0x5e025f12 , 0x5e025f12 , 0x44001c84 , 0x44001c84 }, { 0x0000a554 , 0x61027f12 , 0x61027f12 , 0x48001ce3 , 0x48001ce3 }, { 0x0000a558 , 0x6702bf12 , 0x6702bf12 , 0x4c001ce5 , 0x4c001ce5 }, { 0x0000a55c , 0x6b02bf14 , 0x6b02bf14 , 0x50001ce9 , 0x50001ce9 }, { 0x0000a560 , 0x6f02bf16 , 0x6f02bf16 , 0x54001ceb , 0x54001ceb }, { 0x0000a564 , 0x6f02bf16 , 0x6f02bf16 , 0x56001eec , 0x56001eec }, { 0x0000a568 , 0x6f02bf16 , 0x6f02bf16 , 0x56001eec , 0x56001eec }, { 0x0000a56c , 0x6f02bf16 , 0x6f02bf16 , 0x56001eec , 0x56001eec }, { 0x0000a570 , 0x6f02bf16 , 0x6f02bf16 , 0x56001eec , 0x56001eec }, { 0x0000a574 , 0x6f02bf16 , 0x6f02bf16 , 0x56001eec , 0x56001eec }, { 0x0000a578 , 0x6f02bf16 , 0x6f02bf16 , 0x56001eec , 0x56001eec }, { 0x0000a57c , 0x6f02bf16 , 0x6f02bf16 , 0x56001eec , 0x56001eec }, { 0x0000a580 , 0x00802220 , 0x00802220 , 0x00800000 , 0x00800000 }, { 0x0000a584 , 0x04802222 , 0x04802222 , 0x04800002 , 0x04800002 }, { 0x0000a588 , 0x09802421 , 0x09802421 , 0x08800004 , 0x08800004 }, { 0x0000a58c , 0x0d802621 , 0x0d802621 , 0x0b800200 , 0x0b800200 }, { 0x0000a590 , 0x13804620 , 0x13804620 , 0x0f800202 , 0x0f800202 }, { 0x0000a594 , 0x19804a20 , 0x19804a20 , 0x11800400 , 0x11800400 }, { 0x0000a598 , 0x1d804e20 , 0x1d804e20 , 0x15800402 , 0x15800402 }, { 0x0000a59c , 0x21805420 , 0x21805420 , 0x19800404 , 0x19800404 }, { 0x0000a5a0 , 0x26805e20 , 0x26805e20 , 0x1b800603 , 0x1b800603 }, { 0x0000a5a4 , 0x2b805e40 , 0x2b805e40 , 0x1f800a02 , 0x1f800a02 }, { 0x0000a5a8 , 0x2f805e42 , 0x2f805e42 , 0x23800a04 , 0x23800a04 }, { 0x0000a5ac , 0x33805e44 , 0x33805e44 , 0x26800a20 , 0x26800a20 }, { 0x0000a5b0 , 0x38805e65 , 0x38805e65 , 0x2a800e20 , 0x2a800e20 }, { 0x0000a5b4 , 0x3c805e69 , 0x3c805e69 , 0x2e800e22 , 0x2e800e22 }, { 0x0000a5b8 , 0x40805e6b , 0x40805e6b , 0x31800e24 , 0x31800e24 }, { 0x0000a5bc , 0x44805e6d , 0x44805e6d , 0x34801640 , 0x34801640 }, { 0x0000a5c0 , 0x49805e72 , 0x49805e72 , 0x38801660 , 0x38801660 }, { 0x0000a5c4 , 0x4e805eb2 , 0x4e805eb2 , 0x3b801861 , 0x3b801861 }, { 0x0000a5c8 , 0x53805f12 , 0x53805f12 , 0x3e801a81 , 0x3e801a81 }, { 0x0000a5cc , 0x59825eb2 , 0x59825eb2 , 0x42801a83 , 0x42801a83 }, { 0x0000a5d0 , 0x5e825f12 , 0x5e825f12 , 0x44801c84 , 0x44801c84 }, { 0x0000a5d4 , 0x61827f12 , 0x61827f12 , 0x48801ce3 , 0x48801ce3 }, { 0x0000a5d8 , 0x6782bf12 , 0x6782bf12 , 0x4c801ce5 , 0x4c801ce5 }, { 0x0000a5dc , 0x6b82bf14 , 0x6b82bf14 , 0x50801ce9 , 0x50801ce9 }, { 0x0000a5e0 , 0x6f82bf16 , 0x6f82bf16 , 0x54801ceb , 0x54801ceb }, { 0x0000a5e4 , 0x6f82bf16 , 0x6f82bf16 , 0x56801eec , 0x56801eec }, { 0x0000a5e8 , 0x6f82bf16 , 0x6f82bf16 , 0x56801eec , 0x56801eec }, { 0x0000a5ec , 0x6f82bf16 , 0x6f82bf16 , 0x56801eec , 0x56801eec }, { 0x0000a5f0 , 0x6f82bf16 , 0x6f82bf16 , 0x56801eec , 0x56801eec }, { 0x0000a5f4 , 0x6f82bf16 , 0x6f82bf16 , 0x56801eec , 0x56801eec }, { 0x0000a5f8 , 0x6f82bf16 , 0x6f82bf16 , 0x56801eec , 0x56801eec }, { 0x0000a5fc , 0x6f82bf16 , 0x6f82bf16 , 0x56801eec , 0x56801eec }, { 0x0000a600 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a604 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a608 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a60c , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a610 , 0x00804000 , 0x00804000 , 0x00000000 , 0x00000000 }, { 0x0000a614 , 0x00804201 , 0x00804201 , 0x01404000 , 0x01404000 }, { 0x0000a618 , 0x0280c802 , 0x0280c802 , 0x01404501 , 0x01404501 }, { 0x0000a61c , 0x0280ca03 , 0x0280ca03 , 0x02008501 , 0x02008501 }, { 0x0000a620 , 0x04c15104 , 0x04c15104 , 0x0280ca03 , 0x0280ca03 }, { 0x0000a624 , 0x04c15305 , 0x04c15305 , 0x03010c04 , 0x03010c04 }, { 0x0000a628 , 0x04c15305 , 0x04c15305 , 0x04014c04 , 0x04014c04 }, { 0x0000a62c , 0x04c15305 , 0x04c15305 , 0x04015005 , 0x04015005 }, { 0x0000a630 , 0x04c15305 , 0x04c15305 , 0x04015005 , 0x04015005 }, { 0x0000a634 , 0x04c15305 , 0x04c15305 , 0x04015005 , 0x04015005 }, { 0x0000a638 , 0x04c15305 , 0x04c15305 , 0x04015005 , 0x04015005 }, { 0x0000a63c , 0x04c15305 , 0x04c15305 , 0x04015005 , 0x04015005 }, { 0x0000b2dc , 0x01feee00 , 0x01feee00 , 0x03aaa352 , 0x03aaa352 }, { 0x0000b2e0 , 0x0000f000 , 0x0000f000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000b2e4 , 0x01ff0000 , 0x01ff0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000b2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x0000c2dc , 0x01feee00 , 0x01feee00 , 0x03aaa352 , 0x03aaa352 }, { 0x0000c2e0 , 0x0000f000 , 0x0000f000 , 0x03ccc584 , 0x03ccc584 }, { 0x0000c2e4 , 0x01ff0000 , 0x01ff0000 , 0x03f0f800 , 0x03f0f800 }, { 0x0000c2e8 , 0x00000000 , 0x00000000 , 0x03ff0000 , 0x03ff0000 }, { 0x00016044 , 0x056db2e4 , 0x056db2e4 , 0x056db2e4 , 0x056db2e4 }, { 0x00016048 , 0x8e480001 , 0x8e480001 , 0x8e480001 , 0x8e480001 }, { 0x00016068 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, { 0x00016444 , 0x056db2e4 , 0x056db2e4 , 0x056db2e4 , 0x056db2e4 }, { 0x00016448 , 0x8e480001 , 0x8e480001 , 0x8e480001 , 0x8e480001 }, { 0x00016468 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, { 0x00016844 , 0x056db2e4 , 0x056db2e4 , 0x056db2e4 , 0x056db2e4 }, { 0x00016848 , 0x8e480001 , 0x8e480001 , 0x8e480001 , 0x8e480001 }, { 0x00016868 , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c , 0x6db6db6c }, }; static const u_int32_t ar9300_ar9580_1p0_soc_preamble[][2] = { /* Addr allmodes */ { 0x000040a4 , 0x00a0c1c9 }, { 0x00007008 , 0x00000000 }, { 0x00007020 , 0x00000000 }, { 0x00007034 , 0x00000002 }, { 0x00007038 , 0x000004c2 }, { 0x00007048 , 0x00000008 }, }; static const u_int32_t ar9300_common_rx_gain_table_ar9580_1p0[][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 , 0x01910190 }, { 0x0000a030 , 0x01930192 }, { 0x0000a034 , 0x01950194 }, { 0x0000a038 , 0x038a0196 }, { 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 , 0x22222229 }, { 0x0000a084 , 0x1d1d1d1d }, { 0x0000a088 , 0x1d1d1d1d }, { 0x0000a08c , 0x1d1d1d1d }, { 0x0000a090 , 0x171d1d1d }, { 0x0000a094 , 0x11111717 }, { 0x0000a098 , 0x00030311 }, { 0x0000a09c , 0x00000000 }, { 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 , 0x23232323 }, { 0x0000b084 , 0x21232323 }, { 0x0000b088 , 0x19191c1e }, { 0x0000b08c , 0x12141417 }, { 0x0000b090 , 0x07070e0e }, { 0x0000b094 , 0x03030305 }, { 0x0000b098 , 0x00000003 }, { 0x0000b09c , 0x00000000 }, { 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 u_int32_t ar9300_ar9580_1p0_radio_core[][2] = { /* Addr allmodes */ { 0x00016000 , 0x36db6db6 }, { 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 , 0x36db6db6 }, { 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 , 0x36db6db6 }, { 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 }, }; static const u_int32_t ar9300_ar9580_1p0_baseband_core_emulation[][2] = { /* Addr allmodes */ { 0x00009800 , 0xafa68e30 }, { 0x00009884 , 0x00002842 }, { 0x00009c04 , 0xff55ff55 }, { 0x00009c08 , 0x0320ff55 }, { 0x00009e50 , 0x00000000 }, { 0x00009fcc , 0x00000014 }, { 0x0000a344 , 0x00000010 }, { 0x0000a398 , 0x00000000 }, { 0x0000a39c , 0x71733d01 }, { 0x0000a3a0 , 0xd0ad5c12 }, { 0x0000a3c0 , 0x22222220 }, { 0x0000a3c4 , 0x22222222 }, { 0x0000a404 , 0x00418a11 }, { 0x0000a418 , 0x050001ce }, { 0x0000a438 , 0x00001800 }, { 0x0000a458 , 0x01444452 }, { 0x0000a690 , 0x00000038 }, { 0x0000b8dc , 0x00400000 }, }; static const u_int32_t ar9300_ar9580_1p0_baseband_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ { 0x00009810 , 0xd00a8005 , 0xd00a8005 , 0xd00a8011 , 0xd00a8011 }, { 0x00009820 , 0x206a022e , 0x206a022e , 0x206a012e , 0x206a012e }, { 0x00009824 , 0x5ac640d0 , 0x5ac640d0 , 0x5ac640d0 , 0x5ac640d0 }, { 0x00009828 , 0x06903081 , 0x06903081 , 0x06903881 , 0x06903881 }, { 0x0000982c , 0x05eea6d4 , 0x05eea6d4 , 0x05eea6d4 , 0x05eea6d4 }, { 0x00009830 , 0x0000059c , 0x0000059c , 0x0000119c , 0x0000119c }, { 0x00009c00 , 0x000000c4 , 0x000000c4 , 0x000000c4 , 0x000000c4 }, { 0x00009e00 , 0x0372111a , 0x0372111a , 0x037216a0 , 0x037216a0 }, { 0x00009e04 , 0x001c2020 , 0x001c2020 , 0x001c2020 , 0x001c2020 }, { 0x00009e0c , 0x6c4000e2 , 0x6d4000e2 , 0x6d4000e2 , 0x6c4000e2 }, { 0x00009e10 , 0x7ec88d2e , 0x7ec88d2e , 0x7ec84d2e , 0x7ec84d2e }, { 0x00009e14 , 0x37b95d5e , 0x37b9605e , 0x3379605e , 0x33795d5e }, { 0x00009e18 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, - { 0x00009e1c , 0x0001cf9c , 0x0001cf9c , 0x00021f9c , 0x00021f9c }, + { 0x00009e1c , 0x0001c59c , 0x0001c59c , 0x0002159c , 0x0002159c }, { 0x00009e20 , 0x000003b5 , 0x000003b5 , 0x000003ce , 0x000003ce }, { 0x00009e2c , 0x0000001c , 0x0000001c , 0x00000021 , 0x00000021 }, { 0x00009e3c , 0xcf946220 , 0xcf946220 , 0xcf946222 , 0xcf946222 }, { 0x00009e44 , 0x02321e27 , 0x02321e27 , 0x02291e27 , 0x02291e27 }, { 0x00009e48 , 0x5030201a , 0x5030201a , 0x50302012 , 0x50302012 }, { 0x00009fc8 , 0x0003f000 , 0x0003f000 , 0x0001a000 , 0x0001a000 }, { 0x0000a204 , 0x000036c0 , 0x000036c4 , 0x000036c4 , 0x000036c0 }, { 0x0000a208 , 0x00000104 , 0x00000104 , 0x00000004 , 0x00000004 }, { 0x0000a22c , 0x07e26a2f , 0x07e26a2f , 0x01026a2f , 0x01026a2f }, { 0x0000a230 , 0x0000000a , 0x00000014 , 0x00000016 , 0x0000000b }, { 0x0000a234 , 0x00000fff , 0x10000fff , 0x10000fff , 0x00000fff }, { 0x0000a238 , 0xffb01018 , 0xffb01018 , 0xffb01018 , 0xffb01018 }, { 0x0000a250 , 0x00000000 , 0x00000000 , 0x00000210 , 0x00000108 }, { 0x0000a254 , 0x000007d0 , 0x00000fa0 , 0x00001130 , 0x00000898 }, { 0x0000a258 , 0x02020002 , 0x02020002 , 0x02020002 , 0x02020002 }, { 0x0000a25c , 0x01000e0e , 0x01000e0e , 0x01000e0e , 0x01000e0e }, { 0x0000a260 , 0x0a021501 , 0x0a021501 , 0x3a021501 , 0x3a021501 }, { 0x0000a264 , 0x00000e0e , 0x00000e0e , 0x00000e0e , 0x00000e0e }, { 0x0000a280 , 0x00000007 , 0x00000007 , 0x0000000b , 0x0000000b }, { 0x0000a284 , 0x00000000 , 0x00000000 , 0x00000150 , 0x00000150 }, { 0x0000a288 , 0x00000110 , 0x00000110 , 0x00000110 , 0x00000110 }, { 0x0000a28c , 0x00022222 , 0x00022222 , 0x00022222 , 0x00022222 }, { 0x0000a2c4 , 0x00158d18 , 0x00158d18 , 0x00158d18 , 0x00158d18 }, { 0x0000a2d0 , 0x00041983 , 0x00041983 , 0x00041981 , 0x00041982 }, { 0x0000a2d8 , 0x7999a83b , 0x7999a83b , 0x7999a83b , 0x7999a83b }, { 0x0000a358 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000a830 , 0x0000019c , 0x0000019c , 0x0000019c , 0x0000019c }, { 0x0000ae04 , 0x001c0000 , 0x001c0000 , 0x001c0000 , 0x001c0000 }, { 0x0000ae18 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000ae1c , 0x0000019c , 0x0000019c , 0x0000019c , 0x0000019c }, { 0x0000ae20 , 0x000001b5 , 0x000001b5 , 0x000001ce , 0x000001ce }, { 0x0000b284 , 0x00000000 , 0x00000000 , 0x00000150 , 0x00000150 }, { 0x0000b830 , 0x0000019c , 0x0000019c , 0x0000019c , 0x0000019c }, { 0x0000be04 , 0x001c0000 , 0x001c0000 , 0x001c0000 , 0x001c0000 }, { 0x0000be18 , 0x00000000 , 0x00000000 , 0x00000000 , 0x00000000 }, { 0x0000be1c , 0x0000019c , 0x0000019c , 0x0000019c , 0x0000019c }, { 0x0000be20 , 0x000001b5 , 0x000001b5 , 0x000001ce , 0x000001ce }, { 0x0000c284 , 0x00000000 , 0x00000000 , 0x00000150 , 0x00000150 }, }; /* PCIE-PHY programming array */ static const u_int32_t ar9300PciePhy_clkreq_enable_L1_ar9580_1p0[][2] = { {0x00004040, 0x0835365e}, {0x00004040, 0x0008003b}, {0x00004044, 0x00000000}, }; /* PCIE-PHY programming array */ static const u_int32_t ar9300PciePhy_clkreq_disable_L1_ar9580_1p0[][2] = { {0x00004040, 0x0831365e}, {0x00004040, 0x0008003b}, {0x00004044, 0x00000000}, }; /* PCIE-PHY programming array */ static const u_int32_t ar9300PciePhy_pll_on_clkreq_disable_L1_ar9580_1p0[][2] = { {0x00004040, 0x0831265e}, {0x00004040, 0x0008003b}, {0x00004044, 0x00000000}, }; Index: projects/building-blocks/sys/contrib/vchiq/interface/compat/vchi_bsd.h =================================================================== --- projects/building-blocks/sys/contrib/vchiq/interface/compat/vchi_bsd.h (revision 278776) +++ projects/building-blocks/sys/contrib/vchiq/interface/compat/vchi_bsd.h (revision 278777) @@ -1,434 +1,440 @@ /*- * Copyright (c) 2010 Max Khon * Copyright (c) 2012 Oleksandr Tymoshenko * Copyright (c) 2013 Jared D. McNeill * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ #ifndef __VCHI_BSD_H__ #define __VCHI_BSD_H__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Copy from/to user API */ #define copy_from_user(to, from, n) copyin((from), (to), (n)) #define copy_to_user(to, from, n) copyout((from), (to), (n)) /* * Bit API */ static __inline int test_and_set_bit(int nr, volatile void *addr) { int val; do { val = *(volatile int *) addr; } while (atomic_cmpset_int(addr, val, val | (1 << nr)) == 0); return (val & (1 << nr)); } static __inline__ int test_and_clear_bit(int nr, volatile void *addr) { int val; do { val = *(volatile int *) addr; } while (atomic_cmpset_int(addr, val, val & ~(1 << nr)) == 0); return (val & (1 << nr)); } /* * Atomic API */ typedef volatile unsigned atomic_t; #define atomic_set(p, v) (*(p) = (v)) #define atomic_read(p) (*(p)) #define atomic_inc(p) atomic_add_int(p, 1) #define atomic_dec(p) atomic_subtract_int(p, 1) #define atomic_dec_and_test(p) (atomic_fetchadd_int(p, -1) == 1) #define atomic_inc_return(v) atomic_add_return(1, (v)) #define atomic_dec_return(v) atomic_sub_return(1, (v)) #define atomic_add(v, p) atomic_add_int(p, v) #define atomic_sub(v, p) atomic_subtract_int(p, v) #define ATOMIC_INIT(v) (v) static inline int atomic_add_return(int i, atomic_t *v) { return i + atomic_fetchadd_int(v, i); } static inline int atomic_sub_return(int i, atomic_t *v) { return atomic_fetchadd_int(v, -i) - i; } static inline int atomic_cmpxchg(atomic_t *v, int oldv, int newv) { if (atomic_cmpset_rel_int(v, oldv, newv)) return newv; else return *v; } static inline int atomic_xchg(atomic_t *v, int newv) { int oldv; if (newv == 0) return atomic_readandclear_int(v); else { do { oldv = atomic_load_acq_int(v); } while (!atomic_cmpset_rel_int(v, oldv, newv)); } return (oldv); } /* * Spinlock API */ typedef struct mtx spinlock_t; #define DEFINE_SPINLOCK(name) \ struct mtx name #define spin_lock_init(lock) mtx_init(lock, "VCHI spinlock " # lock, NULL, MTX_DEF) #define spin_lock_destroy(lock) mtx_destroy(lock) #define spin_lock(lock) mtx_lock(lock) #define spin_unlock(lock) mtx_unlock(lock) #define spin_lock_bh(lock) spin_lock(lock) #define spin_unlock_bh(lock) spin_unlock(lock) /* * Mutex API */ struct mutex { struct mtx mtx; }; #define lmutex_init(lock) mtx_init(&(lock)->mtx, #lock, NULL, MTX_DEF) #define lmutex_lock(lock) mtx_lock(&(lock)->mtx) -#define lmutex_lock_interruptible(lock) (mtx_lock(&(lock)->mtx),0) #define lmutex_unlock(lock) mtx_unlock(&(lock)->mtx) #define lmutex_destroy(lock) mtx_destroy(&(lock)->mtx) + +static __inline int +lmutex_lock_interruptible(struct mutex *lock) +{ + mtx_lock(&(lock)->mtx); + return 0; +} /* * Rwlock API */ typedef struct sx rwlock_t; #if defined(SX_ADAPTIVESPIN) && !defined(SX_NOADAPTIVE) #define SX_NOADAPTIVE SX_ADAPTIVESPIN #endif #define DEFINE_RWLOCK(name) \ struct sx name; \ SX_SYSINIT(name, &name, #name) #define rwlock_init(rwlock) sx_init_flags(rwlock, "VCHI rwlock", SX_NOADAPTIVE) #define read_lock(rwlock) sx_slock(rwlock) #define read_unlock(rwlock) sx_sunlock(rwlock) #define write_lock(rwlock) sx_xlock(rwlock) #define write_unlock(rwlock) sx_xunlock(rwlock) #define write_lock_irqsave(rwlock, flags) \ do { \ sx_xlock(rwlock); \ (void) &(flags); \ } while (0) #define write_unlock_irqrestore(rwlock, flags) \ sx_xunlock(rwlock) #define read_lock_bh(rwlock) sx_slock(rwlock) #define read_unlock_bh(rwlock) sx_sunlock(rwlock) #define write_lock_bh(rwlock) sx_xlock(rwlock) #define write_unlock_bh(rwlock) sx_xunlock(rwlock) /* * Timer API */ struct timer_list { struct mtx mtx; struct callout callout; unsigned long expires; void (*function)(unsigned long); unsigned long data; }; void init_timer(struct timer_list *t); void setup_timer(struct timer_list *t, void (*function)(unsigned long), unsigned long data); void mod_timer(struct timer_list *t, unsigned long expires); void add_timer(struct timer_list *t); int del_timer(struct timer_list *t); int del_timer_sync(struct timer_list *t); /* * Completion API */ struct completion { struct cv cv; struct mtx lock; int done; }; void init_completion(struct completion *c); void destroy_completion(struct completion *c); int try_wait_for_completion(struct completion *); int wait_for_completion_interruptible(struct completion *); int wait_for_completion_interruptible_timeout(struct completion *, unsigned long ticks); int wait_for_completion_killable(struct completion *); void wait_for_completion(struct completion *c); void complete(struct completion *c); void complete_all(struct completion *c); void INIT_COMPLETION_locked(struct completion *c); #define INIT_COMPLETION(x) INIT_COMPLETION_locked(&(x)) /* * Semaphore API */ struct semaphore { struct mtx mtx; struct cv cv; int value; int waiters; }; #define DEFINE_SEMAPHORE(name) \ struct semaphore name; \ SYSINIT(name##_sema_sysinit, SI_SUB_LOCK, SI_ORDER_MIDDLE, \ sema_sysinit, &name); \ SYSUNINIT(name##_sema_sysuninit, SI_SUB_LOCK, SI_ORDER_MIDDLE, \ _sema_destroy, __DEVOLATILE(void *, &(name))) void sema_sysinit(void *arg); void _sema_init(struct semaphore *s, int value); void _sema_destroy(struct semaphore *s); void down(struct semaphore *s); int down_interruptible(struct semaphore *s); int down_trylock(struct semaphore *s); void up(struct semaphore *s); /* * Logging and assertions API */ void rlprintf(int pps, const char *fmt, ...) __printflike(2, 3); void device_rlprintf(int pps, device_t dev, const char *fmt, ...) __printflike(3, 4); #define might_sleep() #define WARN(condition, msg) \ ({ \ int __ret_warn_on = !!(condition); \ if (unlikely(__ret_warn_on)) \ printf((msg)); \ unlikely(__ret_warn_on); \ }) #define WARN_ON(condition) \ ({ \ int __ret_warn_on = !!(condition); \ if (unlikely(__ret_warn_on)) \ printf("WARN_ON: " #condition "\n"); \ unlikely(__ret_warn_on); \ }) #define WARN_ON_ONCE(condition) ({ \ static int __warned; \ int __ret_warn_once = !!(condition); \ \ if (unlikely(__ret_warn_once)) \ if (WARN_ON(!__warned)) \ __warned = 1; \ unlikely(__ret_warn_once); \ }) #define BUG_ON(cond) \ do { \ if (cond) \ panic("BUG_ON: " #cond); \ } while (0) #define BUG() \ do { \ panic("BUG: %s:%d", __FILE__, __LINE__); \ } while (0) #define vchiq_static_assert(cond) CTASSERT(cond) #define KERN_EMERG "<0>" /* system is unusable */ #define KERN_ALERT "<1>" /* action must be taken immediately */ #define KERN_CRIT "<2>" /* critical conditions */ #define KERN_ERR "<3>" /* error conditions */ #define KERN_WARNING "<4>" /* warning conditions */ #define KERN_NOTICE "<5>" /* normal but significant condition */ #define KERN_INFO "<6>" /* informational */ #define KERN_DEBUG "<7>" /* debug-level messages */ #define KERN_CONT "" #define printk(fmt, args...) printf(fmt, ##args) #define vprintk(fmt, args) vprintf(fmt, args) /* * Malloc API */ #define GFP_KERNEL 0 #define GFP_ATOMIC 0 MALLOC_DECLARE(M_VCHI); #define kmalloc(size, flags) malloc((size), M_VCHI, M_NOWAIT | M_ZERO) #define kcalloc(n, size, flags) malloc((n) * (size), M_VCHI, M_NOWAIT | M_ZERO) #define kzalloc(a, b) kcalloc(1, (a), (b)) #define kfree(p) free(p, M_VCHI) /* * Kernel module API */ #define __init #define __exit #define __devinit #define __devexit #define __devinitdata /* * Time API */ #if 1 /* emulate jiffies */ static inline unsigned long _jiffies(void) { struct timeval tv; microuptime(&tv); return tvtohz(&tv); } static inline unsigned long msecs_to_jiffies(unsigned long msecs) { struct timeval tv; tv.tv_sec = msecs / 1000000UL; tv.tv_usec = msecs % 1000000UL; return tvtohz(&tv); } #define jiffies _jiffies() #else #define jiffies ticks #endif #define HZ hz #define udelay(usec) DELAY(usec) #define mdelay(msec) DELAY((msec) * 1000) #define schedule_timeout(jiff) pause("dhdslp", jiff) #if defined(msleep) #undef msleep #endif #define msleep(msec) mdelay(msec) #define time_after(a, b) ((a) > (b)) #define time_after_eq(a, b) ((a) >= (b)) #define time_before(a, b) time_after((b), (a)) /* * kthread API (we use proc) */ typedef struct proc * VCHIQ_THREAD_T; VCHIQ_THREAD_T vchiq_thread_create(int (*threadfn)(void *data), void *data, const char namefmt[], ...); void set_user_nice(VCHIQ_THREAD_T p, int nice); void wake_up_process(VCHIQ_THREAD_T p); /* * Proc APIs */ void flush_signals(VCHIQ_THREAD_T); int fatal_signal_pending(VCHIQ_THREAD_T); /* * mbox API */ void bcm_mbox_write(int channel, uint32_t data); /* * Misc API */ #define ENODATA EINVAL #define __user #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) #define current curproc #define EXPORT_SYMBOL(x) #define PAGE_ALIGN(addr) round_page(addr) typedef void irqreturn_t; typedef off_t loff_t; #define BCM2835_MBOX_CHAN_VCHIQ 3 #define smp_mb wmb #define smp_rmb rmb #define smp_wmb wmb #define device_print_prettyname(dev) device_printf((dev), "") #endif /* __VCHI_BSD_H__ */ Index: projects/building-blocks/sys/dev/ath/if_ath_pci.c =================================================================== --- projects/building-blocks/sys/dev/ath/if_ath_pci.c (revision 278776) +++ projects/building-blocks/sys/dev/ath/if_ath_pci.c (revision 278777) @@ -1,467 +1,472 @@ /*- * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting * 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, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * * NO WARRANTY * 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 NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. */ #include __FBSDID("$FreeBSD$"); /* * PCI/Cardbus front-end for the Atheros Wireless LAN controller driver. */ #include "opt_ath.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* For EEPROM firmware */ #ifdef ATH_EEPROM_FIRMWARE #include #include #endif /* ATH_EEPROM_FIRMWARE */ /* * PCI glue. */ struct ath_pci_softc { struct ath_softc sc_sc; struct resource *sc_sr; /* memory resource */ struct resource *sc_irq; /* irq resource */ void *sc_ih; /* interrupt handler */ }; /* * XXX eventually this should be some system level definition * so modules will hvae probe/attach information like USB. * But for now.. */ struct pci_device_id { int vendor_id; int device_id; int sub_vendor_id; int sub_device_id; int driver_data; int match_populated:1; int match_vendor_id:1; int match_device_id:1; int match_sub_vendor_id:1; int match_sub_device_id:1; }; #define PCI_VDEVICE(v, s) \ .vendor_id = (v), \ .device_id = (s), \ .match_populated = 1, \ .match_vendor_id = 1, \ .match_device_id = 1 #define PCI_DEVICE_SUB(v, d, dv, ds) \ .match_populated = 1, \ .vendor_id = (v), .match_vendor_id = 1, \ .device_id = (d), .match_device_id = 1, \ .sub_vendor_id = (dv), .match_sub_vendor_id = 1, \ .sub_device_id = (ds), .match_sub_device_id = 1 #define PCI_VENDOR_ID_ATHEROS 0x168c #define PCI_VENDOR_ID_SAMSUNG 0x144d #define PCI_VENDOR_ID_AZWAVE 0x1a3b #define PCI_VENDOR_ID_FOXCONN 0x105b #define PCI_VENDOR_ID_ATTANSIC 0x1969 #define PCI_VENDOR_ID_ASUSTEK 0x1043 #define PCI_VENDOR_ID_DELL 0x1028 #define PCI_VENDOR_ID_QMI 0x1a32 #define PCI_VENDOR_ID_LENOVO 0x17aa #define PCI_VENDOR_ID_HP 0x103c #include "if_ath_pci_devlist.h" /* * Attempt to find a match for the given device in * the given device table. * * Returns the device structure or NULL if no matching * PCI device is found. */ static const struct pci_device_id * ath_pci_probe_device(device_t dev, const struct pci_device_id *dev_table, int nentries) { int i; int vendor_id, device_id; int sub_vendor_id, sub_device_id; vendor_id = pci_get_vendor(dev); device_id = pci_get_device(dev); sub_vendor_id = pci_get_subvendor(dev); sub_device_id = pci_get_subdevice(dev); for (i = 0; i < nentries; i++) { /* Don't match on non-populated (eg empty) entries */ if (! dev_table[i].match_populated) continue; if (dev_table[i].match_vendor_id && (dev_table[i].vendor_id != vendor_id)) continue; if (dev_table[i].match_device_id && (dev_table[i].device_id != device_id)) continue; if (dev_table[i].match_sub_vendor_id && (dev_table[i].sub_vendor_id != sub_vendor_id)) continue; if (dev_table[i].match_sub_device_id && (dev_table[i].sub_device_id != sub_device_id)) continue; /* Match */ return (&dev_table[i]); } return (NULL); } #define BS_BAR 0x10 #define PCIR_RETRY_TIMEOUT 0x41 #define PCIR_CFG_PMCSR 0x48 #define DEFAULT_CACHESIZE 32 static void ath_pci_setup(device_t dev) { uint8_t cz; /* XXX TODO: need to override the _system_ saved copies of this */ /* * If the cache line size is 0, force it to a reasonable * value. */ cz = pci_read_config(dev, PCIR_CACHELNSZ, 1); if (cz == 0) { pci_write_config(dev, PCIR_CACHELNSZ, DEFAULT_CACHESIZE / 4, 1); } /* Override the system latency timer */ pci_write_config(dev, PCIR_LATTIMER, 0xa8, 1); /* If a PCI NIC, force wakeup */ #ifdef ATH_PCI_WAKEUP_WAR /* XXX TODO: don't do this for non-PCI (ie, PCIe, Cardbus!) */ if (1) { uint16_t pmcsr; pmcsr = pci_read_config(dev, PCIR_CFG_PMCSR, 2); pmcsr |= 3; pci_write_config(dev, PCIR_CFG_PMCSR, pmcsr, 2); pmcsr &= ~3; pci_write_config(dev, PCIR_CFG_PMCSR, pmcsr, 2); } #endif /* * Disable retry timeout to keep PCI Tx retries from * interfering with C3 CPU state. */ pci_write_config(dev, PCIR_RETRY_TIMEOUT, 0, 1); } static int ath_pci_probe(device_t dev) { const char* devname; devname = ath_hal_probe(pci_get_vendor(dev), pci_get_device(dev)); if (devname != NULL) { device_set_desc(dev, devname); return BUS_PROBE_DEFAULT; } return ENXIO; } static int ath_pci_attach(device_t dev) { struct ath_pci_softc *psc = device_get_softc(dev); struct ath_softc *sc = &psc->sc_sc; int error = ENXIO; int rid; #ifdef ATH_EEPROM_FIRMWARE const struct firmware *fw = NULL; const char *buf; #endif const struct pci_device_id *pd; sc->sc_dev = dev; /* Do this lookup anyway; figure out what to do with it later */ pd = ath_pci_probe_device(dev, ath_pci_id_table, nitems(ath_pci_id_table)); if (pd) sc->sc_pci_devinfo = pd->driver_data; /* * Enable bus mastering. */ pci_enable_busmaster(dev); /* * Setup other PCI bus configuration parameters. */ ath_pci_setup(dev); /* * Setup memory-mapping of PCI registers. */ rid = BS_BAR; psc->sc_sr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (psc->sc_sr == NULL) { device_printf(dev, "cannot map register space\n"); goto bad; } sc->sc_st = (HAL_BUS_TAG) rman_get_bustag(psc->sc_sr); sc->sc_sh = (HAL_BUS_HANDLE) rman_get_bushandle(psc->sc_sr); /* * Mark device invalid so any interrupts (shared or otherwise) * that arrive before the HAL is setup are discarded. */ sc->sc_invalid = 1; + ATH_LOCK_INIT(sc); + ATH_PCU_LOCK_INIT(sc); + ATH_RX_LOCK_INIT(sc); + ATH_TX_LOCK_INIT(sc); + ATH_TX_IC_LOCK_INIT(sc); + ATH_TXSTATUS_LOCK_INIT(sc); + /* * Arrange interrupt line. */ rid = 0; psc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE|RF_ACTIVE); if (psc->sc_irq == NULL) { device_printf(dev, "could not map interrupt\n"); goto bad1; } if (bus_setup_intr(dev, psc->sc_irq, INTR_TYPE_NET | INTR_MPSAFE, NULL, ath_intr, sc, &psc->sc_ih)) { device_printf(dev, "could not establish interrupt\n"); goto bad2; } /* * Setup DMA descriptor area. */ if (bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */ 1, 0, /* alignment, bounds */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ 0x3ffff, /* maxsize XXX */ ATH_MAX_SCATTER, /* nsegments */ 0x3ffff, /* maxsegsize XXX */ BUS_DMA_ALLOCNOW, /* flags */ NULL, /* lockfunc */ NULL, /* lockarg */ &sc->sc_dmat)) { device_printf(dev, "cannot allocate DMA tag\n"); goto bad3; } #ifdef ATH_EEPROM_FIRMWARE /* * If there's an EEPROM firmware image, load that in. */ if (resource_string_value(device_get_name(dev), device_get_unit(dev), "eeprom_firmware", &buf) == 0) { if (bootverbose) device_printf(dev, "%s: looking up firmware @ '%s'\n", __func__, buf); fw = firmware_get(buf); if (fw == NULL) { device_printf(dev, "%s: couldn't find firmware\n", __func__); - goto bad3; + goto bad4; } device_printf(dev, "%s: EEPROM firmware @ %p\n", __func__, fw->data); sc->sc_eepromdata = malloc(fw->datasize, M_TEMP, M_WAITOK | M_ZERO); if (! sc->sc_eepromdata) { device_printf(dev, "%s: can't malloc eepromdata\n", __func__); - goto bad3; + goto bad4; } memcpy(sc->sc_eepromdata, fw->data, fw->datasize); firmware_put(fw, 0); } #endif /* ATH_EEPROM_FIRMWARE */ - ATH_LOCK_INIT(sc); - ATH_PCU_LOCK_INIT(sc); - ATH_RX_LOCK_INIT(sc); - ATH_TX_LOCK_INIT(sc); - ATH_TX_IC_LOCK_INIT(sc); - ATH_TXSTATUS_LOCK_INIT(sc); - error = ath_attach(pci_get_device(dev), sc); if (error == 0) /* success */ return 0; - ATH_TXSTATUS_LOCK_DESTROY(sc); - ATH_PCU_LOCK_DESTROY(sc); - ATH_RX_LOCK_DESTROY(sc); - ATH_TX_IC_LOCK_DESTROY(sc); - ATH_TX_LOCK_DESTROY(sc); - ATH_LOCK_DESTROY(sc); +#ifdef ATH_EEPROM_FIRMWARE +bad4: +#endif bus_dma_tag_destroy(sc->sc_dmat); bad3: bus_teardown_intr(dev, psc->sc_irq, psc->sc_ih); bad2: bus_release_resource(dev, SYS_RES_IRQ, 0, psc->sc_irq); bad1: bus_release_resource(dev, SYS_RES_MEMORY, BS_BAR, psc->sc_sr); + + ATH_TXSTATUS_LOCK_DESTROY(sc); + ATH_PCU_LOCK_DESTROY(sc); + ATH_RX_LOCK_DESTROY(sc); + ATH_TX_IC_LOCK_DESTROY(sc); + ATH_TX_LOCK_DESTROY(sc); + ATH_LOCK_DESTROY(sc); + bad: return (error); } static int ath_pci_detach(device_t dev) { struct ath_pci_softc *psc = device_get_softc(dev); struct ath_softc *sc = &psc->sc_sc; /* check if device was removed */ sc->sc_invalid = !bus_child_present(dev); /* * Do a config read to clear pre-existing pci error status. */ (void) pci_read_config(dev, PCIR_COMMAND, 4); ath_detach(sc); bus_generic_detach(dev); bus_teardown_intr(dev, psc->sc_irq, psc->sc_ih); bus_release_resource(dev, SYS_RES_IRQ, 0, psc->sc_irq); bus_dma_tag_destroy(sc->sc_dmat); bus_release_resource(dev, SYS_RES_MEMORY, BS_BAR, psc->sc_sr); if (sc->sc_eepromdata) free(sc->sc_eepromdata, M_TEMP); ATH_TXSTATUS_LOCK_DESTROY(sc); ATH_PCU_LOCK_DESTROY(sc); ATH_RX_LOCK_DESTROY(sc); ATH_TX_IC_LOCK_DESTROY(sc); ATH_TX_LOCK_DESTROY(sc); ATH_LOCK_DESTROY(sc); return (0); } static int ath_pci_shutdown(device_t dev) { struct ath_pci_softc *psc = device_get_softc(dev); ath_shutdown(&psc->sc_sc); return (0); } static int ath_pci_suspend(device_t dev) { struct ath_pci_softc *psc = device_get_softc(dev); ath_suspend(&psc->sc_sc); return (0); } static int ath_pci_resume(device_t dev) { struct ath_pci_softc *psc = device_get_softc(dev); /* * Suspend/resume resets the PCI configuration space. */ ath_pci_setup(dev); ath_resume(&psc->sc_sc); return (0); } static device_method_t ath_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ath_pci_probe), DEVMETHOD(device_attach, ath_pci_attach), DEVMETHOD(device_detach, ath_pci_detach), DEVMETHOD(device_shutdown, ath_pci_shutdown), DEVMETHOD(device_suspend, ath_pci_suspend), DEVMETHOD(device_resume, ath_pci_resume), { 0,0 } }; static driver_t ath_pci_driver = { "ath", ath_pci_methods, sizeof (struct ath_pci_softc) }; static devclass_t ath_devclass; DRIVER_MODULE(ath_pci, pci, ath_pci_driver, ath_devclass, 0, 0); MODULE_VERSION(ath_pci, 1); MODULE_DEPEND(ath_pci, wlan, 1, 1, 1); /* 802.11 media layer */ MODULE_DEPEND(ath_pci, if_ath, 1, 1, 1); /* if_ath driver */ Index: projects/building-blocks/sys/dev/dwc/if_dwc.c =================================================================== --- projects/building-blocks/sys/dev/dwc/if_dwc.c (revision 278776) +++ projects/building-blocks/sys/dev/dwc/if_dwc.c (revision 278777) @@ -1,1324 +1,1325 @@ /*- * Copyright (c) 2014 Ruslan Bukin * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ /* * Ethernet media access controller (EMAC) * Chapter 17, Altera Cyclone V Device Handbook (CV-5V2 2014.07.22) * * EMAC is an instance of the Synopsys DesignWare 3504-0 * Universal 10/100/1000 Ethernet MAC (DWC_gmac). */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "miibus_if.h" #define READ4(_sc, _reg) \ bus_read_4((_sc)->res[0], _reg) #define WRITE4(_sc, _reg, _val) \ bus_write_4((_sc)->res[0], _reg, _val) +#define MAC_RESET_TIMEOUT 100 #define WATCHDOG_TIMEOUT_SECS 5 #define STATS_HARVEST_INTERVAL 2 #define MII_CLK_VAL 2 #include #define DWC_LOCK(sc) mtx_lock(&(sc)->mtx) #define DWC_UNLOCK(sc) mtx_unlock(&(sc)->mtx) #define DWC_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mtx, MA_OWNED); #define DWC_ASSERT_UNLOCKED(sc) mtx_assert(&(sc)->mtx, MA_NOTOWNED); #define DDESC_TDES0_OWN (1 << 31) #define DDESC_TDES0_TXINT (1 << 30) #define DDESC_TDES0_TXLAST (1 << 29) #define DDESC_TDES0_TXFIRST (1 << 28) #define DDESC_TDES0_TXCRCDIS (1 << 27) #define DDESC_TDES0_TXRINGEND (1 << 21) #define DDESC_TDES0_TXCHAIN (1 << 20) #define DDESC_RDES0_OWN (1 << 31) #define DDESC_RDES0_FL_MASK 0x3fff #define DDESC_RDES0_FL_SHIFT 16 /* Frame Length */ #define DDESC_RDES1_CHAINED (1 << 14) struct dwc_bufmap { bus_dmamap_t map; struct mbuf *mbuf; }; /* * A hardware buffer descriptor. Rx and Tx buffers have the same descriptor * layout, but the bits in the flags field have different meanings. */ struct dwc_hwdesc { uint32_t tdes0; uint32_t tdes1; uint32_t addr; /* pointer to buffer data */ uint32_t addr_next; /* link to next descriptor */ }; /* * Driver data and defines. */ #define RX_DESC_COUNT 1024 #define RX_DESC_SIZE (sizeof(struct dwc_hwdesc) * RX_DESC_COUNT) #define TX_DESC_COUNT 1024 #define TX_DESC_SIZE (sizeof(struct dwc_hwdesc) * TX_DESC_COUNT) /* * The hardware imposes alignment restrictions on various objects involved in * DMA transfers. These values are expressed in bytes (not bits). */ #define DWC_DESC_RING_ALIGN 2048 struct dwc_softc { struct resource *res[2]; bus_space_tag_t bst; bus_space_handle_t bsh; device_t dev; int mii_clk; device_t miibus; struct mii_data * mii_softc; struct ifnet *ifp; int if_flags; struct mtx mtx; void * intr_cookie; struct callout dwc_callout; uint8_t phy_conn_type; uint8_t mactype; boolean_t link_is_up; boolean_t is_attached; boolean_t is_detaching; int tx_watchdog_count; int stats_harvest_count; /* RX */ bus_dma_tag_t rxdesc_tag; bus_dmamap_t rxdesc_map; struct dwc_hwdesc *rxdesc_ring; bus_addr_t rxdesc_ring_paddr; bus_dma_tag_t rxbuf_tag; struct dwc_bufmap rxbuf_map[RX_DESC_COUNT]; uint32_t rx_idx; /* TX */ bus_dma_tag_t txdesc_tag; bus_dmamap_t txdesc_map; struct dwc_hwdesc *txdesc_ring; bus_addr_t txdesc_ring_paddr; bus_dma_tag_t txbuf_tag; struct dwc_bufmap txbuf_map[RX_DESC_COUNT]; uint32_t tx_idx_head; uint32_t tx_idx_tail; int txcount; }; static struct resource_spec dwc_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; static void dwc_txfinish_locked(struct dwc_softc *sc); static void dwc_rxfinish_locked(struct dwc_softc *sc); static void dwc_stop_locked(struct dwc_softc *sc); static void dwc_setup_rxfilter(struct dwc_softc *sc); static inline uint32_t next_rxidx(struct dwc_softc *sc, uint32_t curidx) { return ((curidx + 1) % RX_DESC_COUNT); } static inline uint32_t next_txidx(struct dwc_softc *sc, uint32_t curidx) { return ((curidx + 1) % TX_DESC_COUNT); } static void dwc_get1paddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { if (error != 0) return; *(bus_addr_t *)arg = segs[0].ds_addr; } inline static uint32_t dwc_setup_txdesc(struct dwc_softc *sc, int idx, bus_addr_t paddr, uint32_t len) { uint32_t flags; uint32_t nidx; nidx = next_txidx(sc, idx); /* Addr/len 0 means we're clearing the descriptor after xmit done. */ if (paddr == 0 || len == 0) { flags = 0; --sc->txcount; } else { flags = DDESC_TDES0_TXCHAIN | DDESC_TDES0_TXFIRST | DDESC_TDES0_TXLAST | DDESC_TDES0_TXINT; ++sc->txcount; } sc->txdesc_ring[idx].addr = (uint32_t)(paddr); sc->txdesc_ring[idx].tdes0 = flags; sc->txdesc_ring[idx].tdes1 = len; if (paddr && len) { wmb(); sc->txdesc_ring[idx].tdes0 |= DDESC_TDES0_OWN; wmb(); } return (nidx); } static int dwc_setup_txbuf(struct dwc_softc *sc, int idx, struct mbuf **mp) { struct bus_dma_segment seg; int error, nsegs; struct mbuf * m; if ((m = m_defrag(*mp, M_NOWAIT)) == NULL) return (ENOMEM); *mp = m; error = bus_dmamap_load_mbuf_sg(sc->txbuf_tag, sc->txbuf_map[idx].map, m, &seg, &nsegs, 0); if (error != 0) { return (ENOMEM); } KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); bus_dmamap_sync(sc->txbuf_tag, sc->txbuf_map[idx].map, BUS_DMASYNC_PREWRITE); sc->txbuf_map[idx].mbuf = m; dwc_setup_txdesc(sc, idx, seg.ds_addr, seg.ds_len); return (0); } static void dwc_txstart_locked(struct dwc_softc *sc) { struct ifnet *ifp; struct mbuf *m; int enqueued; DWC_ASSERT_LOCKED(sc); if (!sc->link_is_up) return; ifp = sc->ifp; if (ifp->if_drv_flags & IFF_DRV_OACTIVE) { return; } enqueued = 0; for (;;) { if (sc->txcount == (TX_DESC_COUNT-1)) { ifp->if_drv_flags |= IFF_DRV_OACTIVE; break; } IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) break; if (dwc_setup_txbuf(sc, sc->tx_idx_head, &m) != 0) { IFQ_DRV_PREPEND(&ifp->if_snd, m); break; } BPF_MTAP(ifp, m); sc->tx_idx_head = next_txidx(sc, sc->tx_idx_head); ++enqueued; } if (enqueued != 0) { WRITE4(sc, TRANSMIT_POLL_DEMAND, 0x1); sc->tx_watchdog_count = WATCHDOG_TIMEOUT_SECS; } } static void dwc_txstart(struct ifnet *ifp) { struct dwc_softc *sc = ifp->if_softc; DWC_LOCK(sc); dwc_txstart_locked(sc); DWC_UNLOCK(sc); } static void dwc_stop_locked(struct dwc_softc *sc) { struct ifnet *ifp; int reg; DWC_ASSERT_LOCKED(sc); ifp = sc->ifp; ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); sc->tx_watchdog_count = 0; sc->stats_harvest_count = 0; callout_stop(&sc->dwc_callout); /* Stop DMA TX */ reg = READ4(sc, OPERATION_MODE); reg &= ~(MODE_ST); WRITE4(sc, OPERATION_MODE, reg); /* Flush TX */ reg = READ4(sc, OPERATION_MODE); reg |= (MODE_FTF); WRITE4(sc, OPERATION_MODE, reg); /* Stop transmitters */ reg = READ4(sc, MAC_CONFIGURATION); reg &= ~(CONF_TE | CONF_RE); WRITE4(sc, MAC_CONFIGURATION, reg); /* Stop DMA RX */ reg = READ4(sc, OPERATION_MODE); reg &= ~(MODE_SR); WRITE4(sc, OPERATION_MODE, reg); } static void dwc_clear_stats(struct dwc_softc *sc) { int reg; reg = READ4(sc, MMC_CONTROL); reg |= (MMC_CONTROL_CNTRST); WRITE4(sc, MMC_CONTROL, reg); } static void dwc_harvest_stats(struct dwc_softc *sc) { struct ifnet *ifp; /* We don't need to harvest too often. */ if (++sc->stats_harvest_count < STATS_HARVEST_INTERVAL) return; sc->stats_harvest_count = 0; ifp = sc->ifp; if_inc_counter(ifp, IFCOUNTER_IPACKETS, READ4(sc, RXFRAMECOUNT_GB)); if_inc_counter(ifp, IFCOUNTER_IMCASTS, READ4(sc, RXMULTICASTFRAMES_G)); if_inc_counter(ifp, IFCOUNTER_IERRORS, READ4(sc, RXOVERSIZE_G) + READ4(sc, RXUNDERSIZE_G) + READ4(sc, RXCRCERROR) + READ4(sc, RXALIGNMENTERROR) + READ4(sc, RXRUNTERROR) + READ4(sc, RXJABBERERROR) + READ4(sc, RXLENGTHERROR)); if_inc_counter(ifp, IFCOUNTER_OPACKETS, READ4(sc, TXFRAMECOUNT_G)); if_inc_counter(ifp, IFCOUNTER_OMCASTS, READ4(sc, TXMULTICASTFRAMES_G)); if_inc_counter(ifp, IFCOUNTER_OERRORS, READ4(sc, TXOVERSIZE_G) + READ4(sc, TXEXCESSDEF) + READ4(sc, TXCARRIERERR) + READ4(sc, TXUNDERFLOWERROR)); if_inc_counter(ifp, IFCOUNTER_COLLISIONS, READ4(sc, TXEXESSCOL) + READ4(sc, TXLATECOL)); dwc_clear_stats(sc); } static void dwc_tick(void *arg) { struct dwc_softc *sc; struct ifnet *ifp; int link_was_up; sc = arg; DWC_ASSERT_LOCKED(sc); ifp = sc->ifp; if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) return; /* * Typical tx watchdog. If this fires it indicates that we enqueued * packets for output and never got a txdone interrupt for them. Maybe * it's a missed interrupt somehow, just pretend we got one. */ if (sc->tx_watchdog_count > 0) { if (--sc->tx_watchdog_count == 0) { dwc_txfinish_locked(sc); } } /* Gather stats from hardware counters. */ dwc_harvest_stats(sc); /* Check the media status. */ link_was_up = sc->link_is_up; mii_tick(sc->mii_softc); if (sc->link_is_up && !link_was_up) dwc_txstart_locked(sc); /* Schedule another check one second from now. */ callout_reset(&sc->dwc_callout, hz, dwc_tick, sc); } static void dwc_init_locked(struct dwc_softc *sc) { struct ifnet *ifp = sc->ifp; int reg; DWC_ASSERT_LOCKED(sc); if (ifp->if_drv_flags & IFF_DRV_RUNNING) return; ifp->if_drv_flags |= IFF_DRV_RUNNING; dwc_setup_rxfilter(sc); /* Initializa DMA and enable transmitters */ reg = READ4(sc, OPERATION_MODE); reg |= (MODE_TSF | MODE_OSF | MODE_FUF); reg &= ~(MODE_RSF); reg |= (MODE_RTC_LEV32 << MODE_RTC_SHIFT); WRITE4(sc, OPERATION_MODE, reg); WRITE4(sc, INTERRUPT_ENABLE, INT_EN_DEFAULT); /* Start DMA */ reg = READ4(sc, OPERATION_MODE); reg |= (MODE_ST | MODE_SR); WRITE4(sc, OPERATION_MODE, reg); /* Enable transmitters */ reg = READ4(sc, MAC_CONFIGURATION); reg |= (CONF_JD | CONF_ACS | CONF_BE); reg |= (CONF_TE | CONF_RE); WRITE4(sc, MAC_CONFIGURATION, reg); /* * Call mii_mediachg() which will call back into dwc_miibus_statchg() * to set up the remaining config registers based on current media. */ mii_mediachg(sc->mii_softc); callout_reset(&sc->dwc_callout, hz, dwc_tick, sc); } static void dwc_init(void *if_softc) { struct dwc_softc *sc = if_softc; DWC_LOCK(sc); dwc_init_locked(sc); DWC_UNLOCK(sc); } inline static uint32_t dwc_setup_rxdesc(struct dwc_softc *sc, int idx, bus_addr_t paddr) { uint32_t nidx; sc->rxdesc_ring[idx].addr = (uint32_t)paddr; nidx = next_rxidx(sc, idx); sc->rxdesc_ring[idx].addr_next = sc->rxdesc_ring_paddr + \ (nidx * sizeof(struct dwc_hwdesc)); sc->rxdesc_ring[idx].tdes1 = DDESC_RDES1_CHAINED | MCLBYTES; wmb(); sc->rxdesc_ring[idx].tdes0 = DDESC_RDES0_OWN; wmb(); return (nidx); } static int dwc_setup_rxbuf(struct dwc_softc *sc, int idx, struct mbuf *m) { struct bus_dma_segment seg; int error, nsegs; m_adj(m, ETHER_ALIGN); error = bus_dmamap_load_mbuf_sg(sc->rxbuf_tag, sc->rxbuf_map[idx].map, m, &seg, &nsegs, 0); if (error != 0) { return (error); } KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); bus_dmamap_sync(sc->rxbuf_tag, sc->rxbuf_map[idx].map, BUS_DMASYNC_PREREAD); sc->rxbuf_map[idx].mbuf = m; dwc_setup_rxdesc(sc, idx, seg.ds_addr); return (0); } static struct mbuf * dwc_alloc_mbufcl(struct dwc_softc *sc) { struct mbuf *m; m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); m->m_pkthdr.len = m->m_len = m->m_ext.ext_size; return (m); } static void dwc_media_status(struct ifnet * ifp, struct ifmediareq *ifmr) { struct dwc_softc *sc; struct mii_data *mii; sc = ifp->if_softc; mii = sc->mii_softc; DWC_LOCK(sc); mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; DWC_UNLOCK(sc); } static int dwc_media_change_locked(struct dwc_softc *sc) { return (mii_mediachg(sc->mii_softc)); } static int dwc_media_change(struct ifnet * ifp) { struct dwc_softc *sc; int error; sc = ifp->if_softc; DWC_LOCK(sc); error = dwc_media_change_locked(sc); DWC_UNLOCK(sc); return (error); } static const uint8_t nibbletab[] = { /* 0x0 0000 -> 0000 */ 0x0, /* 0x1 0001 -> 1000 */ 0x8, /* 0x2 0010 -> 0100 */ 0x4, /* 0x3 0011 -> 1100 */ 0xc, /* 0x4 0100 -> 0010 */ 0x2, /* 0x5 0101 -> 1010 */ 0xa, /* 0x6 0110 -> 0110 */ 0x6, /* 0x7 0111 -> 1110 */ 0xe, /* 0x8 1000 -> 0001 */ 0x1, /* 0x9 1001 -> 1001 */ 0x9, /* 0xa 1010 -> 0101 */ 0x5, /* 0xb 1011 -> 1101 */ 0xd, /* 0xc 1100 -> 0011 */ 0x3, /* 0xd 1101 -> 1011 */ 0xb, /* 0xe 1110 -> 0111 */ 0x7, /* 0xf 1111 -> 1111 */ 0xf, }; static uint8_t bitreverse(uint8_t x) { return (nibbletab[x & 0xf] << 4) | nibbletab[x >> 4]; } static void dwc_setup_rxfilter(struct dwc_softc *sc) { struct ifmultiaddr *ifma; struct ifnet *ifp; uint8_t *eaddr; uint32_t crc; uint8_t val; int hashbit; int hashreg; int ffval; int reg; int lo; int hi; DWC_ASSERT_LOCKED(sc); ifp = sc->ifp; /* * Set the multicast (group) filter hash. */ if ((ifp->if_flags & IFF_ALLMULTI)) ffval = (FRAME_FILTER_PM); else { ffval = (FRAME_FILTER_HMC); if_maddr_rlock(ifp); TAILQ_FOREACH(ifma, &sc->ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; crc = ether_crc32_le(LLADDR((struct sockaddr_dl *) ifma->ifma_addr), ETHER_ADDR_LEN); /* Take lower 8 bits and reverse it */ val = bitreverse(~crc & 0xff); hashreg = (val >> 5); hashbit = (val & 31); reg = READ4(sc, HASH_TABLE_REG(hashreg)); reg |= (1 << hashbit); WRITE4(sc, HASH_TABLE_REG(hashreg), reg); } if_maddr_runlock(ifp); } /* * Set the individual address filter hash. */ if (ifp->if_flags & IFF_PROMISC) ffval |= (FRAME_FILTER_PR); /* * Set the primary address. */ eaddr = IF_LLADDR(ifp); lo = eaddr[0] | (eaddr[1] << 8) | (eaddr[2] << 16) | (eaddr[3] << 24); hi = eaddr[4] | (eaddr[5] << 8); WRITE4(sc, MAC_ADDRESS_LOW(0), lo); WRITE4(sc, MAC_ADDRESS_HIGH(0), hi); WRITE4(sc, MAC_FRAME_FILTER, ffval); } static int dwc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct dwc_softc *sc; struct mii_data *mii; struct ifreq *ifr; int mask, error; sc = ifp->if_softc; ifr = (struct ifreq *)data; error = 0; switch (cmd) { case SIOCSIFFLAGS: DWC_LOCK(sc); if (ifp->if_flags & IFF_UP) { if (ifp->if_drv_flags & IFF_DRV_RUNNING) { if ((ifp->if_flags ^ sc->if_flags) & (IFF_PROMISC | IFF_ALLMULTI)) dwc_setup_rxfilter(sc); } else { if (!sc->is_detaching) dwc_init_locked(sc); } } else { if (ifp->if_drv_flags & IFF_DRV_RUNNING) dwc_stop_locked(sc); } sc->if_flags = ifp->if_flags; DWC_UNLOCK(sc); break; case SIOCADDMULTI: case SIOCDELMULTI: if (ifp->if_drv_flags & IFF_DRV_RUNNING) { DWC_LOCK(sc); dwc_setup_rxfilter(sc); DWC_UNLOCK(sc); } break; case SIOCSIFMEDIA: case SIOCGIFMEDIA: mii = sc->mii_softc; error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); break; case SIOCSIFCAP: mask = ifp->if_capenable ^ ifr->ifr_reqcap; if (mask & IFCAP_VLAN_MTU) { /* No work to do except acknowledge the change took */ ifp->if_capenable ^= IFCAP_VLAN_MTU; } break; default: error = ether_ioctl(ifp, cmd, data); break; } return (error); } static void dwc_txfinish_locked(struct dwc_softc *sc) { struct dwc_bufmap *bmap; struct dwc_hwdesc *desc; struct ifnet *ifp; DWC_ASSERT_LOCKED(sc); ifp = sc->ifp; while (sc->tx_idx_tail != sc->tx_idx_head) { desc = &sc->txdesc_ring[sc->tx_idx_tail]; if ((desc->tdes0 & DDESC_TDES0_OWN) != 0) break; bmap = &sc->txbuf_map[sc->tx_idx_tail]; bus_dmamap_sync(sc->txbuf_tag, bmap->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->txbuf_tag, bmap->map); m_freem(bmap->mbuf); bmap->mbuf = NULL; dwc_setup_txdesc(sc, sc->tx_idx_tail, 0, 0); sc->tx_idx_tail = next_txidx(sc, sc->tx_idx_tail); } /* If there are no buffers outstanding, muzzle the watchdog. */ if (sc->tx_idx_tail == sc->tx_idx_head) { sc->tx_watchdog_count = 0; } } static void dwc_rxfinish_locked(struct dwc_softc *sc) { struct ifnet *ifp; struct mbuf *m0; struct mbuf *m; int error; int rdes0; int idx; int len; ifp = sc->ifp; for (;;) { idx = sc->rx_idx; rdes0 = sc->rxdesc_ring[idx].tdes0; if ((rdes0 & DDESC_RDES0_OWN) != 0) break; bus_dmamap_sync(sc->rxbuf_tag, sc->rxbuf_map[idx].map, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->rxbuf_tag, sc->rxbuf_map[idx].map); len = (rdes0 >> DDESC_RDES0_FL_SHIFT) & DDESC_RDES0_FL_MASK; if (len != 0) { m = sc->rxbuf_map[idx].mbuf; m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = len; m->m_len = len; if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); DWC_UNLOCK(sc); (*ifp->if_input)(ifp, m); DWC_LOCK(sc); } else { /* XXX Zero-length packet ? */ } if ((m0 = dwc_alloc_mbufcl(sc)) != NULL) { if ((error = dwc_setup_rxbuf(sc, idx, m0)) != 0) { /* * XXX Now what? * We've got a hole in the rx ring. */ } } else if_inc_counter(sc->ifp, IFCOUNTER_IQDROPS, 1); sc->rx_idx = next_rxidx(sc, sc->rx_idx); } } static void dwc_intr(void *arg) { struct dwc_softc *sc; uint32_t reg; sc = arg; DWC_LOCK(sc); reg = READ4(sc, INTERRUPT_STATUS); if (reg) { mii_mediachg(sc->mii_softc); READ4(sc, SGMII_RGMII_SMII_CTRL_STATUS); } reg = READ4(sc, DMA_STATUS); if (reg & DMA_STATUS_NIS) { if (reg & DMA_STATUS_RI) dwc_rxfinish_locked(sc); if (reg & DMA_STATUS_TI) dwc_txfinish_locked(sc); } if (reg & DMA_STATUS_AIS) { if (reg & DMA_STATUS_FBI) { /* Fatal bus error */ device_printf(sc->dev, "Ethernet DMA error, restarting controller.\n"); dwc_stop_locked(sc); dwc_init_locked(sc); } } WRITE4(sc, DMA_STATUS, reg & DMA_STATUS_INTR_MASK); DWC_UNLOCK(sc); } static int setup_dma(struct dwc_softc *sc) { struct mbuf *m; int error; int nidx; int idx; /* * Set up TX descriptor ring, descriptors, and dma maps. */ error = bus_dma_tag_create( bus_get_dma_tag(sc->dev), /* Parent tag. */ DWC_DESC_RING_ALIGN, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ TX_DESC_SIZE, 1, /* maxsize, nsegments */ TX_DESC_SIZE, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->txdesc_tag); if (error != 0) { device_printf(sc->dev, "could not create TX ring DMA tag.\n"); goto out; } error = bus_dmamem_alloc(sc->txdesc_tag, (void**)&sc->txdesc_ring, BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->txdesc_map); if (error != 0) { device_printf(sc->dev, "could not allocate TX descriptor ring.\n"); goto out; } error = bus_dmamap_load(sc->txdesc_tag, sc->txdesc_map, sc->txdesc_ring, TX_DESC_SIZE, dwc_get1paddr, &sc->txdesc_ring_paddr, 0); if (error != 0) { device_printf(sc->dev, "could not load TX descriptor ring map.\n"); goto out; } for (idx = 0; idx < TX_DESC_COUNT; idx++) { sc->txdesc_ring[idx].tdes0 = DDESC_TDES0_TXCHAIN; sc->txdesc_ring[idx].tdes1 = 0; nidx = next_txidx(sc, idx); sc->txdesc_ring[idx].addr_next = sc->txdesc_ring_paddr + \ (nidx * sizeof(struct dwc_hwdesc)); } error = bus_dma_tag_create( bus_get_dma_tag(sc->dev), /* Parent tag. */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MCLBYTES, 1, /* maxsize, nsegments */ MCLBYTES, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->txbuf_tag); if (error != 0) { device_printf(sc->dev, "could not create TX ring DMA tag.\n"); goto out; } for (idx = 0; idx < TX_DESC_COUNT; idx++) { error = bus_dmamap_create(sc->txbuf_tag, BUS_DMA_COHERENT, &sc->txbuf_map[idx].map); if (error != 0) { device_printf(sc->dev, "could not create TX buffer DMA map.\n"); goto out; } dwc_setup_txdesc(sc, idx, 0, 0); } /* * Set up RX descriptor ring, descriptors, dma maps, and mbufs. */ error = bus_dma_tag_create( bus_get_dma_tag(sc->dev), /* Parent tag. */ DWC_DESC_RING_ALIGN, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ RX_DESC_SIZE, 1, /* maxsize, nsegments */ RX_DESC_SIZE, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->rxdesc_tag); if (error != 0) { device_printf(sc->dev, "could not create RX ring DMA tag.\n"); goto out; } error = bus_dmamem_alloc(sc->rxdesc_tag, (void **)&sc->rxdesc_ring, BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, &sc->rxdesc_map); if (error != 0) { device_printf(sc->dev, "could not allocate RX descriptor ring.\n"); goto out; } error = bus_dmamap_load(sc->rxdesc_tag, sc->rxdesc_map, sc->rxdesc_ring, RX_DESC_SIZE, dwc_get1paddr, &sc->rxdesc_ring_paddr, 0); if (error != 0) { device_printf(sc->dev, "could not load RX descriptor ring map.\n"); goto out; } error = bus_dma_tag_create( bus_get_dma_tag(sc->dev), /* Parent tag. */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MCLBYTES, 1, /* maxsize, nsegments */ MCLBYTES, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->rxbuf_tag); if (error != 0) { device_printf(sc->dev, "could not create RX buf DMA tag.\n"); goto out; } for (idx = 0; idx < RX_DESC_COUNT; idx++) { error = bus_dmamap_create(sc->rxbuf_tag, BUS_DMA_COHERENT, &sc->rxbuf_map[idx].map); if (error != 0) { device_printf(sc->dev, "could not create RX buffer DMA map.\n"); goto out; } if ((m = dwc_alloc_mbufcl(sc)) == NULL) { device_printf(sc->dev, "Could not alloc mbuf\n"); error = ENOMEM; goto out; } if ((error = dwc_setup_rxbuf(sc, idx, m)) != 0) { device_printf(sc->dev, "could not create new RX buffer.\n"); goto out; } } out: if (error != 0) return (ENXIO); return (0); } static int dwc_get_hwaddr(struct dwc_softc *sc, uint8_t *hwaddr) { int rnd; int lo; int hi; /* * Try to recover a MAC address from the running hardware. If there's * something non-zero there, assume the bootloader did the right thing * and just use it. * * Otherwise, set the address to a convenient locally assigned address, * 'bsd' + random 24 low-order bits. 'b' is 0x62, which has the locally * assigned bit set, and the broadcast/multicast bit clear. */ lo = READ4(sc, MAC_ADDRESS_LOW(0)); hi = READ4(sc, MAC_ADDRESS_HIGH(0)) & 0xffff; if ((lo != 0xffffffff) || (hi != 0xffff)) { hwaddr[0] = (lo >> 0) & 0xff; hwaddr[1] = (lo >> 8) & 0xff; hwaddr[2] = (lo >> 16) & 0xff; hwaddr[3] = (lo >> 24) & 0xff; hwaddr[4] = (hi >> 0) & 0xff; hwaddr[5] = (hi >> 8) & 0xff; } else { rnd = arc4random() & 0x00ffffff; hwaddr[0] = 'b'; hwaddr[1] = 's'; hwaddr[2] = 'd'; hwaddr[3] = rnd >> 16; hwaddr[4] = rnd >> 8; hwaddr[5] = rnd >> 0; } return (0); } static int dwc_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "snps,dwmac")) return (ENXIO); device_set_desc(dev, "Gigabit Ethernet Controller"); return (BUS_PROBE_DEFAULT); } static int dwc_attach(device_t dev) { uint8_t macaddr[ETHER_ADDR_LEN]; struct dwc_softc *sc; struct ifnet *ifp; int error; int reg; int i; sc = device_get_softc(dev); sc->dev = dev; sc->mii_clk = MII_CLK_VAL; sc->rx_idx = 0; sc->txcount = TX_DESC_COUNT; if (bus_alloc_resources(dev, dwc_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } /* Memory interface */ sc->bst = rman_get_bustag(sc->res[0]); sc->bsh = rman_get_bushandle(sc->res[0]); - mtx_init(&sc->mtx, device_get_nameunit(sc->dev), - MTX_NETWORK_LOCK, MTX_DEF); - - callout_init_mtx(&sc->dwc_callout, &sc->mtx, 0); - - /* Setup interrupt handler. */ - error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_NET | INTR_MPSAFE, - NULL, dwc_intr, sc, &sc->intr_cookie); - if (error != 0) { - device_printf(dev, "could not setup interrupt handler.\n"); - return (ENXIO); - } - /* Read MAC before reset */ if (dwc_get_hwaddr(sc, macaddr)) { device_printf(sc->dev, "can't get mac\n"); return (ENXIO); } /* Reset */ reg = READ4(sc, BUS_MODE); reg |= (BUS_MODE_SWR); WRITE4(sc, BUS_MODE, reg); - for (i = 0; i < 100; i++) { + for (i = 0; i < MAC_RESET_TIMEOUT; i++) { if ((READ4(sc, BUS_MODE) & BUS_MODE_SWR) == 0) break; DELAY(10); } - if (i == 0) { + if (i >= MAC_RESET_TIMEOUT) { device_printf(sc->dev, "Can't reset DWC.\n"); return (ENXIO); } reg = READ4(sc, BUS_MODE); reg |= (BUS_MODE_EIGHTXPBL); reg |= (BUS_MODE_PBL_BEATS_8 << BUS_MODE_PBL_SHIFT); WRITE4(sc, BUS_MODE, reg); /* * DMA must be stop while changing descriptor list addresses. */ reg = READ4(sc, OPERATION_MODE); reg &= ~(MODE_ST | MODE_SR); WRITE4(sc, OPERATION_MODE, reg); if (setup_dma(sc)) return (ENXIO); /* Setup addresses */ WRITE4(sc, RX_DESCR_LIST_ADDR, sc->rxdesc_ring_paddr); WRITE4(sc, TX_DESCR_LIST_ADDR, sc->txdesc_ring_paddr); + + mtx_init(&sc->mtx, device_get_nameunit(sc->dev), + MTX_NETWORK_LOCK, MTX_DEF); + + callout_init_mtx(&sc->dwc_callout, &sc->mtx, 0); + + /* Setup interrupt handler. */ + error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_NET | INTR_MPSAFE, + NULL, dwc_intr, sc, &sc->intr_cookie); + if (error != 0) { + device_printf(dev, "could not setup interrupt handler.\n"); + return (ENXIO); + } /* Set up the ethernet interface. */ sc->ifp = ifp = if_alloc(IFT_ETHER); ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_capabilities = IFCAP_VLAN_MTU; ifp->if_capenable = ifp->if_capabilities; ifp->if_start = dwc_txstart; ifp->if_ioctl = dwc_ioctl; ifp->if_init = dwc_init; IFQ_SET_MAXLEN(&ifp->if_snd, TX_DESC_COUNT - 1); ifp->if_snd.ifq_drv_maxlen = TX_DESC_COUNT - 1; IFQ_SET_READY(&ifp->if_snd); ifp->if_hdrlen = sizeof(struct ether_vlan_header); /* Attach the mii driver. */ error = mii_attach(dev, &sc->miibus, ifp, dwc_media_change, dwc_media_status, BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, 0); if (error != 0) { device_printf(dev, "PHY attach failed\n"); return (ENXIO); } sc->mii_softc = device_get_softc(sc->miibus); /* All ready to run, attach the ethernet interface. */ ether_ifattach(ifp, macaddr); sc->is_attached = true; return (0); } static int dwc_miibus_read_reg(device_t dev, int phy, int reg) { struct dwc_softc *sc; uint16_t mii; size_t cnt; int rv = 0; sc = device_get_softc(dev); mii = ((phy & GMII_ADDRESS_PA_MASK) << GMII_ADDRESS_PA_SHIFT) | ((reg & GMII_ADDRESS_GR_MASK) << GMII_ADDRESS_GR_SHIFT) | (sc->mii_clk << GMII_ADDRESS_CR_SHIFT) | GMII_ADDRESS_GB; /* Busy flag */ WRITE4(sc, GMII_ADDRESS, mii); for (cnt = 0; cnt < 1000; cnt++) { if (!(READ4(sc, GMII_ADDRESS) & GMII_ADDRESS_GB)) { rv = READ4(sc, GMII_DATA); break; } DELAY(10); } return rv; } static int dwc_miibus_write_reg(device_t dev, int phy, int reg, int val) { struct dwc_softc *sc; uint16_t mii; size_t cnt; sc = device_get_softc(dev); mii = ((phy & GMII_ADDRESS_PA_MASK) << GMII_ADDRESS_PA_SHIFT) | ((reg & GMII_ADDRESS_GR_MASK) << GMII_ADDRESS_GR_SHIFT) | (sc->mii_clk << GMII_ADDRESS_CR_SHIFT) | GMII_ADDRESS_GB | GMII_ADDRESS_GW; WRITE4(sc, GMII_DATA, val); WRITE4(sc, GMII_ADDRESS, mii); for (cnt = 0; cnt < 1000; cnt++) { if (!(READ4(sc, GMII_ADDRESS) & GMII_ADDRESS_GB)) { break; } DELAY(10); } return (0); } static void dwc_miibus_statchg(device_t dev) { struct dwc_softc *sc; struct mii_data *mii; int reg; /* * Called by the MII bus driver when the PHY establishes * link to set the MAC interface registers. */ sc = device_get_softc(dev); DWC_ASSERT_LOCKED(sc); mii = sc->mii_softc; if (mii->mii_media_status & IFM_ACTIVE) sc->link_is_up = true; else sc->link_is_up = false; reg = READ4(sc, MAC_CONFIGURATION); switch (IFM_SUBTYPE(mii->mii_media_active)) { case IFM_1000_T: case IFM_1000_SX: reg &= ~(CONF_FES | CONF_PS); break; case IFM_100_TX: reg |= (CONF_FES | CONF_PS); break; case IFM_10_T: reg &= ~(CONF_FES); reg |= (CONF_PS); break; case IFM_NONE: sc->link_is_up = false; return; default: sc->link_is_up = false; device_printf(dev, "Unsupported media %u\n", IFM_SUBTYPE(mii->mii_media_active)); return; } if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) reg |= (CONF_DM); else reg &= ~(CONF_DM); WRITE4(sc, MAC_CONFIGURATION, reg); } static device_method_t dwc_methods[] = { DEVMETHOD(device_probe, dwc_probe), DEVMETHOD(device_attach, dwc_attach), /* MII Interface */ DEVMETHOD(miibus_readreg, dwc_miibus_read_reg), DEVMETHOD(miibus_writereg, dwc_miibus_write_reg), DEVMETHOD(miibus_statchg, dwc_miibus_statchg), { 0, 0 } }; static driver_t dwc_driver = { "dwc", dwc_methods, sizeof(struct dwc_softc), }; static devclass_t dwc_devclass; DRIVER_MODULE(dwc, simplebus, dwc_driver, dwc_devclass, 0, 0); DRIVER_MODULE(miibus, dwc, miibus_driver, miibus_devclass, 0, 0); MODULE_DEPEND(dwc, ether, 1, 1, 1); MODULE_DEPEND(dwc, miibus, 1, 1, 1); Index: projects/building-blocks/sys/dev/netmap/netmap.c =================================================================== --- projects/building-blocks/sys/dev/netmap/netmap.c (revision 278776) +++ projects/building-blocks/sys/dev/netmap/netmap.c (revision 278777) @@ -1,3096 +1,3093 @@ /* * Copyright (C) 2011-2014 Matteo Landi, Luigi Rizzo. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ /* * $FreeBSD$ * * This module supports memory mapped access to network devices, * see netmap(4). * * The module uses a large, memory pool allocated by the kernel * and accessible as mmapped memory by multiple userspace threads/processes. * The memory pool contains packet buffers and "netmap rings", * i.e. user-accessible copies of the interface's queues. * * Access to the network card works like this: * 1. a process/thread issues one or more open() on /dev/netmap, to create * select()able file descriptor on which events are reported. * 2. on each descriptor, the process issues an ioctl() to identify * the interface that should report events to the file descriptor. * 3. on each descriptor, the process issues an mmap() request to * map the shared memory region within the process' address space. * The list of interesting queues is indicated by a location in * the shared memory region. * 4. using the functions in the netmap(4) userspace API, a process * can look up the occupation state of a queue, access memory buffers, * and retrieve received packets or enqueue packets to transmit. * 5. using some ioctl()s the process can synchronize the userspace view * of the queue with the actual status in the kernel. This includes both * receiving the notification of new packets, and transmitting new * packets on the output interface. * 6. select() or poll() can be used to wait for events on individual * transmit or receive queues (or all queues for a given interface). * SYNCHRONIZATION (USER) The netmap rings and data structures may be shared among multiple user threads or even independent processes. Any synchronization among those threads/processes is delegated to the threads themselves. Only one thread at a time can be in a system call on the same netmap ring. The OS does not enforce this and only guarantees against system crashes in case of invalid usage. LOCKING (INTERNAL) Within the kernel, access to the netmap rings is protected as follows: - a spinlock on each ring, to handle producer/consumer races on RX rings attached to the host stack (against multiple host threads writing from the host stack to the same ring), and on 'destination' rings attached to a VALE switch (i.e. RX rings in VALE ports, and TX rings in NIC/host ports) protecting multiple active senders for the same destination) - an atomic variable to guarantee that there is at most one instance of *_*xsync() on the ring at any time. For rings connected to user file descriptors, an atomic_test_and_set() protects this, and the lock on the ring is not actually used. For NIC RX rings connected to a VALE switch, an atomic_test_and_set() is also used to prevent multiple executions (the driver might indeed already guarantee this). For NIC TX rings connected to a VALE switch, the lock arbitrates access to the queue (both when allocating buffers and when pushing them out). - *xsync() should be protected against initializations of the card. On FreeBSD most devices have the reset routine protected by a RING lock (ixgbe, igb, em) or core lock (re). lem is missing the RING protection on rx_reset(), this should be added. On linux there is an external lock on the tx path, which probably also arbitrates access to the reset routine. XXX to be revised - a per-interface core_lock protecting access from the host stack while interfaces may be detached from netmap mode. XXX there should be no need for this lock if we detach the interfaces only while they are down. --- VALE SWITCH --- NMG_LOCK() serializes all modifications to switches and ports. A switch cannot be deleted until all ports are gone. For each switch, an SX lock (RWlock on linux) protects deletion of ports. When configuring or deleting a new port, the lock is acquired in exclusive mode (after holding NMG_LOCK). When forwarding, the lock is acquired in shared mode (without NMG_LOCK). The lock is held throughout the entire forwarding cycle, during which the thread may incur in a page fault. Hence it is important that sleepable shared locks are used. On the rx ring, the per-port lock is grabbed initially to reserve a number of slot in the ring, then the lock is released, packets are copied from source to destination, and then the lock is acquired again and the receive ring is updated. (A similar thing is done on the tx ring for NIC and host stack ports attached to the switch) */ /* --- internals ---- * * Roadmap to the code that implements the above. * * > 1. a process/thread issues one or more open() on /dev/netmap, to create * > select()able file descriptor on which events are reported. * * Internally, we allocate a netmap_priv_d structure, that will be * initialized on ioctl(NIOCREGIF). * * os-specific: * FreeBSD: netmap_open (netmap_freebsd.c). The priv is * per-thread. * linux: linux_netmap_open (netmap_linux.c). The priv is * per-open. * * > 2. on each descriptor, the process issues an ioctl() to identify * > the interface that should report events to the file descriptor. * * Implemented by netmap_ioctl(), NIOCREGIF case, with nmr->nr_cmd==0. * Most important things happen in netmap_get_na() and * netmap_do_regif(), called from there. Additional details can be * found in the comments above those functions. * * In all cases, this action creates/takes-a-reference-to a * netmap_*_adapter describing the port, and allocates a netmap_if * and all necessary netmap rings, filling them with netmap buffers. * * In this phase, the sync callbacks for each ring are set (these are used * in steps 5 and 6 below). The callbacks depend on the type of adapter. * The adapter creation/initialization code puts them in the * netmap_adapter (fields na->nm_txsync and na->nm_rxsync). Then, they * are copied from there to the netmap_kring's during netmap_do_regif(), by * the nm_krings_create() callback. All the nm_krings_create callbacks * actually call netmap_krings_create() to perform this and the other * common stuff. netmap_krings_create() also takes care of the host rings, * if needed, by setting their sync callbacks appropriately. * * Additional actions depend on the kind of netmap_adapter that has been * registered: * * - netmap_hw_adapter: [netmap.c] * This is a system netdev/ifp with native netmap support. * The ifp is detached from the host stack by redirecting: * - transmissions (from the network stack) to netmap_transmit() * - receive notifications to the nm_notify() callback for * this adapter. The callback is normally netmap_notify(), unless * the ifp is attached to a bridge using bwrap, in which case it * is netmap_bwrap_intr_notify(). * * - netmap_generic_adapter: [netmap_generic.c] * A system netdev/ifp without native netmap support. * * (the decision about native/non native support is taken in * netmap_get_hw_na(), called by netmap_get_na()) * * - netmap_vp_adapter [netmap_vale.c] * Returned by netmap_get_bdg_na(). * This is a persistent or ephemeral VALE port. Ephemeral ports * are created on the fly if they don't already exist, and are * always attached to a bridge. * Persistent VALE ports must must be created seperately, and i * then attached like normal NICs. The NIOCREGIF we are examining * will find them only if they had previosly been created and * attached (see VALE_CTL below). * * - netmap_pipe_adapter [netmap_pipe.c] * Returned by netmap_get_pipe_na(). * Both pipe ends are created, if they didn't already exist. * * - netmap_monitor_adapter [netmap_monitor.c] * Returned by netmap_get_monitor_na(). * If successful, the nm_sync callbacks of the monitored adapter * will be intercepted by the returned monitor. * * - netmap_bwrap_adapter [netmap_vale.c] * Cannot be obtained in this way, see VALE_CTL below * * * os-specific: * linux: we first go through linux_netmap_ioctl() to * adapt the FreeBSD interface to the linux one. * * * > 3. on each descriptor, the process issues an mmap() request to * > map the shared memory region within the process' address space. * > The list of interesting queues is indicated by a location in * > the shared memory region. * * os-specific: * FreeBSD: netmap_mmap_single (netmap_freebsd.c). * linux: linux_netmap_mmap (netmap_linux.c). * * > 4. using the functions in the netmap(4) userspace API, a process * > can look up the occupation state of a queue, access memory buffers, * > and retrieve received packets or enqueue packets to transmit. * * these actions do not involve the kernel. * * > 5. using some ioctl()s the process can synchronize the userspace view * > of the queue with the actual status in the kernel. This includes both * > receiving the notification of new packets, and transmitting new * > packets on the output interface. * * These are implemented in netmap_ioctl(), NIOCTXSYNC and NIOCRXSYNC * cases. They invoke the nm_sync callbacks on the netmap_kring * structures, as initialized in step 2 and maybe later modified * by a monitor. Monitors, however, will always call the original * callback before doing anything else. * * * > 6. select() or poll() can be used to wait for events on individual * > transmit or receive queues (or all queues for a given interface). * * Implemented in netmap_poll(). This will call the same nm_sync() * callbacks as in step 5 above. * * os-specific: * linux: we first go through linux_netmap_poll() to adapt * the FreeBSD interface to the linux one. * * * ---- VALE_CTL ----- * * VALE switches are controlled by issuing a NIOCREGIF with a non-null * nr_cmd in the nmreq structure. These subcommands are handled by * netmap_bdg_ctl() in netmap_vale.c. Persistent VALE ports are created * and destroyed by issuing the NETMAP_BDG_NEWIF and NETMAP_BDG_DELIF * subcommands, respectively. * * Any network interface known to the system (including a persistent VALE * port) can be attached to a VALE switch by issuing the * NETMAP_BDG_ATTACH subcommand. After the attachment, persistent VALE ports * look exactly like ephemeral VALE ports (as created in step 2 above). The * attachment of other interfaces, instead, requires the creation of a * netmap_bwrap_adapter. Moreover, the attached interface must be put in * netmap mode. This may require the creation of a netmap_generic_adapter if * we have no native support for the interface, or if generic adapters have * been forced by sysctl. * * Both persistent VALE ports and bwraps are handled by netmap_get_bdg_na(), * called by nm_bdg_ctl_attach(), and discriminated by the nm_bdg_attach() * callback. In the case of the bwrap, the callback creates the * netmap_bwrap_adapter. The initialization of the bwrap is then * completed by calling netmap_do_regif() on it, in the nm_bdg_ctl() * callback (netmap_bwrap_bdg_ctl in netmap_vale.c). * A generic adapter for the wrapped ifp will be created if needed, when * netmap_get_bdg_na() calls netmap_get_hw_na(). * * * ---- DATAPATHS ----- * * -= SYSTEM DEVICE WITH NATIVE SUPPORT =- * * na == NA(ifp) == netmap_hw_adapter created in DEVICE_netmap_attach() * * - tx from netmap userspace: * concurrently: * 1) ioctl(NIOCTXSYNC)/netmap_poll() in process context * kring->nm_sync() == DEVICE_netmap_txsync() * 2) device interrupt handler * na->nm_notify() == netmap_notify() * - rx from netmap userspace: * concurrently: * 1) ioctl(NIOCRXSYNC)/netmap_poll() in process context * kring->nm_sync() == DEVICE_netmap_rxsync() * 2) device interrupt handler * na->nm_notify() == netmap_notify() * - tx from host stack * concurrently: * 1) host stack * netmap_transmit() * na->nm_notify == netmap_notify() * 2) ioctl(NIOCRXSYNC)/netmap_poll() in process context * kring->nm_sync() == netmap_rxsync_from_host_compat * netmap_rxsync_from_host(na, NULL, NULL) * - tx to host stack * ioctl(NIOCTXSYNC)/netmap_poll() in process context * kring->nm_sync() == netmap_txsync_to_host_compat * netmap_txsync_to_host(na) * NM_SEND_UP() * FreeBSD: na->if_input() == ?? XXX * linux: netif_rx() with NM_MAGIC_PRIORITY_RX * * * * -= SYSTEM DEVICE WITH GENERIC SUPPORT =- * * * * -= VALE PORT =- * * * * -= NETMAP PIPE =- * * * * -= SYSTEM DEVICE WITH NATIVE SUPPORT, CONNECTED TO VALE, NO HOST RINGS =- * * * * -= SYSTEM DEVICE WITH NATIVE SUPPORT, CONNECTED TO VALE, WITH HOST RINGS =- * * * * -= SYSTEM DEVICE WITH GENERIC SUPPORT, CONNECTED TO VALE, NO HOST RINGS =- * * * * -= SYSTEM DEVICE WITH GENERIC SUPPORT, CONNECTED TO VALE, WITH HOST RINGS =- * * * */ /* * OS-specific code that is used only within this file. * Other OS-specific code that must be accessed by drivers * is present in netmap_kern.h */ #if defined(__FreeBSD__) #include /* prerequisite */ #include #include #include /* defines used in kernel.h */ #include /* types used in module initialization */ #include /* cdevsw struct, UID, GID */ #include /* FIONBIO */ #include #include /* struct socket */ #include #include #include #include /* sockaddrs */ #include #include #include #include #include #include #include /* BIOCIMMEDIATE */ #include /* bus_dmamap_* */ #include #include /* reduce conditional code */ // linux API, use for the knlist in FreeBSD /* use a private mutex for the knlist */ #define init_waitqueue_head(x) do { \ struct mtx *m = &(x)->m; \ mtx_init(m, "nm_kn_lock", NULL, MTX_DEF); \ knlist_init_mtx(&(x)->si.si_note, m); \ } while (0) #define OS_selrecord(a, b) selrecord(a, &((b)->si)) #define OS_selwakeup(a, b) freebsd_selwakeup(a, b) #elif defined(linux) #include "bsd_glue.h" #elif defined(__APPLE__) #warning OSX support is only partial #include "osx_glue.h" #else #error Unsupported platform #endif /* unsupported */ /* * common headers */ #include #include #include MALLOC_DEFINE(M_NETMAP, "netmap", "Network memory map"); /* * The following variables are used by the drivers and replicate * fields in the global memory pool. They only refer to buffers * used by physical interfaces. */ u_int netmap_total_buffers; u_int netmap_buf_size; char *netmap_buffer_base; /* also address of an invalid buffer */ /* user-controlled variables */ int netmap_verbose; static int netmap_no_timestamp; /* don't timestamp on rxsync */ SYSCTL_NODE(_dev, OID_AUTO, netmap, CTLFLAG_RW, 0, "Netmap args"); SYSCTL_INT(_dev_netmap, OID_AUTO, verbose, CTLFLAG_RW, &netmap_verbose, 0, "Verbose mode"); SYSCTL_INT(_dev_netmap, OID_AUTO, no_timestamp, CTLFLAG_RW, &netmap_no_timestamp, 0, "no_timestamp"); int netmap_mitigate = 1; SYSCTL_INT(_dev_netmap, OID_AUTO, mitigate, CTLFLAG_RW, &netmap_mitigate, 0, ""); int netmap_no_pendintr = 1; SYSCTL_INT(_dev_netmap, OID_AUTO, no_pendintr, CTLFLAG_RW, &netmap_no_pendintr, 0, "Always look for new received packets."); int netmap_txsync_retry = 2; SYSCTL_INT(_dev_netmap, OID_AUTO, txsync_retry, CTLFLAG_RW, &netmap_txsync_retry, 0 , "Number of txsync loops in bridge's flush."); int netmap_adaptive_io = 0; SYSCTL_INT(_dev_netmap, OID_AUTO, adaptive_io, CTLFLAG_RW, &netmap_adaptive_io, 0 , "Adaptive I/O on paravirt"); int netmap_flags = 0; /* debug flags */ int netmap_fwd = 0; /* force transparent mode */ int netmap_mmap_unreg = 0; /* allow mmap of unregistered fds */ /* * netmap_admode selects the netmap mode to use. * Invalid values are reset to NETMAP_ADMODE_BEST */ enum { NETMAP_ADMODE_BEST = 0, /* use native, fallback to generic */ NETMAP_ADMODE_NATIVE, /* either native or none */ NETMAP_ADMODE_GENERIC, /* force generic */ NETMAP_ADMODE_LAST }; static int netmap_admode = NETMAP_ADMODE_BEST; int netmap_generic_mit = 100*1000; /* Generic mitigation interval in nanoseconds. */ int netmap_generic_ringsize = 1024; /* Generic ringsize. */ int netmap_generic_rings = 1; /* number of queues in generic. */ SYSCTL_INT(_dev_netmap, OID_AUTO, flags, CTLFLAG_RW, &netmap_flags, 0 , ""); SYSCTL_INT(_dev_netmap, OID_AUTO, fwd, CTLFLAG_RW, &netmap_fwd, 0 , ""); SYSCTL_INT(_dev_netmap, OID_AUTO, mmap_unreg, CTLFLAG_RW, &netmap_mmap_unreg, 0, ""); SYSCTL_INT(_dev_netmap, OID_AUTO, admode, CTLFLAG_RW, &netmap_admode, 0 , ""); SYSCTL_INT(_dev_netmap, OID_AUTO, generic_mit, CTLFLAG_RW, &netmap_generic_mit, 0 , ""); SYSCTL_INT(_dev_netmap, OID_AUTO, generic_ringsize, CTLFLAG_RW, &netmap_generic_ringsize, 0 , ""); SYSCTL_INT(_dev_netmap, OID_AUTO, generic_rings, CTLFLAG_RW, &netmap_generic_rings, 0 , ""); NMG_LOCK_T netmap_global_lock; static void nm_kr_get(struct netmap_kring *kr) { while (NM_ATOMIC_TEST_AND_SET(&kr->nr_busy)) tsleep(kr, 0, "NM_KR_GET", 4); } /* * mark the ring as stopped, and run through the locks * to make sure other users get to see it. */ static void netmap_disable_ring(struct netmap_kring *kr) { kr->nkr_stopped = 1; nm_kr_get(kr); mtx_lock(&kr->q_lock); mtx_unlock(&kr->q_lock); nm_kr_put(kr); } /* stop or enable a single tx ring */ void netmap_set_txring(struct netmap_adapter *na, u_int ring_id, int stopped) { if (stopped) netmap_disable_ring(na->tx_rings + ring_id); else na->tx_rings[ring_id].nkr_stopped = 0; /* nofify that the stopped state has changed. This is currently *only used by bwrap to propagate the state to its own krings. * (see netmap_bwrap_intr_notify). */ na->nm_notify(na, ring_id, NR_TX, NAF_DISABLE_NOTIFY); } /* stop or enable a single rx ring */ void netmap_set_rxring(struct netmap_adapter *na, u_int ring_id, int stopped) { if (stopped) netmap_disable_ring(na->rx_rings + ring_id); else na->rx_rings[ring_id].nkr_stopped = 0; /* nofify that the stopped state has changed. This is currently *only used by bwrap to propagate the state to its own krings. * (see netmap_bwrap_intr_notify). */ na->nm_notify(na, ring_id, NR_RX, NAF_DISABLE_NOTIFY); } /* stop or enable all the rings of na */ void netmap_set_all_rings(struct netmap_adapter *na, int stopped) { int i; u_int ntx, nrx; if (!nm_netmap_on(na)) return; ntx = netmap_real_tx_rings(na); nrx = netmap_real_rx_rings(na); for (i = 0; i < ntx; i++) { netmap_set_txring(na, i, stopped); } for (i = 0; i < nrx; i++) { netmap_set_rxring(na, i, stopped); } } /* * Convenience function used in drivers. Waits for current txsync()s/rxsync()s * to finish and prevents any new one from starting. Call this before turning * netmap mode off, or before removing the harware rings (e.g., on module * onload). As a rule of thumb for linux drivers, this should be placed near * each napi_disable(). */ void netmap_disable_all_rings(struct ifnet *ifp) { netmap_set_all_rings(NA(ifp), 1 /* stopped */); } /* * Convenience function used in drivers. Re-enables rxsync and txsync on the * adapter's rings In linux drivers, this should be placed near each * napi_enable(). */ void netmap_enable_all_rings(struct ifnet *ifp) { netmap_set_all_rings(NA(ifp), 0 /* enabled */); } /* * generic bound_checking function */ u_int nm_bound_var(u_int *v, u_int dflt, u_int lo, u_int hi, const char *msg) { u_int oldv = *v; const char *op = NULL; if (dflt < lo) dflt = lo; if (dflt > hi) dflt = hi; if (oldv < lo) { *v = dflt; op = "Bump"; } else if (oldv > hi) { *v = hi; op = "Clamp"; } if (op && msg) printf("%s %s to %d (was %d)\n", op, msg, *v, oldv); return *v; } /* * packet-dump function, user-supplied or static buffer. * The destination buffer must be at least 30+4*len */ const char * nm_dump_buf(char *p, int len, int lim, char *dst) { static char _dst[8192]; int i, j, i0; static char hex[] ="0123456789abcdef"; char *o; /* output position */ #define P_HI(x) hex[((x) & 0xf0)>>4] #define P_LO(x) hex[((x) & 0xf)] #define P_C(x) ((x) >= 0x20 && (x) <= 0x7e ? (x) : '.') if (!dst) dst = _dst; if (lim <= 0 || lim > len) lim = len; o = dst; sprintf(o, "buf 0x%p len %d lim %d\n", p, len, lim); o += strlen(o); /* hexdump routine */ for (i = 0; i < lim; ) { sprintf(o, "%5d: ", i); o += strlen(o); memset(o, ' ', 48); i0 = i; for (j=0; j < 16 && i < lim; i++, j++) { o[j*3] = P_HI(p[i]); o[j*3+1] = P_LO(p[i]); } i = i0; for (j=0; j < 16 && i < lim; i++, j++) o[j + 48] = P_C(p[i]); o[j+48] = '\n'; o += j+49; } *o = '\0'; #undef P_HI #undef P_LO #undef P_C return dst; } /* * Fetch configuration from the device, to cope with dynamic * reconfigurations after loading the module. */ /* call with NMG_LOCK held */ int netmap_update_config(struct netmap_adapter *na) { u_int txr, txd, rxr, rxd; txr = txd = rxr = rxd = 0; - if (na->nm_config) { - na->nm_config(na, &txr, &txd, &rxr, &rxd); - } else { + if (na->nm_config == NULL || + na->nm_config(na, &txr, &txd, &rxr, &rxd)) { /* take whatever we had at init time */ txr = na->num_tx_rings; txd = na->num_tx_desc; rxr = na->num_rx_rings; rxd = na->num_rx_desc; } if (na->num_tx_rings == txr && na->num_tx_desc == txd && na->num_rx_rings == rxr && na->num_rx_desc == rxd) return 0; /* nothing changed */ if (netmap_verbose || na->active_fds > 0) { D("stored config %s: txring %d x %d, rxring %d x %d", na->name, na->num_tx_rings, na->num_tx_desc, na->num_rx_rings, na->num_rx_desc); D("new config %s: txring %d x %d, rxring %d x %d", na->name, txr, txd, rxr, rxd); } if (na->active_fds == 0) { D("configuration changed (but fine)"); na->num_tx_rings = txr; na->num_tx_desc = txd; na->num_rx_rings = rxr; na->num_rx_desc = rxd; return 0; } D("configuration changed while active, this is bad..."); return 1; } /* kring->nm_sync callback for the host tx ring */ static int netmap_txsync_to_host_compat(struct netmap_kring *kring, int flags) { (void)flags; /* unused */ netmap_txsync_to_host(kring->na); return 0; } /* kring->nm_sync callback for the host rx ring */ static int netmap_rxsync_from_host_compat(struct netmap_kring *kring, int flags) { (void)flags; /* unused */ netmap_rxsync_from_host(kring->na, NULL, NULL); return 0; } /* create the krings array and initialize the fields common to all adapters. * The array layout is this: * * +----------+ * na->tx_rings ----->| | \ * | | } na->num_tx_ring * | | / * +----------+ * | | host tx kring * na->rx_rings ----> +----------+ * | | \ * | | } na->num_rx_rings * | | / * +----------+ * | | host rx kring * +----------+ * na->tailroom ----->| | \ * | | } tailroom bytes * | | / * +----------+ * * Note: for compatibility, host krings are created even when not needed. * The tailroom space is currently used by vale ports for allocating leases. */ /* call with NMG_LOCK held */ int netmap_krings_create(struct netmap_adapter *na, u_int tailroom) { u_int i, len, ndesc; struct netmap_kring *kring; u_int ntx, nrx; /* account for the (possibly fake) host rings */ ntx = na->num_tx_rings + 1; nrx = na->num_rx_rings + 1; len = (ntx + nrx) * sizeof(struct netmap_kring) + tailroom; na->tx_rings = malloc((size_t)len, M_DEVBUF, M_NOWAIT | M_ZERO); if (na->tx_rings == NULL) { D("Cannot allocate krings"); return ENOMEM; } na->rx_rings = na->tx_rings + ntx; /* * All fields in krings are 0 except the one initialized below. * but better be explicit on important kring fields. */ ndesc = na->num_tx_desc; for (i = 0; i < ntx; i++) { /* Transmit rings */ kring = &na->tx_rings[i]; bzero(kring, sizeof(*kring)); kring->na = na; kring->ring_id = i; kring->nkr_num_slots = ndesc; if (i < na->num_tx_rings) { kring->nm_sync = na->nm_txsync; } else if (i == na->num_tx_rings) { kring->nm_sync = netmap_txsync_to_host_compat; } /* * IMPORTANT: Always keep one slot empty. */ kring->rhead = kring->rcur = kring->nr_hwcur = 0; kring->rtail = kring->nr_hwtail = ndesc - 1; snprintf(kring->name, sizeof(kring->name) - 1, "%s TX%d", na->name, i); ND("ktx %s h %d c %d t %d", kring->name, kring->rhead, kring->rcur, kring->rtail); mtx_init(&kring->q_lock, "nm_txq_lock", NULL, MTX_DEF); init_waitqueue_head(&kring->si); } ndesc = na->num_rx_desc; for (i = 0; i < nrx; i++) { /* Receive rings */ kring = &na->rx_rings[i]; bzero(kring, sizeof(*kring)); kring->na = na; kring->ring_id = i; kring->nkr_num_slots = ndesc; if (i < na->num_rx_rings) { kring->nm_sync = na->nm_rxsync; } else if (i == na->num_rx_rings) { kring->nm_sync = netmap_rxsync_from_host_compat; } kring->rhead = kring->rcur = kring->nr_hwcur = 0; kring->rtail = kring->nr_hwtail = 0; snprintf(kring->name, sizeof(kring->name) - 1, "%s RX%d", na->name, i); ND("krx %s h %d c %d t %d", kring->name, kring->rhead, kring->rcur, kring->rtail); mtx_init(&kring->q_lock, "nm_rxq_lock", NULL, MTX_DEF); init_waitqueue_head(&kring->si); } init_waitqueue_head(&na->tx_si); init_waitqueue_head(&na->rx_si); na->tailroom = na->rx_rings + nrx; return 0; } #ifdef __FreeBSD__ static void netmap_knlist_destroy(NM_SELINFO_T *si) { /* XXX kqueue(9) needed; these will mirror knlist_init. */ knlist_delete(&si->si.si_note, curthread, 0 /* not locked */ ); knlist_destroy(&si->si.si_note); /* now we don't need the mutex anymore */ mtx_destroy(&si->m); } #endif /* __FreeBSD__ */ /* undo the actions performed by netmap_krings_create */ /* call with NMG_LOCK held */ void netmap_krings_delete(struct netmap_adapter *na) { struct netmap_kring *kring = na->tx_rings; /* we rely on the krings layout described above */ for ( ; kring != na->tailroom; kring++) { mtx_destroy(&kring->q_lock); netmap_knlist_destroy(&kring->si); } free(na->tx_rings, M_DEVBUF); na->tx_rings = na->rx_rings = na->tailroom = NULL; } /* * Destructor for NIC ports. They also have an mbuf queue * on the rings connected to the host so we need to purge * them first. */ /* call with NMG_LOCK held */ static void netmap_hw_krings_delete(struct netmap_adapter *na) { struct mbq *q = &na->rx_rings[na->num_rx_rings].rx_queue; ND("destroy sw mbq with len %d", mbq_len(q)); mbq_purge(q); mbq_safe_destroy(q); netmap_krings_delete(na); } /* create a new netmap_if for a newly registered fd. * If this is the first registration of the adapter, * also create the netmap rings and their in-kernel view, * the netmap krings. */ /* call with NMG_LOCK held */ static struct netmap_if* netmap_if_new(struct netmap_adapter *na) { struct netmap_if *nifp; if (netmap_update_config(na)) { /* configuration mismatch, report and fail */ return NULL; } if (na->active_fds) /* already registered */ goto final; /* create and init the krings arrays. * Depending on the adapter, this may also create * the netmap rings themselves */ if (na->nm_krings_create(na)) return NULL; /* create all missing netmap rings */ if (netmap_mem_rings_create(na)) goto cleanup; final: /* in all cases, create a new netmap if */ nifp = netmap_mem_if_new(na); if (nifp == NULL) goto cleanup; return (nifp); cleanup: if (na->active_fds == 0) { netmap_mem_rings_delete(na); na->nm_krings_delete(na); } return NULL; } /* grab a reference to the memory allocator, if we don't have one already. The * reference is taken from the netmap_adapter registered with the priv. */ /* call with NMG_LOCK held */ static int netmap_get_memory_locked(struct netmap_priv_d* p) { struct netmap_mem_d *nmd; int error = 0; if (p->np_na == NULL) { if (!netmap_mmap_unreg) return ENODEV; /* for compatibility with older versions of the API * we use the global allocator when no interface has been * registered */ nmd = &nm_mem; } else { nmd = p->np_na->nm_mem; } if (p->np_mref == NULL) { error = netmap_mem_finalize(nmd, p->np_na); if (!error) p->np_mref = nmd; } else if (p->np_mref != nmd) { /* a virtual port has been registered, but previous * syscalls already used the global allocator. * We cannot continue */ error = ENODEV; } return error; } /* call with NMG_LOCK *not* held */ int netmap_get_memory(struct netmap_priv_d* p) { int error; NMG_LOCK(); error = netmap_get_memory_locked(p); NMG_UNLOCK(); return error; } /* call with NMG_LOCK held */ static int netmap_have_memory_locked(struct netmap_priv_d* p) { return p->np_mref != NULL; } /* call with NMG_LOCK held */ static void netmap_drop_memory_locked(struct netmap_priv_d* p) { if (p->np_mref) { netmap_mem_deref(p->np_mref, p->np_na); p->np_mref = NULL; } } /* * Call nm_register(ifp,0) to stop netmap mode on the interface and * revert to normal operation. * The second argument is the nifp to work on. In some cases it is * not attached yet to the netmap_priv_d so we need to pass it as * a separate argument. */ /* call with NMG_LOCK held */ static void netmap_do_unregif(struct netmap_priv_d *priv, struct netmap_if *nifp) { struct netmap_adapter *na = priv->np_na; NMG_LOCK_ASSERT(); na->active_fds--; if (na->active_fds <= 0) { /* last instance */ if (netmap_verbose) D("deleting last instance for %s", na->name); /* * (TO CHECK) This function is only called * when the last reference to this file descriptor goes * away. This means we cannot have any pending poll() * or interrupt routine operating on the structure. * XXX The file may be closed in a thread while * another thread is using it. * Linux keeps the file opened until the last reference * by any outstanding ioctl/poll or mmap is gone. * FreeBSD does not track mmap()s (but we do) and * wakes up any sleeping poll(). Need to check what * happens if the close() occurs while a concurrent * syscall is running. */ na->nm_register(na, 0); /* off, clear flags */ /* Wake up any sleeping threads. netmap_poll will * then return POLLERR * XXX The wake up now must happen during *_down(), when * we order all activities to stop. -gl */ netmap_knlist_destroy(&na->tx_si); netmap_knlist_destroy(&na->rx_si); /* delete rings and buffers */ netmap_mem_rings_delete(na); na->nm_krings_delete(na); } /* delete the nifp */ netmap_mem_if_delete(na, nifp); } /* call with NMG_LOCK held */ static __inline int nm_tx_si_user(struct netmap_priv_d *priv) { return (priv->np_na != NULL && (priv->np_txqlast - priv->np_txqfirst > 1)); } /* call with NMG_LOCK held */ static __inline int nm_rx_si_user(struct netmap_priv_d *priv) { return (priv->np_na != NULL && (priv->np_rxqlast - priv->np_rxqfirst > 1)); } /* * Destructor of the netmap_priv_d, called when the fd has * no active open() and mmap(). Also called in error paths. * * returns 1 if this is the last instance and we can free priv */ /* call with NMG_LOCK held */ int netmap_dtor_locked(struct netmap_priv_d *priv) { struct netmap_adapter *na = priv->np_na; #ifdef __FreeBSD__ /* * np_refcount is the number of active mmaps on * this file descriptor */ if (--priv->np_refcount > 0) { return 0; } #endif /* __FreeBSD__ */ if (!na) { return 1; //XXX is it correct? } netmap_do_unregif(priv, priv->np_nifp); priv->np_nifp = NULL; netmap_drop_memory_locked(priv); if (priv->np_na) { if (nm_tx_si_user(priv)) na->tx_si_users--; if (nm_rx_si_user(priv)) na->rx_si_users--; netmap_adapter_put(na); priv->np_na = NULL; } return 1; } /* call with NMG_LOCK *not* held */ void netmap_dtor(void *data) { struct netmap_priv_d *priv = data; int last_instance; NMG_LOCK(); last_instance = netmap_dtor_locked(priv); NMG_UNLOCK(); if (last_instance) { bzero(priv, sizeof(*priv)); /* for safety */ free(priv, M_DEVBUF); } } /* * Handlers for synchronization of the queues from/to the host. * Netmap has two operating modes: * - in the default mode, the rings connected to the host stack are * just another ring pair managed by userspace; * - in transparent mode (XXX to be defined) incoming packets * (from the host or the NIC) are marked as NS_FORWARD upon * arrival, and the user application has a chance to reset the * flag for packets that should be dropped. * On the RXSYNC or poll(), packets in RX rings between * kring->nr_kcur and ring->cur with NS_FORWARD still set are moved * to the other side. * The transfer NIC --> host is relatively easy, just encapsulate * into mbufs and we are done. The host --> NIC side is slightly * harder because there might not be room in the tx ring so it * might take a while before releasing the buffer. */ /* * pass a chain of buffers to the host stack as coming from 'dst' * We do not need to lock because the queue is private. */ static void netmap_send_up(struct ifnet *dst, struct mbq *q) { struct mbuf *m; /* send packets up, outside the lock */ while ((m = mbq_dequeue(q)) != NULL) { if (netmap_verbose & NM_VERB_HOST) D("sending up pkt %p size %d", m, MBUF_LEN(m)); NM_SEND_UP(dst, m); } mbq_destroy(q); } /* * put a copy of the buffers marked NS_FORWARD into an mbuf chain. * Take packets from hwcur to ring->head marked NS_FORWARD (or forced) * and pass them up. Drop remaining packets in the unlikely event * of an mbuf shortage. */ static void netmap_grab_packets(struct netmap_kring *kring, struct mbq *q, int force) { u_int const lim = kring->nkr_num_slots - 1; u_int const head = kring->ring->head; u_int n; struct netmap_adapter *na = kring->na; for (n = kring->nr_hwcur; n != head; n = nm_next(n, lim)) { struct mbuf *m; struct netmap_slot *slot = &kring->ring->slot[n]; if ((slot->flags & NS_FORWARD) == 0 && !force) continue; if (slot->len < 14 || slot->len > NETMAP_BUF_SIZE(na)) { RD(5, "bad pkt at %d len %d", n, slot->len); continue; } slot->flags &= ~NS_FORWARD; // XXX needed ? /* XXX TODO: adapt to the case of a multisegment packet */ m = m_devget(NMB(na, slot), slot->len, 0, na->ifp, NULL); if (m == NULL) break; mbq_enqueue(q, m); } } /* * Send to the NIC rings packets marked NS_FORWARD between * kring->nr_hwcur and kring->rhead * Called under kring->rx_queue.lock on the sw rx ring, */ static u_int netmap_sw_to_nic(struct netmap_adapter *na) { struct netmap_kring *kring = &na->rx_rings[na->num_rx_rings]; struct netmap_slot *rxslot = kring->ring->slot; u_int i, rxcur = kring->nr_hwcur; u_int const head = kring->rhead; u_int const src_lim = kring->nkr_num_slots - 1; u_int sent = 0; /* scan rings to find space, then fill as much as possible */ for (i = 0; i < na->num_tx_rings; i++) { struct netmap_kring *kdst = &na->tx_rings[i]; struct netmap_ring *rdst = kdst->ring; u_int const dst_lim = kdst->nkr_num_slots - 1; /* XXX do we trust ring or kring->rcur,rtail ? */ for (; rxcur != head && !nm_ring_empty(rdst); rxcur = nm_next(rxcur, src_lim) ) { struct netmap_slot *src, *dst, tmp; u_int dst_cur = rdst->cur; src = &rxslot[rxcur]; if ((src->flags & NS_FORWARD) == 0 && !netmap_fwd) continue; sent++; dst = &rdst->slot[dst_cur]; tmp = *src; src->buf_idx = dst->buf_idx; src->flags = NS_BUF_CHANGED; dst->buf_idx = tmp.buf_idx; dst->len = tmp.len; dst->flags = NS_BUF_CHANGED; rdst->cur = nm_next(dst_cur, dst_lim); } /* if (sent) XXX txsync ? */ } return sent; } /* * netmap_txsync_to_host() passes packets up. We are called from a * system call in user process context, and the only contention * can be among multiple user threads erroneously calling * this routine concurrently. */ void netmap_txsync_to_host(struct netmap_adapter *na) { struct netmap_kring *kring = &na->tx_rings[na->num_tx_rings]; struct netmap_ring *ring = kring->ring; u_int const lim = kring->nkr_num_slots - 1; u_int const head = kring->rhead; struct mbq q; /* Take packets from hwcur to head and pass them up. * force head = cur since netmap_grab_packets() stops at head * In case of no buffers we give up. At the end of the loop, * the queue is drained in all cases. */ mbq_init(&q); ring->cur = head; netmap_grab_packets(kring, &q, 1 /* force */); ND("have %d pkts in queue", mbq_len(&q)); kring->nr_hwcur = head; kring->nr_hwtail = head + lim; if (kring->nr_hwtail > lim) kring->nr_hwtail -= lim + 1; nm_txsync_finalize(kring); netmap_send_up(na->ifp, &q); } /* * rxsync backend for packets coming from the host stack. * They have been put in kring->rx_queue by netmap_transmit(). * We protect access to the kring using kring->rx_queue.lock * * This routine also does the selrecord if called from the poll handler * (we know because td != NULL). * * NOTE: on linux, selrecord() is defined as a macro and uses pwait * as an additional hidden argument. * returns the number of packets delivered to tx queues in * transparent mode, or a negative value if error */ int netmap_rxsync_from_host(struct netmap_adapter *na, struct thread *td, void *pwait) { struct netmap_kring *kring = &na->rx_rings[na->num_rx_rings]; struct netmap_ring *ring = kring->ring; u_int nm_i, n; u_int const lim = kring->nkr_num_slots - 1; u_int const head = kring->rhead; int ret = 0; struct mbq *q = &kring->rx_queue; (void)pwait; /* disable unused warnings */ (void)td; mbq_lock(q); /* First part: import newly received packets */ n = mbq_len(q); if (n) { /* grab packets from the queue */ struct mbuf *m; uint32_t stop_i; nm_i = kring->nr_hwtail; stop_i = nm_prev(nm_i, lim); while ( nm_i != stop_i && (m = mbq_dequeue(q)) != NULL ) { int len = MBUF_LEN(m); struct netmap_slot *slot = &ring->slot[nm_i]; m_copydata(m, 0, len, NMB(na, slot)); ND("nm %d len %d", nm_i, len); if (netmap_verbose) D("%s", nm_dump_buf(NMB(na, slot),len, 128, NULL)); slot->len = len; slot->flags = kring->nkr_slot_flags; nm_i = nm_next(nm_i, lim); m_freem(m); } kring->nr_hwtail = nm_i; } /* * Second part: skip past packets that userspace has released. */ nm_i = kring->nr_hwcur; if (nm_i != head) { /* something was released */ if (netmap_fwd || kring->ring->flags & NR_FORWARD) ret = netmap_sw_to_nic(na); kring->nr_hwcur = head; } nm_rxsync_finalize(kring); /* access copies of cur,tail in the kring */ if (kring->rcur == kring->rtail && td) /* no bufs available */ OS_selrecord(td, &kring->si); mbq_unlock(q); return ret; } /* Get a netmap adapter for the port. * * If it is possible to satisfy the request, return 0 * with *na containing the netmap adapter found. * Otherwise return an error code, with *na containing NULL. * * When the port is attached to a bridge, we always return * EBUSY. * Otherwise, if the port is already bound to a file descriptor, * then we unconditionally return the existing adapter into *na. * In all the other cases, we return (into *na) either native, * generic or NULL, according to the following table: * * native_support * active_fds dev.netmap.admode YES NO * ------------------------------------------------------- * >0 * NA(ifp) NA(ifp) * * 0 NETMAP_ADMODE_BEST NATIVE GENERIC * 0 NETMAP_ADMODE_NATIVE NATIVE NULL * 0 NETMAP_ADMODE_GENERIC GENERIC GENERIC * */ int netmap_get_hw_na(struct ifnet *ifp, struct netmap_adapter **na) { /* generic support */ int i = netmap_admode; /* Take a snapshot. */ int error = 0; struct netmap_adapter *prev_na; struct netmap_generic_adapter *gna; *na = NULL; /* default */ /* reset in case of invalid value */ if (i < NETMAP_ADMODE_BEST || i >= NETMAP_ADMODE_LAST) i = netmap_admode = NETMAP_ADMODE_BEST; if (NETMAP_CAPABLE(ifp)) { prev_na = NA(ifp); /* If an adapter already exists, return it if * there are active file descriptors or if * netmap is not forced to use generic * adapters. */ if (NETMAP_OWNED_BY_ANY(prev_na) || i != NETMAP_ADMODE_GENERIC || prev_na->na_flags & NAF_FORCE_NATIVE #ifdef WITH_PIPES /* ugly, but we cannot allow an adapter switch * if some pipe is referring to this one */ || prev_na->na_next_pipe > 0 #endif ) { *na = prev_na; return 0; } } /* If there isn't native support and netmap is not allowed * to use generic adapters, we cannot satisfy the request. */ if (!NETMAP_CAPABLE(ifp) && i == NETMAP_ADMODE_NATIVE) return EOPNOTSUPP; /* Otherwise, create a generic adapter and return it, * saving the previously used netmap adapter, if any. * * Note that here 'prev_na', if not NULL, MUST be a * native adapter, and CANNOT be a generic one. This is * true because generic adapters are created on demand, and * destroyed when not used anymore. Therefore, if the adapter * currently attached to an interface 'ifp' is generic, it * must be that * (NA(ifp)->active_fds > 0 || NETMAP_OWNED_BY_KERN(NA(ifp))). * Consequently, if NA(ifp) is generic, we will enter one of * the branches above. This ensures that we never override * a generic adapter with another generic adapter. */ prev_na = NA(ifp); error = generic_netmap_attach(ifp); if (error) return error; *na = NA(ifp); gna = (struct netmap_generic_adapter*)NA(ifp); gna->prev = prev_na; /* save old na */ if (prev_na != NULL) { ifunit_ref(ifp->if_xname); // XXX add a refcount ? netmap_adapter_get(prev_na); } ND("Created generic NA %p (prev %p)", gna, gna->prev); return 0; } /* * MUST BE CALLED UNDER NMG_LOCK() * * Get a refcounted reference to a netmap adapter attached * to the interface specified by nmr. * This is always called in the execution of an ioctl(). * * Return ENXIO if the interface specified by the request does * not exist, ENOTSUP if netmap is not supported by the interface, * EBUSY if the interface is already attached to a bridge, * EINVAL if parameters are invalid, ENOMEM if needed resources * could not be allocated. * If successful, hold a reference to the netmap adapter. * * No reference is kept on the real interface, which may then * disappear at any time. */ int netmap_get_na(struct nmreq *nmr, struct netmap_adapter **na, int create) { struct ifnet *ifp = NULL; int error = 0; struct netmap_adapter *ret = NULL; *na = NULL; /* default return value */ NMG_LOCK_ASSERT(); /* we cascade through all possibile types of netmap adapter. * All netmap_get_*_na() functions return an error and an na, * with the following combinations: * * error na * 0 NULL type doesn't match * !0 NULL type matches, but na creation/lookup failed * 0 !NULL type matches and na created/found * !0 !NULL impossible */ /* try to see if this is a monitor port */ error = netmap_get_monitor_na(nmr, na, create); if (error || *na != NULL) return error; /* try to see if this is a pipe port */ error = netmap_get_pipe_na(nmr, na, create); if (error || *na != NULL) return error; /* try to see if this is a bridge port */ error = netmap_get_bdg_na(nmr, na, create); if (error) return error; if (*na != NULL) /* valid match in netmap_get_bdg_na() */ goto pipes; /* * This must be a hardware na, lookup the name in the system. * Note that by hardware we actually mean "it shows up in ifconfig". * This may still be a tap, a veth/epair, or even a * persistent VALE port. */ ifp = ifunit_ref(nmr->nr_name); if (ifp == NULL) { return ENXIO; } error = netmap_get_hw_na(ifp, &ret); if (error) goto out; *na = ret; netmap_adapter_get(ret); pipes: /* * If we are opening a pipe whose parent was not in netmap mode, * we have to allocate the pipe array now. * XXX get rid of this clumsiness (2014-03-15) */ error = netmap_pipe_alloc(*na, nmr); out: if (error && ret != NULL) netmap_adapter_put(ret); if (ifp) if_rele(ifp); /* allow live unloading of drivers modules */ return error; } /* * validate parameters on entry for *_txsync() * Returns ring->cur if ok, or something >= kring->nkr_num_slots * in case of error. * * rhead, rcur and rtail=hwtail are stored from previous round. * hwcur is the next packet to send to the ring. * * We want * hwcur <= *rhead <= head <= cur <= tail = *rtail <= hwtail * * hwcur, rhead, rtail and hwtail are reliable */ u_int nm_txsync_prologue(struct netmap_kring *kring) { struct netmap_ring *ring = kring->ring; u_int head = ring->head; /* read only once */ u_int cur = ring->cur; /* read only once */ u_int n = kring->nkr_num_slots; ND(5, "%s kcur %d ktail %d head %d cur %d tail %d", kring->name, kring->nr_hwcur, kring->nr_hwtail, ring->head, ring->cur, ring->tail); #if 1 /* kernel sanity checks; but we can trust the kring. */ if (kring->nr_hwcur >= n || kring->rhead >= n || kring->rtail >= n || kring->nr_hwtail >= n) goto error; #endif /* kernel sanity checks */ /* * user sanity checks. We only use 'cur', * A, B, ... are possible positions for cur: * * 0 A cur B tail C n-1 * 0 D tail E cur F n-1 * * B, F, D are valid. A, C, E are wrong */ if (kring->rtail >= kring->rhead) { /* want rhead <= head <= rtail */ if (head < kring->rhead || head > kring->rtail) goto error; /* and also head <= cur <= rtail */ if (cur < head || cur > kring->rtail) goto error; } else { /* here rtail < rhead */ /* we need head outside rtail .. rhead */ if (head > kring->rtail && head < kring->rhead) goto error; /* two cases now: head <= rtail or head >= rhead */ if (head <= kring->rtail) { /* want head <= cur <= rtail */ if (cur < head || cur > kring->rtail) goto error; } else { /* head >= rhead */ /* cur must be outside rtail..head */ if (cur > kring->rtail && cur < head) goto error; } } if (ring->tail != kring->rtail) { RD(5, "tail overwritten was %d need %d", ring->tail, kring->rtail); ring->tail = kring->rtail; } kring->rhead = head; kring->rcur = cur; return head; error: RD(5, "%s kring error: hwcur %d rcur %d hwtail %d cur %d tail %d", kring->name, kring->nr_hwcur, kring->rcur, kring->nr_hwtail, cur, ring->tail); return n; } /* * validate parameters on entry for *_rxsync() * Returns ring->head if ok, kring->nkr_num_slots on error. * * For a valid configuration, * hwcur <= head <= cur <= tail <= hwtail * * We only consider head and cur. * hwcur and hwtail are reliable. * */ u_int nm_rxsync_prologue(struct netmap_kring *kring) { struct netmap_ring *ring = kring->ring; uint32_t const n = kring->nkr_num_slots; uint32_t head, cur; ND("%s kc %d kt %d h %d c %d t %d", kring->name, kring->nr_hwcur, kring->nr_hwtail, ring->head, ring->cur, ring->tail); /* * Before storing the new values, we should check they do not * move backwards. However: * - head is not an issue because the previous value is hwcur; * - cur could in principle go back, however it does not matter * because we are processing a brand new rxsync() */ cur = kring->rcur = ring->cur; /* read only once */ head = kring->rhead = ring->head; /* read only once */ #if 1 /* kernel sanity checks */ if (kring->nr_hwcur >= n || kring->nr_hwtail >= n) goto error; #endif /* kernel sanity checks */ /* user sanity checks */ if (kring->nr_hwtail >= kring->nr_hwcur) { /* want hwcur <= rhead <= hwtail */ if (head < kring->nr_hwcur || head > kring->nr_hwtail) goto error; /* and also rhead <= rcur <= hwtail */ if (cur < head || cur > kring->nr_hwtail) goto error; } else { /* we need rhead outside hwtail..hwcur */ if (head < kring->nr_hwcur && head > kring->nr_hwtail) goto error; /* two cases now: head <= hwtail or head >= hwcur */ if (head <= kring->nr_hwtail) { /* want head <= cur <= hwtail */ if (cur < head || cur > kring->nr_hwtail) goto error; } else { /* cur must be outside hwtail..head */ if (cur < head && cur > kring->nr_hwtail) goto error; } } if (ring->tail != kring->rtail) { RD(5, "%s tail overwritten was %d need %d", kring->name, ring->tail, kring->rtail); ring->tail = kring->rtail; } return head; error: RD(5, "kring error: hwcur %d rcur %d hwtail %d head %d cur %d tail %d", kring->nr_hwcur, kring->rcur, kring->nr_hwtail, kring->rhead, kring->rcur, ring->tail); return n; } /* * Error routine called when txsync/rxsync detects an error. * Can't do much more than resetting head =cur = hwcur, tail = hwtail * Return 1 on reinit. * * This routine is only called by the upper half of the kernel. * It only reads hwcur (which is changed only by the upper half, too) * and hwtail (which may be changed by the lower half, but only on * a tx ring and only to increase it, so any error will be recovered * on the next call). For the above, we don't strictly need to call * it under lock. */ int netmap_ring_reinit(struct netmap_kring *kring) { struct netmap_ring *ring = kring->ring; u_int i, lim = kring->nkr_num_slots - 1; int errors = 0; // XXX KASSERT nm_kr_tryget RD(10, "called for %s", kring->name); // XXX probably wrong to trust userspace kring->rhead = ring->head; kring->rcur = ring->cur; kring->rtail = ring->tail; if (ring->cur > lim) errors++; if (ring->head > lim) errors++; if (ring->tail > lim) errors++; for (i = 0; i <= lim; i++) { u_int idx = ring->slot[i].buf_idx; u_int len = ring->slot[i].len; if (idx < 2 || idx >= netmap_total_buffers) { RD(5, "bad index at slot %d idx %d len %d ", i, idx, len); ring->slot[i].buf_idx = 0; ring->slot[i].len = 0; } else if (len > NETMAP_BUF_SIZE(kring->na)) { ring->slot[i].len = 0; RD(5, "bad len at slot %d idx %d len %d", i, idx, len); } } if (errors) { RD(10, "total %d errors", errors); RD(10, "%s reinit, cur %d -> %d tail %d -> %d", kring->name, ring->cur, kring->nr_hwcur, ring->tail, kring->nr_hwtail); ring->head = kring->rhead = kring->nr_hwcur; ring->cur = kring->rcur = kring->nr_hwcur; ring->tail = kring->rtail = kring->nr_hwtail; } return (errors ? 1 : 0); } /* interpret the ringid and flags fields of an nmreq, by translating them * into a pair of intervals of ring indices: * * [priv->np_txqfirst, priv->np_txqlast) and * [priv->np_rxqfirst, priv->np_rxqlast) * */ int netmap_interp_ringid(struct netmap_priv_d *priv, uint16_t ringid, uint32_t flags) { struct netmap_adapter *na = priv->np_na; u_int j, i = ringid & NETMAP_RING_MASK; u_int reg = flags & NR_REG_MASK; if (reg == NR_REG_DEFAULT) { /* convert from old ringid to flags */ if (ringid & NETMAP_SW_RING) { reg = NR_REG_SW; } else if (ringid & NETMAP_HW_RING) { reg = NR_REG_ONE_NIC; } else { reg = NR_REG_ALL_NIC; } D("deprecated API, old ringid 0x%x -> ringid %x reg %d", ringid, i, reg); } switch (reg) { case NR_REG_ALL_NIC: case NR_REG_PIPE_MASTER: case NR_REG_PIPE_SLAVE: priv->np_txqfirst = 0; priv->np_txqlast = na->num_tx_rings; priv->np_rxqfirst = 0; priv->np_rxqlast = na->num_rx_rings; ND("%s %d %d", "ALL/PIPE", priv->np_rxqfirst, priv->np_rxqlast); break; case NR_REG_SW: case NR_REG_NIC_SW: if (!(na->na_flags & NAF_HOST_RINGS)) { D("host rings not supported"); return EINVAL; } priv->np_txqfirst = (reg == NR_REG_SW ? na->num_tx_rings : 0); priv->np_txqlast = na->num_tx_rings + 1; priv->np_rxqfirst = (reg == NR_REG_SW ? na->num_rx_rings : 0); priv->np_rxqlast = na->num_rx_rings + 1; ND("%s %d %d", reg == NR_REG_SW ? "SW" : "NIC+SW", priv->np_rxqfirst, priv->np_rxqlast); break; case NR_REG_ONE_NIC: if (i >= na->num_tx_rings && i >= na->num_rx_rings) { D("invalid ring id %d", i); return EINVAL; } /* if not enough rings, use the first one */ j = i; if (j >= na->num_tx_rings) j = 0; priv->np_txqfirst = j; priv->np_txqlast = j + 1; j = i; if (j >= na->num_rx_rings) j = 0; priv->np_rxqfirst = j; priv->np_rxqlast = j + 1; break; default: D("invalid regif type %d", reg); return EINVAL; } priv->np_flags = (flags & ~NR_REG_MASK) | reg; if (netmap_verbose) { D("%s: tx [%d,%d) rx [%d,%d) id %d", na->name, priv->np_txqfirst, priv->np_txqlast, priv->np_rxqfirst, priv->np_rxqlast, i); } return 0; } /* * Set the ring ID. For devices with a single queue, a request * for all rings is the same as a single ring. */ static int netmap_set_ringid(struct netmap_priv_d *priv, uint16_t ringid, uint32_t flags) { struct netmap_adapter *na = priv->np_na; int error; error = netmap_interp_ringid(priv, ringid, flags); if (error) { return error; } priv->np_txpoll = (ringid & NETMAP_NO_TX_POLL) ? 0 : 1; /* optimization: count the users registered for more than * one ring, which are the ones sleeping on the global queue. * The default netmap_notify() callback will then * avoid signaling the global queue if nobody is using it */ if (nm_tx_si_user(priv)) na->tx_si_users++; if (nm_rx_si_user(priv)) na->rx_si_users++; return 0; } /* * possibly move the interface to netmap-mode. * If success it returns a pointer to netmap_if, otherwise NULL. * This must be called with NMG_LOCK held. * * The following na callbacks are called in the process: * * na->nm_config() [by netmap_update_config] * (get current number and size of rings) * * We have a generic one for linux (netmap_linux_config). * The bwrap has to override this, since it has to forward * the request to the wrapped adapter (netmap_bwrap_config). * * XXX netmap_if_new calls this again (2014-03-15) * * na->nm_krings_create() [by netmap_if_new] * (create and init the krings array) * * One of the following: * * * netmap_hw_krings_create, (hw ports) * creates the standard layout for the krings * and adds the mbq (used for the host rings). * * * netmap_vp_krings_create (VALE ports) * add leases and scratchpads * * * netmap_pipe_krings_create (pipes) * create the krings and rings of both ends and * cross-link them * * * netmap_monitor_krings_create (monitors) * avoid allocating the mbq * * * netmap_bwrap_krings_create (bwraps) * create both the brap krings array, * the krings array of the wrapped adapter, and * (if needed) the fake array for the host adapter * * na->nm_register(, 1) * (put the adapter in netmap mode) * * This may be one of the following: * (XXX these should be either all *_register or all *_reg 2014-03-15) * * * netmap_hw_register (hw ports) * checks that the ifp is still there, then calls * the hardware specific callback; * * * netmap_vp_reg (VALE ports) * If the port is connected to a bridge, * set the NAF_NETMAP_ON flag under the * bridge write lock. * * * netmap_pipe_reg (pipes) * inform the other pipe end that it is no * longer responsibile for the lifetime of this * pipe end * * * netmap_monitor_reg (monitors) * intercept the sync callbacks of the monitored * rings * * * netmap_bwrap_register (bwraps) * cross-link the bwrap and hwna rings, * forward the request to the hwna, override * the hwna notify callback (to get the frames * coming from outside go through the bridge). * * XXX maybe netmap_if_new() should be merged with this (2014-03-15). * */ struct netmap_if * netmap_do_regif(struct netmap_priv_d *priv, struct netmap_adapter *na, uint16_t ringid, uint32_t flags, int *err) { struct netmap_if *nifp = NULL; int error, need_mem = 0; NMG_LOCK_ASSERT(); /* ring configuration may have changed, fetch from the card */ netmap_update_config(na); priv->np_na = na; /* store the reference */ error = netmap_set_ringid(priv, ringid, flags); if (error) goto out; /* ensure allocators are ready */ need_mem = !netmap_have_memory_locked(priv); if (need_mem) { error = netmap_get_memory_locked(priv); ND("get_memory returned %d", error); if (error) goto out; } /* Allocate a netmap_if and, if necessary, all the netmap_ring's */ nifp = netmap_if_new(na); if (nifp == NULL) { /* allocation failed */ error = ENOMEM; goto out; } na->active_fds++; if (!nm_netmap_on(na)) { /* Netmap not active, set the card in netmap mode * and make it use the shared buffers. */ /* cache the allocator info in the na */ na->na_lut = netmap_mem_get_lut(na->nm_mem); ND("%p->na_lut == %p", na, na->na_lut); na->na_lut_objtotal = netmap_mem_get_buftotal(na->nm_mem); na->na_lut_objsize = netmap_mem_get_bufsize(na->nm_mem); error = na->nm_register(na, 1); /* mode on */ if (error) { netmap_do_unregif(priv, nifp); nifp = NULL; } } out: *err = error; if (error) { /* we should drop the allocator, but only * if we were the ones who grabbed it */ if (need_mem) netmap_drop_memory_locked(priv); priv->np_na = NULL; } if (nifp != NULL) { /* * advertise that the interface is ready bt setting ni_nifp. * The barrier is needed because readers (poll and *SYNC) * check for priv->np_nifp != NULL without locking */ wmb(); /* make sure previous writes are visible to all CPUs */ priv->np_nifp = nifp; } return nifp; } /* * ioctl(2) support for the "netmap" device. * * Following a list of accepted commands: * - NIOCGINFO * - SIOCGIFADDR just for convenience * - NIOCREGIF * - NIOCTXSYNC * - NIOCRXSYNC * * Return 0 on success, errno otherwise. */ int netmap_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) { struct netmap_priv_d *priv = NULL; struct nmreq *nmr = (struct nmreq *) data; struct netmap_adapter *na = NULL; int error; u_int i, qfirst, qlast; struct netmap_if *nifp; struct netmap_kring *krings; (void)dev; /* UNUSED */ (void)fflag; /* UNUSED */ if (cmd == NIOCGINFO || cmd == NIOCREGIF) { /* truncate name */ nmr->nr_name[sizeof(nmr->nr_name) - 1] = '\0'; if (nmr->nr_version != NETMAP_API) { D("API mismatch for %s got %d need %d", nmr->nr_name, nmr->nr_version, NETMAP_API); nmr->nr_version = NETMAP_API; } if (nmr->nr_version < NETMAP_MIN_API || nmr->nr_version > NETMAP_MAX_API) { return EINVAL; } } CURVNET_SET(TD_TO_VNET(td)); error = devfs_get_cdevpriv((void **)&priv); if (error) { CURVNET_RESTORE(); /* XXX ENOENT should be impossible, since the priv * is now created in the open */ return (error == ENOENT ? ENXIO : error); } switch (cmd) { case NIOCGINFO: /* return capabilities etc */ if (nmr->nr_cmd == NETMAP_BDG_LIST) { error = netmap_bdg_ctl(nmr, NULL); break; } NMG_LOCK(); do { /* memsize is always valid */ struct netmap_mem_d *nmd = &nm_mem; u_int memflags; if (nmr->nr_name[0] != '\0') { /* get a refcount */ error = netmap_get_na(nmr, &na, 1 /* create */); if (error) break; nmd = na->nm_mem; /* get memory allocator */ } error = netmap_mem_get_info(nmd, &nmr->nr_memsize, &memflags, &nmr->nr_arg2); if (error) break; if (na == NULL) /* only memory info */ break; nmr->nr_offset = 0; nmr->nr_rx_slots = nmr->nr_tx_slots = 0; netmap_update_config(na); nmr->nr_rx_rings = na->num_rx_rings; nmr->nr_tx_rings = na->num_tx_rings; nmr->nr_rx_slots = na->num_rx_desc; nmr->nr_tx_slots = na->num_tx_desc; netmap_adapter_put(na); } while (0); NMG_UNLOCK(); break; case NIOCREGIF: /* possibly attach/detach NIC and VALE switch */ i = nmr->nr_cmd; if (i == NETMAP_BDG_ATTACH || i == NETMAP_BDG_DETACH || i == NETMAP_BDG_VNET_HDR || i == NETMAP_BDG_NEWIF || i == NETMAP_BDG_DELIF) { error = netmap_bdg_ctl(nmr, NULL); break; } else if (i != 0) { D("nr_cmd must be 0 not %d", i); error = EINVAL; break; } /* protect access to priv from concurrent NIOCREGIF */ NMG_LOCK(); do { u_int memflags; if (priv->np_na != NULL) { /* thread already registered */ error = EBUSY; break; } /* find the interface and a reference */ error = netmap_get_na(nmr, &na, 1 /* create */); /* keep reference */ if (error) break; if (NETMAP_OWNED_BY_KERN(na)) { netmap_adapter_put(na); error = EBUSY; break; } nifp = netmap_do_regif(priv, na, nmr->nr_ringid, nmr->nr_flags, &error); if (!nifp) { /* reg. failed, release priv and ref */ netmap_adapter_put(na); priv->np_nifp = NULL; break; } priv->np_td = td; // XXX kqueue, debugging only /* return the offset of the netmap_if object */ nmr->nr_rx_rings = na->num_rx_rings; nmr->nr_tx_rings = na->num_tx_rings; nmr->nr_rx_slots = na->num_rx_desc; nmr->nr_tx_slots = na->num_tx_desc; error = netmap_mem_get_info(na->nm_mem, &nmr->nr_memsize, &memflags, &nmr->nr_arg2); if (error) { netmap_adapter_put(na); break; } if (memflags & NETMAP_MEM_PRIVATE) { *(uint32_t *)(uintptr_t)&nifp->ni_flags |= NI_PRIV_MEM; } priv->np_txsi = (priv->np_txqlast - priv->np_txqfirst > 1) ? &na->tx_si : &na->tx_rings[priv->np_txqfirst].si; priv->np_rxsi = (priv->np_rxqlast - priv->np_rxqfirst > 1) ? &na->rx_si : &na->rx_rings[priv->np_rxqfirst].si; if (nmr->nr_arg3) { D("requested %d extra buffers", nmr->nr_arg3); nmr->nr_arg3 = netmap_extra_alloc(na, &nifp->ni_bufs_head, nmr->nr_arg3); D("got %d extra buffers", nmr->nr_arg3); } nmr->nr_offset = netmap_mem_if_offset(na->nm_mem, nifp); } while (0); NMG_UNLOCK(); break; case NIOCTXSYNC: case NIOCRXSYNC: nifp = priv->np_nifp; if (nifp == NULL) { error = ENXIO; break; } - rmb(); /* make sure following reads are not from cache */ + mb(); /* make sure following reads are not from cache */ na = priv->np_na; /* we have a reference */ if (na == NULL) { D("Internal error: nifp != NULL && na == NULL"); error = ENXIO; break; } if (!nm_netmap_on(na)) { error = ENXIO; break; } if (cmd == NIOCTXSYNC) { krings = na->tx_rings; qfirst = priv->np_txqfirst; qlast = priv->np_txqlast; } else { krings = na->rx_rings; qfirst = priv->np_rxqfirst; qlast = priv->np_rxqlast; } for (i = qfirst; i < qlast; i++) { struct netmap_kring *kring = krings + i; if (nm_kr_tryget(kring)) { error = EBUSY; goto out; } if (cmd == NIOCTXSYNC) { if (netmap_verbose & NM_VERB_TXSYNC) D("pre txsync ring %d cur %d hwcur %d", i, kring->ring->cur, kring->nr_hwcur); if (nm_txsync_prologue(kring) >= kring->nkr_num_slots) { netmap_ring_reinit(kring); } else { kring->nm_sync(kring, NAF_FORCE_RECLAIM); } if (netmap_verbose & NM_VERB_TXSYNC) D("post txsync ring %d cur %d hwcur %d", i, kring->ring->cur, kring->nr_hwcur); } else { kring->nm_sync(kring, NAF_FORCE_READ); microtime(&na->rx_rings[i].ring->ts); } nm_kr_put(kring); } break; case NIOCCONFIG: error = netmap_bdg_config(nmr); break; #ifdef __FreeBSD__ case FIONBIO: case FIOASYNC: ND("FIONBIO/FIOASYNC are no-ops"); break; case BIOCIMMEDIATE: case BIOCGHDRCMPLT: case BIOCSHDRCMPLT: case BIOCSSEESENT: D("ignore BIOCIMMEDIATE/BIOCSHDRCMPLT/BIOCSHDRCMPLT/BIOCSSEESENT"); break; default: /* allow device-specific ioctls */ { struct ifnet *ifp = ifunit_ref(nmr->nr_name); if (ifp == NULL) { error = ENXIO; } else { struct socket so; bzero(&so, sizeof(so)); so.so_vnet = ifp->if_vnet; // so->so_proto not null. error = ifioctl(&so, cmd, data, td); if_rele(ifp); } break; } #else /* linux */ default: error = EOPNOTSUPP; #endif /* linux */ } out: CURVNET_RESTORE(); return (error); } /* * select(2) and poll(2) handlers for the "netmap" device. * * Can be called for one or more queues. * Return true the event mask corresponding to ready events. * If there are no ready events, do a selrecord on either individual * selinfo or on the global one. * Device-dependent parts (locking and sync of tx/rx rings) * are done through callbacks. * * On linux, arguments are really pwait, the poll table, and 'td' is struct file * * The first one is remapped to pwait as selrecord() uses the name as an * hidden argument. */ int netmap_poll(struct cdev *dev, int events, struct thread *td) { struct netmap_priv_d *priv = NULL; struct netmap_adapter *na; struct netmap_kring *kring; u_int i, check_all_tx, check_all_rx, want_tx, want_rx, revents = 0; struct mbq q; /* packets from hw queues to host stack */ void *pwait = dev; /* linux compatibility */ int is_kevent = 0; /* * In order to avoid nested locks, we need to "double check" * txsync and rxsync if we decide to do a selrecord(). * retry_tx (and retry_rx, later) prevent looping forever. */ int retry_tx = 1, retry_rx = 1; (void)pwait; mbq_init(&q); /* * XXX kevent has curthread->tp_fop == NULL, * so devfs_get_cdevpriv() fails. We circumvent this by passing * priv as the first argument, which is also useful to avoid * the selrecord() which are not necessary in that case. */ if (devfs_get_cdevpriv((void **)&priv) != 0) { is_kevent = 1; if (netmap_verbose) D("called from kevent"); priv = (struct netmap_priv_d *)dev; } if (priv == NULL) return POLLERR; if (priv->np_nifp == NULL) { D("No if registered"); return POLLERR; } rmb(); /* make sure following reads are not from cache */ na = priv->np_na; if (!nm_netmap_on(na)) return POLLERR; if (netmap_verbose & 0x8000) D("device %s events 0x%x", na->name, events); want_tx = events & (POLLOUT | POLLWRNORM); want_rx = events & (POLLIN | POLLRDNORM); /* * check_all_{tx|rx} are set if the card has more than one queue AND * the file descriptor is bound to all of them. If so, we sleep on * the "global" selinfo, otherwise we sleep on individual selinfo * (FreeBSD only allows two selinfo's per file descriptor). * The interrupt routine in the driver wake one or the other * (or both) depending on which clients are active. * * rxsync() is only called if we run out of buffers on a POLLIN. * txsync() is called if we run out of buffers on POLLOUT, or * there are pending packets to send. The latter can be disabled * passing NETMAP_NO_TX_POLL in the NIOCREG call. */ check_all_tx = nm_tx_si_user(priv); check_all_rx = nm_rx_si_user(priv); /* * We start with a lock free round which is cheap if we have * slots available. If this fails, then lock and call the sync * routines. */ for (i = priv->np_rxqfirst; want_rx && i < priv->np_rxqlast; i++) { kring = &na->rx_rings[i]; /* XXX compare ring->cur and kring->tail */ if (!nm_ring_empty(kring->ring)) { revents |= want_rx; want_rx = 0; /* also breaks the loop */ } } for (i = priv->np_txqfirst; want_tx && i < priv->np_txqlast; i++) { kring = &na->tx_rings[i]; /* XXX compare ring->cur and kring->tail */ if (!nm_ring_empty(kring->ring)) { revents |= want_tx; want_tx = 0; /* also breaks the loop */ } } /* * If we want to push packets out (priv->np_txpoll) or * want_tx is still set, we must issue txsync calls * (on all rings, to avoid that the tx rings stall). * XXX should also check cur != hwcur on the tx rings. * Fortunately, normal tx mode has np_txpoll set. */ if (priv->np_txpoll || want_tx) { /* * The first round checks if anyone is ready, if not * do a selrecord and another round to handle races. * want_tx goes to 0 if any space is found, and is * used to skip rings with no pending transmissions. */ flush_tx: for (i = priv->np_txqfirst; i < priv->np_txqlast; i++) { int found = 0; kring = &na->tx_rings[i]; if (!want_tx && kring->ring->cur == kring->nr_hwcur) continue; /* only one thread does txsync */ if (nm_kr_tryget(kring)) { /* either busy or stopped * XXX if the ring is stopped, sleeping would * be better. In current code, however, we only * stop the rings for brief intervals (2014-03-14) */ if (netmap_verbose) RD(2, "%p lost race on txring %d, ok", priv, i); continue; } if (nm_txsync_prologue(kring) >= kring->nkr_num_slots) { netmap_ring_reinit(kring); revents |= POLLERR; } else { if (kring->nm_sync(kring, 0)) revents |= POLLERR; } /* * If we found new slots, notify potential * listeners on the same ring. * Since we just did a txsync, look at the copies * of cur,tail in the kring. */ found = kring->rcur != kring->rtail; nm_kr_put(kring); if (found) { /* notify other listeners */ revents |= want_tx; want_tx = 0; na->nm_notify(na, i, NR_TX, 0); } } if (want_tx && retry_tx && !is_kevent) { OS_selrecord(td, check_all_tx ? &na->tx_si : &na->tx_rings[priv->np_txqfirst].si); retry_tx = 0; goto flush_tx; } } /* * If want_rx is still set scan receive rings. * Do it on all rings because otherwise we starve. */ if (want_rx) { int send_down = 0; /* transparent mode */ /* two rounds here for race avoidance */ do_retry_rx: for (i = priv->np_rxqfirst; i < priv->np_rxqlast; i++) { int found = 0; kring = &na->rx_rings[i]; if (nm_kr_tryget(kring)) { if (netmap_verbose) RD(2, "%p lost race on rxring %d, ok", priv, i); continue; } /* * transparent mode support: collect packets * from the rxring(s). * XXX NR_FORWARD should only be read on * physical or NIC ports */ if (netmap_fwd ||kring->ring->flags & NR_FORWARD) { ND(10, "forwarding some buffers up %d to %d", kring->nr_hwcur, kring->ring->cur); netmap_grab_packets(kring, &q, netmap_fwd); } if (kring->nm_sync(kring, 0)) revents |= POLLERR; if (netmap_no_timestamp == 0 || kring->ring->flags & NR_TIMESTAMP) { microtime(&kring->ring->ts); } /* after an rxsync we can use kring->rcur, rtail */ found = kring->rcur != kring->rtail; nm_kr_put(kring); if (found) { revents |= want_rx; retry_rx = 0; na->nm_notify(na, i, NR_RX, 0); } } /* transparent mode XXX only during first pass ? */ if (na->na_flags & NAF_HOST_RINGS) { kring = &na->rx_rings[na->num_rx_rings]; if (check_all_rx && (netmap_fwd || kring->ring->flags & NR_FORWARD)) { /* XXX fix to use kring fields */ if (nm_ring_empty(kring->ring)) send_down = netmap_rxsync_from_host(na, td, dev); if (!nm_ring_empty(kring->ring)) revents |= want_rx; } } if (retry_rx && !is_kevent) OS_selrecord(td, check_all_rx ? &na->rx_si : &na->rx_rings[priv->np_rxqfirst].si); if (send_down > 0 || retry_rx) { retry_rx = 0; if (send_down) goto flush_tx; /* and retry_rx */ else goto do_retry_rx; } } /* * Transparent mode: marked bufs on rx rings between * kring->nr_hwcur and ring->head * are passed to the other endpoint. * * In this mode we also scan the sw rxring, which in * turn passes packets up. * * XXX Transparent mode at the moment requires to bind all * rings to a single file descriptor. */ if (q.head && na->ifp != NULL) netmap_send_up(na->ifp, &q); return (revents); } /*-------------------- driver support routines -------------------*/ static int netmap_hw_krings_create(struct netmap_adapter *); /* default notify callback */ static int netmap_notify(struct netmap_adapter *na, u_int n_ring, enum txrx tx, int flags) { struct netmap_kring *kring; if (tx == NR_TX) { kring = na->tx_rings + n_ring; OS_selwakeup(&kring->si, PI_NET); /* optimization: avoid a wake up on the global * queue if nobody has registered for more * than one ring */ if (na->tx_si_users > 0) OS_selwakeup(&na->tx_si, PI_NET); } else { kring = na->rx_rings + n_ring; OS_selwakeup(&kring->si, PI_NET); /* optimization: same as above */ if (na->rx_si_users > 0) OS_selwakeup(&na->rx_si, PI_NET); } return 0; } /* called by all routines that create netmap_adapters. * Attach na to the ifp (if any) and provide defaults * for optional callbacks. Defaults assume that we * are creating an hardware netmap_adapter. */ int netmap_attach_common(struct netmap_adapter *na) { struct ifnet *ifp = na->ifp; if (na->num_tx_rings == 0 || na->num_rx_rings == 0) { D("%s: invalid rings tx %d rx %d", na->name, na->num_tx_rings, na->num_rx_rings); return EINVAL; } /* ifp is NULL for virtual adapters (bwrap, non-persistent VALE ports, * pipes, monitors). For bwrap we actually have a non-null ifp for * use by the external modules, but that is set after this * function has been called. * XXX this is ugly, maybe split this function in two (2014-03-14) */ if (ifp != NULL) { WNA(ifp) = na; /* the following is only needed for na that use the host port. * XXX do we have something similar for linux ? */ #ifdef __FreeBSD__ na->if_input = ifp->if_input; /* for netmap_send_up */ #endif /* __FreeBSD__ */ NETMAP_SET_CAPABLE(ifp); } if (na->nm_krings_create == NULL) { /* we assume that we have been called by a driver, * since other port types all provide their own * nm_krings_create */ na->nm_krings_create = netmap_hw_krings_create; na->nm_krings_delete = netmap_hw_krings_delete; } if (na->nm_notify == NULL) na->nm_notify = netmap_notify; na->active_fds = 0; if (na->nm_mem == NULL) /* use the global allocator */ na->nm_mem = &nm_mem; if (na->nm_bdg_attach == NULL) /* no special nm_bdg_attach callback. On VALE * attach, we need to interpose a bwrap */ na->nm_bdg_attach = netmap_bwrap_attach; return 0; } /* standard cleanup, called by all destructors */ void netmap_detach_common(struct netmap_adapter *na) { if (na->ifp != NULL) WNA(na->ifp) = NULL; /* XXX do we need this? */ if (na->tx_rings) { /* XXX should not happen */ D("freeing leftover tx_rings"); na->nm_krings_delete(na); } netmap_pipe_dealloc(na); if (na->na_flags & NAF_MEM_OWNER) netmap_mem_private_delete(na->nm_mem); bzero(na, sizeof(*na)); free(na, M_DEVBUF); } /* Wrapper for the register callback provided hardware drivers. * na->ifp == NULL means the the driver module has been * unloaded, so we cannot call into it. * Note that module unloading, in our patched linux drivers, * happens under NMG_LOCK and after having stopped all the * nic rings (see netmap_detach). This provides sufficient * protection for the other driver-provied callbacks * (i.e., nm_config and nm_*xsync), that therefore don't need * to wrapped. */ static int netmap_hw_register(struct netmap_adapter *na, int onoff) { struct netmap_hw_adapter *hwna = (struct netmap_hw_adapter*)na; if (na->ifp == NULL) return onoff ? ENXIO : 0; return hwna->nm_hw_register(na, onoff); } /* * Initialize a ``netmap_adapter`` object created by driver on attach. * We allocate a block of memory with room for a struct netmap_adapter * plus two sets of N+2 struct netmap_kring (where N is the number * of hardware rings): * krings 0..N-1 are for the hardware queues. * kring N is for the host stack queue * kring N+1 is only used for the selinfo for all queues. // XXX still true ? * Return 0 on success, ENOMEM otherwise. */ int netmap_attach(struct netmap_adapter *arg) { struct netmap_hw_adapter *hwna = NULL; // XXX when is arg == NULL ? struct ifnet *ifp = arg ? arg->ifp : NULL; if (arg == NULL || ifp == NULL) goto fail; hwna = malloc(sizeof(*hwna), M_DEVBUF, M_NOWAIT | M_ZERO); if (hwna == NULL) goto fail; hwna->up = *arg; hwna->up.na_flags |= NAF_HOST_RINGS; strncpy(hwna->up.name, ifp->if_xname, sizeof(hwna->up.name)); hwna->nm_hw_register = hwna->up.nm_register; hwna->up.nm_register = netmap_hw_register; if (netmap_attach_common(&hwna->up)) { free(hwna, M_DEVBUF); goto fail; } netmap_adapter_get(&hwna->up); #ifdef linux if (ifp->netdev_ops) { /* prepare a clone of the netdev ops */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28) hwna->nm_ndo.ndo_start_xmit = ifp->netdev_ops; #else hwna->nm_ndo = *ifp->netdev_ops; #endif } hwna->nm_ndo.ndo_start_xmit = linux_netmap_start_xmit; if (ifp->ethtool_ops) { hwna->nm_eto = *ifp->ethtool_ops; } hwna->nm_eto.set_ringparam = linux_netmap_set_ringparam; #ifdef ETHTOOL_SCHANNELS hwna->nm_eto.set_channels = linux_netmap_set_channels; #endif if (arg->nm_config == NULL) { hwna->up.nm_config = netmap_linux_config; } #endif /* linux */ D("success for %s tx %d/%d rx %d/%d queues/slots", hwna->up.name, hwna->up.num_tx_rings, hwna->up.num_tx_desc, hwna->up.num_rx_rings, hwna->up.num_rx_desc ); return 0; fail: D("fail, arg %p ifp %p na %p", arg, ifp, hwna); if (ifp) netmap_detach(ifp); return (hwna ? EINVAL : ENOMEM); } void NM_DBG(netmap_adapter_get)(struct netmap_adapter *na) { if (!na) { return; } refcount_acquire(&na->na_refcount); } /* returns 1 iff the netmap_adapter is destroyed */ int NM_DBG(netmap_adapter_put)(struct netmap_adapter *na) { if (!na) return 1; if (!refcount_release(&na->na_refcount)) return 0; if (na->nm_dtor) na->nm_dtor(na); netmap_detach_common(na); return 1; } /* nm_krings_create callback for all hardware native adapters */ int netmap_hw_krings_create(struct netmap_adapter *na) { int ret = netmap_krings_create(na, 0); if (ret == 0) { /* initialize the mbq for the sw rx ring */ mbq_safe_init(&na->rx_rings[na->num_rx_rings].rx_queue); ND("initialized sw rx queue %d", na->num_rx_rings); } return ret; } /* * Called on module unload by the netmap-enabled drivers */ void netmap_detach(struct ifnet *ifp) { struct netmap_adapter *na = NA(ifp); if (!na) return; NMG_LOCK(); netmap_disable_all_rings(ifp); if (!netmap_adapter_put(na)) { /* someone is still using the adapter, * tell them that the interface is gone */ na->ifp = NULL; // XXX also clear NAF_NATIVE_ON ? na->na_flags &= ~NAF_NETMAP_ON; /* give them a chance to notice */ netmap_enable_all_rings(ifp); } NMG_UNLOCK(); } /* * Intercept packets from the network stack and pass them * to netmap as incoming packets on the 'software' ring. * * We only store packets in a bounded mbq and then copy them * in the relevant rxsync routine. * * We rely on the OS to make sure that the ifp and na do not go * away (typically the caller checks for IFF_DRV_RUNNING or the like). * In nm_register() or whenever there is a reinitialization, * we make sure to make the mode change visible here. */ int netmap_transmit(struct ifnet *ifp, struct mbuf *m) { struct netmap_adapter *na = NA(ifp); struct netmap_kring *kring; u_int len = MBUF_LEN(m); u_int error = ENOBUFS; struct mbq *q; int space; // XXX [Linux] we do not need this lock // if we follow the down/configure/up protocol -gl // mtx_lock(&na->core_lock); if (!nm_netmap_on(na)) { D("%s not in netmap mode anymore", na->name); error = ENXIO; goto done; } kring = &na->rx_rings[na->num_rx_rings]; q = &kring->rx_queue; // XXX reconsider long packets if we handle fragments if (len > NETMAP_BUF_SIZE(na)) { /* too long for us */ D("%s from_host, drop packet size %d > %d", na->name, len, NETMAP_BUF_SIZE(na)); goto done; } /* protect against rxsync_from_host(), netmap_sw_to_nic() * and maybe other instances of netmap_transmit (the latter * not possible on Linux). * Also avoid overflowing the queue. */ mbq_lock(q); space = kring->nr_hwtail - kring->nr_hwcur; if (space < 0) space += kring->nkr_num_slots; if (space + mbq_len(q) >= kring->nkr_num_slots - 1) { // XXX RD(10, "%s full hwcur %d hwtail %d qlen %d len %d m %p", na->name, kring->nr_hwcur, kring->nr_hwtail, mbq_len(q), len, m); } else { mbq_enqueue(q, m); ND(10, "%s %d bufs in queue len %d m %p", na->name, mbq_len(q), len, m); /* notify outside the lock */ m = NULL; error = 0; } mbq_unlock(q); done: if (m) m_freem(m); /* unconditionally wake up listeners */ na->nm_notify(na, na->num_rx_rings, NR_RX, 0); /* this is normally netmap_notify(), but for nics * connected to a bridge it is netmap_bwrap_intr_notify(), * that possibly forwards the frames through the switch */ return (error); } /* * netmap_reset() is called by the driver routines when reinitializing * a ring. The driver is in charge of locking to protect the kring. * If native netmap mode is not set just return NULL. */ struct netmap_slot * netmap_reset(struct netmap_adapter *na, enum txrx tx, u_int n, u_int new_cur) { struct netmap_kring *kring; int new_hwofs, lim; if (!nm_native_on(na)) { ND("interface not in native netmap mode"); return NULL; /* nothing to reinitialize */ } /* XXX note- in the new scheme, we are not guaranteed to be * under lock (e.g. when called on a device reset). * In this case, we should set a flag and do not trust too * much the values. In practice: TODO * - set a RESET flag somewhere in the kring * - do the processing in a conservative way * - let the *sync() fixup at the end. */ if (tx == NR_TX) { if (n >= na->num_tx_rings) return NULL; kring = na->tx_rings + n; // XXX check whether we should use hwcur or rcur new_hwofs = kring->nr_hwcur - new_cur; } else { if (n >= na->num_rx_rings) return NULL; kring = na->rx_rings + n; new_hwofs = kring->nr_hwtail - new_cur; } lim = kring->nkr_num_slots - 1; if (new_hwofs > lim) new_hwofs -= lim + 1; /* Always set the new offset value and realign the ring. */ if (netmap_verbose) D("%s %s%d hwofs %d -> %d, hwtail %d -> %d", na->name, tx == NR_TX ? "TX" : "RX", n, kring->nkr_hwofs, new_hwofs, kring->nr_hwtail, tx == NR_TX ? lim : kring->nr_hwtail); kring->nkr_hwofs = new_hwofs; if (tx == NR_TX) { kring->nr_hwtail = kring->nr_hwcur + lim; if (kring->nr_hwtail > lim) kring->nr_hwtail -= lim + 1; } #if 0 // def linux /* XXX check that the mappings are correct */ /* need ring_nr, adapter->pdev, direction */ buffer_info->dma = dma_map_single(&pdev->dev, addr, adapter->rx_buffer_len, DMA_FROM_DEVICE); if (dma_mapping_error(&adapter->pdev->dev, buffer_info->dma)) { D("error mapping rx netmap buffer %d", i); // XXX fix error handling } #endif /* linux */ /* * Wakeup on the individual and global selwait * We do the wakeup here, but the ring is not yet reconfigured. * However, we are under lock so there are no races. */ na->nm_notify(na, n, tx, 0); return kring->ring->slot; } /* * Dispatch rx/tx interrupts to the netmap rings. * * "work_done" is non-null on the RX path, NULL for the TX path. * We rely on the OS to make sure that there is only one active * instance per queue, and that there is appropriate locking. * * The 'notify' routine depends on what the ring is attached to. * - for a netmap file descriptor, do a selwakeup on the individual * waitqueue, plus one on the global one if needed * (see netmap_notify) * - for a nic connected to a switch, call the proper forwarding routine * (see netmap_bwrap_intr_notify) */ void netmap_common_irq(struct ifnet *ifp, u_int q, u_int *work_done) { struct netmap_adapter *na = NA(ifp); struct netmap_kring *kring; q &= NETMAP_RING_MASK; if (netmap_verbose) { RD(5, "received %s queue %d", work_done ? "RX" : "TX" , q); } if (work_done) { /* RX path */ if (q >= na->num_rx_rings) return; // not a physical queue kring = na->rx_rings + q; kring->nr_kflags |= NKR_PENDINTR; // XXX atomic ? na->nm_notify(na, q, NR_RX, 0); *work_done = 1; /* do not fire napi again */ } else { /* TX path */ if (q >= na->num_tx_rings) return; // not a physical queue kring = na->tx_rings + q; na->nm_notify(na, q, NR_TX, 0); } } /* * Default functions to handle rx/tx interrupts from a physical device. * "work_done" is non-null on the RX path, NULL for the TX path. * * If the card is not in netmap mode, simply return 0, * so that the caller proceeds with regular processing. * Otherwise call netmap_common_irq() and return 1. * * If the card is connected to a netmap file descriptor, * do a selwakeup on the individual queue, plus one on the global one * if needed (multiqueue card _and_ there are multiqueue listeners), * and return 1. * * Finally, if called on rx from an interface connected to a switch, * calls the proper forwarding routine, and return 1. */ int netmap_rx_irq(struct ifnet *ifp, u_int q, u_int *work_done) { struct netmap_adapter *na = NA(ifp); /* * XXX emulated netmap mode sets NAF_SKIP_INTR so * we still use the regular driver even though the previous * check fails. It is unclear whether we should use * nm_native_on() here. */ if (!nm_netmap_on(na)) return 0; if (na->na_flags & NAF_SKIP_INTR) { ND("use regular interrupt"); return 0; } netmap_common_irq(ifp, q, work_done); return 1; } /* * Module loader and unloader * * netmap_init() creates the /dev/netmap device and initializes * all global variables. Returns 0 on success, errno on failure * (but there is no chance) * * netmap_fini() destroys everything. */ static struct cdev *netmap_dev; /* /dev/netmap character device. */ extern struct cdevsw netmap_cdevsw; void netmap_fini(void) { // XXX destroy_bridges() ? if (netmap_dev) destroy_dev(netmap_dev); netmap_mem_fini(); NMG_LOCK_DESTROY(); printf("netmap: unloaded module.\n"); } int netmap_init(void) { int error; NMG_LOCK_INIT(); error = netmap_mem_init(); if (error != 0) goto fail; - /* XXX could use make_dev_credv() to get error number */ -#ifdef __FreeBSD__ - /* support for the 'eternal' flag */ + /* + * MAKEDEV_ETERNAL_KLD avoids an expensive check on syscalls + * when the module is compiled in. + * XXX could use make_dev_credv() to get error number + */ netmap_dev = make_dev_credf(MAKEDEV_ETERNAL_KLD, &netmap_cdevsw, 0, NULL, UID_ROOT, GID_WHEEL, 0600, "netmap"); -#else - netmap_dev = make_dev(&netmap_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, - "netmap"); -#endif if (!netmap_dev) goto fail; netmap_init_bridges(); #ifdef __FreeBSD__ nm_vi_init_index(); #endif printf("netmap: loaded module\n"); return (0); fail: netmap_fini(); return (EINVAL); /* may be incorrect */ } Index: projects/building-blocks/sys/dev/oce/oce_mbox.c =================================================================== --- projects/building-blocks/sys/dev/oce/oce_mbox.c (revision 278776) +++ projects/building-blocks/sys/dev/oce/oce_mbox.c (revision 278777) @@ -1,2223 +1,2222 @@ /*- * Copyright (C) 2013 Emulex * 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 Emulex Corporation 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 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 OWNER 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. * * Contact Information: * freebsd-drivers@emulex.com * * Emulex * 3333 Susan Street * Costa Mesa, CA 92626 */ /* $FreeBSD$ */ #include "oce_if.h" extern uint32_t sfp_vpd_dump_buffer[TRANSCEIVER_DATA_NUM_ELE]; /** * @brief Reset (firmware) common function * @param sc software handle to the device * @returns 0 on success, ETIMEDOUT on failure */ int oce_reset_fun(POCE_SOFTC sc) { struct oce_mbx *mbx; struct oce_bmbx *mb; struct ioctl_common_function_reset *fwcmd; int rc = 0; if (sc->flags & OCE_FLAGS_FUNCRESET_RQD) { mb = OCE_DMAPTR(&sc->bsmbx, struct oce_bmbx); mbx = &mb->mbx; bzero(mbx, sizeof(struct oce_mbx)); fwcmd = (struct ioctl_common_function_reset *)&mbx->payload; mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_COMMON, OPCODE_COMMON_FUNCTION_RESET, 10, /* MBX_TIMEOUT_SEC */ sizeof(struct ioctl_common_function_reset), OCE_MBX_VER_V0); mbx->u0.s.embedded = 1; mbx->payload_length = sizeof(struct ioctl_common_function_reset); rc = oce_mbox_dispatch(sc, 2); } return rc; } /** * @brief This funtions tells firmware we are * done with commands. * @param sc software handle to the device * @returns 0 on success, ETIMEDOUT on failure */ int oce_fw_clean(POCE_SOFTC sc) { struct oce_bmbx *mbx; uint8_t *ptr; int ret = 0; mbx = OCE_DMAPTR(&sc->bsmbx, struct oce_bmbx); ptr = (uint8_t *) &mbx->mbx; /* Endian Signature */ *ptr++ = 0xff; *ptr++ = 0xaa; *ptr++ = 0xbb; *ptr++ = 0xff; *ptr++ = 0xff; *ptr++ = 0xcc; *ptr++ = 0xdd; *ptr = 0xff; ret = oce_mbox_dispatch(sc, 2); return ret; } /** * @brief Mailbox wait * @param sc software handle to the device * @param tmo_sec timeout in seconds */ static int oce_mbox_wait(POCE_SOFTC sc, uint32_t tmo_sec) { tmo_sec *= 10000; pd_mpu_mbox_db_t mbox_db; for (;;) { if (tmo_sec != 0) { if (--tmo_sec == 0) break; } mbox_db.dw0 = OCE_READ_REG32(sc, db, PD_MPU_MBOX_DB); if (mbox_db.bits.ready) return 0; DELAY(100); } device_printf(sc->dev, "Mailbox timed out\n"); return ETIMEDOUT; } /** * @brief Mailbox dispatch * @param sc software handle to the device * @param tmo_sec timeout in seconds */ int oce_mbox_dispatch(POCE_SOFTC sc, uint32_t tmo_sec) { pd_mpu_mbox_db_t mbox_db; uint32_t pa; int rc; oce_dma_sync(&sc->bsmbx, BUS_DMASYNC_PREWRITE); pa = (uint32_t) ((uint64_t) sc->bsmbx.paddr >> 34); bzero(&mbox_db, sizeof(pd_mpu_mbox_db_t)); mbox_db.bits.ready = 0; mbox_db.bits.hi = 1; mbox_db.bits.address = pa; rc = oce_mbox_wait(sc, tmo_sec); if (rc == 0) { OCE_WRITE_REG32(sc, db, PD_MPU_MBOX_DB, mbox_db.dw0); pa = (uint32_t) ((uint64_t) sc->bsmbx.paddr >> 4) & 0x3fffffff; mbox_db.bits.ready = 0; mbox_db.bits.hi = 0; mbox_db.bits.address = pa; rc = oce_mbox_wait(sc, tmo_sec); if (rc == 0) { OCE_WRITE_REG32(sc, db, PD_MPU_MBOX_DB, mbox_db.dw0); rc = oce_mbox_wait(sc, tmo_sec); oce_dma_sync(&sc->bsmbx, BUS_DMASYNC_POSTWRITE); } } return rc; } /** * @brief Mailbox common request header initialization * @param hdr mailbox header * @param dom domain * @param port port * @param subsys subsystem * @param opcode opcode * @param timeout timeout * @param pyld_len payload length */ void mbx_common_req_hdr_init(struct mbx_hdr *hdr, uint8_t dom, uint8_t port, uint8_t subsys, uint8_t opcode, uint32_t timeout, uint32_t pyld_len, uint8_t version) { hdr->u0.req.opcode = opcode; hdr->u0.req.subsystem = subsys; hdr->u0.req.port_number = port; hdr->u0.req.domain = dom; hdr->u0.req.timeout = timeout; hdr->u0.req.request_length = pyld_len - sizeof(struct mbx_hdr); hdr->u0.req.version = version; } /** * @brief Function to initialize the hw with host endian information * @param sc software handle to the device * @returns 0 on success, ETIMEDOUT on failure */ int oce_mbox_init(POCE_SOFTC sc) { struct oce_bmbx *mbx; uint8_t *ptr; int ret = 0; if (sc->flags & OCE_FLAGS_MBOX_ENDIAN_RQD) { mbx = OCE_DMAPTR(&sc->bsmbx, struct oce_bmbx); ptr = (uint8_t *) &mbx->mbx; /* Endian Signature */ *ptr++ = 0xff; *ptr++ = 0x12; *ptr++ = 0x34; *ptr++ = 0xff; *ptr++ = 0xff; *ptr++ = 0x56; *ptr++ = 0x78; *ptr = 0xff; ret = oce_mbox_dispatch(sc, 0); } return ret; } /** * @brief Function to get the firmware version * @param sc software handle to the device * @returns 0 on success, EIO on failure */ int oce_get_fw_version(POCE_SOFTC sc) { struct oce_mbx mbx; struct mbx_get_common_fw_version *fwcmd; int ret = 0; bzero(&mbx, sizeof(struct oce_mbx)); fwcmd = (struct mbx_get_common_fw_version *)&mbx.payload; mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_COMMON, OPCODE_COMMON_GET_FW_VERSION, MBX_TIMEOUT_SEC, sizeof(struct mbx_get_common_fw_version), OCE_MBX_VER_V0); mbx.u0.s.embedded = 1; mbx.payload_length = sizeof(struct mbx_get_common_fw_version); DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ); ret = oce_mbox_post(sc, &mbx, NULL); if (!ret) ret = fwcmd->hdr.u0.rsp.status; if (ret) { device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, ret, fwcmd->hdr.u0.rsp.additional_status); goto error; } bcopy(fwcmd->params.rsp.fw_ver_str, sc->fw_version, 32); error: return ret; } /** * @brief Firmware will send gracious notifications during * attach only after sending first mcc commnad. We * use MCC queue only for getting async and mailbox * for sending cmds. So to get gracious notifications * atleast send one dummy command on mcc. */ int oce_first_mcc_cmd(POCE_SOFTC sc) { struct oce_mbx *mbx; struct oce_mq *mq = sc->mq; struct mbx_get_common_fw_version *fwcmd; uint32_t reg_value; mbx = RING_GET_PRODUCER_ITEM_VA(mq->ring, struct oce_mbx); bzero(mbx, sizeof(struct oce_mbx)); fwcmd = (struct mbx_get_common_fw_version *)&mbx->payload; mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_COMMON, OPCODE_COMMON_GET_FW_VERSION, MBX_TIMEOUT_SEC, sizeof(struct mbx_get_common_fw_version), OCE_MBX_VER_V0); mbx->u0.s.embedded = 1; mbx->payload_length = sizeof(struct mbx_get_common_fw_version); bus_dmamap_sync(mq->ring->dma.tag, mq->ring->dma.map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); RING_PUT(mq->ring, 1); reg_value = (1 << 16) | mq->mq_id; OCE_WRITE_REG32(sc, db, PD_MQ_DB, reg_value); return 0; } /** * @brief Function to post a MBX to the mbox * @param sc software handle to the device * @param mbx pointer to the MBX to send * @param mbxctx pointer to the mbx context structure * @returns 0 on success, error on failure */ int oce_mbox_post(POCE_SOFTC sc, struct oce_mbx *mbx, struct oce_mbx_ctx *mbxctx) { struct oce_mbx *mb_mbx = NULL; struct oce_mq_cqe *mb_cqe = NULL; struct oce_bmbx *mb = NULL; int rc = 0; uint32_t tmo = 0; uint32_t cstatus = 0; uint32_t xstatus = 0; LOCK(&sc->bmbx_lock); mb = OCE_DMAPTR(&sc->bsmbx, struct oce_bmbx); mb_mbx = &mb->mbx; /* get the tmo */ tmo = mbx->tag[0]; mbx->tag[0] = 0; /* copy mbx into mbox */ bcopy(mbx, mb_mbx, sizeof(struct oce_mbx)); /* now dispatch */ rc = oce_mbox_dispatch(sc, tmo); if (rc == 0) { /* * the command completed successfully. Now get the * completion queue entry */ mb_cqe = &mb->cqe; DW_SWAP(u32ptr(&mb_cqe->u0.dw[0]), sizeof(struct oce_mq_cqe)); /* copy mbox mbx back */ bcopy(mb_mbx, mbx, sizeof(struct oce_mbx)); /* pick up the mailbox status */ cstatus = mb_cqe->u0.s.completion_status; xstatus = mb_cqe->u0.s.extended_status; /* * store the mbx context in the cqe tag section so that * the upper layer handling the cqe can associate the mbx * with the response */ if (cstatus == 0 && mbxctx) { /* save context */ mbxctx->mbx = mb_mbx; bcopy(&mbxctx, mb_cqe->u0.s.mq_tag, sizeof(struct oce_mbx_ctx *)); } } UNLOCK(&sc->bmbx_lock); return rc; } /** * @brief Function to read the mac address associated with an interface * @param sc software handle to the device * @param if_id interface id to read the address from * @param perm set to 1 if reading the factory mac address. * In this case if_id is ignored * @param type type of the mac address, whether network or storage * @param[out] mac [OUTPUT] pointer to a buffer containing the * mac address when the command succeeds. * @returns 0 on success, EIO on failure */ int oce_read_mac_addr(POCE_SOFTC sc, uint32_t if_id, uint8_t perm, uint8_t type, struct mac_address_format *mac) { struct oce_mbx mbx; struct mbx_query_common_iface_mac *fwcmd; int ret = 0; bzero(&mbx, sizeof(struct oce_mbx)); fwcmd = (struct mbx_query_common_iface_mac *)&mbx.payload; mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_COMMON, OPCODE_COMMON_QUERY_IFACE_MAC, MBX_TIMEOUT_SEC, sizeof(struct mbx_query_common_iface_mac), OCE_MBX_VER_V0); fwcmd->params.req.permanent = perm; if (!perm) fwcmd->params.req.if_id = (uint16_t) if_id; else fwcmd->params.req.if_id = 0; fwcmd->params.req.type = type; mbx.u0.s.embedded = 1; mbx.payload_length = sizeof(struct mbx_query_common_iface_mac); DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ); ret = oce_mbox_post(sc, &mbx, NULL); if (!ret) ret = fwcmd->hdr.u0.rsp.status; if (ret) { device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, ret, fwcmd->hdr.u0.rsp.additional_status); goto error; } /* copy the mac addres in the output parameter */ mac->size_of_struct = fwcmd->params.rsp.mac.size_of_struct; bcopy(&fwcmd->params.rsp.mac.mac_addr[0], &mac->mac_addr[0], mac->size_of_struct); error: return ret; } /** * @brief Function to query the fw attributes from the hw * @param sc software handle to the device * @returns 0 on success, EIO on failure */ int oce_get_fw_config(POCE_SOFTC sc) { struct oce_mbx mbx; struct mbx_common_query_fw_config *fwcmd; int ret = 0; bzero(&mbx, sizeof(struct oce_mbx)); fwcmd = (struct mbx_common_query_fw_config *)&mbx.payload; mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_COMMON, OPCODE_COMMON_QUERY_FIRMWARE_CONFIG, MBX_TIMEOUT_SEC, sizeof(struct mbx_common_query_fw_config), OCE_MBX_VER_V0); mbx.u0.s.embedded = 1; mbx.payload_length = sizeof(struct mbx_common_query_fw_config); DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ); ret = oce_mbox_post(sc, &mbx, NULL); if (!ret) ret = fwcmd->hdr.u0.rsp.status; if (ret) { device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, ret, fwcmd->hdr.u0.rsp.additional_status); goto error; } DW_SWAP(u32ptr(fwcmd), sizeof(struct mbx_common_query_fw_config)); sc->config_number = HOST_32(fwcmd->params.rsp.config_number); sc->asic_revision = HOST_32(fwcmd->params.rsp.asic_revision); sc->port_id = HOST_32(fwcmd->params.rsp.port_id); sc->function_mode = HOST_32(fwcmd->params.rsp.function_mode); sc->function_caps = HOST_32(fwcmd->params.rsp.function_caps); if (fwcmd->params.rsp.ulp[0].ulp_mode & ULP_NIC_MODE) { sc->max_tx_rings = HOST_32(fwcmd->params.rsp.ulp[0].nic_wq_tot); sc->max_rx_rings = HOST_32(fwcmd->params.rsp.ulp[0].lro_rqid_tot); } else { sc->max_tx_rings = HOST_32(fwcmd->params.rsp.ulp[1].nic_wq_tot); sc->max_rx_rings = HOST_32(fwcmd->params.rsp.ulp[1].lro_rqid_tot); } error: return ret; } /** * * @brief function to create a device interface * @param sc software handle to the device * @param cap_flags capability flags * @param en_flags enable capability flags * @param vlan_tag optional vlan tag to associate with the if * @param mac_addr pointer to a buffer containing the mac address * @param[out] if_id [OUTPUT] pointer to an integer to hold the ID of the interface created * @returns 0 on success, EIO on failure */ int oce_if_create(POCE_SOFTC sc, uint32_t cap_flags, uint32_t en_flags, uint16_t vlan_tag, uint8_t *mac_addr, uint32_t *if_id) { struct oce_mbx mbx; struct mbx_create_common_iface *fwcmd; int rc = 0; bzero(&mbx, sizeof(struct oce_mbx)); fwcmd = (struct mbx_create_common_iface *)&mbx.payload; mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_COMMON, OPCODE_COMMON_CREATE_IFACE, MBX_TIMEOUT_SEC, sizeof(struct mbx_create_common_iface), OCE_MBX_VER_V0); DW_SWAP(u32ptr(&fwcmd->hdr), sizeof(struct mbx_hdr)); fwcmd->params.req.version = 0; fwcmd->params.req.cap_flags = LE_32(cap_flags); fwcmd->params.req.enable_flags = LE_32(en_flags); if (mac_addr != NULL) { bcopy(mac_addr, &fwcmd->params.req.mac_addr[0], 6); fwcmd->params.req.vlan_tag.u0.normal.vtag = LE_16(vlan_tag); fwcmd->params.req.mac_invalid = 0; } else { fwcmd->params.req.mac_invalid = 1; } mbx.u0.s.embedded = 1; mbx.payload_length = sizeof(struct mbx_create_common_iface); DW_SWAP(u32ptr(&mbx), OCE_BMBX_RHDR_SZ); rc = oce_mbox_post(sc, &mbx, NULL); if (!rc) rc = fwcmd->hdr.u0.rsp.status; if (rc) { device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, rc, fwcmd->hdr.u0.rsp.additional_status); goto error; } *if_id = HOST_32(fwcmd->params.rsp.if_id); if (mac_addr != NULL) sc->pmac_id = HOST_32(fwcmd->params.rsp.pmac_id); error: return rc; } /** * @brief Function to delete an interface * @param sc software handle to the device * @param if_id ID of the interface to delete * @returns 0 on success, EIO on failure */ int oce_if_del(POCE_SOFTC sc, uint32_t if_id) { struct oce_mbx mbx; struct mbx_destroy_common_iface *fwcmd; int rc = 0; bzero(&mbx, sizeof(struct oce_mbx)); fwcmd = (struct mbx_destroy_common_iface *)&mbx.payload; mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_COMMON, OPCODE_COMMON_DESTROY_IFACE, MBX_TIMEOUT_SEC, sizeof(struct mbx_destroy_common_iface), OCE_MBX_VER_V0); fwcmd->params.req.if_id = if_id; mbx.u0.s.embedded = 1; mbx.payload_length = sizeof(struct mbx_destroy_common_iface); DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ); rc = oce_mbox_post(sc, &mbx, NULL); if (!rc) rc = fwcmd->hdr.u0.rsp.status; if (rc) device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, rc, fwcmd->hdr.u0.rsp.additional_status); return rc; } /** * @brief Function to send the mbx command to configure vlan * @param sc software handle to the device * @param if_id interface identifier index * @param vtag_arr array of vlan tags * @param vtag_cnt number of elements in array * @param untagged boolean TRUE/FLASE * @param enable_promisc flag to enable/disable VLAN promiscuous mode * @returns 0 on success, EIO on failure */ int oce_config_vlan(POCE_SOFTC sc, uint32_t if_id, struct normal_vlan *vtag_arr, uint8_t vtag_cnt, uint32_t untagged, uint32_t enable_promisc) { struct oce_mbx mbx; struct mbx_common_config_vlan *fwcmd; int rc = 0; if (sc->vlans_added > sc->max_vlans) goto vlan_promisc; bzero(&mbx, sizeof(struct oce_mbx)); fwcmd = (struct mbx_common_config_vlan *)&mbx.payload; mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_COMMON, OPCODE_COMMON_CONFIG_IFACE_VLAN, MBX_TIMEOUT_SEC, sizeof(struct mbx_common_config_vlan), OCE_MBX_VER_V0); fwcmd->params.req.if_id = (uint8_t) if_id; fwcmd->params.req.promisc = (uint8_t) enable_promisc; fwcmd->params.req.untagged = (uint8_t) untagged; fwcmd->params.req.num_vlans = vtag_cnt; if (!enable_promisc) { bcopy(vtag_arr, fwcmd->params.req.tags.normal_vlans, vtag_cnt * sizeof(struct normal_vlan)); } mbx.u0.s.embedded = 1; mbx.payload_length = sizeof(struct mbx_common_config_vlan); DW_SWAP(u32ptr(&mbx), (OCE_BMBX_RHDR_SZ + mbx.payload_length)); rc = oce_mbox_post(sc, &mbx, NULL); if (!rc) rc = fwcmd->hdr.u0.rsp.status; if (rc) device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, rc, fwcmd->hdr.u0.rsp.additional_status); goto done; vlan_promisc: /* Enable Vlan Promis */ oce_rxf_set_promiscuous(sc, (1 << 1)); device_printf(sc->dev,"Enabling Vlan Promisc Mode\n"); done: return rc; } /** * @brief Function to set flow control capability in the hardware * @param sc software handle to the device * @param flow_control flow control flags to set * @returns 0 on success, EIO on failure */ int oce_set_flow_control(POCE_SOFTC sc, uint32_t flow_control) { struct oce_mbx mbx; struct mbx_common_get_set_flow_control *fwcmd = (struct mbx_common_get_set_flow_control *)&mbx.payload; int rc; bzero(&mbx, sizeof(struct oce_mbx)); mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_COMMON, OPCODE_COMMON_SET_FLOW_CONTROL, MBX_TIMEOUT_SEC, sizeof(struct mbx_common_get_set_flow_control), OCE_MBX_VER_V0); if (flow_control & OCE_FC_TX) fwcmd->tx_flow_control = 1; if (flow_control & OCE_FC_RX) fwcmd->rx_flow_control = 1; mbx.u0.s.embedded = 1; mbx.payload_length = sizeof(struct mbx_common_get_set_flow_control); DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ); rc = oce_mbox_post(sc, &mbx, NULL); if (!rc) rc = fwcmd->hdr.u0.rsp.status; if (rc) device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, rc, fwcmd->hdr.u0.rsp.additional_status); return rc; } /** * @brief Initialize the RSS CPU indirection table * * The table is used to choose the queue to place the incomming packets. * Incomming packets are hashed. The lowest bits in the hash result * are used as the index into the CPU indirection table. * Each entry in the table contains the RSS CPU-ID returned by the NIC * create. Based on the CPU ID, the receive completion is routed to * the corresponding RSS CQs. (Non-RSS packets are always completed * on the default (0) CQ). * * @param sc software handle to the device * @param *fwcmd pointer to the rss mbox command * @returns none */ static int oce_rss_itbl_init(POCE_SOFTC sc, struct mbx_config_nic_rss *fwcmd) { int i = 0, j = 0, rc = 0; uint8_t *tbl = fwcmd->params.req.cputable; struct oce_rq *rq = NULL; for (j = 0; j < INDIRECTION_TABLE_ENTRIES ; j += (sc->nrqs - 1)) { for_all_rss_queues(sc, rq, i) { if ((j + i) >= INDIRECTION_TABLE_ENTRIES) break; tbl[j + i] = rq->rss_cpuid; } } if (i == 0) { device_printf(sc->dev, "error: Invalid number of RSS RQ's\n"); rc = ENXIO; } /* fill log2 value indicating the size of the CPU table */ if (rc == 0) fwcmd->params.req.cpu_tbl_sz_log2 = LE_16(OCE_LOG2(i)); return rc; } /** * @brief Function to set flow control capability in the hardware * @param sc software handle to the device * @param if_id interface id to read the address from * @param enable_rss 0=disable, RSS_ENABLE_xxx flags otherwise * @returns 0 on success, EIO on failure */ int oce_config_nic_rss(POCE_SOFTC sc, uint32_t if_id, uint16_t enable_rss) { int rc; struct oce_mbx mbx; struct mbx_config_nic_rss *fwcmd = (struct mbx_config_nic_rss *)&mbx.payload; int version; bzero(&mbx, sizeof(struct oce_mbx)); if (IS_XE201(sc) || IS_SH(sc)) { version = OCE_MBX_VER_V1; fwcmd->params.req.enable_rss = RSS_ENABLE_UDP_IPV4 | RSS_ENABLE_UDP_IPV6; } else version = OCE_MBX_VER_V0; mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_NIC, NIC_CONFIG_RSS, MBX_TIMEOUT_SEC, sizeof(struct mbx_config_nic_rss), version); if (enable_rss) fwcmd->params.req.enable_rss |= (RSS_ENABLE_IPV4 | RSS_ENABLE_TCP_IPV4 | RSS_ENABLE_IPV6 | RSS_ENABLE_TCP_IPV6); fwcmd->params.req.flush = OCE_FLUSH; fwcmd->params.req.if_id = LE_32(if_id); - srandom(arc4random()); /* random entropy seed */ read_random(fwcmd->params.req.hash, sizeof(fwcmd->params.req.hash)); rc = oce_rss_itbl_init(sc, fwcmd); if (rc == 0) { mbx.u0.s.embedded = 1; mbx.payload_length = sizeof(struct mbx_config_nic_rss); DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ); rc = oce_mbox_post(sc, &mbx, NULL); if (!rc) rc = fwcmd->hdr.u0.rsp.status; if (rc) device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, rc, fwcmd->hdr.u0.rsp.additional_status); } return rc; } /** * @brief RXF function to enable/disable device promiscuous mode * @param sc software handle to the device * @param enable enable/disable flag * @returns 0 on success, EIO on failure * @note * The NIC_CONFIG_PROMISCUOUS command deprecated for Lancer. * This function uses the COMMON_SET_IFACE_RX_FILTER command instead. */ int oce_rxf_set_promiscuous(POCE_SOFTC sc, uint8_t enable) { struct mbx_set_common_iface_rx_filter *fwcmd; int sz = sizeof(struct mbx_set_common_iface_rx_filter); iface_rx_filter_ctx_t *req; OCE_DMA_MEM sgl; int rc; /* allocate mbx payload's dma scatter/gather memory */ rc = oce_dma_alloc(sc, sz, &sgl, 0); if (rc) return rc; fwcmd = OCE_DMAPTR(&sgl, struct mbx_set_common_iface_rx_filter); req = &fwcmd->params.req; req->iface_flags_mask = MBX_RX_IFACE_FLAGS_PROMISCUOUS | MBX_RX_IFACE_FLAGS_VLAN_PROMISCUOUS; /* Bit 0 Mac promisc, Bit 1 Vlan promisc */ if (enable & 0x01) req->iface_flags = MBX_RX_IFACE_FLAGS_PROMISCUOUS; if (enable & 0x02) req->iface_flags = MBX_RX_IFACE_FLAGS_VLAN_PROMISCUOUS; req->if_id = sc->if_id; rc = oce_set_common_iface_rx_filter(sc, &sgl); oce_dma_free(sc, &sgl); return rc; } /** * @brief Function modify and select rx filter options * @param sc software handle to the device * @param sgl scatter/gather request/response * @returns 0 on success, error code on failure */ int oce_set_common_iface_rx_filter(POCE_SOFTC sc, POCE_DMA_MEM sgl) { struct oce_mbx mbx; int mbx_sz = sizeof(struct mbx_set_common_iface_rx_filter); struct mbx_set_common_iface_rx_filter *fwcmd; int rc; bzero(&mbx, sizeof(struct oce_mbx)); fwcmd = OCE_DMAPTR(sgl, struct mbx_set_common_iface_rx_filter); mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_COMMON, OPCODE_COMMON_SET_IFACE_RX_FILTER, MBX_TIMEOUT_SEC, mbx_sz, OCE_MBX_VER_V0); oce_dma_sync(sgl, BUS_DMASYNC_PREWRITE); mbx.u0.s.embedded = 0; mbx.u0.s.sge_count = 1; mbx.payload.u0.u1.sgl[0].pa_lo = ADDR_LO(sgl->paddr); mbx.payload.u0.u1.sgl[0].pa_hi = ADDR_HI(sgl->paddr); mbx.payload.u0.u1.sgl[0].length = mbx_sz; mbx.payload_length = mbx_sz; DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ); rc = oce_mbox_post(sc, &mbx, NULL); if (!rc) rc = fwcmd->hdr.u0.rsp.status; if (rc) device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, rc, fwcmd->hdr.u0.rsp.additional_status); return rc; } /** * @brief Function to query the link status from the hardware * @param sc software handle to the device * @param[out] link pointer to the structure returning link attributes * @returns 0 on success, EIO on failure */ int oce_get_link_status(POCE_SOFTC sc, struct link_status *link) { struct oce_mbx mbx; struct mbx_query_common_link_config *fwcmd; int rc = 0, version; bzero(&mbx, sizeof(struct oce_mbx)); IS_BE2(sc) ? (version = OCE_MBX_VER_V0) : (version = OCE_MBX_VER_V1); fwcmd = (struct mbx_query_common_link_config *)&mbx.payload; mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_COMMON, OPCODE_COMMON_QUERY_LINK_CONFIG, MBX_TIMEOUT_SEC, sizeof(struct mbx_query_common_link_config), version); mbx.u0.s.embedded = 1; mbx.payload_length = sizeof(struct mbx_query_common_link_config); DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ); rc = oce_mbox_post(sc, &mbx, NULL); if (!rc) rc = fwcmd->hdr.u0.rsp.status; if (rc) { device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, rc, fwcmd->hdr.u0.rsp.additional_status); goto error; } /* interpret response */ link->qos_link_speed = HOST_16(fwcmd->params.rsp.qos_link_speed); link->phys_port_speed = fwcmd->params.rsp.physical_port_speed; link->logical_link_status = fwcmd->params.rsp.logical_link_status; error: return rc; } int oce_mbox_get_nic_stats_v0(POCE_SOFTC sc, POCE_DMA_MEM pstats_dma_mem) { struct oce_mbx mbx; struct mbx_get_nic_stats_v0 *fwcmd; int rc = 0; bzero(&mbx, sizeof(struct oce_mbx)); fwcmd = OCE_DMAPTR(pstats_dma_mem, struct mbx_get_nic_stats_v0); bzero(fwcmd, sizeof(struct mbx_get_nic_stats_v0)); mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_NIC, NIC_GET_STATS, MBX_TIMEOUT_SEC, sizeof(struct mbx_get_nic_stats_v0), OCE_MBX_VER_V0); mbx.u0.s.embedded = 0; mbx.u0.s.sge_count = 1; oce_dma_sync(pstats_dma_mem, BUS_DMASYNC_PREWRITE); mbx.payload.u0.u1.sgl[0].pa_lo = ADDR_LO(pstats_dma_mem->paddr); mbx.payload.u0.u1.sgl[0].pa_hi = ADDR_HI(pstats_dma_mem->paddr); mbx.payload.u0.u1.sgl[0].length = sizeof(struct mbx_get_nic_stats_v0); mbx.payload_length = sizeof(struct mbx_get_nic_stats_v0); DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ); rc = oce_mbox_post(sc, &mbx, NULL); oce_dma_sync(pstats_dma_mem, BUS_DMASYNC_POSTWRITE); if (!rc) rc = fwcmd->hdr.u0.rsp.status; if (rc) device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, rc, fwcmd->hdr.u0.rsp.additional_status); return rc; } /** * @brief Function to get NIC statistics * @param sc software handle to the device * @param *stats pointer to where to store statistics * @param reset_stats resets statistics of set * @returns 0 on success, EIO on failure * @note command depricated in Lancer */ int oce_mbox_get_nic_stats(POCE_SOFTC sc, POCE_DMA_MEM pstats_dma_mem) { struct oce_mbx mbx; struct mbx_get_nic_stats *fwcmd; int rc = 0; bzero(&mbx, sizeof(struct oce_mbx)); fwcmd = OCE_DMAPTR(pstats_dma_mem, struct mbx_get_nic_stats); bzero(fwcmd, sizeof(struct mbx_get_nic_stats)); mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_NIC, NIC_GET_STATS, MBX_TIMEOUT_SEC, sizeof(struct mbx_get_nic_stats), OCE_MBX_VER_V1); mbx.u0.s.embedded = 0; /* stats too large for embedded mbx rsp */ mbx.u0.s.sge_count = 1; /* using scatter gather instead */ oce_dma_sync(pstats_dma_mem, BUS_DMASYNC_PREWRITE); mbx.payload.u0.u1.sgl[0].pa_lo = ADDR_LO(pstats_dma_mem->paddr); mbx.payload.u0.u1.sgl[0].pa_hi = ADDR_HI(pstats_dma_mem->paddr); mbx.payload.u0.u1.sgl[0].length = sizeof(struct mbx_get_nic_stats); mbx.payload_length = sizeof(struct mbx_get_nic_stats); DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ); rc = oce_mbox_post(sc, &mbx, NULL); oce_dma_sync(pstats_dma_mem, BUS_DMASYNC_POSTWRITE); if (!rc) rc = fwcmd->hdr.u0.rsp.status; if (rc) device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, rc, fwcmd->hdr.u0.rsp.additional_status); return rc; } /** * @brief Function to get pport (physical port) statistics * @param sc software handle to the device * @param *stats pointer to where to store statistics * @param reset_stats resets statistics of set * @returns 0 on success, EIO on failure */ int oce_mbox_get_pport_stats(POCE_SOFTC sc, POCE_DMA_MEM pstats_dma_mem, uint32_t reset_stats) { struct oce_mbx mbx; struct mbx_get_pport_stats *fwcmd; int rc = 0; bzero(&mbx, sizeof(struct oce_mbx)); fwcmd = OCE_DMAPTR(pstats_dma_mem, struct mbx_get_pport_stats); bzero(fwcmd, sizeof(struct mbx_get_pport_stats)); mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_NIC, NIC_GET_PPORT_STATS, MBX_TIMEOUT_SEC, sizeof(struct mbx_get_pport_stats), OCE_MBX_VER_V0); fwcmd->params.req.reset_stats = reset_stats; fwcmd->params.req.port_number = sc->port_id; mbx.u0.s.embedded = 0; /* stats too large for embedded mbx rsp */ mbx.u0.s.sge_count = 1; /* using scatter gather instead */ oce_dma_sync(pstats_dma_mem, BUS_DMASYNC_PREWRITE); mbx.payload.u0.u1.sgl[0].pa_lo = ADDR_LO(pstats_dma_mem->paddr); mbx.payload.u0.u1.sgl[0].pa_hi = ADDR_HI(pstats_dma_mem->paddr); mbx.payload.u0.u1.sgl[0].length = sizeof(struct mbx_get_pport_stats); mbx.payload_length = sizeof(struct mbx_get_pport_stats); DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ); rc = oce_mbox_post(sc, &mbx, NULL); oce_dma_sync(pstats_dma_mem, BUS_DMASYNC_POSTWRITE); if (!rc) rc = fwcmd->hdr.u0.rsp.status; if (rc) device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, rc, fwcmd->hdr.u0.rsp.additional_status); return rc; } /** * @brief Function to get vport (virtual port) statistics * @param sc software handle to the device * @param *stats pointer to where to store statistics * @param reset_stats resets statistics of set * @returns 0 on success, EIO on failure */ int oce_mbox_get_vport_stats(POCE_SOFTC sc, POCE_DMA_MEM pstats_dma_mem, uint32_t req_size, uint32_t reset_stats) { struct oce_mbx mbx; struct mbx_get_vport_stats *fwcmd; int rc = 0; bzero(&mbx, sizeof(struct oce_mbx)); fwcmd = OCE_DMAPTR(pstats_dma_mem, struct mbx_get_vport_stats); bzero(fwcmd, sizeof(struct mbx_get_vport_stats)); mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_NIC, NIC_GET_VPORT_STATS, MBX_TIMEOUT_SEC, sizeof(struct mbx_get_vport_stats), OCE_MBX_VER_V0); fwcmd->params.req.reset_stats = reset_stats; fwcmd->params.req.vport_number = sc->if_id; mbx.u0.s.embedded = 0; /* stats too large for embedded mbx rsp */ mbx.u0.s.sge_count = 1; /* using scatter gather instead */ oce_dma_sync(pstats_dma_mem, BUS_DMASYNC_PREWRITE); mbx.payload.u0.u1.sgl[0].pa_lo = ADDR_LO(pstats_dma_mem->paddr); mbx.payload.u0.u1.sgl[0].pa_hi = ADDR_HI(pstats_dma_mem->paddr); mbx.payload.u0.u1.sgl[0].length = sizeof(struct mbx_get_vport_stats); mbx.payload_length = sizeof(struct mbx_get_vport_stats); DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ); rc = oce_mbox_post(sc, &mbx, NULL); oce_dma_sync(pstats_dma_mem, BUS_DMASYNC_POSTWRITE); if (!rc) rc = fwcmd->hdr.u0.rsp.status; if (rc) device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, rc, fwcmd->hdr.u0.rsp.additional_status); return rc; } /** * @brief Function to update the muticast filter with * values in dma_mem * @param sc software handle to the device * @param dma_mem pointer to dma memory region * @returns 0 on success, EIO on failure */ int oce_update_multicast(POCE_SOFTC sc, POCE_DMA_MEM pdma_mem) { struct oce_mbx mbx; struct oce_mq_sge *sgl; struct mbx_set_common_iface_multicast *req = NULL; int rc = 0; req = OCE_DMAPTR(pdma_mem, struct mbx_set_common_iface_multicast); mbx_common_req_hdr_init(&req->hdr, 0, 0, MBX_SUBSYSTEM_COMMON, OPCODE_COMMON_SET_IFACE_MULTICAST, MBX_TIMEOUT_SEC, sizeof(struct mbx_set_common_iface_multicast), OCE_MBX_VER_V0); bzero(&mbx, sizeof(struct oce_mbx)); mbx.u0.s.embedded = 0; /*Non embeded*/ mbx.payload_length = sizeof(struct mbx_set_common_iface_multicast); mbx.u0.s.sge_count = 1; sgl = &mbx.payload.u0.u1.sgl[0]; sgl->pa_hi = htole32(upper_32_bits(pdma_mem->paddr)); sgl->pa_lo = htole32((pdma_mem->paddr) & 0xFFFFFFFF); sgl->length = htole32(mbx.payload_length); DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ); rc = oce_mbox_post(sc, &mbx, NULL); if (!rc) rc = req->hdr.u0.rsp.status; if (rc) device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, rc, req->hdr.u0.rsp.additional_status); return rc; } /** * @brief Function to send passthrough Ioctls * @param sc software handle to the device * @param dma_mem pointer to dma memory region * @param req_size size of dma_mem * @returns 0 on success, EIO on failure */ int oce_pass_through_mbox(POCE_SOFTC sc, POCE_DMA_MEM dma_mem, uint32_t req_size) { struct oce_mbx mbx; struct oce_mq_sge *sgl; int rc = 0; bzero(&mbx, sizeof(struct oce_mbx)); mbx.u0.s.embedded = 0; /*Non embeded*/ mbx.payload_length = req_size; mbx.u0.s.sge_count = 1; sgl = &mbx.payload.u0.u1.sgl[0]; sgl->pa_hi = htole32(upper_32_bits(dma_mem->paddr)); sgl->pa_lo = htole32((dma_mem->paddr) & 0xFFFFFFFF); sgl->length = htole32(req_size); DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ); rc = oce_mbox_post(sc, &mbx, NULL); return rc; } int oce_mbox_macaddr_add(POCE_SOFTC sc, uint8_t *mac_addr, uint32_t if_id, uint32_t *pmac_id) { struct oce_mbx mbx; struct mbx_add_common_iface_mac *fwcmd; int rc = 0; bzero(&mbx, sizeof(struct oce_mbx)); fwcmd = (struct mbx_add_common_iface_mac *)&mbx.payload; mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_COMMON, OPCODE_COMMON_ADD_IFACE_MAC, MBX_TIMEOUT_SEC, sizeof(struct mbx_add_common_iface_mac), OCE_MBX_VER_V0); fwcmd->params.req.if_id = (uint16_t) if_id; bcopy(mac_addr, fwcmd->params.req.mac_address, 6); mbx.u0.s.embedded = 1; mbx.payload_length = sizeof(struct mbx_add_common_iface_mac); DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ); rc = oce_mbox_post(sc, &mbx, NULL); if (!rc) rc = fwcmd->hdr.u0.rsp.status; if (rc) { device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, rc, fwcmd->hdr.u0.rsp.additional_status); goto error; } *pmac_id = fwcmd->params.rsp.pmac_id; error: return rc; } int oce_mbox_macaddr_del(POCE_SOFTC sc, uint32_t if_id, uint32_t pmac_id) { struct oce_mbx mbx; struct mbx_del_common_iface_mac *fwcmd; int rc = 0; bzero(&mbx, sizeof(struct oce_mbx)); fwcmd = (struct mbx_del_common_iface_mac *)&mbx.payload; mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_COMMON, OPCODE_COMMON_DEL_IFACE_MAC, MBX_TIMEOUT_SEC, sizeof(struct mbx_del_common_iface_mac), OCE_MBX_VER_V0); fwcmd->params.req.if_id = (uint16_t)if_id; fwcmd->params.req.pmac_id = pmac_id; mbx.u0.s.embedded = 1; mbx.payload_length = sizeof(struct mbx_del_common_iface_mac); DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ); rc = oce_mbox_post(sc, &mbx, NULL); if (!rc) rc = fwcmd->hdr.u0.rsp.status; if (rc) device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, rc, fwcmd->hdr.u0.rsp.additional_status); return rc; } int oce_mbox_check_native_mode(POCE_SOFTC sc) { struct oce_mbx mbx; struct mbx_common_set_function_cap *fwcmd; int rc = 0; bzero(&mbx, sizeof(struct oce_mbx)); fwcmd = (struct mbx_common_set_function_cap *)&mbx.payload; mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_COMMON, OPCODE_COMMON_SET_FUNCTIONAL_CAPS, MBX_TIMEOUT_SEC, sizeof(struct mbx_common_set_function_cap), OCE_MBX_VER_V0); fwcmd->params.req.valid_capability_flags = CAP_SW_TIMESTAMPS | CAP_BE3_NATIVE_ERX_API; fwcmd->params.req.capability_flags = CAP_BE3_NATIVE_ERX_API; mbx.u0.s.embedded = 1; mbx.payload_length = sizeof(struct mbx_common_set_function_cap); DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ); rc = oce_mbox_post(sc, &mbx, NULL); if (!rc) rc = fwcmd->hdr.u0.rsp.status; if (rc) { device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, rc, fwcmd->hdr.u0.rsp.additional_status); goto error; } sc->be3_native = HOST_32(fwcmd->params.rsp.capability_flags) & CAP_BE3_NATIVE_ERX_API; error: return 0; } int oce_mbox_cmd_set_loopback(POCE_SOFTC sc, uint8_t port_num, uint8_t loopback_type, uint8_t enable) { struct oce_mbx mbx; struct mbx_lowlevel_set_loopback_mode *fwcmd; int rc = 0; bzero(&mbx, sizeof(struct oce_mbx)); fwcmd = (struct mbx_lowlevel_set_loopback_mode *)&mbx.payload; mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_LOWLEVEL, OPCODE_LOWLEVEL_SET_LOOPBACK_MODE, MBX_TIMEOUT_SEC, sizeof(struct mbx_lowlevel_set_loopback_mode), OCE_MBX_VER_V0); fwcmd->params.req.src_port = port_num; fwcmd->params.req.dest_port = port_num; fwcmd->params.req.loopback_type = loopback_type; fwcmd->params.req.loopback_state = enable; mbx.u0.s.embedded = 1; mbx.payload_length = sizeof(struct mbx_lowlevel_set_loopback_mode); DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ); rc = oce_mbox_post(sc, &mbx, NULL); if (!rc) rc = fwcmd->hdr.u0.rsp.status; if (rc) device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, rc, fwcmd->hdr.u0.rsp.additional_status); return rc; } int oce_mbox_cmd_test_loopback(POCE_SOFTC sc, uint32_t port_num, uint32_t loopback_type, uint32_t pkt_size, uint32_t num_pkts, uint64_t pattern) { struct oce_mbx mbx; struct mbx_lowlevel_test_loopback_mode *fwcmd; int rc = 0; bzero(&mbx, sizeof(struct oce_mbx)); fwcmd = (struct mbx_lowlevel_test_loopback_mode *)&mbx.payload; mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_LOWLEVEL, OPCODE_LOWLEVEL_TEST_LOOPBACK, MBX_TIMEOUT_SEC, sizeof(struct mbx_lowlevel_test_loopback_mode), OCE_MBX_VER_V0); fwcmd->params.req.pattern = pattern; fwcmd->params.req.src_port = port_num; fwcmd->params.req.dest_port = port_num; fwcmd->params.req.pkt_size = pkt_size; fwcmd->params.req.num_pkts = num_pkts; fwcmd->params.req.loopback_type = loopback_type; mbx.u0.s.embedded = 1; mbx.payload_length = sizeof(struct mbx_lowlevel_test_loopback_mode); DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ); rc = oce_mbox_post(sc, &mbx, NULL); if (!rc) rc = fwcmd->hdr.u0.rsp.status; if (rc) device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, rc, fwcmd->hdr.u0.rsp.additional_status); return rc; } int oce_mbox_write_flashrom(POCE_SOFTC sc, uint32_t optype,uint32_t opcode, POCE_DMA_MEM pdma_mem, uint32_t num_bytes) { struct oce_mbx mbx; struct oce_mq_sge *sgl = NULL; struct mbx_common_read_write_flashrom *fwcmd = NULL; int rc = 0, payload_len = 0; bzero(&mbx, sizeof(struct oce_mbx)); fwcmd = OCE_DMAPTR(pdma_mem, struct mbx_common_read_write_flashrom); payload_len = sizeof(struct mbx_common_read_write_flashrom) + 32*1024; mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_COMMON, OPCODE_COMMON_WRITE_FLASHROM, LONG_TIMEOUT, payload_len, OCE_MBX_VER_V0); fwcmd->flash_op_type = LE_32(optype); fwcmd->flash_op_code = LE_32(opcode); fwcmd->data_buffer_size = LE_32(num_bytes); mbx.u0.s.embedded = 0; /*Non embeded*/ mbx.payload_length = payload_len; mbx.u0.s.sge_count = 1; sgl = &mbx.payload.u0.u1.sgl[0]; sgl->pa_hi = upper_32_bits(pdma_mem->paddr); sgl->pa_lo = pdma_mem->paddr & 0xFFFFFFFF; sgl->length = payload_len; /* post the command */ rc = oce_mbox_post(sc, &mbx, NULL); if (!rc) rc = fwcmd->hdr.u0.rsp.status; if (rc) device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, rc, fwcmd->hdr.u0.rsp.additional_status); return rc; } int oce_mbox_get_flashrom_crc(POCE_SOFTC sc, uint8_t *flash_crc, uint32_t offset, uint32_t optype) { int rc = 0, payload_len = 0; struct oce_mbx mbx; struct mbx_common_read_write_flashrom *fwcmd; bzero(&mbx, sizeof(struct oce_mbx)); fwcmd = (struct mbx_common_read_write_flashrom *)&mbx.payload; /* Firmware requires extra 4 bytes with this ioctl. Since there is enough room in the mbx payload it should be good enough Reference: Bug 14853 */ payload_len = sizeof(struct mbx_common_read_write_flashrom) + 4; mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_COMMON, OPCODE_COMMON_READ_FLASHROM, MBX_TIMEOUT_SEC, payload_len, OCE_MBX_VER_V0); fwcmd->flash_op_type = optype; fwcmd->flash_op_code = FLASHROM_OPER_REPORT; fwcmd->data_offset = offset; fwcmd->data_buffer_size = 0x4; mbx.u0.s.embedded = 1; mbx.payload_length = payload_len; /* post the command */ rc = oce_mbox_post(sc, &mbx, NULL); if (!rc) rc = fwcmd->hdr.u0.rsp.status; if (rc) { device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, rc, fwcmd->hdr.u0.rsp.additional_status); goto error; } bcopy(fwcmd->data_buffer, flash_crc, 4); error: return rc; } int oce_mbox_get_phy_info(POCE_SOFTC sc, struct oce_phy_info *phy_info) { struct oce_mbx mbx; struct mbx_common_phy_info *fwcmd; int rc = 0; bzero(&mbx, sizeof(struct oce_mbx)); fwcmd = (struct mbx_common_phy_info *)&mbx.payload; mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_COMMON, OPCODE_COMMON_GET_PHY_CONFIG, MBX_TIMEOUT_SEC, sizeof(struct mbx_common_phy_info), OCE_MBX_VER_V0); mbx.u0.s.embedded = 1; mbx.payload_length = sizeof(struct mbx_common_phy_info); /* now post the command */ rc = oce_mbox_post(sc, &mbx, NULL); if (!rc) rc = fwcmd->hdr.u0.rsp.status; if (rc) { device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, rc, fwcmd->hdr.u0.rsp.additional_status); goto error; } phy_info->phy_type = HOST_16(fwcmd->params.rsp.phy_info.phy_type); phy_info->interface_type = HOST_16(fwcmd->params.rsp.phy_info.interface_type); phy_info->auto_speeds_supported = HOST_16(fwcmd->params.rsp.phy_info.auto_speeds_supported); phy_info->fixed_speeds_supported = HOST_16(fwcmd->params.rsp.phy_info.fixed_speeds_supported); phy_info->misc_params = HOST_32(fwcmd->params.rsp.phy_info.misc_params); error: return rc; } int oce_mbox_lancer_write_flashrom(POCE_SOFTC sc, uint32_t data_size, uint32_t data_offset, POCE_DMA_MEM pdma_mem, uint32_t *written_data, uint32_t *additional_status) { struct oce_mbx mbx; struct mbx_lancer_common_write_object *fwcmd = NULL; int rc = 0, payload_len = 0; bzero(&mbx, sizeof(struct oce_mbx)); payload_len = sizeof(struct mbx_lancer_common_write_object); mbx.u0.s.embedded = 1;/* Embedded */ mbx.payload_length = payload_len; fwcmd = (struct mbx_lancer_common_write_object *)&mbx.payload; /* initialize the ioctl header */ mbx_common_req_hdr_init(&fwcmd->params.req.hdr, 0, 0, MBX_SUBSYSTEM_COMMON, OPCODE_COMMON_WRITE_OBJECT, LONG_TIMEOUT, payload_len, OCE_MBX_VER_V0); fwcmd->params.req.write_length = data_size; if (data_size == 0) fwcmd->params.req.eof = 1; else fwcmd->params.req.eof = 0; strcpy(fwcmd->params.req.object_name, "/prg"); fwcmd->params.req.descriptor_count = 1; fwcmd->params.req.write_offset = data_offset; fwcmd->params.req.buffer_length = data_size; fwcmd->params.req.address_lower = pdma_mem->paddr & 0xFFFFFFFF; fwcmd->params.req.address_upper = upper_32_bits(pdma_mem->paddr); /* post the command */ rc = oce_mbox_post(sc, &mbx, NULL); if (!rc) rc = fwcmd->params.rsp.status; if (rc) { device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, rc, fwcmd->params.rsp.additional_status); goto error; } *written_data = HOST_32(fwcmd->params.rsp.actual_write_length); *additional_status = fwcmd->params.rsp.additional_status; error: return rc; } int oce_mbox_create_rq(struct oce_rq *rq) { struct oce_mbx mbx; struct mbx_create_nic_rq *fwcmd; POCE_SOFTC sc = rq->parent; int rc, num_pages = 0; if (rq->qstate == QCREATED) return 0; bzero(&mbx, sizeof(struct oce_mbx)); fwcmd = (struct mbx_create_nic_rq *)&mbx.payload; mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_NIC, NIC_CREATE_RQ, MBX_TIMEOUT_SEC, sizeof(struct mbx_create_nic_rq), OCE_MBX_VER_V0); /* oce_page_list will also prepare pages */ num_pages = oce_page_list(rq->ring, &fwcmd->params.req.pages[0]); if (IS_XE201(sc)) { fwcmd->params.req.frag_size = rq->cfg.frag_size/2048; fwcmd->params.req.page_size = 1; fwcmd->hdr.u0.req.version = OCE_MBX_VER_V1; } else fwcmd->params.req.frag_size = OCE_LOG2(rq->cfg.frag_size); fwcmd->params.req.num_pages = num_pages; fwcmd->params.req.cq_id = rq->cq->cq_id; fwcmd->params.req.if_id = sc->if_id; fwcmd->params.req.max_frame_size = rq->cfg.mtu; fwcmd->params.req.is_rss_queue = rq->cfg.is_rss_queue; mbx.u0.s.embedded = 1; mbx.payload_length = sizeof(struct mbx_create_nic_rq); rc = oce_mbox_post(sc, &mbx, NULL); if (!rc) rc = fwcmd->hdr.u0.rsp.status; if (rc) { device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, rc, fwcmd->hdr.u0.rsp.additional_status); goto error; } rq->rq_id = HOST_16(fwcmd->params.rsp.rq_id); rq->rss_cpuid = fwcmd->params.rsp.rss_cpuid; error: return rc; } int oce_mbox_create_wq(struct oce_wq *wq) { struct oce_mbx mbx; struct mbx_create_nic_wq *fwcmd; POCE_SOFTC sc = wq->parent; int rc = 0, version, num_pages; bzero(&mbx, sizeof(struct oce_mbx)); fwcmd = (struct mbx_create_nic_wq *)&mbx.payload; if (IS_XE201(sc)) version = OCE_MBX_VER_V1; else if(IS_BE(sc)) IS_PROFILE_SUPER_NIC(sc) ? (version = OCE_MBX_VER_V2) : (version = OCE_MBX_VER_V0); else version = OCE_MBX_VER_V2; if (version > OCE_MBX_VER_V0) fwcmd->params.req.if_id = sc->if_id; mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_NIC, NIC_CREATE_WQ, MBX_TIMEOUT_SEC, sizeof(struct mbx_create_nic_wq), version); num_pages = oce_page_list(wq->ring, &fwcmd->params.req.pages[0]); fwcmd->params.req.nic_wq_type = wq->cfg.wq_type; fwcmd->params.req.num_pages = num_pages; fwcmd->params.req.wq_size = OCE_LOG2(wq->cfg.q_len) + 1; fwcmd->params.req.cq_id = wq->cq->cq_id; fwcmd->params.req.ulp_num = 1; mbx.u0.s.embedded = 1; mbx.payload_length = sizeof(struct mbx_create_nic_wq); rc = oce_mbox_post(sc, &mbx, NULL); if (!rc) rc = fwcmd->hdr.u0.rsp.status; if (rc) { device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, rc, fwcmd->hdr.u0.rsp.additional_status); goto error; } wq->wq_id = HOST_16(fwcmd->params.rsp.wq_id); if (version == OCE_MBX_VER_V2) wq->db_offset = HOST_32(fwcmd->params.rsp.db_offset); else wq->db_offset = PD_TXULP_DB; error: return rc; } int oce_mbox_create_eq(struct oce_eq *eq) { struct oce_mbx mbx; struct mbx_create_common_eq *fwcmd; POCE_SOFTC sc = eq->parent; int rc = 0; uint32_t num_pages; bzero(&mbx, sizeof(struct oce_mbx)); fwcmd = (struct mbx_create_common_eq *)&mbx.payload; mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_COMMON, OPCODE_COMMON_CREATE_EQ, MBX_TIMEOUT_SEC, sizeof(struct mbx_create_common_eq), OCE_MBX_VER_V0); num_pages = oce_page_list(eq->ring, &fwcmd->params.req.pages[0]); fwcmd->params.req.ctx.num_pages = num_pages; fwcmd->params.req.ctx.valid = 1; fwcmd->params.req.ctx.size = (eq->eq_cfg.item_size == 4) ? 0 : 1; fwcmd->params.req.ctx.count = OCE_LOG2(eq->eq_cfg.q_len / 256); fwcmd->params.req.ctx.armed = 0; fwcmd->params.req.ctx.delay_mult = eq->eq_cfg.cur_eqd; mbx.u0.s.embedded = 1; mbx.payload_length = sizeof(struct mbx_create_common_eq); rc = oce_mbox_post(sc, &mbx, NULL); if (!rc) rc = fwcmd->hdr.u0.rsp.status; if (rc) { device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, rc, fwcmd->hdr.u0.rsp.additional_status); goto error; } eq->eq_id = HOST_16(fwcmd->params.rsp.eq_id); error: return rc; } int oce_mbox_cq_create(struct oce_cq *cq, uint32_t ncoalesce, uint32_t is_eventable) { struct oce_mbx mbx; struct mbx_create_common_cq *fwcmd; POCE_SOFTC sc = cq->parent; uint8_t version; oce_cq_ctx_t *ctx; uint32_t num_pages, page_size; int rc = 0; bzero(&mbx, sizeof(struct oce_mbx)); fwcmd = (struct mbx_create_common_cq *)&mbx.payload; if (IS_XE201(sc)) version = OCE_MBX_VER_V2; else version = OCE_MBX_VER_V0; mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_COMMON, OPCODE_COMMON_CREATE_CQ, MBX_TIMEOUT_SEC, sizeof(struct mbx_create_common_cq), version); ctx = &fwcmd->params.req.cq_ctx; num_pages = oce_page_list(cq->ring, &fwcmd->params.req.pages[0]); page_size = 1; /* 1 for 4K */ if (version == OCE_MBX_VER_V2) { ctx->v2.num_pages = LE_16(num_pages); ctx->v2.page_size = page_size; ctx->v2.eventable = is_eventable; ctx->v2.valid = 1; ctx->v2.count = OCE_LOG2(cq->cq_cfg.q_len / 256); ctx->v2.nodelay = cq->cq_cfg.nodelay; ctx->v2.coalesce_wm = ncoalesce; ctx->v2.armed = 0; ctx->v2.eq_id = cq->eq->eq_id; if (ctx->v2.count == 3) { if ((u_int)cq->cq_cfg.q_len > (4*1024)-1) ctx->v2.cqe_count = (4*1024)-1; else ctx->v2.cqe_count = cq->cq_cfg.q_len; } } else { ctx->v0.num_pages = LE_16(num_pages); ctx->v0.eventable = is_eventable; ctx->v0.valid = 1; ctx->v0.count = OCE_LOG2(cq->cq_cfg.q_len / 256); ctx->v0.nodelay = cq->cq_cfg.nodelay; ctx->v0.coalesce_wm = ncoalesce; ctx->v0.armed = 0; ctx->v0.eq_id = cq->eq->eq_id; } mbx.u0.s.embedded = 1; mbx.payload_length = sizeof(struct mbx_create_common_cq); rc = oce_mbox_post(sc, &mbx, NULL); if (!rc) rc = fwcmd->hdr.u0.rsp.status; if (rc) { device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, rc, fwcmd->hdr.u0.rsp.additional_status); goto error; } cq->cq_id = HOST_16(fwcmd->params.rsp.cq_id); error: return rc; } int oce_mbox_read_transrecv_data(POCE_SOFTC sc, uint32_t page_num) { int rc = 0; struct oce_mbx mbx; struct mbx_read_common_transrecv_data *fwcmd; struct oce_mq_sge *sgl; OCE_DMA_MEM dma; /* Allocate DMA mem*/ if (oce_dma_alloc(sc, sizeof(struct mbx_read_common_transrecv_data), &dma, 0)) return ENOMEM; fwcmd = OCE_DMAPTR(&dma, struct mbx_read_common_transrecv_data); bzero(fwcmd, sizeof(struct mbx_read_common_transrecv_data)); bzero(&mbx, sizeof(struct oce_mbx)); mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_COMMON, OPCODE_COMMON_READ_TRANSRECEIVER_DATA, MBX_TIMEOUT_SEC, sizeof(struct mbx_read_common_transrecv_data), OCE_MBX_VER_V0); /* fill rest of mbx */ mbx.u0.s.embedded = 0; mbx.payload_length = sizeof(struct mbx_read_common_transrecv_data); mbx.u0.s.sge_count = 1; sgl = &mbx.payload.u0.u1.sgl[0]; sgl->pa_hi = htole32(upper_32_bits(dma.paddr)); sgl->pa_lo = htole32((dma.paddr) & 0xFFFFFFFF); sgl->length = htole32(mbx.payload_length); DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ); fwcmd->params.req.port = LE_32(sc->port_id); fwcmd->params.req.page_num = LE_32(page_num); /* command post */ rc = oce_mbox_post(sc, &mbx, NULL); if (!rc) rc = fwcmd->hdr.u0.rsp.status; if (rc) { device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, rc, fwcmd->hdr.u0.rsp.additional_status); goto error; } if(fwcmd->params.rsp.page_num == PAGE_NUM_A0) { bcopy((char *)fwcmd->params.rsp.page_data, (char *)&sfp_vpd_dump_buffer[0], TRANSCEIVER_A0_SIZE); } if(fwcmd->params.rsp.page_num == PAGE_NUM_A2) { bcopy((char *)fwcmd->params.rsp.page_data, (char *)&sfp_vpd_dump_buffer[32], TRANSCEIVER_A2_SIZE); } error: oce_dma_free(sc, &dma); return rc; } void oce_mbox_eqd_modify_periodic(POCE_SOFTC sc, struct oce_set_eqd *set_eqd, int num) { struct oce_mbx mbx; struct mbx_modify_common_eq_delay *fwcmd; int rc = 0; int i = 0; bzero(&mbx, sizeof(struct oce_mbx)); /* Initialize MODIFY_EQ_DELAY ioctl header */ fwcmd = (struct mbx_modify_common_eq_delay *)&mbx.payload; mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_COMMON, OPCODE_COMMON_MODIFY_EQ_DELAY, MBX_TIMEOUT_SEC, sizeof(struct mbx_modify_common_eq_delay), OCE_MBX_VER_V0); /* fill rest of mbx */ mbx.u0.s.embedded = 1; mbx.payload_length = sizeof(struct mbx_modify_common_eq_delay); DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ); fwcmd->params.req.num_eq = num; for (i = 0; i < num; i++) { fwcmd->params.req.delay[i].eq_id = htole32(set_eqd[i].eq_id); fwcmd->params.req.delay[i].phase = 0; fwcmd->params.req.delay[i].dm = htole32(set_eqd[i].delay_multiplier); } /* command post */ rc = oce_mbox_post(sc, &mbx, NULL); if (!rc) rc = fwcmd->hdr.u0.rsp.status; if (rc) device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, rc, fwcmd->hdr.u0.rsp.additional_status); } int oce_get_profile_config(POCE_SOFTC sc, uint32_t max_rss) { struct oce_mbx mbx; struct mbx_common_get_profile_config *fwcmd; int rc = 0; int version = 0; struct oce_mq_sge *sgl; OCE_DMA_MEM dma; uint32_t desc_count = 0; struct oce_nic_resc_desc *nic_desc = NULL; int i; boolean_t nic_desc_valid = FALSE; if (IS_BE2(sc)) return -1; /* Allocate DMA mem*/ if (oce_dma_alloc(sc, sizeof(struct mbx_common_get_profile_config), &dma, 0)) return ENOMEM; /* Initialize MODIFY_EQ_DELAY ioctl header */ fwcmd = OCE_DMAPTR(&dma, struct mbx_common_get_profile_config); bzero(fwcmd, sizeof(struct mbx_common_get_profile_config)); if (!IS_XE201(sc)) version = OCE_MBX_VER_V1; else version = OCE_MBX_VER_V0; bzero(&mbx, sizeof(struct oce_mbx)); mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_COMMON, OPCODE_COMMON_GET_PROFILE_CONFIG, MBX_TIMEOUT_SEC, sizeof(struct mbx_common_get_profile_config), version); /* fill rest of mbx */ mbx.u0.s.embedded = 0; mbx.payload_length = sizeof(struct mbx_common_get_profile_config); mbx.u0.s.sge_count = 1; sgl = &mbx.payload.u0.u1.sgl[0]; sgl->pa_hi = htole32(upper_32_bits(dma.paddr)); sgl->pa_lo = htole32((dma.paddr) & 0xFFFFFFFF); sgl->length = htole32(mbx.payload_length); DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ); fwcmd->params.req.type = ACTIVE_PROFILE; /* command post */ rc = oce_mbox_post(sc, &mbx, NULL); if (!rc) rc = fwcmd->hdr.u0.rsp.status; if (rc) { device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, rc, fwcmd->hdr.u0.rsp.additional_status); goto error; } nic_desc = (struct oce_nic_resc_desc *) fwcmd->params.rsp.resources; desc_count = HOST_32(fwcmd->params.rsp.desc_count); for (i = 0; i < desc_count; i++) { if ((nic_desc->desc_type == NIC_RESC_DESC_TYPE_V0) || (nic_desc->desc_type == NIC_RESC_DESC_TYPE_V1)) { nic_desc_valid = TRUE; break; } nic_desc = (struct oce_nic_resc_desc *) \ ((char *)nic_desc + nic_desc->desc_len); } if (!nic_desc_valid) { rc = -1; goto error; } else { sc->max_vlans = HOST_16(nic_desc->vlan_count); sc->nwqs = HOST_16(nic_desc->txq_count); if (sc->nwqs) sc->nwqs = MIN(sc->nwqs, OCE_MAX_WQ); else sc->nwqs = OCE_MAX_WQ; sc->nrssqs = HOST_16(nic_desc->rssq_count); if (sc->nrssqs) sc->nrssqs = MIN(sc->nrssqs, max_rss); else sc->nrssqs = max_rss; sc->nrqs = sc->nrssqs + 1; /* 1 for def RX */; } error: oce_dma_free(sc, &dma); return rc; } int oce_get_func_config(POCE_SOFTC sc) { struct oce_mbx mbx; struct mbx_common_get_func_config *fwcmd; int rc = 0; int version = 0; struct oce_mq_sge *sgl; OCE_DMA_MEM dma; uint32_t desc_count = 0; struct oce_nic_resc_desc *nic_desc = NULL; int i; boolean_t nic_desc_valid = FALSE; uint32_t max_rss = 0; if ((IS_BE(sc) || IS_SH(sc)) && (!sc->be3_native)) max_rss = OCE_LEGACY_MODE_RSS; else max_rss = OCE_MAX_RSS; /* Allocate DMA mem*/ if (oce_dma_alloc(sc, sizeof(struct mbx_common_get_func_config), &dma, 0)) return ENOMEM; /* Initialize MODIFY_EQ_DELAY ioctl header */ fwcmd = OCE_DMAPTR(&dma, struct mbx_common_get_func_config); bzero(fwcmd, sizeof(struct mbx_common_get_func_config)); if (IS_SH(sc)) version = OCE_MBX_VER_V1; else version = OCE_MBX_VER_V0; bzero(&mbx, sizeof(struct oce_mbx)); mbx_common_req_hdr_init(&fwcmd->hdr, 0, 0, MBX_SUBSYSTEM_COMMON, OPCODE_COMMON_GET_FUNCTION_CONFIG, MBX_TIMEOUT_SEC, sizeof(struct mbx_common_get_func_config), version); /* fill rest of mbx */ mbx.u0.s.embedded = 0; mbx.payload_length = sizeof(struct mbx_common_get_func_config); mbx.u0.s.sge_count = 1; sgl = &mbx.payload.u0.u1.sgl[0]; sgl->pa_hi = htole32(upper_32_bits(dma.paddr)); sgl->pa_lo = htole32((dma.paddr) & 0xFFFFFFFF); sgl->length = htole32(mbx.payload_length); DW_SWAP(u32ptr(&mbx), mbx.payload_length + OCE_BMBX_RHDR_SZ); /* command post */ rc = oce_mbox_post(sc, &mbx, NULL); if (!rc) rc = fwcmd->hdr.u0.rsp.status; if (rc) { device_printf(sc->dev, "%s failed - cmd status: %d addi status: %d\n", __FUNCTION__, rc, fwcmd->hdr.u0.rsp.additional_status); goto error; } nic_desc = (struct oce_nic_resc_desc *) fwcmd->params.rsp.resources; desc_count = HOST_32(fwcmd->params.rsp.desc_count); for (i = 0; i < desc_count; i++) { if ((nic_desc->desc_type == NIC_RESC_DESC_TYPE_V0) || (nic_desc->desc_type == NIC_RESC_DESC_TYPE_V1)) { nic_desc_valid = TRUE; break; } nic_desc = (struct oce_nic_resc_desc *) \ ((char *)nic_desc + nic_desc->desc_len); } if (!nic_desc_valid) { rc = -1; goto error; } else { sc->max_vlans = nic_desc->vlan_count; sc->nwqs = HOST_32(nic_desc->txq_count); if (sc->nwqs) sc->nwqs = MIN(sc->nwqs, OCE_MAX_WQ); else sc->nwqs = OCE_MAX_WQ; sc->nrssqs = HOST_32(nic_desc->rssq_count); if (sc->nrssqs) sc->nrssqs = MIN(sc->nrssqs, max_rss); else sc->nrssqs = max_rss; sc->nrqs = sc->nrssqs + 1; /* 1 for def RX */; } error: oce_dma_free(sc, &dma); return rc; } Index: projects/building-blocks/sys/dev/smc/if_smc.c =================================================================== --- projects/building-blocks/sys/dev/smc/if_smc.c (revision 278776) +++ projects/building-blocks/sys/dev/smc/if_smc.c (revision 278777) @@ -1,1309 +1,1328 @@ /*- * Copyright (c) 2008 Benno Rice. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 __FBSDID("$FreeBSD$"); /* * Driver for SMSC LAN91C111, may work for older variants. */ #ifdef HAVE_KERNEL_OPTION_HEADERS #include "opt_device_polling.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #include #include #endif #include #include #include #include #include #include #include #define SMC_LOCK(sc) mtx_lock(&(sc)->smc_mtx) #define SMC_UNLOCK(sc) mtx_unlock(&(sc)->smc_mtx) #define SMC_ASSERT_LOCKED(sc) mtx_assert(&(sc)->smc_mtx, MA_OWNED) #define SMC_INTR_PRIORITY 0 #define SMC_RX_PRIORITY 5 #define SMC_TX_PRIORITY 10 devclass_t smc_devclass; static const char *smc_chip_ids[16] = { NULL, NULL, NULL, /* 3 */ "SMSC LAN91C90 or LAN91C92", /* 4 */ "SMSC LAN91C94", /* 5 */ "SMSC LAN91C95", /* 6 */ "SMSC LAN91C96", /* 7 */ "SMSC LAN91C100", /* 8 */ "SMSC LAN91C100FD", /* 9 */ "SMSC LAN91C110FD or LAN91C111FD", NULL, NULL, NULL, NULL, NULL, NULL }; static void smc_init(void *); static void smc_start(struct ifnet *); static void smc_stop(struct smc_softc *); static int smc_ioctl(struct ifnet *, u_long, caddr_t); static void smc_init_locked(struct smc_softc *); static void smc_start_locked(struct ifnet *); static void smc_reset(struct smc_softc *); static int smc_mii_ifmedia_upd(struct ifnet *); static void smc_mii_ifmedia_sts(struct ifnet *, struct ifmediareq *); static void smc_mii_tick(void *); static void smc_mii_mediachg(struct smc_softc *); static int smc_mii_mediaioctl(struct smc_softc *, struct ifreq *, u_long); static void smc_task_intr(void *, int); static void smc_task_rx(void *, int); static void smc_task_tx(void *, int); static driver_filter_t smc_intr; static timeout_t smc_watchdog; #ifdef DEVICE_POLLING static poll_handler_t smc_poll; #endif /* * MII bit-bang glue */ static uint32_t smc_mii_bitbang_read(device_t); static void smc_mii_bitbang_write(device_t, uint32_t); static const struct mii_bitbang_ops smc_mii_bitbang_ops = { smc_mii_bitbang_read, smc_mii_bitbang_write, { MGMT_MDO, /* MII_BIT_MDO */ MGMT_MDI, /* MII_BIT_MDI */ MGMT_MCLK, /* MII_BIT_MDC */ MGMT_MDOE, /* MII_BIT_DIR_HOST_PHY */ 0, /* MII_BIT_DIR_PHY_HOST */ } }; static __inline void smc_select_bank(struct smc_softc *sc, uint16_t bank) { bus_barrier(sc->smc_reg, BSR, 2, BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); bus_write_2(sc->smc_reg, BSR, bank & BSR_BANK_MASK); bus_barrier(sc->smc_reg, BSR, 2, BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); } /* Never call this when not in bank 2. */ static __inline void smc_mmu_wait(struct smc_softc *sc) { KASSERT((bus_read_2(sc->smc_reg, BSR) & BSR_BANK_MASK) == 2, ("%s: smc_mmu_wait called when not in bank 2", device_get_nameunit(sc->smc_dev))); while (bus_read_2(sc->smc_reg, MMUCR) & MMUCR_BUSY) ; } static __inline uint8_t smc_read_1(struct smc_softc *sc, bus_size_t offset) { return (bus_read_1(sc->smc_reg, offset)); } static __inline void smc_write_1(struct smc_softc *sc, bus_size_t offset, uint8_t val) { bus_write_1(sc->smc_reg, offset, val); } static __inline uint16_t smc_read_2(struct smc_softc *sc, bus_size_t offset) { return (bus_read_2(sc->smc_reg, offset)); } static __inline void smc_write_2(struct smc_softc *sc, bus_size_t offset, uint16_t val) { bus_write_2(sc->smc_reg, offset, val); } static __inline void smc_read_multi_2(struct smc_softc *sc, bus_size_t offset, uint16_t *datap, bus_size_t count) { bus_read_multi_2(sc->smc_reg, offset, datap, count); } static __inline void smc_write_multi_2(struct smc_softc *sc, bus_size_t offset, uint16_t *datap, bus_size_t count) { bus_write_multi_2(sc->smc_reg, offset, datap, count); } static __inline void smc_barrier(struct smc_softc *sc, bus_size_t offset, bus_size_t length, int flags) { bus_barrier(sc->smc_reg, offset, length, flags); } int smc_probe(device_t dev) { int rid, type, error; uint16_t val; struct smc_softc *sc; struct resource *reg; sc = device_get_softc(dev); rid = 0; type = SYS_RES_IOPORT; error = 0; if (sc->smc_usemem) type = SYS_RES_MEMORY; reg = bus_alloc_resource(dev, type, &rid, 0, ~0, 16, RF_ACTIVE); if (reg == NULL) { if (bootverbose) device_printf(dev, "could not allocate I/O resource for probe\n"); return (ENXIO); } /* Check for the identification value in the BSR. */ val = bus_read_2(reg, BSR); if ((val & BSR_IDENTIFY_MASK) != BSR_IDENTIFY) { if (bootverbose) device_printf(dev, "identification value not in BSR\n"); error = ENXIO; goto done; } /* * Try switching banks and make sure we still get the identification * value. */ bus_write_2(reg, BSR, 0); val = bus_read_2(reg, BSR); if ((val & BSR_IDENTIFY_MASK) != BSR_IDENTIFY) { if (bootverbose) device_printf(dev, "identification value not in BSR after write\n"); error = ENXIO; goto done; } #if 0 /* Check the BAR. */ bus_write_2(reg, BSR, 1); val = bus_read_2(reg, BAR); val = BAR_ADDRESS(val); if (rman_get_start(reg) != val) { if (bootverbose) device_printf(dev, "BAR address %x does not match " "I/O resource address %lx\n", val, rman_get_start(reg)); error = ENXIO; goto done; } #endif /* Compare REV against known chip revisions. */ bus_write_2(reg, BSR, 3); val = bus_read_2(reg, REV); val = (val & REV_CHIP_MASK) >> REV_CHIP_SHIFT; if (smc_chip_ids[val] == NULL) { if (bootverbose) device_printf(dev, "Unknown chip revision: %d\n", val); error = ENXIO; goto done; } device_set_desc(dev, smc_chip_ids[val]); done: bus_release_resource(dev, type, rid, reg); return (error); } int smc_attach(device_t dev) { int type, error; uint16_t val; u_char eaddr[ETHER_ADDR_LEN]; struct smc_softc *sc; struct ifnet *ifp; sc = device_get_softc(dev); error = 0; sc->smc_dev = dev; ifp = sc->smc_ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { error = ENOSPC; goto done; } mtx_init(&sc->smc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); /* Set up watchdog callout. */ callout_init_mtx(&sc->smc_watchdog, &sc->smc_mtx, 0); type = SYS_RES_IOPORT; if (sc->smc_usemem) type = SYS_RES_MEMORY; sc->smc_reg_rid = 0; sc->smc_reg = bus_alloc_resource(dev, type, &sc->smc_reg_rid, 0, ~0, 16, RF_ACTIVE); if (sc->smc_reg == NULL) { error = ENXIO; goto done; } sc->smc_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->smc_irq_rid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); if (sc->smc_irq == NULL) { error = ENXIO; goto done; } SMC_LOCK(sc); smc_reset(sc); SMC_UNLOCK(sc); smc_select_bank(sc, 3); val = smc_read_2(sc, REV); sc->smc_chip = (val & REV_CHIP_MASK) >> REV_CHIP_SHIFT; sc->smc_rev = (val * REV_REV_MASK) >> REV_REV_SHIFT; if (bootverbose) device_printf(dev, "revision %x\n", sc->smc_rev); callout_init_mtx(&sc->smc_mii_tick_ch, &sc->smc_mtx, CALLOUT_RETURNUNLOCKED); if (sc->smc_chip >= REV_CHIP_91110FD) { (void)mii_attach(dev, &sc->smc_miibus, ifp, smc_mii_ifmedia_upd, smc_mii_ifmedia_sts, BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, 0); if (sc->smc_miibus != NULL) { sc->smc_mii_tick = smc_mii_tick; sc->smc_mii_mediachg = smc_mii_mediachg; sc->smc_mii_mediaioctl = smc_mii_mediaioctl; } } smc_select_bank(sc, 1); eaddr[0] = smc_read_1(sc, IAR0); eaddr[1] = smc_read_1(sc, IAR1); eaddr[2] = smc_read_1(sc, IAR2); eaddr[3] = smc_read_1(sc, IAR3); eaddr[4] = smc_read_1(sc, IAR4); eaddr[5] = smc_read_1(sc, IAR5); if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_softc = sc; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_init = smc_init; ifp->if_ioctl = smc_ioctl; ifp->if_start = smc_start; IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); IFQ_SET_READY(&ifp->if_snd); ifp->if_capabilities = ifp->if_capenable = 0; #ifdef DEVICE_POLLING ifp->if_capabilities |= IFCAP_POLLING; #endif ether_ifattach(ifp, eaddr); /* Set up taskqueue */ TASK_INIT(&sc->smc_intr, SMC_INTR_PRIORITY, smc_task_intr, ifp); TASK_INIT(&sc->smc_rx, SMC_RX_PRIORITY, smc_task_rx, ifp); TASK_INIT(&sc->smc_tx, SMC_TX_PRIORITY, smc_task_tx, ifp); sc->smc_tq = taskqueue_create_fast("smc_taskq", M_NOWAIT, taskqueue_thread_enqueue, &sc->smc_tq); taskqueue_start_threads(&sc->smc_tq, 1, PI_NET, "%s taskq", device_get_nameunit(sc->smc_dev)); /* Mask all interrupts. */ sc->smc_mask = 0; smc_write_1(sc, MSK, 0); /* Wire up interrupt */ error = bus_setup_intr(dev, sc->smc_irq, INTR_TYPE_NET|INTR_MPSAFE, smc_intr, NULL, sc, &sc->smc_ih); if (error != 0) goto done; done: if (error != 0) smc_detach(dev); return (error); } int smc_detach(device_t dev) { int type; struct smc_softc *sc; sc = device_get_softc(dev); SMC_LOCK(sc); smc_stop(sc); SMC_UNLOCK(sc); if (sc->smc_ifp != NULL) { ether_ifdetach(sc->smc_ifp); } callout_drain(&sc->smc_watchdog); callout_drain(&sc->smc_mii_tick_ch); #ifdef DEVICE_POLLING if (sc->smc_ifp->if_capenable & IFCAP_POLLING) ether_poll_deregister(sc->smc_ifp); #endif if (sc->smc_ih != NULL) bus_teardown_intr(sc->smc_dev, sc->smc_irq, sc->smc_ih); if (sc->smc_tq != NULL) { taskqueue_drain(sc->smc_tq, &sc->smc_intr); taskqueue_drain(sc->smc_tq, &sc->smc_rx); taskqueue_drain(sc->smc_tq, &sc->smc_tx); taskqueue_free(sc->smc_tq); sc->smc_tq = NULL; } if (sc->smc_ifp != NULL) { if_free(sc->smc_ifp); } if (sc->smc_miibus != NULL) { device_delete_child(sc->smc_dev, sc->smc_miibus); bus_generic_detach(sc->smc_dev); } if (sc->smc_reg != NULL) { type = SYS_RES_IOPORT; if (sc->smc_usemem) type = SYS_RES_MEMORY; bus_release_resource(sc->smc_dev, type, sc->smc_reg_rid, sc->smc_reg); } if (sc->smc_irq != NULL) bus_release_resource(sc->smc_dev, SYS_RES_IRQ, sc->smc_irq_rid, sc->smc_irq); if (mtx_initialized(&sc->smc_mtx)) mtx_destroy(&sc->smc_mtx); return (0); } static void smc_start(struct ifnet *ifp) { struct smc_softc *sc; sc = ifp->if_softc; SMC_LOCK(sc); smc_start_locked(ifp); SMC_UNLOCK(sc); } static void smc_start_locked(struct ifnet *ifp) { struct smc_softc *sc; struct mbuf *m; u_int len, npages, spin_count; sc = ifp->if_softc; SMC_ASSERT_LOCKED(sc); if (ifp->if_drv_flags & IFF_DRV_OACTIVE) return; if (IFQ_IS_EMPTY(&ifp->if_snd)) return; /* * Grab the next packet. If it's too big, drop it. */ IFQ_DRV_DEQUEUE(&ifp->if_snd, m); len = m_length(m, NULL); len += (len & 1); if (len > ETHER_MAX_LEN - ETHER_CRC_LEN) { if_printf(ifp, "large packet discarded\n"); if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); m_freem(m); return; /* XXX readcheck? */ } /* * Flag that we're busy. */ ifp->if_drv_flags |= IFF_DRV_OACTIVE; sc->smc_pending = m; /* * Work out how many 256 byte "pages" we need. We have to include the * control data for the packet in this calculation. */ - npages = (len * PKT_CTRL_DATA_LEN) >> 8; + npages = (len + PKT_CTRL_DATA_LEN) >> 8; if (npages == 0) npages = 1; /* * Request memory. */ smc_select_bank(sc, 2); smc_mmu_wait(sc); smc_write_2(sc, MMUCR, MMUCR_CMD_TX_ALLOC | npages); /* * Spin briefly to see if the allocation succeeds. */ spin_count = TX_ALLOC_WAIT_TIME; do { if (smc_read_1(sc, IST) & ALLOC_INT) { smc_write_1(sc, ACK, ALLOC_INT); break; } } while (--spin_count); /* * If the allocation is taking too long, unmask the alloc interrupt * and wait. */ if (spin_count == 0) { sc->smc_mask |= ALLOC_INT; if ((ifp->if_capenable & IFCAP_POLLING) == 0) smc_write_1(sc, MSK, sc->smc_mask); return; } taskqueue_enqueue_fast(sc->smc_tq, &sc->smc_tx); } static void smc_task_tx(void *context, int pending) { struct ifnet *ifp; struct smc_softc *sc; struct mbuf *m, *m0; u_int packet, len; int last_len; uint8_t *data; (void)pending; ifp = (struct ifnet *)context; sc = ifp->if_softc; SMC_LOCK(sc); if (sc->smc_pending == NULL) { SMC_UNLOCK(sc); goto next_packet; } m = m0 = sc->smc_pending; sc->smc_pending = NULL; smc_select_bank(sc, 2); /* * Check the allocation result. */ packet = smc_read_1(sc, ARR); /* * If the allocation failed, requeue the packet and retry. */ if (packet & ARR_FAILED) { IFQ_DRV_PREPEND(&ifp->if_snd, m); if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; smc_start_locked(ifp); SMC_UNLOCK(sc); return; } /* * Tell the device to write to our packet number. */ smc_write_1(sc, PNR, packet); smc_write_2(sc, PTR, 0 | PTR_AUTO_INCR); /* * Tell the device how long the packet is (including control data). */ len = m_length(m, 0); len += PKT_CTRL_DATA_LEN; smc_write_2(sc, DATA0, 0); smc_write_2(sc, DATA0, len); /* * Push the data out to the device. */ data = NULL; last_len = 0; for (; m != NULL; m = m->m_next) { data = mtod(m, uint8_t *); smc_write_multi_2(sc, DATA0, (uint16_t *)data, m->m_len / 2); last_len = m->m_len; } /* * Push out the control byte and and the odd byte if needed. */ if ((len & 1) != 0 && data != NULL) smc_write_2(sc, DATA0, (CTRL_ODD << 8) | data[last_len - 1]); else smc_write_2(sc, DATA0, 0); /* * Unmask the TX empty interrupt. */ sc->smc_mask |= TX_EMPTY_INT; if ((ifp->if_capenable & IFCAP_POLLING) == 0) smc_write_1(sc, MSK, sc->smc_mask); /* * Enqueue the packet. */ smc_mmu_wait(sc); smc_write_2(sc, MMUCR, MMUCR_CMD_ENQUEUE); callout_reset(&sc->smc_watchdog, hz * 2, smc_watchdog, sc); /* * Finish up. */ if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; SMC_UNLOCK(sc); BPF_MTAP(ifp, m0); m_freem(m0); next_packet: /* * See if there's anything else to do. */ smc_start(ifp); } static void smc_task_rx(void *context, int pending) { u_int packet, status, len; uint8_t *data; struct ifnet *ifp; struct smc_softc *sc; struct mbuf *m, *mhead, *mtail; (void)pending; ifp = (struct ifnet *)context; sc = ifp->if_softc; mhead = mtail = NULL; SMC_LOCK(sc); packet = smc_read_1(sc, FIFO_RX); while ((packet & FIFO_EMPTY) == 0) { /* * Grab an mbuf and attach a cluster. */ MGETHDR(m, M_NOWAIT, MT_DATA); if (m == NULL) { break; } if (!(MCLGET(m, M_NOWAIT))) { m_freem(m); break; } /* * Point to the start of the packet. */ smc_select_bank(sc, 2); smc_write_1(sc, PNR, packet); smc_write_2(sc, PTR, 0 | PTR_READ | PTR_RCV | PTR_AUTO_INCR); /* * Grab status and packet length. */ status = smc_read_2(sc, DATA0); len = smc_read_2(sc, DATA0) & RX_LEN_MASK; len -= 6; if (status & RX_ODDFRM) len += 1; /* * Check for errors. */ if (status & (RX_TOOSHORT | RX_TOOLNG | RX_BADCRC | RX_ALGNERR)) { smc_mmu_wait(sc); smc_write_2(sc, MMUCR, MMUCR_CMD_RELEASE); if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); m_freem(m); break; } /* * Set the mbuf up the way we want it. */ m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = len + 2; /* XXX: Is this right? */ m_adj(m, ETHER_ALIGN); /* * Pull the packet out of the device. Make sure we're in the * right bank first as things may have changed while we were * allocating our mbuf. */ smc_select_bank(sc, 2); smc_write_1(sc, PNR, packet); smc_write_2(sc, PTR, 4 | PTR_READ | PTR_RCV | PTR_AUTO_INCR); data = mtod(m, uint8_t *); smc_read_multi_2(sc, DATA0, (uint16_t *)data, len >> 1); if (len & 1) { data += len & ~1; *data = smc_read_1(sc, DATA0); } /* * Tell the device we're done. */ smc_mmu_wait(sc); smc_write_2(sc, MMUCR, MMUCR_CMD_RELEASE); if (m == NULL) { break; } if (mhead == NULL) { mhead = mtail = m; m->m_next = NULL; } else { mtail->m_next = m; mtail = m; } packet = smc_read_1(sc, FIFO_RX); } sc->smc_mask |= RCV_INT; if ((ifp->if_capenable & IFCAP_POLLING) == 0) smc_write_1(sc, MSK, sc->smc_mask); SMC_UNLOCK(sc); while (mhead != NULL) { m = mhead; mhead = mhead->m_next; m->m_next = NULL; if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); (*ifp->if_input)(ifp, m); } } #ifdef DEVICE_POLLING static void smc_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) { struct smc_softc *sc; sc = ifp->if_softc; SMC_LOCK(sc); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { SMC_UNLOCK(sc); return; } SMC_UNLOCK(sc); if (cmd == POLL_AND_CHECK_STATUS) taskqueue_enqueue_fast(sc->smc_tq, &sc->smc_intr); } #endif static int smc_intr(void *context) { struct smc_softc *sc; - + uint32_t curbank; + sc = (struct smc_softc *)context; + /* + * Save current bank and restore later in this function + */ + curbank = (smc_read_2(sc, BSR) & BSR_BANK_MASK); + + /* * Block interrupts in order to let smc_task_intr to kick in */ + smc_select_bank(sc, 2); smc_write_1(sc, MSK, 0); + + /* Restore bank */ + smc_select_bank(sc, curbank); + taskqueue_enqueue_fast(sc->smc_tq, &sc->smc_intr); return (FILTER_HANDLED); } static void smc_task_intr(void *context, int pending) { struct smc_softc *sc; struct ifnet *ifp; u_int status, packet, counter, tcr; (void)pending; ifp = (struct ifnet *)context; sc = ifp->if_softc; SMC_LOCK(sc); smc_select_bank(sc, 2); /* * Find out what interrupts are flagged. */ status = smc_read_1(sc, IST) & sc->smc_mask; /* * Transmit error */ if (status & TX_INT) { /* * Kill off the packet if there is one and re-enable transmit. */ packet = smc_read_1(sc, FIFO_TX); if ((packet & FIFO_EMPTY) == 0) { + callout_stop(&sc->smc_watchdog); + smc_select_bank(sc, 2); smc_write_1(sc, PNR, packet); smc_write_2(sc, PTR, 0 | PTR_READ | PTR_AUTO_INCR); - tcr = smc_read_2(sc, DATA0); + smc_select_bank(sc, 0); + tcr = smc_read_2(sc, EPHSR); +#if 0 if ((tcr & EPHSR_TX_SUC) == 0) device_printf(sc->smc_dev, "bad packet\n"); +#endif + smc_select_bank(sc, 2); smc_mmu_wait(sc); smc_write_2(sc, MMUCR, MMUCR_CMD_RELEASE_PKT); smc_select_bank(sc, 0); tcr = smc_read_2(sc, TCR); tcr |= TCR_TXENA | TCR_PAD_EN; smc_write_2(sc, TCR, tcr); smc_select_bank(sc, 2); taskqueue_enqueue_fast(sc->smc_tq, &sc->smc_tx); } /* * Ack the interrupt. */ smc_write_1(sc, ACK, TX_INT); } /* * Receive */ if (status & RCV_INT) { smc_write_1(sc, ACK, RCV_INT); sc->smc_mask &= ~RCV_INT; taskqueue_enqueue_fast(sc->smc_tq, &sc->smc_rx); } /* * Allocation */ if (status & ALLOC_INT) { smc_write_1(sc, ACK, ALLOC_INT); sc->smc_mask &= ~ALLOC_INT; taskqueue_enqueue_fast(sc->smc_tq, &sc->smc_tx); } /* * Receive overrun */ if (status & RX_OVRN_INT) { smc_write_1(sc, ACK, RX_OVRN_INT); if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); } /* * Transmit empty */ if (status & TX_EMPTY_INT) { smc_write_1(sc, ACK, TX_EMPTY_INT); sc->smc_mask &= ~TX_EMPTY_INT; callout_stop(&sc->smc_watchdog); /* * Update collision stats. */ smc_select_bank(sc, 0); counter = smc_read_2(sc, ECR); smc_select_bank(sc, 2); if_inc_counter(ifp, IFCOUNTER_COLLISIONS, ((counter & ECR_SNGLCOL_MASK) >> ECR_SNGLCOL_SHIFT) + ((counter & ECR_MULCOL_MASK) >> ECR_MULCOL_SHIFT)); /* * See if there are any packets to transmit. */ taskqueue_enqueue_fast(sc->smc_tq, &sc->smc_tx); } /* * Update the interrupt mask. */ + smc_select_bank(sc, 2); if ((ifp->if_capenable & IFCAP_POLLING) == 0) smc_write_1(sc, MSK, sc->smc_mask); SMC_UNLOCK(sc); } static uint32_t smc_mii_bitbang_read(device_t dev) { struct smc_softc *sc; uint32_t val; sc = device_get_softc(dev); SMC_ASSERT_LOCKED(sc); KASSERT((smc_read_2(sc, BSR) & BSR_BANK_MASK) == 3, ("%s: smc_mii_bitbang_read called with bank %d (!= 3)", device_get_nameunit(sc->smc_dev), smc_read_2(sc, BSR) & BSR_BANK_MASK)); val = smc_read_2(sc, MGMT); smc_barrier(sc, MGMT, 2, BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); return (val); } static void smc_mii_bitbang_write(device_t dev, uint32_t val) { struct smc_softc *sc; sc = device_get_softc(dev); SMC_ASSERT_LOCKED(sc); KASSERT((smc_read_2(sc, BSR) & BSR_BANK_MASK) == 3, ("%s: smc_mii_bitbang_write called with bank %d (!= 3)", device_get_nameunit(sc->smc_dev), smc_read_2(sc, BSR) & BSR_BANK_MASK)); smc_write_2(sc, MGMT, val); smc_barrier(sc, MGMT, 2, BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); } int smc_miibus_readreg(device_t dev, int phy, int reg) { struct smc_softc *sc; int val; sc = device_get_softc(dev); SMC_LOCK(sc); smc_select_bank(sc, 3); val = mii_bitbang_readreg(dev, &smc_mii_bitbang_ops, phy, reg); SMC_UNLOCK(sc); return (val); } int smc_miibus_writereg(device_t dev, int phy, int reg, int data) { struct smc_softc *sc; sc = device_get_softc(dev); SMC_LOCK(sc); smc_select_bank(sc, 3); mii_bitbang_writereg(dev, &smc_mii_bitbang_ops, phy, reg, data); SMC_UNLOCK(sc); return (0); } void smc_miibus_statchg(device_t dev) { struct smc_softc *sc; struct mii_data *mii; uint16_t tcr; sc = device_get_softc(dev); mii = device_get_softc(sc->smc_miibus); SMC_LOCK(sc); smc_select_bank(sc, 0); tcr = smc_read_2(sc, TCR); if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) tcr |= TCR_SWFDUP; else tcr &= ~TCR_SWFDUP; smc_write_2(sc, TCR, tcr); SMC_UNLOCK(sc); } static int smc_mii_ifmedia_upd(struct ifnet *ifp) { struct smc_softc *sc; struct mii_data *mii; sc = ifp->if_softc; if (sc->smc_miibus == NULL) return (ENXIO); mii = device_get_softc(sc->smc_miibus); return (mii_mediachg(mii)); } static void smc_mii_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { struct smc_softc *sc; struct mii_data *mii; sc = ifp->if_softc; if (sc->smc_miibus == NULL) return; mii = device_get_softc(sc->smc_miibus); mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; } static void smc_mii_tick(void *context) { struct smc_softc *sc; sc = (struct smc_softc *)context; if (sc->smc_miibus == NULL) return; SMC_UNLOCK(sc); mii_tick(device_get_softc(sc->smc_miibus)); callout_reset(&sc->smc_mii_tick_ch, hz, smc_mii_tick, sc); } static void smc_mii_mediachg(struct smc_softc *sc) { if (sc->smc_miibus == NULL) return; mii_mediachg(device_get_softc(sc->smc_miibus)); } static int smc_mii_mediaioctl(struct smc_softc *sc, struct ifreq *ifr, u_long command) { struct mii_data *mii; if (sc->smc_miibus == NULL) return (EINVAL); mii = device_get_softc(sc->smc_miibus); return (ifmedia_ioctl(sc->smc_ifp, ifr, &mii->mii_media, command)); } static void smc_reset(struct smc_softc *sc) { u_int ctr; SMC_ASSERT_LOCKED(sc); smc_select_bank(sc, 2); /* * Mask all interrupts. */ smc_write_1(sc, MSK, 0); /* * Tell the device to reset. */ smc_select_bank(sc, 0); smc_write_2(sc, RCR, RCR_SOFT_RST); /* * Set up the configuration register. */ smc_select_bank(sc, 1); smc_write_2(sc, CR, CR_EPH_POWER_EN); DELAY(1); /* * Turn off transmit and receive. */ smc_select_bank(sc, 0); smc_write_2(sc, TCR, 0); smc_write_2(sc, RCR, 0); /* * Set up the control register. */ smc_select_bank(sc, 1); ctr = smc_read_2(sc, CTR); ctr |= CTR_LE_ENABLE | CTR_AUTO_RELEASE; smc_write_2(sc, CTR, ctr); /* * Reset the MMU. */ smc_select_bank(sc, 2); smc_mmu_wait(sc); smc_write_2(sc, MMUCR, MMUCR_CMD_MMU_RESET); } static void smc_enable(struct smc_softc *sc) { struct ifnet *ifp; SMC_ASSERT_LOCKED(sc); ifp = sc->smc_ifp; /* * Set up the receive/PHY control register. */ smc_select_bank(sc, 0); smc_write_2(sc, RPCR, RPCR_ANEG | (RPCR_LED_LINK_ANY << RPCR_LSA_SHIFT) | (RPCR_LED_ACT_ANY << RPCR_LSB_SHIFT)); /* * Set up the transmit and receive control registers. */ smc_write_2(sc, TCR, TCR_TXENA | TCR_PAD_EN); smc_write_2(sc, RCR, RCR_RXEN | RCR_STRIP_CRC); /* * Set up the interrupt mask. */ smc_select_bank(sc, 2); sc->smc_mask = EPH_INT | RX_OVRN_INT | RCV_INT | TX_INT; if ((ifp->if_capenable & IFCAP_POLLING) != 0) smc_write_1(sc, MSK, sc->smc_mask); } static void smc_stop(struct smc_softc *sc) { SMC_ASSERT_LOCKED(sc); /* * Turn off callouts. */ callout_stop(&sc->smc_watchdog); callout_stop(&sc->smc_mii_tick_ch); /* * Mask all interrupts. */ smc_select_bank(sc, 2); sc->smc_mask = 0; smc_write_1(sc, MSK, 0); #ifdef DEVICE_POLLING ether_poll_deregister(sc->smc_ifp); sc->smc_ifp->if_capenable &= ~IFCAP_POLLING; sc->smc_ifp->if_capenable &= ~IFCAP_POLLING_NOCOUNT; #endif /* * Disable transmit and receive. */ smc_select_bank(sc, 0); smc_write_2(sc, TCR, 0); smc_write_2(sc, RCR, 0); sc->smc_ifp->if_drv_flags &= ~IFF_DRV_RUNNING; } static void smc_watchdog(void *arg) { struct smc_softc *sc; sc = (struct smc_softc *)arg; device_printf(sc->smc_dev, "watchdog timeout\n"); taskqueue_enqueue_fast(sc->smc_tq, &sc->smc_intr); } static void smc_init(void *context) { struct smc_softc *sc; sc = (struct smc_softc *)context; SMC_LOCK(sc); smc_init_locked(sc); SMC_UNLOCK(sc); } static void smc_init_locked(struct smc_softc *sc) { struct ifnet *ifp; SMC_ASSERT_LOCKED(sc); ifp = sc->smc_ifp; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) return; smc_reset(sc); smc_enable(sc); ifp->if_drv_flags |= IFF_DRV_RUNNING; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; smc_start_locked(ifp); if (sc->smc_mii_tick != NULL) callout_reset(&sc->smc_mii_tick_ch, hz, sc->smc_mii_tick, sc); #ifdef DEVICE_POLLING SMC_UNLOCK(sc); ether_poll_register(smc_poll, ifp); SMC_LOCK(sc); ifp->if_capenable |= IFCAP_POLLING; ifp->if_capenable |= IFCAP_POLLING_NOCOUNT; #endif } static int smc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct smc_softc *sc; int error; sc = ifp->if_softc; error = 0; switch (cmd) { case SIOCSIFFLAGS: if ((ifp->if_flags & IFF_UP) == 0 && (ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { SMC_LOCK(sc); smc_stop(sc); SMC_UNLOCK(sc); } else { smc_init(sc); if (sc->smc_mii_mediachg != NULL) sc->smc_mii_mediachg(sc); } break; case SIOCADDMULTI: case SIOCDELMULTI: /* XXX SMC_LOCK(sc); smc_setmcast(sc); SMC_UNLOCK(sc); */ error = EINVAL; break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: if (sc->smc_mii_mediaioctl == NULL) { error = EINVAL; break; } sc->smc_mii_mediaioctl(sc, (struct ifreq *)data, cmd); break; default: error = ether_ioctl(ifp, cmd, data); break; } return (error); } Index: projects/building-blocks/sys/dev/vt/font/vt_mouse_cursor.c =================================================================== --- projects/building-blocks/sys/dev/vt/font/vt_mouse_cursor.c (revision 278776) +++ projects/building-blocks/sys/dev/vt/font/vt_mouse_cursor.c (revision 278777) @@ -1,70 +1,76 @@ /*- * Copyright (c) 2013 The FreeBSD Foundation * All rights reserved. * * This software was developed by Aleksandr Rybalko under sponsorship from the * FreeBSD Foundation. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 __FBSDID("$FreeBSD$"); #include #ifndef SC_NO_CUTPASTE struct vt_mouse_cursor vt_default_mouse_pointer = { .map = { - 0x00, /* "__ " */ - 0x40, /* "_*_ " */ - 0x60, /* "_**_ " */ - 0x70, /* "_***_ " */ - 0x78, /* "_****_ " */ - 0x7c, /* "_*****_ " */ - 0x7e, /* "_******_" */ - 0x68, /* "_**_****" */ - 0x4c, /* "_*__**_ " */ - 0x0c, /* " _ _**_ " */ - 0x06, /* " _**_" */ - 0x06, /* " _**_" */ - 0x00, /* " ____" */ + 0x00, 0x00, /* "__ " */ + 0x40, 0x00, /* "_*_ " */ + 0x60, 0x00, /* "_**_ " */ + 0x70, 0x00, /* "_***_ " */ + 0x78, 0x00, /* "_****_ " */ + 0x7c, 0x00, /* "_*****_ " */ + 0x7e, 0x00, /* "_******_ " */ + 0x7f, 0x00, /* "_*******_ " */ + 0x7f, 0x80, /* "_********_" */ + 0x7c, 0x00, /* "_*****____" */ + 0x6c, 0x00, /* "_**_**_ " */ + 0x46, 0x00, /* "_*_ _**_ " */ + 0x06, 0x00, /* "__ _**_ " */ + 0x03, 0x00, /* " _**_ " */ + 0x03, 0x00, /* " _**_ " */ + 0x00, 0x00, /* " __ " */ }, .mask = { - 0xc0, /* "__ " */ - 0xe0, /* "___ " */ - 0xf0, /* "____ " */ - 0xf8, /* "_____ " */ - 0xfc, /* "______ " */ - 0xfe, /* "_______ " */ - 0xff, /* "________" */ - 0xff, /* "________" */ - 0xfe, /* "_______ " */ - 0x5e, /* " _ ____ " */ - 0x0f, /* " ____" */ - 0x0f, /* " ____" */ - 0x0f, /* " ____" */ + 0xc0, 0x00, /* "__ " */ + 0xe0, 0x00, /* "___ " */ + 0xf0, 0x00, /* "____ " */ + 0xf8, 0x00, /* "_____ " */ + 0xfc, 0x00, /* "______ " */ + 0xfe, 0x00, /* "_______ " */ + 0xff, 0x00, /* "________ " */ + 0xff, 0x80, /* "_________ " */ + 0xff, 0xc0, /* "__________" */ + 0xff, 0xc0, /* "__________" */ + 0xfe, 0x00, /* "_______ " */ + 0xef, 0x00, /* "___ ____ " */ + 0xcf, 0x00, /* "__ ____ " */ + 0x07, 0x80, /* " ____ " */ + 0x07, 0x80, /* " ____ " */ + 0x03, 0x00, /* " __ " */ }, - .width = 8, - .height = 13, + .width = 10, + .height = 16, }; #endif Index: projects/building-blocks/sys/dev/wpi/if_wpi.c =================================================================== --- projects/building-blocks/sys/dev/wpi/if_wpi.c (revision 278776) +++ projects/building-blocks/sys/dev/wpi/if_wpi.c (revision 278777) @@ -1,4883 +1,4885 @@ /*- * Copyright (c) 2006,2007 * Damien Bergamini * Benjamin Close * * Permission to use, copy, modify, and 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 __FBSDID("$FreeBSD$"); /* * Driver for Intel PRO/Wireless 3945ABG 802.11 network adapters. * * The 3945ABG network adapter doesn't use traditional hardware as * many other adaptors do. Instead at run time the eeprom is set into a known * state and told to load boot firmware. The boot firmware loads an init and a * main binary firmware image into SRAM on the card via DMA. * Once the firmware is loaded, the driver/hw then * communicate by way of circular dma rings via the SRAM to the firmware. * * There is 6 memory rings. 1 command ring, 1 rx data ring & 4 tx data rings. * The 4 tx data rings allow for prioritization QoS. * * The rx data ring consists of 32 dma buffers. Two registers are used to * indicate where in the ring the driver and the firmware are up to. The * driver sets the initial read index (reg1) and the initial write index (reg2), * the firmware updates the read index (reg1) on rx of a packet and fires an * interrupt. The driver then processes the buffers starting at reg1 indicating * to the firmware which buffers have been accessed by updating reg2. At the * same time allocating new memory for the processed buffer. * * A similar thing happens with the tx rings. The difference is the firmware * stop processing buffers once the queue is full and until confirmation - * of a successful transmition (tx_intr) has occurred. + * of a successful transmition (tx_done) has occurred. * * The command ring operates in the same manner as the tx queues. * * All communication direct to the card (ie eeprom) is classed as Stage1 * communication * * All communication via the firmware to the card is classed as State2. * The firmware consists of 2 parts. A bootstrap firmware and a runtime * firmware. The bootstrap firmware and runtime firmware are loaded * from host memory via dma to the card then told to execute. From this point * on the majority of communications between the driver and the card goes * via the firmware. */ #include "opt_wlan.h" #include "opt_wpi.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct wpi_ident { uint16_t vendor; uint16_t device; uint16_t subdevice; const char *name; }; static const struct wpi_ident wpi_ident_table[] = { /* The below entries support ABG regardless of the subid */ { 0x8086, 0x4222, 0x0, "Intel(R) PRO/Wireless 3945ABG" }, { 0x8086, 0x4227, 0x0, "Intel(R) PRO/Wireless 3945ABG" }, /* The below entries only support BG */ { 0x8086, 0x4222, 0x1005, "Intel(R) PRO/Wireless 3945BG" }, { 0x8086, 0x4222, 0x1034, "Intel(R) PRO/Wireless 3945BG" }, { 0x8086, 0x4227, 0x1014, "Intel(R) PRO/Wireless 3945BG" }, { 0x8086, 0x4222, 0x1044, "Intel(R) PRO/Wireless 3945BG" }, { 0, 0, 0, NULL } }; static int wpi_probe(device_t); static int wpi_attach(device_t); static void wpi_radiotap_attach(struct wpi_softc *); static void wpi_sysctlattach(struct wpi_softc *); static struct ieee80211vap *wpi_vap_create(struct ieee80211com *, const char [IFNAMSIZ], int, enum ieee80211_opmode, int, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN]); static void wpi_vap_delete(struct ieee80211vap *); static int wpi_detach(device_t); static int wpi_shutdown(device_t); static int wpi_suspend(device_t); static int wpi_resume(device_t); static int wpi_nic_lock(struct wpi_softc *); static int wpi_read_prom_data(struct wpi_softc *, uint32_t, void *, int); static void wpi_dma_map_addr(void *, bus_dma_segment_t *, int, int); static int wpi_dma_contig_alloc(struct wpi_softc *, struct wpi_dma_info *, void **, bus_size_t, bus_size_t); static void wpi_dma_contig_free(struct wpi_dma_info *); static int wpi_alloc_shared(struct wpi_softc *); static void wpi_free_shared(struct wpi_softc *); static int wpi_alloc_fwmem(struct wpi_softc *); static void wpi_free_fwmem(struct wpi_softc *); static int wpi_alloc_rx_ring(struct wpi_softc *); static void wpi_update_rx_ring(struct wpi_softc *); static void wpi_reset_rx_ring(struct wpi_softc *); static void wpi_free_rx_ring(struct wpi_softc *); static int wpi_alloc_tx_ring(struct wpi_softc *, struct wpi_tx_ring *, int); static void wpi_update_tx_ring(struct wpi_softc *, struct wpi_tx_ring *); static void wpi_reset_tx_ring(struct wpi_softc *, struct wpi_tx_ring *); static void wpi_free_tx_ring(struct wpi_softc *, struct wpi_tx_ring *); static int wpi_read_eeprom(struct wpi_softc *, uint8_t macaddr[IEEE80211_ADDR_LEN]); static uint32_t wpi_eeprom_channel_flags(struct wpi_eeprom_chan *); static void wpi_read_eeprom_band(struct wpi_softc *, int); static int wpi_read_eeprom_channels(struct wpi_softc *, int); static struct wpi_eeprom_chan *wpi_find_eeprom_channel(struct wpi_softc *, struct ieee80211_channel *); static int wpi_setregdomain(struct ieee80211com *, struct ieee80211_regdomain *, int, struct ieee80211_channel[]); static int wpi_read_eeprom_group(struct wpi_softc *, int); static void wpi_node_free(struct ieee80211_node *); static struct ieee80211_node *wpi_node_alloc(struct ieee80211vap *, const uint8_t mac[IEEE80211_ADDR_LEN]); static int wpi_newstate(struct ieee80211vap *, enum ieee80211_state, int); static void wpi_calib_timeout(void *); static void wpi_rx_done(struct wpi_softc *, struct wpi_rx_desc *, struct wpi_rx_data *); static void wpi_rx_statistics(struct wpi_softc *, struct wpi_rx_desc *, struct wpi_rx_data *); static void wpi_tx_done(struct wpi_softc *, struct wpi_rx_desc *); static void wpi_cmd_done(struct wpi_softc *, struct wpi_rx_desc *); static void wpi_notif_intr(struct wpi_softc *); static void wpi_wakeup_intr(struct wpi_softc *); static void wpi_fatal_intr(struct wpi_softc *); static void wpi_intr(void *); static int wpi_cmd2(struct wpi_softc *, struct wpi_buf *); static int wpi_tx_data(struct wpi_softc *, struct mbuf *, struct ieee80211_node *); static int wpi_tx_data_raw(struct wpi_softc *, struct mbuf *, struct ieee80211_node *, const struct ieee80211_bpf_params *); static int wpi_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static void wpi_start(struct ifnet *); static void wpi_start_locked(struct ifnet *); static void wpi_watchdog_rfkill(void *); static void wpi_watchdog(void *); static int wpi_ioctl(struct ifnet *, u_long, caddr_t); static int wpi_cmd(struct wpi_softc *, int, const void *, size_t, int); static int wpi_mrr_setup(struct wpi_softc *); static int wpi_add_node(struct wpi_softc *, struct ieee80211_node *); static int wpi_add_broadcast_node(struct wpi_softc *, int); static int wpi_add_ibss_node(struct wpi_softc *, struct ieee80211_node *); static void wpi_del_node(struct wpi_softc *, struct ieee80211_node *); static int wpi_updateedca(struct ieee80211com *); static void wpi_set_promisc(struct wpi_softc *); static void wpi_update_promisc(struct ifnet *); static void wpi_update_mcast(struct ifnet *); static void wpi_set_led(struct wpi_softc *, uint8_t, uint8_t, uint8_t); static int wpi_set_timing(struct wpi_softc *, struct ieee80211_node *); static void wpi_power_calibration(struct wpi_softc *); static int wpi_set_txpower(struct wpi_softc *, int); static int wpi_get_power_index(struct wpi_softc *, struct wpi_power_group *, struct ieee80211_channel *, int); static int wpi_set_pslevel(struct wpi_softc *, uint8_t, int, int); static int wpi_send_btcoex(struct wpi_softc *); static int wpi_send_rxon(struct wpi_softc *, int, int); static int wpi_config(struct wpi_softc *); static uint16_t wpi_get_active_dwell_time(struct wpi_softc *, struct ieee80211_channel *, uint8_t); static uint16_t wpi_limit_dwell(struct wpi_softc *, uint16_t); static uint16_t wpi_get_passive_dwell_time(struct wpi_softc *, struct ieee80211_channel *); static int wpi_scan(struct wpi_softc *, struct ieee80211_channel *); static int wpi_auth(struct wpi_softc *, struct ieee80211vap *); static void wpi_update_beacon(struct ieee80211vap *, int); static int wpi_setup_beacon(struct wpi_softc *, struct ieee80211_node *); static int wpi_run(struct wpi_softc *, struct ieee80211vap *); static int wpi_key_alloc(struct ieee80211vap *, struct ieee80211_key *, ieee80211_keyix *, ieee80211_keyix *); static int wpi_key_set(struct ieee80211vap *, const struct ieee80211_key *, const uint8_t mac[IEEE80211_ADDR_LEN]); static int wpi_key_delete(struct ieee80211vap *, const struct ieee80211_key *); static int wpi_post_alive(struct wpi_softc *); static int wpi_load_bootcode(struct wpi_softc *, const uint8_t *, int); static int wpi_load_firmware(struct wpi_softc *); static int wpi_read_firmware(struct wpi_softc *); static void wpi_unload_firmware(struct wpi_softc *); static int wpi_clock_wait(struct wpi_softc *); static int wpi_apm_init(struct wpi_softc *); static void wpi_apm_stop_master(struct wpi_softc *); static void wpi_apm_stop(struct wpi_softc *); static void wpi_nic_config(struct wpi_softc *); static int wpi_hw_init(struct wpi_softc *); static void wpi_hw_stop(struct wpi_softc *); static void wpi_radio_on(void *, int); static void wpi_radio_off(void *, int); static void wpi_init_locked(struct wpi_softc *); static void wpi_init(void *); static void wpi_stop_locked(struct wpi_softc *); static void wpi_stop(struct wpi_softc *); static void wpi_scan_start(struct ieee80211com *); static void wpi_scan_end(struct ieee80211com *); static void wpi_set_channel(struct ieee80211com *); static void wpi_scan_curchan(struct ieee80211_scan_state *, unsigned long); static void wpi_scan_mindwell(struct ieee80211_scan_state *); static void wpi_hw_reset(void *, int); static device_method_t wpi_methods[] = { /* Device interface */ DEVMETHOD(device_probe, wpi_probe), DEVMETHOD(device_attach, wpi_attach), DEVMETHOD(device_detach, wpi_detach), DEVMETHOD(device_shutdown, wpi_shutdown), DEVMETHOD(device_suspend, wpi_suspend), DEVMETHOD(device_resume, wpi_resume), DEVMETHOD_END }; static driver_t wpi_driver = { "wpi", wpi_methods, sizeof (struct wpi_softc) }; static devclass_t wpi_devclass; DRIVER_MODULE(wpi, pci, wpi_driver, wpi_devclass, NULL, NULL); MODULE_VERSION(wpi, 1); MODULE_DEPEND(wpi, pci, 1, 1, 1); MODULE_DEPEND(wpi, wlan, 1, 1, 1); MODULE_DEPEND(wpi, firmware, 1, 1, 1); static int wpi_probe(device_t dev) { const struct wpi_ident *ident; for (ident = wpi_ident_table; ident->name != NULL; ident++) { if (pci_get_vendor(dev) == ident->vendor && pci_get_device(dev) == ident->device) { device_set_desc(dev, ident->name); return (BUS_PROBE_DEFAULT); } } return ENXIO; } static int wpi_attach(device_t dev) { struct wpi_softc *sc = (struct wpi_softc *)device_get_softc(dev); struct ieee80211com *ic; struct ifnet *ifp; int i, error, rid, supportsa = 1; const struct wpi_ident *ident; uint8_t macaddr[IEEE80211_ADDR_LEN]; sc->sc_dev = dev; #ifdef WPI_DEBUG error = resource_int_value(device_get_name(sc->sc_dev), device_get_unit(sc->sc_dev), "debug", &(sc->sc_debug)); if (error != 0) sc->sc_debug = 0; #else sc->sc_debug = 0; #endif DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); /* * Get the offset of the PCI Express Capability Structure in PCI * Configuration Space. */ error = pci_find_cap(dev, PCIY_EXPRESS, &sc->sc_cap_off); if (error != 0) { device_printf(dev, "PCIe capability structure not found!\n"); return error; } /* * Some card's only support 802.11b/g not a, check to see if * this is one such card. A 0x0 in the subdevice table indicates * the entire subdevice range is to be ignored. */ for (ident = wpi_ident_table; ident->name != NULL; ident++) { if (ident->subdevice && pci_get_subdevice(dev) == ident->subdevice) { supportsa = 0; break; } } /* Clear device-specific "PCI retry timeout" register (41h). */ pci_write_config(dev, 0x41, 0, 1); /* Enable bus-mastering. */ pci_enable_busmaster(dev); rid = PCIR_BAR(0); sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mem == NULL) { device_printf(dev, "can't map mem space\n"); error = ENOMEM; return error; } sc->sc_st = rman_get_bustag(sc->mem); sc->sc_sh = rman_get_bushandle(sc->mem); i = 1; rid = 0; if (pci_alloc_msi(dev, &i) == 0) rid = 1; /* Install interrupt handler. */ sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | (rid != 0 ? 0 : RF_SHAREABLE)); if (sc->irq == NULL) { device_printf(dev, "can't map interrupt\n"); error = ENOMEM; goto fail; } WPI_LOCK_INIT(sc); sc->sc_unr = new_unrhdr(WPI_ID_IBSS_MIN, WPI_ID_IBSS_MAX, &sc->sc_mtx); /* Allocate DMA memory for firmware transfers. */ if ((error = wpi_alloc_fwmem(sc)) != 0) { device_printf(dev, "could not allocate memory for firmware, error %d\n", error); goto fail; } /* Allocate shared page. */ if ((error = wpi_alloc_shared(sc)) != 0) { device_printf(dev, "could not allocate shared page\n"); goto fail; } /* Allocate TX rings - 4 for QoS purposes, 1 for commands. */ for (i = 0; i < WPI_NTXQUEUES; i++) { if ((error = wpi_alloc_tx_ring(sc, &sc->txq[i], i)) != 0) { device_printf(dev, "could not allocate TX ring %d, error %d\n", i, error); goto fail; } } /* Allocate RX ring. */ if ((error = wpi_alloc_rx_ring(sc)) != 0) { device_printf(dev, "could not allocate RX ring, error %d\n", error); goto fail; } /* Clear pending interrupts. */ WPI_WRITE(sc, WPI_INT, 0xffffffff); ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); if (ifp == NULL) { device_printf(dev, "can not allocate ifnet structure\n"); goto fail; } ic = ifp->if_l2com; ic->ic_ifp = ifp; ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ /* Set device capabilities. */ ic->ic_caps = IEEE80211_C_STA /* station mode supported */ | IEEE80211_C_IBSS /* IBSS mode supported */ | IEEE80211_C_MONITOR /* monitor mode supported */ | IEEE80211_C_AHDEMO /* adhoc demo mode */ | IEEE80211_C_BGSCAN /* capable of bg scanning */ | IEEE80211_C_TXPMGT /* tx power management */ | IEEE80211_C_SHSLOT /* short slot time supported */ | IEEE80211_C_WPA /* 802.11i */ | IEEE80211_C_SHPREAMBLE /* short preamble supported */ #if 0 | IEEE80211_C_HOSTAP /* Host access point mode */ #endif | IEEE80211_C_WME /* 802.11e */ | IEEE80211_C_PMGT /* Station-side power mgmt */ ; ic->ic_cryptocaps = IEEE80211_CRYPTO_AES_CCM; + ic->ic_flags |= IEEE80211_F_DATAPAD; + /* * Read in the eeprom and also setup the channels for * net80211. We don't set the rates as net80211 does this for us */ if ((error = wpi_read_eeprom(sc, macaddr)) != 0) { device_printf(dev, "could not read EEPROM, error %d\n", error); goto fail; } #ifdef WPI_DEBUG if (bootverbose) { device_printf(sc->sc_dev, "Regulatory Domain: %.4s\n", sc->domain); device_printf(sc->sc_dev, "Hardware Type: %c\n", sc->type > 1 ? 'B': '?'); device_printf(sc->sc_dev, "Hardware Revision: %c\n", ((le16toh(sc->rev) & 0xf0) == 0xd0) ? 'D': '?'); device_printf(sc->sc_dev, "SKU %s support 802.11a\n", supportsa ? "does" : "does not"); /* XXX hw_config uses the PCIDEV for the Hardware rev. Must check what sc->rev really represents - benjsc 20070615 */ } #endif if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_softc = sc; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_init = wpi_init; ifp->if_ioctl = wpi_ioctl; ifp->if_start = wpi_start; IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; IFQ_SET_READY(&ifp->if_snd); ieee80211_ifattach(ic, macaddr); ic->ic_vap_create = wpi_vap_create; ic->ic_vap_delete = wpi_vap_delete; ic->ic_raw_xmit = wpi_raw_xmit; ic->ic_node_alloc = wpi_node_alloc; sc->sc_node_free = ic->ic_node_free; ic->ic_node_free = wpi_node_free; ic->ic_wme.wme_update = wpi_updateedca; ic->ic_update_promisc = wpi_update_promisc; ic->ic_update_mcast = wpi_update_mcast; ic->ic_scan_start = wpi_scan_start; ic->ic_scan_end = wpi_scan_end; ic->ic_set_channel = wpi_set_channel; sc->sc_scan_curchan = ic->ic_scan_curchan; ic->ic_scan_curchan = wpi_scan_curchan; ic->ic_scan_mindwell = wpi_scan_mindwell; ic->ic_setregdomain = wpi_setregdomain; wpi_radiotap_attach(sc); callout_init_mtx(&sc->calib_to, &sc->sc_mtx, 0); callout_init_mtx(&sc->watchdog_to, &sc->sc_mtx, 0); callout_init_mtx(&sc->watchdog_rfkill, &sc->sc_mtx, 0); TASK_INIT(&sc->sc_reinittask, 0, wpi_hw_reset, sc); TASK_INIT(&sc->sc_radiooff_task, 0, wpi_radio_off, sc); TASK_INIT(&sc->sc_radioon_task, 0, wpi_radio_on, sc); wpi_sysctlattach(sc); /* * Hook our interrupt after all initialization is complete. */ error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE, NULL, wpi_intr, sc, &sc->sc_ih); if (error != 0) { device_printf(dev, "can't establish interrupt, error %d\n", error); goto fail; } if (bootverbose) ieee80211_announce(ic); #ifdef WPI_DEBUG if (sc->sc_debug & WPI_DEBUG_HW) ieee80211_announce_channels(ic); #endif DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); return 0; fail: wpi_detach(dev); DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); return error; } /* * Attach the interface to 802.11 radiotap. */ static void wpi_radiotap_attach(struct wpi_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), WPI_TX_RADIOTAP_PRESENT, &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), WPI_RX_RADIOTAP_PRESENT); DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); } static void wpi_sysctlattach(struct wpi_softc *sc) { #ifdef WPI_DEBUG struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "debug", CTLFLAG_RW, &sc->sc_debug, sc->sc_debug, "control debugging printfs"); #endif } static struct ieee80211vap * wpi_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, enum ieee80211_opmode opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct wpi_vap *wvp; struct wpi_buf *bcn; struct ieee80211vap *vap; if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return NULL; wvp = (struct wpi_vap *) malloc(sizeof(struct wpi_vap), M_80211_VAP, M_NOWAIT | M_ZERO); if (wvp == NULL) return NULL; vap = &wvp->vap; ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac); bcn = &wvp->wv_bcbuf; bcn->data = NULL; /* Override with driver methods. */ wvp->newstate = vap->iv_newstate; vap->iv_key_alloc = wpi_key_alloc; vap->iv_key_set = wpi_key_set; vap->iv_key_delete = wpi_key_delete; vap->iv_newstate = wpi_newstate; vap->iv_update_beacon = wpi_update_beacon; ieee80211_ratectl_init(vap); /* Complete setup. */ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); ic->ic_opmode = opmode; return vap; } static void wpi_vap_delete(struct ieee80211vap *vap) { struct wpi_vap *wvp = WPI_VAP(vap); struct wpi_buf *bcn = &wvp->wv_bcbuf; ieee80211_ratectl_deinit(vap); ieee80211_vap_detach(vap); if (bcn->data != NULL) free(bcn->data, M_DEVBUF); free(wvp, M_80211_VAP); } static int wpi_detach(device_t dev) { struct wpi_softc *sc = device_get_softc(dev); struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic; int qid; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); if (ifp != NULL) { ic = ifp->if_l2com; ieee80211_draintask(ic, &sc->sc_reinittask); ieee80211_draintask(ic, &sc->sc_radiooff_task); wpi_stop(sc); callout_drain(&sc->watchdog_to); callout_drain(&sc->watchdog_rfkill); callout_drain(&sc->calib_to); ieee80211_ifdetach(ic); } /* Uninstall interrupt handler. */ if (sc->irq != NULL) { bus_teardown_intr(dev, sc->irq, sc->sc_ih); bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->irq), sc->irq); pci_release_msi(dev); } if (sc->txq[0].data_dmat) { /* Free DMA resources. */ for (qid = 0; qid < WPI_NTXQUEUES; qid++) wpi_free_tx_ring(sc, &sc->txq[qid]); wpi_free_rx_ring(sc); wpi_free_shared(sc); } if (sc->fw_dma.tag) wpi_free_fwmem(sc); if (sc->mem != NULL) bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->mem), sc->mem); if (ifp != NULL) if_free(ifp); delete_unrhdr(sc->sc_unr); DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); WPI_LOCK_DESTROY(sc); return 0; } static int wpi_shutdown(device_t dev) { struct wpi_softc *sc = device_get_softc(dev); wpi_stop(sc); return 0; } static int wpi_suspend(device_t dev) { struct wpi_softc *sc = device_get_softc(dev); struct ieee80211com *ic = sc->sc_ifp->if_l2com; ieee80211_suspend_all(ic); return 0; } static int wpi_resume(device_t dev) { struct wpi_softc *sc = device_get_softc(dev); struct ieee80211com *ic = sc->sc_ifp->if_l2com; /* Clear device-specific "PCI retry timeout" register (41h). */ pci_write_config(dev, 0x41, 0, 1); ieee80211_resume_all(ic); return 0; } /* * Grab exclusive access to NIC memory. */ static int wpi_nic_lock(struct wpi_softc *sc) { int ntries; /* Request exclusive access to NIC. */ WPI_SETBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ); /* Spin until we actually get the lock. */ for (ntries = 0; ntries < 1000; ntries++) { if ((WPI_READ(sc, WPI_GP_CNTRL) & (WPI_GP_CNTRL_MAC_ACCESS_ENA | WPI_GP_CNTRL_SLEEP)) == WPI_GP_CNTRL_MAC_ACCESS_ENA) return 0; DELAY(10); } device_printf(sc->sc_dev, "could not lock memory\n"); return ETIMEDOUT; } /* * Release lock on NIC memory. */ static __inline void wpi_nic_unlock(struct wpi_softc *sc) { WPI_CLRBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ); } static __inline uint32_t wpi_prph_read(struct wpi_softc *sc, uint32_t addr) { WPI_WRITE(sc, WPI_PRPH_RADDR, WPI_PRPH_DWORD | addr); WPI_BARRIER_READ_WRITE(sc); return WPI_READ(sc, WPI_PRPH_RDATA); } static __inline void wpi_prph_write(struct wpi_softc *sc, uint32_t addr, uint32_t data) { WPI_WRITE(sc, WPI_PRPH_WADDR, WPI_PRPH_DWORD | addr); WPI_BARRIER_WRITE(sc); WPI_WRITE(sc, WPI_PRPH_WDATA, data); } static __inline void wpi_prph_setbits(struct wpi_softc *sc, uint32_t addr, uint32_t mask) { wpi_prph_write(sc, addr, wpi_prph_read(sc, addr) | mask); } static __inline void wpi_prph_clrbits(struct wpi_softc *sc, uint32_t addr, uint32_t mask) { wpi_prph_write(sc, addr, wpi_prph_read(sc, addr) & ~mask); } static __inline void wpi_prph_write_region_4(struct wpi_softc *sc, uint32_t addr, const uint32_t *data, int count) { for (; count > 0; count--, data++, addr += 4) wpi_prph_write(sc, addr, *data); } static __inline uint32_t wpi_mem_read(struct wpi_softc *sc, uint32_t addr) { WPI_WRITE(sc, WPI_MEM_RADDR, addr); WPI_BARRIER_READ_WRITE(sc); return WPI_READ(sc, WPI_MEM_RDATA); } static __inline void wpi_mem_read_region_4(struct wpi_softc *sc, uint32_t addr, uint32_t *data, int count) { for (; count > 0; count--, addr += 4) *data++ = wpi_mem_read(sc, addr); } static int wpi_read_prom_data(struct wpi_softc *sc, uint32_t addr, void *data, int count) { uint8_t *out = data; uint32_t val; int error, ntries; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); if ((error = wpi_nic_lock(sc)) != 0) return error; for (; count > 0; count -= 2, addr++) { WPI_WRITE(sc, WPI_EEPROM, addr << 2); for (ntries = 0; ntries < 10; ntries++) { val = WPI_READ(sc, WPI_EEPROM); if (val & WPI_EEPROM_READ_VALID) break; DELAY(5); } if (ntries == 10) { device_printf(sc->sc_dev, "timeout reading ROM at 0x%x\n", addr); return ETIMEDOUT; } *out++= val >> 16; if (count > 1) *out ++= val >> 24; } wpi_nic_unlock(sc); DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); return 0; } static void wpi_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { if (error != 0) return; KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs)); *(bus_addr_t *)arg = segs[0].ds_addr; } /* * Allocates a contiguous block of dma memory of the requested size and * alignment. */ static int wpi_dma_contig_alloc(struct wpi_softc *sc, struct wpi_dma_info *dma, void **kvap, bus_size_t size, bus_size_t alignment) { int error; dma->tag = NULL; dma->size = size; error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), alignment, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size, 1, size, BUS_DMA_NOWAIT, NULL, NULL, &dma->tag); if (error != 0) goto fail; error = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr, BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, &dma->map); if (error != 0) goto fail; error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr, size, wpi_dma_map_addr, &dma->paddr, BUS_DMA_NOWAIT); if (error != 0) goto fail; bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); if (kvap != NULL) *kvap = dma->vaddr; return 0; fail: wpi_dma_contig_free(dma); return error; } static void wpi_dma_contig_free(struct wpi_dma_info *dma) { if (dma->vaddr != NULL) { bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(dma->tag, dma->map); bus_dmamem_free(dma->tag, dma->vaddr, dma->map); dma->vaddr = NULL; } if (dma->tag != NULL) { bus_dma_tag_destroy(dma->tag); dma->tag = NULL; } } /* * Allocate a shared page between host and NIC. */ static int wpi_alloc_shared(struct wpi_softc *sc) { /* Shared buffer must be aligned on a 4KB boundary. */ return wpi_dma_contig_alloc(sc, &sc->shared_dma, (void **)&sc->shared, sizeof (struct wpi_shared), 4096); } static void wpi_free_shared(struct wpi_softc *sc) { wpi_dma_contig_free(&sc->shared_dma); } /* * Allocate DMA-safe memory for firmware transfer. */ static int wpi_alloc_fwmem(struct wpi_softc *sc) { /* Must be aligned on a 16-byte boundary. */ return wpi_dma_contig_alloc(sc, &sc->fw_dma, NULL, WPI_FW_TEXT_MAXSZ + WPI_FW_DATA_MAXSZ, 16); } static void wpi_free_fwmem(struct wpi_softc *sc) { wpi_dma_contig_free(&sc->fw_dma); } static int wpi_alloc_rx_ring(struct wpi_softc *sc) { struct wpi_rx_ring *ring = &sc->rxq; bus_size_t size; int i, error; ring->cur = 0; ring->update = 0; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); /* Allocate RX descriptors (16KB aligned.) */ size = WPI_RX_RING_COUNT * sizeof (uint32_t); error = wpi_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc, size, WPI_RING_DMA_ALIGN); if (error != 0) { device_printf(sc->sc_dev, "%s: could not allocate RX ring DMA memory, error %d\n", __func__, error); goto fail; } /* Create RX buffer DMA tag. */ error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MJUMPAGESIZE, 1, MJUMPAGESIZE, BUS_DMA_NOWAIT, NULL, NULL, &ring->data_dmat); if (error != 0) { device_printf(sc->sc_dev, "%s: could not create RX buf DMA tag, error %d\n", __func__, error); goto fail; } /* * Allocate and map RX buffers. */ for (i = 0; i < WPI_RX_RING_COUNT; i++) { struct wpi_rx_data *data = &ring->data[i]; bus_addr_t paddr; error = bus_dmamap_create(ring->data_dmat, 0, &data->map); if (error != 0) { device_printf(sc->sc_dev, "%s: could not create RX buf DMA map, error %d\n", __func__, error); goto fail; } data->m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); if (data->m == NULL) { device_printf(sc->sc_dev, "%s: could not allocate RX mbuf\n", __func__); error = ENOBUFS; goto fail; } error = bus_dmamap_load(ring->data_dmat, data->map, mtod(data->m, void *), MJUMPAGESIZE, wpi_dma_map_addr, &paddr, BUS_DMA_NOWAIT); if (error != 0 && error != EFBIG) { device_printf(sc->sc_dev, "%s: can't map mbuf (error %d)\n", __func__, error); goto fail; } /* Set physical address of RX buffer. */ ring->desc[i] = htole32(paddr); } bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, BUS_DMASYNC_PREWRITE); DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); return 0; fail: wpi_free_rx_ring(sc); DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); return error; } static void wpi_update_rx_ring(struct wpi_softc *sc) { struct wpi_rx_ring *ring = &sc->rxq; if (WPI_READ(sc, WPI_UCODE_GP1) & WPI_UCODE_GP1_MAC_SLEEP) { DPRINTF(sc, WPI_DEBUG_PWRSAVE, "%s: wakeup request\n", __func__); WPI_SETBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ); ring->update = 1; } else WPI_WRITE(sc, WPI_FH_RX_WPTR, ring->cur & ~7); } static void wpi_reset_rx_ring(struct wpi_softc *sc) { struct wpi_rx_ring *ring = &sc->rxq; int ntries; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); if (wpi_nic_lock(sc) == 0) { WPI_WRITE(sc, WPI_FH_RX_CONFIG, 0); for (ntries = 0; ntries < 1000; ntries++) { if (WPI_READ(sc, WPI_FH_RX_STATUS) & WPI_FH_RX_STATUS_IDLE) break; DELAY(10); } #ifdef WPI_DEBUG if (ntries == 1000) { device_printf(sc->sc_dev, "timeout resetting Rx ring\n"); } #endif wpi_nic_unlock(sc); } ring->cur = 0; ring->update = 0; } static void wpi_free_rx_ring(struct wpi_softc *sc) { struct wpi_rx_ring *ring = &sc->rxq; int i; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); wpi_dma_contig_free(&ring->desc_dma); for (i = 0; i < WPI_RX_RING_COUNT; i++) { struct wpi_rx_data *data = &ring->data[i]; if (data->m != NULL) { bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); data->m = NULL; } if (data->map != NULL) bus_dmamap_destroy(ring->data_dmat, data->map); } if (ring->data_dmat != NULL) { bus_dma_tag_destroy(ring->data_dmat); ring->data_dmat = NULL; } } static int wpi_alloc_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring, int qid) { bus_addr_t paddr; bus_size_t size; int i, error; ring->qid = qid; ring->queued = 0; ring->cur = 0; ring->update = 0; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); /* Allocate TX descriptors (16KB aligned.) */ size = WPI_TX_RING_COUNT * sizeof (struct wpi_tx_desc); error = wpi_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc, size, WPI_RING_DMA_ALIGN); if (error != 0) { device_printf(sc->sc_dev, "%s: could not allocate TX ring DMA memory, error %d\n", __func__, error); goto fail; } /* Update shared area with ring physical address. */ sc->shared->txbase[qid] = htole32(ring->desc_dma.paddr); bus_dmamap_sync(sc->shared_dma.tag, sc->shared_dma.map, BUS_DMASYNC_PREWRITE); /* * We only use rings 0 through 4 (4 EDCA + cmd) so there is no need * to allocate commands space for other rings. * XXX Do we really need to allocate descriptors for other rings? */ if (qid > 4) return 0; size = WPI_TX_RING_COUNT * sizeof (struct wpi_tx_cmd); error = wpi_dma_contig_alloc(sc, &ring->cmd_dma, (void **)&ring->cmd, size, 4); if (error != 0) { device_printf(sc->sc_dev, "%s: could not allocate TX cmd DMA memory, error %d\n", __func__, error); goto fail; } error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, WPI_MAX_SCATTER - 1, MCLBYTES, BUS_DMA_NOWAIT, NULL, NULL, &ring->data_dmat); if (error != 0) { device_printf(sc->sc_dev, "%s: could not create TX buf DMA tag, error %d\n", __func__, error); goto fail; } paddr = ring->cmd_dma.paddr; for (i = 0; i < WPI_TX_RING_COUNT; i++) { struct wpi_tx_data *data = &ring->data[i]; data->cmd_paddr = paddr; paddr += sizeof (struct wpi_tx_cmd); error = bus_dmamap_create(ring->data_dmat, 0, &data->map); if (error != 0) { device_printf(sc->sc_dev, "%s: could not create TX buf DMA map, error %d\n", __func__, error); goto fail; } } DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); return 0; fail: wpi_free_tx_ring(sc, ring); DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); return error; } static void wpi_update_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring) { if (WPI_READ(sc, WPI_UCODE_GP1) & WPI_UCODE_GP1_MAC_SLEEP) { DPRINTF(sc, WPI_DEBUG_PWRSAVE, "%s (%d): requesting wakeup\n", __func__, ring->qid); WPI_SETBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ); ring->update = 1; } else WPI_WRITE(sc, WPI_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); } static void wpi_reset_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring) { int i; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); for (i = 0; i < WPI_TX_RING_COUNT; i++) { struct wpi_tx_data *data = &ring->data[i]; if (data->m != NULL) { bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); data->m = NULL; } } /* Clear TX descriptors. */ memset(ring->desc, 0, ring->desc_dma.size); bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, BUS_DMASYNC_PREWRITE); sc->qfullmsk &= ~(1 << ring->qid); ring->queued = 0; ring->cur = 0; ring->update = 0; } static void wpi_free_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring) { int i; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); wpi_dma_contig_free(&ring->desc_dma); wpi_dma_contig_free(&ring->cmd_dma); for (i = 0; i < WPI_TX_RING_COUNT; i++) { struct wpi_tx_data *data = &ring->data[i]; if (data->m != NULL) { bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); } if (data->map != NULL) bus_dmamap_destroy(ring->data_dmat, data->map); } if (ring->data_dmat != NULL) { bus_dma_tag_destroy(ring->data_dmat); ring->data_dmat = NULL; } } /* * Extract various information from EEPROM. */ static int wpi_read_eeprom(struct wpi_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN]) { #define WPI_CHK(res) do { \ if ((error = res) != 0) \ goto fail; \ } while (0) int error, i; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); /* Adapter has to be powered on for EEPROM access to work. */ if ((error = wpi_apm_init(sc)) != 0) { device_printf(sc->sc_dev, "%s: could not power ON adapter, error %d\n", __func__, error); return error; } if ((WPI_READ(sc, WPI_EEPROM_GP) & 0x6) == 0) { device_printf(sc->sc_dev, "bad EEPROM signature\n"); error = EIO; goto fail; } /* Clear HW ownership of EEPROM. */ WPI_CLRBITS(sc, WPI_EEPROM_GP, WPI_EEPROM_GP_IF_OWNER); /* Read the hardware capabilities, revision and SKU type. */ WPI_CHK(wpi_read_prom_data(sc, WPI_EEPROM_SKU_CAP, &sc->cap, sizeof(sc->cap))); WPI_CHK(wpi_read_prom_data(sc, WPI_EEPROM_REVISION, &sc->rev, sizeof(sc->rev))); WPI_CHK(wpi_read_prom_data(sc, WPI_EEPROM_TYPE, &sc->type, sizeof(sc->type))); DPRINTF(sc, WPI_DEBUG_EEPROM, "cap=%x rev=%x type=%x\n", sc->cap, le16toh(sc->rev), sc->type); /* Read the regulatory domain (4 ASCII characters.) */ WPI_CHK(wpi_read_prom_data(sc, WPI_EEPROM_DOMAIN, sc->domain, sizeof(sc->domain))); /* Read MAC address. */ WPI_CHK(wpi_read_prom_data(sc, WPI_EEPROM_MAC, macaddr, IEEE80211_ADDR_LEN)); /* Read the list of authorized channels. */ for (i = 0; i < WPI_CHAN_BANDS_COUNT; i++) WPI_CHK(wpi_read_eeprom_channels(sc, i)); /* Read the list of TX power groups. */ for (i = 0; i < WPI_POWER_GROUPS_COUNT; i++) WPI_CHK(wpi_read_eeprom_group(sc, i)); fail: wpi_apm_stop(sc); /* Power OFF adapter. */ DPRINTF(sc, WPI_DEBUG_TRACE, error ? TRACE_STR_END_ERR : TRACE_STR_END, __func__); return error; #undef WPI_CHK } /* * Translate EEPROM flags to net80211. */ static uint32_t wpi_eeprom_channel_flags(struct wpi_eeprom_chan *channel) { uint32_t nflags; nflags = 0; if ((channel->flags & WPI_EEPROM_CHAN_ACTIVE) == 0) nflags |= IEEE80211_CHAN_PASSIVE; if ((channel->flags & WPI_EEPROM_CHAN_IBSS) == 0) nflags |= IEEE80211_CHAN_NOADHOC; if (channel->flags & WPI_EEPROM_CHAN_RADAR) { nflags |= IEEE80211_CHAN_DFS; /* XXX apparently IBSS may still be marked */ nflags |= IEEE80211_CHAN_NOADHOC; } return nflags; } static void wpi_read_eeprom_band(struct wpi_softc *sc, int n) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct wpi_eeprom_chan *channels = sc->eeprom_channels[n]; const struct wpi_chan_band *band = &wpi_bands[n]; struct ieee80211_channel *c; uint8_t chan; int i, nflags; for (i = 0; i < band->nchan; i++) { if (!(channels[i].flags & WPI_EEPROM_CHAN_VALID)) { DPRINTF(sc, WPI_DEBUG_HW, "Channel Not Valid: %d, band %d\n", band->chan[i],n); continue; } chan = band->chan[i]; nflags = wpi_eeprom_channel_flags(&channels[i]); c = &ic->ic_channels[ic->ic_nchans++]; c->ic_ieee = chan; c->ic_maxregpower = channels[i].maxpwr; c->ic_maxpower = 2*c->ic_maxregpower; if (n == 0) { /* 2GHz band */ c->ic_freq = ieee80211_ieee2mhz(chan, IEEE80211_CHAN_G); /* G =>'s B is supported */ c->ic_flags = IEEE80211_CHAN_B | nflags; c = &ic->ic_channels[ic->ic_nchans++]; c[0] = c[-1]; c->ic_flags = IEEE80211_CHAN_G | nflags; } else { /* 5GHz band */ c->ic_freq = ieee80211_ieee2mhz(chan, IEEE80211_CHAN_A); c->ic_flags = IEEE80211_CHAN_A | nflags; } /* Save maximum allowed TX power for this channel. */ sc->maxpwr[chan] = channels[i].maxpwr; DPRINTF(sc, WPI_DEBUG_EEPROM, "adding chan %d (%dMHz) flags=0x%x maxpwr=%d passive=%d," " offset %d\n", chan, c->ic_freq, channels[i].flags, sc->maxpwr[chan], - (c->ic_flags & IEEE80211_CHAN_PASSIVE) != 0, - ic->ic_nchans); + IEEE80211_IS_CHAN_PASSIVE(c), ic->ic_nchans); } } /** * Read the eeprom to find out what channels are valid for the given * band and update net80211 with what we find. */ static int wpi_read_eeprom_channels(struct wpi_softc *sc, int n) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; const struct wpi_chan_band *band = &wpi_bands[n]; int error; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); error = wpi_read_prom_data(sc, band->addr, &sc->eeprom_channels[n], band->nchan * sizeof (struct wpi_eeprom_chan)); if (error != 0) { DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); return error; } wpi_read_eeprom_band(sc, n); ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans); DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); return 0; } static struct wpi_eeprom_chan * wpi_find_eeprom_channel(struct wpi_softc *sc, struct ieee80211_channel *c) { int i, j; for (j = 0; j < WPI_CHAN_BANDS_COUNT; j++) for (i = 0; i < wpi_bands[j].nchan; i++) if (wpi_bands[j].chan[i] == c->ic_ieee) return &sc->eeprom_channels[j][i]; return NULL; } /* * Enforce flags read from EEPROM. */ static int wpi_setregdomain(struct ieee80211com *ic, struct ieee80211_regdomain *rd, int nchan, struct ieee80211_channel chans[]) { struct ifnet *ifp = ic->ic_ifp; struct wpi_softc *sc = ifp->if_softc; int i; for (i = 0; i < nchan; i++) { struct ieee80211_channel *c = &chans[i]; struct wpi_eeprom_chan *channel; channel = wpi_find_eeprom_channel(sc, c); if (channel == NULL) { if_printf(ic->ic_ifp, "%s: invalid channel %u freq %u/0x%x\n", __func__, c->ic_ieee, c->ic_freq, c->ic_flags); return EINVAL; } c->ic_flags |= wpi_eeprom_channel_flags(channel); } return 0; } static int wpi_read_eeprom_group(struct wpi_softc *sc, int n) { struct wpi_power_group *group = &sc->groups[n]; struct wpi_eeprom_group rgroup; int i, error; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); if ((error = wpi_read_prom_data(sc, WPI_EEPROM_POWER_GRP + n * 32, &rgroup, sizeof rgroup)) != 0) { DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); return error; } /* Save TX power group information. */ group->chan = rgroup.chan; group->maxpwr = rgroup.maxpwr; /* Retrieve temperature at which the samples were taken. */ group->temp = (int16_t)le16toh(rgroup.temp); DPRINTF(sc, WPI_DEBUG_EEPROM, "power group %d: chan=%d maxpwr=%d temp=%d\n", n, group->chan, group->maxpwr, group->temp); for (i = 0; i < WPI_SAMPLES_COUNT; i++) { group->samples[i].index = rgroup.samples[i].index; group->samples[i].power = rgroup.samples[i].power; DPRINTF(sc, WPI_DEBUG_EEPROM, "\tsample %d: index=%d power=%d\n", i, group->samples[i].index, group->samples[i].power); } DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); return 0; } static struct ieee80211_node * wpi_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) { struct wpi_node *wn; wn = malloc(sizeof (struct wpi_node), M_80211_NODE, M_NOWAIT | M_ZERO); if (wn == NULL) return NULL; wn->id = WPI_ID_UNDEFINED; return &wn->ni; } static void wpi_node_free(struct ieee80211_node *ni) { struct ieee80211com *ic = ni->ni_ic; struct wpi_softc *sc = ic->ic_ifp->if_softc; struct wpi_node *wn = (struct wpi_node *)ni; if (wn->id >= WPI_ID_IBSS_MIN && wn->id <= WPI_ID_IBSS_MAX) { free_unr(sc->sc_unr, wn->id); WPI_LOCK(sc); if (sc->rxon.filter & htole32(WPI_FILTER_BSS)) wpi_del_node(sc, ni); WPI_UNLOCK(sc); } sc->sc_node_free(ni); } /** * Called by net80211 when ever there is a change to 80211 state machine */ static int wpi_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct wpi_vap *wvp = WPI_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct ifnet *ifp = ic->ic_ifp; struct wpi_softc *sc = ifp->if_softc; int error = 0; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); DPRINTF(sc, WPI_DEBUG_STATE, "%s: %s -> %s\n", __func__, ieee80211_state_name[vap->iv_state], ieee80211_state_name[nstate]); IEEE80211_UNLOCK(ic); WPI_LOCK(sc); switch (nstate) { case IEEE80211_S_SCAN: if ((vap->iv_opmode == IEEE80211_M_IBSS || vap->iv_opmode == IEEE80211_M_AHDEMO) && (sc->rxon.filter & htole32(WPI_FILTER_BSS))) { sc->rxon.filter &= ~htole32(WPI_FILTER_BSS); if ((error = wpi_send_rxon(sc, 0, 1)) != 0) { device_printf(sc->sc_dev, "%s: could not send RXON\n", __func__); } } break; case IEEE80211_S_ASSOC: if (vap->iv_state != IEEE80211_S_RUN) break; /* FALLTHROUGH */ case IEEE80211_S_AUTH: /* * The node must be registered in the firmware before auth. * Also the associd must be cleared on RUN -> ASSOC * transitions. */ if ((error = wpi_auth(sc, vap)) != 0) { device_printf(sc->sc_dev, "%s: could not move to AUTH state, error %d\n", __func__, error); } break; case IEEE80211_S_RUN: /* * RUN -> RUN transition; Just restart the timers. */ if (vap->iv_state == IEEE80211_S_RUN) { wpi_calib_timeout(sc); break; } /* * !RUN -> RUN requires setting the association id * which is done with a firmware cmd. We also defer * starting the timers until that work is done. */ if ((error = wpi_run(sc, vap)) != 0) { device_printf(sc->sc_dev, "%s: could not move to RUN state\n", __func__); } break; default: break; } WPI_UNLOCK(sc); IEEE80211_LOCK(ic); if (error != 0) { DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); return error; } DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); return wvp->newstate(vap, nstate, arg); } static void wpi_calib_timeout(void *arg) { struct wpi_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); if (vap->iv_state != IEEE80211_S_RUN) return; wpi_power_calibration(sc); callout_reset(&sc->calib_to, 60*hz, wpi_calib_timeout, sc); } static __inline uint8_t rate2plcp(const uint8_t rate) { switch (rate) { case 12: return 0xd; case 18: return 0xf; case 24: return 0x5; case 36: return 0x7; case 48: return 0x9; case 72: return 0xb; case 96: return 0x1; case 108: return 0x3; case 2: return 10; case 4: return 20; case 11: return 55; case 22: return 110; default: return 0; } } static __inline uint8_t plcp2rate(const uint8_t plcp) { switch (plcp) { case 0xd: return 12; case 0xf: return 18; case 0x5: return 24; case 0x7: return 36; case 0x9: return 48; case 0xb: return 72; case 0x1: return 96; case 0x3: return 108; case 10: return 2; case 20: return 4; case 55: return 11; case 110: return 22; default: return 0; } } /* Quickly determine if a given rate is CCK or OFDM. */ #define WPI_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22) static void wpi_rx_done(struct wpi_softc *sc, struct wpi_rx_desc *desc, struct wpi_rx_data *data) { struct ifnet *ifp = sc->sc_ifp; const struct ieee80211_cipher *cip = NULL; struct ieee80211com *ic = ifp->if_l2com; struct wpi_rx_ring *ring = &sc->rxq; struct wpi_rx_stat *stat; struct wpi_rx_head *head; struct wpi_rx_tail *tail; struct ieee80211_frame *wh; struct ieee80211_node *ni; struct mbuf *m, *m1; bus_addr_t paddr; uint32_t flags; uint16_t len; int error; stat = (struct wpi_rx_stat *)(desc + 1); if (stat->len > WPI_STAT_MAXLEN) { device_printf(sc->sc_dev, "invalid RX statistic header\n"); - if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); - return; + goto fail1; } bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD); head = (struct wpi_rx_head *)((caddr_t)(stat + 1) + stat->len); len = le16toh(head->len); tail = (struct wpi_rx_tail *)((caddr_t)(head + 1) + len); flags = le32toh(tail->flags); DPRINTF(sc, WPI_DEBUG_RECV, "%s: idx %d len %d stat len %u rssi %d" " rate %x chan %d tstamp %ju\n", __func__, ring->cur, le32toh(desc->len), len, (int8_t)stat->rssi, head->plcp, head->chan, (uintmax_t)le64toh(tail->tstamp)); /* Discard frames with a bad FCS early. */ if ((flags & WPI_RX_NOERROR) != WPI_RX_NOERROR) { DPRINTF(sc, WPI_DEBUG_RECV, "%s: RX flags error %x\n", __func__, flags); - if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); - return; + goto fail1; } /* Discard frames that are too short. */ if (len < sizeof (*wh)) { DPRINTF(sc, WPI_DEBUG_RECV, "%s: frame too short: %d\n", __func__, len); - if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); - return; + goto fail1; } m1 = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); if (m1 == NULL) { DPRINTF(sc, WPI_DEBUG_ANY, "%s: no mbuf to restock ring\n", __func__); - if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); - return; + goto fail1; } bus_dmamap_unload(ring->data_dmat, data->map); error = bus_dmamap_load(ring->data_dmat, data->map, mtod(m1, void *), MJUMPAGESIZE, wpi_dma_map_addr, &paddr, BUS_DMA_NOWAIT); if (error != 0 && error != EFBIG) { device_printf(sc->sc_dev, "%s: bus_dmamap_load failed, error %d\n", __func__, error); m_freem(m1); /* Try to reload the old mbuf. */ error = bus_dmamap_load(ring->data_dmat, data->map, mtod(data->m, void *), MJUMPAGESIZE, wpi_dma_map_addr, &paddr, BUS_DMA_NOWAIT); if (error != 0 && error != EFBIG) { panic("%s: could not load old RX mbuf", __func__); } /* Physical address may have changed. */ ring->desc[ring->cur] = htole32(paddr); bus_dmamap_sync(ring->data_dmat, ring->desc_dma.map, BUS_DMASYNC_PREWRITE); - if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); - return; + goto fail1; } m = data->m; data->m = m1; /* Update RX descriptor. */ ring->desc[ring->cur] = htole32(paddr); bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, BUS_DMASYNC_PREWRITE); /* Finalize mbuf. */ m->m_pkthdr.rcvif = ifp; m->m_data = (caddr_t)(head + 1); m->m_pkthdr.len = m->m_len = len; /* Grab a reference to the source node. */ wh = mtod(m, struct ieee80211_frame *); ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); if (ni != NULL) cip = ni->ni_ucastkey.wk_cipher; if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) && !IEEE80211_IS_MULTICAST(wh->i_addr1) && cip != NULL && cip->ic_cipher == IEEE80211_CIPHER_AES_CCM) { - if ((flags & WPI_RX_CIPHER_MASK) != WPI_RX_CIPHER_CCMP) { - if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); - m_freem(m); - return; - } + if ((flags & WPI_RX_CIPHER_MASK) != WPI_RX_CIPHER_CCMP) + goto fail2; + /* Check whether decryption was successful or not. */ if ((flags & WPI_RX_DECRYPT_MASK) != WPI_RX_DECRYPT_OK) { DPRINTF(sc, WPI_DEBUG_RECV, "CCMP decryption failed 0x%x\n", flags); - if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); - m_freem(m); - return; + goto fail2; } m->m_flags |= M_WEP; } if (ieee80211_radiotap_active(ic)) { struct wpi_rx_radiotap_header *tap = &sc->sc_rxtap; tap->wr_flags = 0; if (head->flags & htole16(WPI_STAT_FLAG_SHPREAMBLE)) tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; tap->wr_dbm_antsignal = (int8_t)(stat->rssi - WPI_RSSI_OFFSET); tap->wr_dbm_antnoise = (int8_t)le16toh(stat->noise); tap->wr_tsft = tail->tstamp; tap->wr_antenna = (le16toh(head->flags) >> 4) & 0xf; tap->wr_rate = plcp2rate(head->plcp); } WPI_UNLOCK(sc); /* Send the frame to the 802.11 layer. */ if (ni != NULL) { (void)ieee80211_input(ni, m, stat->rssi, -WPI_RSSI_OFFSET); /* Node is no longer needed. */ ieee80211_free_node(ni); } else (void)ieee80211_input_all(ic, m, stat->rssi, -WPI_RSSI_OFFSET); WPI_LOCK(sc); + + return; + +fail2: ieee80211_free_node(ni); + m_freem(m); + +fail1: if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); } static void wpi_rx_statistics(struct wpi_softc *sc, struct wpi_rx_desc *desc, struct wpi_rx_data *data) { /* Ignore */ } static void wpi_tx_done(struct wpi_softc *sc, struct wpi_rx_desc *desc) { struct ifnet *ifp = sc->sc_ifp; struct wpi_tx_ring *ring = &sc->txq[desc->qid & 0x3]; struct wpi_tx_data *data = &ring->data[desc->idx]; struct wpi_tx_stat *stat = (struct wpi_tx_stat *)(desc + 1); struct mbuf *m; struct ieee80211_node *ni; struct ieee80211vap *vap; + int ackfailcnt = stat->ackfailcnt; int status = le32toh(stat->status); KASSERT(data->ni != NULL, ("no node")); DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); DPRINTF(sc, WPI_DEBUG_XMIT, "%s: " "qid %d idx %d retries %d btkillcnt %d rate %x duration %d " - "status %x\n", __func__, desc->qid, desc->idx, stat->ackfailcnt, + "status %x\n", __func__, desc->qid, desc->idx, ackfailcnt, stat->btkillcnt, stat->rate, le32toh(stat->duration), status); /* Unmap and free mbuf. */ bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ring->data_dmat, data->map); m = data->m, data->m = NULL; ni = data->ni, data->ni = NULL; vap = ni->ni_vap; /* * Update rate control statistics for the node. */ WPI_UNLOCK(sc); if ((status & 0xff) != 1) { if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); ieee80211_ratectl_tx_complete(vap, ni, - IEEE80211_RATECTL_TX_FAILURE, &stat->ackfailcnt, NULL); + IEEE80211_RATECTL_TX_FAILURE, &ackfailcnt, NULL); } else { if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); ieee80211_ratectl_tx_complete(vap, ni, - IEEE80211_RATECTL_TX_SUCCESS, &stat->ackfailcnt, NULL); + IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL); } ieee80211_tx_complete(ni, m, (status & 0xff) != 1); WPI_LOCK(sc); sc->sc_tx_timer = 0; if (--ring->queued < WPI_TX_RING_LOMARK) { sc->qfullmsk &= ~(1 << ring->qid); if (sc->qfullmsk == 0 && (ifp->if_drv_flags & IFF_DRV_OACTIVE)) { ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; wpi_start_locked(ifp); } } DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); } /* * Process a "command done" firmware notification. This is where we wakeup * processes waiting for a synchronous command completion. */ static void wpi_cmd_done(struct wpi_softc *sc, struct wpi_rx_desc *desc) { struct wpi_tx_ring *ring = &sc->txq[4]; struct wpi_tx_data *data; DPRINTF(sc, WPI_DEBUG_CMD, "cmd notification qid=%x idx=%d flags=%x " "type=%s len=%d\n", desc->qid, desc->idx, desc->flags, wpi_cmd_str(desc->type), le32toh(desc->len)); if ((desc->qid & 7) != 4) return; /* Not a command ack. */ data = &ring->data[desc->idx]; /* If the command was mapped in an mbuf, free it. */ if (data->m != NULL) { bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); data->m = NULL; } sc->flags &= ~WPI_FLAG_BUSY; wakeup(&ring->cmd[desc->idx]); } static void wpi_notif_intr(struct wpi_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); int hw; bus_dmamap_sync(sc->shared_dma.tag, sc->shared_dma.map, BUS_DMASYNC_POSTREAD); hw = le32toh(sc->shared->next); hw = (hw == 0) ? WPI_RX_RING_COUNT - 1 : hw - 1; - if (sc->rxq.cur == hw) - return; - - do { + while (sc->rxq.cur != hw) { sc->rxq.cur = (sc->rxq.cur + 1) % WPI_RX_RING_COUNT; struct wpi_rx_data *data = &sc->rxq.data[sc->rxq.cur]; struct wpi_rx_desc *desc; bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); desc = mtod(data->m, struct wpi_rx_desc *); DPRINTF(sc, WPI_DEBUG_NOTIFY, "%s: cur=%d; qid %x idx %d flags %x type %d(%s) len %d\n", __func__, sc->rxq.cur, desc->qid, desc->idx, desc->flags, desc->type, wpi_cmd_str(desc->type), le32toh(desc->len)); if (!(desc->qid & 0x80)) /* Reply to a command. */ wpi_cmd_done(sc, desc); switch (desc->type) { case WPI_RX_DONE: /* An 802.11 frame has been received. */ wpi_rx_done(sc, desc, data); break; case WPI_TX_DONE: /* An 802.11 frame has been transmitted. */ wpi_tx_done(sc, desc); break; case WPI_RX_STATISTICS: case WPI_BEACON_STATISTICS: wpi_rx_statistics(sc, desc, data); break; case WPI_BEACON_MISSED: { struct wpi_beacon_missed *miss = (struct wpi_beacon_missed *)(desc + 1); int misses; bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); misses = le32toh(miss->consecutive); DPRINTF(sc, WPI_DEBUG_STATE, "%s: beacons missed %d/%d\n", __func__, misses, le32toh(miss->total)); if (vap->iv_state == IEEE80211_S_RUN && (ic->ic_flags & IEEE80211_S_SCAN) == 0) { if (misses >= vap->iv_bmissthreshold) { WPI_UNLOCK(sc); ieee80211_beacon_miss(ic); WPI_LOCK(sc); } } break; } case WPI_UC_READY: { struct wpi_ucode_info *uc = (struct wpi_ucode_info *)(desc + 1); /* The microcontroller is ready. */ bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); DPRINTF(sc, WPI_DEBUG_RESET, "microcode alive notification version=%d.%d " "subtype=%x alive=%x\n", uc->major, uc->minor, uc->subtype, le32toh(uc->valid)); if (le32toh(uc->valid) != 1) { device_printf(sc->sc_dev, "microcontroller initialization failed\n"); wpi_stop_locked(sc); } /* Save the address of the error log in SRAM. */ sc->errptr = le32toh(uc->errptr); break; } case WPI_STATE_CHANGED: { bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); uint32_t *status = (uint32_t *)(desc + 1); -#ifdef WPI_DEBUG + DPRINTF(sc, WPI_DEBUG_STATE, "state changed to %x\n", le32toh(*status)); -#endif + if (le32toh(*status) & 1) { ieee80211_runtask(ic, &sc->sc_radiooff_task); return; } break; } case WPI_START_SCAN: { bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); #ifdef WPI_DEBUG struct wpi_start_scan *scan = (struct wpi_start_scan *)(desc + 1); DPRINTF(sc, WPI_DEBUG_SCAN, "%s: scanning channel %d status %x\n", __func__, scan->chan, le32toh(scan->status)); #endif break; } case WPI_STOP_SCAN: { bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); #ifdef WPI_DEBUG struct wpi_stop_scan *scan = (struct wpi_stop_scan *)(desc + 1); DPRINTF(sc, WPI_DEBUG_SCAN, "scan finished nchan=%d status=%d chan=%d\n", scan->nchan, scan->status, scan->chan); #endif sc->sc_scan_timer = 0; WPI_UNLOCK(sc); ieee80211_scan_next(vap); WPI_LOCK(sc); break; } } - } while (sc->rxq.cur != hw); + } /* Tell the firmware what we have processed. */ wpi_update_rx_ring(sc); } /* * Process an INT_WAKEUP interrupt raised when the microcontroller wakes up * from power-down sleep mode. */ static void wpi_wakeup_intr(struct wpi_softc *sc) { int qid; DPRINTF(sc, WPI_DEBUG_PWRSAVE, "%s: ucode wakeup from power-down sleep\n", __func__); /* Wakeup RX and TX rings. */ if (sc->rxq.update) { - wpi_update_rx_ring(sc); sc->rxq.update = 0; + wpi_update_rx_ring(sc); } for (qid = 0; qid < WPI_NTXQUEUES; qid++) { struct wpi_tx_ring *ring = &sc->txq[qid]; if (ring->update) { - wpi_update_tx_ring(sc, ring); ring->update = 0; + wpi_update_tx_ring(sc, ring); } } WPI_CLRBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ); } /* * Dump the error log of the firmware when a firmware panic occurs. Although * we can't debug the firmware because it is neither open source nor free, it * can help us to identify certain classes of problems. */ static void wpi_fatal_intr(struct wpi_softc *sc) { struct wpi_fw_dump dump; uint32_t i, offset, count; const uint32_t size_errmsg = (sizeof (wpi_fw_errmsg) / sizeof ((wpi_fw_errmsg)[0])); /* Check that the error log address is valid. */ if (sc->errptr < WPI_FW_DATA_BASE || sc->errptr + sizeof (dump) > WPI_FW_DATA_BASE + WPI_FW_DATA_MAXSZ) { printf("%s: bad firmware error log address 0x%08x\n", __func__, sc->errptr); return; } if (wpi_nic_lock(sc) != 0) { printf("%s: could not read firmware error log\n", __func__); return; } /* Read number of entries in the log. */ count = wpi_mem_read(sc, sc->errptr); if (count == 0 || count * sizeof (dump) > WPI_FW_DATA_MAXSZ) { printf("%s: invalid count field (count = %u)\n", __func__, count); wpi_nic_unlock(sc); return; } /* Skip "count" field. */ offset = sc->errptr + sizeof (uint32_t); printf("firmware error log (count = %u):\n", count); for (i = 0; i < count; i++) { wpi_mem_read_region_4(sc, offset, (uint32_t *)&dump, sizeof (dump) / sizeof (uint32_t)); printf(" error type = \"%s\" (0x%08X)\n", (dump.desc < size_errmsg) ? wpi_fw_errmsg[dump.desc] : "UNKNOWN", dump.desc); printf(" error data = 0x%08X\n", dump.data); printf(" branch link = 0x%08X%08X\n", dump.blink[0], dump.blink[1]); printf(" interrupt link = 0x%08X%08X\n", dump.ilink[0], dump.ilink[1]); printf(" time = %u\n", dump.time); offset += sizeof (dump); } wpi_nic_unlock(sc); /* Dump driver status (TX and RX rings) while we're here. */ printf("driver status:\n"); for (i = 0; i < WPI_NTXQUEUES; i++) { struct wpi_tx_ring *ring = &sc->txq[i]; printf(" tx ring %2d: qid=%-2d cur=%-3d queued=%-3d\n", i, ring->qid, ring->cur, ring->queued); } printf(" rx ring: cur=%d\n", sc->rxq.cur); } static void wpi_intr(void *arg) { struct wpi_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; uint32_t r1, r2; WPI_LOCK(sc); /* Disable interrupts. */ WPI_WRITE(sc, WPI_INT_MASK, 0); r1 = WPI_READ(sc, WPI_INT); if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0) { WPI_UNLOCK(sc); return; /* Hardware gone! */ } r2 = WPI_READ(sc, WPI_FH_INT); DPRINTF(sc, WPI_DEBUG_INTR, "%s: reg1=0x%08x reg2=0x%08x\n", __func__, r1, r2); if (r1 == 0 && r2 == 0) goto done; /* Interrupt not for us. */ /* Acknowledge interrupts. */ WPI_WRITE(sc, WPI_INT, r1); WPI_WRITE(sc, WPI_FH_INT, r2); if (r1 & (WPI_INT_SW_ERR | WPI_INT_HW_ERR)) { struct ieee80211com *ic = ifp->if_l2com; device_printf(sc->sc_dev, "fatal firmware error\n"); wpi_fatal_intr(sc); DPRINTF(sc, WPI_DEBUG_HW, "(%s)\n", (r1 & WPI_INT_SW_ERR) ? "(Software Error)" : "(Hardware Error)"); ieee80211_runtask(ic, &sc->sc_reinittask); sc->flags &= ~WPI_FLAG_BUSY; WPI_UNLOCK(sc); return; } if ((r1 & (WPI_INT_FH_RX | WPI_INT_SW_RX)) || (r2 & WPI_FH_INT_RX)) wpi_notif_intr(sc); if (r1 & WPI_INT_ALIVE) wakeup(sc); /* Firmware is alive. */ if (r1 & WPI_INT_WAKEUP) wpi_wakeup_intr(sc); done: /* Re-enable interrupts. */ if (ifp->if_flags & IFF_UP) WPI_WRITE(sc, WPI_INT_MASK, WPI_INT_MASK_DEF); WPI_UNLOCK(sc); } static int wpi_cmd2(struct wpi_softc *sc, struct wpi_buf *buf) { + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; struct ieee80211_frame *wh; struct wpi_tx_cmd *cmd; struct wpi_tx_data *data; struct wpi_tx_desc *desc; struct wpi_tx_ring *ring; struct mbuf *m1; bus_dma_segment_t *seg, segs[WPI_MAX_SCATTER]; - u_int hdrlen; - int error, i, nsegs, pad, totlen; + int error, i, hdrspace, nsegs, totlen; WPI_LOCK_ASSERT(sc); DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); wh = mtod(buf->m, struct ieee80211_frame *); - hdrlen = ieee80211_anyhdrsize(wh); + hdrspace = ieee80211_anyhdrspace(ic, wh); totlen = buf->m->m_pkthdr.len; - if (hdrlen & 3) { - /* First segment length must be a multiple of 4. */ - pad = 4 - (hdrlen & 3); - } else - pad = 0; - ring = &sc->txq[buf->ac]; desc = &ring->desc[ring->cur]; data = &ring->data[ring->cur]; /* Prepare TX firmware command. */ cmd = &ring->cmd[ring->cur]; cmd->code = buf->code; cmd->flags = 0; cmd->qid = ring->qid; cmd->idx = ring->cur; memcpy(cmd->data, buf->data, buf->size); /* Save and trim IEEE802.11 header. */ - memcpy((uint8_t *)(cmd->data + buf->size), wh, hdrlen); - m_adj(buf->m, hdrlen); + memcpy((uint8_t *)(cmd->data + buf->size), wh, hdrspace); + m_adj(buf->m, hdrspace); error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, buf->m, segs, &nsegs, BUS_DMA_NOWAIT); if (error != 0 && error != EFBIG) { device_printf(sc->sc_dev, "%s: can't map mbuf (error %d)\n", __func__, error); m_freem(buf->m); return error; } if (error != 0) { /* Too many DMA segments, linearize mbuf. */ m1 = m_collapse(buf->m, M_NOWAIT, WPI_MAX_SCATTER); if (m1 == NULL) { device_printf(sc->sc_dev, "%s: could not defrag mbuf\n", __func__); m_freem(buf->m); return ENOBUFS; } buf->m = m1; error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, buf->m, segs, &nsegs, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_dev, "%s: can't map mbuf (error %d)\n", __func__, error); m_freem(buf->m); return error; } } data->m = buf->m; data->ni = buf->ni; DPRINTF(sc, WPI_DEBUG_XMIT, "%s: qid %d idx %d len %d nsegs %d\n", __func__, ring->qid, ring->cur, totlen, nsegs); /* Fill TX descriptor. */ - desc->nsegs = WPI_PAD32(totlen + pad) << 4 | (1 + nsegs); + desc->nsegs = WPI_PAD32(totlen) << 4 | (1 + nsegs); /* First DMA segment is used by the TX command. */ desc->segs[0].addr = htole32(data->cmd_paddr); - desc->segs[0].len = htole32(4 + buf->size + hdrlen + pad); + desc->segs[0].len = htole32(4 + buf->size + hdrspace); /* Other DMA segments are for data payload. */ seg = &segs[0]; for (i = 1; i <= nsegs; i++) { desc->segs[i].addr = htole32(seg->ds_addr); desc->segs[i].len = htole32(seg->ds_len); seg++; } bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map, BUS_DMASYNC_PREWRITE); bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, BUS_DMASYNC_PREWRITE); /* Kick TX ring. */ ring->cur = (ring->cur + 1) % WPI_TX_RING_COUNT; wpi_update_tx_ring(sc, ring); /* Mark TX ring as full if we reach a certain threshold. */ if (++ring->queued > WPI_TX_RING_HIMARK) sc->qfullmsk |= 1 << ring->qid; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); return 0; } /* * Construct the data packet for a transmit buffer. */ static int wpi_tx_data(struct wpi_softc *sc, struct mbuf *m, struct ieee80211_node *ni) { const struct ieee80211_txparam *tp; struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct wpi_node *wn = (void *)ni; struct ieee80211_channel *chan; struct ieee80211_frame *wh; struct ieee80211_key *k = NULL; struct wpi_cmd_data tx; struct wpi_buf tx_data; uint32_t flags; uint16_t qos; uint8_t tid, type; - int ac, error, rate, ismcast, totlen; + int ac, error, rate, ismcast, hdrlen, totlen; wh = mtod(m, struct ieee80211_frame *); + hdrlen = ieee80211_anyhdrsize(wh); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); /* Select EDCA Access Category and TX ring for this frame. */ if (IEEE80211_QOS_HAS_SEQ(wh)) { qos = ((const struct ieee80211_qosframe *)wh)->i_qos[0]; tid = qos & IEEE80211_QOS_TID; } else { qos = 0; tid = 0; } ac = M_WME_GETAC(m); chan = (ni->ni_chan != IEEE80211_CHAN_ANYC) ? ni->ni_chan : ic->ic_curchan; tp = &vap->iv_txparms[ieee80211_chan2mode(chan)]; /* Choose a TX rate index. */ if (type == IEEE80211_FC0_TYPE_MGT) rate = tp->mgmtrate; else if (ismcast) rate = tp->mcastrate; else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) rate = tp->ucastrate; else if (m->m_flags & M_EAPOL) rate = tp->mgmtrate; else { /* XXX pass pktlen */ (void) ieee80211_ratectl_rate(ni, NULL, 0); rate = ni->ni_txrate; } /* Encrypt the frame if need be. */ if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { /* Retrieve key for TX. */ k = ieee80211_crypto_encap(ni, m); if (k == NULL) { error = ENOBUFS; goto fail; } /* 802.11 header may have moved. */ wh = mtod(m, struct ieee80211_frame *); } - totlen = m->m_pkthdr.len; + totlen = m->m_pkthdr.len - (hdrlen & 3); if (ieee80211_radiotap_active_vap(vap)) { struct wpi_tx_radiotap_header *tap = &sc->sc_txtap; - tap->wt_flags = 0; + tap->wt_flags = IEEE80211_RADIOTAP_F_DATAPAD; tap->wt_rate = rate; if (k != NULL) tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; ieee80211_radiotap_tx(vap, m); } flags = 0; if (!ismcast) { /* Unicast frame, check if an ACK is expected. */ if (!qos || (qos & IEEE80211_QOS_ACKPOLICY) != IEEE80211_QOS_ACKPOLICY_NOACK) flags |= WPI_TX_NEED_ACK; } + if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) + flags |= WPI_TX_MORE_FRAG; /* Cannot happen yet. */ + /* Check if frame must be protected using RTS/CTS or CTS-to-self. */ if (!ismcast) { /* NB: Group frames are sent using CCK in 802.11b/g. */ if (totlen + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) { flags |= WPI_TX_NEED_RTS; } else if ((ic->ic_flags & IEEE80211_F_USEPROT) && WPI_RATE_IS_OFDM(rate)) { if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) flags |= WPI_TX_NEED_CTS; else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) flags |= WPI_TX_NEED_RTS; } if (flags & (WPI_TX_NEED_RTS | WPI_TX_NEED_CTS)) flags |= WPI_TX_FULL_TXOP; } memset(&tx, 0, sizeof (struct wpi_cmd_data)); if (type == IEEE80211_FC0_TYPE_MGT) { uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; /* Tell HW to set timestamp in probe responses. */ if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) flags |= WPI_TX_INSERT_TSTAMP; if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) tx.timeout = htole16(3); else tx.timeout = htole16(2); } if (ismcast || type != IEEE80211_FC0_TYPE_DATA) tx.id = WPI_ID_BROADCAST; else { if (wn->id == WPI_ID_UNDEFINED && (vap->iv_opmode == IEEE80211_M_IBSS || vap->iv_opmode == IEEE80211_M_AHDEMO)) { error = wpi_add_ibss_node(sc, ni); if (error != 0) { device_printf(sc->sc_dev, "%s: could not add IBSS node, error %d\n", __func__, error); goto fail; } } if (wn->id == WPI_ID_UNDEFINED) { device_printf(sc->sc_dev, "%s: undefined node id\n", __func__); error = EINVAL; goto fail; } tx.id = wn->id; } if (type != IEEE80211_FC0_TYPE_MGT) tx.data_ntries = tp->maxretry; tx.len = htole16(totlen); tx.flags = htole32(flags); tx.plcp = rate2plcp(rate); tx.tid = tid; tx.lifetime = htole32(WPI_LIFETIME_INFINITE); tx.ofdm_mask = 0xff; tx.cck_mask = 0x0f; tx.rts_ntries = 7; if (k != NULL && k->wk_cipher->ic_cipher == IEEE80211_CIPHER_AES_CCM) { if (!(k->wk_flags & IEEE80211_KEY_SWCRYPT)) { tx.security = WPI_CIPHER_CCMP; memcpy(tx.key, k->wk_key, k->wk_keylen); } } tx_data.data = &tx; tx_data.ni = ni; tx_data.m = m; tx_data.size = sizeof(tx); tx_data.code = WPI_CMD_TX_DATA; tx_data.ac = ac; return wpi_cmd2(sc, &tx_data); fail: m_freem(m); return error; } static int wpi_tx_data_raw(struct wpi_softc *sc, struct mbuf *m, struct ieee80211_node *ni, const struct ieee80211_bpf_params *params) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_frame *wh; struct wpi_cmd_data tx; struct wpi_buf tx_data; uint32_t flags; uint8_t type; - int ac, rate, totlen; + int ac, rate, hdrlen, totlen; wh = mtod(m, struct ieee80211_frame *); + hdrlen = ieee80211_anyhdrsize(wh); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; - totlen = m->m_pkthdr.len; + totlen = m->m_pkthdr.len - (hdrlen & 3); ac = params->ibp_pri & 3; /* Choose a TX rate index. */ rate = params->ibp_rate0; flags = 0; if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) flags |= WPI_TX_NEED_ACK; if (params->ibp_flags & IEEE80211_BPF_RTS) flags |= WPI_TX_NEED_RTS; if (params->ibp_flags & IEEE80211_BPF_CTS) flags |= WPI_TX_NEED_CTS; if (flags & (WPI_TX_NEED_RTS | WPI_TX_NEED_CTS)) flags |= WPI_TX_FULL_TXOP; if (ieee80211_radiotap_active_vap(vap)) { struct wpi_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = rate; + if (params->ibp_flags & IEEE80211_BPF_DATAPAD) + tap->wt_flags |= IEEE80211_RADIOTAP_F_DATAPAD; ieee80211_radiotap_tx(vap, m); } memset(&tx, 0, sizeof (struct wpi_cmd_data)); if (type == IEEE80211_FC0_TYPE_MGT) { uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; /* Tell HW to set timestamp in probe responses. */ if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) flags |= WPI_TX_INSERT_TSTAMP; if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) tx.timeout = htole16(3); else tx.timeout = htole16(2); } tx.len = htole16(totlen); tx.flags = htole32(flags); tx.plcp = rate2plcp(rate); tx.id = WPI_ID_BROADCAST; tx.lifetime = htole32(WPI_LIFETIME_INFINITE); tx.rts_ntries = params->ibp_try1; tx.data_ntries = params->ibp_try0; tx_data.data = &tx; tx_data.ni = ni; tx_data.m = m; tx_data.size = sizeof(tx); tx_data.code = WPI_CMD_TX_DATA; tx_data.ac = ac; return wpi_cmd2(sc, &tx_data); } static int wpi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = ic->ic_ifp; struct wpi_softc *sc = ifp->if_softc; int error = 0; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { ieee80211_free_node(ni); m_freem(m); return ENETDOWN; } WPI_LOCK(sc); if (params == NULL) { /* * Legacy path; interpret frame contents to decide * precisely how to send the frame. */ error = wpi_tx_data(sc, m, ni); } else { /* * Caller supplied explicit parameters to use in * sending the frame. */ error = wpi_tx_data_raw(sc, m, ni, params); } WPI_UNLOCK(sc); if (error != 0) { /* NB: m is reclaimed on tx failure */ ieee80211_free_node(ni); if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); return error; } sc->sc_tx_timer = 5; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); return 0; } /** * Process data waiting to be sent on the IFNET output queue */ static void wpi_start(struct ifnet *ifp) { struct wpi_softc *sc = ifp->if_softc; WPI_LOCK(sc); wpi_start_locked(ifp); WPI_UNLOCK(sc); } static void wpi_start_locked(struct ifnet *ifp) { struct wpi_softc *sc = ifp->if_softc; struct ieee80211_node *ni; struct mbuf *m; WPI_LOCK_ASSERT(sc); DPRINTF(sc, WPI_DEBUG_XMIT, "%s: called\n", __func__); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || (ifp->if_drv_flags & IFF_DRV_OACTIVE)) return; for (;;) { if (sc->qfullmsk != 0) { ifp->if_drv_flags |= IFF_DRV_OACTIVE; break; } IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) break; ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; if (wpi_tx_data(sc, m, ni) != 0) { WPI_UNLOCK(sc); ieee80211_free_node(ni); WPI_LOCK(sc); if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); } else sc->sc_tx_timer = 5; } DPRINTF(sc, WPI_DEBUG_XMIT, "%s: done\n", __func__); } static void wpi_watchdog_rfkill(void *arg) { struct wpi_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; DPRINTF(sc, WPI_DEBUG_WATCHDOG, "RFkill Watchdog: tick\n"); /* No need to lock firmware memory. */ if ((wpi_prph_read(sc, WPI_APMG_RFKILL) & 0x1) == 0) { /* Radio kill switch is still off. */ callout_reset(&sc->watchdog_rfkill, hz, wpi_watchdog_rfkill, sc); } else ieee80211_runtask(ic, &sc->sc_radioon_task); } /** * Called every second, wpi_watchdog used by the watch dog timer * to check that the card is still alive */ static void wpi_watchdog(void *arg) { struct wpi_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; DPRINTF(sc, WPI_DEBUG_WATCHDOG, "Watchdog: tick\n"); if (sc->sc_tx_timer > 0) { if (--sc->sc_tx_timer == 0) { if_printf(ifp, "device timeout\n"); if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); ieee80211_runtask(ic, &sc->sc_reinittask); } } if (sc->sc_scan_timer > 0) { struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); if (--sc->sc_scan_timer == 0 && vap != NULL) { if_printf(ifp, "scan timeout\n"); ieee80211_cancel_scan(vap); ieee80211_runtask(ic, &sc->sc_reinittask); } } if (ifp->if_drv_flags & IFF_DRV_RUNNING) callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc); } static int wpi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct wpi_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct ifreq *ifr = (struct ifreq *) data; int error = 0, startall = 0, stop = 0; switch (cmd) { case SIOCGIFADDR: error = ether_ioctl(ifp, cmd, data); break; case SIOCSIFFLAGS: WPI_LOCK(sc); if (ifp->if_flags & IFF_UP) { if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { wpi_init_locked(sc); if (WPI_READ(sc, WPI_GP_CNTRL) & WPI_GP_CNTRL_RFKILL) startall = 1; else stop = 1; } } else if (ifp->if_drv_flags & IFF_DRV_RUNNING) wpi_stop_locked(sc); WPI_UNLOCK(sc); if (startall) ieee80211_start_all(ic); else if (vap != NULL && stop) ieee80211_stop(vap); break; case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); break; default: error = EINVAL; break; } return error; } /* * Send a command to the firmware. */ static int wpi_cmd(struct wpi_softc *sc, int code, const void *buf, size_t size, int async) { struct wpi_tx_ring *ring = &sc->txq[4]; struct wpi_tx_desc *desc; struct wpi_tx_data *data; struct wpi_tx_cmd *cmd; struct mbuf *m; bus_addr_t paddr; int totlen, error; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); if (async == 0) WPI_LOCK_ASSERT(sc); DPRINTF(sc, WPI_DEBUG_CMD, "wpi_cmd %s size %zu async %d\n", wpi_cmd_str(code), size, async); if (sc->flags & WPI_FLAG_BUSY) { device_printf(sc->sc_dev, "%s: cmd %d not sent, busy\n", __func__, code); return EAGAIN; } sc->flags |= WPI_FLAG_BUSY; desc = &ring->desc[ring->cur]; data = &ring->data[ring->cur]; totlen = 4 + size; if (size > sizeof cmd->data) { /* Command is too large to fit in a descriptor. */ if (totlen > MCLBYTES) return EINVAL; m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); if (m == NULL) return ENOMEM; cmd = mtod(m, struct wpi_tx_cmd *); error = bus_dmamap_load(ring->data_dmat, data->map, cmd, totlen, wpi_dma_map_addr, &paddr, BUS_DMA_NOWAIT); if (error != 0) { m_freem(m); return error; } data->m = m; } else { cmd = &ring->cmd[ring->cur]; paddr = data->cmd_paddr; } cmd->code = code; cmd->flags = 0; cmd->qid = ring->qid; cmd->idx = ring->cur; memcpy(cmd->data, buf, size); desc->nsegs = 1 + (WPI_PAD32(size) << 4); desc->segs[0].addr = htole32(paddr); desc->segs[0].len = htole32(totlen); if (size > sizeof cmd->data) { bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); } else { bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map, BUS_DMASYNC_PREWRITE); } bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, BUS_DMASYNC_PREWRITE); /* Kick command ring. */ ring->cur = (ring->cur + 1) % WPI_TX_RING_COUNT; wpi_update_tx_ring(sc, ring); DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); if (async) { sc->flags &= ~WPI_FLAG_BUSY; return 0; } return msleep(cmd, &sc->sc_mtx, PCATCH, "wpicmd", hz); } /* * Configure HW multi-rate retries. */ static int wpi_mrr_setup(struct wpi_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct wpi_mrr_setup mrr; int i, error; /* CCK rates (not used with 802.11a). */ for (i = WPI_RIDX_CCK1; i <= WPI_RIDX_CCK11; i++) { mrr.rates[i].flags = 0; mrr.rates[i].plcp = wpi_ridx_to_plcp[i]; /* Fallback to the immediate lower CCK rate (if any.) */ mrr.rates[i].next = (i == WPI_RIDX_CCK1) ? WPI_RIDX_CCK1 : i - 1; /* Try one time at this rate before falling back to "next". */ mrr.rates[i].ntries = 1; } /* OFDM rates (not used with 802.11b). */ for (i = WPI_RIDX_OFDM6; i <= WPI_RIDX_OFDM54; i++) { mrr.rates[i].flags = 0; mrr.rates[i].plcp = wpi_ridx_to_plcp[i]; /* Fallback to the immediate lower rate (if any.) */ /* We allow fallback from OFDM/6 to CCK/2 in 11b/g mode. */ mrr.rates[i].next = (i == WPI_RIDX_OFDM6) ? ((ic->ic_curmode == IEEE80211_MODE_11A) ? WPI_RIDX_OFDM6 : WPI_RIDX_CCK2) : i - 1; /* Try one time at this rate before falling back to "next". */ mrr.rates[i].ntries = 1; } /* Setup MRR for control frames. */ mrr.which = htole32(WPI_MRR_CTL); error = wpi_cmd(sc, WPI_CMD_MRR_SETUP, &mrr, sizeof mrr, 0); if (error != 0) { device_printf(sc->sc_dev, "could not setup MRR for control frames\n"); return error; } /* Setup MRR for data frames. */ mrr.which = htole32(WPI_MRR_DATA); error = wpi_cmd(sc, WPI_CMD_MRR_SETUP, &mrr, sizeof mrr, 0); if (error != 0) { device_printf(sc->sc_dev, "could not setup MRR for data frames\n"); return error; } return 0; } static int wpi_add_node(struct wpi_softc *sc, struct ieee80211_node *ni) { struct ieee80211com *ic = ni->ni_ic; struct wpi_node *wn = (void *)ni; struct wpi_node_info node; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); if (wn->id == WPI_ID_UNDEFINED) return EINVAL; memset(&node, 0, sizeof node); IEEE80211_ADDR_COPY(node.macaddr, ni->ni_bssid); node.id = wn->id; node.plcp = (ic->ic_curmode == IEEE80211_MODE_11A) ? wpi_ridx_to_plcp[WPI_RIDX_OFDM6] : wpi_ridx_to_plcp[WPI_RIDX_CCK1]; node.action = htole32(WPI_ACTION_SET_RATE); node.antenna = WPI_ANTENNA_BOTH; return wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 1); } /* * Broadcast node is used to send group-addressed and management frames. */ static int wpi_add_broadcast_node(struct wpi_softc *sc, int async) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct wpi_node_info node; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); memset(&node, 0, sizeof node); IEEE80211_ADDR_COPY(node.macaddr, ifp->if_broadcastaddr); node.id = WPI_ID_BROADCAST; node.plcp = (ic->ic_curmode == IEEE80211_MODE_11A) ? wpi_ridx_to_plcp[WPI_RIDX_OFDM6] : wpi_ridx_to_plcp[WPI_RIDX_CCK1]; node.action = htole32(WPI_ACTION_SET_RATE); node.antenna = WPI_ANTENNA_BOTH; return wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, async); } static int wpi_add_ibss_node(struct wpi_softc *sc, struct ieee80211_node *ni) { struct wpi_node *wn = (void *)ni; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); if (wn->id != WPI_ID_UNDEFINED) return EINVAL; wn->id = alloc_unrl(sc->sc_unr); if (wn->id == (uint8_t)-1) return ENOBUFS; return wpi_add_node(sc, ni); } static void wpi_del_node(struct wpi_softc *sc, struct ieee80211_node *ni) { struct wpi_node *wn = (void *)ni; struct wpi_cmd_del_node node; int error; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); if (wn->id == WPI_ID_UNDEFINED) { device_printf(sc->sc_dev, "%s: undefined node id passed\n", __func__); return; } memset(&node, 0, sizeof node); IEEE80211_ADDR_COPY(node.macaddr, ni->ni_bssid); node.count = 1; error = wpi_cmd(sc, WPI_CMD_DEL_NODE, &node, sizeof node, 1); if (error != 0) { device_printf(sc->sc_dev, "%s: could not delete node %u, error %d\n", __func__, wn->id, error); } } static int wpi_updateedca(struct ieee80211com *ic) { #define WPI_EXP2(x) ((1 << (x)) - 1) /* CWmin = 2^ECWmin - 1 */ struct wpi_softc *sc = ic->ic_ifp->if_softc; struct wpi_edca_params cmd; int aci, error; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); memset(&cmd, 0, sizeof cmd); cmd.flags = htole32(WPI_EDCA_UPDATE); for (aci = 0; aci < WME_NUM_AC; aci++) { const struct wmeParams *ac = &ic->ic_wme.wme_chanParams.cap_wmeParams[aci]; cmd.ac[aci].aifsn = ac->wmep_aifsn; cmd.ac[aci].cwmin = htole16(WPI_EXP2(ac->wmep_logcwmin)); cmd.ac[aci].cwmax = htole16(WPI_EXP2(ac->wmep_logcwmax)); cmd.ac[aci].txoplimit = htole16(IEEE80211_TXOP_TO_US(ac->wmep_txopLimit)); DPRINTF(sc, WPI_DEBUG_EDCA, "setting WME for queue %d aifsn=%d cwmin=%d cwmax=%d " "txoplimit=%d\n", aci, cmd.ac[aci].aifsn, cmd.ac[aci].cwmin, cmd.ac[aci].cwmax, cmd.ac[aci].txoplimit); } IEEE80211_UNLOCK(ic); WPI_LOCK(sc); error = wpi_cmd(sc, WPI_CMD_EDCA_PARAMS, &cmd, sizeof cmd, 1); WPI_UNLOCK(sc); IEEE80211_LOCK(ic); DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); return error; #undef WPI_EXP2 } static void wpi_set_promisc(struct wpi_softc *sc) { struct ifnet *ifp = sc->sc_ifp; uint32_t promisc_filter; promisc_filter = WPI_FILTER_PROMISC | WPI_FILTER_CTL; if (ifp->if_flags & IFF_PROMISC) sc->rxon.filter |= htole32(promisc_filter); else sc->rxon.filter &= ~htole32(promisc_filter); } static void wpi_update_promisc(struct ifnet *ifp) { struct wpi_softc *sc = ifp->if_softc; wpi_set_promisc(sc); WPI_LOCK(sc); if (wpi_send_rxon(sc, 1, 1) != 0) { device_printf(sc->sc_dev, "%s: could not send RXON\n", __func__); } WPI_UNLOCK(sc); } static void wpi_update_mcast(struct ifnet *ifp) { /* Ignore */ } static void wpi_set_led(struct wpi_softc *sc, uint8_t which, uint8_t off, uint8_t on) { struct wpi_cmd_led led; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); led.which = which; led.unit = htole32(100000); /* on/off in unit of 100ms */ led.off = off; led.on = on; (void)wpi_cmd(sc, WPI_CMD_SET_LED, &led, sizeof led, 1); } static int wpi_set_timing(struct wpi_softc *sc, struct ieee80211_node *ni) { struct wpi_cmd_timing cmd; uint64_t val, mod; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); memset(&cmd, 0, sizeof cmd); memcpy(&cmd.tstamp, ni->ni_tstamp.data, sizeof (uint64_t)); cmd.bintval = htole16(ni->ni_intval); cmd.lintval = htole16(10); /* Compute remaining time until next beacon. */ val = (uint64_t)ni->ni_intval * IEEE80211_DUR_TU; mod = le64toh(cmd.tstamp) % val; cmd.binitval = htole32((uint32_t)(val - mod)); DPRINTF(sc, WPI_DEBUG_RESET, "timing bintval=%u tstamp=%ju, init=%u\n", ni->ni_intval, le64toh(cmd.tstamp), (uint32_t)(val - mod)); return wpi_cmd(sc, WPI_CMD_TIMING, &cmd, sizeof cmd, 1); } /* * This function is called periodically (every 60 seconds) to adjust output * power to temperature changes. */ static void wpi_power_calibration(struct wpi_softc *sc) { int temp; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); /* Update sensor data. */ temp = (int)WPI_READ(sc, WPI_UCODE_GP2); DPRINTF(sc, WPI_DEBUG_TEMP, "Temp in calibration is: %d\n", temp); /* Sanity-check read value. */ if (temp < -260 || temp > 25) { /* This can't be correct, ignore. */ DPRINTF(sc, WPI_DEBUG_TEMP, "out-of-range temperature reported: %d\n", temp); return; } DPRINTF(sc, WPI_DEBUG_TEMP, "temperature %d->%d\n", sc->temp, temp); /* Adjust Tx power if need be. */ if (abs(temp - sc->temp) <= 6) return; sc->temp = temp; if (wpi_set_txpower(sc, 1) != 0) { /* just warn, too bad for the automatic calibration... */ device_printf(sc->sc_dev,"could not adjust Tx power\n"); } } /* * Set TX power for current channel. */ static int wpi_set_txpower(struct wpi_softc *sc, int async) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211_channel *ch; struct wpi_power_group *group; struct wpi_cmd_txpower cmd; uint8_t chan; int idx, i; /* Retrieve current channel from last RXON. */ chan = sc->rxon.chan; ch = &ic->ic_channels[chan]; /* Find the TX power group to which this channel belongs. */ if (IEEE80211_IS_CHAN_5GHZ(ch)) { for (group = &sc->groups[1]; group < &sc->groups[4]; group++) if (chan <= group->chan) break; } else group = &sc->groups[0]; memset(&cmd, 0, sizeof cmd); cmd.band = IEEE80211_IS_CHAN_5GHZ(ch) ? 0 : 1; cmd.chan = htole16(chan); /* Set TX power for all OFDM and CCK rates. */ for (i = 0; i <= WPI_RIDX_MAX ; i++) { /* Retrieve TX power for this channel/rate. */ idx = wpi_get_power_index(sc, group, ch, i); cmd.rates[i].plcp = wpi_ridx_to_plcp[i]; if (IEEE80211_IS_CHAN_5GHZ(ch)) { cmd.rates[i].rf_gain = wpi_rf_gain_5ghz[idx]; cmd.rates[i].dsp_gain = wpi_dsp_gain_5ghz[idx]; } else { cmd.rates[i].rf_gain = wpi_rf_gain_2ghz[idx]; cmd.rates[i].dsp_gain = wpi_dsp_gain_2ghz[idx]; } DPRINTF(sc, WPI_DEBUG_TEMP, "chan %d/ridx %d: power index %d\n", chan, i, idx); } return wpi_cmd(sc, WPI_CMD_TXPOWER, &cmd, sizeof cmd, async); } /* * Determine Tx power index for a given channel/rate combination. * This takes into account the regulatory information from EEPROM and the * current temperature. */ static int wpi_get_power_index(struct wpi_softc *sc, struct wpi_power_group *group, struct ieee80211_channel *c, int ridx) { /* Fixed-point arithmetic division using a n-bit fractional part. */ #define fdivround(a, b, n) \ ((((1 << n) * (a)) / (b) + (1 << n) / 2) / (1 << n)) /* Linear interpolation. */ #define interpolate(x, x1, y1, x2, y2, n) \ ((y1) + fdivround(((x) - (x1)) * ((y2) - (y1)), (x2) - (x1), n)) struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct wpi_power_sample *sample; int pwr, idx; u_int chan; /* Get channel number. */ chan = ieee80211_chan2ieee(ic, c); /* Default TX power is group maximum TX power minus 3dB. */ pwr = group->maxpwr / 2; /* Decrease TX power for highest OFDM rates to reduce distortion. */ switch (ridx) { case WPI_RIDX_OFDM36: pwr -= IEEE80211_IS_CHAN_2GHZ(c) ? 0 : 5; break; case WPI_RIDX_OFDM48: pwr -= IEEE80211_IS_CHAN_2GHZ(c) ? 7 : 10; break; case WPI_RIDX_OFDM54: pwr -= IEEE80211_IS_CHAN_2GHZ(c) ? 9 : 12; break; } /* Never exceed the channel maximum allowed TX power. */ pwr = min(pwr, sc->maxpwr[chan]); /* Retrieve TX power index into gain tables from samples. */ for (sample = group->samples; sample < &group->samples[3]; sample++) if (pwr > sample[1].power) break; /* Fixed-point linear interpolation using a 19-bit fractional part. */ idx = interpolate(pwr, sample[0].power, sample[0].index, sample[1].power, sample[1].index, 19); /*- * Adjust power index based on current temperature: * - if cooler than factory-calibrated: decrease output power * - if warmer than factory-calibrated: increase output power */ idx -= (sc->temp - group->temp) * 11 / 100; /* Decrease TX power for CCK rates (-5dB). */ if (ridx >= WPI_RIDX_CCK1) idx += 10; /* Make sure idx stays in a valid range. */ if (idx < 0) return 0; if (idx > WPI_MAX_PWR_INDEX) return WPI_MAX_PWR_INDEX; return idx; #undef interpolate #undef fdivround } /* * Set STA mode power saving level (between 0 and 5). * Level 0 is CAM (Continuously Aware Mode), 5 is for maximum power saving. */ static int wpi_set_pslevel(struct wpi_softc *sc, uint8_t dtim, int level, int async) { struct wpi_pmgt_cmd cmd; const struct wpi_pmgt *pmgt; uint32_t max, skip_dtim; uint32_t reg; int i; DPRINTF(sc, WPI_DEBUG_PWRSAVE, "%s: dtim=%d, level=%d, async=%d\n", __func__, dtim, level, async); /* Select which PS parameters to use. */ if (dtim <= 10) pmgt = &wpi_pmgt[0][level]; else pmgt = &wpi_pmgt[1][level]; memset(&cmd, 0, sizeof cmd); if (level != 0) /* not CAM */ cmd.flags |= htole16(WPI_PS_ALLOW_SLEEP); /* Retrieve PCIe Active State Power Management (ASPM). */ reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + 0x10, 1); if (!(reg & 0x1)) /* L0s Entry disabled. */ cmd.flags |= htole16(WPI_PS_PCI_PMGT); cmd.rxtimeout = htole32(pmgt->rxtimeout * IEEE80211_DUR_TU); cmd.txtimeout = htole32(pmgt->txtimeout * IEEE80211_DUR_TU); if (dtim == 0) { dtim = 1; skip_dtim = 0; } else skip_dtim = pmgt->skip_dtim; if (skip_dtim != 0) { cmd.flags |= htole16(WPI_PS_SLEEP_OVER_DTIM); max = pmgt->intval[4]; if (max == (uint32_t)-1) max = dtim * (skip_dtim + 1); else if (max > dtim) max = (max / dtim) * dtim; } else max = dtim; for (i = 0; i < 5; i++) cmd.intval[i] = htole32(MIN(max, pmgt->intval[i])); return wpi_cmd(sc, WPI_CMD_SET_POWER_MODE, &cmd, sizeof cmd, async); } static int wpi_send_btcoex(struct wpi_softc *sc) { struct wpi_bluetooth cmd; memset(&cmd, 0, sizeof cmd); cmd.flags = WPI_BT_COEX_MODE_4WIRE; cmd.lead_time = WPI_BT_LEAD_TIME_DEF; cmd.max_kill = WPI_BT_MAX_KILL_DEF; DPRINTF(sc, WPI_DEBUG_RESET, "%s: configuring bluetooth coexistence\n", __func__); return wpi_cmd(sc, WPI_CMD_BT_COEX, &cmd, sizeof(cmd), 0); } static int wpi_send_rxon(struct wpi_softc *sc, int assoc, int async) { int error; if (assoc && (sc->rxon.filter & htole32(WPI_FILTER_BSS))) { struct wpi_assoc rxon_assoc; rxon_assoc.flags = sc->rxon.flags; rxon_assoc.filter = sc->rxon.filter; rxon_assoc.ofdm_mask = sc->rxon.ofdm_mask; rxon_assoc.cck_mask = sc->rxon.cck_mask; rxon_assoc.reserved = 0; error = wpi_cmd(sc, WPI_CMD_RXON_ASSOC, &rxon_assoc, sizeof (struct wpi_assoc), async); } else { error = wpi_cmd(sc, WPI_CMD_RXON, &sc->rxon, sizeof (struct wpi_rxon), async); } if (error != 0) { device_printf(sc->sc_dev, "RXON command failed, error %d\n", error); return error; } /* Configuration has changed, set Tx power accordingly. */ if ((error = wpi_set_txpower(sc, async)) != 0) { device_printf(sc->sc_dev, "%s: could not set TX power, error %d\n", __func__, error); return error; } if (!(sc->rxon.filter & htole32(WPI_FILTER_BSS))) { /* Add broadcast node. */ error = wpi_add_broadcast_node(sc, async); if (error != 0) { device_printf(sc->sc_dev, "could not add broadcast node, error %d\n", error); return error; } } return 0; } /** * Configure the card to listen to a particular channel, this transisions the * card in to being able to receive frames from remote devices. */ static int wpi_config(struct wpi_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint32_t flags; int error; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); /* Set power saving level to CAM during initialization. */ if ((error = wpi_set_pslevel(sc, 0, 0, 0)) != 0) { device_printf(sc->sc_dev, "%s: could not set power saving level\n", __func__); return error; } /* Configure bluetooth coexistence. */ if ((error = wpi_send_btcoex(sc)) != 0) { device_printf(sc->sc_dev, "could not configure bluetooth coexistence\n"); return error; } /* Configure adapter. */ memset(&sc->rxon, 0, sizeof (struct wpi_rxon)); IEEE80211_ADDR_COPY(sc->rxon.myaddr, IF_LLADDR(ifp)); /* Set default channel. */ sc->rxon.chan = ieee80211_chan2ieee(ic, ic->ic_curchan); sc->rxon.flags = htole32(WPI_RXON_TSF | WPI_RXON_CTS_TO_SELF); if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) sc->rxon.flags |= htole32(WPI_RXON_AUTO | WPI_RXON_24GHZ); + sc->rxon.filter = WPI_FILTER_MULTICAST; switch (ic->ic_opmode) { case IEEE80211_M_STA: sc->rxon.mode = WPI_MODE_STA; - sc->rxon.filter = htole32(WPI_FILTER_MULTICAST); break; case IEEE80211_M_IBSS: sc->rxon.mode = WPI_MODE_IBSS; - sc->rxon.filter = htole32(WPI_FILTER_BEACON | - WPI_FILTER_MULTICAST); + sc->rxon.filter |= WPI_FILTER_BEACON; break; /* XXX workaround for passive channels selection */ case IEEE80211_M_AHDEMO: - sc->rxon.filter = htole32(WPI_FILTER_MULTICAST); - /* FALLTHROUGH */ case IEEE80211_M_HOSTAP: sc->rxon.mode = WPI_MODE_HOSTAP; break; case IEEE80211_M_MONITOR: sc->rxon.mode = WPI_MODE_MONITOR; - sc->rxon.filter = htole32(WPI_FILTER_MULTICAST); break; default: device_printf(sc->sc_dev, "unknown opmode %d\n", ic->ic_opmode); return EINVAL; } + sc->rxon.filter = htole32(sc->rxon.filter); wpi_set_promisc(sc); sc->rxon.cck_mask = 0x0f; /* not yet negotiated */ sc->rxon.ofdm_mask = 0xff; /* not yet negotiated */ if ((error = wpi_send_rxon(sc, 0, 0)) != 0) { device_printf(sc->sc_dev, "%s: could not send RXON\n", __func__); return error; } /* Setup rate scalling. */ if ((error = wpi_mrr_setup(sc)) != 0) { device_printf(sc->sc_dev, "could not setup MRR, error %d\n", error); return error; } /* Disable beacon notifications (unused). */ flags = WPI_STATISTICS_BEACON_DISABLE; error = wpi_cmd(sc, WPI_CMD_GET_STATISTICS, &flags, sizeof flags, 1); if (error != 0) { device_printf(sc->sc_dev, "could not disable beacon statistics, error %d\n", error); return error; } DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); return 0; } static uint16_t wpi_get_active_dwell_time(struct wpi_softc *sc, struct ieee80211_channel *c, uint8_t n_probes) { /* No channel? Default to 2GHz settings. */ if (c == NULL || IEEE80211_IS_CHAN_2GHZ(c)) { return (WPI_ACTIVE_DWELL_TIME_2GHZ + WPI_ACTIVE_DWELL_FACTOR_2GHZ * (n_probes + 1)); } /* 5GHz dwell time. */ return (WPI_ACTIVE_DWELL_TIME_5GHZ + WPI_ACTIVE_DWELL_FACTOR_5GHZ * (n_probes + 1)); } /* * Limit the total dwell time to 85% of the beacon interval. * * Returns the dwell time in milliseconds. */ static uint16_t wpi_limit_dwell(struct wpi_softc *sc, uint16_t dwell_time) { struct ieee80211com *ic = sc->sc_ifp->if_l2com; struct ieee80211vap *vap = NULL; int bintval = 0; /* bintval is in TU (1.024mS) */ if (! TAILQ_EMPTY(&ic->ic_vaps)) { vap = TAILQ_FIRST(&ic->ic_vaps); bintval = vap->iv_bss->ni_intval; } /* * If it's non-zero, we should calculate the minimum of * it and the DWELL_BASE. * * XXX Yes, the math should take into account that bintval * is 1.024mS, not 1mS.. */ if (bintval > 0) { DPRINTF(sc, WPI_DEBUG_SCAN, "%s: bintval=%d\n", __func__, bintval); return (MIN(WPI_PASSIVE_DWELL_BASE, ((bintval * 85) / 100))); } /* No association context? Default. */ return (WPI_PASSIVE_DWELL_BASE); } static uint16_t wpi_get_passive_dwell_time(struct wpi_softc *sc, struct ieee80211_channel *c) { uint16_t passive; if (c == NULL || IEEE80211_IS_CHAN_2GHZ(c)) passive = WPI_PASSIVE_DWELL_BASE + WPI_PASSIVE_DWELL_TIME_2GHZ; else passive = WPI_PASSIVE_DWELL_BASE + WPI_PASSIVE_DWELL_TIME_5GHZ; /* Clamp to the beacon interval if we're associated. */ return (wpi_limit_dwell(sc, passive)); } /* * Send a scan request to the firmware. */ static int wpi_scan(struct wpi_softc *sc, struct ieee80211_channel *c) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211_scan_state *ss = ic->ic_scan; struct wpi_scan_hdr *hdr; struct wpi_cmd_data *tx; struct wpi_scan_essid *essids; struct wpi_scan_chan *chan; struct ieee80211_frame *wh; struct ieee80211_rateset *rs; uint16_t dwell_active, dwell_passive; uint8_t *buf, *frm; int buflen, error, i, nssid; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); /* * We are absolutely not allowed to send a scan command when another * scan command is pending. */ if (sc->sc_scan_timer) { device_printf(sc->sc_dev, "%s: called whilst scanning!\n", __func__); return (EAGAIN); } buf = malloc(WPI_SCAN_MAXSZ, M_DEVBUF, M_NOWAIT | M_ZERO); if (buf == NULL) { device_printf(sc->sc_dev, "%s: could not allocate buffer for scan command\n", __func__); return ENOMEM; } hdr = (struct wpi_scan_hdr *)buf; /* * Move to the next channel if no packets are received within 10 msecs * after sending the probe request. */ hdr->quiet_time = htole16(10); /* timeout in milliseconds */ hdr->quiet_threshold = htole16(1); /* min # of packets */ /* * Max needs to be greater than active and passive and quiet! * It's also in microseconds! */ hdr->max_svc = htole32(250 * IEEE80211_DUR_TU); hdr->pause_svc = htole32((4 << 24) | (100 * IEEE80211_DUR_TU)); /* Hardcode for now */ hdr->filter = htole32(WPI_FILTER_MULTICAST | WPI_FILTER_BEACON); tx = (struct wpi_cmd_data *)(hdr + 1); tx->flags = htole32(WPI_TX_AUTO_SEQ); tx->id = WPI_ID_BROADCAST; tx->lifetime = htole32(WPI_LIFETIME_INFINITE); if (IEEE80211_IS_CHAN_5GHZ(c)) { /* Send probe requests at 6Mbps. */ tx->plcp = wpi_ridx_to_plcp[WPI_RIDX_OFDM6]; rs = &ic->ic_sup_rates[IEEE80211_MODE_11A]; } else { hdr->flags = htole32(WPI_RXON_24GHZ | WPI_RXON_AUTO); /* Send probe requests at 1Mbps. */ tx->plcp = wpi_ridx_to_plcp[WPI_RIDX_CCK1]; rs = &ic->ic_sup_rates[IEEE80211_MODE_11G]; } essids = (struct wpi_scan_essid *)(tx + 1); nssid = MIN(ss->ss_nssid, WPI_SCAN_MAX_ESSIDS); for (i = 0; i < nssid; i++) { essids[i].id = IEEE80211_ELEMID_SSID; essids[i].len = MIN(ss->ss_ssid[i].len, IEEE80211_NWID_LEN); memcpy(essids[i].data, ss->ss_ssid[i].ssid, essids[i].len); #ifdef WPI_DEBUG if (sc->sc_debug & WPI_DEBUG_SCAN) { printf("Scanning Essid: "); ieee80211_print_essid(essids[i].data, essids[i].len); printf("\n"); } #endif } /* * Build a probe request frame. Most of the following code is a * copy & paste of what is done in net80211. */ wh = (struct ieee80211_frame *)(essids + WPI_SCAN_MAX_ESSIDS); wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ; wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr); IEEE80211_ADDR_COPY(wh->i_addr2, IF_LLADDR(ifp)); IEEE80211_ADDR_COPY(wh->i_addr3, ifp->if_broadcastaddr); *(uint16_t *)&wh->i_dur[0] = 0; /* filled by h/w */ *(uint16_t *)&wh->i_seq[0] = 0; /* filled by h/w */ frm = (uint8_t *)(wh + 1); frm = ieee80211_add_ssid(frm, NULL, 0); frm = ieee80211_add_rates(frm, rs); if (rs->rs_nrates > IEEE80211_RATE_SIZE) frm = ieee80211_add_xrates(frm, rs); /* Set length of probe request. */ tx->len = htole16(frm - (uint8_t *)wh); /* * Construct information about the channel that we * want to scan. The firmware expects this to be directly * after the scan probe request */ chan = (struct wpi_scan_chan *)frm; chan->chan = htole16(ieee80211_chan2ieee(ic, c)); chan->flags = 0; if (nssid) { hdr->crc_threshold = WPI_SCAN_CRC_TH_DEFAULT; chan->flags |= WPI_CHAN_NPBREQS(nssid); } else hdr->crc_threshold = WPI_SCAN_CRC_TH_NEVER; - if (!(c->ic_flags & IEEE80211_CHAN_PASSIVE)) + if (!IEEE80211_IS_CHAN_PASSIVE(c)) chan->flags |= WPI_CHAN_ACTIVE; /* * Calculate the active/passive dwell times. */ dwell_active = wpi_get_active_dwell_time(sc, c, nssid); dwell_passive = wpi_get_passive_dwell_time(sc, c); /* Make sure they're valid. */ if (dwell_passive <= dwell_active) dwell_passive = dwell_active + 1; chan->active = htole16(dwell_active); chan->passive = htole16(dwell_passive); chan->dsp_gain = 0x6e; /* Default level */ if (IEEE80211_IS_CHAN_5GHZ(c)) chan->rf_gain = 0x3b; else chan->rf_gain = 0x28; DPRINTF(sc, WPI_DEBUG_SCAN, "Scanning %u Passive: %d\n", - chan->chan, (c->ic_flags & IEEE80211_CHAN_PASSIVE) ? 1 : 0); + chan->chan, IEEE80211_IS_CHAN_PASSIVE(c)); hdr->nchan++; chan++; buflen = (uint8_t *)chan - buf; hdr->len = htole16(buflen); DPRINTF(sc, WPI_DEBUG_CMD, "sending scan command nchan=%d\n", hdr->nchan); error = wpi_cmd(sc, WPI_CMD_SCAN, buf, buflen, 1); free(buf, M_DEVBUF); sc->sc_scan_timer = 5; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); return error; } static int wpi_auth(struct wpi_softc *sc, struct ieee80211vap *vap) { struct ieee80211com *ic = vap->iv_ic; struct ieee80211_node *ni = vap->iv_bss; int error; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); /* Update adapter configuration. */ sc->rxon.associd = 0; sc->rxon.filter &= ~htole32(WPI_FILTER_BSS); IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid); sc->rxon.chan = ieee80211_chan2ieee(ic, ni->ni_chan); sc->rxon.flags = htole32(WPI_RXON_TSF | WPI_RXON_CTS_TO_SELF); if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) sc->rxon.flags |= htole32(WPI_RXON_AUTO | WPI_RXON_24GHZ); if (ic->ic_flags & IEEE80211_F_SHSLOT) sc->rxon.flags |= htole32(WPI_RXON_SHSLOT); if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) sc->rxon.flags |= htole32(WPI_RXON_SHPREAMBLE); if (IEEE80211_IS_CHAN_A(ni->ni_chan)) { sc->rxon.cck_mask = 0; sc->rxon.ofdm_mask = 0x15; } else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) { sc->rxon.cck_mask = 0x03; sc->rxon.ofdm_mask = 0; } else { /* Assume 802.11b/g. */ sc->rxon.cck_mask = 0x0f; sc->rxon.ofdm_mask = 0x15; } DPRINTF(sc, WPI_DEBUG_STATE, "rxon chan %d flags %x cck %x ofdm %x\n", sc->rxon.chan, sc->rxon.flags, sc->rxon.cck_mask, sc->rxon.ofdm_mask); if ((error = wpi_send_rxon(sc, 0, 1)) != 0) { device_printf(sc->sc_dev, "%s: could not send RXON\n", __func__); } DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); return error; } static int wpi_setup_beacon(struct wpi_softc *sc, struct ieee80211_node *ni) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = ni->ni_vap; struct wpi_vap *wvp = WPI_VAP(vap); struct wpi_buf *bcn = &wvp->wv_bcbuf; struct ieee80211_beacon_offsets bo; struct wpi_cmd_beacon *cmd; struct mbuf *m; int totlen; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); if (ni->ni_chan == IEEE80211_CHAN_ANYC) return EINVAL; m = ieee80211_beacon_alloc(ni, &bo); if (m == NULL) { device_printf(sc->sc_dev, "%s: could not allocate beacon frame\n", __func__); return ENOMEM; } totlen = m->m_pkthdr.len; if (bcn->data == NULL) { cmd = malloc(sizeof(struct wpi_cmd_beacon), M_DEVBUF, M_NOWAIT | M_ZERO); if (cmd == NULL) { device_printf(sc->sc_dev, "could not allocate buffer for beacon command\n"); m_freem(m); return ENOMEM; } cmd->id = WPI_ID_BROADCAST; cmd->ofdm_mask = 0xff; cmd->cck_mask = 0x0f; cmd->lifetime = htole32(WPI_LIFETIME_INFINITE); cmd->flags = htole32(WPI_TX_AUTO_SEQ | WPI_TX_INSERT_TSTAMP); bcn->data = cmd; bcn->ni = NULL; bcn->code = WPI_CMD_SET_BEACON; bcn->ac = 4; bcn->size = sizeof(struct wpi_cmd_beacon); } else cmd = bcn->data; cmd->len = htole16(totlen); cmd->plcp = (ic->ic_curmode == IEEE80211_MODE_11A) ? wpi_ridx_to_plcp[WPI_RIDX_OFDM6] : wpi_ridx_to_plcp[WPI_RIDX_CCK1]; /* NB: m will be freed in wpi_cmd_done() */ bcn->m = m; return wpi_cmd2(sc, bcn); } static void wpi_update_beacon(struct ieee80211vap *vap, int item) { struct ieee80211_node *ni = vap->iv_bss; struct ifnet *ifp = vap->iv_ifp; struct wpi_softc *sc = ifp->if_softc; int error; + WPI_LOCK(sc); if ((error = wpi_setup_beacon(sc, ni)) != 0) { device_printf(sc->sc_dev, "%s: could not update beacon frame, error %d", __func__, error); } + WPI_UNLOCK(sc); } static int wpi_run(struct wpi_softc *sc, struct ieee80211vap *vap) { struct ieee80211com *ic = vap->iv_ic; struct ieee80211_node *ni = vap->iv_bss; int error; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); if (vap->iv_opmode == IEEE80211_M_MONITOR) { /* Link LED blinks while monitoring. */ wpi_set_led(sc, WPI_LED_LINK, 5, 5); return 0; } /* XXX kernel panic workaround */ if (ni->ni_chan == IEEE80211_CHAN_ANYC) { device_printf(sc->sc_dev, "%s: incomplete configuration\n", __func__); return EINVAL; } if ((error = wpi_set_timing(sc, ni)) != 0) { device_printf(sc->sc_dev, "%s: could not set timing, error %d\n", __func__, error); return error; } /* Update adapter configuration. */ IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid); sc->rxon.associd = htole16(IEEE80211_NODE_AID(ni)); sc->rxon.chan = ieee80211_chan2ieee(ic, ni->ni_chan); sc->rxon.flags = htole32(WPI_RXON_TSF | WPI_RXON_CTS_TO_SELF); if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) sc->rxon.flags |= htole32(WPI_RXON_AUTO | WPI_RXON_24GHZ); /* Short preamble and slot time are negotiated when associating. */ sc->rxon.flags &= ~htole32(WPI_RXON_SHPREAMBLE | WPI_RXON_SHSLOT); if (ic->ic_flags & IEEE80211_F_SHSLOT) sc->rxon.flags |= htole32(WPI_RXON_SHSLOT); if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) sc->rxon.flags |= htole32(WPI_RXON_SHPREAMBLE); if (IEEE80211_IS_CHAN_A(ni->ni_chan)) { sc->rxon.cck_mask = 0; sc->rxon.ofdm_mask = 0x15; } else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) { sc->rxon.cck_mask = 0x03; sc->rxon.ofdm_mask = 0; } else { /* Assume 802.11b/g. */ sc->rxon.cck_mask = 0x0f; sc->rxon.ofdm_mask = 0x15; } sc->rxon.filter |= htole32(WPI_FILTER_BSS); /* XXX put somewhere HC_QOS_SUPPORT_ASSOC + HC_IBSS_START */ DPRINTF(sc, WPI_DEBUG_STATE, "rxon chan %d flags %x\n", sc->rxon.chan, sc->rxon.flags); if ((error = wpi_send_rxon(sc, 0, 1)) != 0) { device_printf(sc->sc_dev, "%s: could not send RXON\n", __func__); return error; } if (vap->iv_opmode == IEEE80211_M_IBSS) { if ((error = wpi_setup_beacon(sc, ni)) != 0) { device_printf(sc->sc_dev, "%s: could not setup beacon, error %d\n", __func__, error); return error; } } if (vap->iv_opmode == IEEE80211_M_STA) { /* Add BSS node. */ ((struct wpi_node *)ni)->id = WPI_ID_BSS; if ((error = wpi_add_node(sc, ni)) != 0) { device_printf(sc->sc_dev, "%s: could not add BSS node, error %d\n", __func__, error); return error; } } /* Link LED always on while associated. */ wpi_set_led(sc, WPI_LED_LINK, 0, 1); /* Start periodic calibration timer. */ callout_reset(&sc->calib_to, 60*hz, wpi_calib_timeout, sc); /* Enable power-saving mode if requested by user. */ if (vap->iv_flags & IEEE80211_F_PMGTON) (void)wpi_set_pslevel(sc, 0, 3, 1); + else + (void)wpi_set_pslevel(sc, 0, 0, 1); DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); return 0; } static int wpi_key_alloc(struct ieee80211vap *vap, struct ieee80211_key *k, ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix) { struct ifnet *ifp = vap->iv_ifp; struct wpi_softc *sc = ifp->if_softc; if (!(&vap->iv_nw_keys[0] <= k && k < &vap->iv_nw_keys[IEEE80211_WEP_NKID])) { if (k->wk_flags & IEEE80211_KEY_GROUP) { /* should not happen */ DPRINTF(sc, WPI_DEBUG_KEY, "%s: bogus group key\n", __func__); return 0; } *keyix = 0; /* NB: use key index 0 for ucast key */ } else { *keyix = *rxkeyix = k - vap->iv_nw_keys; if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_AES_CCM) k->wk_flags |= IEEE80211_KEY_SWCRYPT; } return 1; } static int wpi_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k, const uint8_t mac[IEEE80211_ADDR_LEN]) { const struct ieee80211_cipher *cip = k->wk_cipher; struct ieee80211com *ic = vap->iv_ic; struct ieee80211_node *ni = vap->iv_bss; struct wpi_softc *sc = ic->ic_ifp->if_softc; struct wpi_node *wn = (void *)ni; struct wpi_node_info node; uint16_t kflags; int error; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); switch (cip->ic_cipher) { case IEEE80211_CIPHER_AES_CCM: if (k->wk_flags & IEEE80211_KEY_GROUP) return 1; kflags = WPI_KFLAG_CCMP; break; default: /* null_key_set() */ return 1; } if (wn->id == WPI_ID_UNDEFINED) return 0; kflags |= WPI_KFLAG_KID(k->wk_keyix); if (k->wk_flags & IEEE80211_KEY_GROUP) kflags |= WPI_KFLAG_MULTICAST; memset(&node, 0, sizeof node); node.id = wn->id; node.control = WPI_NODE_UPDATE; node.flags = WPI_FLAG_KEY_SET; node.kflags = htole16(kflags); memcpy(node.key, k->wk_key, k->wk_keylen); DPRINTF(sc, WPI_DEBUG_KEY, "set key id=%d for node %d\n", k->wk_keyix, node.id); error = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 1); if (error != 0) { device_printf(sc->sc_dev, "can't update node info, error %d\n", error); return 0; } return 1; } static int wpi_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k) { const struct ieee80211_cipher *cip = k->wk_cipher; struct ieee80211com *ic = vap->iv_ic; struct ieee80211_node *ni = vap->iv_bss; struct wpi_softc *sc = ic->ic_ifp->if_softc; struct wpi_node *wn = (void *)ni; struct wpi_node_info node; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); switch (cip->ic_cipher) { case IEEE80211_CIPHER_AES_CCM: break; default: /* null_key_delete() */ return 1; } if (vap->iv_state != IEEE80211_S_RUN || (k->wk_flags & IEEE80211_KEY_GROUP)) return 1; /* Nothing to do. */ memset(&node, 0, sizeof node); node.id = wn->id; node.control = WPI_NODE_UPDATE; node.flags = WPI_FLAG_KEY_SET; DPRINTF(sc, WPI_DEBUG_KEY, "delete keys for node %d\n", node.id); (void)wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 1); return 1; } /* * This function is called after the runtime firmware notifies us of its * readiness (called in a process context). */ static int wpi_post_alive(struct wpi_softc *sc) { int ntries, error; /* Check (again) that the radio is not disabled. */ if ((error = wpi_nic_lock(sc)) != 0) return error; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); /* NB: Runtime firmware must be up and running. */ if (!(wpi_prph_read(sc, WPI_APMG_RFKILL) & 1)) { device_printf(sc->sc_dev, "RF switch: radio disabled (%s)\n", __func__); wpi_nic_unlock(sc); return EPERM; /* :-) */ } wpi_nic_unlock(sc); /* Wait for thermal sensor to calibrate. */ for (ntries = 0; ntries < 1000; ntries++) { if ((sc->temp = (int)WPI_READ(sc, WPI_UCODE_GP2)) != 0) break; DELAY(10); } if (ntries == 1000) { device_printf(sc->sc_dev, "timeout waiting for thermal sensor calibration\n"); return ETIMEDOUT; } DPRINTF(sc, WPI_DEBUG_TEMP, "temperature %d\n", sc->temp); return 0; } /* * The firmware boot code is small and is intended to be copied directly into * the NIC internal memory (no DMA transfer). */ static int wpi_load_bootcode(struct wpi_softc *sc, const uint8_t *ucode, int size) { int error, ntries; DPRINTF(sc, WPI_DEBUG_HW, "Loading microcode size 0x%x\n", size); size /= sizeof (uint32_t); if ((error = wpi_nic_lock(sc)) != 0) return error; /* Copy microcode image into NIC memory. */ wpi_prph_write_region_4(sc, WPI_BSM_SRAM_BASE, (const uint32_t *)ucode, size); wpi_prph_write(sc, WPI_BSM_WR_MEM_SRC, 0); wpi_prph_write(sc, WPI_BSM_WR_MEM_DST, WPI_FW_TEXT_BASE); wpi_prph_write(sc, WPI_BSM_WR_DWCOUNT, size); /* Start boot load now. */ wpi_prph_write(sc, WPI_BSM_WR_CTRL, WPI_BSM_WR_CTRL_START); /* Wait for transfer to complete. */ for (ntries = 0; ntries < 1000; ntries++) { uint32_t status = WPI_READ(sc, WPI_FH_TX_STATUS); DPRINTF(sc, WPI_DEBUG_HW, "firmware status=0x%x, val=0x%x, result=0x%x\n", status, WPI_FH_TX_STATUS_IDLE(6), status & WPI_FH_TX_STATUS_IDLE(6)); if (status & WPI_FH_TX_STATUS_IDLE(6)) { DPRINTF(sc, WPI_DEBUG_HW, "Status Match! - ntries = %d\n", ntries); break; } DELAY(10); } if (ntries == 1000) { device_printf(sc->sc_dev, "%s: could not load boot firmware\n", __func__); wpi_nic_unlock(sc); return ETIMEDOUT; } /* Enable boot after power up. */ wpi_prph_write(sc, WPI_BSM_WR_CTRL, WPI_BSM_WR_CTRL_START_EN); wpi_nic_unlock(sc); return 0; } static int wpi_load_firmware(struct wpi_softc *sc) { struct wpi_fw_info *fw = &sc->fw; struct wpi_dma_info *dma = &sc->fw_dma; int error; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); /* Copy initialization sections into pre-allocated DMA-safe memory. */ memcpy(dma->vaddr, fw->init.data, fw->init.datasz); bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); memcpy(dma->vaddr + WPI_FW_DATA_MAXSZ, fw->init.text, fw->init.textsz); bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); /* Tell adapter where to find initialization sections. */ if ((error = wpi_nic_lock(sc)) != 0) return error; wpi_prph_write(sc, WPI_BSM_DRAM_DATA_ADDR, dma->paddr); wpi_prph_write(sc, WPI_BSM_DRAM_DATA_SIZE, fw->init.datasz); wpi_prph_write(sc, WPI_BSM_DRAM_TEXT_ADDR, dma->paddr + WPI_FW_DATA_MAXSZ); wpi_prph_write(sc, WPI_BSM_DRAM_TEXT_SIZE, fw->init.textsz); wpi_nic_unlock(sc); /* Load firmware boot code. */ error = wpi_load_bootcode(sc, fw->boot.text, fw->boot.textsz); if (error != 0) { device_printf(sc->sc_dev, "%s: could not load boot firmware\n", __func__); return error; } /* Now press "execute". */ WPI_WRITE(sc, WPI_RESET, 0); /* Wait at most one second for first alive notification. */ if ((error = msleep(sc, &sc->sc_mtx, PCATCH, "wpiinit", hz)) != 0) { device_printf(sc->sc_dev, "%s: timeout waiting for adapter to initialize, error %d\n", __func__, error); return error; } /* Copy runtime sections into pre-allocated DMA-safe memory. */ memcpy(dma->vaddr, fw->main.data, fw->main.datasz); bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); memcpy(dma->vaddr + WPI_FW_DATA_MAXSZ, fw->main.text, fw->main.textsz); bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); /* Tell adapter where to find runtime sections. */ if ((error = wpi_nic_lock(sc)) != 0) return error; wpi_prph_write(sc, WPI_BSM_DRAM_DATA_ADDR, dma->paddr); wpi_prph_write(sc, WPI_BSM_DRAM_DATA_SIZE, fw->main.datasz); wpi_prph_write(sc, WPI_BSM_DRAM_TEXT_ADDR, dma->paddr + WPI_FW_DATA_MAXSZ); wpi_prph_write(sc, WPI_BSM_DRAM_TEXT_SIZE, WPI_FW_UPDATED | fw->main.textsz); wpi_nic_unlock(sc); return 0; } static int wpi_read_firmware(struct wpi_softc *sc) { const struct firmware *fp; struct wpi_fw_info *fw = &sc->fw; const struct wpi_firmware_hdr *hdr; int error; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); DPRINTF(sc, WPI_DEBUG_FIRMWARE, "Attempting Loading Firmware from %s module\n", WPI_FW_NAME); WPI_UNLOCK(sc); fp = firmware_get(WPI_FW_NAME); WPI_LOCK(sc); if (fp == NULL) { device_printf(sc->sc_dev, "could not load firmware image '%s'\n", WPI_FW_NAME); return EINVAL; } sc->fw_fp = fp; if (fp->datasize < sizeof (struct wpi_firmware_hdr)) { device_printf(sc->sc_dev, "firmware file too short: %zu bytes\n", fp->datasize); error = EINVAL; goto fail; } fw->size = fp->datasize; fw->data = (const uint8_t *)fp->data; /* Extract firmware header information. */ hdr = (const struct wpi_firmware_hdr *)fw->data; /* | RUNTIME FIRMWARE | INIT FIRMWARE | BOOT FW | |HDR|<--TEXT-->|<--DATA-->|<--TEXT-->|<--DATA-->|<--TEXT-->| */ fw->main.textsz = le32toh(hdr->rtextsz); fw->main.datasz = le32toh(hdr->rdatasz); fw->init.textsz = le32toh(hdr->itextsz); fw->init.datasz = le32toh(hdr->idatasz); fw->boot.textsz = le32toh(hdr->btextsz); fw->boot.datasz = 0; /* Sanity-check firmware header. */ if (fw->main.textsz > WPI_FW_TEXT_MAXSZ || fw->main.datasz > WPI_FW_DATA_MAXSZ || fw->init.textsz > WPI_FW_TEXT_MAXSZ || fw->init.datasz > WPI_FW_DATA_MAXSZ || fw->boot.textsz > WPI_FW_BOOT_TEXT_MAXSZ || (fw->boot.textsz & 3) != 0) { device_printf(sc->sc_dev, "invalid firmware header\n"); error = EINVAL; goto fail; } /* Check that all firmware sections fit. */ if (fw->size < sizeof (*hdr) + fw->main.textsz + fw->main.datasz + fw->init.textsz + fw->init.datasz + fw->boot.textsz) { device_printf(sc->sc_dev, "firmware file too short: %zu bytes\n", fw->size); error = EINVAL; goto fail; } /* Get pointers to firmware sections. */ fw->main.text = (const uint8_t *)(hdr + 1); fw->main.data = fw->main.text + fw->main.textsz; fw->init.text = fw->main.data + fw->main.datasz; fw->init.data = fw->init.text + fw->init.textsz; fw->boot.text = fw->init.data + fw->init.datasz; DPRINTF(sc, WPI_DEBUG_FIRMWARE, "Firmware Version: Major %d, Minor %d, Driver %d, \n" "runtime (text: %u, data: %u) init (text: %u, data %u) boot (text %u)\n", hdr->major, hdr->minor, le32toh(hdr->driver), fw->main.textsz, fw->main.datasz, fw->init.textsz, fw->init.datasz, fw->boot.textsz); DPRINTF(sc, WPI_DEBUG_FIRMWARE, "fw->main.text %p\n", fw->main.text); DPRINTF(sc, WPI_DEBUG_FIRMWARE, "fw->main.data %p\n", fw->main.data); DPRINTF(sc, WPI_DEBUG_FIRMWARE, "fw->init.text %p\n", fw->init.text); DPRINTF(sc, WPI_DEBUG_FIRMWARE, "fw->init.data %p\n", fw->init.data); DPRINTF(sc, WPI_DEBUG_FIRMWARE, "fw->boot.text %p\n", fw->boot.text); return 0; fail: wpi_unload_firmware(sc); return error; } /** * Free the referenced firmware image */ static void wpi_unload_firmware(struct wpi_softc *sc) { if (sc->fw_fp != NULL) { firmware_put(sc->fw_fp, FIRMWARE_UNLOAD); sc->fw_fp = NULL; } } static int wpi_clock_wait(struct wpi_softc *sc) { int ntries; /* Set "initialization complete" bit. */ WPI_SETBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_INIT_DONE); /* Wait for clock stabilization. */ for (ntries = 0; ntries < 2500; ntries++) { if (WPI_READ(sc, WPI_GP_CNTRL) & WPI_GP_CNTRL_MAC_CLOCK_READY) return 0; DELAY(100); } device_printf(sc->sc_dev, "%s: timeout waiting for clock stabilization\n", __func__); return ETIMEDOUT; } static int wpi_apm_init(struct wpi_softc *sc) { uint32_t reg; int error; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); /* Disable L0s exit timer (NMI bug workaround). */ WPI_SETBITS(sc, WPI_GIO_CHICKEN, WPI_GIO_CHICKEN_DIS_L0S_TIMER); /* Don't wait for ICH L0s (ICH bug workaround). */ WPI_SETBITS(sc, WPI_GIO_CHICKEN, WPI_GIO_CHICKEN_L1A_NO_L0S_RX); /* Set FH wait threshold to max (HW bug under stress workaround). */ WPI_SETBITS(sc, WPI_DBG_HPET_MEM, 0xffff0000); /* Retrieve PCIe Active State Power Management (ASPM). */ reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + 0x10, 1); /* Workaround for HW instability in PCIe L0->L0s->L1 transition. */ if (reg & 0x02) /* L1 Entry enabled. */ WPI_SETBITS(sc, WPI_GIO, WPI_GIO_L0S_ENA); else WPI_CLRBITS(sc, WPI_GIO, WPI_GIO_L0S_ENA); WPI_SETBITS(sc, WPI_ANA_PLL, WPI_ANA_PLL_INIT); /* Wait for clock stabilization before accessing prph. */ if ((error = wpi_clock_wait(sc)) != 0) return error; if ((error = wpi_nic_lock(sc)) != 0) return error; /* Enable DMA and BSM (Bootstrap State Machine). */ wpi_prph_write(sc, WPI_APMG_CLK_EN, WPI_APMG_CLK_CTRL_DMA_CLK_RQT | WPI_APMG_CLK_CTRL_BSM_CLK_RQT); DELAY(20); /* Disable L1-Active. */ wpi_prph_setbits(sc, WPI_APMG_PCI_STT, WPI_APMG_PCI_STT_L1A_DIS); + /* ??? */ + wpi_prph_clrbits(sc, WPI_APMG_PS, 0x00000E00); wpi_nic_unlock(sc); return 0; } static void wpi_apm_stop_master(struct wpi_softc *sc) { int ntries; /* Stop busmaster DMA activity. */ WPI_SETBITS(sc, WPI_RESET, WPI_RESET_STOP_MASTER); if ((WPI_READ(sc, WPI_GP_CNTRL) & WPI_GP_CNTRL_PS_MASK) == WPI_GP_CNTRL_MAC_PS) return; /* Already asleep. */ for (ntries = 0; ntries < 100; ntries++) { if (WPI_READ(sc, WPI_RESET) & WPI_RESET_MASTER_DISABLED) return; DELAY(10); } device_printf(sc->sc_dev, "%s: timeout waiting for master\n", __func__); } static void wpi_apm_stop(struct wpi_softc *sc) { wpi_apm_stop_master(sc); /* Reset the entire device. */ WPI_SETBITS(sc, WPI_RESET, WPI_RESET_SW); DELAY(10); /* Clear "initialization complete" bit. */ WPI_CLRBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_INIT_DONE); } static void wpi_nic_config(struct wpi_softc *sc) { uint32_t rev; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); /* voodoo from the Linux "driver".. */ rev = pci_read_config(sc->sc_dev, PCIR_REVID, 1); if ((rev & 0xc0) == 0x40) WPI_SETBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_ALM_MB); else if (!(rev & 0x80)) WPI_SETBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_ALM_MM); if (sc->cap == 0x80) WPI_SETBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_SKU_MRC); if ((le16toh(sc->rev) & 0xf0) == 0xd0) WPI_SETBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_REV_D); else WPI_CLRBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_REV_D); if (sc->type > 1) WPI_SETBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_TYPE_B); } static int wpi_hw_init(struct wpi_softc *sc) { int chnl, ntries, error; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); /* Clear pending interrupts. */ WPI_WRITE(sc, WPI_INT, 0xffffffff); if ((error = wpi_apm_init(sc)) != 0) { device_printf(sc->sc_dev, "%s: could not power ON adapter, error %d\n", __func__, error); return error; } /* Select VMAIN power source. */ if ((error = wpi_nic_lock(sc)) != 0) return error; wpi_prph_clrbits(sc, WPI_APMG_PS, WPI_APMG_PS_PWR_SRC_MASK); wpi_nic_unlock(sc); /* Spin until VMAIN gets selected. */ for (ntries = 0; ntries < 5000; ntries++) { if (WPI_READ(sc, WPI_GPIO_IN) & WPI_GPIO_IN_VMAIN) break; DELAY(10); } if (ntries == 5000) { device_printf(sc->sc_dev, "timeout selecting power source\n"); return ETIMEDOUT; } /* Perform adapter initialization. */ wpi_nic_config(sc); /* Initialize RX ring. */ if ((error = wpi_nic_lock(sc)) != 0) return error; /* Set physical address of RX ring. */ WPI_WRITE(sc, WPI_FH_RX_BASE, sc->rxq.desc_dma.paddr); /* Set physical address of RX read pointer. */ WPI_WRITE(sc, WPI_FH_RX_RPTR_ADDR, sc->shared_dma.paddr + offsetof(struct wpi_shared, next)); WPI_WRITE(sc, WPI_FH_RX_WPTR, 0); /* Enable RX. */ WPI_WRITE(sc, WPI_FH_RX_CONFIG, WPI_FH_RX_CONFIG_DMA_ENA | WPI_FH_RX_CONFIG_RDRBD_ENA | WPI_FH_RX_CONFIG_WRSTATUS_ENA | WPI_FH_RX_CONFIG_MAXFRAG | WPI_FH_RX_CONFIG_NRBD(WPI_RX_RING_COUNT_LOG) | WPI_FH_RX_CONFIG_IRQ_DST_HOST | WPI_FH_RX_CONFIG_IRQ_TIMEOUT(1)); (void)WPI_READ(sc, WPI_FH_RSSR_TBL); /* barrier */ wpi_nic_unlock(sc); WPI_WRITE(sc, WPI_FH_RX_WPTR, (WPI_RX_RING_COUNT - 1) & ~7); /* Initialize TX rings. */ if ((error = wpi_nic_lock(sc)) != 0) return error; wpi_prph_write(sc, WPI_ALM_SCHED_MODE, 2); /* bypass mode */ wpi_prph_write(sc, WPI_ALM_SCHED_ARASTAT, 1); /* enable RA0 */ /* Enable all 6 TX rings. */ wpi_prph_write(sc, WPI_ALM_SCHED_TXFACT, 0x3f); wpi_prph_write(sc, WPI_ALM_SCHED_SBYPASS_MODE1, 0x10000); wpi_prph_write(sc, WPI_ALM_SCHED_SBYPASS_MODE2, 0x30002); wpi_prph_write(sc, WPI_ALM_SCHED_TXF4MF, 4); wpi_prph_write(sc, WPI_ALM_SCHED_TXF5MF, 5); /* Set physical address of TX rings. */ WPI_WRITE(sc, WPI_FH_TX_BASE, sc->shared_dma.paddr); WPI_WRITE(sc, WPI_FH_MSG_CONFIG, 0xffff05a5); /* Enable all DMA channels. */ for (chnl = 0; chnl < WPI_NDMACHNLS; chnl++) { WPI_WRITE(sc, WPI_FH_CBBC_CTRL(chnl), 0); WPI_WRITE(sc, WPI_FH_CBBC_BASE(chnl), 0); WPI_WRITE(sc, WPI_FH_TX_CONFIG(chnl), 0x80200008); } wpi_nic_unlock(sc); (void)WPI_READ(sc, WPI_FH_TX_BASE); /* barrier */ /* Clear "radio off" and "commands blocked" bits. */ WPI_WRITE(sc, WPI_UCODE_GP1_CLR, WPI_UCODE_GP1_RFKILL); WPI_WRITE(sc, WPI_UCODE_GP1_CLR, WPI_UCODE_GP1_CMD_BLOCKED); /* Clear pending interrupts. */ WPI_WRITE(sc, WPI_INT, 0xffffffff); /* Enable interrupts. */ WPI_WRITE(sc, WPI_INT_MASK, WPI_INT_MASK_DEF); /* _Really_ make sure "radio off" bit is cleared! */ WPI_WRITE(sc, WPI_UCODE_GP1_CLR, WPI_UCODE_GP1_RFKILL); WPI_WRITE(sc, WPI_UCODE_GP1_CLR, WPI_UCODE_GP1_RFKILL); if ((error = wpi_load_firmware(sc)) != 0) { device_printf(sc->sc_dev, "%s: could not load firmware, error %d\n", __func__, error); return error; } /* Wait at most one second for firmware alive notification. */ if ((error = msleep(sc, &sc->sc_mtx, PCATCH, "wpiinit", hz)) != 0) { device_printf(sc->sc_dev, "%s: timeout waiting for adapter to initialize, error %d\n", __func__, error); return error; } DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); /* Do post-firmware initialization. */ return wpi_post_alive(sc); } static void wpi_hw_stop(struct wpi_softc *sc) { int chnl, qid, ntries; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); if (WPI_READ(sc, WPI_UCODE_GP1) & WPI_UCODE_GP1_MAC_SLEEP) wpi_nic_lock(sc); WPI_WRITE(sc, WPI_RESET, WPI_RESET_NEVO); /* Disable interrupts. */ WPI_WRITE(sc, WPI_INT_MASK, 0); WPI_WRITE(sc, WPI_INT, 0xffffffff); WPI_WRITE(sc, WPI_FH_INT, 0xffffffff); /* Make sure we no longer hold the NIC lock. */ wpi_nic_unlock(sc); if (wpi_nic_lock(sc) == 0) { /* Stop TX scheduler. */ wpi_prph_write(sc, WPI_ALM_SCHED_MODE, 0); wpi_prph_write(sc, WPI_ALM_SCHED_TXFACT, 0); /* Stop all DMA channels. */ for (chnl = 0; chnl < WPI_NDMACHNLS; chnl++) { WPI_WRITE(sc, WPI_FH_TX_CONFIG(chnl), 0); for (ntries = 0; ntries < 200; ntries++) { if (WPI_READ(sc, WPI_FH_TX_STATUS) & WPI_FH_TX_STATUS_IDLE(chnl)) break; DELAY(10); } } wpi_nic_unlock(sc); } /* Stop RX ring. */ wpi_reset_rx_ring(sc); /* Reset all TX rings. */ for (qid = 0; qid < WPI_NTXQUEUES; qid++) wpi_reset_tx_ring(sc, &sc->txq[qid]); if (wpi_nic_lock(sc) == 0) { wpi_prph_write(sc, WPI_APMG_CLK_DIS, WPI_APMG_CLK_CTRL_DMA_CLK_RQT); wpi_nic_unlock(sc); } DELAY(5); /* Power OFF adapter. */ wpi_apm_stop(sc); } static void wpi_radio_on(void *arg0, int pending) { struct wpi_softc *sc = arg0; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); device_printf(sc->sc_dev, "RF switch: radio enabled\n"); if (vap != NULL) { wpi_init(sc); ieee80211_init(vap); } if (WPI_READ(sc, WPI_GP_CNTRL) & WPI_GP_CNTRL_RFKILL) { WPI_LOCK(sc); callout_stop(&sc->watchdog_rfkill); WPI_UNLOCK(sc); } } static void wpi_radio_off(void *arg0, int pending) { struct wpi_softc *sc = arg0; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); device_printf(sc->sc_dev, "RF switch: radio disabled\n"); wpi_stop(sc); if (vap != NULL) ieee80211_stop(vap); callout_reset(&sc->watchdog_rfkill, hz, wpi_watchdog_rfkill, sc); } static void wpi_init_locked(struct wpi_softc *sc) { struct ifnet *ifp = sc->sc_ifp; int error; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); WPI_LOCK_ASSERT(sc); /* Check that the radio is not disabled by hardware switch. */ if (!(WPI_READ(sc, WPI_GP_CNTRL) & WPI_GP_CNTRL_RFKILL)) { device_printf(sc->sc_dev, "RF switch: radio disabled (%s)\n", __func__); callout_reset(&sc->watchdog_rfkill, hz, wpi_watchdog_rfkill, sc); return; } /* Read firmware images from the filesystem. */ if ((error = wpi_read_firmware(sc)) != 0) { device_printf(sc->sc_dev, "%s: could not read firmware, error %d\n", __func__, error); goto fail; } /* Initialize hardware and upload firmware. */ error = wpi_hw_init(sc); wpi_unload_firmware(sc); if (error != 0) { device_printf(sc->sc_dev, "%s: could not initialize hardware, error %d\n", __func__, error); goto fail; } /* Configure adapter now that it is ready. */ if ((error = wpi_config(sc)) != 0) { device_printf(sc->sc_dev, "%s: could not configure device, error %d\n", __func__, error); goto fail; } ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; ifp->if_drv_flags |= IFF_DRV_RUNNING; callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc); DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); return; fail: wpi_stop_locked(sc); DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); } static void wpi_init(void *arg) { struct wpi_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; WPI_LOCK(sc); wpi_init_locked(sc); WPI_UNLOCK(sc); if (ifp->if_drv_flags & IFF_DRV_RUNNING) ieee80211_start_all(ic); } static void wpi_stop_locked(struct wpi_softc *sc) { struct ifnet *ifp = sc->sc_ifp; WPI_LOCK_ASSERT(sc); sc->sc_scan_timer = 0; sc->sc_tx_timer = 0; callout_stop(&sc->watchdog_to); callout_stop(&sc->calib_to); ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); /* Power OFF hardware. */ wpi_hw_stop(sc); } static void wpi_stop(struct wpi_softc *sc) { WPI_LOCK(sc); wpi_stop_locked(sc); WPI_UNLOCK(sc); } /* * Callback from net80211 to start a scan. */ static void wpi_scan_start(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct wpi_softc *sc = ifp->if_softc; WPI_LOCK(sc); wpi_set_led(sc, WPI_LED_LINK, 20, 2); WPI_UNLOCK(sc); } /* * Callback from net80211 to terminate a scan. */ static void wpi_scan_end(struct ieee80211com *ic) { struct ifnet *ifp = ic->ic_ifp; struct wpi_softc *sc = ifp->if_softc; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); if (vap->iv_state == IEEE80211_S_RUN) { WPI_LOCK(sc); wpi_set_led(sc, WPI_LED_LINK, 0, 1); WPI_UNLOCK(sc); } } /** * Called by the net80211 framework to indicate to the driver * that the channel should be changed */ static void wpi_set_channel(struct ieee80211com *ic) { const struct ieee80211_channel *c = ic->ic_curchan; struct ifnet *ifp = ic->ic_ifp; struct wpi_softc *sc = ifp->if_softc; int error; DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); WPI_LOCK(sc); sc->sc_rxtap.wr_chan_freq = htole16(c->ic_freq); sc->sc_rxtap.wr_chan_flags = htole16(c->ic_flags); sc->sc_txtap.wt_chan_freq = htole16(c->ic_freq); sc->sc_txtap.wt_chan_flags = htole16(c->ic_flags); /* * Only need to set the channel in Monitor mode. AP scanning and auth * are already taken care of by their respective firmware commands. */ if (ic->ic_opmode == IEEE80211_M_MONITOR) { sc->rxon.chan = ieee80211_chan2ieee(ic, c); if (IEEE80211_IS_CHAN_2GHZ(c)) { sc->rxon.flags |= htole32(WPI_RXON_AUTO | WPI_RXON_24GHZ); } else { sc->rxon.flags &= ~htole32(WPI_RXON_AUTO | WPI_RXON_24GHZ); } if ((error = wpi_send_rxon(sc, 0, 0)) != 0) device_printf(sc->sc_dev, "%s: error %d settting channel\n", __func__, error); } WPI_UNLOCK(sc); } /** * Called by net80211 to indicate that we need to scan the current * channel. The channel is previously be set via the wpi_set_channel * callback. */ static void wpi_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) { struct ieee80211vap *vap = ss->ss_vap; struct ieee80211com *ic = vap->iv_ic; struct ifnet *ifp = ic->ic_ifp; struct wpi_softc *sc = ifp->if_softc; int error; if (sc->rxon.chan != ieee80211_chan2ieee(ic, ic->ic_curchan)) { WPI_LOCK(sc); error = wpi_scan(sc, ic->ic_curchan); WPI_UNLOCK(sc); if (error != 0) ieee80211_cancel_scan(vap); } else { /* Send probe request when associated. */ sc->sc_scan_curchan(ss, maxdwell); } } /** * Called by the net80211 framework to indicate * the minimum dwell time has been met, terminate the scan. * We don't actually terminate the scan as the firmware will notify * us when it's finished and we have no way to interrupt it. */ static void wpi_scan_mindwell(struct ieee80211_scan_state *ss) { /* NB: don't try to abort scan; wait for firmware to finish */ } static void wpi_hw_reset(void *arg, int pending) { struct wpi_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); wpi_stop(sc); if (vap != NULL) ieee80211_stop(vap); wpi_init(sc); if (vap != NULL) ieee80211_init(vap); } Index: projects/building-blocks/sys/dev/wpi/if_wpireg.h =================================================================== --- projects/building-blocks/sys/dev/wpi/if_wpireg.h (revision 278776) +++ projects/building-blocks/sys/dev/wpi/if_wpireg.h (revision 278777) @@ -1,994 +1,990 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2006,2007 * Damien Bergamini * * Permission to use, copy, modify, and 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. */ #define WPI_TX_RING_COUNT 256 #define WPI_TX_RING_LOMARK 192 #define WPI_TX_RING_HIMARK 224 #define WPI_RX_RING_COUNT_LOG 6 #define WPI_RX_RING_COUNT (1 << WPI_RX_RING_COUNT_LOG) #define WPI_NTXQUEUES 8 #define WPI_NDMACHNLS 6 /* Maximum scatter/gather. */ #define WPI_MAX_SCATTER 4 /* * Rings must be aligned on a 16K boundary. */ #define WPI_RING_DMA_ALIGN 0x4000 /* Maximum Rx buffer size. */ #define WPI_RBUF_SIZE ( 3 * 1024 ) /* XXX 3000 but must be aligned */ /* * Control and status registers. */ #define WPI_HW_IF_CONFIG 0x000 #define WPI_INT 0x008 #define WPI_INT_MASK 0x00c #define WPI_FH_INT 0x010 #define WPI_GPIO_IN 0x018 #define WPI_RESET 0x020 #define WPI_GP_CNTRL 0x024 #define WPI_EEPROM 0x02c #define WPI_EEPROM_GP 0x030 #define WPI_GIO 0x03c #define WPI_UCODE_GP1 0x054 #define WPI_UCODE_GP1_SET 0x058 #define WPI_UCODE_GP1_CLR 0x05c #define WPI_UCODE_GP2 0x060 #define WPI_GIO_CHICKEN 0x100 #define WPI_ANA_PLL 0x20c #define WPI_DBG_HPET_MEM 0x240 #define WPI_MEM_RADDR 0x40c #define WPI_MEM_WADDR 0x410 #define WPI_MEM_WDATA 0x418 #define WPI_MEM_RDATA 0x41c #define WPI_PRPH_WADDR 0x444 #define WPI_PRPH_RADDR 0x448 #define WPI_PRPH_WDATA 0x44c #define WPI_PRPH_RDATA 0x450 #define WPI_HBUS_TARG_WRPTR 0x460 /* * Flow-Handler registers. */ #define WPI_FH_CBBC_CTRL(qid) (0x940 + (qid) * 8) #define WPI_FH_CBBC_BASE(qid) (0x944 + (qid) * 8) #define WPI_FH_RX_CONFIG 0xc00 #define WPI_FH_RX_BASE 0xc04 #define WPI_FH_RX_WPTR 0xc20 #define WPI_FH_RX_RPTR_ADDR 0xc24 #define WPI_FH_RSSR_TBL 0xcc0 #define WPI_FH_RX_STATUS 0xcc4 #define WPI_FH_TX_CONFIG(qid) (0xd00 + (qid) * 32) #define WPI_FH_TX_BASE 0xe80 #define WPI_FH_MSG_CONFIG 0xe88 #define WPI_FH_TX_STATUS 0xe90 /* * NIC internal memory offsets. */ #define WPI_ALM_SCHED_MODE 0x2e00 #define WPI_ALM_SCHED_ARASTAT 0x2e04 #define WPI_ALM_SCHED_TXFACT 0x2e10 #define WPI_ALM_SCHED_TXF4MF 0x2e14 #define WPI_ALM_SCHED_TXF5MF 0x2e20 #define WPI_ALM_SCHED_SBYPASS_MODE1 0x2e2c #define WPI_ALM_SCHED_SBYPASS_MODE2 0x2e30 #define WPI_APMG_CLK_EN 0x3004 #define WPI_APMG_CLK_DIS 0x3008 #define WPI_APMG_PS 0x300c #define WPI_APMG_PCI_STT 0x3010 #define WPI_APMG_RFKILL 0x3014 #define WPI_BSM_WR_CTRL 0x3400 #define WPI_BSM_WR_MEM_SRC 0x3404 #define WPI_BSM_WR_MEM_DST 0x3408 #define WPI_BSM_WR_DWCOUNT 0x340c #define WPI_BSM_DRAM_TEXT_ADDR 0x3490 #define WPI_BSM_DRAM_TEXT_SIZE 0x3494 #define WPI_BSM_DRAM_DATA_ADDR 0x3498 #define WPI_BSM_DRAM_DATA_SIZE 0x349c #define WPI_BSM_SRAM_BASE 0x3800 /* Possible flags for register WPI_HW_IF_CONFIG. */ #define WPI_HW_IF_CONFIG_ALM_MB (1 << 8) #define WPI_HW_IF_CONFIG_ALM_MM (1 << 9) #define WPI_HW_IF_CONFIG_SKU_MRC (1 << 10) #define WPI_HW_IF_CONFIG_REV_D (1 << 11) #define WPI_HW_IF_CONFIG_TYPE_B (1 << 12) /* Possible flags for registers WPI_PRPH_RADDR/WPI_PRPH_WADDR. */ #define WPI_PRPH_DWORD ((sizeof (uint32_t) - 1) << 24) /* Possible values for WPI_BSM_WR_MEM_DST. */ #define WPI_FW_TEXT_BASE 0x00000000 #define WPI_FW_DATA_BASE 0x00800000 /* Possible flags for WPI_GPIO_IN. */ #define WPI_GPIO_IN_VMAIN (1 << 9) /* Possible flags for register WPI_RESET. */ #define WPI_RESET_NEVO (1 << 0) #define WPI_RESET_SW (1 << 7) #define WPI_RESET_MASTER_DISABLED (1 << 8) #define WPI_RESET_STOP_MASTER (1 << 9) /* Possible flags for register WPI_GP_CNTRL. */ #define WPI_GP_CNTRL_MAC_ACCESS_ENA (1 << 0) #define WPI_GP_CNTRL_MAC_CLOCK_READY (1 << 0) #define WPI_GP_CNTRL_INIT_DONE (1 << 2) #define WPI_GP_CNTRL_MAC_ACCESS_REQ (1 << 3) #define WPI_GP_CNTRL_SLEEP (1 << 4) #define WPI_GP_CNTRL_PS_MASK (7 << 24) #define WPI_GP_CNTRL_MAC_PS (4 << 24) #define WPI_GP_CNTRL_RFKILL (1 << 27) /* Possible flags for register WPI_GIO_CHICKEN. */ #define WPI_GIO_CHICKEN_L1A_NO_L0S_RX (1 << 23) #define WPI_GIO_CHICKEN_DIS_L0S_TIMER (1 << 29) /* Possible flags for register WPI_GIO. */ #define WPI_GIO_L0S_ENA (1 << 1) /* Possible flags for register WPI_FH_RX_CONFIG. */ #define WPI_FH_RX_CONFIG_DMA_ENA (1U << 31) #define WPI_FH_RX_CONFIG_RDRBD_ENA (1 << 29) #define WPI_FH_RX_CONFIG_WRSTATUS_ENA (1 << 27) #define WPI_FH_RX_CONFIG_MAXFRAG (1 << 24) #define WPI_FH_RX_CONFIG_NRBD(x) ((x) << 20) #define WPI_FH_RX_CONFIG_IRQ_DST_HOST (1 << 12) #define WPI_FH_RX_CONFIG_IRQ_TIMEOUT(x) ((x) << 4) /* Possible flags for register WPI_ANA_PLL. */ #define WPI_ANA_PLL_INIT (1 << 24) /* Possible flags for register WPI_UCODE_GP1*. */ #define WPI_UCODE_GP1_MAC_SLEEP (1 << 0) #define WPI_UCODE_GP1_RFKILL (1 << 1) #define WPI_UCODE_GP1_CMD_BLOCKED (1 << 2) /* Possible flags for register WPI_FH_RX_STATUS. */ #define WPI_FH_RX_STATUS_IDLE (1 << 24) /* Possible flags for register WPI_BSM_WR_CTRL. */ #define WPI_BSM_WR_CTRL_START_EN (1 << 30) #define WPI_BSM_WR_CTRL_START (1U << 31) /* Possible flags for register WPI_INT. */ #define WPI_INT_ALIVE (1 << 0) #define WPI_INT_WAKEUP (1 << 1) #define WPI_INT_SW_RX (1 << 3) #define WPI_INT_SW_ERR (1 << 25) #define WPI_INT_FH_TX (1 << 27) #define WPI_INT_HW_ERR (1 << 29) #define WPI_INT_FH_RX (1U << 31) /* Shortcut. */ #define WPI_INT_MASK_DEF \ (WPI_INT_SW_ERR | WPI_INT_HW_ERR | WPI_INT_FH_TX | \ WPI_INT_FH_RX | WPI_INT_ALIVE | WPI_INT_WAKEUP | \ WPI_INT_SW_RX) /* Possible flags for register WPI_FH_INT. */ #define WPI_FH_INT_RX_CHNL(x) (1 << ((x) + 16)) #define WPI_FH_INT_HI_PRIOR (1 << 30) /* Shortcuts for the above. */ #define WPI_FH_INT_RX \ (WPI_FH_INT_RX_CHNL(0) | \ WPI_FH_INT_RX_CHNL(1) | \ WPI_FH_INT_RX_CHNL(2) | \ WPI_FH_INT_HI_PRIOR) /* Possible flags for register WPI_FH_TX_STATUS. */ #define WPI_FH_TX_STATUS_IDLE(qid) \ (1 << ((qid) + 24) | 1 << ((qid) + 16)) /* Possible flags for register WPI_EEPROM. */ #define WPI_EEPROM_READ_VALID (1 << 0) /* Possible flags for register WPI_EEPROM_GP. */ #define WPI_EEPROM_VERSION 0x00000007 #define WPI_EEPROM_GP_IF_OWNER 0x00000180 /* Possible flags for register WPI_APMG_PS. */ #define WPI_APMG_PS_PWR_SRC_MASK (3 << 24) /* Possible flags for registers WPI_APMG_CLK_*. */ #define WPI_APMG_CLK_CTRL_DMA_CLK_RQT (1 << 9) #define WPI_APMG_CLK_CTRL_BSM_CLK_RQT (1 << 11) /* Possible flags for register WPI_APMG_PCI_STT. */ #define WPI_APMG_PCI_STT_L1A_DIS (1 << 11) struct wpi_shared { uint32_t txbase[8]; uint32_t next; uint32_t reserved[2]; } __packed; #define WPI_MAX_SEG_LEN 65520 struct wpi_tx_desc { uint8_t reserved1[3]; uint8_t nsegs; #define WPI_PAD32(x) (roundup2(x, 4) - (x)) struct { uint32_t addr; uint32_t len; } __packed segs[WPI_MAX_SCATTER]; uint8_t reserved2[28]; } __packed; struct wpi_tx_stat { uint8_t rtsfailcnt; uint8_t ackfailcnt; uint8_t btkillcnt; uint8_t rate; uint32_t duration; uint32_t status; } __packed; struct wpi_rx_desc { uint32_t len; uint8_t type; #define WPI_UC_READY 1 #define WPI_RX_DONE 27 #define WPI_TX_DONE 28 #define WPI_START_SCAN 130 #define WPI_SCAN_RESULTS 131 #define WPI_STOP_SCAN 132 #define WPI_BEACON_SENT 144 #define WPI_RX_STATISTICS 156 #define WPI_BEACON_STATISTICS 157 #define WPI_STATE_CHANGED 161 #define WPI_BEACON_MISSED 162 uint8_t flags; uint8_t idx; uint8_t qid; } __packed; struct wpi_rx_stat { uint8_t len; #define WPI_STAT_MAXLEN 20 uint8_t id; uint8_t rssi; /* received signal strength */ #define WPI_RSSI_OFFSET 95 uint8_t agc; /* access gain control */ uint16_t signal; uint16_t noise; } __packed; struct wpi_rx_head { uint16_t chan; uint16_t flags; #define WPI_STAT_FLAG_SHPREAMBLE (1 << 2) uint8_t reserved; uint8_t plcp; uint16_t len; } __packed; struct wpi_rx_tail { uint32_t flags; #define WPI_RX_NO_CRC_ERR (1 << 0) #define WPI_RX_NO_OVFL_ERR (1 << 1) /* shortcut for the above */ #define WPI_RX_NOERROR (WPI_RX_NO_CRC_ERR | WPI_RX_NO_OVFL_ERR) #define WPI_RX_CIPHER_MASK (7 << 8) #define WPI_RX_CIPHER_CCMP (2 << 8) #define WPI_RX_DECRYPT_MASK (3 << 11) #define WPI_RX_DECRYPT_OK (3 << 11) uint64_t tstamp; uint32_t tbeacon; } __packed; struct wpi_tx_cmd { uint8_t code; #define WPI_CMD_RXON 16 #define WPI_CMD_RXON_ASSOC 17 #define WPI_CMD_EDCA_PARAMS 19 #define WPI_CMD_TIMING 20 #define WPI_CMD_ADD_NODE 24 #define WPI_CMD_DEL_NODE 25 #define WPI_CMD_TX_DATA 28 #define WPI_CMD_MRR_SETUP 71 #define WPI_CMD_SET_LED 72 #define WPI_CMD_SET_POWER_MODE 119 #define WPI_CMD_SCAN 128 #define WPI_CMD_SET_BEACON 145 #define WPI_CMD_TXPOWER 151 #define WPI_CMD_BT_COEX 155 #define WPI_CMD_GET_STATISTICS 156 uint8_t flags; uint8_t idx; uint8_t qid; uint8_t data[124]; } __packed; /* Structure for command WPI_CMD_RXON. */ struct wpi_rxon { uint8_t myaddr[IEEE80211_ADDR_LEN]; uint16_t reserved1; uint8_t bssid[IEEE80211_ADDR_LEN]; uint16_t reserved2; uint8_t wlap[IEEE80211_ADDR_LEN]; uint16_t reserved3; uint8_t mode; #define WPI_MODE_HOSTAP 1 #define WPI_MODE_STA 3 #define WPI_MODE_IBSS 4 #define WPI_MODE_MONITOR 6 uint8_t air; uint16_t reserved4; uint8_t ofdm_mask; uint8_t cck_mask; uint16_t associd; uint32_t flags; #define WPI_RXON_24GHZ (1 << 0) #define WPI_RXON_CCK (1 << 1) #define WPI_RXON_AUTO (1 << 2) #define WPI_RXON_SHSLOT (1 << 4) #define WPI_RXON_SHPREAMBLE (1 << 5) #define WPI_RXON_NODIVERSITY (1 << 7) #define WPI_RXON_ANTENNA_A (1 << 8) #define WPI_RXON_ANTENNA_B (1 << 9) #define WPI_RXON_TSF (1 << 15) #define WPI_RXON_CTS_TO_SELF (1 << 30) uint32_t filter; #define WPI_FILTER_PROMISC (1 << 0) #define WPI_FILTER_CTL (1 << 1) #define WPI_FILTER_MULTICAST (1 << 2) #define WPI_FILTER_NODECRYPT (1 << 3) #define WPI_FILTER_BSS (1 << 5) #define WPI_FILTER_BEACON (1 << 6) uint8_t chan; uint16_t reserved5; } __packed; /* Structure for command WPI_CMD_RXON_ASSOC. */ struct wpi_assoc { uint32_t flags; uint32_t filter; uint8_t ofdm_mask; uint8_t cck_mask; uint16_t reserved; } __packed; /* Structure for command WPI_CMD_EDCA_PARAMS. */ struct wpi_edca_params { uint32_t flags; #define WPI_EDCA_UPDATE (1 << 0) struct { uint16_t cwmin; uint16_t cwmax; uint8_t aifsn; uint8_t reserved; uint16_t txoplimit; } __packed ac[WME_NUM_AC]; } __packed; /* Structure for command WPI_CMD_TIMING. */ struct wpi_cmd_timing { uint64_t tstamp; uint16_t bintval; uint16_t atim; uint32_t binitval; uint16_t lintval; uint16_t reserved; } __packed; /* Structure for command WPI_CMD_ADD_NODE. */ struct wpi_node_info { uint8_t control; #define WPI_NODE_UPDATE (1 << 0) uint8_t reserved1[3]; uint8_t macaddr[IEEE80211_ADDR_LEN]; uint16_t reserved2; uint8_t id; #define WPI_ID_BSS 0 #define WPI_ID_IBSS_MIN 2 #define WPI_ID_IBSS_MAX 23 #define WPI_ID_BROADCAST 24 #define WPI_ID_UNDEFINED (uint8_t)-1 uint8_t flags; #define WPI_FLAG_KEY_SET (1 << 0) uint16_t reserved3; uint16_t kflags; #define WPI_KFLAG_CCMP (1 << 1) #define WPI_KFLAG_KID(kid) ((kid) << 8) #define WPI_KFLAG_MULTICAST (1 << 14) uint8_t tsc2; uint8_t reserved4; uint16_t ttak[5]; uint16_t reserved5; uint8_t key[IEEE80211_KEYBUF_SIZE]; uint32_t action; #define WPI_ACTION_SET_RATE (1 << 2) uint32_t mask; uint16_t tid; uint8_t plcp; uint8_t antenna; #define WPI_ANTENNA_A (1 << 6) #define WPI_ANTENNA_B (1 << 7) #define WPI_ANTENNA_BOTH (WPI_ANTENNA_A | WPI_ANTENNA_B) uint8_t add_imm; uint8_t del_imm; uint16_t add_imm_start; } __packed; /* Structure for command WPI_CMD_DEL_NODE. */ struct wpi_cmd_del_node { uint8_t count; uint8_t reserved1[3]; uint8_t macaddr[IEEE80211_ADDR_LEN]; uint16_t reserved2; } __packed; /* Structure for command WPI_CMD_TX_DATA. */ struct wpi_cmd_data { uint16_t len; uint16_t lnext; uint32_t flags; #define WPI_TX_NEED_RTS (1 << 1) #define WPI_TX_NEED_CTS (1 << 2) #define WPI_TX_NEED_ACK (1 << 3) #define WPI_TX_FULL_TXOP (1 << 7) #define WPI_TX_BT_DISABLE (1 << 12) /* bluetooth coexistence */ #define WPI_TX_AUTO_SEQ (1 << 13) +#define WPI_TX_MORE_FRAG (1 << 14) #define WPI_TX_INSERT_TSTAMP (1 << 16) uint8_t plcp; uint8_t id; uint8_t tid; uint8_t security; #define WPI_CIPHER_WEP 1 #define WPI_CIPHER_CCMP 2 #define WPI_CIPHER_TKIP 3 #define WPI_CIPHER_WEP104 9 uint8_t key[IEEE80211_KEYBUF_SIZE]; uint8_t tkip[IEEE80211_WEP_MICLEN]; uint32_t fnext; uint32_t lifetime; #define WPI_LIFETIME_INFINITE 0xffffffff uint8_t ofdm_mask; uint8_t cck_mask; uint8_t rts_ntries; uint8_t data_ntries; uint16_t timeout; uint16_t txop; } __packed; /* Structure for command WPI_CMD_SET_BEACON. */ struct wpi_cmd_beacon { uint16_t len; uint16_t reserved1; uint32_t flags; /* same as wpi_cmd_data */ uint8_t plcp; uint8_t id; uint8_t reserved2[30]; uint32_t lifetime; uint8_t ofdm_mask; uint8_t cck_mask; uint16_t reserved3[3]; uint16_t tim; uint8_t timsz; uint8_t reserved4; } __packed; /* Structure for notification WPI_BEACON_MISSED. */ struct wpi_beacon_missed { uint32_t consecutive; uint32_t total; uint32_t expected; uint32_t received; } __packed; /* Structure for command WPI_CMD_MRR_SETUP. */ #define WPI_RIDX_MAX 11 struct wpi_mrr_setup { uint32_t which; #define WPI_MRR_CTL 0 #define WPI_MRR_DATA 1 struct { uint8_t plcp; uint8_t flags; uint8_t ntries; uint8_t next; } __packed rates[WPI_RIDX_MAX + 1]; } __packed; /* Structure for command WPI_CMD_SET_LED. */ struct wpi_cmd_led { uint32_t unit; /* multiplier (in usecs) */ uint8_t which; #define WPI_LED_ACTIVITY 1 #define WPI_LED_LINK 2 uint8_t off; uint8_t on; uint8_t reserved; } __packed; /* Structure for command WPI_CMD_SET_POWER_MODE. */ struct wpi_pmgt_cmd { uint16_t flags; #define WPI_PS_ALLOW_SLEEP (1 << 0) #define WPI_PS_NOTIFY (1 << 1) #define WPI_PS_SLEEP_OVER_DTIM (1 << 2) #define WPI_PS_PCI_PMGT (1 << 3) uint8_t reserved[2]; uint32_t rxtimeout; uint32_t txtimeout; uint32_t intval[5]; } __packed; /* Structures for command WPI_CMD_SCAN. */ #define WPI_SCAN_MAX_ESSIDS 4 struct wpi_scan_essid { uint8_t id; uint8_t len; uint8_t data[IEEE80211_NWID_LEN]; } __packed; struct wpi_scan_hdr { uint16_t len; uint8_t reserved1; uint8_t nchan; uint16_t quiet_time; uint16_t quiet_threshold; uint16_t crc_threshold; uint16_t reserved2; uint32_t max_svc; /* background scans */ uint32_t pause_svc; /* background scans */ uint32_t flags; uint32_t filter; /* Followed by a struct wpi_cmd_data. */ /* Followed by an array of 4 structs wpi_scan_essid. */ /* Followed by probe request body. */ /* Followed by an array of ``nchan'' structs wpi_scan_chan. */ } __packed; struct wpi_scan_chan { uint8_t flags; #define WPI_CHAN_ACTIVE (1 << 0) #define WPI_CHAN_NPBREQS(x) (((1 << (x)) - 1) << 1) uint8_t chan; uint8_t rf_gain; uint8_t dsp_gain; uint16_t active; /* msecs */ uint16_t passive; /* msecs */ } __packed; #define WPI_SCAN_CRC_TH_DEFAULT htole16(1) #define WPI_SCAN_CRC_TH_NEVER htole16(0xffff) /* Maximum size of a scan command. */ #define WPI_SCAN_MAXSZ (MCLBYTES - 4) #define WPI_ACTIVE_DWELL_TIME_2GHZ (30) /* all times in msec */ #define WPI_ACTIVE_DWELL_TIME_5GHZ (20) #define WPI_ACTIVE_DWELL_FACTOR_2GHZ ( 3) #define WPI_ACTIVE_DWELL_FACTOR_5GHZ ( 2) #define WPI_PASSIVE_DWELL_TIME_2GHZ ( 20) #define WPI_PASSIVE_DWELL_TIME_5GHZ ( 10) #define WPI_PASSIVE_DWELL_BASE (100) /* Structure for command WPI_CMD_TXPOWER. */ struct wpi_cmd_txpower { uint8_t band; #define WPI_BAND_5GHZ 0 #define WPI_BAND_2GHZ 1 uint8_t reserved; uint16_t chan; struct { uint8_t plcp; uint8_t rf_gain; uint8_t dsp_gain; uint8_t reserved; } __packed rates[WPI_RIDX_MAX + 1]; } __packed; /* Structure for command WPI_CMD_BT_COEX. */ struct wpi_bluetooth { uint8_t flags; #define WPI_BT_COEX_DISABLE 0 #define WPI_BT_COEX_MODE_2WIRE 1 #define WPI_BT_COEX_MODE_3WIRE 2 #define WPI_BT_COEX_MODE_4WIRE 3 uint8_t lead_time; #define WPI_BT_LEAD_TIME_DEF 30 uint8_t max_kill; #define WPI_BT_MAX_KILL_DEF 5 uint8_t reserved; uint32_t kill_ack; uint32_t kill_cts; } __packed; /* Structure for WPI_UC_READY notification. */ struct wpi_ucode_info { uint8_t minor; uint8_t major; uint16_t reserved1; uint8_t revision[8]; uint8_t type; uint8_t subtype; uint16_t reserved2; uint32_t logptr; uint32_t errptr; uint32_t tstamp; uint32_t valid; } __packed; /* Structure for WPI_START_SCAN notification. */ struct wpi_start_scan { uint64_t tstamp; uint32_t tbeacon; uint8_t chan; uint8_t band; uint16_t reserved; uint32_t status; } __packed; /* Structure for WPI_STOP_SCAN notification. */ struct wpi_stop_scan { uint8_t nchan; uint8_t status; uint8_t reserved; uint8_t chan; uint64_t tsf; } __packed; /* Structures for WPI_{RX,BEACON}_STATISTICS notification. */ struct wpi_rx_phy_stats { uint32_t ina; uint32_t fina; uint32_t bad_plcp; uint32_t bad_crc32; uint32_t overrun; uint32_t eoverrun; uint32_t good_crc32; uint32_t fa; uint32_t bad_fina_sync; uint32_t sfd_timeout; uint32_t fina_timeout; uint32_t no_rts_ack; uint32_t rxe_limit; uint32_t ack; uint32_t cts; } __packed; struct wpi_rx_general_stats { uint32_t bad_cts; uint32_t bad_ack; uint32_t not_bss; uint32_t filtered; uint32_t bad_chan; } __packed; struct wpi_rx_stats { struct wpi_rx_phy_stats ofdm; struct wpi_rx_phy_stats cck; struct wpi_rx_general_stats general; } __packed; struct wpi_tx_stats { uint32_t preamble; uint32_t rx_detected; uint32_t bt_defer; uint32_t bt_kill; uint32_t short_len; uint32_t cts_timeout; uint32_t ack_timeout; uint32_t exp_ack; uint32_t ack; } __packed; struct wpi_general_stats { uint32_t temp; uint32_t burst_check; uint32_t burst; uint32_t reserved[4]; uint32_t sleep; uint32_t slot_out; uint32_t slot_idle; uint32_t ttl_tstamp; uint32_t tx_ant_a; uint32_t tx_ant_b; uint32_t exec; uint32_t probe; } __packed; struct wpi_stats { uint32_t flags; struct wpi_rx_stats rx; struct wpi_tx_stats tx; struct wpi_general_stats general; } __packed; /* Possible flags for command WPI_CMD_GET_STATISTICS. */ #define WPI_STATISTICS_BEACON_DISABLE (1 << 1) /* Firmware error dump entry. */ struct wpi_fw_dump { uint32_t desc; uint32_t time; uint32_t blink[2]; uint32_t ilink[2]; uint32_t data; } __packed; /* Firmware image file header. */ struct wpi_firmware_hdr { #define WPI_FW_MINVERSION 2144 #define WPI_FW_NAME "wpifw" uint16_t driver; uint8_t minor; uint8_t major; uint32_t rtextsz; uint32_t rdatasz; uint32_t itextsz; uint32_t idatasz; uint32_t btextsz; } __packed; #define WPI_FW_TEXT_MAXSZ ( 80 * 1024 ) #define WPI_FW_DATA_MAXSZ ( 32 * 1024 ) #define WPI_FW_BOOT_TEXT_MAXSZ 1024 #define WPI_FW_UPDATED (1U << 31 ) /* * Offsets into EEPROM. */ #define WPI_EEPROM_MAC 0x015 #define WPI_EEPROM_REVISION 0x035 #define WPI_EEPROM_SKU_CAP 0x045 #define WPI_EEPROM_TYPE 0x04a #define WPI_EEPROM_DOMAIN 0x060 #define WPI_EEPROM_BAND1 0x063 #define WPI_EEPROM_BAND2 0x072 #define WPI_EEPROM_BAND3 0x080 #define WPI_EEPROM_BAND4 0x08d #define WPI_EEPROM_BAND5 0x099 #define WPI_EEPROM_POWER_GRP 0x100 struct wpi_eeprom_chan { uint8_t flags; #define WPI_EEPROM_CHAN_VALID (1 << 0) #define WPI_EEPROM_CHAN_IBSS (1 << 1) #define WPI_EEPROM_CHAN_ACTIVE (1 << 3) #define WPI_EEPROM_CHAN_RADAR (1 << 4) int8_t maxpwr; } __packed; struct wpi_eeprom_sample { uint8_t index; int8_t power; uint16_t volt; } __packed; #define WPI_POWER_GROUPS_COUNT 5 struct wpi_eeprom_group { struct wpi_eeprom_sample samples[5]; int32_t coef[5]; int32_t corr[5]; int8_t maxpwr; uint8_t chan; int16_t temp; } __packed; #define WPI_CHAN_BANDS_COUNT 5 #define WPI_MAX_CHAN_PER_BAND 14 static const struct wpi_chan_band { uint32_t addr; /* offset in EEPROM */ uint8_t nchan; uint8_t chan[WPI_MAX_CHAN_PER_BAND]; } wpi_bands[] = { /* 20MHz channels, 2GHz band. */ { WPI_EEPROM_BAND1, 14, { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 } }, /* 20MHz channels, 5GHz band. */ { WPI_EEPROM_BAND2, 13, { 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16 } }, { WPI_EEPROM_BAND3, 12, { 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 } }, { WPI_EEPROM_BAND4, 11, { 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 } }, { WPI_EEPROM_BAND5, 6, { 145, 149, 153, 157, 161, 165 } } }; /* HW rate indices. */ #define WPI_RIDX_OFDM6 0 #define WPI_RIDX_OFDM36 5 #define WPI_RIDX_OFDM48 6 #define WPI_RIDX_OFDM54 7 #define WPI_RIDX_CCK1 8 #define WPI_RIDX_CCK2 9 #define WPI_RIDX_CCK11 11 static const uint8_t wpi_ridx_to_plcp[] = { /* OFDM: IEEE Std 802.11a-1999, pp. 14 Table 80 */ /* R1-R4 (ral/ural is R4-R1) */ 0xd, 0xf, 0x5, 0x7, 0x9, 0xb, 0x1, 0x3, /* CCK: device-dependent */ 10, 20, 55, 110 }; #define WPI_MAX_PWR_INDEX 77 /* * RF Tx gain values from highest to lowest power (values obtained from * the reference driver.) */ static const uint8_t wpi_rf_gain_2ghz[WPI_MAX_PWR_INDEX + 1] = { 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xbb, 0xbb, 0xbb, 0xbb, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xd3, 0xd3, 0xb3, 0xb3, 0xb3, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x73, 0xeb, 0xeb, 0xeb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xab, 0xab, 0xab, 0x8b, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xc3, 0xc3, 0xc3, 0xc3, 0xa3, 0xa3, 0xa3, 0xa3, 0x83, 0x83, 0x83, 0x83, 0x63, 0x63, 0x63, 0x63, 0x43, 0x43, 0x43, 0x43, 0x23, 0x23, 0x23, 0x23, 0x03, 0x03, 0x03, 0x03 }; static const uint8_t wpi_rf_gain_5ghz[WPI_MAX_PWR_INDEX + 1] = { 0xfb, 0xfb, 0xfb, 0xdb, 0xdb, 0xbb, 0xbb, 0x9b, 0x9b, 0x7b, 0x7b, 0x7b, 0x7b, 0x5b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x1b, 0x1b, 0x1b, 0x73, 0x73, 0x73, 0x53, 0x53, 0x53, 0x53, 0x53, 0x33, 0x33, 0x33, 0x33, 0x13, 0x13, 0x13, 0x13, 0x13, 0xab, 0xab, 0xab, 0x8b, 0x8b, 0x8b, 0x8b, 0x6b, 0x6b, 0x6b, 0x6b, 0x4b, 0x4b, 0x4b, 0x4b, 0x2b, 0x2b, 0x2b, 0x2b, 0x0b, 0x0b, 0x0b, 0x0b, 0x83, 0x83, 0x63, 0x63, 0x63, 0x63, 0x43, 0x43, 0x43, 0x43, 0x23, 0x23, 0x23, 0x23, 0x03 }; /* * DSP pre-DAC gain values from highest to lowest power (values obtained * from the reference driver.) */ static const uint8_t wpi_dsp_gain_2ghz[WPI_MAX_PWR_INDEX + 1] = { 0x7f, 0x7f, 0x7f, 0x7f, 0x7d, 0x6e, 0x69, 0x62, 0x7d, 0x73, 0x6c, 0x63, 0x77, 0x6f, 0x69, 0x61, 0x5c, 0x6a, 0x64, 0x78, 0x71, 0x6b, 0x7d, 0x77, 0x70, 0x6a, 0x65, 0x61, 0x5b, 0x6b, 0x79, 0x73, 0x6d, 0x7f, 0x79, 0x73, 0x6c, 0x66, 0x60, 0x5c, 0x6e, 0x68, 0x62, 0x74, 0x7d, 0x77, 0x71, 0x6b, 0x65, 0x60, 0x71, 0x6a, 0x66, 0x5f, 0x71, 0x6a, 0x66, 0x5f, 0x71, 0x6a, 0x66, 0x5f, 0x71, 0x6a, 0x66, 0x5f, 0x71, 0x6a, 0x66, 0x5f, 0x71, 0x6a, 0x66, 0x5f, 0x71, 0x6a, 0x66, 0x5f }; static const uint8_t wpi_dsp_gain_5ghz[WPI_MAX_PWR_INDEX + 1] = { 0x7f, 0x78, 0x72, 0x77, 0x65, 0x71, 0x66, 0x72, 0x67, 0x75, 0x6b, 0x63, 0x5c, 0x6c, 0x7d, 0x76, 0x6d, 0x66, 0x60, 0x5a, 0x68, 0x62, 0x5c, 0x76, 0x6f, 0x68, 0x7e, 0x79, 0x71, 0x69, 0x63, 0x76, 0x6f, 0x68, 0x62, 0x74, 0x6d, 0x66, 0x62, 0x5d, 0x71, 0x6b, 0x63, 0x78, 0x71, 0x6b, 0x63, 0x78, 0x71, 0x6b, 0x63, 0x78, 0x71, 0x6b, 0x63, 0x78, 0x71, 0x6b, 0x63, 0x78, 0x71, 0x6b, 0x63, 0x6b, 0x63, 0x78, 0x71, 0x6b, 0x63, 0x78, 0x71, 0x6b, 0x63, 0x78, 0x71, 0x6b, 0x63, 0x78 }; /* * Power saving settings (values obtained from the reference driver.) */ #define WPI_NDTIMRANGES 2 #define WPI_NPOWERLEVELS 6 static const struct wpi_pmgt { uint32_t rxtimeout; uint32_t txtimeout; uint32_t intval[5]; int skip_dtim; } wpi_pmgt[WPI_NDTIMRANGES][WPI_NPOWERLEVELS] = { /* DTIM <= 10 */ { { 0, 0, { 0, 0, 0, 0, 0 }, 0 }, /* CAM */ { 200, 500, { 1, 2, 3, 4, 4 }, 0 }, /* PS level 1 */ { 200, 300, { 2, 4, 6, 7, 7 }, 0 }, /* PS level 2 */ { 50, 100, { 2, 6, 9, 9, 10 }, 0 }, /* PS level 3 */ { 50, 25, { 2, 7, 9, 9, 10 }, 1 }, /* PS level 4 */ { 25, 25, { 4, 7, 10, 10, 10 }, 1 } /* PS level 5 */ }, /* DTIM >= 11 */ { { 0, 0, { 0, 0, 0, 0, 0 }, 0 }, /* CAM */ { 200, 500, { 1, 2, 3, 4, -1 }, 0 }, /* PS level 1 */ { 200, 300, { 2, 4, 6, 7, -1 }, 0 }, /* PS level 2 */ { 50, 100, { 2, 6, 9, 9, -1 }, 0 }, /* PS level 3 */ { 50, 25, { 2, 7, 9, 9, -1 }, 0 }, /* PS level 4 */ { 25, 25, { 4, 7, 10, 10, -1 }, 0 } /* PS level 5 */ } }; /* Firmware errors. */ static const char * const wpi_fw_errmsg[] = { "OK", "FAIL", "BAD_PARAM", "BAD_CHECKSUM", "NMI_INTERRUPT", "SYSASSERT", "FATAL_ERROR" }; - -/* XXX description for some error codes (error data). */ -/* 0x00000074 - wrong totlen field */ -/* 0x000003B3 - powersave error */ -/* 0x00000447 - wrong channel selected */ #define WPI_READ(sc, reg) \ bus_space_read_4((sc)->sc_st, (sc)->sc_sh, (reg)) #define WPI_WRITE(sc, reg, val) \ bus_space_write_4((sc)->sc_st, (sc)->sc_sh, (reg), (val)) #define WPI_WRITE_REGION_4(sc, offset, datap, count) \ bus_space_write_region_4((sc)->sc_st, (sc)->sc_sh, (offset), \ (datap), (count)) #define WPI_SETBITS(sc, reg, mask) \ WPI_WRITE(sc, reg, WPI_READ(sc, reg) | (mask)) #define WPI_CLRBITS(sc, reg, mask) \ WPI_WRITE(sc, reg, WPI_READ(sc, reg) & ~(mask)) #define WPI_BARRIER_WRITE(sc) \ bus_space_barrier((sc)->sc_st, (sc)->sc_sh, 0, (sc)->sc_sz, \ BUS_SPACE_BARRIER_WRITE) #define WPI_BARRIER_READ_WRITE(sc) \ bus_space_barrier((sc)->sc_st, (sc)->sc_sh, 0, (sc)->sc_sz, \ BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE) Index: projects/building-blocks/sys/i386/i386/db_disasm.c =================================================================== --- projects/building-blocks/sys/i386/i386/db_disasm.c (revision 278776) +++ projects/building-blocks/sys/i386/i386/db_disasm.c (revision 278777) @@ -1,1481 +1,1501 @@ /*- * Mach Operating System * Copyright (c) 1991,1990 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ #include __FBSDID("$FreeBSD$"); /* * Instruction disassembler. */ #include #include #include #include /* * Size attributes */ #define BYTE 0 #define WORD 1 #define LONG 2 #define QUAD 3 #define SNGL 4 #define DBLR 5 #define EXTR 6 #define SDEP 7 #define NONE 8 /* * Addressing modes */ #define E 1 /* general effective address */ #define Eind 2 /* indirect address (jump, call) */ #define Ew 3 /* address, word size */ #define Eb 4 /* address, byte size */ #define R 5 /* register, in 'reg' field */ #define Rw 6 /* word register, in 'reg' field */ #define Ri 7 /* register in instruction */ #define S 8 /* segment reg, in 'reg' field */ #define Si 9 /* segment reg, in instruction */ #define A 10 /* accumulator */ #define BX 11 /* (bx) */ #define CL 12 /* cl, for shifts */ #define DX 13 /* dx, for IO */ #define SI 14 /* si */ #define DI 15 /* di */ #define CR 16 /* control register */ #define DR 17 /* debug register */ #define TR 18 /* test register */ #define I 19 /* immediate, unsigned */ #define Is 20 /* immediate, signed */ #define Ib 21 /* byte immediate, unsigned */ #define Ibs 22 /* byte immediate, signed */ #define Iw 23 /* word immediate, unsigned */ #define O 25 /* direct address */ #define Db 26 /* byte displacement from EIP */ #define Dl 27 /* long displacement from EIP */ #define o1 28 /* constant 1 */ #define o3 29 /* constant 3 */ #define OS 30 /* immediate offset/segment */ #define ST 31 /* FP stack top */ #define STI 32 /* FP stack */ #define X 33 /* extended FP op */ #define XA 34 /* for 'fstcw %ax' */ #define El 35 /* address, long size */ #define Ril 36 /* long register in instruction */ #define Iba 37 /* byte immediate, don't print if 0xa */ struct inst { const char * i_name; /* name */ short i_has_modrm; /* has regmodrm byte */ short i_size; /* operand size */ int i_mode; /* addressing modes */ const void * i_extra; /* pointer to extra opcode table */ }; #define op1(x) (x) #define op2(x,y) ((x)|((y)<<8)) #define op3(x,y,z) ((x)|((y)<<8)|((z)<<16)) struct finst { const char * f_name; /* name for memory instruction */ int f_size; /* size for memory instruction */ int f_rrmode; /* mode for rr instruction */ const void * f_rrname; /* name for rr instruction (or pointer to table) */ }; static const char * const db_Grp6[] = { "sldt", "str", "lldt", "ltr", "verr", "verw", "", "" }; static const char * const db_Grp7[] = { "sgdt", "sidt", "lgdt", "lidt", "smsw", "", "lmsw", "invlpg" }; static const char * const db_Grp8[] = { "", "", "", "", "bt", "bts", "btr", "btc" }; static const char * const db_Grp9[] = { "", "cmpxchg8b", "", "", "", "", "", "" }; static const char * const db_Grp15[] = { "fxsave", "fxrstor", "ldmxcsr", "stmxcsr", "", "", "", "clflush" }; static const char * const db_Grp15b[] = { "", "", "", "", "", "lfence", "mfence", "sfence" }; static const struct inst db_inst_0f0x[] = { /*00*/ { "", TRUE, NONE, op1(Ew), db_Grp6 }, /*01*/ { "", TRUE, NONE, op1(Ew), db_Grp7 }, /*02*/ { "lar", TRUE, LONG, op2(E,R), 0 }, /*03*/ { "lsl", TRUE, LONG, op2(E,R), 0 }, /*04*/ { "", FALSE, NONE, 0, 0 }, /*05*/ { "syscall",FALSE,NONE, 0, 0 }, /*06*/ { "clts", FALSE, NONE, 0, 0 }, /*07*/ { "sysret",FALSE, NONE, 0, 0 }, /*08*/ { "invd", FALSE, NONE, 0, 0 }, /*09*/ { "wbinvd",FALSE, NONE, 0, 0 }, /*0a*/ { "", FALSE, NONE, 0, 0 }, /*0b*/ { "", FALSE, NONE, 0, 0 }, /*0c*/ { "", FALSE, NONE, 0, 0 }, /*0d*/ { "", FALSE, NONE, 0, 0 }, /*0e*/ { "", FALSE, NONE, 0, 0 }, /*0f*/ { "", FALSE, NONE, 0, 0 }, }; +static const struct inst db_inst_0f1x[] = { +/*10*/ { "", FALSE, NONE, 0, 0 }, +/*11*/ { "", FALSE, NONE, 0, 0 }, +/*12*/ { "", FALSE, NONE, 0, 0 }, +/*13*/ { "", FALSE, NONE, 0, 0 }, +/*14*/ { "", FALSE, NONE, 0, 0 }, +/*15*/ { "", FALSE, NONE, 0, 0 }, +/*16*/ { "", FALSE, NONE, 0, 0 }, +/*17*/ { "", FALSE, NONE, 0, 0 }, + +/*18*/ { "", FALSE, NONE, 0, 0 }, +/*19*/ { "", FALSE, NONE, 0, 0 }, +/*1a*/ { "", FALSE, NONE, 0, 0 }, +/*1b*/ { "", FALSE, NONE, 0, 0 }, +/*1c*/ { "", FALSE, NONE, 0, 0 }, +/*1d*/ { "", FALSE, NONE, 0, 0 }, +/*1e*/ { "", FALSE, NONE, 0, 0 }, +/*1f*/ { "nopl", TRUE, SDEP, 0, "nopw" }, +}; + static const struct inst db_inst_0f2x[] = { /*20*/ { "mov", TRUE, LONG, op2(CR,El), 0 }, /*21*/ { "mov", TRUE, LONG, op2(DR,El), 0 }, /*22*/ { "mov", TRUE, LONG, op2(El,CR), 0 }, /*23*/ { "mov", TRUE, LONG, op2(El,DR), 0 }, /*24*/ { "mov", TRUE, LONG, op2(TR,El), 0 }, /*25*/ { "", FALSE, NONE, 0, 0 }, /*26*/ { "mov", TRUE, LONG, op2(El,TR), 0 }, /*27*/ { "", FALSE, NONE, 0, 0 }, /*28*/ { "", FALSE, NONE, 0, 0 }, /*29*/ { "", FALSE, NONE, 0, 0 }, /*2a*/ { "", FALSE, NONE, 0, 0 }, /*2b*/ { "", FALSE, NONE, 0, 0 }, /*2c*/ { "", FALSE, NONE, 0, 0 }, /*2d*/ { "", FALSE, NONE, 0, 0 }, /*2e*/ { "", FALSE, NONE, 0, 0 }, /*2f*/ { "", FALSE, NONE, 0, 0 }, }; static const struct inst db_inst_0f3x[] = { /*30*/ { "wrmsr", FALSE, NONE, 0, 0 }, /*31*/ { "rdtsc", FALSE, NONE, 0, 0 }, /*32*/ { "rdmsr", FALSE, NONE, 0, 0 }, /*33*/ { "rdpmc", FALSE, NONE, 0, 0 }, /*34*/ { "sysenter",FALSE,NONE, 0, 0 }, /*35*/ { "sysexit",FALSE,NONE, 0, 0 }, /*36*/ { "", FALSE, NONE, 0, 0 }, /*37*/ { "getsec",FALSE, NONE, 0, 0 }, /*38*/ { "", FALSE, NONE, 0, 0 }, /*39*/ { "", FALSE, NONE, 0, 0 }, /*3a*/ { "", FALSE, NONE, 0, 0 }, /*3b*/ { "", FALSE, NONE, 0, 0 }, /*3c*/ { "", FALSE, NONE, 0, 0 }, /*3d*/ { "", FALSE, NONE, 0, 0 }, /*3e*/ { "", FALSE, NONE, 0, 0 }, /*3f*/ { "", FALSE, NONE, 0, 0 }, }; static const struct inst db_inst_0f4x[] = { /*40*/ { "cmovo", TRUE, NONE, op2(E, R), 0 }, /*41*/ { "cmovno", TRUE, NONE, op2(E, R), 0 }, /*42*/ { "cmovb", TRUE, NONE, op2(E, R), 0 }, /*43*/ { "cmovnb", TRUE, NONE, op2(E, R), 0 }, /*44*/ { "cmovz", TRUE, NONE, op2(E, R), 0 }, /*45*/ { "cmovnz", TRUE, NONE, op2(E, R), 0 }, /*46*/ { "cmovbe", TRUE, NONE, op2(E, R), 0 }, /*47*/ { "cmovnbe",TRUE, NONE, op2(E, R), 0 }, /*48*/ { "cmovs", TRUE, NONE, op2(E, R), 0 }, /*49*/ { "cmovns", TRUE, NONE, op2(E, R), 0 }, /*4a*/ { "cmovp", TRUE, NONE, op2(E, R), 0 }, /*4b*/ { "cmovnp", TRUE, NONE, op2(E, R), 0 }, /*4c*/ { "cmovl", TRUE, NONE, op2(E, R), 0 }, /*4d*/ { "cmovnl", TRUE, NONE, op2(E, R), 0 }, /*4e*/ { "cmovle", TRUE, NONE, op2(E, R), 0 }, /*4f*/ { "cmovnle",TRUE, NONE, op2(E, R), 0 }, }; static const struct inst db_inst_0f8x[] = { /*80*/ { "jo", FALSE, NONE, op1(Dl), 0 }, /*81*/ { "jno", FALSE, NONE, op1(Dl), 0 }, /*82*/ { "jb", FALSE, NONE, op1(Dl), 0 }, /*83*/ { "jnb", FALSE, NONE, op1(Dl), 0 }, /*84*/ { "jz", FALSE, NONE, op1(Dl), 0 }, /*85*/ { "jnz", FALSE, NONE, op1(Dl), 0 }, /*86*/ { "jbe", FALSE, NONE, op1(Dl), 0 }, /*87*/ { "jnbe", FALSE, NONE, op1(Dl), 0 }, /*88*/ { "js", FALSE, NONE, op1(Dl), 0 }, /*89*/ { "jns", FALSE, NONE, op1(Dl), 0 }, /*8a*/ { "jp", FALSE, NONE, op1(Dl), 0 }, /*8b*/ { "jnp", FALSE, NONE, op1(Dl), 0 }, /*8c*/ { "jl", FALSE, NONE, op1(Dl), 0 }, /*8d*/ { "jnl", FALSE, NONE, op1(Dl), 0 }, /*8e*/ { "jle", FALSE, NONE, op1(Dl), 0 }, /*8f*/ { "jnle", FALSE, NONE, op1(Dl), 0 }, }; static const struct inst db_inst_0f9x[] = { /*90*/ { "seto", TRUE, NONE, op1(Eb), 0 }, /*91*/ { "setno", TRUE, NONE, op1(Eb), 0 }, /*92*/ { "setb", TRUE, NONE, op1(Eb), 0 }, /*93*/ { "setnb", TRUE, NONE, op1(Eb), 0 }, /*94*/ { "setz", TRUE, NONE, op1(Eb), 0 }, /*95*/ { "setnz", TRUE, NONE, op1(Eb), 0 }, /*96*/ { "setbe", TRUE, NONE, op1(Eb), 0 }, /*97*/ { "setnbe",TRUE, NONE, op1(Eb), 0 }, /*98*/ { "sets", TRUE, NONE, op1(Eb), 0 }, /*99*/ { "setns", TRUE, NONE, op1(Eb), 0 }, /*9a*/ { "setp", TRUE, NONE, op1(Eb), 0 }, /*9b*/ { "setnp", TRUE, NONE, op1(Eb), 0 }, /*9c*/ { "setl", TRUE, NONE, op1(Eb), 0 }, /*9d*/ { "setnl", TRUE, NONE, op1(Eb), 0 }, /*9e*/ { "setle", TRUE, NONE, op1(Eb), 0 }, /*9f*/ { "setnle",TRUE, NONE, op1(Eb), 0 }, }; static const struct inst db_inst_0fax[] = { /*a0*/ { "push", FALSE, NONE, op1(Si), 0 }, /*a1*/ { "pop", FALSE, NONE, op1(Si), 0 }, /*a2*/ { "cpuid", FALSE, NONE, 0, 0 }, /*a3*/ { "bt", TRUE, LONG, op2(R,E), 0 }, /*a4*/ { "shld", TRUE, LONG, op3(Ib,R,E), 0 }, /*a5*/ { "shld", TRUE, LONG, op3(CL,R,E), 0 }, /*a6*/ { "", FALSE, NONE, 0, 0 }, /*a7*/ { "", FALSE, NONE, 0, 0 }, /*a8*/ { "push", FALSE, NONE, op1(Si), 0 }, /*a9*/ { "pop", FALSE, NONE, op1(Si), 0 }, /*aa*/ { "rsm", FALSE, NONE, 0, 0 }, /*ab*/ { "bts", TRUE, LONG, op2(R,E), 0 }, /*ac*/ { "shrd", TRUE, LONG, op3(Ib,R,E), 0 }, /*ad*/ { "shrd", TRUE, LONG, op3(CL,R,E), 0 }, /*ae*/ { "", TRUE, LONG, op1(E), db_Grp15 }, /*af*/ { "imul", TRUE, LONG, op2(E,R), 0 }, }; static const struct inst db_inst_0fbx[] = { /*b0*/ { "cmpxchg",TRUE, BYTE, op2(R, E), 0 }, /*b0*/ { "cmpxchg",TRUE, LONG, op2(R, E), 0 }, /*b2*/ { "lss", TRUE, LONG, op2(E, R), 0 }, /*b3*/ { "btr", TRUE, LONG, op2(R, E), 0 }, /*b4*/ { "lfs", TRUE, LONG, op2(E, R), 0 }, /*b5*/ { "lgs", TRUE, LONG, op2(E, R), 0 }, /*b6*/ { "movzb", TRUE, LONG, op2(Eb, R), 0 }, /*b7*/ { "movzw", TRUE, LONG, op2(Ew, R), 0 }, /*b8*/ { "", FALSE, NONE, 0, 0 }, /*b9*/ { "", FALSE, NONE, 0, 0 }, /*ba*/ { "", TRUE, LONG, op2(Ib, E), db_Grp8 }, /*bb*/ { "btc", TRUE, LONG, op2(R, E), 0 }, /*bc*/ { "bsf", TRUE, LONG, op2(E, R), 0 }, /*bd*/ { "bsr", TRUE, LONG, op2(E, R), 0 }, /*be*/ { "movsb", TRUE, LONG, op2(Eb, R), 0 }, /*bf*/ { "movsw", TRUE, LONG, op2(Ew, R), 0 }, }; static const struct inst db_inst_0fcx[] = { /*c0*/ { "xadd", TRUE, BYTE, op2(R, E), 0 }, /*c1*/ { "xadd", TRUE, LONG, op2(R, E), 0 }, /*c2*/ { "", FALSE, NONE, 0, 0 }, /*c3*/ { "", FALSE, NONE, 0, 0 }, /*c4*/ { "", FALSE, NONE, 0, 0 }, /*c5*/ { "", FALSE, NONE, 0, 0 }, /*c6*/ { "", FALSE, NONE, 0, 0 }, /*c7*/ { "", TRUE, NONE, op1(E), db_Grp9 }, /*c8*/ { "bswap", FALSE, LONG, op1(Ril), 0 }, /*c9*/ { "bswap", FALSE, LONG, op1(Ril), 0 }, /*ca*/ { "bswap", FALSE, LONG, op1(Ril), 0 }, /*cb*/ { "bswap", FALSE, LONG, op1(Ril), 0 }, /*cc*/ { "bswap", FALSE, LONG, op1(Ril), 0 }, /*cd*/ { "bswap", FALSE, LONG, op1(Ril), 0 }, /*ce*/ { "bswap", FALSE, LONG, op1(Ril), 0 }, /*cf*/ { "bswap", FALSE, LONG, op1(Ril), 0 }, }; static const struct inst * const db_inst_0f[] = { db_inst_0f0x, - 0, + db_inst_0f1x, db_inst_0f2x, db_inst_0f3x, db_inst_0f4x, 0, 0, 0, db_inst_0f8x, db_inst_0f9x, db_inst_0fax, db_inst_0fbx, db_inst_0fcx, 0, 0, 0 }; static const char * const db_Esc92[] = { "fnop", "", "", "", "", "", "", "" }; static const char * const db_Esc94[] = { "fchs", "fabs", "", "", "ftst", "fxam", "", "" }; static const char * const db_Esc95[] = { "fld1", "fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz","" }; static const char * const db_Esc96[] = { "f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp", "fincstp" }; static const char * const db_Esc97[] = { "fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos" }; static const char * const db_Esca5[] = { "", "fucompp","", "", "", "", "", "" }; static const char * const db_Escb4[] = { "fneni","fndisi", "fnclex","fninit","fsetpm", "", "", "" }; static const char * const db_Esce3[] = { "", "fcompp","", "", "", "", "", "" }; static const char * const db_Escf4[] = { "fnstsw","", "", "", "", "", "", "" }; static const struct finst db_Esc8[] = { /*0*/ { "fadd", SNGL, op2(STI,ST), 0 }, /*1*/ { "fmul", SNGL, op2(STI,ST), 0 }, /*2*/ { "fcom", SNGL, op2(STI,ST), 0 }, /*3*/ { "fcomp", SNGL, op2(STI,ST), 0 }, /*4*/ { "fsub", SNGL, op2(STI,ST), 0 }, /*5*/ { "fsubr", SNGL, op2(STI,ST), 0 }, /*6*/ { "fdiv", SNGL, op2(STI,ST), 0 }, /*7*/ { "fdivr", SNGL, op2(STI,ST), 0 }, }; static const struct finst db_Esc9[] = { /*0*/ { "fld", SNGL, op1(STI), 0 }, /*1*/ { "", NONE, op1(STI), "fxch" }, /*2*/ { "fst", SNGL, op1(X), db_Esc92 }, /*3*/ { "fstp", SNGL, 0, 0 }, /*4*/ { "fldenv", NONE, op1(X), db_Esc94 }, /*5*/ { "fldcw", NONE, op1(X), db_Esc95 }, /*6*/ { "fnstenv",NONE, op1(X), db_Esc96 }, /*7*/ { "fnstcw", NONE, op1(X), db_Esc97 }, }; static const struct finst db_Esca[] = { /*0*/ { "fiadd", LONG, 0, 0 }, /*1*/ { "fimul", LONG, 0, 0 }, /*2*/ { "ficom", LONG, 0, 0 }, /*3*/ { "ficomp", LONG, 0, 0 }, /*4*/ { "fisub", LONG, 0, 0 }, /*5*/ { "fisubr", LONG, op1(X), db_Esca5 }, /*6*/ { "fidiv", LONG, 0, 0 }, /*7*/ { "fidivr", LONG, 0, 0 } }; static const struct finst db_Escb[] = { /*0*/ { "fild", LONG, 0, 0 }, /*1*/ { "", NONE, 0, 0 }, /*2*/ { "fist", LONG, 0, 0 }, /*3*/ { "fistp", LONG, 0, 0 }, /*4*/ { "", WORD, op1(X), db_Escb4 }, /*5*/ { "fld", EXTR, 0, 0 }, /*6*/ { "", WORD, 0, 0 }, /*7*/ { "fstp", EXTR, 0, 0 }, }; static const struct finst db_Escc[] = { /*0*/ { "fadd", DBLR, op2(ST,STI), 0 }, /*1*/ { "fmul", DBLR, op2(ST,STI), 0 }, /*2*/ { "fcom", DBLR, 0, 0 }, /*3*/ { "fcomp", DBLR, 0, 0 }, /*4*/ { "fsub", DBLR, op2(ST,STI), "fsubr" }, /*5*/ { "fsubr", DBLR, op2(ST,STI), "fsub" }, /*6*/ { "fdiv", DBLR, op2(ST,STI), "fdivr" }, /*7*/ { "fdivr", DBLR, op2(ST,STI), "fdiv" }, }; static const struct finst db_Escd[] = { /*0*/ { "fld", DBLR, op1(STI), "ffree" }, /*1*/ { "", NONE, 0, 0 }, /*2*/ { "fst", DBLR, op1(STI), 0 }, /*3*/ { "fstp", DBLR, op1(STI), 0 }, /*4*/ { "frstor", NONE, op1(STI), "fucom" }, /*5*/ { "", NONE, op1(STI), "fucomp" }, /*6*/ { "fnsave", NONE, 0, 0 }, /*7*/ { "fnstsw", NONE, 0, 0 }, }; static const struct finst db_Esce[] = { /*0*/ { "fiadd", WORD, op2(ST,STI), "faddp" }, /*1*/ { "fimul", WORD, op2(ST,STI), "fmulp" }, /*2*/ { "ficom", WORD, 0, 0 }, /*3*/ { "ficomp", WORD, op1(X), db_Esce3 }, /*4*/ { "fisub", WORD, op2(ST,STI), "fsubrp" }, /*5*/ { "fisubr", WORD, op2(ST,STI), "fsubp" }, /*6*/ { "fidiv", WORD, op2(ST,STI), "fdivrp" }, /*7*/ { "fidivr", WORD, op2(ST,STI), "fdivp" }, }; static const struct finst db_Escf[] = { /*0*/ { "fild", WORD, 0, 0 }, /*1*/ { "", NONE, 0, 0 }, /*2*/ { "fist", WORD, 0, 0 }, /*3*/ { "fistp", WORD, 0, 0 }, /*4*/ { "fbld", NONE, op1(XA), db_Escf4 }, /*5*/ { "fild", QUAD, 0, 0 }, /*6*/ { "fbstp", NONE, 0, 0 }, /*7*/ { "fistp", QUAD, 0, 0 }, }; static const struct finst * const db_Esc_inst[] = { db_Esc8, db_Esc9, db_Esca, db_Escb, db_Escc, db_Escd, db_Esce, db_Escf }; static const char * const db_Grp1[] = { "add", "or", "adc", "sbb", "and", "sub", "xor", "cmp" }; static const char * const db_Grp2[] = { "rol", "ror", "rcl", "rcr", "shl", "shr", "shl", "sar" }; static const struct inst db_Grp3[] = { { "test", TRUE, NONE, op2(I,E), 0 }, { "test", TRUE, NONE, op2(I,E), 0 }, { "not", TRUE, NONE, op1(E), 0 }, { "neg", TRUE, NONE, op1(E), 0 }, { "mul", TRUE, NONE, op2(E,A), 0 }, { "imul", TRUE, NONE, op2(E,A), 0 }, { "div", TRUE, NONE, op2(E,A), 0 }, { "idiv", TRUE, NONE, op2(E,A), 0 }, }; static const struct inst db_Grp4[] = { { "inc", TRUE, BYTE, op1(E), 0 }, { "dec", TRUE, BYTE, op1(E), 0 }, { "", TRUE, NONE, 0, 0 }, { "", TRUE, NONE, 0, 0 }, { "", TRUE, NONE, 0, 0 }, { "", TRUE, NONE, 0, 0 }, { "", TRUE, NONE, 0, 0 }, { "", TRUE, NONE, 0, 0 } }; static const struct inst db_Grp5[] = { { "inc", TRUE, LONG, op1(E), 0 }, { "dec", TRUE, LONG, op1(E), 0 }, { "call", TRUE, LONG, op1(Eind),0 }, { "lcall", TRUE, LONG, op1(Eind),0 }, { "jmp", TRUE, LONG, op1(Eind),0 }, { "ljmp", TRUE, LONG, op1(Eind),0 }, { "push", TRUE, LONG, op1(E), 0 }, { "", TRUE, NONE, 0, 0 } }; static const struct inst db_inst_table[256] = { /*00*/ { "add", TRUE, BYTE, op2(R, E), 0 }, /*01*/ { "add", TRUE, LONG, op2(R, E), 0 }, /*02*/ { "add", TRUE, BYTE, op2(E, R), 0 }, /*03*/ { "add", TRUE, LONG, op2(E, R), 0 }, /*04*/ { "add", FALSE, BYTE, op2(I, A), 0 }, /*05*/ { "add", FALSE, LONG, op2(Is, A), 0 }, /*06*/ { "push", FALSE, NONE, op1(Si), 0 }, /*07*/ { "pop", FALSE, NONE, op1(Si), 0 }, /*08*/ { "or", TRUE, BYTE, op2(R, E), 0 }, /*09*/ { "or", TRUE, LONG, op2(R, E), 0 }, /*0a*/ { "or", TRUE, BYTE, op2(E, R), 0 }, /*0b*/ { "or", TRUE, LONG, op2(E, R), 0 }, /*0c*/ { "or", FALSE, BYTE, op2(I, A), 0 }, /*0d*/ { "or", FALSE, LONG, op2(I, A), 0 }, /*0e*/ { "push", FALSE, NONE, op1(Si), 0 }, /*0f*/ { "", FALSE, NONE, 0, 0 }, /*10*/ { "adc", TRUE, BYTE, op2(R, E), 0 }, /*11*/ { "adc", TRUE, LONG, op2(R, E), 0 }, /*12*/ { "adc", TRUE, BYTE, op2(E, R), 0 }, /*13*/ { "adc", TRUE, LONG, op2(E, R), 0 }, /*14*/ { "adc", FALSE, BYTE, op2(I, A), 0 }, /*15*/ { "adc", FALSE, LONG, op2(Is, A), 0 }, /*16*/ { "push", FALSE, NONE, op1(Si), 0 }, /*17*/ { "pop", FALSE, NONE, op1(Si), 0 }, /*18*/ { "sbb", TRUE, BYTE, op2(R, E), 0 }, /*19*/ { "sbb", TRUE, LONG, op2(R, E), 0 }, /*1a*/ { "sbb", TRUE, BYTE, op2(E, R), 0 }, /*1b*/ { "sbb", TRUE, LONG, op2(E, R), 0 }, /*1c*/ { "sbb", FALSE, BYTE, op2(I, A), 0 }, /*1d*/ { "sbb", FALSE, LONG, op2(Is, A), 0 }, /*1e*/ { "push", FALSE, NONE, op1(Si), 0 }, /*1f*/ { "pop", FALSE, NONE, op1(Si), 0 }, /*20*/ { "and", TRUE, BYTE, op2(R, E), 0 }, /*21*/ { "and", TRUE, LONG, op2(R, E), 0 }, /*22*/ { "and", TRUE, BYTE, op2(E, R), 0 }, /*23*/ { "and", TRUE, LONG, op2(E, R), 0 }, /*24*/ { "and", FALSE, BYTE, op2(I, A), 0 }, /*25*/ { "and", FALSE, LONG, op2(I, A), 0 }, /*26*/ { "", FALSE, NONE, 0, 0 }, /*27*/ { "daa", FALSE, NONE, 0, 0 }, /*28*/ { "sub", TRUE, BYTE, op2(R, E), 0 }, /*29*/ { "sub", TRUE, LONG, op2(R, E), 0 }, /*2a*/ { "sub", TRUE, BYTE, op2(E, R), 0 }, /*2b*/ { "sub", TRUE, LONG, op2(E, R), 0 }, /*2c*/ { "sub", FALSE, BYTE, op2(I, A), 0 }, /*2d*/ { "sub", FALSE, LONG, op2(Is, A), 0 }, /*2e*/ { "", FALSE, NONE, 0, 0 }, /*2f*/ { "das", FALSE, NONE, 0, 0 }, /*30*/ { "xor", TRUE, BYTE, op2(R, E), 0 }, /*31*/ { "xor", TRUE, LONG, op2(R, E), 0 }, /*32*/ { "xor", TRUE, BYTE, op2(E, R), 0 }, /*33*/ { "xor", TRUE, LONG, op2(E, R), 0 }, /*34*/ { "xor", FALSE, BYTE, op2(I, A), 0 }, /*35*/ { "xor", FALSE, LONG, op2(I, A), 0 }, /*36*/ { "", FALSE, NONE, 0, 0 }, /*37*/ { "aaa", FALSE, NONE, 0, 0 }, /*38*/ { "cmp", TRUE, BYTE, op2(R, E), 0 }, /*39*/ { "cmp", TRUE, LONG, op2(R, E), 0 }, /*3a*/ { "cmp", TRUE, BYTE, op2(E, R), 0 }, /*3b*/ { "cmp", TRUE, LONG, op2(E, R), 0 }, /*3c*/ { "cmp", FALSE, BYTE, op2(I, A), 0 }, /*3d*/ { "cmp", FALSE, LONG, op2(Is, A), 0 }, /*3e*/ { "", FALSE, NONE, 0, 0 }, /*3f*/ { "aas", FALSE, NONE, 0, 0 }, /*40*/ { "inc", FALSE, LONG, op1(Ri), 0 }, /*41*/ { "inc", FALSE, LONG, op1(Ri), 0 }, /*42*/ { "inc", FALSE, LONG, op1(Ri), 0 }, /*43*/ { "inc", FALSE, LONG, op1(Ri), 0 }, /*44*/ { "inc", FALSE, LONG, op1(Ri), 0 }, /*45*/ { "inc", FALSE, LONG, op1(Ri), 0 }, /*46*/ { "inc", FALSE, LONG, op1(Ri), 0 }, /*47*/ { "inc", FALSE, LONG, op1(Ri), 0 }, /*48*/ { "dec", FALSE, LONG, op1(Ri), 0 }, /*49*/ { "dec", FALSE, LONG, op1(Ri), 0 }, /*4a*/ { "dec", FALSE, LONG, op1(Ri), 0 }, /*4b*/ { "dec", FALSE, LONG, op1(Ri), 0 }, /*4c*/ { "dec", FALSE, LONG, op1(Ri), 0 }, /*4d*/ { "dec", FALSE, LONG, op1(Ri), 0 }, /*4e*/ { "dec", FALSE, LONG, op1(Ri), 0 }, /*4f*/ { "dec", FALSE, LONG, op1(Ri), 0 }, /*50*/ { "push", FALSE, LONG, op1(Ri), 0 }, /*51*/ { "push", FALSE, LONG, op1(Ri), 0 }, /*52*/ { "push", FALSE, LONG, op1(Ri), 0 }, /*53*/ { "push", FALSE, LONG, op1(Ri), 0 }, /*54*/ { "push", FALSE, LONG, op1(Ri), 0 }, /*55*/ { "push", FALSE, LONG, op1(Ri), 0 }, /*56*/ { "push", FALSE, LONG, op1(Ri), 0 }, /*57*/ { "push", FALSE, LONG, op1(Ri), 0 }, /*58*/ { "pop", FALSE, LONG, op1(Ri), 0 }, /*59*/ { "pop", FALSE, LONG, op1(Ri), 0 }, /*5a*/ { "pop", FALSE, LONG, op1(Ri), 0 }, /*5b*/ { "pop", FALSE, LONG, op1(Ri), 0 }, /*5c*/ { "pop", FALSE, LONG, op1(Ri), 0 }, /*5d*/ { "pop", FALSE, LONG, op1(Ri), 0 }, /*5e*/ { "pop", FALSE, LONG, op1(Ri), 0 }, /*5f*/ { "pop", FALSE, LONG, op1(Ri), 0 }, /*60*/ { "pusha", FALSE, LONG, 0, 0 }, /*61*/ { "popa", FALSE, LONG, 0, 0 }, /*62*/ { "bound", TRUE, LONG, op2(E, R), 0 }, /*63*/ { "arpl", TRUE, NONE, op2(Rw,Ew), 0 }, /*64*/ { "", FALSE, NONE, 0, 0 }, /*65*/ { "", FALSE, NONE, 0, 0 }, /*66*/ { "", FALSE, NONE, 0, 0 }, /*67*/ { "", FALSE, NONE, 0, 0 }, /*68*/ { "push", FALSE, LONG, op1(I), 0 }, /*69*/ { "imul", TRUE, LONG, op3(I,E,R), 0 }, /*6a*/ { "push", FALSE, LONG, op1(Ibs), 0 }, /*6b*/ { "imul", TRUE, LONG, op3(Ibs,E,R),0 }, /*6c*/ { "ins", FALSE, BYTE, op2(DX, DI), 0 }, /*6d*/ { "ins", FALSE, LONG, op2(DX, DI), 0 }, /*6e*/ { "outs", FALSE, BYTE, op2(SI, DX), 0 }, /*6f*/ { "outs", FALSE, LONG, op2(SI, DX), 0 }, /*70*/ { "jo", FALSE, NONE, op1(Db), 0 }, /*71*/ { "jno", FALSE, NONE, op1(Db), 0 }, /*72*/ { "jb", FALSE, NONE, op1(Db), 0 }, /*73*/ { "jnb", FALSE, NONE, op1(Db), 0 }, /*74*/ { "jz", FALSE, NONE, op1(Db), 0 }, /*75*/ { "jnz", FALSE, NONE, op1(Db), 0 }, /*76*/ { "jbe", FALSE, NONE, op1(Db), 0 }, /*77*/ { "jnbe", FALSE, NONE, op1(Db), 0 }, /*78*/ { "js", FALSE, NONE, op1(Db), 0 }, /*79*/ { "jns", FALSE, NONE, op1(Db), 0 }, /*7a*/ { "jp", FALSE, NONE, op1(Db), 0 }, /*7b*/ { "jnp", FALSE, NONE, op1(Db), 0 }, /*7c*/ { "jl", FALSE, NONE, op1(Db), 0 }, /*7d*/ { "jnl", FALSE, NONE, op1(Db), 0 }, /*7e*/ { "jle", FALSE, NONE, op1(Db), 0 }, /*7f*/ { "jnle", FALSE, NONE, op1(Db), 0 }, /*80*/ { "", TRUE, BYTE, op2(I, E), db_Grp1 }, /*81*/ { "", TRUE, LONG, op2(I, E), db_Grp1 }, /*82*/ { "", TRUE, BYTE, op2(I, E), db_Grp1 }, /*83*/ { "", TRUE, LONG, op2(Ibs,E), db_Grp1 }, /*84*/ { "test", TRUE, BYTE, op2(R, E), 0 }, /*85*/ { "test", TRUE, LONG, op2(R, E), 0 }, /*86*/ { "xchg", TRUE, BYTE, op2(R, E), 0 }, /*87*/ { "xchg", TRUE, LONG, op2(R, E), 0 }, /*88*/ { "mov", TRUE, BYTE, op2(R, E), 0 }, /*89*/ { "mov", TRUE, LONG, op2(R, E), 0 }, /*8a*/ { "mov", TRUE, BYTE, op2(E, R), 0 }, /*8b*/ { "mov", TRUE, LONG, op2(E, R), 0 }, /*8c*/ { "mov", TRUE, NONE, op2(S, Ew), 0 }, /*8d*/ { "lea", TRUE, LONG, op2(E, R), 0 }, /*8e*/ { "mov", TRUE, NONE, op2(Ew, S), 0 }, /*8f*/ { "pop", TRUE, LONG, op1(E), 0 }, /*90*/ { "nop", FALSE, NONE, 0, 0 }, /*91*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 }, /*92*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 }, /*93*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 }, /*94*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 }, /*95*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 }, /*96*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 }, /*97*/ { "xchg", FALSE, LONG, op2(A, Ri), 0 }, /*98*/ { "cbw", FALSE, SDEP, 0, "cwde" }, /* cbw/cwde */ /*99*/ { "cwd", FALSE, SDEP, 0, "cdq" }, /* cwd/cdq */ /*9a*/ { "lcall", FALSE, NONE, op1(OS), 0 }, /*9b*/ { "wait", FALSE, NONE, 0, 0 }, /*9c*/ { "pushf", FALSE, LONG, 0, 0 }, /*9d*/ { "popf", FALSE, LONG, 0, 0 }, /*9e*/ { "sahf", FALSE, NONE, 0, 0 }, /*9f*/ { "lahf", FALSE, NONE, 0, 0 }, /*a0*/ { "mov", FALSE, BYTE, op2(O, A), 0 }, /*a1*/ { "mov", FALSE, LONG, op2(O, A), 0 }, /*a2*/ { "mov", FALSE, BYTE, op2(A, O), 0 }, /*a3*/ { "mov", FALSE, LONG, op2(A, O), 0 }, /*a4*/ { "movs", FALSE, BYTE, op2(SI,DI), 0 }, /*a5*/ { "movs", FALSE, LONG, op2(SI,DI), 0 }, /*a6*/ { "cmps", FALSE, BYTE, op2(SI,DI), 0 }, /*a7*/ { "cmps", FALSE, LONG, op2(SI,DI), 0 }, /*a8*/ { "test", FALSE, BYTE, op2(I, A), 0 }, /*a9*/ { "test", FALSE, LONG, op2(I, A), 0 }, /*aa*/ { "stos", FALSE, BYTE, op1(DI), 0 }, /*ab*/ { "stos", FALSE, LONG, op1(DI), 0 }, /*ac*/ { "lods", FALSE, BYTE, op1(SI), 0 }, /*ad*/ { "lods", FALSE, LONG, op1(SI), 0 }, /*ae*/ { "scas", FALSE, BYTE, op1(SI), 0 }, /*af*/ { "scas", FALSE, LONG, op1(SI), 0 }, /*b0*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 }, /*b1*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 }, /*b2*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 }, /*b3*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 }, /*b4*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 }, /*b5*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 }, /*b6*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 }, /*b7*/ { "mov", FALSE, BYTE, op2(I, Ri), 0 }, /*b8*/ { "mov", FALSE, LONG, op2(I, Ri), 0 }, /*b9*/ { "mov", FALSE, LONG, op2(I, Ri), 0 }, /*ba*/ { "mov", FALSE, LONG, op2(I, Ri), 0 }, /*bb*/ { "mov", FALSE, LONG, op2(I, Ri), 0 }, /*bc*/ { "mov", FALSE, LONG, op2(I, Ri), 0 }, /*bd*/ { "mov", FALSE, LONG, op2(I, Ri), 0 }, /*be*/ { "mov", FALSE, LONG, op2(I, Ri), 0 }, /*bf*/ { "mov", FALSE, LONG, op2(I, Ri), 0 }, /*c0*/ { "", TRUE, BYTE, op2(Ib, E), db_Grp2 }, /*c1*/ { "", TRUE, LONG, op2(Ib, E), db_Grp2 }, /*c2*/ { "ret", FALSE, NONE, op1(Iw), 0 }, /*c3*/ { "ret", FALSE, NONE, 0, 0 }, /*c4*/ { "les", TRUE, LONG, op2(E, R), 0 }, /*c5*/ { "lds", TRUE, LONG, op2(E, R), 0 }, /*c6*/ { "mov", TRUE, BYTE, op2(I, E), 0 }, /*c7*/ { "mov", TRUE, LONG, op2(I, E), 0 }, /*c8*/ { "enter", FALSE, NONE, op2(Iw, Ib), 0 }, /*c9*/ { "leave", FALSE, NONE, 0, 0 }, /*ca*/ { "lret", FALSE, NONE, op1(Iw), 0 }, /*cb*/ { "lret", FALSE, NONE, 0, 0 }, /*cc*/ { "int", FALSE, NONE, op1(o3), 0 }, /*cd*/ { "int", FALSE, NONE, op1(Ib), 0 }, /*ce*/ { "into", FALSE, NONE, 0, 0 }, /*cf*/ { "iret", FALSE, NONE, 0, 0 }, /*d0*/ { "", TRUE, BYTE, op2(o1, E), db_Grp2 }, /*d1*/ { "", TRUE, LONG, op2(o1, E), db_Grp2 }, /*d2*/ { "", TRUE, BYTE, op2(CL, E), db_Grp2 }, /*d3*/ { "", TRUE, LONG, op2(CL, E), db_Grp2 }, /*d4*/ { "aam", FALSE, NONE, op1(Iba), 0 }, /*d5*/ { "aad", FALSE, NONE, op1(Iba), 0 }, /*d6*/ { ".byte\t0xd6", FALSE, NONE, 0, 0 }, /*d7*/ { "xlat", FALSE, BYTE, op1(BX), 0 }, /*d8*/ { "", TRUE, NONE, 0, db_Esc8 }, /*d9*/ { "", TRUE, NONE, 0, db_Esc9 }, /*da*/ { "", TRUE, NONE, 0, db_Esca }, /*db*/ { "", TRUE, NONE, 0, db_Escb }, /*dc*/ { "", TRUE, NONE, 0, db_Escc }, /*dd*/ { "", TRUE, NONE, 0, db_Escd }, /*de*/ { "", TRUE, NONE, 0, db_Esce }, /*df*/ { "", TRUE, NONE, 0, db_Escf }, /*e0*/ { "loopne",FALSE, NONE, op1(Db), 0 }, /*e1*/ { "loope", FALSE, NONE, op1(Db), 0 }, /*e2*/ { "loop", FALSE, NONE, op1(Db), 0 }, /*e3*/ { "jcxz", FALSE, SDEP, op1(Db), "jecxz" }, /*e4*/ { "in", FALSE, BYTE, op2(Ib, A), 0 }, /*e5*/ { "in", FALSE, LONG, op2(Ib, A) , 0 }, /*e6*/ { "out", FALSE, BYTE, op2(A, Ib), 0 }, /*e7*/ { "out", FALSE, LONG, op2(A, Ib) , 0 }, /*e8*/ { "call", FALSE, NONE, op1(Dl), 0 }, /*e9*/ { "jmp", FALSE, NONE, op1(Dl), 0 }, /*ea*/ { "ljmp", FALSE, NONE, op1(OS), 0 }, /*eb*/ { "jmp", FALSE, NONE, op1(Db), 0 }, /*ec*/ { "in", FALSE, BYTE, op2(DX, A), 0 }, /*ed*/ { "in", FALSE, LONG, op2(DX, A) , 0 }, /*ee*/ { "out", FALSE, BYTE, op2(A, DX), 0 }, /*ef*/ { "out", FALSE, LONG, op2(A, DX) , 0 }, /*f0*/ { "", FALSE, NONE, 0, 0 }, /*f1*/ { ".byte\t0xf1", FALSE, NONE, 0, 0 }, /*f2*/ { "", FALSE, NONE, 0, 0 }, /*f3*/ { "", FALSE, NONE, 0, 0 }, /*f4*/ { "hlt", FALSE, NONE, 0, 0 }, /*f5*/ { "cmc", FALSE, NONE, 0, 0 }, /*f6*/ { "", TRUE, BYTE, 0, db_Grp3 }, /*f7*/ { "", TRUE, LONG, 0, db_Grp3 }, /*f8*/ { "clc", FALSE, NONE, 0, 0 }, /*f9*/ { "stc", FALSE, NONE, 0, 0 }, /*fa*/ { "cli", FALSE, NONE, 0, 0 }, /*fb*/ { "sti", FALSE, NONE, 0, 0 }, /*fc*/ { "cld", FALSE, NONE, 0, 0 }, /*fd*/ { "std", FALSE, NONE, 0, 0 }, /*fe*/ { "", TRUE, NONE, 0, db_Grp4 }, /*ff*/ { "", TRUE, NONE, 0, db_Grp5 }, }; static const struct inst db_bad_inst = { "???", FALSE, NONE, 0, 0 } ; #define f_mod(byte) ((byte)>>6) #define f_reg(byte) (((byte)>>3)&0x7) #define f_rm(byte) ((byte)&0x7) #define sib_ss(byte) ((byte)>>6) #define sib_index(byte) (((byte)>>3)&0x7) #define sib_base(byte) ((byte)&0x7) struct i_addr { int is_reg; /* if reg, reg number is in 'disp' */ int disp; const char * base; const char * index; int ss; }; static const char * const db_index_reg_16[8] = { "%bx,%si", "%bx,%di", "%bp,%si", "%bp,%di", "%si", "%di", "%bp", "%bx" }; static const char * const db_reg[3][8] = { { "%al", "%cl", "%dl", "%bl", "%ah", "%ch", "%dh", "%bh" }, { "%ax", "%cx", "%dx", "%bx", "%sp", "%bp", "%si", "%di" }, { "%eax", "%ecx", "%edx", "%ebx", "%esp", "%ebp", "%esi", "%edi" } }; static const char * const db_seg_reg[8] = { "%es", "%cs", "%ss", "%ds", "%fs", "%gs", "", "" }; /* * lengths for size attributes */ static const int db_lengths[] = { 1, /* BYTE */ 2, /* WORD */ 4, /* LONG */ 8, /* QUAD */ 4, /* SNGL */ 8, /* DBLR */ 10, /* EXTR */ }; #define get_value_inc(result, loc, size, is_signed) \ result = db_get_value((loc), (size), (is_signed)); \ (loc) += (size); static db_addr_t db_disasm_esc(db_addr_t loc, int inst, int short_addr, int size, const char *seg); static void db_print_address(const char *seg, int size, struct i_addr *addrp); static db_addr_t db_read_address(db_addr_t loc, int short_addr, int regmodrm, struct i_addr *addrp); /* * Read address at location and return updated location. */ static db_addr_t db_read_address(loc, short_addr, regmodrm, addrp) db_addr_t loc; int short_addr; int regmodrm; struct i_addr * addrp; /* out */ { int mod, rm, sib, index, disp; mod = f_mod(regmodrm); rm = f_rm(regmodrm); if (mod == 3) { addrp->is_reg = TRUE; addrp->disp = rm; return (loc); } addrp->is_reg = FALSE; addrp->index = 0; if (short_addr) { addrp->index = 0; addrp->ss = 0; switch (mod) { case 0: if (rm == 6) { get_value_inc(disp, loc, 2, FALSE); addrp->disp = disp; addrp->base = 0; } else { addrp->disp = 0; addrp->base = db_index_reg_16[rm]; } break; case 1: get_value_inc(disp, loc, 1, TRUE); disp &= 0xFFFF; addrp->disp = disp; addrp->base = db_index_reg_16[rm]; break; case 2: get_value_inc(disp, loc, 2, FALSE); addrp->disp = disp; addrp->base = db_index_reg_16[rm]; break; } } else { if (mod != 3 && rm == 4) { get_value_inc(sib, loc, 1, FALSE); rm = sib_base(sib); index = sib_index(sib); if (index != 4) addrp->index = db_reg[LONG][index]; addrp->ss = sib_ss(sib); } switch (mod) { case 0: if (rm == 5) { get_value_inc(addrp->disp, loc, 4, FALSE); addrp->base = 0; } else { addrp->disp = 0; addrp->base = db_reg[LONG][rm]; } break; case 1: get_value_inc(disp, loc, 1, TRUE); addrp->disp = disp; addrp->base = db_reg[LONG][rm]; break; case 2: get_value_inc(disp, loc, 4, FALSE); addrp->disp = disp; addrp->base = db_reg[LONG][rm]; break; } } return (loc); } static void db_print_address(seg, size, addrp) const char * seg; int size; struct i_addr * addrp; { if (addrp->is_reg) { db_printf("%s", db_reg[size][addrp->disp]); return; } if (seg) { db_printf("%s:", seg); } db_printsym((db_addr_t)addrp->disp, DB_STGY_ANY); if (addrp->base != 0 || addrp->index != 0) { db_printf("("); if (addrp->base) db_printf("%s", addrp->base); if (addrp->index) db_printf(",%s,%d", addrp->index, 1<ss); db_printf(")"); } } /* * Disassemble floating-point ("escape") instruction * and return updated location. */ static db_addr_t db_disasm_esc(loc, inst, short_addr, size, seg) db_addr_t loc; int inst; int short_addr; int size; const char * seg; { int regmodrm; const struct finst * fp; int mod; struct i_addr address; const char * name; get_value_inc(regmodrm, loc, 1, FALSE); fp = &db_Esc_inst[inst - 0xd8][f_reg(regmodrm)]; mod = f_mod(regmodrm); if (mod != 3) { if (*fp->f_name == '\0') { db_printf(""); return (loc); } /* * Normal address modes. */ loc = db_read_address(loc, short_addr, regmodrm, &address); db_printf("%s", fp->f_name); switch(fp->f_size) { case SNGL: db_printf("s"); break; case DBLR: db_printf("l"); break; case EXTR: db_printf("t"); break; case WORD: db_printf("s"); break; case LONG: db_printf("l"); break; case QUAD: db_printf("q"); break; default: break; } db_printf("\t"); db_print_address(seg, BYTE, &address); } else { /* * 'reg-reg' - special formats */ switch (fp->f_rrmode) { case op2(ST,STI): name = (fp->f_rrname) ? fp->f_rrname : fp->f_name; db_printf("%s\t%%st,%%st(%d)",name,f_rm(regmodrm)); break; case op2(STI,ST): name = (fp->f_rrname) ? fp->f_rrname : fp->f_name; db_printf("%s\t%%st(%d),%%st",name, f_rm(regmodrm)); break; case op1(STI): name = (fp->f_rrname) ? fp->f_rrname : fp->f_name; db_printf("%s\t%%st(%d)",name, f_rm(regmodrm)); break; case op1(X): name = ((const char * const *)fp->f_rrname)[f_rm(regmodrm)]; if (*name == '\0') goto bad; db_printf("%s", name); break; case op1(XA): name = ((const char * const *)fp->f_rrname)[f_rm(regmodrm)]; if (*name == '\0') goto bad; db_printf("%s\t%%ax", name); break; default: bad: db_printf(""); break; } } return (loc); } /* * Disassemble instruction at 'loc'. 'altfmt' specifies an * (optional) alternate format. Return address of start of * next instruction. */ db_addr_t db_disasm(loc, altfmt) db_addr_t loc; boolean_t altfmt; { int inst; int size; int short_addr; const char * seg; const struct inst * ip; const char * i_name; int i_size; int i_mode; int regmodrm = 0; boolean_t first; int displ; int prefix; int rep; int imm; int imm2; int len; struct i_addr address; get_value_inc(inst, loc, 1, FALSE); short_addr = FALSE; size = LONG; seg = 0; /* * Get prefixes */ rep = FALSE; prefix = TRUE; do { switch (inst) { case 0x66: /* data16 */ size = WORD; break; case 0x67: short_addr = TRUE; break; case 0x26: seg = "%es"; break; case 0x36: seg = "%ss"; break; case 0x2e: seg = "%cs"; break; case 0x3e: seg = "%ds"; break; case 0x64: seg = "%fs"; break; case 0x65: seg = "%gs"; break; case 0xf0: db_printf("lock "); break; case 0xf2: db_printf("repne "); break; case 0xf3: rep = TRUE; break; default: prefix = FALSE; break; } if (prefix) { get_value_inc(inst, loc, 1, FALSE); } if (rep == TRUE) { if (inst == 0x90) { db_printf("pause\n"); return (loc); } db_printf("repe "); /* XXX repe VS rep */ rep = FALSE; } } while (prefix); if (inst >= 0xd8 && inst <= 0xdf) { loc = db_disasm_esc(loc, inst, short_addr, size, seg); db_printf("\n"); return (loc); } if (inst == 0x0f) { get_value_inc(inst, loc, 1, FALSE); ip = db_inst_0f[inst>>4]; if (ip == 0) { ip = &db_bad_inst; } else { ip = &ip[inst&0xf]; } } else ip = &db_inst_table[inst]; if (ip->i_has_modrm) { get_value_inc(regmodrm, loc, 1, FALSE); loc = db_read_address(loc, short_addr, regmodrm, &address); } i_name = ip->i_name; i_size = ip->i_size; i_mode = ip->i_mode; if (ip->i_extra == db_Grp1 || ip->i_extra == db_Grp2 || ip->i_extra == db_Grp6 || ip->i_extra == db_Grp7 || ip->i_extra == db_Grp8 || ip->i_extra == db_Grp9 || ip->i_extra == db_Grp15) { i_name = ((const char * const *)ip->i_extra)[f_reg(regmodrm)]; } else if (ip->i_extra == db_Grp3) { ip = ip->i_extra; ip = &ip[f_reg(regmodrm)]; i_name = ip->i_name; i_mode = ip->i_mode; } else if (ip->i_extra == db_Grp4 || ip->i_extra == db_Grp5) { ip = ip->i_extra; ip = &ip[f_reg(regmodrm)]; i_name = ip->i_name; i_mode = ip->i_mode; i_size = ip->i_size; } /* Special cases that don't fit well in the tables. */ if (ip->i_extra == db_Grp7 && f_mod(regmodrm) == 3) { switch (regmodrm) { case 0xc8: i_name = "monitor"; i_size = NONE; i_mode = 0; break; case 0xc9: i_name = "mwait"; i_size = NONE; i_mode = 0; break; } } if (ip->i_extra == db_Grp15 && f_mod(regmodrm) == 3) { i_name = db_Grp15b[f_reg(regmodrm)]; i_size = NONE; i_mode = 0; } if (i_size == SDEP) { if (size == WORD) db_printf("%s", i_name); else db_printf("%s", (const char *)ip->i_extra); } else { db_printf("%s", i_name); if (i_size != NONE) { if (i_size == BYTE) { db_printf("b"); size = BYTE; } else if (i_size == WORD) { db_printf("w"); size = WORD; } else if (size == WORD) db_printf("w"); else db_printf("l"); } } db_printf("\t"); for (first = TRUE; i_mode != 0; i_mode >>= 8, first = FALSE) { if (!first) db_printf(","); switch (i_mode & 0xFF) { case E: db_print_address(seg, size, &address); break; case Eind: db_printf("*"); db_print_address(seg, size, &address); break; case El: db_print_address(seg, LONG, &address); break; case Ew: db_print_address(seg, WORD, &address); break; case Eb: db_print_address(seg, BYTE, &address); break; case R: db_printf("%s", db_reg[size][f_reg(regmodrm)]); break; case Rw: db_printf("%s", db_reg[WORD][f_reg(regmodrm)]); break; case Ri: db_printf("%s", db_reg[size][f_rm(inst)]); break; case Ril: db_printf("%s", db_reg[LONG][f_rm(inst)]); break; case S: db_printf("%s", db_seg_reg[f_reg(regmodrm)]); break; case Si: db_printf("%s", db_seg_reg[f_reg(inst)]); break; case A: db_printf("%s", db_reg[size][0]); /* acc */ break; case BX: if (seg) db_printf("%s:", seg); db_printf("(%s)", short_addr ? "%bx" : "%ebx"); break; case CL: db_printf("%%cl"); break; case DX: db_printf("%%dx"); break; case SI: if (seg) db_printf("%s:", seg); db_printf("(%s)", short_addr ? "%si" : "%esi"); break; case DI: db_printf("%%es:(%s)", short_addr ? "%di" : "%edi"); break; case CR: db_printf("%%cr%d", f_reg(regmodrm)); break; case DR: db_printf("%%dr%d", f_reg(regmodrm)); break; case TR: db_printf("%%tr%d", f_reg(regmodrm)); break; case I: len = db_lengths[size]; get_value_inc(imm, loc, len, FALSE); db_printf("$%#r", imm); break; case Is: len = db_lengths[size]; get_value_inc(imm, loc, len, FALSE); db_printf("$%+#r", imm); break; case Ib: get_value_inc(imm, loc, 1, FALSE); db_printf("$%#r", imm); break; case Iba: get_value_inc(imm, loc, 1, FALSE); if (imm != 0x0a) db_printf("$%#r", imm); break; case Ibs: get_value_inc(imm, loc, 1, TRUE); if (size == WORD) imm &= 0xFFFF; db_printf("$%+#r", imm); break; case Iw: get_value_inc(imm, loc, 2, FALSE); db_printf("$%#r", imm); break; case O: len = (short_addr ? 2 : 4); get_value_inc(displ, loc, len, FALSE); if (seg) db_printf("%s:%+#r",seg, displ); else db_printsym((db_addr_t)displ, DB_STGY_ANY); break; case Db: get_value_inc(displ, loc, 1, TRUE); displ += loc; if (size == WORD) displ &= 0xFFFF; db_printsym((db_addr_t)displ, DB_STGY_XTRN); break; case Dl: len = db_lengths[size]; get_value_inc(displ, loc, len, FALSE); displ += loc; if (size == WORD) displ &= 0xFFFF; db_printsym((db_addr_t)displ, DB_STGY_XTRN); break; case o1: db_printf("$1"); break; case o3: db_printf("$3"); break; case OS: len = db_lengths[size]; get_value_inc(imm, loc, len, FALSE); /* offset */ get_value_inc(imm2, loc, 2, FALSE); /* segment */ db_printf("$%#r,%#r", imm2, imm); break; } } db_printf("\n"); return (loc); } Index: projects/building-blocks/sys/kern/imgact_elf.c =================================================================== --- projects/building-blocks/sys/kern/imgact_elf.c (revision 278776) +++ projects/building-blocks/sys/kern/imgact_elf.c (revision 278777) @@ -1,2194 +1,2195 @@ /*- * Copyright (c) 2000 David O'Brien * Copyright (c) 1995-1996 Søren Schmidt * Copyright (c) 1996 Peter Wemm * 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 * in this position and unchanged. * 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. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 __FBSDID("$FreeBSD$"); #include "opt_capsicum.h" #include "opt_compat.h" #include "opt_core.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ELF_NOTE_ROUNDSIZE 4 #define OLD_EI_BRAND 8 static int __elfN(check_header)(const Elf_Ehdr *hdr); static Elf_Brandinfo *__elfN(get_brandinfo)(struct image_params *imgp, const char *interp, int interp_name_len, int32_t *osrel); static int __elfN(load_file)(struct proc *p, const char *file, u_long *addr, u_long *entry, size_t pagesize); static int __elfN(load_section)(struct image_params *imgp, vm_offset_t offset, caddr_t vmaddr, size_t memsz, size_t filsz, vm_prot_t prot, size_t pagesize); static int __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp); static boolean_t __elfN(freebsd_trans_osrel)(const Elf_Note *note, int32_t *osrel); static boolean_t kfreebsd_trans_osrel(const Elf_Note *note, int32_t *osrel); static boolean_t __elfN(check_note)(struct image_params *imgp, Elf_Brandnote *checknote, int32_t *osrel); static vm_prot_t __elfN(trans_prot)(Elf_Word); static Elf_Word __elfN(untrans_prot)(vm_prot_t); SYSCTL_NODE(_kern, OID_AUTO, __CONCAT(elf, __ELF_WORD_SIZE), CTLFLAG_RW, 0, ""); #ifdef COMPRESS_USER_CORES static int compress_core(gzFile, char *, char *, unsigned int, struct thread * td); #endif #define CORE_BUF_SIZE (16 * 1024) int __elfN(fallback_brand) = -1; SYSCTL_INT(__CONCAT(_kern_elf, __ELF_WORD_SIZE), OID_AUTO, fallback_brand, CTLFLAG_RWTUN, &__elfN(fallback_brand), 0, __XSTRING(__CONCAT(ELF, __ELF_WORD_SIZE)) " brand of last resort"); static int elf_legacy_coredump = 0; SYSCTL_INT(_debug, OID_AUTO, __elfN(legacy_coredump), CTLFLAG_RW, &elf_legacy_coredump, 0, ""); int __elfN(nxstack) = #if defined(__amd64__) || defined(__powerpc64__) /* both 64 and 32 bit */ 1; #else 0; #endif SYSCTL_INT(__CONCAT(_kern_elf, __ELF_WORD_SIZE), OID_AUTO, nxstack, CTLFLAG_RW, &__elfN(nxstack), 0, __XSTRING(__CONCAT(ELF, __ELF_WORD_SIZE)) ": enable non-executable stack"); #if __ELF_WORD_SIZE == 32 #if defined(__amd64__) int i386_read_exec = 0; SYSCTL_INT(_kern_elf32, OID_AUTO, read_exec, CTLFLAG_RW, &i386_read_exec, 0, "enable execution from readable segments"); #endif #endif static Elf_Brandinfo *elf_brand_list[MAX_BRANDS]; #define trunc_page_ps(va, ps) ((va) & ~(ps - 1)) #define round_page_ps(va, ps) (((va) + (ps - 1)) & ~(ps - 1)) #define aligned(a, t) (trunc_page_ps((u_long)(a), sizeof(t)) == (u_long)(a)) static const char FREEBSD_ABI_VENDOR[] = "FreeBSD"; Elf_Brandnote __elfN(freebsd_brandnote) = { .hdr.n_namesz = sizeof(FREEBSD_ABI_VENDOR), .hdr.n_descsz = sizeof(int32_t), .hdr.n_type = 1, .vendor = FREEBSD_ABI_VENDOR, .flags = BN_TRANSLATE_OSREL, .trans_osrel = __elfN(freebsd_trans_osrel) }; static boolean_t __elfN(freebsd_trans_osrel)(const Elf_Note *note, int32_t *osrel) { uintptr_t p; p = (uintptr_t)(note + 1); p += roundup2(note->n_namesz, ELF_NOTE_ROUNDSIZE); *osrel = *(const int32_t *)(p); return (TRUE); } static const char GNU_ABI_VENDOR[] = "GNU"; static int GNU_KFREEBSD_ABI_DESC = 3; Elf_Brandnote __elfN(kfreebsd_brandnote) = { .hdr.n_namesz = sizeof(GNU_ABI_VENDOR), .hdr.n_descsz = 16, /* XXX at least 16 */ .hdr.n_type = 1, .vendor = GNU_ABI_VENDOR, .flags = BN_TRANSLATE_OSREL, .trans_osrel = kfreebsd_trans_osrel }; static boolean_t kfreebsd_trans_osrel(const Elf_Note *note, int32_t *osrel) { const Elf32_Word *desc; uintptr_t p; p = (uintptr_t)(note + 1); p += roundup2(note->n_namesz, ELF_NOTE_ROUNDSIZE); desc = (const Elf32_Word *)p; if (desc[0] != GNU_KFREEBSD_ABI_DESC) return (FALSE); /* * Debian GNU/kFreeBSD embed the earliest compatible kernel version * (__FreeBSD_version: Rxx) in the LSB way. */ *osrel = desc[1] * 100000 + desc[2] * 1000 + desc[3]; return (TRUE); } int __elfN(insert_brand_entry)(Elf_Brandinfo *entry) { int i; for (i = 0; i < MAX_BRANDS; i++) { if (elf_brand_list[i] == NULL) { elf_brand_list[i] = entry; break; } } if (i == MAX_BRANDS) { printf("WARNING: %s: could not insert brandinfo entry: %p\n", __func__, entry); return (-1); } return (0); } int __elfN(remove_brand_entry)(Elf_Brandinfo *entry) { int i; for (i = 0; i < MAX_BRANDS; i++) { if (elf_brand_list[i] == entry) { elf_brand_list[i] = NULL; break; } } if (i == MAX_BRANDS) return (-1); return (0); } int __elfN(brand_inuse)(Elf_Brandinfo *entry) { struct proc *p; int rval = FALSE; sx_slock(&allproc_lock); FOREACH_PROC_IN_SYSTEM(p) { if (p->p_sysent == entry->sysvec) { rval = TRUE; break; } } sx_sunlock(&allproc_lock); return (rval); } static Elf_Brandinfo * __elfN(get_brandinfo)(struct image_params *imgp, const char *interp, int interp_name_len, int32_t *osrel) { const Elf_Ehdr *hdr = (const Elf_Ehdr *)imgp->image_header; Elf_Brandinfo *bi; boolean_t ret; int i; /* * We support four types of branding -- (1) the ELF EI_OSABI field * that SCO added to the ELF spec, (2) FreeBSD 3.x's traditional string * branding w/in the ELF header, (3) path of the `interp_path' * field, and (4) the ".note.ABI-tag" ELF section. */ /* Look for an ".note.ABI-tag" ELF section */ for (i = 0; i < MAX_BRANDS; i++) { bi = elf_brand_list[i]; if (bi == NULL) continue; if (hdr->e_machine == bi->machine && (bi->flags & (BI_BRAND_NOTE|BI_BRAND_NOTE_MANDATORY)) != 0) { ret = __elfN(check_note)(imgp, bi->brand_note, osrel); if (ret) return (bi); } } /* If the executable has a brand, search for it in the brand list. */ for (i = 0; i < MAX_BRANDS; i++) { bi = elf_brand_list[i]; if (bi == NULL || bi->flags & BI_BRAND_NOTE_MANDATORY) continue; if (hdr->e_machine == bi->machine && (hdr->e_ident[EI_OSABI] == bi->brand || strncmp((const char *)&hdr->e_ident[OLD_EI_BRAND], bi->compat_3_brand, strlen(bi->compat_3_brand)) == 0)) return (bi); } /* No known brand, see if the header is recognized by any brand */ for (i = 0; i < MAX_BRANDS; i++) { bi = elf_brand_list[i]; if (bi == NULL || bi->flags & BI_BRAND_NOTE_MANDATORY || bi->header_supported == NULL) continue; if (hdr->e_machine == bi->machine) { ret = bi->header_supported(imgp); if (ret) return (bi); } } /* Lacking a known brand, search for a recognized interpreter. */ if (interp != NULL) { for (i = 0; i < MAX_BRANDS; i++) { bi = elf_brand_list[i]; if (bi == NULL || bi->flags & BI_BRAND_NOTE_MANDATORY) continue; if (hdr->e_machine == bi->machine && /* ELF image p_filesz includes terminating zero */ strlen(bi->interp_path) + 1 == interp_name_len && strncmp(interp, bi->interp_path, interp_name_len) == 0) return (bi); } } /* Lacking a recognized interpreter, try the default brand */ for (i = 0; i < MAX_BRANDS; i++) { bi = elf_brand_list[i]; if (bi == NULL || bi->flags & BI_BRAND_NOTE_MANDATORY) continue; if (hdr->e_machine == bi->machine && __elfN(fallback_brand) == bi->brand) return (bi); } return (NULL); } static int __elfN(check_header)(const Elf_Ehdr *hdr) { Elf_Brandinfo *bi; int i; if (!IS_ELF(*hdr) || hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || hdr->e_ident[EI_DATA] != ELF_TARG_DATA || hdr->e_ident[EI_VERSION] != EV_CURRENT || hdr->e_phentsize != sizeof(Elf_Phdr) || hdr->e_version != ELF_TARG_VER) return (ENOEXEC); /* * Make sure we have at least one brand for this machine. */ for (i = 0; i < MAX_BRANDS; i++) { bi = elf_brand_list[i]; if (bi != NULL && bi->machine == hdr->e_machine) break; } if (i == MAX_BRANDS) return (ENOEXEC); return (0); } static int __elfN(map_partial)(vm_map_t map, vm_object_t object, vm_ooffset_t offset, vm_offset_t start, vm_offset_t end, vm_prot_t prot) { struct sf_buf *sf; int error; vm_offset_t off; /* * Create the page if it doesn't exist yet. Ignore errors. */ vm_map_lock(map); vm_map_insert(map, NULL, 0, trunc_page(start), round_page(end), VM_PROT_ALL, VM_PROT_ALL, 0); vm_map_unlock(map); /* * Find the page from the underlying object. */ if (object) { sf = vm_imgact_map_page(object, offset); if (sf == NULL) return (KERN_FAILURE); off = offset - trunc_page(offset); error = copyout((caddr_t)sf_buf_kva(sf) + off, (caddr_t)start, end - start); vm_imgact_unmap_page(sf); if (error) { return (KERN_FAILURE); } } return (KERN_SUCCESS); } static int __elfN(map_insert)(vm_map_t map, vm_object_t object, vm_ooffset_t offset, vm_offset_t start, vm_offset_t end, vm_prot_t prot, int cow) { struct sf_buf *sf; vm_offset_t off; vm_size_t sz; int error, rv; if (start != trunc_page(start)) { rv = __elfN(map_partial)(map, object, offset, start, round_page(start), prot); if (rv) return (rv); offset += round_page(start) - start; start = round_page(start); } if (end != round_page(end)) { rv = __elfN(map_partial)(map, object, offset + trunc_page(end) - start, trunc_page(end), end, prot); if (rv) return (rv); end = trunc_page(end); } if (end > start) { if (offset & PAGE_MASK) { /* * The mapping is not page aligned. This means we have * to copy the data. Sigh. */ rv = vm_map_find(map, NULL, 0, &start, end - start, 0, VMFS_NO_SPACE, prot | VM_PROT_WRITE, VM_PROT_ALL, 0); if (rv) return (rv); if (object == NULL) return (KERN_SUCCESS); for (; start < end; start += sz) { sf = vm_imgact_map_page(object, offset); if (sf == NULL) return (KERN_FAILURE); off = offset - trunc_page(offset); sz = end - start; if (sz > PAGE_SIZE - off) sz = PAGE_SIZE - off; error = copyout((caddr_t)sf_buf_kva(sf) + off, (caddr_t)start, sz); vm_imgact_unmap_page(sf); if (error) { return (KERN_FAILURE); } offset += sz; } rv = KERN_SUCCESS; } else { vm_object_reference(object); vm_map_lock(map); rv = vm_map_insert(map, object, offset, start, end, prot, VM_PROT_ALL, cow); vm_map_unlock(map); if (rv != KERN_SUCCESS) vm_object_deallocate(object); } return (rv); } else { return (KERN_SUCCESS); } } static int __elfN(load_section)(struct image_params *imgp, vm_offset_t offset, caddr_t vmaddr, size_t memsz, size_t filsz, vm_prot_t prot, size_t pagesize) { struct sf_buf *sf; size_t map_len; vm_map_t map; vm_object_t object; vm_offset_t map_addr; int error, rv, cow; size_t copy_len; vm_offset_t file_addr; /* * It's necessary to fail if the filsz + offset taken from the * header is greater than the actual file pager object's size. * If we were to allow this, then the vm_map_find() below would * walk right off the end of the file object and into the ether. * * While I'm here, might as well check for something else that * is invalid: filsz cannot be greater than memsz. */ if ((off_t)filsz + offset > imgp->attr->va_size || filsz > memsz) { uprintf("elf_load_section: truncated ELF file\n"); return (ENOEXEC); } object = imgp->object; map = &imgp->proc->p_vmspace->vm_map; map_addr = trunc_page_ps((vm_offset_t)vmaddr, pagesize); file_addr = trunc_page_ps(offset, pagesize); /* * We have two choices. We can either clear the data in the last page * of an oversized mapping, or we can start the anon mapping a page * early and copy the initialized data into that first page. We * choose the second.. */ if (memsz > filsz) map_len = trunc_page_ps(offset + filsz, pagesize) - file_addr; else map_len = round_page_ps(offset + filsz, pagesize) - file_addr; if (map_len != 0) { /* cow flags: don't dump readonly sections in core */ cow = MAP_COPY_ON_WRITE | MAP_PREFAULT | (prot & VM_PROT_WRITE ? 0 : MAP_DISABLE_COREDUMP); rv = __elfN(map_insert)(map, object, file_addr, /* file offset */ map_addr, /* virtual start */ map_addr + map_len,/* virtual end */ prot, cow); if (rv != KERN_SUCCESS) return (EINVAL); /* we can stop now if we've covered it all */ if (memsz == filsz) { return (0); } } /* * We have to get the remaining bit of the file into the first part * of the oversized map segment. This is normally because the .data * segment in the file is extended to provide bss. It's a neat idea * to try and save a page, but it's a pain in the behind to implement. */ copy_len = (offset + filsz) - trunc_page_ps(offset + filsz, pagesize); map_addr = trunc_page_ps((vm_offset_t)vmaddr + filsz, pagesize); map_len = round_page_ps((vm_offset_t)vmaddr + memsz, pagesize) - map_addr; /* This had damn well better be true! */ if (map_len != 0) { rv = __elfN(map_insert)(map, NULL, 0, map_addr, map_addr + map_len, VM_PROT_ALL, 0); if (rv != KERN_SUCCESS) { return (EINVAL); } } if (copy_len != 0) { vm_offset_t off; sf = vm_imgact_map_page(object, offset + filsz); if (sf == NULL) return (EIO); /* send the page fragment to user space */ off = trunc_page_ps(offset + filsz, pagesize) - trunc_page(offset + filsz); error = copyout((caddr_t)sf_buf_kva(sf) + off, (caddr_t)map_addr, copy_len); vm_imgact_unmap_page(sf); if (error) { return (error); } } /* * set it to the specified protection. * XXX had better undo the damage from pasting over the cracks here! */ vm_map_protect(map, trunc_page(map_addr), round_page(map_addr + map_len), prot, FALSE); return (0); } /* * Load the file "file" into memory. It may be either a shared object * or an executable. * * The "addr" reference parameter is in/out. On entry, it specifies * the address where a shared object should be loaded. If the file is * an executable, this value is ignored. On exit, "addr" specifies * where the file was actually loaded. * * The "entry" reference parameter is out only. On exit, it specifies * the entry point for the loaded file. */ static int __elfN(load_file)(struct proc *p, const char *file, u_long *addr, u_long *entry, size_t pagesize) { struct { struct nameidata nd; struct vattr attr; struct image_params image_params; } *tempdata; const Elf_Ehdr *hdr = NULL; const Elf_Phdr *phdr = NULL; struct nameidata *nd; struct vattr *attr; struct image_params *imgp; vm_prot_t prot; u_long rbase; u_long base_addr = 0; int error, i, numsegs; #ifdef CAPABILITY_MODE /* * XXXJA: This check can go away once we are sufficiently confident * that the checks in namei() are correct. */ if (IN_CAPABILITY_MODE(curthread)) return (ECAPMODE); #endif tempdata = malloc(sizeof(*tempdata), M_TEMP, M_WAITOK); nd = &tempdata->nd; attr = &tempdata->attr; imgp = &tempdata->image_params; /* * Initialize part of the common data */ imgp->proc = p; imgp->attr = attr; imgp->firstpage = NULL; imgp->image_header = NULL; imgp->object = NULL; imgp->execlabel = NULL; NDINIT(nd, LOOKUP, LOCKLEAF | FOLLOW, UIO_SYSSPACE, file, curthread); if ((error = namei(nd)) != 0) { nd->ni_vp = NULL; goto fail; } NDFREE(nd, NDF_ONLY_PNBUF); imgp->vp = nd->ni_vp; /* * Check permissions, modes, uid, etc on the file, and "open" it. */ error = exec_check_permissions(imgp); if (error) goto fail; error = exec_map_first_page(imgp); if (error) goto fail; /* * Also make certain that the interpreter stays the same, so set * its VV_TEXT flag, too. */ VOP_SET_TEXT(nd->ni_vp); imgp->object = nd->ni_vp->v_object; hdr = (const Elf_Ehdr *)imgp->image_header; if ((error = __elfN(check_header)(hdr)) != 0) goto fail; if (hdr->e_type == ET_DYN) rbase = *addr; else if (hdr->e_type == ET_EXEC) rbase = 0; else { error = ENOEXEC; goto fail; } /* Only support headers that fit within first page for now */ if ((hdr->e_phoff > PAGE_SIZE) || (u_int)hdr->e_phentsize * hdr->e_phnum > PAGE_SIZE - hdr->e_phoff) { error = ENOEXEC; goto fail; } phdr = (const Elf_Phdr *)(imgp->image_header + hdr->e_phoff); if (!aligned(phdr, Elf_Addr)) { error = ENOEXEC; goto fail; } for (i = 0, numsegs = 0; i < hdr->e_phnum; i++) { if (phdr[i].p_type == PT_LOAD && phdr[i].p_memsz != 0) { /* Loadable segment */ prot = __elfN(trans_prot)(phdr[i].p_flags); error = __elfN(load_section)(imgp, phdr[i].p_offset, (caddr_t)(uintptr_t)phdr[i].p_vaddr + rbase, phdr[i].p_memsz, phdr[i].p_filesz, prot, pagesize); if (error != 0) goto fail; /* * Establish the base address if this is the * first segment. */ if (numsegs == 0) base_addr = trunc_page(phdr[i].p_vaddr + rbase); numsegs++; } } *addr = base_addr; *entry = (unsigned long)hdr->e_entry + rbase; fail: if (imgp->firstpage) exec_unmap_first_page(imgp); if (nd->ni_vp) vput(nd->ni_vp); free(tempdata, M_TEMP); return (error); } static int __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) { const Elf_Ehdr *hdr = (const Elf_Ehdr *)imgp->image_header; const Elf_Phdr *phdr; Elf_Auxargs *elf_auxargs; struct vmspace *vmspace; vm_prot_t prot; u_long text_size = 0, data_size = 0, total_size = 0; u_long text_addr = 0, data_addr = 0; u_long seg_size, seg_addr; u_long addr, baddr, et_dyn_addr, entry = 0, proghdr = 0; int32_t osrel = 0; int error = 0, i, n, interp_name_len = 0; const char *interp = NULL, *newinterp = NULL; Elf_Brandinfo *brand_info; char *path; struct sysentvec *sv; /* * Do we have a valid ELF header ? * * Only allow ET_EXEC & ET_DYN here, reject ET_DYN later * if particular brand doesn't support it. */ if (__elfN(check_header)(hdr) != 0 || (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN)) return (-1); /* * From here on down, we return an errno, not -1, as we've * detected an ELF file. */ if ((hdr->e_phoff > PAGE_SIZE) || (u_int)hdr->e_phentsize * hdr->e_phnum > PAGE_SIZE - hdr->e_phoff) { /* Only support headers in first page for now */ return (ENOEXEC); } phdr = (const Elf_Phdr *)(imgp->image_header + hdr->e_phoff); if (!aligned(phdr, Elf_Addr)) return (ENOEXEC); n = 0; baddr = 0; for (i = 0; i < hdr->e_phnum; i++) { switch (phdr[i].p_type) { case PT_LOAD: if (n == 0) baddr = phdr[i].p_vaddr; n++; break; case PT_INTERP: /* Path to interpreter */ if (phdr[i].p_filesz > MAXPATHLEN || phdr[i].p_offset > PAGE_SIZE || phdr[i].p_filesz > PAGE_SIZE - phdr[i].p_offset) return (ENOEXEC); interp = imgp->image_header + phdr[i].p_offset; interp_name_len = phdr[i].p_filesz; break; case PT_GNU_STACK: if (__elfN(nxstack)) imgp->stack_prot = __elfN(trans_prot)(phdr[i].p_flags); break; } } brand_info = __elfN(get_brandinfo)(imgp, interp, interp_name_len, &osrel); if (brand_info == NULL) { uprintf("ELF binary type \"%u\" not known.\n", hdr->e_ident[EI_OSABI]); return (ENOEXEC); } if (hdr->e_type == ET_DYN) { if ((brand_info->flags & BI_CAN_EXEC_DYN) == 0) return (ENOEXEC); /* * Honour the base load address from the dso if it is * non-zero for some reason. */ if (baddr == 0) et_dyn_addr = ET_DYN_LOAD_ADDR; else et_dyn_addr = 0; } else et_dyn_addr = 0; sv = brand_info->sysvec; if (interp != NULL && brand_info->interp_newpath != NULL) newinterp = brand_info->interp_newpath; /* * Avoid a possible deadlock if the current address space is destroyed * and that address space maps the locked vnode. In the common case, * the locked vnode's v_usecount is decremented but remains greater * than zero. Consequently, the vnode lock is not needed by vrele(). * However, in cases where the vnode lock is external, such as nullfs, * v_usecount may become zero. * * The VV_TEXT flag prevents modifications to the executable while * the vnode is unlocked. */ VOP_UNLOCK(imgp->vp, 0); error = exec_new_vmspace(imgp, sv); imgp->proc->p_sysent = sv; vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY); if (error) return (error); for (i = 0; i < hdr->e_phnum; i++) { switch (phdr[i].p_type) { case PT_LOAD: /* Loadable segment */ if (phdr[i].p_memsz == 0) break; prot = __elfN(trans_prot)(phdr[i].p_flags); error = __elfN(load_section)(imgp, phdr[i].p_offset, (caddr_t)(uintptr_t)phdr[i].p_vaddr + et_dyn_addr, phdr[i].p_memsz, phdr[i].p_filesz, prot, sv->sv_pagesize); if (error != 0) return (error); /* * If this segment contains the program headers, * remember their virtual address for the AT_PHDR * aux entry. Static binaries don't usually include * a PT_PHDR entry. */ if (phdr[i].p_offset == 0 && hdr->e_phoff + hdr->e_phnum * hdr->e_phentsize <= phdr[i].p_filesz) proghdr = phdr[i].p_vaddr + hdr->e_phoff + et_dyn_addr; seg_addr = trunc_page(phdr[i].p_vaddr + et_dyn_addr); seg_size = round_page(phdr[i].p_memsz + phdr[i].p_vaddr + et_dyn_addr - seg_addr); /* * Make the largest executable segment the official * text segment and all others data. * * Note that obreak() assumes that data_addr + * data_size == end of data load area, and the ELF * file format expects segments to be sorted by * address. If multiple data segments exist, the * last one will be used. */ if (phdr[i].p_flags & PF_X && text_size < seg_size) { text_size = seg_size; text_addr = seg_addr; } else { data_size = seg_size; data_addr = seg_addr; } total_size += seg_size; break; case PT_PHDR: /* Program header table info */ proghdr = phdr[i].p_vaddr + et_dyn_addr; break; default: break; } } if (data_addr == 0 && data_size == 0) { data_addr = text_addr; data_size = text_size; } entry = (u_long)hdr->e_entry + et_dyn_addr; /* * Check limits. It should be safe to check the * limits after loading the segments since we do * not actually fault in all the segments pages. */ PROC_LOCK(imgp->proc); if (data_size > lim_cur(imgp->proc, RLIMIT_DATA) || text_size > maxtsiz || total_size > lim_cur(imgp->proc, RLIMIT_VMEM) || racct_set(imgp->proc, RACCT_DATA, data_size) != 0 || racct_set(imgp->proc, RACCT_VMEM, total_size) != 0) { PROC_UNLOCK(imgp->proc); return (ENOMEM); } vmspace = imgp->proc->p_vmspace; vmspace->vm_tsize = text_size >> PAGE_SHIFT; vmspace->vm_taddr = (caddr_t)(uintptr_t)text_addr; vmspace->vm_dsize = data_size >> PAGE_SHIFT; vmspace->vm_daddr = (caddr_t)(uintptr_t)data_addr; /* * We load the dynamic linker where a userland call * to mmap(0, ...) would put it. The rationale behind this * calculation is that it leaves room for the heap to grow to * its maximum allowed size. */ addr = round_page((vm_offset_t)vmspace->vm_daddr + lim_max(imgp->proc, RLIMIT_DATA)); PROC_UNLOCK(imgp->proc); imgp->entry_addr = entry; if (interp != NULL) { int have_interp = FALSE; VOP_UNLOCK(imgp->vp, 0); if (brand_info->emul_path != NULL && brand_info->emul_path[0] != '\0') { path = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); snprintf(path, MAXPATHLEN, "%s%s", brand_info->emul_path, interp); error = __elfN(load_file)(imgp->proc, path, &addr, &imgp->entry_addr, sv->sv_pagesize); free(path, M_TEMP); if (error == 0) have_interp = TRUE; } if (!have_interp && newinterp != NULL) { error = __elfN(load_file)(imgp->proc, newinterp, &addr, &imgp->entry_addr, sv->sv_pagesize); if (error == 0) have_interp = TRUE; } if (!have_interp) { error = __elfN(load_file)(imgp->proc, interp, &addr, &imgp->entry_addr, sv->sv_pagesize); } vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY); if (error != 0) { uprintf("ELF interpreter %s not found\n", interp); return (error); } } else addr = et_dyn_addr; /* * Construct auxargs table (used by the fixup routine) */ elf_auxargs = malloc(sizeof(Elf_Auxargs), M_TEMP, M_WAITOK); elf_auxargs->execfd = -1; elf_auxargs->phdr = proghdr; elf_auxargs->phent = hdr->e_phentsize; elf_auxargs->phnum = hdr->e_phnum; elf_auxargs->pagesz = PAGE_SIZE; elf_auxargs->base = addr; elf_auxargs->flags = 0; elf_auxargs->entry = entry; imgp->auxargs = elf_auxargs; imgp->interpreted = 0; imgp->reloc_base = addr; imgp->proc->p_osrel = osrel; return (error); } #define suword __CONCAT(suword, __ELF_WORD_SIZE) int __elfN(freebsd_fixup)(register_t **stack_base, struct image_params *imgp) { Elf_Auxargs *args = (Elf_Auxargs *)imgp->auxargs; Elf_Addr *base; Elf_Addr *pos; base = (Elf_Addr *)*stack_base; pos = base + (imgp->args->argc + imgp->args->envc + 2); if (args->execfd != -1) AUXARGS_ENTRY(pos, AT_EXECFD, args->execfd); AUXARGS_ENTRY(pos, AT_PHDR, args->phdr); AUXARGS_ENTRY(pos, AT_PHENT, args->phent); AUXARGS_ENTRY(pos, AT_PHNUM, args->phnum); AUXARGS_ENTRY(pos, AT_PAGESZ, args->pagesz); AUXARGS_ENTRY(pos, AT_FLAGS, args->flags); AUXARGS_ENTRY(pos, AT_ENTRY, args->entry); AUXARGS_ENTRY(pos, AT_BASE, args->base); if (imgp->execpathp != 0) AUXARGS_ENTRY(pos, AT_EXECPATH, imgp->execpathp); AUXARGS_ENTRY(pos, AT_OSRELDATE, osreldate); if (imgp->canary != 0) { AUXARGS_ENTRY(pos, AT_CANARY, imgp->canary); AUXARGS_ENTRY(pos, AT_CANARYLEN, imgp->canarylen); } AUXARGS_ENTRY(pos, AT_NCPUS, mp_ncpus); if (imgp->pagesizes != 0) { AUXARGS_ENTRY(pos, AT_PAGESIZES, imgp->pagesizes); AUXARGS_ENTRY(pos, AT_PAGESIZESLEN, imgp->pagesizeslen); } if (imgp->sysent->sv_timekeep_base != 0) { AUXARGS_ENTRY(pos, AT_TIMEKEEP, imgp->sysent->sv_timekeep_base); } AUXARGS_ENTRY(pos, AT_STACKPROT, imgp->sysent->sv_shared_page_obj != NULL && imgp->stack_prot != 0 ? imgp->stack_prot : imgp->sysent->sv_stackprot); AUXARGS_ENTRY(pos, AT_NULL, 0); free(imgp->auxargs, M_TEMP); imgp->auxargs = NULL; base--; suword(base, (long)imgp->args->argc); *stack_base = (register_t *)base; return (0); } /* * Code for generating ELF core dumps. */ typedef void (*segment_callback)(vm_map_entry_t, void *); /* Closure for cb_put_phdr(). */ struct phdr_closure { Elf_Phdr *phdr; /* Program header to fill in */ Elf_Off offset; /* Offset of segment in core file */ }; /* Closure for cb_size_segment(). */ struct sseg_closure { int count; /* Count of writable segments. */ size_t size; /* Total size of all writable segments. */ }; typedef void (*outfunc_t)(void *, struct sbuf *, size_t *); struct note_info { int type; /* Note type. */ outfunc_t outfunc; /* Output function. */ void *outarg; /* Argument for the output function. */ size_t outsize; /* Output size. */ TAILQ_ENTRY(note_info) link; /* Link to the next note info. */ }; TAILQ_HEAD(note_info_list, note_info); static void cb_put_phdr(vm_map_entry_t, void *); static void cb_size_segment(vm_map_entry_t, void *); static void each_writable_segment(struct thread *, segment_callback, void *); static int __elfN(corehdr)(struct thread *, struct vnode *, struct ucred *, int, void *, size_t, struct note_info_list *, size_t, gzFile); static void __elfN(prepare_notes)(struct thread *, struct note_info_list *, size_t *); static void __elfN(puthdr)(struct thread *, void *, size_t, int, size_t); static void __elfN(putnote)(struct note_info *, struct sbuf *); static size_t register_note(struct note_info_list *, int, outfunc_t, void *); static int sbuf_drain_core_output(void *, const char *, int); static int sbuf_drain_count(void *arg, const char *data, int len); static void __elfN(note_fpregset)(void *, struct sbuf *, size_t *); static void __elfN(note_prpsinfo)(void *, struct sbuf *, size_t *); static void __elfN(note_prstatus)(void *, struct sbuf *, size_t *); static void __elfN(note_threadmd)(void *, struct sbuf *, size_t *); static void __elfN(note_thrmisc)(void *, struct sbuf *, size_t *); static void __elfN(note_procstat_auxv)(void *, struct sbuf *, size_t *); static void __elfN(note_procstat_proc)(void *, struct sbuf *, size_t *); static void __elfN(note_procstat_psstrings)(void *, struct sbuf *, size_t *); static void note_procstat_files(void *, struct sbuf *, size_t *); static void note_procstat_groups(void *, struct sbuf *, size_t *); static void note_procstat_osrel(void *, struct sbuf *, size_t *); static void note_procstat_rlimit(void *, struct sbuf *, size_t *); static void note_procstat_umask(void *, struct sbuf *, size_t *); static void note_procstat_vmmap(void *, struct sbuf *, size_t *); #ifdef COMPRESS_USER_CORES extern int compress_user_cores; extern int compress_user_cores_gzlevel; #endif static int core_output(struct vnode *vp, void *base, size_t len, off_t offset, struct ucred *active_cred, struct ucred *file_cred, struct thread *td, char *core_buf, gzFile gzfile) { int error; if (gzfile) { #ifdef COMPRESS_USER_CORES error = compress_core(gzfile, base, core_buf, len, td); #else panic("shouldn't be here"); #endif } else { error = vn_rdwr_inchunks(UIO_WRITE, vp, base, len, offset, UIO_USERSPACE, IO_UNIT | IO_DIRECT | IO_RANGELOCKED, active_cred, file_cred, NULL, td); } return (error); } /* Coredump output parameters for sbuf drain routine. */ struct sbuf_drain_core_params { off_t offset; struct ucred *active_cred; struct ucred *file_cred; struct thread *td; struct vnode *vp; #ifdef COMPRESS_USER_CORES gzFile gzfile; #endif }; /* * Drain into a core file. */ static int sbuf_drain_core_output(void *arg, const char *data, int len) { struct sbuf_drain_core_params *p; int error, locked; p = (struct sbuf_drain_core_params *)arg; /* * Some kern_proc out routines that print to this sbuf may * call us with the process lock held. Draining with the * non-sleepable lock held is unsafe. The lock is needed for * those routines when dumping a live process. In our case we * can safely release the lock before draining and acquire * again after. */ locked = PROC_LOCKED(p->td->td_proc); if (locked) PROC_UNLOCK(p->td->td_proc); #ifdef COMPRESS_USER_CORES if (p->gzfile != Z_NULL) error = compress_core(p->gzfile, NULL, __DECONST(char *, data), len, p->td); else #endif error = vn_rdwr_inchunks(UIO_WRITE, p->vp, __DECONST(void *, data), len, p->offset, UIO_SYSSPACE, IO_UNIT | IO_DIRECT | IO_RANGELOCKED, p->active_cred, p->file_cred, NULL, p->td); if (locked) PROC_LOCK(p->td->td_proc); if (error != 0) return (-error); p->offset += len; return (len); } /* * Drain into a counter. */ static int sbuf_drain_count(void *arg, const char *data __unused, int len) { size_t *sizep; sizep = (size_t *)arg; *sizep += len; return (len); } int __elfN(coredump)(struct thread *td, struct vnode *vp, off_t limit, int flags) { struct ucred *cred = td->td_ucred; int error = 0; struct sseg_closure seginfo; struct note_info_list notelst; struct note_info *ninfo; void *hdr; size_t hdrsize, notesz, coresize; gzFile gzfile = Z_NULL; char *core_buf = NULL; #ifdef COMPRESS_USER_CORES char gzopen_flags[8]; char *p; int doing_compress = flags & IMGACT_CORE_COMPRESS; #endif hdr = NULL; TAILQ_INIT(¬elst); #ifdef COMPRESS_USER_CORES if (doing_compress) { p = gzopen_flags; *p++ = 'w'; if (compress_user_cores_gzlevel >= 0 && compress_user_cores_gzlevel <= 9) *p++ = '0' + compress_user_cores_gzlevel; *p = 0; gzfile = gz_open("", gzopen_flags, vp); if (gzfile == Z_NULL) { error = EFAULT; goto done; } core_buf = malloc(CORE_BUF_SIZE, M_TEMP, M_WAITOK | M_ZERO); if (!core_buf) { error = ENOMEM; goto done; } } #endif /* Size the program segments. */ seginfo.count = 0; seginfo.size = 0; each_writable_segment(td, cb_size_segment, &seginfo); /* * Collect info about the core file header area. */ hdrsize = sizeof(Elf_Ehdr) + sizeof(Elf_Phdr) * (1 + seginfo.count); __elfN(prepare_notes)(td, ¬elst, ¬esz); coresize = round_page(hdrsize + notesz) + seginfo.size; #ifdef RACCT PROC_LOCK(td->td_proc); error = racct_add(td->td_proc, RACCT_CORE, coresize); PROC_UNLOCK(td->td_proc); if (error != 0) { error = EFAULT; goto done; } #endif if (coresize >= limit) { error = EFAULT; goto done; } /* * Allocate memory for building the header, fill it up, * and write it out following the notes. */ hdr = malloc(hdrsize, M_TEMP, M_WAITOK); if (hdr == NULL) { error = EINVAL; goto done; } error = __elfN(corehdr)(td, vp, cred, seginfo.count, hdr, hdrsize, ¬elst, notesz, gzfile); /* Write the contents of all of the writable segments. */ if (error == 0) { Elf_Phdr *php; off_t offset; int i; php = (Elf_Phdr *)((char *)hdr + sizeof(Elf_Ehdr)) + 1; offset = round_page(hdrsize + notesz); for (i = 0; i < seginfo.count; i++) { error = core_output(vp, (caddr_t)(uintptr_t)php->p_vaddr, php->p_filesz, offset, cred, NOCRED, curthread, core_buf, gzfile); if (error != 0) break; offset += php->p_filesz; php++; } } if (error) { log(LOG_WARNING, "Failed to write core file for process %s (error %d)\n", curproc->p_comm, error); } done: #ifdef COMPRESS_USER_CORES if (core_buf) free(core_buf, M_TEMP); if (gzfile) gzclose(gzfile); #endif while ((ninfo = TAILQ_FIRST(¬elst)) != NULL) { TAILQ_REMOVE(¬elst, ninfo, link); free(ninfo, M_TEMP); } if (hdr != NULL) free(hdr, M_TEMP); return (error); } /* * A callback for each_writable_segment() to write out the segment's * program header entry. */ static void cb_put_phdr(entry, closure) vm_map_entry_t entry; void *closure; { struct phdr_closure *phc = (struct phdr_closure *)closure; Elf_Phdr *phdr = phc->phdr; phc->offset = round_page(phc->offset); phdr->p_type = PT_LOAD; phdr->p_offset = phc->offset; phdr->p_vaddr = entry->start; phdr->p_paddr = 0; phdr->p_filesz = phdr->p_memsz = entry->end - entry->start; phdr->p_align = PAGE_SIZE; phdr->p_flags = __elfN(untrans_prot)(entry->protection); phc->offset += phdr->p_filesz; phc->phdr++; } /* * A callback for each_writable_segment() to gather information about * the number of segments and their total size. */ static void cb_size_segment(entry, closure) vm_map_entry_t entry; void *closure; { struct sseg_closure *ssc = (struct sseg_closure *)closure; ssc->count++; ssc->size += entry->end - entry->start; } /* * For each writable segment in the process's memory map, call the given * function with a pointer to the map entry and some arbitrary * caller-supplied data. */ static void each_writable_segment(td, func, closure) struct thread *td; segment_callback func; void *closure; { struct proc *p = td->td_proc; vm_map_t map = &p->p_vmspace->vm_map; vm_map_entry_t entry; vm_object_t backing_object, object; boolean_t ignore_entry; vm_map_lock_read(map); for (entry = map->header.next; entry != &map->header; entry = entry->next) { /* * Don't dump inaccessible mappings, deal with legacy * coredump mode. * * Note that read-only segments related to the elf binary * are marked MAP_ENTRY_NOCOREDUMP now so we no longer * need to arbitrarily ignore such segments. */ if (elf_legacy_coredump) { if ((entry->protection & VM_PROT_RW) != VM_PROT_RW) continue; } else { if ((entry->protection & VM_PROT_ALL) == 0) continue; } /* * Dont include memory segment in the coredump if * MAP_NOCORE is set in mmap(2) or MADV_NOCORE in * madvise(2). Do not dump submaps (i.e. parts of the * kernel map). */ if (entry->eflags & (MAP_ENTRY_NOCOREDUMP|MAP_ENTRY_IS_SUB_MAP)) continue; if ((object = entry->object.vm_object) == NULL) continue; /* Ignore memory-mapped devices and such things. */ VM_OBJECT_RLOCK(object); while ((backing_object = object->backing_object) != NULL) { VM_OBJECT_RLOCK(backing_object); VM_OBJECT_RUNLOCK(object); object = backing_object; } ignore_entry = object->type != OBJT_DEFAULT && - object->type != OBJT_SWAP && object->type != OBJT_VNODE; + object->type != OBJT_SWAP && object->type != OBJT_VNODE && + object->type != OBJT_PHYS; VM_OBJECT_RUNLOCK(object); if (ignore_entry) continue; (*func)(entry, closure); } vm_map_unlock_read(map); } /* * Write the core file header to the file, including padding up to * the page boundary. */ static int __elfN(corehdr)(struct thread *td, struct vnode *vp, struct ucred *cred, int numsegs, void *hdr, size_t hdrsize, struct note_info_list *notelst, size_t notesz, gzFile gzfile) { struct sbuf_drain_core_params params; struct note_info *ninfo; struct sbuf *sb; int error; /* Fill in the header. */ bzero(hdr, hdrsize); __elfN(puthdr)(td, hdr, hdrsize, numsegs, notesz); params.offset = 0; params.active_cred = cred; params.file_cred = NOCRED; params.td = td; params.vp = vp; #ifdef COMPRESS_USER_CORES params.gzfile = gzfile; #endif sb = sbuf_new(NULL, NULL, CORE_BUF_SIZE, SBUF_FIXEDLEN); sbuf_set_drain(sb, sbuf_drain_core_output, ¶ms); sbuf_start_section(sb, NULL); sbuf_bcat(sb, hdr, hdrsize); TAILQ_FOREACH(ninfo, notelst, link) __elfN(putnote)(ninfo, sb); /* Align up to a page boundary for the program segments. */ sbuf_end_section(sb, -1, PAGE_SIZE, 0); error = sbuf_finish(sb); sbuf_delete(sb); return (error); } static void __elfN(prepare_notes)(struct thread *td, struct note_info_list *list, size_t *sizep) { struct proc *p; struct thread *thr; size_t size; p = td->td_proc; size = 0; size += register_note(list, NT_PRPSINFO, __elfN(note_prpsinfo), p); /* * To have the debugger select the right thread (LWP) as the initial * thread, we dump the state of the thread passed to us in td first. * This is the thread that causes the core dump and thus likely to * be the right thread one wants to have selected in the debugger. */ thr = td; while (thr != NULL) { size += register_note(list, NT_PRSTATUS, __elfN(note_prstatus), thr); size += register_note(list, NT_FPREGSET, __elfN(note_fpregset), thr); size += register_note(list, NT_THRMISC, __elfN(note_thrmisc), thr); size += register_note(list, -1, __elfN(note_threadmd), thr); thr = (thr == td) ? TAILQ_FIRST(&p->p_threads) : TAILQ_NEXT(thr, td_plist); if (thr == td) thr = TAILQ_NEXT(thr, td_plist); } size += register_note(list, NT_PROCSTAT_PROC, __elfN(note_procstat_proc), p); size += register_note(list, NT_PROCSTAT_FILES, note_procstat_files, p); size += register_note(list, NT_PROCSTAT_VMMAP, note_procstat_vmmap, p); size += register_note(list, NT_PROCSTAT_GROUPS, note_procstat_groups, p); size += register_note(list, NT_PROCSTAT_UMASK, note_procstat_umask, p); size += register_note(list, NT_PROCSTAT_RLIMIT, note_procstat_rlimit, p); size += register_note(list, NT_PROCSTAT_OSREL, note_procstat_osrel, p); size += register_note(list, NT_PROCSTAT_PSSTRINGS, __elfN(note_procstat_psstrings), p); size += register_note(list, NT_PROCSTAT_AUXV, __elfN(note_procstat_auxv), p); *sizep = size; } static void __elfN(puthdr)(struct thread *td, void *hdr, size_t hdrsize, int numsegs, size_t notesz) { Elf_Ehdr *ehdr; Elf_Phdr *phdr; struct phdr_closure phc; ehdr = (Elf_Ehdr *)hdr; phdr = (Elf_Phdr *)((char *)hdr + sizeof(Elf_Ehdr)); ehdr->e_ident[EI_MAG0] = ELFMAG0; ehdr->e_ident[EI_MAG1] = ELFMAG1; ehdr->e_ident[EI_MAG2] = ELFMAG2; ehdr->e_ident[EI_MAG3] = ELFMAG3; ehdr->e_ident[EI_CLASS] = ELF_CLASS; ehdr->e_ident[EI_DATA] = ELF_DATA; ehdr->e_ident[EI_VERSION] = EV_CURRENT; ehdr->e_ident[EI_OSABI] = ELFOSABI_FREEBSD; ehdr->e_ident[EI_ABIVERSION] = 0; ehdr->e_ident[EI_PAD] = 0; ehdr->e_type = ET_CORE; #if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32 ehdr->e_machine = ELF_ARCH32; #else ehdr->e_machine = ELF_ARCH; #endif ehdr->e_version = EV_CURRENT; ehdr->e_entry = 0; ehdr->e_phoff = sizeof(Elf_Ehdr); ehdr->e_flags = 0; ehdr->e_ehsize = sizeof(Elf_Ehdr); ehdr->e_phentsize = sizeof(Elf_Phdr); ehdr->e_phnum = numsegs + 1; ehdr->e_shentsize = sizeof(Elf_Shdr); ehdr->e_shnum = 0; ehdr->e_shstrndx = SHN_UNDEF; /* * Fill in the program header entries. */ /* The note segement. */ phdr->p_type = PT_NOTE; phdr->p_offset = hdrsize; phdr->p_vaddr = 0; phdr->p_paddr = 0; phdr->p_filesz = notesz; phdr->p_memsz = 0; phdr->p_flags = PF_R; phdr->p_align = ELF_NOTE_ROUNDSIZE; phdr++; /* All the writable segments from the program. */ phc.phdr = phdr; phc.offset = round_page(hdrsize + notesz); each_writable_segment(td, cb_put_phdr, &phc); } static size_t register_note(struct note_info_list *list, int type, outfunc_t out, void *arg) { struct note_info *ninfo; size_t size, notesize; size = 0; out(arg, NULL, &size); ninfo = malloc(sizeof(*ninfo), M_TEMP, M_ZERO | M_WAITOK); ninfo->type = type; ninfo->outfunc = out; ninfo->outarg = arg; ninfo->outsize = size; TAILQ_INSERT_TAIL(list, ninfo, link); if (type == -1) return (size); notesize = sizeof(Elf_Note) + /* note header */ roundup2(sizeof(FREEBSD_ABI_VENDOR), ELF_NOTE_ROUNDSIZE) + /* note name */ roundup2(size, ELF_NOTE_ROUNDSIZE); /* note description */ return (notesize); } static size_t append_note_data(const void *src, void *dst, size_t len) { size_t padded_len; padded_len = roundup2(len, ELF_NOTE_ROUNDSIZE); if (dst != NULL) { bcopy(src, dst, len); bzero((char *)dst + len, padded_len - len); } return (padded_len); } size_t __elfN(populate_note)(int type, void *src, void *dst, size_t size, void **descp) { Elf_Note *note; char *buf; size_t notesize; buf = dst; if (buf != NULL) { note = (Elf_Note *)buf; note->n_namesz = sizeof(FREEBSD_ABI_VENDOR); note->n_descsz = size; note->n_type = type; buf += sizeof(*note); buf += append_note_data(FREEBSD_ABI_VENDOR, buf, sizeof(FREEBSD_ABI_VENDOR)); append_note_data(src, buf, size); if (descp != NULL) *descp = buf; } notesize = sizeof(Elf_Note) + /* note header */ roundup2(sizeof(FREEBSD_ABI_VENDOR), ELF_NOTE_ROUNDSIZE) + /* note name */ roundup2(size, ELF_NOTE_ROUNDSIZE); /* note description */ return (notesize); } static void __elfN(putnote)(struct note_info *ninfo, struct sbuf *sb) { Elf_Note note; ssize_t old_len; if (ninfo->type == -1) { ninfo->outfunc(ninfo->outarg, sb, &ninfo->outsize); return; } note.n_namesz = sizeof(FREEBSD_ABI_VENDOR); note.n_descsz = ninfo->outsize; note.n_type = ninfo->type; sbuf_bcat(sb, ¬e, sizeof(note)); sbuf_start_section(sb, &old_len); sbuf_bcat(sb, FREEBSD_ABI_VENDOR, sizeof(FREEBSD_ABI_VENDOR)); sbuf_end_section(sb, old_len, ELF_NOTE_ROUNDSIZE, 0); if (note.n_descsz == 0) return; sbuf_start_section(sb, &old_len); ninfo->outfunc(ninfo->outarg, sb, &ninfo->outsize); sbuf_end_section(sb, old_len, ELF_NOTE_ROUNDSIZE, 0); } /* * Miscellaneous note out functions. */ #if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32 #include typedef struct prstatus32 elf_prstatus_t; typedef struct prpsinfo32 elf_prpsinfo_t; typedef struct fpreg32 elf_prfpregset_t; typedef struct fpreg32 elf_fpregset_t; typedef struct reg32 elf_gregset_t; typedef struct thrmisc32 elf_thrmisc_t; #define ELF_KERN_PROC_MASK KERN_PROC_MASK32 typedef struct kinfo_proc32 elf_kinfo_proc_t; typedef uint32_t elf_ps_strings_t; #else typedef prstatus_t elf_prstatus_t; typedef prpsinfo_t elf_prpsinfo_t; typedef prfpregset_t elf_prfpregset_t; typedef prfpregset_t elf_fpregset_t; typedef gregset_t elf_gregset_t; typedef thrmisc_t elf_thrmisc_t; #define ELF_KERN_PROC_MASK 0 typedef struct kinfo_proc elf_kinfo_proc_t; typedef vm_offset_t elf_ps_strings_t; #endif static void __elfN(note_prpsinfo)(void *arg, struct sbuf *sb, size_t *sizep) { struct proc *p; elf_prpsinfo_t *psinfo; p = (struct proc *)arg; if (sb != NULL) { KASSERT(*sizep == sizeof(*psinfo), ("invalid size")); psinfo = malloc(sizeof(*psinfo), M_TEMP, M_ZERO | M_WAITOK); psinfo->pr_version = PRPSINFO_VERSION; psinfo->pr_psinfosz = sizeof(elf_prpsinfo_t); strlcpy(psinfo->pr_fname, p->p_comm, sizeof(psinfo->pr_fname)); /* * XXX - We don't fill in the command line arguments properly * yet. */ strlcpy(psinfo->pr_psargs, p->p_comm, sizeof(psinfo->pr_psargs)); sbuf_bcat(sb, psinfo, sizeof(*psinfo)); free(psinfo, M_TEMP); } *sizep = sizeof(*psinfo); } static void __elfN(note_prstatus)(void *arg, struct sbuf *sb, size_t *sizep) { struct thread *td; elf_prstatus_t *status; td = (struct thread *)arg; if (sb != NULL) { KASSERT(*sizep == sizeof(*status), ("invalid size")); status = malloc(sizeof(*status), M_TEMP, M_ZERO | M_WAITOK); status->pr_version = PRSTATUS_VERSION; status->pr_statussz = sizeof(elf_prstatus_t); status->pr_gregsetsz = sizeof(elf_gregset_t); status->pr_fpregsetsz = sizeof(elf_fpregset_t); status->pr_osreldate = osreldate; status->pr_cursig = td->td_proc->p_sig; status->pr_pid = td->td_tid; #if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32 fill_regs32(td, &status->pr_reg); #else fill_regs(td, &status->pr_reg); #endif sbuf_bcat(sb, status, sizeof(*status)); free(status, M_TEMP); } *sizep = sizeof(*status); } static void __elfN(note_fpregset)(void *arg, struct sbuf *sb, size_t *sizep) { struct thread *td; elf_prfpregset_t *fpregset; td = (struct thread *)arg; if (sb != NULL) { KASSERT(*sizep == sizeof(*fpregset), ("invalid size")); fpregset = malloc(sizeof(*fpregset), M_TEMP, M_ZERO | M_WAITOK); #if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32 fill_fpregs32(td, fpregset); #else fill_fpregs(td, fpregset); #endif sbuf_bcat(sb, fpregset, sizeof(*fpregset)); free(fpregset, M_TEMP); } *sizep = sizeof(*fpregset); } static void __elfN(note_thrmisc)(void *arg, struct sbuf *sb, size_t *sizep) { struct thread *td; elf_thrmisc_t thrmisc; td = (struct thread *)arg; if (sb != NULL) { KASSERT(*sizep == sizeof(thrmisc), ("invalid size")); bzero(&thrmisc._pad, sizeof(thrmisc._pad)); strcpy(thrmisc.pr_tname, td->td_name); sbuf_bcat(sb, &thrmisc, sizeof(thrmisc)); } *sizep = sizeof(thrmisc); } /* * Allow for MD specific notes, as well as any MD * specific preparations for writing MI notes. */ static void __elfN(note_threadmd)(void *arg, struct sbuf *sb, size_t *sizep) { struct thread *td; void *buf; size_t size; td = (struct thread *)arg; size = *sizep; if (size != 0 && sb != NULL) buf = malloc(size, M_TEMP, M_ZERO | M_WAITOK); else buf = NULL; size = 0; __elfN(dump_thread)(td, buf, &size); KASSERT(sb == NULL || *sizep == size, ("invalid size")); if (size != 0 && sb != NULL) sbuf_bcat(sb, buf, size); free(buf, M_TEMP); *sizep = size; } #ifdef KINFO_PROC_SIZE CTASSERT(sizeof(struct kinfo_proc) == KINFO_PROC_SIZE); #endif static void __elfN(note_procstat_proc)(void *arg, struct sbuf *sb, size_t *sizep) { struct proc *p; size_t size; int structsize; p = (struct proc *)arg; size = sizeof(structsize) + p->p_numthreads * sizeof(elf_kinfo_proc_t); if (sb != NULL) { KASSERT(*sizep == size, ("invalid size")); structsize = sizeof(elf_kinfo_proc_t); sbuf_bcat(sb, &structsize, sizeof(structsize)); sx_slock(&proctree_lock); PROC_LOCK(p); kern_proc_out(p, sb, ELF_KERN_PROC_MASK); sx_sunlock(&proctree_lock); } *sizep = size; } #ifdef KINFO_FILE_SIZE CTASSERT(sizeof(struct kinfo_file) == KINFO_FILE_SIZE); #endif static void note_procstat_files(void *arg, struct sbuf *sb, size_t *sizep) { struct proc *p; size_t size; int structsize; p = (struct proc *)arg; if (sb == NULL) { size = 0; sb = sbuf_new(NULL, NULL, 128, SBUF_FIXEDLEN); sbuf_set_drain(sb, sbuf_drain_count, &size); sbuf_bcat(sb, &structsize, sizeof(structsize)); PROC_LOCK(p); kern_proc_filedesc_out(p, sb, -1); sbuf_finish(sb); sbuf_delete(sb); *sizep = size; } else { structsize = sizeof(struct kinfo_file); sbuf_bcat(sb, &structsize, sizeof(structsize)); PROC_LOCK(p); kern_proc_filedesc_out(p, sb, -1); } } #ifdef KINFO_VMENTRY_SIZE CTASSERT(sizeof(struct kinfo_vmentry) == KINFO_VMENTRY_SIZE); #endif static void note_procstat_vmmap(void *arg, struct sbuf *sb, size_t *sizep) { struct proc *p; size_t size; int structsize; p = (struct proc *)arg; if (sb == NULL) { size = 0; sb = sbuf_new(NULL, NULL, 128, SBUF_FIXEDLEN); sbuf_set_drain(sb, sbuf_drain_count, &size); sbuf_bcat(sb, &structsize, sizeof(structsize)); PROC_LOCK(p); kern_proc_vmmap_out(p, sb); sbuf_finish(sb); sbuf_delete(sb); *sizep = size; } else { structsize = sizeof(struct kinfo_vmentry); sbuf_bcat(sb, &structsize, sizeof(structsize)); PROC_LOCK(p); kern_proc_vmmap_out(p, sb); } } static void note_procstat_groups(void *arg, struct sbuf *sb, size_t *sizep) { struct proc *p; size_t size; int structsize; p = (struct proc *)arg; size = sizeof(structsize) + p->p_ucred->cr_ngroups * sizeof(gid_t); if (sb != NULL) { KASSERT(*sizep == size, ("invalid size")); structsize = sizeof(gid_t); sbuf_bcat(sb, &structsize, sizeof(structsize)); sbuf_bcat(sb, p->p_ucred->cr_groups, p->p_ucred->cr_ngroups * sizeof(gid_t)); } *sizep = size; } static void note_procstat_umask(void *arg, struct sbuf *sb, size_t *sizep) { struct proc *p; size_t size; int structsize; p = (struct proc *)arg; size = sizeof(structsize) + sizeof(p->p_fd->fd_cmask); if (sb != NULL) { KASSERT(*sizep == size, ("invalid size")); structsize = sizeof(p->p_fd->fd_cmask); sbuf_bcat(sb, &structsize, sizeof(structsize)); sbuf_bcat(sb, &p->p_fd->fd_cmask, sizeof(p->p_fd->fd_cmask)); } *sizep = size; } static void note_procstat_rlimit(void *arg, struct sbuf *sb, size_t *sizep) { struct proc *p; struct rlimit rlim[RLIM_NLIMITS]; size_t size; int structsize, i; p = (struct proc *)arg; size = sizeof(structsize) + sizeof(rlim); if (sb != NULL) { KASSERT(*sizep == size, ("invalid size")); structsize = sizeof(rlim); sbuf_bcat(sb, &structsize, sizeof(structsize)); PROC_LOCK(p); for (i = 0; i < RLIM_NLIMITS; i++) lim_rlimit(p, i, &rlim[i]); PROC_UNLOCK(p); sbuf_bcat(sb, rlim, sizeof(rlim)); } *sizep = size; } static void note_procstat_osrel(void *arg, struct sbuf *sb, size_t *sizep) { struct proc *p; size_t size; int structsize; p = (struct proc *)arg; size = sizeof(structsize) + sizeof(p->p_osrel); if (sb != NULL) { KASSERT(*sizep == size, ("invalid size")); structsize = sizeof(p->p_osrel); sbuf_bcat(sb, &structsize, sizeof(structsize)); sbuf_bcat(sb, &p->p_osrel, sizeof(p->p_osrel)); } *sizep = size; } static void __elfN(note_procstat_psstrings)(void *arg, struct sbuf *sb, size_t *sizep) { struct proc *p; elf_ps_strings_t ps_strings; size_t size; int structsize; p = (struct proc *)arg; size = sizeof(structsize) + sizeof(ps_strings); if (sb != NULL) { KASSERT(*sizep == size, ("invalid size")); structsize = sizeof(ps_strings); #if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32 ps_strings = PTROUT(p->p_sysent->sv_psstrings); #else ps_strings = p->p_sysent->sv_psstrings; #endif sbuf_bcat(sb, &structsize, sizeof(structsize)); sbuf_bcat(sb, &ps_strings, sizeof(ps_strings)); } *sizep = size; } static void __elfN(note_procstat_auxv)(void *arg, struct sbuf *sb, size_t *sizep) { struct proc *p; size_t size; int structsize; p = (struct proc *)arg; if (sb == NULL) { size = 0; sb = sbuf_new(NULL, NULL, 128, SBUF_FIXEDLEN); sbuf_set_drain(sb, sbuf_drain_count, &size); sbuf_bcat(sb, &structsize, sizeof(structsize)); PHOLD(p); proc_getauxv(curthread, p, sb); PRELE(p); sbuf_finish(sb); sbuf_delete(sb); *sizep = size; } else { structsize = sizeof(Elf_Auxinfo); sbuf_bcat(sb, &structsize, sizeof(structsize)); PHOLD(p); proc_getauxv(curthread, p, sb); PRELE(p); } } static boolean_t __elfN(parse_notes)(struct image_params *imgp, Elf_Brandnote *checknote, int32_t *osrel, const Elf_Phdr *pnote) { const Elf_Note *note, *note0, *note_end; const char *note_name; int i; if (pnote == NULL || pnote->p_offset > PAGE_SIZE || pnote->p_filesz > PAGE_SIZE - pnote->p_offset) return (FALSE); note = note0 = (const Elf_Note *)(imgp->image_header + pnote->p_offset); note_end = (const Elf_Note *)(imgp->image_header + pnote->p_offset + pnote->p_filesz); for (i = 0; i < 100 && note >= note0 && note < note_end; i++) { if (!aligned(note, Elf32_Addr) || (const char *)note_end - (const char *)note < sizeof(Elf_Note)) return (FALSE); if (note->n_namesz != checknote->hdr.n_namesz || note->n_descsz != checknote->hdr.n_descsz || note->n_type != checknote->hdr.n_type) goto nextnote; note_name = (const char *)(note + 1); if (note_name + checknote->hdr.n_namesz >= (const char *)note_end || strncmp(checknote->vendor, note_name, checknote->hdr.n_namesz) != 0) goto nextnote; /* * Fetch the osreldate for binary * from the ELF OSABI-note if necessary. */ if ((checknote->flags & BN_TRANSLATE_OSREL) != 0 && checknote->trans_osrel != NULL) return (checknote->trans_osrel(note, osrel)); return (TRUE); nextnote: note = (const Elf_Note *)((const char *)(note + 1) + roundup2(note->n_namesz, ELF_NOTE_ROUNDSIZE) + roundup2(note->n_descsz, ELF_NOTE_ROUNDSIZE)); } return (FALSE); } /* * Try to find the appropriate ABI-note section for checknote, * fetch the osreldate for binary from the ELF OSABI-note. Only the * first page of the image is searched, the same as for headers. */ static boolean_t __elfN(check_note)(struct image_params *imgp, Elf_Brandnote *checknote, int32_t *osrel) { const Elf_Phdr *phdr; const Elf_Ehdr *hdr; int i; hdr = (const Elf_Ehdr *)imgp->image_header; phdr = (const Elf_Phdr *)(imgp->image_header + hdr->e_phoff); for (i = 0; i < hdr->e_phnum; i++) { if (phdr[i].p_type == PT_NOTE && __elfN(parse_notes)(imgp, checknote, osrel, &phdr[i])) return (TRUE); } return (FALSE); } /* * Tell kern_execve.c about it, with a little help from the linker. */ static struct execsw __elfN(execsw) = { __CONCAT(exec_, __elfN(imgact)), __XSTRING(__CONCAT(ELF, __ELF_WORD_SIZE)) }; EXEC_SET(__CONCAT(elf, __ELF_WORD_SIZE), __elfN(execsw)); #ifdef COMPRESS_USER_CORES /* * Compress and write out a core segment for a user process. * * 'inbuf' is the starting address of a VM segment in the process' address * space that is to be compressed and written out to the core file. 'dest_buf' * is a buffer in the kernel's address space. The segment is copied from * 'inbuf' to 'dest_buf' first before being processed by the compression * routine gzwrite(). This copying is necessary because the content of the VM * segment may change between the compression pass and the crc-computation pass * in gzwrite(). This is because realtime threads may preempt the UNIX kernel. * * If inbuf is NULL it is assumed that data is already copied to 'dest_buf'. */ static int compress_core (gzFile file, char *inbuf, char *dest_buf, unsigned int len, struct thread *td) { int len_compressed; int error = 0; unsigned int chunk_len; while (len) { if (inbuf != NULL) { chunk_len = (len > CORE_BUF_SIZE) ? CORE_BUF_SIZE : len; copyin(inbuf, dest_buf, chunk_len); inbuf += chunk_len; } else { chunk_len = len; } len_compressed = gzwrite(file, dest_buf, chunk_len); EVENTHANDLER_INVOKE(app_coredump_progress, td, len_compressed); if ((unsigned int)len_compressed != chunk_len) { log(LOG_WARNING, "compress_core: length mismatch (0x%x returned, " "0x%x expected)\n", len_compressed, chunk_len); EVENTHANDLER_INVOKE(app_coredump_error, td, "compress_core: length mismatch %x -> %x", chunk_len, len_compressed); error = EFAULT; break; } len -= chunk_len; maybe_yield(); } return (error); } #endif /* COMPRESS_USER_CORES */ static vm_prot_t __elfN(trans_prot)(Elf_Word flags) { vm_prot_t prot; prot = 0; if (flags & PF_X) prot |= VM_PROT_EXECUTE; if (flags & PF_W) prot |= VM_PROT_WRITE; if (flags & PF_R) prot |= VM_PROT_READ; #if __ELF_WORD_SIZE == 32 #if defined(__amd64__) if (i386_read_exec && (flags & PF_R)) prot |= VM_PROT_EXECUTE; #endif #endif return (prot); } static Elf_Word __elfN(untrans_prot)(vm_prot_t prot) { Elf_Word flags; flags = 0; if (prot & VM_PROT_EXECUTE) flags |= PF_X; if (prot & VM_PROT_READ) flags |= PF_R; if (prot & VM_PROT_WRITE) flags |= PF_W; return (flags); } Index: projects/building-blocks/sys/kern/sysv_shm.c =================================================================== --- projects/building-blocks/sys/kern/sysv_shm.c (revision 278776) +++ projects/building-blocks/sys/kern/sysv_shm.c (revision 278777) @@ -1,1399 +1,1400 @@ /* $NetBSD: sysv_shm.c,v 1.23 1994/07/04 23:25:12 glass Exp $ */ /*- * Copyright (c) 1994 Adam Glass and Charles Hannum. 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Adam Glass and Charles * Hannum. * 4. The names of the authors may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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. */ /*- * Copyright (c) 2003-2005 McAfee, Inc. * All rights reserved. * * This software was developed for the FreeBSD Project in part by McAfee * Research, the Security Research Division of McAfee, Inc under DARPA/SPAWAR * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS research * program. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include "opt_sysvipc.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include FEATURE(sysv_shm, "System V shared memory segments support"); static MALLOC_DEFINE(M_SHM, "shm", "SVID compatible shared memory segments"); static int shmget_allocate_segment(struct thread *td, struct shmget_args *uap, int mode); static int shmget_existing(struct thread *td, struct shmget_args *uap, int mode, int segnum); #define SHMSEG_FREE 0x0200 #define SHMSEG_REMOVED 0x0400 #define SHMSEG_ALLOCATED 0x0800 #define SHMSEG_WANTED 0x1000 static int shm_last_free, shm_nused, shmalloced; vm_size_t shm_committed; static struct shmid_kernel *shmsegs; struct shmmap_state { vm_offset_t va; int shmid; }; static void shm_deallocate_segment(struct shmid_kernel *); static int shm_find_segment_by_key(key_t); static struct shmid_kernel *shm_find_segment_by_shmid(int); static struct shmid_kernel *shm_find_segment_by_shmidx(int); static int shm_delete_mapping(struct vmspace *vm, struct shmmap_state *); static void shmrealloc(void); static int shminit(void); static int sysvshm_modload(struct module *, int, void *); static int shmunload(void); static void shmexit_myhook(struct vmspace *vm); static void shmfork_myhook(struct proc *p1, struct proc *p2); static int sysctl_shmsegs(SYSCTL_HANDLER_ARGS); /* * Tuneable values. */ #ifndef SHMMAXPGS #define SHMMAXPGS 131072 /* Note: sysv shared memory is swap backed. */ #endif #ifndef SHMMAX #define SHMMAX (SHMMAXPGS*PAGE_SIZE) #endif #ifndef SHMMIN #define SHMMIN 1 #endif #ifndef SHMMNI #define SHMMNI 192 #endif #ifndef SHMSEG #define SHMSEG 128 #endif #ifndef SHMALL #define SHMALL (SHMMAXPGS) #endif struct shminfo shminfo = { .shmmax = SHMMAX, .shmmin = SHMMIN, .shmmni = SHMMNI, .shmseg = SHMSEG, .shmall = SHMALL }; static int shm_use_phys; static int shm_allow_removed; SYSCTL_ULONG(_kern_ipc, OID_AUTO, shmmax, CTLFLAG_RWTUN, &shminfo.shmmax, 0, "Maximum shared memory segment size"); SYSCTL_ULONG(_kern_ipc, OID_AUTO, shmmin, CTLFLAG_RWTUN, &shminfo.shmmin, 0, "Minimum shared memory segment size"); SYSCTL_ULONG(_kern_ipc, OID_AUTO, shmmni, CTLFLAG_RDTUN, &shminfo.shmmni, 0, "Number of shared memory identifiers"); SYSCTL_ULONG(_kern_ipc, OID_AUTO, shmseg, CTLFLAG_RDTUN, &shminfo.shmseg, 0, "Number of segments per process"); SYSCTL_ULONG(_kern_ipc, OID_AUTO, shmall, CTLFLAG_RWTUN, &shminfo.shmall, 0, "Maximum number of pages available for shared memory"); SYSCTL_INT(_kern_ipc, OID_AUTO, shm_use_phys, CTLFLAG_RWTUN, &shm_use_phys, 0, "Enable/Disable locking of shared memory pages in core"); SYSCTL_INT(_kern_ipc, OID_AUTO, shm_allow_removed, CTLFLAG_RWTUN, &shm_allow_removed, 0, "Enable/Disable attachment to attached segments marked for removal"); SYSCTL_PROC(_kern_ipc, OID_AUTO, shmsegs, CTLTYPE_OPAQUE | CTLFLAG_RD, NULL, 0, sysctl_shmsegs, "", "Current number of shared memory segments allocated"); static int shm_find_segment_by_key(key) key_t key; { int i; for (i = 0; i < shmalloced; i++) if ((shmsegs[i].u.shm_perm.mode & SHMSEG_ALLOCATED) && shmsegs[i].u.shm_perm.key == key) return (i); return (-1); } static struct shmid_kernel * shm_find_segment_by_shmid(int shmid) { int segnum; struct shmid_kernel *shmseg; segnum = IPCID_TO_IX(shmid); if (segnum < 0 || segnum >= shmalloced) return (NULL); shmseg = &shmsegs[segnum]; if ((shmseg->u.shm_perm.mode & SHMSEG_ALLOCATED) == 0 || (!shm_allow_removed && (shmseg->u.shm_perm.mode & SHMSEG_REMOVED) != 0) || shmseg->u.shm_perm.seq != IPCID_TO_SEQ(shmid)) return (NULL); return (shmseg); } static struct shmid_kernel * shm_find_segment_by_shmidx(int segnum) { struct shmid_kernel *shmseg; if (segnum < 0 || segnum >= shmalloced) return (NULL); shmseg = &shmsegs[segnum]; if ((shmseg->u.shm_perm.mode & SHMSEG_ALLOCATED) == 0 || (!shm_allow_removed && (shmseg->u.shm_perm.mode & SHMSEG_REMOVED) != 0)) return (NULL); return (shmseg); } static void shm_deallocate_segment(shmseg) struct shmid_kernel *shmseg; { vm_size_t size; GIANT_REQUIRED; vm_object_deallocate(shmseg->object); shmseg->object = NULL; size = round_page(shmseg->u.shm_segsz); shm_committed -= btoc(size); shm_nused--; shmseg->u.shm_perm.mode = SHMSEG_FREE; #ifdef MAC mac_sysvshm_cleanup(shmseg); #endif racct_sub_cred(shmseg->cred, RACCT_NSHM, 1); racct_sub_cred(shmseg->cred, RACCT_SHMSIZE, size); crfree(shmseg->cred); shmseg->cred = NULL; } static int shm_delete_mapping(struct vmspace *vm, struct shmmap_state *shmmap_s) { struct shmid_kernel *shmseg; int segnum, result; vm_size_t size; GIANT_REQUIRED; segnum = IPCID_TO_IX(shmmap_s->shmid); shmseg = &shmsegs[segnum]; size = round_page(shmseg->u.shm_segsz); result = vm_map_remove(&vm->vm_map, shmmap_s->va, shmmap_s->va + size); if (result != KERN_SUCCESS) return (EINVAL); shmmap_s->shmid = -1; shmseg->u.shm_dtime = time_second; if ((--shmseg->u.shm_nattch <= 0) && (shmseg->u.shm_perm.mode & SHMSEG_REMOVED)) { shm_deallocate_segment(shmseg); shm_last_free = segnum; } return (0); } #ifndef _SYS_SYSPROTO_H_ struct shmdt_args { const void *shmaddr; }; #endif int sys_shmdt(td, uap) struct thread *td; struct shmdt_args *uap; { struct proc *p = td->td_proc; struct shmmap_state *shmmap_s; #ifdef MAC struct shmid_kernel *shmsegptr; #endif int i; int error = 0; if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC)) return (ENOSYS); mtx_lock(&Giant); shmmap_s = p->p_vmspace->vm_shm; if (shmmap_s == NULL) { error = EINVAL; goto done2; } for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) { if (shmmap_s->shmid != -1 && shmmap_s->va == (vm_offset_t)uap->shmaddr) { break; } } if (i == shminfo.shmseg) { error = EINVAL; goto done2; } #ifdef MAC shmsegptr = &shmsegs[IPCID_TO_IX(shmmap_s->shmid)]; error = mac_sysvshm_check_shmdt(td->td_ucred, shmsegptr); if (error != 0) goto done2; #endif error = shm_delete_mapping(p->p_vmspace, shmmap_s); done2: mtx_unlock(&Giant); return (error); } #ifndef _SYS_SYSPROTO_H_ struct shmat_args { int shmid; const void *shmaddr; int shmflg; }; #endif int kern_shmat(td, shmid, shmaddr, shmflg) struct thread *td; int shmid; const void *shmaddr; int shmflg; { struct proc *p = td->td_proc; int i; struct shmid_kernel *shmseg; struct shmmap_state *shmmap_s = NULL; vm_offset_t attach_va; vm_prot_t prot; vm_size_t size; int rv; int error = 0; if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC)) return (ENOSYS); mtx_lock(&Giant); shmmap_s = p->p_vmspace->vm_shm; if (shmmap_s == NULL) { shmmap_s = malloc(shminfo.shmseg * sizeof(struct shmmap_state), M_SHM, M_WAITOK); for (i = 0; i < shminfo.shmseg; i++) shmmap_s[i].shmid = -1; p->p_vmspace->vm_shm = shmmap_s; } shmseg = shm_find_segment_by_shmid(shmid); if (shmseg == NULL) { error = EINVAL; goto done2; } error = ipcperm(td, &shmseg->u.shm_perm, (shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W); if (error) goto done2; #ifdef MAC error = mac_sysvshm_check_shmat(td->td_ucred, shmseg, shmflg); if (error != 0) goto done2; #endif for (i = 0; i < shminfo.shmseg; i++) { if (shmmap_s->shmid == -1) break; shmmap_s++; } if (i >= shminfo.shmseg) { error = EMFILE; goto done2; } size = round_page(shmseg->u.shm_segsz); prot = VM_PROT_READ; if ((shmflg & SHM_RDONLY) == 0) prot |= VM_PROT_WRITE; if (shmaddr) { if (shmflg & SHM_RND) { attach_va = (vm_offset_t)shmaddr & ~(SHMLBA-1); } else if (((vm_offset_t)shmaddr & (SHMLBA-1)) == 0) { attach_va = (vm_offset_t)shmaddr; } else { error = EINVAL; goto done2; } } else { /* * This is just a hint to vm_map_find() about where to * put it. */ PROC_LOCK(p); attach_va = round_page((vm_offset_t)p->p_vmspace->vm_daddr + lim_max(p, RLIMIT_DATA)); PROC_UNLOCK(p); } vm_object_reference(shmseg->object); rv = vm_map_find(&p->p_vmspace->vm_map, shmseg->object, 0, &attach_va, size, 0, shmaddr != NULL ? VMFS_NO_SPACE : VMFS_OPTIMAL_SPACE, prot, prot, MAP_INHERIT_SHARE | MAP_PREFAULT_PARTIAL); if (rv != KERN_SUCCESS) { vm_object_deallocate(shmseg->object); error = ENOMEM; goto done2; } shmmap_s->va = attach_va; shmmap_s->shmid = shmid; shmseg->u.shm_lpid = p->p_pid; shmseg->u.shm_atime = time_second; shmseg->u.shm_nattch++; td->td_retval[0] = attach_va; done2: mtx_unlock(&Giant); return (error); } int sys_shmat(td, uap) struct thread *td; struct shmat_args *uap; { return kern_shmat(td, uap->shmid, uap->shmaddr, uap->shmflg); } int kern_shmctl(td, shmid, cmd, buf, bufsz) struct thread *td; int shmid; int cmd; void *buf; size_t *bufsz; { int error = 0; struct shmid_kernel *shmseg; if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC)) return (ENOSYS); mtx_lock(&Giant); switch (cmd) { /* * It is possible that kern_shmctl is being called from the Linux ABI * layer, in which case, we will need to implement IPC_INFO. It should * be noted that other shmctl calls will be funneled through here for * Linix binaries as well. * * NB: The Linux ABI layer will convert this data to structure(s) more * consistent with the Linux ABI. */ case IPC_INFO: memcpy(buf, &shminfo, sizeof(shminfo)); if (bufsz) *bufsz = sizeof(shminfo); td->td_retval[0] = shmalloced; goto done2; case SHM_INFO: { struct shm_info shm_info; shm_info.used_ids = shm_nused; shm_info.shm_rss = 0; /*XXX where to get from ? */ shm_info.shm_tot = 0; /*XXX where to get from ? */ shm_info.shm_swp = 0; /*XXX where to get from ? */ shm_info.swap_attempts = 0; /*XXX where to get from ? */ shm_info.swap_successes = 0; /*XXX where to get from ? */ memcpy(buf, &shm_info, sizeof(shm_info)); if (bufsz) *bufsz = sizeof(shm_info); td->td_retval[0] = shmalloced; goto done2; } } if (cmd == SHM_STAT) shmseg = shm_find_segment_by_shmidx(shmid); else shmseg = shm_find_segment_by_shmid(shmid); if (shmseg == NULL) { error = EINVAL; goto done2; } #ifdef MAC error = mac_sysvshm_check_shmctl(td->td_ucred, shmseg, cmd); if (error != 0) goto done2; #endif switch (cmd) { case SHM_STAT: case IPC_STAT: error = ipcperm(td, &shmseg->u.shm_perm, IPC_R); if (error) goto done2; memcpy(buf, &shmseg->u, sizeof(struct shmid_ds)); if (bufsz) *bufsz = sizeof(struct shmid_ds); if (cmd == SHM_STAT) td->td_retval[0] = IXSEQ_TO_IPCID(shmid, shmseg->u.shm_perm); break; case IPC_SET: { struct shmid_ds *shmid; shmid = (struct shmid_ds *)buf; error = ipcperm(td, &shmseg->u.shm_perm, IPC_M); if (error) goto done2; shmseg->u.shm_perm.uid = shmid->shm_perm.uid; shmseg->u.shm_perm.gid = shmid->shm_perm.gid; shmseg->u.shm_perm.mode = (shmseg->u.shm_perm.mode & ~ACCESSPERMS) | (shmid->shm_perm.mode & ACCESSPERMS); shmseg->u.shm_ctime = time_second; break; } case IPC_RMID: error = ipcperm(td, &shmseg->u.shm_perm, IPC_M); if (error) goto done2; shmseg->u.shm_perm.key = IPC_PRIVATE; shmseg->u.shm_perm.mode |= SHMSEG_REMOVED; if (shmseg->u.shm_nattch <= 0) { shm_deallocate_segment(shmseg); shm_last_free = IPCID_TO_IX(shmid); } break; #if 0 case SHM_LOCK: case SHM_UNLOCK: #endif default: error = EINVAL; break; } done2: mtx_unlock(&Giant); return (error); } #ifndef _SYS_SYSPROTO_H_ struct shmctl_args { int shmid; int cmd; struct shmid_ds *buf; }; #endif int sys_shmctl(td, uap) struct thread *td; struct shmctl_args *uap; { int error = 0; struct shmid_ds buf; size_t bufsz; /* * The only reason IPC_INFO, SHM_INFO, SHM_STAT exists is to support * Linux binaries. If we see the call come through the FreeBSD ABI, * return an error back to the user since we do not to support this. */ if (uap->cmd == IPC_INFO || uap->cmd == SHM_INFO || uap->cmd == SHM_STAT) return (EINVAL); /* IPC_SET needs to copyin the buffer before calling kern_shmctl */ if (uap->cmd == IPC_SET) { if ((error = copyin(uap->buf, &buf, sizeof(struct shmid_ds)))) goto done; } error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&buf, &bufsz); if (error) goto done; /* Cases in which we need to copyout */ switch (uap->cmd) { case IPC_STAT: error = copyout(&buf, uap->buf, bufsz); break; } done: if (error) { /* Invalidate the return value */ td->td_retval[0] = -1; } return (error); } static int shmget_existing(td, uap, mode, segnum) struct thread *td; struct shmget_args *uap; int mode; int segnum; { struct shmid_kernel *shmseg; int error; shmseg = &shmsegs[segnum]; if (shmseg->u.shm_perm.mode & SHMSEG_REMOVED) { /* * This segment is in the process of being allocated. Wait * until it's done, and look the key up again (in case the * allocation failed or it was freed). */ shmseg->u.shm_perm.mode |= SHMSEG_WANTED; error = tsleep(shmseg, PLOCK | PCATCH, "shmget", 0); if (error) return (error); return (EAGAIN); } if ((uap->shmflg & (IPC_CREAT | IPC_EXCL)) == (IPC_CREAT | IPC_EXCL)) return (EEXIST); #ifdef MAC error = mac_sysvshm_check_shmget(td->td_ucred, shmseg, uap->shmflg); if (error != 0) return (error); #endif if (uap->size != 0 && uap->size > shmseg->u.shm_segsz) return (EINVAL); td->td_retval[0] = IXSEQ_TO_IPCID(segnum, shmseg->u.shm_perm); return (0); } static int shmget_allocate_segment(td, uap, mode) struct thread *td; struct shmget_args *uap; int mode; { int i, segnum, shmid; size_t size; struct ucred *cred = td->td_ucred; struct shmid_kernel *shmseg; vm_object_t shm_object; GIANT_REQUIRED; if (uap->size < shminfo.shmmin || uap->size > shminfo.shmmax) return (EINVAL); if (shm_nused >= shminfo.shmmni) /* Any shmids left? */ return (ENOSPC); size = round_page(uap->size); if (shm_committed + btoc(size) > shminfo.shmall) return (ENOMEM); if (shm_last_free < 0) { shmrealloc(); /* Maybe expand the shmsegs[] array. */ for (i = 0; i < shmalloced; i++) if (shmsegs[i].u.shm_perm.mode & SHMSEG_FREE) break; if (i == shmalloced) return (ENOSPC); segnum = i; } else { segnum = shm_last_free; shm_last_free = -1; } shmseg = &shmsegs[segnum]; #ifdef RACCT PROC_LOCK(td->td_proc); if (racct_add(td->td_proc, RACCT_NSHM, 1)) { PROC_UNLOCK(td->td_proc); return (ENOSPC); } if (racct_add(td->td_proc, RACCT_SHMSIZE, size)) { racct_sub(td->td_proc, RACCT_NSHM, 1); PROC_UNLOCK(td->td_proc); return (ENOMEM); } PROC_UNLOCK(td->td_proc); #endif /* * In case we sleep in malloc(), mark the segment present but deleted * so that noone else tries to create the same key. */ shmseg->u.shm_perm.mode = SHMSEG_ALLOCATED | SHMSEG_REMOVED; shmseg->u.shm_perm.key = uap->key; shmseg->u.shm_perm.seq = (shmseg->u.shm_perm.seq + 1) & 0x7fff; shmid = IXSEQ_TO_IPCID(segnum, shmseg->u.shm_perm); /* * We make sure that we have allocated a pager before we need * to. */ shm_object = vm_pager_allocate(shm_use_phys ? OBJT_PHYS : OBJT_SWAP, 0, size, VM_PROT_DEFAULT, 0, cred); if (shm_object == NULL) { #ifdef RACCT PROC_LOCK(td->td_proc); racct_sub(td->td_proc, RACCT_NSHM, 1); racct_sub(td->td_proc, RACCT_SHMSIZE, size); PROC_UNLOCK(td->td_proc); #endif return (ENOMEM); } + shm_object->pg_color = 0; VM_OBJECT_WLOCK(shm_object); vm_object_clear_flag(shm_object, OBJ_ONEMAPPING); - vm_object_set_flag(shm_object, OBJ_NOSPLIT); + vm_object_set_flag(shm_object, OBJ_COLORED | OBJ_NOSPLIT); VM_OBJECT_WUNLOCK(shm_object); shmseg->object = shm_object; shmseg->u.shm_perm.cuid = shmseg->u.shm_perm.uid = cred->cr_uid; shmseg->u.shm_perm.cgid = shmseg->u.shm_perm.gid = cred->cr_gid; shmseg->u.shm_perm.mode = (shmseg->u.shm_perm.mode & SHMSEG_WANTED) | (mode & ACCESSPERMS) | SHMSEG_ALLOCATED; shmseg->cred = crhold(cred); shmseg->u.shm_segsz = uap->size; shmseg->u.shm_cpid = td->td_proc->p_pid; shmseg->u.shm_lpid = shmseg->u.shm_nattch = 0; shmseg->u.shm_atime = shmseg->u.shm_dtime = 0; #ifdef MAC mac_sysvshm_create(cred, shmseg); #endif shmseg->u.shm_ctime = time_second; shm_committed += btoc(size); shm_nused++; if (shmseg->u.shm_perm.mode & SHMSEG_WANTED) { /* * Somebody else wanted this key while we were asleep. Wake * them up now. */ shmseg->u.shm_perm.mode &= ~SHMSEG_WANTED; wakeup(shmseg); } td->td_retval[0] = shmid; return (0); } #ifndef _SYS_SYSPROTO_H_ struct shmget_args { key_t key; size_t size; int shmflg; }; #endif int sys_shmget(td, uap) struct thread *td; struct shmget_args *uap; { int segnum, mode; int error; if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC)) return (ENOSYS); mtx_lock(&Giant); mode = uap->shmflg & ACCESSPERMS; if (uap->key != IPC_PRIVATE) { again: segnum = shm_find_segment_by_key(uap->key); if (segnum >= 0) { error = shmget_existing(td, uap, mode, segnum); if (error == EAGAIN) goto again; goto done2; } if ((uap->shmflg & IPC_CREAT) == 0) { error = ENOENT; goto done2; } } error = shmget_allocate_segment(td, uap, mode); done2: mtx_unlock(&Giant); return (error); } static void shmfork_myhook(p1, p2) struct proc *p1, *p2; { struct shmmap_state *shmmap_s; size_t size; int i; mtx_lock(&Giant); size = shminfo.shmseg * sizeof(struct shmmap_state); shmmap_s = malloc(size, M_SHM, M_WAITOK); bcopy(p1->p_vmspace->vm_shm, shmmap_s, size); p2->p_vmspace->vm_shm = shmmap_s; for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) if (shmmap_s->shmid != -1) shmsegs[IPCID_TO_IX(shmmap_s->shmid)].u.shm_nattch++; mtx_unlock(&Giant); } static void shmexit_myhook(struct vmspace *vm) { struct shmmap_state *base, *shm; int i; if ((base = vm->vm_shm) != NULL) { vm->vm_shm = NULL; mtx_lock(&Giant); for (i = 0, shm = base; i < shminfo.shmseg; i++, shm++) { if (shm->shmid != -1) shm_delete_mapping(vm, shm); } mtx_unlock(&Giant); free(base, M_SHM); } } static void shmrealloc(void) { int i; struct shmid_kernel *newsegs; if (shmalloced >= shminfo.shmmni) return; newsegs = malloc(shminfo.shmmni * sizeof(*newsegs), M_SHM, M_WAITOK); if (newsegs == NULL) return; for (i = 0; i < shmalloced; i++) bcopy(&shmsegs[i], &newsegs[i], sizeof(newsegs[0])); for (; i < shminfo.shmmni; i++) { shmsegs[i].u.shm_perm.mode = SHMSEG_FREE; shmsegs[i].u.shm_perm.seq = 0; #ifdef MAC mac_sysvshm_init(&shmsegs[i]); #endif } free(shmsegs, M_SHM); shmsegs = newsegs; shmalloced = shminfo.shmmni; } static struct syscall_helper_data shm_syscalls[] = { SYSCALL_INIT_HELPER(shmat), SYSCALL_INIT_HELPER(shmctl), SYSCALL_INIT_HELPER(shmdt), SYSCALL_INIT_HELPER(shmget), #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7) SYSCALL_INIT_HELPER_COMPAT(freebsd7_shmctl), #endif #if defined(__i386__) && (defined(COMPAT_FREEBSD4) || defined(COMPAT_43)) SYSCALL_INIT_HELPER(shmsys), #endif SYSCALL_INIT_LAST }; #ifdef COMPAT_FREEBSD32 #include #include #include #include #include #include static struct syscall_helper_data shm32_syscalls[] = { SYSCALL32_INIT_HELPER_COMPAT(shmat), SYSCALL32_INIT_HELPER_COMPAT(shmdt), SYSCALL32_INIT_HELPER_COMPAT(shmget), SYSCALL32_INIT_HELPER(freebsd32_shmsys), SYSCALL32_INIT_HELPER(freebsd32_shmctl), #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7) SYSCALL32_INIT_HELPER(freebsd7_freebsd32_shmctl), #endif SYSCALL_INIT_LAST }; #endif static int shminit() { int i, error; #ifndef BURN_BRIDGES if (TUNABLE_ULONG_FETCH("kern.ipc.shmmaxpgs", &shminfo.shmall) != 0) printf("kern.ipc.shmmaxpgs is now called kern.ipc.shmall!\n"); #endif if (shminfo.shmmax == SHMMAX) { /* Initialize shmmax dealing with possible overflow. */ for (i = PAGE_SIZE; i != 0; i--) { shminfo.shmmax = shminfo.shmall * i; if ((shminfo.shmmax / shminfo.shmall) == (u_long)i) break; } } shmalloced = shminfo.shmmni; shmsegs = malloc(shmalloced * sizeof(shmsegs[0]), M_SHM, M_WAITOK); for (i = 0; i < shmalloced; i++) { shmsegs[i].u.shm_perm.mode = SHMSEG_FREE; shmsegs[i].u.shm_perm.seq = 0; #ifdef MAC mac_sysvshm_init(&shmsegs[i]); #endif } shm_last_free = 0; shm_nused = 0; shm_committed = 0; shmexit_hook = &shmexit_myhook; shmfork_hook = &shmfork_myhook; error = syscall_helper_register(shm_syscalls, SY_THR_STATIC_KLD); if (error != 0) return (error); #ifdef COMPAT_FREEBSD32 error = syscall32_helper_register(shm32_syscalls, SY_THR_STATIC_KLD); if (error != 0) return (error); #endif return (0); } static int shmunload() { int i; if (shm_nused > 0) return (EBUSY); #ifdef COMPAT_FREEBSD32 syscall32_helper_unregister(shm32_syscalls); #endif syscall_helper_unregister(shm_syscalls); for (i = 0; i < shmalloced; i++) { #ifdef MAC mac_sysvshm_destroy(&shmsegs[i]); #endif /* * Objects might be still mapped into the processes * address spaces. Actual free would happen on the * last mapping destruction. */ if (shmsegs[i].u.shm_perm.mode != SHMSEG_FREE) vm_object_deallocate(shmsegs[i].object); } free(shmsegs, M_SHM); shmexit_hook = NULL; shmfork_hook = NULL; return (0); } static int sysctl_shmsegs(SYSCTL_HANDLER_ARGS) { return (SYSCTL_OUT(req, shmsegs, shmalloced * sizeof(shmsegs[0]))); } #if defined(__i386__) && (defined(COMPAT_FREEBSD4) || defined(COMPAT_43)) struct oshmid_ds { struct ipc_perm_old shm_perm; /* operation perms */ int shm_segsz; /* size of segment (bytes) */ u_short shm_cpid; /* pid, creator */ u_short shm_lpid; /* pid, last operation */ short shm_nattch; /* no. of current attaches */ time_t shm_atime; /* last attach time */ time_t shm_dtime; /* last detach time */ time_t shm_ctime; /* last change time */ void *shm_handle; /* internal handle for shm segment */ }; struct oshmctl_args { int shmid; int cmd; struct oshmid_ds *ubuf; }; static int oshmctl(struct thread *td, struct oshmctl_args *uap) { #ifdef COMPAT_43 int error = 0; struct shmid_kernel *shmseg; struct oshmid_ds outbuf; if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC)) return (ENOSYS); mtx_lock(&Giant); shmseg = shm_find_segment_by_shmid(uap->shmid); if (shmseg == NULL) { error = EINVAL; goto done2; } switch (uap->cmd) { case IPC_STAT: error = ipcperm(td, &shmseg->u.shm_perm, IPC_R); if (error) goto done2; #ifdef MAC error = mac_sysvshm_check_shmctl(td->td_ucred, shmseg, uap->cmd); if (error != 0) goto done2; #endif ipcperm_new2old(&shmseg->u.shm_perm, &outbuf.shm_perm); outbuf.shm_segsz = shmseg->u.shm_segsz; outbuf.shm_cpid = shmseg->u.shm_cpid; outbuf.shm_lpid = shmseg->u.shm_lpid; outbuf.shm_nattch = shmseg->u.shm_nattch; outbuf.shm_atime = shmseg->u.shm_atime; outbuf.shm_dtime = shmseg->u.shm_dtime; outbuf.shm_ctime = shmseg->u.shm_ctime; outbuf.shm_handle = shmseg->object; error = copyout(&outbuf, uap->ubuf, sizeof(outbuf)); if (error) goto done2; break; default: error = freebsd7_shmctl(td, (struct freebsd7_shmctl_args *)uap); break; } done2: mtx_unlock(&Giant); return (error); #else return (EINVAL); #endif } /* XXX casting to (sy_call_t *) is bogus, as usual. */ static sy_call_t *shmcalls[] = { (sy_call_t *)sys_shmat, (sy_call_t *)oshmctl, (sy_call_t *)sys_shmdt, (sy_call_t *)sys_shmget, (sy_call_t *)freebsd7_shmctl }; int sys_shmsys(td, uap) struct thread *td; /* XXX actually varargs. */ struct shmsys_args /* { int which; int a2; int a3; int a4; } */ *uap; { int error; if (!prison_allow(td->td_ucred, PR_ALLOW_SYSVIPC)) return (ENOSYS); if (uap->which < 0 || uap->which >= sizeof(shmcalls)/sizeof(shmcalls[0])) return (EINVAL); mtx_lock(&Giant); error = (*shmcalls[uap->which])(td, &uap->a2); mtx_unlock(&Giant); return (error); } #endif /* i386 && (COMPAT_FREEBSD4 || COMPAT_43) */ #ifdef COMPAT_FREEBSD32 int freebsd32_shmsys(struct thread *td, struct freebsd32_shmsys_args *uap) { #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7) switch (uap->which) { case 0: { /* shmat */ struct shmat_args ap; ap.shmid = uap->a2; ap.shmaddr = PTRIN(uap->a3); ap.shmflg = uap->a4; return (sysent[SYS_shmat].sy_call(td, &ap)); } case 2: { /* shmdt */ struct shmdt_args ap; ap.shmaddr = PTRIN(uap->a2); return (sysent[SYS_shmdt].sy_call(td, &ap)); } case 3: { /* shmget */ struct shmget_args ap; ap.key = uap->a2; ap.size = uap->a3; ap.shmflg = uap->a4; return (sysent[SYS_shmget].sy_call(td, &ap)); } case 4: { /* shmctl */ struct freebsd7_freebsd32_shmctl_args ap; ap.shmid = uap->a2; ap.cmd = uap->a3; ap.buf = PTRIN(uap->a4); return (freebsd7_freebsd32_shmctl(td, &ap)); } case 1: /* oshmctl */ default: return (EINVAL); } #else return (nosys(td, NULL)); #endif } #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7) int freebsd7_freebsd32_shmctl(struct thread *td, struct freebsd7_freebsd32_shmctl_args *uap) { int error = 0; union { struct shmid_ds shmid_ds; struct shm_info shm_info; struct shminfo shminfo; } u; union { struct shmid_ds32_old shmid_ds32; struct shm_info32 shm_info32; struct shminfo32 shminfo32; } u32; size_t sz; if (uap->cmd == IPC_SET) { if ((error = copyin(uap->buf, &u32.shmid_ds32, sizeof(u32.shmid_ds32)))) goto done; freebsd32_ipcperm_old_in(&u32.shmid_ds32.shm_perm, &u.shmid_ds.shm_perm); CP(u32.shmid_ds32, u.shmid_ds, shm_segsz); CP(u32.shmid_ds32, u.shmid_ds, shm_lpid); CP(u32.shmid_ds32, u.shmid_ds, shm_cpid); CP(u32.shmid_ds32, u.shmid_ds, shm_nattch); CP(u32.shmid_ds32, u.shmid_ds, shm_atime); CP(u32.shmid_ds32, u.shmid_ds, shm_dtime); CP(u32.shmid_ds32, u.shmid_ds, shm_ctime); } error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&u, &sz); if (error) goto done; /* Cases in which we need to copyout */ switch (uap->cmd) { case IPC_INFO: CP(u.shminfo, u32.shminfo32, shmmax); CP(u.shminfo, u32.shminfo32, shmmin); CP(u.shminfo, u32.shminfo32, shmmni); CP(u.shminfo, u32.shminfo32, shmseg); CP(u.shminfo, u32.shminfo32, shmall); error = copyout(&u32.shminfo32, uap->buf, sizeof(u32.shminfo32)); break; case SHM_INFO: CP(u.shm_info, u32.shm_info32, used_ids); CP(u.shm_info, u32.shm_info32, shm_rss); CP(u.shm_info, u32.shm_info32, shm_tot); CP(u.shm_info, u32.shm_info32, shm_swp); CP(u.shm_info, u32.shm_info32, swap_attempts); CP(u.shm_info, u32.shm_info32, swap_successes); error = copyout(&u32.shm_info32, uap->buf, sizeof(u32.shm_info32)); break; case SHM_STAT: case IPC_STAT: freebsd32_ipcperm_old_out(&u.shmid_ds.shm_perm, &u32.shmid_ds32.shm_perm); if (u.shmid_ds.shm_segsz > INT32_MAX) u32.shmid_ds32.shm_segsz = INT32_MAX; else CP(u.shmid_ds, u32.shmid_ds32, shm_segsz); CP(u.shmid_ds, u32.shmid_ds32, shm_lpid); CP(u.shmid_ds, u32.shmid_ds32, shm_cpid); CP(u.shmid_ds, u32.shmid_ds32, shm_nattch); CP(u.shmid_ds, u32.shmid_ds32, shm_atime); CP(u.shmid_ds, u32.shmid_ds32, shm_dtime); CP(u.shmid_ds, u32.shmid_ds32, shm_ctime); u32.shmid_ds32.shm_internal = 0; error = copyout(&u32.shmid_ds32, uap->buf, sizeof(u32.shmid_ds32)); break; } done: if (error) { /* Invalidate the return value */ td->td_retval[0] = -1; } return (error); } #endif int freebsd32_shmctl(struct thread *td, struct freebsd32_shmctl_args *uap) { int error = 0; union { struct shmid_ds shmid_ds; struct shm_info shm_info; struct shminfo shminfo; } u; union { struct shmid_ds32 shmid_ds32; struct shm_info32 shm_info32; struct shminfo32 shminfo32; } u32; size_t sz; if (uap->cmd == IPC_SET) { if ((error = copyin(uap->buf, &u32.shmid_ds32, sizeof(u32.shmid_ds32)))) goto done; freebsd32_ipcperm_in(&u32.shmid_ds32.shm_perm, &u.shmid_ds.shm_perm); CP(u32.shmid_ds32, u.shmid_ds, shm_segsz); CP(u32.shmid_ds32, u.shmid_ds, shm_lpid); CP(u32.shmid_ds32, u.shmid_ds, shm_cpid); CP(u32.shmid_ds32, u.shmid_ds, shm_nattch); CP(u32.shmid_ds32, u.shmid_ds, shm_atime); CP(u32.shmid_ds32, u.shmid_ds, shm_dtime); CP(u32.shmid_ds32, u.shmid_ds, shm_ctime); } error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&u, &sz); if (error) goto done; /* Cases in which we need to copyout */ switch (uap->cmd) { case IPC_INFO: CP(u.shminfo, u32.shminfo32, shmmax); CP(u.shminfo, u32.shminfo32, shmmin); CP(u.shminfo, u32.shminfo32, shmmni); CP(u.shminfo, u32.shminfo32, shmseg); CP(u.shminfo, u32.shminfo32, shmall); error = copyout(&u32.shminfo32, uap->buf, sizeof(u32.shminfo32)); break; case SHM_INFO: CP(u.shm_info, u32.shm_info32, used_ids); CP(u.shm_info, u32.shm_info32, shm_rss); CP(u.shm_info, u32.shm_info32, shm_tot); CP(u.shm_info, u32.shm_info32, shm_swp); CP(u.shm_info, u32.shm_info32, swap_attempts); CP(u.shm_info, u32.shm_info32, swap_successes); error = copyout(&u32.shm_info32, uap->buf, sizeof(u32.shm_info32)); break; case SHM_STAT: case IPC_STAT: freebsd32_ipcperm_out(&u.shmid_ds.shm_perm, &u32.shmid_ds32.shm_perm); if (u.shmid_ds.shm_segsz > INT32_MAX) u32.shmid_ds32.shm_segsz = INT32_MAX; else CP(u.shmid_ds, u32.shmid_ds32, shm_segsz); CP(u.shmid_ds, u32.shmid_ds32, shm_lpid); CP(u.shmid_ds, u32.shmid_ds32, shm_cpid); CP(u.shmid_ds, u32.shmid_ds32, shm_nattch); CP(u.shmid_ds, u32.shmid_ds32, shm_atime); CP(u.shmid_ds, u32.shmid_ds32, shm_dtime); CP(u.shmid_ds, u32.shmid_ds32, shm_ctime); error = copyout(&u32.shmid_ds32, uap->buf, sizeof(u32.shmid_ds32)); break; } done: if (error) { /* Invalidate the return value */ td->td_retval[0] = -1; } return (error); } #endif #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7) #ifndef CP #define CP(src, dst, fld) do { (dst).fld = (src).fld; } while (0) #endif #ifndef _SYS_SYSPROTO_H_ struct freebsd7_shmctl_args { int shmid; int cmd; struct shmid_ds_old *buf; }; #endif int freebsd7_shmctl(td, uap) struct thread *td; struct freebsd7_shmctl_args *uap; { int error = 0; struct shmid_ds_old old; struct shmid_ds buf; size_t bufsz; /* * The only reason IPC_INFO, SHM_INFO, SHM_STAT exists is to support * Linux binaries. If we see the call come through the FreeBSD ABI, * return an error back to the user since we do not to support this. */ if (uap->cmd == IPC_INFO || uap->cmd == SHM_INFO || uap->cmd == SHM_STAT) return (EINVAL); /* IPC_SET needs to copyin the buffer before calling kern_shmctl */ if (uap->cmd == IPC_SET) { if ((error = copyin(uap->buf, &old, sizeof(old)))) goto done; ipcperm_old2new(&old.shm_perm, &buf.shm_perm); CP(old, buf, shm_segsz); CP(old, buf, shm_lpid); CP(old, buf, shm_cpid); CP(old, buf, shm_nattch); CP(old, buf, shm_atime); CP(old, buf, shm_dtime); CP(old, buf, shm_ctime); } error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&buf, &bufsz); if (error) goto done; /* Cases in which we need to copyout */ switch (uap->cmd) { case IPC_STAT: ipcperm_new2old(&buf.shm_perm, &old.shm_perm); if (buf.shm_segsz > INT_MAX) old.shm_segsz = INT_MAX; else CP(buf, old, shm_segsz); CP(buf, old, shm_lpid); CP(buf, old, shm_cpid); if (buf.shm_nattch > SHRT_MAX) old.shm_nattch = SHRT_MAX; else CP(buf, old, shm_nattch); CP(buf, old, shm_atime); CP(buf, old, shm_dtime); CP(buf, old, shm_ctime); old.shm_internal = NULL; error = copyout(&old, uap->buf, sizeof(old)); break; } done: if (error) { /* Invalidate the return value */ td->td_retval[0] = -1; } return (error); } #endif /* COMPAT_FREEBSD4 || COMPAT_FREEBSD5 || COMPAT_FREEBSD6 || COMPAT_FREEBSD7 */ static int sysvshm_modload(struct module *module, int cmd, void *arg) { int error = 0; switch (cmd) { case MOD_LOAD: error = shminit(); if (error != 0) shmunload(); break; case MOD_UNLOAD: error = shmunload(); break; case MOD_SHUTDOWN: break; default: error = EINVAL; break; } return (error); } static moduledata_t sysvshm_mod = { "sysvshm", &sysvshm_modload, NULL }; DECLARE_MODULE(sysvshm, sysvshm_mod, SI_SUB_SYSV_SHM, SI_ORDER_FIRST); MODULE_VERSION(sysvshm, 1); Index: projects/building-blocks/sys/kern/vfs_subr.c =================================================================== --- projects/building-blocks/sys/kern/vfs_subr.c (revision 278776) +++ projects/building-blocks/sys/kern/vfs_subr.c (revision 278777) @@ -1,4878 +1,4890 @@ /*- * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * 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. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)vfs_subr.c 8.31 (Berkeley) 5/26/95 */ /* * External virtual filesystem routines */ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include "opt_ddb.h" #include "opt_watchdog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DDB #include #endif static void delmntque(struct vnode *vp); static int flushbuflist(struct bufv *bufv, int flags, struct bufobj *bo, int slpflag, int slptimeo); static void syncer_shutdown(void *arg, int howto); static int vtryrecycle(struct vnode *vp); static void v_incr_usecount(struct vnode *); static void v_decr_usecount(struct vnode *); static void v_decr_useonly(struct vnode *); static void v_upgrade_usecount(struct vnode *); static void vnlru_free(int); static void vgonel(struct vnode *); static void vfs_knllock(void *arg); static void vfs_knlunlock(void *arg); static void vfs_knl_assert_locked(void *arg); static void vfs_knl_assert_unlocked(void *arg); static void destroy_vpollinfo(struct vpollinfo *vi); /* * Number of vnodes in existence. Increased whenever getnewvnode() * allocates a new vnode, decreased in vdropl() for VI_DOOMED vnode. */ static unsigned long numvnodes; SYSCTL_ULONG(_vfs, OID_AUTO, numvnodes, CTLFLAG_RD, &numvnodes, 0, "Number of vnodes in existence"); +static u_long vnodes_created; +SYSCTL_ULONG(_vfs, OID_AUTO, vnodes_created, CTLFLAG_RD, &vnodes_created, + 0, "Number of vnodes created by getnewvnode"); + /* * Conversion tables for conversion from vnode types to inode formats * and back. */ enum vtype iftovt_tab[16] = { VNON, VFIFO, VCHR, VNON, VDIR, VNON, VBLK, VNON, VREG, VNON, VLNK, VNON, VSOCK, VNON, VNON, VBAD, }; int vttoif_tab[10] = { 0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFSOCK, S_IFIFO, S_IFMT, S_IFMT }; /* * List of vnodes that are ready for recycling. */ static TAILQ_HEAD(freelst, vnode) vnode_free_list; /* * Free vnode target. Free vnodes may simply be files which have been stat'd * but not read. This is somewhat common, and a small cache of such files * should be kept to avoid recreation costs. */ static u_long wantfreevnodes; SYSCTL_ULONG(_vfs, OID_AUTO, wantfreevnodes, CTLFLAG_RW, &wantfreevnodes, 0, ""); /* Number of vnodes in the free list. */ static u_long freevnodes; SYSCTL_ULONG(_vfs, OID_AUTO, freevnodes, CTLFLAG_RD, &freevnodes, 0, "Number of vnodes in the free list"); static int vlru_allow_cache_src; SYSCTL_INT(_vfs, OID_AUTO, vlru_allow_cache_src, CTLFLAG_RW, &vlru_allow_cache_src, 0, "Allow vlru to reclaim source vnode"); +static u_long recycles_count; +SYSCTL_ULONG(_vfs, OID_AUTO, recycles, CTLFLAG_RD, &recycles_count, 0, + "Number of vnodes recycled to avoid exceding kern.maxvnodes"); + /* * Various variables used for debugging the new implementation of * reassignbuf(). * XXX these are probably of (very) limited utility now. */ static int reassignbufcalls; SYSCTL_INT(_vfs, OID_AUTO, reassignbufcalls, CTLFLAG_RW, &reassignbufcalls, 0, "Number of calls to reassignbuf"); /* * Cache for the mount type id assigned to NFS. This is used for * special checks in nfs/nfs_nqlease.c and vm/vnode_pager.c. */ int nfs_mount_type = -1; /* To keep more than one thread at a time from running vfs_getnewfsid */ static struct mtx mntid_mtx; /* * Lock for any access to the following: * vnode_free_list * numvnodes * freevnodes */ static struct mtx vnode_free_list_mtx; /* Publicly exported FS */ struct nfs_public nfs_pub; static uma_zone_t buf_trie_zone; /* Zone for allocation of new vnodes - used exclusively by getnewvnode() */ static uma_zone_t vnode_zone; static uma_zone_t vnodepoll_zone; /* * The workitem queue. * * It is useful to delay writes of file data and filesystem metadata * for tens of seconds so that quickly created and deleted files need * not waste disk bandwidth being created and removed. To realize this, * we append vnodes to a "workitem" queue. When running with a soft * updates implementation, most pending metadata dependencies should * not wait for more than a few seconds. Thus, mounted on block devices * are delayed only about a half the time that file data is delayed. * Similarly, directory updates are more critical, so are only delayed * about a third the time that file data is delayed. Thus, there are * SYNCER_MAXDELAY queues that are processed round-robin at a rate of * one each second (driven off the filesystem syncer process). The * syncer_delayno variable indicates the next queue that is to be processed. * Items that need to be processed soon are placed in this queue: * * syncer_workitem_pending[syncer_delayno] * * A delay of fifteen seconds is done by placing the request fifteen * entries later in the queue: * * syncer_workitem_pending[(syncer_delayno + 15) & syncer_mask] * */ static int syncer_delayno; static long syncer_mask; LIST_HEAD(synclist, bufobj); static struct synclist *syncer_workitem_pending; /* * The sync_mtx protects: * bo->bo_synclist * sync_vnode_count * syncer_delayno * syncer_state * syncer_workitem_pending * syncer_worklist_len * rushjob */ static struct mtx sync_mtx; static struct cv sync_wakeup; #define SYNCER_MAXDELAY 32 static int syncer_maxdelay = SYNCER_MAXDELAY; /* maximum delay time */ static int syncdelay = 30; /* max time to delay syncing data */ static int filedelay = 30; /* time to delay syncing files */ SYSCTL_INT(_kern, OID_AUTO, filedelay, CTLFLAG_RW, &filedelay, 0, "Time to delay syncing files (in seconds)"); static int dirdelay = 29; /* time to delay syncing directories */ SYSCTL_INT(_kern, OID_AUTO, dirdelay, CTLFLAG_RW, &dirdelay, 0, "Time to delay syncing directories (in seconds)"); static int metadelay = 28; /* time to delay syncing metadata */ SYSCTL_INT(_kern, OID_AUTO, metadelay, CTLFLAG_RW, &metadelay, 0, "Time to delay syncing metadata (in seconds)"); static int rushjob; /* number of slots to run ASAP */ static int stat_rush_requests; /* number of times I/O speeded up */ SYSCTL_INT(_debug, OID_AUTO, rush_requests, CTLFLAG_RW, &stat_rush_requests, 0, "Number of times I/O speeded up (rush requests)"); /* * When shutting down the syncer, run it at four times normal speed. */ #define SYNCER_SHUTDOWN_SPEEDUP 4 static int sync_vnode_count; static int syncer_worklist_len; static enum { SYNCER_RUNNING, SYNCER_SHUTTING_DOWN, SYNCER_FINAL_DELAY } syncer_state; /* * Number of vnodes we want to exist at any one time. This is mostly used * to size hash tables in vnode-related code. It is normally not used in * getnewvnode(), as wantfreevnodes is normally nonzero.) * * XXX desiredvnodes is historical cruft and should not exist. */ int desiredvnodes; SYSCTL_INT(_kern, KERN_MAXVNODES, maxvnodes, CTLFLAG_RW, &desiredvnodes, 0, "Maximum number of vnodes"); SYSCTL_ULONG(_kern, OID_AUTO, minvnodes, CTLFLAG_RW, &wantfreevnodes, 0, "Minimum number of vnodes (legacy)"); static int vnlru_nowhere; SYSCTL_INT(_debug, OID_AUTO, vnlru_nowhere, CTLFLAG_RW, &vnlru_nowhere, 0, "Number of times the vnlru process ran without success"); /* Shift count for (uintptr_t)vp to initialize vp->v_hash. */ static int vnsz2log; /* * Support for the bufobj clean & dirty pctrie. */ static void * buf_trie_alloc(struct pctrie *ptree) { return uma_zalloc(buf_trie_zone, M_NOWAIT); } static void buf_trie_free(struct pctrie *ptree, void *node) { uma_zfree(buf_trie_zone, node); } PCTRIE_DEFINE(BUF, buf, b_lblkno, buf_trie_alloc, buf_trie_free); /* * Initialize the vnode management data structures. * * Reevaluate the following cap on the number of vnodes after the physical * memory size exceeds 512GB. In the limit, as the physical memory size * grows, the ratio of physical pages to vnodes approaches sixteen to one. */ #ifndef MAXVNODES_MAX #define MAXVNODES_MAX (512 * (1024 * 1024 * 1024 / (int)PAGE_SIZE / 16)) #endif static void vntblinit(void *dummy __unused) { u_int i; int physvnodes, virtvnodes; /* * Desiredvnodes is a function of the physical memory size and the * kernel's heap size. Generally speaking, it scales with the * physical memory size. The ratio of desiredvnodes to physical pages * is one to four until desiredvnodes exceeds 98,304. Thereafter, the * marginal ratio of desiredvnodes to physical pages is one to * sixteen. However, desiredvnodes is limited by the kernel's heap * size. The memory required by desiredvnodes vnodes and vm objects * may not exceed one seventh of the kernel's heap size. */ physvnodes = maxproc + vm_cnt.v_page_count / 16 + 3 * min(98304 * 4, vm_cnt.v_page_count) / 16; virtvnodes = vm_kmem_size / (7 * (sizeof(struct vm_object) + sizeof(struct vnode))); desiredvnodes = min(physvnodes, virtvnodes); if (desiredvnodes > MAXVNODES_MAX) { if (bootverbose) printf("Reducing kern.maxvnodes %d -> %d\n", desiredvnodes, MAXVNODES_MAX); desiredvnodes = MAXVNODES_MAX; } wantfreevnodes = desiredvnodes / 4; mtx_init(&mntid_mtx, "mntid", NULL, MTX_DEF); TAILQ_INIT(&vnode_free_list); mtx_init(&vnode_free_list_mtx, "vnode_free_list", NULL, MTX_DEF); vnode_zone = uma_zcreate("VNODE", sizeof (struct vnode), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); vnodepoll_zone = uma_zcreate("VNODEPOLL", sizeof (struct vpollinfo), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); /* * Preallocate enough nodes to support one-per buf so that * we can not fail an insert. reassignbuf() callers can not * tolerate the insertion failure. */ buf_trie_zone = uma_zcreate("BUF TRIE", pctrie_node_size(), NULL, NULL, pctrie_zone_init, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE | UMA_ZONE_VM); uma_prealloc(buf_trie_zone, nbuf); /* * Initialize the filesystem syncer. */ syncer_workitem_pending = hashinit(syncer_maxdelay, M_VNODE, &syncer_mask); syncer_maxdelay = syncer_mask + 1; mtx_init(&sync_mtx, "Syncer mtx", NULL, MTX_DEF); cv_init(&sync_wakeup, "syncer"); for (i = 1; i <= sizeof(struct vnode); i <<= 1) vnsz2log++; vnsz2log--; } SYSINIT(vfs, SI_SUB_VFS, SI_ORDER_FIRST, vntblinit, NULL); /* * Mark a mount point as busy. Used to synchronize access and to delay * unmounting. Eventually, mountlist_mtx is not released on failure. * * vfs_busy() is a custom lock, it can block the caller. * vfs_busy() only sleeps if the unmount is active on the mount point. * For a mountpoint mp, vfs_busy-enforced lock is before lock of any * vnode belonging to mp. * * Lookup uses vfs_busy() to traverse mount points. * root fs var fs * / vnode lock A / vnode lock (/var) D * /var vnode lock B /log vnode lock(/var/log) E * vfs_busy lock C vfs_busy lock F * * Within each file system, the lock order is C->A->B and F->D->E. * * When traversing across mounts, the system follows that lock order: * * C->A->B * | * +->F->D->E * * The lookup() process for namei("/var") illustrates the process: * VOP_LOOKUP() obtains B while A is held * vfs_busy() obtains a shared lock on F while A and B are held * vput() releases lock on B * vput() releases lock on A * VFS_ROOT() obtains lock on D while shared lock on F is held * vfs_unbusy() releases shared lock on F * vn_lock() obtains lock on deadfs vnode vp_crossmp instead of A. * Attempt to lock A (instead of vp_crossmp) while D is held would * violate the global order, causing deadlocks. * * dounmount() locks B while F is drained. */ int vfs_busy(struct mount *mp, int flags) { MPASS((flags & ~MBF_MASK) == 0); CTR3(KTR_VFS, "%s: mp %p with flags %d", __func__, mp, flags); MNT_ILOCK(mp); MNT_REF(mp); /* * If mount point is currenly being unmounted, sleep until the * mount point fate is decided. If thread doing the unmounting fails, * it will clear MNTK_UNMOUNT flag before waking us up, indicating * that this mount point has survived the unmount attempt and vfs_busy * should retry. Otherwise the unmounter thread will set MNTK_REFEXPIRE * flag in addition to MNTK_UNMOUNT, indicating that mount point is * about to be really destroyed. vfs_busy needs to release its * reference on the mount point in this case and return with ENOENT, * telling the caller that mount mount it tried to busy is no longer * valid. */ while (mp->mnt_kern_flag & MNTK_UNMOUNT) { if (flags & MBF_NOWAIT || mp->mnt_kern_flag & MNTK_REFEXPIRE) { MNT_REL(mp); MNT_IUNLOCK(mp); CTR1(KTR_VFS, "%s: failed busying before sleeping", __func__); return (ENOENT); } if (flags & MBF_MNTLSTLOCK) mtx_unlock(&mountlist_mtx); mp->mnt_kern_flag |= MNTK_MWAIT; msleep(mp, MNT_MTX(mp), PVFS | PDROP, "vfs_busy", 0); if (flags & MBF_MNTLSTLOCK) mtx_lock(&mountlist_mtx); MNT_ILOCK(mp); } if (flags & MBF_MNTLSTLOCK) mtx_unlock(&mountlist_mtx); mp->mnt_lockref++; MNT_IUNLOCK(mp); return (0); } /* * Free a busy filesystem. */ void vfs_unbusy(struct mount *mp) { CTR2(KTR_VFS, "%s: mp %p", __func__, mp); MNT_ILOCK(mp); MNT_REL(mp); KASSERT(mp->mnt_lockref > 0, ("negative mnt_lockref")); mp->mnt_lockref--; if (mp->mnt_lockref == 0 && (mp->mnt_kern_flag & MNTK_DRAINING) != 0) { MPASS(mp->mnt_kern_flag & MNTK_UNMOUNT); CTR1(KTR_VFS, "%s: waking up waiters", __func__); mp->mnt_kern_flag &= ~MNTK_DRAINING; wakeup(&mp->mnt_lockref); } MNT_IUNLOCK(mp); } /* * Lookup a mount point by filesystem identifier. */ struct mount * vfs_getvfs(fsid_t *fsid) { struct mount *mp; CTR2(KTR_VFS, "%s: fsid %p", __func__, fsid); mtx_lock(&mountlist_mtx); TAILQ_FOREACH(mp, &mountlist, mnt_list) { if (mp->mnt_stat.f_fsid.val[0] == fsid->val[0] && mp->mnt_stat.f_fsid.val[1] == fsid->val[1]) { vfs_ref(mp); mtx_unlock(&mountlist_mtx); return (mp); } } mtx_unlock(&mountlist_mtx); CTR2(KTR_VFS, "%s: lookup failed for %p id", __func__, fsid); return ((struct mount *) 0); } /* * Lookup a mount point by filesystem identifier, busying it before * returning. * * To avoid congestion on mountlist_mtx, implement simple direct-mapped * cache for popular filesystem identifiers. The cache is lockess, using * the fact that struct mount's are never freed. In worst case we may * get pointer to unmounted or even different filesystem, so we have to * check what we got, and go slow way if so. */ struct mount * vfs_busyfs(fsid_t *fsid) { #define FSID_CACHE_SIZE 256 typedef struct mount * volatile vmp_t; static vmp_t cache[FSID_CACHE_SIZE]; struct mount *mp; int error; uint32_t hash; CTR2(KTR_VFS, "%s: fsid %p", __func__, fsid); hash = fsid->val[0] ^ fsid->val[1]; hash = (hash >> 16 ^ hash) & (FSID_CACHE_SIZE - 1); mp = cache[hash]; if (mp == NULL || mp->mnt_stat.f_fsid.val[0] != fsid->val[0] || mp->mnt_stat.f_fsid.val[1] != fsid->val[1]) goto slow; if (vfs_busy(mp, 0) != 0) { cache[hash] = NULL; goto slow; } if (mp->mnt_stat.f_fsid.val[0] == fsid->val[0] && mp->mnt_stat.f_fsid.val[1] == fsid->val[1]) return (mp); else vfs_unbusy(mp); slow: mtx_lock(&mountlist_mtx); TAILQ_FOREACH(mp, &mountlist, mnt_list) { if (mp->mnt_stat.f_fsid.val[0] == fsid->val[0] && mp->mnt_stat.f_fsid.val[1] == fsid->val[1]) { error = vfs_busy(mp, MBF_MNTLSTLOCK); if (error) { cache[hash] = NULL; mtx_unlock(&mountlist_mtx); return (NULL); } cache[hash] = mp; return (mp); } } CTR2(KTR_VFS, "%s: lookup failed for %p id", __func__, fsid); mtx_unlock(&mountlist_mtx); return ((struct mount *) 0); } /* * Check if a user can access privileged mount options. */ int vfs_suser(struct mount *mp, struct thread *td) { int error; /* * If the thread is jailed, but this is not a jail-friendly file * system, deny immediately. */ if (!(mp->mnt_vfc->vfc_flags & VFCF_JAIL) && jailed(td->td_ucred)) return (EPERM); /* * If the file system was mounted outside the jail of the calling * thread, deny immediately. */ if (prison_check(td->td_ucred, mp->mnt_cred) != 0) return (EPERM); /* * If file system supports delegated administration, we don't check * for the PRIV_VFS_MOUNT_OWNER privilege - it will be better verified * by the file system itself. * If this is not the user that did original mount, we check for * the PRIV_VFS_MOUNT_OWNER privilege. */ if (!(mp->mnt_vfc->vfc_flags & VFCF_DELEGADMIN) && mp->mnt_cred->cr_uid != td->td_ucred->cr_uid) { if ((error = priv_check(td, PRIV_VFS_MOUNT_OWNER)) != 0) return (error); } return (0); } /* * Get a new unique fsid. Try to make its val[0] unique, since this value * will be used to create fake device numbers for stat(). Also try (but * not so hard) make its val[0] unique mod 2^16, since some emulators only * support 16-bit device numbers. We end up with unique val[0]'s for the * first 2^16 calls and unique val[0]'s mod 2^16 for the first 2^8 calls. * * Keep in mind that several mounts may be running in parallel. Starting * the search one past where the previous search terminated is both a * micro-optimization and a defense against returning the same fsid to * different mounts. */ void vfs_getnewfsid(struct mount *mp) { static uint16_t mntid_base; struct mount *nmp; fsid_t tfsid; int mtype; CTR2(KTR_VFS, "%s: mp %p", __func__, mp); mtx_lock(&mntid_mtx); mtype = mp->mnt_vfc->vfc_typenum; tfsid.val[1] = mtype; mtype = (mtype & 0xFF) << 24; for (;;) { tfsid.val[0] = makedev(255, mtype | ((mntid_base & 0xFF00) << 8) | (mntid_base & 0xFF)); mntid_base++; if ((nmp = vfs_getvfs(&tfsid)) == NULL) break; vfs_rel(nmp); } mp->mnt_stat.f_fsid.val[0] = tfsid.val[0]; mp->mnt_stat.f_fsid.val[1] = tfsid.val[1]; mtx_unlock(&mntid_mtx); } /* * Knob to control the precision of file timestamps: * * 0 = seconds only; nanoseconds zeroed. * 1 = seconds and nanoseconds, accurate within 1/HZ. * 2 = seconds and nanoseconds, truncated to microseconds. * >=3 = seconds and nanoseconds, maximum precision. */ enum { TSP_SEC, TSP_HZ, TSP_USEC, TSP_NSEC }; static int timestamp_precision = TSP_USEC; SYSCTL_INT(_vfs, OID_AUTO, timestamp_precision, CTLFLAG_RW, ×tamp_precision, 0, "File timestamp precision (0: seconds, " "1: sec + ns accurate to 1/HZ, 2: sec + ns truncated to ms, " "3+: sec + ns (max. precision))"); /* * Get a current timestamp. */ void vfs_timestamp(struct timespec *tsp) { struct timeval tv; switch (timestamp_precision) { case TSP_SEC: tsp->tv_sec = time_second; tsp->tv_nsec = 0; break; case TSP_HZ: getnanotime(tsp); break; case TSP_USEC: microtime(&tv); TIMEVAL_TO_TIMESPEC(&tv, tsp); break; case TSP_NSEC: default: nanotime(tsp); break; } } /* * Set vnode attributes to VNOVAL */ void vattr_null(struct vattr *vap) { vap->va_type = VNON; vap->va_size = VNOVAL; vap->va_bytes = VNOVAL; vap->va_mode = VNOVAL; vap->va_nlink = VNOVAL; vap->va_uid = VNOVAL; vap->va_gid = VNOVAL; vap->va_fsid = VNOVAL; vap->va_fileid = VNOVAL; vap->va_blocksize = VNOVAL; vap->va_rdev = VNOVAL; vap->va_atime.tv_sec = VNOVAL; vap->va_atime.tv_nsec = VNOVAL; vap->va_mtime.tv_sec = VNOVAL; vap->va_mtime.tv_nsec = VNOVAL; vap->va_ctime.tv_sec = VNOVAL; vap->va_ctime.tv_nsec = VNOVAL; vap->va_birthtime.tv_sec = VNOVAL; vap->va_birthtime.tv_nsec = VNOVAL; vap->va_flags = VNOVAL; vap->va_gen = VNOVAL; vap->va_vaflags = 0; } /* * This routine is called when we have too many vnodes. It attempts * to free vnodes and will potentially free vnodes that still * have VM backing store (VM backing store is typically the cause * of a vnode blowout so we want to do this). Therefore, this operation * is not considered cheap. * * A number of conditions may prevent a vnode from being reclaimed. * the buffer cache may have references on the vnode, a directory * vnode may still have references due to the namei cache representing * underlying files, or the vnode may be in active use. It is not * desireable to reuse such vnodes. These conditions may cause the * number of vnodes to reach some minimum value regardless of what * you set kern.maxvnodes to. Do not set kern.maxvnodes too low. */ static int vlrureclaim(struct mount *mp) { struct vnode *vp; int done; int trigger; int usevnodes; int count; /* * Calculate the trigger point, don't allow user * screwups to blow us up. This prevents us from * recycling vnodes with lots of resident pages. We * aren't trying to free memory, we are trying to * free vnodes. */ usevnodes = desiredvnodes; if (usevnodes <= 0) usevnodes = 1; trigger = vm_cnt.v_page_count * 2 / usevnodes; done = 0; vn_start_write(NULL, &mp, V_WAIT); MNT_ILOCK(mp); count = mp->mnt_nvnodelistsize / 10 + 1; while (count != 0) { vp = TAILQ_FIRST(&mp->mnt_nvnodelist); while (vp != NULL && vp->v_type == VMARKER) vp = TAILQ_NEXT(vp, v_nmntvnodes); if (vp == NULL) break; TAILQ_REMOVE(&mp->mnt_nvnodelist, vp, v_nmntvnodes); TAILQ_INSERT_TAIL(&mp->mnt_nvnodelist, vp, v_nmntvnodes); --count; if (!VI_TRYLOCK(vp)) goto next_iter; /* * If it's been deconstructed already, it's still * referenced, or it exceeds the trigger, skip it. */ if (vp->v_usecount || (!vlru_allow_cache_src && !LIST_EMPTY(&(vp)->v_cache_src)) || (vp->v_iflag & VI_DOOMED) != 0 || (vp->v_object != NULL && vp->v_object->resident_page_count > trigger)) { VI_UNLOCK(vp); goto next_iter; } MNT_IUNLOCK(mp); vholdl(vp); if (VOP_LOCK(vp, LK_INTERLOCK|LK_EXCLUSIVE|LK_NOWAIT)) { vdrop(vp); goto next_iter_mntunlocked; } VI_LOCK(vp); /* * v_usecount may have been bumped after VOP_LOCK() dropped * the vnode interlock and before it was locked again. * * It is not necessary to recheck VI_DOOMED because it can * only be set by another thread that holds both the vnode * lock and vnode interlock. If another thread has the * vnode lock before we get to VOP_LOCK() and obtains the * vnode interlock after VOP_LOCK() drops the vnode * interlock, the other thread will be unable to drop the * vnode lock before our VOP_LOCK() call fails. */ if (vp->v_usecount || (!vlru_allow_cache_src && !LIST_EMPTY(&(vp)->v_cache_src)) || (vp->v_object != NULL && vp->v_object->resident_page_count > trigger)) { VOP_UNLOCK(vp, LK_INTERLOCK); vdrop(vp); goto next_iter_mntunlocked; } KASSERT((vp->v_iflag & VI_DOOMED) == 0, ("VI_DOOMED unexpectedly detected in vlrureclaim()")); + atomic_add_long(&recycles_count, 1); vgonel(vp); VOP_UNLOCK(vp, 0); vdropl(vp); done++; next_iter_mntunlocked: if (!should_yield()) goto relock_mnt; goto yield; next_iter: if (!should_yield()) continue; MNT_IUNLOCK(mp); yield: kern_yield(PRI_USER); relock_mnt: MNT_ILOCK(mp); } MNT_IUNLOCK(mp); vn_finished_write(mp); return done; } /* * Attempt to keep the free list at wantfreevnodes length. */ static void vnlru_free(int count) { struct vnode *vp; mtx_assert(&vnode_free_list_mtx, MA_OWNED); for (; count > 0; count--) { vp = TAILQ_FIRST(&vnode_free_list); /* * The list can be modified while the free_list_mtx * has been dropped and vp could be NULL here. */ if (!vp) break; VNASSERT(vp->v_op != NULL, vp, ("vnlru_free: vnode already reclaimed.")); KASSERT((vp->v_iflag & VI_FREE) != 0, ("Removing vnode not on freelist")); KASSERT((vp->v_iflag & VI_ACTIVE) == 0, ("Mangling active vnode")); TAILQ_REMOVE(&vnode_free_list, vp, v_actfreelist); /* * Don't recycle if we can't get the interlock. */ if (!VI_TRYLOCK(vp)) { TAILQ_INSERT_TAIL(&vnode_free_list, vp, v_actfreelist); continue; } VNASSERT((vp->v_iflag & VI_FREE) != 0 && vp->v_holdcnt == 0, vp, ("vp inconsistent on freelist")); /* * The clear of VI_FREE prevents activation of the * vnode. There is no sense in putting the vnode on * the mount point active list, only to remove it * later during recycling. Inline the relevant part * of vholdl(), to avoid triggering assertions or * activating. */ freevnodes--; vp->v_iflag &= ~VI_FREE; vp->v_holdcnt++; mtx_unlock(&vnode_free_list_mtx); VI_UNLOCK(vp); vtryrecycle(vp); /* * If the recycled succeeded this vdrop will actually free * the vnode. If not it will simply place it back on * the free list. */ vdrop(vp); mtx_lock(&vnode_free_list_mtx); } } /* * Attempt to recycle vnodes in a context that is always safe to block. * Calling vlrurecycle() from the bowels of filesystem code has some * interesting deadlock problems. */ static struct proc *vnlruproc; static int vnlruproc_sig; static void vnlru_proc(void) { struct mount *mp, *nmp; int done; struct proc *p = vnlruproc; EVENTHANDLER_REGISTER(shutdown_pre_sync, kproc_shutdown, p, SHUTDOWN_PRI_FIRST); for (;;) { kproc_suspend_check(p); mtx_lock(&vnode_free_list_mtx); if (freevnodes > wantfreevnodes) vnlru_free(freevnodes - wantfreevnodes); if (numvnodes <= desiredvnodes * 9 / 10) { vnlruproc_sig = 0; wakeup(&vnlruproc_sig); msleep(vnlruproc, &vnode_free_list_mtx, PVFS|PDROP, "vlruwt", hz); continue; } mtx_unlock(&vnode_free_list_mtx); done = 0; mtx_lock(&mountlist_mtx); for (mp = TAILQ_FIRST(&mountlist); mp != NULL; mp = nmp) { if (vfs_busy(mp, MBF_NOWAIT | MBF_MNTLSTLOCK)) { nmp = TAILQ_NEXT(mp, mnt_list); continue; } done += vlrureclaim(mp); mtx_lock(&mountlist_mtx); nmp = TAILQ_NEXT(mp, mnt_list); vfs_unbusy(mp); } mtx_unlock(&mountlist_mtx); if (done == 0) { #if 0 /* These messages are temporary debugging aids */ if (vnlru_nowhere < 5) printf("vnlru process getting nowhere..\n"); else if (vnlru_nowhere == 5) printf("vnlru process messages stopped.\n"); #endif vnlru_nowhere++; tsleep(vnlruproc, PPAUSE, "vlrup", hz * 3); } else kern_yield(PRI_USER); } } static struct kproc_desc vnlru_kp = { "vnlru", vnlru_proc, &vnlruproc }; SYSINIT(vnlru, SI_SUB_KTHREAD_UPDATE, SI_ORDER_FIRST, kproc_start, &vnlru_kp); /* * Routines having to do with the management of the vnode table. */ /* * Try to recycle a freed vnode. We abort if anyone picks up a reference * before we actually vgone(). This function must be called with the vnode * held to prevent the vnode from being returned to the free list midway * through vgone(). */ static int vtryrecycle(struct vnode *vp) { struct mount *vnmp; CTR2(KTR_VFS, "%s: vp %p", __func__, vp); VNASSERT(vp->v_holdcnt, vp, ("vtryrecycle: Recycling vp %p without a reference.", vp)); /* * This vnode may found and locked via some other list, if so we * can't recycle it yet. */ if (VOP_LOCK(vp, LK_EXCLUSIVE | LK_NOWAIT) != 0) { CTR2(KTR_VFS, "%s: impossible to recycle, vp %p lock is already held", __func__, vp); return (EWOULDBLOCK); } /* * Don't recycle if its filesystem is being suspended. */ if (vn_start_write(vp, &vnmp, V_NOWAIT) != 0) { VOP_UNLOCK(vp, 0); CTR2(KTR_VFS, "%s: impossible to recycle, cannot start the write for %p", __func__, vp); return (EBUSY); } /* * If we got this far, we need to acquire the interlock and see if * anyone picked up this vnode from another list. If not, we will * mark it with DOOMED via vgonel() so that anyone who does find it * will skip over it. */ VI_LOCK(vp); if (vp->v_usecount) { VOP_UNLOCK(vp, LK_INTERLOCK); vn_finished_write(vnmp); CTR2(KTR_VFS, "%s: impossible to recycle, %p is already referenced", __func__, vp); return (EBUSY); } - if ((vp->v_iflag & VI_DOOMED) == 0) + if ((vp->v_iflag & VI_DOOMED) == 0) { + atomic_add_long(&recycles_count, 1); vgonel(vp); + } VOP_UNLOCK(vp, LK_INTERLOCK); vn_finished_write(vnmp); return (0); } /* * Wait for available vnodes. */ static int getnewvnode_wait(int suspended) { mtx_assert(&vnode_free_list_mtx, MA_OWNED); if (numvnodes > desiredvnodes) { if (suspended) { /* * File system is beeing suspended, we cannot risk a * deadlock here, so allocate new vnode anyway. */ if (freevnodes > wantfreevnodes) vnlru_free(freevnodes - wantfreevnodes); return (0); } if (vnlruproc_sig == 0) { vnlruproc_sig = 1; /* avoid unnecessary wakeups */ wakeup(vnlruproc); } msleep(&vnlruproc_sig, &vnode_free_list_mtx, PVFS, "vlruwk", hz); } return (numvnodes > desiredvnodes ? ENFILE : 0); } void getnewvnode_reserve(u_int count) { struct thread *td; td = curthread; /* First try to be quick and racy. */ if (atomic_fetchadd_long(&numvnodes, count) + count <= desiredvnodes) { td->td_vp_reserv += count; return; } else atomic_subtract_long(&numvnodes, count); mtx_lock(&vnode_free_list_mtx); while (count > 0) { if (getnewvnode_wait(0) == 0) { count--; td->td_vp_reserv++; atomic_add_long(&numvnodes, 1); } } mtx_unlock(&vnode_free_list_mtx); } void getnewvnode_drop_reserve(void) { struct thread *td; td = curthread; atomic_subtract_long(&numvnodes, td->td_vp_reserv); td->td_vp_reserv = 0; } /* * Return the next vnode from the free list. */ int getnewvnode(const char *tag, struct mount *mp, struct vop_vector *vops, struct vnode **vpp) { struct vnode *vp; struct bufobj *bo; struct thread *td; int error; CTR3(KTR_VFS, "%s: mp %p with tag %s", __func__, mp, tag); vp = NULL; td = curthread; if (td->td_vp_reserv > 0) { td->td_vp_reserv -= 1; goto alloc; } mtx_lock(&vnode_free_list_mtx); /* * Lend our context to reclaim vnodes if they've exceeded the max. */ if (freevnodes > wantfreevnodes) vnlru_free(1); error = getnewvnode_wait(mp != NULL && (mp->mnt_kern_flag & MNTK_SUSPEND)); #if 0 /* XXX Not all VFS_VGET/ffs_vget callers check returns. */ if (error != 0) { mtx_unlock(&vnode_free_list_mtx); return (error); } #endif atomic_add_long(&numvnodes, 1); mtx_unlock(&vnode_free_list_mtx); alloc: + atomic_add_long(&vnodes_created, 1); vp = (struct vnode *) uma_zalloc(vnode_zone, M_WAITOK|M_ZERO); /* * Setup locks. */ vp->v_vnlock = &vp->v_lock; mtx_init(&vp->v_interlock, "vnode interlock", NULL, MTX_DEF); /* * By default, don't allow shared locks unless filesystems * opt-in. */ lockinit(vp->v_vnlock, PVFS, tag, VLKTIMEOUT, LK_NOSHARE | LK_IS_VNODE); /* * Initialize bufobj. */ bo = &vp->v_bufobj; bo->__bo_vnode = vp; rw_init(BO_LOCKPTR(bo), "bufobj interlock"); bo->bo_ops = &buf_ops_bio; bo->bo_private = vp; TAILQ_INIT(&bo->bo_clean.bv_hd); TAILQ_INIT(&bo->bo_dirty.bv_hd); /* * Initialize namecache. */ LIST_INIT(&vp->v_cache_src); TAILQ_INIT(&vp->v_cache_dst); /* * Finalize various vnode identity bits. */ vp->v_type = VNON; vp->v_tag = tag; vp->v_op = vops; v_incr_usecount(vp); vp->v_data = NULL; #ifdef MAC mac_vnode_init(vp); if (mp != NULL && (mp->mnt_flag & MNT_MULTILABEL) == 0) mac_vnode_associate_singlelabel(mp, vp); else if (mp == NULL && vops != &dead_vnodeops) printf("NULL mp in getnewvnode()\n"); #endif if (mp != NULL) { bo->bo_bsize = mp->mnt_stat.f_iosize; if ((mp->mnt_kern_flag & MNTK_NOKNOTE) != 0) vp->v_vflag |= VV_NOKNOTE; } rangelock_init(&vp->v_rl); /* * For the filesystems which do not use vfs_hash_insert(), * still initialize v_hash to have vfs_hash_index() useful. * E.g., nullfs uses vfs_hash_index() on the lower vnode for * its own hashing. */ vp->v_hash = (uintptr_t)vp >> vnsz2log; *vpp = vp; return (0); } /* * Delete from old mount point vnode list, if on one. */ static void delmntque(struct vnode *vp) { struct mount *mp; int active; mp = vp->v_mount; if (mp == NULL) return; MNT_ILOCK(mp); VI_LOCK(vp); KASSERT(mp->mnt_activevnodelistsize <= mp->mnt_nvnodelistsize, ("Active vnode list size %d > Vnode list size %d", mp->mnt_activevnodelistsize, mp->mnt_nvnodelistsize)); active = vp->v_iflag & VI_ACTIVE; vp->v_iflag &= ~VI_ACTIVE; if (active) { mtx_lock(&vnode_free_list_mtx); TAILQ_REMOVE(&mp->mnt_activevnodelist, vp, v_actfreelist); mp->mnt_activevnodelistsize--; mtx_unlock(&vnode_free_list_mtx); } vp->v_mount = NULL; VI_UNLOCK(vp); VNASSERT(mp->mnt_nvnodelistsize > 0, vp, ("bad mount point vnode list size")); TAILQ_REMOVE(&mp->mnt_nvnodelist, vp, v_nmntvnodes); mp->mnt_nvnodelistsize--; MNT_REL(mp); MNT_IUNLOCK(mp); } static void insmntque_stddtr(struct vnode *vp, void *dtr_arg) { vp->v_data = NULL; vp->v_op = &dead_vnodeops; vgone(vp); vput(vp); } /* * Insert into list of vnodes for the new mount point, if available. */ int insmntque1(struct vnode *vp, struct mount *mp, void (*dtr)(struct vnode *, void *), void *dtr_arg) { KASSERT(vp->v_mount == NULL, ("insmntque: vnode already on per mount vnode list")); VNASSERT(mp != NULL, vp, ("Don't call insmntque(foo, NULL)")); ASSERT_VOP_ELOCKED(vp, "insmntque: non-locked vp"); /* * We acquire the vnode interlock early to ensure that the * vnode cannot be recycled by another process releasing a * holdcnt on it before we get it on both the vnode list * and the active vnode list. The mount mutex protects only * manipulation of the vnode list and the vnode freelist * mutex protects only manipulation of the active vnode list. * Hence the need to hold the vnode interlock throughout. */ MNT_ILOCK(mp); VI_LOCK(vp); if (((mp->mnt_kern_flag & MNTK_NOINSMNTQ) != 0 && ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0 || mp->mnt_nvnodelistsize == 0)) && (vp->v_vflag & VV_FORCEINSMQ) == 0) { VI_UNLOCK(vp); MNT_IUNLOCK(mp); if (dtr != NULL) dtr(vp, dtr_arg); return (EBUSY); } vp->v_mount = mp; MNT_REF(mp); TAILQ_INSERT_TAIL(&mp->mnt_nvnodelist, vp, v_nmntvnodes); VNASSERT(mp->mnt_nvnodelistsize >= 0, vp, ("neg mount point vnode list size")); mp->mnt_nvnodelistsize++; KASSERT((vp->v_iflag & VI_ACTIVE) == 0, ("Activating already active vnode")); vp->v_iflag |= VI_ACTIVE; mtx_lock(&vnode_free_list_mtx); TAILQ_INSERT_HEAD(&mp->mnt_activevnodelist, vp, v_actfreelist); mp->mnt_activevnodelistsize++; mtx_unlock(&vnode_free_list_mtx); VI_UNLOCK(vp); MNT_IUNLOCK(mp); return (0); } int insmntque(struct vnode *vp, struct mount *mp) { return (insmntque1(vp, mp, insmntque_stddtr, NULL)); } /* * Flush out and invalidate all buffers associated with a bufobj * Called with the underlying object locked. */ int bufobj_invalbuf(struct bufobj *bo, int flags, int slpflag, int slptimeo) { int error; BO_LOCK(bo); if (flags & V_SAVE) { error = bufobj_wwait(bo, slpflag, slptimeo); if (error) { BO_UNLOCK(bo); return (error); } if (bo->bo_dirty.bv_cnt > 0) { BO_UNLOCK(bo); if ((error = BO_SYNC(bo, MNT_WAIT)) != 0) return (error); /* * XXX We could save a lock/unlock if this was only * enabled under INVARIANTS */ BO_LOCK(bo); if (bo->bo_numoutput > 0 || bo->bo_dirty.bv_cnt > 0) panic("vinvalbuf: dirty bufs"); } } /* * If you alter this loop please notice that interlock is dropped and * reacquired in flushbuflist. Special care is needed to ensure that * no race conditions occur from this. */ do { error = flushbuflist(&bo->bo_clean, flags, bo, slpflag, slptimeo); if (error == 0 && !(flags & V_CLEANONLY)) error = flushbuflist(&bo->bo_dirty, flags, bo, slpflag, slptimeo); if (error != 0 && error != EAGAIN) { BO_UNLOCK(bo); return (error); } } while (error != 0); /* * Wait for I/O to complete. XXX needs cleaning up. The vnode can * have write I/O in-progress but if there is a VM object then the * VM object can also have read-I/O in-progress. */ do { bufobj_wwait(bo, 0, 0); BO_UNLOCK(bo); if (bo->bo_object != NULL) { VM_OBJECT_WLOCK(bo->bo_object); vm_object_pip_wait(bo->bo_object, "bovlbx"); VM_OBJECT_WUNLOCK(bo->bo_object); } BO_LOCK(bo); } while (bo->bo_numoutput > 0); BO_UNLOCK(bo); /* * Destroy the copy in the VM cache, too. */ if (bo->bo_object != NULL && (flags & (V_ALT | V_NORMAL | V_CLEANONLY)) == 0) { VM_OBJECT_WLOCK(bo->bo_object); vm_object_page_remove(bo->bo_object, 0, 0, (flags & V_SAVE) ? OBJPR_CLEANONLY : 0); VM_OBJECT_WUNLOCK(bo->bo_object); } #ifdef INVARIANTS BO_LOCK(bo); if ((flags & (V_ALT | V_NORMAL | V_CLEANONLY)) == 0 && (bo->bo_dirty.bv_cnt > 0 || bo->bo_clean.bv_cnt > 0)) panic("vinvalbuf: flush failed"); BO_UNLOCK(bo); #endif return (0); } /* * Flush out and invalidate all buffers associated with a vnode. * Called with the underlying object locked. */ int vinvalbuf(struct vnode *vp, int flags, int slpflag, int slptimeo) { CTR3(KTR_VFS, "%s: vp %p with flags %d", __func__, vp, flags); ASSERT_VOP_LOCKED(vp, "vinvalbuf"); if (vp->v_object != NULL && vp->v_object->handle != vp) return (0); return (bufobj_invalbuf(&vp->v_bufobj, flags, slpflag, slptimeo)); } /* * Flush out buffers on the specified list. * */ static int flushbuflist(struct bufv *bufv, int flags, struct bufobj *bo, int slpflag, int slptimeo) { struct buf *bp, *nbp; int retval, error; daddr_t lblkno; b_xflags_t xflags; ASSERT_BO_WLOCKED(bo); retval = 0; TAILQ_FOREACH_SAFE(bp, &bufv->bv_hd, b_bobufs, nbp) { if (((flags & V_NORMAL) && (bp->b_xflags & BX_ALTDATA)) || ((flags & V_ALT) && (bp->b_xflags & BX_ALTDATA) == 0)) { continue; } lblkno = 0; xflags = 0; if (nbp != NULL) { lblkno = nbp->b_lblkno; xflags = nbp->b_xflags & (BX_VNDIRTY | BX_VNCLEAN); } retval = EAGAIN; error = BUF_TIMELOCK(bp, LK_EXCLUSIVE | LK_SLEEPFAIL | LK_INTERLOCK, BO_LOCKPTR(bo), "flushbuf", slpflag, slptimeo); if (error) { BO_LOCK(bo); return (error != ENOLCK ? error : EAGAIN); } KASSERT(bp->b_bufobj == bo, ("bp %p wrong b_bufobj %p should be %p", bp, bp->b_bufobj, bo)); if (bp->b_bufobj != bo) { /* XXX: necessary ? */ BUF_UNLOCK(bp); BO_LOCK(bo); return (EAGAIN); } /* * XXX Since there are no node locks for NFS, I * believe there is a slight chance that a delayed * write will occur while sleeping just above, so * check for it. */ if (((bp->b_flags & (B_DELWRI | B_INVAL)) == B_DELWRI) && (flags & V_SAVE)) { bremfree(bp); bp->b_flags |= B_ASYNC; bwrite(bp); BO_LOCK(bo); return (EAGAIN); /* XXX: why not loop ? */ } bremfree(bp); bp->b_flags |= (B_INVAL | B_RELBUF); bp->b_flags &= ~B_ASYNC; brelse(bp); BO_LOCK(bo); if (nbp != NULL && (nbp->b_bufobj != bo || nbp->b_lblkno != lblkno || (nbp->b_xflags & (BX_VNDIRTY | BX_VNCLEAN)) != xflags)) break; /* nbp invalid */ } return (retval); } /* * Truncate a file's buffer and pages to a specified length. This * is in lieu of the old vinvalbuf mechanism, which performed unneeded * sync activity. */ int vtruncbuf(struct vnode *vp, struct ucred *cred, off_t length, int blksize) { struct buf *bp, *nbp; int anyfreed; int trunclbn; struct bufobj *bo; CTR5(KTR_VFS, "%s: vp %p with cred %p and block %d:%ju", __func__, vp, cred, blksize, (uintmax_t)length); /* * Round up to the *next* lbn. */ trunclbn = (length + blksize - 1) / blksize; ASSERT_VOP_LOCKED(vp, "vtruncbuf"); restart: bo = &vp->v_bufobj; BO_LOCK(bo); anyfreed = 1; for (;anyfreed;) { anyfreed = 0; TAILQ_FOREACH_SAFE(bp, &bo->bo_clean.bv_hd, b_bobufs, nbp) { if (bp->b_lblkno < trunclbn) continue; if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_SLEEPFAIL | LK_INTERLOCK, BO_LOCKPTR(bo)) == ENOLCK) goto restart; bremfree(bp); bp->b_flags |= (B_INVAL | B_RELBUF); bp->b_flags &= ~B_ASYNC; brelse(bp); anyfreed = 1; BO_LOCK(bo); if (nbp != NULL && (((nbp->b_xflags & BX_VNCLEAN) == 0) || (nbp->b_vp != vp) || (nbp->b_flags & B_DELWRI))) { BO_UNLOCK(bo); goto restart; } } TAILQ_FOREACH_SAFE(bp, &bo->bo_dirty.bv_hd, b_bobufs, nbp) { if (bp->b_lblkno < trunclbn) continue; if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_SLEEPFAIL | LK_INTERLOCK, BO_LOCKPTR(bo)) == ENOLCK) goto restart; bremfree(bp); bp->b_flags |= (B_INVAL | B_RELBUF); bp->b_flags &= ~B_ASYNC; brelse(bp); anyfreed = 1; BO_LOCK(bo); if (nbp != NULL && (((nbp->b_xflags & BX_VNDIRTY) == 0) || (nbp->b_vp != vp) || (nbp->b_flags & B_DELWRI) == 0)) { BO_UNLOCK(bo); goto restart; } } } if (length > 0) { restartsync: TAILQ_FOREACH_SAFE(bp, &bo->bo_dirty.bv_hd, b_bobufs, nbp) { if (bp->b_lblkno > 0) continue; /* * Since we hold the vnode lock this should only * fail if we're racing with the buf daemon. */ if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_SLEEPFAIL | LK_INTERLOCK, BO_LOCKPTR(bo)) == ENOLCK) { goto restart; } VNASSERT((bp->b_flags & B_DELWRI), vp, ("buf(%p) on dirty queue without DELWRI", bp)); bremfree(bp); bawrite(bp); BO_LOCK(bo); goto restartsync; } } bufobj_wwait(bo, 0, 0); BO_UNLOCK(bo); vnode_pager_setsize(vp, length); return (0); } static void buf_vlist_remove(struct buf *bp) { struct bufv *bv; KASSERT(bp->b_bufobj != NULL, ("No b_bufobj %p", bp)); ASSERT_BO_WLOCKED(bp->b_bufobj); KASSERT((bp->b_xflags & (BX_VNDIRTY|BX_VNCLEAN)) != (BX_VNDIRTY|BX_VNCLEAN), ("buf_vlist_remove: Buf %p is on two lists", bp)); if (bp->b_xflags & BX_VNDIRTY) bv = &bp->b_bufobj->bo_dirty; else bv = &bp->b_bufobj->bo_clean; BUF_PCTRIE_REMOVE(&bv->bv_root, bp->b_lblkno); TAILQ_REMOVE(&bv->bv_hd, bp, b_bobufs); bv->bv_cnt--; bp->b_xflags &= ~(BX_VNDIRTY | BX_VNCLEAN); } /* * Add the buffer to the sorted clean or dirty block list. * * NOTE: xflags is passed as a constant, optimizing this inline function! */ static void buf_vlist_add(struct buf *bp, struct bufobj *bo, b_xflags_t xflags) { struct bufv *bv; struct buf *n; int error; ASSERT_BO_WLOCKED(bo); KASSERT((bo->bo_flag & BO_DEAD) == 0, ("dead bo %p", bo)); KASSERT((bp->b_xflags & (BX_VNDIRTY|BX_VNCLEAN)) == 0, ("buf_vlist_add: Buf %p has existing xflags %d", bp, bp->b_xflags)); bp->b_xflags |= xflags; if (xflags & BX_VNDIRTY) bv = &bo->bo_dirty; else bv = &bo->bo_clean; /* * Keep the list ordered. Optimize empty list insertion. Assume * we tend to grow at the tail so lookup_le should usually be cheaper * than _ge. */ if (bv->bv_cnt == 0 || bp->b_lblkno > TAILQ_LAST(&bv->bv_hd, buflists)->b_lblkno) TAILQ_INSERT_TAIL(&bv->bv_hd, bp, b_bobufs); else if ((n = BUF_PCTRIE_LOOKUP_LE(&bv->bv_root, bp->b_lblkno)) == NULL) TAILQ_INSERT_HEAD(&bv->bv_hd, bp, b_bobufs); else TAILQ_INSERT_AFTER(&bv->bv_hd, n, bp, b_bobufs); error = BUF_PCTRIE_INSERT(&bv->bv_root, bp); if (error) panic("buf_vlist_add: Preallocated nodes insufficient."); bv->bv_cnt++; } /* * Lookup a buffer using the splay tree. Note that we specifically avoid * shadow buffers used in background bitmap writes. * * This code isn't quite efficient as it could be because we are maintaining * two sorted lists and do not know which list the block resides in. * * During a "make buildworld" the desired buffer is found at one of * the roots more than 60% of the time. Thus, checking both roots * before performing either splay eliminates unnecessary splays on the * first tree splayed. */ struct buf * gbincore(struct bufobj *bo, daddr_t lblkno) { struct buf *bp; ASSERT_BO_LOCKED(bo); bp = BUF_PCTRIE_LOOKUP(&bo->bo_clean.bv_root, lblkno); if (bp != NULL) return (bp); return BUF_PCTRIE_LOOKUP(&bo->bo_dirty.bv_root, lblkno); } /* * Associate a buffer with a vnode. */ void bgetvp(struct vnode *vp, struct buf *bp) { struct bufobj *bo; bo = &vp->v_bufobj; ASSERT_BO_WLOCKED(bo); VNASSERT(bp->b_vp == NULL, bp->b_vp, ("bgetvp: not free")); CTR3(KTR_BUF, "bgetvp(%p) vp %p flags %X", bp, vp, bp->b_flags); VNASSERT((bp->b_xflags & (BX_VNDIRTY|BX_VNCLEAN)) == 0, vp, ("bgetvp: bp already attached! %p", bp)); vhold(vp); bp->b_vp = vp; bp->b_bufobj = bo; /* * Insert onto list for new vnode. */ buf_vlist_add(bp, bo, BX_VNCLEAN); } /* * Disassociate a buffer from a vnode. */ void brelvp(struct buf *bp) { struct bufobj *bo; struct vnode *vp; CTR3(KTR_BUF, "brelvp(%p) vp %p flags %X", bp, bp->b_vp, bp->b_flags); KASSERT(bp->b_vp != NULL, ("brelvp: NULL")); /* * Delete from old vnode list, if on one. */ vp = bp->b_vp; /* XXX */ bo = bp->b_bufobj; BO_LOCK(bo); if (bp->b_xflags & (BX_VNDIRTY | BX_VNCLEAN)) buf_vlist_remove(bp); else panic("brelvp: Buffer %p not on queue.", bp); if ((bo->bo_flag & BO_ONWORKLST) && bo->bo_dirty.bv_cnt == 0) { bo->bo_flag &= ~BO_ONWORKLST; mtx_lock(&sync_mtx); LIST_REMOVE(bo, bo_synclist); syncer_worklist_len--; mtx_unlock(&sync_mtx); } bp->b_vp = NULL; bp->b_bufobj = NULL; BO_UNLOCK(bo); vdrop(vp); } /* * Add an item to the syncer work queue. */ static void vn_syncer_add_to_worklist(struct bufobj *bo, int delay) { int slot; ASSERT_BO_WLOCKED(bo); mtx_lock(&sync_mtx); if (bo->bo_flag & BO_ONWORKLST) LIST_REMOVE(bo, bo_synclist); else { bo->bo_flag |= BO_ONWORKLST; syncer_worklist_len++; } if (delay > syncer_maxdelay - 2) delay = syncer_maxdelay - 2; slot = (syncer_delayno + delay) & syncer_mask; LIST_INSERT_HEAD(&syncer_workitem_pending[slot], bo, bo_synclist); mtx_unlock(&sync_mtx); } static int sysctl_vfs_worklist_len(SYSCTL_HANDLER_ARGS) { int error, len; mtx_lock(&sync_mtx); len = syncer_worklist_len - sync_vnode_count; mtx_unlock(&sync_mtx); error = SYSCTL_OUT(req, &len, sizeof(len)); return (error); } SYSCTL_PROC(_vfs, OID_AUTO, worklist_len, CTLTYPE_INT | CTLFLAG_RD, NULL, 0, sysctl_vfs_worklist_len, "I", "Syncer thread worklist length"); static struct proc *updateproc; static void sched_sync(void); static struct kproc_desc up_kp = { "syncer", sched_sync, &updateproc }; SYSINIT(syncer, SI_SUB_KTHREAD_UPDATE, SI_ORDER_FIRST, kproc_start, &up_kp); static int sync_vnode(struct synclist *slp, struct bufobj **bo, struct thread *td) { struct vnode *vp; struct mount *mp; *bo = LIST_FIRST(slp); if (*bo == NULL) return (0); vp = (*bo)->__bo_vnode; /* XXX */ if (VOP_ISLOCKED(vp) != 0 || VI_TRYLOCK(vp) == 0) return (1); /* * We use vhold in case the vnode does not * successfully sync. vhold prevents the vnode from * going away when we unlock the sync_mtx so that * we can acquire the vnode interlock. */ vholdl(vp); mtx_unlock(&sync_mtx); VI_UNLOCK(vp); if (vn_start_write(vp, &mp, V_NOWAIT) != 0) { vdrop(vp); mtx_lock(&sync_mtx); return (*bo == LIST_FIRST(slp)); } vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); (void) VOP_FSYNC(vp, MNT_LAZY, td); VOP_UNLOCK(vp, 0); vn_finished_write(mp); BO_LOCK(*bo); if (((*bo)->bo_flag & BO_ONWORKLST) != 0) { /* * Put us back on the worklist. The worklist * routine will remove us from our current * position and then add us back in at a later * position. */ vn_syncer_add_to_worklist(*bo, syncdelay); } BO_UNLOCK(*bo); vdrop(vp); mtx_lock(&sync_mtx); return (0); } static int first_printf = 1; /* * System filesystem synchronizer daemon. */ static void sched_sync(void) { struct synclist *next, *slp; struct bufobj *bo; long starttime; struct thread *td = curthread; int last_work_seen; int net_worklist_len; int syncer_final_iter; int error; last_work_seen = 0; syncer_final_iter = 0; syncer_state = SYNCER_RUNNING; starttime = time_uptime; td->td_pflags |= TDP_NORUNNINGBUF; EVENTHANDLER_REGISTER(shutdown_pre_sync, syncer_shutdown, td->td_proc, SHUTDOWN_PRI_LAST); mtx_lock(&sync_mtx); for (;;) { if (syncer_state == SYNCER_FINAL_DELAY && syncer_final_iter == 0) { mtx_unlock(&sync_mtx); kproc_suspend_check(td->td_proc); mtx_lock(&sync_mtx); } net_worklist_len = syncer_worklist_len - sync_vnode_count; if (syncer_state != SYNCER_RUNNING && starttime != time_uptime) { if (first_printf) { printf("\nSyncing disks, vnodes remaining..."); first_printf = 0; } printf("%d ", net_worklist_len); } starttime = time_uptime; /* * Push files whose dirty time has expired. Be careful * of interrupt race on slp queue. * * Skip over empty worklist slots when shutting down. */ do { slp = &syncer_workitem_pending[syncer_delayno]; syncer_delayno += 1; if (syncer_delayno == syncer_maxdelay) syncer_delayno = 0; next = &syncer_workitem_pending[syncer_delayno]; /* * If the worklist has wrapped since the * it was emptied of all but syncer vnodes, * switch to the FINAL_DELAY state and run * for one more second. */ if (syncer_state == SYNCER_SHUTTING_DOWN && net_worklist_len == 0 && last_work_seen == syncer_delayno) { syncer_state = SYNCER_FINAL_DELAY; syncer_final_iter = SYNCER_SHUTDOWN_SPEEDUP; } } while (syncer_state != SYNCER_RUNNING && LIST_EMPTY(slp) && syncer_worklist_len > 0); /* * Keep track of the last time there was anything * on the worklist other than syncer vnodes. * Return to the SHUTTING_DOWN state if any * new work appears. */ if (net_worklist_len > 0 || syncer_state == SYNCER_RUNNING) last_work_seen = syncer_delayno; if (net_worklist_len > 0 && syncer_state == SYNCER_FINAL_DELAY) syncer_state = SYNCER_SHUTTING_DOWN; while (!LIST_EMPTY(slp)) { error = sync_vnode(slp, &bo, td); if (error == 1) { LIST_REMOVE(bo, bo_synclist); LIST_INSERT_HEAD(next, bo, bo_synclist); continue; } if (first_printf == 0) { /* * Drop the sync mutex, because some watchdog * drivers need to sleep while patting */ mtx_unlock(&sync_mtx); wdog_kern_pat(WD_LASTVAL); mtx_lock(&sync_mtx); } } if (syncer_state == SYNCER_FINAL_DELAY && syncer_final_iter > 0) syncer_final_iter--; /* * The variable rushjob allows the kernel to speed up the * processing of the filesystem syncer process. A rushjob * value of N tells the filesystem syncer to process the next * N seconds worth of work on its queue ASAP. Currently rushjob * is used by the soft update code to speed up the filesystem * syncer process when the incore state is getting so far * ahead of the disk that the kernel memory pool is being * threatened with exhaustion. */ if (rushjob > 0) { rushjob -= 1; continue; } /* * Just sleep for a short period of time between * iterations when shutting down to allow some I/O * to happen. * * If it has taken us less than a second to process the * current work, then wait. Otherwise start right over * again. We can still lose time if any single round * takes more than two seconds, but it does not really * matter as we are just trying to generally pace the * filesystem activity. */ if (syncer_state != SYNCER_RUNNING || time_uptime == starttime) { thread_lock(td); sched_prio(td, PPAUSE); thread_unlock(td); } if (syncer_state != SYNCER_RUNNING) cv_timedwait(&sync_wakeup, &sync_mtx, hz / SYNCER_SHUTDOWN_SPEEDUP); else if (time_uptime == starttime) cv_timedwait(&sync_wakeup, &sync_mtx, hz); } } /* * Request the syncer daemon to speed up its work. * We never push it to speed up more than half of its * normal turn time, otherwise it could take over the cpu. */ int speedup_syncer(void) { int ret = 0; mtx_lock(&sync_mtx); if (rushjob < syncdelay / 2) { rushjob += 1; stat_rush_requests += 1; ret = 1; } mtx_unlock(&sync_mtx); cv_broadcast(&sync_wakeup); return (ret); } /* * Tell the syncer to speed up its work and run though its work * list several times, then tell it to shut down. */ static void syncer_shutdown(void *arg, int howto) { if (howto & RB_NOSYNC) return; mtx_lock(&sync_mtx); syncer_state = SYNCER_SHUTTING_DOWN; rushjob = 0; mtx_unlock(&sync_mtx); cv_broadcast(&sync_wakeup); kproc_shutdown(arg, howto); } void syncer_suspend(void) { syncer_shutdown(updateproc, 0); } void syncer_resume(void) { mtx_lock(&sync_mtx); first_printf = 1; syncer_state = SYNCER_RUNNING; mtx_unlock(&sync_mtx); cv_broadcast(&sync_wakeup); kproc_resume(updateproc); } /* * Reassign a buffer from one vnode to another. * Used to assign file specific control information * (indirect blocks) to the vnode to which they belong. */ void reassignbuf(struct buf *bp) { struct vnode *vp; struct bufobj *bo; int delay; #ifdef INVARIANTS struct bufv *bv; #endif vp = bp->b_vp; bo = bp->b_bufobj; ++reassignbufcalls; CTR3(KTR_BUF, "reassignbuf(%p) vp %p flags %X", bp, bp->b_vp, bp->b_flags); /* * B_PAGING flagged buffers cannot be reassigned because their vp * is not fully linked in. */ if (bp->b_flags & B_PAGING) panic("cannot reassign paging buffer"); /* * Delete from old vnode list, if on one. */ BO_LOCK(bo); if (bp->b_xflags & (BX_VNDIRTY | BX_VNCLEAN)) buf_vlist_remove(bp); else panic("reassignbuf: Buffer %p not on queue.", bp); /* * If dirty, put on list of dirty buffers; otherwise insert onto list * of clean buffers. */ if (bp->b_flags & B_DELWRI) { if ((bo->bo_flag & BO_ONWORKLST) == 0) { switch (vp->v_type) { case VDIR: delay = dirdelay; break; case VCHR: delay = metadelay; break; default: delay = filedelay; } vn_syncer_add_to_worklist(bo, delay); } buf_vlist_add(bp, bo, BX_VNDIRTY); } else { buf_vlist_add(bp, bo, BX_VNCLEAN); if ((bo->bo_flag & BO_ONWORKLST) && bo->bo_dirty.bv_cnt == 0) { mtx_lock(&sync_mtx); LIST_REMOVE(bo, bo_synclist); syncer_worklist_len--; mtx_unlock(&sync_mtx); bo->bo_flag &= ~BO_ONWORKLST; } } #ifdef INVARIANTS bv = &bo->bo_clean; bp = TAILQ_FIRST(&bv->bv_hd); KASSERT(bp == NULL || bp->b_bufobj == bo, ("bp %p wrong b_bufobj %p should be %p", bp, bp->b_bufobj, bo)); bp = TAILQ_LAST(&bv->bv_hd, buflists); KASSERT(bp == NULL || bp->b_bufobj == bo, ("bp %p wrong b_bufobj %p should be %p", bp, bp->b_bufobj, bo)); bv = &bo->bo_dirty; bp = TAILQ_FIRST(&bv->bv_hd); KASSERT(bp == NULL || bp->b_bufobj == bo, ("bp %p wrong b_bufobj %p should be %p", bp, bp->b_bufobj, bo)); bp = TAILQ_LAST(&bv->bv_hd, buflists); KASSERT(bp == NULL || bp->b_bufobj == bo, ("bp %p wrong b_bufobj %p should be %p", bp, bp->b_bufobj, bo)); #endif BO_UNLOCK(bo); } /* * Increment the use and hold counts on the vnode, taking care to reference * the driver's usecount if this is a chardev. The vholdl() will remove * the vnode from the free list if it is presently free. Requires the * vnode interlock and returns with it held. */ static void v_incr_usecount(struct vnode *vp) { CTR2(KTR_VFS, "%s: vp %p", __func__, vp); vholdl(vp); vp->v_usecount++; if (vp->v_type == VCHR && vp->v_rdev != NULL) { dev_lock(); vp->v_rdev->si_usecount++; dev_unlock(); } } /* * Turn a holdcnt into a use+holdcnt such that only one call to * v_decr_usecount is needed. */ static void v_upgrade_usecount(struct vnode *vp) { CTR2(KTR_VFS, "%s: vp %p", __func__, vp); vp->v_usecount++; if (vp->v_type == VCHR && vp->v_rdev != NULL) { dev_lock(); vp->v_rdev->si_usecount++; dev_unlock(); } } /* * Decrement the vnode use and hold count along with the driver's usecount * if this is a chardev. The vdropl() below releases the vnode interlock * as it may free the vnode. */ static void v_decr_usecount(struct vnode *vp) { ASSERT_VI_LOCKED(vp, __FUNCTION__); VNASSERT(vp->v_usecount > 0, vp, ("v_decr_usecount: negative usecount")); CTR2(KTR_VFS, "%s: vp %p", __func__, vp); vp->v_usecount--; if (vp->v_type == VCHR && vp->v_rdev != NULL) { dev_lock(); vp->v_rdev->si_usecount--; dev_unlock(); } vdropl(vp); } /* * Decrement only the use count and driver use count. This is intended to * be paired with a follow on vdropl() to release the remaining hold count. * In this way we may vgone() a vnode with a 0 usecount without risk of * having it end up on a free list because the hold count is kept above 0. */ static void v_decr_useonly(struct vnode *vp) { ASSERT_VI_LOCKED(vp, __FUNCTION__); VNASSERT(vp->v_usecount > 0, vp, ("v_decr_useonly: negative usecount")); CTR2(KTR_VFS, "%s: vp %p", __func__, vp); vp->v_usecount--; if (vp->v_type == VCHR && vp->v_rdev != NULL) { dev_lock(); vp->v_rdev->si_usecount--; dev_unlock(); } } /* * Grab a particular vnode from the free list, increment its * reference count and lock it. VI_DOOMED is set if the vnode * is being destroyed. Only callers who specify LK_RETRY will * see doomed vnodes. If inactive processing was delayed in * vput try to do it here. */ int vget(struct vnode *vp, int flags, struct thread *td) { int error; error = 0; VNASSERT((flags & LK_TYPE_MASK) != 0, vp, ("vget: invalid lock operation")); CTR3(KTR_VFS, "%s: vp %p with flags %d", __func__, vp, flags); if ((flags & LK_INTERLOCK) == 0) VI_LOCK(vp); vholdl(vp); if ((error = vn_lock(vp, flags | LK_INTERLOCK)) != 0) { vdrop(vp); CTR2(KTR_VFS, "%s: impossible to lock vnode %p", __func__, vp); return (error); } if (vp->v_iflag & VI_DOOMED && (flags & LK_RETRY) == 0) panic("vget: vn_lock failed to return ENOENT\n"); VI_LOCK(vp); /* Upgrade our holdcnt to a usecount. */ v_upgrade_usecount(vp); /* * We don't guarantee that any particular close will * trigger inactive processing so just make a best effort * here at preventing a reference to a removed file. If * we don't succeed no harm is done. */ if (vp->v_iflag & VI_OWEINACT) { if (VOP_ISLOCKED(vp) == LK_EXCLUSIVE && (flags & LK_NOWAIT) == 0) vinactive(vp, td); vp->v_iflag &= ~VI_OWEINACT; } VI_UNLOCK(vp); return (0); } /* * Increase the reference count of a vnode. */ void vref(struct vnode *vp) { CTR2(KTR_VFS, "%s: vp %p", __func__, vp); VI_LOCK(vp); v_incr_usecount(vp); VI_UNLOCK(vp); } /* * Return reference count of a vnode. * * The results of this call are only guaranteed when some mechanism other * than the VI lock is used to stop other processes from gaining references * to the vnode. This may be the case if the caller holds the only reference. * This is also useful when stale data is acceptable as race conditions may * be accounted for by some other means. */ int vrefcnt(struct vnode *vp) { int usecnt; VI_LOCK(vp); usecnt = vp->v_usecount; VI_UNLOCK(vp); return (usecnt); } #define VPUTX_VRELE 1 #define VPUTX_VPUT 2 #define VPUTX_VUNREF 3 static void vputx(struct vnode *vp, int func) { int error; KASSERT(vp != NULL, ("vputx: null vp")); if (func == VPUTX_VUNREF) ASSERT_VOP_LOCKED(vp, "vunref"); else if (func == VPUTX_VPUT) ASSERT_VOP_LOCKED(vp, "vput"); else KASSERT(func == VPUTX_VRELE, ("vputx: wrong func")); CTR2(KTR_VFS, "%s: vp %p", __func__, vp); VI_LOCK(vp); /* Skip this v_writecount check if we're going to panic below. */ VNASSERT(vp->v_writecount < vp->v_usecount || vp->v_usecount < 1, vp, ("vputx: missed vn_close")); error = 0; if (vp->v_usecount > 1 || ((vp->v_iflag & VI_DOINGINACT) && vp->v_usecount == 1)) { if (func == VPUTX_VPUT) VOP_UNLOCK(vp, 0); v_decr_usecount(vp); return; } if (vp->v_usecount != 1) { vprint("vputx: negative ref count", vp); panic("vputx: negative ref cnt"); } CTR2(KTR_VFS, "%s: return vnode %p to the freelist", __func__, vp); /* * We want to hold the vnode until the inactive finishes to * prevent vgone() races. We drop the use count here and the * hold count below when we're done. */ v_decr_useonly(vp); /* * We must call VOP_INACTIVE with the node locked. Mark * as VI_DOINGINACT to avoid recursion. */ vp->v_iflag |= VI_OWEINACT; switch (func) { case VPUTX_VRELE: error = vn_lock(vp, LK_EXCLUSIVE | LK_INTERLOCK); VI_LOCK(vp); break; case VPUTX_VPUT: if (VOP_ISLOCKED(vp) != LK_EXCLUSIVE) { error = VOP_LOCK(vp, LK_UPGRADE | LK_INTERLOCK | LK_NOWAIT); VI_LOCK(vp); } break; case VPUTX_VUNREF: if (VOP_ISLOCKED(vp) != LK_EXCLUSIVE) { error = VOP_LOCK(vp, LK_TRYUPGRADE | LK_INTERLOCK); VI_LOCK(vp); } break; } if (vp->v_usecount > 0) vp->v_iflag &= ~VI_OWEINACT; if (error == 0) { if (vp->v_iflag & VI_OWEINACT) vinactive(vp, curthread); if (func != VPUTX_VUNREF) VOP_UNLOCK(vp, 0); } vdropl(vp); } /* * Vnode put/release. * If count drops to zero, call inactive routine and return to freelist. */ void vrele(struct vnode *vp) { vputx(vp, VPUTX_VRELE); } /* * Release an already locked vnode. This give the same effects as * unlock+vrele(), but takes less time and avoids releasing and * re-aquiring the lock (as vrele() acquires the lock internally.) */ void vput(struct vnode *vp) { vputx(vp, VPUTX_VPUT); } /* * Release an exclusively locked vnode. Do not unlock the vnode lock. */ void vunref(struct vnode *vp) { vputx(vp, VPUTX_VUNREF); } /* * Somebody doesn't want the vnode recycled. */ void vhold(struct vnode *vp) { VI_LOCK(vp); vholdl(vp); VI_UNLOCK(vp); } /* * Increase the hold count and activate if this is the first reference. */ void vholdl(struct vnode *vp) { struct mount *mp; CTR2(KTR_VFS, "%s: vp %p", __func__, vp); #ifdef INVARIANTS /* getnewvnode() calls v_incr_usecount() without holding interlock. */ if (vp->v_type != VNON || vp->v_data != NULL) { ASSERT_VI_LOCKED(vp, "vholdl"); VNASSERT(vp->v_holdcnt > 0 || (vp->v_iflag & VI_FREE) != 0, vp, ("vholdl: free vnode is held")); } #endif vp->v_holdcnt++; if ((vp->v_iflag & VI_FREE) == 0) return; VNASSERT(vp->v_holdcnt == 1, vp, ("vholdl: wrong hold count")); VNASSERT(vp->v_op != NULL, vp, ("vholdl: vnode already reclaimed.")); /* * Remove a vnode from the free list, mark it as in use, * and put it on the active list. */ mtx_lock(&vnode_free_list_mtx); TAILQ_REMOVE(&vnode_free_list, vp, v_actfreelist); freevnodes--; vp->v_iflag &= ~(VI_FREE|VI_AGE); KASSERT((vp->v_iflag & VI_ACTIVE) == 0, ("Activating already active vnode")); vp->v_iflag |= VI_ACTIVE; mp = vp->v_mount; TAILQ_INSERT_HEAD(&mp->mnt_activevnodelist, vp, v_actfreelist); mp->mnt_activevnodelistsize++; mtx_unlock(&vnode_free_list_mtx); } /* * Note that there is one less who cares about this vnode. * vdrop() is the opposite of vhold(). */ void vdrop(struct vnode *vp) { VI_LOCK(vp); vdropl(vp); } /* * Drop the hold count of the vnode. If this is the last reference to * the vnode we place it on the free list unless it has been vgone'd * (marked VI_DOOMED) in which case we will free it. */ void vdropl(struct vnode *vp) { struct bufobj *bo; struct mount *mp; int active; ASSERT_VI_LOCKED(vp, "vdropl"); CTR2(KTR_VFS, "%s: vp %p", __func__, vp); if (vp->v_holdcnt <= 0) panic("vdrop: holdcnt %d", vp->v_holdcnt); vp->v_holdcnt--; VNASSERT(vp->v_holdcnt >= vp->v_usecount, vp, ("hold count less than use count")); if (vp->v_holdcnt > 0) { VI_UNLOCK(vp); return; } if ((vp->v_iflag & VI_DOOMED) == 0) { /* * Mark a vnode as free: remove it from its active list * and put it up for recycling on the freelist. */ VNASSERT(vp->v_op != NULL, vp, ("vdropl: vnode already reclaimed.")); VNASSERT((vp->v_iflag & VI_FREE) == 0, vp, ("vnode already free")); VNASSERT(vp->v_holdcnt == 0, vp, ("vdropl: freeing when we shouldn't")); active = vp->v_iflag & VI_ACTIVE; vp->v_iflag &= ~VI_ACTIVE; mp = vp->v_mount; mtx_lock(&vnode_free_list_mtx); if (active) { TAILQ_REMOVE(&mp->mnt_activevnodelist, vp, v_actfreelist); mp->mnt_activevnodelistsize--; } if (vp->v_iflag & VI_AGE) { TAILQ_INSERT_HEAD(&vnode_free_list, vp, v_actfreelist); } else { TAILQ_INSERT_TAIL(&vnode_free_list, vp, v_actfreelist); } freevnodes++; vp->v_iflag &= ~VI_AGE; vp->v_iflag |= VI_FREE; mtx_unlock(&vnode_free_list_mtx); VI_UNLOCK(vp); return; } /* * The vnode has been marked for destruction, so free it. */ CTR2(KTR_VFS, "%s: destroying the vnode %p", __func__, vp); atomic_subtract_long(&numvnodes, 1); bo = &vp->v_bufobj; VNASSERT((vp->v_iflag & VI_FREE) == 0, vp, ("cleaned vnode still on the free list.")); VNASSERT(vp->v_data == NULL, vp, ("cleaned vnode isn't")); VNASSERT(vp->v_holdcnt == 0, vp, ("Non-zero hold count")); VNASSERT(vp->v_usecount == 0, vp, ("Non-zero use count")); VNASSERT(vp->v_writecount == 0, vp, ("Non-zero write count")); VNASSERT(bo->bo_numoutput == 0, vp, ("Clean vnode has pending I/O's")); VNASSERT(bo->bo_clean.bv_cnt == 0, vp, ("cleanbufcnt not 0")); VNASSERT(pctrie_is_empty(&bo->bo_clean.bv_root), vp, ("clean blk trie not empty")); VNASSERT(bo->bo_dirty.bv_cnt == 0, vp, ("dirtybufcnt not 0")); VNASSERT(pctrie_is_empty(&bo->bo_dirty.bv_root), vp, ("dirty blk trie not empty")); VNASSERT(TAILQ_EMPTY(&vp->v_cache_dst), vp, ("vp has namecache dst")); VNASSERT(LIST_EMPTY(&vp->v_cache_src), vp, ("vp has namecache src")); VNASSERT(vp->v_cache_dd == NULL, vp, ("vp has namecache for ..")); VI_UNLOCK(vp); #ifdef MAC mac_vnode_destroy(vp); #endif if (vp->v_pollinfo != NULL) destroy_vpollinfo(vp->v_pollinfo); #ifdef INVARIANTS /* XXX Elsewhere we detect an already freed vnode via NULL v_op. */ vp->v_op = NULL; #endif rangelock_destroy(&vp->v_rl); lockdestroy(vp->v_vnlock); mtx_destroy(&vp->v_interlock); rw_destroy(BO_LOCKPTR(bo)); uma_zfree(vnode_zone, vp); } /* * Call VOP_INACTIVE on the vnode and manage the DOINGINACT and OWEINACT * flags. DOINGINACT prevents us from recursing in calls to vinactive. * OWEINACT tracks whether a vnode missed a call to inactive due to a * failed lock upgrade. */ void vinactive(struct vnode *vp, struct thread *td) { struct vm_object *obj; ASSERT_VOP_ELOCKED(vp, "vinactive"); ASSERT_VI_LOCKED(vp, "vinactive"); VNASSERT((vp->v_iflag & VI_DOINGINACT) == 0, vp, ("vinactive: recursed on VI_DOINGINACT")); CTR2(KTR_VFS, "%s: vp %p", __func__, vp); vp->v_iflag |= VI_DOINGINACT; vp->v_iflag &= ~VI_OWEINACT; VI_UNLOCK(vp); /* * Before moving off the active list, we must be sure that any * modified pages are on the vnode's dirty list since these will * no longer be checked once the vnode is on the inactive list. * Because the vnode vm object keeps a hold reference on the vnode * if there is at least one resident non-cached page, the vnode * cannot leave the active list without the page cleanup done. */ obj = vp->v_object; if (obj != NULL && (obj->flags & OBJ_MIGHTBEDIRTY) != 0) { VM_OBJECT_WLOCK(obj); vm_object_page_clean(obj, 0, 0, OBJPC_NOSYNC); VM_OBJECT_WUNLOCK(obj); } VOP_INACTIVE(vp, td); VI_LOCK(vp); VNASSERT(vp->v_iflag & VI_DOINGINACT, vp, ("vinactive: lost VI_DOINGINACT")); vp->v_iflag &= ~VI_DOINGINACT; } /* * Remove any vnodes in the vnode table belonging to mount point mp. * * If FORCECLOSE is not specified, there should not be any active ones, * return error if any are found (nb: this is a user error, not a * system error). If FORCECLOSE is specified, detach any active vnodes * that are found. * * If WRITECLOSE is set, only flush out regular file vnodes open for * writing. * * SKIPSYSTEM causes any vnodes marked VV_SYSTEM to be skipped. * * `rootrefs' specifies the base reference count for the root vnode * of this filesystem. The root vnode is considered busy if its * v_usecount exceeds this value. On a successful return, vflush(, td) * will call vrele() on the root vnode exactly rootrefs times. * If the SKIPSYSTEM or WRITECLOSE flags are specified, rootrefs must * be zero. */ #ifdef DIAGNOSTIC static int busyprt = 0; /* print out busy vnodes */ SYSCTL_INT(_debug, OID_AUTO, busyprt, CTLFLAG_RW, &busyprt, 0, "Print out busy vnodes"); #endif int vflush(struct mount *mp, int rootrefs, int flags, struct thread *td) { struct vnode *vp, *mvp, *rootvp = NULL; struct vattr vattr; int busy = 0, error; CTR4(KTR_VFS, "%s: mp %p with rootrefs %d and flags %d", __func__, mp, rootrefs, flags); if (rootrefs > 0) { KASSERT((flags & (SKIPSYSTEM | WRITECLOSE)) == 0, ("vflush: bad args")); /* * Get the filesystem root vnode. We can vput() it * immediately, since with rootrefs > 0, it won't go away. */ if ((error = VFS_ROOT(mp, LK_EXCLUSIVE, &rootvp)) != 0) { CTR2(KTR_VFS, "%s: vfs_root lookup failed with %d", __func__, error); return (error); } vput(rootvp); } loop: MNT_VNODE_FOREACH_ALL(vp, mp, mvp) { vholdl(vp); error = vn_lock(vp, LK_INTERLOCK | LK_EXCLUSIVE); if (error) { vdrop(vp); MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp); goto loop; } /* * Skip over a vnodes marked VV_SYSTEM. */ if ((flags & SKIPSYSTEM) && (vp->v_vflag & VV_SYSTEM)) { VOP_UNLOCK(vp, 0); vdrop(vp); continue; } /* * If WRITECLOSE is set, flush out unlinked but still open * files (even if open only for reading) and regular file * vnodes open for writing. */ if (flags & WRITECLOSE) { if (vp->v_object != NULL) { VM_OBJECT_WLOCK(vp->v_object); vm_object_page_clean(vp->v_object, 0, 0, 0); VM_OBJECT_WUNLOCK(vp->v_object); } error = VOP_FSYNC(vp, MNT_WAIT, td); if (error != 0) { VOP_UNLOCK(vp, 0); vdrop(vp); MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp); return (error); } error = VOP_GETATTR(vp, &vattr, td->td_ucred); VI_LOCK(vp); if ((vp->v_type == VNON || (error == 0 && vattr.va_nlink > 0)) && (vp->v_writecount == 0 || vp->v_type != VREG)) { VOP_UNLOCK(vp, 0); vdropl(vp); continue; } } else VI_LOCK(vp); /* * With v_usecount == 0, all we need to do is clear out the * vnode data structures and we are done. * * If FORCECLOSE is set, forcibly close the vnode. */ if (vp->v_usecount == 0 || (flags & FORCECLOSE)) { VNASSERT(vp->v_usecount == 0 || (vp->v_type != VCHR && vp->v_type != VBLK), vp, ("device VNODE %p is FORCECLOSED", vp)); vgonel(vp); } else { busy++; #ifdef DIAGNOSTIC if (busyprt) vprint("vflush: busy vnode", vp); #endif } VOP_UNLOCK(vp, 0); vdropl(vp); } if (rootrefs > 0 && (flags & FORCECLOSE) == 0) { /* * If just the root vnode is busy, and if its refcount * is equal to `rootrefs', then go ahead and kill it. */ VI_LOCK(rootvp); KASSERT(busy > 0, ("vflush: not busy")); VNASSERT(rootvp->v_usecount >= rootrefs, rootvp, ("vflush: usecount %d < rootrefs %d", rootvp->v_usecount, rootrefs)); if (busy == 1 && rootvp->v_usecount == rootrefs) { VOP_LOCK(rootvp, LK_EXCLUSIVE|LK_INTERLOCK); vgone(rootvp); VOP_UNLOCK(rootvp, 0); busy = 0; } else VI_UNLOCK(rootvp); } if (busy) { CTR2(KTR_VFS, "%s: failing as %d vnodes are busy", __func__, busy); return (EBUSY); } for (; rootrefs > 0; rootrefs--) vrele(rootvp); return (0); } /* * Recycle an unused vnode to the front of the free list. */ int vrecycle(struct vnode *vp) { int recycled; ASSERT_VOP_ELOCKED(vp, "vrecycle"); CTR2(KTR_VFS, "%s: vp %p", __func__, vp); recycled = 0; VI_LOCK(vp); if (vp->v_usecount == 0) { recycled = 1; vgonel(vp); } VI_UNLOCK(vp); return (recycled); } /* * Eliminate all activity associated with a vnode * in preparation for reuse. */ void vgone(struct vnode *vp) { VI_LOCK(vp); vgonel(vp); VI_UNLOCK(vp); } static void notify_lowervp_vfs_dummy(struct mount *mp __unused, struct vnode *lowervp __unused) { } /* * Notify upper mounts about reclaimed or unlinked vnode. */ void vfs_notify_upper(struct vnode *vp, int event) { static struct vfsops vgonel_vfsops = { .vfs_reclaim_lowervp = notify_lowervp_vfs_dummy, .vfs_unlink_lowervp = notify_lowervp_vfs_dummy, }; struct mount *mp, *ump, *mmp; mp = vp->v_mount; if (mp == NULL) return; MNT_ILOCK(mp); if (TAILQ_EMPTY(&mp->mnt_uppers)) goto unlock; MNT_IUNLOCK(mp); mmp = malloc(sizeof(struct mount), M_TEMP, M_WAITOK | M_ZERO); mmp->mnt_op = &vgonel_vfsops; mmp->mnt_kern_flag |= MNTK_MARKER; MNT_ILOCK(mp); mp->mnt_kern_flag |= MNTK_VGONE_UPPER; for (ump = TAILQ_FIRST(&mp->mnt_uppers); ump != NULL;) { if ((ump->mnt_kern_flag & MNTK_MARKER) != 0) { ump = TAILQ_NEXT(ump, mnt_upper_link); continue; } TAILQ_INSERT_AFTER(&mp->mnt_uppers, ump, mmp, mnt_upper_link); MNT_IUNLOCK(mp); switch (event) { case VFS_NOTIFY_UPPER_RECLAIM: VFS_RECLAIM_LOWERVP(ump, vp); break; case VFS_NOTIFY_UPPER_UNLINK: VFS_UNLINK_LOWERVP(ump, vp); break; default: KASSERT(0, ("invalid event %d", event)); break; } MNT_ILOCK(mp); ump = TAILQ_NEXT(mmp, mnt_upper_link); TAILQ_REMOVE(&mp->mnt_uppers, mmp, mnt_upper_link); } free(mmp, M_TEMP); mp->mnt_kern_flag &= ~MNTK_VGONE_UPPER; if ((mp->mnt_kern_flag & MNTK_VGONE_WAITER) != 0) { mp->mnt_kern_flag &= ~MNTK_VGONE_WAITER; wakeup(&mp->mnt_uppers); } unlock: MNT_IUNLOCK(mp); } /* * vgone, with the vp interlock held. */ void vgonel(struct vnode *vp) { struct thread *td; int oweinact; int active; struct mount *mp; ASSERT_VOP_ELOCKED(vp, "vgonel"); ASSERT_VI_LOCKED(vp, "vgonel"); VNASSERT(vp->v_holdcnt, vp, ("vgonel: vp %p has no reference.", vp)); CTR2(KTR_VFS, "%s: vp %p", __func__, vp); td = curthread; /* * Don't vgonel if we're already doomed. */ if (vp->v_iflag & VI_DOOMED) return; vp->v_iflag |= VI_DOOMED; /* * Check to see if the vnode is in use. If so, we have to call * VOP_CLOSE() and VOP_INACTIVE(). */ active = vp->v_usecount; oweinact = (vp->v_iflag & VI_OWEINACT); VI_UNLOCK(vp); vfs_notify_upper(vp, VFS_NOTIFY_UPPER_RECLAIM); /* * If purging an active vnode, it must be closed and * deactivated before being reclaimed. */ if (active) VOP_CLOSE(vp, FNONBLOCK, NOCRED, td); if (oweinact || active) { VI_LOCK(vp); if ((vp->v_iflag & VI_DOINGINACT) == 0) vinactive(vp, td); VI_UNLOCK(vp); } if (vp->v_type == VSOCK) vfs_unp_reclaim(vp); /* * Clean out any buffers associated with the vnode. * If the flush fails, just toss the buffers. */ mp = NULL; if (!TAILQ_EMPTY(&vp->v_bufobj.bo_dirty.bv_hd)) (void) vn_start_secondary_write(vp, &mp, V_WAIT); if (vinvalbuf(vp, V_SAVE, 0, 0) != 0) { while (vinvalbuf(vp, 0, 0, 0) != 0) ; } #ifdef INVARIANTS BO_LOCK(&vp->v_bufobj); KASSERT(TAILQ_EMPTY(&vp->v_bufobj.bo_dirty.bv_hd) && vp->v_bufobj.bo_dirty.bv_cnt == 0 && TAILQ_EMPTY(&vp->v_bufobj.bo_clean.bv_hd) && vp->v_bufobj.bo_clean.bv_cnt == 0, ("vp %p bufobj not invalidated", vp)); vp->v_bufobj.bo_flag |= BO_DEAD; BO_UNLOCK(&vp->v_bufobj); #endif /* * Reclaim the vnode. */ if (VOP_RECLAIM(vp, td)) panic("vgone: cannot reclaim"); if (mp != NULL) vn_finished_secondary_write(mp); VNASSERT(vp->v_object == NULL, vp, ("vop_reclaim left v_object vp=%p, tag=%s", vp, vp->v_tag)); /* * Clear the advisory locks and wake up waiting threads. */ (void)VOP_ADVLOCKPURGE(vp); /* * Delete from old mount point vnode list. */ delmntque(vp); cache_purge(vp); /* * Done with purge, reset to the standard lock and invalidate * the vnode. */ VI_LOCK(vp); vp->v_vnlock = &vp->v_lock; vp->v_op = &dead_vnodeops; vp->v_tag = "none"; vp->v_type = VBAD; } /* * Calculate the total number of references to a special device. */ int vcount(struct vnode *vp) { int count; dev_lock(); count = vp->v_rdev->si_usecount; dev_unlock(); return (count); } /* * Same as above, but using the struct cdev *as argument */ int count_dev(struct cdev *dev) { int count; dev_lock(); count = dev->si_usecount; dev_unlock(); return(count); } /* * Print out a description of a vnode. */ static char *typename[] = {"VNON", "VREG", "VDIR", "VBLK", "VCHR", "VLNK", "VSOCK", "VFIFO", "VBAD", "VMARKER"}; void vn_printf(struct vnode *vp, const char *fmt, ...) { va_list ap; char buf[256], buf2[16]; u_long flags; va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); printf("%p: ", (void *)vp); printf("tag %s, type %s\n", vp->v_tag, typename[vp->v_type]); printf(" usecount %d, writecount %d, refcount %d mountedhere %p\n", vp->v_usecount, vp->v_writecount, vp->v_holdcnt, vp->v_mountedhere); buf[0] = '\0'; buf[1] = '\0'; if (vp->v_vflag & VV_ROOT) strlcat(buf, "|VV_ROOT", sizeof(buf)); if (vp->v_vflag & VV_ISTTY) strlcat(buf, "|VV_ISTTY", sizeof(buf)); if (vp->v_vflag & VV_NOSYNC) strlcat(buf, "|VV_NOSYNC", sizeof(buf)); if (vp->v_vflag & VV_ETERNALDEV) strlcat(buf, "|VV_ETERNALDEV", sizeof(buf)); if (vp->v_vflag & VV_CACHEDLABEL) strlcat(buf, "|VV_CACHEDLABEL", sizeof(buf)); if (vp->v_vflag & VV_TEXT) strlcat(buf, "|VV_TEXT", sizeof(buf)); if (vp->v_vflag & VV_COPYONWRITE) strlcat(buf, "|VV_COPYONWRITE", sizeof(buf)); if (vp->v_vflag & VV_SYSTEM) strlcat(buf, "|VV_SYSTEM", sizeof(buf)); if (vp->v_vflag & VV_PROCDEP) strlcat(buf, "|VV_PROCDEP", sizeof(buf)); if (vp->v_vflag & VV_NOKNOTE) strlcat(buf, "|VV_NOKNOTE", sizeof(buf)); if (vp->v_vflag & VV_DELETED) strlcat(buf, "|VV_DELETED", sizeof(buf)); if (vp->v_vflag & VV_MD) strlcat(buf, "|VV_MD", sizeof(buf)); if (vp->v_vflag & VV_FORCEINSMQ) strlcat(buf, "|VV_FORCEINSMQ", sizeof(buf)); flags = vp->v_vflag & ~(VV_ROOT | VV_ISTTY | VV_NOSYNC | VV_ETERNALDEV | VV_CACHEDLABEL | VV_TEXT | VV_COPYONWRITE | VV_SYSTEM | VV_PROCDEP | VV_NOKNOTE | VV_DELETED | VV_MD | VV_FORCEINSMQ); if (flags != 0) { snprintf(buf2, sizeof(buf2), "|VV(0x%lx)", flags); strlcat(buf, buf2, sizeof(buf)); } if (vp->v_iflag & VI_MOUNT) strlcat(buf, "|VI_MOUNT", sizeof(buf)); if (vp->v_iflag & VI_AGE) strlcat(buf, "|VI_AGE", sizeof(buf)); if (vp->v_iflag & VI_DOOMED) strlcat(buf, "|VI_DOOMED", sizeof(buf)); if (vp->v_iflag & VI_FREE) strlcat(buf, "|VI_FREE", sizeof(buf)); if (vp->v_iflag & VI_ACTIVE) strlcat(buf, "|VI_ACTIVE", sizeof(buf)); if (vp->v_iflag & VI_DOINGINACT) strlcat(buf, "|VI_DOINGINACT", sizeof(buf)); if (vp->v_iflag & VI_OWEINACT) strlcat(buf, "|VI_OWEINACT", sizeof(buf)); flags = vp->v_iflag & ~(VI_MOUNT | VI_AGE | VI_DOOMED | VI_FREE | VI_ACTIVE | VI_DOINGINACT | VI_OWEINACT); if (flags != 0) { snprintf(buf2, sizeof(buf2), "|VI(0x%lx)", flags); strlcat(buf, buf2, sizeof(buf)); } printf(" flags (%s)\n", buf + 1); if (mtx_owned(VI_MTX(vp))) printf(" VI_LOCKed"); if (vp->v_object != NULL) printf(" v_object %p ref %d pages %d " "cleanbuf %d dirtybuf %d\n", vp->v_object, vp->v_object->ref_count, vp->v_object->resident_page_count, vp->v_bufobj.bo_dirty.bv_cnt, vp->v_bufobj.bo_clean.bv_cnt); printf(" "); lockmgr_printinfo(vp->v_vnlock); if (vp->v_data != NULL) VOP_PRINT(vp); } #ifdef DDB /* * List all of the locked vnodes in the system. * Called when debugging the kernel. */ DB_SHOW_COMMAND(lockedvnods, lockedvnodes) { struct mount *mp; struct vnode *vp; /* * Note: because this is DDB, we can't obey the locking semantics * for these structures, which means we could catch an inconsistent * state and dereference a nasty pointer. Not much to be done * about that. */ db_printf("Locked vnodes\n"); TAILQ_FOREACH(mp, &mountlist, mnt_list) { TAILQ_FOREACH(vp, &mp->mnt_nvnodelist, v_nmntvnodes) { if (vp->v_type != VMARKER && VOP_ISLOCKED(vp)) vprint("", vp); } } } /* * Show details about the given vnode. */ DB_SHOW_COMMAND(vnode, db_show_vnode) { struct vnode *vp; if (!have_addr) return; vp = (struct vnode *)addr; vn_printf(vp, "vnode "); } /* * Show details about the given mount point. */ DB_SHOW_COMMAND(mount, db_show_mount) { struct mount *mp; struct vfsopt *opt; struct statfs *sp; struct vnode *vp; char buf[512]; uint64_t mflags; u_int flags; if (!have_addr) { /* No address given, print short info about all mount points. */ TAILQ_FOREACH(mp, &mountlist, mnt_list) { db_printf("%p %s on %s (%s)\n", mp, mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname, mp->mnt_stat.f_fstypename); if (db_pager_quit) break; } db_printf("\nMore info: show mount \n"); return; } mp = (struct mount *)addr; db_printf("%p %s on %s (%s)\n", mp, mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname, mp->mnt_stat.f_fstypename); buf[0] = '\0'; mflags = mp->mnt_flag; #define MNT_FLAG(flag) do { \ if (mflags & (flag)) { \ if (buf[0] != '\0') \ strlcat(buf, ", ", sizeof(buf)); \ strlcat(buf, (#flag) + 4, sizeof(buf)); \ mflags &= ~(flag); \ } \ } while (0) MNT_FLAG(MNT_RDONLY); MNT_FLAG(MNT_SYNCHRONOUS); MNT_FLAG(MNT_NOEXEC); MNT_FLAG(MNT_NOSUID); MNT_FLAG(MNT_NFS4ACLS); MNT_FLAG(MNT_UNION); MNT_FLAG(MNT_ASYNC); MNT_FLAG(MNT_SUIDDIR); MNT_FLAG(MNT_SOFTDEP); MNT_FLAG(MNT_NOSYMFOLLOW); MNT_FLAG(MNT_GJOURNAL); MNT_FLAG(MNT_MULTILABEL); MNT_FLAG(MNT_ACLS); MNT_FLAG(MNT_NOATIME); MNT_FLAG(MNT_NOCLUSTERR); MNT_FLAG(MNT_NOCLUSTERW); MNT_FLAG(MNT_SUJ); MNT_FLAG(MNT_EXRDONLY); MNT_FLAG(MNT_EXPORTED); MNT_FLAG(MNT_DEFEXPORTED); MNT_FLAG(MNT_EXPORTANON); MNT_FLAG(MNT_EXKERB); MNT_FLAG(MNT_EXPUBLIC); MNT_FLAG(MNT_LOCAL); MNT_FLAG(MNT_QUOTA); MNT_FLAG(MNT_ROOTFS); MNT_FLAG(MNT_USER); MNT_FLAG(MNT_IGNORE); MNT_FLAG(MNT_UPDATE); MNT_FLAG(MNT_DELEXPORT); MNT_FLAG(MNT_RELOAD); MNT_FLAG(MNT_FORCE); MNT_FLAG(MNT_SNAPSHOT); MNT_FLAG(MNT_BYFSID); #undef MNT_FLAG if (mflags != 0) { if (buf[0] != '\0') strlcat(buf, ", ", sizeof(buf)); snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "0x%016jx", mflags); } db_printf(" mnt_flag = %s\n", buf); buf[0] = '\0'; flags = mp->mnt_kern_flag; #define MNT_KERN_FLAG(flag) do { \ if (flags & (flag)) { \ if (buf[0] != '\0') \ strlcat(buf, ", ", sizeof(buf)); \ strlcat(buf, (#flag) + 5, sizeof(buf)); \ flags &= ~(flag); \ } \ } while (0) MNT_KERN_FLAG(MNTK_UNMOUNTF); MNT_KERN_FLAG(MNTK_ASYNC); MNT_KERN_FLAG(MNTK_SOFTDEP); MNT_KERN_FLAG(MNTK_NOINSMNTQ); MNT_KERN_FLAG(MNTK_DRAINING); MNT_KERN_FLAG(MNTK_REFEXPIRE); MNT_KERN_FLAG(MNTK_EXTENDED_SHARED); MNT_KERN_FLAG(MNTK_SHARED_WRITES); MNT_KERN_FLAG(MNTK_NO_IOPF); MNT_KERN_FLAG(MNTK_VGONE_UPPER); MNT_KERN_FLAG(MNTK_VGONE_WAITER); MNT_KERN_FLAG(MNTK_LOOKUP_EXCL_DOTDOT); MNT_KERN_FLAG(MNTK_MARKER); MNT_KERN_FLAG(MNTK_NOASYNC); MNT_KERN_FLAG(MNTK_UNMOUNT); MNT_KERN_FLAG(MNTK_MWAIT); MNT_KERN_FLAG(MNTK_SUSPEND); MNT_KERN_FLAG(MNTK_SUSPEND2); MNT_KERN_FLAG(MNTK_SUSPENDED); MNT_KERN_FLAG(MNTK_LOOKUP_SHARED); MNT_KERN_FLAG(MNTK_NOKNOTE); #undef MNT_KERN_FLAG if (flags != 0) { if (buf[0] != '\0') strlcat(buf, ", ", sizeof(buf)); snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "0x%08x", flags); } db_printf(" mnt_kern_flag = %s\n", buf); db_printf(" mnt_opt = "); opt = TAILQ_FIRST(mp->mnt_opt); if (opt != NULL) { db_printf("%s", opt->name); opt = TAILQ_NEXT(opt, link); while (opt != NULL) { db_printf(", %s", opt->name); opt = TAILQ_NEXT(opt, link); } } db_printf("\n"); sp = &mp->mnt_stat; db_printf(" mnt_stat = { version=%u type=%u flags=0x%016jx " "bsize=%ju iosize=%ju blocks=%ju bfree=%ju bavail=%jd files=%ju " "ffree=%jd syncwrites=%ju asyncwrites=%ju syncreads=%ju " "asyncreads=%ju namemax=%u owner=%u fsid=[%d, %d] }\n", (u_int)sp->f_version, (u_int)sp->f_type, (uintmax_t)sp->f_flags, (uintmax_t)sp->f_bsize, (uintmax_t)sp->f_iosize, (uintmax_t)sp->f_blocks, (uintmax_t)sp->f_bfree, (intmax_t)sp->f_bavail, (uintmax_t)sp->f_files, (intmax_t)sp->f_ffree, (uintmax_t)sp->f_syncwrites, (uintmax_t)sp->f_asyncwrites, (uintmax_t)sp->f_syncreads, (uintmax_t)sp->f_asyncreads, (u_int)sp->f_namemax, (u_int)sp->f_owner, (int)sp->f_fsid.val[0], (int)sp->f_fsid.val[1]); db_printf(" mnt_cred = { uid=%u ruid=%u", (u_int)mp->mnt_cred->cr_uid, (u_int)mp->mnt_cred->cr_ruid); if (jailed(mp->mnt_cred)) db_printf(", jail=%d", mp->mnt_cred->cr_prison->pr_id); db_printf(" }\n"); db_printf(" mnt_ref = %d\n", mp->mnt_ref); db_printf(" mnt_gen = %d\n", mp->mnt_gen); db_printf(" mnt_nvnodelistsize = %d\n", mp->mnt_nvnodelistsize); db_printf(" mnt_activevnodelistsize = %d\n", mp->mnt_activevnodelistsize); db_printf(" mnt_writeopcount = %d\n", mp->mnt_writeopcount); db_printf(" mnt_maxsymlinklen = %d\n", mp->mnt_maxsymlinklen); db_printf(" mnt_iosize_max = %d\n", mp->mnt_iosize_max); db_printf(" mnt_hashseed = %u\n", mp->mnt_hashseed); db_printf(" mnt_secondary_writes = %d\n", mp->mnt_secondary_writes); db_printf(" mnt_secondary_accwrites = %d\n", mp->mnt_secondary_accwrites); db_printf(" mnt_gjprovider = %s\n", mp->mnt_gjprovider != NULL ? mp->mnt_gjprovider : "NULL"); db_printf("\n\nList of active vnodes\n"); TAILQ_FOREACH(vp, &mp->mnt_activevnodelist, v_actfreelist) { if (vp->v_type != VMARKER) { vn_printf(vp, "vnode "); if (db_pager_quit) break; } } db_printf("\n\nList of inactive vnodes\n"); TAILQ_FOREACH(vp, &mp->mnt_nvnodelist, v_nmntvnodes) { if (vp->v_type != VMARKER && (vp->v_iflag & VI_ACTIVE) == 0) { vn_printf(vp, "vnode "); if (db_pager_quit) break; } } } #endif /* DDB */ /* * Fill in a struct xvfsconf based on a struct vfsconf. */ static int vfsconf2x(struct sysctl_req *req, struct vfsconf *vfsp) { struct xvfsconf xvfsp; bzero(&xvfsp, sizeof(xvfsp)); strcpy(xvfsp.vfc_name, vfsp->vfc_name); xvfsp.vfc_typenum = vfsp->vfc_typenum; xvfsp.vfc_refcount = vfsp->vfc_refcount; xvfsp.vfc_flags = vfsp->vfc_flags; /* * These are unused in userland, we keep them * to not break binary compatibility. */ xvfsp.vfc_vfsops = NULL; xvfsp.vfc_next = NULL; return (SYSCTL_OUT(req, &xvfsp, sizeof(xvfsp))); } #ifdef COMPAT_FREEBSD32 struct xvfsconf32 { uint32_t vfc_vfsops; char vfc_name[MFSNAMELEN]; int32_t vfc_typenum; int32_t vfc_refcount; int32_t vfc_flags; uint32_t vfc_next; }; static int vfsconf2x32(struct sysctl_req *req, struct vfsconf *vfsp) { struct xvfsconf32 xvfsp; strcpy(xvfsp.vfc_name, vfsp->vfc_name); xvfsp.vfc_typenum = vfsp->vfc_typenum; xvfsp.vfc_refcount = vfsp->vfc_refcount; xvfsp.vfc_flags = vfsp->vfc_flags; xvfsp.vfc_vfsops = 0; xvfsp.vfc_next = 0; return (SYSCTL_OUT(req, &xvfsp, sizeof(xvfsp))); } #endif /* * Top level filesystem related information gathering. */ static int sysctl_vfs_conflist(SYSCTL_HANDLER_ARGS) { struct vfsconf *vfsp; int error; error = 0; vfsconf_slock(); TAILQ_FOREACH(vfsp, &vfsconf, vfc_list) { #ifdef COMPAT_FREEBSD32 if (req->flags & SCTL_MASK32) error = vfsconf2x32(req, vfsp); else #endif error = vfsconf2x(req, vfsp); if (error) break; } vfsconf_sunlock(); return (error); } SYSCTL_PROC(_vfs, OID_AUTO, conflist, CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0, sysctl_vfs_conflist, "S,xvfsconf", "List of all configured filesystems"); #ifndef BURN_BRIDGES static int sysctl_ovfs_conf(SYSCTL_HANDLER_ARGS); static int vfs_sysctl(SYSCTL_HANDLER_ARGS) { int *name = (int *)arg1 - 1; /* XXX */ u_int namelen = arg2 + 1; /* XXX */ struct vfsconf *vfsp; log(LOG_WARNING, "userland calling deprecated sysctl, " "please rebuild world\n"); #if 1 || defined(COMPAT_PRELITE2) /* Resolve ambiguity between VFS_VFSCONF and VFS_GENERIC. */ if (namelen == 1) return (sysctl_ovfs_conf(oidp, arg1, arg2, req)); #endif switch (name[1]) { case VFS_MAXTYPENUM: if (namelen != 2) return (ENOTDIR); return (SYSCTL_OUT(req, &maxvfsconf, sizeof(int))); case VFS_CONF: if (namelen != 3) return (ENOTDIR); /* overloaded */ vfsconf_slock(); TAILQ_FOREACH(vfsp, &vfsconf, vfc_list) { if (vfsp->vfc_typenum == name[2]) break; } vfsconf_sunlock(); if (vfsp == NULL) return (EOPNOTSUPP); #ifdef COMPAT_FREEBSD32 if (req->flags & SCTL_MASK32) return (vfsconf2x32(req, vfsp)); else #endif return (vfsconf2x(req, vfsp)); } return (EOPNOTSUPP); } static SYSCTL_NODE(_vfs, VFS_GENERIC, generic, CTLFLAG_RD | CTLFLAG_SKIP | CTLFLAG_MPSAFE, vfs_sysctl, "Generic filesystem"); #if 1 || defined(COMPAT_PRELITE2) static int sysctl_ovfs_conf(SYSCTL_HANDLER_ARGS) { int error; struct vfsconf *vfsp; struct ovfsconf ovfs; vfsconf_slock(); TAILQ_FOREACH(vfsp, &vfsconf, vfc_list) { bzero(&ovfs, sizeof(ovfs)); ovfs.vfc_vfsops = vfsp->vfc_vfsops; /* XXX used as flag */ strcpy(ovfs.vfc_name, vfsp->vfc_name); ovfs.vfc_index = vfsp->vfc_typenum; ovfs.vfc_refcount = vfsp->vfc_refcount; ovfs.vfc_flags = vfsp->vfc_flags; error = SYSCTL_OUT(req, &ovfs, sizeof ovfs); if (error != 0) { vfsconf_sunlock(); return (error); } } vfsconf_sunlock(); return (0); } #endif /* 1 || COMPAT_PRELITE2 */ #endif /* !BURN_BRIDGES */ #define KINFO_VNODESLOP 10 #ifdef notyet /* * Dump vnode list (via sysctl). */ /* ARGSUSED */ static int sysctl_vnode(SYSCTL_HANDLER_ARGS) { struct xvnode *xvn; struct mount *mp; struct vnode *vp; int error, len, n; /* * Stale numvnodes access is not fatal here. */ req->lock = 0; len = (numvnodes + KINFO_VNODESLOP) * sizeof *xvn; if (!req->oldptr) /* Make an estimate */ return (SYSCTL_OUT(req, 0, len)); error = sysctl_wire_old_buffer(req, 0); if (error != 0) return (error); xvn = malloc(len, M_TEMP, M_ZERO | M_WAITOK); n = 0; mtx_lock(&mountlist_mtx); TAILQ_FOREACH(mp, &mountlist, mnt_list) { if (vfs_busy(mp, MBF_NOWAIT | MBF_MNTLSTLOCK)) continue; MNT_ILOCK(mp); TAILQ_FOREACH(vp, &mp->mnt_nvnodelist, v_nmntvnodes) { if (n == len) break; vref(vp); xvn[n].xv_size = sizeof *xvn; xvn[n].xv_vnode = vp; xvn[n].xv_id = 0; /* XXX compat */ #define XV_COPY(field) xvn[n].xv_##field = vp->v_##field XV_COPY(usecount); XV_COPY(writecount); XV_COPY(holdcnt); XV_COPY(mount); XV_COPY(numoutput); XV_COPY(type); #undef XV_COPY xvn[n].xv_flag = vp->v_vflag; switch (vp->v_type) { case VREG: case VDIR: case VLNK: break; case VBLK: case VCHR: if (vp->v_rdev == NULL) { vrele(vp); continue; } xvn[n].xv_dev = dev2udev(vp->v_rdev); break; case VSOCK: xvn[n].xv_socket = vp->v_socket; break; case VFIFO: xvn[n].xv_fifo = vp->v_fifoinfo; break; case VNON: case VBAD: default: /* shouldn't happen? */ vrele(vp); continue; } vrele(vp); ++n; } MNT_IUNLOCK(mp); mtx_lock(&mountlist_mtx); vfs_unbusy(mp); if (n == len) break; } mtx_unlock(&mountlist_mtx); error = SYSCTL_OUT(req, xvn, n * sizeof *xvn); free(xvn, M_TEMP); return (error); } SYSCTL_PROC(_kern, KERN_VNODE, vnode, CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE, 0, 0, sysctl_vnode, "S,xvnode", ""); #endif /* * Unmount all filesystems. The list is traversed in reverse order * of mounting to avoid dependencies. */ void vfs_unmountall(void) { struct mount *mp; struct thread *td; int error; CTR1(KTR_VFS, "%s: unmounting all filesystems", __func__); td = curthread; /* * Since this only runs when rebooting, it is not interlocked. */ while(!TAILQ_EMPTY(&mountlist)) { mp = TAILQ_LAST(&mountlist, mntlist); error = dounmount(mp, MNT_FORCE, td); if (error) { TAILQ_REMOVE(&mountlist, mp, mnt_list); /* * XXX: Due to the way in which we mount the root * file system off of devfs, devfs will generate a * "busy" warning when we try to unmount it before * the root. Don't print a warning as a result in * order to avoid false positive errors that may * cause needless upset. */ if (strcmp(mp->mnt_vfc->vfc_name, "devfs") != 0) { printf("unmount of %s failed (", mp->mnt_stat.f_mntonname); if (error == EBUSY) printf("BUSY)\n"); else printf("%d)\n", error); } } else { /* The unmount has removed mp from the mountlist */ } } } /* * perform msync on all vnodes under a mount point * the mount point must be locked. */ void vfs_msync(struct mount *mp, int flags) { struct vnode *vp, *mvp; struct vm_object *obj; CTR2(KTR_VFS, "%s: mp %p", __func__, mp); MNT_VNODE_FOREACH_ACTIVE(vp, mp, mvp) { obj = vp->v_object; if (obj != NULL && (obj->flags & OBJ_MIGHTBEDIRTY) != 0 && (flags == MNT_WAIT || VOP_ISLOCKED(vp) == 0)) { if (!vget(vp, LK_EXCLUSIVE | LK_RETRY | LK_INTERLOCK, curthread)) { if (vp->v_vflag & VV_NOSYNC) { /* unlinked */ vput(vp); continue; } obj = vp->v_object; if (obj != NULL) { VM_OBJECT_WLOCK(obj); vm_object_page_clean(obj, 0, 0, flags == MNT_WAIT ? OBJPC_SYNC : OBJPC_NOSYNC); VM_OBJECT_WUNLOCK(obj); } vput(vp); } } else VI_UNLOCK(vp); } } static void destroy_vpollinfo_free(struct vpollinfo *vi) { knlist_destroy(&vi->vpi_selinfo.si_note); mtx_destroy(&vi->vpi_lock); uma_zfree(vnodepoll_zone, vi); } static void destroy_vpollinfo(struct vpollinfo *vi) { knlist_clear(&vi->vpi_selinfo.si_note, 1); seldrain(&vi->vpi_selinfo); destroy_vpollinfo_free(vi); } /* * Initalize per-vnode helper structure to hold poll-related state. */ void v_addpollinfo(struct vnode *vp) { struct vpollinfo *vi; if (vp->v_pollinfo != NULL) return; vi = uma_zalloc(vnodepoll_zone, M_WAITOK); mtx_init(&vi->vpi_lock, "vnode pollinfo", NULL, MTX_DEF); knlist_init(&vi->vpi_selinfo.si_note, vp, vfs_knllock, vfs_knlunlock, vfs_knl_assert_locked, vfs_knl_assert_unlocked); VI_LOCK(vp); if (vp->v_pollinfo != NULL) { VI_UNLOCK(vp); destroy_vpollinfo_free(vi); return; } vp->v_pollinfo = vi; VI_UNLOCK(vp); } /* * Record a process's interest in events which might happen to * a vnode. Because poll uses the historic select-style interface * internally, this routine serves as both the ``check for any * pending events'' and the ``record my interest in future events'' * functions. (These are done together, while the lock is held, * to avoid race conditions.) */ int vn_pollrecord(struct vnode *vp, struct thread *td, int events) { v_addpollinfo(vp); mtx_lock(&vp->v_pollinfo->vpi_lock); if (vp->v_pollinfo->vpi_revents & events) { /* * This leaves events we are not interested * in available for the other process which * which presumably had requested them * (otherwise they would never have been * recorded). */ events &= vp->v_pollinfo->vpi_revents; vp->v_pollinfo->vpi_revents &= ~events; mtx_unlock(&vp->v_pollinfo->vpi_lock); return (events); } vp->v_pollinfo->vpi_events |= events; selrecord(td, &vp->v_pollinfo->vpi_selinfo); mtx_unlock(&vp->v_pollinfo->vpi_lock); return (0); } /* * Routine to create and manage a filesystem syncer vnode. */ #define sync_close ((int (*)(struct vop_close_args *))nullop) static int sync_fsync(struct vop_fsync_args *); static int sync_inactive(struct vop_inactive_args *); static int sync_reclaim(struct vop_reclaim_args *); static struct vop_vector sync_vnodeops = { .vop_bypass = VOP_EOPNOTSUPP, .vop_close = sync_close, /* close */ .vop_fsync = sync_fsync, /* fsync */ .vop_inactive = sync_inactive, /* inactive */ .vop_reclaim = sync_reclaim, /* reclaim */ .vop_lock1 = vop_stdlock, /* lock */ .vop_unlock = vop_stdunlock, /* unlock */ .vop_islocked = vop_stdislocked, /* islocked */ }; /* * Create a new filesystem syncer vnode for the specified mount point. */ void vfs_allocate_syncvnode(struct mount *mp) { struct vnode *vp; struct bufobj *bo; static long start, incr, next; int error; /* Allocate a new vnode */ error = getnewvnode("syncer", mp, &sync_vnodeops, &vp); if (error != 0) panic("vfs_allocate_syncvnode: getnewvnode() failed"); vp->v_type = VNON; vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); vp->v_vflag |= VV_FORCEINSMQ; error = insmntque(vp, mp); if (error != 0) panic("vfs_allocate_syncvnode: insmntque() failed"); vp->v_vflag &= ~VV_FORCEINSMQ; VOP_UNLOCK(vp, 0); /* * Place the vnode onto the syncer worklist. We attempt to * scatter them about on the list so that they will go off * at evenly distributed times even if all the filesystems * are mounted at once. */ next += incr; if (next == 0 || next > syncer_maxdelay) { start /= 2; incr /= 2; if (start == 0) { start = syncer_maxdelay / 2; incr = syncer_maxdelay; } next = start; } bo = &vp->v_bufobj; BO_LOCK(bo); vn_syncer_add_to_worklist(bo, syncdelay > 0 ? next % syncdelay : 0); /* XXX - vn_syncer_add_to_worklist() also grabs and drops sync_mtx. */ mtx_lock(&sync_mtx); sync_vnode_count++; if (mp->mnt_syncer == NULL) { mp->mnt_syncer = vp; vp = NULL; } mtx_unlock(&sync_mtx); BO_UNLOCK(bo); if (vp != NULL) { vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); vgone(vp); vput(vp); } } void vfs_deallocate_syncvnode(struct mount *mp) { struct vnode *vp; mtx_lock(&sync_mtx); vp = mp->mnt_syncer; if (vp != NULL) mp->mnt_syncer = NULL; mtx_unlock(&sync_mtx); if (vp != NULL) vrele(vp); } /* * Do a lazy sync of the filesystem. */ static int sync_fsync(struct vop_fsync_args *ap) { struct vnode *syncvp = ap->a_vp; struct mount *mp = syncvp->v_mount; int error, save; struct bufobj *bo; /* * We only need to do something if this is a lazy evaluation. */ if (ap->a_waitfor != MNT_LAZY) return (0); /* * Move ourselves to the back of the sync list. */ bo = &syncvp->v_bufobj; BO_LOCK(bo); vn_syncer_add_to_worklist(bo, syncdelay); BO_UNLOCK(bo); /* * Walk the list of vnodes pushing all that are dirty and * not already on the sync list. */ if (vfs_busy(mp, MBF_NOWAIT) != 0) return (0); if (vn_start_write(NULL, &mp, V_NOWAIT) != 0) { vfs_unbusy(mp); return (0); } save = curthread_pflags_set(TDP_SYNCIO); vfs_msync(mp, MNT_NOWAIT); error = VFS_SYNC(mp, MNT_LAZY); curthread_pflags_restore(save); vn_finished_write(mp); vfs_unbusy(mp); return (error); } /* * The syncer vnode is no referenced. */ static int sync_inactive(struct vop_inactive_args *ap) { vgone(ap->a_vp); return (0); } /* * The syncer vnode is no longer needed and is being decommissioned. * * Modifications to the worklist must be protected by sync_mtx. */ static int sync_reclaim(struct vop_reclaim_args *ap) { struct vnode *vp = ap->a_vp; struct bufobj *bo; bo = &vp->v_bufobj; BO_LOCK(bo); mtx_lock(&sync_mtx); if (vp->v_mount->mnt_syncer == vp) vp->v_mount->mnt_syncer = NULL; if (bo->bo_flag & BO_ONWORKLST) { LIST_REMOVE(bo, bo_synclist); syncer_worklist_len--; sync_vnode_count--; bo->bo_flag &= ~BO_ONWORKLST; } mtx_unlock(&sync_mtx); BO_UNLOCK(bo); return (0); } /* * Check if vnode represents a disk device */ int vn_isdisk(struct vnode *vp, int *errp) { int error; if (vp->v_type != VCHR) { error = ENOTBLK; goto out; } error = 0; dev_lock(); if (vp->v_rdev == NULL) error = ENXIO; else if (vp->v_rdev->si_devsw == NULL) error = ENXIO; else if (!(vp->v_rdev->si_devsw->d_flags & D_DISK)) error = ENOTBLK; dev_unlock(); out: if (errp != NULL) *errp = error; return (error == 0); } /* * Common filesystem object access control check routine. Accepts a * vnode's type, "mode", uid and gid, requested access mode, credentials, * and optional call-by-reference privused argument allowing vaccess() * to indicate to the caller whether privilege was used to satisfy the * request (obsoleted). Returns 0 on success, or an errno on failure. */ int vaccess(enum vtype type, mode_t file_mode, uid_t file_uid, gid_t file_gid, accmode_t accmode, struct ucred *cred, int *privused) { accmode_t dac_granted; accmode_t priv_granted; KASSERT((accmode & ~(VEXEC | VWRITE | VREAD | VADMIN | VAPPEND)) == 0, ("invalid bit in accmode")); KASSERT((accmode & VAPPEND) == 0 || (accmode & VWRITE), ("VAPPEND without VWRITE")); /* * Look for a normal, non-privileged way to access the file/directory * as requested. If it exists, go with that. */ if (privused != NULL) *privused = 0; dac_granted = 0; /* Check the owner. */ if (cred->cr_uid == file_uid) { dac_granted |= VADMIN; if (file_mode & S_IXUSR) dac_granted |= VEXEC; if (file_mode & S_IRUSR) dac_granted |= VREAD; if (file_mode & S_IWUSR) dac_granted |= (VWRITE | VAPPEND); if ((accmode & dac_granted) == accmode) return (0); goto privcheck; } /* Otherwise, check the groups (first match) */ if (groupmember(file_gid, cred)) { if (file_mode & S_IXGRP) dac_granted |= VEXEC; if (file_mode & S_IRGRP) dac_granted |= VREAD; if (file_mode & S_IWGRP) dac_granted |= (VWRITE | VAPPEND); if ((accmode & dac_granted) == accmode) return (0); goto privcheck; } /* Otherwise, check everyone else. */ if (file_mode & S_IXOTH) dac_granted |= VEXEC; if (file_mode & S_IROTH) dac_granted |= VREAD; if (file_mode & S_IWOTH) dac_granted |= (VWRITE | VAPPEND); if ((accmode & dac_granted) == accmode) return (0); privcheck: /* * Build a privilege mask to determine if the set of privileges * satisfies the requirements when combined with the granted mask * from above. For each privilege, if the privilege is required, * bitwise or the request type onto the priv_granted mask. */ priv_granted = 0; if (type == VDIR) { /* * For directories, use PRIV_VFS_LOOKUP to satisfy VEXEC * requests, instead of PRIV_VFS_EXEC. */ if ((accmode & VEXEC) && ((dac_granted & VEXEC) == 0) && !priv_check_cred(cred, PRIV_VFS_LOOKUP, 0)) priv_granted |= VEXEC; } else { /* * Ensure that at least one execute bit is on. Otherwise, * a privileged user will always succeed, and we don't want * this to happen unless the file really is executable. */ if ((accmode & VEXEC) && ((dac_granted & VEXEC) == 0) && (file_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0 && !priv_check_cred(cred, PRIV_VFS_EXEC, 0)) priv_granted |= VEXEC; } if ((accmode & VREAD) && ((dac_granted & VREAD) == 0) && !priv_check_cred(cred, PRIV_VFS_READ, 0)) priv_granted |= VREAD; if ((accmode & VWRITE) && ((dac_granted & VWRITE) == 0) && !priv_check_cred(cred, PRIV_VFS_WRITE, 0)) priv_granted |= (VWRITE | VAPPEND); if ((accmode & VADMIN) && ((dac_granted & VADMIN) == 0) && !priv_check_cred(cred, PRIV_VFS_ADMIN, 0)) priv_granted |= VADMIN; if ((accmode & (priv_granted | dac_granted)) == accmode) { /* XXX audit: privilege used */ if (privused != NULL) *privused = 1; return (0); } return ((accmode & VADMIN) ? EPERM : EACCES); } /* * Credential check based on process requesting service, and per-attribute * permissions. */ int extattr_check_cred(struct vnode *vp, int attrnamespace, struct ucred *cred, struct thread *td, accmode_t accmode) { /* * Kernel-invoked always succeeds. */ if (cred == NOCRED) return (0); /* * Do not allow privileged processes in jail to directly manipulate * system attributes. */ switch (attrnamespace) { case EXTATTR_NAMESPACE_SYSTEM: /* Potentially should be: return (EPERM); */ return (priv_check_cred(cred, PRIV_VFS_EXTATTR_SYSTEM, 0)); case EXTATTR_NAMESPACE_USER: return (VOP_ACCESS(vp, accmode, cred, td)); default: return (EPERM); } } #ifdef DEBUG_VFS_LOCKS /* * This only exists to supress warnings from unlocked specfs accesses. It is * no longer ok to have an unlocked VFS. */ #define IGNORE_LOCK(vp) (panicstr != NULL || (vp) == NULL || \ (vp)->v_type == VCHR || (vp)->v_type == VBAD) int vfs_badlock_ddb = 1; /* Drop into debugger on violation. */ SYSCTL_INT(_debug, OID_AUTO, vfs_badlock_ddb, CTLFLAG_RW, &vfs_badlock_ddb, 0, "Drop into debugger on lock violation"); int vfs_badlock_mutex = 1; /* Check for interlock across VOPs. */ SYSCTL_INT(_debug, OID_AUTO, vfs_badlock_mutex, CTLFLAG_RW, &vfs_badlock_mutex, 0, "Check for interlock across VOPs"); int vfs_badlock_print = 1; /* Print lock violations. */ SYSCTL_INT(_debug, OID_AUTO, vfs_badlock_print, CTLFLAG_RW, &vfs_badlock_print, 0, "Print lock violations"); #ifdef KDB int vfs_badlock_backtrace = 1; /* Print backtrace at lock violations. */ SYSCTL_INT(_debug, OID_AUTO, vfs_badlock_backtrace, CTLFLAG_RW, &vfs_badlock_backtrace, 0, "Print backtrace at lock violations"); #endif static void vfs_badlock(const char *msg, const char *str, struct vnode *vp) { #ifdef KDB if (vfs_badlock_backtrace) kdb_backtrace(); #endif if (vfs_badlock_print) printf("%s: %p %s\n", str, (void *)vp, msg); if (vfs_badlock_ddb) kdb_enter(KDB_WHY_VFSLOCK, "lock violation"); } void assert_vi_locked(struct vnode *vp, const char *str) { if (vfs_badlock_mutex && !mtx_owned(VI_MTX(vp))) vfs_badlock("interlock is not locked but should be", str, vp); } void assert_vi_unlocked(struct vnode *vp, const char *str) { if (vfs_badlock_mutex && mtx_owned(VI_MTX(vp))) vfs_badlock("interlock is locked but should not be", str, vp); } void assert_vop_locked(struct vnode *vp, const char *str) { int locked; if (!IGNORE_LOCK(vp)) { locked = VOP_ISLOCKED(vp); if (locked == 0 || locked == LK_EXCLOTHER) vfs_badlock("is not locked but should be", str, vp); } } void assert_vop_unlocked(struct vnode *vp, const char *str) { if (!IGNORE_LOCK(vp) && VOP_ISLOCKED(vp) == LK_EXCLUSIVE) vfs_badlock("is locked but should not be", str, vp); } void assert_vop_elocked(struct vnode *vp, const char *str) { if (!IGNORE_LOCK(vp) && VOP_ISLOCKED(vp) != LK_EXCLUSIVE) vfs_badlock("is not exclusive locked but should be", str, vp); } #if 0 void assert_vop_elocked_other(struct vnode *vp, const char *str) { if (!IGNORE_LOCK(vp) && VOP_ISLOCKED(vp) != LK_EXCLOTHER) vfs_badlock("is not exclusive locked by another thread", str, vp); } void assert_vop_slocked(struct vnode *vp, const char *str) { if (!IGNORE_LOCK(vp) && VOP_ISLOCKED(vp) != LK_SHARED) vfs_badlock("is not locked shared but should be", str, vp); } #endif /* 0 */ #endif /* DEBUG_VFS_LOCKS */ void vop_rename_fail(struct vop_rename_args *ap) { if (ap->a_tvp != NULL) vput(ap->a_tvp); if (ap->a_tdvp == ap->a_tvp) vrele(ap->a_tdvp); else vput(ap->a_tdvp); vrele(ap->a_fdvp); vrele(ap->a_fvp); } void vop_rename_pre(void *ap) { struct vop_rename_args *a = ap; #ifdef DEBUG_VFS_LOCKS if (a->a_tvp) ASSERT_VI_UNLOCKED(a->a_tvp, "VOP_RENAME"); ASSERT_VI_UNLOCKED(a->a_tdvp, "VOP_RENAME"); ASSERT_VI_UNLOCKED(a->a_fvp, "VOP_RENAME"); ASSERT_VI_UNLOCKED(a->a_fdvp, "VOP_RENAME"); /* Check the source (from). */ if (a->a_tdvp->v_vnlock != a->a_fdvp->v_vnlock && (a->a_tvp == NULL || a->a_tvp->v_vnlock != a->a_fdvp->v_vnlock)) ASSERT_VOP_UNLOCKED(a->a_fdvp, "vop_rename: fdvp locked"); if (a->a_tvp == NULL || a->a_tvp->v_vnlock != a->a_fvp->v_vnlock) ASSERT_VOP_UNLOCKED(a->a_fvp, "vop_rename: fvp locked"); /* Check the target. */ if (a->a_tvp) ASSERT_VOP_LOCKED(a->a_tvp, "vop_rename: tvp not locked"); ASSERT_VOP_LOCKED(a->a_tdvp, "vop_rename: tdvp not locked"); #endif if (a->a_tdvp != a->a_fdvp) vhold(a->a_fdvp); if (a->a_tvp != a->a_fvp) vhold(a->a_fvp); vhold(a->a_tdvp); if (a->a_tvp) vhold(a->a_tvp); } void vop_strategy_pre(void *ap) { #ifdef DEBUG_VFS_LOCKS struct vop_strategy_args *a; struct buf *bp; a = ap; bp = a->a_bp; /* * Cluster ops lock their component buffers but not the IO container. */ if ((bp->b_flags & B_CLUSTER) != 0) return; if (panicstr == NULL && !BUF_ISLOCKED(bp)) { if (vfs_badlock_print) printf( "VOP_STRATEGY: bp is not locked but should be\n"); if (vfs_badlock_ddb) kdb_enter(KDB_WHY_VFSLOCK, "lock violation"); } #endif } void vop_lock_pre(void *ap) { #ifdef DEBUG_VFS_LOCKS struct vop_lock1_args *a = ap; if ((a->a_flags & LK_INTERLOCK) == 0) ASSERT_VI_UNLOCKED(a->a_vp, "VOP_LOCK"); else ASSERT_VI_LOCKED(a->a_vp, "VOP_LOCK"); #endif } void vop_lock_post(void *ap, int rc) { #ifdef DEBUG_VFS_LOCKS struct vop_lock1_args *a = ap; ASSERT_VI_UNLOCKED(a->a_vp, "VOP_LOCK"); if (rc == 0 && (a->a_flags & LK_EXCLOTHER) == 0) ASSERT_VOP_LOCKED(a->a_vp, "VOP_LOCK"); #endif } void vop_unlock_pre(void *ap) { #ifdef DEBUG_VFS_LOCKS struct vop_unlock_args *a = ap; if (a->a_flags & LK_INTERLOCK) ASSERT_VI_LOCKED(a->a_vp, "VOP_UNLOCK"); ASSERT_VOP_LOCKED(a->a_vp, "VOP_UNLOCK"); #endif } void vop_unlock_post(void *ap, int rc) { #ifdef DEBUG_VFS_LOCKS struct vop_unlock_args *a = ap; if (a->a_flags & LK_INTERLOCK) ASSERT_VI_UNLOCKED(a->a_vp, "VOP_UNLOCK"); #endif } void vop_create_post(void *ap, int rc) { struct vop_create_args *a = ap; if (!rc) VFS_KNOTE_LOCKED(a->a_dvp, NOTE_WRITE); } void vop_deleteextattr_post(void *ap, int rc) { struct vop_deleteextattr_args *a = ap; if (!rc) VFS_KNOTE_LOCKED(a->a_vp, NOTE_ATTRIB); } void vop_link_post(void *ap, int rc) { struct vop_link_args *a = ap; if (!rc) { VFS_KNOTE_LOCKED(a->a_vp, NOTE_LINK); VFS_KNOTE_LOCKED(a->a_tdvp, NOTE_WRITE); } } void vop_mkdir_post(void *ap, int rc) { struct vop_mkdir_args *a = ap; if (!rc) VFS_KNOTE_LOCKED(a->a_dvp, NOTE_WRITE | NOTE_LINK); } void vop_mknod_post(void *ap, int rc) { struct vop_mknod_args *a = ap; if (!rc) VFS_KNOTE_LOCKED(a->a_dvp, NOTE_WRITE); } void vop_remove_post(void *ap, int rc) { struct vop_remove_args *a = ap; if (!rc) { VFS_KNOTE_LOCKED(a->a_dvp, NOTE_WRITE); VFS_KNOTE_LOCKED(a->a_vp, NOTE_DELETE); } } void vop_rename_post(void *ap, int rc) { struct vop_rename_args *a = ap; if (!rc) { VFS_KNOTE_UNLOCKED(a->a_fdvp, NOTE_WRITE); VFS_KNOTE_UNLOCKED(a->a_tdvp, NOTE_WRITE); VFS_KNOTE_UNLOCKED(a->a_fvp, NOTE_RENAME); if (a->a_tvp) VFS_KNOTE_UNLOCKED(a->a_tvp, NOTE_DELETE); } if (a->a_tdvp != a->a_fdvp) vdrop(a->a_fdvp); if (a->a_tvp != a->a_fvp) vdrop(a->a_fvp); vdrop(a->a_tdvp); if (a->a_tvp) vdrop(a->a_tvp); } void vop_rmdir_post(void *ap, int rc) { struct vop_rmdir_args *a = ap; if (!rc) { VFS_KNOTE_LOCKED(a->a_dvp, NOTE_WRITE | NOTE_LINK); VFS_KNOTE_LOCKED(a->a_vp, NOTE_DELETE); } } void vop_setattr_post(void *ap, int rc) { struct vop_setattr_args *a = ap; if (!rc) VFS_KNOTE_LOCKED(a->a_vp, NOTE_ATTRIB); } void vop_setextattr_post(void *ap, int rc) { struct vop_setextattr_args *a = ap; if (!rc) VFS_KNOTE_LOCKED(a->a_vp, NOTE_ATTRIB); } void vop_symlink_post(void *ap, int rc) { struct vop_symlink_args *a = ap; if (!rc) VFS_KNOTE_LOCKED(a->a_dvp, NOTE_WRITE); } static struct knlist fs_knlist; static void vfs_event_init(void *arg) { knlist_init_mtx(&fs_knlist, NULL); } /* XXX - correct order? */ SYSINIT(vfs_knlist, SI_SUB_VFS, SI_ORDER_ANY, vfs_event_init, NULL); void vfs_event_signal(fsid_t *fsid, uint32_t event, intptr_t data __unused) { KNOTE_UNLOCKED(&fs_knlist, event); } static int filt_fsattach(struct knote *kn); static void filt_fsdetach(struct knote *kn); static int filt_fsevent(struct knote *kn, long hint); struct filterops fs_filtops = { .f_isfd = 0, .f_attach = filt_fsattach, .f_detach = filt_fsdetach, .f_event = filt_fsevent }; static int filt_fsattach(struct knote *kn) { kn->kn_flags |= EV_CLEAR; knlist_add(&fs_knlist, kn, 0); return (0); } static void filt_fsdetach(struct knote *kn) { knlist_remove(&fs_knlist, kn, 0); } static int filt_fsevent(struct knote *kn, long hint) { kn->kn_fflags |= hint; return (kn->kn_fflags != 0); } static int sysctl_vfs_ctl(SYSCTL_HANDLER_ARGS) { struct vfsidctl vc; int error; struct mount *mp; error = SYSCTL_IN(req, &vc, sizeof(vc)); if (error) return (error); if (vc.vc_vers != VFS_CTL_VERS1) return (EINVAL); mp = vfs_getvfs(&vc.vc_fsid); if (mp == NULL) return (ENOENT); /* ensure that a specific sysctl goes to the right filesystem. */ if (strcmp(vc.vc_fstypename, "*") != 0 && strcmp(vc.vc_fstypename, mp->mnt_vfc->vfc_name) != 0) { vfs_rel(mp); return (EINVAL); } VCTLTOREQ(&vc, req); error = VFS_SYSCTL(mp, vc.vc_op, req); vfs_rel(mp); return (error); } SYSCTL_PROC(_vfs, OID_AUTO, ctl, CTLTYPE_OPAQUE | CTLFLAG_WR, NULL, 0, sysctl_vfs_ctl, "", "Sysctl by fsid"); /* * Function to initialize a va_filerev field sensibly. * XXX: Wouldn't a random number make a lot more sense ?? */ u_quad_t init_va_filerev(void) { struct bintime bt; getbinuptime(&bt); return (((u_quad_t)bt.sec << 32LL) | (bt.frac >> 32LL)); } static int filt_vfsread(struct knote *kn, long hint); static int filt_vfswrite(struct knote *kn, long hint); static int filt_vfsvnode(struct knote *kn, long hint); static void filt_vfsdetach(struct knote *kn); static struct filterops vfsread_filtops = { .f_isfd = 1, .f_detach = filt_vfsdetach, .f_event = filt_vfsread }; static struct filterops vfswrite_filtops = { .f_isfd = 1, .f_detach = filt_vfsdetach, .f_event = filt_vfswrite }; static struct filterops vfsvnode_filtops = { .f_isfd = 1, .f_detach = filt_vfsdetach, .f_event = filt_vfsvnode }; static void vfs_knllock(void *arg) { struct vnode *vp = arg; vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); } static void vfs_knlunlock(void *arg) { struct vnode *vp = arg; VOP_UNLOCK(vp, 0); } static void vfs_knl_assert_locked(void *arg) { #ifdef DEBUG_VFS_LOCKS struct vnode *vp = arg; ASSERT_VOP_LOCKED(vp, "vfs_knl_assert_locked"); #endif } static void vfs_knl_assert_unlocked(void *arg) { #ifdef DEBUG_VFS_LOCKS struct vnode *vp = arg; ASSERT_VOP_UNLOCKED(vp, "vfs_knl_assert_unlocked"); #endif } int vfs_kqfilter(struct vop_kqfilter_args *ap) { struct vnode *vp = ap->a_vp; struct knote *kn = ap->a_kn; struct knlist *knl; switch (kn->kn_filter) { case EVFILT_READ: kn->kn_fop = &vfsread_filtops; break; case EVFILT_WRITE: kn->kn_fop = &vfswrite_filtops; break; case EVFILT_VNODE: kn->kn_fop = &vfsvnode_filtops; break; default: return (EINVAL); } kn->kn_hook = (caddr_t)vp; v_addpollinfo(vp); if (vp->v_pollinfo == NULL) return (ENOMEM); knl = &vp->v_pollinfo->vpi_selinfo.si_note; vhold(vp); knlist_add(knl, kn, 0); return (0); } /* * Detach knote from vnode */ static void filt_vfsdetach(struct knote *kn) { struct vnode *vp = (struct vnode *)kn->kn_hook; KASSERT(vp->v_pollinfo != NULL, ("Missing v_pollinfo")); knlist_remove(&vp->v_pollinfo->vpi_selinfo.si_note, kn, 0); vdrop(vp); } /*ARGSUSED*/ static int filt_vfsread(struct knote *kn, long hint) { struct vnode *vp = (struct vnode *)kn->kn_hook; struct vattr va; int res; /* * filesystem is gone, so set the EOF flag and schedule * the knote for deletion. */ if (hint == NOTE_REVOKE) { VI_LOCK(vp); kn->kn_flags |= (EV_EOF | EV_ONESHOT); VI_UNLOCK(vp); return (1); } if (VOP_GETATTR(vp, &va, curthread->td_ucred)) return (0); VI_LOCK(vp); kn->kn_data = va.va_size - kn->kn_fp->f_offset; res = (kn->kn_data != 0); VI_UNLOCK(vp); return (res); } /*ARGSUSED*/ static int filt_vfswrite(struct knote *kn, long hint) { struct vnode *vp = (struct vnode *)kn->kn_hook; VI_LOCK(vp); /* * filesystem is gone, so set the EOF flag and schedule * the knote for deletion. */ if (hint == NOTE_REVOKE) kn->kn_flags |= (EV_EOF | EV_ONESHOT); kn->kn_data = 0; VI_UNLOCK(vp); return (1); } static int filt_vfsvnode(struct knote *kn, long hint) { struct vnode *vp = (struct vnode *)kn->kn_hook; int res; VI_LOCK(vp); if (kn->kn_sfflags & hint) kn->kn_fflags |= hint; if (hint == NOTE_REVOKE) { kn->kn_flags |= EV_EOF; VI_UNLOCK(vp); return (1); } res = (kn->kn_fflags != 0); VI_UNLOCK(vp); return (res); } int vfs_read_dirent(struct vop_readdir_args *ap, struct dirent *dp, off_t off) { int error; if (dp->d_reclen > ap->a_uio->uio_resid) return (ENAMETOOLONG); error = uiomove(dp, dp->d_reclen, ap->a_uio); if (error) { if (ap->a_ncookies != NULL) { if (ap->a_cookies != NULL) free(ap->a_cookies, M_TEMP); ap->a_cookies = NULL; *ap->a_ncookies = 0; } return (error); } if (ap->a_ncookies == NULL) return (0); KASSERT(ap->a_cookies, ("NULL ap->a_cookies value with non-NULL ap->a_ncookies!")); *ap->a_cookies = realloc(*ap->a_cookies, (*ap->a_ncookies + 1) * sizeof(u_long), M_TEMP, M_WAITOK | M_ZERO); (*ap->a_cookies)[*ap->a_ncookies] = off; return (0); } /* * Mark for update the access time of the file if the filesystem * supports VOP_MARKATIME. This functionality is used by execve and * mmap, so we want to avoid the I/O implied by directly setting * va_atime for the sake of efficiency. */ void vfs_mark_atime(struct vnode *vp, struct ucred *cred) { struct mount *mp; mp = vp->v_mount; ASSERT_VOP_LOCKED(vp, "vfs_mark_atime"); if (mp != NULL && (mp->mnt_flag & (MNT_NOATIME | MNT_RDONLY)) == 0) (void)VOP_MARKATIME(vp); } /* * The purpose of this routine is to remove granularity from accmode_t, * reducing it into standard unix access bits - VEXEC, VREAD, VWRITE, * VADMIN and VAPPEND. * * If it returns 0, the caller is supposed to continue with the usual * access checks using 'accmode' as modified by this routine. If it * returns nonzero value, the caller is supposed to return that value * as errno. * * Note that after this routine runs, accmode may be zero. */ int vfs_unixify_accmode(accmode_t *accmode) { /* * There is no way to specify explicit "deny" rule using * file mode or POSIX.1e ACLs. */ if (*accmode & VEXPLICIT_DENY) { *accmode = 0; return (0); } /* * None of these can be translated into usual access bits. * Also, the common case for NFSv4 ACLs is to not contain * either of these bits. Caller should check for VWRITE * on the containing directory instead. */ if (*accmode & (VDELETE_CHILD | VDELETE)) return (EPERM); if (*accmode & VADMIN_PERMS) { *accmode &= ~VADMIN_PERMS; *accmode |= VADMIN; } /* * There is no way to deny VREAD_ATTRIBUTES, VREAD_ACL * or VSYNCHRONIZE using file mode or POSIX.1e ACL. */ *accmode &= ~(VSTAT_PERMS | VSYNCHRONIZE); return (0); } /* * These are helper functions for filesystems to traverse all * their vnodes. See MNT_VNODE_FOREACH_ALL() in sys/mount.h. * * This interface replaces MNT_VNODE_FOREACH. */ MALLOC_DEFINE(M_VNODE_MARKER, "vnodemarker", "vnode marker"); struct vnode * __mnt_vnode_next_all(struct vnode **mvp, struct mount *mp) { struct vnode *vp; if (should_yield()) kern_yield(PRI_USER); MNT_ILOCK(mp); KASSERT((*mvp)->v_mount == mp, ("marker vnode mount list mismatch")); vp = TAILQ_NEXT(*mvp, v_nmntvnodes); while (vp != NULL && (vp->v_type == VMARKER || (vp->v_iflag & VI_DOOMED) != 0)) vp = TAILQ_NEXT(vp, v_nmntvnodes); /* Check if we are done */ if (vp == NULL) { __mnt_vnode_markerfree_all(mvp, mp); /* MNT_IUNLOCK(mp); -- done in above function */ mtx_assert(MNT_MTX(mp), MA_NOTOWNED); return (NULL); } TAILQ_REMOVE(&mp->mnt_nvnodelist, *mvp, v_nmntvnodes); TAILQ_INSERT_AFTER(&mp->mnt_nvnodelist, vp, *mvp, v_nmntvnodes); VI_LOCK(vp); MNT_IUNLOCK(mp); return (vp); } struct vnode * __mnt_vnode_first_all(struct vnode **mvp, struct mount *mp) { struct vnode *vp; *mvp = malloc(sizeof(struct vnode), M_VNODE_MARKER, M_WAITOK | M_ZERO); MNT_ILOCK(mp); MNT_REF(mp); (*mvp)->v_type = VMARKER; vp = TAILQ_FIRST(&mp->mnt_nvnodelist); while (vp != NULL && (vp->v_type == VMARKER || (vp->v_iflag & VI_DOOMED) != 0)) vp = TAILQ_NEXT(vp, v_nmntvnodes); /* Check if we are done */ if (vp == NULL) { MNT_REL(mp); MNT_IUNLOCK(mp); free(*mvp, M_VNODE_MARKER); *mvp = NULL; return (NULL); } (*mvp)->v_mount = mp; TAILQ_INSERT_AFTER(&mp->mnt_nvnodelist, vp, *mvp, v_nmntvnodes); VI_LOCK(vp); MNT_IUNLOCK(mp); return (vp); } void __mnt_vnode_markerfree_all(struct vnode **mvp, struct mount *mp) { if (*mvp == NULL) { MNT_IUNLOCK(mp); return; } mtx_assert(MNT_MTX(mp), MA_OWNED); KASSERT((*mvp)->v_mount == mp, ("marker vnode mount list mismatch")); TAILQ_REMOVE(&mp->mnt_nvnodelist, *mvp, v_nmntvnodes); MNT_REL(mp); MNT_IUNLOCK(mp); free(*mvp, M_VNODE_MARKER); *mvp = NULL; } /* * These are helper functions for filesystems to traverse their * active vnodes. See MNT_VNODE_FOREACH_ACTIVE() in sys/mount.h */ static void mnt_vnode_markerfree_active(struct vnode **mvp, struct mount *mp) { KASSERT((*mvp)->v_mount == mp, ("marker vnode mount list mismatch")); MNT_ILOCK(mp); MNT_REL(mp); MNT_IUNLOCK(mp); free(*mvp, M_VNODE_MARKER); *mvp = NULL; } static struct vnode * mnt_vnode_next_active(struct vnode **mvp, struct mount *mp) { struct vnode *vp, *nvp; mtx_assert(&vnode_free_list_mtx, MA_OWNED); KASSERT((*mvp)->v_mount == mp, ("marker vnode mount list mismatch")); restart: vp = TAILQ_NEXT(*mvp, v_actfreelist); TAILQ_REMOVE(&mp->mnt_activevnodelist, *mvp, v_actfreelist); while (vp != NULL) { if (vp->v_type == VMARKER) { vp = TAILQ_NEXT(vp, v_actfreelist); continue; } if (!VI_TRYLOCK(vp)) { if (mp_ncpus == 1 || should_yield()) { TAILQ_INSERT_BEFORE(vp, *mvp, v_actfreelist); mtx_unlock(&vnode_free_list_mtx); pause("vnacti", 1); mtx_lock(&vnode_free_list_mtx); goto restart; } continue; } KASSERT(vp->v_type != VMARKER, ("locked marker %p", vp)); KASSERT(vp->v_mount == mp || vp->v_mount == NULL, ("alien vnode on the active list %p %p", vp, mp)); if (vp->v_mount == mp && (vp->v_iflag & VI_DOOMED) == 0) break; nvp = TAILQ_NEXT(vp, v_actfreelist); VI_UNLOCK(vp); vp = nvp; } /* Check if we are done */ if (vp == NULL) { mtx_unlock(&vnode_free_list_mtx); mnt_vnode_markerfree_active(mvp, mp); return (NULL); } TAILQ_INSERT_AFTER(&mp->mnt_activevnodelist, vp, *mvp, v_actfreelist); mtx_unlock(&vnode_free_list_mtx); ASSERT_VI_LOCKED(vp, "active iter"); KASSERT((vp->v_iflag & VI_ACTIVE) != 0, ("Non-active vp %p", vp)); return (vp); } struct vnode * __mnt_vnode_next_active(struct vnode **mvp, struct mount *mp) { if (should_yield()) kern_yield(PRI_USER); mtx_lock(&vnode_free_list_mtx); return (mnt_vnode_next_active(mvp, mp)); } struct vnode * __mnt_vnode_first_active(struct vnode **mvp, struct mount *mp) { struct vnode *vp; *mvp = malloc(sizeof(struct vnode), M_VNODE_MARKER, M_WAITOK | M_ZERO); MNT_ILOCK(mp); MNT_REF(mp); MNT_IUNLOCK(mp); (*mvp)->v_type = VMARKER; (*mvp)->v_mount = mp; mtx_lock(&vnode_free_list_mtx); vp = TAILQ_FIRST(&mp->mnt_activevnodelist); if (vp == NULL) { mtx_unlock(&vnode_free_list_mtx); mnt_vnode_markerfree_active(mvp, mp); return (NULL); } TAILQ_INSERT_BEFORE(vp, *mvp, v_actfreelist); return (mnt_vnode_next_active(mvp, mp)); } void __mnt_vnode_markerfree_active(struct vnode **mvp, struct mount *mp) { if (*mvp == NULL) return; mtx_lock(&vnode_free_list_mtx); TAILQ_REMOVE(&mp->mnt_activevnodelist, *mvp, v_actfreelist); mtx_unlock(&vnode_free_list_mtx); mnt_vnode_markerfree_active(mvp, mp); } Index: projects/building-blocks/sys/net/if_bridge.c =================================================================== --- projects/building-blocks/sys/net/if_bridge.c (revision 278776) +++ projects/building-blocks/sys/net/if_bridge.c (revision 278777) @@ -1,3562 +1,3569 @@ /* $NetBSD: if_bridge.c,v 1.31 2005/06/01 19:45:34 jdc Exp $ */ /* * Copyright 2001 Wasabi Systems, Inc. * All rights reserved. * * Written by Jason R. Thorpe for Wasabi Systems, Inc. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed for the NetBSD Project by * Wasabi Systems, Inc. * 4. The name of Wasabi Systems, Inc. may not be used to endorse * or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC * 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. */ /* * Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net) * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. * * OpenBSD: if_bridge.c,v 1.60 2001/06/15 03:38:33 itojun Exp */ /* * Network interface bridge support. * * TODO: * * - Currently only supports Ethernet-like interfaces (Ethernet, * 802.11, VLANs on Ethernet, etc.) Figure out a nice way * to bridge other types of interfaces (FDDI-FDDI, and maybe * consider heterogenous bridges). */ #include __FBSDID("$FreeBSD$"); #include "opt_inet.h" #include "opt_inet6.h" #include #include #include #include #include #include #include #include #include /* for net/if.h */ #include #include /* string functions */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET6 #include #include #include #endif #if defined(INET) || defined(INET6) #include #endif #include #include #include #include #include #include #include /* * Size of the route hash table. Must be a power of two. */ #ifndef BRIDGE_RTHASH_SIZE #define BRIDGE_RTHASH_SIZE 1024 #endif #define BRIDGE_RTHASH_MASK (BRIDGE_RTHASH_SIZE - 1) /* * Default maximum number of addresses to cache. */ #ifndef BRIDGE_RTABLE_MAX #define BRIDGE_RTABLE_MAX 2000 #endif /* * Timeout (in seconds) for entries learned dynamically. */ #ifndef BRIDGE_RTABLE_TIMEOUT #define BRIDGE_RTABLE_TIMEOUT (20 * 60) /* same as ARP */ #endif /* * Number of seconds between walks of the route list. */ #ifndef BRIDGE_RTABLE_PRUNE_PERIOD #define BRIDGE_RTABLE_PRUNE_PERIOD (5 * 60) #endif /* * List of capabilities to possibly mask on the member interface. */ #define BRIDGE_IFCAPS_MASK (IFCAP_TOE|IFCAP_TSO|IFCAP_TXCSUM) /* * List of capabilities to strip */ #define BRIDGE_IFCAPS_STRIP IFCAP_LRO /* * Bridge interface list entry. */ struct bridge_iflist { LIST_ENTRY(bridge_iflist) bif_next; struct ifnet *bif_ifp; /* member if */ struct bstp_port bif_stp; /* STP state */ uint32_t bif_flags; /* member if flags */ int bif_savedcaps; /* saved capabilities */ uint32_t bif_addrmax; /* max # of addresses */ uint32_t bif_addrcnt; /* cur. # of addresses */ uint32_t bif_addrexceeded;/* # of address violations */ }; /* * Bridge route node. */ struct bridge_rtnode { LIST_ENTRY(bridge_rtnode) brt_hash; /* hash table linkage */ LIST_ENTRY(bridge_rtnode) brt_list; /* list linkage */ struct bridge_iflist *brt_dst; /* destination if */ unsigned long brt_expire; /* expiration time */ uint8_t brt_flags; /* address flags */ uint8_t brt_addr[ETHER_ADDR_LEN]; uint16_t brt_vlan; /* vlan id */ }; #define brt_ifp brt_dst->bif_ifp /* * Software state for each bridge. */ struct bridge_softc { struct ifnet *sc_ifp; /* make this an interface */ LIST_ENTRY(bridge_softc) sc_list; struct mtx sc_mtx; struct cv sc_cv; uint32_t sc_brtmax; /* max # of addresses */ uint32_t sc_brtcnt; /* cur. # of addresses */ uint32_t sc_brttimeout; /* rt timeout in seconds */ struct callout sc_brcallout; /* bridge callout */ uint32_t sc_iflist_ref; /* refcount for sc_iflist */ uint32_t sc_iflist_xcnt; /* refcount for sc_iflist */ LIST_HEAD(, bridge_iflist) sc_iflist; /* member interface list */ LIST_HEAD(, bridge_rtnode) *sc_rthash; /* our forwarding table */ LIST_HEAD(, bridge_rtnode) sc_rtlist; /* list version of above */ uint32_t sc_rthash_key; /* key for hash */ LIST_HEAD(, bridge_iflist) sc_spanlist; /* span ports list */ struct bstp_state sc_stp; /* STP state */ uint32_t sc_brtexceeded; /* # of cache drops */ struct ifnet *sc_ifaddr; /* member mac copied from */ u_char sc_defaddr[6]; /* Default MAC address */ }; static VNET_DEFINE(struct mtx, bridge_list_mtx); #define V_bridge_list_mtx VNET(bridge_list_mtx) -eventhandler_tag bridge_detach_cookie = NULL; +static eventhandler_tag bridge_detach_cookie; int bridge_rtable_prune_period = BRIDGE_RTABLE_PRUNE_PERIOD; uma_zone_t bridge_rtnode_zone; static int bridge_clone_create(struct if_clone *, int, caddr_t); static void bridge_clone_destroy(struct ifnet *); static int bridge_ioctl(struct ifnet *, u_long, caddr_t); static void bridge_mutecaps(struct bridge_softc *); static void bridge_set_ifcap(struct bridge_softc *, struct bridge_iflist *, int); static void bridge_ifdetach(void *arg __unused, struct ifnet *); static void bridge_init(void *); static void bridge_dummynet(struct mbuf *, struct ifnet *); static void bridge_stop(struct ifnet *, int); static int bridge_transmit(struct ifnet *, struct mbuf *); static void bridge_qflush(struct ifnet *); static struct mbuf *bridge_input(struct ifnet *, struct mbuf *); static int bridge_output(struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *); static int bridge_enqueue(struct bridge_softc *, struct ifnet *, struct mbuf *); static void bridge_rtdelete(struct bridge_softc *, struct ifnet *ifp, int); static void bridge_forward(struct bridge_softc *, struct bridge_iflist *, struct mbuf *m); static void bridge_timer(void *); static void bridge_broadcast(struct bridge_softc *, struct ifnet *, struct mbuf *, int); static void bridge_span(struct bridge_softc *, struct mbuf *); static int bridge_rtupdate(struct bridge_softc *, const uint8_t *, uint16_t, struct bridge_iflist *, int, uint8_t); static struct ifnet *bridge_rtlookup(struct bridge_softc *, const uint8_t *, uint16_t); static void bridge_rttrim(struct bridge_softc *); static void bridge_rtage(struct bridge_softc *); static void bridge_rtflush(struct bridge_softc *, int); static int bridge_rtdaddr(struct bridge_softc *, const uint8_t *, uint16_t); static void bridge_rtable_init(struct bridge_softc *); static void bridge_rtable_fini(struct bridge_softc *); static int bridge_rtnode_addr_cmp(const uint8_t *, const uint8_t *); static struct bridge_rtnode *bridge_rtnode_lookup(struct bridge_softc *, const uint8_t *, uint16_t); static int bridge_rtnode_insert(struct bridge_softc *, struct bridge_rtnode *); static void bridge_rtnode_destroy(struct bridge_softc *, struct bridge_rtnode *); static void bridge_rtable_expire(struct ifnet *, int); static void bridge_state_change(struct ifnet *, int); static struct bridge_iflist *bridge_lookup_member(struct bridge_softc *, const char *name); static struct bridge_iflist *bridge_lookup_member_if(struct bridge_softc *, struct ifnet *ifp); static void bridge_delete_member(struct bridge_softc *, struct bridge_iflist *, int); static void bridge_delete_span(struct bridge_softc *, struct bridge_iflist *); static int bridge_ioctl_add(struct bridge_softc *, void *); static int bridge_ioctl_del(struct bridge_softc *, void *); static int bridge_ioctl_gifflags(struct bridge_softc *, void *); static int bridge_ioctl_sifflags(struct bridge_softc *, void *); static int bridge_ioctl_scache(struct bridge_softc *, void *); static int bridge_ioctl_gcache(struct bridge_softc *, void *); static int bridge_ioctl_gifs(struct bridge_softc *, void *); static int bridge_ioctl_rts(struct bridge_softc *, void *); static int bridge_ioctl_saddr(struct bridge_softc *, void *); static int bridge_ioctl_sto(struct bridge_softc *, void *); static int bridge_ioctl_gto(struct bridge_softc *, void *); static int bridge_ioctl_daddr(struct bridge_softc *, void *); static int bridge_ioctl_flush(struct bridge_softc *, void *); static int bridge_ioctl_gpri(struct bridge_softc *, void *); static int bridge_ioctl_spri(struct bridge_softc *, void *); static int bridge_ioctl_ght(struct bridge_softc *, void *); static int bridge_ioctl_sht(struct bridge_softc *, void *); static int bridge_ioctl_gfd(struct bridge_softc *, void *); static int bridge_ioctl_sfd(struct bridge_softc *, void *); static int bridge_ioctl_gma(struct bridge_softc *, void *); static int bridge_ioctl_sma(struct bridge_softc *, void *); static int bridge_ioctl_sifprio(struct bridge_softc *, void *); static int bridge_ioctl_sifcost(struct bridge_softc *, void *); static int bridge_ioctl_sifmaxaddr(struct bridge_softc *, void *); static int bridge_ioctl_addspan(struct bridge_softc *, void *); static int bridge_ioctl_delspan(struct bridge_softc *, void *); static int bridge_ioctl_gbparam(struct bridge_softc *, void *); static int bridge_ioctl_grte(struct bridge_softc *, void *); static int bridge_ioctl_gifsstp(struct bridge_softc *, void *); static int bridge_ioctl_sproto(struct bridge_softc *, void *); static int bridge_ioctl_stxhc(struct bridge_softc *, void *); static int bridge_pfil(struct mbuf **, struct ifnet *, struct ifnet *, int); static int bridge_ip_checkbasic(struct mbuf **mp); #ifdef INET6 static int bridge_ip6_checkbasic(struct mbuf **mp); #endif /* INET6 */ static int bridge_fragment(struct ifnet *, struct mbuf *, struct ether_header *, int, struct llc *); static void bridge_linkstate(struct ifnet *ifp); static void bridge_linkcheck(struct bridge_softc *sc); extern void (*bridge_linkstate_p)(struct ifnet *ifp); /* The default bridge vlan is 1 (IEEE 802.1Q-2003 Table 9-2) */ #define VLANTAGOF(_m) \ (_m->m_flags & M_VLANTAG) ? EVL_VLANOFTAG(_m->m_pkthdr.ether_vtag) : 1 static struct bstp_cb_ops bridge_ops = { .bcb_state = bridge_state_change, .bcb_rtage = bridge_rtable_expire }; SYSCTL_DECL(_net_link); static SYSCTL_NODE(_net_link, IFT_BRIDGE, bridge, CTLFLAG_RW, 0, "Bridge"); /* only pass IP[46] packets when pfil is enabled */ static VNET_DEFINE(int, pfil_onlyip) = 1; #define V_pfil_onlyip VNET(pfil_onlyip) SYSCTL_INT(_net_link_bridge, OID_AUTO, pfil_onlyip, CTLFLAG_RWTUN | CTLFLAG_VNET, &VNET_NAME(pfil_onlyip), 0, "Only pass IP packets when pfil is enabled"); /* run pfil hooks on the bridge interface */ static VNET_DEFINE(int, pfil_bridge) = 1; #define V_pfil_bridge VNET(pfil_bridge) SYSCTL_INT(_net_link_bridge, OID_AUTO, pfil_bridge, CTLFLAG_RWTUN | CTLFLAG_VNET, &VNET_NAME(pfil_bridge), 0, "Packet filter on the bridge interface"); /* layer2 filter with ipfw */ static VNET_DEFINE(int, pfil_ipfw); #define V_pfil_ipfw VNET(pfil_ipfw) /* layer2 ARP filter with ipfw */ static VNET_DEFINE(int, pfil_ipfw_arp); #define V_pfil_ipfw_arp VNET(pfil_ipfw_arp) SYSCTL_INT(_net_link_bridge, OID_AUTO, ipfw_arp, CTLFLAG_RWTUN | CTLFLAG_VNET, &VNET_NAME(pfil_ipfw_arp), 0, "Filter ARP packets through IPFW layer2"); /* run pfil hooks on the member interface */ static VNET_DEFINE(int, pfil_member) = 1; #define V_pfil_member VNET(pfil_member) SYSCTL_INT(_net_link_bridge, OID_AUTO, pfil_member, CTLFLAG_RWTUN | CTLFLAG_VNET, &VNET_NAME(pfil_member), 0, "Packet filter on the member interface"); /* run pfil hooks on the physical interface for locally destined packets */ static VNET_DEFINE(int, pfil_local_phys); #define V_pfil_local_phys VNET(pfil_local_phys) SYSCTL_INT(_net_link_bridge, OID_AUTO, pfil_local_phys, CTLFLAG_RWTUN | CTLFLAG_VNET, &VNET_NAME(pfil_local_phys), 0, "Packet filter on the physical interface for locally destined packets"); /* log STP state changes */ static VNET_DEFINE(int, log_stp); #define V_log_stp VNET(log_stp) SYSCTL_INT(_net_link_bridge, OID_AUTO, log_stp, CTLFLAG_RWTUN | CTLFLAG_VNET, &VNET_NAME(log_stp), 0, "Log STP state changes"); /* share MAC with first bridge member */ static VNET_DEFINE(int, bridge_inherit_mac); #define V_bridge_inherit_mac VNET(bridge_inherit_mac) SYSCTL_INT(_net_link_bridge, OID_AUTO, inherit_mac, CTLFLAG_RWTUN | CTLFLAG_VNET, &VNET_NAME(bridge_inherit_mac), 0, "Inherit MAC address from the first bridge member"); static VNET_DEFINE(int, allow_llz_overlap) = 0; #define V_allow_llz_overlap VNET(allow_llz_overlap) SYSCTL_INT(_net_link_bridge, OID_AUTO, allow_llz_overlap, CTLFLAG_VNET | CTLFLAG_RW | CTLFLAG_VNET, &VNET_NAME(allow_llz_overlap), 0, "Allow overlap of link-local scope " "zones of a bridge interface and the member interfaces"); struct bridge_control { int (*bc_func)(struct bridge_softc *, void *); int bc_argsize; int bc_flags; }; #define BC_F_COPYIN 0x01 /* copy arguments in */ #define BC_F_COPYOUT 0x02 /* copy arguments out */ #define BC_F_SUSER 0x04 /* do super-user check */ const struct bridge_control bridge_control_table[] = { { bridge_ioctl_add, sizeof(struct ifbreq), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_del, sizeof(struct ifbreq), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_gifflags, sizeof(struct ifbreq), BC_F_COPYIN|BC_F_COPYOUT }, { bridge_ioctl_sifflags, sizeof(struct ifbreq), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_scache, sizeof(struct ifbrparam), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_gcache, sizeof(struct ifbrparam), BC_F_COPYOUT }, { bridge_ioctl_gifs, sizeof(struct ifbifconf), BC_F_COPYIN|BC_F_COPYOUT }, { bridge_ioctl_rts, sizeof(struct ifbaconf), BC_F_COPYIN|BC_F_COPYOUT }, { bridge_ioctl_saddr, sizeof(struct ifbareq), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_sto, sizeof(struct ifbrparam), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_gto, sizeof(struct ifbrparam), BC_F_COPYOUT }, { bridge_ioctl_daddr, sizeof(struct ifbareq), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_flush, sizeof(struct ifbreq), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_gpri, sizeof(struct ifbrparam), BC_F_COPYOUT }, { bridge_ioctl_spri, sizeof(struct ifbrparam), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_ght, sizeof(struct ifbrparam), BC_F_COPYOUT }, { bridge_ioctl_sht, sizeof(struct ifbrparam), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_gfd, sizeof(struct ifbrparam), BC_F_COPYOUT }, { bridge_ioctl_sfd, sizeof(struct ifbrparam), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_gma, sizeof(struct ifbrparam), BC_F_COPYOUT }, { bridge_ioctl_sma, sizeof(struct ifbrparam), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_sifprio, sizeof(struct ifbreq), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_sifcost, sizeof(struct ifbreq), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_addspan, sizeof(struct ifbreq), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_delspan, sizeof(struct ifbreq), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_gbparam, sizeof(struct ifbropreq), BC_F_COPYOUT }, { bridge_ioctl_grte, sizeof(struct ifbrparam), BC_F_COPYOUT }, { bridge_ioctl_gifsstp, sizeof(struct ifbpstpconf), BC_F_COPYIN|BC_F_COPYOUT }, { bridge_ioctl_sproto, sizeof(struct ifbrparam), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_stxhc, sizeof(struct ifbrparam), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_sifmaxaddr, sizeof(struct ifbreq), BC_F_COPYIN|BC_F_SUSER }, }; const int bridge_control_table_size = nitems(bridge_control_table); static VNET_DEFINE(LIST_HEAD(, bridge_softc), bridge_list); #define V_bridge_list VNET(bridge_list) #define BRIDGE_LIST_LOCK_INIT(x) mtx_init(&V_bridge_list_mtx, \ "if_bridge list", NULL, MTX_DEF) #define BRIDGE_LIST_LOCK_DESTROY(x) mtx_destroy(&V_bridge_list_mtx) #define BRIDGE_LIST_LOCK(x) mtx_lock(&V_bridge_list_mtx) #define BRIDGE_LIST_UNLOCK(x) mtx_unlock(&V_bridge_list_mtx) static VNET_DEFINE(struct if_clone *, bridge_cloner); #define V_bridge_cloner VNET(bridge_cloner) static const char bridge_name[] = "bridge"; static void vnet_bridge_init(const void *unused __unused) { BRIDGE_LIST_LOCK_INIT(); LIST_INIT(&V_bridge_list); V_bridge_cloner = if_clone_simple(bridge_name, bridge_clone_create, bridge_clone_destroy, 0); } VNET_SYSINIT(vnet_bridge_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, vnet_bridge_init, NULL); static void vnet_bridge_uninit(const void *unused __unused) { if_clone_detach(V_bridge_cloner); + V_bridge_cloner = NULL; BRIDGE_LIST_LOCK_DESTROY(); } VNET_SYSUNINIT(vnet_bridge_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, vnet_bridge_uninit, NULL); static int bridge_modevent(module_t mod, int type, void *data) { switch (type) { case MOD_LOAD: bridge_rtnode_zone = uma_zcreate("bridge_rtnode", sizeof(struct bridge_rtnode), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); bridge_input_p = bridge_input; bridge_output_p = bridge_output; bridge_dn_p = bridge_dummynet; bridge_linkstate_p = bridge_linkstate; bridge_detach_cookie = EVENTHANDLER_REGISTER( ifnet_departure_event, bridge_ifdetach, NULL, EVENTHANDLER_PRI_ANY); break; case MOD_UNLOAD: EVENTHANDLER_DEREGISTER(ifnet_departure_event, bridge_detach_cookie); uma_zdestroy(bridge_rtnode_zone); bridge_input_p = NULL; bridge_output_p = NULL; bridge_dn_p = NULL; bridge_linkstate_p = NULL; break; default: return (EOPNOTSUPP); } return (0); } static moduledata_t bridge_mod = { "if_bridge", bridge_modevent, 0 }; DECLARE_MODULE(if_bridge, bridge_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); MODULE_DEPEND(if_bridge, bridgestp, 1, 1, 1); /* * handler for net.link.bridge.ipfw */ static int sysctl_pfil_ipfw(SYSCTL_HANDLER_ARGS) { int enable = V_pfil_ipfw; int error; error = sysctl_handle_int(oidp, &enable, 0, req); enable &= 1; if (enable != V_pfil_ipfw) { V_pfil_ipfw = enable; /* * Disable pfil so that ipfw doesnt run twice, if the user * really wants both then they can re-enable pfil_bridge and/or * pfil_member. Also allow non-ip packets as ipfw can filter by * layer2 type. */ if (V_pfil_ipfw) { V_pfil_onlyip = 0; V_pfil_bridge = 0; V_pfil_member = 0; } } return (error); } SYSCTL_PROC(_net_link_bridge, OID_AUTO, ipfw, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_VNET, &VNET_NAME(pfil_ipfw), 0, &sysctl_pfil_ipfw, "I", "Layer2 filter with IPFW"); /* * bridge_clone_create: * * Create a new bridge instance. */ static int bridge_clone_create(struct if_clone *ifc, int unit, caddr_t params) { struct bridge_softc *sc, *sc2; struct ifnet *bifp, *ifp; int fb, retry; unsigned long hostid; sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO); ifp = sc->sc_ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { free(sc, M_DEVBUF); return (ENOSPC); } BRIDGE_LOCK_INIT(sc); sc->sc_brtmax = BRIDGE_RTABLE_MAX; sc->sc_brttimeout = BRIDGE_RTABLE_TIMEOUT; /* Initialize our routing table. */ bridge_rtable_init(sc); callout_init_mtx(&sc->sc_brcallout, &sc->sc_mtx, 0); LIST_INIT(&sc->sc_iflist); LIST_INIT(&sc->sc_spanlist); ifp->if_softc = sc; if_initname(ifp, bridge_name, unit); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = bridge_ioctl; ifp->if_transmit = bridge_transmit; ifp->if_qflush = bridge_qflush; ifp->if_init = bridge_init; ifp->if_type = IFT_BRIDGE; /* * Generate an ethernet address with a locally administered address. * * Since we are using random ethernet addresses for the bridge, it is * possible that we might have address collisions, so make sure that * this hardware address isn't already in use on another bridge. * The first try uses the hostid and falls back to arc4rand(). */ fb = 0; getcredhostid(curthread->td_ucred, &hostid); do { if (fb || hostid == 0) { arc4rand(sc->sc_defaddr, ETHER_ADDR_LEN, 1); sc->sc_defaddr[0] &= ~1;/* clear multicast bit */ sc->sc_defaddr[0] |= 2; /* set the LAA bit */ } else { sc->sc_defaddr[0] = 0x2; sc->sc_defaddr[1] = (hostid >> 24) & 0xff; sc->sc_defaddr[2] = (hostid >> 16) & 0xff; sc->sc_defaddr[3] = (hostid >> 8 ) & 0xff; sc->sc_defaddr[4] = hostid & 0xff; sc->sc_defaddr[5] = ifp->if_dunit & 0xff; } fb = 1; retry = 0; BRIDGE_LIST_LOCK(); LIST_FOREACH(sc2, &V_bridge_list, sc_list) { bifp = sc2->sc_ifp; if (memcmp(sc->sc_defaddr, IF_LLADDR(bifp), ETHER_ADDR_LEN) == 0) { retry = 1; break; } } BRIDGE_LIST_UNLOCK(); } while (retry == 1); bstp_attach(&sc->sc_stp, &bridge_ops); ether_ifattach(ifp, sc->sc_defaddr); /* Now undo some of the damage... */ ifp->if_baudrate = 0; ifp->if_type = IFT_BRIDGE; BRIDGE_LIST_LOCK(); LIST_INSERT_HEAD(&V_bridge_list, sc, sc_list); BRIDGE_LIST_UNLOCK(); return (0); } /* * bridge_clone_destroy: * * Destroy a bridge instance. */ static void bridge_clone_destroy(struct ifnet *ifp) { struct bridge_softc *sc = ifp->if_softc; struct bridge_iflist *bif; BRIDGE_LOCK(sc); bridge_stop(ifp, 1); ifp->if_flags &= ~IFF_UP; while ((bif = LIST_FIRST(&sc->sc_iflist)) != NULL) bridge_delete_member(sc, bif, 0); while ((bif = LIST_FIRST(&sc->sc_spanlist)) != NULL) { bridge_delete_span(sc, bif); } BRIDGE_UNLOCK(sc); callout_drain(&sc->sc_brcallout); BRIDGE_LIST_LOCK(); LIST_REMOVE(sc, sc_list); BRIDGE_LIST_UNLOCK(); bstp_detach(&sc->sc_stp); ether_ifdetach(ifp); if_free(ifp); /* Tear down the routing table. */ bridge_rtable_fini(sc); BRIDGE_LOCK_DESTROY(sc); free(sc, M_DEVBUF); } /* * bridge_ioctl: * * Handle a control request from the operator. */ static int bridge_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct bridge_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *)data; struct bridge_iflist *bif; struct thread *td = curthread; union { struct ifbreq ifbreq; struct ifbifconf ifbifconf; struct ifbareq ifbareq; struct ifbaconf ifbaconf; struct ifbrparam ifbrparam; struct ifbropreq ifbropreq; } args; struct ifdrv *ifd = (struct ifdrv *) data; const struct bridge_control *bc; int error = 0; switch (cmd) { case SIOCADDMULTI: case SIOCDELMULTI: break; case SIOCGDRVSPEC: case SIOCSDRVSPEC: if (ifd->ifd_cmd >= bridge_control_table_size) { error = EINVAL; break; } bc = &bridge_control_table[ifd->ifd_cmd]; if (cmd == SIOCGDRVSPEC && (bc->bc_flags & BC_F_COPYOUT) == 0) { error = EINVAL; break; } else if (cmd == SIOCSDRVSPEC && (bc->bc_flags & BC_F_COPYOUT) != 0) { error = EINVAL; break; } if (bc->bc_flags & BC_F_SUSER) { error = priv_check(td, PRIV_NET_BRIDGE); if (error) break; } if (ifd->ifd_len != bc->bc_argsize || ifd->ifd_len > sizeof(args)) { error = EINVAL; break; } bzero(&args, sizeof(args)); if (bc->bc_flags & BC_F_COPYIN) { error = copyin(ifd->ifd_data, &args, ifd->ifd_len); if (error) break; } BRIDGE_LOCK(sc); error = (*bc->bc_func)(sc, &args); BRIDGE_UNLOCK(sc); if (error) break; if (bc->bc_flags & BC_F_COPYOUT) error = copyout(&args, ifd->ifd_data, ifd->ifd_len); break; case SIOCSIFFLAGS: if (!(ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING)) { /* * If interface is marked down and it is running, * then stop and disable it. */ BRIDGE_LOCK(sc); bridge_stop(ifp, 1); BRIDGE_UNLOCK(sc); } else if ((ifp->if_flags & IFF_UP) && !(ifp->if_drv_flags & IFF_DRV_RUNNING)) { /* * If interface is marked up and it is stopped, then * start it. */ (*ifp->if_init)(sc); } break; case SIOCSIFMTU: if (ifr->ifr_mtu < 576) { error = EINVAL; break; } if (LIST_EMPTY(&sc->sc_iflist)) { sc->sc_ifp->if_mtu = ifr->ifr_mtu; break; } BRIDGE_LOCK(sc); LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { if (bif->bif_ifp->if_mtu != ifr->ifr_mtu) { log(LOG_NOTICE, "%s: invalid MTU: %u(%s)" " != %d\n", sc->sc_ifp->if_xname, bif->bif_ifp->if_mtu, bif->bif_ifp->if_xname, ifr->ifr_mtu); error = EINVAL; break; } } if (!error) sc->sc_ifp->if_mtu = ifr->ifr_mtu; BRIDGE_UNLOCK(sc); break; default: /* * drop the lock as ether_ioctl() will call bridge_start() and * cause the lock to be recursed. */ error = ether_ioctl(ifp, cmd, data); break; } return (error); } /* * bridge_mutecaps: * * Clear or restore unwanted capabilities on the member interface */ static void bridge_mutecaps(struct bridge_softc *sc) { struct bridge_iflist *bif; int enabled, mask; /* Initial bitmask of capabilities to test */ mask = BRIDGE_IFCAPS_MASK; LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { /* Every member must support it or its disabled */ mask &= bif->bif_savedcaps; } LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { enabled = bif->bif_ifp->if_capenable; enabled &= ~BRIDGE_IFCAPS_STRIP; /* strip off mask bits and enable them again if allowed */ enabled &= ~BRIDGE_IFCAPS_MASK; enabled |= mask; bridge_set_ifcap(sc, bif, enabled); } } static void bridge_set_ifcap(struct bridge_softc *sc, struct bridge_iflist *bif, int set) { struct ifnet *ifp = bif->bif_ifp; struct ifreq ifr; int error; bzero(&ifr, sizeof(ifr)); ifr.ifr_reqcap = set; if (ifp->if_capenable != set) { error = (*ifp->if_ioctl)(ifp, SIOCSIFCAP, (caddr_t)&ifr); if (error) if_printf(sc->sc_ifp, "error setting interface capabilities on %s\n", ifp->if_xname); } } /* * bridge_lookup_member: * * Lookup a bridge member interface. */ static struct bridge_iflist * bridge_lookup_member(struct bridge_softc *sc, const char *name) { struct bridge_iflist *bif; struct ifnet *ifp; BRIDGE_LOCK_ASSERT(sc); LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { ifp = bif->bif_ifp; if (strcmp(ifp->if_xname, name) == 0) return (bif); } return (NULL); } /* * bridge_lookup_member_if: * * Lookup a bridge member interface by ifnet*. */ static struct bridge_iflist * bridge_lookup_member_if(struct bridge_softc *sc, struct ifnet *member_ifp) { struct bridge_iflist *bif; BRIDGE_LOCK_ASSERT(sc); LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { if (bif->bif_ifp == member_ifp) return (bif); } return (NULL); } /* * bridge_delete_member: * * Delete the specified member interface. */ static void bridge_delete_member(struct bridge_softc *sc, struct bridge_iflist *bif, int gone) { struct ifnet *ifs = bif->bif_ifp; struct ifnet *fif = NULL; BRIDGE_LOCK_ASSERT(sc); if (bif->bif_flags & IFBIF_STP) bstp_disable(&bif->bif_stp); ifs->if_bridge = NULL; BRIDGE_XLOCK(sc); LIST_REMOVE(bif, bif_next); BRIDGE_XDROP(sc); /* * If removing the interface that gave the bridge its mac address, set * the mac address of the bridge to the address of the next member, or * to its default address if no members are left. */ if (V_bridge_inherit_mac && sc->sc_ifaddr == ifs) { if (LIST_EMPTY(&sc->sc_iflist)) { bcopy(sc->sc_defaddr, IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN); sc->sc_ifaddr = NULL; } else { fif = LIST_FIRST(&sc->sc_iflist)->bif_ifp; bcopy(IF_LLADDR(fif), IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN); sc->sc_ifaddr = fif; } EVENTHANDLER_INVOKE(iflladdr_event, sc->sc_ifp); } bridge_linkcheck(sc); bridge_mutecaps(sc); /* recalcuate now this interface is removed */ bridge_rtdelete(sc, ifs, IFBF_FLUSHALL); KASSERT(bif->bif_addrcnt == 0, ("%s: %d bridge routes referenced", __func__, bif->bif_addrcnt)); BRIDGE_UNLOCK(sc); if (!gone) { switch (ifs->if_type) { case IFT_ETHER: case IFT_L2VLAN: /* * Take the interface out of promiscuous mode. */ (void) ifpromisc(ifs, 0); break; case IFT_GIF: break; default: #ifdef DIAGNOSTIC panic("bridge_delete_member: impossible"); #endif break; } /* reneable any interface capabilities */ bridge_set_ifcap(sc, bif, bif->bif_savedcaps); } bstp_destroy(&bif->bif_stp); /* prepare to free */ BRIDGE_LOCK(sc); free(bif, M_DEVBUF); } /* * bridge_delete_span: * * Delete the specified span interface. */ static void bridge_delete_span(struct bridge_softc *sc, struct bridge_iflist *bif) { BRIDGE_LOCK_ASSERT(sc); KASSERT(bif->bif_ifp->if_bridge == NULL, ("%s: not a span interface", __func__)); LIST_REMOVE(bif, bif_next); free(bif, M_DEVBUF); } static int bridge_ioctl_add(struct bridge_softc *sc, void *arg) { struct ifbreq *req = arg; struct bridge_iflist *bif = NULL; struct ifnet *ifs; int error = 0; ifs = ifunit(req->ifbr_ifsname); if (ifs == NULL) return (ENOENT); if (ifs->if_ioctl == NULL) /* must be supported */ return (EINVAL); /* If it's in the span list, it can't be a member. */ LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) if (ifs == bif->bif_ifp) return (EBUSY); if (ifs->if_bridge == sc) return (EEXIST); if (ifs->if_bridge != NULL) return (EBUSY); switch (ifs->if_type) { case IFT_ETHER: case IFT_L2VLAN: case IFT_GIF: /* permitted interface types */ break; default: return (EINVAL); } #ifdef INET6 /* * Two valid inet6 addresses with link-local scope must not be * on the parent interface and the member interfaces at the * same time. This restriction is needed to prevent violation * of link-local scope zone. Attempts to add a member * interface which has inet6 addresses when the parent has * inet6 triggers removal of all inet6 addresses on the member * interface. */ /* Check if the parent interface has a link-local scope addr. */ if (V_allow_llz_overlap == 0 && in6ifa_llaonifp(sc->sc_ifp) != NULL) { /* * If any, remove all inet6 addresses from the member * interfaces. */ BRIDGE_XLOCK(sc); LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { if (in6ifa_llaonifp(bif->bif_ifp)) { BRIDGE_UNLOCK(sc); in6_ifdetach(bif->bif_ifp); BRIDGE_LOCK(sc); if_printf(sc->sc_ifp, "IPv6 addresses on %s have been removed " "before adding it as a member to prevent " "IPv6 address scope violation.\n", bif->bif_ifp->if_xname); } } BRIDGE_XDROP(sc); if (in6ifa_llaonifp(ifs)) { BRIDGE_UNLOCK(sc); in6_ifdetach(ifs); BRIDGE_LOCK(sc); if_printf(sc->sc_ifp, "IPv6 addresses on %s have been removed " "before adding it as a member to prevent " "IPv6 address scope violation.\n", ifs->if_xname); } } #endif /* Allow the first Ethernet member to define the MTU */ if (LIST_EMPTY(&sc->sc_iflist)) sc->sc_ifp->if_mtu = ifs->if_mtu; else if (sc->sc_ifp->if_mtu != ifs->if_mtu) { if_printf(sc->sc_ifp, "invalid MTU: %u(%s) != %u\n", ifs->if_mtu, ifs->if_xname, sc->sc_ifp->if_mtu); return (EINVAL); } bif = malloc(sizeof(*bif), M_DEVBUF, M_NOWAIT|M_ZERO); if (bif == NULL) return (ENOMEM); bif->bif_ifp = ifs; bif->bif_flags = IFBIF_LEARNING | IFBIF_DISCOVER; bif->bif_savedcaps = ifs->if_capenable; /* * Assign the interface's MAC address to the bridge if it's the first * member and the MAC address of the bridge has not been changed from * the default randomly generated one. */ if (V_bridge_inherit_mac && LIST_EMPTY(&sc->sc_iflist) && !memcmp(IF_LLADDR(sc->sc_ifp), sc->sc_defaddr, ETHER_ADDR_LEN)) { bcopy(IF_LLADDR(ifs), IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN); sc->sc_ifaddr = ifs; EVENTHANDLER_INVOKE(iflladdr_event, sc->sc_ifp); } ifs->if_bridge = sc; bstp_create(&sc->sc_stp, &bif->bif_stp, bif->bif_ifp); /* * XXX: XLOCK HERE!?! * * NOTE: insert_***HEAD*** should be safe for the traversals. */ LIST_INSERT_HEAD(&sc->sc_iflist, bif, bif_next); /* Set interface capabilities to the intersection set of all members */ bridge_mutecaps(sc); bridge_linkcheck(sc); /* Place the interface into promiscuous mode */ switch (ifs->if_type) { case IFT_ETHER: case IFT_L2VLAN: BRIDGE_UNLOCK(sc); error = ifpromisc(ifs, 1); BRIDGE_LOCK(sc); break; } if (error) { bridge_delete_member(sc, bif, 0); free(bif, M_DEVBUF); } return (error); } static int bridge_ioctl_del(struct bridge_softc *sc, void *arg) { struct ifbreq *req = arg; struct bridge_iflist *bif; bif = bridge_lookup_member(sc, req->ifbr_ifsname); if (bif == NULL) return (ENOENT); bridge_delete_member(sc, bif, 0); return (0); } static int bridge_ioctl_gifflags(struct bridge_softc *sc, void *arg) { struct ifbreq *req = arg; struct bridge_iflist *bif; struct bstp_port *bp; bif = bridge_lookup_member(sc, req->ifbr_ifsname); if (bif == NULL) return (ENOENT); bp = &bif->bif_stp; req->ifbr_ifsflags = bif->bif_flags; req->ifbr_state = bp->bp_state; req->ifbr_priority = bp->bp_priority; req->ifbr_path_cost = bp->bp_path_cost; req->ifbr_portno = bif->bif_ifp->if_index & 0xfff; req->ifbr_proto = bp->bp_protover; req->ifbr_role = bp->bp_role; req->ifbr_stpflags = bp->bp_flags; req->ifbr_addrcnt = bif->bif_addrcnt; req->ifbr_addrmax = bif->bif_addrmax; req->ifbr_addrexceeded = bif->bif_addrexceeded; /* Copy STP state options as flags */ if (bp->bp_operedge) req->ifbr_ifsflags |= IFBIF_BSTP_EDGE; if (bp->bp_flags & BSTP_PORT_AUTOEDGE) req->ifbr_ifsflags |= IFBIF_BSTP_AUTOEDGE; if (bp->bp_ptp_link) req->ifbr_ifsflags |= IFBIF_BSTP_PTP; if (bp->bp_flags & BSTP_PORT_AUTOPTP) req->ifbr_ifsflags |= IFBIF_BSTP_AUTOPTP; if (bp->bp_flags & BSTP_PORT_ADMEDGE) req->ifbr_ifsflags |= IFBIF_BSTP_ADMEDGE; if (bp->bp_flags & BSTP_PORT_ADMCOST) req->ifbr_ifsflags |= IFBIF_BSTP_ADMCOST; return (0); } static int bridge_ioctl_sifflags(struct bridge_softc *sc, void *arg) { struct ifbreq *req = arg; struct bridge_iflist *bif; struct bstp_port *bp; int error; bif = bridge_lookup_member(sc, req->ifbr_ifsname); if (bif == NULL) return (ENOENT); bp = &bif->bif_stp; if (req->ifbr_ifsflags & IFBIF_SPAN) /* SPAN is readonly */ return (EINVAL); if (req->ifbr_ifsflags & IFBIF_STP) { if ((bif->bif_flags & IFBIF_STP) == 0) { error = bstp_enable(&bif->bif_stp); if (error) return (error); } } else { if ((bif->bif_flags & IFBIF_STP) != 0) bstp_disable(&bif->bif_stp); } /* Pass on STP flags */ bstp_set_edge(bp, req->ifbr_ifsflags & IFBIF_BSTP_EDGE ? 1 : 0); bstp_set_autoedge(bp, req->ifbr_ifsflags & IFBIF_BSTP_AUTOEDGE ? 1 : 0); bstp_set_ptp(bp, req->ifbr_ifsflags & IFBIF_BSTP_PTP ? 1 : 0); bstp_set_autoptp(bp, req->ifbr_ifsflags & IFBIF_BSTP_AUTOPTP ? 1 : 0); /* Save the bits relating to the bridge */ bif->bif_flags = req->ifbr_ifsflags & IFBIFMASK; return (0); } static int bridge_ioctl_scache(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; sc->sc_brtmax = param->ifbrp_csize; bridge_rttrim(sc); return (0); } static int bridge_ioctl_gcache(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; param->ifbrp_csize = sc->sc_brtmax; return (0); } static int bridge_ioctl_gifs(struct bridge_softc *sc, void *arg) { struct ifbifconf *bifc = arg; struct bridge_iflist *bif; struct ifbreq breq; char *buf, *outbuf; int count, buflen, len, error = 0; count = 0; LIST_FOREACH(bif, &sc->sc_iflist, bif_next) count++; LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) count++; buflen = sizeof(breq) * count; if (bifc->ifbic_len == 0) { bifc->ifbic_len = buflen; return (0); } BRIDGE_UNLOCK(sc); outbuf = malloc(buflen, M_TEMP, M_WAITOK | M_ZERO); BRIDGE_LOCK(sc); count = 0; buf = outbuf; len = min(bifc->ifbic_len, buflen); bzero(&breq, sizeof(breq)); LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { if (len < sizeof(breq)) break; strlcpy(breq.ifbr_ifsname, bif->bif_ifp->if_xname, sizeof(breq.ifbr_ifsname)); /* Fill in the ifbreq structure */ error = bridge_ioctl_gifflags(sc, &breq); if (error) break; memcpy(buf, &breq, sizeof(breq)); count++; buf += sizeof(breq); len -= sizeof(breq); } LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) { if (len < sizeof(breq)) break; strlcpy(breq.ifbr_ifsname, bif->bif_ifp->if_xname, sizeof(breq.ifbr_ifsname)); breq.ifbr_ifsflags = bif->bif_flags; breq.ifbr_portno = bif->bif_ifp->if_index & 0xfff; memcpy(buf, &breq, sizeof(breq)); count++; buf += sizeof(breq); len -= sizeof(breq); } BRIDGE_UNLOCK(sc); bifc->ifbic_len = sizeof(breq) * count; error = copyout(outbuf, bifc->ifbic_req, bifc->ifbic_len); BRIDGE_LOCK(sc); free(outbuf, M_TEMP); return (error); } static int bridge_ioctl_rts(struct bridge_softc *sc, void *arg) { struct ifbaconf *bac = arg; struct bridge_rtnode *brt; struct ifbareq bareq; char *buf, *outbuf; int count, buflen, len, error = 0; if (bac->ifbac_len == 0) return (0); count = 0; LIST_FOREACH(brt, &sc->sc_rtlist, brt_list) count++; buflen = sizeof(bareq) * count; BRIDGE_UNLOCK(sc); outbuf = malloc(buflen, M_TEMP, M_WAITOK | M_ZERO); BRIDGE_LOCK(sc); count = 0; buf = outbuf; len = min(bac->ifbac_len, buflen); bzero(&bareq, sizeof(bareq)); LIST_FOREACH(brt, &sc->sc_rtlist, brt_list) { if (len < sizeof(bareq)) goto out; strlcpy(bareq.ifba_ifsname, brt->brt_ifp->if_xname, sizeof(bareq.ifba_ifsname)); memcpy(bareq.ifba_dst, brt->brt_addr, sizeof(brt->brt_addr)); bareq.ifba_vlan = brt->brt_vlan; if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC && time_uptime < brt->brt_expire) bareq.ifba_expire = brt->brt_expire - time_uptime; else bareq.ifba_expire = 0; bareq.ifba_flags = brt->brt_flags; memcpy(buf, &bareq, sizeof(bareq)); count++; buf += sizeof(bareq); len -= sizeof(bareq); } out: BRIDGE_UNLOCK(sc); bac->ifbac_len = sizeof(bareq) * count; error = copyout(outbuf, bac->ifbac_req, bac->ifbac_len); BRIDGE_LOCK(sc); free(outbuf, M_TEMP); return (error); } static int bridge_ioctl_saddr(struct bridge_softc *sc, void *arg) { struct ifbareq *req = arg; struct bridge_iflist *bif; int error; bif = bridge_lookup_member(sc, req->ifba_ifsname); if (bif == NULL) return (ENOENT); error = bridge_rtupdate(sc, req->ifba_dst, req->ifba_vlan, bif, 1, req->ifba_flags); return (error); } static int bridge_ioctl_sto(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; sc->sc_brttimeout = param->ifbrp_ctime; return (0); } static int bridge_ioctl_gto(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; param->ifbrp_ctime = sc->sc_brttimeout; return (0); } static int bridge_ioctl_daddr(struct bridge_softc *sc, void *arg) { struct ifbareq *req = arg; return (bridge_rtdaddr(sc, req->ifba_dst, req->ifba_vlan)); } static int bridge_ioctl_flush(struct bridge_softc *sc, void *arg) { struct ifbreq *req = arg; bridge_rtflush(sc, req->ifbr_ifsflags); return (0); } static int bridge_ioctl_gpri(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; struct bstp_state *bs = &sc->sc_stp; param->ifbrp_prio = bs->bs_bridge_priority; return (0); } static int bridge_ioctl_spri(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; return (bstp_set_priority(&sc->sc_stp, param->ifbrp_prio)); } static int bridge_ioctl_ght(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; struct bstp_state *bs = &sc->sc_stp; param->ifbrp_hellotime = bs->bs_bridge_htime >> 8; return (0); } static int bridge_ioctl_sht(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; return (bstp_set_htime(&sc->sc_stp, param->ifbrp_hellotime)); } static int bridge_ioctl_gfd(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; struct bstp_state *bs = &sc->sc_stp; param->ifbrp_fwddelay = bs->bs_bridge_fdelay >> 8; return (0); } static int bridge_ioctl_sfd(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; return (bstp_set_fdelay(&sc->sc_stp, param->ifbrp_fwddelay)); } static int bridge_ioctl_gma(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; struct bstp_state *bs = &sc->sc_stp; param->ifbrp_maxage = bs->bs_bridge_max_age >> 8; return (0); } static int bridge_ioctl_sma(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; return (bstp_set_maxage(&sc->sc_stp, param->ifbrp_maxage)); } static int bridge_ioctl_sifprio(struct bridge_softc *sc, void *arg) { struct ifbreq *req = arg; struct bridge_iflist *bif; bif = bridge_lookup_member(sc, req->ifbr_ifsname); if (bif == NULL) return (ENOENT); return (bstp_set_port_priority(&bif->bif_stp, req->ifbr_priority)); } static int bridge_ioctl_sifcost(struct bridge_softc *sc, void *arg) { struct ifbreq *req = arg; struct bridge_iflist *bif; bif = bridge_lookup_member(sc, req->ifbr_ifsname); if (bif == NULL) return (ENOENT); return (bstp_set_path_cost(&bif->bif_stp, req->ifbr_path_cost)); } static int bridge_ioctl_sifmaxaddr(struct bridge_softc *sc, void *arg) { struct ifbreq *req = arg; struct bridge_iflist *bif; bif = bridge_lookup_member(sc, req->ifbr_ifsname); if (bif == NULL) return (ENOENT); bif->bif_addrmax = req->ifbr_addrmax; return (0); } static int bridge_ioctl_addspan(struct bridge_softc *sc, void *arg) { struct ifbreq *req = arg; struct bridge_iflist *bif = NULL; struct ifnet *ifs; ifs = ifunit(req->ifbr_ifsname); if (ifs == NULL) return (ENOENT); LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) if (ifs == bif->bif_ifp) return (EBUSY); if (ifs->if_bridge != NULL) return (EBUSY); switch (ifs->if_type) { case IFT_ETHER: case IFT_GIF: case IFT_L2VLAN: break; default: return (EINVAL); } bif = malloc(sizeof(*bif), M_DEVBUF, M_NOWAIT|M_ZERO); if (bif == NULL) return (ENOMEM); bif->bif_ifp = ifs; bif->bif_flags = IFBIF_SPAN; LIST_INSERT_HEAD(&sc->sc_spanlist, bif, bif_next); return (0); } static int bridge_ioctl_delspan(struct bridge_softc *sc, void *arg) { struct ifbreq *req = arg; struct bridge_iflist *bif; struct ifnet *ifs; ifs = ifunit(req->ifbr_ifsname); if (ifs == NULL) return (ENOENT); LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) if (ifs == bif->bif_ifp) break; if (bif == NULL) return (ENOENT); bridge_delete_span(sc, bif); return (0); } static int bridge_ioctl_gbparam(struct bridge_softc *sc, void *arg) { struct ifbropreq *req = arg; struct bstp_state *bs = &sc->sc_stp; struct bstp_port *root_port; req->ifbop_maxage = bs->bs_bridge_max_age >> 8; req->ifbop_hellotime = bs->bs_bridge_htime >> 8; req->ifbop_fwddelay = bs->bs_bridge_fdelay >> 8; root_port = bs->bs_root_port; if (root_port == NULL) req->ifbop_root_port = 0; else req->ifbop_root_port = root_port->bp_ifp->if_index; req->ifbop_holdcount = bs->bs_txholdcount; req->ifbop_priority = bs->bs_bridge_priority; req->ifbop_protocol = bs->bs_protover; req->ifbop_root_path_cost = bs->bs_root_pv.pv_cost; req->ifbop_bridgeid = bs->bs_bridge_pv.pv_dbridge_id; req->ifbop_designated_root = bs->bs_root_pv.pv_root_id; req->ifbop_designated_bridge = bs->bs_root_pv.pv_dbridge_id; req->ifbop_last_tc_time.tv_sec = bs->bs_last_tc_time.tv_sec; req->ifbop_last_tc_time.tv_usec = bs->bs_last_tc_time.tv_usec; return (0); } static int bridge_ioctl_grte(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; param->ifbrp_cexceeded = sc->sc_brtexceeded; return (0); } static int bridge_ioctl_gifsstp(struct bridge_softc *sc, void *arg) { struct ifbpstpconf *bifstp = arg; struct bridge_iflist *bif; struct bstp_port *bp; struct ifbpstpreq bpreq; char *buf, *outbuf; int count, buflen, len, error = 0; count = 0; LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { if ((bif->bif_flags & IFBIF_STP) != 0) count++; } buflen = sizeof(bpreq) * count; if (bifstp->ifbpstp_len == 0) { bifstp->ifbpstp_len = buflen; return (0); } BRIDGE_UNLOCK(sc); outbuf = malloc(buflen, M_TEMP, M_WAITOK | M_ZERO); BRIDGE_LOCK(sc); count = 0; buf = outbuf; len = min(bifstp->ifbpstp_len, buflen); bzero(&bpreq, sizeof(bpreq)); LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { if (len < sizeof(bpreq)) break; if ((bif->bif_flags & IFBIF_STP) == 0) continue; bp = &bif->bif_stp; bpreq.ifbp_portno = bif->bif_ifp->if_index & 0xfff; bpreq.ifbp_fwd_trans = bp->bp_forward_transitions; bpreq.ifbp_design_cost = bp->bp_desg_pv.pv_cost; bpreq.ifbp_design_port = bp->bp_desg_pv.pv_port_id; bpreq.ifbp_design_bridge = bp->bp_desg_pv.pv_dbridge_id; bpreq.ifbp_design_root = bp->bp_desg_pv.pv_root_id; memcpy(buf, &bpreq, sizeof(bpreq)); count++; buf += sizeof(bpreq); len -= sizeof(bpreq); } BRIDGE_UNLOCK(sc); bifstp->ifbpstp_len = sizeof(bpreq) * count; error = copyout(outbuf, bifstp->ifbpstp_req, bifstp->ifbpstp_len); BRIDGE_LOCK(sc); free(outbuf, M_TEMP); return (error); } static int bridge_ioctl_sproto(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; return (bstp_set_protocol(&sc->sc_stp, param->ifbrp_proto)); } static int bridge_ioctl_stxhc(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; return (bstp_set_holdcount(&sc->sc_stp, param->ifbrp_txhc)); } /* * bridge_ifdetach: * * Detach an interface from a bridge. Called when a member * interface is detaching. */ static void bridge_ifdetach(void *arg __unused, struct ifnet *ifp) { struct bridge_softc *sc = ifp->if_bridge; struct bridge_iflist *bif; if (ifp->if_flags & IFF_RENAMING) return; - + if (V_bridge_cloner == NULL) { + /* + * This detach handler can be called after + * vnet_bridge_uninit(). Just return in that case. + */ + return; + } /* Check if the interface is a bridge member */ if (sc != NULL) { BRIDGE_LOCK(sc); bif = bridge_lookup_member_if(sc, ifp); if (bif != NULL) bridge_delete_member(sc, bif, 1); BRIDGE_UNLOCK(sc); return; } /* Check if the interface is a span port */ BRIDGE_LIST_LOCK(); LIST_FOREACH(sc, &V_bridge_list, sc_list) { BRIDGE_LOCK(sc); LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) if (ifp == bif->bif_ifp) { bridge_delete_span(sc, bif); break; } BRIDGE_UNLOCK(sc); } BRIDGE_LIST_UNLOCK(); } /* * bridge_init: * * Initialize a bridge interface. */ static void bridge_init(void *xsc) { struct bridge_softc *sc = (struct bridge_softc *)xsc; struct ifnet *ifp = sc->sc_ifp; if (ifp->if_drv_flags & IFF_DRV_RUNNING) return; BRIDGE_LOCK(sc); callout_reset(&sc->sc_brcallout, bridge_rtable_prune_period * hz, bridge_timer, sc); ifp->if_drv_flags |= IFF_DRV_RUNNING; bstp_init(&sc->sc_stp); /* Initialize Spanning Tree */ BRIDGE_UNLOCK(sc); } /* * bridge_stop: * * Stop the bridge interface. */ static void bridge_stop(struct ifnet *ifp, int disable) { struct bridge_softc *sc = ifp->if_softc; BRIDGE_LOCK_ASSERT(sc); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) return; callout_stop(&sc->sc_brcallout); bstp_stop(&sc->sc_stp); bridge_rtflush(sc, IFBF_FLUSHDYN); ifp->if_drv_flags &= ~IFF_DRV_RUNNING; } /* * bridge_enqueue: * * Enqueue a packet on a bridge member interface. * */ static int bridge_enqueue(struct bridge_softc *sc, struct ifnet *dst_ifp, struct mbuf *m) { int len, err = 0; short mflags; struct mbuf *m0; /* We may be sending a fragment so traverse the mbuf */ for (; m; m = m0) { m0 = m->m_nextpkt; m->m_nextpkt = NULL; len = m->m_pkthdr.len; mflags = m->m_flags; /* * If underlying interface can not do VLAN tag insertion itself * then attach a packet tag that holds it. */ if ((m->m_flags & M_VLANTAG) && (dst_ifp->if_capenable & IFCAP_VLAN_HWTAGGING) == 0) { m = ether_vlanencap(m, m->m_pkthdr.ether_vtag); if (m == NULL) { if_printf(dst_ifp, "unable to prepend VLAN header\n"); if_inc_counter(dst_ifp, IFCOUNTER_OERRORS, 1); continue; } m->m_flags &= ~M_VLANTAG; } if ((err = dst_ifp->if_transmit(dst_ifp, m))) { m_freem(m0); if_inc_counter(sc->sc_ifp, IFCOUNTER_OERRORS, 1); break; } if_inc_counter(sc->sc_ifp, IFCOUNTER_OPACKETS, 1); if_inc_counter(sc->sc_ifp, IFCOUNTER_OBYTES, len); if (mflags & M_MCAST) if_inc_counter(sc->sc_ifp, IFCOUNTER_OMCASTS, 1); } return (err); } /* * bridge_dummynet: * * Receive a queued packet from dummynet and pass it on to the output * interface. * * The mbuf has the Ethernet header already attached. */ static void bridge_dummynet(struct mbuf *m, struct ifnet *ifp) { struct bridge_softc *sc; sc = ifp->if_bridge; /* * The packet didnt originate from a member interface. This should only * ever happen if a member interface is removed while packets are * queued for it. */ if (sc == NULL) { m_freem(m); return; } if (PFIL_HOOKED(&V_inet_pfil_hook) #ifdef INET6 || PFIL_HOOKED(&V_inet6_pfil_hook) #endif ) { if (bridge_pfil(&m, sc->sc_ifp, ifp, PFIL_OUT) != 0) return; if (m == NULL) return; } bridge_enqueue(sc, ifp, m); } /* * bridge_output: * * Send output from a bridge member interface. This * performs the bridging function for locally originated * packets. * * The mbuf has the Ethernet header already attached. We must * enqueue or free the mbuf before returning. */ static int bridge_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa, struct rtentry *rt) { struct ether_header *eh; struct ifnet *dst_if; struct bridge_softc *sc; uint16_t vlan; if (m->m_len < ETHER_HDR_LEN) { m = m_pullup(m, ETHER_HDR_LEN); if (m == NULL) return (0); } eh = mtod(m, struct ether_header *); sc = ifp->if_bridge; vlan = VLANTAGOF(m); BRIDGE_LOCK(sc); /* * If bridge is down, but the original output interface is up, * go ahead and send out that interface. Otherwise, the packet * is dropped below. */ if ((sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { dst_if = ifp; goto sendunicast; } /* * If the packet is a multicast, or we don't know a better way to * get there, send to all interfaces. */ if (ETHER_IS_MULTICAST(eh->ether_dhost)) dst_if = NULL; else dst_if = bridge_rtlookup(sc, eh->ether_dhost, vlan); if (dst_if == NULL) { struct bridge_iflist *bif; struct mbuf *mc; int error = 0, used = 0; bridge_span(sc, m); BRIDGE_LOCK2REF(sc, error); if (error) { m_freem(m); return (0); } LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { dst_if = bif->bif_ifp; if (dst_if->if_type == IFT_GIF) continue; if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) continue; /* * If this is not the original output interface, * and the interface is participating in spanning * tree, make sure the port is in a state that * allows forwarding. */ if (dst_if != ifp && (bif->bif_flags & IFBIF_STP) && bif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING) continue; if (LIST_NEXT(bif, bif_next) == NULL) { used = 1; mc = m; } else { mc = m_copypacket(m, M_NOWAIT); if (mc == NULL) { if_inc_counter(sc->sc_ifp, IFCOUNTER_OERRORS, 1); continue; } } bridge_enqueue(sc, dst_if, mc); } if (used == 0) m_freem(m); BRIDGE_UNREF(sc); return (0); } sendunicast: /* * XXX Spanning tree consideration here? */ bridge_span(sc, m); if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) { m_freem(m); BRIDGE_UNLOCK(sc); return (0); } BRIDGE_UNLOCK(sc); bridge_enqueue(sc, dst_if, m); return (0); } /* * bridge_transmit: * * Do output on a bridge. * */ static int bridge_transmit(struct ifnet *ifp, struct mbuf *m) { struct bridge_softc *sc; struct ether_header *eh; struct ifnet *dst_if; int error = 0; sc = ifp->if_softc; ETHER_BPF_MTAP(ifp, m); eh = mtod(m, struct ether_header *); BRIDGE_LOCK(sc); if (((m->m_flags & (M_BCAST|M_MCAST)) == 0) && (dst_if = bridge_rtlookup(sc, eh->ether_dhost, 1)) != NULL) { BRIDGE_UNLOCK(sc); error = bridge_enqueue(sc, dst_if, m); } else bridge_broadcast(sc, ifp, m, 0); return (error); } /* * The ifp->if_qflush entry point for if_bridge(4) is no-op. */ static void bridge_qflush(struct ifnet *ifp __unused) { } /* * bridge_forward: * * The forwarding function of the bridge. * * NOTE: Releases the lock on return. */ static void bridge_forward(struct bridge_softc *sc, struct bridge_iflist *sbif, struct mbuf *m) { struct bridge_iflist *dbif; struct ifnet *src_if, *dst_if, *ifp; struct ether_header *eh; uint16_t vlan; uint8_t *dst; int error; src_if = m->m_pkthdr.rcvif; ifp = sc->sc_ifp; if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len); vlan = VLANTAGOF(m); if ((sbif->bif_flags & IFBIF_STP) && sbif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING) goto drop; eh = mtod(m, struct ether_header *); dst = eh->ether_dhost; /* If the interface is learning, record the address. */ if (sbif->bif_flags & IFBIF_LEARNING) { error = bridge_rtupdate(sc, eh->ether_shost, vlan, sbif, 0, IFBAF_DYNAMIC); /* * If the interface has addresses limits then deny any source * that is not in the cache. */ if (error && sbif->bif_addrmax) goto drop; } if ((sbif->bif_flags & IFBIF_STP) != 0 && sbif->bif_stp.bp_state == BSTP_IFSTATE_LEARNING) goto drop; /* * At this point, the port either doesn't participate * in spanning tree or it is in the forwarding state. */ /* * If the packet is unicast, destined for someone on * "this" side of the bridge, drop it. */ if ((m->m_flags & (M_BCAST|M_MCAST)) == 0) { dst_if = bridge_rtlookup(sc, dst, vlan); if (src_if == dst_if) goto drop; } else { /* * Check if its a reserved multicast address, any address * listed in 802.1D section 7.12.6 may not be forwarded by the * bridge. * This is currently 01-80-C2-00-00-00 to 01-80-C2-00-00-0F */ if (dst[0] == 0x01 && dst[1] == 0x80 && dst[2] == 0xc2 && dst[3] == 0x00 && dst[4] == 0x00 && dst[5] <= 0x0f) goto drop; /* ...forward it to all interfaces. */ if_inc_counter(ifp, IFCOUNTER_IMCASTS, 1); dst_if = NULL; } /* * If we have a destination interface which is a member of our bridge, * OR this is a unicast packet, push it through the bpf(4) machinery. * For broadcast or multicast packets, don't bother because it will * be reinjected into ether_input. We do this before we pass the packets * through the pfil(9) framework, as it is possible that pfil(9) will * drop the packet, or possibly modify it, making it difficult to debug * firewall issues on the bridge. */ if (dst_if != NULL || (m->m_flags & (M_BCAST | M_MCAST)) == 0) ETHER_BPF_MTAP(ifp, m); /* run the packet filter */ if (PFIL_HOOKED(&V_inet_pfil_hook) #ifdef INET6 || PFIL_HOOKED(&V_inet6_pfil_hook) #endif ) { BRIDGE_UNLOCK(sc); if (bridge_pfil(&m, ifp, src_if, PFIL_IN) != 0) return; if (m == NULL) return; BRIDGE_LOCK(sc); } if (dst_if == NULL) { bridge_broadcast(sc, src_if, m, 1); return; } /* * At this point, we're dealing with a unicast frame * going to a different interface. */ if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) goto drop; dbif = bridge_lookup_member_if(sc, dst_if); if (dbif == NULL) /* Not a member of the bridge (anymore?) */ goto drop; /* Private segments can not talk to each other */ if (sbif->bif_flags & dbif->bif_flags & IFBIF_PRIVATE) goto drop; if ((dbif->bif_flags & IFBIF_STP) && dbif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING) goto drop; BRIDGE_UNLOCK(sc); if (PFIL_HOOKED(&V_inet_pfil_hook) #ifdef INET6 || PFIL_HOOKED(&V_inet6_pfil_hook) #endif ) { if (bridge_pfil(&m, ifp, dst_if, PFIL_OUT) != 0) return; if (m == NULL) return; } bridge_enqueue(sc, dst_if, m); return; drop: BRIDGE_UNLOCK(sc); m_freem(m); } /* * bridge_input: * * Receive input from a member interface. Queue the packet for * bridging if it is not for us. */ static struct mbuf * bridge_input(struct ifnet *ifp, struct mbuf *m) { struct bridge_softc *sc = ifp->if_bridge; struct bridge_iflist *bif, *bif2; struct ifnet *bifp; struct ether_header *eh; struct mbuf *mc, *mc2; uint16_t vlan; int error; if ((sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) return (m); bifp = sc->sc_ifp; vlan = VLANTAGOF(m); /* * Implement support for bridge monitoring. If this flag has been * set on this interface, discard the packet once we push it through * the bpf(4) machinery, but before we do, increment the byte and * packet counters associated with this interface. */ if ((bifp->if_flags & IFF_MONITOR) != 0) { m->m_pkthdr.rcvif = bifp; ETHER_BPF_MTAP(bifp, m); if_inc_counter(bifp, IFCOUNTER_IPACKETS, 1); if_inc_counter(bifp, IFCOUNTER_IBYTES, m->m_pkthdr.len); m_freem(m); return (NULL); } BRIDGE_LOCK(sc); bif = bridge_lookup_member_if(sc, ifp); if (bif == NULL) { BRIDGE_UNLOCK(sc); return (m); } eh = mtod(m, struct ether_header *); bridge_span(sc, m); if (m->m_flags & (M_BCAST|M_MCAST)) { /* Tap off 802.1D packets; they do not get forwarded. */ if (memcmp(eh->ether_dhost, bstp_etheraddr, ETHER_ADDR_LEN) == 0) { bstp_input(&bif->bif_stp, ifp, m); /* consumes mbuf */ BRIDGE_UNLOCK(sc); return (NULL); } if ((bif->bif_flags & IFBIF_STP) && bif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING) { BRIDGE_UNLOCK(sc); return (m); } /* * Make a deep copy of the packet and enqueue the copy * for bridge processing; return the original packet for * local processing. */ mc = m_dup(m, M_NOWAIT); if (mc == NULL) { BRIDGE_UNLOCK(sc); return (m); } /* Perform the bridge forwarding function with the copy. */ bridge_forward(sc, bif, mc); /* * Reinject the mbuf as arriving on the bridge so we have a * chance at claiming multicast packets. We can not loop back * here from ether_input as a bridge is never a member of a * bridge. */ KASSERT(bifp->if_bridge == NULL, ("loop created in bridge_input")); mc2 = m_dup(m, M_NOWAIT); if (mc2 != NULL) { /* Keep the layer3 header aligned */ int i = min(mc2->m_pkthdr.len, max_protohdr); mc2 = m_copyup(mc2, i, ETHER_ALIGN); } if (mc2 != NULL) { mc2->m_pkthdr.rcvif = bifp; (*bifp->if_input)(bifp, mc2); } /* Return the original packet for local processing. */ return (m); } if ((bif->bif_flags & IFBIF_STP) && bif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING) { BRIDGE_UNLOCK(sc); return (m); } #if (defined(INET) || defined(INET6)) # define OR_CARP_CHECK_WE_ARE_DST(iface) \ || ((iface)->if_carp \ && (*carp_forus_p)((iface), eh->ether_dhost)) # define OR_CARP_CHECK_WE_ARE_SRC(iface) \ || ((iface)->if_carp \ && (*carp_forus_p)((iface), eh->ether_shost)) #else # define OR_CARP_CHECK_WE_ARE_DST(iface) # define OR_CARP_CHECK_WE_ARE_SRC(iface) #endif #ifdef INET6 # define OR_PFIL_HOOKED_INET6 \ || PFIL_HOOKED(&V_inet6_pfil_hook) #else # define OR_PFIL_HOOKED_INET6 #endif #define GRAB_OUR_PACKETS(iface) \ if ((iface)->if_type == IFT_GIF) \ continue; \ /* It is destined for us. */ \ if (memcmp(IF_LLADDR((iface)), eh->ether_dhost, ETHER_ADDR_LEN) == 0 \ OR_CARP_CHECK_WE_ARE_DST((iface)) \ ) { \ if ((iface)->if_type == IFT_BRIDGE) { \ ETHER_BPF_MTAP(iface, m); \ if_inc_counter(iface, IFCOUNTER_IPACKETS, 1); \ if_inc_counter(iface, IFCOUNTER_IBYTES, m->m_pkthdr.len); \ /* Filter on the physical interface. */ \ if (V_pfil_local_phys && \ (PFIL_HOOKED(&V_inet_pfil_hook) \ OR_PFIL_HOOKED_INET6)) { \ if (bridge_pfil(&m, NULL, ifp, \ PFIL_IN) != 0 || m == NULL) { \ BRIDGE_UNLOCK(sc); \ return (NULL); \ } \ eh = mtod(m, struct ether_header *); \ } \ } \ if (bif->bif_flags & IFBIF_LEARNING) { \ error = bridge_rtupdate(sc, eh->ether_shost, \ vlan, bif, 0, IFBAF_DYNAMIC); \ if (error && bif->bif_addrmax) { \ BRIDGE_UNLOCK(sc); \ m_freem(m); \ return (NULL); \ } \ } \ m->m_pkthdr.rcvif = iface; \ BRIDGE_UNLOCK(sc); \ return (m); \ } \ \ /* We just received a packet that we sent out. */ \ if (memcmp(IF_LLADDR((iface)), eh->ether_shost, ETHER_ADDR_LEN) == 0 \ OR_CARP_CHECK_WE_ARE_SRC((iface)) \ ) { \ BRIDGE_UNLOCK(sc); \ m_freem(m); \ return (NULL); \ } /* * Unicast. Make sure it's not for the bridge. */ do { GRAB_OUR_PACKETS(bifp) } while (0); /* * Give a chance for ifp at first priority. This will help when the * packet comes through the interface like VLAN's with the same MACs * on several interfaces from the same bridge. This also will save * some CPU cycles in case the destination interface and the input * interface (eq ifp) are the same. */ do { GRAB_OUR_PACKETS(ifp) } while (0); /* Now check the all bridge members. */ LIST_FOREACH(bif2, &sc->sc_iflist, bif_next) { GRAB_OUR_PACKETS(bif2->bif_ifp) } #undef OR_CARP_CHECK_WE_ARE_DST #undef OR_CARP_CHECK_WE_ARE_SRC #undef OR_PFIL_HOOKED_INET6 #undef GRAB_OUR_PACKETS /* Perform the bridge forwarding function. */ bridge_forward(sc, bif, m); return (NULL); } /* * bridge_broadcast: * * Send a frame to all interfaces that are members of * the bridge, except for the one on which the packet * arrived. * * NOTE: Releases the lock on return. */ static void bridge_broadcast(struct bridge_softc *sc, struct ifnet *src_if, struct mbuf *m, int runfilt) { struct bridge_iflist *dbif, *sbif; struct mbuf *mc; struct ifnet *dst_if; int error = 0, used = 0, i; sbif = bridge_lookup_member_if(sc, src_if); BRIDGE_LOCK2REF(sc, error); if (error) { m_freem(m); return; } /* Filter on the bridge interface before broadcasting */ if (runfilt && (PFIL_HOOKED(&V_inet_pfil_hook) #ifdef INET6 || PFIL_HOOKED(&V_inet6_pfil_hook) #endif )) { if (bridge_pfil(&m, sc->sc_ifp, NULL, PFIL_OUT) != 0) goto out; if (m == NULL) goto out; } LIST_FOREACH(dbif, &sc->sc_iflist, bif_next) { dst_if = dbif->bif_ifp; if (dst_if == src_if) continue; /* Private segments can not talk to each other */ if (sbif && (sbif->bif_flags & dbif->bif_flags & IFBIF_PRIVATE)) continue; if ((dbif->bif_flags & IFBIF_STP) && dbif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING) continue; if ((dbif->bif_flags & IFBIF_DISCOVER) == 0 && (m->m_flags & (M_BCAST|M_MCAST)) == 0) continue; if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) continue; if (LIST_NEXT(dbif, bif_next) == NULL) { mc = m; used = 1; } else { mc = m_dup(m, M_NOWAIT); if (mc == NULL) { if_inc_counter(sc->sc_ifp, IFCOUNTER_OERRORS, 1); continue; } } /* * Filter on the output interface. Pass a NULL bridge interface * pointer so we do not redundantly filter on the bridge for * each interface we broadcast on. */ if (runfilt && (PFIL_HOOKED(&V_inet_pfil_hook) #ifdef INET6 || PFIL_HOOKED(&V_inet6_pfil_hook) #endif )) { if (used == 0) { /* Keep the layer3 header aligned */ i = min(mc->m_pkthdr.len, max_protohdr); mc = m_copyup(mc, i, ETHER_ALIGN); if (mc == NULL) { if_inc_counter(sc->sc_ifp, IFCOUNTER_OERRORS, 1); continue; } } if (bridge_pfil(&mc, NULL, dst_if, PFIL_OUT) != 0) continue; if (mc == NULL) continue; } bridge_enqueue(sc, dst_if, mc); } if (used == 0) m_freem(m); out: BRIDGE_UNREF(sc); } /* * bridge_span: * * Duplicate a packet out one or more interfaces that are in span mode, * the original mbuf is unmodified. */ static void bridge_span(struct bridge_softc *sc, struct mbuf *m) { struct bridge_iflist *bif; struct ifnet *dst_if; struct mbuf *mc; if (LIST_EMPTY(&sc->sc_spanlist)) return; LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) { dst_if = bif->bif_ifp; if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) continue; mc = m_copypacket(m, M_NOWAIT); if (mc == NULL) { if_inc_counter(sc->sc_ifp, IFCOUNTER_OERRORS, 1); continue; } bridge_enqueue(sc, dst_if, mc); } } /* * bridge_rtupdate: * * Add a bridge routing entry. */ static int bridge_rtupdate(struct bridge_softc *sc, const uint8_t *dst, uint16_t vlan, struct bridge_iflist *bif, int setflags, uint8_t flags) { struct bridge_rtnode *brt; int error; BRIDGE_LOCK_ASSERT(sc); /* Check the source address is valid and not multicast. */ if (ETHER_IS_MULTICAST(dst) || (dst[0] == 0 && dst[1] == 0 && dst[2] == 0 && dst[3] == 0 && dst[4] == 0 && dst[5] == 0) != 0) return (EINVAL); /* 802.1p frames map to vlan 1 */ if (vlan == 0) vlan = 1; /* * A route for this destination might already exist. If so, * update it, otherwise create a new one. */ if ((brt = bridge_rtnode_lookup(sc, dst, vlan)) == NULL) { if (sc->sc_brtcnt >= sc->sc_brtmax) { sc->sc_brtexceeded++; return (ENOSPC); } /* Check per interface address limits (if enabled) */ if (bif->bif_addrmax && bif->bif_addrcnt >= bif->bif_addrmax) { bif->bif_addrexceeded++; return (ENOSPC); } /* * Allocate a new bridge forwarding node, and * initialize the expiration time and Ethernet * address. */ brt = uma_zalloc(bridge_rtnode_zone, M_NOWAIT | M_ZERO); if (brt == NULL) return (ENOMEM); if (bif->bif_flags & IFBIF_STICKY) brt->brt_flags = IFBAF_STICKY; else brt->brt_flags = IFBAF_DYNAMIC; memcpy(brt->brt_addr, dst, ETHER_ADDR_LEN); brt->brt_vlan = vlan; if ((error = bridge_rtnode_insert(sc, brt)) != 0) { uma_zfree(bridge_rtnode_zone, brt); return (error); } brt->brt_dst = bif; bif->bif_addrcnt++; } if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC && brt->brt_dst != bif) { brt->brt_dst->bif_addrcnt--; brt->brt_dst = bif; brt->brt_dst->bif_addrcnt++; } if ((flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) brt->brt_expire = time_uptime + sc->sc_brttimeout; if (setflags) brt->brt_flags = flags; return (0); } /* * bridge_rtlookup: * * Lookup the destination interface for an address. */ static struct ifnet * bridge_rtlookup(struct bridge_softc *sc, const uint8_t *addr, uint16_t vlan) { struct bridge_rtnode *brt; BRIDGE_LOCK_ASSERT(sc); if ((brt = bridge_rtnode_lookup(sc, addr, vlan)) == NULL) return (NULL); return (brt->brt_ifp); } /* * bridge_rttrim: * * Trim the routine table so that we have a number * of routing entries less than or equal to the * maximum number. */ static void bridge_rttrim(struct bridge_softc *sc) { struct bridge_rtnode *brt, *nbrt; BRIDGE_LOCK_ASSERT(sc); /* Make sure we actually need to do this. */ if (sc->sc_brtcnt <= sc->sc_brtmax) return; /* Force an aging cycle; this might trim enough addresses. */ bridge_rtage(sc); if (sc->sc_brtcnt <= sc->sc_brtmax) return; LIST_FOREACH_SAFE(brt, &sc->sc_rtlist, brt_list, nbrt) { if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) { bridge_rtnode_destroy(sc, brt); if (sc->sc_brtcnt <= sc->sc_brtmax) return; } } } /* * bridge_timer: * * Aging timer for the bridge. */ static void bridge_timer(void *arg) { struct bridge_softc *sc = arg; BRIDGE_LOCK_ASSERT(sc); bridge_rtage(sc); if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) callout_reset(&sc->sc_brcallout, bridge_rtable_prune_period * hz, bridge_timer, sc); } /* * bridge_rtage: * * Perform an aging cycle. */ static void bridge_rtage(struct bridge_softc *sc) { struct bridge_rtnode *brt, *nbrt; BRIDGE_LOCK_ASSERT(sc); LIST_FOREACH_SAFE(brt, &sc->sc_rtlist, brt_list, nbrt) { if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) { if (time_uptime >= brt->brt_expire) bridge_rtnode_destroy(sc, brt); } } } /* * bridge_rtflush: * * Remove all dynamic addresses from the bridge. */ static void bridge_rtflush(struct bridge_softc *sc, int full) { struct bridge_rtnode *brt, *nbrt; BRIDGE_LOCK_ASSERT(sc); LIST_FOREACH_SAFE(brt, &sc->sc_rtlist, brt_list, nbrt) { if (full || (brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) bridge_rtnode_destroy(sc, brt); } } /* * bridge_rtdaddr: * * Remove an address from the table. */ static int bridge_rtdaddr(struct bridge_softc *sc, const uint8_t *addr, uint16_t vlan) { struct bridge_rtnode *brt; int found = 0; BRIDGE_LOCK_ASSERT(sc); /* * If vlan is zero then we want to delete for all vlans so the lookup * may return more than one. */ while ((brt = bridge_rtnode_lookup(sc, addr, vlan)) != NULL) { bridge_rtnode_destroy(sc, brt); found = 1; } return (found ? 0 : ENOENT); } /* * bridge_rtdelete: * * Delete routes to a speicifc member interface. */ static void bridge_rtdelete(struct bridge_softc *sc, struct ifnet *ifp, int full) { struct bridge_rtnode *brt, *nbrt; BRIDGE_LOCK_ASSERT(sc); LIST_FOREACH_SAFE(brt, &sc->sc_rtlist, brt_list, nbrt) { if (brt->brt_ifp == ifp && (full || (brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC)) bridge_rtnode_destroy(sc, brt); } } /* * bridge_rtable_init: * * Initialize the route table for this bridge. */ static void bridge_rtable_init(struct bridge_softc *sc) { int i; sc->sc_rthash = malloc(sizeof(*sc->sc_rthash) * BRIDGE_RTHASH_SIZE, M_DEVBUF, M_WAITOK); for (i = 0; i < BRIDGE_RTHASH_SIZE; i++) LIST_INIT(&sc->sc_rthash[i]); sc->sc_rthash_key = arc4random(); LIST_INIT(&sc->sc_rtlist); } /* * bridge_rtable_fini: * * Deconstruct the route table for this bridge. */ static void bridge_rtable_fini(struct bridge_softc *sc) { KASSERT(sc->sc_brtcnt == 0, ("%s: %d bridge routes referenced", __func__, sc->sc_brtcnt)); free(sc->sc_rthash, M_DEVBUF); } /* * The following hash function is adapted from "Hash Functions" by Bob Jenkins * ("Algorithm Alley", Dr. Dobbs Journal, September 1997). */ #define mix(a, b, c) \ do { \ a -= b; a -= c; a ^= (c >> 13); \ b -= c; b -= a; b ^= (a << 8); \ c -= a; c -= b; c ^= (b >> 13); \ a -= b; a -= c; a ^= (c >> 12); \ b -= c; b -= a; b ^= (a << 16); \ c -= a; c -= b; c ^= (b >> 5); \ a -= b; a -= c; a ^= (c >> 3); \ b -= c; b -= a; b ^= (a << 10); \ c -= a; c -= b; c ^= (b >> 15); \ } while (/*CONSTCOND*/0) static __inline uint32_t bridge_rthash(struct bridge_softc *sc, const uint8_t *addr) { uint32_t a = 0x9e3779b9, b = 0x9e3779b9, c = sc->sc_rthash_key; b += addr[5] << 8; b += addr[4]; a += addr[3] << 24; a += addr[2] << 16; a += addr[1] << 8; a += addr[0]; mix(a, b, c); return (c & BRIDGE_RTHASH_MASK); } #undef mix static int bridge_rtnode_addr_cmp(const uint8_t *a, const uint8_t *b) { int i, d; for (i = 0, d = 0; i < ETHER_ADDR_LEN && d == 0; i++) { d = ((int)a[i]) - ((int)b[i]); } return (d); } /* * bridge_rtnode_lookup: * * Look up a bridge route node for the specified destination. Compare the * vlan id or if zero then just return the first match. */ static struct bridge_rtnode * bridge_rtnode_lookup(struct bridge_softc *sc, const uint8_t *addr, uint16_t vlan) { struct bridge_rtnode *brt; uint32_t hash; int dir; BRIDGE_LOCK_ASSERT(sc); hash = bridge_rthash(sc, addr); LIST_FOREACH(brt, &sc->sc_rthash[hash], brt_hash) { dir = bridge_rtnode_addr_cmp(addr, brt->brt_addr); if (dir == 0 && (brt->brt_vlan == vlan || vlan == 0)) return (brt); if (dir > 0) return (NULL); } return (NULL); } /* * bridge_rtnode_insert: * * Insert the specified bridge node into the route table. We * assume the entry is not already in the table. */ static int bridge_rtnode_insert(struct bridge_softc *sc, struct bridge_rtnode *brt) { struct bridge_rtnode *lbrt; uint32_t hash; int dir; BRIDGE_LOCK_ASSERT(sc); hash = bridge_rthash(sc, brt->brt_addr); lbrt = LIST_FIRST(&sc->sc_rthash[hash]); if (lbrt == NULL) { LIST_INSERT_HEAD(&sc->sc_rthash[hash], brt, brt_hash); goto out; } do { dir = bridge_rtnode_addr_cmp(brt->brt_addr, lbrt->brt_addr); if (dir == 0 && brt->brt_vlan == lbrt->brt_vlan) return (EEXIST); if (dir > 0) { LIST_INSERT_BEFORE(lbrt, brt, brt_hash); goto out; } if (LIST_NEXT(lbrt, brt_hash) == NULL) { LIST_INSERT_AFTER(lbrt, brt, brt_hash); goto out; } lbrt = LIST_NEXT(lbrt, brt_hash); } while (lbrt != NULL); #ifdef DIAGNOSTIC panic("bridge_rtnode_insert: impossible"); #endif out: LIST_INSERT_HEAD(&sc->sc_rtlist, brt, brt_list); sc->sc_brtcnt++; return (0); } /* * bridge_rtnode_destroy: * * Destroy a bridge rtnode. */ static void bridge_rtnode_destroy(struct bridge_softc *sc, struct bridge_rtnode *brt) { BRIDGE_LOCK_ASSERT(sc); LIST_REMOVE(brt, brt_hash); LIST_REMOVE(brt, brt_list); sc->sc_brtcnt--; brt->brt_dst->bif_addrcnt--; uma_zfree(bridge_rtnode_zone, brt); } /* * bridge_rtable_expire: * * Set the expiry time for all routes on an interface. */ static void bridge_rtable_expire(struct ifnet *ifp, int age) { struct bridge_softc *sc = ifp->if_bridge; struct bridge_rtnode *brt; BRIDGE_LOCK(sc); /* * If the age is zero then flush, otherwise set all the expiry times to * age for the interface */ if (age == 0) bridge_rtdelete(sc, ifp, IFBF_FLUSHDYN); else { LIST_FOREACH(brt, &sc->sc_rtlist, brt_list) { /* Cap the expiry time to 'age' */ if (brt->brt_ifp == ifp && brt->brt_expire > time_uptime + age && (brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) brt->brt_expire = time_uptime + age; } } BRIDGE_UNLOCK(sc); } /* * bridge_state_change: * * Callback from the bridgestp code when a port changes states. */ static void bridge_state_change(struct ifnet *ifp, int state) { struct bridge_softc *sc = ifp->if_bridge; static const char *stpstates[] = { "disabled", "listening", "learning", "forwarding", "blocking", "discarding" }; if (V_log_stp) log(LOG_NOTICE, "%s: state changed to %s on %s\n", sc->sc_ifp->if_xname, stpstates[state], ifp->if_xname); } /* * Send bridge packets through pfil if they are one of the types pfil can deal * with, or if they are ARP or REVARP. (pfil will pass ARP and REVARP without * question.) If *bifp or *ifp are NULL then packet filtering is skipped for * that interface. */ static int bridge_pfil(struct mbuf **mp, struct ifnet *bifp, struct ifnet *ifp, int dir) { int snap, error, i, hlen; struct ether_header *eh1, eh2; struct ip *ip; struct llc llc1; u_int16_t ether_type; snap = 0; error = -1; /* Default error if not error == 0 */ #if 0 /* we may return with the IP fields swapped, ensure its not shared */ KASSERT(M_WRITABLE(*mp), ("%s: modifying a shared mbuf", __func__)); #endif if (V_pfil_bridge == 0 && V_pfil_member == 0 && V_pfil_ipfw == 0) return (0); /* filtering is disabled */ i = min((*mp)->m_pkthdr.len, max_protohdr); if ((*mp)->m_len < i) { *mp = m_pullup(*mp, i); if (*mp == NULL) { printf("%s: m_pullup failed\n", __func__); return (-1); } } eh1 = mtod(*mp, struct ether_header *); ether_type = ntohs(eh1->ether_type); /* * Check for SNAP/LLC. */ if (ether_type < ETHERMTU) { struct llc *llc2 = (struct llc *)(eh1 + 1); if ((*mp)->m_len >= ETHER_HDR_LEN + 8 && llc2->llc_dsap == LLC_SNAP_LSAP && llc2->llc_ssap == LLC_SNAP_LSAP && llc2->llc_control == LLC_UI) { ether_type = htons(llc2->llc_un.type_snap.ether_type); snap = 1; } } /* * If we're trying to filter bridge traffic, don't look at anything * other than IP and ARP traffic. If the filter doesn't understand * IPv6, don't allow IPv6 through the bridge either. This is lame * since if we really wanted, say, an AppleTalk filter, we are hosed, * but of course we don't have an AppleTalk filter to begin with. * (Note that since pfil doesn't understand ARP it will pass *ALL* * ARP traffic.) */ switch (ether_type) { case ETHERTYPE_ARP: case ETHERTYPE_REVARP: if (V_pfil_ipfw_arp == 0) return (0); /* Automatically pass */ break; case ETHERTYPE_IP: #ifdef INET6 case ETHERTYPE_IPV6: #endif /* INET6 */ break; default: /* * Check to see if the user wants to pass non-ip * packets, these will not be checked by pfil(9) and * passed unconditionally so the default is to drop. */ if (V_pfil_onlyip) goto bad; } /* Run the packet through pfil before stripping link headers */ if (PFIL_HOOKED(&V_link_pfil_hook) && V_pfil_ipfw != 0 && dir == PFIL_OUT && ifp != NULL) { error = pfil_run_hooks(&V_link_pfil_hook, mp, ifp, dir, NULL); if (*mp == NULL || error != 0) /* packet consumed by filter */ return (error); } /* Strip off the Ethernet header and keep a copy. */ m_copydata(*mp, 0, ETHER_HDR_LEN, (caddr_t) &eh2); m_adj(*mp, ETHER_HDR_LEN); /* Strip off snap header, if present */ if (snap) { m_copydata(*mp, 0, sizeof(struct llc), (caddr_t) &llc1); m_adj(*mp, sizeof(struct llc)); } /* * Check the IP header for alignment and errors */ if (dir == PFIL_IN) { switch (ether_type) { case ETHERTYPE_IP: error = bridge_ip_checkbasic(mp); break; #ifdef INET6 case ETHERTYPE_IPV6: error = bridge_ip6_checkbasic(mp); break; #endif /* INET6 */ default: error = 0; } if (error) goto bad; } error = 0; /* * Run the packet through pfil */ switch (ether_type) { case ETHERTYPE_IP: /* * Run pfil on the member interface and the bridge, both can * be skipped by clearing pfil_member or pfil_bridge. * * Keep the order: * in_if -> bridge_if -> out_if */ if (V_pfil_bridge && dir == PFIL_OUT && bifp != NULL) error = pfil_run_hooks(&V_inet_pfil_hook, mp, bifp, dir, NULL); if (*mp == NULL || error != 0) /* filter may consume */ break; if (V_pfil_member && ifp != NULL) error = pfil_run_hooks(&V_inet_pfil_hook, mp, ifp, dir, NULL); if (*mp == NULL || error != 0) /* filter may consume */ break; if (V_pfil_bridge && dir == PFIL_IN && bifp != NULL) error = pfil_run_hooks(&V_inet_pfil_hook, mp, bifp, dir, NULL); if (*mp == NULL || error != 0) /* filter may consume */ break; /* check if we need to fragment the packet */ if (V_pfil_member && ifp != NULL && dir == PFIL_OUT) { i = (*mp)->m_pkthdr.len; if (i > ifp->if_mtu) { error = bridge_fragment(ifp, *mp, &eh2, snap, &llc1); return (error); } } /* Recalculate the ip checksum. */ ip = mtod(*mp, struct ip *); hlen = ip->ip_hl << 2; if (hlen < sizeof(struct ip)) goto bad; if (hlen > (*mp)->m_len) { if ((*mp = m_pullup(*mp, hlen)) == 0) goto bad; ip = mtod(*mp, struct ip *); if (ip == NULL) goto bad; } ip->ip_sum = 0; if (hlen == sizeof(struct ip)) ip->ip_sum = in_cksum_hdr(ip); else ip->ip_sum = in_cksum(*mp, hlen); break; #ifdef INET6 case ETHERTYPE_IPV6: if (V_pfil_bridge && dir == PFIL_OUT && bifp != NULL) error = pfil_run_hooks(&V_inet6_pfil_hook, mp, bifp, dir, NULL); if (*mp == NULL || error != 0) /* filter may consume */ break; if (V_pfil_member && ifp != NULL) error = pfil_run_hooks(&V_inet6_pfil_hook, mp, ifp, dir, NULL); if (*mp == NULL || error != 0) /* filter may consume */ break; if (V_pfil_bridge && dir == PFIL_IN && bifp != NULL) error = pfil_run_hooks(&V_inet6_pfil_hook, mp, bifp, dir, NULL); break; #endif default: error = 0; break; } if (*mp == NULL) return (error); if (error != 0) goto bad; error = -1; /* * Finally, put everything back the way it was and return */ if (snap) { M_PREPEND(*mp, sizeof(struct llc), M_NOWAIT); if (*mp == NULL) return (error); bcopy(&llc1, mtod(*mp, caddr_t), sizeof(struct llc)); } M_PREPEND(*mp, ETHER_HDR_LEN, M_NOWAIT); if (*mp == NULL) return (error); bcopy(&eh2, mtod(*mp, caddr_t), ETHER_HDR_LEN); return (0); bad: m_freem(*mp); *mp = NULL; return (error); } /* * Perform basic checks on header size since * pfil assumes ip_input has already processed * it for it. Cut-and-pasted from ip_input.c. * Given how simple the IPv6 version is, * does the IPv4 version really need to be * this complicated? * * XXX Should we update ipstat here, or not? * XXX Right now we update ipstat but not * XXX csum_counter. */ static int bridge_ip_checkbasic(struct mbuf **mp) { struct mbuf *m = *mp; struct ip *ip; int len, hlen; u_short sum; if (*mp == NULL) return (-1); if (IP_HDR_ALIGNED_P(mtod(m, caddr_t)) == 0) { if ((m = m_copyup(m, sizeof(struct ip), (max_linkhdr + 3) & ~3)) == NULL) { /* XXXJRT new stat, please */ KMOD_IPSTAT_INC(ips_toosmall); goto bad; } } else if (__predict_false(m->m_len < sizeof (struct ip))) { if ((m = m_pullup(m, sizeof (struct ip))) == NULL) { KMOD_IPSTAT_INC(ips_toosmall); goto bad; } } ip = mtod(m, struct ip *); if (ip == NULL) goto bad; if (ip->ip_v != IPVERSION) { KMOD_IPSTAT_INC(ips_badvers); goto bad; } hlen = ip->ip_hl << 2; if (hlen < sizeof(struct ip)) { /* minimum header length */ KMOD_IPSTAT_INC(ips_badhlen); goto bad; } if (hlen > m->m_len) { if ((m = m_pullup(m, hlen)) == 0) { KMOD_IPSTAT_INC(ips_badhlen); goto bad; } ip = mtod(m, struct ip *); if (ip == NULL) goto bad; } if (m->m_pkthdr.csum_flags & CSUM_IP_CHECKED) { sum = !(m->m_pkthdr.csum_flags & CSUM_IP_VALID); } else { if (hlen == sizeof(struct ip)) { sum = in_cksum_hdr(ip); } else { sum = in_cksum(m, hlen); } } if (sum) { KMOD_IPSTAT_INC(ips_badsum); goto bad; } /* Retrieve the packet length. */ len = ntohs(ip->ip_len); /* * Check for additional length bogosity */ if (len < hlen) { KMOD_IPSTAT_INC(ips_badlen); goto bad; } /* * Check that the amount of data in the buffers * is as at least much as the IP header would have us expect. * Drop packet if shorter than we expect. */ if (m->m_pkthdr.len < len) { KMOD_IPSTAT_INC(ips_tooshort); goto bad; } /* Checks out, proceed */ *mp = m; return (0); bad: *mp = m; return (-1); } #ifdef INET6 /* * Same as above, but for IPv6. * Cut-and-pasted from ip6_input.c. * XXX Should we update ip6stat, or not? */ static int bridge_ip6_checkbasic(struct mbuf **mp) { struct mbuf *m = *mp; struct ip6_hdr *ip6; /* * If the IPv6 header is not aligned, slurp it up into a new * mbuf with space for link headers, in the event we forward * it. Otherwise, if it is aligned, make sure the entire base * IPv6 header is in the first mbuf of the chain. */ if (IP6_HDR_ALIGNED_P(mtod(m, caddr_t)) == 0) { struct ifnet *inifp = m->m_pkthdr.rcvif; if ((m = m_copyup(m, sizeof(struct ip6_hdr), (max_linkhdr + 3) & ~3)) == NULL) { /* XXXJRT new stat, please */ IP6STAT_INC(ip6s_toosmall); in6_ifstat_inc(inifp, ifs6_in_hdrerr); goto bad; } } else if (__predict_false(m->m_len < sizeof(struct ip6_hdr))) { struct ifnet *inifp = m->m_pkthdr.rcvif; if ((m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) { IP6STAT_INC(ip6s_toosmall); in6_ifstat_inc(inifp, ifs6_in_hdrerr); goto bad; } } ip6 = mtod(m, struct ip6_hdr *); if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { IP6STAT_INC(ip6s_badvers); in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_hdrerr); goto bad; } /* Checks out, proceed */ *mp = m; return (0); bad: *mp = m; return (-1); } #endif /* INET6 */ /* * bridge_fragment: * * Return a fragmented mbuf chain. */ static int bridge_fragment(struct ifnet *ifp, struct mbuf *m, struct ether_header *eh, int snap, struct llc *llc) { struct mbuf *m0; struct ip *ip; int error = -1; if (m->m_len < sizeof(struct ip) && (m = m_pullup(m, sizeof(struct ip))) == NULL) goto out; ip = mtod(m, struct ip *); m->m_pkthdr.csum_flags |= CSUM_IP; error = ip_fragment(ip, &m, ifp->if_mtu, ifp->if_hwassist); if (error) goto out; /* walk the chain and re-add the Ethernet header */ for (m0 = m; m0; m0 = m0->m_nextpkt) { if (error == 0) { if (snap) { M_PREPEND(m0, sizeof(struct llc), M_NOWAIT); if (m0 == NULL) { error = ENOBUFS; continue; } bcopy(llc, mtod(m0, caddr_t), sizeof(struct llc)); } M_PREPEND(m0, ETHER_HDR_LEN, M_NOWAIT); if (m0 == NULL) { error = ENOBUFS; continue; } bcopy(eh, mtod(m0, caddr_t), ETHER_HDR_LEN); } else m_freem(m); } if (error == 0) KMOD_IPSTAT_INC(ips_fragmented); return (error); out: if (m != NULL) m_freem(m); return (error); } static void bridge_linkstate(struct ifnet *ifp) { struct bridge_softc *sc = ifp->if_bridge; struct bridge_iflist *bif; BRIDGE_LOCK(sc); bif = bridge_lookup_member_if(sc, ifp); if (bif == NULL) { BRIDGE_UNLOCK(sc); return; } bridge_linkcheck(sc); BRIDGE_UNLOCK(sc); bstp_linkstate(&bif->bif_stp); } static void bridge_linkcheck(struct bridge_softc *sc) { struct bridge_iflist *bif; int new_link, hasls; BRIDGE_LOCK_ASSERT(sc); new_link = LINK_STATE_DOWN; hasls = 0; /* Our link is considered up if at least one of our ports is active */ LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { if (bif->bif_ifp->if_capabilities & IFCAP_LINKSTATE) hasls++; if (bif->bif_ifp->if_link_state == LINK_STATE_UP) { new_link = LINK_STATE_UP; break; } } if (!LIST_EMPTY(&sc->sc_iflist) && !hasls) { /* If no interfaces support link-state then we default to up */ new_link = LINK_STATE_UP; } if_link_state_change(sc->sc_ifp, new_link); } Index: projects/building-blocks/sys/netgraph/ng_base.c =================================================================== --- projects/building-blocks/sys/netgraph/ng_base.c (revision 278776) +++ projects/building-blocks/sys/netgraph/ng_base.c (revision 278777) @@ -1,3845 +1,3845 @@ /*- * Copyright (c) 1996-1999 Whistle Communications, Inc. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, use and * redistribution of this software, in source or object code forms, with or * without modifications are expressly permitted by Whistle Communications; * provided, however, that: * 1. Any and all reproductions of the source or object code must include the * copyright notice above and the following disclaimer of warranties; and * 2. No rights are granted, in any manner or form, to use Whistle * Communications, Inc. trademarks, including the mark "WHISTLE * COMMUNICATIONS" on advertising, endorsements, or otherwise except as * such appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER 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 WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. * * Authors: Julian Elischer * Archie Cobbs * * $FreeBSD$ * $Whistle: ng_base.c,v 1.39 1999/01/28 23:54:53 julian Exp $ */ /* * This file implements the base netgraph code. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MODULE_VERSION(netgraph, NG_ABI_VERSION); /* Mutex to protect topology events. */ static struct rwlock ng_topo_lock; #define TOPOLOGY_RLOCK() rw_rlock(&ng_topo_lock) #define TOPOLOGY_RUNLOCK() rw_runlock(&ng_topo_lock) #define TOPOLOGY_WLOCK() rw_wlock(&ng_topo_lock) #define TOPOLOGY_WUNLOCK() rw_wunlock(&ng_topo_lock) #define TOPOLOGY_NOTOWNED() rw_assert(&ng_topo_lock, RA_UNLOCKED) #ifdef NETGRAPH_DEBUG static struct mtx ng_nodelist_mtx; /* protects global node/hook lists */ static struct mtx ngq_mtx; /* protects the queue item list */ static SLIST_HEAD(, ng_node) ng_allnodes; static LIST_HEAD(, ng_node) ng_freenodes; /* in debug, we never free() them */ static SLIST_HEAD(, ng_hook) ng_allhooks; static LIST_HEAD(, ng_hook) ng_freehooks; /* in debug, we never free() them */ static void ng_dumpitems(void); static void ng_dumpnodes(void); static void ng_dumphooks(void); #endif /* NETGRAPH_DEBUG */ /* * DEAD versions of the structures. * In order to avoid races, it is sometimes necessary to point * at SOMETHING even though theoretically, the current entity is * INVALID. Use these to avoid these races. */ struct ng_type ng_deadtype = { NG_ABI_VERSION, "dead", NULL, /* modevent */ NULL, /* constructor */ NULL, /* rcvmsg */ NULL, /* shutdown */ NULL, /* newhook */ NULL, /* findhook */ NULL, /* connect */ NULL, /* rcvdata */ NULL, /* disconnect */ NULL, /* cmdlist */ }; struct ng_node ng_deadnode = { "dead", &ng_deadtype, NGF_INVALID, 0, /* numhooks */ NULL, /* private */ 0, /* ID */ LIST_HEAD_INITIALIZER(ng_deadnode.nd_hooks), {}, /* all_nodes list entry */ {}, /* id hashtable list entry */ { 0, 0, {}, /* should never use! (should hang) */ {}, /* workqueue entry */ STAILQ_HEAD_INITIALIZER(ng_deadnode.nd_input_queue.queue), }, 1, /* refs */ NULL, /* vnet */ #ifdef NETGRAPH_DEBUG ND_MAGIC, __FILE__, __LINE__, {NULL} #endif /* NETGRAPH_DEBUG */ }; struct ng_hook ng_deadhook = { "dead", NULL, /* private */ HK_INVALID | HK_DEAD, 0, /* undefined data link type */ &ng_deadhook, /* Peer is self */ &ng_deadnode, /* attached to deadnode */ {}, /* hooks list */ NULL, /* override rcvmsg() */ NULL, /* override rcvdata() */ 1, /* refs always >= 1 */ #ifdef NETGRAPH_DEBUG HK_MAGIC, __FILE__, __LINE__, {NULL} #endif /* NETGRAPH_DEBUG */ }; /* * END DEAD STRUCTURES */ /* List nodes with unallocated work */ static STAILQ_HEAD(, ng_node) ng_worklist = STAILQ_HEAD_INITIALIZER(ng_worklist); static struct mtx ng_worklist_mtx; /* MUST LOCK NODE FIRST */ /* List of installed types */ static LIST_HEAD(, ng_type) ng_typelist; static struct rwlock ng_typelist_lock; #define TYPELIST_RLOCK() rw_rlock(&ng_typelist_lock) #define TYPELIST_RUNLOCK() rw_runlock(&ng_typelist_lock) #define TYPELIST_WLOCK() rw_wlock(&ng_typelist_lock) #define TYPELIST_WUNLOCK() rw_wunlock(&ng_typelist_lock) /* Hash related definitions. */ LIST_HEAD(nodehash, ng_node); static VNET_DEFINE(struct nodehash *, ng_ID_hash); static VNET_DEFINE(u_long, ng_ID_hmask); static VNET_DEFINE(u_long, ng_nodes); static VNET_DEFINE(struct nodehash *, ng_name_hash); static VNET_DEFINE(u_long, ng_name_hmask); static VNET_DEFINE(u_long, ng_named_nodes); #define V_ng_ID_hash VNET(ng_ID_hash) #define V_ng_ID_hmask VNET(ng_ID_hmask) #define V_ng_nodes VNET(ng_nodes) #define V_ng_name_hash VNET(ng_name_hash) #define V_ng_name_hmask VNET(ng_name_hmask) #define V_ng_named_nodes VNET(ng_named_nodes) static struct rwlock ng_idhash_lock; #define IDHASH_RLOCK() rw_rlock(&ng_idhash_lock) #define IDHASH_RUNLOCK() rw_runlock(&ng_idhash_lock) #define IDHASH_WLOCK() rw_wlock(&ng_idhash_lock) #define IDHASH_WUNLOCK() rw_wunlock(&ng_idhash_lock) /* Method to find a node.. used twice so do it here */ #define NG_IDHASH_FN(ID) ((ID) % (V_ng_ID_hmask + 1)) #define NG_IDHASH_FIND(ID, node) \ do { \ rw_assert(&ng_idhash_lock, RA_LOCKED); \ LIST_FOREACH(node, &V_ng_ID_hash[NG_IDHASH_FN(ID)], \ nd_idnodes) { \ if (NG_NODE_IS_VALID(node) \ && (NG_NODE_ID(node) == ID)) { \ break; \ } \ } \ } while (0) static struct rwlock ng_namehash_lock; #define NAMEHASH_RLOCK() rw_rlock(&ng_namehash_lock) #define NAMEHASH_RUNLOCK() rw_runlock(&ng_namehash_lock) #define NAMEHASH_WLOCK() rw_wlock(&ng_namehash_lock) #define NAMEHASH_WUNLOCK() rw_wunlock(&ng_namehash_lock) /* Internal functions */ static int ng_add_hook(node_p node, const char *name, hook_p * hookp); static int ng_generic_msg(node_p here, item_p item, hook_p lasthook); static ng_ID_t ng_decodeidname(const char *name); static int ngb_mod_event(module_t mod, int event, void *data); static void ng_worklist_add(node_p node); static void ngthread(void *); static int ng_apply_item(node_p node, item_p item, int rw); static void ng_flush_input_queue(node_p node); static node_p ng_ID2noderef(ng_ID_t ID); static int ng_con_nodes(item_p item, node_p node, const char *name, node_p node2, const char *name2); static int ng_con_part2(node_p node, item_p item, hook_p hook); static int ng_con_part3(node_p node, item_p item, hook_p hook); static int ng_mkpeer(node_p node, const char *name, const char *name2, char *type); static void ng_name_rehash(void); static void ng_ID_rehash(void); /* Imported, these used to be externally visible, some may go back. */ void ng_destroy_hook(hook_p hook); int ng_path2noderef(node_p here, const char *path, node_p *dest, hook_p *lasthook); int ng_make_node(const char *type, node_p *nodepp); int ng_path_parse(char *addr, char **node, char **path, char **hook); void ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3); void ng_unname(node_p node); /* Our own netgraph malloc type */ MALLOC_DEFINE(M_NETGRAPH, "netgraph", "netgraph structures and ctrl messages"); MALLOC_DEFINE(M_NETGRAPH_MSG, "netgraph_msg", "netgraph name storage"); static MALLOC_DEFINE(M_NETGRAPH_HOOK, "netgraph_hook", "netgraph hook structures"); static MALLOC_DEFINE(M_NETGRAPH_NODE, "netgraph_node", "netgraph node structures"); static MALLOC_DEFINE(M_NETGRAPH_ITEM, "netgraph_item", "netgraph item structures"); /* Should not be visible outside this file */ #define _NG_ALLOC_HOOK(hook) \ hook = malloc(sizeof(*hook), M_NETGRAPH_HOOK, M_NOWAIT | M_ZERO) #define _NG_ALLOC_NODE(node) \ node = malloc(sizeof(*node), M_NETGRAPH_NODE, M_NOWAIT | M_ZERO) #define NG_QUEUE_LOCK_INIT(n) \ mtx_init(&(n)->q_mtx, "ng_node", NULL, MTX_DEF) #define NG_QUEUE_LOCK(n) \ mtx_lock(&(n)->q_mtx) #define NG_QUEUE_UNLOCK(n) \ mtx_unlock(&(n)->q_mtx) #define NG_WORKLIST_LOCK_INIT() \ mtx_init(&ng_worklist_mtx, "ng_worklist", NULL, MTX_DEF) #define NG_WORKLIST_LOCK() \ mtx_lock(&ng_worklist_mtx) #define NG_WORKLIST_UNLOCK() \ mtx_unlock(&ng_worklist_mtx) #define NG_WORKLIST_SLEEP() \ mtx_sleep(&ng_worklist, &ng_worklist_mtx, PI_NET, "sleep", 0) #define NG_WORKLIST_WAKEUP() \ wakeup_one(&ng_worklist) #ifdef NETGRAPH_DEBUG /*----------------------------------------------*/ /* * In debug mode: * In an attempt to help track reference count screwups * we do not free objects back to the malloc system, but keep them * in a local cache where we can examine them and keep information safely * after they have been freed. * We use this scheme for nodes and hooks, and to some extent for items. */ static __inline hook_p ng_alloc_hook(void) { hook_p hook; SLIST_ENTRY(ng_hook) temp; mtx_lock(&ng_nodelist_mtx); hook = LIST_FIRST(&ng_freehooks); if (hook) { LIST_REMOVE(hook, hk_hooks); bcopy(&hook->hk_all, &temp, sizeof(temp)); bzero(hook, sizeof(struct ng_hook)); bcopy(&temp, &hook->hk_all, sizeof(temp)); mtx_unlock(&ng_nodelist_mtx); hook->hk_magic = HK_MAGIC; } else { mtx_unlock(&ng_nodelist_mtx); _NG_ALLOC_HOOK(hook); if (hook) { hook->hk_magic = HK_MAGIC; mtx_lock(&ng_nodelist_mtx); SLIST_INSERT_HEAD(&ng_allhooks, hook, hk_all); mtx_unlock(&ng_nodelist_mtx); } } return (hook); } static __inline node_p ng_alloc_node(void) { node_p node; SLIST_ENTRY(ng_node) temp; mtx_lock(&ng_nodelist_mtx); node = LIST_FIRST(&ng_freenodes); if (node) { LIST_REMOVE(node, nd_nodes); bcopy(&node->nd_all, &temp, sizeof(temp)); bzero(node, sizeof(struct ng_node)); bcopy(&temp, &node->nd_all, sizeof(temp)); mtx_unlock(&ng_nodelist_mtx); node->nd_magic = ND_MAGIC; } else { mtx_unlock(&ng_nodelist_mtx); _NG_ALLOC_NODE(node); if (node) { node->nd_magic = ND_MAGIC; mtx_lock(&ng_nodelist_mtx); SLIST_INSERT_HEAD(&ng_allnodes, node, nd_all); mtx_unlock(&ng_nodelist_mtx); } } return (node); } #define NG_ALLOC_HOOK(hook) do { (hook) = ng_alloc_hook(); } while (0) #define NG_ALLOC_NODE(node) do { (node) = ng_alloc_node(); } while (0) #define NG_FREE_HOOK(hook) \ do { \ mtx_lock(&ng_nodelist_mtx); \ LIST_INSERT_HEAD(&ng_freehooks, hook, hk_hooks); \ hook->hk_magic = 0; \ mtx_unlock(&ng_nodelist_mtx); \ } while (0) #define NG_FREE_NODE(node) \ do { \ mtx_lock(&ng_nodelist_mtx); \ LIST_INSERT_HEAD(&ng_freenodes, node, nd_nodes); \ node->nd_magic = 0; \ mtx_unlock(&ng_nodelist_mtx); \ } while (0) #else /* NETGRAPH_DEBUG */ /*----------------------------------------------*/ #define NG_ALLOC_HOOK(hook) _NG_ALLOC_HOOK(hook) #define NG_ALLOC_NODE(node) _NG_ALLOC_NODE(node) #define NG_FREE_HOOK(hook) do { free((hook), M_NETGRAPH_HOOK); } while (0) #define NG_FREE_NODE(node) do { free((node), M_NETGRAPH_NODE); } while (0) #endif /* NETGRAPH_DEBUG */ /*----------------------------------------------*/ /* Set this to kdb_enter("X") to catch all errors as they occur */ #ifndef TRAP_ERROR #define TRAP_ERROR() #endif static VNET_DEFINE(ng_ID_t, nextID) = 1; #define V_nextID VNET(nextID) #ifdef INVARIANTS #define CHECK_DATA_MBUF(m) do { \ struct mbuf *n; \ int total; \ \ M_ASSERTPKTHDR(m); \ for (total = 0, n = (m); n != NULL; n = n->m_next) { \ total += n->m_len; \ if (n->m_nextpkt != NULL) \ panic("%s: m_nextpkt", __func__); \ } \ \ if ((m)->m_pkthdr.len != total) { \ panic("%s: %d != %d", \ __func__, (m)->m_pkthdr.len, total); \ } \ } while (0) #else #define CHECK_DATA_MBUF(m) #endif #define ERROUT(x) do { error = (x); goto done; } while (0) /************************************************************************ Parse type definitions for generic messages ************************************************************************/ /* Handy structure parse type defining macro */ #define DEFINE_PARSE_STRUCT_TYPE(lo, up, args) \ static const struct ng_parse_struct_field \ ng_ ## lo ## _type_fields[] = NG_GENERIC_ ## up ## _INFO args; \ static const struct ng_parse_type ng_generic_ ## lo ## _type = { \ &ng_parse_struct_type, \ &ng_ ## lo ## _type_fields \ } DEFINE_PARSE_STRUCT_TYPE(mkpeer, MKPEER, ()); DEFINE_PARSE_STRUCT_TYPE(connect, CONNECT, ()); DEFINE_PARSE_STRUCT_TYPE(name, NAME, ()); DEFINE_PARSE_STRUCT_TYPE(rmhook, RMHOOK, ()); DEFINE_PARSE_STRUCT_TYPE(nodeinfo, NODEINFO, ()); DEFINE_PARSE_STRUCT_TYPE(typeinfo, TYPEINFO, ()); DEFINE_PARSE_STRUCT_TYPE(linkinfo, LINKINFO, (&ng_generic_nodeinfo_type)); /* Get length of an array when the length is stored as a 32 bit value immediately preceding the array -- as with struct namelist and struct typelist. */ static int ng_generic_list_getLength(const struct ng_parse_type *type, const u_char *start, const u_char *buf) { return *((const u_int32_t *)(buf - 4)); } /* Get length of the array of struct linkinfo inside a struct hooklist */ static int ng_generic_linkinfo_getLength(const struct ng_parse_type *type, const u_char *start, const u_char *buf) { const struct hooklist *hl = (const struct hooklist *)start; return hl->nodeinfo.hooks; } /* Array type for a variable length array of struct namelist */ static const struct ng_parse_array_info ng_nodeinfoarray_type_info = { &ng_generic_nodeinfo_type, &ng_generic_list_getLength }; static const struct ng_parse_type ng_generic_nodeinfoarray_type = { &ng_parse_array_type, &ng_nodeinfoarray_type_info }; /* Array type for a variable length array of struct typelist */ static const struct ng_parse_array_info ng_typeinfoarray_type_info = { &ng_generic_typeinfo_type, &ng_generic_list_getLength }; static const struct ng_parse_type ng_generic_typeinfoarray_type = { &ng_parse_array_type, &ng_typeinfoarray_type_info }; /* Array type for array of struct linkinfo in struct hooklist */ static const struct ng_parse_array_info ng_generic_linkinfo_array_type_info = { &ng_generic_linkinfo_type, &ng_generic_linkinfo_getLength }; static const struct ng_parse_type ng_generic_linkinfo_array_type = { &ng_parse_array_type, &ng_generic_linkinfo_array_type_info }; DEFINE_PARSE_STRUCT_TYPE(typelist, TYPELIST, (&ng_generic_typeinfoarray_type)); DEFINE_PARSE_STRUCT_TYPE(hooklist, HOOKLIST, (&ng_generic_nodeinfo_type, &ng_generic_linkinfo_array_type)); DEFINE_PARSE_STRUCT_TYPE(listnodes, LISTNODES, (&ng_generic_nodeinfoarray_type)); /* List of commands and how to convert arguments to/from ASCII */ static const struct ng_cmdlist ng_generic_cmds[] = { { NGM_GENERIC_COOKIE, NGM_SHUTDOWN, "shutdown", NULL, NULL }, { NGM_GENERIC_COOKIE, NGM_MKPEER, "mkpeer", &ng_generic_mkpeer_type, NULL }, { NGM_GENERIC_COOKIE, NGM_CONNECT, "connect", &ng_generic_connect_type, NULL }, { NGM_GENERIC_COOKIE, NGM_NAME, "name", &ng_generic_name_type, NULL }, { NGM_GENERIC_COOKIE, NGM_RMHOOK, "rmhook", &ng_generic_rmhook_type, NULL }, { NGM_GENERIC_COOKIE, NGM_NODEINFO, "nodeinfo", NULL, &ng_generic_nodeinfo_type }, { NGM_GENERIC_COOKIE, NGM_LISTHOOKS, "listhooks", NULL, &ng_generic_hooklist_type }, { NGM_GENERIC_COOKIE, NGM_LISTNAMES, "listnames", NULL, &ng_generic_listnodes_type /* same as NGM_LISTNODES */ }, { NGM_GENERIC_COOKIE, NGM_LISTNODES, "listnodes", NULL, &ng_generic_listnodes_type }, { NGM_GENERIC_COOKIE, NGM_LISTTYPES, "listtypes", NULL, &ng_generic_typelist_type }, { NGM_GENERIC_COOKIE, NGM_TEXT_CONFIG, "textconfig", NULL, &ng_parse_string_type }, { NGM_GENERIC_COOKIE, NGM_TEXT_STATUS, "textstatus", NULL, &ng_parse_string_type }, { NGM_GENERIC_COOKIE, NGM_ASCII2BINARY, "ascii2binary", &ng_parse_ng_mesg_type, &ng_parse_ng_mesg_type }, { NGM_GENERIC_COOKIE, NGM_BINARY2ASCII, "binary2ascii", &ng_parse_ng_mesg_type, &ng_parse_ng_mesg_type }, { 0 } }; /************************************************************************ Node routines ************************************************************************/ /* * Instantiate a node of the requested type */ int ng_make_node(const char *typename, node_p *nodepp) { struct ng_type *type; int error; /* Check that the type makes sense */ if (typename == NULL) { TRAP_ERROR(); return (EINVAL); } /* Locate the node type. If we fail we return. Do not try to load * module. */ if ((type = ng_findtype(typename)) == NULL) return (ENXIO); /* * If we have a constructor, then make the node and * call the constructor to do type specific initialisation. */ if (type->constructor != NULL) { if ((error = ng_make_node_common(type, nodepp)) == 0) { if ((error = ((*type->constructor)(*nodepp))) != 0) { NG_NODE_UNREF(*nodepp); } } } else { /* * Node has no constructor. We cannot ask for one * to be made. It must be brought into existence by * some external agency. The external agency should * call ng_make_node_common() directly to get the * netgraph part initialised. */ TRAP_ERROR(); error = EINVAL; } return (error); } /* * Generic node creation. Called by node initialisation for externally * instantiated nodes (e.g. hardware, sockets, etc ). * The returned node has a reference count of 1. */ int ng_make_node_common(struct ng_type *type, node_p *nodepp) { node_p node; /* Require the node type to have been already installed */ if (ng_findtype(type->name) == NULL) { TRAP_ERROR(); return (EINVAL); } /* Make a node and try attach it to the type */ NG_ALLOC_NODE(node); if (node == NULL) { TRAP_ERROR(); return (ENOMEM); } node->nd_type = type; #ifdef VIMAGE node->nd_vnet = curvnet; #endif NG_NODE_REF(node); /* note reference */ type->refs++; NG_QUEUE_LOCK_INIT(&node->nd_input_queue); STAILQ_INIT(&node->nd_input_queue.queue); node->nd_input_queue.q_flags = 0; /* Initialize hook list for new node */ LIST_INIT(&node->nd_hooks); /* Get an ID and put us in the hash chain. */ IDHASH_WLOCK(); for (;;) { /* wrap protection, even if silly */ node_p node2 = NULL; node->nd_ID = V_nextID++; /* 137/sec for 1 year before wrap */ /* Is there a problem with the new number? */ NG_IDHASH_FIND(node->nd_ID, node2); /* already taken? */ if ((node->nd_ID != 0) && (node2 == NULL)) { break; } } V_ng_nodes++; if (V_ng_nodes * 2 > V_ng_ID_hmask) ng_ID_rehash(); LIST_INSERT_HEAD(&V_ng_ID_hash[NG_IDHASH_FN(node->nd_ID)], node, nd_idnodes); IDHASH_WUNLOCK(); /* Done */ *nodepp = node; return (0); } /* * Forceably start the shutdown process on a node. Either call * its shutdown method, or do the default shutdown if there is * no type-specific method. * * We can only be called from a shutdown message, so we know we have * a writer lock, and therefore exclusive access. It also means * that we should not be on the work queue, but we check anyhow. * * Persistent node types must have a type-specific method which * allocates a new node in which case, this one is irretrievably going away, * or cleans up anything it needs, and just makes the node valid again, * in which case we allow the node to survive. * * XXX We need to think of how to tell a persistent node that we * REALLY need to go away because the hardware has gone or we * are rebooting.... etc. */ void ng_rmnode(node_p node, hook_p dummy1, void *dummy2, int dummy3) { hook_p hook; /* Check if it's already shutting down */ if ((node->nd_flags & NGF_CLOSING) != 0) return; if (node == &ng_deadnode) { printf ("shutdown called on deadnode\n"); return; } /* Add an extra reference so it doesn't go away during this */ NG_NODE_REF(node); /* * Mark it invalid so any newcomers know not to try use it * Also add our own mark so we can't recurse * note that NGF_INVALID does not do this as it's also set during * creation */ node->nd_flags |= NGF_INVALID|NGF_CLOSING; /* If node has its pre-shutdown method, then call it first*/ if (node->nd_type && node->nd_type->close) (*node->nd_type->close)(node); /* Notify all remaining connected nodes to disconnect */ while ((hook = LIST_FIRST(&node->nd_hooks)) != NULL) ng_destroy_hook(hook); /* * Drain the input queue forceably. * it has no hooks so what's it going to do, bleed on someone? * Theoretically we came here from a queue entry that was added * Just before the queue was closed, so it should be empty anyway. * Also removes us from worklist if needed. */ ng_flush_input_queue(node); /* Ask the type if it has anything to do in this case */ if (node->nd_type && node->nd_type->shutdown) { (*node->nd_type->shutdown)(node); if (NG_NODE_IS_VALID(node)) { /* * Well, blow me down if the node code hasn't declared * that it doesn't want to die. * Presumably it is a persistant node. * If we REALLY want it to go away, * e.g. hardware going away, * Our caller should set NGF_REALLY_DIE in nd_flags. */ node->nd_flags &= ~(NGF_INVALID|NGF_CLOSING); NG_NODE_UNREF(node); /* Assume they still have theirs */ return; } } else { /* do the default thing */ NG_NODE_UNREF(node); } ng_unname(node); /* basically a NOP these days */ /* * Remove extra reference, possibly the last * Possible other holders of references may include * timeout callouts, but theoretically the node's supposed to * have cancelled them. Possibly hardware dependencies may * force a driver to 'linger' with a reference. */ NG_NODE_UNREF(node); } /* * Remove a reference to the node, possibly the last. * deadnode always acts as it it were the last. */ void ng_unref_node(node_p node) { if (node == &ng_deadnode) return; CURVNET_SET(node->nd_vnet); if (refcount_release(&node->nd_refs)) { /* we were the last */ node->nd_type->refs--; /* XXX maybe should get types lock? */ NAMEHASH_WLOCK(); if (NG_NODE_HAS_NAME(node)) { V_ng_named_nodes--; LIST_REMOVE(node, nd_nodes); } NAMEHASH_WUNLOCK(); IDHASH_WLOCK(); V_ng_nodes--; LIST_REMOVE(node, nd_idnodes); IDHASH_WUNLOCK(); mtx_destroy(&node->nd_input_queue.q_mtx); NG_FREE_NODE(node); } CURVNET_RESTORE(); } /************************************************************************ Node ID handling ************************************************************************/ static node_p ng_ID2noderef(ng_ID_t ID) { node_p node; IDHASH_RLOCK(); NG_IDHASH_FIND(ID, node); if (node) NG_NODE_REF(node); IDHASH_RUNLOCK(); return(node); } ng_ID_t ng_node2ID(node_p node) { return (node ? NG_NODE_ID(node) : 0); } /************************************************************************ Node name handling ************************************************************************/ /* * Assign a node a name. */ int ng_name_node(node_p node, const char *name) { uint32_t hash; node_p node2; int i; /* Check the name is valid */ for (i = 0; i < NG_NODESIZ; i++) { if (name[i] == '\0' || name[i] == '.' || name[i] == ':') break; } if (i == 0 || name[i] != '\0') { TRAP_ERROR(); return (EINVAL); } if (ng_decodeidname(name) != 0) { /* valid IDs not allowed here */ TRAP_ERROR(); return (EINVAL); } NAMEHASH_WLOCK(); if (V_ng_named_nodes * 2 > V_ng_name_hmask) ng_name_rehash(); hash = hash32_str(name, HASHINIT) & V_ng_name_hmask; /* Check the name isn't already being used. */ LIST_FOREACH(node2, &V_ng_name_hash[hash], nd_nodes) if (NG_NODE_IS_VALID(node2) && (strcmp(NG_NODE_NAME(node2), name) == 0)) { NAMEHASH_WUNLOCK(); return (EADDRINUSE); } if (NG_NODE_HAS_NAME(node)) LIST_REMOVE(node, nd_nodes); else V_ng_named_nodes++; /* Copy it. */ strlcpy(NG_NODE_NAME(node), name, NG_NODESIZ); /* Update name hash. */ LIST_INSERT_HEAD(&V_ng_name_hash[hash], node, nd_nodes); NAMEHASH_WUNLOCK(); return (0); } /* * Find a node by absolute name. The name should NOT end with ':' * The name "." means "this node" and "[xxx]" means "the node * with ID (ie, at address) xxx". * * Returns the node if found, else NULL. * Eventually should add something faster than a sequential search. * Note it acquires a reference on the node so you can be sure it's still * there. */ node_p ng_name2noderef(node_p here, const char *name) { node_p node; ng_ID_t temp; int hash; /* "." means "this node" */ if (strcmp(name, ".") == 0) { NG_NODE_REF(here); return(here); } /* Check for name-by-ID */ if ((temp = ng_decodeidname(name)) != 0) { return (ng_ID2noderef(temp)); } /* Find node by name. */ hash = hash32_str(name, HASHINIT) & V_ng_name_hmask; NAMEHASH_RLOCK(); LIST_FOREACH(node, &V_ng_name_hash[hash], nd_nodes) if (NG_NODE_IS_VALID(node) && (strcmp(NG_NODE_NAME(node), name) == 0)) { NG_NODE_REF(node); break; } NAMEHASH_RUNLOCK(); return (node); } /* * Decode an ID name, eg. "[f03034de]". Returns 0 if the * string is not valid, otherwise returns the value. */ static ng_ID_t ng_decodeidname(const char *name) { const int len = strlen(name); char *eptr; u_long val; /* Check for proper length, brackets, no leading junk */ if ((len < 3) || (name[0] != '[') || (name[len - 1] != ']') || (!isxdigit(name[1]))) return ((ng_ID_t)0); /* Decode number */ val = strtoul(name + 1, &eptr, 16); if ((eptr - name != len - 1) || (val == ULONG_MAX) || (val == 0)) return ((ng_ID_t)0); return ((ng_ID_t)val); } /* * Remove a name from a node. This should only be called * when shutting down and removing the node. */ void ng_unname(node_p node) { } /* * Allocate a bigger name hash. */ static void ng_name_rehash() { struct nodehash *new; uint32_t hash; u_long hmask; node_p node, node2; int i; new = hashinit_flags((V_ng_name_hmask + 1) * 2, M_NETGRAPH_NODE, &hmask, HASH_NOWAIT); if (new == NULL) return; for (i = 0; i <= V_ng_name_hmask; i++) LIST_FOREACH_SAFE(node, &V_ng_name_hash[i], nd_nodes, node2) { #ifdef INVARIANTS LIST_REMOVE(node, nd_nodes); #endif hash = hash32_str(NG_NODE_NAME(node), HASHINIT) & hmask; LIST_INSERT_HEAD(&new[hash], node, nd_nodes); } hashdestroy(V_ng_name_hash, M_NETGRAPH_NODE, V_ng_name_hmask); V_ng_name_hash = new; V_ng_name_hmask = hmask; } /* * Allocate a bigger ID hash. */ static void ng_ID_rehash() { struct nodehash *new; uint32_t hash; u_long hmask; node_p node, node2; int i; new = hashinit_flags((V_ng_ID_hmask + 1) * 2, M_NETGRAPH_NODE, &hmask, HASH_NOWAIT); if (new == NULL) return; for (i = 0; i <= V_ng_ID_hmask; i++) LIST_FOREACH_SAFE(node, &V_ng_ID_hash[i], nd_idnodes, node2) { #ifdef INVARIANTS LIST_REMOVE(node, nd_idnodes); #endif hash = (node->nd_ID % (hmask + 1)); LIST_INSERT_HEAD(&new[hash], node, nd_idnodes); } hashdestroy(V_ng_ID_hash, M_NETGRAPH_NODE, V_ng_name_hmask); V_ng_ID_hash = new; V_ng_ID_hmask = hmask; } /************************************************************************ Hook routines Names are not optional. Hooks are always connected, except for a brief moment within these routines. On invalidation or during creation they are connected to the 'dead' hook. ************************************************************************/ /* * Remove a hook reference */ void ng_unref_hook(hook_p hook) { if (hook == &ng_deadhook) return; if (refcount_release(&hook->hk_refs)) { /* we were the last */ if (_NG_HOOK_NODE(hook)) /* it'll probably be ng_deadnode */ _NG_NODE_UNREF((_NG_HOOK_NODE(hook))); NG_FREE_HOOK(hook); } } /* * Add an unconnected hook to a node. Only used internally. * Assumes node is locked. (XXX not yet true ) */ static int ng_add_hook(node_p node, const char *name, hook_p *hookp) { hook_p hook; int error = 0; /* Check that the given name is good */ if (name == NULL) { TRAP_ERROR(); return (EINVAL); } if (ng_findhook(node, name) != NULL) { TRAP_ERROR(); return (EEXIST); } /* Allocate the hook and link it up */ NG_ALLOC_HOOK(hook); if (hook == NULL) { TRAP_ERROR(); return (ENOMEM); } hook->hk_refs = 1; /* add a reference for us to return */ hook->hk_flags = HK_INVALID; hook->hk_peer = &ng_deadhook; /* start off this way */ hook->hk_node = node; NG_NODE_REF(node); /* each hook counts as a reference */ /* Set hook name */ strlcpy(NG_HOOK_NAME(hook), name, NG_HOOKSIZ); /* * Check if the node type code has something to say about it * If it fails, the unref of the hook will also unref the node. */ if (node->nd_type->newhook != NULL) { if ((error = (*node->nd_type->newhook)(node, hook, name))) { NG_HOOK_UNREF(hook); /* this frees the hook */ return (error); } } /* * The 'type' agrees so far, so go ahead and link it in. * We'll ask again later when we actually connect the hooks. */ LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks); node->nd_numhooks++; NG_HOOK_REF(hook); /* one for the node */ if (hookp) *hookp = hook; return (0); } /* * Find a hook * * Node types may supply their own optimized routines for finding * hooks. If none is supplied, we just do a linear search. * XXX Possibly we should add a reference to the hook? */ hook_p ng_findhook(node_p node, const char *name) { hook_p hook; if (node->nd_type->findhook != NULL) return (*node->nd_type->findhook)(node, name); LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) { if (NG_HOOK_IS_VALID(hook) && (strcmp(NG_HOOK_NAME(hook), name) == 0)) return (hook); } return (NULL); } /* * Destroy a hook * * As hooks are always attached, this really destroys two hooks. * The one given, and the one attached to it. Disconnect the hooks * from each other first. We reconnect the peer hook to the 'dead' * hook so that it can still exist after we depart. We then * send the peer its own destroy message. This ensures that we only * interact with the peer's structures when it is locked processing that * message. We hold a reference to the peer hook so we are guaranteed that * the peer hook and node are still going to exist until * we are finished there as the hook holds a ref on the node. * We run this same code again on the peer hook, but that time it is already * attached to the 'dead' hook. * * This routine is called at all stages of hook creation * on error detection and must be able to handle any such stage. */ void ng_destroy_hook(hook_p hook) { hook_p peer; node_p node; if (hook == &ng_deadhook) { /* better safe than sorry */ printf("ng_destroy_hook called on deadhook\n"); return; } /* * Protect divorce process with mutex, to avoid races on * simultaneous disconnect. */ TOPOLOGY_WLOCK(); hook->hk_flags |= HK_INVALID; peer = NG_HOOK_PEER(hook); node = NG_HOOK_NODE(hook); if (peer && (peer != &ng_deadhook)) { /* * Set the peer to point to ng_deadhook * from this moment on we are effectively independent it. * send it an rmhook message of it's own. */ peer->hk_peer = &ng_deadhook; /* They no longer know us */ hook->hk_peer = &ng_deadhook; /* Nor us, them */ if (NG_HOOK_NODE(peer) == &ng_deadnode) { /* * If it's already divorced from a node, * just free it. */ TOPOLOGY_WUNLOCK(); } else { TOPOLOGY_WUNLOCK(); ng_rmhook_self(peer); /* Send it a surprise */ } NG_HOOK_UNREF(peer); /* account for peer link */ NG_HOOK_UNREF(hook); /* account for peer link */ } else TOPOLOGY_WUNLOCK(); TOPOLOGY_NOTOWNED(); /* * Remove the hook from the node's list to avoid possible recursion * in case the disconnection results in node shutdown. */ if (node == &ng_deadnode) { /* happens if called from ng_con_nodes() */ return; } LIST_REMOVE(hook, hk_hooks); node->nd_numhooks--; if (node->nd_type->disconnect) { /* * The type handler may elect to destroy the node so don't * trust its existence after this point. (except * that we still hold a reference on it. (which we * inherrited from the hook we are destroying) */ (*node->nd_type->disconnect) (hook); } /* * Note that because we will point to ng_deadnode, the original node * is not decremented automatically so we do that manually. */ _NG_HOOK_NODE(hook) = &ng_deadnode; NG_NODE_UNREF(node); /* We no longer point to it so adjust count */ NG_HOOK_UNREF(hook); /* Account for linkage (in list) to node */ } /* * Take two hooks on a node and merge the connection so that the given node * is effectively bypassed. */ int ng_bypass(hook_p hook1, hook_p hook2) { if (hook1->hk_node != hook2->hk_node) { TRAP_ERROR(); return (EINVAL); } TOPOLOGY_WLOCK(); if (NG_HOOK_NOT_VALID(hook1) || NG_HOOK_NOT_VALID(hook2)) { TOPOLOGY_WUNLOCK(); return (EINVAL); } hook1->hk_peer->hk_peer = hook2->hk_peer; hook2->hk_peer->hk_peer = hook1->hk_peer; hook1->hk_peer = &ng_deadhook; hook2->hk_peer = &ng_deadhook; TOPOLOGY_WUNLOCK(); NG_HOOK_UNREF(hook1); NG_HOOK_UNREF(hook2); /* XXX If we ever cache methods on hooks update them as well */ ng_destroy_hook(hook1); ng_destroy_hook(hook2); return (0); } /* * Install a new netgraph type */ int ng_newtype(struct ng_type *tp) { const size_t namelen = strlen(tp->name); /* Check version and type name fields */ if ((tp->version != NG_ABI_VERSION) || (namelen == 0) || (namelen >= NG_TYPESIZ)) { TRAP_ERROR(); if (tp->version != NG_ABI_VERSION) { printf("Netgraph: Node type rejected. ABI mismatch. " "Suggest recompile\n"); } return (EINVAL); } /* Check for name collision */ if (ng_findtype(tp->name) != NULL) { TRAP_ERROR(); return (EEXIST); } /* Link in new type */ TYPELIST_WLOCK(); LIST_INSERT_HEAD(&ng_typelist, tp, types); tp->refs = 1; /* first ref is linked list */ TYPELIST_WUNLOCK(); return (0); } /* * unlink a netgraph type * If no examples exist */ int ng_rmtype(struct ng_type *tp) { /* Check for name collision */ if (tp->refs != 1) { TRAP_ERROR(); return (EBUSY); } /* Unlink type */ TYPELIST_WLOCK(); LIST_REMOVE(tp, types); TYPELIST_WUNLOCK(); return (0); } /* * Look for a type of the name given */ struct ng_type * ng_findtype(const char *typename) { struct ng_type *type; TYPELIST_RLOCK(); LIST_FOREACH(type, &ng_typelist, types) { if (strcmp(type->name, typename) == 0) break; } TYPELIST_RUNLOCK(); return (type); } /************************************************************************ Composite routines ************************************************************************/ /* * Connect two nodes using the specified hooks, using queued functions. */ static int ng_con_part3(node_p node, item_p item, hook_p hook) { int error = 0; /* * When we run, we know that the node 'node' is locked for us. * Our caller has a reference on the hook. * Our caller has a reference on the node. * (In this case our caller is ng_apply_item() ). * The peer hook has a reference on the hook. * We are all set up except for the final call to the node, and * the clearing of the INVALID flag. */ if (NG_HOOK_NODE(hook) == &ng_deadnode) { /* * The node must have been freed again since we last visited * here. ng_destry_hook() has this effect but nothing else does. * We should just release our references and * free anything we can think of. * Since we know it's been destroyed, and it's our caller * that holds the references, just return. */ ERROUT(ENOENT); } if (hook->hk_node->nd_type->connect) { if ((error = (*hook->hk_node->nd_type->connect) (hook))) { ng_destroy_hook(hook); /* also zaps peer */ printf("failed in ng_con_part3()\n"); ERROUT(error); } } /* * XXX this is wrong for SMP. Possibly we need * to separate out 'create' and 'invalid' flags. * should only set flags on hooks we have locked under our node. */ hook->hk_flags &= ~HK_INVALID; done: NG_FREE_ITEM(item); return (error); } static int ng_con_part2(node_p node, item_p item, hook_p hook) { hook_p peer; int error = 0; /* * When we run, we know that the node 'node' is locked for us. * Our caller has a reference on the hook. * Our caller has a reference on the node. * (In this case our caller is ng_apply_item() ). * The peer hook has a reference on the hook. * our node pointer points to the 'dead' node. * First check the hook name is unique. * Should not happen because we checked before queueing this. */ if (ng_findhook(node, NG_HOOK_NAME(hook)) != NULL) { TRAP_ERROR(); ng_destroy_hook(hook); /* should destroy peer too */ printf("failed in ng_con_part2()\n"); ERROUT(EEXIST); } /* * Check if the node type code has something to say about it * If it fails, the unref of the hook will also unref the attached node, * however since that node is 'ng_deadnode' this will do nothing. * The peer hook will also be destroyed. */ if (node->nd_type->newhook != NULL) { if ((error = (*node->nd_type->newhook)(node, hook, hook->hk_name))) { ng_destroy_hook(hook); /* should destroy peer too */ printf("failed in ng_con_part2()\n"); ERROUT(error); } } /* * The 'type' agrees so far, so go ahead and link it in. * We'll ask again later when we actually connect the hooks. */ hook->hk_node = node; /* just overwrite ng_deadnode */ NG_NODE_REF(node); /* each hook counts as a reference */ LIST_INSERT_HEAD(&node->nd_hooks, hook, hk_hooks); node->nd_numhooks++; NG_HOOK_REF(hook); /* one for the node */ /* * We now have a symmetrical situation, where both hooks have been * linked to their nodes, the newhook methods have been called * And the references are all correct. The hooks are still marked * as invalid, as we have not called the 'connect' methods * yet. * We can call the local one immediately as we have the * node locked, but we need to queue the remote one. */ if (hook->hk_node->nd_type->connect) { if ((error = (*hook->hk_node->nd_type->connect) (hook))) { ng_destroy_hook(hook); /* also zaps peer */ printf("failed in ng_con_part2(A)\n"); ERROUT(error); } } /* * Acquire topo mutex to avoid race with ng_destroy_hook(). */ TOPOLOGY_RLOCK(); peer = hook->hk_peer; if (peer == &ng_deadhook) { TOPOLOGY_RUNLOCK(); printf("failed in ng_con_part2(B)\n"); ng_destroy_hook(hook); ERROUT(ENOENT); } TOPOLOGY_RUNLOCK(); if ((error = ng_send_fn2(peer->hk_node, peer, item, &ng_con_part3, NULL, 0, NG_REUSE_ITEM))) { printf("failed in ng_con_part2(C)\n"); ng_destroy_hook(hook); /* also zaps peer */ return (error); /* item was consumed. */ } hook->hk_flags &= ~HK_INVALID; /* need both to be able to work */ return (0); /* item was consumed. */ done: NG_FREE_ITEM(item); return (error); } /* * Connect this node with another node. We assume that this node is * currently locked, as we are only called from an NGM_CONNECT message. */ static int ng_con_nodes(item_p item, node_p node, const char *name, node_p node2, const char *name2) { int error; hook_p hook; hook_p hook2; if (ng_findhook(node2, name2) != NULL) { return(EEXIST); } if ((error = ng_add_hook(node, name, &hook))) /* gives us a ref */ return (error); /* Allocate the other hook and link it up */ NG_ALLOC_HOOK(hook2); if (hook2 == NULL) { TRAP_ERROR(); ng_destroy_hook(hook); /* XXX check ref counts so far */ NG_HOOK_UNREF(hook); /* including our ref */ return (ENOMEM); } hook2->hk_refs = 1; /* start with a reference for us. */ hook2->hk_flags = HK_INVALID; hook2->hk_peer = hook; /* Link the two together */ hook->hk_peer = hook2; NG_HOOK_REF(hook); /* Add a ref for the peer to each*/ NG_HOOK_REF(hook2); hook2->hk_node = &ng_deadnode; strlcpy(NG_HOOK_NAME(hook2), name2, NG_HOOKSIZ); /* * Queue the function above. * Procesing continues in that function in the lock context of * the other node. */ if ((error = ng_send_fn2(node2, hook2, item, &ng_con_part2, NULL, 0, NG_NOFLAGS))) { printf("failed in ng_con_nodes(): %d\n", error); ng_destroy_hook(hook); /* also zaps peer */ } NG_HOOK_UNREF(hook); /* Let each hook go if it wants to */ NG_HOOK_UNREF(hook2); return (error); } /* * Make a peer and connect. * We assume that the local node is locked. * The new node probably doesn't need a lock until * it has a hook, because it cannot really have any work until then, * but we should think about it a bit more. * * The problem may come if the other node also fires up * some hardware or a timer or some other source of activation, * also it may already get a command msg via it's ID. * * We could use the same method as ng_con_nodes() but we'd have * to add ability to remove the node when failing. (Not hard, just * make arg1 point to the node to remove). * Unless of course we just ignore failure to connect and leave * an unconnected node? */ static int ng_mkpeer(node_p node, const char *name, const char *name2, char *type) { node_p node2; hook_p hook1, hook2; int error; if ((error = ng_make_node(type, &node2))) { return (error); } if ((error = ng_add_hook(node, name, &hook1))) { /* gives us a ref */ ng_rmnode(node2, NULL, NULL, 0); return (error); } if ((error = ng_add_hook(node2, name2, &hook2))) { ng_rmnode(node2, NULL, NULL, 0); ng_destroy_hook(hook1); NG_HOOK_UNREF(hook1); return (error); } /* * Actually link the two hooks together. */ hook1->hk_peer = hook2; hook2->hk_peer = hook1; /* Each hook is referenced by the other */ NG_HOOK_REF(hook1); NG_HOOK_REF(hook2); /* Give each node the opportunity to veto the pending connection */ if (hook1->hk_node->nd_type->connect) { error = (*hook1->hk_node->nd_type->connect) (hook1); } if ((error == 0) && hook2->hk_node->nd_type->connect) { error = (*hook2->hk_node->nd_type->connect) (hook2); } /* * drop the references we were holding on the two hooks. */ if (error) { ng_destroy_hook(hook2); /* also zaps hook1 */ ng_rmnode(node2, NULL, NULL, 0); } else { /* As a last act, allow the hooks to be used */ hook1->hk_flags &= ~HK_INVALID; hook2->hk_flags &= ~HK_INVALID; } NG_HOOK_UNREF(hook1); NG_HOOK_UNREF(hook2); return (error); } /************************************************************************ Utility routines to send self messages ************************************************************************/ /* Shut this node down as soon as everyone is clear of it */ /* Should add arg "immediately" to jump the queue */ int ng_rmnode_self(node_p node) { int error; if (node == &ng_deadnode) return (0); node->nd_flags |= NGF_INVALID; if (node->nd_flags & NGF_CLOSING) return (0); error = ng_send_fn(node, NULL, &ng_rmnode, NULL, 0); return (error); } static void ng_rmhook_part2(node_p node, hook_p hook, void *arg1, int arg2) { ng_destroy_hook(hook); return ; } int ng_rmhook_self(hook_p hook) { int error; node_p node = NG_HOOK_NODE(hook); if (node == &ng_deadnode) return (0); error = ng_send_fn(node, hook, &ng_rmhook_part2, NULL, 0); return (error); } /*********************************************************************** * Parse and verify a string of the form: * * Such a string can refer to a specific node or a specific hook * on a specific node, depending on how you look at it. In the * latter case, the PATH component must not end in a dot. * * Both and are optional. The is a string * of hook names separated by dots. This breaks out the original * string, setting *nodep to "NODE" (or NULL if none) and *pathp * to "PATH" (or NULL if degenerate). Also, *hookp will point to * the final hook component of , if any, otherwise NULL. * * This returns -1 if the path is malformed. The char ** are optional. ***********************************************************************/ int ng_path_parse(char *addr, char **nodep, char **pathp, char **hookp) { char *node, *path, *hook; int k; /* * Extract absolute NODE, if any */ for (path = addr; *path && *path != ':'; path++); if (*path) { node = addr; /* Here's the NODE */ *path++ = '\0'; /* Here's the PATH */ /* Node name must not be empty */ if (!*node) return -1; /* A name of "." is OK; otherwise '.' not allowed */ if (strcmp(node, ".") != 0) { for (k = 0; node[k]; k++) if (node[k] == '.') return -1; } } else { node = NULL; /* No absolute NODE */ path = addr; /* Here's the PATH */ } /* Snoop for illegal characters in PATH */ for (k = 0; path[k]; k++) if (path[k] == ':') return -1; /* Check for no repeated dots in PATH */ for (k = 0; path[k]; k++) if (path[k] == '.' && path[k + 1] == '.') return -1; /* Remove extra (degenerate) dots from beginning or end of PATH */ if (path[0] == '.') path++; if (*path && path[strlen(path) - 1] == '.') path[strlen(path) - 1] = 0; /* If PATH has a dot, then we're not talking about a hook */ if (*path) { for (hook = path, k = 0; path[k]; k++) if (path[k] == '.') { hook = NULL; break; } } else path = hook = NULL; /* Done */ if (nodep) *nodep = node; if (pathp) *pathp = path; if (hookp) *hookp = hook; return (0); } /* * Given a path, which may be absolute or relative, and a starting node, * return the destination node. */ int ng_path2noderef(node_p here, const char *address, node_p *destp, hook_p *lasthook) { char fullpath[NG_PATHSIZ]; char *nodename, *path; node_p node, oldnode; /* Initialize */ if (destp == NULL) { TRAP_ERROR(); return EINVAL; } *destp = NULL; /* Make a writable copy of address for ng_path_parse() */ strncpy(fullpath, address, sizeof(fullpath) - 1); fullpath[sizeof(fullpath) - 1] = '\0'; /* Parse out node and sequence of hooks */ if (ng_path_parse(fullpath, &nodename, &path, NULL) < 0) { TRAP_ERROR(); return EINVAL; } /* * For an absolute address, jump to the starting node. * Note that this holds a reference on the node for us. * Don't forget to drop the reference if we don't need it. */ if (nodename) { node = ng_name2noderef(here, nodename); if (node == NULL) { TRAP_ERROR(); return (ENOENT); } } else { if (here == NULL) { TRAP_ERROR(); return (EINVAL); } node = here; NG_NODE_REF(node); } if (path == NULL) { if (lasthook != NULL) *lasthook = NULL; *destp = node; return (0); } /* * Now follow the sequence of hooks * * XXXGL: The path may demolish as we go the sequence, but if * we hold the topology mutex at critical places, then, I hope, * we would always have valid pointers in hand, although the * path behind us may no longer exist. */ for (;;) { hook_p hook; char *segment; /* * Break out the next path segment. Replace the dot we just * found with a NUL; "path" points to the next segment (or the * NUL at the end). */ for (segment = path; *path != '\0'; path++) { if (*path == '.') { *path++ = '\0'; break; } } /* We have a segment, so look for a hook by that name */ hook = ng_findhook(node, segment); TOPOLOGY_WLOCK(); /* Can't get there from here... */ if (hook == NULL || NG_HOOK_PEER(hook) == NULL || NG_HOOK_NOT_VALID(hook) || NG_HOOK_NOT_VALID(NG_HOOK_PEER(hook))) { TRAP_ERROR(); NG_NODE_UNREF(node); TOPOLOGY_WUNLOCK(); return (ENOENT); } /* * Hop on over to the next node * XXX * Big race conditions here as hooks and nodes go away * *** Idea.. store an ng_ID_t in each hook and use that * instead of the direct hook in this crawl? */ oldnode = node; if ((node = NG_PEER_NODE(hook))) NG_NODE_REF(node); /* XXX RACE */ NG_NODE_UNREF(oldnode); /* XXX another race */ if (NG_NODE_NOT_VALID(node)) { NG_NODE_UNREF(node); /* XXX more races */ TOPOLOGY_WUNLOCK(); TRAP_ERROR(); return (ENXIO); } if (*path == '\0') { if (lasthook != NULL) { if (hook != NULL) { *lasthook = NG_HOOK_PEER(hook); NG_HOOK_REF(*lasthook); } else *lasthook = NULL; } TOPOLOGY_WUNLOCK(); *destp = node; return (0); } TOPOLOGY_WUNLOCK(); } } /***************************************************************\ * Input queue handling. * All activities are submitted to the node via the input queue * which implements a multiple-reader/single-writer gate. * Items which cannot be handled immediately are queued. * * read-write queue locking inline functions * \***************************************************************/ static __inline void ng_queue_rw(node_p node, item_p item, int rw); static __inline item_p ng_dequeue(node_p node, int *rw); static __inline item_p ng_acquire_read(node_p node, item_p item); static __inline item_p ng_acquire_write(node_p node, item_p item); static __inline void ng_leave_read(node_p node); static __inline void ng_leave_write(node_p node); /* * Definition of the bits fields in the ng_queue flag word. * Defined here rather than in netgraph.h because no-one should fiddle * with them. * * The ordering here may be important! don't shuffle these. */ /*- Safety Barrier--------+ (adjustable to suit taste) (not used yet) | V +-------+-------+-------+-------+-------+-------+-------+-------+ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |A|c|t|i|v|e| |R|e|a|d|e|r| |C|o|u|n|t| | | | | | | | | |P|A| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |O|W| +-------+-------+-------+-------+-------+-------+-------+-------+ \___________________________ ____________________________/ | | V | | [active reader count] | | | | Operation Pending -------------------------------+ | | Active Writer ---------------------------------------+ Node queue has such semantics: - All flags modifications are atomic. - Reader count can be incremented only if there is no writer or pending flags. As soon as this can't be done with single operation, it is implemented with spin loop and atomic_cmpset(). - Writer flag can be set only if there is no any bits set. It is implemented with atomic_cmpset(). - Pending flag can be set any time, but to avoid collision on queue processing all queue fields are protected by the mutex. - Queue processing thread reads queue holding the mutex, but releases it while processing. When queue is empty pending flag is removed. */ #define WRITER_ACTIVE 0x00000001 #define OP_PENDING 0x00000002 #define READER_INCREMENT 0x00000004 #define READER_MASK 0xfffffffc /* Not valid if WRITER_ACTIVE is set */ #define SAFETY_BARRIER 0x00100000 /* 128K items queued should be enough */ /* Defines of more elaborate states on the queue */ /* Mask of bits a new read cares about */ #define NGQ_RMASK (WRITER_ACTIVE|OP_PENDING) /* Mask of bits a new write cares about */ #define NGQ_WMASK (NGQ_RMASK|READER_MASK) /* Test to decide if there is something on the queue. */ #define QUEUE_ACTIVE(QP) ((QP)->q_flags & OP_PENDING) /* How to decide what the next queued item is. */ #define HEAD_IS_READER(QP) NGI_QUEUED_READER(STAILQ_FIRST(&(QP)->queue)) #define HEAD_IS_WRITER(QP) NGI_QUEUED_WRITER(STAILQ_FIRST(&(QP)->queue)) /* notused */ /* Read the status to decide if the next item on the queue can now run. */ #define QUEUED_READER_CAN_PROCEED(QP) \ (((QP)->q_flags & (NGQ_RMASK & ~OP_PENDING)) == 0) #define QUEUED_WRITER_CAN_PROCEED(QP) \ (((QP)->q_flags & (NGQ_WMASK & ~OP_PENDING)) == 0) /* Is there a chance of getting ANY work off the queue? */ #define NEXT_QUEUED_ITEM_CAN_PROCEED(QP) \ ((HEAD_IS_READER(QP)) ? QUEUED_READER_CAN_PROCEED(QP) : \ QUEUED_WRITER_CAN_PROCEED(QP)) #define NGQRW_R 0 #define NGQRW_W 1 #define NGQ2_WORKQ 0x00000001 /* * Taking into account the current state of the queue and node, possibly take * the next entry off the queue and return it. Return NULL if there was * nothing we could return, either because there really was nothing there, or * because the node was in a state where it cannot yet process the next item * on the queue. */ static __inline item_p ng_dequeue(node_p node, int *rw) { item_p item; struct ng_queue *ngq = &node->nd_input_queue; /* This MUST be called with the mutex held. */ mtx_assert(&ngq->q_mtx, MA_OWNED); /* If there is nothing queued, then just return. */ if (!QUEUE_ACTIVE(ngq)) { CTR4(KTR_NET, "%20s: node [%x] (%p) queue empty; " "queue flags 0x%lx", __func__, node->nd_ID, node, ngq->q_flags); return (NULL); } /* * From here, we can assume there is a head item. * We need to find out what it is and if it can be dequeued, given * the current state of the node. */ if (HEAD_IS_READER(ngq)) { while (1) { long t = ngq->q_flags; if (t & WRITER_ACTIVE) { /* There is writer, reader can't proceed. */ CTR4(KTR_NET, "%20s: node [%x] (%p) queued " "reader can't proceed; queue flags 0x%lx", __func__, node->nd_ID, node, t); return (NULL); } if (atomic_cmpset_acq_int(&ngq->q_flags, t, t + READER_INCREMENT)) break; cpu_spinwait(); } /* We have got reader lock for the node. */ *rw = NGQRW_R; } else if (atomic_cmpset_acq_int(&ngq->q_flags, OP_PENDING, OP_PENDING + WRITER_ACTIVE)) { /* We have got writer lock for the node. */ *rw = NGQRW_W; } else { /* There is somebody other, writer can't proceed. */ CTR4(KTR_NET, "%20s: node [%x] (%p) queued writer can't " "proceed; queue flags 0x%lx", __func__, node->nd_ID, node, ngq->q_flags); return (NULL); } /* * Now we dequeue the request (whatever it may be) and correct the * pending flags and the next and last pointers. */ item = STAILQ_FIRST(&ngq->queue); STAILQ_REMOVE_HEAD(&ngq->queue, el_next); if (STAILQ_EMPTY(&ngq->queue)) atomic_clear_int(&ngq->q_flags, OP_PENDING); CTR6(KTR_NET, "%20s: node [%x] (%p) returning item %p as %s; queue " "flags 0x%lx", __func__, node->nd_ID, node, item, *rw ? "WRITER" : "READER", ngq->q_flags); return (item); } /* * Queue a packet to be picked up later by someone else. * If the queue could be run now, add node to the queue handler's worklist. */ static __inline void ng_queue_rw(node_p node, item_p item, int rw) { struct ng_queue *ngq = &node->nd_input_queue; if (rw == NGQRW_W) NGI_SET_WRITER(item); else NGI_SET_READER(item); item->depth = 1; NG_QUEUE_LOCK(ngq); /* Set OP_PENDING flag and enqueue the item. */ atomic_set_int(&ngq->q_flags, OP_PENDING); STAILQ_INSERT_TAIL(&ngq->queue, item, el_next); CTR5(KTR_NET, "%20s: node [%x] (%p) queued item %p as %s", __func__, node->nd_ID, node, item, rw ? "WRITER" : "READER" ); /* * We can take the worklist lock with the node locked * BUT NOT THE REVERSE! */ if (NEXT_QUEUED_ITEM_CAN_PROCEED(ngq)) ng_worklist_add(node); NG_QUEUE_UNLOCK(ngq); } /* Acquire reader lock on node. If node is busy, queue the packet. */ static __inline item_p ng_acquire_read(node_p node, item_p item) { KASSERT(node != &ng_deadnode, ("%s: working on deadnode", __func__)); /* Reader needs node without writer and pending items. */ for (;;) { long t = node->nd_input_queue.q_flags; if (t & NGQ_RMASK) break; /* Node is not ready for reader. */ if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags, t, t + READER_INCREMENT)) { /* Successfully grabbed node */ CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p", __func__, node->nd_ID, node, item); return (item); } cpu_spinwait(); }; /* Queue the request for later. */ ng_queue_rw(node, item, NGQRW_R); return (NULL); } /* Acquire writer lock on node. If node is busy, queue the packet. */ static __inline item_p ng_acquire_write(node_p node, item_p item) { KASSERT(node != &ng_deadnode, ("%s: working on deadnode", __func__)); /* Writer needs completely idle node. */ if (atomic_cmpset_acq_int(&node->nd_input_queue.q_flags, 0, WRITER_ACTIVE)) { /* Successfully grabbed node */ CTR4(KTR_NET, "%20s: node [%x] (%p) acquired item %p", __func__, node->nd_ID, node, item); return (item); } /* Queue the request for later. */ ng_queue_rw(node, item, NGQRW_W); return (NULL); } #if 0 static __inline item_p ng_upgrade_write(node_p node, item_p item) { struct ng_queue *ngq = &node->nd_input_queue; KASSERT(node != &ng_deadnode, ("%s: working on deadnode", __func__)); NGI_SET_WRITER(item); NG_QUEUE_LOCK(ngq); /* * There will never be no readers as we are there ourselves. * Set the WRITER_ACTIVE flags ASAP to block out fast track readers. * The caller we are running from will call ng_leave_read() * soon, so we must account for that. We must leave again with the * READER lock. If we find other readers, then * queue the request for later. However "later" may be rignt now * if there are no readers. We don't really care if there are queued * items as we will bypass them anyhow. */ atomic_add_int(&ngq->q_flags, WRITER_ACTIVE - READER_INCREMENT); if ((ngq->q_flags & (NGQ_WMASK & ~OP_PENDING)) == WRITER_ACTIVE) { NG_QUEUE_UNLOCK(ngq); /* It's just us, act on the item. */ /* will NOT drop writer lock when done */ ng_apply_item(node, item, 0); /* * Having acted on the item, atomically * downgrade back to READER and finish up. */ atomic_add_int(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE); /* Our caller will call ng_leave_read() */ return; } /* * It's not just us active, so queue us AT THE HEAD. * "Why?" I hear you ask. * Put us at the head of the queue as we've already been * through it once. If there is nothing else waiting, * set the correct flags. */ if (STAILQ_EMPTY(&ngq->queue)) { /* We've gone from, 0 to 1 item in the queue */ atomic_set_int(&ngq->q_flags, OP_PENDING); CTR3(KTR_NET, "%20s: node [%x] (%p) set OP_PENDING", __func__, node->nd_ID, node); }; STAILQ_INSERT_HEAD(&ngq->queue, item, el_next); CTR4(KTR_NET, "%20s: node [%x] (%p) requeued item %p as WRITER", __func__, node->nd_ID, node, item ); /* Reverse what we did above. That downgrades us back to reader */ atomic_add_int(&ngq->q_flags, READER_INCREMENT - WRITER_ACTIVE); if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq)) ng_worklist_add(node); NG_QUEUE_UNLOCK(ngq); return; } #endif /* Release reader lock. */ static __inline void ng_leave_read(node_p node) { atomic_subtract_rel_int(&node->nd_input_queue.q_flags, READER_INCREMENT); } /* Release writer lock. */ static __inline void ng_leave_write(node_p node) { atomic_clear_rel_int(&node->nd_input_queue.q_flags, WRITER_ACTIVE); } /* Purge node queue. Called on node shutdown. */ static void ng_flush_input_queue(node_p node) { struct ng_queue *ngq = &node->nd_input_queue; item_p item; NG_QUEUE_LOCK(ngq); while ((item = STAILQ_FIRST(&ngq->queue)) != NULL) { STAILQ_REMOVE_HEAD(&ngq->queue, el_next); if (STAILQ_EMPTY(&ngq->queue)) atomic_clear_int(&ngq->q_flags, OP_PENDING); NG_QUEUE_UNLOCK(ngq); /* If the item is supplying a callback, call it with an error */ if (item->apply != NULL) { if (item->depth == 1) item->apply->error = ENOENT; if (refcount_release(&item->apply->refs)) { (*item->apply->apply)(item->apply->context, item->apply->error); } } NG_FREE_ITEM(item); NG_QUEUE_LOCK(ngq); } NG_QUEUE_UNLOCK(ngq); } /*********************************************************************** * Externally visible method for sending or queueing messages or data. ***********************************************************************/ /* * The module code should have filled out the item correctly by this stage: * Common: * reference to destination node. * Reference to destination rcv hook if relevant. * apply pointer must be or NULL or reference valid struct ng_apply_info. * Data: * pointer to mbuf * Control_Message: * pointer to msg. * ID of original sender node. (return address) * Function: * Function pointer * void * argument * integer argument * * The nodes have several routines and macros to help with this task: */ int ng_snd_item(item_p item, int flags) { hook_p hook; node_p node; int queue, rw; struct ng_queue *ngq; int error = 0; /* We are sending item, so it must be present! */ KASSERT(item != NULL, ("ng_snd_item: item is NULL")); #ifdef NETGRAPH_DEBUG _ngi_check(item, __FILE__, __LINE__); #endif /* Item was sent once more, postpone apply() call. */ if (item->apply) refcount_acquire(&item->apply->refs); node = NGI_NODE(item); /* Node is never optional. */ KASSERT(node != NULL, ("ng_snd_item: node is NULL")); hook = NGI_HOOK(item); /* Valid hook and mbuf are mandatory for data. */ if ((item->el_flags & NGQF_TYPE) == NGQF_DATA) { KASSERT(hook != NULL, ("ng_snd_item: hook for data is NULL")); if (NGI_M(item) == NULL) ERROUT(EINVAL); CHECK_DATA_MBUF(NGI_M(item)); } /* * If the item or the node specifies single threading, force * writer semantics. Similarly, the node may say one hook always * produces writers. These are overrides. */ if (((item->el_flags & NGQF_RW) == NGQF_WRITER) || (node->nd_flags & NGF_FORCE_WRITER) || (hook && (hook->hk_flags & HK_FORCE_WRITER))) { rw = NGQRW_W; } else { rw = NGQRW_R; } /* * If sender or receiver requests queued delivery, or call graph * loops back from outbound to inbound path, or stack usage * level is dangerous - enqueue message. */ if ((flags & NG_QUEUE) || (hook && (hook->hk_flags & HK_QUEUE))) { queue = 1; } else if (hook && (hook->hk_flags & HK_TO_INBOUND) && curthread->td_ng_outbound) { queue = 1; } else { queue = 0; #ifdef GET_STACK_USAGE /* * Most of netgraph nodes have small stack consumption and * for them 25% of free stack space is more than enough. * Nodes/hooks with higher stack usage should be marked as * HI_STACK. For them 50% of stack will be guaranteed then. * XXX: Values 25% and 50% are completely empirical. */ size_t st, su, sl; GET_STACK_USAGE(st, su); sl = st - su; if ((sl * 4 < st) || ((sl * 2 < st) && ((node->nd_flags & NGF_HI_STACK) || (hook && (hook->hk_flags & HK_HI_STACK))))) queue = 1; #endif } if (queue) { /* Put it on the queue for that node*/ ng_queue_rw(node, item, rw); return ((flags & NG_PROGRESS) ? EINPROGRESS : 0); } /* * We already decided how we will be queueud or treated. * Try get the appropriate operating permission. */ if (rw == NGQRW_R) item = ng_acquire_read(node, item); else item = ng_acquire_write(node, item); /* Item was queued while trying to get permission. */ if (item == NULL) return ((flags & NG_PROGRESS) ? EINPROGRESS : 0); NGI_GET_NODE(item, node); /* zaps stored node */ item->depth++; error = ng_apply_item(node, item, rw); /* drops r/w lock when done */ /* If something is waiting on queue and ready, schedule it. */ ngq = &node->nd_input_queue; if (QUEUE_ACTIVE(ngq)) { NG_QUEUE_LOCK(ngq); if (QUEUE_ACTIVE(ngq) && NEXT_QUEUED_ITEM_CAN_PROCEED(ngq)) ng_worklist_add(node); NG_QUEUE_UNLOCK(ngq); } /* * Node may go away as soon as we remove the reference. * Whatever we do, DO NOT access the node again! */ NG_NODE_UNREF(node); return (error); done: /* If was not sent, apply callback here. */ if (item->apply != NULL) { if (item->depth == 0 && error != 0) item->apply->error = error; if (refcount_release(&item->apply->refs)) { (*item->apply->apply)(item->apply->context, item->apply->error); } } NG_FREE_ITEM(item); return (error); } /* * We have an item that was possibly queued somewhere. * It should contain all the information needed * to run it on the appropriate node/hook. * If there is apply pointer and we own the last reference, call apply(). */ static int ng_apply_item(node_p node, item_p item, int rw) { hook_p hook; ng_rcvdata_t *rcvdata; ng_rcvmsg_t *rcvmsg; struct ng_apply_info *apply; int error = 0, depth; /* Node and item are never optional. */ KASSERT(node != NULL, ("ng_apply_item: node is NULL")); KASSERT(item != NULL, ("ng_apply_item: item is NULL")); NGI_GET_HOOK(item, hook); /* clears stored hook */ #ifdef NETGRAPH_DEBUG _ngi_check(item, __FILE__, __LINE__); #endif apply = item->apply; depth = item->depth; switch (item->el_flags & NGQF_TYPE) { case NGQF_DATA: /* * Check things are still ok as when we were queued. */ KASSERT(hook != NULL, ("ng_apply_item: hook for data is NULL")); if (NG_HOOK_NOT_VALID(hook) || NG_NODE_NOT_VALID(node)) { error = EIO; NG_FREE_ITEM(item); break; } /* * If no receive method, just silently drop it. * Give preference to the hook over-ride method. */ if ((!(rcvdata = hook->hk_rcvdata)) && (!(rcvdata = NG_HOOK_NODE(hook)->nd_type->rcvdata))) { error = 0; NG_FREE_ITEM(item); break; } error = (*rcvdata)(hook, item); break; case NGQF_MESG: if (hook && NG_HOOK_NOT_VALID(hook)) { /* * The hook has been zapped then we can't use it. * Immediately drop its reference. * The message may not need it. */ NG_HOOK_UNREF(hook); hook = NULL; } /* * Similarly, if the node is a zombie there is * nothing we can do with it, drop everything. */ if (NG_NODE_NOT_VALID(node)) { TRAP_ERROR(); error = EINVAL; NG_FREE_ITEM(item); break; } /* * Call the appropriate message handler for the object. * It is up to the message handler to free the message. * If it's a generic message, handle it generically, * otherwise call the type's message handler (if it exists). * XXX (race). Remember that a queued message may * reference a node or hook that has just been * invalidated. It will exist as the queue code * is holding a reference, but.. */ if ((NGI_MSG(item)->header.typecookie == NGM_GENERIC_COOKIE) && ((NGI_MSG(item)->header.flags & NGF_RESP) == 0)) { error = ng_generic_msg(node, item, hook); break; } if (((!hook) || (!(rcvmsg = hook->hk_rcvmsg))) && (!(rcvmsg = node->nd_type->rcvmsg))) { TRAP_ERROR(); error = 0; NG_FREE_ITEM(item); break; } error = (*rcvmsg)(node, item, hook); break; case NGQF_FN: case NGQF_FN2: /* * In the case of the shutdown message we allow it to hit * even if the node is invalid. */ if (NG_NODE_NOT_VALID(node) && NGI_FN(item) != &ng_rmnode) { TRAP_ERROR(); error = EINVAL; NG_FREE_ITEM(item); break; } /* Same is about some internal functions and invalid hook. */ if (hook && NG_HOOK_NOT_VALID(hook) && NGI_FN2(item) != &ng_con_part2 && NGI_FN2(item) != &ng_con_part3 && NGI_FN(item) != &ng_rmhook_part2) { TRAP_ERROR(); error = EINVAL; NG_FREE_ITEM(item); break; } if ((item->el_flags & NGQF_TYPE) == NGQF_FN) { (*NGI_FN(item))(node, hook, NGI_ARG1(item), NGI_ARG2(item)); NG_FREE_ITEM(item); } else /* it is NGQF_FN2 */ error = (*NGI_FN2(item))(node, item, hook); break; } /* * We held references on some of the resources * that we took from the item. Now that we have * finished doing everything, drop those references. */ if (hook) NG_HOOK_UNREF(hook); if (rw == NGQRW_R) ng_leave_read(node); else ng_leave_write(node); /* Apply callback. */ if (apply != NULL) { if (depth == 1 && error != 0) apply->error = error; if (refcount_release(&apply->refs)) (*apply->apply)(apply->context, apply->error); } return (error); } /*********************************************************************** * Implement the 'generic' control messages ***********************************************************************/ static int ng_generic_msg(node_p here, item_p item, hook_p lasthook) { int error = 0; struct ng_mesg *msg; struct ng_mesg *resp = NULL; NGI_GET_MSG(item, msg); if (msg->header.typecookie != NGM_GENERIC_COOKIE) { TRAP_ERROR(); error = EINVAL; goto out; } switch (msg->header.cmd) { case NGM_SHUTDOWN: ng_rmnode(here, NULL, NULL, 0); break; case NGM_MKPEER: { struct ngm_mkpeer *const mkp = (struct ngm_mkpeer *) msg->data; if (msg->header.arglen != sizeof(*mkp)) { TRAP_ERROR(); error = EINVAL; break; } mkp->type[sizeof(mkp->type) - 1] = '\0'; mkp->ourhook[sizeof(mkp->ourhook) - 1] = '\0'; mkp->peerhook[sizeof(mkp->peerhook) - 1] = '\0'; error = ng_mkpeer(here, mkp->ourhook, mkp->peerhook, mkp->type); break; } case NGM_CONNECT: { struct ngm_connect *const con = (struct ngm_connect *) msg->data; node_p node2; if (msg->header.arglen != sizeof(*con)) { TRAP_ERROR(); error = EINVAL; break; } con->path[sizeof(con->path) - 1] = '\0'; con->ourhook[sizeof(con->ourhook) - 1] = '\0'; con->peerhook[sizeof(con->peerhook) - 1] = '\0'; /* Don't forget we get a reference.. */ error = ng_path2noderef(here, con->path, &node2, NULL); if (error) break; error = ng_con_nodes(item, here, con->ourhook, node2, con->peerhook); NG_NODE_UNREF(node2); break; } case NGM_NAME: { struct ngm_name *const nam = (struct ngm_name *) msg->data; if (msg->header.arglen != sizeof(*nam)) { TRAP_ERROR(); error = EINVAL; break; } nam->name[sizeof(nam->name) - 1] = '\0'; error = ng_name_node(here, nam->name); break; } case NGM_RMHOOK: { struct ngm_rmhook *const rmh = (struct ngm_rmhook *) msg->data; hook_p hook; if (msg->header.arglen != sizeof(*rmh)) { TRAP_ERROR(); error = EINVAL; break; } rmh->ourhook[sizeof(rmh->ourhook) - 1] = '\0'; if ((hook = ng_findhook(here, rmh->ourhook)) != NULL) ng_destroy_hook(hook); break; } case NGM_NODEINFO: { struct nodeinfo *ni; NG_MKRESPONSE(resp, msg, sizeof(*ni), M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } /* Fill in node info */ ni = (struct nodeinfo *) resp->data; if (NG_NODE_HAS_NAME(here)) strcpy(ni->name, NG_NODE_NAME(here)); strcpy(ni->type, here->nd_type->name); ni->id = ng_node2ID(here); ni->hooks = here->nd_numhooks; break; } case NGM_LISTHOOKS: { const int nhooks = here->nd_numhooks; struct hooklist *hl; struct nodeinfo *ni; hook_p hook; /* Get response struct */ NG_MKRESPONSE(resp, msg, sizeof(*hl) + (nhooks * sizeof(struct linkinfo)), M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } hl = (struct hooklist *) resp->data; ni = &hl->nodeinfo; /* Fill in node info */ if (NG_NODE_HAS_NAME(here)) strcpy(ni->name, NG_NODE_NAME(here)); strcpy(ni->type, here->nd_type->name); ni->id = ng_node2ID(here); /* Cycle through the linked list of hooks */ ni->hooks = 0; LIST_FOREACH(hook, &here->nd_hooks, hk_hooks) { struct linkinfo *const link = &hl->link[ni->hooks]; if (ni->hooks >= nhooks) { log(LOG_ERR, "%s: number of %s changed\n", __func__, "hooks"); break; } if (NG_HOOK_NOT_VALID(hook)) continue; strcpy(link->ourhook, NG_HOOK_NAME(hook)); strcpy(link->peerhook, NG_PEER_HOOK_NAME(hook)); if (NG_PEER_NODE_NAME(hook)[0] != '\0') strcpy(link->nodeinfo.name, NG_PEER_NODE_NAME(hook)); strcpy(link->nodeinfo.type, NG_PEER_NODE(hook)->nd_type->name); link->nodeinfo.id = ng_node2ID(NG_PEER_NODE(hook)); link->nodeinfo.hooks = NG_PEER_NODE(hook)->nd_numhooks; ni->hooks++; } break; } case NGM_LISTNODES: { struct namelist *nl; node_p node; int i; IDHASH_RLOCK(); /* Get response struct. */ NG_MKRESPONSE(resp, msg, sizeof(*nl) + (V_ng_nodes * sizeof(struct nodeinfo)), M_NOWAIT | M_ZERO); if (resp == NULL) { IDHASH_RUNLOCK(); error = ENOMEM; break; } nl = (struct namelist *) resp->data; /* Cycle through the lists of nodes. */ nl->numnames = 0; for (i = 0; i <= V_ng_ID_hmask; i++) { LIST_FOREACH(node, &V_ng_ID_hash[i], nd_idnodes) { struct nodeinfo *const np = &nl->nodeinfo[nl->numnames]; if (NG_NODE_NOT_VALID(node)) continue; if (NG_NODE_HAS_NAME(node)) strcpy(np->name, NG_NODE_NAME(node)); strcpy(np->type, node->nd_type->name); np->id = ng_node2ID(node); np->hooks = node->nd_numhooks; KASSERT(nl->numnames < V_ng_nodes, ("%s: no space", __func__)); nl->numnames++; } } IDHASH_RUNLOCK(); break; } case NGM_LISTNAMES: { struct namelist *nl; node_p node; int i; NAMEHASH_RLOCK(); /* Get response struct. */ NG_MKRESPONSE(resp, msg, sizeof(*nl) + (V_ng_named_nodes * sizeof(struct nodeinfo)), M_NOWAIT); if (resp == NULL) { NAMEHASH_RUNLOCK(); error = ENOMEM; break; } nl = (struct namelist *) resp->data; /* Cycle through the lists of nodes. */ nl->numnames = 0; for (i = 0; i <= V_ng_name_hmask; i++) { LIST_FOREACH(node, &V_ng_name_hash[i], nd_nodes) { struct nodeinfo *const np = &nl->nodeinfo[nl->numnames]; if (NG_NODE_NOT_VALID(node)) continue; strcpy(np->name, NG_NODE_NAME(node)); strcpy(np->type, node->nd_type->name); np->id = ng_node2ID(node); np->hooks = node->nd_numhooks; KASSERT(nl->numnames < V_ng_named_nodes, ("%s: no space", __func__)); nl->numnames++; } } NAMEHASH_RUNLOCK(); break; } case NGM_LISTTYPES: { struct typelist *tl; struct ng_type *type; int num = 0; TYPELIST_RLOCK(); /* Count number of types */ LIST_FOREACH(type, &ng_typelist, types) num++; /* Get response struct */ NG_MKRESPONSE(resp, msg, sizeof(*tl) + (num * sizeof(struct typeinfo)), M_NOWAIT); if (resp == NULL) { TYPELIST_RUNLOCK(); error = ENOMEM; break; } tl = (struct typelist *) resp->data; /* Cycle through the linked list of types */ tl->numtypes = 0; LIST_FOREACH(type, &ng_typelist, types) { struct typeinfo *const tp = &tl->typeinfo[tl->numtypes]; strcpy(tp->type_name, type->name); tp->numnodes = type->refs - 1; /* don't count list */ KASSERT(tl->numtypes < num, ("%s: no space", __func__)); tl->numtypes++; } TYPELIST_RUNLOCK(); break; } case NGM_BINARY2ASCII: { int bufSize = 20 * 1024; /* XXX hard coded constant */ const struct ng_parse_type *argstype; const struct ng_cmdlist *c; struct ng_mesg *binary, *ascii; /* Data area must contain a valid netgraph message */ binary = (struct ng_mesg *)msg->data; if (msg->header.arglen < sizeof(struct ng_mesg) || (msg->header.arglen - sizeof(struct ng_mesg) < binary->header.arglen)) { TRAP_ERROR(); error = EINVAL; break; } /* Get a response message with lots of room */ NG_MKRESPONSE(resp, msg, sizeof(*ascii) + bufSize, M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } ascii = (struct ng_mesg *)resp->data; /* Copy binary message header to response message payload */ bcopy(binary, ascii, sizeof(*binary)); /* Find command by matching typecookie and command number */ for (c = here->nd_type->cmdlist; c != NULL && c->name != NULL; c++) { if (binary->header.typecookie == c->cookie && binary->header.cmd == c->cmd) break; } if (c == NULL || c->name == NULL) { for (c = ng_generic_cmds; c->name != NULL; c++) { if (binary->header.typecookie == c->cookie && binary->header.cmd == c->cmd) break; } if (c->name == NULL) { NG_FREE_MSG(resp); error = ENOSYS; break; } } /* Convert command name to ASCII */ snprintf(ascii->header.cmdstr, sizeof(ascii->header.cmdstr), "%s", c->name); /* Convert command arguments to ASCII */ argstype = (binary->header.flags & NGF_RESP) ? c->respType : c->mesgType; if (argstype == NULL) { *ascii->data = '\0'; } else { if ((error = ng_unparse(argstype, (u_char *)binary->data, ascii->data, bufSize)) != 0) { NG_FREE_MSG(resp); break; } } /* Return the result as struct ng_mesg plus ASCII string */ bufSize = strlen(ascii->data) + 1; ascii->header.arglen = bufSize; resp->header.arglen = sizeof(*ascii) + bufSize; break; } case NGM_ASCII2BINARY: { int bufSize = 20 * 1024; /* XXX hard coded constant */ const struct ng_cmdlist *c; const struct ng_parse_type *argstype; struct ng_mesg *ascii, *binary; int off = 0; /* Data area must contain at least a struct ng_mesg + '\0' */ ascii = (struct ng_mesg *)msg->data; if ((msg->header.arglen < sizeof(*ascii) + 1) || (ascii->header.arglen < 1) || (msg->header.arglen < sizeof(*ascii) + ascii->header.arglen)) { TRAP_ERROR(); error = EINVAL; break; } ascii->data[ascii->header.arglen - 1] = '\0'; /* Get a response message with lots of room */ NG_MKRESPONSE(resp, msg, sizeof(*binary) + bufSize, M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } binary = (struct ng_mesg *)resp->data; /* Copy ASCII message header to response message payload */ bcopy(ascii, binary, sizeof(*ascii)); /* Find command by matching ASCII command string */ for (c = here->nd_type->cmdlist; c != NULL && c->name != NULL; c++) { if (strcmp(ascii->header.cmdstr, c->name) == 0) break; } if (c == NULL || c->name == NULL) { for (c = ng_generic_cmds; c->name != NULL; c++) { if (strcmp(ascii->header.cmdstr, c->name) == 0) break; } if (c->name == NULL) { NG_FREE_MSG(resp); error = ENOSYS; break; } } /* Convert command name to binary */ binary->header.cmd = c->cmd; binary->header.typecookie = c->cookie; /* Convert command arguments to binary */ argstype = (binary->header.flags & NGF_RESP) ? c->respType : c->mesgType; if (argstype == NULL) { bufSize = 0; } else { if ((error = ng_parse(argstype, ascii->data, &off, (u_char *)binary->data, &bufSize)) != 0) { NG_FREE_MSG(resp); break; } } /* Return the result */ binary->header.arglen = bufSize; resp->header.arglen = sizeof(*binary) + bufSize; break; } case NGM_TEXT_CONFIG: case NGM_TEXT_STATUS: /* * This one is tricky as it passes the command down to the * actual node, even though it is a generic type command. * This means we must assume that the item/msg is already freed * when control passes back to us. */ if (here->nd_type->rcvmsg != NULL) { NGI_MSG(item) = msg; /* put it back as we found it */ return((*here->nd_type->rcvmsg)(here, item, lasthook)); } /* Fall through if rcvmsg not supported */ default: TRAP_ERROR(); error = EINVAL; } /* * Sometimes a generic message may be statically allocated * to avoid problems with allocating when in tight memory situations. * Don't free it if it is so. * I break them appart here, because erros may cause a free if the item * in which case we'd be doing it twice. * they are kept together above, to simplify freeing. */ out: NG_RESPOND_MSG(error, here, item, resp); NG_FREE_MSG(msg); return (error); } /************************************************************************ Queue element get/free routines ************************************************************************/ uma_zone_t ng_qzone; uma_zone_t ng_qdzone; static int numthreads = 0; /* number of queue threads */ static int maxalloc = 4096;/* limit the damage of a leak */ -static int maxdata = 512; /* limit the damage of a DoS */ +static int maxdata = 4096; /* limit the damage of a DoS */ SYSCTL_INT(_net_graph, OID_AUTO, threads, CTLFLAG_RDTUN, &numthreads, 0, "Number of queue processing threads"); SYSCTL_INT(_net_graph, OID_AUTO, maxalloc, CTLFLAG_RDTUN, &maxalloc, 0, "Maximum number of non-data queue items to allocate"); SYSCTL_INT(_net_graph, OID_AUTO, maxdata, CTLFLAG_RDTUN, &maxdata, 0, "Maximum number of data queue items to allocate"); #ifdef NETGRAPH_DEBUG static TAILQ_HEAD(, ng_item) ng_itemlist = TAILQ_HEAD_INITIALIZER(ng_itemlist); static int allocated; /* number of items malloc'd */ #endif /* * Get a queue entry. * This is usually called when a packet first enters netgraph. * By definition, this is usually from an interrupt, or from a user. * Users are not so important, but try be quick for the times that it's * an interrupt. */ static __inline item_p ng_alloc_item(int type, int flags) { item_p item; KASSERT(((type & ~NGQF_TYPE) == 0), ("%s: incorrect item type: %d", __func__, type)); item = uma_zalloc((type == NGQF_DATA) ? ng_qdzone : ng_qzone, ((flags & NG_WAITOK) ? M_WAITOK : M_NOWAIT) | M_ZERO); if (item) { item->el_flags = type; #ifdef NETGRAPH_DEBUG mtx_lock(&ngq_mtx); TAILQ_INSERT_TAIL(&ng_itemlist, item, all); allocated++; mtx_unlock(&ngq_mtx); #endif } return (item); } /* * Release a queue entry */ void ng_free_item(item_p item) { /* * The item may hold resources on it's own. We need to free * these before we can free the item. What they are depends upon * what kind of item it is. it is important that nodes zero * out pointers to resources that they remove from the item * or we release them again here. */ switch (item->el_flags & NGQF_TYPE) { case NGQF_DATA: /* If we have an mbuf still attached.. */ NG_FREE_M(_NGI_M(item)); break; case NGQF_MESG: _NGI_RETADDR(item) = 0; NG_FREE_MSG(_NGI_MSG(item)); break; case NGQF_FN: case NGQF_FN2: /* nothing to free really, */ _NGI_FN(item) = NULL; _NGI_ARG1(item) = NULL; _NGI_ARG2(item) = 0; break; } /* If we still have a node or hook referenced... */ _NGI_CLR_NODE(item); _NGI_CLR_HOOK(item); #ifdef NETGRAPH_DEBUG mtx_lock(&ngq_mtx); TAILQ_REMOVE(&ng_itemlist, item, all); allocated--; mtx_unlock(&ngq_mtx); #endif uma_zfree(((item->el_flags & NGQF_TYPE) == NGQF_DATA) ? ng_qdzone : ng_qzone, item); } /* * Change type of the queue entry. * Possibly reallocates it from another UMA zone. */ static __inline item_p ng_realloc_item(item_p pitem, int type, int flags) { item_p item; int from, to; KASSERT((pitem != NULL), ("%s: can't reallocate NULL", __func__)); KASSERT(((type & ~NGQF_TYPE) == 0), ("%s: incorrect item type: %d", __func__, type)); from = ((pitem->el_flags & NGQF_TYPE) == NGQF_DATA); to = (type == NGQF_DATA); if (from != to) { /* If reallocation is required do it and copy item. */ if ((item = ng_alloc_item(type, flags)) == NULL) { ng_free_item(pitem); return (NULL); } *item = *pitem; ng_free_item(pitem); } else item = pitem; item->el_flags = (item->el_flags & ~NGQF_TYPE) | type; return (item); } /************************************************************************ Module routines ************************************************************************/ /* * Handle the loading/unloading of a netgraph node type module */ int ng_mod_event(module_t mod, int event, void *data) { struct ng_type *const type = data; int error = 0; switch (event) { case MOD_LOAD: /* Register new netgraph node type */ if ((error = ng_newtype(type)) != 0) break; /* Call type specific code */ if (type->mod_event != NULL) if ((error = (*type->mod_event)(mod, event, data))) { TYPELIST_WLOCK(); type->refs--; /* undo it */ LIST_REMOVE(type, types); TYPELIST_WUNLOCK(); } break; case MOD_UNLOAD: if (type->refs > 1) { /* make sure no nodes exist! */ error = EBUSY; } else { if (type->refs == 0) /* failed load, nothing to undo */ break; if (type->mod_event != NULL) { /* check with type */ error = (*type->mod_event)(mod, event, data); if (error != 0) /* type refuses.. */ break; } TYPELIST_WLOCK(); LIST_REMOVE(type, types); TYPELIST_WUNLOCK(); } break; default: if (type->mod_event != NULL) error = (*type->mod_event)(mod, event, data); else error = EOPNOTSUPP; /* XXX ? */ break; } return (error); } static void vnet_netgraph_init(const void *unused __unused) { /* We start with small hashes, but they can grow. */ V_ng_ID_hash = hashinit(16, M_NETGRAPH_NODE, &V_ng_ID_hmask); V_ng_name_hash = hashinit(16, M_NETGRAPH_NODE, &V_ng_name_hmask); } VNET_SYSINIT(vnet_netgraph_init, SI_SUB_NETGRAPH, SI_ORDER_FIRST, vnet_netgraph_init, NULL); #ifdef VIMAGE static void vnet_netgraph_uninit(const void *unused __unused) { node_p node = NULL, last_killed = NULL; int i; do { /* Find a node to kill */ IDHASH_RLOCK(); for (i = 0; i <= V_ng_ID_hmask; i++) { LIST_FOREACH(node, &V_ng_ID_hash[i], nd_idnodes) { if (node != &ng_deadnode) { NG_NODE_REF(node); break; } } if (node != NULL) break; } IDHASH_RUNLOCK(); /* Attempt to kill it only if it is a regular node */ if (node != NULL) { if (node == last_killed) { /* This should never happen */ printf("ng node %s needs NGF_REALLY_DIE\n", node->nd_name); if (node->nd_flags & NGF_REALLY_DIE) panic("ng node %s won't die", node->nd_name); node->nd_flags |= NGF_REALLY_DIE; } ng_rmnode(node, NULL, NULL, 0); NG_NODE_UNREF(node); last_killed = node; } } while (node != NULL); hashdestroy(V_ng_name_hash, M_NETGRAPH_NODE, V_ng_name_hmask); hashdestroy(V_ng_ID_hash, M_NETGRAPH_NODE, V_ng_ID_hmask); } VNET_SYSUNINIT(vnet_netgraph_uninit, SI_SUB_NETGRAPH, SI_ORDER_FIRST, vnet_netgraph_uninit, NULL); #endif /* VIMAGE */ /* * Handle loading and unloading for this code. * The only thing we need to link into is the NETISR strucure. */ static int ngb_mod_event(module_t mod, int event, void *data) { struct proc *p; struct thread *td; int i, error = 0; switch (event) { case MOD_LOAD: /* Initialize everything. */ NG_WORKLIST_LOCK_INIT(); rw_init(&ng_typelist_lock, "netgraph types"); rw_init(&ng_idhash_lock, "netgraph idhash"); rw_init(&ng_namehash_lock, "netgraph namehash"); rw_init(&ng_topo_lock, "netgraph topology mutex"); #ifdef NETGRAPH_DEBUG mtx_init(&ng_nodelist_mtx, "netgraph nodelist mutex", NULL, MTX_DEF); mtx_init(&ngq_mtx, "netgraph item list mutex", NULL, MTX_DEF); #endif ng_qzone = uma_zcreate("NetGraph items", sizeof(struct ng_item), NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0); uma_zone_set_max(ng_qzone, maxalloc); ng_qdzone = uma_zcreate("NetGraph data items", sizeof(struct ng_item), NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0); uma_zone_set_max(ng_qdzone, maxdata); /* Autoconfigure number of threads. */ if (numthreads <= 0) numthreads = mp_ncpus; /* Create threads. */ p = NULL; /* start with no process */ for (i = 0; i < numthreads; i++) { if (kproc_kthread_add(ngthread, NULL, &p, &td, RFHIGHPID, 0, "ng_queue", "ng_queue%d", i)) { numthreads = i; break; } } break; case MOD_UNLOAD: /* You can't unload it because an interface may be using it. */ error = EBUSY; break; default: error = EOPNOTSUPP; break; } return (error); } static moduledata_t netgraph_mod = { "netgraph", ngb_mod_event, (NULL) }; DECLARE_MODULE(netgraph, netgraph_mod, SI_SUB_NETGRAPH, SI_ORDER_FIRST); SYSCTL_NODE(_net, OID_AUTO, graph, CTLFLAG_RW, 0, "netgraph Family"); SYSCTL_INT(_net_graph, OID_AUTO, abi_version, CTLFLAG_RD, SYSCTL_NULL_INT_PTR, NG_ABI_VERSION,""); SYSCTL_INT(_net_graph, OID_AUTO, msg_version, CTLFLAG_RD, SYSCTL_NULL_INT_PTR, NG_VERSION, ""); #ifdef NETGRAPH_DEBUG void dumphook (hook_p hook, char *file, int line) { printf("hook: name %s, %d refs, Last touched:\n", _NG_HOOK_NAME(hook), hook->hk_refs); printf(" Last active @ %s, line %d\n", hook->lastfile, hook->lastline); if (line) { printf(" problem discovered at file %s, line %d\n", file, line); #ifdef KDB kdb_backtrace(); #endif } } void dumpnode(node_p node, char *file, int line) { printf("node: ID [%x]: type '%s', %d hooks, flags 0x%x, %d refs, %s:\n", _NG_NODE_ID(node), node->nd_type->name, node->nd_numhooks, node->nd_flags, node->nd_refs, node->nd_name); printf(" Last active @ %s, line %d\n", node->lastfile, node->lastline); if (line) { printf(" problem discovered at file %s, line %d\n", file, line); #ifdef KDB kdb_backtrace(); #endif } } void dumpitem(item_p item, char *file, int line) { printf(" ACTIVE item, last used at %s, line %d", item->lastfile, item->lastline); switch(item->el_flags & NGQF_TYPE) { case NGQF_DATA: printf(" - [data]\n"); break; case NGQF_MESG: printf(" - retaddr[%d]:\n", _NGI_RETADDR(item)); break; case NGQF_FN: printf(" - fn@%p (%p, %p, %p, %d (%x))\n", _NGI_FN(item), _NGI_NODE(item), _NGI_HOOK(item), item->body.fn.fn_arg1, item->body.fn.fn_arg2, item->body.fn.fn_arg2); break; case NGQF_FN2: printf(" - fn2@%p (%p, %p, %p, %d (%x))\n", _NGI_FN2(item), _NGI_NODE(item), _NGI_HOOK(item), item->body.fn.fn_arg1, item->body.fn.fn_arg2, item->body.fn.fn_arg2); break; } if (line) { printf(" problem discovered at file %s, line %d\n", file, line); if (_NGI_NODE(item)) { printf("node %p ([%x])\n", _NGI_NODE(item), ng_node2ID(_NGI_NODE(item))); } } } static void ng_dumpitems(void) { item_p item; int i = 1; TAILQ_FOREACH(item, &ng_itemlist, all) { printf("[%d] ", i++); dumpitem(item, NULL, 0); } } static void ng_dumpnodes(void) { node_p node; int i = 1; mtx_lock(&ng_nodelist_mtx); SLIST_FOREACH(node, &ng_allnodes, nd_all) { printf("[%d] ", i++); dumpnode(node, NULL, 0); } mtx_unlock(&ng_nodelist_mtx); } static void ng_dumphooks(void) { hook_p hook; int i = 1; mtx_lock(&ng_nodelist_mtx); SLIST_FOREACH(hook, &ng_allhooks, hk_all) { printf("[%d] ", i++); dumphook(hook, NULL, 0); } mtx_unlock(&ng_nodelist_mtx); } static int sysctl_debug_ng_dump_items(SYSCTL_HANDLER_ARGS) { int error; int val; int i; val = allocated; i = 1; error = sysctl_handle_int(oidp, &val, 0, req); if (error != 0 || req->newptr == NULL) return (error); if (val == 42) { ng_dumpitems(); ng_dumpnodes(); ng_dumphooks(); } return (0); } SYSCTL_PROC(_debug, OID_AUTO, ng_dump_items, CTLTYPE_INT | CTLFLAG_RW, 0, sizeof(int), sysctl_debug_ng_dump_items, "I", "Number of allocated items"); #endif /* NETGRAPH_DEBUG */ /*********************************************************************** * Worklist routines **********************************************************************/ /* * Pick a node off the list of nodes with work, * try get an item to process off it. Remove the node from the list. */ static void ngthread(void *arg) { for (;;) { node_p node; /* Get node from the worklist. */ NG_WORKLIST_LOCK(); while ((node = STAILQ_FIRST(&ng_worklist)) == NULL) NG_WORKLIST_SLEEP(); STAILQ_REMOVE_HEAD(&ng_worklist, nd_input_queue.q_work); NG_WORKLIST_UNLOCK(); CURVNET_SET(node->nd_vnet); CTR3(KTR_NET, "%20s: node [%x] (%p) taken off worklist", __func__, node->nd_ID, node); /* * We have the node. We also take over the reference * that the list had on it. * Now process as much as you can, until it won't * let you have another item off the queue. * All this time, keep the reference * that lets us be sure that the node still exists. * Let the reference go at the last minute. */ for (;;) { item_p item; int rw; NG_QUEUE_LOCK(&node->nd_input_queue); item = ng_dequeue(node, &rw); if (item == NULL) { node->nd_input_queue.q_flags2 &= ~NGQ2_WORKQ; NG_QUEUE_UNLOCK(&node->nd_input_queue); break; /* go look for another node */ } else { NG_QUEUE_UNLOCK(&node->nd_input_queue); NGI_GET_NODE(item, node); /* zaps stored node */ ng_apply_item(node, item, rw); NG_NODE_UNREF(node); } } NG_NODE_UNREF(node); CURVNET_RESTORE(); } } /* * XXX * It's posible that a debugging NG_NODE_REF may need * to be outside the mutex zone */ static void ng_worklist_add(node_p node) { mtx_assert(&node->nd_input_queue.q_mtx, MA_OWNED); if ((node->nd_input_queue.q_flags2 & NGQ2_WORKQ) == 0) { /* * If we are not already on the work queue, * then put us on. */ node->nd_input_queue.q_flags2 |= NGQ2_WORKQ; NG_NODE_REF(node); /* XXX safe in mutex? */ NG_WORKLIST_LOCK(); STAILQ_INSERT_TAIL(&ng_worklist, node, nd_input_queue.q_work); NG_WORKLIST_UNLOCK(); CTR3(KTR_NET, "%20s: node [%x] (%p) put on worklist", __func__, node->nd_ID, node); NG_WORKLIST_WAKEUP(); } else { CTR3(KTR_NET, "%20s: node [%x] (%p) already on worklist", __func__, node->nd_ID, node); } } /*********************************************************************** * Externally useable functions to set up a queue item ready for sending ***********************************************************************/ #ifdef NETGRAPH_DEBUG #define ITEM_DEBUG_CHECKS \ do { \ if (NGI_NODE(item) ) { \ printf("item already has node"); \ kdb_enter(KDB_WHY_NETGRAPH, "has node"); \ NGI_CLR_NODE(item); \ } \ if (NGI_HOOK(item) ) { \ printf("item already has hook"); \ kdb_enter(KDB_WHY_NETGRAPH, "has hook"); \ NGI_CLR_HOOK(item); \ } \ } while (0) #else #define ITEM_DEBUG_CHECKS #endif /* * Put mbuf into the item. * Hook and node references will be removed when the item is dequeued. * (or equivalent) * (XXX) Unsafe because no reference held by peer on remote node. * remote node might go away in this timescale. * We know the hooks can't go away because that would require getting * a writer item on both nodes and we must have at least a reader * here to be able to do this. * Note that the hook loaded is the REMOTE hook. * * This is possibly in the critical path for new data. */ item_p ng_package_data(struct mbuf *m, int flags) { item_p item; if ((item = ng_alloc_item(NGQF_DATA, flags)) == NULL) { NG_FREE_M(m); return (NULL); } ITEM_DEBUG_CHECKS; item->el_flags |= NGQF_READER; NGI_M(item) = m; return (item); } /* * Allocate a queue item and put items into it.. * Evaluate the address as this will be needed to queue it and * to work out what some of the fields should be. * Hook and node references will be removed when the item is dequeued. * (or equivalent) */ item_p ng_package_msg(struct ng_mesg *msg, int flags) { item_p item; if ((item = ng_alloc_item(NGQF_MESG, flags)) == NULL) { NG_FREE_MSG(msg); return (NULL); } ITEM_DEBUG_CHECKS; /* Messages items count as writers unless explicitly exempted. */ if (msg->header.cmd & NGM_READONLY) item->el_flags |= NGQF_READER; else item->el_flags |= NGQF_WRITER; /* * Set the current lasthook into the queue item */ NGI_MSG(item) = msg; NGI_RETADDR(item) = 0; return (item); } #define SET_RETADDR(item, here, retaddr) \ do { /* Data or fn items don't have retaddrs */ \ if ((item->el_flags & NGQF_TYPE) == NGQF_MESG) { \ if (retaddr) { \ NGI_RETADDR(item) = retaddr; \ } else { \ /* \ * The old return address should be ok. \ * If there isn't one, use the address \ * here. \ */ \ if (NGI_RETADDR(item) == 0) { \ NGI_RETADDR(item) \ = ng_node2ID(here); \ } \ } \ } \ } while (0) int ng_address_hook(node_p here, item_p item, hook_p hook, ng_ID_t retaddr) { hook_p peer; node_p peernode; ITEM_DEBUG_CHECKS; /* * Quick sanity check.. * Since a hook holds a reference on it's node, once we know * that the peer is still connected (even if invalid,) we know * that the peer node is present, though maybe invalid. */ TOPOLOGY_RLOCK(); if ((hook == NULL) || NG_HOOK_NOT_VALID(hook) || NG_HOOK_NOT_VALID(peer = NG_HOOK_PEER(hook)) || NG_NODE_NOT_VALID(peernode = NG_PEER_NODE(hook))) { NG_FREE_ITEM(item); TRAP_ERROR(); TOPOLOGY_RUNLOCK(); return (ENETDOWN); } /* * Transfer our interest to the other (peer) end. */ NG_HOOK_REF(peer); NG_NODE_REF(peernode); NGI_SET_HOOK(item, peer); NGI_SET_NODE(item, peernode); SET_RETADDR(item, here, retaddr); TOPOLOGY_RUNLOCK(); return (0); } int ng_address_path(node_p here, item_p item, const char *address, ng_ID_t retaddr) { node_p dest = NULL; hook_p hook = NULL; int error; ITEM_DEBUG_CHECKS; /* * Note that ng_path2noderef increments the reference count * on the node for us if it finds one. So we don't have to. */ error = ng_path2noderef(here, address, &dest, &hook); if (error) { NG_FREE_ITEM(item); return (error); } NGI_SET_NODE(item, dest); if (hook) NGI_SET_HOOK(item, hook); SET_RETADDR(item, here, retaddr); return (0); } int ng_address_ID(node_p here, item_p item, ng_ID_t ID, ng_ID_t retaddr) { node_p dest; ITEM_DEBUG_CHECKS; /* * Find the target node. */ dest = ng_ID2noderef(ID); /* GETS REFERENCE! */ if (dest == NULL) { NG_FREE_ITEM(item); TRAP_ERROR(); return(EINVAL); } /* Fill out the contents */ NGI_SET_NODE(item, dest); NGI_CLR_HOOK(item); SET_RETADDR(item, here, retaddr); return (0); } /* * special case to send a message to self (e.g. destroy node) * Possibly indicate an arrival hook too. * Useful for removing that hook :-) */ item_p ng_package_msg_self(node_p here, hook_p hook, struct ng_mesg *msg) { item_p item; /* * Find the target node. * If there is a HOOK argument, then use that in preference * to the address. */ if ((item = ng_alloc_item(NGQF_MESG, NG_NOFLAGS)) == NULL) { NG_FREE_MSG(msg); return (NULL); } /* Fill out the contents */ item->el_flags |= NGQF_WRITER; NG_NODE_REF(here); NGI_SET_NODE(item, here); if (hook) { NG_HOOK_REF(hook); NGI_SET_HOOK(item, hook); } NGI_MSG(item) = msg; NGI_RETADDR(item) = ng_node2ID(here); return (item); } /* * Send ng_item_fn function call to the specified node. */ int ng_send_fn(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2) { return ng_send_fn1(node, hook, fn, arg1, arg2, NG_NOFLAGS); } int ng_send_fn1(node_p node, hook_p hook, ng_item_fn *fn, void * arg1, int arg2, int flags) { item_p item; if ((item = ng_alloc_item(NGQF_FN, flags)) == NULL) { return (ENOMEM); } item->el_flags |= NGQF_WRITER; NG_NODE_REF(node); /* and one for the item */ NGI_SET_NODE(item, node); if (hook) { NG_HOOK_REF(hook); NGI_SET_HOOK(item, hook); } NGI_FN(item) = fn; NGI_ARG1(item) = arg1; NGI_ARG2(item) = arg2; return(ng_snd_item(item, flags)); } /* * Send ng_item_fn2 function call to the specified node. * * If an optional pitem parameter is supplied, its apply * callback will be copied to the new item. If also NG_REUSE_ITEM * flag is set, no new item will be allocated, but pitem will * be used. */ int ng_send_fn2(node_p node, hook_p hook, item_p pitem, ng_item_fn2 *fn, void *arg1, int arg2, int flags) { item_p item; KASSERT((pitem != NULL || (flags & NG_REUSE_ITEM) == 0), ("%s: NG_REUSE_ITEM but no pitem", __func__)); /* * Allocate a new item if no supplied or * if we can't use supplied one. */ if (pitem == NULL || (flags & NG_REUSE_ITEM) == 0) { if ((item = ng_alloc_item(NGQF_FN2, flags)) == NULL) return (ENOMEM); if (pitem != NULL) item->apply = pitem->apply; } else { if ((item = ng_realloc_item(pitem, NGQF_FN2, flags)) == NULL) return (ENOMEM); } item->el_flags = (item->el_flags & ~NGQF_RW) | NGQF_WRITER; NG_NODE_REF(node); /* and one for the item */ NGI_SET_NODE(item, node); if (hook) { NG_HOOK_REF(hook); NGI_SET_HOOK(item, hook); } NGI_FN2(item) = fn; NGI_ARG1(item) = arg1; NGI_ARG2(item) = arg2; return(ng_snd_item(item, flags)); } /* * Official timeout routines for Netgraph nodes. */ static void ng_callout_trampoline(void *arg) { item_p item = arg; CURVNET_SET(NGI_NODE(item)->nd_vnet); ng_snd_item(item, 0); CURVNET_RESTORE(); } int ng_callout(struct callout *c, node_p node, hook_p hook, int ticks, ng_item_fn *fn, void * arg1, int arg2) { item_p item, oitem; if ((item = ng_alloc_item(NGQF_FN, NG_NOFLAGS)) == NULL) return (ENOMEM); item->el_flags |= NGQF_WRITER; NG_NODE_REF(node); /* and one for the item */ NGI_SET_NODE(item, node); if (hook) { NG_HOOK_REF(hook); NGI_SET_HOOK(item, hook); } NGI_FN(item) = fn; NGI_ARG1(item) = arg1; NGI_ARG2(item) = arg2; oitem = c->c_arg; if (callout_reset(c, ticks, &ng_callout_trampoline, item) == 1 && oitem != NULL) NG_FREE_ITEM(oitem); return (0); } /* A special modified version of untimeout() */ int ng_uncallout(struct callout *c, node_p node) { item_p item; int rval; KASSERT(c != NULL, ("ng_uncallout: NULL callout")); KASSERT(node != NULL, ("ng_uncallout: NULL node")); rval = callout_stop(c); item = c->c_arg; /* Do an extra check */ if ((rval > 0) && (c->c_func == &ng_callout_trampoline) && (NGI_NODE(item) == node)) { /* * We successfully removed it from the queue before it ran * So now we need to unreference everything that was * given extra references. (NG_FREE_ITEM does this). */ NG_FREE_ITEM(item); } c->c_arg = NULL; return (rval); } /* * Set the address, if none given, give the node here. */ void ng_replace_retaddr(node_p here, item_p item, ng_ID_t retaddr) { if (retaddr) { NGI_RETADDR(item) = retaddr; } else { /* * The old return address should be ok. * If there isn't one, use the address here. */ NGI_RETADDR(item) = ng_node2ID(here); } } Index: projects/building-blocks/sys/ofed/include/linux/gfp.h =================================================================== --- projects/building-blocks/sys/ofed/include/linux/gfp.h (revision 278776) +++ projects/building-blocks/sys/ofed/include/linux/gfp.h (revision 278777) @@ -1,131 +1,148 @@ /*- * Copyright (c) 2010 Isilon Systems, Inc. * Copyright (c) 2010 iX Systems, Inc. * Copyright (c) 2010 Panasas, Inc. * Copyright (c) 2013 Mellanox Technologies, Ltd. * 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 unmodified, 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. */ #ifndef _LINUX_GFP_H_ #define _LINUX_GFP_H_ #include #include #include #include #include #include #include #include #include #define __GFP_NOWARN 0 #define __GFP_HIGHMEM 0 #define __GFP_ZERO M_ZERO #define GFP_NOWAIT M_NOWAIT #define GFP_ATOMIC (M_NOWAIT | M_USE_RESERVE) #define GFP_KERNEL M_WAITOK #define GFP_USER M_WAITOK #define GFP_HIGHUSER M_WAITOK #define GFP_HIGHUSER_MOVABLE M_WAITOK #define GFP_IOFS M_NOWAIT static inline void * page_address(struct page *page) { if (page->object != kmem_object && page->object != kernel_object) return (NULL); return ((void *)(uintptr_t)(VM_MIN_KERNEL_ADDRESS + IDX_TO_OFF(page->pindex))); } static inline unsigned long _get_page(gfp_t mask) { return kmem_malloc(kmem_arena, PAGE_SIZE, mask); } #define get_zeroed_page(mask) _get_page((mask) | M_ZERO) #define alloc_page(mask) virt_to_page(_get_page((mask))) #define __get_free_page(mask) _get_page((mask)) static inline void free_page(unsigned long page) { if (page == 0) return; kmem_free(kmem_arena, page, PAGE_SIZE); } static inline void __free_page(struct page *m) { if (m->object != kmem_object) panic("__free_page: Freed page %p not allocated via wrappers.", m); kmem_free(kmem_arena, (vm_offset_t)page_address(m), PAGE_SIZE); } static inline void __free_pages(struct page *m, unsigned int order) { size_t size; if (m == NULL) return; size = PAGE_SIZE << order; kmem_free(kmem_arena, (vm_offset_t)page_address(m), size); } +static inline void free_pages(uintptr_t addr, unsigned int order) +{ + if (addr == 0) + return; + __free_pages(virt_to_page((void *)addr), order); +} + /* * Alloc pages allocates directly from the buddy allocator on linux so * order specifies a power of two bucket of pages and the results * are expected to be aligned on the size as well. */ static inline struct page * alloc_pages(gfp_t gfp_mask, unsigned int order) { unsigned long page; size_t size; size = PAGE_SIZE << order; page = kmem_alloc_contig(kmem_arena, size, gfp_mask, 0, -1, size, 0, VM_MEMATTR_DEFAULT); if (page == 0) return (NULL); return (virt_to_page(page)); +} + +static inline uintptr_t __get_free_pages(gfp_t gfp_mask, unsigned int order) +{ + struct page *page; + + page = alloc_pages(gfp_mask, order); + if (page == NULL) + return (0); + return ((uintptr_t)page_address(page)); } #define alloc_pages_node(node, mask, order) alloc_pages(mask, order) #define kmalloc_node(chunk, mask, node) kmalloc(chunk, mask) #endif /* _LINUX_GFP_H_ */ Index: projects/building-blocks/sys/ofed/include/linux/kernel.h =================================================================== --- projects/building-blocks/sys/ofed/include/linux/kernel.h (revision 278776) +++ projects/building-blocks/sys/ofed/include/linux/kernel.h (revision 278777) @@ -1,183 +1,185 @@ /*- * Copyright (c) 2010 Isilon Systems, Inc. * Copyright (c) 2010 iX Systems, Inc. * Copyright (c) 2010 Panasas, Inc. * Copyright (c) 2013, 2014 Mellanox Technologies, Ltd. * 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 unmodified, 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. */ #ifndef _LINUX_KERNEL_H_ #define _LINUX_KERNEL_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define KERN_CONT "" #define KERN_EMERG "<0>" #define KERN_ALERT "<1>" #define KERN_CRIT "<2>" #define KERN_ERR "<3>" #define KERN_WARNING "<4>" #define KERN_NOTICE "<5>" #define KERN_INFO "<6>" #define KERN_DEBUG "<7>" #define BUILD_BUG_ON(x) CTASSERT(x) #define BUG() panic("BUG") #define BUG_ON(condition) do { if (condition) BUG(); } while(0) #define WARN_ON BUG_ON #undef ALIGN #define ALIGN(x, y) roundup2((x), (y)) #define DIV_ROUND_UP howmany +#define FIELD_SIZEOF(t, f) sizeof(((t *)0)->f) #define printk(X...) printf(X) /* * The "pr_debug()" and "pr_devel()" macros should produce zero code * unless DEBUG is defined: */ #ifdef DEBUG #define pr_debug(fmt, ...) \ log(LOG_DEBUG, fmt, ##__VA_ARGS__) #define pr_devel(fmt, ...) \ log(LOG_DEBUG, pr_fmt(fmt), ##__VA_ARGS__) #else #define pr_debug(fmt, ...) \ ({ if (0) log(LOG_DEBUG, fmt, ##__VA_ARGS__); 0; }) #define pr_devel(fmt, ...) \ ({ if (0) log(LOG_DEBUG, pr_fmt(fmt), ##__VA_ARGS__); 0; }) #endif #define udelay(t) DELAY(t) #define usleep_range(min,max) DELAY(min) #ifndef pr_fmt #define pr_fmt(fmt) fmt #endif /* * Print a one-time message (analogous to WARN_ONCE() et al): */ #define printk_once(...) do { \ static bool __print_once; \ \ if (!__print_once) { \ __print_once = true; \ printk(__VA_ARGS__); \ } \ } while (0) /* * Log a one-time message (analogous to WARN_ONCE() et al): */ #define log_once(level,...) do { \ static bool __log_once; \ \ if (!__log_once) { \ __log_once = true; \ log(level, __VA_ARGS__); \ } \ } while (0) #define pr_emerg(fmt, ...) \ log(LOG_EMERG, pr_fmt(fmt), ##__VA_ARGS__) #define pr_alert(fmt, ...) \ log(LOG_ALERT, pr_fmt(fmt), ##__VA_ARGS__) #define pr_crit(fmt, ...) \ log(LOG_CRIT, pr_fmt(fmt), ##__VA_ARGS__) #define pr_err(fmt, ...) \ log(LOG_ERR, pr_fmt(fmt), ##__VA_ARGS__) #define pr_warning(fmt, ...) \ log(LOG_WARNING, pr_fmt(fmt), ##__VA_ARGS__) #define pr_warn pr_warning #define pr_notice(fmt, ...) \ log(LOG_NOTICE, pr_fmt(fmt), ##__VA_ARGS__) #define pr_info(fmt, ...) \ log(LOG_INFO, pr_fmt(fmt), ##__VA_ARGS__) #define pr_info_once(fmt, ...) \ log_once(LOG_INFO, pr_fmt(fmt), ##__VA_ARGS__) #define pr_cont(fmt, ...) \ printk(KERN_CONT fmt, ##__VA_ARGS__) #ifndef WARN #define WARN(condition, format...) ({ \ int __ret_warn_on = !!(condition); \ if (unlikely(__ret_warn_on)) \ pr_warning(format); \ unlikely(__ret_warn_on); \ }) #endif #define container_of(ptr, type, member) \ ({ \ __typeof(((type *)0)->member) *_p = (ptr); \ (type *)((char *)_p - offsetof(type, member)); \ }) #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #define simple_strtoul strtoul #define simple_strtol strtol #define kstrtol(a,b,c) ({*(c) = strtol(a,0,b);}) #define min(x, y) ((x) < (y) ? (x) : (y)) #define max(x, y) ((x) > (y) ? (x) : (y)) #define min_t(type, _x, _y) ((type)(_x) < (type)(_y) ? (type)(_x) : (type)(_y)) #define max_t(type, _x, _y) ((type)(_x) > (type)(_y) ? (type)(_x) : (type)(_y)) /* * This looks more complex than it should be. But we need to * get the type for the ~ right in round_down (it needs to be * as wide as the result!), and we want to evaluate the macro * arguments just once each. */ #define __round_mask(x, y) ((__typeof__(x))((y)-1)) #define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1) #define round_down(x, y) ((x) & ~__round_mask(x, y)) #define num_possible_cpus() mp_ncpus +#define num_online_cpus() mp_ncpus typedef struct pm_message { int event; } pm_message_t; #endif /* _LINUX_KERNEL_H_ */ Index: projects/building-blocks/sys/ofed/include/linux/kref.h =================================================================== --- projects/building-blocks/sys/ofed/include/linux/kref.h (revision 278776) +++ projects/building-blocks/sys/ofed/include/linux/kref.h (revision 278777) @@ -1,63 +1,64 @@ /*- * Copyright (c) 2010 Isilon Systems, Inc. * Copyright (c) 2010 iX Systems, Inc. * Copyright (c) 2010 Panasas, Inc. * Copyright (c) 2013, 2014 Mellanox Technologies, Ltd. * 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 unmodified, 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. */ #ifndef _LINUX_KREF_H_ #define _LINUX_KREF_H_ +#include #include struct kref { volatile u_int count; }; static inline void kref_init(struct kref *kref) { refcount_init(&kref->count, 1); } static inline void kref_get(struct kref *kref) { refcount_acquire(&kref->count); } static inline int kref_put(struct kref *kref, void (*rel)(struct kref *kref)) { if (refcount_release(&kref->count)) { rel(kref); return 1; } return 0; } #endif /* _LINUX_KREF_H_ */ Index: projects/building-blocks/sys/ofed/include/linux/pci.h =================================================================== --- projects/building-blocks/sys/ofed/include/linux/pci.h (revision 278776) +++ projects/building-blocks/sys/ofed/include/linux/pci.h (revision 278777) @@ -1,702 +1,734 @@ /*- * Copyright (c) 2010 Isilon Systems, Inc. * Copyright (c) 2010 iX Systems, Inc. * Copyright (c) 2010 Panasas, Inc. * Copyright (c) 2013, 2014 Mellanox Technologies, Ltd. * 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 unmodified, 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. */ #ifndef _LINUX_PCI_H_ #define _LINUX_PCI_H_ #define CONFIG_PCI_MSI #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct pci_device_id { uint32_t vendor; uint32_t device; uint32_t subvendor; uint32_t subdevice; uint32_t class_mask; uintptr_t driver_data; }; #define MODULE_DEVICE_TABLE(bus, table) #define PCI_ANY_ID (-1) #define PCI_VENDOR_ID_MELLANOX 0x15b3 #define PCI_VENDOR_ID_TOPSPIN 0x1867 #define PCI_DEVICE_ID_MELLANOX_TAVOR 0x5a44 #define PCI_DEVICE_ID_MELLANOX_TAVOR_BRIDGE 0x5a46 #define PCI_DEVICE_ID_MELLANOX_ARBEL_COMPAT 0x6278 #define PCI_DEVICE_ID_MELLANOX_ARBEL 0x6282 #define PCI_DEVICE_ID_MELLANOX_SINAI_OLD 0x5e8c #define PCI_DEVICE_ID_MELLANOX_SINAI 0x6274 #define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) #define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f) #define PCI_FUNC(devfn) ((devfn) & 0x07) #define PCI_VDEVICE(_vendor, _device) \ .vendor = PCI_VENDOR_ID_##_vendor, .device = (_device), \ .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID #define PCI_DEVICE(_vendor, _device) \ .vendor = (_vendor), .device = (_device), \ .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID #define to_pci_dev(n) container_of(n, struct pci_dev, dev) #define PCI_VENDOR_ID PCIR_DEVVENDOR #define PCI_COMMAND PCIR_COMMAND #define PCI_EXP_DEVCTL PCIER_DEVICE_CTL /* Device Control */ #define PCI_EXP_LNKCTL PCIER_LINK_CTL /* Link Control */ #define PCI_EXP_FLAGS_TYPE PCIEM_FLAGS_TYPE /* Device/Port type */ #define PCI_EXP_DEVCAP PCIER_DEVICE_CAP /* Device capabilities */ #define PCI_EXP_DEVSTA PCIER_DEVICE_STA /* Device Status */ #define PCI_EXP_LNKCAP PCIER_LINK_CAP /* Link Capabilities */ #define PCI_EXP_LNKSTA PCIER_LINK_STA /* Link Status */ #define PCI_EXP_SLTCAP PCIER_SLOT_CAP /* Slot Capabilities */ #define PCI_EXP_SLTCTL PCIER_SLOT_CTL /* Slot Control */ #define PCI_EXP_SLTSTA PCIER_SLOT_STA /* Slot Status */ #define PCI_EXP_RTCTL PCIER_ROOT_CTL /* Root Control */ #define PCI_EXP_RTCAP PCIER_ROOT_CAP /* Root Capabilities */ #define PCI_EXP_RTSTA PCIER_ROOT_STA /* Root Status */ #define PCI_EXP_DEVCAP2 PCIER_DEVICE_CAP2 /* Device Capabilities 2 */ #define PCI_EXP_DEVCTL2 PCIER_DEVICE_CTL2 /* Device Control 2 */ #define PCI_EXP_LNKCAP2 PCIER_LINK_CAP2 /* Link Capabilities 2 */ #define PCI_EXP_LNKCTL2 PCIER_LINK_CTL2 /* Link Control 2 */ #define PCI_EXP_LNKSTA2 PCIER_LINK_STA2 /* Link Status 2 */ #define PCI_EXP_FLAGS PCIER_FLAGS /* Capabilities register */ #define PCI_EXP_FLAGS_VERS PCIEM_FLAGS_VERSION /* Capability version */ #define PCI_EXP_TYPE_ROOT_PORT PCIEM_TYPE_ROOT_PORT /* Root Port */ #define PCI_EXP_TYPE_ENDPOINT PCIEM_TYPE_ENDPOINT /* Express Endpoint */ #define PCI_EXP_TYPE_LEG_END PCIEM_TYPE_LEGACY_ENDPOINT /* Legacy Endpoint */ #define PCI_EXP_TYPE_DOWNSTREAM PCIEM_TYPE_DOWNSTREAM_PORT /* Downstream Port */ #define PCI_EXP_FLAGS_SLOT PCIEM_FLAGS_SLOT /* Slot implemented */ #define PCI_EXP_TYPE_RC_EC PCIEM_TYPE_ROOT_EC /* Root Complex Event Collector */ #define IORESOURCE_MEM SYS_RES_MEMORY #define IORESOURCE_IO SYS_RES_IOPORT #define IORESOURCE_IRQ SYS_RES_IRQ struct pci_dev; struct pci_driver { struct list_head links; char *name; const struct pci_device_id *id_table; int (*probe)(struct pci_dev *dev, const struct pci_device_id *id); void (*remove)(struct pci_dev *dev); int (*suspend) (struct pci_dev *dev, pm_message_t state); /* Device suspended */ int (*resume) (struct pci_dev *dev); /* Device woken up */ driver_t driver; devclass_t bsdclass; const struct pci_error_handlers *err_handler; }; extern struct list_head pci_drivers; extern struct list_head pci_devices; extern spinlock_t pci_lock; #define __devexit_p(x) x struct pci_dev { struct device dev; struct list_head links; struct pci_driver *pdrv; uint64_t dma_mask; uint16_t device; uint16_t vendor; unsigned int irq; unsigned int devfn; u8 revision; }; static inline struct resource_list_entry * _pci_get_rle(struct pci_dev *pdev, int type, int rid) { struct pci_devinfo *dinfo; struct resource_list *rl; dinfo = device_get_ivars(pdev->dev.bsddev); rl = &dinfo->resources; return resource_list_find(rl, type, rid); } static inline struct resource_list_entry * _pci_get_bar(struct pci_dev *pdev, int bar) { struct resource_list_entry *rle; bar = PCIR_BAR(bar); if ((rle = _pci_get_rle(pdev, SYS_RES_MEMORY, bar)) == NULL) rle = _pci_get_rle(pdev, SYS_RES_IOPORT, bar); return (rle); } static inline struct device * _pci_find_irq_dev(unsigned int irq) { struct pci_dev *pdev; spin_lock(&pci_lock); list_for_each_entry(pdev, &pci_devices, links) { if (irq == pdev->dev.irq) break; if (irq >= pdev->dev.msix && irq < pdev->dev.msix_max) break; } spin_unlock(&pci_lock); if (pdev) return &pdev->dev; return (NULL); } static inline unsigned long pci_resource_start(struct pci_dev *pdev, int bar) { struct resource_list_entry *rle; if ((rle = _pci_get_bar(pdev, bar)) == NULL) return (0); return rle->start; } static inline unsigned long pci_resource_len(struct pci_dev *pdev, int bar) { struct resource_list_entry *rle; if ((rle = _pci_get_bar(pdev, bar)) == NULL) return (0); return rle->count; } /* * All drivers just seem to want to inspect the type not flags. */ static inline int pci_resource_flags(struct pci_dev *pdev, int bar) { struct resource_list_entry *rle; if ((rle = _pci_get_bar(pdev, bar)) == NULL) return (0); return rle->type; } static inline const char * pci_name(struct pci_dev *d) { return device_get_desc(d->dev.bsddev); } static inline void * pci_get_drvdata(struct pci_dev *pdev) { return dev_get_drvdata(&pdev->dev); } static inline void pci_set_drvdata(struct pci_dev *pdev, void *data) { dev_set_drvdata(&pdev->dev, data); } static inline int pci_enable_device(struct pci_dev *pdev) { pci_enable_io(pdev->dev.bsddev, SYS_RES_IOPORT); pci_enable_io(pdev->dev.bsddev, SYS_RES_MEMORY); return (0); } static inline void pci_disable_device(struct pci_dev *pdev) { } static inline int pci_set_master(struct pci_dev *pdev) { pci_enable_busmaster(pdev->dev.bsddev); return (0); } static inline int +pci_clear_master(struct pci_dev *pdev) +{ + + pci_disable_busmaster(pdev->dev.bsddev); + return (0); +} + +static inline int pci_request_region(struct pci_dev *pdev, int bar, const char *res_name) { int rid; int type; type = pci_resource_flags(pdev, bar); if (type == 0) return (-ENODEV); rid = PCIR_BAR(bar); if (bus_alloc_resource_any(pdev->dev.bsddev, type, &rid, RF_ACTIVE) == NULL) return (-EINVAL); return (0); } static inline void pci_release_region(struct pci_dev *pdev, int bar) { struct resource_list_entry *rle; if ((rle = _pci_get_bar(pdev, bar)) == NULL) return; bus_release_resource(pdev->dev.bsddev, rle->type, rle->rid, rle->res); } static inline void pci_release_regions(struct pci_dev *pdev) { int i; for (i = 0; i <= PCIR_MAX_BAR_0; i++) pci_release_region(pdev, i); } static inline int pci_request_regions(struct pci_dev *pdev, const char *res_name) { int error; int i; for (i = 0; i <= PCIR_MAX_BAR_0; i++) { error = pci_request_region(pdev, i, res_name); if (error && error != -ENODEV) { pci_release_regions(pdev); return (error); } } return (0); } static inline void pci_disable_msix(struct pci_dev *pdev) { pci_release_msi(pdev->dev.bsddev); } #define PCI_CAP_ID_EXP PCIY_EXPRESS #define PCI_CAP_ID_PCIX PCIY_PCIX static inline int pci_find_capability(struct pci_dev *pdev, int capid) { int reg; if (pci_find_cap(pdev->dev.bsddev, capid, ®)) return (0); return (reg); } /** * pci_pcie_cap - get the saved PCIe capability offset * @dev: PCI device * * PCIe capability offset is calculated at PCI device initialization * time and saved in the data structure. This function returns saved * PCIe capability offset. Using this instead of pci_find_capability() * reduces unnecessary search in the PCI configuration space. If you * need to calculate PCIe capability offset from raw device for some * reasons, please use pci_find_capability() instead. */ static inline int pci_pcie_cap(struct pci_dev *dev) { return pci_find_capability(dev, PCI_CAP_ID_EXP); } static inline int pci_read_config_byte(struct pci_dev *pdev, int where, u8 *val) { *val = (u8)pci_read_config(pdev->dev.bsddev, where, 1); return (0); } static inline int pci_read_config_word(struct pci_dev *pdev, int where, u16 *val) { *val = (u16)pci_read_config(pdev->dev.bsddev, where, 2); return (0); } static inline int pci_read_config_dword(struct pci_dev *pdev, int where, u32 *val) { *val = (u32)pci_read_config(pdev->dev.bsddev, where, 4); return (0); } static inline int pci_write_config_byte(struct pci_dev *pdev, int where, u8 val) { pci_write_config(pdev->dev.bsddev, where, val, 1); return (0); } static inline int pci_write_config_word(struct pci_dev *pdev, int where, u16 val) { pci_write_config(pdev->dev.bsddev, where, val, 2); return (0); } static inline int pci_write_config_dword(struct pci_dev *pdev, int where, u32 val) { pci_write_config(pdev->dev.bsddev, where, val, 4); return (0); } extern int pci_register_driver(struct pci_driver *pdrv); extern void pci_unregister_driver(struct pci_driver *pdrv); struct msix_entry { int entry; int vector; }; /* * Enable msix, positive errors indicate actual number of available * vectors. Negative errors are failures. * * NB: define added to prevent this definition of pci_enable_msix from * clashing with the native FreeBSD version. */ #define pci_enable_msix linux_pci_enable_msix static inline int pci_enable_msix(struct pci_dev *pdev, struct msix_entry *entries, int nreq) { struct resource_list_entry *rle; int error; int avail; int i; avail = pci_msix_count(pdev->dev.bsddev); if (avail < nreq) { if (avail == 0) return -EINVAL; return avail; } avail = nreq; if ((error = -pci_alloc_msix(pdev->dev.bsddev, &avail)) != 0) return error; /* * Handle case where "pci_alloc_msix()" may allocate less * interrupts than available and return with no error: */ if (avail < nreq) { pci_release_msi(pdev->dev.bsddev); return avail; } rle = _pci_get_rle(pdev, SYS_RES_IRQ, 1); pdev->dev.msix = rle->start; pdev->dev.msix_max = rle->start + avail; for (i = 0; i < nreq; i++) entries[i].vector = pdev->dev.msix + i; return (0); +} + +#define pci_enable_msix_range linux_pci_enable_msix_range +static inline int +pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries, + int minvec, int maxvec) +{ + int nvec = maxvec; + int rc; + + if (maxvec < minvec) + return (-ERANGE); + + do { + rc = pci_enable_msix(dev, entries, nvec); + if (rc < 0) { + return (rc); + } else if (rc > 0) { + if (rc < minvec) + return (-ENOSPC); + nvec = rc; + } + } while (rc); + return (nvec); } static inline int pci_channel_offline(struct pci_dev *pdev) { return false; } static inline int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn) { return -ENODEV; } static inline void pci_disable_sriov(struct pci_dev *dev) { } /** * DEFINE_PCI_DEVICE_TABLE - macro used to describe a pci device table * @_table: device table name * * This macro is used to create a struct pci_device_id array (a device table) * in a generic manner. */ #define DEFINE_PCI_DEVICE_TABLE(_table) \ const struct pci_device_id _table[] __devinitdata /* XXX This should not be necessary. */ #define pcix_set_mmrbc(d, v) 0 #define pcix_get_max_mmrbc(d) 0 #define pcie_set_readrq(d, v) 0 #define PCI_DMA_BIDIRECTIONAL 0 #define PCI_DMA_TODEVICE 1 #define PCI_DMA_FROMDEVICE 2 #define PCI_DMA_NONE 3 #define pci_pool dma_pool #define pci_pool_destroy dma_pool_destroy #define pci_pool_alloc dma_pool_alloc #define pci_pool_free dma_pool_free #define pci_pool_create(_name, _pdev, _size, _align, _alloc) \ dma_pool_create(_name, &(_pdev)->dev, _size, _align, _alloc) #define pci_free_consistent(_hwdev, _size, _vaddr, _dma_handle) \ dma_free_coherent((_hwdev) == NULL ? NULL : &(_hwdev)->dev, \ _size, _vaddr, _dma_handle) #define pci_map_sg(_hwdev, _sg, _nents, _dir) \ dma_map_sg((_hwdev) == NULL ? NULL : &(_hwdev->dev), \ _sg, _nents, (enum dma_data_direction)_dir) #define pci_map_single(_hwdev, _ptr, _size, _dir) \ dma_map_single((_hwdev) == NULL ? NULL : &(_hwdev->dev), \ (_ptr), (_size), (enum dma_data_direction)_dir) #define pci_unmap_single(_hwdev, _addr, _size, _dir) \ dma_unmap_single((_hwdev) == NULL ? NULL : &(_hwdev)->dev, \ _addr, _size, (enum dma_data_direction)_dir) #define pci_unmap_sg(_hwdev, _sg, _nents, _dir) \ dma_unmap_sg((_hwdev) == NULL ? NULL : &(_hwdev)->dev, \ _sg, _nents, (enum dma_data_direction)_dir) #define pci_map_page(_hwdev, _page, _offset, _size, _dir) \ dma_map_page((_hwdev) == NULL ? NULL : &(_hwdev)->dev, _page,\ _offset, _size, (enum dma_data_direction)_dir) #define pci_unmap_page(_hwdev, _dma_address, _size, _dir) \ dma_unmap_page((_hwdev) == NULL ? NULL : &(_hwdev)->dev, \ _dma_address, _size, (enum dma_data_direction)_dir) #define pci_set_dma_mask(_pdev, mask) dma_set_mask(&(_pdev)->dev, (mask)) #define pci_dma_mapping_error(_pdev, _dma_addr) \ dma_mapping_error(&(_pdev)->dev, _dma_addr) #define pci_set_consistent_dma_mask(_pdev, _mask) \ dma_set_coherent_mask(&(_pdev)->dev, (_mask)) #define DECLARE_PCI_UNMAP_ADDR(x) DEFINE_DMA_UNMAP_ADDR(x); #define DECLARE_PCI_UNMAP_LEN(x) DEFINE_DMA_UNMAP_LEN(x); #define pci_unmap_addr dma_unmap_addr #define pci_unmap_addr_set dma_unmap_addr_set #define pci_unmap_len dma_unmap_len #define pci_unmap_len_set dma_unmap_len_set typedef unsigned int __bitwise pci_channel_state_t; typedef unsigned int __bitwise pci_ers_result_t; enum pci_channel_state { /* I/O channel is in normal state */ pci_channel_io_normal = (__force pci_channel_state_t) 1, /* I/O to channel is blocked */ pci_channel_io_frozen = (__force pci_channel_state_t) 2, /* PCI card is dead */ pci_channel_io_perm_failure = (__force pci_channel_state_t) 3, }; enum pci_ers_result { /* no result/none/not supported in device driver */ PCI_ERS_RESULT_NONE = (__force pci_ers_result_t) 1, /* Device driver can recover without slot reset */ PCI_ERS_RESULT_CAN_RECOVER = (__force pci_ers_result_t) 2, /* Device driver wants slot to be reset. */ PCI_ERS_RESULT_NEED_RESET = (__force pci_ers_result_t) 3, /* Device has completely failed, is unrecoverable */ PCI_ERS_RESULT_DISCONNECT = (__force pci_ers_result_t) 4, /* Device driver is fully recovered and operational */ PCI_ERS_RESULT_RECOVERED = (__force pci_ers_result_t) 5, }; /* PCI bus error event callbacks */ struct pci_error_handlers { /* PCI bus error detected on this device */ pci_ers_result_t (*error_detected)(struct pci_dev *dev, enum pci_channel_state error); /* MMIO has been re-enabled, but not DMA */ pci_ers_result_t (*mmio_enabled)(struct pci_dev *dev); /* PCI Express link has been reset */ pci_ers_result_t (*link_reset)(struct pci_dev *dev); /* PCI slot has been reset */ pci_ers_result_t (*slot_reset)(struct pci_dev *dev); /* Device driver may resume normal operations */ void (*resume)(struct pci_dev *dev); }; /* freeBSD does not support SRIOV - yet */ static inline struct pci_dev *pci_physfn(struct pci_dev *dev) { return dev; } static inline bool pci_is_pcie(struct pci_dev *dev) { return !!pci_pcie_cap(dev); } static inline u16 pcie_flags_reg(struct pci_dev *dev) { int pos; u16 reg16; pos = pci_find_capability(dev, PCI_CAP_ID_EXP); if (!pos) return 0; pci_read_config_word(dev, pos + PCI_EXP_FLAGS, ®16); return reg16; } static inline int pci_pcie_type(struct pci_dev *dev) { return (pcie_flags_reg(dev) & PCI_EXP_FLAGS_TYPE) >> 4; } static inline int pcie_cap_version(struct pci_dev *dev) { return pcie_flags_reg(dev) & PCI_EXP_FLAGS_VERS; } static inline bool pcie_cap_has_lnkctl(struct pci_dev *dev) { int type = pci_pcie_type(dev); return pcie_cap_version(dev) > 1 || type == PCI_EXP_TYPE_ROOT_PORT || type == PCI_EXP_TYPE_ENDPOINT || type == PCI_EXP_TYPE_LEG_END; } static inline bool pcie_cap_has_devctl(const struct pci_dev *dev) { return true; } static inline bool pcie_cap_has_sltctl(struct pci_dev *dev) { int type = pci_pcie_type(dev); return pcie_cap_version(dev) > 1 || type == PCI_EXP_TYPE_ROOT_PORT || (type == PCI_EXP_TYPE_DOWNSTREAM && pcie_flags_reg(dev) & PCI_EXP_FLAGS_SLOT); } static inline bool pcie_cap_has_rtctl(struct pci_dev *dev) { int type = pci_pcie_type(dev); return pcie_cap_version(dev) > 1 || type == PCI_EXP_TYPE_ROOT_PORT || type == PCI_EXP_TYPE_RC_EC; } static bool pcie_capability_reg_implemented(struct pci_dev *dev, int pos) { if (!pci_is_pcie(dev)) return false; switch (pos) { case PCI_EXP_FLAGS_TYPE: return true; case PCI_EXP_DEVCAP: case PCI_EXP_DEVCTL: case PCI_EXP_DEVSTA: return pcie_cap_has_devctl(dev); case PCI_EXP_LNKCAP: case PCI_EXP_LNKCTL: case PCI_EXP_LNKSTA: return pcie_cap_has_lnkctl(dev); case PCI_EXP_SLTCAP: case PCI_EXP_SLTCTL: case PCI_EXP_SLTSTA: return pcie_cap_has_sltctl(dev); case PCI_EXP_RTCTL: case PCI_EXP_RTCAP: case PCI_EXP_RTSTA: return pcie_cap_has_rtctl(dev); case PCI_EXP_DEVCAP2: case PCI_EXP_DEVCTL2: case PCI_EXP_LNKCAP2: case PCI_EXP_LNKCTL2: case PCI_EXP_LNKSTA2: return pcie_cap_version(dev) > 1; default: return false; } } static inline int pcie_capability_write_word(struct pci_dev *dev, int pos, u16 val) { if (pos & 1) return -EINVAL; if (!pcie_capability_reg_implemented(dev, pos)) return 0; return pci_write_config_word(dev, pci_pcie_cap(dev) + pos, val); } #endif /* _LINUX_PCI_H_ */ Index: projects/building-blocks/sys/sys/disk.h =================================================================== --- projects/building-blocks/sys/sys/disk.h (revision 278776) +++ projects/building-blocks/sys/sys/disk.h (revision 278777) @@ -1,138 +1,139 @@ /*- * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * * $FreeBSD$ * */ #ifndef _SYS_DISK_H_ #define _SYS_DISK_H_ #include #include #ifdef _KERNEL #ifndef _SYS_CONF_H_ #include /* XXX: temporary to avoid breakage */ #endif void disk_err(struct bio *bp, const char *what, int blkdone, int nl); #endif #define DIOCGSECTORSIZE _IOR('d', 128, u_int) /* * Get the sector size of the device in bytes. The sector size is the * smallest unit of data which can be transferred from this device. * Usually this is a power of 2 but it might not be (i.e. CDROM audio). */ #define DIOCGMEDIASIZE _IOR('d', 129, off_t) /* Get media size in bytes */ /* * Get the size of the entire device in bytes. This should be a * multiple of the sector size. */ #define DIOCGFWSECTORS _IOR('d', 130, u_int) /* Get firmware's sectorcount */ /* * Get the firmware's notion of number of sectors per track. This * value is mostly used for compatibility with various ill designed * disk label formats. Don't use it unless you have to. */ #define DIOCGFWHEADS _IOR('d', 131, u_int) /* Get firmware's headcount */ /* * Get the firmwares notion of number of heads per cylinder. This * value is mostly used for compatibility with various ill designed * disk label formats. Don't use it unless you have to. */ #define DIOCSKERNELDUMP _IOW('d', 133, u_int) /* Set/Clear kernel dumps */ /* * Enable/Disable (the argument is boolean) the device for kernel * core dumps. */ #define DIOCGFRONTSTUFF _IOR('d', 134, off_t) /* * Many disk formats have some amount of space reserved at the * start of the disk to hold bootblocks, various disklabels and * similar stuff. This ioctl returns the number of such bytes * which may apply to the device. */ #define DIOCGFLUSH _IO('d', 135) /* Flush write cache */ /* * Flush write cache of the device. */ #define DIOCGDELETE _IOW('d', 136, off_t[2]) /* Delete data */ /* * Mark data on the device as unused. */ #define DISK_IDENT_SIZE 256 #define DIOCGIDENT _IOR('d', 137, char[DISK_IDENT_SIZE]) /*- * Get the ident of the given provider. Ident is (most of the time) * a uniqe and fixed provider's identifier. Ident's properties are as * follow: * - ident value is preserved between reboots, * - provider can be detached/attached and ident is preserved, * - provider's name can change - ident can't, * - ident value should not be based on on-disk metadata; in other * words copying whole data from one disk to another should not * yield the same ident for the other disk, * - there could be more than one provider with the same ident, but * only if they point at exactly the same physical storage, this is * the case for multipathing for example, * - GEOM classes that consumes single providers and provide single * providers, like geli, gbde, should just attach class name to the * ident of the underlying provider, * - ident is an ASCII string (is printable), * - ident is optional and applications can't relay on its presence. */ #define DIOCGPROVIDERNAME _IOR('d', 138, char[MAXPATHLEN]) /* * Store the provider name, given a device path, in a buffer. The buffer * must be at least MAXPATHLEN bytes long. */ #define DIOCGSTRIPESIZE _IOR('d', 139, off_t) /* Get stripe size in bytes */ /* * Get the size of the device's optimal access block in bytes. * This should be a multiple of the sector size. */ #define DIOCGSTRIPEOFFSET _IOR('d', 140, off_t) /* Get stripe offset in bytes */ /* * Get the offset of the first device's optimal access block in bytes. * This should be a multiple of the sector size. */ #define DIOCGPHYSPATH _IOR('d', 141, char[MAXPATHLEN]) /* * Get a string defining the physical path for a given provider. * This has similar rules to ident, but is intended to uniquely * identify the physical location of the device, not the current * occupant of that location. */ struct diocgattr_arg { char name[64]; int len; union { char str[DISK_IDENT_SIZE]; off_t off; + int i; } value; }; #define DIOCGATTR _IOWR('d', 142, struct diocgattr_arg) #endif /* _SYS_DISK_H_ */ Index: projects/building-blocks/sys/sys/sockbuf.h =================================================================== --- projects/building-blocks/sys/sys/sockbuf.h (revision 278776) +++ projects/building-blocks/sys/sys/sockbuf.h (revision 278777) @@ -1,252 +1,252 @@ /*- * Copyright (c) 1982, 1986, 1990, 1993 * The Regents of the University of California. 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. * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. * * @(#)socketvar.h 8.3 (Berkeley) 2/19/95 * * $FreeBSD$ */ #ifndef _SYS_SOCKBUF_H_ #define _SYS_SOCKBUF_H_ #include /* for struct selinfo */ #include #include #include #define SB_MAX (2*1024*1024) /* default for max chars in sockbuf */ /* * Constants for sb_flags field of struct sockbuf. */ #define SB_WAIT 0x04 /* someone is waiting for data/space */ #define SB_SEL 0x08 /* someone is selecting */ #define SB_ASYNC 0x10 /* ASYNC I/O, need signals */ #define SB_UPCALL 0x20 /* someone wants an upcall */ #define SB_NOINTR 0x40 /* operations not interruptible */ #define SB_AIO 0x80 /* AIO operations queued */ #define SB_KNOTE 0x100 /* kernel note attached */ #define SB_NOCOALESCE 0x200 /* don't coalesce new data into existing mbufs */ #define SB_IN_TOE 0x400 /* socket buffer is in the middle of an operation */ #define SB_AUTOSIZE 0x800 /* automatically size socket buffer */ #define SB_STOP 0x1000 /* backpressure indicator */ #define SBS_CANTSENDMORE 0x0010 /* can't send more data to peer */ #define SBS_CANTRCVMORE 0x0020 /* can't receive more data from peer */ #define SBS_RCVATMARK 0x0040 /* at mark on input */ struct mbuf; struct sockaddr; struct socket; struct thread; struct xsockbuf { u_int sb_cc; u_int sb_hiwat; u_int sb_mbcnt; u_int sb_mcnt; u_int sb_ccnt; u_int sb_mbmax; int sb_lowat; int sb_timeo; short sb_flags; }; /* * Variables for socket buffering. */ struct sockbuf { struct selinfo sb_sel; /* process selecting read/write */ struct mtx sb_mtx; /* sockbuf lock */ struct sx sb_sx; /* prevent I/O interlacing */ short sb_state; /* (c/d) socket state on sockbuf */ #define sb_startzero sb_mb struct mbuf *sb_mb; /* (c/d) the mbuf chain */ struct mbuf *sb_mbtail; /* (c/d) the last mbuf in the chain */ struct mbuf *sb_lastrecord; /* (c/d) first mbuf of last * record in socket buffer */ struct mbuf *sb_sndptr; /* (c/d) pointer into mbuf chain */ struct mbuf *sb_fnrdy; /* (c/d) pointer to first not ready buffer */ u_int sb_sndptroff; /* (c/d) byte offset of ptr into chain */ u_int sb_acc; /* (c/d) available chars in buffer */ u_int sb_ccc; /* (c/d) claimed chars in buffer */ u_int sb_hiwat; /* (c/d) max actual char count */ u_int sb_mbcnt; /* (c/d) chars of mbufs used */ u_int sb_mcnt; /* (c/d) number of mbufs in buffer */ u_int sb_ccnt; /* (c/d) number of clusters in buffer */ u_int sb_mbmax; /* (c/d) max chars of mbufs to use */ u_int sb_ctl; /* (c/d) non-data chars in buffer */ int sb_lowat; /* (c/d) low water mark */ sbintime_t sb_timeo; /* (c/d) timeout for read/write */ short sb_flags; /* (c/d) flags, see below */ int (*sb_upcall)(struct socket *, void *, int); /* (c/d) */ void *sb_upcallarg; /* (c/d) */ }; #ifdef _KERNEL /* * Per-socket buffer mutex used to protect most fields in the socket * buffer. */ #define SOCKBUF_MTX(_sb) (&(_sb)->sb_mtx) #define SOCKBUF_LOCK_INIT(_sb, _name) \ mtx_init(SOCKBUF_MTX(_sb), _name, NULL, MTX_DEF) #define SOCKBUF_LOCK_DESTROY(_sb) mtx_destroy(SOCKBUF_MTX(_sb)) #define SOCKBUF_LOCK(_sb) mtx_lock(SOCKBUF_MTX(_sb)) #define SOCKBUF_OWNED(_sb) mtx_owned(SOCKBUF_MTX(_sb)) #define SOCKBUF_UNLOCK(_sb) mtx_unlock(SOCKBUF_MTX(_sb)) #define SOCKBUF_LOCK_ASSERT(_sb) mtx_assert(SOCKBUF_MTX(_sb), MA_OWNED) #define SOCKBUF_UNLOCK_ASSERT(_sb) mtx_assert(SOCKBUF_MTX(_sb), MA_NOTOWNED) /* * Socket buffer private mbuf(9) flags. */ #define M_NOTREADY M_PROTO1 /* m_data not populated yet */ #define M_BLOCKED M_PROTO2 /* M_NOTREADY in front of m */ #define M_NOTAVAIL (M_NOTREADY | M_BLOCKED) void sbappend(struct sockbuf *sb, struct mbuf *m); void sbappend_locked(struct sockbuf *sb, struct mbuf *m); void sbappendstream(struct sockbuf *sb, struct mbuf *m, int flags); void sbappendstream_locked(struct sockbuf *sb, struct mbuf *m, int flags); int sbappendaddr(struct sockbuf *sb, const struct sockaddr *asa, struct mbuf *m0, struct mbuf *control); int sbappendaddr_locked(struct sockbuf *sb, const struct sockaddr *asa, struct mbuf *m0, struct mbuf *control); int sbappendaddr_nospacecheck_locked(struct sockbuf *sb, const struct sockaddr *asa, struct mbuf *m0, struct mbuf *control); int sbappendcontrol(struct sockbuf *sb, struct mbuf *m0, struct mbuf *control); int sbappendcontrol_locked(struct sockbuf *sb, struct mbuf *m0, struct mbuf *control); void sbappendrecord(struct sockbuf *sb, struct mbuf *m0); void sbappendrecord_locked(struct sockbuf *sb, struct mbuf *m0); void sbcompress(struct sockbuf *sb, struct mbuf *m, struct mbuf *n); struct mbuf * sbcreatecontrol(caddr_t p, int size, int type, int level); void sbdestroy(struct sockbuf *sb, struct socket *so); void sbdrop(struct sockbuf *sb, int len); void sbdrop_locked(struct sockbuf *sb, int len); struct mbuf * sbcut_locked(struct sockbuf *sb, int len); void sbdroprecord(struct sockbuf *sb); void sbdroprecord_locked(struct sockbuf *sb); void sbflush(struct sockbuf *sb); void sbflush_locked(struct sockbuf *sb); void sbrelease(struct sockbuf *sb, struct socket *so); void sbrelease_internal(struct sockbuf *sb, struct socket *so); void sbrelease_locked(struct sockbuf *sb, struct socket *so); int sbreserve(struct sockbuf *sb, u_long cc, struct socket *so, struct thread *td); int sbreserve_locked(struct sockbuf *sb, u_long cc, struct socket *so, struct thread *td); struct mbuf * sbsndptr(struct sockbuf *sb, u_int off, u_int len, u_int *moff); struct mbuf * sbsndmbuf(struct sockbuf *sb, u_int off, u_int *moff); void sbtoxsockbuf(struct sockbuf *sb, struct xsockbuf *xsb); int sbwait(struct sockbuf *sb); int sblock(struct sockbuf *sb, int flags); void sbunlock(struct sockbuf *sb); void sballoc(struct sockbuf *, struct mbuf *); void sbfree(struct sockbuf *, struct mbuf *); int sbready(struct sockbuf *, struct mbuf *, int); /* * Return how much data is available to be taken out of socket * buffer right now. */ static inline u_int sbavail(struct sockbuf *sb) { #if 0 SOCKBUF_LOCK_ASSERT(sb); #endif return (sb->sb_acc); } /* * Return how much data sits there in the socket buffer * It might be that some data is not yet ready to be read. */ static inline u_int sbused(struct sockbuf *sb) { #if 0 SOCKBUF_LOCK_ASSERT(sb); #endif return (sb->sb_ccc); } /* * How much space is there in a socket buffer (so->so_snd or so->so_rcv)? * This is problematical if the fields are unsigned, as the space might * still be negative (ccc > hiwat or mbcnt > mbmax). */ static inline long sbspace(struct sockbuf *sb) { - long bleft, mleft; + int bleft, mleft; /* size should match sockbuf fields */ #if 0 SOCKBUF_LOCK_ASSERT(sb); #endif if (sb->sb_flags & SB_STOP) return(0); bleft = sb->sb_hiwat - sb->sb_ccc; mleft = sb->sb_mbmax - sb->sb_mbcnt; return ((bleft < mleft) ? bleft : mleft); } #define SB_EMPTY_FIXUP(sb) do { \ if ((sb)->sb_mb == NULL) { \ (sb)->sb_mbtail = NULL; \ (sb)->sb_lastrecord = NULL; \ } \ } while (/*CONSTCOND*/0) #ifdef SOCKBUF_DEBUG void sblastrecordchk(struct sockbuf *, const char *, int); void sblastmbufchk(struct sockbuf *, const char *, int); void sbcheck(struct sockbuf *, const char *, int); #define SBLASTRECORDCHK(sb) sblastrecordchk((sb), __FILE__, __LINE__) #define SBLASTMBUFCHK(sb) sblastmbufchk((sb), __FILE__, __LINE__) #define SBCHECK(sb) sbcheck((sb), __FILE__, __LINE__) #else #define SBLASTRECORDCHK(sb) do {} while (0) #define SBLASTMBUFCHK(sb) do {} while (0) #define SBCHECK(sb) do {} while (0) #endif /* SOCKBUF_DEBUG */ #endif /* _KERNEL */ #endif /* _SYS_SOCKBUF_H_ */ Index: projects/building-blocks/sys/x86/acpica/madt.c =================================================================== --- projects/building-blocks/sys/x86/acpica/madt.c (revision 278776) +++ projects/building-blocks/sys/x86/acpica/madt.c (revision 278777) @@ -1,610 +1,623 @@ /*- * Copyright (c) 2003 John Baldwin * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 __FBSDID("$FreeBSD$"); #include #include #include #include +#include #include #include #include #include #include #include #include #include +#include #include #include #include #include /* These two arrays are indexed by APIC IDs. */ static struct { void *io_apic; UINT32 io_vector; } *ioapics; static struct lapic_info { u_int la_enabled:1; u_int la_acpi_id:8; } lapics[MAX_APIC_ID + 1]; int madt_found_sci_override; static ACPI_TABLE_MADT *madt; static vm_paddr_t madt_physaddr; static vm_offset_t madt_length; static MALLOC_DEFINE(M_MADT, "madt_table", "ACPI MADT Table Items"); static enum intr_polarity interrupt_polarity(UINT16 IntiFlags, UINT8 Source); static enum intr_trigger interrupt_trigger(UINT16 IntiFlags, UINT8 Source); static int madt_find_cpu(u_int acpi_id, u_int *apic_id); static int madt_find_interrupt(int intr, void **apic, u_int *pin); static void madt_parse_apics(ACPI_SUBTABLE_HEADER *entry, void *arg); static void madt_parse_interrupt_override( ACPI_MADT_INTERRUPT_OVERRIDE *intr); static void madt_parse_ints(ACPI_SUBTABLE_HEADER *entry, void *arg __unused); static void madt_parse_local_nmi(ACPI_MADT_LOCAL_APIC_NMI *nmi); static void madt_parse_nmi(ACPI_MADT_NMI_SOURCE *nmi); static int madt_probe(void); static int madt_probe_cpus(void); static void madt_probe_cpus_handler(ACPI_SUBTABLE_HEADER *entry, void *arg __unused); static void madt_register(void *dummy); static int madt_setup_local(void); static int madt_setup_io(void); static void madt_walk_table(acpi_subtable_handler *handler, void *arg); static struct apic_enumerator madt_enumerator = { "MADT", madt_probe, madt_probe_cpus, madt_setup_local, madt_setup_io }; /* * Look for an ACPI Multiple APIC Description Table ("APIC") */ static int madt_probe(void) { madt_physaddr = acpi_find_table(ACPI_SIG_MADT); if (madt_physaddr == 0) return (ENXIO); return (-50); } /* * Run through the MP table enumerating CPUs. */ static int madt_probe_cpus(void) { madt = acpi_map_table(madt_physaddr, ACPI_SIG_MADT); madt_length = madt->Header.Length; KASSERT(madt != NULL, ("Unable to re-map MADT")); madt_walk_table(madt_probe_cpus_handler, NULL); acpi_unmap_table(madt); madt = NULL; return (0); } /* * Initialize the local APIC on the BSP. */ static int madt_setup_local(void) { ACPI_TABLE_DMAR *dmartbl; vm_paddr_t dmartbl_physaddr; + u_int p[4]; madt = pmap_mapbios(madt_physaddr, madt_length); if ((cpu_feature2 & CPUID2_X2APIC) != 0) { x2apic_mode = 1; dmartbl_physaddr = acpi_find_table(ACPI_SIG_DMAR); if (dmartbl_physaddr != 0) { dmartbl = acpi_map_table(dmartbl_physaddr, ACPI_SIG_DMAR); if ((dmartbl->Flags & ACPI_DMAR_X2APIC_OPT_OUT) != 0) { x2apic_mode = 0; if (bootverbose) printf( "x2APIC available but disabled by DMAR table\n"); } acpi_unmap_table(dmartbl); + } + if (vm_guest == VM_GUEST_VMWARE) { + vmware_hvcall(VMW_HVCMD_GETVCPU_INFO, p); + if ((p[0] & VMW_VCPUINFO_VCPU_RESERVED) != 0 || + (p[0] & VMW_VCPUINFO_LEGACY_X2APIC) == 0) { + x2apic_mode = 0; + if (bootverbose) + printf( + "x2APIC available but disabled inside VMWare without intr redirection\n"); + } } TUNABLE_INT_FETCH("hw.x2apic_enable", &x2apic_mode); } lapic_init(madt->Address); printf("ACPI APIC Table: <%.*s %.*s>\n", (int)sizeof(madt->Header.OemId), madt->Header.OemId, (int)sizeof(madt->Header.OemTableId), madt->Header.OemTableId); /* * We ignore 64-bit local APIC override entries. Should we * perhaps emit a warning here if we find one? */ return (0); } /* * Enumerate I/O APICs and setup interrupt sources. */ static int madt_setup_io(void) { void *ioapic; u_int pin; int i; /* Try to initialize ACPI so that we can access the FADT. */ i = acpi_Startup(); if (ACPI_FAILURE(i)) { printf("MADT: ACPI Startup failed with %s\n", AcpiFormatException(i)); printf("Try disabling either ACPI or apic support.\n"); panic("Using MADT but ACPI doesn't work"); } ioapics = malloc(sizeof(*ioapics) * (MAX_APIC_ID + 1), M_MADT, M_WAITOK | M_ZERO); /* First, we run through adding I/O APIC's. */ madt_walk_table(madt_parse_apics, NULL); /* Second, we run through the table tweaking interrupt sources. */ madt_walk_table(madt_parse_ints, NULL); /* * If there was not an explicit override entry for the SCI, * force it to use level trigger and active-low polarity. */ if (!madt_found_sci_override) { if (madt_find_interrupt(AcpiGbl_FADT.SciInterrupt, &ioapic, &pin) != 0) printf("MADT: Could not find APIC for SCI IRQ %u\n", AcpiGbl_FADT.SciInterrupt); else { printf( "MADT: Forcing active-low polarity and level trigger for SCI\n"); ioapic_set_polarity(ioapic, pin, INTR_POLARITY_LOW); ioapic_set_triggermode(ioapic, pin, INTR_TRIGGER_LEVEL); } } /* Third, we register all the I/O APIC's. */ for (i = 0; i <= MAX_APIC_ID; i++) if (ioapics[i].io_apic != NULL) ioapic_register(ioapics[i].io_apic); /* Finally, we throw the switch to enable the I/O APIC's. */ acpi_SetDefaultIntrModel(ACPI_INTR_APIC); free(ioapics, M_MADT); ioapics = NULL; return (0); } static void madt_register(void *dummy __unused) { apic_register_enumerator(&madt_enumerator); } SYSINIT(madt_register, SI_SUB_TUNABLES - 1, SI_ORDER_FIRST, madt_register, NULL); /* * Call the handler routine for each entry in the MADT table. */ static void madt_walk_table(acpi_subtable_handler *handler, void *arg) { acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length, handler, arg); } static void madt_probe_cpus_handler(ACPI_SUBTABLE_HEADER *entry, void *arg) { ACPI_MADT_LOCAL_APIC *proc; struct lapic_info *la; switch (entry->Type) { case ACPI_MADT_TYPE_LOCAL_APIC: /* * The MADT does not include a BSP flag, so we have to * let the MP code figure out which CPU is the BSP on * its own. */ proc = (ACPI_MADT_LOCAL_APIC *)entry; if (bootverbose) printf("MADT: Found CPU APIC ID %u ACPI ID %u: %s\n", proc->Id, proc->ProcessorId, (proc->LapicFlags & ACPI_MADT_ENABLED) ? "enabled" : "disabled"); if (!(proc->LapicFlags & ACPI_MADT_ENABLED)) break; if (proc->Id > MAX_APIC_ID) panic("%s: CPU ID %u too high", __func__, proc->Id); la = &lapics[proc->Id]; KASSERT(la->la_enabled == 0, ("Duplicate local APIC ID %u", proc->Id)); la->la_enabled = 1; la->la_acpi_id = proc->ProcessorId; lapic_create(proc->Id, 0); break; } } /* * Add an I/O APIC from an entry in the table. */ static void madt_parse_apics(ACPI_SUBTABLE_HEADER *entry, void *arg __unused) { ACPI_MADT_IO_APIC *apic; switch (entry->Type) { case ACPI_MADT_TYPE_IO_APIC: apic = (ACPI_MADT_IO_APIC *)entry; if (bootverbose) printf( "MADT: Found IO APIC ID %u, Interrupt %u at %p\n", apic->Id, apic->GlobalIrqBase, (void *)(uintptr_t)apic->Address); if (apic->Id > MAX_APIC_ID) panic("%s: I/O APIC ID %u too high", __func__, apic->Id); if (ioapics[apic->Id].io_apic != NULL) panic("%s: Double APIC ID %u", __func__, apic->Id); if (apic->GlobalIrqBase >= FIRST_MSI_INT) { printf("MADT: Ignoring bogus I/O APIC ID %u", apic->Id); break; } ioapics[apic->Id].io_apic = ioapic_create(apic->Address, apic->Id, apic->GlobalIrqBase); ioapics[apic->Id].io_vector = apic->GlobalIrqBase; break; default: break; } } /* * Determine properties of an interrupt source. Note that for ACPI these * functions are only used for ISA interrupts, so we assume ISA bus values * (Active Hi, Edge Triggered) for conforming values except for the ACPI * SCI for which we use Active Lo, Level Triggered. */ static enum intr_polarity interrupt_polarity(UINT16 IntiFlags, UINT8 Source) { switch (IntiFlags & ACPI_MADT_POLARITY_MASK) { default: printf("WARNING: Bogus Interrupt Polarity. Assume CONFORMS\n"); /* FALLTHROUGH*/ case ACPI_MADT_POLARITY_CONFORMS: if (Source == AcpiGbl_FADT.SciInterrupt) return (INTR_POLARITY_LOW); else return (INTR_POLARITY_HIGH); case ACPI_MADT_POLARITY_ACTIVE_HIGH: return (INTR_POLARITY_HIGH); case ACPI_MADT_POLARITY_ACTIVE_LOW: return (INTR_POLARITY_LOW); } } static enum intr_trigger interrupt_trigger(UINT16 IntiFlags, UINT8 Source) { switch (IntiFlags & ACPI_MADT_TRIGGER_MASK) { default: printf("WARNING: Bogus Interrupt Trigger Mode. Assume CONFORMS.\n"); /*FALLTHROUGH*/ case ACPI_MADT_TRIGGER_CONFORMS: if (Source == AcpiGbl_FADT.SciInterrupt) return (INTR_TRIGGER_LEVEL); else return (INTR_TRIGGER_EDGE); case ACPI_MADT_TRIGGER_EDGE: return (INTR_TRIGGER_EDGE); case ACPI_MADT_TRIGGER_LEVEL: return (INTR_TRIGGER_LEVEL); } } /* * Find the local APIC ID associated with a given ACPI Processor ID. */ static int madt_find_cpu(u_int acpi_id, u_int *apic_id) { int i; for (i = 0; i <= MAX_APIC_ID; i++) { if (!lapics[i].la_enabled) continue; if (lapics[i].la_acpi_id != acpi_id) continue; *apic_id = i; return (0); } return (ENOENT); } /* * Find the IO APIC and pin on that APIC associated with a given global * interrupt. */ static int madt_find_interrupt(int intr, void **apic, u_int *pin) { int i, best; best = -1; for (i = 0; i <= MAX_APIC_ID; i++) { if (ioapics[i].io_apic == NULL || ioapics[i].io_vector > intr) continue; if (best == -1 || ioapics[best].io_vector < ioapics[i].io_vector) best = i; } if (best == -1) return (ENOENT); *apic = ioapics[best].io_apic; *pin = intr - ioapics[best].io_vector; if (*pin > 32) printf("WARNING: Found intpin of %u for vector %d\n", *pin, intr); return (0); } void madt_parse_interrupt_values(void *entry, enum intr_trigger *trig, enum intr_polarity *pol) { ACPI_MADT_INTERRUPT_OVERRIDE *intr; char buf[64]; intr = entry; if (bootverbose) printf("MADT: Interrupt override: source %u, irq %u\n", intr->SourceIrq, intr->GlobalIrq); KASSERT(intr->Bus == 0, ("bus for interrupt overrides must be zero")); /* * Lookup the appropriate trigger and polarity modes for this * entry. */ *trig = interrupt_trigger(intr->IntiFlags, intr->SourceIrq); *pol = interrupt_polarity(intr->IntiFlags, intr->SourceIrq); /* * If the SCI is identity mapped but has edge trigger and * active-hi polarity or the force_sci_lo tunable is set, * force it to use level/lo. */ if (intr->SourceIrq == AcpiGbl_FADT.SciInterrupt) { madt_found_sci_override = 1; if (getenv_string("hw.acpi.sci.trigger", buf, sizeof(buf))) { if (tolower(buf[0]) == 'e') *trig = INTR_TRIGGER_EDGE; else if (tolower(buf[0]) == 'l') *trig = INTR_TRIGGER_LEVEL; else panic( "Invalid trigger %s: must be 'edge' or 'level'", buf); printf("MADT: Forcing SCI to %s trigger\n", *trig == INTR_TRIGGER_EDGE ? "edge" : "level"); } if (getenv_string("hw.acpi.sci.polarity", buf, sizeof(buf))) { if (tolower(buf[0]) == 'h') *pol = INTR_POLARITY_HIGH; else if (tolower(buf[0]) == 'l') *pol = INTR_POLARITY_LOW; else panic( "Invalid polarity %s: must be 'high' or 'low'", buf); printf("MADT: Forcing SCI to active %s polarity\n", *pol == INTR_POLARITY_HIGH ? "high" : "low"); } } } /* * Parse an interrupt source override for an ISA interrupt. */ static void madt_parse_interrupt_override(ACPI_MADT_INTERRUPT_OVERRIDE *intr) { void *new_ioapic, *old_ioapic; u_int new_pin, old_pin; enum intr_trigger trig; enum intr_polarity pol; if (acpi_quirks & ACPI_Q_MADT_IRQ0 && intr->SourceIrq == 0 && intr->GlobalIrq == 2) { if (bootverbose) printf("MADT: Skipping timer override\n"); return; } if (madt_find_interrupt(intr->GlobalIrq, &new_ioapic, &new_pin) != 0) { printf("MADT: Could not find APIC for vector %u (IRQ %u)\n", intr->GlobalIrq, intr->SourceIrq); return; } madt_parse_interrupt_values(intr, &trig, &pol); /* Remap the IRQ if it is mapped to a different interrupt vector. */ if (intr->SourceIrq != intr->GlobalIrq) { /* * If the SCI is remapped to a non-ISA global interrupt, * then override the vector we use to setup and allocate * the interrupt. */ if (intr->GlobalIrq > 15 && intr->SourceIrq == AcpiGbl_FADT.SciInterrupt) acpi_OverrideInterruptLevel(intr->GlobalIrq); else ioapic_remap_vector(new_ioapic, new_pin, intr->SourceIrq); if (madt_find_interrupt(intr->SourceIrq, &old_ioapic, &old_pin) != 0) printf("MADT: Could not find APIC for source IRQ %u\n", intr->SourceIrq); else if (ioapic_get_vector(old_ioapic, old_pin) == intr->SourceIrq) ioapic_disable_pin(old_ioapic, old_pin); } /* Program the polarity and trigger mode. */ ioapic_set_triggermode(new_ioapic, new_pin, trig); ioapic_set_polarity(new_ioapic, new_pin, pol); } /* * Parse an entry for an NMI routed to an IO APIC. */ static void madt_parse_nmi(ACPI_MADT_NMI_SOURCE *nmi) { void *ioapic; u_int pin; if (madt_find_interrupt(nmi->GlobalIrq, &ioapic, &pin) != 0) { printf("MADT: Could not find APIC for vector %u\n", nmi->GlobalIrq); return; } ioapic_set_nmi(ioapic, pin); if (!(nmi->IntiFlags & ACPI_MADT_TRIGGER_CONFORMS)) ioapic_set_triggermode(ioapic, pin, interrupt_trigger(nmi->IntiFlags, 0)); if (!(nmi->IntiFlags & ACPI_MADT_POLARITY_CONFORMS)) ioapic_set_polarity(ioapic, pin, interrupt_polarity(nmi->IntiFlags, 0)); } /* * Parse an entry for an NMI routed to a local APIC LVT pin. */ static void madt_parse_local_nmi(ACPI_MADT_LOCAL_APIC_NMI *nmi) { u_int apic_id, pin; if (nmi->ProcessorId == 0xff) apic_id = APIC_ID_ALL; else if (madt_find_cpu(nmi->ProcessorId, &apic_id) != 0) { if (bootverbose) printf("MADT: Ignoring local NMI routed to " "ACPI CPU %u\n", nmi->ProcessorId); return; } if (nmi->Lint == 0) pin = APIC_LVT_LINT0; else pin = APIC_LVT_LINT1; lapic_set_lvt_mode(apic_id, pin, APIC_LVT_DM_NMI); if (!(nmi->IntiFlags & ACPI_MADT_TRIGGER_CONFORMS)) lapic_set_lvt_triggermode(apic_id, pin, interrupt_trigger(nmi->IntiFlags, 0)); if (!(nmi->IntiFlags & ACPI_MADT_POLARITY_CONFORMS)) lapic_set_lvt_polarity(apic_id, pin, interrupt_polarity(nmi->IntiFlags, 0)); } /* * Parse interrupt entries. */ static void madt_parse_ints(ACPI_SUBTABLE_HEADER *entry, void *arg __unused) { switch (entry->Type) { case ACPI_MADT_TYPE_INTERRUPT_OVERRIDE: madt_parse_interrupt_override( (ACPI_MADT_INTERRUPT_OVERRIDE *)entry); break; case ACPI_MADT_TYPE_NMI_SOURCE: madt_parse_nmi((ACPI_MADT_NMI_SOURCE *)entry); break; case ACPI_MADT_TYPE_LOCAL_APIC_NMI: madt_parse_local_nmi((ACPI_MADT_LOCAL_APIC_NMI *)entry); break; } } /* * Setup per-CPU ACPI IDs. */ static void madt_set_ids(void *dummy) { struct lapic_info *la; struct pcpu *pc; u_int i; if (madt == NULL) return; CPU_FOREACH(i) { pc = pcpu_find(i); KASSERT(pc != NULL, ("no pcpu data for CPU %u", i)); la = &lapics[pc->pc_apic_id]; if (!la->la_enabled) panic("APIC: CPU with APIC ID %u is not enabled", pc->pc_apic_id); pc->pc_acpi_id = la->la_acpi_id; if (bootverbose) printf("APIC: CPU %u has ACPI ID %u\n", i, la->la_acpi_id); } } SYSINIT(madt_set_ids, SI_SUB_CPU, SI_ORDER_MIDDLE, madt_set_ids, NULL); Index: projects/building-blocks/sys/x86/include/vmware.h =================================================================== --- projects/building-blocks/sys/x86/include/vmware.h (revision 278776) +++ projects/building-blocks/sys/x86/include/vmware.h (revision 278777) @@ -1,47 +1,52 @@ /*- * Copyright (c) 2011-2014 Jung-uk Kim * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * * $FreeBSD$ */ #ifndef _X86_VMWARE_H_ #define _X86_VMWARE_H_ #define VMW_HVMAGIC 0x564d5868 #define VMW_HVPORT 0x5658 + #define VMW_HVCMD_GETVERSION 10 #define VMW_HVCMD_GETHZ 45 +#define VMW_HVCMD_GETVCPU_INFO 68 + +#define VMW_VCPUINFO_LEGACY_X2APIC (1 << 3) +#define VMW_VCPUINFO_VCPU_RESERVED (1 << 31) static __inline void vmware_hvcall(u_int cmd, u_int *p) { __asm __volatile("inl %w3, %0" : "=a" (p[0]), "=b" (p[1]), "=c" (p[2]), "=d" (p[3]) : "0" (VMW_HVMAGIC), "1" (UINT_MAX), "2" (cmd), "3" (VMW_HVPORT) : "memory"); } #endif /* !_X86_VMWARE_H_ */ Index: projects/building-blocks/sys =================================================================== --- projects/building-blocks/sys (revision 278776) +++ projects/building-blocks/sys (revision 278777) Property changes on: projects/building-blocks/sys ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/sys:r278637-278776 Index: projects/building-blocks/tools/tools/netmap/pkt-gen.c =================================================================== --- projects/building-blocks/tools/tools/netmap/pkt-gen.c (revision 278776) +++ projects/building-blocks/tools/tools/netmap/pkt-gen.c (revision 278777) @@ -1,2009 +1,2009 @@ /* * Copyright (C) 2011-2014 Matteo Landi, Luigi Rizzo. All rights reserved. * Copyright (C) 2013-2014 Universita` di Pisa. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ /* * $FreeBSD$ * $Id: pkt-gen.c 12346 2013-06-12 17:36:25Z luigi $ * * Example program to show how to build a multithreaded packet * source/sink using the netmap device. * * In this example we create a programmable number of threads * to take care of all the queues of the interface used to * send or receive traffic. * */ // #define TRASH_VHOST_HDR #define _GNU_SOURCE /* for CPU_SET() */ #include #define NETMAP_WITH_LIBS #include #include // isprint() #include // sysconf() #include #include /* ntohs */ #include /* sysctl */ #include /* getifaddrs */ #include #include #include #include #include #ifndef NO_PCAP #include #endif #ifdef linux #define cpuset_t cpu_set_t #define ifr_flagshigh ifr_flags /* only the low 16 bits here */ #define IFF_PPROMISC IFF_PROMISC /* IFF_PPROMISC does not exist */ #include #include #define CLOCK_REALTIME_PRECISE CLOCK_REALTIME #include /* ether_aton */ #include /* sockaddr_ll */ #endif /* linux */ #ifdef __FreeBSD__ #include /* le64toh */ #include #include /* pthread w/ affinity */ #include /* cpu_set */ #include /* LLADDR */ #endif /* __FreeBSD__ */ #ifdef __APPLE__ #define cpuset_t uint64_t // XXX static inline void CPU_ZERO(cpuset_t *p) { *p = 0; } static inline void CPU_SET(uint32_t i, cpuset_t *p) { *p |= 1<< (i & 0x3f); } #define pthread_setaffinity_np(a, b, c) ((void)a, 0) #define ifr_flagshigh ifr_flags // XXX #define IFF_PPROMISC IFF_PROMISC #include /* LLADDR */ #define clock_gettime(a,b) \ do {struct timespec t0 = {0,0}; *(b) = t0; } while (0) #endif /* __APPLE__ */ const char *default_payload="netmap pkt-gen DIRECT payload\n" "http://info.iet.unipi.it/~luigi/netmap/ "; const char *indirect_payload="netmap pkt-gen indirect payload\n" "http://info.iet.unipi.it/~luigi/netmap/ "; int verbose = 0; #define SKIP_PAYLOAD 1 /* do not check payload. XXX unused */ #define VIRT_HDR_1 10 /* length of a base vnet-hdr */ #define VIRT_HDR_2 12 /* length of the extenede vnet-hdr */ #define VIRT_HDR_MAX VIRT_HDR_2 struct virt_header { uint8_t fields[VIRT_HDR_MAX]; }; #define MAX_BODYSIZE 16384 struct pkt { struct virt_header vh; struct ether_header eh; struct ip ip; struct udphdr udp; uint8_t body[MAX_BODYSIZE]; // XXX hardwired } __attribute__((__packed__)); struct ip_range { char *name; uint32_t start, end; /* same as struct in_addr */ uint16_t port0, port1; }; struct mac_range { char *name; struct ether_addr start, end; }; /* ifname can be netmap:foo-xxxx */ #define MAX_IFNAMELEN 64 /* our buffer for ifname */ //#define MAX_PKTSIZE 1536 #define MAX_PKTSIZE MAX_BODYSIZE /* XXX: + IP_HDR + ETH_HDR */ /* compact timestamp to fit into 60 byte packet. (enough to obtain RTT) */ struct tstamp { uint32_t sec; uint32_t nsec; }; /* * global arguments for all threads */ struct glob_arg { struct ip_range src_ip; struct ip_range dst_ip; struct mac_range dst_mac; struct mac_range src_mac; int pkt_size; int burst; int forever; int npackets; /* total packets to send */ int frags; /* fragments per packet */ int nthreads; int cpus; int options; /* testing */ #define OPT_PREFETCH 1 #define OPT_ACCESS 2 #define OPT_COPY 4 #define OPT_MEMCPY 8 #define OPT_TS 16 /* add a timestamp */ #define OPT_INDIRECT 32 /* use indirect buffers, tx only */ #define OPT_DUMP 64 /* dump rx/tx traffic */ #define OPT_MONITOR_TX 128 #define OPT_MONITOR_RX 256 int dev_type; #ifndef NO_PCAP pcap_t *p; #endif int tx_rate; struct timespec tx_period; int affinity; int main_fd; struct nm_desc *nmd; int report_interval; /* milliseconds between prints */ void *(*td_body)(void *); void *mmap_addr; char ifname[MAX_IFNAMELEN]; char *nmr_config; int dummy_send; int virt_header; /* send also the virt_header */ int extra_bufs; /* goes in nr_arg3 */ char *packet_file; /* -P option */ }; enum dev_type { DEV_NONE, DEV_NETMAP, DEV_PCAP, DEV_TAP }; /* * Arguments for a new thread. The same structure is used by * the source and the sink */ struct targ { struct glob_arg *g; int used; int completed; int cancel; int fd; struct nm_desc *nmd; volatile uint64_t count; struct timespec tic, toc; int me; pthread_t thread; int affinity; struct pkt pkt; void *frame; }; /* * extract the extremes from a range of ipv4 addresses. * addr_lo[-addr_hi][:port_lo[-port_hi]] */ static void extract_ip_range(struct ip_range *r) { char *ap, *pp; struct in_addr a; if (verbose) D("extract IP range from %s", r->name); r->port0 = r->port1 = 0; r->start = r->end = 0; /* the first - splits start/end of range */ ap = index(r->name, '-'); /* do we have ports ? */ if (ap) { *ap++ = '\0'; } /* grab the initial values (mandatory) */ pp = index(r->name, ':'); if (pp) { *pp++ = '\0'; r->port0 = r->port1 = strtol(pp, NULL, 0); }; inet_aton(r->name, &a); r->start = r->end = ntohl(a.s_addr); if (ap) { pp = index(ap, ':'); if (pp) { *pp++ = '\0'; if (*pp) r->port1 = strtol(pp, NULL, 0); } if (*ap) { inet_aton(ap, &a); r->end = ntohl(a.s_addr); } } if (r->port0 > r->port1) { uint16_t tmp = r->port0; r->port0 = r->port1; r->port1 = tmp; } if (r->start > r->end) { uint32_t tmp = r->start; r->start = r->end; r->end = tmp; } { struct in_addr a; char buf1[16]; // one ip address a.s_addr = htonl(r->end); strncpy(buf1, inet_ntoa(a), sizeof(buf1)); a.s_addr = htonl(r->start); if (1) D("range is %s:%d to %s:%d", inet_ntoa(a), r->port0, buf1, r->port1); } } static void extract_mac_range(struct mac_range *r) { if (verbose) D("extract MAC range from %s", r->name); bcopy(ether_aton(r->name), &r->start, 6); bcopy(ether_aton(r->name), &r->end, 6); #if 0 bcopy(targ->src_mac, eh->ether_shost, 6); p = index(targ->g->src_mac, '-'); if (p) targ->src_mac_range = atoi(p+1); bcopy(ether_aton(targ->g->dst_mac), targ->dst_mac, 6); bcopy(targ->dst_mac, eh->ether_dhost, 6); p = index(targ->g->dst_mac, '-'); if (p) targ->dst_mac_range = atoi(p+1); #endif if (verbose) D("%s starts at %s", r->name, ether_ntoa(&r->start)); } static struct targ *targs; static int global_nthreads; /* control-C handler */ static void sigint_h(int sig) { int i; (void)sig; /* UNUSED */ D("received control-C on thread %p", pthread_self()); for (i = 0; i < global_nthreads; i++) { targs[i].cancel = 1; } signal(SIGINT, SIG_DFL); } /* sysctl wrapper to return the number of active CPUs */ static int system_ncpus(void) { int ncpus; #if defined (__FreeBSD__) int mib[2] = { CTL_HW, HW_NCPU }; size_t len = sizeof(mib); sysctl(mib, 2, &ncpus, &len, NULL, 0); #elif defined(linux) ncpus = sysconf(_SC_NPROCESSORS_ONLN); #else /* others */ ncpus = 1; #endif /* others */ return (ncpus); } #ifdef __linux__ #define sockaddr_dl sockaddr_ll #define sdl_family sll_family #define AF_LINK AF_PACKET #define LLADDR(s) s->sll_addr; #include #define TAP_CLONEDEV "/dev/net/tun" #endif /* __linux__ */ #ifdef __FreeBSD__ #include #define TAP_CLONEDEV "/dev/tap" #endif /* __FreeBSD */ #ifdef __APPLE__ // #warning TAP not supported on apple ? #include #define TAP_CLONEDEV "/dev/tap" #endif /* __APPLE__ */ /* * parse the vale configuration in conf and put it in nmr. * Return the flag set if necessary. * The configuration may consist of 0 to 4 numbers separated * by commas: #tx-slots,#rx-slots,#tx-rings,#rx-rings. * Missing numbers or zeroes stand for default values. * As an additional convenience, if exactly one number * is specified, then this is assigned to both #tx-slots and #rx-slots. * If there is no 4th number, then the 3rd is assigned to both #tx-rings * and #rx-rings. */ int parse_nmr_config(const char* conf, struct nmreq *nmr) { char *w, *tok; int i, v; nmr->nr_tx_rings = nmr->nr_rx_rings = 0; nmr->nr_tx_slots = nmr->nr_rx_slots = 0; if (conf == NULL || ! *conf) return 0; w = strdup(conf); for (i = 0, tok = strtok(w, ","); tok; i++, tok = strtok(NULL, ",")) { v = atoi(tok); switch (i) { case 0: nmr->nr_tx_slots = nmr->nr_rx_slots = v; break; case 1: nmr->nr_rx_slots = v; break; case 2: nmr->nr_tx_rings = nmr->nr_rx_rings = v; break; case 3: nmr->nr_rx_rings = v; break; default: D("ignored config: %s", tok); break; } } D("txr %d txd %d rxr %d rxd %d", nmr->nr_tx_rings, nmr->nr_tx_slots, nmr->nr_rx_rings, nmr->nr_rx_slots); free(w); return (nmr->nr_tx_rings || nmr->nr_tx_slots || nmr->nr_rx_rings || nmr->nr_rx_slots) ? NM_OPEN_RING_CFG : 0; } /* * locate the src mac address for our interface, put it * into the user-supplied buffer. return 0 if ok, -1 on error. */ static int source_hwaddr(const char *ifname, char *buf) { struct ifaddrs *ifaphead, *ifap; int l = sizeof(ifap->ifa_name); if (getifaddrs(&ifaphead) != 0) { D("getifaddrs %s failed", ifname); return (-1); } for (ifap = ifaphead; ifap; ifap = ifap->ifa_next) { struct sockaddr_dl *sdl = (struct sockaddr_dl *)ifap->ifa_addr; uint8_t *mac; if (!sdl || sdl->sdl_family != AF_LINK) continue; if (strncmp(ifap->ifa_name, ifname, l) != 0) continue; mac = (uint8_t *)LLADDR(sdl); sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); if (verbose) D("source hwaddr %s", buf); break; } freeifaddrs(ifaphead); return ifap ? 0 : 1; } /* set the thread affinity. */ static int setaffinity(pthread_t me, int i) { cpuset_t cpumask; if (i == -1) return 0; /* Set thread affinity affinity.*/ CPU_ZERO(&cpumask); CPU_SET(i, &cpumask); if (pthread_setaffinity_np(me, sizeof(cpuset_t), &cpumask) != 0) { D("Unable to set affinity: %s", strerror(errno)); return 1; } return 0; } /* Compute the checksum of the given ip header. */ static uint16_t checksum(const void *data, uint16_t len, uint32_t sum) { const uint8_t *addr = data; uint32_t i; /* Checksum all the pairs of bytes first... */ for (i = 0; i < (len & ~1U); i += 2) { sum += (u_int16_t)ntohs(*((u_int16_t *)(addr + i))); if (sum > 0xFFFF) sum -= 0xFFFF; } /* * If there's a single byte left over, checksum it, too. * Network byte order is big-endian, so the remaining byte is * the high byte. */ if (i < len) { sum += addr[i] << 8; if (sum > 0xFFFF) sum -= 0xFFFF; } return sum; } static u_int16_t wrapsum(u_int32_t sum) { sum = ~sum & 0xFFFF; return (htons(sum)); } /* Check the payload of the packet for errors (use it for debug). * Look for consecutive ascii representations of the size of the packet. */ static void dump_payload(char *p, int len, struct netmap_ring *ring, int cur) { char buf[128]; int i, j, i0; /* get the length in ASCII of the length of the packet. */ printf("ring %p cur %5d [buf %6d flags 0x%04x len %5d]\n", ring, cur, ring->slot[cur].buf_idx, ring->slot[cur].flags, len); /* hexdump routine */ for (i = 0; i < len; ) { memset(buf, sizeof(buf), ' '); sprintf(buf, "%5d: ", i); i0 = i; for (j=0; j < 16 && i < len; i++, j++) sprintf(buf+7+j*3, "%02x ", (uint8_t)(p[i])); i = i0; for (j=0; j < 16 && i < len; i++, j++) sprintf(buf+7+j + 48, "%c", isprint(p[i]) ? p[i] : '.'); printf("%s\n", buf); } } /* * Fill a packet with some payload. * We create a UDP packet so the payload starts at * 14+20+8 = 42 bytes. */ #ifdef __linux__ #define uh_sport source #define uh_dport dest #define uh_ulen len #define uh_sum check #endif /* linux */ /* * increment the addressed in the packet, * starting from the least significant field. * DST_IP DST_PORT SRC_IP SRC_PORT */ static void update_addresses(struct pkt *pkt, struct glob_arg *g) { uint32_t a; uint16_t p; struct ip *ip = &pkt->ip; struct udphdr *udp = &pkt->udp; do { p = ntohs(udp->uh_sport); if (p < g->src_ip.port1) { /* just inc, no wrap */ udp->uh_sport = htons(p + 1); break; } udp->uh_sport = htons(g->src_ip.port0); a = ntohl(ip->ip_src.s_addr); if (a < g->src_ip.end) { /* just inc, no wrap */ ip->ip_src.s_addr = htonl(a + 1); break; } ip->ip_src.s_addr = htonl(g->src_ip.start); udp->uh_sport = htons(g->src_ip.port0); p = ntohs(udp->uh_dport); if (p < g->dst_ip.port1) { /* just inc, no wrap */ udp->uh_dport = htons(p + 1); break; } udp->uh_dport = htons(g->dst_ip.port0); a = ntohl(ip->ip_dst.s_addr); if (a < g->dst_ip.end) { /* just inc, no wrap */ ip->ip_dst.s_addr = htonl(a + 1); break; } ip->ip_dst.s_addr = htonl(g->dst_ip.start); } while (0); // update checksum } /* * initialize one packet and prepare for the next one. * The copy could be done better instead of repeating it each time. */ static void initialize_packet(struct targ *targ) { struct pkt *pkt = &targ->pkt; struct ether_header *eh; struct ip *ip; struct udphdr *udp; uint16_t paylen = targ->g->pkt_size - sizeof(*eh) - sizeof(struct ip); const char *payload = targ->g->options & OPT_INDIRECT ? indirect_payload : default_payload; int i, l0 = strlen(payload); char errbuf[PCAP_ERRBUF_SIZE]; pcap_t *file; struct pcap_pkthdr *header; const unsigned char *packet; /* Read a packet from a PCAP file if asked. */ if (targ->g->packet_file != NULL) { if ((file = pcap_open_offline(targ->g->packet_file, errbuf)) == NULL) D("failed to open pcap file %s", targ->g->packet_file); if (pcap_next_ex(file, &header, &packet) < 0) D("failed to read packet from %s", targ->g->packet_file); if ((targ->frame = malloc(header->caplen)) == NULL) D("out of memory"); bcopy(packet, (unsigned char *)targ->frame, header->caplen); targ->g->pkt_size = header->caplen; pcap_close(file); return; } /* create a nice NUL-terminated string */ for (i = 0; i < paylen; i += l0) { if (l0 > paylen - i) l0 = paylen - i; // last round bcopy(payload, pkt->body + i, l0); } pkt->body[i-1] = '\0'; ip = &pkt->ip; /* prepare the headers */ ip->ip_v = IPVERSION; ip->ip_hl = 5; ip->ip_id = 0; ip->ip_tos = IPTOS_LOWDELAY; ip->ip_len = ntohs(targ->g->pkt_size - sizeof(*eh)); ip->ip_id = 0; ip->ip_off = htons(IP_DF); /* Don't fragment */ ip->ip_ttl = IPDEFTTL; ip->ip_p = IPPROTO_UDP; ip->ip_dst.s_addr = htonl(targ->g->dst_ip.start); ip->ip_src.s_addr = htonl(targ->g->src_ip.start); ip->ip_sum = wrapsum(checksum(ip, sizeof(*ip), 0)); udp = &pkt->udp; udp->uh_sport = htons(targ->g->src_ip.port0); udp->uh_dport = htons(targ->g->dst_ip.port0); udp->uh_ulen = htons(paylen); /* Magic: taken from sbin/dhclient/packet.c */ udp->uh_sum = wrapsum(checksum(udp, sizeof(*udp), checksum(pkt->body, paylen - sizeof(*udp), checksum(&ip->ip_src, 2 * sizeof(ip->ip_src), IPPROTO_UDP + (u_int32_t)ntohs(udp->uh_ulen) ) ) )); eh = &pkt->eh; bcopy(&targ->g->src_mac.start, eh->ether_shost, 6); bcopy(&targ->g->dst_mac.start, eh->ether_dhost, 6); eh->ether_type = htons(ETHERTYPE_IP); bzero(&pkt->vh, sizeof(pkt->vh)); #ifdef TRASH_VHOST_HDR /* set bogus content */ pkt->vh.fields[0] = 0xff; pkt->vh.fields[1] = 0xff; pkt->vh.fields[2] = 0xff; pkt->vh.fields[3] = 0xff; pkt->vh.fields[4] = 0xff; pkt->vh.fields[5] = 0xff; #endif /* TRASH_VHOST_HDR */ // dump_payload((void *)pkt, targ->g->pkt_size, NULL, 0); } static void set_vnet_hdr_len(struct targ *t) { int err, l = t->g->virt_header; struct nmreq req; if (l == 0) return; memset(&req, 0, sizeof(req)); bcopy(t->nmd->req.nr_name, req.nr_name, sizeof(req.nr_name)); req.nr_version = NETMAP_API; req.nr_cmd = NETMAP_BDG_VNET_HDR; req.nr_arg1 = l; err = ioctl(t->fd, NIOCREGIF, &req); if (err) { D("Unable to set vnet header length %d", l); } } /* * create and enqueue a batch of packets on a ring. * On the last one set NS_REPORT to tell the driver to generate * an interrupt when done. */ static int send_packets(struct netmap_ring *ring, struct pkt *pkt, void *frame, int size, struct glob_arg *g, u_int count, int options, u_int nfrags) { u_int n, sent, cur = ring->cur; u_int fcnt; n = nm_ring_space(ring); if (n < count) count = n; if (count < nfrags) { D("truncating packet, no room for frags %d %d", count, nfrags); } #if 0 if (options & (OPT_COPY | OPT_PREFETCH) ) { for (sent = 0; sent < count; sent++) { struct netmap_slot *slot = &ring->slot[cur]; char *p = NETMAP_BUF(ring, slot->buf_idx); __builtin_prefetch(p); cur = nm_ring_next(ring, cur); } cur = ring->cur; } #endif for (fcnt = nfrags, sent = 0; sent < count; sent++) { struct netmap_slot *slot = &ring->slot[cur]; char *p = NETMAP_BUF(ring, slot->buf_idx); slot->flags = 0; if (options & OPT_INDIRECT) { slot->flags |= NS_INDIRECT; slot->ptr = (uint64_t)frame; } else if (options & OPT_COPY) { nm_pkt_copy(frame, p, size); if (fcnt == nfrags) update_addresses(pkt, g); } else if (options & OPT_MEMCPY) { memcpy(p, frame, size); if (fcnt == nfrags) update_addresses(pkt, g); } else if (options & OPT_PREFETCH) { __builtin_prefetch(p); } if (options & OPT_DUMP) dump_payload(p, size, ring, cur); slot->len = size; if (--fcnt > 0) slot->flags |= NS_MOREFRAG; else fcnt = nfrags; if (sent == count - 1) { slot->flags &= ~NS_MOREFRAG; slot->flags |= NS_REPORT; } cur = nm_ring_next(ring, cur); } ring->head = ring->cur = cur; return (sent); } /* * Send a packet, and wait for a response. * The payload (after UDP header, ofs 42) has a 4-byte sequence * followed by a struct timeval (or bintime?) */ #define PAY_OFS 42 /* where in the pkt... */ static void * pinger_body(void *data) { struct targ *targ = (struct targ *) data; struct pollfd pfd = { .fd = targ->fd, .events = POLLIN }; struct netmap_if *nifp = targ->nmd->nifp; int i, rx = 0, n = targ->g->npackets; void *frame; int size; uint32_t sent = 0; struct timespec ts, now, last_print; uint32_t count = 0, min = 1000000000, av = 0; frame = &targ->pkt; frame += sizeof(targ->pkt.vh) - targ->g->virt_header; size = targ->g->pkt_size + targ->g->virt_header; if (targ->g->nthreads > 1) { D("can only ping with 1 thread"); return NULL; } clock_gettime(CLOCK_REALTIME_PRECISE, &last_print); now = last_print; while (n == 0 || (int)sent < n) { struct netmap_ring *ring = NETMAP_TXRING(nifp, 0); struct netmap_slot *slot; char *p; for (i = 0; i < 1; i++) { /* XXX why the loop for 1 pkt ? */ slot = &ring->slot[ring->cur]; slot->len = size; p = NETMAP_BUF(ring, slot->buf_idx); if (nm_ring_empty(ring)) { D("-- ouch, cannot send"); } else { struct tstamp *tp; nm_pkt_copy(frame, p, size); clock_gettime(CLOCK_REALTIME_PRECISE, &ts); bcopy(&sent, p+42, sizeof(sent)); tp = (struct tstamp *)(p+46); tp->sec = (uint32_t)ts.tv_sec; tp->nsec = (uint32_t)ts.tv_nsec; sent++; ring->head = ring->cur = nm_ring_next(ring, ring->cur); } } /* should use a parameter to decide how often to send */ if (poll(&pfd, 1, 3000) <= 0) { D("poll error/timeout on queue %d: %s", targ->me, strerror(errno)); continue; } /* see what we got back */ for (i = targ->nmd->first_tx_ring; i <= targ->nmd->last_tx_ring; i++) { ring = NETMAP_RXRING(nifp, i); while (!nm_ring_empty(ring)) { uint32_t seq; struct tstamp *tp; slot = &ring->slot[ring->cur]; p = NETMAP_BUF(ring, slot->buf_idx); clock_gettime(CLOCK_REALTIME_PRECISE, &now); bcopy(p+42, &seq, sizeof(seq)); tp = (struct tstamp *)(p+46); ts.tv_sec = (time_t)tp->sec; ts.tv_nsec = (long)tp->nsec; ts.tv_sec = now.tv_sec - ts.tv_sec; ts.tv_nsec = now.tv_nsec - ts.tv_nsec; if (ts.tv_nsec < 0) { ts.tv_nsec += 1000000000; ts.tv_sec--; } if (1) D("seq %d/%d delta %d.%09d", seq, sent, (int)ts.tv_sec, (int)ts.tv_nsec); if (ts.tv_nsec < (int)min) min = ts.tv_nsec; count ++; av += ts.tv_nsec; ring->head = ring->cur = nm_ring_next(ring, ring->cur); rx++; } } //D("tx %d rx %d", sent, rx); //usleep(100000); ts.tv_sec = now.tv_sec - last_print.tv_sec; ts.tv_nsec = now.tv_nsec - last_print.tv_nsec; if (ts.tv_nsec < 0) { ts.tv_nsec += 1000000000; ts.tv_sec--; } if (ts.tv_sec >= 1) { D("count %d min %d av %d", count, min, av/count); count = 0; av = 0; min = 100000000; last_print = now; } } return NULL; } /* * reply to ping requests */ static void * ponger_body(void *data) { struct targ *targ = (struct targ *) data; struct pollfd pfd = { .fd = targ->fd, .events = POLLIN }; struct netmap_if *nifp = targ->nmd->nifp; struct netmap_ring *txring, *rxring; int i, rx = 0, sent = 0, n = targ->g->npackets; if (targ->g->nthreads > 1) { D("can only reply ping with 1 thread"); return NULL; } D("understood ponger %d but don't know how to do it", n); while (n == 0 || sent < n) { uint32_t txcur, txavail; //#define BUSYWAIT #ifdef BUSYWAIT ioctl(pfd.fd, NIOCRXSYNC, NULL); #else if (poll(&pfd, 1, 1000) <= 0) { D("poll error/timeout on queue %d: %s", targ->me, strerror(errno)); continue; } #endif txring = NETMAP_TXRING(nifp, 0); txcur = txring->cur; txavail = nm_ring_space(txring); /* see what we got back */ for (i = targ->nmd->first_rx_ring; i <= targ->nmd->last_rx_ring; i++) { rxring = NETMAP_RXRING(nifp, i); while (!nm_ring_empty(rxring)) { uint16_t *spkt, *dpkt; uint32_t cur = rxring->cur; struct netmap_slot *slot = &rxring->slot[cur]; char *src, *dst; src = NETMAP_BUF(rxring, slot->buf_idx); //D("got pkt %p of size %d", src, slot->len); rxring->head = rxring->cur = nm_ring_next(rxring, cur); rx++; if (txavail == 0) continue; dst = NETMAP_BUF(txring, txring->slot[txcur].buf_idx); /* copy... */ dpkt = (uint16_t *)dst; spkt = (uint16_t *)src; nm_pkt_copy(src, dst, slot->len); dpkt[0] = spkt[3]; dpkt[1] = spkt[4]; dpkt[2] = spkt[5]; dpkt[3] = spkt[0]; dpkt[4] = spkt[1]; dpkt[5] = spkt[2]; txring->slot[txcur].len = slot->len; /* XXX swap src dst mac */ txcur = nm_ring_next(txring, txcur); txavail--; sent++; } } txring->head = txring->cur = txcur; targ->count = sent; #ifdef BUSYWAIT ioctl(pfd.fd, NIOCTXSYNC, NULL); #endif //D("tx %d rx %d", sent, rx); } return NULL; } static __inline int timespec_ge(const struct timespec *a, const struct timespec *b) { if (a->tv_sec > b->tv_sec) return (1); if (a->tv_sec < b->tv_sec) return (0); if (a->tv_nsec >= b->tv_nsec) return (1); return (0); } static __inline struct timespec timeval2spec(const struct timeval *a) { struct timespec ts = { .tv_sec = a->tv_sec, .tv_nsec = a->tv_usec * 1000 }; return ts; } static __inline struct timeval timespec2val(const struct timespec *a) { struct timeval tv = { .tv_sec = a->tv_sec, .tv_usec = a->tv_nsec / 1000 }; return tv; } static __inline struct timespec timespec_add(struct timespec a, struct timespec b) { struct timespec ret = { a.tv_sec + b.tv_sec, a.tv_nsec + b.tv_nsec }; if (ret.tv_nsec >= 1000000000) { ret.tv_sec++; ret.tv_nsec -= 1000000000; } return ret; } static __inline struct timespec timespec_sub(struct timespec a, struct timespec b) { struct timespec ret = { a.tv_sec - b.tv_sec, a.tv_nsec - b.tv_nsec }; if (ret.tv_nsec < 0) { ret.tv_sec--; ret.tv_nsec += 1000000000; } return ret; } /* * wait until ts, either busy or sleeping if more than 1ms. * Return wakeup time. */ static struct timespec wait_time(struct timespec ts) { for (;;) { struct timespec w, cur; clock_gettime(CLOCK_REALTIME_PRECISE, &cur); w = timespec_sub(ts, cur); if (w.tv_sec < 0) return cur; else if (w.tv_sec > 0 || w.tv_nsec > 1000000) poll(NULL, 0, 1); } } static void * sender_body(void *data) { struct targ *targ = (struct targ *) data; struct pollfd pfd = { .fd = targ->fd, .events = POLLOUT }; struct netmap_if *nifp; struct netmap_ring *txring; int i, n = targ->g->npackets / targ->g->nthreads; int64_t sent = 0; int options = targ->g->options | OPT_COPY; struct timespec nexttime = { 0, 0}; // XXX silence compiler int rate_limit = targ->g->tx_rate; struct pkt *pkt = &targ->pkt; void *frame; int size; if (targ->frame == NULL) { frame = pkt; frame += sizeof(pkt->vh) - targ->g->virt_header; size = targ->g->pkt_size + targ->g->virt_header; } else { frame = targ->frame; size = targ->g->pkt_size; } D("start, fd %d main_fd %d", targ->fd, targ->g->main_fd); if (setaffinity(targ->thread, targ->affinity)) goto quit; /* main loop.*/ clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic); if (rate_limit) { targ->tic = timespec_add(targ->tic, (struct timespec){2,0}); targ->tic.tv_nsec = 0; wait_time(targ->tic); nexttime = targ->tic; } if (targ->g->dev_type == DEV_TAP) { D("writing to file desc %d", targ->g->main_fd); for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) { if (write(targ->g->main_fd, frame, size) != -1) sent++; update_addresses(pkt, targ->g); if (i > 10000) { targ->count = sent; i = 0; } } #ifndef NO_PCAP } else if (targ->g->dev_type == DEV_PCAP) { pcap_t *p = targ->g->p; for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) { if (pcap_inject(p, frame, size) != -1) sent++; update_addresses(pkt, targ->g); if (i > 10000) { targ->count = sent; i = 0; } } #endif /* NO_PCAP */ } else { int tosend = 0; int frags = targ->g->frags; nifp = targ->nmd->nifp; while (!targ->cancel && (n == 0 || sent < n)) { if (rate_limit && tosend <= 0) { tosend = targ->g->burst; nexttime = timespec_add(nexttime, targ->g->tx_period); wait_time(nexttime); } /* * wait for available room in the send queue(s) */ if (poll(&pfd, 1, 2000) <= 0) { if (targ->cancel) break; D("poll error/timeout on queue %d: %s", targ->me, strerror(errno)); // goto quit; } if (pfd.revents & POLLERR) { D("poll error"); goto quit; } /* * scan our queues and send on those with room */ if (options & OPT_COPY && sent > 100000 && !(targ->g->options & OPT_COPY) ) { D("drop copy"); options &= ~OPT_COPY; } for (i = targ->nmd->first_tx_ring; i <= targ->nmd->last_tx_ring; i++) { int m, limit = rate_limit ? tosend : targ->g->burst; if (n > 0 && n - sent < limit) limit = n - sent; txring = NETMAP_TXRING(nifp, i); if (nm_ring_empty(txring)) continue; if (frags > 1) limit = ((limit + frags - 1) / frags) * frags; m = send_packets(txring, pkt, frame, size, targ->g, limit, options, frags); ND("limit %d tail %d frags %d m %d", limit, txring->tail, frags, m); sent += m; targ->count = sent; if (rate_limit) { tosend -= m; if (tosend <= 0) break; } } } /* flush any remaining packets */ D("flush tail %d head %d on thread %p", txring->tail, txring->head, pthread_self()); ioctl(pfd.fd, NIOCTXSYNC, NULL); /* final part: wait all the TX queues to be empty. */ for (i = targ->nmd->first_tx_ring; i <= targ->nmd->last_tx_ring; i++) { txring = NETMAP_TXRING(nifp, i); while (nm_tx_pending(txring)) { RD(5, "pending tx tail %d head %d on ring %d", txring->tail, txring->head, i); ioctl(pfd.fd, NIOCTXSYNC, NULL); usleep(1); /* wait 1 tick */ } } } /* end DEV_NETMAP */ clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); targ->completed = 1; targ->count = sent; quit: /* reset the ``used`` flag. */ targ->used = 0; return (NULL); } #ifndef NO_PCAP static void receive_pcap(u_char *user, const struct pcap_pkthdr * h, const u_char * bytes) { int *count = (int *)user; (void)h; /* UNUSED */ (void)bytes; /* UNUSED */ (*count)++; } #endif /* !NO_PCAP */ static int receive_packets(struct netmap_ring *ring, u_int limit, int dump) { u_int cur, rx, n; cur = ring->cur; n = nm_ring_space(ring); if (n < limit) limit = n; for (rx = 0; rx < limit; rx++) { struct netmap_slot *slot = &ring->slot[cur]; char *p = NETMAP_BUF(ring, slot->buf_idx); if (dump) dump_payload(p, slot->len, ring, cur); cur = nm_ring_next(ring, cur); } ring->head = ring->cur = cur; return (rx); } static void * receiver_body(void *data) { struct targ *targ = (struct targ *) data; struct pollfd pfd = { .fd = targ->fd, .events = POLLIN }; struct netmap_if *nifp; struct netmap_ring *rxring; int i; uint64_t received = 0; if (setaffinity(targ->thread, targ->affinity)) goto quit; D("reading from %s fd %d main_fd %d", targ->g->ifname, targ->fd, targ->g->main_fd); /* unbounded wait for the first packet. */ for (;!targ->cancel;) { i = poll(&pfd, 1, 1000); if (i > 0 && !(pfd.revents & POLLERR)) break; RD(1, "waiting for initial packets, poll returns %d %d", i, pfd.revents); } /* main loop, exit after 1s silence */ clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic); if (targ->g->dev_type == DEV_TAP) { while (!targ->cancel) { char buf[MAX_BODYSIZE]; /* XXX should we poll ? */ if (read(targ->g->main_fd, buf, sizeof(buf)) > 0) targ->count++; } #ifndef NO_PCAP } else if (targ->g->dev_type == DEV_PCAP) { while (!targ->cancel) { /* XXX should we poll ? */ pcap_dispatch(targ->g->p, targ->g->burst, receive_pcap, (u_char *)&targ->count); } #endif /* !NO_PCAP */ } else { int dump = targ->g->options & OPT_DUMP; nifp = targ->nmd->nifp; while (!targ->cancel) { /* Once we started to receive packets, wait at most 1 seconds before quitting. */ if (poll(&pfd, 1, 1 * 1000) <= 0 && !targ->g->forever) { clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); targ->toc.tv_sec -= 1; /* Subtract timeout time. */ goto out; } if (pfd.revents & POLLERR) { D("poll err"); goto quit; } for (i = targ->nmd->first_rx_ring; i <= targ->nmd->last_rx_ring; i++) { int m; rxring = NETMAP_RXRING(nifp, i); if (nm_ring_empty(rxring)) continue; m = receive_packets(rxring, targ->g->burst, dump); received += m; } targ->count = received; } } clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); out: targ->completed = 1; targ->count = received; quit: /* reset the ``used`` flag. */ targ->used = 0; return (NULL); } /* very crude code to print a number in normalized form. * Caller has to make sure that the buffer is large enough. */ static const char * norm(char *buf, double val) { char *units[] = { "", "K", "M", "G", "T" }; u_int i; for (i = 0; val >=1000 && i < sizeof(units)/sizeof(char *) - 1; i++) val /= 1000; sprintf(buf, "%.2f %s", val, units[i]); return buf; } static void tx_output(uint64_t sent, int size, double delta) { double bw, raw_bw, pps; char b1[40], b2[80], b3[80]; printf("Sent %llu packets, %d bytes each, in %.2f seconds.\n", (unsigned long long)sent, size, delta); if (delta == 0) delta = 1e-6; if (size < 60) /* correct for min packet size */ size = 60; pps = sent / delta; bw = (8.0 * size * sent) / delta; /* raw packets have4 bytes crc + 20 bytes framing */ raw_bw = (8.0 * (size + 24) * sent) / delta; printf("Speed: %spps Bandwidth: %sbps (raw %sbps)\n", norm(b1, pps), norm(b2, bw), norm(b3, raw_bw) ); } static void rx_output(uint64_t received, double delta) { double pps; char b1[40]; printf("Received %llu packets, in %.2f seconds.\n", (unsigned long long) received, delta); if (delta == 0) delta = 1e-6; pps = received / delta; printf("Speed: %spps\n", norm(b1, pps)); } static void usage(void) { const char *cmd = "pkt-gen"; fprintf(stderr, "Usage:\n" "%s arguments\n" "\t-i interface interface name\n" "\t-f function tx rx ping pong\n" "\t-n count number of iterations (can be 0)\n" "\t-t pkts_to_send also forces tx mode\n" "\t-r pkts_to_receive also forces rx mode\n" "\t-l pkt_size in bytes excluding CRC\n" "\t-d dst_ip[:port[-dst_ip:port]] single or range\n" "\t-s src_ip[:port[-src_ip:port]] single or range\n" "\t-D dst-mac\n" "\t-S src-mac\n" "\t-a cpu_id use setaffinity\n" "\t-b burst size testing, mostly\n" "\t-c cores cores to use\n" "\t-p threads processes/threads to use\n" "\t-T report_ms milliseconds between reports\n" "\t-P use libpcap instead of netmap\n" "\t-w wait_for_link_time in seconds\n" "\t-R rate in packets per second\n" "\t-X dump payload\n" "\t-H len add empty virtio-net-header with size 'len'\n" "\t-P file load packet from pcap file" "", cmd); exit(0); } static void start_threads(struct glob_arg *g) { int i; targs = calloc(g->nthreads, sizeof(*targs)); /* * Now create the desired number of threads, each one * using a single descriptor. */ for (i = 0; i < g->nthreads; i++) { struct targ *t = &targs[i]; bzero(t, sizeof(*t)); t->fd = -1; /* default, with pcap */ t->g = g; if (g->dev_type == DEV_NETMAP) { struct nm_desc nmd = *g->nmd; /* copy, we overwrite ringid */ uint64_t nmd_flags = 0; nmd.self = &nmd; if (g->nthreads > 1) { if (nmd.req.nr_flags != NR_REG_ALL_NIC) { D("invalid nthreads mode %d", nmd.req.nr_flags); continue; } nmd.req.nr_flags = NR_REG_ONE_NIC; nmd.req.nr_ringid = i; } /* Only touch one of the rings (rx is already ok) */ if (g->td_body == receiver_body) nmd_flags |= NETMAP_NO_TX_POLL; /* register interface. Override ifname and ringid etc. */ if (g->options & OPT_MONITOR_TX) nmd.req.nr_flags |= NR_MONITOR_TX; if (g->options & OPT_MONITOR_RX) nmd.req.nr_flags |= NR_MONITOR_RX; t->nmd = nm_open(t->g->ifname, NULL, nmd_flags | NM_OPEN_IFNAME | NM_OPEN_NO_MMAP, &nmd); if (t->nmd == NULL) { D("Unable to open %s: %s", t->g->ifname, strerror(errno)); continue; } t->fd = t->nmd->fd; set_vnet_hdr_len(t); } else { targs[i].fd = g->main_fd; } t->used = 1; t->me = i; if (g->affinity >= 0) { if (g->affinity < g->cpus) t->affinity = g->affinity; else t->affinity = i % g->cpus; } else { t->affinity = -1; } /* default, init packets */ initialize_packet(t); if (pthread_create(&t->thread, NULL, g->td_body, t) == -1) { D("Unable to create thread %d: %s", i, strerror(errno)); t->used = 0; } } } static void main_thread(struct glob_arg *g) { int i; uint64_t prev = 0; uint64_t count = 0; double delta_t; struct timeval tic, toc; gettimeofday(&toc, NULL); for (;;) { struct timeval now, delta; uint64_t pps, usec, my_count, npkts; int done = 0; delta.tv_sec = g->report_interval/1000; delta.tv_usec = (g->report_interval%1000)*1000; select(0, NULL, NULL, NULL, &delta); gettimeofday(&now, NULL); timersub(&now, &toc, &toc); my_count = 0; for (i = 0; i < g->nthreads; i++) { my_count += targs[i].count; if (targs[i].used == 0) done++; } usec = toc.tv_sec* 1000000 + toc.tv_usec; if (usec < 10000) continue; npkts = my_count - prev; pps = (npkts*1000000 + usec/2) / usec; D("%llu pps (%llu pkts in %llu usec)", (unsigned long long)pps, (unsigned long long)npkts, (unsigned long long)usec); prev = my_count; toc = now; if (done == g->nthreads) break; } timerclear(&tic); timerclear(&toc); for (i = 0; i < g->nthreads; i++) { struct timespec t_tic, t_toc; /* * Join active threads, unregister interfaces and close * file descriptors. */ if (targs[i].used) pthread_join(targs[i].thread, NULL); close(targs[i].fd); if (targs[i].completed == 0) D("ouch, thread %d exited with error", i); /* * Collect threads output and extract information about * how long it took to send all the packets. */ count += targs[i].count; t_tic = timeval2spec(&tic); t_toc = timeval2spec(&toc); if (!timerisset(&tic) || timespec_ge(&targs[i].tic, &t_tic)) tic = timespec2val(&targs[i].tic); if (!timerisset(&toc) || timespec_ge(&targs[i].toc, &t_toc)) toc = timespec2val(&targs[i].toc); } /* print output. */ timersub(&toc, &tic, &toc); delta_t = toc.tv_sec + 1e-6* toc.tv_usec; if (g->td_body == sender_body) tx_output(count, g->pkt_size, delta_t); else rx_output(count, delta_t); if (g->dev_type == DEV_NETMAP) { munmap(g->nmd->mem, g->nmd->req.nr_memsize); close(g->main_fd); } } struct sf { char *key; void *f; }; static struct sf func[] = { { "tx", sender_body }, { "rx", receiver_body }, { "ping", pinger_body }, { "pong", ponger_body }, { NULL, NULL } }; static int tap_alloc(char *dev) { struct ifreq ifr; int fd, err; char *clonedev = TAP_CLONEDEV; (void)err; (void)dev; /* Arguments taken by the function: * * char *dev: the name of an interface (or '\0'). MUST have enough * space to hold the interface name if '\0' is passed * int flags: interface flags (eg, IFF_TUN etc.) */ #ifdef __FreeBSD__ if (dev[3]) { /* tapSomething */ static char buf[128]; snprintf(buf, sizeof(buf), "/dev/%s", dev); clonedev = buf; } #endif /* open the device */ if( (fd = open(clonedev, O_RDWR)) < 0 ) { return fd; } D("%s open successful", clonedev); /* preparation of the struct ifr, of type "struct ifreq" */ memset(&ifr, 0, sizeof(ifr)); #ifdef linux ifr.ifr_flags = IFF_TAP | IFF_NO_PI; if (*dev) { /* if a device name was specified, put it in the structure; otherwise, * the kernel will try to allocate the "next" device of the * specified type */ strncpy(ifr.ifr_name, dev, IFNAMSIZ); } /* try to create the device */ if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ) { D("failed to to a TUNSETIFF: %s", strerror(errno)); close(fd); return err; } /* if the operation was successful, write back the name of the * interface to the variable "dev", so the caller can know * it. Note that the caller MUST reserve space in *dev (see calling * code below) */ strcpy(dev, ifr.ifr_name); D("new name is %s", dev); #endif /* linux */ /* this is the special file descriptor that the caller will use to talk * with the virtual interface */ return fd; } int main(int arc, char **argv) { int i; struct glob_arg g; int ch; int wait_link = 2; int devqueues = 1; /* how many device queues */ bzero(&g, sizeof(g)); g.main_fd = -1; g.td_body = receiver_body; g.report_interval = 1000; /* report interval */ g.affinity = -1; /* ip addresses can also be a range x.x.x.x-x.x.x.y */ g.src_ip.name = "10.0.0.1"; g.dst_ip.name = "10.1.0.1"; g.dst_mac.name = "ff:ff:ff:ff:ff:ff"; g.src_mac.name = NULL; g.pkt_size = 60; g.burst = 512; // default g.nthreads = 1; g.cpus = 1; g.forever = 1; g.tx_rate = 0; g.frags = 1; g.nmr_config = ""; g.virt_header = 0; while ( (ch = getopt(arc, argv, "a:f:F:n:i:Il:d:s:D:S:b:c:o:p:T:w:WvR:XC:H:e:m:P:")) != -1) { struct sf *fn; switch(ch) { default: D("bad option %c %s", ch, optarg); usage(); break; case 'n': g.npackets = atoi(optarg); break; case 'F': i = atoi(optarg); if (i < 1 || i > 63) { D("invalid frags %d [1..63], ignore", i); break; } g.frags = i; break; case 'f': for (fn = func; fn->key; fn++) { if (!strcmp(fn->key, optarg)) break; } if (fn->key) g.td_body = fn->f; else D("unrecognised function %s", optarg); break; case 'o': /* data generation options */ g.options = atoi(optarg); break; case 'a': /* force affinity */ g.affinity = atoi(optarg); break; case 'i': /* interface */ /* a prefix of tap: netmap: or pcap: forces the mode. * otherwise we guess */ D("interface is %s", optarg); if (strlen(optarg) > MAX_IFNAMELEN - 8) { D("ifname too long %s", optarg); break; } strcpy(g.ifname, optarg); if (!strcmp(optarg, "null")) { g.dev_type = DEV_NETMAP; g.dummy_send = 1; } else if (!strncmp(optarg, "tap:", 4)) { g.dev_type = DEV_TAP; strcpy(g.ifname, optarg + 4); } else if (!strncmp(optarg, "pcap:", 5)) { g.dev_type = DEV_PCAP; strcpy(g.ifname, optarg + 5); } else if (!strncmp(optarg, "netmap:", 7) || !strncmp(optarg, "vale", 4)) { g.dev_type = DEV_NETMAP; } else if (!strncmp(optarg, "tap", 3)) { g.dev_type = DEV_TAP; } else { /* prepend netmap: */ g.dev_type = DEV_NETMAP; sprintf(g.ifname, "netmap:%s", optarg); } break; case 'I': g.options |= OPT_INDIRECT; /* XXX use indirect buffer */ break; case 'l': /* pkt_size */ g.pkt_size = atoi(optarg); break; case 'd': g.dst_ip.name = optarg; break; case 's': g.src_ip.name = optarg; break; case 'T': /* report interval */ g.report_interval = atoi(optarg); break; case 'w': wait_link = atoi(optarg); break; case 'W': /* XXX changed default */ g.forever = 0; /* do not exit rx even with no traffic */ break; case 'b': /* burst */ g.burst = atoi(optarg); break; case 'c': g.cpus = atoi(optarg); break; case 'p': g.nthreads = atoi(optarg); break; case 'D': /* destination mac */ g.dst_mac.name = optarg; break; case 'S': /* source mac */ g.src_mac.name = optarg; break; case 'v': verbose++; break; case 'R': g.tx_rate = atoi(optarg); break; case 'X': g.options |= OPT_DUMP; break; case 'C': g.nmr_config = strdup(optarg); break; case 'H': g.virt_header = atoi(optarg); break; case 'e': /* extra bufs */ g.extra_bufs = atoi(optarg); break; case 'm': if (strcmp(optarg, "tx") == 0) { g.options |= OPT_MONITOR_TX; } else if (strcmp(optarg, "rx") == 0) { g.options |= OPT_MONITOR_RX; } else { D("unrecognized monitor mode %s", optarg); } break; case 'P': g.packet_file = strdup(optarg); break; } } - if (g.ifname == NULL) { + if (strlen(g.ifname) <=0 ) { D("missing ifname"); usage(); } i = system_ncpus(); if (g.cpus < 0 || g.cpus > i) { D("%d cpus is too high, have only %d cpus", g.cpus, i); usage(); } if (g.cpus == 0) g.cpus = i; if (g.pkt_size < 16 || g.pkt_size > MAX_PKTSIZE) { D("bad pktsize %d [16..%d]\n", g.pkt_size, MAX_PKTSIZE); usage(); } if (g.src_mac.name == NULL) { static char mybuf[20] = "00:00:00:00:00:00"; /* retrieve source mac address. */ if (source_hwaddr(g.ifname, mybuf) == -1) { D("Unable to retrieve source mac"); // continue, fail later } g.src_mac.name = mybuf; } /* extract address ranges */ extract_ip_range(&g.src_ip); extract_ip_range(&g.dst_ip); extract_mac_range(&g.src_mac); extract_mac_range(&g.dst_mac); if (g.src_ip.start != g.src_ip.end || g.src_ip.port0 != g.src_ip.port1 || g.dst_ip.start != g.dst_ip.end || g.dst_ip.port0 != g.dst_ip.port1) g.options |= OPT_COPY; if (g.virt_header != 0 && g.virt_header != VIRT_HDR_1 && g.virt_header != VIRT_HDR_2) { D("bad virtio-net-header length"); usage(); } if (g.dev_type == DEV_TAP) { D("want to use tap %s", g.ifname); g.main_fd = tap_alloc(g.ifname); if (g.main_fd < 0) { D("cannot open tap %s", g.ifname); usage(); } #ifndef NO_PCAP } else if (g.dev_type == DEV_PCAP) { char pcap_errbuf[PCAP_ERRBUF_SIZE]; pcap_errbuf[0] = '\0'; // init the buffer g.p = pcap_open_live(g.ifname, 256 /* XXX */, 1, 100, pcap_errbuf); if (g.p == NULL) { D("cannot open pcap on %s", g.ifname); usage(); } g.main_fd = pcap_fileno(g.p); D("using pcap on %s fileno %d", g.ifname, g.main_fd); #endif /* !NO_PCAP */ } else if (g.dummy_send) { /* but DEV_NETMAP */ D("using a dummy send routine"); } else { struct nmreq base_nmd; bzero(&base_nmd, sizeof(base_nmd)); parse_nmr_config(g.nmr_config, &base_nmd); if (g.extra_bufs) { base_nmd.nr_arg3 = g.extra_bufs; } /* * Open the netmap device using nm_open(). * * protocol stack and may cause a reset of the card, * which in turn may take some time for the PHY to * reconfigure. We do the open here to have time to reset. */ g.nmd = nm_open(g.ifname, &base_nmd, 0, NULL); if (g.nmd == NULL) { D("Unable to open %s: %s", g.ifname, strerror(errno)); goto out; } g.main_fd = g.nmd->fd; D("mapped %dKB at %p", g.nmd->req.nr_memsize>>10, g.nmd->mem); /* get num of queues in tx or rx */ if (g.td_body == sender_body) devqueues = g.nmd->req.nr_tx_rings; else devqueues = g.nmd->req.nr_rx_rings; /* validate provided nthreads. */ if (g.nthreads < 1 || g.nthreads > devqueues) { D("bad nthreads %d, have %d queues", g.nthreads, devqueues); // continue, fail later } if (verbose) { struct netmap_if *nifp = g.nmd->nifp; struct nmreq *req = &g.nmd->req; D("nifp at offset %d, %d tx %d rx region %d", req->nr_offset, req->nr_tx_rings, req->nr_rx_rings, req->nr_arg2); for (i = 0; i <= req->nr_tx_rings; i++) { struct netmap_ring *ring = NETMAP_TXRING(nifp, i); D(" TX%d at 0x%lx slots %d", i, (char *)ring - (char *)nifp, ring->num_slots); } for (i = 0; i <= req->nr_rx_rings; i++) { struct netmap_ring *ring = NETMAP_RXRING(nifp, i); D(" RX%d at 0x%lx slots %d", i, (char *)ring - (char *)nifp, ring->num_slots); } } /* Print some debug information. */ fprintf(stdout, "%s %s: %d queues, %d threads and %d cpus.\n", (g.td_body == sender_body) ? "Sending on" : "Receiving from", g.ifname, devqueues, g.nthreads, g.cpus); if (g.td_body == sender_body) { fprintf(stdout, "%s -> %s (%s -> %s)\n", g.src_ip.name, g.dst_ip.name, g.src_mac.name, g.dst_mac.name); } out: /* Exit if something went wrong. */ if (g.main_fd < 0) { D("aborting"); usage(); } } if (g.options) { D("--- SPECIAL OPTIONS:%s%s%s%s%s\n", g.options & OPT_PREFETCH ? " prefetch" : "", g.options & OPT_ACCESS ? " access" : "", g.options & OPT_MEMCPY ? " memcpy" : "", g.options & OPT_INDIRECT ? " indirect" : "", g.options & OPT_COPY ? " copy" : ""); } g.tx_period.tv_sec = g.tx_period.tv_nsec = 0; if (g.tx_rate > 0) { /* try to have at least something every second, * reducing the burst size to some 0.01s worth of data * (but no less than one full set of fragments) */ uint64_t x; int lim = (g.tx_rate)/300; if (g.burst > lim) g.burst = lim; if (g.burst < g.frags) g.burst = g.frags; x = ((uint64_t)1000000000 * (uint64_t)g.burst) / (uint64_t) g.tx_rate; g.tx_period.tv_nsec = x; g.tx_period.tv_sec = g.tx_period.tv_nsec / 1000000000; g.tx_period.tv_nsec = g.tx_period.tv_nsec % 1000000000; } if (g.td_body == sender_body) D("Sending %d packets every %ld.%09ld s", g.burst, g.tx_period.tv_sec, g.tx_period.tv_nsec); /* Wait for PHY reset. */ D("Wait %d secs for phy reset", wait_link); sleep(wait_link); D("Ready..."); /* Install ^C handler. */ global_nthreads = g.nthreads; signal(SIGINT, sigint_h); start_threads(&g); main_thread(&g); return 0; } /* end of file */ Index: projects/building-blocks/usr.bin/gcore/elfcore.c =================================================================== --- projects/building-blocks/usr.bin/gcore/elfcore.c (revision 278776) +++ projects/building-blocks/usr.bin/gcore/elfcore.c (revision 278777) @@ -1,809 +1,810 @@ /*- * Copyright (c) 2007 Sandvine Incorporated * Copyright (c) 1998 John D. Polstra * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "extern.h" /* * Code for generating ELF core dumps. */ typedef void (*segment_callback)(vm_map_entry_t, void *); /* Closure for cb_put_phdr(). */ struct phdr_closure { Elf_Phdr *phdr; /* Program header to fill in */ Elf_Off offset; /* Offset of segment in core file */ }; /* Closure for cb_size_segment(). */ struct sseg_closure { int count; /* Count of writable segments. */ size_t size; /* Total size of all writable segments. */ }; #ifdef ELFCORE_COMPAT_32 typedef struct fpreg32 elfcore_fpregset_t; typedef struct reg32 elfcore_gregset_t; typedef struct prpsinfo32 elfcore_prpsinfo_t; typedef struct prstatus32 elfcore_prstatus_t; static void elf_convert_gregset(elfcore_gregset_t *rd, struct reg *rs); static void elf_convert_fpregset(elfcore_fpregset_t *rd, struct fpreg *rs); #else typedef fpregset_t elfcore_fpregset_t; typedef gregset_t elfcore_gregset_t; typedef prpsinfo_t elfcore_prpsinfo_t; typedef prstatus_t elfcore_prstatus_t; #define elf_convert_gregset(d,s) *d = *s #define elf_convert_fpregset(d,s) *d = *s #endif typedef void* (*notefunc_t)(void *, size_t *); static void cb_put_phdr(vm_map_entry_t, void *); static void cb_size_segment(vm_map_entry_t, void *); static void each_writable_segment(vm_map_entry_t, segment_callback, void *closure); static void elf_detach(void); /* atexit() handler. */ static void *elf_note_fpregset(void *, size_t *); static void *elf_note_prpsinfo(void *, size_t *); static void *elf_note_prstatus(void *, size_t *); static void *elf_note_thrmisc(void *, size_t *); #if defined(__i386__) || defined(__amd64__) static void *elf_note_x86_xstate(void *, size_t *); #endif #if defined(__powerpc__) static void *elf_note_powerpc_vmx(void *, size_t *); #endif static void *elf_note_procstat_auxv(void *, size_t *); static void *elf_note_procstat_files(void *, size_t *); static void *elf_note_procstat_groups(void *, size_t *); static void *elf_note_procstat_osrel(void *, size_t *); static void *elf_note_procstat_proc(void *, size_t *); static void *elf_note_procstat_psstrings(void *, size_t *); static void *elf_note_procstat_rlimit(void *, size_t *); static void *elf_note_procstat_umask(void *, size_t *); static void *elf_note_procstat_vmmap(void *, size_t *); static void elf_puthdr(pid_t, vm_map_entry_t, void *, size_t, size_t, size_t, int); static void elf_putnote(int, notefunc_t, void *, struct sbuf *); static void elf_putnotes(pid_t, struct sbuf *, size_t *); static void freemap(vm_map_entry_t); static vm_map_entry_t readmap(pid_t); static void *procstat_sysctl(void *, int, size_t, size_t *sizep); static pid_t g_pid; /* Pid being dumped, global for elf_detach */ static int elf_ident(int efd, pid_t pid __unused, char *binfile __unused) { Elf_Ehdr hdr; int cnt; uint16_t machine; cnt = read(efd, &hdr, sizeof(hdr)); if (cnt != sizeof(hdr)) return (0); if (!IS_ELF(hdr)) return (0); switch (hdr.e_ident[EI_DATA]) { case ELFDATA2LSB: machine = le16toh(hdr.e_machine); break; case ELFDATA2MSB: machine = be16toh(hdr.e_machine); break; default: return (0); } if (!ELF_MACHINE_OK(machine)) return (0); /* Looks good. */ return (1); } static void elf_detach(void) { if (g_pid != 0) ptrace(PT_DETACH, g_pid, (caddr_t)1, 0); } /* * Write an ELF coredump for the given pid to the given fd. */ static void elf_coredump(int efd __unused, int fd, pid_t pid) { vm_map_entry_t map; struct sseg_closure seginfo; struct sbuf *sb; void *hdr; size_t hdrsize, notesz, segoff; ssize_t n, old_len; Elf_Phdr *php; int i; /* Attach to process to dump. */ g_pid = pid; if (atexit(elf_detach) != 0) err(1, "atexit"); errno = 0; ptrace(PT_ATTACH, pid, NULL, 0); if (errno) err(1, "PT_ATTACH"); if (waitpid(pid, NULL, 0) == -1) err(1, "waitpid"); /* Get the program's memory map. */ map = readmap(pid); /* Size the program segments. */ seginfo.count = 0; seginfo.size = 0; each_writable_segment(map, cb_size_segment, &seginfo); /* * Build the header and the notes using sbuf and write to the file. */ sb = sbuf_new_auto(); hdrsize = sizeof(Elf_Ehdr) + sizeof(Elf_Phdr) * (1 + seginfo.count); /* Start header + notes section. */ sbuf_start_section(sb, NULL); /* Make empty header subsection. */ sbuf_start_section(sb, &old_len); sbuf_putc(sb, 0); sbuf_end_section(sb, old_len, hdrsize, 0); /* Put notes. */ elf_putnotes(pid, sb, ¬esz); /* Align up to a page boundary for the program segments. */ sbuf_end_section(sb, -1, PAGE_SIZE, 0); if (sbuf_finish(sb) != 0) err(1, "sbuf_finish"); hdr = sbuf_data(sb); segoff = sbuf_len(sb); /* Fill in the header. */ elf_puthdr(pid, map, hdr, hdrsize, notesz, segoff, seginfo.count); n = write(fd, hdr, segoff); if (n == -1) err(1, "write"); if (n < segoff) errx(1, "short write"); /* Write the contents of all of the writable segments. */ php = (Elf_Phdr *)((char *)hdr + sizeof(Elf_Ehdr)) + 1; for (i = 0; i < seginfo.count; i++) { struct ptrace_io_desc iorequest; uintmax_t nleft = php->p_filesz; iorequest.piod_op = PIOD_READ_D; iorequest.piod_offs = (caddr_t)(uintptr_t)php->p_vaddr; while (nleft > 0) { char buf[8*1024]; size_t nwant; ssize_t ngot; if (nleft > sizeof(buf)) nwant = sizeof buf; else nwant = nleft; iorequest.piod_addr = buf; iorequest.piod_len = nwant; ptrace(PT_IO, pid, (caddr_t)&iorequest, 0); ngot = iorequest.piod_len; if ((size_t)ngot < nwant) errx(1, "short read wanted %zu, got %zd", nwant, ngot); ngot = write(fd, buf, nwant); if (ngot == -1) err(1, "write of segment %d failed", i); if ((size_t)ngot != nwant) errx(1, "short write"); nleft -= nwant; iorequest.piod_offs += ngot; } php++; } sbuf_delete(sb); freemap(map); } /* * A callback for each_writable_segment() to write out the segment's * program header entry. */ static void cb_put_phdr(vm_map_entry_t entry, void *closure) { struct phdr_closure *phc = (struct phdr_closure *)closure; Elf_Phdr *phdr = phc->phdr; phc->offset = round_page(phc->offset); phdr->p_type = PT_LOAD; phdr->p_offset = phc->offset; phdr->p_vaddr = entry->start; phdr->p_paddr = 0; phdr->p_filesz = phdr->p_memsz = entry->end - entry->start; phdr->p_align = PAGE_SIZE; phdr->p_flags = 0; if (entry->protection & VM_PROT_READ) phdr->p_flags |= PF_R; if (entry->protection & VM_PROT_WRITE) phdr->p_flags |= PF_W; if (entry->protection & VM_PROT_EXECUTE) phdr->p_flags |= PF_X; phc->offset += phdr->p_filesz; phc->phdr++; } /* * A callback for each_writable_segment() to gather information about * the number of segments and their total size. */ static void cb_size_segment(vm_map_entry_t entry, void *closure) { struct sseg_closure *ssc = (struct sseg_closure *)closure; ssc->count++; ssc->size += entry->end - entry->start; } /* * For each segment in the given memory map, call the given function * with a pointer to the map entry and some arbitrary caller-supplied * data. */ static void each_writable_segment(vm_map_entry_t map, segment_callback func, void *closure) { vm_map_entry_t entry; for (entry = map; entry != NULL; entry = entry->next) (*func)(entry, closure); } static void elf_putnotes(pid_t pid, struct sbuf *sb, size_t *sizep) { lwpid_t *tids; size_t threads, old_len; ssize_t size; int i; errno = 0; threads = ptrace(PT_GETNUMLWPS, pid, NULL, 0); if (errno) err(1, "PT_GETNUMLWPS"); tids = malloc(threads * sizeof(*tids)); if (tids == NULL) errx(1, "out of memory"); errno = 0; ptrace(PT_GETLWPLIST, pid, (void *)tids, threads); if (errno) err(1, "PT_GETLWPLIST"); sbuf_start_section(sb, &old_len); elf_putnote(NT_PRPSINFO, elf_note_prpsinfo, &pid, sb); for (i = 0; i < threads; ++i) { elf_putnote(NT_PRSTATUS, elf_note_prstatus, tids + i, sb); elf_putnote(NT_FPREGSET, elf_note_fpregset, tids + i, sb); elf_putnote(NT_THRMISC, elf_note_thrmisc, tids + i, sb); #if defined(__i386__) || defined(__amd64__) elf_putnote(NT_X86_XSTATE, elf_note_x86_xstate, tids + i, sb); #endif #if defined(__powerpc__) elf_putnote(NT_PPC_VMX, elf_note_powerpc_vmx, tids + i, sb); #endif } #ifndef ELFCORE_COMPAT_32 elf_putnote(NT_PROCSTAT_PROC, elf_note_procstat_proc, &pid, sb); elf_putnote(NT_PROCSTAT_FILES, elf_note_procstat_files, &pid, sb); elf_putnote(NT_PROCSTAT_VMMAP, elf_note_procstat_vmmap, &pid, sb); elf_putnote(NT_PROCSTAT_GROUPS, elf_note_procstat_groups, &pid, sb); elf_putnote(NT_PROCSTAT_UMASK, elf_note_procstat_umask, &pid, sb); elf_putnote(NT_PROCSTAT_RLIMIT, elf_note_procstat_rlimit, &pid, sb); elf_putnote(NT_PROCSTAT_OSREL, elf_note_procstat_osrel, &pid, sb); elf_putnote(NT_PROCSTAT_PSSTRINGS, elf_note_procstat_psstrings, &pid, sb); elf_putnote(NT_PROCSTAT_AUXV, elf_note_procstat_auxv, &pid, sb); #endif size = sbuf_end_section(sb, old_len, 1, 0); if (size == -1) err(1, "sbuf_end_section"); free(tids); *sizep = size; } /* * Emit one note section to sbuf. */ static void elf_putnote(int type, notefunc_t notefunc, void *arg, struct sbuf *sb) { Elf_Note note; size_t descsz; ssize_t old_len; void *desc; desc = notefunc(arg, &descsz); note.n_namesz = 8; /* strlen("FreeBSD") + 1 */ note.n_descsz = descsz; note.n_type = type; sbuf_bcat(sb, ¬e, sizeof(note)); sbuf_start_section(sb, &old_len); sbuf_bcat(sb, "FreeBSD", note.n_namesz); sbuf_end_section(sb, old_len, sizeof(Elf32_Size), 0); if (descsz == 0) return; sbuf_start_section(sb, &old_len); sbuf_bcat(sb, desc, descsz); sbuf_end_section(sb, old_len, sizeof(Elf32_Size), 0); free(desc); } /* * Generate the ELF coredump header. */ static void elf_puthdr(pid_t pid, vm_map_entry_t map, void *hdr, size_t hdrsize, size_t notesz, size_t segoff, int numsegs) { Elf_Ehdr *ehdr; Elf_Phdr *phdr; struct phdr_closure phc; ehdr = (Elf_Ehdr *)hdr; phdr = (Elf_Phdr *)((char *)hdr + sizeof(Elf_Ehdr)); ehdr->e_ident[EI_MAG0] = ELFMAG0; ehdr->e_ident[EI_MAG1] = ELFMAG1; ehdr->e_ident[EI_MAG2] = ELFMAG2; ehdr->e_ident[EI_MAG3] = ELFMAG3; ehdr->e_ident[EI_CLASS] = ELF_CLASS; ehdr->e_ident[EI_DATA] = ELF_DATA; ehdr->e_ident[EI_VERSION] = EV_CURRENT; ehdr->e_ident[EI_OSABI] = ELFOSABI_FREEBSD; ehdr->e_ident[EI_ABIVERSION] = 0; ehdr->e_ident[EI_PAD] = 0; ehdr->e_type = ET_CORE; ehdr->e_machine = ELF_ARCH; ehdr->e_version = EV_CURRENT; ehdr->e_entry = 0; ehdr->e_phoff = sizeof(Elf_Ehdr); ehdr->e_flags = 0; ehdr->e_ehsize = sizeof(Elf_Ehdr); ehdr->e_phentsize = sizeof(Elf_Phdr); ehdr->e_phnum = numsegs + 1; ehdr->e_shentsize = sizeof(Elf_Shdr); ehdr->e_shnum = 0; ehdr->e_shstrndx = SHN_UNDEF; /* * Fill in the program header entries. */ /* The note segement. */ phdr->p_type = PT_NOTE; phdr->p_offset = hdrsize; phdr->p_vaddr = 0; phdr->p_paddr = 0; phdr->p_filesz = notesz; phdr->p_memsz = 0; phdr->p_flags = PF_R; phdr->p_align = sizeof(Elf32_Size); phdr++; /* All the writable segments from the program. */ phc.phdr = phdr; phc.offset = segoff; each_writable_segment(map, cb_put_phdr, &phc); } /* * Free the memory map. */ static void freemap(vm_map_entry_t map) { while (map != NULL) { vm_map_entry_t next = map->next; free(map); map = next; } } /* * Read the process's memory map using kinfo_getvmmap(), and return a list of * VM map entries. Only the non-device read/writable segments are * returned. The map entries in the list aren't fully filled in; only * the items we need are present. */ static vm_map_entry_t readmap(pid_t pid) { vm_map_entry_t ent, *linkp, map; struct kinfo_vmentry *vmentl, *kve; int i, nitems; vmentl = kinfo_getvmmap(pid, &nitems); if (vmentl == NULL) err(1, "cannot retrieve mappings for %u process", pid); map = NULL; linkp = ↦ for (i = 0; i < nitems; i++) { kve = &vmentl[i]; /* * Ignore 'malformed' segments or ones representing memory * mapping with MAP_NOCORE on. * If the 'full' support is disabled, just dump the most * meaningful data segments. */ if ((kve->kve_protection & KVME_PROT_READ) == 0 || (kve->kve_flags & KVME_FLAG_NOCOREDUMP) != 0 || kve->kve_type == KVME_TYPE_DEAD || kve->kve_type == KVME_TYPE_UNKNOWN || ((pflags & PFLAGS_FULL) == 0 && kve->kve_type != KVME_TYPE_DEFAULT && kve->kve_type != KVME_TYPE_VNODE && - kve->kve_type != KVME_TYPE_SWAP)) + kve->kve_type != KVME_TYPE_SWAP && + kve->kve_type != KVME_TYPE_PHYS)) continue; ent = calloc(1, sizeof(*ent)); if (ent == NULL) errx(1, "out of memory"); ent->start = (vm_offset_t)kve->kve_start; ent->end = (vm_offset_t)kve->kve_end; ent->protection = VM_PROT_READ | VM_PROT_WRITE; if ((kve->kve_protection & KVME_PROT_EXEC) != 0) ent->protection |= VM_PROT_EXECUTE; *linkp = ent; linkp = &ent->next; } free(vmentl); return (map); } /* * Miscellaneous note out functions. */ static void * elf_note_prpsinfo(void *arg, size_t *sizep) { pid_t pid; elfcore_prpsinfo_t *psinfo; struct kinfo_proc kip; size_t len; int name[4]; pid = *(pid_t *)arg; psinfo = calloc(1, sizeof(*psinfo)); if (psinfo == NULL) errx(1, "out of memory"); psinfo->pr_version = PRPSINFO_VERSION; psinfo->pr_psinfosz = sizeof(*psinfo); name[0] = CTL_KERN; name[1] = KERN_PROC; name[2] = KERN_PROC_PID; name[3] = pid; len = sizeof(kip); if (sysctl(name, 4, &kip, &len, NULL, 0) == -1) err(1, "kern.proc.pid.%u", pid); if (kip.ki_pid != pid) err(1, "kern.proc.pid.%u", pid); strncpy(psinfo->pr_fname, kip.ki_comm, MAXCOMLEN); strncpy(psinfo->pr_psargs, psinfo->pr_fname, PRARGSZ); *sizep = sizeof(*psinfo); return (psinfo); } static void * elf_note_prstatus(void *arg, size_t *sizep) { lwpid_t tid; elfcore_prstatus_t *status; struct reg greg; tid = *(lwpid_t *)arg; status = calloc(1, sizeof(*status)); if (status == NULL) errx(1, "out of memory"); status->pr_version = PRSTATUS_VERSION; status->pr_statussz = sizeof(*status); status->pr_gregsetsz = sizeof(elfcore_gregset_t); status->pr_fpregsetsz = sizeof(elfcore_fpregset_t); status->pr_osreldate = __FreeBSD_version; status->pr_pid = tid; ptrace(PT_GETREGS, tid, (void *)&greg, 0); elf_convert_gregset(&status->pr_reg, &greg); *sizep = sizeof(*status); return (status); } static void * elf_note_fpregset(void *arg, size_t *sizep) { lwpid_t tid; elfcore_fpregset_t *fpregset; fpregset_t fpreg; tid = *(lwpid_t *)arg; fpregset = calloc(1, sizeof(*fpregset)); if (fpregset == NULL) errx(1, "out of memory"); ptrace(PT_GETFPREGS, tid, (void *)&fpreg, 0); elf_convert_fpregset(fpregset, &fpreg); *sizep = sizeof(*fpregset); return (fpregset); } static void * elf_note_thrmisc(void *arg, size_t *sizep) { lwpid_t tid; struct ptrace_lwpinfo lwpinfo; thrmisc_t *thrmisc; tid = *(lwpid_t *)arg; thrmisc = calloc(1, sizeof(*thrmisc)); if (thrmisc == NULL) errx(1, "out of memory"); ptrace(PT_LWPINFO, tid, (void *)&lwpinfo, sizeof(lwpinfo)); memset(&thrmisc->_pad, 0, sizeof(thrmisc->_pad)); strcpy(thrmisc->pr_tname, lwpinfo.pl_tdname); *sizep = sizeof(*thrmisc); return (thrmisc); } #if defined(__i386__) || defined(__amd64__) static void * elf_note_x86_xstate(void *arg, size_t *sizep) { lwpid_t tid; char *xstate; static bool xsave_checked = false; static struct ptrace_xstate_info info; tid = *(lwpid_t *)arg; if (!xsave_checked) { if (ptrace(PT_GETXSTATE_INFO, tid, (void *)&info, sizeof(info)) != 0) info.xsave_len = 0; xsave_checked = true; } if (info.xsave_len == 0) { *sizep = 0; return (NULL); } xstate = calloc(1, info.xsave_len); ptrace(PT_GETXSTATE, tid, xstate, 0); *(uint64_t *)(xstate + X86_XSTATE_XCR0_OFFSET) = info.xsave_mask; *sizep = info.xsave_len; return (xstate); } #endif #if defined(__powerpc__) static void * elf_note_powerpc_vmx(void *arg, size_t *sizep) { lwpid_t tid; struct vmxreg *vmx; static bool has_vmx = true; struct vmxreg info; tid = *(lwpid_t *)arg; if (has_vmx) { if (ptrace(PT_GETVRREGS, tid, (void *)&info, sizeof(info)) != 0) has_vmx = false; } if (!has_vmx) { *sizep = 0; return (NULL); } vmx = calloc(1, sizeof(*vmx)); memcpy(vmx, &info, sizeof(*vmx)); *sizep = sizeof(*vmx); return (vmx); } #endif static void * procstat_sysctl(void *arg, int what, size_t structsz, size_t *sizep) { size_t len; pid_t pid; int name[4], structsize; void *buf, *p; pid = *(pid_t *)arg; structsize = structsz; name[0] = CTL_KERN; name[1] = KERN_PROC; name[2] = what; name[3] = pid; len = 0; if (sysctl(name, 4, NULL, &len, NULL, 0) == -1) err(1, "kern.proc.%d.%u", what, pid); buf = calloc(1, sizeof(structsize) + len * 4 / 3); if (buf == NULL) errx(1, "out of memory"); bcopy(&structsize, buf, sizeof(structsize)); p = (char *)buf + sizeof(structsize); if (sysctl(name, 4, p, &len, NULL, 0) == -1) err(1, "kern.proc.%d.%u", what, pid); *sizep = sizeof(structsize) + len; return (buf); } static void * elf_note_procstat_proc(void *arg, size_t *sizep) { return (procstat_sysctl(arg, KERN_PROC_PID | KERN_PROC_INC_THREAD, sizeof(struct kinfo_proc), sizep)); } static void * elf_note_procstat_files(void *arg, size_t *sizep) { return (procstat_sysctl(arg, KERN_PROC_FILEDESC, sizeof(struct kinfo_file), sizep)); } static void * elf_note_procstat_vmmap(void *arg, size_t *sizep) { return (procstat_sysctl(arg, KERN_PROC_VMMAP, sizeof(struct kinfo_vmentry), sizep)); } static void * elf_note_procstat_groups(void *arg, size_t *sizep) { return (procstat_sysctl(arg, KERN_PROC_GROUPS, sizeof(gid_t), sizep)); } static void * elf_note_procstat_umask(void *arg, size_t *sizep) { return (procstat_sysctl(arg, KERN_PROC_UMASK, sizeof(u_short), sizep)); } static void * elf_note_procstat_osrel(void *arg, size_t *sizep) { return (procstat_sysctl(arg, KERN_PROC_OSREL, sizeof(int), sizep)); } static void * elf_note_procstat_psstrings(void *arg, size_t *sizep) { return (procstat_sysctl(arg, KERN_PROC_PS_STRINGS, sizeof(vm_offset_t), sizep)); } static void * elf_note_procstat_auxv(void *arg, size_t *sizep) { return (procstat_sysctl(arg, KERN_PROC_AUXV, sizeof(Elf_Auxinfo), sizep)); } static void * elf_note_procstat_rlimit(void *arg, size_t *sizep) { pid_t pid; size_t len; int i, name[5], structsize; void *buf, *p; pid = *(pid_t *)arg; structsize = sizeof(struct rlimit) * RLIM_NLIMITS; buf = calloc(1, sizeof(structsize) + structsize); if (buf == NULL) errx(1, "out of memory"); bcopy(&structsize, buf, sizeof(structsize)); p = (char *)buf + sizeof(structsize); name[0] = CTL_KERN; name[1] = KERN_PROC; name[2] = KERN_PROC_RLIMIT; name[3] = pid; len = sizeof(struct rlimit); for (i = 0; i < RLIM_NLIMITS; i++) { name[4] = i; if (sysctl(name, 5, p, &len, NULL, 0) == -1) err(1, "kern.proc.rlimit.%u", pid); if (len != sizeof(struct rlimit)) errx(1, "kern.proc.rlimit.%u: short read", pid); p += len; } *sizep = sizeof(structsize) + structsize; return (buf); } struct dumpers __elfN(dump) = { elf_ident, elf_coredump }; TEXT_SET(dumpset, __elfN(dump)); Index: projects/building-blocks/usr.sbin/flowctl/flowctl.c =================================================================== --- projects/building-blocks/usr.sbin/flowctl/flowctl.c (revision 278776) +++ projects/building-blocks/usr.sbin/flowctl/flowctl.c (revision 278777) @@ -1,424 +1,426 @@ /*- * Copyright (c) 2004-2005 Gleb Smirnoff * Copyright (c) 2001-2003 Roman V. Palagin * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * * $SourceForge: flowctl.c,v 1.15 2004/08/31 20:24:58 glebius Exp $ */ #ifndef lint static const char rcs_id[] = "@(#) $FreeBSD$"; #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define CISCO_SH_FLOW_HEADER "SrcIf SrcIPaddress " \ "DstIf DstIPaddress Pr SrcP DstP Pkts\n" #define CISCO_SH_FLOW "%-13s %-15s %-13s %-15s %2u %4.4x %4.4x %6lu\n" /* human-readable IPv4 header */ #define CISCO_SH_FLOW_HHEADER "SrcIf SrcIPaddress " \ "DstIf DstIPaddress Proto SrcPort DstPort Pkts\n" #define CISCO_SH_FLOW_H "%-13s %-15s %-13s %-15s %5u %8d %8d %8lu\n" #define CISCO_SH_FLOW6_HEADER "SrcIf SrcIPaddress " \ "DstIf DstIPaddress Pr SrcP DstP Pkts\n" #define CISCO_SH_FLOW6 "%-13s %-30s %-13s %-30s %2u %4.4x %4.4x %6lu\n" /* Human-readable IPv6 headers */ #define CISCO_SH_FLOW6_HHEADER "SrcIf SrcIPaddress " \ "DstIf DstIPaddress Proto SrcPort DstPort Pkts\n" #define CISCO_SH_FLOW6_H "%-13s %-36s %-13s %-36s %5u %8d %8d %8lu\n" #define CISCO_SH_VERB_FLOW_HEADER "SrcIf SrcIPaddress " \ "DstIf DstIPaddress Pr TOS Flgs Pkts\n" \ "Port Msk AS Port Msk AS NextHop B/Pk Active\n" #define CISCO_SH_VERB_FLOW "%-14s %-15s %-14s %-15s %2u %3x %4x %6lu\n" \ "%4.4x /%-2u %-5u %4.4x /%-2u %-5u %-15s %9u %8u\n\n" #define CISCO_SH_VERB_FLOW6_HEADER "SrcIf SrcIPaddress " \ "DstIf DstIPaddress Pr TOS Flgs Pkts\n" \ "Port Msk AS Port Msk AS NextHop B/Pk Active\n" #define CISCO_SH_VERB_FLOW6 "%-14s %-30s %-14s %-30s %2u %3x %4x %6lu\n" \ "%4.4x /%-2u %-5u %4.4x /%-2u %-5u %-30s %9u %8u\n\n" #ifdef INET static void flow_cache_print(struct ngnf_show_header *resp); static void flow_cache_print_verbose(struct ngnf_show_header *resp); #endif #ifdef INET6 static void flow_cache_print6(struct ngnf_show_header *resp); static void flow_cache_print6_verbose(struct ngnf_show_header *resp); #endif static void ctl_show(int, char **); #if defined(INET) || defined(INET6) static void do_show(int, void (*func)(struct ngnf_show_header *)); #endif static void help(void); static void execute_command(int, char **); struct ip_ctl_cmd { char *cmd_name; void (*cmd_func)(int argc, char **argv); }; struct ip_ctl_cmd cmds[] = { {"show", ctl_show}, {NULL, NULL}, }; int cs, human = 0; char *ng_path; int main(int argc, char **argv) { int c; char sname[NG_NODESIZ]; int rcvbuf = SORCVBUF_SIZE; /* parse options */ while ((c = getopt(argc, argv, "d:")) != -1) { switch (c) { case 'd': /* set libnetgraph debug level. */ NgSetDebug(atoi(optarg)); break; } } argc -= optind; argv += optind; ng_path = argv[0]; if (ng_path == NULL || (strlen(ng_path) > NG_PATHSIZ)) help(); argc--; argv++; /* create control socket. */ snprintf(sname, sizeof(sname), "flowctl%i", getpid()); if (NgMkSockNode(sname, &cs, NULL) == -1) err(1, "NgMkSockNode"); /* set receive buffer size */ if (setsockopt(cs, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(int)) == -1) err(1, "setsockopt(SOL_SOCKET, SO_RCVBUF)"); /* parse and execute command */ execute_command(argc, argv); close(cs); exit(0); } static void execute_command(int argc, char **argv) { int cindex = -1; int i; if (!argc) help(); for (i = 0; cmds[i].cmd_name != NULL; i++) if (!strncmp(argv[0], cmds[i].cmd_name, strlen(argv[0]))) { if (cindex != -1) errx(1, "ambiguous command: %s", argv[0]); cindex = i; } if (cindex == -1) errx(1, "bad command: %s", argv[0]); argc--; argv++; (*cmds[cindex].cmd_func)(argc, argv); } static void ctl_show(int argc, char **argv) { int ipv4, ipv6, verbose = 0; ipv4 = feature_present("inet"); ipv6 = feature_present("inet6"); if (argc > 0 && !strncmp(argv[0], "ipv4", 4)) { ipv6 = 0; argc--; argv++; } if (argc > 0 && !strncmp(argv[0], "ipv6", 4)) { ipv4 = 0; argc--; argv++; } if (argc > 0 && !strncmp(argv[0], "verbose", strlen(argv[0]))) verbose = 1; if (argc > 0 && !strncmp(argv[0], "human", strlen(argv[0]))) human = 1; #ifdef INET if (ipv4) { if (verbose) do_show(4, &flow_cache_print_verbose); else do_show(4, &flow_cache_print); } #endif #ifdef INET6 if (ipv6) { if (verbose) do_show(6, &flow_cache_print6_verbose); else do_show(6, &flow_cache_print6); } #endif } #if defined(INET) || defined(INET6) static void do_show(int version, void (*func)(struct ngnf_show_header *)) { - struct ng_mesg ng_mesg[SORCVBUF_SIZE]; + char buf[SORCVBUF_SIZE]; + struct ng_mesg *ng_mesg; struct ngnf_show_header req, *resp; int token, nread; + ng_mesg = (struct ng_mesg *)buf; req.version = version; req.hash_id = req.list_id = 0; for (;;) { /* request set of accounting records */ token = NgSendMsg(cs, ng_path, NGM_NETFLOW_COOKIE, NGM_NETFLOW_SHOW, (void *)&req, sizeof(req)); if (token == -1) err(1, "NgSendMsg(NGM_NETFLOW_SHOW)"); /* read reply */ nread = NgRecvMsg(cs, ng_mesg, SORCVBUF_SIZE, NULL); if (nread == -1) err(1, "NgRecvMsg() failed"); if (ng_mesg->header.token != token) err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): token mismatch"); resp = (struct ngnf_show_header *)ng_mesg->data; if ((ng_mesg->header.arglen < (sizeof(*resp))) || (ng_mesg->header.arglen < (sizeof(*resp) + (resp->nentries * sizeof(struct flow_entry_data))))) err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): arglen too small"); (*func)(resp); if (resp->hash_id != 0) req.hash_id = resp->hash_id; else break; req.list_id = resp->list_id; } } #endif #ifdef INET static void flow_cache_print(struct ngnf_show_header *resp) { struct flow_entry_data *fle; char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN]; char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; int i; if (resp->version != 4) errx(EX_SOFTWARE, "%s: version mismatch: %u", __func__, resp->version); if (resp->nentries > 0) printf(human ? CISCO_SH_FLOW_HHEADER : CISCO_SH_FLOW_HEADER); fle = (struct flow_entry_data *)(resp + 1); for (i = 0; i < resp->nentries; i++, fle++) { inet_ntop(AF_INET, &fle->r.r_src, src, sizeof(src)); inet_ntop(AF_INET, &fle->r.r_dst, dst, sizeof(dst)); printf(human ? CISCO_SH_FLOW_H : CISCO_SH_FLOW, if_indextoname(fle->fle_i_ifx, src_if), src, if_indextoname(fle->fle_o_ifx, dst_if), dst, fle->r.r_ip_p, ntohs(fle->r.r_sport), ntohs(fle->r.r_dport), fle->packets); } } #endif #ifdef INET6 static void flow_cache_print6(struct ngnf_show_header *resp) { struct flow6_entry_data *fle6; char src6[INET6_ADDRSTRLEN], dst6[INET6_ADDRSTRLEN]; char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; int i; if (resp->version != 6) errx(EX_SOFTWARE, "%s: version mismatch: %u", __func__, resp->version); if (resp->nentries > 0) printf(human ? CISCO_SH_FLOW6_HHEADER : CISCO_SH_FLOW6_HEADER); fle6 = (struct flow6_entry_data *)(resp + 1); for (i = 0; i < resp->nentries; i++, fle6++) { inet_ntop(AF_INET6, &fle6->r.src.r_src6, src6, sizeof(src6)); inet_ntop(AF_INET6, &fle6->r.dst.r_dst6, dst6, sizeof(dst6)); printf(human ? CISCO_SH_FLOW6_H : CISCO_SH_FLOW6, if_indextoname(fle6->fle_i_ifx, src_if), src6, if_indextoname(fle6->fle_o_ifx, dst_if), dst6, fle6->r.r_ip_p, ntohs(fle6->r.r_sport), ntohs(fle6->r.r_dport), fle6->packets); } } #endif #ifdef INET static void flow_cache_print_verbose(struct ngnf_show_header *resp) { struct flow_entry_data *fle; char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN], next[INET_ADDRSTRLEN]; char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; int i; if (resp->version != 4) errx(EX_SOFTWARE, "%s: version mismatch: %u", __func__, resp->version); printf(CISCO_SH_VERB_FLOW_HEADER); fle = (struct flow_entry_data *)(resp + 1); for (i = 0; i < resp->nentries; i++, fle++) { inet_ntop(AF_INET, &fle->r.r_src, src, sizeof(src)); inet_ntop(AF_INET, &fle->r.r_dst, dst, sizeof(dst)); inet_ntop(AF_INET, &fle->next_hop, next, sizeof(next)); printf(CISCO_SH_VERB_FLOW, if_indextoname(fle->fle_i_ifx, src_if), src, if_indextoname(fle->fle_o_ifx, dst_if), dst, fle->r.r_ip_p, fle->r.r_tos, fle->tcp_flags, fle->packets, ntohs(fle->r.r_sport), fle->src_mask, 0, ntohs(fle->r.r_dport), fle->dst_mask, 0, next, (u_int)(fle->bytes / fle->packets), 0); } } #endif #ifdef INET6 static void flow_cache_print6_verbose(struct ngnf_show_header *resp) { struct flow6_entry_data *fle6; char src6[INET6_ADDRSTRLEN], dst6[INET6_ADDRSTRLEN], next6[INET6_ADDRSTRLEN]; char src_if[IFNAMSIZ], dst_if[IFNAMSIZ]; int i; if (resp->version != 6) errx(EX_SOFTWARE, "%s: version mismatch: %u", __func__, resp->version); printf(CISCO_SH_VERB_FLOW6_HEADER); fle6 = (struct flow6_entry_data *)(resp + 1); for (i = 0; i < resp->nentries; i++, fle6++) { inet_ntop(AF_INET6, &fle6->r.src.r_src6, src6, sizeof(src6)); inet_ntop(AF_INET6, &fle6->r.dst.r_dst6, dst6, sizeof(dst6)); inet_ntop(AF_INET6, &fle6->n.next_hop6, next6, sizeof(next6)); printf(CISCO_SH_VERB_FLOW6, if_indextoname(fle6->fle_i_ifx, src_if), src6, if_indextoname(fle6->fle_o_ifx, dst_if), dst6, fle6->r.r_ip_p, fle6->r.r_tos, fle6->tcp_flags, fle6->packets, ntohs(fle6->r.r_sport), fle6->src_mask, 0, ntohs(fle6->r.r_dport), fle6->dst_mask, 0, next6, (u_int)(fle6->bytes / fle6->packets), 0); } } #endif static void help(void) { extern char *__progname; fprintf(stderr, "usage: %s [-d level] nodename command\n", __progname); exit (0); } Index: projects/building-blocks/usr.sbin/freebsd-update/freebsd-update.sh =================================================================== --- projects/building-blocks/usr.sbin/freebsd-update/freebsd-update.sh (revision 278776) +++ projects/building-blocks/usr.sbin/freebsd-update/freebsd-update.sh (revision 278777) @@ -1,3268 +1,3268 @@ #!/bin/sh #- # Copyright 2004-2007 Colin Percival # All rights reserved # # Redistribution and use in source and binary forms, with or without # modification, are permitted providing 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. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. # $FreeBSD$ #### Usage function -- called from command-line handling code. # Usage instructions. Options not listed: # --debug -- don't filter output from utilities # --no-stats -- don't show progress statistics while fetching files usage () { cat < ${LINE}" exit 1 fi done < ${CONFFILE} # Merge the settings read from the configuration file with those # provided at the command line. mergeconfig } # Provide some default parameters default_params () { # Save any parameters already configured, and clear the slate saveconfig nullconfig # Default configurations config_WorkDir /var/db/freebsd-update config_MailTo root config_AllowAdd yes config_AllowDelete yes config_KeepModifiedMetadata yes config_BaseDir / config_VerboseLevel stats config_StrictComponents no config_BackupKernel yes config_BackupKernelDir /boot/kernel.old config_BackupKernelSymbolFiles no # Merge these defaults into the earlier-configured settings mergeconfig } # Set utility output filtering options, based on ${VERBOSELEVEL} fetch_setup_verboselevel () { case ${VERBOSELEVEL} in debug) QUIETREDIR="/dev/stderr" QUIETFLAG=" " STATSREDIR="/dev/stderr" DDSTATS=".." XARGST="-t" NDEBUG=" " ;; nostats) QUIETREDIR="" QUIETFLAG="" STATSREDIR="/dev/null" DDSTATS=".." XARGST="" NDEBUG="" ;; stats) QUIETREDIR="/dev/null" QUIETFLAG="-q" STATSREDIR="/dev/stdout" DDSTATS="" XARGST="" NDEBUG="-n" ;; esac } # Perform sanity checks and set some final parameters # in preparation for fetching files. Figure out which # set of updates should be downloaded: If the user is # running *-p[0-9]+, strip off the last part; if the # user is running -SECURITY, call it -RELEASE. Chdir # into the working directory. fetchupgrade_check_params () { export HTTP_USER_AGENT="freebsd-update (${COMMAND}, `uname -r`)" _SERVERNAME_z=\ "SERVERNAME must be given via command line or configuration file." _KEYPRINT_z="Key must be given via -k option or configuration file." _KEYPRINT_bad="Invalid key fingerprint: " _WORKDIR_bad="Directory does not exist or is not writable: " _WORKDIR_bad2="Directory is not on a persistent filesystem: " if [ -z "${SERVERNAME}" ]; then echo -n "`basename $0`: " echo "${_SERVERNAME_z}" exit 1 fi if [ -z "${KEYPRINT}" ]; then echo -n "`basename $0`: " echo "${_KEYPRINT_z}" exit 1 fi if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then echo -n "`basename $0`: " echo -n "${_KEYPRINT_bad}" echo ${KEYPRINT} exit 1 fi if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then echo -n "`basename $0`: " echo -n "${_WORKDIR_bad}" echo ${WORKDIR} exit 1 fi case `df -T ${WORKDIR}` in */dev/md[0-9]* | *tmpfs*) echo -n "`basename $0`: " echo -n "${_WORKDIR_bad2}" echo ${WORKDIR} exit 1 ;; esac chmod 700 ${WORKDIR} cd ${WORKDIR} || exit 1 # Generate release number. The s/SECURITY/RELEASE/ bit exists # to provide an upgrade path for FreeBSD Update 1.x users, since # the kernels provided by FreeBSD Update 1.x are always labelled # as X.Y-SECURITY. RELNUM=`uname -r | sed -E 's,-p[0-9]+,,' | sed -E 's,-SECURITY,-RELEASE,'` ARCH=`uname -m` FETCHDIR=${RELNUM}/${ARCH} PATCHDIR=${RELNUM}/${ARCH}/bp # Figure out what directory contains the running kernel BOOTFILE=`sysctl -n kern.bootfile` KERNELDIR=${BOOTFILE%/kernel} if ! [ -d ${KERNELDIR} ]; then echo "Cannot identify running kernel" exit 1 fi # Figure out what kernel configuration is running. We start with # the output of `uname -i`, and then make the following adjustments: # 1. Replace "SMP-GENERIC" with "SMP". Why the SMP kernel config # file says "ident SMP-GENERIC", I don't know... # 2. If the kernel claims to be GENERIC _and_ ${ARCH} is "amd64" # _and_ `sysctl kern.version` contains a line which ends "/SMP", then # we're running an SMP kernel. This mis-identification is a bug # which was fixed in 6.2-STABLE. KERNCONF=`uname -i` if [ ${KERNCONF} = "SMP-GENERIC" ]; then KERNCONF=SMP fi if [ ${KERNCONF} = "GENERIC" ] && [ ${ARCH} = "amd64" ]; then if sysctl kern.version | grep -qE '/SMP$'; then KERNCONF=SMP fi fi # Define some paths BSPATCH=/usr/bin/bspatch SHA256=/sbin/sha256 PHTTPGET=/usr/libexec/phttpget # Set up variables relating to VERBOSELEVEL fetch_setup_verboselevel # Construct a unique name from ${BASEDIR} BDHASH=`echo ${BASEDIR} | sha256 -q` } # Perform sanity checks etc. before fetching updates. fetch_check_params () { fetchupgrade_check_params if ! [ -z "${TARGETRELEASE}" ]; then echo -n "`basename $0`: " echo -n "-r option is meaningless with 'fetch' command. " echo "(Did you mean 'upgrade' instead?)" exit 1 fi } # Perform sanity checks etc. before fetching upgrades. upgrade_check_params () { fetchupgrade_check_params # Unless set otherwise, we're upgrading to the same kernel config. NKERNCONF=${KERNCONF} # We need TARGETRELEASE set _TARGETRELEASE_z="Release target must be specified via -r option." if [ -z "${TARGETRELEASE}" ]; then echo -n "`basename $0`: " echo "${_TARGETRELEASE_z}" exit 1 fi # The target release should be != the current release. if [ "${TARGETRELEASE}" = "${RELNUM}" ]; then echo -n "`basename $0`: " echo "Cannot upgrade from ${RELNUM} to itself" exit 1 fi # Turning off AllowAdd or AllowDelete is a bad idea for upgrades. if [ "${ALLOWADD}" = "no" ]; then echo -n "`basename $0`: " echo -n "WARNING: \"AllowAdd no\" is a bad idea " echo "when upgrading between releases." echo fi if [ "${ALLOWDELETE}" = "no" ]; then echo -n "`basename $0`: " echo -n "WARNING: \"AllowDelete no\" is a bad idea " echo "when upgrading between releases." echo fi # Set EDITOR to /usr/bin/vi if it isn't already set : ${EDITOR:='/usr/bin/vi'} } # Perform sanity checks and set some final parameters in # preparation for installing updates. install_check_params () { # Check that we are root. All sorts of things won't work otherwise. if [ `id -u` != 0 ]; then echo "You must be root to run this." exit 1 fi # Check that securelevel <= 0. Otherwise we can't update schg files. if [ `sysctl -n kern.securelevel` -gt 0 ]; then echo "Updates cannot be installed when the system securelevel" echo "is greater than zero." exit 1 fi # Check that we have a working directory _WORKDIR_bad="Directory does not exist or is not writable: " if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then echo -n "`basename $0`: " echo -n "${_WORKDIR_bad}" echo ${WORKDIR} exit 1 fi cd ${WORKDIR} || exit 1 # Construct a unique name from ${BASEDIR} BDHASH=`echo ${BASEDIR} | sha256 -q` # Check that we have updates ready to install if ! [ -L ${BDHASH}-install ]; then echo "No updates are available to install." echo "Run '$0 fetch' first." exit 1 fi if ! [ -f ${BDHASH}-install/INDEX-OLD ] || ! [ -f ${BDHASH}-install/INDEX-NEW ]; then echo "Update manifest is corrupt -- this should never happen." echo "Re-run '$0 fetch'." exit 1 fi # Figure out what directory contains the running kernel BOOTFILE=`sysctl -n kern.bootfile` KERNELDIR=${BOOTFILE%/kernel} if ! [ -d ${KERNELDIR} ]; then echo "Cannot identify running kernel" exit 1 fi } # Perform sanity checks and set some final parameters in # preparation for UNinstalling updates. rollback_check_params () { # Check that we are root. All sorts of things won't work otherwise. if [ `id -u` != 0 ]; then echo "You must be root to run this." exit 1 fi # Check that we have a working directory _WORKDIR_bad="Directory does not exist or is not writable: " if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then echo -n "`basename $0`: " echo -n "${_WORKDIR_bad}" echo ${WORKDIR} exit 1 fi cd ${WORKDIR} || exit 1 # Construct a unique name from ${BASEDIR} BDHASH=`echo ${BASEDIR} | sha256 -q` # Check that we have updates ready to rollback if ! [ -L ${BDHASH}-rollback ]; then echo "No rollback directory found." exit 1 fi if ! [ -f ${BDHASH}-rollback/INDEX-OLD ] || ! [ -f ${BDHASH}-rollback/INDEX-NEW ]; then echo "Update manifest is corrupt -- this should never happen." exit 1 fi } # Perform sanity checks and set some final parameters # in preparation for comparing the system against the # published index. Figure out which index we should # compare against: If the user is running *-p[0-9]+, # strip off the last part; if the user is running # -SECURITY, call it -RELEASE. Chdir into the working # directory. IDS_check_params () { export HTTP_USER_AGENT="freebsd-update (${COMMAND}, `uname -r`)" _SERVERNAME_z=\ "SERVERNAME must be given via command line or configuration file." _KEYPRINT_z="Key must be given via -k option or configuration file." _KEYPRINT_bad="Invalid key fingerprint: " _WORKDIR_bad="Directory does not exist or is not writable: " if [ -z "${SERVERNAME}" ]; then echo -n "`basename $0`: " echo "${_SERVERNAME_z}" exit 1 fi if [ -z "${KEYPRINT}" ]; then echo -n "`basename $0`: " echo "${_KEYPRINT_z}" exit 1 fi if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then echo -n "`basename $0`: " echo -n "${_KEYPRINT_bad}" echo ${KEYPRINT} exit 1 fi if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then echo -n "`basename $0`: " echo -n "${_WORKDIR_bad}" echo ${WORKDIR} exit 1 fi cd ${WORKDIR} || exit 1 # Generate release number. The s/SECURITY/RELEASE/ bit exists # to provide an upgrade path for FreeBSD Update 1.x users, since # the kernels provided by FreeBSD Update 1.x are always labelled # as X.Y-SECURITY. RELNUM=`uname -r | sed -E 's,-p[0-9]+,,' | sed -E 's,-SECURITY,-RELEASE,'` ARCH=`uname -m` FETCHDIR=${RELNUM}/${ARCH} PATCHDIR=${RELNUM}/${ARCH}/bp # Figure out what directory contains the running kernel BOOTFILE=`sysctl -n kern.bootfile` KERNELDIR=${BOOTFILE%/kernel} if ! [ -d ${KERNELDIR} ]; then echo "Cannot identify running kernel" exit 1 fi # Figure out what kernel configuration is running. We start with # the output of `uname -i`, and then make the following adjustments: # 1. Replace "SMP-GENERIC" with "SMP". Why the SMP kernel config # file says "ident SMP-GENERIC", I don't know... # 2. If the kernel claims to be GENERIC _and_ ${ARCH} is "amd64" # _and_ `sysctl kern.version` contains a line which ends "/SMP", then # we're running an SMP kernel. This mis-identification is a bug # which was fixed in 6.2-STABLE. KERNCONF=`uname -i` if [ ${KERNCONF} = "SMP-GENERIC" ]; then KERNCONF=SMP fi if [ ${KERNCONF} = "GENERIC" ] && [ ${ARCH} = "amd64" ]; then if sysctl kern.version | grep -qE '/SMP$'; then KERNCONF=SMP fi fi # Define some paths SHA256=/sbin/sha256 PHTTPGET=/usr/libexec/phttpget # Set up variables relating to VERBOSELEVEL fetch_setup_verboselevel } #### Core functionality -- the actual work gets done here # Use an SRV query to pick a server. If the SRV query doesn't provide # a useful answer, use the server name specified by the user. # Put another way... look up _http._tcp.${SERVERNAME} and pick a server # from that; or if no servers are returned, use ${SERVERNAME}. # This allows a user to specify "portsnap.freebsd.org" (in which case # portsnap will select one of the mirrors) or "portsnap5.tld.freebsd.org" # (in which case portsnap will use that particular server, since there # won't be an SRV entry for that name). # # We ignore the Port field, since we are always going to use port 80. # Fetch the mirror list, but do not pick a mirror yet. Returns 1 if # no mirrors are available for any reason. fetch_pick_server_init () { : > serverlist_tried # Check that host(1) exists (i.e., that the system wasn't built with the # WITHOUT_BIND set) and don't try to find a mirror if it doesn't exist. if ! which -s host; then : > serverlist_full return 1 fi echo -n "Looking up ${SERVERNAME} mirrors... " # Issue the SRV query and pull out the Priority, Weight, and Target fields. # BIND 9 prints "$name has SRV record ..." while BIND 8 prints # "$name server selection ..."; we allow either format. MLIST="_http._tcp.${SERVERNAME}" host -t srv "${MLIST}" | sed -nE "s/${MLIST} (has SRV record|server selection) //p" | cut -f 1,2,4 -d ' ' | sed -e 's/\.$//' | sort > serverlist_full # If no records, give up -- we'll just use the server name we were given. if [ `wc -l < serverlist_full` -eq 0 ]; then echo "none found." return 1 fi # Report how many mirrors we found. echo `wc -l < serverlist_full` "mirrors found." # Generate a random seed for use in picking mirrors. If HTTP_PROXY # is set, this will be used to generate the seed; otherwise, the seed # will be random. if [ -n "${HTTP_PROXY}${http_proxy}" ]; then RANDVALUE=`sha256 -qs "${HTTP_PROXY}${http_proxy}" | tr -d 'a-f' | cut -c 1-9` else RANDVALUE=`jot -r 1 0 999999999` fi } # Pick a mirror. Returns 1 if we have run out of mirrors to try. fetch_pick_server () { # Generate a list of not-yet-tried mirrors sort serverlist_tried | comm -23 serverlist_full - > serverlist # Have we run out of mirrors? if [ `wc -l < serverlist` -eq 0 ]; then echo "No mirrors remaining, giving up." return 1 fi # Find the highest priority level (lowest numeric value). SRV_PRIORITY=`cut -f 1 -d ' ' serverlist | sort -n | head -1` # Add up the weights of the response lines at that priority level. SRV_WSUM=0; while read X; do case "$X" in ${SRV_PRIORITY}\ *) SRV_W=`echo $X | cut -f 2 -d ' '` SRV_WSUM=$(($SRV_WSUM + $SRV_W)) ;; esac done < serverlist # If all the weights are 0, pretend that they are all 1 instead. if [ ${SRV_WSUM} -eq 0 ]; then SRV_WSUM=`grep -E "^${SRV_PRIORITY} " serverlist | wc -l` SRV_W_ADD=1 else SRV_W_ADD=0 fi # Pick a value between 0 and the sum of the weights - 1 SRV_RND=`expr ${RANDVALUE} % ${SRV_WSUM}` # Read through the list of mirrors and set SERVERNAME. Write the line # corresponding to the mirror we selected into serverlist_tried so that # we won't try it again. while read X; do case "$X" in ${SRV_PRIORITY}\ *) SRV_W=`echo $X | cut -f 2 -d ' '` SRV_W=$(($SRV_W + $SRV_W_ADD)) if [ $SRV_RND -lt $SRV_W ]; then SERVERNAME=`echo $X | cut -f 3 -d ' '` echo "$X" >> serverlist_tried break else SRV_RND=$(($SRV_RND - $SRV_W)) fi ;; esac done < serverlist } # Take a list of ${oldhash}|${newhash} and output a list of needed patches, # i.e., those for which we have ${oldhash} and don't have ${newhash}. fetch_make_patchlist () { grep -vE "^([0-9a-f]{64})\|\1$" | tr '|' ' ' | while read X Y; do if [ -f "files/${Y}.gz" ] || [ ! -f "files/${X}.gz" ]; then continue fi echo "${X}|${Y}" done | uniq } # Print user-friendly progress statistics fetch_progress () { LNC=0 while read x; do LNC=$(($LNC + 1)) if [ $(($LNC % 10)) = 0 ]; then echo -n $LNC elif [ $(($LNC % 2)) = 0 ]; then echo -n . fi done echo -n " " } # Function for asking the user if everything is ok continuep () { while read -p "Does this look reasonable (y/n)? " CONTINUE; do case "${CONTINUE}" in y*) return 0 ;; n*) return 1 ;; esac done } # Initialize the working directory workdir_init () { mkdir -p files touch tINDEX.present } # Check that we have a public key with an appropriate hash, or # fetch the key if it doesn't exist. Returns 1 if the key has # not yet been fetched. fetch_key () { if [ -r pub.ssl ] && [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then return 0 fi echo -n "Fetching public key from ${SERVERNAME}... " rm -f pub.ssl fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/pub.ssl \ 2>${QUIETREDIR} || true if ! [ -r pub.ssl ]; then echo "failed." return 1 fi if ! [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then echo "key has incorrect hash." rm -f pub.ssl return 1 fi echo "done." } # Fetch metadata signature, aka "tag". fetch_tag () { echo -n "Fetching metadata signature " echo ${NDEBUG} "for ${RELNUM} from ${SERVERNAME}... " rm -f latest.ssl fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/latest.ssl \ 2>${QUIETREDIR} || true if ! [ -r latest.ssl ]; then echo "failed." return 1 fi openssl rsautl -pubin -inkey pub.ssl -verify \ < latest.ssl > tag.new 2>${QUIETREDIR} || true rm latest.ssl if ! [ `wc -l < tag.new` = 1 ] || ! grep -qE \ "^freebsd-update\|${ARCH}\|${RELNUM}\|[0-9]+\|[0-9a-f]{64}\|[0-9]{10}" \ tag.new; then echo "invalid signature." return 1 fi echo "done." RELPATCHNUM=`cut -f 4 -d '|' < tag.new` TINDEXHASH=`cut -f 5 -d '|' < tag.new` EOLTIME=`cut -f 6 -d '|' < tag.new` } # Sanity-check the patch number in a tag, to make sure that we're not # going to "update" backwards and to prevent replay attacks. fetch_tagsanity () { # Check that we're not going to move from -pX to -pY with Y < X. RELPX=`uname -r | sed -E 's,.*-,,'` if echo ${RELPX} | grep -qE '^p[0-9]+$'; then RELPX=`echo ${RELPX} | cut -c 2-` else RELPX=0 fi if [ "${RELPATCHNUM}" -lt "${RELPX}" ]; then echo echo -n "Files on mirror (${RELNUM}-p${RELPATCHNUM})" echo " appear older than what" echo "we are currently running (`uname -r`)!" echo "Cowardly refusing to proceed any further." return 1 fi # If "tag" exists and corresponds to ${RELNUM}, make sure that # it contains a patch number <= RELPATCHNUM, in order to protect # against rollback (replay) attacks. if [ -f tag ] && grep -qE \ "^freebsd-update\|${ARCH}\|${RELNUM}\|[0-9]+\|[0-9a-f]{64}\|[0-9]{10}" \ tag; then LASTRELPATCHNUM=`cut -f 4 -d '|' < tag` if [ "${RELPATCHNUM}" -lt "${LASTRELPATCHNUM}" ]; then echo echo -n "Files on mirror (${RELNUM}-p${RELPATCHNUM})" echo " are older than the" echo -n "most recently seen updates" echo " (${RELNUM}-p${LASTRELPATCHNUM})." echo "Cowardly refusing to proceed any further." return 1 fi fi } # Fetch metadata index file fetch_metadata_index () { echo ${NDEBUG} "Fetching metadata index... " rm -f ${TINDEXHASH} fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/t/${TINDEXHASH} 2>${QUIETREDIR} if ! [ -f ${TINDEXHASH} ]; then echo "failed." return 1 fi if [ `${SHA256} -q ${TINDEXHASH}` != ${TINDEXHASH} ]; then echo "update metadata index corrupt." return 1 fi echo "done." } # Print an error message about signed metadata being bogus. fetch_metadata_bogus () { echo echo "The update metadata$1 is correctly signed, but" echo "failed an integrity check." echo "Cowardly refusing to proceed any further." return 1 } # Construct tINDEX.new by merging the lines named in $1 from ${TINDEXHASH} # with the lines not named in $@ from tINDEX.present (if that file exists). fetch_metadata_index_merge () { for METAFILE in $@; do if [ `grep -E "^${METAFILE}\|" ${TINDEXHASH} | wc -l` \ -ne 1 ]; then fetch_metadata_bogus " index" return 1 fi grep -E "${METAFILE}\|" ${TINDEXHASH} done | sort > tINDEX.wanted if [ -f tINDEX.present ]; then join -t '|' -v 2 tINDEX.wanted tINDEX.present | sort -m - tINDEX.wanted > tINDEX.new rm tINDEX.wanted else mv tINDEX.wanted tINDEX.new fi } # Sanity check all the lines of tINDEX.new. Even if more metadata lines # are added by future versions of the server, this won't cause problems, # since the only lines which appear in tINDEX.new are the ones which we # specifically grepped out of ${TINDEXHASH}. fetch_metadata_index_sanity () { if grep -qvE '^[0-9A-Z.-]+\|[0-9a-f]{64}$' tINDEX.new; then fetch_metadata_bogus " index" return 1 fi } # Sanity check the metadata file $1. fetch_metadata_sanity () { # Some aliases to save space later: ${P} is a character which can # appear in a path; ${M} is the four numeric metadata fields; and # ${H} is a sha256 hash. P="[-+./:=%@_[~[:alnum:]]" M="[0-9]+\|[0-9]+\|[0-9]+\|[0-9]+" H="[0-9a-f]{64}" # Check that the first four fields make sense. if gunzip -c < files/$1.gz | grep -qvE "^[a-z]+\|[0-9a-z]+\|${P}+\|[fdL-]\|"; then fetch_metadata_bogus "" return 1 fi # Remove the first three fields. gunzip -c < files/$1.gz | cut -f 4- -d '|' > sanitycheck.tmp # Sanity check entries with type 'f' if grep -E '^f' sanitycheck.tmp | grep -qvE "^f\|${M}\|${H}\|${P}*\$"; then fetch_metadata_bogus "" return 1 fi # Sanity check entries with type 'd' if grep -E '^d' sanitycheck.tmp | grep -qvE "^d\|${M}\|\|\$"; then fetch_metadata_bogus "" return 1 fi # Sanity check entries with type 'L' if grep -E '^L' sanitycheck.tmp | grep -qvE "^L\|${M}\|${P}*\|\$"; then fetch_metadata_bogus "" return 1 fi # Sanity check entries with type '-' if grep -E '^-' sanitycheck.tmp | grep -qvE "^-\|\|\|\|\|\|"; then fetch_metadata_bogus "" return 1 fi # Clean up rm sanitycheck.tmp } # Fetch the metadata index and metadata files listed in $@, # taking advantage of metadata patches where possible. fetch_metadata () { fetch_metadata_index || return 1 fetch_metadata_index_merge $@ || return 1 fetch_metadata_index_sanity || return 1 # Generate a list of wanted metadata patches join -t '|' -o 1.2,2.2 tINDEX.present tINDEX.new | fetch_make_patchlist > patchlist if [ -s patchlist ]; then # Attempt to fetch metadata patches echo -n "Fetching `wc -l < patchlist | tr -d ' '` " echo ${NDEBUG} "metadata patches.${DDSTATS}" tr '|' '-' < patchlist | lam -s "${FETCHDIR}/tp/" - -s ".gz" | xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \ 2>${STATSREDIR} | fetch_progress echo "done." # Attempt to apply metadata patches echo -n "Applying metadata patches... " tr '|' ' ' < patchlist | while read X Y; do if [ ! -f "${X}-${Y}.gz" ]; then continue; fi gunzip -c < ${X}-${Y}.gz > diff gunzip -c < files/${X}.gz > diff-OLD # Figure out which lines are being added and removed grep -E '^-' diff | cut -c 2- | while read PREFIX; do look "${PREFIX}" diff-OLD done | sort > diff-rm grep -E '^\+' diff | cut -c 2- > diff-add # Generate the new file comm -23 diff-OLD diff-rm | sort - diff-add > diff-NEW if [ `${SHA256} -q diff-NEW` = ${Y} ]; then mv diff-NEW files/${Y} gzip -n files/${Y} else mv diff-NEW ${Y}.bad fi rm -f ${X}-${Y}.gz diff rm -f diff-OLD diff-NEW diff-add diff-rm done 2>${QUIETREDIR} echo "done." fi # Update metadata without patches cut -f 2 -d '|' < tINDEX.new | while read Y; do if [ ! -f "files/${Y}.gz" ]; then echo ${Y}; fi done | sort -u > filelist if [ -s filelist ]; then echo -n "Fetching `wc -l < filelist | tr -d ' '` " echo ${NDEBUG} "metadata files... " lam -s "${FETCHDIR}/m/" - -s ".gz" < filelist | xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \ 2>${QUIETREDIR} while read Y; do if ! [ -f ${Y}.gz ]; then echo "failed." return 1 fi if [ `gunzip -c < ${Y}.gz | ${SHA256} -q` = ${Y} ]; then mv ${Y}.gz files/${Y}.gz else echo "metadata is corrupt." return 1 fi done < filelist echo "done." fi # Sanity-check the metadata files. cut -f 2 -d '|' tINDEX.new > filelist while read X; do fetch_metadata_sanity ${X} || return 1 done < filelist # Remove files which are no longer needed cut -f 2 -d '|' tINDEX.present | sort > oldfiles cut -f 2 -d '|' tINDEX.new | sort | comm -13 - oldfiles | lam -s "files/" - -s ".gz" | xargs rm -f rm patchlist filelist oldfiles rm ${TINDEXHASH} # We're done! mv tINDEX.new tINDEX.present mv tag.new tag return 0 } # Extract a subset of a downloaded metadata file containing only the parts # which are listed in COMPONENTS. fetch_filter_metadata_components () { METAHASH=`look "$1|" tINDEX.present | cut -f 2 -d '|'` gunzip -c < files/${METAHASH}.gz > $1.all # Fish out the lines belonging to components we care about. for C in ${COMPONENTS}; do look "`echo ${C} | tr '/' '|'`|" $1.all done > $1 # Remove temporary file. rm $1.all } # Generate a filtered version of the metadata file $1 from the downloaded # file, by fishing out the lines corresponding to components we're trying # to keep updated, and then removing lines corresponding to paths we want # to ignore. fetch_filter_metadata () { # Fish out the lines belonging to components we care about. fetch_filter_metadata_components $1 # Canonicalize directory names by removing any trailing / in # order to avoid listing directories multiple times if they # belong to multiple components. Turning "/" into "" doesn't # matter, since we add a leading "/" when we use paths later. cut -f 3- -d '|' $1 | sed -e 's,/|d|,|d|,' | sed -e 's,/|-|,|-|,' | sort -u > $1.tmp # Figure out which lines to ignore and remove them. for X in ${IGNOREPATHS}; do grep -E "^${X}" $1.tmp done | sort -u | comm -13 - $1.tmp > $1 # Remove temporary files. rm $1.tmp } # Filter the metadata file $1 by adding lines with "/boot/$2" # replaced by ${KERNELDIR} (which is `sysctl -n kern.bootfile` minus the # trailing "/kernel"); and if "/boot/$2" does not exist, remove # the original lines which start with that. # Put another way: Deal with the fact that the FOO kernel is sometimes # installed in /boot/FOO/ and is sometimes installed elsewhere. fetch_filter_kernel_names () { grep ^/boot/$2 $1 | sed -e "s,/boot/$2,${KERNELDIR},g" | sort - $1 > $1.tmp mv $1.tmp $1 if ! [ -d /boot/$2 ]; then grep -v ^/boot/$2 $1 > $1.tmp mv $1.tmp $1 fi } # For all paths appearing in $1 or $3, inspect the system # and generate $2 describing what is currently installed. fetch_inspect_system () { # No errors yet... rm -f .err # Tell the user why his disk is suddenly making lots of noise echo -n "Inspecting system... " # Generate list of files to inspect cat $1 $3 | cut -f 1 -d '|' | sort -u > filelist # Examine each file and output lines of the form # /path/to/file|type|device-inum|user|group|perm|flags|value # sorted by device and inode number. while read F; do # If the symlink/file/directory does not exist, record this. if ! [ -e ${BASEDIR}/${F} ]; then echo "${F}|-||||||" continue fi if ! [ -r ${BASEDIR}/${F} ]; then echo "Cannot read file: ${BASEDIR}/${F}" \ >/dev/stderr touch .err return 1 fi # Otherwise, output an index line. if [ -L ${BASEDIR}/${F} ]; then echo -n "${F}|L|" stat -n -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F}; readlink ${BASEDIR}/${F}; elif [ -f ${BASEDIR}/${F} ]; then echo -n "${F}|f|" stat -n -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F}; sha256 -q ${BASEDIR}/${F}; elif [ -d ${BASEDIR}/${F} ]; then echo -n "${F}|d|" stat -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F}; else echo "Unknown file type: ${BASEDIR}/${F}" \ >/dev/stderr touch .err return 1 fi done < filelist | sort -k 3,3 -t '|' > $2.tmp rm filelist # Check if an error occurred during system inspection if [ -f .err ]; then return 1 fi # Convert to the form # /path/to/file|type|user|group|perm|flags|value|hlink # by resolving identical device and inode numbers into hard links. cut -f 1,3 -d '|' $2.tmp | sort -k 1,1 -t '|' | sort -s -u -k 2,2 -t '|' | join -1 2 -2 3 -t '|' - $2.tmp | awk -F \| -v OFS=\| \ '{ if (($2 == $3) || ($4 == "-")) print $3,$4,$5,$6,$7,$8,$9,"" else print $3,$4,$5,$6,$7,$8,$9,$2 }' | sort > $2 rm $2.tmp # We're finished looking around echo "done." } # For any paths matching ${MERGECHANGES}, compare $1 and $2 and find any # files which differ; generate $3 containing these paths and the old hashes. fetch_filter_mergechanges () { # Pull out the paths and hashes of the files matching ${MERGECHANGES}. for F in $1 $2; do for X in ${MERGECHANGES}; do grep -E "^${X}" ${F} done | cut -f 1,2,7 -d '|' | sort > ${F}-values done # Any line in $2-values which doesn't appear in $1-values and is a # file means that we should list the path in $3. comm -13 $1-values $2-values | fgrep '|f|' | cut -f 1 -d '|' > $2-paths # For each path, pull out one (and only one!) entry from $1-values. # Note that we cannot distinguish which "old" version the user made # changes to; but hopefully any changes which occur due to security # updates will exist in both the "new" version and the version which # the user has installed, so the merging will still work. while read X; do look "${X}|" $1-values | head -1 done < $2-paths > $3 # Clean up rm $1-values $2-values $2-paths } # For any paths matching ${UPDATEIFUNMODIFIED}, remove lines from $[123] # which correspond to lines in $2 with hashes not matching $1 or $3, unless # the paths are listed in $4. For entries in $2 marked "not present" # (aka. type -), remove lines from $[123] unless there is a corresponding # entry in $1. fetch_filter_unmodified_notpresent () { # Figure out which lines of $1 and $3 correspond to bits which # should only be updated if they haven't changed, and fish out # the (path, type, value) tuples. # NOTE: We don't consider a file to be "modified" if it matches # the hash from $3. for X in ${UPDATEIFUNMODIFIED}; do grep -E "^${X}" $1 grep -E "^${X}" $3 done | cut -f 1,2,7 -d '|' | sort > $1-values # Do the same for $2. for X in ${UPDATEIFUNMODIFIED}; do grep -E "^${X}" $2 done | cut -f 1,2,7 -d '|' | sort > $2-values # Any entry in $2-values which is not in $1-values corresponds to # a path which we need to remove from $1, $2, and $3, unless it # that path appears in $4. comm -13 $1-values $2-values | sort -t '|' -k 1,1 > mlines.tmp cut -f 1 -d '|' $4 | sort | join -v 2 -t '|' - mlines.tmp | sort > mlines rm $1-values $2-values mlines.tmp # Any lines in $2 which are not in $1 AND are "not present" lines # also belong in mlines. comm -13 $1 $2 | cut -f 1,2,7 -d '|' | fgrep '|-|' >> mlines # Remove lines from $1, $2, and $3 for X in $1 $2 $3; do sort -t '|' -k 1,1 ${X} > ${X}.tmp cut -f 1 -d '|' < mlines | sort | join -v 2 -t '|' - ${X}.tmp | sort > ${X} rm ${X}.tmp done # Store a list of the modified files, for future reference fgrep -v '|-|' mlines | cut -f 1 -d '|' > modifiedfiles rm mlines } # For each entry in $1 of type -, remove any corresponding # entry from $2 if ${ALLOWADD} != "yes". Remove all entries # of type - from $1. fetch_filter_allowadd () { cut -f 1,2 -d '|' < $1 | fgrep '|-' | cut -f 1 -d '|' > filesnotpresent if [ ${ALLOWADD} != "yes" ]; then sort < $2 | join -v 1 -t '|' - filesnotpresent | sort > $2.tmp mv $2.tmp $2 fi sort < $1 | join -v 1 -t '|' - filesnotpresent | sort > $1.tmp mv $1.tmp $1 rm filesnotpresent } # If ${ALLOWDELETE} != "yes", then remove any entries from $1 # which don't correspond to entries in $2. fetch_filter_allowdelete () { # Produce a lists ${PATH}|${TYPE} for X in $1 $2; do cut -f 1-2 -d '|' < ${X} | sort -u > ${X}.nodes done # Figure out which lines need to be removed from $1. if [ ${ALLOWDELETE} != "yes" ]; then comm -23 $1.nodes $2.nodes > $1.badnodes else : > $1.badnodes fi # Remove the relevant lines from $1 while read X; do look "${X}|" $1 done < $1.badnodes | comm -13 - $1 > $1.tmp mv $1.tmp $1 rm $1.badnodes $1.nodes $2.nodes } # If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in $2 # with metadata not matching any entry in $1, replace the corresponding # line of $3 with one having the same metadata as the entry in $2. fetch_filter_modified_metadata () { # Fish out the metadata from $1 and $2 for X in $1 $2; do cut -f 1-6 -d '|' < ${X} > ${X}.metadata done # Find the metadata we need to keep if [ ${KEEPMODIFIEDMETADATA} = "yes" ]; then comm -13 $1.metadata $2.metadata > keepmeta else : > keepmeta fi # Extract the lines which we need to remove from $3, and # construct the lines which we need to add to $3. : > $3.remove : > $3.add while read LINE; do NODE=`echo "${LINE}" | cut -f 1-2 -d '|'` look "${NODE}|" $3 >> $3.remove look "${NODE}|" $3 | cut -f 7- -d '|' | lam -s "${LINE}|" - >> $3.add done < keepmeta # Remove the specified lines and add the new lines. sort $3.remove | comm -13 - $3 | sort -u - $3.add > $3.tmp mv $3.tmp $3 rm keepmeta $1.metadata $2.metadata $3.add $3.remove } # Remove lines from $1 and $2 which are identical; # no need to update a file if it isn't changing. fetch_filter_uptodate () { comm -23 $1 $2 > $1.tmp comm -13 $1 $2 > $2.tmp mv $1.tmp $1 mv $2.tmp $2 } # Fetch any "clean" old versions of files we need for merging changes. fetch_files_premerge () { # We only need to do anything if $1 is non-empty. if [ -s $1 ]; then # Tell the user what we're doing echo -n "Fetching files from ${OLDRELNUM} for merging... " # List of files wanted fgrep '|f|' < $1 | cut -f 3 -d '|' | sort -u > files.wanted # Only fetch the files we don't already have while read Y; do if [ ! -f "files/${Y}.gz" ]; then echo ${Y}; fi done < files.wanted > filelist # Actually fetch them lam -s "${OLDFETCHDIR}/f/" - -s ".gz" < filelist | xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \ 2>${QUIETREDIR} # Make sure we got them all, and move them into /files/ while read Y; do if ! [ -f ${Y}.gz ]; then echo "failed." return 1 fi if [ `gunzip -c < ${Y}.gz | ${SHA256} -q` = ${Y} ]; then mv ${Y}.gz files/${Y}.gz else echo "${Y} has incorrect hash." return 1 fi done < filelist echo "done." # Clean up rm filelist files.wanted fi } # Prepare to fetch files: Generate a list of the files we need, # copy the unmodified files we have into /files/, and generate # a list of patches to download. fetch_files_prepare () { # Tell the user why his disk is suddenly making lots of noise echo -n "Preparing to download files... " # Reduce indices to ${PATH}|${HASH} pairs for X in $1 $2 $3; do cut -f 1,2,7 -d '|' < ${X} | fgrep '|f|' | cut -f 1,3 -d '|' | sort > ${X}.hashes done # List of files wanted cut -f 2 -d '|' < $3.hashes | sort -u | while read HASH; do if ! [ -f files/${HASH}.gz ]; then echo ${HASH} fi done > files.wanted # Generate a list of unmodified files comm -12 $1.hashes $2.hashes | sort -k 1,1 -t '|' > unmodified.files # Copy all files into /files/. We only need the unmodified files # for use in patching; but we'll want all of them if the user asks # to rollback the updates later. while read LINE; do F=`echo "${LINE}" | cut -f 1 -d '|'` HASH=`echo "${LINE}" | cut -f 2 -d '|'` # Skip files we already have. if [ -f files/${HASH}.gz ]; then continue fi # Make sure the file hasn't changed. cp "${BASEDIR}/${F}" tmpfile if [ `sha256 -q tmpfile` != ${HASH} ]; then echo echo "File changed while FreeBSD Update running: ${F}" return 1 fi # Place the file into storage. gzip -c < tmpfile > files/${HASH}.gz rm tmpfile done < $2.hashes # Produce a list of patches to download sort -k 1,1 -t '|' $3.hashes | join -t '|' -o 2.2,1.2 - unmodified.files | fetch_make_patchlist > patchlist # Garbage collect rm unmodified.files $1.hashes $2.hashes $3.hashes # We don't need the list of possible old files any more. rm $1 # We're finished making noise echo "done." } # Fetch files. fetch_files () { # Attempt to fetch patches if [ -s patchlist ]; then echo -n "Fetching `wc -l < patchlist | tr -d ' '` " echo ${NDEBUG} "patches.${DDSTATS}" tr '|' '-' < patchlist | lam -s "${PATCHDIR}/" - | xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \ 2>${STATSREDIR} | fetch_progress echo "done." # Attempt to apply patches echo -n "Applying patches... " tr '|' ' ' < patchlist | while read X Y; do if [ ! -f "${X}-${Y}" ]; then continue; fi gunzip -c < files/${X}.gz > OLD bspatch OLD NEW ${X}-${Y} if [ `${SHA256} -q NEW` = ${Y} ]; then mv NEW files/${Y} gzip -n files/${Y} fi rm -f diff OLD NEW ${X}-${Y} done 2>${QUIETREDIR} echo "done." fi # Download files which couldn't be generate via patching while read Y; do if [ ! -f "files/${Y}.gz" ]; then echo ${Y}; fi done < files.wanted > filelist if [ -s filelist ]; then echo -n "Fetching `wc -l < filelist | tr -d ' '` " echo ${NDEBUG} "files... " lam -s "${FETCHDIR}/f/" - -s ".gz" < filelist | xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \ 2>${QUIETREDIR} while read Y; do if ! [ -f ${Y}.gz ]; then echo "failed." return 1 fi if [ `gunzip -c < ${Y}.gz | ${SHA256} -q` = ${Y} ]; then mv ${Y}.gz files/${Y}.gz else echo "${Y} has incorrect hash." return 1 fi done < filelist echo "done." fi # Clean up rm files.wanted filelist patchlist } # Create and populate install manifest directory; and report what updates # are available. fetch_create_manifest () { # If we have an existing install manifest, nuke it. if [ -L "${BDHASH}-install" ]; then rm -r ${BDHASH}-install/ rm ${BDHASH}-install fi # Report to the user if any updates were avoided due to local changes if [ -s modifiedfiles ]; then echo echo -n "The following files are affected by updates, " echo "but no changes have" echo -n "been downloaded because the files have been " echo "modified locally:" cat modifiedfiles fi | $PAGER rm modifiedfiles # If no files will be updated, tell the user and exit if ! [ -s INDEX-PRESENT ] && ! [ -s INDEX-NEW ]; then rm INDEX-PRESENT INDEX-NEW echo echo -n "No updates needed to update system to " echo "${RELNUM}-p${RELPATCHNUM}." return fi # Divide files into (a) removed files, (b) added files, and # (c) updated files. cut -f 1 -d '|' < INDEX-PRESENT | sort > INDEX-PRESENT.flist cut -f 1 -d '|' < INDEX-NEW | sort > INDEX-NEW.flist comm -23 INDEX-PRESENT.flist INDEX-NEW.flist > files.removed comm -13 INDEX-PRESENT.flist INDEX-NEW.flist > files.added comm -12 INDEX-PRESENT.flist INDEX-NEW.flist > files.updated rm INDEX-PRESENT.flist INDEX-NEW.flist # Report removed files, if any if [ -s files.removed ]; then echo echo -n "The following files will be removed " echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:" cat files.removed fi | $PAGER rm files.removed # Report added files, if any if [ -s files.added ]; then echo echo -n "The following files will be added " echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:" cat files.added fi | $PAGER rm files.added # Report updated files, if any if [ -s files.updated ]; then echo echo -n "The following files will be updated " echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:" cat files.updated fi | $PAGER rm files.updated # Create a directory for the install manifest. MDIR=`mktemp -d install.XXXXXX` || return 1 # Populate it mv INDEX-PRESENT ${MDIR}/INDEX-OLD mv INDEX-NEW ${MDIR}/INDEX-NEW # Link it into place ln -s ${MDIR} ${BDHASH}-install } # Warn about any upcoming EoL fetch_warn_eol () { # What's the current time? NOWTIME=`date "+%s"` # When did we last warn about the EoL date? if [ -f lasteolwarn ]; then LASTWARN=`cat lasteolwarn` else LASTWARN=`expr ${NOWTIME} - 63072000` fi # If the EoL time is past, warn. if [ ${EOLTIME} -lt ${NOWTIME} ]; then echo cat <<-EOF WARNING: `uname -sr` HAS PASSED ITS END-OF-LIFE DATE. Any security issues discovered after `date -r ${EOLTIME}` will not have been corrected. EOF return 1 fi # Figure out how long it has been since we last warned about the # upcoming EoL, and how much longer we have left. SINCEWARN=`expr ${NOWTIME} - ${LASTWARN}` TIMELEFT=`expr ${EOLTIME} - ${NOWTIME}` # Don't warn if the EoL is more than 3 months away if [ ${TIMELEFT} -gt 7884000 ]; then return 0 fi # Don't warn if the time remaining is more than 3 times the time # since the last warning. if [ ${TIMELEFT} -gt `expr ${SINCEWARN} \* 3` ]; then return 0 fi # Figure out what time units to use. if [ ${TIMELEFT} -lt 604800 ]; then UNIT="day" SIZE=86400 elif [ ${TIMELEFT} -lt 2678400 ]; then UNIT="week" SIZE=604800 else UNIT="month" SIZE=2678400 fi # Compute the right number of units NUM=`expr ${TIMELEFT} / ${SIZE}` if [ ${NUM} != 1 ]; then UNIT="${UNIT}s" fi # Print the warning echo cat <<-EOF WARNING: `uname -sr` is approaching its End-of-Life date. It is strongly recommended that you upgrade to a newer release within the next ${NUM} ${UNIT}. EOF # Update the stored time of last warning echo ${NOWTIME} > lasteolwarn } # Do the actual work involved in "fetch" / "cron". fetch_run () { workdir_init || return 1 # Prepare the mirror list. fetch_pick_server_init && fetch_pick_server # Try to fetch the public key until we run out of servers. while ! fetch_key; do fetch_pick_server || return 1 done # Try to fetch the metadata index signature ("tag") until we run # out of available servers; and sanity check the downloaded tag. while ! fetch_tag; do fetch_pick_server || return 1 done fetch_tagsanity || return 1 # Fetch the latest INDEX-NEW and INDEX-OLD files. fetch_metadata INDEX-NEW INDEX-OLD || return 1 # Generate filtered INDEX-NEW and INDEX-OLD files containing only # the lines which (a) belong to components we care about, and (b) # don't correspond to paths we're explicitly ignoring. fetch_filter_metadata INDEX-NEW || return 1 fetch_filter_metadata INDEX-OLD || return 1 # Translate /boot/${KERNCONF} into ${KERNELDIR} fetch_filter_kernel_names INDEX-NEW ${KERNCONF} fetch_filter_kernel_names INDEX-OLD ${KERNCONF} # For all paths appearing in INDEX-OLD or INDEX-NEW, inspect the # system and generate an INDEX-PRESENT file. fetch_inspect_system INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1 # Based on ${UPDATEIFUNMODIFIED}, remove lines from INDEX-* which # correspond to lines in INDEX-PRESENT with hashes not appearing # in INDEX-OLD or INDEX-NEW. Also remove lines where the entry in # INDEX-PRESENT has type - and there isn't a corresponding entry in # INDEX-OLD with type -. fetch_filter_unmodified_notpresent \ INDEX-OLD INDEX-PRESENT INDEX-NEW /dev/null # For each entry in INDEX-PRESENT of type -, remove any corresponding # entry from INDEX-NEW if ${ALLOWADD} != "yes". Remove all entries # of type - from INDEX-PRESENT. fetch_filter_allowadd INDEX-PRESENT INDEX-NEW # If ${ALLOWDELETE} != "yes", then remove any entries from # INDEX-PRESENT which don't correspond to entries in INDEX-NEW. fetch_filter_allowdelete INDEX-PRESENT INDEX-NEW # If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in # INDEX-PRESENT with metadata not matching any entry in INDEX-OLD, # replace the corresponding line of INDEX-NEW with one having the # same metadata as the entry in INDEX-PRESENT. fetch_filter_modified_metadata INDEX-OLD INDEX-PRESENT INDEX-NEW # Remove lines from INDEX-PRESENT and INDEX-NEW which are identical; # no need to update a file if it isn't changing. fetch_filter_uptodate INDEX-PRESENT INDEX-NEW # Prepare to fetch files: Generate a list of the files we need, # copy the unmodified files we have into /files/, and generate # a list of patches to download. fetch_files_prepare INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1 # Fetch files. fetch_files || return 1 # Create and populate install manifest directory; and report what # updates are available. fetch_create_manifest || return 1 # Warn about any upcoming EoL fetch_warn_eol || return 1 } # If StrictComponents is not "yes", generate a new components list # with only the components which appear to be installed. upgrade_guess_components () { if [ "${STRICTCOMPONENTS}" = "no" ]; then # Generate filtered INDEX-ALL with only the components listed # in COMPONENTS. fetch_filter_metadata_components $1 || return 1 # Tell the user why his disk is suddenly making lots of noise echo -n "Inspecting system... " # Look at the files on disk, and assume that a component is # supposed to be present if it is more than half-present. cut -f 1-3 -d '|' < INDEX-ALL | tr '|' ' ' | while read C S F; do if [ -e ${BASEDIR}/${F} ]; then echo "+ ${C}|${S}" fi echo "= ${C}|${S}" done | sort | uniq -c | sed -E 's,^ +,,' > compfreq grep ' = ' compfreq | cut -f 1,3 -d ' ' | sort -k 2,2 -t ' ' > compfreq.total grep ' + ' compfreq | cut -f 1,3 -d ' ' | sort -k 2,2 -t ' ' > compfreq.present join -t ' ' -1 2 -2 2 compfreq.present compfreq.total | while read S P T; do if [ ${P} -gt `expr ${T} / 2` ]; then echo ${S} fi done > comp.present cut -f 2 -d ' ' < compfreq.total > comp.total rm INDEX-ALL compfreq compfreq.total compfreq.present # We're done making noise. echo "done." # Sometimes the kernel isn't installed where INDEX-ALL # thinks that it should be: In particular, it is often in # /boot/kernel instead of /boot/GENERIC or /boot/SMP. To # deal with this, if "kernel|X" is listed in comp.total # (i.e., is a component which would be upgraded if it is # found to be present) we will add it to comp.present. # If "kernel|" is in comp.total but "kernel|X" is # not, we print a warning -- the user is running a kernel # which isn't part of the release. KCOMP=`echo ${KERNCONF} | tr 'A-Z' 'a-z'` grep -E "^kernel\|${KCOMP}\$" comp.total >> comp.present if grep -qE "^kernel\|" comp.total && ! grep -qE "^kernel\|${KCOMP}\$" comp.total; then cat <<-EOF WARNING: This system is running a "${KCOMP}" kernel, which is not a kernel configuration distributed as part of FreeBSD ${RELNUM}. This kernel will not be updated: you MUST update the kernel manually before running "$0 install". EOF fi # Re-sort the list of installed components and generate # the list of non-installed components. sort -u < comp.present > comp.present.tmp mv comp.present.tmp comp.present comm -13 comp.present comp.total > comp.absent # Ask the user to confirm that what we have is correct. To # reduce user confusion, translate "X|Y" back to "X/Y" (as # subcomponents must be listed in the configuration file). echo echo -n "The following components of FreeBSD " echo "seem to be installed:" tr '|' '/' < comp.present | fmt -72 echo echo -n "The following components of FreeBSD " echo "do not seem to be installed:" tr '|' '/' < comp.absent | fmt -72 echo continuep || return 1 echo # Suck the generated list of components into ${COMPONENTS}. # Note that comp.present.tmp is used due to issues with # pipelines and setting variables. COMPONENTS="" tr '|' '/' < comp.present > comp.present.tmp while read C; do COMPONENTS="${COMPONENTS} ${C}" done < comp.present.tmp # Delete temporary files rm comp.present comp.present.tmp comp.absent comp.total fi } # If StrictComponents is not "yes", COMPONENTS contains an entry # corresponding to the currently running kernel, and said kernel # does not exist in the new release, add "kernel/generic" to the # list of components. upgrade_guess_new_kernel () { if [ "${STRICTCOMPONENTS}" = "no" ]; then # Grab the unfiltered metadata file. METAHASH=`look "$1|" tINDEX.present | cut -f 2 -d '|'` gunzip -c < files/${METAHASH}.gz > $1.all # If "kernel/${KCOMP}" is in ${COMPONENTS} and that component # isn't in $1.all, we need to add kernel/generic. for C in ${COMPONENTS}; do if [ ${C} = "kernel/${KCOMP}" ] && ! grep -qE "^kernel\|${KCOMP}\|" $1.all; then COMPONENTS="${COMPONENTS} kernel/generic" NKERNCONF="GENERIC" cat <<-EOF WARNING: This system is running a "${KCOMP}" kernel, which is not a kernel configuration distributed as part of FreeBSD ${RELNUM}. As part of upgrading to FreeBSD ${RELNUM}, this kernel will be replaced with a "generic" kernel. EOF continuep || return 1 fi done # Don't need this any more... rm $1.all fi } # Convert INDEX-OLD (last release) and INDEX-ALL (new release) into # INDEX-OLD and INDEX-NEW files (in the sense of normal upgrades). upgrade_oldall_to_oldnew () { # For each ${F}|... which appears in INDEX-ALL but does not appear # in INDEX-OLD, add ${F}|-|||||| to INDEX-OLD. cut -f 1 -d '|' < $1 | sort -u > $1.paths cut -f 1 -d '|' < $2 | sort -u | comm -13 $1.paths - | lam - -s "|-||||||" | sort - $1 > $1.tmp mv $1.tmp $1 # Remove lines from INDEX-OLD which also appear in INDEX-ALL comm -23 $1 $2 > $1.tmp mv $1.tmp $1 # Remove lines from INDEX-ALL which have a file name not appearing # anywhere in INDEX-OLD (since these must be files which haven't # changed -- if they were new, there would be an entry of type "-"). cut -f 1 -d '|' < $1 | sort -u > $1.paths sort -k 1,1 -t '|' < $2 | join -t '|' - $1.paths | sort > $2.tmp rm $1.paths mv $2.tmp $2 # Rename INDEX-ALL to INDEX-NEW. mv $2 $3 } # Helper for upgrade_merge: Return zero true iff the two files differ only # in the contents of their RCS tags. samef () { X=`sed -E 's/\\$FreeBSD.*\\$/\$FreeBSD\$/' < $1 | ${SHA256}` Y=`sed -E 's/\\$FreeBSD.*\\$/\$FreeBSD\$/' < $2 | ${SHA256}` if [ $X = $Y ]; then return 0; else return 1; fi } # From the list of "old" files in $1, merge changes in $2 with those in $3, # and update $3 to reflect the hashes of merged files. upgrade_merge () { # We only need to do anything if $1 is non-empty. if [ -s $1 ]; then cut -f 1 -d '|' $1 | sort > $1-paths # Create staging area for merging files rm -rf merge/ while read F; do D=`dirname ${F}` mkdir -p merge/old/${D} mkdir -p merge/${OLDRELNUM}/${D} mkdir -p merge/${RELNUM}/${D} mkdir -p merge/new/${D} done < $1-paths # Copy in files while read F; do # Currently installed file V=`look "${F}|" $2 | cut -f 7 -d '|'` gunzip < files/${V}.gz > merge/old/${F} # Old release if look "${F}|" $1 | fgrep -q "|f|"; then V=`look "${F}|" $1 | cut -f 3 -d '|'` gunzip < files/${V}.gz \ > merge/${OLDRELNUM}/${F} fi # New release if look "${F}|" $3 | cut -f 1,2,7 -d '|' | fgrep -q "|f|"; then V=`look "${F}|" $3 | cut -f 7 -d '|'` gunzip < files/${V}.gz \ > merge/${RELNUM}/${F} fi done < $1-paths # Attempt to automatically merge changes echo -n "Attempting to automatically merge " echo -n "changes in files..." : > failed.merges while read F; do # If the file doesn't exist in the new release, # the result of "merging changes" is having the file # not exist. if ! [ -f merge/${RELNUM}/${F} ]; then continue fi # If the file didn't exist in the old release, we're # going to throw away the existing file and hope that # the version from the new release is what we want. if ! [ -f merge/${OLDRELNUM}/${F} ]; then cp merge/${RELNUM}/${F} merge/new/${F} continue fi # Some files need special treatment. case ${F} in /etc/spwd.db | /etc/pwd.db | /etc/login.conf.db) # Don't merge these -- we're rebuild them # after updates are installed. cp merge/old/${F} merge/new/${F} ;; *) if ! merge -p -L "current version" \ -L "${OLDRELNUM}" -L "${RELNUM}" \ merge/old/${F} \ merge/${OLDRELNUM}/${F} \ merge/${RELNUM}/${F} \ > merge/new/${F} 2>/dev/null; then echo ${F} >> failed.merges fi ;; esac done < $1-paths echo " done." # Ask the user to handle any files which didn't merge. while read F; do # If the installed file differs from the version in # the old release only due to RCS tag expansion # then just use the version in the new release. if samef merge/old/${F} merge/${OLDRELNUM}/${F}; then cp merge/${RELNUM}/${F} merge/new/${F} continue fi cat <<-EOF The following file could not be merged automatically: ${F} Press Enter to edit this file in ${EDITOR} and resolve the conflicts manually... EOF read dummy files/${V}.gz echo "${F}|${V}" fi done < $1-paths > newhashes # Pull lines out from $3 which need to be updated to # reflect merged files. while read F; do look "${F}|" $3 done < $1-paths > $3-oldlines # Update lines to reflect merged files join -t '|' -o 1.1,1.2,1.3,1.4,1.5,1.6,2.2,1.8 \ $3-oldlines newhashes > $3-newlines # Remove old lines from $3 and add new lines. sort $3-oldlines | comm -13 - $3 | sort - $3-newlines > $3.tmp mv $3.tmp $3 # Clean up rm $1-paths newhashes $3-oldlines $3-newlines rm -rf merge/ fi # We're done with merging files. rm $1 } # Do the work involved in fetching upgrades to a new release upgrade_run () { workdir_init || return 1 # Prepare the mirror list. fetch_pick_server_init && fetch_pick_server # Try to fetch the public key until we run out of servers. while ! fetch_key; do fetch_pick_server || return 1 done # Try to fetch the metadata index signature ("tag") until we run # out of available servers; and sanity check the downloaded tag. while ! fetch_tag; do fetch_pick_server || return 1 done fetch_tagsanity || return 1 # Fetch the INDEX-OLD and INDEX-ALL. fetch_metadata INDEX-OLD INDEX-ALL || return 1 # If StrictComponents is not "yes", generate a new components list # with only the components which appear to be installed. upgrade_guess_components INDEX-ALL || return 1 # Generate filtered INDEX-OLD and INDEX-ALL files containing only # the components we want and without anything marked as "Ignore". fetch_filter_metadata INDEX-OLD || return 1 fetch_filter_metadata INDEX-ALL || return 1 # Merge the INDEX-OLD and INDEX-ALL files into INDEX-OLD. sort INDEX-OLD INDEX-ALL > INDEX-OLD.tmp mv INDEX-OLD.tmp INDEX-OLD rm INDEX-ALL # Adjust variables for fetching files from the new release. OLDRELNUM=${RELNUM} RELNUM=${TARGETRELEASE} OLDFETCHDIR=${FETCHDIR} FETCHDIR=${RELNUM}/${ARCH} # Try to fetch the NEW metadata index signature ("tag") until we run # out of available servers; and sanity check the downloaded tag. while ! fetch_tag; do fetch_pick_server || return 1 done # Fetch the new INDEX-ALL. fetch_metadata INDEX-ALL || return 1 # If StrictComponents is not "yes", COMPONENTS contains an entry # corresponding to the currently running kernel, and said kernel # does not exist in the new release, add "kernel/generic" to the # list of components. upgrade_guess_new_kernel INDEX-ALL || return 1 # Filter INDEX-ALL to contain only the components we want and without # anything marked as "Ignore". fetch_filter_metadata INDEX-ALL || return 1 # Convert INDEX-OLD (last release) and INDEX-ALL (new release) into # INDEX-OLD and INDEX-NEW files (in the sense of normal upgrades). upgrade_oldall_to_oldnew INDEX-OLD INDEX-ALL INDEX-NEW # Translate /boot/${KERNCONF} or /boot/${NKERNCONF} into ${KERNELDIR} fetch_filter_kernel_names INDEX-NEW ${NKERNCONF} fetch_filter_kernel_names INDEX-OLD ${KERNCONF} # For all paths appearing in INDEX-OLD or INDEX-NEW, inspect the # system and generate an INDEX-PRESENT file. fetch_inspect_system INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1 # Based on ${MERGECHANGES}, generate a file tomerge-old with the # paths and hashes of old versions of files to merge. fetch_filter_mergechanges INDEX-OLD INDEX-PRESENT tomerge-old # Based on ${UPDATEIFUNMODIFIED}, remove lines from INDEX-* which # correspond to lines in INDEX-PRESENT with hashes not appearing # in INDEX-OLD or INDEX-NEW. Also remove lines where the entry in # INDEX-PRESENT has type - and there isn't a corresponding entry in # INDEX-OLD with type -. fetch_filter_unmodified_notpresent \ INDEX-OLD INDEX-PRESENT INDEX-NEW tomerge-old # For each entry in INDEX-PRESENT of type -, remove any corresponding # entry from INDEX-NEW if ${ALLOWADD} != "yes". Remove all entries # of type - from INDEX-PRESENT. fetch_filter_allowadd INDEX-PRESENT INDEX-NEW # If ${ALLOWDELETE} != "yes", then remove any entries from # INDEX-PRESENT which don't correspond to entries in INDEX-NEW. fetch_filter_allowdelete INDEX-PRESENT INDEX-NEW # If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in # INDEX-PRESENT with metadata not matching any entry in INDEX-OLD, # replace the corresponding line of INDEX-NEW with one having the # same metadata as the entry in INDEX-PRESENT. fetch_filter_modified_metadata INDEX-OLD INDEX-PRESENT INDEX-NEW # Remove lines from INDEX-PRESENT and INDEX-NEW which are identical; # no need to update a file if it isn't changing. fetch_filter_uptodate INDEX-PRESENT INDEX-NEW # Fetch "clean" files from the old release for merging changes. fetch_files_premerge tomerge-old # Prepare to fetch files: Generate a list of the files we need, # copy the unmodified files we have into /files/, and generate # a list of patches to download. fetch_files_prepare INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1 # Fetch patches from to-${RELNUM}/${ARCH}/bp/ PATCHDIR=to-${RELNUM}/${ARCH}/bp fetch_files || return 1 # Merge configuration file changes. upgrade_merge tomerge-old INDEX-PRESENT INDEX-NEW || return 1 # Create and populate install manifest directory; and report what # updates are available. fetch_create_manifest || return 1 # Leave a note behind to tell the "install" command that the kernel # needs to be installed before the world. touch ${BDHASH}-install/kernelfirst # Remind the user that they need to run "freebsd-update install" # to install the downloaded bits, in case they didn't RTFM. echo "To install the downloaded upgrades, run \"$0 install\"." } # Make sure that all the file hashes mentioned in $@ have corresponding # gzipped files stored in /files/. install_verify () { # Generate a list of hashes cat $@ | cut -f 2,7 -d '|' | grep -E '^f' | cut -f 2 -d '|' | sort -u > filelist # Make sure all the hashes exist while read HASH; do if ! [ -f files/${HASH}.gz ]; then echo -n "Update files missing -- " echo "this should never happen." echo "Re-run '$0 fetch'." return 1 fi done < filelist # Clean up rm filelist } # Remove the system immutable flag from files install_unschg () { # Generate file list cat $@ | cut -f 1 -d '|' > filelist # Remove flags while read F; do if ! [ -e ${BASEDIR}/${F} ]; then continue fi chflags noschg ${BASEDIR}/${F} || return 1 done < filelist # Clean up rm filelist } # Decide which directory name to use for kernel backups. backup_kernel_finddir () { CNT=0 while true ; do # Pathname does not exist, so it is OK use that name # for backup directory. - if [ ! -e $BACKUPKERNELDIR ]; then + if [ ! -e $BASEDIR/$BACKUPKERNELDIR ]; then return 0 fi # If directory do exist, we only use if it has our # marker file. - if [ -d $BACKUPKERNELDIR -a \ - -e $BACKUPKERNELDIR/.freebsd-update ]; then + if [ -d $BASEDIR/$BACKUPKERNELDIR -a \ + -e $BASEDIR/$BACKUPKERNELDIR/.freebsd-update ]; then return 0 fi # We could not use current directory name, so add counter to # the end and try again. CNT=$((CNT + 1)) if [ $CNT -gt 9 ]; then - echo "Could not find valid backup dir ($BACKUPKERNELDIR)" + echo "Could not find valid backup dir ($BASEDIR/$BACKUPKERNELDIR)" exit 1 fi BACKUPKERNELDIR="`echo $BACKUPKERNELDIR | sed -Ee 's/[0-9]\$//'`" BACKUPKERNELDIR="${BACKUPKERNELDIR}${CNT}" done } # Backup the current kernel using hardlinks, if not disabled by user. # Since we delete all files in the directory used for previous backups # we create a marker file called ".freebsd-update" in the directory so # we can determine on the next run that the directory was created by # freebsd-update and we then do not accidentally remove user files in # the unlikely case that the user has created a directory with a # conflicting name. backup_kernel () { # Only make kernel backup is so configured. if [ $BACKUPKERNEL != yes ]; then return 0 fi # Decide which directory name to use for kernel backups. backup_kernel_finddir # Remove old kernel backup files. If $BACKUPKERNELDIR was # "not ours", backup_kernel_finddir would have exited, so # deleting the directory content is as safe as we can make it. - if [ -d $BACKUPKERNELDIR ]; then - rm -fr $BACKUPKERNELDIR + if [ -d $BASEDIR/$BACKUPKERNELDIR ]; then + rm -fr $BASEDIR/$BACKUPKERNELDIR fi # Create directories for backup. - mkdir -p $BACKUPKERNELDIR - mtree -cdn -p "${KERNELDIR}" | \ - mtree -Ue -p "${BACKUPKERNELDIR}" > /dev/null + mkdir -p $BASEDIR/$BACKUPKERNELDIR + mtree -cdn -p "${BASEDIR}/${KERNELDIR}" | \ + mtree -Ue -p "${BASEDIR}/${BACKUPKERNELDIR}" > /dev/null # Mark the directory as having been created by freebsd-update. - touch $BACKUPKERNELDIR/.freebsd-update + touch $BASEDIR/$BACKUPKERNELDIR/.freebsd-update if [ $? -ne 0 ]; then echo "Could not create kernel backup directory" exit 1 fi # Disable pathname expansion to be sure *.symbols is not # expanded. set -f # Use find to ignore symbol files, unless disabled by user. if [ $BACKUPKERNELSYMBOLFILES = yes ]; then FINDFILTER="" else FINDFILTER=-"a ! -name *.symbols" fi # Backup all the kernel files using hardlinks. - (cd $KERNELDIR && find . -type f $FINDFILTER -exec \ - cp -pl '{}' ${BACKUPKERNELDIR}/'{}' \;) + (cd ${BASEDIR}/${KERNELDIR} && find . -type f $FINDFILTER -exec \ + cp -pl '{}' ${BASEDIR}/${BACKUPKERNELDIR}/'{}' \;) # Re-enable patchname expansion. set +f } # Install new files install_from_index () { # First pass: Do everything apart from setting file flags. We # can't set flags yet, because schg inhibits hard linking. sort -k 1,1 -t '|' $1 | tr '|' ' ' | while read FPATH TYPE OWNER GROUP PERM FLAGS HASH LINK; do case ${TYPE} in d) # Create a directory install -d -o ${OWNER} -g ${GROUP} \ -m ${PERM} ${BASEDIR}/${FPATH} ;; f) if [ -z "${LINK}" ]; then # Create a file, without setting flags. gunzip < files/${HASH}.gz > ${HASH} install -S -o ${OWNER} -g ${GROUP} \ -m ${PERM} ${HASH} ${BASEDIR}/${FPATH} rm ${HASH} else # Create a hard link. ln -f ${BASEDIR}/${LINK} ${BASEDIR}/${FPATH} fi ;; L) # Create a symlink ln -sfh ${HASH} ${BASEDIR}/${FPATH} ;; esac done # Perform a second pass, adding file flags. tr '|' ' ' < $1 | while read FPATH TYPE OWNER GROUP PERM FLAGS HASH LINK; do if [ ${TYPE} = "f" ] && ! [ ${FLAGS} = "0" ]; then chflags ${FLAGS} ${BASEDIR}/${FPATH} fi done } # Remove files which we want to delete install_delete () { # Generate list of new files cut -f 1 -d '|' < $2 | sort > newfiles # Generate subindex of old files we want to nuke sort -k 1,1 -t '|' $1 | join -t '|' -v 1 - newfiles | sort -r -k 1,1 -t '|' | cut -f 1,2 -d '|' | tr '|' ' ' > killfiles # Remove the offending bits while read FPATH TYPE; do case ${TYPE} in d) rmdir ${BASEDIR}/${FPATH} ;; f) rm ${BASEDIR}/${FPATH} ;; L) rm ${BASEDIR}/${FPATH} ;; esac done < killfiles # Clean up rm newfiles killfiles } # Install new files, delete old files, and update linker.hints install_files () { # If we haven't already dealt with the kernel, deal with it. if ! [ -f $1/kerneldone ]; then grep -E '^/boot/' $1/INDEX-OLD > INDEX-OLD grep -E '^/boot/' $1/INDEX-NEW > INDEX-NEW # Backup current kernel before installing a new one backup_kernel || return 1 # Install new files install_from_index INDEX-NEW || return 1 # Remove files which need to be deleted install_delete INDEX-OLD INDEX-NEW || return 1 # Update linker.hints if necessary if [ -s INDEX-OLD -o -s INDEX-NEW ]; then - kldxref -R /boot/ 2>/dev/null + kldxref -R ${BASEDIR}/boot/ 2>/dev/null fi # We've finished updating the kernel. touch $1/kerneldone # Do we need to ask for a reboot now? if [ -f $1/kernelfirst ] && [ -s INDEX-OLD -o -s INDEX-NEW ]; then cat <<-EOF Kernel updates have been installed. Please reboot and run "$0 install" again to finish installing updates. EOF exit 0 fi fi # If we haven't already dealt with the world, deal with it. if ! [ -f $1/worlddone ]; then # Create any necessary directories first grep -vE '^/boot/' $1/INDEX-NEW | grep -E '^[^|]+\|d\|' > INDEX-NEW install_from_index INDEX-NEW || return 1 # Install new runtime linker grep -vE '^/boot/' $1/INDEX-NEW | grep -vE '^[^|]+\|d\|' | grep -E '^/libexec/ld-elf[^|]*\.so\.[0-9]+\|' > INDEX-NEW install_from_index INDEX-NEW || return 1 # Install new shared libraries next grep -vE '^/boot/' $1/INDEX-NEW | grep -vE '^[^|]+\|d\|' | grep -vE '^/libexec/ld-elf[^|]*\.so\.[0-9]+\|' | grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-NEW install_from_index INDEX-NEW || return 1 # Deal with everything else grep -vE '^/boot/' $1/INDEX-OLD | grep -vE '^[^|]+\|d\|' | grep -vE '^/libexec/ld-elf[^|]*\.so\.[0-9]+\|' | grep -vE '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-OLD grep -vE '^/boot/' $1/INDEX-NEW | grep -vE '^[^|]+\|d\|' | grep -vE '^/libexec/ld-elf[^|]*\.so\.[0-9]+\|' | grep -vE '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-NEW install_from_index INDEX-NEW || return 1 install_delete INDEX-OLD INDEX-NEW || return 1 # Rebuild /etc/spwd.db and /etc/pwd.db if necessary. - if [ /etc/master.passwd -nt /etc/spwd.db ] || - [ /etc/master.passwd -nt /etc/pwd.db ]; then - pwd_mkdb /etc/master.passwd + if [ ${BASEDIR}/etc/master.passwd -nt ${BASEDIR}/etc/spwd.db ] || + [ ${BASEDIR}/etc/master.passwd -nt ${BASEDIR}/etc/pwd.db ]; then + pwd_mkdb -d ${BASEDIR}/etc ${BASEDIR}/etc/master.passwd fi # Rebuild /etc/login.conf.db if necessary. - if [ /etc/login.conf -nt /etc/login.conf.db ]; then - cap_mkdb /etc/login.conf + if [ ${BASEDIR}/etc/login.conf -nt ${BASEDIR}/etc/login.conf.db ]; then + cap_mkdb ${BASEDIR}/etc/login.conf fi # We've finished installing the world and deleting old files # which are not shared libraries. touch $1/worlddone # Do we need to ask the user to portupgrade now? grep -vE '^/boot/' $1/INDEX-NEW | grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' | cut -f 1 -d '|' | sort > newfiles if grep -vE '^/boot/' $1/INDEX-OLD | grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' | cut -f 1 -d '|' | sort | join -v 1 - newfiles | grep -q .; then cat <<-EOF Completing this upgrade requires removing old shared object files. Please rebuild all installed 3rd party software (e.g., programs installed from the ports tree) and then run "$0 install" again to finish installing updates. EOF rm newfiles exit 0 fi rm newfiles fi # Remove old shared libraries grep -vE '^/boot/' $1/INDEX-NEW | grep -vE '^[^|]+\|d\|' | grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-NEW grep -vE '^/boot/' $1/INDEX-OLD | grep -vE '^[^|]+\|d\|' | grep -E '^[^|]*/lib/[^|]*\.so\.[0-9]+\|' > INDEX-OLD install_delete INDEX-OLD INDEX-NEW || return 1 # Remove old directories grep -vE '^/boot/' $1/INDEX-NEW | grep -E '^[^|]+\|d\|' > INDEX-NEW grep -vE '^/boot/' $1/INDEX-OLD | grep -E '^[^|]+\|d\|' > INDEX-OLD install_delete INDEX-OLD INDEX-NEW || return 1 # Remove temporary files rm INDEX-OLD INDEX-NEW } # Rearrange bits to allow the installed updates to be rolled back install_setup_rollback () { # Remove the "reboot after installing kernel", "kernel updated", and # "finished installing the world" flags if present -- they are # irrelevant when rolling back updates. if [ -f ${BDHASH}-install/kernelfirst ]; then rm ${BDHASH}-install/kernelfirst rm ${BDHASH}-install/kerneldone fi if [ -f ${BDHASH}-install/worlddone ]; then rm ${BDHASH}-install/worlddone fi if [ -L ${BDHASH}-rollback ]; then mv ${BDHASH}-rollback ${BDHASH}-install/rollback fi mv ${BDHASH}-install ${BDHASH}-rollback } # Actually install updates install_run () { echo -n "Installing updates..." # Make sure we have all the files we should have install_verify ${BDHASH}-install/INDEX-OLD \ ${BDHASH}-install/INDEX-NEW || return 1 # Remove system immutable flag from files install_unschg ${BDHASH}-install/INDEX-OLD \ ${BDHASH}-install/INDEX-NEW || return 1 # Install new files, delete old files, and update linker.hints install_files ${BDHASH}-install || return 1 # Rearrange bits to allow the installed updates to be rolled back install_setup_rollback echo " done." } # Rearrange bits to allow the previous set of updates to be rolled back next. rollback_setup_rollback () { if [ -L ${BDHASH}-rollback/rollback ]; then mv ${BDHASH}-rollback/rollback rollback-tmp rm -r ${BDHASH}-rollback/ rm ${BDHASH}-rollback mv rollback-tmp ${BDHASH}-rollback else rm -r ${BDHASH}-rollback/ rm ${BDHASH}-rollback fi } # Install old files, delete new files, and update linker.hints rollback_files () { # Install old shared library files which don't have the same path as # a new shared library file. grep -vE '^/boot/' $1/INDEX-NEW | grep -E '/lib/.*\.so\.[0-9]+\|' | cut -f 1 -d '|' | sort > INDEX-NEW.libs.flist grep -vE '^/boot/' $1/INDEX-OLD | grep -E '/lib/.*\.so\.[0-9]+\|' | sort -k 1,1 -t '|' - | join -t '|' -v 1 - INDEX-NEW.libs.flist > INDEX-OLD install_from_index INDEX-OLD || return 1 # Deal with files which are neither kernel nor shared library grep -vE '^/boot/' $1/INDEX-OLD | grep -vE '/lib/.*\.so\.[0-9]+\|' > INDEX-OLD grep -vE '^/boot/' $1/INDEX-NEW | grep -vE '/lib/.*\.so\.[0-9]+\|' > INDEX-NEW install_from_index INDEX-OLD || return 1 install_delete INDEX-NEW INDEX-OLD || return 1 # Install any old shared library files which we didn't install above. grep -vE '^/boot/' $1/INDEX-OLD | grep -E '/lib/.*\.so\.[0-9]+\|' | sort -k 1,1 -t '|' - | join -t '|' - INDEX-NEW.libs.flist > INDEX-OLD install_from_index INDEX-OLD || return 1 # Delete unneeded shared library files grep -vE '^/boot/' $1/INDEX-OLD | grep -E '/lib/.*\.so\.[0-9]+\|' > INDEX-OLD grep -vE '^/boot/' $1/INDEX-NEW | grep -E '/lib/.*\.so\.[0-9]+\|' > INDEX-NEW install_delete INDEX-NEW INDEX-OLD || return 1 # Deal with kernel files grep -E '^/boot/' $1/INDEX-OLD > INDEX-OLD grep -E '^/boot/' $1/INDEX-NEW > INDEX-NEW install_from_index INDEX-OLD || return 1 install_delete INDEX-NEW INDEX-OLD || return 1 if [ -s INDEX-OLD -o -s INDEX-NEW ]; then kldxref -R /boot/ 2>/dev/null fi # Remove temporary files rm INDEX-OLD INDEX-NEW INDEX-NEW.libs.flist } # Actually rollback updates rollback_run () { echo -n "Uninstalling updates..." # If there are updates waiting to be installed, remove them; we # want the user to re-run 'fetch' after rolling back updates. if [ -L ${BDHASH}-install ]; then rm -r ${BDHASH}-install/ rm ${BDHASH}-install fi # Make sure we have all the files we should have install_verify ${BDHASH}-rollback/INDEX-NEW \ ${BDHASH}-rollback/INDEX-OLD || return 1 # Remove system immutable flag from files install_unschg ${BDHASH}-rollback/INDEX-NEW \ ${BDHASH}-rollback/INDEX-OLD || return 1 # Install old files, delete new files, and update linker.hints rollback_files ${BDHASH}-rollback || return 1 # Remove the rollback directory and the symlink pointing to it; and # rearrange bits to allow the previous set of updates to be rolled # back next. rollback_setup_rollback echo " done." } # Compare INDEX-ALL and INDEX-PRESENT and print warnings about differences. IDS_compare () { # Get all the lines which mismatch in something other than file # flags. We ignore file flags because sysinstall doesn't seem to # set them when it installs FreeBSD; warning about these adds a # very large amount of noise. cut -f 1-5,7-8 -d '|' $1 > $1.noflags sort -k 1,1 -t '|' $1.noflags > $1.sorted cut -f 1-5,7-8 -d '|' $2 | comm -13 $1.noflags - | fgrep -v '|-|||||' | sort -k 1,1 -t '|' | join -t '|' $1.sorted - > INDEX-NOTMATCHING # Ignore files which match IDSIGNOREPATHS. for X in ${IDSIGNOREPATHS}; do grep -E "^${X}" INDEX-NOTMATCHING done | sort -u | comm -13 - INDEX-NOTMATCHING > INDEX-NOTMATCHING.tmp mv INDEX-NOTMATCHING.tmp INDEX-NOTMATCHING # Go through the lines and print warnings. local IFS='|' while read FPATH TYPE OWNER GROUP PERM HASH LINK P_TYPE P_OWNER P_GROUP P_PERM P_HASH P_LINK; do # Warn about different object types. if ! [ "${TYPE}" = "${P_TYPE}" ]; then echo -n "${FPATH} is a " case "${P_TYPE}" in f) echo -n "regular file, " ;; d) echo -n "directory, " ;; L) echo -n "symlink, " ;; esac echo -n "but should be a " case "${TYPE}" in f) echo -n "regular file." ;; d) echo -n "directory." ;; L) echo -n "symlink." ;; esac echo # Skip other tests, since they don't make sense if # we're comparing different object types. continue fi # Warn about different owners. if ! [ "${OWNER}" = "${P_OWNER}" ]; then echo -n "${FPATH} is owned by user id ${P_OWNER}, " echo "but should be owned by user id ${OWNER}." fi # Warn about different groups. if ! [ "${GROUP}" = "${P_GROUP}" ]; then echo -n "${FPATH} is owned by group id ${P_GROUP}, " echo "but should be owned by group id ${GROUP}." fi # Warn about different permissions. We do not warn about # different permissions on symlinks, since some archivers # don't extract symlink permissions correctly and they are # ignored anyway. if ! [ "${PERM}" = "${P_PERM}" ] && ! [ "${TYPE}" = "L" ]; then echo -n "${FPATH} has ${P_PERM} permissions, " echo "but should have ${PERM} permissions." fi # Warn about different file hashes / symlink destinations. if ! [ "${HASH}" = "${P_HASH}" ]; then if [ "${TYPE}" = "L" ]; then echo -n "${FPATH} is a symlink to ${P_HASH}, " echo "but should be a symlink to ${HASH}." fi if [ "${TYPE}" = "f" ]; then echo -n "${FPATH} has SHA256 hash ${P_HASH}, " echo "but should have SHA256 hash ${HASH}." fi fi # We don't warn about different hard links, since some # some archivers break hard links, and as long as the # underlying data is correct they really don't matter. done < INDEX-NOTMATCHING # Clean up rm $1 $1.noflags $1.sorted $2 INDEX-NOTMATCHING } # Do the work involved in comparing the system to a "known good" index IDS_run () { workdir_init || return 1 # Prepare the mirror list. fetch_pick_server_init && fetch_pick_server # Try to fetch the public key until we run out of servers. while ! fetch_key; do fetch_pick_server || return 1 done # Try to fetch the metadata index signature ("tag") until we run # out of available servers; and sanity check the downloaded tag. while ! fetch_tag; do fetch_pick_server || return 1 done fetch_tagsanity || return 1 # Fetch INDEX-OLD and INDEX-ALL. fetch_metadata INDEX-OLD INDEX-ALL || return 1 # Generate filtered INDEX-OLD and INDEX-ALL files containing only # the components we want and without anything marked as "Ignore". fetch_filter_metadata INDEX-OLD || return 1 fetch_filter_metadata INDEX-ALL || return 1 # Merge the INDEX-OLD and INDEX-ALL files into INDEX-ALL. sort INDEX-OLD INDEX-ALL > INDEX-ALL.tmp mv INDEX-ALL.tmp INDEX-ALL rm INDEX-OLD # Translate /boot/${KERNCONF} to ${KERNELDIR} fetch_filter_kernel_names INDEX-ALL ${KERNCONF} # Inspect the system and generate an INDEX-PRESENT file. fetch_inspect_system INDEX-ALL INDEX-PRESENT /dev/null || return 1 # Compare INDEX-ALL and INDEX-PRESENT and print warnings about any # differences. IDS_compare INDEX-ALL INDEX-PRESENT } #### Main functions -- call parameter-handling and core functions # Using the command line, configuration file, and defaults, # set all the parameters which are needed later. get_params () { init_params parse_cmdline $@ parse_conffile default_params } # Fetch command. Make sure that we're being called # interactively, then run fetch_check_params and fetch_run cmd_fetch () { if [ ! -t 0 ]; then echo -n "`basename $0` fetch should not " echo "be run non-interactively." echo "Run `basename $0` cron instead." exit 1 fi fetch_check_params fetch_run || exit 1 } # Cron command. Make sure the parameters are sensible; wait # rand(3600) seconds; then fetch updates. While fetching updates, # send output to a temporary file; only print that file if the # fetching failed. cmd_cron () { fetch_check_params sleep `jot -r 1 0 3600` TMPFILE=`mktemp /tmp/freebsd-update.XXXXXX` || exit 1 if ! fetch_run >> ${TMPFILE} || ! grep -q "No updates needed" ${TMPFILE} || [ ${VERBOSELEVEL} = "debug" ]; then mail -s "`hostname` security updates" ${MAILTO} < ${TMPFILE} fi rm ${TMPFILE} } # Fetch files for upgrading to a new release. cmd_upgrade () { upgrade_check_params upgrade_run || exit 1 } # Install downloaded updates. cmd_install () { install_check_params install_run || exit 1 } # Rollback most recently installed updates. cmd_rollback () { rollback_check_params rollback_run || exit 1 } # Compare system against a "known good" index. cmd_IDS () { IDS_check_params IDS_run || exit 1 } #### Entry point # Make sure we find utilities from the base system export PATH=/sbin:/bin:/usr/sbin:/usr/bin:${PATH} # Set a pager if the user doesn't if [ -z "$PAGER" ]; then PAGER=/usr/bin/more fi # Set LC_ALL in order to avoid problems with character ranges like [A-Z]. export LC_ALL=C get_params $@ for COMMAND in ${COMMANDS}; do cmd_${COMMAND} done Index: projects/building-blocks/usr.sbin/gssd/Makefile =================================================================== --- projects/building-blocks/usr.sbin/gssd/Makefile (revision 278776) +++ projects/building-blocks/usr.sbin/gssd/Makefile (revision 278777) @@ -1,35 +1,35 @@ # $FreeBSD$ .include PROG= gssd MAN= gssd.8 SRCS= gssd.c gssd.h gssd_svc.c gssd_xdr.c gssd_prot.c CFLAGS+= -I. WARNS?= 1 LIBADD= gssapi .if ${MK_KERBEROS_SUPPORT} != "no" LIBADD+= krb5 roken .else CFLAGS+= -DWITHOUT_KERBEROS .endif -CLEANFILES= gssd_svc.c gssd.h +CLEANFILES= gssd_svc.c gssd_xdr.c gssd.h RPCSRC= ${.CURDIR}/../../sys/kgssapi/gssd.x RPCGEN= RPCGEN_CPP=${CPP:Q} rpcgen -L -C -M gssd_svc.c: ${RPCSRC} gssd.h ${RPCGEN} -m -o ${.TARGET} ${RPCSRC} gssd_xdr.c: ${RPCSRC} gssd.h ${RPCGEN} -c -o ${.TARGET} ${RPCSRC} gssd.h: ${RPCSRC} ${RPCGEN} -h -o ${.TARGET} ${RPCSRC} .PATH: ${.CURDIR}/../../sys/kgssapi .include Index: projects/building-blocks/usr.sbin/gssd/gssd.c =================================================================== --- projects/building-blocks/usr.sbin/gssd/gssd.c (revision 278776) +++ projects/building-blocks/usr.sbin/gssd/gssd.c (revision 278777) @@ -1,1289 +1,1290 @@ /*- * Copyright (c) 2008 Isilon Inc http://www.isilon.com/ * Authors: Doug Rabson * Developed with Red Inc: Alfred Perlstein * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #ifndef WITHOUT_KERBEROS #include #endif #include #include #include #include #include #include #include #include #include #include #include "gssd.h" #ifndef _PATH_GSS_MECH #define _PATH_GSS_MECH "/etc/gss/mech" #endif #ifndef _PATH_GSSDSOCK #define _PATH_GSSDSOCK "/var/run/gssd.sock" #endif #define GSSD_CREDENTIAL_CACHE_FILE "/tmp/krb5cc_gssd" struct gss_resource { LIST_ENTRY(gss_resource) gr_link; uint64_t gr_id; /* indentifier exported to kernel */ void* gr_res; /* GSS-API resource pointer */ }; LIST_HEAD(gss_resource_list, gss_resource) gss_resources; int gss_resource_count; uint32_t gss_next_id; uint32_t gss_start_time; int debug_level; static char ccfile_dirlist[PATH_MAX + 1], ccfile_substring[NAME_MAX + 1]; static char pref_realm[1024]; static int verbose; static int use_old_des; static int hostbased_initiator_cred; #ifndef WITHOUT_KERBEROS /* 1.2.752.43.13.14 */ static gss_OID_desc gss_krb5_set_allowable_enctypes_x_desc = {6, (void *) "\x2a\x85\x70\x2b\x0d\x0e"}; static gss_OID GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X = &gss_krb5_set_allowable_enctypes_x_desc; static gss_OID_desc gss_krb5_mech_oid_x_desc = {9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" }; static gss_OID GSS_KRB5_MECH_OID_X = &gss_krb5_mech_oid_x_desc; #endif static void gssd_load_mech(void); static int find_ccache_file(const char *, uid_t, char *); static int is_a_valid_tgt_cache(const char *, uid_t, int *, time_t *); static void gssd_verbose_out(const char *, ...); #ifndef WITHOUT_KERBEROS static krb5_error_code gssd_get_cc_from_keytab(const char *); static OM_uint32 gssd_get_user_cred(OM_uint32 *, uid_t, gss_cred_id_t *); #endif void gssd_terminate(int); extern void gssd_1(struct svc_req *rqstp, SVCXPRT *transp); extern int gssd_syscall(char *path); int main(int argc, char **argv) { /* * We provide an RPC service on a local-domain socket. The * kernel's GSS-API code will pass what it can't handle * directly to us. */ struct sockaddr_un sun; int fd, oldmask, ch, debug; SVCXPRT *xprt; /* * Initialize the credential cache file name substring and the * search directory list. */ strlcpy(ccfile_substring, "krb5cc_", sizeof(ccfile_substring)); ccfile_dirlist[0] = '\0'; pref_realm[0] = '\0'; debug = 0; verbose = 0; while ((ch = getopt(argc, argv, "dhovs:c:r:")) != -1) { switch (ch) { case 'd': debug_level++; break; case 'h': #ifndef WITHOUT_KERBEROS /* * Enable use of a host based initiator credential * in the default keytab file. */ hostbased_initiator_cred = 1; #else errx(1, "This option not available when built" " without MK_KERBEROS\n"); #endif break; case 'o': #ifndef WITHOUT_KERBEROS /* * Force use of DES and the old type of GSSAPI token. */ use_old_des = 1; #else errx(1, "This option not available when built" " without MK_KERBEROS\n"); #endif break; case 'v': verbose = 1; break; case 's': #ifndef WITHOUT_KERBEROS /* * Set the directory search list. This enables use of * find_ccache_file() to search the directories for a * suitable credentials cache file. */ strlcpy(ccfile_dirlist, optarg, sizeof(ccfile_dirlist)); #else errx(1, "This option not available when built" " without MK_KERBEROS\n"); #endif break; case 'c': /* * Specify a non-default credential cache file * substring. */ strlcpy(ccfile_substring, optarg, sizeof(ccfile_substring)); break; case 'r': /* * Set the preferred realm for the credential cache tgt. */ strlcpy(pref_realm, optarg, sizeof(pref_realm)); break; default: fprintf(stderr, "usage: %s [-d] [-s dir-list] [-c file-substring]" " [-r preferred-realm]\n", argv[0]); exit(1); break; } } gssd_load_mech(); if (!debug_level) { - daemon(0, 0); + if (daemon(0, 0) != 0) + err(1, "Can't daemonize"); signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); signal(SIGHUP, SIG_IGN); } signal(SIGTERM, gssd_terminate); memset(&sun, 0, sizeof sun); sun.sun_family = AF_LOCAL; unlink(_PATH_GSSDSOCK); strcpy(sun.sun_path, _PATH_GSSDSOCK); sun.sun_len = SUN_LEN(&sun); fd = socket(AF_LOCAL, SOCK_STREAM, 0); - if (!fd) { + if (fd < 0) { if (debug_level == 0) { syslog(LOG_ERR, "Can't create local gssd socket"); exit(1); } err(1, "Can't create local gssd socket"); } oldmask = umask(S_IXUSR|S_IRWXG|S_IRWXO); if (bind(fd, (struct sockaddr *) &sun, sun.sun_len) < 0) { if (debug_level == 0) { syslog(LOG_ERR, "Can't bind local gssd socket"); exit(1); } err(1, "Can't bind local gssd socket"); } umask(oldmask); if (listen(fd, SOMAXCONN) < 0) { if (debug_level == 0) { syslog(LOG_ERR, "Can't listen on local gssd socket"); exit(1); } err(1, "Can't listen on local gssd socket"); } xprt = svc_vc_create(fd, RPC_MAXDATASIZE, RPC_MAXDATASIZE); if (!xprt) { if (debug_level == 0) { syslog(LOG_ERR, "Can't create transport for local gssd socket"); exit(1); } err(1, "Can't create transport for local gssd socket"); } if (!svc_reg(xprt, GSSD, GSSDVERS, gssd_1, NULL)) { if (debug_level == 0) { syslog(LOG_ERR, "Can't register service for local gssd socket"); exit(1); } err(1, "Can't register service for local gssd socket"); } LIST_INIT(&gss_resources); gss_next_id = 1; gss_start_time = time(0); gssd_syscall(_PATH_GSSDSOCK); svc_run(); return (0); } static void gssd_load_mech(void) { FILE *fp; char buf[256]; char *p; char *name, *oid, *lib, *kobj; fp = fopen(_PATH_GSS_MECH, "r"); if (!fp) return; while (fgets(buf, sizeof(buf), fp)) { if (*buf == '#') continue; p = buf; name = strsep(&p, "\t\n "); if (p) while (isspace(*p)) p++; oid = strsep(&p, "\t\n "); if (p) while (isspace(*p)) p++; lib = strsep(&p, "\t\n "); if (p) while (isspace(*p)) p++; kobj = strsep(&p, "\t\n "); if (!name || !oid || !lib || !kobj) continue; if (strcmp(kobj, "-")) { /* * Attempt to load the kernel module if its * not already present. */ if (modfind(kobj) < 0) { if (kldload(kobj) < 0) { fprintf(stderr, "%s: can't find or load kernel module %s for %s\n", getprogname(), kobj, name); } } } } fclose(fp); } static void * gssd_find_resource(uint64_t id) { struct gss_resource *gr; if (!id) return (NULL); LIST_FOREACH(gr, &gss_resources, gr_link) if (gr->gr_id == id) return (gr->gr_res); return (NULL); } static uint64_t gssd_make_resource(void *res) { struct gss_resource *gr; if (!res) return (0); gr = malloc(sizeof(struct gss_resource)); if (!gr) return (0); gr->gr_id = (gss_next_id++) + ((uint64_t) gss_start_time << 32); gr->gr_res = res; LIST_INSERT_HEAD(&gss_resources, gr, gr_link); gss_resource_count++; if (debug_level > 1) printf("%d resources allocated\n", gss_resource_count); return (gr->gr_id); } static void gssd_delete_resource(uint64_t id) { struct gss_resource *gr; LIST_FOREACH(gr, &gss_resources, gr_link) { if (gr->gr_id == id) { LIST_REMOVE(gr, gr_link); free(gr); gss_resource_count--; if (debug_level > 1) printf("%d resources allocated\n", gss_resource_count); return; } } } static void gssd_verbose_out(const char *fmt, ...) { va_list ap; if (verbose != 0) { va_start(ap, fmt); if (debug_level == 0) vsyslog(LOG_INFO | LOG_DAEMON, fmt, ap); else vfprintf(stderr, fmt, ap); va_end(ap); } } bool_t gssd_null_1_svc(void *argp, void *result, struct svc_req *rqstp) { gssd_verbose_out("gssd_null: done\n"); return (TRUE); } bool_t gssd_init_sec_context_1_svc(init_sec_context_args *argp, init_sec_context_res *result, struct svc_req *rqstp) { gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; gss_name_t name = GSS_C_NO_NAME; char ccname[PATH_MAX + 5 + 1], *cp, *cp2; int gotone, gotcred; OM_uint32 min_stat; #ifndef WITHOUT_KERBEROS gss_buffer_desc principal_desc; char enctype[sizeof(uint32_t)]; int key_enctype; OM_uint32 maj_stat; #endif memset(result, 0, sizeof(*result)); if (hostbased_initiator_cred != 0 && argp->cred != 0 && argp->uid == 0) { /* * These credentials are for a host based initiator name * in a keytab file, which should now have credentials * in /tmp/krb5cc_gssd, because gss_acquire_cred() did * the equivalent of "kinit -k". */ snprintf(ccname, sizeof(ccname), "FILE:%s", GSSD_CREDENTIAL_CACHE_FILE); } else if (ccfile_dirlist[0] != '\0' && argp->cred == 0) { /* * For the "-s" case and no credentials provided as an * argument, search the directory list for an appropriate * credential cache file. If the search fails, return failure. */ gotone = 0; cp = ccfile_dirlist; do { cp2 = strchr(cp, ':'); if (cp2 != NULL) *cp2 = '\0'; gotone = find_ccache_file(cp, argp->uid, ccname); if (gotone != 0) break; if (cp2 != NULL) *cp2++ = ':'; cp = cp2; } while (cp != NULL && *cp != '\0'); if (gotone == 0) { result->major_status = GSS_S_CREDENTIALS_EXPIRED; gssd_verbose_out("gssd_init_sec_context: -s no" " credential cache file found for uid=%d\n", (int)argp->uid); return (TRUE); } } else { /* * If there wasn't a "-s" option or the credentials have * been provided as an argument, do it the old way. * When credentials are provided, the uid should be root. */ if (argp->cred != 0 && argp->uid != 0) { if (debug_level == 0) syslog(LOG_ERR, "gss_init_sec_context:" " cred for non-root"); else fprintf(stderr, "gss_init_sec_context:" " cred for non-root\n"); } snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d", (int) argp->uid); } setenv("KRB5CCNAME", ccname, TRUE); if (argp->cred) { cred = gssd_find_resource(argp->cred); if (!cred) { result->major_status = GSS_S_CREDENTIALS_EXPIRED; gssd_verbose_out("gssd_init_sec_context: cred" " resource not found\n"); return (TRUE); } } if (argp->ctx) { ctx = gssd_find_resource(argp->ctx); if (!ctx) { result->major_status = GSS_S_CONTEXT_EXPIRED; gssd_verbose_out("gssd_init_sec_context: context" " resource not found\n"); return (TRUE); } } if (argp->name) { name = gssd_find_resource(argp->name); if (!name) { result->major_status = GSS_S_BAD_NAME; gssd_verbose_out("gssd_init_sec_context: name" " resource not found\n"); return (TRUE); } } gotcred = 0; #ifndef WITHOUT_KERBEROS if (use_old_des != 0) { if (cred == GSS_C_NO_CREDENTIAL) { /* Acquire a credential for the uid. */ maj_stat = gssd_get_user_cred(&min_stat, argp->uid, &cred); if (maj_stat == GSS_S_COMPLETE) gotcred = 1; else gssd_verbose_out("gssd_init_sec_context: " "get user cred failed uid=%d major=0x%x " "minor=%d\n", (int)argp->uid, (unsigned int)maj_stat, (int)min_stat); } if (cred != GSS_C_NO_CREDENTIAL) { key_enctype = ETYPE_DES_CBC_CRC; enctype[0] = (key_enctype >> 24) & 0xff; enctype[1] = (key_enctype >> 16) & 0xff; enctype[2] = (key_enctype >> 8) & 0xff; enctype[3] = key_enctype & 0xff; principal_desc.length = sizeof(enctype); principal_desc.value = enctype; result->major_status = gss_set_cred_option( &result->minor_status, &cred, GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X, &principal_desc); gssd_verbose_out("gssd_init_sec_context: set allowable " "enctype major=0x%x minor=%d\n", (unsigned int)result->major_status, (int)result->minor_status); if (result->major_status != GSS_S_COMPLETE) { if (gotcred != 0) gss_release_cred(&min_stat, &cred); return (TRUE); } } } #endif result->major_status = gss_init_sec_context(&result->minor_status, cred, &ctx, name, argp->mech_type, argp->req_flags, argp->time_req, argp->input_chan_bindings, &argp->input_token, &result->actual_mech_type, &result->output_token, &result->ret_flags, &result->time_rec); gssd_verbose_out("gssd_init_sec_context: done major=0x%x minor=%d" " uid=%d\n", (unsigned int)result->major_status, (int)result->minor_status, (int)argp->uid); if (gotcred != 0) gss_release_cred(&min_stat, &cred); if (result->major_status == GSS_S_COMPLETE || result->major_status == GSS_S_CONTINUE_NEEDED) { if (argp->ctx) result->ctx = argp->ctx; else result->ctx = gssd_make_resource(ctx); } return (TRUE); } bool_t gssd_accept_sec_context_1_svc(accept_sec_context_args *argp, accept_sec_context_res *result, struct svc_req *rqstp) { gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; gss_name_t src_name; gss_cred_id_t delegated_cred_handle; memset(result, 0, sizeof(*result)); if (argp->ctx) { ctx = gssd_find_resource(argp->ctx); if (!ctx) { result->major_status = GSS_S_CONTEXT_EXPIRED; gssd_verbose_out("gssd_accept_sec_context: ctx" " resource not found\n"); return (TRUE); } } if (argp->cred) { cred = gssd_find_resource(argp->cred); if (!cred) { result->major_status = GSS_S_CREDENTIALS_EXPIRED; gssd_verbose_out("gssd_accept_sec_context: cred" " resource not found\n"); return (TRUE); } } memset(result, 0, sizeof(*result)); result->major_status = gss_accept_sec_context(&result->minor_status, &ctx, cred, &argp->input_token, argp->input_chan_bindings, &src_name, &result->mech_type, &result->output_token, &result->ret_flags, &result->time_rec, &delegated_cred_handle); gssd_verbose_out("gssd_accept_sec_context: done major=0x%x minor=%d\n", (unsigned int)result->major_status, (int)result->minor_status); if (result->major_status == GSS_S_COMPLETE || result->major_status == GSS_S_CONTINUE_NEEDED) { if (argp->ctx) result->ctx = argp->ctx; else result->ctx = gssd_make_resource(ctx); result->src_name = gssd_make_resource(src_name); result->delegated_cred_handle = gssd_make_resource(delegated_cred_handle); } return (TRUE); } bool_t gssd_delete_sec_context_1_svc(delete_sec_context_args *argp, delete_sec_context_res *result, struct svc_req *rqstp) { gss_ctx_id_t ctx = gssd_find_resource(argp->ctx); if (ctx) { result->major_status = gss_delete_sec_context( &result->minor_status, &ctx, &result->output_token); gssd_delete_resource(argp->ctx); } else { result->major_status = GSS_S_COMPLETE; result->minor_status = 0; } gssd_verbose_out("gssd_delete_sec_context: done major=0x%x minor=%d\n", (unsigned int)result->major_status, (int)result->minor_status); return (TRUE); } bool_t gssd_export_sec_context_1_svc(export_sec_context_args *argp, export_sec_context_res *result, struct svc_req *rqstp) { gss_ctx_id_t ctx = gssd_find_resource(argp->ctx); if (ctx) { result->major_status = gss_export_sec_context( &result->minor_status, &ctx, &result->interprocess_token); result->format = KGSS_HEIMDAL_1_1; gssd_delete_resource(argp->ctx); } else { result->major_status = GSS_S_FAILURE; result->minor_status = 0; result->interprocess_token.length = 0; result->interprocess_token.value = NULL; } gssd_verbose_out("gssd_export_sec_context: done major=0x%x minor=%d\n", (unsigned int)result->major_status, (int)result->minor_status); return (TRUE); } bool_t gssd_import_name_1_svc(import_name_args *argp, import_name_res *result, struct svc_req *rqstp) { gss_name_t name; result->major_status = gss_import_name(&result->minor_status, &argp->input_name_buffer, argp->input_name_type, &name); gssd_verbose_out("gssd_import_name: done major=0x%x minor=%d\n", (unsigned int)result->major_status, (int)result->minor_status); if (result->major_status == GSS_S_COMPLETE) result->output_name = gssd_make_resource(name); else result->output_name = 0; return (TRUE); } bool_t gssd_canonicalize_name_1_svc(canonicalize_name_args *argp, canonicalize_name_res *result, struct svc_req *rqstp) { gss_name_t name = gssd_find_resource(argp->input_name); gss_name_t output_name; memset(result, 0, sizeof(*result)); if (!name) { result->major_status = GSS_S_BAD_NAME; return (TRUE); } result->major_status = gss_canonicalize_name(&result->minor_status, name, argp->mech_type, &output_name); gssd_verbose_out("gssd_canonicalize_name: done major=0x%x minor=%d\n", (unsigned int)result->major_status, (int)result->minor_status); if (result->major_status == GSS_S_COMPLETE) result->output_name = gssd_make_resource(output_name); else result->output_name = 0; return (TRUE); } bool_t gssd_export_name_1_svc(export_name_args *argp, export_name_res *result, struct svc_req *rqstp) { gss_name_t name = gssd_find_resource(argp->input_name); memset(result, 0, sizeof(*result)); if (!name) { result->major_status = GSS_S_BAD_NAME; gssd_verbose_out("gssd_export_name: name resource not found\n"); return (TRUE); } result->major_status = gss_export_name(&result->minor_status, name, &result->exported_name); gssd_verbose_out("gssd_export_name: done major=0x%x minor=%d\n", (unsigned int)result->major_status, (int)result->minor_status); return (TRUE); } bool_t gssd_release_name_1_svc(release_name_args *argp, release_name_res *result, struct svc_req *rqstp) { gss_name_t name = gssd_find_resource(argp->input_name); if (name) { result->major_status = gss_release_name(&result->minor_status, &name); gssd_delete_resource(argp->input_name); } else { result->major_status = GSS_S_COMPLETE; result->minor_status = 0; } gssd_verbose_out("gssd_release_name: done major=0x%x minor=%d\n", (unsigned int)result->major_status, (int)result->minor_status); return (TRUE); } bool_t gssd_pname_to_uid_1_svc(pname_to_uid_args *argp, pname_to_uid_res *result, struct svc_req *rqstp) { gss_name_t name = gssd_find_resource(argp->pname); uid_t uid; char buf[1024], *bufp; struct passwd pwd, *pw; size_t buflen; int error; static size_t buflen_hint = 1024; memset(result, 0, sizeof(*result)); if (name) { result->major_status = gss_pname_to_uid(&result->minor_status, name, argp->mech, &uid); if (result->major_status == GSS_S_COMPLETE) { result->uid = uid; buflen = buflen_hint; for (;;) { pw = NULL; bufp = buf; if (buflen > sizeof(buf)) bufp = malloc(buflen); if (bufp == NULL) break; error = getpwuid_r(uid, &pwd, bufp, buflen, &pw); if (error != ERANGE) break; if (buflen > sizeof(buf)) free(bufp); buflen += 1024; if (buflen > buflen_hint) buflen_hint = buflen; } if (pw) { int len = NGRPS; int groups[NGRPS]; result->gid = pw->pw_gid; getgrouplist(pw->pw_name, pw->pw_gid, groups, &len); result->gidlist.gidlist_len = len; result->gidlist.gidlist_val = mem_alloc(len * sizeof(int)); memcpy(result->gidlist.gidlist_val, groups, len * sizeof(int)); gssd_verbose_out("gssd_pname_to_uid: mapped" " to uid=%d, gid=%d\n", (int)result->uid, (int)result->gid); } else { result->gid = 65534; result->gidlist.gidlist_len = 0; result->gidlist.gidlist_val = NULL; gssd_verbose_out("gssd_pname_to_uid: mapped" " to uid=%d, but no groups\n", (int)result->uid); } if (bufp != NULL && buflen > sizeof(buf)) free(bufp); } else gssd_verbose_out("gssd_pname_to_uid: failed major=0x%x" " minor=%d\n", (unsigned int)result->major_status, (int)result->minor_status); } else { result->major_status = GSS_S_BAD_NAME; result->minor_status = 0; gssd_verbose_out("gssd_pname_to_uid: no name\n"); } return (TRUE); } bool_t gssd_acquire_cred_1_svc(acquire_cred_args *argp, acquire_cred_res *result, struct svc_req *rqstp) { gss_name_t desired_name = GSS_C_NO_NAME; gss_cred_id_t cred; char ccname[PATH_MAX + 5 + 1], *cp, *cp2; int gotone; #ifndef WITHOUT_KERBEROS gss_buffer_desc namebuf; uint32_t minstat; krb5_error_code kret; #endif memset(result, 0, sizeof(*result)); if (argp->desired_name) { desired_name = gssd_find_resource(argp->desired_name); if (!desired_name) { result->major_status = GSS_S_BAD_NAME; gssd_verbose_out("gssd_acquire_cred: no desired name" " found\n"); return (TRUE); } } #ifndef WITHOUT_KERBEROS if (hostbased_initiator_cred != 0 && argp->desired_name != 0 && argp->uid == 0 && argp->cred_usage == GSS_C_INITIATE) { /* This is a host based initiator name in the keytab file. */ snprintf(ccname, sizeof(ccname), "FILE:%s", GSSD_CREDENTIAL_CACHE_FILE); setenv("KRB5CCNAME", ccname, TRUE); result->major_status = gss_display_name(&result->minor_status, desired_name, &namebuf, NULL); gssd_verbose_out("gssd_acquire_cred: desired name for host " "based initiator cred major=0x%x minor=%d\n", (unsigned int)result->major_status, (int)result->minor_status); if (result->major_status != GSS_S_COMPLETE) return (TRUE); if (namebuf.length > PATH_MAX + 5) { result->minor_status = 0; result->major_status = GSS_S_FAILURE; return (TRUE); } memcpy(ccname, namebuf.value, namebuf.length); ccname[namebuf.length] = '\0'; if ((cp = strchr(ccname, '@')) != NULL) *cp = '/'; kret = gssd_get_cc_from_keytab(ccname); gssd_verbose_out("gssd_acquire_cred: using keytab entry for " "%s, kerberos ret=%d\n", ccname, (int)kret); gss_release_buffer(&minstat, &namebuf); if (kret != 0) { result->minor_status = kret; result->major_status = GSS_S_FAILURE; return (TRUE); } } else #endif /* !WITHOUT_KERBEROS */ if (ccfile_dirlist[0] != '\0' && argp->desired_name == 0) { /* * For the "-s" case and no name provided as an * argument, search the directory list for an appropriate * credential cache file. If the search fails, return failure. */ gotone = 0; cp = ccfile_dirlist; do { cp2 = strchr(cp, ':'); if (cp2 != NULL) *cp2 = '\0'; gotone = find_ccache_file(cp, argp->uid, ccname); if (gotone != 0) break; if (cp2 != NULL) *cp2++ = ':'; cp = cp2; } while (cp != NULL && *cp != '\0'); if (gotone == 0) { result->major_status = GSS_S_CREDENTIALS_EXPIRED; gssd_verbose_out("gssd_acquire_cred: no cred cache" " file found\n"); return (TRUE); } setenv("KRB5CCNAME", ccname, TRUE); } else { /* * If there wasn't a "-s" option or the name has * been provided as an argument, do it the old way. * When a name is provided, it will normally exist in the * default keytab file and the uid will be root. */ if (argp->desired_name != 0 && argp->uid != 0) { if (debug_level == 0) syslog(LOG_ERR, "gss_acquire_cred:" " principal_name for non-root"); else fprintf(stderr, "gss_acquire_cred:" " principal_name for non-root\n"); } snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d", (int) argp->uid); setenv("KRB5CCNAME", ccname, TRUE); } result->major_status = gss_acquire_cred(&result->minor_status, desired_name, argp->time_req, argp->desired_mechs, argp->cred_usage, &cred, &result->actual_mechs, &result->time_rec); gssd_verbose_out("gssd_acquire_cred: done major=0x%x minor=%d\n", (unsigned int)result->major_status, (int)result->minor_status); if (result->major_status == GSS_S_COMPLETE) result->output_cred = gssd_make_resource(cred); else result->output_cred = 0; return (TRUE); } bool_t gssd_set_cred_option_1_svc(set_cred_option_args *argp, set_cred_option_res *result, struct svc_req *rqstp) { gss_cred_id_t cred = gssd_find_resource(argp->cred); memset(result, 0, sizeof(*result)); if (!cred) { result->major_status = GSS_S_CREDENTIALS_EXPIRED; gssd_verbose_out("gssd_set_cred: no credentials\n"); return (TRUE); } result->major_status = gss_set_cred_option(&result->minor_status, &cred, argp->option_name, &argp->option_value); gssd_verbose_out("gssd_set_cred: done major=0x%x minor=%d\n", (unsigned int)result->major_status, (int)result->minor_status); return (TRUE); } bool_t gssd_release_cred_1_svc(release_cred_args *argp, release_cred_res *result, struct svc_req *rqstp) { gss_cred_id_t cred = gssd_find_resource(argp->cred); if (cred) { result->major_status = gss_release_cred(&result->minor_status, &cred); gssd_delete_resource(argp->cred); } else { result->major_status = GSS_S_COMPLETE; result->minor_status = 0; } gssd_verbose_out("gssd_release_cred: done major=0x%x minor=%d\n", (unsigned int)result->major_status, (int)result->minor_status); return (TRUE); } bool_t gssd_display_status_1_svc(display_status_args *argp, display_status_res *result, struct svc_req *rqstp) { result->message_context = argp->message_context; result->major_status = gss_display_status(&result->minor_status, argp->status_value, argp->status_type, argp->mech_type, &result->message_context, &result->status_string); gssd_verbose_out("gssd_display_status: done major=0x%x minor=%d\n", (unsigned int)result->major_status, (int)result->minor_status); return (TRUE); } int gssd_1_freeresult(SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result) { /* * We don't use XDR to free the results - anything which was * allocated came from GSS-API. We use xdr_result to figure * out what to do. */ OM_uint32 junk; if (xdr_result == (xdrproc_t) xdr_init_sec_context_res) { init_sec_context_res *p = (init_sec_context_res *) result; gss_release_buffer(&junk, &p->output_token); } else if (xdr_result == (xdrproc_t) xdr_accept_sec_context_res) { accept_sec_context_res *p = (accept_sec_context_res *) result; gss_release_buffer(&junk, &p->output_token); } else if (xdr_result == (xdrproc_t) xdr_delete_sec_context_res) { delete_sec_context_res *p = (delete_sec_context_res *) result; gss_release_buffer(&junk, &p->output_token); } else if (xdr_result == (xdrproc_t) xdr_export_sec_context_res) { export_sec_context_res *p = (export_sec_context_res *) result; if (p->interprocess_token.length) memset(p->interprocess_token.value, 0, p->interprocess_token.length); gss_release_buffer(&junk, &p->interprocess_token); } else if (xdr_result == (xdrproc_t) xdr_export_name_res) { export_name_res *p = (export_name_res *) result; gss_release_buffer(&junk, &p->exported_name); } else if (xdr_result == (xdrproc_t) xdr_acquire_cred_res) { acquire_cred_res *p = (acquire_cred_res *) result; gss_release_oid_set(&junk, &p->actual_mechs); } else if (xdr_result == (xdrproc_t) xdr_pname_to_uid_res) { pname_to_uid_res *p = (pname_to_uid_res *) result; if (p->gidlist.gidlist_val) free(p->gidlist.gidlist_val); } else if (xdr_result == (xdrproc_t) xdr_display_status_res) { display_status_res *p = (display_status_res *) result; gss_release_buffer(&junk, &p->status_string); } return (TRUE); } /* * Search a directory for the most likely candidate to be used as the * credential cache for a uid. If successful, return 1 and fill the * file's path id into "rpath". Otherwise, return 0. */ static int find_ccache_file(const char *dirpath, uid_t uid, char *rpath) { DIR *dirp; struct dirent *dp; struct stat sb; time_t exptime, oexptime; int gotone, len, rating, orating; char namepath[PATH_MAX + 5 + 1]; char retpath[PATH_MAX + 5 + 1]; dirp = opendir(dirpath); if (dirp == NULL) return (0); gotone = 0; orating = 0; oexptime = 0; while ((dp = readdir(dirp)) != NULL) { len = snprintf(namepath, sizeof(namepath), "%s/%s", dirpath, dp->d_name); if (len < sizeof(namepath) && (hostbased_initiator_cred == 0 || strcmp(namepath, GSSD_CREDENTIAL_CACHE_FILE) != 0) && strstr(dp->d_name, ccfile_substring) != NULL && lstat(namepath, &sb) >= 0 && sb.st_uid == uid && S_ISREG(sb.st_mode)) { len = snprintf(namepath, sizeof(namepath), "FILE:%s/%s", dirpath, dp->d_name); if (len < sizeof(namepath) && is_a_valid_tgt_cache(namepath, uid, &rating, &exptime) != 0) { if (gotone == 0 || rating > orating || (rating == orating && exptime > oexptime)) { orating = rating; oexptime = exptime; strcpy(retpath, namepath); gotone = 1; } } } } closedir(dirp); if (gotone != 0) { strcpy(rpath, retpath); return (1); } return (0); } /* * Try to determine if the file is a valid tgt cache file. * Check that the file has a valid tgt for a principal. * If it does, return 1, otherwise return 0. * It also returns a "rating" and the expiry time for the TGT, when found. * This "rating" is higher based on heuristics that make it more * likely to be the correct credential cache file to use. It can * be used by the caller, along with expiry time, to select from * multiple credential cache files. */ static int is_a_valid_tgt_cache(const char *filepath, uid_t uid, int *retrating, time_t *retexptime) { #ifndef WITHOUT_KERBEROS krb5_context context; krb5_principal princ; krb5_ccache ccache; krb5_error_code retval; krb5_cc_cursor curse; krb5_creds krbcred; int gotone, orating, rating, ret; struct passwd *pw; char *cp, *cp2, *pname; time_t exptime; /* Find a likely name for the uid principal. */ pw = getpwuid(uid); /* * Do a bunch of krb5 library stuff to try and determine if * this file is a credentials cache with an appropriate TGT * in it. */ retval = krb5_init_context(&context); if (retval != 0) return (0); retval = krb5_cc_resolve(context, filepath, &ccache); if (retval != 0) { krb5_free_context(context); return (0); } ret = 0; orating = 0; exptime = 0; retval = krb5_cc_start_seq_get(context, ccache, &curse); if (retval == 0) { while ((retval = krb5_cc_next_cred(context, ccache, &curse, &krbcred)) == 0) { gotone = 0; rating = 0; retval = krb5_unparse_name(context, krbcred.server, &pname); if (retval == 0) { cp = strchr(pname, '/'); if (cp != NULL) { *cp++ = '\0'; if (strcmp(pname, "krbtgt") == 0 && krbcred.times.endtime > time(NULL) ) { gotone = 1; /* * Test to see if this is a * tgt for cross-realm auth. * Rate it higher, if it is not. */ cp2 = strchr(cp, '@'); if (cp2 != NULL) { *cp2++ = '\0'; if (strcmp(cp, cp2) == 0) rating++; } } } free(pname); } if (gotone != 0) { retval = krb5_unparse_name(context, krbcred.client, &pname); if (retval == 0) { cp = strchr(pname, '@'); if (cp != NULL) { *cp++ = '\0'; if (pw != NULL && strcmp(pname, pw->pw_name) == 0) rating++; if (strchr(pname, '/') == NULL) rating++; if (pref_realm[0] != '\0' && strcmp(cp, pref_realm) == 0) rating++; } } free(pname); if (rating > orating) { orating = rating; exptime = krbcred.times.endtime; } else if (rating == orating && krbcred.times.endtime > exptime) exptime = krbcred.times.endtime; ret = 1; } krb5_free_cred_contents(context, &krbcred); } krb5_cc_end_seq_get(context, ccache, &curse); } krb5_cc_close(context, ccache); krb5_free_context(context); if (ret != 0) { *retrating = orating; *retexptime = exptime; } return (ret); #else /* WITHOUT_KERBEROS */ return (0); #endif /* !WITHOUT_KERBEROS */ } #ifndef WITHOUT_KERBEROS /* * This function attempts to do essentially a "kinit -k" for the principal * name provided as the argument, so that there will be a TGT in the * credential cache. */ static krb5_error_code gssd_get_cc_from_keytab(const char *name) { krb5_error_code ret, opt_ret, princ_ret, cc_ret, kt_ret, cred_ret; krb5_context context; krb5_principal principal; krb5_keytab kt; krb5_creds cred; krb5_get_init_creds_opt *opt; krb5_deltat start_time = 0; krb5_ccache ccache; ret = krb5_init_context(&context); if (ret != 0) return (ret); opt_ret = cc_ret = kt_ret = cred_ret = 1; /* anything non-zero */ princ_ret = ret = krb5_parse_name(context, name, &principal); if (ret == 0) opt_ret = ret = krb5_get_init_creds_opt_alloc(context, &opt); if (ret == 0) cc_ret = ret = krb5_cc_default(context, &ccache); if (ret == 0) ret = krb5_cc_initialize(context, ccache, principal); if (ret == 0) { krb5_get_init_creds_opt_set_default_flags(context, "gssd", krb5_principal_get_realm(context, principal), opt); kt_ret = ret = krb5_kt_default(context, &kt); } if (ret == 0) cred_ret = ret = krb5_get_init_creds_keytab(context, &cred, principal, kt, start_time, NULL, opt); if (ret == 0) ret = krb5_cc_store_cred(context, ccache, &cred); if (kt_ret == 0) krb5_kt_close(context, kt); if (cc_ret == 0) krb5_cc_close(context, ccache); if (opt_ret == 0) krb5_get_init_creds_opt_free(context, opt); if (princ_ret == 0) krb5_free_principal(context, principal); if (cred_ret == 0) krb5_free_cred_contents(context, &cred); krb5_free_context(context); return (ret); } /* * Acquire a gss credential for a uid. */ static OM_uint32 gssd_get_user_cred(OM_uint32 *min_statp, uid_t uid, gss_cred_id_t *credp) { gss_buffer_desc principal_desc; gss_name_t name; OM_uint32 maj_stat, min_stat; gss_OID_set mechlist; struct passwd *pw; pw = getpwuid(uid); if (pw == NULL) { *min_statp = 0; return (GSS_S_FAILURE); } /* * The mechanism must be set to KerberosV for acquisition * of credentials to work reliably. */ maj_stat = gss_create_empty_oid_set(min_statp, &mechlist); if (maj_stat != GSS_S_COMPLETE) return (maj_stat); maj_stat = gss_add_oid_set_member(min_statp, GSS_KRB5_MECH_OID_X, &mechlist); if (maj_stat != GSS_S_COMPLETE) { gss_release_oid_set(&min_stat, &mechlist); return (maj_stat); } principal_desc.value = (void *)pw->pw_name; principal_desc.length = strlen(pw->pw_name); maj_stat = gss_import_name(min_statp, &principal_desc, GSS_C_NT_USER_NAME, &name); if (maj_stat != GSS_S_COMPLETE) { gss_release_oid_set(&min_stat, &mechlist); return (maj_stat); } /* Acquire the credentials. */ maj_stat = gss_acquire_cred(min_statp, name, 0, mechlist, GSS_C_INITIATE, credp, NULL, NULL); gss_release_name(&min_stat, &name); gss_release_oid_set(&min_stat, &mechlist); return (maj_stat); } #endif /* !WITHOUT_KERBEROS */ void gssd_terminate(int sig __unused) { #ifndef WITHOUT_KERBEROS if (hostbased_initiator_cred != 0) unlink(GSSD_CREDENTIAL_CACHE_FILE); #endif exit(0); } Index: projects/building-blocks/usr.sbin/pw/tests/pw_usernext.sh =================================================================== --- projects/building-blocks/usr.sbin/pw/tests/pw_usernext.sh (revision 278776) +++ projects/building-blocks/usr.sbin/pw/tests/pw_usernext.sh (revision 278777) @@ -1,42 +1,42 @@ # $FreeBSD$ # Import helper functions . $(atf_get_srcdir)/helper_functions.shin # Test usernext after adding a random number of new users. atf_test_case usernext usernext_body() { populate_etc_skel var0=1 LIMIT=`jot -r 1 2 10` while [ "$var0" -lt "$LIMIT" ] do atf_check -s exit:0 ${PW} useradd test$var0 var0=`expr $var0 + 1` done atf_check -s exit:0 -o match:"100${LIMIT}:100${LIMIT}" \ ${PW} usernext } # Test usernext when multiple users are added to the same group so # that group id doesn't increment at the same pace as new users. atf_test_case usernext_assigned_group usernext_assigned_group_body() { populate_etc_skel var0=1 LIMIT=`jot -r 1 2 10` while [ "$var0" -lt "$LIMIT" ] do atf_check -s exit:0 ${PW} useradd -n test$var0 -g 0 var0=`expr $var0 + 1` done - atf_check -s exit:0 -o match:"100${LIMIT}:1001}" \ + atf_check -s exit:0 -o match:"100${LIMIT}:1001" \ ${PW} usernext } atf_init_test_cases() { atf_add_test_case usernext atf_add_test_case usernext_assigned_group } Index: projects/building-blocks =================================================================== --- projects/building-blocks (revision 278776) +++ projects/building-blocks (revision 278777) Property changes on: projects/building-blocks ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r278637-278776