From 6cbee2d68d949ac9946473c6fa8f1d4f2fe9c022 Mon Sep 17 00:00:00 2001 From: Koen Kooi Date: Fri, 25 Sep 2009 11:13:34 +0200 Subject: linux-omap-pm 2.6.29: add omap3-touchbook support --- .../omap3-touchbook/accelerometer-mma7455l.patch | 677 + .../accelerometer-touchscreen-mux-spi.patch | 64 + .../omap3-touchbook/aufs-1.patch | 35 + .../omap3-touchbook/aufs-2.patch | 285 + .../omap3-touchbook/aufs-3.patch | 23604 +++++++ .../aufs-squashfs-mount-to-avoid-initramfs.patch | 40 + .../battery1-tps65950-charging-management-1.patch | 221 + .../battery1-tps65950-charging-management-2.patch | 246 + .../battery2-bq27200-gpio-charged.patch | 53 + .../battery2-bq27200-no-error-message.patch | 39 + .../omap3-touchbook/beagle-asoc.patch | 35 + .../omap3-touchbook/board-omap3beagle.c | 744 + .../omap3-touchbook/boot-no-power-message.patch | 11 + .../linux-omap-pm-2.6.29/omap3-touchbook/defconfig | 2720 + .../omap3-touchbook/defconfig.orig | 2701 + .../omap3-touchbook/dspbridge.patch | 66715 +++++++++++++++++++ .../dss2-blank-rotate-accessible-by-user.patch | 20 + .../omap3-touchbook/dss2-export-status.patch | 44 + .../dss2-fix-XY-coordinates-when-rotating.patch | 177 + .../dss2-fix-scaling-when-rotating.patch | 52 + .../omap3-touchbook/ehci.patch | 131 + .../omap3-touchbook/logo_linux_clut224.ppm | 773 + .../omap3-touchbook/memory-move-malloc-end.patch | 9 + .../screen-backlight-accessible-by-user.patch | 11 + .../omap3-touchbook/screen-backlight.patch | 10 + .../sound-headphone-detection.patch | 93 + .../omap3-touchbook/tincantools-puppy.diff | 66 + .../touchscreen-ads7846-export-settings.patch | 87 + .../touchscreen-ads7846-rotation-support.patch | 108 + ...-lower-current-consumption-upon-insertion.patch | 30 + .../omap3-touchbook/usb-otg-pc-connection.patch | 36 + recipes/linux/linux-omap-pm_2.6.29.bb | 35 +- 32 files changed, 99871 insertions(+), 1 deletion(-) create mode 100644 recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/accelerometer-mma7455l.patch create mode 100644 recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/accelerometer-touchscreen-mux-spi.patch create mode 100644 recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/aufs-1.patch create mode 100644 recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/aufs-2.patch create mode 100644 recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/aufs-3.patch create mode 100644 recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/aufs-squashfs-mount-to-avoid-initramfs.patch create mode 100644 recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/battery1-tps65950-charging-management-1.patch create mode 100644 recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/battery1-tps65950-charging-management-2.patch create mode 100644 recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/battery2-bq27200-gpio-charged.patch create mode 100644 recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/battery2-bq27200-no-error-message.patch create mode 100644 recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/beagle-asoc.patch create mode 100644 recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/board-omap3beagle.c create mode 100644 recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/boot-no-power-message.patch create mode 100644 recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/defconfig create mode 100644 recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/defconfig.orig create mode 100644 recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/dspbridge.patch create mode 100644 recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/dss2-blank-rotate-accessible-by-user.patch create mode 100644 recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/dss2-export-status.patch create mode 100644 recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/dss2-fix-XY-coordinates-when-rotating.patch create mode 100644 recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/dss2-fix-scaling-when-rotating.patch create mode 100644 recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/ehci.patch create mode 100644 recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/logo_linux_clut224.ppm create mode 100644 recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/memory-move-malloc-end.patch create mode 100644 recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/screen-backlight-accessible-by-user.patch create mode 100644 recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/screen-backlight.patch create mode 100644 recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/sound-headphone-detection.patch create mode 100644 recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/tincantools-puppy.diff create mode 100644 recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/touchscreen-ads7846-export-settings.patch create mode 100644 recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/touchscreen-ads7846-rotation-support.patch create mode 100644 recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/usb-lower-current-consumption-upon-insertion.patch create mode 100644 recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/usb-otg-pc-connection.patch diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/accelerometer-mma7455l.patch b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/accelerometer-mma7455l.patch new file mode 100644 index 0000000000..f6d522e352 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/accelerometer-mma7455l.patch @@ -0,0 +1,677 @@ +From 0b60175d1b7db1301e2ad3ad4d64fb0c8d121c0a Mon Sep 17 00:00:00 2001 +From: Tim Yamin +Date: Sat, 11 Apr 2009 13:05:21 -0700 +Subject: [PATCH] MMA7455L driver. + +--- + drivers/input/misc/Kconfig | 9 + + drivers/input/misc/Makefile | 1 + + drivers/input/misc/mma7455l.c | 614 +++++++++++++++++++++++++++++++++++++++++ + include/linux/mma7455l.h | 11 + + 4 files changed, 635 insertions(+), 0 deletions(-) + +diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig +index 6fa9e38..05dca18 100644 +--- a/drivers/input/misc/Kconfig ++++ b/drivers/input/misc/Kconfig +@@ -237,4 +237,13 @@ config INPUT_PCF50633_PMU + Say Y to include support for delivering PMU events via input + layer on NXP PCF50633. + ++config INPUT_MMA7455L ++ tristate "Freescale MMA7455L 3-axis accelerometer" ++ depends on SPI_MASTER ++ help ++ SPI driver for the Freescale MMA7455L 3-axis accelerometer. ++ ++ The userspace interface is a 3-axis (X/Y/Z) relative movement ++ Linux input device, reporting REL_[XYZ] events. ++ + endif +diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile +index 2fabcdb..e355d82 100644 +--- a/drivers/input/misc/Makefile ++++ b/drivers/input/misc/Makefile +@@ -23,3 +23,4 @@ obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o + obj-$(CONFIG_INPUT_APANEL) += apanel.o + obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o + obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o ++obj-$(CONFIG_INPUT_MMA7455L) += mma7455l.o +diff --git a/drivers/input/misc/mma7455l.c b/drivers/input/misc/mma7455l.c +new file mode 100644 +index 0000000..b33c513 +--- /dev/null ++++ b/drivers/input/misc/mma7455l.c +@@ -0,0 +1,614 @@ ++/* Linux kernel driver for the Freescale MMA7455L 3-axis accelerometer ++ * ++ * Copyright (C) 2009 by Always Innovating, Inc. ++ * Author: Gregoire Gentil ++ * Author: Tim Yamin ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of ++ * the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, ++ * MA 02111-1307 USA ++ * ++ */ ++ ++/* ++ * What this driver doesn't yet support: ++ * ++ * - I2C ++ * - INT2 handling ++ * - Pulse detection (and the sysctls to control it) ++ * - 10-bit measurement ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define MMA7455L_WHOAMI_MAGIC 0x55 ++ ++enum mma7455l_reg { ++ MMA7455L_REG_XOUTL = 0x00, ++ MMA7455L_REG_XOUTH = 0x01, ++ MMA7455L_REG_YOUTL = 0x02, ++ MMA7455L_REG_YOUTH = 0x03, ++ MMA7455L_REG_ZOUTL = 0x04, ++ MMA7455L_REG_ZOUTH = 0x05, ++ MMA7455L_REG_XOUT8 = 0x06, ++ MMA7455L_REG_YOUT8 = 0x07, ++ MMA7455L_REG_ZOUT8 = 0x08, ++ MMA7455L_REG_STATUS = 0x09, ++ MMA7455L_REG_DETSRC = 0x0a, ++ MMA7455L_REG_TOUT = 0x0b, ++ MMA7455L_REG_RESERVED1 = 0x0c, ++ MMA7455L_REG_I2CAD = 0x0d, ++ MMA7455L_REG_USRINF = 0x0e, ++ MMA7455L_REG_WHOAMI = 0x0f, ++ MMA7455L_REG_XOFFL = 0x10, ++ MMA7455L_REG_XOFFH = 0x11, ++ MMA7455L_REG_YOFFL = 0x12, ++ MMA7455L_REG_YOFFH = 0x13, ++ MMA7455L_REG_ZOFFL = 0x14, ++ MMA7455L_REG_ZOFFH = 0x15, ++ MMA7455L_REG_MCTL = 0x16, ++ MMA7455L_REG_INTRST = 0x17, ++ MMA7455L_REG_CTL1 = 0x18, ++ MMA7455L_REG_CTL2 = 0x19, ++ MMA7455L_REG_LDTH = 0x1a, ++ MMA7455L_REG_PDTH = 0x1b, ++ MMA7455L_REG_PW = 0x1c, ++ MMA7455L_REG_LT = 0x1d, ++ MMA7455L_REG_TW = 0x1e, ++ MMA7455L_REG_RESERVED2 = 0x1f, ++}; ++ ++enum mma7455l_reg_status { ++ MMA7455L_STATUS_XDA = 0x08, ++ MMA7455L_STATUS_YDA = 0x10, ++ MMA7455L_STATUS_ZDA = 0x20, ++}; ++ ++enum mma7455l_mode { ++ MMA7455L_MODE_STANDBY = 0, ++ MMA7455L_MODE_MEASUREMENT = 1, ++ MMA7455L_MODE_LEVELDETECTION = 0x42, /* Set DRPD to on */ ++ MMA7455L_MODE_PULSEDETECTION = 0x43, /* Set DRPD to on */ ++ MMA7455L_MODE_MASK = 0x43, ++}; ++ ++enum mma7455l_gselect { ++ MMA7455L_GSELECT_8 = 0x0, ++ MMA7455L_GSELECT_2 = 0x4, ++ MMA7455L_GSELECT_4 = 0x8, ++ MMA7455L_GSELECT_MASK = 0xC, ++}; ++ ++/* FIXME */ ++#define MMA7455L_F_FS 0x0020 /* ADC full scale */ ++ ++struct mma7455l_info { ++ struct spi_device *spi_dev; ++ struct input_dev *input_dev; ++ struct mutex lock; ++ struct delayed_work work; ++ ++ u8 mode; ++ u8 gSelect; ++ ++ u8 flags; ++ u8 working; ++}; ++ ++/* lowlevel register access functions */ ++ ++#define WRITE_BIT (1 << 7) ++#define ADDR_SHIFT 1 ++ ++static inline u_int8_t __reg_read(struct mma7455l_info *mma, u_int8_t reg) ++{ ++ int rc; ++ u_int8_t cmd; ++ ++ cmd = ((reg & 0x3f) << ADDR_SHIFT); ++ rc = spi_w8r8(mma->spi_dev, cmd); ++ ++ return rc; ++} ++ ++static u_int8_t reg_read(struct mma7455l_info *mma, u_int8_t reg) ++{ ++ u_int8_t ret; ++ ++ mutex_lock(&mma->lock); ++ ret = __reg_read(mma, reg); ++ mutex_unlock(&mma->lock); ++ ++ return ret; ++} ++ ++static s16 __reg_read_10(struct mma7455l_info *mma, u8 reg1, u8 reg2) ++{ ++ u8 v1, v2; ++ ++ v1 = __reg_read(mma, reg1); ++ v2 = __reg_read(mma, reg2); ++ ++ return (v2 & 0x4) << 13 | (v2 & 0x3) << 8 | v1; ++} ++ ++static inline int __reg_write(struct mma7455l_info *mma, u_int8_t reg, u_int8_t val) ++{ ++ u_int8_t buf[2]; ++ ++ buf[0] = ((reg & 0x3f) << ADDR_SHIFT) | WRITE_BIT; ++ buf[1] = val; ++ ++ return spi_write(mma->spi_dev, buf, sizeof(buf)); ++} ++ ++static int reg_write(struct mma7455l_info *mma, u_int8_t reg, u_int8_t val) ++{ ++ int ret; ++ ++ mutex_lock(&mma->lock); ++ ret = __reg_write(mma, reg, val); ++ mutex_unlock(&mma->lock); ++ ++ return ret; ++} ++ ++static s16 __reg_write_10(struct mma7455l_info *mma, u8 reg1, u8 reg2, s16 value) ++{ ++ int ret; ++ u8 v1, v2; ++ ++ v1 = value & 0xFF; ++ if(value < 0) ++ v2 = ((value >> 8) & 0x3) | 0x4; ++ else ++ v2 = 0; ++ ++ ret = __reg_write(mma, reg1, v1); ++ ret = __reg_write(mma, reg2, v2); ++ return ret; ++} ++ ++static void mma7455l_work(struct work_struct *work) ++{ ++ struct mma7455l_info *mma = ++ container_of(work, struct mma7455l_info, work.work); ++ ++ s8 val; ++ mma->working = 1; ++ ++ /* FIXME: 10 bit accuracy? */ ++ if (!(mma->flags & MMA7455L_STATUS_XDA)) { ++ val = reg_read(mma, MMA7455L_REG_XOUT8); ++ input_report_abs(mma->input_dev, ABS_X, val); ++ } ++ if (!(mma->flags & MMA7455L_STATUS_YDA)) { ++ val = reg_read(mma, MMA7455L_REG_YOUT8); ++ input_report_abs(mma->input_dev, ABS_Y, val); ++ } ++ if (!(mma->flags & MMA7455L_STATUS_ZDA)) { ++ val = reg_read(mma, MMA7455L_REG_ZOUT8); ++ input_report_abs(mma->input_dev, ABS_Z, val); ++ } ++ ++ mma->working = 0; ++ input_sync(mma->input_dev); ++ put_device(&mma->spi_dev->dev); ++ ++ /* Enable IRQ and clear out interrupt */ ++ reg_write(mma, MMA7455L_REG_INTRST, 0x3); ++ reg_write(mma, MMA7455L_REG_INTRST, 0x0); ++ enable_irq(mma->spi_dev->irq); ++} ++ ++static void mma7455l_schedule_work(struct mma7455l_info *mma) ++{ ++ int status; ++ ++ get_device(&mma->spi_dev->dev); ++ status = schedule_delayed_work(&mma->work, HZ / 10); ++} ++ ++static irqreturn_t mma7455l_interrupt(int irq, void *_mma) ++{ ++ struct mma7455l_info *mma = _mma; ++ mma7455l_schedule_work(mma); ++ ++ /* Disable any further interrupts until we have processed ++ * the current one */ ++ disable_irq(mma->spi_dev->irq); ++ return IRQ_HANDLED; ++} ++ ++/* sysfs */ ++ ++static void get_mode(struct mma7455l_info *mma, u8 *mode, u8 *gSelect) ++{ ++ u8 tmp = reg_read(mma, MMA7455L_REG_MCTL); ++ ++ *mode = tmp & MMA7455L_MODE_MASK; ++ *gSelect = tmp & MMA7455L_GSELECT_MASK; ++} ++ ++static void set_mode(struct mma7455l_info *mma, u8 mode, u8 gSelect) ++{ ++ reg_write(mma, MMA7455L_REG_MCTL, mode | gSelect); ++} ++ ++static void update_mode(struct mma7455l_info *mma, u8 mode, u8 gSelect) ++{ ++ mma->mode = mode; ++ mma->gSelect = gSelect; ++ ++ reg_write(mma, MMA7455L_REG_MCTL, mma->mode | mma->gSelect); ++} ++ ++static ssize_t show_measure(struct device *dev, struct device_attribute *attr, char *buf) ++{ ++ struct mma7455l_info *mma = dev_get_drvdata(dev); ++ s8 x, y, z; ++ u8 old_Mode, old_gSelect; ++ ++ get_mode(mma, &old_Mode, &old_gSelect); ++ set_mode(mma, MMA7455L_MODE_MEASUREMENT, MMA7455L_GSELECT_2); ++ ++ while (reg_read(mma, MMA7455L_REG_STATUS) == 0) { ++ msleep(10); ++ } ++ ++ x = reg_read(mma, MMA7455L_REG_XOUT8); ++ y = reg_read(mma, MMA7455L_REG_YOUT8); ++ z = reg_read(mma, MMA7455L_REG_ZOUT8); ++ ++ set_mode(mma, old_Mode, old_gSelect); ++ return sprintf(buf, "%d %d %d\n", x, y, z); ++} ++ ++static ssize_t show_mode(struct device *dev, struct device_attribute *attr, char *buf) ++{ ++ struct mma7455l_info *mma = dev_get_drvdata(dev); ++ ++ switch(mma->mode) ++ { ++ case MMA7455L_MODE_STANDBY: ++ return sprintf(buf, "Standby\n"); ++ break; ++ case MMA7455L_MODE_MEASUREMENT: ++ return sprintf(buf, "Measurement\n"); ++ break; ++ case MMA7455L_MODE_LEVELDETECTION: ++ return sprintf(buf, "Level Detection\n"); ++ break; ++ case MMA7455L_MODE_PULSEDETECTION: ++ return sprintf(buf, "Pulse Detection\n"); ++ break; ++ } ++ ++ return sprintf(buf, "Unknown mode!\n"); ++} ++ ++static ssize_t show_gSelect(struct device *dev, struct device_attribute *attr, char *buf) ++{ ++ struct mma7455l_info *mma = dev_get_drvdata(dev); ++ ++ switch(mma->gSelect) ++ { ++ case MMA7455L_GSELECT_8: ++ return sprintf(buf, "8\n"); ++ break; ++ case MMA7455L_GSELECT_4: ++ return sprintf(buf, "4\n"); ++ break; ++ case MMA7455L_GSELECT_2: ++ return sprintf(buf, "2\n"); ++ break; ++ } ++ ++ return sprintf(buf, "Unknown gSelect!\n"); ++} ++ ++static ssize_t show_level_threshold(struct device *dev, struct device_attribute *attr, char *buf) ++{ ++ struct mma7455l_info *mma = dev_get_drvdata(dev); ++ return sprintf(buf, "%u\n", reg_read(mma, MMA7455L_REG_LDTH)); ++} ++ ++static ssize_t show_calibration(struct device *dev, struct device_attribute *attr, char *buf) ++{ ++ s16 x, y, z; ++ struct mma7455l_info *mma = dev_get_drvdata(dev); ++ ++ mutex_lock(&mma->lock); ++ x = __reg_read_10(mma, MMA7455L_REG_XOFFL, MMA7455L_REG_XOFFH); ++ y = __reg_read_10(mma, MMA7455L_REG_YOFFL, MMA7455L_REG_YOFFH); ++ z = __reg_read_10(mma, MMA7455L_REG_ZOFFL, MMA7455L_REG_ZOFFH); ++ mutex_unlock(&mma->lock); ++ ++ return sprintf(buf, "%d %d %d\n", x, y, z); ++} ++ ++static ssize_t write_mode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) ++{ ++ struct mma7455l_info *mma = dev_get_drvdata(dev); ++ ++ if (!strncmp(buf, "Standby", count)) ++ update_mode(mma, MMA7455L_MODE_STANDBY, mma->gSelect); ++ else if (!strncmp(buf, "Measurement", count)) ++ update_mode(mma, MMA7455L_MODE_MEASUREMENT, mma->gSelect); ++ else if (!strncmp(buf, "Level Detection", count)) ++ update_mode(mma, MMA7455L_MODE_LEVELDETECTION, mma->gSelect); ++ else if (!strncmp(buf, "Pulse Detection", count)) ++ update_mode(mma, MMA7455L_MODE_PULSEDETECTION, mma->gSelect); ++ ++ return count; ++} ++ ++static ssize_t write_gSelect(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) ++{ ++ unsigned long v; ++ struct mma7455l_info *mma = dev_get_drvdata(dev); ++ ++ if(strict_strtoul(buf, 10, &v) == 0) ++ { ++ switch(v) ++ { ++ case 8: ++ update_mode(mma, mma->mode, MMA7455L_GSELECT_8); ++ break; ++ case 4: ++ update_mode(mma, mma->mode, MMA7455L_GSELECT_4); ++ break; ++ case 2: ++ update_mode(mma, mma->mode, MMA7455L_GSELECT_2); ++ break; ++ default: ++ return -EINVAL; ++ break; ++ } ++ return count; ++ } ++ ++ return -EINVAL; ++} ++ ++static ssize_t write_level_threshold(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) ++{ ++ unsigned long v; ++ struct mma7455l_info *mma = dev_get_drvdata(dev); ++ ++ if(strict_strtoul(buf, 10, &v) == 0) ++ { ++ if(v <= 0xFF) { ++ reg_write(mma, MMA7455L_REG_LDTH, v); ++ return count; ++ } else ++ return -EINVAL; ++ } ++ ++ return -EINVAL; ++} ++ ++static ssize_t write_calibration(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) ++{ ++ int x, y, z; ++ struct mma7455l_info *mma = dev_get_drvdata(dev); ++ ++ if (sscanf(buf, "%d %d %d", &x, &y, &z) == 3) ++ { ++ mutex_lock(&mma->lock); ++ __reg_write_10(mma, MMA7455L_REG_XOFFL, MMA7455L_REG_XOFFH, x); ++ __reg_write_10(mma, MMA7455L_REG_YOFFL, MMA7455L_REG_YOFFH, y); ++ __reg_write_10(mma, MMA7455L_REG_ZOFFL, MMA7455L_REG_ZOFFH, z); ++ mutex_unlock(&mma->lock); ++ ++ return count; ++ } ++ ++ return -EINVAL; ++} ++ ++static DEVICE_ATTR(measure, S_IRUGO, show_measure, NULL); ++static DEVICE_ATTR(mode, S_IRUGO | S_IWUGO, show_mode, write_mode); ++static DEVICE_ATTR(gSelect, S_IRUGO | S_IWUGO, show_gSelect, write_gSelect); ++static DEVICE_ATTR(level_threshold, S_IRUGO | S_IWUGO, show_level_threshold, write_level_threshold); ++static DEVICE_ATTR(calibration, S_IRUGO | S_IWUGO, show_calibration, write_calibration); ++ ++static struct attribute *mma7455l_sysfs_entries[] = { ++ &dev_attr_measure.attr, ++ &dev_attr_mode.attr, ++ &dev_attr_gSelect.attr, ++ &dev_attr_level_threshold.attr, ++ &dev_attr_calibration.attr, ++ NULL ++}; ++ ++static struct attribute_group mma7455l_attr_group = { ++ .attrs = mma7455l_sysfs_entries, ++}; ++ ++/* input device handling and driver core interaction */ ++static int mma7455l_input_open(struct input_dev *inp) ++{ ++ struct mma7455l_info *mma = input_get_drvdata(inp); ++ if(mma->mode == MMA7455L_MODE_STANDBY) ++ update_mode(mma, MMA7455L_MODE_MEASUREMENT, mma->gSelect); ++ ++ return 0; ++} ++ ++static void mma7455l_input_close(struct input_dev *inp) ++{ ++ struct mma7455l_info *mma = input_get_drvdata(inp); ++ update_mode(mma, MMA7455L_MODE_STANDBY, MMA7455L_GSELECT_2); ++} ++ ++static int __devinit mma7455l_probe(struct spi_device *spi) ++{ ++ int rc; ++ struct mma7455l_info *mma; ++ struct mma7455l_platform_data *pdata = spi->dev.platform_data; ++ u_int8_t wai; ++ ++ mma = kzalloc(sizeof(*mma), GFP_KERNEL); ++ if (!mma) ++ return -ENOMEM; ++ ++ mutex_init(&mma->lock); ++ INIT_DELAYED_WORK(&mma->work, mma7455l_work); ++ mma->spi_dev = spi; ++ mma->flags = mma->working = 0; ++ ++ spi_set_drvdata(spi, mma); ++ ++ rc = spi_setup(spi); ++ if (rc < 0) { ++ printk(KERN_ERR "mma7455l error durign spi_setup of mma7455l driver\n"); ++ dev_set_drvdata(&spi->dev, NULL); ++ kfree(mma); ++ return rc; ++ } ++ ++ wai = reg_read(mma, MMA7455L_REG_WHOAMI); ++ if (wai != MMA7455L_WHOAMI_MAGIC) { ++ printk(KERN_ERR "mma7455l unknown whoami signature 0x%02x\n", wai); ++ dev_set_drvdata(&spi->dev, NULL); ++ kfree(mma); ++ return -ENODEV; ++ } ++ ++ rc = request_irq(mma->spi_dev->irq, mma7455l_interrupt, IRQF_TRIGGER_HIGH, ++ "mma7455l", mma); ++ if (rc < 0) { ++ dev_err(&spi->dev, "mma7455l error requesting IRQ %d\n", ++ mma->spi_dev->irq); ++ /* FIXME */ ++ return rc; ++ } ++ ++ rc = sysfs_create_group(&spi->dev.kobj, &mma7455l_attr_group); ++ if (rc) { ++ dev_err(&spi->dev, "error creating sysfs group\n"); ++ return rc; ++ } ++ ++ /* initialize input layer details */ ++ mma->input_dev = input_allocate_device(); ++ if (!mma->input_dev) { ++ dev_err(&spi->dev, "mma7455l Unable to allocate input device\n"); ++ /* FIXME */ ++ } ++ ++ set_bit(EV_ABS, mma->input_dev->evbit); ++ set_bit(ABS_X, mma->input_dev->absbit); ++ set_bit(ABS_Y, mma->input_dev->absbit); ++ set_bit(ABS_Z, mma->input_dev->absbit); ++ ++ input_set_drvdata(mma->input_dev, mma); ++ mma->input_dev->name = "MMA7455L"; ++ mma->input_dev->open = mma7455l_input_open; ++ mma->input_dev->close = mma7455l_input_close; ++ ++ rc = input_register_device(mma->input_dev); ++ if(!rc) ++ { ++ update_mode(mma, MMA7455L_MODE_STANDBY, MMA7455L_GSELECT_2); ++ ++ mutex_lock(&mma->lock); ++ __reg_write_10(mma, MMA7455L_REG_XOFFL, MMA7455L_REG_XOFFH, pdata->calibration_x); ++ __reg_write_10(mma, MMA7455L_REG_YOFFL, MMA7455L_REG_YOFFH, pdata->calibration_y); ++ __reg_write_10(mma, MMA7455L_REG_ZOFFL, MMA7455L_REG_ZOFFH, pdata->calibration_z); ++ mutex_unlock(&mma->lock); ++ ++ return 0; ++ } ++ ++ input_free_device(mma->input_dev); ++ return rc; ++} ++ ++static int __devexit mma7455l_remove(struct spi_device *spi) ++{ ++ struct mma7455l_info *mma = dev_get_drvdata(&spi->dev); ++ ++ sysfs_remove_group(&spi->dev.kobj, &mma7455l_attr_group); ++ input_unregister_device(mma->input_dev); ++ dev_set_drvdata(&spi->dev, NULL); ++ kfree(mma); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int mma7455l_suspend(struct spi_device *spi, pm_message_t message) ++{ ++ struct mma7455l_info *mma = dev_get_drvdata(&spi->dev); ++ get_mode(mma, &mma->mode, &mma->gSelect); ++ set_mode(mma, MMA7455L_MODE_STANDBY, MMA7455L_GSELECT_2); ++ ++ return 0; ++} ++ ++static int mma7455l_resume(struct spi_device *spi) ++{ ++ struct mma7455l_info *mma = dev_get_drvdata(&spi->dev); ++ update_mode(mma, mma->mode, mma->gSelect); ++ ++ return 0; ++} ++#else ++#define mma7455l_suspend NULL ++#define mma7455l_resume NULL ++#endif ++ ++static struct spi_driver mma7455l_driver = { ++ .driver = { ++ .name = "mma7455l", ++ .owner = THIS_MODULE, ++ }, ++ ++ .probe = mma7455l_probe, ++ .remove = __devexit_p(mma7455l_remove), ++ .suspend = mma7455l_suspend, ++ .resume = mma7455l_resume, ++}; ++ ++static int __init mma7455l_init(void) ++{ ++ return spi_register_driver(&mma7455l_driver); ++} ++ ++static void __exit mma7455l_exit(void) ++{ ++ spi_unregister_driver(&mma7455l_driver); ++} ++ ++MODULE_AUTHOR("Gregoire Gentil "); ++MODULE_LICENSE("GPL"); ++ ++module_init(mma7455l_init); ++module_exit(mma7455l_exit); +diff --git a/include/linux/mma7455l.h b/include/linux/mma7455l.h +new file mode 100644 +index 0000000..12ab50a +--- /dev/null ++++ b/include/linux/mma7455l.h +@@ -0,0 +1,11 @@ ++#ifndef _LINUX_MMA7455L_H ++#define _LINUX_MMA7455L_H ++ ++struct mma7455l_platform_data { ++ /* Calibration offsets */ ++ s16 calibration_x; ++ s16 calibration_y; ++ s16 calibration_z; ++}; ++ ++#endif /* _LINUX_MMA7455L_H */ + diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/accelerometer-touchscreen-mux-spi.patch b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/accelerometer-touchscreen-mux-spi.patch new file mode 100644 index 0000000000..57bb4196dd --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/accelerometer-touchscreen-mux-spi.patch @@ -0,0 +1,64 @@ +--- a/arch/arm/mach-omap2/devices.c 2009-02-17 21:15:12.000000000 -0800 ++++ b/arch/arm/mach-omap2/devices.c 2009-02-17 22:30:26.000000000 -0800 +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -347,6 +348,37 @@ + .platform_data = &omap2_mcspi4_config, + }, + }; ++ ++static struct spi_gpio_platform_data spi3_gpio_platform_data = { ++ .miso = 132, ++ .mosi = 131, ++ .sck = 130, ++ .num_chipselect = 1, ++}; ++ ++static struct platform_device spi3_gpio = { ++ .name = "spi_gpio", ++ .id = 3, ++ .dev = { ++ .platform_data = &spi3_gpio_platform_data, ++ }, ++}; ++ ++static struct spi_gpio_platform_data spi4_gpio_platform_data = { ++ .miso = 159, ++ .mosi = 158, ++ .sck = 156, ++ .num_chipselect = 1, ++}; ++ ++static struct platform_device spi4_gpio = { ++ .name = "spi_gpio", ++ .id = 4, ++ .dev = { ++ .platform_data = &spi4_gpio_platform_data, ++ }, ++}; ++ + #endif + + static void omap_init_mcspi(void) +--- a/drivers/input/touchscreen/ads7846.c 2009-04-07 10:04:12.000000000 -0700 ++++ b/drivers/input/touchscreen/ads7846.c 2009-04-11 12:39:51.000000000 -0700 +@@ -887,13 +919,6 @@ + return 0; + } + +- err = gpio_request(pdata->gpio_pendown, "ads7846_pendown"); +- if (err) { +- dev_err(&spi->dev, "failed to request pendown GPIO%d\n", +- pdata->gpio_pendown); +- return err; +- } +- + ts->gpio_pendown = pdata->gpio_pendown; + return 0; + } diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/aufs-1.patch b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/aufs-1.patch new file mode 100644 index 0000000000..3b54e71791 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/aufs-1.patch @@ -0,0 +1,35 @@ +aufs2 kbuild patch for linux-2.6.29 + +diff --git a/fs/Kconfig b/fs/Kconfig +index 93945dd..75156dd 100644 +--- a/fs/Kconfig ++++ b/fs/Kconfig +@@ -222,6 +222,7 @@ source "fs/qnx4/Kconfig" + source "fs/romfs/Kconfig" + source "fs/sysv/Kconfig" + source "fs/ufs/Kconfig" ++source "fs/aufs/Kconfig" + + endif # MISC_FILESYSTEMS + +diff --git a/fs/Makefile b/fs/Makefile +index dc20db3..a4e9a65 100644 +--- a/fs/Makefile ++++ b/fs/Makefile +@@ -124,3 +124,4 @@ obj-$(CONFIG_DEBUG_FS) += debugfs/ + obj-$(CONFIG_OCFS2_FS) += ocfs2/ + obj-$(CONFIG_BTRFS_FS) += btrfs/ + obj-$(CONFIG_GFS2_FS) += gfs2/ ++obj-$(CONFIG_AUFS_FS) += aufs/ +diff --git a/include/linux/Kbuild b/include/linux/Kbuild +index 106c3ba..d0c7262 100644 +--- a/include/linux/Kbuild ++++ b/include/linux/Kbuild +@@ -34,6 +34,7 @@ header-y += atmppp.h + header-y += atmsap.h + header-y += atmsvc.h + header-y += atm_zatm.h ++header-y += aufs_type.h + header-y += auto_fs4.h + header-y += ax25.h + header-y += b1lli.h diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/aufs-2.patch b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/aufs-2.patch new file mode 100644 index 0000000000..bc3d5d8795 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/aufs-2.patch @@ -0,0 +1,285 @@ +aufs2 standalone patch for linux-2.6.29 + +diff --git a/fs/namei.c b/fs/namei.c +index bbc15c2..f446c65 100644 +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -335,6 +335,9 @@ int deny_write_access(struct file * file) + + return 0; + } ++#ifdef CONFIG_AUFS_FS_MODULE ++EXPORT_SYMBOL(deny_write_access); ++#endif + + /** + * path_get - get a reference to a path +@@ -1196,7 +1199,7 @@ out: + * needs parent already locked. Doesn't follow mounts. + * SMP-safe. + */ +-static struct dentry *lookup_hash(struct nameidata *nd) ++struct dentry *lookup_hash(struct nameidata *nd) + { + int err; + +@@ -1205,8 +1208,11 @@ static struct dentry *lookup_hash(struct nameidata *nd) + return ERR_PTR(err); + return __lookup_hash(&nd->last, nd->path.dentry, nd); + } ++#ifdef CONFIG_AUFS_FS_MODULE ++EXPORT_SYMBOL(lookup_hash); ++#endif + +-static int __lookup_one_len(const char *name, struct qstr *this, ++int __lookup_one_len(const char *name, struct qstr *this, + struct dentry *base, int len) + { + unsigned long hash; +@@ -1227,6 +1233,9 @@ static int __lookup_one_len(const char *name, struct qstr *this, + this->hash = end_name_hash(hash); + return 0; + } ++#ifdef CONFIG_AUFS_FS_MODULE ++EXPORT_SYMBOL(__lookup_one_len); ++#endif + + /** + * lookup_one_len - filesystem helper to lookup single pathname component +diff --git a/fs/namespace.c b/fs/namespace.c +index 06f8e63..dcdc4ff 100644 +--- a/fs/namespace.c ++++ b/fs/namespace.c +@@ -37,6 +37,9 @@ + + /* spinlock for vfsmount related operations, inplace of dcache_lock */ + __cacheline_aligned_in_smp DEFINE_SPINLOCK(vfsmount_lock); ++#if defined(CONFIG_AUFS_FS_MODULE) && defined(CONFIG_AUFS_EXPORT) ++EXPORT_SYMBOL(vfsmount_lock); ++#endif + + static int event; + static DEFINE_IDA(mnt_id_ida); +diff --git a/fs/open.c b/fs/open.c +index a3a78ce..7d70245 100644 +--- a/fs/open.c ++++ b/fs/open.c +@@ -220,6 +220,9 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs, + mutex_unlock(&dentry->d_inode->i_mutex); + return err; + } ++#ifdef CONFIG_AUFS_FS_MODULE ++EXPORT_SYMBOL(do_truncate); ++#endif + + static long do_sys_truncate(const char __user *pathname, loff_t length) + { +diff --git a/fs/splice.c b/fs/splice.c +index 4ed0ba4..a110d76 100644 +--- a/fs/splice.c ++++ b/fs/splice.c +@@ -888,8 +888,8 @@ EXPORT_SYMBOL(generic_splice_sendpage); + /* + * Attempt to initiate a splice from pipe to file. + */ +-static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, +- loff_t *ppos, size_t len, unsigned int flags) ++long do_splice_from(struct pipe_inode_info *pipe, struct file *out, ++ loff_t *ppos, size_t len, unsigned int flags) + { + int ret; + +@@ -908,13 +908,16 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, + + return out->f_op->splice_write(pipe, out, ppos, len, flags); + } ++#ifdef CONFIG_AUFS_FS_MODULE ++EXPORT_SYMBOL(do_splice_from); ++#endif + + /* + * Attempt to initiate a splice from a file to a pipe. + */ +-static long do_splice_to(struct file *in, loff_t *ppos, +- struct pipe_inode_info *pipe, size_t len, +- unsigned int flags) ++long do_splice_to(struct file *in, loff_t *ppos, ++ struct pipe_inode_info *pipe, size_t len, ++ unsigned int flags) + { + int ret; + +@@ -930,6 +933,9 @@ static long do_splice_to(struct file *in, loff_t *ppos, + + return in->f_op->splice_read(in, ppos, pipe, len, flags); + } ++#ifdef CONFIG_AUFS_FS_MODULE ++EXPORT_SYMBOL(do_splice_to); ++#endif + + /** + * splice_direct_to_actor - splices data directly between two non-pipes +diff --git a/fs/super.c b/fs/super.c +index 6ce5014..95eefb9 100644 +--- a/fs/super.c ++++ b/fs/super.c +@@ -287,6 +287,9 @@ int fsync_super(struct super_block *sb) + __fsync_super(sb); + return sync_blockdev(sb->s_bdev); + } ++#ifdef CONFIG_AUFS_FS_MODULE ++EXPORT_SYMBOL(fsync_super); ++#endif + + /** + * generic_shutdown_super - common helper for ->kill_sb() +diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h +index 23bf02f..49e5b47 100644 +--- a/include/linux/lockdep.h ++++ b/include/linux/lockdep.h +@@ -58,7 +58,7 @@ enum lock_usage_bit + #define LOCKF_USED_IN_IRQ_READ \ + (LOCKF_USED_IN_HARDIRQ_READ | LOCKF_USED_IN_SOFTIRQ_READ) + +-#define MAX_LOCKDEP_SUBCLASSES 8UL ++#define MAX_LOCKDEP_SUBCLASSES 12UL + + /* + * Lock-classes are keyed via unique addresses, by embedding the +diff --git a/include/linux/namei.h b/include/linux/namei.h +index fc2e035..182d43b 100644 +--- a/include/linux/namei.h ++++ b/include/linux/namei.h +@@ -75,6 +75,9 @@ extern struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry + extern struct file *nameidata_to_filp(struct nameidata *nd, int flags); + extern void release_open_intent(struct nameidata *); + ++extern struct dentry *lookup_hash(struct nameidata *nd); ++extern int __lookup_one_len(const char *name, struct qstr *this, ++ struct dentry *base, int len); + extern struct dentry *lookup_one_len(const char *, struct dentry *, int); + extern struct dentry *lookup_one_noperm(const char *, struct dentry *); + +diff --git a/include/linux/splice.h b/include/linux/splice.h +index 528dcb9..5123bc6 100644 +--- a/include/linux/splice.h ++++ b/include/linux/splice.h +@@ -71,4 +71,10 @@ extern ssize_t splice_to_pipe(struct pipe_inode_info *, + extern ssize_t splice_direct_to_actor(struct file *, struct splice_desc *, + splice_direct_actor *); + ++extern long do_splice_from(struct pipe_inode_info *pipe, struct file *out, ++ loff_t *ppos, size_t len, unsigned int flags); ++extern long do_splice_to(struct file *in, loff_t *ppos, ++ struct pipe_inode_info *pipe, size_t len, ++ unsigned int flags); ++ + #endif +diff --git a/security/device_cgroup.c b/security/device_cgroup.c +index 3aacd0f..76c8876 100644 +--- a/security/device_cgroup.c ++++ b/security/device_cgroup.c +@@ -507,6 +507,9 @@ acc_check: + + return -EPERM; + } ++#ifdef CONFIG_AUFS_FS_MODULE ++EXPORT_SYMBOL(devcgroup_inode_permission); ++#endif + + int devcgroup_inode_mknod(int mode, dev_t dev) + { +diff --git a/security/security.c b/security/security.c +index c3586c0..f8798f8 100644 +--- a/security/security.c ++++ b/security/security.c +@@ -389,6 +389,9 @@ int security_path_mkdir(struct path *path, struct dentry *dentry, int mode) + return 0; + return security_ops->path_mkdir(path, dentry, mode); + } ++#ifdef CONFIG_AUFS_FS_MODULE ++EXPORT_SYMBOL(security_path_mkdir); ++#endif + + int security_path_rmdir(struct path *path, struct dentry *dentry) + { +@@ -396,6 +399,9 @@ int security_path_rmdir(struct path *path, struct dentry *dentry) + return 0; + return security_ops->path_rmdir(path, dentry); + } ++#ifdef CONFIG_AUFS_FS_MODULE ++EXPORT_SYMBOL(security_path_rmdir); ++#endif + + int security_path_unlink(struct path *path, struct dentry *dentry) + { +@@ -403,6 +409,9 @@ int security_path_unlink(struct path *path, struct dentry *dentry) + return 0; + return security_ops->path_unlink(path, dentry); + } ++#ifdef CONFIG_AUFS_FS_MODULE ++EXPORT_SYMBOL(security_path_unlink); ++#endif + + int security_path_symlink(struct path *path, struct dentry *dentry, + const char *old_name) +@@ -411,6 +420,9 @@ int security_path_symlink(struct path *path, struct dentry *dentry, + return 0; + return security_ops->path_symlink(path, dentry, old_name); + } ++#ifdef CONFIG_AUFS_FS_MODULE ++EXPORT_SYMBOL(security_path_symlink); ++#endif + + int security_path_link(struct dentry *old_dentry, struct path *new_dir, + struct dentry *new_dentry) +@@ -419,6 +431,9 @@ int security_path_link(struct dentry *old_dentry, struct path *new_dir, + return 0; + return security_ops->path_link(old_dentry, new_dir, new_dentry); + } ++#ifdef CONFIG_AUFS_FS_MODULE ++EXPORT_SYMBOL(security_path_link); ++#endif + + int security_path_rename(struct path *old_dir, struct dentry *old_dentry, + struct path *new_dir, struct dentry *new_dentry) +@@ -429,6 +444,9 @@ int security_path_rename(struct path *old_dir, struct dentry *old_dentry, + return security_ops->path_rename(old_dir, old_dentry, new_dir, + new_dentry); + } ++#ifdef CONFIG_AUFS_FS_MODULE ++EXPORT_SYMBOL(security_path_rename); ++#endif + + int security_path_truncate(struct path *path, loff_t length, + unsigned int time_attrs) +@@ -437,6 +455,9 @@ int security_path_truncate(struct path *path, loff_t length, + return 0; + return security_ops->path_truncate(path, length, time_attrs); + } ++#ifdef CONFIG_AUFS_FS_MODULE ++EXPORT_SYMBOL(security_path_truncate); ++#endif + #endif + + int security_inode_create(struct inode *dir, struct dentry *dentry, int mode) +@@ -506,6 +527,9 @@ int security_inode_readlink(struct dentry *dentry) + return 0; + return security_ops->inode_readlink(dentry); + } ++#ifdef CONFIG_AUFS_FS_MODULE ++EXPORT_SYMBOL(security_inode_readlink); ++#endif + + int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd) + { +@@ -520,6 +544,9 @@ int security_inode_permission(struct inode *inode, int mask) + return 0; + return security_ops->inode_permission(inode, mask); + } ++#ifdef CONFIG_AUFS_FS_MODULE ++EXPORT_SYMBOL(security_inode_permission); ++#endif + + int security_inode_setattr(struct dentry *dentry, struct iattr *attr) + { diff --git a/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/aufs-3.patch b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/aufs-3.patch new file mode 100644 index 0000000000..34a035a037 --- /dev/null +++ b/recipes/linux/linux-omap-pm-2.6.29/omap3-touchbook/aufs-3.patch @@ -0,0 +1,23604 @@ +diff -Naur a/Documentation/ABI/testing/debugfs-aufs b/Documentation/ABI/testing/debugfs-aufs +--- a/Documentation/ABI/testing/debugfs-aufs 1969-12-31 16:00:00.000000000 -0800 ++++ b/Documentation/ABI/testing/debugfs-aufs 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,40 @@ ++What: /debug/aufs/si_/ ++Date: March 2009 ++Contact: J. R. Okajima ++Description: ++ Under /debug/aufs, a directory named si_ is created ++ per aufs mount, where is a unique id generated ++ internally. ++ ++What: /debug/aufs/si_/xib ++Date: March 2009 ++Contact: J. R. Okajima ++Description: ++ It shows the consumed blocks by xib (External Inode Number ++ Bitmap), its block size and file size. ++ When the aufs mount option 'noxino' is specified, it ++ will be empty. About XINO files, see ++ Documentation/filesystems/aufs/aufs.5 in detail. ++ ++What: /debug/aufs/si_/xino0, xino1 ... xinoN ++Date: March 2009 ++Contact: J. R. Okajima ++Description: ++ It shows the consumed blocks by xino (External Inode Number ++ Translation Table), its link count, block size and file ++ size. ++ When the aufs mount option 'noxino' is specified, it ++ will be empty. About XINO files, see ++ Documentation/filesystems/aufs/aufs.5 in detail. ++ ++What: /debug/aufs/si_/xigen ++Date: March 2009 ++Contact: J. R. Okajima ++Description: ++ It shows the consumed blocks by xigen (External Inode ++ Generation Table), its block size and file size. ++ If CONFIG_AUFS_EXPORT is disabled, this entry will not ++ be created. ++ When the aufs mount option 'noxino' is specified, it ++ will be empty. About XINO files, see ++ Documentation/filesystems/aufs/aufs.5 in detail. +diff -Naur a/Documentation/ABI/testing/sysfs-aufs b/Documentation/ABI/testing/sysfs-aufs +--- a/Documentation/ABI/testing/sysfs-aufs 1969-12-31 16:00:00.000000000 -0800 ++++ b/Documentation/ABI/testing/sysfs-aufs 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,25 @@ ++What: /sys/fs/aufs/si_/ ++Date: March 2009 ++Contact: J. R. Okajima ++Description: ++ Under /sys/fs/aufs, a directory named si_ is created ++ per aufs mount, where is a unique id generated ++ internally. ++ ++What: /sys/fs/aufs/si_/br0, br1 ... brN ++Date: March 2009 ++Contact: J. R. Okajima ++Description: ++ It shows the abolute path of a member directory (which ++ is called branch) in aufs, and its permission. ++ ++What: /sys/fs/aufs/si_/xi_path ++Date: March 2009 ++Contact: J. R. Okajima ++Description: ++ It shows the abolute path of XINO (External Inode Number ++ Bitmap, Translation Table and Generation Table) file ++ even if it is the default path. ++ When the aufs mount option 'noxino' is specified, it ++ will be empty. About XINO files, see ++ Documentation/filesystems/aufs/aufs.5 in detail. +diff -Naur a/fs/aufs/aufs.h b/fs/aufs/aufs.h +--- a/fs/aufs/aufs.h 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/aufs.h 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,51 @@ ++/* ++ * Copyright (C) 2005-2009 Junjiro R. Okajima ++ * ++ * This program, aufs 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * all header files ++ */ ++ ++#ifndef __AUFS_H__ ++#define __AUFS_H__ ++ ++#ifdef __KERNEL__ ++ ++#include "debug.h" ++ ++#include "branch.h" ++#include "cpup.h" ++#include "dcsub.h" ++#include "dbgaufs.h" ++#include "dentry.h" ++#include "dir.h" ++#include "file.h" ++#include "fstype.h" ++#include "inode.h" ++#include "loop.h" ++#include "module.h" ++#include "opts.h" ++#include "rwsem.h" ++#include "spl.h" ++#include "super.h" ++#include "sysaufs.h" ++#include "vfsub.h" ++#include "whout.h" ++#include "wkq.h" ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_H__ */ +diff -Naur a/fs/aufs/branch.c b/fs/aufs/branch.c +--- a/fs/aufs/branch.c 1969-12-31 16:00:00.000000000 -0800 ++++ b/fs/aufs/branch.c 2009-06-22 23:36:25.000000000 -0700 +@@ -0,0 +1,956 @@ ++/* ++ * Copyright (C) 2005-2009 Junjiro R. Okajima ++ * ++ * This program, aufs 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * branch management ++ */ ++ ++#include ++#include "aufs.h" ++ ++/* ++ * free a single branch ++ */ ++static void au_br_do_free(struct au_branch *br) ++{ ++ int i; ++ struct au_wbr *wbr; ++ ++ if (br->br_xino.xi_file) ++ fput(br->br_xino.xi_file); ++ mutex_destroy(&br->br_xino.xi_nondir_mtx); ++ ++ AuDebugOn(atomic_read(&br->br_count)); ++ ++ wbr = br->br_wbr; ++ if (wbr) { ++ for (i = 0; i < AuBrWh_Last; i++) ++ dput(wbr->wbr_wh[i]); ++ AuDebugOn(atomic_read(&wbr->wbr_wh_running)); ++ au_rwsem_destroy(&wbr->wbr_wh_rwsem); ++ } ++ ++ /* some filesystems acquire extra lock */ ++ lockdep_off(); ++ mntput(br->br_mnt); ++ lockdep_on(); ++ ++ kfree(wbr); ++ kfree(br); ++} ++ ++/* ++ * frees all branches ++ */ ++void au_br_free(struct au_sbinfo *sbinfo) ++{ ++ aufs_bindex_t bmax; ++ struct au_branch **br; ++ ++ bmax = sbinfo->si_bend + 1; ++ br = sbinfo->si_branch; ++ while (bmax--) ++ au_br_do_free(*br++); ++} ++ ++/* ++ * find the index of a branch which is specified by @br_id. ++ */ ++int au_br_index(struct super_block *sb, aufs_bindex_t br_id) ++{ ++ aufs_bindex_t bindex, bend; ++ ++ bend = au_sbend(sb); ++ for (bindex = 0; bindex <= bend; bindex++) ++ if (au_sbr_id(sb, bindex) == br_id) ++ return bindex; ++ return -1; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * add a branch ++ */ ++ ++static int test_overlap(struct super_block *sb, struct dentry *h_d1, ++ struct dentry *h_d2) ++{ ++ if (unlikely(h_d1 == h_d2)) ++ return 1; ++ return !!au_test_subdir(h_d1, h_d2) ++ || !!au_test_subdir(h_d2, h_d1) ++ || au_test_loopback_overlap(sb, h_d1, h_d2) ++ || au_test_loopback_overlap(sb, h_d2, h_d1); ++} ++ ++/* ++ * returns a newly allocated branch. @new_nbranch is a number of branches ++ * after adding a branch. ++ */ ++static struct au_branch *au_br_alloc(struct super_block *sb, int new_nbranch, ++ int perm) ++{ ++ struct au_branch *add_branch; ++ struct dentry *root; ++ ++ root = sb->s_root; ++ add_branch = kmalloc(sizeof(*add_branch), GFP_NOFS); ++ if (unlikely(!add_branch)) ++ goto out; ++ ++ add_branch->br_wbr = NULL; ++ if (au_br_writable(perm)) { ++ /* may be freed separately at changing the branch permission */ ++ add_branch->br_wbr = kmalloc(sizeof(*add_branch->br_wbr), ++ GFP_NOFS); ++ if (unlikely(!add_branch->br_wbr)) ++ goto out_br; ++ } ++ ++ if (unlikely(au_sbr_realloc(au_sbi(sb), new_nbranch) ++ || au_di_realloc(au_di(root), new_nbranch) ++ || au_ii_realloc(au_ii(root->d_inode), new_nbranch))) ++ goto out_wbr; ++ return add_branch; /* success */ ++ ++ out_wbr: ++ kfree(add_branch->br_wbr); ++ out_br: ++ kfree(add_branch); ++ out: ++ return ERR_PTR(-ENOMEM); ++} ++ ++/* ++ * test if the branch permission is legal or not. ++ */ ++static int test_br(struct inode *inode, int brperm, char *path) ++{ ++ int err; ++ ++ err = 0; ++ if (unlikely(au_br_writable(brperm) && IS_RDONLY(inode))) { ++ AuErr("write permission for readonly mount or inode, %s\n", ++ path); ++ err = -EINVAL; ++ } ++ ++ return err; ++} ++ ++/* ++ * returns: ++ * 0: success, the caller will add it ++ * plus: success, it is already unified, the caller should ignore it ++ * minus: error ++ */ ++static int test_add(struct super_block *sb, struct au_opt_add *add, int remount) ++{ ++ int err; ++ aufs_bindex_t bend, bindex; ++ struct dentry *root; ++ struct inode *inode, *h_inode; ++ ++ root = sb->s_root; ++ bend = au_sbend(sb); ++ if (unlikely(bend >= 0 ++ && au_find_dbindex(root, add->path.dentry) >= 0)) { ++ err = 1; ++ if (!remount) { ++ err = -EINVAL; ++ AuErr("%s duplicated\n", add->pathname); ++ } ++ goto out; ++ } ++ ++ err = -ENOSPC; /* -E2BIG; */ ++ if (unlikely(AUFS_BRANCH_MAX <= add->bindex ++ || AUFS_BRANCH_MAX - 1 <= bend)) { ++ AuErr("number of branches exceeded %s\n", add->pathname); ++ goto out; ++ } ++ ++ err = -EDOM; ++ if (unlikely(add->bindex < 0 || bend + 1 < add->bindex)) { ++ AuErr("bad index %d\n", add->bindex); ++ goto out; ++ } ++ ++ inode = add->path.dentry->d_inode; ++ err = -ENOENT; ++ if (unlikely(!inode->i_nlink)) { ++ AuErr("no existence %s\n", add->pathname); ++ goto out; ++ } ++ ++ err = -EINVAL; ++ if (unlikely(inode->i_sb == sb)) { ++ AuErr("%s must be outside\n", add->pathname); ++ goto out; ++ } ++ ++ if (unlikely(au_test_fs_unsuppoted(inode->i_sb))) { ++ AuErr("unsupported filesystem, %s (%s)\n", ++ add->pathname, au_sbtype(inode->i_sb)); ++ goto out; ++ } ++ ++ err = test_br(add->path.dentry->d_inode, add->perm, add->pathname); ++ if (unlikely(err)) ++ goto out; ++ ++ if (bend < 0) ++ return 0; /* success */ ++ ++ err = -EINVAL; ++ for (bindex = 0; bindex <= bend; bindex++) ++ if (unlikely(test_overlap(sb, add->path.dentry, ++ au_h_dptr(root, bindex)))) { ++ AuErr("%s is overlapped\n", add->pathname); ++ goto out; ++ } ++ ++ err = 0; ++ if (au_opt_test(au_mntflags(sb), WARN_PERM)) { ++ h_inode = au_h_dptr(root, 0)->d_inode; ++ if ((h_inode->i_mode & S_IALLUGO) != (inode->i_mode & S_IALLUGO) ++ || h_inode->i_uid != inode->i_uid ++ || h_inode->i_gid != inode->i_gid) ++ AuWarn("uid/gid/perm %s %u/%u/0%o, %u/%u/0%o\n", ++ add->pathname, ++ inode->i_uid, inode->i_gid, ++ (inode->i_mode & S_IALLUGO), ++ h_inode->i_uid, h_inode->i_gid, ++ (h_inode->i_mode & S_IALLUGO)); ++ } ++ ++ out: ++ return err; ++} ++ ++/* ++ * initialize or clean the whiteouts for an adding branch ++ */ ++static int au_br_init_wh(struct super_block *sb, struct au_branch *br, ++ int new_perm, struct dentry *h_root) ++{ ++ int err, old_perm; ++ aufs_bindex_t bindex; ++ struct mutex *h_mtx; ++ struct au_wbr *wbr; ++ struct au_hinode *hdir; ++ ++ wbr = br->br_wbr; ++ old_perm = br->br_perm; ++ br->br_perm = new_perm; ++ hdir = NULL; ++ h_mtx = NULL; ++ bindex = au_br_index(sb, br->br_id); ++ if (0 <= bindex) { ++ hdir = au_hi(sb->s_root->d_inode, bindex); ++ au_hin_imtx_lock_nested(hdir, AuLsc_I_PARENT); ++ } else { ++ h_mtx = &h_root->d_inode->i_mutex; ++ mutex_lock_nested(h_mtx, AuLsc_I_PARENT); ++ } ++ if (!wbr) ++ err = au_wh_init(h_root, br, sb); ++ else { ++ wbr_wh_write_lock(wbr); ++ err = au_wh_init(h_root, br, sb); ++ wbr_wh_write_unlock(wbr); ++ } ++ if (hdir) ++ au_hin_imtx_unlock(hdir); ++ else ++ mutex_unlock(h_mtx); ++ br->br_perm = old_perm; ++ ++ if (!err && wbr && !au_br_writable(new_perm)) { ++ kfree(wbr); ++ br->br_wbr = NULL; ++ } ++ ++ return err; ++} ++ ++static int au_wbr_init(struct au_branch *br, struct super_block *sb, ++ int perm, struct path *path) ++{ ++ int err; ++ struct au_wbr *wbr; ++ ++ wbr = br->br_wbr; ++ init_rwsem(&wbr->wbr_wh_rwsem); ++ memset(wbr->wbr_wh, 0, sizeof(wbr->wbr_wh)); ++ atomic_set(&wbr->wbr_wh_running, 0); ++ wbr->wbr_bytes = 0; ++ ++ err = au_br_init_wh(sb, br, perm, path->dentry); ++ ++ return err; ++} ++ ++/* intialize a new branch */ ++static int au_br_init(struct au_branch *br, struct super_block *sb, ++ struct au_opt_add *add) ++{ ++ int err; ++ ++ err = 0; ++ memset(&br->br_xino, 0, sizeof(br->br_xino)); ++ mutex_init(&br->br_xino.xi_nondir_mtx); ++ br->br_perm = add->perm; ++ br->br_mnt = add->path.mnt; /* set first, mntget() later */ ++ atomic_set(&br->br_count, 0); ++ br->br_xino_upper = AUFS_XINO_TRUNC_INIT; ++ atomic_set(&br->br_xino_running, 0); ++ br->br_id = au_new_br_id(sb); ++ ++ if (au_br_writable(add->perm)) { ++ err = au_wbr_init(br, sb, add->perm, &add->path); ++ if (unlikely(err)) ++ goto out; ++ } ++ ++ if (au_opt_test(au_mntflags(sb), XINO)) { ++ err = au_xino_br(sb, br, add->path.dentry->d_inode->i_ino, ++ au_sbr(sb, 0)->br_xino.xi_file, /*do_test*/1); ++ if (unlikely(err)) { ++ AuDebugOn(br->br_xino.xi_file); ++ goto out; ++ } ++ } ++ ++ sysaufs_br_init(br); ++ mntget(add->path.mnt); ++ ++ out: ++ return err; ++} ++ ++static void au_br_do_add_brp(struct au_sbinfo *sbinfo, aufs_bindex_t bindex, ++ struct au_branch *br, aufs_bindex_t bend, ++ aufs_bindex_t amount) ++{ ++ struct au_branch **brp; ++ ++ brp = sbinfo->si_branch + bindex; ++ memmove(brp + 1, brp, sizeof(*brp) * amount); ++ *brp = br; ++ sbinfo->si_bend++; ++ if (unlikely(bend < 0)) ++ sbinfo->si_bend = 0; ++} ++ ++static void au_br_do_add_hdp(struct au_dinfo *dinfo, aufs_bindex_t bindex, ++ aufs_bindex_t bend, aufs_bindex_t amount) ++{ ++ struct au_hdentry *hdp; ++ ++ hdp = dinfo->di_hdentry + bindex; ++ memmove(hdp + 1, hdp, sizeof(*hdp) * amount); ++ au_h_dentry_init(hdp); ++ dinfo->di_bend++; ++ if (unlikely(bend < 0)) ++ dinfo->di_bstart = 0; ++} ++ ++static void au_br_do_add_hip(struct au_iinfo *iinfo, aufs_bindex_t bindex, ++ aufs_bindex_t bend, aufs_bindex_t amount) ++{ ++ struct au_hinode *hip; ++ ++ hip = iinfo->ii_hinode + bindex; ++ memmove(hip + 1, hip, sizeof(*hip) * amount); ++ hip->hi_inode = NULL; ++ au_hin_init(hip, NULL); ++ iinfo->ii_bend++; ++ if (unlikely(bend < 0)) ++ iinfo->ii_bstart = 0; ++} ++ ++static void au_br_do_add(struct super_block *sb, struct dentry *h_dentry, ++ struct au_branch *br, aufs_bindex_t bindex) ++{ ++ struct dentry *root; ++ struct inode *root_inode; ++ aufs_bindex_t bend, amount; ++ ++ root = sb->s_root; ++ root_inode = root->d_inode; ++ au_plink_block_maintain(sb); ++ bend = au_sbend(sb); ++ amount = bend + 1 - bindex; ++ au_br_do_add_brp(au_sbi(sb), bindex, br, bend, amount); ++ au_br_do_add_hdp(au_di(root), bindex, bend, amount); ++ au_br_do_add_hip(au_ii(root_inode), bindex, bend, amount); ++ au_set_h_dptr(root, bindex, dget(h_dentry)); ++ au_set_h_iptr(root_inode, bindex, au_igrab(h_dentry->d_inode), ++ /*flags*/0); ++} ++ ++int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount) ++{ ++ int err; ++ unsigned long long maxb; ++ aufs_bindex_t bend, add_bindex; ++ struct dentry *root, *h_dentry; ++ struct inode *root_inode; ++ struct au_branch *add_branch; ++ ++ root = sb->s_root; ++ root_inode = root->d_inode; ++ IMustLock(root_inode); ++ err = test_add(sb, add, remount); ++ if (unlikely(err < 0)) ++ goto out; ++ if (err) { ++ err = 0; ++ goto out; /* success */ ++ } ++ ++ bend = au_sbend(sb); ++ add_branch = au_br_alloc(sb, bend + 2, add->perm); ++ err = PTR_ERR(add_branch); ++ if (IS_ERR(add_branch)) ++ goto out; ++ ++ err = au_br_init(add_branch, sb, add); ++ if (unlikely(err)) { ++ au_br_do_free(add_branch); ++ goto out; ++ } ++ ++ add_bindex = add->bindex; ++ h_dentry = add->path.dentry; ++ if (!remount) ++ au_br_do_add(sb, h_dentry, add_branch, add_bindex); ++ else { ++ sysaufs_brs_del(sb, add_bindex); ++ au_br_do_add(sb, h_dentry, add_branch, add_bindex); ++ sysaufs_brs_add(sb, add_bindex); ++ } ++ ++ if (!add_bindex) ++ au_cpup_attr_all(root_inode, /*force*/1); ++ else ++ au_add_nlink(root_inode, h_dentry->d_inode); ++ maxb = h_dentry->d_sb->s_maxbytes; ++ if (sb->s_maxbytes < maxb) ++ sb->s_maxbytes = maxb; ++ ++ /* ++ * this test/set prevents aufs from handling unnecesary inotify events ++ * of xino files, in a case of re-adding a writable branch which was ++ * once detached from aufs. ++ */ ++ if (au_xino_brid(sb) < 0 ++ && au_br_writable(add_branch->br_perm) ++ && !au_test_fs_bad_xino(h_dentry->d_sb) ++ && add_branch->br_xino.xi_file ++ && add_branch->br_xino.xi_file->f_dentry->d_parent == h_dentry) ++ au_xino_brid_set(sb, add_branch->br_id); ++ ++ out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * delete a branch ++ */ ++ ++/* to show the line number, do not make it inlined function */ ++#define AuVerbose(do_info, fmt, args...) do { \ ++ if (do_info) \ ++ AuInfo(fmt, ##args); \ ++} while (0) ++ ++/* ++ * test if the branch is deletable or not. ++ */ ++static int test_dentry_busy(struct dentry *root, aufs_bindex_t bindex, ++ unsigned int sigen) ++{ ++ int err, i, j, ndentry; ++ aufs_bindex_t bstart, bend; ++ unsigned char verbose; ++ struct au_dcsub_pages dpages; ++ struct au_dpage *dpage; ++ struct dentry *d; ++ struct inode *inode; ++ ++ err = au_dpages_init(&dpages, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ err = au_dcsub_pages(&dpages, root, NULL, NULL); ++ if (unlikely(err)) ++ goto out_dpages; ++ ++ verbose = !!au_opt_test(au_mntflags(root->d_sb), VERBOSE); ++ for (i = 0; !err && i < dpages.ndpage; i++) { ++ dpage = dpages.dpages + i; ++ ndentry = dpage->ndentry; ++ for (j = 0; !err && j < ndentry; j++) { ++ d = dpage->dentries[j]; ++ AuDebugOn(!atomic_read(&d->d_count)); ++ inode = d->d_inode; ++ if (au_digen(d) == sigen && au_iigen(inode) == sigen) ++ di_read_lock_child(d, AuLock_IR); ++ else { ++ di_write_lock_child(d); ++ err = au_reval_dpath(d, sigen); ++ if (!err) ++ di_downgrade_lock(d, AuLock_IR); ++ else { ++ di_write_unlock(d); ++ break; ++ } ++ } ++ ++ bstart = au_dbstart(d); ++ bend = au_dbend(d); ++ if (bstart <= bindex ++ && bindex <= bend ++ && au_h_dptr(d, bindex) ++ && (!S_ISDIR(inode->i_mode) || bstart == bend)) { ++ err = -EBUSY; ++ AuVerbose(verbose, "busy %.*s\n", AuDLNPair(d)); ++ } ++ di_read_unlock(d, AuLock_IR); ++ } ++ } ++ ++ out_dpages: ++ au_dpages_free(&dpages); ++ out: ++ return err; ++} ++ ++static int test_inode_busy(struct super_block *sb, aufs_bindex_t bindex, ++ unsigned int sigen) ++{ ++ int err; ++ struct inode *i; ++ aufs_bindex_t bstart, bend; ++ unsigned char verbose; ++ ++ err = 0; ++ verbose = !!au_opt_test(au_mntflags(sb), VERBOSE); ++ list_for_each_entry(i, &sb->s_inodes, i_sb_list) { ++ AuDebugOn(!atomic_read(&i->i_count)); ++ if (!list_empty(&i->i_dentry)) ++ continue; ++ ++ if (au_iigen(i) == sigen) ++ ii_read_lock_child(i); ++ else { ++ ii_write_lock_child(i); ++ err = au_refresh_hinode_self(i, /*do_attr*/1); ++ if (!err) ++ ii_downgrade_lock(i); ++ else { ++ ii_write_unlock(i); ++ break; ++ } ++ } ++ ++ bstart = au_ibstart(i); ++ bend = au_ibend(i); ++ if (bstart <= bindex ++ && bindex <= bend ++ && au_h_iptr(i, bindex) ++ && (!S_ISDIR(i->i_mode) || bstart == bend)) { ++ err = -EBUSY; ++ AuVerbose(verbose, "busy i%lu\n", i->i_ino); ++ ii_read_unlock(i); ++ break; ++ } ++ ii_read_unlock(i); ++ } ++ ++ return err; ++} ++ ++static int test_children_busy(struct dentry *root, aufs_bindex_t bindex) ++{ ++ int err; ++ unsigned int sigen; ++ ++ sigen = au_sigen(root->d_sb); ++ DiMustNoWaiters(root); ++ IiMustNoWaiters(root->d_inode); ++ di_write_unlock(root); ++ err = test_dentry_busy(root, bindex, sigen); ++ if (!err) ++ err = test_inode_busy(root->d_sb, bindex, sigen); ++ di_write_lock_child(root); /* aufs_write_lock() calls ..._child() */ ++ ++ return err; ++} ++ ++static void au_br_do_del_brp(struct au_sbinfo *sbinfo, ++ const aufs_bindex_t bindex, ++ const aufs_bindex_t bend) ++{ ++ struct au_branch **brp, **p; ++ ++ brp = sbinfo->si_branch + bindex; ++ if (bindex < bend) ++ memmove(brp, brp + 1, sizeof(*brp) * (bend - bindex)); ++ sbinfo->si_branch[0 + bend] = NULL; ++ sbinfo->si_bend--; ++ ++ p = krealloc(sbinfo->si_branch, sizeof(*p) * bend, GFP_NOFS); ++ if (p) ++ sbinfo->si_branch = p; ++} ++ ++static void au_br_do_del_hdp(struct au_dinfo *dinfo, const aufs_bindex_t bindex, ++ const aufs_bindex_t bend) ++{ ++ struct au_hdentry *hdp, *p; ++ ++ hdp = dinfo->di_hdentry + bindex; ++ if (bindex < bend) ++ memmove(hdp, hdp + 1, sizeof(*hdp) * (bend - bindex)); ++ dinfo->di_hdentry[0 + bend].hd_dentry = NULL; ++ dinfo->di_bend--; ++ ++ p = krealloc(dinfo->di_hdentry, sizeof(*p) * bend, GFP_NOFS); ++ if (p) ++ dinfo->di_hdentry = p; ++} ++ ++static void au_br_do_del_hip(struct au_iinfo *iinfo, const aufs_bindex_t bindex, ++ const aufs_bindex_t bend) ++{ ++ struct au_hinode *hip, *p; ++ ++ hip = iinfo->ii_hinode + bindex; ++ if (bindex < bend) ++ memmove(hip, hip + 1, sizeof(*hip) * (bend - bindex)); ++ iinfo->ii_hinode[0 + bend].hi_inode = NULL; ++ au_hin_init(iinfo->ii_hinode + bend, NULL); ++ iinfo->ii_bend--; ++ ++ p = krealloc(iinfo->ii_hinode, sizeof(*p) * bend, GFP_NOFS); ++ if (p) ++ iinfo->ii_hinode = p; ++} ++ ++static void au_br_do_del(struct super_block *sb, aufs_bindex_t bindex, ++ struct au_branch *br) ++{ ++ aufs_bindex_t bend; ++ struct au_sbinfo *sbinfo; ++ struct dentry *root; ++ struct inode *inode; ++ ++ root = sb->s_root; ++ inode = root->d_inode; ++ au_plink_block_maintain(sb); ++ sbinfo = au_sbi(sb); ++ bend = sbinfo->si_bend; ++ ++ dput(au_h_dptr(root, bindex)); ++ au_hiput(au_hi(inode, bindex)); ++ au_br_do_free(br); ++ ++ au_br_do_del_brp(sbinfo, bindex, bend); ++ au_br_do_del_hdp(au_di(root), bindex, bend); ++ au_br_do_del_hip(au_ii(inode), bindex, bend); ++} ++ ++int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount) ++{ ++ int err, rerr, i; ++ unsigned int mnt_flags; ++ aufs_bindex_t bindex, bend, br_id; ++ unsigned char do_wh, verbose; ++ struct au_branch *br; ++ struct au_wbr *wbr; ++ ++ err = 0; ++ bindex = au_find_dbindex(sb->s_root, del->h_path.dentry); ++ if (bindex < 0) { ++ if (remount) ++ goto out; /* success */ ++ err = -ENOENT; ++ AuErr("%s no such branch\n", del->pathname); ++ goto out; ++ } ++ AuDbg("bindex b%d\n", bindex); ++ ++ err = -EBUSY; ++ mnt_flags = au_mntflags(sb); ++ verbose = !!au_opt_test(mnt_flags, VERBOSE); ++ bend = au_sbend(sb); ++ if (unlikely(!bend)) { ++ AuVerbose(verbose, "no more branches left\n"); ++ goto out; ++ } ++ br = au_sbr(sb, bindex); ++ i = atomic_read(&br->br_count); ++ if (unlikely(i)) { ++ AuVerbose(verbose, "%d file(s) opened\n", i); ++ goto out; ++ } ++ ++ wbr = br->br_wbr; ++ do_wh = wbr && (wbr->wbr_whbase || wbr->wbr_plink || wbr->wbr_orph); ++ if (do_wh) { ++ for (i = 0; i < AuBrWh_Last; i++) { ++ dput(wbr->wbr_wh[i]); ++ wbr->wbr_wh[i] = NULL; ++ } ++ } ++ ++ err = test_children_busy(sb->s_root, bindex); ++ if (unlikely(err)) { ++ if (do_wh) ++ goto out_wh; ++ goto out; ++ } ++ ++ err = 0; ++ br_id = br->br_id; ++ if (!remount) ++ au_br_do_del(sb, bindex, br); ++ else { ++ sysaufs_brs_del(sb, bindex); ++ au_br_do_del(sb, bindex, br); ++ sysaufs_brs_add(sb, bindex); ++ } ++ ++ if (!bindex) ++ au_cpup_attr_all(sb->s_root->d_inode, /*force*/1); ++ else ++ au_sub_nlink(sb->s_root->d_inode, del->h_path.dentry->d_inode); ++ if (au_opt_test(mnt_flags, PLINK)) ++ au_plink_half_refresh(sb, br_id); ++ ++ if (sb->s_maxbytes == del->h_path.dentry->d_sb->s_maxbytes) { ++ bend--; ++ sb->s_maxbytes = 0; ++ for (bindex = 0; bindex <= bend; bindex++) { ++ unsigned long long maxb; ++ ++ maxb = au_sbr_sb(sb, bindex)->s_maxbytes; ++ if (sb->s_maxbytes < maxb) ++ sb->s_maxbytes = maxb; ++ } ++ } ++ ++ if (au_xino_brid(sb) == br->br_id) ++ au_xino_brid_set(sb, -1); ++ goto out; /* success */ ++ ++ out_wh: ++ /* revert */ ++ rerr = au_br_init_wh(sb, br, br->br_perm, del->h_path.dentry); ++ if (rerr) ++ AuWarn("failed re-creating base whiteout, %s. (%d)\n", ++ del->pathname, rerr); ++ out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * change a branch permission ++ */ ++ ++static int do_need_sigen_inc(int a, int b) ++{ ++ return au_br_whable(a) && !au_br_whable(b); ++} ++ ++static int need_sigen_inc(int old, int new) ++{ ++ return do_need_sigen_inc(old, new) ++ || do_need_sigen_inc(new, old); ++} ++ ++static int au_br_mod_files_ro(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ int err; ++ unsigned long n, ul, bytes, files; ++ aufs_bindex_t bstart; ++ struct file *file, *hf, **a; ++ const int step_bytes = 1024, /* memory allocation unit */ ++ step_files = step_bytes / sizeof(*a); ++ ++ err = -ENOMEM; ++ n = 0; ++ bytes = step_bytes; ++ files = step_files; ++ a = kmalloc(bytes, GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ /* no need file_list_lock() since sbinfo is locked? defered? */ ++ list_for_each_entry(file, &sb->s_files, f_u.fu_list) { ++ if (special_file(file->f_dentry->d_inode->i_mode)) ++ continue; ++ ++ AuDbg("%.*s\n", AuDLNPair(file->f_dentry)); ++ fi_read_lock(file); ++ if (unlikely(au_test_mmapped(file))) { ++ err = -EBUSY; ++ FiMustNoWaiters(file); ++ fi_read_unlock(file); ++ goto out_free; ++ } ++ ++ bstart = au_fbstart(file); ++ if (!S_ISREG(file->f_dentry->d_inode->i_mode) ++ || !(file->f_mode & FMODE_WRITE) ++ || bstart != bindex) { ++ FiMustNoWaiters(file); ++ fi_read_unlock(file); ++ continue; ++ } ++ ++ hf = au_h_fptr(file, bstart); ++ FiMustNoWaiters(file); ++ fi_read_unlock(file); ++ ++ if (n < files) ++ a[n++] = hf; ++ else { ++ void *p; ++ ++ err = -ENOMEM; ++ bytes += step_bytes; ++ files += step_files; ++ p = krealloc(a, bytes, GFP_NOFS); ++ if (p) { ++ a = p; ++ a[n++] = hf; ++ } else ++ goto out_free; ++ } ++ } ++ ++ err = 0; ++ for (ul = 0; ul < n; ul++) { ++ /* todo: already flushed? */ ++ /* cf. fs/super.c:mark_files_ro() */ ++ hf = a[ul]; ++ hf->f_mode &= ~FMODE_WRITE; ++ if (!file_check_writeable(hf)) { ++ file_release_write(hf); ++ mnt_drop_write(hf->f_vfsmnt); ++ } ++ } ++ ++ out_free: ++ kfree(a); ++ out: ++ return err; ++} ++ ++int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount, ++ int *do_update) ++{ ++ int err, rerr; ++ aufs_bindex_t bindex; ++ struct dentry *root; ++ struct au_branch *br; ++ ++ root = sb->s_root; ++ au_plink_block_maintain(sb); ++ bindex = au_find_dbindex(root, mod->h_root); ++ if (bindex < 0) { ++ if (remount) ++ return 0; /* success */ ++ err = -ENOENT; ++ AuErr("%s no such branch\n", mod->path); ++ goto out; ++ } ++ AuDbg("bindex b%d\n", bindex); ++ ++ err = test_br(mod->h_root->d_inode, mod->perm, mod->path); ++ if (unlikely(err)) ++ goto out; ++ ++ br = au_sbr(sb, bindex); ++ if (br->br_perm == mod->perm) ++ return