port generic and ixp4xx kernel patches to 2.6.27 (compiles except for iptables, but otherwise completely untested)
SVN-Revision: 12790
This commit is contained in:
parent
9b2097af21
commit
bc92b67c41
147 changed files with 79002 additions and 0 deletions
|
@ -28,6 +28,9 @@ endif
|
||||||
ifeq ($(LINUX_VERSION),2.6.26.5)
|
ifeq ($(LINUX_VERSION),2.6.26.5)
|
||||||
LINUX_KERNEL_MD5SUM:=98261b39a558cf0739703ffea7db9f43
|
LINUX_KERNEL_MD5SUM:=98261b39a558cf0739703ffea7db9f43
|
||||||
endif
|
endif
|
||||||
|
ifeq ($(LINUX_VERSION),2.6.27-rc7)
|
||||||
|
LINUX_KERNEL_MD5SUM:=39c859ea15b96b30de63305008a39ab1
|
||||||
|
endif
|
||||||
|
|
||||||
# disable the md5sum check for unknown kernel versions
|
# disable the md5sum check for unknown kernel versions
|
||||||
LINUX_KERNEL_MD5SUM?=x
|
LINUX_KERNEL_MD5SUM?=x
|
||||||
|
|
133
package/madwifi/patches/409-wext_compat.patch
Normal file
133
package/madwifi/patches/409-wext_compat.patch
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
--- a/net80211/ieee80211_wireless.c
|
||||||
|
+++ b/net80211/ieee80211_wireless.c
|
||||||
|
@@ -73,6 +73,13 @@
|
||||||
|
(_vap)->iv_ic->ic_roaming == IEEE80211_ROAMING_AUTO)
|
||||||
|
#define RESCAN 1
|
||||||
|
|
||||||
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
|
||||||
|
+#define IWE(func, ...) func(&iweinfo, __VA_ARGS__)
|
||||||
|
+static struct iw_request_info iweinfo = { 0, 0 };
|
||||||
|
+#else
|
||||||
|
+#define IWE(func, ...) func(__VA_ARGS__)
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
static void
|
||||||
|
pre_announced_chanswitch(struct net_device *dev, u_int32_t channel, u_int32_t tbtt);
|
||||||
|
|
||||||
|
@@ -1800,7 +1807,7 @@
|
||||||
|
IEEE80211_ADDR_COPY(iwe.u.ap_addr.sa_data, se->se_macaddr);
|
||||||
|
else
|
||||||
|
IEEE80211_ADDR_COPY(iwe.u.ap_addr.sa_data, se->se_bssid);
|
||||||
|
- current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_ADDR_LEN);
|
||||||
|
+ current_ev = IWE(iwe_stream_add_event, current_ev, end_buf, &iwe, IW_EV_ADDR_LEN);
|
||||||
|
|
||||||
|
/* We ran out of space in the buffer. */
|
||||||
|
if (last_ev == current_ev)
|
||||||
|
@@ -1811,7 +1818,7 @@
|
||||||
|
iwe.cmd = SIOCGIWESSID;
|
||||||
|
iwe.u.data.flags = 1;
|
||||||
|
iwe.u.data.length = se->se_ssid[1];
|
||||||
|
- current_ev = iwe_stream_add_point(current_ev,
|
||||||
|
+ current_ev = IWE(iwe_stream_add_point, current_ev,
|
||||||
|
end_buf, &iwe, (char *) se->se_ssid+2);
|
||||||
|
|
||||||
|
/* We ran out of space in the buffer. */
|
||||||
|
@@ -1824,7 +1831,7 @@
|
||||||
|
iwe.cmd = SIOCGIWMODE;
|
||||||
|
iwe.u.mode = se->se_capinfo & IEEE80211_CAPINFO_ESS ?
|
||||||
|
IW_MODE_MASTER : IW_MODE_ADHOC;
|
||||||
|
- current_ev = iwe_stream_add_event(current_ev,
|
||||||
|
+ current_ev = IWE(iwe_stream_add_event, current_ev,
|
||||||
|
end_buf, &iwe, IW_EV_UINT_LEN);
|
||||||
|
|
||||||
|
/* We ran out of space in the buffer. */
|
||||||
|
@@ -1837,7 +1844,7 @@
|
||||||
|
iwe.cmd = SIOCGIWFREQ;
|
||||||
|
iwe.u.freq.m = se->se_chan->ic_freq * 100000;
|
||||||
|
iwe.u.freq.e = 1;
|
||||||
|
- current_ev = iwe_stream_add_event(current_ev,
|
||||||
|
+ current_ev = IWE(iwe_stream_add_event, current_ev,
|
||||||
|
end_buf, &iwe, IW_EV_FREQ_LEN);
|
||||||
|
|
||||||
|
/* We ran out of space in the buffer. */
|
||||||
|
@@ -1848,7 +1855,7 @@
|
||||||
|
last_ev = current_ev;
|
||||||
|
iwe.cmd = IWEVQUAL;
|
||||||
|
set_quality(&iwe.u.qual, se->se_rssi, ATH_DEFAULT_NOISE);
|
||||||
|
- current_ev = iwe_stream_add_event(current_ev,
|
||||||
|
+ current_ev = IWE(iwe_stream_add_event, current_ev,
|
||||||
|
end_buf, &iwe, IW_EV_QUAL_LEN);
|
||||||
|
|
||||||
|
/* We ran out of space in the buffer */
|
||||||
|
@@ -1863,7 +1870,7 @@
|
||||||
|
else
|
||||||
|
iwe.u.data.flags = IW_ENCODE_DISABLED;
|
||||||
|
iwe.u.data.length = 0;
|
||||||
|
- current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, "");
|
||||||
|
+ current_ev = IWE(iwe_stream_add_point, current_ev, end_buf, &iwe, "");
|
||||||
|
|
||||||
|
/* We ran out of space in the buffer. */
|
||||||
|
if (last_ev == current_ev)
|
||||||
|
@@ -1878,7 +1885,7 @@
|
||||||
|
int r = se->se_rates[2 + j] & IEEE80211_RATE_VAL;
|
||||||
|
if (r != 0) {
|
||||||
|
iwe.u.bitrate.value = r * (1000000 / 2);
|
||||||
|
- current_val = iwe_stream_add_value(current_ev,
|
||||||
|
+ current_val = IWE(iwe_stream_add_value, current_ev,
|
||||||
|
current_val, end_buf, &iwe,
|
||||||
|
IW_EV_PARAM_LEN);
|
||||||
|
}
|
||||||
|
@@ -1887,7 +1894,7 @@
|
||||||
|
int r = se->se_xrates[2+j] & IEEE80211_RATE_VAL;
|
||||||
|
if (r != 0) {
|
||||||
|
iwe.u.bitrate.value = r * (1000000 / 2);
|
||||||
|
- current_val = iwe_stream_add_value(current_ev,
|
||||||
|
+ current_val = IWE(iwe_stream_add_value, current_ev,
|
||||||
|
current_val, end_buf, &iwe,
|
||||||
|
IW_EV_PARAM_LEN);
|
||||||
|
}
|
||||||
|
@@ -1906,7 +1913,7 @@
|
||||||
|
iwe.cmd = IWEVCUSTOM;
|
||||||
|
snprintf(buf, sizeof(buf), "bcn_int=%d", se->se_intval);
|
||||||
|
iwe.u.data.length = strlen(buf);
|
||||||
|
- current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, buf);
|
||||||
|
+ current_ev = IWE(iwe_stream_add_point, current_ev, end_buf, &iwe, buf);
|
||||||
|
|
||||||
|
/* We ran out of space in the buffer. */
|
||||||
|
if (last_ev == current_ev)
|
||||||
|
@@ -1930,7 +1937,7 @@
|
||||||
|
rsn_leader, sizeof(rsn_leader) - 1);
|
||||||
|
#endif
|
||||||
|
if (iwe.u.data.length != 0) {
|
||||||
|
- current_ev = iwe_stream_add_point(current_ev, end_buf,
|
||||||
|
+ current_ev = IWE(iwe_stream_add_point, current_ev, end_buf,
|
||||||
|
&iwe, buf);
|
||||||
|
|
||||||
|
/* We ran out of space in the buffer */
|
||||||
|
@@ -1956,7 +1963,7 @@
|
||||||
|
wpa_leader, sizeof(wpa_leader) - 1);
|
||||||
|
#endif
|
||||||
|
if (iwe.u.data.length != 0) {
|
||||||
|
- current_ev = iwe_stream_add_point(current_ev, end_buf,
|
||||||
|
+ current_ev = IWE(iwe_stream_add_point, current_ev, end_buf,
|
||||||
|
&iwe, buf);
|
||||||
|
|
||||||
|
/* We ran out of space in the buffer. */
|
||||||
|
@@ -1975,7 +1982,7 @@
|
||||||
|
se->se_wme_ie, se->se_wme_ie[1] + 2,
|
||||||
|
wme_leader, sizeof(wme_leader) - 1);
|
||||||
|
if (iwe.u.data.length != 0) {
|
||||||
|
- current_ev = iwe_stream_add_point(current_ev, end_buf,
|
||||||
|
+ current_ev = IWE(iwe_stream_add_point, current_ev, end_buf,
|
||||||
|
&iwe, buf);
|
||||||
|
|
||||||
|
/* We ran out of space in the buffer. */
|
||||||
|
@@ -1993,7 +2000,7 @@
|
||||||
|
se->se_ath_ie, se->se_ath_ie[1] + 2,
|
||||||
|
ath_leader, sizeof(ath_leader) - 1);
|
||||||
|
if (iwe.u.data.length != 0) {
|
||||||
|
- current_ev = iwe_stream_add_point(current_ev, end_buf,
|
||||||
|
+ current_ev = IWE(iwe_stream_add_point, current_ev, end_buf,
|
||||||
|
&iwe, buf);
|
||||||
|
|
||||||
|
/* We ran out of space in the buffer. */
|
1914
target/linux/generic-2.6/config-2.6.27
Normal file
1914
target/linux/generic-2.6/config-2.6.27
Normal file
File diff suppressed because it is too large
Load diff
201
target/linux/generic-2.6/files-2.6.27/drivers/char/gpio_dev.c
Normal file
201
target/linux/generic-2.6/files-2.6.27/drivers/char/gpio_dev.c
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
/*
|
||||||
|
* character device wrapper for generic gpio layer
|
||||||
|
*
|
||||||
|
* 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, MA02111-1307USA
|
||||||
|
*
|
||||||
|
* Feedback, Bugs... blogic@openwrt.org
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/errno.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <asm/uaccess.h>
|
||||||
|
#include <asm/io.h>
|
||||||
|
#include <asm/gpio.h>
|
||||||
|
#include <asm/atomic.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/genhd.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/gpio_dev.h>
|
||||||
|
|
||||||
|
#define DRVNAME "gpiodev"
|
||||||
|
#define DEVNAME "gpio"
|
||||||
|
|
||||||
|
static int dev_major;
|
||||||
|
static unsigned int gpio_access_mask;
|
||||||
|
static struct class *gpio_class;
|
||||||
|
|
||||||
|
/* Counter is 1, if the device is not opened and zero (or less) if opened. */
|
||||||
|
static atomic_t gpio_open_cnt = ATOMIC_INIT(1);
|
||||||
|
|
||||||
|
static int
|
||||||
|
gpio_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg)
|
||||||
|
{
|
||||||
|
int retval = 0;
|
||||||
|
|
||||||
|
if (((1 << arg) & gpio_access_mask) != (1 << arg))
|
||||||
|
{
|
||||||
|
retval = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (cmd)
|
||||||
|
{
|
||||||
|
case GPIO_GET:
|
||||||
|
retval = gpio_get_value(arg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GPIO_SET:
|
||||||
|
gpio_set_value(arg, 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GPIO_CLEAR:
|
||||||
|
gpio_set_value(arg, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GPIO_DIR_IN:
|
||||||
|
gpio_direction_input(arg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GPIO_DIR_OUT:
|
||||||
|
gpio_direction_output(arg, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
retval = -EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
gpio_open(struct inode *inode, struct file *file)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
unsigned int dev_minor = MINOR(inode->i_rdev);
|
||||||
|
|
||||||
|
if (dev_minor != 0)
|
||||||
|
{
|
||||||
|
printk(KERN_ERR DRVNAME ": trying to access unknown minor device -> %d\n", dev_minor);
|
||||||
|
result = -ENODEV;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FIXME: We should really allow multiple applications to open the device
|
||||||
|
* at the same time, as long as the apps access different IO pins.
|
||||||
|
* The generic gpio-registration functions can be used for that.
|
||||||
|
* Two new IOCTLs have to be introduced for that. Need to check userspace
|
||||||
|
* compatibility first. --mb */
|
||||||
|
if (!atomic_dec_and_test(&gpio_open_cnt)) {
|
||||||
|
atomic_inc(&gpio_open_cnt);
|
||||||
|
printk(KERN_ERR DRVNAME ": Device with minor ID %d already in use\n", dev_minor);
|
||||||
|
result = -EBUSY;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
gpio_close(struct inode * inode, struct file * file)
|
||||||
|
{
|
||||||
|
smp_mb__before_atomic_inc();
|
||||||
|
atomic_inc(&gpio_open_cnt);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct file_operations gpio_fops = {
|
||||||
|
ioctl: gpio_ioctl,
|
||||||
|
open: gpio_open,
|
||||||
|
release: gpio_close
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
gpio_probe(struct platform_device *dev)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
dev_major = register_chrdev(0, DEVNAME, &gpio_fops);
|
||||||
|
if (!dev_major)
|
||||||
|
{
|
||||||
|
printk(KERN_ERR DRVNAME ": Error whilst opening %s \n", DEVNAME);
|
||||||
|
result = -ENODEV;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpio_class = class_create(THIS_MODULE, DEVNAME);
|
||||||
|
device_create(gpio_class, NULL, MKDEV(dev_major, 0), dev, DEVNAME);
|
||||||
|
|
||||||
|
printk(KERN_INFO DRVNAME ": gpio device registered with major %d\n", dev_major);
|
||||||
|
|
||||||
|
if (dev->num_resources != 1)
|
||||||
|
{
|
||||||
|
printk(KERN_ERR DRVNAME ": device may only have 1 resource\n");
|
||||||
|
result = -ENODEV;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpio_access_mask = dev->resource[0].start;
|
||||||
|
|
||||||
|
printk(KERN_INFO DRVNAME ": gpio platform device registered with access mask %08X\n", gpio_access_mask);
|
||||||
|
out:
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
gpio_remove(struct platform_device *dev)
|
||||||
|
{
|
||||||
|
unregister_chrdev(dev_major, DEVNAME);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct
|
||||||
|
platform_driver gpio_driver = {
|
||||||
|
.probe = gpio_probe,
|
||||||
|
.remove = gpio_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = "GPIODEV",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init
|
||||||
|
gpio_mod_init(void)
|
||||||
|
{
|
||||||
|
int ret = platform_driver_register(&gpio_driver);
|
||||||
|
if (ret)
|
||||||
|
printk(KERN_INFO DRVNAME ": Error registering platfom driver!");
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit
|
||||||
|
gpio_mod_exit(void)
|
||||||
|
{
|
||||||
|
platform_driver_unregister(&gpio_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init (gpio_mod_init);
|
||||||
|
module_exit (gpio_mod_exit);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_AUTHOR("John Crispin / OpenWrt");
|
||||||
|
MODULE_DESCRIPTION("Character device for for generic gpio api");
|
|
@ -0,0 +1,209 @@
|
||||||
|
/*
|
||||||
|
* Driver for buttons on GPIO lines not capable of generating interrupts
|
||||||
|
*
|
||||||
|
* Copyright (C) 2007,2008 Gabor Juhos <juhosg at openwrt.org>
|
||||||
|
*
|
||||||
|
* This file was based on: /drivers/input/misc/cobalt_btns.c
|
||||||
|
* Copyright (C) 2007 Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>
|
||||||
|
*
|
||||||
|
* also was based on: /drivers/input/keyboard/gpio_keys.c
|
||||||
|
* Copyright 2005 Phil Blundell
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/input-polldev.h>
|
||||||
|
#include <linux/ioport.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
|
||||||
|
#include <linux/gpio_buttons.h>
|
||||||
|
|
||||||
|
#include <asm/gpio.h>
|
||||||
|
|
||||||
|
#define DRV_NAME "gpio-buttons"
|
||||||
|
#define DRV_VERSION "0.1.1"
|
||||||
|
#define PFX DRV_NAME ": "
|
||||||
|
|
||||||
|
struct gpio_buttons_dev {
|
||||||
|
struct input_polled_dev *poll_dev;
|
||||||
|
struct gpio_buttons_platform_data *pdata;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void gpio_buttons_poll(struct input_polled_dev *dev)
|
||||||
|
{
|
||||||
|
struct gpio_buttons_dev *bdev = dev->private;
|
||||||
|
struct gpio_buttons_platform_data *pdata = bdev->pdata;
|
||||||
|
struct input_dev *input = dev->input;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < bdev->pdata->nbuttons; i++) {
|
||||||
|
struct gpio_button *button = &pdata->buttons[i];
|
||||||
|
unsigned int type = button->type ?: EV_KEY;
|
||||||
|
int state;
|
||||||
|
|
||||||
|
state = gpio_get_value(button->gpio) ? 1 : 0;
|
||||||
|
state ^= button->active_low;
|
||||||
|
|
||||||
|
if (state) {
|
||||||
|
button->count++;
|
||||||
|
} else {
|
||||||
|
if (button->count >= button->threshold) {
|
||||||
|
input_event(input, type, button->code, 1);
|
||||||
|
input_sync(input);
|
||||||
|
}
|
||||||
|
button->count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (button->count == button->threshold) {
|
||||||
|
input_event(input, type, button->code, 0);
|
||||||
|
input_sync(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit gpio_buttons_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct gpio_buttons_platform_data *pdata = pdev->dev.platform_data;
|
||||||
|
struct gpio_buttons_dev *bdev;
|
||||||
|
struct input_polled_dev *poll_dev;
|
||||||
|
struct input_dev *input;
|
||||||
|
int error, i;
|
||||||
|
|
||||||
|
|
||||||
|
if (!pdata)
|
||||||
|
return -ENXIO;
|
||||||
|
|
||||||
|
bdev = kzalloc(sizeof(*bdev), GFP_KERNEL);
|
||||||
|
if (!bdev) {
|
||||||
|
printk(KERN_ERR DRV_NAME "no memory for device\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
poll_dev = input_allocate_polled_device();
|
||||||
|
if (!poll_dev) {
|
||||||
|
printk(KERN_ERR DRV_NAME "no memory for polled device\n");
|
||||||
|
error = -ENOMEM;
|
||||||
|
goto err_free_bdev;
|
||||||
|
}
|
||||||
|
|
||||||
|
poll_dev->private = bdev;
|
||||||
|
poll_dev->poll = gpio_buttons_poll;
|
||||||
|
poll_dev->poll_interval = pdata->poll_interval;
|
||||||
|
|
||||||
|
input = poll_dev->input;
|
||||||
|
|
||||||
|
input->evbit[0] = BIT(EV_KEY);
|
||||||
|
input->name = pdev->name;
|
||||||
|
input->phys = "gpio-buttons/input0";
|
||||||
|
input->dev.parent = &pdev->dev;
|
||||||
|
|
||||||
|
input->id.bustype = BUS_HOST;
|
||||||
|
input->id.vendor = 0x0001;
|
||||||
|
input->id.product = 0x0001;
|
||||||
|
input->id.version = 0x0100;
|
||||||
|
|
||||||
|
for (i = 0; i < pdata->nbuttons; i++) {
|
||||||
|
struct gpio_button *button = &pdata->buttons[i];
|
||||||
|
unsigned int gpio = button->gpio;
|
||||||
|
unsigned int type = button->type ?: EV_KEY;
|
||||||
|
|
||||||
|
error = gpio_request(gpio, button->desc ?
|
||||||
|
button->desc : DRV_NAME);
|
||||||
|
if (error) {
|
||||||
|
printk(KERN_ERR PFX "unable to claim gpio %u, "
|
||||||
|
"error %d\n", gpio, error);
|
||||||
|
goto err_free_gpio;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = gpio_direction_input(gpio);
|
||||||
|
if (error) {
|
||||||
|
printk(KERN_ERR PFX "unable to set direction on "
|
||||||
|
"gpio %u, error %d\n", gpio, error);
|
||||||
|
goto err_free_gpio;
|
||||||
|
}
|
||||||
|
|
||||||
|
input_set_capability(input, type, button->code);
|
||||||
|
button->count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bdev->poll_dev = poll_dev;
|
||||||
|
bdev->pdata = pdata;
|
||||||
|
platform_set_drvdata(pdev, bdev);
|
||||||
|
|
||||||
|
error = input_register_polled_device(poll_dev);
|
||||||
|
if (error) {
|
||||||
|
printk(KERN_ERR PFX "unable to register polled device, "
|
||||||
|
"error %d\n", error);
|
||||||
|
goto err_free_gpio;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_free_gpio:
|
||||||
|
for (i = i - 1; i >= 0; i--)
|
||||||
|
gpio_free(pdata->buttons[i].gpio);
|
||||||
|
|
||||||
|
input_free_polled_device(poll_dev);
|
||||||
|
|
||||||
|
err_free_bdev:
|
||||||
|
kfree(bdev);
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devexit gpio_buttons_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct gpio_buttons_dev *bdev = platform_get_drvdata(pdev);
|
||||||
|
struct gpio_buttons_platform_data *pdata = bdev->pdata;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
input_unregister_polled_device(bdev->poll_dev);
|
||||||
|
|
||||||
|
for (i = 0; i < pdata->nbuttons; i++)
|
||||||
|
gpio_free(pdata->buttons[i].gpio);
|
||||||
|
|
||||||
|
input_free_polled_device(bdev->poll_dev);
|
||||||
|
|
||||||
|
kfree(bdev);
|
||||||
|
platform_set_drvdata(pdev, NULL);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct platform_driver gpio_buttons_driver = {
|
||||||
|
.probe = gpio_buttons_probe,
|
||||||
|
.remove = __devexit_p(gpio_buttons_remove),
|
||||||
|
.driver = {
|
||||||
|
.name = DRV_NAME,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init gpio_buttons_init(void)
|
||||||
|
{
|
||||||
|
printk(KERN_INFO DRV_NAME " driver version " DRV_VERSION "\n");
|
||||||
|
return platform_driver_register(&gpio_buttons_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit gpio_buttons_exit(void)
|
||||||
|
{
|
||||||
|
platform_driver_unregister(&gpio_buttons_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(gpio_buttons_init);
|
||||||
|
module_exit(gpio_buttons_exit);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_AUTHOR("Gabor Juhos <juhosg at openwrt.org>");
|
||||||
|
MODULE_VERSION(DRV_VERSION);
|
||||||
|
MODULE_DESCRIPTION("Polled buttons driver for CPU GPIOs");
|
||||||
|
|
172
target/linux/generic-2.6/files-2.6.27/drivers/leds/leds-alix.c
Normal file
172
target/linux/generic-2.6/files-2.6.27/drivers/leds/leds-alix.c
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
/*
|
||||||
|
* LEDs driver for PCEngines ALIX 2/3 series
|
||||||
|
*
|
||||||
|
* Copyright (C) 2007 Petr Liebman
|
||||||
|
*
|
||||||
|
* Based on leds-wrap.c
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/leds.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <asm/io.h>
|
||||||
|
|
||||||
|
#define DRVNAME "alix-led"
|
||||||
|
|
||||||
|
#define ALIX_LED1_PORT (0x6100)
|
||||||
|
#define ALIX_LED1_ON (1<<22)
|
||||||
|
#define ALIX_LED1_OFF (1<<6)
|
||||||
|
|
||||||
|
#define ALIX_LED2_PORT (0x6180)
|
||||||
|
#define ALIX_LED2_ON (1<<25)
|
||||||
|
#define ALIX_LED2_OFF (1<<9)
|
||||||
|
|
||||||
|
#define ALIX_LED3_PORT (0x6180)
|
||||||
|
#define ALIX_LED3_ON (1<<27)
|
||||||
|
#define ALIX_LED3_OFF (1<<11)
|
||||||
|
|
||||||
|
|
||||||
|
static struct platform_device *pdev;
|
||||||
|
|
||||||
|
static void alix_led_set_1(struct led_classdev *led_cdev,
|
||||||
|
enum led_brightness value)
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
outl(ALIX_LED1_ON, ALIX_LED1_PORT);
|
||||||
|
else
|
||||||
|
outl(ALIX_LED1_OFF, ALIX_LED1_PORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void alix_led_set_2(struct led_classdev *led_cdev,
|
||||||
|
enum led_brightness value)
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
outl(ALIX_LED2_ON, ALIX_LED2_PORT);
|
||||||
|
else
|
||||||
|
outl(ALIX_LED2_OFF, ALIX_LED2_PORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void alix_led_set_3(struct led_classdev *led_cdev,
|
||||||
|
enum led_brightness value)
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
outl(ALIX_LED3_ON, ALIX_LED3_PORT);
|
||||||
|
else
|
||||||
|
outl(ALIX_LED3_OFF, ALIX_LED3_PORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct led_classdev alix_led_1 = {
|
||||||
|
.name = "alix:1",
|
||||||
|
.brightness_set = alix_led_set_1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct led_classdev alix_led_2 = {
|
||||||
|
.name = "alix:2",
|
||||||
|
.brightness_set = alix_led_set_2,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct led_classdev alix_led_3 = {
|
||||||
|
.name = "alix:3",
|
||||||
|
.brightness_set = alix_led_set_3,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
static int alix_led_suspend(struct platform_device *dev,
|
||||||
|
pm_message_t state)
|
||||||
|
{
|
||||||
|
led_classdev_suspend(&alix_led_1);
|
||||||
|
led_classdev_suspend(&alix_led_2);
|
||||||
|
led_classdev_suspend(&alix_led_3);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int alix_led_resume(struct platform_device *dev)
|
||||||
|
{
|
||||||
|
led_classdev_resume(&alix_led_1);
|
||||||
|
led_classdev_resume(&alix_led_2);
|
||||||
|
led_classdev_resume(&alix_led_3);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define alix_led_suspend NULL
|
||||||
|
#define alix_led_resume NULL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int alix_led_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = led_classdev_register(&pdev->dev, &alix_led_1);
|
||||||
|
if (ret >= 0)
|
||||||
|
{
|
||||||
|
ret = led_classdev_register(&pdev->dev, &alix_led_2);
|
||||||
|
if (ret >= 0)
|
||||||
|
{
|
||||||
|
ret = led_classdev_register(&pdev->dev, &alix_led_3);
|
||||||
|
if (ret < 0)
|
||||||
|
led_classdev_unregister(&alix_led_2);
|
||||||
|
}
|
||||||
|
if (ret < 0)
|
||||||
|
led_classdev_unregister(&alix_led_1);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int alix_led_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
led_classdev_unregister(&alix_led_1);
|
||||||
|
led_classdev_unregister(&alix_led_2);
|
||||||
|
led_classdev_unregister(&alix_led_3);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct platform_driver alix_led_driver = {
|
||||||
|
.probe = alix_led_probe,
|
||||||
|
.remove = alix_led_remove,
|
||||||
|
.suspend = alix_led_suspend,
|
||||||
|
.resume = alix_led_resume,
|
||||||
|
.driver = {
|
||||||
|
.name = DRVNAME,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init alix_led_init(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = platform_driver_register(&alix_led_driver);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
pdev = platform_device_register_simple(DRVNAME, -1, NULL, 0);
|
||||||
|
if (IS_ERR(pdev)) {
|
||||||
|
ret = PTR_ERR(pdev);
|
||||||
|
platform_driver_unregister(&alix_led_driver);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit alix_led_exit(void)
|
||||||
|
{
|
||||||
|
platform_device_unregister(pdev);
|
||||||
|
platform_driver_unregister(&alix_led_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(alix_led_init);
|
||||||
|
module_exit(alix_led_exit);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Petr Liebman");
|
||||||
|
MODULE_DESCRIPTION("PCEngines ALIX LED driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
|
|
@ -0,0 +1,365 @@
|
||||||
|
/*
|
||||||
|
* LED Morse Trigger
|
||||||
|
*
|
||||||
|
* Copyright (C) 2007 Gabor Juhos <juhosg at openwrt.org>
|
||||||
|
*
|
||||||
|
* This file was based on: drivers/led/ledtrig-timer.c
|
||||||
|
* Copyright 2005-2006 Openedhand Ltd.
|
||||||
|
* Author: Richard Purdie <rpurdie@openedhand.com>
|
||||||
|
*
|
||||||
|
* also based on the patch '[PATCH] 2.5.59 morse code panics' posted
|
||||||
|
* in the LKML by Tomas Szepe at Thu, 30 Jan 2003
|
||||||
|
* Copyright (C) 2002 Andrew Rodland <arodland@noln.com>
|
||||||
|
* Copyright (C) 2003 Tomas Szepe <szepe@pinerecords.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 as published
|
||||||
|
* by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/jiffies.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/sysdev.h>
|
||||||
|
#include <linux/timer.h>
|
||||||
|
#include <linux/ctype.h>
|
||||||
|
#include <linux/leds.h>
|
||||||
|
|
||||||
|
#include "leds.h"
|
||||||
|
|
||||||
|
#define MORSE_DELAY_BASE (HZ/2)
|
||||||
|
|
||||||
|
#define MORSE_STATE_BLINK_START 0
|
||||||
|
#define MORSE_STATE_BLINK_STOP 1
|
||||||
|
|
||||||
|
#define MORSE_DIT_LEN 1
|
||||||
|
#define MORSE_DAH_LEN 3
|
||||||
|
#define MORSE_SPACE_LEN 7
|
||||||
|
|
||||||
|
struct morse_trig_data {
|
||||||
|
unsigned long delay;
|
||||||
|
char *msg;
|
||||||
|
|
||||||
|
unsigned char morse;
|
||||||
|
unsigned char state;
|
||||||
|
char *msgpos;
|
||||||
|
struct timer_list timer;
|
||||||
|
};
|
||||||
|
|
||||||
|
const unsigned char morsetable[] = {
|
||||||
|
0122, 0, 0310, 0, 0, 0163, /* "#$%&' */
|
||||||
|
055, 0155, 0, 0, 0163, 0141, 0152, 0051, /* ()*+,-./ */
|
||||||
|
077, 076, 074, 070, 060, 040, 041, 043, 047, 057, /* 0-9 */
|
||||||
|
0107, 0125, 0, 0061, 0, 0114, 0, /* :;<=>?@ */
|
||||||
|
006, 021, 025, 011, 002, 024, 013, 020, 004, /* A-I */
|
||||||
|
036, 015, 022, 007, 005, 017, 026, 033, 012, /* J-R */
|
||||||
|
010, 003, 014, 030, 016, 031, 035, 023, /* S-Z */
|
||||||
|
0, 0, 0, 0, 0154 /* [\]^_ */
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline unsigned char tomorse(char c) {
|
||||||
|
if (c >= 'a' && c <= 'z')
|
||||||
|
c = c - 'a' + 'A';
|
||||||
|
if (c >= '"' && c <= '_') {
|
||||||
|
return morsetable[c - '"'];
|
||||||
|
} else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned long dit_len(struct morse_trig_data *morse_data)
|
||||||
|
{
|
||||||
|
return MORSE_DIT_LEN*morse_data->delay;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned long dah_len(struct morse_trig_data *morse_data)
|
||||||
|
{
|
||||||
|
return MORSE_DAH_LEN*morse_data->delay;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned long space_len(struct morse_trig_data *morse_data)
|
||||||
|
{
|
||||||
|
return MORSE_SPACE_LEN*morse_data->delay;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void morse_timer_function(unsigned long data)
|
||||||
|
{
|
||||||
|
struct led_classdev *led_cdev = (struct led_classdev *)data;
|
||||||
|
struct morse_trig_data *morse_data = led_cdev->trigger_data;
|
||||||
|
unsigned long brightness = LED_OFF;
|
||||||
|
unsigned long delay = 0;
|
||||||
|
|
||||||
|
if (!morse_data->msg)
|
||||||
|
goto set_led;
|
||||||
|
|
||||||
|
switch (morse_data->state) {
|
||||||
|
case MORSE_STATE_BLINK_START:
|
||||||
|
/* Starting a new blink. We have a valid code in morse. */
|
||||||
|
delay = (morse_data->morse & 001) ? dah_len(morse_data):
|
||||||
|
dit_len(morse_data);
|
||||||
|
brightness = LED_FULL;
|
||||||
|
morse_data->state = MORSE_STATE_BLINK_STOP;
|
||||||
|
morse_data->morse >>= 1;
|
||||||
|
break;
|
||||||
|
case MORSE_STATE_BLINK_STOP:
|
||||||
|
/* Coming off of a blink. */
|
||||||
|
morse_data->state = MORSE_STATE_BLINK_START;
|
||||||
|
|
||||||
|
if (morse_data->morse > 1) {
|
||||||
|
/* Not done yet, just a one-dit pause. */
|
||||||
|
delay = dit_len(morse_data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get a new char, figure out how much space. */
|
||||||
|
/* First time through */
|
||||||
|
if (!morse_data->msgpos)
|
||||||
|
morse_data->msgpos = (char *)morse_data->msg;
|
||||||
|
|
||||||
|
if (!*morse_data->msgpos) {
|
||||||
|
/* Repeating */
|
||||||
|
morse_data->msgpos = (char *)morse_data->msg;
|
||||||
|
delay = space_len(morse_data);
|
||||||
|
} else {
|
||||||
|
/* Inter-letter space */
|
||||||
|
delay = dah_len(morse_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(morse_data->morse = tomorse(*morse_data->msgpos))) {
|
||||||
|
delay = space_len(morse_data);
|
||||||
|
/* And get us back here */
|
||||||
|
morse_data->state = MORSE_STATE_BLINK_STOP;
|
||||||
|
}
|
||||||
|
morse_data->msgpos++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
mod_timer(&morse_data->timer, jiffies + msecs_to_jiffies(delay));
|
||||||
|
|
||||||
|
set_led:
|
||||||
|
led_set_brightness(led_cdev, brightness);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t _morse_delay_show(struct led_classdev *led_cdev, char *buf)
|
||||||
|
{
|
||||||
|
struct morse_trig_data *morse_data = led_cdev->trigger_data;
|
||||||
|
|
||||||
|
sprintf(buf, "%lu\n", morse_data->delay);
|
||||||
|
|
||||||
|
return strlen(buf) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t _morse_delay_store(struct led_classdev *led_cdev,
|
||||||
|
const char *buf, size_t size)
|
||||||
|
{
|
||||||
|
struct morse_trig_data *morse_data = led_cdev->trigger_data;
|
||||||
|
char *after;
|
||||||
|
unsigned long state = simple_strtoul(buf, &after, 10);
|
||||||
|
size_t count = after - buf;
|
||||||
|
int ret = -EINVAL;
|
||||||
|
|
||||||
|
if (*after && isspace(*after))
|
||||||
|
count++;
|
||||||
|
|
||||||
|
if (count == size) {
|
||||||
|
morse_data->delay = state;
|
||||||
|
mod_timer(&morse_data->timer, jiffies + 1);
|
||||||
|
ret = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t _morse_msg_show(struct led_classdev *led_cdev, char *buf)
|
||||||
|
{
|
||||||
|
struct morse_trig_data *morse_data = led_cdev->trigger_data;
|
||||||
|
|
||||||
|
if (!morse_data->msg)
|
||||||
|
sprintf(buf, "<none>\n");
|
||||||
|
else
|
||||||
|
sprintf(buf, "%s\n", morse_data->msg);
|
||||||
|
|
||||||
|
return strlen(buf) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t _morse_msg_store(struct led_classdev *led_cdev,
|
||||||
|
const char *buf, size_t size)
|
||||||
|
{
|
||||||
|
struct morse_trig_data *morse_data = led_cdev->trigger_data;
|
||||||
|
char *m;
|
||||||
|
|
||||||
|
m = kmalloc(size, GFP_KERNEL);
|
||||||
|
if (!m)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
memcpy(m,buf,size);
|
||||||
|
m[size]='\0';
|
||||||
|
|
||||||
|
if (morse_data->msg)
|
||||||
|
kfree(morse_data->msg);
|
||||||
|
|
||||||
|
morse_data->msg = m;
|
||||||
|
morse_data->msgpos = NULL;
|
||||||
|
morse_data->state = MORSE_STATE_BLINK_STOP;
|
||||||
|
|
||||||
|
mod_timer(&morse_data->timer, jiffies + 1);
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
|
||||||
|
static ssize_t morse_delay_show(struct device *dev,
|
||||||
|
struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
return _morse_delay_show(led_cdev, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t morse_delay_store(struct device *dev,
|
||||||
|
struct device_attribute *attr, const char *buf, size_t size)
|
||||||
|
{
|
||||||
|
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
return _morse_delay_store(led_cdev, buf, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t morse_msg_show(struct device *dev,
|
||||||
|
struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
return _morse_msg_show(led_cdev, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t morse_msg_store(struct device *dev,
|
||||||
|
struct device_attribute *attr, const char *buf, size_t size)
|
||||||
|
{
|
||||||
|
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
return _morse_msg_store(led_cdev, buf, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEVICE_ATTR(delay, 0644, morse_delay_show, morse_delay_store);
|
||||||
|
static DEVICE_ATTR(message, 0644, morse_msg_show, morse_msg_store);
|
||||||
|
|
||||||
|
#define led_device_create_file(leddev, attr) \
|
||||||
|
device_create_file(leddev->dev, &dev_attr_ ## attr)
|
||||||
|
#define led_device_remove_file(leddev, attr) \
|
||||||
|
device_remove_file(leddev->dev, &dev_attr_ ## attr)
|
||||||
|
|
||||||
|
#else
|
||||||
|
static ssize_t morse_delay_show(struct class_device *dev, char *buf)
|
||||||
|
{
|
||||||
|
struct led_classdev *led_cdev = class_get_devdata(dev);
|
||||||
|
|
||||||
|
return _morse_delay_show(led_cdev, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t morse_delay_store(struct class_device *dev, const char *buf,
|
||||||
|
size_t size)
|
||||||
|
{
|
||||||
|
struct led_classdev *led_cdev = class_get_devdata(dev);
|
||||||
|
|
||||||
|
return _morse_delay_store(led_cdev, buf, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t morse_msg_show(struct class_device *dev, char *buf)
|
||||||
|
{
|
||||||
|
struct led_classdev *led_cdev = class_get_devdata(dev);
|
||||||
|
|
||||||
|
return _morse_msg_show(led_cdev, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t morse_msg_store(struct class_device *dev, const char *buf,
|
||||||
|
size_t size)
|
||||||
|
{
|
||||||
|
struct led_classdev *led_cdev = class_get_devdata(dev);
|
||||||
|
|
||||||
|
return _morse_msg_store(led_cdev, buf, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static CLASS_DEVICE_ATTR(delay, 0644, morse_delay_show, morse_delay_store);
|
||||||
|
static CLASS_DEVICE_ATTR(message, 0644, morse_msg_show, morse_msg_store);
|
||||||
|
|
||||||
|
#define led_device_create_file(leddev, attr) \
|
||||||
|
class_device_create_file(leddev->class_dev, &class_device_attr_ ## attr)
|
||||||
|
#define led_device_remove_file(leddev, attr) \
|
||||||
|
class_device_remove_file(leddev->class_dev, &class_device_attr_ ## attr)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void morse_trig_activate(struct led_classdev *led_cdev)
|
||||||
|
{
|
||||||
|
struct morse_trig_data *morse_data;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
morse_data = kzalloc(sizeof(*morse_data), GFP_KERNEL);
|
||||||
|
if (!morse_data)
|
||||||
|
return;
|
||||||
|
|
||||||
|
morse_data->delay = MORSE_DELAY_BASE;
|
||||||
|
init_timer(&morse_data->timer);
|
||||||
|
morse_data->timer.function = morse_timer_function;
|
||||||
|
morse_data->timer.data = (unsigned long)led_cdev;
|
||||||
|
|
||||||
|
rc = led_device_create_file(led_cdev, delay);
|
||||||
|
if (rc) goto err;
|
||||||
|
|
||||||
|
rc = led_device_create_file(led_cdev, message);
|
||||||
|
if (rc) goto err_delay;
|
||||||
|
|
||||||
|
led_cdev->trigger_data = morse_data;
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
err_delay:
|
||||||
|
led_device_remove_file(led_cdev, delay);
|
||||||
|
err:
|
||||||
|
kfree(morse_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void morse_trig_deactivate(struct led_classdev *led_cdev)
|
||||||
|
{
|
||||||
|
struct morse_trig_data *morse_data = led_cdev->trigger_data;
|
||||||
|
|
||||||
|
if (!morse_data)
|
||||||
|
return;
|
||||||
|
|
||||||
|
led_device_remove_file(led_cdev, message);
|
||||||
|
led_device_remove_file(led_cdev, delay);
|
||||||
|
|
||||||
|
del_timer_sync(&morse_data->timer);
|
||||||
|
if (morse_data->msg)
|
||||||
|
kfree(morse_data->msg);
|
||||||
|
|
||||||
|
kfree(morse_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct led_trigger morse_led_trigger = {
|
||||||
|
.name = "morse",
|
||||||
|
.activate = morse_trig_activate,
|
||||||
|
.deactivate = morse_trig_deactivate,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init morse_trig_init(void)
|
||||||
|
{
|
||||||
|
return led_trigger_register(&morse_led_trigger);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit morse_trig_exit(void)
|
||||||
|
{
|
||||||
|
led_trigger_unregister(&morse_led_trigger);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(morse_trig_init);
|
||||||
|
module_exit(morse_trig_exit);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Gabor Juhos <juhosg at openwrt.org>");
|
||||||
|
MODULE_DESCRIPTION("Morse LED trigger");
|
||||||
|
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,451 @@
|
||||||
|
/*
|
||||||
|
* LED Kernel Netdev Trigger
|
||||||
|
*
|
||||||
|
* Toggles the LED to reflect the link and traffic state of a named net device
|
||||||
|
*
|
||||||
|
* Copyright 2007 Oliver Jowett <oliver@opencloud.com>
|
||||||
|
*
|
||||||
|
* Derived from ledtrig-timer.c which is:
|
||||||
|
* Copyright 2005-2006 Openedhand Ltd.
|
||||||
|
* Author: Richard Purdie <rpurdie@openedhand.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/jiffies.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/sysdev.h>
|
||||||
|
#include <linux/netdevice.h>
|
||||||
|
#include <linux/timer.h>
|
||||||
|
#include <linux/ctype.h>
|
||||||
|
#include <linux/leds.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
|
||||||
|
#include <net/net_namespace.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "leds.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Configurable sysfs attributes:
|
||||||
|
*
|
||||||
|
* device_name - network device name to monitor
|
||||||
|
*
|
||||||
|
* interval - duration of LED blink, in milliseconds
|
||||||
|
*
|
||||||
|
* mode - either "none" (LED is off) or a space separated list of one or more of:
|
||||||
|
* link: LED's normal state reflects whether the link is up (has carrier) or not
|
||||||
|
* tx: LED blinks on transmitted data
|
||||||
|
* rx: LED blinks on receive data
|
||||||
|
*
|
||||||
|
* Some suggestions:
|
||||||
|
*
|
||||||
|
* Simple link status LED:
|
||||||
|
* $ echo netdev >someled/trigger
|
||||||
|
* $ echo eth0 >someled/device_name
|
||||||
|
* $ echo link >someled/mode
|
||||||
|
*
|
||||||
|
* Ethernet-style link/activity LED:
|
||||||
|
* $ echo netdev >someled/trigger
|
||||||
|
* $ echo eth0 >someled/device_name
|
||||||
|
* $ echo "link tx rx" >someled/mode
|
||||||
|
*
|
||||||
|
* Modem-style tx/rx LEDs:
|
||||||
|
* $ echo netdev >led1/trigger
|
||||||
|
* $ echo ppp0 >led1/device_name
|
||||||
|
* $ echo tx >led1/mode
|
||||||
|
* $ echo netdev >led2/trigger
|
||||||
|
* $ echo ppp0 >led2/device_name
|
||||||
|
* $ echo rx >led2/mode
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define MODE_LINK 1
|
||||||
|
#define MODE_TX 2
|
||||||
|
#define MODE_RX 4
|
||||||
|
|
||||||
|
struct led_netdev_data {
|
||||||
|
rwlock_t lock;
|
||||||
|
|
||||||
|
struct timer_list timer;
|
||||||
|
struct notifier_block notifier;
|
||||||
|
|
||||||
|
struct led_classdev *led_cdev;
|
||||||
|
struct net_device *net_dev;
|
||||||
|
|
||||||
|
char device_name[IFNAMSIZ];
|
||||||
|
unsigned interval;
|
||||||
|
unsigned mode;
|
||||||
|
unsigned link_up;
|
||||||
|
unsigned last_activity;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void set_baseline_state(struct led_netdev_data *trigger_data)
|
||||||
|
{
|
||||||
|
if ((trigger_data->mode & MODE_LINK) != 0 && trigger_data->link_up)
|
||||||
|
led_set_brightness(trigger_data->led_cdev, LED_FULL);
|
||||||
|
else
|
||||||
|
led_set_brightness(trigger_data->led_cdev, LED_OFF);
|
||||||
|
|
||||||
|
if ((trigger_data->mode & (MODE_TX | MODE_RX)) != 0 && trigger_data->link_up)
|
||||||
|
mod_timer(&trigger_data->timer, jiffies + trigger_data->interval);
|
||||||
|
else
|
||||||
|
del_timer(&trigger_data->timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t led_device_name_show(struct device *dev,
|
||||||
|
struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||||
|
struct led_netdev_data *trigger_data = led_cdev->trigger_data;
|
||||||
|
|
||||||
|
read_lock(&trigger_data->lock);
|
||||||
|
sprintf(buf, "%s\n", trigger_data->device_name);
|
||||||
|
read_unlock(&trigger_data->lock);
|
||||||
|
|
||||||
|
return strlen(buf) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
|
||||||
|
extern struct net init_net;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static ssize_t led_device_name_store(struct device *dev,
|
||||||
|
struct device_attribute *attr, const char *buf, size_t size)
|
||||||
|
{
|
||||||
|
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||||
|
struct led_netdev_data *trigger_data = led_cdev->trigger_data;
|
||||||
|
|
||||||
|
if (size < 0 || size >= IFNAMSIZ)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
write_lock(&trigger_data->lock);
|
||||||
|
|
||||||
|
strcpy(trigger_data->device_name, buf);
|
||||||
|
if (size > 0 && trigger_data->device_name[size-1] == '\n')
|
||||||
|
trigger_data->device_name[size-1] = 0;
|
||||||
|
|
||||||
|
if (trigger_data->device_name[0] != 0) {
|
||||||
|
/* check for existing device to update from */
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
|
||||||
|
trigger_data->net_dev = dev_get_by_name(&init_net, trigger_data->device_name);
|
||||||
|
#else
|
||||||
|
trigger_data->net_dev = dev_get_by_name(trigger_data->device_name);
|
||||||
|
#endif
|
||||||
|
if (trigger_data->net_dev != NULL)
|
||||||
|
trigger_data->link_up = (dev_get_flags(trigger_data->net_dev) & IFF_LOWER_UP) != 0;
|
||||||
|
set_baseline_state(trigger_data); /* updates LEDs, may start timers */
|
||||||
|
}
|
||||||
|
|
||||||
|
write_unlock(&trigger_data->lock);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEVICE_ATTR(device_name, 0644, led_device_name_show, led_device_name_store);
|
||||||
|
|
||||||
|
static ssize_t led_mode_show(struct device *dev,
|
||||||
|
struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||||
|
struct led_netdev_data *trigger_data = led_cdev->trigger_data;
|
||||||
|
|
||||||
|
read_lock(&trigger_data->lock);
|
||||||
|
|
||||||
|
if (trigger_data->mode == 0) {
|
||||||
|
strcpy(buf, "none\n");
|
||||||
|
} else {
|
||||||
|
if (trigger_data->mode & MODE_LINK)
|
||||||
|
strcat(buf, "link ");
|
||||||
|
if (trigger_data->mode & MODE_TX)
|
||||||
|
strcat(buf, "tx ");
|
||||||
|
if (trigger_data->mode & MODE_RX)
|
||||||
|
strcat(buf, "rx ");
|
||||||
|
strcat(buf, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
read_unlock(&trigger_data->lock);
|
||||||
|
|
||||||
|
return strlen(buf)+1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t led_mode_store(struct device *dev,
|
||||||
|
struct device_attribute *attr, const char *buf, size_t size)
|
||||||
|
{
|
||||||
|
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||||
|
struct led_netdev_data *trigger_data = led_cdev->trigger_data;
|
||||||
|
char copybuf[1024];
|
||||||
|
int new_mode = -1;
|
||||||
|
char *p, *token;
|
||||||
|
|
||||||
|
/* take a copy since we don't want to trash the inbound buffer when using strsep */
|
||||||
|
strncpy(copybuf, buf, sizeof(copybuf));
|
||||||
|
copybuf[1023] = 0;
|
||||||
|
p = copybuf;
|
||||||
|
|
||||||
|
while ((token = strsep(&p, " \t\n")) != NULL) {
|
||||||
|
if (!*token)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (new_mode == -1)
|
||||||
|
new_mode = 0;
|
||||||
|
|
||||||
|
if (!strcmp(token, "none"))
|
||||||
|
new_mode = 0;
|
||||||
|
else if (!strcmp(token, "tx"))
|
||||||
|
new_mode |= MODE_TX;
|
||||||
|
else if (!strcmp(token, "rx"))
|
||||||
|
new_mode |= MODE_RX;
|
||||||
|
else if (!strcmp(token, "link"))
|
||||||
|
new_mode |= MODE_LINK;
|
||||||
|
else
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_mode == -1)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
write_lock(&trigger_data->lock);
|
||||||
|
trigger_data->mode = new_mode;
|
||||||
|
set_baseline_state(trigger_data);
|
||||||
|
write_unlock(&trigger_data->lock);
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEVICE_ATTR(mode, 0644, led_mode_show, led_mode_store);
|
||||||
|
|
||||||
|
static ssize_t led_interval_show(struct device *dev,
|
||||||
|
struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||||
|
struct led_netdev_data *trigger_data = led_cdev->trigger_data;
|
||||||
|
|
||||||
|
read_lock(&trigger_data->lock);
|
||||||
|
sprintf(buf, "%u\n", jiffies_to_msecs(trigger_data->interval));
|
||||||
|
read_unlock(&trigger_data->lock);
|
||||||
|
|
||||||
|
return strlen(buf) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t led_interval_store(struct device *dev,
|
||||||
|
struct device_attribute *attr, const char *buf, size_t size)
|
||||||
|
{
|
||||||
|
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||||
|
struct led_netdev_data *trigger_data = led_cdev->trigger_data;
|
||||||
|
int ret = -EINVAL;
|
||||||
|
char *after;
|
||||||
|
unsigned long value = simple_strtoul(buf, &after, 10);
|
||||||
|
size_t count = after - buf;
|
||||||
|
|
||||||
|
if (*after && isspace(*after))
|
||||||
|
count++;
|
||||||
|
|
||||||
|
/* impose some basic bounds on the timer interval */
|
||||||
|
if (count == size && value >= 5 && value <= 10000) {
|
||||||
|
write_lock(&trigger_data->lock);
|
||||||
|
trigger_data->interval = msecs_to_jiffies(value);
|
||||||
|
set_baseline_state(trigger_data); // resets timer
|
||||||
|
write_unlock(&trigger_data->lock);
|
||||||
|
ret = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEVICE_ATTR(interval, 0644, led_interval_show, led_interval_store);
|
||||||
|
|
||||||
|
static int netdev_trig_notify(struct notifier_block *nb,
|
||||||
|
unsigned long evt,
|
||||||
|
void *dv)
|
||||||
|
{
|
||||||
|
struct net_device *dev = dv;
|
||||||
|
struct led_netdev_data *trigger_data = container_of(nb, struct led_netdev_data, notifier);
|
||||||
|
|
||||||
|
if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER)
|
||||||
|
return NOTIFY_DONE;
|
||||||
|
|
||||||
|
write_lock(&trigger_data->lock);
|
||||||
|
|
||||||
|
if (strcmp(dev->name, trigger_data->device_name))
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
if (evt == NETDEV_REGISTER) {
|
||||||
|
if (trigger_data->net_dev != NULL)
|
||||||
|
dev_put(trigger_data->net_dev);
|
||||||
|
dev_hold(dev);
|
||||||
|
trigger_data->net_dev = dev;
|
||||||
|
trigger_data->link_up = 0;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (evt == NETDEV_UNREGISTER && trigger_data->net_dev != NULL) {
|
||||||
|
dev_put(trigger_data->net_dev);
|
||||||
|
trigger_data->net_dev = NULL;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* UP / DOWN / CHANGE */
|
||||||
|
|
||||||
|
trigger_data->link_up = (evt != NETDEV_DOWN && netif_carrier_ok(dev));
|
||||||
|
set_baseline_state(trigger_data);
|
||||||
|
|
||||||
|
done:
|
||||||
|
write_unlock(&trigger_data->lock);
|
||||||
|
return NOTIFY_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* here's the real work! */
|
||||||
|
static void netdev_trig_timer(unsigned long arg)
|
||||||
|
{
|
||||||
|
struct led_netdev_data *trigger_data = (struct led_netdev_data *)arg;
|
||||||
|
struct net_device_stats *dev_stats;
|
||||||
|
unsigned new_activity;
|
||||||
|
|
||||||
|
write_lock(&trigger_data->lock);
|
||||||
|
|
||||||
|
if (!trigger_data->link_up || !trigger_data->net_dev || (trigger_data->mode & (MODE_TX | MODE_RX)) == 0) {
|
||||||
|
/* we don't need to do timer work, just reflect link state. */
|
||||||
|
led_set_brightness(trigger_data->led_cdev, ((trigger_data->mode & MODE_LINK) != 0 && trigger_data->link_up) ? LED_FULL : LED_OFF);
|
||||||
|
goto no_restart;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_stats = trigger_data->net_dev->get_stats(trigger_data->net_dev);
|
||||||
|
new_activity =
|
||||||
|
((trigger_data->mode & MODE_TX) ? dev_stats->tx_packets : 0) +
|
||||||
|
((trigger_data->mode & MODE_RX) ? dev_stats->rx_packets : 0);
|
||||||
|
|
||||||
|
if (trigger_data->mode & MODE_LINK) {
|
||||||
|
/* base state is ON (link present) */
|
||||||
|
/* if there's no link, we don't get this far and the LED is off */
|
||||||
|
|
||||||
|
/* OFF -> ON always */
|
||||||
|
/* ON -> OFF on activity */
|
||||||
|
if (trigger_data->led_cdev->brightness == LED_OFF) {
|
||||||
|
led_set_brightness(trigger_data->led_cdev, LED_FULL);
|
||||||
|
} else if (trigger_data->last_activity != new_activity) {
|
||||||
|
led_set_brightness(trigger_data->led_cdev, LED_OFF);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* base state is OFF */
|
||||||
|
/* ON -> OFF always */
|
||||||
|
/* OFF -> ON on activity */
|
||||||
|
if (trigger_data->led_cdev->brightness == LED_FULL) {
|
||||||
|
led_set_brightness(trigger_data->led_cdev, LED_OFF);
|
||||||
|
} else if (trigger_data->last_activity != new_activity) {
|
||||||
|
led_set_brightness(trigger_data->led_cdev, LED_FULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trigger_data->last_activity = new_activity;
|
||||||
|
mod_timer(&trigger_data->timer, jiffies + trigger_data->interval);
|
||||||
|
|
||||||
|
no_restart:
|
||||||
|
write_unlock(&trigger_data->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void netdev_trig_activate(struct led_classdev *led_cdev)
|
||||||
|
{
|
||||||
|
struct led_netdev_data *trigger_data;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
trigger_data = kzalloc(sizeof(struct led_netdev_data), GFP_KERNEL);
|
||||||
|
if (!trigger_data)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rwlock_init(&trigger_data->lock);
|
||||||
|
|
||||||
|
trigger_data->notifier.notifier_call = netdev_trig_notify;
|
||||||
|
trigger_data->notifier.priority = 10;
|
||||||
|
|
||||||
|
setup_timer(&trigger_data->timer, netdev_trig_timer, (unsigned long) trigger_data);
|
||||||
|
|
||||||
|
trigger_data->led_cdev = led_cdev;
|
||||||
|
trigger_data->net_dev = NULL;
|
||||||
|
trigger_data->device_name[0] = 0;
|
||||||
|
|
||||||
|
trigger_data->mode = 0;
|
||||||
|
trigger_data->interval = msecs_to_jiffies(50);
|
||||||
|
trigger_data->link_up = 0;
|
||||||
|
trigger_data->last_activity = 0;
|
||||||
|
|
||||||
|
led_cdev->trigger_data = trigger_data;
|
||||||
|
|
||||||
|
rc = device_create_file(led_cdev->dev, &dev_attr_device_name);
|
||||||
|
if (rc)
|
||||||
|
goto err_out;
|
||||||
|
rc = device_create_file(led_cdev->dev, &dev_attr_mode);
|
||||||
|
if (rc)
|
||||||
|
goto err_out_device_name;
|
||||||
|
rc = device_create_file(led_cdev->dev, &dev_attr_interval);
|
||||||
|
if (rc)
|
||||||
|
goto err_out_mode;
|
||||||
|
|
||||||
|
register_netdevice_notifier(&trigger_data->notifier);
|
||||||
|
return;
|
||||||
|
|
||||||
|
err_out_mode:
|
||||||
|
device_remove_file(led_cdev->dev, &dev_attr_mode);
|
||||||
|
err_out_device_name:
|
||||||
|
device_remove_file(led_cdev->dev, &dev_attr_device_name);
|
||||||
|
err_out:
|
||||||
|
led_cdev->trigger_data = NULL;
|
||||||
|
kfree(trigger_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void netdev_trig_deactivate(struct led_classdev *led_cdev)
|
||||||
|
{
|
||||||
|
struct led_netdev_data *trigger_data = led_cdev->trigger_data;
|
||||||
|
|
||||||
|
if (trigger_data) {
|
||||||
|
unregister_netdevice_notifier(&trigger_data->notifier);
|
||||||
|
|
||||||
|
device_remove_file(led_cdev->dev, &dev_attr_device_name);
|
||||||
|
device_remove_file(led_cdev->dev, &dev_attr_mode);
|
||||||
|
device_remove_file(led_cdev->dev, &dev_attr_interval);
|
||||||
|
|
||||||
|
write_lock(&trigger_data->lock);
|
||||||
|
|
||||||
|
if (trigger_data->net_dev) {
|
||||||
|
dev_put(trigger_data->net_dev);
|
||||||
|
trigger_data->net_dev = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_unlock(&trigger_data->lock);
|
||||||
|
|
||||||
|
del_timer_sync(&trigger_data->timer);
|
||||||
|
|
||||||
|
kfree(trigger_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct led_trigger netdev_led_trigger = {
|
||||||
|
.name = "netdev",
|
||||||
|
.activate = netdev_trig_activate,
|
||||||
|
.deactivate = netdev_trig_deactivate,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init netdev_trig_init(void)
|
||||||
|
{
|
||||||
|
return led_trigger_register(&netdev_led_trigger);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit netdev_trig_exit(void)
|
||||||
|
{
|
||||||
|
led_trigger_unregister(&netdev_led_trigger);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(netdev_trig_init);
|
||||||
|
module_exit(netdev_trig_exit);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Oliver Jowett <oliver@opencloud.com>");
|
||||||
|
MODULE_DESCRIPTION("Netdev LED trigger");
|
||||||
|
MODULE_LICENSE("GPL");
|
170
target/linux/generic-2.6/files-2.6.27/drivers/net/phy/adm6996.c
Normal file
170
target/linux/generic-2.6/files-2.6.27/drivers/net/phy/adm6996.c
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
/*
|
||||||
|
* ADM6996 switch driver
|
||||||
|
*
|
||||||
|
* Copyright (c) 2008 Felix Fietkau <nbd@openwrt.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License v2 as published by the
|
||||||
|
* Free Software Foundation
|
||||||
|
*/
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <linux/errno.h>
|
||||||
|
#include <linux/unistd.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/netdevice.h>
|
||||||
|
#include <linux/etherdevice.h>
|
||||||
|
#include <linux/skbuff.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
#include <linux/mm.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/mii.h>
|
||||||
|
#include <linux/ethtool.h>
|
||||||
|
#include <linux/phy.h>
|
||||||
|
|
||||||
|
#include <asm/io.h>
|
||||||
|
#include <asm/irq.h>
|
||||||
|
#include <asm/uaccess.h>
|
||||||
|
#include "adm6996.h"
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("Infineon ADM6996 Switch");
|
||||||
|
MODULE_AUTHOR("Felix Fietkau");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
|
struct adm6996_priv {
|
||||||
|
/* use abstraction for regops, we want to add gpio support in the future */
|
||||||
|
u16 (*read)(struct phy_device *phydev, enum admreg reg);
|
||||||
|
void (*write)(struct phy_device *phydev, enum admreg reg, u16 val);
|
||||||
|
};
|
||||||
|
|
||||||
|
#define to_adm(_phy) ((struct adm6996_priv *) (_phy)->priv)
|
||||||
|
|
||||||
|
|
||||||
|
static inline u16
|
||||||
|
r16(struct phy_device *pdev, enum admreg reg)
|
||||||
|
{
|
||||||
|
return to_adm(pdev)->read(pdev, reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
w16(struct phy_device *pdev, enum admreg reg, u16 val)
|
||||||
|
{
|
||||||
|
to_adm(pdev)->write(pdev, reg, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static u16
|
||||||
|
adm6996_read_mii_reg(struct phy_device *phydev, enum admreg reg)
|
||||||
|
{
|
||||||
|
return phydev->bus->read(phydev->bus, PHYADDR(reg));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
adm6996_write_mii_reg(struct phy_device *phydev, enum admreg reg, u16 val)
|
||||||
|
{
|
||||||
|
phydev->bus->write(phydev->bus, PHYADDR(reg), val);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int adm6996_config_init(struct phy_device *pdev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
printk("%s: ADM6996 PHY driver attached.\n", pdev->attached_dev->name);
|
||||||
|
pdev->supported = ADVERTISED_100baseT_Full;
|
||||||
|
pdev->advertising = ADVERTISED_100baseT_Full;
|
||||||
|
|
||||||
|
/* initialize port and vlan settings */
|
||||||
|
for (i = 0; i < ADM_PHY_PORTS; i++) {
|
||||||
|
w16(pdev, adm_portcfg[i], ADM_PORTCFG_INIT |
|
||||||
|
ADM_PORTCFG_PVID((i == ADM_WAN_PORT) ? 1 : 0));
|
||||||
|
}
|
||||||
|
w16(pdev, adm_portcfg[5], ADM_PORTCFG_CPU);
|
||||||
|
|
||||||
|
/* reset all ports */
|
||||||
|
for (i = 0; i < ADM_PHY_PORTS; i++) {
|
||||||
|
w16(pdev, ADM_PHY_PORT(i), ADM_PHYCFG_INIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int adm6996_read_status(struct phy_device *phydev)
|
||||||
|
{
|
||||||
|
phydev->speed = SPEED_100;
|
||||||
|
phydev->duplex = DUPLEX_FULL;
|
||||||
|
phydev->state = PHY_UP;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int adm6996_config_aneg(struct phy_device *phydev)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int adm6996_probe(struct phy_device *pdev)
|
||||||
|
{
|
||||||
|
struct adm6996_priv *priv;
|
||||||
|
|
||||||
|
priv = kzalloc(sizeof(struct adm6996_priv), GFP_KERNEL);
|
||||||
|
if (priv == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
priv->read = adm6996_read_mii_reg;
|
||||||
|
priv->write = adm6996_write_mii_reg;
|
||||||
|
pdev->priv = priv;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void adm6996_remove(struct phy_device *pdev)
|
||||||
|
{
|
||||||
|
kfree(pdev->priv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool adm6996_detect(struct mii_bus *bus, int addr)
|
||||||
|
{
|
||||||
|
u16 reg;
|
||||||
|
|
||||||
|
/* we only attach to phy id 0 */
|
||||||
|
if (addr != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* look for the switch on the bus */
|
||||||
|
reg = bus->read(bus, PHYADDR(ADM_SIG0)) & ADM_SIG0_MASK;
|
||||||
|
if (reg != ADM_SIG0_VAL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
reg = bus->read(bus, PHYADDR(ADM_SIG1)) & ADM_SIG1_MASK;
|
||||||
|
if (reg != ADM_SIG1_VAL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct phy_driver adm6996_driver = {
|
||||||
|
.name = "Infineon ADM6996",
|
||||||
|
.features = PHY_BASIC_FEATURES,
|
||||||
|
.detect = adm6996_detect,
|
||||||
|
.probe = adm6996_probe,
|
||||||
|
.remove = adm6996_remove,
|
||||||
|
.config_init = &adm6996_config_init,
|
||||||
|
.config_aneg = &adm6996_config_aneg,
|
||||||
|
.read_status = &adm6996_read_status,
|
||||||
|
.driver = { .owner = THIS_MODULE,},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init adm6996_init(void)
|
||||||
|
{
|
||||||
|
return phy_driver_register(&adm6996_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit adm6996_exit(void)
|
||||||
|
{
|
||||||
|
phy_driver_unregister(&adm6996_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(adm6996_init);
|
||||||
|
module_exit(adm6996_exit);
|
105
target/linux/generic-2.6/files-2.6.27/drivers/net/phy/adm6996.h
Normal file
105
target/linux/generic-2.6/files-2.6.27/drivers/net/phy/adm6996.h
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
/*
|
||||||
|
* ADM6996 switch driver
|
||||||
|
*
|
||||||
|
* Copyright (c) 2008 Felix Fietkau <nbd@openwrt.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License v2 as published by the
|
||||||
|
* Free Software Foundation
|
||||||
|
*/
|
||||||
|
#ifndef __ADM6996_H
|
||||||
|
#define __ADM6996_H
|
||||||
|
|
||||||
|
#define ADM_PHY_PORTS 5
|
||||||
|
#define ADM_CPU_PORT 5
|
||||||
|
#define ADM_WAN_PORT 0 /* FIXME: dynamic ? */
|
||||||
|
|
||||||
|
enum admreg {
|
||||||
|
ADM_EEPROM_BASE = 0x0,
|
||||||
|
ADM_P0_CFG = ADM_EEPROM_BASE + 1,
|
||||||
|
ADM_P1_CFG = ADM_EEPROM_BASE + 3,
|
||||||
|
ADM_P2_CFG = ADM_EEPROM_BASE + 5,
|
||||||
|
ADM_P3_CFG = ADM_EEPROM_BASE + 7,
|
||||||
|
ADM_P4_CFG = ADM_EEPROM_BASE + 8,
|
||||||
|
ADM_P5_CFG = ADM_EEPROM_BASE + 9,
|
||||||
|
ADM_EEPROM_EXT_BASE = 0x40,
|
||||||
|
ADM_COUNTER_BASE = 0xa0,
|
||||||
|
ADM_SIG0 = ADM_COUNTER_BASE + 0,
|
||||||
|
ADM_SIG1 = ADM_COUNTER_BASE + 1,
|
||||||
|
ADM_PHY_BASE = 0x200,
|
||||||
|
#define ADM_PHY_PORT(n) (ADM_PHY_BASE + (0x20 * n))
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Chip identification patterns */
|
||||||
|
#define ADM_SIG0_MASK 0xfff0
|
||||||
|
#define ADM_SIG0_VAL 0x1020
|
||||||
|
#define ADM_SIG1_MASK 0xffff
|
||||||
|
#define ADM_SIG1_VAL 0x0007
|
||||||
|
|
||||||
|
enum {
|
||||||
|
ADM_PHYCFG_COLTST = (1 << 7), /* Enable collision test */
|
||||||
|
ADM_PHYCFG_DPLX = (1 << 8), /* Enable full duplex */
|
||||||
|
ADM_PHYCFG_ANEN_RST = (1 << 9), /* Restart auto negotiation (self clear) */
|
||||||
|
ADM_PHYCFG_ISO = (1 << 10), /* Isolate PHY */
|
||||||
|
ADM_PHYCFG_PDN = (1 << 11), /* Power down PHY */
|
||||||
|
ADM_PHYCFG_ANEN = (1 << 12), /* Enable auto negotiation */
|
||||||
|
ADM_PHYCFG_SPEED_100 = (1 << 13), /* Enable 100 Mbit/s */
|
||||||
|
ADM_PHYCFG_LPBK = (1 << 14), /* Enable loopback operation */
|
||||||
|
ADM_PHYCFG_RST = (1 << 15), /* Reset the port (self clear) */
|
||||||
|
ADM_PHYCFG_INIT = (
|
||||||
|
ADM_PHYCFG_RST |
|
||||||
|
ADM_PHYCFG_SPEED_100 |
|
||||||
|
ADM_PHYCFG_ANEN |
|
||||||
|
ADM_PHYCFG_ANEN_RST
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
ADM_PORTCFG_FC = (1 << 0), /* Enable 802.x flow control */
|
||||||
|
ADM_PORTCFG_AN = (1 << 1), /* Enable auto-negotiation */
|
||||||
|
ADM_PORTCFG_SPEED_100 = (1 << 2), /* Enable 100 Mbit/s */
|
||||||
|
ADM_PORTCFG_DPLX = (1 << 3), /* Enable full duplex */
|
||||||
|
ADM_PORTCFG_OT = (1 << 4), /* Output tagged packets */
|
||||||
|
ADM_PORTCFG_PD = (1 << 5), /* Port disable */
|
||||||
|
ADM_PORTCFG_TV_PRIO = (1 << 6), /* 0 = VLAN based priority
|
||||||
|
* 1 = TOS based priority */
|
||||||
|
ADM_PORTCFG_PPE = (1 << 7), /* Port based priority enable */
|
||||||
|
ADM_PORTCFG_PP_S = (1 << 8), /* Port based priority, 2 bits */
|
||||||
|
ADM_PORTCFG_PVID_BASE = (1 << 10), /* Primary VLAN id, 4 bits */
|
||||||
|
ADM_PORTCFG_FSE = (1 << 14), /* Fx select enable */
|
||||||
|
ADM_PORTCFG_CAM = (1 << 15), /* Crossover Auto MDIX */
|
||||||
|
|
||||||
|
ADM_PORTCFG_INIT = (
|
||||||
|
ADM_PORTCFG_FC |
|
||||||
|
ADM_PORTCFG_AN |
|
||||||
|
ADM_PORTCFG_SPEED_100 |
|
||||||
|
ADM_PORTCFG_DPLX |
|
||||||
|
ADM_PORTCFG_CAM
|
||||||
|
),
|
||||||
|
ADM_PORTCFG_CPU = (
|
||||||
|
ADM_PORTCFG_FC |
|
||||||
|
ADM_PORTCFG_SPEED_100 |
|
||||||
|
ADM_PORTCFG_OT |
|
||||||
|
ADM_PORTCFG_DPLX
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
#define ADM_PORTCFG_PPID(N) ((n & 0x3) << 8)
|
||||||
|
#define ADM_PORTCFG_PVID(n) ((n & 0xf) << 10)
|
||||||
|
|
||||||
|
static const u8 adm_portcfg[] = {
|
||||||
|
[0] = ADM_P0_CFG,
|
||||||
|
[1] = ADM_P1_CFG,
|
||||||
|
[2] = ADM_P2_CFG,
|
||||||
|
[3] = ADM_P3_CFG,
|
||||||
|
[4] = ADM_P4_CFG,
|
||||||
|
[5] = ADM_P5_CFG,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Split the register address in phy id and register
|
||||||
|
* it will get combined again by the mdio bus op
|
||||||
|
*/
|
||||||
|
#define PHYADDR(_reg) ((_reg >> 5) & 0xff), (_reg & 0x1f)
|
||||||
|
|
||||||
|
#endif
|
474
target/linux/generic-2.6/files-2.6.27/drivers/net/phy/mvswitch.c
Normal file
474
target/linux/generic-2.6/files-2.6.27/drivers/net/phy/mvswitch.c
Normal file
|
@ -0,0 +1,474 @@
|
||||||
|
/*
|
||||||
|
* Marvell 88E6060 switch driver
|
||||||
|
* Copyright (c) 2008 Felix Fietkau <nbd@openwrt.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License v2 as published by the
|
||||||
|
* Free Software Foundation
|
||||||
|
*/
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <linux/errno.h>
|
||||||
|
#include <linux/unistd.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/netdevice.h>
|
||||||
|
#include <linux/etherdevice.h>
|
||||||
|
#include <linux/skbuff.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
#include <linux/mm.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/mii.h>
|
||||||
|
#include <linux/ethtool.h>
|
||||||
|
#include <linux/phy.h>
|
||||||
|
#include <linux/if_vlan.h>
|
||||||
|
|
||||||
|
#include <asm/io.h>
|
||||||
|
#include <asm/irq.h>
|
||||||
|
#include <asm/uaccess.h>
|
||||||
|
#include "mvswitch.h"
|
||||||
|
|
||||||
|
/* Undefine this to use trailer mode instead.
|
||||||
|
* I don't know if header mode works with all chips */
|
||||||
|
#define HEADER_MODE 1
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("Marvell 88E6060 Switch driver");
|
||||||
|
MODULE_AUTHOR("Felix Fietkau");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
|
struct mvswitch_priv {
|
||||||
|
/* the driver's tx function */
|
||||||
|
int (*hardstart)(struct sk_buff *skb, struct net_device *dev);
|
||||||
|
struct vlan_group *grp;
|
||||||
|
u8 vlans[16];
|
||||||
|
};
|
||||||
|
|
||||||
|
#define to_mvsw(_phy) ((struct mvswitch_priv *) (_phy)->priv)
|
||||||
|
|
||||||
|
static inline u16
|
||||||
|
r16(struct phy_device *phydev, int addr, int reg)
|
||||||
|
{
|
||||||
|
return phydev->bus->read(phydev->bus, addr, reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
w16(struct phy_device *phydev, int addr, int reg, u16 val)
|
||||||
|
{
|
||||||
|
phydev->bus->write(phydev->bus, addr, reg, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
mvswitch_mangle_tx(struct sk_buff *skb, struct net_device *dev)
|
||||||
|
{
|
||||||
|
struct mvswitch_priv *priv;
|
||||||
|
char *buf = NULL;
|
||||||
|
u16 vid;
|
||||||
|
|
||||||
|
priv = dev->phy_ptr;
|
||||||
|
if (unlikely(!priv))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (unlikely(skb->len < 16))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
#ifdef HEADER_MODE
|
||||||
|
if (__vlan_hwaccel_get_tag(skb, &vid))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (skb_cloned(skb) || (skb->len <= 62) || (skb_headroom(skb) < MV_HEADER_SIZE)) {
|
||||||
|
if (pskb_expand_head(skb, MV_HEADER_SIZE, (skb->len < 62 ? 62 - skb->len : 0), GFP_ATOMIC))
|
||||||
|
goto error_expand;
|
||||||
|
if (skb->len < 62)
|
||||||
|
skb->len = 62;
|
||||||
|
}
|
||||||
|
buf = skb_push(skb, MV_HEADER_SIZE);
|
||||||
|
#else
|
||||||
|
if (__vlan_get_tag(skb, &vid))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (unlikely((vid > 15 || !priv->vlans[vid])))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (skb->len <= 64) {
|
||||||
|
if (pskb_expand_head(skb, 0, 64 + MV_TRAILER_SIZE - skb->len, GFP_ATOMIC))
|
||||||
|
goto error_expand;
|
||||||
|
|
||||||
|
buf = skb->data + 64;
|
||||||
|
skb->len = 64 + MV_TRAILER_SIZE;
|
||||||
|
} else {
|
||||||
|
if (skb_cloned(skb) || unlikely(skb_tailroom(skb) < 4)) {
|
||||||
|
if (pskb_expand_head(skb, 0, 4, GFP_ATOMIC))
|
||||||
|
goto error_expand;
|
||||||
|
}
|
||||||
|
buf = skb_put(skb, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* move the ethernet header 4 bytes forward, overwriting the vlan tag */
|
||||||
|
memmove(skb->data + 4, skb->data, 12);
|
||||||
|
skb->data += 4;
|
||||||
|
skb->len -= 4;
|
||||||
|
skb->mac_header += 4;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!buf)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef HEADER_MODE
|
||||||
|
/* prepend the tag */
|
||||||
|
*((__be16 *) buf) = cpu_to_be16(
|
||||||
|
((vid << MV_HEADER_VLAN_S) & MV_HEADER_VLAN_M) |
|
||||||
|
((priv->vlans[vid] << MV_HEADER_PORTS_S) & MV_HEADER_PORTS_M)
|
||||||
|
);
|
||||||
|
#else
|
||||||
|
/* append the tag */
|
||||||
|
*((__be32 *) buf) = cpu_to_be32((
|
||||||
|
(MV_TRAILER_OVERRIDE << MV_TRAILER_FLAGS_S) |
|
||||||
|
((priv->vlans[vid] & MV_TRAILER_PORTS_M) << MV_TRAILER_PORTS_S)
|
||||||
|
));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return priv->hardstart(skb, dev);
|
||||||
|
|
||||||
|
error_expand:
|
||||||
|
if (net_ratelimit())
|
||||||
|
printk("%s: failed to expand/update skb for the switch\n", dev->name);
|
||||||
|
|
||||||
|
error:
|
||||||
|
/* any errors? drop the packet! */
|
||||||
|
dev_kfree_skb_any(skb);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
mvswitch_mangle_rx(struct sk_buff *skb, int napi)
|
||||||
|
{
|
||||||
|
struct mvswitch_priv *priv;
|
||||||
|
struct net_device *dev;
|
||||||
|
int vlan = -1;
|
||||||
|
unsigned char *buf;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
dev = skb->dev;
|
||||||
|
if (!dev)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
priv = dev->phy_ptr;
|
||||||
|
if (!priv)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (!priv->grp)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
#ifdef HEADER_MODE
|
||||||
|
buf = skb->data;
|
||||||
|
skb_pull(skb, MV_HEADER_SIZE);
|
||||||
|
#else
|
||||||
|
buf = skb->data + skb->len - MV_TRAILER_SIZE;
|
||||||
|
if (buf[0] != 0x80)
|
||||||
|
goto error;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* look for the vlan matching the incoming port */
|
||||||
|
for (i = 0; i < ARRAY_SIZE(priv->vlans); i++) {
|
||||||
|
if ((1 << buf[1]) & priv->vlans[i])
|
||||||
|
vlan = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vlan == -1)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
skb->protocol = eth_type_trans(skb, skb->dev);
|
||||||
|
|
||||||
|
if (napi)
|
||||||
|
return vlan_hwaccel_receive_skb(skb, priv->grp, vlan);
|
||||||
|
else
|
||||||
|
return vlan_hwaccel_rx(skb, priv->grp, vlan);
|
||||||
|
|
||||||
|
error:
|
||||||
|
/* no vlan? eat the packet! */
|
||||||
|
dev_kfree_skb_any(skb);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
mvswitch_netif_rx(struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
return mvswitch_mangle_rx(skb, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
mvswitch_netif_receive_skb(struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
return mvswitch_mangle_rx(skb, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
mvswitch_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
|
||||||
|
{
|
||||||
|
struct mvswitch_priv *priv = dev->phy_ptr;
|
||||||
|
priv->grp = grp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
mvswitch_wait_mask(struct phy_device *pdev, int addr, int reg, u16 mask, u16 val)
|
||||||
|
{
|
||||||
|
int i = 100;
|
||||||
|
u16 r;
|
||||||
|
|
||||||
|
do {
|
||||||
|
r = r16(pdev, addr, reg) & mask;
|
||||||
|
if (r == val)
|
||||||
|
return 0;
|
||||||
|
} while(--i > 0);
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
mvswitch_config_init(struct phy_device *pdev)
|
||||||
|
{
|
||||||
|
struct mvswitch_priv *priv = to_mvsw(pdev);
|
||||||
|
struct net_device *dev = pdev->attached_dev;
|
||||||
|
u8 vlmap = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!dev)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
printk("%s: Marvell 88E6060 PHY driver attached.\n", dev->name);
|
||||||
|
pdev->supported = ADVERTISED_100baseT_Full;
|
||||||
|
pdev->advertising = ADVERTISED_100baseT_Full;
|
||||||
|
dev->phy_ptr = priv;
|
||||||
|
dev->irq = PHY_POLL;
|
||||||
|
|
||||||
|
/* initialize default vlans */
|
||||||
|
for (i = 0; i < MV_PORTS; i++)
|
||||||
|
priv->vlans[(i == MV_WANPORT ? 1 : 0)] |= (1 << i);
|
||||||
|
|
||||||
|
/* before entering reset, disable all ports */
|
||||||
|
for (i = 0; i < MV_PORTS; i++)
|
||||||
|
w16(pdev, MV_PORTREG(CONTROL, i), 0x00);
|
||||||
|
|
||||||
|
msleep(2); /* wait for the status change to settle in */
|
||||||
|
|
||||||
|
/* put the ATU in reset */
|
||||||
|
w16(pdev, MV_SWITCHREG(ATU_CTRL), MV_ATUCTL_RESET);
|
||||||
|
|
||||||
|
i = mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_CTRL), MV_ATUCTL_RESET, 0);
|
||||||
|
if (i < 0) {
|
||||||
|
printk("%s: Timeout waiting for the switch to reset.\n", dev->name);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set the ATU flags */
|
||||||
|
w16(pdev, MV_SWITCHREG(ATU_CTRL),
|
||||||
|
MV_ATUCTL_NO_LEARN |
|
||||||
|
MV_ATUCTL_ATU_1K |
|
||||||
|
MV_ATUCTL_AGETIME(MV_ATUCTL_AGETIME_MIN) /* minimum without disabling ageing */
|
||||||
|
);
|
||||||
|
|
||||||
|
/* initialize the cpu port */
|
||||||
|
w16(pdev, MV_PORTREG(CONTROL, MV_CPUPORT),
|
||||||
|
#ifdef HEADER_MODE
|
||||||
|
MV_PORTCTRL_HEADER |
|
||||||
|
#else
|
||||||
|
MV_PORTCTRL_RXTR |
|
||||||
|
MV_PORTCTRL_TXTR |
|
||||||
|
#endif
|
||||||
|
MV_PORTCTRL_ENABLED
|
||||||
|
);
|
||||||
|
/* wait for the phy change to settle in */
|
||||||
|
msleep(2);
|
||||||
|
for (i = 0; i < MV_PORTS; i++) {
|
||||||
|
u8 pvid = 0;
|
||||||
|
int j;
|
||||||
|
|
||||||
|
vlmap = 0;
|
||||||
|
|
||||||
|
/* look for the matching vlan */
|
||||||
|
for (j = 0; j < ARRAY_SIZE(priv->vlans); j++) {
|
||||||
|
if (priv->vlans[j] & (1 << i)) {
|
||||||
|
vlmap = priv->vlans[j];
|
||||||
|
pvid = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* leave port unconfigured if it's not part of a vlan */
|
||||||
|
if (!vlmap)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* add the cpu port to the allowed destinations list */
|
||||||
|
vlmap |= (1 << MV_CPUPORT);
|
||||||
|
|
||||||
|
/* take port out of its own vlan destination map */
|
||||||
|
vlmap &= ~(1 << i);
|
||||||
|
|
||||||
|
/* apply vlan settings */
|
||||||
|
w16(pdev, MV_PORTREG(VLANMAP, i),
|
||||||
|
MV_PORTVLAN_PORTS(vlmap) |
|
||||||
|
MV_PORTVLAN_ID(i)
|
||||||
|
);
|
||||||
|
|
||||||
|
/* re-enable port */
|
||||||
|
w16(pdev, MV_PORTREG(CONTROL, i),
|
||||||
|
MV_PORTCTRL_ENABLED
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
w16(pdev, MV_PORTREG(VLANMAP, MV_CPUPORT),
|
||||||
|
MV_PORTVLAN_ID(MV_CPUPORT)
|
||||||
|
);
|
||||||
|
|
||||||
|
/* set the port association vector */
|
||||||
|
for (i = 0; i <= MV_PORTS; i++) {
|
||||||
|
w16(pdev, MV_PORTREG(ASSOC, i),
|
||||||
|
MV_PORTASSOC_PORTS(1 << i)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* init switch control */
|
||||||
|
w16(pdev, MV_SWITCHREG(CTRL),
|
||||||
|
MV_SWITCHCTL_MSIZE |
|
||||||
|
MV_SWITCHCTL_DROP
|
||||||
|
);
|
||||||
|
|
||||||
|
/* hook into the tx function */
|
||||||
|
priv->hardstart = dev->hard_start_xmit;
|
||||||
|
pdev->netif_receive_skb = mvswitch_netif_receive_skb;
|
||||||
|
pdev->netif_rx = mvswitch_netif_rx;
|
||||||
|
dev->hard_start_xmit = mvswitch_mangle_tx;
|
||||||
|
dev->vlan_rx_register = mvswitch_vlan_rx_register;
|
||||||
|
#ifdef HEADER_MODE
|
||||||
|
dev->features |= NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_TX;
|
||||||
|
#else
|
||||||
|
dev->features |= NETIF_F_HW_VLAN_RX;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
mvswitch_read_status(struct phy_device *pdev)
|
||||||
|
{
|
||||||
|
pdev->speed = SPEED_100;
|
||||||
|
pdev->duplex = DUPLEX_FULL;
|
||||||
|
pdev->state = PHY_UP;
|
||||||
|
|
||||||
|
/* XXX ugly workaround: we can't force the switch
|
||||||
|
* to gracefully handle hosts moving from one port to another,
|
||||||
|
* so we have to regularly clear the ATU database */
|
||||||
|
|
||||||
|
/* wait for the ATU to become available */
|
||||||
|
mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_OP), MV_ATUOP_INPROGRESS, 0);
|
||||||
|
|
||||||
|
/* flush the ATU */
|
||||||
|
w16(pdev, MV_SWITCHREG(ATU_OP),
|
||||||
|
MV_ATUOP_INPROGRESS |
|
||||||
|
MV_ATUOP_FLUSH_ALL
|
||||||
|
);
|
||||||
|
|
||||||
|
/* wait for operation to complete */
|
||||||
|
mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_OP), MV_ATUOP_INPROGRESS, 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
mvswitch_config_aneg(struct phy_device *phydev)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mvswitch_remove(struct phy_device *pdev)
|
||||||
|
{
|
||||||
|
struct mvswitch_priv *priv = to_mvsw(pdev);
|
||||||
|
struct net_device *dev = pdev->attached_dev;
|
||||||
|
|
||||||
|
/* restore old xmit handler */
|
||||||
|
if (priv->hardstart && dev)
|
||||||
|
dev->hard_start_xmit = priv->hardstart;
|
||||||
|
dev->vlan_rx_register = NULL;
|
||||||
|
dev->vlan_rx_kill_vid = NULL;
|
||||||
|
dev->phy_ptr = NULL;
|
||||||
|
dev->features &= ~NETIF_F_HW_VLAN_RX;
|
||||||
|
kfree(priv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
mvswitch_detect(struct mii_bus *bus, int addr)
|
||||||
|
{
|
||||||
|
u16 reg;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* we attach to phy id 31 to make sure that the late probe works */
|
||||||
|
if (addr != 31)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* look for the switch on the bus */
|
||||||
|
reg = bus->read(bus, MV_PORTREG(IDENT, 0)) & MV_IDENT_MASK;
|
||||||
|
if (reg != MV_IDENT_VALUE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now that we've established that the switch actually exists, let's
|
||||||
|
* get rid of the competition :)
|
||||||
|
*/
|
||||||
|
for (i = 0; i < 31; i++) {
|
||||||
|
if (!bus->phy_map[i])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
device_unregister(&bus->phy_map[i]->dev);
|
||||||
|
kfree(bus->phy_map[i]);
|
||||||
|
bus->phy_map[i] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
mvswitch_probe(struct phy_device *pdev)
|
||||||
|
{
|
||||||
|
struct mvswitch_priv *priv;
|
||||||
|
|
||||||
|
priv = kzalloc(sizeof(struct mvswitch_priv), GFP_KERNEL);
|
||||||
|
if (priv == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
pdev->priv = priv;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct phy_driver mvswitch_driver = {
|
||||||
|
.name = "Marvell 88E6060",
|
||||||
|
.features = PHY_BASIC_FEATURES,
|
||||||
|
.detect = &mvswitch_detect,
|
||||||
|
.probe = &mvswitch_probe,
|
||||||
|
.remove = &mvswitch_remove,
|
||||||
|
.config_init = &mvswitch_config_init,
|
||||||
|
.config_aneg = &mvswitch_config_aneg,
|
||||||
|
.read_status = &mvswitch_read_status,
|
||||||
|
.driver = { .owner = THIS_MODULE,},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init
|
||||||
|
mvswitch_init(void)
|
||||||
|
{
|
||||||
|
return phy_driver_register(&mvswitch_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit
|
||||||
|
mvswitch_exit(void)
|
||||||
|
{
|
||||||
|
phy_driver_unregister(&mvswitch_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(mvswitch_init);
|
||||||
|
module_exit(mvswitch_exit);
|
145
target/linux/generic-2.6/files-2.6.27/drivers/net/phy/mvswitch.h
Normal file
145
target/linux/generic-2.6/files-2.6.27/drivers/net/phy/mvswitch.h
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
/*
|
||||||
|
* Marvell 88E6060 switch driver
|
||||||
|
* Copyright (c) 2008 Felix Fietkau <nbd@openwrt.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License v2 as published by the
|
||||||
|
* Free Software Foundation
|
||||||
|
*/
|
||||||
|
#ifndef __MVSWITCH_H
|
||||||
|
#define __MVSWITCH_H
|
||||||
|
|
||||||
|
#define MV_HEADER_SIZE 2
|
||||||
|
#define MV_HEADER_PORTS_M 0x001f
|
||||||
|
#define MV_HEADER_PORTS_S 0
|
||||||
|
#define MV_HEADER_VLAN_M 0xf000
|
||||||
|
#define MV_HEADER_VLAN_S 12
|
||||||
|
|
||||||
|
#define MV_TRAILER_SIZE 4
|
||||||
|
#define MV_TRAILER_PORTS_M 0x1f
|
||||||
|
#define MV_TRAILER_PORTS_S 16
|
||||||
|
#define MV_TRAILER_FLAGS_S 24
|
||||||
|
#define MV_TRAILER_OVERRIDE 0x80
|
||||||
|
|
||||||
|
|
||||||
|
#define MV_PORTS 5
|
||||||
|
#define MV_WANPORT 4
|
||||||
|
#define MV_CPUPORT 5
|
||||||
|
|
||||||
|
#define MV_BASE 0x10
|
||||||
|
|
||||||
|
#define MV_PHYPORT_BASE (MV_BASE + 0x0)
|
||||||
|
#define MV_PHYPORT(_n) (MV_PHYPORT_BASE + (_n))
|
||||||
|
#define MV_SWITCHPORT_BASE (MV_BASE + 0x8)
|
||||||
|
#define MV_SWITCHPORT(_n) (MV_SWITCHPORT_BASE + (_n))
|
||||||
|
#define MV_SWITCHREGS (MV_BASE + 0xf)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
MV_PHY_CONTROL = 0x00,
|
||||||
|
MV_PHY_STATUS = 0x01,
|
||||||
|
MV_PHY_IDENT0 = 0x02,
|
||||||
|
MV_PHY_IDENT1 = 0x03,
|
||||||
|
MV_PHY_ANEG = 0x04,
|
||||||
|
MV_PHY_LINK_ABILITY = 0x05,
|
||||||
|
MV_PHY_ANEG_EXPAND = 0x06,
|
||||||
|
MV_PHY_XMIT_NEXTP = 0x07,
|
||||||
|
MV_PHY_LINK_NEXTP = 0x08,
|
||||||
|
MV_PHY_CONTROL1 = 0x10,
|
||||||
|
MV_PHY_STATUS1 = 0x11,
|
||||||
|
MV_PHY_INTR_EN = 0x12,
|
||||||
|
MV_PHY_INTR_STATUS = 0x13,
|
||||||
|
MV_PHY_INTR_PORT = 0x14,
|
||||||
|
MV_PHY_RECV_COUNTER = 0x16,
|
||||||
|
MV_PHY_LED_PARALLEL = 0x16,
|
||||||
|
MV_PHY_LED_STREAM = 0x17,
|
||||||
|
MV_PHY_LED_CTRL = 0x18,
|
||||||
|
MV_PHY_LED_OVERRIDE = 0x19,
|
||||||
|
MV_PHY_VCT_CTRL = 0x1a,
|
||||||
|
MV_PHY_VCT_STATUS = 0x1b,
|
||||||
|
MV_PHY_CONTROL2 = 0x1e
|
||||||
|
};
|
||||||
|
#define MV_PHYREG(_type, _port) MV_PHYPORT(_port), MV_PHY_##_type
|
||||||
|
|
||||||
|
enum {
|
||||||
|
MV_PORT_STATUS = 0x00,
|
||||||
|
MV_PORT_IDENT = 0x03,
|
||||||
|
MV_PORT_CONTROL = 0x04,
|
||||||
|
MV_PORT_VLANMAP = 0x06,
|
||||||
|
MV_PORT_ASSOC = 0x0b,
|
||||||
|
MV_PORT_RXCOUNT = 0x10,
|
||||||
|
MV_PORT_TXCOUNT = 0x11,
|
||||||
|
};
|
||||||
|
#define MV_PORTREG(_type, _port) MV_SWITCHPORT(_port), MV_PORT_##_type
|
||||||
|
|
||||||
|
enum {
|
||||||
|
MV_PORTCTRL_BLOCK = (1 << 0),
|
||||||
|
MV_PORTCTRL_LEARN = (2 << 0),
|
||||||
|
MV_PORTCTRL_ENABLED = (3 << 0),
|
||||||
|
MV_PORTCTRL_VLANTUN = (1 << 7), /* Enforce VLANs on packets */
|
||||||
|
MV_PORTCTRL_RXTR = (1 << 8), /* Enable Marvell packet trailer for ingress */
|
||||||
|
MV_PORTCTRL_HEADER = (1 << 11), /* Enable Marvell packet header mode for port */
|
||||||
|
MV_PORTCTRL_TXTR = (1 << 14), /* Enable Marvell packet trailer for egress */
|
||||||
|
MV_PORTCTRL_FORCEFL = (1 << 15), /* force flow control */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MV_PORTVLAN_ID(_n) (((_n) & 0xf) << 12)
|
||||||
|
#define MV_PORTVLAN_PORTS(_n) ((_n) & 0x3f)
|
||||||
|
|
||||||
|
#define MV_PORTASSOC_PORTS(_n) ((_n) & 0x1f)
|
||||||
|
#define MV_PORTASSOC_MONITOR (1 << 15)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
MV_SWITCH_MAC0 = 0x01,
|
||||||
|
MV_SWITCH_MAC1 = 0x02,
|
||||||
|
MV_SWITCH_MAC2 = 0x03,
|
||||||
|
MV_SWITCH_CTRL = 0x04,
|
||||||
|
MV_SWITCH_ATU_CTRL = 0x0a,
|
||||||
|
MV_SWITCH_ATU_OP = 0x0b,
|
||||||
|
MV_SWITCH_ATU_DATA = 0x0c,
|
||||||
|
MV_SWITCH_ATU_MAC0 = 0x0d,
|
||||||
|
MV_SWITCH_ATU_MAC1 = 0x0e,
|
||||||
|
MV_SWITCH_ATU_MAC2 = 0x0f,
|
||||||
|
};
|
||||||
|
#define MV_SWITCHREG(_type) MV_SWITCHREGS, MV_SWITCH_##_type
|
||||||
|
|
||||||
|
enum {
|
||||||
|
MV_SWITCHCTL_EEIE = (1 << 0), /* EEPROM interrupt enable */
|
||||||
|
MV_SWITCHCTL_PHYIE = (1 << 1), /* PHY interrupt enable */
|
||||||
|
MV_SWITCHCTL_ATUDONE= (1 << 2), /* ATU done interrupt enable */
|
||||||
|
MV_SWITCHCTL_ATUIE = (1 << 3), /* ATU interrupt enable */
|
||||||
|
MV_SWITCHCTL_CTRMODE= (1 << 8), /* statistics for rx and tx errors */
|
||||||
|
MV_SWITCHCTL_RELOAD = (1 << 9), /* reload registers from eeprom */
|
||||||
|
MV_SWITCHCTL_MSIZE = (1 << 10), /* increase maximum frame size */
|
||||||
|
MV_SWITCHCTL_DROP = (1 << 13), /* discard frames with excessive collisions */
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
#define MV_ATUCTL_AGETIME_MIN 16
|
||||||
|
#define MV_ATUCTL_AGETIME_MAX 4080
|
||||||
|
#define MV_ATUCTL_AGETIME(_n) ((((_n) / 16) & 0xff) << 4)
|
||||||
|
MV_ATUCTL_ATU_256 = (0 << 12),
|
||||||
|
MV_ATUCTL_ATU_512 = (1 << 12),
|
||||||
|
MV_ATUCTL_ATU_1K = (2 << 12),
|
||||||
|
MV_ATUCTL_ATUMASK = (3 << 12),
|
||||||
|
MV_ATUCTL_NO_LEARN = (1 << 14),
|
||||||
|
MV_ATUCTL_RESET = (1 << 15),
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
#define MV_ATUOP_DBNUM(_n) ((_n) & 0x0f)
|
||||||
|
|
||||||
|
MV_ATUOP_NOOP = (0 << 12),
|
||||||
|
MV_ATUOP_FLUSH_ALL = (1 << 12),
|
||||||
|
MV_ATUOP_FLUSH_U = (2 << 12),
|
||||||
|
MV_ATUOP_LOAD_DB = (3 << 12),
|
||||||
|
MV_ATUOP_GET_NEXT = (4 << 12),
|
||||||
|
MV_ATUOP_FLUSH_DB = (5 << 12),
|
||||||
|
MV_ATUOP_FLUSH_DB_UU= (6 << 12),
|
||||||
|
|
||||||
|
MV_ATUOP_INPROGRESS = (1 << 15),
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MV_IDENT_MASK 0xfff0
|
||||||
|
#define MV_IDENT_VALUE 0x0600
|
||||||
|
|
||||||
|
#endif
|
175
target/linux/generic-2.6/files-2.6.27/fs/yaffs2/Kconfig
Normal file
175
target/linux/generic-2.6/files-2.6.27/fs/yaffs2/Kconfig
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
#
|
||||||
|
# YAFFS file system configurations
|
||||||
|
#
|
||||||
|
|
||||||
|
config YAFFS_FS
|
||||||
|
tristate "YAFFS2 file system support"
|
||||||
|
default n
|
||||||
|
depends on MTD
|
||||||
|
select YAFFS_YAFFS1
|
||||||
|
select YAFFS_YAFFS2
|
||||||
|
help
|
||||||
|
YAFFS2, or Yet Another Flash Filing System, is a filing system
|
||||||
|
optimised for NAND Flash chips.
|
||||||
|
|
||||||
|
To compile the YAFFS2 file system support as a module, choose M
|
||||||
|
here: the module will be called yaffs2.
|
||||||
|
|
||||||
|
If unsure, say N.
|
||||||
|
|
||||||
|
Further information on YAFFS2 is available at
|
||||||
|
<http://www.aleph1.co.uk/yaffs/>.
|
||||||
|
|
||||||
|
config YAFFS_YAFFS1
|
||||||
|
bool "512 byte / page devices"
|
||||||
|
depends on YAFFS_FS
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Enable YAFFS1 support -- yaffs for 512 byte / page devices
|
||||||
|
|
||||||
|
Not needed for 2K-page devices.
|
||||||
|
|
||||||
|
If unsure, say Y.
|
||||||
|
|
||||||
|
config YAFFS_9BYTE_TAGS
|
||||||
|
bool "Use older-style on-NAND data format with pageStatus byte"
|
||||||
|
depends on YAFFS_YAFFS1
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
|
||||||
|
Older-style on-NAND data format has a "pageStatus" byte to record
|
||||||
|
chunk/page state. This byte is zero when the page is discarded.
|
||||||
|
Choose this option if you have existing on-NAND data using this
|
||||||
|
format that you need to continue to support. New data written
|
||||||
|
also uses the older-style format. Note: Use of this option
|
||||||
|
generally requires that MTD's oob layout be adjusted to use the
|
||||||
|
older-style format. See notes on tags formats and MTD versions.
|
||||||
|
|
||||||
|
If unsure, say N.
|
||||||
|
|
||||||
|
config YAFFS_DOES_ECC
|
||||||
|
bool "Lets Yaffs do its own ECC"
|
||||||
|
depends on YAFFS_FS && YAFFS_YAFFS1 && !YAFFS_9BYTE_TAGS
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
This enables Yaffs to use its own ECC functions instead of using
|
||||||
|
the ones from the generic MTD-NAND driver.
|
||||||
|
|
||||||
|
If unsure, say N.
|
||||||
|
|
||||||
|
config YAFFS_ECC_WRONG_ORDER
|
||||||
|
bool "Use the same ecc byte order as Steven Hill's nand_ecc.c"
|
||||||
|
depends on YAFFS_FS && YAFFS_DOES_ECC && !YAFFS_9BYTE_TAGS
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
This makes yaffs_ecc.c use the same ecc byte order as Steven
|
||||||
|
Hill's nand_ecc.c. If not set, then you get the same ecc byte
|
||||||
|
order as SmartMedia.
|
||||||
|
|
||||||
|
If unsure, say N.
|
||||||
|
|
||||||
|
config YAFFS_YAFFS2
|
||||||
|
bool "2048 byte (or larger) / page devices"
|
||||||
|
depends on YAFFS_FS
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Enable YAFFS2 support -- yaffs for >= 2K bytes per page devices
|
||||||
|
|
||||||
|
If unsure, say Y.
|
||||||
|
|
||||||
|
config YAFFS_AUTO_YAFFS2
|
||||||
|
bool "Autoselect yaffs2 format"
|
||||||
|
depends on YAFFS_YAFFS2
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Without this, you need to explicitely use yaffs2 as the file
|
||||||
|
system type. With this, you can say "yaffs" and yaffs or yaffs2
|
||||||
|
will be used depending on the device page size (yaffs on
|
||||||
|
512-byte page devices, yaffs2 on 2K page devices).
|
||||||
|
|
||||||
|
If unsure, say Y.
|
||||||
|
|
||||||
|
config YAFFS_DISABLE_LAZY_LOAD
|
||||||
|
bool "Disable lazy loading"
|
||||||
|
depends on YAFFS_YAFFS2
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
"Lazy loading" defers loading file details until they are
|
||||||
|
required. This saves mount time, but makes the first look-up
|
||||||
|
a bit longer.
|
||||||
|
|
||||||
|
Lazy loading will only happen if enabled by this option being 'n'
|
||||||
|
and if the appropriate tags are available, else yaffs2 will
|
||||||
|
automatically fall back to immediate loading and do the right
|
||||||
|
thing.
|
||||||
|
|
||||||
|
Lazy laoding will be required by checkpointing.
|
||||||
|
|
||||||
|
Setting this to 'y' will disable lazy loading.
|
||||||
|
|
||||||
|
If unsure, say N.
|
||||||
|
|
||||||
|
config YAFFS_CHECKPOINT_RESERVED_BLOCKS
|
||||||
|
int "Reserved blocks for checkpointing"
|
||||||
|
depends on YAFFS_YAFFS2
|
||||||
|
default 10
|
||||||
|
help
|
||||||
|
Give the number of Blocks to reserve for checkpointing.
|
||||||
|
Checkpointing saves the state at unmount so that mounting is
|
||||||
|
much faster as a scan of all the flash to regenerate this state
|
||||||
|
is not needed. These Blocks are reserved per partition, so if
|
||||||
|
you have very small partitions the default (10) may be a mess
|
||||||
|
for you. You can set this value to 0, but that does not mean
|
||||||
|
checkpointing is disabled at all. There only won't be any
|
||||||
|
specially reserved blocks for checkpointing, so if there is
|
||||||
|
enough free space on the filesystem, it will be used for
|
||||||
|
checkpointing.
|
||||||
|
|
||||||
|
If unsure, leave at default (10), but don't wonder if there are
|
||||||
|
always 2MB used on your large page device partition (10 x 2k
|
||||||
|
pagesize). When using small partitions or when being very small
|
||||||
|
on space, you probably want to set this to zero.
|
||||||
|
|
||||||
|
config YAFFS_DISABLE_WIDE_TNODES
|
||||||
|
bool "Turn off wide tnodes"
|
||||||
|
depends on YAFFS_FS
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Wide tnodes are only used for NAND arrays >=32MB for 512-byte
|
||||||
|
page devices and >=128MB for 2k page devices. They use slightly
|
||||||
|
more RAM but are faster since they eliminate chunk group
|
||||||
|
searching.
|
||||||
|
|
||||||
|
Setting this to 'y' will force tnode width to 16 bits and save
|
||||||
|
memory but make large arrays slower.
|
||||||
|
|
||||||
|
If unsure, say N.
|
||||||
|
|
||||||
|
config YAFFS_ALWAYS_CHECK_CHUNK_ERASED
|
||||||
|
bool "Force chunk erase check"
|
||||||
|
depends on YAFFS_FS
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Normally YAFFS only checks chunks before writing until an erased
|
||||||
|
chunk is found. This helps to detect any partially written
|
||||||
|
chunks that might have happened due to power loss.
|
||||||
|
|
||||||
|
Enabling this forces on the test that chunks are erased in flash
|
||||||
|
before writing to them. This takes more time but is potentially
|
||||||
|
a bit more secure.
|
||||||
|
|
||||||
|
Suggest setting Y during development and ironing out driver
|
||||||
|
issues etc. Suggest setting to N if you want faster writing.
|
||||||
|
|
||||||
|
If unsure, say Y.
|
||||||
|
|
||||||
|
config YAFFS_SHORT_NAMES_IN_RAM
|
||||||
|
bool "Cache short names in RAM"
|
||||||
|
depends on YAFFS_FS
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
If this config is set, then short names are stored with the
|
||||||
|
yaffs_Object. This costs an extra 16 bytes of RAM per object,
|
||||||
|
but makes look-ups faster.
|
||||||
|
|
||||||
|
If unsure, say Y.
|
11
target/linux/generic-2.6/files-2.6.27/fs/yaffs2/Makefile
Normal file
11
target/linux/generic-2.6/files-2.6.27/fs/yaffs2/Makefile
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#
|
||||||
|
# Makefile for the linux YAFFS filesystem routines.
|
||||||
|
#
|
||||||
|
|
||||||
|
obj-$(CONFIG_YAFFS_FS) += yaffs.o
|
||||||
|
|
||||||
|
yaffs-y := yaffs_ecc.o yaffs_fs.o yaffs_guts.o yaffs_checkptrw.o
|
||||||
|
yaffs-y += yaffs_packedtags2.o yaffs_nand.o yaffs_qsort.o
|
||||||
|
yaffs-y += yaffs_tagscompat.o yaffs_tagsvalidity.o
|
||||||
|
yaffs-y += yaffs_mtdif1.o yaffs_packedtags1.o
|
||||||
|
yaffs-y += yaffs_mtdif.o yaffs_mtdif2.o
|
264
target/linux/generic-2.6/files-2.6.27/fs/yaffs2/devextras.h
Normal file
264
target/linux/generic-2.6/files-2.6.27/fs/yaffs2/devextras.h
Normal file
|
@ -0,0 +1,264 @@
|
||||||
|
/*
|
||||||
|
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2002-2007 Aleph One Ltd.
|
||||||
|
* for Toby Churchill Ltd and Brightstar Engineering
|
||||||
|
*
|
||||||
|
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is just holds extra declarations used during development.
|
||||||
|
* Most of these are from kernel includes placed here so we can use them in
|
||||||
|
* applications.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __EXTRAS_H__
|
||||||
|
#define __EXTRAS_H__
|
||||||
|
|
||||||
|
#if defined WIN32
|
||||||
|
#define __inline__ __inline
|
||||||
|
#define new newHack
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !(defined __KERNEL__) || (defined WIN32)
|
||||||
|
|
||||||
|
/* User space defines */
|
||||||
|
|
||||||
|
typedef unsigned char __u8;
|
||||||
|
typedef unsigned short __u16;
|
||||||
|
typedef unsigned __u32;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Simple doubly linked list implementation.
|
||||||
|
*
|
||||||
|
* Some of the internal functions ("__xxx") are useful when
|
||||||
|
* manipulating whole lists rather than single entries, as
|
||||||
|
* sometimes we already know the next/prev entries and we can
|
||||||
|
* generate better code by using them directly rather than
|
||||||
|
* using the generic single-entry routines.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define prefetch(x) 1
|
||||||
|
|
||||||
|
struct list_head {
|
||||||
|
struct list_head *next, *prev;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define LIST_HEAD_INIT(name) { &(name), &(name) }
|
||||||
|
|
||||||
|
#define LIST_HEAD(name) \
|
||||||
|
struct list_head name = LIST_HEAD_INIT(name)
|
||||||
|
|
||||||
|
#define INIT_LIST_HEAD(ptr) do { \
|
||||||
|
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Insert a new entry between two known consecutive entries.
|
||||||
|
*
|
||||||
|
* This is only for internal list manipulation where we know
|
||||||
|
* the prev/next entries already!
|
||||||
|
*/
|
||||||
|
static __inline__ void __list_add(struct list_head *new,
|
||||||
|
struct list_head *prev,
|
||||||
|
struct list_head *next)
|
||||||
|
{
|
||||||
|
next->prev = new;
|
||||||
|
new->next = next;
|
||||||
|
new->prev = prev;
|
||||||
|
prev->next = new;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_add - add a new entry
|
||||||
|
* @new: new entry to be added
|
||||||
|
* @head: list head to add it after
|
||||||
|
*
|
||||||
|
* Insert a new entry after the specified head.
|
||||||
|
* This is good for implementing stacks.
|
||||||
|
*/
|
||||||
|
static __inline__ void list_add(struct list_head *new, struct list_head *head)
|
||||||
|
{
|
||||||
|
__list_add(new, head, head->next);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_add_tail - add a new entry
|
||||||
|
* @new: new entry to be added
|
||||||
|
* @head: list head to add it before
|
||||||
|
*
|
||||||
|
* Insert a new entry before the specified head.
|
||||||
|
* This is useful for implementing queues.
|
||||||
|
*/
|
||||||
|
static __inline__ void list_add_tail(struct list_head *new,
|
||||||
|
struct list_head *head)
|
||||||
|
{
|
||||||
|
__list_add(new, head->prev, head);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Delete a list entry by making the prev/next entries
|
||||||
|
* point to each other.
|
||||||
|
*
|
||||||
|
* This is only for internal list manipulation where we know
|
||||||
|
* the prev/next entries already!
|
||||||
|
*/
|
||||||
|
static __inline__ void __list_del(struct list_head *prev,
|
||||||
|
struct list_head *next)
|
||||||
|
{
|
||||||
|
next->prev = prev;
|
||||||
|
prev->next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_del - deletes entry from list.
|
||||||
|
* @entry: the element to delete from the list.
|
||||||
|
* Note: list_empty on entry does not return true after this, the entry is
|
||||||
|
* in an undefined state.
|
||||||
|
*/
|
||||||
|
static __inline__ void list_del(struct list_head *entry)
|
||||||
|
{
|
||||||
|
__list_del(entry->prev, entry->next);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_del_init - deletes entry from list and reinitialize it.
|
||||||
|
* @entry: the element to delete from the list.
|
||||||
|
*/
|
||||||
|
static __inline__ void list_del_init(struct list_head *entry)
|
||||||
|
{
|
||||||
|
__list_del(entry->prev, entry->next);
|
||||||
|
INIT_LIST_HEAD(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_empty - tests whether a list is empty
|
||||||
|
* @head: the list to test.
|
||||||
|
*/
|
||||||
|
static __inline__ int list_empty(struct list_head *head)
|
||||||
|
{
|
||||||
|
return head->next == head;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_splice - join two lists
|
||||||
|
* @list: the new list to add.
|
||||||
|
* @head: the place to add it in the first list.
|
||||||
|
*/
|
||||||
|
static __inline__ void list_splice(struct list_head *list,
|
||||||
|
struct list_head *head)
|
||||||
|
{
|
||||||
|
struct list_head *first = list->next;
|
||||||
|
|
||||||
|
if (first != list) {
|
||||||
|
struct list_head *last = list->prev;
|
||||||
|
struct list_head *at = head->next;
|
||||||
|
|
||||||
|
first->prev = head;
|
||||||
|
head->next = first;
|
||||||
|
|
||||||
|
last->next = at;
|
||||||
|
at->prev = last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_entry - get the struct for this entry
|
||||||
|
* @ptr: the &struct list_head pointer.
|
||||||
|
* @type: the type of the struct this is embedded in.
|
||||||
|
* @member: the name of the list_struct within the struct.
|
||||||
|
*/
|
||||||
|
#define list_entry(ptr, type, member) \
|
||||||
|
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_for_each - iterate over a list
|
||||||
|
* @pos: the &struct list_head to use as a loop counter.
|
||||||
|
* @head: the head for your list.
|
||||||
|
*/
|
||||||
|
#define list_for_each(pos, head) \
|
||||||
|
for (pos = (head)->next, prefetch(pos->next); pos != (head); \
|
||||||
|
pos = pos->next, prefetch(pos->next))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_for_each_safe - iterate over a list safe against removal
|
||||||
|
* of list entry
|
||||||
|
* @pos: the &struct list_head to use as a loop counter.
|
||||||
|
* @n: another &struct list_head to use as temporary storage
|
||||||
|
* @head: the head for your list.
|
||||||
|
*/
|
||||||
|
#define list_for_each_safe(pos, n, head) \
|
||||||
|
for (pos = (head)->next, n = pos->next; pos != (head); \
|
||||||
|
pos = n, n = pos->next)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* File types
|
||||||
|
*/
|
||||||
|
#define DT_UNKNOWN 0
|
||||||
|
#define DT_FIFO 1
|
||||||
|
#define DT_CHR 2
|
||||||
|
#define DT_DIR 4
|
||||||
|
#define DT_BLK 6
|
||||||
|
#define DT_REG 8
|
||||||
|
#define DT_LNK 10
|
||||||
|
#define DT_SOCK 12
|
||||||
|
#define DT_WHT 14
|
||||||
|
|
||||||
|
#ifndef WIN32
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Attribute flags. These should be or-ed together to figure out what
|
||||||
|
* has been changed!
|
||||||
|
*/
|
||||||
|
#define ATTR_MODE 1
|
||||||
|
#define ATTR_UID 2
|
||||||
|
#define ATTR_GID 4
|
||||||
|
#define ATTR_SIZE 8
|
||||||
|
#define ATTR_ATIME 16
|
||||||
|
#define ATTR_MTIME 32
|
||||||
|
#define ATTR_CTIME 64
|
||||||
|
#define ATTR_ATIME_SET 128
|
||||||
|
#define ATTR_MTIME_SET 256
|
||||||
|
#define ATTR_FORCE 512 /* Not a change, but a change it */
|
||||||
|
#define ATTR_ATTR_FLAG 1024
|
||||||
|
|
||||||
|
struct iattr {
|
||||||
|
unsigned int ia_valid;
|
||||||
|
unsigned ia_mode;
|
||||||
|
unsigned ia_uid;
|
||||||
|
unsigned ia_gid;
|
||||||
|
unsigned ia_size;
|
||||||
|
unsigned ia_atime;
|
||||||
|
unsigned ia_mtime;
|
||||||
|
unsigned ia_ctime;
|
||||||
|
unsigned int ia_attr_flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define KERN_DEBUG
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#ifndef WIN32
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/stat.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined WIN32
|
||||||
|
#undef new
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2002-2007 Aleph One Ltd.
|
||||||
|
* for Toby Churchill Ltd and Brightstar Engineering
|
||||||
|
*
|
||||||
|
* Created by Martin Fouts <Martin.Fouts@palmsource.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __YAFFS_CONFIG_H__
|
||||||
|
#define __YAFFS_CONFIG_H__
|
||||||
|
|
||||||
|
#ifdef YAFFS_OUT_OF_TREE
|
||||||
|
|
||||||
|
/* DO NOT UNSET THESE THREE. YAFFS2 will not compile if you do. */
|
||||||
|
#define CONFIG_YAFFS_FS
|
||||||
|
#define CONFIG_YAFFS_YAFFS1
|
||||||
|
#define CONFIG_YAFFS_YAFFS2
|
||||||
|
|
||||||
|
/* These options are independent of each other. Select those that matter. */
|
||||||
|
|
||||||
|
/* Default: Not selected */
|
||||||
|
/* Meaning: Yaffs does its own ECC, rather than using MTD ECC */
|
||||||
|
//#define CONFIG_YAFFS_DOES_ECC
|
||||||
|
|
||||||
|
/* Default: Not selected */
|
||||||
|
/* Meaning: ECC byte order is 'wrong'. Only meaningful if */
|
||||||
|
/* CONFIG_YAFFS_DOES_ECC is set */
|
||||||
|
//#define CONFIG_YAFFS_ECC_WRONG_ORDER
|
||||||
|
|
||||||
|
/* Default: Selected */
|
||||||
|
/* Meaning: Disables testing whether chunks are erased before writing to them*/
|
||||||
|
#define CONFIG_YAFFS_DISABLE_CHUNK_ERASED_CHECK
|
||||||
|
|
||||||
|
/* Default: Selected */
|
||||||
|
/* Meaning: Cache short names, taking more RAM, but faster look-ups */
|
||||||
|
#define CONFIG_YAFFS_SHORT_NAMES_IN_RAM
|
||||||
|
|
||||||
|
/* Default: 10 */
|
||||||
|
/* Meaning: set the count of blocks to reserve for checkpointing */
|
||||||
|
#define CONFIG_YAFFS_CHECKPOINT_RESERVED_BLOCKS 10
|
||||||
|
|
||||||
|
/*
|
||||||
|
Older-style on-NAND data format has a "pageStatus" byte to record
|
||||||
|
chunk/page state. This byte is zeroed when the page is discarded.
|
||||||
|
Choose this option if you have existing on-NAND data in this format
|
||||||
|
that you need to continue to support. New data written also uses the
|
||||||
|
older-style format.
|
||||||
|
Note: Use of this option generally requires that MTD's oob layout be
|
||||||
|
adjusted to use the older-style format. See notes on tags formats and
|
||||||
|
MTD versions.
|
||||||
|
*/
|
||||||
|
/* Default: Not selected */
|
||||||
|
/* Meaning: Use older-style on-NAND data format with pageStatus byte */
|
||||||
|
#define CONFIG_YAFFS_9BYTE_TAGS
|
||||||
|
|
||||||
|
#endif /* YAFFS_OUT_OF_TREE */
|
||||||
|
|
||||||
|
#endif /* __YAFFS_CONFIG_H__ */
|
|
@ -0,0 +1,404 @@
|
||||||
|
/*
|
||||||
|
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2002-2007 Aleph One Ltd.
|
||||||
|
* for Toby Churchill Ltd and Brightstar Engineering
|
||||||
|
*
|
||||||
|
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const char *yaffs_checkptrw_c_version =
|
||||||
|
"$Id: yaffs_checkptrw.c,v 1.14 2007-05-15 20:07:40 charles Exp $";
|
||||||
|
|
||||||
|
|
||||||
|
#include "yaffs_checkptrw.h"
|
||||||
|
|
||||||
|
|
||||||
|
static int yaffs_CheckpointSpaceOk(yaffs_Device *dev)
|
||||||
|
{
|
||||||
|
|
||||||
|
int blocksAvailable = dev->nErasedBlocks - dev->nReservedBlocks;
|
||||||
|
|
||||||
|
T(YAFFS_TRACE_CHECKPOINT,
|
||||||
|
(TSTR("checkpt blocks available = %d" TENDSTR),
|
||||||
|
blocksAvailable));
|
||||||
|
|
||||||
|
|
||||||
|
return (blocksAvailable <= 0) ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int yaffs_CheckpointErase(yaffs_Device *dev)
|
||||||
|
{
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
|
||||||
|
if(!dev->eraseBlockInNAND)
|
||||||
|
return 0;
|
||||||
|
T(YAFFS_TRACE_CHECKPOINT,(TSTR("checking blocks %d to %d"TENDSTR),
|
||||||
|
dev->internalStartBlock,dev->internalEndBlock));
|
||||||
|
|
||||||
|
for(i = dev->internalStartBlock; i <= dev->internalEndBlock; i++) {
|
||||||
|
yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,i);
|
||||||
|
if(bi->blockState == YAFFS_BLOCK_STATE_CHECKPOINT){
|
||||||
|
T(YAFFS_TRACE_CHECKPOINT,(TSTR("erasing checkpt block %d"TENDSTR),i));
|
||||||
|
if(dev->eraseBlockInNAND(dev,i- dev->blockOffset /* realign */)){
|
||||||
|
bi->blockState = YAFFS_BLOCK_STATE_EMPTY;
|
||||||
|
dev->nErasedBlocks++;
|
||||||
|
dev->nFreeChunks += dev->nChunksPerBlock;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dev->markNANDBlockBad(dev,i);
|
||||||
|
bi->blockState = YAFFS_BLOCK_STATE_DEAD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->blocksInCheckpoint = 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void yaffs_CheckpointFindNextErasedBlock(yaffs_Device *dev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int blocksAvailable = dev->nErasedBlocks - dev->nReservedBlocks;
|
||||||
|
T(YAFFS_TRACE_CHECKPOINT,
|
||||||
|
(TSTR("allocating checkpt block: erased %d reserved %d avail %d next %d "TENDSTR),
|
||||||
|
dev->nErasedBlocks,dev->nReservedBlocks,blocksAvailable,dev->checkpointNextBlock));
|
||||||
|
|
||||||
|
if(dev->checkpointNextBlock >= 0 &&
|
||||||
|
dev->checkpointNextBlock <= dev->internalEndBlock &&
|
||||||
|
blocksAvailable > 0){
|
||||||
|
|
||||||
|
for(i = dev->checkpointNextBlock; i <= dev->internalEndBlock; i++){
|
||||||
|
yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,i);
|
||||||
|
if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY){
|
||||||
|
dev->checkpointNextBlock = i + 1;
|
||||||
|
dev->checkpointCurrentBlock = i;
|
||||||
|
T(YAFFS_TRACE_CHECKPOINT,(TSTR("allocating checkpt block %d"TENDSTR),i));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
T(YAFFS_TRACE_CHECKPOINT,(TSTR("out of checkpt blocks"TENDSTR)));
|
||||||
|
|
||||||
|
dev->checkpointNextBlock = -1;
|
||||||
|
dev->checkpointCurrentBlock = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void yaffs_CheckpointFindNextCheckpointBlock(yaffs_Device *dev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
yaffs_ExtendedTags tags;
|
||||||
|
|
||||||
|
T(YAFFS_TRACE_CHECKPOINT,(TSTR("find next checkpt block: start: blocks %d next %d" TENDSTR),
|
||||||
|
dev->blocksInCheckpoint, dev->checkpointNextBlock));
|
||||||
|
|
||||||
|
if(dev->blocksInCheckpoint < dev->checkpointMaxBlocks)
|
||||||
|
for(i = dev->checkpointNextBlock; i <= dev->internalEndBlock; i++){
|
||||||
|
int chunk = i * dev->nChunksPerBlock;
|
||||||
|
int realignedChunk = chunk - dev->chunkOffset;
|
||||||
|
|
||||||
|
dev->readChunkWithTagsFromNAND(dev,realignedChunk,NULL,&tags);
|
||||||
|
T(YAFFS_TRACE_CHECKPOINT,(TSTR("find next checkpt block: search: block %d oid %d seq %d eccr %d" TENDSTR),
|
||||||
|
i, tags.objectId,tags.sequenceNumber,tags.eccResult));
|
||||||
|
|
||||||
|
if(tags.sequenceNumber == YAFFS_SEQUENCE_CHECKPOINT_DATA){
|
||||||
|
/* Right kind of block */
|
||||||
|
dev->checkpointNextBlock = tags.objectId;
|
||||||
|
dev->checkpointCurrentBlock = i;
|
||||||
|
dev->checkpointBlockList[dev->blocksInCheckpoint] = i;
|
||||||
|
dev->blocksInCheckpoint++;
|
||||||
|
T(YAFFS_TRACE_CHECKPOINT,(TSTR("found checkpt block %d"TENDSTR),i));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
T(YAFFS_TRACE_CHECKPOINT,(TSTR("found no more checkpt blocks"TENDSTR)));
|
||||||
|
|
||||||
|
dev->checkpointNextBlock = -1;
|
||||||
|
dev->checkpointCurrentBlock = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int yaffs_CheckpointOpen(yaffs_Device *dev, int forWriting)
|
||||||
|
{
|
||||||
|
|
||||||
|
/* Got the functions we need? */
|
||||||
|
if (!dev->writeChunkWithTagsToNAND ||
|
||||||
|
!dev->readChunkWithTagsFromNAND ||
|
||||||
|
!dev->eraseBlockInNAND ||
|
||||||
|
!dev->markNANDBlockBad)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if(forWriting && !yaffs_CheckpointSpaceOk(dev))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if(!dev->checkpointBuffer)
|
||||||
|
dev->checkpointBuffer = YMALLOC_DMA(dev->nDataBytesPerChunk);
|
||||||
|
if(!dev->checkpointBuffer)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
|
||||||
|
dev->checkpointPageSequence = 0;
|
||||||
|
|
||||||
|
dev->checkpointOpenForWrite = forWriting;
|
||||||
|
|
||||||
|
dev->checkpointByteCount = 0;
|
||||||
|
dev->checkpointSum = 0;
|
||||||
|
dev->checkpointXor = 0;
|
||||||
|
dev->checkpointCurrentBlock = -1;
|
||||||
|
dev->checkpointCurrentChunk = -1;
|
||||||
|
dev->checkpointNextBlock = dev->internalStartBlock;
|
||||||
|
|
||||||
|
/* Erase all the blocks in the checkpoint area */
|
||||||
|
if(forWriting){
|
||||||
|
memset(dev->checkpointBuffer,0,dev->nDataBytesPerChunk);
|
||||||
|
dev->checkpointByteOffset = 0;
|
||||||
|
return yaffs_CheckpointErase(dev);
|
||||||
|
|
||||||
|
|
||||||
|
} else {
|
||||||
|
int i;
|
||||||
|
/* Set to a value that will kick off a read */
|
||||||
|
dev->checkpointByteOffset = dev->nDataBytesPerChunk;
|
||||||
|
/* A checkpoint block list of 1 checkpoint block per 16 block is (hopefully)
|
||||||
|
* going to be way more than we need */
|
||||||
|
dev->blocksInCheckpoint = 0;
|
||||||
|
dev->checkpointMaxBlocks = (dev->internalEndBlock - dev->internalStartBlock)/16 + 2;
|
||||||
|
dev->checkpointBlockList = YMALLOC(sizeof(int) * dev->checkpointMaxBlocks);
|
||||||
|
for(i = 0; i < dev->checkpointMaxBlocks; i++)
|
||||||
|
dev->checkpointBlockList[i] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int yaffs_GetCheckpointSum(yaffs_Device *dev, __u32 *sum)
|
||||||
|
{
|
||||||
|
__u32 compositeSum;
|
||||||
|
compositeSum = (dev->checkpointSum << 8) | (dev->checkpointXor & 0xFF);
|
||||||
|
*sum = compositeSum;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int yaffs_CheckpointFlushBuffer(yaffs_Device *dev)
|
||||||
|
{
|
||||||
|
|
||||||
|
int chunk;
|
||||||
|
int realignedChunk;
|
||||||
|
|
||||||
|
yaffs_ExtendedTags tags;
|
||||||
|
|
||||||
|
if(dev->checkpointCurrentBlock < 0){
|
||||||
|
yaffs_CheckpointFindNextErasedBlock(dev);
|
||||||
|
dev->checkpointCurrentChunk = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(dev->checkpointCurrentBlock < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
tags.chunkDeleted = 0;
|
||||||
|
tags.objectId = dev->checkpointNextBlock; /* Hint to next place to look */
|
||||||
|
tags.chunkId = dev->checkpointPageSequence + 1;
|
||||||
|
tags.sequenceNumber = YAFFS_SEQUENCE_CHECKPOINT_DATA;
|
||||||
|
tags.byteCount = dev->nDataBytesPerChunk;
|
||||||
|
if(dev->checkpointCurrentChunk == 0){
|
||||||
|
/* First chunk we write for the block? Set block state to
|
||||||
|
checkpoint */
|
||||||
|
yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,dev->checkpointCurrentBlock);
|
||||||
|
bi->blockState = YAFFS_BLOCK_STATE_CHECKPOINT;
|
||||||
|
dev->blocksInCheckpoint++;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk = dev->checkpointCurrentBlock * dev->nChunksPerBlock + dev->checkpointCurrentChunk;
|
||||||
|
|
||||||
|
|
||||||
|
T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint wite buffer nand %d(%d:%d) objid %d chId %d" TENDSTR),
|
||||||
|
chunk, dev->checkpointCurrentBlock, dev->checkpointCurrentChunk,tags.objectId,tags.chunkId));
|
||||||
|
|
||||||
|
realignedChunk = chunk - dev->chunkOffset;
|
||||||
|
|
||||||
|
dev->writeChunkWithTagsToNAND(dev,realignedChunk,dev->checkpointBuffer,&tags);
|
||||||
|
dev->checkpointByteOffset = 0;
|
||||||
|
dev->checkpointPageSequence++;
|
||||||
|
dev->checkpointCurrentChunk++;
|
||||||
|
if(dev->checkpointCurrentChunk >= dev->nChunksPerBlock){
|
||||||
|
dev->checkpointCurrentChunk = 0;
|
||||||
|
dev->checkpointCurrentBlock = -1;
|
||||||
|
}
|
||||||
|
memset(dev->checkpointBuffer,0,dev->nDataBytesPerChunk);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int yaffs_CheckpointWrite(yaffs_Device *dev,const void *data, int nBytes)
|
||||||
|
{
|
||||||
|
int i=0;
|
||||||
|
int ok = 1;
|
||||||
|
|
||||||
|
|
||||||
|
__u8 * dataBytes = (__u8 *)data;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(!dev->checkpointBuffer)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if(!dev->checkpointOpenForWrite)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
while(i < nBytes && ok) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
dev->checkpointBuffer[dev->checkpointByteOffset] = *dataBytes ;
|
||||||
|
dev->checkpointSum += *dataBytes;
|
||||||
|
dev->checkpointXor ^= *dataBytes;
|
||||||
|
|
||||||
|
dev->checkpointByteOffset++;
|
||||||
|
i++;
|
||||||
|
dataBytes++;
|
||||||
|
dev->checkpointByteCount++;
|
||||||
|
|
||||||
|
|
||||||
|
if(dev->checkpointByteOffset < 0 ||
|
||||||
|
dev->checkpointByteOffset >= dev->nDataBytesPerChunk)
|
||||||
|
ok = yaffs_CheckpointFlushBuffer(dev);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
int yaffs_CheckpointRead(yaffs_Device *dev, void *data, int nBytes)
|
||||||
|
{
|
||||||
|
int i=0;
|
||||||
|
int ok = 1;
|
||||||
|
yaffs_ExtendedTags tags;
|
||||||
|
|
||||||
|
|
||||||
|
int chunk;
|
||||||
|
int realignedChunk;
|
||||||
|
|
||||||
|
__u8 *dataBytes = (__u8 *)data;
|
||||||
|
|
||||||
|
if(!dev->checkpointBuffer)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if(dev->checkpointOpenForWrite)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
while(i < nBytes && ok) {
|
||||||
|
|
||||||
|
|
||||||
|
if(dev->checkpointByteOffset < 0 ||
|
||||||
|
dev->checkpointByteOffset >= dev->nDataBytesPerChunk) {
|
||||||
|
|
||||||
|
if(dev->checkpointCurrentBlock < 0){
|
||||||
|
yaffs_CheckpointFindNextCheckpointBlock(dev);
|
||||||
|
dev->checkpointCurrentChunk = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(dev->checkpointCurrentBlock < 0)
|
||||||
|
ok = 0;
|
||||||
|
else {
|
||||||
|
|
||||||
|
chunk = dev->checkpointCurrentBlock * dev->nChunksPerBlock +
|
||||||
|
dev->checkpointCurrentChunk;
|
||||||
|
|
||||||
|
realignedChunk = chunk - dev->chunkOffset;
|
||||||
|
|
||||||
|
/* read in the next chunk */
|
||||||
|
/* printf("read checkpoint page %d\n",dev->checkpointPage); */
|
||||||
|
dev->readChunkWithTagsFromNAND(dev, realignedChunk,
|
||||||
|
dev->checkpointBuffer,
|
||||||
|
&tags);
|
||||||
|
|
||||||
|
if(tags.chunkId != (dev->checkpointPageSequence + 1) ||
|
||||||
|
tags.sequenceNumber != YAFFS_SEQUENCE_CHECKPOINT_DATA)
|
||||||
|
ok = 0;
|
||||||
|
|
||||||
|
dev->checkpointByteOffset = 0;
|
||||||
|
dev->checkpointPageSequence++;
|
||||||
|
dev->checkpointCurrentChunk++;
|
||||||
|
|
||||||
|
if(dev->checkpointCurrentChunk >= dev->nChunksPerBlock)
|
||||||
|
dev->checkpointCurrentBlock = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ok){
|
||||||
|
*dataBytes = dev->checkpointBuffer[dev->checkpointByteOffset];
|
||||||
|
dev->checkpointSum += *dataBytes;
|
||||||
|
dev->checkpointXor ^= *dataBytes;
|
||||||
|
dev->checkpointByteOffset++;
|
||||||
|
i++;
|
||||||
|
dataBytes++;
|
||||||
|
dev->checkpointByteCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
int yaffs_CheckpointClose(yaffs_Device *dev)
|
||||||
|
{
|
||||||
|
|
||||||
|
if(dev->checkpointOpenForWrite){
|
||||||
|
if(dev->checkpointByteOffset != 0)
|
||||||
|
yaffs_CheckpointFlushBuffer(dev);
|
||||||
|
} else {
|
||||||
|
int i;
|
||||||
|
for(i = 0; i < dev->blocksInCheckpoint && dev->checkpointBlockList[i] >= 0; i++){
|
||||||
|
yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,dev->checkpointBlockList[i]);
|
||||||
|
if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY)
|
||||||
|
bi->blockState = YAFFS_BLOCK_STATE_CHECKPOINT;
|
||||||
|
else {
|
||||||
|
// Todo this looks odd...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
YFREE(dev->checkpointBlockList);
|
||||||
|
dev->checkpointBlockList = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->nFreeChunks -= dev->blocksInCheckpoint * dev->nChunksPerBlock;
|
||||||
|
dev->nErasedBlocks -= dev->blocksInCheckpoint;
|
||||||
|
|
||||||
|
|
||||||
|
T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint byte count %d" TENDSTR),
|
||||||
|
dev->checkpointByteCount));
|
||||||
|
|
||||||
|
if(dev->checkpointBuffer){
|
||||||
|
/* free the buffer */
|
||||||
|
YFREE(dev->checkpointBuffer);
|
||||||
|
dev->checkpointBuffer = NULL;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int yaffs_CheckpointInvalidateStream(yaffs_Device *dev)
|
||||||
|
{
|
||||||
|
/* Erase the first checksum block */
|
||||||
|
|
||||||
|
T(YAFFS_TRACE_CHECKPOINT,(TSTR("checkpoint invalidate"TENDSTR)));
|
||||||
|
|
||||||
|
if(!yaffs_CheckpointSpaceOk(dev))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return yaffs_CheckpointErase(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2002-2007 Aleph One Ltd.
|
||||||
|
* for Toby Churchill Ltd and Brightstar Engineering
|
||||||
|
*
|
||||||
|
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __YAFFS_CHECKPTRW_H__
|
||||||
|
#define __YAFFS_CHECKPTRW_H__
|
||||||
|
|
||||||
|
#include "yaffs_guts.h"
|
||||||
|
|
||||||
|
int yaffs_CheckpointOpen(yaffs_Device *dev, int forWriting);
|
||||||
|
|
||||||
|
int yaffs_CheckpointWrite(yaffs_Device *dev,const void *data, int nBytes);
|
||||||
|
|
||||||
|
int yaffs_CheckpointRead(yaffs_Device *dev,void *data, int nBytes);
|
||||||
|
|
||||||
|
int yaffs_GetCheckpointSum(yaffs_Device *dev, __u32 *sum);
|
||||||
|
|
||||||
|
int yaffs_CheckpointClose(yaffs_Device *dev);
|
||||||
|
|
||||||
|
int yaffs_CheckpointInvalidateStream(yaffs_Device *dev);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
331
target/linux/generic-2.6/files-2.6.27/fs/yaffs2/yaffs_ecc.c
Normal file
331
target/linux/generic-2.6/files-2.6.27/fs/yaffs2/yaffs_ecc.c
Normal file
|
@ -0,0 +1,331 @@
|
||||||
|
/*
|
||||||
|
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2002-2007 Aleph One Ltd.
|
||||||
|
* for Toby Churchill Ltd and Brightstar Engineering
|
||||||
|
*
|
||||||
|
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This code implements the ECC algorithm used in SmartMedia.
|
||||||
|
*
|
||||||
|
* The ECC comprises 22 bits of parity information and is stuffed into 3 bytes.
|
||||||
|
* The two unused bit are set to 1.
|
||||||
|
* The ECC can correct single bit errors in a 256-byte page of data. Thus, two such ECC
|
||||||
|
* blocks are used on a 512-byte NAND page.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Table generated by gen-ecc.c
|
||||||
|
* Using a table means we do not have to calculate p1..p4 and p1'..p4'
|
||||||
|
* for each byte of data. These are instead provided in a table in bits7..2.
|
||||||
|
* Bit 0 of each entry indicates whether the entry has an odd or even parity, and therefore
|
||||||
|
* this bytes influence on the line parity.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const char *yaffs_ecc_c_version =
|
||||||
|
"$Id: yaffs_ecc.c,v 1.9 2007-02-14 01:09:06 wookey Exp $";
|
||||||
|
|
||||||
|
#include "yportenv.h"
|
||||||
|
|
||||||
|
#include "yaffs_ecc.h"
|
||||||
|
|
||||||
|
static const unsigned char column_parity_table[] = {
|
||||||
|
0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69,
|
||||||
|
0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00,
|
||||||
|
0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc,
|
||||||
|
0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95,
|
||||||
|
0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0,
|
||||||
|
0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99,
|
||||||
|
0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65,
|
||||||
|
0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c,
|
||||||
|
0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc,
|
||||||
|
0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5,
|
||||||
|
0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59,
|
||||||
|
0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30,
|
||||||
|
0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55,
|
||||||
|
0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c,
|
||||||
|
0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0,
|
||||||
|
0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9,
|
||||||
|
0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0,
|
||||||
|
0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9,
|
||||||
|
0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55,
|
||||||
|
0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c,
|
||||||
|
0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59,
|
||||||
|
0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30,
|
||||||
|
0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc,
|
||||||
|
0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5,
|
||||||
|
0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65,
|
||||||
|
0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c,
|
||||||
|
0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0,
|
||||||
|
0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99,
|
||||||
|
0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc,
|
||||||
|
0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95,
|
||||||
|
0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69,
|
||||||
|
0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Count the bits in an unsigned char or a U32 */
|
||||||
|
|
||||||
|
static int yaffs_CountBits(unsigned char x)
|
||||||
|
{
|
||||||
|
int r = 0;
|
||||||
|
while (x) {
|
||||||
|
if (x & 1)
|
||||||
|
r++;
|
||||||
|
x >>= 1;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int yaffs_CountBits32(unsigned x)
|
||||||
|
{
|
||||||
|
int r = 0;
|
||||||
|
while (x) {
|
||||||
|
if (x & 1)
|
||||||
|
r++;
|
||||||
|
x >>= 1;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate the ECC for a 256-byte block of data */
|
||||||
|
void yaffs_ECCCalculate(const unsigned char *data, unsigned char *ecc)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
unsigned char col_parity = 0;
|
||||||
|
unsigned char line_parity = 0;
|
||||||
|
unsigned char line_parity_prime = 0;
|
||||||
|
unsigned char t;
|
||||||
|
unsigned char b;
|
||||||
|
|
||||||
|
for (i = 0; i < 256; i++) {
|
||||||
|
b = column_parity_table[*data++];
|
||||||
|
col_parity ^= b;
|
||||||
|
|
||||||
|
if (b & 0x01) // odd number of bits in the byte
|
||||||
|
{
|
||||||
|
line_parity ^= i;
|
||||||
|
line_parity_prime ^= ~i;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ecc[2] = (~col_parity) | 0x03;
|
||||||
|
|
||||||
|
t = 0;
|
||||||
|
if (line_parity & 0x80)
|
||||||
|
t |= 0x80;
|
||||||
|
if (line_parity_prime & 0x80)
|
||||||
|
t |= 0x40;
|
||||||
|
if (line_parity & 0x40)
|
||||||
|
t |= 0x20;
|
||||||
|
if (line_parity_prime & 0x40)
|
||||||
|
t |= 0x10;
|
||||||
|
if (line_parity & 0x20)
|
||||||
|
t |= 0x08;
|
||||||
|
if (line_parity_prime & 0x20)
|
||||||
|
t |= 0x04;
|
||||||
|
if (line_parity & 0x10)
|
||||||
|
t |= 0x02;
|
||||||
|
if (line_parity_prime & 0x10)
|
||||||
|
t |= 0x01;
|
||||||
|
ecc[1] = ~t;
|
||||||
|
|
||||||
|
t = 0;
|
||||||
|
if (line_parity & 0x08)
|
||||||
|
t |= 0x80;
|
||||||
|
if (line_parity_prime & 0x08)
|
||||||
|
t |= 0x40;
|
||||||
|
if (line_parity & 0x04)
|
||||||
|
t |= 0x20;
|
||||||
|
if (line_parity_prime & 0x04)
|
||||||
|
t |= 0x10;
|
||||||
|
if (line_parity & 0x02)
|
||||||
|
t |= 0x08;
|
||||||
|
if (line_parity_prime & 0x02)
|
||||||
|
t |= 0x04;
|
||||||
|
if (line_parity & 0x01)
|
||||||
|
t |= 0x02;
|
||||||
|
if (line_parity_prime & 0x01)
|
||||||
|
t |= 0x01;
|
||||||
|
ecc[0] = ~t;
|
||||||
|
|
||||||
|
#ifdef CONFIG_YAFFS_ECC_WRONG_ORDER
|
||||||
|
// Swap the bytes into the wrong order
|
||||||
|
t = ecc[0];
|
||||||
|
ecc[0] = ecc[1];
|
||||||
|
ecc[1] = t;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Correct the ECC on a 256 byte block of data */
|
||||||
|
|
||||||
|
int yaffs_ECCCorrect(unsigned char *data, unsigned char *read_ecc,
|
||||||
|
const unsigned char *test_ecc)
|
||||||
|
{
|
||||||
|
unsigned char d0, d1, d2; /* deltas */
|
||||||
|
|
||||||
|
d0 = read_ecc[0] ^ test_ecc[0];
|
||||||
|
d1 = read_ecc[1] ^ test_ecc[1];
|
||||||
|
d2 = read_ecc[2] ^ test_ecc[2];
|
||||||
|
|
||||||
|
if ((d0 | d1 | d2) == 0)
|
||||||
|
return 0; /* no error */
|
||||||
|
|
||||||
|
if (((d0 ^ (d0 >> 1)) & 0x55) == 0x55 &&
|
||||||
|
((d1 ^ (d1 >> 1)) & 0x55) == 0x55 &&
|
||||||
|
((d2 ^ (d2 >> 1)) & 0x54) == 0x54) {
|
||||||
|
/* Single bit (recoverable) error in data */
|
||||||
|
|
||||||
|
unsigned byte;
|
||||||
|
unsigned bit;
|
||||||
|
|
||||||
|
#ifdef CONFIG_YAFFS_ECC_WRONG_ORDER
|
||||||
|
// swap the bytes to correct for the wrong order
|
||||||
|
unsigned char t;
|
||||||
|
|
||||||
|
t = d0;
|
||||||
|
d0 = d1;
|
||||||
|
d1 = t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bit = byte = 0;
|
||||||
|
|
||||||
|
if (d1 & 0x80)
|
||||||
|
byte |= 0x80;
|
||||||
|
if (d1 & 0x20)
|
||||||
|
byte |= 0x40;
|
||||||
|
if (d1 & 0x08)
|
||||||
|
byte |= 0x20;
|
||||||
|
if (d1 & 0x02)
|
||||||
|
byte |= 0x10;
|
||||||
|
if (d0 & 0x80)
|
||||||
|
byte |= 0x08;
|
||||||
|
if (d0 & 0x20)
|
||||||
|
byte |= 0x04;
|
||||||
|
if (d0 & 0x08)
|
||||||
|
byte |= 0x02;
|
||||||
|
if (d0 & 0x02)
|
||||||
|
byte |= 0x01;
|
||||||
|
|
||||||
|
if (d2 & 0x80)
|
||||||
|
bit |= 0x04;
|
||||||
|
if (d2 & 0x20)
|
||||||
|
bit |= 0x02;
|
||||||
|
if (d2 & 0x08)
|
||||||
|
bit |= 0x01;
|
||||||
|
|
||||||
|
data[byte] ^= (1 << bit);
|
||||||
|
|
||||||
|
return 1; /* Corrected the error */
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((yaffs_CountBits(d0) +
|
||||||
|
yaffs_CountBits(d1) +
|
||||||
|
yaffs_CountBits(d2)) == 1) {
|
||||||
|
/* Reccoverable error in ecc */
|
||||||
|
|
||||||
|
read_ecc[0] = test_ecc[0];
|
||||||
|
read_ecc[1] = test_ecc[1];
|
||||||
|
read_ecc[2] = test_ecc[2];
|
||||||
|
|
||||||
|
return 1; /* Corrected the error */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unrecoverable error */
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ECCxxxOther does ECC calcs on arbitrary n bytes of data
|
||||||
|
*/
|
||||||
|
void yaffs_ECCCalculateOther(const unsigned char *data, unsigned nBytes,
|
||||||
|
yaffs_ECCOther * eccOther)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
unsigned char col_parity = 0;
|
||||||
|
unsigned line_parity = 0;
|
||||||
|
unsigned line_parity_prime = 0;
|
||||||
|
unsigned char b;
|
||||||
|
|
||||||
|
for (i = 0; i < nBytes; i++) {
|
||||||
|
b = column_parity_table[*data++];
|
||||||
|
col_parity ^= b;
|
||||||
|
|
||||||
|
if (b & 0x01) {
|
||||||
|
/* odd number of bits in the byte */
|
||||||
|
line_parity ^= i;
|
||||||
|
line_parity_prime ^= ~i;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
eccOther->colParity = (col_parity >> 2) & 0x3f;
|
||||||
|
eccOther->lineParity = line_parity;
|
||||||
|
eccOther->lineParityPrime = line_parity_prime;
|
||||||
|
}
|
||||||
|
|
||||||
|
int yaffs_ECCCorrectOther(unsigned char *data, unsigned nBytes,
|
||||||
|
yaffs_ECCOther * read_ecc,
|
||||||
|
const yaffs_ECCOther * test_ecc)
|
||||||
|
{
|
||||||
|
unsigned char cDelta; /* column parity delta */
|
||||||
|
unsigned lDelta; /* line parity delta */
|
||||||
|
unsigned lDeltaPrime; /* line parity delta */
|
||||||
|
unsigned bit;
|
||||||
|
|
||||||
|
cDelta = read_ecc->colParity ^ test_ecc->colParity;
|
||||||
|
lDelta = read_ecc->lineParity ^ test_ecc->lineParity;
|
||||||
|
lDeltaPrime = read_ecc->lineParityPrime ^ test_ecc->lineParityPrime;
|
||||||
|
|
||||||
|
if ((cDelta | lDelta | lDeltaPrime) == 0)
|
||||||
|
return 0; /* no error */
|
||||||
|
|
||||||
|
if (lDelta == ~lDeltaPrime &&
|
||||||
|
(((cDelta ^ (cDelta >> 1)) & 0x15) == 0x15))
|
||||||
|
{
|
||||||
|
/* Single bit (recoverable) error in data */
|
||||||
|
|
||||||
|
bit = 0;
|
||||||
|
|
||||||
|
if (cDelta & 0x20)
|
||||||
|
bit |= 0x04;
|
||||||
|
if (cDelta & 0x08)
|
||||||
|
bit |= 0x02;
|
||||||
|
if (cDelta & 0x02)
|
||||||
|
bit |= 0x01;
|
||||||
|
|
||||||
|
if(lDelta >= nBytes)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
data[lDelta] ^= (1 << bit);
|
||||||
|
|
||||||
|
return 1; /* corrected */
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((yaffs_CountBits32(lDelta) + yaffs_CountBits32(lDeltaPrime) +
|
||||||
|
yaffs_CountBits(cDelta)) == 1) {
|
||||||
|
/* Reccoverable error in ecc */
|
||||||
|
|
||||||
|
*read_ecc = *test_ecc;
|
||||||
|
return 1; /* corrected */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unrecoverable error */
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
44
target/linux/generic-2.6/files-2.6.27/fs/yaffs2/yaffs_ecc.h
Normal file
44
target/linux/generic-2.6/files-2.6.27/fs/yaffs2/yaffs_ecc.h
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2002-2007 Aleph One Ltd.
|
||||||
|
* for Toby Churchill Ltd and Brightstar Engineering
|
||||||
|
*
|
||||||
|
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This code implements the ECC algorithm used in SmartMedia.
|
||||||
|
*
|
||||||
|
* The ECC comprises 22 bits of parity information and is stuffed into 3 bytes.
|
||||||
|
* The two unused bit are set to 1.
|
||||||
|
* The ECC can correct single bit errors in a 256-byte page of data. Thus, two such ECC
|
||||||
|
* blocks are used on a 512-byte NAND page.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __YAFFS_ECC_H__
|
||||||
|
#define __YAFFS_ECC_H__
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned char colParity;
|
||||||
|
unsigned lineParity;
|
||||||
|
unsigned lineParityPrime;
|
||||||
|
} yaffs_ECCOther;
|
||||||
|
|
||||||
|
void yaffs_ECCCalculate(const unsigned char *data, unsigned char *ecc);
|
||||||
|
int yaffs_ECCCorrect(unsigned char *data, unsigned char *read_ecc,
|
||||||
|
const unsigned char *test_ecc);
|
||||||
|
|
||||||
|
void yaffs_ECCCalculateOther(const unsigned char *data, unsigned nBytes,
|
||||||
|
yaffs_ECCOther * ecc);
|
||||||
|
int yaffs_ECCCorrectOther(unsigned char *data, unsigned nBytes,
|
||||||
|
yaffs_ECCOther * read_ecc,
|
||||||
|
const yaffs_ECCOther * test_ecc);
|
||||||
|
#endif
|
2299
target/linux/generic-2.6/files-2.6.27/fs/yaffs2/yaffs_fs.c
Normal file
2299
target/linux/generic-2.6/files-2.6.27/fs/yaffs2/yaffs_fs.c
Normal file
File diff suppressed because it is too large
Load diff
7469
target/linux/generic-2.6/files-2.6.27/fs/yaffs2/yaffs_guts.c
Normal file
7469
target/linux/generic-2.6/files-2.6.27/fs/yaffs2/yaffs_guts.c
Normal file
File diff suppressed because it is too large
Load diff
902
target/linux/generic-2.6/files-2.6.27/fs/yaffs2/yaffs_guts.h
Normal file
902
target/linux/generic-2.6/files-2.6.27/fs/yaffs2/yaffs_guts.h
Normal file
|
@ -0,0 +1,902 @@
|
||||||
|
/*
|
||||||
|
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2002-2007 Aleph One Ltd.
|
||||||
|
* for Toby Churchill Ltd and Brightstar Engineering
|
||||||
|
*
|
||||||
|
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __YAFFS_GUTS_H__
|
||||||
|
#define __YAFFS_GUTS_H__
|
||||||
|
|
||||||
|
#include "devextras.h"
|
||||||
|
#include "yportenv.h"
|
||||||
|
|
||||||
|
#define YAFFS_OK 1
|
||||||
|
#define YAFFS_FAIL 0
|
||||||
|
|
||||||
|
/* Give us a Y=0x59,
|
||||||
|
* Give us an A=0x41,
|
||||||
|
* Give us an FF=0xFF
|
||||||
|
* Give us an S=0x53
|
||||||
|
* And what have we got...
|
||||||
|
*/
|
||||||
|
#define YAFFS_MAGIC 0x5941FF53
|
||||||
|
|
||||||
|
#define YAFFS_NTNODES_LEVEL0 16
|
||||||
|
#define YAFFS_TNODES_LEVEL0_BITS 4
|
||||||
|
#define YAFFS_TNODES_LEVEL0_MASK 0xf
|
||||||
|
|
||||||
|
#define YAFFS_NTNODES_INTERNAL (YAFFS_NTNODES_LEVEL0 / 2)
|
||||||
|
#define YAFFS_TNODES_INTERNAL_BITS (YAFFS_TNODES_LEVEL0_BITS - 1)
|
||||||
|
#define YAFFS_TNODES_INTERNAL_MASK 0x7
|
||||||
|
#define YAFFS_TNODES_MAX_LEVEL 6
|
||||||
|
|
||||||
|
#ifndef CONFIG_YAFFS_NO_YAFFS1
|
||||||
|
#define YAFFS_BYTES_PER_SPARE 16
|
||||||
|
#define YAFFS_BYTES_PER_CHUNK 512
|
||||||
|
#define YAFFS_CHUNK_SIZE_SHIFT 9
|
||||||
|
#define YAFFS_CHUNKS_PER_BLOCK 32
|
||||||
|
#define YAFFS_BYTES_PER_BLOCK (YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define YAFFS_MIN_YAFFS2_CHUNK_SIZE 1024
|
||||||
|
#define YAFFS_MIN_YAFFS2_SPARE_SIZE 32
|
||||||
|
|
||||||
|
#define YAFFS_MAX_CHUNK_ID 0x000FFFFF
|
||||||
|
|
||||||
|
#define YAFFS_UNUSED_OBJECT_ID 0x0003FFFF
|
||||||
|
|
||||||
|
#define YAFFS_ALLOCATION_NOBJECTS 100
|
||||||
|
#define YAFFS_ALLOCATION_NTNODES 100
|
||||||
|
#define YAFFS_ALLOCATION_NLINKS 100
|
||||||
|
|
||||||
|
#define YAFFS_NOBJECT_BUCKETS 256
|
||||||
|
|
||||||
|
|
||||||
|
#define YAFFS_OBJECT_SPACE 0x40000
|
||||||
|
|
||||||
|
#define YAFFS_CHECKPOINT_VERSION 3
|
||||||
|
|
||||||
|
#ifdef CONFIG_YAFFS_UNICODE
|
||||||
|
#define YAFFS_MAX_NAME_LENGTH 127
|
||||||
|
#define YAFFS_MAX_ALIAS_LENGTH 79
|
||||||
|
#else
|
||||||
|
#define YAFFS_MAX_NAME_LENGTH 255
|
||||||
|
#define YAFFS_MAX_ALIAS_LENGTH 159
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define YAFFS_SHORT_NAME_LENGTH 15
|
||||||
|
|
||||||
|
/* Some special object ids for pseudo objects */
|
||||||
|
#define YAFFS_OBJECTID_ROOT 1
|
||||||
|
#define YAFFS_OBJECTID_LOSTNFOUND 2
|
||||||
|
#define YAFFS_OBJECTID_UNLINKED 3
|
||||||
|
#define YAFFS_OBJECTID_DELETED 4
|
||||||
|
|
||||||
|
/* Sseudo object ids for checkpointing */
|
||||||
|
#define YAFFS_OBJECTID_SB_HEADER 0x10
|
||||||
|
#define YAFFS_OBJECTID_CHECKPOINT_DATA 0x20
|
||||||
|
#define YAFFS_SEQUENCE_CHECKPOINT_DATA 0x21
|
||||||
|
|
||||||
|
/* */
|
||||||
|
|
||||||
|
#define YAFFS_MAX_SHORT_OP_CACHES 20
|
||||||
|
|
||||||
|
#define YAFFS_N_TEMP_BUFFERS 4
|
||||||
|
|
||||||
|
/* We limit the number attempts at sucessfully saving a chunk of data.
|
||||||
|
* Small-page devices have 32 pages per block; large-page devices have 64.
|
||||||
|
* Default to something in the order of 5 to 10 blocks worth of chunks.
|
||||||
|
*/
|
||||||
|
#define YAFFS_WR_ATTEMPTS (5*64)
|
||||||
|
|
||||||
|
/* Sequence numbers are used in YAFFS2 to determine block allocation order.
|
||||||
|
* The range is limited slightly to help distinguish bad numbers from good.
|
||||||
|
* This also allows us to perhaps in the future use special numbers for
|
||||||
|
* special purposes.
|
||||||
|
* EFFFFF00 allows the allocation of 8 blocks per second (~1Mbytes) for 15 years,
|
||||||
|
* and is a larger number than the lifetime of a 2GB device.
|
||||||
|
*/
|
||||||
|
#define YAFFS_LOWEST_SEQUENCE_NUMBER 0x00001000
|
||||||
|
#define YAFFS_HIGHEST_SEQUENCE_NUMBER 0xEFFFFF00
|
||||||
|
|
||||||
|
/* ChunkCache is used for short read/write operations.*/
|
||||||
|
typedef struct {
|
||||||
|
struct yaffs_ObjectStruct *object;
|
||||||
|
int chunkId;
|
||||||
|
int lastUse;
|
||||||
|
int dirty;
|
||||||
|
int nBytes; /* Only valid if the cache is dirty */
|
||||||
|
int locked; /* Can't push out or flush while locked. */
|
||||||
|
#ifdef CONFIG_YAFFS_YAFFS2
|
||||||
|
__u8 *data;
|
||||||
|
#else
|
||||||
|
__u8 data[YAFFS_BYTES_PER_CHUNK];
|
||||||
|
#endif
|
||||||
|
} yaffs_ChunkCache;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Tags structures in RAM
|
||||||
|
* NB This uses bitfield. Bitfields should not straddle a u32 boundary otherwise
|
||||||
|
* the structure size will get blown out.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CONFIG_YAFFS_NO_YAFFS1
|
||||||
|
typedef struct {
|
||||||
|
unsigned chunkId:20;
|
||||||
|
unsigned serialNumber:2;
|
||||||
|
unsigned byteCount:10;
|
||||||
|
unsigned objectId:18;
|
||||||
|
unsigned ecc:12;
|
||||||
|
unsigned unusedStuff:2;
|
||||||
|
|
||||||
|
} yaffs_Tags;
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
yaffs_Tags asTags;
|
||||||
|
__u8 asBytes[8];
|
||||||
|
} yaffs_TagsUnion;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Stuff used for extended tags in YAFFS2 */
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
YAFFS_ECC_RESULT_UNKNOWN,
|
||||||
|
YAFFS_ECC_RESULT_NO_ERROR,
|
||||||
|
YAFFS_ECC_RESULT_FIXED,
|
||||||
|
YAFFS_ECC_RESULT_UNFIXED
|
||||||
|
} yaffs_ECCResult;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
YAFFS_OBJECT_TYPE_UNKNOWN,
|
||||||
|
YAFFS_OBJECT_TYPE_FILE,
|
||||||
|
YAFFS_OBJECT_TYPE_SYMLINK,
|
||||||
|
YAFFS_OBJECT_TYPE_DIRECTORY,
|
||||||
|
YAFFS_OBJECT_TYPE_HARDLINK,
|
||||||
|
YAFFS_OBJECT_TYPE_SPECIAL
|
||||||
|
} yaffs_ObjectType;
|
||||||
|
|
||||||
|
#define YAFFS_OBJECT_TYPE_MAX YAFFS_OBJECT_TYPE_SPECIAL
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
|
||||||
|
unsigned validMarker0;
|
||||||
|
unsigned chunkUsed; /* Status of the chunk: used or unused */
|
||||||
|
unsigned objectId; /* If 0 then this is not part of an object (unused) */
|
||||||
|
unsigned chunkId; /* If 0 then this is a header, else a data chunk */
|
||||||
|
unsigned byteCount; /* Only valid for data chunks */
|
||||||
|
|
||||||
|
/* The following stuff only has meaning when we read */
|
||||||
|
yaffs_ECCResult eccResult;
|
||||||
|
unsigned blockBad;
|
||||||
|
|
||||||
|
/* YAFFS 1 stuff */
|
||||||
|
unsigned chunkDeleted; /* The chunk is marked deleted */
|
||||||
|
unsigned serialNumber; /* Yaffs1 2-bit serial number */
|
||||||
|
|
||||||
|
/* YAFFS2 stuff */
|
||||||
|
unsigned sequenceNumber; /* The sequence number of this block */
|
||||||
|
|
||||||
|
/* Extra info if this is an object header (YAFFS2 only) */
|
||||||
|
|
||||||
|
unsigned extraHeaderInfoAvailable; /* There is extra info available if this is not zero */
|
||||||
|
unsigned extraParentObjectId; /* The parent object */
|
||||||
|
unsigned extraIsShrinkHeader; /* Is it a shrink header? */
|
||||||
|
unsigned extraShadows; /* Does this shadow another object? */
|
||||||
|
|
||||||
|
yaffs_ObjectType extraObjectType; /* What object type? */
|
||||||
|
|
||||||
|
unsigned extraFileLength; /* Length if it is a file */
|
||||||
|
unsigned extraEquivalentObjectId; /* Equivalent object Id if it is a hard link */
|
||||||
|
|
||||||
|
unsigned validMarker1;
|
||||||
|
|
||||||
|
} yaffs_ExtendedTags;
|
||||||
|
|
||||||
|
/* Spare structure for YAFFS1 */
|
||||||
|
typedef struct {
|
||||||
|
__u8 tagByte0;
|
||||||
|
__u8 tagByte1;
|
||||||
|
__u8 tagByte2;
|
||||||
|
__u8 tagByte3;
|
||||||
|
__u8 pageStatus; /* set to 0 to delete the chunk */
|
||||||
|
__u8 blockStatus;
|
||||||
|
__u8 tagByte4;
|
||||||
|
__u8 tagByte5;
|
||||||
|
__u8 ecc1[3];
|
||||||
|
__u8 tagByte6;
|
||||||
|
__u8 tagByte7;
|
||||||
|
__u8 ecc2[3];
|
||||||
|
} yaffs_Spare;
|
||||||
|
|
||||||
|
/*Special structure for passing through to mtd */
|
||||||
|
struct yaffs_NANDSpare {
|
||||||
|
yaffs_Spare spare;
|
||||||
|
int eccres1;
|
||||||
|
int eccres2;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Block data in RAM */
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
YAFFS_BLOCK_STATE_UNKNOWN = 0,
|
||||||
|
|
||||||
|
YAFFS_BLOCK_STATE_SCANNING,
|
||||||
|
YAFFS_BLOCK_STATE_NEEDS_SCANNING,
|
||||||
|
/* The block might have something on it (ie it is allocating or full, perhaps empty)
|
||||||
|
* but it needs to be scanned to determine its true state.
|
||||||
|
* This state is only valid during yaffs_Scan.
|
||||||
|
* NB We tolerate empty because the pre-scanner might be incapable of deciding
|
||||||
|
* However, if this state is returned on a YAFFS2 device, then we expect a sequence number
|
||||||
|
*/
|
||||||
|
|
||||||
|
YAFFS_BLOCK_STATE_EMPTY,
|
||||||
|
/* This block is empty */
|
||||||
|
|
||||||
|
YAFFS_BLOCK_STATE_ALLOCATING,
|
||||||
|
/* This block is partially allocated.
|
||||||
|
* At least one page holds valid data.
|
||||||
|
* This is the one currently being used for page
|
||||||
|
* allocation. Should never be more than one of these
|
||||||
|
*/
|
||||||
|
|
||||||
|
YAFFS_BLOCK_STATE_FULL,
|
||||||
|
/* All the pages in this block have been allocated.
|
||||||
|
*/
|
||||||
|
|
||||||
|
YAFFS_BLOCK_STATE_DIRTY,
|
||||||
|
/* All pages have been allocated and deleted.
|
||||||
|
* Erase me, reuse me.
|
||||||
|
*/
|
||||||
|
|
||||||
|
YAFFS_BLOCK_STATE_CHECKPOINT,
|
||||||
|
/* This block is assigned to holding checkpoint data.
|
||||||
|
*/
|
||||||
|
|
||||||
|
YAFFS_BLOCK_STATE_COLLECTING,
|
||||||
|
/* This block is being garbage collected */
|
||||||
|
|
||||||
|
YAFFS_BLOCK_STATE_DEAD
|
||||||
|
/* This block has failed and is not in use */
|
||||||
|
} yaffs_BlockState;
|
||||||
|
|
||||||
|
#define YAFFS_NUMBER_OF_BLOCK_STATES (YAFFS_BLOCK_STATE_DEAD + 1)
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
|
||||||
|
int softDeletions:10; /* number of soft deleted pages */
|
||||||
|
int pagesInUse:10; /* number of pages in use */
|
||||||
|
yaffs_BlockState blockState:4; /* One of the above block states */
|
||||||
|
__u32 needsRetiring:1; /* Data has failed on this block, need to get valid data off */
|
||||||
|
/* and retire the block. */
|
||||||
|
__u32 skipErasedCheck: 1; /* If this is set we can skip the erased check on this block */
|
||||||
|
__u32 gcPrioritise: 1; /* An ECC check or blank check has failed on this block.
|
||||||
|
It should be prioritised for GC */
|
||||||
|
__u32 chunkErrorStrikes:3; /* How many times we've had ecc etc failures on this block and tried to reuse it */
|
||||||
|
|
||||||
|
#ifdef CONFIG_YAFFS_YAFFS2
|
||||||
|
__u32 hasShrinkHeader:1; /* This block has at least one shrink object header */
|
||||||
|
__u32 sequenceNumber; /* block sequence number for yaffs2 */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} yaffs_BlockInfo;
|
||||||
|
|
||||||
|
/* -------------------------- Object structure -------------------------------*/
|
||||||
|
/* This is the object structure as stored on NAND */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
yaffs_ObjectType type;
|
||||||
|
|
||||||
|
/* Apply to everything */
|
||||||
|
int parentObjectId;
|
||||||
|
__u16 sum__NoLongerUsed; /* checksum of name. No longer used */
|
||||||
|
YCHAR name[YAFFS_MAX_NAME_LENGTH + 1];
|
||||||
|
|
||||||
|
/* Thes following apply to directories, files, symlinks - not hard links */
|
||||||
|
__u32 yst_mode; /* protection */
|
||||||
|
|
||||||
|
#ifdef CONFIG_YAFFS_WINCE
|
||||||
|
__u32 notForWinCE[5];
|
||||||
|
#else
|
||||||
|
__u32 yst_uid;
|
||||||
|
__u32 yst_gid;
|
||||||
|
__u32 yst_atime;
|
||||||
|
__u32 yst_mtime;
|
||||||
|
__u32 yst_ctime;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* File size applies to files only */
|
||||||
|
int fileSize;
|
||||||
|
|
||||||
|
/* Equivalent object id applies to hard links only. */
|
||||||
|
int equivalentObjectId;
|
||||||
|
|
||||||
|
/* Alias is for symlinks only. */
|
||||||
|
YCHAR alias[YAFFS_MAX_ALIAS_LENGTH + 1];
|
||||||
|
|
||||||
|
__u32 yst_rdev; /* device stuff for block and char devices (major/min) */
|
||||||
|
|
||||||
|
#ifdef CONFIG_YAFFS_WINCE
|
||||||
|
__u32 win_ctime[2];
|
||||||
|
__u32 win_atime[2];
|
||||||
|
__u32 win_mtime[2];
|
||||||
|
__u32 roomToGrow[4];
|
||||||
|
#else
|
||||||
|
__u32 roomToGrow[10];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int shadowsObject; /* This object header shadows the specified object if > 0 */
|
||||||
|
|
||||||
|
/* isShrink applies to object headers written when we shrink the file (ie resize) */
|
||||||
|
__u32 isShrink;
|
||||||
|
|
||||||
|
} yaffs_ObjectHeader;
|
||||||
|
|
||||||
|
/*--------------------------- Tnode -------------------------- */
|
||||||
|
|
||||||
|
union yaffs_Tnode_union {
|
||||||
|
#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
|
||||||
|
union yaffs_Tnode_union *internal[YAFFS_NTNODES_INTERNAL + 1];
|
||||||
|
#else
|
||||||
|
union yaffs_Tnode_union *internal[YAFFS_NTNODES_INTERNAL];
|
||||||
|
#endif
|
||||||
|
/* __u16 level0[YAFFS_NTNODES_LEVEL0]; */
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef union yaffs_Tnode_union yaffs_Tnode;
|
||||||
|
|
||||||
|
struct yaffs_TnodeList_struct {
|
||||||
|
struct yaffs_TnodeList_struct *next;
|
||||||
|
yaffs_Tnode *tnodes;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct yaffs_TnodeList_struct yaffs_TnodeList;
|
||||||
|
|
||||||
|
/*------------------------ Object -----------------------------*/
|
||||||
|
/* An object can be one of:
|
||||||
|
* - a directory (no data, has children links
|
||||||
|
* - a regular file (data.... not prunes :->).
|
||||||
|
* - a symlink [symbolic link] (the alias).
|
||||||
|
* - a hard link
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
__u32 fileSize;
|
||||||
|
__u32 scannedFileSize;
|
||||||
|
__u32 shrinkSize;
|
||||||
|
int topLevel;
|
||||||
|
yaffs_Tnode *top;
|
||||||
|
} yaffs_FileStructure;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
struct list_head children; /* list of child links */
|
||||||
|
} yaffs_DirectoryStructure;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
YCHAR *alias;
|
||||||
|
} yaffs_SymLinkStructure;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
struct yaffs_ObjectStruct *equivalentObject;
|
||||||
|
__u32 equivalentObjectId;
|
||||||
|
} yaffs_HardLinkStructure;
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
yaffs_FileStructure fileVariant;
|
||||||
|
yaffs_DirectoryStructure directoryVariant;
|
||||||
|
yaffs_SymLinkStructure symLinkVariant;
|
||||||
|
yaffs_HardLinkStructure hardLinkVariant;
|
||||||
|
} yaffs_ObjectVariant;
|
||||||
|
|
||||||
|
struct yaffs_ObjectStruct {
|
||||||
|
__u8 deleted:1; /* This should only apply to unlinked files. */
|
||||||
|
__u8 softDeleted:1; /* it has also been soft deleted */
|
||||||
|
__u8 unlinked:1; /* An unlinked file. The file should be in the unlinked directory.*/
|
||||||
|
__u8 fake:1; /* A fake object has no presence on NAND. */
|
||||||
|
__u8 renameAllowed:1; /* Some objects are not allowed to be renamed. */
|
||||||
|
__u8 unlinkAllowed:1;
|
||||||
|
__u8 dirty:1; /* the object needs to be written to flash */
|
||||||
|
__u8 valid:1; /* When the file system is being loaded up, this
|
||||||
|
* object might be created before the data
|
||||||
|
* is available (ie. file data records appear before the header).
|
||||||
|
*/
|
||||||
|
__u8 lazyLoaded:1; /* This object has been lazy loaded and is missing some detail */
|
||||||
|
|
||||||
|
__u8 deferedFree:1; /* For Linux kernel. Object is removed from NAND, but is
|
||||||
|
* still in the inode cache. Free of object is defered.
|
||||||
|
* until the inode is released.
|
||||||
|
*/
|
||||||
|
|
||||||
|
__u8 serial; /* serial number of chunk in NAND. Cached here */
|
||||||
|
__u16 sum; /* sum of the name to speed searching */
|
||||||
|
|
||||||
|
struct yaffs_DeviceStruct *myDev; /* The device I'm on */
|
||||||
|
|
||||||
|
struct list_head hashLink; /* list of objects in this hash bucket */
|
||||||
|
|
||||||
|
struct list_head hardLinks; /* all the equivalent hard linked objects */
|
||||||
|
|
||||||
|
/* directory structure stuff */
|
||||||
|
/* also used for linking up the free list */
|
||||||
|
struct yaffs_ObjectStruct *parent;
|
||||||
|
struct list_head siblings;
|
||||||
|
|
||||||
|
/* Where's my object header in NAND? */
|
||||||
|
int chunkId;
|
||||||
|
|
||||||
|
int nDataChunks; /* Number of data chunks attached to the file. */
|
||||||
|
|
||||||
|
__u32 objectId; /* the object id value */
|
||||||
|
|
||||||
|
__u32 yst_mode;
|
||||||
|
|
||||||
|
#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM
|
||||||
|
YCHAR shortName[YAFFS_SHORT_NAME_LENGTH + 1];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __KERNEL__
|
||||||
|
__u32 inUse;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_YAFFS_WINCE
|
||||||
|
__u32 win_ctime[2];
|
||||||
|
__u32 win_mtime[2];
|
||||||
|
__u32 win_atime[2];
|
||||||
|
#else
|
||||||
|
__u32 yst_uid;
|
||||||
|
__u32 yst_gid;
|
||||||
|
__u32 yst_atime;
|
||||||
|
__u32 yst_mtime;
|
||||||
|
__u32 yst_ctime;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
__u32 yst_rdev;
|
||||||
|
|
||||||
|
#ifdef __KERNEL__
|
||||||
|
struct inode *myInode;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
yaffs_ObjectType variantType;
|
||||||
|
|
||||||
|
yaffs_ObjectVariant variant;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct yaffs_ObjectStruct yaffs_Object;
|
||||||
|
|
||||||
|
struct yaffs_ObjectList_struct {
|
||||||
|
yaffs_Object *objects;
|
||||||
|
struct yaffs_ObjectList_struct *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct yaffs_ObjectList_struct yaffs_ObjectList;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
struct list_head list;
|
||||||
|
int count;
|
||||||
|
} yaffs_ObjectBucket;
|
||||||
|
|
||||||
|
|
||||||
|
/* yaffs_CheckpointObject holds the definition of an object as dumped
|
||||||
|
* by checkpointing.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int structType;
|
||||||
|
__u32 objectId;
|
||||||
|
__u32 parentId;
|
||||||
|
int chunkId;
|
||||||
|
|
||||||
|
yaffs_ObjectType variantType:3;
|
||||||
|
__u8 deleted:1;
|
||||||
|
__u8 softDeleted:1;
|
||||||
|
__u8 unlinked:1;
|
||||||
|
__u8 fake:1;
|
||||||
|
__u8 renameAllowed:1;
|
||||||
|
__u8 unlinkAllowed:1;
|
||||||
|
__u8 serial;
|
||||||
|
|
||||||
|
int nDataChunks;
|
||||||
|
__u32 fileSizeOrEquivalentObjectId;
|
||||||
|
|
||||||
|
}yaffs_CheckpointObject;
|
||||||
|
|
||||||
|
/*--------------------- Temporary buffers ----------------
|
||||||
|
*
|
||||||
|
* These are chunk-sized working buffers. Each device has a few
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
__u8 *buffer;
|
||||||
|
int line; /* track from whence this buffer was allocated */
|
||||||
|
int maxLine;
|
||||||
|
} yaffs_TempBuffer;
|
||||||
|
|
||||||
|
/*----------------- Device ---------------------------------*/
|
||||||
|
|
||||||
|
struct yaffs_DeviceStruct {
|
||||||
|
struct list_head devList;
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
/* Entry parameters set up way early. Yaffs sets up the rest.*/
|
||||||
|
int nDataBytesPerChunk; /* Should be a power of 2 >= 512 */
|
||||||
|
int nChunksPerBlock; /* does not need to be a power of 2 */
|
||||||
|
int nBytesPerSpare; /* spare area size */
|
||||||
|
int startBlock; /* Start block we're allowed to use */
|
||||||
|
int endBlock; /* End block we're allowed to use */
|
||||||
|
int nReservedBlocks; /* We want this tuneable so that we can reduce */
|
||||||
|
/* reserved blocks on NOR and RAM. */
|
||||||
|
|
||||||
|
|
||||||
|
/* Stuff used by the shared space checkpointing mechanism */
|
||||||
|
/* If this value is zero, then this mechanism is disabled */
|
||||||
|
|
||||||
|
int nCheckpointReservedBlocks; /* Blocks to reserve for checkpoint data */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int nShortOpCaches; /* If <= 0, then short op caching is disabled, else
|
||||||
|
* the number of short op caches (don't use too many)
|
||||||
|
*/
|
||||||
|
|
||||||
|
int useHeaderFileSize; /* Flag to determine if we should use file sizes from the header */
|
||||||
|
|
||||||
|
int useNANDECC; /* Flag to decide whether or not to use NANDECC */
|
||||||
|
|
||||||
|
void *genericDevice; /* Pointer to device context
|
||||||
|
* On an mtd this holds the mtd pointer.
|
||||||
|
*/
|
||||||
|
void *superBlock;
|
||||||
|
|
||||||
|
/* NAND access functions (Must be set before calling YAFFS)*/
|
||||||
|
|
||||||
|
int (*writeChunkToNAND) (struct yaffs_DeviceStruct * dev,
|
||||||
|
int chunkInNAND, const __u8 * data,
|
||||||
|
const yaffs_Spare * spare);
|
||||||
|
int (*readChunkFromNAND) (struct yaffs_DeviceStruct * dev,
|
||||||
|
int chunkInNAND, __u8 * data,
|
||||||
|
yaffs_Spare * spare);
|
||||||
|
int (*eraseBlockInNAND) (struct yaffs_DeviceStruct * dev,
|
||||||
|
int blockInNAND);
|
||||||
|
int (*initialiseNAND) (struct yaffs_DeviceStruct * dev);
|
||||||
|
|
||||||
|
#ifdef CONFIG_YAFFS_YAFFS2
|
||||||
|
int (*writeChunkWithTagsToNAND) (struct yaffs_DeviceStruct * dev,
|
||||||
|
int chunkInNAND, const __u8 * data,
|
||||||
|
const yaffs_ExtendedTags * tags);
|
||||||
|
int (*readChunkWithTagsFromNAND) (struct yaffs_DeviceStruct * dev,
|
||||||
|
int chunkInNAND, __u8 * data,
|
||||||
|
yaffs_ExtendedTags * tags);
|
||||||
|
int (*markNANDBlockBad) (struct yaffs_DeviceStruct * dev, int blockNo);
|
||||||
|
int (*queryNANDBlock) (struct yaffs_DeviceStruct * dev, int blockNo,
|
||||||
|
yaffs_BlockState * state, int *sequenceNumber);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int isYaffs2;
|
||||||
|
|
||||||
|
/* The removeObjectCallback function must be supplied by OS flavours that
|
||||||
|
* need it. The Linux kernel does not use this, but yaffs direct does use
|
||||||
|
* it to implement the faster readdir
|
||||||
|
*/
|
||||||
|
void (*removeObjectCallback)(struct yaffs_ObjectStruct *obj);
|
||||||
|
|
||||||
|
/* Callback to mark the superblock dirsty */
|
||||||
|
void (*markSuperBlockDirty)(void * superblock);
|
||||||
|
|
||||||
|
int wideTnodesDisabled; /* Set to disable wide tnodes */
|
||||||
|
|
||||||
|
|
||||||
|
/* End of stuff that must be set before initialisation. */
|
||||||
|
|
||||||
|
/* Checkpoint control. Can be set before or after initialisation */
|
||||||
|
__u8 skipCheckpointRead;
|
||||||
|
__u8 skipCheckpointWrite;
|
||||||
|
|
||||||
|
/* Runtime parameters. Set up by YAFFS. */
|
||||||
|
|
||||||
|
__u16 chunkGroupBits; /* 0 for devices <= 32MB. else log2(nchunks) - 16 */
|
||||||
|
__u16 chunkGroupSize; /* == 2^^chunkGroupBits */
|
||||||
|
|
||||||
|
/* Stuff to support wide tnodes */
|
||||||
|
__u32 tnodeWidth;
|
||||||
|
__u32 tnodeMask;
|
||||||
|
|
||||||
|
/* Stuff to support various file offses to chunk/offset translations */
|
||||||
|
/* "Crumbs" for nDataBytesPerChunk not being a power of 2 */
|
||||||
|
__u32 crumbMask;
|
||||||
|
__u32 crumbShift;
|
||||||
|
__u32 crumbsPerChunk;
|
||||||
|
|
||||||
|
/* Straight shifting for nDataBytesPerChunk being a power of 2 */
|
||||||
|
__u32 chunkShift;
|
||||||
|
__u32 chunkMask;
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __KERNEL__
|
||||||
|
|
||||||
|
struct semaphore sem; /* Semaphore for waiting on erasure.*/
|
||||||
|
struct semaphore grossLock; /* Gross locking semaphore */
|
||||||
|
__u8 *spareBuffer; /* For mtdif2 use. Don't know the size of the buffer
|
||||||
|
* at compile time so we have to allocate it.
|
||||||
|
*/
|
||||||
|
void (*putSuperFunc) (struct super_block * sb);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int isMounted;
|
||||||
|
|
||||||
|
int isCheckpointed;
|
||||||
|
|
||||||
|
|
||||||
|
/* Stuff to support block offsetting to support start block zero */
|
||||||
|
int internalStartBlock;
|
||||||
|
int internalEndBlock;
|
||||||
|
int blockOffset;
|
||||||
|
int chunkOffset;
|
||||||
|
|
||||||
|
|
||||||
|
/* Runtime checkpointing stuff */
|
||||||
|
int checkpointPageSequence; /* running sequence number of checkpoint pages */
|
||||||
|
int checkpointByteCount;
|
||||||
|
int checkpointByteOffset;
|
||||||
|
__u8 *checkpointBuffer;
|
||||||
|
int checkpointOpenForWrite;
|
||||||
|
int blocksInCheckpoint;
|
||||||
|
int checkpointCurrentChunk;
|
||||||
|
int checkpointCurrentBlock;
|
||||||
|
int checkpointNextBlock;
|
||||||
|
int *checkpointBlockList;
|
||||||
|
int checkpointMaxBlocks;
|
||||||
|
__u32 checkpointSum;
|
||||||
|
__u32 checkpointXor;
|
||||||
|
|
||||||
|
/* Block Info */
|
||||||
|
yaffs_BlockInfo *blockInfo;
|
||||||
|
__u8 *chunkBits; /* bitmap of chunks in use */
|
||||||
|
unsigned blockInfoAlt:1; /* was allocated using alternative strategy */
|
||||||
|
unsigned chunkBitsAlt:1; /* was allocated using alternative strategy */
|
||||||
|
int chunkBitmapStride; /* Number of bytes of chunkBits per block.
|
||||||
|
* Must be consistent with nChunksPerBlock.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int nErasedBlocks;
|
||||||
|
int allocationBlock; /* Current block being allocated off */
|
||||||
|
__u32 allocationPage;
|
||||||
|
int allocationBlockFinder; /* Used to search for next allocation block */
|
||||||
|
|
||||||
|
/* Runtime state */
|
||||||
|
int nTnodesCreated;
|
||||||
|
yaffs_Tnode *freeTnodes;
|
||||||
|
int nFreeTnodes;
|
||||||
|
yaffs_TnodeList *allocatedTnodeList;
|
||||||
|
|
||||||
|
int isDoingGC;
|
||||||
|
|
||||||
|
int nObjectsCreated;
|
||||||
|
yaffs_Object *freeObjects;
|
||||||
|
int nFreeObjects;
|
||||||
|
|
||||||
|
yaffs_ObjectList *allocatedObjectList;
|
||||||
|
|
||||||
|
yaffs_ObjectBucket objectBucket[YAFFS_NOBJECT_BUCKETS];
|
||||||
|
|
||||||
|
int nFreeChunks;
|
||||||
|
|
||||||
|
int currentDirtyChecker; /* Used to find current dirtiest block */
|
||||||
|
|
||||||
|
__u32 *gcCleanupList; /* objects to delete at the end of a GC. */
|
||||||
|
int nonAggressiveSkip; /* GC state/mode */
|
||||||
|
|
||||||
|
/* Statistcs */
|
||||||
|
int nPageWrites;
|
||||||
|
int nPageReads;
|
||||||
|
int nBlockErasures;
|
||||||
|
int nErasureFailures;
|
||||||
|
int nGCCopies;
|
||||||
|
int garbageCollections;
|
||||||
|
int passiveGarbageCollections;
|
||||||
|
int nRetriedWrites;
|
||||||
|
int nRetiredBlocks;
|
||||||
|
int eccFixed;
|
||||||
|
int eccUnfixed;
|
||||||
|
int tagsEccFixed;
|
||||||
|
int tagsEccUnfixed;
|
||||||
|
int nDeletions;
|
||||||
|
int nUnmarkedDeletions;
|
||||||
|
|
||||||
|
int hasPendingPrioritisedGCs; /* We think this device might have pending prioritised gcs */
|
||||||
|
|
||||||
|
/* Special directories */
|
||||||
|
yaffs_Object *rootDir;
|
||||||
|
yaffs_Object *lostNFoundDir;
|
||||||
|
|
||||||
|
/* Buffer areas for storing data to recover from write failures TODO
|
||||||
|
* __u8 bufferedData[YAFFS_CHUNKS_PER_BLOCK][YAFFS_BYTES_PER_CHUNK];
|
||||||
|
* yaffs_Spare bufferedSpare[YAFFS_CHUNKS_PER_BLOCK];
|
||||||
|
*/
|
||||||
|
|
||||||
|
int bufferedBlock; /* Which block is buffered here? */
|
||||||
|
int doingBufferedBlockRewrite;
|
||||||
|
|
||||||
|
yaffs_ChunkCache *srCache;
|
||||||
|
int srLastUse;
|
||||||
|
|
||||||
|
int cacheHits;
|
||||||
|
|
||||||
|
/* Stuff for background deletion and unlinked files.*/
|
||||||
|
yaffs_Object *unlinkedDir; /* Directory where unlinked and deleted files live. */
|
||||||
|
yaffs_Object *deletedDir; /* Directory where deleted objects are sent to disappear. */
|
||||||
|
yaffs_Object *unlinkedDeletion; /* Current file being background deleted.*/
|
||||||
|
int nDeletedFiles; /* Count of files awaiting deletion;*/
|
||||||
|
int nUnlinkedFiles; /* Count of unlinked files. */
|
||||||
|
int nBackgroundDeletions; /* Count of background deletions. */
|
||||||
|
|
||||||
|
|
||||||
|
yaffs_TempBuffer tempBuffer[YAFFS_N_TEMP_BUFFERS];
|
||||||
|
int maxTemp;
|
||||||
|
int unmanagedTempAllocations;
|
||||||
|
int unmanagedTempDeallocations;
|
||||||
|
|
||||||
|
/* yaffs2 runtime stuff */
|
||||||
|
unsigned sequenceNumber; /* Sequence number of currently allocating block */
|
||||||
|
unsigned oldestDirtySequence;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct yaffs_DeviceStruct yaffs_Device;
|
||||||
|
|
||||||
|
/* The static layout of bllock usage etc is stored in the super block header */
|
||||||
|
typedef struct {
|
||||||
|
int StructType;
|
||||||
|
int version;
|
||||||
|
int checkpointStartBlock;
|
||||||
|
int checkpointEndBlock;
|
||||||
|
int startBlock;
|
||||||
|
int endBlock;
|
||||||
|
int rfu[100];
|
||||||
|
} yaffs_SuperBlockHeader;
|
||||||
|
|
||||||
|
/* The CheckpointDevice structure holds the device information that changes at runtime and
|
||||||
|
* must be preserved over unmount/mount cycles.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
int structType;
|
||||||
|
int nErasedBlocks;
|
||||||
|
int allocationBlock; /* Current block being allocated off */
|
||||||
|
__u32 allocationPage;
|
||||||
|
int nFreeChunks;
|
||||||
|
|
||||||
|
int nDeletedFiles; /* Count of files awaiting deletion;*/
|
||||||
|
int nUnlinkedFiles; /* Count of unlinked files. */
|
||||||
|
int nBackgroundDeletions; /* Count of background deletions. */
|
||||||
|
|
||||||
|
/* yaffs2 runtime stuff */
|
||||||
|
unsigned sequenceNumber; /* Sequence number of currently allocating block */
|
||||||
|
unsigned oldestDirtySequence;
|
||||||
|
|
||||||
|
} yaffs_CheckpointDevice;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int structType;
|
||||||
|
__u32 magic;
|
||||||
|
__u32 version;
|
||||||
|
__u32 head;
|
||||||
|
} yaffs_CheckpointValidity;
|
||||||
|
|
||||||
|
/* Function to manipulate block info */
|
||||||
|
static Y_INLINE yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device * dev, int blk)
|
||||||
|
{
|
||||||
|
if (blk < dev->internalStartBlock || blk > dev->internalEndBlock) {
|
||||||
|
T(YAFFS_TRACE_ERROR,
|
||||||
|
(TSTR
|
||||||
|
("**>> yaffs: getBlockInfo block %d is not valid" TENDSTR),
|
||||||
|
blk));
|
||||||
|
YBUG();
|
||||||
|
}
|
||||||
|
return &dev->blockInfo[blk - dev->internalStartBlock];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------- YAFFS Functions -----------------------*/
|
||||||
|
|
||||||
|
int yaffs_GutsInitialise(yaffs_Device * dev);
|
||||||
|
void yaffs_Deinitialise(yaffs_Device * dev);
|
||||||
|
|
||||||
|
int yaffs_GetNumberOfFreeChunks(yaffs_Device * dev);
|
||||||
|
|
||||||
|
int yaffs_RenameObject(yaffs_Object * oldDir, const YCHAR * oldName,
|
||||||
|
yaffs_Object * newDir, const YCHAR * newName);
|
||||||
|
|
||||||
|
int yaffs_Unlink(yaffs_Object * dir, const YCHAR * name);
|
||||||
|
int yaffs_DeleteFile(yaffs_Object * obj);
|
||||||
|
|
||||||
|
int yaffs_GetObjectName(yaffs_Object * obj, YCHAR * name, int buffSize);
|
||||||
|
int yaffs_GetObjectFileLength(yaffs_Object * obj);
|
||||||
|
int yaffs_GetObjectInode(yaffs_Object * obj);
|
||||||
|
unsigned yaffs_GetObjectType(yaffs_Object * obj);
|
||||||
|
int yaffs_GetObjectLinkCount(yaffs_Object * obj);
|
||||||
|
|
||||||
|
int yaffs_SetAttributes(yaffs_Object * obj, struct iattr *attr);
|
||||||
|
int yaffs_GetAttributes(yaffs_Object * obj, struct iattr *attr);
|
||||||
|
|
||||||
|
/* File operations */
|
||||||
|
int yaffs_ReadDataFromFile(yaffs_Object * obj, __u8 * buffer, loff_t offset,
|
||||||
|
int nBytes);
|
||||||
|
int yaffs_WriteDataToFile(yaffs_Object * obj, const __u8 * buffer, loff_t offset,
|
||||||
|
int nBytes, int writeThrough);
|
||||||
|
int yaffs_ResizeFile(yaffs_Object * obj, loff_t newSize);
|
||||||
|
|
||||||
|
yaffs_Object *yaffs_MknodFile(yaffs_Object * parent, const YCHAR * name,
|
||||||
|
__u32 mode, __u32 uid, __u32 gid);
|
||||||
|
int yaffs_FlushFile(yaffs_Object * obj, int updateTime);
|
||||||
|
|
||||||
|
/* Flushing and checkpointing */
|
||||||
|
void yaffs_FlushEntireDeviceCache(yaffs_Device *dev);
|
||||||
|
|
||||||
|
int yaffs_CheckpointSave(yaffs_Device *dev);
|
||||||
|
int yaffs_CheckpointRestore(yaffs_Device *dev);
|
||||||
|
|
||||||
|
/* Directory operations */
|
||||||
|
yaffs_Object *yaffs_MknodDirectory(yaffs_Object * parent, const YCHAR * name,
|
||||||
|
__u32 mode, __u32 uid, __u32 gid);
|
||||||
|
yaffs_Object *yaffs_FindObjectByName(yaffs_Object * theDir, const YCHAR * name);
|
||||||
|
int yaffs_ApplyToDirectoryChildren(yaffs_Object * theDir,
|
||||||
|
int (*fn) (yaffs_Object *));
|
||||||
|
|
||||||
|
yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device * dev, __u32 number);
|
||||||
|
|
||||||
|
/* Link operations */
|
||||||
|
yaffs_Object *yaffs_Link(yaffs_Object * parent, const YCHAR * name,
|
||||||
|
yaffs_Object * equivalentObject);
|
||||||
|
|
||||||
|
yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object * obj);
|
||||||
|
|
||||||
|
/* Symlink operations */
|
||||||
|
yaffs_Object *yaffs_MknodSymLink(yaffs_Object * parent, const YCHAR * name,
|
||||||
|
__u32 mode, __u32 uid, __u32 gid,
|
||||||
|
const YCHAR * alias);
|
||||||
|
YCHAR *yaffs_GetSymlinkAlias(yaffs_Object * obj);
|
||||||
|
|
||||||
|
/* Special inodes (fifos, sockets and devices) */
|
||||||
|
yaffs_Object *yaffs_MknodSpecial(yaffs_Object * parent, const YCHAR * name,
|
||||||
|
__u32 mode, __u32 uid, __u32 gid, __u32 rdev);
|
||||||
|
|
||||||
|
/* Special directories */
|
||||||
|
yaffs_Object *yaffs_Root(yaffs_Device * dev);
|
||||||
|
yaffs_Object *yaffs_LostNFound(yaffs_Device * dev);
|
||||||
|
|
||||||
|
#ifdef CONFIG_YAFFS_WINCE
|
||||||
|
/* CONFIG_YAFFS_WINCE special stuff */
|
||||||
|
void yfsd_WinFileTimeNow(__u32 target[2]);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __KERNEL__
|
||||||
|
|
||||||
|
void yaffs_HandleDeferedFree(yaffs_Object * obj);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Debug dump */
|
||||||
|
int yaffs_DumpObject(yaffs_Object * obj);
|
||||||
|
|
||||||
|
void yaffs_GutsTest(yaffs_Device * dev);
|
||||||
|
|
||||||
|
/* A few useful functions */
|
||||||
|
void yaffs_InitialiseTags(yaffs_ExtendedTags * tags);
|
||||||
|
void yaffs_DeleteChunk(yaffs_Device * dev, int chunkId, int markNAND, int lyn);
|
||||||
|
int yaffs_CheckFF(__u8 * buffer, int nBytes);
|
||||||
|
void yaffs_HandleChunkError(yaffs_Device *dev, yaffs_BlockInfo *bi);
|
||||||
|
|
||||||
|
#endif
|
241
target/linux/generic-2.6/files-2.6.27/fs/yaffs2/yaffs_mtdif.c
Normal file
241
target/linux/generic-2.6/files-2.6.27/fs/yaffs2/yaffs_mtdif.c
Normal file
|
@ -0,0 +1,241 @@
|
||||||
|
/*
|
||||||
|
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2002-2007 Aleph One Ltd.
|
||||||
|
* for Toby Churchill Ltd and Brightstar Engineering
|
||||||
|
*
|
||||||
|
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const char *yaffs_mtdif_c_version =
|
||||||
|
"$Id: yaffs_mtdif.c,v 1.19 2007-02-14 01:09:06 wookey Exp $";
|
||||||
|
|
||||||
|
#include "yportenv.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include "yaffs_mtdif.h"
|
||||||
|
|
||||||
|
#include "linux/mtd/mtd.h"
|
||||||
|
#include "linux/types.h"
|
||||||
|
#include "linux/time.h"
|
||||||
|
#include "linux/mtd/nand.h"
|
||||||
|
|
||||||
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18))
|
||||||
|
static struct nand_oobinfo yaffs_oobinfo = {
|
||||||
|
.useecc = 1,
|
||||||
|
.eccbytes = 6,
|
||||||
|
.eccpos = {8, 9, 10, 13, 14, 15}
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct nand_oobinfo yaffs_noeccinfo = {
|
||||||
|
.useecc = 0,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
|
||||||
|
static inline void translate_spare2oob(const yaffs_Spare *spare, __u8 *oob)
|
||||||
|
{
|
||||||
|
oob[0] = spare->tagByte0;
|
||||||
|
oob[1] = spare->tagByte1;
|
||||||
|
oob[2] = spare->tagByte2;
|
||||||
|
oob[3] = spare->tagByte3;
|
||||||
|
oob[4] = spare->tagByte4;
|
||||||
|
oob[5] = spare->tagByte5 & 0x3f;
|
||||||
|
oob[5] |= spare->blockStatus == 'Y' ? 0: 0x80;
|
||||||
|
oob[5] |= spare->pageStatus == 0 ? 0: 0x40;
|
||||||
|
oob[6] = spare->tagByte6;
|
||||||
|
oob[7] = spare->tagByte7;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void translate_oob2spare(yaffs_Spare *spare, __u8 *oob)
|
||||||
|
{
|
||||||
|
struct yaffs_NANDSpare *nspare = (struct yaffs_NANDSpare *)spare;
|
||||||
|
spare->tagByte0 = oob[0];
|
||||||
|
spare->tagByte1 = oob[1];
|
||||||
|
spare->tagByte2 = oob[2];
|
||||||
|
spare->tagByte3 = oob[3];
|
||||||
|
spare->tagByte4 = oob[4];
|
||||||
|
spare->tagByte5 = oob[5] == 0xff ? 0xff : oob[5] & 0x3f;
|
||||||
|
spare->blockStatus = oob[5] & 0x80 ? 0xff : 'Y';
|
||||||
|
spare->pageStatus = oob[5] & 0x40 ? 0xff : 0;
|
||||||
|
spare->ecc1[0] = spare->ecc1[1] = spare->ecc1[2] = 0xff;
|
||||||
|
spare->tagByte6 = oob[6];
|
||||||
|
spare->tagByte7 = oob[7];
|
||||||
|
spare->ecc2[0] = spare->ecc2[1] = spare->ecc2[2] = 0xff;
|
||||||
|
|
||||||
|
nspare->eccres1 = nspare->eccres2 = 0; /* FIXME */
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int nandmtd_WriteChunkToNAND(yaffs_Device * dev, int chunkInNAND,
|
||||||
|
const __u8 * data, const yaffs_Spare * spare)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
|
||||||
|
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
|
||||||
|
struct mtd_oob_ops ops;
|
||||||
|
#endif
|
||||||
|
size_t dummy;
|
||||||
|
int retval = 0;
|
||||||
|
|
||||||
|
loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk;
|
||||||
|
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
|
||||||
|
__u8 spareAsBytes[8]; /* OOB */
|
||||||
|
|
||||||
|
if (data && !spare)
|
||||||
|
retval = mtd->write(mtd, addr, dev->nDataBytesPerChunk,
|
||||||
|
&dummy, data);
|
||||||
|
else if (spare) {
|
||||||
|
if (dev->useNANDECC) {
|
||||||
|
translate_spare2oob(spare, spareAsBytes);
|
||||||
|
ops.mode = MTD_OOB_AUTO;
|
||||||
|
ops.ooblen = 8; /* temp hack */
|
||||||
|
} else {
|
||||||
|
ops.mode = MTD_OOB_RAW;
|
||||||
|
ops.ooblen = YAFFS_BYTES_PER_SPARE;
|
||||||
|
}
|
||||||
|
ops.len = data ? dev->nDataBytesPerChunk : ops.ooblen;
|
||||||
|
ops.datbuf = (u8 *)data;
|
||||||
|
ops.ooboffs = 0;
|
||||||
|
ops.oobbuf = spareAsBytes;
|
||||||
|
retval = mtd->write_oob(mtd, addr, &ops);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
__u8 *spareAsBytes = (__u8 *) spare;
|
||||||
|
|
||||||
|
if (data && spare) {
|
||||||
|
if (dev->useNANDECC)
|
||||||
|
retval =
|
||||||
|
mtd->write_ecc(mtd, addr, dev->nDataBytesPerChunk,
|
||||||
|
&dummy, data, spareAsBytes,
|
||||||
|
&yaffs_oobinfo);
|
||||||
|
else
|
||||||
|
retval =
|
||||||
|
mtd->write_ecc(mtd, addr, dev->nDataBytesPerChunk,
|
||||||
|
&dummy, data, spareAsBytes,
|
||||||
|
&yaffs_noeccinfo);
|
||||||
|
} else {
|
||||||
|
if (data)
|
||||||
|
retval =
|
||||||
|
mtd->write(mtd, addr, dev->nDataBytesPerChunk, &dummy,
|
||||||
|
data);
|
||||||
|
if (spare)
|
||||||
|
retval =
|
||||||
|
mtd->write_oob(mtd, addr, YAFFS_BYTES_PER_SPARE,
|
||||||
|
&dummy, spareAsBytes);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (retval == 0)
|
||||||
|
return YAFFS_OK;
|
||||||
|
else
|
||||||
|
return YAFFS_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nandmtd_ReadChunkFromNAND(yaffs_Device * dev, int chunkInNAND, __u8 * data,
|
||||||
|
yaffs_Spare * spare)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
|
||||||
|
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
|
||||||
|
struct mtd_oob_ops ops;
|
||||||
|
#endif
|
||||||
|
size_t dummy;
|
||||||
|
int retval = 0;
|
||||||
|
|
||||||
|
loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk;
|
||||||
|
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
|
||||||
|
__u8 spareAsBytes[8]; /* OOB */
|
||||||
|
|
||||||
|
if (data && !spare)
|
||||||
|
retval = mtd->read(mtd, addr, dev->nDataBytesPerChunk,
|
||||||
|
&dummy, data);
|
||||||
|
else if (spare) {
|
||||||
|
if (dev->useNANDECC) {
|
||||||
|
ops.mode = MTD_OOB_AUTO;
|
||||||
|
ops.ooblen = 8; /* temp hack */
|
||||||
|
} else {
|
||||||
|
ops.mode = MTD_OOB_RAW;
|
||||||
|
ops.ooblen = YAFFS_BYTES_PER_SPARE;
|
||||||
|
}
|
||||||
|
ops.len = data ? dev->nDataBytesPerChunk : ops.ooblen;
|
||||||
|
ops.datbuf = data;
|
||||||
|
ops.ooboffs = 0;
|
||||||
|
ops.oobbuf = spareAsBytes;
|
||||||
|
retval = mtd->read_oob(mtd, addr, &ops);
|
||||||
|
if (dev->useNANDECC)
|
||||||
|
translate_oob2spare(spare, spareAsBytes);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
__u8 *spareAsBytes = (__u8 *) spare;
|
||||||
|
|
||||||
|
if (data && spare) {
|
||||||
|
if (dev->useNANDECC) {
|
||||||
|
/* Careful, this call adds 2 ints */
|
||||||
|
/* to the end of the spare data. Calling function */
|
||||||
|
/* should allocate enough memory for spare, */
|
||||||
|
/* i.e. [YAFFS_BYTES_PER_SPARE+2*sizeof(int)]. */
|
||||||
|
retval =
|
||||||
|
mtd->read_ecc(mtd, addr, dev->nDataBytesPerChunk,
|
||||||
|
&dummy, data, spareAsBytes,
|
||||||
|
&yaffs_oobinfo);
|
||||||
|
} else {
|
||||||
|
retval =
|
||||||
|
mtd->read_ecc(mtd, addr, dev->nDataBytesPerChunk,
|
||||||
|
&dummy, data, spareAsBytes,
|
||||||
|
&yaffs_noeccinfo);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (data)
|
||||||
|
retval =
|
||||||
|
mtd->read(mtd, addr, dev->nDataBytesPerChunk, &dummy,
|
||||||
|
data);
|
||||||
|
if (spare)
|
||||||
|
retval =
|
||||||
|
mtd->read_oob(mtd, addr, YAFFS_BYTES_PER_SPARE,
|
||||||
|
&dummy, spareAsBytes);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (retval == 0)
|
||||||
|
return YAFFS_OK;
|
||||||
|
else
|
||||||
|
return YAFFS_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nandmtd_EraseBlockInNAND(yaffs_Device * dev, int blockNumber)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
|
||||||
|
__u32 addr =
|
||||||
|
((loff_t) blockNumber) * dev->nDataBytesPerChunk
|
||||||
|
* dev->nChunksPerBlock;
|
||||||
|
struct erase_info ei;
|
||||||
|
int retval = 0;
|
||||||
|
|
||||||
|
ei.mtd = mtd;
|
||||||
|
ei.addr = addr;
|
||||||
|
ei.len = dev->nDataBytesPerChunk * dev->nChunksPerBlock;
|
||||||
|
ei.time = 1000;
|
||||||
|
ei.retries = 2;
|
||||||
|
ei.callback = NULL;
|
||||||
|
ei.priv = (u_long) dev;
|
||||||
|
|
||||||
|
/* Todo finish off the ei if required */
|
||||||
|
|
||||||
|
sema_init(&dev->sem, 0);
|
||||||
|
|
||||||
|
retval = mtd->erase(mtd, &ei);
|
||||||
|
|
||||||
|
if (retval == 0)
|
||||||
|
return YAFFS_OK;
|
||||||
|
else
|
||||||
|
return YAFFS_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nandmtd_InitialiseNAND(yaffs_Device * dev)
|
||||||
|
{
|
||||||
|
return YAFFS_OK;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2002-2007 Aleph One Ltd.
|
||||||
|
* for Toby Churchill Ltd and Brightstar Engineering
|
||||||
|
*
|
||||||
|
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __YAFFS_MTDIF_H__
|
||||||
|
#define __YAFFS_MTDIF_H__
|
||||||
|
|
||||||
|
#include "yaffs_guts.h"
|
||||||
|
|
||||||
|
int nandmtd_WriteChunkToNAND(yaffs_Device * dev, int chunkInNAND,
|
||||||
|
const __u8 * data, const yaffs_Spare * spare);
|
||||||
|
int nandmtd_ReadChunkFromNAND(yaffs_Device * dev, int chunkInNAND, __u8 * data,
|
||||||
|
yaffs_Spare * spare);
|
||||||
|
int nandmtd_EraseBlockInNAND(yaffs_Device * dev, int blockNumber);
|
||||||
|
int nandmtd_InitialiseNAND(yaffs_Device * dev);
|
||||||
|
#endif
|
|
@ -0,0 +1,434 @@
|
||||||
|
From ian@brightstareng.com Fri May 18 15:06:49 2007
|
||||||
|
From ian@brightstareng.com Fri May 18 15:08:21 2007
|
||||||
|
Received: from 206.173.66.57.ptr.us.xo.net ([206.173.66.57] helo=zebra.brightstareng.com)
|
||||||
|
by apollo.linkchoose.co.uk with esmtp (Exim 4.60)
|
||||||
|
(envelope-from <ian@brightstareng.com>)
|
||||||
|
id 1Hp380-00011e-T6
|
||||||
|
for david.goodenough@linkchoose.co.uk; Fri, 18 May 2007 15:08:21 +0100
|
||||||
|
Received: from localhost (localhost.localdomain [127.0.0.1])
|
||||||
|
by zebra.brightstareng.com (Postfix) with ESMTP
|
||||||
|
id 4819F28C004; Fri, 18 May 2007 10:07:49 -0400 (EDT)
|
||||||
|
Received: from zebra.brightstareng.com ([127.0.0.1])
|
||||||
|
by localhost (zebra [127.0.0.1]) (amavisd-new, port 10024) with ESMTP
|
||||||
|
id 05328-06; Fri, 18 May 2007 10:07:16 -0400 (EDT)
|
||||||
|
Received: from pippin (unknown [192.168.1.25])
|
||||||
|
by zebra.brightstareng.com (Postfix) with ESMTP
|
||||||
|
id 8BEF528C1BC; Fri, 18 May 2007 10:06:53 -0400 (EDT)
|
||||||
|
From: Ian McDonnell <ian@brightstareng.com>
|
||||||
|
To: David Goodenough <david.goodenough@linkchoose.co.uk>
|
||||||
|
Subject: Re: something tested this time -- yaffs_mtdif1-compat.c
|
||||||
|
Date: Fri, 18 May 2007 10:06:49 -0400
|
||||||
|
User-Agent: KMail/1.9.1
|
||||||
|
References: <200705142207.06909.ian@brightstareng.com> <200705171131.53536.ian@brightstareng.com> <200705181334.32166.david.goodenough@linkchoose.co.uk>
|
||||||
|
In-Reply-To: <200705181334.32166.david.goodenough@linkchoose.co.uk>
|
||||||
|
Cc: Andrea Conti <alyf@alyf.net>,
|
||||||
|
Charles Manning <manningc2@actrix.gen.nz>
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: Multipart/Mixed;
|
||||||
|
boundary="Boundary-00=_5LbTGmt62YoutxM"
|
||||||
|
Message-Id: <200705181006.49860.ian@brightstareng.com>
|
||||||
|
X-Virus-Scanned: by amavisd-new at brightstareng.com
|
||||||
|
Status: R
|
||||||
|
X-Status: NT
|
||||||
|
X-KMail-EncryptionState:
|
||||||
|
X-KMail-SignatureState:
|
||||||
|
X-KMail-MDN-Sent:
|
||||||
|
|
||||||
|
--Boundary-00=_5LbTGmt62YoutxM
|
||||||
|
Content-Type: text/plain;
|
||||||
|
charset="iso-8859-15"
|
||||||
|
Content-Transfer-Encoding: 7bit
|
||||||
|
Content-Disposition: inline
|
||||||
|
|
||||||
|
David, Andrea,
|
||||||
|
|
||||||
|
On Friday 18 May 2007 08:34, you wrote:
|
||||||
|
> Yea team. With this fix in place (I put it in the wrong place
|
||||||
|
> at first) I can now mount and ls the Yaffs partition without
|
||||||
|
> an error messages!
|
||||||
|
|
||||||
|
Good news!
|
||||||
|
|
||||||
|
Attached is a newer yaffs_mtdif1.c with a bandaid to help the
|
||||||
|
2.6.18 and 2.6.19 versions of MTD not trip on the oob read.
|
||||||
|
See the LINUX_VERSION_CODE conditional in
|
||||||
|
nandmtd1_ReadChunkWithTagsFromNAND.
|
||||||
|
|
||||||
|
-imcd
|
||||||
|
|
||||||
|
--Boundary-00=_5LbTGmt62YoutxM
|
||||||
|
Content-Type: text/x-csrc;
|
||||||
|
charset="iso-8859-15";
|
||||||
|
name="yaffs_mtdif1.c"
|
||||||
|
Content-Transfer-Encoding: 7bit
|
||||||
|
Content-Disposition: attachment;
|
||||||
|
filename="yaffs_mtdif1.c"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* YAFFS: Yet another FFS. A NAND-flash specific file system.
|
||||||
|
* yaffs_mtdif1.c NAND mtd interface functions for small-page NAND.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2002 Aleph One Ltd.
|
||||||
|
* for Toby Churchill Ltd and Brightstar Engineering
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This module provides the interface between yaffs_nand.c and the
|
||||||
|
* MTD API. This version is used when the MTD interface supports the
|
||||||
|
* 'mtd_oob_ops' style calls to read_oob and write_oob, circa 2.6.17,
|
||||||
|
* and we have small-page NAND device.
|
||||||
|
*
|
||||||
|
* These functions are invoked via function pointers in yaffs_nand.c.
|
||||||
|
* This replaces functionality provided by functions in yaffs_mtdif.c
|
||||||
|
* and the yaffs_TagsCompatability functions in yaffs_tagscompat.c that are
|
||||||
|
* called in yaffs_mtdif.c when the function pointers are NULL.
|
||||||
|
* We assume the MTD layer is performing ECC (useNANDECC is true).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "yportenv.h"
|
||||||
|
#include "yaffs_guts.h"
|
||||||
|
#include "yaffs_packedtags1.h"
|
||||||
|
#include "yaffs_tagscompat.h" // for yaffs_CalcTagsECC
|
||||||
|
|
||||||
|
#include "linux/kernel.h"
|
||||||
|
#include "linux/version.h"
|
||||||
|
#include "linux/types.h"
|
||||||
|
#include "linux/mtd/mtd.h"
|
||||||
|
|
||||||
|
/* Don't compile this module if we don't have MTD's mtd_oob_ops interface */
|
||||||
|
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
|
||||||
|
|
||||||
|
const char *yaffs_mtdif1_c_version = "$Id$";
|
||||||
|
|
||||||
|
#ifndef CONFIG_YAFFS_9BYTE_TAGS
|
||||||
|
# define YTAG1_SIZE 8
|
||||||
|
#else
|
||||||
|
# define YTAG1_SIZE 9
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/* Use the following nand_ecclayout with MTD when using
|
||||||
|
* CONFIG_YAFFS_9BYTE_TAGS and the older on-NAND tags layout.
|
||||||
|
* If you have existing Yaffs images and the byte order differs from this,
|
||||||
|
* adjust 'oobfree' to match your existing Yaffs data.
|
||||||
|
*
|
||||||
|
* This nand_ecclayout scatters/gathers to/from the old-yaffs layout with the
|
||||||
|
* pageStatus byte (at NAND spare offset 4) scattered/gathered from/to
|
||||||
|
* the 9th byte.
|
||||||
|
*
|
||||||
|
* Old-style on-NAND format: T0,T1,T2,T3,P,B,T4,T5,E0,E1,E2,T6,T7,E3,E4,E5
|
||||||
|
* We have/need PackedTags1 plus pageStatus: T0,T1,T2,T3,T4,T5,T6,T7,P
|
||||||
|
* where Tn are the tag bytes, En are MTD's ECC bytes, P is the pageStatus
|
||||||
|
* byte and B is the small-page bad-block indicator byte.
|
||||||
|
*/
|
||||||
|
static struct nand_ecclayout nand_oob_16 = {
|
||||||
|
.eccbytes = 6,
|
||||||
|
.eccpos = { 8, 9, 10, 13, 14, 15 },
|
||||||
|
.oobavail = 9,
|
||||||
|
.oobfree = { { 0, 4 }, { 6, 2 }, { 11, 2 }, { 4, 1 } }
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Write a chunk (page) of data to NAND.
|
||||||
|
*
|
||||||
|
* Caller always provides ExtendedTags data which are converted to a more
|
||||||
|
* compact (packed) form for storage in NAND. A mini-ECC runs over the
|
||||||
|
* contents of the tags meta-data; used to valid the tags when read.
|
||||||
|
*
|
||||||
|
* - Pack ExtendedTags to PackedTags1 form
|
||||||
|
* - Compute mini-ECC for PackedTags1
|
||||||
|
* - Write data and packed tags to NAND.
|
||||||
|
*
|
||||||
|
* Note: Due to the use of the PackedTags1 meta-data which does not include
|
||||||
|
* a full sequence number (as found in the larger PackedTags2 form) it is
|
||||||
|
* necessary for Yaffs to re-write a chunk/page (just once) to mark it as
|
||||||
|
* discarded and dirty. This is not ideal: newer NAND parts are supposed
|
||||||
|
* to be written just once. When Yaffs performs this operation, this
|
||||||
|
* function is called with a NULL data pointer -- calling MTD write_oob
|
||||||
|
* without data is valid usage (2.6.17).
|
||||||
|
*
|
||||||
|
* Any underlying MTD error results in YAFFS_FAIL.
|
||||||
|
* Returns YAFFS_OK or YAFFS_FAIL.
|
||||||
|
*/
|
||||||
|
int nandmtd1_WriteChunkWithTagsToNAND(yaffs_Device *dev,
|
||||||
|
int chunkInNAND, const __u8 * data, const yaffs_ExtendedTags * etags)
|
||||||
|
{
|
||||||
|
struct mtd_info * mtd = dev->genericDevice;
|
||||||
|
int chunkBytes = dev->nDataBytesPerChunk;
|
||||||
|
loff_t addr = ((loff_t)chunkInNAND) * chunkBytes;
|
||||||
|
struct mtd_oob_ops ops;
|
||||||
|
yaffs_PackedTags1 pt1;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
/* we assume that PackedTags1 and yaffs_Tags are compatible */
|
||||||
|
compile_time_assertion(sizeof(yaffs_PackedTags1) == 12);
|
||||||
|
compile_time_assertion(sizeof(yaffs_Tags) == 8);
|
||||||
|
|
||||||
|
yaffs_PackTags1(&pt1, etags);
|
||||||
|
yaffs_CalcTagsECC((yaffs_Tags *)&pt1);
|
||||||
|
|
||||||
|
/* When deleting a chunk, the upper layer provides only skeletal
|
||||||
|
* etags, one with chunkDeleted set. However, we need to update the
|
||||||
|
* tags, not erase them completely. So we use the NAND write property
|
||||||
|
* that only zeroed-bits stick and set tag bytes to all-ones and
|
||||||
|
* zero just the (not) deleted bit.
|
||||||
|
*/
|
||||||
|
#ifndef CONFIG_YAFFS_9BYTE_TAGS
|
||||||
|
if (etags->chunkDeleted) {
|
||||||
|
memset(&pt1, 0xff, 8);
|
||||||
|
/* clear delete status bit to indicate deleted */
|
||||||
|
pt1.deleted = 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
((__u8 *)&pt1)[8] = 0xff;
|
||||||
|
if (etags->chunkDeleted) {
|
||||||
|
memset(&pt1, 0xff, 8);
|
||||||
|
/* zero pageStatus byte to indicate deleted */
|
||||||
|
((__u8 *)&pt1)[8] = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
memset(&ops, 0, sizeof(ops));
|
||||||
|
ops.mode = MTD_OOB_AUTO;
|
||||||
|
ops.len = (data) ? chunkBytes : 0;
|
||||||
|
ops.ooblen = YTAG1_SIZE;
|
||||||
|
ops.datbuf = (__u8 *)data;
|
||||||
|
ops.oobbuf = (__u8 *)&pt1;
|
||||||
|
|
||||||
|
retval = mtd->write_oob(mtd, addr, &ops);
|
||||||
|
if (retval) {
|
||||||
|
yaffs_trace(YAFFS_TRACE_MTD,
|
||||||
|
"write_oob failed, chunk %d, mtd error %d\n",
|
||||||
|
chunkInNAND, retval);
|
||||||
|
}
|
||||||
|
return retval ? YAFFS_FAIL : YAFFS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return with empty ExtendedTags but add eccResult.
|
||||||
|
*/
|
||||||
|
static int rettags(yaffs_ExtendedTags * etags, int eccResult, int retval)
|
||||||
|
{
|
||||||
|
if (etags) {
|
||||||
|
memset(etags, 0, sizeof(*etags));
|
||||||
|
etags->eccResult = eccResult;
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read a chunk (page) from NAND.
|
||||||
|
*
|
||||||
|
* Caller expects ExtendedTags data to be usable even on error; that is,
|
||||||
|
* all members except eccResult and blockBad are zeroed.
|
||||||
|
*
|
||||||
|
* - Check ECC results for data (if applicable)
|
||||||
|
* - Check for blank/erased block (return empty ExtendedTags if blank)
|
||||||
|
* - Check the PackedTags1 mini-ECC (correct if necessary/possible)
|
||||||
|
* - Convert PackedTags1 to ExtendedTags
|
||||||
|
* - Update eccResult and blockBad members to refect state.
|
||||||
|
*
|
||||||
|
* Returns YAFFS_OK or YAFFS_FAIL.
|
||||||
|
*/
|
||||||
|
int nandmtd1_ReadChunkWithTagsFromNAND(yaffs_Device *dev,
|
||||||
|
int chunkInNAND, __u8 * data, yaffs_ExtendedTags * etags)
|
||||||
|
{
|
||||||
|
struct mtd_info * mtd = dev->genericDevice;
|
||||||
|
int chunkBytes = dev->nDataBytesPerChunk;
|
||||||
|
loff_t addr = ((loff_t)chunkInNAND) * chunkBytes;
|
||||||
|
int eccres = YAFFS_ECC_RESULT_NO_ERROR;
|
||||||
|
struct mtd_oob_ops ops;
|
||||||
|
yaffs_PackedTags1 pt1;
|
||||||
|
int retval;
|
||||||
|
int deleted;
|
||||||
|
|
||||||
|
memset(&ops, 0, sizeof(ops));
|
||||||
|
ops.mode = MTD_OOB_AUTO;
|
||||||
|
ops.len = (data) ? chunkBytes : 0;
|
||||||
|
ops.ooblen = YTAG1_SIZE;
|
||||||
|
ops.datbuf = data;
|
||||||
|
ops.oobbuf = (__u8 *)&pt1;
|
||||||
|
|
||||||
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20))
|
||||||
|
/* In MTD 2.6.18 to 2.6.19 nand_base.c:nand_do_read_oob() has a bug;
|
||||||
|
* help it out with ops.len = ops.ooblen when ops.datbuf == NULL.
|
||||||
|
*/
|
||||||
|
ops.len = (ops.datbuf) ? ops.len : ops.ooblen;
|
||||||
|
#endif
|
||||||
|
/* Read page and oob using MTD.
|
||||||
|
* Check status and determine ECC result.
|
||||||
|
*/
|
||||||
|
retval = mtd->read_oob(mtd, addr, &ops);
|
||||||
|
if (retval) {
|
||||||
|
yaffs_trace(YAFFS_TRACE_MTD,
|
||||||
|
"read_oob failed, chunk %d, mtd error %d\n",
|
||||||
|
chunkInNAND, retval);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (retval) {
|
||||||
|
case 0:
|
||||||
|
/* no error */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case -EUCLEAN:
|
||||||
|
/* MTD's ECC fixed the data */
|
||||||
|
eccres = YAFFS_ECC_RESULT_FIXED;
|
||||||
|
dev->eccFixed++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case -EBADMSG:
|
||||||
|
/* MTD's ECC could not fix the data */
|
||||||
|
dev->eccUnfixed++;
|
||||||
|
/* fall into... */
|
||||||
|
default:
|
||||||
|
rettags(etags, YAFFS_ECC_RESULT_UNFIXED, 0);
|
||||||
|
etags->blockBad = (mtd->block_isbad)(mtd, addr);
|
||||||
|
return YAFFS_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for a blank/erased chunk.
|
||||||
|
*/
|
||||||
|
if (yaffs_CheckFF((__u8 *)&pt1, 8)) {
|
||||||
|
/* when blank, upper layers want eccResult to be <= NO_ERROR */
|
||||||
|
return rettags(etags, YAFFS_ECC_RESULT_NO_ERROR, YAFFS_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef CONFIG_YAFFS_9BYTE_TAGS
|
||||||
|
/* Read deleted status (bit) then return it to it's non-deleted
|
||||||
|
* state before performing tags mini-ECC check. pt1.deleted is
|
||||||
|
* inverted.
|
||||||
|
*/
|
||||||
|
deleted = !pt1.deleted;
|
||||||
|
pt1.deleted = 1;
|
||||||
|
#else
|
||||||
|
(void) deleted; /* not used */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Check the packed tags mini-ECC and correct if necessary/possible.
|
||||||
|
*/
|
||||||
|
retval = yaffs_CheckECCOnTags((yaffs_Tags *)&pt1);
|
||||||
|
switch (retval) {
|
||||||
|
case 0:
|
||||||
|
/* no tags error, use MTD result */
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
/* recovered tags-ECC error */
|
||||||
|
dev->tagsEccFixed++;
|
||||||
|
eccres = YAFFS_ECC_RESULT_FIXED;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* unrecovered tags-ECC error */
|
||||||
|
dev->tagsEccUnfixed++;
|
||||||
|
return rettags(etags, YAFFS_ECC_RESULT_UNFIXED, YAFFS_FAIL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unpack the tags to extended form and set ECC result.
|
||||||
|
* [set shouldBeFF just to keep yaffs_UnpackTags1 happy]
|
||||||
|
*/
|
||||||
|
pt1.shouldBeFF = 0xFFFFFFFF;
|
||||||
|
yaffs_UnpackTags1(etags, &pt1);
|
||||||
|
etags->eccResult = eccres;
|
||||||
|
|
||||||
|
/* Set deleted state.
|
||||||
|
*/
|
||||||
|
#ifndef CONFIG_YAFFS_9BYTE_TAGS
|
||||||
|
etags->chunkDeleted = deleted;
|
||||||
|
#else
|
||||||
|
etags->chunkDeleted = (yaffs_CountBits(((__u8 *)&pt1)[8]) < 7);
|
||||||
|
#endif
|
||||||
|
return YAFFS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mark a block bad.
|
||||||
|
*
|
||||||
|
* This is a persistant state.
|
||||||
|
* Use of this function should be rare.
|
||||||
|
*
|
||||||
|
* Returns YAFFS_OK or YAFFS_FAIL.
|
||||||
|
*/
|
||||||
|
int nandmtd1_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo)
|
||||||
|
{
|
||||||
|
struct mtd_info * mtd = dev->genericDevice;
|
||||||
|
int blocksize = dev->nChunksPerBlock * dev->nDataBytesPerChunk;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", blockNo);
|
||||||
|
|
||||||
|
retval = mtd->block_markbad(mtd, (loff_t)blocksize * blockNo);
|
||||||
|
return (retval) ? YAFFS_FAIL : YAFFS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check any MTD prerequists.
|
||||||
|
*
|
||||||
|
* Returns YAFFS_OK or YAFFS_FAIL.
|
||||||
|
*/
|
||||||
|
static int nandmtd1_TestPrerequists(struct mtd_info * mtd)
|
||||||
|
{
|
||||||
|
/* 2.6.18 has mtd->ecclayout->oobavail */
|
||||||
|
/* 2.6.21 has mtd->ecclayout->oobavail and mtd->oobavail */
|
||||||
|
int oobavail = mtd->ecclayout->oobavail;
|
||||||
|
|
||||||
|
if (oobavail < YTAG1_SIZE) {
|
||||||
|
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||||
|
"mtd device has only %d bytes for tags, need %d",
|
||||||
|
oobavail, YTAG1_SIZE);
|
||||||
|
return YAFFS_FAIL;
|
||||||
|
}
|
||||||
|
return YAFFS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Query for the current state of a specific block.
|
||||||
|
*
|
||||||
|
* Examine the tags of the first chunk of the block and return the state:
|
||||||
|
* - YAFFS_BLOCK_STATE_DEAD, the block is marked bad
|
||||||
|
* - YAFFS_BLOCK_STATE_NEEDS_SCANNING, the block is in use
|
||||||
|
* - YAFFS_BLOCK_STATE_EMPTY, the block is clean
|
||||||
|
*
|
||||||
|
* Always returns YAFFS_OK.
|
||||||
|
*/
|
||||||
|
int nandmtd1_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo,
|
||||||
|
yaffs_BlockState * pState, int *pSequenceNumber)
|
||||||
|
{
|
||||||
|
struct mtd_info * mtd = dev->genericDevice;
|
||||||
|
int chunkNo = blockNo * dev->nChunksPerBlock;
|
||||||
|
yaffs_ExtendedTags etags;
|
||||||
|
int state = YAFFS_BLOCK_STATE_DEAD;
|
||||||
|
int seqnum = 0;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
/* We don't yet have a good place to test for MTD config prerequists.
|
||||||
|
* Do it here as we are called during the initial scan.
|
||||||
|
*/
|
||||||
|
if (nandmtd1_TestPrerequists(mtd) != YAFFS_OK) {
|
||||||
|
return YAFFS_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = nandmtd1_ReadChunkWithTagsFromNAND(dev, chunkNo, NULL, &etags);
|
||||||
|
if (etags.blockBad) {
|
||||||
|
yaffs_trace(YAFFS_TRACE_BAD_BLOCKS,
|
||||||
|
"block %d is marked bad", blockNo);
|
||||||
|
state = YAFFS_BLOCK_STATE_DEAD;
|
||||||
|
}
|
||||||
|
else if (etags.chunkUsed) {
|
||||||
|
state = YAFFS_BLOCK_STATE_NEEDS_SCANNING;
|
||||||
|
seqnum = etags.sequenceNumber;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
state = YAFFS_BLOCK_STATE_EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pState = state;
|
||||||
|
*pSequenceNumber = seqnum;
|
||||||
|
|
||||||
|
/* query always succeeds */
|
||||||
|
return YAFFS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /*KERNEL_VERSION*/
|
||||||
|
|
||||||
|
--Boundary-00=_5LbTGmt62YoutxM--
|
||||||
|
|
||||||
|
|
||||||
|
|
363
target/linux/generic-2.6/files-2.6.27/fs/yaffs2/yaffs_mtdif1.c
Normal file
363
target/linux/generic-2.6/files-2.6.27/fs/yaffs2/yaffs_mtdif1.c
Normal file
|
@ -0,0 +1,363 @@
|
||||||
|
/*
|
||||||
|
* YAFFS: Yet another FFS. A NAND-flash specific file system.
|
||||||
|
* yaffs_mtdif1.c NAND mtd interface functions for small-page NAND.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2002 Aleph One Ltd.
|
||||||
|
* for Toby Churchill Ltd and Brightstar Engineering
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This module provides the interface between yaffs_nand.c and the
|
||||||
|
* MTD API. This version is used when the MTD interface supports the
|
||||||
|
* 'mtd_oob_ops' style calls to read_oob and write_oob, circa 2.6.17,
|
||||||
|
* and we have small-page NAND device.
|
||||||
|
*
|
||||||
|
* These functions are invoked via function pointers in yaffs_nand.c.
|
||||||
|
* This replaces functionality provided by functions in yaffs_mtdif.c
|
||||||
|
* and the yaffs_TagsCompatability functions in yaffs_tagscompat.c that are
|
||||||
|
* called in yaffs_mtdif.c when the function pointers are NULL.
|
||||||
|
* We assume the MTD layer is performing ECC (useNANDECC is true).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "yportenv.h"
|
||||||
|
#include "yaffs_guts.h"
|
||||||
|
#include "yaffs_packedtags1.h"
|
||||||
|
#include "yaffs_tagscompat.h" // for yaffs_CalcTagsECC
|
||||||
|
|
||||||
|
#include "linux/kernel.h"
|
||||||
|
#include "linux/version.h"
|
||||||
|
#include "linux/types.h"
|
||||||
|
#include "linux/mtd/mtd.h"
|
||||||
|
|
||||||
|
/* Don't compile this module if we don't have MTD's mtd_oob_ops interface */
|
||||||
|
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
|
||||||
|
|
||||||
|
const char *yaffs_mtdif1_c_version = "$Id: yaffs_mtdif1.c,v 1.3 2007/05/15 20:16:11 ian Exp $";
|
||||||
|
|
||||||
|
#ifndef CONFIG_YAFFS_9BYTE_TAGS
|
||||||
|
# define YTAG1_SIZE 8
|
||||||
|
#else
|
||||||
|
# define YTAG1_SIZE 9
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/* Use the following nand_ecclayout with MTD when using
|
||||||
|
* CONFIG_YAFFS_9BYTE_TAGS and the older on-NAND tags layout.
|
||||||
|
* If you have existing Yaffs images and the byte order differs from this,
|
||||||
|
* adjust 'oobfree' to match your existing Yaffs data.
|
||||||
|
*
|
||||||
|
* This nand_ecclayout scatters/gathers to/from the old-yaffs layout with the
|
||||||
|
* pageStatus byte (at NAND spare offset 4) scattered/gathered from/to
|
||||||
|
* the 9th byte.
|
||||||
|
*
|
||||||
|
* Old-style on-NAND format: T0,T1,T2,T3,P,B,T4,T5,E0,E1,E2,T6,T7,E3,E4,E5
|
||||||
|
* We have/need PackedTags1 plus pageStatus: T0,T1,T2,T3,T4,T5,T6,T7,P
|
||||||
|
* where Tn are the tag bytes, En are MTD's ECC bytes, P is the pageStatus
|
||||||
|
* byte and B is the small-page bad-block indicator byte.
|
||||||
|
*/
|
||||||
|
static struct nand_ecclayout nand_oob_16 = {
|
||||||
|
.eccbytes = 6,
|
||||||
|
.eccpos = { 8, 9, 10, 13, 14, 15 },
|
||||||
|
.oobavail = 9,
|
||||||
|
.oobfree = { { 0, 4 }, { 6, 2 }, { 11, 2 }, { 4, 1 } }
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Write a chunk (page) of data to NAND.
|
||||||
|
*
|
||||||
|
* Caller always provides ExtendedTags data which are converted to a more
|
||||||
|
* compact (packed) form for storage in NAND. A mini-ECC runs over the
|
||||||
|
* contents of the tags meta-data; used to valid the tags when read.
|
||||||
|
*
|
||||||
|
* - Pack ExtendedTags to PackedTags1 form
|
||||||
|
* - Compute mini-ECC for PackedTags1
|
||||||
|
* - Write data and packed tags to NAND.
|
||||||
|
*
|
||||||
|
* Note: Due to the use of the PackedTags1 meta-data which does not include
|
||||||
|
* a full sequence number (as found in the larger PackedTags2 form) it is
|
||||||
|
* necessary for Yaffs to re-write a chunk/page (just once) to mark it as
|
||||||
|
* discarded and dirty. This is not ideal: newer NAND parts are supposed
|
||||||
|
* to be written just once. When Yaffs performs this operation, this
|
||||||
|
* function is called with a NULL data pointer -- calling MTD write_oob
|
||||||
|
* without data is valid usage (2.6.17).
|
||||||
|
*
|
||||||
|
* Any underlying MTD error results in YAFFS_FAIL.
|
||||||
|
* Returns YAFFS_OK or YAFFS_FAIL.
|
||||||
|
*/
|
||||||
|
int nandmtd1_WriteChunkWithTagsToNAND(yaffs_Device *dev,
|
||||||
|
int chunkInNAND, const __u8 * data, const yaffs_ExtendedTags * etags)
|
||||||
|
{
|
||||||
|
struct mtd_info * mtd = dev->genericDevice;
|
||||||
|
int chunkBytes = dev->nDataBytesPerChunk;
|
||||||
|
loff_t addr = ((loff_t)chunkInNAND) * chunkBytes;
|
||||||
|
struct mtd_oob_ops ops;
|
||||||
|
yaffs_PackedTags1 pt1;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
/* we assume that PackedTags1 and yaffs_Tags are compatible */
|
||||||
|
compile_time_assertion(sizeof(yaffs_PackedTags1) == 12);
|
||||||
|
compile_time_assertion(sizeof(yaffs_Tags) == 8);
|
||||||
|
|
||||||
|
dev->nPageWrites++;
|
||||||
|
|
||||||
|
yaffs_PackTags1(&pt1, etags);
|
||||||
|
yaffs_CalcTagsECC((yaffs_Tags *)&pt1);
|
||||||
|
|
||||||
|
/* When deleting a chunk, the upper layer provides only skeletal
|
||||||
|
* etags, one with chunkDeleted set. However, we need to update the
|
||||||
|
* tags, not erase them completely. So we use the NAND write property
|
||||||
|
* that only zeroed-bits stick and set tag bytes to all-ones and
|
||||||
|
* zero just the (not) deleted bit.
|
||||||
|
*/
|
||||||
|
#ifndef CONFIG_YAFFS_9BYTE_TAGS
|
||||||
|
if (etags->chunkDeleted) {
|
||||||
|
memset(&pt1, 0xff, 8);
|
||||||
|
/* clear delete status bit to indicate deleted */
|
||||||
|
pt1.deleted = 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
((__u8 *)&pt1)[8] = 0xff;
|
||||||
|
if (etags->chunkDeleted) {
|
||||||
|
memset(&pt1, 0xff, 8);
|
||||||
|
/* zero pageStatus byte to indicate deleted */
|
||||||
|
((__u8 *)&pt1)[8] = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
memset(&ops, 0, sizeof(ops));
|
||||||
|
ops.mode = MTD_OOB_AUTO;
|
||||||
|
ops.len = (data) ? chunkBytes : 0;
|
||||||
|
ops.ooblen = YTAG1_SIZE;
|
||||||
|
ops.datbuf = (__u8 *)data;
|
||||||
|
ops.oobbuf = (__u8 *)&pt1;
|
||||||
|
|
||||||
|
retval = mtd->write_oob(mtd, addr, &ops);
|
||||||
|
if (retval) {
|
||||||
|
yaffs_trace(YAFFS_TRACE_MTD,
|
||||||
|
"write_oob failed, chunk %d, mtd error %d\n",
|
||||||
|
chunkInNAND, retval);
|
||||||
|
}
|
||||||
|
return retval ? YAFFS_FAIL : YAFFS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return with empty ExtendedTags but add eccResult.
|
||||||
|
*/
|
||||||
|
static int rettags(yaffs_ExtendedTags * etags, int eccResult, int retval)
|
||||||
|
{
|
||||||
|
if (etags) {
|
||||||
|
memset(etags, 0, sizeof(*etags));
|
||||||
|
etags->eccResult = eccResult;
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read a chunk (page) from NAND.
|
||||||
|
*
|
||||||
|
* Caller expects ExtendedTags data to be usable even on error; that is,
|
||||||
|
* all members except eccResult and blockBad are zeroed.
|
||||||
|
*
|
||||||
|
* - Check ECC results for data (if applicable)
|
||||||
|
* - Check for blank/erased block (return empty ExtendedTags if blank)
|
||||||
|
* - Check the PackedTags1 mini-ECC (correct if necessary/possible)
|
||||||
|
* - Convert PackedTags1 to ExtendedTags
|
||||||
|
* - Update eccResult and blockBad members to refect state.
|
||||||
|
*
|
||||||
|
* Returns YAFFS_OK or YAFFS_FAIL.
|
||||||
|
*/
|
||||||
|
int nandmtd1_ReadChunkWithTagsFromNAND(yaffs_Device *dev,
|
||||||
|
int chunkInNAND, __u8 * data, yaffs_ExtendedTags * etags)
|
||||||
|
{
|
||||||
|
struct mtd_info * mtd = dev->genericDevice;
|
||||||
|
int chunkBytes = dev->nDataBytesPerChunk;
|
||||||
|
loff_t addr = ((loff_t)chunkInNAND) * chunkBytes;
|
||||||
|
int eccres = YAFFS_ECC_RESULT_NO_ERROR;
|
||||||
|
struct mtd_oob_ops ops;
|
||||||
|
yaffs_PackedTags1 pt1;
|
||||||
|
int retval;
|
||||||
|
int deleted;
|
||||||
|
|
||||||
|
dev->nPageReads++;
|
||||||
|
|
||||||
|
memset(&ops, 0, sizeof(ops));
|
||||||
|
ops.mode = MTD_OOB_AUTO;
|
||||||
|
ops.len = (data) ? chunkBytes : 0;
|
||||||
|
ops.ooblen = YTAG1_SIZE;
|
||||||
|
ops.datbuf = data;
|
||||||
|
ops.oobbuf = (__u8 *)&pt1;
|
||||||
|
|
||||||
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20))
|
||||||
|
/* In MTD 2.6.18 to 2.6.19 nand_base.c:nand_do_read_oob() has a bug;
|
||||||
|
* help it out with ops.len = ops.ooblen when ops.datbuf == NULL.
|
||||||
|
*/
|
||||||
|
ops.len = (ops.datbuf) ? ops.len : ops.ooblen;
|
||||||
|
#endif
|
||||||
|
/* Read page and oob using MTD.
|
||||||
|
* Check status and determine ECC result.
|
||||||
|
*/
|
||||||
|
retval = mtd->read_oob(mtd, addr, &ops);
|
||||||
|
if (retval) {
|
||||||
|
yaffs_trace(YAFFS_TRACE_MTD,
|
||||||
|
"read_oob failed, chunk %d, mtd error %d\n",
|
||||||
|
chunkInNAND, retval);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (retval) {
|
||||||
|
case 0:
|
||||||
|
/* no error */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case -EUCLEAN:
|
||||||
|
/* MTD's ECC fixed the data */
|
||||||
|
eccres = YAFFS_ECC_RESULT_FIXED;
|
||||||
|
dev->eccFixed++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case -EBADMSG:
|
||||||
|
/* MTD's ECC could not fix the data */
|
||||||
|
dev->eccUnfixed++;
|
||||||
|
/* fall into... */
|
||||||
|
default:
|
||||||
|
rettags(etags, YAFFS_ECC_RESULT_UNFIXED, 0);
|
||||||
|
etags->blockBad = (mtd->block_isbad)(mtd, addr);
|
||||||
|
return YAFFS_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for a blank/erased chunk.
|
||||||
|
*/
|
||||||
|
if (yaffs_CheckFF((__u8 *)&pt1, 8)) {
|
||||||
|
/* when blank, upper layers want eccResult to be <= NO_ERROR */
|
||||||
|
return rettags(etags, YAFFS_ECC_RESULT_NO_ERROR, YAFFS_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef CONFIG_YAFFS_9BYTE_TAGS
|
||||||
|
/* Read deleted status (bit) then return it to it's non-deleted
|
||||||
|
* state before performing tags mini-ECC check. pt1.deleted is
|
||||||
|
* inverted.
|
||||||
|
*/
|
||||||
|
deleted = !pt1.deleted;
|
||||||
|
pt1.deleted = 1;
|
||||||
|
#else
|
||||||
|
deleted = (yaffs_CountBits(((__u8 *)&pt1)[8]) < 7);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Check the packed tags mini-ECC and correct if necessary/possible.
|
||||||
|
*/
|
||||||
|
retval = yaffs_CheckECCOnTags((yaffs_Tags *)&pt1);
|
||||||
|
switch (retval) {
|
||||||
|
case 0:
|
||||||
|
/* no tags error, use MTD result */
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
/* recovered tags-ECC error */
|
||||||
|
dev->tagsEccFixed++;
|
||||||
|
if (eccres == YAFFS_ECC_RESULT_NO_ERROR)
|
||||||
|
eccres = YAFFS_ECC_RESULT_FIXED;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* unrecovered tags-ECC error */
|
||||||
|
dev->tagsEccUnfixed++;
|
||||||
|
return rettags(etags, YAFFS_ECC_RESULT_UNFIXED, YAFFS_FAIL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unpack the tags to extended form and set ECC result.
|
||||||
|
* [set shouldBeFF just to keep yaffs_UnpackTags1 happy]
|
||||||
|
*/
|
||||||
|
pt1.shouldBeFF = 0xFFFFFFFF;
|
||||||
|
yaffs_UnpackTags1(etags, &pt1);
|
||||||
|
etags->eccResult = eccres;
|
||||||
|
|
||||||
|
/* Set deleted state */
|
||||||
|
etags->chunkDeleted = deleted;
|
||||||
|
return YAFFS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mark a block bad.
|
||||||
|
*
|
||||||
|
* This is a persistant state.
|
||||||
|
* Use of this function should be rare.
|
||||||
|
*
|
||||||
|
* Returns YAFFS_OK or YAFFS_FAIL.
|
||||||
|
*/
|
||||||
|
int nandmtd1_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo)
|
||||||
|
{
|
||||||
|
struct mtd_info * mtd = dev->genericDevice;
|
||||||
|
int blocksize = dev->nChunksPerBlock * dev->nDataBytesPerChunk;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", blockNo);
|
||||||
|
|
||||||
|
retval = mtd->block_markbad(mtd, (loff_t)blocksize * blockNo);
|
||||||
|
return (retval) ? YAFFS_FAIL : YAFFS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check any MTD prerequists.
|
||||||
|
*
|
||||||
|
* Returns YAFFS_OK or YAFFS_FAIL.
|
||||||
|
*/
|
||||||
|
static int nandmtd1_TestPrerequists(struct mtd_info * mtd)
|
||||||
|
{
|
||||||
|
/* 2.6.18 has mtd->ecclayout->oobavail */
|
||||||
|
/* 2.6.21 has mtd->ecclayout->oobavail and mtd->oobavail */
|
||||||
|
int oobavail = mtd->ecclayout->oobavail;
|
||||||
|
|
||||||
|
if (oobavail < YTAG1_SIZE) {
|
||||||
|
yaffs_trace(YAFFS_TRACE_ERROR,
|
||||||
|
"mtd device has only %d bytes for tags, need %d\n",
|
||||||
|
oobavail, YTAG1_SIZE);
|
||||||
|
return YAFFS_FAIL;
|
||||||
|
}
|
||||||
|
return YAFFS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Query for the current state of a specific block.
|
||||||
|
*
|
||||||
|
* Examine the tags of the first chunk of the block and return the state:
|
||||||
|
* - YAFFS_BLOCK_STATE_DEAD, the block is marked bad
|
||||||
|
* - YAFFS_BLOCK_STATE_NEEDS_SCANNING, the block is in use
|
||||||
|
* - YAFFS_BLOCK_STATE_EMPTY, the block is clean
|
||||||
|
*
|
||||||
|
* Always returns YAFFS_OK.
|
||||||
|
*/
|
||||||
|
int nandmtd1_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo,
|
||||||
|
yaffs_BlockState * pState, int *pSequenceNumber)
|
||||||
|
{
|
||||||
|
struct mtd_info * mtd = dev->genericDevice;
|
||||||
|
int chunkNo = blockNo * dev->nChunksPerBlock;
|
||||||
|
yaffs_ExtendedTags etags;
|
||||||
|
int state = YAFFS_BLOCK_STATE_DEAD;
|
||||||
|
int seqnum = 0;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
/* We don't yet have a good place to test for MTD config prerequists.
|
||||||
|
* Do it here as we are called during the initial scan.
|
||||||
|
*/
|
||||||
|
if (nandmtd1_TestPrerequists(mtd) != YAFFS_OK) {
|
||||||
|
return YAFFS_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = nandmtd1_ReadChunkWithTagsFromNAND(dev, chunkNo, NULL, &etags);
|
||||||
|
if (etags.blockBad) {
|
||||||
|
yaffs_trace(YAFFS_TRACE_BAD_BLOCKS,
|
||||||
|
"block %d is marked bad", blockNo);
|
||||||
|
state = YAFFS_BLOCK_STATE_DEAD;
|
||||||
|
}
|
||||||
|
else if (etags.chunkUsed) {
|
||||||
|
state = YAFFS_BLOCK_STATE_NEEDS_SCANNING;
|
||||||
|
seqnum = etags.sequenceNumber;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
state = YAFFS_BLOCK_STATE_EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pState = state;
|
||||||
|
*pSequenceNumber = seqnum;
|
||||||
|
|
||||||
|
/* query always succeeds */
|
||||||
|
return YAFFS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /*KERNEL_VERSION*/
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* YAFFS: Yet another Flash File System. A NAND-flash specific file system.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2002-2007 Aleph One Ltd.
|
||||||
|
* for Toby Churchill Ltd and Brightstar Engineering
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __YAFFS_MTDIF1_H__
|
||||||
|
#define __YAFFS_MTDIF1_H__
|
||||||
|
|
||||||
|
int nandmtd1_WriteChunkWithTagsToNAND(yaffs_Device * dev, int chunkInNAND,
|
||||||
|
const __u8 * data, const yaffs_ExtendedTags * tags);
|
||||||
|
|
||||||
|
int nandmtd1_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND,
|
||||||
|
__u8 * data, yaffs_ExtendedTags * tags);
|
||||||
|
|
||||||
|
int nandmtd1_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo);
|
||||||
|
|
||||||
|
int nandmtd1_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo,
|
||||||
|
yaffs_BlockState * state, int *sequenceNumber);
|
||||||
|
|
||||||
|
#endif
|
232
target/linux/generic-2.6/files-2.6.27/fs/yaffs2/yaffs_mtdif2.c
Normal file
232
target/linux/generic-2.6/files-2.6.27/fs/yaffs2/yaffs_mtdif2.c
Normal file
|
@ -0,0 +1,232 @@
|
||||||
|
/*
|
||||||
|
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2002-2007 Aleph One Ltd.
|
||||||
|
* for Toby Churchill Ltd and Brightstar Engineering
|
||||||
|
*
|
||||||
|
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* mtd interface for YAFFS2 */
|
||||||
|
|
||||||
|
const char *yaffs_mtdif2_c_version =
|
||||||
|
"$Id: yaffs_mtdif2.c,v 1.17 2007-02-14 01:09:06 wookey Exp $";
|
||||||
|
|
||||||
|
#include "yportenv.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include "yaffs_mtdif2.h"
|
||||||
|
|
||||||
|
#include "linux/mtd/mtd.h"
|
||||||
|
#include "linux/types.h"
|
||||||
|
#include "linux/time.h"
|
||||||
|
|
||||||
|
#include "yaffs_packedtags2.h"
|
||||||
|
|
||||||
|
int nandmtd2_WriteChunkWithTagsToNAND(yaffs_Device * dev, int chunkInNAND,
|
||||||
|
const __u8 * data,
|
||||||
|
const yaffs_ExtendedTags * tags)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
|
||||||
|
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
|
||||||
|
struct mtd_oob_ops ops;
|
||||||
|
#else
|
||||||
|
size_t dummy;
|
||||||
|
#endif
|
||||||
|
int retval = 0;
|
||||||
|
|
||||||
|
loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk;
|
||||||
|
|
||||||
|
yaffs_PackedTags2 pt;
|
||||||
|
|
||||||
|
T(YAFFS_TRACE_MTD,
|
||||||
|
(TSTR
|
||||||
|
("nandmtd2_WriteChunkWithTagsToNAND chunk %d data %p tags %p"
|
||||||
|
TENDSTR), chunkInNAND, data, tags));
|
||||||
|
|
||||||
|
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
|
||||||
|
if (tags)
|
||||||
|
yaffs_PackTags2(&pt, tags);
|
||||||
|
else
|
||||||
|
BUG(); /* both tags and data should always be present */
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
ops.mode = MTD_OOB_AUTO;
|
||||||
|
ops.ooblen = sizeof(pt);
|
||||||
|
ops.len = dev->nDataBytesPerChunk;
|
||||||
|
ops.ooboffs = 0;
|
||||||
|
ops.datbuf = (__u8 *)data;
|
||||||
|
ops.oobbuf = (void *)&pt;
|
||||||
|
retval = mtd->write_oob(mtd, addr, &ops);
|
||||||
|
} else
|
||||||
|
BUG(); /* both tags and data should always be present */
|
||||||
|
#else
|
||||||
|
if (tags) {
|
||||||
|
yaffs_PackTags2(&pt, tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data && tags) {
|
||||||
|
if (dev->useNANDECC)
|
||||||
|
retval =
|
||||||
|
mtd->write_ecc(mtd, addr, dev->nDataBytesPerChunk,
|
||||||
|
&dummy, data, (__u8 *) & pt, NULL);
|
||||||
|
else
|
||||||
|
retval =
|
||||||
|
mtd->write_ecc(mtd, addr, dev->nDataBytesPerChunk,
|
||||||
|
&dummy, data, (__u8 *) & pt, NULL);
|
||||||
|
} else {
|
||||||
|
if (data)
|
||||||
|
retval =
|
||||||
|
mtd->write(mtd, addr, dev->nDataBytesPerChunk, &dummy,
|
||||||
|
data);
|
||||||
|
if (tags)
|
||||||
|
retval =
|
||||||
|
mtd->write_oob(mtd, addr, mtd->oobsize, &dummy,
|
||||||
|
(__u8 *) & pt);
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (retval == 0)
|
||||||
|
return YAFFS_OK;
|
||||||
|
else
|
||||||
|
return YAFFS_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nandmtd2_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND,
|
||||||
|
__u8 * data, yaffs_ExtendedTags * tags)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
|
||||||
|
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
|
||||||
|
struct mtd_oob_ops ops;
|
||||||
|
#endif
|
||||||
|
size_t dummy;
|
||||||
|
int retval = 0;
|
||||||
|
|
||||||
|
loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk;
|
||||||
|
|
||||||
|
yaffs_PackedTags2 pt;
|
||||||
|
|
||||||
|
T(YAFFS_TRACE_MTD,
|
||||||
|
(TSTR
|
||||||
|
("nandmtd2_ReadChunkWithTagsFromNAND chunk %d data %p tags %p"
|
||||||
|
TENDSTR), chunkInNAND, data, tags));
|
||||||
|
|
||||||
|
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
|
||||||
|
if (data && !tags)
|
||||||
|
retval = mtd->read(mtd, addr, dev->nDataBytesPerChunk,
|
||||||
|
&dummy, data);
|
||||||
|
else if (tags) {
|
||||||
|
ops.mode = MTD_OOB_AUTO;
|
||||||
|
ops.ooblen = sizeof(pt);
|
||||||
|
ops.len = data ? dev->nDataBytesPerChunk : sizeof(pt);
|
||||||
|
ops.ooboffs = 0;
|
||||||
|
ops.datbuf = data;
|
||||||
|
ops.oobbuf = dev->spareBuffer;
|
||||||
|
retval = mtd->read_oob(mtd, addr, &ops);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (data && tags) {
|
||||||
|
if (dev->useNANDECC) {
|
||||||
|
retval =
|
||||||
|
mtd->read_ecc(mtd, addr, dev->nDataBytesPerChunk,
|
||||||
|
&dummy, data, dev->spareBuffer,
|
||||||
|
NULL);
|
||||||
|
} else {
|
||||||
|
retval =
|
||||||
|
mtd->read_ecc(mtd, addr, dev->nDataBytesPerChunk,
|
||||||
|
&dummy, data, dev->spareBuffer,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (data)
|
||||||
|
retval =
|
||||||
|
mtd->read(mtd, addr, dev->nDataBytesPerChunk, &dummy,
|
||||||
|
data);
|
||||||
|
if (tags)
|
||||||
|
retval =
|
||||||
|
mtd->read_oob(mtd, addr, mtd->oobsize, &dummy,
|
||||||
|
dev->spareBuffer);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
memcpy(&pt, dev->spareBuffer, sizeof(pt));
|
||||||
|
|
||||||
|
if (tags)
|
||||||
|
yaffs_UnpackTags2(tags, &pt);
|
||||||
|
|
||||||
|
if(tags && retval == -EBADMSG && tags->eccResult == YAFFS_ECC_RESULT_NO_ERROR)
|
||||||
|
tags->eccResult = YAFFS_ECC_RESULT_UNFIXED;
|
||||||
|
|
||||||
|
if (retval == 0)
|
||||||
|
return YAFFS_OK;
|
||||||
|
else
|
||||||
|
return YAFFS_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nandmtd2_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
|
||||||
|
int retval;
|
||||||
|
T(YAFFS_TRACE_MTD,
|
||||||
|
(TSTR("nandmtd2_MarkNANDBlockBad %d" TENDSTR), blockNo));
|
||||||
|
|
||||||
|
retval =
|
||||||
|
mtd->block_markbad(mtd,
|
||||||
|
blockNo * dev->nChunksPerBlock *
|
||||||
|
dev->nDataBytesPerChunk);
|
||||||
|
|
||||||
|
if (retval == 0)
|
||||||
|
return YAFFS_OK;
|
||||||
|
else
|
||||||
|
return YAFFS_FAIL;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int nandmtd2_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo,
|
||||||
|
yaffs_BlockState * state, int *sequenceNumber)
|
||||||
|
{
|
||||||
|
struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
T(YAFFS_TRACE_MTD,
|
||||||
|
(TSTR("nandmtd2_QueryNANDBlock %d" TENDSTR), blockNo));
|
||||||
|
retval =
|
||||||
|
mtd->block_isbad(mtd,
|
||||||
|
blockNo * dev->nChunksPerBlock *
|
||||||
|
dev->nDataBytesPerChunk);
|
||||||
|
|
||||||
|
if (retval) {
|
||||||
|
T(YAFFS_TRACE_MTD, (TSTR("block is bad" TENDSTR)));
|
||||||
|
|
||||||
|
*state = YAFFS_BLOCK_STATE_DEAD;
|
||||||
|
*sequenceNumber = 0;
|
||||||
|
} else {
|
||||||
|
yaffs_ExtendedTags t;
|
||||||
|
nandmtd2_ReadChunkWithTagsFromNAND(dev,
|
||||||
|
blockNo *
|
||||||
|
dev->nChunksPerBlock, NULL,
|
||||||
|
&t);
|
||||||
|
|
||||||
|
if (t.chunkUsed) {
|
||||||
|
*sequenceNumber = t.sequenceNumber;
|
||||||
|
*state = YAFFS_BLOCK_STATE_NEEDS_SCANNING;
|
||||||
|
} else {
|
||||||
|
*sequenceNumber = 0;
|
||||||
|
*state = YAFFS_BLOCK_STATE_EMPTY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
T(YAFFS_TRACE_MTD,
|
||||||
|
(TSTR("block is bad seq %d state %d" TENDSTR), *sequenceNumber,
|
||||||
|
*state));
|
||||||
|
|
||||||
|
if (retval == 0)
|
||||||
|
return YAFFS_OK;
|
||||||
|
else
|
||||||
|
return YAFFS_FAIL;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2002-2007 Aleph One Ltd.
|
||||||
|
* for Toby Churchill Ltd and Brightstar Engineering
|
||||||
|
*
|
||||||
|
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __YAFFS_MTDIF2_H__
|
||||||
|
#define __YAFFS_MTDIF2_H__
|
||||||
|
|
||||||
|
#include "yaffs_guts.h"
|
||||||
|
int nandmtd2_WriteChunkWithTagsToNAND(yaffs_Device * dev, int chunkInNAND,
|
||||||
|
const __u8 * data,
|
||||||
|
const yaffs_ExtendedTags * tags);
|
||||||
|
int nandmtd2_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND,
|
||||||
|
__u8 * data, yaffs_ExtendedTags * tags);
|
||||||
|
int nandmtd2_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo);
|
||||||
|
int nandmtd2_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo,
|
||||||
|
yaffs_BlockState * state, int *sequenceNumber);
|
||||||
|
|
||||||
|
#endif
|
134
target/linux/generic-2.6/files-2.6.27/fs/yaffs2/yaffs_nand.c
Normal file
134
target/linux/generic-2.6/files-2.6.27/fs/yaffs2/yaffs_nand.c
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
/*
|
||||||
|
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2002-2007 Aleph One Ltd.
|
||||||
|
* for Toby Churchill Ltd and Brightstar Engineering
|
||||||
|
*
|
||||||
|
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const char *yaffs_nand_c_version =
|
||||||
|
"$Id: yaffs_nand.c,v 1.7 2007-02-14 01:09:06 wookey Exp $";
|
||||||
|
|
||||||
|
#include "yaffs_nand.h"
|
||||||
|
#include "yaffs_tagscompat.h"
|
||||||
|
#include "yaffs_tagsvalidity.h"
|
||||||
|
|
||||||
|
|
||||||
|
int yaffs_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND,
|
||||||
|
__u8 * buffer,
|
||||||
|
yaffs_ExtendedTags * tags)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
yaffs_ExtendedTags localTags;
|
||||||
|
|
||||||
|
int realignedChunkInNAND = chunkInNAND - dev->chunkOffset;
|
||||||
|
|
||||||
|
/* If there are no tags provided, use local tags to get prioritised gc working */
|
||||||
|
if(!tags)
|
||||||
|
tags = &localTags;
|
||||||
|
|
||||||
|
if (dev->readChunkWithTagsFromNAND)
|
||||||
|
result = dev->readChunkWithTagsFromNAND(dev, realignedChunkInNAND, buffer,
|
||||||
|
tags);
|
||||||
|
else
|
||||||
|
result = yaffs_TagsCompatabilityReadChunkWithTagsFromNAND(dev,
|
||||||
|
realignedChunkInNAND,
|
||||||
|
buffer,
|
||||||
|
tags);
|
||||||
|
if(tags &&
|
||||||
|
tags->eccResult > YAFFS_ECC_RESULT_NO_ERROR){
|
||||||
|
|
||||||
|
yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev, chunkInNAND/dev->nChunksPerBlock);
|
||||||
|
yaffs_HandleChunkError(dev,bi);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int yaffs_WriteChunkWithTagsToNAND(yaffs_Device * dev,
|
||||||
|
int chunkInNAND,
|
||||||
|
const __u8 * buffer,
|
||||||
|
yaffs_ExtendedTags * tags)
|
||||||
|
{
|
||||||
|
chunkInNAND -= dev->chunkOffset;
|
||||||
|
|
||||||
|
|
||||||
|
if (tags) {
|
||||||
|
tags->sequenceNumber = dev->sequenceNumber;
|
||||||
|
tags->chunkUsed = 1;
|
||||||
|
if (!yaffs_ValidateTags(tags)) {
|
||||||
|
T(YAFFS_TRACE_ERROR,
|
||||||
|
(TSTR("Writing uninitialised tags" TENDSTR)));
|
||||||
|
YBUG();
|
||||||
|
}
|
||||||
|
T(YAFFS_TRACE_WRITE,
|
||||||
|
(TSTR("Writing chunk %d tags %d %d" TENDSTR), chunkInNAND,
|
||||||
|
tags->objectId, tags->chunkId));
|
||||||
|
} else {
|
||||||
|
T(YAFFS_TRACE_ERROR, (TSTR("Writing with no tags" TENDSTR)));
|
||||||
|
YBUG();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dev->writeChunkWithTagsToNAND)
|
||||||
|
return dev->writeChunkWithTagsToNAND(dev, chunkInNAND, buffer,
|
||||||
|
tags);
|
||||||
|
else
|
||||||
|
return yaffs_TagsCompatabilityWriteChunkWithTagsToNAND(dev,
|
||||||
|
chunkInNAND,
|
||||||
|
buffer,
|
||||||
|
tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
int yaffs_MarkBlockBad(yaffs_Device * dev, int blockNo)
|
||||||
|
{
|
||||||
|
blockNo -= dev->blockOffset;
|
||||||
|
|
||||||
|
;
|
||||||
|
if (dev->markNANDBlockBad)
|
||||||
|
return dev->markNANDBlockBad(dev, blockNo);
|
||||||
|
else
|
||||||
|
return yaffs_TagsCompatabilityMarkNANDBlockBad(dev, blockNo);
|
||||||
|
}
|
||||||
|
|
||||||
|
int yaffs_QueryInitialBlockState(yaffs_Device * dev,
|
||||||
|
int blockNo,
|
||||||
|
yaffs_BlockState * state,
|
||||||
|
unsigned *sequenceNumber)
|
||||||
|
{
|
||||||
|
blockNo -= dev->blockOffset;
|
||||||
|
|
||||||
|
if (dev->queryNANDBlock)
|
||||||
|
return dev->queryNANDBlock(dev, blockNo, state, sequenceNumber);
|
||||||
|
else
|
||||||
|
return yaffs_TagsCompatabilityQueryNANDBlock(dev, blockNo,
|
||||||
|
state,
|
||||||
|
sequenceNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev,
|
||||||
|
int blockInNAND)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
blockInNAND -= dev->blockOffset;
|
||||||
|
|
||||||
|
|
||||||
|
dev->nBlockErasures++;
|
||||||
|
result = dev->eraseBlockInNAND(dev, blockInNAND);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int yaffs_InitialiseNAND(struct yaffs_DeviceStruct *dev)
|
||||||
|
{
|
||||||
|
return dev->initialiseNAND(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
44
target/linux/generic-2.6/files-2.6.27/fs/yaffs2/yaffs_nand.h
Normal file
44
target/linux/generic-2.6/files-2.6.27/fs/yaffs2/yaffs_nand.h
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2002-2007 Aleph One Ltd.
|
||||||
|
* for Toby Churchill Ltd and Brightstar Engineering
|
||||||
|
*
|
||||||
|
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __YAFFS_NAND_H__
|
||||||
|
#define __YAFFS_NAND_H__
|
||||||
|
#include "yaffs_guts.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int yaffs_ReadChunkWithTagsFromNAND(yaffs_Device * dev, int chunkInNAND,
|
||||||
|
__u8 * buffer,
|
||||||
|
yaffs_ExtendedTags * tags);
|
||||||
|
|
||||||
|
int yaffs_WriteChunkWithTagsToNAND(yaffs_Device * dev,
|
||||||
|
int chunkInNAND,
|
||||||
|
const __u8 * buffer,
|
||||||
|
yaffs_ExtendedTags * tags);
|
||||||
|
|
||||||
|
int yaffs_MarkBlockBad(yaffs_Device * dev, int blockNo);
|
||||||
|
|
||||||
|
int yaffs_QueryInitialBlockState(yaffs_Device * dev,
|
||||||
|
int blockNo,
|
||||||
|
yaffs_BlockState * state,
|
||||||
|
unsigned *sequenceNumber);
|
||||||
|
|
||||||
|
int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev,
|
||||||
|
int blockInNAND);
|
||||||
|
|
||||||
|
int yaffs_InitialiseNAND(struct yaffs_DeviceStruct *dev);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2002-2007 Aleph One Ltd.
|
||||||
|
* for Toby Churchill Ltd and Brightstar Engineering
|
||||||
|
*
|
||||||
|
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Interface to emulated NAND functions (2k page size) */
|
||||||
|
|
||||||
|
#ifndef __YAFFS_NANDEMUL2K_H__
|
||||||
|
#define __YAFFS_NANDEMUL2K_H__
|
||||||
|
|
||||||
|
#include "yaffs_guts.h"
|
||||||
|
|
||||||
|
int nandemul2k_WriteChunkWithTagsToNAND(struct yaffs_DeviceStruct *dev,
|
||||||
|
int chunkInNAND, const __u8 * data,
|
||||||
|
yaffs_ExtendedTags * tags);
|
||||||
|
int nandemul2k_ReadChunkWithTagsFromNAND(struct yaffs_DeviceStruct *dev,
|
||||||
|
int chunkInNAND, __u8 * data,
|
||||||
|
yaffs_ExtendedTags * tags);
|
||||||
|
int nandemul2k_MarkNANDBlockBad(struct yaffs_DeviceStruct *dev, int blockNo);
|
||||||
|
int nandemul2k_QueryNANDBlock(struct yaffs_DeviceStruct *dev, int blockNo,
|
||||||
|
yaffs_BlockState * state, int *sequenceNumber);
|
||||||
|
int nandemul2k_EraseBlockInNAND(struct yaffs_DeviceStruct *dev,
|
||||||
|
int blockInNAND);
|
||||||
|
int nandemul2k_InitialiseNAND(struct yaffs_DeviceStruct *dev);
|
||||||
|
int nandemul2k_GetBytesPerChunk(void);
|
||||||
|
int nandemul2k_GetChunksPerBlock(void);
|
||||||
|
int nandemul2k_GetNumberOfBlocks(void);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2002-2007 Aleph One Ltd.
|
||||||
|
* for Toby Churchill Ltd and Brightstar Engineering
|
||||||
|
*
|
||||||
|
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "yaffs_packedtags1.h"
|
||||||
|
#include "yportenv.h"
|
||||||
|
|
||||||
|
void yaffs_PackTags1(yaffs_PackedTags1 * pt, const yaffs_ExtendedTags * t)
|
||||||
|
{
|
||||||
|
pt->chunkId = t->chunkId;
|
||||||
|
pt->serialNumber = t->serialNumber;
|
||||||
|
pt->byteCount = t->byteCount;
|
||||||
|
pt->objectId = t->objectId;
|
||||||
|
pt->ecc = 0;
|
||||||
|
pt->deleted = (t->chunkDeleted) ? 0 : 1;
|
||||||
|
pt->unusedStuff = 0;
|
||||||
|
pt->shouldBeFF = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void yaffs_UnpackTags1(yaffs_ExtendedTags * t, const yaffs_PackedTags1 * pt)
|
||||||
|
{
|
||||||
|
static const __u8 allFF[] =
|
||||||
|
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff };
|
||||||
|
|
||||||
|
if (memcmp(allFF, pt, sizeof(yaffs_PackedTags1))) {
|
||||||
|
t->blockBad = 0;
|
||||||
|
if (pt->shouldBeFF != 0xFFFFFFFF) {
|
||||||
|
t->blockBad = 1;
|
||||||
|
}
|
||||||
|
t->chunkUsed = 1;
|
||||||
|
t->objectId = pt->objectId;
|
||||||
|
t->chunkId = pt->chunkId;
|
||||||
|
t->byteCount = pt->byteCount;
|
||||||
|
t->eccResult = YAFFS_ECC_RESULT_NO_ERROR;
|
||||||
|
t->chunkDeleted = (pt->deleted) ? 0 : 1;
|
||||||
|
t->serialNumber = pt->serialNumber;
|
||||||
|
} else {
|
||||||
|
memset(t, 0, sizeof(yaffs_ExtendedTags));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2002-2007 Aleph One Ltd.
|
||||||
|
* for Toby Churchill Ltd and Brightstar Engineering
|
||||||
|
*
|
||||||
|
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* This is used to pack YAFFS1 tags, not YAFFS2 tags. */
|
||||||
|
|
||||||
|
#ifndef __YAFFS_PACKEDTAGS1_H__
|
||||||
|
#define __YAFFS_PACKEDTAGS1_H__
|
||||||
|
|
||||||
|
#include "yaffs_guts.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned chunkId:20;
|
||||||
|
unsigned serialNumber:2;
|
||||||
|
unsigned byteCount:10;
|
||||||
|
unsigned objectId:18;
|
||||||
|
unsigned ecc:12;
|
||||||
|
unsigned deleted:1;
|
||||||
|
unsigned unusedStuff:1;
|
||||||
|
unsigned shouldBeFF;
|
||||||
|
|
||||||
|
} yaffs_PackedTags1;
|
||||||
|
|
||||||
|
void yaffs_PackTags1(yaffs_PackedTags1 * pt, const yaffs_ExtendedTags * t);
|
||||||
|
void yaffs_UnpackTags1(yaffs_ExtendedTags * t, const yaffs_PackedTags1 * pt);
|
||||||
|
#endif
|
|
@ -0,0 +1,182 @@
|
||||||
|
/*
|
||||||
|
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2002-2007 Aleph One Ltd.
|
||||||
|
* for Toby Churchill Ltd and Brightstar Engineering
|
||||||
|
*
|
||||||
|
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "yaffs_packedtags2.h"
|
||||||
|
#include "yportenv.h"
|
||||||
|
#include "yaffs_tagsvalidity.h"
|
||||||
|
|
||||||
|
/* This code packs a set of extended tags into a binary structure for
|
||||||
|
* NAND storage
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Some of the information is "extra" struff which can be packed in to
|
||||||
|
* speed scanning
|
||||||
|
* This is defined by having the EXTRA_HEADER_INFO_FLAG set.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Extra flags applied to chunkId */
|
||||||
|
|
||||||
|
#define EXTRA_HEADER_INFO_FLAG 0x80000000
|
||||||
|
#define EXTRA_SHRINK_FLAG 0x40000000
|
||||||
|
#define EXTRA_SHADOWS_FLAG 0x20000000
|
||||||
|
#define EXTRA_SPARE_FLAGS 0x10000000
|
||||||
|
|
||||||
|
#define ALL_EXTRA_FLAGS 0xF0000000
|
||||||
|
|
||||||
|
/* Also, the top 4 bits of the object Id are set to the object type. */
|
||||||
|
#define EXTRA_OBJECT_TYPE_SHIFT (28)
|
||||||
|
#define EXTRA_OBJECT_TYPE_MASK ((0x0F) << EXTRA_OBJECT_TYPE_SHIFT)
|
||||||
|
|
||||||
|
static void yaffs_DumpPackedTags2(const yaffs_PackedTags2 * pt)
|
||||||
|
{
|
||||||
|
T(YAFFS_TRACE_MTD,
|
||||||
|
(TSTR("packed tags obj %d chunk %d byte %d seq %d" TENDSTR),
|
||||||
|
pt->t.objectId, pt->t.chunkId, pt->t.byteCount,
|
||||||
|
pt->t.sequenceNumber));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void yaffs_DumpTags2(const yaffs_ExtendedTags * t)
|
||||||
|
{
|
||||||
|
T(YAFFS_TRACE_MTD,
|
||||||
|
(TSTR
|
||||||
|
("ext.tags eccres %d blkbad %d chused %d obj %d chunk%d byte "
|
||||||
|
"%d del %d ser %d seq %d"
|
||||||
|
TENDSTR), t->eccResult, t->blockBad, t->chunkUsed, t->objectId,
|
||||||
|
t->chunkId, t->byteCount, t->chunkDeleted, t->serialNumber,
|
||||||
|
t->sequenceNumber));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void yaffs_PackTags2(yaffs_PackedTags2 * pt, const yaffs_ExtendedTags * t)
|
||||||
|
{
|
||||||
|
pt->t.chunkId = t->chunkId;
|
||||||
|
pt->t.sequenceNumber = t->sequenceNumber;
|
||||||
|
pt->t.byteCount = t->byteCount;
|
||||||
|
pt->t.objectId = t->objectId;
|
||||||
|
|
||||||
|
if (t->chunkId == 0 && t->extraHeaderInfoAvailable) {
|
||||||
|
/* Store the extra header info instead */
|
||||||
|
/* We save the parent object in the chunkId */
|
||||||
|
pt->t.chunkId = EXTRA_HEADER_INFO_FLAG
|
||||||
|
| t->extraParentObjectId;
|
||||||
|
if (t->extraIsShrinkHeader) {
|
||||||
|
pt->t.chunkId |= EXTRA_SHRINK_FLAG;
|
||||||
|
}
|
||||||
|
if (t->extraShadows) {
|
||||||
|
pt->t.chunkId |= EXTRA_SHADOWS_FLAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
pt->t.objectId &= ~EXTRA_OBJECT_TYPE_MASK;
|
||||||
|
pt->t.objectId |=
|
||||||
|
(t->extraObjectType << EXTRA_OBJECT_TYPE_SHIFT);
|
||||||
|
|
||||||
|
if (t->extraObjectType == YAFFS_OBJECT_TYPE_HARDLINK) {
|
||||||
|
pt->t.byteCount = t->extraEquivalentObjectId;
|
||||||
|
} else if (t->extraObjectType == YAFFS_OBJECT_TYPE_FILE) {
|
||||||
|
pt->t.byteCount = t->extraFileLength;
|
||||||
|
} else {
|
||||||
|
pt->t.byteCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yaffs_DumpPackedTags2(pt);
|
||||||
|
yaffs_DumpTags2(t);
|
||||||
|
|
||||||
|
#ifndef YAFFS_IGNORE_TAGS_ECC
|
||||||
|
{
|
||||||
|
yaffs_ECCCalculateOther((unsigned char *)&pt->t,
|
||||||
|
sizeof(yaffs_PackedTags2TagsPart),
|
||||||
|
&pt->ecc);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void yaffs_UnpackTags2(yaffs_ExtendedTags * t, yaffs_PackedTags2 * pt)
|
||||||
|
{
|
||||||
|
|
||||||
|
memset(t, 0, sizeof(yaffs_ExtendedTags));
|
||||||
|
|
||||||
|
yaffs_InitialiseTags(t);
|
||||||
|
|
||||||
|
if (pt->t.sequenceNumber != 0xFFFFFFFF) {
|
||||||
|
/* Page is in use */
|
||||||
|
#ifdef YAFFS_IGNORE_TAGS_ECC
|
||||||
|
{
|
||||||
|
t->eccResult = YAFFS_ECC_RESULT_NO_ERROR;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
{
|
||||||
|
yaffs_ECCOther ecc;
|
||||||
|
int result;
|
||||||
|
yaffs_ECCCalculateOther((unsigned char *)&pt->t,
|
||||||
|
sizeof
|
||||||
|
(yaffs_PackedTags2TagsPart),
|
||||||
|
&ecc);
|
||||||
|
result =
|
||||||
|
yaffs_ECCCorrectOther((unsigned char *)&pt->t,
|
||||||
|
sizeof
|
||||||
|
(yaffs_PackedTags2TagsPart),
|
||||||
|
&pt->ecc, &ecc);
|
||||||
|
switch(result){
|
||||||
|
case 0:
|
||||||
|
t->eccResult = YAFFS_ECC_RESULT_NO_ERROR;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
t->eccResult = YAFFS_ECC_RESULT_FIXED;
|
||||||
|
break;
|
||||||
|
case -1:
|
||||||
|
t->eccResult = YAFFS_ECC_RESULT_UNFIXED;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
t->eccResult = YAFFS_ECC_RESULT_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
t->blockBad = 0;
|
||||||
|
t->chunkUsed = 1;
|
||||||
|
t->objectId = pt->t.objectId;
|
||||||
|
t->chunkId = pt->t.chunkId;
|
||||||
|
t->byteCount = pt->t.byteCount;
|
||||||
|
t->chunkDeleted = 0;
|
||||||
|
t->serialNumber = 0;
|
||||||
|
t->sequenceNumber = pt->t.sequenceNumber;
|
||||||
|
|
||||||
|
/* Do extra header info stuff */
|
||||||
|
|
||||||
|
if (pt->t.chunkId & EXTRA_HEADER_INFO_FLAG) {
|
||||||
|
t->chunkId = 0;
|
||||||
|
t->byteCount = 0;
|
||||||
|
|
||||||
|
t->extraHeaderInfoAvailable = 1;
|
||||||
|
t->extraParentObjectId =
|
||||||
|
pt->t.chunkId & (~(ALL_EXTRA_FLAGS));
|
||||||
|
t->extraIsShrinkHeader =
|
||||||
|
(pt->t.chunkId & EXTRA_SHRINK_FLAG) ? 1 : 0;
|
||||||
|
t->extraShadows =
|
||||||
|
(pt->t.chunkId & EXTRA_SHADOWS_FLAG) ? 1 : 0;
|
||||||
|
t->extraObjectType =
|
||||||
|
pt->t.objectId >> EXTRA_OBJECT_TYPE_SHIFT;
|
||||||
|
t->objectId &= ~EXTRA_OBJECT_TYPE_MASK;
|
||||||
|
|
||||||
|
if (t->extraObjectType == YAFFS_OBJECT_TYPE_HARDLINK) {
|
||||||
|
t->extraEquivalentObjectId = pt->t.byteCount;
|
||||||
|
} else {
|
||||||
|
t->extraFileLength = pt->t.byteCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yaffs_DumpPackedTags2(pt);
|
||||||
|
yaffs_DumpTags2(t);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2002-2007 Aleph One Ltd.
|
||||||
|
* for Toby Churchill Ltd and Brightstar Engineering
|
||||||
|
*
|
||||||
|
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* This is used to pack YAFFS2 tags, not YAFFS1tags. */
|
||||||
|
|
||||||
|
#ifndef __YAFFS_PACKEDTAGS2_H__
|
||||||
|
#define __YAFFS_PACKEDTAGS2_H__
|
||||||
|
|
||||||
|
#include "yaffs_guts.h"
|
||||||
|
#include "yaffs_ecc.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned sequenceNumber;
|
||||||
|
unsigned objectId;
|
||||||
|
unsigned chunkId;
|
||||||
|
unsigned byteCount;
|
||||||
|
} yaffs_PackedTags2TagsPart;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
yaffs_PackedTags2TagsPart t;
|
||||||
|
yaffs_ECCOther ecc;
|
||||||
|
} yaffs_PackedTags2;
|
||||||
|
|
||||||
|
void yaffs_PackTags2(yaffs_PackedTags2 * pt, const yaffs_ExtendedTags * t);
|
||||||
|
void yaffs_UnpackTags2(yaffs_ExtendedTags * t, yaffs_PackedTags2 * pt);
|
||||||
|
#endif
|
160
target/linux/generic-2.6/files-2.6.27/fs/yaffs2/yaffs_qsort.c
Normal file
160
target/linux/generic-2.6/files-2.6.27/fs/yaffs2/yaffs_qsort.c
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1992, 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.
|
||||||
|
* 3. 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "yportenv.h"
|
||||||
|
//#include <linux/string.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Qsort routine from Bentley & McIlroy's "Engineering a Sort Function".
|
||||||
|
*/
|
||||||
|
#define swapcode(TYPE, parmi, parmj, n) { \
|
||||||
|
long i = (n) / sizeof (TYPE); \
|
||||||
|
register TYPE *pi = (TYPE *) (parmi); \
|
||||||
|
register TYPE *pj = (TYPE *) (parmj); \
|
||||||
|
do { \
|
||||||
|
register TYPE t = *pi; \
|
||||||
|
*pi++ = *pj; \
|
||||||
|
*pj++ = t; \
|
||||||
|
} while (--i > 0); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \
|
||||||
|
es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1;
|
||||||
|
|
||||||
|
static __inline void
|
||||||
|
swapfunc(char *a, char *b, int n, int swaptype)
|
||||||
|
{
|
||||||
|
if (swaptype <= 1)
|
||||||
|
swapcode(long, a, b, n)
|
||||||
|
else
|
||||||
|
swapcode(char, a, b, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
#define swap(a, b) \
|
||||||
|
if (swaptype == 0) { \
|
||||||
|
long t = *(long *)(a); \
|
||||||
|
*(long *)(a) = *(long *)(b); \
|
||||||
|
*(long *)(b) = t; \
|
||||||
|
} else \
|
||||||
|
swapfunc(a, b, es, swaptype)
|
||||||
|
|
||||||
|
#define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype)
|
||||||
|
|
||||||
|
static __inline char *
|
||||||
|
med3(char *a, char *b, char *c, int (*cmp)(const void *, const void *))
|
||||||
|
{
|
||||||
|
return cmp(a, b) < 0 ?
|
||||||
|
(cmp(b, c) < 0 ? b : (cmp(a, c) < 0 ? c : a ))
|
||||||
|
:(cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c ));
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef min
|
||||||
|
#define min(a,b) (((a) < (b)) ? (a) : (b))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void
|
||||||
|
yaffs_qsort(void *aa, size_t n, size_t es,
|
||||||
|
int (*cmp)(const void *, const void *))
|
||||||
|
{
|
||||||
|
char *pa, *pb, *pc, *pd, *pl, *pm, *pn;
|
||||||
|
int d, r, swaptype, swap_cnt;
|
||||||
|
register char *a = aa;
|
||||||
|
|
||||||
|
loop: SWAPINIT(a, es);
|
||||||
|
swap_cnt = 0;
|
||||||
|
if (n < 7) {
|
||||||
|
for (pm = (char *)a + es; pm < (char *) a + n * es; pm += es)
|
||||||
|
for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0;
|
||||||
|
pl -= es)
|
||||||
|
swap(pl, pl - es);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pm = (char *)a + (n / 2) * es;
|
||||||
|
if (n > 7) {
|
||||||
|
pl = (char *)a;
|
||||||
|
pn = (char *)a + (n - 1) * es;
|
||||||
|
if (n > 40) {
|
||||||
|
d = (n / 8) * es;
|
||||||
|
pl = med3(pl, pl + d, pl + 2 * d, cmp);
|
||||||
|
pm = med3(pm - d, pm, pm + d, cmp);
|
||||||
|
pn = med3(pn - 2 * d, pn - d, pn, cmp);
|
||||||
|
}
|
||||||
|
pm = med3(pl, pm, pn, cmp);
|
||||||
|
}
|
||||||
|
swap(a, pm);
|
||||||
|
pa = pb = (char *)a + es;
|
||||||
|
|
||||||
|
pc = pd = (char *)a + (n - 1) * es;
|
||||||
|
for (;;) {
|
||||||
|
while (pb <= pc && (r = cmp(pb, a)) <= 0) {
|
||||||
|
if (r == 0) {
|
||||||
|
swap_cnt = 1;
|
||||||
|
swap(pa, pb);
|
||||||
|
pa += es;
|
||||||
|
}
|
||||||
|
pb += es;
|
||||||
|
}
|
||||||
|
while (pb <= pc && (r = cmp(pc, a)) >= 0) {
|
||||||
|
if (r == 0) {
|
||||||
|
swap_cnt = 1;
|
||||||
|
swap(pc, pd);
|
||||||
|
pd -= es;
|
||||||
|
}
|
||||||
|
pc -= es;
|
||||||
|
}
|
||||||
|
if (pb > pc)
|
||||||
|
break;
|
||||||
|
swap(pb, pc);
|
||||||
|
swap_cnt = 1;
|
||||||
|
pb += es;
|
||||||
|
pc -= es;
|
||||||
|
}
|
||||||
|
if (swap_cnt == 0) { /* Switch to insertion sort */
|
||||||
|
for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es)
|
||||||
|
for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0;
|
||||||
|
pl -= es)
|
||||||
|
swap(pl, pl - es);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pn = (char *)a + n * es;
|
||||||
|
r = min(pa - (char *)a, pb - pa);
|
||||||
|
vecswap(a, pb - r, r);
|
||||||
|
r = min((long)(pd - pc), (long)(pn - pd - es));
|
||||||
|
vecswap(pb, pn - r, r);
|
||||||
|
if ((r = pb - pa) > es)
|
||||||
|
yaffs_qsort(a, r / es, es, cmp);
|
||||||
|
if ((r = pd - pc) > es) {
|
||||||
|
/* Iterate rather than recurse to save stack space */
|
||||||
|
a = pn - r;
|
||||||
|
n = r / es;
|
||||||
|
goto loop;
|
||||||
|
}
|
||||||
|
/* yaffs_qsort(pn - r, r / es, es, cmp);*/
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2002-2007 Aleph One Ltd.
|
||||||
|
* for Toby Churchill Ltd and Brightstar Engineering
|
||||||
|
*
|
||||||
|
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef __YAFFS_QSORT_H__
|
||||||
|
#define __YAFFS_QSORT_H__
|
||||||
|
|
||||||
|
extern void yaffs_qsort (void *const base, size_t total_elems, size_t size,
|
||||||
|
int (*cmp)(const void *, const void *));
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,530 @@
|
||||||
|
/*
|
||||||
|
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2002-2007 Aleph One Ltd.
|
||||||
|
* for Toby Churchill Ltd and Brightstar Engineering
|
||||||
|
*
|
||||||
|
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "yaffs_guts.h"
|
||||||
|
#include "yaffs_tagscompat.h"
|
||||||
|
#include "yaffs_ecc.h"
|
||||||
|
|
||||||
|
static void yaffs_HandleReadDataError(yaffs_Device * dev, int chunkInNAND);
|
||||||
|
#ifdef NOTYET
|
||||||
|
static void yaffs_CheckWrittenBlock(yaffs_Device * dev, int chunkInNAND);
|
||||||
|
static void yaffs_HandleWriteChunkOk(yaffs_Device * dev, int chunkInNAND,
|
||||||
|
const __u8 * data,
|
||||||
|
const yaffs_Spare * spare);
|
||||||
|
static void yaffs_HandleUpdateChunk(yaffs_Device * dev, int chunkInNAND,
|
||||||
|
const yaffs_Spare * spare);
|
||||||
|
static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const char yaffs_countBitsTable[256] = {
|
||||||
|
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
|
||||||
|
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
||||||
|
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
||||||
|
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||||
|
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
||||||
|
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||||
|
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||||
|
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
||||||
|
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
||||||
|
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||||
|
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||||
|
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
||||||
|
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||||
|
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
||||||
|
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
||||||
|
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
|
||||||
|
};
|
||||||
|
|
||||||
|
int yaffs_CountBits(__u8 x)
|
||||||
|
{
|
||||||
|
int retVal;
|
||||||
|
retVal = yaffs_countBitsTable[x];
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********** Tags ECC calculations *********/
|
||||||
|
|
||||||
|
void yaffs_CalcECC(const __u8 * data, yaffs_Spare * spare)
|
||||||
|
{
|
||||||
|
yaffs_ECCCalculate(data, spare->ecc1);
|
||||||
|
yaffs_ECCCalculate(&data[256], spare->ecc2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void yaffs_CalcTagsECC(yaffs_Tags * tags)
|
||||||
|
{
|
||||||
|
/* Calculate an ecc */
|
||||||
|
|
||||||
|
unsigned char *b = ((yaffs_TagsUnion *) tags)->asBytes;
|
||||||
|
unsigned i, j;
|
||||||
|
unsigned ecc = 0;
|
||||||
|
unsigned bit = 0;
|
||||||
|
|
||||||
|
tags->ecc = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
for (j = 1; j & 0xff; j <<= 1) {
|
||||||
|
bit++;
|
||||||
|
if (b[i] & j) {
|
||||||
|
ecc ^= bit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tags->ecc = ecc;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int yaffs_CheckECCOnTags(yaffs_Tags * tags)
|
||||||
|
{
|
||||||
|
unsigned ecc = tags->ecc;
|
||||||
|
|
||||||
|
yaffs_CalcTagsECC(tags);
|
||||||
|
|
||||||
|
ecc ^= tags->ecc;
|
||||||
|
|
||||||
|
if (ecc && ecc <= 64) {
|
||||||
|
/* TODO: Handle the failure better. Retire? */
|
||||||
|
unsigned char *b = ((yaffs_TagsUnion *) tags)->asBytes;
|
||||||
|
|
||||||
|
ecc--;
|
||||||
|
|
||||||
|
b[ecc / 8] ^= (1 << (ecc & 7));
|
||||||
|
|
||||||
|
/* Now recvalc the ecc */
|
||||||
|
yaffs_CalcTagsECC(tags);
|
||||||
|
|
||||||
|
return 1; /* recovered error */
|
||||||
|
} else if (ecc) {
|
||||||
|
/* Wierd ecc failure value */
|
||||||
|
/* TODO Need to do somethiong here */
|
||||||
|
return -1; /* unrecovered error */
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********** Tags **********/
|
||||||
|
|
||||||
|
static void yaffs_LoadTagsIntoSpare(yaffs_Spare * sparePtr,
|
||||||
|
yaffs_Tags * tagsPtr)
|
||||||
|
{
|
||||||
|
yaffs_TagsUnion *tu = (yaffs_TagsUnion *) tagsPtr;
|
||||||
|
|
||||||
|
yaffs_CalcTagsECC(tagsPtr);
|
||||||
|
|
||||||
|
sparePtr->tagByte0 = tu->asBytes[0];
|
||||||
|
sparePtr->tagByte1 = tu->asBytes[1];
|
||||||
|
sparePtr->tagByte2 = tu->asBytes[2];
|
||||||
|
sparePtr->tagByte3 = tu->asBytes[3];
|
||||||
|
sparePtr->tagByte4 = tu->asBytes[4];
|
||||||
|
sparePtr->tagByte5 = tu->asBytes[5];
|
||||||
|
sparePtr->tagByte6 = tu->asBytes[6];
|
||||||
|
sparePtr->tagByte7 = tu->asBytes[7];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void yaffs_GetTagsFromSpare(yaffs_Device * dev, yaffs_Spare * sparePtr,
|
||||||
|
yaffs_Tags * tagsPtr)
|
||||||
|
{
|
||||||
|
yaffs_TagsUnion *tu = (yaffs_TagsUnion *) tagsPtr;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
tu->asBytes[0] = sparePtr->tagByte0;
|
||||||
|
tu->asBytes[1] = sparePtr->tagByte1;
|
||||||
|
tu->asBytes[2] = sparePtr->tagByte2;
|
||||||
|
tu->asBytes[3] = sparePtr->tagByte3;
|
||||||
|
tu->asBytes[4] = sparePtr->tagByte4;
|
||||||
|
tu->asBytes[5] = sparePtr->tagByte5;
|
||||||
|
tu->asBytes[6] = sparePtr->tagByte6;
|
||||||
|
tu->asBytes[7] = sparePtr->tagByte7;
|
||||||
|
|
||||||
|
result = yaffs_CheckECCOnTags(tagsPtr);
|
||||||
|
if (result > 0) {
|
||||||
|
dev->tagsEccFixed++;
|
||||||
|
} else if (result < 0) {
|
||||||
|
dev->tagsEccUnfixed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void yaffs_SpareInitialise(yaffs_Spare * spare)
|
||||||
|
{
|
||||||
|
memset(spare, 0xFF, sizeof(yaffs_Spare));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int yaffs_WriteChunkToNAND(struct yaffs_DeviceStruct *dev,
|
||||||
|
int chunkInNAND, const __u8 * data,
|
||||||
|
yaffs_Spare * spare)
|
||||||
|
{
|
||||||
|
if (chunkInNAND < dev->startBlock * dev->nChunksPerBlock) {
|
||||||
|
T(YAFFS_TRACE_ERROR,
|
||||||
|
(TSTR("**>> yaffs chunk %d is not valid" TENDSTR),
|
||||||
|
chunkInNAND));
|
||||||
|
return YAFFS_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->nPageWrites++;
|
||||||
|
return dev->writeChunkToNAND(dev, chunkInNAND, data, spare);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev,
|
||||||
|
int chunkInNAND,
|
||||||
|
__u8 * data,
|
||||||
|
yaffs_Spare * spare,
|
||||||
|
yaffs_ECCResult * eccResult,
|
||||||
|
int doErrorCorrection)
|
||||||
|
{
|
||||||
|
int retVal;
|
||||||
|
yaffs_Spare localSpare;
|
||||||
|
|
||||||
|
dev->nPageReads++;
|
||||||
|
|
||||||
|
if (!spare && data) {
|
||||||
|
/* If we don't have a real spare, then we use a local one. */
|
||||||
|
/* Need this for the calculation of the ecc */
|
||||||
|
spare = &localSpare;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dev->useNANDECC) {
|
||||||
|
retVal = dev->readChunkFromNAND(dev, chunkInNAND, data, spare);
|
||||||
|
if (data && doErrorCorrection) {
|
||||||
|
/* Do ECC correction */
|
||||||
|
/* Todo handle any errors */
|
||||||
|
int eccResult1, eccResult2;
|
||||||
|
__u8 calcEcc[3];
|
||||||
|
|
||||||
|
yaffs_ECCCalculate(data, calcEcc);
|
||||||
|
eccResult1 =
|
||||||
|
yaffs_ECCCorrect(data, spare->ecc1, calcEcc);
|
||||||
|
yaffs_ECCCalculate(&data[256], calcEcc);
|
||||||
|
eccResult2 =
|
||||||
|
yaffs_ECCCorrect(&data[256], spare->ecc2, calcEcc);
|
||||||
|
|
||||||
|
if (eccResult1 > 0) {
|
||||||
|
T(YAFFS_TRACE_ERROR,
|
||||||
|
(TSTR
|
||||||
|
("**>>yaffs ecc error fix performed on chunk %d:0"
|
||||||
|
TENDSTR), chunkInNAND));
|
||||||
|
dev->eccFixed++;
|
||||||
|
} else if (eccResult1 < 0) {
|
||||||
|
T(YAFFS_TRACE_ERROR,
|
||||||
|
(TSTR
|
||||||
|
("**>>yaffs ecc error unfixed on chunk %d:0"
|
||||||
|
TENDSTR), chunkInNAND));
|
||||||
|
dev->eccUnfixed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eccResult2 > 0) {
|
||||||
|
T(YAFFS_TRACE_ERROR,
|
||||||
|
(TSTR
|
||||||
|
("**>>yaffs ecc error fix performed on chunk %d:1"
|
||||||
|
TENDSTR), chunkInNAND));
|
||||||
|
dev->eccFixed++;
|
||||||
|
} else if (eccResult2 < 0) {
|
||||||
|
T(YAFFS_TRACE_ERROR,
|
||||||
|
(TSTR
|
||||||
|
("**>>yaffs ecc error unfixed on chunk %d:1"
|
||||||
|
TENDSTR), chunkInNAND));
|
||||||
|
dev->eccUnfixed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eccResult1 || eccResult2) {
|
||||||
|
/* We had a data problem on this page */
|
||||||
|
yaffs_HandleReadDataError(dev, chunkInNAND);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eccResult1 < 0 || eccResult2 < 0)
|
||||||
|
*eccResult = YAFFS_ECC_RESULT_UNFIXED;
|
||||||
|
else if (eccResult1 > 0 || eccResult2 > 0)
|
||||||
|
*eccResult = YAFFS_ECC_RESULT_FIXED;
|
||||||
|
else
|
||||||
|
*eccResult = YAFFS_ECC_RESULT_NO_ERROR;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Must allocate enough memory for spare+2*sizeof(int) */
|
||||||
|
/* for ecc results from device. */
|
||||||
|
struct yaffs_NANDSpare nspare;
|
||||||
|
retVal =
|
||||||
|
dev->readChunkFromNAND(dev, chunkInNAND, data,
|
||||||
|
(yaffs_Spare *) & nspare);
|
||||||
|
memcpy(spare, &nspare, sizeof(yaffs_Spare));
|
||||||
|
if (data && doErrorCorrection) {
|
||||||
|
if (nspare.eccres1 > 0) {
|
||||||
|
T(YAFFS_TRACE_ERROR,
|
||||||
|
(TSTR
|
||||||
|
("**>>mtd ecc error fix performed on chunk %d:0"
|
||||||
|
TENDSTR), chunkInNAND));
|
||||||
|
} else if (nspare.eccres1 < 0) {
|
||||||
|
T(YAFFS_TRACE_ERROR,
|
||||||
|
(TSTR
|
||||||
|
("**>>mtd ecc error unfixed on chunk %d:0"
|
||||||
|
TENDSTR), chunkInNAND));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nspare.eccres2 > 0) {
|
||||||
|
T(YAFFS_TRACE_ERROR,
|
||||||
|
(TSTR
|
||||||
|
("**>>mtd ecc error fix performed on chunk %d:1"
|
||||||
|
TENDSTR), chunkInNAND));
|
||||||
|
} else if (nspare.eccres2 < 0) {
|
||||||
|
T(YAFFS_TRACE_ERROR,
|
||||||
|
(TSTR
|
||||||
|
("**>>mtd ecc error unfixed on chunk %d:1"
|
||||||
|
TENDSTR), chunkInNAND));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nspare.eccres1 || nspare.eccres2) {
|
||||||
|
/* We had a data problem on this page */
|
||||||
|
yaffs_HandleReadDataError(dev, chunkInNAND);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nspare.eccres1 < 0 || nspare.eccres2 < 0)
|
||||||
|
*eccResult = YAFFS_ECC_RESULT_UNFIXED;
|
||||||
|
else if (nspare.eccres1 > 0 || nspare.eccres2 > 0)
|
||||||
|
*eccResult = YAFFS_ECC_RESULT_FIXED;
|
||||||
|
else
|
||||||
|
*eccResult = YAFFS_ECC_RESULT_NO_ERROR;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef NOTYET
|
||||||
|
static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,
|
||||||
|
int chunkInNAND)
|
||||||
|
{
|
||||||
|
|
||||||
|
static int init = 0;
|
||||||
|
static __u8 cmpbuf[YAFFS_BYTES_PER_CHUNK];
|
||||||
|
static __u8 data[YAFFS_BYTES_PER_CHUNK];
|
||||||
|
/* Might as well always allocate the larger size for */
|
||||||
|
/* dev->useNANDECC == true; */
|
||||||
|
static __u8 spare[sizeof(struct yaffs_NANDSpare)];
|
||||||
|
|
||||||
|
dev->readChunkFromNAND(dev, chunkInNAND, data, (yaffs_Spare *) spare);
|
||||||
|
|
||||||
|
if (!init) {
|
||||||
|
memset(cmpbuf, 0xff, YAFFS_BYTES_PER_CHUNK);
|
||||||
|
init = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memcmp(cmpbuf, data, YAFFS_BYTES_PER_CHUNK))
|
||||||
|
return YAFFS_FAIL;
|
||||||
|
if (memcmp(cmpbuf, spare, 16))
|
||||||
|
return YAFFS_FAIL;
|
||||||
|
|
||||||
|
return YAFFS_OK;
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Functions for robustisizing
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void yaffs_HandleReadDataError(yaffs_Device * dev, int chunkInNAND)
|
||||||
|
{
|
||||||
|
int blockInNAND = chunkInNAND / dev->nChunksPerBlock;
|
||||||
|
|
||||||
|
/* Mark the block for retirement */
|
||||||
|
yaffs_GetBlockInfo(dev, blockInNAND)->needsRetiring = 1;
|
||||||
|
T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,
|
||||||
|
(TSTR("**>>Block %d marked for retirement" TENDSTR), blockInNAND));
|
||||||
|
|
||||||
|
/* TODO:
|
||||||
|
* Just do a garbage collection on the affected block
|
||||||
|
* then retire the block
|
||||||
|
* NB recursion
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef NOTYET
|
||||||
|
static void yaffs_CheckWrittenBlock(yaffs_Device * dev, int chunkInNAND)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void yaffs_HandleWriteChunkOk(yaffs_Device * dev, int chunkInNAND,
|
||||||
|
const __u8 * data,
|
||||||
|
const yaffs_Spare * spare)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void yaffs_HandleUpdateChunk(yaffs_Device * dev, int chunkInNAND,
|
||||||
|
const yaffs_Spare * spare)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void yaffs_HandleWriteChunkError(yaffs_Device * dev, int chunkInNAND)
|
||||||
|
{
|
||||||
|
int blockInNAND = chunkInNAND / dev->nChunksPerBlock;
|
||||||
|
|
||||||
|
/* Mark the block for retirement */
|
||||||
|
yaffs_GetBlockInfo(dev, blockInNAND)->needsRetiring = 1;
|
||||||
|
/* Delete the chunk */
|
||||||
|
yaffs_DeleteChunk(dev, chunkInNAND, 1, __LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int yaffs_VerifyCompare(const __u8 * d0, const __u8 * d1,
|
||||||
|
const yaffs_Spare * s0, const yaffs_Spare * s1)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (memcmp(d0, d1, YAFFS_BYTES_PER_CHUNK) != 0 ||
|
||||||
|
s0->tagByte0 != s1->tagByte0 ||
|
||||||
|
s0->tagByte1 != s1->tagByte1 ||
|
||||||
|
s0->tagByte2 != s1->tagByte2 ||
|
||||||
|
s0->tagByte3 != s1->tagByte3 ||
|
||||||
|
s0->tagByte4 != s1->tagByte4 ||
|
||||||
|
s0->tagByte5 != s1->tagByte5 ||
|
||||||
|
s0->tagByte6 != s1->tagByte6 ||
|
||||||
|
s0->tagByte7 != s1->tagByte7 ||
|
||||||
|
s0->ecc1[0] != s1->ecc1[0] ||
|
||||||
|
s0->ecc1[1] != s1->ecc1[1] ||
|
||||||
|
s0->ecc1[2] != s1->ecc1[2] ||
|
||||||
|
s0->ecc2[0] != s1->ecc2[0] ||
|
||||||
|
s0->ecc2[1] != s1->ecc2[1] || s0->ecc2[2] != s1->ecc2[2]) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#endif /* NOTYET */
|
||||||
|
|
||||||
|
int yaffs_TagsCompatabilityWriteChunkWithTagsToNAND(yaffs_Device * dev,
|
||||||
|
int chunkInNAND,
|
||||||
|
const __u8 * data,
|
||||||
|
const yaffs_ExtendedTags *
|
||||||
|
eTags)
|
||||||
|
{
|
||||||
|
yaffs_Spare spare;
|
||||||
|
yaffs_Tags tags;
|
||||||
|
|
||||||
|
yaffs_SpareInitialise(&spare);
|
||||||
|
|
||||||
|
if (eTags->chunkDeleted) {
|
||||||
|
spare.pageStatus = 0;
|
||||||
|
} else {
|
||||||
|
tags.objectId = eTags->objectId;
|
||||||
|
tags.chunkId = eTags->chunkId;
|
||||||
|
tags.byteCount = eTags->byteCount;
|
||||||
|
tags.serialNumber = eTags->serialNumber;
|
||||||
|
|
||||||
|
if (!dev->useNANDECC && data) {
|
||||||
|
yaffs_CalcECC(data, &spare);
|
||||||
|
}
|
||||||
|
yaffs_LoadTagsIntoSpare(&spare, &tags);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return yaffs_WriteChunkToNAND(dev, chunkInNAND, data, &spare);
|
||||||
|
}
|
||||||
|
|
||||||
|
int yaffs_TagsCompatabilityReadChunkWithTagsFromNAND(yaffs_Device * dev,
|
||||||
|
int chunkInNAND,
|
||||||
|
__u8 * data,
|
||||||
|
yaffs_ExtendedTags * eTags)
|
||||||
|
{
|
||||||
|
|
||||||
|
yaffs_Spare spare;
|
||||||
|
yaffs_Tags tags;
|
||||||
|
yaffs_ECCResult eccResult;
|
||||||
|
|
||||||
|
static yaffs_Spare spareFF;
|
||||||
|
static int init;
|
||||||
|
|
||||||
|
if (!init) {
|
||||||
|
memset(&spareFF, 0xFF, sizeof(spareFF));
|
||||||
|
init = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (yaffs_ReadChunkFromNAND
|
||||||
|
(dev, chunkInNAND, data, &spare, &eccResult, 1)) {
|
||||||
|
/* eTags may be NULL */
|
||||||
|
if (eTags) {
|
||||||
|
|
||||||
|
int deleted =
|
||||||
|
(yaffs_CountBits(spare.pageStatus) < 7) ? 1 : 0;
|
||||||
|
|
||||||
|
eTags->chunkDeleted = deleted;
|
||||||
|
eTags->eccResult = eccResult;
|
||||||
|
eTags->blockBad = 0; /* We're reading it */
|
||||||
|
/* therefore it is not a bad block */
|
||||||
|
eTags->chunkUsed =
|
||||||
|
(memcmp(&spareFF, &spare, sizeof(spareFF)) !=
|
||||||
|
0) ? 1 : 0;
|
||||||
|
|
||||||
|
if (eTags->chunkUsed) {
|
||||||
|
yaffs_GetTagsFromSpare(dev, &spare, &tags);
|
||||||
|
|
||||||
|
eTags->objectId = tags.objectId;
|
||||||
|
eTags->chunkId = tags.chunkId;
|
||||||
|
eTags->byteCount = tags.byteCount;
|
||||||
|
eTags->serialNumber = tags.serialNumber;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return YAFFS_OK;
|
||||||
|
} else {
|
||||||
|
return YAFFS_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int yaffs_TagsCompatabilityMarkNANDBlockBad(struct yaffs_DeviceStruct *dev,
|
||||||
|
int blockInNAND)
|
||||||
|
{
|
||||||
|
|
||||||
|
yaffs_Spare spare;
|
||||||
|
|
||||||
|
memset(&spare, 0xff, sizeof(yaffs_Spare));
|
||||||
|
|
||||||
|
spare.blockStatus = 'Y';
|
||||||
|
|
||||||
|
yaffs_WriteChunkToNAND(dev, blockInNAND * dev->nChunksPerBlock, NULL,
|
||||||
|
&spare);
|
||||||
|
yaffs_WriteChunkToNAND(dev, blockInNAND * dev->nChunksPerBlock + 1,
|
||||||
|
NULL, &spare);
|
||||||
|
|
||||||
|
return YAFFS_OK;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int yaffs_TagsCompatabilityQueryNANDBlock(struct yaffs_DeviceStruct *dev,
|
||||||
|
int blockNo, yaffs_BlockState *
|
||||||
|
state,
|
||||||
|
int *sequenceNumber)
|
||||||
|
{
|
||||||
|
|
||||||
|
yaffs_Spare spare0, spare1;
|
||||||
|
static yaffs_Spare spareFF;
|
||||||
|
static int init;
|
||||||
|
yaffs_ECCResult dummy;
|
||||||
|
|
||||||
|
if (!init) {
|
||||||
|
memset(&spareFF, 0xFF, sizeof(spareFF));
|
||||||
|
init = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*sequenceNumber = 0;
|
||||||
|
|
||||||
|
yaffs_ReadChunkFromNAND(dev, blockNo * dev->nChunksPerBlock, NULL,
|
||||||
|
&spare0, &dummy, 1);
|
||||||
|
yaffs_ReadChunkFromNAND(dev, blockNo * dev->nChunksPerBlock + 1, NULL,
|
||||||
|
&spare1, &dummy, 1);
|
||||||
|
|
||||||
|
if (yaffs_CountBits(spare0.blockStatus & spare1.blockStatus) < 7)
|
||||||
|
*state = YAFFS_BLOCK_STATE_DEAD;
|
||||||
|
else if (memcmp(&spareFF, &spare0, sizeof(spareFF)) == 0)
|
||||||
|
*state = YAFFS_BLOCK_STATE_EMPTY;
|
||||||
|
else
|
||||||
|
*state = YAFFS_BLOCK_STATE_NEEDS_SCANNING;
|
||||||
|
|
||||||
|
return YAFFS_OK;
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2002-2007 Aleph One Ltd.
|
||||||
|
* for Toby Churchill Ltd and Brightstar Engineering
|
||||||
|
*
|
||||||
|
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __YAFFS_TAGSCOMPAT_H__
|
||||||
|
#define __YAFFS_TAGSCOMPAT_H__
|
||||||
|
|
||||||
|
#include "yaffs_guts.h"
|
||||||
|
int yaffs_TagsCompatabilityWriteChunkWithTagsToNAND(yaffs_Device * dev,
|
||||||
|
int chunkInNAND,
|
||||||
|
const __u8 * data,
|
||||||
|
const yaffs_ExtendedTags *
|
||||||
|
tags);
|
||||||
|
int yaffs_TagsCompatabilityReadChunkWithTagsFromNAND(yaffs_Device * dev,
|
||||||
|
int chunkInNAND,
|
||||||
|
__u8 * data,
|
||||||
|
yaffs_ExtendedTags *
|
||||||
|
tags);
|
||||||
|
int yaffs_TagsCompatabilityMarkNANDBlockBad(struct yaffs_DeviceStruct *dev,
|
||||||
|
int blockNo);
|
||||||
|
int yaffs_TagsCompatabilityQueryNANDBlock(struct yaffs_DeviceStruct *dev,
|
||||||
|
int blockNo, yaffs_BlockState *
|
||||||
|
state, int *sequenceNumber);
|
||||||
|
|
||||||
|
void yaffs_CalcTagsECC(yaffs_Tags * tags);
|
||||||
|
int yaffs_CheckECCOnTags(yaffs_Tags * tags);
|
||||||
|
int yaffs_CountBits(__u8 byte);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2002-2007 Aleph One Ltd.
|
||||||
|
* for Toby Churchill Ltd and Brightstar Engineering
|
||||||
|
*
|
||||||
|
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "yaffs_tagsvalidity.h"
|
||||||
|
|
||||||
|
void yaffs_InitialiseTags(yaffs_ExtendedTags * tags)
|
||||||
|
{
|
||||||
|
memset(tags, 0, sizeof(yaffs_ExtendedTags));
|
||||||
|
tags->validMarker0 = 0xAAAAAAAA;
|
||||||
|
tags->validMarker1 = 0x55555555;
|
||||||
|
}
|
||||||
|
|
||||||
|
int yaffs_ValidateTags(yaffs_ExtendedTags * tags)
|
||||||
|
{
|
||||||
|
return (tags->validMarker0 == 0xAAAAAAAA &&
|
||||||
|
tags->validMarker1 == 0x55555555);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2002-2007 Aleph One Ltd.
|
||||||
|
* for Toby Churchill Ltd and Brightstar Engineering
|
||||||
|
*
|
||||||
|
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef __YAFFS_TAGS_VALIDITY_H__
|
||||||
|
#define __YAFFS_TAGS_VALIDITY_H__
|
||||||
|
|
||||||
|
#include "yaffs_guts.h"
|
||||||
|
|
||||||
|
void yaffs_InitialiseTags(yaffs_ExtendedTags * tags);
|
||||||
|
int yaffs_ValidateTags(yaffs_ExtendedTags * tags);
|
||||||
|
#endif
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2002-2007 Aleph One Ltd.
|
||||||
|
* for Toby Churchill Ltd and Brightstar Engineering
|
||||||
|
*
|
||||||
|
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __YAFFSINTERFACE_H__
|
||||||
|
#define __YAFFSINTERFACE_H__
|
||||||
|
|
||||||
|
int yaffs_Initialise(unsigned nBlocks);
|
||||||
|
|
||||||
|
#endif
|
187
target/linux/generic-2.6/files-2.6.27/fs/yaffs2/yportenv.h
Normal file
187
target/linux/generic-2.6/files-2.6.27/fs/yaffs2/yportenv.h
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
/*
|
||||||
|
* YAFFS: Yet another Flash File System . A NAND-flash specific file system.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2002-2007 Aleph One Ltd.
|
||||||
|
* for Toby Churchill Ltd and Brightstar Engineering
|
||||||
|
*
|
||||||
|
* Created by Charles Manning <charles@aleph1.co.uk>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License version 2.1 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef __YPORTENV_H__
|
||||||
|
#define __YPORTENV_H__
|
||||||
|
|
||||||
|
#if defined CONFIG_YAFFS_WINCE
|
||||||
|
|
||||||
|
#include "ywinceenv.h"
|
||||||
|
|
||||||
|
#elif defined __KERNEL__
|
||||||
|
|
||||||
|
#include "moduleconfig.h"
|
||||||
|
|
||||||
|
/* Linux kernel */
|
||||||
|
#include <linux/version.h>
|
||||||
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19))
|
||||||
|
#include <linux/config.h>
|
||||||
|
#endif
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/mm.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/vmalloc.h>
|
||||||
|
|
||||||
|
#define YCHAR char
|
||||||
|
#define YUCHAR unsigned char
|
||||||
|
#define _Y(x) x
|
||||||
|
#define yaffs_strcpy(a,b) strcpy(a,b)
|
||||||
|
#define yaffs_strncpy(a,b,c) strncpy(a,b,c)
|
||||||
|
#define yaffs_strncmp(a,b,c) strncmp(a,b,c)
|
||||||
|
#define yaffs_strlen(s) strlen(s)
|
||||||
|
#define yaffs_sprintf sprintf
|
||||||
|
#define yaffs_toupper(a) toupper(a)
|
||||||
|
|
||||||
|
#define Y_INLINE inline
|
||||||
|
|
||||||
|
#define YAFFS_LOSTNFOUND_NAME "lost+found"
|
||||||
|
#define YAFFS_LOSTNFOUND_PREFIX "obj"
|
||||||
|
|
||||||
|
/* #define YPRINTF(x) printk x */
|
||||||
|
#define YMALLOC(x) kmalloc(x,GFP_KERNEL)
|
||||||
|
#define YFREE(x) kfree(x)
|
||||||
|
#define YMALLOC_ALT(x) vmalloc(x)
|
||||||
|
#define YFREE_ALT(x) vfree(x)
|
||||||
|
#define YMALLOC_DMA(x) YMALLOC(x)
|
||||||
|
|
||||||
|
// KR - added for use in scan so processes aren't blocked indefinitely.
|
||||||
|
#define YYIELD() schedule()
|
||||||
|
|
||||||
|
#define YAFFS_ROOT_MODE 0666
|
||||||
|
#define YAFFS_LOSTNFOUND_MODE 0666
|
||||||
|
|
||||||
|
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
|
||||||
|
#define Y_CURRENT_TIME CURRENT_TIME.tv_sec
|
||||||
|
#define Y_TIME_CONVERT(x) (x).tv_sec
|
||||||
|
#else
|
||||||
|
#define Y_CURRENT_TIME CURRENT_TIME
|
||||||
|
#define Y_TIME_CONVERT(x) (x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define yaffs_SumCompare(x,y) ((x) == (y))
|
||||||
|
#define yaffs_strcmp(a,b) strcmp(a,b)
|
||||||
|
|
||||||
|
#define TENDSTR "\n"
|
||||||
|
#define TSTR(x) KERN_WARNING x
|
||||||
|
#define TOUT(p) printk p
|
||||||
|
|
||||||
|
#define yaffs_trace(mask, fmt, args...) \
|
||||||
|
do { if ((mask) & (yaffs_traceMask|YAFFS_TRACE_ERROR)) \
|
||||||
|
printk(KERN_WARNING "yaffs: " fmt, ## args); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define compile_time_assertion(assertion) \
|
||||||
|
({ int x = __builtin_choose_expr(assertion, 0, (void)0); (void) x; })
|
||||||
|
|
||||||
|
#elif defined CONFIG_YAFFS_DIRECT
|
||||||
|
|
||||||
|
/* Direct interface */
|
||||||
|
#include "ydirectenv.h"
|
||||||
|
|
||||||
|
#elif defined CONFIG_YAFFS_UTIL
|
||||||
|
|
||||||
|
/* Stuff for YAFFS utilities */
|
||||||
|
|
||||||
|
#include "stdlib.h"
|
||||||
|
#include "stdio.h"
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
#include "devextras.h"
|
||||||
|
|
||||||
|
#define YMALLOC(x) malloc(x)
|
||||||
|
#define YFREE(x) free(x)
|
||||||
|
#define YMALLOC_ALT(x) malloc(x)
|
||||||
|
#define YFREE_ALT(x) free(x)
|
||||||
|
|
||||||
|
#define YCHAR char
|
||||||
|
#define YUCHAR unsigned char
|
||||||
|
#define _Y(x) x
|
||||||
|
#define yaffs_strcpy(a,b) strcpy(a,b)
|
||||||
|
#define yaffs_strncpy(a,b,c) strncpy(a,b,c)
|
||||||
|
#define yaffs_strlen(s) strlen(s)
|
||||||
|
#define yaffs_sprintf sprintf
|
||||||
|
#define yaffs_toupper(a) toupper(a)
|
||||||
|
|
||||||
|
#define Y_INLINE inline
|
||||||
|
|
||||||
|
/* #define YINFO(s) YPRINTF(( __FILE__ " %d %s\n",__LINE__,s)) */
|
||||||
|
/* #define YALERT(s) YINFO(s) */
|
||||||
|
|
||||||
|
#define TENDSTR "\n"
|
||||||
|
#define TSTR(x) x
|
||||||
|
#define TOUT(p) printf p
|
||||||
|
|
||||||
|
#define YAFFS_LOSTNFOUND_NAME "lost+found"
|
||||||
|
#define YAFFS_LOSTNFOUND_PREFIX "obj"
|
||||||
|
/* #define YPRINTF(x) printf x */
|
||||||
|
|
||||||
|
#define YAFFS_ROOT_MODE 0666
|
||||||
|
#define YAFFS_LOSTNFOUND_MODE 0666
|
||||||
|
|
||||||
|
#define yaffs_SumCompare(x,y) ((x) == (y))
|
||||||
|
#define yaffs_strcmp(a,b) strcmp(a,b)
|
||||||
|
|
||||||
|
#else
|
||||||
|
/* Should have specified a configuration type */
|
||||||
|
#error Unknown configuration
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* see yaffs_fs.c */
|
||||||
|
extern unsigned int yaffs_traceMask;
|
||||||
|
extern unsigned int yaffs_wr_attempts;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tracing flags.
|
||||||
|
* The flags masked in YAFFS_TRACE_ALWAYS are always traced.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define YAFFS_TRACE_OS 0x00000002
|
||||||
|
#define YAFFS_TRACE_ALLOCATE 0x00000004
|
||||||
|
#define YAFFS_TRACE_SCAN 0x00000008
|
||||||
|
#define YAFFS_TRACE_BAD_BLOCKS 0x00000010
|
||||||
|
#define YAFFS_TRACE_ERASE 0x00000020
|
||||||
|
#define YAFFS_TRACE_GC 0x00000040
|
||||||
|
#define YAFFS_TRACE_WRITE 0x00000080
|
||||||
|
#define YAFFS_TRACE_TRACING 0x00000100
|
||||||
|
#define YAFFS_TRACE_DELETION 0x00000200
|
||||||
|
#define YAFFS_TRACE_BUFFERS 0x00000400
|
||||||
|
#define YAFFS_TRACE_NANDACCESS 0x00000800
|
||||||
|
#define YAFFS_TRACE_GC_DETAIL 0x00001000
|
||||||
|
#define YAFFS_TRACE_SCAN_DEBUG 0x00002000
|
||||||
|
#define YAFFS_TRACE_MTD 0x00004000
|
||||||
|
#define YAFFS_TRACE_CHECKPOINT 0x00008000
|
||||||
|
|
||||||
|
#define YAFFS_TRACE_VERIFY 0x00010000
|
||||||
|
#define YAFFS_TRACE_VERIFY_NAND 0x00020000
|
||||||
|
#define YAFFS_TRACE_VERIFY_FULL 0x00040000
|
||||||
|
#define YAFFS_TRACE_VERIFY_ALL 0x000F0000
|
||||||
|
|
||||||
|
|
||||||
|
#define YAFFS_TRACE_ERROR 0x40000000
|
||||||
|
#define YAFFS_TRACE_BUG 0x80000000
|
||||||
|
#define YAFFS_TRACE_ALWAYS 0xF0000000
|
||||||
|
|
||||||
|
|
||||||
|
#define T(mask,p) do{ if((mask) & (yaffs_traceMask | YAFFS_TRACE_ALWAYS)) TOUT(p);} while(0)
|
||||||
|
|
||||||
|
#ifndef CONFIG_YAFFS_WINCE
|
||||||
|
#define YBUG() T(YAFFS_TRACE_BUG,(TSTR("==>> yaffs bug: " __FILE__ " %d" TENDSTR),__LINE__))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Definitions for the GPIO buttons interface driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2007,2008 Gabor Juhos <juhosg at openwrt.org>
|
||||||
|
*
|
||||||
|
* This file was based on: /include/linux/gpio_keys.h
|
||||||
|
* The original gpio_keys.h seems not to have a license.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _GPIO_BUTTONS_H_
|
||||||
|
#define _GPIO_BUTTONS_H_
|
||||||
|
|
||||||
|
struct gpio_button {
|
||||||
|
int gpio; /* GPIO line number */
|
||||||
|
int active_low;
|
||||||
|
char *desc; /* button description */
|
||||||
|
int type; /* input event type (EV_KEY, EV_SW) */
|
||||||
|
int code; /* input event code (KEY_*, SW_*) */
|
||||||
|
int count;
|
||||||
|
int threshold; /* count threshold */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct gpio_buttons_platform_data {
|
||||||
|
struct gpio_button *buttons;
|
||||||
|
int nbuttons; /* number of buttons */
|
||||||
|
int poll_interval; /* polling interval */
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _GPIO_BUTTONS_H_ */
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
#ifndef _GPIODEV_H__
|
||||||
|
#define _GPIODEV_H__
|
||||||
|
|
||||||
|
#define IOC_GPIODEV_MAGIC 'B'
|
||||||
|
#define GPIO_GET _IO(IOC_GPIODEV_MAGIC, 10)
|
||||||
|
#define GPIO_SET _IO(IOC_GPIODEV_MAGIC, 11)
|
||||||
|
#define GPIO_CLEAR _IO(IOC_GPIODEV_MAGIC, 12)
|
||||||
|
#define GPIO_DIR_IN _IO(IOC_GPIODEV_MAGIC, 13)
|
||||||
|
#define GPIO_DIR_OUT _IO(IOC_GPIODEV_MAGIC, 14)
|
||||||
|
|
||||||
|
#endif
|
4170
target/linux/generic-2.6/patches-2.6.27/001-squashfs.patch
Normal file
4170
target/linux/generic-2.6/patches-2.6.27/001-squashfs.patch
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,780 @@
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/include/linux/LzmaDecode.h
|
||||||
|
@@ -0,0 +1,100 @@
|
||||||
|
+/*
|
||||||
|
+ LzmaDecode.h
|
||||||
|
+ LZMA Decoder interface
|
||||||
|
+
|
||||||
|
+ LZMA SDK 4.05 Copyright (c) 1999-2004 Igor Pavlov (2004-08-25)
|
||||||
|
+ http://www.7-zip.org/
|
||||||
|
+
|
||||||
|
+ LZMA SDK is licensed under two licenses:
|
||||||
|
+ 1) GNU Lesser General Public License (GNU LGPL)
|
||||||
|
+ 2) Common Public License (CPL)
|
||||||
|
+ It means that you can select one of these two licenses and
|
||||||
|
+ follow rules of that license.
|
||||||
|
+
|
||||||
|
+ SPECIAL EXCEPTION:
|
||||||
|
+ Igor Pavlov, as the author of this code, expressly permits you to
|
||||||
|
+ statically or dynamically link your code (or bind by name) to the
|
||||||
|
+ interfaces of this file without subjecting your linked code to the
|
||||||
|
+ terms of the CPL or GNU LGPL. Any modifications or additions
|
||||||
|
+ to this file, however, are subject to the LGPL or CPL terms.
|
||||||
|
+*/
|
||||||
|
+
|
||||||
|
+#ifndef __LZMADECODE_H
|
||||||
|
+#define __LZMADECODE_H
|
||||||
|
+
|
||||||
|
+/* #define _LZMA_IN_CB */
|
||||||
|
+/* Use callback for input data */
|
||||||
|
+
|
||||||
|
+/* #define _LZMA_OUT_READ */
|
||||||
|
+/* Use read function for output data */
|
||||||
|
+
|
||||||
|
+/* #define _LZMA_PROB32 */
|
||||||
|
+/* It can increase speed on some 32-bit CPUs,
|
||||||
|
+ but memory usage will be doubled in that case */
|
||||||
|
+
|
||||||
|
+/* #define _LZMA_LOC_OPT */
|
||||||
|
+/* Enable local speed optimizations inside code */
|
||||||
|
+
|
||||||
|
+#ifndef UInt32
|
||||||
|
+#ifdef _LZMA_UINT32_IS_ULONG
|
||||||
|
+#define UInt32 unsigned long
|
||||||
|
+#else
|
||||||
|
+#define UInt32 unsigned int
|
||||||
|
+#endif
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+#ifdef _LZMA_PROB32
|
||||||
|
+#define CProb UInt32
|
||||||
|
+#else
|
||||||
|
+#define CProb unsigned short
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+#define LZMA_RESULT_OK 0
|
||||||
|
+#define LZMA_RESULT_DATA_ERROR 1
|
||||||
|
+#define LZMA_RESULT_NOT_ENOUGH_MEM 2
|
||||||
|
+
|
||||||
|
+#ifdef _LZMA_IN_CB
|
||||||
|
+typedef struct _ILzmaInCallback
|
||||||
|
+{
|
||||||
|
+ int (*Read)(void *object, unsigned char **buffer, UInt32 *bufferSize);
|
||||||
|
+} ILzmaInCallback;
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+#define LZMA_BASE_SIZE 1846
|
||||||
|
+#define LZMA_LIT_SIZE 768
|
||||||
|
+
|
||||||
|
+/*
|
||||||
|
+bufferSize = (LZMA_BASE_SIZE + (LZMA_LIT_SIZE << (lc + lp)))* sizeof(CProb)
|
||||||
|
+bufferSize += 100 in case of _LZMA_OUT_READ
|
||||||
|
+by default CProb is unsigned short,
|
||||||
|
+but if specify _LZMA_PROB_32, CProb will be UInt32(unsigned int)
|
||||||
|
+*/
|
||||||
|
+
|
||||||
|
+#ifdef _LZMA_OUT_READ
|
||||||
|
+int LzmaDecoderInit(
|
||||||
|
+ unsigned char *buffer, UInt32 bufferSize,
|
||||||
|
+ int lc, int lp, int pb,
|
||||||
|
+ unsigned char *dictionary, UInt32 dictionarySize,
|
||||||
|
+ #ifdef _LZMA_IN_CB
|
||||||
|
+ ILzmaInCallback *inCallback
|
||||||
|
+ #else
|
||||||
|
+ unsigned char *inStream, UInt32 inSize
|
||||||
|
+ #endif
|
||||||
|
+);
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+int LzmaDecode(
|
||||||
|
+ unsigned char *buffer,
|
||||||
|
+ #ifndef _LZMA_OUT_READ
|
||||||
|
+ UInt32 bufferSize,
|
||||||
|
+ int lc, int lp, int pb,
|
||||||
|
+ #ifdef _LZMA_IN_CB
|
||||||
|
+ ILzmaInCallback *inCallback,
|
||||||
|
+ #else
|
||||||
|
+ unsigned char *inStream, UInt32 inSize,
|
||||||
|
+ #endif
|
||||||
|
+ #endif
|
||||||
|
+ unsigned char *outStream, UInt32 outSize,
|
||||||
|
+ UInt32 *outSizeProcessed);
|
||||||
|
+
|
||||||
|
+#endif
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/lib/LzmaDecode.c
|
||||||
|
@@ -0,0 +1,663 @@
|
||||||
|
+/*
|
||||||
|
+ LzmaDecode.c
|
||||||
|
+ LZMA Decoder
|
||||||
|
+
|
||||||
|
+ LZMA SDK 4.05 Copyright (c) 1999-2004 Igor Pavlov (2004-08-25)
|
||||||
|
+ http://www.7-zip.org/
|
||||||
|
+
|
||||||
|
+ LZMA SDK is licensed under two licenses:
|
||||||
|
+ 1) GNU Lesser General Public License (GNU LGPL)
|
||||||
|
+ 2) Common Public License (CPL)
|
||||||
|
+ It means that you can select one of these two licenses and
|
||||||
|
+ follow rules of that license.
|
||||||
|
+
|
||||||
|
+ SPECIAL EXCEPTION:
|
||||||
|
+ Igor Pavlov, as the author of this code, expressly permits you to
|
||||||
|
+ statically or dynamically link your code (or bind by name) to the
|
||||||
|
+ interfaces of this file without subjecting your linked code to the
|
||||||
|
+ terms of the CPL or GNU LGPL. Any modifications or additions
|
||||||
|
+ to this file, however, are subject to the LGPL or CPL terms.
|
||||||
|
+*/
|
||||||
|
+
|
||||||
|
+#include <linux/LzmaDecode.h>
|
||||||
|
+
|
||||||
|
+#ifndef Byte
|
||||||
|
+#define Byte unsigned char
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+#define kNumTopBits 24
|
||||||
|
+#define kTopValue ((UInt32)1 << kNumTopBits)
|
||||||
|
+
|
||||||
|
+#define kNumBitModelTotalBits 11
|
||||||
|
+#define kBitModelTotal (1 << kNumBitModelTotalBits)
|
||||||
|
+#define kNumMoveBits 5
|
||||||
|
+
|
||||||
|
+typedef struct _CRangeDecoder
|
||||||
|
+{
|
||||||
|
+ Byte *Buffer;
|
||||||
|
+ Byte *BufferLim;
|
||||||
|
+ UInt32 Range;
|
||||||
|
+ UInt32 Code;
|
||||||
|
+ #ifdef _LZMA_IN_CB
|
||||||
|
+ ILzmaInCallback *InCallback;
|
||||||
|
+ int Result;
|
||||||
|
+ #endif
|
||||||
|
+ int ExtraBytes;
|
||||||
|
+} CRangeDecoder;
|
||||||
|
+
|
||||||
|
+Byte RangeDecoderReadByte(CRangeDecoder *rd)
|
||||||
|
+{
|
||||||
|
+ if (rd->Buffer == rd->BufferLim)
|
||||||
|
+ {
|
||||||
|
+ #ifdef _LZMA_IN_CB
|
||||||
|
+ UInt32 size;
|
||||||
|
+ rd->Result = rd->InCallback->Read(rd->InCallback, &rd->Buffer, &size);
|
||||||
|
+ rd->BufferLim = rd->Buffer + size;
|
||||||
|
+ if (size == 0)
|
||||||
|
+ #endif
|
||||||
|
+ {
|
||||||
|
+ rd->ExtraBytes = 1;
|
||||||
|
+ return 0xFF;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ return (*rd->Buffer++);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/* #define ReadByte (*rd->Buffer++) */
|
||||||
|
+#define ReadByte (RangeDecoderReadByte(rd))
|
||||||
|
+
|
||||||
|
+void RangeDecoderInit(CRangeDecoder *rd,
|
||||||
|
+ #ifdef _LZMA_IN_CB
|
||||||
|
+ ILzmaInCallback *inCallback
|
||||||
|
+ #else
|
||||||
|
+ Byte *stream, UInt32 bufferSize
|
||||||
|
+ #endif
|
||||||
|
+ )
|
||||||
|
+{
|
||||||
|
+ int i;
|
||||||
|
+ #ifdef _LZMA_IN_CB
|
||||||
|
+ rd->InCallback = inCallback;
|
||||||
|
+ rd->Buffer = rd->BufferLim = 0;
|
||||||
|
+ #else
|
||||||
|
+ rd->Buffer = stream;
|
||||||
|
+ rd->BufferLim = stream + bufferSize;
|
||||||
|
+ #endif
|
||||||
|
+ rd->ExtraBytes = 0;
|
||||||
|
+ rd->Code = 0;
|
||||||
|
+ rd->Range = (0xFFFFFFFF);
|
||||||
|
+ for(i = 0; i < 5; i++)
|
||||||
|
+ rd->Code = (rd->Code << 8) | ReadByte;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+#define RC_INIT_VAR UInt32 range = rd->Range; UInt32 code = rd->Code;
|
||||||
|
+#define RC_FLUSH_VAR rd->Range = range; rd->Code = code;
|
||||||
|
+#define RC_NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | ReadByte; }
|
||||||
|
+
|
||||||
|
+UInt32 RangeDecoderDecodeDirectBits(CRangeDecoder *rd, int numTotalBits)
|
||||||
|
+{
|
||||||
|
+ RC_INIT_VAR
|
||||||
|
+ UInt32 result = 0;
|
||||||
|
+ int i;
|
||||||
|
+ for (i = numTotalBits; i > 0; i--)
|
||||||
|
+ {
|
||||||
|
+ /* UInt32 t; */
|
||||||
|
+ range >>= 1;
|
||||||
|
+
|
||||||
|
+ result <<= 1;
|
||||||
|
+ if (code >= range)
|
||||||
|
+ {
|
||||||
|
+ code -= range;
|
||||||
|
+ result |= 1;
|
||||||
|
+ }
|
||||||
|
+ /*
|
||||||
|
+ t = (code - range) >> 31;
|
||||||
|
+ t &= 1;
|
||||||
|
+ code -= range & (t - 1);
|
||||||
|
+ result = (result + result) | (1 - t);
|
||||||
|
+ */
|
||||||
|
+ RC_NORMALIZE
|
||||||
|
+ }
|
||||||
|
+ RC_FLUSH_VAR
|
||||||
|
+ return result;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+int RangeDecoderBitDecode(CProb *prob, CRangeDecoder *rd)
|
||||||
|
+{
|
||||||
|
+ UInt32 bound = (rd->Range >> kNumBitModelTotalBits) * *prob;
|
||||||
|
+ if (rd->Code < bound)
|
||||||
|
+ {
|
||||||
|
+ rd->Range = bound;
|
||||||
|
+ *prob += (kBitModelTotal - *prob) >> kNumMoveBits;
|
||||||
|
+ if (rd->Range < kTopValue)
|
||||||
|
+ {
|
||||||
|
+ rd->Code = (rd->Code << 8) | ReadByte;
|
||||||
|
+ rd->Range <<= 8;
|
||||||
|
+ }
|
||||||
|
+ return 0;
|
||||||
|
+ }
|
||||||
|
+ else
|
||||||
|
+ {
|
||||||
|
+ rd->Range -= bound;
|
||||||
|
+ rd->Code -= bound;
|
||||||
|
+ *prob -= (*prob) >> kNumMoveBits;
|
||||||
|
+ if (rd->Range < kTopValue)
|
||||||
|
+ {
|
||||||
|
+ rd->Code = (rd->Code << 8) | ReadByte;
|
||||||
|
+ rd->Range <<= 8;
|
||||||
|
+ }
|
||||||
|
+ return 1;
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+#define RC_GET_BIT2(prob, mi, A0, A1) \
|
||||||
|
+ UInt32 bound = (range >> kNumBitModelTotalBits) * *prob; \
|
||||||
|
+ if (code < bound) \
|
||||||
|
+ { A0; range = bound; *prob += (kBitModelTotal - *prob) >> kNumMoveBits; mi <<= 1; } \
|
||||||
|
+ else \
|
||||||
|
+ { A1; range -= bound; code -= bound; *prob -= (*prob) >> kNumMoveBits; mi = (mi + mi) + 1; } \
|
||||||
|
+ RC_NORMALIZE
|
||||||
|
+
|
||||||
|
+#define RC_GET_BIT(prob, mi) RC_GET_BIT2(prob, mi, ; , ;)
|
||||||
|
+
|
||||||
|
+int RangeDecoderBitTreeDecode(CProb *probs, int numLevels, CRangeDecoder *rd)
|
||||||
|
+{
|
||||||
|
+ int mi = 1;
|
||||||
|
+ int i;
|
||||||
|
+ #ifdef _LZMA_LOC_OPT
|
||||||
|
+ RC_INIT_VAR
|
||||||
|
+ #endif
|
||||||
|
+ for(i = numLevels; i > 0; i--)
|
||||||
|
+ {
|
||||||
|
+ #ifdef _LZMA_LOC_OPT
|
||||||
|
+ CProb *prob = probs + mi;
|
||||||
|
+ RC_GET_BIT(prob, mi)
|
||||||
|
+ #else
|
||||||
|
+ mi = (mi + mi) + RangeDecoderBitDecode(probs + mi, rd);
|
||||||
|
+ #endif
|
||||||
|
+ }
|
||||||
|
+ #ifdef _LZMA_LOC_OPT
|
||||||
|
+ RC_FLUSH_VAR
|
||||||
|
+ #endif
|
||||||
|
+ return mi - (1 << numLevels);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+int RangeDecoderReverseBitTreeDecode(CProb *probs, int numLevels, CRangeDecoder *rd)
|
||||||
|
+{
|
||||||
|
+ int mi = 1;
|
||||||
|
+ int i;
|
||||||
|
+ int symbol = 0;
|
||||||
|
+ #ifdef _LZMA_LOC_OPT
|
||||||
|
+ RC_INIT_VAR
|
||||||
|
+ #endif
|
||||||
|
+ for(i = 0; i < numLevels; i++)
|
||||||
|
+ {
|
||||||
|
+ #ifdef _LZMA_LOC_OPT
|
||||||
|
+ CProb *prob = probs + mi;
|
||||||
|
+ RC_GET_BIT2(prob, mi, ; , symbol |= (1 << i))
|
||||||
|
+ #else
|
||||||
|
+ int bit = RangeDecoderBitDecode(probs + mi, rd);
|
||||||
|
+ mi = mi + mi + bit;
|
||||||
|
+ symbol |= (bit << i);
|
||||||
|
+ #endif
|
||||||
|
+ }
|
||||||
|
+ #ifdef _LZMA_LOC_OPT
|
||||||
|
+ RC_FLUSH_VAR
|
||||||
|
+ #endif
|
||||||
|
+ return symbol;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+Byte LzmaLiteralDecode(CProb *probs, CRangeDecoder *rd)
|
||||||
|
+{
|
||||||
|
+ int symbol = 1;
|
||||||
|
+ #ifdef _LZMA_LOC_OPT
|
||||||
|
+ RC_INIT_VAR
|
||||||
|
+ #endif
|
||||||
|
+ do
|
||||||
|
+ {
|
||||||
|
+ #ifdef _LZMA_LOC_OPT
|
||||||
|
+ CProb *prob = probs + symbol;
|
||||||
|
+ RC_GET_BIT(prob, symbol)
|
||||||
|
+ #else
|
||||||
|
+ symbol = (symbol + symbol) | RangeDecoderBitDecode(probs + symbol, rd);
|
||||||
|
+ #endif
|
||||||
|
+ }
|
||||||
|
+ while (symbol < 0x100);
|
||||||
|
+ #ifdef _LZMA_LOC_OPT
|
||||||
|
+ RC_FLUSH_VAR
|
||||||
|
+ #endif
|
||||||
|
+ return symbol;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+Byte LzmaLiteralDecodeMatch(CProb *probs, CRangeDecoder *rd, Byte matchByte)
|
||||||
|
+{
|
||||||
|
+ int symbol = 1;
|
||||||
|
+ #ifdef _LZMA_LOC_OPT
|
||||||
|
+ RC_INIT_VAR
|
||||||
|
+ #endif
|
||||||
|
+ do
|
||||||
|
+ {
|
||||||
|
+ int bit;
|
||||||
|
+ int matchBit = (matchByte >> 7) & 1;
|
||||||
|
+ matchByte <<= 1;
|
||||||
|
+ #ifdef _LZMA_LOC_OPT
|
||||||
|
+ {
|
||||||
|
+ CProb *prob = probs + ((1 + matchBit) << 8) + symbol;
|
||||||
|
+ RC_GET_BIT2(prob, symbol, bit = 0, bit = 1)
|
||||||
|
+ }
|
||||||
|
+ #else
|
||||||
|
+ bit = RangeDecoderBitDecode(probs + ((1 + matchBit) << 8) + symbol, rd);
|
||||||
|
+ symbol = (symbol << 1) | bit;
|
||||||
|
+ #endif
|
||||||
|
+ if (matchBit != bit)
|
||||||
|
+ {
|
||||||
|
+ while (symbol < 0x100)
|
||||||
|
+ {
|
||||||
|
+ #ifdef _LZMA_LOC_OPT
|
||||||
|
+ CProb *prob = probs + symbol;
|
||||||
|
+ RC_GET_BIT(prob, symbol)
|
||||||
|
+ #else
|
||||||
|
+ symbol = (symbol + symbol) | RangeDecoderBitDecode(probs + symbol, rd);
|
||||||
|
+ #endif
|
||||||
|
+ }
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ while (symbol < 0x100);
|
||||||
|
+ #ifdef _LZMA_LOC_OPT
|
||||||
|
+ RC_FLUSH_VAR
|
||||||
|
+ #endif
|
||||||
|
+ return symbol;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+#define kNumPosBitsMax 4
|
||||||
|
+#define kNumPosStatesMax (1 << kNumPosBitsMax)
|
||||||
|
+
|
||||||
|
+#define kLenNumLowBits 3
|
||||||
|
+#define kLenNumLowSymbols (1 << kLenNumLowBits)
|
||||||
|
+#define kLenNumMidBits 3
|
||||||
|
+#define kLenNumMidSymbols (1 << kLenNumMidBits)
|
||||||
|
+#define kLenNumHighBits 8
|
||||||
|
+#define kLenNumHighSymbols (1 << kLenNumHighBits)
|
||||||
|
+
|
||||||
|
+#define LenChoice 0
|
||||||
|
+#define LenChoice2 (LenChoice + 1)
|
||||||
|
+#define LenLow (LenChoice2 + 1)
|
||||||
|
+#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits))
|
||||||
|
+#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits))
|
||||||
|
+#define kNumLenProbs (LenHigh + kLenNumHighSymbols)
|
||||||
|
+
|
||||||
|
+int LzmaLenDecode(CProb *p, CRangeDecoder *rd, int posState)
|
||||||
|
+{
|
||||||
|
+ if(RangeDecoderBitDecode(p + LenChoice, rd) == 0)
|
||||||
|
+ return RangeDecoderBitTreeDecode(p + LenLow +
|
||||||
|
+ (posState << kLenNumLowBits), kLenNumLowBits, rd);
|
||||||
|
+ if(RangeDecoderBitDecode(p + LenChoice2, rd) == 0)
|
||||||
|
+ return kLenNumLowSymbols + RangeDecoderBitTreeDecode(p + LenMid +
|
||||||
|
+ (posState << kLenNumMidBits), kLenNumMidBits, rd);
|
||||||
|
+ return kLenNumLowSymbols + kLenNumMidSymbols +
|
||||||
|
+ RangeDecoderBitTreeDecode(p + LenHigh, kLenNumHighBits, rd);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+#define kNumStates 12
|
||||||
|
+
|
||||||
|
+#define kStartPosModelIndex 4
|
||||||
|
+#define kEndPosModelIndex 14
|
||||||
|
+#define kNumFullDistances (1 << (kEndPosModelIndex >> 1))
|
||||||
|
+
|
||||||
|
+#define kNumPosSlotBits 6
|
||||||
|
+#define kNumLenToPosStates 4
|
||||||
|
+
|
||||||
|
+#define kNumAlignBits 4
|
||||||
|
+#define kAlignTableSize (1 << kNumAlignBits)
|
||||||
|
+
|
||||||
|
+#define kMatchMinLen 2
|
||||||
|
+
|
||||||
|
+#define IsMatch 0
|
||||||
|
+#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax))
|
||||||
|
+#define IsRepG0 (IsRep + kNumStates)
|
||||||
|
+#define IsRepG1 (IsRepG0 + kNumStates)
|
||||||
|
+#define IsRepG2 (IsRepG1 + kNumStates)
|
||||||
|
+#define IsRep0Long (IsRepG2 + kNumStates)
|
||||||
|
+#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax))
|
||||||
|
+#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits))
|
||||||
|
+#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex)
|
||||||
|
+#define LenCoder (Align + kAlignTableSize)
|
||||||
|
+#define RepLenCoder (LenCoder + kNumLenProbs)
|
||||||
|
+#define Literal (RepLenCoder + kNumLenProbs)
|
||||||
|
+
|
||||||
|
+#if Literal != LZMA_BASE_SIZE
|
||||||
|
+StopCompilingDueBUG
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+#ifdef _LZMA_OUT_READ
|
||||||
|
+
|
||||||
|
+typedef struct _LzmaVarState
|
||||||
|
+{
|
||||||
|
+ CRangeDecoder RangeDecoder;
|
||||||
|
+ Byte *Dictionary;
|
||||||
|
+ UInt32 DictionarySize;
|
||||||
|
+ UInt32 DictionaryPos;
|
||||||
|
+ UInt32 GlobalPos;
|
||||||
|
+ UInt32 Reps[4];
|
||||||
|
+ int lc;
|
||||||
|
+ int lp;
|
||||||
|
+ int pb;
|
||||||
|
+ int State;
|
||||||
|
+ int PreviousIsMatch;
|
||||||
|
+ int RemainLen;
|
||||||
|
+} LzmaVarState;
|
||||||
|
+
|
||||||
|
+int LzmaDecoderInit(
|
||||||
|
+ unsigned char *buffer, UInt32 bufferSize,
|
||||||
|
+ int lc, int lp, int pb,
|
||||||
|
+ unsigned char *dictionary, UInt32 dictionarySize,
|
||||||
|
+ #ifdef _LZMA_IN_CB
|
||||||
|
+ ILzmaInCallback *inCallback
|
||||||
|
+ #else
|
||||||
|
+ unsigned char *inStream, UInt32 inSize
|
||||||
|
+ #endif
|
||||||
|
+ )
|
||||||
|
+{
|
||||||
|
+ LzmaVarState *vs = (LzmaVarState *)buffer;
|
||||||
|
+ CProb *p = (CProb *)(buffer + sizeof(LzmaVarState));
|
||||||
|
+ UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + lp));
|
||||||
|
+ UInt32 i;
|
||||||
|
+ if (bufferSize < numProbs * sizeof(CProb) + sizeof(LzmaVarState))
|
||||||
|
+ return LZMA_RESULT_NOT_ENOUGH_MEM;
|
||||||
|
+ vs->Dictionary = dictionary;
|
||||||
|
+ vs->DictionarySize = dictionarySize;
|
||||||
|
+ vs->DictionaryPos = 0;
|
||||||
|
+ vs->GlobalPos = 0;
|
||||||
|
+ vs->Reps[0] = vs->Reps[1] = vs->Reps[2] = vs->Reps[3] = 1;
|
||||||
|
+ vs->lc = lc;
|
||||||
|
+ vs->lp = lp;
|
||||||
|
+ vs->pb = pb;
|
||||||
|
+ vs->State = 0;
|
||||||
|
+ vs->PreviousIsMatch = 0;
|
||||||
|
+ vs->RemainLen = 0;
|
||||||
|
+ dictionary[dictionarySize - 1] = 0;
|
||||||
|
+ for (i = 0; i < numProbs; i++)
|
||||||
|
+ p[i] = kBitModelTotal >> 1;
|
||||||
|
+ RangeDecoderInit(&vs->RangeDecoder,
|
||||||
|
+ #ifdef _LZMA_IN_CB
|
||||||
|
+ inCallback
|
||||||
|
+ #else
|
||||||
|
+ inStream, inSize
|
||||||
|
+ #endif
|
||||||
|
+ );
|
||||||
|
+ return LZMA_RESULT_OK;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+int LzmaDecode(unsigned char *buffer,
|
||||||
|
+ unsigned char *outStream, UInt32 outSize,
|
||||||
|
+ UInt32 *outSizeProcessed)
|
||||||
|
+{
|
||||||
|
+ LzmaVarState *vs = (LzmaVarState *)buffer;
|
||||||
|
+ CProb *p = (CProb *)(buffer + sizeof(LzmaVarState));
|
||||||
|
+ CRangeDecoder rd = vs->RangeDecoder;
|
||||||
|
+ int state = vs->State;
|
||||||
|
+ int previousIsMatch = vs->PreviousIsMatch;
|
||||||
|
+ Byte previousByte;
|
||||||
|
+ UInt32 rep0 = vs->Reps[0], rep1 = vs->Reps[1], rep2 = vs->Reps[2], rep3 = vs->Reps[3];
|
||||||
|
+ UInt32 nowPos = 0;
|
||||||
|
+ UInt32 posStateMask = (1 << (vs->pb)) - 1;
|
||||||
|
+ UInt32 literalPosMask = (1 << (vs->lp)) - 1;
|
||||||
|
+ int lc = vs->lc;
|
||||||
|
+ int len = vs->RemainLen;
|
||||||
|
+ UInt32 globalPos = vs->GlobalPos;
|
||||||
|
+
|
||||||
|
+ Byte *dictionary = vs->Dictionary;
|
||||||
|
+ UInt32 dictionarySize = vs->DictionarySize;
|
||||||
|
+ UInt32 dictionaryPos = vs->DictionaryPos;
|
||||||
|
+
|
||||||
|
+ if (len == -1)
|
||||||
|
+ {
|
||||||
|
+ *outSizeProcessed = 0;
|
||||||
|
+ return LZMA_RESULT_OK;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ while(len > 0 && nowPos < outSize)
|
||||||
|
+ {
|
||||||
|
+ UInt32 pos = dictionaryPos - rep0;
|
||||||
|
+ if (pos >= dictionarySize)
|
||||||
|
+ pos += dictionarySize;
|
||||||
|
+ outStream[nowPos++] = dictionary[dictionaryPos] = dictionary[pos];
|
||||||
|
+ if (++dictionaryPos == dictionarySize)
|
||||||
|
+ dictionaryPos = 0;
|
||||||
|
+ len--;
|
||||||
|
+ }
|
||||||
|
+ if (dictionaryPos == 0)
|
||||||
|
+ previousByte = dictionary[dictionarySize - 1];
|
||||||
|
+ else
|
||||||
|
+ previousByte = dictionary[dictionaryPos - 1];
|
||||||
|
+#else
|
||||||
|
+
|
||||||
|
+int LzmaDecode(
|
||||||
|
+ Byte *buffer, UInt32 bufferSize,
|
||||||
|
+ int lc, int lp, int pb,
|
||||||
|
+ #ifdef _LZMA_IN_CB
|
||||||
|
+ ILzmaInCallback *inCallback,
|
||||||
|
+ #else
|
||||||
|
+ unsigned char *inStream, UInt32 inSize,
|
||||||
|
+ #endif
|
||||||
|
+ unsigned char *outStream, UInt32 outSize,
|
||||||
|
+ UInt32 *outSizeProcessed)
|
||||||
|
+{
|
||||||
|
+ UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + lp));
|
||||||
|
+ CProb *p = (CProb *)buffer;
|
||||||
|
+ CRangeDecoder rd;
|
||||||
|
+ UInt32 i;
|
||||||
|
+ int state = 0;
|
||||||
|
+ int previousIsMatch = 0;
|
||||||
|
+ Byte previousByte = 0;
|
||||||
|
+ UInt32 rep0 = 1, rep1 = 1, rep2 = 1, rep3 = 1;
|
||||||
|
+ UInt32 nowPos = 0;
|
||||||
|
+ UInt32 posStateMask = (1 << pb) - 1;
|
||||||
|
+ UInt32 literalPosMask = (1 << lp) - 1;
|
||||||
|
+ int len = 0;
|
||||||
|
+ if (bufferSize < numProbs * sizeof(CProb))
|
||||||
|
+ return LZMA_RESULT_NOT_ENOUGH_MEM;
|
||||||
|
+ for (i = 0; i < numProbs; i++)
|
||||||
|
+ p[i] = kBitModelTotal >> 1;
|
||||||
|
+ RangeDecoderInit(&rd,
|
||||||
|
+ #ifdef _LZMA_IN_CB
|
||||||
|
+ inCallback
|
||||||
|
+ #else
|
||||||
|
+ inStream, inSize
|
||||||
|
+ #endif
|
||||||
|
+ );
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+ *outSizeProcessed = 0;
|
||||||
|
+ while(nowPos < outSize)
|
||||||
|
+ {
|
||||||
|
+ int posState = (int)(
|
||||||
|
+ (nowPos
|
||||||
|
+ #ifdef _LZMA_OUT_READ
|
||||||
|
+ + globalPos
|
||||||
|
+ #endif
|
||||||
|
+ )
|
||||||
|
+ & posStateMask);
|
||||||
|
+ #ifdef _LZMA_IN_CB
|
||||||
|
+ if (rd.Result != LZMA_RESULT_OK)
|
||||||
|
+ return rd.Result;
|
||||||
|
+ #endif
|
||||||
|
+ if (rd.ExtraBytes != 0)
|
||||||
|
+ return LZMA_RESULT_DATA_ERROR;
|
||||||
|
+ if (RangeDecoderBitDecode(p + IsMatch + (state << kNumPosBitsMax) + posState, &rd) == 0)
|
||||||
|
+ {
|
||||||
|
+ CProb *probs = p + Literal + (LZMA_LIT_SIZE *
|
||||||
|
+ (((
|
||||||
|
+ (nowPos
|
||||||
|
+ #ifdef _LZMA_OUT_READ
|
||||||
|
+ + globalPos
|
||||||
|
+ #endif
|
||||||
|
+ )
|
||||||
|
+ & literalPosMask) << lc) + (previousByte >> (8 - lc))));
|
||||||
|
+
|
||||||
|
+ if (state < 4) state = 0;
|
||||||
|
+ else if (state < 10) state -= 3;
|
||||||
|
+ else state -= 6;
|
||||||
|
+ if (previousIsMatch)
|
||||||
|
+ {
|
||||||
|
+ Byte matchByte;
|
||||||
|
+ #ifdef _LZMA_OUT_READ
|
||||||
|
+ UInt32 pos = dictionaryPos - rep0;
|
||||||
|
+ if (pos >= dictionarySize)
|
||||||
|
+ pos += dictionarySize;
|
||||||
|
+ matchByte = dictionary[pos];
|
||||||
|
+ #else
|
||||||
|
+ matchByte = outStream[nowPos - rep0];
|
||||||
|
+ #endif
|
||||||
|
+ previousByte = LzmaLiteralDecodeMatch(probs, &rd, matchByte);
|
||||||
|
+ previousIsMatch = 0;
|
||||||
|
+ }
|
||||||
|
+ else
|
||||||
|
+ previousByte = LzmaLiteralDecode(probs, &rd);
|
||||||
|
+ outStream[nowPos++] = previousByte;
|
||||||
|
+ #ifdef _LZMA_OUT_READ
|
||||||
|
+ dictionary[dictionaryPos] = previousByte;
|
||||||
|
+ if (++dictionaryPos == dictionarySize)
|
||||||
|
+ dictionaryPos = 0;
|
||||||
|
+ #endif
|
||||||
|
+ }
|
||||||
|
+ else
|
||||||
|
+ {
|
||||||
|
+ previousIsMatch = 1;
|
||||||
|
+ if (RangeDecoderBitDecode(p + IsRep + state, &rd) == 1)
|
||||||
|
+ {
|
||||||
|
+ if (RangeDecoderBitDecode(p + IsRepG0 + state, &rd) == 0)
|
||||||
|
+ {
|
||||||
|
+ if (RangeDecoderBitDecode(p + IsRep0Long + (state << kNumPosBitsMax) + posState, &rd) == 0)
|
||||||
|
+ {
|
||||||
|
+ #ifdef _LZMA_OUT_READ
|
||||||
|
+ UInt32 pos;
|
||||||
|
+ #endif
|
||||||
|
+ if (
|
||||||
|
+ (nowPos
|
||||||
|
+ #ifdef _LZMA_OUT_READ
|
||||||
|
+ + globalPos
|
||||||
|
+ #endif
|
||||||
|
+ )
|
||||||
|
+ == 0)
|
||||||
|
+ return LZMA_RESULT_DATA_ERROR;
|
||||||
|
+ state = state < 7 ? 9 : 11;
|
||||||
|
+ #ifdef _LZMA_OUT_READ
|
||||||
|
+ pos = dictionaryPos - rep0;
|
||||||
|
+ if (pos >= dictionarySize)
|
||||||
|
+ pos += dictionarySize;
|
||||||
|
+ previousByte = dictionary[pos];
|
||||||
|
+ dictionary[dictionaryPos] = previousByte;
|
||||||
|
+ if (++dictionaryPos == dictionarySize)
|
||||||
|
+ dictionaryPos = 0;
|
||||||
|
+ #else
|
||||||
|
+ previousByte = outStream[nowPos - rep0];
|
||||||
|
+ #endif
|
||||||
|
+ outStream[nowPos++] = previousByte;
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ else
|
||||||
|
+ {
|
||||||
|
+ UInt32 distance;
|
||||||
|
+ if(RangeDecoderBitDecode(p + IsRepG1 + state, &rd) == 0)
|
||||||
|
+ distance = rep1;
|
||||||
|
+ else
|
||||||
|
+ {
|
||||||
|
+ if(RangeDecoderBitDecode(p + IsRepG2 + state, &rd) == 0)
|
||||||
|
+ distance = rep2;
|
||||||
|
+ else
|
||||||
|
+ {
|
||||||
|
+ distance = rep3;
|
||||||
|
+ rep3 = rep2;
|
||||||
|
+ }
|
||||||
|
+ rep2 = rep1;
|
||||||
|
+ }
|
||||||
|
+ rep1 = rep0;
|
||||||
|
+ rep0 = distance;
|
||||||
|
+ }
|
||||||
|
+ len = LzmaLenDecode(p + RepLenCoder, &rd, posState);
|
||||||
|
+ state = state < 7 ? 8 : 11;
|
||||||
|
+ }
|
||||||
|
+ else
|
||||||
|
+ {
|
||||||
|
+ int posSlot;
|
||||||
|
+ rep3 = rep2;
|
||||||
|
+ rep2 = rep1;
|
||||||
|
+ rep1 = rep0;
|
||||||
|
+ state = state < 7 ? 7 : 10;
|
||||||
|
+ len = LzmaLenDecode(p + LenCoder, &rd, posState);
|
||||||
|
+ posSlot = RangeDecoderBitTreeDecode(p + PosSlot +
|
||||||
|
+ ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) <<
|
||||||
|
+ kNumPosSlotBits), kNumPosSlotBits, &rd);
|
||||||
|
+ if (posSlot >= kStartPosModelIndex)
|
||||||
|
+ {
|
||||||
|
+ int numDirectBits = ((posSlot >> 1) - 1);
|
||||||
|
+ rep0 = ((2 | ((UInt32)posSlot & 1)) << numDirectBits);
|
||||||
|
+ if (posSlot < kEndPosModelIndex)
|
||||||
|
+ {
|
||||||
|
+ rep0 += RangeDecoderReverseBitTreeDecode(
|
||||||
|
+ p + SpecPos + rep0 - posSlot - 1, numDirectBits, &rd);
|
||||||
|
+ }
|
||||||
|
+ else
|
||||||
|
+ {
|
||||||
|
+ rep0 += RangeDecoderDecodeDirectBits(&rd,
|
||||||
|
+ numDirectBits - kNumAlignBits) << kNumAlignBits;
|
||||||
|
+ rep0 += RangeDecoderReverseBitTreeDecode(p + Align, kNumAlignBits, &rd);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ else
|
||||||
|
+ rep0 = posSlot;
|
||||||
|
+ rep0++;
|
||||||
|
+ }
|
||||||
|
+ if (rep0 == (UInt32)(0))
|
||||||
|
+ {
|
||||||
|
+ /* it's for stream version */
|
||||||
|
+ len = -1;
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ if (rep0 > nowPos
|
||||||
|
+ #ifdef _LZMA_OUT_READ
|
||||||
|
+ + globalPos
|
||||||
|
+ #endif
|
||||||
|
+ )
|
||||||
|
+ {
|
||||||
|
+ return LZMA_RESULT_DATA_ERROR;
|
||||||
|
+ }
|
||||||
|
+ len += kMatchMinLen;
|
||||||
|
+ do
|
||||||
|
+ {
|
||||||
|
+ #ifdef _LZMA_OUT_READ
|
||||||
|
+ UInt32 pos = dictionaryPos - rep0;
|
||||||
|
+ if (pos >= dictionarySize)
|
||||||
|
+ pos += dictionarySize;
|
||||||
|
+ previousByte = dictionary[pos];
|
||||||
|
+ dictionary[dictionaryPos] = previousByte;
|
||||||
|
+ if (++dictionaryPos == dictionarySize)
|
||||||
|
+ dictionaryPos = 0;
|
||||||
|
+ #else
|
||||||
|
+ previousByte = outStream[nowPos - rep0];
|
||||||
|
+ #endif
|
||||||
|
+ outStream[nowPos++] = previousByte;
|
||||||
|
+ len--;
|
||||||
|
+ }
|
||||||
|
+ while(len > 0 && nowPos < outSize);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ #ifdef _LZMA_OUT_READ
|
||||||
|
+ vs->RangeDecoder = rd;
|
||||||
|
+ vs->DictionaryPos = dictionaryPos;
|
||||||
|
+ vs->GlobalPos = globalPos + nowPos;
|
||||||
|
+ vs->Reps[0] = rep0;
|
||||||
|
+ vs->Reps[1] = rep1;
|
||||||
|
+ vs->Reps[2] = rep2;
|
||||||
|
+ vs->Reps[3] = rep3;
|
||||||
|
+ vs->State = state;
|
||||||
|
+ vs->PreviousIsMatch = previousIsMatch;
|
||||||
|
+ vs->RemainLen = len;
|
||||||
|
+ #endif
|
||||||
|
+
|
||||||
|
+ *outSizeProcessed = nowPos;
|
||||||
|
+ return LZMA_RESULT_OK;
|
||||||
|
+}
|
||||||
|
--- a/lib/Makefile
|
||||||
|
+++ b/lib/Makefile
|
||||||
|
@@ -19,7 +19,7 @@
|
||||||
|
lib-y += kobject.o kref.o klist.o
|
||||||
|
|
||||||
|
obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \
|
||||||
|
- bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o
|
||||||
|
+ bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o LzmaDecode.o
|
||||||
|
|
||||||
|
ifeq ($(CONFIG_DEBUG_KOBJECT),y)
|
||||||
|
CFLAGS_kobject.o += -DDEBUG
|
107
target/linux/generic-2.6/patches-2.6.27/003-squashfs_lzma.patch
Normal file
107
target/linux/generic-2.6/patches-2.6.27/003-squashfs_lzma.patch
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
--- a/fs/squashfs/inode.c
|
||||||
|
+++ b/fs/squashfs/inode.c
|
||||||
|
@@ -4,6 +4,9 @@
|
||||||
|
* Copyright (c) 2002, 2003, 2004, 2005, 2006
|
||||||
|
* Phillip Lougher <phillip@lougher.org.uk>
|
||||||
|
*
|
||||||
|
+ * LZMA decompressor support added by Oleg I. Vdovikin
|
||||||
|
+ * Copyright (c) 2005 Oleg I.Vdovikin <oleg@cs.msu.su>
|
||||||
|
+ *
|
||||||
|
* 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,
|
||||||
|
@@ -21,6 +24,7 @@
|
||||||
|
* inode.c
|
||||||
|
*/
|
||||||
|
|
||||||
|
+#define SQUASHFS_LZMA
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/squashfs_fs.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
@@ -44,6 +48,19 @@
|
||||||
|
|
||||||
|
#include "squashfs.h"
|
||||||
|
|
||||||
|
+#ifdef SQUASHFS_LZMA
|
||||||
|
+#include <linux/LzmaDecode.h>
|
||||||
|
+
|
||||||
|
+/* default LZMA settings, should be in sync with mksquashfs */
|
||||||
|
+#define LZMA_LC 3
|
||||||
|
+#define LZMA_LP 0
|
||||||
|
+#define LZMA_PB 2
|
||||||
|
+
|
||||||
|
+#define LZMA_WORKSPACE_SIZE ((LZMA_BASE_SIZE + \
|
||||||
|
+ (LZMA_LIT_SIZE << (LZMA_LC + LZMA_LP))) * sizeof(CProb))
|
||||||
|
+
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
static void squashfs_put_super(struct super_block *);
|
||||||
|
static int squashfs_statfs(struct dentry *, struct kstatfs *);
|
||||||
|
static int squashfs_symlink_readpage(struct file *file, struct page *page);
|
||||||
|
@@ -64,7 +81,11 @@
|
||||||
|
const char *, void *, struct vfsmount *);
|
||||||
|
|
||||||
|
|
||||||
|
+#ifdef SQUASHFS_LZMA
|
||||||
|
+static unsigned char lzma_workspace[LZMA_WORKSPACE_SIZE];
|
||||||
|
+#else
|
||||||
|
static z_stream stream;
|
||||||
|
+#endif
|
||||||
|
|
||||||
|
static struct file_system_type squashfs_fs_type = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
@@ -249,6 +270,15 @@
|
||||||
|
if (compressed) {
|
||||||
|
int zlib_err;
|
||||||
|
|
||||||
|
+#ifdef SQUASHFS_LZMA
|
||||||
|
+ if ((zlib_err = LzmaDecode(lzma_workspace,
|
||||||
|
+ LZMA_WORKSPACE_SIZE, LZMA_LC, LZMA_LP, LZMA_PB,
|
||||||
|
+ c_buffer, c_byte, buffer, msblk->read_size, &bytes)) != LZMA_RESULT_OK)
|
||||||
|
+ {
|
||||||
|
+ ERROR("lzma returned unexpected result 0x%x\n", zlib_err);
|
||||||
|
+ bytes = 0;
|
||||||
|
+ }
|
||||||
|
+#else
|
||||||
|
stream.next_in = c_buffer;
|
||||||
|
stream.avail_in = c_byte;
|
||||||
|
stream.next_out = buffer;
|
||||||
|
@@ -263,7 +293,7 @@
|
||||||
|
bytes = 0;
|
||||||
|
} else
|
||||||
|
bytes = stream.total_out;
|
||||||
|
-
|
||||||
|
+#endif
|
||||||
|
up(&msblk->read_data_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -2045,15 +2075,19 @@
|
||||||
|
printk(KERN_INFO "squashfs: version 3.0 (2006/03/15) "
|
||||||
|
"Phillip Lougher\n");
|
||||||
|
|
||||||
|
+#ifndef SQUASHFS_LZMA
|
||||||
|
if (!(stream.workspace = vmalloc(zlib_inflate_workspacesize()))) {
|
||||||
|
ERROR("Failed to allocate zlib workspace\n");
|
||||||
|
destroy_inodecache();
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
+#endif
|
||||||
|
|
||||||
|
if ((err = register_filesystem(&squashfs_fs_type))) {
|
||||||
|
+#ifndef SQUASHFS_LZMA
|
||||||
|
vfree(stream.workspace);
|
||||||
|
+#endif
|
||||||
|
destroy_inodecache();
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -2064,7 +2098,9 @@
|
||||||
|
|
||||||
|
static void __exit exit_squashfs_fs(void)
|
||||||
|
{
|
||||||
|
+#ifndef SQUASHFS_LZMA
|
||||||
|
vfree(stream.workspace);
|
||||||
|
+#endif
|
||||||
|
unregister_filesystem(&squashfs_fs_type);
|
||||||
|
destroy_inodecache();
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
--- a/Makefile
|
||||||
|
+++ b/Makefile
|
||||||
|
@@ -549,6 +549,9 @@
|
||||||
|
NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include)
|
||||||
|
CHECKFLAGS += $(NOSTDINC_FLAGS)
|
||||||
|
|
||||||
|
+# improve gcc optimization
|
||||||
|
+CFLAGS += $(call cc-option,-funit-at-a-time,)
|
||||||
|
+
|
||||||
|
# warn about C99 declaration after statement
|
||||||
|
KBUILD_CFLAGS += $(call cc-option,-Wdeclaration-after-statement,)
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
--- a/fs/squashfs/inode.c
|
||||||
|
+++ b/fs/squashfs/inode.c
|
||||||
|
@@ -33,6 +33,7 @@
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/smp_lock.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
+#include <linux/exportfs.h>
|
||||||
|
#include <linux/squashfs_fs_sb.h>
|
||||||
|
#include <linux/squashfs_fs_i.h>
|
||||||
|
#include <linux/buffer_head.h>
|
||||||
|
@@ -43,8 +44,8 @@
|
||||||
|
#include <linux/zlib.h>
|
||||||
|
#include <linux/blkdev.h>
|
||||||
|
#include <linux/vmalloc.h>
|
||||||
|
+#include <linux/semaphore.h>
|
||||||
|
#include <asm/uaccess.h>
|
||||||
|
-#include <asm/semaphore.h>
|
||||||
|
|
||||||
|
#include "squashfs.h"
|
||||||
|
|
||||||
|
@@ -2125,7 +2126,7 @@
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
-static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flags)
|
||||||
|
+static void init_once(void *foo)
|
||||||
|
{
|
||||||
|
struct squashfs_inode_info *ei = foo;
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
--- a/include/asm-mips/system.h
|
||||||
|
+++ b/include/asm-mips/system.h
|
||||||
|
@@ -185,7 +185,7 @@
|
||||||
|
if something tries to do an invalid xchg(). */
|
||||||
|
extern void __xchg_called_with_bad_pointer(void);
|
||||||
|
|
||||||
|
-static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int size)
|
||||||
|
+static __always_inline unsigned long __xchg(unsigned long x, volatile void * ptr, int size)
|
||||||
|
{
|
||||||
|
switch (size) {
|
||||||
|
case 4:
|
|
@ -0,0 +1,36 @@
|
||||||
|
--- a/drivers/mtd/chips/cfi_cmdset_0002.c
|
||||||
|
+++ b/drivers/mtd/chips/cfi_cmdset_0002.c
|
||||||
|
@@ -48,6 +48,7 @@
|
||||||
|
#define SST49LF040B 0x0050
|
||||||
|
#define SST49LF008A 0x005a
|
||||||
|
#define AT49BV6416 0x00d6
|
||||||
|
+#define MANUFACTURER_SAMSUNG 0x00ec
|
||||||
|
|
||||||
|
static int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
|
||||||
|
static int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
|
||||||
|
@@ -321,12 +322,19 @@
|
||||||
|
|
||||||
|
if (extp->MajorVersion != '1' ||
|
||||||
|
(extp->MinorVersion < '0' || extp->MinorVersion > '4')) {
|
||||||
|
- printk(KERN_ERR " Unknown Amd/Fujitsu Extended Query "
|
||||||
|
- "version %c.%c.\n", extp->MajorVersion,
|
||||||
|
- extp->MinorVersion);
|
||||||
|
- kfree(extp);
|
||||||
|
- kfree(mtd);
|
||||||
|
- return NULL;
|
||||||
|
+ if (cfi->mfr == MANUFACTURER_SAMSUNG &&
|
||||||
|
+ (extp->MajorVersion == '3' && extp->MinorVersion == '3')) {
|
||||||
|
+ printk(KERN_NOTICE " Newer Samsung flash detected, "
|
||||||
|
+ "should be compatibile with Amd/Fujitsu.\n");
|
||||||
|
+ }
|
||||||
|
+ else {
|
||||||
|
+ printk(KERN_ERR " Unknown Amd/Fujitsu Extended Query "
|
||||||
|
+ "version %c.%c.\n", extp->MajorVersion,
|
||||||
|
+ extp->MinorVersion);
|
||||||
|
+ kfree(extp);
|
||||||
|
+ kfree(mtd);
|
||||||
|
+ return NULL;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Install our own private info structure */
|
|
@ -0,0 +1,19 @@
|
||||||
|
--- a/fs/squashfs/Makefile
|
||||||
|
+++ b/fs/squashfs/Makefile
|
||||||
|
@@ -4,4 +4,3 @@
|
||||||
|
|
||||||
|
obj-$(CONFIG_SQUASHFS) += squashfs.o
|
||||||
|
squashfs-y += inode.o
|
||||||
|
-squashfs-y += squashfs2_0.o
|
||||||
|
--- a/fs/squashfs/squashfs.h
|
||||||
|
+++ b/fs/squashfs/squashfs.h
|
||||||
|
@@ -24,6 +24,9 @@
|
||||||
|
#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
|
||||||
|
#undef CONFIG_SQUASHFS_1_0_COMPATIBILITY
|
||||||
|
#endif
|
||||||
|
+#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY
|
||||||
|
+#undef CONFIG_SQUASHFS_2_0_COMPATIBILITY
|
||||||
|
+#endif
|
||||||
|
|
||||||
|
#ifdef SQUASHFS_TRACE
|
||||||
|
#define TRACE(s, args...) printk(KERN_NOTICE "SQUASHFS: "s, ## args)
|
11
target/linux/generic-2.6/patches-2.6.27/011-mips_boot.patch
Normal file
11
target/linux/generic-2.6/patches-2.6.27/011-mips_boot.patch
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
--- a/arch/mips/kernel/head.S
|
||||||
|
+++ b/arch/mips/kernel/head.S
|
||||||
|
@@ -120,6 +120,8 @@
|
||||||
|
#endif
|
||||||
|
.endm
|
||||||
|
|
||||||
|
+ j kernel_entry
|
||||||
|
+ nop
|
||||||
|
#ifndef CONFIG_NO_EXCEPT_FILL
|
||||||
|
/*
|
||||||
|
* Reserved space for exception handlers.
|
110
target/linux/generic-2.6/patches-2.6.27/060-block2mtd_init.patch
Normal file
110
target/linux/generic-2.6/patches-2.6.27/060-block2mtd_init.patch
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
--- a/drivers/mtd/devices/block2mtd.c
|
||||||
|
+++ b/drivers/mtd/devices/block2mtd.c
|
||||||
|
@@ -14,6 +14,7 @@
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/mtd/mtd.h>
|
||||||
|
+#include <linux/mtd/partitions.h>
|
||||||
|
#include <linux/buffer_head.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/mount.h>
|
||||||
|
@@ -232,10 +233,11 @@
|
||||||
|
|
||||||
|
|
||||||
|
/* FIXME: ensure that mtd->size % erase_size == 0 */
|
||||||
|
-static struct block2mtd_dev *add_device(char *devname, int erase_size)
|
||||||
|
+static struct block2mtd_dev *add_device(char *devname, int erase_size, const char *mtdname)
|
||||||
|
{
|
||||||
|
struct block_device *bdev;
|
||||||
|
struct block2mtd_dev *dev;
|
||||||
|
+ struct mtd_partition *part;
|
||||||
|
char *name;
|
||||||
|
|
||||||
|
if (!devname)
|
||||||
|
@@ -273,17 +275,17 @@
|
||||||
|
|
||||||
|
mutex_init(&dev->write_mutex);
|
||||||
|
|
||||||
|
- /* Setup the MTD structure */
|
||||||
|
- /* make the name contain the block device in */
|
||||||
|
- name = kmalloc(sizeof("block2mtd: ") + strlen(devname) + 1,
|
||||||
|
- GFP_KERNEL);
|
||||||
|
+ if (!mtdname)
|
||||||
|
+ mtdname = devname;
|
||||||
|
+
|
||||||
|
+ name = kmalloc(strlen(mtdname) + 1, GFP_KERNEL);
|
||||||
|
if (!name)
|
||||||
|
goto devinit_err;
|
||||||
|
|
||||||
|
- sprintf(name, "block2mtd: %s", devname);
|
||||||
|
+ strcpy(name, mtdname);
|
||||||
|
dev->mtd.name = name;
|
||||||
|
|
||||||
|
- dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK;
|
||||||
|
+ dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK & ~(erase_size - 1);
|
||||||
|
dev->mtd.erasesize = erase_size;
|
||||||
|
dev->mtd.writesize = 1;
|
||||||
|
dev->mtd.type = MTD_RAM;
|
||||||
|
@@ -296,14 +298,17 @@
|
||||||
|
dev->mtd.priv = dev;
|
||||||
|
dev->mtd.owner = THIS_MODULE;
|
||||||
|
|
||||||
|
- if (add_mtd_device(&dev->mtd)) {
|
||||||
|
+ part = kzalloc(sizeof(struct mtd_partition), GFP_KERNEL);
|
||||||
|
+ part->name = dev->mtd.name;
|
||||||
|
+ part->offset = 0;
|
||||||
|
+ part->size = dev->mtd.size;
|
||||||
|
+ if (add_mtd_partitions(&dev->mtd, part, 1)) {
|
||||||
|
/* Device didnt get added, so free the entry */
|
||||||
|
goto devinit_err;
|
||||||
|
}
|
||||||
|
list_add(&dev->list, &blkmtd_device_list);
|
||||||
|
INFO("mtd%d: [%s] erase_size = %dKiB [%d]", dev->mtd.index,
|
||||||
|
- dev->mtd.name + strlen("block2mtd: "),
|
||||||
|
- dev->mtd.erasesize >> 10, dev->mtd.erasesize);
|
||||||
|
+ mtdname, dev->mtd.erasesize >> 10, dev->mtd.erasesize);
|
||||||
|
return dev;
|
||||||
|
|
||||||
|
devinit_err:
|
||||||
|
@@ -376,9 +381,9 @@
|
||||||
|
|
||||||
|
static int block2mtd_setup2(const char *val)
|
||||||
|
{
|
||||||
|
- char buf[80 + 12]; /* 80 for device, 12 for erase size */
|
||||||
|
+ char buf[80 + 12 + 80]; /* 80 for device, 12 for erase size, 80 for name */
|
||||||
|
char *str = buf;
|
||||||
|
- char *token[2];
|
||||||
|
+ char *token[3];
|
||||||
|
char *name;
|
||||||
|
size_t erase_size = PAGE_SIZE;
|
||||||
|
int i, ret;
|
||||||
|
@@ -389,7 +394,7 @@
|
||||||
|
strcpy(str, val);
|
||||||
|
kill_final_newline(str);
|
||||||
|
|
||||||
|
- for (i = 0; i < 2; i++)
|
||||||
|
+ for (i = 0; i < 3; i++)
|
||||||
|
token[i] = strsep(&str, ",");
|
||||||
|
|
||||||
|
if (str)
|
||||||
|
@@ -408,8 +413,10 @@
|
||||||
|
parse_err("illegal erase size");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+ if (token[2] && (strlen(token[2]) + 1 > 80))
|
||||||
|
+ parse_err("mtd device name too long");
|
||||||
|
|
||||||
|
- add_device(name, erase_size);
|
||||||
|
+ add_device(name, erase_size, token[2]);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@@ -443,7 +450,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
module_param_call(block2mtd, block2mtd_setup, NULL, NULL, 0200);
|
||||||
|
-MODULE_PARM_DESC(block2mtd, "Device to use. \"block2mtd=<dev>[,<erasesize>]\"");
|
||||||
|
+MODULE_PARM_DESC(block2mtd, "Device to use. \"block2mtd=<dev>[,<erasesize>[,<name>]]\"");
|
||||||
|
|
||||||
|
static int __init block2mtd_init(void)
|
||||||
|
{
|
612
target/linux/generic-2.6/patches-2.6.27/065-rootfs_split.patch
Normal file
612
target/linux/generic-2.6/patches-2.6.27/065-rootfs_split.patch
Normal file
|
@ -0,0 +1,612 @@
|
||||||
|
--- a/drivers/mtd/Kconfig
|
||||||
|
+++ b/drivers/mtd/Kconfig
|
||||||
|
@@ -45,6 +45,16 @@
|
||||||
|
devices. Partitioning on NFTL 'devices' is a different - that's the
|
||||||
|
'normal' form of partitioning used on a block device.
|
||||||
|
|
||||||
|
+config MTD_ROOTFS_ROOT_DEV
|
||||||
|
+ bool "Automatically set 'rootfs' partition to be root filesystem"
|
||||||
|
+ depends on MTD_PARTITIONS
|
||||||
|
+ default y
|
||||||
|
+
|
||||||
|
+config MTD_ROOTFS_SPLIT
|
||||||
|
+ bool "Automatically split 'rootfs' partition for squashfs"
|
||||||
|
+ depends on MTD_PARTITIONS
|
||||||
|
+ default y
|
||||||
|
+
|
||||||
|
config MTD_REDBOOT_PARTS
|
||||||
|
tristate "RedBoot partition table parsing"
|
||||||
|
depends on MTD_PARTITIONS
|
||||||
|
--- a/drivers/mtd/mtdpart.c
|
||||||
|
+++ b/drivers/mtd/mtdpart.c
|
||||||
|
@@ -18,6 +18,8 @@
|
||||||
|
#include <linux/mtd/mtd.h>
|
||||||
|
#include <linux/mtd/partitions.h>
|
||||||
|
#include <linux/mtd/compatmac.h>
|
||||||
|
+#include <linux/squashfs_fs.h>
|
||||||
|
+#include <linux/root_dev.h>
|
||||||
|
|
||||||
|
/* Our partition linked list */
|
||||||
|
static LIST_HEAD(mtd_partitions);
|
||||||
|
@@ -37,7 +39,7 @@
|
||||||
|
* the pointer to that structure with this macro.
|
||||||
|
*/
|
||||||
|
#define PART(x) ((struct mtd_part *)(x))
|
||||||
|
-
|
||||||
|
+#define IS_PART(mtd) (mtd->read == part_read)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MTD methods which simply translate the effective address and pass through
|
||||||
|
@@ -489,6 +491,148 @@
|
||||||
|
return slave;
|
||||||
|
}
|
||||||
|
|
||||||
|
+#ifdef CONFIG_MTD_ROOTFS_SPLIT
|
||||||
|
+#define ROOTFS_SPLIT_NAME "rootfs_data"
|
||||||
|
+#define ROOTFS_REMOVED_NAME "<removed>"
|
||||||
|
+static int split_squashfs(struct mtd_info *master, int offset, int *split_offset)
|
||||||
|
+{
|
||||||
|
+ char buf[512];
|
||||||
|
+ struct squashfs_super_block *sb = (struct squashfs_super_block *) buf;
|
||||||
|
+ int len, ret;
|
||||||
|
+
|
||||||
|
+ ret = master->read(master, offset, sizeof(*sb), &len, buf);
|
||||||
|
+ if (ret || (len != sizeof(*sb))) {
|
||||||
|
+ printk(KERN_ALERT "split_squashfs: error occured while reading "
|
||||||
|
+ "from \"%s\"\n", master->name);
|
||||||
|
+ return -EINVAL;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (*((u32 *) buf) != SQUASHFS_MAGIC) {
|
||||||
|
+ printk(KERN_ALERT "split_squashfs: no squashfs found in \"%s\"\n",
|
||||||
|
+ master->name);
|
||||||
|
+ *split_offset = 0;
|
||||||
|
+ return 0;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (sb->bytes_used <= 0) {
|
||||||
|
+ printk(KERN_ALERT "split_squashfs: squashfs is empty in \"%s\"\n",
|
||||||
|
+ master->name);
|
||||||
|
+ *split_offset = 0;
|
||||||
|
+ return 0;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ len = (u32) sb->bytes_used;
|
||||||
|
+ len += (offset & 0x000fffff);
|
||||||
|
+ len += (master->erasesize - 1);
|
||||||
|
+ len &= ~(master->erasesize - 1);
|
||||||
|
+ len -= (offset & 0x000fffff);
|
||||||
|
+ *split_offset = offset + len;
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int split_rootfs_data(struct mtd_info *master, struct mtd_info *rpart, const struct mtd_partition *part,
|
||||||
|
+ int index)
|
||||||
|
+{
|
||||||
|
+ struct mtd_partition *dpart;
|
||||||
|
+ struct mtd_part *slave = NULL;
|
||||||
|
+ int split_offset = 0;
|
||||||
|
+ int ret;
|
||||||
|
+
|
||||||
|
+ ret = split_squashfs(master, part->offset, &split_offset);
|
||||||
|
+ if (ret)
|
||||||
|
+ return ret;
|
||||||
|
+
|
||||||
|
+ if (split_offset <= 0)
|
||||||
|
+ return 0;
|
||||||
|
+
|
||||||
|
+ dpart = kmalloc(sizeof(*part)+sizeof(ROOTFS_SPLIT_NAME)+1, GFP_KERNEL);
|
||||||
|
+ if (dpart == NULL) {
|
||||||
|
+ printk(KERN_INFO "split_squashfs: no memory for partition \"%s\"\n",
|
||||||
|
+ ROOTFS_SPLIT_NAME);
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ memcpy(dpart, part, sizeof(*part));
|
||||||
|
+ dpart->name = (unsigned char *)&dpart[1];
|
||||||
|
+ strcpy(dpart->name, ROOTFS_SPLIT_NAME);
|
||||||
|
+
|
||||||
|
+ dpart->size -= split_offset - dpart->offset;
|
||||||
|
+ dpart->offset = split_offset;
|
||||||
|
+
|
||||||
|
+ if (dpart == NULL)
|
||||||
|
+ return 1;
|
||||||
|
+
|
||||||
|
+ printk(KERN_INFO "mtd: partition \"%s\" created automatically, ofs=%X, len=%X \n",
|
||||||
|
+ ROOTFS_SPLIT_NAME, dpart->offset, dpart->size);
|
||||||
|
+
|
||||||
|
+ slave = add_one_partition(master, dpart, index, split_offset);
|
||||||
|
+ if (!slave) {
|
||||||
|
+ kfree(dpart);
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+ }
|
||||||
|
+ rpart->split = &slave->mtd;
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int refresh_rootfs_split(struct mtd_info *mtd)
|
||||||
|
+{
|
||||||
|
+ struct mtd_partition tpart;
|
||||||
|
+ struct mtd_part *part;
|
||||||
|
+ char *name;
|
||||||
|
+ int index = 0;
|
||||||
|
+ int offset, size;
|
||||||
|
+ int ret;
|
||||||
|
+
|
||||||
|
+ part = PART(mtd);
|
||||||
|
+
|
||||||
|
+ /* check for the new squashfs offset first */
|
||||||
|
+ ret = split_squashfs(part->master, part->offset, &offset);
|
||||||
|
+ if (ret)
|
||||||
|
+ return ret;
|
||||||
|
+
|
||||||
|
+ if ((offset > 0) && !mtd->split) {
|
||||||
|
+ printk(KERN_INFO "%s: creating new split partition for \"%s\"\n", __func__, mtd->name);
|
||||||
|
+ /* if we don't have a rootfs split partition, create a new one */
|
||||||
|
+ tpart.name = (char *) mtd->name;
|
||||||
|
+ tpart.size = mtd->size;
|
||||||
|
+ tpart.offset = part->offset;
|
||||||
|
+
|
||||||
|
+ /* find the index of the last partition */
|
||||||
|
+ if (!list_empty(&mtd_partitions))
|
||||||
|
+ index = list_first_entry(&mtd_partitions, struct mtd_part, list)->index + 1;
|
||||||
|
+
|
||||||
|
+ return split_rootfs_data(part->master, &part->mtd, &tpart, index);
|
||||||
|
+ } else if ((offset > 0) && mtd->split) {
|
||||||
|
+ /* update the offsets of the existing partition */
|
||||||
|
+ size = mtd->size + part->offset - offset;
|
||||||
|
+
|
||||||
|
+ part = PART(mtd->split);
|
||||||
|
+ part->offset = offset;
|
||||||
|
+ part->mtd.size = size;
|
||||||
|
+ printk(KERN_INFO "%s: %s partition \"" ROOTFS_SPLIT_NAME "\", offset: 0x%06x (0x%06x)\n",
|
||||||
|
+ __func__, (!strcmp(part->mtd.name, ROOTFS_SPLIT_NAME) ? "updating" : "creating"),
|
||||||
|
+ part->offset, part->mtd.size);
|
||||||
|
+ name = kmalloc(sizeof(ROOTFS_SPLIT_NAME) + 1, GFP_KERNEL);
|
||||||
|
+ strcpy(name, ROOTFS_SPLIT_NAME);
|
||||||
|
+ part->mtd.name = name;
|
||||||
|
+ } else if ((offset <= 0) && mtd->split) {
|
||||||
|
+ printk(KERN_INFO "%s: removing partition \"%s\"\n", __func__, mtd->split->name);
|
||||||
|
+
|
||||||
|
+ /* mark existing partition as removed */
|
||||||
|
+ part = PART(mtd->split);
|
||||||
|
+ name = kmalloc(sizeof(ROOTFS_SPLIT_NAME) + 1, GFP_KERNEL);
|
||||||
|
+ strcpy(name, ROOTFS_REMOVED_NAME);
|
||||||
|
+ part->mtd.name = name;
|
||||||
|
+ part->offset = 0;
|
||||||
|
+ part->mtd.size = 0;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+#endif /* CONFIG_MTD_ROOTFS_SPLIT */
|
||||||
|
+
|
||||||
|
/*
|
||||||
|
* This function, given a master MTD object and a partition table, creates
|
||||||
|
* and registers slave MTD objects which are bound to the master according to
|
||||||
|
@@ -502,14 +646,29 @@
|
||||||
|
{
|
||||||
|
struct mtd_part *slave;
|
||||||
|
u_int32_t cur_offset = 0;
|
||||||
|
- int i;
|
||||||
|
+ int i, j, ret;
|
||||||
|
|
||||||
|
printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
|
||||||
|
|
||||||
|
- for (i = 0; i < nbparts; i++) {
|
||||||
|
- slave = add_one_partition(master, parts + i, i, cur_offset);
|
||||||
|
+ for (i = 0, j = 0; i < nbparts; i++) {
|
||||||
|
+ slave = add_one_partition(master, parts + i, j++, cur_offset);
|
||||||
|
if (!slave)
|
||||||
|
return -ENOMEM;
|
||||||
|
+
|
||||||
|
+ if (!strcmp(parts[i].name, "rootfs") && slave->registered) {
|
||||||
|
+#ifdef CONFIG_MTD_ROOTFS_ROOT_DEV
|
||||||
|
+ if (ROOT_DEV == 0) {
|
||||||
|
+ printk(KERN_NOTICE "mtd: partition \"rootfs\" "
|
||||||
|
+ "set to be root filesystem\n");
|
||||||
|
+ ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, slave->mtd.index);
|
||||||
|
+ }
|
||||||
|
+#endif
|
||||||
|
+#ifdef CONFIG_MTD_ROOTFS_SPLIT
|
||||||
|
+ ret = split_rootfs_data(master, &slave->mtd, &parts[i], j);
|
||||||
|
+ if (ret == 0)
|
||||||
|
+ j++;
|
||||||
|
+#endif
|
||||||
|
+ }
|
||||||
|
cur_offset = slave->offset + slave->mtd.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -517,6 +676,32 @@
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(add_mtd_partitions);
|
||||||
|
|
||||||
|
+int refresh_mtd_partitions(struct mtd_info *mtd)
|
||||||
|
+{
|
||||||
|
+ int ret = 0;
|
||||||
|
+
|
||||||
|
+ if (IS_PART(mtd)) {
|
||||||
|
+ struct mtd_part *part;
|
||||||
|
+ struct mtd_info *master;
|
||||||
|
+
|
||||||
|
+ part = PART(mtd);
|
||||||
|
+ master = part->master;
|
||||||
|
+ if (master->refresh_device)
|
||||||
|
+ ret = master->refresh_device(master);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (!ret && mtd->refresh_device)
|
||||||
|
+ ret = mtd->refresh_device(mtd);
|
||||||
|
+
|
||||||
|
+#ifdef CONFIG_MTD_ROOTFS_SPLIT
|
||||||
|
+ if (!ret && IS_PART(mtd) && !strcmp(mtd->name, "rootfs"))
|
||||||
|
+ refresh_rootfs_split(mtd);
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+EXPORT_SYMBOL_GPL(refresh_mtd_partitions);
|
||||||
|
+
|
||||||
|
static DEFINE_SPINLOCK(part_parser_lock);
|
||||||
|
static LIST_HEAD(part_parsers);
|
||||||
|
|
||||||
|
--- a/drivers/mtd/devices/block2mtd.c
|
||||||
|
+++ b/drivers/mtd/devices/block2mtd.c
|
||||||
|
@@ -29,6 +29,8 @@
|
||||||
|
struct block_device *blkdev;
|
||||||
|
struct mtd_info mtd;
|
||||||
|
struct mutex write_mutex;
|
||||||
|
+ rwlock_t bdev_mutex;
|
||||||
|
+ char devname[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@@ -81,6 +83,12 @@
|
||||||
|
size_t len = instr->len;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
+ read_lock(&dev->bdev_mutex);
|
||||||
|
+ if (!dev->blkdev) {
|
||||||
|
+ err = -EINVAL;
|
||||||
|
+ goto done;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
instr->state = MTD_ERASING;
|
||||||
|
mutex_lock(&dev->write_mutex);
|
||||||
|
err = _block2mtd_erase(dev, from, len);
|
||||||
|
@@ -93,6 +101,10 @@
|
||||||
|
|
||||||
|
instr->state = MTD_ERASE_DONE;
|
||||||
|
mtd_erase_callback(instr);
|
||||||
|
+
|
||||||
|
+done:
|
||||||
|
+ read_unlock(&dev->bdev_mutex);
|
||||||
|
+
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -104,10 +116,14 @@
|
||||||
|
struct page *page;
|
||||||
|
int index = from >> PAGE_SHIFT;
|
||||||
|
int offset = from & (PAGE_SIZE-1);
|
||||||
|
- int cpylen;
|
||||||
|
+ int cpylen, err = 0;
|
||||||
|
+
|
||||||
|
+ read_lock(&dev->bdev_mutex);
|
||||||
|
+ if (!dev->blkdev || (from > mtd->size)) {
|
||||||
|
+ err = -EINVAL;
|
||||||
|
+ goto done;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
- if (from > mtd->size)
|
||||||
|
- return -EINVAL;
|
||||||
|
if (from + len > mtd->size)
|
||||||
|
len = mtd->size - from;
|
||||||
|
|
||||||
|
@@ -122,10 +138,14 @@
|
||||||
|
len = len - cpylen;
|
||||||
|
|
||||||
|
page = page_read(dev->blkdev->bd_inode->i_mapping, index);
|
||||||
|
- if (!page)
|
||||||
|
- return -ENOMEM;
|
||||||
|
- if (IS_ERR(page))
|
||||||
|
- return PTR_ERR(page);
|
||||||
|
+ if (!page) {
|
||||||
|
+ err = -ENOMEM;
|
||||||
|
+ goto done;
|
||||||
|
+ }
|
||||||
|
+ if (IS_ERR(page)) {
|
||||||
|
+ err = PTR_ERR(page);
|
||||||
|
+ goto done;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
memcpy(buf, page_address(page) + offset, cpylen);
|
||||||
|
page_cache_release(page);
|
||||||
|
@@ -136,7 +156,10 @@
|
||||||
|
offset = 0;
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
- return 0;
|
||||||
|
+
|
||||||
|
+done:
|
||||||
|
+ read_unlock(&dev->bdev_mutex);
|
||||||
|
+ return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -188,12 +211,22 @@
|
||||||
|
size_t *retlen, const u_char *buf)
|
||||||
|
{
|
||||||
|
struct block2mtd_dev *dev = mtd->priv;
|
||||||
|
- int err;
|
||||||
|
+ int err = 0;
|
||||||
|
+
|
||||||
|
+ read_lock(&dev->bdev_mutex);
|
||||||
|
+ if (!dev->blkdev) {
|
||||||
|
+ err = -EINVAL;
|
||||||
|
+ goto done;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
if (!len)
|
||||||
|
- return 0;
|
||||||
|
- if (to >= mtd->size)
|
||||||
|
- return -ENOSPC;
|
||||||
|
+ goto done;
|
||||||
|
+
|
||||||
|
+ if (to >= mtd->size) {
|
||||||
|
+ err = -ENOSPC;
|
||||||
|
+ goto done;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
if (to + len > mtd->size)
|
||||||
|
len = mtd->size - to;
|
||||||
|
|
||||||
|
@@ -202,6 +235,9 @@
|
||||||
|
mutex_unlock(&dev->write_mutex);
|
||||||
|
if (err > 0)
|
||||||
|
err = 0;
|
||||||
|
+
|
||||||
|
+done:
|
||||||
|
+ read_unlock(&dev->bdev_mutex);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -210,7 +246,12 @@
|
||||||
|
static void block2mtd_sync(struct mtd_info *mtd)
|
||||||
|
{
|
||||||
|
struct block2mtd_dev *dev = mtd->priv;
|
||||||
|
- sync_blockdev(dev->blkdev);
|
||||||
|
+
|
||||||
|
+ read_lock(&dev->bdev_mutex);
|
||||||
|
+ if (dev->blkdev)
|
||||||
|
+ sync_blockdev(dev->blkdev);
|
||||||
|
+ read_unlock(&dev->bdev_mutex);
|
||||||
|
+
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -231,31 +272,22 @@
|
||||||
|
kfree(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
-
|
||||||
|
-/* FIXME: ensure that mtd->size % erase_size == 0 */
|
||||||
|
-static struct block2mtd_dev *add_device(char *devname, int erase_size, const char *mtdname)
|
||||||
|
+static int _open_bdev(struct block2mtd_dev *dev)
|
||||||
|
{
|
||||||
|
struct block_device *bdev;
|
||||||
|
struct block2mtd_dev *dev;
|
||||||
|
struct mtd_partition *part;
|
||||||
|
char *name;
|
||||||
|
|
||||||
|
- if (!devname)
|
||||||
|
- return NULL;
|
||||||
|
-
|
||||||
|
- dev = kzalloc(sizeof(struct block2mtd_dev), GFP_KERNEL);
|
||||||
|
- if (!dev)
|
||||||
|
- return NULL;
|
||||||
|
-
|
||||||
|
/* Get a handle on the device */
|
||||||
|
- bdev = open_bdev_excl(devname, O_RDWR, NULL);
|
||||||
|
+ bdev = open_bdev_excl(dev->devname, O_RDWR, NULL);
|
||||||
|
#ifndef MODULE
|
||||||
|
if (IS_ERR(bdev)) {
|
||||||
|
|
||||||
|
/* We might not have rootfs mounted at this point. Try
|
||||||
|
to resolve the device name by other means. */
|
||||||
|
|
||||||
|
- dev_t devt = name_to_dev_t(devname);
|
||||||
|
+ dev_t devt = name_to_dev_t(dev->devname);
|
||||||
|
if (devt) {
|
||||||
|
bdev = open_by_devnum(devt, FMODE_WRITE | FMODE_READ);
|
||||||
|
}
|
||||||
|
@@ -263,17 +295,96 @@
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (IS_ERR(bdev)) {
|
||||||
|
- ERROR("error: cannot open device %s", devname);
|
||||||
|
- goto devinit_err;
|
||||||
|
+ ERROR("error: cannot open device %s", dev->devname);
|
||||||
|
+ return 1;
|
||||||
|
}
|
||||||
|
dev->blkdev = bdev;
|
||||||
|
|
||||||
|
if (MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) {
|
||||||
|
ERROR("attempting to use an MTD device as a block device");
|
||||||
|
- goto devinit_err;
|
||||||
|
+ return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void _close_bdev(struct block2mtd_dev *dev)
|
||||||
|
+{
|
||||||
|
+ struct block_device *bdev;
|
||||||
|
+
|
||||||
|
+ if (!dev->blkdev)
|
||||||
|
+ return;
|
||||||
|
+
|
||||||
|
+ bdev = dev->blkdev;
|
||||||
|
+ invalidate_mapping_pages(dev->blkdev->bd_inode->i_mapping, 0, -1);
|
||||||
|
+ close_bdev_excl(dev->blkdev);
|
||||||
|
+ dev->blkdev = NULL;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void block2mtd_free_device(struct block2mtd_dev *dev)
|
||||||
|
+{
|
||||||
|
+ if (!dev)
|
||||||
|
+ return;
|
||||||
|
+
|
||||||
|
+ kfree(dev->mtd.name);
|
||||||
|
+ _close_bdev(dev);
|
||||||
|
+ kfree(dev);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+static int block2mtd_refresh(struct mtd_info *mtd)
|
||||||
|
+{
|
||||||
|
+ struct block2mtd_dev *dev = mtd->priv;
|
||||||
|
+ struct block_device *bdev;
|
||||||
|
+ dev_t devt;
|
||||||
|
+ int err = 0;
|
||||||
|
+
|
||||||
|
+ /* no other mtd function can run at this point */
|
||||||
|
+ write_lock(&dev->bdev_mutex);
|
||||||
|
+
|
||||||
|
+ /* get the device number for the whole disk */
|
||||||
|
+ devt = MKDEV(MAJOR(dev->blkdev->bd_dev), 0);
|
||||||
|
+
|
||||||
|
+ /* close the old block device */
|
||||||
|
+ _close_bdev(dev);
|
||||||
|
+
|
||||||
|
+ /* open the whole disk, issue a partition rescan, then */
|
||||||
|
+ bdev = open_by_devnum(devt, FMODE_WRITE | FMODE_READ);
|
||||||
|
+ if (!bdev || !bdev->bd_disk)
|
||||||
|
+ err = -EINVAL;
|
||||||
|
+ else {
|
||||||
|
+ err = rescan_partitions(bdev->bd_disk, bdev);
|
||||||
|
+ }
|
||||||
|
+ if (bdev)
|
||||||
|
+ close_bdev_excl(bdev);
|
||||||
|
+
|
||||||
|
+ /* try to open the partition block device again */
|
||||||
|
+ _open_bdev(dev);
|
||||||
|
+ write_unlock(&dev->bdev_mutex);
|
||||||
|
+
|
||||||
|
+ return err;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/* FIXME: ensure that mtd->size % erase_size == 0 */
|
||||||
|
+static struct block2mtd_dev *add_device(char *devname, int erase_size, char *mtdname)
|
||||||
|
+{
|
||||||
|
+ struct block2mtd_dev *dev;
|
||||||
|
+ struct mtd_partition *part;
|
||||||
|
+
|
||||||
|
+ if (!devname)
|
||||||
|
+ return NULL;
|
||||||
|
+
|
||||||
|
+ dev = kzalloc(sizeof(struct block2mtd_dev) + strlen(devname) + 1, GFP_KERNEL);
|
||||||
|
+ if (!dev)
|
||||||
|
+ return NULL;
|
||||||
|
+
|
||||||
|
+ strcpy(dev->devname, devname);
|
||||||
|
+
|
||||||
|
+ if (_open_bdev(dev))
|
||||||
|
+ goto devinit_err;
|
||||||
|
+
|
||||||
|
mutex_init(&dev->write_mutex);
|
||||||
|
+ rwlock_init(&dev->bdev_mutex);
|
||||||
|
|
||||||
|
if (!mtdname)
|
||||||
|
mtdname = devname;
|
||||||
|
@@ -297,6 +408,7 @@
|
||||||
|
dev->mtd.read = block2mtd_read;
|
||||||
|
dev->mtd.priv = dev;
|
||||||
|
dev->mtd.owner = THIS_MODULE;
|
||||||
|
+ dev->mtd.refresh_device = block2mtd_refresh;
|
||||||
|
|
||||||
|
part = kzalloc(sizeof(struct mtd_partition), GFP_KERNEL);
|
||||||
|
part->name = dev->mtd.name;
|
||||||
|
--- a/drivers/mtd/mtdchar.c
|
||||||
|
+++ b/drivers/mtd/mtdchar.c
|
||||||
|
@@ -16,6 +16,7 @@
|
||||||
|
|
||||||
|
#include <linux/mtd/mtd.h>
|
||||||
|
#include <linux/mtd/compatmac.h>
|
||||||
|
+#include <linux/mtd/partitions.h>
|
||||||
|
|
||||||
|
#include <asm/uaccess.h>
|
||||||
|
|
||||||
|
@@ -771,6 +772,13 @@
|
||||||
|
file->f_pos = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
+#ifdef CONFIG_MTD_PARTITIONS
|
||||||
|
+ case MTDREFRESH:
|
||||||
|
+ {
|
||||||
|
+ ret = refresh_mtd_partitions(mtd);
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+#endif
|
||||||
|
|
||||||
|
default:
|
||||||
|
ret = -ENOTTY;
|
||||||
|
--- a/include/linux/mtd/mtd.h
|
||||||
|
+++ b/include/linux/mtd/mtd.h
|
||||||
|
@@ -96,6 +96,7 @@
|
||||||
|
uint8_t *oobbuf;
|
||||||
|
};
|
||||||
|
|
||||||
|
+struct mtd_info;
|
||||||
|
struct mtd_info {
|
||||||
|
u_char type;
|
||||||
|
u_int32_t flags;
|
||||||
|
@@ -211,6 +212,9 @@
|
||||||
|
struct module *owner;
|
||||||
|
int usecount;
|
||||||
|
|
||||||
|
+ int (*refresh_device)(struct mtd_info *mtd);
|
||||||
|
+ struct mtd_info *split;
|
||||||
|
+
|
||||||
|
/* If the driver is something smart, like UBI, it may need to maintain
|
||||||
|
* its own reference counting. The below functions are only for driver.
|
||||||
|
* The driver may register its callbacks. These callbacks are not
|
||||||
|
--- a/include/linux/mtd/partitions.h
|
||||||
|
+++ b/include/linux/mtd/partitions.h
|
||||||
|
@@ -34,6 +34,7 @@
|
||||||
|
* erasesize aligned (e.g. use MTDPART_OFS_NEXTBLK).
|
||||||
|
*/
|
||||||
|
|
||||||
|
+struct mtd_partition;
|
||||||
|
struct mtd_partition {
|
||||||
|
char *name; /* identifier string */
|
||||||
|
u_int32_t size; /* partition size */
|
||||||
|
@@ -41,6 +42,7 @@
|
||||||
|
u_int32_t mask_flags; /* master MTD flags to mask out for this partition */
|
||||||
|
struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only)*/
|
||||||
|
struct mtd_info **mtdp; /* pointer to store the MTD object */
|
||||||
|
+ int (*refresh_partition)(struct mtd_info *);
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MTDPART_OFS_NXTBLK (-2)
|
||||||
|
@@ -50,6 +52,7 @@
|
||||||
|
|
||||||
|
int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int);
|
||||||
|
int del_mtd_partitions(struct mtd_info *);
|
||||||
|
+int refresh_mtd_partitions(struct mtd_info *);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Functions dealing with the various ways of partitioning the space
|
||||||
|
--- a/include/mtd/mtd-abi.h
|
||||||
|
+++ b/include/mtd/mtd-abi.h
|
||||||
|
@@ -93,6 +93,7 @@
|
||||||
|
#define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout)
|
||||||
|
#define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats)
|
||||||
|
#define MTDFILEMODE _IO('M', 19)
|
||||||
|
+#define MTDREFRESH _IO('M', 23)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Obsolete legacy interface. Keep it in order not to break userspace
|
|
@ -0,0 +1,30 @@
|
||||||
|
--- a/drivers/mtd/redboot.c
|
||||||
|
+++ b/drivers/mtd/redboot.c
|
||||||
|
@@ -249,14 +249,21 @@
|
||||||
|
#endif
|
||||||
|
names += strlen(names)+1;
|
||||||
|
|
||||||
|
-#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
|
||||||
|
if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize <= fl->next->img->flash_base) {
|
||||||
|
- i++;
|
||||||
|
- parts[i].offset = parts[i-1].size + parts[i-1].offset;
|
||||||
|
- parts[i].size = fl->next->img->flash_base - parts[i].offset;
|
||||||
|
- parts[i].name = nullname;
|
||||||
|
- }
|
||||||
|
+ if (!strcmp(parts[i].name, "rootfs")) {
|
||||||
|
+ parts[i].size = fl->next->img->flash_base;
|
||||||
|
+ parts[i].size &= ~(master->erasesize - 1);
|
||||||
|
+ parts[i].size -= parts[i].offset;
|
||||||
|
+#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
|
||||||
|
+ nrparts--;
|
||||||
|
+ } else {
|
||||||
|
+ i++;
|
||||||
|
+ parts[i].offset = parts[i-1].size + parts[i-1].offset;
|
||||||
|
+ parts[i].size = fl->next->img->flash_base - parts[i].offset;
|
||||||
|
+ parts[i].name = nullname;
|
||||||
|
#endif
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
tmp_fl = fl;
|
||||||
|
fl = fl->next;
|
||||||
|
kfree(tmp_fl);
|
|
@ -0,0 +1,60 @@
|
||||||
|
--- a/drivers/mtd/redboot.c
|
||||||
|
+++ b/drivers/mtd/redboot.c
|
||||||
|
@@ -11,6 +11,8 @@
|
||||||
|
#include <linux/mtd/mtd.h>
|
||||||
|
#include <linux/mtd/partitions.h>
|
||||||
|
|
||||||
|
+#define BOARD_CONFIG_PART "boardconfig"
|
||||||
|
+
|
||||||
|
struct fis_image_desc {
|
||||||
|
unsigned char name[16]; // Null terminated name
|
||||||
|
uint32_t flash_base; // Address within FLASH of image
|
||||||
|
@@ -41,6 +43,7 @@
|
||||||
|
struct mtd_partition **pparts,
|
||||||
|
unsigned long fis_origin)
|
||||||
|
{
|
||||||
|
+ unsigned long max_offset = 0;
|
||||||
|
int nrparts = 0;
|
||||||
|
struct fis_image_desc *buf;
|
||||||
|
struct mtd_partition *parts;
|
||||||
|
@@ -209,14 +212,14 @@
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
- parts = kzalloc(sizeof(*parts)*nrparts + nulllen + namelen, GFP_KERNEL);
|
||||||
|
+ parts = kzalloc(sizeof(*parts) * (nrparts + 1) + nulllen + namelen + sizeof(BOARD_CONFIG_PART), GFP_KERNEL);
|
||||||
|
|
||||||
|
if (!parts) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
- nullname = (char *)&parts[nrparts];
|
||||||
|
+ nullname = (char *)&parts[nrparts + 1];
|
||||||
|
#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
|
||||||
|
if (nulllen > 0) {
|
||||||
|
strcpy(nullname, nullstring);
|
||||||
|
@@ -235,6 +238,8 @@
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
for ( ; i<nrparts; i++) {
|
||||||
|
+ if(max_offset < buf[i].flash_base + buf[i].size)
|
||||||
|
+ max_offset = buf[i].flash_base + buf[i].size;
|
||||||
|
parts[i].size = fl->img->size;
|
||||||
|
parts[i].offset = fl->img->flash_base;
|
||||||
|
parts[i].name = names;
|
||||||
|
@@ -268,6 +273,14 @@
|
||||||
|
fl = fl->next;
|
||||||
|
kfree(tmp_fl);
|
||||||
|
}
|
||||||
|
+ if(master->size - max_offset >= master->erasesize)
|
||||||
|
+ {
|
||||||
|
+ parts[nrparts].size = master->size - max_offset;
|
||||||
|
+ parts[nrparts].offset = max_offset;
|
||||||
|
+ parts[nrparts].name = names;
|
||||||
|
+ strcpy(names, BOARD_CONFIG_PART);
|
||||||
|
+ nrparts++;
|
||||||
|
+ }
|
||||||
|
ret = nrparts;
|
||||||
|
*pparts = parts;
|
||||||
|
out:
|
|
@ -0,0 +1,32 @@
|
||||||
|
--- a/include/linux/mtd/nand.h
|
||||||
|
+++ b/include/linux/mtd/nand.h
|
||||||
|
@@ -578,6 +578,7 @@
|
||||||
|
int chip_delay;
|
||||||
|
unsigned int options;
|
||||||
|
const char **part_probe_types;
|
||||||
|
+ int (*chip_fixup)(struct mtd_info *mtd);
|
||||||
|
void *priv;
|
||||||
|
};
|
||||||
|
|
||||||
|
--- a/drivers/mtd/nand/plat_nand.c
|
||||||
|
+++ b/drivers/mtd/nand/plat_nand.c
|
||||||
|
@@ -71,7 +71,18 @@
|
||||||
|
platform_set_drvdata(pdev, data);
|
||||||
|
|
||||||
|
/* Scan to find existance of the device */
|
||||||
|
- if (nand_scan(&data->mtd, 1)) {
|
||||||
|
+ if (nand_scan_ident(&data->mtd, 1)) {
|
||||||
|
+ res = -ENXIO;
|
||||||
|
+ goto out;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (pdata->chip.chip_fixup) {
|
||||||
|
+ res = pdata->chip.chip_fixup(&data->mtd);
|
||||||
|
+ if (res)
|
||||||
|
+ goto out;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (nand_scan_tail(&data->mtd)) {
|
||||||
|
res = -ENXIO;
|
||||||
|
goto out;
|
||||||
|
}
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,108 @@
|
||||||
|
--- a/include/linux/netfilter/xt_layer7.h
|
||||||
|
+++ b/include/linux/netfilter/xt_layer7.h
|
||||||
|
@@ -8,6 +8,7 @@
|
||||||
|
char protocol[MAX_PROTOCOL_LEN];
|
||||||
|
char pattern[MAX_PATTERN_LEN];
|
||||||
|
u_int8_t invert;
|
||||||
|
+ u_int8_t pkt;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _XT_LAYER7_H */
|
||||||
|
--- a/net/netfilter/xt_layer7.c
|
||||||
|
+++ b/net/netfilter/xt_layer7.c
|
||||||
|
@@ -297,34 +297,36 @@
|
||||||
|
}
|
||||||
|
|
||||||
|
/* add the new app data to the conntrack. Return number of bytes added. */
|
||||||
|
-static int add_data(struct nf_conn * master_conntrack,
|
||||||
|
- char * app_data, int appdatalen)
|
||||||
|
+static int add_datastr(char *target, int offset, char *app_data, int len)
|
||||||
|
{
|
||||||
|
int length = 0, i;
|
||||||
|
- int oldlength = master_conntrack->layer7.app_data_len;
|
||||||
|
|
||||||
|
- /* This is a fix for a race condition by Deti Fliegl. However, I'm not
|
||||||
|
- clear on whether the race condition exists or whether this really
|
||||||
|
- fixes it. I might just be being dense... Anyway, if it's not really
|
||||||
|
- a fix, all it does is waste a very small amount of time. */
|
||||||
|
- if(!master_conntrack->layer7.app_data) return 0;
|
||||||
|
+ if (!target) return 0;
|
||||||
|
|
||||||
|
/* Strip nulls. Make everything lower case (our regex lib doesn't
|
||||||
|
do case insensitivity). Add it to the end of the current data. */
|
||||||
|
- for(i = 0; i < maxdatalen-oldlength-1 &&
|
||||||
|
- i < appdatalen; i++) {
|
||||||
|
+ for(i = 0; i < maxdatalen-offset-1 && i < len; i++) {
|
||||||
|
if(app_data[i] != '\0') {
|
||||||
|
/* the kernel version of tolower mungs 'upper ascii' */
|
||||||
|
- master_conntrack->layer7.app_data[length+oldlength] =
|
||||||
|
+ target[length+offset] =
|
||||||
|
isascii(app_data[i])?
|
||||||
|
tolower(app_data[i]) : app_data[i];
|
||||||
|
length++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+ target[length+offset] = '\0';
|
||||||
|
+
|
||||||
|
+ return length;
|
||||||
|
+}
|
||||||
|
|
||||||
|
- master_conntrack->layer7.app_data[length+oldlength] = '\0';
|
||||||
|
- master_conntrack->layer7.app_data_len = length + oldlength;
|
||||||
|
+/* add the new app data to the conntrack. Return number of bytes added. */
|
||||||
|
+static int add_data(struct nf_conn * master_conntrack,
|
||||||
|
+ char * app_data, int appdatalen)
|
||||||
|
+{
|
||||||
|
+ int length;
|
||||||
|
|
||||||
|
+ length = add_datastr(master_conntrack->layer7.app_data, master_conntrack->layer7.app_data_len, app_data, appdatalen);
|
||||||
|
+ master_conntrack->layer7.app_data_len += length;
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -411,7 +413,7 @@
|
||||||
|
const struct xt_layer7_info * info = matchinfo;
|
||||||
|
enum ip_conntrack_info master_ctinfo, ctinfo;
|
||||||
|
struct nf_conn *master_conntrack, *conntrack;
|
||||||
|
- unsigned char * app_data;
|
||||||
|
+ unsigned char *app_data, *tmp_data;
|
||||||
|
unsigned int pattern_result, appdatalen;
|
||||||
|
regexp * comppattern;
|
||||||
|
|
||||||
|
@@ -439,8 +441,8 @@
|
||||||
|
master_conntrack = master_ct(master_conntrack);
|
||||||
|
|
||||||
|
/* if we've classified it or seen too many packets */
|
||||||
|
- if(TOTAL_PACKETS > num_packets ||
|
||||||
|
- master_conntrack->layer7.app_proto) {
|
||||||
|
+ if(!info->pkt && (TOTAL_PACKETS > num_packets ||
|
||||||
|
+ master_conntrack->layer7.app_proto)) {
|
||||||
|
|
||||||
|
pattern_result = match_no_append(conntrack, master_conntrack,
|
||||||
|
ctinfo, master_ctinfo, info);
|
||||||
|
@@ -473,6 +475,25 @@
|
||||||
|
/* the return value gets checked later, when we're ready to use it */
|
||||||
|
comppattern = compile_and_cache(info->pattern, info->protocol);
|
||||||
|
|
||||||
|
+ if (info->pkt) {
|
||||||
|
+ tmp_data = kmalloc(maxdatalen, GFP_ATOMIC);
|
||||||
|
+ if(!tmp_data){
|
||||||
|
+ if (net_ratelimit())
|
||||||
|
+ printk(KERN_ERR "layer7: out of memory in match, bailing.\n");
|
||||||
|
+ return info->invert;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ tmp_data[0] = '\0';
|
||||||
|
+ add_datastr(tmp_data, 0, app_data, appdatalen);
|
||||||
|
+ pattern_result = ((comppattern && regexec(comppattern, tmp_data)) ? 1 : 0);
|
||||||
|
+
|
||||||
|
+ kfree(tmp_data);
|
||||||
|
+ tmp_data = NULL;
|
||||||
|
+ spin_unlock_bh(&l7_lock);
|
||||||
|
+
|
||||||
|
+ return (pattern_result ^ info->invert);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
/* On the first packet of a connection, allocate space for app data */
|
||||||
|
if(TOTAL_PACKETS == 1 && !skb->cb[0] &&
|
||||||
|
!master_conntrack->layer7.app_data){
|
919
target/linux/generic-2.6/patches-2.6.27/110-ipp2p_0.8.1rc1.patch
Normal file
919
target/linux/generic-2.6/patches-2.6.27/110-ipp2p_0.8.1rc1.patch
Normal file
|
@ -0,0 +1,919 @@
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/include/linux/netfilter_ipv4/ipt_ipp2p.h
|
||||||
|
@@ -0,0 +1,31 @@
|
||||||
|
+#ifndef __IPT_IPP2P_H
|
||||||
|
+#define __IPT_IPP2P_H
|
||||||
|
+#define IPP2P_VERSION "0.8.1_rc1"
|
||||||
|
+
|
||||||
|
+struct ipt_p2p_info {
|
||||||
|
+ int cmd;
|
||||||
|
+ int debug;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+#endif //__IPT_IPP2P_H
|
||||||
|
+
|
||||||
|
+#define SHORT_HAND_IPP2P 1 /* --ipp2p switch*/
|
||||||
|
+//#define SHORT_HAND_DATA 4 /* --ipp2p-data switch*/
|
||||||
|
+#define SHORT_HAND_NONE 5 /* no short hand*/
|
||||||
|
+
|
||||||
|
+#define IPP2P_EDK (1 << 1)
|
||||||
|
+#define IPP2P_DATA_KAZAA (1 << 2)
|
||||||
|
+#define IPP2P_DATA_EDK (1 << 3)
|
||||||
|
+#define IPP2P_DATA_DC (1 << 4)
|
||||||
|
+#define IPP2P_DC (1 << 5)
|
||||||
|
+#define IPP2P_DATA_GNU (1 << 6)
|
||||||
|
+#define IPP2P_GNU (1 << 7)
|
||||||
|
+#define IPP2P_KAZAA (1 << 8)
|
||||||
|
+#define IPP2P_BIT (1 << 9)
|
||||||
|
+#define IPP2P_APPLE (1 << 10)
|
||||||
|
+#define IPP2P_SOUL (1 << 11)
|
||||||
|
+#define IPP2P_WINMX (1 << 12)
|
||||||
|
+#define IPP2P_ARES (1 << 13)
|
||||||
|
+#define IPP2P_MUTE (1 << 14)
|
||||||
|
+#define IPP2P_WASTE (1 << 15)
|
||||||
|
+#define IPP2P_XDCC (1 << 16)
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/net/ipv4/netfilter/ipt_ipp2p.c
|
||||||
|
@@ -0,0 +1,856 @@
|
||||||
|
+#if defined(MODVERSIONS)
|
||||||
|
+#include <linux/modversions.h>
|
||||||
|
+#endif
|
||||||
|
+#include <linux/module.h>
|
||||||
|
+#include <linux/netfilter_ipv4/ip_tables.h>
|
||||||
|
+#include <linux/version.h>
|
||||||
|
+#include <linux/netfilter_ipv4/ipt_ipp2p.h>
|
||||||
|
+#include <net/tcp.h>
|
||||||
|
+#include <net/udp.h>
|
||||||
|
+
|
||||||
|
+#define get_u8(X,O) (*(__u8 *)(X + O))
|
||||||
|
+#define get_u16(X,O) (*(__u16 *)(X + O))
|
||||||
|
+#define get_u32(X,O) (*(__u32 *)(X + O))
|
||||||
|
+
|
||||||
|
+MODULE_AUTHOR("Eicke Friedrich/Klaus Degner <ipp2p@ipp2p.org>");
|
||||||
|
+MODULE_DESCRIPTION("An extension to iptables to identify P2P traffic.");
|
||||||
|
+MODULE_LICENSE("GPL");
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+/*Search for UDP eDonkey/eMule/Kad commands*/
|
||||||
|
+int
|
||||||
|
+udp_search_edk (unsigned char *haystack, int packet_len)
|
||||||
|
+{
|
||||||
|
+ unsigned char *t = haystack;
|
||||||
|
+ t += 8;
|
||||||
|
+
|
||||||
|
+ switch (t[0]) {
|
||||||
|
+ case 0xe3:
|
||||||
|
+ { /*edonkey*/
|
||||||
|
+ switch (t[1])
|
||||||
|
+ {
|
||||||
|
+ /* client -> server status request */
|
||||||
|
+ case 0x96:
|
||||||
|
+ if (packet_len == 14) return ((IPP2P_EDK * 100) + 50);
|
||||||
|
+ break;
|
||||||
|
+ /* server -> client status request */
|
||||||
|
+ case 0x97: if (packet_len == 42) return ((IPP2P_EDK * 100) + 51);
|
||||||
|
+ break;
|
||||||
|
+ /* server description request */
|
||||||
|
+ /* e3 2a ff f0 .. | size == 6 */
|
||||||
|
+ case 0xa2: if ( (packet_len == 14) && ( get_u16(t,2) == __constant_htons(0xfff0) ) ) return ((IPP2P_EDK * 100) + 52);
|
||||||
|
+ break;
|
||||||
|
+ /* server description response */
|
||||||
|
+ /* e3 a3 ff f0 .. | size > 40 && size < 200 */
|
||||||
|
+ //case 0xa3: return ((IPP2P_EDK * 100) + 53);
|
||||||
|
+ // break;
|
||||||
|
+ case 0x9a: if (packet_len==26) return ((IPP2P_EDK * 100) + 54);
|
||||||
|
+ break;
|
||||||
|
+
|
||||||
|
+ case 0x92: if (packet_len==18) return ((IPP2P_EDK * 100) + 55);
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ case 0xe4:
|
||||||
|
+ {
|
||||||
|
+ switch (t[1])
|
||||||
|
+ {
|
||||||
|
+ /* e4 20 .. | size == 43 */
|
||||||
|
+ case 0x20: if ((packet_len == 43) && (t[2] != 0x00) && (t[34] != 0x00)) return ((IPP2P_EDK * 100) + 60);
|
||||||
|
+ break;
|
||||||
|
+ /* e4 00 .. 00 | size == 35 ? */
|
||||||
|
+ case 0x00: if ((packet_len == 35) && (t[26] == 0x00)) return ((IPP2P_EDK * 100) + 61);
|
||||||
|
+ break;
|
||||||
|
+ /* e4 10 .. 00 | size == 35 ? */
|
||||||
|
+ case 0x10: if ((packet_len == 35) && (t[26] == 0x00)) return ((IPP2P_EDK * 100) + 62);
|
||||||
|
+ break;
|
||||||
|
+ /* e4 18 .. 00 | size == 35 ? */
|
||||||
|
+ case 0x18: if ((packet_len == 35) && (t[26] == 0x00)) return ((IPP2P_EDK * 100) + 63);
|
||||||
|
+ break;
|
||||||
|
+ /* e4 52 .. | size = 44 */
|
||||||
|
+ case 0x52: if (packet_len == 44 ) return ((IPP2P_EDK * 100) + 64);
|
||||||
|
+ break;
|
||||||
|
+ /* e4 58 .. | size == 6 */
|
||||||
|
+ case 0x58: if (packet_len == 14 ) return ((IPP2P_EDK * 100) + 65);
|
||||||
|
+ break;
|
||||||
|
+ /* e4 59 .. | size == 2 */
|
||||||
|
+ case 0x59: if (packet_len == 10 )return ((IPP2P_EDK * 100) + 66);
|
||||||
|
+ break;
|
||||||
|
+ /* e4 28 .. | packet_len == 52,77,102,127... */
|
||||||
|
+ case 0x28: if (((packet_len-52) % 25) == 0) return ((IPP2P_EDK * 100) + 67);
|
||||||
|
+ break;
|
||||||
|
+ /* e4 50 xx xx | size == 4 */
|
||||||
|
+ case 0x50: if (packet_len == 12) return ((IPP2P_EDK * 100) + 68);
|
||||||
|
+ break;
|
||||||
|
+ /* e4 40 xx xx | size == 48 */
|
||||||
|
+ case 0x40: if (packet_len == 56) return ((IPP2P_EDK * 100) + 69);
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ } /* end of switch (t[0]) */
|
||||||
|
+ return 0;
|
||||||
|
+}/*udp_search_edk*/
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+/*Search for UDP Gnutella commands*/
|
||||||
|
+int
|
||||||
|
+udp_search_gnu (unsigned char *haystack, int packet_len)
|
||||||
|
+{
|
||||||
|
+ unsigned char *t = haystack;
|
||||||
|
+ t += 8;
|
||||||
|
+
|
||||||
|
+ if (memcmp(t, "GND", 3) == 0) return ((IPP2P_GNU * 100) + 51);
|
||||||
|
+ if (memcmp(t, "GNUTELLA ", 9) == 0) return ((IPP2P_GNU * 100) + 52);
|
||||||
|
+ return 0;
|
||||||
|
+}/*udp_search_gnu*/
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+/*Search for UDP KaZaA commands*/
|
||||||
|
+int
|
||||||
|
+udp_search_kazaa (unsigned char *haystack, int packet_len)
|
||||||
|
+{
|
||||||
|
+ unsigned char *t = haystack;
|
||||||
|
+
|
||||||
|
+ if (t[packet_len-1] == 0x00){
|
||||||
|
+ t += (packet_len - 6);
|
||||||
|
+ if (memcmp(t, "KaZaA", 5) == 0) return (IPP2P_KAZAA * 100 +50);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}/*udp_search_kazaa*/
|
||||||
|
+
|
||||||
|
+/*Search for UDP DirectConnect commands*/
|
||||||
|
+int
|
||||||
|
+udp_search_directconnect (unsigned char *haystack, int packet_len)
|
||||||
|
+{
|
||||||
|
+ unsigned char *t = haystack;
|
||||||
|
+ if ((*(t + 8) == 0x24) && (*(t + packet_len - 1) == 0x7c)) {
|
||||||
|
+ t+=8;
|
||||||
|
+ if (memcmp(t, "SR ", 3) == 0) return ((IPP2P_DC * 100) + 60);
|
||||||
|
+ if (memcmp(t, "Ping ", 5) == 0) return ((IPP2P_DC * 100) + 61);
|
||||||
|
+ }
|
||||||
|
+ return 0;
|
||||||
|
+}/*udp_search_directconnect*/
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+/*Search for UDP BitTorrent commands*/
|
||||||
|
+int
|
||||||
|
+udp_search_bit (unsigned char *haystack, int packet_len)
|
||||||
|
+{
|
||||||
|
+ switch(packet_len)
|
||||||
|
+ {
|
||||||
|
+ case 24:
|
||||||
|
+ /* ^ 00 00 04 17 27 10 19 80 */
|
||||||
|
+ if ((ntohl(get_u32(haystack, 8)) == 0x00000417) && (ntohl(get_u32(haystack, 12)) == 0x27101980))
|
||||||
|
+ return (IPP2P_BIT * 100 + 50);
|
||||||
|
+ break;
|
||||||
|
+ case 44:
|
||||||
|
+ if (get_u32(haystack, 16) == __constant_htonl(0x00000400) && get_u32(haystack, 36) == __constant_htonl(0x00000104))
|
||||||
|
+ return (IPP2P_BIT * 100 + 51);
|
||||||
|
+ if (get_u32(haystack, 16) == __constant_htonl(0x00000400))
|
||||||
|
+ return (IPP2P_BIT * 100 + 61);
|
||||||
|
+ break;
|
||||||
|
+ case 65:
|
||||||
|
+ if (get_u32(haystack, 16) == __constant_htonl(0x00000404) && get_u32(haystack, 36) == __constant_htonl(0x00000104))
|
||||||
|
+ return (IPP2P_BIT * 100 + 52);
|
||||||
|
+ if (get_u32(haystack, 16) == __constant_htonl(0x00000404))
|
||||||
|
+ return (IPP2P_BIT * 100 + 62);
|
||||||
|
+ break;
|
||||||
|
+ case 67:
|
||||||
|
+ if (get_u32(haystack, 16) == __constant_htonl(0x00000406) && get_u32(haystack, 36) == __constant_htonl(0x00000104))
|
||||||
|
+ return (IPP2P_BIT * 100 + 53);
|
||||||
|
+ if (get_u32(haystack, 16) == __constant_htonl(0x00000406))
|
||||||
|
+ return (IPP2P_BIT * 100 + 63);
|
||||||
|
+ break;
|
||||||
|
+ case 211:
|
||||||
|
+ if (get_u32(haystack, 8) == __constant_htonl(0x00000405))
|
||||||
|
+ return (IPP2P_BIT * 100 + 54);
|
||||||
|
+ break;
|
||||||
|
+ case 29:
|
||||||
|
+ if ((get_u32(haystack, 8) == __constant_htonl(0x00000401)))
|
||||||
|
+ return (IPP2P_BIT * 100 + 55);
|
||||||
|
+ break;
|
||||||
|
+ case 52:
|
||||||
|
+ if (get_u32(haystack,8) == __constant_htonl(0x00000827) &&
|
||||||
|
+ get_u32(haystack,12) == __constant_htonl(0x37502950))
|
||||||
|
+ return (IPP2P_BIT * 100 + 80);
|
||||||
|
+ break;
|
||||||
|
+ default:
|
||||||
|
+ /* this packet does not have a constant size */
|
||||||
|
+ if (packet_len >= 40 && get_u32(haystack, 16) == __constant_htonl(0x00000402) && get_u32(haystack, 36) == __constant_htonl(0x00000104))
|
||||||
|
+ return (IPP2P_BIT * 100 + 56);
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* some extra-bitcomet rules:
|
||||||
|
+ * "d1:" [a|r] "d2:id20:"
|
||||||
|
+ */
|
||||||
|
+ if (packet_len > 30 && get_u8(haystack, 8) == 'd' && get_u8(haystack, 9) == '1' && get_u8(haystack, 10) == ':' )
|
||||||
|
+ {
|
||||||
|
+ if (get_u8(haystack, 11) == 'a' || get_u8(haystack, 11) == 'r')
|
||||||
|
+ {
|
||||||
|
+ if (memcmp(haystack+12,"d2:id20:",8)==0)
|
||||||
|
+ return (IPP2P_BIT * 100 + 57);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+#if 0
|
||||||
|
+ /* bitlord rules */
|
||||||
|
+ /* packetlen must be bigger than 40 */
|
||||||
|
+ /* first 4 bytes are zero */
|
||||||
|
+ if (packet_len > 40 && get_u32(haystack, 8) == 0x00000000)
|
||||||
|
+ {
|
||||||
|
+ /* first rule: 00 00 00 00 01 00 00 xx xx xx xx 00 00 00 00*/
|
||||||
|
+ if (get_u32(haystack, 12) == 0x00000000 &&
|
||||||
|
+ get_u32(haystack, 16) == 0x00010000 &&
|
||||||
|
+ get_u32(haystack, 24) == 0x00000000 )
|
||||||
|
+ return (IPP2P_BIT * 100 + 71);
|
||||||
|
+
|
||||||
|
+ /* 00 01 00 00 0d 00 00 xx xx xx xx 00 00 00 00*/
|
||||||
|
+ if (get_u32(haystack, 12) == 0x00000001 &&
|
||||||
|
+ get_u32(haystack, 16) == 0x000d0000 &&
|
||||||
|
+ get_u32(haystack, 24) == 0x00000000 )
|
||||||
|
+ return (IPP2P_BIT * 100 + 71);
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+ }
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}/*udp_search_bit*/
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+/*Search for Ares commands*/
|
||||||
|
+//#define IPP2P_DEBUG_ARES
|
||||||
|
+int
|
||||||
|
+search_ares (const unsigned char *payload, const u16 plen)
|
||||||
|
+//int search_ares (unsigned char *haystack, int packet_len, int head_len)
|
||||||
|
+{
|
||||||
|
+// const unsigned char *t = haystack + head_len;
|
||||||
|
+
|
||||||
|
+ /* all ares packets start with */
|
||||||
|
+ if (payload[1] == 0 && (plen - payload[0]) == 3)
|
||||||
|
+ {
|
||||||
|
+ switch (payload[2])
|
||||||
|
+ {
|
||||||
|
+ case 0x5a:
|
||||||
|
+ /* ares connect */
|
||||||
|
+ if ( plen == 6 && payload[5] == 0x05 ) return ((IPP2P_ARES * 100) + 1);
|
||||||
|
+ break;
|
||||||
|
+ case 0x09:
|
||||||
|
+ /* ares search, min 3 chars --> 14 bytes
|
||||||
|
+ * lets define a search can be up to 30 chars --> max 34 bytes
|
||||||
|
+ */
|
||||||
|
+ if ( plen >= 14 && plen <= 34 ) return ((IPP2P_ARES * 100) + 1);
|
||||||
|
+ break;
|
||||||
|
+#ifdef IPP2P_DEBUG_ARES
|
||||||
|
+ default:
|
||||||
|
+ printk(KERN_DEBUG "Unknown Ares command %x recognized, len: %u \n", (unsigned int) payload[2],plen);
|
||||||
|
+#endif /* IPP2P_DEBUG_ARES */
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+#if 0
|
||||||
|
+ /* found connect packet: 03 00 5a 04 03 05 */
|
||||||
|
+ /* new version ares 1.8: 03 00 5a xx xx 05 */
|
||||||
|
+ if ((plen) == 6){ /* possible connect command*/
|
||||||
|
+ if ((payload[0] == 0x03) && (payload[1] == 0x00) && (payload[2] == 0x5a) && (payload[5] == 0x05))
|
||||||
|
+ return ((IPP2P_ARES * 100) + 1);
|
||||||
|
+ }
|
||||||
|
+ if ((plen) == 60){ /* possible download command*/
|
||||||
|
+ if ((payload[59] == 0x0a) && (payload[58] == 0x0a)){
|
||||||
|
+ if (memcmp(t, "PUSH SHA1:", 10) == 0) /* found download command */
|
||||||
|
+ return ((IPP2P_ARES * 100) + 2);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+} /*search_ares*/
|
||||||
|
+
|
||||||
|
+/*Search for SoulSeek commands*/
|
||||||
|
+int
|
||||||
|
+search_soul (const unsigned char *payload, const u16 plen)
|
||||||
|
+{
|
||||||
|
+//#define IPP2P_DEBUG_SOUL
|
||||||
|
+ /* match: xx xx xx xx | xx = sizeof(payload) - 4 */
|
||||||
|
+ if (get_u32(payload, 0) == (plen - 4)){
|
||||||
|
+ const __u32 m=get_u32(payload, 4);
|
||||||
|
+ /* match 00 yy yy 00, yy can be everything */
|
||||||
|
+ if ( get_u8(payload, 4) == 0x00 && get_u8(payload, 7) == 0x00 )
|
||||||
|
+ {
|
||||||
|
+#ifdef IPP2P_DEBUG_SOUL
|
||||||
|
+ printk(KERN_DEBUG "0: Soulseek command 0x%x recognized\n",get_u32(payload, 4));
|
||||||
|
+#endif /* IPP2P_DEBUG_SOUL */
|
||||||
|
+ return ((IPP2P_SOUL * 100) + 1);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* next match: 01 yy 00 00 | yy can be everything */
|
||||||
|
+ if ( get_u8(payload, 4) == 0x01 && get_u16(payload, 6) == 0x0000 )
|
||||||
|
+ {
|
||||||
|
+#ifdef IPP2P_DEBUG_SOUL
|
||||||
|
+ printk(KERN_DEBUG "1: Soulseek command 0x%x recognized\n",get_u16(payload, 4));
|
||||||
|
+#endif /* IPP2P_DEBUG_SOUL */
|
||||||
|
+ return ((IPP2P_SOUL * 100) + 2);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* other soulseek commandos are: 1-5,7,9,13-18,22,23,26,28,35-37,40-46,50,51,60,62-69,91,92,1001 */
|
||||||
|
+ /* try to do this in an intelligent way */
|
||||||
|
+ /* get all small commandos */
|
||||||
|
+ switch(m)
|
||||||
|
+ {
|
||||||
|
+ case 7:
|
||||||
|
+ case 9:
|
||||||
|
+ case 22:
|
||||||
|
+ case 23:
|
||||||
|
+ case 26:
|
||||||
|
+ case 28:
|
||||||
|
+ case 50:
|
||||||
|
+ case 51:
|
||||||
|
+ case 60:
|
||||||
|
+ case 91:
|
||||||
|
+ case 92:
|
||||||
|
+ case 1001:
|
||||||
|
+#ifdef IPP2P_DEBUG_SOUL
|
||||||
|
+ printk(KERN_DEBUG "2: Soulseek command 0x%x recognized\n",get_u16(payload, 4));
|
||||||
|
+#endif /* IPP2P_DEBUG_SOUL */
|
||||||
|
+ return ((IPP2P_SOUL * 100) + 3);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (m > 0 && m < 6 )
|
||||||
|
+ {
|
||||||
|
+#ifdef IPP2P_DEBUG_SOUL
|
||||||
|
+ printk(KERN_DEBUG "3: Soulseek command 0x%x recognized\n",get_u16(payload, 4));
|
||||||
|
+#endif /* IPP2P_DEBUG_SOUL */
|
||||||
|
+ return ((IPP2P_SOUL * 100) + 4);
|
||||||
|
+ }
|
||||||
|
+ if (m > 12 && m < 19 )
|
||||||
|
+ {
|
||||||
|
+#ifdef IPP2P_DEBUG_SOUL
|
||||||
|
+ printk(KERN_DEBUG "4: Soulseek command 0x%x recognized\n",get_u16(payload, 4));
|
||||||
|
+#endif /* IPP2P_DEBUG_SOUL */
|
||||||
|
+ return ((IPP2P_SOUL * 100) + 5);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (m > 34 && m < 38 )
|
||||||
|
+ {
|
||||||
|
+#ifdef IPP2P_DEBUG_SOUL
|
||||||
|
+ printk(KERN_DEBUG "5: Soulseek command 0x%x recognized\n",get_u16(payload, 4));
|
||||||
|
+#endif /* IPP2P_DEBUG_SOUL */
|
||||||
|
+ return ((IPP2P_SOUL * 100) + 6);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (m > 39 && m < 47 )
|
||||||
|
+ {
|
||||||
|
+#ifdef IPP2P_DEBUG_SOUL
|
||||||
|
+ printk(KERN_DEBUG "6: Soulseek command 0x%x recognized\n",get_u16(payload, 4));
|
||||||
|
+#endif /* IPP2P_DEBUG_SOUL */
|
||||||
|
+ return ((IPP2P_SOUL * 100) + 7);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (m > 61 && m < 70 )
|
||||||
|
+ {
|
||||||
|
+#ifdef IPP2P_DEBUG_SOUL
|
||||||
|
+ printk(KERN_DEBUG "7: Soulseek command 0x%x recognized\n",get_u16(payload, 4));
|
||||||
|
+#endif /* IPP2P_DEBUG_SOUL */
|
||||||
|
+ return ((IPP2P_SOUL * 100) + 8);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+#ifdef IPP2P_DEBUG_SOUL
|
||||||
|
+ printk(KERN_DEBUG "unknown SOULSEEK command: 0x%x, first 16 bit: 0x%x, first 8 bit: 0x%x ,soulseek ???\n",get_u32(payload, 4),get_u16(payload, 4) >> 16,get_u8(payload, 4) >> 24);
|
||||||
|
+#endif /* IPP2P_DEBUG_SOUL */
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* match 14 00 00 00 01 yy 00 00 00 STRING(YY) 01 00 00 00 00 46|50 00 00 00 00 */
|
||||||
|
+ /* without size at the beginning !!! */
|
||||||
|
+ if ( get_u32(payload, 0) == 0x14 && get_u8(payload, 4) == 0x01 )
|
||||||
|
+ {
|
||||||
|
+ __u32 y=get_u32(payload, 5);
|
||||||
|
+ /* we need 19 chars + string */
|
||||||
|
+ if ( (y + 19) <= (plen) )
|
||||||
|
+ {
|
||||||
|
+ const unsigned char *w=payload+9+y;
|
||||||
|
+ if (get_u32(w, 0) == 0x01 && ( get_u16(w, 4) == 0x4600 || get_u16(w, 4) == 0x5000) && get_u32(w, 6) == 0x00);
|
||||||
|
+#ifdef IPP2P_DEBUG_SOUL
|
||||||
|
+ printk(KERN_DEBUG "Soulssek special client command recognized\n");
|
||||||
|
+#endif /* IPP2P_DEBUG_SOUL */
|
||||||
|
+ return ((IPP2P_SOUL * 100) + 9);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+/*Search for WinMX commands*/
|
||||||
|
+int
|
||||||
|
+search_winmx (const unsigned char *payload, const u16 plen)
|
||||||
|
+{
|
||||||
|
+//#define IPP2P_DEBUG_WINMX
|
||||||
|
+ if (((plen) == 4) && (memcmp(payload, "SEND", 4) == 0)) return ((IPP2P_WINMX * 100) + 1);
|
||||||
|
+ if (((plen) == 3) && (memcmp(payload, "GET", 3) == 0)) return ((IPP2P_WINMX * 100) + 2);
|
||||||
|
+ //if (packet_len < (head_len + 10)) return 0;
|
||||||
|
+ if (plen < 10) return 0;
|
||||||
|
+
|
||||||
|
+ if ((memcmp(payload, "SEND", 4) == 0) || (memcmp(payload, "GET", 3) == 0)){
|
||||||
|
+ u16 c=4;
|
||||||
|
+ const u16 end=plen-2;
|
||||||
|
+ u8 count=0;
|
||||||
|
+ while (c < end)
|
||||||
|
+ {
|
||||||
|
+ if (payload[c]== 0x20 && payload[c+1] == 0x22)
|
||||||
|
+ {
|
||||||
|
+ c++;
|
||||||
|
+ count++;
|
||||||
|
+ if (count>=2) return ((IPP2P_WINMX * 100) + 3);
|
||||||
|
+ }
|
||||||
|
+ c++;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if ( plen == 149 && payload[0] == '8' )
|
||||||
|
+ {
|
||||||
|
+#ifdef IPP2P_DEBUG_WINMX
|
||||||
|
+ printk(KERN_INFO "maybe WinMX\n");
|
||||||
|
+#endif
|
||||||
|
+ if (get_u32(payload,17) == 0 && get_u32(payload,21) == 0 && get_u32(payload,25) == 0 &&
|
||||||
|
+// get_u32(payload,33) == __constant_htonl(0x71182b1a) && get_u32(payload,37) == __constant_htonl(0x05050000) &&
|
||||||
|
+// get_u32(payload,133) == __constant_htonl(0x31097edf) && get_u32(payload,145) == __constant_htonl(0xdcb8f792))
|
||||||
|
+ get_u16(payload,39) == 0 && get_u16(payload,135) == __constant_htons(0x7edf) && get_u16(payload,147) == __constant_htons(0xf792))
|
||||||
|
+
|
||||||
|
+ {
|
||||||
|
+#ifdef IPP2P_DEBUG_WINMX
|
||||||
|
+ printk(KERN_INFO "got WinMX\n");
|
||||||
|
+#endif
|
||||||
|
+ return ((IPP2P_WINMX * 100) + 4);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ return 0;
|
||||||
|
+} /*search_winmx*/
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+/*Search for appleJuice commands*/
|
||||||
|
+int
|
||||||
|
+search_apple (const unsigned char *payload, const u16 plen)
|
||||||
|
+{
|
||||||
|
+ if ( (plen > 7) && (payload[6] == 0x0d) && (payload[7] == 0x0a) && (memcmp(payload, "ajprot", 6) == 0)) return (IPP2P_APPLE * 100);
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+/*Search for BitTorrent commands*/
|
||||||
|
+int
|
||||||
|
+search_bittorrent (const unsigned char *payload, const u16 plen)
|
||||||
|
+{
|
||||||
|
+ if (plen > 20)
|
||||||
|
+ {
|
||||||
|
+ /* test for match 0x13+"BitTorrent protocol" */
|
||||||
|
+ if (payload[0] == 0x13)
|
||||||
|
+ {
|
||||||
|
+ if (memcmp(payload+1, "BitTorrent protocol", 19) == 0) return (IPP2P_BIT * 100);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* get tracker commandos, all starts with GET /
|
||||||
|
+ * then it can follow: scrape| announce
|
||||||
|
+ * and then ?hash_info=
|
||||||
|
+ */
|
||||||
|
+ if (memcmp(payload,"GET /",5) == 0)
|
||||||
|
+ {
|
||||||
|
+ /* message scrape */
|
||||||
|
+ if ( memcmp(payload+5,"scrape?info_hash=",17)==0 ) return (IPP2P_BIT * 100 + 1);
|
||||||
|
+ /* message announce */
|
||||||
|
+ if ( memcmp(payload+5,"announce?info_hash=",19)==0 ) return (IPP2P_BIT * 100 + 2);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ else
|
||||||
|
+ {
|
||||||
|
+ /* bitcomet encryptes the first packet, so we have to detect another
|
||||||
|
+ * one later in the flow */
|
||||||
|
+ /* first try failed, too many missdetections */
|
||||||
|
+ //if ( size == 5 && get_u32(t,0) == __constant_htonl(1) && t[4] < 3) return (IPP2P_BIT * 100 + 3);
|
||||||
|
+
|
||||||
|
+ /* second try: block request packets */
|
||||||
|
+ if ( plen == 17 && get_u32(payload,0) == __constant_htonl(0x0d) && payload[4] == 0x06 && get_u32(payload,13) == __constant_htonl(0x4000) ) return (IPP2P_BIT * 100 + 3);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+/*check for Kazaa get command*/
|
||||||
|
+int
|
||||||
|
+search_kazaa (const unsigned char *payload, const u16 plen)
|
||||||
|
+
|
||||||
|
+{
|
||||||
|
+ if ((payload[plen-2] == 0x0d) && (payload[plen-1] == 0x0a) && memcmp(payload, "GET /.hash=", 11) == 0)
|
||||||
|
+ return (IPP2P_DATA_KAZAA * 100);
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+/*check for gnutella get command*/
|
||||||
|
+int
|
||||||
|
+search_gnu (const unsigned char *payload, const u16 plen)
|
||||||
|
+{
|
||||||
|
+ if ((payload[plen-2] == 0x0d) && (payload[plen-1] == 0x0a))
|
||||||
|
+ {
|
||||||
|
+ if (memcmp(payload, "GET /get/", 9) == 0) return ((IPP2P_DATA_GNU * 100) + 1);
|
||||||
|
+ if (memcmp(payload, "GET /uri-res/", 13) == 0) return ((IPP2P_DATA_GNU * 100) + 2);
|
||||||
|
+ }
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+/*check for gnutella get commands and other typical data*/
|
||||||
|
+int
|
||||||
|
+search_all_gnu (const unsigned char *payload, const u16 plen)
|
||||||
|
+{
|
||||||
|
+
|
||||||
|
+ if ((payload[plen-2] == 0x0d) && (payload[plen-1] == 0x0a))
|
||||||
|
+ {
|
||||||
|
+
|
||||||
|
+ if (memcmp(payload, "GNUTELLA CONNECT/", 17) == 0) return ((IPP2P_GNU * 100) + 1);
|
||||||
|
+ if (memcmp(payload, "GNUTELLA/", 9) == 0) return ((IPP2P_GNU * 100) + 2);
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+ if ((memcmp(payload, "GET /get/", 9) == 0) || (memcmp(payload, "GET /uri-res/", 13) == 0))
|
||||||
|
+ {
|
||||||
|
+ u16 c=8;
|
||||||
|
+ const u16 end=plen-22;
|
||||||
|
+ while (c < end) {
|
||||||
|
+ if ( payload[c] == 0x0a && payload[c+1] == 0x0d && ((memcmp(&payload[c+2], "X-Gnutella-", 11) == 0) || (memcmp(&payload[c+2], "X-Queue:", 8) == 0)))
|
||||||
|
+ return ((IPP2P_GNU * 100) + 3);
|
||||||
|
+ c++;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+/*check for KaZaA download commands and other typical data*/
|
||||||
|
+int
|
||||||
|
+search_all_kazaa (const unsigned char *payload, const u16 plen)
|
||||||
|
+{
|
||||||
|
+ if ((payload[plen-2] == 0x0d) && (payload[plen-1] == 0x0a))
|
||||||
|
+ {
|
||||||
|
+
|
||||||
|
+ if (memcmp(payload, "GIVE ", 5) == 0) return ((IPP2P_KAZAA * 100) + 1);
|
||||||
|
+
|
||||||
|
+ if (memcmp(payload, "GET /", 5) == 0) {
|
||||||
|
+ u16 c = 8;
|
||||||
|
+ const u16 end=plen-22;
|
||||||
|
+ while (c < end) {
|
||||||
|
+ if ( payload[c] == 0x0a && payload[c+1] == 0x0d && ((memcmp(&payload[c+2], "X-Kazaa-Username: ", 18) == 0) || (memcmp(&payload[c+2], "User-Agent: PeerEnabler/", 24) == 0)))
|
||||||
|
+ return ((IPP2P_KAZAA * 100) + 2);
|
||||||
|
+ c++;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/*fast check for edonkey file segment transfer command*/
|
||||||
|
+int
|
||||||
|
+search_edk (const unsigned char *payload, const u16 plen)
|
||||||
|
+{
|
||||||
|
+ if (payload[0] != 0xe3)
|
||||||
|
+ return 0;
|
||||||
|
+ else {
|
||||||
|
+ if (payload[5] == 0x47)
|
||||||
|
+ return (IPP2P_DATA_EDK * 100);
|
||||||
|
+ else
|
||||||
|
+ return 0;
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+/*intensive but slower search for some edonkey packets including size-check*/
|
||||||
|
+int
|
||||||
|
+search_all_edk (const unsigned char *payload, const u16 plen)
|
||||||
|
+{
|
||||||
|
+ if (payload[0] != 0xe3)
|
||||||
|
+ return 0;
|
||||||
|
+ else {
|
||||||
|
+ //t += head_len;
|
||||||
|
+ const u16 cmd = get_u16(payload, 1);
|
||||||
|
+ if (cmd == (plen - 5)) {
|
||||||
|
+ switch (payload[5]) {
|
||||||
|
+ case 0x01: return ((IPP2P_EDK * 100) + 1); /*Client: hello or Server:hello*/
|
||||||
|
+ case 0x4c: return ((IPP2P_EDK * 100) + 9); /*Client: Hello-Answer*/
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ return 0;
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+/*fast check for Direct Connect send command*/
|
||||||
|
+int
|
||||||
|
+search_dc (const unsigned char *payload, const u16 plen)
|
||||||
|
+{
|
||||||
|
+
|
||||||
|
+ if (payload[0] != 0x24 )
|
||||||
|
+ return 0;
|
||||||
|
+ else {
|
||||||
|
+ if (memcmp(&payload[1], "Send|", 5) == 0)
|
||||||
|
+ return (IPP2P_DATA_DC * 100);
|
||||||
|
+ else
|
||||||
|
+ return 0;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+/*intensive but slower check for all direct connect packets*/
|
||||||
|
+int
|
||||||
|
+search_all_dc (const unsigned char *payload, const u16 plen)
|
||||||
|
+{
|
||||||
|
+// unsigned char *t = haystack;
|
||||||
|
+
|
||||||
|
+ if (payload[0] == 0x24 && payload[plen-1] == 0x7c)
|
||||||
|
+ {
|
||||||
|
+ const unsigned char *t=&payload[1];
|
||||||
|
+ /* Client-Hub-Protocol */
|
||||||
|
+ if (memcmp(t, "Lock ", 5) == 0) return ((IPP2P_DC * 100) + 1);
|
||||||
|
+ /* Client-Client-Protocol, some are already recognized by client-hub (like lock) */
|
||||||
|
+ if (memcmp(t, "MyNick ", 7) == 0) return ((IPP2P_DC * 100) + 38);
|
||||||
|
+ }
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/*check for mute*/
|
||||||
|
+int
|
||||||
|
+search_mute (const unsigned char *payload, const u16 plen)
|
||||||
|
+{
|
||||||
|
+ if ( plen == 209 || plen == 345 || plen == 473 || plen == 609 || plen == 1121 )
|
||||||
|
+ {
|
||||||
|
+ //printk(KERN_DEBUG "size hit: %u",size);
|
||||||
|
+ if (memcmp(payload,"PublicKey: ",11) == 0 )
|
||||||
|
+ {
|
||||||
|
+ return ((IPP2P_MUTE * 100) + 0);
|
||||||
|
+
|
||||||
|
+/* if (memcmp(t+size-14,"\x0aEndPublicKey\x0a",14) == 0)
|
||||||
|
+ {
|
||||||
|
+ printk(KERN_DEBUG "end pubic key hit: %u",size);
|
||||||
|
+
|
||||||
|
+ }*/
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+/* check for xdcc */
|
||||||
|
+int
|
||||||
|
+search_xdcc (const unsigned char *payload, const u16 plen)
|
||||||
|
+{
|
||||||
|
+ /* search in small packets only */
|
||||||
|
+ if (plen > 20 && plen < 200 && payload[plen-1] == 0x0a && payload[plen-2] == 0x0d && memcmp(payload,"PRIVMSG ",8) == 0)
|
||||||
|
+ {
|
||||||
|
+
|
||||||
|
+ u16 x=10;
|
||||||
|
+ const u16 end=plen - 13;
|
||||||
|
+
|
||||||
|
+ /* is seems to be a irc private massage, chedck for xdcc command */
|
||||||
|
+ while (x < end)
|
||||||
|
+ {
|
||||||
|
+ if (payload[x] == ':')
|
||||||
|
+ {
|
||||||
|
+ if ( memcmp(&payload[x+1],"xdcc send #",11) == 0 )
|
||||||
|
+ return ((IPP2P_XDCC * 100) + 0);
|
||||||
|
+ }
|
||||||
|
+ x++;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/* search for waste */
|
||||||
|
+int search_waste(const unsigned char *payload, const u16 plen)
|
||||||
|
+{
|
||||||
|
+ if ( plen >= 8 && memcmp(payload,"GET.sha1:",9) == 0)
|
||||||
|
+ return ((IPP2P_WASTE * 100) + 0);
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+static struct {
|
||||||
|
+ int command;
|
||||||
|
+ __u8 short_hand; /*for fucntions included in short hands*/
|
||||||
|
+ int packet_len;
|
||||||
|
+ int (*function_name) (const unsigned char *, const u16);
|
||||||
|
+} matchlist[] = {
|
||||||
|
+ {IPP2P_EDK,SHORT_HAND_IPP2P,20, &search_all_edk},
|
||||||
|
+// {IPP2P_DATA_KAZAA,SHORT_HAND_DATA,200, &search_kazaa},
|
||||||
|
+// {IPP2P_DATA_EDK,SHORT_HAND_DATA,60, &search_edk},
|
||||||
|
+// {IPP2P_DATA_DC,SHORT_HAND_DATA,26, &search_dc},
|
||||||
|
+ {IPP2P_DC,SHORT_HAND_IPP2P,5, search_all_dc},
|
||||||
|
+// {IPP2P_DATA_GNU,SHORT_HAND_DATA,40, &search_gnu},
|
||||||
|
+ {IPP2P_GNU,SHORT_HAND_IPP2P,5, &search_all_gnu},
|
||||||
|
+ {IPP2P_KAZAA,SHORT_HAND_IPP2P,5, &search_all_kazaa},
|
||||||
|
+ {IPP2P_BIT,SHORT_HAND_IPP2P,20, &search_bittorrent},
|
||||||
|
+ {IPP2P_APPLE,SHORT_HAND_IPP2P,5, &search_apple},
|
||||||
|
+ {IPP2P_SOUL,SHORT_HAND_IPP2P,5, &search_soul},
|
||||||
|
+ {IPP2P_WINMX,SHORT_HAND_IPP2P,2, &search_winmx},
|
||||||
|
+ {IPP2P_ARES,SHORT_HAND_IPP2P,5, &search_ares},
|
||||||
|
+ {IPP2P_MUTE,SHORT_HAND_NONE,200, &search_mute},
|
||||||
|
+ {IPP2P_WASTE,SHORT_HAND_NONE,5, &search_waste},
|
||||||
|
+ {IPP2P_XDCC,SHORT_HAND_NONE,5, &search_xdcc},
|
||||||
|
+ {0,0,0,NULL}
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+static struct {
|
||||||
|
+ int command;
|
||||||
|
+ __u8 short_hand; /*for fucntions included in short hands*/
|
||||||
|
+ int packet_len;
|
||||||
|
+ int (*function_name) (unsigned char *, int);
|
||||||
|
+} udp_list[] = {
|
||||||
|
+ {IPP2P_KAZAA,SHORT_HAND_IPP2P,14, &udp_search_kazaa},
|
||||||
|
+ {IPP2P_BIT,SHORT_HAND_IPP2P,23, &udp_search_bit},
|
||||||
|
+ {IPP2P_GNU,SHORT_HAND_IPP2P,11, &udp_search_gnu},
|
||||||
|
+ {IPP2P_EDK,SHORT_HAND_IPP2P,9, &udp_search_edk},
|
||||||
|
+ {IPP2P_DC,SHORT_HAND_IPP2P,12, &udp_search_directconnect},
|
||||||
|
+ {0,0,0,NULL}
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+static bool
|
||||||
|
+match(const struct sk_buff *skb,
|
||||||
|
+ const struct net_device *in,
|
||||||
|
+ const struct net_device *out,
|
||||||
|
+ const struct xt_match *match,
|
||||||
|
+ const void *matchinfo,
|
||||||
|
+ int offset,
|
||||||
|
+ unsigned int protoff,
|
||||||
|
+ bool *hotdrop)
|
||||||
|
+{
|
||||||
|
+ const struct ipt_p2p_info *info = matchinfo;
|
||||||
|
+ unsigned char *haystack;
|
||||||
|
+ struct iphdr *ip = ip_hdr(skb);
|
||||||
|
+ int p2p_result = 0, i = 0;
|
||||||
|
+// int head_len;
|
||||||
|
+ int hlen = ntohs(ip->tot_len)-(ip->ihl*4); /*hlen = packet-data length*/
|
||||||
|
+
|
||||||
|
+ /*must not be a fragment*/
|
||||||
|
+ if (offset) {
|
||||||
|
+ if (info->debug) printk("IPP2P.match: offset found %i \n",offset);
|
||||||
|
+ return 0;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /*make sure that skb is linear*/
|
||||||
|
+ if(skb_is_nonlinear(skb)){
|
||||||
|
+ if (info->debug) printk("IPP2P.match: nonlinear skb found\n");
|
||||||
|
+ return 0;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+ haystack=(char *)ip+(ip->ihl*4); /*haystack = packet data*/
|
||||||
|
+
|
||||||
|
+ switch (ip->protocol){
|
||||||
|
+ case IPPROTO_TCP: /*what to do with a TCP packet*/
|
||||||
|
+ {
|
||||||
|
+ struct tcphdr *tcph = (void *) ip + ip->ihl * 4;
|
||||||
|
+
|
||||||
|
+ if (tcph->fin) return 0; /*if FIN bit is set bail out*/
|
||||||
|
+ if (tcph->syn) return 0; /*if SYN bit is set bail out*/
|
||||||
|
+ if (tcph->rst) return 0; /*if RST bit is set bail out*/
|
||||||
|
+
|
||||||
|
+ haystack += tcph->doff * 4; /*get TCP-Header-Size*/
|
||||||
|
+ hlen -= tcph->doff * 4;
|
||||||
|
+ while (matchlist[i].command) {
|
||||||
|
+ if ((((info->cmd & matchlist[i].command) == matchlist[i].command) ||
|
||||||
|
+ ((info->cmd & matchlist[i].short_hand) == matchlist[i].short_hand)) &&
|
||||||
|
+ (hlen > matchlist[i].packet_len)) {
|
||||||
|
+ p2p_result = matchlist[i].function_name(haystack, hlen);
|
||||||
|
+ if (p2p_result)
|
||||||
|
+ {
|
||||||
|
+ if (info->debug) printk("IPP2P.debug:TCP-match: %i from: %u.%u.%u.%u:%i to: %u.%u.%u.%u:%i Length: %i\n",
|
||||||
|
+ p2p_result, NIPQUAD(ip->saddr),ntohs(tcph->source), NIPQUAD(ip->daddr),ntohs(tcph->dest),hlen);
|
||||||
|
+ return p2p_result;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ i++;
|
||||||
|
+ }
|
||||||
|
+ return p2p_result;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ case IPPROTO_UDP: /*what to do with an UDP packet*/
|
||||||
|
+ {
|
||||||
|
+ struct udphdr *udph = (void *) ip + ip->ihl * 4;
|
||||||
|
+
|
||||||
|
+ while (udp_list[i].command){
|
||||||
|
+ if ((((info->cmd & udp_list[i].command) == udp_list[i].command) ||
|
||||||
|
+ ((info->cmd & udp_list[i].short_hand) == udp_list[i].short_hand)) &&
|
||||||
|
+ (hlen > udp_list[i].packet_len)) {
|
||||||
|
+ p2p_result = udp_list[i].function_name(haystack, hlen);
|
||||||
|
+ if (p2p_result){
|
||||||
|
+ if (info->debug) printk("IPP2P.debug:UDP-match: %i from: %u.%u.%u.%u:%i to: %u.%u.%u.%u:%i Length: %i\n",
|
||||||
|
+ p2p_result, NIPQUAD(ip->saddr),ntohs(udph->source), NIPQUAD(ip->daddr),ntohs(udph->dest),hlen);
|
||||||
|
+ return p2p_result;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ i++;
|
||||||
|
+ }
|
||||||
|
+ return p2p_result;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ default: return 0;
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+static bool
|
||||||
|
+checkentry(const char *tablename,
|
||||||
|
+ const void *ip,
|
||||||
|
+ const struct xt_match *match,
|
||||||
|
+ void *matchinfo,
|
||||||
|
+ unsigned int hook_mask)
|
||||||
|
+{
|
||||||
|
+ /* Must specify -p tcp */
|
||||||
|
+/* if (ip->proto != IPPROTO_TCP || (ip->invflags & IPT_INV_PROTO)) {
|
||||||
|
+ * printk("ipp2p: Only works on TCP packets, use -p tcp\n");
|
||||||
|
+ * return 0;
|
||||||
|
+ * }*/
|
||||||
|
+ return 1;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+static struct ipt_match ipp2p_match = {
|
||||||
|
+ .name = "ipp2p",
|
||||||
|
+ .match = &match,
|
||||||
|
+ .family = AF_INET,
|
||||||
|
+ .matchsize = sizeof(struct ipt_p2p_info),
|
||||||
|
+ .checkentry = &checkentry,
|
||||||
|
+ .me = THIS_MODULE,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+static int __init init(void)
|
||||||
|
+{
|
||||||
|
+ printk(KERN_INFO "IPP2P v%s loading\n", IPP2P_VERSION);
|
||||||
|
+ return xt_register_match(&ipp2p_match);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void __exit fini(void)
|
||||||
|
+{
|
||||||
|
+ xt_unregister_match(&ipp2p_match);
|
||||||
|
+ printk(KERN_INFO "IPP2P v%s unloaded\n", IPP2P_VERSION);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+module_init(init);
|
||||||
|
+module_exit(fini);
|
||||||
|
+
|
||||||
|
+
|
||||||
|
--- a/net/ipv4/netfilter/Kconfig
|
||||||
|
+++ b/net/ipv4/netfilter/Kconfig
|
||||||
|
@@ -57,6 +57,12 @@
|
||||||
|
To compile it as a module, choose M here. If unsure, say N.
|
||||||
|
|
||||||
|
# The matches.
|
||||||
|
+config IP_NF_MATCH_IPP2P
|
||||||
|
+ tristate "IPP2P"
|
||||||
|
+ depends on IP_NF_IPTABLES
|
||||||
|
+ help
|
||||||
|
+ Module for matching traffic of various Peer-to-Peer applications
|
||||||
|
+
|
||||||
|
config IP_NF_MATCH_RECENT
|
||||||
|
tristate '"recent" match support'
|
||||||
|
depends on IP_NF_IPTABLES
|
||||||
|
--- a/net/ipv4/netfilter/Makefile
|
||||||
|
+++ b/net/ipv4/netfilter/Makefile
|
||||||
|
@@ -51,6 +51,8 @@
|
||||||
|
obj-$(CONFIG_IP_NF_MATCH_RECENT) += ipt_recent.o
|
||||||
|
obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o
|
||||||
|
|
||||||
|
+obj-$(CONFIG_IP_NF_MATCH_IPP2P) += ipt_ipp2p.o
|
||||||
|
+
|
||||||
|
# targets
|
||||||
|
obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o
|
||||||
|
obj-$(CONFIG_IP_NF_TARGET_ECN) += ipt_ECN.o
|
7671
target/linux/generic-2.6/patches-2.6.27/130-netfilter_ipset.patch
Normal file
7671
target/linux/generic-2.6/patches-2.6.27/130-netfilter_ipset.patch
Normal file
File diff suppressed because it is too large
Load diff
239
target/linux/generic-2.6/patches-2.6.27/140-netfilter_time.patch
Normal file
239
target/linux/generic-2.6/patches-2.6.27/140-netfilter_time.patch
Normal file
|
@ -0,0 +1,239 @@
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/include/linux/netfilter_ipv4/ipt_time.h
|
||||||
|
@@ -0,0 +1,18 @@
|
||||||
|
+#ifndef __ipt_time_h_included__
|
||||||
|
+#define __ipt_time_h_included__
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+struct ipt_time_info {
|
||||||
|
+ u_int8_t days_match; /* 1 bit per day. -SMTWTFS */
|
||||||
|
+ u_int16_t time_start; /* 0 < time_start < 23*60+59 = 1439 */
|
||||||
|
+ u_int16_t time_stop; /* 0:0 < time_stat < 23:59 */
|
||||||
|
+
|
||||||
|
+ /* FIXME: Keep this one for userspace iptables binary compability: */
|
||||||
|
+ u_int8_t kerneltime; /* ignore skb time (and use kerneltime) or not. */
|
||||||
|
+
|
||||||
|
+ time_t date_start;
|
||||||
|
+ time_t date_stop;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+#endif /* __ipt_time_h_included__ */
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/net/ipv4/netfilter/ipt_time.c
|
||||||
|
@@ -0,0 +1,180 @@
|
||||||
|
+/*
|
||||||
|
+ This is a module which is used for time matching
|
||||||
|
+ It is using some modified code from dietlibc (localtime() function)
|
||||||
|
+ that you can find at http://www.fefe.de/dietlibc/
|
||||||
|
+ This file is distributed under the terms of the GNU General Public
|
||||||
|
+ License (GPL). Copies of the GPL can be obtained from: ftp://prep.ai.mit.edu/pub/gnu/GPL
|
||||||
|
+ 2001-05-04 Fabrice MARIE <fabrice@netfilter.org> : initial development.
|
||||||
|
+ 2001-21-05 Fabrice MARIE <fabrice@netfilter.org> : bug fix in the match code,
|
||||||
|
+ thanks to "Zeng Yu" <zengy@capitel.com.cn> for bug report.
|
||||||
|
+ 2001-26-09 Fabrice MARIE <fabrice@netfilter.org> : force the match to be in LOCAL_IN or PRE_ROUTING only.
|
||||||
|
+ 2001-30-11 Fabrice : added the possibility to use the match in FORWARD/OUTPUT with a little hack,
|
||||||
|
+ added Nguyen Dang Phuoc Dong <dongnd@tlnet.com.vn> patch to support timezones.
|
||||||
|
+ 2004-05-02 Fabrice : added support for date matching, from an idea of Fabien COELHO.
|
||||||
|
+*/
|
||||||
|
+
|
||||||
|
+#include <linux/module.h>
|
||||||
|
+#include <linux/skbuff.h>
|
||||||
|
+#include <linux/netfilter_ipv4/ip_tables.h>
|
||||||
|
+#include <linux/netfilter_ipv4/ipt_time.h>
|
||||||
|
+#include <linux/time.h>
|
||||||
|
+
|
||||||
|
+MODULE_AUTHOR("Fabrice MARIE <fabrice@netfilter.org>");
|
||||||
|
+MODULE_DESCRIPTION("Match arrival timestamp/date");
|
||||||
|
+MODULE_LICENSE("GPL");
|
||||||
|
+
|
||||||
|
+struct tm
|
||||||
|
+{
|
||||||
|
+ int tm_sec; /* Seconds. [0-60] (1 leap second) */
|
||||||
|
+ int tm_min; /* Minutes. [0-59] */
|
||||||
|
+ int tm_hour; /* Hours. [0-23] */
|
||||||
|
+ int tm_mday; /* Day. [1-31] */
|
||||||
|
+ int tm_mon; /* Month. [0-11] */
|
||||||
|
+ int tm_year; /* Year - 1900. */
|
||||||
|
+ int tm_wday; /* Day of week. [0-6] */
|
||||||
|
+ int tm_yday; /* Days in year.[0-365] */
|
||||||
|
+ int tm_isdst; /* DST. [-1/0/1]*/
|
||||||
|
+
|
||||||
|
+ long int tm_gmtoff; /* we don't care, we count from GMT */
|
||||||
|
+ const char *tm_zone; /* we don't care, we count from GMT */
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+void
|
||||||
|
+localtime(const u32 time, struct tm *r);
|
||||||
|
+
|
||||||
|
+static bool
|
||||||
|
+match(const struct sk_buff *skb,
|
||||||
|
+ const struct net_device *in,
|
||||||
|
+ const struct net_device *out,
|
||||||
|
+ const struct xt_match *match,
|
||||||
|
+ const void *matchinfo,
|
||||||
|
+ int offset,
|
||||||
|
+ unsigned int protoff,
|
||||||
|
+ bool *hotdrop)
|
||||||
|
+{
|
||||||
|
+ const struct ipt_time_info *info = matchinfo; /* match info for rule */
|
||||||
|
+ struct timeval tv;
|
||||||
|
+ struct tm currenttime; /* time human readable */
|
||||||
|
+ u_int8_t days_of_week[7] = {64, 32, 16, 8, 4, 2, 1};
|
||||||
|
+ u_int16_t packet_time;
|
||||||
|
+
|
||||||
|
+ /* We might not have a timestamp, get one */
|
||||||
|
+ if (skb->tstamp.tv64 == 0)
|
||||||
|
+ __net_timestamp((struct sk_buff *)skb);
|
||||||
|
+
|
||||||
|
+ skb_get_timestamp(skb, &tv);
|
||||||
|
+ /* First we make sure we are in the date start-stop boundaries */
|
||||||
|
+ if ((tv.tv_sec < info->date_start) || (tv.tv_sec > info->date_stop))
|
||||||
|
+ return 0; /* We are outside the date boundaries */
|
||||||
|
+
|
||||||
|
+ /* Transform the timestamp of the packet, in a human readable form */
|
||||||
|
+ localtime(tv.tv_sec, ¤ttime);
|
||||||
|
+
|
||||||
|
+ /* check if we match this timestamp, we start by the days... */
|
||||||
|
+ if ((days_of_week[currenttime.tm_wday] & info->days_match) != days_of_week[currenttime.tm_wday])
|
||||||
|
+ return 0; /* the day doesn't match */
|
||||||
|
+
|
||||||
|
+ /* ... check the time now */
|
||||||
|
+ packet_time = (currenttime.tm_hour * 60) + currenttime.tm_min;
|
||||||
|
+ if ((packet_time < info->time_start) || (packet_time > info->time_stop))
|
||||||
|
+ return 0;
|
||||||
|
+
|
||||||
|
+ /* here we match ! */
|
||||||
|
+ return 1;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static bool
|
||||||
|
+checkentry(const char *tablename,
|
||||||
|
+ const void *ip,
|
||||||
|
+ const struct xt_match *match,
|
||||||
|
+ void *matchinfo,
|
||||||
|
+ unsigned int hook_mask)
|
||||||
|
+{
|
||||||
|
+ struct ipt_time_info *info = matchinfo; /* match info for rule */
|
||||||
|
+
|
||||||
|
+ /* First, check that we are in the correct hooks */
|
||||||
|
+ if (hook_mask
|
||||||
|
+ & ~((1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_FORWARD) | (1 << NF_INET_LOCAL_OUT)))
|
||||||
|
+ {
|
||||||
|
+ printk("ipt_time: error, only valid for PRE_ROUTING, LOCAL_IN, FORWARD and OUTPUT)\n");
|
||||||
|
+ return 0;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* Now check the coherence of the data ... */
|
||||||
|
+ if ((info->time_start > 1439) || /* 23*60+59 = 1439*/
|
||||||
|
+ (info->time_stop > 1439))
|
||||||
|
+ {
|
||||||
|
+ printk(KERN_WARNING "ipt_time: invalid argument\n");
|
||||||
|
+ return 0;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return 1;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static struct ipt_match time_match = {
|
||||||
|
+ .name = "time",
|
||||||
|
+ .match = &match,
|
||||||
|
+ .matchsize = sizeof(struct ipt_time_info),
|
||||||
|
+ .checkentry = &checkentry,
|
||||||
|
+ .me = THIS_MODULE
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static int __init init(void)
|
||||||
|
+{
|
||||||
|
+ printk("ipt_time loading\n");
|
||||||
|
+ return xt_register_match(&time_match);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void __exit fini(void)
|
||||||
|
+{
|
||||||
|
+ xt_unregister_match(&time_match);
|
||||||
|
+ printk("ipt_time unloaded\n");
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+module_init(init);
|
||||||
|
+module_exit(fini);
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+/* The part below is borowed and modified from dietlibc */
|
||||||
|
+
|
||||||
|
+/* seconds per day */
|
||||||
|
+#define SPD 24*60*60
|
||||||
|
+
|
||||||
|
+void
|
||||||
|
+localtime(const u32 time, struct tm *r) {
|
||||||
|
+ u32 i, timep;
|
||||||
|
+ extern struct timezone sys_tz;
|
||||||
|
+ const unsigned int __spm[12] =
|
||||||
|
+ { 0,
|
||||||
|
+ (31),
|
||||||
|
+ (31+28),
|
||||||
|
+ (31+28+31),
|
||||||
|
+ (31+28+31+30),
|
||||||
|
+ (31+28+31+30+31),
|
||||||
|
+ (31+28+31+30+31+30),
|
||||||
|
+ (31+28+31+30+31+30+31),
|
||||||
|
+ (31+28+31+30+31+30+31+31),
|
||||||
|
+ (31+28+31+30+31+30+31+31+30),
|
||||||
|
+ (31+28+31+30+31+30+31+31+30+31),
|
||||||
|
+ (31+28+31+30+31+30+31+31+30+31+30),
|
||||||
|
+ };
|
||||||
|
+ register u32 work;
|
||||||
|
+
|
||||||
|
+ timep = time - (sys_tz.tz_minuteswest * 60);
|
||||||
|
+ work=timep%(SPD);
|
||||||
|
+ r->tm_sec=work%60; work/=60;
|
||||||
|
+ r->tm_min=work%60; r->tm_hour=work/60;
|
||||||
|
+ work=timep/(SPD);
|
||||||
|
+ r->tm_wday=(4+work)%7;
|
||||||
|
+ for (i=1970; ; ++i) {
|
||||||
|
+ register time_t k= (!(i%4) && ((i%100) || !(i%400)))?366:365;
|
||||||
|
+ if (work>k)
|
||||||
|
+ work-=k;
|
||||||
|
+ else
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ r->tm_year=i-1900;
|
||||||
|
+ for (i=11; i && __spm[i]>work; --i) ;
|
||||||
|
+ r->tm_mon=i;
|
||||||
|
+ r->tm_mday=work-__spm[i]+1;
|
||||||
|
+}
|
||||||
|
--- a/net/ipv4/netfilter/Kconfig
|
||||||
|
+++ b/net/ipv4/netfilter/Kconfig
|
||||||
|
@@ -63,6 +63,22 @@
|
||||||
|
help
|
||||||
|
Module for matching traffic of various Peer-to-Peer applications
|
||||||
|
|
||||||
|
+
|
||||||
|
+config IP_NF_MATCH_TIME
|
||||||
|
+ tristate 'TIME match support'
|
||||||
|
+ depends on IP_NF_IPTABLES
|
||||||
|
+ help
|
||||||
|
+ This option adds a `time' match, which allows you
|
||||||
|
+ to match based on the packet arrival time/date
|
||||||
|
+ (arrival time/date at the machine which netfilter is running on) or
|
||||||
|
+ departure time/date (for locally generated packets).
|
||||||
|
+
|
||||||
|
+ If you say Y here, try iptables -m time --help for more information.
|
||||||
|
+ If you want to compile it as a module, say M here and read
|
||||||
|
+
|
||||||
|
+ Documentation/modules.txt. If unsure, say `N'.
|
||||||
|
+
|
||||||
|
+
|
||||||
|
config IP_NF_MATCH_RECENT
|
||||||
|
tristate '"recent" match support'
|
||||||
|
depends on IP_NF_IPTABLES
|
||||||
|
--- a/net/ipv4/netfilter/Makefile
|
||||||
|
+++ b/net/ipv4/netfilter/Makefile
|
||||||
|
@@ -51,6 +51,7 @@
|
||||||
|
obj-$(CONFIG_IP_NF_MATCH_RECENT) += ipt_recent.o
|
||||||
|
obj-$(CONFIG_IP_NF_MATCH_TTL) += ipt_ttl.o
|
||||||
|
obj-$(CONFIG_IP_NF_MATCH_SET) += ipt_set.o
|
||||||
|
+obj-$(CONFIG_IP_NF_MATCH_TIME) += ipt_time.o
|
||||||
|
|
||||||
|
obj-$(CONFIG_IP_NF_MATCH_IPP2P) += ipt_ipp2p.o
|
||||||
|
|
914
target/linux/generic-2.6/patches-2.6.27/150-netfilter_imq.patch
Normal file
914
target/linux/generic-2.6/patches-2.6.27/150-netfilter_imq.patch
Normal file
|
@ -0,0 +1,914 @@
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/drivers/net/imq.c
|
||||||
|
@@ -0,0 +1,474 @@
|
||||||
|
+/*
|
||||||
|
+ * Pseudo-driver for the intermediate queue device.
|
||||||
|
+ *
|
||||||
|
+ * 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.
|
||||||
|
+ *
|
||||||
|
+ * Authors: Patrick McHardy, <kaber@trash.net>
|
||||||
|
+ *
|
||||||
|
+ * The first version was written by Martin Devera, <devik@cdi.cz>
|
||||||
|
+ *
|
||||||
|
+ * Credits: Jan Rafaj <imq2t@cedric.vabo.cz>
|
||||||
|
+ * - Update patch to 2.4.21
|
||||||
|
+ * Sebastian Strollo <sstrollo@nortelnetworks.com>
|
||||||
|
+ * - Fix "Dead-loop on netdevice imq"-issue
|
||||||
|
+ * Marcel Sebek <sebek64@post.cz>
|
||||||
|
+ * - Update to 2.6.2-rc1
|
||||||
|
+ *
|
||||||
|
+ * After some time of inactivity there is a group taking care
|
||||||
|
+ * of IMQ again: http://www.linuximq.net
|
||||||
|
+ *
|
||||||
|
+ *
|
||||||
|
+ * 2004/06/30 - New version of IMQ patch to kernels <=2.6.7
|
||||||
|
+ * including the following changes:
|
||||||
|
+ *
|
||||||
|
+ * - Correction of ipv6 support "+"s issue (Hasso Tepper)
|
||||||
|
+ * - Correction of imq_init_devs() issue that resulted in
|
||||||
|
+ * kernel OOPS unloading IMQ as module (Norbert Buchmuller)
|
||||||
|
+ * - Addition of functionality to choose number of IMQ devices
|
||||||
|
+ * during kernel config (Andre Correa)
|
||||||
|
+ * - Addition of functionality to choose how IMQ hooks on
|
||||||
|
+ * PRE and POSTROUTING (after or before NAT) (Andre Correa)
|
||||||
|
+ * - Cosmetic corrections (Norbert Buchmuller) (Andre Correa)
|
||||||
|
+ *
|
||||||
|
+ *
|
||||||
|
+ * 2005/12/16 - IMQ versions between 2.6.7 and 2.6.13 were
|
||||||
|
+ * released with almost no problems. 2.6.14-x was released
|
||||||
|
+ * with some important changes: nfcache was removed; After
|
||||||
|
+ * some weeks of trouble we figured out that some IMQ fields
|
||||||
|
+ * in skb were missing in skbuff.c - skb_clone and copy_skb_header.
|
||||||
|
+ * These functions are correctly patched by this new patch version.
|
||||||
|
+ *
|
||||||
|
+ * Thanks for all who helped to figure out all the problems with
|
||||||
|
+ * 2.6.14.x: Patrick McHardy, Rune Kock, VeNoMouS, Max CtRiX,
|
||||||
|
+ * Kevin Shanahan, Richard Lucassen, Valery Dachev (hopefully
|
||||||
|
+ * I didn't forget anybody). I apologize again for my lack of time.
|
||||||
|
+ *
|
||||||
|
+ *
|
||||||
|
+ * 2008/06/17 - 2.6.25 - Changed imq.c to use qdisc_run() instead
|
||||||
|
+ * of qdisc_restart() and moved qdisc_run() to tasklet to avoid
|
||||||
|
+ * recursive locking. New initialization routines to fix 'rmmod' not
|
||||||
|
+ * working anymore. Used code from ifb.c. (Jussi Kivilinna)
|
||||||
|
+ *
|
||||||
|
+ * Also, many thanks to pablo Sebastian Greco for making the initial
|
||||||
|
+ * patch and to those who helped the testing.
|
||||||
|
+ *
|
||||||
|
+ * More info at: http://www.linuximq.net/ (Andre Correa)
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+#include <linux/module.h>
|
||||||
|
+#include <linux/kernel.h>
|
||||||
|
+#include <linux/moduleparam.h>
|
||||||
|
+#include <linux/skbuff.h>
|
||||||
|
+#include <linux/netdevice.h>
|
||||||
|
+#include <linux/rtnetlink.h>
|
||||||
|
+#include <linux/if_arp.h>
|
||||||
|
+#include <linux/netfilter.h>
|
||||||
|
+#include <linux/netfilter_ipv4.h>
|
||||||
|
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
||||||
|
+ #include <linux/netfilter_ipv6.h>
|
||||||
|
+#endif
|
||||||
|
+#include <linux/imq.h>
|
||||||
|
+#include <net/pkt_sched.h>
|
||||||
|
+#include <net/netfilter/nf_queue.h>
|
||||||
|
+
|
||||||
|
+struct imq_private {
|
||||||
|
+ struct tasklet_struct tasklet;
|
||||||
|
+ unsigned long tasklet_pending;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static nf_hookfn imq_nf_hook;
|
||||||
|
+
|
||||||
|
+static struct nf_hook_ops imq_ingress_ipv4 = {
|
||||||
|
+ .hook = imq_nf_hook,
|
||||||
|
+ .owner = THIS_MODULE,
|
||||||
|
+ .pf = PF_INET,
|
||||||
|
+ .hooknum = NF_INET_PRE_ROUTING,
|
||||||
|
+#if defined(CONFIG_IMQ_BEHAVIOR_BA) || defined(CONFIG_IMQ_BEHAVIOR_BB)
|
||||||
|
+ .priority = NF_IP_PRI_MANGLE + 1
|
||||||
|
+#else
|
||||||
|
+ .priority = NF_IP_PRI_NAT_DST + 1
|
||||||
|
+#endif
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static struct nf_hook_ops imq_egress_ipv4 = {
|
||||||
|
+ .hook = imq_nf_hook,
|
||||||
|
+ .owner = THIS_MODULE,
|
||||||
|
+ .pf = PF_INET,
|
||||||
|
+ .hooknum = NF_INET_POST_ROUTING,
|
||||||
|
+#if defined(CONFIG_IMQ_BEHAVIOR_AA) || defined(CONFIG_IMQ_BEHAVIOR_BA)
|
||||||
|
+ .priority = NF_IP_PRI_LAST
|
||||||
|
+#else
|
||||||
|
+ .priority = NF_IP_PRI_NAT_SRC - 1
|
||||||
|
+#endif
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
||||||
|
+static struct nf_hook_ops imq_ingress_ipv6 = {
|
||||||
|
+ .hook = imq_nf_hook,
|
||||||
|
+ .owner = THIS_MODULE,
|
||||||
|
+ .pf = PF_INET6,
|
||||||
|
+ .hooknum = NF_INET_PRE_ROUTING,
|
||||||
|
+#if defined(CONFIG_IMQ_BEHAVIOR_BA) || defined(CONFIG_IMQ_BEHAVIOR_BB)
|
||||||
|
+ .priority = NF_IP6_PRI_MANGLE + 1
|
||||||
|
+#else
|
||||||
|
+ .priority = NF_IP6_PRI_NAT_DST + 1
|
||||||
|
+#endif
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static struct nf_hook_ops imq_egress_ipv6 = {
|
||||||
|
+ .hook = imq_nf_hook,
|
||||||
|
+ .owner = THIS_MODULE,
|
||||||
|
+ .pf = PF_INET6,
|
||||||
|
+ .hooknum = NF_INET_POST_ROUTING,
|
||||||
|
+#if defined(CONFIG_IMQ_BEHAVIOR_AA) || defined(CONFIG_IMQ_BEHAVIOR_BA)
|
||||||
|
+ .priority = NF_IP6_PRI_LAST
|
||||||
|
+#else
|
||||||
|
+ .priority = NF_IP6_PRI_NAT_SRC - 1
|
||||||
|
+#endif
|
||||||
|
+};
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+#if defined(CONFIG_IMQ_NUM_DEVS)
|
||||||
|
+static unsigned int numdevs = CONFIG_IMQ_NUM_DEVS;
|
||||||
|
+#else
|
||||||
|
+static unsigned int numdevs = IMQ_MAX_DEVS;
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+static struct net_device *imq_devs_cache[IMQ_MAX_DEVS];
|
||||||
|
+
|
||||||
|
+static struct net_device_stats *imq_get_stats(struct net_device *dev)
|
||||||
|
+{
|
||||||
|
+ return &dev->stats;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/* called for packets kfree'd in qdiscs at places other than enqueue */
|
||||||
|
+static void imq_skb_destructor(struct sk_buff *skb)
|
||||||
|
+{
|
||||||
|
+ struct nf_queue_entry *entry = skb->nf_queue_entry;
|
||||||
|
+
|
||||||
|
+ if (entry) {
|
||||||
|
+ if (entry->indev)
|
||||||
|
+ dev_put(entry->indev);
|
||||||
|
+ if (entry->outdev)
|
||||||
|
+ dev_put(entry->outdev);
|
||||||
|
+ kfree(entry);
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int imq_dev_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||||
|
+{
|
||||||
|
+ dev->stats.tx_bytes += skb->len;
|
||||||
|
+ dev->stats.tx_packets++;
|
||||||
|
+
|
||||||
|
+ skb->imq_flags = 0;
|
||||||
|
+ skb->destructor = NULL;
|
||||||
|
+
|
||||||
|
+ dev->trans_start = jiffies;
|
||||||
|
+ nf_reinject(skb->nf_queue_entry, NF_ACCEPT);
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int imq_nf_queue(struct nf_queue_entry *entry, unsigned queue_num)
|
||||||
|
+{
|
||||||
|
+ struct net_device *dev;
|
||||||
|
+ struct imq_private *priv;
|
||||||
|
+ struct sk_buff *skb2 = NULL;
|
||||||
|
+ struct Qdisc *q;
|
||||||
|
+ unsigned int index = entry->skb->imq_flags & IMQ_F_IFMASK;
|
||||||
|
+ int ret = -1;
|
||||||
|
+
|
||||||
|
+ if (index > numdevs)
|
||||||
|
+ return -1;
|
||||||
|
+
|
||||||
|
+ /* check for imq device by index from cache */
|
||||||
|
+ dev = imq_devs_cache[index];
|
||||||
|
+ if (!dev) {
|
||||||
|
+ char buf[8];
|
||||||
|
+
|
||||||
|
+ /* get device by name and cache result */
|
||||||
|
+ snprintf(buf, sizeof(buf), "imq%d", index);
|
||||||
|
+ dev = dev_get_by_name(&init_net, buf);
|
||||||
|
+ if (!dev) {
|
||||||
|
+ /* not found ?!*/
|
||||||
|
+ BUG();
|
||||||
|
+ return -1;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ imq_devs_cache[index] = dev;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ priv = netdev_priv(dev);
|
||||||
|
+ if (!(dev->flags & IFF_UP)) {
|
||||||
|
+ entry->skb->imq_flags = 0;
|
||||||
|
+ nf_reinject(entry, NF_ACCEPT);
|
||||||
|
+ return 0;
|
||||||
|
+ }
|
||||||
|
+ dev->last_rx = jiffies;
|
||||||
|
+
|
||||||
|
+ if (entry->skb->destructor) {
|
||||||
|
+ skb2 = entry->skb;
|
||||||
|
+ entry->skb = skb_clone(entry->skb, GFP_ATOMIC);
|
||||||
|
+ if (!entry->skb)
|
||||||
|
+ return -1;
|
||||||
|
+ }
|
||||||
|
+ entry->skb->nf_queue_entry = entry;
|
||||||
|
+
|
||||||
|
+ dev->stats.rx_bytes += entry->skb->len;
|
||||||
|
+ dev->stats.rx_packets++;
|
||||||
|
+
|
||||||
|
+ spin_lock_bh(&dev->queue_lock);
|
||||||
|
+ q = dev->qdisc;
|
||||||
|
+ if (q->enqueue) {
|
||||||
|
+ q->enqueue(skb_get(entry->skb), q);
|
||||||
|
+ if (skb_shared(entry->skb)) {
|
||||||
|
+ entry->skb->destructor = imq_skb_destructor;
|
||||||
|
+ kfree_skb(entry->skb);
|
||||||
|
+ ret = 0;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ if (!test_and_set_bit(1, &priv->tasklet_pending))
|
||||||
|
+ tasklet_schedule(&priv->tasklet);
|
||||||
|
+ spin_unlock_bh(&dev->queue_lock);
|
||||||
|
+
|
||||||
|
+ if (skb2)
|
||||||
|
+ kfree_skb(ret ? entry->skb : skb2);
|
||||||
|
+
|
||||||
|
+ return ret;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static struct nf_queue_handler nfqh = {
|
||||||
|
+ .name = "imq",
|
||||||
|
+ .outfn = imq_nf_queue,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static void qdisc_run_tasklet(unsigned long arg)
|
||||||
|
+{
|
||||||
|
+ struct net_device *dev = (struct net_device *)arg;
|
||||||
|
+ struct imq_private *priv = netdev_priv(dev);
|
||||||
|
+
|
||||||
|
+ spin_lock(&dev->queue_lock);
|
||||||
|
+ qdisc_run(dev);
|
||||||
|
+ clear_bit(1, &priv->tasklet_pending);
|
||||||
|
+ spin_unlock(&dev->queue_lock);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static unsigned int imq_nf_hook(unsigned int hook, struct sk_buff *pskb,
|
||||||
|
+ const struct net_device *indev,
|
||||||
|
+ const struct net_device *outdev,
|
||||||
|
+ int (*okfn)(struct sk_buff *))
|
||||||
|
+{
|
||||||
|
+ if (pskb->imq_flags & IMQ_F_ENQUEUE)
|
||||||
|
+ return NF_QUEUE;
|
||||||
|
+
|
||||||
|
+ return NF_ACCEPT;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int imq_close(struct net_device *dev)
|
||||||
|
+{
|
||||||
|
+ struct imq_private *priv = netdev_priv(dev);
|
||||||
|
+
|
||||||
|
+ tasklet_kill(&priv->tasklet);
|
||||||
|
+ netif_stop_queue(dev);
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int imq_open(struct net_device *dev)
|
||||||
|
+{
|
||||||
|
+ struct imq_private *priv = netdev_priv(dev);
|
||||||
|
+
|
||||||
|
+ tasklet_init(&priv->tasklet, qdisc_run_tasklet, (unsigned long)dev);
|
||||||
|
+ netif_start_queue(dev);
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void imq_setup(struct net_device *dev)
|
||||||
|
+{
|
||||||
|
+ dev->hard_start_xmit = imq_dev_xmit;
|
||||||
|
+ dev->open = imq_open;
|
||||||
|
+ dev->get_stats = imq_get_stats;
|
||||||
|
+ dev->stop = imq_close;
|
||||||
|
+ dev->type = ARPHRD_VOID;
|
||||||
|
+ dev->mtu = 16000;
|
||||||
|
+ dev->tx_queue_len = 11000;
|
||||||
|
+ dev->flags = IFF_NOARP;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static struct rtnl_link_ops imq_link_ops __read_mostly = {
|
||||||
|
+ .kind = "imq",
|
||||||
|
+ .priv_size = sizeof(struct imq_private),
|
||||||
|
+ .setup = imq_setup,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static int __init imq_init_hooks(void)
|
||||||
|
+{
|
||||||
|
+ int err;
|
||||||
|
+
|
||||||
|
+ err = nf_register_queue_handler(PF_INET, &nfqh);
|
||||||
|
+ if (err)
|
||||||
|
+ goto err1;
|
||||||
|
+
|
||||||
|
+ err = nf_register_hook(&imq_ingress_ipv4);
|
||||||
|
+ if (err)
|
||||||
|
+ goto err2;
|
||||||
|
+
|
||||||
|
+ err = nf_register_hook(&imq_egress_ipv4);
|
||||||
|
+ if (err)
|
||||||
|
+ goto err3;
|
||||||
|
+
|
||||||
|
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
||||||
|
+ err = nf_register_queue_handler(PF_INET6, &nfqh);
|
||||||
|
+ if (err)
|
||||||
|
+ goto err4;
|
||||||
|
+
|
||||||
|
+ err = nf_register_hook(&imq_ingress_ipv6);
|
||||||
|
+ if (err)
|
||||||
|
+ goto err5;
|
||||||
|
+
|
||||||
|
+ err = nf_register_hook(&imq_egress_ipv6);
|
||||||
|
+ if (err)
|
||||||
|
+ goto err6;
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+
|
||||||
|
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
||||||
|
+err6:
|
||||||
|
+ nf_unregister_hook(&imq_ingress_ipv6);
|
||||||
|
+err5:
|
||||||
|
+ nf_unregister_queue_handler(PF_INET6, &nfqh);
|
||||||
|
+err4:
|
||||||
|
+ nf_unregister_hook(&imq_egress_ipv4);
|
||||||
|
+#endif
|
||||||
|
+err3:
|
||||||
|
+ nf_unregister_hook(&imq_ingress_ipv4);
|
||||||
|
+err2:
|
||||||
|
+ nf_unregister_queue_handler(PF_INET, &nfqh);
|
||||||
|
+err1:
|
||||||
|
+ return err;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int __init imq_init_one(int index)
|
||||||
|
+{
|
||||||
|
+ struct net_device *dev;
|
||||||
|
+ int ret;
|
||||||
|
+
|
||||||
|
+ dev = alloc_netdev(sizeof(struct imq_private), "imq%d", imq_setup);
|
||||||
|
+ if (!dev)
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+
|
||||||
|
+ ret = dev_alloc_name(dev, dev->name);
|
||||||
|
+ if (ret < 0)
|
||||||
|
+ goto fail;
|
||||||
|
+
|
||||||
|
+ dev->rtnl_link_ops = &imq_link_ops;
|
||||||
|
+ ret = register_netdevice(dev);
|
||||||
|
+ if (ret < 0)
|
||||||
|
+ goto fail;
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+fail:
|
||||||
|
+ free_netdev(dev);
|
||||||
|
+ return ret;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int __init imq_init_devs(void)
|
||||||
|
+{
|
||||||
|
+ int err, i;
|
||||||
|
+
|
||||||
|
+ if (!numdevs || numdevs > IMQ_MAX_DEVS) {
|
||||||
|
+ printk(KERN_ERR "IMQ: numdevs has to be betweed 1 and %u\n",
|
||||||
|
+ IMQ_MAX_DEVS);
|
||||||
|
+ return -EINVAL;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ rtnl_lock();
|
||||||
|
+ err = __rtnl_link_register(&imq_link_ops);
|
||||||
|
+
|
||||||
|
+ for (i = 0; i < numdevs && !err; i++)
|
||||||
|
+ err = imq_init_one(i);
|
||||||
|
+
|
||||||
|
+ if (err) {
|
||||||
|
+ __rtnl_link_unregister(&imq_link_ops);
|
||||||
|
+ memset(imq_devs_cache, 0, sizeof(imq_devs_cache));
|
||||||
|
+ }
|
||||||
|
+ rtnl_unlock();
|
||||||
|
+
|
||||||
|
+ return err;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int __init imq_init_module(void)
|
||||||
|
+{
|
||||||
|
+ int err;
|
||||||
|
+
|
||||||
|
+ err = imq_init_devs();
|
||||||
|
+ if (err) {
|
||||||
|
+ printk(KERN_ERR "IMQ: Error trying imq_init_devs(net)\n");
|
||||||
|
+ return err;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ err = imq_init_hooks();
|
||||||
|
+ if (err) {
|
||||||
|
+ printk(KERN_ERR "IMQ: Error trying imq_init_hooks()\n");
|
||||||
|
+ rtnl_link_unregister(&imq_link_ops);
|
||||||
|
+ memset(imq_devs_cache, 0, sizeof(imq_devs_cache));
|
||||||
|
+ return err;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ printk(KERN_INFO "IMQ driver loaded successfully.\n");
|
||||||
|
+
|
||||||
|
+#if defined(CONFIG_IMQ_BEHAVIOR_BA) || defined(CONFIG_IMQ_BEHAVIOR_BB)
|
||||||
|
+ printk(KERN_INFO "\tHooking IMQ before NAT on PREROUTING.\n");
|
||||||
|
+#else
|
||||||
|
+ printk(KERN_INFO "\tHooking IMQ after NAT on PREROUTING.\n");
|
||||||
|
+#endif
|
||||||
|
+#if defined(CONFIG_IMQ_BEHAVIOR_AB) || defined(CONFIG_IMQ_BEHAVIOR_BB)
|
||||||
|
+ printk(KERN_INFO "\tHooking IMQ before NAT on POSTROUTING.\n");
|
||||||
|
+#else
|
||||||
|
+ printk(KERN_INFO "\tHooking IMQ after NAT on POSTROUTING.\n");
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void __exit imq_unhook(void)
|
||||||
|
+{
|
||||||
|
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
||||||
|
+ nf_unregister_hook(&imq_ingress_ipv6);
|
||||||
|
+ nf_unregister_hook(&imq_egress_ipv6);
|
||||||
|
+ nf_unregister_queue_handler(PF_INET6, &nfqh);
|
||||||
|
+#endif
|
||||||
|
+ nf_unregister_hook(&imq_ingress_ipv4);
|
||||||
|
+ nf_unregister_hook(&imq_egress_ipv4);
|
||||||
|
+ nf_unregister_queue_handler(PF_INET, &nfqh);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void __exit imq_cleanup_devs(void)
|
||||||
|
+{
|
||||||
|
+ rtnl_link_unregister(&imq_link_ops);
|
||||||
|
+ memset(imq_devs_cache, 0, sizeof(imq_devs_cache));
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void __exit imq_exit_module(void)
|
||||||
|
+{
|
||||||
|
+ imq_unhook();
|
||||||
|
+ imq_cleanup_devs();
|
||||||
|
+ printk(KERN_INFO "IMQ driver unloaded successfully.\n");
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+module_init(imq_init_module);
|
||||||
|
+module_exit(imq_exit_module);
|
||||||
|
+
|
||||||
|
+module_param(numdevs, int, 0);
|
||||||
|
+MODULE_PARM_DESC(numdevs, "number of IMQ devices (how many imq* devices will "
|
||||||
|
+ "be created)");
|
||||||
|
+MODULE_AUTHOR("http://www.linuximq.net");
|
||||||
|
+MODULE_DESCRIPTION("Pseudo-driver for the intermediate queue device. See "
|
||||||
|
+ "http://www.linuximq.net/ for more information.");
|
||||||
|
+MODULE_LICENSE("GPL");
|
||||||
|
+MODULE_ALIAS_RTNL_LINK("imq");
|
||||||
|
+
|
||||||
|
--- a/drivers/net/Kconfig
|
||||||
|
+++ b/drivers/net/Kconfig
|
||||||
|
@@ -109,6 +109,129 @@
|
||||||
|
To compile this driver as a module, choose M here: the module
|
||||||
|
will be called eql. If unsure, say N.
|
||||||
|
|
||||||
|
+config IMQ
|
||||||
|
+ tristate "IMQ (intermediate queueing device) support"
|
||||||
|
+ depends on NETDEVICES && NETFILTER
|
||||||
|
+ ---help---
|
||||||
|
+ The IMQ device(s) is used as placeholder for QoS queueing
|
||||||
|
+ disciplines. Every packet entering/leaving the IP stack can be
|
||||||
|
+ directed through the IMQ device where it's enqueued/dequeued to the
|
||||||
|
+ attached qdisc. This allows you to treat network devices as classes
|
||||||
|
+ and distribute bandwidth among them. Iptables is used to specify
|
||||||
|
+ through which IMQ device, if any, packets travel.
|
||||||
|
+
|
||||||
|
+ More information at: http://www.linuximq.net/
|
||||||
|
+
|
||||||
|
+ To compile this driver as a module, choose M here: the module
|
||||||
|
+ will be called imq. If unsure, say N.
|
||||||
|
+
|
||||||
|
+choice
|
||||||
|
+ prompt "IMQ behavior (PRE/POSTROUTING)"
|
||||||
|
+ depends on IMQ
|
||||||
|
+ default IMQ_BEHAVIOR_AB
|
||||||
|
+ help
|
||||||
|
+
|
||||||
|
+ This settings defines how IMQ behaves in respect to its
|
||||||
|
+ hooking in PREROUTING and POSTROUTING.
|
||||||
|
+
|
||||||
|
+ IMQ can work in any of the following ways:
|
||||||
|
+
|
||||||
|
+ PREROUTING | POSTROUTING
|
||||||
|
+ -----------------|-------------------
|
||||||
|
+ #1 After NAT | After NAT
|
||||||
|
+ #2 After NAT | Before NAT
|
||||||
|
+ #3 Before NAT | After NAT
|
||||||
|
+ #4 Before NAT | Before NAT
|
||||||
|
+
|
||||||
|
+ The default behavior is to hook before NAT on PREROUTING
|
||||||
|
+ and after NAT on POSTROUTING (#3).
|
||||||
|
+
|
||||||
|
+ This settings are specially usefull when trying to use IMQ
|
||||||
|
+ to shape NATed clients.
|
||||||
|
+
|
||||||
|
+ More information can be found at: www.linuximq.net
|
||||||
|
+
|
||||||
|
+ If not sure leave the default settings alone.
|
||||||
|
+
|
||||||
|
+config IMQ_BEHAVIOR_AA
|
||||||
|
+ bool "IMQ AA"
|
||||||
|
+ help
|
||||||
|
+ This settings defines how IMQ behaves in respect to its
|
||||||
|
+ hooking in PREROUTING and POSTROUTING.
|
||||||
|
+
|
||||||
|
+ Choosing this option will make IMQ hook like this:
|
||||||
|
+
|
||||||
|
+ PREROUTING: After NAT
|
||||||
|
+ POSTROUTING: After NAT
|
||||||
|
+
|
||||||
|
+ More information can be found at: www.linuximq.net
|
||||||
|
+
|
||||||
|
+ If not sure leave the default settings alone.
|
||||||
|
+
|
||||||
|
+config IMQ_BEHAVIOR_AB
|
||||||
|
+ bool "IMQ AB"
|
||||||
|
+ help
|
||||||
|
+ This settings defines how IMQ behaves in respect to its
|
||||||
|
+ hooking in PREROUTING and POSTROUTING.
|
||||||
|
+
|
||||||
|
+ Choosing this option will make IMQ hook like this:
|
||||||
|
+
|
||||||
|
+ PREROUTING: After NAT
|
||||||
|
+ POSTROUTING: Before NAT
|
||||||
|
+
|
||||||
|
+ More information can be found at: www.linuximq.net
|
||||||
|
+
|
||||||
|
+ If not sure leave the default settings alone.
|
||||||
|
+
|
||||||
|
+config IMQ_BEHAVIOR_BA
|
||||||
|
+ bool "IMQ BA"
|
||||||
|
+ help
|
||||||
|
+ This settings defines how IMQ behaves in respect to its
|
||||||
|
+ hooking in PREROUTING and POSTROUTING.
|
||||||
|
+
|
||||||
|
+ Choosing this option will make IMQ hook like this:
|
||||||
|
+
|
||||||
|
+ PREROUTING: Before NAT
|
||||||
|
+ POSTROUTING: After NAT
|
||||||
|
+
|
||||||
|
+ More information can be found at: www.linuximq.net
|
||||||
|
+
|
||||||
|
+ If not sure leave the default settings alone.
|
||||||
|
+
|
||||||
|
+config IMQ_BEHAVIOR_BB
|
||||||
|
+ bool "IMQ BB"
|
||||||
|
+ help
|
||||||
|
+ This settings defines how IMQ behaves in respect to its
|
||||||
|
+ hooking in PREROUTING and POSTROUTING.
|
||||||
|
+
|
||||||
|
+ Choosing this option will make IMQ hook like this:
|
||||||
|
+
|
||||||
|
+ PREROUTING: Before NAT
|
||||||
|
+ POSTROUTING: Before NAT
|
||||||
|
+
|
||||||
|
+ More information can be found at: www.linuximq.net
|
||||||
|
+
|
||||||
|
+ If not sure leave the default settings alone.
|
||||||
|
+
|
||||||
|
+endchoice
|
||||||
|
+
|
||||||
|
+config IMQ_NUM_DEVS
|
||||||
|
+
|
||||||
|
+ int "Number of IMQ devices"
|
||||||
|
+ range 2 16
|
||||||
|
+ depends on IMQ
|
||||||
|
+ default "16"
|
||||||
|
+ help
|
||||||
|
+
|
||||||
|
+ This settings defines how many IMQ devices will be
|
||||||
|
+ created.
|
||||||
|
+
|
||||||
|
+ The default value is 16.
|
||||||
|
+
|
||||||
|
+ More information can be found at: www.linuximq.net
|
||||||
|
+
|
||||||
|
+ If not sure leave the default settings alone.
|
||||||
|
+
|
||||||
|
config TUN
|
||||||
|
tristate "Universal TUN/TAP device driver support"
|
||||||
|
select CRC32
|
||||||
|
--- a/drivers/net/Makefile
|
||||||
|
+++ b/drivers/net/Makefile
|
||||||
|
@@ -144,6 +144,7 @@
|
||||||
|
obj-$(CONFIG_XEN_NETDEV_FRONTEND) += xen-netfront.o
|
||||||
|
|
||||||
|
obj-$(CONFIG_DUMMY) += dummy.o
|
||||||
|
+obj-$(CONFIG_IMQ) += imq.o
|
||||||
|
obj-$(CONFIG_IFB) += ifb.o
|
||||||
|
obj-$(CONFIG_MACVLAN) += macvlan.o
|
||||||
|
obj-$(CONFIG_DE600) += de600.o
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/include/linux/imq.h
|
||||||
|
@@ -0,0 +1,9 @@
|
||||||
|
+#ifndef _IMQ_H
|
||||||
|
+#define _IMQ_H
|
||||||
|
+
|
||||||
|
+#define IMQ_MAX_DEVS 16
|
||||||
|
+
|
||||||
|
+#define IMQ_F_IFMASK 0x7f
|
||||||
|
+#define IMQ_F_ENQUEUE 0x80
|
||||||
|
+
|
||||||
|
+#endif /* _IMQ_H */
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/include/linux/netfilter_ipv4/ipt_IMQ.h
|
||||||
|
@@ -0,0 +1,8 @@
|
||||||
|
+#ifndef _IPT_IMQ_H
|
||||||
|
+#define _IPT_IMQ_H
|
||||||
|
+
|
||||||
|
+struct ipt_imq_info {
|
||||||
|
+ unsigned int todev; /* target imq device */
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+#endif /* _IPT_IMQ_H */
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/include/linux/netfilter_ipv6/ip6t_IMQ.h
|
||||||
|
@@ -0,0 +1,8 @@
|
||||||
|
+#ifndef _IP6T_IMQ_H
|
||||||
|
+#define _IP6T_IMQ_H
|
||||||
|
+
|
||||||
|
+struct ip6t_imq_info {
|
||||||
|
+ unsigned int todev; /* target imq device */
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+#endif /* _IP6T_IMQ_H */
|
||||||
|
--- a/include/linux/skbuff.h
|
||||||
|
+++ b/include/linux/skbuff.h
|
||||||
|
@@ -302,6 +302,10 @@
|
||||||
|
struct nf_conntrack *nfct;
|
||||||
|
struct sk_buff *nfct_reasm;
|
||||||
|
#endif
|
||||||
|
+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
|
||||||
|
+ unsigned char imq_flags;
|
||||||
|
+ struct nf_queue_entry *nf_queue_entry;
|
||||||
|
+#endif
|
||||||
|
#ifdef CONFIG_BRIDGE_NETFILTER
|
||||||
|
struct nf_bridge_info *nf_bridge;
|
||||||
|
#endif
|
||||||
|
@@ -1642,6 +1646,10 @@
|
||||||
|
dst->nfct_reasm = src->nfct_reasm;
|
||||||
|
nf_conntrack_get_reasm(src->nfct_reasm);
|
||||||
|
#endif
|
||||||
|
+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
|
||||||
|
+ dst->imq_flags = src->imq_flags;
|
||||||
|
+ dst->nf_queue_entry = src->nf_queue_entry;
|
||||||
|
+#endif
|
||||||
|
#ifdef CONFIG_BRIDGE_NETFILTER
|
||||||
|
dst->nf_bridge = src->nf_bridge;
|
||||||
|
nf_bridge_get(src->nf_bridge);
|
||||||
|
--- a/net/core/dev.c
|
||||||
|
+++ b/net/core/dev.c
|
||||||
|
@@ -96,6 +96,9 @@
|
||||||
|
#include <net/net_namespace.h>
|
||||||
|
#include <net/sock.h>
|
||||||
|
#include <linux/rtnetlink.h>
|
||||||
|
+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
|
||||||
|
+#include <linux/imq.h>
|
||||||
|
+#endif
|
||||||
|
#include <linux/proc_fs.h>
|
||||||
|
#include <linux/seq_file.h>
|
||||||
|
#include <linux/stat.h>
|
||||||
|
@@ -1623,7 +1626,11 @@
|
||||||
|
struct netdev_queue *txq)
|
||||||
|
{
|
||||||
|
if (likely(!skb->next)) {
|
||||||
|
- if (!list_empty(&ptype_all))
|
||||||
|
+ if (!list_empty(&ptype_all)
|
||||||
|
+#if defined(CONFIG_IMQ) || defined(CONFIG_IMQ_MODULE)
|
||||||
|
+ && !(skb->imq_flags & IMQ_F_ENQUEUE)
|
||||||
|
+#endif
|
||||||
|
+ )
|
||||||
|
dev_queue_xmit_nit(skb, dev);
|
||||||
|
|
||||||
|
if (netif_needs_gso(dev, skb)) {
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/net/ipv4/netfilter/ipt_IMQ.c
|
||||||
|
@@ -0,0 +1,69 @@
|
||||||
|
+/*
|
||||||
|
+ * This target marks packets to be enqueued to an imq device
|
||||||
|
+ */
|
||||||
|
+#include <linux/module.h>
|
||||||
|
+#include <linux/skbuff.h>
|
||||||
|
+#include <linux/netfilter_ipv4/ip_tables.h>
|
||||||
|
+#include <linux/netfilter_ipv4/ipt_IMQ.h>
|
||||||
|
+#include <linux/imq.h>
|
||||||
|
+
|
||||||
|
+static unsigned int imq_target(struct sk_buff *pskb,
|
||||||
|
+ const struct net_device *in,
|
||||||
|
+ const struct net_device *out,
|
||||||
|
+ unsigned int hooknum,
|
||||||
|
+ const struct xt_target *target,
|
||||||
|
+ const void *targinfo)
|
||||||
|
+{
|
||||||
|
+ struct ipt_imq_info *mr = (struct ipt_imq_info *)targinfo;
|
||||||
|
+
|
||||||
|
+ pskb->imq_flags = mr->todev | IMQ_F_ENQUEUE;
|
||||||
|
+
|
||||||
|
+ return XT_CONTINUE;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static bool imq_checkentry(const char *tablename,
|
||||||
|
+ const void *e,
|
||||||
|
+ const struct xt_target *target,
|
||||||
|
+ void *targinfo,
|
||||||
|
+ unsigned int hook_mask)
|
||||||
|
+{
|
||||||
|
+ struct ipt_imq_info *mr;
|
||||||
|
+
|
||||||
|
+ mr = (struct ipt_imq_info *)targinfo;
|
||||||
|
+
|
||||||
|
+ if (mr->todev > IMQ_MAX_DEVS) {
|
||||||
|
+ printk(KERN_WARNING
|
||||||
|
+ "IMQ: invalid device specified, highest is %u\n",
|
||||||
|
+ IMQ_MAX_DEVS);
|
||||||
|
+ return 0;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return 1;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static struct xt_target ipt_imq_reg = {
|
||||||
|
+ .name = "IMQ",
|
||||||
|
+ .family = AF_INET,
|
||||||
|
+ .target = imq_target,
|
||||||
|
+ .targetsize = sizeof(struct ipt_imq_info),
|
||||||
|
+ .checkentry = imq_checkentry,
|
||||||
|
+ .me = THIS_MODULE,
|
||||||
|
+ .table = "mangle"
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static int __init init(void)
|
||||||
|
+{
|
||||||
|
+ return xt_register_target(&ipt_imq_reg);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void __exit fini(void)
|
||||||
|
+{
|
||||||
|
+ xt_unregister_target(&ipt_imq_reg);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+module_init(init);
|
||||||
|
+module_exit(fini);
|
||||||
|
+
|
||||||
|
+MODULE_AUTHOR("http://www.linuximq.net");
|
||||||
|
+MODULE_DESCRIPTION("Pseudo-driver for the intermediate queue device. See http://www.linuximq.net/ for more information.");
|
||||||
|
+MODULE_LICENSE("GPL");
|
||||||
|
--- a/net/ipv4/netfilter/Kconfig
|
||||||
|
+++ b/net/ipv4/netfilter/Kconfig
|
||||||
|
@@ -145,6 +145,17 @@
|
||||||
|
|
||||||
|
To compile it as a module, choose M here. If unsure, say N.
|
||||||
|
|
||||||
|
+config IP_NF_TARGET_IMQ
|
||||||
|
+ tristate "IMQ target support"
|
||||||
|
+ depends on IP_NF_MANGLE && IMQ
|
||||||
|
+ help
|
||||||
|
+ This option adds a `IMQ' target which is used to specify if and
|
||||||
|
+ to which IMQ device packets should get enqueued/dequeued.
|
||||||
|
+
|
||||||
|
+ For more information visit: http://www.linuximq.net/
|
||||||
|
+
|
||||||
|
+ To compile it as a module, choose M here. If unsure, say N.
|
||||||
|
+
|
||||||
|
config IP_NF_TARGET_REJECT
|
||||||
|
tristate "REJECT target support"
|
||||||
|
depends on IP_NF_FILTER
|
||||||
|
--- a/net/ipv4/netfilter/Makefile
|
||||||
|
+++ b/net/ipv4/netfilter/Makefile
|
||||||
|
@@ -59,6 +59,7 @@
|
||||||
|
obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o
|
||||||
|
obj-$(CONFIG_IP_NF_TARGET_ECN) += ipt_ECN.o
|
||||||
|
obj-$(CONFIG_IP_NF_TARGET_LOG) += ipt_LOG.o
|
||||||
|
+obj-$(CONFIG_IP_NF_TARGET_IMQ) += ipt_IMQ.o
|
||||||
|
obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o
|
||||||
|
obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o
|
||||||
|
obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/net/ipv6/netfilter/ip6t_IMQ.c
|
||||||
|
@@ -0,0 +1,69 @@
|
||||||
|
+/*
|
||||||
|
+ * This target marks packets to be enqueued to an imq device
|
||||||
|
+ */
|
||||||
|
+#include <linux/module.h>
|
||||||
|
+#include <linux/skbuff.h>
|
||||||
|
+#include <linux/netfilter_ipv6/ip6_tables.h>
|
||||||
|
+#include <linux/netfilter_ipv6/ip6t_IMQ.h>
|
||||||
|
+#include <linux/imq.h>
|
||||||
|
+
|
||||||
|
+static unsigned int imq_target(struct sk_buff *pskb,
|
||||||
|
+ const struct net_device *in,
|
||||||
|
+ const struct net_device *out,
|
||||||
|
+ unsigned int hooknum,
|
||||||
|
+ const struct xt_target *target,
|
||||||
|
+ const void *targinfo)
|
||||||
|
+{
|
||||||
|
+ struct ip6t_imq_info *mr = (struct ip6t_imq_info *)targinfo;
|
||||||
|
+
|
||||||
|
+ pskb->imq_flags = mr->todev | IMQ_F_ENQUEUE;
|
||||||
|
+
|
||||||
|
+ return XT_CONTINUE;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static bool imq_checkentry(const char *tablename,
|
||||||
|
+ const void *entry,
|
||||||
|
+ const struct xt_target *target,
|
||||||
|
+ void *targinfo,
|
||||||
|
+ unsigned int hook_mask)
|
||||||
|
+{
|
||||||
|
+ struct ip6t_imq_info *mr;
|
||||||
|
+
|
||||||
|
+ mr = (struct ip6t_imq_info *)targinfo;
|
||||||
|
+
|
||||||
|
+ if (mr->todev > IMQ_MAX_DEVS) {
|
||||||
|
+ printk(KERN_WARNING
|
||||||
|
+ "IMQ: invalid device specified, highest is %u\n",
|
||||||
|
+ IMQ_MAX_DEVS);
|
||||||
|
+ return 0;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return 1;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static struct xt_target ip6t_imq_reg = {
|
||||||
|
+ .name = "IMQ",
|
||||||
|
+ .family = AF_INET6,
|
||||||
|
+ .target = imq_target,
|
||||||
|
+ .targetsize = sizeof(struct ip6t_imq_info),
|
||||||
|
+ .table = "mangle",
|
||||||
|
+ .checkentry = imq_checkentry,
|
||||||
|
+ .me = THIS_MODULE
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static int __init init(void)
|
||||||
|
+{
|
||||||
|
+ return xt_register_target(&ip6t_imq_reg);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void __exit fini(void)
|
||||||
|
+{
|
||||||
|
+ xt_unregister_target(&ip6t_imq_reg);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+module_init(init);
|
||||||
|
+module_exit(fini);
|
||||||
|
+
|
||||||
|
+MODULE_AUTHOR("http://www.linuximq.net");
|
||||||
|
+MODULE_DESCRIPTION("Pseudo-driver for the intermediate queue device. See http://www.linuximq.net/ for more information.");
|
||||||
|
+MODULE_LICENSE("GPL");
|
||||||
|
--- a/net/ipv6/netfilter/Kconfig
|
||||||
|
+++ b/net/ipv6/netfilter/Kconfig
|
||||||
|
@@ -179,6 +179,15 @@
|
||||||
|
|
||||||
|
To compile it as a module, choose M here. If unsure, say N.
|
||||||
|
|
||||||
|
+config IP6_NF_TARGET_IMQ
|
||||||
|
+ tristate "IMQ target support"
|
||||||
|
+ depends on IP6_NF_MANGLE && IMQ
|
||||||
|
+ help
|
||||||
|
+ This option adds a `IMQ' target which is used to specify if and
|
||||||
|
+ to which imq device packets should get enqueued/dequeued.
|
||||||
|
+
|
||||||
|
+ To compile it as a module, choose M here. If unsure, say N.
|
||||||
|
+
|
||||||
|
config IP6_NF_TARGET_HL
|
||||||
|
tristate 'HL (hoplimit) target support'
|
||||||
|
depends on IP6_NF_MANGLE
|
||||||
|
--- a/net/ipv6/netfilter/Makefile
|
||||||
|
+++ b/net/ipv6/netfilter/Makefile
|
||||||
|
@@ -6,6 +6,7 @@
|
||||||
|
obj-$(CONFIG_IP6_NF_IPTABLES) += ip6_tables.o
|
||||||
|
obj-$(CONFIG_IP6_NF_FILTER) += ip6table_filter.o
|
||||||
|
obj-$(CONFIG_IP6_NF_MANGLE) += ip6table_mangle.o
|
||||||
|
+obj-$(CONFIG_IP6_NF_TARGET_IMQ) += ip6t_IMQ.o
|
||||||
|
obj-$(CONFIG_IP6_NF_QUEUE) += ip6_queue.o
|
||||||
|
obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o
|
||||||
|
obj-$(CONFIG_IP6_NF_SECURITY) += ip6table_security.o
|
||||||
|
--- a/net/sched/sch_generic.c
|
||||||
|
+++ b/net/sched/sch_generic.c
|
||||||
|
@@ -188,6 +188,7 @@
|
||||||
|
|
||||||
|
clear_bit(__QDISC_STATE_RUNNING, &q->state);
|
||||||
|
}
|
||||||
|
+EXPORT_SYMBOL(__qdisc_run);
|
||||||
|
|
||||||
|
static void dev_watchdog(unsigned long arg)
|
||||||
|
{
|
|
@ -0,0 +1,844 @@
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/include/linux/netfilter/oot_conntrack.h
|
||||||
|
@@ -0,0 +1,5 @@
|
||||||
|
+#if defined(CONFIG_IP_NF_CONNTRACK) || defined(CONFIG_IP_NF_CONNTRACK_MODULE)
|
||||||
|
+# include <linux/netfilter_ipv4/ip_conntrack.h>
|
||||||
|
+#else /* linux-2.6.20+ */
|
||||||
|
+# include <net/netfilter/nf_nat_rule.h>
|
||||||
|
+#endif
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/include/linux/netfilter/oot_trans.h
|
||||||
|
@@ -0,0 +1,14 @@
|
||||||
|
+/* Out of tree workarounds */
|
||||||
|
+#include <linux/version.h>
|
||||||
|
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18)
|
||||||
|
+# define HAVE_MATCHINFOSIZE 1
|
||||||
|
+# define HAVE_TARGUSERINFO 1
|
||||||
|
+# define HAVE_TARGINFOSIZE 1
|
||||||
|
+#endif
|
||||||
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
|
||||||
|
+# define nfmark mark
|
||||||
|
+#endif
|
||||||
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 21)
|
||||||
|
+# define tcp_v4_check(tcph, tcph_sz, s, d, csp) \
|
||||||
|
+ tcp_v4_check((tcph_sz), (s), (d), (csp))
|
||||||
|
+#endif
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/include/linux/netfilter/xt_CHAOS.h
|
||||||
|
@@ -0,0 +1,14 @@
|
||||||
|
+#ifndef _LINUX_NETFILTER_XT_CHAOS_H
|
||||||
|
+#define _LINUX_NETFILTER_XT_CHAOS_H 1
|
||||||
|
+
|
||||||
|
+enum xt_chaos_target_variant {
|
||||||
|
+ XTCHAOS_NORMAL,
|
||||||
|
+ XTCHAOS_TARPIT,
|
||||||
|
+ XTCHAOS_DELUDE,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+struct xt_chaos_target_info {
|
||||||
|
+ uint8_t variant;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+#endif /* _LINUX_NETFILTER_XT_CHAOS_H */
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/include/linux/netfilter/xt_portscan.h
|
||||||
|
@@ -0,0 +1,8 @@
|
||||||
|
+#ifndef _LINUX_NETFILTER_XT_PORTSCAN_H
|
||||||
|
+#define _LINUX_NETFILTER_XT_PORTSCAN_H 1
|
||||||
|
+
|
||||||
|
+struct xt_portscan_match_info {
|
||||||
|
+ uint8_t match_stealth, match_syn, match_cn, match_gr;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+#endif /* _LINUX_NETFILTER_XT_PORTSCAN_H */
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/net/netfilter/find_match.c
|
||||||
|
@@ -0,0 +1,39 @@
|
||||||
|
+/*
|
||||||
|
+ xt_request_find_match
|
||||||
|
+ by Jan Engelhardt <jengelh [at] gmx de>, 2006 - 2007
|
||||||
|
+
|
||||||
|
+ Based upon linux-2.6.18.5/net/netfilter/x_tables.c:
|
||||||
|
+ Copyright (C) 2006-2006 Harald Welte <laforge@netfilter.org>
|
||||||
|
+ This program is free software; you can redistribute it and/or modify
|
||||||
|
+ it under the terms of the GNU General Public License version 2 as
|
||||||
|
+ published by the Free Software Foundation.
|
||||||
|
+*/
|
||||||
|
+#include <linux/err.h>
|
||||||
|
+#include <linux/netfilter_arp.h>
|
||||||
|
+#include <linux/socket.h>
|
||||||
|
+#include <linux/netfilter/x_tables.h>
|
||||||
|
+
|
||||||
|
+/*
|
||||||
|
+ * Yeah this code is sub-optimal, but the function is missing in
|
||||||
|
+ * mainline so far. -jengelh
|
||||||
|
+ */
|
||||||
|
+static struct xt_match *xt_request_find_match_lo(int af, const char *name,
|
||||||
|
+ u8 revision)
|
||||||
|
+{
|
||||||
|
+ static const char *const xt_prefix[] = {
|
||||||
|
+ [AF_INET] = "ip",
|
||||||
|
+ [AF_INET6] = "ip6",
|
||||||
|
+ [NF_ARP] = "arp",
|
||||||
|
+ };
|
||||||
|
+ struct xt_match *match;
|
||||||
|
+
|
||||||
|
+ match = try_then_request_module(xt_find_match(af, name, revision),
|
||||||
|
+ "%st_%s", xt_prefix[af], name);
|
||||||
|
+ if (IS_ERR(match) || match == NULL)
|
||||||
|
+ return NULL;
|
||||||
|
+
|
||||||
|
+ return match;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/* In case it goes into mainline, let this out-of-tree package compile */
|
||||||
|
+#define xt_request_find_match xt_request_find_match_lo
|
||||||
|
--- a/net/netfilter/Kconfig
|
||||||
|
+++ b/net/netfilter/Kconfig
|
||||||
|
@@ -296,6 +296,14 @@
|
||||||
|
|
||||||
|
# alphabetically ordered list of targets
|
||||||
|
|
||||||
|
+config NETFILTER_XT_TARGET_CHAOS
|
||||||
|
+ tristate '"CHAOS" target support'
|
||||||
|
+ depends on NETFILTER_XTABLES
|
||||||
|
+ help
|
||||||
|
+ This option adds a `CHAOS' target.
|
||||||
|
+
|
||||||
|
+ To compile it as a module, choose M here. If unsure, say N.
|
||||||
|
+
|
||||||
|
config NETFILTER_XT_TARGET_CLASSIFY
|
||||||
|
tristate '"CLASSIFY" target support'
|
||||||
|
depends on NETFILTER_XTABLES
|
||||||
|
@@ -325,6 +333,14 @@
|
||||||
|
<file:Documentation/kbuild/modules.txt>. The module will be called
|
||||||
|
ipt_CONNMARK.ko. If unsure, say `N'.
|
||||||
|
|
||||||
|
+config NETFILTER_XT_TARGET_DELUDE
|
||||||
|
+ tristate '"DELUDE" target support'
|
||||||
|
+ depends on NETFILTER_XTABLES
|
||||||
|
+ help
|
||||||
|
+ This option adds a `DELUDE' target.
|
||||||
|
+
|
||||||
|
+ To compile it as a module, choose M here. If unsure, say N.
|
||||||
|
+
|
||||||
|
config NETFILTER_XT_TARGET_DSCP
|
||||||
|
tristate '"DSCP" and "TOS" target support'
|
||||||
|
depends on NETFILTER_XTABLES
|
||||||
|
@@ -661,6 +677,14 @@
|
||||||
|
|
||||||
|
To compile it as a module, choose M here. If unsure, say N.
|
||||||
|
|
||||||
|
+config NETFILTER_XT_MATCH_PORTSCAN
|
||||||
|
+ tristate '"portscan" match support'
|
||||||
|
+ depends on NETFILTER_XTABLES
|
||||||
|
+ help
|
||||||
|
+ This option adds a 'portscan' match support.
|
||||||
|
+
|
||||||
|
+ To compile it as a module, choose M here. If unsure, say N.
|
||||||
|
+
|
||||||
|
config NETFILTER_XT_MATCH_MULTIPORT
|
||||||
|
tristate '"multiport" Multiple port match support'
|
||||||
|
depends on NETFILTER_XTABLES
|
||||||
|
--- a/net/netfilter/Makefile
|
||||||
|
+++ b/net/netfilter/Makefile
|
||||||
|
@@ -51,6 +51,8 @@
|
||||||
|
obj-$(CONFIG_NETFILTER_XT_TARGET_TCPMSS) += xt_TCPMSS.o
|
||||||
|
obj-$(CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP) += xt_TCPOPTSTRIP.o
|
||||||
|
obj-$(CONFIG_NETFILTER_XT_TARGET_TRACE) += xt_TRACE.o
|
||||||
|
+obj-$(CONFIG_NETFILTER_XT_TARGET_CHAOS) += xt_CHAOS.o
|
||||||
|
+obj-$(CONFIG_NETFILTER_XT_TARGET_DELUDE) += xt_DELUDE.o
|
||||||
|
|
||||||
|
# matches
|
||||||
|
obj-$(CONFIG_NETFILTER_XT_MATCH_COMMENT) += xt_comment.o
|
||||||
|
@@ -84,3 +86,4 @@
|
||||||
|
obj-$(CONFIG_NETFILTER_XT_MATCH_TCPMSS) += xt_tcpmss.o
|
||||||
|
obj-$(CONFIG_NETFILTER_XT_MATCH_TIME) += xt_time.o
|
||||||
|
obj-$(CONFIG_NETFILTER_XT_MATCH_U32) += xt_u32.o
|
||||||
|
+obj-$(CONFIG_NETFILTER_XT_MATCH_PORTSCAN) += xt_portscan.o
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/net/netfilter/xt_CHAOS.c
|
||||||
|
@@ -0,0 +1,200 @@
|
||||||
|
+/*
|
||||||
|
+ * CHAOS target for netfilter
|
||||||
|
+ * Copyright © CC Computer Consultants GmbH, 2006 - 2007
|
||||||
|
+ * Contact: Jan Engelhardt <jengelh@computergmbh.de>
|
||||||
|
+ *
|
||||||
|
+ * This program is free software; you can redistribute it and/or modify
|
||||||
|
+ * it under the terms of the GNU General Public License; either version
|
||||||
|
+ * 2 or 3 as published by the Free Software Foundation.
|
||||||
|
+ */
|
||||||
|
+#include <linux/icmp.h>
|
||||||
|
+#include <linux/in.h>
|
||||||
|
+#include <linux/ip.h>
|
||||||
|
+#include <linux/module.h>
|
||||||
|
+#include <linux/skbuff.h>
|
||||||
|
+#include <linux/stat.h>
|
||||||
|
+#include <linux/netfilter/x_tables.h>
|
||||||
|
+#include <linux/netfilter/xt_tcpudp.h>
|
||||||
|
+#include <linux/netfilter_ipv4/ipt_REJECT.h>
|
||||||
|
+#include <net/ip.h>
|
||||||
|
+#if defined(_LOCAL)
|
||||||
|
+# include "xt_CHAOS.h"
|
||||||
|
+# include "find_match.c"
|
||||||
|
+#elif defined(CONFIG_NETFILTER_XT_TARGET_CHAOS) || \
|
||||||
|
+ defined(CONFIG_NETFILTER_XT_TARGET_CHAOS_MODULE)
|
||||||
|
+# include <linux/netfilter/xt_CHAOS.h>
|
||||||
|
+# include "find_match.c"
|
||||||
|
+#else
|
||||||
|
+# include "xt_CHAOS.h"
|
||||||
|
+# include "find_match.c"
|
||||||
|
+#endif
|
||||||
|
+#define PFX KBUILD_MODNAME ": "
|
||||||
|
+
|
||||||
|
+/* Module parameters */
|
||||||
|
+static unsigned int reject_percentage = ~0U * .01;
|
||||||
|
+static unsigned int delude_percentage = ~0U * .0101;
|
||||||
|
+module_param(reject_percentage, uint, S_IRUGO | S_IWUSR);
|
||||||
|
+module_param(delude_percentage, uint, S_IRUGO | S_IWUSR);
|
||||||
|
+
|
||||||
|
+/* References to other matches/targets */
|
||||||
|
+static struct xt_match *xm_tcp;
|
||||||
|
+static struct xt_target *xt_delude, *xt_reject, *xt_tarpit;
|
||||||
|
+
|
||||||
|
+static int have_delude, have_tarpit;
|
||||||
|
+
|
||||||
|
+/* Static data for other matches/targets */
|
||||||
|
+static const struct ipt_reject_info reject_params = {
|
||||||
|
+ .with = ICMP_HOST_UNREACH,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static const struct xt_tcp tcp_params = {
|
||||||
|
+ .spts = {0, ~0},
|
||||||
|
+ .dpts = {0, ~0},
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+/* CHAOS functions */
|
||||||
|
+static void xt_chaos_total(const struct xt_chaos_target_info *info,
|
||||||
|
+ struct sk_buff *skb, const struct net_device *in,
|
||||||
|
+ const struct net_device *out, unsigned int hooknum)
|
||||||
|
+{
|
||||||
|
+ const struct iphdr *iph = ip_hdr(skb);
|
||||||
|
+ const int protoff = 4 * iph->ihl;
|
||||||
|
+ const int offset = ntohs(iph->frag_off) & IP_OFFSET;
|
||||||
|
+ const struct xt_target *destiny;
|
||||||
|
+ bool hotdrop = false, ret;
|
||||||
|
+
|
||||||
|
+ ret = xm_tcp->match(skb, in, out, xm_tcp, &tcp_params,
|
||||||
|
+ offset, protoff, &hotdrop);
|
||||||
|
+ if (!ret || hotdrop || (unsigned int)net_random() > delude_percentage)
|
||||||
|
+ return;
|
||||||
|
+
|
||||||
|
+ destiny = (info->variant == XTCHAOS_TARPIT) ? xt_tarpit : xt_delude;
|
||||||
|
+ destiny->target(skb, in, out, hooknum, destiny, NULL);
|
||||||
|
+ return;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static unsigned int chaos_tg(struct sk_buff *skb,
|
||||||
|
+ const struct net_device *in, const struct net_device *out,
|
||||||
|
+ unsigned int hooknum, const struct xt_target *target, const void *targinfo)
|
||||||
|
+{
|
||||||
|
+ /*
|
||||||
|
+ * Equivalent to:
|
||||||
|
+ * -A chaos -m statistic --mode random --probability \
|
||||||
|
+ * $reject_percentage -j REJECT --reject-with host-unreach;
|
||||||
|
+ * -A chaos -p tcp -m statistic --mode random --probability \
|
||||||
|
+ * $delude_percentage -j DELUDE;
|
||||||
|
+ * -A chaos -j DROP;
|
||||||
|
+ */
|
||||||
|
+ const struct xt_chaos_target_info *info = targinfo;
|
||||||
|
+ const struct iphdr *iph = ip_hdr(skb);
|
||||||
|
+
|
||||||
|
+ if ((unsigned int)net_random() <= reject_percentage)
|
||||||
|
+ return xt_reject->target(skb, in, out, hooknum, target,
|
||||||
|
+ &reject_params);
|
||||||
|
+
|
||||||
|
+ /* TARPIT/DELUDE may not be called from the OUTPUT chain */
|
||||||
|
+ if (iph->protocol == IPPROTO_TCP &&
|
||||||
|
+ info->variant != XTCHAOS_NORMAL && hooknum != NF_INET_LOCAL_OUT)
|
||||||
|
+ xt_chaos_total(info, skb, in, out, hooknum);
|
||||||
|
+
|
||||||
|
+ return NF_DROP;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static bool chaos_tg_check(const char *tablename, const void *entry,
|
||||||
|
+ const struct xt_target *target, void *targinfo, unsigned int hook_mask)
|
||||||
|
+{
|
||||||
|
+ const struct xt_chaos_target_info *info = targinfo;
|
||||||
|
+
|
||||||
|
+ if (info->variant == XTCHAOS_DELUDE && !have_delude) {
|
||||||
|
+ printk(KERN_WARNING PFX "Error: Cannot use --delude when "
|
||||||
|
+ "DELUDE module not available\n");
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
+ if (info->variant == XTCHAOS_TARPIT && !have_tarpit) {
|
||||||
|
+ printk(KERN_WARNING PFX "Error: Cannot use --tarpit when "
|
||||||
|
+ "TARPIT module not available\n");
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return true;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static struct xt_target chaos_tg_reg = {
|
||||||
|
+ .name = "CHAOS",
|
||||||
|
+ .family = AF_INET,
|
||||||
|
+ .table = "filter",
|
||||||
|
+ .hooks = (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_FORWARD) |
|
||||||
|
+ (1 << NF_INET_LOCAL_OUT),
|
||||||
|
+ .checkentry = chaos_tg_check,
|
||||||
|
+ .target = chaos_tg,
|
||||||
|
+ .targetsize = sizeof(struct xt_chaos_target_info),
|
||||||
|
+ .me = THIS_MODULE,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static int __init chaos_tg_init(void)
|
||||||
|
+{
|
||||||
|
+ int ret = -EINVAL;
|
||||||
|
+
|
||||||
|
+ xm_tcp = xt_request_find_match(AF_INET, "tcp", 0);
|
||||||
|
+ if (xm_tcp == NULL) {
|
||||||
|
+ printk(KERN_WARNING PFX "Error: Could not find or load "
|
||||||
|
+ "\"tcp\" match\n");
|
||||||
|
+ return -EINVAL;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ xt_reject = xt_request_find_target(AF_INET, "REJECT", 0);
|
||||||
|
+ if (xt_reject == NULL) {
|
||||||
|
+ printk(KERN_WARNING PFX "Error: Could not find or load "
|
||||||
|
+ "\"REJECT\" target\n");
|
||||||
|
+ goto out2;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ xt_tarpit = xt_request_find_target(AF_INET, "TARPIT", 0);
|
||||||
|
+ have_tarpit = xt_tarpit != NULL;
|
||||||
|
+ if (!have_tarpit)
|
||||||
|
+ printk(KERN_WARNING PFX "Warning: Could not find or load "
|
||||||
|
+ "\"TARPIT\" target\n");
|
||||||
|
+
|
||||||
|
+ xt_delude = xt_request_find_target(AF_INET, "DELUDE", 0);
|
||||||
|
+ have_delude = xt_delude != NULL;
|
||||||
|
+ if (!have_delude)
|
||||||
|
+ printk(KERN_WARNING PFX "Warning: Could not find or load "
|
||||||
|
+ "\"DELUDE\" target\n");
|
||||||
|
+
|
||||||
|
+ if ((ret = xt_register_target(&chaos_tg_reg)) != 0) {
|
||||||
|
+ printk(KERN_WARNING PFX "xt_register_target returned "
|
||||||
|
+ "error %d\n", ret);
|
||||||
|
+ goto out3;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+
|
||||||
|
+ out3:
|
||||||
|
+ if (have_delude)
|
||||||
|
+ module_put(xt_delude->me);
|
||||||
|
+ if (have_tarpit)
|
||||||
|
+ module_put(xt_tarpit->me);
|
||||||
|
+ module_put(xt_reject->me);
|
||||||
|
+ out2:
|
||||||
|
+ module_put(xm_tcp->me);
|
||||||
|
+ return ret;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void __exit chaos_tg_exit(void)
|
||||||
|
+{
|
||||||
|
+ xt_unregister_target(&chaos_tg_reg);
|
||||||
|
+ module_put(xm_tcp->me);
|
||||||
|
+ module_put(xt_reject->me);
|
||||||
|
+ if (have_delude)
|
||||||
|
+ module_put(xt_delude->me);
|
||||||
|
+ if (have_tarpit)
|
||||||
|
+ module_put(xt_tarpit->me);
|
||||||
|
+ return;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+module_init(chaos_tg_init);
|
||||||
|
+module_exit(chaos_tg_exit);
|
||||||
|
+MODULE_AUTHOR("Jan Engelhardt <jengelh@computergmbh.de>");
|
||||||
|
+MODULE_DESCRIPTION("netfilter \"CHAOS\" target");
|
||||||
|
+MODULE_LICENSE("GPL");
|
||||||
|
+MODULE_ALIAS("ipt_CHAOS");
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/net/netfilter/xt_DELUDE.c
|
||||||
|
@@ -0,0 +1,197 @@
|
||||||
|
+/*
|
||||||
|
+ * DELUDE target
|
||||||
|
+ * Copyright © CC Computer Consultants GmbH, 2007
|
||||||
|
+ * Contact: Jan Engelhardt <jengelh@computergmbh.de>
|
||||||
|
+ *
|
||||||
|
+ * Based upon linux-2.6.18.5/net/ipv4/netfilter/ipt_REJECT.c:
|
||||||
|
+ * (C) 1999-2001 Paul `Rusty' Russell
|
||||||
|
+ * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
|
||||||
|
+ *
|
||||||
|
+ * xt_DELUDE acts like REJECT, but does reply with SYN-ACK on SYN.
|
||||||
|
+ *
|
||||||
|
+ * This program is free software; you can redistribute it and/or modify
|
||||||
|
+ * it under the terms of the GNU General Public License version 2 as
|
||||||
|
+ * published by the Free Software Foundation.
|
||||||
|
+ */
|
||||||
|
+#include <linux/module.h>
|
||||||
|
+#include <linux/skbuff.h>
|
||||||
|
+#include <linux/ip.h>
|
||||||
|
+#include <linux/tcp.h>
|
||||||
|
+#include <linux/netfilter/x_tables.h>
|
||||||
|
+#ifdef CONFIG_BRIDGE_NETFILTER
|
||||||
|
+# include <linux/netfilter_bridge.h>
|
||||||
|
+#endif
|
||||||
|
+#include <net/tcp.h>
|
||||||
|
+#define PFX KBUILD_MODNAME ": "
|
||||||
|
+
|
||||||
|
+static void delude_send_reset(struct sk_buff *oldskb, unsigned int hook)
|
||||||
|
+{
|
||||||
|
+ struct tcphdr _otcph, *oth, *tcph;
|
||||||
|
+ unsigned int addr_type;
|
||||||
|
+ struct sk_buff *nskb;
|
||||||
|
+ u_int16_t tmp_port;
|
||||||
|
+ u_int32_t tmp_addr;
|
||||||
|
+ struct iphdr *niph;
|
||||||
|
+ bool needs_ack;
|
||||||
|
+
|
||||||
|
+ /* IP header checks: fragment. */
|
||||||
|
+ if (ip_hdr(oldskb)->frag_off & htons(IP_OFFSET))
|
||||||
|
+ return;
|
||||||
|
+
|
||||||
|
+ oth = skb_header_pointer(oldskb, ip_hdrlen(oldskb),
|
||||||
|
+ sizeof(_otcph), &_otcph);
|
||||||
|
+ if (oth == NULL)
|
||||||
|
+ return;
|
||||||
|
+
|
||||||
|
+ /* No RST for RST. */
|
||||||
|
+ if (oth->rst)
|
||||||
|
+ return;
|
||||||
|
+
|
||||||
|
+ /* Check checksum */
|
||||||
|
+ if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP))
|
||||||
|
+ return;
|
||||||
|
+
|
||||||
|
+ /* We need a linear, writeable skb. We also need to expand
|
||||||
|
+ headroom in case hh_len of incoming interface < hh_len of
|
||||||
|
+ outgoing interface */
|
||||||
|
+ nskb = skb_copy_expand(oldskb, LL_MAX_HEADER, skb_tailroom(oldskb),
|
||||||
|
+ GFP_ATOMIC);
|
||||||
|
+ if (!nskb)
|
||||||
|
+ return;
|
||||||
|
+
|
||||||
|
+ /* This packet will not be the same as the other: clear nf fields */
|
||||||
|
+ nf_reset(nskb);
|
||||||
|
+ nskb->mark = 0;
|
||||||
|
+ skb_init_secmark(nskb);
|
||||||
|
+
|
||||||
|
+ skb_shinfo(nskb)->gso_size = 0;
|
||||||
|
+ skb_shinfo(nskb)->gso_segs = 0;
|
||||||
|
+ skb_shinfo(nskb)->gso_type = 0;
|
||||||
|
+
|
||||||
|
+ tcph = (struct tcphdr *)(skb_network_header(nskb) + ip_hdrlen(nskb));
|
||||||
|
+
|
||||||
|
+ /* Swap source and dest */
|
||||||
|
+ niph = ip_hdr(nskb);
|
||||||
|
+ tmp_addr = niph->saddr;
|
||||||
|
+ niph->saddr = niph->daddr;
|
||||||
|
+ niph->daddr = tmp_addr;
|
||||||
|
+ tmp_port = tcph->source;
|
||||||
|
+ tcph->source = tcph->dest;
|
||||||
|
+ tcph->dest = tmp_port;
|
||||||
|
+
|
||||||
|
+ /* Truncate to length (no data) */
|
||||||
|
+ tcph->doff = sizeof(struct tcphdr) / 4;
|
||||||
|
+ skb_trim(nskb, ip_hdrlen(nskb) + sizeof(struct tcphdr));
|
||||||
|
+ niph->tot_len = htons(nskb->len);
|
||||||
|
+
|
||||||
|
+ if (oth->syn && !oth->ack && !oth->rst && !oth->fin) {
|
||||||
|
+ /* DELUDE essential part */
|
||||||
|
+ tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin +
|
||||||
|
+ oldskb->len - ip_hdrlen(oldskb) -
|
||||||
|
+ (oth->doff << 2));
|
||||||
|
+ tcph->seq = false;
|
||||||
|
+ tcph->ack = true;
|
||||||
|
+ } else {
|
||||||
|
+ if (!tcph->ack) {
|
||||||
|
+ needs_ack = true;
|
||||||
|
+ tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn +
|
||||||
|
+ oth->fin + oldskb->len -
|
||||||
|
+ ip_hdrlen(oldskb) - (oth->doff<<2));
|
||||||
|
+ tcph->seq = false;
|
||||||
|
+ } else {
|
||||||
|
+ needs_ack = false;
|
||||||
|
+ tcph->seq = oth->ack_seq;
|
||||||
|
+ tcph->ack_seq = false;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* Reset flags */
|
||||||
|
+ ((u_int8_t *)tcph)[13] = 0;
|
||||||
|
+ tcph->rst = true;
|
||||||
|
+ tcph->ack = needs_ack;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ tcph->window = 0;
|
||||||
|
+ tcph->urg_ptr = 0;
|
||||||
|
+
|
||||||
|
+ /* Adjust TCP checksum */
|
||||||
|
+ tcph->check = 0;
|
||||||
|
+ tcph->check = tcp_v4_check(sizeof(struct tcphdr), niph->saddr,
|
||||||
|
+ niph->daddr, csum_partial((char *)tcph,
|
||||||
|
+ sizeof(struct tcphdr), 0));
|
||||||
|
+
|
||||||
|
+ /* Set DF, id = 0 */
|
||||||
|
+ niph->frag_off = htons(IP_DF);
|
||||||
|
+ niph->id = 0;
|
||||||
|
+
|
||||||
|
+ addr_type = RTN_UNSPEC;
|
||||||
|
+#ifdef CONFIG_BRIDGE_NETFILTER
|
||||||
|
+ if (hook != NF_INET_FORWARD || (nskb->nf_bridge != NULL &&
|
||||||
|
+ nskb->nf_bridge->mask & BRNF_BRIDGED))
|
||||||
|
+#else
|
||||||
|
+ if (hook != NF_INET_FORWARD)
|
||||||
|
+#endif
|
||||||
|
+ addr_type = RTN_LOCAL;
|
||||||
|
+
|
||||||
|
+ if (ip_route_me_harder(nskb, addr_type))
|
||||||
|
+ goto free_nskb;
|
||||||
|
+
|
||||||
|
+ nskb->ip_summed = CHECKSUM_NONE;
|
||||||
|
+
|
||||||
|
+ /* Adjust IP TTL */
|
||||||
|
+ niph->ttl = dst_metric(nskb->dst, RTAX_HOPLIMIT);
|
||||||
|
+
|
||||||
|
+ /* Adjust IP checksum */
|
||||||
|
+ niph->check = 0;
|
||||||
|
+ niph->check = ip_fast_csum(skb_network_header(nskb), niph->ihl);
|
||||||
|
+
|
||||||
|
+ /* "Never happens" */
|
||||||
|
+ if (nskb->len > dst_mtu(nskb->dst))
|
||||||
|
+ goto free_nskb;
|
||||||
|
+
|
||||||
|
+ nf_ct_attach(nskb, oldskb);
|
||||||
|
+
|
||||||
|
+ NF_HOOK(PF_INET, NF_INET_LOCAL_OUT, nskb, NULL, nskb->dst->dev,
|
||||||
|
+ dst_output);
|
||||||
|
+ return;
|
||||||
|
+
|
||||||
|
+ free_nskb:
|
||||||
|
+ kfree_skb(nskb);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static unsigned int delude_tg(struct sk_buff *skb,
|
||||||
|
+ const struct net_device *in, const struct net_device *out,
|
||||||
|
+ unsigned int hooknum, const struct xt_target *target, const void *targinfo)
|
||||||
|
+{
|
||||||
|
+ /* WARNING: This code causes reentry within iptables.
|
||||||
|
+ This means that the iptables jump stack is now crap. We
|
||||||
|
+ must return an absolute verdict. --RR */
|
||||||
|
+ delude_send_reset(skb, hooknum);
|
||||||
|
+ return NF_DROP;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static struct xt_target delude_tg_reg = {
|
||||||
|
+ .name = "DELUDE",
|
||||||
|
+ .family = AF_INET,
|
||||||
|
+ .table = "filter",
|
||||||
|
+ .hooks = (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_FORWARD),
|
||||||
|
+ .target = delude_tg,
|
||||||
|
+ .proto = IPPROTO_TCP,
|
||||||
|
+ .me = THIS_MODULE,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static int __init delude_tg_init(void)
|
||||||
|
+{
|
||||||
|
+ return xt_register_target(&delude_tg_reg);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void __exit delude_tg_exit(void)
|
||||||
|
+{
|
||||||
|
+ xt_unregister_target(&delude_tg_reg);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+module_init(delude_tg_init);
|
||||||
|
+module_exit(delude_tg_exit);
|
||||||
|
+MODULE_AUTHOR("Jan Engelhardt <jengelh@computergmbh.de>");
|
||||||
|
+MODULE_DESCRIPTION("netfilter \"DELUDE\" target");
|
||||||
|
+MODULE_LICENSE("GPL");
|
||||||
|
+MODULE_ALIAS("ipt_DELUDE");
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/net/netfilter/xt_portscan.c
|
||||||
|
@@ -0,0 +1,269 @@
|
||||||
|
+/*
|
||||||
|
+ * portscan match for netfilter
|
||||||
|
+ * Copyright © CC Computer Consultants GmbH, 2006 - 2007
|
||||||
|
+ * Contact: Jan Engelhardt <jengelh@computergmbh.de>
|
||||||
|
+ *
|
||||||
|
+ * This program is free software; you can redistribute it and/or modify
|
||||||
|
+ * it under the terms of the GNU General Public License; either version
|
||||||
|
+ * 2 or 3 as published by the Free Software Foundation.
|
||||||
|
+ */
|
||||||
|
+#include <linux/in.h>
|
||||||
|
+#include <linux/ip.h>
|
||||||
|
+#include <linux/module.h>
|
||||||
|
+#include <linux/moduleparam.h>
|
||||||
|
+#include <linux/skbuff.h>
|
||||||
|
+#include <linux/stat.h>
|
||||||
|
+#include <linux/tcp.h>
|
||||||
|
+#include <linux/types.h>
|
||||||
|
+#include <linux/version.h>
|
||||||
|
+#include <linux/netfilter/x_tables.h>
|
||||||
|
+#include <linux/netfilter/xt_tcpudp.h>
|
||||||
|
+#include <net/netfilter/nf_nat_rule.h>
|
||||||
|
+#if defined(_LOCAL)
|
||||||
|
+# include "xt_portscan.h"
|
||||||
|
+#elif defined(CONFIG_NETFILTER_XT_MATCH_PORTSCAN) || \
|
||||||
|
+ defined(CONFIG_NETFILTER_XT_MATCH_PORTSCAN_MODULE)
|
||||||
|
+# include <linux/netfilter/xt_portscan.h>
|
||||||
|
+#else
|
||||||
|
+# include "xt_portscan.h"
|
||||||
|
+#endif
|
||||||
|
+#define PFX KBUILD_MODNAME ": "
|
||||||
|
+
|
||||||
|
+enum {
|
||||||
|
+ TCP_FLAGS_ALL3 = TCP_FLAG_FIN | TCP_FLAG_RST | TCP_FLAG_SYN,
|
||||||
|
+ TCP_FLAGS_ALL4 = TCP_FLAGS_ALL3 | TCP_FLAG_ACK,
|
||||||
|
+ TCP_FLAGS_ALL6 = TCP_FLAGS_ALL4 | TCP_FLAG_PSH | TCP_FLAG_URG,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+/* Module parameters */
|
||||||
|
+static unsigned int
|
||||||
|
+ connmark_mask = ~0,
|
||||||
|
+ packet_mask = ~0,
|
||||||
|
+ mark_seen = 0x9,
|
||||||
|
+ mark_synrcv = 0x1,
|
||||||
|
+ mark_closed = 0x2,
|
||||||
|
+ mark_synscan = 0x3,
|
||||||
|
+ mark_estab1 = 0x4,
|
||||||
|
+ mark_estab2 = 0x5,
|
||||||
|
+ mark_cnscan = 0x6,
|
||||||
|
+ mark_grscan = 0x7,
|
||||||
|
+ mark_valid = 0x8;
|
||||||
|
+
|
||||||
|
+module_param(connmark_mask, uint, S_IRUGO | S_IWUSR);
|
||||||
|
+module_param(packet_mask, uint, S_IRUGO | S_IWUSR);
|
||||||
|
+module_param(mark_seen, uint, S_IRUGO | S_IWUSR);
|
||||||
|
+module_param(mark_synrcv, uint, S_IRUGO | S_IWUSR);
|
||||||
|
+module_param(mark_closed, uint, S_IRUGO | S_IWUSR);
|
||||||
|
+module_param(mark_synscan, uint, S_IRUGO | S_IWUSR);
|
||||||
|
+module_param(mark_estab1, uint, S_IRUGO | S_IWUSR);
|
||||||
|
+module_param(mark_estab2, uint, S_IRUGO | S_IWUSR);
|
||||||
|
+module_param(mark_cnscan, uint, S_IRUGO | S_IWUSR);
|
||||||
|
+module_param(mark_grscan, uint, S_IRUGO | S_IWUSR);
|
||||||
|
+module_param(mark_valid, uint, S_IRUGO | S_IWUSR);
|
||||||
|
+MODULE_PARM_DESC(connmark_mask, "only set specified bits in connection mark");
|
||||||
|
+MODULE_PARM_DESC(packet_mask, "only set specified bits in packet mark");
|
||||||
|
+MODULE_PARM_DESC(mark_seen, "nfmark value for packet-seen state");
|
||||||
|
+MODULE_PARM_DESC(mark_synrcv, "connmark value for SYN Received state");
|
||||||
|
+MODULE_PARM_DESC(mark_closed, "connmark value for closed state");
|
||||||
|
+MODULE_PARM_DESC(mark_synscan, "connmark value for SYN Scan state");
|
||||||
|
+MODULE_PARM_DESC(mark_estab1, "connmark value for Established-1 state");
|
||||||
|
+MODULE_PARM_DESC(mark_estab2, "connmark value for Established-2 state");
|
||||||
|
+MODULE_PARM_DESC(mark_cnscan, "connmark value for Connect Scan state");
|
||||||
|
+MODULE_PARM_DESC(mark_grscan, "connmark value for Grab Scan state");
|
||||||
|
+MODULE_PARM_DESC(mark_valid, "connmark value for Valid state");
|
||||||
|
+
|
||||||
|
+/* TCP flag functions */
|
||||||
|
+static inline bool tflg_ack4(const struct tcphdr *th)
|
||||||
|
+{
|
||||||
|
+ return (tcp_flag_word(th) & TCP_FLAGS_ALL4) == TCP_FLAG_ACK;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline bool tflg_ack6(const struct tcphdr *th)
|
||||||
|
+{
|
||||||
|
+ return (tcp_flag_word(th) & TCP_FLAGS_ALL6) == TCP_FLAG_ACK;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline bool tflg_fin(const struct tcphdr *th)
|
||||||
|
+{
|
||||||
|
+ return (tcp_flag_word(th) & TCP_FLAGS_ALL3) == TCP_FLAG_FIN;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline bool tflg_rst(const struct tcphdr *th)
|
||||||
|
+{
|
||||||
|
+ return (tcp_flag_word(th) & TCP_FLAGS_ALL3) == TCP_FLAG_RST;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline bool tflg_rstack(const struct tcphdr *th)
|
||||||
|
+{
|
||||||
|
+ return (tcp_flag_word(th) & TCP_FLAGS_ALL4) ==
|
||||||
|
+ (TCP_FLAG_ACK | TCP_FLAG_RST);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline bool tflg_syn(const struct tcphdr *th)
|
||||||
|
+{
|
||||||
|
+ return (tcp_flag_word(th) & TCP_FLAGS_ALL4) == TCP_FLAG_SYN;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline bool tflg_synack(const struct tcphdr *th)
|
||||||
|
+{
|
||||||
|
+ return (tcp_flag_word(th) & TCP_FLAGS_ALL4) ==
|
||||||
|
+ (TCP_FLAG_SYN | TCP_FLAG_ACK);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/* portscan functions */
|
||||||
|
+static inline bool portscan_mt_stealth(const struct tcphdr *th)
|
||||||
|
+{
|
||||||
|
+ /*
|
||||||
|
+ * "Connection refused" replies to our own probes must not be matched.
|
||||||
|
+ */
|
||||||
|
+ if (tflg_rstack(th))
|
||||||
|
+ return false;
|
||||||
|
+
|
||||||
|
+ if (tflg_rst(th) && printk_ratelimit()) {
|
||||||
|
+ printk(KERN_WARNING PFX "Warning: Pure RST received\n");
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * -p tcp ! --syn -m conntrack --ctstate INVALID: Looking for non-start
|
||||||
|
+ * packets that are not associated with any connection -- this will
|
||||||
|
+ * match most scan types (NULL, XMAS, FIN) and ridiculous flag
|
||||||
|
+ * combinations (SYN-RST, SYN-FIN, SYN-FIN-RST, FIN-RST, etc.).
|
||||||
|
+ */
|
||||||
|
+ return !tflg_syn(th);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline unsigned int portscan_mt_full(int mark,
|
||||||
|
+ enum ip_conntrack_info ctstate, bool loopback, const struct tcphdr *tcph,
|
||||||
|
+ unsigned int payload_len)
|
||||||
|
+{
|
||||||
|
+ if (mark == mark_estab2) {
|
||||||
|
+ /*
|
||||||
|
+ * -m connmark --mark $ESTAB2
|
||||||
|
+ */
|
||||||
|
+ if (tflg_ack4(tcph) && payload_len == 0)
|
||||||
|
+ return mark; /* keep mark */
|
||||||
|
+ else if (tflg_rst(tcph) || tflg_fin(tcph))
|
||||||
|
+ return mark_grscan;
|
||||||
|
+ else
|
||||||
|
+ return mark_valid;
|
||||||
|
+ } else if (mark == mark_estab1) {
|
||||||
|
+ /*
|
||||||
|
+ * -m connmark --mark $ESTAB1
|
||||||
|
+ */
|
||||||
|
+ if (tflg_rst(tcph) || tflg_fin(tcph))
|
||||||
|
+ return mark_cnscan;
|
||||||
|
+ else if (!loopback && tflg_ack4(tcph) && payload_len == 0)
|
||||||
|
+ return mark_estab2;
|
||||||
|
+ else
|
||||||
|
+ return mark_valid;
|
||||||
|
+ } else if (mark == mark_synrcv) {
|
||||||
|
+ /*
|
||||||
|
+ * -m connmark --mark $SYN
|
||||||
|
+ */
|
||||||
|
+ if (loopback && tflg_synack(tcph))
|
||||||
|
+ return mark; /* keep mark */
|
||||||
|
+ else if (loopback && tflg_rstack(tcph))
|
||||||
|
+ return mark_closed;
|
||||||
|
+ else if (tflg_ack6(tcph))
|
||||||
|
+ return mark_estab1;
|
||||||
|
+ else
|
||||||
|
+ return mark_synscan;
|
||||||
|
+ } else if (ctstate == IP_CT_NEW && tflg_syn(tcph)) {
|
||||||
|
+ /*
|
||||||
|
+ * -p tcp --syn --ctstate NEW
|
||||||
|
+ */
|
||||||
|
+ return mark_synrcv;
|
||||||
|
+ }
|
||||||
|
+ return mark;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static bool portscan_mt(const struct sk_buff *skb,
|
||||||
|
+ const struct net_device *in, const struct net_device *out,
|
||||||
|
+ const struct xt_match *match, const void *matchinfo, int offset,
|
||||||
|
+ unsigned int protoff, bool *hotdrop)
|
||||||
|
+{
|
||||||
|
+ const struct xt_portscan_match_info *info = matchinfo;
|
||||||
|
+ enum ip_conntrack_info ctstate;
|
||||||
|
+ const struct tcphdr *tcph;
|
||||||
|
+ struct nf_conn *ctdata;
|
||||||
|
+ struct tcphdr tcph_buf;
|
||||||
|
+
|
||||||
|
+ tcph = skb_header_pointer(skb, protoff, sizeof(tcph_buf), &tcph_buf);
|
||||||
|
+ if (tcph == NULL)
|
||||||
|
+ return false;
|
||||||
|
+
|
||||||
|
+ /* Check for invalid packets: -m conntrack --ctstate INVALID */
|
||||||
|
+ if ((ctdata = nf_ct_get(skb, &ctstate)) == NULL) {
|
||||||
|
+ if (info->match_stealth)
|
||||||
|
+ return portscan_mt_stealth(tcph);
|
||||||
|
+ /*
|
||||||
|
+ * If @ctdata is NULL, we cannot match the other scan
|
||||||
|
+ * types, return.
|
||||||
|
+ */
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * If -m portscan was previously applied to this packet, the rules we
|
||||||
|
+ * simulate must not be run through again. And for speedup, do not call
|
||||||
|
+ * it either when the connection is already VALID.
|
||||||
|
+ */
|
||||||
|
+ if ((ctdata->mark & connmark_mask) == mark_valid ||
|
||||||
|
+ (skb->mark & packet_mask) != mark_seen) {
|
||||||
|
+ unsigned int n;
|
||||||
|
+
|
||||||
|
+ n = portscan_mt_full(ctdata->mark & connmark_mask, ctstate,
|
||||||
|
+ (in->flags & IFF_LOOPBACK) == IFF_LOOPBACK, tcph,
|
||||||
|
+ skb->len - protoff - 4 * tcph->doff);
|
||||||
|
+
|
||||||
|
+ ctdata->mark = (ctdata->mark & ~connmark_mask) | n;
|
||||||
|
+ ((struct sk_buff *)skb)->mark =
|
||||||
|
+ (skb->mark & ~packet_mask) ^ mark_seen;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return (info->match_syn && ctdata->mark == mark_synscan) ||
|
||||||
|
+ (info->match_cn && ctdata->mark == mark_cnscan) ||
|
||||||
|
+ (info->match_gr && ctdata->mark == mark_grscan);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static bool portscan_mt_check(const char *tablename, const void *entry,
|
||||||
|
+ const struct xt_match *match, void *matchinfo, unsigned int hook_mask)
|
||||||
|
+{
|
||||||
|
+ const struct xt_portscan_match_info *info = matchinfo;
|
||||||
|
+
|
||||||
|
+ if ((info->match_stealth & ~1) || (info->match_syn & ~1) ||
|
||||||
|
+ (info->match_cn & ~1) || (info->match_gr & ~1)) {
|
||||||
|
+ printk(KERN_WARNING PFX "Invalid flags\n");
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
+ return true;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static struct xt_match portscan_mt_reg __read_mostly = {
|
||||||
|
+ .name = "portscan",
|
||||||
|
+ .family = AF_INET,
|
||||||
|
+ .match = portscan_mt,
|
||||||
|
+ .checkentry = portscan_mt_check,
|
||||||
|
+ .matchsize = sizeof(struct xt_portscan_match_info),
|
||||||
|
+ .proto = IPPROTO_TCP,
|
||||||
|
+ .me = THIS_MODULE,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static int __init portscan_mt_init(void)
|
||||||
|
+{
|
||||||
|
+ return xt_register_match(&portscan_mt_reg);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void __exit portscan_mt_exit(void)
|
||||||
|
+{
|
||||||
|
+ xt_unregister_match(&portscan_mt_reg);
|
||||||
|
+ return;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+module_init(portscan_mt_init);
|
||||||
|
+module_exit(portscan_mt_exit);
|
||||||
|
+MODULE_AUTHOR("Jan Engelhardt <jengelh@computergmbh.de>");
|
||||||
|
+MODULE_DESCRIPTION("netfilter \"portscan\" match");
|
||||||
|
+MODULE_LICENSE("GPL");
|
||||||
|
+MODULE_ALIAS("ipt_portscan");
|
||||||
|
--- a/drivers/char/random.c
|
||||||
|
+++ b/drivers/char/random.c
|
||||||
|
@@ -1555,6 +1555,8 @@
|
||||||
|
return seq;
|
||||||
|
}
|
||||||
|
|
||||||
|
+EXPORT_SYMBOL(secure_tcp_sequence_number);
|
||||||
|
+
|
||||||
|
/* Generate secure starting point for ephemeral IPV4 transport port search */
|
||||||
|
u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport)
|
||||||
|
{
|
|
@ -0,0 +1,318 @@
|
||||||
|
--- a/net/netfilter/Kconfig
|
||||||
|
+++ b/net/netfilter/Kconfig
|
||||||
|
@@ -457,6 +457,23 @@
|
||||||
|
|
||||||
|
To compile it as a module, choose M here. If unsure, say N.
|
||||||
|
|
||||||
|
+config NETFILTER_XT_TARGET_TARPIT
|
||||||
|
+ tristate '"TARPIT" target support'
|
||||||
|
+ depends on NETFILTER_XTABLES
|
||||||
|
+ ---help---
|
||||||
|
+ Adds a TARPIT target to iptables, which captures and holds
|
||||||
|
+ incoming TCP connections using no local per-connection resources.
|
||||||
|
+ Connections are accepted, but immediately switched to the persist
|
||||||
|
+ state (0 byte window), in which the remote side stops sending data
|
||||||
|
+ and asks to continue every 60-240 seconds. Attempts to close the
|
||||||
|
+ connection are ignored, forcing the remote side to time out the
|
||||||
|
+ connection in 12-24 minutes.
|
||||||
|
+
|
||||||
|
+ This offers similar functionality to LaBrea
|
||||||
|
+ <http://www.hackbusters.net/LaBrea/>, but does not require dedicated
|
||||||
|
+ hardware or IPs. Any TCP port that you would normally DROP or REJECT
|
||||||
|
+ can instead become a tarpit.
|
||||||
|
+
|
||||||
|
config NETFILTER_XT_TARGET_TCPMSS
|
||||||
|
tristate '"TCPMSS" target support'
|
||||||
|
depends on NETFILTER_XTABLES && (IPV6 || IPV6=n)
|
||||||
|
--- a/net/netfilter/Makefile
|
||||||
|
+++ b/net/netfilter/Makefile
|
||||||
|
@@ -48,6 +48,7 @@
|
||||||
|
obj-$(CONFIG_NETFILTER_XT_TARGET_NOTRACK) += xt_NOTRACK.o
|
||||||
|
obj-$(CONFIG_NETFILTER_XT_TARGET_RATEEST) += xt_RATEEST.o
|
||||||
|
obj-$(CONFIG_NETFILTER_XT_TARGET_SECMARK) += xt_SECMARK.o
|
||||||
|
+obj-$(CONFIG_NETFILTER_XT_TARGET_TARPIT) += xt_TARPIT.o
|
||||||
|
obj-$(CONFIG_NETFILTER_XT_TARGET_TCPMSS) += xt_TCPMSS.o
|
||||||
|
obj-$(CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP) += xt_TCPOPTSTRIP.o
|
||||||
|
obj-$(CONFIG_NETFILTER_XT_TARGET_TRACE) += xt_TRACE.o
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/net/netfilter/xt_TARPIT.c
|
||||||
|
@@ -0,0 +1,279 @@
|
||||||
|
+/*
|
||||||
|
+ * Kernel module to capture and hold incoming TCP connections using
|
||||||
|
+ * no local per-connection resources.
|
||||||
|
+ *
|
||||||
|
+ * Based on ipt_REJECT.c and offering functionality similar to
|
||||||
|
+ * LaBrea <http://www.hackbusters.net/LaBrea/>.
|
||||||
|
+ *
|
||||||
|
+ * Copyright (c) 2002 Aaron Hopkins <tools@die.net>
|
||||||
|
+ *
|
||||||
|
+ * This program is free software; you can redistribute it and/or modify
|
||||||
|
+ * it under the terms of the GNU General Public License as published by
|
||||||
|
+ * the Free Software Foundation; either version 2 of the License, or
|
||||||
|
+ * (at your option) any later version.
|
||||||
|
+ *
|
||||||
|
+ * This program is distributed in the hope that it will be useful,
|
||||||
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
+ * GNU General Public License for more details.
|
||||||
|
+ *
|
||||||
|
+ * You should have received a copy of the GNU General Public License
|
||||||
|
+ * along with this program; if not, write to the Free Software
|
||||||
|
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
+ *
|
||||||
|
+ * Goal:
|
||||||
|
+ * - Allow incoming TCP connections to be established.
|
||||||
|
+ * - Passing data should result in the connection being switched to the
|
||||||
|
+ * persist state (0 byte window), in which the remote side stops sending
|
||||||
|
+ * data and asks to continue every 60 seconds.
|
||||||
|
+ * - Attempts to shut down the connection should be ignored completely, so
|
||||||
|
+ * the remote side ends up having to time it out.
|
||||||
|
+ *
|
||||||
|
+ * This means:
|
||||||
|
+ * - Reply to TCP SYN,!ACK,!RST,!FIN with SYN-ACK, window 5 bytes
|
||||||
|
+ * - Reply to TCP SYN,ACK,!RST,!FIN with RST to prevent spoofing
|
||||||
|
+ * - Reply to TCP !SYN,!RST,!FIN with ACK, window 0 bytes, rate-limited
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+#include <linux/version.h>
|
||||||
|
+#include <linux/module.h>
|
||||||
|
+#include <linux/skbuff.h>
|
||||||
|
+#include <linux/ip.h>
|
||||||
|
+#include <net/ip.h>
|
||||||
|
+#include <net/tcp.h>
|
||||||
|
+#include <net/icmp.h>
|
||||||
|
+struct in_device;
|
||||||
|
+#include <net/route.h>
|
||||||
|
+#include <linux/random.h>
|
||||||
|
+#include <linux/netfilter_ipv4/ip_tables.h>
|
||||||
|
+
|
||||||
|
+#if 0
|
||||||
|
+#define DEBUGP printk
|
||||||
|
+#else
|
||||||
|
+#define DEBUGP(format, args...)
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+/* Stolen from ip_finish_output2 */
|
||||||
|
+static int ip_direct_send(struct sk_buff *skb)
|
||||||
|
+{
|
||||||
|
+ struct dst_entry *dst = skb->dst;
|
||||||
|
+
|
||||||
|
+ if (dst->hh != NULL)
|
||||||
|
+ return neigh_hh_output(dst->hh, skb);
|
||||||
|
+ else if (dst->neighbour != NULL)
|
||||||
|
+ return dst->neighbour->output(skb);
|
||||||
|
+
|
||||||
|
+ if (net_ratelimit())
|
||||||
|
+ printk(KERN_DEBUG "TARPIT ip_direct_send: no header cache and no neighbor!\n");
|
||||||
|
+
|
||||||
|
+ kfree_skb(skb);
|
||||||
|
+ return -EINVAL;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+/* Send reply */
|
||||||
|
+static void tarpit_tcp(const struct sk_buff *oskb, struct rtable *ort,
|
||||||
|
+ unsigned int local)
|
||||||
|
+{
|
||||||
|
+ struct sk_buff *nskb;
|
||||||
|
+ struct rtable *nrt;
|
||||||
|
+ struct tcphdr *otcph, *ntcph;
|
||||||
|
+ struct flowi fl = {};
|
||||||
|
+ unsigned int otcplen;
|
||||||
|
+ u_int16_t tmp;
|
||||||
|
+
|
||||||
|
+ const struct iphdr *oiph = ip_hdr(oskb);
|
||||||
|
+ struct iphdr *niph;
|
||||||
|
+
|
||||||
|
+ /* A truncated TCP header is not going to be useful */
|
||||||
|
+ if (oskb->len < ip_hdrlen(oskb) + sizeof(struct tcphdr))
|
||||||
|
+ return;
|
||||||
|
+
|
||||||
|
+ otcph = (void *)oiph + ip_hdrlen(oskb);
|
||||||
|
+ otcplen = oskb->len - ip_hdrlen(oskb);
|
||||||
|
+
|
||||||
|
+ /* No replies for RST or FIN */
|
||||||
|
+ if (otcph->rst || otcph->fin)
|
||||||
|
+ return;
|
||||||
|
+
|
||||||
|
+ /* No reply to !SYN,!ACK. Rate-limit replies to !SYN,ACKs */
|
||||||
|
+ if (!otcph->syn && (!otcph->ack || !xrlim_allow(&ort->u.dst, 1*HZ)))
|
||||||
|
+ return;
|
||||||
|
+
|
||||||
|
+ /* Check checksum. */
|
||||||
|
+ if (tcp_v4_check(otcplen, oiph->saddr, oiph->daddr,
|
||||||
|
+ csum_partial((char *)otcph, otcplen, 0)) != 0)
|
||||||
|
+ return;
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * Copy skb (even if skb is about to be dropped, we cannot just
|
||||||
|
+ * clone it because there may be other things, such as tcpdump,
|
||||||
|
+ * interested in it)
|
||||||
|
+ */
|
||||||
|
+ nskb = skb_copy(oskb, GFP_ATOMIC);
|
||||||
|
+ if (nskb == NULL)
|
||||||
|
+ return;
|
||||||
|
+
|
||||||
|
+ niph = ip_hdr(nskb);
|
||||||
|
+
|
||||||
|
+ /* This packet will not be the same as the other: clear nf fields */
|
||||||
|
+ nf_conntrack_put(nskb->nfct);
|
||||||
|
+ nskb->nfct = NULL;
|
||||||
|
+#ifdef CONFIG_NETFILTER_DEBUG
|
||||||
|
+ nskb->nf_debug = 0;
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+ ntcph = (void *)niph + ip_hdrlen(nskb);
|
||||||
|
+
|
||||||
|
+ /* Truncate to length (no data) */
|
||||||
|
+ ntcph->doff = sizeof(struct tcphdr)/4;
|
||||||
|
+ skb_trim(nskb, ip_hdrlen(nskb) + sizeof(struct tcphdr));
|
||||||
|
+ niph->tot_len = htons(nskb->len);
|
||||||
|
+
|
||||||
|
+ /* Swap source and dest */
|
||||||
|
+ niph->daddr = xchg(&niph->saddr, niph->daddr);
|
||||||
|
+ tmp = ntcph->source;
|
||||||
|
+ ntcph->source = ntcph->dest;
|
||||||
|
+ ntcph->dest = tmp;
|
||||||
|
+
|
||||||
|
+ /* Use supplied sequence number or make a new one */
|
||||||
|
+ ntcph->seq = otcph->ack ? otcph->ack_seq
|
||||||
|
+ : htonl(secure_tcp_sequence_number(niph->saddr,
|
||||||
|
+ niph->daddr,
|
||||||
|
+ ntcph->source,
|
||||||
|
+ ntcph->dest));
|
||||||
|
+
|
||||||
|
+ /* Our SYN-ACKs must have a >0 window */
|
||||||
|
+ ntcph->window = (otcph->syn && !otcph->ack) ? htons(5) : 0;
|
||||||
|
+
|
||||||
|
+ ntcph->urg_ptr = 0;
|
||||||
|
+
|
||||||
|
+ /* Reset flags */
|
||||||
|
+ ((u_int8_t *)ntcph)[13] = 0;
|
||||||
|
+
|
||||||
|
+ if (otcph->syn && otcph->ack) {
|
||||||
|
+ ntcph->rst = 1;
|
||||||
|
+ ntcph->ack_seq = 0;
|
||||||
|
+ } else {
|
||||||
|
+ ntcph->syn = otcph->syn;
|
||||||
|
+ ntcph->ack = 1;
|
||||||
|
+ ntcph->ack_seq = htonl(ntohl(otcph->seq) + otcph->syn);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* Adjust TCP checksum */
|
||||||
|
+ ntcph->check = 0;
|
||||||
|
+ ntcph->check = tcp_v4_check(sizeof(struct tcphdr),
|
||||||
|
+ niph->saddr,
|
||||||
|
+ niph->daddr,
|
||||||
|
+ csum_partial((char *)ntcph,
|
||||||
|
+ sizeof(struct tcphdr), 0));
|
||||||
|
+
|
||||||
|
+ fl.nl_u.ip4_u.daddr = niph->daddr;
|
||||||
|
+ fl.nl_u.ip4_u.saddr = local ? niph->saddr : 0;
|
||||||
|
+ fl.nl_u.ip4_u.tos = RT_TOS(niph->tos) | RTO_CONN;
|
||||||
|
+ fl.oif = 0;
|
||||||
|
+
|
||||||
|
+ if (ip_route_output_key(&init_net, &nrt, &fl))
|
||||||
|
+ goto free_nskb;
|
||||||
|
+
|
||||||
|
+ dst_release(nskb->dst);
|
||||||
|
+ nskb->dst = &nrt->u.dst;
|
||||||
|
+
|
||||||
|
+ /* Adjust IP TTL */
|
||||||
|
+ niph->ttl = dst_metric(nskb->dst, RTAX_HOPLIMIT);
|
||||||
|
+
|
||||||
|
+ /* Set DF, id = 0 */
|
||||||
|
+ niph->frag_off = htons(IP_DF);
|
||||||
|
+ niph->id = 0;
|
||||||
|
+
|
||||||
|
+ /* Adjust IP checksum */
|
||||||
|
+ niph->check = 0;
|
||||||
|
+ niph->check = ip_fast_csum((unsigned char *)niph, niph->ihl);
|
||||||
|
+
|
||||||
|
+ /* "Never happens" */
|
||||||
|
+ if (nskb->len > dst_mtu(nskb->dst))
|
||||||
|
+ goto free_nskb;
|
||||||
|
+
|
||||||
|
+ ip_direct_send(nskb);
|
||||||
|
+ return;
|
||||||
|
+
|
||||||
|
+ free_nskb:
|
||||||
|
+ kfree_skb(nskb);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static unsigned int xt_tarpit_target(struct sk_buff *skb,
|
||||||
|
+ const struct net_device *in,
|
||||||
|
+ const struct net_device *out,
|
||||||
|
+ unsigned int hooknum,
|
||||||
|
+ const struct xt_target *target,
|
||||||
|
+ const void *targinfo)
|
||||||
|
+{
|
||||||
|
+ const struct iphdr *iph = ip_hdr(skb);
|
||||||
|
+ struct rtable *rt = (void *)skb->dst;
|
||||||
|
+
|
||||||
|
+ /* Do we have an input route cache entry? */
|
||||||
|
+ if (rt == NULL)
|
||||||
|
+ return NF_DROP;
|
||||||
|
+
|
||||||
|
+ /* No replies to physical multicast/broadcast */
|
||||||
|
+ if (skb->pkt_type != PACKET_HOST && skb->pkt_type != PACKET_OTHERHOST)
|
||||||
|
+ return NF_DROP;
|
||||||
|
+
|
||||||
|
+ /* Now check at the protocol level */
|
||||||
|
+ if (rt->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))
|
||||||
|
+ return NF_DROP;
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * Our naive response construction does not deal with IP
|
||||||
|
+ * options, and probably should not try.
|
||||||
|
+ */
|
||||||
|
+ if (iph->ihl * 4 != sizeof(struct iphdr))
|
||||||
|
+ return NF_DROP;
|
||||||
|
+
|
||||||
|
+ /* We are not interested in fragments */
|
||||||
|
+ if (iph->frag_off & htons(IP_OFFSET))
|
||||||
|
+ return NF_DROP;
|
||||||
|
+
|
||||||
|
+ tarpit_tcp(skb, rt, hooknum == NF_INET_LOCAL_IN);
|
||||||
|
+ return NF_DROP;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static bool xt_tarpit_check(const char *tablename, const void *entry,
|
||||||
|
+ const struct xt_target *target, void *targinfo,
|
||||||
|
+ unsigned int hook_mask)
|
||||||
|
+{
|
||||||
|
+ bool invalid;
|
||||||
|
+
|
||||||
|
+ if (strcmp(tablename, "raw") == 0 && hook_mask == NF_INET_PRE_ROUTING)
|
||||||
|
+ return true;
|
||||||
|
+ if (strcmp(tablename, "filter") != 0)
|
||||||
|
+ return false;
|
||||||
|
+ invalid = hook_mask & ~((1 << NF_INET_LOCAL_IN) | (1 << NF_INET_FORWARD));
|
||||||
|
+ return !invalid;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static struct xt_target xt_tarpit_reg = {
|
||||||
|
+ .name = "TARPIT",
|
||||||
|
+ .family = AF_INET,
|
||||||
|
+ .proto = IPPROTO_TCP,
|
||||||
|
+ .target = xt_tarpit_target,
|
||||||
|
+ .checkentry = xt_tarpit_check,
|
||||||
|
+ .me = THIS_MODULE,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static int __init xt_tarpit_init(void)
|
||||||
|
+{
|
||||||
|
+ return xt_register_target(&xt_tarpit_reg);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void __exit xt_tarpit_exit(void)
|
||||||
|
+{
|
||||||
|
+ xt_unregister_target(&xt_tarpit_reg);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+module_init(xt_tarpit_init);
|
||||||
|
+module_exit(xt_tarpit_exit);
|
||||||
|
+MODULE_DESCRIPTION("netfilter xt_TARPIT target module");
|
||||||
|
+MODULE_AUTHOR("Jan Engelhardt <jengelh@gmx.de>");
|
||||||
|
+MODULE_LICENSE("GPL");
|
||||||
|
+MODULE_ALIAS("ipt_TARPIT");
|
|
@ -0,0 +1,20 @@
|
||||||
|
--- a/net/netfilter/Kconfig
|
||||||
|
+++ b/net/netfilter/Kconfig
|
||||||
|
@@ -165,7 +165,7 @@
|
||||||
|
|
||||||
|
config NF_CONNTRACK_H323
|
||||||
|
tristate "H.323 protocol support"
|
||||||
|
- depends on NF_CONNTRACK && (IPV6 || IPV6=n)
|
||||||
|
+ depends on NF_CONNTRACK
|
||||||
|
depends on NETFILTER_ADVANCED
|
||||||
|
help
|
||||||
|
H.323 is a VoIP signalling protocol from ITU-T. As one of the most
|
||||||
|
@@ -476,7 +476,7 @@
|
||||||
|
|
||||||
|
config NETFILTER_XT_TARGET_TCPMSS
|
||||||
|
tristate '"TCPMSS" target support'
|
||||||
|
- depends on NETFILTER_XTABLES && (IPV6 || IPV6=n)
|
||||||
|
+ depends on NETFILTER_XTABLES
|
||||||
|
default m if NETFILTER_ADVANCED=n
|
||||||
|
---help---
|
||||||
|
This option adds a `TCPMSS' target, which allows you to alter the
|
1366
target/linux/generic-2.6/patches-2.6.27/190-netfilter_rtsp.patch
Normal file
1366
target/linux/generic-2.6/patches-2.6.27/190-netfilter_rtsp.patch
Normal file
File diff suppressed because it is too large
Load diff
795
target/linux/generic-2.6/patches-2.6.27/200-sched_esfq.patch
Normal file
795
target/linux/generic-2.6/patches-2.6.27/200-sched_esfq.patch
Normal file
|
@ -0,0 +1,795 @@
|
||||||
|
--- a/include/linux/pkt_sched.h
|
||||||
|
+++ b/include/linux/pkt_sched.h
|
||||||
|
@@ -173,8 +173,37 @@
|
||||||
|
*
|
||||||
|
* The only reason for this is efficiency, it is possible
|
||||||
|
* to change these parameters in compile time.
|
||||||
|
+ *
|
||||||
|
+ * If you need to play with these values, use esfq instead.
|
||||||
|
*/
|
||||||
|
|
||||||
|
+/* ESFQ section */
|
||||||
|
+
|
||||||
|
+enum
|
||||||
|
+{
|
||||||
|
+ /* traditional */
|
||||||
|
+ TCA_SFQ_HASH_CLASSIC,
|
||||||
|
+ TCA_SFQ_HASH_DST,
|
||||||
|
+ TCA_SFQ_HASH_SRC,
|
||||||
|
+ TCA_SFQ_HASH_FWMARK,
|
||||||
|
+ /* conntrack */
|
||||||
|
+ TCA_SFQ_HASH_CTORIGDST,
|
||||||
|
+ TCA_SFQ_HASH_CTORIGSRC,
|
||||||
|
+ TCA_SFQ_HASH_CTREPLDST,
|
||||||
|
+ TCA_SFQ_HASH_CTREPLSRC,
|
||||||
|
+ TCA_SFQ_HASH_CTNATCHG,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+struct tc_esfq_qopt
|
||||||
|
+{
|
||||||
|
+ unsigned quantum; /* Bytes per round allocated to flow */
|
||||||
|
+ int perturb_period; /* Period of hash perturbation */
|
||||||
|
+ __u32 limit; /* Maximal packets in queue */
|
||||||
|
+ unsigned divisor; /* Hash divisor */
|
||||||
|
+ unsigned flows; /* Maximal number of flows */
|
||||||
|
+ unsigned hash_kind; /* Hash function to use for flow identification */
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
/* RED section */
|
||||||
|
|
||||||
|
enum
|
||||||
|
--- a/net/sched/Kconfig
|
||||||
|
+++ b/net/sched/Kconfig
|
||||||
|
@@ -128,6 +128,37 @@
|
||||||
|
To compile this code as a module, choose M here: the
|
||||||
|
module will be called sch_sfq.
|
||||||
|
|
||||||
|
+config NET_SCH_ESFQ
|
||||||
|
+ tristate "Enhanced Stochastic Fairness Queueing (ESFQ)"
|
||||||
|
+ ---help---
|
||||||
|
+ Say Y here if you want to use the Enhanced Stochastic Fairness
|
||||||
|
+ Queueing (ESFQ) packet scheduling algorithm for some of your network
|
||||||
|
+ devices or as a leaf discipline for a classful qdisc such as HTB or
|
||||||
|
+ CBQ (see the top of <file:net/sched/sch_esfq.c> for details and
|
||||||
|
+ references to the SFQ algorithm).
|
||||||
|
+
|
||||||
|
+ This is an enchanced SFQ version which allows you to control some
|
||||||
|
+ hardcoded values in the SFQ scheduler.
|
||||||
|
+
|
||||||
|
+ ESFQ also adds control of the hash function used to identify packet
|
||||||
|
+ flows. The original SFQ discipline hashes by connection; ESFQ add
|
||||||
|
+ several other hashing methods, such as by src IP or by dst IP, which
|
||||||
|
+ can be more fair to users in some networking situations.
|
||||||
|
+
|
||||||
|
+ To compile this code as a module, choose M here: the
|
||||||
|
+ module will be called sch_esfq.
|
||||||
|
+
|
||||||
|
+config NET_SCH_ESFQ_NFCT
|
||||||
|
+ bool "Connection Tracking Hash Types"
|
||||||
|
+ depends on NET_SCH_ESFQ && NF_CONNTRACK
|
||||||
|
+ ---help---
|
||||||
|
+ Say Y here to enable support for hashing based on netfilter connection
|
||||||
|
+ tracking information. This is useful for a router that is also using
|
||||||
|
+ NAT to connect privately-addressed hosts to the Internet. If you want
|
||||||
|
+ to provide fair distribution of upstream bandwidth, ESFQ must use
|
||||||
|
+ connection tracking information, since all outgoing packets will share
|
||||||
|
+ the same source address.
|
||||||
|
+
|
||||||
|
config NET_SCH_TEQL
|
||||||
|
tristate "True Link Equalizer (TEQL)"
|
||||||
|
---help---
|
||||||
|
--- a/net/sched/Makefile
|
||||||
|
+++ b/net/sched/Makefile
|
||||||
|
@@ -23,6 +23,7 @@
|
||||||
|
obj-$(CONFIG_NET_SCH_INGRESS) += sch_ingress.o
|
||||||
|
obj-$(CONFIG_NET_SCH_DSMARK) += sch_dsmark.o
|
||||||
|
obj-$(CONFIG_NET_SCH_SFQ) += sch_sfq.o
|
||||||
|
+obj-$(CONFIG_NET_SCH_ESFQ) += sch_esfq.o
|
||||||
|
obj-$(CONFIG_NET_SCH_TBF) += sch_tbf.o
|
||||||
|
obj-$(CONFIG_NET_SCH_TEQL) += sch_teql.o
|
||||||
|
obj-$(CONFIG_NET_SCH_PRIO) += sch_prio.o
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/net/sched/sch_esfq.c
|
||||||
|
@@ -0,0 +1,702 @@
|
||||||
|
+/*
|
||||||
|
+ * net/sched/sch_esfq.c Extended Stochastic Fairness Queueing discipline.
|
||||||
|
+ *
|
||||||
|
+ * 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.
|
||||||
|
+ *
|
||||||
|
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
|
||||||
|
+ *
|
||||||
|
+ * Changes: Alexander Atanasov, <alex@ssi.bg>
|
||||||
|
+ * Added dynamic depth,limit,divisor,hash_kind options.
|
||||||
|
+ * Added dst and src hashes.
|
||||||
|
+ *
|
||||||
|
+ * Alexander Clouter, <alex@digriz.org.uk>
|
||||||
|
+ * Ported ESFQ to Linux 2.6.
|
||||||
|
+ *
|
||||||
|
+ * Corey Hickey, <bugfood-c@fatooh.org>
|
||||||
|
+ * Maintenance of the Linux 2.6 port.
|
||||||
|
+ * Added fwmark hash (thanks to Robert Kurjata).
|
||||||
|
+ * Added usage of jhash.
|
||||||
|
+ * Added conntrack support.
|
||||||
|
+ * Added ctnatchg hash (thanks to Ben Pfountz).
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+#include <linux/module.h>
|
||||||
|
+#include <asm/uaccess.h>
|
||||||
|
+#include <asm/system.h>
|
||||||
|
+#include <linux/bitops.h>
|
||||||
|
+#include <linux/types.h>
|
||||||
|
+#include <linux/kernel.h>
|
||||||
|
+#include <linux/jiffies.h>
|
||||||
|
+#include <linux/string.h>
|
||||||
|
+#include <linux/mm.h>
|
||||||
|
+#include <linux/socket.h>
|
||||||
|
+#include <linux/sockios.h>
|
||||||
|
+#include <linux/in.h>
|
||||||
|
+#include <linux/errno.h>
|
||||||
|
+#include <linux/interrupt.h>
|
||||||
|
+#include <linux/if_ether.h>
|
||||||
|
+#include <linux/inet.h>
|
||||||
|
+#include <linux/netdevice.h>
|
||||||
|
+#include <linux/etherdevice.h>
|
||||||
|
+#include <linux/notifier.h>
|
||||||
|
+#include <linux/init.h>
|
||||||
|
+#include <net/ip.h>
|
||||||
|
+#include <linux/ipv6.h>
|
||||||
|
+#include <net/route.h>
|
||||||
|
+#include <linux/skbuff.h>
|
||||||
|
+#include <net/sock.h>
|
||||||
|
+#include <net/pkt_sched.h>
|
||||||
|
+#include <linux/jhash.h>
|
||||||
|
+#include <net/netfilter/nf_conntrack.h>
|
||||||
|
+
|
||||||
|
+/* Stochastic Fairness Queuing algorithm.
|
||||||
|
+ For more comments look at sch_sfq.c.
|
||||||
|
+ The difference is that you can change limit, depth,
|
||||||
|
+ hash table size and choose alternate hash types.
|
||||||
|
+
|
||||||
|
+ classic: same as in sch_sfq.c
|
||||||
|
+ dst: destination IP address
|
||||||
|
+ src: source IP address
|
||||||
|
+ fwmark: netfilter mark value
|
||||||
|
+ ctorigdst: original destination IP address
|
||||||
|
+ ctorigsrc: original source IP address
|
||||||
|
+ ctrepldst: reply destination IP address
|
||||||
|
+ ctreplsrc: reply source IP
|
||||||
|
+
|
||||||
|
+*/
|
||||||
|
+
|
||||||
|
+#define ESFQ_HEAD 0
|
||||||
|
+#define ESFQ_TAIL 1
|
||||||
|
+
|
||||||
|
+/* This type should contain at least SFQ_DEPTH*2 values */
|
||||||
|
+typedef unsigned int esfq_index;
|
||||||
|
+
|
||||||
|
+struct esfq_head
|
||||||
|
+{
|
||||||
|
+ esfq_index next;
|
||||||
|
+ esfq_index prev;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+struct esfq_sched_data
|
||||||
|
+{
|
||||||
|
+/* Parameters */
|
||||||
|
+ int perturb_period;
|
||||||
|
+ unsigned quantum; /* Allotment per round: MUST BE >= MTU */
|
||||||
|
+ int limit;
|
||||||
|
+ unsigned depth;
|
||||||
|
+ unsigned hash_divisor;
|
||||||
|
+ unsigned hash_kind;
|
||||||
|
+/* Variables */
|
||||||
|
+ struct timer_list perturb_timer;
|
||||||
|
+ int perturbation;
|
||||||
|
+ esfq_index tail; /* Index of current slot in round */
|
||||||
|
+ esfq_index max_depth; /* Maximal depth */
|
||||||
|
+
|
||||||
|
+ esfq_index *ht; /* Hash table */
|
||||||
|
+ esfq_index *next; /* Active slots link */
|
||||||
|
+ short *allot; /* Current allotment per slot */
|
||||||
|
+ unsigned short *hash; /* Hash value indexed by slots */
|
||||||
|
+ struct sk_buff_head *qs; /* Slot queue */
|
||||||
|
+ struct esfq_head *dep; /* Linked list of slots, indexed by depth */
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+/* This contains the info we will hash. */
|
||||||
|
+struct esfq_packet_info
|
||||||
|
+{
|
||||||
|
+ u32 proto; /* protocol or port */
|
||||||
|
+ u32 src; /* source from packet header */
|
||||||
|
+ u32 dst; /* destination from packet header */
|
||||||
|
+ u32 ctorigsrc; /* original source from conntrack */
|
||||||
|
+ u32 ctorigdst; /* original destination from conntrack */
|
||||||
|
+ u32 ctreplsrc; /* reply source from conntrack */
|
||||||
|
+ u32 ctrepldst; /* reply destination from conntrack */
|
||||||
|
+ u32 mark; /* netfilter mark (fwmark) */
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static __inline__ unsigned esfq_jhash_1word(struct esfq_sched_data *q,u32 a)
|
||||||
|
+{
|
||||||
|
+ return jhash_1word(a, q->perturbation) & (q->hash_divisor-1);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static __inline__ unsigned esfq_jhash_2words(struct esfq_sched_data *q, u32 a, u32 b)
|
||||||
|
+{
|
||||||
|
+ return jhash_2words(a, b, q->perturbation) & (q->hash_divisor-1);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static __inline__ unsigned esfq_jhash_3words(struct esfq_sched_data *q, u32 a, u32 b, u32 c)
|
||||||
|
+{
|
||||||
|
+ return jhash_3words(a, b, c, q->perturbation) & (q->hash_divisor-1);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static unsigned esfq_hash(struct esfq_sched_data *q, struct sk_buff *skb)
|
||||||
|
+{
|
||||||
|
+ struct esfq_packet_info info;
|
||||||
|
+#ifdef CONFIG_NET_SCH_ESFQ_NFCT
|
||||||
|
+ enum ip_conntrack_info ctinfo;
|
||||||
|
+ struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+ switch (skb->protocol) {
|
||||||
|
+ case __constant_htons(ETH_P_IP):
|
||||||
|
+ {
|
||||||
|
+ struct iphdr *iph = ip_hdr(skb);
|
||||||
|
+ info.dst = iph->daddr;
|
||||||
|
+ info.src = iph->saddr;
|
||||||
|
+ if (!(iph->frag_off&htons(IP_MF|IP_OFFSET)) &&
|
||||||
|
+ (iph->protocol == IPPROTO_TCP ||
|
||||||
|
+ iph->protocol == IPPROTO_UDP ||
|
||||||
|
+ iph->protocol == IPPROTO_SCTP ||
|
||||||
|
+ iph->protocol == IPPROTO_DCCP ||
|
||||||
|
+ iph->protocol == IPPROTO_ESP))
|
||||||
|
+ info.proto = *(((u32*)iph) + iph->ihl);
|
||||||
|
+ else
|
||||||
|
+ info.proto = iph->protocol;
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ case __constant_htons(ETH_P_IPV6):
|
||||||
|
+ {
|
||||||
|
+ struct ipv6hdr *iph = ipv6_hdr(skb);
|
||||||
|
+ /* Hash ipv6 addresses into a u32. This isn't ideal,
|
||||||
|
+ * but the code is simple. */
|
||||||
|
+ info.dst = jhash2(iph->daddr.s6_addr32, 4, q->perturbation);
|
||||||
|
+ info.src = jhash2(iph->saddr.s6_addr32, 4, q->perturbation);
|
||||||
|
+ if (iph->nexthdr == IPPROTO_TCP ||
|
||||||
|
+ iph->nexthdr == IPPROTO_UDP ||
|
||||||
|
+ iph->nexthdr == IPPROTO_SCTP ||
|
||||||
|
+ iph->nexthdr == IPPROTO_DCCP ||
|
||||||
|
+ iph->nexthdr == IPPROTO_ESP)
|
||||||
|
+ info.proto = *(u32*)&iph[1];
|
||||||
|
+ else
|
||||||
|
+ info.proto = iph->nexthdr;
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ default:
|
||||||
|
+ info.dst = (u32)(unsigned long)skb->dst;
|
||||||
|
+ info.src = (u32)(unsigned long)skb->sk;
|
||||||
|
+ info.proto = skb->protocol;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ info.mark = skb->mark;
|
||||||
|
+
|
||||||
|
+#ifdef CONFIG_NET_SCH_ESFQ_NFCT
|
||||||
|
+ /* defaults if there is no conntrack info */
|
||||||
|
+ info.ctorigsrc = info.src;
|
||||||
|
+ info.ctorigdst = info.dst;
|
||||||
|
+ info.ctreplsrc = info.dst;
|
||||||
|
+ info.ctrepldst = info.src;
|
||||||
|
+ /* collect conntrack info */
|
||||||
|
+ if (ct && ct != &nf_conntrack_untracked) {
|
||||||
|
+ if (skb->protocol == __constant_htons(ETH_P_IP)) {
|
||||||
|
+ info.ctorigsrc = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip;
|
||||||
|
+ info.ctorigdst = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.ip;
|
||||||
|
+ info.ctreplsrc = ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip;
|
||||||
|
+ info.ctrepldst = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip;
|
||||||
|
+ }
|
||||||
|
+ else if (skb->protocol == __constant_htons(ETH_P_IPV6)) {
|
||||||
|
+ /* Again, hash ipv6 addresses into a single u32. */
|
||||||
|
+ info.ctorigsrc = jhash2(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip6, 4, q->perturbation);
|
||||||
|
+ info.ctorigdst = jhash2(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.ip6, 4, q->perturbation);
|
||||||
|
+ info.ctreplsrc = jhash2(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip6, 4, q->perturbation);
|
||||||
|
+ info.ctrepldst = jhash2(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip6, 4, q->perturbation);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ }
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+ switch(q->hash_kind) {
|
||||||
|
+ case TCA_SFQ_HASH_CLASSIC:
|
||||||
|
+ return esfq_jhash_3words(q, info.dst, info.src, info.proto);
|
||||||
|
+ case TCA_SFQ_HASH_DST:
|
||||||
|
+ return esfq_jhash_1word(q, info.dst);
|
||||||
|
+ case TCA_SFQ_HASH_SRC:
|
||||||
|
+ return esfq_jhash_1word(q, info.src);
|
||||||
|
+ case TCA_SFQ_HASH_FWMARK:
|
||||||
|
+ return esfq_jhash_1word(q, info.mark);
|
||||||
|
+#ifdef CONFIG_NET_SCH_ESFQ_NFCT
|
||||||
|
+ case TCA_SFQ_HASH_CTORIGDST:
|
||||||
|
+ return esfq_jhash_1word(q, info.ctorigdst);
|
||||||
|
+ case TCA_SFQ_HASH_CTORIGSRC:
|
||||||
|
+ return esfq_jhash_1word(q, info.ctorigsrc);
|
||||||
|
+ case TCA_SFQ_HASH_CTREPLDST:
|
||||||
|
+ return esfq_jhash_1word(q, info.ctrepldst);
|
||||||
|
+ case TCA_SFQ_HASH_CTREPLSRC:
|
||||||
|
+ return esfq_jhash_1word(q, info.ctreplsrc);
|
||||||
|
+ case TCA_SFQ_HASH_CTNATCHG:
|
||||||
|
+ {
|
||||||
|
+ if (info.ctorigdst == info.ctreplsrc)
|
||||||
|
+ return esfq_jhash_1word(q, info.ctorigsrc);
|
||||||
|
+ return esfq_jhash_1word(q, info.ctreplsrc);
|
||||||
|
+ }
|
||||||
|
+#endif
|
||||||
|
+ default:
|
||||||
|
+ if (net_ratelimit())
|
||||||
|
+ printk(KERN_WARNING "ESFQ: Unknown hash method. Falling back to classic.\n");
|
||||||
|
+ }
|
||||||
|
+ return esfq_jhash_3words(q, info.dst, info.src, info.proto);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline void esfq_link(struct esfq_sched_data *q, esfq_index x)
|
||||||
|
+{
|
||||||
|
+ esfq_index p, n;
|
||||||
|
+ int d = q->qs[x].qlen + q->depth;
|
||||||
|
+
|
||||||
|
+ p = d;
|
||||||
|
+ n = q->dep[d].next;
|
||||||
|
+ q->dep[x].next = n;
|
||||||
|
+ q->dep[x].prev = p;
|
||||||
|
+ q->dep[p].next = q->dep[n].prev = x;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline void esfq_dec(struct esfq_sched_data *q, esfq_index x)
|
||||||
|
+{
|
||||||
|
+ esfq_index p, n;
|
||||||
|
+
|
||||||
|
+ n = q->dep[x].next;
|
||||||
|
+ p = q->dep[x].prev;
|
||||||
|
+ q->dep[p].next = n;
|
||||||
|
+ q->dep[n].prev = p;
|
||||||
|
+
|
||||||
|
+ if (n == p && q->max_depth == q->qs[x].qlen + 1)
|
||||||
|
+ q->max_depth--;
|
||||||
|
+
|
||||||
|
+ esfq_link(q, x);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline void esfq_inc(struct esfq_sched_data *q, esfq_index x)
|
||||||
|
+{
|
||||||
|
+ esfq_index p, n;
|
||||||
|
+ int d;
|
||||||
|
+
|
||||||
|
+ n = q->dep[x].next;
|
||||||
|
+ p = q->dep[x].prev;
|
||||||
|
+ q->dep[p].next = n;
|
||||||
|
+ q->dep[n].prev = p;
|
||||||
|
+ d = q->qs[x].qlen;
|
||||||
|
+ if (q->max_depth < d)
|
||||||
|
+ q->max_depth = d;
|
||||||
|
+
|
||||||
|
+ esfq_link(q, x);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static unsigned int esfq_drop(struct Qdisc *sch)
|
||||||
|
+{
|
||||||
|
+ struct esfq_sched_data *q = qdisc_priv(sch);
|
||||||
|
+ esfq_index d = q->max_depth;
|
||||||
|
+ struct sk_buff *skb;
|
||||||
|
+ unsigned int len;
|
||||||
|
+
|
||||||
|
+ /* Queue is full! Find the longest slot and
|
||||||
|
+ drop a packet from it */
|
||||||
|
+
|
||||||
|
+ if (d > 1) {
|
||||||
|
+ esfq_index x = q->dep[d+q->depth].next;
|
||||||
|
+ skb = q->qs[x].prev;
|
||||||
|
+ len = skb->len;
|
||||||
|
+ __skb_unlink(skb, &q->qs[x]);
|
||||||
|
+ kfree_skb(skb);
|
||||||
|
+ esfq_dec(q, x);
|
||||||
|
+ sch->q.qlen--;
|
||||||
|
+ sch->qstats.drops++;
|
||||||
|
+ sch->qstats.backlog -= len;
|
||||||
|
+ return len;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (d == 1) {
|
||||||
|
+ /* It is difficult to believe, but ALL THE SLOTS HAVE LENGTH 1. */
|
||||||
|
+ d = q->next[q->tail];
|
||||||
|
+ q->next[q->tail] = q->next[d];
|
||||||
|
+ q->allot[q->next[d]] += q->quantum;
|
||||||
|
+ skb = q->qs[d].prev;
|
||||||
|
+ len = skb->len;
|
||||||
|
+ __skb_unlink(skb, &q->qs[d]);
|
||||||
|
+ kfree_skb(skb);
|
||||||
|
+ esfq_dec(q, d);
|
||||||
|
+ sch->q.qlen--;
|
||||||
|
+ q->ht[q->hash[d]] = q->depth;
|
||||||
|
+ sch->qstats.drops++;
|
||||||
|
+ sch->qstats.backlog -= len;
|
||||||
|
+ return len;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void esfq_q_enqueue(struct sk_buff *skb, struct esfq_sched_data *q, unsigned int end)
|
||||||
|
+{
|
||||||
|
+ unsigned hash = esfq_hash(q, skb);
|
||||||
|
+ unsigned depth = q->depth;
|
||||||
|
+ esfq_index x;
|
||||||
|
+
|
||||||
|
+ x = q->ht[hash];
|
||||||
|
+ if (x == depth) {
|
||||||
|
+ q->ht[hash] = x = q->dep[depth].next;
|
||||||
|
+ q->hash[x] = hash;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (end == ESFQ_TAIL)
|
||||||
|
+ __skb_queue_tail(&q->qs[x], skb);
|
||||||
|
+ else
|
||||||
|
+ __skb_queue_head(&q->qs[x], skb);
|
||||||
|
+
|
||||||
|
+ esfq_inc(q, x);
|
||||||
|
+ if (q->qs[x].qlen == 1) { /* The flow is new */
|
||||||
|
+ if (q->tail == depth) { /* It is the first flow */
|
||||||
|
+ q->tail = x;
|
||||||
|
+ q->next[x] = x;
|
||||||
|
+ q->allot[x] = q->quantum;
|
||||||
|
+ } else {
|
||||||
|
+ q->next[x] = q->next[q->tail];
|
||||||
|
+ q->next[q->tail] = x;
|
||||||
|
+ q->tail = x;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int esfq_enqueue(struct sk_buff *skb, struct Qdisc* sch)
|
||||||
|
+{
|
||||||
|
+ struct esfq_sched_data *q = qdisc_priv(sch);
|
||||||
|
+ esfq_q_enqueue(skb, q, ESFQ_TAIL);
|
||||||
|
+ sch->qstats.backlog += skb->len;
|
||||||
|
+ if (++sch->q.qlen < q->limit-1) {
|
||||||
|
+ sch->bstats.bytes += skb->len;
|
||||||
|
+ sch->bstats.packets++;
|
||||||
|
+ return 0;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ sch->qstats.drops++;
|
||||||
|
+ esfq_drop(sch);
|
||||||
|
+ return NET_XMIT_CN;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+static int esfq_requeue(struct sk_buff *skb, struct Qdisc* sch)
|
||||||
|
+{
|
||||||
|
+ struct esfq_sched_data *q = qdisc_priv(sch);
|
||||||
|
+ esfq_q_enqueue(skb, q, ESFQ_HEAD);
|
||||||
|
+ sch->qstats.backlog += skb->len;
|
||||||
|
+ if (++sch->q.qlen < q->limit - 1) {
|
||||||
|
+ sch->qstats.requeues++;
|
||||||
|
+ return 0;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ sch->qstats.drops++;
|
||||||
|
+ esfq_drop(sch);
|
||||||
|
+ return NET_XMIT_CN;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static struct sk_buff *esfq_q_dequeue(struct esfq_sched_data *q)
|
||||||
|
+{
|
||||||
|
+ struct sk_buff *skb;
|
||||||
|
+ unsigned depth = q->depth;
|
||||||
|
+ esfq_index a, old_a;
|
||||||
|
+
|
||||||
|
+ /* No active slots */
|
||||||
|
+ if (q->tail == depth)
|
||||||
|
+ return NULL;
|
||||||
|
+
|
||||||
|
+ a = old_a = q->next[q->tail];
|
||||||
|
+
|
||||||
|
+ /* Grab packet */
|
||||||
|
+ skb = __skb_dequeue(&q->qs[a]);
|
||||||
|
+ esfq_dec(q, a);
|
||||||
|
+
|
||||||
|
+ /* Is the slot empty? */
|
||||||
|
+ if (q->qs[a].qlen == 0) {
|
||||||
|
+ q->ht[q->hash[a]] = depth;
|
||||||
|
+ a = q->next[a];
|
||||||
|
+ if (a == old_a) {
|
||||||
|
+ q->tail = depth;
|
||||||
|
+ return skb;
|
||||||
|
+ }
|
||||||
|
+ q->next[q->tail] = a;
|
||||||
|
+ q->allot[a] += q->quantum;
|
||||||
|
+ } else if ((q->allot[a] -= skb->len) <= 0) {
|
||||||
|
+ q->tail = a;
|
||||||
|
+ a = q->next[a];
|
||||||
|
+ q->allot[a] += q->quantum;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return skb;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static struct sk_buff *esfq_dequeue(struct Qdisc* sch)
|
||||||
|
+{
|
||||||
|
+ struct esfq_sched_data *q = qdisc_priv(sch);
|
||||||
|
+ struct sk_buff *skb;
|
||||||
|
+
|
||||||
|
+ skb = esfq_q_dequeue(q);
|
||||||
|
+ if (skb == NULL)
|
||||||
|
+ return NULL;
|
||||||
|
+ sch->q.qlen--;
|
||||||
|
+ sch->qstats.backlog -= skb->len;
|
||||||
|
+ return skb;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void esfq_q_destroy(struct esfq_sched_data *q)
|
||||||
|
+{
|
||||||
|
+ del_timer(&q->perturb_timer);
|
||||||
|
+ if(q->ht)
|
||||||
|
+ kfree(q->ht);
|
||||||
|
+ if(q->dep)
|
||||||
|
+ kfree(q->dep);
|
||||||
|
+ if(q->next)
|
||||||
|
+ kfree(q->next);
|
||||||
|
+ if(q->allot)
|
||||||
|
+ kfree(q->allot);
|
||||||
|
+ if(q->hash)
|
||||||
|
+ kfree(q->hash);
|
||||||
|
+ if(q->qs)
|
||||||
|
+ kfree(q->qs);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void esfq_destroy(struct Qdisc *sch)
|
||||||
|
+{
|
||||||
|
+ struct esfq_sched_data *q = qdisc_priv(sch);
|
||||||
|
+ esfq_q_destroy(q);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+static void esfq_reset(struct Qdisc* sch)
|
||||||
|
+{
|
||||||
|
+ struct sk_buff *skb;
|
||||||
|
+
|
||||||
|
+ while ((skb = esfq_dequeue(sch)) != NULL)
|
||||||
|
+ kfree_skb(skb);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void esfq_perturbation(unsigned long arg)
|
||||||
|
+{
|
||||||
|
+ struct Qdisc *sch = (struct Qdisc*)arg;
|
||||||
|
+ struct esfq_sched_data *q = qdisc_priv(sch);
|
||||||
|
+
|
||||||
|
+ q->perturbation = net_random()&0x1F;
|
||||||
|
+
|
||||||
|
+ if (q->perturb_period) {
|
||||||
|
+ q->perturb_timer.expires = jiffies + q->perturb_period;
|
||||||
|
+ add_timer(&q->perturb_timer);
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static unsigned int esfq_check_hash(unsigned int kind)
|
||||||
|
+{
|
||||||
|
+ switch (kind) {
|
||||||
|
+ case TCA_SFQ_HASH_CTORIGDST:
|
||||||
|
+ case TCA_SFQ_HASH_CTORIGSRC:
|
||||||
|
+ case TCA_SFQ_HASH_CTREPLDST:
|
||||||
|
+ case TCA_SFQ_HASH_CTREPLSRC:
|
||||||
|
+ case TCA_SFQ_HASH_CTNATCHG:
|
||||||
|
+#ifndef CONFIG_NET_SCH_ESFQ_NFCT
|
||||||
|
+ {
|
||||||
|
+ if (net_ratelimit())
|
||||||
|
+ printk(KERN_WARNING "ESFQ: Conntrack hash types disabled in kernel config. Falling back to classic.\n");
|
||||||
|
+ return TCA_SFQ_HASH_CLASSIC;
|
||||||
|
+ }
|
||||||
|
+#endif
|
||||||
|
+ case TCA_SFQ_HASH_CLASSIC:
|
||||||
|
+ case TCA_SFQ_HASH_DST:
|
||||||
|
+ case TCA_SFQ_HASH_SRC:
|
||||||
|
+ case TCA_SFQ_HASH_FWMARK:
|
||||||
|
+ return kind;
|
||||||
|
+ default:
|
||||||
|
+ {
|
||||||
|
+ if (net_ratelimit())
|
||||||
|
+ printk(KERN_WARNING "ESFQ: Unknown hash type. Falling back to classic.\n");
|
||||||
|
+ return TCA_SFQ_HASH_CLASSIC;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int esfq_q_init(struct esfq_sched_data *q, struct rtattr *opt)
|
||||||
|
+{
|
||||||
|
+ struct tc_esfq_qopt *ctl = RTA_DATA(opt);
|
||||||
|
+ esfq_index p = ~0U/2;
|
||||||
|
+ int i;
|
||||||
|
+
|
||||||
|
+ if (opt && opt->rta_len < RTA_LENGTH(sizeof(*ctl)))
|
||||||
|
+ return -EINVAL;
|
||||||
|
+
|
||||||
|
+ q->perturbation = 0;
|
||||||
|
+ q->hash_kind = TCA_SFQ_HASH_CLASSIC;
|
||||||
|
+ q->max_depth = 0;
|
||||||
|
+ if (opt == NULL) {
|
||||||
|
+ q->perturb_period = 0;
|
||||||
|
+ q->hash_divisor = 1024;
|
||||||
|
+ q->tail = q->limit = q->depth = 128;
|
||||||
|
+
|
||||||
|
+ } else {
|
||||||
|
+ struct tc_esfq_qopt *ctl = RTA_DATA(opt);
|
||||||
|
+ if (ctl->quantum)
|
||||||
|
+ q->quantum = ctl->quantum;
|
||||||
|
+ q->perturb_period = ctl->perturb_period*HZ;
|
||||||
|
+ q->hash_divisor = ctl->divisor ? : 1024;
|
||||||
|
+ q->tail = q->limit = q->depth = ctl->flows ? : 128;
|
||||||
|
+
|
||||||
|
+ if ( q->depth > p - 1 )
|
||||||
|
+ return -EINVAL;
|
||||||
|
+
|
||||||
|
+ if (ctl->limit)
|
||||||
|
+ q->limit = min_t(u32, ctl->limit, q->depth);
|
||||||
|
+
|
||||||
|
+ if (ctl->hash_kind) {
|
||||||
|
+ q->hash_kind = esfq_check_hash(ctl->hash_kind);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ q->ht = kmalloc(q->hash_divisor*sizeof(esfq_index), GFP_KERNEL);
|
||||||
|
+ if (!q->ht)
|
||||||
|
+ goto err_case;
|
||||||
|
+ q->dep = kmalloc((1+q->depth*2)*sizeof(struct esfq_head), GFP_KERNEL);
|
||||||
|
+ if (!q->dep)
|
||||||
|
+ goto err_case;
|
||||||
|
+ q->next = kmalloc(q->depth*sizeof(esfq_index), GFP_KERNEL);
|
||||||
|
+ if (!q->next)
|
||||||
|
+ goto err_case;
|
||||||
|
+ q->allot = kmalloc(q->depth*sizeof(short), GFP_KERNEL);
|
||||||
|
+ if (!q->allot)
|
||||||
|
+ goto err_case;
|
||||||
|
+ q->hash = kmalloc(q->depth*sizeof(unsigned short), GFP_KERNEL);
|
||||||
|
+ if (!q->hash)
|
||||||
|
+ goto err_case;
|
||||||
|
+ q->qs = kmalloc(q->depth*sizeof(struct sk_buff_head), GFP_KERNEL);
|
||||||
|
+ if (!q->qs)
|
||||||
|
+ goto err_case;
|
||||||
|
+
|
||||||
|
+ for (i=0; i< q->hash_divisor; i++)
|
||||||
|
+ q->ht[i] = q->depth;
|
||||||
|
+ for (i=0; i<q->depth; i++) {
|
||||||
|
+ skb_queue_head_init(&q->qs[i]);
|
||||||
|
+ q->dep[i+q->depth].next = i+q->depth;
|
||||||
|
+ q->dep[i+q->depth].prev = i+q->depth;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ for (i=0; i<q->depth; i++)
|
||||||
|
+ esfq_link(q, i);
|
||||||
|
+ return 0;
|
||||||
|
+err_case:
|
||||||
|
+ esfq_q_destroy(q);
|
||||||
|
+ return -ENOBUFS;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int esfq_init(struct Qdisc *sch, struct rtattr *opt)
|
||||||
|
+{
|
||||||
|
+ struct esfq_sched_data *q = qdisc_priv(sch);
|
||||||
|
+ int err;
|
||||||
|
+
|
||||||
|
+ q->quantum = psched_mtu(sch->dev); /* default */
|
||||||
|
+ if ((err = esfq_q_init(q, opt)))
|
||||||
|
+ return err;
|
||||||
|
+
|
||||||
|
+ init_timer(&q->perturb_timer);
|
||||||
|
+ q->perturb_timer.data = (unsigned long)sch;
|
||||||
|
+ q->perturb_timer.function = esfq_perturbation;
|
||||||
|
+ if (q->perturb_period) {
|
||||||
|
+ q->perturb_timer.expires = jiffies + q->perturb_period;
|
||||||
|
+ add_timer(&q->perturb_timer);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int esfq_change(struct Qdisc *sch, struct rtattr *opt)
|
||||||
|
+{
|
||||||
|
+ struct esfq_sched_data *q = qdisc_priv(sch);
|
||||||
|
+ struct esfq_sched_data new;
|
||||||
|
+ struct sk_buff *skb;
|
||||||
|
+ int err;
|
||||||
|
+
|
||||||
|
+ /* set up new queue */
|
||||||
|
+ memset(&new, 0, sizeof(struct esfq_sched_data));
|
||||||
|
+ new.quantum = psched_mtu(sch->dev); /* default */
|
||||||
|
+ if ((err = esfq_q_init(&new, opt)))
|
||||||
|
+ return err;
|
||||||
|
+
|
||||||
|
+ /* copy all packets from the old queue to the new queue */
|
||||||
|
+ sch_tree_lock(sch);
|
||||||
|
+ while ((skb = esfq_q_dequeue(q)) != NULL)
|
||||||
|
+ esfq_q_enqueue(skb, &new, ESFQ_TAIL);
|
||||||
|
+
|
||||||
|
+ /* clean up the old queue */
|
||||||
|
+ esfq_q_destroy(q);
|
||||||
|
+
|
||||||
|
+ /* copy elements of the new queue into the old queue */
|
||||||
|
+ q->perturb_period = new.perturb_period;
|
||||||
|
+ q->quantum = new.quantum;
|
||||||
|
+ q->limit = new.limit;
|
||||||
|
+ q->depth = new.depth;
|
||||||
|
+ q->hash_divisor = new.hash_divisor;
|
||||||
|
+ q->hash_kind = new.hash_kind;
|
||||||
|
+ q->tail = new.tail;
|
||||||
|
+ q->max_depth = new.max_depth;
|
||||||
|
+ q->ht = new.ht;
|
||||||
|
+ q->dep = new.dep;
|
||||||
|
+ q->next = new.next;
|
||||||
|
+ q->allot = new.allot;
|
||||||
|
+ q->hash = new.hash;
|
||||||
|
+ q->qs = new.qs;
|
||||||
|
+
|
||||||
|
+ /* finish up */
|
||||||
|
+ if (q->perturb_period) {
|
||||||
|
+ q->perturb_timer.expires = jiffies + q->perturb_period;
|
||||||
|
+ add_timer(&q->perturb_timer);
|
||||||
|
+ } else {
|
||||||
|
+ q->perturbation = 0;
|
||||||
|
+ }
|
||||||
|
+ sch_tree_unlock(sch);
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int esfq_dump(struct Qdisc *sch, struct sk_buff *skb)
|
||||||
|
+{
|
||||||
|
+ struct esfq_sched_data *q = qdisc_priv(sch);
|
||||||
|
+ unsigned char *b = skb->tail;
|
||||||
|
+ struct tc_esfq_qopt opt;
|
||||||
|
+
|
||||||
|
+ opt.quantum = q->quantum;
|
||||||
|
+ opt.perturb_period = q->perturb_period/HZ;
|
||||||
|
+
|
||||||
|
+ opt.limit = q->limit;
|
||||||
|
+ opt.divisor = q->hash_divisor;
|
||||||
|
+ opt.flows = q->depth;
|
||||||
|
+ opt.hash_kind = q->hash_kind;
|
||||||
|
+
|
||||||
|
+ RTA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt);
|
||||||
|
+
|
||||||
|
+ return skb->len;
|
||||||
|
+
|
||||||
|
+rtattr_failure:
|
||||||
|
+ skb_trim(skb, b - skb->data);
|
||||||
|
+ return -1;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static struct Qdisc_ops esfq_qdisc_ops =
|
||||||
|
+{
|
||||||
|
+ .next = NULL,
|
||||||
|
+ .cl_ops = NULL,
|
||||||
|
+ .id = "esfq",
|
||||||
|
+ .priv_size = sizeof(struct esfq_sched_data),
|
||||||
|
+ .enqueue = esfq_enqueue,
|
||||||
|
+ .dequeue = esfq_dequeue,
|
||||||
|
+ .requeue = esfq_requeue,
|
||||||
|
+ .drop = esfq_drop,
|
||||||
|
+ .init = esfq_init,
|
||||||
|
+ .reset = esfq_reset,
|
||||||
|
+ .destroy = esfq_destroy,
|
||||||
|
+ .change = esfq_change,
|
||||||
|
+ .dump = esfq_dump,
|
||||||
|
+ .owner = THIS_MODULE,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static int __init esfq_module_init(void)
|
||||||
|
+{
|
||||||
|
+ return register_qdisc(&esfq_qdisc_ops);
|
||||||
|
+}
|
||||||
|
+static void __exit esfq_module_exit(void)
|
||||||
|
+{
|
||||||
|
+ unregister_qdisc(&esfq_qdisc_ops);
|
||||||
|
+}
|
||||||
|
+module_init(esfq_module_init)
|
||||||
|
+module_exit(esfq_module_exit)
|
||||||
|
+MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,12 @@
|
||||||
|
--- a/arch/mips/Makefile
|
||||||
|
+++ b/arch/mips/Makefile
|
||||||
|
@@ -577,6 +577,9 @@
|
||||||
|
#
|
||||||
|
core-$(CONFIG_TOSHIBA_RBTX4938) += arch/mips/txx9/rbtx4938/
|
||||||
|
|
||||||
|
+# temporary until string.h is fixed
|
||||||
|
+cflags-y += -ffreestanding
|
||||||
|
+
|
||||||
|
cflags-y += -Iinclude/asm-mips/mach-generic
|
||||||
|
drivers-$(CONFIG_PCI) += arch/mips/pci/
|
||||||
|
|
|
@ -0,0 +1,132 @@
|
||||||
|
--- a/fs/jffs2/build.c
|
||||||
|
+++ b/fs/jffs2/build.c
|
||||||
|
@@ -111,6 +111,17 @@
|
||||||
|
dbg_fsbuild("scanned flash completely\n");
|
||||||
|
jffs2_dbg_dump_block_lists_nolock(c);
|
||||||
|
|
||||||
|
+ if (c->flags & (1 << 7)) {
|
||||||
|
+ printk("%s(): unlocking the mtd device... ", __func__);
|
||||||
|
+ if (c->mtd->unlock)
|
||||||
|
+ c->mtd->unlock(c->mtd, 0, c->mtd->size);
|
||||||
|
+ printk("done.\n");
|
||||||
|
+
|
||||||
|
+ printk("%s(): erasing all blocks after the end marker... ", __func__);
|
||||||
|
+ jffs2_erase_pending_blocks(c, -1);
|
||||||
|
+ printk("done.\n");
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
dbg_fsbuild("pass 1 starting\n");
|
||||||
|
c->flags |= JFFS2_SB_FLAG_BUILDING;
|
||||||
|
/* Now scan the directory tree, increasing nlink according to every dirent found. */
|
||||||
|
--- a/fs/jffs2/scan.c
|
||||||
|
+++ b/fs/jffs2/scan.c
|
||||||
|
@@ -72,7 +72,7 @@
|
||||||
|
return ret;
|
||||||
|
if ((ret = jffs2_scan_dirty_space(c, jeb, jeb->free_size)))
|
||||||
|
return ret;
|
||||||
|
- /* Turned wasted size into dirty, since we apparently
|
||||||
|
+ /* Turned wasted size into dirty, since we apparently
|
||||||
|
think it's recoverable now. */
|
||||||
|
jeb->dirty_size += jeb->wasted_size;
|
||||||
|
c->dirty_size += jeb->wasted_size;
|
||||||
|
@@ -144,8 +144,11 @@
|
||||||
|
/* reset summary info for next eraseblock scan */
|
||||||
|
jffs2_sum_reset_collected(s);
|
||||||
|
|
||||||
|
- ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset),
|
||||||
|
- buf_size, s);
|
||||||
|
+ if (c->flags & (1 << 7))
|
||||||
|
+ ret = BLK_STATE_ALLFF;
|
||||||
|
+ else
|
||||||
|
+ ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset),
|
||||||
|
+ buf_size, s);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
@@ -400,7 +403,7 @@
|
||||||
|
if (!ref)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
- /* BEFORE jffs2_build_xattr_subsystem() called,
|
||||||
|
+ /* BEFORE jffs2_build_xattr_subsystem() called,
|
||||||
|
* and AFTER xattr_ref is marked as a dead xref,
|
||||||
|
* ref->xid is used to store 32bit xid, xd is not used
|
||||||
|
* ref->ino is used to store 32bit inode-number, ic is not used
|
||||||
|
@@ -473,7 +476,7 @@
|
||||||
|
struct jffs2_sum_marker *sm;
|
||||||
|
void *sumptr = NULL;
|
||||||
|
uint32_t sumlen;
|
||||||
|
-
|
||||||
|
+
|
||||||
|
if (!buf_size) {
|
||||||
|
/* XIP case. Just look, point at the summary if it's there */
|
||||||
|
sm = (void *)buf + c->sector_size - sizeof(*sm);
|
||||||
|
@@ -489,9 +492,9 @@
|
||||||
|
buf_len = sizeof(*sm);
|
||||||
|
|
||||||
|
/* Read as much as we want into the _end_ of the preallocated buffer */
|
||||||
|
- err = jffs2_fill_scan_buf(c, buf + buf_size - buf_len,
|
||||||
|
+ err = jffs2_fill_scan_buf(c, buf + buf_size - buf_len,
|
||||||
|
jeb->offset + c->sector_size - buf_len,
|
||||||
|
- buf_len);
|
||||||
|
+ buf_len);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
@@ -510,9 +513,9 @@
|
||||||
|
}
|
||||||
|
if (buf_len < sumlen) {
|
||||||
|
/* Need to read more so that the entire summary node is present */
|
||||||
|
- err = jffs2_fill_scan_buf(c, sumptr,
|
||||||
|
+ err = jffs2_fill_scan_buf(c, sumptr,
|
||||||
|
jeb->offset + c->sector_size - sumlen,
|
||||||
|
- sumlen - buf_len);
|
||||||
|
+ sumlen - buf_len);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
@@ -525,7 +528,7 @@
|
||||||
|
|
||||||
|
if (buf_size && sumlen > buf_size)
|
||||||
|
kfree(sumptr);
|
||||||
|
- /* If it returns with a real error, bail.
|
||||||
|
+ /* If it returns with a real error, bail.
|
||||||
|
If it returns positive, that's a block classification
|
||||||
|
(i.e. BLK_STATE_xxx) so return that too.
|
||||||
|
If it returns zero, fall through to full scan. */
|
||||||
|
@@ -546,6 +549,17 @@
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ if ((buf[0] == 0xde) &&
|
||||||
|
+ (buf[1] == 0xad) &&
|
||||||
|
+ (buf[2] == 0xc0) &&
|
||||||
|
+ (buf[3] == 0xde)) {
|
||||||
|
+ /* end of filesystem. erase everything after this point */
|
||||||
|
+ printk("%s(): End of filesystem marker found at 0x%x\n", __func__, jeb->offset);
|
||||||
|
+ c->flags |= (1 << 7);
|
||||||
|
+
|
||||||
|
+ return BLK_STATE_ALLFF;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
/* We temporarily use 'ofs' as a pointer into the buffer/jeb */
|
||||||
|
ofs = 0;
|
||||||
|
|
||||||
|
@@ -671,7 +685,7 @@
|
||||||
|
scan_end = buf_len;
|
||||||
|
goto more_empty;
|
||||||
|
}
|
||||||
|
-
|
||||||
|
+
|
||||||
|
/* See how much more there is to read in this eraseblock... */
|
||||||
|
buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
|
||||||
|
if (!buf_len) {
|
||||||
|
@@ -907,7 +921,7 @@
|
||||||
|
|
||||||
|
D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, unchecked 0x%08x, used 0x%08x, wasted 0x%08x\n",
|
||||||
|
jeb->offset,jeb->free_size, jeb->dirty_size, jeb->unchecked_size, jeb->used_size, jeb->wasted_size));
|
||||||
|
-
|
||||||
|
+
|
||||||
|
/* mark_node_obsolete can add to wasted !! */
|
||||||
|
if (jeb->wasted_size) {
|
||||||
|
jeb->dirty_size += jeb->wasted_size;
|
|
@ -0,0 +1,9 @@
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/include/asm-powerpc/segment.h
|
||||||
|
@@ -0,0 +1,6 @@
|
||||||
|
+#ifndef _ASM_SEGMENT_H
|
||||||
|
+#define _ASM_SEGMENT_H
|
||||||
|
+
|
||||||
|
+/* Only here because we have some old header files that expect it.. */
|
||||||
|
+
|
||||||
|
+#endif /* _ASM_SEGMENT_H */
|
|
@ -0,0 +1,42 @@
|
||||||
|
--- a/drivers/net/r8169.c
|
||||||
|
+++ b/drivers/net/r8169.c
|
||||||
|
@@ -1519,7 +1519,7 @@
|
||||||
|
.hw_start = rtl_hw_start_8169,
|
||||||
|
.region = 1,
|
||||||
|
.align = 0,
|
||||||
|
- .intr_event = SYSErr | LinkChg | RxOverflow |
|
||||||
|
+ .intr_event = LinkChg | RxOverflow |
|
||||||
|
RxFIFOOver | TxErr | TxOK | RxOK | RxErr,
|
||||||
|
.napi_event = RxFIFOOver | TxErr | TxOK | RxOK | RxOverflow,
|
||||||
|
.msi = 0
|
||||||
|
@@ -1528,7 +1528,7 @@
|
||||||
|
.hw_start = rtl_hw_start_8168,
|
||||||
|
.region = 2,
|
||||||
|
.align = 8,
|
||||||
|
- .intr_event = SYSErr | LinkChg | RxOverflow |
|
||||||
|
+ .intr_event = LinkChg | RxOverflow |
|
||||||
|
TxErr | TxOK | RxOK | RxErr,
|
||||||
|
.napi_event = TxErr | TxOK | RxOK | RxOverflow,
|
||||||
|
.msi = RTL_FEATURE_MSI
|
||||||
|
@@ -1537,7 +1537,7 @@
|
||||||
|
.hw_start = rtl_hw_start_8101,
|
||||||
|
.region = 2,
|
||||||
|
.align = 8,
|
||||||
|
- .intr_event = SYSErr | LinkChg | RxOverflow | PCSTimeout |
|
||||||
|
+ .intr_event = LinkChg | RxOverflow | PCSTimeout |
|
||||||
|
RxFIFOOver | TxErr | TxOK | RxOK | RxErr,
|
||||||
|
.napi_event = RxFIFOOver | TxErr | TxOK | RxOK | RxOverflow,
|
||||||
|
.msi = RTL_FEATURE_MSI
|
||||||
|
@@ -2873,10 +2873,12 @@
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
+#if 0
|
||||||
|
if (unlikely(status & SYSErr)) {
|
||||||
|
rtl8169_pcierr_interrupt(dev);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
+#endif
|
||||||
|
|
||||||
|
if (status & LinkChg)
|
||||||
|
rtl8169_check_link_status(dev, tp, ioaddr);
|
7776
target/linux/generic-2.6/patches-2.6.27/209-mini_fo.patch
Normal file
7776
target/linux/generic-2.6/patches-2.6.27/209-mini_fo.patch
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,143 @@
|
||||||
|
--- a/fs/mini_fo/main.c
|
||||||
|
+++ b/fs/mini_fo/main.c
|
||||||
|
@@ -79,6 +79,7 @@
|
||||||
|
* of the new inode's fields
|
||||||
|
*/
|
||||||
|
|
||||||
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
|
||||||
|
/*
|
||||||
|
* original: inode = iget(sb, hidden_inode->i_ino);
|
||||||
|
*/
|
||||||
|
@@ -87,6 +88,13 @@
|
||||||
|
err = -EACCES; /* should be impossible??? */
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
+#else
|
||||||
|
+ inode = mini_fo_iget(sb, iunique(sb, 25));
|
||||||
|
+ if (IS_ERR(inode)) {
|
||||||
|
+ err = PTR_ERR(inode);
|
||||||
|
+ goto out;
|
||||||
|
+ }
|
||||||
|
+#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* interpose the inode if not already interposed
|
||||||
|
@@ -184,9 +192,9 @@
|
||||||
|
hidden_root = ERR_PTR(err);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
- hidden_root = nd.dentry;
|
||||||
|
- stopd(sb)->base_dir_dentry = nd.dentry;
|
||||||
|
- stopd(sb)->hidden_mnt = nd.mnt;
|
||||||
|
+ hidden_root = nd_get_dentry(&nd);
|
||||||
|
+ stopd(sb)->base_dir_dentry = nd_get_dentry(&nd);
|
||||||
|
+ stopd(sb)->hidden_mnt = nd_get_mnt(&nd);
|
||||||
|
|
||||||
|
} else if(!strncmp("sto=", options, 4)) {
|
||||||
|
/* parse the storage dir */
|
||||||
|
@@ -204,9 +212,9 @@
|
||||||
|
hidden_root2 = ERR_PTR(err);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
- hidden_root2 = nd2.dentry;
|
||||||
|
- stopd(sb)->storage_dir_dentry = nd2.dentry;
|
||||||
|
- stopd(sb)->hidden_mnt2 = nd2.mnt;
|
||||||
|
+ hidden_root2 = nd_get_dentry(&nd2);
|
||||||
|
+ stopd(sb)->storage_dir_dentry = nd_get_dentry(&nd2);
|
||||||
|
+ stopd(sb)->hidden_mnt2 = nd_get_mnt(&nd2);
|
||||||
|
stohs2(sb) = hidden_root2->d_sb;
|
||||||
|
|
||||||
|
/* validate storage dir, this is done in
|
||||||
|
--- a/fs/mini_fo/mini_fo.h
|
||||||
|
+++ b/fs/mini_fo/mini_fo.h
|
||||||
|
@@ -302,6 +302,10 @@
|
||||||
|
extern int mini_fo_cp_cont(dentry_t *tgt_dentry, struct vfsmount *tgt_mnt,
|
||||||
|
dentry_t *src_dentry, struct vfsmount *src_mnt);
|
||||||
|
|
||||||
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)
|
||||||
|
+extern struct inode *mini_fo_iget(struct super_block *sb, unsigned long ino);
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
|
||||||
|
extern int mini_fo_create(inode_t *dir, dentry_t *dentry, int mode, struct nameidata *nd);
|
||||||
|
|
||||||
|
@@ -501,6 +505,29 @@
|
||||||
|
#endif /* if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) */
|
||||||
|
#endif /* __KERNEL__ */
|
||||||
|
|
||||||
|
+
|
||||||
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)
|
||||||
|
+static inline dentry_t *nd_get_dentry(struct nameidata *nd)
|
||||||
|
+{
|
||||||
|
+ return (nd->path.dentry);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline struct vfsmount *nd_get_mnt(struct nameidata *nd)
|
||||||
|
+{
|
||||||
|
+ return (nd->path.mnt);
|
||||||
|
+}
|
||||||
|
+#else
|
||||||
|
+static inline dentry_t *nd_get_dentry(struct nameidata *nd)
|
||||||
|
+{
|
||||||
|
+ return (nd->dentry);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static inline struct vfsmount *nd_get_mnt(struct nameidata *nd)
|
||||||
|
+{
|
||||||
|
+ return (nd->mnt);
|
||||||
|
+}
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
/*
|
||||||
|
* Definitions for user and kernel code
|
||||||
|
*/
|
||||||
|
--- a/fs/mini_fo/super.c
|
||||||
|
+++ b/fs/mini_fo/super.c
|
||||||
|
@@ -262,10 +262,31 @@
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)
|
||||||
|
+struct inode *
|
||||||
|
+mini_fo_iget(struct super_block *sb, unsigned long ino)
|
||||||
|
+{
|
||||||
|
+ struct inode *inode;
|
||||||
|
+
|
||||||
|
+ inode = iget_locked(sb, ino);
|
||||||
|
+ if (!inode)
|
||||||
|
+ return ERR_PTR(-ENOMEM);
|
||||||
|
+
|
||||||
|
+ if (!(inode->i_state & I_NEW))
|
||||||
|
+ return inode;
|
||||||
|
+
|
||||||
|
+ mini_fo_read_inode(inode);
|
||||||
|
+
|
||||||
|
+ unlock_new_inode(inode);
|
||||||
|
+ return inode;
|
||||||
|
+}
|
||||||
|
+#endif /* if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25) */
|
||||||
|
|
||||||
|
struct super_operations mini_fo_sops =
|
||||||
|
{
|
||||||
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
|
||||||
|
read_inode: mini_fo_read_inode,
|
||||||
|
+#endif
|
||||||
|
#if defined(FIST_DEBUG) || defined(FIST_FILTER_SCA)
|
||||||
|
write_inode: mini_fo_write_inode,
|
||||||
|
#endif /* defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) */
|
||||||
|
--- a/fs/mini_fo/aux.c
|
||||||
|
+++ b/fs/mini_fo/aux.c
|
||||||
|
@@ -164,11 +164,11 @@
|
||||||
|
err = vfs_path_lookup(mnt->mnt_root, mnt, bpath+1, 0, &nd);
|
||||||
|
|
||||||
|
/* validate */
|
||||||
|
- if (err || !nd.dentry || !nd.dentry->d_inode) {
|
||||||
|
+ if (err || !nd_get_dentry(&nd) || !nd_get_dentry(&nd)->d_inode) {
|
||||||
|
printk(KERN_CRIT "mini_fo: bpath_walk: path_walk failed.\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
- return nd.dentry;
|
||||||
|
+ return nd_get_dentry(&nd);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
--- a/fs/mini_fo/meta.c
|
||||||
|
+++ b/fs/mini_fo/meta.c
|
||||||
|
@@ -442,6 +442,11 @@
|
||||||
|
S_IRUSR | S_IWUSR);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ /* $%& err, is this correct? */
|
||||||
|
+ meta_mnt = stopd(dentry->d_inode->i_sb)->hidden_mnt2;
|
||||||
|
+ mntget(meta_mnt);
|
||||||
|
+
|
||||||
|
/* open META-file for writing */
|
||||||
|
meta_file = dentry_open(meta_dentry, meta_mnt, 0x1);
|
||||||
|
if(!meta_file || IS_ERR(meta_file)) {
|
||||||
|
@@ -535,6 +540,11 @@
|
||||||
|
meta_dentry, S_IRUSR | S_IWUSR);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ /* $%& err, is this correct? */
|
||||||
|
+ meta_mnt = stopd(dentry->d_inode->i_sb)->hidden_mnt2;
|
||||||
|
+ mntget(meta_mnt);
|
||||||
|
+
|
||||||
|
/* open META-file for writing */
|
||||||
|
meta_file = dentry_open(meta_dentry, meta_mnt, 0x1);
|
||||||
|
if(!meta_file || IS_ERR(meta_file)) {
|
||||||
|
@@ -671,14 +681,16 @@
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+ /* $%& err, is this correct? */
|
||||||
|
+ meta_mnt = stopd(dentry->d_inode->i_sb)->hidden_mnt2;
|
||||||
|
+ mntget(meta_mnt);
|
||||||
|
+
|
||||||
|
/* open META-file for writing */
|
||||||
|
meta_file = dentry_open(meta_dentry, meta_mnt, 0x1);
|
||||||
|
if(!meta_file || IS_ERR(meta_file)) {
|
||||||
|
printk(KERN_CRIT "mini_fo: meta_sync_d_list: \
|
||||||
|
ERROR opening meta file.\n");
|
||||||
|
- /* we don't mntget so we dont't mntput (for now)
|
||||||
|
- * mntput(meta_mnt);
|
||||||
|
- */
|
||||||
|
+ mntput(meta_mnt);
|
||||||
|
dput(meta_dentry);
|
||||||
|
err = -1;
|
||||||
|
goto out;
|
||||||
|
@@ -811,14 +823,16 @@
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+ /* $%& err, is this correct? */
|
||||||
|
+ meta_mnt = stopd(dentry->d_inode->i_sb)->hidden_mnt2;
|
||||||
|
+ mntget(meta_mnt);
|
||||||
|
+
|
||||||
|
/* open META-file for writing */
|
||||||
|
meta_file = dentry_open(meta_dentry, meta_mnt, 0x1);
|
||||||
|
if(!meta_file || IS_ERR(meta_file)) {
|
||||||
|
printk(KERN_CRIT "mini_fo: meta_sync_r_list: \
|
||||||
|
ERROR opening meta file.\n");
|
||||||
|
- /* we don't mntget so we dont't mntput (for now)
|
||||||
|
- * mntput(meta_mnt);
|
||||||
|
- */
|
||||||
|
+ mntput(meta_mnt);
|
||||||
|
dput(meta_dentry);
|
||||||
|
err = -1;
|
||||||
|
goto out;
|
|
@ -0,0 +1,37 @@
|
||||||
|
--- a/fs/mini_fo/super.c
|
||||||
|
+++ b/fs/mini_fo/super.c
|
||||||
|
@@ -84,6 +84,7 @@
|
||||||
|
#endif /* defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) */
|
||||||
|
|
||||||
|
|
||||||
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
|
||||||
|
STATIC void
|
||||||
|
mini_fo_put_inode(inode_t *inode)
|
||||||
|
{
|
||||||
|
@@ -99,6 +100,7 @@
|
||||||
|
if (atomic_read(&inode->i_count) == 1)
|
||||||
|
inode->i_nlink = 0;
|
||||||
|
}
|
||||||
|
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) */
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(FIST_DEBUG) || defined(FIST_FILTER_SCA)
|
||||||
|
@@ -238,7 +240,7 @@
|
||||||
|
* dies.
|
||||||
|
*/
|
||||||
|
STATIC void
|
||||||
|
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
|
||||||
|
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26))
|
||||||
|
mini_fo_umount_begin(struct vfsmount *mnt, int flags)
|
||||||
|
{
|
||||||
|
struct vfsmount *hidden_mnt;
|
||||||
|
@@ -290,7 +292,9 @@
|
||||||
|
#if defined(FIST_DEBUG) || defined(FIST_FILTER_SCA)
|
||||||
|
write_inode: mini_fo_write_inode,
|
||||||
|
#endif /* defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) */
|
||||||
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
|
||||||
|
put_inode: mini_fo_put_inode,
|
||||||
|
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) */
|
||||||
|
#if defined(FIST_DEBUG) || defined(FIST_FILTER_SCA)
|
||||||
|
delete_inode: mini_fo_delete_inode,
|
||||||
|
#endif /* defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) */
|
|
@ -0,0 +1,31 @@
|
||||||
|
--- a/fs/mini_fo/inode.c
|
||||||
|
+++ b/fs/mini_fo/inode.c
|
||||||
|
@@ -439,7 +439,7 @@
|
||||||
|
int err=0;
|
||||||
|
dentry_t *hidden_sto_dentry;
|
||||||
|
dentry_t *hidden_sto_dir_dentry;
|
||||||
|
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
|
||||||
|
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27))
|
||||||
|
umode_t mode;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
@@ -466,7 +466,7 @@
|
||||||
|
down(&hidden_sto_dir_dentry->d_inode->i_sem);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
|
||||||
|
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27))
|
||||||
|
mode = S_IALLUGO;
|
||||||
|
err = vfs_symlink(hidden_sto_dir_dentry->d_inode,
|
||||||
|
hidden_sto_dentry, symname, mode);
|
||||||
|
@@ -1151,7 +1151,9 @@
|
||||||
|
* goto out;
|
||||||
|
*/
|
||||||
|
|
||||||
|
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
|
||||||
|
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
|
||||||
|
+ err = generic_permission(hidden_inode, mask, NULL);
|
||||||
|
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
|
||||||
|
err = permission(hidden_inode, mask, nd);
|
||||||
|
#else
|
||||||
|
err = permission(hidden_inode, mask);
|
|
@ -0,0 +1,42 @@
|
||||||
|
--- a/lib/kobject_uevent.c
|
||||||
|
+++ b/lib/kobject_uevent.c
|
||||||
|
@@ -29,7 +29,8 @@
|
||||||
|
char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH;
|
||||||
|
static DEFINE_SPINLOCK(sequence_lock);
|
||||||
|
#if defined(CONFIG_NET)
|
||||||
|
-static struct sock *uevent_sock;
|
||||||
|
+struct sock *uevent_sock = NULL;
|
||||||
|
+EXPORT_SYMBOL_GPL(uevent_sock);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* the strings here must match the enum in include/linux/kobject.h */
|
||||||
|
@@ -42,6 +43,18 @@
|
||||||
|
[KOBJ_OFFLINE] = "offline",
|
||||||
|
};
|
||||||
|
|
||||||
|
+u64 uevent_next_seqnum(void)
|
||||||
|
+{
|
||||||
|
+ u64 seq;
|
||||||
|
+
|
||||||
|
+ spin_lock(&sequence_lock);
|
||||||
|
+ seq = ++uevent_seqnum;
|
||||||
|
+ spin_unlock(&sequence_lock);
|
||||||
|
+
|
||||||
|
+ return seq;
|
||||||
|
+}
|
||||||
|
+EXPORT_SYMBOL_GPL(uevent_next_seqnum);
|
||||||
|
+
|
||||||
|
/**
|
||||||
|
* kobject_action_type - translate action string to numeric type
|
||||||
|
*
|
||||||
|
@@ -194,9 +207,7 @@
|
||||||
|
kobj->state_remove_uevent_sent = 1;
|
||||||
|
|
||||||
|
/* we will send an event, so request a new sequence number */
|
||||||
|
- spin_lock(&sequence_lock);
|
||||||
|
- seq = ++uevent_seqnum;
|
||||||
|
- spin_unlock(&sequence_lock);
|
||||||
|
+ seq = uevent_next_seqnum();
|
||||||
|
retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);
|
||||||
|
if (retval)
|
||||||
|
goto exit;
|
|
@ -0,0 +1,11 @@
|
||||||
|
--- a/sound/core/Kconfig
|
||||||
|
+++ b/sound/core/Kconfig
|
||||||
|
@@ -7,7 +7,7 @@
|
||||||
|
select SND_TIMER
|
||||||
|
|
||||||
|
config SND_HWDEP
|
||||||
|
- tristate
|
||||||
|
+ tristate "Sound hardware support"
|
||||||
|
|
||||||
|
config SND_RAWMIDI
|
||||||
|
tristate
|
|
@ -0,0 +1,18 @@
|
||||||
|
--- a/drivers/leds/Kconfig
|
||||||
|
+++ b/drivers/leds/Kconfig
|
||||||
|
@@ -206,4 +206,8 @@
|
||||||
|
This allows LEDs to be initialised in the ON state.
|
||||||
|
If unsure, say Y.
|
||||||
|
|
||||||
|
+config LEDS_TRIGGER_MORSE
|
||||||
|
+ tristate "LED Morse Trigger"
|
||||||
|
+ depends on LEDS_TRIGGERS
|
||||||
|
+
|
||||||
|
endif # NEW_LEDS
|
||||||
|
--- a/drivers/leds/Makefile
|
||||||
|
+++ b/drivers/leds/Makefile
|
||||||
|
@@ -29,3 +29,4 @@
|
||||||
|
obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK) += ledtrig-ide-disk.o
|
||||||
|
obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o
|
||||||
|
obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o
|
||||||
|
+obj-$(CONFIG_LEDS_TRIGGER_MORSE) += ledtrig-morse.o
|
25
target/linux/generic-2.6/patches-2.6.27/401-led_alix.patch
Normal file
25
target/linux/generic-2.6/patches-2.6.27/401-led_alix.patch
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
--- a/drivers/leds/Kconfig
|
||||||
|
+++ b/drivers/leds/Kconfig
|
||||||
|
@@ -77,6 +77,12 @@
|
||||||
|
help
|
||||||
|
This option enables support for the PCEngines WRAP programmable LEDs.
|
||||||
|
|
||||||
|
+config LEDS_ALIX
|
||||||
|
+ tristate "LED Support for the ALIX 2/3 boards"
|
||||||
|
+ depends on LEDS_CLASS
|
||||||
|
+ help
|
||||||
|
+ This option enables support for the three LEDs on the PCEngines ALIX 2/3 boards.
|
||||||
|
+
|
||||||
|
config LEDS_H1940
|
||||||
|
tristate "LED Support for iPAQ H1940 device"
|
||||||
|
depends on LEDS_CLASS && ARCH_H1940
|
||||||
|
--- a/drivers/leds/Makefile
|
||||||
|
+++ b/drivers/leds/Makefile
|
||||||
|
@@ -13,6 +13,7 @@
|
||||||
|
obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o
|
||||||
|
obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o
|
||||||
|
obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o
|
||||||
|
+obj-$(CONFIG_LEDS_ALIX) += leds-alix.o
|
||||||
|
obj-$(CONFIG_LEDS_H1940) += leds-h1940.o
|
||||||
|
obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o
|
||||||
|
obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o
|
|
@ -0,0 +1,21 @@
|
||||||
|
--- a/drivers/leds/Kconfig
|
||||||
|
+++ b/drivers/leds/Kconfig
|
||||||
|
@@ -216,4 +216,11 @@
|
||||||
|
tristate "LED Morse Trigger"
|
||||||
|
depends on LEDS_TRIGGERS
|
||||||
|
|
||||||
|
+config LEDS_TRIGGER_NETDEV
|
||||||
|
+ tristate "LED Netdev Trigger"
|
||||||
|
+ depends on LEDS_TRIGGERS
|
||||||
|
+ help
|
||||||
|
+ This allows LEDs to be controlled by network device activity.
|
||||||
|
+ If unsure, say Y.
|
||||||
|
+
|
||||||
|
endif # NEW_LEDS
|
||||||
|
--- a/drivers/leds/Makefile
|
||||||
|
+++ b/drivers/leds/Makefile
|
||||||
|
@@ -31,3 +31,4 @@
|
||||||
|
obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o
|
||||||
|
obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o
|
||||||
|
obj-$(CONFIG_LEDS_TRIGGER_MORSE) += ledtrig-morse.o
|
||||||
|
+obj-$(CONFIG_LEDS_TRIGGER_NETDEV) += ledtrig-netdev.o
|
|
@ -0,0 +1,16 @@
|
||||||
|
--- a/drivers/rtc/rtc-ds1672.c
|
||||||
|
+++ b/drivers/rtc/rtc-ds1672.c
|
||||||
|
@@ -13,10 +13,10 @@
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/rtc.h>
|
||||||
|
|
||||||
|
-#define DRV_VERSION "0.3"
|
||||||
|
+#define DRV_VERSION "0.4"
|
||||||
|
|
||||||
|
-/* Addresses to scan: none. This chip cannot be detected. */
|
||||||
|
-static const unsigned short normal_i2c[] = { I2C_CLIENT_END };
|
||||||
|
+/* Addresses to scan: 0x68 */
|
||||||
|
+static const unsigned short normal_i2c[] = { 0x68, I2C_CLIENT_END };
|
||||||
|
|
||||||
|
/* Insmod parameters */
|
||||||
|
I2C_CLIENT_INSMOD;
|
|
@ -0,0 +1,30 @@
|
||||||
|
--- a/drivers/input/misc/Kconfig
|
||||||
|
+++ b/drivers/input/misc/Kconfig
|
||||||
|
@@ -207,4 +207,20 @@
|
||||||
|
Say Y here if you want to support the built-in real time clock
|
||||||
|
of the HP SDC controller.
|
||||||
|
|
||||||
|
+config INPUT_GPIO_BUTTONS
|
||||||
|
+ tristate "Polled GPIO buttons interface"
|
||||||
|
+ depends on GENERIC_GPIO
|
||||||
|
+ select INPUT_POLLDEV
|
||||||
|
+ help
|
||||||
|
+ This driver implements support for buttons connected
|
||||||
|
+ to GPIO pins of various CPUs (and some other chips).
|
||||||
|
+
|
||||||
|
+ Say Y here if your device has buttons connected
|
||||||
|
+ directly to such GPIO pins. Your board-specific
|
||||||
|
+ setup logic must also provide a platform device,
|
||||||
|
+ with configuration data saying which GPIOs are used.
|
||||||
|
+
|
||||||
|
+ To compile this driver as a module, choose M here: the
|
||||||
|
+ module will be called gpio-buttons.
|
||||||
|
+
|
||||||
|
endif
|
||||||
|
--- a/drivers/input/misc/Makefile
|
||||||
|
+++ b/drivers/input/misc/Makefile
|
||||||
|
@@ -20,3 +20,4 @@
|
||||||
|
obj-$(CONFIG_INPUT_UINPUT) += uinput.o
|
||||||
|
obj-$(CONFIG_INPUT_APANEL) += apanel.o
|
||||||
|
obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
|
||||||
|
+obj-$(CONFIG_INPUT_GPIO_BUTTONS) += gpio_buttons.o
|
26
target/linux/generic-2.6/patches-2.6.27/420-gpiodev.patch
Normal file
26
target/linux/generic-2.6/patches-2.6.27/420-gpiodev.patch
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
--- a/drivers/char/Kconfig
|
||||||
|
+++ b/drivers/char/Kconfig
|
||||||
|
@@ -1010,6 +1010,13 @@
|
||||||
|
|
||||||
|
If compiled as a module, it will be called cs5535_gpio.
|
||||||
|
|
||||||
|
+config GPIO_DEVICE
|
||||||
|
+ tristate "GPIO device support"
|
||||||
|
+ depends on GENERIC_GPIO
|
||||||
|
+ help
|
||||||
|
+ Say Y to enable Linux GPIO device support. This allows control of
|
||||||
|
+ GPIO pins using a character device
|
||||||
|
+
|
||||||
|
config GPIO_VR41XX
|
||||||
|
tristate "NEC VR4100 series General-purpose I/O Unit support"
|
||||||
|
depends on CPU_VR41XX
|
||||||
|
--- a/drivers/char/Makefile
|
||||||
|
+++ b/drivers/char/Makefile
|
||||||
|
@@ -94,6 +94,7 @@
|
||||||
|
obj-$(CONFIG_PC8736x_GPIO) += pc8736x_gpio.o
|
||||||
|
obj-$(CONFIG_NSC_GPIO) += nsc_gpio.o
|
||||||
|
obj-$(CONFIG_CS5535_GPIO) += cs5535_gpio.o
|
||||||
|
+obj-$(CONFIG_GPIO_DEVICE) += gpio_dev.o
|
||||||
|
obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o
|
||||||
|
obj-$(CONFIG_GPIO_TB0219) += tb0219.o
|
||||||
|
obj-$(CONFIG_TELCLOCK) += tlclk.o
|
|
@ -0,0 +1,17 @@
|
||||||
|
--- a/fs/Kconfig
|
||||||
|
+++ b/fs/Kconfig
|
||||||
|
@@ -421,6 +421,7 @@
|
||||||
|
|
||||||
|
source "fs/xfs/Kconfig"
|
||||||
|
source "fs/gfs2/Kconfig"
|
||||||
|
+source "fs/yaffs2/Kconfig"
|
||||||
|
|
||||||
|
config OCFS2_FS
|
||||||
|
tristate "OCFS2 file system support"
|
||||||
|
--- a/fs/Makefile
|
||||||
|
+++ b/fs/Makefile
|
||||||
|
@@ -124,3 +124,4 @@
|
||||||
|
obj-$(CONFIG_DEBUG_FS) += debugfs/
|
||||||
|
obj-$(CONFIG_OCFS2_FS) += ocfs2/
|
||||||
|
obj-$(CONFIG_GFS2_FS) += gfs2/
|
||||||
|
+obj-$(CONFIG_YAFFS_FS) += yaffs2/
|
|
@ -0,0 +1,92 @@
|
||||||
|
--- a/fs/yaffs2/yaffs_fs.c
|
||||||
|
+++ b/fs/yaffs2/yaffs_fs.c
|
||||||
|
@@ -181,7 +181,13 @@
|
||||||
|
#else
|
||||||
|
static int yaffs_statfs(struct super_block *sb, struct statfs *buf);
|
||||||
|
#endif
|
||||||
|
+
|
||||||
|
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25))
|
||||||
|
+static struct inode *yaffs_iget(struct super_block *sb, unsigned long ino);
|
||||||
|
+#else
|
||||||
|
static void yaffs_read_inode(struct inode *inode);
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
|
||||||
|
static void yaffs_put_inode(struct inode *inode);
|
||||||
|
static void yaffs_delete_inode(struct inode *);
|
||||||
|
@@ -284,7 +290,9 @@
|
||||||
|
|
||||||
|
static struct super_operations yaffs_super_ops = {
|
||||||
|
.statfs = yaffs_statfs,
|
||||||
|
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25))
|
||||||
|
.read_inode = yaffs_read_inode,
|
||||||
|
+#endif
|
||||||
|
.put_inode = yaffs_put_inode,
|
||||||
|
.put_super = yaffs_put_super,
|
||||||
|
.delete_inode = yaffs_delete_inode,
|
||||||
|
@@ -844,11 +852,17 @@
|
||||||
|
T(YAFFS_TRACE_OS,
|
||||||
|
(KERN_DEBUG "yaffs_get_inode for object %d\n", obj->objectId));
|
||||||
|
|
||||||
|
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25))
|
||||||
|
+ inode = yaffs_iget(sb, obj->objectId);
|
||||||
|
+ if (IS_ERR(inode))
|
||||||
|
+ return NULL;
|
||||||
|
+#else
|
||||||
|
inode = iget(sb, obj->objectId);
|
||||||
|
|
||||||
|
/* NB Side effect: iget calls back to yaffs_read_inode(). */
|
||||||
|
/* iget also increments the inode's i_count */
|
||||||
|
/* NB You can't be holding grossLock or deadlock will happen! */
|
||||||
|
+#endif
|
||||||
|
|
||||||
|
return inode;
|
||||||
|
}
|
||||||
|
@@ -1427,6 +1441,39 @@
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25))
|
||||||
|
+static struct inode *yaffs_iget(struct super_block *sb, unsigned long ino)
|
||||||
|
+{
|
||||||
|
+ yaffs_Object *obj;
|
||||||
|
+ yaffs_Device *dev = yaffs_SuperToDevice(sb);
|
||||||
|
+ struct inode *inode;
|
||||||
|
+
|
||||||
|
+ T(YAFFS_TRACE_OS,
|
||||||
|
+ (KERN_DEBUG "yaffs_iget for %lu\n", ino));
|
||||||
|
+
|
||||||
|
+ inode = iget_locked(sb, ino);
|
||||||
|
+ if (!inode)
|
||||||
|
+ return ERR_PTR(-ENOMEM);
|
||||||
|
+ if (!(inode->i_state & I_NEW))
|
||||||
|
+ return inode;
|
||||||
|
+
|
||||||
|
+ /* NB This is called as a side effect of other functions, but
|
||||||
|
+ * we had to release the lock to prevent deadlocks, so
|
||||||
|
+ * need to lock again.
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+ yaffs_GrossLock(dev);
|
||||||
|
+
|
||||||
|
+ obj = yaffs_FindObjectByNumber(dev, inode->i_ino);
|
||||||
|
+
|
||||||
|
+ yaffs_FillInodeFromObject(inode, obj);
|
||||||
|
+
|
||||||
|
+ yaffs_GrossUnlock(dev);
|
||||||
|
+
|
||||||
|
+ unlock_new_inode(inode);
|
||||||
|
+ return inode;
|
||||||
|
+}
|
||||||
|
+#else
|
||||||
|
static void yaffs_read_inode(struct inode *inode)
|
||||||
|
{
|
||||||
|
/* NB This is called as a side effect of other functions, but
|
||||||
|
@@ -1448,6 +1495,7 @@
|
||||||
|
|
||||||
|
yaffs_GrossUnlock(dev);
|
||||||
|
}
|
||||||
|
+#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)) */
|
||||||
|
|
||||||
|
static LIST_HEAD(yaffs_dev_list);
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
--- a/fs/yaffs2/yaffs_fs.c
|
||||||
|
+++ b/fs/yaffs2/yaffs_fs.c
|
||||||
|
@@ -76,6 +76,12 @@
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26))
|
||||||
|
+#define YPROC_ROOT &proc_root
|
||||||
|
+#else
|
||||||
|
+#define YPROC_ROOT NULL
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
|
||||||
|
#define WRITE_SIZE_STR "writesize"
|
||||||
|
#define WRITE_SIZE(mtd) (mtd)->writesize
|
||||||
|
@@ -189,7 +195,9 @@
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26))
|
||||||
|
static void yaffs_put_inode(struct inode *inode);
|
||||||
|
+#endif
|
||||||
|
static void yaffs_delete_inode(struct inode *);
|
||||||
|
static void yaffs_clear_inode(struct inode *);
|
||||||
|
|
||||||
|
@@ -293,7 +301,9 @@
|
||||||
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25))
|
||||||
|
.read_inode = yaffs_read_inode,
|
||||||
|
#endif
|
||||||
|
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26))
|
||||||
|
.put_inode = yaffs_put_inode,
|
||||||
|
+#endif
|
||||||
|
.put_super = yaffs_put_super,
|
||||||
|
.delete_inode = yaffs_delete_inode,
|
||||||
|
.clear_inode = yaffs_clear_inode,
|
||||||
|
@@ -437,6 +447,7 @@
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26))
|
||||||
|
/* For now put inode is just for debugging
|
||||||
|
* Put inode is called when the inode **structure** is put.
|
||||||
|
*/
|
||||||
|
@@ -447,6 +458,7 @@
|
||||||
|
atomic_read(&inode->i_count)));
|
||||||
|
|
||||||
|
}
|
||||||
|
+#endif
|
||||||
|
|
||||||
|
/* clear is called to tell the fs to release any per-inode data it holds */
|
||||||
|
static void yaffs_clear_inode(struct inode *inode)
|
||||||
|
@@ -2279,7 +2291,7 @@
|
||||||
|
/* Install the proc_fs entry */
|
||||||
|
my_proc_entry = create_proc_entry("yaffs",
|
||||||
|
S_IRUGO | S_IFREG,
|
||||||
|
- &proc_root);
|
||||||
|
+ YPROC_ROOT);
|
||||||
|
|
||||||
|
if (my_proc_entry) {
|
||||||
|
my_proc_entry->write_proc = yaffs_proc_write;
|
||||||
|
@@ -2325,7 +2337,7 @@
|
||||||
|
T(YAFFS_TRACE_ALWAYS, ("yaffs " __DATE__ " " __TIME__
|
||||||
|
" removing. \n"));
|
||||||
|
|
||||||
|
- remove_proc_entry("yaffs", &proc_root);
|
||||||
|
+ remove_proc_entry("yaffs", YPROC_ROOT);
|
||||||
|
|
||||||
|
fsinst = fs_to_install;
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
--- a/drivers/net/phy/phy.c
|
||||||
|
+++ b/drivers/net/phy/phy.c
|
||||||
|
@@ -348,6 +348,50 @@
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(phy_ethtool_gset);
|
||||||
|
|
||||||
|
+int phy_ethtool_ioctl(struct phy_device *phydev, void *useraddr)
|
||||||
|
+{
|
||||||
|
+ u32 cmd;
|
||||||
|
+ int tmp;
|
||||||
|
+ struct ethtool_cmd ecmd = { ETHTOOL_GSET };
|
||||||
|
+ struct ethtool_value edata = { ETHTOOL_GLINK };
|
||||||
|
+
|
||||||
|
+ if (get_user(cmd, (u32 *) useraddr))
|
||||||
|
+ return -EFAULT;
|
||||||
|
+
|
||||||
|
+ switch (cmd) {
|
||||||
|
+ case ETHTOOL_GSET:
|
||||||
|
+ phy_ethtool_gset(phydev, &ecmd);
|
||||||
|
+ if (copy_to_user(useraddr, &ecmd, sizeof(ecmd)))
|
||||||
|
+ return -EFAULT;
|
||||||
|
+ return 0;
|
||||||
|
+
|
||||||
|
+ case ETHTOOL_SSET:
|
||||||
|
+ if (copy_from_user(&ecmd, useraddr, sizeof(ecmd)))
|
||||||
|
+ return -EFAULT;
|
||||||
|
+ return phy_ethtool_sset(phydev, &ecmd);
|
||||||
|
+
|
||||||
|
+ case ETHTOOL_NWAY_RST:
|
||||||
|
+ /* if autoneg is off, it's an error */
|
||||||
|
+ tmp = phy_read(phydev, MII_BMCR);
|
||||||
|
+ if (tmp & BMCR_ANENABLE) {
|
||||||
|
+ tmp |= (BMCR_ANRESTART);
|
||||||
|
+ phy_write(phydev, MII_BMCR, tmp);
|
||||||
|
+ return 0;
|
||||||
|
+ }
|
||||||
|
+ return -EINVAL;
|
||||||
|
+
|
||||||
|
+ case ETHTOOL_GLINK:
|
||||||
|
+ edata.data = (phy_read(phydev,
|
||||||
|
+ MII_BMSR) & BMSR_LSTATUS) ? 1 : 0;
|
||||||
|
+ if (copy_to_user(useraddr, &edata, sizeof(edata)))
|
||||||
|
+ return -EFAULT;
|
||||||
|
+ return 0;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return -EOPNOTSUPP;
|
||||||
|
+}
|
||||||
|
+EXPORT_SYMBOL(phy_ethtool_ioctl);
|
||||||
|
+
|
||||||
|
/**
|
||||||
|
* phy_mii_ioctl - generic PHY MII ioctl interface
|
||||||
|
* @phydev: the phy_device struct
|
||||||
|
@@ -403,8 +447,8 @@
|
||||||
|
}
|
||||||
|
|
||||||
|
phy_write(phydev, mii_data->reg_num, val);
|
||||||
|
-
|
||||||
|
- if (mii_data->reg_num == MII_BMCR
|
||||||
|
+
|
||||||
|
+ if (mii_data->reg_num == MII_BMCR
|
||||||
|
&& val & BMCR_RESET
|
||||||
|
&& phydev->drv->config_init) {
|
||||||
|
phy_scan_fixups(phydev);
|
||||||
|
@@ -524,7 +568,7 @@
|
||||||
|
int idx;
|
||||||
|
|
||||||
|
idx = phy_find_setting(phydev->speed, phydev->duplex);
|
||||||
|
-
|
||||||
|
+
|
||||||
|
idx++;
|
||||||
|
|
||||||
|
idx = phy_find_valid(idx, phydev->supported);
|
||||||
|
--- a/include/linux/phy.h
|
||||||
|
+++ b/include/linux/phy.h
|
||||||
|
@@ -434,6 +434,7 @@
|
||||||
|
void phy_stop_machine(struct phy_device *phydev);
|
||||||
|
int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd);
|
||||||
|
int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd);
|
||||||
|
+int phy_ethtool_ioctl(struct phy_device *phydev, void *useraddr);
|
||||||
|
int phy_mii_ioctl(struct phy_device *phydev,
|
||||||
|
struct mii_ioctl_data *mii_data, int cmd);
|
||||||
|
int phy_start_interrupts(struct phy_device *phydev);
|
26
target/linux/generic-2.6/patches-2.6.27/610-phy_detect.patch
Normal file
26
target/linux/generic-2.6/patches-2.6.27/610-phy_detect.patch
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
--- a/drivers/net/phy/mdio_bus.c
|
||||||
|
+++ b/drivers/net/phy/mdio_bus.c
|
||||||
|
@@ -135,6 +135,9 @@
|
||||||
|
struct phy_device *phydev = to_phy_device(dev);
|
||||||
|
struct phy_driver *phydrv = to_phy_driver(drv);
|
||||||
|
|
||||||
|
+ if (phydrv->detect)
|
||||||
|
+ return (phydrv->detect(phydev->bus, phydev->addr));
|
||||||
|
+
|
||||||
|
return ((phydrv->phy_id & phydrv->phy_id_mask) ==
|
||||||
|
(phydev->phy_id & phydrv->phy_id_mask));
|
||||||
|
}
|
||||||
|
--- a/include/linux/phy.h
|
||||||
|
+++ b/include/linux/phy.h
|
||||||
|
@@ -339,6 +339,11 @@
|
||||||
|
u32 features;
|
||||||
|
u32 flags;
|
||||||
|
|
||||||
|
+ /* Called during discovery to test if the
|
||||||
|
+ * device can attach to the bus, even if
|
||||||
|
+ * phy id and mask do not match */
|
||||||
|
+ bool (*detect)(struct mii_bus *bus, int addr);
|
||||||
|
+
|
||||||
|
/*
|
||||||
|
* Called to initialize the PHY,
|
||||||
|
* including after a reset
|
|
@ -0,0 +1,24 @@
|
||||||
|
--- a/drivers/net/phy/Kconfig
|
||||||
|
+++ b/drivers/net/phy/Kconfig
|
||||||
|
@@ -66,6 +66,11 @@
|
||||||
|
---help---
|
||||||
|
Supports the Realtek 821x PHY.
|
||||||
|
|
||||||
|
+config ADM6996_PHY
|
||||||
|
+ tristate "Driver for ADM6996 switches"
|
||||||
|
+ ---help---
|
||||||
|
+ Currently supports the ADM6996F switch
|
||||||
|
+
|
||||||
|
config FIXED_PHY
|
||||||
|
bool "Driver for MDIO Bus/PHY emulation with fixed speed/link PHYs"
|
||||||
|
depends on PHYLIB=y
|
||||||
|
--- a/drivers/net/phy/Makefile
|
||||||
|
+++ b/drivers/net/phy/Makefile
|
||||||
|
@@ -12,6 +12,7 @@
|
||||||
|
obj-$(CONFIG_VITESSE_PHY) += vitesse.o
|
||||||
|
obj-$(CONFIG_BROADCOM_PHY) += broadcom.o
|
||||||
|
obj-$(CONFIG_ICPLUS_PHY) += icplus.o
|
||||||
|
+obj-$(CONFIG_ADM6996_PHY) += adm6996.o
|
||||||
|
obj-$(CONFIG_REALTEK_PHY) += realtek.o
|
||||||
|
obj-$(CONFIG_FIXED_PHY) += fixed.o
|
||||||
|
obj-$(CONFIG_MDIO_BITBANG) += mdio-bitbang.o
|
|
@ -0,0 +1,60 @@
|
||||||
|
--- a/drivers/net/phy/phy_device.c
|
||||||
|
+++ b/drivers/net/phy/phy_device.c
|
||||||
|
@@ -143,6 +143,18 @@
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(phy_scan_fixups);
|
||||||
|
|
||||||
|
+static int generic_receive_skb(struct sk_buff *skb)
|
||||||
|
+{
|
||||||
|
+ skb->protocol = eth_type_trans(skb, skb->dev);
|
||||||
|
+ return netif_receive_skb(skb);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int generic_rx(struct sk_buff *skb)
|
||||||
|
+{
|
||||||
|
+ skb->protocol = eth_type_trans(skb, skb->dev);
|
||||||
|
+ return netif_rx(skb);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
struct phy_device* phy_device_create(struct mii_bus *bus, int addr, int phy_id)
|
||||||
|
{
|
||||||
|
struct phy_device *dev;
|
||||||
|
@@ -168,6 +180,8 @@
|
||||||
|
dev->bus = bus;
|
||||||
|
|
||||||
|
dev->state = PHY_DOWN;
|
||||||
|
+ dev->netif_receive_skb = &generic_receive_skb;
|
||||||
|
+ dev->netif_rx = &generic_rx;
|
||||||
|
|
||||||
|
mutex_init(&dev->lock);
|
||||||
|
|
||||||
|
--- a/include/linux/phy.h
|
||||||
|
+++ b/include/linux/phy.h
|
||||||
|
@@ -309,6 +309,17 @@
|
||||||
|
void (*adjust_link)(struct net_device *dev);
|
||||||
|
|
||||||
|
void (*adjust_state)(struct net_device *dev);
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * By default these point to the original functions
|
||||||
|
+ * with the same name. adding them to the phy_device
|
||||||
|
+ * allows the phy driver to override them for packet
|
||||||
|
+ * mangling if the ethernet driver supports it
|
||||||
|
+ * This is required to support some really horrible
|
||||||
|
+ * switches such as the Marvell 88E6060
|
||||||
|
+ */
|
||||||
|
+ int (*netif_receive_skb)(struct sk_buff *skb);
|
||||||
|
+ int (*netif_rx)(struct sk_buff *skb);
|
||||||
|
};
|
||||||
|
#define to_phy_device(d) container_of(d, struct phy_device, dev)
|
||||||
|
|
||||||
|
--- a/include/linux/netdevice.h
|
||||||
|
+++ b/include/linux/netdevice.h
|
||||||
|
@@ -613,6 +613,7 @@
|
||||||
|
void *ax25_ptr; /* AX.25 specific data */
|
||||||
|
struct wireless_dev *ieee80211_ptr; /* IEEE 802.11 specific data,
|
||||||
|
assign before registering */
|
||||||
|
+ void *phy_ptr; /* PHY device specific data */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Cache line mostly used on receive path (including eth_type_trans())
|
48
target/linux/generic-2.6/patches-2.6.27/640-mvswitch.patch
Normal file
48
target/linux/generic-2.6/patches-2.6.27/640-mvswitch.patch
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
--- a/drivers/net/phy/Kconfig
|
||||||
|
+++ b/drivers/net/phy/Kconfig
|
||||||
|
@@ -71,6 +71,12 @@
|
||||||
|
---help---
|
||||||
|
Currently supports the ADM6996F switch
|
||||||
|
|
||||||
|
+config MVSWITCH_PHY
|
||||||
|
+ tristate "Driver for Marvell switches"
|
||||||
|
+ select VLAN_8021Q
|
||||||
|
+ ---help---
|
||||||
|
+ Currently supports the Marvell 88E6060 switch.
|
||||||
|
+
|
||||||
|
config FIXED_PHY
|
||||||
|
bool "Driver for MDIO Bus/PHY emulation with fixed speed/link PHYs"
|
||||||
|
depends on PHYLIB=y
|
||||||
|
--- a/drivers/net/phy/Makefile
|
||||||
|
+++ b/drivers/net/phy/Makefile
|
||||||
|
@@ -13,6 +13,7 @@
|
||||||
|
obj-$(CONFIG_BROADCOM_PHY) += broadcom.o
|
||||||
|
obj-$(CONFIG_ICPLUS_PHY) += icplus.o
|
||||||
|
obj-$(CONFIG_ADM6996_PHY) += adm6996.o
|
||||||
|
+obj-$(CONFIG_MVSWITCH_PHY) += mvswitch.o
|
||||||
|
obj-$(CONFIG_REALTEK_PHY) += realtek.o
|
||||||
|
obj-$(CONFIG_FIXED_PHY) += fixed.o
|
||||||
|
obj-$(CONFIG_MDIO_BITBANG) += mdio-bitbang.o
|
||||||
|
--- a/drivers/net/phy/mdio_bus.c
|
||||||
|
+++ b/drivers/net/phy/mdio_bus.c
|
||||||
|
@@ -35,6 +35,12 @@
|
||||||
|
#include <asm/irq.h>
|
||||||
|
#include <asm/uaccess.h>
|
||||||
|
|
||||||
|
+static void mdio_dev_release(struct device *dev)
|
||||||
|
+{
|
||||||
|
+ /* nothing to do */
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
/**
|
||||||
|
* mdiobus_register - bring up all the PHYs on a given bus and attach them to bus
|
||||||
|
* @bus: target mii_bus
|
||||||
|
@@ -85,6 +91,7 @@
|
||||||
|
|
||||||
|
phydev->dev.parent = bus->dev;
|
||||||
|
phydev->dev.bus = &mdio_bus_type;
|
||||||
|
+ phydev->dev.release = mdio_dev_release;
|
||||||
|
snprintf(phydev->dev.bus_id, BUS_ID_SIZE, PHY_ID_FMT, bus->id, i);
|
||||||
|
|
||||||
|
phydev->bus = bus;
|
|
@ -0,0 +1,25 @@
|
||||||
|
--- a/drivers/usb/serial/usb-serial.c
|
||||||
|
+++ b/drivers/usb/serial/usb-serial.c
|
||||||
|
@@ -59,6 +59,7 @@
|
||||||
|
drivers depend on it.
|
||||||
|
*/
|
||||||
|
|
||||||
|
+static ushort maxSize = 0;
|
||||||
|
static int debug;
|
||||||
|
/* initially all NULL */
|
||||||
|
static struct usb_serial *serial_table[SERIAL_TTY_MINORS];
|
||||||
|
@@ -833,7 +834,7 @@
|
||||||
|
dev_err(&interface->dev, "No free urbs available\n");
|
||||||
|
goto probe_error;
|
||||||
|
}
|
||||||
|
- buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
|
||||||
|
+ buffer_size = (endpoint->wMaxPacketSize > maxSize) ? endpoint->wMaxPacketSize : maxSize;
|
||||||
|
port->bulk_in_size = buffer_size;
|
||||||
|
port->bulk_in_endpointAddress = endpoint->bEndpointAddress;
|
||||||
|
port->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
|
||||||
|
@@ -1253,3 +1254,5 @@
|
||||||
|
|
||||||
|
module_param(debug, bool, S_IRUGO | S_IWUSR);
|
||||||
|
MODULE_PARM_DESC(debug, "Debug enabled or not");
|
||||||
|
+module_param(maxSize, ushort,0);
|
||||||
|
+MODULE_PARM_DESC(maxSize,"User specified USB endpoint size");
|
|
@ -0,0 +1,11 @@
|
||||||
|
--- a/init/main.c
|
||||||
|
+++ b/init/main.c
|
||||||
|
@@ -802,7 +802,7 @@
|
||||||
|
numa_default_policy();
|
||||||
|
|
||||||
|
if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
|
||||||
|
- printk(KERN_WARNING "Warning: unable to open an initial console.\n");
|
||||||
|
+ printk(KERN_WARNING "Please be patient, while OpenWrt loads ...\n");
|
||||||
|
|
||||||
|
(void) sys_dup(0);
|
||||||
|
(void) sys_dup(0);
|
|
@ -0,0 +1,46 @@
|
||||||
|
--- a/include/linux/time.h
|
||||||
|
+++ b/include/linux/time.h
|
||||||
|
@@ -1,6 +1,10 @@
|
||||||
|
#ifndef _LINUX_TIME_H
|
||||||
|
#define _LINUX_TIME_H
|
||||||
|
|
||||||
|
+#ifndef __KERNEL__
|
||||||
|
+#include <time.h>
|
||||||
|
+#else
|
||||||
|
+
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
#ifdef __KERNEL__
|
||||||
|
@@ -228,4 +232,6 @@
|
||||||
|
*/
|
||||||
|
#define TIMER_ABSTIME 0x01
|
||||||
|
|
||||||
|
+#endif /* __KERNEL__ DEBIAN */
|
||||||
|
+
|
||||||
|
#endif
|
||||||
|
--- a/include/linux/types.h
|
||||||
|
+++ b/include/linux/types.h
|
||||||
|
@@ -1,6 +1,14 @@
|
||||||
|
#ifndef _LINUX_TYPES_H
|
||||||
|
#define _LINUX_TYPES_H
|
||||||
|
|
||||||
|
+/* Debian: Use userland types instead. */
|
||||||
|
+#ifndef __KERNEL__
|
||||||
|
+# include <sys/types.h>
|
||||||
|
+/* For other kernel headers. */
|
||||||
|
+# include <linux/posix_types.h>
|
||||||
|
+# include <asm/types.h>
|
||||||
|
+#else
|
||||||
|
+
|
||||||
|
#ifdef __KERNEL__
|
||||||
|
|
||||||
|
#define DECLARE_BITMAP(name,bits) \
|
||||||
|
@@ -161,6 +169,8 @@
|
||||||
|
|
||||||
|
#endif /* __KERNEL_STRICT_NAMES */
|
||||||
|
|
||||||
|
+#endif /* __KERNEL__ DEBIAN */
|
||||||
|
+
|
||||||
|
/*
|
||||||
|
* Below are truly Linux-specific types that should never collide with
|
||||||
|
* any application/library that wants linux/types.h.
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue