move package/linux into target/linux, use wbx' new kernel code. support building images with more than one kernel, split kernel module parts off of packages that use their own kernel modules (fuse, shfs, openswan). some cleanup in the image building process in target/. image builder is disabled for now, needs some fixing.

git-svn-id: svn://svn.openwrt.org/openwrt/trunk/openwrt@1085 3c298f89-4303-0410-b956-a3cf2f4a3e73
This commit is contained in:
Felix Fietkau
2005-05-28 09:17:29 +00:00
parent fdd5681c06
commit 1c4a2a55f7
234 changed files with 1012 additions and 121969 deletions

View File

@@ -0,0 +1,4 @@
source "target/linux/package/wlcompat/Config.in"
source "target/linux/package/fuse/Config.in"
source "target/linux/package/shfs/Config.in"
source "target/linux/package/openswan/Config.in"

View File

@@ -0,0 +1,36 @@
# Main makefile for the packages
include $(TOPDIR)/rules.mk
package-$(BR2_PACKAGE_KMOD_FUSE) += fuse
package-$(BR2_PACKAGE_KMOD_SHFS) += shfs
package-$(BR2_PACKAGE_KMOD_OPENSWAN) += openswan
all: compile install
clean: $(patsubst %,%-clean,$(package-) $(package-y) $(package-m))
compile: $(patsubst %,%-compile,$(package-y) $(package-m))
install: $(patsubst %,%-install,$(package-y))
%-prepare:
$(MAKE) -C $(patsubst %-prepare,%,$@) \
BUILD_DIR="$(BUILD_DIR)" \
KERNEL_DIR="$(KERNEL_DIR)" \
LINUX_VERSION="$(LINUX_VERSION)" \
prepare
%-compile: %-prepare
$(MAKE) -C $(patsubst %-compile,%,$@) \
BUILD_DIR="$(BUILD_DIR)" \
KERNEL_DIR="$(KERNEL_DIR)" \
LINUX_VERSION="$(LINUX_VERSION)" \
compile
%-install: %-compile
$(MAKE) -C $(patsubst %-install,%,$@) \
BUILD_DIR="$(BUILD_DIR)" \
KERNEL_DIR="$(KERNEL_DIR)" \
LINUX_VERSION="$(LINUX_VERSION)" \
install
%-clean:
@$(MAKE) -C $(patsubst %-clean,%,$@) clean

View File

@@ -0,0 +1,23 @@
config BR2_PACKAGE_KMOD_FUSE
prompt "kmod-fuse - FUSE kernel module"
tristate
default m if CONFIG_DEVEL
select BR2_PACKAGE_FUSE
help
With FUSE it is possible to implement a fully functional
filesystem in a userspace program.
Features include:
* Simple library API
* Simple installation (no need to patch or recompile the kernel)
* Secure implementation
* Userspace - kernel interface is very efficient
* Usable by non privileged users
* Runs on Linux kernels 2.4.X and 2.6.X
* Has proven very stable over time
http://fuse.sourceforge.net/
This package contains the fuse.o kernel module.

View File

@@ -0,0 +1,79 @@
# $Id$
include $(TOPDIR)/rules.mk
PKG_NAME:=fuse
PKG_VERSION:=2.2.1
PKG_RELEASE:=1
PKG_MD5SUM:=250d89b9c7b6ecf531df60c67f75737d
PKG_SOURCE_URL:=@SF/$(PKG_NAME)
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_CAT:=zcat
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
PKG_INSTALL_DIR:=$(PKG_BUILD_DIR)/ipkg-install
ifeq ($(KERNEL_DIR),)
KERNEL_DIR:=$(LINUX_DIR)
endif
include $(TOPDIR)/package/rules.mk
$(eval $(call PKG_template,KMOD_FUSE,kmod-fuse,$(LINUX_VERSION)+$(PKG_VERSION)-$(PKG_RELEASE),$(ARCH)))
$(PKG_BUILD_DIR)/.configured:
(cd $(PKG_BUILD_DIR); \
rm -rf config.{cache,status} ; \
touch configure.in ; \
touch aclocal.m4 ; \
touch Makefile.in ; \
touch include/config.h.in ; \
touch configure ; \
$(TARGET_CONFIGURE_OPTS) \
CFLAGS="$(TARGET_CFLAGS)" \
./configure \
--target=$(GNU_TARGET_NAME) \
--host=$(GNU_TARGET_NAME) \
--build=$(GNU_HOST_NAME) \
--program-prefix="" \
--program-suffix="" \
--prefix=/usr \
--exec-prefix=/usr \
--bindir=/usr/bin \
--datadir=/usr/share \
--includedir=/usr/include \
--infodir=/usr/share/info \
--libdir=/usr/lib \
--libexecdir=/usr/lib \
--localstatedir=/var \
--mandir=/usr/share/man \
--sbindir=/usr/sbin \
--sysconfdir=/etc \
$(DISABLE_LARGEFILE) \
$(DISABLE_NLS) \
--enable-shared \
--enable-static \
--enable-kernel-module \
--enable-lib \
--enable-util \
--disable-example \
--disable-auto-modprobe \
--with-kernel=$(KERNEL_DIR) \
);
touch $@
$(PKG_BUILD_DIR)/.built:
mkdir -p $(PKG_INSTALL_DIR)
$(MAKE) -C $(PKG_BUILD_DIR) \
DESTDIR="$(PKG_INSTALL_DIR)" \
SUBDIRS="kernel" \
all install
touch $@
$(IPKG_KMOD_FUSE):
mkdir -p $(IDIR_KMOD_FUSE)/lib/modules/$(LINUX_VERSION)
cp -fpR $(PKG_INSTALL_DIR)/lib/modules/$(LINUX_VERSION)/kernel/fs/fuse/fuse.* \
$(IDIR_KMOD_FUSE)/lib/modules/$(LINUX_VERSION)/
$(IPKG_BUILD) $(IDIR_KMOD_FUSE) $(PACKAGE_DIR)

View File

@@ -0,0 +1,8 @@
Package: kmod-fuse
Priority: optional
Section: kernel
Version: [TBDL]
Architecture: [TBDL]
Maintainer: Nico <nthill@free.fr>
Source: http://nthill.free.fr/openwrt/sources/fuse2/
Description: Filesystem in Userspace (kernel module)

View File

@@ -0,0 +1,59 @@
--- fuse-2.2.1-orig/kernel/Makefile.in 2005-01-09 13:07:00.000000000 +0100
+++ fuse-2.2.1-1/kernel/Makefile.in 2005-04-06 14:48:59.000000000 +0200
@@ -31,11 +31,11 @@
install-y: all
$(mkdir_p) $(DESTDIR)$(fusemoduledir)
$(INSTALL) -m 644 $(fusemodule) $(DESTDIR)$(fusemoduledir)/$(fusemodule)
- -/sbin/depmod -a
+# -/sbin/depmod -a
uninstall-y:
rm -f $(DESTDIR)$(fusemoduledir)/$(fusemodule)
- -/sbin/depmod -a
+# -/sbin/depmod -a
clean:
-rm -f $(fusemodule) *.o .*.cmd *.mod.c *.ko *.s */*.o
@@ -54,23 +54,8 @@
ifeq ($(majver), 2.4)
-CC = gcc
-LD = ld
-CFLAGS = -O2 -Wall -Wstrict-prototypes -fno-strict-aliasing -pipe
-CPPFLAGS = -I@kernelsrc@/include -I. -D__KERNEL__ -DMODULE -D_LOOSE_KERNEL_NAMES -DFUSE_VERSION=\"$(VERSION)\" @KERNELCPPFLAGS@
-
fuse_objs = dev.o dir.o file.o inode.o compat/parser.o
-SUFFIXES = .c .o .s
-
-all-spec: fuse.o
-
-.c.o:
- $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
-
-fuse.o: $(fuse_objs)
- $(LD) -r -o fuse.o $(fuse_objs)
-
fuse_headers = fuse_i.h fuse_kernel.h
dev.o: $(fuse_headers)
@@ -78,6 +63,18 @@
file.o: $(fuse_headers)
inode.o: $(fuse_headers)
+EXTRA_CFLAGS += -DFUSE_VERSION=\"$(VERSION)\"
+
+O_TARGET := fuse.o
+
+obj-y := $(fuse_objs)
+obj-m := $(O_TARGET)
+
+-include $(TOPDIR)/Rules.make
+
+all-spec:
+ $(MAKE) -C @kernelsrc@ TOPDIR=@kernelsrc@ SUBDIRS=$(PWD) modules
+
else
EXTRA_CFLAGS += -DFUSE_VERSION=\"$(VERSION)\"

View File

@@ -0,0 +1,22 @@
--- fuse-2.2.1-orig/configure 2005-03-08 15:48:57.000000000 +0100
+++ fuse-2.2.1-1/configure 2005-04-06 14:39:18.000000000 +0200
@@ -9354,7 +9354,7 @@
fi
-CFLAGS="-Wall -W -g -O2"
+[ -z "$CFLAGS" ] && CFLAGS="-Wall -W -g -O2"
CPPFLAGS="$CPPFLAGS -D_FILE_OFFSET_BITS=64 -D_REENTRANT -DFUSE_USE_VERSION=22"
# Check whether --enable-kernel-module or --disable-kernel-module was given.
--- fuse-2.2.1-orig/configure.in 2005-03-08 15:39:23.000000000 +0100
+++ fuse-2.2.1-1/configure.in 2005-04-06 14:39:20.000000000 +0200
@@ -13,7 +13,7 @@
AC_SUBST(mkdir_p)
fi
-CFLAGS="-Wall -W -g -O2"
+[ -z "$CFLAGS" ] && CFLAGS="-Wall -W -g -O2"
CPPFLAGS="$CPPFLAGS -D_FILE_OFFSET_BITS=64 -D_REENTRANT -DFUSE_USE_VERSION=22"
AC_ARG_ENABLE(kernel-module,

View File

@@ -0,0 +1,9 @@
config BR2_PACKAGE_KMOD_OPENSWAN
tristate "kmod-openswan Openswan kernel module"
default m if CONFIG_DEVEL
help
Openswan is an implementation of IPsec for Linux.
http://www.openswan.org/
This package contains the Openswan IPSec module for the kernel

View File

@@ -0,0 +1,36 @@
# $Id$
include $(TOPDIR)/rules.mk
PKG_NAME:=openswan
PKG_VERSION:=2.3.1
PKG_RELEASE:=1
PKG_MD5SUM:=3dcf1cd7efcbe8db3148fc288d429db1
PKG_SOURCE_URL:=http://www.openswan.org/download
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
PKG_CAT:=zcat
include $(TOPDIR)/package/rules.mk
ifeq ($(KERNEL_DIR),)
KERNEL_DIR:=$(LINUX_DIR)
endif
$(eval $(call PKG_template,KMOD_OPENSWAN,kmod-openswan,$(LINUX_VERSION)+$(PKG_VERSION)-$(PKG_RELEASE),$(ARCH)))
FLAGS := $(TARGET_CFLAGS) -I$(PKG_BUILD_DIR)/linux/include -L$(STAGING_DIR)/usr/lib -I$(STAGING_DIR)/usr/include
$(PKG_BUILD_DIR)/.built:
$(MAKE) -C $(PKG_BUILD_DIR) \
$(TARGET_CONFIGURE_OPTS) \
KERNELSRC="$(KERNEL_DIR)" \
ARCH="mips" \
USERCOMPILE="$(FLAGS)" \
module
$(IPKG_KMOD_OPENSWAN):
mkdir -p $(IDIR_KMOD_OPENSWAN)/lib/modules/$(LINUX_VERSION)
cp $(PKG_BUILD_DIR)/modobj/ipsec.*o $(IDIR_KMOD_OPENSWAN)/lib/modules/$(LINUX_VERSION)/
$(IPKG_BUILD) $(IDIR_KMOD_OPENSWAN) $(PACKAGE_DIR)

View File

@@ -0,0 +1,6 @@
Package: kmod-openswan
Section: base
Priority: optional
Maintainer: Felix Fietkau <nbd@vd-s.ath.cx>
Source: buildroot internal
Description: Openswan IPSec kernel module

View File

@@ -0,0 +1,24 @@
config BR2_PACKAGE_KMOD_SHFS
prompt "kmod-shfs - Shell FileSystem kernel module (ShFS) kernel module"
tristate
default m if CONFIG_DEVEL
help
ShFS is a simple and easy to use Linux kernel module which
allows you to mount remote filesystems using a plain shell
(SSH) connection. When using ShFS, you can access all remote
files just like the local ones, only the access is governed
through the transport security of SSH.
ShFS supports some nice features:
* file cache for access speedup
* perl and shell code for the remote (server) side
* could preserve uid/gid (root connection)
* number of remote host platforms (Linux, Solaris, Cygwin, ...)
* Linux kernel 2.4.10+ and 2.6
* arbitrary command used for connection (instead of SSH)
* persistent connection (reconnect after SSH dies)
http://shfs.sourceforge.net/
This package contains the shfs.o kernel module.

View File

@@ -0,0 +1,47 @@
# $Id$
include $(TOPDIR)/rules.mk
PKG_NAME := shfs
PKG_VERSION := 0.35
PKG_RELEASE := 2
PKG_MD5SUM := 016f49d71bc32eee2b5d11fc1600cfbe
PKG_SOURCE_URL := @SF/shfs
PKG_SOURCE := $(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_CAT := zcat
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
PKG_INSTALL_DIR:=$(PKG_BUILD_DIR)/ipkg-install
ifeq ($(KERNEL_DIR),)
KERNEL_DIR:=$(LINUX_DIR)
endif
include $(TOPDIR)/package/rules.mk
$(eval $(call PKG_template,KMOD_SHFS,kmod-shfs,$(LINUX_VERSION)+$(PKG_VERSION)-$(PKG_RELEASE),$(ARCH)))
$(PKG_BUILD_DIR)/.configured:
touch $@
$(PKG_BUILD_DIR)/.built:
rm -rf $(PKG_INSTALL_DIR)
mkdir -p $(PKG_INSTALL_DIR)
$(MAKE) -C $(PKG_BUILD_DIR) \
OFLAGS="$(TARGET_CFLAGS)" \
CC="$(TARGET_CC)" \
LINKER="$(TARGET_CC)" \
KERNEL="$(LINUX_VERSION)" \
KERNEL_SOURCES="$(KERNEL_DIR)" \
ROOT="$(PKG_INSTALL_DIR)" \
module module-install
touch $@
$(IPKG_KMOD_SHFS):
install -m0755 -d $(IDIR_KMOD_SHFS)/lib/modules/$(LINUX_VERSION)
cp -fpR $(PKG_INSTALL_DIR)/lib/modules/$(LINUX_VERSION)/kernel/fs/shfs/shfs.o \
$(IDIR_KMOD_SHFS)/lib/modules/$(LINUX_VERSION)/
$(RSTRIP_KMOD) $(IDIR_KMOD_SHFS)
$(IPKG_BUILD) $(IDIR_KMOD_SHFS) $(PACKAGE_DIR)

View File

@@ -0,0 +1,8 @@
Package: kmod-shfs
Priority: optional
Section: net
Version: [TBDL]
Architecture: [TBDL]
Maintainer: Nico <nthill@free.fr>
Source: http://nthill.free.fr/openwrt/sources/shfs/
Description: SHell FileSystem Linux kernel module

View File

@@ -0,0 +1,103 @@
--- shfs-0.35-orig/shfs/Linux-2.4/Makefile 2004-06-01 15:16:19.000000000 +0200
+++ shfs-0.35-2/shfs/Linux-2.4/Makefile 2005-04-09 02:34:35.000000000 +0200
@@ -1,3 +1,17 @@
+#
+# the original Makefile was trashed and replaced by this one
+# The main reason is that loadable modules should be built with
+# the same compile flags the kernel was built with, so we'd better
+# let the kernel tree build the module for us, like that :
+#
+# make -C $(KERNEL_DIR) SUBDIRS="$(shell pwd)" modules
+# make -C $(KERNEL_DIR) SUBDIRS="$(shell pwd)" modules_install
+#
+#
+# $(TOPDIR)/lib/string.o is needed at link time because the memchr function
+# is not exported on mips (insmod: unresolved symbol memchr)
+#
+
ifndef KERNEL
KERNEL=$(shell uname -r)
endif
@@ -10,67 +24,30 @@
KERNEL_SOURCES=${MODULESDIR}/build
endif
-ifeq (${MODVERSIONS},detect)
- ifeq ($(shell test -e ${KERNEL_SOURCES}/include/linux/modversions.h; echo $$?),0)
- MODVERSIONS=yes
- endif
-endif
-
-ifeq (${MODVERSIONS},yes)
-MVER=-DMODVERSIONS -DEXPORT_SYMTAB
-endif
-
-LINVER=linux-${KERNEL}
-
-ALL_TARGETS := shfs.o
-
-SEARCHDIRS := -I- -I. -I${KERNEL_SOURCES}/include #-I/usr/src/linux/include/
+all: all-y
-CC := gcc
-CFLAGS = -O2 -fomit-frame-pointer -fno-strict-aliasing -pipe -Wall ${SEARCHDIRS} -DMODULE ${MVER} -D__KERNEL__ -DLINUX
-LINKER := ld
-LDFLAGS = -r
-LOADLIBES :=
+O_TARGET := shfs.o
-all: ${ALL_TARGETS}
+shfs-objs := dcache.o dir.o fcache.o file.o inode.o ioctl.o proc.o shell.o symlink.o
-%.o: %.c $(wildcard *.h)
- ${CC} ${CFLAGS} -c $< -o $@
+obj-y := $(shfs-objs)
+obj-m := $(O_TARGET)
-shfs.o: dcache.o dir.o fcache.o file.o inode.o ioctl.o proc.o shell.o symlink.o
- ${LINKER} ${LDFLAGS} -o $@ ${filter-out %.a %.so, $^} ${LOADLIBES}
-
-tidy:
- ${RM} core dcache.o dir.o fcache.o file.o inode.o ioctl.o proc.o shell.o symlink.o
-
-clean: tidy patch-clean
- ${RM} shfs.o
+-include $(TOPDIR)/Rules.make
+all-y:
+ make -C ${KERNEL_SOURCES} TOPDIR="${KERNEL_SOURCES}" SUBDIRS="$(shell pwd)" modules
+
install: shfs.o
rm -f ${MODULESDIR}/kernel/fs/shfs/shfs.o
install -m644 -b -D shfs.o ${MODULESDIR}/kernel/fs/shfs/shfs.o
- if [ -x /sbin/depmod -a "${ROOT}" = "/" ]; then /sbin/depmod -aq; fi
uninstall:
rm -rf ${MODULESDIR}/kernel/fs/shfs
- if [ -x /sbin/depmod -a "${ROOT}" = "/" ]; then /sbin/depmod -aq; fi
-patch:
- rm -rf ${LINVER} ${LINVER}.orig; mkdir ${LINVER};
- for i in Documentation fs/shfs include/linux; do \
- mkdir -p ${LINVER}/$$i; \
- done
- cp ${KERNEL_SOURCES}/Documentation/Configure.help ${LINVER}/Documentation
- cp ${KERNEL_SOURCES}/fs/{Makefile,Config.in} ${LINVER}/fs
- cp -r ${LINVER} ${LINVER}.orig
- cp ../../Changelog *.c shfs_debug.h proc.h ${LINVER}/fs/shfs/
- cp shfs.h shfs_fs* ${LINVER}/include/linux/
- (cd ${LINVER}; patch -p1 <../kernel-config.diff)
- find . -type f -name "*.orig" -print | xargs rm -f
- diff -urN ${LINVER}.orig ${LINVER} >${LINVER}.diff; true
-
-patch-clean:
- rm -rf ${LINVER} ${LINVER}.orig;
- rm -f ${LINVER}.diff
-
-.PHONY : all tidy clean install uninstall patch patch-clean
+clean:
+ rm -f core *.o *.a *.s
+
+shfs.o: $(shfs-objs)
+ $(LD) -r -o $@ $(shfs-objs) $(TOPDIR)/lib/string.o
+

View File

@@ -0,0 +1,7 @@
config BR2_PACKAGE_KMOD_WLCOMPAT
tristate "kmod-wlcompat"
default y
depends BR2_PACKAGE_KMOD_BRCM_WL
help
A wrapper module, that provides Wireless Extension support for the
proprietary Broadcom wl module.

View File

@@ -0,0 +1,34 @@
# $Id$
include $(TOPDIR)/rules.mk
PKG_NAME := kmod-wlcompat
PKG_RELEASE := 1
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
include $(TOPDIR)/package/rules.mk
ifeq ($(patsubst 2.4%,2.4,$(LINUX_VERSION)),2.4)
$(eval $(call PKG_template,KMOD_WLCOMPAT,$(PKG_NAME),$(KERNEL_VERSION)-$(PKG_RELEASE),$(ARCH)))
endif
WLCOMPAT_FLAGS:=$(TARGET_CLFAGS) -D__KERNEL__ -fno-strict-aliasing -fno-common -fomit-frame-pointer -G 0 \
-mno-abicalls -fno-pic -finline-limit=100000 -mabi=32 -march=mips32 -Wa,-32 -Wa,-march=mips32 \
-Wa,-mips32 -Wa,--trap -DMODULE -mlong-calls -fno-common -I. -I linux-2.4 -funsigned-char -nostdinc \
-iwithprefix include -I$(LINUX_DIR)/include -I$(LINUX_DIR)/include/asm/gcc -I./include -c
$(PKG_BUILD_DIR)/.built: $(PKG_BUILD_DIR)/wlcompat.o $(PKG_BUILD_DIR)/wlcompat-debug.o
touch $@
$(PKG_BUILD_DIR)/wlcompat.o:
$(TARGET_CC) $(WLCOMPAT_FLAGS) -o $@ wlcompat.c
$(PKG_BUILD_DIR)/wlcompat-debug.o:
$(TARGET_CC) -DDEBUG $(WLCOMPAT_FLAGS) -o $@ wlcompat.c
$(IPKG_KMOD_WLCOMPAT): $(PKG_BUILD_DIR)/wlcompat.o $(PKG_BUILD_DIR)/wlcompat-debug.o
mkdir -p $(IDIR_WLCOMPAT)/etc/modules.d
echo "wlcompat" > $(IDIR_WLCOMPAT)/etc/modules.d/10-wlcompat
mkdir -p $(IDIR_WLCOMPAT)/lib/modules/$(LINUX_VERSION)
cp $^ $(IDIR_WLCOMPAT)/lib/modules/$(LINUX_VERSION)/
$(IPKG_BUILD) $(IDIR_WLCOMPAT) $(PACKAGE_DIR)

View File

@@ -0,0 +1,7 @@
Package: kmod-wlcompat
Priority: optional
Section: sys
Maintainer: Felix Fietkau <nbd@vd-s.ath.cx>
Source: buildroot internal
Depends: kmod-brcm-wl
Description: Compatibility module for using the Wireless Extension with broadcom's wl

View File

@@ -0,0 +1,824 @@
/*
* wlcompat.c
*
* Copyright (C) 2005 Mike Baker,
* Felix Fietkau <nbd@vd-s.ath.cx>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* $Id$
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/if_arp.h>
#include <asm/uaccess.h>
#include <linux/wireless.h>
#include <net/iw_handler.h>
#include <wlioctl.h>
#include <wlcompat.h>
static struct net_device *dev;
char buf[WLC_IOCTL_MAXLEN];
/* The frequency of each channel in MHz */
const long channel_frequency[] = {
2412, 2417, 2422, 2427, 2432, 2437, 2442,
2447, 2452, 2457, 2462, 2467, 2472, 2484
};
#define NUM_CHANNELS ( sizeof(channel_frequency) / sizeof(channel_frequency[0]) )
static int wlcompat_private_ioctl(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra);
static int wl_ioctl(struct net_device *dev, int cmd, void *buf, int len)
{
mm_segment_t old_fs = get_fs();
struct ifreq ifr;
int ret;
wl_ioctl_t ioc;
ioc.cmd = cmd;
ioc.buf = buf;
ioc.len = len;
strncpy(ifr.ifr_name, dev->name, IFNAMSIZ);
ifr.ifr_data = (caddr_t) &ioc;
set_fs(KERNEL_DS);
ret = dev->do_ioctl(dev,&ifr,SIOCDEVPRIVATE);
set_fs (old_fs);
return ret;
}
static int wl_set_val(struct net_device *dev, char *var, void *val, int len)
{
char buf[128];
int buf_len;
/* check for overflow */
if ((buf_len = strlen(var)) + 1 + len > sizeof(buf))
return -1;
strcpy(buf, var);
buf_len += 1;
/* append int value onto the end of the name string */
memcpy(&buf[buf_len], val, len);
buf_len += len;
return wl_ioctl(dev, WLC_SET_VAR, buf, buf_len);
}
static int wl_get_val(struct net_device *dev, char *var, void *val, int len)
{
char buf[128];
int ret;
/* check for overflow */
if (strlen(var) + 1 > sizeof(buf) || len > sizeof(buf))
return -1;
strcpy(buf, var);
if ((ret = wl_ioctl(dev, WLC_GET_VAR, buf, sizeof(buf))))
return ret;
memcpy(val, buf, len);
return 0;
}
int read_shmem(struct net_device *dev, int offset)
{
if (wl_ioctl(dev, WLC_GET_SHMEM, &offset, sizeof(offset)) < 0)
return -EINVAL;
return offset;
}
static int wlcompat_ioctl_getiwrange(struct net_device *dev,
char *extra)
{
int i, k;
struct iw_range *range;
range = (struct iw_range *) extra;
range->we_version_compiled = WIRELESS_EXT;
range->we_version_source = WIRELESS_EXT;
range->min_nwid = range->max_nwid = 0;
range->num_channels = NUM_CHANNELS;
k = 0;
for (i = 0; i < NUM_CHANNELS; i++) {
range->freq[k].i = i + 1;
range->freq[k].m = channel_frequency[i] * 100000;
range->freq[k].e = 1;
k++;
if (k >= IW_MAX_FREQUENCIES)
break;
}
range->num_frequency = k;
range->sensitivity = 3;
/* nbd: don't know what this means, but other drivers set it this way */
range->pmp_flags = IW_POWER_PERIOD;
range->pmt_flags = IW_POWER_TIMEOUT;
range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_UNICAST_R;
range->min_pmp = 0;
range->max_pmp = 65535000;
range->min_pmt = 0;
range->max_pmt = 65535 * 1000;
range->min_rts = 0;
if (wl_ioctl(dev, WLC_GET_RTS, &range->max_rts, sizeof(int)) < 0)
range->max_rts = 2347;
range->min_frag = 256;
if (wl_ioctl(dev, WLC_GET_FRAG, &range->max_frag, sizeof(int)) < 0)
range->max_frag = 2346;
range->txpower_capa = IW_TXPOW_MWATT;
return 0;
}
static int wlcompat_set_scan(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
int ap = 0, oldap = 0;
wl_scan_params_t params;
memset(&params, 0, sizeof(params));
/* use defaults (same parameters as wl scan) */
memset(&params.bssid, 0xff, sizeof(params.bssid));
params.bss_type = DOT11_BSSTYPE_ANY;
params.scan_type = -1;
params.nprobes = -1;
params.active_time = -1;
params.passive_time = -1;
params.home_time = -1;
/* can only scan in STA mode */
wl_ioctl(dev, WLC_GET_AP, &oldap, sizeof(oldap));
if (oldap > 0)
wl_ioctl(dev, WLC_SET_AP, &ap, sizeof(ap));
if (wl_ioctl(dev, WLC_SCAN, &params, 64) < 0)
return -EINVAL;
if (oldap > 0)
wl_ioctl(dev, WLC_SET_AP, &oldap, sizeof(oldap));
return 0;
}
static int wlcompat_get_scan(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
wl_scan_results_t *results = (wl_scan_results_t *) buf;
wl_bss_info_t *bss_info;
char *info_ptr;
char *current_ev = extra;
char *current_val;
char *end_buf = extra + IW_SCAN_MAX_DATA;
struct iw_event iwe;
int i, j;
if (wl_ioctl(dev, WLC_SCAN_RESULTS, buf, WLC_IOCTL_MAXLEN) < 0)
return -EAGAIN;
bss_info = &(results->bss_info[0]);
info_ptr = (char *) bss_info;
for (i = 0; i < results->count; i++) {
/* send the cell address (must be sent first) */
iwe.cmd = SIOCGIWAP;
iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
memcpy(&iwe.u.ap_addr.sa_data, &bss_info->BSSID, sizeof(bss_info->BSSID));
current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_ADDR_LEN);
/* send the ESSID */
iwe.cmd = SIOCGIWESSID;
iwe.u.data.length = bss_info->SSID_len;
if (iwe.u.data.length > IW_ESSID_MAX_SIZE)
iwe.u.data.length = IW_ESSID_MAX_SIZE;
iwe.u.data.flags = 1;
current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, bss_info->SSID);
/* send frequency/channel info */
iwe.cmd = SIOCGIWFREQ;
iwe.u.freq.e = 0;
iwe.u.freq.m = bss_info->channel;
current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_FREQ_LEN);
/* add quality statistics */
iwe.cmd = IWEVQUAL;
iwe.u.qual.level = bss_info->RSSI;
iwe.u.qual.noise = bss_info->phy_noise;
iwe.u.qual.qual = 0;
current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_QUAL_LEN);
/* send rate information */
iwe.cmd = SIOCGIWRATE;
current_val = current_ev + IW_EV_LCP_LEN;
iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
for(j = 0 ; j < bss_info->rateset.count ; j++) {
iwe.u.bitrate.value = ((bss_info->rateset.rates[j] & 0x7f) * 500000);
current_val = iwe_stream_add_value(current_ev, current_val, end_buf, &iwe, IW_EV_PARAM_LEN);
}
if((current_val - current_ev) > IW_EV_LCP_LEN)
current_ev = current_val;
info_ptr += sizeof(wl_bss_info_t);
if (bss_info->ie_length % 4)
info_ptr += bss_info->ie_length + 4 - (bss_info->ie_length % 4);
else
info_ptr += bss_info->ie_length;
bss_info = (wl_bss_info_t *) info_ptr;
}
wrqu->data.length = (current_ev - extra);
wrqu->data.flags = 0;
return 0;
}
static int wlcompat_ioctl(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
switch (info->cmd) {
case SIOCGIWNAME:
strcpy(wrqu->name, "IEEE 802.11-DS");
break;
case SIOCGIWFREQ:
{
channel_info_t ci;
if (wl_ioctl(dev,WLC_GET_CHANNEL, &ci, sizeof(ci)) < 0)
return -EINVAL;
wrqu->freq.m = ci.target_channel;
wrqu->freq.e = 0;
break;
}
case SIOCSIWFREQ:
{
if (wrqu->freq.e == 1) {
int channel = 0;
int f = wrqu->freq.m / 100000;
while ((channel < NUM_CHANNELS + 1) && (f != channel_frequency[channel]))
channel++;
if (channel == NUM_CHANNELS) // channel not found
return -EINVAL;
wrqu->freq.e = 0;
wrqu->freq.m = channel + 1;
}
if ((wrqu->freq.e == 0) && (wrqu->freq.m < 1000)) {
if (wl_ioctl(dev, WLC_SET_CHANNEL, &wrqu->freq.m, sizeof(int)) < 0)
return -EINVAL;
} else {
return -EINVAL;
}
break;
}
case SIOCSIWAP:
{
int ap = 0;
if (wrqu->ap_addr.sa_family != ARPHRD_ETHER)
return -EINVAL;
if (wl_ioctl(dev, WLC_GET_AP, &ap, sizeof(ap)) < 0)
return -EINVAL;
if (wl_ioctl(dev, (ap ? WLC_SET_BSSID : WLC_REASSOC), wrqu->ap_addr.sa_data, 6) < 0)
return -EINVAL;
break;
}
case SIOCGIWAP:
{
wrqu->ap_addr.sa_family = ARPHRD_ETHER;
if (wl_ioctl(dev,WLC_GET_BSSID,wrqu->ap_addr.sa_data,6) < 0)
return -EINVAL;
break;
}
case SIOCGIWESSID:
{
wlc_ssid_t ssid;
if (wl_ioctl(dev,WLC_GET_SSID, &ssid, sizeof(wlc_ssid_t)) < 0)
return -EINVAL;
wrqu->essid.flags = wrqu->data.flags = 1;
wrqu->essid.length = wrqu->data.length = ssid.SSID_len + 1;
memcpy(extra,ssid.SSID,ssid.SSID_len + 1);
break;
}
case SIOCSIWESSID:
{
wlc_ssid_t ssid;
memset(&ssid, 0, sizeof(ssid));
ssid.SSID_len = strlen(extra);
if (ssid.SSID_len > WLC_ESSID_MAX_SIZE)
ssid.SSID_len = WLC_ESSID_MAX_SIZE;
memcpy(ssid.SSID, extra, ssid.SSID_len);
if (wl_ioctl(dev, WLC_SET_SSID, &ssid, sizeof(ssid)) < 0)
return -EINVAL;
break;
}
case SIOCGIWRTS:
{
if (wl_ioctl(dev,WLC_GET_RTS,&(wrqu->rts.value),sizeof(int)) < 0)
return -EINVAL;
break;
}
case SIOCSIWRTS:
{
if (wl_ioctl(dev,WLC_SET_RTS,&(wrqu->rts.value),sizeof(int)) < 0)
return -EINVAL;
break;
}
case SIOCGIWFRAG:
{
if (wl_ioctl(dev,WLC_GET_FRAG,&(wrqu->frag.value),sizeof(int)) < 0)
return -EINVAL;
break;
}
case SIOCSIWFRAG:
{
if (wl_ioctl(dev,WLC_SET_FRAG,&(wrqu->frag.value),sizeof(int)) < 0)
return -EINVAL;
break;
}
case SIOCGIWTXPOW:
{
if (wl_get_val(dev, "qtxpower", &(wrqu->txpower.value), sizeof(int)) < 0)
return -EINVAL;
wrqu->txpower.value &= ~WL_TXPWR_OVERRIDE;
wrqu->txpower.fixed = 0;
wrqu->txpower.disabled = 0;
wrqu->txpower.flags = IW_TXPOW_MWATT;
break;
}
case SIOCSIWTXPOW:
{
int override;
if (wl_get_val(dev, "qtxpower", &override, sizeof(int)) < 0)
return -EINVAL;
wrqu->txpower.value |= override & WL_TXPWR_OVERRIDE;
if (wrqu->txpower.flags != IW_TXPOW_MWATT)
return -EINVAL;
if (wl_set_val(dev, "qtxpower", &wrqu->txpower.value, sizeof(int)) < 0)
return -EINVAL;
}
case SIOCGIWENCODE:
{
int val;
if (wl_ioctl(dev, WLC_GET_WEP, &val, sizeof(val)) < 0)
return -EINVAL;
if (val > 0) {
int key;
for (key = val = 0; (key < 4) && (val == 0); key++) {
val = key;
if (wl_ioctl(dev, WLC_GET_KEY_PRIMARY, &val, sizeof(val)) < 0)
return -EINVAL;
}
wrqu->data.flags = IW_ENCODE_ENABLED;
if (key-- > 0) {
int magic_offset;
int16 buffer[8];
magic_offset = read_shmem(dev, 0x56) * 2;
wrqu->data.flags |= key + 1;
wrqu->data.length = 16;
for (val = 0; val < 8; val++) {
buffer[val] = read_shmem(dev, magic_offset + (key * 16) + val * 2);
}
memset(extra, 0, 16);
memcpy(extra, buffer, 16);
} else {
wrqu->data.flags |= IW_ENCODE_NOKEY;
}
} else {
wrqu->data.flags = IW_ENCODE_DISABLED;
}
break;
}
case SIOCGIWRANGE:
{
return wlcompat_ioctl_getiwrange(dev, extra);
break;
}
case SIOCSIWMODE:
{
int ap = -1, infra = -1, passive = 0, wet = 0;
switch (wrqu->mode) {
case IW_MODE_MONITOR:
passive = 1;
break;
case IW_MODE_ADHOC:
infra = 0;
ap = 0;
break;
case IW_MODE_MASTER:
infra = 1;
ap = 1;
break;
case IW_MODE_INFRA:
infra = 1;
ap = 0;
break;
case IW_MODE_REPEAT:
infra = 1;
ap = 0;
wet = 1;
break;
default:
return -EINVAL;
}
if (wl_ioctl(dev, WLC_SET_PASSIVE, &passive, sizeof(passive)) < 0)
return -EINVAL;
if (wl_ioctl(dev, WLC_SET_MONITOR, &passive, sizeof(passive)) < 0)
return -EINVAL;
if (wl_ioctl(dev, WLC_SET_WET, &wet, sizeof(wet)) < 0)
return -EINVAL;
if (ap >= 0)
if (wl_ioctl(dev, WLC_SET_AP, &ap, sizeof(ap)) < 0)
return -EINVAL;
if (infra >= 0)
if (wl_ioctl(dev, WLC_SET_INFRA, &infra, sizeof(infra)) < 0)
return -EINVAL;
break;
}
case SIOCGIWMODE:
{
int ap, infra, wet, passive;
if (wl_ioctl(dev, WLC_GET_AP, &ap, sizeof(ap)) < 0)
return -EINVAL;
if (wl_ioctl(dev, WLC_GET_INFRA, &infra, sizeof(infra)) < 0)
return -EINVAL;
if (wl_ioctl(dev, WLC_GET_PASSIVE, &passive, sizeof(passive)) < 0)
return -EINVAL;
if (wl_ioctl(dev, WLC_GET_WET, &wet, sizeof(wet)) < 0)
return -EINVAL;
if (passive) {
wrqu->mode = IW_MODE_MONITOR;
} else if (!infra) {
wrqu->mode = IW_MODE_ADHOC;
} else {
if (ap) {
wrqu->mode = IW_MODE_MASTER;
} else {
if (wet) {
wrqu->mode = IW_MODE_REPEAT;
} else {
wrqu->mode = IW_MODE_INFRA;
}
}
}
break;
}
default:
{
if (info->cmd >= SIOCIWFIRSTPRIV)
return wlcompat_private_ioctl(dev, info, wrqu, extra);
return -EINVAL;
}
}
return 0;
}
static const iw_handler wlcompat_handler[] = {
NULL, /* SIOCSIWCOMMIT */
wlcompat_ioctl, /* SIOCGIWNAME */
NULL, /* SIOCSIWNWID */
NULL, /* SIOCGIWNWID */
wlcompat_ioctl, /* SIOCSIWFREQ */
wlcompat_ioctl, /* SIOCGIWFREQ */
wlcompat_ioctl, /* SIOCSIWMODE */
wlcompat_ioctl, /* SIOCGIWMODE */
NULL, /* SIOCSIWSENS */
NULL, /* SIOCGIWSENS */
NULL, /* SIOCSIWRANGE, unused */
wlcompat_ioctl, /* SIOCGIWRANGE */
NULL, /* SIOCSIWPRIV */
NULL, /* SIOCGIWPRIV */
NULL, /* SIOCSIWSTATS */
NULL, /* SIOCGIWSTATS */
iw_handler_set_spy, /* SIOCSIWSPY */
iw_handler_get_spy, /* SIOCGIWSPY */
iw_handler_set_thrspy, /* SIOCSIWTHRSPY */
iw_handler_get_thrspy, /* SIOCGIWTHRSPY */
wlcompat_ioctl, /* SIOCSIWAP */
wlcompat_ioctl, /* SIOCGIWAP */
NULL, /* -- hole -- */
NULL, /* SIOCGIWAPLIST */
wlcompat_set_scan, /* SIOCSIWSCAN */
wlcompat_get_scan, /* SIOCGIWSCAN */
wlcompat_ioctl, /* SIOCSIWESSID */
wlcompat_ioctl, /* SIOCGIWESSID */
NULL, /* SIOCSIWNICKN */
NULL, /* SIOCGIWNICKN */
NULL, /* -- hole -- */
NULL, /* -- hole -- */
NULL, /* SIOCSIWRATE */
NULL, /* SIOCGIWRATE */
wlcompat_ioctl, /* SIOCSIWRTS */
wlcompat_ioctl, /* SIOCGIWRTS */
wlcompat_ioctl, /* SIOCSIWFRAG */
wlcompat_ioctl, /* SIOCGIWFRAG */
wlcompat_ioctl, /* SIOCSIWTXPOW */
wlcompat_ioctl, /* SIOCGIWTXPOW */
NULL, /* SIOCSIWRETRY */
NULL, /* SIOCGIWRETRY */
NULL, /* SIOCSIWENCODE */
wlcompat_ioctl, /* SIOCGIWENCODE */
};
static int wlcompat_private_ioctl(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra)
{
int *value = (int *) wrqu->name;
switch (info->cmd) {
case WLCOMPAT_SET_MONITOR:
{
if (wl_ioctl(dev, WLC_SET_MONITOR, value, sizeof(int)) < 0)
return -EINVAL;
break;
}
case WLCOMPAT_GET_MONITOR:
{
if (wl_ioctl(dev, WLC_GET_MONITOR, extra, sizeof(int)) < 0)
return -EINVAL;
break;
}
case WLCOMPAT_SET_TXPWR_LIMIT:
{
int val;
if (wl_get_val(dev, "qtxpower", &val, sizeof(int)) < 0)
return -EINVAL;
if (*extra > 0)
val |= WL_TXPWR_OVERRIDE;
else
val &= ~WL_TXPWR_OVERRIDE;
if (wl_set_val(dev, "qtxpower", &val, sizeof(int)) < 0)
return -EINVAL;
break;
}
case WLCOMPAT_GET_TXPWR_LIMIT:
{
if (wl_get_val(dev, "qtxpower", value, sizeof(int)) < 0)
return -EINVAL;
*value = ((*value & WL_TXPWR_OVERRIDE) == WL_TXPWR_OVERRIDE ? 1 : 0);
break;
}
case WLCOMPAT_SET_ANTDIV:
{
if (wl_ioctl(dev, WLC_SET_ANTDIV, value, sizeof(int)) < 0)
return -EINVAL;
break;
}
case WLCOMPAT_GET_ANTDIV:
{
if (wl_ioctl(dev, WLC_GET_ANTDIV, extra, sizeof(int)) < 0)
return -EINVAL;
break;
}
case WLCOMPAT_SET_TXANT:
{
if (wl_ioctl(dev, WLC_SET_TXANT, value, sizeof(int)) < 0)
return -EINVAL;
break;
}
case WLCOMPAT_GET_TXANT:
{
if (wl_ioctl(dev, WLC_GET_TXANT, extra, sizeof(int)) < 0)
return -EINVAL;
break;
}
default:
{
return -EINVAL;
}
}
return 0;
}
static const struct iw_priv_args wlcompat_private_args[] =
{
{ WLCOMPAT_SET_MONITOR,
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
0,
"set_monitor"
},
{ WLCOMPAT_GET_MONITOR,
0,
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
"get_monitor"
},
{ WLCOMPAT_SET_TXPWR_LIMIT,
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
0,
"set_txpwr_force"
},
{ WLCOMPAT_GET_TXPWR_LIMIT,
0,
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
"get_txpwr_force"
},
{ WLCOMPAT_SET_ANTDIV,
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
0,
"set_antdiv"
},
{ WLCOMPAT_GET_ANTDIV,
0,
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
"get_antdiv"
},
{ WLCOMPAT_SET_TXANT,
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
0,
"set_txant"
},
{ WLCOMPAT_GET_TXANT,
0,
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
"get_txant"
},
};
static const iw_handler wlcompat_private[] =
{
wlcompat_private_ioctl,
NULL
};
static const struct iw_handler_def wlcompat_handler_def =
{
.standard = (iw_handler *) wlcompat_handler,
.num_standard = sizeof(wlcompat_handler)/sizeof(iw_handler),
.private = wlcompat_private,
.num_private = 1,
.private_args = wlcompat_private_args,
.num_private_args = sizeof(wlcompat_private_args) / sizeof(wlcompat_private_args[0])
};
#ifdef DEBUG
void print_buffer(int len, unsigned char *buf) {
int x;
if (buf != NULL) {
for (x=0;x<len && x<180 ;x++) {
if ((x % 4) == 0)
printk(" ");
printk("%02X",buf[x]);
}
} else {
printk(" NULL");
}
printk("\n");
}
#endif
static int (*old_ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd);
static int new_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) {
int ret = 0;
struct iwreq *iwr = (struct iwreq *) ifr;
struct iw_request_info info;
#ifdef DEBUG
printk("dev: %s ioctl: 0x%04x\n",dev->name,cmd);
#endif
if (cmd >= SIOCIWFIRSTPRIV) {
info.cmd = cmd;
info.flags = 0;
ret = wlcompat_private_ioctl(dev, &info, &(iwr->u), (char *) &(iwr->u));
#ifdef DEBUG
} else if (cmd==SIOCDEVPRIVATE) {
wl_ioctl_t *ioc = (wl_ioctl_t *)ifr->ifr_data;
unsigned char *buf = ioc->buf;
printk(" cmd: %d buf: 0x%08x len: %d\n",ioc->cmd,&(ioc->buf),ioc->len);
printk(" send: ->");
print_buffer(ioc->len, buf);
ret = old_ioctl(dev,ifr,cmd);
printk(" recv: ->");
print_buffer(ioc->len, buf);
printk(" ret: %d\n", ret);
#endif
} else {
ret = old_ioctl(dev,ifr,cmd);
}
return ret;
}
static int __init wlcompat_init()
{
int found = 0, i;
char *devname = "eth0";
while (!found && (dev = dev_get_by_name(devname))) {
if ((dev->wireless_handlers == NULL) && ((wl_ioctl(dev, WLC_GET_MAGIC, &i, sizeof(i)) == 0) && i == WLC_IOCTL_MAGIC))
found = 1;
devname[3]++;
}
if (!found) {
printk("No Broadcom devices found.\n");
return -ENODEV;
}
old_ioctl = dev->do_ioctl;
dev->do_ioctl = new_ioctl;
dev->wireless_handlers = (struct iw_handler_def *)&wlcompat_handler_def;
return 0;
}
static void __exit wlcompat_exit()
{
dev->wireless_handlers = NULL;
dev->do_ioctl = old_ioctl;
return;
}
EXPORT_NO_SYMBOLS;
MODULE_AUTHOR("openwrt.org");
MODULE_LICENSE("GPL");
module_init(wlcompat_init);
module_exit(wlcompat_exit);